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--core/API/DataTableGenericFilter.php270
-rw-r--r--core/API/DataTableManipulator.php281
-rw-r--r--core/API/DataTableManipulator/Flattener.php216
-rw-r--r--core/API/DataTableManipulator/LabelFilter.php292
-rw-r--r--core/API/DocumentationGenerator.php425
-rw-r--r--core/API/Proxy.php773
-rw-r--r--core/API/Request.php320
-rw-r--r--core/API/ResponseBuilder.php927
-rw-r--r--core/Access.php721
-rw-r--r--core/Archive.php884
-rw-r--r--core/Archive/Array.php261
-rw-r--r--core/Archive/Array/IndexedByDate.php222
-rw-r--r--core/Archive/Array/IndexedBySite.php504
-rw-r--r--core/Archive/Single.php1251
-rw-r--r--core/ArchiveProcessing.php2027
-rw-r--r--core/ArchiveProcessing/Day.php1944
-rw-r--r--core/ArchiveProcessing/Period.php852
-rw-r--r--core/AssetManager.php861
-rw-r--r--core/Auth.php79
-rw-r--r--core/CacheFile.php302
-rw-r--r--core/Common.php3213
-rw-r--r--core/Config.php898
-rw-r--r--core/Config/Compat.php278
-rw-r--r--core/Controller.php1742
-rw-r--r--core/Controller/Admin.php91
-rw-r--r--core/Cookie.php741
-rw-r--r--core/DataFiles/Countries.php600
-rw-r--r--core/DataFiles/Currencies.php331
-rw-r--r--core/DataFiles/LanguageToCountry.php91
-rw-r--r--core/DataFiles/Languages.php377
-rw-r--r--core/DataFiles/SearchEngines.php1655
-rwxr-xr-xcore/DataFiles/Socials.php389
-rw-r--r--core/DataTable.php2813
-rw-r--r--core/DataTable/Array.php789
-rw-r--r--core/DataTable/Filter.php107
-rw-r--r--core/DataTable/Filter/AddColumnsProcessedMetrics.php222
-rw-r--r--core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php378
-rw-r--r--core/DataTable/Filter/AddConstantMetadata.php55
-rw-r--r--core/DataTable/Filter/AddSummaryRow.php123
-rw-r--r--core/DataTable/Filter/BeautifyRangeLabels.php261
-rw-r--r--core/DataTable/Filter/BeautifyTimeRangeLabels.php193
-rwxr-xr-xcore/DataTable/Filter/CalculateEvolutionFilter.php238
-rwxr-xr-xcore/DataTable/Filter/ColumnCallbackAddColumn.php141
-rw-r--r--core/DataTable/Filter/ColumnCallbackAddColumnPercentage.php34
-rw-r--r--core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php224
-rw-r--r--core/DataTable/Filter/ColumnCallbackAddMetadata.php117
-rw-r--r--core/DataTable/Filter/ColumnCallbackDeleteRow.php81
-rw-r--r--core/DataTable/Filter/ColumnCallbackReplace.php166
-rw-r--r--core/DataTable/Filter/ColumnDelete.php211
-rw-r--r--core/DataTable/Filter/ExcludeLowPopulation.php108
-rwxr-xr-xcore/DataTable/Filter/GroupBy.php155
-rw-r--r--core/DataTable/Filter/Limit.php101
-rw-r--r--core/DataTable/Filter/MetadataCallbackAddMetadata.php113
-rw-r--r--core/DataTable/Filter/MetadataCallbackReplace.php68
-rw-r--r--core/DataTable/Filter/Null.php37
-rw-r--r--core/DataTable/Filter/Pattern.php127
-rw-r--r--core/DataTable/Filter/PatternRecursive.php121
-rw-r--r--core/DataTable/Filter/RangeCheck.php79
-rw-r--r--core/DataTable/Filter/ReplaceColumnNames.php167
-rw-r--r--core/DataTable/Filter/ReplaceSummaryRowLabel.php81
-rw-r--r--core/DataTable/Filter/SafeDecodeLabel.php115
-rw-r--r--core/DataTable/Filter/Sort.php395
-rw-r--r--core/DataTable/Filter/Truncate.php60
-rw-r--r--core/DataTable/Manager.php269
-rw-r--r--core/DataTable/Renderer.php804
-rw-r--r--core/DataTable/Renderer/Console.php299
-rw-r--r--core/DataTable/Renderer/Csv.php742
-rw-r--r--core/DataTable/Renderer/Html.php398
-rw-r--r--core/DataTable/Renderer/Json.php185
-rw-r--r--core/DataTable/Renderer/Php.php494
-rw-r--r--core/DataTable/Renderer/Rss.php340
-rw-r--r--core/DataTable/Renderer/Tsv.php42
-rw-r--r--core/DataTable/Renderer/Xml.php877
-rw-r--r--core/DataTable/Row.php1231
-rw-r--r--core/DataTable/Row/DataTableSummary.php85
-rw-r--r--core/DataTable/Simple.php56
-rw-r--r--core/Date.php1276
-rw-r--r--core/Db/Adapter.php174
-rw-r--r--core/Db/Adapter/Interface.php96
-rw-r--r--core/Db/Adapter/Mysqli.php294
-rw-r--r--core/Db/Adapter/Pdo/Mssql.php443
-rw-r--r--core/Db/Adapter/Pdo/Mysql.php414
-rw-r--r--core/Db/Adapter/Pdo/Pgsql.php324
-rw-r--r--core/Db/Schema.php423
-rw-r--r--core/Db/Schema/Interface.php134
-rw-r--r--core/Db/Schema/Myisam.php481
-rw-r--r--core/ErrorHandler.php127
-rw-r--r--core/ExceptionHandler.php55
-rw-r--r--core/FrontController.php672
-rw-r--r--core/HTMLPurifier.php59
-rw-r--r--core/Http.php1427
-rw-r--r--core/IP.php1120
-rw-r--r--core/Loader.php190
-rw-r--r--core/Log.php326
-rw-r--r--core/Log/APICall.php179
-rw-r--r--core/Log/Error.php235
-rw-r--r--core/Log/Exception.php128
-rw-r--r--core/Log/Message.php115
-rw-r--r--core/Mail.php122
-rw-r--r--core/Menu/Abstract.php435
-rw-r--r--core/Menu/Admin.php115
-rw-r--r--core/Menu/Main.php149
-rw-r--r--core/Menu/Top.php131
-rw-r--r--core/Nonce.php241
-rw-r--r--core/Option.php323
-rw-r--r--core/Period.php410
-rw-r--r--core/Period/Day.php156
-rw-r--r--core/Period/Month.php110
-rw-r--r--core/Period/Range.php760
-rw-r--r--core/Period/Week.php149
-rw-r--r--core/Period/Year.php142
-rw-r--r--core/Piwik.php5096
-rw-r--r--core/Plugin.php208
-rw-r--r--core/Plugin/Config.php70
-rw-r--r--core/PluginsFunctions/Sql.php806
-rw-r--r--core/PluginsFunctions/WidgetsList.php322
-rw-r--r--core/PluginsManager.php1346
-rw-r--r--core/ProxyHeaders.php135
-rw-r--r--core/QuickForm2.php209
-rw-r--r--core/RankingQuery.php651
-rw-r--r--core/ReportRenderer.php469
-rw-r--r--core/ReportRenderer/Html.php272
-rw-r--r--core/ReportRenderer/Pdf.php972
-rw-r--r--core/ScheduledTask.php271
-rw-r--r--core/ScheduledTime.php171
-rw-r--r--core/ScheduledTime/Daily.php48
-rw-r--r--core/ScheduledTime/Hourly.php50
-rw-r--r--core/ScheduledTime/Monthly.php185
-rw-r--r--core/ScheduledTime/Weekly.php92
-rw-r--r--core/Segment.php530
-rw-r--r--core/SegmentExpression.php362
-rw-r--r--core/Session.php246
-rw-r--r--core/Session/Namespace.php31
-rw-r--r--core/Session/SaveHandler/DbTable.php246
-rw-r--r--core/Site.php692
-rw-r--r--core/Smarty.php217
-rw-r--r--core/SmartyPlugins/block.purify.php39
-rw-r--r--core/SmartyPlugins/function.ajaxErrorDiv.php19
-rw-r--r--core/SmartyPlugins/function.ajaxLoadingDiv.php27
-rw-r--r--core/SmartyPlugins/function.ajaxRequestErrorDiv.php12
-rw-r--r--core/SmartyPlugins/function.hiddenurl.php35
-rw-r--r--core/SmartyPlugins/function.includeAssets.php40
-rw-r--r--core/SmartyPlugins/function.loadJavascriptTranslations.php47
-rw-r--r--core/SmartyPlugins/function.logoHtml.php36
-rw-r--r--core/SmartyPlugins/function.postEvent.php21
-rw-r--r--core/SmartyPlugins/function.sparkline.php14
-rw-r--r--core/SmartyPlugins/function.url.php10
-rw-r--r--core/SmartyPlugins/modifier.inlineHelp.php18
-rw-r--r--core/SmartyPlugins/modifier.money.php17
-rw-r--r--core/SmartyPlugins/modifier.stripeol.php4
-rw-r--r--core/SmartyPlugins/modifier.sumtime.php6
-rw-r--r--core/SmartyPlugins/modifier.translate.php33
-rw-r--r--core/SmartyPlugins/modifier.unescape.php6
-rw-r--r--core/SmartyPlugins/modifier.urlRewriteBasicView.php35
-rw-r--r--core/SmartyPlugins/modifier.urlRewriteWithParameters.php10
-rw-r--r--core/SmartyPlugins/outputfilter.ajaxcdn.php38
-rw-r--r--core/SmartyPlugins/outputfilter.cachebuster.php32
-rw-r--r--core/TCPDF.php130
-rw-r--r--core/TablePartitioning.php195
-rw-r--r--core/TaskScheduler.php269
-rw-r--r--core/Timer.php91
-rw-r--r--core/Tracker.php1590
-rw-r--r--core/Tracker/Action.php2157
-rw-r--r--core/Tracker/Cache.php269
-rw-r--r--core/Tracker/Config.php20
-rw-r--r--core/Tracker/Db.php398
-rw-r--r--core/Tracker/Db/Exception.php3
-rw-r--r--core/Tracker/Db/Mysqli.php499
-rw-r--r--core/Tracker/Db/Pdo/Mysql.php393
-rw-r--r--core/Tracker/Db/Pdo/Pgsql.php176
-rw-r--r--core/Tracker/GoalManager.php1619
-rw-r--r--core/Tracker/IgnoreCookie.php103
-rw-r--r--core/Tracker/Visit.php3311
-rw-r--r--core/Translate.php372
-rw-r--r--core/TranslationWriter.php208
-rw-r--r--core/Unzip.php61
-rwxr-xr-xcore/Unzip/Gzip.php129
-rw-r--r--core/Unzip/Interface.php40
-rw-r--r--core/Unzip/PclZip.php122
-rwxr-xr-xcore/Unzip/Tar.php122
-rw-r--r--core/Unzip/ZipArchive.php210
-rw-r--r--core/UpdateCheck.php145
-rw-r--r--core/Updater.php562
-rw-r--r--core/Updates.php192
-rw-r--r--core/Updates/0.2.10.php81
-rw-r--r--core/Updates/0.2.12.php30
-rw-r--r--core/Updates/0.2.13.php22
-rw-r--r--core/Updates/0.2.24.php30
-rw-r--r--core/Updates/0.2.27.php50
-rw-r--r--core/Updates/0.2.32.php34
-rw-r--r--core/Updates/0.2.33.php40
-rw-r--r--core/Updates/0.2.34.php14
-rw-r--r--core/Updates/0.2.35.php20
-rw-r--r--core/Updates/0.2.37.php20
-rw-r--r--core/Updates/0.4.1.php24
-rw-r--r--core/Updates/0.4.2.php30
-rw-r--r--core/Updates/0.4.4.php22
-rw-r--r--core/Updates/0.4.php34
-rw-r--r--core/Updates/0.5.4.php82
-rw-r--r--core/Updates/0.5.5.php47
-rw-r--r--core/Updates/0.5.php38
-rw-r--r--core/Updates/0.6-rc1.php95
-rw-r--r--core/Updates/0.6.2.php50
-rw-r--r--core/Updates/0.6.3.php58
-rw-r--r--core/Updates/0.7.php20
-rw-r--r--core/Updates/0.9.1.php60
-rw-r--r--core/Updates/1.1.php26
-rwxr-xr-xcore/Updates/1.10-b4.php29
-rwxr-xr-xcore/Updates/1.10.1.php29
-rwxr-xr-xcore/Updates/1.10.2-b1.php26
-rw-r--r--core/Updates/1.10.2-b2.php26
-rw-r--r--core/Updates/1.11-b1.php29
-rw-r--r--core/Updates/1.12-b1.php34
-rw-r--r--core/Updates/1.2-rc1.php139
-rw-r--r--core/Updates/1.2-rc2.php14
-rw-r--r--core/Updates/1.2.3.php26
-rw-r--r--core/Updates/1.2.5-rc1.php24
-rw-r--r--core/Updates/1.2.5-rc7.php20
-rw-r--r--core/Updates/1.4-rc1.php30
-rw-r--r--core/Updates/1.4-rc2.php42
-rw-r--r--core/Updates/1.5-b1.php28
-rw-r--r--core/Updates/1.5-b2.php20
-rw-r--r--core/Updates/1.5-b3.php28
-rw-r--r--core/Updates/1.5-b4.php20
-rw-r--r--core/Updates/1.5-b5.php20
-rw-r--r--core/Updates/1.5-rc6.php14
-rw-r--r--core/Updates/1.6-b1.php32
-rw-r--r--core/Updates/1.6-rc1.php14
-rw-r--r--core/Updates/1.7-b1.php30
-rw-r--r--core/Updates/1.7.2-rc5.php26
-rwxr-xr-xcore/Updates/1.7.2-rc7.php44
-rw-r--r--core/Updates/1.8.3-b1.php136
-rw-r--r--core/Updates/1.8.4-b1.php141
-rwxr-xr-xcore/Updates/1.9-b16.php44
-rwxr-xr-xcore/Updates/1.9-b19.php34
-rwxr-xr-xcore/Updates/1.9-b9.php65
-rw-r--r--core/Updates/1.9.1-b2.php30
-rwxr-xr-xcore/Updates/1.9.3-b10.php29
-rw-r--r--core/Updates/1.9.3-b3.php18
-rwxr-xr-xcore/Updates/1.9.3-b8.php28
-rw-r--r--core/Url.php882
-rw-r--r--core/Version.php10
-rw-r--r--core/View.php431
-rw-r--r--core/View/Interface.php16
-rw-r--r--core/View/OneClickDone.php74
-rw-r--r--core/View/ReportsByDimension.php175
-rw-r--r--core/ViewDataTable.php2899
-rw-r--r--core/ViewDataTable/Cloud.php210
-rw-r--r--core/ViewDataTable/GenerateGraphData.php445
-rw-r--r--core/ViewDataTable/GenerateGraphData/ChartEvolution.php615
-rw-r--r--core/ViewDataTable/GenerateGraphData/ChartPie.php50
-rw-r--r--core/ViewDataTable/GenerateGraphData/ChartVerticalBar.php46
-rw-r--r--core/ViewDataTable/GenerateGraphHTML.php357
-rw-r--r--core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php358
-rw-r--r--core/ViewDataTable/GenerateGraphHTML/ChartPie.php24
-rw-r--r--core/ViewDataTable/GenerateGraphHTML/ChartVerticalBar.php24
-rw-r--r--core/ViewDataTable/HtmlTable.php425
-rw-r--r--core/ViewDataTable/HtmlTable/AllColumns.php102
-rw-r--r--core/ViewDataTable/HtmlTable/Goals.php500
-rw-r--r--core/ViewDataTable/Sparkline.php175
-rw-r--r--core/Visualization/Chart.php327
-rw-r--r--core/Visualization/Chart/Evolution.php54
-rw-r--r--core/Visualization/Chart/Pie.php72
-rw-r--r--core/Visualization/Chart/VerticalBar.php56
-rw-r--r--core/Visualization/Cloud.php174
-rw-r--r--core/Visualization/Sparkline.php148
-rw-r--r--core/testMinimumPhpVersion.php66
-rw-r--r--index.php45
-rw-r--r--libs/PiwikTracker/PiwikTracker.php1618
-rw-r--r--libs/UserAgentParser/UserAgentParser.php1234
-rw-r--r--libs/UserAgentParser/UserAgentParser.test.php70
-rw-r--r--misc/How to install Piwik.html10
-rw-r--r--misc/cron/archive.php1465
-rw-r--r--misc/others/ExamplePiwikTracker.php2
-rw-r--r--misc/others/api_rest_call.php16
-rwxr-xr-xmisc/others/geoipUpdateRows.php419
-rw-r--r--misc/others/iframeWidget.htm10
-rw-r--r--misc/others/iframeWidget_localhost.php61
-rw-r--r--misc/others/test_cookies_GenerateHundredsWebsitesAndVisits.php7
-rw-r--r--misc/others/test_generateLotsVisitsWebsites.php205
-rw-r--r--misc/others/tracker_simpleImageTracker.php10
-rw-r--r--misc/others/uninstall-delete-piwik-directory.php24
-rw-r--r--misc/others/widget_example_lastvisits.html10
-rw-r--r--misc/proxy-hide-piwik-url/piwik.php60
-rw-r--r--piwik.php111
-rw-r--r--plugins/API/API.php3125
-rw-r--r--plugins/API/Controller.php168
-rw-r--r--plugins/API/css/styles.css47
-rw-r--r--plugins/API/templates/listAllAPI.tpl26
-rw-r--r--plugins/Actions/API.php991
-rw-r--r--plugins/Actions/Actions.php1099
-rw-r--r--plugins/Actions/Archiving.php950
-rw-r--r--plugins/Actions/ArchivingHelper.php939
-rw-r--r--plugins/Actions/Controller.php1007
-rw-r--r--plugins/Actions/templates/indexSiteSearch.tpl20
-rwxr-xr-xplugins/Annotations/API.php691
-rwxr-xr-xplugins/Annotations/AnnotationList.php824
-rwxr-xr-xplugins/Annotations/Annotations.php96
-rwxr-xr-xplugins/Annotations/Controller.php406
-rwxr-xr-xplugins/Annotations/templates/annotation.tpl83
-rwxr-xr-xplugins/Annotations/templates/annotationManager.tpl36
-rwxr-xr-xplugins/Annotations/templates/annotations.js1194
-rwxr-xr-xplugins/Annotations/templates/annotations.tpl47
-rwxr-xr-xplugins/Annotations/templates/evolutionAnnotations.tpl18
-rwxr-xr-xplugins/Annotations/templates/styles.css216
-rw-r--r--plugins/AnonymizeIP/AnonymizeIP.php106
-rw-r--r--plugins/CoreAdminHome/API.php425
-rw-r--r--plugins/CoreAdminHome/Controller.php442
-rw-r--r--plugins/CoreAdminHome/CoreAdminHome.php214
-rw-r--r--plugins/CoreAdminHome/templates/generalSettings.js231
-rw-r--r--plugins/CoreAdminHome/templates/generalSettings.tpl456
-rw-r--r--plugins/CoreAdminHome/templates/header.tpl125
-rw-r--r--plugins/CoreAdminHome/templates/jsTrackingGenerator.css67
-rw-r--r--plugins/CoreAdminHome/templates/jsTrackingGenerator.js661
-rw-r--r--plugins/CoreAdminHome/templates/jsTrackingGenerator.tpl429
-rw-r--r--plugins/CoreAdminHome/templates/menu.css89
-rw-r--r--plugins/CoreAdminHome/templates/menu.tpl37
-rw-r--r--plugins/CoreAdminHome/templates/optOut.tpl49
-rw-r--r--plugins/CoreAdminHome/templates/styles.css165
-rw-r--r--plugins/CoreHome/Controller.php419
-rw-r--r--plugins/CoreHome/CoreHome.php162
-rw-r--r--plugins/CoreHome/DataTableRowAction/MultiRowEvolution.php134
-rw-r--r--plugins/CoreHome/DataTableRowAction/RowEvolution.php548
-rw-r--r--plugins/CoreHome/templates/autocomplete.js463
-rw-r--r--plugins/CoreHome/templates/broadcast.js449
-rw-r--r--plugins/CoreHome/templates/calendar.js1094
-rw-r--r--plugins/CoreHome/templates/cloud.css51
-rw-r--r--plugins/CoreHome/templates/cloud.tpl45
-rw-r--r--plugins/CoreHome/templates/datatable.css706
-rw-r--r--plugins/CoreHome/templates/datatable.js3739
-rw-r--r--plugins/CoreHome/templates/datatable.tpl111
-rw-r--r--plugins/CoreHome/templates/datatable_actions.tpl101
-rw-r--r--plugins/CoreHome/templates/datatable_actions_recursive.tpl71
-rw-r--r--plugins/CoreHome/templates/datatable_actions_subdatable.tpl30
-rw-r--r--plugins/CoreHome/templates/datatable_cell.tpl23
-rw-r--r--plugins/CoreHome/templates/datatable_footer.tpl198
-rw-r--r--plugins/CoreHome/templates/datatable_js.tpl7
-rw-r--r--plugins/CoreHome/templates/datatable_manager.js356
-rw-r--r--plugins/CoreHome/templates/datatable_rowactions.js540
-rw-r--r--plugins/CoreHome/templates/date.js77
-rwxr-xr-xplugins/CoreHome/templates/donate.css156
-rwxr-xr-xplugins/CoreHome/templates/donate.js282
-rwxr-xr-xplugins/CoreHome/templates/donate.tpl117
-rw-r--r--plugins/CoreHome/templates/footer.tpl2
-rw-r--r--plugins/CoreHome/templates/graph.tpl74
-rw-r--r--plugins/CoreHome/templates/header.tpl40
-rw-r--r--plugins/CoreHome/templates/header_message.tpl54
-rw-r--r--plugins/CoreHome/templates/html_report_body.tpl142
-rw-r--r--plugins/CoreHome/templates/html_report_footer.tpl2
-rw-r--r--plugins/CoreHome/templates/html_report_header.tpl55
-rw-r--r--plugins/CoreHome/templates/iframe_buster_body.tpl6
-rw-r--r--plugins/CoreHome/templates/iframe_buster_header.tpl8
-rw-r--r--plugins/CoreHome/templates/index_before_menu.tpl6
-rw-r--r--plugins/CoreHome/templates/index_content.tpl27
-rw-r--r--plugins/CoreHome/templates/jqplot.css199
-rw-r--r--plugins/CoreHome/templates/jqplot.js2600
-rw-r--r--plugins/CoreHome/templates/jquery.ui.autocomplete.css61
-rw-r--r--plugins/CoreHome/templates/js_css_includes.tpl2
-rw-r--r--plugins/CoreHome/templates/js_disabled_notice.tpl4
-rw-r--r--plugins/CoreHome/templates/js_global_variables.tpl68
-rw-r--r--plugins/CoreHome/templates/logo.tpl15
-rw-r--r--plugins/CoreHome/templates/menu.js64
-rw-r--r--plugins/CoreHome/templates/menu.tpl26
-rw-r--r--plugins/CoreHome/templates/menu_init.js6
-rwxr-xr-xplugins/CoreHome/templates/misc.js319
-rw-r--r--plugins/CoreHome/templates/period_select.tpl56
-rw-r--r--plugins/CoreHome/templates/piwik_tag.tpl59
-rw-r--r--plugins/CoreHome/templates/popover.js399
-rw-r--r--plugins/CoreHome/templates/popover_multirowevolution.tpl66
-rw-r--r--plugins/CoreHome/templates/popover_rowevolution.tpl62
-rwxr-xr-xplugins/CoreHome/templates/promo_video.tpl248
-rw-r--r--plugins/CoreHome/templates/reports_by_dimension.tpl45
-rw-r--r--plugins/CoreHome/templates/sites_selection.tpl47
-rw-r--r--plugins/CoreHome/templates/sparkline.js104
-rw-r--r--plugins/CoreHome/templates/sparkline_footer.tpl10
-rw-r--r--plugins/CoreHome/templates/styles.css213
-rw-r--r--plugins/CoreHome/templates/tooltip.js216
-rw-r--r--plugins/CoreHome/templates/top_bar.tpl12
-rw-r--r--plugins/CoreHome/templates/top_bar_hello_menu.tpl10
-rw-r--r--plugins/CoreHome/templates/top_bar_top_menu.tpl21
-rw-r--r--plugins/CoreHome/templates/top_screen.tpl4
-rw-r--r--plugins/CoreHome/templates/warning_invalid_host.tpl9
-rw-r--r--plugins/CorePluginsAdmin/Controller.php129
-rw-r--r--plugins/CorePluginsAdmin/CorePluginsAdmin.php50
-rw-r--r--plugins/CorePluginsAdmin/templates/manage.tpl105
-rw-r--r--plugins/CoreUpdater/Controller.php694
-rw-r--r--plugins/CoreUpdater/CoreUpdater.php132
-rw-r--r--plugins/CoreUpdater/templates/cli_update_database_done.tpl80
-rw-r--r--plugins/CoreUpdater/templates/cli_update_welcome.tpl44
-rw-r--r--plugins/CoreUpdater/templates/header.tpl68
-rw-r--r--plugins/CoreUpdater/templates/update_database_done.tpl123
-rw-r--r--plugins/CoreUpdater/templates/update_new_version_available.tpl29
-rw-r--r--plugins/CoreUpdater/templates/update_one_click_results.tpl19
-rw-r--r--plugins/CoreUpdater/templates/update_welcome.tpl208
-rw-r--r--plugins/CustomVariables/API.php144
-rw-r--r--plugins/CustomVariables/Controller.php78
-rw-r--r--plugins/CustomVariables/CustomVariables.php631
-rw-r--r--plugins/DBStats/API.php584
-rw-r--r--plugins/DBStats/Controller.php717
-rw-r--r--plugins/DBStats/DBStats.php128
-rwxr-xr-xplugins/DBStats/MySQLMetadataProvider.php706
-rwxr-xr-xplugins/DBStats/templates/index.tpl231
-rw-r--r--plugins/Dashboard/Controller.php137
-rw-r--r--plugins/Dashboard/Dashboard.php285
-rw-r--r--plugins/Dashboard/templates/dashboard.css266
-rw-r--r--plugins/Dashboard/templates/dashboard.js62
-rw-r--r--plugins/Dashboard/templates/dashboardObject.js182
-rwxr-xr-xplugins/Dashboard/templates/dashboardWidget.js82
-rw-r--r--plugins/Dashboard/templates/header.tpl159
-rw-r--r--plugins/Dashboard/templates/index.tpl105
-rw-r--r--plugins/Dashboard/templates/standalone.tpl13
-rw-r--r--plugins/Dashboard/templates/widgetMenu.js320
-rw-r--r--plugins/DoNotTrack/DoNotTrack.php97
-rw-r--r--plugins/ExampleAPI/API.php323
-rw-r--r--plugins/ExampleAPI/ExampleAPI.php38
-rw-r--r--plugins/ExamplePlugin/Controller.php220
-rw-r--r--plugins/ExamplePlugin/ExamplePlugin.php120
-rw-r--r--plugins/ExamplePlugin/config/local.config.sample.php6
-rw-r--r--plugins/ExamplePlugin/lang/en.php20
-rw-r--r--plugins/ExamplePlugin/templates/piwikDownloads.tpl26
-rw-r--r--plugins/ExampleRssWidget/Controller.php7
-rw-r--r--plugins/ExampleRssWidget/ExampleRssWidget.php62
-rw-r--r--plugins/ExampleRssWidget/Rss.php23
-rw-r--r--plugins/ExampleRssWidget/templates/styles.css43
-rw-r--r--plugins/ExampleUI/API.php174
-rw-r--r--plugins/ExampleUI/Controller.php234
-rw-r--r--plugins/ExampleUI/ExampleUI.php87
-rw-r--r--plugins/Feedback/Controller.php110
-rw-r--r--plugins/Feedback/Feedback.php94
-rw-r--r--plugins/Feedback/templates/feedback.js58
-rw-r--r--plugins/Feedback/templates/index.tpl103
-rw-r--r--plugins/Feedback/templates/sent.tpl31
-rw-r--r--plugins/Feedback/templates/styles.css83
-rw-r--r--plugins/Goals/API.php1061
-rw-r--r--plugins/Goals/Controller.php1083
-rw-r--r--plugins/Goals/Goals.php1738
-rw-r--r--plugins/Goals/templates/GoalForm.js230
-rw-r--r--plugins/Goals/templates/add_edit_goal.tpl109
-rw-r--r--plugins/Goals/templates/add_new_goal.tpl15
-rw-r--r--plugins/Goals/templates/form_add_goal.tpl180
-rw-r--r--plugins/Goals/templates/goals.css33
-rw-r--r--plugins/Goals/templates/list_goal_edit.tpl88
-rw-r--r--plugins/Goals/templates/list_top_dimension.tpl11
-rw-r--r--plugins/Goals/templates/overview.tpl73
-rw-r--r--plugins/Goals/templates/single_goal.tpl73
-rw-r--r--plugins/Goals/templates/title_and_evolution_graph.tpl109
-rw-r--r--plugins/ImageGraph/API.php1053
-rw-r--r--plugins/ImageGraph/Controller.php106
-rw-r--r--plugins/ImageGraph/ImageGraph.php273
-rw-r--r--plugins/ImageGraph/StaticGraph.php598
-rw-r--r--plugins/ImageGraph/StaticGraph/3DPie.php18
-rw-r--r--plugins/ImageGraph/StaticGraph/Evolution.php22
-rw-r--r--plugins/ImageGraph/StaticGraph/Exception.php86
-rw-r--r--plugins/ImageGraph/StaticGraph/GridGraph.php851
-rw-r--r--plugins/ImageGraph/StaticGraph/HorizontalBar.php346
-rw-r--r--plugins/ImageGraph/StaticGraph/Pie.php18
-rw-r--r--plugins/ImageGraph/StaticGraph/PieGraph.php217
-rw-r--r--plugins/ImageGraph/StaticGraph/VerticalBar.php32
-rw-r--r--plugins/ImageGraph/templates/debug_graphs_all_sizes.tpl91
-rw-r--r--plugins/ImageGraph/templates/index.tpl4
-rw-r--r--plugins/Installation/Controller.php1913
-rw-r--r--plugins/Installation/FormDatabaseSetup.php528
-rw-r--r--plugins/Installation/FormFirstWebsiteSetup.php105
-rw-r--r--plugins/Installation/FormGeneralSetup.php113
-rw-r--r--plugins/Installation/Installation.php142
-rw-r--r--plugins/Installation/View.php79
-rw-r--r--plugins/Installation/templates/allSteps.tpl18
-rw-r--r--plugins/Installation/templates/databaseCheck.tpl44
-rw-r--r--plugins/Installation/templates/databaseSetup.tpl14
-rw-r--r--plugins/Installation/templates/displayJavascriptCode.tpl28
-rw-r--r--plugins/Installation/templates/finished.tpl2
-rw-r--r--plugins/Installation/templates/firstWebsiteSetup.tpl20
-rw-r--r--plugins/Installation/templates/generalSetup.tpl2
-rw-r--r--plugins/Installation/templates/install.css240
-rw-r--r--plugins/Installation/templates/integrityDetails.tpl75
-rw-r--r--plugins/Installation/templates/structure.tpl126
-rw-r--r--plugins/Installation/templates/systemCheck.tpl14
-rwxr-xr-xplugins/Installation/templates/systemCheckPage.css46
-rwxr-xr-xplugins/Installation/templates/systemCheckPage.tpl24
-rwxr-xr-xplugins/Installation/templates/systemCheckSection.tpl558
-rw-r--r--plugins/Installation/templates/systemCheck_legend.tpl20
-rw-r--r--plugins/Installation/templates/tablesCreation.tpl96
-rw-r--r--plugins/Installation/templates/welcome.tpl62
-rw-r--r--plugins/LanguagesManager/API.php322
-rw-r--r--plugins/LanguagesManager/Controller.php47
-rw-r--r--plugins/LanguagesManager/LanguagesManager.php440
-rw-r--r--plugins/LanguagesManager/templates/languageSelector.js82
-rw-r--r--plugins/LanguagesManager/templates/languages.tpl26
-rw-r--r--plugins/Live/API.php888
-rw-r--r--plugins/Live/Controller.php255
-rw-r--r--plugins/Live/Live.php92
-rw-r--r--plugins/Live/Visitor.php1010
-rw-r--r--plugins/Live/templates/index.tpl56
-rw-r--r--plugins/Live/templates/lastVisits.tpl145
-rw-r--r--plugins/Live/templates/live.css116
-rw-r--r--plugins/Live/templates/scripts/live.js128
-rw-r--r--plugins/Live/templates/simpleLastVisitCount.tpl232
-rw-r--r--plugins/Live/templates/totalVisits.tpl51
-rw-r--r--plugins/Live/templates/visitorLog.tpl581
-rw-r--r--plugins/Login/Auth.php177
-rw-r--r--plugins/Login/Controller.php972
-rw-r--r--plugins/Login/FormLogin.php36
-rw-r--r--plugins/Login/FormResetPassword.php32
-rw-r--r--plugins/Login/Login.php355
-rw-r--r--plugins/Login/templates/header.tpl114
-rw-r--r--plugins/Login/templates/login.css251
-rwxr-xr-xplugins/Login/templates/login.js191
-rw-r--r--plugins/Login/templates/login.tpl141
-rwxr-xr-xplugins/Login/templates/message.tpl14
-rw-r--r--plugins/MobileMessaging/API.php893
-rw-r--r--plugins/MobileMessaging/Controller.php91
-rw-r--r--plugins/MobileMessaging/CountryCallingCodes.php486
-rw-r--r--plugins/MobileMessaging/GSMCharset.php276
-rw-r--r--plugins/MobileMessaging/MobileMessaging.php621
-rw-r--r--plugins/MobileMessaging/ReportRenderer/Exception.php90
-rw-r--r--plugins/MobileMessaging/ReportRenderer/Sms.php198
-rw-r--r--plugins/MobileMessaging/SMSProvider.php283
-rw-r--r--plugins/MobileMessaging/SMSProvider/Clockwork.php163
-rw-r--r--plugins/MobileMessaging/SMSProvider/StubbedProvider.php24
-rw-r--r--plugins/MobileMessaging/scripts/MobileMessagingSettings.js337
-rw-r--r--plugins/MobileMessaging/templates/ReportParameters.tpl105
-rw-r--r--plugins/MobileMessaging/templates/SMSReport.tpl138
-rw-r--r--plugins/MobileMessaging/templates/Settings.tpl347
-rwxr-xr-xplugins/MultiSites/API.php930
-rw-r--r--plugins/MultiSites/Controller.php431
-rw-r--r--plugins/MultiSites/MultiSites.php157
-rw-r--r--plugins/MultiSites/templates/common.js411
-rw-r--r--plugins/MultiSites/templates/index.tpl191
-rw-r--r--plugins/MultiSites/templates/row.tpl37
-rw-r--r--plugins/MultiSites/templates/styles.css76
-rw-r--r--plugins/Overlay/API.php219
-rw-r--r--plugins/Overlay/Controller.php340
-rw-r--r--plugins/Overlay/Overlay.php46
-rw-r--r--plugins/Overlay/client/client.css161
-rw-r--r--plugins/Overlay/client/client.js518
-rw-r--r--plugins/Overlay/client/followingpages.js1018
-rw-r--r--plugins/Overlay/client/translations.js56
-rw-r--r--plugins/Overlay/client/urlnormalizer.js385
-rw-r--r--plugins/Overlay/templates/error_wrong_domain.tpl54
-rw-r--r--plugins/Overlay/templates/helper.js44
-rw-r--r--plugins/Overlay/templates/index.css131
-rw-r--r--plugins/Overlay/templates/index.js408
-rw-r--r--plugins/Overlay/templates/index.tpl60
-rw-r--r--plugins/Overlay/templates/index_noframe.tpl32
-rw-r--r--plugins/Overlay/templates/notify_parent_iframe.tpl16
-rw-r--r--plugins/Overlay/templates/rowaction.js68
-rw-r--r--plugins/Overlay/templates/sidebar.tpl34
-rw-r--r--plugins/PDFReports/API.php1430
-rw-r--r--plugins/PDFReports/Controller.php102
-rw-r--r--plugins/PDFReports/PDFReports.php1208
-rw-r--r--plugins/PDFReports/config/tcpdf_config.php432
-rw-r--r--plugins/PDFReports/templates/add.tpl260
-rw-r--r--plugins/PDFReports/templates/index.tpl62
-rw-r--r--plugins/PDFReports/templates/list.tpl182
-rw-r--r--plugins/PDFReports/templates/pdf.js242
-rw-r--r--plugins/PDFReports/templates/report_parameters.tpl187
-rw-r--r--plugins/PrivacyManager/Controller.php557
-rwxr-xr-xplugins/PrivacyManager/LogDataPurger.php615
-rw-r--r--plugins/PrivacyManager/PrivacyManager.php560
-rwxr-xr-xplugins/PrivacyManager/ReportsPurger.php733
-rwxr-xr-xplugins/PrivacyManager/templates/databaseSize.tpl4
-rw-r--r--plugins/PrivacyManager/templates/privacySettings.js300
-rw-r--r--plugins/PrivacyManager/templates/privacySettings.tpl469
-rw-r--r--plugins/Provider/API.php51
-rw-r--r--plugins/Provider/Controller.php53
-rw-r--r--plugins/Provider/Provider.php481
-rw-r--r--plugins/Provider/functions.php63
-rw-r--r--plugins/Proxy/Controller.php313
-rw-r--r--plugins/Proxy/Proxy.php38
-rw-r--r--plugins/Proxy/templates/exportImage.tpl19
-rw-r--r--plugins/Referers/API.php969
-rw-r--r--plugins/Referers/Controller.php1349
-rw-r--r--plugins/Referers/Referers.php1153
-rw-r--r--plugins/Referers/functions.php262
-rwxr-xr-xplugins/Referers/templates/Websites_SocialNetworks.tpl8
-rw-r--r--plugins/Referers/templates/index.tpl128
-rw-r--r--plugins/Referers/templates/searchEngines_Keywords.tpl8
-rw-r--r--plugins/SEO/API.php153
-rw-r--r--plugins/SEO/Controller.php55
-rw-r--r--plugins/SEO/MajesticClient.php154
-rw-r--r--plugins/SEO/RankChecker.php379
-rw-r--r--plugins/SEO/SEO.php44
-rw-r--r--plugins/SEO/templates/index.tpl78
-rw-r--r--plugins/SEO/templates/rank.js2
-rw-r--r--plugins/SecurityInfo/Controller.php42
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/PhpSecInfo.php976
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Application/php.php77
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Application/piwik.php55
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/CGI/force_redirect.php157
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/allow_url_fopen.php100
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/allow_url_include.php107
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/display_errors.php85
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/expose_php.php85
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/file_uploads.php86
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/gid.php112
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/magic_quotes_gpc.php83
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/memory_limit.php87
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/open_basedir.php81
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/post_max_size.php68
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/register_globals.php83
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/uid.php114
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/upload_max_filesize.php84
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Core/upload_tmp_dir.php133
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Curl/file_support.php76
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Session/save_path.php165
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Session/use_trans_sid.php63
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Suhosin/extension.php39
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Suhosin/patch.php67
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Test.php1087
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Test_Application.php71
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Test_Cgi.php78
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Test_Core.php50
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Test_Curl.php80
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Test_Session.php52
-rw-r--r--plugins/SecurityInfo/PhpSecInfo/Test/Test_Suhosin.php65
-rw-r--r--plugins/SecurityInfo/SecurityInfo.php56
-rw-r--r--plugins/SecurityInfo/templates/index.tpl41
-rw-r--r--plugins/SitesManager/API.php2655
-rw-r--r--plugins/SitesManager/Controller.php359
-rw-r--r--plugins/SitesManager/SitesManager.php408
-rw-r--r--plugins/SitesManager/templates/DisplayAlternativeTags.tpl203
-rw-r--r--plugins/SitesManager/templates/DisplayJavascriptCode.tpl109
-rw-r--r--plugins/SitesManager/templates/SitesManager.js551
-rw-r--r--plugins/SitesManager/templates/SitesManager.tpl648
-rw-r--r--plugins/Transitions/API.php582
-rw-r--r--plugins/Transitions/Controller.php150
-rw-r--r--plugins/Transitions/Transitions.php691
-rw-r--r--plugins/Transitions/templates/transitions.css197
-rw-r--r--plugins/Transitions/templates/transitions.js2380
-rw-r--r--plugins/Transitions/templates/transitions.tpl91
-rw-r--r--plugins/UserCountry/API.php381
-rw-r--r--plugins/UserCountry/Controller.php957
-rwxr-xr-xplugins/UserCountry/GeoIPAutoUpdater.php1124
-rwxr-xr-xplugins/UserCountry/LocationProvider.php883
-rwxr-xr-xplugins/UserCountry/LocationProvider/Default.php182
-rwxr-xr-xplugins/UserCountry/LocationProvider/GeoIp.php495
-rwxr-xr-xplugins/UserCountry/LocationProvider/GeoIp/Pecl.php641
-rwxr-xr-xplugins/UserCountry/LocationProvider/GeoIp/Php.php651
-rwxr-xr-xplugins/UserCountry/LocationProvider/GeoIp/ServerBased.php547
-rw-r--r--plugins/UserCountry/UserCountry.php1012
-rw-r--r--plugins/UserCountry/functions.php180
-rwxr-xr-xplugins/UserCountry/templates/admin.js298
-rwxr-xr-xplugins/UserCountry/templates/adminIndex.tpl199
-rw-r--r--plugins/UserCountry/templates/index.tpl29
-rwxr-xr-xplugins/UserCountry/templates/styles.css59
-rwxr-xr-xplugins/UserCountry/templates/updaterSetup.tpl5
-rw-r--r--plugins/UserCountryMap/Controller.php392
-rw-r--r--plugins/UserCountryMap/UserCountryMap.php42
-rw-r--r--plugins/UserCountryMap/css/map.css7
-rw-r--r--plugins/UserCountryMap/css/qtip.css218
-rw-r--r--plugins/UserCountryMap/css/realtime-map.css45
-rw-r--r--plugins/UserCountryMap/css/visitor-map.css31
-rw-r--r--plugins/UserCountryMap/js/realtime-map.js132
-rw-r--r--plugins/UserCountryMap/js/vendor/chroma.min.js680
-rw-r--r--plugins/UserCountryMap/js/vendor/jquery.qtip.min.js364
-rw-r--r--plugins/UserCountryMap/js/vendor/kartograph.js10564
-rw-r--r--plugins/UserCountryMap/js/vendor/kartograph.min.js1674
-rw-r--r--plugins/UserCountryMap/js/vendor/kmeans.js256
-rw-r--r--plugins/UserCountryMap/js/vendor/raphael-min.js2215
-rw-r--r--plugins/UserCountryMap/js/visitor-map.js759
-rw-r--r--plugins/UserCountryMap/templates/realtime-map.tpl60
-rw-r--r--plugins/UserCountryMap/templates/visitor-map.tpl77
-rw-r--r--plugins/UserSettings/API.php372
-rw-r--r--plugins/UserSettings/Controller.php360
-rw-r--r--plugins/UserSettings/UserSettings.php859
-rw-r--r--plugins/UserSettings/functions.php314
-rw-r--r--plugins/UserSettings/templates/index.tpl42
-rw-r--r--plugins/UsersManager/API.php1307
-rw-r--r--plugins/UsersManager/Controller.php675
-rw-r--r--plugins/UsersManager/UsersManager.php245
-rw-r--r--plugins/UsersManager/templates/UsersManager.js315
-rw-r--r--plugins/UsersManager/templates/UsersManager.tpl251
-rw-r--r--plugins/UsersManager/templates/userSettings.js99
-rw-r--r--plugins/UsersManager/templates/userSettings.tpl225
-rw-r--r--plugins/VisitFrequency/API.php258
-rw-r--r--plugins/VisitFrequency/Controller.php170
-rw-r--r--plugins/VisitFrequency/VisitFrequency.php234
-rw-r--r--plugins/VisitFrequency/templates/index.tpl4
-rw-r--r--plugins/VisitFrequency/templates/sparklines.tpl13
-rw-r--r--plugins/VisitTime/API.php285
-rw-r--r--plugins/VisitTime/Controller.php181
-rw-r--r--plugins/VisitTime/VisitTime.php416
-rw-r--r--plugins/VisitTime/templates/index.tpl8
-rw-r--r--plugins/VisitorGenerator/Controller.php278
-rw-r--r--plugins/VisitorGenerator/VisitorGenerator.php50
-rw-r--r--plugins/VisitorGenerator/templates/generate.tpl19
-rw-r--r--plugins/VisitorGenerator/templates/index.tpl58
-rw-r--r--plugins/VisitorInterest/API.php208
-rw-r--r--plugins/VisitorInterest/Controller.php196
-rw-r--r--plugins/VisitorInterest/VisitorInterest.php633
-rw-r--r--plugins/VisitorInterest/templates/index.tpl1
-rw-r--r--plugins/VisitsSummary/API.php274
-rw-r--r--plugins/VisitsSummary/Controller.php301
-rw-r--r--plugins/VisitsSummary/VisitsSummary.php112
-rw-r--r--plugins/VisitsSummary/templates/sparklines.tpl102
-rw-r--r--plugins/Widgetize/Controller.php120
-rw-r--r--plugins/Widgetize/Widgetize.php112
-rw-r--r--plugins/Widgetize/templates/iframe.tpl24
-rw-r--r--plugins/Widgetize/templates/index.tpl165
-rw-r--r--plugins/Widgetize/templates/js.tpl36
-rw-r--r--plugins/Widgetize/templates/test_jsinclude.tpl7
-rw-r--r--plugins/Widgetize/templates/widgetize.js148
-rwxr-xr-xtests/LocalTracker.php212
-rw-r--r--tests/PHPUnit/BaseFixture.php447
-rwxr-xr-xtests/PHPUnit/BenchmarkTestCase.php178
-rwxr-xr-xtests/PHPUnit/Benchmarks/ArchivingProcessBenchmark.php22
-rwxr-xr-xtests/PHPUnit/Benchmarks/Fixtures/OneSiteTwelveThousandVisitsOneYear.php104
-rwxr-xr-xtests/PHPUnit/Benchmarks/Fixtures/SqlDump.php107
-rwxr-xr-xtests/PHPUnit/Benchmarks/Fixtures/ThousandSitesTwelveVisitsEachOneDay.php121
-rwxr-xr-xtests/PHPUnit/Benchmarks/TrackerBenchmark.php115
-rw-r--r--tests/PHPUnit/Core/API/ResponseBuilderTest.php48
-rw-r--r--tests/PHPUnit/Core/AccessTest.php66
-rw-r--r--tests/PHPUnit/Core/ArchiveProcessing/DayTest.php194
-rw-r--r--tests/PHPUnit/Core/ArchiveProcessingTest.php227
-rw-r--r--tests/PHPUnit/Core/CommonTest.php435
-rw-r--r--tests/PHPUnit/Core/ConfigTest.php17
-rw-r--r--tests/PHPUnit/Core/CookieTest.php62
-rwxr-xr-xtests/PHPUnit/Core/DataTable/ArrayTest.php8
-rw-r--r--tests/PHPUnit/Core/DataTable/Filter/AddSummaryRowTest.php105
-rw-r--r--tests/PHPUnit/Core/DataTable/Filter/ExcludeLowPopulationTest.php83
-rw-r--r--tests/PHPUnit/Core/DataTable/Filter/LimitTest.php224
-rw-r--r--tests/PHPUnit/Core/DataTable/Filter/PatternRecursiveTest.php26
-rw-r--r--tests/PHPUnit/Core/DataTable/Filter/PatternTest.php28
-rw-r--r--tests/PHPUnit/Core/DataTable/Filter/RangeCheckTest.php28
-rw-r--r--tests/PHPUnit/Core/DataTable/Filter/SortTest.php138
-rw-r--r--tests/PHPUnit/Core/DataTable/Filter/TruncateTest.php90
-rw-r--r--tests/PHPUnit/Core/DataTable/Renderer/CSVTest.php202
-rw-r--r--tests/PHPUnit/Core/DataTable/Renderer/ConsoleTest.php126
-rw-r--r--tests/PHPUnit/Core/DataTable/Renderer/JSONTest.php206
-rw-r--r--tests/PHPUnit/Core/DataTable/Renderer/PHPTest.php438
-rw-r--r--tests/PHPUnit/Core/DataTable/Renderer/XMLTest.php184
-rw-r--r--tests/PHPUnit/Core/DataTable/RowTest.php274
-rw-r--r--tests/PHPUnit/Core/DataTableTest.php847
-rw-r--r--tests/PHPUnit/Core/DateTest.php57
-rw-r--r--tests/PHPUnit/Core/HttpTest.php98
-rw-r--r--tests/PHPUnit/Core/IPTest.php349
-rw-r--r--tests/PHPUnit/Core/JsProxyTest.php4
-rw-r--r--tests/PHPUnit/Core/NonceTest.php10
-rw-r--r--tests/PHPUnit/Core/OptionTest.php14
-rw-r--r--tests/PHPUnit/Core/Period/DayTest.php96
-rw-r--r--tests/PHPUnit/Core/Period/MonthTest.php40
-rw-r--r--tests/PHPUnit/Core/Period/RangeTest.php1048
-rw-r--r--tests/PHPUnit/Core/Period/WeekTest.php44
-rw-r--r--tests/PHPUnit/Core/Period/YearTest.php20
-rw-r--r--tests/PHPUnit/Core/PeriodTest.php34
-rw-r--r--tests/PHPUnit/Core/PiwikTest.php148
-rw-r--r--tests/PHPUnit/Core/Plugin/ConfigTest.php3
-rw-r--r--tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php30
-rw-r--r--tests/PHPUnit/Core/RankingQueryTest.php128
-rw-r--r--tests/PHPUnit/Core/ReleaseCheckListTest.php82
-rw-r--r--tests/PHPUnit/Core/ScheduledTaskTest.php60
-rw-r--r--tests/PHPUnit/Core/ScheduledTime/DailyTest.php35
-rw-r--r--tests/PHPUnit/Core/ScheduledTime/HourlyTest.php18
-rw-r--r--tests/PHPUnit/Core/ScheduledTime/MonthlyTest.php50
-rw-r--r--tests/PHPUnit/Core/ScheduledTime/WeeklyTest.php40
-rw-r--r--tests/PHPUnit/Core/SegmentExpressionTest.php4
-rw-r--r--tests/PHPUnit/Core/SegmentTest.php189
-rw-r--r--tests/PHPUnit/Core/ServeStaticFileTest.php4
-rwxr-xr-xtests/PHPUnit/Core/SqlTest.php52
-rw-r--r--tests/PHPUnit/Core/TablePartitioningTest.php40
-rw-r--r--tests/PHPUnit/Core/TaskSchedulerTest.php611
-rw-r--r--tests/PHPUnit/Core/Tracker/ActionTest.php219
-rw-r--r--tests/PHPUnit/Core/Tracker/VisitTest.php157
-rw-r--r--tests/PHPUnit/Core/TranslationWriterTest.php17
-rw-r--r--tests/PHPUnit/Core/UnzipTest.php103
-rw-r--r--tests/PHPUnit/Core/UpdaterTest.php12
-rw-r--r--tests/PHPUnit/Core/UrlTest.php40
-rw-r--r--tests/PHPUnit/DatabaseTestCase.php19
-rw-r--r--tests/PHPUnit/FakeAccess.php128
-rw-r--r--tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorId.php20
-rw-r--r--tests/PHPUnit/Fixtures/InvalidVisits.php39
-rw-r--r--tests/PHPUnit/Fixtures/ManySitesImportedLogs.php289
-rw-r--r--tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php334
-rw-r--r--tests/PHPUnit/Fixtures/ManyVisitsWithMockLocationProvider.php374
-rw-r--r--tests/PHPUnit/Fixtures/ManyVisitsWithSubDirReferrersAndCustomVars.php23
-rw-r--r--tests/PHPUnit/Fixtures/OneVisitSeveralPageViews.php26
-rw-r--r--tests/PHPUnit/Fixtures/OneVisitWithAbnormalPageviewUrls.php18
-rw-r--r--tests/PHPUnit/Fixtures/OneVisitorTwoVisits.php127
-rw-r--r--tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php26
-rw-r--r--tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php44
-rw-r--r--tests/PHPUnit/Fixtures/SomeVisitsManyPageviewsWithTransitions.php150
-rw-r--r--tests/PHPUnit/Fixtures/SomeVisitsWithLongUrls.php16
-rw-r--r--tests/PHPUnit/Fixtures/SomeVisitsWithNonUnicodePageTitles.php148
-rw-r--r--tests/PHPUnit/Fixtures/ThreeGoalsOnePageview.php48
-rw-r--r--tests/PHPUnit/Fixtures/ThreeSitesWithManyVisitsWithSiteSearch.php318
-rw-r--r--tests/PHPUnit/Fixtures/TwoSitesEcommerceOrderWithItems.php30
-rw-r--r--tests/PHPUnit/Fixtures/TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers.php63
-rw-r--r--tests/PHPUnit/Fixtures/TwoSitesTwoVisitorsDifferentDays.php54
-rw-r--r--tests/PHPUnit/Fixtures/TwoSitesVisitsInPast.php18
-rw-r--r--tests/PHPUnit/Fixtures/TwoSitesWithAnnotations.php113
-rw-r--r--tests/PHPUnit/Fixtures/TwoVisitsNoKeywordWithBot.php16
-rw-r--r--tests/PHPUnit/Fixtures/TwoVisitsWithCustomVariables.php49
-rw-r--r--tests/PHPUnit/Fixtures/VisitsOverSeveralDays.php58
-rwxr-xr-xtests/PHPUnit/Integration/AnnotationsTest.php662
-rwxr-xr-xtests/PHPUnit/Integration/ApiGetReportMetadataTest.php46
-rwxr-xr-xtests/PHPUnit/Integration/ApiGetReportMetadata_yearTest.php8
-rwxr-xr-xtests/PHPUnit/Integration/BlobReportLimitingTest.php155
-rwxr-xr-xtests/PHPUnit/Integration/CsvExportTest.php10
-rwxr-xr-xtests/PHPUnit/Integration/EcommerceOrderWithItemsTest.php264
-rw-r--r--tests/PHPUnit/Integration/FlattenReportsTest.php56
-rwxr-xr-xtests/PHPUnit/Integration/ImportLogsTest.php70
-rw-r--r--tests/PHPUnit/Integration/LabelFilterTest.php124
-rwxr-xr-xtests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php108
-rwxr-xr-xtests/PHPUnit/Integration/NoVisitTest.php6
-rwxr-xr-xtests/PHPUnit/Integration/NonUnicodeTest.php68
-rwxr-xr-xtests/PHPUnit/Integration/OneVisitorOneWebsite_SeveralDaysDateRangeTest.php90
-rwxr-xr-xtests/PHPUnit/Integration/OneVisitorOneWebsite_SeveralDaysDateRange_ArchivingTestsTest.php20
-rwxr-xr-xtests/PHPUnit/Integration/OneVisitorTwoVisitsTest.php225
-rwxr-xr-xtests/PHPUnit/Integration/OneVisitorTwoVisits_withCookieSupportTest.php2
-rw-r--r--tests/PHPUnit/Integration/OneVisitor_LongUrlsTruncatedTest.php2
-rwxr-xr-xtests/PHPUnit/Integration/OneVisitor_NoKeywordSpecifiedTest.php8
-rwxr-xr-xtests/PHPUnit/Integration/PeriodIsRange_DateIsLastN_MetadataAndNormalAPITest.php52
-rwxr-xr-xtests/PHPUnit/Integration/RowEvolutionTest.php148
-rwxr-xr-xtests/PHPUnit/Integration/SiteSearchTest.php40
-rwxr-xr-xtests/PHPUnit/Integration/TrackCustomVariablesAndCampaigns_ForceUsingVisitIdNotHeuristicsTest.php4
-rwxr-xr-xtests/PHPUnit/Integration/TrackGoals_AllowMultipleConversionsPerVisitTest.php6
-rw-r--r--tests/PHPUnit/Integration/TrackingAPI_SetVisitorIdTest.php8
-rw-r--r--tests/PHPUnit/Integration/TransitionsTest.php58
-rwxr-xr-xtests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDaysTest.php44
-rwxr-xr-xtests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisabledTest.php8
-rwxr-xr-xtests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDays_ConversionsTest.php34
-rwxr-xr-xtests/PHPUnit/Integration/TwoVisitsWithCustomVariablesTest.php26
-rwxr-xr-xtests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentContainsTest.php20
-rwxr-xr-xtests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalDataTest.php6
-rwxr-xr-xtests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchNONETest.php8
-rwxr-xr-xtests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchVisitorTypeTest.php8
-rw-r--r--tests/PHPUnit/Integration/UrlNormalizationTest.php20
-rw-r--r--tests/PHPUnit/Integration/VisitsInPast_InvalidateOldReportsTest.php32
-rw-r--r--tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml4
-rw-r--r--tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__PDFReports.generateReport_month.original.html165
-rw-r--r--tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__PDFReports.generateReport_week.original.html180
-rwxr-xr-xtests/PHPUnit/IntegrationTestCase.php1092
-rw-r--r--tests/PHPUnit/MockEventDispatcher.php19
-rwxr-xr-xtests/PHPUnit/MockLocationProvider.php81
-rw-r--r--tests/PHPUnit/MockPiwikOption.php25
-rw-r--r--tests/PHPUnit/Plugins/ActionsTest.php84
-rw-r--r--tests/PHPUnit/Plugins/AnonymizeIPTest.php45
-rwxr-xr-xtests/PHPUnit/Plugins/LanguagesManagerTest.php56
-rw-r--r--tests/PHPUnit/Plugins/LoginTest.php29
-rw-r--r--tests/PHPUnit/Plugins/MobileMessagingTest.php384
-rw-r--r--tests/PHPUnit/Plugins/MultiSitesTest.php64
-rw-r--r--tests/PHPUnit/Plugins/PDFReportsTest.php927
-rwxr-xr-xtests/PHPUnit/Plugins/PrivacyManagerTest.php466
-rw-r--r--tests/PHPUnit/Plugins/ReferersTest.php40
-rw-r--r--tests/PHPUnit/Plugins/SEOTest.php7
-rw-r--r--tests/PHPUnit/Plugins/SitesManagerTest.php579
-rw-r--r--tests/PHPUnit/Plugins/UserCountryTest.php133
-rw-r--r--tests/PHPUnit/Plugins/UserSettingsTest.php7
-rw-r--r--tests/PHPUnit/Plugins/UsersManagerTest.php651
-rw-r--r--tests/PHPUnit/bootstrap.php80
-rw-r--r--tests/PHPUnit/config.ini.travis.php20
-rw-r--r--tests/PHPUnit/proxy/index.php35
-rwxr-xr-xtests/PHPUnit/proxy/piwik.php12
-rw-r--r--tests/index.php27
854 files changed, 121313 insertions, 121409 deletions
diff --git a/core/API/DataTableGenericFilter.php b/core/API/DataTableGenericFilter.php
index ffdd4de044..02e2b31a24 100644
--- a/core/API/DataTableGenericFilter.php
+++ b/core/API/DataTableGenericFilter.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,146 +15,136 @@
*/
class Piwik_API_DataTableGenericFilter
{
- private static $genericFiltersInfo = null;
+ private static $genericFiltersInfo = null;
- /**
- * Constructor
- *
- * @param $request
- */
- function __construct( $request )
- {
- $this->request = $request;
- }
+ /**
+ * Constructor
+ *
+ * @param $request
+ */
+ function __construct($request)
+ {
+ $this->request = $request;
+ }
- /**
- * Filters the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- $this->applyGenericFilters($table);
- }
-
- /**
- * Returns an array containing the information of the generic Piwik_DataTable_Filter
- * to be applied automatically to the data resulting from the API calls.
- *
- * Order to apply the filters:
- * 1 - Filter that remove filtered rows
- * 2 - Filter that sort the remaining rows
- * 3 - Filter that keep only a subset of the results
- * 4 - Presentation filters
- *
- * @return array See the code for spec
- */
- public static function getGenericFiltersInformation()
- {
- if (is_null(self::$genericFiltersInfo))
- {
- self::$genericFiltersInfo = array(
- 'Pattern' => array(
- 'filter_column' => array('string', 'label'),
- 'filter_pattern' => array('string'),
- ),
- 'PatternRecursive' => array(
- 'filter_column_recursive' => array('string', 'label'),
- 'filter_pattern_recursive' => array('string'),
- ),
- 'ExcludeLowPopulation' => array(
- 'filter_excludelowpop' => array('string'),
- 'filter_excludelowpop_value'=> array('float', '0'),
- ),
- 'AddColumnsProcessedMetrics' => array(
- 'filter_add_columns_when_show_all_columns' => array('integer')
- ),
- 'AddColumnsProcessedMetricsGoal' => array(
- 'filter_update_columns_when_show_all_goals' => array('integer'),
- 'idGoal' => array('string', Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW),
- ),
- 'Sort' => array(
- 'filter_sort_column' => array('string'),
- 'filter_sort_order' => array('string', 'desc'),
- ),
- 'Truncate' => array(
- 'filter_truncate' => array('integer'),
- ),
- 'Limit' => array(
- 'filter_offset' => array('integer', '0'),
- 'filter_limit' => array('integer'),
- 'keep_summary_row' => array('integer', '0'),
- ),
- );
- }
+ /**
+ * Filters the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ $this->applyGenericFilters($table);
+ }
- return self::$genericFiltersInfo;
- }
-
- /**
- * Apply generic filters to the DataTable object resulting from the API Call.
- * Disable this feature by setting the parameter disable_generic_filters to 1 in the API call request.
- *
- * @param Piwik_DataTable $datatable
- * @return bool
- */
- protected function applyGenericFilters($datatable)
- {
- if($datatable instanceof Piwik_DataTable_Array )
- {
- $tables = $datatable->getArray();
- $filterWasApplied = false;
- foreach($tables as $table)
- {
- $filterWasApplied = $this->applyGenericFilters($table);
- }
- return;
- }
-
- $genericFilters = self::getGenericFiltersInformation();
-
- $filterApplied = false;
- foreach($genericFilters as $filterName => $parameters)
- {
- $filterParameters = array();
- $exceptionRaised = false;
- foreach($parameters as $name => $info)
- {
- // parameter type to cast to
- $type = $info[0];
-
- // default value if specified, when the parameter doesn't have a value
- $defaultValue = null;
- if(isset($info[1]))
- {
- $defaultValue = $info[1];
- }
-
- // third element in the array, if it exists, overrides the name of the request variable
- $varName = $name;
- if(isset($info[2]))
- {
- $varName = $info[2];
- }
-
- try {
- $value = Piwik_Common::getRequestVar($name, $defaultValue, $type, $this->request);
- settype($value, $type);
- $filterParameters[] = $value;
- }
- catch(Exception $e)
- {
- $exceptionRaised = true;
- break;
- }
- }
+ /**
+ * Returns an array containing the information of the generic Piwik_DataTable_Filter
+ * to be applied automatically to the data resulting from the API calls.
+ *
+ * Order to apply the filters:
+ * 1 - Filter that remove filtered rows
+ * 2 - Filter that sort the remaining rows
+ * 3 - Filter that keep only a subset of the results
+ * 4 - Presentation filters
+ *
+ * @return array See the code for spec
+ */
+ public static function getGenericFiltersInformation()
+ {
+ if (is_null(self::$genericFiltersInfo)) {
+ self::$genericFiltersInfo = array(
+ 'Pattern' => array(
+ 'filter_column' => array('string', 'label'),
+ 'filter_pattern' => array('string'),
+ ),
+ 'PatternRecursive' => array(
+ 'filter_column_recursive' => array('string', 'label'),
+ 'filter_pattern_recursive' => array('string'),
+ ),
+ 'ExcludeLowPopulation' => array(
+ 'filter_excludelowpop' => array('string'),
+ 'filter_excludelowpop_value' => array('float', '0'),
+ ),
+ 'AddColumnsProcessedMetrics' => array(
+ 'filter_add_columns_when_show_all_columns' => array('integer')
+ ),
+ 'AddColumnsProcessedMetricsGoal' => array(
+ 'filter_update_columns_when_show_all_goals' => array('integer'),
+ 'idGoal' => array('string', Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW),
+ ),
+ 'Sort' => array(
+ 'filter_sort_column' => array('string'),
+ 'filter_sort_order' => array('string', 'desc'),
+ ),
+ 'Truncate' => array(
+ 'filter_truncate' => array('integer'),
+ ),
+ 'Limit' => array(
+ 'filter_offset' => array('integer', '0'),
+ 'filter_limit' => array('integer'),
+ 'keep_summary_row' => array('integer', '0'),
+ ),
+ );
+ }
- if(!$exceptionRaised)
- {
- $datatable->filter($filterName, $filterParameters);
- $filterApplied = true;
- }
- }
- return $filterApplied;
- }
+ return self::$genericFiltersInfo;
+ }
+
+ /**
+ * Apply generic filters to the DataTable object resulting from the API Call.
+ * Disable this feature by setting the parameter disable_generic_filters to 1 in the API call request.
+ *
+ * @param Piwik_DataTable $datatable
+ * @return bool
+ */
+ protected function applyGenericFilters($datatable)
+ {
+ if ($datatable instanceof Piwik_DataTable_Array) {
+ $tables = $datatable->getArray();
+ $filterWasApplied = false;
+ foreach ($tables as $table) {
+ $filterWasApplied = $this->applyGenericFilters($table);
+ }
+ return;
+ }
+
+ $genericFilters = self::getGenericFiltersInformation();
+
+ $filterApplied = false;
+ foreach ($genericFilters as $filterName => $parameters) {
+ $filterParameters = array();
+ $exceptionRaised = false;
+ foreach ($parameters as $name => $info) {
+ // parameter type to cast to
+ $type = $info[0];
+
+ // default value if specified, when the parameter doesn't have a value
+ $defaultValue = null;
+ if (isset($info[1])) {
+ $defaultValue = $info[1];
+ }
+
+ // third element in the array, if it exists, overrides the name of the request variable
+ $varName = $name;
+ if (isset($info[2])) {
+ $varName = $info[2];
+ }
+
+ try {
+ $value = Piwik_Common::getRequestVar($name, $defaultValue, $type, $this->request);
+ settype($value, $type);
+ $filterParameters[] = $value;
+ } catch (Exception $e) {
+ $exceptionRaised = true;
+ break;
+ }
+ }
+
+ if (!$exceptionRaised) {
+ $datatable->filter($filterName, $filterParameters);
+ $filterApplied = true;
+ }
+ }
+ return $filterApplied;
+ }
}
diff --git a/core/API/DataTableManipulator.php b/core/API/DataTableManipulator.php
index b7f1f79a2e..dee8d80316 100644
--- a/core/API/DataTableManipulator.php
+++ b/core/API/DataTableManipulator.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,7 +12,7 @@
/**
* Base class for manipulating data tables.
* It provides generic mechanisms like iteration and loading subtables.
- *
+ *
* The manipulators are used in Piwik_API_ResponseBuilder and are triggered by
* API parameters. They are not filters because they don't work on the pre-
* fetched nested data tables. Instead, they load subtables using this base
@@ -20,156 +20,145 @@
* of using expanded=1. Another difference between manipulators and filters
* is that filters keep the overall structure of the table intact while
* manipulators can change the entire thing.
- *
+ *
* @package Piwik
* @subpackage Piwik_API
*/
abstract class Piwik_API_DataTableManipulator
{
- protected $apiModule;
- protected $apiMethod;
- protected $request;
-
- private $apiMethodForSubtable;
-
- /**
- * Constructor
- *
- * @param bool $apiModule
- * @param bool $apiMethod
- * @param array $request
- */
- public function __construct($apiModule=false, $apiMethod=false, $request=array()) {
- $this->apiModule = $apiModule;
- $this->apiMethod = $apiMethod;
- $this->request = $request;
- }
-
- /**
- * This method can be used by subclasses to iterate over data tables that might be
- * data table arrays. It calls back the template method self::doManipulate for each table.
- * This way, data table arrays can be handled in a transparent fashion.
- *
- * @param Piwik_DataTable_Array|Piwik_DataTable $dataTable
- * @throws Exception
- * @return Piwik_DataTable_Array|Piwik_DataTable
- */
- protected function manipulate($dataTable)
- {
- if ($dataTable instanceof Piwik_DataTable_Array)
- {
- return $this->manipulateDataTableArray($dataTable);
- }
- else if ($dataTable instanceof Piwik_DataTable)
- {
- return $this->manipulateDataTable($dataTable);
- }
- else
- {
- return $dataTable;
- }
- }
-
- /**
- * Manipulates child DataTables of a DataTable_Array. See @manipulate for more info.
- */
- protected function manipulateDataTableArray( $dataTable )
- {
- $result = $dataTable->getEmptyClone();
- foreach ($dataTable->getArray() as $tableLabel => $childTable)
- {
- $newTable = $this->manipulate($childTable);
- $result->addTable($newTable, $tableLabel);
- }
- return $result;
- }
-
- /**
- * Manipulates a single Piwik_DataTable instance. Derived classes must define
- * this function.
- */
- protected abstract function manipulateDataTable( $dataTable );
-
- /**
- * Load the subtable for a row.
- * Returns null if none is found.
- *
- * @param Piwik_Datatable_Row $row
- * @throws Exception
- * @return Piwik_DataTable
- */
- protected function loadSubtable($dataTable, $row) {
- if (!($this->apiModule && $this->apiMethod && count($this->request))) {
- return null;
- }
-
- $request = $this->request;
-
+ protected $apiModule;
+ protected $apiMethod;
+ protected $request;
+
+ private $apiMethodForSubtable;
+
+ /**
+ * Constructor
+ *
+ * @param bool $apiModule
+ * @param bool $apiMethod
+ * @param array $request
+ */
+ public function __construct($apiModule = false, $apiMethod = false, $request = array())
+ {
+ $this->apiModule = $apiModule;
+ $this->apiMethod = $apiMethod;
+ $this->request = $request;
+ }
+
+ /**
+ * This method can be used by subclasses to iterate over data tables that might be
+ * data table arrays. It calls back the template method self::doManipulate for each table.
+ * This way, data table arrays can be handled in a transparent fashion.
+ *
+ * @param Piwik_DataTable_Array|Piwik_DataTable $dataTable
+ * @throws Exception
+ * @return Piwik_DataTable_Array|Piwik_DataTable
+ */
+ protected function manipulate($dataTable)
+ {
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ return $this->manipulateDataTableArray($dataTable);
+ } else if ($dataTable instanceof Piwik_DataTable) {
+ return $this->manipulateDataTable($dataTable);
+ } else {
+ return $dataTable;
+ }
+ }
+
+ /**
+ * Manipulates child DataTables of a DataTable_Array. See @manipulate for more info.
+ */
+ protected function manipulateDataTableArray($dataTable)
+ {
+ $result = $dataTable->getEmptyClone();
+ foreach ($dataTable->getArray() as $tableLabel => $childTable) {
+ $newTable = $this->manipulate($childTable);
+ $result->addTable($newTable, $tableLabel);
+ }
+ return $result;
+ }
+
+ /**
+ * Manipulates a single Piwik_DataTable instance. Derived classes must define
+ * this function.
+ */
+ protected abstract function manipulateDataTable($dataTable);
+
+ /**
+ * Load the subtable for a row.
+ * Returns null if none is found.
+ *
+ * @param Piwik_Datatable_Row $row
+ * @throws Exception
+ * @return Piwik_DataTable
+ */
+ protected function loadSubtable($dataTable, $row)
+ {
+ if (!($this->apiModule && $this->apiMethod && count($this->request))) {
+ return null;
+ }
+
+ $request = $this->request;
+
$idSubTable = $row->getIdSubDataTable();
- if ($idSubTable === null)
- {
- return null;
- }
-
- $request['idSubtable'] = $idSubTable;
- if ($dataTable)
- {
- $request['date'] = $dataTable->metadata['period']->getDateStart()->toString();
- }
-
- $class = 'Piwik_'.$this->apiModule.'_API';
+ if ($idSubTable === null) {
+ return null;
+ }
+
+ $request['idSubtable'] = $idSubTable;
+ if ($dataTable) {
+ $request['date'] = $dataTable->metadata['period']->getDateStart()->toString();
+ }
+
+ $class = 'Piwik_' . $this->apiModule . '_API';
$method = $this->getApiMethodForSubtable();
-
+
$this->manipulateSubtableRequest($request);
$request['serialize'] = 0;
- $request['expanded'] = 0;
-
- // don't want to run recursive filters on the subtables as they are loaded,
- // otherwise the result will be empty in places (or everywhere). instead we
- // run it on the flattened table.
- unset($request['filter_pattern_recursive']);
-
- $dataTable = Piwik_API_Proxy::getInstance()->call($class, $method, $request);
- $response = new Piwik_API_ResponseBuilder($format = 'original', $request);
- $dataTable = $response->getResponse($dataTable);
- if (method_exists($dataTable, 'applyQueuedFilters'))
- {
- $dataTable->applyQueuedFilters();
- }
-
- return $dataTable;
- }
-
- /**
- * In this method, subclasses can clean up the request array for loading subtables
- * in order to make Piwik_API_ResponseBuilder behave correctly (e.g. not trigger the
- * manipulator again).
- *
- * @param $request
- * @return
- */
- protected abstract function manipulateSubtableRequest(&$request);
-
- /**
- * Extract the API method for loading subtables from the meta data
- *
- * @return string
- */
- private function getApiMethodForSubtable()
- {
- if (!$this->apiMethodForSubtable)
- {
- $meta = Piwik_API_API::getInstance()->getMetadata('all', $this->apiModule, $this->apiMethod);
- if (isset($meta[0]['actionToLoadSubTables']))
- {
- $this->apiMethodForSubtable = $meta[0]['actionToLoadSubTables'];
- }
- else
- {
- $this->apiMethodForSubtable = $this->apiMethod;
- }
- }
- return $this->apiMethodForSubtable;
- }
-
+ $request['expanded'] = 0;
+
+ // don't want to run recursive filters on the subtables as they are loaded,
+ // otherwise the result will be empty in places (or everywhere). instead we
+ // run it on the flattened table.
+ unset($request['filter_pattern_recursive']);
+
+ $dataTable = Piwik_API_Proxy::getInstance()->call($class, $method, $request);
+ $response = new Piwik_API_ResponseBuilder($format = 'original', $request);
+ $dataTable = $response->getResponse($dataTable);
+ if (method_exists($dataTable, 'applyQueuedFilters')) {
+ $dataTable->applyQueuedFilters();
+ }
+
+ return $dataTable;
+ }
+
+ /**
+ * In this method, subclasses can clean up the request array for loading subtables
+ * in order to make Piwik_API_ResponseBuilder behave correctly (e.g. not trigger the
+ * manipulator again).
+ *
+ * @param $request
+ * @return
+ */
+ protected abstract function manipulateSubtableRequest(&$request);
+
+ /**
+ * Extract the API method for loading subtables from the meta data
+ *
+ * @return string
+ */
+ private function getApiMethodForSubtable()
+ {
+ if (!$this->apiMethodForSubtable) {
+ $meta = Piwik_API_API::getInstance()->getMetadata('all', $this->apiModule, $this->apiMethod);
+ if (isset($meta[0]['actionToLoadSubTables'])) {
+ $this->apiMethodForSubtable = $meta[0]['actionToLoadSubTables'];
+ } else {
+ $this->apiMethodForSubtable = $this->apiMethod;
+ }
+ }
+ return $this->apiMethodForSubtable;
+ }
+
}
diff --git a/core/API/DataTableManipulator/Flattener.php b/core/API/DataTableManipulator/Flattener.php
index e6cd3c0991..a3360d5618 100644
--- a/core/API/DataTableManipulator/Flattener.php
+++ b/core/API/DataTableManipulator/Flattener.php
@@ -1,136 +1,126 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* This class is responsible for flattening data tables.
- *
+ *
* It loads subtables and combines them into a single table by concatenating the labels.
* This manipulator is triggered by using flat=1 in the API request.
- *
+ *
* @package Piwik
* @subpackage Piwik_API
*/
class Piwik_API_DataTableManipulator_Flattener extends Piwik_API_DataTableManipulator
{
-
- private $includeAggregateRows = false;
-
- /**
- * If the flattener is used after calling this method, aggregate rows will
- * be included in the result. This can be useful when they contain data that
- * the leafs don't have (e.g. conversion stats in some cases).
- */
- public function includeAggregateRows()
- {
- $this->includeAggregateRows = true;
- }
-
- /**
- * Separator for building recursive labels (or paths)
- * @var string
- */
- public $recursiveLabelSeparator = ' - ';
- /**
- * @param Piwik_DataTable $dataTable
- * @return Piwik_DataTable|Piwik_DataTable_Array
- */
- public function flatten($dataTable)
- {
- if ($this->apiModule == 'Actions' || $this->apiMethod == 'getWebsites')
- {
- $this->recursiveLabelSeparator = '/';
- }
-
- return $this->manipulate($dataTable);
- }
+ private $includeAggregateRows = false;
- /**
- * Template method called from self::manipulate.
- * Flatten each data table.
- *
- * @param Piwik_DataTable $dataTable
- * @return Piwik_DataTable
- */
- protected function manipulateDataTable($dataTable)
- {
- $newDataTable = $dataTable->getEmptyClone();
- foreach ($dataTable->getRows() as $row)
- {
- $this->flattenRow($row, $newDataTable);
- }
- return $newDataTable;
- }
+ /**
+ * If the flattener is used after calling this method, aggregate rows will
+ * be included in the result. This can be useful when they contain data that
+ * the leafs don't have (e.g. conversion stats in some cases).
+ */
+ public function includeAggregateRows()
+ {
+ $this->includeAggregateRows = true;
+ }
- /**
- * @param Piwik_DataTable_Row $row
- * @param Piwik_DataTable $dataTable
- * @param string $labelPrefix
- * @param bool $parentLogo
- */
- private function flattenRow(Piwik_DataTable_Row $row, Piwik_DataTable $dataTable,
- $labelPrefix = '', $parentLogo = false) {
-
- $label = $row->getColumn('label');
- if ($label !== false)
- {
- $label = trim($label);
- if (substr($label, 0, 1) == '/' && $this->recursiveLabelSeparator == '/')
- {
- $label = substr($label, 1);
- }
- $label = $labelPrefix . $label;
- $row->setColumn('label', $label);
- }
-
- $logo = $row->getMetadata('logo');
- if ($logo === false && $parentLogo !== false)
- {
- $logo = $parentLogo;
- $row->setMetadata('logo', $logo);
- }
-
- $subTable = $this->loadSubtable($dataTable, $row);
- $row->removeSubtable();
-
- if ($subTable === null)
- {
- if ($this->includeAggregateRows)
- {
- $row->setMetadata('is_aggregate', 0);
- }
- $dataTable->addRow($row);
- }
- else
- {
- if ($this->includeAggregateRows)
- {
- $row->setMetadata('is_aggregate', 1);
- $dataTable->addRow($row);
- }
- $prefix = $label . $this->recursiveLabelSeparator;
- foreach ($subTable->getRows() as $row)
- {
- $this->flattenRow($row, $dataTable, $prefix, $logo);
- }
- }
- }
+ /**
+ * Separator for building recursive labels (or paths)
+ * @var string
+ */
+ public $recursiveLabelSeparator = ' - ';
- /**
- * Remove the flat parameter from the subtable request
- *
- * @param array $request
- */
- protected function manipulateSubtableRequest(&$request)
- {
- unset($request['flat']);
- }
+ /**
+ * @param Piwik_DataTable $dataTable
+ * @return Piwik_DataTable|Piwik_DataTable_Array
+ */
+ public function flatten($dataTable)
+ {
+ if ($this->apiModule == 'Actions' || $this->apiMethod == 'getWebsites') {
+ $this->recursiveLabelSeparator = '/';
+ }
+
+ return $this->manipulate($dataTable);
+ }
+
+ /**
+ * Template method called from self::manipulate.
+ * Flatten each data table.
+ *
+ * @param Piwik_DataTable $dataTable
+ * @return Piwik_DataTable
+ */
+ protected function manipulateDataTable($dataTable)
+ {
+ $newDataTable = $dataTable->getEmptyClone();
+ foreach ($dataTable->getRows() as $row) {
+ $this->flattenRow($row, $newDataTable);
+ }
+ return $newDataTable;
+ }
+
+ /**
+ * @param Piwik_DataTable_Row $row
+ * @param Piwik_DataTable $dataTable
+ * @param string $labelPrefix
+ * @param bool $parentLogo
+ */
+ private function flattenRow(Piwik_DataTable_Row $row, Piwik_DataTable $dataTable,
+ $labelPrefix = '', $parentLogo = false)
+ {
+
+ $label = $row->getColumn('label');
+ if ($label !== false) {
+ $label = trim($label);
+ if (substr($label, 0, 1) == '/' && $this->recursiveLabelSeparator == '/') {
+ $label = substr($label, 1);
+ }
+ $label = $labelPrefix . $label;
+ $row->setColumn('label', $label);
+ }
+
+ $logo = $row->getMetadata('logo');
+ if ($logo === false && $parentLogo !== false) {
+ $logo = $parentLogo;
+ $row->setMetadata('logo', $logo);
+ }
+
+ $subTable = $this->loadSubtable($dataTable, $row);
+ $row->removeSubtable();
+
+ if ($subTable === null) {
+ if ($this->includeAggregateRows) {
+ $row->setMetadata('is_aggregate', 0);
+ }
+ $dataTable->addRow($row);
+ } else {
+ if ($this->includeAggregateRows) {
+ $row->setMetadata('is_aggregate', 1);
+ $dataTable->addRow($row);
+ }
+ $prefix = $label . $this->recursiveLabelSeparator;
+ foreach ($subTable->getRows() as $row) {
+ $this->flattenRow($row, $dataTable, $prefix, $logo);
+ }
+ }
+ }
+
+ /**
+ * Remove the flat parameter from the subtable request
+ *
+ * @param array $request
+ */
+ protected function manipulateSubtableRequest(&$request)
+ {
+ unset($request['flat']);
+ }
}
diff --git a/core/API/DataTableManipulator/LabelFilter.php b/core/API/DataTableManipulator/LabelFilter.php
index 246a4b6ebc..f2aa0020da 100644
--- a/core/API/DataTableManipulator/LabelFilter.php
+++ b/core/API/DataTableManipulator/LabelFilter.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -13,164 +13,156 @@
* This class is responsible for handling the label parameter that can be
* added to every API call. If the parameter is set, only the row with the matching
* label is returned.
- *
+ *
* The labels passed to this class should be urlencoded.
* Some reports use recursive labels (e.g. action reports). Use > to join them.
- *
+ *
* This filter does not work when expanded=1 is set because it is designed to load
* only the subtables on the path, not all existing subtables (which would happen with
* expanded=1). Also, the aim of this filter is to return only the row matching the
* label. With expanded=1, the subtables of the matching row would be returned as well.
- *
+ *
* @package Piwik
* @subpackage Piwik_API
*/
class Piwik_API_DataTableManipulator_LabelFilter extends Piwik_API_DataTableManipulator
{
- const SEPARATOR_RECURSIVE_LABEL = '>';
-
- private $labels;
- private $addEmptyRows;
-
- /**
- * Filter a data table by label.
- * The filtered table is returned, which might be a new instance.
- *
- * $apiModule, $apiMethod and $request are needed load sub-datatables
- * for the recursive search. If the label is not recursive, these parameters
- * are not needed.
- *
- * @param string $labels the labels to search for
- * @param Piwik_DataTable $dataTable the data table to be filtered
- * @param bool $addEmptyRows Whether to add empty rows when a row isn't found
- * for a label, or not.
- * @return Piwik_DataTable
- */
- public function filter($labels, $dataTable, $addEmptyRows = false)
- {
- if (!is_array($labels))
- {
- $labels = array($labels);
- }
-
- $this->labels = $labels;
- $this->addEmptyRows = (bool)$addEmptyRows;
- return $this->manipulate($dataTable);
- }
-
- /**
- * Method for the recursive descend
- *
- * @param array $labelParts
- * @param Piwik_DataTable $dataTable
- * @return Piwik_DataTable_Row|false
- */
- private function doFilterRecursiveDescend($labelParts, $dataTable)
- {
- // search for the first part of the tree search
+ const SEPARATOR_RECURSIVE_LABEL = '>';
+
+ private $labels;
+ private $addEmptyRows;
+
+ /**
+ * Filter a data table by label.
+ * The filtered table is returned, which might be a new instance.
+ *
+ * $apiModule, $apiMethod and $request are needed load sub-datatables
+ * for the recursive search. If the label is not recursive, these parameters
+ * are not needed.
+ *
+ * @param string $labels the labels to search for
+ * @param Piwik_DataTable $dataTable the data table to be filtered
+ * @param bool $addEmptyRows Whether to add empty rows when a row isn't found
+ * for a label, or not.
+ * @return Piwik_DataTable
+ */
+ public function filter($labels, $dataTable, $addEmptyRows = false)
+ {
+ if (!is_array($labels)) {
+ $labels = array($labels);
+ }
+
+ $this->labels = $labels;
+ $this->addEmptyRows = (bool)$addEmptyRows;
+ return $this->manipulate($dataTable);
+ }
+
+ /**
+ * Method for the recursive descend
+ *
+ * @param array $labelParts
+ * @param Piwik_DataTable $dataTable
+ * @return Piwik_DataTable_Row|false
+ */
+ private function doFilterRecursiveDescend($labelParts, $dataTable)
+ {
+ // search for the first part of the tree search
$labelPart = array_shift($labelParts);
-
- foreach ($this->getLabelVariations($labelPart) as $labelPart)
- {
- $row = $dataTable->getRowFromLabel($labelPart);
- if ($row !== false)
- {
- break;
- }
- }
-
- if ($row === false)
- {
- // not found
- return false;
- }
-
- // end of tree search reached
- if (count($labelParts) == 0)
- {
- return $row;
- }
-
- $subTable = $this->loadSubtable($dataTable, $row);
- if ($subTable === null)
- {
- // no more subtables but label parts left => no match found
- return false;
- }
-
- return $this->doFilterRecursiveDescend($labelParts, $subTable);
- }
-
- /**
- * Clean up request for Piwik_API_ResponseBuilder to behave correctly
- *
- * @param $request
- */
- protected function manipulateSubtableRequest(&$request)
- {
- unset($request['label']);
- }
-
- /**
- * Use variations of the label to make it easier to specify the desired label
- *
- * Note: The HTML Encoded version must be tried first, since in Piwik_API_ResponseBuilder the $label is unsanitized
- * via Piwik_Common::unsanitizeInputValue.
- *
- * @param string $label
- * @return array
- */
- private function getLabelVariations($label)
- {
- $variations = array();
- $label = trim($label);
-
- $sanitizedLabel = Piwik_Common::sanitizeInputValue($label);
- $variations[] = $sanitizedLabel;
-
- if ($this->apiModule == 'Actions'
- && $this->apiMethod == 'getPageTitles')
- {
- // special case: the Actions.getPageTitles report prefixes some labels with a blank.
- // the blank might be passed by the user but is removed in Piwik_API_Request::getRequestArrayFromString.
- $variations[] = ' '.$sanitizedLabel;
- $variations[] = ' '.$label;
- }
- $variations[] = $label;
-
- return $variations;
- }
-
- /**
- * Filter a Piwik_DataTable instance. See @filter for more info.
- */
- protected function manipulateDataTable( $dataTable )
- {
- $result = $dataTable->getEmptyClone();
- foreach ($this->labels as $labelIdx => $label)
- {
- $row = null;
- foreach ($this->getLabelVariations($label) as $labelVariation)
- {
- $labelVariation = explode(self::SEPARATOR_RECURSIVE_LABEL, $labelVariation);
- $labelVariation = array_map('urldecode', $labelVariation);
-
- $row = $this->doFilterRecursiveDescend($labelVariation, $dataTable);
- if ($row)
- {
- $result->addRow($row);
- break;
- }
- }
-
- if (empty($row)
- && $this->addEmptyRows) // if no row has been found, add an empty one
- {
- $row = new Piwik_DataTable_Row();
- $row->setColumn('label', $label);
- $result->addRow($row);
- }
- }
- return $result;
- }
+
+ foreach ($this->getLabelVariations($labelPart) as $labelPart) {
+ $row = $dataTable->getRowFromLabel($labelPart);
+ if ($row !== false) {
+ break;
+ }
+ }
+
+ if ($row === false) {
+ // not found
+ return false;
+ }
+
+ // end of tree search reached
+ if (count($labelParts) == 0) {
+ return $row;
+ }
+
+ $subTable = $this->loadSubtable($dataTable, $row);
+ if ($subTable === null) {
+ // no more subtables but label parts left => no match found
+ return false;
+ }
+
+ return $this->doFilterRecursiveDescend($labelParts, $subTable);
+ }
+
+ /**
+ * Clean up request for Piwik_API_ResponseBuilder to behave correctly
+ *
+ * @param $request
+ */
+ protected function manipulateSubtableRequest(&$request)
+ {
+ unset($request['label']);
+ }
+
+ /**
+ * Use variations of the label to make it easier to specify the desired label
+ *
+ * Note: The HTML Encoded version must be tried first, since in Piwik_API_ResponseBuilder the $label is unsanitized
+ * via Piwik_Common::unsanitizeInputValue.
+ *
+ * @param string $label
+ * @return array
+ */
+ private function getLabelVariations($label)
+ {
+ $variations = array();
+ $label = trim($label);
+
+ $sanitizedLabel = Piwik_Common::sanitizeInputValue($label);
+ $variations[] = $sanitizedLabel;
+
+ if ($this->apiModule == 'Actions'
+ && $this->apiMethod == 'getPageTitles'
+ ) {
+ // special case: the Actions.getPageTitles report prefixes some labels with a blank.
+ // the blank might be passed by the user but is removed in Piwik_API_Request::getRequestArrayFromString.
+ $variations[] = ' ' . $sanitizedLabel;
+ $variations[] = ' ' . $label;
+ }
+ $variations[] = $label;
+
+ return $variations;
+ }
+
+ /**
+ * Filter a Piwik_DataTable instance. See @filter for more info.
+ */
+ protected function manipulateDataTable($dataTable)
+ {
+ $result = $dataTable->getEmptyClone();
+ foreach ($this->labels as $labelIdx => $label) {
+ $row = null;
+ foreach ($this->getLabelVariations($label) as $labelVariation) {
+ $labelVariation = explode(self::SEPARATOR_RECURSIVE_LABEL, $labelVariation);
+ $labelVariation = array_map('urldecode', $labelVariation);
+
+ $row = $this->doFilterRecursiveDescend($labelVariation, $dataTable);
+ if ($row) {
+ $result->addRow($row);
+ break;
+ }
+ }
+
+ if (empty($row)
+ && $this->addEmptyRows
+ ) // if no row has been found, add an empty one
+ {
+ $row = new Piwik_DataTable_Row();
+ $row->setColumn('label', $label);
+ $result->addRow($row);
+ }
+ }
+ return $result;
+ }
}
diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php
index 505f35ec04..1c2d9a62a4 100644
--- a/core/API/DocumentationGenerator.php
+++ b/core/API/DocumentationGenerator.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,243 +15,220 @@
*/
class Piwik_API_DocumentationGenerator
{
- protected $modulesToHide = array('CoreAdminHome', 'DBStats');
- protected $countPluginsLoaded = 0;
+ protected $modulesToHide = array('CoreAdminHome', 'DBStats');
+ protected $countPluginsLoaded = 0;
- /**
- * trigger loading all plugins with an API.php file in the Proxy
- */
- public function __construct()
- {
- $plugins = Piwik_PluginsManager::getInstance()->getLoadedPluginsName();
- foreach( $plugins as $plugin )
- {
- $plugin = Piwik::unprefixClass($plugin);
- try {
- Piwik_API_Proxy::getInstance()->registerClass('Piwik_'.$plugin.'_API');
- }
- catch(Exception $e){
- }
- }
- }
+ /**
+ * trigger loading all plugins with an API.php file in the Proxy
+ */
+ public function __construct()
+ {
+ $plugins = Piwik_PluginsManager::getInstance()->getLoadedPluginsName();
+ foreach ($plugins as $plugin) {
+ $plugin = Piwik::unprefixClass($plugin);
+ try {
+ Piwik_API_Proxy::getInstance()->registerClass('Piwik_' . $plugin . '_API');
+ } catch (Exception $e) {
+ }
+ }
+ }
- /**
- * Returns a HTML page containing help for all the successfully loaded APIs.
- * For each module it will return a mini help with the method names, parameters to give,
- * links to get the result in Xml/Csv/etc
- *
- * @param bool $outputExampleUrls
- * @param string $prefixUrls
- * @return string
- */
- public function getAllInterfaceString( $outputExampleUrls = true, $prefixUrls = '' )
- {
- if(!empty($prefixUrls)) {
- $prefixUrls = 'http://demo.piwik.org/';
- }
- $str = $toc = '';
- $token_auth = "&token_auth=" . Piwik::getCurrentUserTokenAuth();
- $parametersToSet = array(
- 'idSite' => Piwik_Common::getRequestVar('idSite', 1, 'int'),
- 'period' => Piwik_Common::getRequestVar('period', 'day', 'string'),
- 'date' => Piwik_Common::getRequestVar('date', 'today', 'string')
- );
-
- foreach(Piwik_API_Proxy::getInstance()->getMetadata() as $class => $info)
- {
- $moduleName = Piwik_API_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))
- {
- $exampleUrlRss1 = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet) ;
- $exampleUrlRss2 = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last5','period' => 'week',) + $parametersToSet );
- $lastNUrls = ", RSS of the last <a target=_blank href='$exampleUrlRss1&format=rss$token_auth&translateColumnNames=1'>10 days</a>";
- }
- $exampleUrl = $prefixUrls . $exampleUrl ;
- $str .= " [ Example in
+ /**
+ * Returns a HTML page containing help for all the successfully loaded APIs.
+ * For each module it will return a mini help with the method names, parameters to give,
+ * links to get the result in Xml/Csv/etc
+ *
+ * @param bool $outputExampleUrls
+ * @param string $prefixUrls
+ * @return string
+ */
+ public function getAllInterfaceString($outputExampleUrls = true, $prefixUrls = '')
+ {
+ if (!empty($prefixUrls)) {
+ $prefixUrls = 'http://demo.piwik.org/';
+ }
+ $str = $toc = '';
+ $token_auth = "&token_auth=" . Piwik::getCurrentUserTokenAuth();
+ $parametersToSet = array(
+ 'idSite' => Piwik_Common::getRequestVar('idSite', 1, 'int'),
+ 'period' => Piwik_Common::getRequestVar('period', 'day', 'string'),
+ 'date' => Piwik_Common::getRequestVar('date', 'today', 'string')
+ );
+
+ foreach (Piwik_API_Proxy::getInstance()->getMetadata() as $class => $info) {
+ $moduleName = Piwik_API_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)) {
+ $exampleUrlRss1 = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet);
+ $exampleUrlRss2 = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last5', 'period' => 'week',) + $parametersToSet);
+ $lastNUrls = ", RSS of the last <a target=_blank href='$exampleUrlRss1&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" style="color:#95AECB">↑ Back to top</a></div>';
- }
-
- $str = "<h2 id='topApiRef' name='topApiRef'>Quick access to APIs</h2>
+ } else {
+ $str .= " [ No example available ]";
+ }
+ $str .= "</span>";
+ }
+ $str .= '</small>';
+ $str .= "</div>\n";
+ }
+ $str .= '<div style="margin:15px;"><a href="#topApiRef" style="color:#95AECB">↑ Back to top</a></div>';
+ }
+
+ $str = "<h2 id='topApiRef' name='topApiRef'>Quick access to APIs</h2>
$toc
$str";
- return $str;
- }
+ return $str;
+ }
+
+ /**
+ * Returns a string containing links to examples on how to call a given method on a given API
+ * It will export links to XML, CSV, HTML, JSON, PHP, etc.
+ * It will not export links for methods such as deleteSite or deleteUser
+ *
+ * @param string $class the class
+ * @param string $methodName the method
+ * @param array $parametersToSet parameters to set
+ * @return string|false when not possible
+ */
+ public function getExampleUrl($class, $methodName, $parametersToSet = array())
+ {
+ $knowExampleDefaultParametersValues = array(
+ 'access' => 'view',
+ 'userLogin' => 'test',
+ 'passwordMd5ied' => 'passwordExample',
+ 'email' => 'test@example.org',
+
+ 'languageCode' => 'fr',
+ 'url' => 'http://forum.piwik.org/',
+ 'apiModule' => 'UserCountry',
+ 'apiAction' => 'getCountry',
+ 'lastMinutes' => '30',
+ 'abandonedCarts' => '0',
+ 'ip' => '194.57.91.215',
+ );
+
+ foreach ($parametersToSet as $name => $value) {
+ $knowExampleDefaultParametersValues[$name] = $value;
+ }
- /**
- * Returns a string containing links to examples on how to call a given method on a given API
- * It will export links to XML, CSV, HTML, JSON, PHP, etc.
- * It will not export links for methods such as deleteSite or deleteUser
- *
- * @param string $class the class
- * @param string $methodName the method
- * @param array $parametersToSet parameters to set
- * @return string|false when not possible
- */
- public function getExampleUrl($class, $methodName, $parametersToSet = array())
- {
- $knowExampleDefaultParametersValues = array(
- 'access' => 'view',
- 'userLogin' => 'test',
- 'passwordMd5ied' => 'passwordExample',
- 'email' => 'test@example.org',
-
- 'languageCode' => 'fr',
- 'url' => 'http://forum.piwik.org/',
- 'apiModule' => 'UserCountry',
- 'apiAction' => 'getCountry',
- 'lastMinutes' => '30',
- 'abandonedCarts' => '0',
- 'ip' => '194.57.91.215',
- );
-
- foreach($parametersToSet as $name => $value)
- {
- $knowExampleDefaultParametersValues[$name] = $value;
- }
-
- // no links for these method names
- $doNotPrintExampleForTheseMethods = array(
- //Sites
- 'deleteSite',
- 'addSite',
- 'updateSite',
- 'addSiteAliasUrls',
- //Users
- 'deleteUser',
- 'addUser',
- 'updateUser',
- 'setUserAccess',
- //Goals
- 'addGoal',
- 'updateGoal',
- 'deleteGoal',
- );
-
- if(in_array($methodName,$doNotPrintExampleForTheseMethods))
- {
- return false;
- }
-
- // we try to give an URL example to call the API
- $aParameters = Piwik_API_Proxy::getInstance()->getParametersList($class, $methodName);
- // Kindly force some known generic parameters to appear in the final list
- // the parameter 'format' can be set to all API methods (used in tests)
- // the parameter 'hideIdSubDatable' is used for integration tests only
- // the parameter 'serialize' sets php outputs human readable, used in integration tests and debug
- // the parameter 'language' sets the language for the response (eg. country names)
- // the parameter 'flat' reduces a hierarchical table to a single level by concatenating labels
- // the parameter 'include_aggregate_rows' can be set to include inner nodes in flat reports
- // the parameter 'translateColumnNames' can be set to translate metric names in csv/tsv exports
- $aParameters['format'] = false;
- $aParameters['hideIdSubDatable'] = false;
- $aParameters['serialize'] = false;
- $aParameters['language'] = false;
- $aParameters['translateColumnNames'] = false;
+ // no links for these method names
+ $doNotPrintExampleForTheseMethods = array(
+ //Sites
+ 'deleteSite',
+ 'addSite',
+ 'updateSite',
+ 'addSiteAliasUrls',
+ //Users
+ 'deleteUser',
+ 'addUser',
+ 'updateUser',
+ 'setUserAccess',
+ //Goals
+ 'addGoal',
+ 'updateGoal',
+ 'deleteGoal',
+ );
+
+ if (in_array($methodName, $doNotPrintExampleForTheseMethods)) {
+ return false;
+ }
+
+ // we try to give an URL example to call the API
+ $aParameters = Piwik_API_Proxy::getInstance()->getParametersList($class, $methodName);
+ // Kindly force some known generic parameters to appear in the final list
+ // the parameter 'format' can be set to all API methods (used in tests)
+ // the parameter 'hideIdSubDatable' is used for integration tests only
+ // the parameter 'serialize' sets php outputs human readable, used in integration tests and debug
+ // the parameter 'language' sets the language for the response (eg. country names)
+ // the parameter 'flat' reduces a hierarchical table to a single level by concatenating labels
+ // the parameter 'include_aggregate_rows' can be set to include inner nodes in flat reports
+ // the parameter 'translateColumnNames' can be set to translate metric names in csv/tsv exports
+ $aParameters['format'] = false;
+ $aParameters['hideIdSubDatable'] = false;
+ $aParameters['serialize'] = false;
+ $aParameters['language'] = false;
+ $aParameters['translateColumnNames'] = false;
$aParameters['label'] = false;
- $aParameters['flat'] = false;
- $aParameters['include_aggregate_rows'] = false;
+ $aParameters['flat'] = false;
+ $aParameters['include_aggregate_rows'] = false;
$aParameters['filter_limit'] = false; //@review without adding this, I can not set filter_limit in $otherRequestParameters integration tests
$aParameters['filter_sort_column'] = false; //@review without adding this, I can not set filter_sort_column in $otherRequestParameters integration tests
$aParameters['filter_truncate'] = false;
$aParameters['hideColumns'] = false;
$aParameters['showColumns'] = false;
$aParameters['filter_pattern_recursive'] = false;
-
- $moduleName = Piwik_API_Proxy::getInstance()->getModuleNameFromClassName($class);
- $aParameters = array_merge(array('module' => 'API', 'method' => $moduleName.'.'.$methodName), $aParameters);
-
- foreach($aParameters as $nameVariable => &$defaultValue)
- {
- if(isset($knowExampleDefaultParametersValues[$nameVariable]))
- {
- $defaultValue = $knowExampleDefaultParametersValues[$nameVariable];
- }
- // if there isn't a default value for a given parameter,
- // we need a 'know default value' or we can't generate the link
- elseif($defaultValue instanceof Piwik_API_Proxy_NoDefaultValue)
- {
- return false;
- }
- }
- return '?'.Piwik_Url::getQueryStringFromParameters($aParameters);
- }
-
-
- /**
- * Returns the methods $class.$name parameters (and default value if provided) as a string.
- *
- * @param string $class The class name
- * @param string $name The method name
- * @return string For example "(idSite, period, date = 'today')"
- */
- public function getParametersString($class, $name)
- {
- $aParameters = Piwik_API_Proxy::getInstance()->getParametersList($class, $name);
- $asParameters = array();
- foreach($aParameters as $nameVariable=> $defaultValue)
- {
- // Do not show API parameters starting with _
- // They are supposed to be used only in internal API calls
- if(strpos($nameVariable, '_') === 0)
- {
- continue;
- }
- $str = $nameVariable;
- if(!($defaultValue instanceof Piwik_API_Proxy_NoDefaultValue))
- {
- if (is_array($defaultValue))
- {
- $str .= " = 'Array'";
- }
- else
- {
- $str .= " = '$defaultValue'";
- }
- }
- $asParameters[] = $str;
- }
- $sParameters = implode(", ", $asParameters);
- return "($sParameters)";
- }
+
+ $moduleName = Piwik_API_Proxy::getInstance()->getModuleNameFromClassName($class);
+ $aParameters = array_merge(array('module' => 'API', 'method' => $moduleName . '.' . $methodName), $aParameters);
+
+ foreach ($aParameters as $nameVariable => &$defaultValue) {
+ if (isset($knowExampleDefaultParametersValues[$nameVariable])) {
+ $defaultValue = $knowExampleDefaultParametersValues[$nameVariable];
+ } // if there isn't a default value for a given parameter,
+ // we need a 'know default value' or we can't generate the link
+ elseif ($defaultValue instanceof Piwik_API_Proxy_NoDefaultValue) {
+ return false;
+ }
+ }
+ return '?' . Piwik_Url::getQueryStringFromParameters($aParameters);
+ }
+
+
+ /**
+ * Returns the methods $class.$name parameters (and default value if provided) as a string.
+ *
+ * @param string $class The class name
+ * @param string $name The method name
+ * @return string For example "(idSite, period, date = 'today')"
+ */
+ public function getParametersString($class, $name)
+ {
+ $aParameters = Piwik_API_Proxy::getInstance()->getParametersList($class, $name);
+ $asParameters = array();
+ foreach ($aParameters as $nameVariable => $defaultValue) {
+ // Do not show API parameters starting with _
+ // They are supposed to be used only in internal API calls
+ if (strpos($nameVariable, '_') === 0) {
+ continue;
+ }
+ $str = $nameVariable;
+ if (!($defaultValue instanceof Piwik_API_Proxy_NoDefaultValue)) {
+ if (is_array($defaultValue)) {
+ $str .= " = 'Array'";
+ } else {
+ $str .= " = '$defaultValue'";
+ }
+ }
+ $asParameters[] = $str;
+ }
+ $sParameters = implode(", ", $asParameters);
+ return "($sParameters)";
+ }
}
diff --git a/core/API/Proxy.php b/core/API/Proxy.php
index 3619e0fc87..727d291b29 100644
--- a/core/API/Proxy.php
+++ b/core/API/Proxy.php
@@ -1,419 +1,402 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* To differentiate between "no value" and default value of null
- *
+ *
* @package Piwik
* @subpackage Piwik_API
*/
-class Piwik_API_Proxy_NoDefaultValue {}
+class Piwik_API_Proxy_NoDefaultValue
+{
+}
/**
- * Proxy is a singleton that has the knowledge of every method available, their parameters
+ * Proxy is a singleton that has the knowledge of every method available, their parameters
* and default values.
- * Proxy receives all the API calls requests via call() and forwards them to the right
- * object, with the parameters in the right order.
- *
+ * Proxy receives all the API calls requests via call() and forwards them to the right
+ * object, with the parameters in the right order.
+ *
* It will also log the performance of API calls (time spent, parameter values, etc.) if logger available
- *
+ *
* @package Piwik
* @subpackage Piwik_API
*/
class Piwik_API_Proxy
{
- // array of already registered plugins names
- protected $alreadyRegistered = array();
-
- private $metadataArray = array();
- private $hideIgnoredFunctions = true;
-
- // when a parameter doesn't have a default value we use this
- private $noDefaultValue;
-
- /**
- * Singleton instance
- * @var self|null
- */
- static private $instance = null;
-
- /**
- * protected constructor
- */
- protected function __construct()
- {
- $this->noDefaultValue = new Piwik_API_Proxy_NoDefaultValue();
- }
-
- /**
- * Singleton, returns instance
- *
- * @return Piwik_API_Proxy
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Returns array containing reflection meta data for all the loaded classes
- * eg. number of parameters, method names, etc.
- *
- * @return array
- */
- public function getMetadata()
- {
- ksort($this->metadataArray);
- return $this->metadataArray;
- }
-
- /**
- * Registers the API information of a given module.
- *
- * The module to be registered must be
- * - a singleton (providing a getInstance() method)
- * - the API file must be located in plugins/ModuleName/API.php
- * for example plugins/Referers/API.php
- *
- * The method will introspect the methods, their parameters, etc.
- *
- * @param string $className ModuleName eg. "Piwik_UserSettings_API"
- */
- public function registerClass( $className )
- {
- if(isset($this->alreadyRegistered[$className]))
- {
- return;
- }
- $this->includeApiFile( $className );
- $this->checkClassIsSingleton($className);
-
- $rClass = new ReflectionClass($className);
- foreach($rClass->getMethods() as $method)
- {
- $this->loadMethodMetadata($className, $method);
- }
-
- $this->setDocumentation($rClass, $className);
- $this->alreadyRegistered[$className] = true;
- }
-
- /**
- * Will be displayed in the API page
- *
- * @param ReflectionClass $rClass Instance of ReflectionClass
- * @param string $className Name of the class
- */
- private function setDocumentation($rClass, $className)
- {
- // Doc comment
- $doc = $rClass->getDocComment();
- $doc = str_replace(" * ".PHP_EOL, "<br>", $doc);
-
- // boldify the first line only if there is more than one line, otherwise too much bold
- if(substr_count($doc, '<br>') > 1)
- {
- $firstLineBreak = strpos($doc, "<br>");
- $doc = "<div class='apiFirstLine'>".substr($doc, 0, $firstLineBreak)."</div>".substr($doc,$firstLineBreak+strlen("<br>"));
- }
- $doc = preg_replace("/(@package)[a-z _A-Z]*/", "", $doc);
- $doc = str_replace(array("\t","\n", "/**", "*/", " * "," *"," ", "\t*", " * @package"), " ", $doc);
- $this->metadataArray[$className]['__documentation'] = $doc;
- }
-
- /**
- * Returns number of classes already loaded
- * @return int
- */
- public function getCountRegisteredClasses()
- {
- return count($this->alreadyRegistered);
- }
-
- /**
- * Will execute $className->$methodName($parametersValues)
- * If any error is detected (wrong number of parameters, method not found, class not found, etc.)
- * it will throw an exception
- *
- * It also logs the API calls, with the parameters values, the returned value, the performance, etc.
- * You can enable logging in config/global.ini.php (log_api_call)
- *
- * @param string $className The class name (eg. Piwik_Referers_API)
- * @param string $methodName The method name
- * @param array $parametersRequest The parameters pairs (name=>value)
- *
- * @return mixed|null
- * @throws Exception|Piwik_Access_NoAccessException
- */
- public function call($className, $methodName, $parametersRequest )
- {
- $returnedValue = null;
-
- // Temporarily sets the Request array to this API call context
- $saveGET = $_GET;
- foreach($parametersRequest as $param => $value) {
- $_GET[$param] = $value;
- }
-
- try {
- $this->registerClass($className);
-
- // instanciate the object
- $object = call_user_func(array($className, "getInstance"));
-
- // check method exists
- $this->checkMethodExists($className, $methodName);
-
- // get the list of parameters required by the method
- $parameterNamesDefaultValues = $this->getParametersList($className, $methodName);
-
- // load parameters in the right order, etc.
- $finalParameters = $this->getRequestParametersArray( $parameterNamesDefaultValues, $parametersRequest );
-
- // start the timer
- $timer = new Piwik_Timer();
-
- // call the method
- $returnedValue = call_user_func_array(array($object, $methodName), $finalParameters);
-
- // allow plugins to manipulate the value
- if (substr($className, 0, 6) == 'Piwik_' && substr($className, -4) == '_API')
- {
- $pluginName = substr($className, 6, -4);
- Piwik_PostEvent('API.Proxy.processReturnValue', $returnedValue, array(
- 'className' => $className,
- 'module' => $pluginName,
- 'action' => $methodName,
- 'parameters' => &$parametersRequest
- ));
- }
-
- // Restore the request
- $_GET = $saveGET;
-
- // log the API Call
- try {
- Zend_Registry::get('logger_api_call')->logEvent(
- $className,
- $methodName,
- $parameterNamesDefaultValues,
- $finalParameters,
- $timer->getTimeMs(),
- $returnedValue
- );
- } catch (Exception $e) {
- // logger can fail (eg. Tracker request)
- }
- } catch (Exception $e) {
- $_GET = $saveGET;
- throw $e;
- }
-
- return $returnedValue;
- }
-
- /**
- * Returns the parameters names and default values for the method $name
- * of the class $class
- *
- * @param string $class The class name
- * @param string $name The method name
- * @return array Format array(
- * 'testParameter' => null, // no default value
- * 'life' => 42, // default value = 42
- * 'date' => 'yesterday',
- * );
- */
- public function getParametersList($class, $name)
- {
- return $this->metadataArray[$class][$name]['parameters'];
- }
-
- /**
- * Returns the 'moduleName' part of 'Piwik_moduleName_API' classname
- *
- * @param string $className "Piwik_Referers_API"
- * @return string "Referers"
- */
- public function getModuleNameFromClassName( $className )
- {
- return str_replace(array('Piwik_', '_API'), '', $className);
- }
-
- /**
- * Sets whether to hide '@ignore'd functions from method metadata or not.
- *
- * @param bool $hideIgnoredFunctions
- */
- public function setHideIgnoredFunctions( $hideIgnoredFunctions )
- {
- $this->hideIgnoredFunctions = $hideIgnoredFunctions;
-
- // make sure metadata gets reloaded
- $this->alreadyRegistered = array();
- $this->metadataArray = array();
- }
-
- /**
- * Returns an array containing the values of the parameters to pass to the method to call
- *
- * @param array $requiredParameters array of (parameter name, default value)
- * @param array $parametersRequest
- * @throws Exception
- * @return array values to pass to the function call
- */
- private function getRequestParametersArray( $requiredParameters, $parametersRequest )
- {
- $finalParameters = array();
- foreach($requiredParameters as $name => $defaultValue)
- {
- try{
- if($defaultValue instanceof Piwik_API_Proxy_NoDefaultValue)
- {
- $requestValue = Piwik_Common::getRequestVar($name, null, null, $parametersRequest);
- }
- else
- {
- try{
- $requestValue = Piwik_Common::getRequestVar($name, $defaultValue, null, $parametersRequest);
- } catch(Exception $e) {
- // Special case: empty parameter in the URL, should return the empty string
- if(isset($parametersRequest[$name])
- && $parametersRequest[$name] === '')
- {
- $requestValue = '';
- }
- else
- {
- $requestValue = $defaultValue;
- }
- }
- }
- } catch(Exception $e) {
- throw new Exception(Piwik_TranslateException('General_PleaseSpecifyValue', array($name)));
- }
- $finalParameters[] = $requestValue;
- }
- return $finalParameters;
- }
-
- /**
- * Includes the class Piwik_UserSettings_API by looking up plugins/UserSettings/API.php
- *
- * @param string $fileName api class name eg. "Piwik_UserSettings_API"
- * @throws Exception
- */
- private function includeApiFile($fileName)
- {
- $module = self::getModuleNameFromClassName($fileName);
- $path = PIWIK_INCLUDE_PATH . '/plugins/' . $module . '/API.php';
-
- if(is_readable($path))
- {
- require_once $path; // prefixed by PIWIK_INCLUDE_PATH
- }
- else
- {
- throw new Exception("API module $module not found.");
- }
- }
-
- /**
- * @param string $class name of a class
- * @param ReflectionMethod $method instance of ReflectionMethod
- */
- private function loadMethodMetadata($class, $method)
- {
- if($method->isPublic()
- && !$method->isConstructor()
- && $method->getName() != 'getInstance'
- && false === strstr($method->getDocComment(), '@deprecated')
- && (!$this->hideIgnoredFunctions || false === strstr($method->getDocComment(), '@ignore'))
- )
- {
- $name = $method->getName();
- $parameters = $method->getParameters();
-
- $aParameters = array();
- foreach($parameters as $parameter)
- {
- $nameVariable = $parameter->getName();
-
- $defaultValue = $this->noDefaultValue;
- if($parameter->isDefaultValueAvailable())
- {
- $defaultValue = $parameter->getDefaultValue();
- }
-
- $aParameters[$nameVariable] = $defaultValue;
- }
- $this->metadataArray[$class][$name]['parameters'] = $aParameters;
- $this->metadataArray[$class][$name]['numberOfRequiredParameters'] = $method->getNumberOfRequiredParameters();
- }
- }
-
- /**
- * Checks that the method exists in the class
- *
- * @param string $className The class name
- * @param string $methodName The method name
- * @throws Exception If the method is not found
- */
- private function checkMethodExists($className, $methodName)
- {
- if(!$this->isMethodAvailable($className, $methodName))
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionMethodNotFound', array($methodName,$className)));
- }
- }
-
- /**
- * Returns the number of required parameters (parameters without default values).
- *
- * @param string $class The class name
- * @param string $name The method name
- * @return int The number of required parameters
- */
- private function getNumberOfRequiredParameters($class, $name)
- {
- return $this->metadataArray[$class][$name]['numberOfRequiredParameters'];
- }
-
- /**
- * Returns true if the method is found in the API of the given class name.
- *
- * @param string $className The class name
- * @param string $methodName The method name
- * @return bool
- */
- private function isMethodAvailable( $className, $methodName)
- {
- return isset($this->metadataArray[$className][$methodName]);
- }
-
- /**
- * Checks that the class is a Singleton (presence of the getInstance() method)
- *
- * @param string $className The class name
- * @throws Exception If the class is not a Singleton
- */
- private function checkClassIsSingleton($className)
- {
- if(!method_exists($className, "getInstance"))
- {
- throw new Exception("Objects that provide an API must be Singleton and have a 'static public function getInstance()' method.");
- }
- }
+ // array of already registered plugins names
+ protected $alreadyRegistered = array();
+
+ private $metadataArray = array();
+ private $hideIgnoredFunctions = true;
+
+ // when a parameter doesn't have a default value we use this
+ private $noDefaultValue;
+
+ /**
+ * Singleton instance
+ * @var self|null
+ */
+ static private $instance = null;
+
+ /**
+ * protected constructor
+ */
+ protected function __construct()
+ {
+ $this->noDefaultValue = new Piwik_API_Proxy_NoDefaultValue();
+ }
+
+ /**
+ * Singleton, returns instance
+ *
+ * @return Piwik_API_Proxy
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Returns array containing reflection meta data for all the loaded classes
+ * eg. number of parameters, method names, etc.
+ *
+ * @return array
+ */
+ public function getMetadata()
+ {
+ ksort($this->metadataArray);
+ return $this->metadataArray;
+ }
+
+ /**
+ * Registers the API information of a given module.
+ *
+ * The module to be registered must be
+ * - a singleton (providing a getInstance() method)
+ * - the API file must be located in plugins/ModuleName/API.php
+ * for example plugins/Referers/API.php
+ *
+ * The method will introspect the methods, their parameters, etc.
+ *
+ * @param string $className ModuleName eg. "Piwik_UserSettings_API"
+ */
+ public function registerClass($className)
+ {
+ if (isset($this->alreadyRegistered[$className])) {
+ return;
+ }
+ $this->includeApiFile($className);
+ $this->checkClassIsSingleton($className);
+
+ $rClass = new ReflectionClass($className);
+ foreach ($rClass->getMethods() as $method) {
+ $this->loadMethodMetadata($className, $method);
+ }
+
+ $this->setDocumentation($rClass, $className);
+ $this->alreadyRegistered[$className] = true;
+ }
+
+ /**
+ * Will be displayed in the API page
+ *
+ * @param ReflectionClass $rClass Instance of ReflectionClass
+ * @param string $className Name of the class
+ */
+ private function setDocumentation($rClass, $className)
+ {
+ // Doc comment
+ $doc = $rClass->getDocComment();
+ $doc = str_replace(" * " . PHP_EOL, "<br>", $doc);
+
+ // boldify the first line only if there is more than one line, otherwise too much bold
+ if (substr_count($doc, '<br>') > 1) {
+ $firstLineBreak = strpos($doc, "<br>");
+ $doc = "<div class='apiFirstLine'>" . substr($doc, 0, $firstLineBreak) . "</div>" . substr($doc, $firstLineBreak + strlen("<br>"));
+ }
+ $doc = preg_replace("/(@package)[a-z _A-Z]*/", "", $doc);
+ $doc = str_replace(array("\t", "\n", "/**", "*/", " * ", " *", " ", "\t*", " * @package"), " ", $doc);
+ $this->metadataArray[$className]['__documentation'] = $doc;
+ }
+
+ /**
+ * Returns number of classes already loaded
+ * @return int
+ */
+ public function getCountRegisteredClasses()
+ {
+ return count($this->alreadyRegistered);
+ }
+
+ /**
+ * Will execute $className->$methodName($parametersValues)
+ * If any error is detected (wrong number of parameters, method not found, class not found, etc.)
+ * it will throw an exception
+ *
+ * It also logs the API calls, with the parameters values, the returned value, the performance, etc.
+ * You can enable logging in config/global.ini.php (log_api_call)
+ *
+ * @param string $className The class name (eg. Piwik_Referers_API)
+ * @param string $methodName The method name
+ * @param array $parametersRequest The parameters pairs (name=>value)
+ *
+ * @return mixed|null
+ * @throws Exception|Piwik_Access_NoAccessException
+ */
+ public function call($className, $methodName, $parametersRequest)
+ {
+ $returnedValue = null;
+
+ // Temporarily sets the Request array to this API call context
+ $saveGET = $_GET;
+ foreach ($parametersRequest as $param => $value) {
+ $_GET[$param] = $value;
+ }
+
+ try {
+ $this->registerClass($className);
+
+ // instanciate the object
+ $object = call_user_func(array($className, "getInstance"));
+
+ // check method exists
+ $this->checkMethodExists($className, $methodName);
+
+ // get the list of parameters required by the method
+ $parameterNamesDefaultValues = $this->getParametersList($className, $methodName);
+
+ // load parameters in the right order, etc.
+ $finalParameters = $this->getRequestParametersArray($parameterNamesDefaultValues, $parametersRequest);
+
+ // start the timer
+ $timer = new Piwik_Timer();
+
+ // call the method
+ $returnedValue = call_user_func_array(array($object, $methodName), $finalParameters);
+
+ // allow plugins to manipulate the value
+ if (substr($className, 0, 6) == 'Piwik_' && substr($className, -4) == '_API') {
+ $pluginName = substr($className, 6, -4);
+ Piwik_PostEvent('API.Proxy.processReturnValue', $returnedValue, array(
+ 'className' => $className,
+ 'module' => $pluginName,
+ 'action' => $methodName,
+ 'parameters' => &$parametersRequest
+ ));
+ }
+
+ // Restore the request
+ $_GET = $saveGET;
+
+ // log the API Call
+ try {
+ Zend_Registry::get('logger_api_call')->logEvent(
+ $className,
+ $methodName,
+ $parameterNamesDefaultValues,
+ $finalParameters,
+ $timer->getTimeMs(),
+ $returnedValue
+ );
+ } catch (Exception $e) {
+ // logger can fail (eg. Tracker request)
+ }
+ } catch (Exception $e) {
+ $_GET = $saveGET;
+ throw $e;
+ }
+
+ return $returnedValue;
+ }
+
+ /**
+ * Returns the parameters names and default values for the method $name
+ * of the class $class
+ *
+ * @param string $class The class name
+ * @param string $name The method name
+ * @return array Format array(
+ * 'testParameter' => null, // no default value
+ * 'life' => 42, // default value = 42
+ * 'date' => 'yesterday',
+ * );
+ */
+ public function getParametersList($class, $name)
+ {
+ return $this->metadataArray[$class][$name]['parameters'];
+ }
+
+ /**
+ * Returns the 'moduleName' part of 'Piwik_moduleName_API' classname
+ *
+ * @param string $className "Piwik_Referers_API"
+ * @return string "Referers"
+ */
+ public function getModuleNameFromClassName($className)
+ {
+ return str_replace(array('Piwik_', '_API'), '', $className);
+ }
+
+ /**
+ * Sets whether to hide '@ignore'd functions from method metadata or not.
+ *
+ * @param bool $hideIgnoredFunctions
+ */
+ public function setHideIgnoredFunctions($hideIgnoredFunctions)
+ {
+ $this->hideIgnoredFunctions = $hideIgnoredFunctions;
+
+ // make sure metadata gets reloaded
+ $this->alreadyRegistered = array();
+ $this->metadataArray = array();
+ }
+
+ /**
+ * Returns an array containing the values of the parameters to pass to the method to call
+ *
+ * @param array $requiredParameters array of (parameter name, default value)
+ * @param array $parametersRequest
+ * @throws Exception
+ * @return array values to pass to the function call
+ */
+ private function getRequestParametersArray($requiredParameters, $parametersRequest)
+ {
+ $finalParameters = array();
+ foreach ($requiredParameters as $name => $defaultValue) {
+ try {
+ if ($defaultValue instanceof Piwik_API_Proxy_NoDefaultValue) {
+ $requestValue = Piwik_Common::getRequestVar($name, null, null, $parametersRequest);
+ } else {
+ try {
+ $requestValue = Piwik_Common::getRequestVar($name, $defaultValue, null, $parametersRequest);
+ } catch (Exception $e) {
+ // Special case: empty parameter in the URL, should return the empty string
+ if (isset($parametersRequest[$name])
+ && $parametersRequest[$name] === ''
+ ) {
+ $requestValue = '';
+ } else {
+ $requestValue = $defaultValue;
+ }
+ }
+ }
+ } catch (Exception $e) {
+ throw new Exception(Piwik_TranslateException('General_PleaseSpecifyValue', array($name)));
+ }
+ $finalParameters[] = $requestValue;
+ }
+ return $finalParameters;
+ }
+
+ /**
+ * Includes the class Piwik_UserSettings_API by looking up plugins/UserSettings/API.php
+ *
+ * @param string $fileName api class name eg. "Piwik_UserSettings_API"
+ * @throws Exception
+ */
+ private function includeApiFile($fileName)
+ {
+ $module = self::getModuleNameFromClassName($fileName);
+ $path = PIWIK_INCLUDE_PATH . '/plugins/' . $module . '/API.php';
+
+ if (is_readable($path)) {
+ require_once $path; // prefixed by PIWIK_INCLUDE_PATH
+ } else {
+ throw new Exception("API module $module not found.");
+ }
+ }
+
+ /**
+ * @param string $class name of a class
+ * @param ReflectionMethod $method instance of ReflectionMethod
+ */
+ private function loadMethodMetadata($class, $method)
+ {
+ if ($method->isPublic()
+ && !$method->isConstructor()
+ && $method->getName() != 'getInstance'
+ && false === strstr($method->getDocComment(), '@deprecated')
+ && (!$this->hideIgnoredFunctions || false === strstr($method->getDocComment(), '@ignore'))
+ ) {
+ $name = $method->getName();
+ $parameters = $method->getParameters();
+
+ $aParameters = array();
+ foreach ($parameters as $parameter) {
+ $nameVariable = $parameter->getName();
+
+ $defaultValue = $this->noDefaultValue;
+ if ($parameter->isDefaultValueAvailable()) {
+ $defaultValue = $parameter->getDefaultValue();
+ }
+
+ $aParameters[$nameVariable] = $defaultValue;
+ }
+ $this->metadataArray[$class][$name]['parameters'] = $aParameters;
+ $this->metadataArray[$class][$name]['numberOfRequiredParameters'] = $method->getNumberOfRequiredParameters();
+ }
+ }
+
+ /**
+ * Checks that the method exists in the class
+ *
+ * @param string $className The class name
+ * @param string $methodName The method name
+ * @throws Exception If the method is not found
+ */
+ private function checkMethodExists($className, $methodName)
+ {
+ if (!$this->isMethodAvailable($className, $methodName)) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionMethodNotFound', array($methodName, $className)));
+ }
+ }
+
+ /**
+ * Returns the number of required parameters (parameters without default values).
+ *
+ * @param string $class The class name
+ * @param string $name The method name
+ * @return int The number of required parameters
+ */
+ private function getNumberOfRequiredParameters($class, $name)
+ {
+ return $this->metadataArray[$class][$name]['numberOfRequiredParameters'];
+ }
+
+ /**
+ * Returns true if the method is found in the API of the given class name.
+ *
+ * @param string $className The class name
+ * @param string $methodName The method name
+ * @return bool
+ */
+ private function isMethodAvailable($className, $methodName)
+ {
+ return isset($this->metadataArray[$className][$methodName]);
+ }
+
+ /**
+ * Checks that the class is a Singleton (presence of the getInstance() method)
+ *
+ * @param string $className The class name
+ * @throws Exception If the class is not a Singleton
+ */
+ private function checkClassIsSingleton($className)
+ {
+ if (!method_exists($className, "getInstance")) {
+ throw new Exception("Objects that provide an API must be Singleton and have a 'static public function getInstance()' method.");
+ }
+ }
}
diff --git a/core/API/Request.php b/core/API/Request.php
index 1c0b970cf3..2abf779c52 100644
--- a/core/API/Request.php
+++ b/core/API/Request.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,183 +12,175 @@
/**
* An API request is the object used to make a call to the API and get the result.
* The request has the format of a normal GET request, ie. parameter_1=X&parameter_2=Y
- *
+ *
* You can use this object from anywhere in piwik (inside plugins for example).
* You can even call it outside of piwik using the REST API over http
* or in a php script on the same server as piwik, by including piwik/index.php
* (see examples in the documentation http://piwik.org/docs/analytics-api)
- *
- * Example:
+ *
+ * Example:
* $request = new Piwik_API_Request('
- * method=UserSettings.getWideScreen
- * &idSite=1
- * &date=yesterday
- * &period=week
- * &format=xml
- * &filter_limit=5
- * &filter_offset=0
- * ');
- * $result = $request->process();
+ * method=UserSettings.getWideScreen
+ * &idSite=1
+ * &date=yesterday
+ * &period=week
+ * &format=xml
+ * &filter_limit=5
+ * &filter_offset=0
+ * ');
+ * $result = $request->process();
* echo $result;
- *
+ *
* @see http://piwik.org/docs/analytics-api
* @package Piwik
* @subpackage Piwik_API
*/
class Piwik_API_Request
-{
- protected $request = null;
-
- /**
- * Returns the request array as string
- *
- * @param string|array $request
- * @return array|null
- */
- static public function getRequestArrayFromString($request)
- {
- $defaultRequest = $_GET + $_POST;
- $requestArray = $defaultRequest;
-
- if(!is_null($request))
- {
- if(is_array($request))
- {
+{
+ protected $request = null;
+
+ /**
+ * Returns the request array as string
+ *
+ * @param string|array $request
+ * @return array|null
+ */
+ static public function getRequestArrayFromString($request)
+ {
+ $defaultRequest = $_GET + $_POST;
+ $requestArray = $defaultRequest;
+
+ if (!is_null($request)) {
+ if (is_array($request)) {
$url = array();
- foreach ($request as $key => $value)
- {
+ foreach ($request as $key => $value) {
$url[] = $key . "=" . $value;
}
$request = implode("&", $url);
}
- $request = trim($request);
- $request = str_replace(array("\n","\t"),'', $request);
- parse_str($request, $requestArray);
-
- $requestArray = $requestArray + $defaultRequest;
- }
-
- foreach($requestArray as &$element)
- {
- if(!is_array($element))
- {
- $element = trim($element);
- }
- }
- return $requestArray;
- }
-
- /**
- * Constructs the request to the API, given the request url
- *
- * @param string $request GET request that defines the API call (must at least contain a "method" parameter)
- * Example: method=UserSettings.getWideScreen&idSite=1&date=yesterday&period=week&format=xml
- * If a request is not provided, then we use the $_GET and $_POST superglobal and fetch
- * the values directly from the HTTP GET query.
- */
- function __construct($request = null)
- {
- $this->request = self::getRequestArrayFromString($request);
- }
-
- /**
- * Handles the request to the API.
- * It first checks that the method called (parameter 'method') is available in the module (it means that the method exists and is public)
- * It then reads the parameters from the request string and throws an exception if there are missing parameters.
- * It then calls the API Proxy which will call the requested method.
- *
- * @throws Piwik_FrontController_PluginDeactivatedException
- * @return mixed The data resulting from the API call
- */
- public function process()
- {
- // read the format requested for the output data
- $outputFormat = strtolower(Piwik_Common::getRequestVar('format', 'xml', 'string', $this->request));
-
- // create the response
- $response = new Piwik_API_ResponseBuilder($outputFormat, $this->request);
-
- try {
- // read parameters
- $moduleMethod = Piwik_Common::getRequestVar('method', null, 'string', $this->request);
-
- list($module, $method) = $this->extractModuleAndMethod($moduleMethod);
-
- if(!Piwik_PluginsManager::getInstance()->isPluginActivated($module))
- {
- throw new Piwik_FrontController_PluginDeactivatedException($module);
- }
- $moduleClass = "Piwik_" . $module . "_API";
-
- self::reloadAuthUsingTokenAuth($this->request);
-
- // call the method
- $returnedValue = Piwik_API_Proxy::getInstance()->call($moduleClass, $method, $this->request);
-
- $toReturn = $response->getResponse($returnedValue, $module, $method);
- } catch(Exception $e ) {
- $toReturn = $response->getResponseException( $e );
- }
- return $toReturn;
- }
-
- /**
- * If the token_auth is found in the $request parameter,
- * the current session will be authenticated using this token_auth.
- * It will overwrite the previous Auth object.
- *
- * @param array $request If null, uses the default request ($_GET)
- * @return void
- */
- static public function reloadAuthUsingTokenAuth($request = null)
- {
- // if a token_auth is specified in the API request, we load the right permissions
- $token_auth = Piwik_Common::getRequestVar('token_auth', '', 'string', $request);
- if($token_auth)
- {
- Piwik_PostEvent('API.Request.authenticate', $token_auth);
- Zend_Registry::get('access')->reloadAccess();
- Piwik::raiseMemoryLimitIfNecessary();
- }
- }
-
- /**
- * Returns array( $class, $method) from the given string $class.$method
- *
- * @param string $parameter
- * @throws Exception
- * @return array
- */
- private function extractModuleAndMethod($parameter)
- {
- $a = explode('.',$parameter);
- if(count($a) != 2)
- {
- throw new Exception("The method name is invalid. Expected 'module.methodName'");
- }
- return $a;
- }
-
- /**
- * Helper method to process an API request using the variables in $_GET and $_POST.
- *
- * @param string $method The API method to call, ie, Actions.getPageTitles
- * @param array $paramOverride The parameter name-value pairs to use instead of what's
- * in $_GET & $_POST.
- * @param mixed The result of the API request.
- */
- public static function processRequest( $method, $paramOverride = array() )
- {
- // set up request params
- $params = $_GET + $_POST;
- $params['format'] = 'original';
- $params['module'] = 'API';
- $params['method'] = $method;
- $params = $paramOverride + $params;
-
- // process request
- $request = new Piwik_API_Request($params);
- return $request->process();
- }
+ $request = trim($request);
+ $request = str_replace(array("\n", "\t"), '', $request);
+ parse_str($request, $requestArray);
+
+ $requestArray = $requestArray + $defaultRequest;
+ }
+
+ foreach ($requestArray as &$element) {
+ if (!is_array($element)) {
+ $element = trim($element);
+ }
+ }
+ return $requestArray;
+ }
+
+ /**
+ * Constructs the request to the API, given the request url
+ *
+ * @param string $request GET request that defines the API call (must at least contain a "method" parameter)
+ * Example: method=UserSettings.getWideScreen&idSite=1&date=yesterday&period=week&format=xml
+ * If a request is not provided, then we use the $_GET and $_POST superglobal and fetch
+ * the values directly from the HTTP GET query.
+ */
+ function __construct($request = null)
+ {
+ $this->request = self::getRequestArrayFromString($request);
+ }
+
+ /**
+ * Handles the request to the API.
+ * It first checks that the method called (parameter 'method') is available in the module (it means that the method exists and is public)
+ * It then reads the parameters from the request string and throws an exception if there are missing parameters.
+ * It then calls the API Proxy which will call the requested method.
+ *
+ * @throws Piwik_FrontController_PluginDeactivatedException
+ * @return mixed The data resulting from the API call
+ */
+ public function process()
+ {
+ // read the format requested for the output data
+ $outputFormat = strtolower(Piwik_Common::getRequestVar('format', 'xml', 'string', $this->request));
+
+ // create the response
+ $response = new Piwik_API_ResponseBuilder($outputFormat, $this->request);
+
+ try {
+ // read parameters
+ $moduleMethod = Piwik_Common::getRequestVar('method', null, 'string', $this->request);
+
+ list($module, $method) = $this->extractModuleAndMethod($moduleMethod);
+
+ if (!Piwik_PluginsManager::getInstance()->isPluginActivated($module)) {
+ throw new Piwik_FrontController_PluginDeactivatedException($module);
+ }
+ $moduleClass = "Piwik_" . $module . "_API";
+
+ self::reloadAuthUsingTokenAuth($this->request);
+
+ // call the method
+ $returnedValue = Piwik_API_Proxy::getInstance()->call($moduleClass, $method, $this->request);
+
+ $toReturn = $response->getResponse($returnedValue, $module, $method);
+ } catch (Exception $e) {
+ $toReturn = $response->getResponseException($e);
+ }
+ return $toReturn;
+ }
+
+ /**
+ * If the token_auth is found in the $request parameter,
+ * the current session will be authenticated using this token_auth.
+ * It will overwrite the previous Auth object.
+ *
+ * @param array $request If null, uses the default request ($_GET)
+ * @return void
+ */
+ static public function reloadAuthUsingTokenAuth($request = null)
+ {
+ // if a token_auth is specified in the API request, we load the right permissions
+ $token_auth = Piwik_Common::getRequestVar('token_auth', '', 'string', $request);
+ if ($token_auth) {
+ Piwik_PostEvent('API.Request.authenticate', $token_auth);
+ Zend_Registry::get('access')->reloadAccess();
+ Piwik::raiseMemoryLimitIfNecessary();
+ }
+ }
+
+ /**
+ * Returns array( $class, $method) from the given string $class.$method
+ *
+ * @param string $parameter
+ * @throws Exception
+ * @return array
+ */
+ private function extractModuleAndMethod($parameter)
+ {
+ $a = explode('.', $parameter);
+ if (count($a) != 2) {
+ throw new Exception("The method name is invalid. Expected 'module.methodName'");
+ }
+ return $a;
+ }
+
+ /**
+ * Helper method to process an API request using the variables in $_GET and $_POST.
+ *
+ * @param string $method The API method to call, ie, Actions.getPageTitles
+ * @param array $paramOverride The parameter name-value pairs to use instead of what's
+ * in $_GET & $_POST.
+ * @param mixed The result of the API request.
+ */
+ public static function processRequest($method, $paramOverride = array())
+ {
+ // set up request params
+ $params = $_GET + $_POST;
+ $params['format'] = 'original';
+ $params['module'] = 'API';
+ $params['method'] = $method;
+ $params = $paramOverride + $params;
+
+ // process request
+ $request = new Piwik_API_Request($params);
+ return $request->process();
+ }
}
diff --git a/core/API/ResponseBuilder.php b/core/API/ResponseBuilder.php
index 07df53ce7e..6830e34c0e 100644
--- a/core/API/ResponseBuilder.php
+++ b/core/API/ResponseBuilder.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,495 +15,452 @@
*/
class Piwik_API_ResponseBuilder
{
- private $request = null;
- private $outputFormat = null;
-
+ private $request = null;
+ private $outputFormat = null;
+
private $apiModule = false;
private $apiMethod = false;
- /**
- * @param string $outputFormat
- * @param array $request
- */
- public function __construct($outputFormat, $request = array())
- {
- $this->request = $request;
- $this->outputFormat = $outputFormat;
- }
-
- /**
- * This method processes the data resulting from the API call.
- *
- * - If the data resulted from the API call is a Piwik_DataTable then
- * - we apply the standard filters if the parameters have been found
- * in the URL. For example to offset,limit the Table you can add the following parameters to any API
- * call that returns a DataTable: filter_limit=10&filter_offset=20
- * - we apply the filters that have been previously queued on the DataTable
- * @see Piwik_DataTable::queueFilter()
- * - we apply the renderer that generate the DataTable in a given format (XML, PHP, HTML, JSON, etc.)
- * the format can be changed using the 'format' parameter in the request.
- * Example: format=xml
- *
- * - If there is nothing returned (void) we display a standard success message
- *
- * - If there is a PHP array returned, we try to convert it to a dataTable
- * It is then possible to convert this datatable to any requested format (xml/etc)
- *
- * - If a bool is returned we convert to a string (true is displayed as 'true' false as 'false')
- *
- * - If an integer / float is returned, we simply return it
- *
- * @param mixed $value The initial returned value, before post process. If set to null, success response is returned.
- * @param bool|string $apiModule The API module that was called
- * @param bool|string $apiMethod The API method that was called
- * @return mixed Usually a string, but can still be a PHP data structure if the format requested is 'original'
- */
- public function getResponse($value = null, $apiModule = false, $apiMethod = false)
- {
+ /**
+ * @param string $outputFormat
+ * @param array $request
+ */
+ public function __construct($outputFormat, $request = array())
+ {
+ $this->request = $request;
+ $this->outputFormat = $outputFormat;
+ }
+
+ /**
+ * This method processes the data resulting from the API call.
+ *
+ * - If the data resulted from the API call is a Piwik_DataTable then
+ * - we apply the standard filters if the parameters have been found
+ * in the URL. For example to offset,limit the Table you can add the following parameters to any API
+ * call that returns a DataTable: filter_limit=10&filter_offset=20
+ * - we apply the filters that have been previously queued on the DataTable
+ * @see Piwik_DataTable::queueFilter()
+ * - we apply the renderer that generate the DataTable in a given format (XML, PHP, HTML, JSON, etc.)
+ * the format can be changed using the 'format' parameter in the request.
+ * Example: format=xml
+ *
+ * - If there is nothing returned (void) we display a standard success message
+ *
+ * - If there is a PHP array returned, we try to convert it to a dataTable
+ * It is then possible to convert this datatable to any requested format (xml/etc)
+ *
+ * - If a bool is returned we convert to a string (true is displayed as 'true' false as 'false')
+ *
+ * - If an integer / float is returned, we simply return it
+ *
+ * @param mixed $value The initial returned value, before post process. If set to null, success response is returned.
+ * @param bool|string $apiModule The API module that was called
+ * @param bool|string $apiMethod The API method that was called
+ * @return mixed Usually a string, but can still be a PHP data structure if the format requested is 'original'
+ */
+ public function getResponse($value = null, $apiModule = false, $apiMethod = false)
+ {
$this->apiModule = $apiModule;
$this->apiMethod = $apiMethod;
-
- // when null or void is returned from the api call, we handle it as a successful operation
- if(!isset($value))
- {
- return $this->handleSuccess();
- }
-
- // If the returned value is an object DataTable we
- // apply the set of generic filters if asked in the URL
- // and we render the DataTable according to the format specified in the URL
- if($value instanceof Piwik_DataTable
- || $value instanceof Piwik_DataTable_Array)
- {
- return $this->handleDataTable($value);
- }
-
- // Case an array is returned from the API call, we convert it to the requested format
- // - if calling from inside the application (format = original)
- // => the data stays unchanged (ie. a standard php array or whatever data structure)
- // - if any other format is requested, we have to convert this data structure (which we assume
- // to be an array) to a DataTable in order to apply the requested DataTable_Renderer (for example XML)
- if(is_array($value))
- {
- return $this->handleArray($value);
- }
-
- // original data structure requested, we return without process
- if( $this->outputFormat == 'original' )
- {
- return $value;
- }
-
- if( is_object($value)
- || is_resource($value))
- {
- return $this->getResponseException(new Exception('The API cannot handle this data structure.'));
- }
-
- // bool // integer // float // serialized object
- return $this->handleScalar($value);
- }
-
- /**
- * Returns an error $message in the requested $format
- *
- * @param Exception $e
- * @throws Exception
- * @return string
- */
- public function getResponseException(Exception $e)
- {
- $format = strtolower($this->outputFormat);
-
- if( $format == 'original' )
- {
- throw $e;
- }
-
- try {
- $renderer = Piwik_DataTable_Renderer::factory($format);
- } catch (Exception $exceptionRenderer) {
- return "Error: " . $e->getMessage() . " and: " . $exceptionRenderer->getMessage();
- }
-
- $renderer->setException($e);
-
- if($format == 'php')
- {
- $renderer->setSerialize($this->caseRendererPHPSerialize());
- }
-
- return $renderer->renderException();
- }
-
- /**
- * Returns true if the user requested to serialize the output data (&serialize=1 in the request)
- *
- * @param mixed $defaultSerializeValue Default value in case the user hasn't specified a value
- * @return bool
- */
- protected function caseRendererPHPSerialize($defaultSerializeValue = 1)
- {
- $serialize = Piwik_Common::getRequestVar('serialize', $defaultSerializeValue, 'int', $this->request);
- if($serialize)
- {
- return true;
- }
- return false;
- }
-
- /**
- * Apply the specified renderer to the DataTable
- *
- * @param Piwik_DataTable|array $dataTable
- * @return string
- */
- protected function getRenderedDataTable($dataTable)
- {
- $format = strtolower($this->outputFormat);
-
- // if asked for original dataStructure
- if($format == 'original')
- {
- // by default "original" data is not serialized
- if($this->caseRendererPHPSerialize( $defaultSerialize = 0))
- {
- $dataTable = serialize($dataTable);
- }
- return $dataTable;
- }
-
- $method = Piwik_Common::getRequestVar('method', '', 'string', $this->request);
-
- $renderer = Piwik_DataTable_Renderer::factory($format);
- $renderer->setTable($dataTable);
- $renderer->setRenderSubTables(Piwik_Common::getRequestVar('expanded', false, 'int', $this->request));
- $renderer->setHideIdSubDatableFromResponse(Piwik_Common::getRequestVar('hideIdSubDatable', false, 'int', $this->request));
-
- if($format == 'php')
- {
- $renderer->setSerialize($this->caseRendererPHPSerialize());
- $renderer->setPrettyDisplay(Piwik_Common::getRequestVar('prettyDisplay', false, 'int', $this->request));
- }
- else if($format == 'html')
- {
- $renderer->setTableId($this->request['method']);
- }
- else if($format == 'csv' || $format == 'tsv')
- {
- $renderer->setConvertToUnicode(Piwik_Common::getRequestVar('convertToUnicode', true, 'int', $this->request));
- }
-
- // prepare translation of column names
- if ($format == 'html' || $format == 'csv' || $format == 'tsv' || $format = 'rss')
- {
- $renderer->setApiMethod($method);
- $renderer->setIdSite(Piwik_Common::getRequestVar('idSite', false, 'int', $this->request));
- $renderer->setTranslateColumnNames(Piwik_Common::getRequestVar('translateColumnNames', false, 'int', $this->request));
- }
-
- return $renderer->render();
- }
-
- /**
- * Returns a success $message in the requested $format
- *
- * @param string $message
- * @return string
- */
- protected function handleSuccess( $message = 'ok' )
- {
- // return a success message only if no content has already been buffered, useful when APIs return raw text or html content to the browser
- if(!ob_get_contents())
- {
- switch($this->outputFormat)
- {
- case 'xml':
- @header("Content-Type: text/xml;charset=utf-8");
- $return =
- "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" .
- "<result>\n".
- "\t<success message=\"".$message."\" />\n".
- "</result>";
- break;
- case 'json':
- @header( "Content-Type: application/json" );
- $return = '{"result":"success", "message":"'.$message.'"}';
- break;
- case 'php':
- $return = array('result' => 'success', 'message' => $message);
- if($this->caseRendererPHPSerialize())
- {
- $return = serialize($return);
- }
- break;
-
- case 'csv':
- @header("Content-Type: application/vnd.ms-excel");
- @header("Content-Disposition: attachment; filename=piwik-report-export.csv");
- $return = "message\n".$message;
- break;
-
- default:
- $return = 'Success:'.$message;
- break;
- }
- return $return;
- }
- }
-
- /**
- * Converts the given scalar to an data table
- *
- * @param mixed $scalar
- * @return string
- */
- protected function handleScalar($scalar)
- {
- $dataTable = new Piwik_DataTable_Simple();
- $dataTable->addRowsFromArray( array($scalar) );
- return $this->getRenderedDataTable($dataTable);
- }
-
- /**
- * Handles the given data table
- *
- * @param Piwik_DataTable $datatable
- * @return string
- */
- protected function handleDataTable($datatable)
- {
- // if requested, flatten nested tables
- if (Piwik_Common::getRequestVar('flat', '0', 'string', $this->request) == '1')
- {
- $flattener = new Piwik_API_DataTableManipulator_Flattener($this->apiModule, $this->apiMethod, $this->request);
- if (Piwik_Common::getRequestVar('include_aggregate_rows', '0', 'string', $this->request) == '1')
- {
- $flattener->includeAggregateRows();
- }
- $datatable = $flattener->flatten($datatable);
- }
-
- // if the flag disable_generic_filters is defined we skip the generic filters
- if(0 == Piwik_Common::getRequestVar('disable_generic_filters', '0', 'string', $this->request))
- {
- $genericFilter = new Piwik_API_DataTableGenericFilter($this->request);
- $genericFilter->filter($datatable);
- }
-
- // we automatically safe decode all datatable labels (against xss)
- $datatable->queueFilter('SafeDecodeLabel');
-
- // if the flag disable_queued_filters is defined we skip the filters that were queued
- if(Piwik_Common::getRequestVar('disable_queued_filters', 'false', 'string', $this->request) == 'false')
- {
- $datatable->applyQueuedFilters();
- }
-
- // use the ColumnDelete filter if hideColumns/showColumns is provided (must be done
- // after queued filters are run so processed metrics can be removed, too)
- $hideColumns = Piwik_Common::getRequestVar('hideColumns', '', 'string', $this->request);
- $showColumns = Piwik_Common::getRequestVar('showColumns', '', 'string', $this->request);
- if ($hideColumns !== '' || $showColumns !== '')
- {
- $datatable->filter('ColumnDelete', array($hideColumns, $showColumns));
- }
-
- // apply label filter: only return rows matching the label parameter (more than one if more than one label)
- $label = $this->getLabelQueryParam();
- if (!empty($label))
- {
- $label = Piwik_Common::unsanitizeInputValues($label);
- $addEmptyRows = Piwik_Common::getRequestVar('labelFilterAddEmptyRows', 0, 'int', $this->request) == 1;
-
- $filter = new Piwik_API_DataTableManipulator_LabelFilter($this->apiModule, $this->apiMethod, $this->request);
- $datatable = $filter->filter($label, $datatable, $addEmptyRows);
- }
- return $this->getRenderedDataTable($datatable);
- }
-
- /**
- * Converts the given simple array to a data table
- *
- * @param array $array
- * @return string
- */
- protected function handleArray($array)
- {
- if($this->outputFormat == 'original')
- {
- // we handle the serialization. Because some php array have a very special structure that
- // couldn't be converted with the automatic DataTable->addRowsFromSimpleArray
- // the user may want to request the original PHP data structure serialized by the API
- // in case he has to setup serialize=1 in the URL
- if($this->caseRendererPHPSerialize( $defaultSerialize = 0))
- {
- return serialize($array);
- }
- return $array;
- }
- $multiDimensional = $this->handleMultiDimensionalArray($array);
- if($multiDimensional !== false)
- {
- return $multiDimensional;
- }
-
- return $this->getRenderedDataTable($array);
- }
-
- /**
- * Is this a multi dimensional array?
- * Multi dim arrays are not supported by the Datatable renderer.
- * We manually render these.
- *
- * array(
- * array(
- * 1,
- * 2 => array( 1,
- * 2
- * )
- * ),
- * array( 2,
- * 3
- * )
- * );
- *
- * @param array $array
- * @return string|bool false if it isn't a multidim array
- */
- protected function handleMultiDimensionalArray($array)
- {
- $first = reset($array);
- foreach($array as $first)
- {
- if(is_array($first))
- {
- foreach($first as $key => $value)
- {
- // Yes, this is a multi dim array
- if(is_array($value))
- {
- switch($this->outputFormat)
- {
- case 'json':
- @header( "Content-Type: application/json" );
- return self::convertMultiDimensionalArrayToJson($array);
- break;
-
- case 'php':
- if($this->caseRendererPHPSerialize( $defaultSerialize = 0))
- {
- return serialize($array);
- }
- return $array;
-
- case 'xml':
- @header("Content-Type: text/xml;charset=utf-8");
- return $this->getRenderedDataTable($array);
- default:
- break;
- }
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Render a multidimensional array to Json
- * Handle Piwik_DataTable|Piwik_DataTable_Array elements in the first dimension only, following case does not work:
- * array(
- * array(
- * Piwik_DataTable,
- * 2 => array(
- * 1,
- * 2
- * ),
- * ),
- * );
- *
- * @param array $array can contain scalar, arrays, Piwik_DataTable and Piwik_DataTable_Array
- * @return string
- */
- public static function convertMultiDimensionalArrayToJson($array)
- {
- $isAssociative = Piwik::isAssociativeArray($array);
-
- if($isAssociative)
- {
- $json = "{";
- }
- else
- {
- $json = "[";
- }
-
- foreach ($array as $key=>$value)
- {
- if($isAssociative)
- {
- $json .= "\"".$key."\":";
- }
-
- switch(true)
- {
- // Case dimension is a PHP array
- case (is_array($value)):
-
- $json .= Piwik_Common::json_encode($value);
- break;
-
- // Case dimension is a Piwik_DataTable_Array or a Piwik_DataTable
- case ($value instanceof Piwik_DataTable_Array || $value instanceof Piwik_DataTable):
-
- $XMLRenderer = new Piwik_DataTable_Renderer_Json();
- $XMLRenderer->setTable($value);
- $renderedReport = $XMLRenderer->render();
- $json .= $renderedReport;
- break;
-
- // Case scalar
- default:
-
- $json .= Piwik_Common::json_encode($value);
- break;
- }
-
- $json .= ",";
- }
-
- // Remove trailing ","
- $json = substr ($json, 0, strlen($json) - 1);
-
- if($isAssociative)
- {
- $json .= "}";
- }
- else
- {
- $json .= "]";
- }
- return $json;
- }
-
- /**
- * Returns the value for the label query parameter which can be either a string
- * (ie, label=...) or array (ie, label[]=...).
- *
- * @return array
- */
- private function getLabelQueryParam()
- {
+
+ // when null or void is returned from the api call, we handle it as a successful operation
+ if (!isset($value)) {
+ return $this->handleSuccess();
+ }
+
+ // If the returned value is an object DataTable we
+ // apply the set of generic filters if asked in the URL
+ // and we render the DataTable according to the format specified in the URL
+ if ($value instanceof Piwik_DataTable
+ || $value instanceof Piwik_DataTable_Array
+ ) {
+ return $this->handleDataTable($value);
+ }
+
+ // Case an array is returned from the API call, we convert it to the requested format
+ // - if calling from inside the application (format = original)
+ // => the data stays unchanged (ie. a standard php array or whatever data structure)
+ // - if any other format is requested, we have to convert this data structure (which we assume
+ // to be an array) to a DataTable in order to apply the requested DataTable_Renderer (for example XML)
+ if (is_array($value)) {
+ return $this->handleArray($value);
+ }
+
+ // original data structure requested, we return without process
+ if ($this->outputFormat == 'original') {
+ return $value;
+ }
+
+ if (is_object($value)
+ || is_resource($value)
+ ) {
+ return $this->getResponseException(new Exception('The API cannot handle this data structure.'));
+ }
+
+ // bool // integer // float // serialized object
+ return $this->handleScalar($value);
+ }
+
+ /**
+ * Returns an error $message in the requested $format
+ *
+ * @param Exception $e
+ * @throws Exception
+ * @return string
+ */
+ public function getResponseException(Exception $e)
+ {
+ $format = strtolower($this->outputFormat);
+
+ if ($format == 'original') {
+ throw $e;
+ }
+
+ try {
+ $renderer = Piwik_DataTable_Renderer::factory($format);
+ } catch (Exception $exceptionRenderer) {
+ return "Error: " . $e->getMessage() . " and: " . $exceptionRenderer->getMessage();
+ }
+
+ $renderer->setException($e);
+
+ if ($format == 'php') {
+ $renderer->setSerialize($this->caseRendererPHPSerialize());
+ }
+
+ return $renderer->renderException();
+ }
+
+ /**
+ * Returns true if the user requested to serialize the output data (&serialize=1 in the request)
+ *
+ * @param mixed $defaultSerializeValue Default value in case the user hasn't specified a value
+ * @return bool
+ */
+ protected function caseRendererPHPSerialize($defaultSerializeValue = 1)
+ {
+ $serialize = Piwik_Common::getRequestVar('serialize', $defaultSerializeValue, 'int', $this->request);
+ if ($serialize) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Apply the specified renderer to the DataTable
+ *
+ * @param Piwik_DataTable|array $dataTable
+ * @return string
+ */
+ protected function getRenderedDataTable($dataTable)
+ {
+ $format = strtolower($this->outputFormat);
+
+ // if asked for original dataStructure
+ if ($format == 'original') {
+ // by default "original" data is not serialized
+ if ($this->caseRendererPHPSerialize($defaultSerialize = 0)) {
+ $dataTable = serialize($dataTable);
+ }
+ return $dataTable;
+ }
+
+ $method = Piwik_Common::getRequestVar('method', '', 'string', $this->request);
+
+ $renderer = Piwik_DataTable_Renderer::factory($format);
+ $renderer->setTable($dataTable);
+ $renderer->setRenderSubTables(Piwik_Common::getRequestVar('expanded', false, 'int', $this->request));
+ $renderer->setHideIdSubDatableFromResponse(Piwik_Common::getRequestVar('hideIdSubDatable', false, 'int', $this->request));
+
+ if ($format == 'php') {
+ $renderer->setSerialize($this->caseRendererPHPSerialize());
+ $renderer->setPrettyDisplay(Piwik_Common::getRequestVar('prettyDisplay', false, 'int', $this->request));
+ } else if ($format == 'html') {
+ $renderer->setTableId($this->request['method']);
+ } else if ($format == 'csv' || $format == 'tsv') {
+ $renderer->setConvertToUnicode(Piwik_Common::getRequestVar('convertToUnicode', true, 'int', $this->request));
+ }
+
+ // prepare translation of column names
+ if ($format == 'html' || $format == 'csv' || $format == 'tsv' || $format = 'rss') {
+ $renderer->setApiMethod($method);
+ $renderer->setIdSite(Piwik_Common::getRequestVar('idSite', false, 'int', $this->request));
+ $renderer->setTranslateColumnNames(Piwik_Common::getRequestVar('translateColumnNames', false, 'int', $this->request));
+ }
+
+ return $renderer->render();
+ }
+
+ /**
+ * Returns a success $message in the requested $format
+ *
+ * @param string $message
+ * @return string
+ */
+ protected function handleSuccess($message = 'ok')
+ {
+ // return a success message only if no content has already been buffered, useful when APIs return raw text or html content to the browser
+ if (!ob_get_contents()) {
+ switch ($this->outputFormat) {
+ case 'xml':
+ @header("Content-Type: text/xml;charset=utf-8");
+ $return =
+ "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" .
+ "<result>\n" .
+ "\t<success message=\"" . $message . "\" />\n" .
+ "</result>";
+ break;
+ case 'json':
+ @header("Content-Type: application/json");
+ $return = '{"result":"success", "message":"' . $message . '"}';
+ break;
+ case 'php':
+ $return = array('result' => 'success', 'message' => $message);
+ if ($this->caseRendererPHPSerialize()) {
+ $return = serialize($return);
+ }
+ break;
+
+ case 'csv':
+ @header("Content-Type: application/vnd.ms-excel");
+ @header("Content-Disposition: attachment; filename=piwik-report-export.csv");
+ $return = "message\n" . $message;
+ break;
+
+ default:
+ $return = 'Success:' . $message;
+ break;
+ }
+ return $return;
+ }
+ }
+
+ /**
+ * Converts the given scalar to an data table
+ *
+ * @param mixed $scalar
+ * @return string
+ */
+ protected function handleScalar($scalar)
+ {
+ $dataTable = new Piwik_DataTable_Simple();
+ $dataTable->addRowsFromArray(array($scalar));
+ return $this->getRenderedDataTable($dataTable);
+ }
+
+ /**
+ * Handles the given data table
+ *
+ * @param Piwik_DataTable $datatable
+ * @return string
+ */
+ protected function handleDataTable($datatable)
+ {
+ // if requested, flatten nested tables
+ if (Piwik_Common::getRequestVar('flat', '0', 'string', $this->request) == '1') {
+ $flattener = new Piwik_API_DataTableManipulator_Flattener($this->apiModule, $this->apiMethod, $this->request);
+ if (Piwik_Common::getRequestVar('include_aggregate_rows', '0', 'string', $this->request) == '1') {
+ $flattener->includeAggregateRows();
+ }
+ $datatable = $flattener->flatten($datatable);
+ }
+
+ // if the flag disable_generic_filters is defined we skip the generic filters
+ if (0 == Piwik_Common::getRequestVar('disable_generic_filters', '0', 'string', $this->request)) {
+ $genericFilter = new Piwik_API_DataTableGenericFilter($this->request);
+ $genericFilter->filter($datatable);
+ }
+
+ // we automatically safe decode all datatable labels (against xss)
+ $datatable->queueFilter('SafeDecodeLabel');
+
+ // if the flag disable_queued_filters is defined we skip the filters that were queued
+ if (Piwik_Common::getRequestVar('disable_queued_filters', 'false', 'string', $this->request) == 'false') {
+ $datatable->applyQueuedFilters();
+ }
+
+ // use the ColumnDelete filter if hideColumns/showColumns is provided (must be done
+ // after queued filters are run so processed metrics can be removed, too)
+ $hideColumns = Piwik_Common::getRequestVar('hideColumns', '', 'string', $this->request);
+ $showColumns = Piwik_Common::getRequestVar('showColumns', '', 'string', $this->request);
+ if ($hideColumns !== '' || $showColumns !== '') {
+ $datatable->filter('ColumnDelete', array($hideColumns, $showColumns));
+ }
+
+ // apply label filter: only return rows matching the label parameter (more than one if more than one label)
+ $label = $this->getLabelQueryParam();
+ if (!empty($label)) {
+ $label = Piwik_Common::unsanitizeInputValues($label);
+ $addEmptyRows = Piwik_Common::getRequestVar('labelFilterAddEmptyRows', 0, 'int', $this->request) == 1;
+
+ $filter = new Piwik_API_DataTableManipulator_LabelFilter($this->apiModule, $this->apiMethod, $this->request);
+ $datatable = $filter->filter($label, $datatable, $addEmptyRows);
+ }
+ return $this->getRenderedDataTable($datatable);
+ }
+
+ /**
+ * Converts the given simple array to a data table
+ *
+ * @param array $array
+ * @return string
+ */
+ protected function handleArray($array)
+ {
+ if ($this->outputFormat == 'original') {
+ // we handle the serialization. Because some php array have a very special structure that
+ // couldn't be converted with the automatic DataTable->addRowsFromSimpleArray
+ // the user may want to request the original PHP data structure serialized by the API
+ // in case he has to setup serialize=1 in the URL
+ if ($this->caseRendererPHPSerialize($defaultSerialize = 0)) {
+ return serialize($array);
+ }
+ return $array;
+ }
+ $multiDimensional = $this->handleMultiDimensionalArray($array);
+ if ($multiDimensional !== false) {
+ return $multiDimensional;
+ }
+
+ return $this->getRenderedDataTable($array);
+ }
+
+ /**
+ * Is this a multi dimensional array?
+ * Multi dim arrays are not supported by the Datatable renderer.
+ * We manually render these.
+ *
+ * array(
+ * array(
+ * 1,
+ * 2 => array( 1,
+ * 2
+ * )
+ * ),
+ * array( 2,
+ * 3
+ * )
+ * );
+ *
+ * @param array $array
+ * @return string|bool false if it isn't a multidim array
+ */
+ protected function handleMultiDimensionalArray($array)
+ {
+ $first = reset($array);
+ foreach ($array as $first) {
+ if (is_array($first)) {
+ foreach ($first as $key => $value) {
+ // Yes, this is a multi dim array
+ if (is_array($value)) {
+ switch ($this->outputFormat) {
+ case 'json':
+ @header("Content-Type: application/json");
+ return self::convertMultiDimensionalArrayToJson($array);
+ break;
+
+ case 'php':
+ if ($this->caseRendererPHPSerialize($defaultSerialize = 0)) {
+ return serialize($array);
+ }
+ return $array;
+
+ case 'xml':
+ @header("Content-Type: text/xml;charset=utf-8");
+ return $this->getRenderedDataTable($array);
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Render a multidimensional array to Json
+ * Handle Piwik_DataTable|Piwik_DataTable_Array elements in the first dimension only, following case does not work:
+ * array(
+ * array(
+ * Piwik_DataTable,
+ * 2 => array(
+ * 1,
+ * 2
+ * ),
+ * ),
+ * );
+ *
+ * @param array $array can contain scalar, arrays, Piwik_DataTable and Piwik_DataTable_Array
+ * @return string
+ */
+ public static function convertMultiDimensionalArrayToJson($array)
+ {
+ $isAssociative = Piwik::isAssociativeArray($array);
+
+ if ($isAssociative) {
+ $json = "{";
+ } else {
+ $json = "[";
+ }
+
+ foreach ($array as $key => $value) {
+ if ($isAssociative) {
+ $json .= "\"" . $key . "\":";
+ }
+
+ switch (true) {
+ // Case dimension is a PHP array
+ case (is_array($value)):
+
+ $json .= Piwik_Common::json_encode($value);
+ break;
+
+ // Case dimension is a Piwik_DataTable_Array or a Piwik_DataTable
+ case ($value instanceof Piwik_DataTable_Array || $value instanceof Piwik_DataTable):
+
+ $XMLRenderer = new Piwik_DataTable_Renderer_Json();
+ $XMLRenderer->setTable($value);
+ $renderedReport = $XMLRenderer->render();
+ $json .= $renderedReport;
+ break;
+
+ // Case scalar
+ default:
+
+ $json .= Piwik_Common::json_encode($value);
+ break;
+ }
+
+ $json .= ",";
+ }
+
+ // Remove trailing ","
+ $json = substr($json, 0, strlen($json) - 1);
+
+ if ($isAssociative) {
+ $json .= "}";
+ } else {
+ $json .= "]";
+ }
+ return $json;
+ }
+
+ /**
+ * Returns the value for the label query parameter which can be either a string
+ * (ie, label=...) or array (ie, label[]=...).
+ *
+ * @return array
+ */
+ private function getLabelQueryParam()
+ {
$label = Piwik_Common::getRequestVar('label', array(), 'array', $this->request);
- if (empty($label))
- {
- $label = Piwik_Common::getRequestVar('label', '', 'string', $this->request);
- if (!empty($label))
- {
- $label = array($label);
- }
+ if (empty($label)) {
+ $label = Piwik_Common::getRequestVar('label', '', 'string', $this->request);
+ if (!empty($label)) {
+ $label = array($label);
+ }
}
return $label;
- }
+ }
}
diff --git a/core/Access.php b/core/Access.php
index ed7b2e386e..0e3bc42054 100644
--- a/core/Access.php
+++ b/core/Access.php
@@ -11,9 +11,9 @@
/**
* Class to handle User Access:
- * - loads user access from the Piwik_Auth_Result object
+ * - loads user access from the Piwik_Auth_Result object
* - provides easy to use API to check the permissions for the current (check* methods)
- *
+ *
* In Piwik there are mainly 4 access levels
* - no access
* - VIEW access
@@ -24,389 +24,368 @@
* A given user has a given access level for a given website.
* For example:
* User Noemie has
- * - VIEW access on the website 1,
+ * - VIEW access on the website 1,
* - ADMIN on the website 2 and 4, and
* - NO access on the website 3 and 5
*
- * There is only one Super User. He has ADMIN access to all the websites
+ * There is only one Super User. He has ADMIN access to all the websites
* and he only can change the main configuration settings.
*
* @package Piwik
* @subpackage Piwik_Access
*/
class Piwik_Access
-{
- /**
- * Array of idsites available to the current user, indexed by permission level
- * @see getSitesIdWith*()
- *
- * @var array
- */
- protected $idsitesByAccess = null;
-
- /**
- * Login of the current user
- *
- * @var string
- */
- protected $login = null;
-
- /**
- * token_auth of the current user
- *
- * @var string
- */
- protected $token_auth = null;
-
- /**
- * Defines if the current user is the super user
- * @see isSuperUser()
- *
- * @var bool
- */
- protected $isSuperUser = false;
-
- /**
- * List of available permissions in Piwik
- *
- * @var array
- */
- static private $availableAccess = array('noaccess', 'view', 'admin', 'superuser');
-
- /**
- * Authentification object (see Piwik_Auth)
- *
- * @var Piwik_Auth
- */
- private $auth = null;
-
- /**
- * Returns the list of the existing Access level.
- * Useful when a given API method requests a given acccess Level.
- * We first check that the required access level exists.
- *
- * @return array
- */
- static public function getListAccess()
- {
- return self::$availableAccess;
- }
-
- /**
- * Constructor
- */
- function __construct()
- {
- $this->idsitesByAccess = array(
- 'view' => array(),
- 'admin' => array(),
- 'superuser' => array()
- );
- }
-
- /**
- * Loads the access levels for the current user.
- *
- * Calls the authentication method to try to log the user in the system.
- * If the user credentials are not correct we don't load anything.
- * If the login/password is correct the user is either the SuperUser or a normal user.
- * We load the access levels for this user for all the websites.
- *
- * @param null|Piwik_Auth $auth Auth adapter
- * @return bool true on success, false if reloading access failed (when auth object wasn't specified and user is not enforced to be Super User)
- */
- public function reloadAccess(Piwik_Auth $auth = null)
- {
- if(!is_null($auth))
- {
- $this->auth = $auth;
- }
-
- // if the Piwik_Auth wasn't set, we may be in the special case of setSuperUser(), otherwise we fail
- if(is_null($this->auth))
- {
- if($this->isSuperUser())
- {
- return $this->reloadAccessSuperUser();
- }
- return false;
- }
-
- // access = array ( idsite => accessIdSite, idsite2 => accessIdSite2)
- $result = $this->auth->authenticate();
-
- if(!$result->isValid())
- {
- return false;
- }
- $this->login = $result->getIdentity();
- $this->token_auth = $result->getTokenAuth();
-
- // case the superUser is logged in
- if($result->getCode() == Piwik_Auth_Result::SUCCESS_SUPERUSER_AUTH_CODE)
- {
- return $this->reloadAccessSuperUser();
- }
- // in case multiple calls to API using different tokens, we ensure we reset it as not SU
- $this->setSuperUser(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 = self::getRawSitesWithSomeViewAccess($this->login);
- foreach($accessRaw as $access)
- {
- $this->idsitesByAccess[$access['access']][] = $access['idsite'];
- }
- return true;
- }
-
- static public function getRawSitesWithSomeViewAccess($login)
- {
- return Piwik_FetchAll(self::getSqlAccessSite("access, t2.idsite"), $login);
- }
-
- /**
- * Returns the SQL query joining sites and access table for a given login
- *
- * @param string $select Columns or expression to SELECT FROM table, eg. "MIN(ts_created)"
- * @return string SQL query
- */
- static public function getSqlAccessSite($select)
- {
- return "SELECT ". $select ."
- FROM ".Piwik_Common::prefixTable('access'). " as t1
- JOIN ".Piwik_Common::prefixTable('site')." as t2 USING (idsite) ".
- " WHERE login = ?";
- }
-
- /**
- * Reload super user access
- *
- * @return bool
- */
- protected function reloadAccessSuperUser()
- {
- $this->isSuperUser = true;
- $this->idsitesByAccess['superuser'] = Piwik_SitesManager_API::getInstance()->getAllSitesId();
- $this->login = Piwik_Config::getInstance()->superuser['login'];
- return true;
- }
-
- /**
- * We bypass the normal auth method and give the current user Super User rights.
- * This should be very carefully used.
- *
- * @param bool $bool
- */
- public function setSuperUser($bool = true)
- {
- if($bool)
- {
- $this->reloadAccessSuperUser();
- }
- else
- {
- $this->isSuperUser = false;
- $this->idsitesByAccess['superuser'] = array();
- }
- }
-
- /**
- * Returns true if the current user is logged in as the super user
- *
- * @return bool
- */
- public function isSuperUser()
- {
- return $this->isSuperUser;
- }
-
- /**
- * Returns the current user login
- *
- * @return string|null
- */
- public function getLogin()
- {
- return $this->login;
- }
-
- /**
- * Returns the token_auth used to authenticate this user in the API
- *
- * @return string|null
- */
- public function getTokenAuth()
- {
- return $this->token_auth;
- }
-
- /**
- * Returns an array of ID sites for which the user has at least a VIEW access.
- * Which means VIEW or ADMIN or SUPERUSER.
- *
- * @return array Example if the user is ADMIN for 4
- * and has VIEW access for 1 and 7, it returns array(1, 4, 7);
- */
- public function getSitesIdWithAtLeastViewAccess()
- {
- return array_unique(array_merge(
- $this->idsitesByAccess['view'],
- $this->idsitesByAccess['admin'],
- $this->idsitesByAccess['superuser'])
- );
- }
-
- /**
- * Returns an array of ID sites for which the user has an ADMIN access.
- *
- * @return array Example if the user is ADMIN for 4 and 8
- * and has VIEW access for 1 and 7, it returns array(4, 8);
- */
- public function getSitesIdWithAdminAccess()
- {
- return array_unique(array_merge(
- $this->idsitesByAccess['admin'],
- $this->idsitesByAccess['superuser'])
- );
- }
-
-
- /**
- * Returns an array of ID sites for which the user has a VIEW access only.
- *
- * @return array Example if the user is ADMIN for 4
- * and has VIEW access for 1 and 7, it returns array(1, 7);
- * @see getSitesIdWithAtLeastViewAccess()
- */
- public function getSitesIdWithViewAccess()
- {
- return $this->idsitesByAccess['view'];
- }
-
- /**
- * Throws an exception if the user is not the SuperUser
- *
- * @throws Piwik_Access_NoAccessException
- */
- public function checkUserIsSuperUser()
- {
- if(!$this->isSuperUser())
- {
- throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilege', array("'superuser'")));
- }
- }
-
- /**
- * If the user doesn't have an ADMIN access for at least one website, throws an exception
- *
- * @throws Piwik_Access_NoAccessException
- */
- public function checkUserHasSomeAdminAccess()
- {
- if($this->isSuperUser())
- {
- return;
- }
- $idSitesAccessible = $this->getSitesIdWithAdminAccess();
- if(count($idSitesAccessible) == 0)
- {
- throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilegeAtLeastOneWebsite', array('admin')));
- }
- }
-
- /**
- * If the user doesn't have any view permission, throw exception
- *
- * @throws Piwik_Access_NoAccessException
- */
- public function checkUserHasSomeViewAccess()
- {
- if($this->isSuperUser())
- {
- return;
- }
- $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess();
- if(count($idSitesAccessible) == 0)
- {
- throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilegeAtLeastOneWebsite', array('view')));
- }
- }
-
- /**
- * This method checks that the user has ADMIN access for the given list of websites.
- * If the user doesn't have ADMIN access for at least one website of the list, we throw an exception.
- *
- * @param int|array $idSites List of ID sites to check
- * @throws Piwik_Access_NoAccessException If for any of the websites the user doesn't have an ADMIN access
- */
- public function checkUserHasAdminAccess( $idSites )
- {
- if($this->isSuperUser())
- {
- return;
- }
- $idSites = $this->getIdSites($idSites);
- $idSitesAccessible = $this->getSitesIdWithAdminAccess();
- foreach($idSites as $idsite)
- {
- if(!in_array($idsite, $idSitesAccessible))
- {
- throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilegeAccessWebsite', array("'admin'", $idsite)));
- }
- }
- }
-
- /**
- * This method checks that the user has VIEW or ADMIN access for the given list of websites.
- * If the user doesn't have VIEW or ADMIN access for at least one website of the list, we throw an exception.
- *
- * @param int|array|string $idSites List of ID sites to check (integer, array of integers, string comma separated list of integers)
- * @throws Piwik_Access_NoAccessException If for any of the websites the user doesn't have an VIEW or ADMIN access
- */
- public function checkUserHasViewAccess( $idSites )
- {
- if($this->isSuperUser())
- {
- return;
- }
- $idSites = $this->getIdSites($idSites);
- $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess();
- foreach($idSites as $idsite)
- {
- if(!in_array($idsite, $idSitesAccessible))
- {
- throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilegeAccessWebsite', array("'view'", $idsite)));
- }
- }
- }
-
- /**
- * @param int|array|string $idSites
- * @return array
- * @throws Piwik_Access_NoAccessException
- */
- protected function getIdSites($idSites)
- {
- if($idSites === 'all')
- {
- $idSites = $this->getSitesIdWithAtLeastViewAccess();
- }
-
- $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites);
- if(empty($idSites))
- {
- throw new Piwik_Access_NoAccessException("The parameter 'idSite=' is missing from the request.");
- }
- return $idSites;
- }
+{
+ /**
+ * Array of idsites available to the current user, indexed by permission level
+ * @see getSitesIdWith*()
+ *
+ * @var array
+ */
+ protected $idsitesByAccess = null;
+
+ /**
+ * Login of the current user
+ *
+ * @var string
+ */
+ protected $login = null;
+
+ /**
+ * token_auth of the current user
+ *
+ * @var string
+ */
+ protected $token_auth = null;
+
+ /**
+ * Defines if the current user is the super user
+ * @see isSuperUser()
+ *
+ * @var bool
+ */
+ protected $isSuperUser = false;
+
+ /**
+ * List of available permissions in Piwik
+ *
+ * @var array
+ */
+ static private $availableAccess = array('noaccess', 'view', 'admin', 'superuser');
+
+ /**
+ * Authentification object (see Piwik_Auth)
+ *
+ * @var Piwik_Auth
+ */
+ private $auth = null;
+
+ /**
+ * Returns the list of the existing Access level.
+ * Useful when a given API method requests a given acccess Level.
+ * We first check that the required access level exists.
+ *
+ * @return array
+ */
+ static public function getListAccess()
+ {
+ return self::$availableAccess;
+ }
+
+ /**
+ * Constructor
+ */
+ function __construct()
+ {
+ $this->idsitesByAccess = array(
+ 'view' => array(),
+ 'admin' => array(),
+ 'superuser' => array()
+ );
+ }
+
+ /**
+ * Loads the access levels for the current user.
+ *
+ * Calls the authentication method to try to log the user in the system.
+ * If the user credentials are not correct we don't load anything.
+ * If the login/password is correct the user is either the SuperUser or a normal user.
+ * We load the access levels for this user for all the websites.
+ *
+ * @param null|Piwik_Auth $auth Auth adapter
+ * @return bool true on success, false if reloading access failed (when auth object wasn't specified and user is not enforced to be Super User)
+ */
+ public function reloadAccess(Piwik_Auth $auth = null)
+ {
+ if (!is_null($auth)) {
+ $this->auth = $auth;
+ }
+
+ // if the Piwik_Auth wasn't set, we may be in the special case of setSuperUser(), otherwise we fail
+ if (is_null($this->auth)) {
+ if ($this->isSuperUser()) {
+ return $this->reloadAccessSuperUser();
+ }
+ return false;
+ }
+
+ // access = array ( idsite => accessIdSite, idsite2 => accessIdSite2)
+ $result = $this->auth->authenticate();
+
+ if (!$result->isValid()) {
+ return false;
+ }
+ $this->login = $result->getIdentity();
+ $this->token_auth = $result->getTokenAuth();
+
+ // case the superUser is logged in
+ if ($result->getCode() == Piwik_Auth_Result::SUCCESS_SUPERUSER_AUTH_CODE) {
+ return $this->reloadAccessSuperUser();
+ }
+ // in case multiple calls to API using different tokens, we ensure we reset it as not SU
+ $this->setSuperUser(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 = self::getRawSitesWithSomeViewAccess($this->login);
+ foreach ($accessRaw as $access) {
+ $this->idsitesByAccess[$access['access']][] = $access['idsite'];
+ }
+ return true;
+ }
+
+ static public function getRawSitesWithSomeViewAccess($login)
+ {
+ return Piwik_FetchAll(self::getSqlAccessSite("access, t2.idsite"), $login);
+ }
+
+ /**
+ * Returns the SQL query joining sites and access table for a given login
+ *
+ * @param string $select Columns or expression to SELECT FROM table, eg. "MIN(ts_created)"
+ * @return string SQL query
+ */
+ static public function getSqlAccessSite($select)
+ {
+ return "SELECT " . $select . "
+ FROM " . Piwik_Common::prefixTable('access') . " as t1
+ JOIN " . Piwik_Common::prefixTable('site') . " as t2 USING (idsite) " .
+ " WHERE login = ?";
+ }
+
+ /**
+ * Reload super user access
+ *
+ * @return bool
+ */
+ protected function reloadAccessSuperUser()
+ {
+ $this->isSuperUser = true;
+ $this->idsitesByAccess['superuser'] = Piwik_SitesManager_API::getInstance()->getAllSitesId();
+ $this->login = Piwik_Config::getInstance()->superuser['login'];
+ return true;
+ }
+
+ /**
+ * We bypass the normal auth method and give the current user Super User rights.
+ * This should be very carefully used.
+ *
+ * @param bool $bool
+ */
+ public function setSuperUser($bool = true)
+ {
+ if ($bool) {
+ $this->reloadAccessSuperUser();
+ } else {
+ $this->isSuperUser = false;
+ $this->idsitesByAccess['superuser'] = array();
+ }
+ }
+
+ /**
+ * Returns true if the current user is logged in as the super user
+ *
+ * @return bool
+ */
+ public function isSuperUser()
+ {
+ return $this->isSuperUser;
+ }
+
+ /**
+ * Returns the current user login
+ *
+ * @return string|null
+ */
+ public function getLogin()
+ {
+ return $this->login;
+ }
+
+ /**
+ * Returns the token_auth used to authenticate this user in the API
+ *
+ * @return string|null
+ */
+ public function getTokenAuth()
+ {
+ return $this->token_auth;
+ }
+
+ /**
+ * Returns an array of ID sites for which the user has at least a VIEW access.
+ * Which means VIEW or ADMIN or SUPERUSER.
+ *
+ * @return array Example if the user is ADMIN for 4
+ * and has VIEW access for 1 and 7, it returns array(1, 4, 7);
+ */
+ public function getSitesIdWithAtLeastViewAccess()
+ {
+ return array_unique(array_merge(
+ $this->idsitesByAccess['view'],
+ $this->idsitesByAccess['admin'],
+ $this->idsitesByAccess['superuser'])
+ );
+ }
+
+ /**
+ * Returns an array of ID sites for which the user has an ADMIN access.
+ *
+ * @return array Example if the user is ADMIN for 4 and 8
+ * and has VIEW access for 1 and 7, it returns array(4, 8);
+ */
+ public function getSitesIdWithAdminAccess()
+ {
+ return array_unique(array_merge(
+ $this->idsitesByAccess['admin'],
+ $this->idsitesByAccess['superuser'])
+ );
+ }
+
+
+ /**
+ * Returns an array of ID sites for which the user has a VIEW access only.
+ *
+ * @return array Example if the user is ADMIN for 4
+ * and has VIEW access for 1 and 7, it returns array(1, 7);
+ * @see getSitesIdWithAtLeastViewAccess()
+ */
+ public function getSitesIdWithViewAccess()
+ {
+ return $this->idsitesByAccess['view'];
+ }
+
+ /**
+ * Throws an exception if the user is not the SuperUser
+ *
+ * @throws Piwik_Access_NoAccessException
+ */
+ public function checkUserIsSuperUser()
+ {
+ if (!$this->isSuperUser()) {
+ throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilege', array("'superuser'")));
+ }
+ }
+
+ /**
+ * If the user doesn't have an ADMIN access for at least one website, throws an exception
+ *
+ * @throws Piwik_Access_NoAccessException
+ */
+ public function checkUserHasSomeAdminAccess()
+ {
+ if ($this->isSuperUser()) {
+ return;
+ }
+ $idSitesAccessible = $this->getSitesIdWithAdminAccess();
+ if (count($idSitesAccessible) == 0) {
+ throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilegeAtLeastOneWebsite', array('admin')));
+ }
+ }
+
+ /**
+ * If the user doesn't have any view permission, throw exception
+ *
+ * @throws Piwik_Access_NoAccessException
+ */
+ public function checkUserHasSomeViewAccess()
+ {
+ if ($this->isSuperUser()) {
+ return;
+ }
+ $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess();
+ if (count($idSitesAccessible) == 0) {
+ throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilegeAtLeastOneWebsite', array('view')));
+ }
+ }
+
+ /**
+ * This method checks that the user has ADMIN access for the given list of websites.
+ * If the user doesn't have ADMIN access for at least one website of the list, we throw an exception.
+ *
+ * @param int|array $idSites List of ID sites to check
+ * @throws Piwik_Access_NoAccessException If for any of the websites the user doesn't have an ADMIN access
+ */
+ public function checkUserHasAdminAccess($idSites)
+ {
+ if ($this->isSuperUser()) {
+ return;
+ }
+ $idSites = $this->getIdSites($idSites);
+ $idSitesAccessible = $this->getSitesIdWithAdminAccess();
+ foreach ($idSites as $idsite) {
+ if (!in_array($idsite, $idSitesAccessible)) {
+ throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilegeAccessWebsite', array("'admin'", $idsite)));
+ }
+ }
+ }
+
+ /**
+ * This method checks that the user has VIEW or ADMIN access for the given list of websites.
+ * If the user doesn't have VIEW or ADMIN access for at least one website of the list, we throw an exception.
+ *
+ * @param int|array|string $idSites List of ID sites to check (integer, array of integers, string comma separated list of integers)
+ * @throws Piwik_Access_NoAccessException If for any of the websites the user doesn't have an VIEW or ADMIN access
+ */
+ public function checkUserHasViewAccess($idSites)
+ {
+ if ($this->isSuperUser()) {
+ return;
+ }
+ $idSites = $this->getIdSites($idSites);
+ $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess();
+ foreach ($idSites as $idsite) {
+ if (!in_array($idsite, $idSitesAccessible)) {
+ throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilegeAccessWebsite', array("'view'", $idsite)));
+ }
+ }
+ }
+
+ /**
+ * @param int|array|string $idSites
+ * @return array
+ * @throws Piwik_Access_NoAccessException
+ */
+ protected function getIdSites($idSites)
+ {
+ if ($idSites === 'all') {
+ $idSites = $this->getSitesIdWithAtLeastViewAccess();
+ }
+
+ $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites);
+ if (empty($idSites)) {
+ throw new Piwik_Access_NoAccessException("The parameter 'idSite=' is missing from the request.");
+ }
+ return $idSites;
+ }
}
/**
* Exception thrown when a user doesn't have sufficient access.
- *
+ *
* @package Piwik
* @subpackage Piwik_Access
*/
class Piwik_Access_NoAccessException extends Exception
-{}
+{
+}
diff --git a/core/Archive.php b/core/Archive.php
index 1a18efd53a..530694f7a6 100644
--- a/core/Archive.php
+++ b/core/Archive.php
@@ -1,478 +1,456 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* The archive object is used to query specific data for a day or a period of statistics for a given website.
- *
+ *
* Example:
* <pre>
- * $archive = Piwik_Archive::build($idSite = 1, $period = 'week', '2008-03-08' );
- * $dataTable = $archive->getDataTable('Provider_hostnameExt');
- * $dataTable->queueFilter('ReplaceColumnNames');
- * return $dataTable;
+ * $archive = Piwik_Archive::build($idSite = 1, $period = 'week', '2008-03-08' );
+ * $dataTable = $archive->getDataTable('Provider_hostnameExt');
+ * $dataTable->queueFilter('ReplaceColumnNames');
+ * return $dataTable;
* </pre>
- *
+ *
* Example bis:
* <pre>
- * $archive = Piwik_Archive::build($idSite = 3, $period = 'day', $date = 'today' );
- * $nbVisits = $archive->getNumeric('nb_visits');
- * return $nbVisits;
+ * $archive = Piwik_Archive::build($idSite = 3, $period = 'day', $date = 'today' );
+ * $nbVisits = $archive->getNumeric('nb_visits');
+ * return $nbVisits;
* </pre>
- *
+ *
* If the requested statistics are not yet processed, Archive uses ArchiveProcessing to archive the statistics.
- *
+ *
* @package Piwik
* @subpackage Piwik_Archive
*/
abstract class Piwik_Archive
{
- /**
- * When saving DataTables in the DB, we sometimes replace the columns name by these IDs so we save up lots of bytes
- * Eg. INDEX_NB_UNIQ_VISITORS is an integer: 4 bytes, but 'nb_uniq_visitors' is 16 bytes at least
- * (in php it's actually even much more)
- *
- */
- const INDEX_NB_UNIQ_VISITORS = 1;
- const INDEX_NB_VISITS = 2;
- const INDEX_NB_ACTIONS = 3;
- const INDEX_MAX_ACTIONS = 4;
- const INDEX_SUM_VISIT_LENGTH = 5;
- const INDEX_BOUNCE_COUNT = 6;
- const INDEX_NB_VISITS_CONVERTED = 7;
- const INDEX_NB_CONVERSIONS = 8;
- const INDEX_REVENUE = 9;
- const INDEX_GOALS = 10;
- const INDEX_SUM_DAILY_NB_UNIQ_VISITORS = 11;
-
- // Specific to the Actions reports
- const INDEX_PAGE_NB_HITS = 12;
- const INDEX_PAGE_SUM_TIME_SPENT = 13;
-
- const INDEX_PAGE_EXIT_NB_UNIQ_VISITORS = 14;
- const INDEX_PAGE_EXIT_NB_VISITS = 15;
- const INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS = 16;
-
- const INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS = 17;
- const INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS = 18;
- const INDEX_PAGE_ENTRY_NB_VISITS = 19;
- const INDEX_PAGE_ENTRY_NB_ACTIONS = 20;
- const INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH = 21;
- const INDEX_PAGE_ENTRY_BOUNCE_COUNT = 22;
-
- // Ecommerce Items reports
- const INDEX_ECOMMERCE_ITEM_REVENUE = 23;
- const INDEX_ECOMMERCE_ITEM_QUANTITY = 24;
- const INDEX_ECOMMERCE_ITEM_PRICE = 25;
- const INDEX_ECOMMERCE_ORDERS = 26;
- const INDEX_ECOMMERCE_ITEM_PRICE_VIEWED = 27;
-
- // Site Search
- const INDEX_SITE_SEARCH_HAS_NO_RESULT = 28;
- const INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS = 29;
-
- // Performance Analytics
- const INDEX_PAGE_SUM_TIME_GENERATION = 30;
- const INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION = 31;
-
- // Goal reports
- const INDEX_GOAL_NB_CONVERSIONS = 1;
- const INDEX_GOAL_REVENUE = 2;
- const INDEX_GOAL_NB_VISITS_CONVERTED = 3;
-
- const INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL = 4;
- const INDEX_GOAL_ECOMMERCE_REVENUE_TAX = 5;
- const INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING = 6;
- const INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT = 7;
- const INDEX_GOAL_ECOMMERCE_ITEMS = 8;
-
- public static $mappingFromIdToName = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 'nb_uniq_visitors',
- Piwik_Archive::INDEX_NB_VISITS => 'nb_visits',
- Piwik_Archive::INDEX_NB_ACTIONS => 'nb_actions',
- Piwik_Archive::INDEX_MAX_ACTIONS => 'max_actions',
- Piwik_Archive::INDEX_SUM_VISIT_LENGTH => 'sum_visit_length',
- Piwik_Archive::INDEX_BOUNCE_COUNT => 'bounce_count',
- Piwik_Archive::INDEX_NB_VISITS_CONVERTED => 'nb_visits_converted',
- Piwik_Archive::INDEX_NB_CONVERSIONS => 'nb_conversions',
- Piwik_Archive::INDEX_REVENUE => 'revenue',
- Piwik_Archive::INDEX_GOALS => 'goals',
- Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS => 'sum_daily_nb_uniq_visitors',
-
- // Actions metrics
- Piwik_Archive::INDEX_PAGE_NB_HITS => 'nb_hits',
- Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT => 'sum_time_spent',
- Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION => 'sum_time_generation',
- Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION => 'nb_hits_with_time_generation',
-
- Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS => 'exit_nb_uniq_visitors',
- Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS => 'exit_nb_visits',
- Piwik_Archive::INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS => 'sum_daily_exit_nb_uniq_visitors',
-
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS => 'entry_nb_uniq_visitors',
- Piwik_Archive::INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS => 'sum_daily_entry_nb_uniq_visitors',
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS => 'entry_nb_visits',
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS => 'entry_nb_actions',
- Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH => 'entry_sum_visit_length',
- Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT => 'entry_bounce_count',
- Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS => 'nb_hits_following_search',
-
- // Items reports metrics
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE => 'revenue',
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY => 'quantity',
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE => 'price',
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED => 'price_viewed',
- Piwik_Archive::INDEX_ECOMMERCE_ORDERS => 'orders',
- );
-
- public static $mappingFromIdToNameGoal = array(
- Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 'nb_conversions',
- Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 'nb_visits_converted',
- Piwik_Archive::INDEX_GOAL_REVENUE => 'revenue',
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL => 'revenue_subtotal',
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX => 'revenue_tax',
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING => 'revenue_shipping',
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT => 'revenue_discount',
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS => 'items',
- );
-
- /**
- * string indexed column name => Integer indexed column name
+ /**
+ * When saving DataTables in the DB, we sometimes replace the columns name by these IDs so we save up lots of bytes
+ * Eg. INDEX_NB_UNIQ_VISITORS is an integer: 4 bytes, but 'nb_uniq_visitors' is 16 bytes at least
+ * (in php it's actually even much more)
+ *
+ */
+ const INDEX_NB_UNIQ_VISITORS = 1;
+ const INDEX_NB_VISITS = 2;
+ const INDEX_NB_ACTIONS = 3;
+ const INDEX_MAX_ACTIONS = 4;
+ const INDEX_SUM_VISIT_LENGTH = 5;
+ const INDEX_BOUNCE_COUNT = 6;
+ const INDEX_NB_VISITS_CONVERTED = 7;
+ const INDEX_NB_CONVERSIONS = 8;
+ const INDEX_REVENUE = 9;
+ const INDEX_GOALS = 10;
+ const INDEX_SUM_DAILY_NB_UNIQ_VISITORS = 11;
+
+ // Specific to the Actions reports
+ const INDEX_PAGE_NB_HITS = 12;
+ const INDEX_PAGE_SUM_TIME_SPENT = 13;
+
+ const INDEX_PAGE_EXIT_NB_UNIQ_VISITORS = 14;
+ const INDEX_PAGE_EXIT_NB_VISITS = 15;
+ const INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS = 16;
+
+ const INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS = 17;
+ const INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS = 18;
+ const INDEX_PAGE_ENTRY_NB_VISITS = 19;
+ const INDEX_PAGE_ENTRY_NB_ACTIONS = 20;
+ const INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH = 21;
+ const INDEX_PAGE_ENTRY_BOUNCE_COUNT = 22;
+
+ // Ecommerce Items reports
+ const INDEX_ECOMMERCE_ITEM_REVENUE = 23;
+ const INDEX_ECOMMERCE_ITEM_QUANTITY = 24;
+ const INDEX_ECOMMERCE_ITEM_PRICE = 25;
+ const INDEX_ECOMMERCE_ORDERS = 26;
+ const INDEX_ECOMMERCE_ITEM_PRICE_VIEWED = 27;
+
+ // Site Search
+ const INDEX_SITE_SEARCH_HAS_NO_RESULT = 28;
+ const INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS = 29;
+
+ // Performance Analytics
+ const INDEX_PAGE_SUM_TIME_GENERATION = 30;
+ const INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION = 31;
+
+ // Goal reports
+ const INDEX_GOAL_NB_CONVERSIONS = 1;
+ const INDEX_GOAL_REVENUE = 2;
+ const INDEX_GOAL_NB_VISITS_CONVERTED = 3;
+
+ const INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL = 4;
+ const INDEX_GOAL_ECOMMERCE_REVENUE_TAX = 5;
+ const INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING = 6;
+ const INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT = 7;
+ const INDEX_GOAL_ECOMMERCE_ITEMS = 8;
+
+ public static $mappingFromIdToName = array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 'nb_uniq_visitors',
+ Piwik_Archive::INDEX_NB_VISITS => 'nb_visits',
+ Piwik_Archive::INDEX_NB_ACTIONS => 'nb_actions',
+ Piwik_Archive::INDEX_MAX_ACTIONS => 'max_actions',
+ Piwik_Archive::INDEX_SUM_VISIT_LENGTH => 'sum_visit_length',
+ Piwik_Archive::INDEX_BOUNCE_COUNT => 'bounce_count',
+ Piwik_Archive::INDEX_NB_VISITS_CONVERTED => 'nb_visits_converted',
+ Piwik_Archive::INDEX_NB_CONVERSIONS => 'nb_conversions',
+ Piwik_Archive::INDEX_REVENUE => 'revenue',
+ Piwik_Archive::INDEX_GOALS => 'goals',
+ Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS => 'sum_daily_nb_uniq_visitors',
+
+ // Actions metrics
+ Piwik_Archive::INDEX_PAGE_NB_HITS => 'nb_hits',
+ Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT => 'sum_time_spent',
+ Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION => 'sum_time_generation',
+ Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION => 'nb_hits_with_time_generation',
+
+ Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS => 'exit_nb_uniq_visitors',
+ Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS => 'exit_nb_visits',
+ Piwik_Archive::INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS => 'sum_daily_exit_nb_uniq_visitors',
+
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS => 'entry_nb_uniq_visitors',
+ Piwik_Archive::INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS => 'sum_daily_entry_nb_uniq_visitors',
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS => 'entry_nb_visits',
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS => 'entry_nb_actions',
+ Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH => 'entry_sum_visit_length',
+ Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT => 'entry_bounce_count',
+ Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS => 'nb_hits_following_search',
+
+ // Items reports metrics
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE => 'revenue',
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY => 'quantity',
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE => 'price',
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED => 'price_viewed',
+ Piwik_Archive::INDEX_ECOMMERCE_ORDERS => 'orders',
+ );
+
+ public static $mappingFromIdToNameGoal = array(
+ Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 'nb_conversions',
+ Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 'nb_visits_converted',
+ Piwik_Archive::INDEX_GOAL_REVENUE => 'revenue',
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL => 'revenue_subtotal',
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX => 'revenue_tax',
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING => 'revenue_shipping',
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT => 'revenue_discount',
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS => 'items',
+ );
+
+ /**
+ * string indexed column name => Integer indexed column name
* @var array
- */
- public static $mappingFromNameToId = array(
- 'nb_uniq_visitors' => Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
- 'nb_visits' => Piwik_Archive::INDEX_NB_VISITS,
- 'nb_actions' => Piwik_Archive::INDEX_NB_ACTIONS,
- 'max_actions' => Piwik_Archive::INDEX_MAX_ACTIONS,
- 'sum_visit_length' => Piwik_Archive::INDEX_SUM_VISIT_LENGTH,
- 'bounce_count' => Piwik_Archive::INDEX_BOUNCE_COUNT,
- 'nb_visits_converted' => Piwik_Archive::INDEX_NB_VISITS_CONVERTED,
- 'nb_conversions' => Piwik_Archive::INDEX_NB_CONVERSIONS,
- 'revenue' => Piwik_Archive::INDEX_REVENUE,
- 'goals' => Piwik_Archive::INDEX_GOALS,
- 'sum_daily_nb_uniq_visitors' => Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS,
- );
-
- /**
- * Metrics calculated and archived by the Actions plugin.
- *
- * @var array
- */
- public static $actionsMetrics = array(
- 'nb_pageviews',
- 'nb_uniq_pageviews',
- 'nb_downloads',
- 'nb_uniq_downloads',
- 'nb_outlinks',
- 'nb_uniq_outlinks',
- 'nb_searches',
- 'nb_keywords',
- 'nb_hits',
- 'nb_hits_following_search',
- );
-
- const LABEL_ECOMMERCE_CART = 'ecommerceAbandonedCart';
- const LABEL_ECOMMERCE_ORDER = 'ecommerceOrder';
-
- /**
- * Website Piwik_Site
- *
- * @var Piwik_Site
- */
- protected $site = null;
-
- /**
- * Segment applied to the visits set
- * @var Piwik_Segment
- */
- protected $segment = false;
-
- /**
- * Builds an Archive object or returns the same archive if previously built.
- *
- * @param int|string $idSite integer, or comma separated list of integer
- * @param string $period 'week' 'day' etc.
- * @param Piwik_Date|string $strDate 'YYYY-MM-DD' or magic keywords 'today' @see Piwik_Date::factory()
- * @param bool|string $segment Segment definition - defaults to false for Backward Compatibility
- * @param bool|string $_restrictSitesToLogin Used only when running as a scheduled task
- * @return Piwik_Archive
- */
- static public function build($idSite, $period, $strDate, $segment = false, $_restrictSitesToLogin = false )
- {
- if($idSite === 'all')
- {
- $sites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin);
- }
- else
- {
- $sites = Piwik_Site::getIdSitesFromIdSitesString($idSite);
- }
-
- if (!($segment instanceof Piwik_Segment))
- {
- $segment = new Piwik_Segment($segment, $idSite);
- }
-
- // idSite=1,3 or idSite=all
- if( $idSite === 'all'
- || is_array($idSite)
- || count($sites) > 1 )
- {
- $archive = new Piwik_Archive_Array_IndexedBySite($sites, $period, $strDate, $segment, $_restrictSitesToLogin);
- }
- // if a period date string is detected: either 'last30', 'previous10' or 'YYYY-MM-DD,YYYY-MM-DD'
- elseif(is_string($strDate) && self::isMultiplePeriod($strDate, $period))
- {
- $oSite = new Piwik_Site($idSite);
- $archive = new Piwik_Archive_Array_IndexedByDate($oSite, $period, $strDate, $segment);
- }
- // case we request a single archive
- else
- {
- $oSite = new Piwik_Site($idSite);
- $oPeriod = Piwik_Archive::makePeriodFromQueryParams($oSite, $period, $strDate);
-
- $archive = new Piwik_Archive_Single();
- $archive->setPeriod($oPeriod);
- $archive->setSite($oSite);
- $archive->setSegment($segment);
- }
- return $archive;
- }
-
- /**
- * Creates a period instance using a Piwik_Site instance and two strings describing
- * the period & date.
- *
- * @param Piwik_Site $site
- * @param string $strPeriod The period string: day, week, month, year, range
- * @param string $strDate The date or date range string.
- * @return Piwik_Period
- */
- static public function makePeriodFromQueryParams( $site, $strPeriod, $strDate )
- {
- $tz = $site->getTimezone();
-
- if($strPeriod == 'range')
- {
- $oPeriod = new Piwik_Period_Range('range', $strDate, $tz, Piwik_Date::factory('today', $tz));
- }
- else
- {
- $oDate = $strDate;
- if(!($strDate instanceof Piwik_Date))
- {
- if($strDate == 'now' || $strDate == 'today')
- {
- $strDate = date('Y-m-d', Piwik_Date::factory('now', $tz)->getTimestamp());
- }
- elseif($strDate == 'yesterday' || $strDate == 'yesterdaySameTime')
- {
- $strDate = date('Y-m-d', Piwik_Date::factory('now', $tz)->subDay(1)->getTimestamp());
- }
- $oDate = Piwik_Date::factory($strDate);
- }
- $date = $oDate->toString();
- $oPeriod = Piwik_Period::factory($strPeriod, $oDate);
- }
-
- return $oPeriod;
- }
-
- abstract public function prepareArchive();
-
- /**
- * Returns the value of the element $name from the current archive
- * The value to be returned is a numeric value and is stored in the archive_numeric_* tables
- *
- * @param string $name For example Referers_distinctKeywords
- * @return float|int|false False if no value with the given name
- */
- abstract public function getNumeric( $name );
-
- /**
- * Returns the value of the element $name from the current archive
- *
- * The value to be returned is a blob value and is stored in the archive_numeric_* tables
- *
- * It can return anything from strings, to serialized PHP arrays or PHP objects, etc.
- *
- * @param string $name For example Referers_distinctKeywords
- * @return mixed False if no value with the given name
- */
- abstract public function getBlob( $name );
-
- /**
- *
- * @param $fields
- * @return Piwik_DataTable
- */
- abstract public function getDataTableFromNumeric( $fields );
-
- /**
- * This method will build a dataTable from the blob value $name in the current archive.
- *
- * For example $name = 'Referers_searchEngineByKeyword' will return a Piwik_DataTable containing all the keywords
- * If a idSubTable is given, the method will return the subTable of $name
- *
- * @param string $name
- * @param int $idSubTable or null if requesting the parent table
- * @return Piwik_DataTable
- * @throws exception If the value cannot be found
- */
- abstract public function getDataTable( $name, $idSubTable = null );
-
- /**
- * Same as getDataTable() except that it will also load in memory
- * all the subtables for the DataTable $name.
- * You can then access the subtables by using the Piwik_DataTable_Manager getTable()
- *
- * @param string $name
- * @param int|null $idSubTable null if requesting the parent table
- * @return Piwik_DataTable
- */
- abstract public function getDataTableExpanded($name, $idSubTable = null);
-
-
- /**
- * Helper - Loads a DataTable from the Archive.
- * Optionally loads the table recursively,
- * or optionally fetches a given subtable with $idSubtable
- *
- * @param string $name
- * @param int $idSite
- * @param string $period
- * @param Piwik_Date $date
- * @param string $segment
- * @param bool $expanded
- * @param null $idSubtable
- * @return Piwik_DataTable|Piwik_DataTable_Array
- */
- static public function getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded, $idSubtable = null )
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- if($idSubtable === false)
- {
- $idSubtable = null;
- }
-
- if($expanded)
- {
- $dataTable = $archive->getDataTableExpanded($name, $idSubtable);
- }
- else
- {
- $dataTable = $archive->getDataTable($name, $idSubtable);
- }
-
- $dataTable->queueFilter('ReplaceSummaryRowLabel');
-
- return $dataTable;
- }
-
- protected function formatNumericValue($value)
- {
- // If there is no dot, we return as is
- // Note: this could be an integer bigger than 32 bits
- if(strpos($value, '.') === false)
- {
- if($value === false)
- {
- return 0;
- }
- return (float)$value;
- }
-
- // Round up the value with 2 decimals
- // we cast the result as float because returns false when no visitors
- $value = round((float)$value, 2);
- return $value;
- }
-
- public function getSegment()
- {
- return $this->segment;
- }
-
- public function setSegment(Piwik_Segment $segment)
- {
- $this->segment = $segment;
- }
-
- /**
- * Sets the site
- *
- * @param Piwik_Site $site
- */
- public function setSite( Piwik_Site $site )
- {
- $this->site = $site;
- }
-
- /**
- * Gets the site
- *
- * @return Piwik_Site
- */
- public function getSite()
- {
- return $this->site;
- }
-
- /**
- * Returns the Id site associated with this archive
- *
- * @return int
- */
- public function getIdSite()
- {
- return $this->site->getId();
- }
-
- /**
- * Returns true if Segmentation is allowed for this user
- *
- * @return bool
- */
- static public function isSegmentationEnabled()
- {
- return !Piwik::isUserIsAnonymous()
- || Piwik_Config::getInstance()->General['anonymous_user_enable_use_segments_API']
- ;
- }
-
- /**
- * Indicate if $dateString and $period correspond to multiple periods
- *
- * @static
- * @param $dateString
- * @param $period
- * @return boolean
- */
- static public function isMultiplePeriod($dateString, $period)
- {
- return (preg_match('/^(last|previous){1}([0-9]*)$/D', $dateString, $regs)
- || Piwik_Period_Range::parseDateRange($dateString))
- && $period != 'range';
- }
-
- /**
- * Indicate if $idSiteString corresponds to multiple sites.
- *
- * @param string $idSiteString
- * @return bool
- */
- static public function isMultipleSites( $idSiteString )
- {
- return $idSiteString == 'all' || strpos($idSiteString, ',') !== false;
- }
+ */
+ public static $mappingFromNameToId = array(
+ 'nb_uniq_visitors' => Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
+ 'nb_visits' => Piwik_Archive::INDEX_NB_VISITS,
+ 'nb_actions' => Piwik_Archive::INDEX_NB_ACTIONS,
+ 'max_actions' => Piwik_Archive::INDEX_MAX_ACTIONS,
+ 'sum_visit_length' => Piwik_Archive::INDEX_SUM_VISIT_LENGTH,
+ 'bounce_count' => Piwik_Archive::INDEX_BOUNCE_COUNT,
+ 'nb_visits_converted' => Piwik_Archive::INDEX_NB_VISITS_CONVERTED,
+ 'nb_conversions' => Piwik_Archive::INDEX_NB_CONVERSIONS,
+ 'revenue' => Piwik_Archive::INDEX_REVENUE,
+ 'goals' => Piwik_Archive::INDEX_GOALS,
+ 'sum_daily_nb_uniq_visitors' => Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS,
+ );
+
+ /**
+ * Metrics calculated and archived by the Actions plugin.
+ *
+ * @var array
+ */
+ public static $actionsMetrics = array(
+ 'nb_pageviews',
+ 'nb_uniq_pageviews',
+ 'nb_downloads',
+ 'nb_uniq_downloads',
+ 'nb_outlinks',
+ 'nb_uniq_outlinks',
+ 'nb_searches',
+ 'nb_keywords',
+ 'nb_hits',
+ 'nb_hits_following_search',
+ );
+
+ const LABEL_ECOMMERCE_CART = 'ecommerceAbandonedCart';
+ const LABEL_ECOMMERCE_ORDER = 'ecommerceOrder';
+
+ /**
+ * Website Piwik_Site
+ *
+ * @var Piwik_Site
+ */
+ protected $site = null;
+
+ /**
+ * Segment applied to the visits set
+ * @var Piwik_Segment
+ */
+ protected $segment = false;
+
+ /**
+ * Builds an Archive object or returns the same archive if previously built.
+ *
+ * @param int|string $idSite integer, or comma separated list of integer
+ * @param string $period 'week' 'day' etc.
+ * @param Piwik_Date|string $strDate 'YYYY-MM-DD' or magic keywords 'today' @see Piwik_Date::factory()
+ * @param bool|string $segment Segment definition - defaults to false for Backward Compatibility
+ * @param bool|string $_restrictSitesToLogin Used only when running as a scheduled task
+ * @return Piwik_Archive
+ */
+ static public function build($idSite, $period, $strDate, $segment = false, $_restrictSitesToLogin = false)
+ {
+ if ($idSite === 'all') {
+ $sites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin);
+ } else {
+ $sites = Piwik_Site::getIdSitesFromIdSitesString($idSite);
+ }
+
+ if (!($segment instanceof Piwik_Segment)) {
+ $segment = new Piwik_Segment($segment, $idSite);
+ }
+
+ // idSite=1,3 or idSite=all
+ if ($idSite === 'all'
+ || is_array($idSite)
+ || count($sites) > 1
+ ) {
+ $archive = new Piwik_Archive_Array_IndexedBySite($sites, $period, $strDate, $segment, $_restrictSitesToLogin);
+ } // if a period date string is detected: either 'last30', 'previous10' or 'YYYY-MM-DD,YYYY-MM-DD'
+ elseif (is_string($strDate) && self::isMultiplePeriod($strDate, $period)) {
+ $oSite = new Piwik_Site($idSite);
+ $archive = new Piwik_Archive_Array_IndexedByDate($oSite, $period, $strDate, $segment);
+ } // case we request a single archive
+ else {
+ $oSite = new Piwik_Site($idSite);
+ $oPeriod = Piwik_Archive::makePeriodFromQueryParams($oSite, $period, $strDate);
+
+ $archive = new Piwik_Archive_Single();
+ $archive->setPeriod($oPeriod);
+ $archive->setSite($oSite);
+ $archive->setSegment($segment);
+ }
+ return $archive;
+ }
+
+ /**
+ * Creates a period instance using a Piwik_Site instance and two strings describing
+ * the period & date.
+ *
+ * @param Piwik_Site $site
+ * @param string $strPeriod The period string: day, week, month, year, range
+ * @param string $strDate The date or date range string.
+ * @return Piwik_Period
+ */
+ static public function makePeriodFromQueryParams($site, $strPeriod, $strDate)
+ {
+ $tz = $site->getTimezone();
+
+ if ($strPeriod == 'range') {
+ $oPeriod = new Piwik_Period_Range('range', $strDate, $tz, Piwik_Date::factory('today', $tz));
+ } else {
+ $oDate = $strDate;
+ if (!($strDate instanceof Piwik_Date)) {
+ if ($strDate == 'now' || $strDate == 'today') {
+ $strDate = date('Y-m-d', Piwik_Date::factory('now', $tz)->getTimestamp());
+ } elseif ($strDate == 'yesterday' || $strDate == 'yesterdaySameTime') {
+ $strDate = date('Y-m-d', Piwik_Date::factory('now', $tz)->subDay(1)->getTimestamp());
+ }
+ $oDate = Piwik_Date::factory($strDate);
+ }
+ $date = $oDate->toString();
+ $oPeriod = Piwik_Period::factory($strPeriod, $oDate);
+ }
+
+ return $oPeriod;
+ }
+
+ abstract public function prepareArchive();
+
+ /**
+ * Returns the value of the element $name from the current archive
+ * The value to be returned is a numeric value and is stored in the archive_numeric_* tables
+ *
+ * @param string $name For example Referers_distinctKeywords
+ * @return float|int|false False if no value with the given name
+ */
+ abstract public function getNumeric($name);
+
+ /**
+ * Returns the value of the element $name from the current archive
+ *
+ * The value to be returned is a blob value and is stored in the archive_numeric_* tables
+ *
+ * It can return anything from strings, to serialized PHP arrays or PHP objects, etc.
+ *
+ * @param string $name For example Referers_distinctKeywords
+ * @return mixed False if no value with the given name
+ */
+ abstract public function getBlob($name);
+
+ /**
+ *
+ * @param $fields
+ * @return Piwik_DataTable
+ */
+ abstract public function getDataTableFromNumeric($fields);
+
+ /**
+ * This method will build a dataTable from the blob value $name in the current archive.
+ *
+ * For example $name = 'Referers_searchEngineByKeyword' will return a Piwik_DataTable containing all the keywords
+ * If a idSubTable is given, the method will return the subTable of $name
+ *
+ * @param string $name
+ * @param int $idSubTable or null if requesting the parent table
+ * @return Piwik_DataTable
+ * @throws exception If the value cannot be found
+ */
+ abstract public function getDataTable($name, $idSubTable = null);
+
+ /**
+ * Same as getDataTable() except that it will also load in memory
+ * all the subtables for the DataTable $name.
+ * You can then access the subtables by using the Piwik_DataTable_Manager getTable()
+ *
+ * @param string $name
+ * @param int|null $idSubTable null if requesting the parent table
+ * @return Piwik_DataTable
+ */
+ abstract public function getDataTableExpanded($name, $idSubTable = null);
+
+
+ /**
+ * Helper - Loads a DataTable from the Archive.
+ * Optionally loads the table recursively,
+ * or optionally fetches a given subtable with $idSubtable
+ *
+ * @param string $name
+ * @param int $idSite
+ * @param string $period
+ * @param Piwik_Date $date
+ * @param string $segment
+ * @param bool $expanded
+ * @param null $idSubtable
+ * @return Piwik_DataTable|Piwik_DataTable_Array
+ */
+ static public function getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded, $idSubtable = null)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ if ($idSubtable === false) {
+ $idSubtable = null;
+ }
+
+ if ($expanded) {
+ $dataTable = $archive->getDataTableExpanded($name, $idSubtable);
+ } else {
+ $dataTable = $archive->getDataTable($name, $idSubtable);
+ }
+
+ $dataTable->queueFilter('ReplaceSummaryRowLabel');
+
+ return $dataTable;
+ }
+
+ protected function formatNumericValue($value)
+ {
+ // If there is no dot, we return as is
+ // Note: this could be an integer bigger than 32 bits
+ if (strpos($value, '.') === false) {
+ if ($value === false) {
+ return 0;
+ }
+ return (float)$value;
+ }
+
+ // Round up the value with 2 decimals
+ // we cast the result as float because returns false when no visitors
+ $value = round((float)$value, 2);
+ return $value;
+ }
+
+ public function getSegment()
+ {
+ return $this->segment;
+ }
+
+ public function setSegment(Piwik_Segment $segment)
+ {
+ $this->segment = $segment;
+ }
+
+ /**
+ * Sets the site
+ *
+ * @param Piwik_Site $site
+ */
+ public function setSite(Piwik_Site $site)
+ {
+ $this->site = $site;
+ }
+
+ /**
+ * Gets the site
+ *
+ * @return Piwik_Site
+ */
+ public function getSite()
+ {
+ return $this->site;
+ }
+
+ /**
+ * Returns the Id site associated with this archive
+ *
+ * @return int
+ */
+ public function getIdSite()
+ {
+ return $this->site->getId();
+ }
+
+ /**
+ * Returns true if Segmentation is allowed for this user
+ *
+ * @return bool
+ */
+ static public function isSegmentationEnabled()
+ {
+ return !Piwik::isUserIsAnonymous()
+ || Piwik_Config::getInstance()->General['anonymous_user_enable_use_segments_API'];
+ }
+
+ /**
+ * Indicate if $dateString and $period correspond to multiple periods
+ *
+ * @static
+ * @param $dateString
+ * @param $period
+ * @return boolean
+ */
+ static public function isMultiplePeriod($dateString, $period)
+ {
+ return (preg_match('/^(last|previous){1}([0-9]*)$/D', $dateString, $regs)
+ || Piwik_Period_Range::parseDateRange($dateString))
+ && $period != 'range';
+ }
+
+ /**
+ * Indicate if $idSiteString corresponds to multiple sites.
+ *
+ * @param string $idSiteString
+ * @return bool
+ */
+ static public function isMultipleSites($idSiteString)
+ {
+ return $idSiteString == 'all' || strpos($idSiteString, ',') !== false;
+ }
}
diff --git a/core/Archive/Array.php b/core/Archive/Array.php
index cd9472a245..896cc1e17c 100644
--- a/core/Archive/Array.php
+++ b/core/Archive/Array.php
@@ -1,152 +1,147 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Piwik_Archive_Array is used to store multiple archives,
+ * Piwik_Archive_Array is used to store multiple archives,
* for example one archive for a given day for each Piwik website
*
* @package Piwik
* @subpackage Piwik_Archive
*/
abstract class Piwik_Archive_Array extends Piwik_Archive
-{
- /**
- * This array contains one Piwik_Archive per entry in the period
- *
- * @var Piwik_Archive[]
- */
- protected $archives = array();
-
- abstract protected function getIndexName();
- abstract protected function getDataTableLabelValue( $archive );
+{
+ /**
+ * This array contains one Piwik_Archive per entry in the period
+ *
+ * @var Piwik_Archive[]
+ */
+ protected $archives = array();
+
+ abstract protected function getIndexName();
+
+ abstract protected function getDataTableLabelValue($archive);
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ foreach ($this->archives as $archive) {
+ destroy($archive);
+ }
+ $this->archives = array();
+ }
+
+ /**
+ * Prepares each archive in the array
+ */
+ public function prepareArchive()
+ {
+ foreach ($this->archives as $archive) {
+ $archive->prepareArchive();
+ }
+ }
+
+ /**
+ * Returns a newly created Piwik_DataTable_Array.
+ *
+ * @return Piwik_DataTable_Array
+ */
+ protected function getNewDataTableArray()
+ {
+ $table = new Piwik_DataTable_Array();
+ $table->setKeyName($this->getIndexName());
+ return $table;
+ }
+
+ /**
+ * Returns a DataTable_Array containing numeric values
+ * of the element $name from the archives in this Archive_Array.
+ *
+ * @param string $name Name of the mysql table field to load eg. Referers_distinctKeywords
+ * @return Piwik_DataTable_Array containing the requested numeric value for each Archive
+ */
+ public function getNumeric($name)
+ {
+ $table = $this->getNewDataTableArray();
+
+ foreach ($this->archives as $archive) {
+ $numeric = $archive->getNumeric($name);
+ $subTable = $archive->makeDataTable($isSimple = true);
+ $subTable->addRowsFromArray(array($numeric));
+ $table->addTable($subTable, $this->getDataTableLabelValue($archive));
+ }
+
+ return $table;
+ }
+
+ /**
+ * Returns a DataTable_Array containing values
+ * of the element $name from the archives in this Archive_Array.
+ *
+ * The value to be returned are blob values (stored in the archive_numeric_* tables in the DB). *
+ * It can return anything from strings, to serialized PHP arrays or PHP objects, etc.
+ *
+ * @param string $name Name of the mysql table field to load eg. Referers_keywordBySearchEngine
+ * @return Piwik_DataTable_Array containing the requested blob values for each Archive
+ */
+ public function getBlob($name)
+ {
+ $table = $this->getNewDataTableArray();
+
+ foreach ($this->archives as $archive) {
+ $blob = $archive->getBlob($name);
+ $subTable = $archive->makeNewDataTable($isSimple = true);
+ $subTable->addRowsFromArray(array('blob' => $blob));
+ $table->addTable($subTable, $this->getDataTableLabelValue($archive));
+ }
+ return $table;
+ }
+
+ /**
+ * Given a BLOB field name (eg. 'Referers_searchEngineByKeyword'), it will return a Piwik_DataTable_Array
+ * which is an array of Piwik_DataTable, ordered by chronological order
+ *
+ * @param string $name Name of the mysql table field to load
+ * @param int $idSubTable optional idSubDataTable
+ * @return Piwik_DataTable_Array
+ * @throws Exception If the value cannot be found
+ */
+ public function getDataTable($name, $idSubTable = null)
+ {
+ $table = $this->getNewDataTableArray();
+ foreach ($this->archives as $archive) {
+ $subTable = $archive->getDataTable($name, $idSubTable);
+ $table->addTable($subTable, $this->getDataTableLabelValue($archive));
+ }
+ return $table;
+ }
- /**
- * Destructor
- */
- public function __destruct()
- {
- foreach($this->archives as $archive)
- {
- destroy($archive);
- }
- $this->archives = array();
- }
- /**
- * Prepares each archive in the array
- */
- public function prepareArchive()
- {
- foreach($this->archives as $archive)
- {
- $archive->prepareArchive();
- }
- }
-
- /**
- * Returns a newly created Piwik_DataTable_Array.
- *
- * @return Piwik_DataTable_Array
- */
- protected function getNewDataTableArray()
- {
- $table = new Piwik_DataTable_Array();
- $table->setKeyName($this->getIndexName());
- return $table;
- }
-
- /**
- * Returns a DataTable_Array containing numeric values
- * of the element $name from the archives in this Archive_Array.
- *
- * @param string $name Name of the mysql table field to load eg. Referers_distinctKeywords
- * @return Piwik_DataTable_Array containing the requested numeric value for each Archive
- */
- public function getNumeric( $name )
- {
- $table = $this->getNewDataTableArray();
-
- foreach($this->archives as $archive)
- {
- $numeric = $archive->getNumeric( $name ) ;
- $subTable = $archive->makeDataTable($isSimple = true);
- $subTable->addRowsFromArray( array( $numeric ) );
- $table->addTable($subTable, $this->getDataTableLabelValue($archive));
- }
-
- return $table;
- }
-
- /**
- * Returns a DataTable_Array containing values
- * of the element $name from the archives in this Archive_Array.
- *
- * The value to be returned are blob values (stored in the archive_numeric_* tables in the DB). *
- * It can return anything from strings, to serialized PHP arrays or PHP objects, etc.
- *
- * @param string $name Name of the mysql table field to load eg. Referers_keywordBySearchEngine
- * @return Piwik_DataTable_Array containing the requested blob values for each Archive
- */
- public function getBlob( $name )
- {
- $table = $this->getNewDataTableArray();
-
- foreach($this->archives as $archive)
- {
- $blob = $archive->getBlob( $name ) ;
- $subTable = $archive->makeNewDataTable($isSimple = true);
- $subTable->addRowsFromArray( array('blob' => $blob));
- $table->addTable($subTable, $this->getDataTableLabelValue($archive));
- }
- return $table;
- }
-
- /**
- * Given a BLOB field name (eg. 'Referers_searchEngineByKeyword'), it will return a Piwik_DataTable_Array
- * which is an array of Piwik_DataTable, ordered by chronological order
- *
- * @param string $name Name of the mysql table field to load
- * @param int $idSubTable optional idSubDataTable
- * @return Piwik_DataTable_Array
- * @throws Exception If the value cannot be found
- */
- public function getDataTable( $name, $idSubTable = null )
- {
- $table = $this->getNewDataTableArray();
- foreach($this->archives as $archive)
- {
- $subTable = $archive->getDataTable( $name, $idSubTable ) ;
- $table->addTable($subTable, $this->getDataTableLabelValue($archive));
- }
- return $table;
- }
-
-
- /**
- * Same as getDataTable() except that it will also load in memory
- * all the subtables for the DataTable $name.
- * You can then access the subtables by using the Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
- *
- * @param string $name Name of the mysql table field to load
- * @param int $idSubTable optional idSubDataTable
- * @return Piwik_DataTable_Array
- */
- public function getDataTableExpanded($name, $idSubTable = null)
- {
- $table = $this->getNewDataTableArray();
- foreach($this->archives as $archive)
- {
- $subTable = $archive->getDataTableExpanded( $name, $idSubTable ) ;
- $table->addTable($subTable, $this->getDataTableLabelValue($archive));
- }
- return $table;
- }
+ /**
+ * Same as getDataTable() except that it will also load in memory
+ * all the subtables for the DataTable $name.
+ * You can then access the subtables by using the Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
+ *
+ * @param string $name Name of the mysql table field to load
+ * @param int $idSubTable optional idSubDataTable
+ * @return Piwik_DataTable_Array
+ */
+ public function getDataTableExpanded($name, $idSubTable = null)
+ {
+ $table = $this->getNewDataTableArray();
+ foreach ($this->archives as $archive) {
+ $subTable = $archive->getDataTableExpanded($name, $idSubTable);
+ $table->addTable($subTable, $this->getDataTableLabelValue($archive));
+ }
+ return $table;
+ }
}
diff --git a/core/Archive/Array/IndexedByDate.php b/core/Archive/Array/IndexedByDate.php
index 91bbab2a78..9a59712d4a 100644
--- a/core/Archive/Array/IndexedByDate.php
+++ b/core/Archive/Array/IndexedByDate.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -13,129 +13,119 @@
* @package Piwik
* @subpackage Piwik_Archive
*/
-class Piwik_Archive_Array_IndexedByDate extends Piwik_Archive_Array
+class Piwik_Archive_Array_IndexedByDate extends Piwik_Archive_Array
{
- /**
- * Builds an array of Piwik_Archive of a given date range
- *
- * @param Piwik_Site $oSite
- * @param string $strPeriod eg. 'day' 'week' etc.
- * @param string $strDate A date range, eg. 'last10', 'previous5' or 'YYYY-MM-DD,YYYY-MM-DD'
- * @param Piwik_Segment $segment
- */
- public function __construct(Piwik_Site $oSite, $strPeriod, $strDate, Piwik_Segment $segment)
- {
- $rangePeriod = new Piwik_Period_Range($strPeriod, $strDate, $oSite->getTimezone());
- foreach($rangePeriod->getSubperiods() as $subPeriod)
- {
- $startDate = $subPeriod->getDateStart();
- $archive = Piwik_Archive::build($oSite->getId(), $strPeriod, $startDate, $segment->getString() );
- $archive->setSegment($segment);
- $this->archives[] = $archive;
- }
- $this->setSite($oSite);
- }
+ /**
+ * Builds an array of Piwik_Archive of a given date range
+ *
+ * @param Piwik_Site $oSite
+ * @param string $strPeriod eg. 'day' 'week' etc.
+ * @param string $strDate A date range, eg. 'last10', 'previous5' or 'YYYY-MM-DD,YYYY-MM-DD'
+ * @param Piwik_Segment $segment
+ */
+ public function __construct(Piwik_Site $oSite, $strPeriod, $strDate, Piwik_Segment $segment)
+ {
+ $rangePeriod = new Piwik_Period_Range($strPeriod, $strDate, $oSite->getTimezone());
+ foreach ($rangePeriod->getSubperiods() as $subPeriod) {
+ $startDate = $subPeriod->getDateStart();
+ $archive = Piwik_Archive::build($oSite->getId(), $strPeriod, $startDate, $segment->getString());
+ $archive->setSegment($segment);
+ $this->archives[] = $archive;
+ }
+ $this->setSite($oSite);
+ }
- /**
- * @return string
- */
- protected function getIndexName()
- {
- return 'date';
- }
+ /**
+ * @return string
+ */
+ protected function getIndexName()
+ {
+ return 'date';
+ }
- /**
- * @param Piwik_Archive $archive
- * @return mixed
- */
- protected function getDataTableLabelValue( $archive )
- {
- return $archive->getPrettyDate();
- }
-
- /**
- * Given a list of fields defining numeric values, it will return a Piwik_DataTable_Array
- * which is an array of Piwik_DataTable_Simple, ordered by chronological order
- *
- * @param array|string $fields array( fieldName1, fieldName2, ...) Names of the mysql table fields to load
- * @return Piwik_DataTable_Array
- */
- public function getDataTableFromNumeric( $fields )
- {
- $inNames = Piwik_Common::getSqlStringFieldsArray($fields);
-
- // we select in different shots
- // one per distinct table (case we select last 300 days, maybe we will select from 10 different tables)
- $queries = array();
- foreach($this->archives as $archive)
- {
- $archive->setRequestedReport( is_string($fields) ? $fields : current($fields) );
- $archive->prepareArchive();
- if(!$archive->isThereSomeVisits)
- {
- continue;
- }
-
- $table = $archive->archiveProcessing->getTableArchiveNumericName();
+ /**
+ * @param Piwik_Archive $archive
+ * @return mixed
+ */
+ protected function getDataTableLabelValue($archive)
+ {
+ return $archive->getPrettyDate();
+ }
- // for every query store IDs
- $queries[$table][] = $archive->getIdArchive();
- }
- // we select the requested value
- $db = Zend_Registry::get('db');
-
- // date => array( 'field1' =>X, 'field2'=>Y)
- // date2 => array( 'field1' =>X2, 'field2'=>Y2)
-
- $arrayValues = array();
- foreach($queries as $table => $aIds)
- {
- $inIds = implode(', ', array_filter($aIds));
- if(empty($inIds))
- {
- // Probable timezone configuration error, i.e., mismatch between PHP and MySQL server.
- continue;
- }
+ /**
+ * Given a list of fields defining numeric values, it will return a Piwik_DataTable_Array
+ * which is an array of Piwik_DataTable_Simple, ordered by chronological order
+ *
+ * @param array|string $fields array( fieldName1, fieldName2, ...) Names of the mysql table fields to load
+ * @return Piwik_DataTable_Array
+ */
+ public function getDataTableFromNumeric($fields)
+ {
+ $inNames = Piwik_Common::getSqlStringFieldsArray($fields);
- $sql = "SELECT value, name, date1 as startDate
+ // we select in different shots
+ // one per distinct table (case we select last 300 days, maybe we will select from 10 different tables)
+ $queries = array();
+ foreach ($this->archives as $archive) {
+ $archive->setRequestedReport(is_string($fields) ? $fields : current($fields));
+ $archive->prepareArchive();
+ if (!$archive->isThereSomeVisits) {
+ continue;
+ }
+
+ $table = $archive->archiveProcessing->getTableArchiveNumericName();
+
+ // for every query store IDs
+ $queries[$table][] = $archive->getIdArchive();
+ }
+ // we select the requested value
+ $db = Zend_Registry::get('db');
+
+ // date => array( 'field1' =>X, 'field2'=>Y)
+ // date2 => array( 'field1' =>X2, 'field2'=>Y2)
+
+ $arrayValues = array();
+ foreach ($queries as $table => $aIds) {
+ $inIds = implode(', ', array_filter($aIds));
+ if (empty($inIds)) {
+ // Probable timezone configuration error, i.e., mismatch between PHP and MySQL server.
+ continue;
+ }
+
+ $sql = "SELECT value, name, date1 as startDate
FROM $table
WHERE idarchive IN ( $inIds )
AND name IN ( $inNames )
ORDER BY date1, name";
- $values = $db->fetchAll($sql, $fields);
- foreach($values as $value)
- {
- $timestamp = Piwik_Date::factory($value['startDate'])->getTimestamp();
- $arrayValues[$timestamp][$value['name']] = $this->formatNumericValue($value['value']);
- }
- }
-
- $contentArray = array();
- // we add empty tables so that every requested date has an entry, even if there is nothing
- // example: <result date="2007-01-01" />
- $archiveByTimestamp = array();
- foreach($this->archives as $archive)
- {
- $timestamp = $archive->getTimestampStartDate();
- $archiveByTimestamp[$timestamp] = $archive;
- $contentArray[$timestamp]['table'] = $archive->makeDataTable($simple = true);
- $contentArray[$timestamp]['prettyDate'] = $archive->getPrettyDate();
- }
+ $values = $db->fetchAll($sql, $fields);
+ foreach ($values as $value) {
+ $timestamp = Piwik_Date::factory($value['startDate'])->getTimestamp();
+ $arrayValues[$timestamp][$value['name']] = $this->formatNumericValue($value['value']);
+ }
+ }
+
+ $contentArray = array();
+ // we add empty tables so that every requested date has an entry, even if there is nothing
+ // example: <result date="2007-01-01" />
+ $archiveByTimestamp = array();
+ foreach ($this->archives as $archive) {
+ $timestamp = $archive->getTimestampStartDate();
+ $archiveByTimestamp[$timestamp] = $archive;
+ $contentArray[$timestamp]['table'] = $archive->makeDataTable($simple = true);
+ $contentArray[$timestamp]['prettyDate'] = $archive->getPrettyDate();
+ }
- foreach($arrayValues as $timestamp => $aNameValues)
- {
- // undefined in some edge/unknown cases see http://dev.piwik.org/trac/ticket/2578
- if(isset($contentArray[$timestamp]['table']))
- {
- $contentArray[$timestamp]['table']->addRowsFromArray($aNameValues);
- }
- }
+ foreach ($arrayValues as $timestamp => $aNameValues) {
+ // undefined in some edge/unknown cases see http://dev.piwik.org/trac/ticket/2578
+ if (isset($contentArray[$timestamp]['table'])) {
+ $contentArray[$timestamp]['table']->addRowsFromArray($aNameValues);
+ }
+ }
- $tableArray = $this->getNewDataTableArray();
- foreach($contentArray as $timestamp => $aData)
- {
- $tableArray->addTable($aData['table'], $aData['prettyDate']);
- }
- return $tableArray;
- }
+ $tableArray = $this->getNewDataTableArray();
+ foreach ($contentArray as $timestamp => $aData) {
+ $tableArray->addTable($aData['table'], $aData['prettyDate']);
+ }
+ return $tableArray;
+ }
}
diff --git a/core/Archive/Array/IndexedBySite.php b/core/Archive/Array/IndexedBySite.php
index c6fee07bcc..1ac9ff682a 100644
--- a/core/Archive/Array/IndexedBySite.php
+++ b/core/Archive/Array/IndexedBySite.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -13,274 +13,254 @@
* @package Piwik
* @subpackage Piwik_Archive
*/
-class Piwik_Archive_Array_IndexedBySite extends Piwik_Archive_Array
+class Piwik_Archive_Array_IndexedBySite extends Piwik_Archive_Array
{
- /**
- * Used to cache the name of the table that holds the data this archive.
- *
- * This will only be used if the archives held by this instance are instances of
- * Piwik_Archive_Single.
- */
- private $tableName = null;
-
- /**
- * @param array $sites array of siteIds
- * @param string $strPeriod eg. 'day' 'week' etc.
- * @param string $strDate A date range, eg. 'last10', 'previous5' or 'YYYY-MM-DD,YYYY-MM-DD'
- * @param Piwik_Segment $segment
- * @param string $_restrictSitesToLogin
- */
- function __construct($sites, $strPeriod, $strDate, Piwik_Segment $segment, $_restrictSitesToLogin)
- {
- foreach($sites as $idSite)
- {
- $archive = Piwik_Archive::build($idSite, $strPeriod, $strDate, $segment, $_restrictSitesToLogin );
- $this->archives[$idSite] = $archive;
- }
- ksort( $this->archives );
- }
-
- /**
- * @return string
- */
- protected function getIndexName()
- {
- return 'idSite';
- }
-
- /**
- * @param Piwik_Archive $archive
- * @return mixed
- */
- protected function getDataTableLabelValue( $archive )
- {
- return $archive->getIdSite();
- }
-
- /**
- * Given a list of fields defining numeric values, it will return a Piwik_DataTable_Array
- * ordered by idsite
- *
- * @param array|string $fields array( fieldName1, fieldName2, ...) Names of the mysql table fields to load
- * @return Piwik_DataTable_Array
- */
- public function getDataTableFromNumeric( $fields )
- {
- $tableArray = $this->getNewDataTableArray();
- if ($this->getFirstArchive() instanceof Piwik_Archive_Single)
- {
- $values = $this->getValues($fields);
- foreach($this->archives as $idSite => $archive)
- {
- $table = $archive->makeDataTable($isSimple = true);
- if (array_key_exists($idSite, $values))
- {
- $table->addRowsFromArray($values[$idSite]);
- }
- $tableArray->addTable($table, $idSite);
- }
- }
- elseif ($this->getFirstArchive() instanceof Piwik_Archive_Array)
- {
- foreach($this->archives as $idSite => $archive)
- {
- $tableArray->addTable($archive->getDataTableFromNumeric($fields), $idSite);
- }
- }
-
- return $tableArray;
- }
-
- /**
- * Returns the values of the requested fields
- *
- * @param array $fields
- * @return array
- */
- private function getValues($fields)
- {
- // Creating the default array, to ensure consistent order
- $defaultValues = array();
- foreach($fields as $field) { $defaultValues[$field] = null; }
-
- $arrayValues = array();
- foreach($this->loadValuesFromDB($fields) as $value)
- {
- if(!isset($arrayValues[$value['idsite']]))
- {
- $arrayValues[$value['idsite']] = $defaultValues;
- }
- $arrayValues[$value['idsite']][$value['name']] = $this->formatNumericValue( $value['value'] );
- }
- return $arrayValues;
- }
-
- /**
- * @param $fields
- * @return array|array (one row in the array per row fetched in the DB)
- */
- private function loadValuesFromDB($fields)
- {
- $requestedMetrics = is_string($fields) ? array($fields) : $fields;
- $inNames = Piwik_Common::getSqlStringFieldsArray($fields);
-
- // get the archive ids
- if (!$this->getFirstArchive()->isArchivingDisabled())
- {
- $archiveIds = $this->getArchiveIdsAfterLaunching($requestedMetrics);
- }
- else
- {
- $archiveIds = $this->getArchiveIdsWithoutLaunching($requestedMetrics);
- }
-
- $archiveIds = implode(', ', array_filter($archiveIds));
-
- // if no archive ids are found, avoid executing any SQL queries
- if(empty($archiveIds))
- {
- return array();
- }
-
- // select archive data
- $sql = "SELECT value, name, idarchive, idsite
+ /**
+ * Used to cache the name of the table that holds the data this archive.
+ *
+ * This will only be used if the archives held by this instance are instances of
+ * Piwik_Archive_Single.
+ */
+ private $tableName = null;
+
+ /**
+ * @param array $sites array of siteIds
+ * @param string $strPeriod eg. 'day' 'week' etc.
+ * @param string $strDate A date range, eg. 'last10', 'previous5' or 'YYYY-MM-DD,YYYY-MM-DD'
+ * @param Piwik_Segment $segment
+ * @param string $_restrictSitesToLogin
+ */
+ function __construct($sites, $strPeriod, $strDate, Piwik_Segment $segment, $_restrictSitesToLogin)
+ {
+ foreach ($sites as $idSite) {
+ $archive = Piwik_Archive::build($idSite, $strPeriod, $strDate, $segment, $_restrictSitesToLogin);
+ $this->archives[$idSite] = $archive;
+ }
+ ksort($this->archives);
+ }
+
+ /**
+ * @return string
+ */
+ protected function getIndexName()
+ {
+ return 'idSite';
+ }
+
+ /**
+ * @param Piwik_Archive $archive
+ * @return mixed
+ */
+ protected function getDataTableLabelValue($archive)
+ {
+ return $archive->getIdSite();
+ }
+
+ /**
+ * Given a list of fields defining numeric values, it will return a Piwik_DataTable_Array
+ * ordered by idsite
+ *
+ * @param array|string $fields array( fieldName1, fieldName2, ...) Names of the mysql table fields to load
+ * @return Piwik_DataTable_Array
+ */
+ public function getDataTableFromNumeric($fields)
+ {
+ $tableArray = $this->getNewDataTableArray();
+ if ($this->getFirstArchive() instanceof Piwik_Archive_Single) {
+ $values = $this->getValues($fields);
+ foreach ($this->archives as $idSite => $archive) {
+ $table = $archive->makeDataTable($isSimple = true);
+ if (array_key_exists($idSite, $values)) {
+ $table->addRowsFromArray($values[$idSite]);
+ }
+ $tableArray->addTable($table, $idSite);
+ }
+ } elseif ($this->getFirstArchive() instanceof Piwik_Archive_Array) {
+ foreach ($this->archives as $idSite => $archive) {
+ $tableArray->addTable($archive->getDataTableFromNumeric($fields), $idSite);
+ }
+ }
+
+ return $tableArray;
+ }
+
+ /**
+ * Returns the values of the requested fields
+ *
+ * @param array $fields
+ * @return array
+ */
+ private function getValues($fields)
+ {
+ // Creating the default array, to ensure consistent order
+ $defaultValues = array();
+ foreach ($fields as $field) {
+ $defaultValues[$field] = null;
+ }
+
+ $arrayValues = array();
+ foreach ($this->loadValuesFromDB($fields) as $value) {
+ if (!isset($arrayValues[$value['idsite']])) {
+ $arrayValues[$value['idsite']] = $defaultValues;
+ }
+ $arrayValues[$value['idsite']][$value['name']] = $this->formatNumericValue($value['value']);
+ }
+ return $arrayValues;
+ }
+
+ /**
+ * @param $fields
+ * @return array|array (one row in the array per row fetched in the DB)
+ */
+ private function loadValuesFromDB($fields)
+ {
+ $requestedMetrics = is_string($fields) ? array($fields) : $fields;
+ $inNames = Piwik_Common::getSqlStringFieldsArray($fields);
+
+ // get the archive ids
+ if (!$this->getFirstArchive()->isArchivingDisabled()) {
+ $archiveIds = $this->getArchiveIdsAfterLaunching($requestedMetrics);
+ } else {
+ $archiveIds = $this->getArchiveIdsWithoutLaunching($requestedMetrics);
+ }
+
+ $archiveIds = implode(', ', array_filter($archiveIds));
+
+ // if no archive ids are found, avoid executing any SQL queries
+ if (empty($archiveIds)) {
+ return array();
+ }
+
+ // select archive data
+ $sql = "SELECT value, name, idarchive, idsite
FROM {$this->getNumericTableName()}
WHERE idarchive IN ( $archiveIds )
AND name IN ( $inNames )";
-
- return Piwik_FetchAll($sql, $fields);
- }
-
- /**
- * Returns the first archive in the list
- *
- * @return Piwik_Archive
- */
- private function getFirstArchive()
- {
- return reset($this->archives);
- }
-
- /**
- * Gets the archive id of every Single archive this archive holds. This method
- * will launch the archiving process if appropriate.
- *
- * @param array $metrics The requested archive metrics.
- * @throws Exception
- * @return array
- */
- private function getArchiveIdsAfterLaunching( $metrics )
- {
- // collect the individual report names for the requested metrics
- $reports = array();
- foreach($metrics as $metric)
- {
- $report = Piwik_Archive_Single::getRequestedReportFor($metric);
- $reports[$report] = $metric;
- }
-
- // process archives for each individual report
- $archiveIds = array();
- foreach($reports as $report => $metric)
- {
- // prepare archives (this will launch archiving when appropriate)
- foreach($this->archives as $archive)
- {
- // NOTE: Piwik_Archive_Single expects a metric here, not a report
- $archive->setRequestedReport( $metric );
- $archive->prepareArchive();
- }
-
- // collect archive ids for archives that have visits
- foreach($this->archives as $archive)
- {
- if( !$archive->isThereSomeVisits )
- {
- continue;
- }
-
- $archiveIds[] = $archive->getIdArchive();
-
- if( $this->getNumericTableName() != $archive->archiveProcessing->getTableArchiveNumericName())
- {
- throw new Exception("Piwik_Archive_Array_IndexedBySite::getDataTableFromNumeric() algorithm won't work if data is stored in different tables");
- }
- }
- }
-
- return $archiveIds;
- }
-
- /**
- * Gets the archive id of every Single archive this archive holds. This method
- * will not launch the archiving process.
- *
- * @param array $metrics The requested archive metrics.
- * @return array
- */
- private function getArchiveIdsWithoutLaunching( $metrics )
- {
- $firstArchive = $this->getFirstArchive();
- $segment = $firstArchive->getSegment();
- $period = $firstArchive->getPeriod();
-
- // the flags used to tell how the archiving process for a specific archive was completed,
- // if it was completed
- $doneFlags = array();
- foreach ($metrics as $metric)
- {
- $done = Piwik_ArchiveProcessing::getDoneStringFlagFor($segment, $period, $metric);
- $donePlugins = Piwik_ArchiveProcessing::getDoneStringFlagFor($segment, $period, $metric, true);
-
- $doneFlags[$done] = $done;
- $doneFlags[$donePlugins] = $donePlugins;
- }
-
- $allDoneFlags = "'".implode("','", $doneFlags)."'";
-
- // create the SQL to query every archive ID
- $nameCondition = "(name IN ($allDoneFlags)) AND
- (value = '".Piwik_ArchiveProcessing::DONE_OK."' OR
- value = '".Piwik_ArchiveProcessing::DONE_OK_TEMPORARY."')";
-
- $sql = "SELECT idsite,
+
+ return Piwik_FetchAll($sql, $fields);
+ }
+
+ /**
+ * Returns the first archive in the list
+ *
+ * @return Piwik_Archive
+ */
+ private function getFirstArchive()
+ {
+ return reset($this->archives);
+ }
+
+ /**
+ * Gets the archive id of every Single archive this archive holds. This method
+ * will launch the archiving process if appropriate.
+ *
+ * @param array $metrics The requested archive metrics.
+ * @throws Exception
+ * @return array
+ */
+ private function getArchiveIdsAfterLaunching($metrics)
+ {
+ // collect the individual report names for the requested metrics
+ $reports = array();
+ foreach ($metrics as $metric) {
+ $report = Piwik_Archive_Single::getRequestedReportFor($metric);
+ $reports[$report] = $metric;
+ }
+
+ // process archives for each individual report
+ $archiveIds = array();
+ foreach ($reports as $report => $metric) {
+ // prepare archives (this will launch archiving when appropriate)
+ foreach ($this->archives as $archive) {
+ // NOTE: Piwik_Archive_Single expects a metric here, not a report
+ $archive->setRequestedReport($metric);
+ $archive->prepareArchive();
+ }
+
+ // collect archive ids for archives that have visits
+ foreach ($this->archives as $archive) {
+ if (!$archive->isThereSomeVisits) {
+ continue;
+ }
+
+ $archiveIds[] = $archive->getIdArchive();
+
+ if ($this->getNumericTableName() != $archive->archiveProcessing->getTableArchiveNumericName()) {
+ throw new Exception("Piwik_Archive_Array_IndexedBySite::getDataTableFromNumeric() algorithm won't work if data is stored in different tables");
+ }
+ }
+ }
+
+ return $archiveIds;
+ }
+
+ /**
+ * Gets the archive id of every Single archive this archive holds. This method
+ * will not launch the archiving process.
+ *
+ * @param array $metrics The requested archive metrics.
+ * @return array
+ */
+ private function getArchiveIdsWithoutLaunching($metrics)
+ {
+ $firstArchive = $this->getFirstArchive();
+ $segment = $firstArchive->getSegment();
+ $period = $firstArchive->getPeriod();
+
+ // the flags used to tell how the archiving process for a specific archive was completed,
+ // if it was completed
+ $doneFlags = array();
+ foreach ($metrics as $metric) {
+ $done = Piwik_ArchiveProcessing::getDoneStringFlagFor($segment, $period, $metric);
+ $donePlugins = Piwik_ArchiveProcessing::getDoneStringFlagFor($segment, $period, $metric, true);
+
+ $doneFlags[$done] = $done;
+ $doneFlags[$donePlugins] = $donePlugins;
+ }
+
+ $allDoneFlags = "'" . implode("','", $doneFlags) . "'";
+
+ // create the SQL to query every archive ID
+ $nameCondition = "(name IN ($allDoneFlags)) AND
+ (value = '" . Piwik_ArchiveProcessing::DONE_OK . "' OR
+ value = '" . Piwik_ArchiveProcessing::DONE_OK_TEMPORARY . "')";
+
+ $sql = "SELECT idsite,
MAX(idarchive) AS idarchive
- FROM ".$this->getNumericTableName()."
+ FROM " . $this->getNumericTableName() . "
WHERE date1 = ?
AND date2 = ?
AND period = ?
AND $nameCondition
- AND idsite IN (".implode(',', array_keys($this->archives)).")
+ AND idsite IN (" . implode(',', array_keys($this->archives)) . ")
GROUP BY idsite";
-
- $bind = array($period->getDateStart()->toString('Y-m-d'),
- $period->getDateEnd()->toString('Y-m-d'),
- $period->getId());
-
- // execute the query and process the results.
- $archiveIds = array();
- foreach (Piwik_FetchAll($sql, $bind) as $row)
- {
- $archiveIds[] = $row['idarchive'];
- }
-
- return $archiveIds;
- }
-
- /**
- * Gets the name of the database table that holds the numeric archive data for
- * this archive.
- *
- * @return string
- */
- private function getNumericTableName()
- {
- if (is_null($this->tableName))
- {
- $table = Piwik_ArchiveProcessing::makeNumericArchiveTable($this->getFirstArchive()->getPeriod());
- $this->tableName = $table->getTableName();
- }
-
- return $this->tableName;
- }
+
+ $bind = array($period->getDateStart()->toString('Y-m-d'),
+ $period->getDateEnd()->toString('Y-m-d'),
+ $period->getId());
+
+ // execute the query and process the results.
+ $archiveIds = array();
+ foreach (Piwik_FetchAll($sql, $bind) as $row) {
+ $archiveIds[] = $row['idarchive'];
+ }
+
+ return $archiveIds;
+ }
+
+ /**
+ * Gets the name of the database table that holds the numeric archive data for
+ * this archive.
+ *
+ * @return string
+ */
+ private function getNumericTableName()
+ {
+ if (is_null($this->tableName)) {
+ $table = Piwik_ArchiveProcessing::makeNumericArchiveTable($this->getFirstArchive()->getPeriod());
+ $this->tableName = $table->getTableName();
+ }
+
+ return $this->tableName;
+ }
}
diff --git a/core/Archive/Single.php b/core/Archive/Single.php
index adb73095e5..2f4d5e283e 100644
--- a/core/Archive/Single.php
+++ b/core/Archive/Single.php
@@ -1,668 +1,631 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- *
+ *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Piwik_Archive_Single is used to store the data of a single archive,
- * for example the statistics for the 'day' '2008-02-21' for the website idSite '2'
+ * Piwik_Archive_Single is used to store the data of a single archive,
+ * for example the statistics for the 'day' '2008-02-21' for the website idSite '2'
*
* @package Piwik
* @subpackage Piwik_Archive
*/
class Piwik_Archive_Single extends Piwik_Archive
{
- /**
- * The Piwik_ArchiveProcessing object used to check that the archive is available
- * and launch the processing if the archive was not yet processed
- *
- * @var Piwik_ArchiveProcessing
- */
- public $archiveProcessing = null;
-
- /**
- * @var bool Set to true if the archive has at least 1 visit
- */
- public $isThereSomeVisits = null;
-
- /**
- * Period of this Archive
- *
- * @var Piwik_Period
- */
- protected $period = null;
-
- /**
- * Set to true will activate numeric value caching for this archive.
- *
- * @var bool
- */
- protected $cacheEnabledForNumeric = true;
-
- /**
- * Array of cached numeric values, used to make requests faster
- * when requesting the same value again and again
- *
- * @var array of numeric
- */
- protected $numericCached = array();
-
- /**
- * Array of cached blob, used to make requests faster when requesting the same blob again and again
- *
- * @var array of mixed
- */
- protected $blobCached = array();
-
- /**
- * idarchive of this Archive in the database
- *
- * @var int
- */
- protected $idArchive = null;
-
- /**
- * name of requested report
- *
- * @var string
- */
- protected $requestedReport = null;
-
- /**
- * Flag set to true once the archive has been checked (when we make sure it is archived)
- *
- * @var bool
- */
- protected $alreadyChecked = array();
-
- protected function clearCache()
- {
- foreach($this->blobCached as $name => $blob)
- {
- $this->freeBlob($name);
- }
- $this->blobCached = array();
- }
-
- public function __destruct()
- {
- $this->clearCache();
- }
-
- /**
- * Returns the blob cache. For testing.
- *
- * @return array
- */
- public function getBlobCache()
- {
- return $this->blobCached;
- }
-
- /**
- * Returns the pretty date of this Archive, eg. 'Thursday 20th March 2008'
- *
- * @return string
- */
- public function getPrettyDate()
- {
- return $this->period->getPrettyString();
- }
-
- /**
- * Returns the idarchive of this Archive used to index this archive in the DB
- *
- * @throws Exception
- * @return int
- */
- public function getIdArchive()
- {
- if(is_null($this->idArchive))
- {
- throw new Exception("idArchive is null");
- }
- return $this->idArchive;
- }
-
- /**
- * Set the period
- *
- * @param Piwik_Period $period
- */
- public function setPeriod( Piwik_Period $period )
- {
- $this->period = $period;
- }
-
- public function getPeriod()
- {
- return $this->period;
- }
-
- /**
- * Returns the timestamp of the first date in the period for this Archive.
- * This is used to sort archives by date when working on a Archive_Array
- *
- * @return int Unix timestamp
- */
- public function getTimestampStartDate()
- {
- if(!is_null($this->archiveProcessing))
- {
- $timestamp = $this->archiveProcessing->getTimestampStartDate();
- if(!empty($timestamp))
- {
- return $timestamp;
- }
- }
- return $this->period->getDateStart()->getTimestamp();
- }
-
- /**
- * Prepares the archive. Gets the idarchive from the ArchiveProcessing.
- *
- * This will possibly launch the archiving process if the archive was not available.
- * @return bool
- */
- public function prepareArchive()
- {
- $archiveJustProcessed = false;
-
-
- $periodString = $this->period->getLabel();
- $plugin = Piwik_ArchiveProcessing::getPluginBeingProcessed($this->getRequestedReport());
-
- $cacheKey = 'all';
- if($periodString == 'range')
- {
- $cacheKey = $plugin;
- }
- if(!isset($this->alreadyChecked[$cacheKey]))
- {
- $this->isThereSomeVisits = false;
- $this->alreadyChecked[$cacheKey] = true;
- $dayString = $this->period->getPrettyString();
- $logMessage = sprintf("%s (%s), plugin %s", $periodString, $dayString, $plugin);
- // if the END of the period is BEFORE the website creation date
- // we already know there are no stats for this period
- // we add one day to make sure we don't miss the day of the website creation
- if( $this->period->getDateEnd()->addDay(2)->isEarlier( $this->site->getCreationDate() ) )
- {
- Piwik::log(sprintf("Archive %s skipped, archive is before the website was created.", $logMessage));
- return;
- }
-
- // if the starting date is in the future we know there is no visit
- if( $this->period->getDateStart()->subDay(2)->isLater( Piwik_Date::today() ) )
- {
- Piwik::log(sprintf("Archive %s skipped, archive is after today.", $logMessage));
- return;
- }
-
- // we make sure the archive is available for the given date
- $periodLabel = $this->period->getLabel();
- $this->archiveProcessing = Piwik_ArchiveProcessing::factory($periodLabel);
- $this->archiveProcessing->setSite($this->site);
- $this->archiveProcessing->setPeriod($this->period);
- $this->archiveProcessing->setSegment($this->segment);
-
- $this->archiveProcessing->init();
-
- $this->archiveProcessing->setRequestedReport( $this->getRequestedReport() );
-
- $archivingDisabledArchiveNotProcessed = false;
- $idArchive = $this->archiveProcessing->loadArchive();
- if(empty($idArchive))
- {
- if($this->archiveProcessing->isArchivingDisabled())
- {
- $archivingDisabledArchiveNotProcessed = true;
- $logMessage = sprintf("Archiving disabled, for %s", $logMessage);
- }
- else
- {
- Piwik::log(sprintf("Processing %s, not archived yet...", $logMessage));
- $archiveJustProcessed = true;
-
- // Process the reports
- $this->archiveProcessing->launchArchiving();
-
- $idArchive = $this->archiveProcessing->getIdArchive();
- $logMessage = sprintf("Processed %d, for %s", $idArchive, $logMessage);
- }
- }
- else
- {
- $logMessage = sprintf("Already processed, fetching idArchive = %d (idSite=%d), for %s", $idArchive, $this->site->getId(), $logMessage);
- }
- Piwik::log(sprintf("%s, Visits = %d", $logMessage, $this->archiveProcessing->getNumberOfVisits()));
- $this->isThereSomeVisits = !$archivingDisabledArchiveNotProcessed
- && $this->archiveProcessing->isThereSomeVisits();
- $this->idArchive = $idArchive;
- }
- return $archiveJustProcessed;
- }
-
- /**
- * Returns a value from the current archive with the name = $name
- * Method used by getNumeric or getBlob
- *
- * @param string $name
- * @param string $typeValue numeric|blob
- * @param string|bool $archivedDate Value to store date of archive info in. If false, not stored.
- * @return mixed|bool false if no result
- */
- protected function get( $name, $typeValue = 'numeric', &$archivedDate = false )
- {
- $this->setRequestedReport($name);
- $this->prepareArchive();
-
- // values previously "get" and now cached
- if($typeValue == 'numeric'
- && $this->cacheEnabledForNumeric
- && isset($this->numericCached[$name])
- )
- {
- return $this->numericCached[$name];
- }
-
- // During archiving we prefetch the blobs recursively
- // and we get them faster from memory after
- if($typeValue == 'blob'
- && isset($this->blobCached[$name]))
- {
- return $this->blobCached[$name];
- }
-
- if($name == 'idarchive')
- {
- return $this->idArchive;
- }
-
- if(!$this->isThereSomeVisits)
- {
- return false;
- }
-
- // select the table to use depending on the type of the data requested
- switch($typeValue)
- {
- case 'blob':
- $table = $this->archiveProcessing->getTableArchiveBlobName();
- break;
-
- case 'numeric':
- default:
- $table = $this->archiveProcessing->getTableArchiveNumericName();
- break;
- }
-
- $db = Zend_Registry::get('db');
- $row = $db->fetchRow("SELECT value, ts_archived
+ /**
+ * The Piwik_ArchiveProcessing object used to check that the archive is available
+ * and launch the processing if the archive was not yet processed
+ *
+ * @var Piwik_ArchiveProcessing
+ */
+ public $archiveProcessing = null;
+
+ /**
+ * @var bool Set to true if the archive has at least 1 visit
+ */
+ public $isThereSomeVisits = null;
+
+ /**
+ * Period of this Archive
+ *
+ * @var Piwik_Period
+ */
+ protected $period = null;
+
+ /**
+ * Set to true will activate numeric value caching for this archive.
+ *
+ * @var bool
+ */
+ protected $cacheEnabledForNumeric = true;
+
+ /**
+ * Array of cached numeric values, used to make requests faster
+ * when requesting the same value again and again
+ *
+ * @var array of numeric
+ */
+ protected $numericCached = array();
+
+ /**
+ * Array of cached blob, used to make requests faster when requesting the same blob again and again
+ *
+ * @var array of mixed
+ */
+ protected $blobCached = array();
+
+ /**
+ * idarchive of this Archive in the database
+ *
+ * @var int
+ */
+ protected $idArchive = null;
+
+ /**
+ * name of requested report
+ *
+ * @var string
+ */
+ protected $requestedReport = null;
+
+ /**
+ * Flag set to true once the archive has been checked (when we make sure it is archived)
+ *
+ * @var bool
+ */
+ protected $alreadyChecked = array();
+
+ protected function clearCache()
+ {
+ foreach ($this->blobCached as $name => $blob) {
+ $this->freeBlob($name);
+ }
+ $this->blobCached = array();
+ }
+
+ public function __destruct()
+ {
+ $this->clearCache();
+ }
+
+ /**
+ * Returns the blob cache. For testing.
+ *
+ * @return array
+ */
+ public function getBlobCache()
+ {
+ return $this->blobCached;
+ }
+
+ /**
+ * Returns the pretty date of this Archive, eg. 'Thursday 20th March 2008'
+ *
+ * @return string
+ */
+ public function getPrettyDate()
+ {
+ return $this->period->getPrettyString();
+ }
+
+ /**
+ * Returns the idarchive of this Archive used to index this archive in the DB
+ *
+ * @throws Exception
+ * @return int
+ */
+ public function getIdArchive()
+ {
+ if (is_null($this->idArchive)) {
+ throw new Exception("idArchive is null");
+ }
+ return $this->idArchive;
+ }
+
+ /**
+ * Set the period
+ *
+ * @param Piwik_Period $period
+ */
+ public function setPeriod(Piwik_Period $period)
+ {
+ $this->period = $period;
+ }
+
+ public function getPeriod()
+ {
+ return $this->period;
+ }
+
+ /**
+ * Returns the timestamp of the first date in the period for this Archive.
+ * This is used to sort archives by date when working on a Archive_Array
+ *
+ * @return int Unix timestamp
+ */
+ public function getTimestampStartDate()
+ {
+ if (!is_null($this->archiveProcessing)) {
+ $timestamp = $this->archiveProcessing->getTimestampStartDate();
+ if (!empty($timestamp)) {
+ return $timestamp;
+ }
+ }
+ return $this->period->getDateStart()->getTimestamp();
+ }
+
+ /**
+ * Prepares the archive. Gets the idarchive from the ArchiveProcessing.
+ *
+ * This will possibly launch the archiving process if the archive was not available.
+ * @return bool
+ */
+ public function prepareArchive()
+ {
+ $archiveJustProcessed = false;
+
+
+ $periodString = $this->period->getLabel();
+ $plugin = Piwik_ArchiveProcessing::getPluginBeingProcessed($this->getRequestedReport());
+
+ $cacheKey = 'all';
+ if ($periodString == 'range') {
+ $cacheKey = $plugin;
+ }
+ if (!isset($this->alreadyChecked[$cacheKey])) {
+ $this->isThereSomeVisits = false;
+ $this->alreadyChecked[$cacheKey] = true;
+ $dayString = $this->period->getPrettyString();
+ $logMessage = sprintf("%s (%s), plugin %s", $periodString, $dayString, $plugin);
+ // if the END of the period is BEFORE the website creation date
+ // we already know there are no stats for this period
+ // we add one day to make sure we don't miss the day of the website creation
+ if ($this->period->getDateEnd()->addDay(2)->isEarlier($this->site->getCreationDate())) {
+ Piwik::log(sprintf("Archive %s skipped, archive is before the website was created.", $logMessage));
+ return;
+ }
+
+ // if the starting date is in the future we know there is no visit
+ if ($this->period->getDateStart()->subDay(2)->isLater(Piwik_Date::today())) {
+ Piwik::log(sprintf("Archive %s skipped, archive is after today.", $logMessage));
+ return;
+ }
+
+ // we make sure the archive is available for the given date
+ $periodLabel = $this->period->getLabel();
+ $this->archiveProcessing = Piwik_ArchiveProcessing::factory($periodLabel);
+ $this->archiveProcessing->setSite($this->site);
+ $this->archiveProcessing->setPeriod($this->period);
+ $this->archiveProcessing->setSegment($this->segment);
+
+ $this->archiveProcessing->init();
+
+ $this->archiveProcessing->setRequestedReport($this->getRequestedReport());
+
+ $archivingDisabledArchiveNotProcessed = false;
+ $idArchive = $this->archiveProcessing->loadArchive();
+ if (empty($idArchive)) {
+ if ($this->archiveProcessing->isArchivingDisabled()) {
+ $archivingDisabledArchiveNotProcessed = true;
+ $logMessage = sprintf("Archiving disabled, for %s", $logMessage);
+ } else {
+ Piwik::log(sprintf("Processing %s, not archived yet...", $logMessage));
+ $archiveJustProcessed = true;
+
+ // Process the reports
+ $this->archiveProcessing->launchArchiving();
+
+ $idArchive = $this->archiveProcessing->getIdArchive();
+ $logMessage = sprintf("Processed %d, for %s", $idArchive, $logMessage);
+ }
+ } else {
+ $logMessage = sprintf("Already processed, fetching idArchive = %d (idSite=%d), for %s", $idArchive, $this->site->getId(), $logMessage);
+ }
+ Piwik::log(sprintf("%s, Visits = %d", $logMessage, $this->archiveProcessing->getNumberOfVisits()));
+ $this->isThereSomeVisits = !$archivingDisabledArchiveNotProcessed
+ && $this->archiveProcessing->isThereSomeVisits();
+ $this->idArchive = $idArchive;
+ }
+ return $archiveJustProcessed;
+ }
+
+ /**
+ * Returns a value from the current archive with the name = $name
+ * Method used by getNumeric or getBlob
+ *
+ * @param string $name
+ * @param string $typeValue numeric|blob
+ * @param string|bool $archivedDate Value to store date of archive info in. If false, not stored.
+ * @return mixed|bool false if no result
+ */
+ protected function get($name, $typeValue = 'numeric', &$archivedDate = false)
+ {
+ $this->setRequestedReport($name);
+ $this->prepareArchive();
+
+ // values previously "get" and now cached
+ if ($typeValue == 'numeric'
+ && $this->cacheEnabledForNumeric
+ && isset($this->numericCached[$name])
+ ) {
+ return $this->numericCached[$name];
+ }
+
+ // During archiving we prefetch the blobs recursively
+ // and we get them faster from memory after
+ if ($typeValue == 'blob'
+ && isset($this->blobCached[$name])
+ ) {
+ return $this->blobCached[$name];
+ }
+
+ if ($name == 'idarchive') {
+ return $this->idArchive;
+ }
+
+ if (!$this->isThereSomeVisits) {
+ return false;
+ }
+
+ // select the table to use depending on the type of the data requested
+ switch ($typeValue) {
+ case 'blob':
+ $table = $this->archiveProcessing->getTableArchiveBlobName();
+ break;
+
+ case 'numeric':
+ default:
+ $table = $this->archiveProcessing->getTableArchiveNumericName();
+ break;
+ }
+
+ $db = Zend_Registry::get('db');
+ $row = $db->fetchRow("SELECT value, ts_archived
FROM $table
WHERE idarchive = ? AND name = ?",
- array( $this->idArchive , $name)
- );
-
- $value = $tsArchived = false;
- if (is_array($row))
- {
- $value = $row['value'];
- $tsArchived = $row['ts_archived'];
- }
-
- if ($archivedDate !== false)
- {
- $archivedDate = $tsArchived;
- }
-
- if($value === false)
- {
- if($typeValue == 'numeric'
- && $this->cacheEnabledForNumeric)
- {
- $this->numericCached[$name] = false;
- }
- return $value;
- }
-
- // uncompress when selecting from the BLOB table
- if($typeValue == 'blob' && $db->hasBlobDataType())
- {
- $value = $this->uncompress($value);
- }
-
- if($typeValue == 'numeric'
- && $this->cacheEnabledForNumeric)
- {
- $this->numericCached[$name] = $value;
- }
- return $value;
- }
-
-
- /**
- * This method loads in memory all the subtables for the main table called $name.
- * You have to give it the parent table $dataTableToLoad so we can lookup the sub tables ids to load.
- *
- * If $addMetadataSubtableId set to true, it will add for each row a 'metadata' called 'databaseSubtableId'
- * containing the child ID of the subtable associated to this row.
- *
- * @param string $name
- * @param Piwik_DataTable $dataTableToLoad
- * @param bool $addMetadataSubtableId
- */
- public function loadSubDataTables($name, Piwik_DataTable $dataTableToLoad, $addMetadataSubtableId = false)
- {
- // we have to recursively load all the subtables associated to this table's rows
- // and update the subtableID so that it matches the newly instanciated table
- foreach($dataTableToLoad->getRows() as $row)
- {
- $subTableID = $row->getIdSubDataTable();
-
- if($subTableID !== null)
- {
- $subDataTableLoaded = $this->getDataTable($name, $subTableID);
-
- $row->setSubtable( $subDataTableLoaded );
-
- $this->loadSubDataTables($name, $subDataTableLoaded, $addMetadataSubtableId);
-
- // we edit the subtable ID so that it matches the newly table created in memory
- // NB: we dont overwrite the datatableid in the case we are displaying the table expanded.
- if($addMetadataSubtableId)
- {
- // this will be written back to the column 'idsubdatatable' just before rendering, see Renderer/Php.php
- $row->addMetadata('idsubdatatable_in_db', $row->getIdSubDataTable());
- }
- }
- }
- }
-
-
- /**
- * Free the blob cache memory array
- * @param $name
- */
- public function freeBlob( $name )
- {
- unset($this->blobCached[$name]);
- $this->blobCached[$name] = null;
- }
-
- protected function uncompress($data)
- {
- return @gzuncompress($data);
- }
-
- /**
- * Fetches all blob fields name_* at once for the current archive for performance reasons.
- *
- * @param $name
- * @return
- */
- public function preFetchBlob( $name )
- {
- $this->setRequestedReport($name);
- $this->prepareArchive();
- if(!$this->isThereSomeVisits) { return; }
-
- $tableBlob = $this->archiveProcessing->getTableArchiveBlobName();
-
- $db = Zend_Registry::get('db');
- $hasBlobs = $db->hasBlobDataType();
-
- // select blobs w/ name like "$name_[0-9]+" w/o using RLIKE
- $nameEnd = strlen($name) + 2;
- $query = $db->query("SELECT value, name
+ array($this->idArchive, $name)
+ );
+
+ $value = $tsArchived = false;
+ if (is_array($row)) {
+ $value = $row['value'];
+ $tsArchived = $row['ts_archived'];
+ }
+
+ if ($archivedDate !== false) {
+ $archivedDate = $tsArchived;
+ }
+
+ if ($value === false) {
+ if ($typeValue == 'numeric'
+ && $this->cacheEnabledForNumeric
+ ) {
+ $this->numericCached[$name] = false;
+ }
+ return $value;
+ }
+
+ // uncompress when selecting from the BLOB table
+ if ($typeValue == 'blob' && $db->hasBlobDataType()) {
+ $value = $this->uncompress($value);
+ }
+
+ if ($typeValue == 'numeric'
+ && $this->cacheEnabledForNumeric
+ ) {
+ $this->numericCached[$name] = $value;
+ }
+ return $value;
+ }
+
+
+ /**
+ * This method loads in memory all the subtables for the main table called $name.
+ * You have to give it the parent table $dataTableToLoad so we can lookup the sub tables ids to load.
+ *
+ * If $addMetadataSubtableId set to true, it will add for each row a 'metadata' called 'databaseSubtableId'
+ * containing the child ID of the subtable associated to this row.
+ *
+ * @param string $name
+ * @param Piwik_DataTable $dataTableToLoad
+ * @param bool $addMetadataSubtableId
+ */
+ public function loadSubDataTables($name, Piwik_DataTable $dataTableToLoad, $addMetadataSubtableId = false)
+ {
+ // we have to recursively load all the subtables associated to this table's rows
+ // and update the subtableID so that it matches the newly instanciated table
+ foreach ($dataTableToLoad->getRows() as $row) {
+ $subTableID = $row->getIdSubDataTable();
+
+ if ($subTableID !== null) {
+ $subDataTableLoaded = $this->getDataTable($name, $subTableID);
+
+ $row->setSubtable($subDataTableLoaded);
+
+ $this->loadSubDataTables($name, $subDataTableLoaded, $addMetadataSubtableId);
+
+ // we edit the subtable ID so that it matches the newly table created in memory
+ // NB: we dont overwrite the datatableid in the case we are displaying the table expanded.
+ if ($addMetadataSubtableId) {
+ // this will be written back to the column 'idsubdatatable' just before rendering, see Renderer/Php.php
+ $row->addMetadata('idsubdatatable_in_db', $row->getIdSubDataTable());
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Free the blob cache memory array
+ * @param $name
+ */
+ public function freeBlob($name)
+ {
+ unset($this->blobCached[$name]);
+ $this->blobCached[$name] = null;
+ }
+
+ protected function uncompress($data)
+ {
+ return @gzuncompress($data);
+ }
+
+ /**
+ * Fetches all blob fields name_* at once for the current archive for performance reasons.
+ *
+ * @param $name
+ * @return
+ */
+ public function preFetchBlob($name)
+ {
+ $this->setRequestedReport($name);
+ $this->prepareArchive();
+ if (!$this->isThereSomeVisits) {
+ return;
+ }
+
+ $tableBlob = $this->archiveProcessing->getTableArchiveBlobName();
+
+ $db = Zend_Registry::get('db');
+ $hasBlobs = $db->hasBlobDataType();
+
+ // select blobs w/ name like "$name_[0-9]+" w/o using RLIKE
+ $nameEnd = strlen($name) + 2;
+ $query = $db->query("SELECT value, name
FROM $tableBlob
WHERE idarchive = ?
AND (name = ? OR
(name LIKE ? AND SUBSTRING(name, $nameEnd, 1) >= '0'
AND SUBSTRING(name, $nameEnd, 1) <= '9') )",
- array( $this->idArchive, $name, $name.'%' )
- );
-
- while($row = $query->fetch())
- {
- $value = $row['value'];
- $name = $row['name'];
-
- if($hasBlobs)
- {
- $this->blobCached[$name] = $this->uncompress($value);
- if($this->blobCached[$name] === false)
- {
- //throw new Exception("Error gzuncompress $name ");
- }
- }
- else
- {
- $this->blobCached[$name] = $value;
- }
- }
- }
-
- /**
- * Returns a numeric value from this Archive, with the name '$name'
- *
- * @param string $name
- * @return int|float
- */
- public function getNumeric( $name )
- {
- return $this->formatNumericValue( $this->get($name, 'numeric') );
- }
-
-
- /**
- * Returns a blob value from this Archive, with the name '$name'
- * Blob values are all values except int and float.
- *
- * @param string $name
- * @return mixed
- */
- public function getBlob( $name )
- {
- return $this->get($name, 'blob');
- }
-
- /**
- * Given a list of fields defining numeric values, it will return a Piwik_DataTable_Simple
- * containing one row per field name.
- *
- * For example $fields = array( 'max_actions',
- * 'nb_uniq_visitors',
- * 'nb_visits',
- * 'nb_actions',
- * 'sum_visit_length',
- * 'bounce_count',
- * 'nb_visits_converted'
- * );
- *
- * @param string|array $fields Name or array of names of Archive fields
- *
- * @return Piwik_DataTable_Simple
- */
- public function getDataTableFromNumeric( $fields )
- {
- if(!is_array($fields))
- {
- $fields = array($fields);
- }
-
- $values = array();
- foreach($fields as $field)
- {
- $values[$field] = $this->getNumeric($field);
- }
-
- $table = new Piwik_DataTable_Simple();
- $table->addRowsFromArray($values);
- return $table;
- }
-
- /**
- * Returns a DataTable that has the name '$name' from the current Archive.
- * If $idSubTable is specified, returns the subDataTable called '$name_$idSubTable'
- *
- * @param string $name
- * @param int $idSubTable optional id SubDataTable
- * @return Piwik_DataTable
- */
- public function getDataTable( $name, $idSubTable = null )
- {
- if(!is_null($idSubTable))
- {
- $name .= sprintf("_%s", $idSubTable);
- }
-
- $this->setRequestedReport($name);
-
- $data = $this->get($name, 'blob', $tsArchived);
-
- $table = $this->makeDataTable();
-
- if($data !== false)
- {
- $table->addRowsFromSerializedArray($data);
- $table->setMetadata(Piwik_DataTable::ARCHIVED_DATE_METADATA_NAME, $tsArchived);
- }
- if($data === false
- && $idSubTable !== null)
- {
- // This is not expected, but somehow happens in some unknown cases and very rarely.
- // Do not throw error in this case
- //throw new Exception("not expected");
- return new Piwik_DataTable();
- }
-
- return $table;
- }
-
- /**
- * Creates a new DataTable with some metadata set. Sets the following metadata:
- * - 'site' => Piwik_Site instance for this archive
- * - 'period' => Piwik_Period instance for this archive
- * - 'timestamp' => the timestamp of the first date in this archive
- *
- * @param bool $isSimple Whether the DataTable should be a DataTable_Simple
- * instance or not.
- * @return Piwik_DataTable
- */
- public function makeDataTable( $isSimple = false )
- {
- if ($isSimple)
- {
- $result = new Piwik_DataTable_Simple();
- }
- else
- {
- $result = new Piwik_DataTable();
- }
-
- $result->setMetadata('site', $this->getSite());
- $result->setMetadata('period', $this->getPeriod());
- $result->setMetadata('timestamp', $this->getTimestampStartDate());
-
- return $result;
- }
-
- public function setRequestedReport($requestedReport )
- {
- $this->requestedReport = $requestedReport;
- }
-
- /**
- * Returns the report (the named collection of metrics) this Archive instance is
- * currently going to query/process.
- *
- * @return string
- */
- protected function getRequestedReport()
- {
- return self::getRequestedReportFor($this->requestedReport);
- }
-
- /**
- * Returns the name of the report (the named collection of metrics) that contains the
- * specified metric.
- *
- * @param string $metric The metric whose report is being requested. If this does
- * not belong to a known report, its assumed to be the report
- * itself.
- * @return string
- */
- public static function getRequestedReportFor($metric)
- {
- // Core metrics are always processed in Core, for the requested date/period/segment
- if(in_array($metric, Piwik_ArchiveProcessing::getCoreMetrics())
- || $metric == 'max_actions')
- {
- return 'VisitsSummary_CoreMetrics';
- }
- // VisitFrequency metrics don't follow the same naming convention (HACK)
- if(strpos($metric, '_returning') > 0
- // ignore Goal_visitor_returning_1_1_nb_conversions
- && strpos($metric, 'Goal_') === false)
- {
- return 'VisitFrequency_Metrics';
- }
- // Goal_* metrics are processed by the Goals plugin (HACK)
- if(strpos($metric, 'Goal_') === 0)
- {
- return 'Goals_Metrics';
- }
- // Actions metrics are processed by the Actions plugin (HACK) (3RD HACK IN FACT) (YES, THIS IS TOO MUCH HACKING)
- // (FIXME PLEASE).
- if (in_array($metric, Piwik_Archive::$actionsMetrics))
- {
- return 'Actions_Metrics';
- }
- return $metric;
- }
-
- /**
- * Returns a DataTable that has the name '$name' from the current Archive.
- * Also loads in memory all subDataTable for this DataTable.
- *
- * For example, if $name = 'Referers_keywordBySearchEngine' it will load all DataTable
- * named 'Referers_keywordBySearchEngine_*' and they will be set as subDataTable to the
- * rows. You can then go through the rows
- * $rows = DataTable->getRows();
- * and for each row request the subDataTable (in this case the DataTable of the keywords for each search engines)
- * $idSubTable = $row->getIdSubDataTable();
- * $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
- *
- * @param string $name
- * @param int $idSubTable Optional subDataTable to load instead of loading the parent DataTable
- * @return Piwik_DataTable
- */
- public function getDataTableExpanded($name, $idSubTable = null)
- {
- $this->preFetchBlob($name);
- $dataTableToLoad = $this->getDataTable($name, $idSubTable);
- $this->loadSubDataTables($name, $dataTableToLoad, $addMetadataSubtableId = true);
- $dataTableToLoad->enableRecursiveFilters();
- $this->freeBlob($name);
- return $dataTableToLoad;
- }
-
- /**
- * Returns true if Piwik can launch the archiving process for this archive,
- * false if otherwise.
- *
- * @return bool
- */
- public function isArchivingDisabled()
- {
- return Piwik_ArchiveProcessing::isArchivingDisabledFor($this->segment, $this->period);
- }
+ array($this->idArchive, $name, $name . '%')
+ );
+
+ while ($row = $query->fetch()) {
+ $value = $row['value'];
+ $name = $row['name'];
+
+ if ($hasBlobs) {
+ $this->blobCached[$name] = $this->uncompress($value);
+ if ($this->blobCached[$name] === false) {
+ //throw new Exception("Error gzuncompress $name ");
+ }
+ } else {
+ $this->blobCached[$name] = $value;
+ }
+ }
+ }
+
+ /**
+ * Returns a numeric value from this Archive, with the name '$name'
+ *
+ * @param string $name
+ * @return int|float
+ */
+ public function getNumeric($name)
+ {
+ return $this->formatNumericValue($this->get($name, 'numeric'));
+ }
+
+
+ /**
+ * Returns a blob value from this Archive, with the name '$name'
+ * Blob values are all values except int and float.
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function getBlob($name)
+ {
+ return $this->get($name, 'blob');
+ }
+
+ /**
+ * Given a list of fields defining numeric values, it will return a Piwik_DataTable_Simple
+ * containing one row per field name.
+ *
+ * For example $fields = array( 'max_actions',
+ * 'nb_uniq_visitors',
+ * 'nb_visits',
+ * 'nb_actions',
+ * 'sum_visit_length',
+ * 'bounce_count',
+ * 'nb_visits_converted'
+ * );
+ *
+ * @param string|array $fields Name or array of names of Archive fields
+ *
+ * @return Piwik_DataTable_Simple
+ */
+ public function getDataTableFromNumeric($fields)
+ {
+ if (!is_array($fields)) {
+ $fields = array($fields);
+ }
+
+ $values = array();
+ foreach ($fields as $field) {
+ $values[$field] = $this->getNumeric($field);
+ }
+
+ $table = new Piwik_DataTable_Simple();
+ $table->addRowsFromArray($values);
+ return $table;
+ }
+
+ /**
+ * Returns a DataTable that has the name '$name' from the current Archive.
+ * If $idSubTable is specified, returns the subDataTable called '$name_$idSubTable'
+ *
+ * @param string $name
+ * @param int $idSubTable optional id SubDataTable
+ * @return Piwik_DataTable
+ */
+ public function getDataTable($name, $idSubTable = null)
+ {
+ if (!is_null($idSubTable)) {
+ $name .= sprintf("_%s", $idSubTable);
+ }
+
+ $this->setRequestedReport($name);
+
+ $data = $this->get($name, 'blob', $tsArchived);
+
+ $table = $this->makeDataTable();
+
+ if ($data !== false) {
+ $table->addRowsFromSerializedArray($data);
+ $table->setMetadata(Piwik_DataTable::ARCHIVED_DATE_METADATA_NAME, $tsArchived);
+ }
+ if ($data === false
+ && $idSubTable !== null
+ ) {
+ // This is not expected, but somehow happens in some unknown cases and very rarely.
+ // Do not throw error in this case
+ //throw new Exception("not expected");
+ return new Piwik_DataTable();
+ }
+
+ return $table;
+ }
+
+ /**
+ * Creates a new DataTable with some metadata set. Sets the following metadata:
+ * - 'site' => Piwik_Site instance for this archive
+ * - 'period' => Piwik_Period instance for this archive
+ * - 'timestamp' => the timestamp of the first date in this archive
+ *
+ * @param bool $isSimple Whether the DataTable should be a DataTable_Simple
+ * instance or not.
+ * @return Piwik_DataTable
+ */
+ public function makeDataTable($isSimple = false)
+ {
+ if ($isSimple) {
+ $result = new Piwik_DataTable_Simple();
+ } else {
+ $result = new Piwik_DataTable();
+ }
+
+ $result->setMetadata('site', $this->getSite());
+ $result->setMetadata('period', $this->getPeriod());
+ $result->setMetadata('timestamp', $this->getTimestampStartDate());
+
+ return $result;
+ }
+
+ public function setRequestedReport($requestedReport)
+ {
+ $this->requestedReport = $requestedReport;
+ }
+
+ /**
+ * Returns the report (the named collection of metrics) this Archive instance is
+ * currently going to query/process.
+ *
+ * @return string
+ */
+ protected function getRequestedReport()
+ {
+ return self::getRequestedReportFor($this->requestedReport);
+ }
+
+ /**
+ * Returns the name of the report (the named collection of metrics) that contains the
+ * specified metric.
+ *
+ * @param string $metric The metric whose report is being requested. If this does
+ * not belong to a known report, its assumed to be the report
+ * itself.
+ * @return string
+ */
+ public static function getRequestedReportFor($metric)
+ {
+ // Core metrics are always processed in Core, for the requested date/period/segment
+ if (in_array($metric, Piwik_ArchiveProcessing::getCoreMetrics())
+ || $metric == 'max_actions'
+ ) {
+ return 'VisitsSummary_CoreMetrics';
+ }
+ // VisitFrequency metrics don't follow the same naming convention (HACK)
+ if (strpos($metric, '_returning') > 0
+ // ignore Goal_visitor_returning_1_1_nb_conversions
+ && strpos($metric, 'Goal_') === false
+ ) {
+ return 'VisitFrequency_Metrics';
+ }
+ // Goal_* metrics are processed by the Goals plugin (HACK)
+ if (strpos($metric, 'Goal_') === 0) {
+ return 'Goals_Metrics';
+ }
+ // Actions metrics are processed by the Actions plugin (HACK) (3RD HACK IN FACT) (YES, THIS IS TOO MUCH HACKING)
+ // (FIXME PLEASE).
+ if (in_array($metric, Piwik_Archive::$actionsMetrics)) {
+ return 'Actions_Metrics';
+ }
+ return $metric;
+ }
+
+ /**
+ * Returns a DataTable that has the name '$name' from the current Archive.
+ * Also loads in memory all subDataTable for this DataTable.
+ *
+ * For example, if $name = 'Referers_keywordBySearchEngine' it will load all DataTable
+ * named 'Referers_keywordBySearchEngine_*' and they will be set as subDataTable to the
+ * rows. You can then go through the rows
+ * $rows = DataTable->getRows();
+ * and for each row request the subDataTable (in this case the DataTable of the keywords for each search engines)
+ * $idSubTable = $row->getIdSubDataTable();
+ * $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
+ *
+ * @param string $name
+ * @param int $idSubTable Optional subDataTable to load instead of loading the parent DataTable
+ * @return Piwik_DataTable
+ */
+ public function getDataTableExpanded($name, $idSubTable = null)
+ {
+ $this->preFetchBlob($name);
+ $dataTableToLoad = $this->getDataTable($name, $idSubTable);
+ $this->loadSubDataTables($name, $dataTableToLoad, $addMetadataSubtableId = true);
+ $dataTableToLoad->enableRecursiveFilters();
+ $this->freeBlob($name);
+ return $dataTableToLoad;
+ }
+
+ /**
+ * Returns true if Piwik can launch the archiving process for this archive,
+ * false if otherwise.
+ *
+ * @return bool
+ */
+ public function isArchivingDisabled()
+ {
+ return Piwik_ArchiveProcessing::isArchivingDisabledFor($this->segment, $this->period);
+ }
}
diff --git a/core/ArchiveProcessing.php b/core/ArchiveProcessing.php
index 17d2a32246..7ffa394ce9 100644
--- a/core/ArchiveProcessing.php
+++ b/core/ArchiveProcessing.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,561 +12,545 @@
/**
* The ArchiveProcessing module is a module that reads the Piwik logs from the DB and
* compute all the reports, which are then stored in the database.
- *
+ *
* The ArchiveProcessing class is used by the Archive object to make sure the given Archive is processed and available in the DB.
- *
+ *
* A record in the Database for a given report is defined by
- * - idarchive = unique ID that is associated to all the data of this archive (idsite+period+date)
- * - idsite = the ID of the website
- * - date1 = starting day of the period
- * - date2 = ending day of the period
- * - period = integer that defines the period (day/week/etc.). @see period::getId()
+ * - idarchive = unique ID that is associated to all the data of this archive (idsite+period+date)
+ * - idsite = the ID of the website
+ * - date1 = starting day of the period
+ * - date2 = ending day of the period
+ * - period = integer that defines the period (day/week/etc.). @see period::getId()
* - ts_archived = timestamp when the archive was processed (UTC)
- * - name = the name of the report (ex: uniq_visitors or search_keywords_by_search_engines)
- * - value = the actual data
- *
+ * - name = the name of the report (ex: uniq_visitors or search_keywords_by_search_engines)
+ * - value = the actual data
+ *
* @package Piwik
* @subpackage Piwik_ArchiveProcessing
*/
abstract class Piwik_ArchiveProcessing
{
- /**
- * Flag stored at the end of the archiving
- *
- * @var int
- */
- const DONE_OK = 1;
-
- /**
- * Flag stored at the start of the archiving
- * When requesting an Archive, we make sure that non-finished archive are not considered valid
- *
- * @var int
- */
- const DONE_ERROR = 2;
-
- /**
- * Flag indicates the archive is over a period that is not finished, eg. the current day, current week, etc.
- * Archives flagged will be regularly purged from the DB.
- *
- * @var int
- */
- const DONE_OK_TEMPORARY = 3;
-
- /**
- * A row is created to lock an idarchive for the current archive being processed
- * @var string
- */
- const PREFIX_SQL_LOCK = "locked_";
-
- /**
- * Idarchive in the DB for the requested archive
- *
- * @var int
- */
- protected $idArchive;
-
- /**
- * Period id @see Piwik_Period::getId()
- *
- * @var int
- */
- protected $periodId;
-
- /**
- * Timestamp for the first date of the period
- *
- * @var int unix timestamp
- */
- protected $timestampDateStart = null;
-
- /**
- * Starting date of the archive
- *
- * @var Piwik_Date
- */
- protected $dateStart;
-
- /**
- * Ending date of the archive
- *
- * @var Piwik_Date
- */
- protected $dateEnd;
-
- /**
- * Object used to generate (depending on the $dateStart) the name of the DB table to use to store numeric values
- *
- * @var Piwik_TablePartitioning
- */
- protected $tableArchiveNumeric;
-
- /**
- * Object used to generate (depending on the $dateStart) the name of the DB table to use to store numeric values
- *
- * @var Piwik_TablePartitioning
- */
- protected $tableArchiveBlob;
-
- /**
- * Minimum timestamp looked at for processed archives
- *
- * @var int
- */
- protected $minDatetimeArchiveProcessedUTC = false;
-
- /**
- * Compress blobs
- *
- * @var bool
- */
- protected $compressBlob;
-
- /**
- * Is the current archive temporary. ie.
- * - today
- * - current week / month / year
- */
- protected $temporaryArchive;
-
- /**
- * Id of the current site
- * Can be accessed by plugins (that is why it's public)
- *
- * @var int
- */
- public $idsite = null;
-
- /**
- * Period of the current archive
- * Can be accessed by plugins (that is why it's public)
- *
- * @var Piwik_Period
- */
- public $period = null;
-
- /**
- * Site of the current archive
- * Can be accessed by plugins (that is why it's public)
- *
- * @var Piwik_Site
- */
- public $site = null;
-
- /**
- * @var Piwik_Segment
- */
- protected $segment = null;
-
- /**
- * Current time.
- * This value is cached.
- *
- * @var int
- */
- public $time = null;
-
- /**
- * Starting datetime in UTC
- *
- * @var string
- */
- public $startDatetimeUTC;
-
- /**
- * Ending date in UTC
- *
- * @var string
- */
- public $strDateEnd;
-
- /**
- * Name of the DB table _log_visit
- *
- * @var string
- */
- public $logTable;
-
- /**
- * When set to true, we always archive, even if the archive is already available.
- * You can change this settings automatically in the config/global.ini.php always_archive_data under the [Debug] section
- *
- * @var bool
- */
- public $debugAlwaysArchive = false;
-
- /**
- * If the archive has at least 1 visit, this is set to true.
- *
- * @var bool
- */
- public $isThereSomeVisits = null;
-
- protected $startTimestampUTC;
- protected $endTimestampUTC;
-
- /**
- * Flag that will forcefully disable the archiving process. Only set by the tests.
- */
- public static $forceDisableArchiving = false;
-
- /**
- * Constructor
- */
- public function __construct()
- {
- $this->time = time();
- }
-
- /**
- * Returns the Piwik_ArchiveProcessing_Day or Piwik_ArchiveProcessing_Period object
- * depending on $name period string
- *
- * @param string $name day|week|month|year
- * @throws Exception
- * @return Piwik_ArchiveProcessing Piwik_ArchiveProcessing_Day|Piwik_ArchiveProcessing_Period
- */
- static function factory($name)
- {
- switch($name)
- {
- case 'day':
- $process = new Piwik_ArchiveProcessing_Day();
- $process->debugAlwaysArchive = Piwik_Config::getInstance()->Debug['always_archive_data_day'];
- break;
-
- case 'week':
- case 'month':
- case 'year':
- $process = new Piwik_ArchiveProcessing_Period();
- $process->debugAlwaysArchive = Piwik_Config::getInstance()->Debug['always_archive_data_period'];
- break;
-
- case 'range':
- $process = new Piwik_ArchiveProcessing_Period();
- $process->debugAlwaysArchive = Piwik_Config::getInstance()->Debug['always_archive_data_range'];
- break;
-
- default:
- throw new Exception("Unknown Archiving period specified '$name'");
- break;
- }
- return $process;
- }
-
- const OPTION_TODAY_ARCHIVE_TTL = 'todayArchiveTimeToLive';
- const OPTION_BROWSER_TRIGGER_ARCHIVING = 'enableBrowserTriggerArchiving';
-
- static public function getCoreMetrics()
- {
- return array(
- 'nb_uniq_visitors',
- 'nb_visits',
- 'nb_actions',
- 'sum_visit_length',
- 'bounce_count',
- 'nb_visits_converted',
- );
- }
-
- static public function setTodayArchiveTimeToLive($timeToLiveSeconds)
- {
- $timeToLiveSeconds = (int)$timeToLiveSeconds;
- if($timeToLiveSeconds <= 0)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionInvalidArchiveTimeToLive'));
- }
- Piwik_SetOption(self::OPTION_TODAY_ARCHIVE_TTL, $timeToLiveSeconds, $autoload = true);
- }
-
- static public function getTodayArchiveTimeToLive()
- {
- $timeToLive = Piwik_GetOption(self::OPTION_TODAY_ARCHIVE_TTL);
- if($timeToLive !== false)
- {
- return $timeToLive;
- }
- return Piwik_Config::getInstance()->General['time_before_today_archive_considered_outdated'];
- }
-
- static public function setBrowserTriggerArchiving($enabled)
- {
- if(!is_bool($enabled))
- {
- throw new Exception('Browser trigger archiving must be set to true or false.');
- }
- Piwik_SetOption(self::OPTION_BROWSER_TRIGGER_ARCHIVING, (int)$enabled, $autoload = true);
- Piwik_Tracker_Cache::clearCacheGeneral();
- }
-
- static public function isBrowserTriggerArchivingEnabled()
- {
- $browserArchivingEnabled = Piwik_GetOption(self::OPTION_BROWSER_TRIGGER_ARCHIVING);
- if($browserArchivingEnabled !== false)
- {
- return (bool)$browserArchivingEnabled;
- }
- return (bool)Piwik_Config::getInstance()->General['enable_browser_archiving_triggering'];
- }
-
- public function getIdArchive()
- {
- return $this->idArchive;
- }
-
- /**
- * Sets object attributes that will be used throughout the process
- */
- public function init()
- {
- $this->idsite = $this->site->getId();
- $this->periodId = $this->period->getId();
-
- $this->initDates();
-
- $this->tableArchiveNumeric = self::makeNumericArchiveTable($this->period);
- $this->tableArchiveBlob = self::makeBlobArchiveTable($this->period);
-
- $this->minDatetimeArchiveProcessedUTC = $this->getMinTimeArchivedProcessed();
- $db = Zend_Registry::get('db');
- $this->compressBlob = $db->hasBlobDataType();
- }
-
- /**
- * The archive processing classes have features that might be useful for live querying;
- * In particular, Piwik_ArchiveProcessing_Day::query*. In order to reuse those methods
- * outside the actual archiving or to reuse archiving code for live querying, an instance
- * of archive processing has to be faked.
- *
- * For example, this code can be used in an API method:
- * $archiveProcessing = new Piwik_ArchiveProcessing_Day();
- * $archiveProcessing->setSite(new Piwik_Site($idSite));
- * $archiveProcessing->setPeriod(Piwik_Period::advancedFactory($period, $date));
- * $archiveProcessing->setSegment(new Piwik_Segment($segment, $idSite));
- * $archiveProcessing->initForLiveUsage();
- * Then, either use $archiveProcessing->query* or pass the instance to the archiving
- * code of the plugin. Note that even though we use Piwik_ArchiveProcessing_Day, this
- * works for any $period and $date that has been passed to the API.
- */
- public function initForLiveUsage() {
- $this->idsite = $this->site->getId();
- $this->initDates();
- }
-
- private function initDates() {
- $dateStartLocalTimezone = $this->period->getDateStart();
- $dateEndLocalTimezone = $this->period->getDateEnd();
-
- $dateStartUTC = $dateStartLocalTimezone->setTimezone($this->site->getTimezone());
- $dateEndUTC = $dateEndLocalTimezone->setTimezone($this->site->getTimezone());
- $this->startDatetimeUTC = $dateStartUTC->getDateStartUTC();
- $this->endDatetimeUTC = $dateEndUTC->getDateEndUTC();
- $this->startTimestampUTC = $dateStartUTC->getTimestamp();
- $this->endTimestampUTC = strtotime($this->endDatetimeUTC);
- }
-
- /**
- * Utility function which creates a TablePartitioning instance for the numeric
- * archive data of a given period.
- *
- * @param Piwik_Period $period The time period of the archive data.
- * @return Piwik_TablePartitioning_Monthly
- */
- public static function makeNumericArchiveTable($period)
- {
- $result = new Piwik_TablePartitioning_Monthly('archive_numeric');
- $result->setTimestamp($period->getDateStart()->getTimestamp());
- return $result;
- }
-
- /**
- * Utility function which creates a TablePartitioning instance for the blob
- * archive data of a given period.
- *
- * @param Piwik_Period $period The time period of the archive data.
- * @return Piwik_TablePartitioning_Monthly
- */
- public static function makeBlobArchiveTable($period)
- {
- $result = new Piwik_TablePartitioning_Monthly('archive_blob');
- $result->setTimestamp($period->getDateStart()->getTimestamp());
- return $result;
- }
-
- public function getStartDatetimeUTC()
- {
- return $this->startDatetimeUTC;
- }
-
- public function getEndDatetimeUTC()
- {
- return $this->endDatetimeUTC;
- }
-
- public function isArchiveTemporary()
- {
- return $this->temporaryArchive;
- }
-
- /**
- * Returns the minimum archive processed datetime to look at
- *
- * @return string Datetime string, or false if must look at any archive available
- */
- public function getMinTimeArchivedProcessed()
- {
- $this->temporaryArchive = false;
- // if the current archive is a DAY and if it's today,
- // we set this minDatetimeArchiveProcessedUTC that defines the lifetime value of today's archive
- if( $this->period->getNumberOfSubperiods() == 0
- && ($this->startTimestampUTC <= $this->time && $this->endTimestampUTC > $this->time)
- )
- {
- $this->temporaryArchive = true;
- $minDatetimeArchiveProcessedUTC = $this->time - self::getTodayArchiveTimeToLive();
- // see #1150; if new archives are not triggered from the browser,
- // we still want to try and return the latest archive available for today (rather than return nothing)
- if($this->isArchivingDisabled())
- {
- return false;
- }
- }
- // - 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
- elseif($this->endTimestampUTC <= $this->time)
- {
- $minDatetimeArchiveProcessedUTC = $this->endTimestampUTC;
- }
- // - if the period we're looking for is not finished, we look for a recent enough archive
- else
- {
- $this->temporaryArchive = true;
-
- // We choose to only look at archives that are newer than the specified timeout
- $minDatetimeArchiveProcessedUTC = $this->time - self::getTodayArchiveTimeToLive();
-
- // However, if archiving is disabled for this request, we shall
- // accept any archive that was processed today after 00:00:01 this morning
- if($this->isArchivingDisabled())
- {
- $timezone = $this->site->getTimezone();
- $minDatetimeArchiveProcessedUTC = Piwik_Date::factory(Piwik_Date::factory('now', $timezone)->getDateStartUTC())->setTimezone($timezone)->getTimestamp();
- }
- }
- return $minDatetimeArchiveProcessedUTC;
- }
-
- /**
- * This method returns the idArchive ; if necessary, it triggers the archiving process.
- *
- * If the archive was not processed yet, it will launch the archiving process.
- * If the current archive needs sub-archives (eg. a month archive needs all the days archive)
- * it will recursively launch the archiving (using this loadArchive() on the sub-periods)
- *
- * @return int|false The idarchive of the archive, false if the archive is not archived yet
- */
- public function loadArchive()
- {
- $this->init();
- if($this->debugAlwaysArchive)
- {
- return false;
- }
- $this->idArchive = $this->isArchived();
-
- if($this->idArchive === false)
- {
- return false;
- }
- return $this->idArchive;
- }
-
- /**
- * @see loadArchive()
- */
- public function launchArchiving()
- {
- if (!Piwik::getArchiveProcessingLock($this->idsite, $this->period, $this->segment))
- {
- Piwik::log('Unable to get lock for idSite = ' . $this->idsite
- . ', period = ' . $this->period->getLabel()
- . ', UTC datetime [' . $this->startDatetimeUTC . ' -> ' . $this->endDatetimeUTC . ' ]...');
- return;
- }
-
- $this->initCompute();
- $this->compute();
- $this->postCompute();
- // we execute again the isArchived that does some initialization work
- $this->idArchive = $this->isArchived();
- Piwik::releaseArchiveProcessingLock($this->idsite, $this->period, $this->segment);
- }
-
- /**
- * This methods reads the subperiods if necessary,
- * and computes the archive of the current period.
- */
- abstract protected function compute();
-
- abstract public function isThereSomeVisits();
-
- /**
- * Returns the name of the archive field used to tell the status of an archive, (ie,
- * whether the archive was created successfully or not).
- *
- * @param bool $flagArchiveAsAllPlugins
- * @return string
- */
- public function getDoneStringFlag($flagArchiveAsAllPlugins = false)
- {
- return self::getDoneStringFlagFor(
- $this->getSegment(), $this->period, $this->getRequestedReport(), $flagArchiveAsAllPlugins);
- }
-
- /**
- * Returns the name of the archive field used to tell the status of an archive, (ie,
- * whether the archive was created successfully or not).
- *
- * @param Piwik_Segment $segment
- * @param Piwik_Period $period
- * @param string $requestedReport
- * @param bool $flagArchiveAsAllPlugins
- * @return string
- */
- public static function getDoneStringFlagFor($segment, $period, $requestedReport, $flagArchiveAsAllPlugins = false)
- {
- $segmentHash = $segment->getHash();
- if(!self::shouldProcessReportsAllPluginsFor($segment, $period))
- {
- $pluginProcessed = self::getPluginBeingProcessed($requestedReport);
- if(!Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginProcessed)
- || $flagArchiveAsAllPlugins
- )
- {
- $pluginProcessed = 'all';
- }
- $segmentHash .= '.'.$pluginProcessed;
- }
- return 'done' . $segmentHash;
- }
-
- /**
- * Init the object before launching the real archive processing
- */
- protected function initCompute()
- {
- $this->loadNextIdarchive();
- $done = $this->getDoneStringFlag();
- $this->insertNumericRecord($done, Piwik_ArchiveProcessing::DONE_ERROR);
-
- // Can be removed when GeoIp is in core
- $this->logTable = Piwik_Common::prefixTable('log_visit');
-
- $temporary = 'definitive archive';
- if($this->isArchiveTemporary())
- {
- $temporary = 'temporary archive';
- }
+ /**
+ * Flag stored at the end of the archiving
+ *
+ * @var int
+ */
+ const DONE_OK = 1;
+
+ /**
+ * Flag stored at the start of the archiving
+ * When requesting an Archive, we make sure that non-finished archive are not considered valid
+ *
+ * @var int
+ */
+ const DONE_ERROR = 2;
+
+ /**
+ * Flag indicates the archive is over a period that is not finished, eg. the current day, current week, etc.
+ * Archives flagged will be regularly purged from the DB.
+ *
+ * @var int
+ */
+ const DONE_OK_TEMPORARY = 3;
+
+ /**
+ * A row is created to lock an idarchive for the current archive being processed
+ * @var string
+ */
+ const PREFIX_SQL_LOCK = "locked_";
+
+ /**
+ * Idarchive in the DB for the requested archive
+ *
+ * @var int
+ */
+ protected $idArchive;
+
+ /**
+ * Period id @see Piwik_Period::getId()
+ *
+ * @var int
+ */
+ protected $periodId;
+
+ /**
+ * Timestamp for the first date of the period
+ *
+ * @var int unix timestamp
+ */
+ protected $timestampDateStart = null;
+
+ /**
+ * Starting date of the archive
+ *
+ * @var Piwik_Date
+ */
+ protected $dateStart;
+
+ /**
+ * Ending date of the archive
+ *
+ * @var Piwik_Date
+ */
+ protected $dateEnd;
+
+ /**
+ * Object used to generate (depending on the $dateStart) the name of the DB table to use to store numeric values
+ *
+ * @var Piwik_TablePartitioning
+ */
+ protected $tableArchiveNumeric;
+
+ /**
+ * Object used to generate (depending on the $dateStart) the name of the DB table to use to store numeric values
+ *
+ * @var Piwik_TablePartitioning
+ */
+ protected $tableArchiveBlob;
+
+ /**
+ * Minimum timestamp looked at for processed archives
+ *
+ * @var int
+ */
+ protected $minDatetimeArchiveProcessedUTC = false;
+
+ /**
+ * Compress blobs
+ *
+ * @var bool
+ */
+ protected $compressBlob;
+
+ /**
+ * Is the current archive temporary. ie.
+ * - today
+ * - current week / month / year
+ */
+ protected $temporaryArchive;
+
+ /**
+ * Id of the current site
+ * Can be accessed by plugins (that is why it's public)
+ *
+ * @var int
+ */
+ public $idsite = null;
+
+ /**
+ * Period of the current archive
+ * Can be accessed by plugins (that is why it's public)
+ *
+ * @var Piwik_Period
+ */
+ public $period = null;
+
+ /**
+ * Site of the current archive
+ * Can be accessed by plugins (that is why it's public)
+ *
+ * @var Piwik_Site
+ */
+ public $site = null;
+
+ /**
+ * @var Piwik_Segment
+ */
+ protected $segment = null;
+
+ /**
+ * Current time.
+ * This value is cached.
+ *
+ * @var int
+ */
+ public $time = null;
+
+ /**
+ * Starting datetime in UTC
+ *
+ * @var string
+ */
+ public $startDatetimeUTC;
+
+ /**
+ * Ending date in UTC
+ *
+ * @var string
+ */
+ public $strDateEnd;
+
+ /**
+ * Name of the DB table _log_visit
+ *
+ * @var string
+ */
+ public $logTable;
+
+ /**
+ * When set to true, we always archive, even if the archive is already available.
+ * You can change this settings automatically in the config/global.ini.php always_archive_data under the [Debug] section
+ *
+ * @var bool
+ */
+ public $debugAlwaysArchive = false;
+
+ /**
+ * If the archive has at least 1 visit, this is set to true.
+ *
+ * @var bool
+ */
+ public $isThereSomeVisits = null;
+
+ protected $startTimestampUTC;
+ protected $endTimestampUTC;
+
+ /**
+ * Flag that will forcefully disable the archiving process. Only set by the tests.
+ */
+ public static $forceDisableArchiving = false;
+
+ /**
+ * Constructor
+ */
+ public function __construct()
+ {
+ $this->time = time();
+ }
+
+ /**
+ * Returns the Piwik_ArchiveProcessing_Day or Piwik_ArchiveProcessing_Period object
+ * depending on $name period string
+ *
+ * @param string $name day|week|month|year
+ * @throws Exception
+ * @return Piwik_ArchiveProcessing Piwik_ArchiveProcessing_Day|Piwik_ArchiveProcessing_Period
+ */
+ static function factory($name)
+ {
+ switch ($name) {
+ case 'day':
+ $process = new Piwik_ArchiveProcessing_Day();
+ $process->debugAlwaysArchive = Piwik_Config::getInstance()->Debug['always_archive_data_day'];
+ break;
+
+ case 'week':
+ case 'month':
+ case 'year':
+ $process = new Piwik_ArchiveProcessing_Period();
+ $process->debugAlwaysArchive = Piwik_Config::getInstance()->Debug['always_archive_data_period'];
+ break;
+
+ case 'range':
+ $process = new Piwik_ArchiveProcessing_Period();
+ $process->debugAlwaysArchive = Piwik_Config::getInstance()->Debug['always_archive_data_range'];
+ break;
+
+ default:
+ throw new Exception("Unknown Archiving period specified '$name'");
+ break;
+ }
+ return $process;
+ }
+
+ const OPTION_TODAY_ARCHIVE_TTL = 'todayArchiveTimeToLive';
+ const OPTION_BROWSER_TRIGGER_ARCHIVING = 'enableBrowserTriggerArchiving';
+
+ static public function getCoreMetrics()
+ {
+ return array(
+ 'nb_uniq_visitors',
+ 'nb_visits',
+ 'nb_actions',
+ 'sum_visit_length',
+ 'bounce_count',
+ 'nb_visits_converted',
+ );
+ }
+
+ static public function setTodayArchiveTimeToLive($timeToLiveSeconds)
+ {
+ $timeToLiveSeconds = (int)$timeToLiveSeconds;
+ if ($timeToLiveSeconds <= 0) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionInvalidArchiveTimeToLive'));
+ }
+ Piwik_SetOption(self::OPTION_TODAY_ARCHIVE_TTL, $timeToLiveSeconds, $autoload = true);
+ }
+
+ static public function getTodayArchiveTimeToLive()
+ {
+ $timeToLive = Piwik_GetOption(self::OPTION_TODAY_ARCHIVE_TTL);
+ if ($timeToLive !== false) {
+ return $timeToLive;
+ }
+ return Piwik_Config::getInstance()->General['time_before_today_archive_considered_outdated'];
+ }
+
+ static public function setBrowserTriggerArchiving($enabled)
+ {
+ if (!is_bool($enabled)) {
+ throw new Exception('Browser trigger archiving must be set to true or false.');
+ }
+ Piwik_SetOption(self::OPTION_BROWSER_TRIGGER_ARCHIVING, (int)$enabled, $autoload = true);
+ Piwik_Tracker_Cache::clearCacheGeneral();
+ }
+
+ static public function isBrowserTriggerArchivingEnabled()
+ {
+ $browserArchivingEnabled = Piwik_GetOption(self::OPTION_BROWSER_TRIGGER_ARCHIVING);
+ if ($browserArchivingEnabled !== false) {
+ return (bool)$browserArchivingEnabled;
+ }
+ return (bool)Piwik_Config::getInstance()->General['enable_browser_archiving_triggering'];
+ }
+
+ public function getIdArchive()
+ {
+ return $this->idArchive;
+ }
+
+ /**
+ * Sets object attributes that will be used throughout the process
+ */
+ public function init()
+ {
+ $this->idsite = $this->site->getId();
+ $this->periodId = $this->period->getId();
+
+ $this->initDates();
+
+ $this->tableArchiveNumeric = self::makeNumericArchiveTable($this->period);
+ $this->tableArchiveBlob = self::makeBlobArchiveTable($this->period);
+
+ $this->minDatetimeArchiveProcessedUTC = $this->getMinTimeArchivedProcessed();
+ $db = Zend_Registry::get('db');
+ $this->compressBlob = $db->hasBlobDataType();
+ }
+
+ /**
+ * The archive processing classes have features that might be useful for live querying;
+ * In particular, Piwik_ArchiveProcessing_Day::query*. In order to reuse those methods
+ * outside the actual archiving or to reuse archiving code for live querying, an instance
+ * of archive processing has to be faked.
+ *
+ * For example, this code can be used in an API method:
+ * $archiveProcessing = new Piwik_ArchiveProcessing_Day();
+ * $archiveProcessing->setSite(new Piwik_Site($idSite));
+ * $archiveProcessing->setPeriod(Piwik_Period::advancedFactory($period, $date));
+ * $archiveProcessing->setSegment(new Piwik_Segment($segment, $idSite));
+ * $archiveProcessing->initForLiveUsage();
+ * Then, either use $archiveProcessing->query* or pass the instance to the archiving
+ * code of the plugin. Note that even though we use Piwik_ArchiveProcessing_Day, this
+ * works for any $period and $date that has been passed to the API.
+ */
+ public function initForLiveUsage()
+ {
+ $this->idsite = $this->site->getId();
+ $this->initDates();
+ }
+
+ private function initDates()
+ {
+ $dateStartLocalTimezone = $this->period->getDateStart();
+ $dateEndLocalTimezone = $this->period->getDateEnd();
+
+ $dateStartUTC = $dateStartLocalTimezone->setTimezone($this->site->getTimezone());
+ $dateEndUTC = $dateEndLocalTimezone->setTimezone($this->site->getTimezone());
+ $this->startDatetimeUTC = $dateStartUTC->getDateStartUTC();
+ $this->endDatetimeUTC = $dateEndUTC->getDateEndUTC();
+ $this->startTimestampUTC = $dateStartUTC->getTimestamp();
+ $this->endTimestampUTC = strtotime($this->endDatetimeUTC);
+ }
+
+ /**
+ * Utility function which creates a TablePartitioning instance for the numeric
+ * archive data of a given period.
+ *
+ * @param Piwik_Period $period The time period of the archive data.
+ * @return Piwik_TablePartitioning_Monthly
+ */
+ public static function makeNumericArchiveTable($period)
+ {
+ $result = new Piwik_TablePartitioning_Monthly('archive_numeric');
+ $result->setTimestamp($period->getDateStart()->getTimestamp());
+ return $result;
+ }
+
+ /**
+ * Utility function which creates a TablePartitioning instance for the blob
+ * archive data of a given period.
+ *
+ * @param Piwik_Period $period The time period of the archive data.
+ * @return Piwik_TablePartitioning_Monthly
+ */
+ public static function makeBlobArchiveTable($period)
+ {
+ $result = new Piwik_TablePartitioning_Monthly('archive_blob');
+ $result->setTimestamp($period->getDateStart()->getTimestamp());
+ return $result;
+ }
+
+ public function getStartDatetimeUTC()
+ {
+ return $this->startDatetimeUTC;
+ }
+
+ public function getEndDatetimeUTC()
+ {
+ return $this->endDatetimeUTC;
+ }
+
+ public function isArchiveTemporary()
+ {
+ return $this->temporaryArchive;
+ }
+
+ /**
+ * Returns the minimum archive processed datetime to look at
+ *
+ * @return string Datetime string, or false if must look at any archive available
+ */
+ public function getMinTimeArchivedProcessed()
+ {
+ $this->temporaryArchive = false;
+ // if the current archive is a DAY and if it's today,
+ // we set this minDatetimeArchiveProcessedUTC that defines the lifetime value of today's archive
+ if ($this->period->getNumberOfSubperiods() == 0
+ && ($this->startTimestampUTC <= $this->time && $this->endTimestampUTC > $this->time)
+ ) {
+ $this->temporaryArchive = true;
+ $minDatetimeArchiveProcessedUTC = $this->time - self::getTodayArchiveTimeToLive();
+ // see #1150; if new archives are not triggered from the browser,
+ // we still want to try and return the latest archive available for today (rather than return nothing)
+ if ($this->isArchivingDisabled()) {
+ return false;
+ }
+ } // - 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
+ elseif ($this->endTimestampUTC <= $this->time) {
+ $minDatetimeArchiveProcessedUTC = $this->endTimestampUTC;
+ } // - if the period we're looking for is not finished, we look for a recent enough archive
+ else {
+ $this->temporaryArchive = true;
+
+ // We choose to only look at archives that are newer than the specified timeout
+ $minDatetimeArchiveProcessedUTC = $this->time - self::getTodayArchiveTimeToLive();
+
+ // However, if archiving is disabled for this request, we shall
+ // accept any archive that was processed today after 00:00:01 this morning
+ if ($this->isArchivingDisabled()) {
+ $timezone = $this->site->getTimezone();
+ $minDatetimeArchiveProcessedUTC = Piwik_Date::factory(Piwik_Date::factory('now', $timezone)->getDateStartUTC())->setTimezone($timezone)->getTimestamp();
+ }
+ }
+ return $minDatetimeArchiveProcessedUTC;
+ }
+
+ /**
+ * This method returns the idArchive ; if necessary, it triggers the archiving process.
+ *
+ * If the archive was not processed yet, it will launch the archiving process.
+ * If the current archive needs sub-archives (eg. a month archive needs all the days archive)
+ * it will recursively launch the archiving (using this loadArchive() on the sub-periods)
+ *
+ * @return int|false The idarchive of the archive, false if the archive is not archived yet
+ */
+ public function loadArchive()
+ {
+ $this->init();
+ if ($this->debugAlwaysArchive) {
+ return false;
+ }
+ $this->idArchive = $this->isArchived();
+
+ if ($this->idArchive === false) {
+ return false;
+ }
+ return $this->idArchive;
+ }
+
+ /**
+ * @see loadArchive()
+ */
+ public function launchArchiving()
+ {
+ if (!Piwik::getArchiveProcessingLock($this->idsite, $this->period, $this->segment)) {
+ Piwik::log('Unable to get lock for idSite = ' . $this->idsite
+ . ', period = ' . $this->period->getLabel()
+ . ', UTC datetime [' . $this->startDatetimeUTC . ' -> ' . $this->endDatetimeUTC . ' ]...');
+ return;
+ }
+
+ $this->initCompute();
+ $this->compute();
+ $this->postCompute();
+ // we execute again the isArchived that does some initialization work
+ $this->idArchive = $this->isArchived();
+ Piwik::releaseArchiveProcessingLock($this->idsite, $this->period, $this->segment);
+ }
+
+ /**
+ * This methods reads the subperiods if necessary,
+ * and computes the archive of the current period.
+ */
+ abstract protected function compute();
+
+ abstract public function isThereSomeVisits();
+
+ /**
+ * Returns the name of the archive field used to tell the status of an archive, (ie,
+ * whether the archive was created successfully or not).
+ *
+ * @param bool $flagArchiveAsAllPlugins
+ * @return string
+ */
+ public function getDoneStringFlag($flagArchiveAsAllPlugins = false)
+ {
+ return self::getDoneStringFlagFor(
+ $this->getSegment(), $this->period, $this->getRequestedReport(), $flagArchiveAsAllPlugins);
+ }
+
+ /**
+ * Returns the name of the archive field used to tell the status of an archive, (ie,
+ * whether the archive was created successfully or not).
+ *
+ * @param Piwik_Segment $segment
+ * @param Piwik_Period $period
+ * @param string $requestedReport
+ * @param bool $flagArchiveAsAllPlugins
+ * @return string
+ */
+ public static function getDoneStringFlagFor($segment, $period, $requestedReport, $flagArchiveAsAllPlugins = false)
+ {
+ $segmentHash = $segment->getHash();
+ if (!self::shouldProcessReportsAllPluginsFor($segment, $period)) {
+ $pluginProcessed = self::getPluginBeingProcessed($requestedReport);
+ if (!Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginProcessed)
+ || $flagArchiveAsAllPlugins
+ ) {
+ $pluginProcessed = 'all';
+ }
+ $segmentHash .= '.' . $pluginProcessed;
+ }
+ return 'done' . $segmentHash;
+ }
+
+ /**
+ * Init the object before launching the real archive processing
+ */
+ protected function initCompute()
+ {
+ $this->loadNextIdarchive();
+ $done = $this->getDoneStringFlag();
+ $this->insertNumericRecord($done, Piwik_ArchiveProcessing::DONE_ERROR);
+
+ // Can be removed when GeoIp is in core
+ $this->logTable = Piwik_Common::prefixTable('log_visit');
+
+ $temporary = 'definitive archive';
+ if ($this->isArchiveTemporary()) {
+ $temporary = 'temporary archive';
+ }
Piwik::log(sprintf("'%s, idSite = %d (%s), segment '%s', report = '%s', UTC datetime [%s -> %s]",
$this->period->getLabel(),
$this->idsite,
@@ -576,518 +560,491 @@ abstract class Piwik_ArchiveProcessing
$this->startDatetimeUTC,
$this->endTimestampUTC
));
- }
-
- /**
- * Post processing called at the end of the main archive processing.
- * Makes sure the new archive is marked as "successful" in the DB
- *
- * We also try to delete some stuff from memory but really there is still a lot...
- */
- protected function postCompute()
- {
- // delete the first done = ERROR
- $done = $this->getDoneStringFlag();
- Piwik_Query("DELETE FROM ".$this->tableArchiveNumeric->getTableName()."
- WHERE idarchive = ? AND (name = '".$done."' OR name LIKE '".self::PREFIX_SQL_LOCK."%')",
- array($this->idArchive)
- );
-
- $flag = Piwik_ArchiveProcessing::DONE_OK;
- if($this->isArchiveTemporary())
- {
- $flag = Piwik_ArchiveProcessing::DONE_OK_TEMPORARY;
- }
- $this->insertNumericRecord($done, $flag);
- }
-
- /**
- * Returns the name of the numeric table where the archive numeric values are stored
- *
- * @return string
- */
- public function getTableArchiveNumericName()
- {
- return $this->tableArchiveNumeric->getTableName();
- }
-
- /**
- * Returns the name of the blob table where the archive blob values are stored
- *
- * @return string
- */
- public function getTableArchiveBlobName()
- {
- return $this->tableArchiveBlob->getTableName();
- }
-
- /**
- * Set the period
- *
- * @param Piwik_Period $period
- */
- public function setPeriod( Piwik_Period $period )
- {
- $this->period = $period;
- }
-
- public function setSegment( Piwik_Segment $segment)
- {
- $this->segment = $segment;
- }
-
- public function getSegment()
- {
- return $this->segment;
- }
- /**
- * Set the site
- *
- * @param Piwik_Site $site
- */
- public function setSite( Piwik_Site $site )
- {
- $this->site = $site;
- }
-
- public function setRequestedReport($requestedReport)
- {
- $this->requestedReport = $requestedReport;
- }
-
- protected function getRequestedReport()
- {
- return $this->requestedReport;
- }
-
- static public function getPluginBeingProcessed( $requestedReport )
- {
- $plugin = substr($requestedReport, 0, strpos($requestedReport, '_'));
- if(empty($plugin)
- || !Piwik_PluginsManager::getInstance()->isPluginActivated($plugin))
- {
- $pluginStr = empty($plugin) ? '' : "($plugin)";
- throw new Exception("Error: The report '$requestedReport' was requested but it is not available at this stage. You may also disable the related plugin $pluginStr to avoid this error.");
- }
- return $plugin;
- }
-
- /**
- * Returns the timestamp of the first date of the period
- *
- * @return int
- */
- public function getTimestampStartDate()
- {
- return $this->timestampDateStart;
- }
-
- // exposing the number of visits publicly (number used to compute conversions rates)
- protected $nb_visits = null;
- protected $nb_visits_converted = null;
-
- protected function setNumberOfVisits($nb_visits)
- {
- $this->nb_visits = $nb_visits;
- }
- public function getNumberOfVisits()
- {
- return $this->nb_visits;
- }
- protected function setNumberOfVisitsConverted($nb_visits_converted)
- {
- $this->nb_visits_converted = $nb_visits_converted;
- }
- public function getNumberOfVisitsConverted()
- {
- return $this->nb_visits_converted;
- }
-
-
- /**
- * Returns the idArchive we will use for the current archive
- *
- * @return int IdArchive to use when saving the current Archive
- */
- protected function loadNextIdarchive()
- {
- $table = $this->tableArchiveNumeric->getTableName();
- $dbLockName = "loadNextIdArchive.$table";
-
- $db = Zend_Registry::get('db');
- $locked = self::PREFIX_SQL_LOCK . Piwik_Common::generateUniqId();
- $date = date("Y-m-d H:i:s");
-
- if (Piwik_GetDbLock($dbLockName, $maxRetries = 30) === false)
- {
- throw new Exception("loadNextIdArchive: Cannot get named lock for table $table.");
- }
- $db->exec("INSERT INTO $table "
- ." SELECT ifnull(max(idarchive),0)+1,
- '".$locked."',
- ".(int)$this->idsite.",
- '".$date."',
- '".$date."',
+ }
+
+ /**
+ * Post processing called at the end of the main archive processing.
+ * Makes sure the new archive is marked as "successful" in the DB
+ *
+ * We also try to delete some stuff from memory but really there is still a lot...
+ */
+ protected function postCompute()
+ {
+ // delete the first done = ERROR
+ $done = $this->getDoneStringFlag();
+ Piwik_Query("DELETE FROM " . $this->tableArchiveNumeric->getTableName() . "
+ WHERE idarchive = ? AND (name = '" . $done . "' OR name LIKE '" . self::PREFIX_SQL_LOCK . "%')",
+ array($this->idArchive)
+ );
+
+ $flag = Piwik_ArchiveProcessing::DONE_OK;
+ if ($this->isArchiveTemporary()) {
+ $flag = Piwik_ArchiveProcessing::DONE_OK_TEMPORARY;
+ }
+ $this->insertNumericRecord($done, $flag);
+ }
+
+ /**
+ * Returns the name of the numeric table where the archive numeric values are stored
+ *
+ * @return string
+ */
+ public function getTableArchiveNumericName()
+ {
+ return $this->tableArchiveNumeric->getTableName();
+ }
+
+ /**
+ * Returns the name of the blob table where the archive blob values are stored
+ *
+ * @return string
+ */
+ public function getTableArchiveBlobName()
+ {
+ return $this->tableArchiveBlob->getTableName();
+ }
+
+ /**
+ * Set the period
+ *
+ * @param Piwik_Period $period
+ */
+ public function setPeriod(Piwik_Period $period)
+ {
+ $this->period = $period;
+ }
+
+ public function setSegment(Piwik_Segment $segment)
+ {
+ $this->segment = $segment;
+ }
+
+ public function getSegment()
+ {
+ return $this->segment;
+ }
+
+ /**
+ * Set the site
+ *
+ * @param Piwik_Site $site
+ */
+ public function setSite(Piwik_Site $site)
+ {
+ $this->site = $site;
+ }
+
+ public function setRequestedReport($requestedReport)
+ {
+ $this->requestedReport = $requestedReport;
+ }
+
+ protected function getRequestedReport()
+ {
+ return $this->requestedReport;
+ }
+
+ static public function getPluginBeingProcessed($requestedReport)
+ {
+ $plugin = substr($requestedReport, 0, strpos($requestedReport, '_'));
+ if (empty($plugin)
+ || !Piwik_PluginsManager::getInstance()->isPluginActivated($plugin)
+ ) {
+ $pluginStr = empty($plugin) ? '' : "($plugin)";
+ throw new Exception("Error: The report '$requestedReport' was requested but it is not available at this stage. You may also disable the related plugin $pluginStr to avoid this error.");
+ }
+ return $plugin;
+ }
+
+ /**
+ * Returns the timestamp of the first date of the period
+ *
+ * @return int
+ */
+ public function getTimestampStartDate()
+ {
+ return $this->timestampDateStart;
+ }
+
+ // exposing the number of visits publicly (number used to compute conversions rates)
+ protected $nb_visits = null;
+ protected $nb_visits_converted = null;
+
+ protected function setNumberOfVisits($nb_visits)
+ {
+ $this->nb_visits = $nb_visits;
+ }
+
+ public function getNumberOfVisits()
+ {
+ return $this->nb_visits;
+ }
+
+ protected function setNumberOfVisitsConverted($nb_visits_converted)
+ {
+ $this->nb_visits_converted = $nb_visits_converted;
+ }
+
+ public function getNumberOfVisitsConverted()
+ {
+ return $this->nb_visits_converted;
+ }
+
+
+ /**
+ * Returns the idArchive we will use for the current archive
+ *
+ * @return int IdArchive to use when saving the current Archive
+ */
+ protected function loadNextIdarchive()
+ {
+ $table = $this->tableArchiveNumeric->getTableName();
+ $dbLockName = "loadNextIdArchive.$table";
+
+ $db = Zend_Registry::get('db');
+ $locked = self::PREFIX_SQL_LOCK . Piwik_Common::generateUniqId();
+ $date = date("Y-m-d H:i:s");
+
+ if (Piwik_GetDbLock($dbLockName, $maxRetries = 30) === false) {
+ throw new Exception("loadNextIdArchive: Cannot get named lock for table $table.");
+ }
+ $db->exec("INSERT INTO $table "
+ . " SELECT ifnull(max(idarchive),0)+1,
+ '" . $locked . "',
+ " . (int)$this->idsite . ",
+ '" . $date . "',
+ '" . $date . "',
0,
- '".$date."',
+ '" . $date . "',
0 "
- ." FROM $table as tb1");
- Piwik_ReleaseDbLock($dbLockName);
+ . " FROM $table as tb1");
+ Piwik_ReleaseDbLock($dbLockName);
$id = $db->fetchOne("SELECT idarchive FROM $table WHERE name = ? LIMIT 1", $locked);
- $this->idArchive = $id;
- }
-
- /**
- * @param string $name
- * @param int|float $value
- */
- public function insertNumericRecord($name, $value)
- {
- $value = round($value, 2);
- return $this->insertRecord($name, $value);
- }
-
- /**
- * @param string $name
- * @param string|array $values
- * @return bool|array
- */
- public function insertBlobRecord($name, $values)
- {
- if(is_array($values))
- {
- $clean = array();
- foreach($values as $id => $value)
- {
- // for the parent Table we keep the name
- // for example for the Table of searchEngines we keep the name 'referer_search_engine'
- // but for the child table of 'Google' which has the ID = 9 the name would be 'referer_search_engine_9'
- $newName = $name;
- if($id != 0)
- {
- $newName = $name . '_' . $id;
- }
-
- if($this->compressBlob)
- {
- $value = $this->compress($value);
- }
- $clean[] = array($newName, $value);
- }
- return $this->insertBulkRecords($clean);
- }
-
- if($this->compressBlob)
- {
- $values = $this->compress($values);
- }
-
- $this->insertRecord($name, $values);
- return array($name => $values);
- }
-
- protected function compress($data)
- {
- return gzcompress($data);
- }
-
- protected function insertBulkRecords($records)
- {
- // Using standard plain INSERT if there is only one record to insert
- if($DEBUG_DO_NOT_USE_BULK_INSERT = false
- || count($records) == 1)
- {
- foreach($records as $record)
- {
- $this->insertRecord($record[0], $record[1]);
- }
- return ;
- }
- $bindSql = $this->getBindArray();
- $values = array();
-
- foreach($records as $record)
- {
- // don't record zero
- if(empty($record[1])) continue;
-
- $bind = $bindSql;
- $bind[] = $record[0]; // name
- $bind[] = $record[1]; // value
- $values[] = $bind;
-
- }
- if(empty($values)) return ;
-
- if(is_numeric($record[1]))
- {
- $table = $this->tableArchiveNumeric;
- }
- else
- {
- $table = $this->tableArchiveBlob;
- }
-
- Piwik::tableInsertBatch($table->getTableName(), $this->getInsertFields(), $values);
- return true;
- }
-
- protected function getBindArray()
- {
- return array( $this->idArchive,
- $this->idsite,
- $this->period->getDateStart()->toString('Y-m-d'),
- $this->period->getDateEnd()->toString('Y-m-d'),
- $this->periodId,
- date("Y-m-d H:i:s"));
- }
-
- protected function getInsertFields()
- {
- return array('idarchive', 'idsite', 'date1', 'date2', 'period', 'ts_archived', 'name', 'value');
- }
-
- /**
- * Inserts a record in the right table (either NUMERIC or BLOB)
- * @param $name
- * @param $value
- * @return
- */
- protected function insertRecord($name, $value)
- {
- // table to use to save the data
- if(is_numeric($value))
- {
- // We choose not to record records with a value of 0
- if($value == 0)
- {
- return;
- }
- $table = $this->tableArchiveNumeric;
- }
- else
- {
- $table = $this->tableArchiveBlob;
- }
-
- // duplicate idarchives are Ignored, see http://dev.piwik.org/trac/ticket/987
-
- $query = "INSERT IGNORE INTO ".$table->getTableName()."
- (". implode(", ", $this->getInsertFields()).")
+ $this->idArchive = $id;
+ }
+
+ /**
+ * @param string $name
+ * @param int|float $value
+ */
+ public function insertNumericRecord($name, $value)
+ {
+ $value = round($value, 2);
+ return $this->insertRecord($name, $value);
+ }
+
+ /**
+ * @param string $name
+ * @param string|array $values
+ * @return bool|array
+ */
+ public function insertBlobRecord($name, $values)
+ {
+ if (is_array($values)) {
+ $clean = array();
+ foreach ($values as $id => $value) {
+ // for the parent Table we keep the name
+ // for example for the Table of searchEngines we keep the name 'referer_search_engine'
+ // but for the child table of 'Google' which has the ID = 9 the name would be 'referer_search_engine_9'
+ $newName = $name;
+ if ($id != 0) {
+ $newName = $name . '_' . $id;
+ }
+
+ if ($this->compressBlob) {
+ $value = $this->compress($value);
+ }
+ $clean[] = array($newName, $value);
+ }
+ return $this->insertBulkRecords($clean);
+ }
+
+ if ($this->compressBlob) {
+ $values = $this->compress($values);
+ }
+
+ $this->insertRecord($name, $values);
+ return array($name => $values);
+ }
+
+ protected function compress($data)
+ {
+ return gzcompress($data);
+ }
+
+ protected function insertBulkRecords($records)
+ {
+ // Using standard plain INSERT if there is only one record to insert
+ if ($DEBUG_DO_NOT_USE_BULK_INSERT = false
+ || count($records) == 1
+ ) {
+ foreach ($records as $record) {
+ $this->insertRecord($record[0], $record[1]);
+ }
+ return;
+ }
+ $bindSql = $this->getBindArray();
+ $values = array();
+
+ foreach ($records as $record) {
+ // don't record zero
+ if (empty($record[1])) continue;
+
+ $bind = $bindSql;
+ $bind[] = $record[0]; // name
+ $bind[] = $record[1]; // value
+ $values[] = $bind;
+
+ }
+ if (empty($values)) return;
+
+ if (is_numeric($record[1])) {
+ $table = $this->tableArchiveNumeric;
+ } else {
+ $table = $this->tableArchiveBlob;
+ }
+
+ Piwik::tableInsertBatch($table->getTableName(), $this->getInsertFields(), $values);
+ return true;
+ }
+
+ protected function getBindArray()
+ {
+ return array($this->idArchive,
+ $this->idsite,
+ $this->period->getDateStart()->toString('Y-m-d'),
+ $this->period->getDateEnd()->toString('Y-m-d'),
+ $this->periodId,
+ date("Y-m-d H:i:s"));
+ }
+
+ protected function getInsertFields()
+ {
+ return array('idarchive', 'idsite', 'date1', 'date2', 'period', 'ts_archived', 'name', 'value');
+ }
+
+ /**
+ * Inserts a record in the right table (either NUMERIC or BLOB)
+ * @param $name
+ * @param $value
+ * @return
+ */
+ protected function insertRecord($name, $value)
+ {
+ // table to use to save the data
+ if (is_numeric($value)) {
+ // We choose not to record records with a value of 0
+ if ($value == 0) {
+ return;
+ }
+ $table = $this->tableArchiveNumeric;
+ } else {
+ $table = $this->tableArchiveBlob;
+ }
+
+ // duplicate idarchives are Ignored, see http://dev.piwik.org/trac/ticket/987
+
+ $query = "INSERT IGNORE INTO " . $table->getTableName() . "
+ (" . implode(", ", $this->getInsertFields()) . ")
VALUES (?,?,?,?,?,?,?,?)";
- $bindSql = $this->getBindArray();
- $bindSql[] = $name;
- $bindSql[] = $value;
- Piwik_Query($query, $bindSql);
- }
-
- /**
- * Returns the idArchive if the archive is available in the database.
- * Returns false if the archive needs to be computed.
- *
- * An archive is available if
- * - for today, the archive was computed less than minDatetimeArchiveProcessedUTC seconds ago
- * - for any other day, if the archive was computed once this day was finished
- * - for other periods, if the archive was computed once the period was finished
- *
- * @return int|false
- */
- protected function isArchived()
- {
- $bindSQL = array( $this->idsite,
- $this->period->getDateStart()->toString('Y-m-d'),
- $this->period->getDateEnd()->toString('Y-m-d'),
- $this->periodId,
- );
-
- $timeStampWhere = '';
-
- if($this->minDatetimeArchiveProcessedUTC)
- {
- $timeStampWhere = " AND ts_archived >= ? ";
- $bindSQL[] = Piwik_Date::factory($this->minDatetimeArchiveProcessedUTC)->getDatetime();
- }
-
- // When a Segment is specified, we try and only process the requested report in the archive
- // As a limitation, we don't know all the time which plugin should process which report
- // There is a catch all flag 'all' appended to archives containing all reports already
- // We look for this 'done.ABCDEFG.all', or for an archive that contains only our plugin data 'done.ABDCDEFG.Referers'
- $done = $this->getDoneStringFlag();
- $doneAllPluginsProcessed = $this->getDoneStringFlag($flagArchiveAsAllPlugins = true);
-
- $sqlSegmentsFindArchiveAllPlugins = '';
-
- if($done != $doneAllPluginsProcessed)
- {
- $sqlSegmentsFindArchiveAllPlugins = "OR (name = '".$doneAllPluginsProcessed."' AND value = ".Piwik_ArchiveProcessing::DONE_OK.")
- OR (name = '".$doneAllPluginsProcessed."' AND value = ".Piwik_ArchiveProcessing::DONE_OK_TEMPORARY.")";
- }
- $sqlQuery = " SELECT idarchive, value, name, date1 as startDate
- FROM ".$this->tableArchiveNumeric->getTableName()."
+ $bindSql = $this->getBindArray();
+ $bindSql[] = $name;
+ $bindSql[] = $value;
+ Piwik_Query($query, $bindSql);
+ }
+
+ /**
+ * Returns the idArchive if the archive is available in the database.
+ * Returns false if the archive needs to be computed.
+ *
+ * An archive is available if
+ * - for today, the archive was computed less than minDatetimeArchiveProcessedUTC seconds ago
+ * - for any other day, if the archive was computed once this day was finished
+ * - for other periods, if the archive was computed once the period was finished
+ *
+ * @return int|false
+ */
+ protected function isArchived()
+ {
+ $bindSQL = array($this->idsite,
+ $this->period->getDateStart()->toString('Y-m-d'),
+ $this->period->getDateEnd()->toString('Y-m-d'),
+ $this->periodId,
+ );
+
+ $timeStampWhere = '';
+
+ if ($this->minDatetimeArchiveProcessedUTC) {
+ $timeStampWhere = " AND ts_archived >= ? ";
+ $bindSQL[] = Piwik_Date::factory($this->minDatetimeArchiveProcessedUTC)->getDatetime();
+ }
+
+ // When a Segment is specified, we try and only process the requested report in the archive
+ // As a limitation, we don't know all the time which plugin should process which report
+ // There is a catch all flag 'all' appended to archives containing all reports already
+ // We look for this 'done.ABCDEFG.all', or for an archive that contains only our plugin data 'done.ABDCDEFG.Referers'
+ $done = $this->getDoneStringFlag();
+ $doneAllPluginsProcessed = $this->getDoneStringFlag($flagArchiveAsAllPlugins = true);
+
+ $sqlSegmentsFindArchiveAllPlugins = '';
+
+ if ($done != $doneAllPluginsProcessed) {
+ $sqlSegmentsFindArchiveAllPlugins = "OR (name = '" . $doneAllPluginsProcessed . "' AND value = " . Piwik_ArchiveProcessing::DONE_OK . ")
+ OR (name = '" . $doneAllPluginsProcessed . "' AND value = " . Piwik_ArchiveProcessing::DONE_OK_TEMPORARY . ")";
+ }
+ $sqlQuery = " SELECT idarchive, value, name, date1 as startDate
+ FROM " . $this->tableArchiveNumeric->getTableName() . "
WHERE idsite = ?
AND date1 = ?
AND date2 = ?
AND period = ?
- AND ( (name = '".$done."' AND value = ".Piwik_ArchiveProcessing::DONE_OK.")
- OR (name = '".$done."' AND value = ".Piwik_ArchiveProcessing::DONE_OK_TEMPORARY.")
+ AND ( (name = '" . $done . "' AND value = " . Piwik_ArchiveProcessing::DONE_OK . ")
+ OR (name = '" . $done . "' AND value = " . Piwik_ArchiveProcessing::DONE_OK_TEMPORARY . ")
$sqlSegmentsFindArchiveAllPlugins
OR name = 'nb_visits')
$timeStampWhere
ORDER BY idarchive DESC";
- $results = Piwik_FetchAll($sqlQuery, $bindSQL );
- if(empty($results))
- {
- return false;
- }
-
- $idarchive = false;
- // we look for the more recent idarchive
- foreach($results as $result)
- {
- if($result['name'] == $done
- || $result['name'] == $doneAllPluginsProcessed)
- {
- $idarchive = $result['idarchive'];
- $this->timestampDateStart = Piwik_Date::factory($result['startDate'])->getTimestamp();
- break;
- }
- }
-
- // case when we have a nb_visits entry in the archive, but the process is not finished yet or failed to finish
- // therefore we don't have the done=OK
- if($idarchive === false)
- {
- return false;
- }
-
- if($this->getPluginBeingProcessed($this->getRequestedReport()) == 'VisitsSummary')
- {
- $this->isThereSomeVisits = false;
- }
-
- // we look for the nb_visits result for this most recent archive
- foreach($results as $result)
- {
- if($result['name'] == 'nb_visits'
- && $result['idarchive'] == $idarchive)
- {
- $this->isThereSomeVisits = ($result['value'] > 0);
- $this->setNumberOfVisits($result['value']);
- break;
- }
- }
- return $idarchive;
- }
-
- /**
- * Returns true if, for some reasons, triggering the archiving is disabled.
- * Note that when a segment is passed to the function, archiving will always occur
- * (since segments are by default not pre-processed)
- *
- * @return bool
- */
- public function isArchivingDisabled()
- {
- return self::isArchivingDisabledFor($this->getSegment(), $this->period);
- }
-
- public static function isArchivingDisabledFor($segment, $period)
- {
- if($period->getLabel() == 'range') {
+ $results = Piwik_FetchAll($sqlQuery, $bindSQL);
+ if (empty($results)) {
+ return false;
+ }
+
+ $idarchive = false;
+ // we look for the more recent idarchive
+ foreach ($results as $result) {
+ if ($result['name'] == $done
+ || $result['name'] == $doneAllPluginsProcessed
+ ) {
+ $idarchive = $result['idarchive'];
+ $this->timestampDateStart = Piwik_Date::factory($result['startDate'])->getTimestamp();
+ break;
+ }
+ }
+
+ // case when we have a nb_visits entry in the archive, but the process is not finished yet or failed to finish
+ // therefore we don't have the done=OK
+ if ($idarchive === false) {
+ return false;
+ }
+
+ if ($this->getPluginBeingProcessed($this->getRequestedReport()) == 'VisitsSummary') {
+ $this->isThereSomeVisits = false;
+ }
+
+ // we look for the nb_visits result for this most recent archive
+ foreach ($results as $result) {
+ if ($result['name'] == 'nb_visits'
+ && $result['idarchive'] == $idarchive
+ ) {
+ $this->isThereSomeVisits = ($result['value'] > 0);
+ $this->setNumberOfVisits($result['value']);
+ break;
+ }
+ }
+ return $idarchive;
+ }
+
+ /**
+ * Returns true if, for some reasons, triggering the archiving is disabled.
+ * Note that when a segment is passed to the function, archiving will always occur
+ * (since segments are by default not pre-processed)
+ *
+ * @return bool
+ */
+ public function isArchivingDisabled()
+ {
+ return self::isArchivingDisabledFor($this->getSegment(), $this->period);
+ }
+
+ public static function isArchivingDisabledFor($segment, $period)
+ {
+ if ($period->getLabel() == 'range') {
return false;
}
$processOneReportOnly = !self::shouldProcessReportsAllPluginsFor($segment, $period);
$isArchivingDisabled = !self::isRequestAuthorizedToArchive();
- if($processOneReportOnly)
- {
+ if ($processOneReportOnly) {
// When there is a segment, archiving is not necessary allowed
// If browser archiving is allowed, then archiving is enabled
// if browser archiving is not allowed, then archiving is disabled
- if(!$segment->isEmpty()
+ if (!$segment->isEmpty()
&& $isArchivingDisabled
&& Piwik_Config::getInstance()->General['browser_archiving_disabled_enforce']
- )
- {
+ ) {
Piwik::log("Archiving is disabled because of config setting browser_archiving_disabled_enforce=1");
return true;
}
return false;
}
- return $isArchivingDisabled;
- }
-
- protected static function isRequestAuthorizedToArchive()
- {
- return !self::$forceDisableArchiving &&
- (self::isBrowserTriggerArchivingEnabled()
- || Piwik_Common::isPhpCliMode()
- || (Piwik::isUserIsSuperUser()
- && Piwik_Common::isArchivePhpTriggered()))
- ;
- }
-
- /**
- * Returns true when
- * - there is no segment and period is not range
- * - there is a segment that is part of the preprocessed [Segments] list
- * @param Piwik_Segment $segment
- * @param Piwik_Period $period
- * @return bool
- */
- protected function shouldProcessReportsAllPlugins($segment, $period)
- {
- return self::shouldProcessReportsAllPluginsFor($segment, $period);
- }
-
- /**
- * @param Piwik_Segment $segment
- * @param Piwik_Period $period
- * @return bool
- */
- protected static function shouldProcessReportsAllPluginsFor($segment, $period)
- {
- if($segment->isEmpty() && $period->getLabel() != 'range')
- {
- return true;
- }
-
- $segmentsToProcess = Piwik::getKnownSegmentsToArchive();
- if(!empty($segmentsToProcess))
- {
- // If the requested segment is one of the segments to pre-process
- // we ensure that any call to the API will trigger archiving of all reports for this segment
- $segment = $segment->getString();
- if(in_array($segment, $segmentsToProcess))
- {
- return true;
- }
- }
- return false;
- }
-
- /**
- * When a segment is set, we shall only process the requested report (no more).
- * The requested data set will return a lot faster if we only process these reports rather than all plugins.
- * Similarly, when a period=range is requested, we shall only process the requested report for the range itself.
- *
- * @param string $pluginName
- * @return bool
- */
- public function shouldProcessReportsForPlugin($pluginName)
- {
- if($this->shouldProcessReportsAllPlugins($this->getSegment(), $this->period))
- {
- return true;
- }
-
- // If any other segment, only process if the requested report belong to this plugin
- // or process all plugins if the requested report plugin couldn't be guessed
- $pluginBeingProcessed = self::getPluginBeingProcessed($this->getRequestedReport());
- return $pluginBeingProcessed == $pluginName
- || !Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginBeingProcessed)
- ;
- }
-
+ return $isArchivingDisabled;
+ }
+
+ protected static function isRequestAuthorizedToArchive()
+ {
+ return !self::$forceDisableArchiving &&
+ (self::isBrowserTriggerArchivingEnabled()
+ || Piwik_Common::isPhpCliMode()
+ || (Piwik::isUserIsSuperUser()
+ && Piwik_Common::isArchivePhpTriggered()));
+ }
+
+ /**
+ * Returns true when
+ * - there is no segment and period is not range
+ * - there is a segment that is part of the preprocessed [Segments] list
+ * @param Piwik_Segment $segment
+ * @param Piwik_Period $period
+ * @return bool
+ */
+ protected function shouldProcessReportsAllPlugins($segment, $period)
+ {
+ return self::shouldProcessReportsAllPluginsFor($segment, $period);
+ }
+
+ /**
+ * @param Piwik_Segment $segment
+ * @param Piwik_Period $period
+ * @return bool
+ */
+ protected static function shouldProcessReportsAllPluginsFor($segment, $period)
+ {
+ if ($segment->isEmpty() && $period->getLabel() != 'range') {
+ return true;
+ }
+
+ $segmentsToProcess = Piwik::getKnownSegmentsToArchive();
+ if (!empty($segmentsToProcess)) {
+ // If the requested segment is one of the segments to pre-process
+ // we ensure that any call to the API will trigger archiving of all reports for this segment
+ $segment = $segment->getString();
+ if (in_array($segment, $segmentsToProcess)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * When a segment is set, we shall only process the requested report (no more).
+ * The requested data set will return a lot faster if we only process these reports rather than all plugins.
+ * Similarly, when a period=range is requested, we shall only process the requested report for the range itself.
+ *
+ * @param string $pluginName
+ * @return bool
+ */
+ public function shouldProcessReportsForPlugin($pluginName)
+ {
+ if ($this->shouldProcessReportsAllPlugins($this->getSegment(), $this->period)) {
+ return true;
+ }
+
+ // If any other segment, only process if the requested report belong to this plugin
+ // or process all plugins if the requested report plugin couldn't be guessed
+ $pluginBeingProcessed = self::getPluginBeingProcessed($this->getRequestedReport());
+ return $pluginBeingProcessed == $pluginName
+ || !Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginBeingProcessed);
+ }
+
}
diff --git a/core/ArchiveProcessing/Day.php b/core/ArchiveProcessing/Day.php
index 0e4198a36f..07921e9a50 100644
--- a/core/ArchiveProcessing/Day.php
+++ b/core/ArchiveProcessing/Day.php
@@ -21,65 +21,62 @@
*/
class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing
{
- /**
- * Constructor
- */
- function __construct()
- {
- parent::__construct();
- $this->db = Zend_Registry::get('db');
- }
-
- /**
- * Main method to process logs for a day. The only logic done here is computing the number of visits, actions, etc.
- * All the other reports are computed inside plugins listening to the event 'ArchiveProcessing_Day.compute'.
- * See some of the plugins for an example eg. 'Provider'
- */
- protected function compute()
- {
- if(!$this->isThereSomeVisits())
- {
- return;
- }
- Piwik_PostEvent('ArchiveProcessing_Day.compute', $this);
- }
-
- /**
- * Returns true if there are logs for the current archive.
- *
- * If the current archive is for a specific plugin (for example, Referers),
- * (for example when a Segment is defined and the Keywords report is requested)
- * Then the function will create the Archive for the Core metrics 'VisitsSummary' which will in turn process the number of visits
- *
- * If there is no specified segment, the SQL query will always run.
- *
- * @return bool|null
- */
- public function isThereSomeVisits()
- {
- if (!is_null($this->isThereSomeVisits))
- {
- if ($this->isThereSomeVisits && is_null($this->nb_visits))
- {
- debug_print_backtrace();
- exit;
- }
- return $this->isThereSomeVisits;
- }
-
- // prepare segmentation
- $segment = $this->getSegment();
-
- // We check if there is visits for the requested date / site / segment
- // If no specified Segment
- // Or if a segment is passed and we specifically process VisitsSummary
- // Then we check the logs. This is to ensure that this query is ran only once for this day/site/segment (rather than running it for every plugin)
- $reportType = self::getPluginBeingProcessed($this->getRequestedReport());
- if ($this->shouldProcessReportsAllPlugins($this->getSegment(), $this->period)
- || ($reportType == 'VisitsSummary'))
- {
- // build query parts
- $select = "count(distinct log_visit.idvisitor) as nb_uniq_visitors,
+ /**
+ * Constructor
+ */
+ function __construct()
+ {
+ parent::__construct();
+ $this->db = Zend_Registry::get('db');
+ }
+
+ /**
+ * Main method to process logs for a day. The only logic done here is computing the number of visits, actions, etc.
+ * All the other reports are computed inside plugins listening to the event 'ArchiveProcessing_Day.compute'.
+ * See some of the plugins for an example eg. 'Provider'
+ */
+ protected function compute()
+ {
+ if (!$this->isThereSomeVisits()) {
+ return;
+ }
+ Piwik_PostEvent('ArchiveProcessing_Day.compute', $this);
+ }
+
+ /**
+ * Returns true if there are logs for the current archive.
+ *
+ * If the current archive is for a specific plugin (for example, Referers),
+ * (for example when a Segment is defined and the Keywords report is requested)
+ * Then the function will create the Archive for the Core metrics 'VisitsSummary' which will in turn process the number of visits
+ *
+ * If there is no specified segment, the SQL query will always run.
+ *
+ * @return bool|null
+ */
+ public function isThereSomeVisits()
+ {
+ if (!is_null($this->isThereSomeVisits)) {
+ if ($this->isThereSomeVisits && is_null($this->nb_visits)) {
+ debug_print_backtrace();
+ exit;
+ }
+ return $this->isThereSomeVisits;
+ }
+
+ // prepare segmentation
+ $segment = $this->getSegment();
+
+ // We check if there is visits for the requested date / site / segment
+ // If no specified Segment
+ // Or if a segment is passed and we specifically process VisitsSummary
+ // Then we check the logs. This is to ensure that this query is ran only once for this day/site/segment (rather than running it for every plugin)
+ $reportType = self::getPluginBeingProcessed($this->getRequestedReport());
+ if ($this->shouldProcessReportsAllPlugins($this->getSegment(), $this->period)
+ || ($reportType == 'VisitsSummary')
+ ) {
+ // build query parts
+ $select = "count(distinct log_visit.idvisitor) as nb_uniq_visitors,
count(*) as nb_visits,
sum(log_visit.visit_total_actions) as nb_actions,
max(log_visit.visit_total_actions) as max_actions,
@@ -87,545 +84,503 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as bounce_count,
sum(case log_visit.visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted
";
- $from = "log_visit";
- $where = "log_visit.visit_last_action_time >= ?
+ $from = "log_visit";
+ $where = "log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?
";
-
- $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
- $query = $segment->getSelectQuery($select, $from, $where, $bind);
-
- $bind = $query['bind'];
- $sql = $query['sql'];
-
- $data = $this->db->fetchRow($sql, $bind);
-
- // no visits found
- if (!is_array($data) || $data['nb_visits'] == 0)
- {
- return $this->isThereSomeVisits = false;
- }
-
- // visits found: set attribtues
- foreach ($data as $name => $value)
- {
- $this->insertNumericRecord($name, $value);
- }
-
- $this->setNumberOfVisits($data['nb_visits']);
- $this->setNumberOfVisitsConverted($data['nb_visits_converted']);
-
- return $this->isThereSomeVisits = true;
- }
-
- return $this->redirectRequestToVisitsSummary();
- }
-
- /**
- * If a segment is specified but a plugin other than 'VisitsSummary' is being requested,
- * we create an archive for processing VisitsSummary Core Metrics, which will in turn
- * execute the query above (in isThereSomeVisits)
- *
- * @return bool|null
- */
- private function redirectRequestToVisitsSummary()
- {
- $archive = new Piwik_Archive_Single();
- $archive->setSite($this->site);
- $archive->setPeriod($this->period);
- $archive->setSegment($this->getSegment());
- $archive->setRequestedReport('VisitsSummary');
-
- $nbVisits = $archive->getNumeric('nb_visits');
- $this->isThereSomeVisits = $nbVisits > 0;
-
- if ($this->isThereSomeVisits)
- {
- $nbVisitsConverted = $archive->getNumeric('nb_visits_converted');
- $this->setNumberOfVisits($nbVisits);
- $this->setNumberOfVisitsConverted($nbVisitsConverted);
- }
-
- return $this->isThereSomeVisits;
- }
-
- /**
- * Creates and returns an array of SQL SELECT expressions that will summarize
- * the data in a column of a specified table, over a set of ranges.
- *
- * The SELECT expressions will count the number of column values that are
- * within each range.
- *
- * @param string $column The column of the log_conversion table to reduce.
- * @param array $ranges The ranges to reduce data over.
- * @param string $table The table the SELECTs should use.
- * @param string $selectColumnPrefix The prefix when specifying what a SELECT
- * expression will be selected AS.
- * @param bool|string $extraCondition An extra condition to be appended to 'case when'
- * expressions. Must start with the logical operator,
- * ie (AND, OR, etc.).
- * @return array An array of SQL SELECT expressions.
- */
- public static function buildReduceByRangeSelect(
- $column, $ranges, $table, $selectColumnPrefix = '', $extraCondition = false)
- {
- $selects = array();
-
- foreach($ranges as $gap)
- {
- if (count($gap) == 2)
- {
- $lowerBound = $gap[0];
- $upperBound = $gap[1];
-
- $selectAs = "$selectColumnPrefix$lowerBound-$upperBound";
-
- $selects[] = "sum(case when $table.$column between $lowerBound and $upperBound $extraCondition".
- " then 1 else 0 end) as `$selectAs`";
- }
- else
- {
- $lowerBound = $gap[0];
-
- $selectAs = $selectColumnPrefix.($lowerBound + 1).urlencode('+');
-
- $selects[] = "sum(case when $table.$column > $lowerBound $extraCondition then 1 else 0 end) as `$selectAs`";
- }
- }
-
- return $selects;
- }
-
- /**
- * Converts a database SELECT result into a whole DataTable with two columns and as many
- * rows as elements in $row.
- *
- * The key of each element in $row is used as the value of the first column, and the
- * value of each element is used as the second column.
- *
- * NOTE: $selectAsPrefix can be used to make sure only SOME of the data in $row is used.
- *
- * @param array $row The database row to convert.
- * @param mixed $labelCount The label to use for the second column of the DataTable result.
- * @param string $selectAsPrefix A string that identifies which elements of $row to use
- * in the result. Every key of $row that starts with this
- * value is used.
- * @return Piwik_DataTable
- */
- public function getSimpleDataTableFromRow($row, $labelCount, $selectAsPrefix = '')
- {
- // the labels in $row can have prefixes that need to be removed before creating a table
- $cleanRow = array();
-
- foreach($row as $label => $count)
- {
- if (empty($selectAsPrefix) || strpos($label, $selectAsPrefix) === 0)
- {
- $cleanLabel = substr($label, strlen($selectAsPrefix));
-
- $cleanRow[$cleanLabel] = array($labelCount => $count);
- }
- }
-
- $table = new Piwik_DataTable();
- $table->addRowsFromArrayWithIndexLabel($cleanRow);
- return $table;
- }
-
- /**
- * Performs a simple query on the log_visit table within the time range this archive
- * represents.
- *
- * @param string $select The SELECT clause.
- * @param string|bool $orderBy The ORDER BY clause (without the 'ORDER BY' part). Set to
- * false to specify no ORDER BY.
- * @param array|bool $groupByCols An array of column names to group by. Set to false to
- * specify no GROUP BY.
- * @param bool $oneResultRow Whether only one row is expected or not. If set to true,
- * this function returns one row, if false, an array of rows.
- * @return array
- */
- public function queryVisitsSimple($select, $orderBy = false, $groupByCols = false, $oneResultRow = true)
- {
- $from = "log_visit";
- $where = "log_visit.visit_last_action_time >= ?
+
+ $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
+ $query = $segment->getSelectQuery($select, $from, $where, $bind);
+
+ $bind = $query['bind'];
+ $sql = $query['sql'];
+
+ $data = $this->db->fetchRow($sql, $bind);
+
+ // no visits found
+ if (!is_array($data) || $data['nb_visits'] == 0) {
+ return $this->isThereSomeVisits = false;
+ }
+
+ // visits found: set attribtues
+ foreach ($data as $name => $value) {
+ $this->insertNumericRecord($name, $value);
+ }
+
+ $this->setNumberOfVisits($data['nb_visits']);
+ $this->setNumberOfVisitsConverted($data['nb_visits_converted']);
+
+ return $this->isThereSomeVisits = true;
+ }
+
+ return $this->redirectRequestToVisitsSummary();
+ }
+
+ /**
+ * If a segment is specified but a plugin other than 'VisitsSummary' is being requested,
+ * we create an archive for processing VisitsSummary Core Metrics, which will in turn
+ * execute the query above (in isThereSomeVisits)
+ *
+ * @return bool|null
+ */
+ private function redirectRequestToVisitsSummary()
+ {
+ $archive = new Piwik_Archive_Single();
+ $archive->setSite($this->site);
+ $archive->setPeriod($this->period);
+ $archive->setSegment($this->getSegment());
+ $archive->setRequestedReport('VisitsSummary');
+
+ $nbVisits = $archive->getNumeric('nb_visits');
+ $this->isThereSomeVisits = $nbVisits > 0;
+
+ if ($this->isThereSomeVisits) {
+ $nbVisitsConverted = $archive->getNumeric('nb_visits_converted');
+ $this->setNumberOfVisits($nbVisits);
+ $this->setNumberOfVisitsConverted($nbVisitsConverted);
+ }
+
+ return $this->isThereSomeVisits;
+ }
+
+ /**
+ * Creates and returns an array of SQL SELECT expressions that will summarize
+ * the data in a column of a specified table, over a set of ranges.
+ *
+ * The SELECT expressions will count the number of column values that are
+ * within each range.
+ *
+ * @param string $column The column of the log_conversion table to reduce.
+ * @param array $ranges The ranges to reduce data over.
+ * @param string $table The table the SELECTs should use.
+ * @param string $selectColumnPrefix The prefix when specifying what a SELECT
+ * expression will be selected AS.
+ * @param bool|string $extraCondition An extra condition to be appended to 'case when'
+ * expressions. Must start with the logical operator,
+ * ie (AND, OR, etc.).
+ * @return array An array of SQL SELECT expressions.
+ */
+ public static function buildReduceByRangeSelect(
+ $column, $ranges, $table, $selectColumnPrefix = '', $extraCondition = false)
+ {
+ $selects = array();
+
+ foreach ($ranges as $gap) {
+ if (count($gap) == 2) {
+ $lowerBound = $gap[0];
+ $upperBound = $gap[1];
+
+ $selectAs = "$selectColumnPrefix$lowerBound-$upperBound";
+
+ $selects[] = "sum(case when $table.$column between $lowerBound and $upperBound $extraCondition" .
+ " then 1 else 0 end) as `$selectAs`";
+ } else {
+ $lowerBound = $gap[0];
+
+ $selectAs = $selectColumnPrefix . ($lowerBound + 1) . urlencode('+');
+
+ $selects[] = "sum(case when $table.$column > $lowerBound $extraCondition then 1 else 0 end) as `$selectAs`";
+ }
+ }
+
+ return $selects;
+ }
+
+ /**
+ * Converts a database SELECT result into a whole DataTable with two columns and as many
+ * rows as elements in $row.
+ *
+ * The key of each element in $row is used as the value of the first column, and the
+ * value of each element is used as the second column.
+ *
+ * NOTE: $selectAsPrefix can be used to make sure only SOME of the data in $row is used.
+ *
+ * @param array $row The database row to convert.
+ * @param mixed $labelCount The label to use for the second column of the DataTable result.
+ * @param string $selectAsPrefix A string that identifies which elements of $row to use
+ * in the result. Every key of $row that starts with this
+ * value is used.
+ * @return Piwik_DataTable
+ */
+ public function getSimpleDataTableFromRow($row, $labelCount, $selectAsPrefix = '')
+ {
+ // the labels in $row can have prefixes that need to be removed before creating a table
+ $cleanRow = array();
+
+ foreach ($row as $label => $count) {
+ if (empty($selectAsPrefix) || strpos($label, $selectAsPrefix) === 0) {
+ $cleanLabel = substr($label, strlen($selectAsPrefix));
+
+ $cleanRow[$cleanLabel] = array($labelCount => $count);
+ }
+ }
+
+ $table = new Piwik_DataTable();
+ $table->addRowsFromArrayWithIndexLabel($cleanRow);
+ return $table;
+ }
+
+ /**
+ * Performs a simple query on the log_visit table within the time range this archive
+ * represents.
+ *
+ * @param string $select The SELECT clause.
+ * @param string|bool $orderBy The ORDER BY clause (without the 'ORDER BY' part). Set to
+ * false to specify no ORDER BY.
+ * @param array|bool $groupByCols An array of column names to group by. Set to false to
+ * specify no GROUP BY.
+ * @param bool $oneResultRow Whether only one row is expected or not. If set to true,
+ * this function returns one row, if false, an array of rows.
+ * @return array
+ */
+ public function queryVisitsSimple($select, $orderBy = false, $groupByCols = false, $oneResultRow = true)
+ {
+ $from = "log_visit";
+ $where = "log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?";
-
- $groupBy = false;
- if ($groupByCols and !empty($groupByCols))
- {
- $groupBy = implode(',', $groupByCols);
- }
-
- $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
-
- $query = $this->getSegment()->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy);
-
- if ($oneResultRow)
- {
- return $this->db->fetchRow($query['sql'], $query['bind']);
- }
- else
- {
- return $this->db->fetchAll($query['sql'], $query['bind']);
- }
- }
-
- /**
- * Helper function that returns a DataTable containing the $select fields / value pairs.
- * IMPORTANT: The $select must return only one row!!
- *
- * Example $select = "count(distinct( config_os )) as countDistinctOs,
- * sum( config_flash ) / count(distinct(idvisit)) as percentFlash "
- * $labelCount = "test_column_name"
- * will return a dataTable that looks like
- * label test_column_name
- * CountDistinctOs 9
- * PercentFlash 0.5676
- *
- *
- * @param string $select
- * @param string $labelCount
- * @return Piwik_DataTable
- */
- public function getSimpleDataTableFromSelect($select, $labelCount)
- {
- $data = $this->queryVisitsSimple($select);
- return $this->getSimpleDataTableFromRow($data, $labelCount);
- }
-
- /**
- * Returns the actions by the given dimension
- *
- * - The basic use case is to use $label and optionally $where.
- * - If you want to apply a limit and group the others, use $orderBy to sort the way you
- * want the limit to be applied and pass a pre-configured instance of Piwik_RankingQuery.
- * The ranking query instance has to have a limit and at least one label column.
- * See Piwik_RankingQuery::setLimit() and Piwik_RankingQuery::addLabelColumn().
- * If $rankingQuery is set, the return value is the array returned by
- * Piwik_RankingQuery::execute().
- * - By default, the method only queries log_link_visit_action. If you need data from
- * log_action (e.g. to partition the result from the ranking query into the different
- * action types), use $joinLogActionOnColumn and $addSelect to join log_action and select
- * the column you need from log_action.
- *
- *
- * @param array|string $label the dimensions(s) you're interested in
- * @param string $where where clause
- * @param bool|array $metrics Set this if you want to limit the columns that are returned.
- * The possible values in the array are Piwik_Archive::INDEX_*.
- * @param bool|string $orderBy order by clause
- * @param Piwik_RankingQuery $rankingQuery pre-configured ranking query instance
- * @param bool|string $joinLogActionOnColumn column from log_link_visit_action that
- * log_action should be joined on.
- * can be an array to join multiple times.
- * @param bool|string $addSelect additional select clause
- * @return mixed
- */
- public function queryActionsByDimension($label, $where = '', $metrics = false, $orderBy = false,
- $rankingQuery = null, $joinLogActionOnColumn = false, $addSelect = false)
- {
- if(is_array($label))
- {
- $label2 = $label;
- foreach($label2 as &$field) { $field = 'log_link_visit_action.'. $field; }
- $groupBy = implode(", ", $label2);
- foreach($label2 as $id => &$field) { $field = "$field AS ".$label[$id]; }
- $select = implode(", ", $label2);
-
- // IF we query Custom Variables scope "page" either: Product SKU, Product Name,
- // then we also query the "Product page view" price which was possibly recorded.
- if(in_array(reset($label), array('custom_var_k3','custom_var_k4','custom_var_k5')))
- {
- $select .= ", ".self::getSqlRevenue("AVG(log_link_visit_action.custom_var_v2)")." as `". Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED ."`";
- }
- }
- else
- {
- $select = $label . " AS label ";
- $groupBy = 'label';
- }
-
- if(!empty($where))
- {
- $where = sprintf($where, "log_link_visit_action", "log_link_visit_action");
- $where = ' AND '.$where;
- }
-
- $pre = ", \n\t\t\t";
- if (!$metrics || in_array(Piwik_Archive::INDEX_NB_VISITS, $metrics))
- $select .= $pre . "count(distinct log_link_visit_action.idvisit) as `". Piwik_Archive::INDEX_NB_VISITS ."`";
- if (!$metrics || in_array(Piwik_Archive::INDEX_NB_UNIQ_VISITORS, $metrics))
- $select .= $pre . "count(distinct log_link_visit_action.idvisitor) as `". Piwik_Archive::INDEX_NB_UNIQ_VISITORS ."`";
- if (!$metrics || in_array(Piwik_Archive::INDEX_NB_ACTIONS, $metrics))
- $select .= $pre . "count(*) as `". Piwik_Archive::INDEX_NB_ACTIONS ."`";
-
- $from = "log_link_visit_action";
-
- if ($joinLogActionOnColumn !== false)
- {
- $multiJoin = is_array($joinLogActionOnColumn);
- if (!$multiJoin)
- {
- $joinLogActionOnColumn = array($joinLogActionOnColumn);
- }
-
- $from = array($from);
-
- foreach ($joinLogActionOnColumn as $i => $joinColumn)
- {
- $tableAlias = 'log_action'.($multiJoin ? $i + 1 : '');
- if (strpos($joinColumn, ' ') === false) {
- $joinOn = $tableAlias.'.idaction = log_link_visit_action.'.$joinColumn;
- } else {
- // more complex join column like IF(...)
- $joinOn = $tableAlias.'.idaction = '.$joinColumn;
- }
- $from[] = array(
- 'table' => 'log_action',
- 'tableAlias' => $tableAlias,
- 'joinOn' => $joinOn
- );
- }
- }
-
- if ($addSelect !== false)
- {
- $select .= ', '.$addSelect;
- }
-
- $where = "log_link_visit_action.server_time >= ?
+
+ $groupBy = false;
+ if ($groupByCols and !empty($groupByCols)) {
+ $groupBy = implode(',', $groupByCols);
+ }
+
+ $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
+
+ $query = $this->getSegment()->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy);
+
+ if ($oneResultRow) {
+ return $this->db->fetchRow($query['sql'], $query['bind']);
+ } else {
+ return $this->db->fetchAll($query['sql'], $query['bind']);
+ }
+ }
+
+ /**
+ * Helper function that returns a DataTable containing the $select fields / value pairs.
+ * IMPORTANT: The $select must return only one row!!
+ *
+ * Example $select = "count(distinct( config_os )) as countDistinctOs,
+ * sum( config_flash ) / count(distinct(idvisit)) as percentFlash "
+ * $labelCount = "test_column_name"
+ * will return a dataTable that looks like
+ * label test_column_name
+ * CountDistinctOs 9
+ * PercentFlash 0.5676
+ *
+ *
+ * @param string $select
+ * @param string $labelCount
+ * @return Piwik_DataTable
+ */
+ public function getSimpleDataTableFromSelect($select, $labelCount)
+ {
+ $data = $this->queryVisitsSimple($select);
+ return $this->getSimpleDataTableFromRow($data, $labelCount);
+ }
+
+ /**
+ * Returns the actions by the given dimension
+ *
+ * - The basic use case is to use $label and optionally $where.
+ * - If you want to apply a limit and group the others, use $orderBy to sort the way you
+ * want the limit to be applied and pass a pre-configured instance of Piwik_RankingQuery.
+ * The ranking query instance has to have a limit and at least one label column.
+ * See Piwik_RankingQuery::setLimit() and Piwik_RankingQuery::addLabelColumn().
+ * If $rankingQuery is set, the return value is the array returned by
+ * Piwik_RankingQuery::execute().
+ * - By default, the method only queries log_link_visit_action. If you need data from
+ * log_action (e.g. to partition the result from the ranking query into the different
+ * action types), use $joinLogActionOnColumn and $addSelect to join log_action and select
+ * the column you need from log_action.
+ *
+ *
+ * @param array|string $label the dimensions(s) you're interested in
+ * @param string $where where clause
+ * @param bool|array $metrics Set this if you want to limit the columns that are returned.
+ * The possible values in the array are Piwik_Archive::INDEX_*.
+ * @param bool|string $orderBy order by clause
+ * @param Piwik_RankingQuery $rankingQuery pre-configured ranking query instance
+ * @param bool|string $joinLogActionOnColumn column from log_link_visit_action that
+ * log_action should be joined on.
+ * can be an array to join multiple times.
+ * @param bool|string $addSelect additional select clause
+ * @return mixed
+ */
+ public function queryActionsByDimension($label, $where = '', $metrics = false, $orderBy = false,
+ $rankingQuery = null, $joinLogActionOnColumn = false, $addSelect = false)
+ {
+ if (is_array($label)) {
+ $label2 = $label;
+ foreach ($label2 as &$field) {
+ $field = 'log_link_visit_action.' . $field;
+ }
+ $groupBy = implode(", ", $label2);
+ foreach ($label2 as $id => &$field) {
+ $field = "$field AS " . $label[$id];
+ }
+ $select = implode(", ", $label2);
+
+ // IF we query Custom Variables scope "page" either: Product SKU, Product Name,
+ // then we also query the "Product page view" price which was possibly recorded.
+ if (in_array(reset($label), array('custom_var_k3', 'custom_var_k4', 'custom_var_k5'))) {
+ $select .= ", " . self::getSqlRevenue("AVG(log_link_visit_action.custom_var_v2)") . " as `" . Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED . "`";
+ }
+ } else {
+ $select = $label . " AS label ";
+ $groupBy = 'label';
+ }
+
+ if (!empty($where)) {
+ $where = sprintf($where, "log_link_visit_action", "log_link_visit_action");
+ $where = ' AND ' . $where;
+ }
+
+ $pre = ", \n\t\t\t";
+ if (!$metrics || in_array(Piwik_Archive::INDEX_NB_VISITS, $metrics))
+ $select .= $pre . "count(distinct log_link_visit_action.idvisit) as `" . Piwik_Archive::INDEX_NB_VISITS . "`";
+ if (!$metrics || in_array(Piwik_Archive::INDEX_NB_UNIQ_VISITORS, $metrics))
+ $select .= $pre . "count(distinct log_link_visit_action.idvisitor) as `" . Piwik_Archive::INDEX_NB_UNIQ_VISITORS . "`";
+ if (!$metrics || in_array(Piwik_Archive::INDEX_NB_ACTIONS, $metrics))
+ $select .= $pre . "count(*) as `" . Piwik_Archive::INDEX_NB_ACTIONS . "`";
+
+ $from = "log_link_visit_action";
+
+ if ($joinLogActionOnColumn !== false) {
+ $multiJoin = is_array($joinLogActionOnColumn);
+ if (!$multiJoin) {
+ $joinLogActionOnColumn = array($joinLogActionOnColumn);
+ }
+
+ $from = array($from);
+
+ foreach ($joinLogActionOnColumn as $i => $joinColumn) {
+ $tableAlias = 'log_action' . ($multiJoin ? $i + 1 : '');
+ if (strpos($joinColumn, ' ') === false) {
+ $joinOn = $tableAlias . '.idaction = log_link_visit_action.' . $joinColumn;
+ } else {
+ // more complex join column like IF(...)
+ $joinOn = $tableAlias . '.idaction = ' . $joinColumn;
+ }
+ $from[] = array(
+ 'table' => 'log_action',
+ 'tableAlias' => $tableAlias,
+ 'joinOn' => $joinOn
+ );
+ }
+ }
+
+ if ($addSelect !== false) {
+ $select .= ', ' . $addSelect;
+ }
+
+ $where = "log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?
$where";
- $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
-
- $query = $this->getSegment()->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy);
-
- if ($rankingQuery !== null)
- {
- $sumColumns = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_NB_VISITS,
- Piwik_Archive::INDEX_NB_ACTIONS
- );
- if ($metrics)
- {
- foreach ($sumColumns as $i => $column)
- {
- if (!in_array($column, $metrics))
- {
- unset($sumColumns[$i]);
- }
- }
- $sumColumns = array_values($sumColumns);
- }
- $rankingQuery->addColumn($sumColumns, 'sum');
- return $rankingQuery->execute($query['sql'], $query['bind']);
- }
-
- return $this->db->query($query['sql'], $query['bind']);
- }
-
- /**
- * Query visits by dimension
- *
- * @param array|string $label Can be a string, eg. "referer_name", will be aliased as 'label' in the returned rows
- * Can also be an array of strings, when the dimension spans multiple fields,
- * eg. array("referer_name", "referer_keyword")
- * @param string $where Additional condition for WHERE clause
- * @param bool|array $metrics Set this if you want to limit the columns that are returned.
- * The possible values in the array are Piwik_Archive::INDEX_*.
- * @param bool|string $orderBy ORDER BY clause. This is needed in combination with $rankingQuery.
- * @param Piwik_RankingQuery $rankingQuery
- * A pre-configured ranking query instance that is used to limit the result.
- * If set, the return value is the array returned by Piwik_RankingQuery::execute().
- * @param string $addSelect Additional SELECT clause
- * @param bool $addSelectGeneratesLabelColumn
- * Set to true if the $label column is generated in $addSelect.
- * @return mixed
- */
- public function queryVisitsByDimension($label, $where = '', $metrics = false, $orderBy = false,
- $rankingQuery = null, $addSelect = false, $addSelectGeneratesLabelColumn = false)
- {
- if(is_array($label))
- {
- $groupBy = "log_visit.".implode(", log_visit.", $label);
- foreach($label as &$field)
- {
- $field = 'log_visit.'.$field.' AS '.$field;
- }
- $select = implode(", ", $label);
- }
- else if ($addSelectGeneratesLabelColumn)
- {
- $select = $addSelect;
- $groupBy = $label;
- }
- else
- {
- $select = $label . " AS label ";
- $groupBy = 'label';
- }
-
- if(!empty($where))
- {
- $where = sprintf($where, "log_visit", "log_visit");
- $where = ' AND '.$where;
- }
-
- $pre = ", \n\t\t\t";
- if (!$metrics || in_array(Piwik_Archive::INDEX_NB_UNIQ_VISITORS, $metrics))
- $select .= $pre . "count(distinct log_visit.idvisitor) as `". Piwik_Archive::INDEX_NB_UNIQ_VISITORS ."`";
- if (!$metrics || in_array(Piwik_Archive::INDEX_NB_VISITS, $metrics))
- $select .= $pre . "count(*) as `". Piwik_Archive::INDEX_NB_VISITS ."`";
- if (!$metrics || in_array(Piwik_Archive::INDEX_NB_ACTIONS, $metrics))
- $select .= $pre . "sum(log_visit.visit_total_actions) as `". Piwik_Archive::INDEX_NB_ACTIONS ."`";
- if (!$metrics || in_array(Piwik_Archive::INDEX_MAX_ACTIONS, $metrics))
- $select .= $pre . "max(log_visit.visit_total_actions) as `". Piwik_Archive::INDEX_MAX_ACTIONS ."`";
- if (!$metrics || in_array(Piwik_Archive::INDEX_SUM_VISIT_LENGTH, $metrics))
- $select .= $pre . "sum(log_visit.visit_total_time) as `". Piwik_Archive::INDEX_SUM_VISIT_LENGTH ."`";
- if (!$metrics || in_array(Piwik_Archive::INDEX_BOUNCE_COUNT, $metrics))
- $select .= $pre . "sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `". Piwik_Archive::INDEX_BOUNCE_COUNT ."`";
- if (!$metrics || in_array(Piwik_Archive::INDEX_NB_VISITS_CONVERTED, $metrics))
- $select .= $pre . "sum(case log_visit.visit_goal_converted when 1 then 1 else 0 end) as `". Piwik_Archive::INDEX_NB_VISITS_CONVERTED ."`";
-
- if ($addSelect && !$addSelectGeneratesLabelColumn) {
- $select .= ', '.$addSelect;
- }
-
- $from = "log_visit";
-
- $where = "log_visit.visit_last_action_time >= ?
+ $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
+
+ $query = $this->getSegment()->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy);
+
+ if ($rankingQuery !== null) {
+ $sumColumns = array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_NB_VISITS,
+ Piwik_Archive::INDEX_NB_ACTIONS
+ );
+ if ($metrics) {
+ foreach ($sumColumns as $i => $column) {
+ if (!in_array($column, $metrics)) {
+ unset($sumColumns[$i]);
+ }
+ }
+ $sumColumns = array_values($sumColumns);
+ }
+ $rankingQuery->addColumn($sumColumns, 'sum');
+ return $rankingQuery->execute($query['sql'], $query['bind']);
+ }
+
+ return $this->db->query($query['sql'], $query['bind']);
+ }
+
+ /**
+ * Query visits by dimension
+ *
+ * @param array|string $label Can be a string, eg. "referer_name", will be aliased as 'label' in the returned rows
+ * Can also be an array of strings, when the dimension spans multiple fields,
+ * eg. array("referer_name", "referer_keyword")
+ * @param string $where Additional condition for WHERE clause
+ * @param bool|array $metrics Set this if you want to limit the columns that are returned.
+ * The possible values in the array are Piwik_Archive::INDEX_*.
+ * @param bool|string $orderBy ORDER BY clause. This is needed in combination with $rankingQuery.
+ * @param Piwik_RankingQuery $rankingQuery
+ * A pre-configured ranking query instance that is used to limit the result.
+ * If set, the return value is the array returned by Piwik_RankingQuery::execute().
+ * @param string $addSelect Additional SELECT clause
+ * @param bool $addSelectGeneratesLabelColumn
+ * Set to true if the $label column is generated in $addSelect.
+ * @return mixed
+ */
+ public function queryVisitsByDimension($label, $where = '', $metrics = false, $orderBy = false,
+ $rankingQuery = null, $addSelect = false, $addSelectGeneratesLabelColumn = false)
+ {
+ if (is_array($label)) {
+ $groupBy = "log_visit." . implode(", log_visit.", $label);
+ foreach ($label as &$field) {
+ $field = 'log_visit.' . $field . ' AS ' . $field;
+ }
+ $select = implode(", ", $label);
+ } else if ($addSelectGeneratesLabelColumn) {
+ $select = $addSelect;
+ $groupBy = $label;
+ } else {
+ $select = $label . " AS label ";
+ $groupBy = 'label';
+ }
+
+ if (!empty($where)) {
+ $where = sprintf($where, "log_visit", "log_visit");
+ $where = ' AND ' . $where;
+ }
+
+ $pre = ", \n\t\t\t";
+ if (!$metrics || in_array(Piwik_Archive::INDEX_NB_UNIQ_VISITORS, $metrics))
+ $select .= $pre . "count(distinct log_visit.idvisitor) as `" . Piwik_Archive::INDEX_NB_UNIQ_VISITORS . "`";
+ if (!$metrics || in_array(Piwik_Archive::INDEX_NB_VISITS, $metrics))
+ $select .= $pre . "count(*) as `" . Piwik_Archive::INDEX_NB_VISITS . "`";
+ if (!$metrics || in_array(Piwik_Archive::INDEX_NB_ACTIONS, $metrics))
+ $select .= $pre . "sum(log_visit.visit_total_actions) as `" . Piwik_Archive::INDEX_NB_ACTIONS . "`";
+ if (!$metrics || in_array(Piwik_Archive::INDEX_MAX_ACTIONS, $metrics))
+ $select .= $pre . "max(log_visit.visit_total_actions) as `" . Piwik_Archive::INDEX_MAX_ACTIONS . "`";
+ if (!$metrics || in_array(Piwik_Archive::INDEX_SUM_VISIT_LENGTH, $metrics))
+ $select .= $pre . "sum(log_visit.visit_total_time) as `" . Piwik_Archive::INDEX_SUM_VISIT_LENGTH . "`";
+ if (!$metrics || in_array(Piwik_Archive::INDEX_BOUNCE_COUNT, $metrics))
+ $select .= $pre . "sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `" . Piwik_Archive::INDEX_BOUNCE_COUNT . "`";
+ if (!$metrics || in_array(Piwik_Archive::INDEX_NB_VISITS_CONVERTED, $metrics))
+ $select .= $pre . "sum(case log_visit.visit_goal_converted when 1 then 1 else 0 end) as `" . Piwik_Archive::INDEX_NB_VISITS_CONVERTED . "`";
+
+ if ($addSelect && !$addSelectGeneratesLabelColumn) {
+ $select .= ', ' . $addSelect;
+ }
+
+ $from = "log_visit";
+
+ $where = "log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?
$where";
-
- $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
-
- $query = $this->getSegment()->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy);
-
- if ($rankingQuery !== null)
- {
- $sumColumns = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS, Piwik_Archive::INDEX_NB_VISITS,
- Piwik_Archive::INDEX_NB_ACTIONS, Piwik_Archive::INDEX_SUM_VISIT_LENGTH,
- Piwik_Archive::INDEX_BOUNCE_COUNT, Piwik_Archive::INDEX_NB_VISITS_CONVERTED
- );
- if ($metrics)
- {
- foreach ($sumColumns as $i => $column)
- {
- if (!in_array($column, $metrics))
- {
- unset($sumColumns[$i]);
- }
- }
- $sumColumns = array_values($sumColumns);
- }
- $rankingQuery->addColumn($sumColumns, 'sum');
- if (!$metrics || in_array(Piwik_Archive::INDEX_MAX_ACTIONS, $metrics))
- {
- $rankingQuery->addColumn(Piwik_Archive::INDEX_MAX_ACTIONS, 'max');
- }
- return $rankingQuery->execute($query['sql'], $query['bind']);
- }
-
- return $this->db->query($query['sql'], $query['bind']);
- }
-
- /**
- * @see queryVisitsByDimension() Similar to this function,
- * but queries metrics for the requested dimensions,
- * for each Goal conversion
- *
- * @param string|array $label
- * @param string $where
- * @param array $aggregateLabels
- * @return
- */
- public function queryConversionsByDimension($label, $where = '', $aggregateLabels = array())
- {
- if(empty($label))
- {
- $select = "";
- $groupBy = "";
- }
- elseif(is_array($label))
- {
- $groupBy = "log_conversion.".implode(", log_conversion.", $label);
- foreach($label as &$field)
- {
- $field = 'log_conversion.'.$field.' AS '.$field ;
- }
- $select = implode(", ", $label) . ", ";
- }
- else
- {
- $select = $label . " AS label, ";
- $groupBy = 'label';
- }
- if(!empty($aggregateLabels))
- {
- $select .= implode(", ", $aggregateLabels) . ", ";
- }
- if(!empty($where))
- {
- $where = sprintf($where, "log_conversion", "log_conversion");
- $where = ' AND '.$where;
- }
-
- $select .= self::getSqlRevenue('SUM(log_conversion.revenue_subtotal)')." as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL ."`,".
- self::getSqlRevenue('SUM(log_conversion.revenue_tax)')." as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX ."`,".
- self::getSqlRevenue('SUM(log_conversion.revenue_shipping)')." as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING ."`,".
- self::getSqlRevenue('SUM(log_conversion.revenue_discount)')." as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT ."`,".
- "SUM(log_conversion.items) as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS ."`, ";
-
- $groupBy = !empty($groupBy) ? ", $groupBy" : '';
-
- $select = "$select
+
+ $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
+
+ $query = $this->getSegment()->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy);
+
+ if ($rankingQuery !== null) {
+ $sumColumns = array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS, Piwik_Archive::INDEX_NB_VISITS,
+ Piwik_Archive::INDEX_NB_ACTIONS, Piwik_Archive::INDEX_SUM_VISIT_LENGTH,
+ Piwik_Archive::INDEX_BOUNCE_COUNT, Piwik_Archive::INDEX_NB_VISITS_CONVERTED
+ );
+ if ($metrics) {
+ foreach ($sumColumns as $i => $column) {
+ if (!in_array($column, $metrics)) {
+ unset($sumColumns[$i]);
+ }
+ }
+ $sumColumns = array_values($sumColumns);
+ }
+ $rankingQuery->addColumn($sumColumns, 'sum');
+ if (!$metrics || in_array(Piwik_Archive::INDEX_MAX_ACTIONS, $metrics)) {
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_MAX_ACTIONS, 'max');
+ }
+ return $rankingQuery->execute($query['sql'], $query['bind']);
+ }
+
+ return $this->db->query($query['sql'], $query['bind']);
+ }
+
+ /**
+ * @see queryVisitsByDimension() Similar to this function,
+ * but queries metrics for the requested dimensions,
+ * for each Goal conversion
+ *
+ * @param string|array $label
+ * @param string $where
+ * @param array $aggregateLabels
+ * @return
+ */
+ public function queryConversionsByDimension($label, $where = '', $aggregateLabels = array())
+ {
+ if (empty($label)) {
+ $select = "";
+ $groupBy = "";
+ } elseif (is_array($label)) {
+ $groupBy = "log_conversion." . implode(", log_conversion.", $label);
+ foreach ($label as &$field) {
+ $field = 'log_conversion.' . $field . ' AS ' . $field;
+ }
+ $select = implode(", ", $label) . ", ";
+ } else {
+ $select = $label . " AS label, ";
+ $groupBy = 'label';
+ }
+ if (!empty($aggregateLabels)) {
+ $select .= implode(", ", $aggregateLabels) . ", ";
+ }
+ if (!empty($where)) {
+ $where = sprintf($where, "log_conversion", "log_conversion");
+ $where = ' AND ' . $where;
+ }
+
+ $select .= self::getSqlRevenue('SUM(log_conversion.revenue_subtotal)') . " as `" . Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL . "`," .
+ self::getSqlRevenue('SUM(log_conversion.revenue_tax)') . " as `" . Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX . "`," .
+ self::getSqlRevenue('SUM(log_conversion.revenue_shipping)') . " as `" . Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING . "`," .
+ self::getSqlRevenue('SUM(log_conversion.revenue_discount)') . " as `" . Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT . "`," .
+ "SUM(log_conversion.items) as `" . Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS . "`, ";
+
+ $groupBy = !empty($groupBy) ? ", $groupBy" : '';
+
+ $select = "$select
log_conversion.idgoal,
- count(*) as `". Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS ."`,
- ".self::getSqlRevenue('SUM(log_conversion.revenue)')." as `". Piwik_Archive::INDEX_GOAL_REVENUE ."`,
- count(distinct log_conversion.idvisit) as `". Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED."`";
-
- $from = "log_conversion";
-
- $where = "log_conversion.server_time >= ?
+ count(*) as `" . Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS . "`,
+ " . self::getSqlRevenue('SUM(log_conversion.revenue)') . " as `" . Piwik_Archive::INDEX_GOAL_REVENUE . "`,
+ count(distinct log_conversion.idvisit) as `" . Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED . "`";
+
+ $from = "log_conversion";
+
+ $where = "log_conversion.server_time >= ?
AND log_conversion.server_time <= ?
AND log_conversion.idsite = ?
$where";
-
- $groupBy = "log_conversion.idgoal $groupBy";
-
- $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
-
- $query = $this->getSegment()->getSelectQuery($select, $from, $where, $bind, $orderBy=false, $groupBy);
-
+
+ $groupBy = "log_conversion.idgoal $groupBy";
+
+ $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
+
+ $query = $this->getSegment()->getSelectQuery($select, $from, $where, $bind, $orderBy = false, $groupBy);
+
return $this->db->query($query['sql'], $query['bind']);
- }
-
- /**
- * Returns the ecommerce items
- *
- * @param string $field
- * @return string
- */
- public function queryEcommerceItems($field)
- {
- $query = "SELECT
+ }
+
+ /**
+ * Returns the ecommerce items
+ *
+ * @param string $field
+ * @return string
+ */
+ public function queryEcommerceItems($field)
+ {
+ $query = "SELECT
name as label,
- ".self::getSqlRevenue('SUM(quantity * price)')." as `". Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE ."`,
- ".self::getSqlRevenue('SUM(quantity)')." as `". Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY ."`,
- ".self::getSqlRevenue('SUM(price)')." as `". Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE ."`,
- count(distinct idorder) as `". Piwik_Archive::INDEX_ECOMMERCE_ORDERS."`,
- count(idvisit) as `". Piwik_Archive::INDEX_NB_VISITS."`,
- case idorder when '0' then ".Piwik_Tracker_GoalManager::IDGOAL_CART." else ".Piwik_Tracker_GoalManager::IDGOAL_ORDER." end as ecommerceType
- FROM ".Piwik_Common::prefixTable('log_conversion_item')."
- LEFT JOIN ".Piwik_Common::prefixTable('log_action')."
+ " . self::getSqlRevenue('SUM(quantity * price)') . " as `" . Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE . "`,
+ " . self::getSqlRevenue('SUM(quantity)') . " as `" . Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY . "`,
+ " . self::getSqlRevenue('SUM(price)') . " as `" . Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE . "`,
+ count(distinct idorder) as `" . Piwik_Archive::INDEX_ECOMMERCE_ORDERS . "`,
+ count(idvisit) as `" . Piwik_Archive::INDEX_NB_VISITS . "`,
+ case idorder when '0' then " . Piwik_Tracker_GoalManager::IDGOAL_CART . " else " . Piwik_Tracker_GoalManager::IDGOAL_ORDER . " end as ecommerceType
+ FROM " . Piwik_Common::prefixTable('log_conversion_item') . "
+ LEFT JOIN " . Piwik_Common::prefixTable('log_action') . "
ON $field = idaction
WHERE server_time >= ?
AND server_time <= ?
@@ -633,433 +588,406 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing
AND deleted = 0
GROUP BY ecommerceType, $field
ORDER BY NULL";
-
- $bind = array( $this->getStartDatetimeUTC(),
- $this->getEndDatetimeUTC(),
- $this->idsite
+
+ $bind = array($this->getStartDatetimeUTC(),
+ $this->getEndDatetimeUTC(),
+ $this->idsite
+ );
+ $query = $this->db->query($query, $bind);
+ return $query;
+ }
+
+ /**
+ * @param string $field
+ * @return string
+ */
+ static public function getSqlRevenue($field)
+ {
+ return "ROUND(" . $field . "," . Piwik_Tracker_GoalManager::REVENUE_PRECISION . ")";
+ }
+
+ /**
+ * Converts the given array to a datatable
+ * @param array $array
+ * @return Piwik_DataTable
+ */
+ static public function getDataTableFromArray($array)
+ {
+ $table = new Piwik_DataTable();
+ $table->addRowsFromArrayWithIndexLabel($array);
+ return $table;
+ }
+
+ /**
+ * Output:
+ * array(
+ * LABEL => array(
+ * Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
+ * Piwik_Archive::INDEX_NB_VISITS => 0
+ * ),
+ * LABEL2 => array(
+ * [...]
+ * )
+ * )
+ *
+ * Helper function that returns an array with common statistics for a given database field distinct values.
+ *
+ * The statistics returned are:
+ * - number of unique visitors
+ * - number of visits
+ * - number of actions
+ * - maximum number of action for a visit
+ * - sum of the visits' length in sec
+ * - count of bouncing visits (visits with one page view)
+ *
+ * For example if $label = 'config_os' it will return the statistics for every distinct Operating systems
+ * The returned array will have a row per distinct operating systems,
+ * and a column per stat (nb of visits, max actions, etc)
+ *
+ * 'label' Piwik_Archive::INDEX_NB_UNIQ_VISITORS Piwik_Archive::INDEX_NB_VISITS etc.
+ * Linux 27 66 ...
+ * Windows XP 12 ...
+ * Mac OS 15 36 ...
+ *
+ * @param string $label Table log_visit field name to be use to compute common stats
+ * @return array
+ */
+ public function getArrayInterestForLabel($label)
+ {
+ $query = $this->queryVisitsByDimension($label);
+ $interest = array();
+ while ($row = $query->fetch()) {
+ if (!isset($interest[$row['label']])) $interest[$row['label']] = $this->getNewInterestRow();
+ $this->updateInterestStats($row, $interest[$row['label']]);
+ }
+ return $interest;
+ }
+
+ /**
+ * Generates a dataTable given a multidimensional PHP array that associates LABELS to Piwik_DataTableRows
+ * This is used for the "Actions" DataTable, where a line is the aggregate of all the subtables
+ * Example: the category /blog has 3 visits because it has /blog/index (2 visits) + /blog/about (1 visit)
+ *
+ * @param array $table
+ * @param array $parents
+ * @return Piwik_DataTable
+ */
+ static public function generateDataTable($table, $parents = array())
+ {
+ $dataTableToReturn = new Piwik_DataTable();
+ foreach ($table as $label => $maybeDatatableRow) {
+ // case the aInfo is a subtable-like array
+ // it means that we have to go recursively and process it
+ // then we build the row that is an aggregate of all the children
+ // and we associate this row to the subtable
+ if (!($maybeDatatableRow instanceof Piwik_DataTable_Row)) {
+ array_push($parents, array($dataTableToReturn->getId(), $label));
+
+ $subTable = self::generateDataTable($maybeDatatableRow, $parents);
+ $subTable->setParents($parents);
+ $row = new Piwik_DataTable_Row_DataTableSummary($subTable);
+ $row->setColumns(array('label' => $label) + $row->getColumns());
+ $row->addSubtable($subTable);
+
+ array_pop($parents);
+ } // if aInfo is a simple Row we build it
+ else {
+ $row = $maybeDatatableRow;
+ }
+
+ if ($row->getMetadata('issummaryrow') == true) {
+ $row->deleteMetadata('issummaryrow');
+ $dataTableToReturn->addSummaryRow($row);
+ } else {
+ $dataTableToReturn->addRow($row);
+ }
+ }
+ return $dataTableToReturn;
+ }
+
+ /**
+ * Helper function that returns the serialized DataTable of the given PHP array.
+ * The array must have the format of Piwik_DataTable::addRowsFromArrayWithIndexLabel()
+ * Example: array (
+ * LABEL => array(col1 => X, col2 => Y),
+ * LABEL2 => array(col1 => X, col2 => Y),
+ * )
+ *
+ * @param array $array at the given format
+ * @return array Array with one element: the serialized data table string
+ */
+ public function getDataTableSerialized($array)
+ {
+ $table = new Piwik_DataTable();
+ $table->addRowsFromArrayWithIndexLabel($array);
+ $toReturn = $table->getSerialized();
+ return $toReturn;
+ }
+
+
+ /**
+ * Helper function that returns the multiple serialized DataTable of the given PHP array.
+ * The DataTable here associates a subtable to every row of the level 0 array.
+ * This is used for example for search engines.
+ * Every search engine (level 0) has a subtable containing the keywords.
+ *
+ * The $arrayLevel0 must have the format
+ * Example: array (
+ * // Yahoo.com => array( kwd1 => stats, kwd2 => stats )
+ * LABEL => array(col1 => X, col2 => Y),
+ * LABEL2 => array(col1 => X, col2 => Y),
+ * )
+ *
+ * The $subArrayLevel1ByKey must have the format
+ * Example: array(
+ * // Yahoo.com => array( stats )
+ * LABEL => #Piwik_DataTable_ForLABEL,
+ * LABEL2 => #Piwik_DataTable_ForLABEL2,
+ * )
+ *
+ *
+ * @param array $arrayLevel0
+ * @param array $subArrayLevel1ByKey Array of Piwik_DataTable
+ * @return array Array with N elements: the strings of the datatable serialized
+ */
+ public function getDataTableWithSubtablesFromArraysIndexedByLabel($arrayLevel0, $subArrayLevel1ByKey)
+ {
+ $parentTableLevel0 = new Piwik_DataTable();
+
+ $tablesByLabel = array();
+ foreach ($arrayLevel0 as $label => $aAllRowsForThisLabel) {
+ $table = new Piwik_DataTable();
+ $table->addRowsFromArrayWithIndexLabel($aAllRowsForThisLabel);
+ $tablesByLabel[$label] = $table;
+ }
+ $parentTableLevel0->addRowsFromArrayWithIndexLabel($subArrayLevel1ByKey, $tablesByLabel);
+
+ return $parentTableLevel0;
+ }
+
+ /**
+ * Returns an empty row containing default values for the common stat
+ *
+ * @param bool $onlyMetricsAvailableInActionsTable
+ * @param bool $doNotSumVisits
+ * @return array
+ */
+ public function getNewInterestRow($onlyMetricsAvailableInActionsTable = false, $doNotSumVisits = false)
+ {
+ if ($onlyMetricsAvailableInActionsTable) {
+ if ($doNotSumVisits) {
+ return array(Piwik_Archive::INDEX_NB_ACTIONS => 0);
+ }
+ return array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
+ Piwik_Archive::INDEX_NB_VISITS => 0,
+ Piwik_Archive::INDEX_NB_ACTIONS => 0);
+ }
+ return array(Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
+ Piwik_Archive::INDEX_NB_VISITS => 0,
+ Piwik_Archive::INDEX_NB_ACTIONS => 0,
+ Piwik_Archive::INDEX_MAX_ACTIONS => 0,
+ Piwik_Archive::INDEX_SUM_VISIT_LENGTH => 0,
+ Piwik_Archive::INDEX_BOUNCE_COUNT => 0,
+ Piwik_Archive::INDEX_NB_VISITS_CONVERTED => 0,
+ );
+ }
+
+
+ /**
+ * Returns a Piwik_DataTable_Row containing default values for common stat,
+ * plus a column 'label' with the value $label
+ *
+ * @param string $label
+ * @return Piwik_DataTable_Row
+ */
+ public function getNewInterestRowLabeled($label)
+ {
+ return new Piwik_DataTable_Row(
+ array(
+ Piwik_DataTable_Row::COLUMNS => array('label' => $label)
+ + $this->getNewInterestRow()
+ )
+ );
+ }
+
+ /**
+ * Adds the given row $newRowToAdd to the existing $oldRowToUpdate passed by reference
+ *
+ * The rows are php arrays Name => value
+ *
+ * @param array $newRowToAdd
+ * @param array $oldRowToUpdate
+ * @param bool $onlyMetricsAvailableInActionsTable
+ * @param bool $doNotSumVisits
+ * @return
+ */
+ public function updateInterestStats($newRowToAdd, &$oldRowToUpdate, $onlyMetricsAvailableInActionsTable = false, $doNotSumVisits = false)
+ {
+ // Pre 1.2 format: string indexed rows are returned from the DB
+ // Left here for Backward compatibility with plugins doing custom SQL queries using these metrics as string
+ if (!isset($newRowToAdd[Piwik_Archive::INDEX_NB_VISITS])) {
+ if (!$doNotSumVisits) {
+ $oldRowToUpdate[Piwik_Archive::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd['nb_uniq_visitors'];
+ $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS] += $newRowToAdd['nb_visits'];
+ }
+ $oldRowToUpdate[Piwik_Archive::INDEX_NB_ACTIONS] += $newRowToAdd['nb_actions'];
+ if ($onlyMetricsAvailableInActionsTable) {
+ return;
+ }
+ $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS] = (float)max($newRowToAdd['max_actions'], $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS]);
+ $oldRowToUpdate[Piwik_Archive::INDEX_SUM_VISIT_LENGTH] += $newRowToAdd['sum_visit_length'];
+ $oldRowToUpdate[Piwik_Archive::INDEX_BOUNCE_COUNT] += $newRowToAdd['bounce_count'];
+ $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS_CONVERTED] += $newRowToAdd['nb_visits_converted'];
+ return;
+ }
+ if (!$doNotSumVisits) {
+ $oldRowToUpdate[Piwik_Archive::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd[Piwik_Archive::INDEX_NB_UNIQ_VISITORS];
+ $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS] += $newRowToAdd[Piwik_Archive::INDEX_NB_VISITS];
+ }
+ $oldRowToUpdate[Piwik_Archive::INDEX_NB_ACTIONS] += $newRowToAdd[Piwik_Archive::INDEX_NB_ACTIONS];
+
+ // Hack for Price tracking on Ecommerce product/category pages
+ // The price is not summed, but AVG is taken in the SQL query
+ $index = Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED;
+ if (!empty($newRowToAdd[$index])) {
+ $oldRowToUpdate[$index] = (float)$newRowToAdd[$index];
+ }
+
+ if ($onlyMetricsAvailableInActionsTable) {
+ return;
+ }
+ $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS] = (float)max($newRowToAdd[Piwik_Archive::INDEX_MAX_ACTIONS], $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS]);
+ $oldRowToUpdate[Piwik_Archive::INDEX_SUM_VISIT_LENGTH] += $newRowToAdd[Piwik_Archive::INDEX_SUM_VISIT_LENGTH];
+ $oldRowToUpdate[Piwik_Archive::INDEX_BOUNCE_COUNT] += $newRowToAdd[Piwik_Archive::INDEX_BOUNCE_COUNT];
+ $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS_CONVERTED] += $newRowToAdd[Piwik_Archive::INDEX_NB_VISITS_CONVERTED];
+
+ }
+
+ /**
+ * Given an array of stats, it will process the sum of goal conversions
+ * and sum of revenue and add it in the stats array in two new fields.
+ *
+ * @param array $interestByLabel Passed by reference, it will be modified as follows:
+ * Input:
+ * array(
+ * LABEL => array( Piwik_Archive::INDEX_NB_VISITS => X,
+ * Piwik_Archive::INDEX_GOALS => array(
+ * idgoal1 => array( [...] ),
+ * idgoal2 => array( [...] ),
+ * ),
+ * [...] ),
+ * LABEL2 => array( Piwik_Archive::INDEX_NB_VISITS => Y, [...] )
+ * );
+ *
+ *
+ * Output:
+ * array(
+ * LABEL => array( Piwik_Archive::INDEX_NB_VISITS => X,
+ * Piwik_Archive::INDEX_NB_CONVERSIONS => Y, // sum of all conversions
+ * Piwik_Archive::INDEX_REVENUE => Z, // sum of all revenue
+ * Piwik_Archive::INDEX_GOALS => array(
+ * idgoal1 => array( [...] ),
+ * idgoal2 => array( [...] ),
+ * ),
+ * [...] ),
+ * LABEL2 => array( Piwik_Archive::INDEX_NB_VISITS => Y, [...] )
+ * );
+ * )
+ *
+ * @param array $interestByLabel Passed by reference, will be modified
+ */
+ function enrichConversionsByLabelArray(&$interestByLabel)
+ {
+ foreach ($interestByLabel as $label => &$values) {
+ if (isset($values[Piwik_Archive::INDEX_GOALS])) {
+ // When per goal metrics are processed, general 'visits converted' is not meaningful because
+ // it could differ from the sum of each goal conversions
+ unset($values[Piwik_Archive::INDEX_NB_VISITS_CONVERTED]);
+ $revenue = $conversions = 0;
+ foreach ($values[Piwik_Archive::INDEX_GOALS] as $idgoal => $goalValues) {
+ // Do not sum Cart revenue since it is a lost revenue
+ if ($idgoal >= Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
+ $revenue += $goalValues[Piwik_Archive::INDEX_GOAL_REVENUE];
+ $conversions += $goalValues[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS];
+ }
+ }
+ $values[Piwik_Archive::INDEX_NB_CONVERSIONS] = $conversions;
+
+ // 25.00 recorded as 25
+ if (round($revenue) == $revenue) {
+ $revenue = round($revenue);
+ }
+ $values[Piwik_Archive::INDEX_REVENUE] = $revenue;
+ }
+ }
+ }
+
+ /**
+ *
+ * @param array $interestByLabelAndSubLabel Passed by reference, will be modified
+ */
+ function enrichConversionsByLabelArrayHasTwoLevels(&$interestByLabelAndSubLabel)
+ {
+ foreach ($interestByLabelAndSubLabel as $mainLabel => &$interestBySubLabel) {
+ $this->enrichConversionsByLabelArray($interestBySubLabel);
+ }
+ }
+
+ /**
+ *
+ * @param $newRowToAdd
+ * @param $oldRowToUpdate
+ */
+ function updateGoalStats($newRowToAdd, &$oldRowToUpdate)
+ {
+ $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS];
+ $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED];
+ $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_REVENUE] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_REVENUE];
+
+ // Cart & Order
+ if (isset($oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS])) {
+ $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS];
+
+ // Order only
+ if (isset($oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL])) {
+ $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL];
+ $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX];
+ $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING];
+ $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT];
+ }
+ }
+ }
+
+ /**
+ *
+ * @param $idGoal
+ * @return array
+ */
+ function getNewGoalRow($idGoal)
+ {
+ if ($idGoal > Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
+ return array(Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0,
+ Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0,
+ Piwik_Archive::INDEX_GOAL_REVENUE => 0,
+ );
+ }
+ if ($idGoal == Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
+ return array(Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0,
+ Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0,
+ Piwik_Archive::INDEX_GOAL_REVENUE => 0,
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL => 0,
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX => 0,
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING => 0,
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT => 0,
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS => 0,
+ );
+ }
+ // $row['idgoal'] == Piwik_Tracker_GoalManager::IDGOAL_CART
+ return array(Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0,
+ Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0,
+ Piwik_Archive::INDEX_GOAL_REVENUE => 0,
+ Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS => 0,
);
- $query = $this->db->query($query, $bind);
- return $query;
- }
-
- /**
- * @param string $field
- * @return string
- */
- static public function getSqlRevenue($field)
- {
- return "ROUND(".$field.",".Piwik_Tracker_GoalManager::REVENUE_PRECISION.")";
- }
-
- /**
- * Converts the given array to a datatable
- * @param array $array
- * @return Piwik_DataTable
- */
- static public function getDataTableFromArray( $array )
- {
- $table = new Piwik_DataTable();
- $table->addRowsFromArrayWithIndexLabel($array);
- return $table;
- }
-
- /**
- * Output:
- * array(
- * LABEL => array(
- * Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
- * Piwik_Archive::INDEX_NB_VISITS => 0
- * ),
- * LABEL2 => array(
- * [...]
- * )
- * )
- *
- * Helper function that returns an array with common statistics for a given database field distinct values.
- *
- * The statistics returned are:
- * - number of unique visitors
- * - number of visits
- * - number of actions
- * - maximum number of action for a visit
- * - sum of the visits' length in sec
- * - count of bouncing visits (visits with one page view)
- *
- * For example if $label = 'config_os' it will return the statistics for every distinct Operating systems
- * The returned array will have a row per distinct operating systems,
- * and a column per stat (nb of visits, max actions, etc)
- *
- * 'label' Piwik_Archive::INDEX_NB_UNIQ_VISITORS Piwik_Archive::INDEX_NB_VISITS etc.
- * Linux 27 66 ...
- * Windows XP 12 ...
- * Mac OS 15 36 ...
- *
- * @param string $label Table log_visit field name to be use to compute common stats
- * @return array
- */
- public function getArrayInterestForLabel($label)
- {
- $query = $this->queryVisitsByDimension($label);
- $interest = array();
- while($row = $query->fetch())
- {
- if(!isset($interest[$row['label']])) $interest[$row['label']]= $this->getNewInterestRow();
- $this->updateInterestStats( $row, $interest[$row['label']]);
- }
- return $interest;
- }
-
- /**
- * Generates a dataTable given a multidimensional PHP array that associates LABELS to Piwik_DataTableRows
- * This is used for the "Actions" DataTable, where a line is the aggregate of all the subtables
- * Example: the category /blog has 3 visits because it has /blog/index (2 visits) + /blog/about (1 visit)
- *
- * @param array $table
- * @param array $parents
- * @return Piwik_DataTable
- */
- static public function generateDataTable( $table, $parents=array() )
- {
- $dataTableToReturn = new Piwik_DataTable();
- foreach($table as $label => $maybeDatatableRow)
- {
- // case the aInfo is a subtable-like array
- // it means that we have to go recursively and process it
- // then we build the row that is an aggregate of all the children
- // and we associate this row to the subtable
- if( !($maybeDatatableRow instanceof Piwik_DataTable_Row) )
- {
- array_push($parents, array($dataTableToReturn->getId(), $label));
-
- $subTable = self::generateDataTable($maybeDatatableRow, $parents);
- $subTable->setParents($parents);
- $row = new Piwik_DataTable_Row_DataTableSummary( $subTable );
- $row->setColumns( array('label' => $label) + $row->getColumns());
- $row->addSubtable($subTable);
-
- array_pop($parents);
- }
- // if aInfo is a simple Row we build it
- else
- {
- $row = $maybeDatatableRow;
- }
-
- if ($row->getMetadata('issummaryrow') == true)
- {
- $row->deleteMetadata('issummaryrow');
- $dataTableToReturn->addSummaryRow($row);
- }
- else
- {
- $dataTableToReturn->addRow($row);
- }
- }
- return $dataTableToReturn;
- }
-
- /**
- * Helper function that returns the serialized DataTable of the given PHP array.
- * The array must have the format of Piwik_DataTable::addRowsFromArrayWithIndexLabel()
- * Example: array (
- * LABEL => array(col1 => X, col2 => Y),
- * LABEL2 => array(col1 => X, col2 => Y),
- * )
- *
- * @param array $array at the given format
- * @return array Array with one element: the serialized data table string
- */
- public function getDataTableSerialized( $array )
- {
- $table = new Piwik_DataTable();
- $table->addRowsFromArrayWithIndexLabel($array );
- $toReturn = $table->getSerialized();
- return $toReturn;
- }
-
-
- /**
- * Helper function that returns the multiple serialized DataTable of the given PHP array.
- * The DataTable here associates a subtable to every row of the level 0 array.
- * This is used for example for search engines.
- * Every search engine (level 0) has a subtable containing the keywords.
- *
- * The $arrayLevel0 must have the format
- * Example: array (
- * // Yahoo.com => array( kwd1 => stats, kwd2 => stats )
- * LABEL => array(col1 => X, col2 => Y),
- * LABEL2 => array(col1 => X, col2 => Y),
- * )
- *
- * The $subArrayLevel1ByKey must have the format
- * Example: array(
- * // Yahoo.com => array( stats )
- * LABEL => #Piwik_DataTable_ForLABEL,
- * LABEL2 => #Piwik_DataTable_ForLABEL2,
- * )
- *
- *
- * @param array $arrayLevel0
- * @param array $subArrayLevel1ByKey Array of Piwik_DataTable
- * @return array Array with N elements: the strings of the datatable serialized
- */
- public function getDataTableWithSubtablesFromArraysIndexedByLabel( $arrayLevel0, $subArrayLevel1ByKey )
- {
- $parentTableLevel0 = new Piwik_DataTable();
-
- $tablesByLabel = array();
- foreach($arrayLevel0 as $label => $aAllRowsForThisLabel)
- {
- $table = new Piwik_DataTable();
- $table->addRowsFromArrayWithIndexLabel($aAllRowsForThisLabel);
- $tablesByLabel[$label] = $table;
- }
- $parentTableLevel0->addRowsFromArrayWithIndexLabel($subArrayLevel1ByKey, $tablesByLabel);
-
- return $parentTableLevel0;
- }
-
- /**
- * Returns an empty row containing default values for the common stat
- *
- * @param bool $onlyMetricsAvailableInActionsTable
- * @param bool $doNotSumVisits
- * @return array
- */
- public function getNewInterestRow($onlyMetricsAvailableInActionsTable = false, $doNotSumVisits = false)
- {
- if($onlyMetricsAvailableInActionsTable)
- {
- if($doNotSumVisits)
- {
- return array(Piwik_Archive::INDEX_NB_ACTIONS => 0 );
- }
- return array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
- Piwik_Archive::INDEX_NB_VISITS => 0,
- Piwik_Archive::INDEX_NB_ACTIONS => 0 );
- }
- return array( Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
- Piwik_Archive::INDEX_NB_VISITS => 0,
- Piwik_Archive::INDEX_NB_ACTIONS => 0,
- Piwik_Archive::INDEX_MAX_ACTIONS => 0,
- Piwik_Archive::INDEX_SUM_VISIT_LENGTH => 0,
- Piwik_Archive::INDEX_BOUNCE_COUNT => 0,
- Piwik_Archive::INDEX_NB_VISITS_CONVERTED=> 0,
- );
- }
-
-
- /**
- * Returns a Piwik_DataTable_Row containing default values for common stat,
- * plus a column 'label' with the value $label
- *
- * @param string $label
- * @return Piwik_DataTable_Row
- */
- public function getNewInterestRowLabeled( $label )
- {
- return new Piwik_DataTable_Row(
- array(
- Piwik_DataTable_Row::COLUMNS => array( 'label' => $label)
- + $this->getNewInterestRow()
- )
- );
- }
-
- /**
- * Adds the given row $newRowToAdd to the existing $oldRowToUpdate passed by reference
- *
- * The rows are php arrays Name => value
- *
- * @param array $newRowToAdd
- * @param array $oldRowToUpdate
- * @param bool $onlyMetricsAvailableInActionsTable
- * @param bool $doNotSumVisits
- * @return
- */
- public function updateInterestStats( $newRowToAdd, &$oldRowToUpdate, $onlyMetricsAvailableInActionsTable = false, $doNotSumVisits = false)
- {
- // Pre 1.2 format: string indexed rows are returned from the DB
- // Left here for Backward compatibility with plugins doing custom SQL queries using these metrics as string
- if(!isset($newRowToAdd[Piwik_Archive::INDEX_NB_VISITS]))
- {
- if(!$doNotSumVisits)
- {
- $oldRowToUpdate[Piwik_Archive::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd['nb_uniq_visitors'];
- $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS] += $newRowToAdd['nb_visits'];
- }
- $oldRowToUpdate[Piwik_Archive::INDEX_NB_ACTIONS] += $newRowToAdd['nb_actions'];
- if($onlyMetricsAvailableInActionsTable)
- {
- return;
- }
- $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS] = (float)max($newRowToAdd['max_actions'], $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS]);
- $oldRowToUpdate[Piwik_Archive::INDEX_SUM_VISIT_LENGTH] += $newRowToAdd['sum_visit_length'];
- $oldRowToUpdate[Piwik_Archive::INDEX_BOUNCE_COUNT] += $newRowToAdd['bounce_count'];
- $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS_CONVERTED] += $newRowToAdd['nb_visits_converted'];
- return;
- }
- if(!$doNotSumVisits)
- {
- $oldRowToUpdate[Piwik_Archive::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd[Piwik_Archive::INDEX_NB_UNIQ_VISITORS];
- $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS] += $newRowToAdd[Piwik_Archive::INDEX_NB_VISITS];
- }
- $oldRowToUpdate[Piwik_Archive::INDEX_NB_ACTIONS] += $newRowToAdd[Piwik_Archive::INDEX_NB_ACTIONS];
-
- // Hack for Price tracking on Ecommerce product/category pages
- // The price is not summed, but AVG is taken in the SQL query
- $index = Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED;
- if(!empty($newRowToAdd[$index]))
- {
- $oldRowToUpdate[$index] = (float)$newRowToAdd[$index];
- }
-
- if($onlyMetricsAvailableInActionsTable)
- {
- return;
- }
- $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS] = (float)max($newRowToAdd[Piwik_Archive::INDEX_MAX_ACTIONS], $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS]);
- $oldRowToUpdate[Piwik_Archive::INDEX_SUM_VISIT_LENGTH] += $newRowToAdd[Piwik_Archive::INDEX_SUM_VISIT_LENGTH];
- $oldRowToUpdate[Piwik_Archive::INDEX_BOUNCE_COUNT] += $newRowToAdd[Piwik_Archive::INDEX_BOUNCE_COUNT];
- $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS_CONVERTED] += $newRowToAdd[Piwik_Archive::INDEX_NB_VISITS_CONVERTED];
-
- }
-
- /**
- * Given an array of stats, it will process the sum of goal conversions
- * and sum of revenue and add it in the stats array in two new fields.
- *
- * @param array $interestByLabel Passed by reference, it will be modified as follows:
- * Input:
- * array(
- * LABEL => array( Piwik_Archive::INDEX_NB_VISITS => X,
- * Piwik_Archive::INDEX_GOALS => array(
- * idgoal1 => array( [...] ),
- * idgoal2 => array( [...] ),
- * ),
- * [...] ),
- * LABEL2 => array( Piwik_Archive::INDEX_NB_VISITS => Y, [...] )
- * );
- *
- *
- * Output:
- * array(
- * LABEL => array( Piwik_Archive::INDEX_NB_VISITS => X,
- * Piwik_Archive::INDEX_NB_CONVERSIONS => Y, // sum of all conversions
- * Piwik_Archive::INDEX_REVENUE => Z, // sum of all revenue
- * Piwik_Archive::INDEX_GOALS => array(
- * idgoal1 => array( [...] ),
- * idgoal2 => array( [...] ),
- * ),
- * [...] ),
- * LABEL2 => array( Piwik_Archive::INDEX_NB_VISITS => Y, [...] )
- * );
- * )
- *
- * @param array $interestByLabel Passed by reference, will be modified
- */
- function enrichConversionsByLabelArray(&$interestByLabel)
- {
- foreach($interestByLabel as $label => &$values)
- {
- if(isset($values[Piwik_Archive::INDEX_GOALS]))
- {
- // When per goal metrics are processed, general 'visits converted' is not meaningful because
- // it could differ from the sum of each goal conversions
- unset($values[Piwik_Archive::INDEX_NB_VISITS_CONVERTED]);
- $revenue = $conversions = 0;
- foreach($values[Piwik_Archive::INDEX_GOALS] as $idgoal => $goalValues)
- {
- // Do not sum Cart revenue since it is a lost revenue
- if($idgoal >= Piwik_Tracker_GoalManager::IDGOAL_ORDER)
- {
- $revenue += $goalValues[Piwik_Archive::INDEX_GOAL_REVENUE];
- $conversions += $goalValues[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS];
- }
- }
- $values[Piwik_Archive::INDEX_NB_CONVERSIONS] = $conversions;
-
- // 25.00 recorded as 25
- if(round($revenue) == $revenue)
- {
- $revenue = round($revenue);
- }
- $values[Piwik_Archive::INDEX_REVENUE] = $revenue;
- }
- }
- }
-
- /**
- *
- * @param array $interestByLabelAndSubLabel Passed by reference, will be modified
- */
- function enrichConversionsByLabelArrayHasTwoLevels(&$interestByLabelAndSubLabel)
- {
- foreach($interestByLabelAndSubLabel as $mainLabel => &$interestBySubLabel)
- {
- $this->enrichConversionsByLabelArray($interestBySubLabel);
- }
- }
-
- /**
- *
- * @param $newRowToAdd
- * @param $oldRowToUpdate
- */
- function updateGoalStats($newRowToAdd, &$oldRowToUpdate)
- {
- $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS];
- $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED];
- $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_REVENUE] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_REVENUE];
-
- // Cart & Order
- if(isset($oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS]))
- {
- $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS];
-
- // Order only
- if(isset($oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL]))
- {
- $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL];
- $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX];
- $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING];
- $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT];
- }
- }
- }
-
- /**
- *
- * @param $idGoal
- * @return array
- */
- function getNewGoalRow($idGoal)
- {
- if($idGoal > Piwik_Tracker_GoalManager::IDGOAL_ORDER)
- {
- return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0,
- Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0,
- Piwik_Archive::INDEX_GOAL_REVENUE => 0,
- );
- }
- if($idGoal == Piwik_Tracker_GoalManager::IDGOAL_ORDER)
- {
- return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0,
- Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0,
- Piwik_Archive::INDEX_GOAL_REVENUE => 0,
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL => 0,
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX => 0,
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING => 0,
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT => 0,
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS => 0,
- );
- }
- // $row['idgoal'] == Piwik_Tracker_GoalManager::IDGOAL_CART
- return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0,
- Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0,
- Piwik_Archive::INDEX_GOAL_REVENUE => 0,
- Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS => 0,
- );
- }
+ }
}
diff --git a/core/ArchiveProcessing/Period.php b/core/ArchiveProcessing/Period.php
index c6d38a512c..0ca9ce171b 100644
--- a/core/ArchiveProcessing/Period.php
+++ b/core/ArchiveProcessing/Period.php
@@ -1,472 +1,444 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Handles the archiving process for a period
- *
+ *
* This class provides generic methods to archive data for a period (week / month / year).
- *
+ *
* These methods are called by the plugins that do the logic of archiving their own data. \
* They hook on the event 'ArchiveProcessing_Period.compute'
- *
+ *
* @package Piwik
* @subpackage Piwik_ArchiveProcessing
*/
class Piwik_ArchiveProcessing_Period extends Piwik_ArchiveProcessing
{
- /**
- * Array of (column name before => column name renamed) of the columns for which sum operation is invalid.
- * The summed value is not accurate and these columns will be renamed accordingly.
+ /**
+ * Array of (column name before => column name renamed) of the columns for which sum operation is invalid.
+ * The summed value is not accurate and these columns will be renamed accordingly.
* @var array
- */
- static public $invalidSummedColumnNameToRenamedName = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS
- );
-
- /**
- * @var Piwik_Archive_Single[]
- */
- public $archives = array();
-
- /**
- * Sums all values for the given field names $aNames over the period
- * See @archiveNumericValuesGeneral for more information
- *
- * @param string|array $aNames
- * @return array
- */
- public function archiveNumericValuesSum( $aNames )
- {
- return $this->archiveNumericValuesGeneral($aNames, 'sum');
- }
-
- /**
- * Get the maximum value for all values for the given field names $aNames over the period
- * See @archiveNumericValuesGeneral for more information
- *
- * @param string|array $aNames
- * @return array
- */
- public function archiveNumericValuesMax( $aNames )
- {
- return $this->archiveNumericValuesGeneral($aNames, 'max');
- }
-
- /**
- * Given a list of fields names, the method will fetch all their values over the period, and archive them using the given operation.
- *
- * For example if $operationToApply = 'sum' and $aNames = array('nb_visits', 'sum_time_visit')
- * it will sum all values of nb_visits for the period (for example give the number of visits for the month by summing the visits of every day)
- *
- * @param array|string $aNames Array of strings or string containg the field names to select
- * @param string $operationToApply Available operations = sum, max, min
- * @throws Exception
- * @return array
- */
- private function archiveNumericValuesGeneral($aNames, $operationToApply)
- {
- $this->loadSubPeriods();
- if(!is_array($aNames))
- {
- $aNames = array($aNames);
- }
-
- // fetch the numeric values and apply the operation on them
- $results = array();
- foreach($this->archives as $id => $archive)
- {
- foreach($aNames as $name)
- {
- if(!isset($results[$name]))
- {
- $results[$name] = 0;
- }
- if($name == 'nb_uniq_visitors') continue;
-
- $valueToSum = $archive->getNumeric($name);
-
- if($valueToSum !== false)
- {
- switch ($operationToApply) {
- case 'sum':
- $results[$name] += $valueToSum;
- break;
- case 'max':
- $results[$name] = max($results[$name], $valueToSum);
- break;
- case 'min':
- $results[$name] = min($results[$name], $valueToSum);
- break;
- default:
- throw new Exception("Operation not applicable.");
- break;
- }
- }
- }
- }
-
- if(!Piwik::isUniqueVisitorsEnabled($this->period->getLabel()))
- {
- unset($results['nb_uniq_visitors']);
- }
-
- foreach($results as $name => $value)
- {
- if($name == 'nb_uniq_visitors')
- {
- $value = (float) $this->computeNbUniqVisitors();
- }
- $this->insertRecord($name, $value);
- }
-
- // if asked for only one field to sum
- if(count($results) == 1)
- {
- return $results[$name];
- }
-
- // returns the array of records once summed
- return $results;
- }
-
- /**
- * This method will compute the sum of DataTables over the period for the given fields $aRecordName.
- * The resulting DataTable will be then added to queue of data to be recorded in the database.
- * It will usually be called in a plugin that listens to the hook 'ArchiveProcessing_Period.compute'
- *
- * For example if $aRecordName = 'UserCountry_country' the method will select all UserCountry_country DataTable for the period
- * (eg. the 31 dataTable of the last month), sum them, then record it in the DB
- *
- *
- * This method works on recursive dataTable. For example for the 'Actions' it will select all subtables of all dataTable of all the sub periods
- * and get the sum.
- *
- * It returns an array that gives information about the "final" DataTable. The array gives for every field name, the number of rows in the
- * final DataTable (ie. the number of distinct LABEL over the period) (eg. the number of distinct keywords over the last month)
- *
- * @param string|array $aRecordName Field name(s) of DataTable to select so we can get the sum
- * @param array $invalidSummedColumnNameToRenamedName (current_column_name => new_column_name) for columns that must change names when summed
- * (eg. unique visitors go from nb_uniq_visitors to sum_daily_nb_uniq_visitors)
- * @param int $maximumRowsInDataTableLevelZero Max row count of parent datatable to archive
- * @param int $maximumRowsInSubDataTable Max row count of children datatable(s) to archive
- * @param string $columnToSortByBeforeTruncation Column name to sort by, before truncating rows (ie. if there are more rows than the specified max row count)
- *
- * @return array array (
- * nameTable1 => number of rows,
- * nameTable2 => number of rows,
- * )
- */
- public function archiveDataTable( $aRecordName,
- $invalidSummedColumnNameToRenamedName = null,
- $maximumRowsInDataTableLevelZero = null,
- $maximumRowsInSubDataTable = null,
- $columnToSortByBeforeTruncation = null )
- {
- // We clean up below all tables created during this function call (and recursive calls)
- $latestUsedTableId = Piwik_DataTable_Manager::getInstance()->getMostRecentTableId();
-
- $this->loadSubPeriods();
- if(!is_array($aRecordName))
- {
- $aRecordName = array($aRecordName);
- }
-
- $nameToCount = array();
- foreach($aRecordName as $recordName)
- {
- $table = $this->getRecordDataTableSum($recordName, $invalidSummedColumnNameToRenamedName);
-
- $nameToCount[$recordName]['level0'] = $table->getRowsCount();
- $nameToCount[$recordName]['recursive'] = $table->getRowsCountRecursive();
-
- $blob = $table->getSerialized( $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable, $columnToSortByBeforeTruncation );
- destroy($table);
- $this->insertBlobRecord($recordName, $blob);
- }
- Piwik_DataTable_Manager::getInstance()->deleteAll( $latestUsedTableId );
-
- return $nameToCount;
- }
-
- /**
- * This method selects all DataTables that have the name $name over the period.
- * It calls the appropriate methods that sum all these tables together.
- * The resulting DataTable is returned.
- *
- * @param string $name
- * @param array $invalidSummedColumnNameToRenamedName columns in the array (old name, new name) to be renamed as the sum operation is not valid on them (eg. nb_uniq_visitors->sum_daily_nb_uniq_visitors)
- * @return Piwik_DataTable
- */
- protected function getRecordDataTableSum( $name, $invalidSummedColumnNameToRenamedName )
- {
- $table = new Piwik_DataTable();
- foreach($this->archives as $archive)
- {
- $archive->preFetchBlob($name);
- $datatableToSum = $archive->getDataTable($name);
- $archive->loadSubDataTables($name, $datatableToSum);
- $table->addDataTable($datatableToSum);
- $archive->freeBlob($name);
- }
-
- if(is_null($invalidSummedColumnNameToRenamedName))
- {
- $invalidSummedColumnNameToRenamedName = self::$invalidSummedColumnNameToRenamedName;
- }
- foreach($invalidSummedColumnNameToRenamedName as $oldName => $newName)
- {
- $table->renameColumn($oldName, $newName);
- }
- return $table;
- }
-
- protected function initCompute()
- {
- parent::initCompute();
- }
-
- /**
- * Returns the ID of the archived subperiods.
- *
- * @return array Array of the idArchive of the subperiods
- */
- protected function loadSubperiodsArchive()
- {
- $periods = array();
-
- // we first compute every subperiod of the archive
- foreach($this->period->getSubperiods() as $period)
- {
- $archivePeriod = new Piwik_Archive_Single();
- $archivePeriod->setSite( $this->site );
- $archivePeriod->setPeriod( $period );
- $archivePeriod->setSegment( $this->getSegment() );
- $archivePeriod->setRequestedReport($this->getRequestedReport());
-
- $periods[] = $archivePeriod;
- }
- return $periods;
- }
-
- /**
- * Main method to process logs for a period.
- * The only logic done here is computing the number of visits, actions, etc.
- *
- * All the other reports are computed inside plugins listening to the event 'ArchiveProcessing_Period.compute'.
- * See some of the plugins for an example.
- */
- protected function compute()
- {
- if(!$this->isThereSomeVisits())
- {
- return;
- }
- Piwik_PostEvent('ArchiveProcessing_Period.compute', $this);
- }
-
- protected function loadSubPeriods()
- {
- if(empty($this->archives))
- {
- $this->archives = $this->loadSubperiodsArchive();
- }
- }
-
- /**
- *
- * @see Piwik_ArchiveProcessing_Day::isThereSomeVisits()
- * @return bool|null
- */
- public function isThereSomeVisits()
- {
- if(!is_null($this->isThereSomeVisits))
- {
- return $this->isThereSomeVisits;
- }
-
- $this->loadSubPeriods();
- if(self::getPluginBeingProcessed($this->getRequestedReport()) == 'VisitsSummary'
- || $this->shouldProcessReportsAllPlugins($this->getSegment(), $this->period)
- )
- {
- $toSum = self::getCoreMetrics();
- $record = $this->archiveNumericValuesSum($toSum);
- $this->archiveNumericValuesMax( 'max_actions' );
-
- $nbVisitsConverted = $record['nb_visits_converted'];
- $nbVisits = $record['nb_visits'];
- }
- else
- {
- $archive = new Piwik_Archive_Single();
- $archive->setSite( $this->site );
- $archive->setPeriod( $this->period );
- $archive->setSegment( $this->getSegment() );
-
- $nbVisits = $archive->getNumeric('nb_visits');
- $nbVisitsConverted = 0;
- if($nbVisits > 0)
- {
- $nbVisitsConverted = $archive->getNumeric('nb_visits_converted');
- }
- }
-
- $this->setNumberOfVisits($nbVisits);
- $this->setNumberOfVisitsConverted($nbVisitsConverted);
- $this->isThereSomeVisits = ($nbVisits > 0);
- return $this->isThereSomeVisits;
- }
-
- /**
- * Processes number of unique visitors for the given period
- *
- * This is the only metric we process from the logs directly,
- * since unique visitors cannot be summed like other metrics.
- *
- * @return int
- */
- protected function computeNbUniqVisitors()
- {
- $select = "count(distinct log_visit.idvisitor) as nb_uniq_visitors";
- $from = "log_visit";
- $where = "log_visit.visit_last_action_time >= ?
+ */
+ static public $invalidSummedColumnNameToRenamedName = array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS
+ );
+
+ /**
+ * @var Piwik_Archive_Single[]
+ */
+ public $archives = array();
+
+ /**
+ * Sums all values for the given field names $aNames over the period
+ * See @archiveNumericValuesGeneral for more information
+ *
+ * @param string|array $aNames
+ * @return array
+ */
+ public function archiveNumericValuesSum($aNames)
+ {
+ return $this->archiveNumericValuesGeneral($aNames, 'sum');
+ }
+
+ /**
+ * Get the maximum value for all values for the given field names $aNames over the period
+ * See @archiveNumericValuesGeneral for more information
+ *
+ * @param string|array $aNames
+ * @return array
+ */
+ public function archiveNumericValuesMax($aNames)
+ {
+ return $this->archiveNumericValuesGeneral($aNames, 'max');
+ }
+
+ /**
+ * Given a list of fields names, the method will fetch all their values over the period, and archive them using the given operation.
+ *
+ * For example if $operationToApply = 'sum' and $aNames = array('nb_visits', 'sum_time_visit')
+ * it will sum all values of nb_visits for the period (for example give the number of visits for the month by summing the visits of every day)
+ *
+ * @param array|string $aNames Array of strings or string containg the field names to select
+ * @param string $operationToApply Available operations = sum, max, min
+ * @throws Exception
+ * @return array
+ */
+ private function archiveNumericValuesGeneral($aNames, $operationToApply)
+ {
+ $this->loadSubPeriods();
+ if (!is_array($aNames)) {
+ $aNames = array($aNames);
+ }
+
+ // fetch the numeric values and apply the operation on them
+ $results = array();
+ foreach ($this->archives as $id => $archive) {
+ foreach ($aNames as $name) {
+ if (!isset($results[$name])) {
+ $results[$name] = 0;
+ }
+ if ($name == 'nb_uniq_visitors') continue;
+
+ $valueToSum = $archive->getNumeric($name);
+
+ if ($valueToSum !== false) {
+ switch ($operationToApply) {
+ case 'sum':
+ $results[$name] += $valueToSum;
+ break;
+ case 'max':
+ $results[$name] = max($results[$name], $valueToSum);
+ break;
+ case 'min':
+ $results[$name] = min($results[$name], $valueToSum);
+ break;
+ default:
+ throw new Exception("Operation not applicable.");
+ break;
+ }
+ }
+ }
+ }
+
+ if (!Piwik::isUniqueVisitorsEnabled($this->period->getLabel())) {
+ unset($results['nb_uniq_visitors']);
+ }
+
+ foreach ($results as $name => $value) {
+ if ($name == 'nb_uniq_visitors') {
+ $value = (float)$this->computeNbUniqVisitors();
+ }
+ $this->insertRecord($name, $value);
+ }
+
+ // if asked for only one field to sum
+ if (count($results) == 1) {
+ return $results[$name];
+ }
+
+ // returns the array of records once summed
+ return $results;
+ }
+
+ /**
+ * This method will compute the sum of DataTables over the period for the given fields $aRecordName.
+ * The resulting DataTable will be then added to queue of data to be recorded in the database.
+ * It will usually be called in a plugin that listens to the hook 'ArchiveProcessing_Period.compute'
+ *
+ * For example if $aRecordName = 'UserCountry_country' the method will select all UserCountry_country DataTable for the period
+ * (eg. the 31 dataTable of the last month), sum them, then record it in the DB
+ *
+ *
+ * This method works on recursive dataTable. For example for the 'Actions' it will select all subtables of all dataTable of all the sub periods
+ * and get the sum.
+ *
+ * It returns an array that gives information about the "final" DataTable. The array gives for every field name, the number of rows in the
+ * final DataTable (ie. the number of distinct LABEL over the period) (eg. the number of distinct keywords over the last month)
+ *
+ * @param string|array $aRecordName Field name(s) of DataTable to select so we can get the sum
+ * @param array $invalidSummedColumnNameToRenamedName (current_column_name => new_column_name) for columns that must change names when summed
+ * (eg. unique visitors go from nb_uniq_visitors to sum_daily_nb_uniq_visitors)
+ * @param int $maximumRowsInDataTableLevelZero Max row count of parent datatable to archive
+ * @param int $maximumRowsInSubDataTable Max row count of children datatable(s) to archive
+ * @param string $columnToSortByBeforeTruncation Column name to sort by, before truncating rows (ie. if there are more rows than the specified max row count)
+ *
+ * @return array array (
+ * nameTable1 => number of rows,
+ * nameTable2 => number of rows,
+ * )
+ */
+ public function archiveDataTable($aRecordName,
+ $invalidSummedColumnNameToRenamedName = null,
+ $maximumRowsInDataTableLevelZero = null,
+ $maximumRowsInSubDataTable = null,
+ $columnToSortByBeforeTruncation = null)
+ {
+ // We clean up below all tables created during this function call (and recursive calls)
+ $latestUsedTableId = Piwik_DataTable_Manager::getInstance()->getMostRecentTableId();
+
+ $this->loadSubPeriods();
+ if (!is_array($aRecordName)) {
+ $aRecordName = array($aRecordName);
+ }
+
+ $nameToCount = array();
+ foreach ($aRecordName as $recordName) {
+ $table = $this->getRecordDataTableSum($recordName, $invalidSummedColumnNameToRenamedName);
+
+ $nameToCount[$recordName]['level0'] = $table->getRowsCount();
+ $nameToCount[$recordName]['recursive'] = $table->getRowsCountRecursive();
+
+ $blob = $table->getSerialized($maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable, $columnToSortByBeforeTruncation);
+ destroy($table);
+ $this->insertBlobRecord($recordName, $blob);
+ }
+ Piwik_DataTable_Manager::getInstance()->deleteAll($latestUsedTableId);
+
+ return $nameToCount;
+ }
+
+ /**
+ * This method selects all DataTables that have the name $name over the period.
+ * It calls the appropriate methods that sum all these tables together.
+ * The resulting DataTable is returned.
+ *
+ * @param string $name
+ * @param array $invalidSummedColumnNameToRenamedName columns in the array (old name, new name) to be renamed as the sum operation is not valid on them (eg. nb_uniq_visitors->sum_daily_nb_uniq_visitors)
+ * @return Piwik_DataTable
+ */
+ protected function getRecordDataTableSum($name, $invalidSummedColumnNameToRenamedName)
+ {
+ $table = new Piwik_DataTable();
+ foreach ($this->archives as $archive) {
+ $archive->preFetchBlob($name);
+ $datatableToSum = $archive->getDataTable($name);
+ $archive->loadSubDataTables($name, $datatableToSum);
+ $table->addDataTable($datatableToSum);
+ $archive->freeBlob($name);
+ }
+
+ if (is_null($invalidSummedColumnNameToRenamedName)) {
+ $invalidSummedColumnNameToRenamedName = self::$invalidSummedColumnNameToRenamedName;
+ }
+ foreach ($invalidSummedColumnNameToRenamedName as $oldName => $newName) {
+ $table->renameColumn($oldName, $newName);
+ }
+ return $table;
+ }
+
+ protected function initCompute()
+ {
+ parent::initCompute();
+ }
+
+ /**
+ * Returns the ID of the archived subperiods.
+ *
+ * @return array Array of the idArchive of the subperiods
+ */
+ protected function loadSubperiodsArchive()
+ {
+ $periods = array();
+
+ // we first compute every subperiod of the archive
+ foreach ($this->period->getSubperiods() as $period) {
+ $archivePeriod = new Piwik_Archive_Single();
+ $archivePeriod->setSite($this->site);
+ $archivePeriod->setPeriod($period);
+ $archivePeriod->setSegment($this->getSegment());
+ $archivePeriod->setRequestedReport($this->getRequestedReport());
+
+ $periods[] = $archivePeriod;
+ }
+ return $periods;
+ }
+
+ /**
+ * Main method to process logs for a period.
+ * The only logic done here is computing the number of visits, actions, etc.
+ *
+ * All the other reports are computed inside plugins listening to the event 'ArchiveProcessing_Period.compute'.
+ * See some of the plugins for an example.
+ */
+ protected function compute()
+ {
+ if (!$this->isThereSomeVisits()) {
+ return;
+ }
+ Piwik_PostEvent('ArchiveProcessing_Period.compute', $this);
+ }
+
+ protected function loadSubPeriods()
+ {
+ if (empty($this->archives)) {
+ $this->archives = $this->loadSubperiodsArchive();
+ }
+ }
+
+ /**
+ *
+ * @see Piwik_ArchiveProcessing_Day::isThereSomeVisits()
+ * @return bool|null
+ */
+ public function isThereSomeVisits()
+ {
+ if (!is_null($this->isThereSomeVisits)) {
+ return $this->isThereSomeVisits;
+ }
+
+ $this->loadSubPeriods();
+ if (self::getPluginBeingProcessed($this->getRequestedReport()) == 'VisitsSummary'
+ || $this->shouldProcessReportsAllPlugins($this->getSegment(), $this->period)
+ ) {
+ $toSum = self::getCoreMetrics();
+ $record = $this->archiveNumericValuesSum($toSum);
+ $this->archiveNumericValuesMax('max_actions');
+
+ $nbVisitsConverted = $record['nb_visits_converted'];
+ $nbVisits = $record['nb_visits'];
+ } else {
+ $archive = new Piwik_Archive_Single();
+ $archive->setSite($this->site);
+ $archive->setPeriod($this->period);
+ $archive->setSegment($this->getSegment());
+
+ $nbVisits = $archive->getNumeric('nb_visits');
+ $nbVisitsConverted = 0;
+ if ($nbVisits > 0) {
+ $nbVisitsConverted = $archive->getNumeric('nb_visits_converted');
+ }
+ }
+
+ $this->setNumberOfVisits($nbVisits);
+ $this->setNumberOfVisitsConverted($nbVisitsConverted);
+ $this->isThereSomeVisits = ($nbVisits > 0);
+ return $this->isThereSomeVisits;
+ }
+
+ /**
+ * Processes number of unique visitors for the given period
+ *
+ * This is the only metric we process from the logs directly,
+ * since unique visitors cannot be summed like other metrics.
+ *
+ * @return int
+ */
+ protected function computeNbUniqVisitors()
+ {
+ $select = "count(distinct log_visit.idvisitor) as nb_uniq_visitors";
+ $from = "log_visit";
+ $where = "log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?";
-
- $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
-
- $query = $this->getSegment()->getSelectQuery($select, $from, $where, $bind);
-
- return Zend_Registry::get('db')->fetchOne($query['sql'], $query['bind']);
- }
-
- /**
- * Called at the end of the archiving process.
- * Does some cleaning job in the database.
- */
- protected function postCompute()
- {
- parent::postCompute();
-
- $numericTable = $this->tableArchiveNumeric->getTableName();
- self::doPurgeOutdatedArchives($numericTable, $this->isArchiveTemporary());
-
- if(!isset($this->archives))
- {
- return;
- }
- foreach($this->archives as $archive)
- {
- destroy($archive);
- }
- $this->archives = array();
- }
-
- const FLAG_TABLE_PURGED = 'lastPurge_';
-
- // Used to disable Purge Outdated reports during test data setup
- static public $enablePurgeOutdated = true;
-
- /**
- * Given a monthly archive table, will delete all reports that are now outdated,
- * or reports that ended with an error
- */
- static public function doPurgeOutdatedArchives($numericTable)
- {
- if(!self::$enablePurgeOutdated) {
- return;
- }
- $blobTable = str_replace("numeric", "blob", $numericTable);
- $key = self::FLAG_TABLE_PURGED . $blobTable;
- $timestamp = Piwik_GetOption($key);
-
- // we shall purge temporary archives after their timeout is finished, plus an extra 6 hours
- // in case archiving is disabled or run once a day, we give it this extra time to run
- // and re-process more recent records...
- // TODO: Instead of hardcoding 6 we should put the actual number of hours between 2 archiving runs
- $temporaryArchivingTimeout = self::getTodayArchiveTimeToLive();
- $purgeEveryNSeconds = max($temporaryArchivingTimeout, 6 * 3600);
-
- // we only delete archives if we are able to process them, otherwise, the browser might process reports
- // when &segment= is specified (or custom date range) and would below, delete temporary archives that the
- // browser is not able to process until next cron run (which could be more than 1 hour away)
- if(self::isRequestAuthorizedToArchive()
- && (!$timestamp
- || $timestamp < time() - $purgeEveryNSeconds))
- {
- Piwik_SetOption($key, time());
-
- // If Browser Archiving is enabled, it is likely there are many more temporary archives
- // We delete more often which is safe, since reports are re-processed on demand
- if(self::isBrowserTriggerArchivingEnabled())
- {
- $purgeArchivesOlderThan = Piwik_Date::factory(time() - 2 * $temporaryArchivingTimeout)->getDateTime();
- }
- // If archive.php via Cron is building the reports, we should keep all temporary reports from today
- else
- {
- $purgeArchivesOlderThan = Piwik_Date::factory('today')->getDateTime();
- }
- $result = Piwik_FetchAll("
+
+ $bind = array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite);
+
+ $query = $this->getSegment()->getSelectQuery($select, $from, $where, $bind);
+
+ return Zend_Registry::get('db')->fetchOne($query['sql'], $query['bind']);
+ }
+
+ /**
+ * Called at the end of the archiving process.
+ * Does some cleaning job in the database.
+ */
+ protected function postCompute()
+ {
+ parent::postCompute();
+
+ $numericTable = $this->tableArchiveNumeric->getTableName();
+ self::doPurgeOutdatedArchives($numericTable, $this->isArchiveTemporary());
+
+ if (!isset($this->archives)) {
+ return;
+ }
+ foreach ($this->archives as $archive) {
+ destroy($archive);
+ }
+ $this->archives = array();
+ }
+
+ const FLAG_TABLE_PURGED = 'lastPurge_';
+
+ // Used to disable Purge Outdated reports during test data setup
+ static public $enablePurgeOutdated = true;
+
+ /**
+ * Given a monthly archive table, will delete all reports that are now outdated,
+ * or reports that ended with an error
+ */
+ static public function doPurgeOutdatedArchives($numericTable)
+ {
+ if (!self::$enablePurgeOutdated) {
+ return;
+ }
+ $blobTable = str_replace("numeric", "blob", $numericTable);
+ $key = self::FLAG_TABLE_PURGED . $blobTable;
+ $timestamp = Piwik_GetOption($key);
+
+ // we shall purge temporary archives after their timeout is finished, plus an extra 6 hours
+ // in case archiving is disabled or run once a day, we give it this extra time to run
+ // and re-process more recent records...
+ // TODO: Instead of hardcoding 6 we should put the actual number of hours between 2 archiving runs
+ $temporaryArchivingTimeout = self::getTodayArchiveTimeToLive();
+ $purgeEveryNSeconds = max($temporaryArchivingTimeout, 6 * 3600);
+
+ // we only delete archives if we are able to process them, otherwise, the browser might process reports
+ // when &segment= is specified (or custom date range) and would below, delete temporary archives that the
+ // browser is not able to process until next cron run (which could be more than 1 hour away)
+ if (self::isRequestAuthorizedToArchive()
+ && (!$timestamp
+ || $timestamp < time() - $purgeEveryNSeconds)
+ ) {
+ Piwik_SetOption($key, time());
+
+ // If Browser Archiving is enabled, it is likely there are many more temporary archives
+ // We delete more often which is safe, since reports are re-processed on demand
+ if (self::isBrowserTriggerArchivingEnabled()) {
+ $purgeArchivesOlderThan = Piwik_Date::factory(time() - 2 * $temporaryArchivingTimeout)->getDateTime();
+ } // If archive.php via Cron is building the reports, we should keep all temporary reports from today
+ else {
+ $purgeArchivesOlderThan = Piwik_Date::factory('today')->getDateTime();
+ }
+ $result = Piwik_FetchAll("
SELECT idarchive
FROM $numericTable
WHERE name LIKE 'done%'
- AND (( value = ". Piwik_ArchiveProcessing::DONE_OK_TEMPORARY ."
+ AND (( value = " . Piwik_ArchiveProcessing::DONE_OK_TEMPORARY . "
AND ts_archived < ?)
- OR value = ". Piwik_ArchiveProcessing::DONE_ERROR .")",
- array($purgeArchivesOlderThan)
- );
-
- $idArchivesToDelete = array();
- if(!empty($result))
- {
- foreach($result as $row) {
- $idArchivesToDelete[] = $row['idarchive'];
- }
- $query = "DELETE
+ OR value = " . Piwik_ArchiveProcessing::DONE_ERROR . ")",
+ array($purgeArchivesOlderThan)
+ );
+
+ $idArchivesToDelete = array();
+ if (!empty($result)) {
+ foreach ($result as $row) {
+ $idArchivesToDelete[] = $row['idarchive'];
+ }
+ $query = "DELETE
FROM %s
- WHERE idarchive IN (".implode(',',$idArchivesToDelete).")
+ WHERE idarchive IN (" . implode(',', $idArchivesToDelete) . ")
";
-
- Piwik_Query(sprintf($query, $numericTable));
-
- // Individual blob tables could be missing
- try {
- Piwik_Query(sprintf($query, $blobTable));
- } catch(Exception $e) { }
- }
- Piwik::log("Purging temporary archives: done [ purged archives older than $purgeArchivesOlderThan from $blobTable and $numericTable ] [Deleted IDs: ". implode(',',$idArchivesToDelete)."]");
-
- // Deleting "Custom Date Range" reports after 1 day, since they can be re-processed
- // and would take up unecessary space
- $yesterday = Piwik_Date::factory('yesterday')->getDateTime();
- $query = "DELETE
+
+ Piwik_Query(sprintf($query, $numericTable));
+
+ // Individual blob tables could be missing
+ try {
+ Piwik_Query(sprintf($query, $blobTable));
+ } catch (Exception $e) {
+ }
+ }
+ Piwik::log("Purging temporary archives: done [ purged archives older than $purgeArchivesOlderThan from $blobTable and $numericTable ] [Deleted IDs: " . implode(',', $idArchivesToDelete) . "]");
+
+ // Deleting "Custom Date Range" reports after 1 day, since they can be re-processed
+ // and would take up unecessary space
+ $yesterday = Piwik_Date::factory('yesterday')->getDateTime();
+ $query = "DELETE
FROM %s
WHERE period = ?
AND ts_archived < ?";
- $bind = array(Piwik::$idPeriods['range'], $yesterday);
- Piwik::log("Purging Custom Range archives: done [ purged archives older than $yesterday from $blobTable and $numericTable ]");
-
- Piwik_Query(sprintf($query, $numericTable), $bind);
-
- // Individual blob tables could be missing
- try {
- Piwik_Query(sprintf($query, $blobTable), $bind);
- } catch(Exception $e) { }
-
- // these tables will be OPTIMIZEd daily in a scheduled task, to claim lost space
- }
- else
- {
- Piwik::log("Purging temporary archives: skipped.");
- }
- }
+ $bind = array(Piwik::$idPeriods['range'], $yesterday);
+ Piwik::log("Purging Custom Range archives: done [ purged archives older than $yesterday from $blobTable and $numericTable ]");
+
+ Piwik_Query(sprintf($query, $numericTable), $bind);
+
+ // Individual blob tables could be missing
+ try {
+ Piwik_Query(sprintf($query, $blobTable), $bind);
+ } catch (Exception $e) {
+ }
+
+ // these tables will be OPTIMIZEd daily in a scheduled task, to claim lost space
+ } else {
+ Piwik::log("Purging temporary archives: skipped.");
+ }
+ }
} \ No newline at end of file
diff --git a/core/AssetManager.php b/core/AssetManager.php
index 3ef46bf0d3..1091e518f0 100644
--- a/core/AssetManager.php
+++ b/core/AssetManager.php
@@ -24,7 +24,7 @@ require_once PIWIK_INCLUDE_PATH . '/libs/jsmin/jsmin.php';
* JavaScript and CSS files.
*
* It performs the following actions:
- * - Identifies required assets
+ * - Identifies required assets
* - Includes assets in the rendered HTML page
* - Manages asset merging and minifying
* - Manages server-side cache
@@ -39,92 +39,89 @@ require_once PIWIK_INCLUDE_PATH . '/libs/jsmin/jsmin.php';
*/
class Piwik_AssetManager
{
- const MERGED_CSS_FILE = "asset_manager_global_css.css";
- const MERGED_JS_FILE = "asset_manager_global_js.js";
- const CSS_IMPORT_EVENT = "AssetManager.getCssFiles";
- const JS_IMPORT_EVENT = "AssetManager.getJsFiles";
- const MERGED_FILE_DIR = "tmp/assets/";
- const CSS_IMPORT_DIRECTIVE = "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\" />\n";
- const JS_IMPORT_DIRECTIVE = "<script type=\"text/javascript\" src=\"%s\"></script>\n";
- const GET_CSS_MODULE_ACTION = "index.php?module=Proxy&action=getCss";
- const GET_JS_MODULE_ACTION = "index.php?module=Proxy&action=getJs";
- const MINIFIED_JS_RATIO = 100;
-
- /**
- * Returns CSS file inclusion directive(s) using the markup <link>
- *
- * @return string
- */
- public static function getCssAssets()
- {
- if ( self::getDisableMergedAssets() )
- {
- // Individual includes mode
- self::removeMergedAsset(self::MERGED_CSS_FILE);
- return self::getIndividualCssIncludes();
- }
- return sprintf ( self::CSS_IMPORT_DIRECTIVE, self::GET_CSS_MODULE_ACTION );
- }
-
- /**
- * Returns JS file inclusion directive(s) using the markup <script>
- *
- * @return string
- */
- public static function getJsAssets()
- {
- if ( self::getDisableMergedAssets() )
- {
- // Individual includes mode
- self::removeMergedAsset(self::MERGED_JS_FILE);
- return self::getIndividualJsIncludes();
- }
- return sprintf ( self::JS_IMPORT_DIRECTIVE, self::GET_JS_MODULE_ACTION );
- }
-
- /**
- * Generate the merged css file.
- *
- * @throws Exception if a file can not be opened in write mode
- */
- private static function generateMergedCssFile()
- {
- $mergedContent = "";
-
- // absolute path to doc root
- $rootDirectory = realpath(PIWIK_DOCUMENT_ROOT);
- if($rootDirectory != '/' && substr_compare($rootDirectory, '/', -1))
- {
- $rootDirectory .= '/';
- }
- $rootDirectoryLen = strlen($rootDirectory);
-
- // Loop through each css file
- $files = self::getCssFiles();
- foreach ($files as $file) {
-
- self::validateCssFile ( $file );
-
- $fileLocation = self::getAbsoluteLocation($file);
- $content = file_get_contents ($fileLocation);
-
- // Rewrite css url directives
- // - assumes these are all relative paths
- // - rewrite windows directory separator \\ to /
- $baseDirectory = dirname($file);
- $content = preg_replace_callback(
- "/(url\(['\"]?)([^'\")]*)/",
- create_function(
- '$matches',
- "return \$matches[1] . str_replace('\\\\', '/', substr(realpath(PIWIK_DOCUMENT_ROOT . '/$baseDirectory/' . \$matches[2]), $rootDirectoryLen));"
- ),
- $content
- );
- $mergedContent = $mergedContent . $content;
- }
-
- $mergedContent = cssmin::minify($mergedContent);
- $mergedContent = str_replace("\n", "\r\n", $mergedContent);
+ const MERGED_CSS_FILE = "asset_manager_global_css.css";
+ const MERGED_JS_FILE = "asset_manager_global_js.js";
+ const CSS_IMPORT_EVENT = "AssetManager.getCssFiles";
+ const JS_IMPORT_EVENT = "AssetManager.getJsFiles";
+ const MERGED_FILE_DIR = "tmp/assets/";
+ const CSS_IMPORT_DIRECTIVE = "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\" />\n";
+ const JS_IMPORT_DIRECTIVE = "<script type=\"text/javascript\" src=\"%s\"></script>\n";
+ const GET_CSS_MODULE_ACTION = "index.php?module=Proxy&action=getCss";
+ const GET_JS_MODULE_ACTION = "index.php?module=Proxy&action=getJs";
+ const MINIFIED_JS_RATIO = 100;
+
+ /**
+ * Returns CSS file inclusion directive(s) using the markup <link>
+ *
+ * @return string
+ */
+ public static function getCssAssets()
+ {
+ if (self::getDisableMergedAssets()) {
+ // Individual includes mode
+ self::removeMergedAsset(self::MERGED_CSS_FILE);
+ return self::getIndividualCssIncludes();
+ }
+ return sprintf(self::CSS_IMPORT_DIRECTIVE, self::GET_CSS_MODULE_ACTION);
+ }
+
+ /**
+ * Returns JS file inclusion directive(s) using the markup <script>
+ *
+ * @return string
+ */
+ public static function getJsAssets()
+ {
+ if (self::getDisableMergedAssets()) {
+ // Individual includes mode
+ self::removeMergedAsset(self::MERGED_JS_FILE);
+ return self::getIndividualJsIncludes();
+ }
+ return sprintf(self::JS_IMPORT_DIRECTIVE, self::GET_JS_MODULE_ACTION);
+ }
+
+ /**
+ * Generate the merged css file.
+ *
+ * @throws Exception if a file can not be opened in write mode
+ */
+ private static function generateMergedCssFile()
+ {
+ $mergedContent = "";
+
+ // absolute path to doc root
+ $rootDirectory = realpath(PIWIK_DOCUMENT_ROOT);
+ if ($rootDirectory != '/' && substr_compare($rootDirectory, '/', -1)) {
+ $rootDirectory .= '/';
+ }
+ $rootDirectoryLen = strlen($rootDirectory);
+
+ // Loop through each css file
+ $files = self::getCssFiles();
+ foreach ($files as $file) {
+
+ self::validateCssFile($file);
+
+ $fileLocation = self::getAbsoluteLocation($file);
+ $content = file_get_contents($fileLocation);
+
+ // Rewrite css url directives
+ // - assumes these are all relative paths
+ // - rewrite windows directory separator \\ to /
+ $baseDirectory = dirname($file);
+ $content = preg_replace_callback(
+ "/(url\(['\"]?)([^'\")]*)/",
+ create_function(
+ '$matches',
+ "return \$matches[1] . str_replace('\\\\', '/', substr(realpath(PIWIK_DOCUMENT_ROOT . '/$baseDirectory/' . \$matches[2]), $rootDirectoryLen));"
+ ),
+ $content
+ );
+ $mergedContent = $mergedContent . $content;
+ }
+
+ $mergedContent = cssmin::minify($mergedContent);
+ $mergedContent = str_replace("\n", "\r\n", $mergedContent);
Piwik_PostEvent('AssetManager.filterMergedCss', $mergedContent);
@@ -150,359 +147,347 @@ class Piwik_AssetManager
}
/**
- * Returns individual CSS file inclusion directive(s) using the markup <link>
- *
- * @return string
- */
- private static function getIndividualCssIncludes()
- {
- $cssIncludeString = '';
-
- $cssFiles = self::getCssFiles();
-
- foreach ($cssFiles as $cssFile) {
-
- self::validateCssFile ( $cssFile );
- $cssIncludeString = $cssIncludeString . sprintf ( self::CSS_IMPORT_DIRECTIVE, $cssFile );
- }
-
- return $cssIncludeString;
- }
-
- /**
- * Returns required CSS files
- *
- * @return Array
- */
- private static function getCssFiles()
- {
- $cssFiles = array();
- Piwik_PostEvent(self::CSS_IMPORT_EVENT, $cssFiles);
- $cssFiles = self::sortCssFiles($cssFiles);
- return $cssFiles;
- }
-
- /**
- * Ensure CSS stylesheets are loaded in a particular order regardless of the order that plugins are loaded.
- *
- * @param array $cssFiles Array of CSS stylesheet files
- * @return array
- */
- private static function sortCssFiles($cssFiles)
- {
- $priorityCssOrdered = array(
- 'themes/default/common.css',
- 'themes/default/',
- 'libs/',
- 'plugins/',
- );
-
- return self::prioritySort($priorityCssOrdered, $cssFiles);
- }
-
- /**
- * Check the validity of the css file
- *
- * @param string $cssFile CSS file name
- * @return boolean
- * @throws Exception if a file can not be opened in write mode
- */
- private static function validateCssFile ( $cssFile )
- {
- if(!self::assetIsReadable($cssFile))
- {
- throw new Exception("The css asset with 'href' = " . $cssFile . " is not readable");
- }
- }
-
- /**
- * Generate the merged js file.
- *
- * @throws Exception if a file can not be opened in write mode
- */
- private static function generateMergedJsFile()
- {
- $mergedContent = "";
-
- // Loop through each js file
- $files = self::getJsFiles();
- foreach ($files as $file) {
-
- self::validateJsFile ( $file );
-
- $fileLocation = self::getAbsoluteLocation($file);
- $content = file_get_contents ($fileLocation);
-
- if ( !self::isMinifiedJs($content) )
- {
- $content = JSMin::minify($content);
- }
-
- $mergedContent = $mergedContent . PHP_EOL . $content;
- }
- $mergedContent = str_replace("\n", "\r\n", $mergedContent);
+ * Returns individual CSS file inclusion directive(s) using the markup <link>
+ *
+ * @return string
+ */
+ private static function getIndividualCssIncludes()
+ {
+ $cssIncludeString = '';
+
+ $cssFiles = self::getCssFiles();
+
+ foreach ($cssFiles as $cssFile) {
+
+ self::validateCssFile($cssFile);
+ $cssIncludeString = $cssIncludeString . sprintf(self::CSS_IMPORT_DIRECTIVE, $cssFile);
+ }
+
+ return $cssIncludeString;
+ }
+
+ /**
+ * Returns required CSS files
+ *
+ * @return Array
+ */
+ private static function getCssFiles()
+ {
+ $cssFiles = array();
+ Piwik_PostEvent(self::CSS_IMPORT_EVENT, $cssFiles);
+ $cssFiles = self::sortCssFiles($cssFiles);
+ return $cssFiles;
+ }
+
+ /**
+ * Ensure CSS stylesheets are loaded in a particular order regardless of the order that plugins are loaded.
+ *
+ * @param array $cssFiles Array of CSS stylesheet files
+ * @return array
+ */
+ private static function sortCssFiles($cssFiles)
+ {
+ $priorityCssOrdered = array(
+ 'themes/default/common.css',
+ 'themes/default/',
+ 'libs/',
+ 'plugins/',
+ );
+
+ return self::prioritySort($priorityCssOrdered, $cssFiles);
+ }
+
+ /**
+ * Check the validity of the css file
+ *
+ * @param string $cssFile CSS file name
+ * @return boolean
+ * @throws Exception if a file can not be opened in write mode
+ */
+ private static function validateCssFile($cssFile)
+ {
+ if (!self::assetIsReadable($cssFile)) {
+ throw new Exception("The css asset with 'href' = " . $cssFile . " is not readable");
+ }
+ }
+
+ /**
+ * Generate the merged js file.
+ *
+ * @throws Exception if a file can not be opened in write mode
+ */
+ private static function generateMergedJsFile()
+ {
+ $mergedContent = "";
+
+ // Loop through each js file
+ $files = self::getJsFiles();
+ foreach ($files as $file) {
+
+ self::validateJsFile($file);
+
+ $fileLocation = self::getAbsoluteLocation($file);
+ $content = file_get_contents($fileLocation);
+
+ if (!self::isMinifiedJs($content)) {
+ $content = JSMin::minify($content);
+ }
+
+ $mergedContent = $mergedContent . PHP_EOL . $content;
+ }
+ $mergedContent = str_replace("\n", "\r\n", $mergedContent);
Piwik_PostEvent('AssetManager.filterMergedJs', $mergedContent);
self::writeAssetToFile($mergedContent, self::MERGED_JS_FILE);
- }
-
- /**
- * Returns individual JS file inclusion directive(s) using the markup <script>
- *
- * @return string
- */
- private static function getIndividualJsIncludes()
- {
- $jsFiles = self::getJsFiles();
- $jsIncludeString = '';
- foreach ($jsFiles as $jsFile)
- {
- self::validateJsFile( $jsFile );
- $jsIncludeString = $jsIncludeString . sprintf ( self::JS_IMPORT_DIRECTIVE, $jsFile );
- }
- return $jsIncludeString;
- }
-
- /**
- * Returns required JS files
- *
- * @return Array
- */
- private static function getJsFiles()
- {
- $jsFiles = array();
- Piwik_PostEvent(self::JS_IMPORT_EVENT, $jsFiles);
- $jsFiles = self::sortJsFiles($jsFiles);
- return $jsFiles;
- }
-
- /**
- * Ensure core JS (jQuery etc.) are loaded in a particular order regardless of the order that plugins are loaded.
- *
- * @param array $jsFiles Arry of JavaScript files
- * @return array
- */
- private static function sortJsFiles($jsFiles)
- {
- $priorityJsOrdered = array(
- 'libs/jquery/jquery.js',
- 'libs/jquery/jquery-ui.js',
- 'libs/',
- 'themes/default/common.js',
- 'themes/default/',
- 'plugins/CoreHome/templates/broadcast.js',
- 'plugins/',
- );
-
- return self::prioritySort($priorityJsOrdered, $jsFiles);
- }
-
- /**
- * Check the validity of the js file
- *
- * @param string $jsFile JavaScript file name
- * @return boolean
- * @throws Exception if js file is not valid
- */
- private static function validateJsFile ( $jsFile )
- {
- if(!self::assetIsReadable($jsFile))
- {
- throw new Exception("The js asset with 'src' = " . $jsFile . " is not readable");
- }
- }
-
- /**
- * Returns the global option disable_merged_assets
- *
- * @return string
- */
- private static function getDisableMergedAssets()
- {
- return Piwik_Config::getInstance()->Debug['disable_merged_assets'];
- }
-
- /**
- * Returns the css merged file absolute location.
- * If there is none, the generation process will be triggered.
- *
- * @return string The absolute location of the css merged file
- */
- public static function getMergedCssFileLocation()
- {
- $isGenerated = self::isGenerated(self::MERGED_CSS_FILE);
-
- if ( !$isGenerated )
- {
- self::generateMergedCssFile();
- }
-
- return self::getAbsoluteMergedFileLocation(self::MERGED_CSS_FILE);
- }
-
- /**
- * Returns the js merged file absolute location.
- * If there is none, the generation process will be triggered.
- *
- * @return string The absolute location of the js merged file
- */
- public static function getMergedJsFileLocation()
- {
- $isGenerated = self::isGenerated(self::MERGED_JS_FILE);
-
- if ( !$isGenerated )
- {
- self::generateMergedJsFile();
- }
-
- return self::getAbsoluteMergedFileLocation(self::MERGED_JS_FILE);
- }
-
- /**
- * Check if the provided merged file is generated
- *
- * @param string $filename filename of the merged asset
- * @return boolean true is file exists and is readable, false otherwise
- */
- private static function isGenerated($filename)
- {
- return is_readable (self::getAbsoluteMergedFileLocation($filename));
- }
-
- /**
- * Removes the previous merged file if it exists.
- * Also tries to remove compressed version of the merged file.
- *
- * @param string $filename filename of the merged asset
- * @see Piwik::serveStaticFile()
- * @throws Exception if the file couldn't be deleted
- */
- private static function removeMergedAsset($filename)
- {
- $isGenerated = self::isGenerated($filename);
-
- if ( $isGenerated )
- {
- if ( !unlink ( self::getAbsoluteMergedFileLocation($filename) ) )
- {
- throw Exception ("Unable to delete merged file : " . $filename . ". Please delete the file and refresh");
- }
-
- // Tries to remove compressed version of the merged file.
- // See Piwik::serveStaticFile() for more info on static file compression
- $compressedFileLocation = PIWIK_USER_PATH . Piwik::COMPRESSED_FILE_LOCATION . $filename;
-
- @unlink ( $compressedFileLocation . ".deflate");
- @unlink ( $compressedFileLocation . ".gz");
- }
- }
-
- /**
- * Remove previous merged assets
- */
- public static function removeMergedAssets()
- {
- self::removeMergedAsset(self::MERGED_CSS_FILE);
- self::removeMergedAsset(self::MERGED_JS_FILE);
- }
-
- /**
- * Check if asset is readable
- *
- * @param string $relativePath Relative path to file
- * @return boolean
- */
- private static function assetIsReadable ($relativePath)
- {
- return is_readable(self::getAbsoluteLocation($relativePath));
- }
-
- /**
- * Check if the merged file directory exists and is writable.
- *
- * @return string The directory location
- * @throws Exception if directory is not writable.
- */
- private static function getMergedFileDirectory ()
- {
- $mergedFileDirectory = PIWIK_USER_PATH . '/' . self::MERGED_FILE_DIR;
-
- if (!is_dir($mergedFileDirectory))
- {
- Piwik_Common::mkdir($mergedFileDirectory);
- }
-
- if (!is_writable($mergedFileDirectory))
- {
- throw new Exception("Directory " . $mergedFileDirectory . " has to be writable.");
- }
-
- return $mergedFileDirectory;
- }
-
- /**
- * Builds the absolute location of the requested merged file
- *
- * @param string $mergedFile Name of the merge file
- * @return string absolute location of the merged file
- */
- private static function getAbsoluteMergedFileLocation( $mergedFile )
- {
- return self::getMergedFileDirectory() . $mergedFile;
- }
-
- /**
- * Returns the full path of an asset file
- *
- * @param string $relativePath Relative path to file
- * @return string
- */
- private static function getAbsoluteLocation ($relativePath)
- {
- // served by web server directly, so must be a public path
- return PIWIK_DOCUMENT_ROOT . "/" . $relativePath;
- }
-
- /**
- * Indicates if the provided JavaScript content has already been minified or not.
- * The heuristic is based on a custom ratio : (size of file) / (number of lines).
- * The threshold (100) has been found empirically on existing files :
- * - the ratio never exceeds 50 for non-minified content and
- * - it never goes under 150 for minified content.
- *
- * @param string $content Contents of the JavaScript file
- * @return boolean
- */
- private static function isMinifiedJs ( $content )
- {
- $lineCount = substr_count($content, "\n");
- if ( $lineCount == 0 )
- {
- return true;
- }
-
- $contentSize = strlen($content);
-
- $ratio = $contentSize / $lineCount;
-
- return $ratio > self::MINIFIED_JS_RATIO;
- }
-
- /**
- * Sort files according to priority order. Duplicates are also removed.
- *
- * @param array $priorityOrder Ordered array of paths (first to last) serving as buckets
- * @param array $files Unsorted array of files
- * @return array
- */
- public static function prioritySort($priorityOrder, $files)
- {
- $newFiles = array();
- foreach($priorityOrder as $filePattern)
- {
- $newFiles = array_merge($newFiles, preg_grep('~^' . $filePattern . '~', $files));
- }
- return array_keys(array_flip($newFiles));
- }
+ }
+
+ /**
+ * Returns individual JS file inclusion directive(s) using the markup <script>
+ *
+ * @return string
+ */
+ private static function getIndividualJsIncludes()
+ {
+ $jsFiles = self::getJsFiles();
+ $jsIncludeString = '';
+ foreach ($jsFiles as $jsFile) {
+ self::validateJsFile($jsFile);
+ $jsIncludeString = $jsIncludeString . sprintf(self::JS_IMPORT_DIRECTIVE, $jsFile);
+ }
+ return $jsIncludeString;
+ }
+
+ /**
+ * Returns required JS files
+ *
+ * @return Array
+ */
+ private static function getJsFiles()
+ {
+ $jsFiles = array();
+ Piwik_PostEvent(self::JS_IMPORT_EVENT, $jsFiles);
+ $jsFiles = self::sortJsFiles($jsFiles);
+ return $jsFiles;
+ }
+
+ /**
+ * Ensure core JS (jQuery etc.) are loaded in a particular order regardless of the order that plugins are loaded.
+ *
+ * @param array $jsFiles Arry of JavaScript files
+ * @return array
+ */
+ private static function sortJsFiles($jsFiles)
+ {
+ $priorityJsOrdered = array(
+ 'libs/jquery/jquery.js',
+ 'libs/jquery/jquery-ui.js',
+ 'libs/',
+ 'themes/default/common.js',
+ 'themes/default/',
+ 'plugins/CoreHome/templates/broadcast.js',
+ 'plugins/',
+ );
+
+ return self::prioritySort($priorityJsOrdered, $jsFiles);
+ }
+
+ /**
+ * Check the validity of the js file
+ *
+ * @param string $jsFile JavaScript file name
+ * @return boolean
+ * @throws Exception if js file is not valid
+ */
+ private static function validateJsFile($jsFile)
+ {
+ if (!self::assetIsReadable($jsFile)) {
+ throw new Exception("The js asset with 'src' = " . $jsFile . " is not readable");
+ }
+ }
+
+ /**
+ * Returns the global option disable_merged_assets
+ *
+ * @return string
+ */
+ private static function getDisableMergedAssets()
+ {
+ return Piwik_Config::getInstance()->Debug['disable_merged_assets'];
+ }
+
+ /**
+ * Returns the css merged file absolute location.
+ * If there is none, the generation process will be triggered.
+ *
+ * @return string The absolute location of the css merged file
+ */
+ public static function getMergedCssFileLocation()
+ {
+ $isGenerated = self::isGenerated(self::MERGED_CSS_FILE);
+
+ if (!$isGenerated) {
+ self::generateMergedCssFile();
+ }
+
+ return self::getAbsoluteMergedFileLocation(self::MERGED_CSS_FILE);
+ }
+
+ /**
+ * Returns the js merged file absolute location.
+ * If there is none, the generation process will be triggered.
+ *
+ * @return string The absolute location of the js merged file
+ */
+ public static function getMergedJsFileLocation()
+ {
+ $isGenerated = self::isGenerated(self::MERGED_JS_FILE);
+
+ if (!$isGenerated) {
+ self::generateMergedJsFile();
+ }
+
+ return self::getAbsoluteMergedFileLocation(self::MERGED_JS_FILE);
+ }
+
+ /**
+ * Check if the provided merged file is generated
+ *
+ * @param string $filename filename of the merged asset
+ * @return boolean true is file exists and is readable, false otherwise
+ */
+ private static function isGenerated($filename)
+ {
+ return is_readable(self::getAbsoluteMergedFileLocation($filename));
+ }
+
+ /**
+ * Removes the previous merged file if it exists.
+ * Also tries to remove compressed version of the merged file.
+ *
+ * @param string $filename filename of the merged asset
+ * @see Piwik::serveStaticFile()
+ * @throws Exception if the file couldn't be deleted
+ */
+ private static function removeMergedAsset($filename)
+ {
+ $isGenerated = self::isGenerated($filename);
+
+ if ($isGenerated) {
+ if (!unlink(self::getAbsoluteMergedFileLocation($filename))) {
+ throw Exception("Unable to delete merged file : " . $filename . ". Please delete the file and refresh");
+ }
+
+ // Tries to remove compressed version of the merged file.
+ // See Piwik::serveStaticFile() for more info on static file compression
+ $compressedFileLocation = PIWIK_USER_PATH . Piwik::COMPRESSED_FILE_LOCATION . $filename;
+
+ @unlink($compressedFileLocation . ".deflate");
+ @unlink($compressedFileLocation . ".gz");
+ }
+ }
+
+ /**
+ * Remove previous merged assets
+ */
+ public static function removeMergedAssets()
+ {
+ self::removeMergedAsset(self::MERGED_CSS_FILE);
+ self::removeMergedAsset(self::MERGED_JS_FILE);
+ }
+
+ /**
+ * Check if asset is readable
+ *
+ * @param string $relativePath Relative path to file
+ * @return boolean
+ */
+ private static function assetIsReadable($relativePath)
+ {
+ return is_readable(self::getAbsoluteLocation($relativePath));
+ }
+
+ /**
+ * Check if the merged file directory exists and is writable.
+ *
+ * @return string The directory location
+ * @throws Exception if directory is not writable.
+ */
+ private static function getMergedFileDirectory()
+ {
+ $mergedFileDirectory = PIWIK_USER_PATH . '/' . self::MERGED_FILE_DIR;
+
+ if (!is_dir($mergedFileDirectory)) {
+ Piwik_Common::mkdir($mergedFileDirectory);
+ }
+
+ if (!is_writable($mergedFileDirectory)) {
+ throw new Exception("Directory " . $mergedFileDirectory . " has to be writable.");
+ }
+
+ return $mergedFileDirectory;
+ }
+
+ /**
+ * Builds the absolute location of the requested merged file
+ *
+ * @param string $mergedFile Name of the merge file
+ * @return string absolute location of the merged file
+ */
+ private static function getAbsoluteMergedFileLocation($mergedFile)
+ {
+ return self::getMergedFileDirectory() . $mergedFile;
+ }
+
+ /**
+ * Returns the full path of an asset file
+ *
+ * @param string $relativePath Relative path to file
+ * @return string
+ */
+ private static function getAbsoluteLocation($relativePath)
+ {
+ // served by web server directly, so must be a public path
+ return PIWIK_DOCUMENT_ROOT . "/" . $relativePath;
+ }
+
+ /**
+ * Indicates if the provided JavaScript content has already been minified or not.
+ * The heuristic is based on a custom ratio : (size of file) / (number of lines).
+ * The threshold (100) has been found empirically on existing files :
+ * - the ratio never exceeds 50 for non-minified content and
+ * - it never goes under 150 for minified content.
+ *
+ * @param string $content Contents of the JavaScript file
+ * @return boolean
+ */
+ private static function isMinifiedJs($content)
+ {
+ $lineCount = substr_count($content, "\n");
+ if ($lineCount == 0) {
+ return true;
+ }
+
+ $contentSize = strlen($content);
+
+ $ratio = $contentSize / $lineCount;
+
+ return $ratio > self::MINIFIED_JS_RATIO;
+ }
+
+ /**
+ * Sort files according to priority order. Duplicates are also removed.
+ *
+ * @param array $priorityOrder Ordered array of paths (first to last) serving as buckets
+ * @param array $files Unsorted array of files
+ * @return array
+ */
+ public static function prioritySort($priorityOrder, $files)
+ {
+ $newFiles = array();
+ foreach ($priorityOrder as $filePattern) {
+ $newFiles = array_merge($newFiles, preg_grep('~^' . $filePattern . '~', $files));
+ }
+ return array_keys(array_flip($newFiles));
+ }
}
diff --git a/core/Auth.php b/core/Auth.php
index 5e58eca147..252a31afbd 100644
--- a/core/Auth.php
+++ b/core/Auth.php
@@ -15,20 +15,21 @@
* @package Piwik
* @subpackage Piwik_Auth
*/
-interface Piwik_Auth {
- /**
- * Authentication module's name, e.g., "Login"
- *
- * @return string
- */
- public function getName();
+interface Piwik_Auth
+{
+ /**
+ * Authentication module's name, e.g., "Login"
+ *
+ * @return string
+ */
+ public function getName();
- /**
- * Authenticates user
- *
- * @return Piwik_Auth_Result
- */
- public function authenticate();
+ /**
+ * Authenticates user
+ *
+ * @return Piwik_Auth_Result
+ */
+ public function authenticate();
}
/**
@@ -41,32 +42,32 @@ interface Piwik_Auth {
*/
class Piwik_Auth_Result extends Zend_Auth_Result
{
- /**
- * token_auth parameter used to authenticate in the API
- *
- * @var string
- */
- protected $_token_auth = null;
-
- const SUCCESS_SUPERUSER_AUTH_CODE = 42;
+ /**
+ * token_auth parameter used to authenticate in the API
+ *
+ * @var string
+ */
+ protected $_token_auth = null;
+
+ const SUCCESS_SUPERUSER_AUTH_CODE = 42;
+
+ /**
+ * Constructor for Piwik_Auth_Result
+ *
+ * @param int $code
+ * @param string $login identity
+ * @param string $token_auth
+ * @param array $messages
+ */
+ public function __construct($code, $login, $token_auth, array $messages = array())
+ {
+ // Piwik_Auth_Result::SUCCESS_SUPERUSER_AUTH_CODE, Piwik_Auth_Result::SUCCESS, Piwik_Auth_Result::FAILURE
+ $this->_code = (int)$code;
+ $this->_identity = $login;
+ $this->_messages = $messages;
+ $this->_token_auth = $token_auth;
+ }
- /**
- * Constructor for Piwik_Auth_Result
- *
- * @param int $code
- * @param string $login identity
- * @param string $token_auth
- * @param array $messages
- */
- public function __construct($code, $login, $token_auth, array $messages = array())
- {
- // Piwik_Auth_Result::SUCCESS_SUPERUSER_AUTH_CODE, Piwik_Auth_Result::SUCCESS, Piwik_Auth_Result::FAILURE
- $this->_code = (int)$code;
- $this->_identity = $login;
- $this->_messages = $messages;
- $this->_token_auth = $token_auth;
- }
-
/**
* Returns the token_auth to authenticate the current user in the API
*
@@ -74,6 +75,6 @@ class Piwik_Auth_Result extends Zend_Auth_Result
*/
public function getTokenAuth()
{
- return $this->_token_auth;
+ return $this->_token_auth;
}
}
diff --git a/core/CacheFile.php b/core/CacheFile.php
index 2ee1d11176..336945db91 100644
--- a/core/CacheFile.php
+++ b/core/CacheFile.php
@@ -21,155 +21,155 @@
*/
class Piwik_CacheFile
{
- /**
- * @var string
- */
- protected $cachePath;
- /**
- * @var
- */
- protected $cachePrefix;
-
- /**
- * Minimum enforced TTL in seconds
- */
- const MINIMUM_TTL = 60;
-
- /**
- * @param string $directory directory to use
- * @param int TTL
- */
- function __construct($directory, $timeToLiveInSeconds = 300)
- {
- $this->cachePath = PIWIK_USER_PATH . '/tmp/cache/' . $directory . '/';
- if($timeToLiveInSeconds < self::MINIMUM_TTL) {
- $timeToLiveInSeconds = self::MINIMUM_TTL;
- }
- $this->ttl = $timeToLiveInSeconds;
- }
-
- /**
- * Function to fetch a cache entry
- *
- * @param string $id The cache entry ID
- * @return array|bool False on error, or array the cache content
- */
- function get($id)
- {
- if(empty($id)) {
- return false;
- }
- $id = $this->cleanupId($id);
-
- $cache_complete = false;
- $content = '';
- $expires_on = false;
-
- // We are assuming that most of the time cache will exists
- $ok = @include($this->cachePath . $id . '.php');
-
- if ($ok && $cache_complete == true) {
-
- if(empty($expires_on)
- || $expires_on < time()) {
- return false;
- }
- return $content;
- }
-
- return false;
- }
-
- private function getExpiresTime()
- {
- return time() + $this->ttl;
- }
-
- protected function cleanupId($id)
- {
- if(!Piwik_Common::isValidFilename($id)) {
- throw new Exception("Invalid cache ID request $id");
- }
- return $id;
- }
-
- /**
- * A function to store content a cache entry.
- *
- * @param string $id The cache entry ID
- * @param array $content The cache content
- * @return bool True if the entry was succesfully stored
- */
- function set($id, $content)
- {
- if(empty($id)) {
- return false;
- }
- if( !is_dir($this->cachePath))
- {
- Piwik_Common::mkdir($this->cachePath);
- }
- if (!is_writable($this->cachePath)) {
- return false;
- }
- $id = $this->cleanupId($id);
-
- $id = $this->cachePath . $id . '.php';
-
- $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 .= "?".">";
-
- // Write cache to a temp file, then rename it, overwriting the old cache
- // On *nix systems this should guarantee atomicity
- $tmp_filename = tempnam($this->cachePath, 'tmp_');
- @chmod($tmp_filename, 0640);
- if ($fp = @fopen($tmp_filename, 'wb')) {
- @fwrite ($fp, $cache_literal, strlen($cache_literal));
- @fclose ($fp);
-
- if (!@rename($tmp_filename, $id)) {
- // On some systems rename() doesn't overwrite destination
- @unlink($id);
- if (!@rename($tmp_filename, $id)) {
- // Make sure that no temporary file is left over
- // if the destination is not writable
- @unlink($tmp_filename);
- }
- }
- return true;
- }
- return false;
- }
-
- /**
- * A function to delete a single cache entry
- *
- * @param string $id The cache entry ID
- * @return bool True if the entry was succesfully deleted
- */
- function delete($id)
- {
- if(empty($id)) {
- return false;
- }
- $id = $this->cleanupId($id);
-
- $filename = $this->cachePath . $id . '.php';
- if (file_exists($filename)) {
- @unlink ($filename);
- return true;
- }
- return false;
- }
-
- /**
- * A function to delete all cache entries in the directory
- */
- function deleteAll()
- {
- Piwik::unlinkRecursive($this->cachePath, $deleteRootToo = false);
- }
+ /**
+ * @var string
+ */
+ protected $cachePath;
+ /**
+ * @var
+ */
+ protected $cachePrefix;
+
+ /**
+ * Minimum enforced TTL in seconds
+ */
+ const MINIMUM_TTL = 60;
+
+ /**
+ * @param string $directory directory to use
+ * @param int TTL
+ */
+ function __construct($directory, $timeToLiveInSeconds = 300)
+ {
+ $this->cachePath = PIWIK_USER_PATH . '/tmp/cache/' . $directory . '/';
+ if ($timeToLiveInSeconds < self::MINIMUM_TTL) {
+ $timeToLiveInSeconds = self::MINIMUM_TTL;
+ }
+ $this->ttl = $timeToLiveInSeconds;
+ }
+
+ /**
+ * Function to fetch a cache entry
+ *
+ * @param string $id The cache entry ID
+ * @return array|bool False on error, or array the cache content
+ */
+ function get($id)
+ {
+ if (empty($id)) {
+ return false;
+ }
+ $id = $this->cleanupId($id);
+
+ $cache_complete = false;
+ $content = '';
+ $expires_on = false;
+
+ // We are assuming that most of the time cache will exists
+ $ok = @include($this->cachePath . $id . '.php');
+
+ if ($ok && $cache_complete == true) {
+
+ if (empty($expires_on)
+ || $expires_on < time()
+ ) {
+ return false;
+ }
+ return $content;
+ }
+
+ return false;
+ }
+
+ private function getExpiresTime()
+ {
+ return time() + $this->ttl;
+ }
+
+ protected function cleanupId($id)
+ {
+ if (!Piwik_Common::isValidFilename($id)) {
+ throw new Exception("Invalid cache ID request $id");
+ }
+ return $id;
+ }
+
+ /**
+ * A function to store content a cache entry.
+ *
+ * @param string $id The cache entry ID
+ * @param array $content The cache content
+ * @return bool True if the entry was succesfully stored
+ */
+ function set($id, $content)
+ {
+ if (empty($id)) {
+ return false;
+ }
+ if (!is_dir($this->cachePath)) {
+ Piwik_Common::mkdir($this->cachePath);
+ }
+ if (!is_writable($this->cachePath)) {
+ return false;
+ }
+ $id = $this->cleanupId($id);
+
+ $id = $this->cachePath . $id . '.php';
+
+ $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 .= "?" . ">";
+
+ // Write cache to a temp file, then rename it, overwriting the old cache
+ // On *nix systems this should guarantee atomicity
+ $tmp_filename = tempnam($this->cachePath, 'tmp_');
+ @chmod($tmp_filename, 0640);
+ if ($fp = @fopen($tmp_filename, 'wb')) {
+ @fwrite($fp, $cache_literal, strlen($cache_literal));
+ @fclose($fp);
+
+ if (!@rename($tmp_filename, $id)) {
+ // On some systems rename() doesn't overwrite destination
+ @unlink($id);
+ if (!@rename($tmp_filename, $id)) {
+ // Make sure that no temporary file is left over
+ // if the destination is not writable
+ @unlink($tmp_filename);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * A function to delete a single cache entry
+ *
+ * @param string $id The cache entry ID
+ * @return bool True if the entry was succesfully deleted
+ */
+ function delete($id)
+ {
+ if (empty($id)) {
+ return false;
+ }
+ $id = $this->cleanupId($id);
+
+ $filename = $this->cachePath . $id . '.php';
+ if (file_exists($filename)) {
+ @unlink($filename);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * A function to delete all cache entries in the directory
+ */
+ function deleteAll()
+ {
+ Piwik::unlinkRecursive($this->cachePath, $deleteRootToo = false);
+ }
}
diff --git a/core/Common.php b/core/Common.php
index e728675c2b..b2bdbafd82 100644
--- a/core/Common.php
+++ b/core/Common.php
@@ -20,1668 +20,1520 @@
*/
class Piwik_Common
{
- /**
- * Const used to map the referer type to an integer in the log_visit table
- */
- const REFERER_TYPE_DIRECT_ENTRY = 1;
- const REFERER_TYPE_SEARCH_ENGINE = 2;
- const REFERER_TYPE_WEBSITE = 3;
- const REFERER_TYPE_CAMPAIGN = 6;
-
- /**
- * Flag used with htmlspecialchar
- * See php.net/htmlspecialchars
- */
- const HTML_ENCODING_QUOTE_STYLE = ENT_QUOTES;
-
-/*
- * Database
- */
-
- /**
- * Hashes a string into an integer which should be very low collision risks
- * @param string $string String to hash
- * @return int Resulting int hash
- */
- static public function hashStringToInt($string)
- {
- $stringHash = substr(md5($string), 0, 8);
- return base_convert($stringHash, 16, 10);
- }
-
- static public $cachedTablePrefix = null;
-
- /**
- * Returns the table name prefixed by the table prefix.
- * Works in both Tracker and UI mode.
- *
- * @param string $table The table name to prefix, ie "log_visit"
- * @return string The table name prefixed, ie "piwik-production_log_visit"
- */
- static public function prefixTable($table)
- {
- if(is_null(self::$cachedTablePrefix))
- {
- self::$cachedTablePrefix = Piwik_Config::getInstance()->database['tables_prefix'];
- }
- return self::$cachedTablePrefix . $table;
- }
-
- /**
- * Returns an array containing the prefixed table names of every passed argument.
- *
- * @param string ... The table names to prefix, ie "log_visit"
- * @return array The prefixed names in an array.
- */
- static public function prefixTables()
- {
- $result = array();
- foreach (func_get_args() as $table)
- {
- $result[] = self::prefixTable($table);
- }
- return $result;
- }
-
- /**
- * Returns the table name, after removing the table prefix
- *
- * @param string $table
- * @return string
- */
- static public function unprefixTable($table)
- {
- static $prefixTable = null;
- if(is_null($prefixTable))
- {
- $prefixTable = Piwik_Config::getInstance()->database['tables_prefix'];
- }
- if(empty($prefixTable)
- || strpos($table, $prefixTable) !== 0)
- {
- return $table;
- }
- $count = 1;
- return str_replace($prefixTable, '', $table, $count);
- }
-
-/*
- * Tracker
- */
- static public function isGoalPluginEnabled()
- {
- return Piwik_PluginsManager::getInstance()->isPluginActivated('Goals');
- }
-
-/*
- * URLs
- */
-
- /**
- * Returns the path and query part from a URL.
- * Eg. http://piwik.org/test/index.php?module=CoreHome will return /test/index.php?module=CoreHome
- *
- * @param string $url either http://piwik.org/test or /
- * @return string
- */
- static public function getPathAndQueryFromUrl($url)
- {
- $parsedUrl = parse_url( $url );
- $result = '';
- if(isset($parsedUrl['path']))
- {
- $result .= substr($parsedUrl['path'], 1);
- }
- if(isset($parsedUrl['query']))
- {
- $result .= '?'.$parsedUrl['query'];
- }
- return $result;
- }
-
- /**
- * Returns the value of a GET parameter $parameter in an URL query $urlQuery
- *
- * @param string $urlQuery result of parse_url()['query'] and htmlentitied (& is &amp;) eg. module=test&amp;action=toto or ?page=test
- * @param string $parameter
- * @return string|bool Parameter value if found (can be the empty string!), null if not found
- */
- static public function getParameterFromQueryString( $urlQuery, $parameter)
- {
- $nameToValue = self::getArrayFromQueryString($urlQuery);
- if(isset($nameToValue[$parameter]))
- {
- return $nameToValue[$parameter];
- }
- return null;
- }
-
- /**
- * Returns an URL query string in an array format
- *
- * @param string $urlQuery
- * @return array array( param1=> value1, param2=>value2)
- */
- static public function getArrayFromQueryString( $urlQuery )
- {
- if(strlen($urlQuery) == 0)
- {
- return array();
- }
- if($urlQuery[0] == '?')
- {
- $urlQuery = substr($urlQuery, 1);
- }
- $separator = '&';
-
- $urlQuery = $separator . $urlQuery;
- // $urlQuery = str_replace(array('%20'), ' ', $urlQuery);
- $refererQuery = trim($urlQuery);
-
- $values = explode($separator, $refererQuery);
-
- $nameToValue = array();
-
- foreach($values as $value)
- {
- $pos = strpos($value, '=');
- if($pos !== false)
- {
- $name = substr($value, 0, $pos);
- $value = substr($value, $pos+1);
- if ($value === false)
- {
- $value = '';
- }
- }
- else
- {
- $name = $value;
- $value = false;
- }
- if(!empty($name))
- {
- $name = Piwik_Common::sanitizeInputValue($name);
- }
- if(!empty($value))
- {
- $value = Piwik_Common::sanitizeInputValue($value);
- }
-
- // if array without indexes
- $count = 0;
- $tmp = preg_replace('/(\[|%5b)(]|%5d)$/i', '', $name, -1, $count);
- if(!empty($tmp) && $count)
- {
- $name = $tmp;
- if( isset($nameToValue[$name]) == false || is_array($nameToValue[$name]) == false )
- {
- $nameToValue[$name] = array();
- }
- array_push($nameToValue[$name], $value);
- }
- else if(!empty($name))
- {
- $nameToValue[$name] = $value;
- }
- }
- return $nameToValue;
- }
-
- /**
- * Builds a URL from the result of parse_url function
- * Copied from the PHP comments at http://php.net/parse_url
- * @param array $parsed
- * @return bool|string
- */
- static public function getParseUrlReverse($parsed)
- {
- if (!is_array($parsed))
- {
- return false;
- }
-
- $uri = !empty($parsed['scheme']) ? $parsed['scheme'].':'.(!strcasecmp($parsed['scheme'], 'mailto') ? '' : '//') : '';
- $uri .= !empty($parsed['user']) ? $parsed['user'].(!empty($parsed['pass']) ? ':'.$parsed['pass'] : '').'@' : '';
- $uri .= !empty($parsed['host']) ? $parsed['host'] : '';
- $uri .= !empty($parsed['port']) ? ':'.$parsed['port'] : '';
-
- if (!empty($parsed['path']))
- {
- $uri .= (!strncmp($parsed['path'], '/', 1))
- ? $parsed['path']
- : ((!empty($uri) ? '/' : '' ) . $parsed['path']);
- }
-
- $uri .= !empty($parsed['query']) ? '?'.$parsed['query'] : '';
- $uri .= !empty($parsed['fragment']) ? '#'.$parsed['fragment'] : '';
- return $uri;
- }
-
- /**
- * Returns true if the string passed may be a URL.
- * We don't need a precise test here because the value comes from the website
- * tracked source code and the URLs may look very strange.
- *
- * @param string $url
- * @return bool
- */
- static public function isLookLikeUrl( $url )
- {
- return preg_match('~^(ftp|news|http|https)?://(.*)$~D', $url, $matches) !== 0
- && strlen($matches[2]) > 0;
- }
-
-/*
- * File operations
- */
-
- /**
- * ending WITHOUT slash
- *
- * @return string
- */
- static public function getPathToPiwikRoot()
- {
- return realpath( dirname(__FILE__). "/.." );
- }
-
- /**
- * Create directory if permitted
- *
- * @param string $path
- * @param bool $denyAccess
- */
- static public function mkdir( $path, $denyAccess = true )
- {
- if(!is_dir($path))
- {
- // the mode in mkdir is modified by the current umask
- @mkdir($path, $mode = 0755, $recursive = true);
- }
-
- // try to overcome restrictive umask (mis-)configuration
- if(!is_writable($path))
- {
- @chmod($path, 0755);
- if(!is_writable($path))
- {
- @chmod($path, 0775);
-
- // enough! we're not going to make the directory world-writeable
- }
- }
-
- if($denyAccess)
- {
- self::createHtAccess($path, $overwrite = false);
- }
- }
-
- /**
- * Create .htaccess file in specified directory
- *
- * Apache-specific; for IIS @see web.config
- *
- * @param string $path without trailing slash
- * @param bool $overwrite whether to overwrite an existing file or not
- * @param string $content
- */
- static public function createHtAccess( $path, $overwrite = true, $content = "<Files \"*\">\n<IfModule mod_access.c>\nDeny from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nDeny from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nDeny from all\n</IfModule>\n</Files>\n" )
- {
- if(self::isApache())
- {
- $file = $path . '/.htaccess';
- if ($overwrite || !file_exists($file))
- {
- @file_put_contents($file, $content);
- }
- }
- }
-
- /**
- * Get canonicalized absolute path
- * See http://php.net/realpath
- *
- * @param string $path
- * @return string canonicalized absolute path
- */
- static public function realpath($path)
- {
- if (file_exists($path))
- {
- return realpath($path);
- }
- return $path;
- }
-
- /**
- * Returns true if the string is a valid filename
- * File names that start with a-Z or 0-9 and contain a-Z, 0-9, underscore(_), dash(-), and dot(.) will be accepted.
- * File names beginning with anything but a-Z or 0-9 will be rejected (including .htaccess for example).
- * File names containing anything other than above mentioned will also be rejected (file names with spaces won't be accepted).
- *
- * @param string $filename
- * @return bool
- *
- */
- static public function isValidFilename($filename)
- {
- return (0 !== preg_match('/(^[a-zA-Z0-9]+([a-zA-Z_0-9.-]*))$/D', $filename));
- }
-
-/*
- * String operations
- */
-
- /**
- * byte-oriented substr() - ASCII
- *
- * @param string $string
- * @param int $start
- * @param int ... optional length
- * @return string
- */
- static public function substr($string, $start)
- {
- // in case mbstring overloads substr function
- $substr = function_exists('mb_orig_substr') ? 'mb_orig_substr' : 'substr';
-
- $length = func_num_args() > 2
- ? func_get_arg(2)
- : self::strlen($string);
-
- return $substr($string, $start, $length);
- }
-
- /**
- * byte-oriented strlen() - ASCII
- *
- * @param string $string
- * @return int
- */
- static public function strlen($string)
- {
- // in case mbstring overloads strlen function
- $strlen = function_exists('mb_orig_strlen') ? 'mb_orig_strlen' : 'strlen';
- return $strlen($string);
- }
-
- /**
- * multi-byte substr() - UTF-8
- *
- * @param string $string
- * @param int $start
- * @param int ... optional length
- * @return string
- */
- static public function mb_substr($string, $start)
- {
- $length = func_num_args() > 2
- ? func_get_arg(2)
- : self::mb_strlen($string);
-
- if (function_exists('mb_substr'))
- {
- return mb_substr($string, $start, $length, 'UTF-8');
- }
-
- return substr($string, $start, $length);
- }
-
- /**
- * multi-byte strlen() - UTF-8
- *
- * @param string $string
- * @return int
- */
- static public function mb_strlen($string)
- {
- if (function_exists('mb_strlen'))
- {
- return mb_strlen($string, 'UTF-8');
- }
-
- return strlen($string);
- }
-
- /**
- * multi-byte strtolower() - UTF-8
- *
- * @param string $string
- * @return string
- */
- static public function mb_strtolower($string)
- {
- if (function_exists('mb_strtolower'))
- {
- return mb_strtolower($string, 'UTF-8');
- }
-
- return strtolower($string);
- }
-
-/*
- * Escaping input
- */
-
- /**
- * Returns the variable after cleaning operations.
- * NB: The variable still has to be escaped before going into a SQL Query!
- *
- * If an array is passed the cleaning is done recursively on all the sub-arrays.
- * The array's keys are filtered as well!
- *
- * How this method works:
- * - The variable returned has been htmlspecialchars to avoid the XSS security problem.
- * - The single quotes are not protected so "Piwik's amazing" will still be "Piwik's amazing".
- *
- * - Transformations are:
- * - '&' (ampersand) becomes '&amp;'
- * - '"'(double quote) becomes '&quot;'
- * - '<' (less than) becomes '&lt;'
- * - '>' (greater than) becomes '&gt;'
- * - It handles the magic_quotes setting.
- * - A non string value is returned without modification
- *
- * @param mixed $value The variable to be cleaned
- * @param bool $alreadyStripslashed
- * @throws Exception
- * @return mixed The variable after cleaning
- */
- static public function sanitizeInputValues($value, $alreadyStripslashed = false)
- {
- if(is_numeric($value))
- {
- return $value;
- }
- elseif(is_string($value))
- {
- $value = self::sanitizeInputValue($value);
-
- if(!$alreadyStripslashed) // a JSON array was already stripslashed, don't do it again for each value
- {
- $value = self::undoMagicQuotes($value);
- }
- }
- elseif (is_array($value))
- {
- foreach (array_keys($value) as $key)
- {
- $newKey = $key;
- $newKey = self::sanitizeInputValues($newKey, $alreadyStripslashed);
- if ($key != $newKey)
- {
- $value[$newKey] = $value[$key];
- unset($value[$key]);
- }
-
- $value[$newKey] = self::sanitizeInputValues($value[$newKey], $alreadyStripslashed);
- }
- }
- elseif( !is_null($value)
- && !is_bool($value))
- {
- throw new Exception("The value to escape has not a supported type. Value = ".var_export($value, true));
- }
- return $value;
- }
-
- /**
- * Sanitize a single input value
- *
- * @param string $value
- * @return string sanitized input
- */
- static public function sanitizeInputValue($value)
- {
- // $_GET and $_REQUEST already urldecode()'d
- // decode
- // note: before php 5.2.7, htmlspecialchars() double encodes &#x hex items
- $value = html_entity_decode($value, Piwik_Common::HTML_ENCODING_QUOTE_STYLE, 'UTF-8');
-
- // filter
- $value = str_replace(array("\n", "\r", "\0"), '', $value);
-
- // escape
- $tmp = @htmlspecialchars( $value, self::HTML_ENCODING_QUOTE_STYLE, 'UTF-8' );
-
- // note: php 5.2.5 and above, htmlspecialchars is destructive if input is not UTF-8
- if($value != '' && $tmp == '')
- {
- // convert and escape
- $value = utf8_encode($value);
- $tmp = htmlspecialchars( $value, self::HTML_ENCODING_QUOTE_STYLE, 'UTF-8' );
- }
- return $tmp;
- }
-
- /**
- * Unsanitize a single input value
- *
- * @param string $value
- * @return string unsanitized input
- */
- static public function unsanitizeInputValue($value)
- {
- return htmlspecialchars_decode($value, self::HTML_ENCODING_QUOTE_STYLE);
- }
-
- /**
- * Unsanitize one or more values.
- *
- * @param string|array $value
- * @return string|array unsanitized input
- */
- static public function unsanitizeInputValues($value)
- {
- if (is_array($value))
- {
- $result = array();
- foreach ($value as $key => $arrayValue)
- {
- $result[$key] = self::unsanitizeInputValues($arrayValue);
- }
- return $result;
- }
- else
- {
- return self::unsanitizeInputValue($value);
- }
- }
-
- /**
- * Undo the damage caused by magic_quotes; deprecated in php 5.3 but not removed until php 5.4
- *
- * @param string
- * @return string modified or not
- */
- static public function undoMagicQuotes($value)
- {
- return version_compare(PHP_VERSION, '5.4', '<')
- && get_magic_quotes_gpc()
- ? stripslashes($value)
- : $value;
- }
-
- /**
- * Returns a sanitized variable value from the $_GET and $_POST superglobal.
- * If the variable doesn't have a value or an empty value, returns the defaultValue if specified.
- * If the variable doesn't have neither a value nor a default value provided, an exception is raised.
- *
- * @see sanitizeInputValues() for the applied sanitization
- *
- * @param string $varName name of the variable
- * @param string $varDefault default value. If '', and if the type doesn't match, exit() !
- * @param string $varType Expected type, the value must be one of the following: array, int, integer, string, json
- * @param array $requestArrayToUse
- *
- * @throws Exception if the variable type is not known
- * or if the variable we want to read doesn't have neither a value nor a default value specified
- *
- * @return mixed The variable after cleaning
- */
- static public function getRequestVar($varName, $varDefault = null, $varType = null, $requestArrayToUse = null)
- {
- if(is_null($requestArrayToUse))
- {
- $requestArrayToUse = $_GET + $_POST;
- }
- $varDefault = self::sanitizeInputValues( $varDefault );
- if($varType === 'int')
- {
- // settype accepts only integer
- // 'int' is simply a shortcut for 'integer'
- $varType = 'integer';
- }
-
- // there is no value $varName in the REQUEST so we try to use the default value
- if(empty($varName)
- || !isset($requestArrayToUse[$varName])
- || ( !is_array($requestArrayToUse[$varName])
- && strlen($requestArrayToUse[$varName]) === 0
- )
- )
- {
- if( is_null($varDefault))
- {
- throw new Exception("The parameter '$varName' isn't set in the Request, and a default value wasn't provided." );
- }
- else
- {
- if( !is_null($varType)
- && in_array($varType, array('string', 'integer', 'array'))
- )
- {
- settype($varDefault, $varType);
- }
- return $varDefault;
- }
- }
-
- // Normal case, there is a value available in REQUEST for the requested varName:
-
- // we deal w/ json differently
- if ($varType == 'json')
- {
- $value = self::undoMagicQuotes($requestArrayToUse[$varName]);
- $value = Piwik_Common::json_decode($value, $assoc = true);
- return self::sanitizeInputValues($value, $alreadyStripslashed = true);
- }
-
- $value = self::sanitizeInputValues( $requestArrayToUse[$varName] );
- if( !is_null($varType))
- {
- $ok = false;
-
- if($varType === 'string')
- {
- if(is_string($value)) $ok = true;
- }
- elseif($varType === 'integer')
- {
- if($value == (string)(int)$value) $ok = true;
- }
- elseif($varType === 'float')
- {
- if($value == (string)(float)$value) $ok = true;
- }
- elseif($varType === 'array')
- {
- if(is_array($value)) $ok = true;
- }
- else
- {
- throw new Exception("\$varType specified is not known. It should be one of the following: array, int, integer, float, string");
- }
-
- // The type is not correct
- if($ok === false)
- {
- if($varDefault === null)
- {
- throw new Exception("The parameter '$varName' doesn't have a correct type, and a default value wasn't provided.");
- }
- // we return the default value with the good type set
- else
- {
- settype($varDefault, $varType);
- return $varDefault;
- }
- }
- settype($value, $varType);
- }
- return $value;
- }
-
-/*
- * Generating unique strings
- */
-
- /**
- * Returns a 32 characters long uniq ID
- *
- * @return string 32 chars
- */
- static public function generateUniqId()
- {
- return md5(uniqid(rand(), true));
- }
-
- /**
- * Get salt from [superuser] section
- *
- * @return string
- */
- static public function getSalt()
- {
- static $salt = null;
- if(is_null($salt))
- {
- $salt = @Piwik_Config::getInstance()->superuser['salt'];
- }
- return $salt;
- }
-
- /**
- * Configureable hash() algorithm (defaults to md5)
- *
- * @param string $str String to be hashed
- * @param bool $raw_output
- * @return string Hash string
- */
- static function hash($str, $raw_output = false)
- {
- static $hashAlgorithm = null;
- if(is_null($hashAlgorithm))
- {
- $hashAlgorithm = @Piwik_Config::getInstance()->General['hash_algorithm'];
- }
-
- if($hashAlgorithm)
- {
- $hash = @hash($hashAlgorithm, $str, $raw_output);
- if($hash !== false)
- return $hash;
- }
-
- return md5($str, $raw_output);
- }
-
- /**
- * Generate random string
- *
- * @param int $length string length
- * @param string $alphabet characters allowed in random string
- * @return string random string with given length
- */
- public static function getRandomString($length = 16, $alphabet = "abcdefghijklmnoprstuvwxyz0123456789")
- {
- $chars = $alphabet;
- $str = '';
-
- list($usec, $sec) = explode(" ", microtime());
- $seed = ((float)$sec+(float)$usec)*100000;
- mt_srand($seed);
-
- for($i = 0; $i < $length; $i++)
- {
- $rand_key = mt_rand(0, strlen($chars)-1);
- $str .= substr($chars, $rand_key, 1);
- }
- return str_shuffle($str);
- }
-
-/*
- * Conversions
- */
-
- /**
- * Convert hexadecimal representation into binary data.
- * !! Will emit warning if input string is not hex!!
- *
- * @see http://php.net/bin2hex
- *
- * @param string $str Hexadecimal representation
- * @return string
- */
- static public function hex2bin($str)
- {
- return pack("H*" , $str);
- }
-
- /**
- * This function will convert the input string to the binary representation of the ID
- * but it will throw an Exception if the specified input ID is not correct
- *
- * This is used when building segments containing visitorId which could be an invalid string
- * therefore throwing Unexpected PHP error [pack(): Type H: illegal hex digit i] severity [E_WARNING]
- *
- * It would be simply to silent fail the pack() call above but in all other cases, we don't expect an error,
- * so better be safe and get the php error when something unexpected is happening
- * @param string $id
- * @throws Exception
- * @return string binary string
- */
- static public function convertVisitorIdToBin($id)
- {
- if(strlen($id) !== Piwik_Tracker::LENGTH_HEX_ID_STRING
- || @bin2hex(self::hex2bin($id)) != $id)
- {
- throw new Exception("visitorId is expected to be a ".Piwik_Tracker::LENGTH_HEX_ID_STRING." hex char string");
- }
- return self::hex2bin($id);
- }
-
- /**
- * Convert IP address (in network address format) to presentation format.
- * This is a backward compatibility function for code that only expects
- * IPv4 addresses (i.e., doesn't support IPv6).
- *
- * @see Piwik_IP::N2P()
- *
- * This function does not support the long (or its string representation)
- * returned by the built-in ip2long() function, from Piwik 1.3 and earlier.
- *
- * @deprecated 1.4
- *
- * @param string $ip IP address in network address format
- * @return string
- */
- static public function long2ip($ip)
- {
- return Piwik_IP::long2ip($ip);
- }
-
- /**
- * Should we use the replacement json_encode/json_decode functions?
- *
- * @return bool True if broken; false otherwise
- */
- static private function useJsonLibrary()
- {
- static $useLib;
-
- if (!isset($useLib))
- {
- /*
- * 5.1.x - doesn't have json extension; we use lib/upgradephp instead
- * 5.2 to 5.2.4 - broken in various ways, including:
- *
- * @see https://bugs.php.net/bug.php?id=38680 'json_decode cannot decode basic types'
- * @see https://bugs.php.net/bug.php?id=41403 'json_decode cannot decode floats'
- * @see https://bugs.php.net/bug.php?id=42785 'json_encode outputs numbers according to locale'
- */
- $useLib = false;
- if (version_compare(PHP_VERSION, '5.2.1') < 0)
- {
- $useLib = true;
- }
- else if (version_compare(PHP_VERSION, '5.2.5') < 0)
- {
- $info = localeconv();
- $useLib = $info['decimal_point'] != '.';
- }
- }
-
- return $useLib;
- }
-
- /**
- * JSON encode wrapper
- * - missing or broken in some php 5.x versions
- *
- * @param mixed $value
- * @return string
- */
- static public function json_encode($value)
- {
- if (self::useJsonLibrary())
- {
- return _json_encode($value);
- }
-
- return @json_encode($value);
- }
-
- /**
- * JSON decode wrapper
- * - missing or broken in some php 5.x versions
- *
- * @param string $json
- * @param bool $assoc
- * @return mixed
- */
- static public function json_decode($json, $assoc = false)
- {
- if (self::useJsonLibrary())
- {
- return _json_decode($json, $assoc);
- }
-
- return json_decode($json, $assoc);
- }
-
-/*
- * DataFiles
- */
-
- /**
- * Returns list of continent codes
- *
- * @see core/DataFiles/Countries.php
- *
- * @return array Array of 3 letter continent codes
- */
- static public function getContinentsList()
- {
- require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Countries.php';
-
- $continentsList = $GLOBALS['Piwik_ContinentList'];
- return $continentsList;
- }
-
- /**
- * Returns list of valid country codes
- *
- * @see core/DataFiles/Countries.php
- *
- * @param bool $includeInternalCodes
- * @return array Array of (2 letter ISO codes => 3 letter continent code)
- */
- static public function getCountriesList($includeInternalCodes = false)
- {
- require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Countries.php';
-
- $countriesList = $GLOBALS['Piwik_CountryList'];
- $extras = $GLOBALS['Piwik_CountryList_Extras'];
-
- if($includeInternalCodes)
- {
- return array_merge($countriesList, $extras);
- }
- return $countriesList;
- }
-
- /**
- * Returns list of valid language codes
- *
- * @see core/DataFiles/Languages.php
- *
- * @return array Array of 2 letter ISO codes => Language name (in English)
- */
- static public function getLanguagesList()
- {
- require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Languages.php';
-
- $languagesList = $GLOBALS['Piwik_LanguageList'];
- return $languagesList;
- }
-
- /**
- * Returns list of language to country mappings
- *
- * @see core/DataFiles/LanguageToCountry.php
- *
- * @return array Array of ( 2 letter ISO language codes => 2 letter ISO country codes )
- */
- static public function getLanguageToCountryList()
- {
- require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/LanguageToCountry.php';
-
- $languagesList = $GLOBALS['Piwik_LanguageToCountry'];
- return $languagesList;
- }
-
- /**
- * Returns list of search engines by URL
- *
- * @see core/DataFiles/SearchEngines.php
- *
- * @return array Array of ( URL => array( searchEngineName, keywordParameter, path, charset ) )
- */
- static public function getSearchEngineUrls()
- {
- require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/SearchEngines.php';
-
- $searchEngines = $GLOBALS['Piwik_SearchEngines'];
- return $searchEngines;
- }
-
- /**
- * Returns list of search engines by name
- *
- * @see core/DataFiles/SearchEngines.php
- *
- * @return array Array of ( searchEngineName => URL )
- */
- static public function getSearchEngineNames()
- {
- require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/SearchEngines.php';
-
- $searchEngines = $GLOBALS['Piwik_SearchEngines_NameToUrl'];
- return $searchEngines;
- }
-
-/*
- * Language, country, continent
- */
-
- /**
- * Returns the browser language code, eg. "en-gb,en;q=0.5"
- *
- * @param string $browserLang Optional browser language, otherwise taken from the request header
- * @return string
- */
- static public function getBrowserLanguage($browserLang = NULL)
- {
- static $replacementPatterns = array(
- // extraneous bits of RFC 3282 that we ignore
- '/(\\\\.)/', // quoted-pairs
- '/(\s+)/', // CFWcS white space
- '/(\([^)]*\))/', // CFWS comments
- '/(;q=[0-9.]+)/', // quality
-
- // found in the LANG environment variable
- '/\.(.*)/', // charset (e.g., en_CA.UTF-8)
- '/^C$/', // POSIX 'C' locale
- );
-
- if(is_null($browserLang))
- {
- $browserLang = self::sanitizeInputValues(@$_SERVER['HTTP_ACCEPT_LANGUAGE']);
- if(empty($browserLang) && self::isPhpCliMode())
- {
- $browserLang = @getenv('LANG');
- }
- }
-
- if(is_null($browserLang))
- {
- // a fallback might be to infer the language in HTTP_USER_AGENT (i.e., localized build)
- $browserLang = "";
- }
- else
- {
- // language tags are case-insensitive per HTTP/1.1 s3.10 but the region may be capitalized per ISO3166-1;
- // underscores are not permitted per RFC 4646 or 4647 (which obsolete RFC 1766 and 3066),
- // but we guard against a bad user agent which naively uses its locale
- $browserLang = strtolower(str_replace('_', '-', $browserLang));
-
- // filters
- $browserLang = preg_replace($replacementPatterns, '', $browserLang);
-
- $browserLang = preg_replace('/((^|,)chrome:.*)/', '', $browserLang, 1); // Firefox bug
- $browserLang = preg_replace('/(,)(?:en-securid,)|(?:(^|,)en-securid(,|$))/', '$1', $browserLang, 1); // unregistered language tag
-
- $browserLang = str_replace('sr-sp', 'sr-rs', $browserLang); // unofficial (proposed) code in the wild
- }
-
- return $browserLang;
- }
-
- /**
- * Returns the visitor country based on the Browser 'accepted language'
- * information, but provides a hook for geolocation via IP address.
- *
- * @param string $lang browser lang
- * @param bool $enableLanguageToCountryGuess If set to true, some assumption will be made and detection guessed more often, but accuracy could be affected
- * @param string $ip
- * @return string 2 letter ISO code
- */
- static public function getCountry( $lang, $enableLanguageToCountryGuess, $ip )
- {
- $country = null;
- Piwik_PostEvent('Common.getCountry', $country, $ip);
- if(!empty($country))
- {
- return strtolower($country);
- }
-
- if(empty($lang) || strlen($lang) < 2 || $lang == 'xx')
- {
- return 'xx';
- }
-
- $validCountries = self::getCountriesList();
- return self::extractCountryCodeFromBrowserLanguage($lang, $validCountries, $enableLanguageToCountryGuess);
- }
-
- /**
- * Returns list of valid country codes
- *
- * @param string $browserLanguage
- * @param array $validCountries Array of valid countries
- * @param bool $enableLanguageToCountryGuess (if true, will guess country based on language that lacks region information)
- * @return array Array of 2 letter ISO codes
- */
- static public function extractCountryCodeFromBrowserLanguage($browserLanguage, $validCountries, $enableLanguageToCountryGuess)
- {
- $langToCountry = self::getLanguageToCountryList();
-
- if($enableLanguageToCountryGuess)
- {
- if(preg_match('/^([a-z]{2,3})(?:,|;|$)/', $browserLanguage, $matches))
- {
- // match language (without region) to infer the country of origin
- if(array_key_exists($matches[1], $langToCountry))
- {
- return $langToCountry[$matches[1]];
- }
- }
- }
-
- if(!empty($validCountries) && preg_match_all('/[-]([a-z]{2})/', $browserLanguage, $matches, PREG_SET_ORDER))
- {
- foreach($matches as $parts)
- {
- // match location; we don't make any inferences from the language
- if(array_key_exists($parts[1], $validCountries))
- {
- return $parts[1];
- }
- }
- }
- return 'xx';
- }
-
- /**
- * Returns the visitor language based only on the Browser 'accepted language' information
- *
- * @param $browserLanguage Browser's accepted langauge header
- * @param $validLanguages array of valid language codes
- * @return string 2 letter ISO 639 code
- */
- static public function extractLanguageCodeFromBrowserLanguage($browserLanguage, $validLanguages)
- {
- // assumes language preference is sorted;
- // does not handle language-script-region tags or language range (*)
- if(!empty($validLanguages) && preg_match_all('/(?:^|,)([a-z]{2,3})([-][a-z]{2})?/', $browserLanguage, $matches, PREG_SET_ORDER))
- {
- foreach($matches as $parts)
- {
- if(count($parts) == 3)
- {
- // match locale (language and location)
- if(in_array($parts[1].$parts[2], $validLanguages))
- {
- return $parts[1].$parts[2];
- }
- }
- // match language only (where no region provided)
- if(in_array($parts[1], $validLanguages))
- {
- return $parts[1];
- }
- }
- }
- return 'xx';
- }
-
- /**
- * Returns the continent of a given country
- *
- * @param string $country 2 letters isocode
- *
- * @return string Continent (3 letters code : afr, asi, eur, amn, ams, oce)
- */
- static public function getContinent($country)
- {
- $countryList = self::getCountriesList();
- if(isset($countryList[$country]))
- {
- return $countryList[$country];
- }
- return 'unk';
- }
-
-/*
- * Campaign
- */
-
- /**
- * Returns the list of Campaign parameter names that will be read to classify
- * a visit as coming from a Campaign
- *
- * @return array array(
- * 0 => array( ... ) // campaign names parameters
- * 1 => array( ... ) // campaign keyword parameters
- * );
- */
- static public function getCampaignParameters()
- {
- $return = array(
- Piwik_Config::getInstance()->Tracker['campaign_var_name'],
- Piwik_Config::getInstance()->Tracker['campaign_keyword_var_name'],
- );
-
- foreach($return as &$list)
- {
- if(strpos($list, ',') !== false)
- {
- $list = explode(',', $list);
- }
- else
- {
- $list = array($list);
- }
- }
-
- array_walk_recursive($return, 'trim');
- return $return;
- }
-
-/*
- * Referrer
- */
-
- /**
- * Reduce URL to more minimal form. 2 letter country codes are
- * replaced by '{}', while other parts are simply removed.
- *
- * Examples:
- * www.example.com -> example.com
- * search.example.com -> example.com
- * m.example.com -> example.com
- * de.example.com -> {}.example.com
- * example.de -> example.{}
- * example.co.uk -> example.{}
- *
- * @param string $url
- * @return string
- */
- static public function getLossyUrl($url)
- {
- static $countries;
- if(!isset($countries))
- {
- $countries = implode('|', array_keys(self::getCountriesList(true)));
- }
-
- return preg_replace(
- array(
- '/^(w+[0-9]*|search)\./',
- '/(^|\.)m\./',
- '/(\.(com|org|net|co|it|edu))?\.('.$countries.')(\/|$)/',
- '/(^|\.)('.$countries.')\./',
- ),
- array(
- '',
- '$1',
- '.{}$4',
- '$1{}.',
- ),
- $url);
- }
-
- /**
- * Extracts a keyword from a raw not encoded URL.
- * Will only extract keyword if a known search engine has been detected.
- * Returns the keyword:
- * - in UTF8: automatically converted from other charsets when applicable
- * - strtolowered: "QUErY test!" will return "query test!"
- * - trimmed: extra spaces before and after are removed
- *
- * Lists of supported search engines can be found in /core/DataFiles/SearchEngines.php
- * The function returns false when a keyword couldn't be found.
- * eg. if the url is "http://www.google.com/partners.html" this will return false,
- * as the google keyword parameter couldn't be found.
- *
- * @see unit tests in /tests/core/Common.test.php
- * @param string $referrerUrl URL referer URL, eg. $_SERVER['HTTP_REFERER']
- * @return array|false false if a keyword couldn't be extracted,
- * or array(
- * 'name' => 'Google',
- * 'keywords' => 'my searched keywords')
- */
- static public function extractSearchEngineInformationFromUrl($referrerUrl)
- {
- $refererParsed = @parse_url($referrerUrl);
- $refererHost = '';
- if(isset($refererParsed['host']))
- {
- $refererHost = $refererParsed['host'];
- }
- if(empty($refererHost))
- {
- return false;
- }
- // some search engines (eg. Bing Images) use the same domain
- // as an existing search engine (eg. Bing), we must also use the url path
- $refererPath = '';
- if(isset($refererParsed['path']))
- {
- $refererPath = $refererParsed['path'];
- }
-
- // no search query
- if(!isset($refererParsed['query']))
- {
- $refererParsed['query'] = '';
- }
- $query = $refererParsed['query'];
-
- // Google Referrers URLs sometimes have the fragment which contains the keyword
- if(!empty($refererParsed['fragment']))
- {
- $query .= '&' . $refererParsed['fragment'];
- }
-
- $searchEngines = self::getSearchEngineUrls();
-
- $hostPattern = self::getLossyUrl($refererHost);
- if(array_key_exists($refererHost . $refererPath, $searchEngines))
- {
- $refererHost = $refererHost . $refererPath;
- }
- elseif(array_key_exists($hostPattern . $refererPath, $searchEngines))
- {
- $refererHost = $hostPattern . $refererPath;
- }
- elseif(array_key_exists($hostPattern, $searchEngines))
- {
- $refererHost = $hostPattern;
- }
- elseif(!array_key_exists($refererHost, $searchEngines))
- {
- if(!strncmp($query, 'cx=partner-pub-', 15))
- {
- // Google custom search engine
- $refererHost = 'google.com/cse';
- }
- elseif(!strncmp($refererPath, '/pemonitorhosted/ws/results/', 28))
- {
- // private-label search powered by InfoSpace Metasearch
- $refererHost = 'wsdsold.infospace.com';
- }
- elseif(strpos($refererHost, '.images.search.yahoo.com') != false)
- {
- // Yahoo! Images
- $refererHost = 'images.search.yahoo.com';
- }
- elseif(strpos($refererHost, '.search.yahoo.com') != false)
- {
- // Yahoo!
- $refererHost = 'search.yahoo.com';
- }
- else
- {
- return false;
- }
- }
- $searchEngineName = $searchEngines[$refererHost][0];
- $variableNames = null;
- if(isset($searchEngines[$refererHost][1]))
- {
- $variableNames = $searchEngines[$refererHost][1];
- }
- if(!$variableNames)
- {
- $searchEngineNames = self::getSearchEngineNames();
- $url = $searchEngineNames[$searchEngineName];
- $variableNames = $searchEngines[$url][1];
- }
- if(!is_array($variableNames))
- {
- $variableNames = array($variableNames);
- }
-
- $key = null;
- if($searchEngineName === 'Google Images'
- || ($searchEngineName === 'Google' && strpos($referrerUrl, '/imgres') !== false) )
- {
- if (strpos($query, '&prev') !== false)
- {
- $query = urldecode(trim(self::getParameterFromQueryString($query, 'prev')));
- $query = str_replace('&', '&amp;', strstr($query, '?'));
- }
- $searchEngineName = 'Google Images';
- }
- else if($searchEngineName === 'Google'
- && (strpos($query, '&as_') !== false || strpos($query, 'as_') === 0))
- {
- $keys = array();
- $key = self::getParameterFromQueryString($query, 'as_q');
- if(!empty($key))
- {
- array_push($keys, $key);
- }
- $key = self::getParameterFromQueryString($query, 'as_oq');
- if(!empty($key))
- {
- array_push($keys, str_replace('+', ' OR ', $key));
- }
- $key = self::getParameterFromQueryString($query, 'as_epq');
- if(!empty($key))
- {
- array_push($keys, "\"$key\"");
- }
- $key = self::getParameterFromQueryString($query, 'as_eq');
- if(!empty($key))
- {
- array_push($keys, "-$key");
- }
- $key = trim(urldecode(implode(' ', $keys)));
- }
-
- if ($searchEngineName === 'Google')
- {
- // top bar menu
- $tbm = self::getParameterFromQueryString($query, 'tbm');
- switch ($tbm)
- {
- case 'isch':
- $searchEngineName = 'Google Images'; break;
- case 'vid':
- $searchEngineName = 'Google Video'; break;
- case 'shop':
- $searchEngineName = 'Google Shopping'; break;
- }
- }
-
- if(empty($key))
- {
- foreach($variableNames as $variableName)
- {
- if($variableName[0] == '/')
- {
- // regular expression match
- if(preg_match($variableName, $referrerUrl, $matches))
- {
- $key = trim(urldecode($matches[1]));
- break;
- }
- }
- else
- {
- // search for keywords now &vname=keyword
- $key = self::getParameterFromQueryString($query, $variableName);
- $key = trim(urldecode($key));
-
- // Special case: Google & empty q parameter
- if(empty($key)
- && $variableName == 'q'
-
- && (
- // Google search with no keyword
- ($searchEngineName == 'Google'
- && ( // First, they started putting an empty q= parameter
- strpos($query, '&q=') !== false
- || strpos($query, '?q=') !== false
- // then they started sending the full host only (no path/query string)
- || (empty($query) && (empty($refererPath) || $refererPath == '/') && empty($refererParsed['fragment']))
- )
- )
- // search engines with no keyword
- || $searchEngineName == 'Google Images'
- || $searchEngineName == 'DuckDuckGo')
- )
- {
- $key = false;
- }
- if(!empty($key)
- || $key === false)
- {
- break;
- }
- }
- }
- }
-
- // $key === false is the special case "No keyword provided" which is a Search engine match
- if($key === null
- || $key === '')
- {
- return false;
- }
-
- if(!empty($key))
- {
- if(function_exists('iconv')
- && isset($searchEngines[$refererHost][3]))
- {
- // accepts string, array, or comma-separated list string in preferred order
- $charsets = $searchEngines[$refererHost][3];
- if (!is_array($charsets))
- {
- $charsets = explode(',', $charsets);
- }
-
- if(!empty($charsets))
- {
- $charset = $charsets[0];
- if (count($charsets) > 1
- && function_exists('mb_detect_encoding'))
- {
- $charset = mb_detect_encoding($key, $charsets);
- if ($charset === false)
- {
- $charset = $charsets[0];
- }
- }
-
- $newkey = @iconv($charset, 'UTF-8//IGNORE', $key);
- if(!empty($newkey))
- {
- $key = $newkey;
- }
- }
- }
-
- $key = self::mb_strtolower($key);
- }
-
- return array(
- 'name' => $searchEngineName,
- 'keywords' => $key,
- );
- }
-
-/*
- * System environment
- */
-
- /**
- * Returns true if PHP was invoked from command-line interface (shell)
- *
- * @since added in 0.4.4
- * @return bool true if PHP invoked as a CGI or from CLI
- */
- static public function isPhpCliMode()
- {
- $remoteAddr = @$_SERVER['REMOTE_ADDR'];
- return PHP_SAPI == 'cli' ||
- (!strncmp(PHP_SAPI, 'cgi', 3) && empty($remoteAddr));
- }
-
- /**
- * Is the current script execution triggered by misc/cron/archive.php ?
- *
- * Helpful for error handling: directly throw error without HTML (eg. when DB is down)
- * @return bool
- */
- static public function isArchivePhpTriggered()
- {
- return !empty($_GET['trigger'])
- && $_GET['trigger'] == 'archivephp';
- }
-
- /**
- * Assign CLI parameters as if they were REQUEST or GET parameters.
- * You can trigger Piwik from the command line by
- * # /usr/bin/php5 /path/to/piwik/index.php -- "module=API&method=Actions.getActions&idSite=1&period=day&date=previous8&format=php"
- */
- static public function assignCliParametersToRequest()
- {
- if(isset($_SERVER['argc'])
- && $_SERVER['argc'] > 0)
- {
- for ($i=1; $i < $_SERVER['argc']; $i++)
- {
- parse_str($_SERVER['argv'][$i],$tmp);
- $_GET = array_merge($_GET, $tmp);
- }
- }
- }
-
- /**
- * Returns true if running on a Windows operating system
- *
- * @since added in 0.6.5
- * @return bool true if PHP detects it is running on Windows; else false
- */
- static public function isWindows()
- {
- return DIRECTORY_SEPARATOR === '\\';
- }
-
- /**
- * Returns true if running on MacOS
- *
- * @return bool true if PHP detects it is running on MacOS; else false
- */
- static public function isMacOS()
- {
- return PHP_OS === 'Darwin';
- }
-
- /**
- * Returns true if running on an Apache web server
- *
- * @return bool
- */
- static public function isApache()
- {
- $apache = isset($_SERVER['SERVER_SOFTWARE']) &&
- !strncmp($_SERVER['SERVER_SOFTWARE'], 'Apache', 6);
-
- return $apache;
- }
-
- /**
- * Returns true if running on Microsoft IIS 7 (or above)
- *
- * @return bool
- */
- static public function isIIS()
- {
- $iis = isset($_SERVER['SERVER_SOFTWARE']) &&
- preg_match('/^Microsoft-IIS\/(.+)/', $_SERVER['SERVER_SOFTWARE'], $matches) &&
- version_compare($matches[1], '7') >= 0;
-
- return $iis;
- }
-
- /**
- * Takes a list of fields defining numeric values and returns the corresponding
- * unnamed parameters to be bound to the field names in the where clause of a SQL query
- *
- * @param array|string $fields array( fieldName1, fieldName2, fieldName3) Names of the mysql table fields to load
- * @return string "?, ?, ?"
- */
- public static function getSqlStringFieldsArray( $fields )
- {
- if(is_string($fields))
- {
- $fields = array($fields);
- }
- $count = count($fields);
- if($count == 0)
- {
- return "''";
- }
- return '?'.str_repeat(',?', $count-1);
- }
-
- /**
- * Sets outgoing header.
- *
- * @param string $header The header.
- * @param bool $replace Whether to replace existing or not.
- */
- public static function sendHeader( $header, $replace = true )
- {
- if (isset($GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING']) && $GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING'])
- {
- @header($header, $replace);
- }
- else
- {
- header($header, $replace);
- }
- }
-
- /**
- * Returns the ID of the current LocationProvider (see UserCountry plugin code) from
- * the Tracker cache.
- */
- public static function getCurrentLocationProviderId()
- {
- $cache = Piwik_Tracker_Cache::getCacheGeneral();
- return empty($cache['currentLocationProviderId'])
- ? Piwik_UserCountry_LocationProvider_Default::ID
- : $cache['currentLocationProviderId'];
- }
+ /**
+ * Const used to map the referer type to an integer in the log_visit table
+ */
+ const REFERER_TYPE_DIRECT_ENTRY = 1;
+ const REFERER_TYPE_SEARCH_ENGINE = 2;
+ const REFERER_TYPE_WEBSITE = 3;
+ const REFERER_TYPE_CAMPAIGN = 6;
+
+ /**
+ * Flag used with htmlspecialchar
+ * See php.net/htmlspecialchars
+ */
+ const HTML_ENCODING_QUOTE_STYLE = ENT_QUOTES;
+
+ /*
+ * Database
+ */
+
+ /**
+ * Hashes a string into an integer which should be very low collision risks
+ * @param string $string String to hash
+ * @return int Resulting int hash
+ */
+ static public function hashStringToInt($string)
+ {
+ $stringHash = substr(md5($string), 0, 8);
+ return base_convert($stringHash, 16, 10);
+ }
+
+ static public $cachedTablePrefix = null;
+
+ /**
+ * Returns the table name prefixed by the table prefix.
+ * Works in both Tracker and UI mode.
+ *
+ * @param string $table The table name to prefix, ie "log_visit"
+ * @return string The table name prefixed, ie "piwik-production_log_visit"
+ */
+ static public function prefixTable($table)
+ {
+ if (is_null(self::$cachedTablePrefix)) {
+ self::$cachedTablePrefix = Piwik_Config::getInstance()->database['tables_prefix'];
+ }
+ return self::$cachedTablePrefix . $table;
+ }
+
+ /**
+ * Returns an array containing the prefixed table names of every passed argument.
+ *
+ * @param string ... The table names to prefix, ie "log_visit"
+ * @return array The prefixed names in an array.
+ */
+ static public function prefixTables()
+ {
+ $result = array();
+ foreach (func_get_args() as $table) {
+ $result[] = self::prefixTable($table);
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the table name, after removing the table prefix
+ *
+ * @param string $table
+ * @return string
+ */
+ static public function unprefixTable($table)
+ {
+ static $prefixTable = null;
+ if (is_null($prefixTable)) {
+ $prefixTable = Piwik_Config::getInstance()->database['tables_prefix'];
+ }
+ if (empty($prefixTable)
+ || strpos($table, $prefixTable) !== 0
+ ) {
+ return $table;
+ }
+ $count = 1;
+ return str_replace($prefixTable, '', $table, $count);
+ }
+
+ /*
+ * Tracker
+ */
+ static public function isGoalPluginEnabled()
+ {
+ return Piwik_PluginsManager::getInstance()->isPluginActivated('Goals');
+ }
+
+ /*
+ * URLs
+ */
+
+ /**
+ * Returns the path and query part from a URL.
+ * Eg. http://piwik.org/test/index.php?module=CoreHome will return /test/index.php?module=CoreHome
+ *
+ * @param string $url either http://piwik.org/test or /
+ * @return string
+ */
+ static public function getPathAndQueryFromUrl($url)
+ {
+ $parsedUrl = parse_url($url);
+ $result = '';
+ if (isset($parsedUrl['path'])) {
+ $result .= substr($parsedUrl['path'], 1);
+ }
+ if (isset($parsedUrl['query'])) {
+ $result .= '?' . $parsedUrl['query'];
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the value of a GET parameter $parameter in an URL query $urlQuery
+ *
+ * @param string $urlQuery result of parse_url()['query'] and htmlentitied (& is &amp;) eg. module=test&amp;action=toto or ?page=test
+ * @param string $parameter
+ * @return string|bool Parameter value if found (can be the empty string!), null if not found
+ */
+ static public function getParameterFromQueryString($urlQuery, $parameter)
+ {
+ $nameToValue = self::getArrayFromQueryString($urlQuery);
+ if (isset($nameToValue[$parameter])) {
+ return $nameToValue[$parameter];
+ }
+ return null;
+ }
+
+ /**
+ * Returns an URL query string in an array format
+ *
+ * @param string $urlQuery
+ * @return array array( param1=> value1, param2=>value2)
+ */
+ static public function getArrayFromQueryString($urlQuery)
+ {
+ if (strlen($urlQuery) == 0) {
+ return array();
+ }
+ if ($urlQuery[0] == '?') {
+ $urlQuery = substr($urlQuery, 1);
+ }
+ $separator = '&';
+
+ $urlQuery = $separator . $urlQuery;
+ // $urlQuery = str_replace(array('%20'), ' ', $urlQuery);
+ $refererQuery = trim($urlQuery);
+
+ $values = explode($separator, $refererQuery);
+
+ $nameToValue = array();
+
+ foreach ($values as $value) {
+ $pos = strpos($value, '=');
+ if ($pos !== false) {
+ $name = substr($value, 0, $pos);
+ $value = substr($value, $pos + 1);
+ if ($value === false) {
+ $value = '';
+ }
+ } else {
+ $name = $value;
+ $value = false;
+ }
+ if (!empty($name)) {
+ $name = Piwik_Common::sanitizeInputValue($name);
+ }
+ if (!empty($value)) {
+ $value = Piwik_Common::sanitizeInputValue($value);
+ }
+
+ // if array without indexes
+ $count = 0;
+ $tmp = preg_replace('/(\[|%5b)(]|%5d)$/i', '', $name, -1, $count);
+ if (!empty($tmp) && $count) {
+ $name = $tmp;
+ if (isset($nameToValue[$name]) == false || is_array($nameToValue[$name]) == false) {
+ $nameToValue[$name] = array();
+ }
+ array_push($nameToValue[$name], $value);
+ } else if (!empty($name)) {
+ $nameToValue[$name] = $value;
+ }
+ }
+ return $nameToValue;
+ }
+
+ /**
+ * Builds a URL from the result of parse_url function
+ * Copied from the PHP comments at http://php.net/parse_url
+ * @param array $parsed
+ * @return bool|string
+ */
+ static public function getParseUrlReverse($parsed)
+ {
+ if (!is_array($parsed)) {
+ return false;
+ }
+
+ $uri = !empty($parsed['scheme']) ? $parsed['scheme'] . ':' . (!strcasecmp($parsed['scheme'], 'mailto') ? '' : '//') : '';
+ $uri .= !empty($parsed['user']) ? $parsed['user'] . (!empty($parsed['pass']) ? ':' . $parsed['pass'] : '') . '@' : '';
+ $uri .= !empty($parsed['host']) ? $parsed['host'] : '';
+ $uri .= !empty($parsed['port']) ? ':' . $parsed['port'] : '';
+
+ if (!empty($parsed['path'])) {
+ $uri .= (!strncmp($parsed['path'], '/', 1))
+ ? $parsed['path']
+ : ((!empty($uri) ? '/' : '') . $parsed['path']);
+ }
+
+ $uri .= !empty($parsed['query']) ? '?' . $parsed['query'] : '';
+ $uri .= !empty($parsed['fragment']) ? '#' . $parsed['fragment'] : '';
+ return $uri;
+ }
+
+ /**
+ * Returns true if the string passed may be a URL.
+ * We don't need a precise test here because the value comes from the website
+ * tracked source code and the URLs may look very strange.
+ *
+ * @param string $url
+ * @return bool
+ */
+ static public function isLookLikeUrl($url)
+ {
+ return preg_match('~^(ftp|news|http|https)?://(.*)$~D', $url, $matches) !== 0
+ && strlen($matches[2]) > 0;
+ }
+
+ /*
+ * File operations
+ */
+
+ /**
+ * ending WITHOUT slash
+ *
+ * @return string
+ */
+ static public function getPathToPiwikRoot()
+ {
+ return realpath(dirname(__FILE__) . "/..");
+ }
+
+ /**
+ * Create directory if permitted
+ *
+ * @param string $path
+ * @param bool $denyAccess
+ */
+ static public function mkdir($path, $denyAccess = true)
+ {
+ if (!is_dir($path)) {
+ // the mode in mkdir is modified by the current umask
+ @mkdir($path, $mode = 0755, $recursive = true);
+ }
+
+ // try to overcome restrictive umask (mis-)configuration
+ if (!is_writable($path)) {
+ @chmod($path, 0755);
+ if (!is_writable($path)) {
+ @chmod($path, 0775);
+
+ // enough! we're not going to make the directory world-writeable
+ }
+ }
+
+ if ($denyAccess) {
+ self::createHtAccess($path, $overwrite = false);
+ }
+ }
+
+ /**
+ * Create .htaccess file in specified directory
+ *
+ * Apache-specific; for IIS @see web.config
+ *
+ * @param string $path without trailing slash
+ * @param bool $overwrite whether to overwrite an existing file or not
+ * @param string $content
+ */
+ static public function createHtAccess($path, $overwrite = true, $content = "<Files \"*\">\n<IfModule mod_access.c>\nDeny from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nDeny from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nDeny from all\n</IfModule>\n</Files>\n")
+ {
+ if (self::isApache()) {
+ $file = $path . '/.htaccess';
+ if ($overwrite || !file_exists($file)) {
+ @file_put_contents($file, $content);
+ }
+ }
+ }
+
+ /**
+ * Get canonicalized absolute path
+ * See http://php.net/realpath
+ *
+ * @param string $path
+ * @return string canonicalized absolute path
+ */
+ static public function realpath($path)
+ {
+ if (file_exists($path)) {
+ return realpath($path);
+ }
+ return $path;
+ }
+
+ /**
+ * Returns true if the string is a valid filename
+ * File names that start with a-Z or 0-9 and contain a-Z, 0-9, underscore(_), dash(-), and dot(.) will be accepted.
+ * File names beginning with anything but a-Z or 0-9 will be rejected (including .htaccess for example).
+ * File names containing anything other than above mentioned will also be rejected (file names with spaces won't be accepted).
+ *
+ * @param string $filename
+ * @return bool
+ *
+ */
+ static public function isValidFilename($filename)
+ {
+ return (0 !== preg_match('/(^[a-zA-Z0-9]+([a-zA-Z_0-9.-]*))$/D', $filename));
+ }
+
+ /*
+ * String operations
+ */
+
+ /**
+ * byte-oriented substr() - ASCII
+ *
+ * @param string $string
+ * @param int $start
+ * @param int ... optional length
+ * @return string
+ */
+ static public function substr($string, $start)
+ {
+ // in case mbstring overloads substr function
+ $substr = function_exists('mb_orig_substr') ? 'mb_orig_substr' : 'substr';
+
+ $length = func_num_args() > 2
+ ? func_get_arg(2)
+ : self::strlen($string);
+
+ return $substr($string, $start, $length);
+ }
+
+ /**
+ * byte-oriented strlen() - ASCII
+ *
+ * @param string $string
+ * @return int
+ */
+ static public function strlen($string)
+ {
+ // in case mbstring overloads strlen function
+ $strlen = function_exists('mb_orig_strlen') ? 'mb_orig_strlen' : 'strlen';
+ return $strlen($string);
+ }
+
+ /**
+ * multi-byte substr() - UTF-8
+ *
+ * @param string $string
+ * @param int $start
+ * @param int ... optional length
+ * @return string
+ */
+ static public function mb_substr($string, $start)
+ {
+ $length = func_num_args() > 2
+ ? func_get_arg(2)
+ : self::mb_strlen($string);
+
+ if (function_exists('mb_substr')) {
+ return mb_substr($string, $start, $length, 'UTF-8');
+ }
+
+ return substr($string, $start, $length);
+ }
+
+ /**
+ * multi-byte strlen() - UTF-8
+ *
+ * @param string $string
+ * @return int
+ */
+ static public function mb_strlen($string)
+ {
+ if (function_exists('mb_strlen')) {
+ return mb_strlen($string, 'UTF-8');
+ }
+
+ return strlen($string);
+ }
+
+ /**
+ * multi-byte strtolower() - UTF-8
+ *
+ * @param string $string
+ * @return string
+ */
+ static public function mb_strtolower($string)
+ {
+ if (function_exists('mb_strtolower')) {
+ return mb_strtolower($string, 'UTF-8');
+ }
+
+ return strtolower($string);
+ }
+
+ /*
+ * Escaping input
+ */
+
+ /**
+ * Returns the variable after cleaning operations.
+ * NB: The variable still has to be escaped before going into a SQL Query!
+ *
+ * If an array is passed the cleaning is done recursively on all the sub-arrays.
+ * The array's keys are filtered as well!
+ *
+ * How this method works:
+ * - The variable returned has been htmlspecialchars to avoid the XSS security problem.
+ * - The single quotes are not protected so "Piwik's amazing" will still be "Piwik's amazing".
+ *
+ * - Transformations are:
+ * - '&' (ampersand) becomes '&amp;'
+ * - '"'(double quote) becomes '&quot;'
+ * - '<' (less than) becomes '&lt;'
+ * - '>' (greater than) becomes '&gt;'
+ * - It handles the magic_quotes setting.
+ * - A non string value is returned without modification
+ *
+ * @param mixed $value The variable to be cleaned
+ * @param bool $alreadyStripslashed
+ * @throws Exception
+ * @return mixed The variable after cleaning
+ */
+ static public function sanitizeInputValues($value, $alreadyStripslashed = false)
+ {
+ if (is_numeric($value)) {
+ return $value;
+ } elseif (is_string($value)) {
+ $value = self::sanitizeInputValue($value);
+
+ if (!$alreadyStripslashed) // a JSON array was already stripslashed, don't do it again for each value
+ {
+ $value = self::undoMagicQuotes($value);
+ }
+ } elseif (is_array($value)) {
+ foreach (array_keys($value) as $key) {
+ $newKey = $key;
+ $newKey = self::sanitizeInputValues($newKey, $alreadyStripslashed);
+ if ($key != $newKey) {
+ $value[$newKey] = $value[$key];
+ unset($value[$key]);
+ }
+
+ $value[$newKey] = self::sanitizeInputValues($value[$newKey], $alreadyStripslashed);
+ }
+ } elseif (!is_null($value)
+ && !is_bool($value)
+ ) {
+ throw new Exception("The value to escape has not a supported type. Value = " . var_export($value, true));
+ }
+ return $value;
+ }
+
+ /**
+ * Sanitize a single input value
+ *
+ * @param string $value
+ * @return string sanitized input
+ */
+ static public function sanitizeInputValue($value)
+ {
+ // $_GET and $_REQUEST already urldecode()'d
+ // decode
+ // note: before php 5.2.7, htmlspecialchars() double encodes &#x hex items
+ $value = html_entity_decode($value, Piwik_Common::HTML_ENCODING_QUOTE_STYLE, 'UTF-8');
+
+ // filter
+ $value = str_replace(array("\n", "\r", "\0"), '', $value);
+
+ // escape
+ $tmp = @htmlspecialchars($value, self::HTML_ENCODING_QUOTE_STYLE, 'UTF-8');
+
+ // note: php 5.2.5 and above, htmlspecialchars is destructive if input is not UTF-8
+ if ($value != '' && $tmp == '') {
+ // convert and escape
+ $value = utf8_encode($value);
+ $tmp = htmlspecialchars($value, self::HTML_ENCODING_QUOTE_STYLE, 'UTF-8');
+ }
+ return $tmp;
+ }
+
+ /**
+ * Unsanitize a single input value
+ *
+ * @param string $value
+ * @return string unsanitized input
+ */
+ static public function unsanitizeInputValue($value)
+ {
+ return htmlspecialchars_decode($value, self::HTML_ENCODING_QUOTE_STYLE);
+ }
+
+ /**
+ * Unsanitize one or more values.
+ *
+ * @param string|array $value
+ * @return string|array unsanitized input
+ */
+ static public function unsanitizeInputValues($value)
+ {
+ if (is_array($value)) {
+ $result = array();
+ foreach ($value as $key => $arrayValue) {
+ $result[$key] = self::unsanitizeInputValues($arrayValue);
+ }
+ return $result;
+ } else {
+ return self::unsanitizeInputValue($value);
+ }
+ }
+
+ /**
+ * Undo the damage caused by magic_quotes; deprecated in php 5.3 but not removed until php 5.4
+ *
+ * @param string
+ * @return string modified or not
+ */
+ static public function undoMagicQuotes($value)
+ {
+ return version_compare(PHP_VERSION, '5.4', '<')
+ && get_magic_quotes_gpc()
+ ? stripslashes($value)
+ : $value;
+ }
+
+ /**
+ * Returns a sanitized variable value from the $_GET and $_POST superglobal.
+ * If the variable doesn't have a value or an empty value, returns the defaultValue if specified.
+ * If the variable doesn't have neither a value nor a default value provided, an exception is raised.
+ *
+ * @see sanitizeInputValues() for the applied sanitization
+ *
+ * @param string $varName name of the variable
+ * @param string $varDefault default value. If '', and if the type doesn't match, exit() !
+ * @param string $varType Expected type, the value must be one of the following: array, int, integer, string, json
+ * @param array $requestArrayToUse
+ *
+ * @throws Exception if the variable type is not known
+ * or if the variable we want to read doesn't have neither a value nor a default value specified
+ *
+ * @return mixed The variable after cleaning
+ */
+ static public function getRequestVar($varName, $varDefault = null, $varType = null, $requestArrayToUse = null)
+ {
+ if (is_null($requestArrayToUse)) {
+ $requestArrayToUse = $_GET + $_POST;
+ }
+ $varDefault = self::sanitizeInputValues($varDefault);
+ if ($varType === 'int') {
+ // settype accepts only integer
+ // 'int' is simply a shortcut for 'integer'
+ $varType = 'integer';
+ }
+
+ // there is no value $varName in the REQUEST so we try to use the default value
+ if (empty($varName)
+ || !isset($requestArrayToUse[$varName])
+ || (!is_array($requestArrayToUse[$varName])
+ && strlen($requestArrayToUse[$varName]) === 0
+ )
+ ) {
+ if (is_null($varDefault)) {
+ throw new Exception("The parameter '$varName' isn't set in the Request, and a default value wasn't provided.");
+ } else {
+ if (!is_null($varType)
+ && in_array($varType, array('string', 'integer', 'array'))
+ ) {
+ settype($varDefault, $varType);
+ }
+ return $varDefault;
+ }
+ }
+
+ // Normal case, there is a value available in REQUEST for the requested varName:
+
+ // we deal w/ json differently
+ if ($varType == 'json') {
+ $value = self::undoMagicQuotes($requestArrayToUse[$varName]);
+ $value = Piwik_Common::json_decode($value, $assoc = true);
+ return self::sanitizeInputValues($value, $alreadyStripslashed = true);
+ }
+
+ $value = self::sanitizeInputValues($requestArrayToUse[$varName]);
+ if (!is_null($varType)) {
+ $ok = false;
+
+ if ($varType === 'string') {
+ if (is_string($value)) $ok = true;
+ } elseif ($varType === 'integer') {
+ if ($value == (string)(int)$value) $ok = true;
+ } elseif ($varType === 'float') {
+ if ($value == (string)(float)$value) $ok = true;
+ } elseif ($varType === 'array') {
+ if (is_array($value)) $ok = true;
+ } else {
+ throw new Exception("\$varType specified is not known. It should be one of the following: array, int, integer, float, string");
+ }
+
+ // The type is not correct
+ if ($ok === false) {
+ if ($varDefault === null) {
+ throw new Exception("The parameter '$varName' doesn't have a correct type, and a default value wasn't provided.");
+ } // we return the default value with the good type set
+ else {
+ settype($varDefault, $varType);
+ return $varDefault;
+ }
+ }
+ settype($value, $varType);
+ }
+ return $value;
+ }
+
+ /*
+ * Generating unique strings
+ */
+
+ /**
+ * Returns a 32 characters long uniq ID
+ *
+ * @return string 32 chars
+ */
+ static public function generateUniqId()
+ {
+ return md5(uniqid(rand(), true));
+ }
+
+ /**
+ * Get salt from [superuser] section
+ *
+ * @return string
+ */
+ static public function getSalt()
+ {
+ static $salt = null;
+ if (is_null($salt)) {
+ $salt = @Piwik_Config::getInstance()->superuser['salt'];
+ }
+ return $salt;
+ }
+
+ /**
+ * Configureable hash() algorithm (defaults to md5)
+ *
+ * @param string $str String to be hashed
+ * @param bool $raw_output
+ * @return string Hash string
+ */
+ static function hash($str, $raw_output = false)
+ {
+ static $hashAlgorithm = null;
+ if (is_null($hashAlgorithm)) {
+ $hashAlgorithm = @Piwik_Config::getInstance()->General['hash_algorithm'];
+ }
+
+ if ($hashAlgorithm) {
+ $hash = @hash($hashAlgorithm, $str, $raw_output);
+ if ($hash !== false)
+ return $hash;
+ }
+
+ return md5($str, $raw_output);
+ }
+
+ /**
+ * Generate random string
+ *
+ * @param int $length string length
+ * @param string $alphabet characters allowed in random string
+ * @return string random string with given length
+ */
+ public static function getRandomString($length = 16, $alphabet = "abcdefghijklmnoprstuvwxyz0123456789")
+ {
+ $chars = $alphabet;
+ $str = '';
+
+ list($usec, $sec) = explode(" ", microtime());
+ $seed = ((float)$sec + (float)$usec) * 100000;
+ mt_srand($seed);
+
+ for ($i = 0; $i < $length; $i++) {
+ $rand_key = mt_rand(0, strlen($chars) - 1);
+ $str .= substr($chars, $rand_key, 1);
+ }
+ return str_shuffle($str);
+ }
+
+ /*
+ * Conversions
+ */
+
+ /**
+ * Convert hexadecimal representation into binary data.
+ * !! Will emit warning if input string is not hex!!
+ *
+ * @see http://php.net/bin2hex
+ *
+ * @param string $str Hexadecimal representation
+ * @return string
+ */
+ static public function hex2bin($str)
+ {
+ return pack("H*", $str);
+ }
+
+ /**
+ * This function will convert the input string to the binary representation of the ID
+ * but it will throw an Exception if the specified input ID is not correct
+ *
+ * This is used when building segments containing visitorId which could be an invalid string
+ * therefore throwing Unexpected PHP error [pack(): Type H: illegal hex digit i] severity [E_WARNING]
+ *
+ * It would be simply to silent fail the pack() call above but in all other cases, we don't expect an error,
+ * so better be safe and get the php error when something unexpected is happening
+ * @param string $id
+ * @throws Exception
+ * @return string binary string
+ */
+ static public function convertVisitorIdToBin($id)
+ {
+ if (strlen($id) !== Piwik_Tracker::LENGTH_HEX_ID_STRING
+ || @bin2hex(self::hex2bin($id)) != $id
+ ) {
+ throw new Exception("visitorId is expected to be a " . Piwik_Tracker::LENGTH_HEX_ID_STRING . " hex char string");
+ }
+ return self::hex2bin($id);
+ }
+
+ /**
+ * Convert IP address (in network address format) to presentation format.
+ * This is a backward compatibility function for code that only expects
+ * IPv4 addresses (i.e., doesn't support IPv6).
+ *
+ * @see Piwik_IP::N2P()
+ *
+ * This function does not support the long (or its string representation)
+ * returned by the built-in ip2long() function, from Piwik 1.3 and earlier.
+ *
+ * @deprecated 1.4
+ *
+ * @param string $ip IP address in network address format
+ * @return string
+ */
+ static public function long2ip($ip)
+ {
+ return Piwik_IP::long2ip($ip);
+ }
+
+ /**
+ * Should we use the replacement json_encode/json_decode functions?
+ *
+ * @return bool True if broken; false otherwise
+ */
+ static private function useJsonLibrary()
+ {
+ static $useLib;
+
+ if (!isset($useLib)) {
+ /*
+ * 5.1.x - doesn't have json extension; we use lib/upgradephp instead
+ * 5.2 to 5.2.4 - broken in various ways, including:
+ *
+ * @see https://bugs.php.net/bug.php?id=38680 'json_decode cannot decode basic types'
+ * @see https://bugs.php.net/bug.php?id=41403 'json_decode cannot decode floats'
+ * @see https://bugs.php.net/bug.php?id=42785 'json_encode outputs numbers according to locale'
+ */
+ $useLib = false;
+ if (version_compare(PHP_VERSION, '5.2.1') < 0) {
+ $useLib = true;
+ } else if (version_compare(PHP_VERSION, '5.2.5') < 0) {
+ $info = localeconv();
+ $useLib = $info['decimal_point'] != '.';
+ }
+ }
+
+ return $useLib;
+ }
+
+ /**
+ * JSON encode wrapper
+ * - missing or broken in some php 5.x versions
+ *
+ * @param mixed $value
+ * @return string
+ */
+ static public function json_encode($value)
+ {
+ if (self::useJsonLibrary()) {
+ return _json_encode($value);
+ }
+
+ return @json_encode($value);
+ }
+
+ /**
+ * JSON decode wrapper
+ * - missing or broken in some php 5.x versions
+ *
+ * @param string $json
+ * @param bool $assoc
+ * @return mixed
+ */
+ static public function json_decode($json, $assoc = false)
+ {
+ if (self::useJsonLibrary()) {
+ return _json_decode($json, $assoc);
+ }
+
+ return json_decode($json, $assoc);
+ }
+
+ /*
+ * DataFiles
+ */
+
+ /**
+ * Returns list of continent codes
+ *
+ * @see core/DataFiles/Countries.php
+ *
+ * @return array Array of 3 letter continent codes
+ */
+ static public function getContinentsList()
+ {
+ require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Countries.php';
+
+ $continentsList = $GLOBALS['Piwik_ContinentList'];
+ return $continentsList;
+ }
+
+ /**
+ * Returns list of valid country codes
+ *
+ * @see core/DataFiles/Countries.php
+ *
+ * @param bool $includeInternalCodes
+ * @return array Array of (2 letter ISO codes => 3 letter continent code)
+ */
+ static public function getCountriesList($includeInternalCodes = false)
+ {
+ require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Countries.php';
+
+ $countriesList = $GLOBALS['Piwik_CountryList'];
+ $extras = $GLOBALS['Piwik_CountryList_Extras'];
+
+ if ($includeInternalCodes) {
+ return array_merge($countriesList, $extras);
+ }
+ return $countriesList;
+ }
+
+ /**
+ * Returns list of valid language codes
+ *
+ * @see core/DataFiles/Languages.php
+ *
+ * @return array Array of 2 letter ISO codes => Language name (in English)
+ */
+ static public function getLanguagesList()
+ {
+ require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Languages.php';
+
+ $languagesList = $GLOBALS['Piwik_LanguageList'];
+ return $languagesList;
+ }
+
+ /**
+ * Returns list of language to country mappings
+ *
+ * @see core/DataFiles/LanguageToCountry.php
+ *
+ * @return array Array of ( 2 letter ISO language codes => 2 letter ISO country codes )
+ */
+ static public function getLanguageToCountryList()
+ {
+ require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/LanguageToCountry.php';
+
+ $languagesList = $GLOBALS['Piwik_LanguageToCountry'];
+ return $languagesList;
+ }
+
+ /**
+ * Returns list of search engines by URL
+ *
+ * @see core/DataFiles/SearchEngines.php
+ *
+ * @return array Array of ( URL => array( searchEngineName, keywordParameter, path, charset ) )
+ */
+ static public function getSearchEngineUrls()
+ {
+ require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/SearchEngines.php';
+
+ $searchEngines = $GLOBALS['Piwik_SearchEngines'];
+ return $searchEngines;
+ }
+
+ /**
+ * Returns list of search engines by name
+ *
+ * @see core/DataFiles/SearchEngines.php
+ *
+ * @return array Array of ( searchEngineName => URL )
+ */
+ static public function getSearchEngineNames()
+ {
+ require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/SearchEngines.php';
+
+ $searchEngines = $GLOBALS['Piwik_SearchEngines_NameToUrl'];
+ return $searchEngines;
+ }
+
+ /*
+ * Language, country, continent
+ */
+
+ /**
+ * Returns the browser language code, eg. "en-gb,en;q=0.5"
+ *
+ * @param string $browserLang Optional browser language, otherwise taken from the request header
+ * @return string
+ */
+ static public function getBrowserLanguage($browserLang = NULL)
+ {
+ static $replacementPatterns = array(
+ // extraneous bits of RFC 3282 that we ignore
+ '/(\\\\.)/', // quoted-pairs
+ '/(\s+)/', // CFWcS white space
+ '/(\([^)]*\))/', // CFWS comments
+ '/(;q=[0-9.]+)/', // quality
+
+ // found in the LANG environment variable
+ '/\.(.*)/', // charset (e.g., en_CA.UTF-8)
+ '/^C$/', // POSIX 'C' locale
+ );
+
+ if (is_null($browserLang)) {
+ $browserLang = self::sanitizeInputValues(@$_SERVER['HTTP_ACCEPT_LANGUAGE']);
+ if (empty($browserLang) && self::isPhpCliMode()) {
+ $browserLang = @getenv('LANG');
+ }
+ }
+
+ if (is_null($browserLang)) {
+ // a fallback might be to infer the language in HTTP_USER_AGENT (i.e., localized build)
+ $browserLang = "";
+ } else {
+ // language tags are case-insensitive per HTTP/1.1 s3.10 but the region may be capitalized per ISO3166-1;
+ // underscores are not permitted per RFC 4646 or 4647 (which obsolete RFC 1766 and 3066),
+ // but we guard against a bad user agent which naively uses its locale
+ $browserLang = strtolower(str_replace('_', '-', $browserLang));
+
+ // filters
+ $browserLang = preg_replace($replacementPatterns, '', $browserLang);
+
+ $browserLang = preg_replace('/((^|,)chrome:.*)/', '', $browserLang, 1); // Firefox bug
+ $browserLang = preg_replace('/(,)(?:en-securid,)|(?:(^|,)en-securid(,|$))/', '$1', $browserLang, 1); // unregistered language tag
+
+ $browserLang = str_replace('sr-sp', 'sr-rs', $browserLang); // unofficial (proposed) code in the wild
+ }
+
+ return $browserLang;
+ }
+
+ /**
+ * Returns the visitor country based on the Browser 'accepted language'
+ * information, but provides a hook for geolocation via IP address.
+ *
+ * @param string $lang browser lang
+ * @param bool $enableLanguageToCountryGuess If set to true, some assumption will be made and detection guessed more often, but accuracy could be affected
+ * @param string $ip
+ * @return string 2 letter ISO code
+ */
+ static public function getCountry($lang, $enableLanguageToCountryGuess, $ip)
+ {
+ $country = null;
+ Piwik_PostEvent('Common.getCountry', $country, $ip);
+ if (!empty($country)) {
+ return strtolower($country);
+ }
+
+ if (empty($lang) || strlen($lang) < 2 || $lang == 'xx') {
+ return 'xx';
+ }
+
+ $validCountries = self::getCountriesList();
+ return self::extractCountryCodeFromBrowserLanguage($lang, $validCountries, $enableLanguageToCountryGuess);
+ }
+
+ /**
+ * Returns list of valid country codes
+ *
+ * @param string $browserLanguage
+ * @param array $validCountries Array of valid countries
+ * @param bool $enableLanguageToCountryGuess (if true, will guess country based on language that lacks region information)
+ * @return array Array of 2 letter ISO codes
+ */
+ static public function extractCountryCodeFromBrowserLanguage($browserLanguage, $validCountries, $enableLanguageToCountryGuess)
+ {
+ $langToCountry = self::getLanguageToCountryList();
+
+ if ($enableLanguageToCountryGuess) {
+ if (preg_match('/^([a-z]{2,3})(?:,|;|$)/', $browserLanguage, $matches)) {
+ // match language (without region) to infer the country of origin
+ if (array_key_exists($matches[1], $langToCountry)) {
+ return $langToCountry[$matches[1]];
+ }
+ }
+ }
+
+ if (!empty($validCountries) && preg_match_all('/[-]([a-z]{2})/', $browserLanguage, $matches, PREG_SET_ORDER)) {
+ foreach ($matches as $parts) {
+ // match location; we don't make any inferences from the language
+ if (array_key_exists($parts[1], $validCountries)) {
+ return $parts[1];
+ }
+ }
+ }
+ return 'xx';
+ }
+
+ /**
+ * Returns the visitor language based only on the Browser 'accepted language' information
+ *
+ * @param $browserLanguage Browser's accepted langauge header
+ * @param $validLanguages array of valid language codes
+ * @return string 2 letter ISO 639 code
+ */
+ static public function extractLanguageCodeFromBrowserLanguage($browserLanguage, $validLanguages)
+ {
+ // assumes language preference is sorted;
+ // does not handle language-script-region tags or language range (*)
+ if (!empty($validLanguages) && preg_match_all('/(?:^|,)([a-z]{2,3})([-][a-z]{2})?/', $browserLanguage, $matches, PREG_SET_ORDER)) {
+ foreach ($matches as $parts) {
+ if (count($parts) == 3) {
+ // match locale (language and location)
+ if (in_array($parts[1] . $parts[2], $validLanguages)) {
+ return $parts[1] . $parts[2];
+ }
+ }
+ // match language only (where no region provided)
+ if (in_array($parts[1], $validLanguages)) {
+ return $parts[1];
+ }
+ }
+ }
+ return 'xx';
+ }
+
+ /**
+ * Returns the continent of a given country
+ *
+ * @param string $country 2 letters isocode
+ *
+ * @return string Continent (3 letters code : afr, asi, eur, amn, ams, oce)
+ */
+ static public function getContinent($country)
+ {
+ $countryList = self::getCountriesList();
+ if (isset($countryList[$country])) {
+ return $countryList[$country];
+ }
+ return 'unk';
+ }
+
+ /*
+ * Campaign
+ */
+
+ /**
+ * Returns the list of Campaign parameter names that will be read to classify
+ * a visit as coming from a Campaign
+ *
+ * @return array array(
+ * 0 => array( ... ) // campaign names parameters
+ * 1 => array( ... ) // campaign keyword parameters
+ * );
+ */
+ static public function getCampaignParameters()
+ {
+ $return = array(
+ Piwik_Config::getInstance()->Tracker['campaign_var_name'],
+ Piwik_Config::getInstance()->Tracker['campaign_keyword_var_name'],
+ );
+
+ foreach ($return as &$list) {
+ if (strpos($list, ',') !== false) {
+ $list = explode(',', $list);
+ } else {
+ $list = array($list);
+ }
+ }
+
+ array_walk_recursive($return, 'trim');
+ return $return;
+ }
+
+ /*
+ * Referrer
+ */
+
+ /**
+ * Reduce URL to more minimal form. 2 letter country codes are
+ * replaced by '{}', while other parts are simply removed.
+ *
+ * Examples:
+ * www.example.com -> example.com
+ * search.example.com -> example.com
+ * m.example.com -> example.com
+ * de.example.com -> {}.example.com
+ * example.de -> example.{}
+ * example.co.uk -> example.{}
+ *
+ * @param string $url
+ * @return string
+ */
+ static public function getLossyUrl($url)
+ {
+ static $countries;
+ if (!isset($countries)) {
+ $countries = implode('|', array_keys(self::getCountriesList(true)));
+ }
+
+ return preg_replace(
+ array(
+ '/^(w+[0-9]*|search)\./',
+ '/(^|\.)m\./',
+ '/(\.(com|org|net|co|it|edu))?\.(' . $countries . ')(\/|$)/',
+ '/(^|\.)(' . $countries . ')\./',
+ ),
+ array(
+ '',
+ '$1',
+ '.{}$4',
+ '$1{}.',
+ ),
+ $url);
+ }
+
+ /**
+ * Extracts a keyword from a raw not encoded URL.
+ * Will only extract keyword if a known search engine has been detected.
+ * Returns the keyword:
+ * - in UTF8: automatically converted from other charsets when applicable
+ * - strtolowered: "QUErY test!" will return "query test!"
+ * - trimmed: extra spaces before and after are removed
+ *
+ * Lists of supported search engines can be found in /core/DataFiles/SearchEngines.php
+ * The function returns false when a keyword couldn't be found.
+ * eg. if the url is "http://www.google.com/partners.html" this will return false,
+ * as the google keyword parameter couldn't be found.
+ *
+ * @see unit tests in /tests/core/Common.test.php
+ * @param string $referrerUrl URL referer URL, eg. $_SERVER['HTTP_REFERER']
+ * @return array|false false if a keyword couldn't be extracted,
+ * or array(
+ * 'name' => 'Google',
+ * 'keywords' => 'my searched keywords')
+ */
+ static public function extractSearchEngineInformationFromUrl($referrerUrl)
+ {
+ $refererParsed = @parse_url($referrerUrl);
+ $refererHost = '';
+ if (isset($refererParsed['host'])) {
+ $refererHost = $refererParsed['host'];
+ }
+ if (empty($refererHost)) {
+ return false;
+ }
+ // some search engines (eg. Bing Images) use the same domain
+ // as an existing search engine (eg. Bing), we must also use the url path
+ $refererPath = '';
+ if (isset($refererParsed['path'])) {
+ $refererPath = $refererParsed['path'];
+ }
+
+ // no search query
+ if (!isset($refererParsed['query'])) {
+ $refererParsed['query'] = '';
+ }
+ $query = $refererParsed['query'];
+
+ // Google Referrers URLs sometimes have the fragment which contains the keyword
+ if (!empty($refererParsed['fragment'])) {
+ $query .= '&' . $refererParsed['fragment'];
+ }
+
+ $searchEngines = self::getSearchEngineUrls();
+
+ $hostPattern = self::getLossyUrl($refererHost);
+ if (array_key_exists($refererHost . $refererPath, $searchEngines)) {
+ $refererHost = $refererHost . $refererPath;
+ } elseif (array_key_exists($hostPattern . $refererPath, $searchEngines)) {
+ $refererHost = $hostPattern . $refererPath;
+ } elseif (array_key_exists($hostPattern, $searchEngines)) {
+ $refererHost = $hostPattern;
+ } elseif (!array_key_exists($refererHost, $searchEngines)) {
+ if (!strncmp($query, 'cx=partner-pub-', 15)) {
+ // Google custom search engine
+ $refererHost = 'google.com/cse';
+ } elseif (!strncmp($refererPath, '/pemonitorhosted/ws/results/', 28)) {
+ // private-label search powered by InfoSpace Metasearch
+ $refererHost = 'wsdsold.infospace.com';
+ } elseif (strpos($refererHost, '.images.search.yahoo.com') != false) {
+ // Yahoo! Images
+ $refererHost = 'images.search.yahoo.com';
+ } elseif (strpos($refererHost, '.search.yahoo.com') != false) {
+ // Yahoo!
+ $refererHost = 'search.yahoo.com';
+ } else {
+ return false;
+ }
+ }
+ $searchEngineName = $searchEngines[$refererHost][0];
+ $variableNames = null;
+ if (isset($searchEngines[$refererHost][1])) {
+ $variableNames = $searchEngines[$refererHost][1];
+ }
+ if (!$variableNames) {
+ $searchEngineNames = self::getSearchEngineNames();
+ $url = $searchEngineNames[$searchEngineName];
+ $variableNames = $searchEngines[$url][1];
+ }
+ if (!is_array($variableNames)) {
+ $variableNames = array($variableNames);
+ }
+
+ $key = null;
+ if ($searchEngineName === 'Google Images'
+ || ($searchEngineName === 'Google' && strpos($referrerUrl, '/imgres') !== false)
+ ) {
+ if (strpos($query, '&prev') !== false) {
+ $query = urldecode(trim(self::getParameterFromQueryString($query, 'prev')));
+ $query = str_replace('&', '&amp;', strstr($query, '?'));
+ }
+ $searchEngineName = 'Google Images';
+ } else if ($searchEngineName === 'Google'
+ && (strpos($query, '&as_') !== false || strpos($query, 'as_') === 0)
+ ) {
+ $keys = array();
+ $key = self::getParameterFromQueryString($query, 'as_q');
+ if (!empty($key)) {
+ array_push($keys, $key);
+ }
+ $key = self::getParameterFromQueryString($query, 'as_oq');
+ if (!empty($key)) {
+ array_push($keys, str_replace('+', ' OR ', $key));
+ }
+ $key = self::getParameterFromQueryString($query, 'as_epq');
+ if (!empty($key)) {
+ array_push($keys, "\"$key\"");
+ }
+ $key = self::getParameterFromQueryString($query, 'as_eq');
+ if (!empty($key)) {
+ array_push($keys, "-$key");
+ }
+ $key = trim(urldecode(implode(' ', $keys)));
+ }
+
+ if ($searchEngineName === 'Google') {
+ // top bar menu
+ $tbm = self::getParameterFromQueryString($query, 'tbm');
+ switch ($tbm) {
+ case 'isch':
+ $searchEngineName = 'Google Images';
+ break;
+ case 'vid':
+ $searchEngineName = 'Google Video';
+ break;
+ case 'shop':
+ $searchEngineName = 'Google Shopping';
+ break;
+ }
+ }
+
+ if (empty($key)) {
+ foreach ($variableNames as $variableName) {
+ if ($variableName[0] == '/') {
+ // regular expression match
+ if (preg_match($variableName, $referrerUrl, $matches)) {
+ $key = trim(urldecode($matches[1]));
+ break;
+ }
+ } else {
+ // search for keywords now &vname=keyword
+ $key = self::getParameterFromQueryString($query, $variableName);
+ $key = trim(urldecode($key));
+
+ // Special case: Google & empty q parameter
+ if (empty($key)
+ && $variableName == 'q'
+
+ && (
+ // Google search with no keyword
+ ($searchEngineName == 'Google'
+ && ( // First, they started putting an empty q= parameter
+ strpos($query, '&q=') !== false
+ || strpos($query, '?q=') !== false
+ // then they started sending the full host only (no path/query string)
+ || (empty($query) && (empty($refererPath) || $refererPath == '/') && empty($refererParsed['fragment']))
+ )
+ )
+ // search engines with no keyword
+ || $searchEngineName == 'Google Images'
+ || $searchEngineName == 'DuckDuckGo')
+ ) {
+ $key = false;
+ }
+ if (!empty($key)
+ || $key === false
+ ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // $key === false is the special case "No keyword provided" which is a Search engine match
+ if ($key === null
+ || $key === ''
+ ) {
+ return false;
+ }
+
+ if (!empty($key)) {
+ if (function_exists('iconv')
+ && isset($searchEngines[$refererHost][3])
+ ) {
+ // accepts string, array, or comma-separated list string in preferred order
+ $charsets = $searchEngines[$refererHost][3];
+ if (!is_array($charsets)) {
+ $charsets = explode(',', $charsets);
+ }
+
+ if (!empty($charsets)) {
+ $charset = $charsets[0];
+ if (count($charsets) > 1
+ && function_exists('mb_detect_encoding')
+ ) {
+ $charset = mb_detect_encoding($key, $charsets);
+ if ($charset === false) {
+ $charset = $charsets[0];
+ }
+ }
+
+ $newkey = @iconv($charset, 'UTF-8//IGNORE', $key);
+ if (!empty($newkey)) {
+ $key = $newkey;
+ }
+ }
+ }
+
+ $key = self::mb_strtolower($key);
+ }
+
+ return array(
+ 'name' => $searchEngineName,
+ 'keywords' => $key,
+ );
+ }
+
+ /*
+ * System environment
+ */
+
+ /**
+ * Returns true if PHP was invoked from command-line interface (shell)
+ *
+ * @since added in 0.4.4
+ * @return bool true if PHP invoked as a CGI or from CLI
+ */
+ static public function isPhpCliMode()
+ {
+ $remoteAddr = @$_SERVER['REMOTE_ADDR'];
+ return PHP_SAPI == 'cli' ||
+ (!strncmp(PHP_SAPI, 'cgi', 3) && empty($remoteAddr));
+ }
+
+ /**
+ * Is the current script execution triggered by misc/cron/archive.php ?
+ *
+ * Helpful for error handling: directly throw error without HTML (eg. when DB is down)
+ * @return bool
+ */
+ static public function isArchivePhpTriggered()
+ {
+ return !empty($_GET['trigger'])
+ && $_GET['trigger'] == 'archivephp';
+ }
+
+ /**
+ * Assign CLI parameters as if they were REQUEST or GET parameters.
+ * You can trigger Piwik from the command line by
+ * # /usr/bin/php5 /path/to/piwik/index.php -- "module=API&method=Actions.getActions&idSite=1&period=day&date=previous8&format=php"
+ */
+ static public function assignCliParametersToRequest()
+ {
+ if (isset($_SERVER['argc'])
+ && $_SERVER['argc'] > 0
+ ) {
+ for ($i = 1; $i < $_SERVER['argc']; $i++) {
+ parse_str($_SERVER['argv'][$i], $tmp);
+ $_GET = array_merge($_GET, $tmp);
+ }
+ }
+ }
+
+ /**
+ * Returns true if running on a Windows operating system
+ *
+ * @since added in 0.6.5
+ * @return bool true if PHP detects it is running on Windows; else false
+ */
+ static public function isWindows()
+ {
+ return DIRECTORY_SEPARATOR === '\\';
+ }
+
+ /**
+ * Returns true if running on MacOS
+ *
+ * @return bool true if PHP detects it is running on MacOS; else false
+ */
+ static public function isMacOS()
+ {
+ return PHP_OS === 'Darwin';
+ }
+
+ /**
+ * Returns true if running on an Apache web server
+ *
+ * @return bool
+ */
+ static public function isApache()
+ {
+ $apache = isset($_SERVER['SERVER_SOFTWARE']) &&
+ !strncmp($_SERVER['SERVER_SOFTWARE'], 'Apache', 6);
+
+ return $apache;
+ }
+
+ /**
+ * Returns true if running on Microsoft IIS 7 (or above)
+ *
+ * @return bool
+ */
+ static public function isIIS()
+ {
+ $iis = isset($_SERVER['SERVER_SOFTWARE']) &&
+ preg_match('/^Microsoft-IIS\/(.+)/', $_SERVER['SERVER_SOFTWARE'], $matches) &&
+ version_compare($matches[1], '7') >= 0;
+
+ return $iis;
+ }
+
+ /**
+ * Takes a list of fields defining numeric values and returns the corresponding
+ * unnamed parameters to be bound to the field names in the where clause of a SQL query
+ *
+ * @param array|string $fields array( fieldName1, fieldName2, fieldName3) Names of the mysql table fields to load
+ * @return string "?, ?, ?"
+ */
+ public static function getSqlStringFieldsArray($fields)
+ {
+ if (is_string($fields)) {
+ $fields = array($fields);
+ }
+ $count = count($fields);
+ if ($count == 0) {
+ return "''";
+ }
+ return '?' . str_repeat(',?', $count - 1);
+ }
+
+ /**
+ * Sets outgoing header.
+ *
+ * @param string $header The header.
+ * @param bool $replace Whether to replace existing or not.
+ */
+ public static function sendHeader($header, $replace = true)
+ {
+ if (isset($GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING']) && $GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING']) {
+ @header($header, $replace);
+ } else {
+ header($header, $replace);
+ }
+ }
+
+ /**
+ * Returns the ID of the current LocationProvider (see UserCountry plugin code) from
+ * the Tracker cache.
+ */
+ public static function getCurrentLocationProviderId()
+ {
+ $cache = Piwik_Tracker_Cache::getCacheGeneral();
+ return empty($cache['currentLocationProviderId'])
+ ? Piwik_UserCountry_LocationProvider_Default::ID
+ : $cache['currentLocationProviderId'];
+ }
}
/**
@@ -1692,27 +1544,22 @@ class Piwik_Common
*/
function destroy(&$var)
{
- if (is_object($var)) $var->__destruct();
- unset($var);
- $var = null;
+ if (is_object($var)) $var->__destruct();
+ unset($var);
+ $var = null;
}
-if(!function_exists('printDebug'))
-{
- function printDebug( $info = '' )
- {
- if(isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG'])
- {
- if(is_array($info))
- {
- print("<pre>");
- print(htmlspecialchars(var_export($info,true), ENT_QUOTES));
- print("</pre>");
- }
- else
- {
- print(htmlspecialchars($info, ENT_QUOTES) . "<br />\n");
- }
- }
- }
+if (!function_exists('printDebug')) {
+ function printDebug($info = '')
+ {
+ if (isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) {
+ if (is_array($info)) {
+ print("<pre>");
+ print(htmlspecialchars(var_export($info, true), ENT_QUOTES));
+ print("</pre>");
+ } else {
+ print(htmlspecialchars($info, ENT_QUOTES) . "<br />\n");
+ }
+ }
+ }
}
diff --git a/core/Config.php b/core/Config.php
index 78d236d4ac..e906311306 100644
--- a/core/Config.php
+++ b/core/Config.php
@@ -30,7 +30,7 @@
* Piwik_Config::getInstance()->branding = $brandingConfig;
*
* Example setting an option within a section in the configuration:
- *
+ *
* $brandingConfig = Piwik_Config::getInstance()->branding;
* $brandingConfig['use_custom_logo'] = 1;
* Piwik_Config::getInstance()->branding = $brandingConfig;
@@ -40,473 +40,431 @@
*/
class Piwik_Config
{
- static private $instance = null;
-
- /**
- * Returns the singleton Piwik_Config
- *
- * @return Piwik_Config
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Contains configuration files values
- *
- * @var array
- */
- protected $initialized = false;
- protected $configGlobal = array();
- protected $configLocal = array();
- protected $configCache = array();
- protected $pathGlobal = null;
- protected $pathLocal = null;
-
- protected function __construct()
- {
- $this->pathGlobal = self::getGlobalConfigPath();
- $this->pathLocal = self::getLocalConfigPath();
- }
-
- /**
- * @var boolean
- */
- protected $isTest = false;
-
- /**
- * Enable test environment
- *
- * @param string $pathLocal
- * @param string $pathGlobal
- */
- public function setTestEnvironment($pathLocal = null, $pathGlobal = null)
- {
- $this->isTest = true;
-
- $this->clear();
-
- if ($pathLocal)
- {
- $this->pathLocal = $pathLocal;
- }
-
- if ($pathGlobal)
- {
- $this->pathGlobal = $pathGlobal;
- }
-
- $this->init();
- if(isset($this->configGlobal['database_tests'])
- || isset($this->configLocal['database_tests']))
- {
- $this->__get('database_tests');
- $this->configCache['database'] = $this->configCache['database_tests'];
- }
-
- // Ensure local mods do not affect tests
- if(is_null($pathGlobal))
- {
- $this->configCache['Debug'] = $this->configGlobal['Debug'];
- $this->configCache['branding'] = $this->configGlobal['branding'];
- $this->configCache['mail'] = $this->configGlobal['mail'];
- $this->configCache['General'] = $this->configGlobal['General'];
- $this->configCache['Segments'] = $this->configGlobal['Segments'];
- $this->configCache['Tracker'] = $this->configGlobal['Tracker'];
- $this->configCache['Deletelogs'] = $this->configGlobal['Deletelogs'];
- }
-
- // for unit tests, we set that no plugin is installed. This will force
- // the test initialization to create the plugins tables, execute ALTER queries, etc.
- $this->configCache['PluginsInstalled'] = array('PluginsInstalled' => array());
- }
-
- /**
- * Returns absolute path to the global configuration file
- *
- * @return string
- */
- static public function getGlobalConfigPath()
- {
- return PIWIK_USER_PATH .'/config/global.ini.php';
- }
-
- /**
- * Backward compatibility stub
- *
- * @todo remove in 2.0
- * @since 1.7
- * @deprecated 1.7
- * @return string
- */
- static public function getDefaultDefaultConfigPath()
- {
- return self::getGlobalConfigPath();
- }
-
- /**
- * Returns absolute path to the local configuration file
- *
- * @return string
- */
- static public function getLocalConfigPath()
- {
- return PIWIK_USER_PATH .'/config/config.ini.php';
- }
-
- /**
- * Is local configuration file writable?
- *
- * @return bool
- */
- public function isFileWritable()
- {
- return is_writable($this->pathLocal);
- }
-
- /**
- * Clear in-memory configuration so it can be reloaded
- */
- public function clear()
- {
- $this->configGlobal = array();
- $this->configLocal = array();
- $this->configCache = array();
- $this->initialized = false;
-
- $this->pathGlobal = self::getGlobalConfigPath();
- $this->pathLocal = self::getLocalConfigPath();
- }
-
- /**
- * Read configuration from files into memory
- *
- * @throws Exception if local config file is not readable; exits for other errors
- */
- public function init()
- {
- $this->initialized = true;
- $reportError = empty($GLOBALS['PIWIK_TRACKER_MODE']);
-
- // read defaults from global.ini.php
- if(!is_readable($this->pathGlobal) && $reportError)
- {
- Piwik_ExitWithMessage(Piwik_TranslateException('General_ExceptionConfigurationFileNotFound', array($this->pathGlobal)));
- }
-
- $this->configGlobal = _parse_ini_file($this->pathGlobal, true);
- if(empty($this->configGlobal) && $reportError)
- {
- Piwik_ExitWithMessage(Piwik_TranslateException('General_ExceptionUnreadableFileDisabledMethod', array($this->pathGlobal, "parse_ini_file()")));
- }
-
- // read the local settings from config.ini.php
- if(!is_readable($this->pathLocal) && $reportError)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionConfigurationFileNotFound', array($this->pathLocal)));
- }
-
- $this->configLocal = _parse_ini_file($this->pathLocal, true);
- if(empty($this->configLocal) && $reportError)
- {
- Piwik_ExitWithMessage(Piwik_TranslateException('General_ExceptionUnreadableFileDisabledMethod', array($this->pathLocal, "parse_ini_file()")));
- }
- }
-
- /**
- * Decode HTML entities
- *
- * @param mixed $values
- * @return mixed
- */
- protected function decodeValues($values)
- {
- if(is_array($values))
- {
- foreach($values as &$value)
- {
- $value = $this->decodeValues($value);
- }
- }
- else
- {
- $values = html_entity_decode($values, ENT_COMPAT, 'UTF-8');
- }
- return $values;
- }
-
- /**
- * Encode HTML entities
- *
- * @param mixed $values
- * @return mixed
- */
- protected function encodeValues($values)
- {
- if(is_array($values))
- {
- foreach($values as &$value)
- {
- $value = $this->encodeValues($value);
- }
- }
- else
- {
- $values = htmlentities($values, ENT_COMPAT, 'UTF-8');
- }
- return $values;
- }
-
- /**
- * Magic get methods catching calls to $config->var_name
- * Returns the value if found in the configuration
- *
- * @param string $name
- * @return string|array The value requested, returned by reference
- * @throws Exception if the value requested not found in both files
- */
- public function &__get( $name )
- {
- if(!$this->initialized)
- {
- $this->init();
- }
-
- // check cache for merged section
- if (isset($this->configCache[$name]))
- {
- $tmp =& $this->configCache[$name];
- return $tmp;
- }
-
- $section = null;
-
- // merge corresponding sections from global and local settings
- if(isset($this->configGlobal[$name]))
- {
- $section = $this->configGlobal[$name];
- }
-
- if(isset($this->configLocal[$name]))
- {
- // local settings override the global defaults
- $section = $section
- ? array_merge($section, $this->configLocal[$name])
- : $this->configLocal[$name];
- }
-
- if ($section === null)
- {
- throw new Exception("Error while trying to read a specific config file entry <b>'$name'</b> from your configuration files.</b>If you just completed a Piwik upgrade, please check that the file config/global.ini.php was overwritten by the latest Piwik version.");
- }
-
- // cache merged section for later
- $this->configCache[$name] = $this->decodeValues($section);
- $tmp =& $this->configCache[$name];
-
- return $tmp;
- }
-
- /**
- * Set value
- *
- * @param string $name This corresponds to the section name
- * @param mixed $value
- */
- public function __set($name, $value)
- {
- $this->configCache[$name] = $value;
- }
-
- /**
- * Comparison function
- *
- * @param mixed $elem1
- * @param mixed $elem2
- * @return int;
- */
- static function compareElements($elem1, $elem2)
- {
- if (is_array($elem1))
- {
- if (is_array($elem2))
- {
- return strcmp(serialize($elem1), serialize($elem2));
- }
-
- return 1;
- }
-
- if (is_array($elem2))
- {
- return -1;
- }
-
- if ((string)$elem1 === (string)$elem2)
- {
- return 0;
- }
-
- return ((string)$elem1 > (string)$elem2) ? 1 : -1;
- }
-
- /**
- * Compare arrays and return difference, such that:
- *
- * $modified = array_merge($original, $difference);
- *
- * @param array $original original array
- * @param array $modified modified array
- * @return array differences between original and modified
- */
- public function array_unmerge($original, $modified)
- {
- // return key/value pairs for keys in $modified but not in $original
- // return key/value pairs for keys in both $modified and $original, but values differ
- // ignore keys that are in $original but not in $modified
-
- return array_udiff_assoc($modified, $original, array(__CLASS__, 'compareElements'));
- }
-
- /**
- * Dump config
- *
- * @param array $configLocal
- * @param array $configGlobal
- * @param array $configCache
- * @return string
- */
- public function dumpConfig($configLocal, $configGlobal, $configCache)
- {
- $dirty = false;
-
- $output = "; <?php exit; ?> DO NOT REMOVE THIS LINE\n";
- $output .= "; file automatically generated or modified by Piwik; you can manually override the default values in global.ini.php by redefining them in this file.\n";
-
- if ($configCache)
- {
- foreach($configLocal as $name => $section)
- {
- if (!isset($configCache[$name]))
- {
- $configCache[$name] = $this->decodeValues($section);
- }
- }
-
- $sectionNames = array_unique(array_merge(array_keys($configGlobal), array_keys($configCache)));
-
- foreach($sectionNames as $section)
- {
- if(!isset($configCache[$section]))
- {
- continue;
- }
-
- // Only merge if the section exists in global.ini.php (in case a section only lives in config.ini.php)
-
- // get local and cached config
- $local = isset($configLocal[$section]) ? $configLocal[$section] : array();
- $config = $configCache[$section];
-
- // remove default values from both (they should not get written to local)
- if (isset($configGlobal[$section]))
- {
- $config = $this->array_unmerge($configGlobal[$section], $configCache[$section]);
- $local = $this->array_unmerge($configGlobal[$section], $local);
- }
-
- // if either local/config have non-default values and the other doesn't,
- // OR both have values, but different values, we must write to config.ini.php
- if (empty($local) xor empty($config)
- || (!empty($local)
- && !empty($config)
- && self::compareElements($config, $configLocal[$section])))
- {
- $dirty = true;
- }
-
- // no point in writing empty sections, so skip if the cached section is empty
- if (empty($config))
- {
- continue;
- }
-
- $output .= "[$section]\n";
-
- foreach($config as $name => $value)
- {
- $value = $this->encodeValues($value);
-
- if(is_numeric($name))
- {
- $name = $section;
- $value = array($value);
- }
-
- if(is_array($value))
- {
- foreach($value as $currentValue)
- {
- $output .= $name."[] = \"$currentValue\"\n";
- }
- }
- else
- {
- if(!is_numeric($value))
- {
- $value = "\"$value\"";
- }
- $output .= $name.' = '.$value."\n";
- }
- }
-
- $output .= "\n";
- }
-
- if ($dirty)
- {
- return $output;
- }
- }
-
- return false;
- }
-
-
- /**
- * Write user configuration file
- *
- * @param array $configLocal
- * @param array $configGlobal
- * @param array $configCache
- * @param string $pathLocal
- */
- public function writeConfig($configLocal, $configGlobal, $configCache, $pathLocal)
- {
- if ($this->isTest)
- {
- return;
- }
-
- $output = $this->dumpConfig($configLocal, $configGlobal, $configCache);
- if ($output !== false)
- {
- @file_put_contents($pathLocal, $output);
- }
-
- $this->clear();
- }
-
- /**
- * Force save
- */
- public function forceSave()
- {
- $this->writeConfig($this->configLocal, $this->configGlobal, $this->configCache, $this->pathLocal);
- }
+ static private $instance = null;
+
+ /**
+ * Returns the singleton Piwik_Config
+ *
+ * @return Piwik_Config
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Contains configuration files values
+ *
+ * @var array
+ */
+ protected $initialized = false;
+ protected $configGlobal = array();
+ protected $configLocal = array();
+ protected $configCache = array();
+ protected $pathGlobal = null;
+ protected $pathLocal = null;
+
+ protected function __construct()
+ {
+ $this->pathGlobal = self::getGlobalConfigPath();
+ $this->pathLocal = self::getLocalConfigPath();
+ }
+
+ /**
+ * @var boolean
+ */
+ protected $isTest = false;
+
+ /**
+ * Enable test environment
+ *
+ * @param string $pathLocal
+ * @param string $pathGlobal
+ */
+ public function setTestEnvironment($pathLocal = null, $pathGlobal = null)
+ {
+ $this->isTest = true;
+
+ $this->clear();
+
+ if ($pathLocal) {
+ $this->pathLocal = $pathLocal;
+ }
+
+ if ($pathGlobal) {
+ $this->pathGlobal = $pathGlobal;
+ }
+
+ $this->init();
+ if (isset($this->configGlobal['database_tests'])
+ || isset($this->configLocal['database_tests'])
+ ) {
+ $this->__get('database_tests');
+ $this->configCache['database'] = $this->configCache['database_tests'];
+ }
+
+ // Ensure local mods do not affect tests
+ if (is_null($pathGlobal)) {
+ $this->configCache['Debug'] = $this->configGlobal['Debug'];
+ $this->configCache['branding'] = $this->configGlobal['branding'];
+ $this->configCache['mail'] = $this->configGlobal['mail'];
+ $this->configCache['General'] = $this->configGlobal['General'];
+ $this->configCache['Segments'] = $this->configGlobal['Segments'];
+ $this->configCache['Tracker'] = $this->configGlobal['Tracker'];
+ $this->configCache['Deletelogs'] = $this->configGlobal['Deletelogs'];
+ }
+
+ // for unit tests, we set that no plugin is installed. This will force
+ // the test initialization to create the plugins tables, execute ALTER queries, etc.
+ $this->configCache['PluginsInstalled'] = array('PluginsInstalled' => array());
+ }
+
+ /**
+ * Returns absolute path to the global configuration file
+ *
+ * @return string
+ */
+ static public function getGlobalConfigPath()
+ {
+ return PIWIK_USER_PATH . '/config/global.ini.php';
+ }
+
+ /**
+ * Backward compatibility stub
+ *
+ * @todo remove in 2.0
+ * @since 1.7
+ * @deprecated 1.7
+ * @return string
+ */
+ static public function getDefaultDefaultConfigPath()
+ {
+ return self::getGlobalConfigPath();
+ }
+
+ /**
+ * Returns absolute path to the local configuration file
+ *
+ * @return string
+ */
+ static public function getLocalConfigPath()
+ {
+ return PIWIK_USER_PATH . '/config/config.ini.php';
+ }
+
+ /**
+ * Is local configuration file writable?
+ *
+ * @return bool
+ */
+ public function isFileWritable()
+ {
+ return is_writable($this->pathLocal);
+ }
+
+ /**
+ * Clear in-memory configuration so it can be reloaded
+ */
+ public function clear()
+ {
+ $this->configGlobal = array();
+ $this->configLocal = array();
+ $this->configCache = array();
+ $this->initialized = false;
+
+ $this->pathGlobal = self::getGlobalConfigPath();
+ $this->pathLocal = self::getLocalConfigPath();
+ }
+
+ /**
+ * Read configuration from files into memory
+ *
+ * @throws Exception if local config file is not readable; exits for other errors
+ */
+ public function init()
+ {
+ $this->initialized = true;
+ $reportError = empty($GLOBALS['PIWIK_TRACKER_MODE']);
+
+ // read defaults from global.ini.php
+ if (!is_readable($this->pathGlobal) && $reportError) {
+ Piwik_ExitWithMessage(Piwik_TranslateException('General_ExceptionConfigurationFileNotFound', array($this->pathGlobal)));
+ }
+
+ $this->configGlobal = _parse_ini_file($this->pathGlobal, true);
+ if (empty($this->configGlobal) && $reportError) {
+ Piwik_ExitWithMessage(Piwik_TranslateException('General_ExceptionUnreadableFileDisabledMethod', array($this->pathGlobal, "parse_ini_file()")));
+ }
+
+ // read the local settings from config.ini.php
+ if (!is_readable($this->pathLocal) && $reportError) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionConfigurationFileNotFound', array($this->pathLocal)));
+ }
+
+ $this->configLocal = _parse_ini_file($this->pathLocal, true);
+ if (empty($this->configLocal) && $reportError) {
+ Piwik_ExitWithMessage(Piwik_TranslateException('General_ExceptionUnreadableFileDisabledMethod', array($this->pathLocal, "parse_ini_file()")));
+ }
+ }
+
+ /**
+ * Decode HTML entities
+ *
+ * @param mixed $values
+ * @return mixed
+ */
+ protected function decodeValues($values)
+ {
+ if (is_array($values)) {
+ foreach ($values as &$value) {
+ $value = $this->decodeValues($value);
+ }
+ } else {
+ $values = html_entity_decode($values, ENT_COMPAT, 'UTF-8');
+ }
+ return $values;
+ }
+
+ /**
+ * Encode HTML entities
+ *
+ * @param mixed $values
+ * @return mixed
+ */
+ protected function encodeValues($values)
+ {
+ if (is_array($values)) {
+ foreach ($values as &$value) {
+ $value = $this->encodeValues($value);
+ }
+ } else {
+ $values = htmlentities($values, ENT_COMPAT, 'UTF-8');
+ }
+ return $values;
+ }
+
+ /**
+ * Magic get methods catching calls to $config->var_name
+ * Returns the value if found in the configuration
+ *
+ * @param string $name
+ * @return string|array The value requested, returned by reference
+ * @throws Exception if the value requested not found in both files
+ */
+ public function &__get($name)
+ {
+ if (!$this->initialized) {
+ $this->init();
+ }
+
+ // check cache for merged section
+ if (isset($this->configCache[$name])) {
+ $tmp =& $this->configCache[$name];
+ return $tmp;
+ }
+
+ $section = null;
+
+ // merge corresponding sections from global and local settings
+ if (isset($this->configGlobal[$name])) {
+ $section = $this->configGlobal[$name];
+ }
+
+ if (isset($this->configLocal[$name])) {
+ // local settings override the global defaults
+ $section = $section
+ ? array_merge($section, $this->configLocal[$name])
+ : $this->configLocal[$name];
+ }
+
+ if ($section === null) {
+ throw new Exception("Error while trying to read a specific config file entry <b>'$name'</b> from your configuration files.</b>If you just completed a Piwik upgrade, please check that the file config/global.ini.php was overwritten by the latest Piwik version.");
+ }
+
+ // cache merged section for later
+ $this->configCache[$name] = $this->decodeValues($section);
+ $tmp =& $this->configCache[$name];
+
+ return $tmp;
+ }
+
+ /**
+ * Set value
+ *
+ * @param string $name This corresponds to the section name
+ * @param mixed $value
+ */
+ public function __set($name, $value)
+ {
+ $this->configCache[$name] = $value;
+ }
+
+ /**
+ * Comparison function
+ *
+ * @param mixed $elem1
+ * @param mixed $elem2
+ * @return int;
+ */
+ static function compareElements($elem1, $elem2)
+ {
+ if (is_array($elem1)) {
+ if (is_array($elem2)) {
+ return strcmp(serialize($elem1), serialize($elem2));
+ }
+
+ return 1;
+ }
+
+ if (is_array($elem2)) {
+ return -1;
+ }
+
+ if ((string)$elem1 === (string)$elem2) {
+ return 0;
+ }
+
+ return ((string)$elem1 > (string)$elem2) ? 1 : -1;
+ }
+
+ /**
+ * Compare arrays and return difference, such that:
+ *
+ * $modified = array_merge($original, $difference);
+ *
+ * @param array $original original array
+ * @param array $modified modified array
+ * @return array differences between original and modified
+ */
+ public function array_unmerge($original, $modified)
+ {
+ // return key/value pairs for keys in $modified but not in $original
+ // return key/value pairs for keys in both $modified and $original, but values differ
+ // ignore keys that are in $original but not in $modified
+
+ return array_udiff_assoc($modified, $original, array(__CLASS__, 'compareElements'));
+ }
+
+ /**
+ * Dump config
+ *
+ * @param array $configLocal
+ * @param array $configGlobal
+ * @param array $configCache
+ * @return string
+ */
+ public function dumpConfig($configLocal, $configGlobal, $configCache)
+ {
+ $dirty = false;
+
+ $output = "; <?php exit; ?> DO NOT REMOVE THIS LINE\n";
+ $output .= "; file automatically generated or modified by Piwik; you can manually override the default values in global.ini.php by redefining them in this file.\n";
+
+ if ($configCache) {
+ foreach ($configLocal as $name => $section) {
+ if (!isset($configCache[$name])) {
+ $configCache[$name] = $this->decodeValues($section);
+ }
+ }
+
+ $sectionNames = array_unique(array_merge(array_keys($configGlobal), array_keys($configCache)));
+
+ foreach ($sectionNames as $section) {
+ if (!isset($configCache[$section])) {
+ continue;
+ }
+
+ // Only merge if the section exists in global.ini.php (in case a section only lives in config.ini.php)
+
+ // get local and cached config
+ $local = isset($configLocal[$section]) ? $configLocal[$section] : array();
+ $config = $configCache[$section];
+
+ // remove default values from both (they should not get written to local)
+ if (isset($configGlobal[$section])) {
+ $config = $this->array_unmerge($configGlobal[$section], $configCache[$section]);
+ $local = $this->array_unmerge($configGlobal[$section], $local);
+ }
+
+ // if either local/config have non-default values and the other doesn't,
+ // OR both have values, but different values, we must write to config.ini.php
+ if (empty($local) xor empty($config)
+ || (!empty($local)
+ && !empty($config)
+ && self::compareElements($config, $configLocal[$section]))
+ ) {
+ $dirty = true;
+ }
+
+ // no point in writing empty sections, so skip if the cached section is empty
+ if (empty($config)) {
+ continue;
+ }
+
+ $output .= "[$section]\n";
+
+ foreach ($config as $name => $value) {
+ $value = $this->encodeValues($value);
+
+ if (is_numeric($name)) {
+ $name = $section;
+ $value = array($value);
+ }
+
+ if (is_array($value)) {
+ foreach ($value as $currentValue) {
+ $output .= $name . "[] = \"$currentValue\"\n";
+ }
+ } else {
+ if (!is_numeric($value)) {
+ $value = "\"$value\"";
+ }
+ $output .= $name . ' = ' . $value . "\n";
+ }
+ }
+
+ $output .= "\n";
+ }
+
+ if ($dirty) {
+ return $output;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Write user configuration file
+ *
+ * @param array $configLocal
+ * @param array $configGlobal
+ * @param array $configCache
+ * @param string $pathLocal
+ */
+ public function writeConfig($configLocal, $configGlobal, $configCache, $pathLocal)
+ {
+ if ($this->isTest) {
+ return;
+ }
+
+ $output = $this->dumpConfig($configLocal, $configGlobal, $configCache);
+ if ($output !== false) {
+ @file_put_contents($pathLocal, $output);
+ }
+
+ $this->clear();
+ }
+
+ /**
+ * Force save
+ */
+ public function forceSave()
+ {
+ $this->writeConfig($this->configLocal, $this->configGlobal, $this->configCache, $this->pathLocal);
+ }
}
diff --git a/core/Config/Compat.php b/core/Config/Compat.php
index 2169e3e284..549b2cb9d7 100644
--- a/core/Config/Compat.php
+++ b/core/Config/Compat.php
@@ -22,149 +22,145 @@
*/
class Piwik_Config_Compat_Array
{
- private $data;
- /**
- * @var Piwik_Config_Compat
- */
- private $parent;
-
- /**
- * Constructor
- *
- * @param Piwik_Config_Compat $parent
- * @param array $data configuration section
- */
- public function __construct($parent, array $data)
- {
- $this->parent = $parent;
- $this->data = $data;
- }
-
- /**
- * Get value by name
- *
- * @param string $name
- * @return mixed
- */
- public function __get($name)
- {
- $tmp = isset($this->data[$name]) ? $this->data[$name] : false;
- return is_array($tmp) ? new Piwik_Config_Compat_Array($this, $tmp) : $tmp;
- }
-
- /**
- * Set name, value pair
- *
- * @param string $name
- * @param mixed $value
- */
- public function __set($name, $value)
- {
- if (is_object($value) && get_class($value) == 'Piwik_Config_Compat_Array')
- {
- $value = $value->toArray();
- }
-
- $this->data[$name] = $value;
- $this->setDirtyBit();
- }
-
- /**
- * Convert object to array
- *
- * @return array
- */
- public function toArray()
- {
- return $this->data;
- }
-
- /**
- * Set dirty bit
- */
- public function setDirtyBit()
- {
- $this->parent->setDirtyBit();
- }
+ private $data;
+ /**
+ * @var Piwik_Config_Compat
+ */
+ private $parent;
+
+ /**
+ * Constructor
+ *
+ * @param Piwik_Config_Compat $parent
+ * @param array $data configuration section
+ */
+ public function __construct($parent, array $data)
+ {
+ $this->parent = $parent;
+ $this->data = $data;
+ }
+
+ /**
+ * Get value by name
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ $tmp = isset($this->data[$name]) ? $this->data[$name] : false;
+ return is_array($tmp) ? new Piwik_Config_Compat_Array($this, $tmp) : $tmp;
+ }
+
+ /**
+ * Set name, value pair
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function __set($name, $value)
+ {
+ if (is_object($value) && get_class($value) == 'Piwik_Config_Compat_Array') {
+ $value = $value->toArray();
+ }
+
+ $this->data[$name] = $value;
+ $this->setDirtyBit();
+ }
+
+ /**
+ * Convert object to array
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Set dirty bit
+ */
+ public function setDirtyBit()
+ {
+ $this->parent->setDirtyBit();
+ }
}
class Piwik_Config_Compat
{
- private $config;
- private $data;
- private $enabled;
- private $dirty;
-
- /**
- * Constructor
- */
- public function __construct()
- {
- $this->config = Piwik_Config::getInstance();
- $this->data = array();
- $this->enabled = true;
- $this->dirty = false;
- }
-
- /**
- * Destructor
- */
- public function __destruct()
- {
- if ($this->enabled && $this->dirty)
- {
- $this->config->forceSave();
- }
- $this->config->clear();
- }
-
- /**
- * Get value by name
- *
- * @param string $name
- * @return mixed
- */
- public function __get($name)
- {
- if (!isset($this->data[$name]))
- {
- $this->data[$name] = $this->config->__get($name);
- }
-
- $tmp = $this->data[$name];
- return is_array($tmp) ? new Piwik_Config_Compat_Array($this, $tmp) : $tmp;
- }
-
- /**
- * Set name, value pair
- *
- * @param string $name
- * @param mixed $value
- */
- public function __set($name, $value)
- {
- if (is_object($value) && get_class($value) == 'Piwik_Config_Compat_Array')
- {
- $value = $value->toArray();
- }
-
- $this->config->__set($name, $value);
- $this->dirty = true;
- }
-
- /**
- * Set dirty bit
- */
- public function setDirtyBit()
- {
- $this->dirty = true;
- }
-
- /**
- * Disable saving of configuration changes
- */
- public function disableSavingConfigurationFileUpdates()
- {
- $this->enabled = false;
- }
+ private $config;
+ private $data;
+ private $enabled;
+ private $dirty;
+
+ /**
+ * Constructor
+ */
+ public function __construct()
+ {
+ $this->config = Piwik_Config::getInstance();
+ $this->data = array();
+ $this->enabled = true;
+ $this->dirty = false;
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ if ($this->enabled && $this->dirty) {
+ $this->config->forceSave();
+ }
+ $this->config->clear();
+ }
+
+ /**
+ * Get value by name
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ if (!isset($this->data[$name])) {
+ $this->data[$name] = $this->config->__get($name);
+ }
+
+ $tmp = $this->data[$name];
+ return is_array($tmp) ? new Piwik_Config_Compat_Array($this, $tmp) : $tmp;
+ }
+
+ /**
+ * Set name, value pair
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function __set($name, $value)
+ {
+ if (is_object($value) && get_class($value) == 'Piwik_Config_Compat_Array') {
+ $value = $value->toArray();
+ }
+
+ $this->config->__set($name, $value);
+ $this->dirty = true;
+ }
+
+ /**
+ * Set dirty bit
+ */
+ public function setDirtyBit()
+ {
+ $this->dirty = true;
+ }
+
+ /**
+ * Disable saving of configuration changes
+ */
+ public function disableSavingConfigurationFileUpdates()
+ {
+ $this->enabled = false;
+ }
}
diff --git a/core/Controller.php b/core/Controller.php
index 0c625bde6e..9d97b6ff90 100644
--- a/core/Controller.php
+++ b/core/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,914 +12,850 @@
/**
* Parent class of all plugins Controllers (located in /plugins/PluginName/Controller.php
* It defines some helper functions controllers can use.
- *
+ *
* @package Piwik
*/
abstract class Piwik_Controller
{
- /**
- * Plugin name, eg. Referers
- * @var string
- */
- protected $pluginName;
-
- /**
- * Date string
- *
- * @var string
- */
- protected $strDate;
-
- /**
- * Piwik_Date object or null if the requested date is a range
- *
- * @var Piwik_Date|null
- */
- protected $date;
-
- /**
- * @var int
- */
- protected $idSite;
-
- /**
- * @var Piwik_Site
- */
- protected $site = null;
-
- /**
- * Builds the controller object, reads the date from the request, extracts plugin name from
- */
- function __construct()
- {
- $this->init();
- }
-
- protected function init()
- {
- $aPluginName = explode('_', get_class($this));
- $this->pluginName = $aPluginName[1];
- $date = Piwik_Common::getRequestVar('date', 'yesterday', 'string');
- try {
- $this->idSite = Piwik_Common::getRequestVar('idSite', false, 'int');
- $this->site = new Piwik_Site($this->idSite);
- $date = $this->getDateParameterInTimezone($date, $this->site->getTimezone());
- $this->setDate($date);
- } catch(Exception $e){
- // the date looks like YYYY-MM-DD,YYYY-MM-DD or other format
- $this->date = null;
- }
- }
-
- /**
- * Helper method to convert "today" or "yesterday" to the default timezone specified.
- * If the date is absolute, ie. YYYY-MM-DD, it will not be converted to the timezone
- *
- * @param string $date today, yesterday, YYYY-MM-DD
- * @param string $defaultTimezone default timezone to use
- * @return Piwik_Date
- */
- protected function getDateParameterInTimezone($date, $defaultTimezone )
- {
- $timezone = null;
- // if the requested date is not YYYY-MM-DD, we need to ensure
- // it is relative to the website's timezone
- if(in_array($date, array('today', 'yesterday')))
- {
- // today is at midnight; we really want to get the time now, so that
- // * if the website is UTC+12 and it is 5PM now in UTC, the calendar will allow to select the UTC "tomorrow"
- // * if the website is UTC-12 and it is 5AM now in UTC, the calendar will allow to select the UTC "yesterday"
- if($date == 'today')
- {
- $date = 'now';
- }
- elseif($date == 'yesterday')
- {
- $date = 'yesterdaySameTime';
- }
- $timezone = $defaultTimezone;
- }
- return Piwik_Date::factory($date, $timezone);
- }
-
- /**
- * Sets the date to be used by all other methods in the controller.
- * If the date has to be modified, it should be called just after the controller construct
- *
- * @param Piwik_Date $date
- * @return void
- */
- protected function setDate(Piwik_Date $date)
- {
- $this->date = $date;
- $strDate = $this->date->toString();
- $this->strDate = $strDate;
- }
-
- /**
- * Returns the name of the default method that will be called
- * when visiting: index.php?module=PluginName without the action parameter
- *
- * @return string
- */
- function getDefaultAction()
- {
- return 'index';
- }
-
- /**
- * Given an Object implementing Piwik_View_Interface, we either:
- * - echo the output of the rendering if fetch = false
- * - returns the output of the rendering if fetch = true
- *
- * @param Piwik_ViewDataTable $view view object to use
- * @param bool $fetch indicates whether to output or return the content
- * @return string|void
- */
- protected function renderView( Piwik_ViewDataTable $view, $fetch = false)
- {
- Piwik_PostEvent( 'Controller.renderView',
- $this,
- array( 'view' => $view,
- 'controllerName' => $view->getCurrentControllerName(),
- 'controllerAction' => $view->getCurrentControllerAction(),
- 'apiMethodToRequestDataTable' => $view->getApiMethodToRequestDataTable(),
- 'controllerActionCalledWhenRequestSubTable' => $view->getControllerActionCalledWhenRequestSubTable(),
- )
- );
-
- $view->main();
-
- $rendered = $view->getView()->render();
- if($fetch)
- {
- return $rendered;
- }
- echo $rendered;
- }
-
- /**
- * Returns a ViewDataTable object of an Evolution graph
- * for the last30 days/weeks/etc. of the current period, relative to the current date.
- *
- * @param string $currentModuleName
- * @param string $currentControllerAction
- * @param string $apiMethod
- * @return Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution
- */
- protected function getLastUnitGraph($currentModuleName, $currentControllerAction, $apiMethod)
- {
- $view = Piwik_ViewDataTable::factory('graphEvolution');
- $view->init( $currentModuleName, $currentControllerAction, $apiMethod );
- return $view;
- }
-
- /**
- * This method is similar to self::getLastUnitGraph. It works with API.get to combine metrics
- * of different *.get reports. The returned ViewDataTable is configured with column
- * translations and selectable metrics.
- *
- * @param string $currentModuleName
- * @param string $currentControllerAction
- * @param array $columnsToDisplay
- * @param array $selectableColumns
- * @param bool|string $reportDocumentation
- * @param string $apiMethod The method to request the report from
- * (by default, this is API.get but it can be changed for custom stuff)
- * @return Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution
- */
- protected function getLastUnitGraphAcrossPlugins($currentModuleName, $currentControllerAction,
- $columnsToDisplay, $selectableColumns=array(), $reportDocumentation=false, $apiMethod='API.get')
- {
- // back up and manipulate the columns parameter
- $backupColumns = false;
- if (isset($_GET['columns']))
- {
- $backupColumns = $_GET['columns'];
- }
-
- $_GET['columns'] = implode(',', $columnsToDisplay);
-
- // load translations from meta data
- $idSite = Piwik_Common::getRequestVar('idSite');
- $period = Piwik_Common::getRequestVar('period');
- $date = Piwik_Common::getRequestVar('date');
- $meta = Piwik_API_API::getInstance()->getReportMetadata($idSite, $period, $date);
-
- $columns = array_merge($columnsToDisplay, $selectableColumns);
- $translations = array();
- foreach ($meta as $reportMeta)
- {
- if ($reportMeta['action'] == 'get' && !isset($reportMeta['parameters']))
- {
- foreach ($columns as $column)
- {
- if (isset($reportMeta['metrics'][$column]))
- {
- $translations[$column] = $reportMeta['metrics'][$column];
- }
- }
- }
- }
-
- // initialize the graph and load the data
- $view = $this->getLastUnitGraph($currentModuleName, $currentControllerAction, $apiMethod);
- $view->setColumnsToDisplay($columnsToDisplay);
- $view->setSelectableColumns($selectableColumns);
- $view->setColumnsTranslations($translations);
-
- if ($reportDocumentation)
- {
- $view->setReportDocumentation($reportDocumentation);
- }
-
- $view->main();
-
- // restore the columns parameter
- if ($backupColumns !== false)
- {
- $_GET['columns'] = $backupColumns;
- }
- else
- {
- unset($_GET['columns']);
- }
-
- return $view;
- }
-
- /**
- * Returns the array of new processed parameters once the parameters are applied.
- * For example: if you set range=last30 and date=2008-03-10,
- * the date element of the returned array will be "2008-02-10,2008-03-10"
- *
- * Parameters you can set:
- * - range: last30, previous10, etc.
- * - date: YYYY-MM-DD, today, yesterday
- * - period: day, week, month, year
- *
- * @param array $paramsToSet array( 'date' => 'last50', 'viewDataTable' =>'sparkline' )
- * @throws Piwik_Access_NoAccessException
- * @return array
- */
- protected function getGraphParamsModified($paramsToSet = array())
- {
- if(!isset($paramsToSet['period']))
- {
- $period = Piwik_Common::getRequestVar('period');
- }
- else
- {
- $period = $paramsToSet['period'];
- }
- if($period == 'range')
- {
- return $paramsToSet;
- }
- if(!isset($paramsToSet['range']))
- {
- $range = 'last30';
- }
- else
- {
- $range = $paramsToSet['range'];
- }
-
- if(!isset($paramsToSet['date']))
- {
- $endDate = $this->strDate;
- }
- else
- {
- $endDate = $paramsToSet['date'];
- }
-
- if(is_null($this->site))
- {
- throw new Piwik_Access_NoAccessException("Website not initialized, check that you are logged in and/or using the correct token_auth.");
- }
- $paramDate = self::getDateRangeRelativeToEndDate($period, $range, $endDate, $this->site);
-
- $params = array_merge($paramsToSet , array( 'date' => $paramDate ) );
- return $params;
- }
-
- /**
- * Given for example, $period = month, $lastN = 'last6', $endDate = '2011-07-01',
- * It will return the $date = '2011-01-01,2011-07-01' which is useful to draw graphs for the last N periods
- *
- * @param string $period
- * @param string $lastN
- * @param string $endDate
- * @param Piwik_Site $site
- * @return string
- */
- static public function getDateRangeRelativeToEndDate($period, $lastN, $endDate, $site )
- {
- $last30Relative = new Piwik_Period_Range($period, $lastN, $site->getTimezone() );
- $last30Relative->setDefaultEndDate(Piwik_Date::factory($endDate));
- $date = $last30Relative->getDateStart()->toString() . "," . $last30Relative->getDateEnd()->toString();
- return $date;
- }
-
- /**
- * Returns a numeric value from the API.
- * Works only for API methods that originally returns numeric values (there is no cast here)
- *
- * @param string $methodToCall Name of method to call, eg. Referers.getNumberOfDistinctSearchEngines
- * @param string|false $date A custom date to use when getting the value. If false, the 'date' query
- * parameter is used.
- * @return int|float
- */
- protected function getNumericValue( $methodToCall, $date = false )
- {
- $params = $date === false ? array() : array('date' => $date);
-
- $return = Piwik_API_Request::processRequest($methodToCall, $params);
- $columns = $return->getFirstRow()->getColumns();
- return reset($columns);
- }
-
- /**
- * Returns the current URL to use in a img src=X to display a sparkline.
- * $action must be the name of a Controller method that requests data using the Piwik_ViewDataTable::factory
- * It will automatically build a sparkline by setting the viewDataTable=sparkline parameter in the URL.
- * It will also computes automatically the 'date' for the 'last30' days/weeks/etc.
- *
- * @param string $action Method name of the controller to call in the img src
- * @param array $customParameters Array of name => value of parameters to set in the generated GET url
- * @return string The generated URL
- */
- protected function getUrlSparkline( $action, $customParameters = array() )
- {
- $params = $this->getGraphParamsModified(
- array( 'viewDataTable' => 'sparkline',
- 'action' => $action,
- 'module' => $this->pluginName)
- + $customParameters
- );
- // convert array values to comma separated
- foreach($params as &$value)
- {
- if(is_array($value))
- {
- $value = rawurlencode(implode(',', $value));
- }
- }
- $url = Piwik_Url::getCurrentQueryStringWithParametersModified($params);
- return $url;
- }
-
- /**
- * Sets the first date available in the calendar
- *
- * @param Piwik_Date $minDate
- * @param Piwik_View $view
- * @return void
- */
- protected function setMinDateView(Piwik_Date $minDate, $view)
- {
- $view->minDateYear = $minDate->toString('Y');
- $view->minDateMonth = $minDate->toString('m');
- $view->minDateDay = $minDate->toString('d');
- }
-
- /**
- * Sets "today" in the calendar. Today does not always mean "UTC" today, eg. for websites in UTC+12.
- *
- * @param Piwik_Date $maxDate
- * @param Piwik_View $view
- * @return void
- */
- protected function setMaxDateView(Piwik_Date $maxDate, $view)
- {
- $view->maxDateYear = $maxDate->toString('Y');
- $view->maxDateMonth = $maxDate->toString('m');
- $view->maxDateDay = $maxDate->toString('d');
- }
-
- /**
- * Sets general variables to the view that are used by
- * various templates and Javascript.
- * If any error happens, displays the login screen
- *
- * @param Piwik_View $view
- * @throws Exception
- * @return void
- */
- protected function setGeneralVariablesView($view)
- {
- $view->date = $this->strDate;
-
- try {
- $view->idSite = $this->idSite;
- if(empty($this->site) || empty($this->idSite))
- {
- throw new Exception("The requested website idSite is not found in the request, or is invalid.
+ /**
+ * Plugin name, eg. Referers
+ * @var string
+ */
+ protected $pluginName;
+
+ /**
+ * Date string
+ *
+ * @var string
+ */
+ protected $strDate;
+
+ /**
+ * Piwik_Date object or null if the requested date is a range
+ *
+ * @var Piwik_Date|null
+ */
+ protected $date;
+
+ /**
+ * @var int
+ */
+ protected $idSite;
+
+ /**
+ * @var Piwik_Site
+ */
+ protected $site = null;
+
+ /**
+ * Builds the controller object, reads the date from the request, extracts plugin name from
+ */
+ function __construct()
+ {
+ $this->init();
+ }
+
+ protected function init()
+ {
+ $aPluginName = explode('_', get_class($this));
+ $this->pluginName = $aPluginName[1];
+ $date = Piwik_Common::getRequestVar('date', 'yesterday', 'string');
+ try {
+ $this->idSite = Piwik_Common::getRequestVar('idSite', false, 'int');
+ $this->site = new Piwik_Site($this->idSite);
+ $date = $this->getDateParameterInTimezone($date, $this->site->getTimezone());
+ $this->setDate($date);
+ } catch (Exception $e) {
+ // the date looks like YYYY-MM-DD,YYYY-MM-DD or other format
+ $this->date = null;
+ }
+ }
+
+ /**
+ * Helper method to convert "today" or "yesterday" to the default timezone specified.
+ * If the date is absolute, ie. YYYY-MM-DD, it will not be converted to the timezone
+ *
+ * @param string $date today, yesterday, YYYY-MM-DD
+ * @param string $defaultTimezone default timezone to use
+ * @return Piwik_Date
+ */
+ protected function getDateParameterInTimezone($date, $defaultTimezone)
+ {
+ $timezone = null;
+ // if the requested date is not YYYY-MM-DD, we need to ensure
+ // it is relative to the website's timezone
+ if (in_array($date, array('today', 'yesterday'))) {
+ // today is at midnight; we really want to get the time now, so that
+ // * if the website is UTC+12 and it is 5PM now in UTC, the calendar will allow to select the UTC "tomorrow"
+ // * if the website is UTC-12 and it is 5AM now in UTC, the calendar will allow to select the UTC "yesterday"
+ if ($date == 'today') {
+ $date = 'now';
+ } elseif ($date == 'yesterday') {
+ $date = 'yesterdaySameTime';
+ }
+ $timezone = $defaultTimezone;
+ }
+ return Piwik_Date::factory($date, $timezone);
+ }
+
+ /**
+ * Sets the date to be used by all other methods in the controller.
+ * If the date has to be modified, it should be called just after the controller construct
+ *
+ * @param Piwik_Date $date
+ * @return void
+ */
+ protected function setDate(Piwik_Date $date)
+ {
+ $this->date = $date;
+ $strDate = $this->date->toString();
+ $this->strDate = $strDate;
+ }
+
+ /**
+ * Returns the name of the default method that will be called
+ * when visiting: index.php?module=PluginName without the action parameter
+ *
+ * @return string
+ */
+ function getDefaultAction()
+ {
+ return 'index';
+ }
+
+ /**
+ * Given an Object implementing Piwik_View_Interface, we either:
+ * - echo the output of the rendering if fetch = false
+ * - returns the output of the rendering if fetch = true
+ *
+ * @param Piwik_ViewDataTable $view view object to use
+ * @param bool $fetch indicates whether to output or return the content
+ * @return string|void
+ */
+ protected function renderView(Piwik_ViewDataTable $view, $fetch = false)
+ {
+ Piwik_PostEvent('Controller.renderView',
+ $this,
+ array('view' => $view,
+ 'controllerName' => $view->getCurrentControllerName(),
+ 'controllerAction' => $view->getCurrentControllerAction(),
+ 'apiMethodToRequestDataTable' => $view->getApiMethodToRequestDataTable(),
+ 'controllerActionCalledWhenRequestSubTable' => $view->getControllerActionCalledWhenRequestSubTable(),
+ )
+ );
+
+ $view->main();
+
+ $rendered = $view->getView()->render();
+ if ($fetch) {
+ return $rendered;
+ }
+ echo $rendered;
+ }
+
+ /**
+ * Returns a ViewDataTable object of an Evolution graph
+ * for the last30 days/weeks/etc. of the current period, relative to the current date.
+ *
+ * @param string $currentModuleName
+ * @param string $currentControllerAction
+ * @param string $apiMethod
+ * @return Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution
+ */
+ protected function getLastUnitGraph($currentModuleName, $currentControllerAction, $apiMethod)
+ {
+ $view = Piwik_ViewDataTable::factory('graphEvolution');
+ $view->init($currentModuleName, $currentControllerAction, $apiMethod);
+ return $view;
+ }
+
+ /**
+ * This method is similar to self::getLastUnitGraph. It works with API.get to combine metrics
+ * of different *.get reports. The returned ViewDataTable is configured with column
+ * translations and selectable metrics.
+ *
+ * @param string $currentModuleName
+ * @param string $currentControllerAction
+ * @param array $columnsToDisplay
+ * @param array $selectableColumns
+ * @param bool|string $reportDocumentation
+ * @param string $apiMethod The method to request the report from
+ * (by default, this is API.get but it can be changed for custom stuff)
+ * @return Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution
+ */
+ protected function getLastUnitGraphAcrossPlugins($currentModuleName, $currentControllerAction,
+ $columnsToDisplay, $selectableColumns = array(), $reportDocumentation = false, $apiMethod = 'API.get')
+ {
+ // back up and manipulate the columns parameter
+ $backupColumns = false;
+ if (isset($_GET['columns'])) {
+ $backupColumns = $_GET['columns'];
+ }
+
+ $_GET['columns'] = implode(',', $columnsToDisplay);
+
+ // load translations from meta data
+ $idSite = Piwik_Common::getRequestVar('idSite');
+ $period = Piwik_Common::getRequestVar('period');
+ $date = Piwik_Common::getRequestVar('date');
+ $meta = Piwik_API_API::getInstance()->getReportMetadata($idSite, $period, $date);
+
+ $columns = array_merge($columnsToDisplay, $selectableColumns);
+ $translations = array();
+ foreach ($meta as $reportMeta) {
+ if ($reportMeta['action'] == 'get' && !isset($reportMeta['parameters'])) {
+ foreach ($columns as $column) {
+ if (isset($reportMeta['metrics'][$column])) {
+ $translations[$column] = $reportMeta['metrics'][$column];
+ }
+ }
+ }
+ }
+
+ // initialize the graph and load the data
+ $view = $this->getLastUnitGraph($currentModuleName, $currentControllerAction, $apiMethod);
+ $view->setColumnsToDisplay($columnsToDisplay);
+ $view->setSelectableColumns($selectableColumns);
+ $view->setColumnsTranslations($translations);
+
+ if ($reportDocumentation) {
+ $view->setReportDocumentation($reportDocumentation);
+ }
+
+ $view->main();
+
+ // restore the columns parameter
+ if ($backupColumns !== false) {
+ $_GET['columns'] = $backupColumns;
+ } else {
+ unset($_GET['columns']);
+ }
+
+ return $view;
+ }
+
+ /**
+ * Returns the array of new processed parameters once the parameters are applied.
+ * For example: if you set range=last30 and date=2008-03-10,
+ * the date element of the returned array will be "2008-02-10,2008-03-10"
+ *
+ * Parameters you can set:
+ * - range: last30, previous10, etc.
+ * - date: YYYY-MM-DD, today, yesterday
+ * - period: day, week, month, year
+ *
+ * @param array $paramsToSet array( 'date' => 'last50', 'viewDataTable' =>'sparkline' )
+ * @throws Piwik_Access_NoAccessException
+ * @return array
+ */
+ protected function getGraphParamsModified($paramsToSet = array())
+ {
+ if (!isset($paramsToSet['period'])) {
+ $period = Piwik_Common::getRequestVar('period');
+ } else {
+ $period = $paramsToSet['period'];
+ }
+ if ($period == 'range') {
+ return $paramsToSet;
+ }
+ if (!isset($paramsToSet['range'])) {
+ $range = 'last30';
+ } else {
+ $range = $paramsToSet['range'];
+ }
+
+ if (!isset($paramsToSet['date'])) {
+ $endDate = $this->strDate;
+ } else {
+ $endDate = $paramsToSet['date'];
+ }
+
+ if (is_null($this->site)) {
+ throw new Piwik_Access_NoAccessException("Website not initialized, check that you are logged in and/or using the correct token_auth.");
+ }
+ $paramDate = self::getDateRangeRelativeToEndDate($period, $range, $endDate, $this->site);
+
+ $params = array_merge($paramsToSet, array('date' => $paramDate));
+ return $params;
+ }
+
+ /**
+ * Given for example, $period = month, $lastN = 'last6', $endDate = '2011-07-01',
+ * It will return the $date = '2011-01-01,2011-07-01' which is useful to draw graphs for the last N periods
+ *
+ * @param string $period
+ * @param string $lastN
+ * @param string $endDate
+ * @param Piwik_Site $site
+ * @return string
+ */
+ static public function getDateRangeRelativeToEndDate($period, $lastN, $endDate, $site)
+ {
+ $last30Relative = new Piwik_Period_Range($period, $lastN, $site->getTimezone());
+ $last30Relative->setDefaultEndDate(Piwik_Date::factory($endDate));
+ $date = $last30Relative->getDateStart()->toString() . "," . $last30Relative->getDateEnd()->toString();
+ return $date;
+ }
+
+ /**
+ * Returns a numeric value from the API.
+ * Works only for API methods that originally returns numeric values (there is no cast here)
+ *
+ * @param string $methodToCall Name of method to call, eg. Referers.getNumberOfDistinctSearchEngines
+ * @param string|false $date A custom date to use when getting the value. If false, the 'date' query
+ * parameter is used.
+ * @return int|float
+ */
+ protected function getNumericValue($methodToCall, $date = false)
+ {
+ $params = $date === false ? array() : array('date' => $date);
+
+ $return = Piwik_API_Request::processRequest($methodToCall, $params);
+ $columns = $return->getFirstRow()->getColumns();
+ return reset($columns);
+ }
+
+ /**
+ * Returns the current URL to use in a img src=X to display a sparkline.
+ * $action must be the name of a Controller method that requests data using the Piwik_ViewDataTable::factory
+ * It will automatically build a sparkline by setting the viewDataTable=sparkline parameter in the URL.
+ * It will also computes automatically the 'date' for the 'last30' days/weeks/etc.
+ *
+ * @param string $action Method name of the controller to call in the img src
+ * @param array $customParameters Array of name => value of parameters to set in the generated GET url
+ * @return string The generated URL
+ */
+ protected function getUrlSparkline($action, $customParameters = array())
+ {
+ $params = $this->getGraphParamsModified(
+ array('viewDataTable' => 'sparkline',
+ 'action' => $action,
+ 'module' => $this->pluginName)
+ + $customParameters
+ );
+ // convert array values to comma separated
+ foreach ($params as &$value) {
+ if (is_array($value)) {
+ $value = rawurlencode(implode(',', $value));
+ }
+ }
+ $url = Piwik_Url::getCurrentQueryStringWithParametersModified($params);
+ return $url;
+ }
+
+ /**
+ * Sets the first date available in the calendar
+ *
+ * @param Piwik_Date $minDate
+ * @param Piwik_View $view
+ * @return void
+ */
+ protected function setMinDateView(Piwik_Date $minDate, $view)
+ {
+ $view->minDateYear = $minDate->toString('Y');
+ $view->minDateMonth = $minDate->toString('m');
+ $view->minDateDay = $minDate->toString('d');
+ }
+
+ /**
+ * Sets "today" in the calendar. Today does not always mean "UTC" today, eg. for websites in UTC+12.
+ *
+ * @param Piwik_Date $maxDate
+ * @param Piwik_View $view
+ * @return void
+ */
+ protected function setMaxDateView(Piwik_Date $maxDate, $view)
+ {
+ $view->maxDateYear = $maxDate->toString('Y');
+ $view->maxDateMonth = $maxDate->toString('m');
+ $view->maxDateDay = $maxDate->toString('d');
+ }
+
+ /**
+ * Sets general variables to the view that are used by
+ * various templates and Javascript.
+ * If any error happens, displays the login screen
+ *
+ * @param Piwik_View $view
+ * @throws Exception
+ * @return void
+ */
+ protected function setGeneralVariablesView($view)
+ {
+ $view->date = $this->strDate;
+
+ try {
+ $view->idSite = $this->idSite;
+ if (empty($this->site) || empty($this->idSite)) {
+ throw new Exception("The requested website idSite is not found in the request, or is invalid.
Please check that you are logged in Piwik and have permission to access the specified website.");
- }
- $this->setPeriodVariablesView($view);
-
- $rawDate = Piwik_Common::getRequestVar('date');
- $periodStr = Piwik_Common::getRequestVar('period');
- if($periodStr != 'range')
- {
- $date = Piwik_Date::factory($this->strDate);
- $period = Piwik_Period::factory($periodStr, $date);
- }
- else
- {
- $period = new Piwik_Period_Range($periodStr, $rawDate, $this->site->getTimezone());
- }
- $view->rawDate = $rawDate;
- $view->prettyDate = self::getCalendarPrettyDate($period);
-
- $view->siteName = $this->site->getName();
- $view->siteMainUrl = $this->site->getMainUrl();
-
- $datetimeMinDate = $this->site->getCreationDate()->getDatetime();
- $minDate = Piwik_Date::factory($datetimeMinDate, $this->site->getTimezone());
- $this->setMinDateView($minDate, $view);
-
- $maxDate = Piwik_Date::factory('now', $this->site->getTimezone());
- $this->setMaxDateView($maxDate, $view);
-
- // Setting current period start & end dates, for pre-setting the calendar when "Date Range" is selected
- $dateStart = $period->getDateStart();
- if($dateStart->isEarlier($minDate)) { $dateStart = $minDate; }
- $dateEnd = $period->getDateEnd();
- if($dateEnd->isLater($maxDate)) { $dateEnd = $maxDate; }
-
- $view->startDate = $dateStart;
- $view->endDate = $dateEnd;
-
- $language = Piwik_LanguagesManager::getLanguageForSession();
- $view->language = !empty($language) ? $language : Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
-
- $view->config_action_url_category_delimiter = Piwik_Config::getInstance()->General['action_url_category_delimiter'];
-
- $this->setBasicVariablesView($view);
-
- $view->topMenu = Piwik_GetTopMenu();
- } catch(Exception $e) {
- Piwik_ExitWithMessage($e->getMessage(), '' /* $e->getTraceAsString() */ );
- }
- }
-
- /**
- * Set the minimal variables in the view object
- *
- * @param Piwik_View $view
- */
- protected function setBasicVariablesView($view)
- {
- $view->debugTrackVisitsInsidePiwikUI = Piwik_Config::getInstance()->Debug['track_visits_inside_piwik_ui'];
- $view->isSuperUser = Zend_Registry::get('access')->isSuperUser();
- $view->hasSomeAdminAccess = Piwik::isUserHasSomeAdminAccess();
- $view->isCustomLogo = Piwik_Config::getInstance()->branding['use_custom_logo'];
- $view->logoHeader = Piwik_API_API::getInstance()->getHeaderLogoUrl();
- $view->logoLarge = Piwik_API_API::getInstance()->getLogoUrl();
- $view->logoSVG = Piwik_API_API::getInstance()->getSVGLogoUrl();
- $view->hasSVGLogo = Piwik_API_API::getInstance()->hasSVGLogo();
-
- $view->enableFrames = Piwik_Config::getInstance()->General['enable_framed_pages']
- || @Piwik_Config::getInstance()->General['enable_framed_logins'];
- if(!$view->enableFrames)
- {
- $view->setXFrameOptions('sameorigin');
- }
-
- self::setHostValidationVariablesView($view);
- }
-
- /**
- * Checks if the current host is valid and sets variables on the given view, including:
- *
- * isValidHost - true if host is valid, false if otherwise
- * invalidHostMessage - message to display if host is invalid (only set if host is invalid)
- * invalidHost - the invalid hostname (only set if host is invalid)
- * mailLinkStart - the open tag of a link to email the super user of this problem (only set
- * if host is invalid)
- */
- public static function setHostValidationVariablesView( $view )
- {
- // check if host is valid
- $view->isValidHost = Piwik_Url::isValidHost();
- if (!$view->isValidHost)
- {
- // invalid host, so display warning to user
- $validHost = Piwik_Config::getInstance()->General['trusted_hosts'][0];
- $invalidHost = Piwik_Common::sanitizeInputValue($_SERVER['HTTP_HOST']);
-
- $emailSubject = rawurlencode(Piwik_Translate('CoreHome_InjectedHostEmailSubject', $invalidHost));
- $emailBody = rawurlencode(Piwik_Translate('CoreHome_InjectedHostEmailBody'));
- $superUserEmail = Piwik::getSuperUserEmail();
-
- $mailToUrl = "mailto:$superUserEmail?subject=$emailSubject&body=$emailBody";
- $mailLinkStart = "<a href=\"$mailToUrl\">";
-
- $invalidUrl = Piwik_Url::getCurrentUrlWithoutQueryString($checkIfTrusted = false);
- $validUrl = Piwik_Url::getCurrentScheme() . '://' . $validHost
- . Piwik_Url::getCurrentScriptName();
+ }
+ $this->setPeriodVariablesView($view);
+
+ $rawDate = Piwik_Common::getRequestVar('date');
+ $periodStr = Piwik_Common::getRequestVar('period');
+ if ($periodStr != 'range') {
+ $date = Piwik_Date::factory($this->strDate);
+ $period = Piwik_Period::factory($periodStr, $date);
+ } else {
+ $period = new Piwik_Period_Range($periodStr, $rawDate, $this->site->getTimezone());
+ }
+ $view->rawDate = $rawDate;
+ $view->prettyDate = self::getCalendarPrettyDate($period);
+
+ $view->siteName = $this->site->getName();
+ $view->siteMainUrl = $this->site->getMainUrl();
+
+ $datetimeMinDate = $this->site->getCreationDate()->getDatetime();
+ $minDate = Piwik_Date::factory($datetimeMinDate, $this->site->getTimezone());
+ $this->setMinDateView($minDate, $view);
+
+ $maxDate = Piwik_Date::factory('now', $this->site->getTimezone());
+ $this->setMaxDateView($maxDate, $view);
+
+ // Setting current period start & end dates, for pre-setting the calendar when "Date Range" is selected
+ $dateStart = $period->getDateStart();
+ if ($dateStart->isEarlier($minDate)) {
+ $dateStart = $minDate;
+ }
+ $dateEnd = $period->getDateEnd();
+ if ($dateEnd->isLater($maxDate)) {
+ $dateEnd = $maxDate;
+ }
+
+ $view->startDate = $dateStart;
+ $view->endDate = $dateEnd;
+
+ $language = Piwik_LanguagesManager::getLanguageForSession();
+ $view->language = !empty($language) ? $language : Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
+
+ $view->config_action_url_category_delimiter = Piwik_Config::getInstance()->General['action_url_category_delimiter'];
+
+ $this->setBasicVariablesView($view);
+
+ $view->topMenu = Piwik_GetTopMenu();
+ } catch (Exception $e) {
+ Piwik_ExitWithMessage($e->getMessage(), '' /* $e->getTraceAsString() */);
+ }
+ }
+
+ /**
+ * Set the minimal variables in the view object
+ *
+ * @param Piwik_View $view
+ */
+ protected function setBasicVariablesView($view)
+ {
+ $view->debugTrackVisitsInsidePiwikUI = Piwik_Config::getInstance()->Debug['track_visits_inside_piwik_ui'];
+ $view->isSuperUser = Zend_Registry::get('access')->isSuperUser();
+ $view->hasSomeAdminAccess = Piwik::isUserHasSomeAdminAccess();
+ $view->isCustomLogo = Piwik_Config::getInstance()->branding['use_custom_logo'];
+ $view->logoHeader = Piwik_API_API::getInstance()->getHeaderLogoUrl();
+ $view->logoLarge = Piwik_API_API::getInstance()->getLogoUrl();
+ $view->logoSVG = Piwik_API_API::getInstance()->getSVGLogoUrl();
+ $view->hasSVGLogo = Piwik_API_API::getInstance()->hasSVGLogo();
+
+ $view->enableFrames = Piwik_Config::getInstance()->General['enable_framed_pages']
+ || @Piwik_Config::getInstance()->General['enable_framed_logins'];
+ if (!$view->enableFrames) {
+ $view->setXFrameOptions('sameorigin');
+ }
+
+ self::setHostValidationVariablesView($view);
+ }
+
+ /**
+ * Checks if the current host is valid and sets variables on the given view, including:
+ *
+ * isValidHost - true if host is valid, false if otherwise
+ * invalidHostMessage - message to display if host is invalid (only set if host is invalid)
+ * invalidHost - the invalid hostname (only set if host is invalid)
+ * mailLinkStart - the open tag of a link to email the super user of this problem (only set
+ * if host is invalid)
+ */
+ public static function setHostValidationVariablesView($view)
+ {
+ // check if host is valid
+ $view->isValidHost = Piwik_Url::isValidHost();
+ if (!$view->isValidHost) {
+ // invalid host, so display warning to user
+ $validHost = Piwik_Config::getInstance()->General['trusted_hosts'][0];
+ $invalidHost = Piwik_Common::sanitizeInputValue($_SERVER['HTTP_HOST']);
+
+ $emailSubject = rawurlencode(Piwik_Translate('CoreHome_InjectedHostEmailSubject', $invalidHost));
+ $emailBody = rawurlencode(Piwik_Translate('CoreHome_InjectedHostEmailBody'));
+ $superUserEmail = Piwik::getSuperUserEmail();
+
+ $mailToUrl = "mailto:$superUserEmail?subject=$emailSubject&body=$emailBody";
+ $mailLinkStart = "<a href=\"$mailToUrl\">";
+
+ $invalidUrl = Piwik_Url::getCurrentUrlWithoutQueryString($checkIfTrusted = false);
+ $validUrl = Piwik_Url::getCurrentScheme() . '://' . $validHost
+ . Piwik_Url::getCurrentScriptName();
$invalidUrl = Piwik_Common::sanitizeInputValue($invalidUrl);
$validUrl = Piwik_Common::sanitizeInputValue($validUrl);
- $changeTrustedHostsUrl = "index.php"
- . Piwik_Url::getCurrentQueryStringWithParametersModified(array(
- 'module' => 'CoreAdminHome',
- 'action' => 'generalSettings'
- ))
- . "#trustedHostsSection";
-
- $warningStart = Piwik_Translate('CoreHome_InjectedHostWarningIntro', array(
- '<strong>'.$invalidUrl.'</strong>',
- '<strong>'.$validUrl.'</strong>'
- )) . ' <br/>';
-
- if (Piwik::isUserIsSuperUser())
- {
- $view->invalidHostMessage = $warningStart . ' '
- . Piwik_Translate('CoreHome_InjectedHostSuperUserWarning', array(
- "<a href=\"$changeTrustedHostsUrl\">",
- $invalidHost,
- '</a>',
- "<br/><a href=\"$validUrl\">",
- $validHost,
- '</a>'
- ));
- }
- else
- {
- $view->invalidHostMessage = $warningStart . ' '
- . Piwik_Translate('CoreHome_InjectedHostNonSuperUserWarning', array(
- "<br/><a href=\"$validUrl\">",
- '</a>',
- $mailLinkStart,
- '</a>'
- ));
- }
- $view->invalidHostMessageHowToFix = '<b>How do I fix this problem and how do I login again?</b><br/> The Piwik Super User can manually edit the file piwik/config/config.ini.php
- and add the following lines: <pre>[General]'."\n".'trusted_hosts[] = "'.$validHost.'"</pre><br/>After making the change, you will be able to login again.<br/><br/>
+ $changeTrustedHostsUrl = "index.php"
+ . Piwik_Url::getCurrentQueryStringWithParametersModified(array(
+ 'module' => 'CoreAdminHome',
+ 'action' => 'generalSettings'
+ ))
+ . "#trustedHostsSection";
+
+ $warningStart = Piwik_Translate('CoreHome_InjectedHostWarningIntro', array(
+ '<strong>' . $invalidUrl . '</strong>',
+ '<strong>' . $validUrl . '</strong>'
+ )) . ' <br/>';
+
+ if (Piwik::isUserIsSuperUser()) {
+ $view->invalidHostMessage = $warningStart . ' '
+ . Piwik_Translate('CoreHome_InjectedHostSuperUserWarning', array(
+ "<a href=\"$changeTrustedHostsUrl\">",
+ $invalidHost,
+ '</a>',
+ "<br/><a href=\"$validUrl\">",
+ $validHost,
+ '</a>'
+ ));
+ } else {
+ $view->invalidHostMessage = $warningStart . ' '
+ . Piwik_Translate('CoreHome_InjectedHostNonSuperUserWarning', array(
+ "<br/><a href=\"$validUrl\">",
+ '</a>',
+ $mailLinkStart,
+ '</a>'
+ ));
+ }
+ $view->invalidHostMessageHowToFix = '<b>How do I fix this problem and how do I login again?</b><br/> The Piwik Super User can manually edit the file piwik/config/config.ini.php
+ and add the following lines: <pre>[General]' . "\n" . 'trusted_hosts[] = "' . $validHost . '"</pre><br/>After making the change, you will be able to login again.<br/><br/>
You may also <i>disable this security feature (not recommended)</i>. To do so edit config/config.ini.php and add:
- <pre>[General]'."\n".'enable_trusted_host_check=0</pre>';
-
- $view->invalidHost = $invalidHost; // for UserSettings warning
- $view->invalidHostMailLinkStart = $mailLinkStart;
- }
- }
-
- /**
- * Sets general period variables (available periods, current period, period labels) used by templates
- *
- * @param Piwik_View $view
- * @throws Exception
- * @return void
- */
- public static function setPeriodVariablesView($view)
- {
- if(isset($view->period))
- {
- return;
- }
-
- $currentPeriod = Piwik_Common::getRequestVar('period');
- $view->displayUniqueVisitors = Piwik::isUniqueVisitorsEnabled($currentPeriod);
- $availablePeriods = array('day', 'week', 'month', 'year', 'range');
- if(!in_array($currentPeriod,$availablePeriods))
- {
- throw new Exception("Period must be one of: ".implode(",",$availablePeriods));
- }
- $periodNames = array(
- 'day' => array('singular' => Piwik_Translate('CoreHome_PeriodDay'), 'plural' => Piwik_Translate('CoreHome_PeriodDays')),
- 'week' => array('singular' => Piwik_Translate('CoreHome_PeriodWeek'), 'plural' => Piwik_Translate('CoreHome_PeriodWeeks')),
- 'month' => array('singular' => Piwik_Translate('CoreHome_PeriodMonth'), 'plural' => Piwik_Translate('CoreHome_PeriodMonths')),
- 'year' => array('singular' => Piwik_Translate('CoreHome_PeriodYear'), 'plural' => Piwik_Translate('CoreHome_PeriodYears')),
- // Note: plural is not used for date range
- 'range' => array('singular' => Piwik_Translate('General_DateRangeInPeriodList'), 'plural' => Piwik_Translate('General_DateRangeInPeriodList') ),
- );
-
- $found = array_search($currentPeriod,$availablePeriods);
- if($found !== false)
- {
- unset($availablePeriods[$found]);
- }
- $view->period = $currentPeriod;
- $view->otherPeriods = $availablePeriods;
- $view->periodsNames = $periodNames;
- }
-
- /**
- * Set metrics variables (displayed metrics, available metrics) used by template
- * Handles the server-side of the metrics picker
- *
- * @param Piwik_View|Piwik_ViewDataTable $view
- * @param string $defaultMetricDay name of the default metric for period=day
- * @param string $defaultMetric name of the default metric for other periods
- * @param array $metricsForDay metrics that are only available for period=day
- * @param array $metricsForAllPeriods metrics that are available for all periods
- * @param bool $labelDisplayed add 'label' to columns to display?
- * @return void
- */
- protected function setMetricsVariablesView(Piwik_ViewDataTable $view, $defaultMetricDay='nb_uniq_visitors',
- $defaultMetric='nb_visits', $metricsForDay=array('nb_uniq_visitors'),
- $metricsForAllPeriods=array('nb_visits', 'nb_actions'), $labelDisplayed=true)
- {
- // columns is set in the request if metrics picker has been used
- $columns = Piwik_Common::getRequestVar('columns', false);
- if ($columns !== false)
- {
- $columns = Piwik::getArrayFromApiParameter($columns);
- $firstColumn = $columns[0];
- }
- else
- {
- // default columns
- $firstColumn = isset($view->period) && $view->period == 'day' ? $defaultMetricDay : $defaultMetric;
- $columns = array($firstColumn);
- }
- // displayed columns
- if ($labelDisplayed
- && !($view instanceof Piwik_ViewDataTable_GenerateGraphData))
- {
- array_unshift($columns, 'label');
- }
- $view->setColumnsToDisplay($columns);
-
-
- // Continue only for graphs
- if(!($view instanceof Piwik_ViewDataTable_GenerateGraphData))
- {
- return;
- }
- // do not sort if sorted column was initially "label" or eg. it would make "Visits by Server time" not pretty
- if($view->getSortedColumn() != 'label')
- {
- $view->setSortedColumn($firstColumn);
- }
- // selectable columns
- if (isset($view->period) && $view->period == 'day')
- {
- $selectableColumns = array_merge($metricsForDay, $metricsForAllPeriods);
- }
- else
- {
- $selectableColumns = $metricsForAllPeriods;
- }
- $view->setSelectableColumns($selectableColumns);
- }
-
- /**
- * Helper method used to redirect the current http request to another module/action
- * If specified, will also redirect to a given website, period and /or date
- *
- * @param string $moduleToRedirect Module, eg. "MultiSites"
- * @param string $actionToRedirect Action, eg. "index"
- * @param string $websiteId Website ID, eg. 1
- * @param string $defaultPeriod Default period, eg. "day"
- * @param string $defaultDate Default date, eg. "today"
- * @param array $parameters Parameters to append to url
- */
- function redirectToIndex($moduleToRedirect, $actionToRedirect, $websiteId = null, $defaultPeriod = null, $defaultDate = null, $parameters = array())
- {
- if(is_null($websiteId))
- {
- $websiteId = $this->getDefaultWebsiteId();
- }
- if(is_null($defaultDate))
- {
- $defaultDate = $this->getDefaultDate();
- }
- if(is_null($defaultPeriod))
- {
- $defaultPeriod = $this->getDefaultPeriod();
- }
- $parametersString = '';
- if(!empty($parameters))
- {
- $parametersString = '&' . Piwik_Url::getQueryStringFromParameters($parameters);
- }
-
- if($websiteId) {
- $url = "Location: index.php?module=".$moduleToRedirect
- ."&action=".$actionToRedirect
- ."&idSite=".$websiteId
- ."&period=".$defaultPeriod
- ."&date=".$defaultDate
- .$parametersString;
- header($url);
- exit;
- }
-
- if(Piwik::isUserIsSuperUser())
- {
- Piwik_ExitWithMessage("Error: no website was found in this Piwik installation.
- <br />Check the table '". Piwik_Common::prefixTable('site') ."' in your database, it should contain your Piwik websites.", false, true);
- }
-
- $currentLogin = Piwik::getCurrentUserLogin();
- if(!empty($currentLogin)
- && $currentLogin != 'anonymous')
- {
- $errorMessage = sprintf(Piwik_Translate('CoreHome_NoPrivilegesAskPiwikAdmin'), $currentLogin, "<br/><a href='mailto:".Piwik::getSuperUserEmail()."?subject=Access to Piwik for user $currentLogin'>", "</a>");
- $errorMessage .= "<br /><br />&nbsp;&nbsp;&nbsp;<b><a href='index.php?module=". Zend_Registry::get('auth')->getName() ."&amp;action=logout'>&rsaquo; ". Piwik_Translate('General_Logout'). "</a></b><br />";
- Piwik_ExitWithMessage($errorMessage, false, true);
- }
-
- Piwik_FrontController::getInstance()->dispatch(Piwik::getLoginPluginName(), false);
- exit;
- }
-
-
- /**
- * Returns default website that Piwik should load
- *
- * @return Piwik_Site
- */
- protected function getDefaultWebsiteId()
- {
- $defaultWebsiteId = false;
-
- // User preference: default website ID to load
- $defaultReport = Piwik_UsersManager_API::getInstance()->getUserPreference(Piwik::getCurrentUserLogin(), Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT);
- if(is_numeric($defaultReport))
- {
- $defaultWebsiteId = $defaultReport;
- }
-
- Piwik_PostEvent( 'Controller.getDefaultWebsiteId', $defaultWebsiteId );
-
- if($defaultWebsiteId)
- {
- return $defaultWebsiteId;
- }
-
- $sitesId = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess();
- if(!empty($sitesId))
- {
- return $sitesId[0];
- }
- return false;
- }
-
- /**
- * Returns default date for Piwik reports
- *
- * @return string today, 2010-01-01, etc.
- */
- protected function getDefaultDate()
- {
- // NOTE: a change in this function might mean a change in plugins/UsersManager/templates/userSettings.js as well
- $userSettingsDate = Piwik_UsersManager_API::getInstance()->getUserPreference(Piwik::getCurrentUserLogin(), Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT_DATE);
- if($userSettingsDate === false)
- {
- return Piwik_Config::getInstance()->General['default_day'];
- }
- if($userSettingsDate == 'yesterday')
- {
- return $userSettingsDate;
- }
- // if last7, last30, etc.
- if(strpos($userSettingsDate, 'last') === 0
- || strpos($userSettingsDate, 'previous') === 0)
- {
- return $userSettingsDate;
- }
- return 'today';
- }
-
- /**
- * Returns default date for Piwik reports
- *
- * @return string today, 2010-01-01, etc.
- */
- protected function getDefaultPeriod()
- {
- $userSettingsDate = Piwik_UsersManager_API::getInstance()->getUserPreference(Piwik::getCurrentUserLogin(), Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT_DATE);
- if($userSettingsDate === false)
- {
- return Piwik_Config::getInstance()->General['default_period'];
- }
- if(in_array($userSettingsDate, array('today','yesterday')))
- {
- return 'day';
- }
- if(strpos($userSettingsDate, 'last') === 0
- || strpos($userSettingsDate, 'previous') === 0)
- {
- return 'range';
- }
- return $userSettingsDate;
- }
-
- /**
- * Checks that the specified token matches the current logged in user token.
- * Note: this protection against CSRF should be limited to controller
- * actions that are either invoked via AJAX or redirect to a page
- * within the site. The token should never appear in the browser's
- * address bar.
- *
- * @throws Piwik_Access_NoAccessException if token doesn't match
- * @return void
- */
- protected function checkTokenInUrl()
- {
- if(Piwik_Common::getRequestVar('token_auth', false) != Piwik::getCurrentUserTokenAuth()) {
- throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionInvalidToken'));
- }
- }
-
- /**
- * Returns pretty date for use in period selector widget.
- *
- * @param Piwik_Period $period
- * @return string
- */
- public static function getCalendarPrettyDate($period)
- {
- if ($period instanceof Piwik_Period_Month) // show month name when period is for a month
- {
- return $period->getLocalizedLongString();
- }
- else
- {
- return $period->getPrettyString();
- }
- }
-
-
-
- /**
- * Returns the pretty date representation
- *
- * @param $date string
- * @param $period string
- * @return string Pretty date
- */
- public static function getPrettyDate($date, $period)
- {
- return self::getCalendarPrettyDate( Piwik_Period::factory($period, Piwik_Date::factory($date)) );
- }
-
-
- /**
- * Calculates the evolution from one value to another and returns HTML displaying
- * the evolution percent. The HTML includes an up/down arrow and is colored red, black or
- * green depending on whether the evolution is negative, 0 or positive.
- *
- * No HTML is returned if the current value and evolution percent are both 0.
- *
- * @param string $date The date of the current value.
- * @param int $currentValue The value to calculate evolution to.
- * @param string $pastDate The date of past value.
- * @param int $pastValue The value in the past to calculate evolution from.
- * @return string|false The HTML or false if the evolution is 0 and the current value is 0.
- */
- protected function getEvolutionHtml( $date, $currentValue, $pastDate, $pastValue)
- {
- $evolutionPercent = Piwik_DataTable_Filter_CalculateEvolutionFilter::calculate(
- $currentValue, $pastValue, $precision = 1);
-
- // do not display evolution if evolution percent is 0 and current value is 0
- if ($evolutionPercent == 0
- && $currentValue == 0)
- {
- return false;
- }
-
- $titleEvolutionPercent = $evolutionPercent;
- if ($evolutionPercent < 0)
- {
- $color = "#e02a3b"; //red
- $img = "arrow_down.png";
- }
- else if ($evolutionPercent == 0)
- {
- $img = "stop.png";
- }
- else
- {
- $color = "green";
- $img = "arrow_up.png";
- $titleEvolutionPercent = '+'.$titleEvolutionPercent;
- }
-
- $title = Piwik_Translate('General_EvolutionSummaryGeneric', array(
- Piwik_Translate('General_NVisits', $currentValue),
- $date,
- Piwik_Translate('General_NVisits', $pastValue),
- $pastDate,
- $titleEvolutionPercent
- ));
-
- $result = '<span class="metricEvolution" title="'.$title
- . '"><img style="padding-right:4px" src="plugins/MultiSites/images/'.$img.'"/><strong';
-
- if (isset($color))
- {
- $result .= ' style="color:'.$color.'"';
- }
- $result .= '>'.$evolutionPercent.'</strong></span>';
-
- return $result;
- }
+ <pre>[General]' . "\n" . 'enable_trusted_host_check=0</pre>';
+
+ $view->invalidHost = $invalidHost; // for UserSettings warning
+ $view->invalidHostMailLinkStart = $mailLinkStart;
+ }
+ }
+
+ /**
+ * Sets general period variables (available periods, current period, period labels) used by templates
+ *
+ * @param Piwik_View $view
+ * @throws Exception
+ * @return void
+ */
+ public static function setPeriodVariablesView($view)
+ {
+ if (isset($view->period)) {
+ return;
+ }
+
+ $currentPeriod = Piwik_Common::getRequestVar('period');
+ $view->displayUniqueVisitors = Piwik::isUniqueVisitorsEnabled($currentPeriod);
+ $availablePeriods = array('day', 'week', 'month', 'year', 'range');
+ if (!in_array($currentPeriod, $availablePeriods)) {
+ throw new Exception("Period must be one of: " . implode(",", $availablePeriods));
+ }
+ $periodNames = array(
+ 'day' => array('singular' => Piwik_Translate('CoreHome_PeriodDay'), 'plural' => Piwik_Translate('CoreHome_PeriodDays')),
+ 'week' => array('singular' => Piwik_Translate('CoreHome_PeriodWeek'), 'plural' => Piwik_Translate('CoreHome_PeriodWeeks')),
+ 'month' => array('singular' => Piwik_Translate('CoreHome_PeriodMonth'), 'plural' => Piwik_Translate('CoreHome_PeriodMonths')),
+ 'year' => array('singular' => Piwik_Translate('CoreHome_PeriodYear'), 'plural' => Piwik_Translate('CoreHome_PeriodYears')),
+ // Note: plural is not used for date range
+ 'range' => array('singular' => Piwik_Translate('General_DateRangeInPeriodList'), 'plural' => Piwik_Translate('General_DateRangeInPeriodList')),
+ );
+
+ $found = array_search($currentPeriod, $availablePeriods);
+ if ($found !== false) {
+ unset($availablePeriods[$found]);
+ }
+ $view->period = $currentPeriod;
+ $view->otherPeriods = $availablePeriods;
+ $view->periodsNames = $periodNames;
+ }
+
+ /**
+ * Set metrics variables (displayed metrics, available metrics) used by template
+ * Handles the server-side of the metrics picker
+ *
+ * @param Piwik_View|Piwik_ViewDataTable $view
+ * @param string $defaultMetricDay name of the default metric for period=day
+ * @param string $defaultMetric name of the default metric for other periods
+ * @param array $metricsForDay metrics that are only available for period=day
+ * @param array $metricsForAllPeriods metrics that are available for all periods
+ * @param bool $labelDisplayed add 'label' to columns to display?
+ * @return void
+ */
+ protected function setMetricsVariablesView(Piwik_ViewDataTable $view, $defaultMetricDay = 'nb_uniq_visitors',
+ $defaultMetric = 'nb_visits', $metricsForDay = array('nb_uniq_visitors'),
+ $metricsForAllPeriods = array('nb_visits', 'nb_actions'), $labelDisplayed = true)
+ {
+ // columns is set in the request if metrics picker has been used
+ $columns = Piwik_Common::getRequestVar('columns', false);
+ if ($columns !== false) {
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ $firstColumn = $columns[0];
+ } else {
+ // default columns
+ $firstColumn = isset($view->period) && $view->period == 'day' ? $defaultMetricDay : $defaultMetric;
+ $columns = array($firstColumn);
+ }
+ // displayed columns
+ if ($labelDisplayed
+ && !($view instanceof Piwik_ViewDataTable_GenerateGraphData)
+ ) {
+ array_unshift($columns, 'label');
+ }
+ $view->setColumnsToDisplay($columns);
+
+
+ // Continue only for graphs
+ if (!($view instanceof Piwik_ViewDataTable_GenerateGraphData)) {
+ return;
+ }
+ // do not sort if sorted column was initially "label" or eg. it would make "Visits by Server time" not pretty
+ if ($view->getSortedColumn() != 'label') {
+ $view->setSortedColumn($firstColumn);
+ }
+ // selectable columns
+ if (isset($view->period) && $view->period == 'day') {
+ $selectableColumns = array_merge($metricsForDay, $metricsForAllPeriods);
+ } else {
+ $selectableColumns = $metricsForAllPeriods;
+ }
+ $view->setSelectableColumns($selectableColumns);
+ }
+
+ /**
+ * Helper method used to redirect the current http request to another module/action
+ * If specified, will also redirect to a given website, period and /or date
+ *
+ * @param string $moduleToRedirect Module, eg. "MultiSites"
+ * @param string $actionToRedirect Action, eg. "index"
+ * @param string $websiteId Website ID, eg. 1
+ * @param string $defaultPeriod Default period, eg. "day"
+ * @param string $defaultDate Default date, eg. "today"
+ * @param array $parameters Parameters to append to url
+ */
+ function redirectToIndex($moduleToRedirect, $actionToRedirect, $websiteId = null, $defaultPeriod = null, $defaultDate = null, $parameters = array())
+ {
+ if (is_null($websiteId)) {
+ $websiteId = $this->getDefaultWebsiteId();
+ }
+ if (is_null($defaultDate)) {
+ $defaultDate = $this->getDefaultDate();
+ }
+ if (is_null($defaultPeriod)) {
+ $defaultPeriod = $this->getDefaultPeriod();
+ }
+ $parametersString = '';
+ if (!empty($parameters)) {
+ $parametersString = '&' . Piwik_Url::getQueryStringFromParameters($parameters);
+ }
+
+ if ($websiteId) {
+ $url = "Location: index.php?module=" . $moduleToRedirect
+ . "&action=" . $actionToRedirect
+ . "&idSite=" . $websiteId
+ . "&period=" . $defaultPeriod
+ . "&date=" . $defaultDate
+ . $parametersString;
+ header($url);
+ exit;
+ }
+
+ if (Piwik::isUserIsSuperUser()) {
+ Piwik_ExitWithMessage("Error: no website was found in this Piwik installation.
+ <br />Check the table '" . Piwik_Common::prefixTable('site') . "' in your database, it should contain your Piwik websites.", false, true);
+ }
+
+ $currentLogin = Piwik::getCurrentUserLogin();
+ if (!empty($currentLogin)
+ && $currentLogin != 'anonymous'
+ ) {
+ $errorMessage = sprintf(Piwik_Translate('CoreHome_NoPrivilegesAskPiwikAdmin'), $currentLogin, "<br/><a href='mailto:" . Piwik::getSuperUserEmail() . "?subject=Access to Piwik for user $currentLogin'>", "</a>");
+ $errorMessage .= "<br /><br />&nbsp;&nbsp;&nbsp;<b><a href='index.php?module=" . Zend_Registry::get('auth')->getName() . "&amp;action=logout'>&rsaquo; " . Piwik_Translate('General_Logout') . "</a></b><br />";
+ Piwik_ExitWithMessage($errorMessage, false, true);
+ }
+
+ Piwik_FrontController::getInstance()->dispatch(Piwik::getLoginPluginName(), false);
+ exit;
+ }
+
+
+ /**
+ * Returns default website that Piwik should load
+ *
+ * @return Piwik_Site
+ */
+ protected function getDefaultWebsiteId()
+ {
+ $defaultWebsiteId = false;
+
+ // User preference: default website ID to load
+ $defaultReport = Piwik_UsersManager_API::getInstance()->getUserPreference(Piwik::getCurrentUserLogin(), Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT);
+ if (is_numeric($defaultReport)) {
+ $defaultWebsiteId = $defaultReport;
+ }
+
+ Piwik_PostEvent('Controller.getDefaultWebsiteId', $defaultWebsiteId);
+
+ if ($defaultWebsiteId) {
+ return $defaultWebsiteId;
+ }
+
+ $sitesId = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess();
+ if (!empty($sitesId)) {
+ return $sitesId[0];
+ }
+ return false;
+ }
+
+ /**
+ * Returns default date for Piwik reports
+ *
+ * @return string today, 2010-01-01, etc.
+ */
+ protected function getDefaultDate()
+ {
+ // NOTE: a change in this function might mean a change in plugins/UsersManager/templates/userSettings.js as well
+ $userSettingsDate = Piwik_UsersManager_API::getInstance()->getUserPreference(Piwik::getCurrentUserLogin(), Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT_DATE);
+ if ($userSettingsDate === false) {
+ return Piwik_Config::getInstance()->General['default_day'];
+ }
+ if ($userSettingsDate == 'yesterday') {
+ return $userSettingsDate;
+ }
+ // if last7, last30, etc.
+ if (strpos($userSettingsDate, 'last') === 0
+ || strpos($userSettingsDate, 'previous') === 0
+ ) {
+ return $userSettingsDate;
+ }
+ return 'today';
+ }
+
+ /**
+ * Returns default date for Piwik reports
+ *
+ * @return string today, 2010-01-01, etc.
+ */
+ protected function getDefaultPeriod()
+ {
+ $userSettingsDate = Piwik_UsersManager_API::getInstance()->getUserPreference(Piwik::getCurrentUserLogin(), Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT_DATE);
+ if ($userSettingsDate === false) {
+ return Piwik_Config::getInstance()->General['default_period'];
+ }
+ if (in_array($userSettingsDate, array('today', 'yesterday'))) {
+ return 'day';
+ }
+ if (strpos($userSettingsDate, 'last') === 0
+ || strpos($userSettingsDate, 'previous') === 0
+ ) {
+ return 'range';
+ }
+ return $userSettingsDate;
+ }
+
+ /**
+ * Checks that the specified token matches the current logged in user token.
+ * Note: this protection against CSRF should be limited to controller
+ * actions that are either invoked via AJAX or redirect to a page
+ * within the site. The token should never appear in the browser's
+ * address bar.
+ *
+ * @throws Piwik_Access_NoAccessException if token doesn't match
+ * @return void
+ */
+ protected function checkTokenInUrl()
+ {
+ if (Piwik_Common::getRequestVar('token_auth', false) != Piwik::getCurrentUserTokenAuth()) {
+ throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionInvalidToken'));
+ }
+ }
+
+ /**
+ * Returns pretty date for use in period selector widget.
+ *
+ * @param Piwik_Period $period
+ * @return string
+ */
+ public static function getCalendarPrettyDate($period)
+ {
+ if ($period instanceof Piwik_Period_Month) // show month name when period is for a month
+ {
+ return $period->getLocalizedLongString();
+ } else {
+ return $period->getPrettyString();
+ }
+ }
+
+
+ /**
+ * Returns the pretty date representation
+ *
+ * @param $date string
+ * @param $period string
+ * @return string Pretty date
+ */
+ public static function getPrettyDate($date, $period)
+ {
+ return self::getCalendarPrettyDate(Piwik_Period::factory($period, Piwik_Date::factory($date)));
+ }
+
+
+ /**
+ * Calculates the evolution from one value to another and returns HTML displaying
+ * the evolution percent. The HTML includes an up/down arrow and is colored red, black or
+ * green depending on whether the evolution is negative, 0 or positive.
+ *
+ * No HTML is returned if the current value and evolution percent are both 0.
+ *
+ * @param string $date The date of the current value.
+ * @param int $currentValue The value to calculate evolution to.
+ * @param string $pastDate The date of past value.
+ * @param int $pastValue The value in the past to calculate evolution from.
+ * @return string|false The HTML or false if the evolution is 0 and the current value is 0.
+ */
+ protected function getEvolutionHtml($date, $currentValue, $pastDate, $pastValue)
+ {
+ $evolutionPercent = Piwik_DataTable_Filter_CalculateEvolutionFilter::calculate(
+ $currentValue, $pastValue, $precision = 1);
+
+ // do not display evolution if evolution percent is 0 and current value is 0
+ if ($evolutionPercent == 0
+ && $currentValue == 0
+ ) {
+ return false;
+ }
+
+ $titleEvolutionPercent = $evolutionPercent;
+ if ($evolutionPercent < 0) {
+ $color = "#e02a3b"; //red
+ $img = "arrow_down.png";
+ } else if ($evolutionPercent == 0) {
+ $img = "stop.png";
+ } else {
+ $color = "green";
+ $img = "arrow_up.png";
+ $titleEvolutionPercent = '+' . $titleEvolutionPercent;
+ }
+
+ $title = Piwik_Translate('General_EvolutionSummaryGeneric', array(
+ Piwik_Translate('General_NVisits', $currentValue),
+ $date,
+ Piwik_Translate('General_NVisits', $pastValue),
+ $pastDate,
+ $titleEvolutionPercent
+ ));
+
+ $result = '<span class="metricEvolution" title="' . $title
+ . '"><img style="padding-right:4px" src="plugins/MultiSites/images/' . $img . '"/><strong';
+
+ if (isset($color)) {
+ $result .= ' style="color:' . $color . '"';
+ }
+ $result .= '>' . $evolutionPercent . '</strong></span>';
+
+ return $result;
+ }
}
diff --git a/core/Controller/Admin.php b/core/Controller/Admin.php
index 1278b94d89..2378d005d0 100644
--- a/core/Controller/Admin.php
+++ b/core/Controller/Admin.php
@@ -1,66 +1,65 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Parent class of all plugins Controllers with admin functions
- *
+ *
* @package Piwik
*/
abstract class Piwik_Controller_Admin extends Piwik_Controller
{
- /**
- * Set the minimal variables in the view object
- * Extended by some admin view specific variables
- *
- * @param Piwik_View $view
- */
- protected function setBasicVariablesView($view)
- {
- parent::setBasicVariablesView($view);
+ /**
+ * Set the minimal variables in the view object
+ * Extended by some admin view specific variables
+ *
+ * @param Piwik_View $view
+ */
+ protected function setBasicVariablesView($view)
+ {
+ parent::setBasicVariablesView($view);
- self::setBasicVariablesAdminView($view);
- }
+ self::setBasicVariablesAdminView($view);
+ }
- static public function setBasicVariablesAdminView($view)
- {
- $statsEnabled = Piwik_Config::getInstance()->Tracker['record_statistics'];
- if($statsEnabled == "0"){
- $view->statisticsNotRecorded = true;
- }
+ static public function setBasicVariablesAdminView($view)
+ {
+ $statsEnabled = Piwik_Config::getInstance()->Tracker['record_statistics'];
+ if ($statsEnabled == "0") {
+ $view->statisticsNotRecorded = true;
+ }
- $view->topMenu = Piwik_GetTopMenu();
- $view->currentAdminMenuName = Piwik_GetCurrentAdminMenuName();
+ $view->topMenu = Piwik_GetTopMenu();
+ $view->currentAdminMenuName = Piwik_GetCurrentAdminMenuName();
- $view->enableFrames = Piwik_Config::getInstance()->General['enable_framed_settings'];
- if (!$view->enableFrames) {
- $view->setXFrameOptions('sameorigin');
- }
-
- $view->isSuperUser = Piwik::isUserIsSuperUser();
-
- // for old geoip plugin warning
- $view->usingOldGeoIPPlugin = Piwik_PluginsManager::getInstance()->isPluginActivated('GeoIP');
-
- // for cannot find installed plugin warning
- $missingPlugins = Piwik_PluginsManager::getInstance()->getMissingPlugins();
- if (!empty($missingPlugins))
- {
- $pluginsLink = Piwik_Url::getCurrentQueryStringWithParametersModified(array(
- 'module' => 'CorePluginsAdmin', 'action' => 'index'
- ));
- $view->missingPluginsWarning = Piwik_Translate('CoreAdminHome_MissingPluginsWarning', array(
- '<strong>'.implode('</strong>,&nbsp;<strong>', $missingPlugins).'</strong>',
- '<a href="'.$pluginsLink.'"/>',
- '</a>'
- ));
- }
- }
+ $view->enableFrames = Piwik_Config::getInstance()->General['enable_framed_settings'];
+ if (!$view->enableFrames) {
+ $view->setXFrameOptions('sameorigin');
+ }
+
+ $view->isSuperUser = Piwik::isUserIsSuperUser();
+
+ // for old geoip plugin warning
+ $view->usingOldGeoIPPlugin = Piwik_PluginsManager::getInstance()->isPluginActivated('GeoIP');
+
+ // for cannot find installed plugin warning
+ $missingPlugins = Piwik_PluginsManager::getInstance()->getMissingPlugins();
+ if (!empty($missingPlugins)) {
+ $pluginsLink = Piwik_Url::getCurrentQueryStringWithParametersModified(array(
+ 'module' => 'CorePluginsAdmin', 'action' => 'index'
+ ));
+ $view->missingPluginsWarning = Piwik_Translate('CoreAdminHome_MissingPluginsWarning', array(
+ '<strong>' . implode('</strong>,&nbsp;<strong>', $missingPlugins) . '</strong>',
+ '<a href="' . $pluginsLink . '"/>',
+ '</a>'
+ ));
+ }
+ }
}
diff --git a/core/Cookie.php b/core/Cookie.php
index 2651cf390e..46f9dc9e66 100644
--- a/core/Cookie.php
+++ b/core/Cookie.php
@@ -19,382 +19,367 @@
*/
class Piwik_Cookie
{
- /**
- * Don't create a cookie bigger than 1k
- */
- const MAX_COOKIE_SIZE = 1024;
-
- /**
- * The name of the cookie
- * @var string
- */
- protected $name = null;
-
- /**
- * The expire time for the cookie (expressed in UNIX Timestamp)
- * @var int
- */
- protected $expire = null;
-
- /**
- * Restrict cookie path
- * @var string
- */
- protected $path = '';
-
- /**
- * Restrict cookie to a domain (or subdomains)
- * @var string
- */
- protected $domain = '';
-
- /**
- * If true, cookie should only be transmitted over secure HTTPS
- * @var bool
- */
- protected $secure = false;
-
- /**
- * If true, cookie will only be made available via the HTTP protocol.
- * Note: not well supported by browsers.
- * @var bool
- */
- protected $httponly = false;
-
- /**
- * The content of the cookie
- * @var array
- */
- protected $value = array();
-
- /**
- * The character used to separate the tuple name=value in the cookie
- */
- const VALUE_SEPARATOR = ':';
-
- /**
- * Instantiate a new Cookie object and tries to load the cookie content if the cookie
- * exists already.
- *
- * @param string $cookieName cookie Name
- * @param int $expire The timestamp after which the cookie will expire, eg time() + 86400;
- * use 0 (int zero) to expire cookie at end of browser session
- * @param string $path The path on the server in which the cookie will be available on.
- * @param bool|string $keyStore Will be used to store several bits of data (eg. one array per website)
- */
- public function __construct( $cookieName, $expire = null, $path = null, $keyStore = false)
- {
- $this->name = $cookieName;
- $this->path = $path;
- $this->expire = $expire;
- if(is_null($expire)
- || !is_numeric($expire)
- || $expire < 0)
- {
- $this->expire = $this->getDefaultExpire();
- }
-
- $this->keyStore = $keyStore;
- if($this->isCookieFound())
- {
- $this->loadContentFromCookie();
- }
- }
-
- /**
- * Returns true if the visitor already has the cookie.
- *
- * @return bool
- */
- public function isCookieFound()
- {
- return isset($_COOKIE[$this->name]);
- }
-
- /**
- * Returns the default expiry time, 2 years
- *
- * @return int Timestamp in 2 years
- */
- protected function getDefaultExpire()
- {
- return time() + 86400*365*2;
- }
-
- /**
- * setcookie() replacement -- we don't use the built-in function because
- * it is buggy for some PHP versions.
- *
- * @link http://php.net/setcookie
- *
- * @param string $Name Name of cookie
- * @param string $Value Value of cookie
- * @param int $Expires Time the cookie expires
- * @param string $Path
- * @param string $Domain
- * @param bool $Secure
- * @param bool $HTTPOnly
- */
- protected function setCookie($Name, $Value, $Expires, $Path = '', $Domain = '', $Secure = false, $HTTPOnly = false)
- {
- if (!empty($Domain))
- {
- // Fix the domain to accept domains with and without 'www.'.
- if (!strncasecmp($Domain, 'www.', 4))
- {
- $Domain = substr($Domain, 4);
- }
- $Domain = '.' . $Domain;
-
- // Remove port information.
- $Port = strpos($Domain, ':');
- if ($Port !== false) $Domain = substr($Domain, 0, $Port);
- }
-
- $header = 'Set-Cookie: ' . rawurlencode($Name) . '=' . rawurlencode($Value)
- . (empty($Expires) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s', $Expires) . ' GMT')
- . (empty($Path) ? '' : '; path=' . $Path)
- . (empty($Domain) ? '' : '; domain=' . $Domain)
- . (!$Secure ? '' : '; secure')
- . (!$HTTPOnly ? '' : '; HttpOnly');
-
- Piwik_Common::sendHeader($header, false);
- }
-
- /**
- * We set the privacy policy header
- */
- protected function setP3PHeader()
- {
- Piwik_Common::sendHeader("P3P: CP='OTI DSP COR NID STP UNI OTPa OUR'");
- }
-
- /**
- * Delete the cookie
- */
- public function delete()
- {
- $this->setP3PHeader();
- $this->setCookie($this->name, 'deleted', time() - 31536001, $this->path, $this->domain);
- }
-
- /**
- * Saves the cookie (set the Cookie header).
- * You have to call this method before sending any text to the browser or you would get the
- * "Header already sent" error.
- */
- public function save()
- {
- $cookieString = $this->generateContentString();
- if(strlen($cookieString) > self::MAX_COOKIE_SIZE)
- {
- // If the cookie was going to be too large, instead, delete existing cookie and start afresh
- $this->delete();
- return;
- }
-
- $this->setP3PHeader();
- $this->setCookie($this->name, $cookieString, $this->expire, $this->path, $this->domain, $this->secure, $this->httponly);
- }
-
- /**
- * Extract signed content from string: content VALUE_SEPARATOR '_=' signature
- *
- * @param string $content
- * @return string|bool Content or false if unsigned
- */
- private function extractSignedContent($content)
- {
- $signature = substr($content, -40);
- if(substr($content, -43, 3) == self::VALUE_SEPARATOR . '_=' &&
- $signature == sha1(substr($content, 0, -40) . Piwik_Common::getSalt()))
- {
- // strip trailing: VALUE_SEPARATOR '_=' signature"
- return substr($content, 0, -43);
- }
- return false;
- }
-
- /**
- * Load the cookie content into a php array.
- * Parses the cookie string to extract the different variables.
- * Unserialize the array when necessary.
- * Decode the non numeric values that were base64 encoded.
- */
- protected function loadContentFromCookie()
- {
- $cookieStr = $this->extractSignedContent($_COOKIE[$this->name]);
- if($cookieStr === false)
- {
- return;
- }
-
- $values = explode( self::VALUE_SEPARATOR, $cookieStr);
- foreach($values as $nameValue)
- {
- $equalPos = strpos($nameValue, '=');
- $varName = substr($nameValue,0,$equalPos);
- $varValue = substr($nameValue,$equalPos+1);
-
- // no numeric value are base64 encoded so we need to decode them
- if(!is_numeric($varValue))
- {
- $tmpValue = base64_decode($varValue);
- $varValue = safe_unserialize($tmpValue);
-
- // discard entire cookie
- // note: this assumes we never serialize a boolean
- if($varValue === false && $tmpValue !== 'b:0;')
- {
- $this->value = array();
- unset($_COOKIE[$this->name]);
- break;
- }
- }
-
- $this->value[$varName] = $varValue;
- }
- }
-
- /**
- * Returns the string to save in the cookie from the $this->value array of values.
- * It goes through the array and generates the cookie content string.
- *
- * @return string Cookie content
- */
- protected function generateContentString()
- {
- $cookieStr = '';
- foreach($this->value as $name=>$value)
- {
- if(!is_numeric($value))
- {
- $value = base64_encode(safe_serialize($value));
- }
-
- $cookieStr .= "$name=$value" . self::VALUE_SEPARATOR;
- }
-
- if(!empty($cookieStr))
- {
- $cookieStr .= '_=';
-
- // sign cookie
- $signature = sha1($cookieStr . Piwik_Common::getSalt());
- return $cookieStr . $signature;
- }
-
- return '';
- }
-
- /**
- * Set cookie domain
- *
- * @param string $domain
- */
- public function setDomain($domain)
- {
- $this->domain = $domain;
- }
-
- /**
- * Set secure flag
- *
- * @param bool $secure
- */
- public function setSecure($secure)
- {
- $this->secure = $secure;
- }
-
- /**
- * Set HTTP only
- *
- * @param bool $httponly
- */
- public function setHttpOnly($httponly)
- {
- $this->httponly = $httponly;
- }
-
- /**
- * Registers a new name => value association in the cookie.
- *
- * Registering new values is optimal if the value is a numeric value.
- * If the value is a string, it will be saved as a base64 encoded string.
- * If the value is an array, it will be saved as a serialized and base64 encoded
- * string which is not very good in terms of bytes usage.
- * You should save arrays only when you are sure about their maximum data size.
- * A cookie has to stay small and its size shouldn't increase over time!
- *
- * @param string $name Name of the value to save; the name will be used to retrieve this value
- * @param string|array|number $value Value to save. If null, entry will be deleted from cookie.
- */
- public function set( $name, $value )
- {
- $name = self::escapeValue($name);
-
- // Delete value if $value === null
- if(is_null($value))
- {
- if($this->keyStore === false)
- {
- unset($this->value[$name]);
- return;
- }
- unset($this->value[$this->keyStore][$name]);
- return;
- }
-
- if($this->keyStore === false)
- {
- $this->value[$name] = $value;
- return;
- }
- $this->value[$this->keyStore][$name] = $value;
- }
-
- /**
- * Returns the value defined by $name from the cookie.
- *
- * @param string|integer Index name of the value to return
- * @return mixed The value if found, false if the value is not found
- */
- public function get( $name )
- {
- $name = self::escapeValue($name);
- if($this->keyStore === false)
- {
- return isset($this->value[$name])
- ? self::escapeValue($this->value[$name])
- : false;
- }
- return isset($this->value[$this->keyStore][$name])
- ? self::escapeValue($this->value[$this->keyStore][$name])
- : false;
- }
-
- /**
- * Returns an easy to read cookie dump
- *
- * @return string The cookie dump
- */
- public function __toString()
- {
- $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;
- }
-
- /**
- * Escape values from the cookie before sending them back to the client
- * (when using the get() method).
- *
- * @param string $value Value to be escaped
- * @return mixed The value once cleaned.
- */
- static protected function escapeValue( $value )
- {
- return Piwik_Common::sanitizeInputValues($value);
- }
+ /**
+ * Don't create a cookie bigger than 1k
+ */
+ const MAX_COOKIE_SIZE = 1024;
+
+ /**
+ * The name of the cookie
+ * @var string
+ */
+ protected $name = null;
+
+ /**
+ * The expire time for the cookie (expressed in UNIX Timestamp)
+ * @var int
+ */
+ protected $expire = null;
+
+ /**
+ * Restrict cookie path
+ * @var string
+ */
+ protected $path = '';
+
+ /**
+ * Restrict cookie to a domain (or subdomains)
+ * @var string
+ */
+ protected $domain = '';
+
+ /**
+ * If true, cookie should only be transmitted over secure HTTPS
+ * @var bool
+ */
+ protected $secure = false;
+
+ /**
+ * If true, cookie will only be made available via the HTTP protocol.
+ * Note: not well supported by browsers.
+ * @var bool
+ */
+ protected $httponly = false;
+
+ /**
+ * The content of the cookie
+ * @var array
+ */
+ protected $value = array();
+
+ /**
+ * The character used to separate the tuple name=value in the cookie
+ */
+ const VALUE_SEPARATOR = ':';
+
+ /**
+ * Instantiate a new Cookie object and tries to load the cookie content if the cookie
+ * exists already.
+ *
+ * @param string $cookieName cookie Name
+ * @param int $expire The timestamp after which the cookie will expire, eg time() + 86400;
+ * use 0 (int zero) to expire cookie at end of browser session
+ * @param string $path The path on the server in which the cookie will be available on.
+ * @param bool|string $keyStore Will be used to store several bits of data (eg. one array per website)
+ */
+ public function __construct($cookieName, $expire = null, $path = null, $keyStore = false)
+ {
+ $this->name = $cookieName;
+ $this->path = $path;
+ $this->expire = $expire;
+ if (is_null($expire)
+ || !is_numeric($expire)
+ || $expire < 0
+ ) {
+ $this->expire = $this->getDefaultExpire();
+ }
+
+ $this->keyStore = $keyStore;
+ if ($this->isCookieFound()) {
+ $this->loadContentFromCookie();
+ }
+ }
+
+ /**
+ * Returns true if the visitor already has the cookie.
+ *
+ * @return bool
+ */
+ public function isCookieFound()
+ {
+ return isset($_COOKIE[$this->name]);
+ }
+
+ /**
+ * Returns the default expiry time, 2 years
+ *
+ * @return int Timestamp in 2 years
+ */
+ protected function getDefaultExpire()
+ {
+ return time() + 86400 * 365 * 2;
+ }
+
+ /**
+ * setcookie() replacement -- we don't use the built-in function because
+ * it is buggy for some PHP versions.
+ *
+ * @link http://php.net/setcookie
+ *
+ * @param string $Name Name of cookie
+ * @param string $Value Value of cookie
+ * @param int $Expires Time the cookie expires
+ * @param string $Path
+ * @param string $Domain
+ * @param bool $Secure
+ * @param bool $HTTPOnly
+ */
+ protected function setCookie($Name, $Value, $Expires, $Path = '', $Domain = '', $Secure = false, $HTTPOnly = false)
+ {
+ if (!empty($Domain)) {
+ // Fix the domain to accept domains with and without 'www.'.
+ if (!strncasecmp($Domain, 'www.', 4)) {
+ $Domain = substr($Domain, 4);
+ }
+ $Domain = '.' . $Domain;
+
+ // Remove port information.
+ $Port = strpos($Domain, ':');
+ if ($Port !== false) $Domain = substr($Domain, 0, $Port);
+ }
+
+ $header = 'Set-Cookie: ' . rawurlencode($Name) . '=' . rawurlencode($Value)
+ . (empty($Expires) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s', $Expires) . ' GMT')
+ . (empty($Path) ? '' : '; path=' . $Path)
+ . (empty($Domain) ? '' : '; domain=' . $Domain)
+ . (!$Secure ? '' : '; secure')
+ . (!$HTTPOnly ? '' : '; HttpOnly');
+
+ Piwik_Common::sendHeader($header, false);
+ }
+
+ /**
+ * We set the privacy policy header
+ */
+ protected function setP3PHeader()
+ {
+ Piwik_Common::sendHeader("P3P: CP='OTI DSP COR NID STP UNI OTPa OUR'");
+ }
+
+ /**
+ * Delete the cookie
+ */
+ public function delete()
+ {
+ $this->setP3PHeader();
+ $this->setCookie($this->name, 'deleted', time() - 31536001, $this->path, $this->domain);
+ }
+
+ /**
+ * Saves the cookie (set the Cookie header).
+ * You have to call this method before sending any text to the browser or you would get the
+ * "Header already sent" error.
+ */
+ public function save()
+ {
+ $cookieString = $this->generateContentString();
+ if (strlen($cookieString) > self::MAX_COOKIE_SIZE) {
+ // If the cookie was going to be too large, instead, delete existing cookie and start afresh
+ $this->delete();
+ return;
+ }
+
+ $this->setP3PHeader();
+ $this->setCookie($this->name, $cookieString, $this->expire, $this->path, $this->domain, $this->secure, $this->httponly);
+ }
+
+ /**
+ * Extract signed content from string: content VALUE_SEPARATOR '_=' signature
+ *
+ * @param string $content
+ * @return string|bool Content or false if unsigned
+ */
+ private function extractSignedContent($content)
+ {
+ $signature = substr($content, -40);
+ if (substr($content, -43, 3) == self::VALUE_SEPARATOR . '_=' &&
+ $signature == sha1(substr($content, 0, -40) . Piwik_Common::getSalt())
+ ) {
+ // strip trailing: VALUE_SEPARATOR '_=' signature"
+ return substr($content, 0, -43);
+ }
+ return false;
+ }
+
+ /**
+ * Load the cookie content into a php array.
+ * Parses the cookie string to extract the different variables.
+ * Unserialize the array when necessary.
+ * Decode the non numeric values that were base64 encoded.
+ */
+ protected function loadContentFromCookie()
+ {
+ $cookieStr = $this->extractSignedContent($_COOKIE[$this->name]);
+ if ($cookieStr === false) {
+ return;
+ }
+
+ $values = explode(self::VALUE_SEPARATOR, $cookieStr);
+ foreach ($values as $nameValue) {
+ $equalPos = strpos($nameValue, '=');
+ $varName = substr($nameValue, 0, $equalPos);
+ $varValue = substr($nameValue, $equalPos + 1);
+
+ // no numeric value are base64 encoded so we need to decode them
+ if (!is_numeric($varValue)) {
+ $tmpValue = base64_decode($varValue);
+ $varValue = safe_unserialize($tmpValue);
+
+ // discard entire cookie
+ // note: this assumes we never serialize a boolean
+ if ($varValue === false && $tmpValue !== 'b:0;') {
+ $this->value = array();
+ unset($_COOKIE[$this->name]);
+ break;
+ }
+ }
+
+ $this->value[$varName] = $varValue;
+ }
+ }
+
+ /**
+ * Returns the string to save in the cookie from the $this->value array of values.
+ * It goes through the array and generates the cookie content string.
+ *
+ * @return string Cookie content
+ */
+ protected function generateContentString()
+ {
+ $cookieStr = '';
+ foreach ($this->value as $name => $value) {
+ if (!is_numeric($value)) {
+ $value = base64_encode(safe_serialize($value));
+ }
+
+ $cookieStr .= "$name=$value" . self::VALUE_SEPARATOR;
+ }
+
+ if (!empty($cookieStr)) {
+ $cookieStr .= '_=';
+
+ // sign cookie
+ $signature = sha1($cookieStr . Piwik_Common::getSalt());
+ return $cookieStr . $signature;
+ }
+
+ return '';
+ }
+
+ /**
+ * Set cookie domain
+ *
+ * @param string $domain
+ */
+ public function setDomain($domain)
+ {
+ $this->domain = $domain;
+ }
+
+ /**
+ * Set secure flag
+ *
+ * @param bool $secure
+ */
+ public function setSecure($secure)
+ {
+ $this->secure = $secure;
+ }
+
+ /**
+ * Set HTTP only
+ *
+ * @param bool $httponly
+ */
+ public function setHttpOnly($httponly)
+ {
+ $this->httponly = $httponly;
+ }
+
+ /**
+ * Registers a new name => value association in the cookie.
+ *
+ * Registering new values is optimal if the value is a numeric value.
+ * If the value is a string, it will be saved as a base64 encoded string.
+ * If the value is an array, it will be saved as a serialized and base64 encoded
+ * string which is not very good in terms of bytes usage.
+ * You should save arrays only when you are sure about their maximum data size.
+ * A cookie has to stay small and its size shouldn't increase over time!
+ *
+ * @param string $name Name of the value to save; the name will be used to retrieve this value
+ * @param string|array|number $value Value to save. If null, entry will be deleted from cookie.
+ */
+ public function set($name, $value)
+ {
+ $name = self::escapeValue($name);
+
+ // Delete value if $value === null
+ if (is_null($value)) {
+ if ($this->keyStore === false) {
+ unset($this->value[$name]);
+ return;
+ }
+ unset($this->value[$this->keyStore][$name]);
+ return;
+ }
+
+ if ($this->keyStore === false) {
+ $this->value[$name] = $value;
+ return;
+ }
+ $this->value[$this->keyStore][$name] = $value;
+ }
+
+ /**
+ * Returns the value defined by $name from the cookie.
+ *
+ * @param string|integer Index name of the value to return
+ * @return mixed The value if found, false if the value is not found
+ */
+ public function get($name)
+ {
+ $name = self::escapeValue($name);
+ if ($this->keyStore === false) {
+ return isset($this->value[$name])
+ ? self::escapeValue($this->value[$name])
+ : false;
+ }
+ return isset($this->value[$this->keyStore][$name])
+ ? self::escapeValue($this->value[$this->keyStore][$name])
+ : false;
+ }
+
+ /**
+ * Returns an easy to read cookie dump
+ *
+ * @return string The cookie dump
+ */
+ public function __toString()
+ {
+ $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;
+ }
+
+ /**
+ * Escape values from the cookie before sending them back to the client
+ * (when using the get() method).
+ *
+ * @param string $value Value to be escaped
+ * @return mixed The value once cleaned.
+ */
+ static protected function escapeValue($value)
+ {
+ return Piwik_Common::sanitizeInputValues($value);
+ }
}
diff --git a/core/DataFiles/Countries.php b/core/DataFiles/Countries.php
index 6690d75523..2c061e68bf 100644
--- a/core/DataFiles/Countries.php
+++ b/core/DataFiles/Countries.php
@@ -19,312 +19,310 @@
* America lies on its own continental plate (i.e., the Caribbean Plate), we
* currently use a separate continent code (amc).
*/
-if(!isset($GLOBALS['Piwik_CountryList']))
-{
- // Primary reference: ISO 3166-1 alpha-2
- $GLOBALS['Piwik_CountryList'] = array(
- 'ad' => 'eur',
- 'ae' => 'asi',
- 'af' => 'asi',
- 'ag' => 'amc',
- 'ai' => 'amc',
- 'al' => 'eur',
- 'am' => 'asi',
- 'ao' => 'afr',
- 'aq' => 'ant',
- 'ar' => 'ams',
- 'as' => 'oce',
- 'at' => 'eur',
- 'au' => 'oce',
- 'aw' => 'amc',
- 'ax' => 'eur',
- 'az' => 'asi',
- 'ba' => 'eur',
- 'bb' => 'amc',
- 'bd' => 'asi',
- 'be' => 'eur',
- 'bf' => 'afr',
- 'bg' => 'eur',
- 'bh' => 'asi',
- 'bi' => 'afr',
- 'bj' => 'afr',
- 'bl' => 'amc',
- 'bm' => 'amc',
- 'bn' => 'asi',
- 'bo' => 'ams',
- 'bq' => 'amc',
- 'br' => 'ams',
- 'bs' => 'amc',
- 'bt' => 'asi',
- 'bv' => 'ant',
- 'bw' => 'afr',
- 'by' => 'eur',
- 'bz' => 'amc',
- 'ca' => 'amn',
- 'cc' => 'asi',
- 'cd' => 'afr',
- 'cf' => 'afr',
- 'cg' => 'afr',
- 'ch' => 'eur',
- 'ci' => 'afr',
- 'ck' => 'oce',
- 'cl' => 'ams',
- 'cm' => 'afr',
- 'cn' => 'asi',
- 'co' => 'ams',
- 'cr' => 'amc',
- 'cu' => 'amc',
- 'cv' => 'afr',
- 'cw' => 'amc',
- 'cx' => 'asi',
- 'cy' => 'eur',
- 'cz' => 'eur',
- 'de' => 'eur',
- 'dj' => 'afr',
- 'dk' => 'eur',
- 'dm' => 'amc',
- 'do' => 'amc',
- 'dz' => 'afr',
- 'ec' => 'ams',
- 'ee' => 'eur',
- 'eg' => 'afr',
- 'eh' => 'afr',
- 'er' => 'afr',
- 'es' => 'eur',
- 'et' => 'afr',
- 'fi' => 'eur',
- 'fj' => 'oce',
- 'fk' => 'ams',
- 'fm' => 'oce',
- 'fo' => 'eur',
- 'fr' => 'eur',
- 'ga' => 'afr',
- 'gb' => 'eur',
- 'gd' => 'amc',
- 'ge' => 'asi',
- 'gf' => 'ams',
- 'gg' => 'eur',
- 'gh' => 'afr',
- 'gi' => 'eur',
- 'gl' => 'amn',
- 'gm' => 'afr',
- 'gn' => 'afr',
- 'gp' => 'amc',
- 'gq' => 'afr',
- 'gr' => 'eur',
- 'gs' => 'ant',
- 'gt' => 'amc',
- 'gu' => 'oce',
- 'gw' => 'afr',
- 'gy' => 'ams',
- 'hk' => 'asi',
- 'hm' => 'ant',
- 'hn' => 'amc',
- 'hr' => 'eur',
- 'ht' => 'amc',
- 'hu' => 'eur',
- 'id' => 'asi',
- 'ie' => 'eur',
- 'il' => 'asi',
- 'im' => 'eur',
- 'in' => 'asi',
- 'io' => 'asi',
- 'iq' => 'asi',
- 'ir' => 'asi',
- 'is' => 'eur',
- 'it' => 'eur',
- 'je' => 'eur',
- 'jm' => 'amc',
- 'jo' => 'asi',
- 'jp' => 'asi',
- 'ke' => 'afr',
- 'kg' => 'asi',
- 'kh' => 'asi',
- 'ki' => 'oce',
- 'km' => 'afr',
- 'kn' => 'amc',
- 'kp' => 'asi',
- 'kr' => 'asi',
- 'kw' => 'asi',
- 'ky' => 'amc',
- 'kz' => 'asi',
- 'la' => 'asi',
- 'lb' => 'asi',
- 'lc' => 'amc',
- 'li' => 'eur',
- 'lk' => 'asi',
- 'lr' => 'afr',
- 'ls' => 'afr',
- 'lt' => 'eur',
- 'lu' => 'eur',
- 'lv' => 'eur',
- 'ly' => 'afr',
- 'ma' => 'afr',
- 'mc' => 'eur',
- 'md' => 'eur',
- 'me' => 'eur',
- 'mf' => 'amc',
- 'mg' => 'afr',
- 'mh' => 'oce',
- 'mk' => 'eur',
- 'ml' => 'afr',
- 'mm' => 'asi',
- 'mn' => 'asi',
- 'mo' => 'asi',
- 'mp' => 'oce',
- 'mq' => 'amc',
- 'mr' => 'afr',
- 'ms' => 'amc',
- 'mt' => 'eur',
- 'mu' => 'afr',
- 'mv' => 'asi',
- 'mw' => 'afr',
- 'mx' => 'amn',
- 'my' => 'asi',
- 'mz' => 'afr',
- 'na' => 'afr',
- 'nc' => 'oce',
- 'ne' => 'afr',
- 'nf' => 'oce',
- 'ng' => 'afr',
- 'ni' => 'amc',
- 'nl' => 'eur',
- 'no' => 'eur',
- 'np' => 'asi',
- 'nr' => 'oce',
- 'nu' => 'oce',
- 'nz' => 'oce',
- 'om' => 'asi',
- 'pa' => 'amc',
- 'pe' => 'ams',
- 'pf' => 'oce',
- 'pg' => 'oce',
- 'ph' => 'asi',
- 'pk' => 'asi',
- 'pl' => 'eur',
- 'pm' => 'amn',
- 'pn' => 'oce',
- 'pr' => 'amc',
- 'ps' => 'asi',
- 'pt' => 'eur',
- 'pw' => 'oce',
- 'py' => 'ams',
- 'qa' => 'asi',
- 're' => 'afr',
- 'ro' => 'eur',
- 'rs' => 'eur',
- 'ru' => 'eur',
- 'rw' => 'afr',
- 'sa' => 'asi',
- 'sb' => 'oce',
- 'sc' => 'afr',
- 'sd' => 'afr',
- 'se' => 'eur',
- 'sg' => 'asi',
- 'sh' => 'afr',
- 'si' => 'eur',
- 'sj' => 'eur',
- 'sk' => 'eur',
- 'sl' => 'afr',
- 'sm' => 'eur',
- 'sn' => 'afr',
- 'so' => 'afr',
- 'sr' => 'ams',
- 'ss' => 'afr',
- 'st' => 'afr',
- 'sv' => 'amc',
- 'sx' => 'amc',
- 'sy' => 'asi',
- 'sz' => 'afr',
- 'tc' => 'amc',
- 'td' => 'afr',
- 'tf' => 'ant',
- 'tg' => 'afr',
- 'th' => 'asi',
- 'ti' => 'asi',
- 'tj' => 'asi',
- 'tk' => 'oce',
- 'tl' => 'asi',
- 'tm' => 'asi',
- 'tn' => 'afr',
- 'to' => 'oce',
- 'tr' => 'eur',
- 'tt' => 'amc',
- 'tv' => 'oce',
- 'tw' => 'asi',
- 'tz' => 'afr',
- 'ua' => 'eur',
- 'ug' => 'afr',
- 'um' => 'oce',
- 'us' => 'amn',
- 'uy' => 'ams',
- 'uz' => 'asi',
- 'va' => 'eur',
- 'vc' => 'amc',
- 've' => 'ams',
- 'vg' => 'amc',
- 'vi' => 'amc',
- 'vn' => 'asi',
- 'vu' => 'oce',
- 'wf' => 'oce',
- 'ws' => 'oce',
- 'ye' => 'asi',
- 'yt' => 'afr',
- 'za' => 'afr',
- 'zm' => 'afr',
- 'zw' => 'afr',
- );
+if (!isset($GLOBALS['Piwik_CountryList'])) {
+ // Primary reference: ISO 3166-1 alpha-2
+ $GLOBALS['Piwik_CountryList'] = array(
+ 'ad' => 'eur',
+ 'ae' => 'asi',
+ 'af' => 'asi',
+ 'ag' => 'amc',
+ 'ai' => 'amc',
+ 'al' => 'eur',
+ 'am' => 'asi',
+ 'ao' => 'afr',
+ 'aq' => 'ant',
+ 'ar' => 'ams',
+ 'as' => 'oce',
+ 'at' => 'eur',
+ 'au' => 'oce',
+ 'aw' => 'amc',
+ 'ax' => 'eur',
+ 'az' => 'asi',
+ 'ba' => 'eur',
+ 'bb' => 'amc',
+ 'bd' => 'asi',
+ 'be' => 'eur',
+ 'bf' => 'afr',
+ 'bg' => 'eur',
+ 'bh' => 'asi',
+ 'bi' => 'afr',
+ 'bj' => 'afr',
+ 'bl' => 'amc',
+ 'bm' => 'amc',
+ 'bn' => 'asi',
+ 'bo' => 'ams',
+ 'bq' => 'amc',
+ 'br' => 'ams',
+ 'bs' => 'amc',
+ 'bt' => 'asi',
+ 'bv' => 'ant',
+ 'bw' => 'afr',
+ 'by' => 'eur',
+ 'bz' => 'amc',
+ 'ca' => 'amn',
+ 'cc' => 'asi',
+ 'cd' => 'afr',
+ 'cf' => 'afr',
+ 'cg' => 'afr',
+ 'ch' => 'eur',
+ 'ci' => 'afr',
+ 'ck' => 'oce',
+ 'cl' => 'ams',
+ 'cm' => 'afr',
+ 'cn' => 'asi',
+ 'co' => 'ams',
+ 'cr' => 'amc',
+ 'cu' => 'amc',
+ 'cv' => 'afr',
+ 'cw' => 'amc',
+ 'cx' => 'asi',
+ 'cy' => 'eur',
+ 'cz' => 'eur',
+ 'de' => 'eur',
+ 'dj' => 'afr',
+ 'dk' => 'eur',
+ 'dm' => 'amc',
+ 'do' => 'amc',
+ 'dz' => 'afr',
+ 'ec' => 'ams',
+ 'ee' => 'eur',
+ 'eg' => 'afr',
+ 'eh' => 'afr',
+ 'er' => 'afr',
+ 'es' => 'eur',
+ 'et' => 'afr',
+ 'fi' => 'eur',
+ 'fj' => 'oce',
+ 'fk' => 'ams',
+ 'fm' => 'oce',
+ 'fo' => 'eur',
+ 'fr' => 'eur',
+ 'ga' => 'afr',
+ 'gb' => 'eur',
+ 'gd' => 'amc',
+ 'ge' => 'asi',
+ 'gf' => 'ams',
+ 'gg' => 'eur',
+ 'gh' => 'afr',
+ 'gi' => 'eur',
+ 'gl' => 'amn',
+ 'gm' => 'afr',
+ 'gn' => 'afr',
+ 'gp' => 'amc',
+ 'gq' => 'afr',
+ 'gr' => 'eur',
+ 'gs' => 'ant',
+ 'gt' => 'amc',
+ 'gu' => 'oce',
+ 'gw' => 'afr',
+ 'gy' => 'ams',
+ 'hk' => 'asi',
+ 'hm' => 'ant',
+ 'hn' => 'amc',
+ 'hr' => 'eur',
+ 'ht' => 'amc',
+ 'hu' => 'eur',
+ 'id' => 'asi',
+ 'ie' => 'eur',
+ 'il' => 'asi',
+ 'im' => 'eur',
+ 'in' => 'asi',
+ 'io' => 'asi',
+ 'iq' => 'asi',
+ 'ir' => 'asi',
+ 'is' => 'eur',
+ 'it' => 'eur',
+ 'je' => 'eur',
+ 'jm' => 'amc',
+ 'jo' => 'asi',
+ 'jp' => 'asi',
+ 'ke' => 'afr',
+ 'kg' => 'asi',
+ 'kh' => 'asi',
+ 'ki' => 'oce',
+ 'km' => 'afr',
+ 'kn' => 'amc',
+ 'kp' => 'asi',
+ 'kr' => 'asi',
+ 'kw' => 'asi',
+ 'ky' => 'amc',
+ 'kz' => 'asi',
+ 'la' => 'asi',
+ 'lb' => 'asi',
+ 'lc' => 'amc',
+ 'li' => 'eur',
+ 'lk' => 'asi',
+ 'lr' => 'afr',
+ 'ls' => 'afr',
+ 'lt' => 'eur',
+ 'lu' => 'eur',
+ 'lv' => 'eur',
+ 'ly' => 'afr',
+ 'ma' => 'afr',
+ 'mc' => 'eur',
+ 'md' => 'eur',
+ 'me' => 'eur',
+ 'mf' => 'amc',
+ 'mg' => 'afr',
+ 'mh' => 'oce',
+ 'mk' => 'eur',
+ 'ml' => 'afr',
+ 'mm' => 'asi',
+ 'mn' => 'asi',
+ 'mo' => 'asi',
+ 'mp' => 'oce',
+ 'mq' => 'amc',
+ 'mr' => 'afr',
+ 'ms' => 'amc',
+ 'mt' => 'eur',
+ 'mu' => 'afr',
+ 'mv' => 'asi',
+ 'mw' => 'afr',
+ 'mx' => 'amn',
+ 'my' => 'asi',
+ 'mz' => 'afr',
+ 'na' => 'afr',
+ 'nc' => 'oce',
+ 'ne' => 'afr',
+ 'nf' => 'oce',
+ 'ng' => 'afr',
+ 'ni' => 'amc',
+ 'nl' => 'eur',
+ 'no' => 'eur',
+ 'np' => 'asi',
+ 'nr' => 'oce',
+ 'nu' => 'oce',
+ 'nz' => 'oce',
+ 'om' => 'asi',
+ 'pa' => 'amc',
+ 'pe' => 'ams',
+ 'pf' => 'oce',
+ 'pg' => 'oce',
+ 'ph' => 'asi',
+ 'pk' => 'asi',
+ 'pl' => 'eur',
+ 'pm' => 'amn',
+ 'pn' => 'oce',
+ 'pr' => 'amc',
+ 'ps' => 'asi',
+ 'pt' => 'eur',
+ 'pw' => 'oce',
+ 'py' => 'ams',
+ 'qa' => 'asi',
+ 're' => 'afr',
+ 'ro' => 'eur',
+ 'rs' => 'eur',
+ 'ru' => 'eur',
+ 'rw' => 'afr',
+ 'sa' => 'asi',
+ 'sb' => 'oce',
+ 'sc' => 'afr',
+ 'sd' => 'afr',
+ 'se' => 'eur',
+ 'sg' => 'asi',
+ 'sh' => 'afr',
+ 'si' => 'eur',
+ 'sj' => 'eur',
+ 'sk' => 'eur',
+ 'sl' => 'afr',
+ 'sm' => 'eur',
+ 'sn' => 'afr',
+ 'so' => 'afr',
+ 'sr' => 'ams',
+ 'ss' => 'afr',
+ 'st' => 'afr',
+ 'sv' => 'amc',
+ 'sx' => 'amc',
+ 'sy' => 'asi',
+ 'sz' => 'afr',
+ 'tc' => 'amc',
+ 'td' => 'afr',
+ 'tf' => 'ant',
+ 'tg' => 'afr',
+ 'th' => 'asi',
+ 'ti' => 'asi',
+ 'tj' => 'asi',
+ 'tk' => 'oce',
+ 'tl' => 'asi',
+ 'tm' => 'asi',
+ 'tn' => 'afr',
+ 'to' => 'oce',
+ 'tr' => 'eur',
+ 'tt' => 'amc',
+ 'tv' => 'oce',
+ 'tw' => 'asi',
+ 'tz' => 'afr',
+ 'ua' => 'eur',
+ 'ug' => 'afr',
+ 'um' => 'oce',
+ 'us' => 'amn',
+ 'uy' => 'ams',
+ 'uz' => 'asi',
+ 'va' => 'eur',
+ 'vc' => 'amc',
+ 've' => 'ams',
+ 'vg' => 'amc',
+ 'vi' => 'amc',
+ 'vn' => 'asi',
+ 'vu' => 'oce',
+ 'wf' => 'oce',
+ 'ws' => 'oce',
+ 'ye' => 'asi',
+ 'yt' => 'afr',
+ 'za' => 'afr',
+ 'zm' => 'afr',
+ 'zw' => 'afr',
+ );
- // codes for internal use
- $GLOBALS['Piwik_CountryList_Extras'] = array(
- // unknown
- 'xx' => 'unk',
+ // codes for internal use
+ $GLOBALS['Piwik_CountryList_Extras'] = array(
+ // unknown
+ 'xx' => 'unk',
- // exceptionally reserved
- 'ac' => 'afr', // .ac TLD
- 'cp' => 'amc',
- 'dg' => 'asi',
- 'ea' => 'afr',
- 'eu' => 'eur', // .eu TLD
- 'fx' => 'eur',
- 'ic' => 'afr',
- 'su' => 'eur', // .su TLD
- 'ta' => 'afr',
- 'uk' => 'eur', // .uk TLD
+ // exceptionally reserved
+ 'ac' => 'afr', // .ac TLD
+ 'cp' => 'amc',
+ 'dg' => 'asi',
+ 'ea' => 'afr',
+ 'eu' => 'eur', // .eu TLD
+ 'fx' => 'eur',
+ 'ic' => 'afr',
+ 'su' => 'eur', // .su TLD
+ 'ta' => 'afr',
+ 'uk' => 'eur', // .uk TLD
- // transitionally reserved
- 'an' => 'amc', // former Netherlands Antilles
- 'bu' => 'asi',
- 'cs' => 'eur', // former Serbia and Montenegro
- 'nt' => 'asi',
- 'sf' => 'eur',
- 'tp' => 'oce', // .tp TLD
- 'yu' => 'eur', // .yu TLD
- 'zr' => 'afr',
+ // transitionally reserved
+ 'an' => 'amc', // former Netherlands Antilles
+ 'bu' => 'asi',
+ 'cs' => 'eur', // former Serbia and Montenegro
+ 'nt' => 'asi',
+ 'sf' => 'eur',
+ 'tp' => 'oce', // .tp TLD
+ 'yu' => 'eur', // .yu TLD
+ 'zr' => 'afr',
- // MaxMind GeoIP specific
- 'a1' => 'unk',
- 'a2' => 'unk',
- 'ap' => 'asi',
- 'o1' => 'unk',
+ // MaxMind GeoIP specific
+ 'a1' => 'unk',
+ 'a2' => 'unk',
+ 'ap' => 'asi',
+ 'o1' => 'unk',
- // Catalonia (Spain)
- 'cat' => 'eur',
- );
+ // Catalonia (Spain)
+ 'cat' => 'eur',
+ );
}
-if(!isset($GLOBALS['Piwik_ContinentList']))
-{
- // Primary reference: ISO 3166-1 alpha-2
- $GLOBALS['Piwik_ContinentList'] = array(
- 'unk', // unknown
- 'amn', // North America
- 'amc', // Central America
- 'ams', // South America
- 'eur', // Europe
- 'afr', // Africa
- 'asi', // Asia
- 'oce', // Oceania
- 'ant', // Antarctica
- );
+if (!isset($GLOBALS['Piwik_ContinentList'])) {
+ // Primary reference: ISO 3166-1 alpha-2
+ $GLOBALS['Piwik_ContinentList'] = array(
+ 'unk', // unknown
+ 'amn', // North America
+ 'amc', // Central America
+ 'ams', // South America
+ 'eur', // Europe
+ 'afr', // Africa
+ 'asi', // Asia
+ 'oce', // Oceania
+ 'ant', // Antarctica
+ );
}
diff --git a/core/DataFiles/Currencies.php b/core/DataFiles/Currencies.php
index 7cdbd0a65f..b381c6c188 100644
--- a/core/DataFiles/Currencies.php
+++ b/core/DataFiles/Currencies.php
@@ -15,177 +15,176 @@
* @see http://en.wikipedia.org/wiki/List_of_circulating_currencies
* @see http://www.iso.org/iso/currency_codes_list-1.html
*/
-if(!isset($GLOBALS['Piwik_CurrencyList']))
-{
- $GLOBALS['Piwik_CurrencyList'] = array(
- // 'ISO-4217 CODE' => array('currency symbol', 'description'),
+if (!isset($GLOBALS['Piwik_CurrencyList'])) {
+ $GLOBALS['Piwik_CurrencyList'] = array(
+ // 'ISO-4217 CODE' => array('currency symbol', 'description'),
- // Top 5 by global trading volume
- 'USD' => array('$', 'US dollar'),
- 'EUR' => array('€', 'Euro'),
- 'JPY' => array('¥', 'Japanese yen'),
- 'GBP' => array('£', 'British pound'),
- 'CHF' => array('Fr', 'Swiss franc'),
+ // Top 5 by global trading volume
+ 'USD' => array('$', 'US dollar'),
+ 'EUR' => array('€', 'Euro'),
+ 'JPY' => array('¥', 'Japanese yen'),
+ 'GBP' => array('£', 'British pound'),
+ 'CHF' => array('Fr', 'Swiss franc'),
- 'AFN' => array('؋', 'Afghan afghani'),
- 'ALL' => array('L', 'Albanian lek'),
- 'DZD' => array('د.ج', 'Algerian dinar'),
- 'AOA' => array('Kz', 'Angolan kwanza'),
- 'ARS' => array('$', 'Argentine peso'),
- 'AMD' => array('դր.', 'Armenian dram'),
- 'AWG' => array('ƒ', 'Aruban florin'),
- 'AUD' => array('$', 'Australian dollar'),
- 'AZN' => array('m', 'Azerbaijani manat'),
- 'BSD' => array('$', 'Bahamian dollar'),
- 'BHD' => array('.د.ب', 'Bahraini dinar'),
- 'BDT' => array('৳', 'Bangladeshi taka'),
- 'BBD' => array('$', 'Barbadian dollar'),
- 'BYR' => array('Br', 'Belarusian ruble'),
- 'BZD' => array('$', 'Belize dollar'),
- 'BMD' => array('$', 'Bermudian dollar'),
- 'BTN' => array('Nu.', 'Bhutanese ngultrum'),
- 'BOB' => array('Bs.', 'Bolivian boliviano'),
- 'BAM' => array('KM', 'Bosnia Herzegovina mark'),
- 'BWP' => array('P', 'Botswana pula'),
- 'BRL' => array('R$', 'Brazilian real'),
+ 'AFN' => array('؋', 'Afghan afghani'),
+ 'ALL' => array('L', 'Albanian lek'),
+ 'DZD' => array('د.ج', 'Algerian dinar'),
+ 'AOA' => array('Kz', 'Angolan kwanza'),
+ 'ARS' => array('$', 'Argentine peso'),
+ 'AMD' => array('դր.', 'Armenian dram'),
+ 'AWG' => array('ƒ', 'Aruban florin'),
+ 'AUD' => array('$', 'Australian dollar'),
+ 'AZN' => array('m', 'Azerbaijani manat'),
+ 'BSD' => array('$', 'Bahamian dollar'),
+ 'BHD' => array('.د.ب', 'Bahraini dinar'),
+ 'BDT' => array('৳', 'Bangladeshi taka'),
+ 'BBD' => array('$', 'Barbadian dollar'),
+ 'BYR' => array('Br', 'Belarusian ruble'),
+ 'BZD' => array('$', 'Belize dollar'),
+ 'BMD' => array('$', 'Bermudian dollar'),
+ 'BTN' => array('Nu.', 'Bhutanese ngultrum'),
+ 'BOB' => array('Bs.', 'Bolivian boliviano'),
+ 'BAM' => array('KM', 'Bosnia Herzegovina mark'),
+ 'BWP' => array('P', 'Botswana pula'),
+ 'BRL' => array('R$', 'Brazilian real'),
// 'GBP' => array('£', 'British pound'),
- 'BND' => array('$', 'Brunei dollar'),
- 'BGN' => array('лв', 'Bulgarian lev'),
- 'BIF' => array('Fr', 'Burundian franc'),
- 'KHR' => array('៛', 'Cambodian riel'),
- 'CAD' => array('$', 'Canadian dollar'),
- 'CVE' => array('$', 'Cape Verdean escudo'),
- 'KYD' => array('$', 'Cayman Islands dollar'),
- 'XAF' => array('Fr', 'Central African CFA franc'),
- 'CLP' => array('$', 'Chilean peso'),
- 'CNY' => array('元', 'Chinese yuan'),
- 'COP' => array('$', 'Colombian peso'),
- 'KMF' => array('Fr', 'Comorian franc'),
- 'CDF' => array('Fr', 'Congolese franc'),
- 'CRC' => array('₡', 'Costa Rican colón'),
- 'HRK' => array('kn', 'Croatian kuna'),
- 'XPF' => array('F', 'CFP franc'),
- 'CUC' => array('$', 'Cuban convertible peso'),
- 'CUP' => array('$', 'Cuban peso'),
- 'CMG' => array('ƒ', 'Curaçao and Sint Maarten guilder'),
- 'CZK' => array('Kč', 'Czech koruna'),
- 'DKK' => array('kr', 'Danish krone'),
- 'DJF' => array('Fr', 'Djiboutian franc'),
- 'DOP' => array('$', 'Dominican peso'),
- 'XCD' => array('$', 'East Caribbean dollar'),
- 'EGP' => array('ج.م', 'Egyptian pound'),
- 'ERN' => array('Nfk', 'Eritrean nakfa'),
- 'EEK' => array('kr', 'Estonian kroon'),
- 'ETB' => array('Br', 'Ethiopian birr'),
+ 'BND' => array('$', 'Brunei dollar'),
+ 'BGN' => array('лв', 'Bulgarian lev'),
+ 'BIF' => array('Fr', 'Burundian franc'),
+ 'KHR' => array('៛', 'Cambodian riel'),
+ 'CAD' => array('$', 'Canadian dollar'),
+ 'CVE' => array('$', 'Cape Verdean escudo'),
+ 'KYD' => array('$', 'Cayman Islands dollar'),
+ 'XAF' => array('Fr', 'Central African CFA franc'),
+ 'CLP' => array('$', 'Chilean peso'),
+ 'CNY' => array('元', 'Chinese yuan'),
+ 'COP' => array('$', 'Colombian peso'),
+ 'KMF' => array('Fr', 'Comorian franc'),
+ 'CDF' => array('Fr', 'Congolese franc'),
+ 'CRC' => array('₡', 'Costa Rican colón'),
+ 'HRK' => array('kn', 'Croatian kuna'),
+ 'XPF' => array('F', 'CFP franc'),
+ 'CUC' => array('$', 'Cuban convertible peso'),
+ 'CUP' => array('$', 'Cuban peso'),
+ 'CMG' => array('ƒ', 'Curaçao and Sint Maarten guilder'),
+ 'CZK' => array('Kč', 'Czech koruna'),
+ 'DKK' => array('kr', 'Danish krone'),
+ 'DJF' => array('Fr', 'Djiboutian franc'),
+ 'DOP' => array('$', 'Dominican peso'),
+ 'XCD' => array('$', 'East Caribbean dollar'),
+ 'EGP' => array('ج.م', 'Egyptian pound'),
+ 'ERN' => array('Nfk', 'Eritrean nakfa'),
+ 'EEK' => array('kr', 'Estonian kroon'),
+ 'ETB' => array('Br', 'Ethiopian birr'),
// 'EUR' => array('€', 'Euro'),
- 'FKP' => array('£', 'Falkland Islands pound'),
- 'FJD' => array('$', 'Fijian dollar'),
- 'GMD' => array('D', 'Gambian dalasi'),
- 'GEL' => array('ლ', 'Georgian lari'),
- 'GHS' => array('₵', 'Ghanaian cedi'),
- 'GIP' => array('£', 'Gibraltar pound'),
- 'GTQ' => array('Q', 'Guatemalan quetzal'),
- 'GNF' => array('Fr', 'Guinean franc'),
- 'GYD' => array('$', 'Guyanese dollar'),
- 'HTG' => array('G', 'Haitian gourde'),
- 'HNL' => array('L', 'Honduran lempira'),
- 'HKD' => array('$', 'Hong Kong dollar'),
- 'HUF' => array('Ft', 'Hungarian forint'),
- 'ISK' => array('kr', 'Icelandic króna'),
- 'INR' => array('‎₹', 'Indian rupee'),
- 'IDR' => array('Rp', 'Indonesian rupiah'),
- 'IRR' => array('﷼', 'Iranian rial'),
- 'IQD' => array('ع.د', 'Iraqi dinar'),
- 'ILS' => array('₪', 'Israeli new shekel'),
- 'JMD' => array('$', 'Jamaican dollar'),
+ 'FKP' => array('£', 'Falkland Islands pound'),
+ 'FJD' => array('$', 'Fijian dollar'),
+ 'GMD' => array('D', 'Gambian dalasi'),
+ 'GEL' => array('ლ', 'Georgian lari'),
+ 'GHS' => array('₵', 'Ghanaian cedi'),
+ 'GIP' => array('£', 'Gibraltar pound'),
+ 'GTQ' => array('Q', 'Guatemalan quetzal'),
+ 'GNF' => array('Fr', 'Guinean franc'),
+ 'GYD' => array('$', 'Guyanese dollar'),
+ 'HTG' => array('G', 'Haitian gourde'),
+ 'HNL' => array('L', 'Honduran lempira'),
+ 'HKD' => array('$', 'Hong Kong dollar'),
+ 'HUF' => array('Ft', 'Hungarian forint'),
+ 'ISK' => array('kr', 'Icelandic króna'),
+ 'INR' => array('‎₹', 'Indian rupee'),
+ 'IDR' => array('Rp', 'Indonesian rupiah'),
+ 'IRR' => array('﷼', 'Iranian rial'),
+ 'IQD' => array('ع.د', 'Iraqi dinar'),
+ 'ILS' => array('₪', 'Israeli new shekel'),
+ 'JMD' => array('$', 'Jamaican dollar'),
// 'JPY' => array('¥', 'Japanese yen'),
- 'JOD' => array('د.ا', 'Jordanian dinar'),
- 'KZT' => array('₸', 'Kazakhstani tenge'),
- 'KES' => array('Sh', 'Kenyan shilling'),
- 'KWD' => array('د.ك', 'Kuwaiti dinar'),
- 'KGS' => array('лв', 'Kyrgyzstani som'),
- 'LAK' => array('₭', 'Lao kip'),
- 'LVL' => array('Ls', 'Latvian lats'),
- 'LBP' => array('ل.ل', 'Lebanese pound'),
- 'LSL' => array('L', 'Lesotho loti'),
- 'LRD' => array('$', 'Liberian dollar'),
- 'LYD' => array('ل.د', 'Libyan dinar'),
- 'LTL' => array('Lt', 'Lithuanian litas'),
- 'MOP' => array('P', 'Macanese pataca'),
- 'MKD' => array('ден', 'Macedonian denar'),
- 'MGA' => array('Ar', 'Malagasy ariary'),
- 'MWK' => array('MK', 'Malawian kwacha'),
- 'MYR' => array('RM', 'Malaysian ringgit'),
- 'MVR' => array('ރ.', 'Maldivian rufiyaa'),
- 'MRO' => array('UM', 'Mauritanian ouguiya'),
- 'MUR' => array('₨', 'Mauritian rupee'),
- 'MXN' => array('$', 'Mexican peso'),
- 'MDL' => array('L', 'Moldovan leu'),
- 'MNT' => array('₮', 'Mongolian tögrög'),
- 'MAD' => array('د.م.', 'Moroccan dirham'),
- 'MZN' => array('MTn', 'Mozambican metical'),
- 'MMK' => array('K', 'Myanma kyat'),
- 'NAD' => array('$', 'Namibian dollar'),
- 'NPR' => array('₨', 'Nepalese rupee'),
- 'ANG' => array('ƒ', 'Netherlands Antillean guilder'),
- 'TWD' => array('$', 'New Taiwan dollar'),
- 'NZD' => array('$', 'New Zealand dollar'),
- 'NIO' => array('C$', 'Nicaraguan córdoba'),
- 'NGN' => array('₦', 'Nigerian naira'),
- 'KPW' => array('₩', 'North Korean won'),
- 'NOK' => array('kr', 'Norwegian krone'),
- 'OMR' => array('ر.ع.', 'Omani rial'),
- 'PKR' => array('₨', 'Pakistani rupee'),
- 'PAB' => array('B/.', 'Panamanian balboa'),
- 'PGK' => array('K', 'Papua New Guinean kina'),
- 'PYG' => array('₲', 'Paraguayan guaraní'),
- 'PEN' => array('S/.', 'Peruvian nuevo sol'),
- 'PHP' => array('₱', 'Philippine peso'),
- 'PLN' => array('zł', 'Polish złoty'),
- 'QAR' => array('ر.ق', 'Qatari riyal'),
- 'RON' => array('L', 'Romanian leu'),
- 'RUB' => array('руб.', 'Russian ruble'),
- 'RWF' => array('Fr', 'Rwandan franc'),
- 'SHP' => array('£', 'Saint Helena pound'),
- 'SVC' => array('₡', 'Salvadoran colón'),
- 'WST' => array('T', 'Samoan tala'),
- 'STD' => array('Db', 'São Tomé and Príncipe dobra'),
- 'SAR' => array('ر.س', 'Saudi riyal'),
- 'RSD' => array('дин. or din.', 'Serbian dinar'),
- 'SCR' => array('₨', 'Seychellois rupee'),
- 'SLL' => array('Le', 'Sierra Leonean leone'),
- 'SGD' => array('$', 'Singapore dollar'),
- 'SBD' => array('$', 'Solomon Islands dollar'),
- 'SOS' => array('Sh', 'Somali shilling'),
- 'ZAR' => array('R', 'South African rand'),
- 'KRW' => array('₩', 'South Korean won'),
- 'LKR' => array('Rs', 'Sri Lankan rupee'),
- 'SDG' => array('جنيه سوداني', 'Sudanese pound'),
- 'SRD' => array('$', 'Surinamese dollar'),
- 'SZL' => array('L', 'Swazi lilangeni'),
- 'SEK' => array('kr', 'Swedish krona'),
+ 'JOD' => array('د.ا', 'Jordanian dinar'),
+ 'KZT' => array('₸', 'Kazakhstani tenge'),
+ 'KES' => array('Sh', 'Kenyan shilling'),
+ 'KWD' => array('د.ك', 'Kuwaiti dinar'),
+ 'KGS' => array('лв', 'Kyrgyzstani som'),
+ 'LAK' => array('₭', 'Lao kip'),
+ 'LVL' => array('Ls', 'Latvian lats'),
+ 'LBP' => array('ل.ل', 'Lebanese pound'),
+ 'LSL' => array('L', 'Lesotho loti'),
+ 'LRD' => array('$', 'Liberian dollar'),
+ 'LYD' => array('ل.د', 'Libyan dinar'),
+ 'LTL' => array('Lt', 'Lithuanian litas'),
+ 'MOP' => array('P', 'Macanese pataca'),
+ 'MKD' => array('ден', 'Macedonian denar'),
+ 'MGA' => array('Ar', 'Malagasy ariary'),
+ 'MWK' => array('MK', 'Malawian kwacha'),
+ 'MYR' => array('RM', 'Malaysian ringgit'),
+ 'MVR' => array('ރ.', 'Maldivian rufiyaa'),
+ 'MRO' => array('UM', 'Mauritanian ouguiya'),
+ 'MUR' => array('₨', 'Mauritian rupee'),
+ 'MXN' => array('$', 'Mexican peso'),
+ 'MDL' => array('L', 'Moldovan leu'),
+ 'MNT' => array('₮', 'Mongolian tögrög'),
+ 'MAD' => array('د.م.', 'Moroccan dirham'),
+ 'MZN' => array('MTn', 'Mozambican metical'),
+ 'MMK' => array('K', 'Myanma kyat'),
+ 'NAD' => array('$', 'Namibian dollar'),
+ 'NPR' => array('₨', 'Nepalese rupee'),
+ 'ANG' => array('ƒ', 'Netherlands Antillean guilder'),
+ 'TWD' => array('$', 'New Taiwan dollar'),
+ 'NZD' => array('$', 'New Zealand dollar'),
+ 'NIO' => array('C$', 'Nicaraguan córdoba'),
+ 'NGN' => array('₦', 'Nigerian naira'),
+ 'KPW' => array('₩', 'North Korean won'),
+ 'NOK' => array('kr', 'Norwegian krone'),
+ 'OMR' => array('ر.ع.', 'Omani rial'),
+ 'PKR' => array('₨', 'Pakistani rupee'),
+ 'PAB' => array('B/.', 'Panamanian balboa'),
+ 'PGK' => array('K', 'Papua New Guinean kina'),
+ 'PYG' => array('₲', 'Paraguayan guaraní'),
+ 'PEN' => array('S/.', 'Peruvian nuevo sol'),
+ 'PHP' => array('₱', 'Philippine peso'),
+ 'PLN' => array('zł', 'Polish złoty'),
+ 'QAR' => array('ر.ق', 'Qatari riyal'),
+ 'RON' => array('L', 'Romanian leu'),
+ 'RUB' => array('руб.', 'Russian ruble'),
+ 'RWF' => array('Fr', 'Rwandan franc'),
+ 'SHP' => array('£', 'Saint Helena pound'),
+ 'SVC' => array('₡', 'Salvadoran colón'),
+ 'WST' => array('T', 'Samoan tala'),
+ 'STD' => array('Db', 'São Tomé and Príncipe dobra'),
+ 'SAR' => array('ر.س', 'Saudi riyal'),
+ 'RSD' => array('дин. or din.', 'Serbian dinar'),
+ 'SCR' => array('₨', 'Seychellois rupee'),
+ 'SLL' => array('Le', 'Sierra Leonean leone'),
+ 'SGD' => array('$', 'Singapore dollar'),
+ 'SBD' => array('$', 'Solomon Islands dollar'),
+ 'SOS' => array('Sh', 'Somali shilling'),
+ 'ZAR' => array('R', 'South African rand'),
+ 'KRW' => array('₩', 'South Korean won'),
+ 'LKR' => array('Rs', 'Sri Lankan rupee'),
+ 'SDG' => array('جنيه سوداني', 'Sudanese pound'),
+ 'SRD' => array('$', 'Surinamese dollar'),
+ 'SZL' => array('L', 'Swazi lilangeni'),
+ 'SEK' => array('kr', 'Swedish krona'),
// 'CHF' => array('Fr', 'Swiss franc'),
- 'SYP' => array('ل.س', 'Syrian pound'),
- 'TJS' => array('ЅМ', 'Tajikistani somoni'),
- 'TZS' => array('Sh', 'Tanzanian shilling'),
- 'THB' => array('฿', 'Thai baht'),
- 'TOP' => array('T$', 'Tongan paʻanga'),
- 'TTD' => array('$', 'Trinidad and Tobago dollar'),
- 'TND' => array('د.ت', 'Tunisian dinar'),
- 'TRY' => array('TL', 'Turkish lira'),
- 'TMM' => array('m', 'Turkmenistani manat'),
- 'UGX' => array('Sh', 'Ugandan shilling'),
- 'UAH' => array('₴', 'Ukrainian hryvnia'),
- 'AED' => array('د.إ', 'United Arab Emirates dirham'),
+ 'SYP' => array('ل.س', 'Syrian pound'),
+ 'TJS' => array('ЅМ', 'Tajikistani somoni'),
+ 'TZS' => array('Sh', 'Tanzanian shilling'),
+ 'THB' => array('฿', 'Thai baht'),
+ 'TOP' => array('T$', 'Tongan paʻanga'),
+ 'TTD' => array('$', 'Trinidad and Tobago dollar'),
+ 'TND' => array('د.ت', 'Tunisian dinar'),
+ 'TRY' => array('TL', 'Turkish lira'),
+ 'TMM' => array('m', 'Turkmenistani manat'),
+ 'UGX' => array('Sh', 'Ugandan shilling'),
+ 'UAH' => array('₴', 'Ukrainian hryvnia'),
+ 'AED' => array('د.إ', 'United Arab Emirates dirham'),
// 'USD' => array('$', 'United States dollar'),
- 'UYU' => array('$', 'Uruguayan peso'),
- 'UZS' => array('лв', 'Uzbekistani som'),
- 'VUV' => array('Vt', 'Vanuatu vatu'),
- 'VEF' => array('Bs F', 'Venezuelan bolívar'),
- 'VND' => array('₫', 'Vietnamese đồng'),
- 'XOF' => array('Fr', 'West African CFA franc'),
- 'YER' => array('﷼', 'Yemeni rial'),
- 'ZMK' => array('ZK', 'Zambian kwacha'),
- 'ZWL' => array('$', 'Zimbabwean dollar'),
- );
+ 'UYU' => array('$', 'Uruguayan peso'),
+ 'UZS' => array('лв', 'Uzbekistani som'),
+ 'VUV' => array('Vt', 'Vanuatu vatu'),
+ 'VEF' => array('Bs F', 'Venezuelan bolívar'),
+ 'VND' => array('₫', 'Vietnamese đồng'),
+ 'XOF' => array('Fr', 'West African CFA franc'),
+ 'YER' => array('﷼', 'Yemeni rial'),
+ 'ZMK' => array('ZK', 'Zambian kwacha'),
+ 'ZWL' => array('$', 'Zimbabwean dollar'),
+ );
}
diff --git a/core/DataFiles/LanguageToCountry.php b/core/DataFiles/LanguageToCountry.php
index 64f63b1040..4ad1d0ef0a 100644
--- a/core/DataFiles/LanguageToCountry.php
+++ b/core/DataFiles/LanguageToCountry.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package DataFiles
*/
@@ -19,48 +19,47 @@
*
* If you want to add a new entry, please email us at hello at piwik.org
*/
-if(!isset($GLOBALS['Piwik_LanguageToCountry']))
-{
- $GLOBALS['Piwik_LanguageToCountry'] = array(
- 'bg' => 'bg', // Bulgarian => Bulgaria
- 'ca' => 'es', // Catalan => Spain
- 'cs' => 'cz', // Czech => Czech Republic
- 'da' => 'dk', // Danish => Denmark
- 'de' => 'de', // German => Germany
- 'el' => 'gr', // Greek => Greece
- 'es' => 'es', // Spanish => Spain
- 'et' => 'ee', // Estonian => Estonia
- 'fa' => 'ir', // Farsi => Iran
- 'fi' => 'fi', // Finnish => Finland
- 'fr' => 'fr', // French => France
- 'he' => 'il', // Hebrew => Israel
- 'hr' => 'hr', // Croatian => Croatia
- 'hu' => 'hu', // Hungarian => Hungary
- 'id' => 'id', // Indonesian => Indonesia
- 'is' => 'is', // Icelandic => Iceland
- 'it' => 'it', // Italian => Italy
- 'ja' => 'jp', // Japanese => Japan
- 'ko' => 'kr', // Korean => South Korea
- 'lt' => 'lt', // Lithuanian => Lithuania
- 'lv' => 'lv', // Latvian => Latvia
- 'mk' => 'mk', // Macedonian => Macedonia
- 'ms' => 'my', // Malay => Malaysia
- 'nb' => 'no', // Bokmål => Norway
- 'nl' => 'nl', // Dutch => Netherlands
- 'nn' => 'no', // Nynorsk => Norway
- 'no' => 'no', // Norwegian => Norway
- 'pl' => 'pl', // Polish => Poland
- 'pt' => 'pt', // Portugese => Portugal
- 'ro' => 'ro', // Romanian => Romania
- 'ru' => 'ru', // Russian => Russia
- 'sk' => 'sk', // Slovak => Slovakia
- 'sl' => 'si', // Slovene => Slovenia
- 'sq' => 'al', // Albanian => Albania
- 'sr' => 'rs', // Serbian => Serbia
- 'sv' => 'se', // Swedish => Sweden
- 'th' => 'th', // Thai => Thailand
- 'bo' => 'ti', // Tibetan => Tibet
- 'tr' => 'tr', // Turkish => Turkey
- 'uk' => 'ua', // Ukrainian => Ukraine
- );
+if (!isset($GLOBALS['Piwik_LanguageToCountry'])) {
+ $GLOBALS['Piwik_LanguageToCountry'] = array(
+ 'bg' => 'bg', // Bulgarian => Bulgaria
+ 'ca' => 'es', // Catalan => Spain
+ 'cs' => 'cz', // Czech => Czech Republic
+ 'da' => 'dk', // Danish => Denmark
+ 'de' => 'de', // German => Germany
+ 'el' => 'gr', // Greek => Greece
+ 'es' => 'es', // Spanish => Spain
+ 'et' => 'ee', // Estonian => Estonia
+ 'fa' => 'ir', // Farsi => Iran
+ 'fi' => 'fi', // Finnish => Finland
+ 'fr' => 'fr', // French => France
+ 'he' => 'il', // Hebrew => Israel
+ 'hr' => 'hr', // Croatian => Croatia
+ 'hu' => 'hu', // Hungarian => Hungary
+ 'id' => 'id', // Indonesian => Indonesia
+ 'is' => 'is', // Icelandic => Iceland
+ 'it' => 'it', // Italian => Italy
+ 'ja' => 'jp', // Japanese => Japan
+ 'ko' => 'kr', // Korean => South Korea
+ 'lt' => 'lt', // Lithuanian => Lithuania
+ 'lv' => 'lv', // Latvian => Latvia
+ 'mk' => 'mk', // Macedonian => Macedonia
+ 'ms' => 'my', // Malay => Malaysia
+ 'nb' => 'no', // Bokmål => Norway
+ 'nl' => 'nl', // Dutch => Netherlands
+ 'nn' => 'no', // Nynorsk => Norway
+ 'no' => 'no', // Norwegian => Norway
+ 'pl' => 'pl', // Polish => Poland
+ 'pt' => 'pt', // Portugese => Portugal
+ 'ro' => 'ro', // Romanian => Romania
+ 'ru' => 'ru', // Russian => Russia
+ 'sk' => 'sk', // Slovak => Slovakia
+ 'sl' => 'si', // Slovene => Slovenia
+ 'sq' => 'al', // Albanian => Albania
+ 'sr' => 'rs', // Serbian => Serbia
+ 'sv' => 'se', // Swedish => Sweden
+ 'th' => 'th', // Thai => Thailand
+ 'bo' => 'ti', // Tibetan => Tibet
+ 'tr' => 'tr', // Turkish => Turkey
+ 'uk' => 'ua', // Ukrainian => Ukraine
+ );
}
diff --git a/core/DataFiles/Languages.php b/core/DataFiles/Languages.php
index fb3c8c0ef8..0adf560854 100644
--- a/core/DataFiles/Languages.php
+++ b/core/DataFiles/Languages.php
@@ -12,195 +12,194 @@
/*
* Language database
*/
-if(!isset($GLOBALS['Piwik_LanguageList']))
-{
- // Reference: ISO 639-1 alpha-2
- $GLOBALS['Piwik_LanguageList'] = array(
- 'aa' => array('Afar'),
- 'ab' => array('Abkhazian'),
- 'ae' => array('Avestan'),
- 'af' => array('Afrikaans'),
- 'ak' => array('Akan'),
- 'am' => array('Amharic'),
- 'an' => array('Aragonese'),
- 'ar' => array('Arabic'),
- 'as' => array('Assamese'),
- 'av' => array('Avaric'),
- 'ay' => array('Aymara'),
- 'az' => array('Azerbaijani'),
- 'ba' => array('Bashkir'),
- 'be' => array('Belarusian'),
- 'bg' => array('Bulgarian'),
- 'bh' => array('Bihari'), // 'Bihari languages'
- 'bi' => array('Bislama'),
- 'bm' => array('Bambara'),
- 'bn' => array('Bengali'),
- 'bo' => array('Tibetan'),
- 'br' => array('Breton'),
- 'bs' => array('Bosnian'),
- 'ca' => array('Catalan', 'Valencian'),
- 'ce' => array('Chechen'),
- 'ch' => array('Chamorro'),
- 'co' => array('Corsican'),
- 'cr' => array('Cree'),
- 'cs' => array('Czech'),
- 'cu' => array('Church Slavic', 'Old Slavonic', 'Church Slavonic', 'Old Bulgarian', 'Old Church Slavonic'),
- 'cv' => array('Chuvash'),
- 'cy' => array('Welsh'),
- 'da' => array('Danish'),
- 'de' => array('German'),
- 'dv' => array('Divehi', 'Dhivehi', 'Maldivian'),
- 'dz' => array('Dzongkha'),
- 'ee' => array('Ewe'),
- 'el' => array('Greek', 'Modern Greek', 'Hellenic'), // Greek, Modern (1453-)
- 'en' => array('English'),
- 'eo' => array('Esperanto'),
- 'es' => array('Spanish', 'Castilian'),
- 'et' => array('Estonian'),
- 'eu' => array('Basque'),
- 'fa' => array('Persian'),
- 'ff' => array('Fulah'),
- 'fi' => array('Finnish'),
- 'fj' => array('Fijian'),
- 'fo' => array('Faroese'),
- 'fr' => array('French'),
- 'fy' => array('Western Frisian'),
- 'ga' => array('Irish'),
- 'gd' => array('Gaelic', 'Scottish Gaelic'),
- 'gl' => array('Galician'),
- 'gn' => array('Guarani'),
- 'gu' => array('Gujarati'),
- 'gv' => array('Manx'),
- 'ha' => array('Hausa'),
- 'he' => array('Hebrew'),
- 'hi' => array('Hindi'),
- 'ho' => array('Hiri Motu'),
- 'hr' => array('Croatian'),
- 'ht' => array('Haitian', 'Haitian Creole'),
- 'hu' => array('Hungarian'),
- 'hy' => array('Armenian'),
- 'hz' => array('Herero'),
- 'ia' => array('Interlingua'), // 'Interlingua (International Auxiliary Language Association)'
- 'id' => array('Indonesian'),
- 'ie' => array('Interlingue', 'Occidental'),
- 'ig' => array('Igbo'),
- 'ii' => array('Sichuan Yi', 'Nuosu'),
- 'ik' => array('Inupiaq'),
- 'io' => array('Ido'),
- 'is' => array('Icelandic'),
- 'it' => array('Italian'),
- 'iu' => array('Inuktitut'),
- 'ja' => array('Japanese'),
- 'jv' => array('Javanese'),
- 'ka' => array('Georgian'),
- 'kg' => array('Kongo'),
- 'ki' => array('Kikuyu', 'Gikuyu'),
- 'kj' => array('Kuanyama', 'Kwanyama'),
- 'kk' => array('Kazakh'),
- 'kl' => array('Kalaallisut', 'Greenlandic'),
- 'km' => array('Central Khmer'),
- 'kn' => array('Kannada'),
- 'ko' => array('Korean'),
- 'kr' => array('Kanuri'),
- 'ks' => array('Kashmiri'),
- 'ku' => array('Kurdish'),
- 'kv' => array('Komi'),
- 'kw' => array('Cornish'),
- 'ky' => array('Kirghiz', 'Kyrgyz'),
- 'la' => array('Latin'),
- 'lb' => array('Luxembourgish', 'Letzeburgesch'),
- 'lg' => array('Ganda'),
- 'li' => array('Limburgan', 'Limburger', 'Limburgish'),
- 'ln' => array('Lingala'),
- 'lo' => array('Lao'),
- 'lt' => array('Lithuanian'),
- 'lu' => array('Luba-Katanga'),
- 'lv' => array('Latvian'),
- 'mg' => array('Malagasy'),
- 'mh' => array('Marshallese'),
- 'mi' => array('Maori'),
- 'mk' => array('Macedonian'),
- 'ml' => array('Malayalam'),
- 'mn' => array('Mongolian'),
+if (!isset($GLOBALS['Piwik_LanguageList'])) {
+ // Reference: ISO 639-1 alpha-2
+ $GLOBALS['Piwik_LanguageList'] = array(
+ 'aa' => array('Afar'),
+ 'ab' => array('Abkhazian'),
+ 'ae' => array('Avestan'),
+ 'af' => array('Afrikaans'),
+ 'ak' => array('Akan'),
+ 'am' => array('Amharic'),
+ 'an' => array('Aragonese'),
+ 'ar' => array('Arabic'),
+ 'as' => array('Assamese'),
+ 'av' => array('Avaric'),
+ 'ay' => array('Aymara'),
+ 'az' => array('Azerbaijani'),
+ 'ba' => array('Bashkir'),
+ 'be' => array('Belarusian'),
+ 'bg' => array('Bulgarian'),
+ 'bh' => array('Bihari'), // 'Bihari languages'
+ 'bi' => array('Bislama'),
+ 'bm' => array('Bambara'),
+ 'bn' => array('Bengali'),
+ 'bo' => array('Tibetan'),
+ 'br' => array('Breton'),
+ 'bs' => array('Bosnian'),
+ 'ca' => array('Catalan', 'Valencian'),
+ 'ce' => array('Chechen'),
+ 'ch' => array('Chamorro'),
+ 'co' => array('Corsican'),
+ 'cr' => array('Cree'),
+ 'cs' => array('Czech'),
+ 'cu' => array('Church Slavic', 'Old Slavonic', 'Church Slavonic', 'Old Bulgarian', 'Old Church Slavonic'),
+ 'cv' => array('Chuvash'),
+ 'cy' => array('Welsh'),
+ 'da' => array('Danish'),
+ 'de' => array('German'),
+ 'dv' => array('Divehi', 'Dhivehi', 'Maldivian'),
+ 'dz' => array('Dzongkha'),
+ 'ee' => array('Ewe'),
+ 'el' => array('Greek', 'Modern Greek', 'Hellenic'), // Greek, Modern (1453-)
+ 'en' => array('English'),
+ 'eo' => array('Esperanto'),
+ 'es' => array('Spanish', 'Castilian'),
+ 'et' => array('Estonian'),
+ 'eu' => array('Basque'),
+ 'fa' => array('Persian'),
+ 'ff' => array('Fulah'),
+ 'fi' => array('Finnish'),
+ 'fj' => array('Fijian'),
+ 'fo' => array('Faroese'),
+ 'fr' => array('French'),
+ 'fy' => array('Western Frisian'),
+ 'ga' => array('Irish'),
+ 'gd' => array('Gaelic', 'Scottish Gaelic'),
+ 'gl' => array('Galician'),
+ 'gn' => array('Guarani'),
+ 'gu' => array('Gujarati'),
+ 'gv' => array('Manx'),
+ 'ha' => array('Hausa'),
+ 'he' => array('Hebrew'),
+ 'hi' => array('Hindi'),
+ 'ho' => array('Hiri Motu'),
+ 'hr' => array('Croatian'),
+ 'ht' => array('Haitian', 'Haitian Creole'),
+ 'hu' => array('Hungarian'),
+ 'hy' => array('Armenian'),
+ 'hz' => array('Herero'),
+ 'ia' => array('Interlingua'), // 'Interlingua (International Auxiliary Language Association)'
+ 'id' => array('Indonesian'),
+ 'ie' => array('Interlingue', 'Occidental'),
+ 'ig' => array('Igbo'),
+ 'ii' => array('Sichuan Yi', 'Nuosu'),
+ 'ik' => array('Inupiaq'),
+ 'io' => array('Ido'),
+ 'is' => array('Icelandic'),
+ 'it' => array('Italian'),
+ 'iu' => array('Inuktitut'),
+ 'ja' => array('Japanese'),
+ 'jv' => array('Javanese'),
+ 'ka' => array('Georgian'),
+ 'kg' => array('Kongo'),
+ 'ki' => array('Kikuyu', 'Gikuyu'),
+ 'kj' => array('Kuanyama', 'Kwanyama'),
+ 'kk' => array('Kazakh'),
+ 'kl' => array('Kalaallisut', 'Greenlandic'),
+ 'km' => array('Central Khmer'),
+ 'kn' => array('Kannada'),
+ 'ko' => array('Korean'),
+ 'kr' => array('Kanuri'),
+ 'ks' => array('Kashmiri'),
+ 'ku' => array('Kurdish'),
+ 'kv' => array('Komi'),
+ 'kw' => array('Cornish'),
+ 'ky' => array('Kirghiz', 'Kyrgyz'),
+ 'la' => array('Latin'),
+ 'lb' => array('Luxembourgish', 'Letzeburgesch'),
+ 'lg' => array('Ganda'),
+ 'li' => array('Limburgan', 'Limburger', 'Limburgish'),
+ 'ln' => array('Lingala'),
+ 'lo' => array('Lao'),
+ 'lt' => array('Lithuanian'),
+ 'lu' => array('Luba-Katanga'),
+ 'lv' => array('Latvian'),
+ 'mg' => array('Malagasy'),
+ 'mh' => array('Marshallese'),
+ 'mi' => array('Maori'),
+ 'mk' => array('Macedonian'),
+ 'ml' => array('Malayalam'),
+ 'mn' => array('Mongolian'),
// 'mo' => array('Moldavian'), // deprecated
- 'mr' => array('Marathi'),
- 'ms' => array('Malay'),
- 'mt' => array('Maltese'),
- 'my' => array('Burmese'),
- 'na' => array('Nauru'),
- 'nb' => array('Norwegian Bokmål'),
- 'nd' => array('North Ndebele'),
- 'ne' => array('Nepali'),
- 'ng' => array('Ndonga'),
- 'nl' => array('Dutch', 'Flemish'),
- 'nn' => array('Norwegian Nynorsk'),
- 'no' => array('Norwegian'),
- 'nr' => array('South Ndebele'),
- 'nv' => array('Navajo', 'Navaho'),
- 'ny' => array('Chichewa', 'Chewa', 'Nyanja'),
- 'oc' => array('Occitan', 'Provençal'), // Occitan (post 1500)
- 'oj' => array('Ojibwa'),
- 'om' => array('Oromo'),
- 'or' => array('Oriya'),
- 'os' => array('Ossetian', 'Ossetic'),
- 'pa' => array('Panjabi', 'Punjabi'),
- 'pi' => array('Pali'),
- 'pl' => array('Polish'),
- 'ps' => array('Pushto', 'Pashto'),
- 'pt' => array('Portuguese'),
- 'qu' => array('Quechua'),
- 'rm' => array('Romansh'),
- 'rn' => array('Rundi'),
- 'ro' => array('Romanian', 'Moldavian', 'Moldovan'),
- 'ru' => array('Russian'),
- 'rw' => array('Kinyarwanda'),
- 'sa' => array('Sanskrit'),
- 'sc' => array('Sardinian'),
- 'sd' => array('Sindhi'),
- 'se' => array('Northern Sami'),
- 'sg' => array('Sango'),
+ 'mr' => array('Marathi'),
+ 'ms' => array('Malay'),
+ 'mt' => array('Maltese'),
+ 'my' => array('Burmese'),
+ 'na' => array('Nauru'),
+ 'nb' => array('Norwegian Bokmål'),
+ 'nd' => array('North Ndebele'),
+ 'ne' => array('Nepali'),
+ 'ng' => array('Ndonga'),
+ 'nl' => array('Dutch', 'Flemish'),
+ 'nn' => array('Norwegian Nynorsk'),
+ 'no' => array('Norwegian'),
+ 'nr' => array('South Ndebele'),
+ 'nv' => array('Navajo', 'Navaho'),
+ 'ny' => array('Chichewa', 'Chewa', 'Nyanja'),
+ 'oc' => array('Occitan', 'Provençal'), // Occitan (post 1500)
+ 'oj' => array('Ojibwa'),
+ 'om' => array('Oromo'),
+ 'or' => array('Oriya'),
+ 'os' => array('Ossetian', 'Ossetic'),
+ 'pa' => array('Panjabi', 'Punjabi'),
+ 'pi' => array('Pali'),
+ 'pl' => array('Polish'),
+ 'ps' => array('Pushto', 'Pashto'),
+ 'pt' => array('Portuguese'),
+ 'qu' => array('Quechua'),
+ 'rm' => array('Romansh'),
+ 'rn' => array('Rundi'),
+ 'ro' => array('Romanian', 'Moldavian', 'Moldovan'),
+ 'ru' => array('Russian'),
+ 'rw' => array('Kinyarwanda'),
+ 'sa' => array('Sanskrit'),
+ 'sc' => array('Sardinian'),
+ 'sd' => array('Sindhi'),
+ 'se' => array('Northern Sami'),
+ 'sg' => array('Sango'),
// 'sh' => array('Serbo-Croatian'), // deprecated
- 'si' => array('Sinhala', 'Sinhalese'),
- 'sk' => array('Slovak'),
- 'sl' => array('Slovenian'),
- 'sm' => array('Samoan'),
- 'sn' => array('Shona'),
- 'so' => array('Somali'),
- 'sq' => array('Albanian'),
- 'sr' => array('Serbian'),
- 'ss' => array('Swati'),
- 'st' => array('Southern Soth'),
- 'su' => array('Sundanese'),
- 'sv' => array('Swedish'),
- 'sw' => array('Swahili'),
- 'ta' => array('Tamil'),
- 'te' => array('Telugu'),
- 'tg' => array('Tajik'),
- 'th' => array('Thai'),
- 'ti' => array('Tigrinya'),
- 'tk' => array('Turkmen'),
- 'tl' => array('Tagalog'),
- 'tn' => array('Tswana'),
- 'to' => array('Tonga'), // Tonga (Tonga Islands)
- 'tr' => array('Turkish'),
- 'ts' => array('Tsonga'),
- 'tt' => array('Tatar'),
- 'tw' => array('Twi'),
- 'ty' => array('Tahitian'),
- 'ug' => array('Uighur', 'Uyghur'),
- 'uk' => array('Ukrainian'),
- 'ur' => array('Urdu'),
- 'uz' => array('Uzbek'),
- 've' => array('Venda'),
- 'vi' => array('Vietnamese'),
- 'vo' => array('Volapük'),
- 'wa' => array('Walloon'),
- 'wo' => array('Wolof'),
- 'xh' => array('Xhosa'),
- 'yi' => array('Yiddish'),
- 'yo' => array('Yoruba'),
- 'za' => array('Zhuang', 'Chuang'),
- 'zh' => array('Chinese'),
- 'zu' => array('Zulu'),
- );
+ 'si' => array('Sinhala', 'Sinhalese'),
+ 'sk' => array('Slovak'),
+ 'sl' => array('Slovenian'),
+ 'sm' => array('Samoan'),
+ 'sn' => array('Shona'),
+ 'so' => array('Somali'),
+ 'sq' => array('Albanian'),
+ 'sr' => array('Serbian'),
+ 'ss' => array('Swati'),
+ 'st' => array('Southern Soth'),
+ 'su' => array('Sundanese'),
+ 'sv' => array('Swedish'),
+ 'sw' => array('Swahili'),
+ 'ta' => array('Tamil'),
+ 'te' => array('Telugu'),
+ 'tg' => array('Tajik'),
+ 'th' => array('Thai'),
+ 'ti' => array('Tigrinya'),
+ 'tk' => array('Turkmen'),
+ 'tl' => array('Tagalog'),
+ 'tn' => array('Tswana'),
+ 'to' => array('Tonga'), // Tonga (Tonga Islands)
+ 'tr' => array('Turkish'),
+ 'ts' => array('Tsonga'),
+ 'tt' => array('Tatar'),
+ 'tw' => array('Twi'),
+ 'ty' => array('Tahitian'),
+ 'ug' => array('Uighur', 'Uyghur'),
+ 'uk' => array('Ukrainian'),
+ 'ur' => array('Urdu'),
+ 'uz' => array('Uzbek'),
+ 've' => array('Venda'),
+ 'vi' => array('Vietnamese'),
+ 'vo' => array('Volapük'),
+ 'wa' => array('Walloon'),
+ 'wo' => array('Wolof'),
+ 'xh' => array('Xhosa'),
+ 'yi' => array('Yiddish'),
+ 'yo' => array('Yoruba'),
+ 'za' => array('Zhuang', 'Chuang'),
+ 'zh' => array('Chinese'),
+ 'zu' => array('Zulu'),
+ );
}
diff --git a/core/DataFiles/SearchEngines.php b/core/DataFiles/SearchEngines.php
index 300a87017d..67b3bb98b4 100644
--- a/core/DataFiles/SearchEngines.php
+++ b/core/DataFiles/SearchEngines.php
@@ -26,7 +26,7 @@
* The main search engine URL has to be at the top of the list for the given
* search Engine. This serves as the master record so additional URLs
* don't have to duplicate all the information, but can override when needed.
- *
+ *
* The URL, "example.com", will match "example.com", "m.example.com",
* "www.example.com", and "search.example.com".
*
@@ -48,949 +48,946 @@
* automatically be replaced by the keyword.
*
* A simple example is:
- * 'www.google.com' => array('Google', 'q', 'search?q={k}'),
+ * 'www.google.com' => array('Google', 'q', 'search?q={k}'),
*
* A more complicated example, with an array of possible variable names, and a custom charset:
- * 'www.baidu.com' => array('Baidu', array('wd', 'word', 'kw'), 's?wd={k}', 'gb2312'),
+ * 'www.baidu.com' => array('Baidu', array('wd', 'word', 'kw'), 's?wd={k}', 'gb2312'),
*
* Another example using a regular expression to parse the path for keywords:
* 'infospace.com' => array('InfoSpace', array('/dir1\/(pattern)\/dir2/'), '/dir1/{k}/dir2/stuff/'),
*/
-if(!isset($GLOBALS['Piwik_SearchEngines'] ))
-{
- $GLOBALS['Piwik_SearchEngines'] = array(
- // 1
- '1.cz' => array('1.cz', 'q', 'index.php?q={k}', 'iso-8859-2'),
-
- // 123people
- 'www.123people.com' => array('123people', array('/s\/([^\/]+)/', 'search_term'), 's/{k}'),
- '123people.{}' => array('123people'),
-
- // 360search
- 'so.360.cn' => array('360search', 'q', 's?q={k}', array('UTF-8', 'gb2312')),
- 'www.so.com' => array('360search', 'q', 's?q={k}', array('UTF-8', 'gb2312')),
-
- // Abacho
- 'www.abacho.de' => array('Abacho', 'q', 'suche?q={k}'),
- 'www.abacho.com' => array('Abacho'),
- 'www.abacho.co.uk' => array('Abacho'),
- 'www.se.abacho.com' => array('Abacho'),
- 'www.tr.abacho.com' => array('Abacho'),
- 'www.abacho.at' => array('Abacho'),
- 'www.abacho.fr' => array('Abacho'),
- 'www.abacho.es' => array('Abacho'),
- 'www.abacho.ch' => array('Abacho'),
- 'www.abacho.it' => array('Abacho'),
-
- // ABCsøk
- 'abcsok.no' => array('ABCsøk', 'q', '?q={k}'),
- 'verden.abcsok.no' => array('ABCsøk'),
-
- // Acoon
- 'www.acoon.de' => array('Acoon', 'begriff', 'cgi-bin/search.exe?begriff={k}'),
-
- // Alexa
- 'alexa.com' => array('Alexa', 'q', 'search?q={k}'),
- 'search.toolbars.alexa.com' => array('Alexa'),
-
- // Alice Adsl
- 'rechercher.aliceadsl.fr' => array('Alice Adsl', 'qs', 'google.pl?qs={k}'),
-
- // Allesklar
- 'www.allesklar.de' => array('Allesklar', 'words', '?words={k}'),
- 'www.allesklar.at' => array('Allesklar'),
- 'www.allesklar.ch' => array('Allesklar'),
-
- // AllTheWeb
- 'www.alltheweb.com' => array('AllTheWeb', 'q', 'search?q={k}'),
-
- // all.by
- 'all.by' => array('All.by', 'query', 'cgi-bin/search.cgi?mode=by&query={k}'),
-
- // Altavista
- 'www.altavista.com' => array('AltaVista', 'q', 'web/results?q={k}'),
- 'search.altavista.com' => array('AltaVista'),
- 'listings.altavista.com' => array('AltaVista'),
- 'altavista.de' => array('AltaVista'),
- 'altavista.fr' => array('AltaVista'),
- '{}.altavista.com' => array('AltaVista'),
- 'be-nl.altavista.com' => array('AltaVista'),
- 'be-fr.altavista.com' => array('AltaVista'),
-
- // Apollo Latvia
- 'apollo.lv/portal/search/' => array('Apollo lv', 'q', '?cof=FORID%3A11&q={k}&search_where=www'),
-
- // APOLLO7
- 'apollo7.de' => array('Apollo7', 'query', 'a7db/index.php?query={k}&de_sharelook=true&de_bing=true&de_witch=true&de_google=true&de_yahoo=true&de_lycos=true'),
-
- // AOL
- 'search.aol.com' => array('AOL', array('query', 'q'), 'aol/search?q={k}'),
- 'search.aol.it' => array('AOL'),
- 'aolsearch.aol.com' => array('AOL'),
- 'www.aolrecherche.aol.fr' => array('AOL'),
- 'www.aolrecherches.aol.fr' => array('AOL'),
- 'www.aolimages.aol.fr' => array('AOL'),
- 'aim.search.aol.com' => array('AOL'),
- 'www.recherche.aol.fr' => array('AOL'),
- 'recherche.aol.fr' => array('AOL'),
- 'find.web.aol.com' => array('AOL'),
- 'recherche.aol.ca' => array('AOL'),
- 'aolsearch.aol.co.uk' => array('AOL'),
- 'search.aol.co.uk' => array('AOL'),
- 'aolrecherche.aol.fr' => array('AOL'),
- 'sucheaol.aol.de' => array('AOL'),
- 'suche.aol.de' => array('AOL'),
- 'o2suche.aol.de' => array('AOL'),
- 'suche.aolsvc.de' => array('AOL'),
- 'aolbusqueda.aol.com.mx' => array('AOL'),
- 'alicesuche.aol.de' => array('AOL'),
- 'alicesuchet.aol.de' => array('AOL'),
- 'suchet2.aol.de' => array('AOL'),
- 'search.hp.my.aol.com.au' => array('AOL'),
- 'search.hp.my.aol.de' => array('AOL'),
- 'search.hp.my.aol.it' => array('AOL'),
- 'search-intl.netscape.com' => array('AOL'),
- 'de.aolsearch.com' => array('AOL', 'q', 'search?q={k}'),
-
- // Aport
- 'sm.aport.ru' => array('Aport', 'r', 'search?r={k}'),
-
- // arama
- 'arama.com' => array('Arama', 'q', 'search.php3?q={k}'),
-
- // Arcor
- 'www.arcor.de' => array('Arcor', 'Keywords', 'content/searchresult.jsp?Keywords={k}'),
-
- // Arianna (Libero.it)
- 'arianna.libero.it' => array('Arianna', 'query', 'search/abin/integrata.cgi?query={k}'),
- 'www.arianna.com' => array('Arianna'),
-
- // Ask (IAC Search & Media)
- 'ask.com' => array('Ask', array('ask', 'q', 'searchfor'), 'web?q={k}'),
- 'web.ask.com' => array('Ask'),
- 'int.ask.com' => array('Ask'),
- 'mws.ask.com' => array('Ask'),
- 'images.ask.com' => array('Ask'),
- 'images.{}.ask.com' => array('Ask'),
- 'ask.reference.com' => array('Ask'),
- 'www.askkids.com' => array('Ask'),
- 'iwon.ask.com' => array('Ask'),
- 'www.ask.co.uk' => array('Ask'),
- '{}.ask.com' => array('Ask'),
- 'www.qbyrd.com' => array('Ask'),
- '{}.qbyrd.com' => array('Ask'),
- 'www.search-results.com' => array('Ask'),
- 'int.search-results.com' => array('Ask'),
- '{}.search-results.com' => array('Ask'),
- '{}.search.ask.com' => array('Ask'),
- 'avira-int.ask.com' => array('Ask'),
-
- // Atlas
- 'searchatlas.centrum.cz' => array('Atlas', 'q', '?q={k}'),
-
- // Austronaut
- 'www2.austronaut.at' => array('Austronaut', 'q'),
- 'www1.austronaut.at' => array('Austronaut'),
-
- // Babylon (Enhanced by Google)
- 'search.babylon.com' => array('Babylon', array('q', '/\/web\/(.*)/'), '?q={k}'),
- 'searchassist.babylon.com' => array('Babylon'),
-
- // Baidu
- 'www.baidu.com' => array('Baidu', array('wd', 'word', 'kw'), 's?wd={k}', array('UTF-8', 'gb2312')),
- 'www1.baidu.com' => array('Baidu'),
- 'zhidao.baidu.com' => array('Baidu'),
- 'tieba.baidu.com' => array('Baidu'),
- 'news.baidu.com' => array('Baidu'),
- 'web.gougou.com' => array('Baidu', 'search', 'search?search={k}'), // uses baidu search
-
- // Biglobe
- 'cgi.search.biglobe.ne.jp' => array('Biglobe', 'q', 'cgi-bin/search-st?q={k}'),
-
- // Bing
- 'bing.com' => array('Bing', array('q', 'Q'), 'search?q={k}'),
- '{}.bing.com' => array('Bing'),
- 'msnbc.msn.com' => array('Bing'),
- 'dizionario.it.msn.com' => array('Bing'),
-
- // Bing Cache
- 'cc.bingj.com' => array('Bing'),
-
- // Bing Images
- 'bing.com/images/search' => array('Bing Images', array('q', 'Q'), '?q={k}'),
- '{}.bing.com/images/search' => array('Bing Images'),
-
- // blekko
- 'blekko.com' => array('blekko', array('q', '/\/ws\/(.*)/'), 'ws/{k}'),
-
- // Blogdigger
- 'www.blogdigger.com' => array('Blogdigger', 'q'),
-
- // Blogpulse
- 'www.blogpulse.com' => array('Blogpulse', 'query', 'search?query={k}'),
-
- // Bluewin
- 'search.bluewin.ch' => array('Bluewin', 'searchTerm', '?searchTerm={k}'),
-
- // canoe.ca
- 'web.canoe.ca' => array('Canoe.ca', 'q', 'search?q={k}'),
-
- // Centrum
- 'search.centrum.cz' => array('Centrum', 'q', '?q={k}'),
- 'morfeo.centrum.cz' => array('Centrum'),
-
- // Charter
- 'www.charter.net' => array('Charter', 'q', 'search/index.php?q={k}'),
-
- // Clix (Enhanced by Google)
- 'pesquisa.clix.pt' => array('Clix', 'question', 'resultado.html?in=Mundial&question={k}'),
+if (!isset($GLOBALS['Piwik_SearchEngines'])) {
+ $GLOBALS['Piwik_SearchEngines'] = array(
+ // 1
+ '1.cz' => array('1.cz', 'q', 'index.php?q={k}', 'iso-8859-2'),
+
+ // 123people
+ 'www.123people.com' => array('123people', array('/s\/([^\/]+)/', 'search_term'), 's/{k}'),
+ '123people.{}' => array('123people'),
+
+ // 360search
+ 'so.360.cn' => array('360search', 'q', 's?q={k}', array('UTF-8', 'gb2312')),
+ 'www.so.com' => array('360search', 'q', 's?q={k}', array('UTF-8', 'gb2312')),
+
+ // Abacho
+ 'www.abacho.de' => array('Abacho', 'q', 'suche?q={k}'),
+ 'www.abacho.com' => array('Abacho'),
+ 'www.abacho.co.uk' => array('Abacho'),
+ 'www.se.abacho.com' => array('Abacho'),
+ 'www.tr.abacho.com' => array('Abacho'),
+ 'www.abacho.at' => array('Abacho'),
+ 'www.abacho.fr' => array('Abacho'),
+ 'www.abacho.es' => array('Abacho'),
+ 'www.abacho.ch' => array('Abacho'),
+ 'www.abacho.it' => array('Abacho'),
+
+ // ABCsøk
+ 'abcsok.no' => array('ABCsøk', 'q', '?q={k}'),
+ 'verden.abcsok.no' => array('ABCsøk'),
+
+ // Acoon
+ 'www.acoon.de' => array('Acoon', 'begriff', 'cgi-bin/search.exe?begriff={k}'),
+
+ // Alexa
+ 'alexa.com' => array('Alexa', 'q', 'search?q={k}'),
+ 'search.toolbars.alexa.com' => array('Alexa'),
+
+ // Alice Adsl
+ 'rechercher.aliceadsl.fr' => array('Alice Adsl', 'qs', 'google.pl?qs={k}'),
+
+ // Allesklar
+ 'www.allesklar.de' => array('Allesklar', 'words', '?words={k}'),
+ 'www.allesklar.at' => array('Allesklar'),
+ 'www.allesklar.ch' => array('Allesklar'),
+
+ // AllTheWeb
+ 'www.alltheweb.com' => array('AllTheWeb', 'q', 'search?q={k}'),
+
+ // all.by
+ 'all.by' => array('All.by', 'query', 'cgi-bin/search.cgi?mode=by&query={k}'),
+
+ // Altavista
+ 'www.altavista.com' => array('AltaVista', 'q', 'web/results?q={k}'),
+ 'search.altavista.com' => array('AltaVista'),
+ 'listings.altavista.com' => array('AltaVista'),
+ 'altavista.de' => array('AltaVista'),
+ 'altavista.fr' => array('AltaVista'),
+ '{}.altavista.com' => array('AltaVista'),
+ 'be-nl.altavista.com' => array('AltaVista'),
+ 'be-fr.altavista.com' => array('AltaVista'),
+
+ // Apollo Latvia
+ 'apollo.lv/portal/search/' => array('Apollo lv', 'q', '?cof=FORID%3A11&q={k}&search_where=www'),
+
+ // APOLLO7
+ 'apollo7.de' => array('Apollo7', 'query', 'a7db/index.php?query={k}&de_sharelook=true&de_bing=true&de_witch=true&de_google=true&de_yahoo=true&de_lycos=true'),
+
+ // AOL
+ 'search.aol.com' => array('AOL', array('query', 'q'), 'aol/search?q={k}'),
+ 'search.aol.it' => array('AOL'),
+ 'aolsearch.aol.com' => array('AOL'),
+ 'www.aolrecherche.aol.fr' => array('AOL'),
+ 'www.aolrecherches.aol.fr' => array('AOL'),
+ 'www.aolimages.aol.fr' => array('AOL'),
+ 'aim.search.aol.com' => array('AOL'),
+ 'www.recherche.aol.fr' => array('AOL'),
+ 'recherche.aol.fr' => array('AOL'),
+ 'find.web.aol.com' => array('AOL'),
+ 'recherche.aol.ca' => array('AOL'),
+ 'aolsearch.aol.co.uk' => array('AOL'),
+ 'search.aol.co.uk' => array('AOL'),
+ 'aolrecherche.aol.fr' => array('AOL'),
+ 'sucheaol.aol.de' => array('AOL'),
+ 'suche.aol.de' => array('AOL'),
+ 'o2suche.aol.de' => array('AOL'),
+ 'suche.aolsvc.de' => array('AOL'),
+ 'aolbusqueda.aol.com.mx' => array('AOL'),
+ 'alicesuche.aol.de' => array('AOL'),
+ 'alicesuchet.aol.de' => array('AOL'),
+ 'suchet2.aol.de' => array('AOL'),
+ 'search.hp.my.aol.com.au' => array('AOL'),
+ 'search.hp.my.aol.de' => array('AOL'),
+ 'search.hp.my.aol.it' => array('AOL'),
+ 'search-intl.netscape.com' => array('AOL'),
+ 'de.aolsearch.com' => array('AOL', 'q', 'search?q={k}'),
+
+ // Aport
+ 'sm.aport.ru' => array('Aport', 'r', 'search?r={k}'),
+
+ // arama
+ 'arama.com' => array('Arama', 'q', 'search.php3?q={k}'),
+
+ // Arcor
+ 'www.arcor.de' => array('Arcor', 'Keywords', 'content/searchresult.jsp?Keywords={k}'),
+
+ // Arianna (Libero.it)
+ 'arianna.libero.it' => array('Arianna', 'query', 'search/abin/integrata.cgi?query={k}'),
+ 'www.arianna.com' => array('Arianna'),
+
+ // Ask (IAC Search & Media)
+ 'ask.com' => array('Ask', array('ask', 'q', 'searchfor'), 'web?q={k}'),
+ 'web.ask.com' => array('Ask'),
+ 'int.ask.com' => array('Ask'),
+ 'mws.ask.com' => array('Ask'),
+ 'images.ask.com' => array('Ask'),
+ 'images.{}.ask.com' => array('Ask'),
+ 'ask.reference.com' => array('Ask'),
+ 'www.askkids.com' => array('Ask'),
+ 'iwon.ask.com' => array('Ask'),
+ 'www.ask.co.uk' => array('Ask'),
+ '{}.ask.com' => array('Ask'),
+ 'www.qbyrd.com' => array('Ask'),
+ '{}.qbyrd.com' => array('Ask'),
+ 'www.search-results.com' => array('Ask'),
+ 'int.search-results.com' => array('Ask'),
+ '{}.search-results.com' => array('Ask'),
+ '{}.search.ask.com' => array('Ask'),
+ 'avira-int.ask.com' => array('Ask'),
+
+ // Atlas
+ 'searchatlas.centrum.cz' => array('Atlas', 'q', '?q={k}'),
+
+ // Austronaut
+ 'www2.austronaut.at' => array('Austronaut', 'q'),
+ 'www1.austronaut.at' => array('Austronaut'),
+
+ // Babylon (Enhanced by Google)
+ 'search.babylon.com' => array('Babylon', array('q', '/\/web\/(.*)/'), '?q={k}'),
+ 'searchassist.babylon.com' => array('Babylon'),
+
+ // Baidu
+ 'www.baidu.com' => array('Baidu', array('wd', 'word', 'kw'), 's?wd={k}', array('UTF-8', 'gb2312')),
+ 'www1.baidu.com' => array('Baidu'),
+ 'zhidao.baidu.com' => array('Baidu'),
+ 'tieba.baidu.com' => array('Baidu'),
+ 'news.baidu.com' => array('Baidu'),
+ 'web.gougou.com' => array('Baidu', 'search', 'search?search={k}'), // uses baidu search
+
+ // Biglobe
+ 'cgi.search.biglobe.ne.jp' => array('Biglobe', 'q', 'cgi-bin/search-st?q={k}'),
+
+ // Bing
+ 'bing.com' => array('Bing', array('q', 'Q'), 'search?q={k}'),
+ '{}.bing.com' => array('Bing'),
+ 'msnbc.msn.com' => array('Bing'),
+ 'dizionario.it.msn.com' => array('Bing'),
+
+ // Bing Cache
+ 'cc.bingj.com' => array('Bing'),
+
+ // Bing Images
+ 'bing.com/images/search' => array('Bing Images', array('q', 'Q'), '?q={k}'),
+ '{}.bing.com/images/search' => array('Bing Images'),
+
+ // blekko
+ 'blekko.com' => array('blekko', array('q', '/\/ws\/(.*)/'), 'ws/{k}'),
+
+ // Blogdigger
+ 'www.blogdigger.com' => array('Blogdigger', 'q'),
+
+ // Blogpulse
+ 'www.blogpulse.com' => array('Blogpulse', 'query', 'search?query={k}'),
+
+ // Bluewin
+ 'search.bluewin.ch' => array('Bluewin', 'searchTerm', '?searchTerm={k}'),
+
+ // canoe.ca
+ 'web.canoe.ca' => array('Canoe.ca', 'q', 'search?q={k}'),
+
+ // Centrum
+ 'search.centrum.cz' => array('Centrum', 'q', '?q={k}'),
+ 'morfeo.centrum.cz' => array('Centrum'),
+
+ // Charter
+ 'www.charter.net' => array('Charter', 'q', 'search/index.php?q={k}'),
+
+ // Clix (Enhanced by Google)
+ 'pesquisa.clix.pt' => array('Clix', 'question', 'resultado.html?in=Mundial&question={k}'),
- // Conduit
- 'search.conduit.com' => array('Conduit.com', 'q', 'Results.aspx?q={k}'),
+ // Conduit
+ 'search.conduit.com' => array('Conduit.com', 'q', 'Results.aspx?q={k}'),
- // Comcast
- 'search.comcast.net' => array('Comcast', 'q', '?q={k}'),
+ // Comcast
+ 'search.comcast.net' => array('Comcast', 'q', '?q={k}'),
- // Crawler
- 'www.crawler.com' => array('Crawler', 'q', 'search/results1.aspx?q={k}'),
+ // Crawler
+ 'www.crawler.com' => array('Crawler', 'q', 'search/results1.aspx?q={k}'),
- // Compuserve
- 'websearch.cs.com' => array('Compuserve.com (Enhanced by Google)', 'query', 'cs/search?query={k}'),
+ // Compuserve
+ 'websearch.cs.com' => array('Compuserve.com (Enhanced by Google)', 'query', 'cs/search?query={k}'),
- // Cuil
- 'www.cuil.com' => array('Cuil', 'q', 'search?q={k}'),
+ // Cuil
+ 'www.cuil.com' => array('Cuil', 'q', 'search?q={k}'),
- // Daemon search
- 'daemon-search.com' => array('Daemon search', 'q', 'explore/web?q={k}'),
- 'my.daemon-search.com' => array('Daemon search'),
+ // Daemon search
+ 'daemon-search.com' => array('Daemon search', 'q', 'explore/web?q={k}'),
+ 'my.daemon-search.com' => array('Daemon search'),
- // DasOertliche
- 'www.dasoertliche.de' => array('DasOertliche', 'kw'),
+ // DasOertliche
+ 'www.dasoertliche.de' => array('DasOertliche', 'kw'),
- // DasTelefonbuch
- 'www1.dastelefonbuch.de' => array('DasTelefonbuch', 'kw'),
+ // DasTelefonbuch
+ 'www1.dastelefonbuch.de' => array('DasTelefonbuch', 'kw'),
- // Daum
- 'search.daum.net' => array('Daum', 'q', 'search?q={k}', 'EUC-KR'),
+ // Daum
+ 'search.daum.net' => array('Daum', 'q', 'search?q={k}', 'EUC-KR'),
- // Delfi Latvia
- 'smart.delfi.lv' => array('Delfi lv', 'q', 'find?q={k}'),
+ // Delfi Latvia
+ 'smart.delfi.lv' => array('Delfi lv', 'q', 'find?q={k}'),
- // Delfi
- 'otsing.delfi.ee' => array('Delfi EE', 'q', 'find?q={k}'),
+ // Delfi
+ 'otsing.delfi.ee' => array('Delfi EE', 'q', 'find?q={k}'),
- // Digg
- 'digg.com' => array('Digg', 's', 'search?s={k}'),
+ // Digg
+ 'digg.com' => array('Digg', 's', 'search?s={k}'),
- // dir.com
- 'fr.dir.com' => array('dir.com', 'req'),
+ // dir.com
+ 'fr.dir.com' => array('dir.com', 'req'),
- // dmoz
- 'dmoz.org' => array('dmoz', 'search'),
- 'editors.dmoz.org' => array('dmoz'),
+ // dmoz
+ 'dmoz.org' => array('dmoz', 'search'),
+ 'editors.dmoz.org' => array('dmoz'),
- // DuckDuckGo
- 'duckduckgo.com' => array('DuckDuckGo', 'q', '?q={k}'),
+ // DuckDuckGo
+ 'duckduckgo.com' => array('DuckDuckGo', 'q', '?q={k}'),
- // earthlink
- 'search.earthlink.net' => array('Earthlink', 'q', 'search?q={k}'),
+ // earthlink
+ 'search.earthlink.net' => array('Earthlink', 'q', 'search?q={k}'),
- // Ecosia (powered by Bing)
- 'ecosia.org' => array('Ecosia', 'q', 'search.php?q={k}'),
+ // Ecosia (powered by Bing)
+ 'ecosia.org' => array('Ecosia', 'q', 'search.php?q={k}'),
- // Eniro
- 'www.eniro.se' => array('Eniro', array('q', 'search_word'), 'query?q={k}'),
+ // Eniro
+ 'www.eniro.se' => array('Eniro', array('q', 'search_word'), 'query?q={k}'),
- // Eurip
- 'www.eurip.com' => array('Eurip', 'q', 'search/?q={k}'),
+ // Eurip
+ 'www.eurip.com' => array('Eurip', 'q', 'search/?q={k}'),
- // Euroseek
- 'www.euroseek.com' => array('Euroseek', 'string', 'system/search.cgi?string={k}'),
+ // Euroseek
+ 'www.euroseek.com' => array('Euroseek', 'string', 'system/search.cgi?string={k}'),
- // Everyclick
- 'www.everyclick.com' => array('Everyclick', 'keyword'),
+ // Everyclick
+ 'www.everyclick.com' => array('Everyclick', 'keyword'),
- // Excite
- 'search.excite.it' => array('Excite', 'q', 'web/?q={k}'),
- 'search.excite.fr' => array('Excite'),
- 'search.excite.de' => array('Excite'),
- 'search.excite.co.uk' => array('Excite'),
- 'search.excite.es' => array('Excite'),
- 'search.excite.nl' => array('Excite'),
- 'msxml.excite.com' => array('Excite', '/\/[^\/]+\/ws\/results\/[^\/]+\/([^\/]+)/'),
- 'www.excite.co.jp' => array('Excite', 'search', 'search.gw?search={k}', 'SHIFT_JIS'),
+ // Excite
+ 'search.excite.it' => array('Excite', 'q', 'web/?q={k}'),
+ 'search.excite.fr' => array('Excite'),
+ 'search.excite.de' => array('Excite'),
+ 'search.excite.co.uk' => array('Excite'),
+ 'search.excite.es' => array('Excite'),
+ 'search.excite.nl' => array('Excite'),
+ 'msxml.excite.com' => array('Excite', '/\/[^\/]+\/ws\/results\/[^\/]+\/([^\/]+)/'),
+ 'www.excite.co.jp' => array('Excite', 'search', 'search.gw?search={k}', 'SHIFT_JIS'),
- // Exalead
- 'www.exalead.fr' => array('Exalead', 'q', 'search/results?q={k}'),
- 'www.exalead.com' => array('Exalead'),
+ // Exalead
+ 'www.exalead.fr' => array('Exalead', 'q', 'search/results?q={k}'),
+ 'www.exalead.com' => array('Exalead'),
- // eo
- 'eo.st' => array('eo', 'x_query', 'cgi-bin/eolost.cgi?x_query={k}'),
+ // eo
+ 'eo.st' => array('eo', 'x_query', 'cgi-bin/eolost.cgi?x_query={k}'),
- // Facebook
- 'www.facebook.com' => array('Facebook', 'q', 'search/?q={k}'),
-
- // Fast Browser Search
- 'www.fastbrowsersearch.com' => array('Fast Browser Search', 'q', 'results/results.aspx?q={k}'),
-
- // Francite
- 'recherche.francite.com' => array('Francite', 'name'),
-
- // Fireball
- 'www.fireball.de' => array('Fireball', 'q', 'ajax.asp?q={k}'),
-
- // Firstfind
- 'www.firstsfind.com' => array('Firstsfind', 'qry'),
-
- // Fixsuche
- 'www.fixsuche.de' => array('Fixsuche', 'q'),
-
- // Flix
- 'www.flix.de' => array('Flix.de', 'keyword'),
-
- // Forestle
- 'forestle.org' => array('Forestle', 'q', 'search.php?q={k}'),
- '{}.forestle.org' => array('Forestle'),
- 'forestle.mobi' => array('Forestle'),
-
- // Free
- 'search.free.fr' => array('Free', 'q'),
- 'search1-2.free.fr' => array('Free'),
- 'search1-1.free.fr' => array('Free'),
-
- // Freecause
- 'search.freecause.com' => array('FreeCause', 'p', '?p={k}'),
-
- // Freenet
- 'suche.freenet.de' => array('Freenet', array('query', 'Keywords'), 'suche/?query={k}'),
-
- // FriendFeed
- 'friendfeed.com' => array('FriendFeed', 'q', 'search?q={k}'),
-
- // GAIS
- 'gais.cs.ccu.edu.tw' => array('GAIS', 'q', 'search.php?q={k}'),
-
- // Geona
- 'geona.net' => array('Geona', 'q', 'search?q={k}'),
-
- // Gigablast
- 'www.gigablast.com' => array('Gigablast', 'q', 'search?q={k}'),
- 'dir.gigablast.com' => array('Gigablast (Directory)', 'q'),
-
- // Gnadenmeer
- 'www.gnadenmeer.de' => array('Gnadenmeer', 'keyword'),
-
- // Gomeo
- 'www.gomeo.com' => array('Gomeo', array('Keywords', '/\/search\/([^\/]+)/'), '/search/{k}'),
-
- // goo
- 'search.goo.ne.jp' => array('goo', 'MT', 'web.jsp?MT={k}'),
- 'ocnsearch.goo.ne.jp' => array('goo'),
-
- // Google
- 'google.com' => array('Google', 'q', 'search?q={k}'),
- 'google.{}' => array('Google'),
- 'www2.google.com' => array('Google'),
- 'ipv6.google.com' => array('Google'),
- 'go.google.com' => array('Google'),
-
- // Google vs typo squatters
- 'wwwgoogle.com' => array('Google'),
- 'wwwgoogle.{}' => array('Google'),
- 'gogole.com' => array('Google'),
- 'gogole.{}' => array('Google'),
- 'gppgle.com' => array('Google'),
- 'gppgle.{}' => array('Google'),
- 'googel.com' => array('Google'),
- 'googel.{}' => array('Google'),
-
- // Powered by Google
- 'search.avg.com' => array('Google'),
- 'isearch.avg.com' => array('Google'),
- 'www.cnn.com' => array('Google', 'query'),
- 'darkoogle.com' => array('Google'),
- 'search.darkoogle.com' => array('Google'),
- 'search.foxtab.com' => array('Google'),
- 'www.gooofullsearch.com' => array('Google', 'Keywords'),
- 'search.hiyo.com' => array('Google'),
- 'search.incredimail.com' => array('Google'),
- 'search1.incredimail.com' => array('Google'),
- 'search2.incredimail.com' => array('Google'),
- 'search3.incredimail.com' => array('Google'),
- 'search4.incredimail.com' => array('Google'),
- 'search.sweetim.com' => array('Google'),
- 'www.fastweb.it' => array('Google'),
- 'search.juno.com' => array('Google', 'query'),
- 'find.tdc.dk' => array('Google'),
- 'it.luna.tv' => array('Google'),
- 'searchresults.verizon.com' => array('Google'),
- 'search.walla.co.il' => array('Google'),
- 'search.alot.com' => array('Google'),
- 'suche.gmx.net' => array('Google', 'q', 'web?q={k}'),
- 'search.incredibar.com' => array('Google', 'q', 'search.php?q={k}'),
- 'www.delta-search.com' => array('Google', 'q'),
- 'search.1und1.de' => array('Google', 'q', 'web?q={k}'),
- 'search.zonealarm.com' => array('Google'),
- 'start.lenovo.com' => array('Google', 'q', 'search/index.php?q={k}'),
-
- // Google Earth
- // - 2010-09-13: are these redirects now?
- 'www.googleearth.de' => array('Google'),
- 'www.googleearth.fr' => array('Google'),
-
- // Google Cache
- 'webcache.googleusercontent.com'=> array('Google', '/\/search\?q=cache:[A-Za-z0-9]+:[^+]+([^&]+)/', 'search?q={k}'),
-
- // Google SSL
- 'encrypted.google.com' => array('Google SSL', 'q', 'search?q={k}'),
-
- // Google Blogsearch
- 'blogsearch.google.com' => array('Google Blogsearch', 'q', 'blogsearch?q={k}'),
- 'blogsearch.google.{}' => array('Google Blogsearch'),
-
- // Google Custom Search
- 'google.com/cse' => array('Google Custom Search', array('q', 'query')),
- 'google.{}/cse' => array('Google Custom Search'),
- 'google.com/custom' => array('Google Custom Search'),
- 'google.{}/custom' => array('Google Custom Search'),
-
- // Google Translation
- 'translate.google.com' => array('Google Translations', 'q'),
-
- // Google Images
- 'images.google.com' => array('Google Images', 'q', 'images?q={k}'),
- 'images.google.{}' => array('Google Images'),
-
- // Google Maps
- 'maps.google.com' => array('Google Maps', 'q', 'maps?q={k}'),
- 'maps.google.{}' => array('Google Maps'),
-
- // Google News
- 'news.google.com' => array('Google News', 'q'),
- 'news.google.{}' => array('Google News'),
-
- // Google Shopping
- 'google.com/products' => array('Google Shopping', 'q', '?q={k}&tbm=shop'),
- 'google.{}/products' => array('Google Shopping'),
-
- // Google syndicated search
- 'googlesyndicatedsearch.com'=> array('Google syndicated search', 'q'),
-
- // Google Video
- 'video.google.com' => array('Google Video', 'q', 'search?q={k}&tbm=vid'),
-
- // Google Wireless Transcoder
- // - does not appear to execute JavaScript
+ // Facebook
+ 'www.facebook.com' => array('Facebook', 'q', 'search/?q={k}'),
+
+ // Fast Browser Search
+ 'www.fastbrowsersearch.com' => array('Fast Browser Search', 'q', 'results/results.aspx?q={k}'),
+
+ // Francite
+ 'recherche.francite.com' => array('Francite', 'name'),
+
+ // Fireball
+ 'www.fireball.de' => array('Fireball', 'q', 'ajax.asp?q={k}'),
+
+ // Firstfind
+ 'www.firstsfind.com' => array('Firstsfind', 'qry'),
+
+ // Fixsuche
+ 'www.fixsuche.de' => array('Fixsuche', 'q'),
+
+ // Flix
+ 'www.flix.de' => array('Flix.de', 'keyword'),
+
+ // Forestle
+ 'forestle.org' => array('Forestle', 'q', 'search.php?q={k}'),
+ '{}.forestle.org' => array('Forestle'),
+ 'forestle.mobi' => array('Forestle'),
+
+ // Free
+ 'search.free.fr' => array('Free', 'q'),
+ 'search1-2.free.fr' => array('Free'),
+ 'search1-1.free.fr' => array('Free'),
+
+ // Freecause
+ 'search.freecause.com' => array('FreeCause', 'p', '?p={k}'),
+
+ // Freenet
+ 'suche.freenet.de' => array('Freenet', array('query', 'Keywords'), 'suche/?query={k}'),
+
+ // FriendFeed
+ 'friendfeed.com' => array('FriendFeed', 'q', 'search?q={k}'),
+
+ // GAIS
+ 'gais.cs.ccu.edu.tw' => array('GAIS', 'q', 'search.php?q={k}'),
+
+ // Geona
+ 'geona.net' => array('Geona', 'q', 'search?q={k}'),
+
+ // Gigablast
+ 'www.gigablast.com' => array('Gigablast', 'q', 'search?q={k}'),
+ 'dir.gigablast.com' => array('Gigablast (Directory)', 'q'),
+
+ // Gnadenmeer
+ 'www.gnadenmeer.de' => array('Gnadenmeer', 'keyword'),
+
+ // Gomeo
+ 'www.gomeo.com' => array('Gomeo', array('Keywords', '/\/search\/([^\/]+)/'), '/search/{k}'),
+
+ // goo
+ 'search.goo.ne.jp' => array('goo', 'MT', 'web.jsp?MT={k}'),
+ 'ocnsearch.goo.ne.jp' => array('goo'),
+
+ // Google
+ 'google.com' => array('Google', 'q', 'search?q={k}'),
+ 'google.{}' => array('Google'),
+ 'www2.google.com' => array('Google'),
+ 'ipv6.google.com' => array('Google'),
+ 'go.google.com' => array('Google'),
+
+ // Google vs typo squatters
+ 'wwwgoogle.com' => array('Google'),
+ 'wwwgoogle.{}' => array('Google'),
+ 'gogole.com' => array('Google'),
+ 'gogole.{}' => array('Google'),
+ 'gppgle.com' => array('Google'),
+ 'gppgle.{}' => array('Google'),
+ 'googel.com' => array('Google'),
+ 'googel.{}' => array('Google'),
+
+ // Powered by Google
+ 'search.avg.com' => array('Google'),
+ 'isearch.avg.com' => array('Google'),
+ 'www.cnn.com' => array('Google', 'query'),
+ 'darkoogle.com' => array('Google'),
+ 'search.darkoogle.com' => array('Google'),
+ 'search.foxtab.com' => array('Google'),
+ 'www.gooofullsearch.com' => array('Google', 'Keywords'),
+ 'search.hiyo.com' => array('Google'),
+ 'search.incredimail.com' => array('Google'),
+ 'search1.incredimail.com' => array('Google'),
+ 'search2.incredimail.com' => array('Google'),
+ 'search3.incredimail.com' => array('Google'),
+ 'search4.incredimail.com' => array('Google'),
+ 'search.sweetim.com' => array('Google'),
+ 'www.fastweb.it' => array('Google'),
+ 'search.juno.com' => array('Google', 'query'),
+ 'find.tdc.dk' => array('Google'),
+ 'it.luna.tv' => array('Google'),
+ 'searchresults.verizon.com' => array('Google'),
+ 'search.walla.co.il' => array('Google'),
+ 'search.alot.com' => array('Google'),
+ 'suche.gmx.net' => array('Google', 'q', 'web?q={k}'),
+ 'search.incredibar.com' => array('Google', 'q', 'search.php?q={k}'),
+ 'www.delta-search.com' => array('Google', 'q'),
+ 'search.1und1.de' => array('Google', 'q', 'web?q={k}'),
+ 'search.zonealarm.com' => array('Google'),
+ 'start.lenovo.com' => array('Google', 'q', 'search/index.php?q={k}'),
+
+ // Google Earth
+ // - 2010-09-13: are these redirects now?
+ 'www.googleearth.de' => array('Google'),
+ 'www.googleearth.fr' => array('Google'),
+
+ // Google Cache
+ 'webcache.googleusercontent.com' => array('Google', '/\/search\?q=cache:[A-Za-z0-9]+:[^+]+([^&]+)/', 'search?q={k}'),
+
+ // Google SSL
+ 'encrypted.google.com' => array('Google SSL', 'q', 'search?q={k}'),
+
+ // Google Blogsearch
+ 'blogsearch.google.com' => array('Google Blogsearch', 'q', 'blogsearch?q={k}'),
+ 'blogsearch.google.{}' => array('Google Blogsearch'),
+
+ // Google Custom Search
+ 'google.com/cse' => array('Google Custom Search', array('q', 'query')),
+ 'google.{}/cse' => array('Google Custom Search'),
+ 'google.com/custom' => array('Google Custom Search'),
+ 'google.{}/custom' => array('Google Custom Search'),
+
+ // Google Translation
+ 'translate.google.com' => array('Google Translations', 'q'),
+
+ // Google Images
+ 'images.google.com' => array('Google Images', 'q', 'images?q={k}'),
+ 'images.google.{}' => array('Google Images'),
+
+ // Google Maps
+ 'maps.google.com' => array('Google Maps', 'q', 'maps?q={k}'),
+ 'maps.google.{}' => array('Google Maps'),
+
+ // Google News
+ 'news.google.com' => array('Google News', 'q'),
+ 'news.google.{}' => array('Google News'),
+
+ // Google Shopping
+ 'google.com/products' => array('Google Shopping', 'q', '?q={k}&tbm=shop'),
+ 'google.{}/products' => array('Google Shopping'),
+
+ // Google syndicated search
+ 'googlesyndicatedsearch.com' => array('Google syndicated search', 'q'),
+
+ // Google Video
+ 'video.google.com' => array('Google Video', 'q', 'search?q={k}&tbm=vid'),
+
+ // Google Wireless Transcoder
+ // - does not appear to execute JavaScript
// 'google.com/gwt/n' => array('Google Wireless Transcoder'),
- // Goyellow.de
- 'www.goyellow.de' => array('GoYellow.de', 'MDN'),
-
- // Gule Sider
- 'www.gulesider.no' => array('Gule Sider', 'q'),
-
- // HighBeam
- 'www.highbeam.com' => array('HighBeam', 'q', 'Search.aspx?q={k}'),
-
- // Hit-Parade
- 'req.hit-parade.com' => array('Hit-Parade', 'p7', 'general/recherche.asp?p7={k}'),
- 'class.hit-parade.com' => array('Hit-Parade'),
- 'www.hit-parade.com' => array('Hit-Parade'),
-
- // Holmes.ge
- 'holmes.ge' => array('Holmes', 'q', 'search.htm?q={k}'),
-
- // Hooseek.com
- 'www.hooseek.com' => array('Hooseek', 'recherche', 'web?recherche={k}'),
-
- // Hotbot
- 'www.hotbot.com' => array('Hotbot', 'query'),
-
- // Icerocket
- 'blogs.icerocket.com' => array('Icerocket', 'q', 'search?q={k}'),
-
- // ICQ
- 'www.icq.com' => array('ICQ', 'q', 'search/results.php?q={k}'),
- 'search.icq.com' => array('ICQ'),
-
- // Ilse
- 'www.ilse.nl' => array('Ilse NL', 'search_for', '?search_for={k}'),
-
- // Inbox.com
- 'www2.inbox.com' => array('Inbox', 'q', 'search/results1.aspx?q={k}'),
-
- // InfoSpace (and related web properties)
- 'infospace.com' => array('InfoSpace', 'q', '/search/web?q={k}'),
- 'dogpile.com' => array('InfoSpace'),
- 'tattoodle.com' => array('InfoSpace'),
- 'metacrawler.com' => array('InfoSpace'),
- 'webfetch.com' => array('InfoSpace'),
- 'webcrawler.com' => array('InfoSpace'),
- 'search.kiwee.com' => array('InfoSpace'),
-
- // old infospace system
- 'wsdsold.infospace.com' => array('InfoSpace', '/\/[^\/]+\/ws\/results\/[^\/]+\/([^\/]+)/', 'pemonitorhosted/ws/results/Web/{k}/1/417/TopNavigation/Source/'),
-
- // Powered by InfoSpace
- 'isearch.babylon.com' => array('InfoSpace', 'q'),
- 'start.facemoods.com' => array('InfoSpace', 's'),
- 'search.magentic.com' => array('InfoSpace', 'q'),
- 'search.searchcompletion.com'=> array('InfoSpace', 'q'),
-
- /*
- * Other InfoSpace powered metasearches are handled in Piwik_Common::extractSearchEngineInformationFromUrl()
- *
- * This includes sites such as:
- * - search.nation.com
- * - ws.copernic.com
- * - result.iminent.com
- */
-
- // Interia
- 'www.google.interia.pl' => array('Interia', 'q', 'szukaj?q={k}'),
-
- // I-play
- 'start.iplay.com' => array('I-play', 'q', 'searchresults.aspx?q={k}'),
-
- // Ixquick
- 'ixquick.com' => array('Ixquick', 'query'),
- 'www.eu.ixquick.com' => array('Ixquick'),
- 'ixquick.de' => array('Ixquick'),
- 'www.ixquick.de' => array('Ixquick'),
- 'us.ixquick.com' => array('Ixquick'),
- 's1.us.ixquick.com' => array('Ixquick'),
- 's2.us.ixquick.com' => array('Ixquick'),
- 's3.us.ixquick.com' => array('Ixquick'),
- 's4.us.ixquick.com' => array('Ixquick'),
- 's5.us.ixquick.com' => array('Ixquick'),
- 'eu.ixquick.com' => array('Ixquick'),
- 's8-eu.ixquick.com' => array('Ixquick'),
- 's1-eu.ixquick.de' => array('Ixquick'),
-
- // Jyxo
- 'jyxo.1188.cz' => array('Jyxo', 'q', 's?q={k}'),
-
- // Jungle Spider
- 'www.jungle-spider.de' => array('Jungle Spider', 'q'),
-
- // Jungle key
- 'junglekey.com' => array('Jungle Key', 'query', 'search.php?query={k}&type=web&lang=en'),
- 'junglekey.fr' => array('Jungle Key'),
+ // Goyellow.de
+ 'www.goyellow.de' => array('GoYellow.de', 'MDN'),
+
+ // Gule Sider
+ 'www.gulesider.no' => array('Gule Sider', 'q'),
+
+ // HighBeam
+ 'www.highbeam.com' => array('HighBeam', 'q', 'Search.aspx?q={k}'),
+
+ // Hit-Parade
+ 'req.hit-parade.com' => array('Hit-Parade', 'p7', 'general/recherche.asp?p7={k}'),
+ 'class.hit-parade.com' => array('Hit-Parade'),
+ 'www.hit-parade.com' => array('Hit-Parade'),
+
+ // Holmes.ge
+ 'holmes.ge' => array('Holmes', 'q', 'search.htm?q={k}'),
+
+ // Hooseek.com
+ 'www.hooseek.com' => array('Hooseek', 'recherche', 'web?recherche={k}'),
+
+ // Hotbot
+ 'www.hotbot.com' => array('Hotbot', 'query'),
+
+ // Icerocket
+ 'blogs.icerocket.com' => array('Icerocket', 'q', 'search?q={k}'),
+
+ // ICQ
+ 'www.icq.com' => array('ICQ', 'q', 'search/results.php?q={k}'),
+ 'search.icq.com' => array('ICQ'),
+
+ // Ilse
+ 'www.ilse.nl' => array('Ilse NL', 'search_for', '?search_for={k}'),
+
+ // Inbox.com
+ 'www2.inbox.com' => array('Inbox', 'q', 'search/results1.aspx?q={k}'),
+
+ // InfoSpace (and related web properties)
+ 'infospace.com' => array('InfoSpace', 'q', '/search/web?q={k}'),
+ 'dogpile.com' => array('InfoSpace'),
+ 'tattoodle.com' => array('InfoSpace'),
+ 'metacrawler.com' => array('InfoSpace'),
+ 'webfetch.com' => array('InfoSpace'),
+ 'webcrawler.com' => array('InfoSpace'),
+ 'search.kiwee.com' => array('InfoSpace'),
+
+ // old infospace system
+ 'wsdsold.infospace.com' => array('InfoSpace', '/\/[^\/]+\/ws\/results\/[^\/]+\/([^\/]+)/', 'pemonitorhosted/ws/results/Web/{k}/1/417/TopNavigation/Source/'),
+
+ // Powered by InfoSpace
+ 'isearch.babylon.com' => array('InfoSpace', 'q'),
+ 'start.facemoods.com' => array('InfoSpace', 's'),
+ 'search.magentic.com' => array('InfoSpace', 'q'),
+ 'search.searchcompletion.com' => array('InfoSpace', 'q'),
+
+ /*
+ * Other InfoSpace powered metasearches are handled in Piwik_Common::extractSearchEngineInformationFromUrl()
+ *
+ * This includes sites such as:
+ * - search.nation.com
+ * - ws.copernic.com
+ * - result.iminent.com
+ */
+
+ // Interia
+ 'www.google.interia.pl' => array('Interia', 'q', 'szukaj?q={k}'),
+
+ // I-play
+ 'start.iplay.com' => array('I-play', 'q', 'searchresults.aspx?q={k}'),
+
+ // Ixquick
+ 'ixquick.com' => array('Ixquick', 'query'),
+ 'www.eu.ixquick.com' => array('Ixquick'),
+ 'ixquick.de' => array('Ixquick'),
+ 'www.ixquick.de' => array('Ixquick'),
+ 'us.ixquick.com' => array('Ixquick'),
+ 's1.us.ixquick.com' => array('Ixquick'),
+ 's2.us.ixquick.com' => array('Ixquick'),
+ 's3.us.ixquick.com' => array('Ixquick'),
+ 's4.us.ixquick.com' => array('Ixquick'),
+ 's5.us.ixquick.com' => array('Ixquick'),
+ 'eu.ixquick.com' => array('Ixquick'),
+ 's8-eu.ixquick.com' => array('Ixquick'),
+ 's1-eu.ixquick.de' => array('Ixquick'),
+
+ // Jyxo
+ 'jyxo.1188.cz' => array('Jyxo', 'q', 's?q={k}'),
+
+ // Jungle Spider
+ 'www.jungle-spider.de' => array('Jungle Spider', 'q'),
+
+ // Jungle key
+ 'junglekey.com' => array('Jungle Key', 'query', 'search.php?query={k}&type=web&lang=en'),
+ 'junglekey.fr' => array('Jungle Key'),
+
+ // Kataweb
+ 'www.kataweb.it' => array('Kataweb', 'q'),
+
+ // Kvasir
+ 'www.kvasir.no' => array('Kvasir', 'q', 'alle?q={k}'),
+
+ // Latne
+ 'www.latne.lv' => array('Latne', 'q', 'siets.php?q={k}'),
+
+ // La Toile Du Québec via Google
+ 'www.toile.com' => array('La Toile Du Québec (Google)', 'q', 'search?q={k}'),
+ 'web.toile.com' => array('La Toile Du Québec (Google)'),
+
+ // Looksmart
+ 'www.looksmart.com' => array('Looksmart', 'key'),
+
+ // Lo.st (Enhanced by Google)
+ 'lo.st' => array('Lo.st', 'x_query', 'cgi-bin/eolost.cgi?x_query={k}'),
+
+ // Lycos
+ 'search.lycos.com' => array('Lycos', 'query', '?query={k}'),
+ 'lycos.{}' => array('Lycos'),
+
+ // maailm.com
+ 'www.maailm.com' => array('maailm.com', 'tekst'),
+
+ // Mail.ru
+ 'go.mail.ru' => array('Mailru', 'q', 'search?rch=e&q={k}', array('UTF-8', 'windows-1251')),
+
+ // Mamma
+ 'www.mamma.com' => array('Mamma', 'query', 'result.php?q={k}'),
+ 'mamma75.mamma.com' => array('Mamma'),
+
+ // Meta
+ 'meta.ua' => array('Meta.ua', 'q', 'search.asp?q={k}'),
+
+ // MetaCrawler.de
+ 's1.metacrawler.de' => array('MetaCrawler DE', 'qry', '?qry={k}'),
+ 's2.metacrawler.de' => array('MetaCrawler DE'),
+ 's3.metacrawler.de' => array('MetaCrawler DE'),
+
+ // Metager
+ 'meta.rrzn.uni-hannover.de' => array('Metager', 'eingabe', 'meta/cgi-bin/meta.ger1?eingabe={k}'),
+ 'www.metager.de' => array('Metager'),
+
+ // Metager2
+ 'metager2.de' => array('Metager2', 'q', 'search/index.php?q={k}'),
+
+ // Meinestadt
+ 'www.meinestadt.de' => array('Meinestadt.de', 'words'),
- // Kataweb
- 'www.kataweb.it' => array('Kataweb', 'q'),
+ // Mister Wong
+ 'www.mister-wong.com' => array('Mister Wong', 'keywords', 'search/?keywords={k}'),
+ 'www.mister-wong.de' => array('Mister Wong'),
- // Kvasir
- 'www.kvasir.no' => array('Kvasir', 'q', 'alle?q={k}'),
+ // Monstercrawler
+ 'www.monstercrawler.com' => array('Monstercrawler', 'qry'),
- // Latne
- 'www.latne.lv' => array('Latne', 'q', 'siets.php?q={k}'),
+ // Mozbot
+ 'www.mozbot.fr' => array('mozbot', 'q', 'results.php?q={k}'),
+ 'www.mozbot.co.uk' => array('mozbot'),
+ 'www.mozbot.com' => array('mozbot'),
- // La Toile Du Québec via Google
- 'www.toile.com' => array('La Toile Du Québec (Google)', 'q', 'search?q={k}'),
- 'web.toile.com' => array('La Toile Du Québec (Google)'),
-
- // Looksmart
- 'www.looksmart.com' => array('Looksmart', 'key'),
-
- // Lo.st (Enhanced by Google)
- 'lo.st' => array('Lo.st', 'x_query', 'cgi-bin/eolost.cgi?x_query={k}'),
-
- // Lycos
- 'search.lycos.com' => array('Lycos', 'query', '?query={k}'),
- 'lycos.{}' => array('Lycos'),
-
- // maailm.com
- 'www.maailm.com' => array('maailm.com', 'tekst'),
-
- // Mail.ru
- 'go.mail.ru' => array('Mailru', 'q', 'search?rch=e&q={k}', array('UTF-8', 'windows-1251')),
-
- // Mamma
- 'www.mamma.com' => array('Mamma', 'query', 'result.php?q={k}'),
- 'mamma75.mamma.com' => array('Mamma'),
-
- // Meta
- 'meta.ua' => array('Meta.ua', 'q', 'search.asp?q={k}'),
-
- // MetaCrawler.de
- 's1.metacrawler.de' => array('MetaCrawler DE', 'qry', '?qry={k}'),
- 's2.metacrawler.de' => array('MetaCrawler DE'),
- 's3.metacrawler.de' => array('MetaCrawler DE'),
-
- // Metager
- 'meta.rrzn.uni-hannover.de' => array('Metager', 'eingabe', 'meta/cgi-bin/meta.ger1?eingabe={k}'),
- 'www.metager.de' => array('Metager'),
-
- // Metager2
- 'metager2.de' => array('Metager2', 'q', 'search/index.php?q={k}'),
-
- // Meinestadt
- 'www.meinestadt.de' => array('Meinestadt.de', 'words'),
+ // El Mundo
+ 'ariadna.elmundo.es' => array('El Mundo', 'q'),
- // Mister Wong
- 'www.mister-wong.com' => array('Mister Wong', 'keywords', 'search/?keywords={k}'),
- 'www.mister-wong.de' => array('Mister Wong'),
+ // MySpace
+ 'searchservice.myspace.com' => array('MySpace', 'qry', 'index.cfm?fuseaction=sitesearch.results&type=Web&qry={k}'),
- // Monstercrawler
- 'www.monstercrawler.com' => array('Monstercrawler', 'qry'),
+ // MySearch / MyWay / MyWebSearch (default: powered by Ask.com)
+ 'www.mysearch.com' => array('MyWebSearch', array('searchfor', 'searchFor'), 'search/Ajmain.jhtml?searchfor={k}'),
+ 'ms114.mysearch.com' => array('MyWebSearch'),
+ 'ms146.mysearch.com' => array('MyWebSearch'),
+ 'kf.mysearch.myway.com' => array('MyWebSearch'),
+ 'ki.mysearch.myway.com' => array('MyWebSearch'),
+ 'search.myway.com' => array('MyWebSearch'),
+ 'search.mywebsearch.com' => array('MyWebSearch'),
- // Mozbot
- 'www.mozbot.fr' => array('mozbot', 'q', 'results.php?q={k}'),
- 'www.mozbot.co.uk' => array('mozbot'),
- 'www.mozbot.com' => array('mozbot'),
- // El Mundo
- 'ariadna.elmundo.es' => array('El Mundo', 'q'),
+ // Najdi
+ 'www.najdi.si' => array('Najdi.si', 'q', 'search.jsp?q={k}'),
- // MySpace
- 'searchservice.myspace.com' => array('MySpace', 'qry', 'index.cfm?fuseaction=sitesearch.results&type=Web&qry={k}'),
+ // Nate
+ 'search.nate.com' => array('Nate', 'q', 'search/all.html?q={k}', 'EUC-KR'),
- // MySearch / MyWay / MyWebSearch (default: powered by Ask.com)
- 'www.mysearch.com' => array('MyWebSearch', array('searchfor', 'searchFor'), 'search/Ajmain.jhtml?searchfor={k}'),
- 'ms114.mysearch.com' => array('MyWebSearch'),
- 'ms146.mysearch.com' => array('MyWebSearch'),
- 'kf.mysearch.myway.com' => array('MyWebSearch'),
- 'ki.mysearch.myway.com' => array('MyWebSearch'),
- 'search.myway.com' => array('MyWebSearch'),
- 'search.mywebsearch.com' => array('MyWebSearch'),
+ // Naver
+ 'search.naver.com' => array('Naver', 'query', 'search.naver?query={k}', 'EUC-KR'),
+ // Needtofind
+ 'ko.search.need2find.com' => array('Needtofind', 'searchfor', 'search/AJmain.jhtml?searchfor={k}'),
- // Najdi
- 'www.najdi.si' => array('Najdi.si', 'q', 'search.jsp?q={k}'),
+ // Neti
+ 'www.neti.ee' => array('Neti', 'query', 'cgi-bin/otsing?query={k}', 'iso-8859-1'),
- // Nate
- 'search.nate.com' => array('Nate', 'q', 'search/all.html?q={k}', 'EUC-KR'),
+ // Nifty
+ 'search.nifty.com' => array('Nifty', 'q', 'websearch/search?q={k}'),
- // Naver
- 'search.naver.com' => array('Naver', 'query', 'search.naver?query={k}', 'EUC-KR'),
+ // Nigma
+ 'nigma.ru' => array('Nigma', 's', 'index.php?s={k}'),
- // Needtofind
- 'ko.search.need2find.com' => array('Needtofind', 'searchfor', 'search/AJmain.jhtml?searchfor={k}'),
+ // Onet
+ 'szukaj.onet.pl' => array('Onet.pl', 'qt', 'query.html?qt={k}'),
- // Neti
- 'www.neti.ee' => array('Neti', 'query', 'cgi-bin/otsing?query={k}', 'iso-8859-1'),
+ // Online.no
+ 'online.no' => array('Online.no', 'q', 'google/index.jsp?q={k}'),
- // Nifty
- 'search.nifty.com' => array('Nifty', 'q', 'websearch/search?q={k}'),
+ // Opplysningen 1881
+ 'www.1881.no' => array('Opplysningen 1881', 'Query', 'Multi/?Query={k}'),
- // Nigma
- 'nigma.ru' => array('Nigma', 's', 'index.php?s={k}'),
+ // Orange
+ 'busca.orange.es' => array('Orange', 'q', 'search?q={k}'),
- // Onet
- 'szukaj.onet.pl' => array('Onet.pl', 'qt', 'query.html?qt={k}'),
+ // Paperball
+ 'www.paperball.de' => array('Paperball', 'q', 'suche/s/?q={k}'),
- // Online.no
- 'online.no' => array('Online.no', 'q', 'google/index.jsp?q={k}'),
+ // PeoplePC
+ 'search.peoplepc.com' => array('PeoplePC', 'q', 'search?q={k}'),
- // Opplysningen 1881
- 'www.1881.no' => array('Opplysningen 1881', 'Query', 'Multi/?Query={k}'),
+ // Picsearch
+ 'www.picsearch.com' => array('Picsearch', 'q', 'index.cgi?q={k}'),
- // Orange
- 'busca.orange.es' => array('Orange', 'q', 'search?q={k}'),
-
- // Paperball
- 'www.paperball.de' => array('Paperball', 'q', 'suche/s/?q={k}'),
+ // Plazoo
+ 'www.plazoo.com' => array('Plazoo', 'q'),
- // PeoplePC
- 'search.peoplepc.com' => array('PeoplePC', 'q', 'search?q={k}'),
+ // Poisk.Ru
+ 'poisk.ru' => array('Poisk.Ru', 'text', 'cgi-bin/poisk?text={k}', 'windows-1251'),
- // Picsearch
- 'www.picsearch.com' => array('Picsearch', 'q', 'index.cgi?q={k}'),
+ // qip
+ 'search.qip.ru' => array('qip.ru', 'query', 'search?query={k}'),
- // Plazoo
- 'www.plazoo.com' => array('Plazoo', 'q'),
+ // Qualigo
+ 'www.qualigo.at' => array('Qualigo', 'q'),
+ 'www.qualigo.ch' => array('Qualigo'),
+ 'www.qualigo.de' => array('Qualigo'),
+ 'www.qualigo.nl' => array('Qualigo'),
- // Poisk.Ru
- 'poisk.ru' => array('Poisk.Ru', 'text', 'cgi-bin/poisk?text={k}', 'windows-1251'),
+ // Rakuten
+ 'websearch.rakuten.co.jp' => array('Rakuten', 'qt', 'WebIS?qt={k}'),
- // qip
- 'search.qip.ru' => array('qip.ru', 'query', 'search?query={k}'),
+ // Rambler
+ 'nova.rambler.ru' => array('Rambler', array('query', 'words'), 'search?query={k}'),
- // Qualigo
- 'www.qualigo.at' => array('Qualigo', 'q'),
- 'www.qualigo.ch' => array('Qualigo'),
- 'www.qualigo.de' => array('Qualigo'),
- 'www.qualigo.nl' => array('Qualigo'),
+ // RPMFind
+ 'rpmfind.net' => array('rpmfind', 'query', 'linux/rpm2html/search.php?query={k}'),
+ 'fr2.rpmfind.net' => array('rpmfind'),
- // Rakuten
- 'websearch.rakuten.co.jp' => array('Rakuten', 'qt', 'WebIS?qt={k}'),
+ // Road Runner Search
+ 'search.rr.com' => array('Road Runner', 'q', '?q={k}'),
- // Rambler
- 'nova.rambler.ru' => array('Rambler', array('query', 'words'), 'search?query={k}'),
+ // Sapo
+ 'pesquisa.sapo.pt' => array('Sapo', 'q', '?q={k}'),
- // RPMFind
- 'rpmfind.net' => array('rpmfind', 'query', 'linux/rpm2html/search.php?query={k}'),
- 'fr2.rpmfind.net' => array('rpmfind'),
+ // scour.com
+ 'scour.com' => array('Scour.com', '/search\/[^\/]+\/(.*)/', 'search/web/{k}'),
- // Road Runner Search
- 'search.rr.com' => array('Road Runner', 'q', '?q={k}'),
+ // Search.com
+ 'www.search.com' => array('Search.com', 'q', 'search?q={k}'),
- // Sapo
- 'pesquisa.sapo.pt' => array('Sapo', 'q', '?q={k}'),
+ // Search.ch
+ 'www.search.ch' => array('Search.ch', 'q', '?q={k}'),
- // scour.com
- 'scour.com' => array('Scour.com', '/search\/[^\/]+\/(.*)/', 'search/web/{k}'),
+ // Searchalot
+ 'searchalot.com' => array('Searchalot', 'q', '?q={k}'),
- // Search.com
- 'www.search.com' => array('Search.com', 'q', 'search?q={k}'),
+ // SearchCanvas
+ 'www.searchcanvas.com' => array('SearchCanvas', 'q', 'web?q={k}'),
- // Search.ch
- 'www.search.ch' => array('Search.ch', 'q', '?q={k}'),
+ // Searchy
+ 'www.searchy.co.uk' => array('Searchy', 'q', 'index.html?q={k}'),
- // Searchalot
- 'searchalot.com' => array('Searchalot', 'q', '?q={k}'),
+ // Setooz
+ // 2010-09-13: the mismatches are because subdomains are language codes
+ // (not country codes)
+ 'bg.setooz.com' => array('Setooz', 'query', 'search?query={k}'),
+ 'da.setooz.com' => array('Setooz'),
+ 'el.setooz.com' => array('Setooz'),
+ 'fa.setooz.com' => array('Setooz'),
+ 'ur.setooz.com' => array('Setooz'),
+ '{}.setooz.com' => array('Setooz'),
- // SearchCanvas
- 'www.searchcanvas.com' => array('SearchCanvas', 'q', 'web?q={k}'),
+ // Seznam
+ 'search.seznam.cz' => array('Seznam', 'q', '?q={k}'),
- // Searchy
- 'www.searchy.co.uk' => array('Searchy', 'q', 'index.html?q={k}'),
+ // Sharelook
+ 'www.sharelook.fr' => array('Sharelook', 'keyword'),
- // Setooz
- // 2010-09-13: the mismatches are because subdomains are language codes
- // (not country codes)
- 'bg.setooz.com' => array('Setooz', 'query', 'search?query={k}'),
- 'da.setooz.com' => array('Setooz'),
- 'el.setooz.com' => array('Setooz'),
- 'fa.setooz.com' => array('Setooz'),
- 'ur.setooz.com' => array('Setooz'),
- '{}.setooz.com' => array('Setooz'),
+ // Skynet
+ 'www.skynet.be' => array('Skynet', 'q', 'services/recherche/google?q={k}'),
- // Seznam
- 'search.seznam.cz' => array('Seznam', 'q', '?q={k}'),
+ // Sogou
+ 'www.sogou.com' => array('Sogou', 'query', 'web?query={k}'),
- // Sharelook
- 'www.sharelook.fr' => array('Sharelook', 'keyword'),
+ // Softonic
+ 'search.softonic.com' => array('Softonic', 'q', 'default/default?q={k}'),
- // Skynet
- 'www.skynet.be' => array('Skynet', 'q', 'services/recherche/google?q={k}'),
+ // soso.com
+ 'www.soso.com' => array('Soso', 'w', 'q?w={k}', 'gb2312'),
- // Sogou
- 'www.sogou.com' => array('Sogou', 'query', 'web?query={k}'),
+ // Startpagina
+ 'startgoogle.startpagina.nl' => array('Startpagina (Google)', 'q', '?q={k}'),
- // Softonic
- 'search.softonic.com' => array('Softonic', 'q', 'default/default?q={k}'),
+ // Startsiden
+ 'www.startsiden.no' => array('Startsiden', 'q', 'sok/index.html?q={k}'),
- // soso.com
- 'www.soso.com' => array('Soso', 'w', 'q?w={k}', 'gb2312'),
+ // suche.info
+ 'suche.info' => array('Suche.info', 'Keywords', 'suche.php?Keywords={k}'),
- // Startpagina
- 'startgoogle.startpagina.nl'=> array('Startpagina (Google)', 'q', '?q={k}'),
+ // Suchmaschine.com
+ 'www.suchmaschine.com' => array('Suchmaschine.com', 'suchstr', 'cgi-bin/wo.cgi?suchstr={k}'),
- // Startsiden
- 'www.startsiden.no' => array('Startsiden', 'q', 'sok/index.html?q={k}'),
+ // Suchnase
+ 'www.suchnase.de' => array('Suchnase', 'q'),
- // suche.info
- 'suche.info' => array('Suche.info', 'Keywords', 'suche.php?Keywords={k}'),
-
- // Suchmaschine.com
- 'www.suchmaschine.com' => array('Suchmaschine.com', 'suchstr', 'cgi-bin/wo.cgi?suchstr={k}'),
+ // TalkTalk
+ 'www.talktalk.co.uk' => array('TalkTalk', 'query', 'search/results.html?query={k}'),
- // Suchnase
- 'www.suchnase.de' => array('Suchnase', 'q'),
+ // Technorati
+ 'technorati.com' => array('Technorati', 'q', 'search?return=sites&authority=all&q={k}'),
- // TalkTalk
- 'www.talktalk.co.uk' => array('TalkTalk', 'query', 'search/results.html?query={k}'),
+ // Teoma
+ 'www.teoma.com' => array('Teoma', 'q', 'web?q={k}'),
- // Technorati
- 'technorati.com' => array('Technorati', 'q', 'search?return=sites&authority=all&q={k}'),
+ // Terra -- referer does not contain search phrase (keywords)
+ 'buscador.terra.es' => array('Terra', 'query', 'Default.aspx?source=Search&query={k}'),
+ 'buscador.terra.cl' => array('Terra'),
+ 'buscador.terra.com.br' => array('Terra'),
- // Teoma
- 'www.teoma.com' => array('Teoma', 'q', 'web?q={k}'),
+ // Tiscali
+ 'search.tiscali.it' => array('Tiscali', array('q', 'key'), '?q={k}'),
+ 'search-dyn.tiscali.it' => array('Tiscali'),
+ 'hledani.tiscali.cz' => array('Tiscali', 'query'),
- // Terra -- referer does not contain search phrase (keywords)
- 'buscador.terra.es' => array('Terra', 'query', 'Default.aspx?source=Search&query={k}'),
- 'buscador.terra.cl' => array('Terra'),
- 'buscador.terra.com.br' => array('Terra'),
+ // Tixuma
+ 'www.tixuma.de' => array('Tixuma', 'sc', 'index.php?mp=search&stp=&sc={k}&tg=0'),
- // Tiscali
- 'search.tiscali.it' => array('Tiscali', array('q', 'key'), '?q={k}'),
- 'search-dyn.tiscali.it' => array('Tiscali'),
- 'hledani.tiscali.cz' => array('Tiscali', 'query'),
+ // T-Online
+ 'suche.t-online.de' => array('T-Online', 'q', 'fast-cgi/tsc?mandant=toi&context=internet-tab&q={k}'),
+ 'brisbane.t-online.de' => array('T-Online'),
+ 'navigationshilfe.t-online.de' => array('T-Online', 'q', 'dtag/dns/results?mode=search_top&q={k}'),
- // Tixuma
- 'www.tixuma.de' => array('Tixuma', 'sc', 'index.php?mp=search&stp=&sc={k}&tg=0'),
+ // Toolbarhome
+ 'www.toolbarhome.com' => array('Toolbarhome', 'q', 'search.aspx?q={k}'),
- // T-Online
- 'suche.t-online.de' => array('T-Online', 'q', 'fast-cgi/tsc?mandant=toi&context=internet-tab&q={k}'),
- 'brisbane.t-online.de' => array('T-Online'),
- 'navigationshilfe.t-online.de'=> array('T-Online', 'q', 'dtag/dns/results?mode=search_top&q={k}'),
+ 'vshare.toolbarhome.com' => array('Toolbarhome'),
- // Toolbarhome
- 'www.toolbarhome.com' => array('Toolbarhome', 'q', 'search.aspx?q={k}'),
-
- 'vshare.toolbarhome.com' => array('Toolbarhome'),
+ // Trouvez.com
+ 'www.trouvez.com' => array('Trouvez.com', 'query'),
- // Trouvez.com
- 'www.trouvez.com' => array('Trouvez.com', 'query'),
+ // TrovaRapido
+ 'www.trovarapido.com' => array('TrovaRapido', 'q', 'result.php?q={k}'),
- // TrovaRapido
- 'www.trovarapido.com' => array('TrovaRapido', 'q', 'result.php?q={k}'),
-
- // Trusted-Search
- 'www.trusted--search.com' => array('Trusted Search', 'w', 'search?w={k}'),
+ // Trusted-Search
+ 'www.trusted--search.com' => array('Trusted Search', 'w', 'search?w={k}'),
- // Twingly
- 'www.twingly.com' => array('Twingly', 'q', 'search?q={k}'),
+ // Twingly
+ 'www.twingly.com' => array('Twingly', 'q', 'search?q={k}'),
- // uol.com.br
- 'busca.uol.com.br' => array('uol.com.br', 'q', '/web/?q={k}'),
+ // uol.com.br
+ 'busca.uol.com.br' => array('uol.com.br', 'q', '/web/?q={k}'),
- // URL.ORGanzier
- 'www.url.org' => array('URL.ORGanzier', 'q', '?l=de&q={k}'),
+ // URL.ORGanzier
+ 'www.url.org' => array('URL.ORGanzier', 'q', '?l=de&q={k}'),
- // Vinden
- 'www.vinden.nl' => array('Vinden', 'q', '?q={k}'),
+ // Vinden
+ 'www.vinden.nl' => array('Vinden', 'q', '?q={k}'),
- // Vindex
- 'www.vindex.nl' => array('Vindex', 'search_for', '/web?search_for={k}'),
- 'search.vindex.nl' => array('Vindex'),
+ // Vindex
+ 'www.vindex.nl' => array('Vindex', 'search_for', '/web?search_for={k}'),
+ 'search.vindex.nl' => array('Vindex'),
- // Virgilio
- 'ricerca.virgilio.it' => array('Virgilio', 'qs', 'ricerca?qs={k}'),
- 'ricercaimmagini.virgilio.it'=> array('Virgilio'),
- 'ricercavideo.virgilio.it' => array('Virgilio'),
- 'ricercanews.virgilio.it' => array('Virgilio'),
- 'mobile.virgilio.it' => array('Virgilio', 'qrs'),
+ // Virgilio
+ 'ricerca.virgilio.it' => array('Virgilio', 'qs', 'ricerca?qs={k}'),
+ 'ricercaimmagini.virgilio.it' => array('Virgilio'),
+ 'ricercavideo.virgilio.it' => array('Virgilio'),
+ 'ricercanews.virgilio.it' => array('Virgilio'),
+ 'mobile.virgilio.it' => array('Virgilio', 'qrs'),
- // Voila
- 'search.ke.voila.fr' => array('Voila', 'rdata', 'S/voila?rdata={k}'),
- 'www.lemoteur.fr' => array('Voila'), // uses voila search
+ // Voila
+ 'search.ke.voila.fr' => array('Voila', 'rdata', 'S/voila?rdata={k}'),
+ 'www.lemoteur.fr' => array('Voila'), // uses voila search
- // Volny
- 'web.volny.cz' => array('Volny', 'search', 'fulltext/?search={k}', 'windows-1250'),
+ // Volny
+ 'web.volny.cz' => array('Volny', 'search', 'fulltext/?search={k}', 'windows-1250'),
- // Walhello
- 'www.walhello.info' => array('Walhello', 'key', 'search?key={k}'),
- 'www.walhello.com' => array('Walhello'),
- 'www.walhello.de' => array('Walhello'),
- 'www.walhello.nl' => array('Walhello'),
+ // Walhello
+ 'www.walhello.info' => array('Walhello', 'key', 'search?key={k}'),
+ 'www.walhello.com' => array('Walhello'),
+ 'www.walhello.de' => array('Walhello'),
+ 'www.walhello.nl' => array('Walhello'),
- // Web.de
- 'suche.web.de' => array('Web.de', array('su', 'q'), 'search/web/?su={k}'),
+ // Web.de
+ 'suche.web.de' => array('Web.de', array('su', 'q'), 'search/web/?su={k}'),
- // Web.nl
- 'www.web.nl' => array('Web.nl', 'zoekwoord'),
+ // Web.nl
+ 'www.web.nl' => array('Web.nl', 'zoekwoord'),
- // Weborama
- 'www.weborama.fr' => array('weborama', 'QUERY'),
+ // Weborama
+ 'www.weborama.fr' => array('weborama', 'QUERY'),
- // WebSearch
- 'www.websearch.com' => array('WebSearch', array('qkw', 'q'), 'search/results2.aspx?q={k}'),
+ // WebSearch
+ 'www.websearch.com' => array('WebSearch', array('qkw', 'q'), 'search/results2.aspx?q={k}'),
- // Wedoo
- // 2011-02-15 - keyword no longer appears to be in Referer URL; candidate for removal?
- 'fr.wedoo.com' => array('Wedoo', 'keyword'),
- 'en.wedoo.com' => array('Wedoo'),
- 'es.wedoo.com' => array('Wedoo'),
+ // Wedoo
+ // 2011-02-15 - keyword no longer appears to be in Referer URL; candidate for removal?
+ 'fr.wedoo.com' => array('Wedoo', 'keyword'),
+ 'en.wedoo.com' => array('Wedoo'),
+ 'es.wedoo.com' => array('Wedoo'),
- // Winamp (Enhanced by Google)
- 'search.winamp.com' => array('Winamp', 'q', 'search/search?q={k}'),
+ // Winamp (Enhanced by Google)
+ 'search.winamp.com' => array('Winamp', 'q', 'search/search?q={k}'),
- // Witch
- 'www.witch.de' => array('Witch', 'search', 'search-result.php?cn=0&search={k}'),
+ // Witch
+ 'www.witch.de' => array('Witch', 'search', 'search-result.php?cn=0&search={k}'),
- // Wirtualna Polska
- 'szukaj.wp.pl' => array('Wirtualna Polska', 'szukaj', 'http://szukaj.wp.pl/szukaj.html?szukaj={k}'),
+ // Wirtualna Polska
+ 'szukaj.wp.pl' => array('Wirtualna Polska', 'szukaj', 'http://szukaj.wp.pl/szukaj.html?szukaj={k}'),
- // WWW
- 'search.www.ee' => array('www värav', 'query'),
+ // WWW
+ 'search.www.ee' => array('www värav', 'query'),
- // X-recherche
- 'www.x-recherche.com' => array('X-Recherche', 'MOTS', 'cgi-bin/websearch?MOTS={k}'),
+ // X-recherche
+ 'www.x-recherche.com' => array('X-Recherche', 'MOTS', 'cgi-bin/websearch?MOTS={k}'),
- // Yahoo
- 'search.yahoo.com' => array('Yahoo!', array('p', 'q'), 'search?p={k}'),
+ // Yahoo
+ 'search.yahoo.com' => array('Yahoo!', array('p', 'q'), 'search?p={k}'),
// '*.search.yahoo.com' => array('Yahoo!'), // see built-in helper in Common.php
- 'yahoo.com' => array('Yahoo!'),
- 'yahoo.{}' => array('Yahoo!'),
- '{}.yahoo.com' => array('Yahoo!'),
- 'cade.yahoo.com' => array('Yahoo!'),
- 'espanol.yahoo.com' => array('Yahoo!'),
- 'qc.yahoo.com' => array('Yahoo!'),
- 'one.cn.yahoo.com' => array('Yahoo!'),
-
- // Powered by Yahoo APIs
- 'www.cercato.it' => array('Yahoo!', 'q'),
- 'search.offerbox.com' => array('Yahoo!', 'q'),
-
- // Powered by Yahoo! Search Marketing (Overture)
- 'ys.mirostart.com' => array('Yahoo!', 'q'),
-
- // Yahoo! Directory
- 'search.yahoo.com/search/dir' => array('Yahoo! Directory', 'p', '?p={k}'),
+ 'yahoo.com' => array('Yahoo!'),
+ 'yahoo.{}' => array('Yahoo!'),
+ '{}.yahoo.com' => array('Yahoo!'),
+ 'cade.yahoo.com' => array('Yahoo!'),
+ 'espanol.yahoo.com' => array('Yahoo!'),
+ 'qc.yahoo.com' => array('Yahoo!'),
+ 'one.cn.yahoo.com' => array('Yahoo!'),
+
+ // Powered by Yahoo APIs
+ 'www.cercato.it' => array('Yahoo!', 'q'),
+ 'search.offerbox.com' => array('Yahoo!', 'q'),
+
+ // Powered by Yahoo! Search Marketing (Overture)
+ 'ys.mirostart.com' => array('Yahoo!', 'q'),
+
+ // Yahoo! Directory
+ 'search.yahoo.com/search/dir' => array('Yahoo! Directory', 'p', '?p={k}'),
// '{}.dir.yahoo.com' => array('Yahoo! Directory'),
- // Yahoo! Images
- 'images.search.yahoo.com' => array('Yahoo! Images', 'p', 'search/images?p={k}'),
+ // Yahoo! Images
+ 'images.search.yahoo.com' => array('Yahoo! Images', 'p', 'search/images?p={k}'),
// '*.images.search.yahoo.com'=> array('Yahoo! Images'), // see built-in helper in Common.php
- '{}.images.yahoo.com' => array('Yahoo! Images'),
- 'cade.images.yahoo.com' => array('Yahoo! Images'),
- 'espanol.images.yahoo.com' => array('Yahoo! Images'),
- 'qc.images.yahoo.com' => array('Yahoo! Images'),
-
- // Yam
- 'search.yam.com' => array('Yam', 'k', 'Search/Web/?SearchType=web&k={k}'),
-
- // Yandex
- 'yandex.ru' => array('Yandex', 'text', 'yandsearch?text={k}'),
- 'yandex.com' => array('Yandex'),
- 'yandex.{}' => array('Yandex'),
-
- // Yandex Images
- 'images.yandex.ru' => array('Yandex Images', 'text', 'yandsearch?text={k}'),
- 'images.yandex.com' => array('Yandex Images'),
- 'images.yandex.{}' => array('Yandex Images'),
-
- // Yasni
- 'www.yasni.de' => array('Yasni', 'query'),
- 'www.yasni.com' => array('Yasni'),
- 'www.yasni.co.uk' => array('Yasni'),
- 'www.yasni.ch' => array('Yasni'),
- 'www.yasni.at' => array('Yasni'),
-
- // Yatedo
- 'www.yatedo.com' => array('Yatedo', 'q', 'search/profil?q={k}'),
- 'www.yatedo.fr' => array('Yatedo'),
-
- // Yellowmap
- 'yellowmap.de' => array('Yellowmap', ' '),
-
- // Yippy
- 'search.yippy.com' => array('Yippy', 'query', 'search?query={k}'),
-
- // YouGoo
- 'www.yougoo.fr' => array('YouGoo', 'q', '?cx=search&q={k}'),
-
- // Zapmeta
- 'www.zapmeta.com' => array('Zapmeta', array('q', 'query'), '?q={k}'),
- 'www.zapmeta.nl' => array('Zapmeta'),
- 'www.zapmeta.de' => array('Zapmeta'),
- 'uk.zapmeta.com' => array('Zapmeta'),
-
- // Zoek
- 'www3.zoek.nl' => array('Zoek', 'q'),
-
- // Zhongsou
- 'p.zhongsou.com' => array('Zhongsou', 'w', 'p?w={k}'),
-
- // Zoeken
- 'www.zoeken.nl' => array('Zoeken', 'q', '?q={k}'),
-
- // Zoohoo
- 'zoohoo.cz' => array('Zoohoo', 'q', '?q={k}', 'windows-1250'),
-
- // Zoznam
- 'www.zoznam.sk' => array('Zoznam', 's', 'hladaj.fcgi?s={k}&co=svet'),
- );
-
- $GLOBALS['Piwik_SearchEngines_NameToUrl'] = array();
- foreach($GLOBALS['Piwik_SearchEngines'] as $url => $info)
- {
- if(!isset($GLOBALS['Piwik_SearchEngines_NameToUrl'][$info[0]]))
- {
- $GLOBALS['Piwik_SearchEngines_NameToUrl'][$info[0]] = $url;
- }
- }
+ '{}.images.yahoo.com' => array('Yahoo! Images'),
+ 'cade.images.yahoo.com' => array('Yahoo! Images'),
+ 'espanol.images.yahoo.com' => array('Yahoo! Images'),
+ 'qc.images.yahoo.com' => array('Yahoo! Images'),
+
+ // Yam
+ 'search.yam.com' => array('Yam', 'k', 'Search/Web/?SearchType=web&k={k}'),
+
+ // Yandex
+ 'yandex.ru' => array('Yandex', 'text', 'yandsearch?text={k}'),
+ 'yandex.com' => array('Yandex'),
+ 'yandex.{}' => array('Yandex'),
+
+ // Yandex Images
+ 'images.yandex.ru' => array('Yandex Images', 'text', 'yandsearch?text={k}'),
+ 'images.yandex.com' => array('Yandex Images'),
+ 'images.yandex.{}' => array('Yandex Images'),
+
+ // Yasni
+ 'www.yasni.de' => array('Yasni', 'query'),
+ 'www.yasni.com' => array('Yasni'),
+ 'www.yasni.co.uk' => array('Yasni'),
+ 'www.yasni.ch' => array('Yasni'),
+ 'www.yasni.at' => array('Yasni'),
+
+ // Yatedo
+ 'www.yatedo.com' => array('Yatedo', 'q', 'search/profil?q={k}'),
+ 'www.yatedo.fr' => array('Yatedo'),
+
+ // Yellowmap
+ 'yellowmap.de' => array('Yellowmap', ' '),
+
+ // Yippy
+ 'search.yippy.com' => array('Yippy', 'query', 'search?query={k}'),
+
+ // YouGoo
+ 'www.yougoo.fr' => array('YouGoo', 'q', '?cx=search&q={k}'),
+
+ // Zapmeta
+ 'www.zapmeta.com' => array('Zapmeta', array('q', 'query'), '?q={k}'),
+ 'www.zapmeta.nl' => array('Zapmeta'),
+ 'www.zapmeta.de' => array('Zapmeta'),
+ 'uk.zapmeta.com' => array('Zapmeta'),
+
+ // Zoek
+ 'www3.zoek.nl' => array('Zoek', 'q'),
+
+ // Zhongsou
+ 'p.zhongsou.com' => array('Zhongsou', 'w', 'p?w={k}'),
+
+ // Zoeken
+ 'www.zoeken.nl' => array('Zoeken', 'q', '?q={k}'),
+
+ // Zoohoo
+ 'zoohoo.cz' => array('Zoohoo', 'q', '?q={k}', 'windows-1250'),
+
+ // Zoznam
+ 'www.zoznam.sk' => array('Zoznam', 's', 'hladaj.fcgi?s={k}&co=svet'),
+ );
+
+ $GLOBALS['Piwik_SearchEngines_NameToUrl'] = array();
+ foreach ($GLOBALS['Piwik_SearchEngines'] as $url => $info) {
+ if (!isset($GLOBALS['Piwik_SearchEngines_NameToUrl'][$info[0]])) {
+ $GLOBALS['Piwik_SearchEngines_NameToUrl'][$info[0]] = $url;
+ }
+ }
}
diff --git a/core/DataFiles/Socials.php b/core/DataFiles/Socials.php
index 3644d501fe..5299990add 100755
--- a/core/DataFiles/Socials.php
+++ b/core/DataFiles/Socials.php
@@ -8,200 +8,199 @@
* @category Piwik
* @package DataFiles
*/
-
-if (!isset($GLOBALS['Piwik_socialUrl']))
-{
- // Note: the key of the array should have max 3 elements eg. sub.domain.ext
- $GLOBALS['Piwik_socialUrl'] = array (
-
- // Facebook
- 'facebook.com' => 'Facebook',
- 'fb.me' => 'Facebook',
-
- // Ozone
- 'qzone.qq.com' => 'Qzone',
-
- // Haboo
- 'habbo.com' => 'Haboo',
-
- // Twitter
- 'twitter.com' => 'Twitter',
- 't.co' => 'Twitter',
-
- // Renren
- 'renren.com' => 'Renren',
-
- // Windows Live Spaces
- 'login.live.com' => 'Windows Live Spaces',
-
- // LinkedIn
- 'linkedin.com' => 'LinkedIn',
-
- // Bebo
- 'bebo.com' => 'Bebo',
-
- // Vkontakte
- 'vk.com' => 'Vkontakte',
- 'vkontakte.ru' => 'Vkontakte',
-
- // Tagged
- 'login.tagged.com' => 'Tagged',
-
- // Orkut
- 'orkut.com' => 'Orkut',
-
- // Myspace
- 'myspace.com' => 'Myspace',
-
- // Frinedster
- 'friendster.com' => 'Friendster',
-
- // Badoo
- 'badoo.com' => 'Badoo',
-
- // hi5
- 'hi5.com' => 'hi5',
-
- // Netlog
- 'netlog.com' => 'Netlog',
-
- // Flixster
- 'flixster.com' => 'Flixster',
-
- // MyLife
- 'mylife.ru' => 'MyLife',
-
- // Classmates.com
- 'classmates.com' => 'Classmates.com',
-
- // Github
- 'github.com' => 'Github',
-
- // Google+
- 'url.google.com' => 'Google+',
-
- // douban
- 'douban.com' => 'douban',
-
- // Odnoklassniki
- 'odnoklassniki.ru' => 'Odnoklassniki',
-
- // Viadeo
- 'viadeo.com' => 'Viadeo',
-
- // Flickr
- 'flickr.com' => 'Flickr',
-
- // WeeWorld
- 'weeworld.com' => 'WeeWorld',
-
- // Last.fm
- 'lastfm.ru' => 'Last.fm',
-
- // MyHeritage
- 'myheritage.com' => 'MyHeritage',
-
- // Xanga
- 'xanga.com' => 'Xanga',
-
- // Mixi
- 'mixi.jp' => 'Mixi',
-
- // Cyworld
- 'global.cyworld.com' => 'Cyworld',
-
- // Gaia Online
- 'gaiaonline.com' => 'Gaia Online',
-
- // Skyrock
- 'skyrock.com' => 'Skyrock',
-
- // BlackPlanet
- 'blackplanet.com' => 'BlackPlanet',
-
- // myYearbook
- 'myyearbook.com' => 'myYearbook',
-
- // Fotolog
- 'fotolog.com' => 'Fotolog',
-
- // Friends Reunited
- 'friendsreunited.com' => 'Friends Reunited',
-
- // LiveJournal
- 'livejournal.ru' => 'LiveJournal',
-
- // StudiVZ
- 'studivz.net' => 'StudiVZ',
-
- // StackOverflow
- 'stackoverflow.com' => 'StackOverflow',
-
- // Sonico.com
- 'sonico.com' => 'Sonico.com',
-
- // Pinterest
- 'pinterest.com' => 'Pinterest',
-
- // Plaxo
- 'plaxo.com' => 'Plaxo',
-
- // Geni.com
- 'geni.com' => 'Geni.com',
-
- // Tuenti
- 'tuenti.com' => 'Tuenti',
-
- // XING
- 'xing.com' => 'XING',
-
- // Taringa!
- 'taringa.net' => 'Taringa!',
-
- // Nasza-klasa.pl
- 'nk.pl' => 'Nasza-klasa.pl',
-
- // StumbleUpon
- 'stumbleupon.com' => 'StumbleUpon',
-
- // Sourceforge
- 'sourceforge.net' => 'SourceForge',
-
- // Hyves
- 'hyves.nl' => 'Hyves',
-
- // WAYN
- 'wayn.com' => 'WAYN',
-
- // Buzznet
- 'buzznet.com' => 'Buzznet',
-
- // Multiply
- 'multiply.com' => 'Multiply',
-
- // Foursquare
- 'ru.foursquare.com' => 'Foursquare',
-
- // vkrugudruzei.ru
- 'vkrugudruzei.ru' => 'vkrugudruzei.ru',
-
- // my.mail.ru
- 'my.mail.ru' => 'my.mail.ru',
-
- //MoiKrug.ru
- 'moikrug.ru' => 'my.mail.ru',
-
- // Reddit
- 'reddit.com' => 'reddit',
-
- // HackerNews
- 'news.ycombinator.com' => 'Hacker News',
-
- // Identi.ca
- 'identi.ca' => 'identi.ca',
-
- // Weibo
- 'weibo.com' => 'Weibo',
- 't.cn' => 'Weibo',
+
+if (!isset($GLOBALS['Piwik_socialUrl'])) {
+ // Note: the key of the array should have max 3 elements eg. sub.domain.ext
+ $GLOBALS['Piwik_socialUrl'] = array(
+
+ // Facebook
+ 'facebook.com' => 'Facebook',
+ 'fb.me' => 'Facebook',
+
+ // Ozone
+ 'qzone.qq.com' => 'Qzone',
+
+ // Haboo
+ 'habbo.com' => 'Haboo',
+
+ // Twitter
+ 'twitter.com' => 'Twitter',
+ 't.co' => 'Twitter',
+
+ // Renren
+ 'renren.com' => 'Renren',
+
+ // Windows Live Spaces
+ 'login.live.com' => 'Windows Live Spaces',
+
+ // LinkedIn
+ 'linkedin.com' => 'LinkedIn',
+
+ // Bebo
+ 'bebo.com' => 'Bebo',
+
+ // Vkontakte
+ 'vk.com' => 'Vkontakte',
+ 'vkontakte.ru' => 'Vkontakte',
+
+ // Tagged
+ 'login.tagged.com' => 'Tagged',
+
+ // Orkut
+ 'orkut.com' => 'Orkut',
+
+ // Myspace
+ 'myspace.com' => 'Myspace',
+
+ // Frinedster
+ 'friendster.com' => 'Friendster',
+
+ // Badoo
+ 'badoo.com' => 'Badoo',
+
+ // hi5
+ 'hi5.com' => 'hi5',
+
+ // Netlog
+ 'netlog.com' => 'Netlog',
+
+ // Flixster
+ 'flixster.com' => 'Flixster',
+
+ // MyLife
+ 'mylife.ru' => 'MyLife',
+
+ // Classmates.com
+ 'classmates.com' => 'Classmates.com',
+
+ // Github
+ 'github.com' => 'Github',
+
+ // Google+
+ 'url.google.com' => 'Google+',
+
+ // douban
+ 'douban.com' => 'douban',
+
+ // Odnoklassniki
+ 'odnoklassniki.ru' => 'Odnoklassniki',
+
+ // Viadeo
+ 'viadeo.com' => 'Viadeo',
+
+ // Flickr
+ 'flickr.com' => 'Flickr',
+
+ // WeeWorld
+ 'weeworld.com' => 'WeeWorld',
+
+ // Last.fm
+ 'lastfm.ru' => 'Last.fm',
+
+ // MyHeritage
+ 'myheritage.com' => 'MyHeritage',
+
+ // Xanga
+ 'xanga.com' => 'Xanga',
+
+ // Mixi
+ 'mixi.jp' => 'Mixi',
+
+ // Cyworld
+ 'global.cyworld.com' => 'Cyworld',
+
+ // Gaia Online
+ 'gaiaonline.com' => 'Gaia Online',
+
+ // Skyrock
+ 'skyrock.com' => 'Skyrock',
+
+ // BlackPlanet
+ 'blackplanet.com' => 'BlackPlanet',
+
+ // myYearbook
+ 'myyearbook.com' => 'myYearbook',
+
+ // Fotolog
+ 'fotolog.com' => 'Fotolog',
+
+ // Friends Reunited
+ 'friendsreunited.com' => 'Friends Reunited',
+
+ // LiveJournal
+ 'livejournal.ru' => 'LiveJournal',
+
+ // StudiVZ
+ 'studivz.net' => 'StudiVZ',
+
+ // StackOverflow
+ 'stackoverflow.com' => 'StackOverflow',
+
+ // Sonico.com
+ 'sonico.com' => 'Sonico.com',
+
+ // Pinterest
+ 'pinterest.com' => 'Pinterest',
+
+ // Plaxo
+ 'plaxo.com' => 'Plaxo',
+
+ // Geni.com
+ 'geni.com' => 'Geni.com',
+
+ // Tuenti
+ 'tuenti.com' => 'Tuenti',
+
+ // XING
+ 'xing.com' => 'XING',
+
+ // Taringa!
+ 'taringa.net' => 'Taringa!',
+
+ // Nasza-klasa.pl
+ 'nk.pl' => 'Nasza-klasa.pl',
+
+ // StumbleUpon
+ 'stumbleupon.com' => 'StumbleUpon',
+
+ // Sourceforge
+ 'sourceforge.net' => 'SourceForge',
+
+ // Hyves
+ 'hyves.nl' => 'Hyves',
+
+ // WAYN
+ 'wayn.com' => 'WAYN',
+
+ // Buzznet
+ 'buzznet.com' => 'Buzznet',
+
+ // Multiply
+ 'multiply.com' => 'Multiply',
+
+ // Foursquare
+ 'ru.foursquare.com' => 'Foursquare',
+
+ // vkrugudruzei.ru
+ 'vkrugudruzei.ru' => 'vkrugudruzei.ru',
+
+ // my.mail.ru
+ 'my.mail.ru' => 'my.mail.ru',
+
+ //MoiKrug.ru
+ 'moikrug.ru' => 'my.mail.ru',
+
+ // Reddit
+ 'reddit.com' => 'reddit',
+
+ // HackerNews
+ 'news.ycombinator.com' => 'Hacker News',
+
+ // Identi.ca
+ 'identi.ca' => 'identi.ca',
+
+ // Weibo
+ 'weibo.com' => 'Weibo',
+ 't.cn' => 'Weibo',
);
}
diff --git a/core/DataTable.php b/core/DataTable.php
index e12e5347b6..ad27bc4a71 100644
--- a/core/DataTable.php
+++ b/core/DataTable.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,1528 +15,1415 @@
require_once PIWIK_INCLUDE_PATH . '/core/Common.php';
/**
- *
+ *
* ---- DataTable
* A DataTable is a data structure used to store complex tables of data.
- *
+ *
* A DataTable is composed of multiple DataTable_Row.
* A DataTable can be applied one or several DataTable_Filter.
* A DataTable can be given to a DataTable_Renderer that would export the data under a given format (XML, HTML, etc.).
- *
+ *
* A DataTable has the following features:
* - serializable to be stored in the DB
* - loadable from the serialized version
* - efficient way of loading data from an external source (from a PHP array structure)
* - very simple interface to get data from the table
- *
+ *
* ---- DataTable_Row
* A DataTableRow in the table is defined by
* - multiple columns (a label, multiple values, ...)
* - optional metadata
* - optional - a sub DataTable associated to this row
- *
+ *
* Simple row example:
- * - columns = array( 'label' => 'Firefox',
- * 'visitors' => 155,
- * 'pages' => 214,
- * 'bounce_rate' => 67)
+ * - columns = array( 'label' => 'Firefox',
+ * 'visitors' => 155,
+ * 'pages' => 214,
+ * 'bounce_rate' => 67)
* - metadata = array('logo' => '/img/browsers/FF.png')
* - no sub DataTable
- *
+ *
* A more complex example would be a DataTable_Row that is associated to a sub DataTable.
- * For example, for the row of the search engine Google,
+ * For example, for the row of the search engine Google,
* we want to get the list of keywords associated, with their statistics.
* - columns = array( 'label' => 'Google',
- * 'visits' => 1550,
- * 'visits_length' => 514214,
- * 'returning_visits' => 77)
- * - metadata = array( 'logo' => '/img/search/google.png',
- * 'url' => 'http://google.com')
+ * 'visits' => 1550,
+ * 'visits_length' => 514214,
+ * 'returning_visits' => 77)
+ * - metadata = array( 'logo' => '/img/search/google.png',
+ * 'url' => 'http://google.com')
* - DataTable = DataTable containing several DataTable_Row containing the keywords information for this search engine
- * Example of one DataTable_Row
- * - the keyword columns specific to this search engine =
- * array( 'label' => 'Piwik', // the keyword
- * 'visitors' => 155, // Piwik has been searched on Google by 155 visitors
- * 'pages' => 214 // Visitors coming from Google with the kwd Piwik have seen 214 pages
- * )
- * - the keyword metadata = array() // nothing here, but we could imagining storing the URL of the search in Google for example
- * - no subTable
- *
- *
+ * Example of one DataTable_Row
+ * - the keyword columns specific to this search engine =
+ * array( 'label' => 'Piwik', // the keyword
+ * 'visitors' => 155, // Piwik has been searched on Google by 155 visitors
+ * 'pages' => 214 // Visitors coming from Google with the kwd Piwik have seen 214 pages
+ * )
+ * - the keyword metadata = array() // nothing here, but we could imagining storing the URL of the search in Google for example
+ * - no subTable
+ *
+ *
* ---- DataTable_Filter
- * A DataTable_Filter is a applied to a DataTable and so
+ * A DataTable_Filter is a applied to a DataTable and so
* can filter information in the multiple DataTable_Row.
- *
+ *
* For example a DataTable_Filter can:
- * - remove rows from the table,
- * for example the rows' labels that do not match a given searched pattern
- * for example the rows' values that are less than a given percentage (low population)
- * - return a subset of the DataTable
- * for example a function that apply a limit: $offset, $limit
+ * - remove rows from the table,
+ * for example the rows' labels that do not match a given searched pattern
+ * for example the rows' values that are less than a given percentage (low population)
+ * - return a subset of the DataTable
+ * for example a function that apply a limit: $offset, $limit
* - add / remove columns
- * for example adding a column that gives the percentage of a given value
+ * for example adding a column that gives the percentage of a given value
* - add some metadata
- * for example the 'logo' path if the filter detects the logo
+ * for example the 'logo' path if the filter detects the logo
* - edit the value, the label
* - change the rows order
- * for example if we want to sort by Label alphabetical order, or by any column value
- *
+ * for example if we want to sort by Label alphabetical order, or by any column value
+ *
* When several DataTable_Filter are to be applied to a DataTable they are applied sequentially.
- * A DataTable_Filter is assigned a priority.
- * For example, filters that
- * - sort rows should be applied with the highest priority
- * - remove rows should be applied with a high priority as they prune the data and improve performance.
- *
+ * A DataTable_Filter is assigned a priority.
+ * For example, filters that
+ * - sort rows should be applied with the highest priority
+ * - remove rows should be applied with a high priority as they prune the data and improve performance.
+ *
* ---- Code example
- *
+ *
* $table = new DataTable();
* $table->addRowsFromArray( array(...) );
- *
+ *
* # sort the table by visits asc
* $filter = new DataTable_Filter_Sort( $table, 'visits', 'asc');
* $tableFiltered = $filter->getTableFiltered();
- *
+ *
* # add a filter to select only the website with a label matching '*.com' (regular expression)
* $filter = new DataTable_Filter_Pattern( $table, 'label', '*(.com)');
* $tableFiltered = $filter->getTableFiltered();
- *
+ *
* # keep the 20 elements from offset 15
* $filter = new DataTable_Filter_Limit( $tableFiltered, 15, 20);
* $tableFiltered = $filter->getTableFiltered();
- *
+ *
* # add a column computing the percentage of visits
* # params = table, column containing the value, new column name to add, number of total visits to use to compute the %
* $filter = new DataTable_Filter_AddColumnPercentage( $tableFiltered, 'visits', 'visits_percentage', 2042);
* $tableFiltered = $filter->getTableFiltered();
- *
+ *
* # we get the table as XML
* $xmlOutput = new DataTable_Exporter_Xml( $table );
* $xmlOutput->setHeader( ... );
* $xmlOutput->setColumnsToExport( array('visits', 'visits_percent', 'label') );
* $XMLstring = $xmlOutput->getOutput();
- *
- *
+ *
+ *
* ---- Other (ideas)
* We can also imagine building a DataTable_Compare which would take N DataTable that have the same
* structure and would compare them, by computing the percentages of differences, etc.
- *
- * For example
+ *
+ * For example
* DataTable1 = [ keyword1, 1550 visits]
- * [ keyword2, 154 visits ]
+ * [ keyword2, 154 visits ]
* DataTable2 = [ keyword1, 1004 visits ]
- * [ keyword3, 659 visits ]
+ * [ keyword3, 659 visits ]
* DataTable_Compare = result of comparison of table1 with table2
- * [ keyword1, +154% ]
- * [ keyword2, +1000% ]
- * [ keyword3, -430% ]
- *
+ * [ keyword1, +154% ]
+ * [ keyword2, +1000% ]
+ * [ keyword3, -430% ]
+ *
* @see Piwik_DataTable_Row A Piwik_DataTable is composed of Piwik_DataTable_Row
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable
{
- /** Name for metadata that describes when a report was archived. */
- const ARCHIVED_DATE_METADATA_NAME = 'archived_date';
- const MAX_DEPTH_DEFAULT = 15;
- /** Name for metadata that describes which columns are empty and should not be shown. */
- const EMPTY_COLUMNS_METADATA_NAME = 'empty_column';
-
- /**
- * Maximum nesting level.
- */
- static private $maximumDepthLevelAllowed = self::MAX_DEPTH_DEFAULT;
-
- /**
- * Array of Piwik_DataTable_Row
- *
- * @var Piwik_DataTable_Row[]
- */
- protected $rows = array();
-
- /**
- * Array of parent IDs
- *
- * @var array
- */
- protected $parents = null;
-
- /**
- * Id assigned to the DataTable, used to lookup the table using the DataTable_Manager
- *
- * @var int
- */
- protected $currentId;
-
- /**
- * Current depth level of this data table
- * 0 is the parent data table
- *
- * @var int
- */
- protected $depthLevel = 0;
-
- /**
- * This flag is set to false once we modify the table in a way that outdates the index
- *
- * @var bool
- */
- protected $indexNotUpToDate = true;
-
- /**
- * This flag sets the index to be rebuild whenever a new row is added,
- * as opposed to re-building the full index when getRowFromLabel is called.
- * This is to optimize and not rebuild the full Index in the case where we
- * add row, getRowFromLabel, addRow, getRowFromLabel thousands of times.
- *
- * @var bool
- */
- protected $rebuildIndexContinuously = false;
-
- /**
- * Column name of last time the table was sorted
- *
- * @var string
- */
- protected $tableSortedBy = false;
-
- /**
- * List of Piwik_DataTable_Filter queued to this table
- *
- * @var array
- */
- protected $queuedFilters = array();
-
- /**
- * We keep track of the number of rows before applying the LIMIT filter that deletes some rows
- *
- * @var int
- */
- protected $rowsCountBeforeLimitFilter = 0;
-
- /**
- * Defaults to false for performance reasons (most of the time we don't need recursive sorting so we save a looping over the dataTable)
- *
- * @var bool
- */
- protected $enableRecursiveSort = false;
-
- /**
- * When the table and all subtables are loaded, this flag will be set to true to ensure filters are applied to all subtables
- *
- * @var bool
- */
- protected $enableRecursiveFilters = false;
-
- /**
- * @var array
- */
- protected $rowsIndexByLabel = array();
-
- /**
- * @var Piwik_DataTable_Row
- */
- protected $summaryRow = null;
-
- /**
- * Table metadata.
- *
- * @var array
- */
- public $metadata = array();
-
- /**
- * Maximum number of rows allowed in this datatable (including the summary row).
- * If adding more rows is attempted, the extra rows get summed to the summary row.
- *
- * @var int
- */
- protected $maximumAllowedRows = 0;
-
- const ID_SUMMARY_ROW = -1;
- const LABEL_SUMMARY_ROW = -1;
- const ID_PARENTS = -2;
-
- /**
- * Builds the DataTable, registers itself to the manager
- *
- */
- public function __construct()
- {
- $this->currentId = Piwik_DataTable_Manager::getInstance()->addTable($this);
- }
-
- /**
- * At destruction we free all memory
- */
- public function __destruct()
- {
- static $depth = 0;
- // destruct can be called several times
- if($depth < self::$maximumDepthLevelAllowed
- && isset($this->rows))
- {
- $depth++;
- foreach($this->getRows() as $row) {
- destroy($row);
- }
- unset($this->rows);
- Piwik_DataTable_Manager::getInstance()->setTableDeleted($this->getId());
- $depth--;
- }
- }
-
- /**
- * Sort the dataTable rows using the php callback function
- *
- * @param string $functionCallback
- * @param string $columnSortedBy The column name. Used to then ask the datatable what column are you sorted by
- */
- public function sort( $functionCallback, $columnSortedBy )
- {
- $this->indexNotUpToDate = true;
- $this->tableSortedBy = $columnSortedBy;
- usort( $this->rows, $functionCallback );
-
- if($this->enableRecursiveSort === true)
- {
- foreach($this->getRows() as $row)
- {
- if(($idSubtable = $row->getIdSubDataTable()) !== null)
- {
- $table = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable);
- $table->enableRecursiveSort();
- $table->sort($functionCallback, $columnSortedBy);
- }
- }
- }
- }
-
- /**
- * Returns the name of the column the tables is sorted by
- *
- * @return bool|string
- */
- public function getSortedByColumnName()
- {
- return $this->tableSortedBy;
- }
-
- /**
- * Enables the recursive sort. Means that when using $table->sort()
- * it will also sort all subtables using the same callback
- */
- public function enableRecursiveSort()
- {
- $this->enableRecursiveSort = true;
- }
-
- /**
- * Enables the recursive filter. Means that when using $table->filter()
- * it will also filter all subtables using the same callback
- */
- public function enableRecursiveFilters()
- {
- $this->enableRecursiveFilters = true;
- }
-
- /**
- * Returns the number of rows before we applied the limit filter
- *
- * @return int
- */
- public function getRowsCountBeforeLimitFilter()
- {
- $toReturn = $this->rowsCountBeforeLimitFilter;
- if($toReturn == 0)
- {
- return $this->getRowsCount();
- }
- return $toReturn;
- }
-
- /**
- * Saves the current number of rows
- */
- function setRowsCountBeforeLimitFilter()
- {
- $this->rowsCountBeforeLimitFilter = $this->getRowsCount();
- }
-
- /**
- * Apply a filter to this datatable
- *
- * @param string $className Class name, eg. "Sort" or "Piwik_DataTable_Filter_Sort"
- * @param array $parameters Array of parameters to the filter, eg. array('nb_visits', 'asc')
- */
- public function filter( $className, $parameters = array() )
- {
- if(!class_exists($className, false))
- {
- $className = "Piwik_DataTable_Filter_" . $className;
- }
- $reflectionObj = new ReflectionClass($className);
-
- // the first parameter of a filter is the DataTable
- // we add the current datatable as the parameter
- $parameters = array_merge(array($this), $parameters);
-
- $filter = $reflectionObj->newInstanceArgs($parameters);
-
- $filter->enableRecursive( $this->enableRecursiveFilters );
-
- $filter->filter($this);
- }
-
- /**
- * Queue a DataTable_Filter that will be applied when applyQueuedFilters() is called.
- * (just before sending the datatable back to the browser (or API, etc.)
- *
- * @param string $className The class name of the filter, eg. Piwik_DataTable_Filter_Limit
- * @param array $parameters The parameters to give to the filter, eg. array( $offset, $limit) for the filter Piwik_DataTable_Filter_Limit
- */
- public function queueFilter( $className, $parameters = array() )
- {
- if(!is_array($parameters))
- {
- $parameters = array($parameters);
- }
- $this->queuedFilters[] = array('className' => $className, 'parameters' => $parameters);
- }
-
- /**
- * Apply all filters that were previously queued to this table
- * @see queueFilter()
- */
- public function applyQueuedFilters()
- {
- foreach($this->queuedFilters as $filter)
- {
- $this->filter($filter['className'], $filter['parameters']);
- }
- $this->queuedFilters = array();
- }
-
- /**
- * Adds a new DataTable to this DataTable
- * Go through all the rows of the new DataTable and applies the algorithm:
- * - if a row in $table doesnt exist in $this we add the new row to $this
- * - if a row exists in both $table and $this we sum the columns values into $this
- * - if a row in $this doesnt exist in $table we add in $this the row of $table without modification
- *
- * A common row to 2 DataTable is defined by the same label
- *
- * @example tests/core/DataTable.test.php
- *
- * @param Piwik_DataTable $tableToSum
- */
- public function addDataTable( Piwik_DataTable $tableToSum )
- {
- foreach($tableToSum->getRows() as $row)
- {
- $labelToLookFor = $row->getColumn('label');
- $rowFound = $this->getRowFromLabel( $labelToLookFor );
- if($rowFound === false)
- {
- if( $labelToLookFor === self::LABEL_SUMMARY_ROW )
- {
- $this->addSummaryRow( $row );
- }
- else
- {
- $this->addRow( $row );
- }
- }
- else
- {
- $rowFound->sumRow( $row );
-
- // 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
- // then we have to recursively sum the subtables
- if(($idSubTable = $row->getIdSubDataTable()) !== null)
- {
- $rowFound->sumSubtable( Piwik_DataTable_Manager::getInstance()->getTable($idSubTable) );
- }
- }
- }
- }
-
- /**
- * Returns the Piwik_DataTable_Row that has a column 'label' with the value $label
- *
- * @param string $label Value of the column 'label' of the row to return
- * @return Piwik_DataTable_Row|false The row if found, false otherwise
- */
- public function getRowFromLabel( $label )
- {
- $rowId = $this->getRowIdFromLabel($label);
- if($rowId instanceof Piwik_DataTable_Row)
- {
- return $rowId;
- }
- if(is_int($rowId) && isset($this->rows[$rowId]))
- {
- return $this->rows[$rowId];
- }
- return false;
- }
-
- /**
- * Returns the row id for the givel label
- *
- * @param string $label Value of the column 'label' of the row to return
- * @return int|Row
- */
- public function getRowIdFromLabel($label)
- {
- $this->rebuildIndexContinuously = true;
- if($this->indexNotUpToDate)
- {
- $this->rebuildIndex();
- }
-
- if($label === self::LABEL_SUMMARY_ROW
- && !is_null($this->summaryRow))
- {
- return $this->summaryRow;
- }
-
- $label = (string)$label;
- if(!isset($this->rowsIndexByLabel[$label]))
- {
- return false;
- }
- return $this->rowsIndexByLabel[$label];
- }
-
- /**
- * Get an empty table with the same properties as this one
- *
- * @return Piwik_DataTable
- */
- public function getEmptyClone()
- {
- $clone = new Piwik_DataTable;
- $clone->queuedFilters = $this->queuedFilters;
- $clone->metadata = $this->metadata;
- return $clone;
- }
-
- /**
- * Rebuilds the index used to lookup a row by label
- */
- private function rebuildIndex()
- {
- foreach($this->rows as $id => $row)
- {
- $label = $row->getColumn('label');
- if($label !== false)
- {
- $this->rowsIndexByLabel[$label] = $id;
- }
- }
- $this->indexNotUpToDate = false;
- }
-
- /**
- * Returns the ith row in the array
- *
- * @param int $id
- * @return Piwik_DataTable_Row or false if not found
- */
- public function getRowFromId($id)
- {
- if(!isset($this->rows[$id]))
- {
- if($id == self::ID_SUMMARY_ROW
- && !is_null($this->summaryRow))
- {
- return $this->summaryRow;
- }
- return false;
- }
- return $this->rows[$id];
- }
-
- /**
- * Returns a row that has the subtable ID matching the parameter
- *
- * @param int $idSubTable
- * @return Piwik_DataTable_Row|false if not found
- */
- public function getRowFromIdSubDataTable($idSubTable)
- {
- $idSubTable = (int)$idSubTable;
- foreach($this->rows as $row)
- {
- if($row->getIdSubDataTable() === $idSubTable)
- {
- return $row;
- }
- }
- return false;
- }
-
- /**
- * Add a row to the table and rebuild the index if necessary
- *
- * @param Piwik_DataTable_Row $row to add at the end of the array
- */
- public function addRow( Piwik_DataTable_Row $row )
- {
- // if there is a upper limit on the number of allowed rows and the table is full,
- // add the new row to the summary row
- if ($this->maximumAllowedRows > 0
- && $this->getRowsCount() >= $this->maximumAllowedRows - 1)
- {
- if ($this->summaryRow === null) // create the summary row if necessary
- {
- $this->addSummaryRow(new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => $row->getColumns()
- )));
- $this->summaryRow->setColumn('label', self::LABEL_SUMMARY_ROW);
- }
- else
- {
- $this->summaryRow->sumRow($row, $enableCopyMetadata = false);
- }
- return $this->summaryRow;
- }
-
- $this->rows[] = $row;
- if(!$this->indexNotUpToDate
- && $this->rebuildIndexContinuously)
- {
- $label = $row->getColumn('label');
- if($label !== false)
- {
- $this->rowsIndexByLabel[$label] = count($this->rows)-1;
- }
- $this->indexNotUpToDate = false;
- }
- return $row;
- }
-
- /**
- * Sets the summary row (a dataTable can have only one summary row)
- *
- * @param Piwik_DataTable_Row $row
- * @return Piwik_DataTable_Row Returns $row.
- */
- public function addSummaryRow( Piwik_DataTable_Row $row )
- {
- $this->summaryRow = $row;
- return $row;
- }
-
- /**
- * Returns the dataTable ID
- *
- * @return int
- */
- public function getId()
- {
- return $this->currentId;
- }
-
- /**
- * Adds a new row from a PHP array data structure
- *
- * @param array $row eg. array(Piwik_DataTable_Row::COLUMNS => array( 'visits' => 13, 'test' => 'toto'),)
- */
- public function addRowFromArray( $row )
- {
- $this->addRowsFromArray(array($row));
- }
-
- /**
- * Adds a new row a PHP array data structure
- *
- * @param array $row eg. array('name' => 'google analytics', 'license' => 'commercial')
- */
- public function addRowFromSimpleArray( $row )
- {
- $this->addRowsFromSimpleArray(array($row));
- }
-
- /**
- * Returns the array of Piwik_DataTable_Row
- *
- * @return Piwik_DataTable_Row[]
- */
- public function getRows()
- {
- if(is_null($this->summaryRow))
- {
- return $this->rows;
- }
- else
- {
- return $this->rows + array(self::ID_SUMMARY_ROW => $this->summaryRow);
- }
- }
-
- /**
- * Returns the array containing all rows values for the requested column
- *
- * @param string $name
- * @return array
- */
- public function getColumn( $name )
- {
- $columnValues = array();
- foreach($this->getRows() as $row)
- {
- $columnValues[] = $row->getColumn($name);
- }
- return $columnValues;
- }
-
- /**
- * Returns an array containing the rows Metadata values
- *
- * @param string $name Metadata column to return
- * @return array
- */
- public function getRowsMetadata( $name )
- {
- $metadataValues = array();
- foreach($this->getRows() as $row)
- {
- $metadataValues[] = $row->getMetadata($name);
- }
- return $metadataValues;
- }
-
- /**
- * Returns the number of rows in the table
- *
- * @return int
- */
- public function getRowsCount()
- {
- if(is_null($this->summaryRow))
- {
- return count($this->rows);
- }
- else
- {
- return count($this->rows) + 1;
- }
- }
-
- /**
- * Returns the first row of the DataTable
- *
- * @return Piwik_DataTable_Row
- */
- public function getFirstRow()
- {
- if(count($this->rows) == 0)
- {
- if(!is_null($this->summaryRow))
- {
- return $this->summaryRow;
- }
- return false;
- }
- $row = array_slice($this->rows, 0, 1);
- return $row[0];
- }
-
- /**
- * Returns the last row of the DataTable
- *
- * @return Piwik_DataTable_Row
- */
- public function getLastRow()
- {
- if(!is_null($this->summaryRow))
- {
- return $this->summaryRow;
- }
-
- if(count($this->rows) == 0)
- {
- return false;
- }
- $row = array_slice($this->rows, -1);
- return $row[0];
- }
-
- /**
- * Returns the sum of the number of rows of all the subtables
- * + the number of rows in the parent table
- *
- * @return int
- */
- public function getRowsCountRecursive()
- {
- $totalCount = 0;
- foreach($this->rows as $row)
- {
- if(($idSubTable = $row->getIdSubDataTable()) !== null)
- {
- $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
- $count = $subTable->getRowsCountRecursive();
- $totalCount += $count;
- }
- }
-
- $totalCount += $this->getRowsCount();
- return $totalCount;
- }
-
- /**
- * Delete a given column $name in all the rows
- *
- * @param string $name
- */
- public function deleteColumn( $name )
- {
- $this->deleteColumns(array($name));
- }
-
- public function __sleep()
- {
- return array('rows', 'parents', 'summaryRow');
- }
-
- /**
- * Rename a column in all rows
- *
- * @param string $oldName Old column name
- * @param string $newName New column name
- */
- public function renameColumn( $oldName, $newName )
- {
- foreach($this->getRows() as $row)
- {
- $row->renameColumn($oldName, $newName);
- if(($idSubDataTable = $row->getIdSubDataTable()) !== null)
- {
- Piwik_DataTable_Manager::getInstance()->getTable($idSubDataTable)->renameColumn($oldName, $newName);
- }
- }
- if(!is_null($this->summaryRow))
- {
- $this->summaryRow->renameColumn($oldName, $newName);
- }
- }
-
- /**
- * Delete columns by name in all rows
- *
- * @param array $names
- * @param bool $deleteRecursiveInSubtables
- */
- public function deleteColumns($names, $deleteRecursiveInSubtables = false)
- {
- foreach($this->getRows() as $row)
- {
- foreach($names as $name)
- {
- $row->deleteColumn($name);
- }
- if(($idSubDataTable = $row->getIdSubDataTable()) !== null)
- {
- Piwik_DataTable_Manager::getInstance()->getTable($idSubDataTable)->deleteColumns($names, $deleteRecursiveInSubtables);
- }
- }
- if(!is_null($this->summaryRow))
- {
- foreach($names as $name)
- {
- $this->summaryRow->deleteColumn($name);
- }
- }
- }
-
- /**
- * Deletes the ith row
- *
- * @param int $id
- * @throws Exception if the row $id cannot be found
- * @return
- */
- public function deleteRow( $id )
- {
- if($id === self::ID_SUMMARY_ROW)
- {
- $this->summaryRow = null;
- return;
- }
- if(!isset($this->rows[$id]))
- {
- throw new Exception("Trying to delete unknown row with idkey = $id");
- }
- unset($this->rows[$id]);
- }
-
- /**
- * Deletes all row from offset, offset + limit.
- * If limit is null then limit = $table->getRowsCount()
- *
- * @param int $offset
- * @param int $limit
- * @return int
- */
- public function deleteRowsOffset( $offset, $limit = null )
- {
- if($limit === 0)
- {
- return 0;
- }
-
- $count = $this->getRowsCount();
- if($offset >= $count)
- {
- return 0;
- }
-
- // if we delete until the end, we delete the summary row as well
- if( is_null($limit)
- || $limit >= $count )
- {
- $this->summaryRow = null;
- }
-
- if(is_null($limit))
- {
- $spliced = array_splice($this->rows, $offset);
- }
- else
- {
- $spliced = array_splice($this->rows, $offset, $limit);
- }
- $countDeleted = count($spliced);
- return $countDeleted;
- }
-
- /**
- * Deletes the rows from the list of rows ID
- *
- * @param array $aKeys ID of the rows to delete
- * @throws Exception if any of the row to delete couldn't be found
- */
- public function deleteRows( array $aKeys )
- {
- foreach($aKeys as $key)
- {
- $this->deleteRow($key);
- }
- }
-
- /**
- * Returns a simple output of the DataTable for easy visualization
- * Example: echo $datatable;
- *
- * @return string
- */
- public function __toString()
- {
- $renderer = new Piwik_DataTable_Renderer_Html();
- $renderer->setTable($this);
- return (string)$renderer;
- }
-
- /**
- * Returns true if both DataTable are exactly the same.
- * Used in unit tests.
- *
- * @param Piwik_DataTable $table1
- * @param Piwik_DataTable $table2
- * @return bool
- */
- static public function isEqual(Piwik_DataTable $table1, Piwik_DataTable $table2)
- {
- $rows1 = $table1->getRows();
- $rows2 = $table2->getRows();
-
- $table1->rebuildIndex();
- $table2->rebuildIndex();
-
- if($table1->getRowsCount() != $table2->getRowsCount())
- {
- return false;
- }
-
- foreach($rows1 as $row1)
- {
- $row2 = $table2->getRowFromLabel($row1->getColumn('label'));
- if($row2 === false
- || !Piwik_DataTable_Row::isEqual($row1,$row2))
- {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * The serialization returns a one dimension array containing all the
- * serialized DataTable contained in this DataTable.
- * We save DataTable in serialized format in the Database.
- * Each row of this returned PHP array will be a row in the DB table.
- * At the end of the method execution, the dataTable may be truncated (if $maximum* parameters are set).
- *
- * The keys of the array are very important as they are used to define the DataTable
- *
- * IMPORTANT: The main table (level 0, parent of all tables) will always be indexed by 0
- * even it was created after some other tables.
- * It also means that all the parent tables (level 0) will be indexed with 0 in their respective
- * serialized arrays. You should never lookup a parent table using the getTable( $id = 0) as it
- * won't work.
- *
- * @throws Exception if an infinite recursion is found (a table row's has a subtable that is one of its parent table)
- * @param int $maximumRowsInDataTable If not null, defines the number of rows maximum of the serialized dataTable
- * @param int $maximumRowsInSubDataTable If not null, defines the number of rows maximum of the serialized subDataTable
- * @param string $columnToSortByBeforeTruncation Column to sort by before truncation
- * @return array Serialized arrays
- * array( // Datatable level0
- * 0 => 'eghuighahgaueytae78yaet7yaetae',
- *
- * // first Datatable level1
- * 1 => 'gaegae gh gwrh guiwh uigwhuige',
- *
- * //second Datatable level1
- * 2 => 'gqegJHUIGHEQjkgneqjgnqeugUGEQHGUHQE',
- *
- * //first Datatable level3 (child of second Datatable level1 for example)
- * 3 => 'eghuighahgaueytae78yaet7yaetaeGRQWUBGUIQGH&QE',
- * );
- */
- public function getSerialized( $maximumRowsInDataTable = null,
- $maximumRowsInSubDataTable = null,
- $columnToSortByBeforeTruncation = null )
- {
- static $depth = 0;
-
- if($depth > self::$maximumDepthLevelAllowed)
- {
- $depth = 0;
- throw new Exception("Maximum recursion level of ".self::$maximumDepthLevelAllowed. " reached. Maybe you have set a DataTable_Row with an associated DataTable belonging already to one of its parent tables?");
- }
- if( !is_null($maximumRowsInDataTable) )
- {
- $this->filter('AddSummaryRow',
- array( $maximumRowsInDataTable - 1,
- Piwik_DataTable::LABEL_SUMMARY_ROW,
- $columnToSortByBeforeTruncation)
- );
- }
-
- // For each row, get the serialized row
- // If it is associated to a sub table, get the serialized table recursively ;
- // but returns all serialized tables and subtable in an array of 1 dimension
- $aSerializedDataTable = array();
- foreach($this->rows as $row)
- {
- if(($idSubTable = $row->getIdSubDataTable()) !== null)
- {
- $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
- $depth++;
- $aSerializedDataTable = $aSerializedDataTable + $subTable->getSerialized( $maximumRowsInSubDataTable, $maximumRowsInSubDataTable, $columnToSortByBeforeTruncation );
- $depth--;
- }
- }
- // we load the current Id of the DataTable
- $forcedId = $this->getId();
-
- // if the datatable is the parent we force the Id at 0 (this is part of the specification)
- if($depth == 0)
- {
- $forcedId = 0;
- }
-
- // we then serialize the rows and store them in the serialized dataTable
- $addToRows = array( self::ID_SUMMARY_ROW => $this->summaryRow );
- if ($this->parents && Piwik_Config::getInstance()->General['enable_archive_parents_of_datatable'])
- {
- $addToRows[self::ID_PARENTS] = $this->parents;
- }
- $aSerializedDataTable[$forcedId] = serialize($this->rows + $addToRows);
- foreach($this->rows as &$row)
- {
- $row->cleanPostSerialize();
- }
-
- return $aSerializedDataTable;
- }
-
- /**
- * Load a serialized string of a datatable.
- *
- * Does not load recursively all the sub DataTable.
- * They will be loaded only when requesting them specifically.
- *
- * The function creates all the necessary DataTable_Row
- *
- * @param string $stringSerialized string of serialized datatable
- * @throws Exception
- */
- public function addRowsFromSerializedArray( $stringSerialized )
- {
- $serialized = unserialize($stringSerialized);
- if($serialized === false)
- {
- throw new Exception("The unserialization has failed!");
- }
- $this->addRowsFromArray($serialized);
- }
-
- /**
- * Loads the DataTable from a PHP array data structure
- *
- * @param array $array Array with the following structure
- * array(
- * // row1
- * array(
- * Piwik_DataTable_Row::COLUMNS => array( col1_name => value1, col2_name => value2, ...),
- * Piwik_DataTable_Row::METADATA => array( metadata1_name => value1, ...), // see Piwik_DataTable_Row
- * ),
- * // row2
- * array( ... ),
- * )
- */
- public function addRowsFromArray( $array )
- {
- foreach($array as $id => $row)
- {
- if($id == self::ID_PARENTS)
- {
- $this->parents = $row;
- continue;
- }
-
- if(is_array($row))
- {
- $row = new Piwik_DataTable_Row($row);
- }
- if($id == self::ID_SUMMARY_ROW)
- {
- $this->summaryRow = $row;
- }
- else
- {
- $this->addRow($row);
- }
- }
- }
-
- /**
- * Loads the data from a simple php array.
- * Basically maps a simple multidimensional php array to a DataTable.
- * Not recursive (if a row contains a php array itself, it won't be loaded)
- *
- * @param array $array Array with the simple structure:
- * array(
- * array( col1_name => valueA, col2_name => valueC, ...),
- * array( col1_name => valueB, col2_name => valueD, ...),
- * )
- * @throws Exception
- * @return
- */
- public function addRowsFromSimpleArray( $array )
- {
- if(count($array) === 0)
- {
- return;
- }
-
- // we define an exception we may throw if at one point we notice that we cannot handle the data structure
- $e = new Exception(" Data structure returned is not convertible in the requested format.".
- " Try to call this method with the parameters '&format=original&serialize=1'".
- "; you will get the original php data structure serialized.".
- " The data structure looks like this: \n \$data = " . var_export($array, true) . "; ");
-
-
- // first pass to see if the array has the structure
- // array(col1_name => val1, col2_name => val2, etc.)
- // with val* that are never arrays (only strings/numbers/bool/etc.)
- // if we detect such a "simple" data structure we convert it to a row with the correct columns' names
- $thisIsNotThatSimple = false;
-
- foreach($array as $columnValue )
- {
- if(is_array($columnValue) || is_object($columnValue))
- {
- $thisIsNotThatSimple = true;
- break;
- }
- }
- if($thisIsNotThatSimple === false)
- {
- // case when the array is indexed by the default numeric index
- if( array_keys($array) == array_keys(array_fill(0, count($array), true)) )
- {
- foreach($array as $row)
- {
- $this->addRow( new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS => array($row) ) ) );
- }
- }
- else
- {
- $this->addRow( new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS => $array ) ) );
- }
- // we have converted our simple array to one single row
- // => we exit the method as the job is now finished
- return;
- }
-
- foreach($array as $key => $row)
- {
- // stuff that looks like a line
- if(is_array($row))
- {
- /**
- * We make sure we can convert this PHP array without losing information.
- * We are able to convert only simple php array (no strings keys, no sub arrays, etc.)
- *
- */
-
- // if the key is a string it means that some information was contained in this key.
- // it cannot be lost during the conversion. Because we are not able to handle properly
- // this key, we throw an explicit exception.
- if(is_string($key))
- {
- throw $e;
- }
- // if any of the sub elements of row is an array we cannot handle this data structure...
- foreach($row as $subRow)
- {
- if(is_array($subRow))
- {
- throw $e;
- }
- }
- $row = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS => $row ) );
- }
- // other (string, numbers...) => we build a line from this value
- else
- {
- $row = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS => array($key => $row)) );
- }
- $this->addRow($row);
- }
- }
-
- /**
- * Rewrites the input $array
- * array (
- * LABEL => array(col1 => X, col2 => Y),
- * LABEL2 => array(col1 => X, col2 => Y),
- * )
- * to the structure
- * array (
- * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL, col1 => X, col2 => Y)),
- * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL2, col1 => X, col2 => Y)),
- * )
- *
- * It also works with array having only one value per row, eg.
- * array (
- * LABEL => X,
- * LABEL2 => Y,
- * )
- * would be converted to the structure
- * array (
- * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL, 'value' => X)),
- * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL2, 'value' => Y)),
- * )
- *
- * The optional parameter $subtablePerLabel is an array of subTable associated to the rows of the $array
- * For example if $subtablePerLabel is given
- * array(
- * LABEL => #Piwik_DataTable_ForLABEL,
- * LABEL2 => #Piwik_DataTable_ForLABEL2,
- * )
- *
- * the $array would become
- * array (
- * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL, col1 => X, col2 => Y),
- * Piwik_DataTable_Row::DATATABLE_ASSOCIATED => #ID DataTable For LABEL
- * ),
- * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL2, col1 => X, col2 => Y)
- * Piwik_DataTable_Row::DATATABLE_ASSOCIATED => #ID2 DataTable For LABEL2
- * ),
- * )
- *
- * @param array $array See method description
- * @param array|null $subtablePerLabel See method description
- */
- public function addRowsFromArrayWithIndexLabel( $array, $subtablePerLabel = null)
- {
- $cleanRow = array();
- foreach($array as $label => $row)
- {
- if(!is_array($row))
- {
- $row = array('value' => $row);
- }
- $cleanRow[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = null;
- // we put the 'label' column first as it looks prettier in API results
- $cleanRow[Piwik_DataTable_Row::COLUMNS] = array('label' => $label) + $row;
- if(!is_null($subtablePerLabel)
- // some rows of this table don't have subtables
- // (for example case of campaigns without keywords)
- && isset($subtablePerLabel[$label])
- )
- {
- $cleanRow[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $subtablePerLabel[$label];
- }
- $this->addRow( new Piwik_DataTable_Row($cleanRow) );
- }
- }
-
- /**
- * Set the array of parent ids
- *
- * @param array $parents
- */
- public function setParents($parents)
- {
- $this->parents = $parents;
- }
-
- /**
- * Get parents
- *
- * @return array of all parents, root level first
- */
- public function getParents() {
- if ($this->parents == null)
- {
- return array();
- }
- return $this->parents;
- }
-
- /**
- * Sets the maximum nesting level to at least a certain value. If the current value is
- * greater than the supplied level, the maximum nesting level is not changed.
- *
- * @param int $atLeastLevel
- */
- static public function setMaximumDepthLevelAllowedAtLeast( $atLeastLevel )
- {
- self::$maximumDepthLevelAllowed = max($atLeastLevel, self::$maximumDepthLevelAllowed);
- if(self::$maximumDepthLevelAllowed < 1) {
- self::$maximumDepthLevelAllowed = 1;
- }
- }
-
- /**
- * Returns all table metadata.
- *
- * @return array
- */
- public function getAllTableMetadata()
- {
- return $this->metadata;
- }
-
- /**
- * Returns metadata by name.
- *
- * @param string $name The metadata name.
- * @return mixed
- */
- public function getMetadata( $name )
- {
- if (!isset($this->metadata[$name]))
- {
- return false;
- }
- return $this->metadata[$name];
- }
-
- /**
- * Sets a metadata value by name.
- *
- * @param string $name The metadata name.
- * @param mixed $value
- */
- public function setMetadata( $name, $value )
- {
- $this->metadata[$name] = $value;
- }
-
- /**
- * Sets the maximum number of rows allowed in this datatable (including the summary
- * row). If adding more then the allowed number of rows is attempted, the extra
- * rows are added to the summary row.
- *
- * @param int|null $maximumAllowedRows
- */
- public function setMaximumAllowedRows( $maximumAllowedRows )
- {
- $this->maximumAllowedRows = $maximumAllowedRows;
- }
-
- /**
- * Traverses a DataTable tree using an array of labels and returns the row
- * it finds or false if it cannot find one, and the number of segments of
- * the path successfully walked.
- *
- * If $missingRowColumns is supplied, the specified path is created. When
- * a subtable is encountered w/o the queried label, a new row is created
- * with the label, and a subtable is added to the row.
- *
- * @param array $path The path to walk. An array of label values.
- * @param array|false $missingRowColumns
- * The default columns to use when creating new arrays.
- * If this parameter is supplied, new rows will be
- * created if labels cannot be found.
- * @param int $maxSubtableRows The maximum number of allowed rows in new
- * subtables.
- * @return array First element is the found row or false. Second element is
- * the number of path segments walked. If a row is found, this
- * will be == to count($path). Otherwise, it will be the index
- * of the path segment that we could not find.
- */
- public function walkPath( $path, $missingRowColumns = false, $maxSubtableRows = 0 )
- {
- $pathLength = count($path);
-
- $table = $this;
- $next = false;
- for ($i = 0; $i < $pathLength; ++$i)
- {
- $segment = $path[$i];
-
- $next = $table->getRowFromLabel($segment);
- if ($next === false)
- {
- // if there is no table to advance to, and we're not adding missing rows, return false
- if ($missingRowColumns === false)
- {
- return array(false, $i);
- }
- else // if we're adding missing rows, add a new row
- {
- $row = new Piwik_DataTable_Row_DataTableSummary();
- $row->setColumns(array('label' => $segment) + $missingRowColumns);
-
- $next = $table->addRow($row);
-
- if ($next !== $row) // if the row wasn't added, the table is full
- {
- // Summary row, has no metadata
- $next->deleteMetadata();
- return array($next, $i);
- }
- }
- }
-
- $table = $next->getSubtable();
- if ($table === false)
- {
- // if the row has no table (and thus no child rows), and we're not adding
- // missing rows, return false
- if ($missingRowColumns === false)
- {
- return array(false, $i);
- }
- else if ($i != $pathLength - 1) // create subtable if missing, but only if not on the last segment
- {
- $table = new Piwik_DataTable();
- $table->setMaximumAllowedRows($maxSubtableRows);
- $next->setSubtable($table);
- // Summary row, has no metadata
- $next->deleteMetadata();
- }
- }
- }
-
- return array($next, $i);
- }
-
- /**
- * Returns a new DataTable that contains the rows of each of this table's
- * subtables.
- *
- * @param string|false $labelColumn If supplied the label of the parent row will be
- * added to a new column in each subtable row. If set to,
- * 'label' each subtable row's label will be prepended w/
- * the parent row's label.
- * @param bool $useMetadataColumn If true and if $labelColumn is supplied, the parent row's
- * label will be added as metadata.
- * @return Piwik_DataTable
- */
- public function mergeSubtables( $labelColumn = false, $useMetadataColumn = false )
- {
- $result = new Piwik_DataTable();
- foreach ($this->getRows() as $row)
- {
- $subtable = $row->getSubtable();
- if ($subtable !== false)
- {
- $parentLabel = $row->getColumn('label');
-
- // add a copy of each subtable row to the new datatable
- foreach ($subtable->getRows() as $id => $subRow)
- {
- $copy = clone $subRow;
-
- // if the summary row, add it to the existing summary row (or add a new one)
- if ($id == self::ID_SUMMARY_ROW)
- {
- $existing = $result->getRowFromId(self::ID_SUMMARY_ROW);
- if ($existing === false)
- {
- $result->addSummaryRow($copy);
- }
- else
- {
- $existing->sumRow($copy);
- }
- }
- else
- {
- if ($labelColumn !== false)
- {
- // if we're modifying the subtable's rows' label column, then we make
- // sure to prepend the existing label w/ the parent row's label. otherwise
- // we're just adding the parent row's label as a new column/metadata.
- $newLabel = $parentLabel;
- if ($labelColumn == 'label')
- {
- $newLabel .= ' - '.$copy->getColumn('label');
- }
-
- // modify the child row's label or add new column/metadata
- if ($useMetadataColumn)
- {
- $copy->setMetadata($labelColumn, $newLabel);
- }
- else
- {
- $copy->setColumn($labelColumn, $newLabel);
- }
- }
-
- $result->addRow($copy);
- }
- }
- }
- }
- return $result;
- }
-
- /**
- * Returns a new DataTable created with data from a 'simple' array.
- *
- * @param array $array
- * @return Piwik_DataTable
- */
- public static function makeFromSimpleArray( $array )
- {
- $dataTable = new Piwik_DataTable();
- $dataTable->addRowsFromSimpleArray($array);
- return $dataTable;
- }
+ /** Name for metadata that describes when a report was archived. */
+ const ARCHIVED_DATE_METADATA_NAME = 'archived_date';
+ const MAX_DEPTH_DEFAULT = 15;
+ /** Name for metadata that describes which columns are empty and should not be shown. */
+ const EMPTY_COLUMNS_METADATA_NAME = 'empty_column';
+
+ /**
+ * Maximum nesting level.
+ */
+ static private $maximumDepthLevelAllowed = self::MAX_DEPTH_DEFAULT;
+
+ /**
+ * Array of Piwik_DataTable_Row
+ *
+ * @var Piwik_DataTable_Row[]
+ */
+ protected $rows = array();
+
+ /**
+ * Array of parent IDs
+ *
+ * @var array
+ */
+ protected $parents = null;
+
+ /**
+ * Id assigned to the DataTable, used to lookup the table using the DataTable_Manager
+ *
+ * @var int
+ */
+ protected $currentId;
+
+ /**
+ * Current depth level of this data table
+ * 0 is the parent data table
+ *
+ * @var int
+ */
+ protected $depthLevel = 0;
+
+ /**
+ * This flag is set to false once we modify the table in a way that outdates the index
+ *
+ * @var bool
+ */
+ protected $indexNotUpToDate = true;
+
+ /**
+ * This flag sets the index to be rebuild whenever a new row is added,
+ * as opposed to re-building the full index when getRowFromLabel is called.
+ * This is to optimize and not rebuild the full Index in the case where we
+ * add row, getRowFromLabel, addRow, getRowFromLabel thousands of times.
+ *
+ * @var bool
+ */
+ protected $rebuildIndexContinuously = false;
+
+ /**
+ * Column name of last time the table was sorted
+ *
+ * @var string
+ */
+ protected $tableSortedBy = false;
+
+ /**
+ * List of Piwik_DataTable_Filter queued to this table
+ *
+ * @var array
+ */
+ protected $queuedFilters = array();
+
+ /**
+ * We keep track of the number of rows before applying the LIMIT filter that deletes some rows
+ *
+ * @var int
+ */
+ protected $rowsCountBeforeLimitFilter = 0;
+
+ /**
+ * Defaults to false for performance reasons (most of the time we don't need recursive sorting so we save a looping over the dataTable)
+ *
+ * @var bool
+ */
+ protected $enableRecursiveSort = false;
+
+ /**
+ * When the table and all subtables are loaded, this flag will be set to true to ensure filters are applied to all subtables
+ *
+ * @var bool
+ */
+ protected $enableRecursiveFilters = false;
+
+ /**
+ * @var array
+ */
+ protected $rowsIndexByLabel = array();
+
+ /**
+ * @var Piwik_DataTable_Row
+ */
+ protected $summaryRow = null;
+
+ /**
+ * Table metadata.
+ *
+ * @var array
+ */
+ public $metadata = array();
+
+ /**
+ * Maximum number of rows allowed in this datatable (including the summary row).
+ * If adding more rows is attempted, the extra rows get summed to the summary row.
+ *
+ * @var int
+ */
+ protected $maximumAllowedRows = 0;
+
+ const ID_SUMMARY_ROW = -1;
+ const LABEL_SUMMARY_ROW = -1;
+ const ID_PARENTS = -2;
+
+ /**
+ * Builds the DataTable, registers itself to the manager
+ *
+ */
+ public function __construct()
+ {
+ $this->currentId = Piwik_DataTable_Manager::getInstance()->addTable($this);
+ }
+
+ /**
+ * At destruction we free all memory
+ */
+ public function __destruct()
+ {
+ static $depth = 0;
+ // destruct can be called several times
+ if ($depth < self::$maximumDepthLevelAllowed
+ && isset($this->rows)
+ ) {
+ $depth++;
+ foreach ($this->getRows() as $row) {
+ destroy($row);
+ }
+ unset($this->rows);
+ Piwik_DataTable_Manager::getInstance()->setTableDeleted($this->getId());
+ $depth--;
+ }
+ }
+
+ /**
+ * Sort the dataTable rows using the php callback function
+ *
+ * @param string $functionCallback
+ * @param string $columnSortedBy The column name. Used to then ask the datatable what column are you sorted by
+ */
+ public function sort($functionCallback, $columnSortedBy)
+ {
+ $this->indexNotUpToDate = true;
+ $this->tableSortedBy = $columnSortedBy;
+ usort($this->rows, $functionCallback);
+
+ if ($this->enableRecursiveSort === true) {
+ foreach ($this->getRows() as $row) {
+ if (($idSubtable = $row->getIdSubDataTable()) !== null) {
+ $table = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable);
+ $table->enableRecursiveSort();
+ $table->sort($functionCallback, $columnSortedBy);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the name of the column the tables is sorted by
+ *
+ * @return bool|string
+ */
+ public function getSortedByColumnName()
+ {
+ return $this->tableSortedBy;
+ }
+
+ /**
+ * Enables the recursive sort. Means that when using $table->sort()
+ * it will also sort all subtables using the same callback
+ */
+ public function enableRecursiveSort()
+ {
+ $this->enableRecursiveSort = true;
+ }
+
+ /**
+ * Enables the recursive filter. Means that when using $table->filter()
+ * it will also filter all subtables using the same callback
+ */
+ public function enableRecursiveFilters()
+ {
+ $this->enableRecursiveFilters = true;
+ }
+
+ /**
+ * Returns the number of rows before we applied the limit filter
+ *
+ * @return int
+ */
+ public function getRowsCountBeforeLimitFilter()
+ {
+ $toReturn = $this->rowsCountBeforeLimitFilter;
+ if ($toReturn == 0) {
+ return $this->getRowsCount();
+ }
+ return $toReturn;
+ }
+
+ /**
+ * Saves the current number of rows
+ */
+ function setRowsCountBeforeLimitFilter()
+ {
+ $this->rowsCountBeforeLimitFilter = $this->getRowsCount();
+ }
+
+ /**
+ * Apply a filter to this datatable
+ *
+ * @param string $className Class name, eg. "Sort" or "Piwik_DataTable_Filter_Sort"
+ * @param array $parameters Array of parameters to the filter, eg. array('nb_visits', 'asc')
+ */
+ public function filter($className, $parameters = array())
+ {
+ if (!class_exists($className, false)) {
+ $className = "Piwik_DataTable_Filter_" . $className;
+ }
+ $reflectionObj = new ReflectionClass($className);
+
+ // the first parameter of a filter is the DataTable
+ // we add the current datatable as the parameter
+ $parameters = array_merge(array($this), $parameters);
+
+ $filter = $reflectionObj->newInstanceArgs($parameters);
+
+ $filter->enableRecursive($this->enableRecursiveFilters);
+
+ $filter->filter($this);
+ }
+
+ /**
+ * Queue a DataTable_Filter that will be applied when applyQueuedFilters() is called.
+ * (just before sending the datatable back to the browser (or API, etc.)
+ *
+ * @param string $className The class name of the filter, eg. Piwik_DataTable_Filter_Limit
+ * @param array $parameters The parameters to give to the filter, eg. array( $offset, $limit) for the filter Piwik_DataTable_Filter_Limit
+ */
+ public function queueFilter($className, $parameters = array())
+ {
+ if (!is_array($parameters)) {
+ $parameters = array($parameters);
+ }
+ $this->queuedFilters[] = array('className' => $className, 'parameters' => $parameters);
+ }
+
+ /**
+ * Apply all filters that were previously queued to this table
+ * @see queueFilter()
+ */
+ public function applyQueuedFilters()
+ {
+ foreach ($this->queuedFilters as $filter) {
+ $this->filter($filter['className'], $filter['parameters']);
+ }
+ $this->queuedFilters = array();
+ }
+
+ /**
+ * Adds a new DataTable to this DataTable
+ * Go through all the rows of the new DataTable and applies the algorithm:
+ * - if a row in $table doesnt exist in $this we add the new row to $this
+ * - if a row exists in both $table and $this we sum the columns values into $this
+ * - if a row in $this doesnt exist in $table we add in $this the row of $table without modification
+ *
+ * A common row to 2 DataTable is defined by the same label
+ *
+ * @example tests/core/DataTable.test.php
+ *
+ * @param Piwik_DataTable $tableToSum
+ */
+ public function addDataTable(Piwik_DataTable $tableToSum)
+ {
+ foreach ($tableToSum->getRows() as $row) {
+ $labelToLookFor = $row->getColumn('label');
+ $rowFound = $this->getRowFromLabel($labelToLookFor);
+ if ($rowFound === false) {
+ if ($labelToLookFor === self::LABEL_SUMMARY_ROW) {
+ $this->addSummaryRow($row);
+ } else {
+ $this->addRow($row);
+ }
+ } else {
+ $rowFound->sumRow($row);
+
+ // 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
+ // then we have to recursively sum the subtables
+ if (($idSubTable = $row->getIdSubDataTable()) !== null) {
+ $rowFound->sumSubtable(Piwik_DataTable_Manager::getInstance()->getTable($idSubTable));
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the Piwik_DataTable_Row that has a column 'label' with the value $label
+ *
+ * @param string $label Value of the column 'label' of the row to return
+ * @return Piwik_DataTable_Row|false The row if found, false otherwise
+ */
+ public function getRowFromLabel($label)
+ {
+ $rowId = $this->getRowIdFromLabel($label);
+ if ($rowId instanceof Piwik_DataTable_Row) {
+ return $rowId;
+ }
+ if (is_int($rowId) && isset($this->rows[$rowId])) {
+ return $this->rows[$rowId];
+ }
+ return false;
+ }
+
+ /**
+ * Returns the row id for the givel label
+ *
+ * @param string $label Value of the column 'label' of the row to return
+ * @return int|Row
+ */
+ public function getRowIdFromLabel($label)
+ {
+ $this->rebuildIndexContinuously = true;
+ if ($this->indexNotUpToDate) {
+ $this->rebuildIndex();
+ }
+
+ if ($label === self::LABEL_SUMMARY_ROW
+ && !is_null($this->summaryRow)
+ ) {
+ return $this->summaryRow;
+ }
+
+ $label = (string)$label;
+ if (!isset($this->rowsIndexByLabel[$label])) {
+ return false;
+ }
+ return $this->rowsIndexByLabel[$label];
+ }
+
+ /**
+ * Get an empty table with the same properties as this one
+ *
+ * @return Piwik_DataTable
+ */
+ public function getEmptyClone()
+ {
+ $clone = new Piwik_DataTable;
+ $clone->queuedFilters = $this->queuedFilters;
+ $clone->metadata = $this->metadata;
+ return $clone;
+ }
+
+ /**
+ * Rebuilds the index used to lookup a row by label
+ */
+ private function rebuildIndex()
+ {
+ foreach ($this->rows as $id => $row) {
+ $label = $row->getColumn('label');
+ if ($label !== false) {
+ $this->rowsIndexByLabel[$label] = $id;
+ }
+ }
+ $this->indexNotUpToDate = false;
+ }
+
+ /**
+ * Returns the ith row in the array
+ *
+ * @param int $id
+ * @return Piwik_DataTable_Row or false if not found
+ */
+ public function getRowFromId($id)
+ {
+ if (!isset($this->rows[$id])) {
+ if ($id == self::ID_SUMMARY_ROW
+ && !is_null($this->summaryRow)
+ ) {
+ return $this->summaryRow;
+ }
+ return false;
+ }
+ return $this->rows[$id];
+ }
+
+ /**
+ * Returns a row that has the subtable ID matching the parameter
+ *
+ * @param int $idSubTable
+ * @return Piwik_DataTable_Row|false if not found
+ */
+ public function getRowFromIdSubDataTable($idSubTable)
+ {
+ $idSubTable = (int)$idSubTable;
+ foreach ($this->rows as $row) {
+ if ($row->getIdSubDataTable() === $idSubTable) {
+ return $row;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add a row to the table and rebuild the index if necessary
+ *
+ * @param Piwik_DataTable_Row $row to add at the end of the array
+ */
+ public function addRow(Piwik_DataTable_Row $row)
+ {
+ // if there is a upper limit on the number of allowed rows and the table is full,
+ // add the new row to the summary row
+ if ($this->maximumAllowedRows > 0
+ && $this->getRowsCount() >= $this->maximumAllowedRows - 1
+ ) {
+ if ($this->summaryRow === null) // create the summary row if necessary
+ {
+ $this->addSummaryRow(new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => $row->getColumns()
+ )));
+ $this->summaryRow->setColumn('label', self::LABEL_SUMMARY_ROW);
+ } else {
+ $this->summaryRow->sumRow($row, $enableCopyMetadata = false);
+ }
+ return $this->summaryRow;
+ }
+
+ $this->rows[] = $row;
+ if (!$this->indexNotUpToDate
+ && $this->rebuildIndexContinuously
+ ) {
+ $label = $row->getColumn('label');
+ if ($label !== false) {
+ $this->rowsIndexByLabel[$label] = count($this->rows) - 1;
+ }
+ $this->indexNotUpToDate = false;
+ }
+ return $row;
+ }
+
+ /**
+ * Sets the summary row (a dataTable can have only one summary row)
+ *
+ * @param Piwik_DataTable_Row $row
+ * @return Piwik_DataTable_Row Returns $row.
+ */
+ public function addSummaryRow(Piwik_DataTable_Row $row)
+ {
+ $this->summaryRow = $row;
+ return $row;
+ }
+
+ /**
+ * Returns the dataTable ID
+ *
+ * @return int
+ */
+ public function getId()
+ {
+ return $this->currentId;
+ }
+
+ /**
+ * Adds a new row from a PHP array data structure
+ *
+ * @param array $row eg. array(Piwik_DataTable_Row::COLUMNS => array( 'visits' => 13, 'test' => 'toto'),)
+ */
+ public function addRowFromArray($row)
+ {
+ $this->addRowsFromArray(array($row));
+ }
+
+ /**
+ * Adds a new row a PHP array data structure
+ *
+ * @param array $row eg. array('name' => 'google analytics', 'license' => 'commercial')
+ */
+ public function addRowFromSimpleArray($row)
+ {
+ $this->addRowsFromSimpleArray(array($row));
+ }
+
+ /**
+ * Returns the array of Piwik_DataTable_Row
+ *
+ * @return Piwik_DataTable_Row[]
+ */
+ public function getRows()
+ {
+ if (is_null($this->summaryRow)) {
+ return $this->rows;
+ } else {
+ return $this->rows + array(self::ID_SUMMARY_ROW => $this->summaryRow);
+ }
+ }
+
+ /**
+ * Returns the array containing all rows values for the requested column
+ *
+ * @param string $name
+ * @return array
+ */
+ public function getColumn($name)
+ {
+ $columnValues = array();
+ foreach ($this->getRows() as $row) {
+ $columnValues[] = $row->getColumn($name);
+ }
+ return $columnValues;
+ }
+
+ /**
+ * Returns an array containing the rows Metadata values
+ *
+ * @param string $name Metadata column to return
+ * @return array
+ */
+ public function getRowsMetadata($name)
+ {
+ $metadataValues = array();
+ foreach ($this->getRows() as $row) {
+ $metadataValues[] = $row->getMetadata($name);
+ }
+ return $metadataValues;
+ }
+
+ /**
+ * Returns the number of rows in the table
+ *
+ * @return int
+ */
+ public function getRowsCount()
+ {
+ if (is_null($this->summaryRow)) {
+ return count($this->rows);
+ } else {
+ return count($this->rows) + 1;
+ }
+ }
+
+ /**
+ * Returns the first row of the DataTable
+ *
+ * @return Piwik_DataTable_Row
+ */
+ public function getFirstRow()
+ {
+ if (count($this->rows) == 0) {
+ if (!is_null($this->summaryRow)) {
+ return $this->summaryRow;
+ }
+ return false;
+ }
+ $row = array_slice($this->rows, 0, 1);
+ return $row[0];
+ }
+
+ /**
+ * Returns the last row of the DataTable
+ *
+ * @return Piwik_DataTable_Row
+ */
+ public function getLastRow()
+ {
+ if (!is_null($this->summaryRow)) {
+ return $this->summaryRow;
+ }
+
+ if (count($this->rows) == 0) {
+ return false;
+ }
+ $row = array_slice($this->rows, -1);
+ return $row[0];
+ }
+
+ /**
+ * Returns the sum of the number of rows of all the subtables
+ * + the number of rows in the parent table
+ *
+ * @return int
+ */
+ public function getRowsCountRecursive()
+ {
+ $totalCount = 0;
+ foreach ($this->rows as $row) {
+ if (($idSubTable = $row->getIdSubDataTable()) !== null) {
+ $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
+ $count = $subTable->getRowsCountRecursive();
+ $totalCount += $count;
+ }
+ }
+
+ $totalCount += $this->getRowsCount();
+ return $totalCount;
+ }
+
+ /**
+ * Delete a given column $name in all the rows
+ *
+ * @param string $name
+ */
+ public function deleteColumn($name)
+ {
+ $this->deleteColumns(array($name));
+ }
+
+ public function __sleep()
+ {
+ return array('rows', 'parents', 'summaryRow');
+ }
+
+ /**
+ * Rename a column in all rows
+ *
+ * @param string $oldName Old column name
+ * @param string $newName New column name
+ */
+ public function renameColumn($oldName, $newName)
+ {
+ foreach ($this->getRows() as $row) {
+ $row->renameColumn($oldName, $newName);
+ if (($idSubDataTable = $row->getIdSubDataTable()) !== null) {
+ Piwik_DataTable_Manager::getInstance()->getTable($idSubDataTable)->renameColumn($oldName, $newName);
+ }
+ }
+ if (!is_null($this->summaryRow)) {
+ $this->summaryRow->renameColumn($oldName, $newName);
+ }
+ }
+
+ /**
+ * Delete columns by name in all rows
+ *
+ * @param array $names
+ * @param bool $deleteRecursiveInSubtables
+ */
+ public function deleteColumns($names, $deleteRecursiveInSubtables = false)
+ {
+ foreach ($this->getRows() as $row) {
+ foreach ($names as $name) {
+ $row->deleteColumn($name);
+ }
+ if (($idSubDataTable = $row->getIdSubDataTable()) !== null) {
+ Piwik_DataTable_Manager::getInstance()->getTable($idSubDataTable)->deleteColumns($names, $deleteRecursiveInSubtables);
+ }
+ }
+ if (!is_null($this->summaryRow)) {
+ foreach ($names as $name) {
+ $this->summaryRow->deleteColumn($name);
+ }
+ }
+ }
+
+ /**
+ * Deletes the ith row
+ *
+ * @param int $id
+ * @throws Exception if the row $id cannot be found
+ * @return
+ */
+ public function deleteRow($id)
+ {
+ if ($id === self::ID_SUMMARY_ROW) {
+ $this->summaryRow = null;
+ return;
+ }
+ if (!isset($this->rows[$id])) {
+ throw new Exception("Trying to delete unknown row with idkey = $id");
+ }
+ unset($this->rows[$id]);
+ }
+
+ /**
+ * Deletes all row from offset, offset + limit.
+ * If limit is null then limit = $table->getRowsCount()
+ *
+ * @param int $offset
+ * @param int $limit
+ * @return int
+ */
+ public function deleteRowsOffset($offset, $limit = null)
+ {
+ if ($limit === 0) {
+ return 0;
+ }
+
+ $count = $this->getRowsCount();
+ if ($offset >= $count) {
+ return 0;
+ }
+
+ // if we delete until the end, we delete the summary row as well
+ if (is_null($limit)
+ || $limit >= $count
+ ) {
+ $this->summaryRow = null;
+ }
+
+ if (is_null($limit)) {
+ $spliced = array_splice($this->rows, $offset);
+ } else {
+ $spliced = array_splice($this->rows, $offset, $limit);
+ }
+ $countDeleted = count($spliced);
+ return $countDeleted;
+ }
+
+ /**
+ * Deletes the rows from the list of rows ID
+ *
+ * @param array $aKeys ID of the rows to delete
+ * @throws Exception if any of the row to delete couldn't be found
+ */
+ public function deleteRows(array $aKeys)
+ {
+ foreach ($aKeys as $key) {
+ $this->deleteRow($key);
+ }
+ }
+
+ /**
+ * Returns a simple output of the DataTable for easy visualization
+ * Example: echo $datatable;
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $renderer = new Piwik_DataTable_Renderer_Html();
+ $renderer->setTable($this);
+ return (string)$renderer;
+ }
+
+ /**
+ * Returns true if both DataTable are exactly the same.
+ * Used in unit tests.
+ *
+ * @param Piwik_DataTable $table1
+ * @param Piwik_DataTable $table2
+ * @return bool
+ */
+ static public function isEqual(Piwik_DataTable $table1, Piwik_DataTable $table2)
+ {
+ $rows1 = $table1->getRows();
+ $rows2 = $table2->getRows();
+
+ $table1->rebuildIndex();
+ $table2->rebuildIndex();
+
+ if ($table1->getRowsCount() != $table2->getRowsCount()) {
+ return false;
+ }
+
+ foreach ($rows1 as $row1) {
+ $row2 = $table2->getRowFromLabel($row1->getColumn('label'));
+ if ($row2 === false
+ || !Piwik_DataTable_Row::isEqual($row1, $row2)
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * The serialization returns a one dimension array containing all the
+ * serialized DataTable contained in this DataTable.
+ * We save DataTable in serialized format in the Database.
+ * Each row of this returned PHP array will be a row in the DB table.
+ * At the end of the method execution, the dataTable may be truncated (if $maximum* parameters are set).
+ *
+ * The keys of the array are very important as they are used to define the DataTable
+ *
+ * IMPORTANT: The main table (level 0, parent of all tables) will always be indexed by 0
+ * even it was created after some other tables.
+ * It also means that all the parent tables (level 0) will be indexed with 0 in their respective
+ * serialized arrays. You should never lookup a parent table using the getTable( $id = 0) as it
+ * won't work.
+ *
+ * @throws Exception if an infinite recursion is found (a table row's has a subtable that is one of its parent table)
+ * @param int $maximumRowsInDataTable If not null, defines the number of rows maximum of the serialized dataTable
+ * @param int $maximumRowsInSubDataTable If not null, defines the number of rows maximum of the serialized subDataTable
+ * @param string $columnToSortByBeforeTruncation Column to sort by before truncation
+ * @return array Serialized arrays
+ * array( // Datatable level0
+ * 0 => 'eghuighahgaueytae78yaet7yaetae',
+ *
+ * // first Datatable level1
+ * 1 => 'gaegae gh gwrh guiwh uigwhuige',
+ *
+ * //second Datatable level1
+ * 2 => 'gqegJHUIGHEQjkgneqjgnqeugUGEQHGUHQE',
+ *
+ * //first Datatable level3 (child of second Datatable level1 for example)
+ * 3 => 'eghuighahgaueytae78yaet7yaetaeGRQWUBGUIQGH&QE',
+ * );
+ */
+ public function getSerialized($maximumRowsInDataTable = null,
+ $maximumRowsInSubDataTable = null,
+ $columnToSortByBeforeTruncation = null)
+ {
+ static $depth = 0;
+
+ if ($depth > self::$maximumDepthLevelAllowed) {
+ $depth = 0;
+ throw new Exception("Maximum recursion level of " . self::$maximumDepthLevelAllowed . " reached. Maybe you have set a DataTable_Row with an associated DataTable belonging already to one of its parent tables?");
+ }
+ if (!is_null($maximumRowsInDataTable)) {
+ $this->filter('AddSummaryRow',
+ array($maximumRowsInDataTable - 1,
+ Piwik_DataTable::LABEL_SUMMARY_ROW,
+ $columnToSortByBeforeTruncation)
+ );
+ }
+
+ // For each row, get the serialized row
+ // If it is associated to a sub table, get the serialized table recursively ;
+ // but returns all serialized tables and subtable in an array of 1 dimension
+ $aSerializedDataTable = array();
+ foreach ($this->rows as $row) {
+ if (($idSubTable = $row->getIdSubDataTable()) !== null) {
+ $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
+ $depth++;
+ $aSerializedDataTable = $aSerializedDataTable + $subTable->getSerialized($maximumRowsInSubDataTable, $maximumRowsInSubDataTable, $columnToSortByBeforeTruncation);
+ $depth--;
+ }
+ }
+ // we load the current Id of the DataTable
+ $forcedId = $this->getId();
+
+ // if the datatable is the parent we force the Id at 0 (this is part of the specification)
+ if ($depth == 0) {
+ $forcedId = 0;
+ }
+
+ // we then serialize the rows and store them in the serialized dataTable
+ $addToRows = array(self::ID_SUMMARY_ROW => $this->summaryRow);
+ if ($this->parents && Piwik_Config::getInstance()->General['enable_archive_parents_of_datatable']) {
+ $addToRows[self::ID_PARENTS] = $this->parents;
+ }
+ $aSerializedDataTable[$forcedId] = serialize($this->rows + $addToRows);
+ foreach ($this->rows as &$row) {
+ $row->cleanPostSerialize();
+ }
+
+ return $aSerializedDataTable;
+ }
+
+ /**
+ * Load a serialized string of a datatable.
+ *
+ * Does not load recursively all the sub DataTable.
+ * They will be loaded only when requesting them specifically.
+ *
+ * The function creates all the necessary DataTable_Row
+ *
+ * @param string $stringSerialized string of serialized datatable
+ * @throws Exception
+ */
+ public function addRowsFromSerializedArray($stringSerialized)
+ {
+ $serialized = unserialize($stringSerialized);
+ if ($serialized === false) {
+ throw new Exception("The unserialization has failed!");
+ }
+ $this->addRowsFromArray($serialized);
+ }
+
+ /**
+ * Loads the DataTable from a PHP array data structure
+ *
+ * @param array $array Array with the following structure
+ * array(
+ * // row1
+ * array(
+ * Piwik_DataTable_Row::COLUMNS => array( col1_name => value1, col2_name => value2, ...),
+ * Piwik_DataTable_Row::METADATA => array( metadata1_name => value1, ...), // see Piwik_DataTable_Row
+ * ),
+ * // row2
+ * array( ... ),
+ * )
+ */
+ public function addRowsFromArray($array)
+ {
+ foreach ($array as $id => $row) {
+ if ($id == self::ID_PARENTS) {
+ $this->parents = $row;
+ continue;
+ }
+
+ if (is_array($row)) {
+ $row = new Piwik_DataTable_Row($row);
+ }
+ if ($id == self::ID_SUMMARY_ROW) {
+ $this->summaryRow = $row;
+ } else {
+ $this->addRow($row);
+ }
+ }
+ }
+
+ /**
+ * Loads the data from a simple php array.
+ * Basically maps a simple multidimensional php array to a DataTable.
+ * Not recursive (if a row contains a php array itself, it won't be loaded)
+ *
+ * @param array $array Array with the simple structure:
+ * array(
+ * array( col1_name => valueA, col2_name => valueC, ...),
+ * array( col1_name => valueB, col2_name => valueD, ...),
+ * )
+ * @throws Exception
+ * @return
+ */
+ public function addRowsFromSimpleArray($array)
+ {
+ if (count($array) === 0) {
+ return;
+ }
+
+ // we define an exception we may throw if at one point we notice that we cannot handle the data structure
+ $e = new Exception(" Data structure returned is not convertible in the requested format." .
+ " Try to call this method with the parameters '&format=original&serialize=1'" .
+ "; you will get the original php data structure serialized." .
+ " The data structure looks like this: \n \$data = " . var_export($array, true) . "; ");
+
+
+ // first pass to see if the array has the structure
+ // array(col1_name => val1, col2_name => val2, etc.)
+ // with val* that are never arrays (only strings/numbers/bool/etc.)
+ // if we detect such a "simple" data structure we convert it to a row with the correct columns' names
+ $thisIsNotThatSimple = false;
+
+ foreach ($array as $columnValue) {
+ if (is_array($columnValue) || is_object($columnValue)) {
+ $thisIsNotThatSimple = true;
+ break;
+ }
+ }
+ if ($thisIsNotThatSimple === false) {
+ // case when the array is indexed by the default numeric index
+ if (array_keys($array) == array_keys(array_fill(0, count($array), true))) {
+ foreach ($array as $row) {
+ $this->addRow(new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array($row))));
+ }
+ } else {
+ $this->addRow(new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => $array)));
+ }
+ // we have converted our simple array to one single row
+ // => we exit the method as the job is now finished
+ return;
+ }
+
+ foreach ($array as $key => $row) {
+ // stuff that looks like a line
+ if (is_array($row)) {
+ /**
+ * We make sure we can convert this PHP array without losing information.
+ * We are able to convert only simple php array (no strings keys, no sub arrays, etc.)
+ *
+ */
+
+ // if the key is a string it means that some information was contained in this key.
+ // it cannot be lost during the conversion. Because we are not able to handle properly
+ // this key, we throw an explicit exception.
+ if (is_string($key)) {
+ throw $e;
+ }
+ // if any of the sub elements of row is an array we cannot handle this data structure...
+ foreach ($row as $subRow) {
+ if (is_array($subRow)) {
+ throw $e;
+ }
+ }
+ $row = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => $row));
+ } // other (string, numbers...) => we build a line from this value
+ else {
+ $row = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array($key => $row)));
+ }
+ $this->addRow($row);
+ }
+ }
+
+ /**
+ * Rewrites the input $array
+ * array (
+ * LABEL => array(col1 => X, col2 => Y),
+ * LABEL2 => array(col1 => X, col2 => Y),
+ * )
+ * to the structure
+ * array (
+ * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL, col1 => X, col2 => Y)),
+ * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL2, col1 => X, col2 => Y)),
+ * )
+ *
+ * It also works with array having only one value per row, eg.
+ * array (
+ * LABEL => X,
+ * LABEL2 => Y,
+ * )
+ * would be converted to the structure
+ * array (
+ * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL, 'value' => X)),
+ * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL2, 'value' => Y)),
+ * )
+ *
+ * The optional parameter $subtablePerLabel is an array of subTable associated to the rows of the $array
+ * For example if $subtablePerLabel is given
+ * array(
+ * LABEL => #Piwik_DataTable_ForLABEL,
+ * LABEL2 => #Piwik_DataTable_ForLABEL2,
+ * )
+ *
+ * the $array would become
+ * array (
+ * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL, col1 => X, col2 => Y),
+ * Piwik_DataTable_Row::DATATABLE_ASSOCIATED => #ID DataTable For LABEL
+ * ),
+ * array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL2, col1 => X, col2 => Y)
+ * Piwik_DataTable_Row::DATATABLE_ASSOCIATED => #ID2 DataTable For LABEL2
+ * ),
+ * )
+ *
+ * @param array $array See method description
+ * @param array|null $subtablePerLabel See method description
+ */
+ public function addRowsFromArrayWithIndexLabel($array, $subtablePerLabel = null)
+ {
+ $cleanRow = array();
+ foreach ($array as $label => $row) {
+ if (!is_array($row)) {
+ $row = array('value' => $row);
+ }
+ $cleanRow[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = null;
+ // we put the 'label' column first as it looks prettier in API results
+ $cleanRow[Piwik_DataTable_Row::COLUMNS] = array('label' => $label) + $row;
+ if (!is_null($subtablePerLabel)
+ // some rows of this table don't have subtables
+ // (for example case of campaigns without keywords)
+ && isset($subtablePerLabel[$label])
+ ) {
+ $cleanRow[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $subtablePerLabel[$label];
+ }
+ $this->addRow(new Piwik_DataTable_Row($cleanRow));
+ }
+ }
+
+ /**
+ * Set the array of parent ids
+ *
+ * @param array $parents
+ */
+ public function setParents($parents)
+ {
+ $this->parents = $parents;
+ }
+
+ /**
+ * Get parents
+ *
+ * @return array of all parents, root level first
+ */
+ public function getParents()
+ {
+ if ($this->parents == null) {
+ return array();
+ }
+ return $this->parents;
+ }
+
+ /**
+ * Sets the maximum nesting level to at least a certain value. If the current value is
+ * greater than the supplied level, the maximum nesting level is not changed.
+ *
+ * @param int $atLeastLevel
+ */
+ static public function setMaximumDepthLevelAllowedAtLeast($atLeastLevel)
+ {
+ self::$maximumDepthLevelAllowed = max($atLeastLevel, self::$maximumDepthLevelAllowed);
+ if (self::$maximumDepthLevelAllowed < 1) {
+ self::$maximumDepthLevelAllowed = 1;
+ }
+ }
+
+ /**
+ * Returns all table metadata.
+ *
+ * @return array
+ */
+ public function getAllTableMetadata()
+ {
+ return $this->metadata;
+ }
+
+ /**
+ * Returns metadata by name.
+ *
+ * @param string $name The metadata name.
+ * @return mixed
+ */
+ public function getMetadata($name)
+ {
+ if (!isset($this->metadata[$name])) {
+ return false;
+ }
+ return $this->metadata[$name];
+ }
+
+ /**
+ * Sets a metadata value by name.
+ *
+ * @param string $name The metadata name.
+ * @param mixed $value
+ */
+ public function setMetadata($name, $value)
+ {
+ $this->metadata[$name] = $value;
+ }
+
+ /**
+ * Sets the maximum number of rows allowed in this datatable (including the summary
+ * row). If adding more then the allowed number of rows is attempted, the extra
+ * rows are added to the summary row.
+ *
+ * @param int|null $maximumAllowedRows
+ */
+ public function setMaximumAllowedRows($maximumAllowedRows)
+ {
+ $this->maximumAllowedRows = $maximumAllowedRows;
+ }
+
+ /**
+ * Traverses a DataTable tree using an array of labels and returns the row
+ * it finds or false if it cannot find one, and the number of segments of
+ * the path successfully walked.
+ *
+ * If $missingRowColumns is supplied, the specified path is created. When
+ * a subtable is encountered w/o the queried label, a new row is created
+ * with the label, and a subtable is added to the row.
+ *
+ * @param array $path The path to walk. An array of label values.
+ * @param array|false $missingRowColumns
+ * The default columns to use when creating new arrays.
+ * If this parameter is supplied, new rows will be
+ * created if labels cannot be found.
+ * @param int $maxSubtableRows The maximum number of allowed rows in new
+ * subtables.
+ * @return array First element is the found row or false. Second element is
+ * the number of path segments walked. If a row is found, this
+ * will be == to count($path). Otherwise, it will be the index
+ * of the path segment that we could not find.
+ */
+ public function walkPath($path, $missingRowColumns = false, $maxSubtableRows = 0)
+ {
+ $pathLength = count($path);
+
+ $table = $this;
+ $next = false;
+ for ($i = 0; $i < $pathLength; ++$i) {
+ $segment = $path[$i];
+
+ $next = $table->getRowFromLabel($segment);
+ if ($next === false) {
+ // if there is no table to advance to, and we're not adding missing rows, return false
+ if ($missingRowColumns === false) {
+ return array(false, $i);
+ } else // if we're adding missing rows, add a new row
+ {
+ $row = new Piwik_DataTable_Row_DataTableSummary();
+ $row->setColumns(array('label' => $segment) + $missingRowColumns);
+
+ $next = $table->addRow($row);
+
+ if ($next !== $row) // if the row wasn't added, the table is full
+ {
+ // Summary row, has no metadata
+ $next->deleteMetadata();
+ return array($next, $i);
+ }
+ }
+ }
+
+ $table = $next->getSubtable();
+ if ($table === false) {
+ // if the row has no table (and thus no child rows), and we're not adding
+ // missing rows, return false
+ if ($missingRowColumns === false) {
+ return array(false, $i);
+ } else if ($i != $pathLength - 1) // create subtable if missing, but only if not on the last segment
+ {
+ $table = new Piwik_DataTable();
+ $table->setMaximumAllowedRows($maxSubtableRows);
+ $next->setSubtable($table);
+ // Summary row, has no metadata
+ $next->deleteMetadata();
+ }
+ }
+ }
+
+ return array($next, $i);
+ }
+
+ /**
+ * Returns a new DataTable that contains the rows of each of this table's
+ * subtables.
+ *
+ * @param string|false $labelColumn If supplied the label of the parent row will be
+ * added to a new column in each subtable row. If set to,
+ * 'label' each subtable row's label will be prepended w/
+ * the parent row's label.
+ * @param bool $useMetadataColumn If true and if $labelColumn is supplied, the parent row's
+ * label will be added as metadata.
+ * @return Piwik_DataTable
+ */
+ public function mergeSubtables($labelColumn = false, $useMetadataColumn = false)
+ {
+ $result = new Piwik_DataTable();
+ foreach ($this->getRows() as $row) {
+ $subtable = $row->getSubtable();
+ if ($subtable !== false) {
+ $parentLabel = $row->getColumn('label');
+
+ // add a copy of each subtable row to the new datatable
+ foreach ($subtable->getRows() as $id => $subRow) {
+ $copy = clone $subRow;
+
+ // if the summary row, add it to the existing summary row (or add a new one)
+ if ($id == self::ID_SUMMARY_ROW) {
+ $existing = $result->getRowFromId(self::ID_SUMMARY_ROW);
+ if ($existing === false) {
+ $result->addSummaryRow($copy);
+ } else {
+ $existing->sumRow($copy);
+ }
+ } else {
+ if ($labelColumn !== false) {
+ // if we're modifying the subtable's rows' label column, then we make
+ // sure to prepend the existing label w/ the parent row's label. otherwise
+ // we're just adding the parent row's label as a new column/metadata.
+ $newLabel = $parentLabel;
+ if ($labelColumn == 'label') {
+ $newLabel .= ' - ' . $copy->getColumn('label');
+ }
+
+ // modify the child row's label or add new column/metadata
+ if ($useMetadataColumn) {
+ $copy->setMetadata($labelColumn, $newLabel);
+ } else {
+ $copy->setColumn($labelColumn, $newLabel);
+ }
+ }
+
+ $result->addRow($copy);
+ }
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Returns a new DataTable created with data from a 'simple' array.
+ *
+ * @param array $array
+ * @return Piwik_DataTable
+ */
+ public static function makeFromSimpleArray($array)
+ {
+ $dataTable = new Piwik_DataTable();
+ $dataTable->addRowsFromSimpleArray($array);
+ return $dataTable;
+ }
}
diff --git a/core/DataTable/Array.php b/core/DataTable/Array.php
index e07867dfc2..367a1b1aa6 100644
--- a/core/DataTable/Array.php
+++ b/core/DataTable/Array.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,421 +12,398 @@
/**
* The DataTable_Array is a way to store an array of dataTable.
* The Piwik_DataTable_Array implements some of the features of the Piwik_DataTable such as queueFilter, getRowsCount.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Array
{
- /**
- * Array containing the DataTable withing this Piwik_DataTable_Array
- *
- * @var Piwik_DataTable[]
- */
- protected $array = array();
-
- /**
- * This is the label used to index the tables.
- * For example if the tables are indexed using the timestamp of each period
- * eg. $this->array[1045886960] = new Piwik_DataTable();
- * the keyName would be 'timestamp'.
- *
- * This label is used in the Renderer (it becomes a column name or the XML description tag)
- *
- * @var string
- */
- protected $keyName = 'defaultKeyName';
-
- /**
- * Returns the keyName string @see self::$keyName
- *
- * @return string
- */
- public function getKeyName()
- {
- return $this->keyName;
- }
-
- /**
- * Set the keyName @see self::$keyName
- *
- * @param string $name
- */
- public function setKeyName($name)
- {
- $this->keyName = $name;
- }
-
- /**
- * Returns the number of DataTable in this DataTable_Array
- *
- * @return int
- */
- public function getRowsCount()
- {
- return count($this->array);
- }
-
- /**
- * Queue a filter to the DataTable_Array will queue this filter to every DataTable of the DataTable_Array.
- *
- * @param string $className Filter name, eg. Piwik_DataTable_Filter_Limit
- * @param array $parameters Filter parameters, eg. array( 50, 10 )
- */
- public function queueFilter( $className, $parameters = array() )
- {
- foreach($this->array as $table)
- {
- $table->queueFilter($className, $parameters);
- }
- }
-
- /**
- * Apply the filters previously queued to each of the DataTable of this DataTable_Array.
- */
- public function applyQueuedFilters()
- {
- foreach($this->array as $table)
- {
- $table->applyQueuedFilters();
- }
- }
-
- /**
- * Apply a filter to all tables in the array
- *
- * @param string $className Name of filter class
- * @param array $parameters Filter parameters
- */
- public function filter($className, $parameters = array())
- {
- foreach($this->array as $id => $table)
- {
- $table->filter($className, $parameters);
- }
- }
-
- /**
- * Returns the array of DataTable
- *
- * @return Piwik_DataTable[]
- */
- public function getArray()
- {
- return $this->array;
- }
-
- /**
- * Returns the table with the specified label.
- *
- * @param string $label
- * @return Piwik_DataTable
- */
- public function getTable($label)
- {
- return $this->array[$label];
+ /**
+ * Array containing the DataTable withing this Piwik_DataTable_Array
+ *
+ * @var Piwik_DataTable[]
+ */
+ protected $array = array();
+
+ /**
+ * This is the label used to index the tables.
+ * For example if the tables are indexed using the timestamp of each period
+ * eg. $this->array[1045886960] = new Piwik_DataTable();
+ * the keyName would be 'timestamp'.
+ *
+ * This label is used in the Renderer (it becomes a column name or the XML description tag)
+ *
+ * @var string
+ */
+ protected $keyName = 'defaultKeyName';
+
+ /**
+ * Returns the keyName string @see self::$keyName
+ *
+ * @return string
+ */
+ public function getKeyName()
+ {
+ return $this->keyName;
+ }
+
+ /**
+ * Set the keyName @see self::$keyName
+ *
+ * @param string $name
+ */
+ public function setKeyName($name)
+ {
+ $this->keyName = $name;
+ }
+
+ /**
+ * Returns the number of DataTable in this DataTable_Array
+ *
+ * @return int
+ */
+ public function getRowsCount()
+ {
+ return count($this->array);
+ }
+
+ /**
+ * Queue a filter to the DataTable_Array will queue this filter to every DataTable of the DataTable_Array.
+ *
+ * @param string $className Filter name, eg. Piwik_DataTable_Filter_Limit
+ * @param array $parameters Filter parameters, eg. array( 50, 10 )
+ */
+ public function queueFilter($className, $parameters = array())
+ {
+ foreach ($this->array as $table) {
+ $table->queueFilter($className, $parameters);
+ }
+ }
+
+ /**
+ * Apply the filters previously queued to each of the DataTable of this DataTable_Array.
+ */
+ public function applyQueuedFilters()
+ {
+ foreach ($this->array as $table) {
+ $table->applyQueuedFilters();
+ }
+ }
+
+ /**
+ * Apply a filter to all tables in the array
+ *
+ * @param string $className Name of filter class
+ * @param array $parameters Filter parameters
+ */
+ public function filter($className, $parameters = array())
+ {
+ foreach ($this->array as $id => $table) {
+ $table->filter($className, $parameters);
+ }
+ }
+
+ /**
+ * Returns the array of DataTable
+ *
+ * @return Piwik_DataTable[]
+ */
+ public function getArray()
+ {
+ return $this->array;
+ }
+
+ /**
+ * Returns the table with the specified label.
+ *
+ * @param string $label
+ * @return Piwik_DataTable
+ */
+ public function getTable($label)
+ {
+ return $this->array[$label];
+ }
+
+ /**
+ * Returns the first row
+ * This method can be used to treat DataTable and DataTable_Array in the same way
+ *
+ * @return Piwik_DataTable_Row
+ */
+ public function getFirstRow()
+ {
+ foreach ($this->array as $table) {
+ $row = $table->getFirstRow();
+ if ($row !== false) {
+ return $row;
+ }
+ }
+ return false;
}
-
+
+ /**
+ * Adds a new DataTable to the DataTable_Array
+ *
+ * @param Piwik_DataTable $table
+ * @param string $label Label used to index this table in the array
+ */
+ public function addTable($table, $label)
+ {
+ $this->array[$label] = $table;
+ }
+
+ /**
+ * Returns a string output of this DataTable_Array (applying the default renderer to every DataTable
+ * of this DataTable_Array).
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $renderer = new Piwik_DataTable_Renderer_Console();
+ $renderer->setTable($this);
+ return (string)$renderer;
+ }
+
/**
- * Returns the first row
- * This method can be used to treat DataTable and DataTable_Array in the same way
- *
- * @return Piwik_DataTable_Row
- */
- public function getFirstRow()
- {
- foreach ($this->array as $table)
- {
- $row = $table->getFirstRow();
- if ($row !== false)
- {
- return $row;
- }
- }
- return false;
- }
-
- /**
- * Adds a new DataTable to the DataTable_Array
- *
- * @param Piwik_DataTable $table
- * @param string $label Label used to index this table in the array
- */
- public function addTable( $table, $label )
- {
- $this->array[$label] = $table;
- }
-
- /**
- * Returns a string output of this DataTable_Array (applying the default renderer to every DataTable
- * of this DataTable_Array).
- *
- * @return string
- */
- public function __toString()
- {
- $renderer = new Piwik_DataTable_Renderer_Console();
- $renderer->setTable($this);
- return (string)$renderer;
- }
-
- /**
- * @see Piwik_DataTable::enableRecursiveSort()
- */
- public function enableRecursiveSort()
- {
- foreach($this->array as $table)
- {
- $table->enableRecursiveSort();
- }
- }
-
- /**
- * Renames the given column
- *
- * @see Piwik_DataTable::renameColumn
- * @param string $oldName
- * @param string $newName
- */
- public function renameColumn($oldName, $newName)
- {
- foreach($this->array as $table)
- {
- $table->renameColumn($oldName, $newName);
- }
- }
-
- /**
- * Deletes the given columns
- *
- * @see Piwik_DataTable::deleteColumns
- * @param array $columns
- */
- public function deleteColumns($columns)
- {
- foreach($this->array as $table)
- {
- $table->deleteColumns($columns);
- }
- }
+ * @see Piwik_DataTable::enableRecursiveSort()
+ */
+ public function enableRecursiveSort()
+ {
+ foreach ($this->array as $table) {
+ $table->enableRecursiveSort();
+ }
+ }
+
+ /**
+ * Renames the given column
+ *
+ * @see Piwik_DataTable::renameColumn
+ * @param string $oldName
+ * @param string $newName
+ */
+ public function renameColumn($oldName, $newName)
+ {
+ foreach ($this->array as $table) {
+ $table->renameColumn($oldName, $newName);
+ }
+ }
+
+ /**
+ * Deletes the given columns
+ *
+ * @see Piwik_DataTable::deleteColumns
+ * @param array $columns
+ */
+ public function deleteColumns($columns)
+ {
+ foreach ($this->array as $table) {
+ $table->deleteColumns($columns);
+ }
+ }
public function deleteRow($id)
{
- foreach($this->array as $table)
- {
+ foreach ($this->array as $table) {
$table->deleteRow($id);
}
}
- /**
- * Deletes the given column
- *
- * @see Piwik_DataTable::deleteColumn
- * @param string $column
- */
- public function deleteColumn($column)
- {
- foreach($this->array as $table)
- {
- $table->deleteColumn($column);
- }
- }
-
- /**
- * Returns the array containing all rows values in all data tables for the requested column
- *
- * @param string $name
- * @return array
- */
- public function getColumn( $name )
- {
- $values = array();
- foreach($this->array as $table)
- {
- $moreValues = $table->getColumn($name);
- foreach ($moreValues as &$value) {
- $values[] = $value;
- }
- }
- return $values;
- }
-
- /**
- * Merges the rows of every child DataTable into a new DataTable and
- * returns it. This function will also set the label of the merged rows
- * to the label of the DataTable they were originally from.
- *
- * The result of this function is determined by the type of DataTable
- * this instance holds. If this DataTable_Array instance holds an array
- * of DataTables, this function will transform it from:
- * <code>
- * Label 0:
- * DataTable(row1)
- * Label 1:
- * DataTable(row2)
- * </code>
- * to:
- * <code>
- * DataTable(row1[label = 'Label 0'], row2[label = 'Label 1'])
- * </code>
- *
- * If this instance holds an array of DataTable_Arrays, this function will
- * transform it from:
- * <code>
- * Outer Label 0: // the outer DataTable_Array
- * Inner Label 0: // one of the inner DataTable_Arrays
- * DataTable(row1)
- * Inner Label 1:
- * DataTable(row2)
- * Outer Label 1:
- * Inner Label 0:
- * DataTable(row3)
- * Inner Label 1:
- * DataTable(row4)
- * </code>
- * to:
- * <code>
- * Inner Label 0:
- * DataTable(row1[label = 'Outer Label 0'], row3[label = 'Outer Label 1'])
- * Inner Label 1:
- * DataTable(row2[label = 'Outer Label 0'], row4[label = 'Outer Label 1'])
- * </code>
- *
- * In addition, if this instance holds an array of DataTable_Arrays, the
- * metadata of the first child is used as the metadata of the result.
- *
- * This function can be used, for example, to smoosh IndexedBySite archive
- * query results into one DataTable w/ different rows differentiated by site ID.
- *
- * @return Piwik_DataTable|Piwik_DataTable_Array
- */
- public function mergeChildren()
- {
- $firstChild = reset($this->array);
-
- if ($firstChild instanceof Piwik_DataTable_Array)
- {
- $result = $firstChild->getEmptyClone();
-
- foreach ($this->array as $label => $subTableArray)
- {
- foreach ($subTableArray->array as $innerLabel => $subTable)
- {
- if (!isset($result->array[$innerLabel]))
- {
- $dataTable = new Piwik_DataTable();
- $dataTable->metadata = $subTable->metadata;
-
- $result->addTable($dataTable, $innerLabel);
- }
-
- $this->copyRowsAndSetLabel($result->array[$innerLabel], $subTable, $label);
- }
- }
- }
- else
- {
- $result = new Piwik_DataTable();
-
- foreach ($this->array as $label => $subTable)
- {
- $this->copyRowsAndSetLabel($result, $subTable, $label);
- }
- }
-
- return $result;
- }
-
- /**
- * Utility function used by mergeChildren. Copies the rows from one table,
- * sets their 'label' columns to a value and adds them to another table.
- *
- * @param Piwik_DataTable $toTable The table to copy rows to.
- * @param Piwik_DataTable $fromTable The table to copy rows from.
- * @param string $label The value to set the 'label' column of every copied row.
- */
- private function copyRowsAndSetLabel($toTable, $fromTable, $label)
- {
- foreach ($fromTable->getRows() as $fromRow)
- {
- $oldColumns = $fromRow->getColumns();
- unset($oldColumns['label']);
-
- $columns = array_merge(array('label' => $label), $oldColumns);
- $row = new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => $columns,
- Piwik_DataTable_Row::METADATA => $fromRow->getMetadata(),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $fromRow->getIdSubDataTable()
- ));
- $toTable->addRow($row);
- }
- }
-
- /**
- * Adds a DataTable to all the tables in this array
- * NOTE: Will only add $tableToSum if the childTable has some rows
- *
- * @param Piwik_DataTable $tableToSum
- */
- public function addDataTable( Piwik_DataTable $tableToSum )
- {
- foreach ($this->getArray() as $childTable)
- {
- if($childTable->getRowsCount() > 0)
- {
- $childTable->addDataTable($tableToSum);
- }
- }
- }
-
- /**
- * Returns a new DataTable_Array w/ child tables that have had their
- * subtables merged.
- *
- * @see Piwik_DataTable::mergeSubtables
- *
- * @return Piwik_DataTable_Array
- */
- public function mergeSubtables()
- {
- $result = $this->getEmptyClone();
- foreach ($this->array as $label => $childTable)
- {
- $result->addTable($childTable->mergeSubtables(), $label);
- }
- return $result;
- }
-
- /**
- * Returns a new DataTable_Array w/o any child DataTables, but with
- * the same key name as this instance.
- *
- * @return Piwik_DataTable_Array
- */
- public function getEmptyClone()
- {
- $newTableArray = new Piwik_DataTable_Array;
- $newTableArray->setKeyName($this->getKeyName());
- return $newTableArray;
- }
-
- /**
- * Returns the intersection of cildrends' meta data arrays
- *
- * @param string $name The metadata name.
- * @return mixed
- */
- public function getMetadataIntersectArray( $name )
- {
- $data = array();
- foreach ($this->getArray() as $childTable)
- {
- $childData = $childTable->getMetadata($name);
- if (is_array($childData))
- {
- $data = array_intersect($data, $childData);
- }
- }
- return array_values($data);
- }
-
+
+ /**
+ * Deletes the given column
+ *
+ * @see Piwik_DataTable::deleteColumn
+ * @param string $column
+ */
+ public function deleteColumn($column)
+ {
+ foreach ($this->array as $table) {
+ $table->deleteColumn($column);
+ }
+ }
+
+ /**
+ * Returns the array containing all rows values in all data tables for the requested column
+ *
+ * @param string $name
+ * @return array
+ */
+ public function getColumn($name)
+ {
+ $values = array();
+ foreach ($this->array as $table) {
+ $moreValues = $table->getColumn($name);
+ foreach ($moreValues as &$value) {
+ $values[] = $value;
+ }
+ }
+ return $values;
+ }
+
+ /**
+ * Merges the rows of every child DataTable into a new DataTable and
+ * returns it. This function will also set the label of the merged rows
+ * to the label of the DataTable they were originally from.
+ *
+ * The result of this function is determined by the type of DataTable
+ * this instance holds. If this DataTable_Array instance holds an array
+ * of DataTables, this function will transform it from:
+ * <code>
+ * Label 0:
+ * DataTable(row1)
+ * Label 1:
+ * DataTable(row2)
+ * </code>
+ * to:
+ * <code>
+ * DataTable(row1[label = 'Label 0'], row2[label = 'Label 1'])
+ * </code>
+ *
+ * If this instance holds an array of DataTable_Arrays, this function will
+ * transform it from:
+ * <code>
+ * Outer Label 0: // the outer DataTable_Array
+ * Inner Label 0: // one of the inner DataTable_Arrays
+ * DataTable(row1)
+ * Inner Label 1:
+ * DataTable(row2)
+ * Outer Label 1:
+ * Inner Label 0:
+ * DataTable(row3)
+ * Inner Label 1:
+ * DataTable(row4)
+ * </code>
+ * to:
+ * <code>
+ * Inner Label 0:
+ * DataTable(row1[label = 'Outer Label 0'], row3[label = 'Outer Label 1'])
+ * Inner Label 1:
+ * DataTable(row2[label = 'Outer Label 0'], row4[label = 'Outer Label 1'])
+ * </code>
+ *
+ * In addition, if this instance holds an array of DataTable_Arrays, the
+ * metadata of the first child is used as the metadata of the result.
+ *
+ * This function can be used, for example, to smoosh IndexedBySite archive
+ * query results into one DataTable w/ different rows differentiated by site ID.
+ *
+ * @return Piwik_DataTable|Piwik_DataTable_Array
+ */
+ public function mergeChildren()
+ {
+ $firstChild = reset($this->array);
+
+ if ($firstChild instanceof Piwik_DataTable_Array) {
+ $result = $firstChild->getEmptyClone();
+
+ foreach ($this->array as $label => $subTableArray) {
+ foreach ($subTableArray->array as $innerLabel => $subTable) {
+ if (!isset($result->array[$innerLabel])) {
+ $dataTable = new Piwik_DataTable();
+ $dataTable->metadata = $subTable->metadata;
+
+ $result->addTable($dataTable, $innerLabel);
+ }
+
+ $this->copyRowsAndSetLabel($result->array[$innerLabel], $subTable, $label);
+ }
+ }
+ } else {
+ $result = new Piwik_DataTable();
+
+ foreach ($this->array as $label => $subTable) {
+ $this->copyRowsAndSetLabel($result, $subTable, $label);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Utility function used by mergeChildren. Copies the rows from one table,
+ * sets their 'label' columns to a value and adds them to another table.
+ *
+ * @param Piwik_DataTable $toTable The table to copy rows to.
+ * @param Piwik_DataTable $fromTable The table to copy rows from.
+ * @param string $label The value to set the 'label' column of every copied row.
+ */
+ private function copyRowsAndSetLabel($toTable, $fromTable, $label)
+ {
+ foreach ($fromTable->getRows() as $fromRow) {
+ $oldColumns = $fromRow->getColumns();
+ unset($oldColumns['label']);
+
+ $columns = array_merge(array('label' => $label), $oldColumns);
+ $row = new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => $columns,
+ Piwik_DataTable_Row::METADATA => $fromRow->getMetadata(),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $fromRow->getIdSubDataTable()
+ ));
+ $toTable->addRow($row);
+ }
+ }
+
+ /**
+ * Adds a DataTable to all the tables in this array
+ * NOTE: Will only add $tableToSum if the childTable has some rows
+ *
+ * @param Piwik_DataTable $tableToSum
+ */
+ public function addDataTable(Piwik_DataTable $tableToSum)
+ {
+ foreach ($this->getArray() as $childTable) {
+ if ($childTable->getRowsCount() > 0) {
+ $childTable->addDataTable($tableToSum);
+ }
+ }
+ }
+
+ /**
+ * Returns a new DataTable_Array w/ child tables that have had their
+ * subtables merged.
+ *
+ * @see Piwik_DataTable::mergeSubtables
+ *
+ * @return Piwik_DataTable_Array
+ */
+ public function mergeSubtables()
+ {
+ $result = $this->getEmptyClone();
+ foreach ($this->array as $label => $childTable) {
+ $result->addTable($childTable->mergeSubtables(), $label);
+ }
+ return $result;
+ }
+
+ /**
+ * Returns a new DataTable_Array w/o any child DataTables, but with
+ * the same key name as this instance.
+ *
+ * @return Piwik_DataTable_Array
+ */
+ public function getEmptyClone()
+ {
+ $newTableArray = new Piwik_DataTable_Array;
+ $newTableArray->setKeyName($this->getKeyName());
+ return $newTableArray;
+ }
+
+ /**
+ * Returns the intersection of cildrends' meta data arrays
+ *
+ * @param string $name The metadata name.
+ * @return mixed
+ */
+ public function getMetadataIntersectArray($name)
+ {
+ $data = array();
+ foreach ($this->getArray() as $childTable) {
+ $childData = $childTable->getMetadata($name);
+ if (is_array($childData)) {
+ $data = array_intersect($data, $childData);
+ }
+ }
+ return array_values($data);
+ }
+
}
diff --git a/core/DataTable/Filter.php b/core/DataTable/Filter.php
index adaeea453a..455c5aa6e7 100644
--- a/core/DataTable/Filter.php
+++ b/core/DataTable/Filter.php
@@ -1,80 +1,77 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * A filter is applied instantly to a given DataTable and can
- * - remove rows
+ * A filter is applied instantly to a given DataTable and can
+ * - remove rows
* - change columns values (lowercase the strings, truncate, etc.)
* - add/remove columns or metadata (compute percentage values, add an 'icon' metadata based on the label, etc.)
* - add/remove/edit sub DataTable associated to some rows
* - whatever you can imagine
- *
- * The concept is very simple: the filter is given the DataTable
+ *
+ * The concept is very simple: the filter is given the DataTable
* and can do whatever is necessary on the data (in the filter() method).
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
abstract class Piwik_DataTable_Filter
{
- /**
- * @var bool
- */
- protected $enableRecursive = false;
+ /**
+ * @var bool
+ */
+ protected $enableRecursive = false;
- /**
- * @throws Exception
- * @param Piwik_DataTable $table
- */
- public function __construct($table)
- {
- if(!($table instanceof Piwik_DataTable))
- {
- throw new Exception("The filter accepts only a Piwik_DataTable object.");
- }
- }
+ /**
+ * @throws Exception
+ * @param Piwik_DataTable $table
+ */
+ public function __construct($table)
+ {
+ if (!($table instanceof Piwik_DataTable)) {
+ throw new Exception("The filter accepts only a Piwik_DataTable object.");
+ }
+ }
- /**
- * Filters the given data table
- *
- * @param Piwik_DataTable $table
- */
- abstract public function filter($table);
+ /**
+ * Filters the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ abstract public function filter($table);
- /**
- * Enables/Disables the recursive mode
- *
- * @param bool $bool
- */
- public function enableRecursive($bool)
- {
- $this->enableRecursive = (bool)$bool;
- }
+ /**
+ * Enables/Disables the recursive mode
+ *
+ * @param bool $bool
+ */
+ public function enableRecursive($bool)
+ {
+ $this->enableRecursive = (bool)$bool;
+ }
- /**
- * Filters a subtable
- *
- * @param Piwik_DataTable_Row $row
- * @return mixed
- */
- public function filterSubTable(Piwik_DataTable_Row $row)
- {
- if(!$this->enableRecursive)
- {
- return;
- }
- if($row->isSubtableLoaded())
- {
- $subTable = Piwik_DataTable_Manager::getInstance()->getTable( $row->getIdSubDataTable() );
- $this->filter($subTable);
- }
- }
+ /**
+ * Filters a subtable
+ *
+ * @param Piwik_DataTable_Row $row
+ * @return mixed
+ */
+ public function filterSubTable(Piwik_DataTable_Row $row)
+ {
+ if (!$this->enableRecursive) {
+ return;
+ }
+ if ($row->isSubtableLoaded()) {
+ $subTable = Piwik_DataTable_Manager::getInstance()->getTable($row->getIdSubDataTable());
+ $this->filter($subTable);
+ }
+ }
}
diff --git a/core/DataTable/Filter/AddColumnsProcessedMetrics.php b/core/DataTable/Filter/AddColumnsProcessedMetrics.php
index 9929c64df7..63dbf4898d 100644
--- a/core/DataTable/Filter/AddColumnsProcessedMetrics.php
+++ b/core/DataTable/Filter/AddColumnsProcessedMetrics.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,120 +15,112 @@
*/
class Piwik_DataTable_Filter_AddColumnsProcessedMetrics extends Piwik_DataTable_Filter
{
- protected $invalidDivision = 0;
- protected $roundPrecision = 2;
- protected $deleteRowsWithNoVisit = true;
+ protected $invalidDivision = 0;
+ protected $roundPrecision = 2;
+ protected $deleteRowsWithNoVisit = true;
- /**
- * @param Piwik_DataTable $table
- * @param bool $deleteRowsWithNoVisit Automatically set to true when filter_add_columns_when_show_all_columns is found in the API request
- * @return Piwik_DataTable_Filter_AddColumnsProcessedMetrics
- */
- public function __construct( $table, $deleteRowsWithNoVisit = true )
- {
- $this->deleteRowsWithNoVisit = $deleteRowsWithNoVisit;
- parent::__construct($table);
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @param bool $deleteRowsWithNoVisit Automatically set to true when filter_add_columns_when_show_all_columns is found in the API request
+ * @return Piwik_DataTable_Filter_AddColumnsProcessedMetrics
+ */
+ public function __construct($table, $deleteRowsWithNoVisit = true)
+ {
+ $this->deleteRowsWithNoVisit = $deleteRowsWithNoVisit;
+ parent::__construct($table);
+ }
- /**
- * Filters the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- $rowsIdToDelete = array();
- foreach($table->getRows() as $key => $row)
- {
- $nbVisits = $this->getColumn($row, Piwik_Archive::INDEX_NB_VISITS);
- $nbActions = $this->getColumn($row, Piwik_Archive::INDEX_NB_ACTIONS);
- if($nbVisits == 0
- && $nbActions == 0
- && $this->deleteRowsWithNoVisit)
- {
- // case of keyword/website/campaign with a conversion for this day,
- // but no visit, we don't show it
- $rowsIdToDelete[] = $key;
- continue;
- }
-
- $nbVisitsConverted = (int)$this->getColumn($row, Piwik_Archive::INDEX_NB_VISITS_CONVERTED);
- if($nbVisitsConverted > 0)
- {
- $conversionRate = round(100 * $nbVisitsConverted / $nbVisits, $this->roundPrecision);
- try {
- $row->addColumn('conversion_rate', $conversionRate."%");
- } catch(Exception $e) {
- // conversion_rate can be defined upstream apparently? FIXME
- }
- }
-
- if($nbVisits == 0)
- {
- $actionsPerVisit = $averageTimeOnSite = $bounceRate = $this->invalidDivision;
- }
- else
- {
- // nb_actions / nb_visits => Actions/visit
- // sum_visit_length / nb_visits => Avg. Time on Site
- // bounce_count / nb_visits => Bounce Rate
- $actionsPerVisit = round($nbActions / $nbVisits, $this->roundPrecision);
- $visitLength = $this->getColumn($row, Piwik_Archive::INDEX_SUM_VISIT_LENGTH);
- $averageTimeOnSite = round($visitLength / $nbVisits, $rounding = 0);
- $bounceRate = round(100 * $this->getColumn($row, Piwik_Archive::INDEX_BOUNCE_COUNT) / $nbVisits, $this->roundPrecision);
- }
- try {
- $row->addColumn('nb_actions_per_visit', $actionsPerVisit);
- $row->addColumn('avg_time_on_site', $averageTimeOnSite);
- // It could be useful for API users to have raw sum length value.
- //$row->addMetadata('sum_visit_length', $visitLength);
- } catch(Exception $e) {}
-
- try {
- $row->addColumn('bounce_rate', $bounceRate."%");
- } catch(Exception $e) {}
-
- $this->filterSubTable($row);
- }
- $table->deleteRows($rowsIdToDelete);
- }
+ /**
+ * Filters the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ $rowsIdToDelete = array();
+ foreach ($table->getRows() as $key => $row) {
+ $nbVisits = $this->getColumn($row, Piwik_Archive::INDEX_NB_VISITS);
+ $nbActions = $this->getColumn($row, Piwik_Archive::INDEX_NB_ACTIONS);
+ if ($nbVisits == 0
+ && $nbActions == 0
+ && $this->deleteRowsWithNoVisit
+ ) {
+ // case of keyword/website/campaign with a conversion for this day,
+ // but no visit, we don't show it
+ $rowsIdToDelete[] = $key;
+ continue;
+ }
+
+ $nbVisitsConverted = (int)$this->getColumn($row, Piwik_Archive::INDEX_NB_VISITS_CONVERTED);
+ if ($nbVisitsConverted > 0) {
+ $conversionRate = round(100 * $nbVisitsConverted / $nbVisits, $this->roundPrecision);
+ try {
+ $row->addColumn('conversion_rate', $conversionRate . "%");
+ } catch (Exception $e) {
+ // conversion_rate can be defined upstream apparently? FIXME
+ }
+ }
+
+ if ($nbVisits == 0) {
+ $actionsPerVisit = $averageTimeOnSite = $bounceRate = $this->invalidDivision;
+ } else {
+ // nb_actions / nb_visits => Actions/visit
+ // sum_visit_length / nb_visits => Avg. Time on Site
+ // bounce_count / nb_visits => Bounce Rate
+ $actionsPerVisit = round($nbActions / $nbVisits, $this->roundPrecision);
+ $visitLength = $this->getColumn($row, Piwik_Archive::INDEX_SUM_VISIT_LENGTH);
+ $averageTimeOnSite = round($visitLength / $nbVisits, $rounding = 0);
+ $bounceRate = round(100 * $this->getColumn($row, Piwik_Archive::INDEX_BOUNCE_COUNT) / $nbVisits, $this->roundPrecision);
+ }
+ try {
+ $row->addColumn('nb_actions_per_visit', $actionsPerVisit);
+ $row->addColumn('avg_time_on_site', $averageTimeOnSite);
+ // It could be useful for API users to have raw sum length value.
+ //$row->addMetadata('sum_visit_length', $visitLength);
+ } catch (Exception $e) {
+ }
+
+ try {
+ $row->addColumn('bounce_rate', $bounceRate . "%");
+ } catch (Exception $e) {
+ }
+
+ $this->filterSubTable($row);
+ }
+ $table->deleteRows($rowsIdToDelete);
+ }
+
+ /**
+ * Returns column from a given row.
+ * Will work with 2 types of datatable
+ * - raw datatables coming from the archive DB, which columns are int indexed
+ * - datatables processed resulting of API calls, which columns have human readable english names
+ *
+ * @param Piwik_DataTable_Row $row
+ * @param int $columnIdRaw see consts in Piwik_Archive::
+ * @param bool $mappingIdToName
+ * @return mixed Value of column, false if not found
+ */
+ protected function getColumn($row, $columnIdRaw, $mappingIdToName = false)
+ {
+ if (empty($mappingIdToName)) {
+ $mappingIdToName = Piwik_Archive::$mappingFromIdToName;
+ }
+ $columnIdReadable = $mappingIdToName[$columnIdRaw];
+ if ($row instanceof Piwik_DataTable_Row) {
+ $raw = $row->getColumn($columnIdRaw);
+ if ($raw !== false) {
+ return $raw;
+ }
+ return $row->getColumn($columnIdReadable);
+ }
+ if (isset($row[$columnIdRaw])) {
+ return $row[$columnIdRaw];
+ }
+ if (isset($row[$columnIdReadable])) {
+ return $row[$columnIdReadable];
+ }
+ return false;
+ }
- /**
- * Returns column from a given row.
- * Will work with 2 types of datatable
- * - raw datatables coming from the archive DB, which columns are int indexed
- * - datatables processed resulting of API calls, which columns have human readable english names
- *
- * @param Piwik_DataTable_Row $row
- * @param int $columnIdRaw see consts in Piwik_Archive::
- * @param bool $mappingIdToName
- * @return mixed Value of column, false if not found
- */
- protected function getColumn($row, $columnIdRaw, $mappingIdToName = false)
- {
- if(empty($mappingIdToName))
- {
- $mappingIdToName = Piwik_Archive::$mappingFromIdToName;
- }
- $columnIdReadable = $mappingIdToName[$columnIdRaw];
- if($row instanceof Piwik_DataTable_Row)
- {
- $raw = $row->getColumn($columnIdRaw);
- if($raw !== false)
- {
- return $raw;
- }
- return $row->getColumn($columnIdReadable);
- }
- if(isset($row[$columnIdRaw]))
- {
- return $row[$columnIdRaw];
- }
- if(isset($row[$columnIdReadable]))
- {
- return $row[$columnIdReadable];
- }
- return false;
- }
-
}
diff --git a/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php b/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php
index b49b69fd72..2d9e6cf02a 100644
--- a/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php
+++ b/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,200 +15,182 @@
*/
class Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal extends Piwik_DataTable_Filter_AddColumnsProcessedMetrics
{
- /**
- * Process main goal metrics: conversion rate, revenue per visit
- */
- const GOALS_MINIMAL_REPORT = -2;
-
- /**
- * Process main goal metrics, and conversion rate per goal
- */
- const GOALS_OVERVIEW = -1;
-
- /**
- * Process all goal and per-goal metrics
- */
- const GOALS_FULL_TABLE = 0;
-
- /**
- * Adds processed goal metrics to a table:
- * - global conversion rate,
- * - global revenue per visit.
- * Can also process per-goal metrics:
- * - conversion rate
- * - nb conversions
- * - revenue per visit
- *
- * @param Piwik_DataTable $table
- * @param bool $enable should be true (automatically set to true when filter_update_columns_when_show_all_goals is found in the API request)
- * @param string $processOnlyIdGoal Defines what metrics to add (don't process metrics when you don't display them)
- * If self::GOALS_FULL_TABLE, all Goal metrics (and per goal metrics) will be processed
- * If self::GOALS_OVERVIEW, only the main goal metrics will be added
- * If an int > 0, then will process only metrics for this specific Goal
- * @return Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal
- */
- public function __construct( $table, $enable = true, $processOnlyIdGoal )
- {
- $this->processOnlyIdGoal = $processOnlyIdGoal;
- $this->isEcommerce = $this->processOnlyIdGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER || $this->processOnlyIdGoal == Piwik_Archive::LABEL_ECOMMERCE_CART;
- parent::__construct($table);
- // Ensure that all rows with no visit but conversions will be displayed
- $this->deleteRowsWithNoVisit = false;
- }
-
- /**
- * Filters the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- // Add standard processed metrics
- parent::filter($table);
- $roundingPrecision = Piwik_Tracker_GoalManager::REVENUE_PRECISION;
- $expectedColumns = array();
- foreach($table->getRows() as $key => $row)
- {
- $currentColumns = $row->getColumns();
- $newColumns = array();
-
- // visits could be undefined when there is a conversion but no visit
- $nbVisits = (int)$this->getColumn($row, Piwik_Archive::INDEX_NB_VISITS);
- $conversions = (int)$this->getColumn($row, Piwik_Archive::INDEX_NB_CONVERSIONS);
- $goals = $this->getColumn($currentColumns, Piwik_Archive::INDEX_GOALS);
- if($goals)
- {
- $revenue = 0;
- foreach($goals as $goalId => $columnValue)
- {
- if($goalId == Piwik_Archive::LABEL_ECOMMERCE_CART)
- {
- continue;
- }
- if($goalId >= Piwik_Tracker_GoalManager::IDGOAL_ORDER
- || $goalId == Piwik_Archive::LABEL_ECOMMERCE_ORDER
- )
- {
- $revenue += (int)$this->getColumn($columnValue, Piwik_Archive::INDEX_GOAL_REVENUE, Piwik_Archive::$mappingFromIdToNameGoal);
- }
- }
-
- if($revenue == 0)
- {
- $revenue = (int)$this->getColumn($currentColumns, Piwik_Archive::INDEX_REVENUE);
- }
- if(!isset($currentColumns['revenue_per_visit']))
- {
- // If no visit for this metric, but some conversions, we still want to display some kind of "revenue per visit"
- // even though it will actually be in this edge case "Revenue per conversion"
- $revenuePerVisit = $this->invalidDivision;
- if($nbVisits > 0
- || $conversions > 0)
- {
- $revenuePerVisit = round( $revenue / ($nbVisits == 0 ? $conversions : $nbVisits), $roundingPrecision );
- }
- $newColumns['revenue_per_visit'] = $revenuePerVisit;
- }
- if($this->processOnlyIdGoal == self::GOALS_MINIMAL_REPORT)
- {
- $row->addColumns($newColumns);
- continue;
- }
- // Display per goal metrics
- // - conversion rate
- // - conversions
- // - revenue per visit
- foreach($goals as $goalId => $columnValue)
- {
- $goalId = str_replace("idgoal=", "", $goalId);
- if( ($this->processOnlyIdGoal > self::GOALS_FULL_TABLE
- || $this->isEcommerce)
- && $this->processOnlyIdGoal != $goalId)
- {
- continue;
- }
- $conversions = (int)$this->getColumn($columnValue, Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS, Piwik_Archive::$mappingFromIdToNameGoal);
-
- // Goal Conversion rate
- $name = 'goal_' . $goalId . '_conversion_rate';
- if($nbVisits == 0)
- {
- $value = $this->invalidDivision;
- }
- else
- {
- $value = round(100 * $conversions / $nbVisits, $roundingPrecision);
- }
- $newColumns[$name] = $value. "%";
- $expectedColumns[$name] = true;
-
- // When the table is displayed by clicking on the flag icon, we only display the columns
- // Visits, Conversions, Per goal conversion rate, Revenue
- if($this->processOnlyIdGoal == self::GOALS_OVERVIEW)
- {
- continue;
- }
-
- // Goal Conversions
- $name = 'goal_' . $goalId . '_nb_conversions';
- $newColumns[$name] = $conversions;
- $expectedColumns[$name] = true;
-
- // Goal Revenue per visit
- $name = 'goal_' . $goalId . '_revenue_per_visit';
- // See comment above for $revenuePerVisit
- $goalRevenue = (float)$this->getColumn($columnValue, Piwik_Archive::INDEX_GOAL_REVENUE, Piwik_Archive::$mappingFromIdToNameGoal);
- $revenuePerVisit = round( $goalRevenue / ($nbVisits == 0 ? $conversions : $nbVisits), $roundingPrecision );
- $newColumns[$name] = $revenuePerVisit;
- $expectedColumns[$name] = true;
-
- // Total revenue
- $name = 'goal_' . $goalId . '_revenue';
- $newColumns[$name] = $goalRevenue;
- $expectedColumns[$name] = true;
-
- if($this->isEcommerce )
- {
-
- // AOV Average Order Value
- $name = 'goal_' . $goalId . '_avg_order_revenue';
- $newColumns[$name] = $goalRevenue / $conversions;
- $expectedColumns[$name] = true;
-
- // Items qty
- $name = 'goal_' . $goalId . '_items';
- $newColumns[$name] = $this->getColumn($columnValue, Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS, Piwik_Archive::$mappingFromIdToNameGoal);
- $expectedColumns[$name] = true;
- }
- }
- }
-
- // conversion_rate can be defined upstream apparently? FIXME
- try {
- $row->addColumns($newColumns);
- }catch(Exception $e) {
- }
- }
- $expectedColumns['revenue_per_visit'] = true;
-
- // make sure all goals values are set, 0 by default
- // if no value then sorting would put at the end
- $expectedColumns = array_keys($expectedColumns);
- $rows = $table->getRows();
- foreach($rows as &$row)
- {
- foreach($expectedColumns as $name)
- {
- if(false === $row->getColumn($name))
- {
- $value = 0;
- if(strpos($name, 'conversion_rate') !== false)
- {
- $value = '0%';
- }
- $row->addColumn($name, $value);
- }
- }
- }
- }
+ /**
+ * Process main goal metrics: conversion rate, revenue per visit
+ */
+ const GOALS_MINIMAL_REPORT = -2;
+
+ /**
+ * Process main goal metrics, and conversion rate per goal
+ */
+ const GOALS_OVERVIEW = -1;
+
+ /**
+ * Process all goal and per-goal metrics
+ */
+ const GOALS_FULL_TABLE = 0;
+
+ /**
+ * Adds processed goal metrics to a table:
+ * - global conversion rate,
+ * - global revenue per visit.
+ * Can also process per-goal metrics:
+ * - conversion rate
+ * - nb conversions
+ * - revenue per visit
+ *
+ * @param Piwik_DataTable $table
+ * @param bool $enable should be true (automatically set to true when filter_update_columns_when_show_all_goals is found in the API request)
+ * @param string $processOnlyIdGoal Defines what metrics to add (don't process metrics when you don't display them)
+ * If self::GOALS_FULL_TABLE, all Goal metrics (and per goal metrics) will be processed
+ * If self::GOALS_OVERVIEW, only the main goal metrics will be added
+ * If an int > 0, then will process only metrics for this specific Goal
+ * @return Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal
+ */
+ public function __construct($table, $enable = true, $processOnlyIdGoal)
+ {
+ $this->processOnlyIdGoal = $processOnlyIdGoal;
+ $this->isEcommerce = $this->processOnlyIdGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER || $this->processOnlyIdGoal == Piwik_Archive::LABEL_ECOMMERCE_CART;
+ parent::__construct($table);
+ // Ensure that all rows with no visit but conversions will be displayed
+ $this->deleteRowsWithNoVisit = false;
+ }
+
+ /**
+ * Filters the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ // Add standard processed metrics
+ parent::filter($table);
+ $roundingPrecision = Piwik_Tracker_GoalManager::REVENUE_PRECISION;
+ $expectedColumns = array();
+ foreach ($table->getRows() as $key => $row) {
+ $currentColumns = $row->getColumns();
+ $newColumns = array();
+
+ // visits could be undefined when there is a conversion but no visit
+ $nbVisits = (int)$this->getColumn($row, Piwik_Archive::INDEX_NB_VISITS);
+ $conversions = (int)$this->getColumn($row, Piwik_Archive::INDEX_NB_CONVERSIONS);
+ $goals = $this->getColumn($currentColumns, Piwik_Archive::INDEX_GOALS);
+ if ($goals) {
+ $revenue = 0;
+ foreach ($goals as $goalId => $columnValue) {
+ if ($goalId == Piwik_Archive::LABEL_ECOMMERCE_CART) {
+ continue;
+ }
+ if ($goalId >= Piwik_Tracker_GoalManager::IDGOAL_ORDER
+ || $goalId == Piwik_Archive::LABEL_ECOMMERCE_ORDER
+ ) {
+ $revenue += (int)$this->getColumn($columnValue, Piwik_Archive::INDEX_GOAL_REVENUE, Piwik_Archive::$mappingFromIdToNameGoal);
+ }
+ }
+
+ if ($revenue == 0) {
+ $revenue = (int)$this->getColumn($currentColumns, Piwik_Archive::INDEX_REVENUE);
+ }
+ if (!isset($currentColumns['revenue_per_visit'])) {
+ // If no visit for this metric, but some conversions, we still want to display some kind of "revenue per visit"
+ // even though it will actually be in this edge case "Revenue per conversion"
+ $revenuePerVisit = $this->invalidDivision;
+ if ($nbVisits > 0
+ || $conversions > 0
+ ) {
+ $revenuePerVisit = round($revenue / ($nbVisits == 0 ? $conversions : $nbVisits), $roundingPrecision);
+ }
+ $newColumns['revenue_per_visit'] = $revenuePerVisit;
+ }
+ if ($this->processOnlyIdGoal == self::GOALS_MINIMAL_REPORT) {
+ $row->addColumns($newColumns);
+ continue;
+ }
+ // Display per goal metrics
+ // - conversion rate
+ // - conversions
+ // - revenue per visit
+ foreach ($goals as $goalId => $columnValue) {
+ $goalId = str_replace("idgoal=", "", $goalId);
+ if (($this->processOnlyIdGoal > self::GOALS_FULL_TABLE
+ || $this->isEcommerce)
+ && $this->processOnlyIdGoal != $goalId
+ ) {
+ continue;
+ }
+ $conversions = (int)$this->getColumn($columnValue, Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS, Piwik_Archive::$mappingFromIdToNameGoal);
+
+ // Goal Conversion rate
+ $name = 'goal_' . $goalId . '_conversion_rate';
+ if ($nbVisits == 0) {
+ $value = $this->invalidDivision;
+ } else {
+ $value = round(100 * $conversions / $nbVisits, $roundingPrecision);
+ }
+ $newColumns[$name] = $value . "%";
+ $expectedColumns[$name] = true;
+
+ // When the table is displayed by clicking on the flag icon, we only display the columns
+ // Visits, Conversions, Per goal conversion rate, Revenue
+ if ($this->processOnlyIdGoal == self::GOALS_OVERVIEW) {
+ continue;
+ }
+
+ // Goal Conversions
+ $name = 'goal_' . $goalId . '_nb_conversions';
+ $newColumns[$name] = $conversions;
+ $expectedColumns[$name] = true;
+
+ // Goal Revenue per visit
+ $name = 'goal_' . $goalId . '_revenue_per_visit';
+ // See comment above for $revenuePerVisit
+ $goalRevenue = (float)$this->getColumn($columnValue, Piwik_Archive::INDEX_GOAL_REVENUE, Piwik_Archive::$mappingFromIdToNameGoal);
+ $revenuePerVisit = round($goalRevenue / ($nbVisits == 0 ? $conversions : $nbVisits), $roundingPrecision);
+ $newColumns[$name] = $revenuePerVisit;
+ $expectedColumns[$name] = true;
+
+ // Total revenue
+ $name = 'goal_' . $goalId . '_revenue';
+ $newColumns[$name] = $goalRevenue;
+ $expectedColumns[$name] = true;
+
+ if ($this->isEcommerce) {
+
+ // AOV Average Order Value
+ $name = 'goal_' . $goalId . '_avg_order_revenue';
+ $newColumns[$name] = $goalRevenue / $conversions;
+ $expectedColumns[$name] = true;
+
+ // Items qty
+ $name = 'goal_' . $goalId . '_items';
+ $newColumns[$name] = $this->getColumn($columnValue, Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS, Piwik_Archive::$mappingFromIdToNameGoal);
+ $expectedColumns[$name] = true;
+ }
+ }
+ }
+
+ // conversion_rate can be defined upstream apparently? FIXME
+ try {
+ $row->addColumns($newColumns);
+ } catch (Exception $e) {
+ }
+ }
+ $expectedColumns['revenue_per_visit'] = true;
+
+ // make sure all goals values are set, 0 by default
+ // if no value then sorting would put at the end
+ $expectedColumns = array_keys($expectedColumns);
+ $rows = $table->getRows();
+ foreach ($rows as &$row) {
+ foreach ($expectedColumns as $name) {
+ if (false === $row->getColumn($name)) {
+ $value = 0;
+ if (strpos($name, 'conversion_rate') !== false) {
+ $value = '0%';
+ }
+ $row->addColumn($name, $value);
+ }
+ }
+ }
+ }
}
diff --git a/core/DataTable/Filter/AddConstantMetadata.php b/core/DataTable/Filter/AddConstantMetadata.php
index f0fc9040e2..4bff5aa813 100644
--- a/core/DataTable/Filter/AddConstantMetadata.php
+++ b/core/DataTable/Filter/AddConstantMetadata.php
@@ -1,17 +1,17 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Add a new metadata column to the table.
- *
+ *
* This is used to add a column containing the logo width and height of the countries flag icons.
* This value is fixed for all icons so we simply add the same value for all rows.
*
@@ -20,30 +20,29 @@
*/
class Piwik_DataTable_Filter_AddConstantMetadata extends Piwik_DataTable_Filter
{
- /**
- * Creates a new filter and sets all required parameters
- *
- * @param Piwik_DataTable $table
- * @param string $metadataName
- * @param mixed $metadataValue
- */
- public function __construct( $table, $metadataName, $metadataValue )
- {
- parent::__construct($table);
- $this->name = $metadataName;
- $this->value = $metadataValue;
- }
+ /**
+ * Creates a new filter and sets all required parameters
+ *
+ * @param Piwik_DataTable $table
+ * @param string $metadataName
+ * @param mixed $metadataValue
+ */
+ public function __construct($table, $metadataName, $metadataValue)
+ {
+ parent::__construct($table);
+ $this->name = $metadataName;
+ $this->value = $metadataValue;
+ }
- /**
- * Filters the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $row)
- {
- $row->addMetadata($this->name, $this->value);
- }
- }
+ /**
+ * Filters the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $row) {
+ $row->addMetadata($this->name, $this->value);
+ }
+ }
}
diff --git a/core/DataTable/Filter/AddSummaryRow.php b/core/DataTable/Filter/AddSummaryRow.php
index d9a9966022..5bb2a5e10c 100644
--- a/core/DataTable/Filter/AddSummaryRow.php
+++ b/core/DataTable/Filter/AddSummaryRow.php
@@ -27,70 +27,63 @@
*/
class Piwik_DataTable_Filter_AddSummaryRow extends Piwik_DataTable_Filter
{
- /**
- * Creates a new filter and set all required parameters
- *
- * @param Piwik_DataTable $table
- * @param int $startRowToSummarize
- * @param int $labelSummaryRow
- * @param null $columnToSortByBeforeTruncating
- * @param bool $deleteRows
- */
- public function __construct($table,
- $startRowToSummarize,
- $labelSummaryRow = Piwik_DataTable::LABEL_SUMMARY_ROW,
- $columnToSortByBeforeTruncating = null,
- $deleteRows = true )
- {
- parent::__construct($table);
- $this->startRowToSummarize = $startRowToSummarize;
- $this->labelSummaryRow = $labelSummaryRow;
- $this->columnToSortByBeforeTruncating = $columnToSortByBeforeTruncating;
- $this->deleteRows = $deleteRows;
- }
+ /**
+ * Creates a new filter and set all required parameters
+ *
+ * @param Piwik_DataTable $table
+ * @param int $startRowToSummarize
+ * @param int $labelSummaryRow
+ * @param null $columnToSortByBeforeTruncating
+ * @param bool $deleteRows
+ */
+ public function __construct($table,
+ $startRowToSummarize,
+ $labelSummaryRow = Piwik_DataTable::LABEL_SUMMARY_ROW,
+ $columnToSortByBeforeTruncating = null,
+ $deleteRows = true)
+ {
+ parent::__construct($table);
+ $this->startRowToSummarize = $startRowToSummarize;
+ $this->labelSummaryRow = $labelSummaryRow;
+ $this->columnToSortByBeforeTruncating = $columnToSortByBeforeTruncating;
+ $this->deleteRows = $deleteRows;
+ }
- /**
- * Adds a summary row to the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- if($table->getRowsCount() <= $this->startRowToSummarize + 1)
- {
- return;
- }
- $table->filter('Sort',
- array( $this->columnToSortByBeforeTruncating, 'desc'));
-
- $rows = $table->getRows();
- $count = $table->getRowsCount();
- $newRow = new Piwik_DataTable_Row();
- for($i = $this->startRowToSummarize; $i < $count; $i++)
- {
- if(!isset($rows[$i]))
- {
- // case when the last row is a summary row, it is not indexed by $cout but by Piwik_DataTable::ID_SUMMARY_ROW
- $summaryRow = $table->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW);
-
- //FIXME: I'm not sure why it could return false, but it was reported in: http://forum.piwik.org/read.php?2,89324,page=1#msg-89442
- if($summaryRow)
- {
- $newRow->sumRow($summaryRow, $enableCopyMetadata = false);
- }
- }
- else
- {
- $newRow->sumRow($rows[$i], $enableCopyMetadata = false);
- }
- }
-
- $newRow->setColumns(array('label' => $this->labelSummaryRow) + $newRow->getColumns());
- if ($this->deleteRows)
- {
- $table->filter('Limit', array(0, $this->startRowToSummarize));
- }
- $table->addSummaryRow($newRow);
- unset($rows);
- }
+ /**
+ * Adds a summary row to the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ if ($table->getRowsCount() <= $this->startRowToSummarize + 1) {
+ return;
+ }
+ $table->filter('Sort',
+ array($this->columnToSortByBeforeTruncating, 'desc'));
+
+ $rows = $table->getRows();
+ $count = $table->getRowsCount();
+ $newRow = new Piwik_DataTable_Row();
+ for ($i = $this->startRowToSummarize; $i < $count; $i++) {
+ if (!isset($rows[$i])) {
+ // case when the last row is a summary row, it is not indexed by $cout but by Piwik_DataTable::ID_SUMMARY_ROW
+ $summaryRow = $table->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW);
+
+ //FIXME: I'm not sure why it could return false, but it was reported in: http://forum.piwik.org/read.php?2,89324,page=1#msg-89442
+ if ($summaryRow) {
+ $newRow->sumRow($summaryRow, $enableCopyMetadata = false);
+ }
+ } else {
+ $newRow->sumRow($rows[$i], $enableCopyMetadata = false);
+ }
+ }
+
+ $newRow->setColumns(array('label' => $this->labelSummaryRow) + $newRow->getColumns());
+ if ($this->deleteRows) {
+ $table->filter('Limit', array(0, $this->startRowToSummarize));
+ }
+ $table->addSummaryRow($newRow);
+ unset($rows);
+ }
}
diff --git a/core/DataTable/Filter/BeautifyRangeLabels.php b/core/DataTable/Filter/BeautifyRangeLabels.php
index 4b8a8a3709..329bcf3308 100644
--- a/core/DataTable/Filter/BeautifyRangeLabels.php
+++ b/core/DataTable/Filter/BeautifyRangeLabels.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -28,145 +28,132 @@
*/
class Piwik_DataTable_Filter_BeautifyRangeLabels extends Piwik_DataTable_Filter_ColumnCallbackReplace
{
- /**
- * The string to use when the range being beautified is between 1-1 units.
- * @var string
- */
- protected $labelSingular;
+ /**
+ * The string to use when the range being beautified is between 1-1 units.
+ * @var string
+ */
+ protected $labelSingular;
+
+ /**
+ * The format string to use when the range being beautified references more than
+ * one unit.
+ * @var string
+ */
+ protected $labelPlural;
+
+ /**
+ * Constructor.
+ *
+ * @param Piwik_DataTable $table The DataTable that will be filtered.
+ * @param string $labelSingular The string to use when the range being beautified
+ * is equal to '1-1 units'.
+ * @param string $labelPlural The string to use when the range being beautified
+ * references more than one unit. This must be a format
+ * string that takes one string parameter.
+ */
+ public function __construct($table, $labelSingular, $labelPlural)
+ {
+ parent::__construct($table, 'label', array($this, 'beautify'), array());
+
+ $this->labelSingular = $labelSingular;
+ $this->labelPlural = $labelPlural;
+ }
+
+ /**
+ * Beautifies a range label and returns the pretty result.
+ *
+ * @param string $value The range string. This must be in either a '$min-$max' format
+ * a '$min+' format.
+ * @return string The pretty range label.
+ */
+ public function beautify($value)
+ {
+ // if there's more than one element, handle as a range w/ an upper bound
+ if (strpos($value, "-") !== false) {
+ // get the range
+ sscanf($value, "%d - %d", $lowerBound, $upperBound);
+
+ // if the lower bound is the same as the upper bound make sure the singular label
+ // is used
+ if ($lowerBound == $upperBound) {
+ return $this->getSingleUnitLabel($value, $lowerBound);
+ } else {
+ return $this->getRangeLabel($value, $lowerBound, $upperBound);
+ }
+ } // if there's one element, handle as a range w/ no upper bound
+ else {
+ // get the lower bound
+ sscanf($value, "%d", $lowerBound);
- /**
- * The format string to use when the range being beautified references more than
- * one unit.
- * @var string
- */
- protected $labelPlural;
+ if ($lowerBound !== NULL) {
+ $plusEncoded = urlencode('+');
+ $plusLen = strlen($plusEncoded);
+ $len = strlen($value);
- /**
- * Constructor.
- *
- * @param Piwik_DataTable $table The DataTable that will be filtered.
- * @param string $labelSingular The string to use when the range being beautified
- * is equal to '1-1 units'.
- * @param string $labelPlural The string to use when the range being beautified
- * references more than one unit. This must be a format
- * string that takes one string parameter.
- */
- public function __construct( $table, $labelSingular, $labelPlural )
- {
- parent::__construct($table, 'label', array($this, 'beautify'), array());
-
- $this->labelSingular = $labelSingular;
- $this->labelPlural = $labelPlural;
- }
+ // if the label doesn't end with a '+', append it
+ if ($len < $plusLen || substr($value, $len - $plusLen) != $plusEncoded) {
+ $value .= $plusEncoded;
+ }
- /**
- * Beautifies a range label and returns the pretty result.
- *
- * @param string $value The range string. This must be in either a '$min-$max' format
- * a '$min+' format.
- * @return string The pretty range label.
- */
- public function beautify( $value )
- {
- // if there's more than one element, handle as a range w/ an upper bound
- if (strpos($value, "-") !== false)
- {
- // get the range
- sscanf($value, "%d - %d", $lowerBound, $upperBound);
-
- // if the lower bound is the same as the upper bound make sure the singular label
- // is used
- if ($lowerBound == $upperBound)
- {
- return $this->getSingleUnitLabel($value, $lowerBound);
- }
- else
- {
- return $this->getRangeLabel($value, $lowerBound, $upperBound);
- }
- }
- // if there's one element, handle as a range w/ no upper bound
- else
- {
- // get the lower bound
- sscanf($value, "%d", $lowerBound);
-
- if ($lowerBound !== NULL)
- {
- $plusEncoded = urlencode('+');
- $plusLen = strlen($plusEncoded);
- $len = strlen($value);
+ return $this->getUnboundedLabel($value, $lowerBound);
+ } else {
+ // if no lower bound can be found, this isn't a valid range. in this case
+ // we assume its a translation key and try to translate it.
+ return Piwik_Translate(trim($value));
+ }
+ }
+ }
- // if the label doesn't end with a '+', append it
- if ($len < $plusLen || substr($value, $len - $plusLen) != $plusEncoded)
- {
- $value .= $plusEncoded;
- }
-
- return $this->getUnboundedLabel($value, $lowerBound);
- }
- else
- {
- // if no lower bound can be found, this isn't a valid range. in this case
- // we assume its a translation key and try to translate it.
- return Piwik_Translate(trim($value));
- }
- }
- }
+ /**
+ * Beautifies and returns a range label whose range spans over one unit, ie
+ * 1-1, 2-2 or 3-3.
+ *
+ * This function can be overridden in derived types to customize beautifcation
+ * behavior based on the range values.
+ *
+ * @param string $oldLabel The original label value.
+ * @param int $lowerBound The lower bound of the range.
+ * @return string The pretty range label.
+ */
+ public function getSingleUnitLabel($oldLabel, $lowerBound)
+ {
+ if ($lowerBound == 1) {
+ return $this->labelSingular;
+ } else {
+ return sprintf($this->labelPlural, $lowerBound);
+ }
+ }
- /**
- * Beautifies and returns a range label whose range spans over one unit, ie
- * 1-1, 2-2 or 3-3.
- *
- * This function can be overridden in derived types to customize beautifcation
- * behavior based on the range values.
- *
- * @param string $oldLabel The original label value.
- * @param int $lowerBound The lower bound of the range.
- * @return string The pretty range label.
- */
- public function getSingleUnitLabel( $oldLabel, $lowerBound )
- {
- if ($lowerBound == 1)
- {
- return $this->labelSingular;
- }
- else
- {
- return sprintf($this->labelPlural, $lowerBound);
- }
- }
+ /**
+ * Beautifies and returns a range label whose range is bounded and spans over
+ * more than one unit, ie 1-5, 5-10 but NOT 11+.
+ *
+ * This function can be overridden in derived types to customize beautifcation
+ * behavior based on the range values.
+ *
+ * @param string $oldLabel The original label value.
+ * @param int $lowerBound The lower bound of the range.
+ * @param int $upperBound The upper bound of the range.
+ * @return string The pretty range label.
+ */
+ public function getRangeLabel($oldLabel, $lowerBound, $upperBound)
+ {
+ return sprintf($this->labelPlural, $oldLabel);
+ }
- /**
- * Beautifies and returns a range label whose range is bounded and spans over
- * more than one unit, ie 1-5, 5-10 but NOT 11+.
- *
- * This function can be overridden in derived types to customize beautifcation
- * behavior based on the range values.
- *
- * @param string $oldLabel The original label value.
- * @param int $lowerBound The lower bound of the range.
- * @param int $upperBound The upper bound of the range.
- * @return string The pretty range label.
- */
- public function getRangeLabel( $oldLabel, $lowerBound, $upperBound )
- {
- return sprintf($this->labelPlural, $oldLabel);
- }
-
- /**
- * Beautifies and returns a range label whose range is unbounded, ie
- * 5+, 10+, etc.
- *
- * This function can be overridden in derived types to customize beautifcation
- * behavior based on the range values.
- *
- * @param string $oldLabel The original label value.
- * @param int $lowerBound The lower bound of the range.
- * @return string The pretty range label.
- */
- public function getUnboundedLabel( $oldLabel, $lowerBound )
- {
- return sprintf($this->labelPlural, $oldLabel);
- }
+ /**
+ * Beautifies and returns a range label whose range is unbounded, ie
+ * 5+, 10+, etc.
+ *
+ * This function can be overridden in derived types to customize beautifcation
+ * behavior based on the range values.
+ *
+ * @param string $oldLabel The original label value.
+ * @param int $lowerBound The lower bound of the range.
+ * @return string The pretty range label.
+ */
+ public function getUnboundedLabel($oldLabel, $lowerBound)
+ {
+ return sprintf($this->labelPlural, $oldLabel);
+ }
}
diff --git a/core/DataTable/Filter/BeautifyTimeRangeLabels.php b/core/DataTable/Filter/BeautifyTimeRangeLabels.php
index 57d00c6490..1a2dffce5a 100644
--- a/core/DataTable/Filter/BeautifyTimeRangeLabels.php
+++ b/core/DataTable/Filter/BeautifyTimeRangeLabels.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -19,107 +19,96 @@
*/
class Piwik_DataTable_Filter_BeautifyTimeRangeLabels extends Piwik_DataTable_Filter_BeautifyRangeLabels
{
- /**
- * A format string used to create pretty range labels when the range's
- * lower bound is between 0 and 60.
- *
- * This format string must take two numeric parameters, one for each
- * range bound.
- */
- protected $labelSecondsPlural;
+ /**
+ * A format string used to create pretty range labels when the range's
+ * lower bound is between 0 and 60.
+ *
+ * This format string must take two numeric parameters, one for each
+ * range bound.
+ */
+ protected $labelSecondsPlural;
+
+ /**
+ * Constructor.
+ *
+ * @param Piwik_DataTable $table The DataTable this filter will run over.
+ * @param string $labelSecondsPlural A string to use when beautifying range labels
+ * whose lower bound is between 0 and 60. Must be
+ * a format string that takes two numeric params.
+ * @param string $labelMinutesSingular A string to use when replacing a range that
+ * equals 60-60 (or 1 minute - 1 minute).
+ * @param string $labelMinutesPlural A string to use when replacing a range that
+ * spans multiple minutes. This must be a
+ * format string that takes one string parameter.
+ */
+ public function __construct($table, $labelSecondsPlural, $labelMinutesSingular, $labelMinutesPlural)
+ {
+ parent::__construct($table, $labelMinutesSingular, $labelMinutesPlural);
+
+ $this->labelSecondsPlural = $labelSecondsPlural;
+ }
- /**
- * Constructor.
- *
- * @param Piwik_DataTable $table The DataTable this filter will run over.
- * @param string $labelSecondsPlural A string to use when beautifying range labels
- * whose lower bound is between 0 and 60. Must be
- * a format string that takes two numeric params.
- * @param string $labelMinutesSingular A string to use when replacing a range that
- * equals 60-60 (or 1 minute - 1 minute).
- * @param string $labelMinutesPlural A string to use when replacing a range that
- * spans multiple minutes. This must be a
- * format string that takes one string parameter.
- */
- public function __construct( $table, $labelSecondsPlural, $labelMinutesSingular, $labelMinutesPlural )
- {
- parent::__construct($table, $labelMinutesSingular, $labelMinutesPlural);
-
- $this->labelSecondsPlural = $labelSecondsPlural;
- }
+ /**
+ * Beautifies and returns a range label whose range spans over one unit, ie
+ * 1-1, 2-2 or 3-3.
+ *
+ * If the lower bound of the range is less than 60 the pretty range label
+ * will be in seconds. Otherwise, it will be in minutes.
+ *
+ * @param string $oldLabel The original label value.
+ * @param int $lowerBound The lower bound of the range.
+ * @return string The pretty range label.
+ */
+ public function getSingleUnitLabel($oldLabel, $lowerBound)
+ {
+ if ($lowerBound < 60) {
+ return sprintf($this->labelSecondsPlural, $lowerBound, $lowerBound);
+ } else if ($lowerBound == 60) {
+ return $this->labelSingular;
+ } else {
+ return sprintf($this->labelPlural, ceil($lowerBound / 60));
+ }
+ }
- /**
- * Beautifies and returns a range label whose range spans over one unit, ie
- * 1-1, 2-2 or 3-3.
- *
- * If the lower bound of the range is less than 60 the pretty range label
- * will be in seconds. Otherwise, it will be in minutes.
- *
- * @param string $oldLabel The original label value.
- * @param int $lowerBound The lower bound of the range.
- * @return string The pretty range label.
- */
- public function getSingleUnitLabel( $oldLabel, $lowerBound )
- {
- if ($lowerBound < 60)
- {
- return sprintf($this->labelSecondsPlural, $lowerBound, $lowerBound);
- }
- else if ($lowerBound == 60)
- {
- return $this->labelSingular;
- }
- else
- {
- return sprintf($this->labelPlural, ceil($lowerBound / 60));
- }
- }
-
- /**
- * Beautifies and returns a range label whose range is bounded and spans over
- * more than one unit, ie 1-5, 5-10 but NOT 11+.
- *
- * If the lower bound of the range is less than 60 the pretty range label
- * will be in seconds. Otherwise, it will be in minutes.
- *
- * @param string $oldLabel The original label value.
- * @param int $lowerBound The lower bound of the range.
- * @param int $upperBound The upper bound of the range.
- * @return string The pretty range label.
- */
- public function getRangeLabel( $oldLabel, $lowerBound, $upperBound )
- {
- if ($lowerBound < 60)
- {
- return sprintf($this->labelSecondsPlural, $lowerBound, $upperBound);
- }
- else
- {
- return sprintf($this->labelPlural, ceil($lowerBound / 60)."-".ceil($upperBound / 60));
- }
- }
+ /**
+ * Beautifies and returns a range label whose range is bounded and spans over
+ * more than one unit, ie 1-5, 5-10 but NOT 11+.
+ *
+ * If the lower bound of the range is less than 60 the pretty range label
+ * will be in seconds. Otherwise, it will be in minutes.
+ *
+ * @param string $oldLabel The original label value.
+ * @param int $lowerBound The lower bound of the range.
+ * @param int $upperBound The upper bound of the range.
+ * @return string The pretty range label.
+ */
+ public function getRangeLabel($oldLabel, $lowerBound, $upperBound)
+ {
+ if ($lowerBound < 60) {
+ return sprintf($this->labelSecondsPlural, $lowerBound, $upperBound);
+ } else {
+ return sprintf($this->labelPlural, ceil($lowerBound / 60) . "-" . ceil($upperBound / 60));
+ }
+ }
- /**
- * Beautifies and returns a range label whose range is unbounded, ie
- * 5+, 10+, etc.
- *
- * If the lower bound of the range is less than 60 the pretty range label
- * will be in seconds. Otherwise, it will be in minutes.
- *
- * @param string $oldLabel The original label value.
- * @param int $lowerBound The lower bound of the range.
- * @return string The pretty range label.
- */
- public function getUnboundedLabel( $oldLabel, $lowerBound )
- {
- if ($lowerBound < 60)
- {
- return sprintf($this->labelSecondsPlural, $lowerBound);
- }
- else
- {
- // since we're using minutes, we use floor so 1801s+ will be 30m+ and not 31m+
- return sprintf($this->labelPlural, "".floor($lowerBound / 60).urlencode('+'));
- }
- }
+ /**
+ * Beautifies and returns a range label whose range is unbounded, ie
+ * 5+, 10+, etc.
+ *
+ * If the lower bound of the range is less than 60 the pretty range label
+ * will be in seconds. Otherwise, it will be in minutes.
+ *
+ * @param string $oldLabel The original label value.
+ * @param int $lowerBound The lower bound of the range.
+ * @return string The pretty range label.
+ */
+ public function getUnboundedLabel($oldLabel, $lowerBound)
+ {
+ if ($lowerBound < 60) {
+ return sprintf($this->labelSecondsPlural, $lowerBound);
+ } else {
+ // since we're using minutes, we use floor so 1801s+ will be 30m+ and not 31m+
+ return sprintf($this->labelPlural, "" . floor($lowerBound / 60) . urlencode('+'));
+ }
+ }
}
diff --git a/core/DataTable/Filter/CalculateEvolutionFilter.php b/core/DataTable/Filter/CalculateEvolutionFilter.php
index 675a3cf8ef..2cacaf2698 100755
--- a/core/DataTable/Filter/CalculateEvolutionFilter.php
+++ b/core/DataTable/Filter/CalculateEvolutionFilter.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,7 +12,7 @@
/**
* A DataTable filter that calculates the evolution of a metric and adds
* it to each row as a percentage.
- *
+ *
* This filter cannot be used as a normal filter since it requires
* corresponding data from another datatable. Instead, to use it,
* you must manually perform a binary filter (see the MultiSites API).
@@ -22,122 +22,119 @@
*/
class Piwik_DataTable_Filter_CalculateEvolutionFilter extends Piwik_DataTable_Filter_ColumnCallbackAddColumnPercentage
{
- /**
- * The the DataTable that contains past data.
- */
- private $pastDataTable;
-
- /**
- * Tells if column being added is the revenue evolution column.
- */
- private $isRevenueEvolution = null;
-
- /**
- * Constructor.
- *
- * @param Piwik_DataTable $table The DataTable being filtered.
- * @param string $columnToAdd
- * @param string $columnToRead
- * @param int $quotientPrecision
- */
- function __construct($table, $pastDataTable, $columnToAdd, $columnToRead, $quotientPrecision = 0)
- {
- parent::__construct(
- $table, $columnToAdd, $columnToRead, $columnToRead, $quotientPrecision, $shouldSkipRows = true);
-
- $this->pastDataTable = $pastDataTable;
-
- $this->isRevenueEvolution = $columnToAdd == 'revenue_evolution';
- }
-
- /**
- * Returns the difference between the column in the specific row and its
- * sister column in the past DataTable.
- *
- * @param Piwik_DataTable_Row $row
- * @return int|float
- */
- protected function getDividend($row)
- {
- $currentValue = $row->getColumn($this->columnValueToRead);
-
- // if the site this is for doesn't support ecommerce & this is for the revenue_evolution column,
- // we don't add the new column
- if ($currentValue === false
- && $this->isRevenueEvolution
- && !Piwik_Site::isEcommerceEnabledFor($row->getColumn('label')))
- {
- return false;
- }
-
- $pastRow = $this->getPastRowFromCurrent($row);
- if ($pastRow)
- {
- $pastValue = $pastRow->getColumn($this->columnValueToRead);
- }
- else
- {
- $pastValue = 0;
- }
-
- return $currentValue - $pastValue;
- }
-
- /**
- * Returns the value of the column in $row's sister row in the past
- * DataTable.
- *
- * @param Piwik_DataTable_Row $row
- * @return int|float
- */
- protected function getDivisor($row)
- {
- $pastRow = $this->getPastRowFromCurrent($row);
- if (!$pastRow) return 0;
-
- return $pastRow->getColumn($this->columnNameUsedAsDivisor);
- }
-
- /**
- * Calculates and formats a quotient based on a divisor and dividend.
- *
- * Unlike Piwik_DataTable_Filter_ColumnCallbackAddColumnPercentage's,
- * version of this method, this method will return 100% if the past
- * value of a metric is 0, and the current value is not 0. For a
- * value representative of an evolution, this makes sense.
- *
- * @param int|float $value The dividend.
- * @param int|float $divisor
- * @return string
- */
- protected function formatValue($value, $divisor)
- {
- $value = self::getPercentageValue($value, $divisor, $this->quotientPrecision);
+ /**
+ * The the DataTable that contains past data.
+ */
+ private $pastDataTable;
+
+ /**
+ * Tells if column being added is the revenue evolution column.
+ */
+ private $isRevenueEvolution = null;
+
+ /**
+ * Constructor.
+ *
+ * @param Piwik_DataTable $table The DataTable being filtered.
+ * @param string $columnToAdd
+ * @param string $columnToRead
+ * @param int $quotientPrecision
+ */
+ function __construct($table, $pastDataTable, $columnToAdd, $columnToRead, $quotientPrecision = 0)
+ {
+ parent::__construct(
+ $table, $columnToAdd, $columnToRead, $columnToRead, $quotientPrecision, $shouldSkipRows = true);
+
+ $this->pastDataTable = $pastDataTable;
+
+ $this->isRevenueEvolution = $columnToAdd == 'revenue_evolution';
+ }
+
+ /**
+ * Returns the difference between the column in the specific row and its
+ * sister column in the past DataTable.
+ *
+ * @param Piwik_DataTable_Row $row
+ * @return int|float
+ */
+ protected function getDividend($row)
+ {
+ $currentValue = $row->getColumn($this->columnValueToRead);
+
+ // if the site this is for doesn't support ecommerce & this is for the revenue_evolution column,
+ // we don't add the new column
+ if ($currentValue === false
+ && $this->isRevenueEvolution
+ && !Piwik_Site::isEcommerceEnabledFor($row->getColumn('label'))
+ ) {
+ return false;
+ }
+
+ $pastRow = $this->getPastRowFromCurrent($row);
+ if ($pastRow) {
+ $pastValue = $pastRow->getColumn($this->columnValueToRead);
+ } else {
+ $pastValue = 0;
+ }
+
+ return $currentValue - $pastValue;
+ }
+
+ /**
+ * Returns the value of the column in $row's sister row in the past
+ * DataTable.
+ *
+ * @param Piwik_DataTable_Row $row
+ * @return int|float
+ */
+ protected function getDivisor($row)
+ {
+ $pastRow = $this->getPastRowFromCurrent($row);
+ if (!$pastRow) return 0;
+
+ return $pastRow->getColumn($this->columnNameUsedAsDivisor);
+ }
+
+ /**
+ * Calculates and formats a quotient based on a divisor and dividend.
+ *
+ * Unlike Piwik_DataTable_Filter_ColumnCallbackAddColumnPercentage's,
+ * version of this method, this method will return 100% if the past
+ * value of a metric is 0, and the current value is not 0. For a
+ * value representative of an evolution, this makes sense.
+ *
+ * @param int|float $value The dividend.
+ * @param int|float $divisor
+ * @return string
+ */
+ protected function formatValue($value, $divisor)
+ {
+ $value = self::getPercentageValue($value, $divisor, $this->quotientPrecision);
$value = self::appendPercentSign($value);
return $value;
- }
-
- /**
- * Utility function. Returns the current row in the past DataTable.
- *
- * @param Piwik_DataTable_Row $row The row in the 'current' DataTable.
- */
- private function getPastRowFromCurrent($row)
- {
- return $this->pastDataTable->getRowFromLabel($row->getColumn('label'));
- }
-
- /**
- * Calculates the evolution percentage for two arbitrary values.
- *
- * @param numeric $currentValue The current metric value.
- * @param numeric $pastValue The value of the metric in the past. We measure the % change
- * from this value to $currentValue.
+ }
+
+ /**
+ * Utility function. Returns the current row in the past DataTable.
+ *
+ * @param Piwik_DataTable_Row $row The row in the 'current' DataTable.
+ */
+ private function getPastRowFromCurrent($row)
+ {
+ return $this->pastDataTable->getRowFromLabel($row->getColumn('label'));
+ }
+
+ /**
+ * Calculates the evolution percentage for two arbitrary values.
+ *
+ * @param numeric $currentValue The current metric value.
+ * @param numeric $pastValue The value of the metric in the past. We measure the % change
+ * from this value to $currentValue.
* @param numeric $quotientPrecision The quotient precision to round to.
* @return string The evolution percent 15%
*/
public static function calculate($currentValue, $pastValue, $quotientPrecision = 0)
- {
+ {
$number = self::getPercentageValue($currentValue - $pastValue, $pastValue, $quotientPrecision);
$number = self::appendPercentSign($number);
return $number;
@@ -152,7 +149,7 @@ class Piwik_DataTable_Filter_CalculateEvolutionFilter extends Piwik_DataTable_Fi
public static function prependPlusSignToNumber($number)
{
if ($number > 0) {
- $number = '+'.$number;
+ $number = '+' . $number;
}
return $number;
}
@@ -161,17 +158,12 @@ class Piwik_DataTable_Filter_CalculateEvolutionFilter extends Piwik_DataTable_Fi
* Returns an evolution percent based on a value & divisor.
*/
private static function getPercentageValue($value, $divisor, $quotientPrecision)
- {
- if($value == 0)
- {
+ {
+ if ($value == 0) {
$evolution = 0;
- }
- elseif($divisor == 0)
- {
+ } elseif ($divisor == 0) {
$evolution = 100;
- }
- else
- {
+ } else {
$evolution = ($value / $divisor) * 100;
}
diff --git a/core/DataTable/Filter/ColumnCallbackAddColumn.php b/core/DataTable/Filter/ColumnCallbackAddColumn.php
index 64882283c5..4aa8b008e4 100755
--- a/core/DataTable/Filter/ColumnCallbackAddColumn.php
+++ b/core/DataTable/Filter/ColumnCallbackAddColumn.php
@@ -1,89 +1,86 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Adds a new column to every row of a DataTable based on the result of callback.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_ColumnCallbackAddColumn extends Piwik_DataTable_Filter
{
- /**
- * The names of the columns to pass to the callback.
- */
- private $columns;
-
- /**
- * The name of the column to add.
- */
- private $columnToAdd;
-
- /**
- * The callback to apply to each row of the DataTable. The result is added as
- * the value of a new column.
- */
- private $functionToApply;
-
- /**
- * Extra parameters to pass to the callback.
- */
- private $functionParameters;
-
- /**
- * Constructor.
- *
- * @param Piwik_DataTable $table The DataTable that will be filtered.
- * @param array|string $columns The names of the columns to pass to the callback.
- * @param string $columnToAdd The name of the column to add.
- * @param mixed $functionToApply The callback to apply to each row of a DataTable.
- * @param array $functionParameters Extra parameters to pass to $functionToApply.
- */
- public function __construct( $table, $columns, $columnToAdd, $functionToApply, $functionParameters = array() )
- {
- parent::__construct($table);
-
- if (!is_array($columns))
- {
- $columns = array($columns);
- }
-
- $this->columns = $columns;
- $this->columnToAdd = $columnToAdd;
- $this->functionToApply = $functionToApply;
- $this->functionParameters = $functionParameters;
- }
-
- /**
- * Executes a callback on every row of the supplied table and adds the result of
- * the callback as a new column to each row.
- *
- * @param Piwik_DataTable $table The table to filter.
- */
- public function filter( $table )
- {
- foreach ($table->getRows() as $row)
- {
- $columnValues = array();
- foreach ($this->columns as $column)
- {
- $columnValues[] = $row->getColumn($column);
- }
-
- $parameters = array_merge($columnValues, $this->functionParameters);
- $value = call_user_func_array($this->functionToApply, $parameters);
-
- $row->setColumn($this->columnToAdd, $value);
-
- $this->filterSubTable($row);
- }
- }
+ /**
+ * The names of the columns to pass to the callback.
+ */
+ private $columns;
+
+ /**
+ * The name of the column to add.
+ */
+ private $columnToAdd;
+
+ /**
+ * The callback to apply to each row of the DataTable. The result is added as
+ * the value of a new column.
+ */
+ private $functionToApply;
+
+ /**
+ * Extra parameters to pass to the callback.
+ */
+ private $functionParameters;
+
+ /**
+ * Constructor.
+ *
+ * @param Piwik_DataTable $table The DataTable that will be filtered.
+ * @param array|string $columns The names of the columns to pass to the callback.
+ * @param string $columnToAdd The name of the column to add.
+ * @param mixed $functionToApply The callback to apply to each row of a DataTable.
+ * @param array $functionParameters Extra parameters to pass to $functionToApply.
+ */
+ public function __construct($table, $columns, $columnToAdd, $functionToApply, $functionParameters = array())
+ {
+ parent::__construct($table);
+
+ if (!is_array($columns)) {
+ $columns = array($columns);
+ }
+
+ $this->columns = $columns;
+ $this->columnToAdd = $columnToAdd;
+ $this->functionToApply = $functionToApply;
+ $this->functionParameters = $functionParameters;
+ }
+
+ /**
+ * Executes a callback on every row of the supplied table and adds the result of
+ * the callback as a new column to each row.
+ *
+ * @param Piwik_DataTable $table The table to filter.
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $row) {
+ $columnValues = array();
+ foreach ($this->columns as $column) {
+ $columnValues[] = $row->getColumn($column);
+ }
+
+ $parameters = array_merge($columnValues, $this->functionParameters);
+ $value = call_user_func_array($this->functionToApply, $parameters);
+
+ $row->setColumn($this->columnToAdd, $value);
+
+ $this->filterSubTable($row);
+ }
+ }
}
diff --git a/core/DataTable/Filter/ColumnCallbackAddColumnPercentage.php b/core/DataTable/Filter/ColumnCallbackAddColumnPercentage.php
index 71e1764668..99284508e4 100644
--- a/core/DataTable/Filter/ColumnCallbackAddColumnPercentage.php
+++ b/core/DataTable/Filter/ColumnCallbackAddColumnPercentage.php
@@ -1,22 +1,22 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Add a new column to the table which is a percentage based on the value resulting
+ * Add a new column to the table which is a percentage based on the value resulting
* from a callback function with the parameter being another column's value
- *
- * For example in the keywords table, we can create a "nb_visits_percentage" column
+ *
+ * For example in the keywords table, we can create a "nb_visits_percentage" column
* from the "nb_visits" column that will be nb_visits / $totalValueUsedToComputePercentage
* You can also specify the precision of the percentage value to be displayed (defaults to 0, eg "11%")
- *
+ *
* Usage:
* $nbVisits = Piwik_VisitsSummary_API::getInstance()->getVisits($idSite, $period, $date);
* $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('nb_visits', 'nb_visits_percentage', $nbVisits, 1));
@@ -26,15 +26,15 @@
*/
class Piwik_DataTable_Filter_ColumnCallbackAddColumnPercentage extends Piwik_DataTable_Filter_ColumnCallbackAddColumnQuotient
{
- /**
- * Formats the given value
- *
- * @param number $value
- * @param number $divisor
- * @return string
- */
- protected function formatValue($value, $divisor)
- {
- return Piwik::getPercentageSafe($value, $divisor, $this->quotientPrecision) . '%';
- }
+ /**
+ * Formats the given value
+ *
+ * @param number $value
+ * @param number $divisor
+ * @return string
+ */
+ protected function formatValue($value, $divisor)
+ {
+ return Piwik::getPercentageSafe($value, $divisor, $this->quotientPrecision) . '%';
+ }
}
diff --git a/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php b/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php
index 83aba2d437..b5573bb731 100644
--- a/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php
+++ b/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,131 +12,119 @@
/**
* Adds a new column that is a division of two columns of the current row.
* Useful to process bounce rates, exit rates, average time on page, etc.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_ColumnCallbackAddColumnQuotient extends Piwik_DataTable_Filter
{
- protected $table;
- protected $columnValueToRead;
- protected $columnNameToAdd;
- protected $columnNameUsedAsDivisor;
- protected $totalValueUsedAsDivisor;
- protected $quotientPrecision;
- protected $shouldSkipRows;
- protected $getDivisorFromSummaryRow;
-
- /**
- * @param Piwik_DataTable $table
- * @param string $columnNameToAdd
- * @param string $columnValueToRead
- * @param number|string $divisorValueOrDivisorColumnName
- * if a numeric value is given, we use this value as the divisor to process the percentage.
- * if a string is given, this string is the column name's value used as the divisor.
- * @param int $quotientPrecision Division precision
- * @param bool|number $shouldSkipRows Whether rows w/o the column to read should be skipped.
- * @param bool $getDivisorFromSummaryRow Whether to get the divisor from the summary row or the current row.
- */
- public function __construct( $table, $columnNameToAdd, $columnValueToRead, $divisorValueOrDivisorColumnName, $quotientPrecision = 0, $shouldSkipRows = false, $getDivisorFromSummaryRow = false)
- {
- parent::__construct($table);
- $this->table = $table;
- $this->columnValueToRead = $columnValueToRead;
- $this->columnNameToAdd = $columnNameToAdd;
- if(is_numeric($divisorValueOrDivisorColumnName))
- {
- $this->totalValueUsedAsDivisor = $divisorValueOrDivisorColumnName;
- }
- else
- {
- $this->columnNameUsedAsDivisor = $divisorValueOrDivisorColumnName;
- }
- $this->quotientPrecision = $quotientPrecision;
- $this->shouldSkipRows = $shouldSkipRows;
- $this->getDivisorFromSummaryRow = $getDivisorFromSummaryRow;
- }
+ protected $table;
+ protected $columnValueToRead;
+ protected $columnNameToAdd;
+ protected $columnNameUsedAsDivisor;
+ protected $totalValueUsedAsDivisor;
+ protected $quotientPrecision;
+ protected $shouldSkipRows;
+ protected $getDivisorFromSummaryRow;
- /**
- * Filters the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $key => $row)
- {
- $existingValue = $row->getColumn($this->columnNameToAdd);
- if($existingValue !== false)
- {
- continue;
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @param string $columnNameToAdd
+ * @param string $columnValueToRead
+ * @param number|string $divisorValueOrDivisorColumnName
+ * if a numeric value is given, we use this value as the divisor to process the percentage.
+ * if a string is given, this string is the column name's value used as the divisor.
+ * @param int $quotientPrecision Division precision
+ * @param bool|number $shouldSkipRows Whether rows w/o the column to read should be skipped.
+ * @param bool $getDivisorFromSummaryRow Whether to get the divisor from the summary row or the current row.
+ */
+ public function __construct($table, $columnNameToAdd, $columnValueToRead, $divisorValueOrDivisorColumnName, $quotientPrecision = 0, $shouldSkipRows = false, $getDivisorFromSummaryRow = false)
+ {
+ parent::__construct($table);
+ $this->table = $table;
+ $this->columnValueToRead = $columnValueToRead;
+ $this->columnNameToAdd = $columnNameToAdd;
+ if (is_numeric($divisorValueOrDivisorColumnName)) {
+ $this->totalValueUsedAsDivisor = $divisorValueOrDivisorColumnName;
+ } else {
+ $this->columnNameUsedAsDivisor = $divisorValueOrDivisorColumnName;
+ }
+ $this->quotientPrecision = $quotientPrecision;
+ $this->shouldSkipRows = $shouldSkipRows;
+ $this->getDivisorFromSummaryRow = $getDivisorFromSummaryRow;
+ }
- $value = $this->getDividend($row);
- if ($value === false && $this->shouldSkipRows)
- {
- continue;
- }
+ /**
+ * Filters the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $key => $row) {
+ $existingValue = $row->getColumn($this->columnNameToAdd);
+ if ($existingValue !== false) {
+ continue;
+ }
- $divisor = $this->getDivisor($row);
+ $value = $this->getDividend($row);
+ if ($value === false && $this->shouldSkipRows) {
+ continue;
+ }
- $formattedValue = $this->formatValue($value, $divisor);
- $row->addColumn($this->columnNameToAdd, $formattedValue);
-
- $this->filterSubTable($row);
- }
- }
+ $divisor = $this->getDivisor($row);
- /**
- * Formats the given value
- *
- * @param number $value
- * @param number $divisor
- * @return float|int
- */
- protected function formatValue($value, $divisor)
- {
- $quotient = 0;
- if($divisor > 0 && $value > 0)
- {
- $quotient = round($value / $divisor, $this->quotientPrecision);
- }
- return $quotient;
- }
-
- /**
- * Returns the dividend to use when calculating the new column value. Can
- * be overridden by descendent classes to customize behavior.
- *
- * @param Piwik_DataTable_Row $row The row being modified.
- * @return int|float
- */
- protected function getDividend($row)
- {
- return $row->getColumn($this->columnValueToRead);
- }
+ $formattedValue = $this->formatValue($value, $divisor);
+ $row->addColumn($this->columnNameToAdd, $formattedValue);
- /**
- * Returns the divisor to use when calculating the new column value. Can
- * be overridden by descendent classes to customize behavior.
- *
- * @param Piwik_DataTable_Row $row The row being modified.
- * @return int|float
- */
- protected function getDivisor($row)
- {
- if(!is_null($this->totalValueUsedAsDivisor))
- {
- return $this->totalValueUsedAsDivisor;
- }
- else if ($this->getDivisorFromSummaryRow)
- {
- $summaryRow = $this->table->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW);
- return $summaryRow->getColumn($this->columnNameUsedAsDivisor);
- }
- else
- {
- return $row->getColumn($this->columnNameUsedAsDivisor);
- }
- }
+ $this->filterSubTable($row);
+ }
+ }
+
+ /**
+ * Formats the given value
+ *
+ * @param number $value
+ * @param number $divisor
+ * @return float|int
+ */
+ protected function formatValue($value, $divisor)
+ {
+ $quotient = 0;
+ if ($divisor > 0 && $value > 0) {
+ $quotient = round($value / $divisor, $this->quotientPrecision);
+ }
+ return $quotient;
+ }
+
+ /**
+ * Returns the dividend to use when calculating the new column value. Can
+ * be overridden by descendent classes to customize behavior.
+ *
+ * @param Piwik_DataTable_Row $row The row being modified.
+ * @return int|float
+ */
+ protected function getDividend($row)
+ {
+ return $row->getColumn($this->columnValueToRead);
+ }
+
+ /**
+ * Returns the divisor to use when calculating the new column value. Can
+ * be overridden by descendent classes to customize behavior.
+ *
+ * @param Piwik_DataTable_Row $row The row being modified.
+ * @return int|float
+ */
+ protected function getDivisor($row)
+ {
+ if (!is_null($this->totalValueUsedAsDivisor)) {
+ return $this->totalValueUsedAsDivisor;
+ } else if ($this->getDivisorFromSummaryRow) {
+ $summaryRow = $this->table->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW);
+ return $summaryRow->getColumn($this->columnNameUsedAsDivisor);
+ } else {
+ return $row->getColumn($this->columnNameUsedAsDivisor);
+ }
+ }
}
diff --git a/core/DataTable/Filter/ColumnCallbackAddMetadata.php b/core/DataTable/Filter/ColumnCallbackAddMetadata.php
index df35c0da00..27bd0170b5 100644
--- a/core/DataTable/Filter/ColumnCallbackAddMetadata.php
+++ b/core/DataTable/Filter/ColumnCallbackAddMetadata.php
@@ -1,82 +1,75 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Add a new 'metadata' column to the table based on the value resulting
+ * Add a new 'metadata' column to the table based on the value resulting
* from a callback function with the parameter being another column's value
- *
- * For example from the "label" column we can to create an "icon" 'metadata' column
+ *
+ * For example from the "label" column we can to create an "icon" 'metadata' column
* with the icon URI built from the label (LINUX => UserSettings/icons/linux.png)
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_ColumnCallbackAddMetadata extends Piwik_DataTable_Filter
{
- private $columnToRead;
- private $functionToApply;
- private $functionParameters;
- private $metadataToAdd;
- private $applyToSummaryRow;
+ private $columnToRead;
+ private $functionToApply;
+ private $functionParameters;
+ private $metadataToAdd;
+ private $applyToSummaryRow;
- /**
- * @param Piwik_DataTable $table
- * @param $columnToRead
- * @param $metadataToAdd
- * @param null $functionToApply
- * @param null $functionParameters
- */
- public function __construct( $table, $columnToRead, $metadataToAdd, $functionToApply = null,
- $functionParameters = null, $applyToSummaryRow = true )
- {
- parent::__construct($table);
- $this->functionToApply = $functionToApply;
- $this->functionParameters = $functionParameters;
- $this->columnToRead = $columnToRead;
- $this->metadataToAdd = $metadataToAdd;
- $this->applyToSummaryRow = $applyToSummaryRow;
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @param $columnToRead
+ * @param $metadataToAdd
+ * @param null $functionToApply
+ * @param null $functionParameters
+ */
+ public function __construct($table, $columnToRead, $metadataToAdd, $functionToApply = null,
+ $functionParameters = null, $applyToSummaryRow = true)
+ {
+ parent::__construct($table);
+ $this->functionToApply = $functionToApply;
+ $this->functionParameters = $functionParameters;
+ $this->columnToRead = $columnToRead;
+ $this->metadataToAdd = $metadataToAdd;
+ $this->applyToSummaryRow = $applyToSummaryRow;
+ }
- /**
- * Filters the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $key => $row)
- {
- if (!$this->applyToSummaryRow && $key == Piwik_DataTable::ID_SUMMARY_ROW)
- {
- continue;
- }
-
- $oldValue = $row->getColumn($this->columnToRead);
- $parameters = array($oldValue);
- if(!is_null($this->functionParameters))
- {
- $parameters = array_merge($parameters, $this->functionParameters);
- }
- if(!is_null($this->functionToApply))
- {
- $newValue = call_user_func_array( $this->functionToApply, $parameters);
- }
- else
- {
- $newValue = $oldValue;
- }
- if ($newValue !== false)
- {
- $row->addMetadata($this->metadataToAdd, $newValue);
- }
- }
- }
+ /**
+ * Filters the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $key => $row) {
+ if (!$this->applyToSummaryRow && $key == Piwik_DataTable::ID_SUMMARY_ROW) {
+ continue;
+ }
+
+ $oldValue = $row->getColumn($this->columnToRead);
+ $parameters = array($oldValue);
+ if (!is_null($this->functionParameters)) {
+ $parameters = array_merge($parameters, $this->functionParameters);
+ }
+ if (!is_null($this->functionToApply)) {
+ $newValue = call_user_func_array($this->functionToApply, $parameters);
+ } else {
+ $newValue = $oldValue;
+ }
+ if ($newValue !== false) {
+ $row->addMetadata($this->metadataToAdd, $newValue);
+ }
+ }
+ }
}
diff --git a/core/DataTable/Filter/ColumnCallbackDeleteRow.php b/core/DataTable/Filter/ColumnCallbackDeleteRow.php
index da5c773184..dbe0684cf8 100644
--- a/core/DataTable/Filter/ColumnCallbackDeleteRow.php
+++ b/core/DataTable/Filter/ColumnCallbackDeleteRow.php
@@ -1,61 +1,58 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Delete all rows for which a given function returns false for a given column.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_ColumnCallbackDeleteRow extends Piwik_DataTable_Filter
{
- private $columnToFilter;
- private $function;
- private $functionParams;
+ private $columnToFilter;
+ private $function;
+ private $functionParams;
- /**
- * @param Piwik_DataTable $table
- * @param string $columnToFilter
- * @param callback $function
- * @param array $functionParams
- */
- public function __construct( $table, $columnToFilter, $function, $functionParams = array() )
- {
- parent::__construct($table);
-
- if (!is_array($functionParams))
- {
- $functionParams = array($functionParams);
- }
-
- $this->function = $function;
- $this->columnToFilter = $columnToFilter;
- $this->functionParams = $functionParams;
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @param string $columnToFilter
+ * @param callback $function
+ * @param array $functionParams
+ */
+ public function __construct($table, $columnToFilter, $function, $functionParams = array())
+ {
+ parent::__construct($table);
- /**
- * Filters the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $key => $row)
- {
- $columnValue = $row->getColumn($this->columnToFilter);
- if( !call_user_func_array( $this->function, array_merge(array($columnValue), $this->functionParams)))
- {
- $table->deleteRow($key);
- }
- $this->filterSubTable($row);
- }
- }
+ if (!is_array($functionParams)) {
+ $functionParams = array($functionParams);
+ }
+
+ $this->function = $function;
+ $this->columnToFilter = $columnToFilter;
+ $this->functionParams = $functionParams;
+ }
+
+ /**
+ * Filters the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $key => $row) {
+ $columnValue = $row->getColumn($this->columnToFilter);
+ if (!call_user_func_array($this->function, array_merge(array($columnValue), $this->functionParams))) {
+ $table->deleteRow($key);
+ }
+ $this->filterSubTable($row);
+ }
+ }
}
diff --git a/core/DataTable/Filter/ColumnCallbackReplace.php b/core/DataTable/Filter/ColumnCallbackReplace.php
index 1ab5a4931a..f1c2d65f3a 100644
--- a/core/DataTable/Filter/ColumnCallbackReplace.php
+++ b/core/DataTable/Filter/ColumnCallbackReplace.php
@@ -1,108 +1,102 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Replace a column value with a new value resulting
+ * Replace a column value with a new value resulting
* from the function called with the column's value
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_ColumnCallbackReplace extends Piwik_DataTable_Filter
{
- private $columnsToFilter;
- private $functionToApply;
- private $functionParameters;
- private $extraColumnParameters;
+ private $columnsToFilter;
+ private $functionToApply;
+ private $functionParameters;
+ private $extraColumnParameters;
- /**
- * @param Piwik_DataTable $table
- * @param array|string $columnsToFilter
- * @param callback $functionToApply
- * @param array|null $functionParameters
- * @param array $extraColumnParameters
- */
- public function __construct( $table, $columnsToFilter, $functionToApply, $functionParameters = null,
- $extraColumnParameters = array() )
- {
- parent::__construct($table);
- $this->functionToApply = $functionToApply;
- $this->functionParameters = $functionParameters;
-
- if (!is_array($columnsToFilter))
- {
- $columnsToFilter = array($columnsToFilter);
- }
-
- $this->columnsToFilter = $columnsToFilter;
- $this->extraColumnParameters = $extraColumnParameters;
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @param array|string $columnsToFilter
+ * @param callback $functionToApply
+ * @param array|null $functionParameters
+ * @param array $extraColumnParameters
+ */
+ public function __construct($table, $columnsToFilter, $functionToApply, $functionParameters = null,
+ $extraColumnParameters = array())
+ {
+ parent::__construct($table);
+ $this->functionToApply = $functionToApply;
+ $this->functionParameters = $functionParameters;
- /**
- * Filters the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $key => $row)
- {
- $extraColumnParameters = array();
- foreach ($this->extraColumnParameters as $columnName)
- {
- $extraColumnParameters[] = $row->getColumn($columnName);
- }
-
- foreach ($this->columnsToFilter as $column)
- {
- // when a value is not defined, we set it to zero by default (rather than displaying '-')
- $value = $this->getElementToReplace($row, $column);
- if($value === false)
- {
- $value = 0;
- }
+ if (!is_array($columnsToFilter)) {
+ $columnsToFilter = array($columnsToFilter);
+ }
- $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);
- }
- }
- }
+ $this->columnsToFilter = $columnsToFilter;
+ $this->extraColumnParameters = $extraColumnParameters;
+ }
- /**
- * Replaces the given column within given row with the given value
- *
- * @param Piwik_DataTable_Row $row
- * @param string $columnToFilter
- * @param mixed $newValue
- */
- protected function setElementToReplace($row, $columnToFilter, $newValue)
- {
- $row->setColumn($columnToFilter, $newValue);
- }
+ /**
+ * Filters the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $key => $row) {
+ $extraColumnParameters = array();
+ foreach ($this->extraColumnParameters as $columnName) {
+ $extraColumnParameters[] = $row->getColumn($columnName);
+ }
- /**
- * Returns the element that should be replaced
- *
- * @param Piwik_DataTable_Row $row
- * @param string $columnToFilter
- * @return mixed
- */
- protected function getElementToReplace($row, $columnToFilter)
- {
- return $row->getColumn($columnToFilter);
- }
+ foreach ($this->columnsToFilter as $column) {
+ // when a value is not defined, we set it to zero by default (rather than displaying '-')
+ $value = $this->getElementToReplace($row, $column);
+ if ($value === false) {
+ $value = 0;
+ }
+
+ $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);
+ }
+ }
+ }
+
+ /**
+ * Replaces the given column within given row with the given value
+ *
+ * @param Piwik_DataTable_Row $row
+ * @param string $columnToFilter
+ * @param mixed $newValue
+ */
+ protected function setElementToReplace($row, $columnToFilter, $newValue)
+ {
+ $row->setColumn($columnToFilter, $newValue);
+ }
+
+ /**
+ * Returns the element that should be replaced
+ *
+ * @param Piwik_DataTable_Row $row
+ * @param string $columnToFilter
+ * @return mixed
+ */
+ protected function getElementToReplace($row, $columnToFilter)
+ {
+ return $row->getColumn($columnToFilter);
+ }
}
diff --git a/core/DataTable/Filter/ColumnDelete.php b/core/DataTable/Filter/ColumnDelete.php
index 930f9969f8..471315dd4a 100644
--- a/core/DataTable/Filter/ColumnDelete.php
+++ b/core/DataTable/Filter/ColumnDelete.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,122 +12,109 @@
/**
* Filter that will remove columns from a DataTable using either a blacklist,
* whitelist or both.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_ColumnDelete extends Piwik_DataTable_Filter
{
- /**
- * The columns that should be removed from DataTable rows.
- *
- * @var array
- */
- private $columnsToRemove;
-
- /**
- * The columns that should be kept in DataTable rows. All other columns will be
- * removed. If a column is in $columnsToRemove and this variable, it will NOT be kept.
- *
- * @var array
- */
- private $columnsToKeep;
+ /**
+ * The columns that should be removed from DataTable rows.
+ *
+ * @var array
+ */
+ private $columnsToRemove;
- /**
- * Delete the column, only if the value was zero
- *
- * @var bool
- */
- private $deleteIfZeroOnly;
-
- /**
- * Constructor.
- *
- * @param Piwik_DataTable $table
- * @param array|string $columnsToRemove An array of column names or a comma-separated list of
- * column names. These columns will be removed.
- * @param array|string $columnsToKeep An array of column names that should be kept or a
- * comma-separated list of column names. Columns not in
- * this list will be removed.
- */
- public function __construct( $table, $columnsToRemove, $columnsToKeep = array(), $deleteIfZeroOnly = false )
- {
- parent::__construct($table);
-
- if (is_string($columnsToRemove))
- {
- $columnsToRemove = $columnsToRemove == '' ? array() : explode(',', $columnsToRemove);
- }
-
- if (is_string($columnsToKeep))
- {
- $columnsToKeep = $columnsToKeep == '' ? array() : explode(',', $columnsToKeep);
- }
-
- $this->columnsToRemove = $columnsToRemove;
- $this->columnsToKeep = array_flip($columnsToKeep); // flip so we can use isset instead of in_array
- $this->deleteIfZeroOnly = $deleteIfZeroOnly;
- }
+ /**
+ * The columns that should be kept in DataTable rows. All other columns will be
+ * removed. If a column is in $columnsToRemove and this variable, it will NOT be kept.
+ *
+ * @var array
+ */
+ private $columnsToKeep;
- /**
- * Filters the given DataTable. Removes columns that are not desired from
- * each DataTable row.
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- // always do recursive filter
- $this->enableRecursive(true);
- $recurse = false; // only recurse if there are columns to remove/keep
+ /**
+ * Delete the column, only if the value was zero
+ *
+ * @var bool
+ */
+ private $deleteIfZeroOnly;
- // remove columns specified in $this->columnsToRemove
- if (!empty($this->columnsToRemove))
- {
- foreach ($table->getRows() as $row)
- {
- foreach ($this->columnsToRemove as $column)
- {
- if($this->deleteIfZeroOnly)
- {
- $value = $row->getColumn($column);
- if($value === false || !empty($value))
- {
- continue;
- }
- }
- $row->deleteColumn($column);
- }
- }
-
- $recurse = true;
- }
-
- // remove columns not specified in $columnsToKeep
- if (!empty($this->columnsToKeep))
- {
- foreach ($table->getRows() as $row)
- {
- foreach ($row->getColumns() as $name => $value)
- {
- // label cannot be removed via whitelisting
- if ($name != 'label' && !isset($this->columnsToKeep[$name]))
- {
- $row->deleteColumn($name);
- }
- }
- }
-
- $recurse = true;
- }
-
- // recurse
- if ($recurse)
- {
- foreach ($table->getRows() as $row)
- {
- $this->filterSubTable($row);
- }
- }
- }
+ /**
+ * Constructor.
+ *
+ * @param Piwik_DataTable $table
+ * @param array|string $columnsToRemove An array of column names or a comma-separated list of
+ * column names. These columns will be removed.
+ * @param array|string $columnsToKeep An array of column names that should be kept or a
+ * comma-separated list of column names. Columns not in
+ * this list will be removed.
+ */
+ public function __construct($table, $columnsToRemove, $columnsToKeep = array(), $deleteIfZeroOnly = false)
+ {
+ parent::__construct($table);
+
+ if (is_string($columnsToRemove)) {
+ $columnsToRemove = $columnsToRemove == '' ? array() : explode(',', $columnsToRemove);
+ }
+
+ if (is_string($columnsToKeep)) {
+ $columnsToKeep = $columnsToKeep == '' ? array() : explode(',', $columnsToKeep);
+ }
+
+ $this->columnsToRemove = $columnsToRemove;
+ $this->columnsToKeep = array_flip($columnsToKeep); // flip so we can use isset instead of in_array
+ $this->deleteIfZeroOnly = $deleteIfZeroOnly;
+ }
+
+ /**
+ * Filters the given DataTable. Removes columns that are not desired from
+ * each DataTable row.
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ // always do recursive filter
+ $this->enableRecursive(true);
+ $recurse = false; // only recurse if there are columns to remove/keep
+
+ // remove columns specified in $this->columnsToRemove
+ if (!empty($this->columnsToRemove)) {
+ foreach ($table->getRows() as $row) {
+ foreach ($this->columnsToRemove as $column) {
+ if ($this->deleteIfZeroOnly) {
+ $value = $row->getColumn($column);
+ if ($value === false || !empty($value)) {
+ continue;
+ }
+ }
+ $row->deleteColumn($column);
+ }
+ }
+
+ $recurse = true;
+ }
+
+ // remove columns not specified in $columnsToKeep
+ if (!empty($this->columnsToKeep)) {
+ foreach ($table->getRows() as $row) {
+ foreach ($row->getColumns() as $name => $value) {
+ // label cannot be removed via whitelisting
+ if ($name != 'label' && !isset($this->columnsToKeep[$name])) {
+ $row->deleteColumn($name);
+ }
+ }
+ }
+
+ $recurse = true;
+ }
+
+ // recurse
+ if ($recurse) {
+ foreach ($table->getRows() as $row) {
+ $this->filterSubTable($row);
+ }
+ }
+ }
}
diff --git a/core/DataTable/Filter/ExcludeLowPopulation.php b/core/DataTable/Filter/ExcludeLowPopulation.php
index aa3dc82f14..c5d6369d6c 100644
--- a/core/DataTable/Filter/ExcludeLowPopulation.php
+++ b/core/DataTable/Filter/ExcludeLowPopulation.php
@@ -1,77 +1,75 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Delete all rows that have a $columnToFilter value less than the $minimumValue
- *
+ * Delete all rows that have a $columnToFilter value less than the $minimumValue
+ *
* For example we delete from the countries report table all countries that have less than 3 visits.
* It is very useful to exclude noise from the reports.
* You can obviously apply this filter on a percentaged column, eg. remove all countries with the column 'percent_visits' less than 0.05
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_ExcludeLowPopulation extends Piwik_DataTable_Filter
{
- static public $minimumValue;
- const MINIMUM_SIGNIFICANT_PERCENTAGE_THRESHOLD = 0.02;
+ static public $minimumValue;
+ const MINIMUM_SIGNIFICANT_PERCENTAGE_THRESHOLD = 0.02;
- /**
- * Constructor
- *
- * @param Piwik_DataTable $table
- * @param string $columnToFilter column to filter
- * @param number $minimumValue minimum value
- * @param bool $minimumPercentageThreshold
- */
- public function __construct( $table, $columnToFilter, $minimumValue, $minimumPercentageThreshold = false )
- {
- parent::__construct($table);
- $this->columnToFilter = $columnToFilter;
-
- if($minimumValue == 0)
- {
- if($minimumPercentageThreshold === false)
- {
- $minimumPercentageThreshold = self::MINIMUM_SIGNIFICANT_PERCENTAGE_THRESHOLD;
- }
- $allValues = $table->getColumn($this->columnToFilter);
- $sumValues = array_sum($allValues);
- $minimumValue = $sumValues * $minimumPercentageThreshold;
- }
- self::$minimumValue = $minimumValue;
- }
+ /**
+ * Constructor
+ *
+ * @param Piwik_DataTable $table
+ * @param string $columnToFilter column to filter
+ * @param number $minimumValue minimum value
+ * @param bool $minimumPercentageThreshold
+ */
+ public function __construct($table, $columnToFilter, $minimumValue, $minimumPercentageThreshold = false)
+ {
+ parent::__construct($table);
+ $this->columnToFilter = $columnToFilter;
- /**
- * Executes filter and removes all rows below the defined minimum
- *
- * @param Piwik_DataTable $table
- */
- function filter($table)
- {
- $table->filter('ColumnCallbackDeleteRow',
- array($this->columnToFilter,
- array("Piwik_DataTable_Filter_ExcludeLowPopulation", "excludeLowPopulation")
- )
- );
- }
+ if ($minimumValue == 0) {
+ if ($minimumPercentageThreshold === false) {
+ $minimumPercentageThreshold = self::MINIMUM_SIGNIFICANT_PERCENTAGE_THRESHOLD;
+ }
+ $allValues = $table->getColumn($this->columnToFilter);
+ $sumValues = array_sum($allValues);
+ $minimumValue = $sumValues * $minimumPercentageThreshold;
+ }
+ self::$minimumValue = $minimumValue;
+ }
- /**
- * Checks whether the given value is below the defined minimum
- *
- * @param number $value value to check
- * @return bool
- */
- static public function excludeLowPopulation($value)
- {
- return $value >= self::$minimumValue;
- }
+ /**
+ * Executes filter and removes all rows below the defined minimum
+ *
+ * @param Piwik_DataTable $table
+ */
+ function filter($table)
+ {
+ $table->filter('ColumnCallbackDeleteRow',
+ array($this->columnToFilter,
+ array("Piwik_DataTable_Filter_ExcludeLowPopulation", "excludeLowPopulation")
+ )
+ );
+ }
+
+ /**
+ * Checks whether the given value is below the defined minimum
+ *
+ * @param number $value value to check
+ * @return bool
+ */
+ static public function excludeLowPopulation($value)
+ {
+ return $value >= self::$minimumValue;
+ }
}
diff --git a/core/DataTable/Filter/GroupBy.php b/core/DataTable/Filter/GroupBy.php
index 5c521e7ca2..4b42e8d2ba 100755
--- a/core/DataTable/Filter/GroupBy.php
+++ b/core/DataTable/Filter/GroupBy.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,88 +12,83 @@
/**
* DataTable filter that will group DataTable rows together based on the results
* of a reduce function. Rows with the same reduce result will be summed and merged.
- *
+ *
* NOTE: This filter should never be queued, it must be applied directly on a DataTable.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_GroupBy extends Piwik_DataTable_Filter
{
- /**
- * The name of the columns to reduce.
- * @var string
- */
- private $groupByColumn;
-
- /**
- * A callback that modifies the $groupByColumn of each row in some way. Rows with
- * the same reduction result will be added together.
- */
- private $reduceFunction;
-
- /**
- * Extra parameters to pass to the reduce function.
- */
- private $parameters;
-
- /**
- * Constructor.
- *
- * @param Piwik_DataTable $table The DataTable to filter.
- * @param string $groupByColumn The column name to reduce.
- * @param mixed $reduceFunction The reduce function. This must alter the $groupByColumn in some way.
- * @param array $parameters Extra parameters to supply to the reduce function.
- */
- public function __construct( $table, $groupByColumn, $reduceFunction, $parameters = array() )
- {
- parent::__construct($table);
-
- $this->groupByColumn = $groupByColumn;
- $this->reduceFunction = $reduceFunction;
- $this->parameters = $parameters;
- }
-
- /**
- * Applies the reduce function to each row and merges rows w/ the same reduce result.
- *
- * @param Piwik_DataTable $table
- */
- public function filter( $table )
- {
- $groupByRows = array();
- $nonGroupByRowIds = array();
-
- foreach ($table->getRows() as $rowId => $row)
- {
- // skip the summary row
- if ($rowId == Piwik_DataTable::ID_SUMMARY_ROW)
- {
- continue;
- }
-
- // reduce the group by column of this row
- $groupByColumnValue = $row->getColumn($this->groupByColumn);
- $parameters = array_merge(array($groupByColumnValue), $this->parameters);
- $groupByValue = call_user_func_array($this->reduceFunction, $parameters);
-
- if (!isset($groupByRows[$groupByValue]))
- {
- // if we haven't encountered this group by value before, we mark this row as a
- // row to keep, and change the group by column to the reduced value.
- $groupByRows[$groupByValue] = $row;
- $row->setColumn($this->groupByColumn, $groupByValue);
- }
- else
- {
- // if we have already encountered this group by value, we add this row to the
- // row that will be kept, and mark this one for deletion
- $groupByRows[$groupByValue]->sumRow($row);
- $nonGroupByRowIds[] = $rowId;
- }
- }
-
- // delete the unneeded rows.
- $table->deleteRows($nonGroupByRowIds);
- }
+ /**
+ * The name of the columns to reduce.
+ * @var string
+ */
+ private $groupByColumn;
+
+ /**
+ * A callback that modifies the $groupByColumn of each row in some way. Rows with
+ * the same reduction result will be added together.
+ */
+ private $reduceFunction;
+
+ /**
+ * Extra parameters to pass to the reduce function.
+ */
+ private $parameters;
+
+ /**
+ * Constructor.
+ *
+ * @param Piwik_DataTable $table The DataTable to filter.
+ * @param string $groupByColumn The column name to reduce.
+ * @param mixed $reduceFunction The reduce function. This must alter the $groupByColumn in some way.
+ * @param array $parameters Extra parameters to supply to the reduce function.
+ */
+ public function __construct($table, $groupByColumn, $reduceFunction, $parameters = array())
+ {
+ parent::__construct($table);
+
+ $this->groupByColumn = $groupByColumn;
+ $this->reduceFunction = $reduceFunction;
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * Applies the reduce function to each row and merges rows w/ the same reduce result.
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ $groupByRows = array();
+ $nonGroupByRowIds = array();
+
+ foreach ($table->getRows() as $rowId => $row) {
+ // skip the summary row
+ if ($rowId == Piwik_DataTable::ID_SUMMARY_ROW) {
+ continue;
+ }
+
+ // reduce the group by column of this row
+ $groupByColumnValue = $row->getColumn($this->groupByColumn);
+ $parameters = array_merge(array($groupByColumnValue), $this->parameters);
+ $groupByValue = call_user_func_array($this->reduceFunction, $parameters);
+
+ if (!isset($groupByRows[$groupByValue])) {
+ // if we haven't encountered this group by value before, we mark this row as a
+ // row to keep, and change the group by column to the reduced value.
+ $groupByRows[$groupByValue] = $row;
+ $row->setColumn($this->groupByColumn, $groupByValue);
+ } else {
+ // if we have already encountered this group by value, we add this row to the
+ // row that will be kept, and mark this one for deletion
+ $groupByRows[$groupByValue]->sumRow($row);
+ $nonGroupByRowIds[] = $rowId;
+ }
+ }
+
+ // delete the unneeded rows.
+ $table->deleteRows($nonGroupByRowIds);
+ }
}
diff --git a/core/DataTable/Filter/Limit.php b/core/DataTable/Filter/Limit.php
index 8e2e7e3272..5241b466e2 100644
--- a/core/DataTable/Filter/Limit.php
+++ b/core/DataTable/Filter/Limit.php
@@ -1,71 +1,66 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Delete all rows from the table that are not in the offset,offset+limit range
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_Limit extends Piwik_DataTable_Filter
{
- /**
- * Filter constructor.
- *
- * @param Piwik_DataTable $table
- * @param int $offset Starting row (indexed from 0)
- * @param int $limit Number of rows to keep (specify -1 to keep all rows)
- * @param bool $keepSummaryRow Whether to keep the summary row or not.
- */
- public function __construct( $table, $offset, $limit = null, $keepSummaryRow = false )
- {
- parent::__construct($table);
- $this->offset = $offset;
-
- if(is_null($limit))
- {
- $limit = -1;
- }
- $this->limit = $limit;
- $this->keepSummaryRow = $keepSummaryRow;
- }
+ /**
+ * Filter constructor.
+ *
+ * @param Piwik_DataTable $table
+ * @param int $offset Starting row (indexed from 0)
+ * @param int $limit Number of rows to keep (specify -1 to keep all rows)
+ * @param bool $keepSummaryRow Whether to keep the summary row or not.
+ */
+ public function __construct($table, $offset, $limit = null, $keepSummaryRow = false)
+ {
+ parent::__construct($table);
+ $this->offset = $offset;
- /**
- * Limits the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- $table->setRowsCountBeforeLimitFilter();
-
- if ($this->keepSummaryRow)
- {
- $summaryRow = $table->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW);
- }
-
- // we delete from 0 to offset
- if($this->offset > 0)
- {
- $table->deleteRowsOffset( 0, $this->offset );
- }
- // at this point the array has offset less elements. We delete from limit to the end
- if( $this->limit >= 0 )
- {
- $table->deleteRowsOffset( $this->limit );
- }
-
- if ($this->keepSummaryRow && $summaryRow)
- {
- $table->addSummaryRow($summaryRow);
- }
- }
+ if (is_null($limit)) {
+ $limit = -1;
+ }
+ $this->limit = $limit;
+ $this->keepSummaryRow = $keepSummaryRow;
+ }
+
+ /**
+ * Limits the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ $table->setRowsCountBeforeLimitFilter();
+
+ if ($this->keepSummaryRow) {
+ $summaryRow = $table->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW);
+ }
+
+ // we delete from 0 to offset
+ if ($this->offset > 0) {
+ $table->deleteRowsOffset(0, $this->offset);
+ }
+ // at this point the array has offset less elements. We delete from limit to the end
+ if ($this->limit >= 0) {
+ $table->deleteRowsOffset($this->limit);
+ }
+
+ if ($this->keepSummaryRow && $summaryRow) {
+ $table->addSummaryRow($summaryRow);
+ }
+ }
}
diff --git a/core/DataTable/Filter/MetadataCallbackAddMetadata.php b/core/DataTable/Filter/MetadataCallbackAddMetadata.php
index 3d70916bfd..dbfc1728ee 100644
--- a/core/DataTable/Filter/MetadataCallbackAddMetadata.php
+++ b/core/DataTable/Filter/MetadataCallbackAddMetadata.php
@@ -1,78 +1,73 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Add a new metadata to the table based on the value resulting
+ * Add a new metadata to the table based on the value resulting
* from a callback function with the parameter being another metadata value
- *
- * For example for the searchEngine we have a "metadata" information that gives
- * the URL of the search engine. We use this URL to add a new "metadata" that gives
- * the path of the logo for this search engine URL (which has the format URL.png).
- *
+ *
+ * For example for the searchEngine we have a "metadata" information that gives
+ * the URL of the search engine. We use this URL to add a new "metadata" that gives
+ * the path of the logo for this search engine URL (which has the format URL.png).
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_MetadataCallbackAddMetadata extends Piwik_DataTable_Filter
{
- private $metadataToRead;
- private $functionToApply;
- private $metadataToAdd;
- private $applyToSummaryRow;
+ private $metadataToRead;
+ private $functionToApply;
+ private $metadataToAdd;
+ private $applyToSummaryRow;
- /**
- * @param Piwik_DataTable $table
- * @param string|array $metadataToRead
- * @param string $metadataToAdd
- * @param callback $functionToApply
- * @param bool $applyToSummaryRow
- */
- public function __construct( $table, $metadataToRead, $metadataToAdd, $functionToApply,
- $applyToSummaryRow = true )
- {
- parent::__construct($table);
- $this->functionToApply = $functionToApply;
-
- if (!is_array($metadataToRead))
- {
- $metadataToRead = array($metadataToRead);
- }
-
- $this->metadataToRead = $metadataToRead;
- $this->metadataToAdd = $metadataToAdd;
- $this->applyToSummaryRow = $applyToSummaryRow;
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @param string|array $metadataToRead
+ * @param string $metadataToAdd
+ * @param callback $functionToApply
+ * @param bool $applyToSummaryRow
+ */
+ public function __construct($table, $metadataToRead, $metadataToAdd, $functionToApply,
+ $applyToSummaryRow = true)
+ {
+ parent::__construct($table);
+ $this->functionToApply = $functionToApply;
- /**
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $key => $row)
- {
- if (!$this->applyToSummaryRow && $key == Piwik_DataTable::ID_SUMMARY_ROW)
- {
- continue;
- }
-
- $params = array();
- foreach ($this->metadataToRead as $name)
- {
- $params[] = $row->getMetadata($name);
- }
-
- $newValue = call_user_func_array($this->functionToApply, $params);
- if($newValue !== false)
- {
- $row->addMetadata($this->metadataToAdd, $newValue);
- }
- }
- }
+ if (!is_array($metadataToRead)) {
+ $metadataToRead = array($metadataToRead);
+ }
+
+ $this->metadataToRead = $metadataToRead;
+ $this->metadataToAdd = $metadataToAdd;
+ $this->applyToSummaryRow = $applyToSummaryRow;
+ }
+
+ /**
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $key => $row) {
+ if (!$this->applyToSummaryRow && $key == Piwik_DataTable::ID_SUMMARY_ROW) {
+ continue;
+ }
+
+ $params = array();
+ foreach ($this->metadataToRead as $name) {
+ $params[] = $row->getMetadata($name);
+ }
+
+ $newValue = call_user_func_array($this->functionToApply, $params);
+ if ($newValue !== false) {
+ $row->addMetadata($this->metadataToAdd, $newValue);
+ }
+ }
+ }
}
diff --git a/core/DataTable/Filter/MetadataCallbackReplace.php b/core/DataTable/Filter/MetadataCallbackReplace.php
index 9fc49f0403..4e6e5990de 100644
--- a/core/DataTable/Filter/MetadataCallbackReplace.php
+++ b/core/DataTable/Filter/MetadataCallbackReplace.php
@@ -1,53 +1,53 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Replace a metadata value with a new value resulting
+ * Replace a metadata value with a new value resulting
* from the function called with the metadata's value
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_MetadataCallbackReplace extends Piwik_DataTable_Filter_ColumnCallbackReplace
{
- /**
- * @param Piwik_DataTable $table
- * @param array|string $metadataToFilter
- * @param callback $functionToApply
- * @param null|array $functionParameters
- * @param array $extraColumnParameters
- */
- public function __construct( $table, $metadataToFilter, $functionToApply, $functionParameters = null,
- $extraColumnParameters = array() )
- {
- parent::__construct($table, $metadataToFilter, $functionToApply, $functionParameters, $extraColumnParameters);
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @param array|string $metadataToFilter
+ * @param callback $functionToApply
+ * @param null|array $functionParameters
+ * @param array $extraColumnParameters
+ */
+ public function __construct($table, $metadataToFilter, $functionToApply, $functionParameters = null,
+ $extraColumnParameters = array())
+ {
+ parent::__construct($table, $metadataToFilter, $functionToApply, $functionParameters, $extraColumnParameters);
+ }
- /**
- * @param Piwik_DataTable_Row $row
- * @param string $metadataToFilter
- * @param mixed $newValue
- */
- protected function setElementToReplace($row, $metadataToFilter, $newValue)
- {
- $row->setMetadata($metadataToFilter, $newValue);
- }
+ /**
+ * @param Piwik_DataTable_Row $row
+ * @param string $metadataToFilter
+ * @param mixed $newValue
+ */
+ protected function setElementToReplace($row, $metadataToFilter, $newValue)
+ {
+ $row->setMetadata($metadataToFilter, $newValue);
+ }
- /**
- * @param Piwik_DataTable_Row $row
- * @param string $metadataToFilter
- * @return array|false|mixed
- */
- protected function getElementToReplace($row, $metadataToFilter)
- {
- return $row->getMetadata($metadataToFilter);
- }
+ /**
+ * @param Piwik_DataTable_Row $row
+ * @param string $metadataToFilter
+ * @return array|false|mixed
+ */
+ protected function getElementToReplace($row, $metadataToFilter)
+ {
+ return $row->getMetadata($metadataToFilter);
+ }
}
diff --git a/core/DataTable/Filter/Null.php b/core/DataTable/Filter/Null.php
index 0d954532e1..240dc53c41 100644
--- a/core/DataTable/Filter/Null.php
+++ b/core/DataTable/Filter/Null.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,27 +12,26 @@
/**
* Filter template.
* You can use it if you want to create a new filter.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_Null extends Piwik_DataTable_Filter
{
- /**
- * @param Piwik_DataTable $table
- */
- public function __construct( $table )
- {
- parent::__construct($table);
- }
+ /**
+ * @param Piwik_DataTable $table
+ */
+ public function __construct($table)
+ {
+ parent::__construct($table);
+ }
- /**
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $key => $row)
- {
- }
- }
+ /**
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $key => $row) {
+ }
+ }
}
diff --git a/core/DataTable/Filter/Pattern.php b/core/DataTable/Filter/Pattern.php
index 92f8b2b3d5..4a1ef8fd99 100644
--- a/core/DataTable/Filter/Pattern.php
+++ b/core/DataTable/Filter/Pattern.php
@@ -1,89 +1,86 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Delete all rows for which the given $columnToFilter do not contain the $patternToSearch
- * This filter is to be used on columns containing strings.
+ * This filter is to be used on columns containing strings.
* Example: from the keyword report, keep only the rows for which the label contains "piwik"
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_Pattern extends Piwik_DataTable_Filter
{
- private $columnToFilter;
- private $patternToSearch;
- private $patternToSearchQuoted;
+ private $columnToFilter;
+ private $patternToSearch;
+ private $patternToSearchQuoted;
private $invertedMatch;
- /**
- * @param Piwik_DataTable $table
- * @param string $columnToFilter
- * @param string $patternToSearch
- * @param bool $invertedMatch
- */
- public function __construct( $table, $columnToFilter, $patternToSearch, $invertedMatch = false )
- {
- parent::__construct($table);
- $this->patternToSearch = $patternToSearch;
- $this->patternToSearchQuoted = self::getPatternQuoted($patternToSearch);
- $this->columnToFilter = $columnToFilter;
+ /**
+ * @param Piwik_DataTable $table
+ * @param string $columnToFilter
+ * @param string $patternToSearch
+ * @param bool $invertedMatch
+ */
+ public function __construct($table, $columnToFilter, $patternToSearch, $invertedMatch = false)
+ {
+ parent::__construct($table);
+ $this->patternToSearch = $patternToSearch;
+ $this->patternToSearchQuoted = self::getPatternQuoted($patternToSearch);
+ $this->columnToFilter = $columnToFilter;
$this->invertedMatch = $invertedMatch;
- }
+ }
- /**
- * Helper method to return the given pattern quoted
- *
- * @param string $pattern
- * @return string
- */
- static public function getPatternQuoted( $pattern )
- {
- return '/'. str_replace('/', '\/', $pattern) .'/';
- }
+ /**
+ * Helper method to return the given pattern quoted
+ *
+ * @param string $pattern
+ * @return string
+ */
+ static public function getPatternQuoted($pattern)
+ {
+ return '/' . str_replace('/', '\/', $pattern) . '/';
+ }
- /**
- * Performs case insensitive match
- *
- * @param string $pattern
- * @param string $patternQuoted
- * @param string $string
- * @param bool $invertedMatch
- * @return int
- */
- static public function match($pattern, $patternQuoted, $string, $invertedMatch)
- {
- return @preg_match($patternQuoted . "i", $string) == 1 ^ $invertedMatch;
- }
+ /**
+ * Performs case insensitive match
+ *
+ * @param string $pattern
+ * @param string $patternQuoted
+ * @param string $string
+ * @param bool $invertedMatch
+ * @return int
+ */
+ static public function match($pattern, $patternQuoted, $string, $invertedMatch)
+ {
+ return @preg_match($patternQuoted . "i", $string) == 1 ^ $invertedMatch;
+ }
- /**
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $key => $row)
- {
- //instead search must handle
- // - negative search with -piwik
- // - exact match with ""
- // see (?!pattern) A subexpression that performs a negative lookahead search, which matches the search string at any point where a string not matching pattern begins.
- $value = $row->getColumn($this->columnToFilter);
- if($value === false)
- {
- $value = $row->getMetadata($this->columnToFilter);
- }
- if( !self::match($this->patternToSearch, $this->patternToSearchQuoted, $value, $this->invertedMatch))
- {
- $table->deleteRow($key);
- }
- }
- }
+ /**
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $key => $row) {
+ //instead search must handle
+ // - negative search with -piwik
+ // - exact match with ""
+ // see (?!pattern) A subexpression that performs a negative lookahead search, which matches the search string at any point where a string not matching pattern begins.
+ $value = $row->getColumn($this->columnToFilter);
+ if ($value === false) {
+ $value = $row->getMetadata($this->columnToFilter);
+ }
+ if (!self::match($this->patternToSearch, $this->patternToSearchQuoted, $value, $this->invertedMatch)) {
+ $table->deleteRow($key);
+ }
+ }
+ }
}
diff --git a/core/DataTable/Filter/PatternRecursive.php b/core/DataTable/Filter/PatternRecursive.php
index 4d73b4dc75..088546fee5 100644
--- a/core/DataTable/Filter/PatternRecursive.php
+++ b/core/DataTable/Filter/PatternRecursive.php
@@ -1,83 +1,80 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Delete all rows for which
- * - the given $columnToFilter do not contain the $patternToSearch
+ * Delete all rows for which
+ * - the given $columnToFilter do not contain the $patternToSearch
* - AND all the subTables associated to this row do not contain the $patternToSearch
- *
- * This filter is to be used on columns containing strings.
+ *
+ * This filter is to be used on columns containing strings.
* Example: from the pages viewed report, keep only the rows that contain "piwik" or for which a subpage contains "piwik".
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_PatternRecursive extends Piwik_DataTable_Filter
{
- private $columnToFilter;
- private $patternToSearch;
- private $patternToSearchQuoted;
+ private $columnToFilter;
+ private $patternToSearch;
+ private $patternToSearchQuoted;
- /**
- * @param Piwik_DataTable $table
- * @param string $columnToFilter
- * @param string $patternToSearch
- */
- public function __construct( $table, $columnToFilter, $patternToSearch )
- {
- parent::__construct($table);
- $this->patternToSearch = $patternToSearch;
- $this->patternToSearchQuoted = Piwik_DataTable_Filter_Pattern::getPatternQuoted($patternToSearch);
- $this->patternToSearch = $patternToSearch;//preg_quote($patternToSearch);
- $this->columnToFilter = $columnToFilter;
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @param string $columnToFilter
+ * @param string $patternToSearch
+ */
+ public function __construct($table, $columnToFilter, $patternToSearch)
+ {
+ parent::__construct($table);
+ $this->patternToSearch = $patternToSearch;
+ $this->patternToSearchQuoted = Piwik_DataTable_Filter_Pattern::getPatternQuoted($patternToSearch);
+ $this->patternToSearch = $patternToSearch; //preg_quote($patternToSearch);
+ $this->columnToFilter = $columnToFilter;
+ }
- /**
- * @param Piwik_DataTable $table
- * @return int
- */
- public function filter( $table )
- {
- $rows = $table->getRows();
-
- foreach($rows as $key => $row)
- {
- // A row is deleted if
- // 1 - its label doesnt contain the pattern
- // AND 2 - the label is not found in the children
- $patternNotFoundInChildren = false;
-
- try{
- $idSubTable = $row->getIdSubDataTable();
- $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
-
- // we delete the row if we couldn't find the pattern in any row in the
- // children hierarchy
- if( $this->filter($subTable) == 0 )
- {
- $patternNotFoundInChildren = true;
- }
- } catch(Exception $e) {
- // there is no subtable loaded for example
- $patternNotFoundInChildren = true;
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @return int
+ */
+ public function filter($table)
+ {
+ $rows = $table->getRows();
- if( $patternNotFoundInChildren
- && !Piwik_DataTable_Filter_Pattern::match($this->patternToSearch, $this->patternToSearchQuoted, $row->getColumn($this->columnToFilter), $invertedMatch = false)
- )
- {
- $table->deleteRow($key);
- }
- }
-
- return $table->getRowsCount();
- }
+ foreach ($rows as $key => $row) {
+ // A row is deleted if
+ // 1 - its label doesnt contain the pattern
+ // AND 2 - the label is not found in the children
+ $patternNotFoundInChildren = false;
+
+ try {
+ $idSubTable = $row->getIdSubDataTable();
+ $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
+
+ // we delete the row if we couldn't find the pattern in any row in the
+ // children hierarchy
+ if ($this->filter($subTable) == 0) {
+ $patternNotFoundInChildren = true;
+ }
+ } catch (Exception $e) {
+ // there is no subtable loaded for example
+ $patternNotFoundInChildren = true;
+ }
+
+ if ($patternNotFoundInChildren
+ && !Piwik_DataTable_Filter_Pattern::match($this->patternToSearch, $this->patternToSearchQuoted, $row->getColumn($this->columnToFilter), $invertedMatch = false)
+ ) {
+ $table->deleteRow($key);
+ }
+ }
+
+ return $table->getRowsCount();
+ }
}
diff --git a/core/DataTable/Filter/RangeCheck.php b/core/DataTable/Filter/RangeCheck.php
index 105f22f395..ebfea03d7f 100644
--- a/core/DataTable/Filter/RangeCheck.php
+++ b/core/DataTable/Filter/RangeCheck.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -17,48 +17,43 @@
*/
class Piwik_DataTable_Filter_RangeCheck extends Piwik_DataTable_Filter
{
- static public $minimumValue = 0.00;
- static public $maximumValue = 100.0;
+ static public $minimumValue = 0.00;
+ static public $maximumValue = 100.0;
- /**
- * @param Piwik_DataTable $table
- * @param string $columnToFilter name of the column to filter
- * @param float $minimumValue minimum value for range
- * @param float $maximumValue maximum value for range
- */
- public function __construct( $table, $columnToFilter, $minimumValue = 0.00, $maximumValue = 100.0 )
- {
- parent::__construct($table);
+ /**
+ * @param Piwik_DataTable $table
+ * @param string $columnToFilter name of the column to filter
+ * @param float $minimumValue minimum value for range
+ * @param float $maximumValue maximum value for range
+ */
+ public function __construct($table, $columnToFilter, $minimumValue = 0.00, $maximumValue = 100.0)
+ {
+ parent::__construct($table);
- $this->columnToFilter = $columnToFilter;
+ $this->columnToFilter = $columnToFilter;
- if ($minimumValue < $maximumValue) {
- self::$minimumValue = $minimumValue;
- self::$maximumValue = $maximumValue;
- }
- }
+ if ($minimumValue < $maximumValue) {
+ self::$minimumValue = $minimumValue;
+ self::$maximumValue = $maximumValue;
+ }
+ }
- /**
- * Executes the filter an adjusts all columns to fit the defined range
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $row)
- {
- $value = $row->getColumn($this->columnToFilter);
- if($value !== false)
- {
- if ($value < self::$minimumValue)
- {
- $row->setColumn($this->columnToFilter, self::$minimumValue);
- }
- elseif ($value > self::$maximumValue)
- {
- $row->setColumn($this->columnToFilter, self::$maximumValue);
- }
- }
- }
- }
+ /**
+ * Executes the filter an adjusts all columns to fit the defined range
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $row) {
+ $value = $row->getColumn($this->columnToFilter);
+ if ($value !== false) {
+ if ($value < self::$minimumValue) {
+ $row->setColumn($this->columnToFilter, self::$minimumValue);
+ } elseif ($value > self::$maximumValue) {
+ $row->setColumn($this->columnToFilter, self::$maximumValue);
+ }
+ }
+ }
+ }
}
diff --git a/core/DataTable/Filter/ReplaceColumnNames.php b/core/DataTable/Filter/ReplaceColumnNames.php
index a4ff0d9baf..18942f70c1 100644
--- a/core/DataTable/Filter/ReplaceColumnNames.php
+++ b/core/DataTable/Filter/ReplaceColumnNames.php
@@ -1,115 +1,104 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* This filter replaces column names using a mapping table that maps from the old name to the new name.
- *
+ *
* Why this filter?
* For saving bytes in the database, you can change all the columns labels by an integer value.
* Exemple instead of saving 10000 rows with the column name 'nb_uniq_visitors' which would cost a lot of memory,
* we map it to the integer 1 before saving in the DB.
* After selecting the DataTable from the DB though, you need to restore back the real names so that
* it shows nicely in the report (XML for example).
- *
+ *
* You can specify the mapping array to apply in the constructor.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_ReplaceColumnNames extends Piwik_DataTable_Filter
{
- protected $mappingToApply;
-
- /**
- * @param Piwik_DataTable $table Table
- * @param array $mappingToApply Mapping to apply. Must have the format
- * array( OLD_COLUMN_NAME => NEW_COLUMN NAME,
- * OLD_COLUMN_NAME2 => NEW_COLUMN NAME2,
- * )
- */
- public function __construct( $table, $mappingToApply = null )
- {
- parent::__construct($table);
- $this->mappingToApply = Piwik_Archive::$mappingFromIdToName;
- if(!is_null($mappingToApply))
- {
- $this->mappingToApply = $mappingToApply;
- }
- }
+ protected $mappingToApply;
- /**
- * Executes the filter and renames the defined columns
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $key => $row)
- {
- $oldColumns = $row->getColumns();
- $newColumns = $this->getRenamedColumns($oldColumns);
- $row->setColumns( $newColumns );
- $this->filterSubTable($row);
- }
- }
+ /**
+ * @param Piwik_DataTable $table Table
+ * @param array $mappingToApply Mapping to apply. Must have the format
+ * array( OLD_COLUMN_NAME => NEW_COLUMN NAME,
+ * OLD_COLUMN_NAME2 => NEW_COLUMN NAME2,
+ * )
+ */
+ public function __construct($table, $mappingToApply = null)
+ {
+ parent::__construct($table);
+ $this->mappingToApply = Piwik_Archive::$mappingFromIdToName;
+ if (!is_null($mappingToApply)) {
+ $this->mappingToApply = $mappingToApply;
+ }
+ }
- /**
- * Checks the given columns and renames them if required
- *
- * @param array $columns
- * @return array
- */
- protected function getRenamedColumns($columns)
- {
- $newColumns = array();
- foreach($columns as $columnName => $columnValue)
- {
- if(isset($this->mappingToApply[$columnName]))
- {
- $columnName = $this->mappingToApply[$columnName];
-
- if($columnName == 'goals')
- {
- $newSubColumns = array();
- foreach($columnValue as $idGoal => $goalValues)
- {
- $mapping = Piwik_Archive::$mappingFromIdToNameGoal;
- if($idGoal == Piwik_Tracker_GoalManager::IDGOAL_CART)
- {
- $idGoal = Piwik_Archive::LABEL_ECOMMERCE_CART;
- }
- elseif($idGoal == Piwik_Tracker_GoalManager::IDGOAL_ORDER)
- {
- $idGoal = Piwik_Archive::LABEL_ECOMMERCE_ORDER;
- }
- foreach($goalValues as $id => $goalValue)
- {
- $subColumnName = $mapping[$id];
- $newSubColumns['idgoal='.$idGoal][$subColumnName] = $goalValue;
- }
- }
- $columnValue = $newSubColumns;
- }
- // If we happen to rename a column to a name that already exists,
- // sum both values in the column. This should really not happen, but
- // we introduced in 1.1 a new dataTable indexing scheme for Actions table, and
- // could end up with both strings and their int indexes counterpart in a monthly/yearly dataTable
- // built from DataTable with both formats
- if(isset($newColumns[$columnName]))
- {
- $columnValue += $newColumns[$columnName];
- }
- }
- $newColumns[$columnName] = $columnValue;
- }
- return $newColumns;
- }
+ /**
+ * Executes the filter and renames the defined columns
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $key => $row) {
+ $oldColumns = $row->getColumns();
+ $newColumns = $this->getRenamedColumns($oldColumns);
+ $row->setColumns($newColumns);
+ $this->filterSubTable($row);
+ }
+ }
+
+ /**
+ * Checks the given columns and renames them if required
+ *
+ * @param array $columns
+ * @return array
+ */
+ protected function getRenamedColumns($columns)
+ {
+ $newColumns = array();
+ foreach ($columns as $columnName => $columnValue) {
+ if (isset($this->mappingToApply[$columnName])) {
+ $columnName = $this->mappingToApply[$columnName];
+
+ if ($columnName == 'goals') {
+ $newSubColumns = array();
+ foreach ($columnValue as $idGoal => $goalValues) {
+ $mapping = Piwik_Archive::$mappingFromIdToNameGoal;
+ if ($idGoal == Piwik_Tracker_GoalManager::IDGOAL_CART) {
+ $idGoal = Piwik_Archive::LABEL_ECOMMERCE_CART;
+ } elseif ($idGoal == Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
+ $idGoal = Piwik_Archive::LABEL_ECOMMERCE_ORDER;
+ }
+ foreach ($goalValues as $id => $goalValue) {
+ $subColumnName = $mapping[$id];
+ $newSubColumns['idgoal=' . $idGoal][$subColumnName] = $goalValue;
+ }
+ }
+ $columnValue = $newSubColumns;
+ }
+ // If we happen to rename a column to a name that already exists,
+ // sum both values in the column. This should really not happen, but
+ // we introduced in 1.1 a new dataTable indexing scheme for Actions table, and
+ // could end up with both strings and their int indexes counterpart in a monthly/yearly dataTable
+ // built from DataTable with both formats
+ if (isset($newColumns[$columnName])) {
+ $columnValue += $newColumns[$columnName];
+ }
+ }
+ $newColumns[$columnName] = $columnValue;
+ }
+ return $newColumns;
+ }
}
diff --git a/core/DataTable/Filter/ReplaceSummaryRowLabel.php b/core/DataTable/Filter/ReplaceSummaryRowLabel.php
index ff70d3c4a9..b89f05871c 100644
--- a/core/DataTable/Filter/ReplaceSummaryRowLabel.php
+++ b/core/DataTable/Filter/ReplaceSummaryRowLabel.php
@@ -1,60 +1,55 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Filter_ReplaceSummaryRowLabel extends Piwik_DataTable_Filter
{
- /**
- * @param Piwik_DataTable $table
- * @param string|null $newLabel new label for summary row
- */
- public function __construct( $table, $newLabel = null)
- {
- parent::__construct($table);
- if(is_null($newLabel))
- {
- $newLabel = Piwik_Translate('General_Others');
- }
- $this->newLabel = $newLabel;
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @param string|null $newLabel new label for summary row
+ */
+ public function __construct($table, $newLabel = null)
+ {
+ parent::__construct($table);
+ if (is_null($newLabel)) {
+ $newLabel = Piwik_Translate('General_Others');
+ }
+ $this->newLabel = $newLabel;
+ }
- /**
- * Updates the summary row label
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- $rows = $table->getRows();
- foreach($rows as $row)
- {
- if($row->getColumn('label') == Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- $row->setColumn('label', $this->newLabel);
- break;
- }
- }
-
- // recurse
- foreach ($rows as $row)
- {
- if ($row->isSubtableLoaded())
- {
- $subTable = Piwik_DataTable_Manager::getInstance()->getTable($row->getIdSubDataTable());
- $this->filter($subTable);
- }
- }
- }
+ /**
+ * Updates the summary row label
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ $rows = $table->getRows();
+ foreach ($rows as $row) {
+ if ($row->getColumn('label') == Piwik_DataTable::LABEL_SUMMARY_ROW) {
+ $row->setColumn('label', $this->newLabel);
+ break;
+ }
+ }
+
+ // recurse
+ foreach ($rows as $row) {
+ if ($row->isSubtableLoaded()) {
+ $subTable = Piwik_DataTable_Manager::getInstance()->getTable($row->getIdSubDataTable());
+ $this->filter($subTable);
+ }
+ }
+ }
}
diff --git a/core/DataTable/Filter/SafeDecodeLabel.php b/core/DataTable/Filter/SafeDecodeLabel.php
index 035b67072a..ef8643b77f 100644
--- a/core/DataTable/Filter/SafeDecodeLabel.php
+++ b/core/DataTable/Filter/SafeDecodeLabel.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,64 +15,61 @@
*/
class Piwik_DataTable_Filter_SafeDecodeLabel extends Piwik_DataTable_Filter
{
- private $columnToDecode;
- static private $outputHtml = true;
+ private $columnToDecode;
+ static private $outputHtml = true;
- /**
- * @param Piwik_DataTable $table
- */
- public function __construct( $table )
- {
- parent::__construct($table);
- $this->columnToDecode = 'label';
- }
+ /**
+ * @param Piwik_DataTable $table
+ */
+ public function __construct($table)
+ {
+ parent::__construct($table);
+ $this->columnToDecode = 'label';
+ }
- /**
- * Decodes the given value
- *
- * @param string $value
- * @return mixed|string
- */
- static public function safeDecodeLabel($value)
- {
- if(empty($value)) {
- return $value;
- }
- $raw = urldecode($value);
- $value = htmlspecialchars_decode($raw , ENT_QUOTES);
- if(self::$outputHtml)
- {
- // Pre 5.3
- if(!defined('ENT_IGNORE')) {
- $style = ENT_QUOTES;
- } else {
- $style = ENT_QUOTES | ENT_IGNORE;
- }
- // See changes in 5.4: http://nikic.github.com/2012/01/28/htmlspecialchars-improvements-in-PHP-5-4.html
- // Note: at some point we should change ENT_IGNORE to ENT_SUBSTITUTE
- $value = htmlspecialchars($value, $style, 'UTF-8');
- }
- return $value;
- }
+ /**
+ * Decodes the given value
+ *
+ * @param string $value
+ * @return mixed|string
+ */
+ static public function safeDecodeLabel($value)
+ {
+ if (empty($value)) {
+ return $value;
+ }
+ $raw = urldecode($value);
+ $value = htmlspecialchars_decode($raw, ENT_QUOTES);
+ if (self::$outputHtml) {
+ // Pre 5.3
+ if (!defined('ENT_IGNORE')) {
+ $style = ENT_QUOTES;
+ } else {
+ $style = ENT_QUOTES | ENT_IGNORE;
+ }
+ // See changes in 5.4: http://nikic.github.com/2012/01/28/htmlspecialchars-improvements-in-PHP-5-4.html
+ // Note: at some point we should change ENT_IGNORE to ENT_SUBSTITUTE
+ $value = htmlspecialchars($value, $style, 'UTF-8');
+ }
+ return $value;
+ }
+
+ /**
+ * Decodes all columns of the given data table
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ foreach ($table->getRows() as $row) {
+ $value = $row->getColumn($this->columnToDecode);
+ if ($value !== false) {
+ $value = self::safeDecodeLabel($value);
+ $row->setColumn($this->columnToDecode, $value);
+
+ $this->filterSubTable($row);
+ }
+ }
+ }
- /**
- * Decodes all columns of the given data table
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- foreach($table->getRows() as $row)
- {
- $value = $row->getColumn($this->columnToDecode);
- if($value !== false)
- {
- $value = self::safeDecodeLabel($value);
- $row->setColumn($this->columnToDecode,$value);
-
- $this->filterSubTable($row);
- }
- }
- }
-
}
diff --git a/core/DataTable/Filter/Sort.php b/core/DataTable/Filter/Sort.php
index 89d5ee9e4c..ef56f70cad 100644
--- a/core/DataTable/Filter/Sort.php
+++ b/core/DataTable/Filter/Sort.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -18,217 +18,196 @@
*/
class Piwik_DataTable_Filter_Sort extends Piwik_DataTable_Filter
{
- protected $columnToSort;
- protected $order;
+ protected $columnToSort;
+ protected $order;
+
+ /**
+ * @param Piwik_DataTable $table
+ * @param string $columnToSort name of the column to sort by
+ * @param string $order order (asc|desc)
+ * @param bool $naturalSort use natural sort?
+ * @param bool $recursiveSort sort recursively?
+ */
+ 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->setOrder($order);
+ }
+
+ /**
+ * Updates the order
+ *
+ * @param string $order asc|desc
+ */
+ public function setOrder($order)
+ {
+ if ($order == 'asc') {
+ $this->order = 'asc';
+ $this->sign = 1;
+ } else {
+ $this->order = 'desc';
+ $this->sign = -1;
+ }
+ }
+
+ /**
+ * Sorting method used for sorting numbers
+ *
+ * @param number $a
+ * @param number $b
+ * @return int
+ */
+ public function sort($a, $b)
+ {
+ return !isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ && !isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+
+ ? 0
+ : (
+ !isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ ? 1
+ : (
+ !isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ ? -1
+ : (($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort] != $b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort]
+ || !isset($a->c[Piwik_DataTable_Row::COLUMNS]['label']))
+ ? ($this->sign * (
+ $a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort]
+ < $b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort]
+ ? -1
+ : 1)
+ )
+ : -1 * $this->sign * strnatcasecmp(
+ $a->c[Piwik_DataTable_Row::COLUMNS]['label'],
+ $b->c[Piwik_DataTable_Row::COLUMNS]['label'])
+ )
+ )
+ );
+ }
+
+ /**
+ * Sorting method used for sorting values natural
+ *
+ * @param mixed $a
+ * @param mixed $b
+ * @return int
+ */
+ function naturalSort($a, $b)
+ {
+ return !isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ && !isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ ? 0
+ : (!isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ ? 1
+ : (!isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ ? -1
+ : $this->sign * strnatcasecmp(
+ $a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort],
+ $b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort]
+ )
+ )
+ );
+ }
+
+ /**
+ * Sorting method used for sorting values
+ *
+ * @param mixed $a
+ * @param mixed $b
+ * @return int
+ */
+ function sortString($a, $b)
+ {
+ return !isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ && !isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ ? 0
+ : (!isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ ? 1
+ : (!isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
+ ? -1
+ : $this->sign *
+ strcasecmp($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort],
+ $b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort]
+ )
+ )
+ );
+ }
- /**
- * @param Piwik_DataTable $table
- * @param string $columnToSort name of the column to sort by
- * @param string $order order (asc|desc)
- * @param bool $naturalSort use natural sort?
- * @param bool $recursiveSort sort recursively?
- */
- 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->setOrder($order);
- }
+ /**
+ * Sets the column to be used for sorting
+ *
+ * @param Piwik_DataTable_Row $row
+ * @return int
+ */
+ protected function selectColumnToSort($row)
+ {
+ $value = $row->getColumn($this->columnToSort);
+ if ($value !== false) {
+ return $this->columnToSort;
+ }
- /**
- * Updates the order
- *
- * @param string $order asc|desc
- */
- public function setOrder($order)
- {
- if($order == 'asc')
- {
- $this->order = 'asc';
- $this->sign = 1;
- }
- else
- {
- $this->order = 'desc';
- $this->sign = -1;
- }
- }
+ // sorting by "nb_visits" but the index is Piwik_Archive::INDEX_NB_VISITS in the table
+ if (isset(Piwik_Archive::$mappingFromNameToId[$this->columnToSort])) {
+ $column = Piwik_Archive::$mappingFromNameToId[$this->columnToSort];
+ $value = $row->getColumn($column);
- /**
- * Sorting method used for sorting numbers
- *
- * @param number $a
- * @param number $b
- * @return int
- */
- public function sort($a, $b)
- {
- return !isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
- && !isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
-
- ? 0
- : (
- !isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
- ? 1
- : (
- !isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
- ? -1
- : ( ($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort] != $b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort]
- || !isset($a->c[Piwik_DataTable_Row::COLUMNS]['label']))
- ? ( $this->sign * (
- $a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort]
- < $b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort]
- ? -1
- : 1)
- )
- : -1 * $this->sign * strnatcasecmp(
- $a->c[Piwik_DataTable_Row::COLUMNS]['label'],
- $b->c[Piwik_DataTable_Row::COLUMNS]['label'])
- )
- )
- )
- ;
- }
+ if ($value !== false) {
+ return $column;
+ }
+ }
- /**
- * Sorting method used for sorting values natural
- *
- * @param mixed $a
- * @param mixed $b
- * @return int
- */
- function naturalSort($a, $b)
- {
- return !isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
- && !isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort] )
- ? 0
- : (!isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
- ? 1
- : (!isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort] )
- ? -1
- : $this->sign * strnatcasecmp(
- $a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort],
- $b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort]
- )
- )
- )
- ;
- }
+ // eg. was previously sorted by revenue_per_visit, but this table
+ // doesn't have this column; defaults with nb_visits
+ $column = Piwik_Archive::INDEX_NB_VISITS;
+ $value = $row->getColumn($column);
+ if ($value !== false) {
+ return $column;
+ }
- /**
- * Sorting method used for sorting values
- *
- * @param mixed $a
- * @param mixed $b
- * @return int
- */
- function sortString($a, $b)
- {
- return !isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
- && !isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort] )
- ? 0
- : (!isset($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort])
- ? 1
- : (!isset($b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort] )
- ? -1
- : $this->sign *
- strcasecmp($a->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort],
- $b->c[Piwik_DataTable_Row::COLUMNS][$this->columnToSort]
- )
- )
- )
- ;
- }
-
- /**
- * Sets the column to be used for sorting
- *
- * @param Piwik_DataTable_Row $row
- * @return int
- */
- protected function selectColumnToSort($row)
- {
- $value = $row->getColumn($this->columnToSort);
- if($value !== false)
- {
- return $this->columnToSort;
- }
-
- // sorting by "nb_visits" but the index is Piwik_Archive::INDEX_NB_VISITS in the table
- if(isset(Piwik_Archive::$mappingFromNameToId[$this->columnToSort]))
- {
- $column = Piwik_Archive::$mappingFromNameToId[$this->columnToSort];
- $value = $row->getColumn($column);
+ // even though this column is not set properly in the table,
+ // we select it for the sort, so that the table's internal state is set properly
+ return $this->columnToSort;
+ }
- if($value !== false)
- {
- return $column;
- }
- }
-
- // eg. was previously sorted by revenue_per_visit, but this table
- // doesn't have this column; defaults with nb_visits
- $column = Piwik_Archive::INDEX_NB_VISITS;
- $value = $row->getColumn($column);
- if($value !== false)
- {
- return $column;
- }
-
- // even though this column is not set properly in the table,
- // we select it for the sort, so that the table's internal state is set properly
- return $this->columnToSort;
- }
+ /**
+ * Sorts the given data table by defined column and sorting method
+ *
+ * @param Piwik_DataTable $table
+ * @return mixed
+ */
+ public function filter($table)
+ {
+ if ($table instanceof Piwik_DataTable_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);
- /**
- * Sorts the given data table by defined column and sorting method
- *
- * @param Piwik_DataTable $table
- * @return mixed
- */
- public function filter($table)
- {
- if($table instanceof Piwik_DataTable_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);
- if( is_numeric($value))
- {
- $methodToUse = "sort";
- }
- else
- {
- if($this->naturalSort)
- {
- $methodToUse = "naturalSort";
- }
- else
- {
- $methodToUse = "sortString";
- }
- }
- $table->sort( array($this,$methodToUse), $this->columnToSort );
- }
+ $value = $row->getColumn($this->columnToSort);
+ if (is_numeric($value)) {
+ $methodToUse = "sort";
+ } else {
+ if ($this->naturalSort) {
+ $methodToUse = "naturalSort";
+ } else {
+ $methodToUse = "sortString";
+ }
+ }
+ $table->sort(array($this, $methodToUse), $this->columnToSort);
+ }
}
diff --git a/core/DataTable/Filter/Truncate.php b/core/DataTable/Filter/Truncate.php
index d66bc80405..7c917034f9 100644
--- a/core/DataTable/Filter/Truncate.php
+++ b/core/DataTable/Filter/Truncate.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,34 +15,32 @@
*/
class Piwik_DataTable_Filter_Truncate extends Piwik_DataTable_Filter
{
- /**
- * @param Piwik_DataTable $table
- * @param int $truncateAfter
- */
- public function __construct( $table, $truncateAfter)
- {
- parent::__construct($table);
- $this->truncateAfter = $truncateAfter;
- }
+ /**
+ * @param Piwik_DataTable $table
+ * @param int $truncateAfter
+ */
+ public function __construct($table, $truncateAfter)
+ {
+ parent::__construct($table);
+ $this->truncateAfter = $truncateAfter;
+ }
- /**
- * Truncates the table after X rows and adds a summary row
- *
- * @param Piwik_DataTable $table
- */
- public function filter($table)
- {
- $table->filter('AddSummaryRow', array($this->truncateAfter));
- $table->filter('ReplaceSummaryRowLabel');
-
- foreach($table->getRows() as $row)
- {
- if($row->isSubtableLoaded())
- {
- $idSubTable = $row->getIdSubDataTable();
- $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
- $subTable->filter('Truncate', array($this->truncateAfter));
- }
- }
- }
+ /**
+ * Truncates the table after X rows and adds a summary row
+ *
+ * @param Piwik_DataTable $table
+ */
+ public function filter($table)
+ {
+ $table->filter('AddSummaryRow', array($this->truncateAfter));
+ $table->filter('ReplaceSummaryRowLabel');
+
+ foreach ($table->getRows() as $row) {
+ if ($row->isSubtableLoaded()) {
+ $idSubTable = $row->getIdSubDataTable();
+ $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
+ $subTable->filter('Truncate', array($this->truncateAfter));
+ }
+ }
+ }
}
diff --git a/core/DataTable/Manager.php b/core/DataTable/Manager.php
index c7062ebdaa..3fff1bcc2d 100644
--- a/core/DataTable/Manager.php
+++ b/core/DataTable/Manager.php
@@ -1,158 +1,149 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * The DataTable_Manager registers all the instanciated DataTable and provides an
+ * The DataTable_Manager registers all the instanciated DataTable and provides an
* easy way to access them. This is used to store all the DataTable during the archiving process.
* At the end of archiving, the ArchiveProcessing will read the stored datatable and record them in the DB.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Manager
{
- static private $instance = null;
- /**
- * Returns instance
- *
- * @return Piwik_DataTable_Manager
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Array used to store the DataTable
- *
- * @var array
- */
- protected $tables = array();
-
- /**
- * Id of the next inserted table id in the Manager
- * @var int
- */
- protected $nextTableId = 1;
-
- /**
- * Add a DataTable to the registry
- *
- * @param Piwik_DataTable $table
- * @return int Index of the table in the manager array
- */
- public function addTable( $table )
- {
- $this->tables[$this->nextTableId] = $table;
- $this->nextTableId++;
- return $this->nextTableId - 1;
- }
+ static private $instance = null;
- /**
- * Returns the DataTable associated to the ID $idTable.
- * NB: The datatable has to have been instanciated before!
- * This method will not fetch the DataTable from the DB.
- *
- * @param int $idTable
- * @throws Exception If the table can't be found
- * @return Piwik_DataTable The table
- */
- public function getTable( $idTable )
- {
- if(!isset($this->tables[$idTable]))
- {
- throw new Exception(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];
- }
-
- /**
- * Returns the latest used table ID
- *
- * @return int
- */
- public function getMostRecentTableId()
- {
- return $this->nextTableId - 1;
- }
-
- /**
- * Delete all the registered DataTables from the manager
- */
- public function deleteAll( $deleteWhenIdTableGreaterThan = 0)
- {
- foreach($this->tables as $id => $table)
- {
- if($id > $deleteWhenIdTableGreaterThan)
- {
- $this->deleteTable($id);
- }
- }
- if($deleteWhenIdTableGreaterThan == 0)
- {
- $this->tables = array();
- $this->nextTableId = 1;
- }
- }
-
- /**
- * Deletes (unsets) the datatable given its id and removes it from the manager
- * Subsequent get for this table will fail
- *
- * @param int $id
- */
- public function deleteTable( $id )
- {
- if(isset($this->tables[$id]))
- {
- destroy($this->tables[$id]);
- $this->setTableDeleted($id);
- }
- }
-
- /**
- * Remove the table from the manager (table has already been unset)
- *
- * @param int $id
- */
- public function setTableDeleted($id)
- {
- $this->tables[$id] = null;
- }
-
- /**
- * Debug only. Dumps all tables currently registered in the Manager
- */
- public function dumpAllTables()
- {
- echo "<hr />Piwik_DataTable_Manager->dumpAllTables()<br />";
- foreach($this->tables as $id => $table)
- {
- if(!($table instanceof Piwik_DataTable ))
- {
- echo "Error table $id is not instance of datatable<br />";
- var_dump($table);
- }
- else
- {
- echo "<hr />";
- echo "Table (index=$id) TableId = ". $table->getId() . "<br />";
- echo $table;
- echo "<br />";
- }
- }
- echo "<br />-- End Piwik_DataTable_Manager->dumpAllTables()<hr />";
- }
+ /**
+ * Returns instance
+ *
+ * @return Piwik_DataTable_Manager
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Array used to store the DataTable
+ *
+ * @var array
+ */
+ protected $tables = array();
+
+ /**
+ * Id of the next inserted table id in the Manager
+ * @var int
+ */
+ protected $nextTableId = 1;
+
+ /**
+ * Add a DataTable to the registry
+ *
+ * @param Piwik_DataTable $table
+ * @return int Index of the table in the manager array
+ */
+ public function addTable($table)
+ {
+ $this->tables[$this->nextTableId] = $table;
+ $this->nextTableId++;
+ return $this->nextTableId - 1;
+ }
+
+ /**
+ * Returns the DataTable associated to the ID $idTable.
+ * NB: The datatable has to have been instanciated before!
+ * This method will not fetch the DataTable from the DB.
+ *
+ * @param int $idTable
+ * @throws Exception If the table can't be found
+ * @return Piwik_DataTable The table
+ */
+ public function getTable($idTable)
+ {
+ if (!isset($this->tables[$idTable])) {
+ throw new Exception(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];
+ }
+
+ /**
+ * Returns the latest used table ID
+ *
+ * @return int
+ */
+ public function getMostRecentTableId()
+ {
+ return $this->nextTableId - 1;
+ }
+
+ /**
+ * Delete all the registered DataTables from the manager
+ */
+ public function deleteAll($deleteWhenIdTableGreaterThan = 0)
+ {
+ foreach ($this->tables as $id => $table) {
+ if ($id > $deleteWhenIdTableGreaterThan) {
+ $this->deleteTable($id);
+ }
+ }
+ if ($deleteWhenIdTableGreaterThan == 0) {
+ $this->tables = array();
+ $this->nextTableId = 1;
+ }
+ }
+
+ /**
+ * Deletes (unsets) the datatable given its id and removes it from the manager
+ * Subsequent get for this table will fail
+ *
+ * @param int $id
+ */
+ public function deleteTable($id)
+ {
+ if (isset($this->tables[$id])) {
+ destroy($this->tables[$id]);
+ $this->setTableDeleted($id);
+ }
+ }
+
+ /**
+ * Remove the table from the manager (table has already been unset)
+ *
+ * @param int $id
+ */
+ public function setTableDeleted($id)
+ {
+ $this->tables[$id] = null;
+ }
+
+ /**
+ * Debug only. Dumps all tables currently registered in the Manager
+ */
+ public function dumpAllTables()
+ {
+ echo "<hr />Piwik_DataTable_Manager->dumpAllTables()<br />";
+ foreach ($this->tables as $id => $table) {
+ if (!($table instanceof Piwik_DataTable)) {
+ echo "Error table $id is not instance of datatable<br />";
+ var_dump($table);
+ } else {
+ echo "<hr />";
+ echo "Table (index=$id) TableId = " . $table->getId() . "<br />";
+ echo $table;
+ echo "<br />";
+ }
+ }
+ echo "<br />-- End Piwik_DataTable_Manager->dumpAllTables()<hr />";
+ }
}
diff --git a/core/DataTable/Renderer.php b/core/DataTable/Renderer.php
index fbc522f5e8..38855b65bc 100644
--- a/core/DataTable/Renderer.php
+++ b/core/DataTable/Renderer.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -16,421 +16,399 @@
* $render = new Piwik_DataTable_Renderer_Xml();
* $render->setTable($dataTable);
* echo $render;
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
abstract class Piwik_DataTable_Renderer
{
- protected $table;
- protected $exception;
- protected $renderSubTables = false;
- protected $hideIdSubDatatable = false;
-
- /**
- * Whether to translate column names (i.e. metric names) or not
- * @var bool
- */
- public $translateColumnNames = false;
-
- /**
- * Column translations
- * @var array
- */
- private $columnTranslations = false;
-
- /**
- * The API method that has returned the data that should be rendered
- * @var string
- */
- public $apiMethod = false;
-
- /**
- * API metadata for the current report
- * @var array
- */
- private $apiMetaData = null;
-
- /**
- * The current idSite
- * @var int
- */
- public $idSite = 'all';
-
-
- public function __construct()
- {
- }
-
- /**
- * Sets whether to render subtables or not
- *
- * @param bool $enableRenderSubTable
- */
- public function setRenderSubTables($enableRenderSubTable)
- {
- $this->renderSubTables = (bool)$enableRenderSubTable;
- }
-
- /**
- * @param bool $bool
- */
- public function setHideIdSubDatableFromResponse($bool)
- {
- $this->hideIdSubDatatable = (bool)$bool;
- }
-
- /**
- * Returns whether to render subtables or not
- *
- * @return bool
- */
- protected function isRenderSubtables()
- {
- return $this->renderSubTables;
- }
-
- /**
- * Output HTTP Content-Type header
- */
- protected function renderHeader()
- {
- @header('Content-Type: text/plain; charset=utf-8');
- }
-
- /**
- * Computes the dataTable output and returns the string/binary
- *
- * @return string
- */
- abstract public function render();
-
- /**
- * Computes the exception output and returns the string/binary
- *
- * @return string
- */
- abstract public function renderException();
-
- protected function getExceptionMessage()
- {
- $message = self::renderHtmlEntities($this->exception->getMessage());
-
- // DEBUG
+ protected $table;
+ protected $exception;
+ protected $renderSubTables = false;
+ protected $hideIdSubDatatable = false;
+
+ /**
+ * Whether to translate column names (i.e. metric names) or not
+ * @var bool
+ */
+ public $translateColumnNames = false;
+
+ /**
+ * Column translations
+ * @var array
+ */
+ private $columnTranslations = false;
+
+ /**
+ * The API method that has returned the data that should be rendered
+ * @var string
+ */
+ public $apiMethod = false;
+
+ /**
+ * API metadata for the current report
+ * @var array
+ */
+ private $apiMetaData = null;
+
+ /**
+ * The current idSite
+ * @var int
+ */
+ public $idSite = 'all';
+
+
+ public function __construct()
+ {
+ }
+
+ /**
+ * Sets whether to render subtables or not
+ *
+ * @param bool $enableRenderSubTable
+ */
+ public function setRenderSubTables($enableRenderSubTable)
+ {
+ $this->renderSubTables = (bool)$enableRenderSubTable;
+ }
+
+ /**
+ * @param bool $bool
+ */
+ public function setHideIdSubDatableFromResponse($bool)
+ {
+ $this->hideIdSubDatatable = (bool)$bool;
+ }
+
+ /**
+ * Returns whether to render subtables or not
+ *
+ * @return bool
+ */
+ protected function isRenderSubtables()
+ {
+ return $this->renderSubTables;
+ }
+
+ /**
+ * Output HTTP Content-Type header
+ */
+ protected function renderHeader()
+ {
+ @header('Content-Type: text/plain; charset=utf-8');
+ }
+
+ /**
+ * Computes the dataTable output and returns the string/binary
+ *
+ * @return string
+ */
+ abstract public function render();
+
+ /**
+ * Computes the exception output and returns the string/binary
+ *
+ * @return string
+ */
+ abstract public function renderException();
+
+ protected function getExceptionMessage()
+ {
+ $message = self::renderHtmlEntities($this->exception->getMessage());
+
+ // DEBUG
// $message .= $this->exception->getTraceAsString();
- return $message;
- }
-
- /**
- * @see render()
- * @return string
- */
- public function __toString()
- {
- return $this->render();
- }
-
- /**
- * Set the DataTable to be rendered
- *
- * @param Piwik_DataTable|Piwik_DataTable_Simple|Piwik_DataTable_Array $table table to be rendered
- * @throws Exception
- */
- public function setTable($table)
- {
- if (!is_array($table)
- && !($table instanceof Piwik_DataTable)
- && !($table instanceof Piwik_DataTable_Array))
- {
- throw new Exception("DataTable renderers renderer accepts only Piwik_DataTable and Piwik_DataTable_Array instances, and array instances.");
- }
- $this->table = $table;
- }
-
- /**
- * Set the Exception to be rendered
- *
- * @param Exception $exception to be rendered
- * @throws Exception
- */
- public function setException($exception)
- {
- if(!($exception instanceof Exception))
- {
- throw new Exception("The exception renderer accepts only an Exception object.");
- }
- $this->exception = $exception;
- }
-
-
- /**
- * @var array
- */
- static protected $availableRenderers = array( 'xml',
- 'json',
- 'csv',
- 'tsv',
- 'html',
- 'php'
- );
-
- /**
- * Returns available renderers
- *
- * @return array
- */
- static public function getRenderers()
- {
- return self::$availableRenderers;
- }
-
- /**
- * Returns the DataTable associated to the output format $name
- *
- * @param string $name
- * @throws Exception If the renderer is unknown
- * @return Piwik_DataTable_Renderer
- */
- static public function factory( $name )
- {
- $name = ucfirst(strtolower($name));
- $className = 'Piwik_DataTable_Renderer_' . $name;
-
- try {
- Piwik_Loader::loadClass($className);
- return new $className;
- } catch(Exception $e) {
- $availableRenderers = implode(', ', self::getRenderers());
- @header('Content-Type: text/plain; charset=utf-8');
- throw new Exception(Piwik_TranslateException('General_ExceptionInvalidRendererFormat', array($name, $availableRenderers)));
- }
- }
-
- /**
- * Returns $rawData after all applicable characters have been converted to HTML entities.
- *
- * @param String $rawData data to be converted
- * @return String
- */
- static protected function renderHtmlEntities( $rawData )
- {
- return self::formatValueXml($rawData);
- }
-
- /**
- * Format a value to xml
- *
- * @param string|number|bool $value value to format
- * @return int|string
- */
- public static function formatValueXml($value)
- {
- if(is_string($value)
- && !is_numeric($value))
- {
- $value = html_entity_decode($value, ENT_COMPAT, 'UTF-8');
- // make sure non-UTF-8 chars don't cause htmlspecialchars to choke
- if (function_exists('mb_convert_encoding'))
- {
- $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);
- }
- elseif($value===false)
- {
- $value = 0;
- }
- return $value;
- }
-
- /**
- * Translate column names to the current language.
- * Used in subclasses.
- *
- * @param array $names
- * @return array
- */
- protected function translateColumnNames($names)
- {
- if (!$this->apiMethod)
- {
- return $names;
- }
-
- // load the translations only once
- // when multiple dates are requested (date=...,...&period=day), the meta data would
- // be loaded lots of times otherwise
- if ($this->columnTranslations === false)
- {
- $meta = $this->getApiMetaData();
- if ($meta === false)
- {
- return $names;
- }
-
- $t = Piwik_API_API::getDefaultMetricTranslations();
- foreach (array('metrics', 'processedMetrics', 'metricsGoal', 'processedMetricsGoal') as $index)
- {
- if (isset($meta[$index]) && is_array($meta[$index]))
- {
- $t = array_merge($t, $meta[$index]);
- }
- }
-
- $this->columnTranslations = &$t;
- }
-
- foreach ($names as &$name)
- {
- if (isset($this->columnTranslations[$name]))
- {
- $name = $this->columnTranslations[$name];
- }
- }
-
- return $names;
- }
-
- /**
- * @return array|null
- */
- protected function getApiMetaData()
- {
- if ($this->apiMetaData === null)
- {
- list($apiModule, $apiAction) = explode('.', $this->apiMethod);
-
- if(!$apiModule || !$apiAction)
- {
- $this->apiMetaData = false;
- }
-
- $api = Piwik_API_API::getInstance();
- $meta = $api->getMetadata($this->idSite, $apiModule, $apiAction);
- if (is_array($meta[0]))
- {
- $meta = $meta[0];
- }
-
- $this->apiMetaData = &$meta;
- }
-
- return $this->apiMetaData;
- }
-
- /**
- * Translates the given column name
- *
- * @param string $column
- * @return mixed
- */
- protected function translateColumnName($column)
- {
- $columns = array($column);
- $columns = $this->translateColumnNames($columns);
- return $columns[0];
- }
-
- /**
- * Enables column translating
- *
- * @param bool $bool
- */
- public function setTranslateColumnNames($bool)
- {
- $this->translateColumnNames = $bool;
- }
-
- /**
- * Sets the api method
- *
- * @param $method
- */
- public function setApiMethod($method)
- {
- $this->apiMethod = $method;
- }
-
- /**
- * Sets the site id
- *
- * @param int $idSite
- */
- public function setIdSite($idSite)
- {
- $this->idSite = $idSite;
- }
-
- /**
- * Returns true if an array should be wrapped before rendering. This is used to
- * mimic quirks in the old rendering logic (for backwards compatibility). The
- * specific meaning of 'wrap' is left up to the Renderer. For XML, this means a
- * new <row> node. For JSON, this means wrapping in an array.
- *
- * In the old code, arrays were added to new DataTable instances, and then rendered.
- * This transformation wrapped associative arrays except under certain circumstances,
- * including:
- * - single element (ie, array('nb_visits' => 0)) (not wrapped for some renderers)
- * - empty array (ie, array())
- * - array w/ arrays/DataTable instances as values (ie,
- * array('name' => 'myreport',
- * 'reportData' => new Piwik_DataTable())
- * OR array('name' => 'myreport',
- * 'reportData' => array(...)) )
- *
- * @param array $array
- * @param bool $wrapSingleValues Whether to wrap array('key' => 'value') arrays. Some
- * renderers wrap them and some don't.
- * @param bool|null $isAssociativeArray Whether the array is associative or not.
- * If null, it is determined.
- * @return bool
- */
- protected static function shouldWrapArrayBeforeRendering(
- $array, $wrapSingleValues = true, $isAssociativeArray = null )
- {
- if (empty($array))
- {
- return false;
- }
-
- if ($isAssociativeArray === null)
- {
- $isAssociativeArray = Piwik::isAssociativeArray($array);
- }
-
- $wrap = true;
- if ($isAssociativeArray)
- {
- // we don't wrap if the array has one element that is a value
- $firstValue = reset($array);
- if (!$wrapSingleValues
- && count($array) === 1
- && (!is_array($firstValue)
- && !is_object($firstValue)))
- {
- $wrap = false;
- }
- else
- {
- foreach ($array as $value)
- {
- if (is_array($value)
- || is_object($value))
- {
- $wrap = false;
- break;
- }
- }
- }
- }
- else
- {
- $wrap = false;
- }
-
- return $wrap;
- }
+ return $message;
+ }
+
+ /**
+ * @see render()
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render();
+ }
+
+ /**
+ * Set the DataTable to be rendered
+ *
+ * @param Piwik_DataTable|Piwik_DataTable_Simple|Piwik_DataTable_Array $table table to be rendered
+ * @throws Exception
+ */
+ public function setTable($table)
+ {
+ if (!is_array($table)
+ && !($table instanceof Piwik_DataTable)
+ && !($table instanceof Piwik_DataTable_Array)
+ ) {
+ throw new Exception("DataTable renderers renderer accepts only Piwik_DataTable and Piwik_DataTable_Array instances, and array instances.");
+ }
+ $this->table = $table;
+ }
+
+ /**
+ * Set the Exception to be rendered
+ *
+ * @param Exception $exception to be rendered
+ * @throws Exception
+ */
+ public function setException($exception)
+ {
+ if (!($exception instanceof Exception)) {
+ throw new Exception("The exception renderer accepts only an Exception object.");
+ }
+ $this->exception = $exception;
+ }
+
+
+ /**
+ * @var array
+ */
+ static protected $availableRenderers = array('xml',
+ 'json',
+ 'csv',
+ 'tsv',
+ 'html',
+ 'php'
+ );
+
+ /**
+ * Returns available renderers
+ *
+ * @return array
+ */
+ static public function getRenderers()
+ {
+ return self::$availableRenderers;
+ }
+
+ /**
+ * Returns the DataTable associated to the output format $name
+ *
+ * @param string $name
+ * @throws Exception If the renderer is unknown
+ * @return Piwik_DataTable_Renderer
+ */
+ static public function factory($name)
+ {
+ $name = ucfirst(strtolower($name));
+ $className = 'Piwik_DataTable_Renderer_' . $name;
+
+ try {
+ Piwik_Loader::loadClass($className);
+ return new $className;
+ } catch (Exception $e) {
+ $availableRenderers = implode(', ', self::getRenderers());
+ @header('Content-Type: text/plain; charset=utf-8');
+ throw new Exception(Piwik_TranslateException('General_ExceptionInvalidRendererFormat', array($name, $availableRenderers)));
+ }
+ }
+
+ /**
+ * Returns $rawData after all applicable characters have been converted to HTML entities.
+ *
+ * @param String $rawData data to be converted
+ * @return String
+ */
+ static protected function renderHtmlEntities($rawData)
+ {
+ return self::formatValueXml($rawData);
+ }
+
+ /**
+ * Format a value to xml
+ *
+ * @param string|number|bool $value value to format
+ * @return int|string
+ */
+ public static function formatValueXml($value)
+ {
+ if (is_string($value)
+ && !is_numeric($value)
+ ) {
+ $value = html_entity_decode($value, ENT_COMPAT, 'UTF-8');
+ // make sure non-UTF-8 chars don't cause htmlspecialchars to choke
+ if (function_exists('mb_convert_encoding')) {
+ $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);
+ } elseif ($value === false) {
+ $value = 0;
+ }
+ return $value;
+ }
+
+ /**
+ * Translate column names to the current language.
+ * Used in subclasses.
+ *
+ * @param array $names
+ * @return array
+ */
+ protected function translateColumnNames($names)
+ {
+ if (!$this->apiMethod) {
+ return $names;
+ }
+
+ // load the translations only once
+ // when multiple dates are requested (date=...,...&period=day), the meta data would
+ // be loaded lots of times otherwise
+ if ($this->columnTranslations === false) {
+ $meta = $this->getApiMetaData();
+ if ($meta === false) {
+ return $names;
+ }
+
+ $t = Piwik_API_API::getDefaultMetricTranslations();
+ foreach (array('metrics', 'processedMetrics', 'metricsGoal', 'processedMetricsGoal') as $index) {
+ if (isset($meta[$index]) && is_array($meta[$index])) {
+ $t = array_merge($t, $meta[$index]);
+ }
+ }
+
+ $this->columnTranslations = & $t;
+ }
+
+ foreach ($names as &$name) {
+ if (isset($this->columnTranslations[$name])) {
+ $name = $this->columnTranslations[$name];
+ }
+ }
+
+ return $names;
+ }
+
+ /**
+ * @return array|null
+ */
+ protected function getApiMetaData()
+ {
+ if ($this->apiMetaData === null) {
+ list($apiModule, $apiAction) = explode('.', $this->apiMethod);
+
+ if (!$apiModule || !$apiAction) {
+ $this->apiMetaData = false;
+ }
+
+ $api = Piwik_API_API::getInstance();
+ $meta = $api->getMetadata($this->idSite, $apiModule, $apiAction);
+ if (is_array($meta[0])) {
+ $meta = $meta[0];
+ }
+
+ $this->apiMetaData = & $meta;
+ }
+
+ return $this->apiMetaData;
+ }
+
+ /**
+ * Translates the given column name
+ *
+ * @param string $column
+ * @return mixed
+ */
+ protected function translateColumnName($column)
+ {
+ $columns = array($column);
+ $columns = $this->translateColumnNames($columns);
+ return $columns[0];
+ }
+
+ /**
+ * Enables column translating
+ *
+ * @param bool $bool
+ */
+ public function setTranslateColumnNames($bool)
+ {
+ $this->translateColumnNames = $bool;
+ }
+
+ /**
+ * Sets the api method
+ *
+ * @param $method
+ */
+ public function setApiMethod($method)
+ {
+ $this->apiMethod = $method;
+ }
+
+ /**
+ * Sets the site id
+ *
+ * @param int $idSite
+ */
+ public function setIdSite($idSite)
+ {
+ $this->idSite = $idSite;
+ }
+
+ /**
+ * Returns true if an array should be wrapped before rendering. This is used to
+ * mimic quirks in the old rendering logic (for backwards compatibility). The
+ * specific meaning of 'wrap' is left up to the Renderer. For XML, this means a
+ * new <row> node. For JSON, this means wrapping in an array.
+ *
+ * In the old code, arrays were added to new DataTable instances, and then rendered.
+ * This transformation wrapped associative arrays except under certain circumstances,
+ * including:
+ * - single element (ie, array('nb_visits' => 0)) (not wrapped for some renderers)
+ * - empty array (ie, array())
+ * - array w/ arrays/DataTable instances as values (ie,
+ * array('name' => 'myreport',
+ * 'reportData' => new Piwik_DataTable())
+ * OR array('name' => 'myreport',
+ * 'reportData' => array(...)) )
+ *
+ * @param array $array
+ * @param bool $wrapSingleValues Whether to wrap array('key' => 'value') arrays. Some
+ * renderers wrap them and some don't.
+ * @param bool|null $isAssociativeArray Whether the array is associative or not.
+ * If null, it is determined.
+ * @return bool
+ */
+ protected static function shouldWrapArrayBeforeRendering(
+ $array, $wrapSingleValues = true, $isAssociativeArray = null)
+ {
+ if (empty($array)) {
+ return false;
+ }
+
+ if ($isAssociativeArray === null) {
+ $isAssociativeArray = Piwik::isAssociativeArray($array);
+ }
+
+ $wrap = true;
+ if ($isAssociativeArray) {
+ // we don't wrap if the array has one element that is a value
+ $firstValue = reset($array);
+ if (!$wrapSingleValues
+ && count($array) === 1
+ && (!is_array($firstValue)
+ && !is_object($firstValue))
+ ) {
+ $wrap = false;
+ } else {
+ foreach ($array as $value) {
+ if (is_array($value)
+ || is_object($value)
+ ) {
+ $wrap = false;
+ break;
+ }
+ }
+ }
+ } else {
+ $wrap = false;
+ }
+
+ return $wrap;
+ }
}
diff --git a/core/DataTable/Renderer/Console.php b/core/DataTable/Renderer/Console.php
index 941471f91d..c78b4eda1f 100644
--- a/core/DataTable/Renderer/Console.php
+++ b/core/DataTable/Renderer/Console.php
@@ -1,179 +1,164 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Simple output
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Renderer_Console extends Piwik_DataTable_Renderer
{
- /**
- * Prefix
- *
- * @var string
- */
- protected $prefixRows = '#';
+ /**
+ * Prefix
+ *
+ * @var string
+ */
+ protected $prefixRows = '#';
- /**
- * Computes the dataTable output and returns the string/binary
- *
- * @return string
- */
- public function render()
- {
- $this->renderHeader();
- return $this->renderTable($this->table);
- }
+ /**
+ * Computes the dataTable output and returns the string/binary
+ *
+ * @return string
+ */
+ public function render()
+ {
+ $this->renderHeader();
+ return $this->renderTable($this->table);
+ }
- /**
- * Computes the exception output and returns the string/binary
- *
- * @return string
- */
- public function renderException()
- {
- $this->renderHeader();
- $exceptionMessage = $this->getExceptionMessage();
- return 'Error: '.$exceptionMessage;
- }
+ /**
+ * Computes the exception output and returns the string/binary
+ *
+ * @return string
+ */
+ public function renderException()
+ {
+ $this->renderHeader();
+ $exceptionMessage = $this->getExceptionMessage();
+ return 'Error: ' . $exceptionMessage;
+ }
- /**
- * Sets the prefix to be used
- *
- * @param string $str new prefix
- */
- public function setPrefixRow($str)
- {
- $this->prefixRows = $str;
- }
+ /**
+ * Sets the prefix to be used
+ *
+ * @param string $str new prefix
+ */
+ public function setPrefixRow($str)
+ {
+ $this->prefixRows = $str;
+ }
- /**
- * Computes the output of the given array of data tables
- *
- * @param Piwik_DataTable_Array $tableArray data tables to render
- * @param string $prefix prefix to output before table data
- * @return string
- */
- protected function renderDataTableArray(Piwik_DataTable_Array $tableArray, $prefix )
- {
- $output = "Piwik_DataTable_Array<hr />";
- $prefix = $prefix . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
- foreach($tableArray->getArray() as $descTable => $table)
- {
- $output .= $prefix . "<b>". $descTable. "</b><br />";
- $output .= $prefix . $this->renderTable($table, $prefix . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;');
- $output .= "<hr />";
- }
- return $output;
- }
+ /**
+ * Computes the output of the given array of data tables
+ *
+ * @param Piwik_DataTable_Array $tableArray data tables to render
+ * @param string $prefix prefix to output before table data
+ * @return string
+ */
+ protected function renderDataTableArray(Piwik_DataTable_Array $tableArray, $prefix)
+ {
+ $output = "Piwik_DataTable_Array<hr />";
+ $prefix = $prefix . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
+ foreach ($tableArray->getArray() as $descTable => $table) {
+ $output .= $prefix . "<b>" . $descTable . "</b><br />";
+ $output .= $prefix . $this->renderTable($table, $prefix . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;');
+ $output .= "<hr />";
+ }
+ return $output;
+ }
- /**
- * Computes the given dataTable output and returns the string/binary
- *
- * @param Piwik_DataTable $table data table to render
- * @param string $prefix prefix to output before table data
- * @return string
- */
- protected function renderTable($table, $prefix = "")
- {
- if (is_array($table)) // convert array to DataTable
- {
- $table = Piwik_DataTable::makeFromSimpleArray($table);
- }
-
- if($table instanceof Piwik_DataTable_Array)
- {
- return $this->renderDataTableArray($table, $prefix);
- }
-
- if($table->getRowsCount() == 0)
- {
- return "Empty table<br />\n";
- }
-
- static $depth=0;
- $output = '';
- $i = 1;
- foreach($table->getRows() as $row)
- {
- $dataTableArrayBreak = false;
- $columns=array();
- foreach($row->getColumns() as $column => $value)
- {
- if($value instanceof Piwik_DataTable_Array )
- {
- $output .= $this->renderDataTableArray($value, $prefix);
- $dataTableArrayBreak = true;
- break;
- }
- if(is_string($value)) $value = "'$value'";
- elseif(is_array($value)) $value = var_export($value, true);
-
- $columns[] = "'$column' => $value";
- }
- if($dataTableArrayBreak === true)
- {
- continue;
- }
- $columns = implode(", ", $columns);
-
- $metadata = array();
- foreach($row->getMetadata() as $name => $value)
- {
- if(is_string($value)) $value = "'$value'";
- elseif(is_array($value)) $value = var_export($value, true);
- $metadata[] = "'$name' => $value";
- }
- $metadata = implode(", ", $metadata);
-
- $output.= str_repeat($this->prefixRows, $depth)
- . "- $i [".$columns."] [".$metadata."] [idsubtable = "
- . $row->getIdSubDataTable()."]<br />\n";
-
- if(!is_null($row->getIdSubDataTable()))
- {
- if($row->isSubtableLoaded())
- {
- $depth++;
- $output.= $this->renderTable(
- Piwik_DataTable_Manager::getInstance()->getTable(
- $row->getIdSubDataTable()
- ),
- $prefix . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
- );
- $depth--;
- }
- else
- {
- $output.= "-- Sub DataTable not loaded<br />\n";
- }
- }
- $i++;
- }
-
- if (!empty($table->metadata))
- {
- $output .= "<hr />Metadata<br />";
- foreach($table->metadata as $id => $metadata)
- {
- $output .= "<br />";
- $output .= $prefix . " <b>$id</b><br />";
- foreach($metadata as $name => $value)
- {
- $output .= $prefix . $prefix . "$name => $value";
- }
- }
- }
- return $output;
- }
+ /**
+ * Computes the given dataTable output and returns the string/binary
+ *
+ * @param Piwik_DataTable $table data table to render
+ * @param string $prefix prefix to output before table data
+ * @return string
+ */
+ protected function renderTable($table, $prefix = "")
+ {
+ if (is_array($table)) // convert array to DataTable
+ {
+ $table = Piwik_DataTable::makeFromSimpleArray($table);
+ }
+
+ if ($table instanceof Piwik_DataTable_Array) {
+ return $this->renderDataTableArray($table, $prefix);
+ }
+
+ if ($table->getRowsCount() == 0) {
+ return "Empty table<br />\n";
+ }
+
+ static $depth = 0;
+ $output = '';
+ $i = 1;
+ foreach ($table->getRows() as $row) {
+ $dataTableArrayBreak = false;
+ $columns = array();
+ foreach ($row->getColumns() as $column => $value) {
+ if ($value instanceof Piwik_DataTable_Array) {
+ $output .= $this->renderDataTableArray($value, $prefix);
+ $dataTableArrayBreak = true;
+ break;
+ }
+ if (is_string($value)) $value = "'$value'";
+ elseif (is_array($value)) $value = var_export($value, true);
+
+ $columns[] = "'$column' => $value";
+ }
+ if ($dataTableArrayBreak === true) {
+ continue;
+ }
+ $columns = implode(", ", $columns);
+
+ $metadata = array();
+ foreach ($row->getMetadata() as $name => $value) {
+ if (is_string($value)) $value = "'$value'";
+ elseif (is_array($value)) $value = var_export($value, true);
+ $metadata[] = "'$name' => $value";
+ }
+ $metadata = implode(", ", $metadata);
+
+ $output .= str_repeat($this->prefixRows, $depth)
+ . "- $i [" . $columns . "] [" . $metadata . "] [idsubtable = "
+ . $row->getIdSubDataTable() . "]<br />\n";
+
+ if (!is_null($row->getIdSubDataTable())) {
+ if ($row->isSubtableLoaded()) {
+ $depth++;
+ $output .= $this->renderTable(
+ Piwik_DataTable_Manager::getInstance()->getTable(
+ $row->getIdSubDataTable()
+ ),
+ $prefix . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
+ );
+ $depth--;
+ } else {
+ $output .= "-- Sub DataTable not loaded<br />\n";
+ }
+ }
+ $i++;
+ }
+
+ if (!empty($table->metadata)) {
+ $output .= "<hr />Metadata<br />";
+ foreach ($table->metadata as $id => $metadata) {
+ $output .= "<br />";
+ $output .= $prefix . " <b>$id</b><br />";
+ foreach ($metadata as $name => $value) {
+ $output .= $prefix . $prefix . "$name => $value";
+ }
+ }
+ }
+ return $output;
+ }
}
diff --git a/core/DataTable/Renderer/Csv.php b/core/DataTable/Renderer/Csv.php
index 4e97840b05..1e61e62aef 100644
--- a/core/DataTable/Renderer/Csv.php
+++ b/core/DataTable/Renderer/Csv.php
@@ -1,415 +1,371 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* CSV export
- *
+ *
* When rendered using the default settings, a CSV report has the following characteristics:
* The first record contains headers for all the columns in the report.
* All rows have the same number of columns.
* The default field delimiter string is a comma (,).
* Formatting and layout are ignored.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Renderer_Csv extends Piwik_DataTable_Renderer
{
- /**
- * Column separator
- *
- * @var string
- */
- public $separator = ",";
-
- /**
- * Line end
- *
- * @var string
- */
- public $lineEnd = "\n";
-
- /**
- * 'metadata' columns will be exported, prefixed by 'metadata_'
- *
- * @var bool
- */
- public $exportMetadata = true;
-
- /**
- * Converts the content to unicode so that UTF8 characters (eg. chinese) can be imported in Excel
- *
- * @var bool
- */
- public $convertToUnicode = true;
-
- /**
- * idSubtable will be exported in a column called 'idsubdatatable'
- *
- * @var bool
- */
- public $exportIdSubtable = true;
-
- /**
- * Computes the dataTable output and returns the string/binary
- *
- * @return string
- */
- public function render()
- {
- $str = $this->renderTable($this->table);
- if(empty($str))
- {
- return 'No data available';
- }
-
- $this->renderHeader();
-
- if($this->convertToUnicode
- && function_exists('mb_convert_encoding'))
- {
- $str = chr(255) . chr(254) . mb_convert_encoding($str, 'UTF-16LE', 'UTF-8');
- }
- return $str;
- }
-
- /**
- * Computes the exception output and returns the string/binary
- *
- * @return string
- */
- function renderException()
- {
- @header('Content-Type: text/html; charset=utf-8');
- $exceptionMessage = $this->getExceptionMessage();
- return 'Error: '.$exceptionMessage;
- }
-
- /**
- * Enables / Disables unicode converting
- *
- * @param $bool
- */
- public function setConvertToUnicode($bool)
- {
- $this->convertToUnicode = $bool;
- }
-
- /**
- * Sets the column separator
- *
- * @param $separator
- */
- public function setSeparator($separator)
- {
- $this->separator = $separator;
- }
-
- /**
- * Computes the output of the given data table
- *
- * @param Piwik_DataTable|array $table
- * @param array $allColumns
- * @return string
- */
- protected function renderTable($table, &$allColumns = array() )
- {
- if (is_array($table)) // convert array to DataTable
- {
- $table = Piwik_DataTable::makeFromSimpleArray($table);
- }
-
- if($table instanceof Piwik_DataTable_Array)
- {
- $str = $this->renderDataTableArray($table, $allColumns);
- }
- else
- {
- $str = $this->renderDataTable($table, $allColumns);
- }
- return $str;
- }
-
- /**
- * Computes the output of the given data table array
- *
- * @param Piwik_DataTable_Array $table
- * @param array $allColumns
- * @return string
- */
- protected function renderDataTableArray($table, &$allColumns = array())
- {
- $str = '';
- foreach($table->getArray() as $currentLinePrefix => $dataTable)
- {
- $returned = explode("\n",$this->renderTable($dataTable, $allColumns));
-
- // get rid of the columns names
- $returned = array_slice($returned,1);
-
- // case empty datatable we dont print anything in the CSV export
- // when in xml we would output <result date="2008-01-15" />
- if(!empty($returned))
- {
- foreach($returned as &$row)
- {
- $row = $currentLinePrefix . $this->separator . $row;
- }
- $str .= "\n" . implode("\n", $returned);
- }
- }
-
- // prepend table key to column list
- $allColumns = array_merge(array($table->getKeyName() => true), $allColumns);
-
- // add header to output string
- $str = $this->getHeaderLine(array_keys($allColumns)).$str;
-
- return $str;
- }
-
- /**
- * Converts the output of the given simple data table
- *
- * @param Piwik_DataTable_Simple $table
- * @param array $allColumns
- * @return string
- */
- protected function renderDataTable( $table, &$allColumns = array() )
- {
- if($table instanceof Piwik_DataTable_Simple)
- {
- $row = $table->getFirstRow();
- if($row !== false)
- {
- $columnNameToValue = $row->getColumns();
- if(count($columnNameToValue) == 1)
- {
- // simple tables should only have one column, the value
- $allColumns['value'] = true;
-
- $value = array_values($columnNameToValue);
- $str = 'value' . $this->lineEnd . $this->formatValue($value[0]);
- return $str;
- }
- }
- }
- $csv = array();
- foreach($table->getRows() as $row)
- {
- $csvRow = array();
-
- $columns = $row->getColumns();
- foreach($columns as $name => $value)
- {
- //goals => array( 'idgoal=1' =>array(..), 'idgoal=2' => array(..))
- if(is_array($value))
- {
- foreach($value as $key => $subValues)
- {
- if(is_array($subValues))
- {
- foreach($subValues as $subKey => $subValue)
- {
- if ($this->translateColumnNames)
- {
- $subName = $name != 'goals' ? $name . ' ' . $key
- : Piwik_Translate('Goals_GoalX', $key);
- $columnName = $this->translateColumnName($subKey)
- .' ('. $subName . ')';
- }
- else
- {
- // goals_idgoal=1
- $columnName = $name . "_" . $key . "_" . $subKey;
- }
- $allColumns[$columnName] = true;
- $csvRow[$columnName] = $subValue;
- }
- }
- }
- }
- else
- {
- $allColumns[$name] = true;
- $csvRow[$name] = $value;
- }
- }
-
- if($this->exportMetadata)
- {
- $metadata = $row->getMetadata();
- foreach($metadata as $name => $value)
- {
- if($name == 'idsubdatatable_in_db') {
- continue;
- }
- //if a metadata and a column have the same name make sure they dont overwrite
- if($this->translateColumnNames)
- {
- $name = Piwik_Translate('General_Metadata').': '.$name;
- }
- else
- {
- $name = 'metadata_'.$name;
- }
-
- $allColumns[$name] = true;
- $csvRow[$name] = $value;
- }
- }
-
- if($this->exportIdSubtable)
- {
- $idsubdatatable = $row->getIdSubDataTable();
- if($idsubdatatable !== false
- && $this->hideIdSubDatatable === false)
- {
- $csvRow['idsubdatatable'] = $idsubdatatable;
- }
- }
-
- $csv[] = $csvRow;
- }
-
- // now we make sure that all the rows in the CSV array have all the columns
- foreach($csv as &$row)
- {
- foreach($allColumns as $columnName => $true)
- {
- if(!isset($row[$columnName]))
- {
- $row[$columnName] = '';
- }
- }
- }
-
- $str = '';
-
- // specific case, we have only one column and this column wasn't named properly (indexed by a number)
- // we don't print anything in the CSV file => an empty line
- if(sizeof($allColumns) == 1
- && reset($allColumns)
- && !is_string(key($allColumns)))
- {
- $str .= '';
- }
- else
- {
- // render row names
- $str .= $this->getHeaderLine(array_keys($allColumns)).$this->lineEnd;
- }
-
- // we render the CSV
- foreach($csv as $theRow)
- {
- $rowStr = '';
- foreach($allColumns as $columnName => $true)
- {
- $rowStr .= $this->formatValue($theRow[$columnName]) . $this->separator;
- }
- // remove the last separator
- $rowStr = substr_replace($rowStr,"",-strlen($this->separator));
- $str .= $rowStr . $this->lineEnd;
- }
- $str = substr($str, 0, -strlen($this->lineEnd));
- return $str;
- }
-
- /**
- * Returns the CSV header line for a set of metrics. Will translate columns if desired.
- *
- * @param array $columnMetrics
- * @return array
- */
- private function getHeaderLine( $columnMetrics )
- {
- if ($this->translateColumnNames)
- {
- $columnMetrics = $this->translateColumnNames($columnMetrics);
- }
- return implode($this->separator, $columnMetrics);
- }
-
- /**
- * Formats/Escapes the given value
- *
- * @param mixed $value
- * @return string
- */
- protected function formatValue($value)
- {
- if(is_string($value)
- && !is_numeric($value))
- {
- $value = html_entity_decode($value, ENT_COMPAT, 'UTF-8');
- }
- elseif($value === false)
- {
- $value = 0;
- }
- if(is_string($value)
- && (strpos($value, '"') !== false
- || strpos($value, $this->separator) !== false )
- )
- {
- $value = '"'. str_replace('"', '""', $value). '"';
- }
-
- // in some number formats (e.g. German), the decimal separator is a comma
- // we need to catch and replace this
- if (is_numeric($value))
- {
- $value = (string) $value;
- $value = str_replace(',', '.', $value);
- }
-
- return $value;
- }
-
- /**
- * Sends the http headers for csv file
- */
- protected function renderHeader()
- {
- $fileName = 'Piwik '.Piwik_Translate('General_Export');
-
- $period = Piwik_Common::getRequestVar('period', false);
- $date = Piwik_Common::getRequestVar('date', false);
- if ($period || $date) // in test cases, there are no request params set
- {
- if ($period == 'range')
- {
- $period = new Piwik_Period_Range($period, $date);
- }
- else if (strpos($date, ',') !== false)
- {
- $period = new Piwik_Period_Range('range', $date);
- }
- else
- {
- $period = Piwik_Period::factory($period, Piwik_Date::factory($date));
- }
-
- $prettyDate = $period->getLocalizedLongString();
-
- $meta = $this->getApiMetaData();
-
- $fileName .= ' _ '.$meta['name']
- .' _ '.$prettyDate.'.csv';
- }
-
- // silent fail otherwise unit tests fail
- @header('Content-Type: application/vnd.ms-excel');
- @header('Content-Disposition: attachment; filename="'.$fileName.'"');
- Piwik::overrideCacheControlHeaders();
- }
+ /**
+ * Column separator
+ *
+ * @var string
+ */
+ public $separator = ",";
+
+ /**
+ * Line end
+ *
+ * @var string
+ */
+ public $lineEnd = "\n";
+
+ /**
+ * 'metadata' columns will be exported, prefixed by 'metadata_'
+ *
+ * @var bool
+ */
+ public $exportMetadata = true;
+
+ /**
+ * Converts the content to unicode so that UTF8 characters (eg. chinese) can be imported in Excel
+ *
+ * @var bool
+ */
+ public $convertToUnicode = true;
+
+ /**
+ * idSubtable will be exported in a column called 'idsubdatatable'
+ *
+ * @var bool
+ */
+ public $exportIdSubtable = true;
+
+ /**
+ * Computes the dataTable output and returns the string/binary
+ *
+ * @return string
+ */
+ public function render()
+ {
+ $str = $this->renderTable($this->table);
+ if (empty($str)) {
+ return 'No data available';
+ }
+
+ $this->renderHeader();
+
+ if ($this->convertToUnicode
+ && function_exists('mb_convert_encoding')
+ ) {
+ $str = chr(255) . chr(254) . mb_convert_encoding($str, 'UTF-16LE', 'UTF-8');
+ }
+ return $str;
+ }
+
+ /**
+ * Computes the exception output and returns the string/binary
+ *
+ * @return string
+ */
+ function renderException()
+ {
+ @header('Content-Type: text/html; charset=utf-8');
+ $exceptionMessage = $this->getExceptionMessage();
+ return 'Error: ' . $exceptionMessage;
+ }
+
+ /**
+ * Enables / Disables unicode converting
+ *
+ * @param $bool
+ */
+ public function setConvertToUnicode($bool)
+ {
+ $this->convertToUnicode = $bool;
+ }
+
+ /**
+ * Sets the column separator
+ *
+ * @param $separator
+ */
+ public function setSeparator($separator)
+ {
+ $this->separator = $separator;
+ }
+
+ /**
+ * Computes the output of the given data table
+ *
+ * @param Piwik_DataTable|array $table
+ * @param array $allColumns
+ * @return string
+ */
+ protected function renderTable($table, &$allColumns = array())
+ {
+ if (is_array($table)) // convert array to DataTable
+ {
+ $table = Piwik_DataTable::makeFromSimpleArray($table);
+ }
+
+ if ($table instanceof Piwik_DataTable_Array) {
+ $str = $this->renderDataTableArray($table, $allColumns);
+ } else {
+ $str = $this->renderDataTable($table, $allColumns);
+ }
+ return $str;
+ }
+
+ /**
+ * Computes the output of the given data table array
+ *
+ * @param Piwik_DataTable_Array $table
+ * @param array $allColumns
+ * @return string
+ */
+ protected function renderDataTableArray($table, &$allColumns = array())
+ {
+ $str = '';
+ foreach ($table->getArray() as $currentLinePrefix => $dataTable) {
+ $returned = explode("\n", $this->renderTable($dataTable, $allColumns));
+
+ // get rid of the columns names
+ $returned = array_slice($returned, 1);
+
+ // case empty datatable we dont print anything in the CSV export
+ // when in xml we would output <result date="2008-01-15" />
+ if (!empty($returned)) {
+ foreach ($returned as &$row) {
+ $row = $currentLinePrefix . $this->separator . $row;
+ }
+ $str .= "\n" . implode("\n", $returned);
+ }
+ }
+
+ // prepend table key to column list
+ $allColumns = array_merge(array($table->getKeyName() => true), $allColumns);
+
+ // add header to output string
+ $str = $this->getHeaderLine(array_keys($allColumns)) . $str;
+
+ return $str;
+ }
+
+ /**
+ * Converts the output of the given simple data table
+ *
+ * @param Piwik_DataTable_Simple $table
+ * @param array $allColumns
+ * @return string
+ */
+ protected function renderDataTable($table, &$allColumns = array())
+ {
+ if ($table instanceof Piwik_DataTable_Simple) {
+ $row = $table->getFirstRow();
+ if ($row !== false) {
+ $columnNameToValue = $row->getColumns();
+ if (count($columnNameToValue) == 1) {
+ // simple tables should only have one column, the value
+ $allColumns['value'] = true;
+
+ $value = array_values($columnNameToValue);
+ $str = 'value' . $this->lineEnd . $this->formatValue($value[0]);
+ return $str;
+ }
+ }
+ }
+ $csv = array();
+ foreach ($table->getRows() as $row) {
+ $csvRow = array();
+
+ $columns = $row->getColumns();
+ foreach ($columns as $name => $value) {
+ //goals => array( 'idgoal=1' =>array(..), 'idgoal=2' => array(..))
+ if (is_array($value)) {
+ foreach ($value as $key => $subValues) {
+ if (is_array($subValues)) {
+ foreach ($subValues as $subKey => $subValue) {
+ if ($this->translateColumnNames) {
+ $subName = $name != 'goals' ? $name . ' ' . $key
+ : Piwik_Translate('Goals_GoalX', $key);
+ $columnName = $this->translateColumnName($subKey)
+ . ' (' . $subName . ')';
+ } else {
+ // goals_idgoal=1
+ $columnName = $name . "_" . $key . "_" . $subKey;
+ }
+ $allColumns[$columnName] = true;
+ $csvRow[$columnName] = $subValue;
+ }
+ }
+ }
+ } else {
+ $allColumns[$name] = true;
+ $csvRow[$name] = $value;
+ }
+ }
+
+ if ($this->exportMetadata) {
+ $metadata = $row->getMetadata();
+ foreach ($metadata as $name => $value) {
+ if ($name == 'idsubdatatable_in_db') {
+ continue;
+ }
+ //if a metadata and a column have the same name make sure they dont overwrite
+ if ($this->translateColumnNames) {
+ $name = Piwik_Translate('General_Metadata') . ': ' . $name;
+ } else {
+ $name = 'metadata_' . $name;
+ }
+
+ $allColumns[$name] = true;
+ $csvRow[$name] = $value;
+ }
+ }
+
+ if ($this->exportIdSubtable) {
+ $idsubdatatable = $row->getIdSubDataTable();
+ if ($idsubdatatable !== false
+ && $this->hideIdSubDatatable === false
+ ) {
+ $csvRow['idsubdatatable'] = $idsubdatatable;
+ }
+ }
+
+ $csv[] = $csvRow;
+ }
+
+ // now we make sure that all the rows in the CSV array have all the columns
+ foreach ($csv as &$row) {
+ foreach ($allColumns as $columnName => $true) {
+ if (!isset($row[$columnName])) {
+ $row[$columnName] = '';
+ }
+ }
+ }
+
+ $str = '';
+
+ // specific case, we have only one column and this column wasn't named properly (indexed by a number)
+ // we don't print anything in the CSV file => an empty line
+ if (sizeof($allColumns) == 1
+ && reset($allColumns)
+ && !is_string(key($allColumns))
+ ) {
+ $str .= '';
+ } else {
+ // render row names
+ $str .= $this->getHeaderLine(array_keys($allColumns)) . $this->lineEnd;
+ }
+
+ // we render the CSV
+ foreach ($csv as $theRow) {
+ $rowStr = '';
+ foreach ($allColumns as $columnName => $true) {
+ $rowStr .= $this->formatValue($theRow[$columnName]) . $this->separator;
+ }
+ // remove the last separator
+ $rowStr = substr_replace($rowStr, "", -strlen($this->separator));
+ $str .= $rowStr . $this->lineEnd;
+ }
+ $str = substr($str, 0, -strlen($this->lineEnd));
+ return $str;
+ }
+
+ /**
+ * Returns the CSV header line for a set of metrics. Will translate columns if desired.
+ *
+ * @param array $columnMetrics
+ * @return array
+ */
+ private function getHeaderLine($columnMetrics)
+ {
+ if ($this->translateColumnNames) {
+ $columnMetrics = $this->translateColumnNames($columnMetrics);
+ }
+ return implode($this->separator, $columnMetrics);
+ }
+
+ /**
+ * Formats/Escapes the given value
+ *
+ * @param mixed $value
+ * @return string
+ */
+ protected function formatValue($value)
+ {
+ if (is_string($value)
+ && !is_numeric($value)
+ ) {
+ $value = html_entity_decode($value, ENT_COMPAT, 'UTF-8');
+ } elseif ($value === false) {
+ $value = 0;
+ }
+ if (is_string($value)
+ && (strpos($value, '"') !== false
+ || strpos($value, $this->separator) !== false)
+ ) {
+ $value = '"' . str_replace('"', '""', $value) . '"';
+ }
+
+ // in some number formats (e.g. German), the decimal separator is a comma
+ // we need to catch and replace this
+ if (is_numeric($value)) {
+ $value = (string)$value;
+ $value = str_replace(',', '.', $value);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Sends the http headers for csv file
+ */
+ protected function renderHeader()
+ {
+ $fileName = 'Piwik ' . Piwik_Translate('General_Export');
+
+ $period = Piwik_Common::getRequestVar('period', false);
+ $date = Piwik_Common::getRequestVar('date', false);
+ if ($period || $date) // in test cases, there are no request params set
+ {
+ if ($period == 'range') {
+ $period = new Piwik_Period_Range($period, $date);
+ } else if (strpos($date, ',') !== false) {
+ $period = new Piwik_Period_Range('range', $date);
+ } else {
+ $period = Piwik_Period::factory($period, Piwik_Date::factory($date));
+ }
+
+ $prettyDate = $period->getLocalizedLongString();
+
+ $meta = $this->getApiMetaData();
+
+ $fileName .= ' _ ' . $meta['name']
+ . ' _ ' . $prettyDate . '.csv';
+ }
+
+ // silent fail otherwise unit tests fail
+ @header('Content-Type: application/vnd.ms-excel');
+ @header('Content-Disposition: attachment; filename="' . $fileName . '"');
+ Piwik::overrideCacheControlHeaders();
+ }
}
diff --git a/core/DataTable/Renderer/Html.php b/core/DataTable/Renderer/Html.php
index 11fbdbc1e3..1d620f74cc 100644
--- a/core/DataTable/Renderer/Html.php
+++ b/core/DataTable/Renderer/Html.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,217 +12,195 @@
/**
* Simple HTML output
* Does not work with recursive DataTable (i.e., when a row can be associated with a subDataTable).
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Renderer_Html extends Piwik_DataTable_Renderer
{
- protected $tableId;
- protected $allColumns;
- protected $tableStructure;
- protected $i;
-
- /**
- * Sets the table id
- *
- * @param string $id
- */
- function setTableId($id)
- {
- $this->tableId = str_replace('.', '_', $id);
- }
-
- /**
- * Output HTTP Content-Type header
- */
- protected function renderHeader()
- {
- @header('Content-Type: text/html; charset=utf-8');
- }
-
- /**
- * Computes the dataTable output and returns the string/binary
- *
- * @return string
- */
- function render()
- {
- $this->renderHeader();
- $this->tableStructure = array();
- $this->allColumns = array();
- $this->i = 0;
-
- return $this->renderTable($this->table);
- }
-
- /**
- * Computes the exception output and returns the string/binary
- *
- * @return string
- */
- function renderException()
- {
- $this->renderHeader();
- $exceptionMessage = $this->getExceptionMessage();
- return nl2br($exceptionMessage);
- }
-
- /**
- * Computes the output for the given data table
- *
- * @param Piwik_DataTable $table
- * @return string
- */
- protected function renderTable($table)
- {
- if (is_array($table)) // convert array to DataTable
- {
- $table = Piwik_DataTable::makeFromSimpleArray($table);
- }
-
- if($table instanceof Piwik_DataTable_Array)
- {
- foreach($table->getArray() as $date => $subtable )
- {
- if ($subtable->getRowsCount()) {
- $this->buildTableStructure($subtable, '_'. $table->getKeyName(), $date);
- }
- }
- }
- else // Piwik_DataTable_Simple
- {
- if($table->getRowsCount())
- {
- $this->buildTableStructure($table);
- }
- }
-
- $out = $this->renderDataTable();
- return $out;
- }
-
- /**
- * Adds the given data table to the table structure array
- *
- * @param Piwik_DataTable_Simple $table
- * @param null|string $columnToAdd
- * @param null|string $valueToAdd
- * @throws Exception
- */
- protected function buildTableStructure($table, $columnToAdd = null, $valueToAdd = null)
- {
- $i = $this->i;
- $someMetadata = false;
- $someIdSubTable = false;
-
- /*
- * table = array
- * ROW1 = col1 | col2 | col3 | metadata | idSubTable
- * ROW2 = col1 | col2 (no value but appears) | col3 | metadata | idSubTable
- */
- if(!($table instanceof Piwik_DataTable))
- {
- throw new Exception("HTML Renderer does not work with this combination of parameters");
- }
- foreach($table->getRows() as $row)
- {
- if(isset($columnToAdd) && isset($valueToAdd))
- {
- $this->allColumns[$columnToAdd] = true;
- $this->tableStructure[$i][$columnToAdd] = $valueToAdd;
- }
-
- foreach($row->getColumns() as $column => $value)
- {
- $this->allColumns[$column] = true;
- $this->tableStructure[$i][$column] = $value;
- }
-
- $metadata = array();
- foreach($row->getMetadata() as $name => $value)
- {
- if(is_string($value)) $value = "'$value'";
- $metadata[] = "'$name' => $value";
- }
-
- if(count($metadata) != 0)
- {
- $someMetadata = true;
- $metadata = implode("<br />", $metadata);
- $this->tableStructure[$i]['_metadata'] = $metadata;
- }
-
- $idSubtable = $row->getIdSubDataTable();
- if(!is_null($idSubtable))
- {
- $someIdSubTable = true;
- $this->tableStructure[$i]['_idSubtable'] = $idSubtable;
- }
-
- $i++;
- }
- $this->i = $i;
-
- $this->allColumns['_metadata'] = $someMetadata;
- $this->allColumns['_idSubtable'] = $someIdSubTable;
- }
-
- /**
- * Computes the output for the table structure array
- *
- * @return string
- */
- protected function renderDataTable()
- {
- $html = "<table ". ($this->tableId ? "id=\"{$this->tableId}\" " : "") ."border=\"1\">\n<thead>\n\t<tr>\n";
-
- foreach($this->allColumns as $name => $toDisplay)
- {
- if($toDisplay !== false)
- {
- if($name === 0)
- {
- $name = 'value';
- }
- if($this->translateColumnNames)
- {
- $name = $this->translateColumnName($name);
- }
- $html .= "\t\t<th>$name</th>\n";
- }
- }
-
- $html .= "\t</tr>\n</thead>\n<tbody>\n";
-
- foreach($this->tableStructure as $row)
- {
- $html .= "\t<tr>\n";
- foreach($this->allColumns as $name => $toDisplay)
- {
- if($toDisplay !== false)
- {
- $value = "-";
- if(isset($row[$name]))
- {
- if(is_array($row[$name]))
- {
- $value = "<pre>".self::formatValueXml(var_export($row[$name], true)) . "</pre>";
- }
- else
- {
- $value = self::formatValueXml($row[$name]);
- }
- }
-
- $html .= "\t\t<td>$value</td>\n";
- }
- }
- $html .= "\t</tr>\n";
- }
-
- $html .= "</tbody>\n</table>\n";
-
- return $html;
- }
+ protected $tableId;
+ protected $allColumns;
+ protected $tableStructure;
+ protected $i;
+
+ /**
+ * Sets the table id
+ *
+ * @param string $id
+ */
+ function setTableId($id)
+ {
+ $this->tableId = str_replace('.', '_', $id);
+ }
+
+ /**
+ * Output HTTP Content-Type header
+ */
+ protected function renderHeader()
+ {
+ @header('Content-Type: text/html; charset=utf-8');
+ }
+
+ /**
+ * Computes the dataTable output and returns the string/binary
+ *
+ * @return string
+ */
+ function render()
+ {
+ $this->renderHeader();
+ $this->tableStructure = array();
+ $this->allColumns = array();
+ $this->i = 0;
+
+ return $this->renderTable($this->table);
+ }
+
+ /**
+ * Computes the exception output and returns the string/binary
+ *
+ * @return string
+ */
+ function renderException()
+ {
+ $this->renderHeader();
+ $exceptionMessage = $this->getExceptionMessage();
+ return nl2br($exceptionMessage);
+ }
+
+ /**
+ * Computes the output for the given data table
+ *
+ * @param Piwik_DataTable $table
+ * @return string
+ */
+ protected function renderTable($table)
+ {
+ if (is_array($table)) // convert array to DataTable
+ {
+ $table = Piwik_DataTable::makeFromSimpleArray($table);
+ }
+
+ if ($table instanceof Piwik_DataTable_Array) {
+ foreach ($table->getArray() as $date => $subtable) {
+ if ($subtable->getRowsCount()) {
+ $this->buildTableStructure($subtable, '_' . $table->getKeyName(), $date);
+ }
+ }
+ } else // Piwik_DataTable_Simple
+ {
+ if ($table->getRowsCount()) {
+ $this->buildTableStructure($table);
+ }
+ }
+
+ $out = $this->renderDataTable();
+ return $out;
+ }
+
+ /**
+ * Adds the given data table to the table structure array
+ *
+ * @param Piwik_DataTable_Simple $table
+ * @param null|string $columnToAdd
+ * @param null|string $valueToAdd
+ * @throws Exception
+ */
+ protected function buildTableStructure($table, $columnToAdd = null, $valueToAdd = null)
+ {
+ $i = $this->i;
+ $someMetadata = false;
+ $someIdSubTable = false;
+
+ /*
+ * table = array
+ * ROW1 = col1 | col2 | col3 | metadata | idSubTable
+ * ROW2 = col1 | col2 (no value but appears) | col3 | metadata | idSubTable
+ */
+ if (!($table instanceof Piwik_DataTable)) {
+ throw new Exception("HTML Renderer does not work with this combination of parameters");
+ }
+ foreach ($table->getRows() as $row) {
+ if (isset($columnToAdd) && isset($valueToAdd)) {
+ $this->allColumns[$columnToAdd] = true;
+ $this->tableStructure[$i][$columnToAdd] = $valueToAdd;
+ }
+
+ foreach ($row->getColumns() as $column => $value) {
+ $this->allColumns[$column] = true;
+ $this->tableStructure[$i][$column] = $value;
+ }
+
+ $metadata = array();
+ foreach ($row->getMetadata() as $name => $value) {
+ if (is_string($value)) $value = "'$value'";
+ $metadata[] = "'$name' => $value";
+ }
+
+ if (count($metadata) != 0) {
+ $someMetadata = true;
+ $metadata = implode("<br />", $metadata);
+ $this->tableStructure[$i]['_metadata'] = $metadata;
+ }
+
+ $idSubtable = $row->getIdSubDataTable();
+ if (!is_null($idSubtable)) {
+ $someIdSubTable = true;
+ $this->tableStructure[$i]['_idSubtable'] = $idSubtable;
+ }
+
+ $i++;
+ }
+ $this->i = $i;
+
+ $this->allColumns['_metadata'] = $someMetadata;
+ $this->allColumns['_idSubtable'] = $someIdSubTable;
+ }
+
+ /**
+ * Computes the output for the table structure array
+ *
+ * @return string
+ */
+ protected function renderDataTable()
+ {
+ $html = "<table " . ($this->tableId ? "id=\"{$this->tableId}\" " : "") . "border=\"1\">\n<thead>\n\t<tr>\n";
+
+ foreach ($this->allColumns as $name => $toDisplay) {
+ if ($toDisplay !== false) {
+ if ($name === 0) {
+ $name = 'value';
+ }
+ if ($this->translateColumnNames) {
+ $name = $this->translateColumnName($name);
+ }
+ $html .= "\t\t<th>$name</th>\n";
+ }
+ }
+
+ $html .= "\t</tr>\n</thead>\n<tbody>\n";
+
+ foreach ($this->tableStructure as $row) {
+ $html .= "\t<tr>\n";
+ foreach ($this->allColumns as $name => $toDisplay) {
+ if ($toDisplay !== false) {
+ $value = "-";
+ if (isset($row[$name])) {
+ if (is_array($row[$name])) {
+ $value = "<pre>" . self::formatValueXml(var_export($row[$name], true)) . "</pre>";
+ } else {
+ $value = self::formatValueXml($row[$name]);
+ }
+ }
+
+ $html .= "\t\t<td>$value</td>\n";
+ }
+ }
+ $html .= "\t</tr>\n";
+ }
+
+ $html .= "</tbody>\n</table>\n";
+
+ return $html;
+ }
}
diff --git a/core/DataTable/Renderer/Json.php b/core/DataTable/Renderer/Json.php
index 87fb8035f6..286299acd6 100644
--- a/core/DataTable/Renderer/Json.php
+++ b/core/DataTable/Renderer/Json.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,109 +12,102 @@
/**
* JSON export.
* Works with recursive DataTable (when a row can be associated with a subDataTable).
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Renderer_Json extends Piwik_DataTable_Renderer
{
- /**
- * Computes the dataTable output and returns the string/binary
- *
- * @return string
- */
- public function render()
- {
- $this->renderHeader();
- return $this->renderTable($this->table);
- }
+ /**
+ * Computes the dataTable output and returns the string/binary
+ *
+ * @return string
+ */
+ public function render()
+ {
+ $this->renderHeader();
+ return $this->renderTable($this->table);
+ }
- /**
- * Computes the exception output and returns the string/binary
- *
- * @return string
- */
- function renderException()
- {
- $this->renderHeader();
-
- $exceptionMessage = $this->getExceptionMessage();
- $exceptionMessage = str_replace(array("\r\n","\n"), "", $exceptionMessage);
- $exceptionMessage = '{"result":"error", "message":"'.$exceptionMessage.'"}';
-
- return $this->jsonpWrap($exceptionMessage);
- }
+ /**
+ * Computes the exception output and returns the string/binary
+ *
+ * @return string
+ */
+ function renderException()
+ {
+ $this->renderHeader();
- /**
- * Computes the output for the given data table
- *
- * @param Piwik_DataTable $table
- * @return string
- */
- protected function renderTable($table)
- {
- if (is_array($table))
- {
- $array = $table;
- if (self::shouldWrapArrayBeforeRendering($array, $wrapSingleValues = true))
- {
- $array = array($array);
- }
- }
- else
- {
- $renderer = new Piwik_DataTable_Renderer_Php();
- $renderer->setTable($table);
- $renderer->setRenderSubTables($this->isRenderSubtables());
- $renderer->setSerialize(false);
- $renderer->setHideIdSubDatableFromResponse($this->hideIdSubDatatable);
- $array = $renderer->flatRender();
- }
-
- if(!is_array($array))
- {
- $array = array('value' => $array);
- }
+ $exceptionMessage = $this->getExceptionMessage();
+ $exceptionMessage = str_replace(array("\r\n", "\n"), "", $exceptionMessage);
+ $exceptionMessage = '{"result":"error", "message":"' . $exceptionMessage . '"}';
- // decode all entities
- $callback = create_function('&$value,$key', 'if(is_string($value)){$value = html_entity_decode($value, ENT_QUOTES, "UTF-8");}');
- array_walk_recursive($array, $callback);
-
- $str = Piwik_Common::json_encode($array);
-
- return $this->jsonpWrap($str);
- }
+ return $this->jsonpWrap($exceptionMessage);
+ }
- /**
- * @param $str
- * @return string
- */
- protected function jsonpWrap($str)
- {
- if(($jsonCallback = Piwik_Common::getRequestVar('callback', false)) === false)
- $jsonCallback = Piwik_Common::getRequestVar('jsoncallback', false);
- if($jsonCallback !== false)
- {
- if(preg_match('/^[0-9a-zA-Z_]*$/D', $jsonCallback) > 0)
- {
- $str = $jsonCallback . "(" . $str . ")";
- }
- }
-
- return $str;
- }
+ /**
+ * Computes the output for the given data table
+ *
+ * @param Piwik_DataTable $table
+ * @return string
+ */
+ protected function renderTable($table)
+ {
+ if (is_array($table)) {
+ $array = $table;
+ if (self::shouldWrapArrayBeforeRendering($array, $wrapSingleValues = true)) {
+ $array = array($array);
+ }
+ } else {
+ $renderer = new Piwik_DataTable_Renderer_Php();
+ $renderer->setTable($table);
+ $renderer->setRenderSubTables($this->isRenderSubtables());
+ $renderer->setSerialize(false);
+ $renderer->setHideIdSubDatableFromResponse($this->hideIdSubDatatable);
+ $array = $renderer->flatRender();
+ }
- /**
- * Sends the http header for json file
- */
- protected function renderHeader()
- {
- self::sendHeaderJSON();
- Piwik::overrideCacheControlHeaders();
- }
+ if (!is_array($array)) {
+ $array = array('value' => $array);
+ }
- public static function sendHeaderJSON()
- {
- @header('Content-Type: application/json; charset=utf-8');
- }
+ // decode all entities
+ $callback = create_function('&$value,$key', 'if(is_string($value)){$value = html_entity_decode($value, ENT_QUOTES, "UTF-8");}');
+ array_walk_recursive($array, $callback);
+
+ $str = Piwik_Common::json_encode($array);
+
+ return $this->jsonpWrap($str);
+ }
+
+ /**
+ * @param $str
+ * @return string
+ */
+ protected function jsonpWrap($str)
+ {
+ if (($jsonCallback = Piwik_Common::getRequestVar('callback', false)) === false)
+ $jsonCallback = Piwik_Common::getRequestVar('jsoncallback', false);
+ if ($jsonCallback !== false) {
+ if (preg_match('/^[0-9a-zA-Z_]*$/D', $jsonCallback) > 0) {
+ $str = $jsonCallback . "(" . $str . ")";
+ }
+ }
+
+ return $str;
+ }
+
+ /**
+ * Sends the http header for json file
+ */
+ protected function renderHeader()
+ {
+ self::sendHeaderJSON();
+ Piwik::overrideCacheControlHeaders();
+ }
+
+ public static function sendHeaderJSON()
+ {
+ @header('Content-Type: application/json; charset=utf-8');
+ }
}
diff --git a/core/DataTable/Renderer/Php.php b/core/DataTable/Renderer/Php.php
index f0570ae3f6..9c671c1201 100644
--- a/core/DataTable/Renderer/Php.php
+++ b/core/DataTable/Renderer/Php.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -14,283 +14,255 @@
* You can specify in the constructor if you want the serialized version.
* Please note that by default it will produce a flat version of the array.
* See the method flatRender() for details. @see flatRender();
- *
+ *
* Works with recursive DataTable (when a row can be associated with a subDataTable).
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Renderer_Php extends Piwik_DataTable_Renderer
{
- protected $prettyDisplay = false;
- protected $serialize = true;
+ protected $prettyDisplay = false;
+ protected $serialize = true;
- /**
- * Enables/Disables serialize
- *
- * @param bool $bool
- */
- public function setSerialize( $bool )
- {
- $this->serialize = (bool)$bool;
- }
+ /**
+ * Enables/Disables serialize
+ *
+ * @param bool $bool
+ */
+ public function setSerialize($bool)
+ {
+ $this->serialize = (bool)$bool;
+ }
- /**
- * Enables/Disables pretty display
- *
- * @param bool $bool
- */
- public function setPrettyDisplay($bool)
- {
- $this->prettyDisplay = (bool)$bool;
- }
+ /**
+ * Enables/Disables pretty display
+ *
+ * @param bool $bool
+ */
+ public function setPrettyDisplay($bool)
+ {
+ $this->prettyDisplay = (bool)$bool;
+ }
- /**
- * Converts current data table to string
- *
- * @return string
- */
- public function __toString()
- {
- $data = $this->render();
- if(!is_string($data))
- {
- $data = serialize($data);
- }
- return $data;
- }
+ /**
+ * Converts current data table to string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $data = $this->render();
+ if (!is_string($data)) {
+ $data = serialize($data);
+ }
+ return $data;
+ }
- /**
- * Computes the dataTable output and returns the string/binary
- *
- * @param null|Piwik_DataTable_Array|Piwik_DataTable_Simple $dataTable
- * @return string
- */
- public function render( $dataTable = null )
- {
- $this->renderHeader();
+ /**
+ * Computes the dataTable output and returns the string/binary
+ *
+ * @param null|Piwik_DataTable_Array|Piwik_DataTable_Simple $dataTable
+ * @return string
+ */
+ public function render($dataTable = null)
+ {
+ $this->renderHeader();
- if(is_null($dataTable))
- {
- $dataTable = $this->table;
- }
- $toReturn = $this->flatRender( $dataTable );
-
- if( $this->prettyDisplay )
- {
- if(!is_array($toReturn))
- {
- $toReturn = unserialize($toReturn);
- }
- $toReturn = "<pre>" . var_export($toReturn, true ) . "</pre>";
- }
- return $toReturn;
- }
+ if (is_null($dataTable)) {
+ $dataTable = $this->table;
+ }
+ $toReturn = $this->flatRender($dataTable);
- /**
- * Computes the exception output and returns the string/binary
- *
- * @return string
- */
- function renderException()
- {
- $this->renderHeader();
+ if ($this->prettyDisplay) {
+ if (!is_array($toReturn)) {
+ $toReturn = unserialize($toReturn);
+ }
+ $toReturn = "<pre>" . var_export($toReturn, true) . "</pre>";
+ }
+ return $toReturn;
+ }
- $exceptionMessage = $this->getExceptionMessage();
-
- $return = array('result' => 'error', 'message' => $exceptionMessage);
-
- if($this->serialize)
- {
- $return = serialize($return);
- }
-
- return $return;
- }
+ /**
+ * Computes the exception output and returns the string/binary
+ *
+ * @return string
+ */
+ function renderException()
+ {
+ $this->renderHeader();
- /**
- * Produces a flat php array from the DataTable, putting "columns" and "metadata" on the same level.
- *
- * For example, when a originalRender() would be
- * array( 'columns' => array( 'col1_name' => value1, 'col2_name' => value2 ),
- * 'metadata' => array( 'metadata1_name' => value_metadata) )
- *
- * a flatRender() is
- * array( 'col1_name' => value1,
- * 'col2_name' => value2,
- * 'metadata1_name' => value_metadata )
- *
- * @param null|Piwik_DataTable_Array|Piwik_DataTable_Simple $dataTable
- * @return array Php array representing the 'flat' version of the datatable
- */
- public function flatRender( $dataTable = null )
- {
- if(is_null($dataTable))
- {
- $dataTable = $this->table;
- }
-
- if (is_array($dataTable))
- {
- $flatArray = $dataTable;
- if (self::shouldWrapArrayBeforeRendering($flatArray))
- {
- $flatArray = array($flatArray);
- }
- }
- else if ($dataTable instanceof Piwik_DataTable_Array)
- {
- $flatArray = array();
- foreach($dataTable->getArray() as $keyName => $table)
- {
- $serializeSave = $this->serialize;
- $this->serialize = false;
- $flatArray[$keyName] = $this->flatRender($table);
- $this->serialize = $serializeSave;
- }
- }
- else if($dataTable instanceof Piwik_DataTable_Simple)
- {
- $flatArray = $this->renderSimpleTable($dataTable);
-
- // if we return only one numeric value then we print out the result in a simple <result> tag
- // keep it simple!
- if(count($flatArray) == 1)
- {
- $flatArray = current($flatArray);
- }
-
- }
- // A normal DataTable needs to be handled specifically
- else
- {
- $array = $this->renderTable($dataTable);
- $flatArray = $this->flattenArray($array);
- }
-
- if($this->serialize)
- {
- $flatArray = serialize($flatArray);
- }
-
- return $flatArray;
- }
+ $exceptionMessage = $this->getExceptionMessage();
- /**
- *
- * @param array $array
- * @return array
- */
- protected function flattenArray($array)
- {
- $flatArray = array();
- foreach($array as $row)
- {
- $newRow = $row['columns'] + $row['metadata'];
- if(isset($row['idsubdatatable'])
- && $this->hideIdSubDatatable === false)
- {
- $newRow += array('idsubdatatable' => $row['idsubdatatable']);
- }
- if(isset($row['subtable']))
- {
- $newRow += array('subtable' => $this->flattenArray($row['subtable']) );
- }
- $flatArray[] = $newRow;
- }
- return $flatArray;
- }
+ $return = array('result' => 'error', 'message' => $exceptionMessage);
- /**
- * Converts the current data table to an array
- *
- * @return array
- * @throws Exception
- */
- public function originalRender()
- {
- Piwik::checkObjectTypeIs($this->table, array('Piwik_DataTable_Simple', 'Piwik_DataTable'));
-
- if($this->table instanceof Piwik_DataTable_Simple)
- {
- $array = $this->renderSimpleTable($this->table);
- }
- elseif($this->table instanceof Piwik_DataTable)
- {
- $array = $this->renderTable($this->table);
- }
-
- if($this->serialize)
- {
- $array = serialize($array);
- }
- return $array;
- }
+ if ($this->serialize) {
+ $return = serialize($return);
+ }
- /**
- * Converts the given data table to an array
- *
- * @param Piwik_DataTable $table
- * @return array
- */
- protected function renderTable($table)
- {
- $array = array();
+ return $return;
+ }
- foreach($table->getRows() as $id => $row)
- {
- $newRow = array(
- 'columns' => $row->getColumns(),
- 'metadata' => $row->getMetadata(),
- 'idsubdatatable' => $row->getIdSubDataTable(),
- );
-
- if ($id == Piwik_DataTable::ID_SUMMARY_ROW)
- {
- $newRow['issummaryrow'] = true;
- }
-
- if($this->isRenderSubtables()
- && $row->isSubtableLoaded() )
- {
- $subTable = $this->renderTable( Piwik_DataTable_Manager::getInstance()->getTable($row->getIdSubDataTable()));
- $newRow['subtable'] = $subTable;
- if($this->hideIdSubDatatable === false
- && isset($newRow['metadata']['idsubdatatable_in_db']))
- {
- $newRow['columns']['idsubdatatable'] = $newRow['metadata']['idsubdatatable_in_db'];
- }
- unset($newRow['metadata']['idsubdatatable_in_db']);
- }
- if($this->hideIdSubDatatable !== false)
- {
- unset($newRow['idsubdatatable']);
- }
-
- $array[] = $newRow;
- }
- return $array;
- }
+ /**
+ * Produces a flat php array from the DataTable, putting "columns" and "metadata" on the same level.
+ *
+ * For example, when a originalRender() would be
+ * array( 'columns' => array( 'col1_name' => value1, 'col2_name' => value2 ),
+ * 'metadata' => array( 'metadata1_name' => value_metadata) )
+ *
+ * a flatRender() is
+ * array( 'col1_name' => value1,
+ * 'col2_name' => value2,
+ * 'metadata1_name' => value_metadata )
+ *
+ * @param null|Piwik_DataTable_Array|Piwik_DataTable_Simple $dataTable
+ * @return array Php array representing the 'flat' version of the datatable
+ */
+ public function flatRender($dataTable = null)
+ {
+ if (is_null($dataTable)) {
+ $dataTable = $this->table;
+ }
- /**
- * Converts the simple data table to an array
- *
- * @param Piwik_DataTable_Simple $table
- * @return array
- */
- protected function renderSimpleTable($table)
- {
- $array = array();
+ if (is_array($dataTable)) {
+ $flatArray = $dataTable;
+ if (self::shouldWrapArrayBeforeRendering($flatArray)) {
+ $flatArray = array($flatArray);
+ }
+ } else if ($dataTable instanceof Piwik_DataTable_Array) {
+ $flatArray = array();
+ foreach ($dataTable->getArray() as $keyName => $table) {
+ $serializeSave = $this->serialize;
+ $this->serialize = false;
+ $flatArray[$keyName] = $this->flatRender($table);
+ $this->serialize = $serializeSave;
+ }
+ } else if ($dataTable instanceof Piwik_DataTable_Simple) {
+ $flatArray = $this->renderSimpleTable($dataTable);
- $row = $table->getFirstRow();
- if($row === false)
- {
- return $array;
- }
- foreach($row->getColumns() as $columnName => $columnValue)
- {
- $array[$columnName] = $columnValue;
- }
- return $array;
- }
+ // if we return only one numeric value then we print out the result in a simple <result> tag
+ // keep it simple!
+ if (count($flatArray) == 1) {
+ $flatArray = current($flatArray);
+ }
+
+ } // A normal DataTable needs to be handled specifically
+ else {
+ $array = $this->renderTable($dataTable);
+ $flatArray = $this->flattenArray($array);
+ }
+
+ if ($this->serialize) {
+ $flatArray = serialize($flatArray);
+ }
+
+ return $flatArray;
+ }
+
+ /**
+ *
+ * @param array $array
+ * @return array
+ */
+ protected function flattenArray($array)
+ {
+ $flatArray = array();
+ foreach ($array as $row) {
+ $newRow = $row['columns'] + $row['metadata'];
+ if (isset($row['idsubdatatable'])
+ && $this->hideIdSubDatatable === false
+ ) {
+ $newRow += array('idsubdatatable' => $row['idsubdatatable']);
+ }
+ if (isset($row['subtable'])) {
+ $newRow += array('subtable' => $this->flattenArray($row['subtable']));
+ }
+ $flatArray[] = $newRow;
+ }
+ return $flatArray;
+ }
+
+ /**
+ * Converts the current data table to an array
+ *
+ * @return array
+ * @throws Exception
+ */
+ public function originalRender()
+ {
+ Piwik::checkObjectTypeIs($this->table, array('Piwik_DataTable_Simple', 'Piwik_DataTable'));
+
+ if ($this->table instanceof Piwik_DataTable_Simple) {
+ $array = $this->renderSimpleTable($this->table);
+ } elseif ($this->table instanceof Piwik_DataTable) {
+ $array = $this->renderTable($this->table);
+ }
+
+ if ($this->serialize) {
+ $array = serialize($array);
+ }
+ return $array;
+ }
+
+ /**
+ * Converts the given data table to an array
+ *
+ * @param Piwik_DataTable $table
+ * @return array
+ */
+ protected function renderTable($table)
+ {
+ $array = array();
+
+ foreach ($table->getRows() as $id => $row) {
+ $newRow = array(
+ 'columns' => $row->getColumns(),
+ 'metadata' => $row->getMetadata(),
+ 'idsubdatatable' => $row->getIdSubDataTable(),
+ );
+
+ if ($id == Piwik_DataTable::ID_SUMMARY_ROW) {
+ $newRow['issummaryrow'] = true;
+ }
+
+ if ($this->isRenderSubtables()
+ && $row->isSubtableLoaded()
+ ) {
+ $subTable = $this->renderTable(Piwik_DataTable_Manager::getInstance()->getTable($row->getIdSubDataTable()));
+ $newRow['subtable'] = $subTable;
+ if ($this->hideIdSubDatatable === false
+ && isset($newRow['metadata']['idsubdatatable_in_db'])
+ ) {
+ $newRow['columns']['idsubdatatable'] = $newRow['metadata']['idsubdatatable_in_db'];
+ }
+ unset($newRow['metadata']['idsubdatatable_in_db']);
+ }
+ if ($this->hideIdSubDatatable !== false) {
+ unset($newRow['idsubdatatable']);
+ }
+
+ $array[] = $newRow;
+ }
+ return $array;
+ }
+
+ /**
+ * Converts the simple data table to an array
+ *
+ * @param Piwik_DataTable_Simple $table
+ * @return array
+ */
+ protected function renderSimpleTable($table)
+ {
+ $array = array();
+
+ $row = $table->getFirstRow();
+ if ($row === false) {
+ return $array;
+ }
+ foreach ($row->getColumns() as $columnName => $columnValue) {
+ $array[$columnName] = $columnValue;
+ }
+ return $array;
+ }
}
diff --git a/core/DataTable/Renderer/Rss.php b/core/DataTable/Renderer/Rss.php
index 3bda7f3d8d..60f11ca13e 100644
--- a/core/DataTable/Renderer/Rss.php
+++ b/core/DataTable/Renderer/Rss.php
@@ -1,127 +1,126 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * RSS Feed.
+ * RSS Feed.
* The RSS renderer can be used only on Piwik_DataTable_Array that are arrays of Piwik_DataTable.
* A RSS feed contains one dataTable per element in the Piwik_DataTable_Array.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Renderer_Rss extends Piwik_DataTable_Renderer
{
- /**
- * Computes the dataTable output and returns the string/binary
- *
- * @return string
- */
- function render()
- {
- $this->renderHeader();
- return $this->renderTable($this->table);
- }
-
- /**
- * Computes the exception output and returns the string/binary
- *
- * @return string
- */
- function renderException()
- {
- header('Content-type: text/plain');
- $exceptionMessage = $this->getExceptionMessage();
- return 'Error: '.$exceptionMessage;
- }
-
- /**
- * Computes the output for the given data table
- *
- * @param Piwik_DataTable $table
- * @return string
- * @throws Exception
- */
- protected function renderTable($table)
- {
- if(!($table instanceof Piwik_DataTable_Array)
- || $table->getKeyName() != 'date')
- {
- throw new Exception("RSS feeds can be generated for one specific website &idSite=X.".
- "\nPlease specify only one idSite or consider using &format=XML instead.");
- }
-
- $idSite = Piwik_Common::getRequestVar('idSite', 1, 'int');
- $period = Piwik_Common::getRequestVar('period');
-
- $piwikUrl = Piwik_Url::getCurrentUrlWithoutFileName()
- . "?module=CoreHome&action=index&idSite=" . $idSite . "&period=" . $period;
- $out = "";
- $moreRecentFirst = array_reverse($table->getArray(), true);
- foreach($moreRecentFirst as $date => $subtable )
- {
- $timestamp = $subtable->getMetadata('timestamp');
- $site = $subtable->getMetadata('site');
-
- $pudDate = date('r', $timestamp);
-
- $dateInSiteTimezone = Piwik_Date::factory($timestamp)->setTimezone($site->getTimezone())->toString('Y-m-d');
- $thisPiwikUrl = Piwik_Common::sanitizeInputValue($piwikUrl . "&date=$dateInSiteTimezone");
- $siteName = $site->getName();
- $title = $siteName . " on ". $date;
-
- $out .= "\t<item>
+ /**
+ * Computes the dataTable output and returns the string/binary
+ *
+ * @return string
+ */
+ function render()
+ {
+ $this->renderHeader();
+ return $this->renderTable($this->table);
+ }
+
+ /**
+ * Computes the exception output and returns the string/binary
+ *
+ * @return string
+ */
+ function renderException()
+ {
+ header('Content-type: text/plain');
+ $exceptionMessage = $this->getExceptionMessage();
+ return 'Error: ' . $exceptionMessage;
+ }
+
+ /**
+ * Computes the output for the given data table
+ *
+ * @param Piwik_DataTable $table
+ * @return string
+ * @throws Exception
+ */
+ protected function renderTable($table)
+ {
+ if (!($table instanceof Piwik_DataTable_Array)
+ || $table->getKeyName() != 'date'
+ ) {
+ throw new Exception("RSS feeds can be generated for one specific website &idSite=X." .
+ "\nPlease specify only one idSite or consider using &format=XML instead.");
+ }
+
+ $idSite = Piwik_Common::getRequestVar('idSite', 1, 'int');
+ $period = Piwik_Common::getRequestVar('period');
+
+ $piwikUrl = Piwik_Url::getCurrentUrlWithoutFileName()
+ . "?module=CoreHome&action=index&idSite=" . $idSite . "&period=" . $period;
+ $out = "";
+ $moreRecentFirst = array_reverse($table->getArray(), true);
+ foreach ($moreRecentFirst as $date => $subtable) {
+ $timestamp = $subtable->getMetadata('timestamp');
+ $site = $subtable->getMetadata('site');
+
+ $pudDate = date('r', $timestamp);
+
+ $dateInSiteTimezone = Piwik_Date::factory($timestamp)->setTimezone($site->getTimezone())->toString('Y-m-d');
+ $thisPiwikUrl = Piwik_Common::sanitizeInputValue($piwikUrl . "&date=$dateInSiteTimezone");
+ $siteName = $site->getName();
+ $title = $siteName . " on " . $date;
+
+ $out .= "\t<item>
<pubDate>$pudDate</pubDate>
<guid>$thisPiwikUrl</guid>
<link>$thisPiwikUrl</link>
<title>$title</title>
<author>http://piwik.org</author>
- <description>";
-
- $out .= Piwik_Common::sanitizeInputValue( $this->renderDataTable($subtable) );
- $out .= "</description>\n\t</item>\n";
- }
-
- $header = $this->getRssHeader();
- $footer = $this->getRssFooter();
-
- return $header . $out . $footer;
- }
-
- /**
- * Sends the xml file http header
- */
- protected function renderHeader()
- {
- @header('Content-Type: text/xml; charset=utf-8');
- }
-
- /**
- * Returns the RSS file footer
- *
- * @return string
- */
- protected function getRssFooter()
- {
- return "\t</channel>\n</rss>";
- }
-
- /**
- * Returns the RSS file header
- *
- * @return string
- */
- protected function getRssHeader()
- {
- $generationDate = date('r');
- $header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+ <description>";
+
+ $out .= Piwik_Common::sanitizeInputValue($this->renderDataTable($subtable));
+ $out .= "</description>\n\t</item>\n";
+ }
+
+ $header = $this->getRssHeader();
+ $footer = $this->getRssFooter();
+
+ return $header . $out . $footer;
+ }
+
+ /**
+ * Sends the xml file http header
+ */
+ protected function renderHeader()
+ {
+ @header('Content-Type: text/xml; charset=utf-8');
+ }
+
+ /**
+ * Returns the RSS file footer
+ *
+ * @return string
+ */
+ protected function getRssFooter()
+ {
+ return "\t</channel>\n</rss>";
+ }
+
+ /**
+ * Returns the RSS file header
+ *
+ * @return string
+ */
+ protected function getRssHeader()
+ {
+ $generationDate = date('r');
+ $header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<rss version=\"2.0\">
<channel>
<title>piwik statistics - RSS</title>
@@ -131,78 +130,67 @@ class Piwik_DataTable_Renderer_Rss extends Piwik_DataTable_Renderer
<generator>piwik</generator>
<language>en</language>
<lastBuildDate>$generationDate</lastBuildDate>";
- return $header;
- }
-
- protected function renderDataTable($table)
- {
- if($table->getRowsCount() == 0)
- {
- return "<b><i>Empty table</i></b><br />\n";
- }
-
- $i = 1;
- $tableStructure = array();
-
- /*
- * table = array
- * ROW1 = col1 | col2 | col3 | metadata | idSubTable
- * ROW2 = col1 | col2 (no value but appears) | col3 | metadata | idSubTable
- * subtable here
- */
- $allColumns = array();
- foreach($table->getRows() as $row)
- {
- foreach($row->getColumns() as $column => $value)
- {
- // for example, goals data is array: not supported in export RSS
- // in the future we shall reuse ViewDataTable for html exports in RSS anyway
- if(is_array($value))
- {
- continue;
- }
- $allColumns[$column] = true;
- $tableStructure[$i][$column] = $value;
- }
- $i++;
- }
- $html = "\n";
- $html .= "<table border=1 width=70%>";
- $html .= "\n<tr>";
- foreach($allColumns as $name => $toDisplay)
- {
- if($toDisplay !== false)
- {
- if($this->translateColumnNames)
- {
- $name = $this->translateColumnName($name);
- }
- $html .= "\n\t<td><b>$name</b></td>";
- }
- }
- $html .= "\n</tr>";
- $colspan = count($allColumns);
-
- foreach($tableStructure as $row)
- {
- $html .= "\n\n<tr>";
- foreach($allColumns as $columnName => $toDisplay)
- {
- if($toDisplay !== false)
- {
- $value = "-";
- if(isset($row[$columnName]))
- {
- $value = urldecode($row[$columnName]);
- }
-
- $html .= "\n\t<td>$value</td>";
- }
- }
- $html .= "</tr>";
-
- }
- $html .= "\n\n</table>";
- return $html;
- }
+ return $header;
+ }
+
+ protected function renderDataTable($table)
+ {
+ if ($table->getRowsCount() == 0) {
+ return "<b><i>Empty table</i></b><br />\n";
+ }
+
+ $i = 1;
+ $tableStructure = array();
+
+ /*
+ * table = array
+ * ROW1 = col1 | col2 | col3 | metadata | idSubTable
+ * ROW2 = col1 | col2 (no value but appears) | col3 | metadata | idSubTable
+ * subtable here
+ */
+ $allColumns = array();
+ foreach ($table->getRows() as $row) {
+ foreach ($row->getColumns() as $column => $value) {
+ // for example, goals data is array: not supported in export RSS
+ // in the future we shall reuse ViewDataTable for html exports in RSS anyway
+ if (is_array($value)) {
+ continue;
+ }
+ $allColumns[$column] = true;
+ $tableStructure[$i][$column] = $value;
+ }
+ $i++;
+ }
+ $html = "\n";
+ $html .= "<table border=1 width=70%>";
+ $html .= "\n<tr>";
+ foreach ($allColumns as $name => $toDisplay) {
+ if ($toDisplay !== false) {
+ if ($this->translateColumnNames) {
+ $name = $this->translateColumnName($name);
+ }
+ $html .= "\n\t<td><b>$name</b></td>";
+ }
+ }
+ $html .= "\n</tr>";
+ $colspan = count($allColumns);
+
+ foreach ($tableStructure as $row) {
+ $html .= "\n\n<tr>";
+ foreach ($allColumns as $columnName => $toDisplay) {
+ if ($toDisplay !== false) {
+ $value = "-";
+ if (isset($row[$columnName])) {
+ $value = urldecode($row[$columnName]);
+ }
+
+ $html .= "\n\t<td>$value</td>";
+ }
+ }
+ $html .= "</tr>";
+
+ }
+ $html .= "\n\n</table>";
+ return $html;
+ }
}
diff --git a/core/DataTable/Renderer/Tsv.php b/core/DataTable/Renderer/Tsv.php
index db0132913b..4835853143 100644
--- a/core/DataTable/Renderer/Tsv.php
+++ b/core/DataTable/Renderer/Tsv.php
@@ -1,41 +1,41 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* TSV export
- *
+ *
* Excel doesn't import CSV properly, it expects TAB separated values by default.
* TSV is therefore the 'CSV' that is Excel compatible
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Renderer_Tsv extends Piwik_DataTable_Renderer_Csv
{
- /**
- * Constructor
- */
- function __construct()
- {
- parent::__construct();
- $this->setSeparator("\t");
- }
+ /**
+ * Constructor
+ */
+ function __construct()
+ {
+ parent::__construct();
+ $this->setSeparator("\t");
+ }
- /**
- * Computes the dataTable output and returns the string/binary
- *
- * @return string
- */
- function render()
- {
- return parent::render();
- }
+ /**
+ * Computes the dataTable output and returns the string/binary
+ *
+ * @return string
+ */
+ function render()
+ {
+ return parent::render();
+ }
}
diff --git a/core/DataTable/Renderer/Xml.php b/core/DataTable/Renderer/Xml.php
index 4e60e043f8..e04392bb02 100644
--- a/core/DataTable/Renderer/Xml.php
+++ b/core/DataTable/Renderer/Xml.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -13,497 +13,414 @@
* XML export of a given DataTable.
* See the tests cases for more information about the XML format (/tests/core/DataTable/Renderer.test.php)
* Or have a look at the API calls examples.
- *
+ *
* Works with recursive DataTable (when a row can be associated with a subDataTable).
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Renderer_Xml extends Piwik_DataTable_Renderer
{
- /**
- * Computes the dataTable output and returns the string/binary
- *
- * @return string
- */
- function render()
- {
- $this->renderHeader();
- return '<?xml version="1.0" encoding="utf-8" ?>' . "\n" . $this->renderTable($this->table);
- }
+ /**
+ * Computes the dataTable output and returns the string/binary
+ *
+ * @return string
+ */
+ function render()
+ {
+ $this->renderHeader();
+ return '<?xml version="1.0" encoding="utf-8" ?>' . "\n" . $this->renderTable($this->table);
+ }
- /**
- * Computes the exception output and returns the string/binary
- *
- * @return string
- */
- function renderException()
- {
- $this->renderHeader();
+ /**
+ * Computes the exception output and returns the string/binary
+ *
+ * @return string
+ */
+ function renderException()
+ {
+ $this->renderHeader();
- $exceptionMessage = $this->getExceptionMessage();
-
- $return = '<?xml version="1.0" encoding="utf-8" ?>' . "\n" .
- "<result>\n".
- "\t<error message=\"".$exceptionMessage."\" />\n".
- "</result>";
-
- return $return;
- }
+ $exceptionMessage = $this->getExceptionMessage();
- /**
- * Converts the given data table to an array
- *
- * @param Piwik_DataTable $table data table to convert
- * @return array
- */
- protected function getArrayFromDataTable($table)
- {
- if (is_array($table))
- {
- return $table;
- }
-
- $renderer = new Piwik_DataTable_Renderer_Php();
- $renderer->setRenderSubTables($this->isRenderSubtables());
- $renderer->setSerialize(false);
- $renderer->setTable($table);
- $renderer->setHideIdSubDatableFromResponse($this->hideIdSubDatatable);
- return $renderer->flatRender();
- }
+ $return = '<?xml version="1.0" encoding="utf-8" ?>' . "\n" .
+ "<result>\n" .
+ "\t<error message=\"" . $exceptionMessage . "\" />\n" .
+ "</result>";
- /**
- * Computes the output for the given data table
- *
- * @param Piwik_DataTable $table
- * @param bool $returnOnlyDataTableXml
- * @param string $prefixLines
- * @return array|string
- * @throws Exception
- */
- protected function renderTable($table, $returnOnlyDataTableXml = false, $prefixLines = '')
- {
- $array = $this->getArrayFromDataTable($table);
- if($table instanceof Piwik_DataTable_Array)
- {
- $out = $this->renderDataTableArray($table, $array, $prefixLines);
-
- if($returnOnlyDataTableXml)
- {
- return $out;
- }
- $out = "<results>\n$out</results>";
- return $out;
- }
-
- // integer value of ZERO is a value we want to display
- if($array != 0 && empty($array))
- {
- if($returnOnlyDataTableXml)
- {
- throw new Exception("Illegal state, what xml shall we return?");
- }
- $out = "<result />";
- return $out;
- }
- if($table instanceof Piwik_DataTable_Simple)
- {
- if(is_array($array))
- {
- $out = $this->renderDataTableSimple($array);
- }
- else
- {
- $out = $array;
- }
- if($returnOnlyDataTableXml)
- {
- return $out;
- }
-
- if(is_array($array))
- {
- $out = "<result>\n".$out."</result>";
- }
- else
- {
- $value = self::formatValueXml($out);
- if($value === '')
- {
- $out = "<result />";
- }
- else
- {
- $out = "<result>".$value."</result>";
- }
- }
- return $out;
- }
-
- if ($table instanceof Piwik_DataTable)
- {
- $out = $this->renderDataTable($array);
- if($returnOnlyDataTableXml)
- {
- return $out;
- }
- $out = "<result>\n$out</result>";
- return $out;
- }
-
- if (is_array($array))
- {
- $out = $this->renderArray($array, $prefixLines."\t");
- if ($returnOnlyDataTableXml)
- {
- return $out;
- }
- return "<result>\n$out</result>";
- }
- }
-
- /**
- * Renders an array as XML.
- *
- * @param array $array The array to render.
- * @param string $prefixLines The string to prefix each line in the output.
- * @return string
- */
- private function renderArray( $array, $prefixLines )
- {
- $isAssociativeArray = Piwik::isAssociativeArray($array);
-
- // check if array contains arrays, and if not wrap the result in an extra <row> element
- // (only check if this is the root renderArray call)
- // NOTE: this is for backwards compatibility. before, array's were added to a new DataTable.
- // if the array had arrays, they were added as multiple rows, otherwise it was treated as
- // one row. removing will change API output.
- $wrapInRow = $prefixLines === "\t"
- && self::shouldWrapArrayBeforeRendering($array, $wrapSingleValues = false, $isAssociativeArray);
-
- // render the array
- $result = "";
- if ($wrapInRow)
- {
- $result .= "$prefixLines<row>\n";
- $prefixLines .= "\t";
- }
- foreach ($array as $key => $value)
- {
- // based on the type of array & the key, determine how this node will look
- if ($isAssociativeArray)
- {
- if (is_numeric($key))
- {
- $prefix = "<row key=\"$key\">";
- $suffix = "</row>";
- $emptyNode = "<row key=\"$key\"/>";
- }
- else
- {
- $prefix = "<$key>";
- $suffix = "</$key>";
- $emptyNode = "<$key />";
- }
- }
- else
- {
- $prefix = "<row>";
- $suffix = "</row>";
- $emptyNode = "<row/>";
- }
-
- // render the array item
- if (is_array($value))
- {
- $result .= $prefixLines.$prefix."\n";
- $result .= $this->renderArray($value, $prefixLines."\t");
- $result .= $prefixLines.$suffix."\n";
- }
- else if ($value instanceof Piwik_DataTable
- || $value instanceof Piwik_DataTable_Array)
- {
- if ($value->getRowsCount() == 0)
- {
- $result .= $prefixLines.$emptyNode."\n";
- }
- else
- {
- $result .= $prefixLines.$prefix."\n";
- if ($value instanceof Piwik_DataTable_Array)
- {
- $result .= $this->renderDataTableArray($value, $this->getArrayFromDataTable($value), $prefixLines);
- }
- else if ($value instanceof Piwik_DataTable_Simple)
- {
- $result .= $this->renderDataTableSimple($this->getArrayFromDataTable($value), $prefixLines);
- }
- else
- {
- $result .= $this->renderDataTable($this->getArrayFromDataTable($value), $prefixLines);
- }
- $result .= $prefixLines.$suffix."\n";
- }
- }
- else
- {
- $xmlValue = self::formatValueXml($value);
- if (strlen($xmlValue) != 0)
- {
- $result .= $prefixLines.$prefix.$xmlValue.$suffix."\n";
- }
- else
- {
- $result .= $prefixLines.$emptyNode."\n";
- }
- }
- }
- if ($wrapInRow)
- {
- $result .= substr($prefixLines, 0, strlen($prefixLines) - 1)."</row>\n";
- }
- return $result;
- }
+ return $return;
+ }
- /**
- * Computes the output for the given data table array
- *
- * @param Piwik_DataTable_Array $table
- * @param array $array
- * @param string $prefixLines
- * @return string
- */
- protected function renderDataTableArray($table, $array, $prefixLines = "")
- {
- // CASE 1
- //array
- // 'day1' => string '14' (length=2)
- // 'day2' => string '6' (length=1)
- $firstTable = current($array);
- if(!is_array( $firstTable ))
- {
- $xml = '';
- $nameDescriptionAttribute = $table->getKeyName();
- foreach($array as $valueAttribute => $value)
- {
- if(empty($value))
- {
- $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$valueAttribute\" />\n";
- }
- elseif($value instanceof Piwik_DataTable_Array )
- {
- $out = $this->renderTable($value, true);
- //TODO somehow this code is not tested, cover this case
- $xml .= "\t<result $nameDescriptionAttribute=\"$valueAttribute\">\n$out</result>\n";
- }
- else
- {
- $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$valueAttribute\">".self::formatValueXml($value)."</result>\n";
- }
- }
- return $xml;
- }
-
- $subTables = $table->getArray();
- $firstTable = current($subTables);
-
- // CASE 2
- //array
- // 'day1' =>
- // array
- // 'nb_uniq_visitors' => string '18'
- // 'nb_visits' => string '101'
- // 'day2' =>
- // array
- // 'nb_uniq_visitors' => string '28'
- // 'nb_visits' => string '11'
- if( $firstTable instanceof Piwik_DataTable_Simple)
- {
- $xml = '';
- $nameDescriptionAttribute = $table->getKeyName();
- foreach($array as $valueAttribute => $dataTableSimple)
- {
- if(count($dataTableSimple) == 0)
- {
- $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$valueAttribute\" />\n";
- }
- else
- {
- if(is_array($dataTableSimple))
- {
- $dataTableSimple = "\n" . $this->renderDataTableSimple($dataTableSimple, $prefixLines . "\t") . $prefixLines . "\t";
- }
- $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$valueAttribute\">".$dataTableSimple. "</result>\n";
- }
- }
- return $xml;
- }
-
- // CASE 3
- //array
- // 'day1' =>
- // array
- // 0 =>
- // array
- // 'label' => string 'phpmyvisites'
- // 'nb_uniq_visitors' => int 11
- // 'nb_visits' => int 13
- // 1 =>
- // array
- // 'label' => string 'phpmyvisits'
- // 'nb_uniq_visitors' => int 2
- // 'nb_visits' => int 2
- // 'day2' =>
- // array
- // 0 =>
- // array
- // 'label' => string 'piwik'
- // 'nb_uniq_visitors' => int 121
- // 'nb_visits' => int 130
- // 1 =>
- // array
- // 'label' => string 'piwik bis'
- // 'nb_uniq_visitors' => int 20
- // 'nb_visits' => int 120
- if($firstTable instanceof Piwik_DataTable)
- {
- $xml = '';
- $nameDescriptionAttribute = $table->getKeyName();
- foreach($array as $keyName => $arrayForSingleDate)
- {
- $dataTableOut = $this->renderDataTable( $arrayForSingleDate, $prefixLines . "\t" );
- if(empty($dataTableOut))
- {
- $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$keyName\" />\n";
- }
- else
- {
- $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$keyName\">\n";
- $xml .= $dataTableOut;
- $xml .= $prefixLines . "\t</result>\n";
- }
- }
- return $xml;
- }
-
- if($firstTable instanceof Piwik_DataTable_Array)
- {
- $xml = '';
- $tables = $table->getArray();
- $nameDescriptionAttribute = $table->getKeyName();
- foreach( $tables as $valueAttribute => $tableInArray)
- {
- $out = $this->renderTable($tableInArray, true, $prefixLines . "\t");
- $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$valueAttribute\">\n".$out.$prefixLines."\t</result>\n";
-
- }
- return $xml;
- }
- }
+ /**
+ * Converts the given data table to an array
+ *
+ * @param Piwik_DataTable $table data table to convert
+ * @return array
+ */
+ protected function getArrayFromDataTable($table)
+ {
+ if (is_array($table)) {
+ return $table;
+ }
- /**
- * Computes the output for the given data array
- *
- * @param array $array
- * @param string $prefixLine
- * @return string
- */
- protected function renderDataTable( $array, $prefixLine = "" )
- {
- $out = '';
- foreach($array as $rowId => $row)
- {
- if(!is_array($row))
- {
- $value = self::formatValueXml($row);
- if(strlen($value) == 0)
- {
- $out .= $prefixLine."\t\t<$rowId />\n";
- }
- else
- {
- $out .= $prefixLine."\t\t<$rowId>".$value."</$rowId>\n";
- }
- continue;
- }
+ $renderer = new Piwik_DataTable_Renderer_Php();
+ $renderer->setRenderSubTables($this->isRenderSubtables());
+ $renderer->setSerialize(false);
+ $renderer->setTable($table);
+ $renderer->setHideIdSubDatableFromResponse($this->hideIdSubDatatable);
+ return $renderer->flatRender();
+ }
- // Handing case idgoal=7, creating a new array for that one
- $rowAttribute = '';
- if(($equalFound = strstr($rowId, '=')) !== false)
- {
- $rowAttribute = explode('=', $rowId);
- $rowAttribute = " " . $rowAttribute[0] . "='" . $rowAttribute[1] . "'";
- }
- $out .= $prefixLine."\t<row$rowAttribute>";
-
- if(count($row) === 1
- && key($row) === 0)
- {
- $value = self::formatValueXml(current($row));
- $out .= $prefixLine . $value;
- }
- else
- {
- $out .= "\n";
- foreach($row as $name => $value)
- {
- // handle the recursive dataTable case by XML outputting the recursive table
- if(is_array($value))
- {
- $value = "\n".$this->renderDataTable($value, $prefixLine."\t\t");
- $value .= $prefixLine."\t\t";
- }
- else
- {
- $value = self::formatValueXml($value);
- }
- if(strlen($value) == 0)
- {
- $out .= $prefixLine."\t\t<$name />\n";
- }
- else
- {
- $out .= $prefixLine."\t\t<$name>".$value."</$name>\n";
- }
- }
- $out .= "\t";
- }
- $out .= $prefixLine."</row>\n";
- }
- return $out;
- }
+ /**
+ * Computes the output for the given data table
+ *
+ * @param Piwik_DataTable $table
+ * @param bool $returnOnlyDataTableXml
+ * @param string $prefixLines
+ * @return array|string
+ * @throws Exception
+ */
+ protected function renderTable($table, $returnOnlyDataTableXml = false, $prefixLines = '')
+ {
+ $array = $this->getArrayFromDataTable($table);
+ if ($table instanceof Piwik_DataTable_Array) {
+ $out = $this->renderDataTableArray($table, $array, $prefixLines);
- /**
- * Computes the output for the given data array (representing a simple data table)
- *
- * @param $array
- * @param string $prefixLine
- * @return string
- */
- protected function renderDataTableSimple( $array, $prefixLine = "")
- {
- $out = '';
- foreach($array as $keyName => $value)
- {
- $xmlValue = self::formatValueXml($value);
- if(strlen($xmlValue) == 0)
- {
- $out .= $prefixLine."\t<$keyName />\n";
- }
- else
- {
- $out .= $prefixLine."\t<$keyName>".$xmlValue."</$keyName>\n";
- }
- }
- return $out;
- }
+ if ($returnOnlyDataTableXml) {
+ return $out;
+ }
+ $out = "<results>\n$out</results>";
+ return $out;
+ }
- /**
- * Sends the XML headers
- */
- protected function renderHeader()
- {
- // silent fail because otherwise it throws an exception in the unit tests
- @header('Content-Type: text/xml; charset=utf-8');
- }
+ // integer value of ZERO is a value we want to display
+ if ($array != 0 && empty($array)) {
+ if ($returnOnlyDataTableXml) {
+ throw new Exception("Illegal state, what xml shall we return?");
+ }
+ $out = "<result />";
+ return $out;
+ }
+ if ($table instanceof Piwik_DataTable_Simple) {
+ if (is_array($array)) {
+ $out = $this->renderDataTableSimple($array);
+ } else {
+ $out = $array;
+ }
+ if ($returnOnlyDataTableXml) {
+ return $out;
+ }
+
+ if (is_array($array)) {
+ $out = "<result>\n" . $out . "</result>";
+ } else {
+ $value = self::formatValueXml($out);
+ if ($value === '') {
+ $out = "<result />";
+ } else {
+ $out = "<result>" . $value . "</result>";
+ }
+ }
+ return $out;
+ }
+
+ if ($table instanceof Piwik_DataTable) {
+ $out = $this->renderDataTable($array);
+ if ($returnOnlyDataTableXml) {
+ return $out;
+ }
+ $out = "<result>\n$out</result>";
+ return $out;
+ }
+
+ if (is_array($array)) {
+ $out = $this->renderArray($array, $prefixLines . "\t");
+ if ($returnOnlyDataTableXml) {
+ return $out;
+ }
+ return "<result>\n$out</result>";
+ }
+ }
+
+ /**
+ * Renders an array as XML.
+ *
+ * @param array $array The array to render.
+ * @param string $prefixLines The string to prefix each line in the output.
+ * @return string
+ */
+ private function renderArray($array, $prefixLines)
+ {
+ $isAssociativeArray = Piwik::isAssociativeArray($array);
+
+ // check if array contains arrays, and if not wrap the result in an extra <row> element
+ // (only check if this is the root renderArray call)
+ // NOTE: this is for backwards compatibility. before, array's were added to a new DataTable.
+ // if the array had arrays, they were added as multiple rows, otherwise it was treated as
+ // one row. removing will change API output.
+ $wrapInRow = $prefixLines === "\t"
+ && self::shouldWrapArrayBeforeRendering($array, $wrapSingleValues = false, $isAssociativeArray);
+
+ // render the array
+ $result = "";
+ if ($wrapInRow) {
+ $result .= "$prefixLines<row>\n";
+ $prefixLines .= "\t";
+ }
+ foreach ($array as $key => $value) {
+ // based on the type of array & the key, determine how this node will look
+ if ($isAssociativeArray) {
+ if (is_numeric($key)) {
+ $prefix = "<row key=\"$key\">";
+ $suffix = "</row>";
+ $emptyNode = "<row key=\"$key\"/>";
+ } else {
+ $prefix = "<$key>";
+ $suffix = "</$key>";
+ $emptyNode = "<$key />";
+ }
+ } else {
+ $prefix = "<row>";
+ $suffix = "</row>";
+ $emptyNode = "<row/>";
+ }
+
+ // render the array item
+ if (is_array($value)) {
+ $result .= $prefixLines . $prefix . "\n";
+ $result .= $this->renderArray($value, $prefixLines . "\t");
+ $result .= $prefixLines . $suffix . "\n";
+ } else if ($value instanceof Piwik_DataTable
+ || $value instanceof Piwik_DataTable_Array
+ ) {
+ if ($value->getRowsCount() == 0) {
+ $result .= $prefixLines . $emptyNode . "\n";
+ } else {
+ $result .= $prefixLines . $prefix . "\n";
+ if ($value instanceof Piwik_DataTable_Array) {
+ $result .= $this->renderDataTableArray($value, $this->getArrayFromDataTable($value), $prefixLines);
+ } else if ($value instanceof Piwik_DataTable_Simple) {
+ $result .= $this->renderDataTableSimple($this->getArrayFromDataTable($value), $prefixLines);
+ } else {
+ $result .= $this->renderDataTable($this->getArrayFromDataTable($value), $prefixLines);
+ }
+ $result .= $prefixLines . $suffix . "\n";
+ }
+ } else {
+ $xmlValue = self::formatValueXml($value);
+ if (strlen($xmlValue) != 0) {
+ $result .= $prefixLines . $prefix . $xmlValue . $suffix . "\n";
+ } else {
+ $result .= $prefixLines . $emptyNode . "\n";
+ }
+ }
+ }
+ if ($wrapInRow) {
+ $result .= substr($prefixLines, 0, strlen($prefixLines) - 1) . "</row>\n";
+ }
+ return $result;
+ }
+
+ /**
+ * Computes the output for the given data table array
+ *
+ * @param Piwik_DataTable_Array $table
+ * @param array $array
+ * @param string $prefixLines
+ * @return string
+ */
+ protected function renderDataTableArray($table, $array, $prefixLines = "")
+ {
+ // CASE 1
+ //array
+ // 'day1' => string '14' (length=2)
+ // 'day2' => string '6' (length=1)
+ $firstTable = current($array);
+ if (!is_array($firstTable)) {
+ $xml = '';
+ $nameDescriptionAttribute = $table->getKeyName();
+ foreach ($array as $valueAttribute => $value) {
+ if (empty($value)) {
+ $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$valueAttribute\" />\n";
+ } elseif ($value instanceof Piwik_DataTable_Array) {
+ $out = $this->renderTable($value, true);
+ //TODO somehow this code is not tested, cover this case
+ $xml .= "\t<result $nameDescriptionAttribute=\"$valueAttribute\">\n$out</result>\n";
+ } else {
+ $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$valueAttribute\">" . self::formatValueXml($value) . "</result>\n";
+ }
+ }
+ return $xml;
+ }
+
+ $subTables = $table->getArray();
+ $firstTable = current($subTables);
+
+ // CASE 2
+ //array
+ // 'day1' =>
+ // array
+ // 'nb_uniq_visitors' => string '18'
+ // 'nb_visits' => string '101'
+ // 'day2' =>
+ // array
+ // 'nb_uniq_visitors' => string '28'
+ // 'nb_visits' => string '11'
+ if ($firstTable instanceof Piwik_DataTable_Simple) {
+ $xml = '';
+ $nameDescriptionAttribute = $table->getKeyName();
+ foreach ($array as $valueAttribute => $dataTableSimple) {
+ if (count($dataTableSimple) == 0) {
+ $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$valueAttribute\" />\n";
+ } else {
+ if (is_array($dataTableSimple)) {
+ $dataTableSimple = "\n" . $this->renderDataTableSimple($dataTableSimple, $prefixLines . "\t") . $prefixLines . "\t";
+ }
+ $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$valueAttribute\">" . $dataTableSimple . "</result>\n";
+ }
+ }
+ return $xml;
+ }
+
+ // CASE 3
+ //array
+ // 'day1' =>
+ // array
+ // 0 =>
+ // array
+ // 'label' => string 'phpmyvisites'
+ // 'nb_uniq_visitors' => int 11
+ // 'nb_visits' => int 13
+ // 1 =>
+ // array
+ // 'label' => string 'phpmyvisits'
+ // 'nb_uniq_visitors' => int 2
+ // 'nb_visits' => int 2
+ // 'day2' =>
+ // array
+ // 0 =>
+ // array
+ // 'label' => string 'piwik'
+ // 'nb_uniq_visitors' => int 121
+ // 'nb_visits' => int 130
+ // 1 =>
+ // array
+ // 'label' => string 'piwik bis'
+ // 'nb_uniq_visitors' => int 20
+ // 'nb_visits' => int 120
+ if ($firstTable instanceof Piwik_DataTable) {
+ $xml = '';
+ $nameDescriptionAttribute = $table->getKeyName();
+ foreach ($array as $keyName => $arrayForSingleDate) {
+ $dataTableOut = $this->renderDataTable($arrayForSingleDate, $prefixLines . "\t");
+ if (empty($dataTableOut)) {
+ $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$keyName\" />\n";
+ } else {
+ $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$keyName\">\n";
+ $xml .= $dataTableOut;
+ $xml .= $prefixLines . "\t</result>\n";
+ }
+ }
+ return $xml;
+ }
+
+ if ($firstTable instanceof Piwik_DataTable_Array) {
+ $xml = '';
+ $tables = $table->getArray();
+ $nameDescriptionAttribute = $table->getKeyName();
+ foreach ($tables as $valueAttribute => $tableInArray) {
+ $out = $this->renderTable($tableInArray, true, $prefixLines . "\t");
+ $xml .= $prefixLines . "\t<result $nameDescriptionAttribute=\"$valueAttribute\">\n" . $out . $prefixLines . "\t</result>\n";
+
+ }
+ return $xml;
+ }
+ }
+
+ /**
+ * Computes the output for the given data array
+ *
+ * @param array $array
+ * @param string $prefixLine
+ * @return string
+ */
+ protected function renderDataTable($array, $prefixLine = "")
+ {
+ $out = '';
+ foreach ($array as $rowId => $row) {
+ if (!is_array($row)) {
+ $value = self::formatValueXml($row);
+ if (strlen($value) == 0) {
+ $out .= $prefixLine . "\t\t<$rowId />\n";
+ } else {
+ $out .= $prefixLine . "\t\t<$rowId>" . $value . "</$rowId>\n";
+ }
+ continue;
+ }
+
+ // Handing case idgoal=7, creating a new array for that one
+ $rowAttribute = '';
+ if (($equalFound = strstr($rowId, '=')) !== false) {
+ $rowAttribute = explode('=', $rowId);
+ $rowAttribute = " " . $rowAttribute[0] . "='" . $rowAttribute[1] . "'";
+ }
+ $out .= $prefixLine . "\t<row$rowAttribute>";
+
+ if (count($row) === 1
+ && key($row) === 0
+ ) {
+ $value = self::formatValueXml(current($row));
+ $out .= $prefixLine . $value;
+ } else {
+ $out .= "\n";
+ foreach ($row as $name => $value) {
+ // handle the recursive dataTable case by XML outputting the recursive table
+ if (is_array($value)) {
+ $value = "\n" . $this->renderDataTable($value, $prefixLine . "\t\t");
+ $value .= $prefixLine . "\t\t";
+ } else {
+ $value = self::formatValueXml($value);
+ }
+ if (strlen($value) == 0) {
+ $out .= $prefixLine . "\t\t<$name />\n";
+ } else {
+ $out .= $prefixLine . "\t\t<$name>" . $value . "</$name>\n";
+ }
+ }
+ $out .= "\t";
+ }
+ $out .= $prefixLine . "</row>\n";
+ }
+ return $out;
+ }
+
+ /**
+ * Computes the output for the given data array (representing a simple data table)
+ *
+ * @param $array
+ * @param string $prefixLine
+ * @return string
+ */
+ protected function renderDataTableSimple($array, $prefixLine = "")
+ {
+ $out = '';
+ foreach ($array as $keyName => $value) {
+ $xmlValue = self::formatValueXml($value);
+ if (strlen($xmlValue) == 0) {
+ $out .= $prefixLine . "\t<$keyName />\n";
+ } else {
+ $out .= $prefixLine . "\t<$keyName>" . $xmlValue . "</$keyName>\n";
+ }
+ }
+ return $out;
+ }
+
+ /**
+ * Sends the XML headers
+ */
+ protected function renderHeader()
+ {
+ // silent fail because otherwise it throws an exception in the unit tests
+ @header('Content-Type: text/xml; charset=utf-8');
+ }
}
diff --git a/core/DataTable/Row.php b/core/DataTable/Row.php
index a0682541c9..8fb60b0f29 100644
--- a/core/DataTable/Row.php
+++ b/core/DataTable/Row.php
@@ -1,662 +1,619 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* A DataTable is composed of rows.
- *
+ *
* A row is composed of:
- * - columns often at least a 'label' column containing the description
- * of the row, and some numeric values ('nb_visits', etc.)
+ * - columns often at least a 'label' column containing the description
+ * of the row, and some numeric values ('nb_visits', etc.)
* - metadata: other information never to be shown as 'columns'
* - idSubtable: a row can be linked to a SubTable
- *
+ *
* IMPORTANT: Make sure that the column named 'label' contains at least one non-numeric character.
* Otherwise the method addDataTable() or sumRow() would fail because they would consider
* the 'label' as being a numeric column to sum.
- *
- * PERFORMANCE: Do *not* add new fields except if necessary in this object. New fields will be
+ *
+ * PERFORMANCE: Do *not* add new fields except if necessary in this object. New fields will be
* serialized and recorded in the DB millions of times. This object size is critical and must be under control.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Row
{
- /**
- * List of columns that cannot be summed. An associative array for speed.
- *
- * @var array
- */
- private static $unsummableColumns = array(
- 'label' => true,
- 'full_url' => true // column used w/ old Piwik versions
- );
-
- /**
- * This array contains the row information:
- * - array indexed by self::COLUMNS contains the columns, pairs of (column names, value)
- * - (optional) array indexed by self::METADATA contains the metadata, pairs of (metadata name, value)
- * - (optional) integer indexed by self::DATATABLE_ASSOCIATED contains the ID of the Piwik_DataTable associated to this row.
- * This ID can be used to read the DataTable from the DataTable_Manager.
- *
- * @var array
- * @see constructor for more information
- */
- public $c = array();
- private $subtableIdWasNegativeBeforeSerialize = false;
-
- // @see sumRow - implementation detail
- public $maxVisitsSummed = 0;
-
- const COLUMNS = 0;
- const METADATA = 1;
- const DATATABLE_ASSOCIATED = 3;
-
-
- /**
- * Efficient load of the Row structure from a well structured php array
- *
- * @param array $row The row array has the structure
- * array(
- * Piwik_DataTable_Row::COLUMNS => array(
- * 'label' => 'Piwik',
- * 'column1' => 42,
- * 'visits' => 657,
- * 'time_spent' => 155744,
- * ),
- * Piwik_DataTable_Row::METADATA => array(
- * 'logo' => 'test.png'
- * ),
- * Piwik_DataTable_Row::DATATABLE_ASSOCIATED => #Piwik_DataTable object
- * (but in the row only the ID will be stored)
- * )
- */
- public function __construct( $row = array() )
- {
- $this->c[self::COLUMNS] = array();
- $this->c[self::METADATA] = array();
- $this->c[self::DATATABLE_ASSOCIATED] = null;
-
- if(isset($row[self::COLUMNS]))
- {
- $this->c[self::COLUMNS] = $row[self::COLUMNS];
- }
- if(isset($row[self::METADATA]))
- {
- $this->c[self::METADATA] = $row[self::METADATA];
- }
- if(isset($row[self::DATATABLE_ASSOCIATED])
- && $row[self::DATATABLE_ASSOCIATED] instanceof Piwik_DataTable)
- {
- $this->setSubtable($row[self::DATATABLE_ASSOCIATED]);
- }
- }
-
- /**
- * Because $this->c[self::DATATABLE_ASSOCIATED] is negative when the table is in memory,
- * we must prior to serialize() call, make sure the ID is saved as positive integer
- *
- * Only serialize the "c" member
- */
- public function __sleep()
- {
- if(!empty($this->c[self::DATATABLE_ASSOCIATED])
- && $this->c[self::DATATABLE_ASSOCIATED] < 0)
- {
- $this->c[self::DATATABLE_ASSOCIATED] = -1 * $this->c[self::DATATABLE_ASSOCIATED];
- $this->subtableIdWasNegativeBeforeSerialize = true;
- }
- return array('c');
- }
-
- /**
- * Must be called after the row was serialized and __sleep was called
- *
- */
- public function cleanPostSerialize()
- {
- if($this->subtableIdWasNegativeBeforeSerialize)
- {
- $this->c[self::DATATABLE_ASSOCIATED] = -1 * $this->c[self::DATATABLE_ASSOCIATED];
- $this->subtableIdWasNegativeBeforeSerialize = false;
- }
- }
-
- /**
- * When destroyed, a row destroys its associated subTable if there is one
- */
- public function __destruct()
- {
- if($this->isSubtableLoaded())
- {
- Piwik_DataTable_Manager::getInstance()->deleteTable( $this->getIdSubDataTable() );
- $this->c[self::DATATABLE_ASSOCIATED] = null;
- }
- }
-
- /**
- * Applies a basic rendering to the Row and returns the output
- *
- * @return string characterizing the row. Example: - 1 ['label' => 'piwik', 'nb_uniq_visitors' => 1685, 'nb_visits' => 1861, 'nb_actions' => 2271, 'max_actions' => 13, 'sum_visit_length' => 920131, 'bounce_count' => 1599] [] [idsubtable = 1375]
- */
- public function __toString()
- {
- $columns = array();
- foreach($this->getColumns() as $column => $value)
- {
- if(is_string($value)) $value = "'$value'";
- elseif(is_array($value)) $value = var_export($value, true);
- $columns[] = "'$column' => $value";
- }
- $columns = implode(", ", $columns);
- $metadata = array();
- foreach($this->getMetadata() as $name => $value)
- {
- if(is_string($value)) $value = "'$value'";
- elseif(is_array($value)) $value = var_export($value, true);
- $metadata[] = "'$name' => $value";
- }
- $metadata = implode(", ", $metadata);
- $output = "# [".$columns."] [".$metadata."] [idsubtable = " . $this->getIdSubDataTable()."]<br />\n";
- return $output;
- }
-
- /**
- * Deletes the given column
- *
- * @param string $name Column name
- * @return bool True on success, false if the column didn't exist
- */
- public function deleteColumn( $name )
- {
- if(!isset($this->c[self::COLUMNS][$name]))
- {
- return false;
- }
- unset($this->c[self::COLUMNS][$name]);
- return true;
- }
-
- /**
- * Renames the given column
- *
- * @param string $oldName
- * @param string $newName
- */
- public function renameColumn($oldName, $newName)
- {
- 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
- unset($this->c[self::COLUMNS][$oldName]);
- }
-
- /**
- * Returns the given column
- *
- * @param string $name Column name
- * @return mixed|false The column value
- */
- public function getColumn( $name )
- {
- if(!isset($this->c[self::COLUMNS][$name]))
- {
- return false;
- }
- return $this->c[self::COLUMNS][$name];
- }
-
- /**
- * Returns the array of all metadata,
- * or the specified metadata
- *
- * @param string $name Metadata name
- * @return mixed|array|false
- */
- public function getMetadata( $name = null )
- {
- if(is_null($name))
- {
- return $this->c[self::METADATA];
- }
- if(!isset($this->c[self::METADATA][$name]))
- {
- return false;
- }
- return $this->c[self::METADATA][$name];
- }
-
- /**
- * Returns the array containing all the columns
- *
- * @return array Example: array(
- * 'column1' => VALUE,
- * 'label' => 'www.php.net'
- * 'nb_visits' => 15894,
- * )
- */
- public function getColumns()
- {
- return $this->c[self::COLUMNS];
- }
-
- /**
- * Returns the ID of the subDataTable.
- * If there is no such a table, returns null.
- *
- * @return int|null
- */
- public function getIdSubDataTable()
- {
- return !is_null($this->c[self::DATATABLE_ASSOCIATED])
- // abs() is to ensure we return a positive int, @see isSubtableLoaded()
- ? abs($this->c[self::DATATABLE_ASSOCIATED])
- : null;
- }
-
- /**
- * Returns the associated subtable, if one exists.
- *
- * @return Piwik_DataTable|false
- */
- public function getSubtable()
- {
- if ($this->isSubtableLoaded())
- {
- return Piwik_DataTable_Manager::getInstance()->getTable($this->getIdSubDataTable());
- }
- return false;
- }
-
- /**
- * Sums a DataTable to this row subDataTable.
- * If this row doesn't have a SubDataTable yet, we create a new one.
- * Then we add the values of the given DataTable to this row's DataTable.
- *
- * @param Piwik_DataTable $subTable Table to sum to this row's subDatatable
- * @see Piwik_DataTable::addDataTable() for the algorithm used for the sum
- */
- public function sumSubtable(Piwik_DataTable $subTable)
- {
- if($this->isSubtableLoaded())
- {
- $thisSubTable = $this->getSubtable();
- }
- else
- {
- $thisSubTable = new Piwik_DataTable();
- $this->addSubtable($thisSubTable);
- }
- $thisSubTable->addDataTable($subTable);
- }
-
-
- /**
- * Set a DataTable to be associated to this row.
- * If the row already has a DataTable associated to it, throws an Exception.
- *
- * @param Piwik_DataTable $subTable DataTable to associate to this row
- * @return Piwik_DataTable Returns $subTable.
- * @throws Exception
- */
- public function addSubtable(Piwik_DataTable $subTable)
- {
- if(!is_null($this->c[self::DATATABLE_ASSOCIATED]))
- {
- throw new Exception("Adding a subtable to the row, but it already has a subtable associated.");
- }
- return $this->setSubtable($subTable);
- }
-
- /**
- * Set a DataTable to this row. If there is already
- * a DataTable associated, it is simply overwritten.
- *
- * @param Piwik_DataTable $subTable DataTable to associate to this row
- * @return Piwik_DataTable Returns $subTable.
- */
- public function setSubtable(Piwik_DataTable $subTable)
- {
- // Hacking -1 to ensure value is negative, so we know the table was loaded
- // @see isSubtableLoaded()
- $this->c[self::DATATABLE_ASSOCIATED] = -1 * $subTable->getId();
- return $subTable;
- }
-
- /**
- * Returns true if the subtable is currently loaded in memory via DataTable_Manager
- *
- *
- * @return bool
- */
- public function isSubtableLoaded()
- {
- // self::DATATABLE_ASSOCIATED are set as negative values,
- // as a flag to signify that the subtable is loaded in memory
- return !is_null($this->c[self::DATATABLE_ASSOCIATED])
- && $this->c[self::DATATABLE_ASSOCIATED] < 0;
- }
-
- /**
- * Remove the sub table reference
- */
- public function removeSubtable()
- {
- $this->c[self::DATATABLE_ASSOCIATED] = null;
- }
-
- /**
- * Set all the columns at once. Overwrites previously set columns.
- *
- * @param array array(
- * 'label' => 'www.php.net'
- * 'nb_visits' => 15894,
- * )
- */
- public function setColumns( $columns )
- {
- $this->c[self::COLUMNS] = $columns;
- }
-
- /**
- * Set the value $value to the column called $name.
- *
- * @param string $name name of the column to set
- * @param mixed $value value of the column to set
- */
- public function setColumn($name, $value)
- {
- $this->c[self::COLUMNS][$name] = $value;
- }
-
- /**
- * Set the value $value to the metadata called $name.
- *
- * @param string $name name of the metadata to set
- * @param mixed $value value of the metadata to set
- */
- public function setMetadata($name, $value)
- {
- $this->c[self::METADATA][$name] = $value;
- }
-
- /**
- * Deletes the given metadata
- *
- * @param bool|string $name Meta column name (omit to delete entire metadata)
- * @return bool True on success, false if the column didn't exist
- */
- public function deleteMetadata($name = false)
- {
- if($name === false)
- {
- $this->c[self::METADATA] = array();
- return true;
- }
- if(!isset($this->c[self::METADATA][$name]))
- {
- return false;
- }
- unset($this->c[self::METADATA][$name]);
- return true;
- }
-
- /**
- * Add a new column to the row. If the column already exists, throws an exception
- *
- * @param string $name name of the column to add
- * @param mixed $value value of the column to set
- * @throws Exception
- */
- public function addColumn($name, $value)
- {
- if(isset($this->c[self::COLUMNS][$name]))
- {
+ /**
+ * List of columns that cannot be summed. An associative array for speed.
+ *
+ * @var array
+ */
+ private static $unsummableColumns = array(
+ 'label' => true,
+ 'full_url' => true // column used w/ old Piwik versions
+ );
+
+ /**
+ * This array contains the row information:
+ * - array indexed by self::COLUMNS contains the columns, pairs of (column names, value)
+ * - (optional) array indexed by self::METADATA contains the metadata, pairs of (metadata name, value)
+ * - (optional) integer indexed by self::DATATABLE_ASSOCIATED contains the ID of the Piwik_DataTable associated to this row.
+ * This ID can be used to read the DataTable from the DataTable_Manager.
+ *
+ * @var array
+ * @see constructor for more information
+ */
+ public $c = array();
+ private $subtableIdWasNegativeBeforeSerialize = false;
+
+ // @see sumRow - implementation detail
+ public $maxVisitsSummed = 0;
+
+ const COLUMNS = 0;
+ const METADATA = 1;
+ const DATATABLE_ASSOCIATED = 3;
+
+
+ /**
+ * Efficient load of the Row structure from a well structured php array
+ *
+ * @param array $row The row array has the structure
+ * array(
+ * Piwik_DataTable_Row::COLUMNS => array(
+ * 'label' => 'Piwik',
+ * 'column1' => 42,
+ * 'visits' => 657,
+ * 'time_spent' => 155744,
+ * ),
+ * Piwik_DataTable_Row::METADATA => array(
+ * 'logo' => 'test.png'
+ * ),
+ * Piwik_DataTable_Row::DATATABLE_ASSOCIATED => #Piwik_DataTable object
+ * (but in the row only the ID will be stored)
+ * )
+ */
+ public function __construct($row = array())
+ {
+ $this->c[self::COLUMNS] = array();
+ $this->c[self::METADATA] = array();
+ $this->c[self::DATATABLE_ASSOCIATED] = null;
+
+ if (isset($row[self::COLUMNS])) {
+ $this->c[self::COLUMNS] = $row[self::COLUMNS];
+ }
+ if (isset($row[self::METADATA])) {
+ $this->c[self::METADATA] = $row[self::METADATA];
+ }
+ if (isset($row[self::DATATABLE_ASSOCIATED])
+ && $row[self::DATATABLE_ASSOCIATED] instanceof Piwik_DataTable
+ ) {
+ $this->setSubtable($row[self::DATATABLE_ASSOCIATED]);
+ }
+ }
+
+ /**
+ * Because $this->c[self::DATATABLE_ASSOCIATED] is negative when the table is in memory,
+ * we must prior to serialize() call, make sure the ID is saved as positive integer
+ *
+ * Only serialize the "c" member
+ */
+ public function __sleep()
+ {
+ if (!empty($this->c[self::DATATABLE_ASSOCIATED])
+ && $this->c[self::DATATABLE_ASSOCIATED] < 0
+ ) {
+ $this->c[self::DATATABLE_ASSOCIATED] = -1 * $this->c[self::DATATABLE_ASSOCIATED];
+ $this->subtableIdWasNegativeBeforeSerialize = true;
+ }
+ return array('c');
+ }
+
+ /**
+ * Must be called after the row was serialized and __sleep was called
+ *
+ */
+ public function cleanPostSerialize()
+ {
+ if ($this->subtableIdWasNegativeBeforeSerialize) {
+ $this->c[self::DATATABLE_ASSOCIATED] = -1 * $this->c[self::DATATABLE_ASSOCIATED];
+ $this->subtableIdWasNegativeBeforeSerialize = false;
+ }
+ }
+
+ /**
+ * When destroyed, a row destroys its associated subTable if there is one
+ */
+ public function __destruct()
+ {
+ if ($this->isSubtableLoaded()) {
+ Piwik_DataTable_Manager::getInstance()->deleteTable($this->getIdSubDataTable());
+ $this->c[self::DATATABLE_ASSOCIATED] = null;
+ }
+ }
+
+ /**
+ * Applies a basic rendering to the Row and returns the output
+ *
+ * @return string characterizing the row. Example: - 1 ['label' => 'piwik', 'nb_uniq_visitors' => 1685, 'nb_visits' => 1861, 'nb_actions' => 2271, 'max_actions' => 13, 'sum_visit_length' => 920131, 'bounce_count' => 1599] [] [idsubtable = 1375]
+ */
+ public function __toString()
+ {
+ $columns = array();
+ foreach ($this->getColumns() as $column => $value) {
+ if (is_string($value)) $value = "'$value'";
+ elseif (is_array($value)) $value = var_export($value, true);
+ $columns[] = "'$column' => $value";
+ }
+ $columns = implode(", ", $columns);
+ $metadata = array();
+ foreach ($this->getMetadata() as $name => $value) {
+ if (is_string($value)) $value = "'$value'";
+ elseif (is_array($value)) $value = var_export($value, true);
+ $metadata[] = "'$name' => $value";
+ }
+ $metadata = implode(", ", $metadata);
+ $output = "# [" . $columns . "] [" . $metadata . "] [idsubtable = " . $this->getIdSubDataTable() . "]<br />\n";
+ return $output;
+ }
+
+ /**
+ * Deletes the given column
+ *
+ * @param string $name Column name
+ * @return bool True on success, false if the column didn't exist
+ */
+ public function deleteColumn($name)
+ {
+ if (!isset($this->c[self::COLUMNS][$name])) {
+ return false;
+ }
+ unset($this->c[self::COLUMNS][$name]);
+ return true;
+ }
+
+ /**
+ * Renames the given column
+ *
+ * @param string $oldName
+ * @param string $newName
+ */
+ public function renameColumn($oldName, $newName)
+ {
+ 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
+ unset($this->c[self::COLUMNS][$oldName]);
+ }
+
+ /**
+ * Returns the given column
+ *
+ * @param string $name Column name
+ * @return mixed|false The column value
+ */
+ public function getColumn($name)
+ {
+ if (!isset($this->c[self::COLUMNS][$name])) {
+ return false;
+ }
+ return $this->c[self::COLUMNS][$name];
+ }
+
+ /**
+ * Returns the array of all metadata,
+ * or the specified metadata
+ *
+ * @param string $name Metadata name
+ * @return mixed|array|false
+ */
+ public function getMetadata($name = null)
+ {
+ if (is_null($name)) {
+ return $this->c[self::METADATA];
+ }
+ if (!isset($this->c[self::METADATA][$name])) {
+ return false;
+ }
+ return $this->c[self::METADATA][$name];
+ }
+
+ /**
+ * Returns the array containing all the columns
+ *
+ * @return array Example: array(
+ * 'column1' => VALUE,
+ * 'label' => 'www.php.net'
+ * 'nb_visits' => 15894,
+ * )
+ */
+ public function getColumns()
+ {
+ return $this->c[self::COLUMNS];
+ }
+
+ /**
+ * Returns the ID of the subDataTable.
+ * If there is no such a table, returns null.
+ *
+ * @return int|null
+ */
+ public function getIdSubDataTable()
+ {
+ return !is_null($this->c[self::DATATABLE_ASSOCIATED])
+ // abs() is to ensure we return a positive int, @see isSubtableLoaded()
+ ? abs($this->c[self::DATATABLE_ASSOCIATED])
+ : null;
+ }
+
+ /**
+ * Returns the associated subtable, if one exists.
+ *
+ * @return Piwik_DataTable|false
+ */
+ public function getSubtable()
+ {
+ if ($this->isSubtableLoaded()) {
+ return Piwik_DataTable_Manager::getInstance()->getTable($this->getIdSubDataTable());
+ }
+ return false;
+ }
+
+ /**
+ * Sums a DataTable to this row subDataTable.
+ * If this row doesn't have a SubDataTable yet, we create a new one.
+ * Then we add the values of the given DataTable to this row's DataTable.
+ *
+ * @param Piwik_DataTable $subTable Table to sum to this row's subDatatable
+ * @see Piwik_DataTable::addDataTable() for the algorithm used for the sum
+ */
+ public function sumSubtable(Piwik_DataTable $subTable)
+ {
+ if ($this->isSubtableLoaded()) {
+ $thisSubTable = $this->getSubtable();
+ } else {
+ $thisSubTable = new Piwik_DataTable();
+ $this->addSubtable($thisSubTable);
+ }
+ $thisSubTable->addDataTable($subTable);
+ }
+
+
+ /**
+ * Set a DataTable to be associated to this row.
+ * If the row already has a DataTable associated to it, throws an Exception.
+ *
+ * @param Piwik_DataTable $subTable DataTable to associate to this row
+ * @return Piwik_DataTable Returns $subTable.
+ * @throws Exception
+ */
+ public function addSubtable(Piwik_DataTable $subTable)
+ {
+ if (!is_null($this->c[self::DATATABLE_ASSOCIATED])) {
+ throw new Exception("Adding a subtable to the row, but it already has a subtable associated.");
+ }
+ return $this->setSubtable($subTable);
+ }
+
+ /**
+ * Set a DataTable to this row. If there is already
+ * a DataTable associated, it is simply overwritten.
+ *
+ * @param Piwik_DataTable $subTable DataTable to associate to this row
+ * @return Piwik_DataTable Returns $subTable.
+ */
+ public function setSubtable(Piwik_DataTable $subTable)
+ {
+ // Hacking -1 to ensure value is negative, so we know the table was loaded
+ // @see isSubtableLoaded()
+ $this->c[self::DATATABLE_ASSOCIATED] = -1 * $subTable->getId();
+ return $subTable;
+ }
+
+ /**
+ * Returns true if the subtable is currently loaded in memory via DataTable_Manager
+ *
+ *
+ * @return bool
+ */
+ public function isSubtableLoaded()
+ {
+ // self::DATATABLE_ASSOCIATED are set as negative values,
+ // as a flag to signify that the subtable is loaded in memory
+ return !is_null($this->c[self::DATATABLE_ASSOCIATED])
+ && $this->c[self::DATATABLE_ASSOCIATED] < 0;
+ }
+
+ /**
+ * Remove the sub table reference
+ */
+ public function removeSubtable()
+ {
+ $this->c[self::DATATABLE_ASSOCIATED] = null;
+ }
+
+ /**
+ * Set all the columns at once. Overwrites previously set columns.
+ *
+ * @param array array(
+ * 'label' => 'www.php.net'
+ * 'nb_visits' => 15894,
+ * )
+ */
+ public function setColumns($columns)
+ {
+ $this->c[self::COLUMNS] = $columns;
+ }
+
+ /**
+ * Set the value $value to the column called $name.
+ *
+ * @param string $name name of the column to set
+ * @param mixed $value value of the column to set
+ */
+ public function setColumn($name, $value)
+ {
+ $this->c[self::COLUMNS][$name] = $value;
+ }
+
+ /**
+ * Set the value $value to the metadata called $name.
+ *
+ * @param string $name name of the metadata to set
+ * @param mixed $value value of the metadata to set
+ */
+ public function setMetadata($name, $value)
+ {
+ $this->c[self::METADATA][$name] = $value;
+ }
+
+ /**
+ * Deletes the given metadata
+ *
+ * @param bool|string $name Meta column name (omit to delete entire metadata)
+ * @return bool True on success, false if the column didn't exist
+ */
+ public function deleteMetadata($name = false)
+ {
+ if ($name === false) {
+ $this->c[self::METADATA] = array();
+ return true;
+ }
+ if (!isset($this->c[self::METADATA][$name])) {
+ return false;
+ }
+ unset($this->c[self::METADATA][$name]);
+ return true;
+ }
+
+ /**
+ * Add a new column to the row. If the column already exists, throws an exception
+ *
+ * @param string $name name of the column to add
+ * @param mixed $value value of the column to set
+ * @throws Exception
+ */
+ public function addColumn($name, $value)
+ {
+ if (isset($this->c[self::COLUMNS][$name])) {
// debug_print_backtrace();
- throw new Exception("Column $name already in the array!");
- }
- $this->c[self::COLUMNS][$name] = $value;
- }
-
- /**
- * Add columns to the row
- *
- * @param array $columns Name/Value pairs, e.g., array( name => value , ...)
- * @return void
- */
- public function addColumns($columns)
- {
- foreach($columns as $name => $value)
- {
- try {
- $this->addColumn($name, $value);
- } catch(Exception $e) {
- }
- }
-
- if(!empty($e)) {
- throw $e;
- }
- }
-
- /**
- * Add a new metadata to the row. If the column already exists, throws an exception.
- *
- * @param string $name name of the metadata to add
- * @param mixed $value value of the metadata to set
- * @throws Exception
- */
- public function addMetadata($name, $value)
- {
- if(isset($this->c[self::METADATA][$name]))
- {
- throw new Exception("Metadata $name already in the array!");
- }
- $this->c[self::METADATA][$name] = $value;
- }
-
- /**
- * Sums the given $row columns values to the existing row' columns values.
- * It will sum only the int or float values of $row.
- * It will not sum the column 'label' even if it has a numeric value.
- *
- * If a given column doesn't exist in $this then it is added with the value of $row.
- * If the column already exists in $this then we have
- * this.columns[idThisCol] += $row.columns[idThisCol]
- *
- * @param Piwik_DataTable_Row $rowToSum
- */
- public function sumRow( Piwik_DataTable_Row $rowToSum, $enableCopyMetadata = true )
- {
- foreach($rowToSum->getColumns() as $columnToSumName => $columnToSumValue)
- {
- if (!isset(self::$unsummableColumns[$columnToSumName])) // make sure we can add this column
- {
- $thisColumnValue = $this->getColumn($columnToSumName);
-
- // Max operation
- if($columnToSumName == Piwik_Archive::INDEX_MAX_ACTIONS )
- {
- $newValue = max($thisColumnValue, $columnToSumValue);
- }
- else
- {
- $newValue = $this->sumRowArray($thisColumnValue, $columnToSumValue);
- }
- $this->setColumn( $columnToSumName, $newValue);
- }
- }
-
- if($enableCopyMetadata)
- {
- $this->sumRowMetadata($rowToSum);
- }
- }
-
- public function sumRowMetadata($rowToSum)
- {
- if (!empty($rowToSum->c[self::METADATA])
- && !$this->isSummaryRow())
- {
- // We shall update metadata, and keep the metadata with the _most visits or pageviews_, rather than first or last seen
- $visits = max($rowToSum->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS) || $rowToSum->getColumn(Piwik_Archive::INDEX_NB_VISITS),
- // Old format pre-1.2, @see also method updateInterestStats()
- $rowToSum->getColumn('nb_actions') || $rowToSum->getColumn('nb_visits'));
- if (($visits && $visits > $this->maxVisitsSummed)
- || empty($this->c[self::METADATA])
- ) {
- $this->maxVisitsSummed = $visits;
- $this->c[self::METADATA] = $rowToSum->c[self::METADATA];
- }
- }
- }
-
- public function isSummaryRow()
- {
- return $this->getColumn('label') === Piwik_DataTable::LABEL_SUMMARY_ROW;
- }
-
- /**
- * Helper function: sums 2 values
- *
- * @param number|bool $thisColumnValue
- * @param number|array $columnToSumValue
- * @return array|int
- */
- protected function sumRowArray( $thisColumnValue, $columnToSumValue )
- {
- if(is_numeric($columnToSumValue))
- {
- if($thisColumnValue === false)
- {
- $thisColumnValue = 0;
- }
- return $thisColumnValue + $columnToSumValue;
- }
-
- if(is_array($columnToSumValue))
- {
- if($thisColumnValue == false)
- {
- return $columnToSumValue;
- }
- $newValue = $thisColumnValue;
- foreach($columnToSumValue as $arrayIndex => $arrayValue)
- {
- if(!isset($newValue[$arrayIndex]))
- {
- $newValue[$arrayIndex] = false;
- }
- $newValue[$arrayIndex] = $this->sumRowArray($newValue[$arrayIndex], $arrayValue);
- }
- return $newValue;
- }
-
- if (is_string($columnToSumValue))
- {
- if ($thisColumnValue === false)
- {
- return $columnToSumValue;
- }
- else if ($columnToSumValue === false)
- {
- return $thisColumnValue;
- }
- else
- {
- throw new Exception("Trying to add two strings values in DataTable_Row::sumRowArray: "
- . "'$thisColumnValue' + '$columnToSumValue'");
- }
- }
-
- return 0;
- }
-
- /**
- * Helper function to compare array elements
- *
- * @param mixed $elem1
- * @param mixed $elem2
- * @return bool
- */
- static public function compareElements($elem1, $elem2)
- {
- if (is_array($elem1)) {
- if (is_array($elem2))
- {
- return strcmp(serialize($elem1), serialize($elem2));
- }
- return 1;
- }
- if (is_array($elem2))
- return -1;
-
- if ((string)$elem1 === (string)$elem2)
- return 0;
-
- return ((string)$elem1 > (string)$elem2) ? 1 : -1;
- }
-
- /**
- * Helper function to test if two rows are equal.
- *
- * Two rows are equal
- * - if they have exactly the same columns / metadata
- * - if they have a subDataTable associated, then we check that both of them are the same.
- *
- * @param Piwik_DataTable_Row $row1 first to compare
- * @param Piwik_DataTable_Row $row2 second to compare
- * @return bool
- */
- static public function isEqual( Piwik_DataTable_Row $row1, Piwik_DataTable_Row $row2 )
- {
- //same columns
- $cols1 = $row1->getColumns();
- $cols2 = $row2->getColumns();
-
- $diff1 = array_udiff($cols1, $cols2, array(__CLASS__, 'compareElements'));
- $diff2 = array_udiff($cols2, $cols1, array(__CLASS__, 'compareElements'));
-
- if($diff1 != $diff2)
- {
- return false;
- }
-
- $dets1 = $row1->getMetadata();
- $dets2 = $row2->getMetadata();
-
- ksort($dets1);
- ksort($dets2);
-
- if($dets1 != $dets2)
- {
- return false;
- }
-
- // either both are null
- // or both have a value
- if( !(is_null($row1->getIdSubDataTable())
- && is_null($row2->getIdSubDataTable())
- )
- )
- {
- $subtable1 = $row1->getSubtable();
- $subtable2 = $row2->getSubtable();
- if(!Piwik_DataTable::isEqual($subtable1, $subtable2))
- {
- return false;
- }
- }
- return true;
- }
+ throw new Exception("Column $name already in the array!");
+ }
+ $this->c[self::COLUMNS][$name] = $value;
+ }
+
+ /**
+ * Add columns to the row
+ *
+ * @param array $columns Name/Value pairs, e.g., array( name => value , ...)
+ * @return void
+ */
+ public function addColumns($columns)
+ {
+ foreach ($columns as $name => $value) {
+ try {
+ $this->addColumn($name, $value);
+ } catch (Exception $e) {
+ }
+ }
+
+ if (!empty($e)) {
+ throw $e;
+ }
+ }
+
+ /**
+ * Add a new metadata to the row. If the column already exists, throws an exception.
+ *
+ * @param string $name name of the metadata to add
+ * @param mixed $value value of the metadata to set
+ * @throws Exception
+ */
+ public function addMetadata($name, $value)
+ {
+ if (isset($this->c[self::METADATA][$name])) {
+ throw new Exception("Metadata $name already in the array!");
+ }
+ $this->c[self::METADATA][$name] = $value;
+ }
+
+ /**
+ * Sums the given $row columns values to the existing row' columns values.
+ * It will sum only the int or float values of $row.
+ * It will not sum the column 'label' even if it has a numeric value.
+ *
+ * If a given column doesn't exist in $this then it is added with the value of $row.
+ * If the column already exists in $this then we have
+ * this.columns[idThisCol] += $row.columns[idThisCol]
+ *
+ * @param Piwik_DataTable_Row $rowToSum
+ */
+ public function sumRow(Piwik_DataTable_Row $rowToSum, $enableCopyMetadata = true)
+ {
+ foreach ($rowToSum->getColumns() as $columnToSumName => $columnToSumValue) {
+ if (!isset(self::$unsummableColumns[$columnToSumName])) // make sure we can add this column
+ {
+ $thisColumnValue = $this->getColumn($columnToSumName);
+
+ // Max operation
+ if ($columnToSumName == Piwik_Archive::INDEX_MAX_ACTIONS) {
+ $newValue = max($thisColumnValue, $columnToSumValue);
+ } else {
+ $newValue = $this->sumRowArray($thisColumnValue, $columnToSumValue);
+ }
+ $this->setColumn($columnToSumName, $newValue);
+ }
+ }
+
+ if ($enableCopyMetadata) {
+ $this->sumRowMetadata($rowToSum);
+ }
+ }
+
+ public function sumRowMetadata($rowToSum)
+ {
+ if (!empty($rowToSum->c[self::METADATA])
+ && !$this->isSummaryRow()
+ ) {
+ // We shall update metadata, and keep the metadata with the _most visits or pageviews_, rather than first or last seen
+ $visits = max($rowToSum->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS) || $rowToSum->getColumn(Piwik_Archive::INDEX_NB_VISITS),
+ // Old format pre-1.2, @see also method updateInterestStats()
+ $rowToSum->getColumn('nb_actions') || $rowToSum->getColumn('nb_visits'));
+ if (($visits && $visits > $this->maxVisitsSummed)
+ || empty($this->c[self::METADATA])
+ ) {
+ $this->maxVisitsSummed = $visits;
+ $this->c[self::METADATA] = $rowToSum->c[self::METADATA];
+ }
+ }
+ }
+
+ public function isSummaryRow()
+ {
+ return $this->getColumn('label') === Piwik_DataTable::LABEL_SUMMARY_ROW;
+ }
+
+ /**
+ * Helper function: sums 2 values
+ *
+ * @param number|bool $thisColumnValue
+ * @param number|array $columnToSumValue
+ * @return array|int
+ */
+ protected function sumRowArray($thisColumnValue, $columnToSumValue)
+ {
+ if (is_numeric($columnToSumValue)) {
+ if ($thisColumnValue === false) {
+ $thisColumnValue = 0;
+ }
+ return $thisColumnValue + $columnToSumValue;
+ }
+
+ if (is_array($columnToSumValue)) {
+ if ($thisColumnValue == false) {
+ return $columnToSumValue;
+ }
+ $newValue = $thisColumnValue;
+ foreach ($columnToSumValue as $arrayIndex => $arrayValue) {
+ if (!isset($newValue[$arrayIndex])) {
+ $newValue[$arrayIndex] = false;
+ }
+ $newValue[$arrayIndex] = $this->sumRowArray($newValue[$arrayIndex], $arrayValue);
+ }
+ return $newValue;
+ }
+
+ if (is_string($columnToSumValue)) {
+ if ($thisColumnValue === false) {
+ return $columnToSumValue;
+ } else if ($columnToSumValue === false) {
+ return $thisColumnValue;
+ } else {
+ throw new Exception("Trying to add two strings values in DataTable_Row::sumRowArray: "
+ . "'$thisColumnValue' + '$columnToSumValue'");
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Helper function to compare array elements
+ *
+ * @param mixed $elem1
+ * @param mixed $elem2
+ * @return bool
+ */
+ static public function compareElements($elem1, $elem2)
+ {
+ if (is_array($elem1)) {
+ if (is_array($elem2)) {
+ return strcmp(serialize($elem1), serialize($elem2));
+ }
+ return 1;
+ }
+ if (is_array($elem2))
+ return -1;
+
+ if ((string)$elem1 === (string)$elem2)
+ return 0;
+
+ return ((string)$elem1 > (string)$elem2) ? 1 : -1;
+ }
+
+ /**
+ * Helper function to test if two rows are equal.
+ *
+ * Two rows are equal
+ * - if they have exactly the same columns / metadata
+ * - if they have a subDataTable associated, then we check that both of them are the same.
+ *
+ * @param Piwik_DataTable_Row $row1 first to compare
+ * @param Piwik_DataTable_Row $row2 second to compare
+ * @return bool
+ */
+ static public function isEqual(Piwik_DataTable_Row $row1, Piwik_DataTable_Row $row2)
+ {
+ //same columns
+ $cols1 = $row1->getColumns();
+ $cols2 = $row2->getColumns();
+
+ $diff1 = array_udiff($cols1, $cols2, array(__CLASS__, 'compareElements'));
+ $diff2 = array_udiff($cols2, $cols1, array(__CLASS__, 'compareElements'));
+
+ if ($diff1 != $diff2) {
+ return false;
+ }
+
+ $dets1 = $row1->getMetadata();
+ $dets2 = $row2->getMetadata();
+
+ ksort($dets1);
+ ksort($dets2);
+
+ if ($dets1 != $dets2) {
+ return false;
+ }
+
+ // either both are null
+ // or both have a value
+ if (!(is_null($row1->getIdSubDataTable())
+ && is_null($row2->getIdSubDataTable())
+ )
+ ) {
+ $subtable1 = $row1->getSubtable();
+ $subtable2 = $row2->getSubtable();
+ if (!Piwik_DataTable::isEqual($subtable1, $subtable2)) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/core/DataTable/Row/DataTableSummary.php b/core/DataTable/Row/DataTableSummary.php
index 7a38dc6118..f0ad234d54 100644
--- a/core/DataTable/Row/DataTableSummary.php
+++ b/core/DataTable/Row/DataTableSummary.php
@@ -1,63 +1,60 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * This class creates a row from a given DataTable.
- * The row contains
+ * This class creates a row from a given DataTable.
+ * The row contains
* - for each numeric column, the returned "summary" column is the sum of all the subRows
* - for every other column, it is ignored and will not be in the "summary row"
- *
+ *
* @see Piwik_DataTable_Row::sumRow() for more information on the algorithm
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Row_DataTableSummary extends Piwik_DataTable_Row
{
- /**
- * @param Piwik_DataTable $subTable
- */
- function __construct($subTable = null)
- {
- parent::__construct();
-
- if ($subTable !== null)
- {
- $this->sumTable($subTable);
- }
- }
-
- /**
- * Reset this row to an empty one and sum the associated subtable again.
- */
- public function recalculate()
- {
- $id = $this->getIdSubDataTable();
- if ($id !== null)
- {
- $subtable = Piwik_DataTable_Manager::getInstance()->getTable($id);
- $this->sumTable($subtable);
- }
- }
-
- /**
- * Sums a tables row with this one.
- *
- * @param Piwik_DataTable $table
- */
- private function sumTable( $table )
- {
- foreach($table->getRows() as $row)
- {
- $this->sumRow($row, $enableCopyMetadata = false);
- }
- }
+ /**
+ * @param Piwik_DataTable $subTable
+ */
+ function __construct($subTable = null)
+ {
+ parent::__construct();
+
+ if ($subTable !== null) {
+ $this->sumTable($subTable);
+ }
+ }
+
+ /**
+ * Reset this row to an empty one and sum the associated subtable again.
+ */
+ public function recalculate()
+ {
+ $id = $this->getIdSubDataTable();
+ if ($id !== null) {
+ $subtable = Piwik_DataTable_Manager::getInstance()->getTable($id);
+ $this->sumTable($subtable);
+ }
+ }
+
+ /**
+ * Sums a tables row with this one.
+ *
+ * @param Piwik_DataTable $table
+ */
+ private function sumTable($table)
+ {
+ foreach ($table->getRows() as $row) {
+ $this->sumRow($row, $enableCopyMetadata = false);
+ }
+ }
}
diff --git a/core/DataTable/Simple.php b/core/DataTable/Simple.php
index bba122db20..7189fb0b2e 100644
--- a/core/DataTable/Simple.php
+++ b/core/DataTable/Simple.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,40 +12,40 @@
/**
* The DataTable_Simple is used to provide an easy way to create simple DataGrid.
* A DataTable_Simple actually is a DataTable with 2 columns: 'label' and 'value'.
- *
- * It is usually best to return a DataTable_Simple instead of
+ *
+ * It is usually best to return a DataTable_Simple instead of
* a PHP array (or other custom data structure) in API methods:
* - the generic filters can be applied automatically (offset, limit, pattern search, sort, etc.)
* - the renderer can be applied (XML, PHP, HTML, etc.)
* So you don't have to write specific renderer for your data, it is already available in all the formats supported natively by Piwik.
- *
+ *
* @package Piwik
* @subpackage Piwik_DataTable
*/
class Piwik_DataTable_Simple extends Piwik_DataTable
{
- /**
- * Loads (append) in the DataTable the array information
- *
- * @param array $array Array containing the rows information
- * array(
- * 'Label row 1' => Value row 1,
- * 'Label row 2' => Value row 2,
- * )
- */
- public function addRowsFromArray($array)
- {
- $this->addRowsFromSimpleArray(array($array));
- }
+ /**
+ * Loads (append) in the DataTable the array information
+ *
+ * @param array $array Array containing the rows information
+ * array(
+ * 'Label row 1' => Value row 1,
+ * 'Label row 2' => Value row 2,
+ * )
+ */
+ public function addRowsFromArray($array)
+ {
+ $this->addRowsFromSimpleArray(array($array));
+ }
- /**
- * Updates the given column with the given value
- *
- * @param string $columnName
- * @param mixed $value
- */
- public function setColumn($columnName, $value)
- {
- $this->getLastRow()->setColumn($columnName, $value);
- }
+ /**
+ * Updates the given column with the given value
+ *
+ * @param string $columnName
+ * @param mixed $value
+ */
+ public function setColumn($columnName, $value)
+ {
+ $this->getLastRow()->setColumn($columnName, $value);
+ }
}
diff --git a/core/Date.php b/core/Date.php
index dcafb0d993..cd0fa6c238 100644
--- a/core/Date.php
+++ b/core/Date.php
@@ -16,658 +16,626 @@
*/
class Piwik_Date
{
- /**
- * The stored timestamp is always UTC based.
- * The returned timestamp via getTimestamp() will have the conversion applied
- * @var int|null
- */
- protected $timestamp = null;
-
- /**
- * Timezone the current date object is set to.
- * Timezone will only affect the returned timestamp via getTimestamp()
- * @var string
- */
- protected $timezone = 'UTC';
-
- const DATE_TIME_FORMAT = 'Y-m-d H:i:s';
-
- /**
- * Builds a Piwik_Date object
- *
- * @param int $timestamp
- * @param string $timezone
- * @throws Exception
- */
- protected function __construct( $timestamp, $timezone = 'UTC')
- {
- if(!is_int( $timestamp ))
- {
- throw new Exception("Piwik_Date is expecting a unix timestamp");
- }
- $this->timezone = $timezone;
- $this->timestamp = $timestamp ;
- }
-
- /**
- * Returns a Piwik_Date objects.
- *
- * @param string|self $dateString 'today' 'yesterday' or any YYYY-MM-DD or timestamp
- * @param string $timezone if specified, the dateString will be relative to this $timezone.
- * For example, today in UTC+12 will be a timestamp in the future for UTC.
- * This is different from using ->setTimezone()
- * @throws Exception
- * @return Piwik_Date
- */
- static public function factory($dateString, $timezone = null)
- {
- $invalidDateException = new Exception(Piwik_TranslateException('General_ExceptionInvalidDateFormat', array("YYYY-MM-DD, or 'today' or 'yesterday'", "strtotime", "http://php.net/strtotime")).": $dateString");
- if($dateString instanceof self)
- {
- $dateString = $dateString->toString();
- }
- if($dateString == 'now')
- {
- $date = self::now();
- }
- elseif($dateString == 'today')
- {
- $date = self::today();
- }
- elseif($dateString == 'yesterday')
- {
- $date = self::yesterday();
- }
- elseif($dateString == 'yesterdaySameTime')
- {
- $date = self::yesterdaySameTime();
- }
- elseif (!is_int($dateString)
- && (
- // strtotime returns the timestamp for April 1st for a date like 2011-04-01,today
- // but we don't want this, as this is a date range and supposed to throw the exception
- strpos($dateString, ',') !== false
- ||
- ($dateString = strtotime($dateString)) === false
- ))
- {
- throw $invalidDateException;
- }
- else
- {
- $date = new Piwik_Date($dateString);
- }
- $timestamp = $date->getTimestamp();
- // can't be doing web analytics before the 1st website
- // Tue, 06 Aug 1991 00:00:00 GMT
- if($timestamp < 681436800 )
- {
- throw $invalidDateException;
- }
- if(empty($timezone))
- {
- return $date;
- }
-
- $timestamp = self::adjustForTimezone($timestamp, $timezone);
- return Piwik_Date::factory($timestamp);
- }
-
- /**
- * Returns the datetime of the current timestamp
- *
- * @return string
- */
- function getDatetime()
- {
- return $this->toString(self::DATE_TIME_FORMAT);
- }
-
- /**
- * Returns the datetime start in UTC
- *
- * @return string
- */
- function getDateStartUTC()
- {
- $dateStartUTC = gmdate('Y-m-d', $this->timestamp);
- $date = Piwik_Date::factory($dateStartUTC)->setTimezone($this->timezone);
- return $date->toString(self::DATE_TIME_FORMAT);
- }
-
- /**
- * Returns the datetime end in UTC
- *
- * @return string
- */
- function getDateEndUTC()
- {
- $dateEndUTC = gmdate('Y-m-d 23:59:59', $this->timestamp);
- $date = Piwik_Date::factory($dateEndUTC)->setTimezone($this->timezone);
- return $date->toString(self::DATE_TIME_FORMAT);
- }
-
- /**
- * Returns a new date object, copy of $this, with the timezone set
- * This timezone is used to offset the UTC timestamp returned by @see getTimestamp()
- * Doesn't modify $this
- *
- * @param string $timezone 'UTC', 'Europe/London', ...
- * @return Piwik_Date
- */
- public function setTimezone($timezone)
- {
- return new Piwik_Date($this->timestamp, $timezone);
- }
-
- /**
- * Helper function that returns the offset in the timezone string 'UTC+14'
- * Returns false if the timezone is not UTC+X or UTC-X
- *
- * @param string $timezone
- * @return int|bool utc offset or false
- */
- static protected function extractUtcOffset($timezone)
- {
- if($timezone == 'UTC')
- {
- return 0;
- }
- $start = substr($timezone, 0, 4);
- if($start != 'UTC-'
- && $start != 'UTC+')
- {
- return false;
- }
- $offset = (float)substr($timezone, 4);
- if($start == 'UTC-') {
- $offset = -$offset;
- }
- return $offset;
- }
-
- /**
- * Adjusts a UNIX timestamp in UTC to a specific timezone.
- *
- * @param int $timestamp The UNIX timestamp to adjust.
- * @param string $timezone The timezone to adjust to.
- * @return int The adjusted time as seconds from EPOCH.
- */
- static public function adjustForTimezone($timestamp, $timezone)
- {
- // manually adjust for UTC timezones
- $utcOffset = self::extractUtcOffset($timezone);
- if($utcOffset !== false)
- {
- return self::addHourTo($timestamp, $utcOffset);
- }
-
- date_default_timezone_set($timezone);
- $datetime = date(self::DATE_TIME_FORMAT, $timestamp);
- date_default_timezone_set('UTC');
-
- return strtotime($datetime);
- }
-
- /**
- * Returns the Unix timestamp of the date in UTC
- *
- * @return int
- */
- public function getTimestampUTC()
- {
- return $this->timestamp;
- }
-
- /**
- * Returns the unix timestamp of the date in UTC,
- * converted from the date timezone
- *
- * @return int
- */
- public function getTimestamp()
- {
- if(empty($this->timezone))
- {
- $this->timezone = 'UTC';
- }
- $utcOffset = self::extractUtcOffset($this->timezone);
- if($utcOffset !== false) {
- return (int)($this->timestamp - $utcOffset * 3600);
- }
- // The following code seems clunky - I thought the DateTime php class would allow to return timestamps
- // after applying the timezone offset. Instead, the underlying timestamp is not changed.
- // I decided to get the date without the timezone information, and create the timestamp from the truncated string.
- // 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);
- date_timezone_set($dtime, $dtzone);
- $dateWithTimezone = date_format($dtime, 'r');
- $dateWithoutTimezone = substr($dateWithTimezone, 0, -6);
- $timestamp = strtotime($dateWithoutTimezone);
- date_default_timezone_set('UTC');
-
- return (int)$timestamp;
- }
-
- /**
- * Returns true if the current date is older than the given $date
- *
- * @param Piwik_Date $date
- * @return bool
- */
- public function isLater( Piwik_Date $date)
- {
- return $this->getTimestamp() > $date->getTimestamp();
- }
-
- /**
- * Returns true if the current date is earlier than the given $date
- *
- * @param Piwik_Date $date
- * @return bool
- */
- public function isEarlier(Piwik_Date $date)
- {
- return $this->getTimestamp() < $date->getTimestamp();
- }
-
- /**
- * Returns the Y-m-d representation of the string.
- * You can specify the output, see the list on php.net/date
- *
- * @param string $part
- * @return string
- */
- public function toString($part = 'Y-m-d')
- {
- return date($part, $this->getTimestamp());
- }
-
- /**
- * @see toString()
- *
- * @return string
- */
- public function __toString()
- {
- return $this->toString();
- }
-
- /**
- * Compares the week of the current date against the given $date
- * Returns 0 if equal, -1 if current week is earlier or 1 if current week is later
- * Example: 09.Jan.2007 13:07:25 -> compareWeek(2); -> 0
- *
- * @param Piwik_Date $date
- * @return int 0 = equal, 1 = later, -1 = earlier
- */
- public function compareWeek(Piwik_Date $date)
- {
- $currentWeek = date('W', $this->getTimestamp());
- $toCompareWeek = date('W', $date->getTimestamp());
- if( $currentWeek == $toCompareWeek)
- {
- return 0;
- }
- if( $currentWeek < $toCompareWeek)
- {
- return -1;
- }
- return 1;
- }
-
- /**
- * Compares the month of the current date against the given $date month
- * Returns 0 if equal, -1 if current month is earlier or 1 if current month is later
- * For example: 10.03.2000 -> 15.03.1950 -> 0
- *
- * @param Piwik_Date $date Month to compare
- * @return int 0 = equal, 1 = later, -1 = earlier
- */
- function compareMonth( Piwik_Date $date )
- {
- $currentMonth = date('n', $this->getTimestamp());
- $toCompareMonth = date('n', $date->getTimestamp());
- if( $currentMonth == $toCompareMonth)
- {
- return 0;
- }
- if( $currentMonth < $toCompareMonth)
- {
- return -1;
- }
- return 1;
- }
-
- /**
- * Returns true if current date is today
- *
- * @return bool
- */
- public function isToday()
- {
- return $this->toString('Y-m-d') === Piwik_Date::factory('today', $this->timezone)->toString('Y-m-d');
- }
-
- /**
- * Returns a date object set to now (same as today, except that the time is also set)
- *
- * @return Piwik_Date
- */
- static public function now()
- {
- return new Piwik_Date(time());
- }
-
- /**
- * Returns a date object set to today midnight
- *
- * @return Piwik_Date
- */
- static public function today()
- {
- return new Piwik_Date(strtotime(date("Y-m-d 00:00:00")));
- }
-
- /**
- * Returns a date object set to yesterday midnight
- *
- * @return Piwik_Date
- */
- static public function yesterday()
- {
- return new Piwik_Date(strtotime("yesterday"));
- }
-
- /**
- * Returns a date object set to yesterday same time of day
- *
- * @return Piwik_Date
- */
- static public function yesterdaySameTime()
- {
- return new Piwik_Date(strtotime("yesterday ".date('H:i:s')));
- }
-
- /**
- * Sets the time part of the date
- * Doesn't modify $this
- *
- * @param string $time HH:MM:SS
- * @return Piwik_Date The new date with the time part set
- */
- public function setTime($time)
- {
- return new Piwik_Date( strtotime( date("Y-m-d", $this->timestamp) . " $time"), $this->timezone);
- }
-
- /**
- * Sets a new day
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $day Day eg. 31
- * @return Piwik_Date new date
- */
- public function setDay( $day )
- {
- $ts = $this->timestamp;
- $result = mktime(
- date('H', $ts),
- date('i', $ts),
- date('s', $ts),
- date('n', $ts),
- $day,
- date('Y', $ts)
- );
- return new Piwik_Date( $result, $this->timezone );
- }
-
- /**
- * Sets a new year
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $year 2010
- * @return Piwik_Date new date
- */
- public function setYear( $year )
- {
- $ts = $this->timestamp;
- $result = mktime(
- date('H', $ts),
- date('i', $ts),
- date('s', $ts),
- date('n', $ts),
- date('j', $ts),
- $year
- );
- return new Piwik_Date( $result, $this->timezone );
- }
-
- /**
- * Subtracts days from the existing date object and returns a new Piwik_Date object
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $n
- * @return Piwik_Date new date
- */
- public function subDay( $n )
- {
- if($n === 0)
- {
- return clone $this;
- }
- $ts = strtotime("-$n day", $this->timestamp);
- return new Piwik_Date( $ts, $this->timezone );
- }
-
- /**
- * Subtracts weeks from the existing date object and returns a new Piwik_Date object
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $n
- * @return Piwik_Date new date
- */
- public function subWeek( $n )
- {
- return $this->subDay( 7 * $n );
- }
-
- /**
- * Subtracts a month from the existing date object.
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $n
- * @return Piwik_Date new date
- */
- public function subMonth( $n )
- {
- if($n === 0)
- {
- return clone $this;
- }
- $ts = $this->timestamp;
- $result = mktime(
- date('H', $ts),
- date('i', $ts),
- date('s', $ts),
- date('n', $ts) - $n,
- 1, // we set the day to 1
- date('Y', $ts)
- );
- return new Piwik_Date( $result, $this->timezone );
- }
-
- /**
- * Subtracts a year from the existing date object.
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $n
- * @return Piwik_Date new date
- */
- public function subYear( $n )
- {
- if($n === 0)
- {
- return clone $this;
- }
- $ts = $this->timestamp;
- $result = mktime(
- date('H', $ts),
- date('i', $ts),
- date('s', $ts),
- 1, // we set the month to 1
- 1, // we set the day to 1
- date('Y', $ts) - $n
- );
- return new Piwik_Date( $result, $this->timezone );
- }
-
- /**
- * Returns a localized date string, given a template.
- * Allowed tags are: %day%, %shortDay%, %longDay%, etc.
- *
- * @param string $template string eg. %shortMonth% %longYear%
- * @return string eg. "Aug 2009"
- */
- public function getLocalized($template)
- {
- $day = $this->toString('j');
- $dayOfWeek = $this->toString('N');
- $monthOfYear = $this->toString('n');
- $patternToValue = array(
- "%day%" => $day,
- "%shortMonth%" => Piwik_Translate('General_ShortMonth_'.$monthOfYear),
- "%longMonth%" => Piwik_Translate('General_LongMonth_'.$monthOfYear),
- "%shortDay%" => Piwik_Translate('General_ShortDay_'.$dayOfWeek),
- "%longDay%" => Piwik_Translate('General_LongDay_'.$dayOfWeek),
- "%longYear%" => $this->toString('Y'),
- "%shortYear%" => $this->toString('y'),
- "%time%" => $this->toString('H:i:s')
- );
- $out = str_replace(array_keys($patternToValue), array_values($patternToValue), $template);
- return $out;
- }
-
- /**
- * Adds days to the existing date object.
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $n Number of days to add
- * @return Piwik_Date new date
- */
- public function addDay( $n )
- {
- $ts = strtotime("+$n day", $this->timestamp);
- return new Piwik_Date( $ts, $this->timezone );
- }
-
- /**
- * Adds hours to the existing date object.
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $n Number of hours to add
- * @return Piwik_Date new date
- */
- public function addHour( $n )
- {
- $ts = self::addHourTo( $this->timestamp, $n );
- return new Piwik_Date( $ts, $this->timezone );
- }
-
- /**
- * Adds N number of hours to a UNIX timestamp and returns the result.
- *
- * @param int $timestamp The timestamp to add to.
- * @param number $n Number of hours to add.
- * @return int The result as a UNIX timestamp.
- */
- public static function addHourTo( $timestamp, $n )
- {
- $isNegative = ($n < 0);
- $minutes = 0;
- if($n != round($n))
- {
- if($n >= 1 || $n <= -1)
- {
- $extraMinutes = floor(abs($n));
- if($isNegative)
- {
- $extraMinutes = -$extraMinutes;
- }
- $minutes = abs($n - $extraMinutes) * 60;
- if($isNegative) {
- $minutes *= -1;
- }
- }
- else
- {
- $minutes = $n * 60;
- }
- $n = floor(abs($n));
- if($isNegative) {
- $n *= -1;
- }
- }
- return (int)($timestamp + round($minutes * 60) + $n * 3600);
- }
-
- /**
- * Substract hour to the existing date object.
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $n Number of hours to substract
- * @return Piwik_Date new date
- */
- public function subHour( $n )
- {
- return $this->addHour(-$n);
- }
-
- /**
- * Adds period to the existing date object.
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $n
- * @param string $period period to add (WEEK, DAY,...)
- * @return Piwik_Date new date
- */
- public function addPeriod( $n, $period )
- {
- if($n < 0)
- {
- $ts = strtotime("$n $period", $this->timestamp);
- }
- else
- {
- $ts = strtotime("+$n $period", $this->timestamp);
- }
- return new Piwik_Date( $ts, $this->timezone );
- }
-
- /**
- * Subtracts period from the existing date object.
- * Returned is the new date object
- * Doesn't modify $this
- *
- * @param int $n
- * @param string $period period to sub
- * @return Piwik_Date new date
- */
- public function subPeriod( $n, $period )
- {
- return $this->addPeriod(-$n, $period );
- }
+ /**
+ * The stored timestamp is always UTC based.
+ * The returned timestamp via getTimestamp() will have the conversion applied
+ * @var int|null
+ */
+ protected $timestamp = null;
+
+ /**
+ * Timezone the current date object is set to.
+ * Timezone will only affect the returned timestamp via getTimestamp()
+ * @var string
+ */
+ protected $timezone = 'UTC';
+
+ const DATE_TIME_FORMAT = 'Y-m-d H:i:s';
+
+ /**
+ * Builds a Piwik_Date object
+ *
+ * @param int $timestamp
+ * @param string $timezone
+ * @throws Exception
+ */
+ protected function __construct($timestamp, $timezone = 'UTC')
+ {
+ if (!is_int($timestamp)) {
+ throw new Exception("Piwik_Date is expecting a unix timestamp");
+ }
+ $this->timezone = $timezone;
+ $this->timestamp = $timestamp;
+ }
+
+ /**
+ * Returns a Piwik_Date objects.
+ *
+ * @param string|self $dateString 'today' 'yesterday' or any YYYY-MM-DD or timestamp
+ * @param string $timezone if specified, the dateString will be relative to this $timezone.
+ * For example, today in UTC+12 will be a timestamp in the future for UTC.
+ * This is different from using ->setTimezone()
+ * @throws Exception
+ * @return Piwik_Date
+ */
+ static public function factory($dateString, $timezone = null)
+ {
+ $invalidDateException = new Exception(Piwik_TranslateException('General_ExceptionInvalidDateFormat', array("YYYY-MM-DD, or 'today' or 'yesterday'", "strtotime", "http://php.net/strtotime")) . ": $dateString");
+ if ($dateString instanceof self) {
+ $dateString = $dateString->toString();
+ }
+ if ($dateString == 'now') {
+ $date = self::now();
+ } elseif ($dateString == 'today') {
+ $date = self::today();
+ } elseif ($dateString == 'yesterday') {
+ $date = self::yesterday();
+ } elseif ($dateString == 'yesterdaySameTime') {
+ $date = self::yesterdaySameTime();
+ } elseif (!is_int($dateString)
+ && (
+ // strtotime returns the timestamp for April 1st for a date like 2011-04-01,today
+ // but we don't want this, as this is a date range and supposed to throw the exception
+ strpos($dateString, ',') !== false
+ ||
+ ($dateString = strtotime($dateString)) === false
+ )
+ ) {
+ throw $invalidDateException;
+ } else {
+ $date = new Piwik_Date($dateString);
+ }
+ $timestamp = $date->getTimestamp();
+ // can't be doing web analytics before the 1st website
+ // Tue, 06 Aug 1991 00:00:00 GMT
+ if ($timestamp < 681436800) {
+ throw $invalidDateException;
+ }
+ if (empty($timezone)) {
+ return $date;
+ }
+
+ $timestamp = self::adjustForTimezone($timestamp, $timezone);
+ return Piwik_Date::factory($timestamp);
+ }
+
+ /**
+ * Returns the datetime of the current timestamp
+ *
+ * @return string
+ */
+ function getDatetime()
+ {
+ return $this->toString(self::DATE_TIME_FORMAT);
+ }
+
+ /**
+ * Returns the datetime start in UTC
+ *
+ * @return string
+ */
+ function getDateStartUTC()
+ {
+ $dateStartUTC = gmdate('Y-m-d', $this->timestamp);
+ $date = Piwik_Date::factory($dateStartUTC)->setTimezone($this->timezone);
+ return $date->toString(self::DATE_TIME_FORMAT);
+ }
+
+ /**
+ * Returns the datetime end in UTC
+ *
+ * @return string
+ */
+ function getDateEndUTC()
+ {
+ $dateEndUTC = gmdate('Y-m-d 23:59:59', $this->timestamp);
+ $date = Piwik_Date::factory($dateEndUTC)->setTimezone($this->timezone);
+ return $date->toString(self::DATE_TIME_FORMAT);
+ }
+
+ /**
+ * Returns a new date object, copy of $this, with the timezone set
+ * This timezone is used to offset the UTC timestamp returned by @see getTimestamp()
+ * Doesn't modify $this
+ *
+ * @param string $timezone 'UTC', 'Europe/London', ...
+ * @return Piwik_Date
+ */
+ public function setTimezone($timezone)
+ {
+ return new Piwik_Date($this->timestamp, $timezone);
+ }
+
+ /**
+ * Helper function that returns the offset in the timezone string 'UTC+14'
+ * Returns false if the timezone is not UTC+X or UTC-X
+ *
+ * @param string $timezone
+ * @return int|bool utc offset or false
+ */
+ static protected function extractUtcOffset($timezone)
+ {
+ if ($timezone == 'UTC') {
+ return 0;
+ }
+ $start = substr($timezone, 0, 4);
+ if ($start != 'UTC-'
+ && $start != 'UTC+'
+ ) {
+ return false;
+ }
+ $offset = (float)substr($timezone, 4);
+ if ($start == 'UTC-') {
+ $offset = -$offset;
+ }
+ return $offset;
+ }
+
+ /**
+ * Adjusts a UNIX timestamp in UTC to a specific timezone.
+ *
+ * @param int $timestamp The UNIX timestamp to adjust.
+ * @param string $timezone The timezone to adjust to.
+ * @return int The adjusted time as seconds from EPOCH.
+ */
+ static public function adjustForTimezone($timestamp, $timezone)
+ {
+ // manually adjust for UTC timezones
+ $utcOffset = self::extractUtcOffset($timezone);
+ if ($utcOffset !== false) {
+ return self::addHourTo($timestamp, $utcOffset);
+ }
+
+ date_default_timezone_set($timezone);
+ $datetime = date(self::DATE_TIME_FORMAT, $timestamp);
+ date_default_timezone_set('UTC');
+
+ return strtotime($datetime);
+ }
+
+ /**
+ * Returns the Unix timestamp of the date in UTC
+ *
+ * @return int
+ */
+ public function getTimestampUTC()
+ {
+ return $this->timestamp;
+ }
+
+ /**
+ * Returns the unix timestamp of the date in UTC,
+ * converted from the date timezone
+ *
+ * @return int
+ */
+ public function getTimestamp()
+ {
+ if (empty($this->timezone)) {
+ $this->timezone = 'UTC';
+ }
+ $utcOffset = self::extractUtcOffset($this->timezone);
+ if ($utcOffset !== false) {
+ return (int)($this->timestamp - $utcOffset * 3600);
+ }
+ // The following code seems clunky - I thought the DateTime php class would allow to return timestamps
+ // after applying the timezone offset. Instead, the underlying timestamp is not changed.
+ // I decided to get the date without the timezone information, and create the timestamp from the truncated string.
+ // 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);
+ date_timezone_set($dtime, $dtzone);
+ $dateWithTimezone = date_format($dtime, 'r');
+ $dateWithoutTimezone = substr($dateWithTimezone, 0, -6);
+ $timestamp = strtotime($dateWithoutTimezone);
+ date_default_timezone_set('UTC');
+
+ return (int)$timestamp;
+ }
+
+ /**
+ * Returns true if the current date is older than the given $date
+ *
+ * @param Piwik_Date $date
+ * @return bool
+ */
+ public function isLater(Piwik_Date $date)
+ {
+ return $this->getTimestamp() > $date->getTimestamp();
+ }
+
+ /**
+ * Returns true if the current date is earlier than the given $date
+ *
+ * @param Piwik_Date $date
+ * @return bool
+ */
+ public function isEarlier(Piwik_Date $date)
+ {
+ return $this->getTimestamp() < $date->getTimestamp();
+ }
+
+ /**
+ * Returns the Y-m-d representation of the string.
+ * You can specify the output, see the list on php.net/date
+ *
+ * @param string $part
+ * @return string
+ */
+ public function toString($part = 'Y-m-d')
+ {
+ return date($part, $this->getTimestamp());
+ }
+
+ /**
+ * @see toString()
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * Compares the week of the current date against the given $date
+ * Returns 0 if equal, -1 if current week is earlier or 1 if current week is later
+ * Example: 09.Jan.2007 13:07:25 -> compareWeek(2); -> 0
+ *
+ * @param Piwik_Date $date
+ * @return int 0 = equal, 1 = later, -1 = earlier
+ */
+ public function compareWeek(Piwik_Date $date)
+ {
+ $currentWeek = date('W', $this->getTimestamp());
+ $toCompareWeek = date('W', $date->getTimestamp());
+ if ($currentWeek == $toCompareWeek) {
+ return 0;
+ }
+ if ($currentWeek < $toCompareWeek) {
+ return -1;
+ }
+ return 1;
+ }
+
+ /**
+ * Compares the month of the current date against the given $date month
+ * Returns 0 if equal, -1 if current month is earlier or 1 if current month is later
+ * For example: 10.03.2000 -> 15.03.1950 -> 0
+ *
+ * @param Piwik_Date $date Month to compare
+ * @return int 0 = equal, 1 = later, -1 = earlier
+ */
+ function compareMonth(Piwik_Date $date)
+ {
+ $currentMonth = date('n', $this->getTimestamp());
+ $toCompareMonth = date('n', $date->getTimestamp());
+ if ($currentMonth == $toCompareMonth) {
+ return 0;
+ }
+ if ($currentMonth < $toCompareMonth) {
+ return -1;
+ }
+ return 1;
+ }
+
+ /**
+ * Returns true if current date is today
+ *
+ * @return bool
+ */
+ public function isToday()
+ {
+ return $this->toString('Y-m-d') === Piwik_Date::factory('today', $this->timezone)->toString('Y-m-d');
+ }
+
+ /**
+ * Returns a date object set to now (same as today, except that the time is also set)
+ *
+ * @return Piwik_Date
+ */
+ static public function now()
+ {
+ return new Piwik_Date(time());
+ }
+
+ /**
+ * Returns a date object set to today midnight
+ *
+ * @return Piwik_Date
+ */
+ static public function today()
+ {
+ return new Piwik_Date(strtotime(date("Y-m-d 00:00:00")));
+ }
+
+ /**
+ * Returns a date object set to yesterday midnight
+ *
+ * @return Piwik_Date
+ */
+ static public function yesterday()
+ {
+ return new Piwik_Date(strtotime("yesterday"));
+ }
+
+ /**
+ * Returns a date object set to yesterday same time of day
+ *
+ * @return Piwik_Date
+ */
+ static public function yesterdaySameTime()
+ {
+ return new Piwik_Date(strtotime("yesterday " . date('H:i:s')));
+ }
+
+ /**
+ * Sets the time part of the date
+ * Doesn't modify $this
+ *
+ * @param string $time HH:MM:SS
+ * @return Piwik_Date The new date with the time part set
+ */
+ public function setTime($time)
+ {
+ return new Piwik_Date(strtotime(date("Y-m-d", $this->timestamp) . " $time"), $this->timezone);
+ }
+
+ /**
+ * Sets a new day
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $day Day eg. 31
+ * @return Piwik_Date new date
+ */
+ public function setDay($day)
+ {
+ $ts = $this->timestamp;
+ $result = mktime(
+ date('H', $ts),
+ date('i', $ts),
+ date('s', $ts),
+ date('n', $ts),
+ $day,
+ date('Y', $ts)
+ );
+ return new Piwik_Date($result, $this->timezone);
+ }
+
+ /**
+ * Sets a new year
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $year 2010
+ * @return Piwik_Date new date
+ */
+ public function setYear($year)
+ {
+ $ts = $this->timestamp;
+ $result = mktime(
+ date('H', $ts),
+ date('i', $ts),
+ date('s', $ts),
+ date('n', $ts),
+ date('j', $ts),
+ $year
+ );
+ return new Piwik_Date($result, $this->timezone);
+ }
+
+ /**
+ * Subtracts days from the existing date object and returns a new Piwik_Date object
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $n
+ * @return Piwik_Date new date
+ */
+ public function subDay($n)
+ {
+ if ($n === 0) {
+ return clone $this;
+ }
+ $ts = strtotime("-$n day", $this->timestamp);
+ return new Piwik_Date($ts, $this->timezone);
+ }
+
+ /**
+ * Subtracts weeks from the existing date object and returns a new Piwik_Date object
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $n
+ * @return Piwik_Date new date
+ */
+ public function subWeek($n)
+ {
+ return $this->subDay(7 * $n);
+ }
+
+ /**
+ * Subtracts a month from the existing date object.
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $n
+ * @return Piwik_Date new date
+ */
+ public function subMonth($n)
+ {
+ if ($n === 0) {
+ return clone $this;
+ }
+ $ts = $this->timestamp;
+ $result = mktime(
+ date('H', $ts),
+ date('i', $ts),
+ date('s', $ts),
+ date('n', $ts) - $n,
+ 1, // we set the day to 1
+ date('Y', $ts)
+ );
+ return new Piwik_Date($result, $this->timezone);
+ }
+
+ /**
+ * Subtracts a year from the existing date object.
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $n
+ * @return Piwik_Date new date
+ */
+ public function subYear($n)
+ {
+ if ($n === 0) {
+ return clone $this;
+ }
+ $ts = $this->timestamp;
+ $result = mktime(
+ date('H', $ts),
+ date('i', $ts),
+ date('s', $ts),
+ 1, // we set the month to 1
+ 1, // we set the day to 1
+ date('Y', $ts) - $n
+ );
+ return new Piwik_Date($result, $this->timezone);
+ }
+
+ /**
+ * Returns a localized date string, given a template.
+ * Allowed tags are: %day%, %shortDay%, %longDay%, etc.
+ *
+ * @param string $template string eg. %shortMonth% %longYear%
+ * @return string eg. "Aug 2009"
+ */
+ public function getLocalized($template)
+ {
+ $day = $this->toString('j');
+ $dayOfWeek = $this->toString('N');
+ $monthOfYear = $this->toString('n');
+ $patternToValue = array(
+ "%day%" => $day,
+ "%shortMonth%" => Piwik_Translate('General_ShortMonth_' . $monthOfYear),
+ "%longMonth%" => Piwik_Translate('General_LongMonth_' . $monthOfYear),
+ "%shortDay%" => Piwik_Translate('General_ShortDay_' . $dayOfWeek),
+ "%longDay%" => Piwik_Translate('General_LongDay_' . $dayOfWeek),
+ "%longYear%" => $this->toString('Y'),
+ "%shortYear%" => $this->toString('y'),
+ "%time%" => $this->toString('H:i:s')
+ );
+ $out = str_replace(array_keys($patternToValue), array_values($patternToValue), $template);
+ return $out;
+ }
+
+ /**
+ * Adds days to the existing date object.
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $n Number of days to add
+ * @return Piwik_Date new date
+ */
+ public function addDay($n)
+ {
+ $ts = strtotime("+$n day", $this->timestamp);
+ return new Piwik_Date($ts, $this->timezone);
+ }
+
+ /**
+ * Adds hours to the existing date object.
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $n Number of hours to add
+ * @return Piwik_Date new date
+ */
+ public function addHour($n)
+ {
+ $ts = self::addHourTo($this->timestamp, $n);
+ return new Piwik_Date($ts, $this->timezone);
+ }
+
+ /**
+ * Adds N number of hours to a UNIX timestamp and returns the result.
+ *
+ * @param int $timestamp The timestamp to add to.
+ * @param number $n Number of hours to add.
+ * @return int The result as a UNIX timestamp.
+ */
+ public static function addHourTo($timestamp, $n)
+ {
+ $isNegative = ($n < 0);
+ $minutes = 0;
+ if ($n != round($n)) {
+ if ($n >= 1 || $n <= -1) {
+ $extraMinutes = floor(abs($n));
+ if ($isNegative) {
+ $extraMinutes = -$extraMinutes;
+ }
+ $minutes = abs($n - $extraMinutes) * 60;
+ if ($isNegative) {
+ $minutes *= -1;
+ }
+ } else {
+ $minutes = $n * 60;
+ }
+ $n = floor(abs($n));
+ if ($isNegative) {
+ $n *= -1;
+ }
+ }
+ return (int)($timestamp + round($minutes * 60) + $n * 3600);
+ }
+
+ /**
+ * Substract hour to the existing date object.
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $n Number of hours to substract
+ * @return Piwik_Date new date
+ */
+ public function subHour($n)
+ {
+ return $this->addHour(-$n);
+ }
+
+ /**
+ * Adds period to the existing date object.
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $n
+ * @param string $period period to add (WEEK, DAY,...)
+ * @return Piwik_Date new date
+ */
+ public function addPeriod($n, $period)
+ {
+ if ($n < 0) {
+ $ts = strtotime("$n $period", $this->timestamp);
+ } else {
+ $ts = strtotime("+$n $period", $this->timestamp);
+ }
+ return new Piwik_Date($ts, $this->timezone);
+ }
+
+ /**
+ * Subtracts period from the existing date object.
+ * Returned is the new date object
+ * Doesn't modify $this
+ *
+ * @param int $n
+ * @param string $period period to sub
+ * @return Piwik_Date new date
+ */
+ public function subPeriod($n, $period)
+ {
+ return $this->addPeriod(-$n, $period);
+ }
}
diff --git a/core/Db/Adapter.php b/core/Db/Adapter.php
index 9e616c52fc..de3412e9a6 100644
--- a/core/Db/Adapter.php
+++ b/core/Db/Adapter.php
@@ -15,95 +15,89 @@
*/
class Piwik_Db_Adapter
{
- /**
- * Create adapter
- *
- * @param string $adapterName database adapter name
- * @param array $dbInfos database connection info
- * @param bool $connect
- * @return Piwik_Db_Adapter_Interface
- */
- public static function factory($adapterName, & $dbInfos, $connect = true)
- {
- if($connect)
- {
- if($dbInfos['port'][0] == '/')
- {
- $dbInfos['unix_socket'] = $dbInfos['port'];
- unset($dbInfos['host']);
- unset($dbInfos['port']);
- }
+ /**
+ * Create adapter
+ *
+ * @param string $adapterName database adapter name
+ * @param array $dbInfos database connection info
+ * @param bool $connect
+ * @return Piwik_Db_Adapter_Interface
+ */
+ public static function factory($adapterName, & $dbInfos, $connect = true)
+ {
+ if ($connect) {
+ if ($dbInfos['port'][0] == '/') {
+ $dbInfos['unix_socket'] = $dbInfos['port'];
+ unset($dbInfos['host']);
+ unset($dbInfos['port']);
+ }
- // not used by Zend Framework
- unset($dbInfos['tables_prefix']);
- unset($dbInfos['adapter']);
- unset($dbInfos['schema']);
- }
+ // not used by Zend Framework
+ unset($dbInfos['tables_prefix']);
+ unset($dbInfos['adapter']);
+ unset($dbInfos['schema']);
+ }
- $className = self::getAdapterClassName($adapterName);
- Piwik_Loader::loadClass($className);
+ $className = self::getAdapterClassName($adapterName);
+ Piwik_Loader::loadClass($className);
- /*
- * 5.2.1 fixes various bugs with references that caused PDO_MYSQL getConnection()
- * to clobber $dbInfos. (#33282, #35106, #39944)
- */
- if (version_compare(PHP_VERSION, '5.2.1') < 0)
- {
- $adapter = new $className(array_map('trim', $dbInfos));
- }
- else
- {
- $adapter = new $className($dbInfos);
- }
+ /*
+ * 5.2.1 fixes various bugs with references that caused PDO_MYSQL getConnection()
+ * to clobber $dbInfos. (#33282, #35106, #39944)
+ */
+ if (version_compare(PHP_VERSION, '5.2.1') < 0) {
+ $adapter = new $className(array_map('trim', $dbInfos));
+ } else {
+ $adapter = new $className($dbInfos);
+ }
- if($connect)
- {
- $adapter->getConnection();
+ if ($connect) {
+ $adapter->getConnection();
- Zend_Db_Table::setDefaultAdapter($adapter);
- // we don't want the connection information to appear in the logs
- $adapter->resetConfig();
- }
+ Zend_Db_Table::setDefaultAdapter($adapter);
+ // we don't want the connection information to appear in the logs
+ $adapter->resetConfig();
+ }
- return $adapter;
- }
+ return $adapter;
+ }
- /**
- * Get adapter class name
- *
- * @param string $adapterName
- * @return string
- */
- private static function getAdapterClassName($adapterName)
- {
- return 'Piwik_Db_Adapter_' . str_replace(' ', '_', ucwords(str_replace('_', ' ', strtolower($adapterName))));
- }
+ /**
+ * Get adapter class name
+ *
+ * @param string $adapterName
+ * @return string
+ */
+ private static function getAdapterClassName($adapterName)
+ {
+ return 'Piwik_Db_Adapter_' . str_replace(' ', '_', ucwords(str_replace('_', ' ', strtolower($adapterName))));
+ }
- /**
- * Get default port for named adapter
- *
- * @param string $adapterName
- * @return int
- */
- public static function getDefaultPortForAdapter($adapterName)
- {
- $className = self::getAdapterClassName($adapterName);
- return call_user_func(array($className, 'getDefaultPort'));
- }
+ /**
+ * Get default port for named adapter
+ *
+ * @param string $adapterName
+ * @return int
+ */
+ public static function getDefaultPortForAdapter($adapterName)
+ {
+ $className = self::getAdapterClassName($adapterName);
+ return call_user_func(array($className, 'getDefaultPort'));
+ }
- /**
- * Get list of adapters
- *
- * @return array
- */
- public static function getAdapters()
- {
- static $adapterNames = array(
- // currently supported by Piwik
- 'Pdo_Mysql',
- 'Mysqli',
+ /**
+ * Get list of adapters
+ *
+ * @return array
+ */
+ public static function getAdapters()
+ {
+ static $adapterNames = array(
+ // currently supported by Piwik
+ 'Pdo_Mysql',
+ 'Mysqli',
- // other adapters supported by Zend_Db
+ // other adapters supported by Zend_Db
// 'Pdo_Pgsql',
// 'Pdo_Mssql',
// 'Sqlsrv',
@@ -111,19 +105,17 @@ class Piwik_Db_Adapter
// 'Db2',
// 'Pdo_Oci',
// 'Oracle',
- );
+ );
- $adapters = array();
+ $adapters = array();
- foreach($adapterNames as $adapterName)
- {
- $className = 'Piwik_Db_Adapter_'.$adapterName;
- if(call_user_func(array($className, 'isEnabled')))
- {
- $adapters[strtoupper($adapterName)] = call_user_func(array($className, 'getDefaultPort'));
- }
- }
+ foreach ($adapterNames as $adapterName) {
+ $className = 'Piwik_Db_Adapter_' . $adapterName;
+ if (call_user_func(array($className, 'isEnabled'))) {
+ $adapters[strtoupper($adapterName)] = call_user_func(array($className, 'getDefaultPort'));
+ }
+ }
- return $adapters;
- }
+ return $adapters;
+ }
}
diff --git a/core/Db/Adapter/Interface.php b/core/Db/Adapter/Interface.php
index db0762161a..bd81c0834d 100644
--- a/core/Db/Adapter/Interface.php
+++ b/core/Db/Adapter/Interface.php
@@ -15,59 +15,59 @@
*/
interface Piwik_Db_Adapter_Interface
{
- /**
- * Reset the configuration variables in this adapter.
- */
- public function resetConfig();
+ /**
+ * Reset the configuration variables in this adapter.
+ */
+ public function resetConfig();
- /**
- * Return default port.
- *
- * @return int
- */
- public static function getDefaultPort();
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort();
- /**
- * Check database server version
- *
- * @throws Exception if database version is less than required version
- */
- public function checkServerVersion();
+ /**
+ * Check database server version
+ *
+ * @throws Exception if database version is less than required version
+ */
+ public function checkServerVersion();
- /**
- * Returns true if this adapter's required extensions are enabled
- *
- * @return bool
- */
- public static function isEnabled();
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled();
- /**
- * Returns true if this adapter supports blobs as fields
- *
- * @return bool
- */
- public function hasBlobDataType();
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType();
- /**
- * Returns true if this adapter supports bulk loading
- *
- * @return bool
- */
- public function hasBulkLoader();
+ /**
+ * Returns true if this adapter supports bulk loading
+ *
+ * @return bool
+ */
+ public function hasBulkLoader();
- /**
- * Test error number
- *
- * @param Exception $e
- * @param string $errno
- * @return bool
- */
- public function isErrNo($e, $errno);
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno);
- /**
- * Is the connection character set equal to utf8?
- *
- * @return bool
- */
- public function isConnectionUTF8();
+ /**
+ * Is the connection character set equal to utf8?
+ *
+ * @return bool
+ */
+ public function isConnectionUTF8();
}
diff --git a/core/Db/Adapter/Mysqli.php b/core/Db/Adapter/Mysqli.php
index db38734f81..efb309afa5 100644
--- a/core/Db/Adapter/Mysqli.php
+++ b/core/Db/Adapter/Mysqli.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,163 +15,159 @@
*/
class Piwik_Db_Adapter_Mysqli extends Zend_Db_Adapter_Mysqli implements Piwik_Db_Adapter_Interface
{
- /**
- * Constructor
- *
- * @param array|Zend_Config $config database configuration
- */
- public function __construct($config)
- {
- // Enable LOAD DATA INFILE
- $config['driver_options'][MYSQLI_OPT_LOCAL_INFILE] = true;
- parent::__construct($config);
- }
+ /**
+ * Constructor
+ *
+ * @param array|Zend_Config $config database configuration
+ */
+ public function __construct($config)
+ {
+ // Enable LOAD DATA INFILE
+ $config['driver_options'][MYSQLI_OPT_LOCAL_INFILE] = true;
+ parent::__construct($config);
+ }
- /**
- * Reset the configuration variables in this adapter.
- */
- public function resetConfig()
- {
- $this->_config = array();
- }
+ /**
+ * Reset the configuration variables in this adapter.
+ */
+ public function resetConfig()
+ {
+ $this->_config = array();
+ }
- /**
- * Return default port.
- *
- * @return int
- */
- public static function getDefaultPort()
- {
- return 3306;
- }
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 3306;
+ }
- /**
- * Check MySQL version
- *
- * @throws Exception
- */
- public function checkServerVersion()
- {
- $serverVersion = $this->getServerVersion();
- $requiredVersion = Piwik_Config::getInstance()->General['minimum_mysql_version'];
- if(version_compare($serverVersion, $requiredVersion) === -1)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
- }
- }
+ /**
+ * Check MySQL version
+ *
+ * @throws Exception
+ */
+ public function checkServerVersion()
+ {
+ $serverVersion = $this->getServerVersion();
+ $requiredVersion = Piwik_Config::getInstance()->General['minimum_mysql_version'];
+ if (version_compare($serverVersion, $requiredVersion) === -1) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
+ }
+ }
- /**
- * Check client version compatibility against database server
- *
- * @throws Exception
- */
- public function checkClientVersion()
- {
- $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)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionIncompatibleClientServerVersions', array('MySQL', $clientVersion, $serverVersion)));
- }
- }
+ /**
+ * Check client version compatibility against database server
+ *
+ * @throws Exception
+ */
+ public function checkClientVersion()
+ {
+ $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
+ ) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionIncompatibleClientServerVersions', array('MySQL', $clientVersion, $serverVersion)));
+ }
+ }
- /**
- * Returns true if this adapter's required extensions are enabled
- *
- * @return bool
- */
- public static function isEnabled()
- {
- $extensions = @get_loaded_extensions();
- return in_array('mysqli', $extensions);
- }
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ $extensions = @get_loaded_extensions();
+ return in_array('mysqli', $extensions);
+ }
- /**
- * Returns true if this adapter supports blobs as fields
- *
- * @return bool
- */
- public function hasBlobDataType()
- {
- return true;
- }
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ return true;
+ }
- /**
- * Returns true if this adapter supports bulk loading
- *
- * @return bool
- */
- public function hasBulkLoader()
- {
- return true;
- }
+ /**
+ * Returns true if this adapter supports bulk loading
+ *
+ * @return bool
+ */
+ public function hasBulkLoader()
+ {
+ return true;
+ }
- /**
- * Test error number
- *
- * @param Exception $e
- * @param string $errno
- * @return bool
- */
- public function isErrNo($e, $errno)
- {
- if(is_null($this->_connection))
- {
- if(preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match))
- {
- return $match[1] == $errno;
- }
- return mysqli_connect_errno() == $errno;
- }
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ if (is_null($this->_connection)) {
+ if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) {
+ return $match[1] == $errno;
+ }
+ return mysqli_connect_errno() == $errno;
+ }
- return mysqli_errno($this->_connection) == $errno;
- }
+ return mysqli_errno($this->_connection) == $errno;
+ }
- /**
- * Execute unprepared SQL query and throw away the result
- *
- * Workaround some SQL statements not compatible with prepare().
- * See http://framework.zend.com/issues/browse/ZF-1398
- *
- * @param string $sqlQuery
- * @return int Number of rows affected (SELECT/INSERT/UPDATE/DELETE)
- */
- public function exec( $sqlQuery )
- {
- $rc = mysqli_query($this->_connection, $sqlQuery);
- $rowsAffected = mysqli_affected_rows($this->_connection);
- if(!is_bool($rc))
- {
- mysqli_free_result($rc);
- }
- return $rowsAffected;
- }
+ /**
+ * Execute unprepared SQL query and throw away the result
+ *
+ * Workaround some SQL statements not compatible with prepare().
+ * See http://framework.zend.com/issues/browse/ZF-1398
+ *
+ * @param string $sqlQuery
+ * @return int Number of rows affected (SELECT/INSERT/UPDATE/DELETE)
+ */
+ public function exec($sqlQuery)
+ {
+ $rc = mysqli_query($this->_connection, $sqlQuery);
+ $rowsAffected = mysqli_affected_rows($this->_connection);
+ if (!is_bool($rc)) {
+ mysqli_free_result($rc);
+ }
+ return $rowsAffected;
+ }
- /**
- * Is the connection character set equal to utf8?
- *
- * @return bool
- */
- public function isConnectionUTF8()
- {
- $charset = mysqli_character_set_name($this->_connection);
- return $charset === 'utf8';
- }
+ /**
+ * Is the connection character set equal to utf8?
+ *
+ * @return bool
+ */
+ public function isConnectionUTF8()
+ {
+ $charset = mysqli_character_set_name($this->_connection);
+ return $charset === 'utf8';
+ }
- /**
- * Get client version
- *
- * @return string
- */
- public function getClientVersion()
- {
- $this->_connect();
- $version = $this->_connection->server_version;
- $major = (int) ($version / 10000);
- $minor = (int) ($version % 10000 / 100);
- $revision = (int) ($version % 100);
- return $major . '.' . $minor . '.' . $revision;
- }
+ /**
+ * Get client version
+ *
+ * @return string
+ */
+ public function getClientVersion()
+ {
+ $this->_connect();
+ $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 56fe7691ac..6754962e6d 100644
--- a/core/Db/Adapter/Pdo/Mssql.php
+++ b/core/Db/Adapter/Pdo/Mssql.php
@@ -15,264 +15,245 @@
*/
class Piwik_Db_Pdo_Mssql extends Zend_Db_Adapter_Pdo_Mssql implements Piwik_Db_Adapter_Interface
{
- /**
- * Returns connection handle
- *
- * @throws Zend_Db_Adapter_Exception
- * @return resource
- */
- public function getConnection()
- {
- // if we already have a PDO object, no need to re-connect.
- if ($this->_connection)
- {
- return $this->_connection;
- }
+ /**
+ * Returns connection handle
+ *
+ * @throws Zend_Db_Adapter_Exception
+ * @return resource
+ */
+ public function getConnection()
+ {
+ // if we already have a PDO object, no need to re-connect.
+ if ($this->_connection) {
+ return $this->_connection;
+ }
- $this->_pdoType = "sqlsrv";
- // get the dsn first, because some adapters alter the $_pdoType
- //$dsn = $this->_dsn();
+ $this->_pdoType = "sqlsrv";
+ // get the dsn first, because some adapters alter the $_pdoType
+ //$dsn = $this->_dsn();
- // check for PDO extension
- if (!extension_loaded('pdo'))
- {
- /**
- * @see Zend_Db_Adapter_Exception
- */
- throw new Zend_Db_Adapter_Exception('The PDO extension is required for this adapter but the extension is not loaded');
- }
+ // check for PDO extension
+ if (!extension_loaded('pdo')) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ throw new Zend_Db_Adapter_Exception('The PDO extension is required for this adapter but the extension is not loaded');
+ }
- // check the PDO driver is available
- if (!in_array($this->_pdoType, PDO::getAvailableDrivers()))
- {
- /**
- * @see Zend_Db_Adapter_Exception
- */
- throw new Zend_Db_Adapter_Exception('The ' . $this->_pdoType . ' driver is not currently installed');
- }
+ // check the PDO driver is available
+ if (!in_array($this->_pdoType, PDO::getAvailableDrivers())) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ throw new Zend_Db_Adapter_Exception('The ' . $this->_pdoType . ' driver is not currently installed');
+ }
- // create PDO connection
- $q = $this->_profiler->queryStart('connect', Zend_Db_Profiler::CONNECT);
+ // create PDO connection
+ $q = $this->_profiler->queryStart('connect', Zend_Db_Profiler::CONNECT);
- // add the persistence flag if we find it in our config array
- if (isset($this->_config['persistent']) && ($this->_config['persistent'] == true))
- {
- $this->_config['driver_options'][PDO::ATTR_PERSISTENT] = true;
- }
+ // add the persistence flag if we find it in our config array
+ if (isset($this->_config['persistent']) && ($this->_config['persistent'] == true)) {
+ $this->_config['driver_options'][PDO::ATTR_PERSISTENT] = true;
+ }
- try {
- $serverName = $this->_config["host"];
- $database = $this->_config["dbname"];
- if(is_null($database))
- {
- $database = 'master';
- }
- $uid = $this->_config['username'];
- $pwd = $this->_config['password'];
- if($this->_config["port"] != "")
- {
- $serverName = $serverName.",".$this->_config["port"];
- }
+ try {
+ $serverName = $this->_config["host"];
+ $database = $this->_config["dbname"];
+ if (is_null($database)) {
+ $database = 'master';
+ }
+ $uid = $this->_config['username'];
+ $pwd = $this->_config['password'];
+ if ($this->_config["port"] != "") {
+ $serverName = $serverName . "," . $this->_config["port"];
+ }
- $this->_connection = new PDO( "sqlsrv:$serverName", $uid, $pwd, array( 'Database' => $database ));
+ $this->_connection = new PDO("sqlsrv:$serverName", $uid, $pwd, array('Database' => $database));
- if( $this->_connection === false )
- {
- die( self::FormatErrors( sqlsrv_errors() ) );
- }
+ if ($this->_connection === false) {
+ die(self::FormatErrors(sqlsrv_errors()));
+ }
- /*
- $this->_connection = new PDO(
- $dsn,
- $this->_config['username'],
- $this->_config['password'],
- $this->_config['driver_options']
- );
- */
+ /*
+ $this->_connection = new PDO(
+ $dsn,
+ $this->_config['username'],
+ $this->_config['password'],
+ $this->_config['driver_options']
+ );
+ */
- $this->_profiler->queryEnd($q);
+ $this->_profiler->queryEnd($q);
- // set the PDO connection to perform case-folding on array keys, or not
- $this->_connection->setAttribute(PDO::ATTR_CASE, $this->_caseFolding);
- $this->_connection->setAttribute(PDO::SQLSRV_ENCODING_UTF8, true);
+ // set the PDO connection to perform case-folding on array keys, or not
+ $this->_connection->setAttribute(PDO::ATTR_CASE, $this->_caseFolding);
+ $this->_connection->setAttribute(PDO::SQLSRV_ENCODING_UTF8, true);
- // always use exceptions.
- $this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ // always use exceptions.
+ $this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
- return $this->_connection;
- } catch (PDOException $e) {
- /**
- * @see Zend_Db_Adapter_Exception
- */
- throw new Zend_Db_Adapter_Exception($e->getMessage(), $e->getCode(), $e);
- }
- }
+ return $this->_connection;
+ } catch (PDOException $e) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ throw new Zend_Db_Adapter_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
- /**
- * Reset the configuration variables in this adapter.
- */
- public function resetConfig()
- {
- $this->_config = array();
- }
+ /**
+ * Reset the configuration variables in this adapter.
+ */
+ public function resetConfig()
+ {
+ $this->_config = array();
+ }
- /**
- * Return default port.
- *
- * @return int
- */
- public static function getDefaultPort()
- {
- return 1433;
- }
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 1433;
+ }
- /**
- * Check MSSQL version
- *
- * @throws Exception
- */
- public function checkServerVersion()
- {
- $serverVersion = $this->getServerVersion();
- $requiredVersion = Piwik_Config::getInstance()->General['minimum_mssql_version'];
- if(version_compare($serverVersion, $requiredVersion) === -1)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MSSQL', $serverVersion, $requiredVersion)));
- }
+ /**
+ * Check MSSQL version
+ *
+ * @throws Exception
+ */
+ public function checkServerVersion()
+ {
+ $serverVersion = $this->getServerVersion();
+ $requiredVersion = Piwik_Config::getInstance()->General['minimum_mssql_version'];
+ if (version_compare($serverVersion, $requiredVersion) === -1) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MSSQL', $serverVersion, $requiredVersion)));
+ }
- }
+ }
- /**
- * Returns the Mssql server version
- *
- * @return null|string
- */
- public function getServerVersion()
- {
- try
- {
- $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];
- }
- }
- catch (PDOException $e)
- {
- }
+ /**
+ * Returns the Mssql server version
+ *
+ * @return null|string
+ */
+ public function getServerVersion()
+ {
+ try {
+ $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];
+ }
+ } catch (PDOException $e) {
+ }
- return null;
- }
+ return null;
+ }
- /**
- * Check client version compatibility against database server
- *
- * @throws Exception
- */
- public function checkClientVersion()
- {
- $serverVersion = $this->getServerVersion();
- $clientVersion = $this->getClientVersion();
- if(version_compare($serverVersion, '10') >= 0
- && version_compare($clientVersion, '10') < 0)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionIncompatibleClientServerVersions', array('MSSQL', $clientVersion, $serverVersion)));
- }
- }
+ /**
+ * Check client version compatibility against database server
+ *
+ * @throws Exception
+ */
+ public function checkClientVersion()
+ {
+ $serverVersion = $this->getServerVersion();
+ $clientVersion = $this->getClientVersion();
+ if (version_compare($serverVersion, '10') >= 0
+ && version_compare($clientVersion, '10') < 0
+ ) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionIncompatibleClientServerVersions', array('MSSQL', $clientVersion, $serverVersion)));
+ }
+ }
- /**
- * Returns true if this adapter's required extensions are enabled
- *
- * @return bool
- */
- public static function isEnabled()
- {
- $extensions = @get_loaded_extensions();
- return in_array('PDO', $extensions) && in_array('pdo_sqlsrv', $extensions);
- }
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ $extensions = @get_loaded_extensions();
+ return in_array('PDO', $extensions) && in_array('pdo_sqlsrv', $extensions);
+ }
- /**
- * Returns true if this adapter supports blobs as fields
- *
- * @return bool
- */
- public function hasBlobDataType()
- {
- return true;
- }
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ return true;
+ }
- /**
- * Returns true if this adapter supports bulk loading
- *
- * @return bool
- */
- public function hasBulkLoader()
- {
- /**
- * BULK INSERT doesn't have a way to escape a terminator that appears in a value
- *
- * @link http://msdn.microsoft.com/en-us/library/ms188365.aspx
- */
- return false;
- }
+ /**
+ * Returns true if this adapter supports bulk loading
+ *
+ * @return bool
+ */
+ public function hasBulkLoader()
+ {
+ /**
+ * BULK INSERT doesn't have a way to escape a terminator that appears in a value
+ *
+ * @link http://msdn.microsoft.com/en-us/library/ms188365.aspx
+ */
+ return false;
+ }
- /**
- * Test error number
- *
- * @param Exception $e
- * @param string $errno
- * @return bool
- */
- public function isErrNo($e, $errno)
- {
- if(preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match))
- {
- return $match[1] == $errno;
- }
- return false;
- }
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) {
+ return $match[1] == $errno;
+ }
+ return false;
+ }
- /**
- * Is the connection character set equal to utf8?
- *
- * @return bool
- */
- public function isConnectionUTF8()
- {
- //check the getconnection, it's specified on the connection string.
- return true;
- }
+ /**
+ * Is the connection character set equal to utf8?
+ *
+ * @return bool
+ */
+ public function isConnectionUTF8()
+ {
+ //check the getconnection, it's specified on the connection string.
+ return true;
+ }
- /**
- * Retrieve client version in PHP style
- *
- * @throws Exception
- * @return string
- */
- public function getClientVersion()
- {
- $this->_connect();
- try
- {
- $version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
- $requiredVersion = Piwik_Config::getInstance()->General['minimum_mssql_client_version'];
- if(version_compare($version['DriverVer'], $requiredVersion) === -1)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MSSQL', $serverVersion, $requiredVersion)));
- }
- else
- {
- return $version['DriverVer'];
- }
- }
- catch (PDOException $e)
- {
- // In case of the driver doesn't support getting attributes
- }
+ /**
+ * Retrieve client version in PHP style
+ *
+ * @throws Exception
+ * @return string
+ */
+ public function getClientVersion()
+ {
+ $this->_connect();
+ try {
+ $version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
+ $requiredVersion = Piwik_Config::getInstance()->General['minimum_mssql_client_version'];
+ if (version_compare($version['DriverVer'], $requiredVersion) === -1) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MSSQL', $serverVersion, $requiredVersion)));
+ } else {
+ return $version['DriverVer'];
+ }
+ } catch (PDOException $e) {
+ // In case of the driver doesn't support getting attributes
+ }
- return null;
- }
+ return null;
+ }
}
diff --git a/core/Db/Adapter/Pdo/Mysql.php b/core/Db/Adapter/Pdo/Mysql.php
index d8753016c1..515019cb4f 100644
--- a/core/Db/Adapter/Pdo/Mysql.php
+++ b/core/Db/Adapter/Pdo/Mysql.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,212 +15,206 @@
*/
class Piwik_Db_Adapter_Pdo_Mysql extends Zend_Db_Adapter_Pdo_Mysql implements Piwik_Db_Adapter_Interface
{
- /**
- * Constructor
- *
- * @param array|Zend_Config $config database configuration
- */
- public function __construct($config)
- {
- // Enable LOAD DATA INFILE
- if(defined('PDO::MYSQL_ATTR_LOCAL_INFILE'))
- {
- $config['driver_options'][PDO::MYSQL_ATTR_LOCAL_INFILE] = true;
- }
- parent::__construct($config);
- }
-
- /**
- * Returns connection handle
- *
- * @return resource
- */
- public function getConnection()
- {
- if($this->_connection)
- {
- return $this->_connection;
- }
-
- $this->_connect();
-
- /**
- * Before MySQL 5.1.17, server-side prepared statements
- * do not use the query cache.
- * @see http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html
- *
- * MySQL also does not support preparing certain DDL and SHOW
- * statements.
- * @see http://framework.zend.com/issues/browse/ZF-1398
- */
- $this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
- $this->_connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
-
- return $this->_connection;
- }
-
- /**
- * Reset the configuration variables in this adapter.
- */
- public function resetConfig()
- {
- $this->_config = array();
- }
-
- /**
- * Return default port.
- *
- * @return int
- */
- public static function getDefaultPort()
- {
- return 3306;
- }
-
- /**
- * Check MySQL version
- *
- * @throws Exception
- */
- public function checkServerVersion()
- {
- $serverVersion = $this->getServerVersion();
- $requiredVersion = Piwik_Config::getInstance()->General['minimum_mysql_version'];
- if(version_compare($serverVersion, $requiredVersion) === -1)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
- }
- }
-
- /**
- * Check client version compatibility against database server
- *
- * @throws Exception
- */
- public function checkClientVersion()
- {
- $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)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionIncompatibleClientServerVersions', array('MySQL', $clientVersion, $serverVersion)));
- }
- }
-
- /**
- * Returns true if this adapter's required extensions are enabled
- *
- * @return bool
- */
- public static function isEnabled()
- {
- $extensions = @get_loaded_extensions();
- return in_array('PDO', $extensions) && in_array('pdo_mysql', $extensions) && in_array('mysql', PDO::getAvailableDrivers());
- }
-
- /**
- * Returns true if this adapter supports blobs as fields
- *
- * @return bool
- */
- public function hasBlobDataType()
- {
- return true;
- }
-
- /**
- * Returns true if this adapter supports bulk loading
- *
- * @return bool
- */
- public function hasBulkLoader()
- {
- return true;
- }
-
- /**
- * Test error number
- *
- * @param Exception $e
- * @param string $errno
- * @return bool
- */
- public function isErrNo($e, $errno)
- {
- if(preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match))
- {
- return $match[1] == $errno;
- }
- return false;
- }
-
- /**
- * Is the connection character set equal to utf8?
- *
- * @return bool
- */
- 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';
- }
-
- /**
- * Retrieve client version in PHP style
- *
- * @return string
- */
- public function getClientVersion()
- {
- $this->_connect();
- try {
- $version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
- $matches = null;
- if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
- return $matches[1];
- }
- } catch (PDOException $e) {
- // In case of the driver doesn't support getting attributes
- }
- return null;
- }
-
- private $cachePreparedStatement = array();
-
- /**
- * Prepares and executes an SQL statement with bound data.
- * Caches prepared statements to avoid preparing the same query more than once
- *
- * @param string|Zend_Db_Select $sql The SQL statement with placeholders.
- * @param array $bind An array of data to bind to the placeholders.
- * @return Zend_Db_Statement_Interface
- */
- public function query($sql, $bind = array())
- {
- if(!is_string($sql))
- {
- return parent::query($sql, $bind);
- }
-
- if(isset($this->cachePreparedStatement[$sql]))
- {
- if (!is_array($bind)) {
- $bind = array($bind);
- }
-
- $stmt = $this->cachePreparedStatement[$sql];
- $stmt->execute($bind);
- return $stmt;
- }
-
- $stmt = parent::query($sql, $bind);
- $this->cachePreparedStatement[$sql] = $stmt;
- return $stmt;
- }
+ /**
+ * Constructor
+ *
+ * @param array|Zend_Config $config database configuration
+ */
+ public function __construct($config)
+ {
+ // Enable LOAD DATA INFILE
+ if (defined('PDO::MYSQL_ATTR_LOCAL_INFILE')) {
+ $config['driver_options'][PDO::MYSQL_ATTR_LOCAL_INFILE] = true;
+ }
+ parent::__construct($config);
+ }
+
+ /**
+ * Returns connection handle
+ *
+ * @return resource
+ */
+ public function getConnection()
+ {
+ if ($this->_connection) {
+ return $this->_connection;
+ }
+
+ $this->_connect();
+
+ /**
+ * Before MySQL 5.1.17, server-side prepared statements
+ * do not use the query cache.
+ * @see http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html
+ *
+ * MySQL also does not support preparing certain DDL and SHOW
+ * statements.
+ * @see http://framework.zend.com/issues/browse/ZF-1398
+ */
+ $this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
+ $this->_connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
+
+ return $this->_connection;
+ }
+
+ /**
+ * Reset the configuration variables in this adapter.
+ */
+ public function resetConfig()
+ {
+ $this->_config = array();
+ }
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 3306;
+ }
+
+ /**
+ * Check MySQL version
+ *
+ * @throws Exception
+ */
+ public function checkServerVersion()
+ {
+ $serverVersion = $this->getServerVersion();
+ $requiredVersion = Piwik_Config::getInstance()->General['minimum_mysql_version'];
+ if (version_compare($serverVersion, $requiredVersion) === -1) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
+ }
+ }
+
+ /**
+ * Check client version compatibility against database server
+ *
+ * @throws Exception
+ */
+ public function checkClientVersion()
+ {
+ $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
+ ) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionIncompatibleClientServerVersions', array('MySQL', $clientVersion, $serverVersion)));
+ }
+ }
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ $extensions = @get_loaded_extensions();
+ return in_array('PDO', $extensions) && in_array('pdo_mysql', $extensions) && in_array('mysql', PDO::getAvailableDrivers());
+ }
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ return true;
+ }
+
+ /**
+ * Returns true if this adapter supports bulk loading
+ *
+ * @return bool
+ */
+ public function hasBulkLoader()
+ {
+ return true;
+ }
+
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) {
+ return $match[1] == $errno;
+ }
+ return false;
+ }
+
+ /**
+ * Is the connection character set equal to utf8?
+ *
+ * @return bool
+ */
+ 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';
+ }
+
+ /**
+ * Retrieve client version in PHP style
+ *
+ * @return string
+ */
+ public function getClientVersion()
+ {
+ $this->_connect();
+ try {
+ $version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
+ $matches = null;
+ if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
+ return $matches[1];
+ }
+ } catch (PDOException $e) {
+ // In case of the driver doesn't support getting attributes
+ }
+ return null;
+ }
+
+ private $cachePreparedStatement = array();
+
+ /**
+ * Prepares and executes an SQL statement with bound data.
+ * Caches prepared statements to avoid preparing the same query more than once
+ *
+ * @param string|Zend_Db_Select $sql The SQL statement with placeholders.
+ * @param array $bind An array of data to bind to the placeholders.
+ * @return Zend_Db_Statement_Interface
+ */
+ public function query($sql, $bind = array())
+ {
+ if (!is_string($sql)) {
+ return parent::query($sql, $bind);
+ }
+
+ if (isset($this->cachePreparedStatement[$sql])) {
+ if (!is_array($bind)) {
+ $bind = array($bind);
+ }
+
+ $stmt = $this->cachePreparedStatement[$sql];
+ $stmt->execute($bind);
+ return $stmt;
+ }
+
+ $stmt = parent::query($sql, $bind);
+ $this->cachePreparedStatement[$sql] = $stmt;
+ return $stmt;
+ }
}
diff --git a/core/Db/Adapter/Pdo/Pgsql.php b/core/Db/Adapter/Pdo/Pgsql.php
index 035a508892..c6b07bedf3 100644
--- a/core/Db/Adapter/Pdo/Pgsql.php
+++ b/core/Db/Adapter/Pdo/Pgsql.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,165 +15,163 @@
*/
class Piwik_Db_Adapter_Pdo_Pgsql extends Zend_Db_Adapter_Pdo_Pgsql implements Piwik_Db_Adapter_Interface
{
- /**
- * Reset the configuration variables in this adapter.
- */
- public function resetConfig()
- {
- $this->_config = array();
- }
-
- /**
- * Return default port.
- *
- * @return int
- */
- public static function getDefaultPort()
- {
- return 5432;
- }
-
- /**
- * Check PostgreSQL version
- *
- * @throws Exception
- */
- public function checkServerVersion()
- {
- $databaseVersion = $this->getServerVersion();
- $requiredVersion = Piwik_Config::getInstance()->General['minimum_pgsql_version'];
- if(version_compare($databaseVersion, $requiredVersion) === -1)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('PostgreSQL', $databaseVersion, $requiredVersion)));
- }
- }
-
- /**
- * Check client version compatibility against database server
- */
- public function checkClientVersion()
- {
- }
-
- /**
- * Returns true if this adapter's required extensions are enabled
- *
- * @return bool
- */
- public static function isEnabled()
- {
- $extensions = @get_loaded_extensions();
- return in_array('PDO', $extensions) && in_array('pdo_pgsql', $extensions);
- }
-
- /**
- * Returns true if this adapter supports blobs as fields
- *
- * @return bool
- */
- public function hasBlobDataType()
- {
- // large objects must be loaded from a file using a non-SQL API
- // and then referenced by the object ID (oid);
- // the alternative, bytea fields, incur a space and time
- // penalty for encoding/decoding
- return false;
- }
-
- /**
- * Returns true if this adapter supports bulk loading
- *
- * @return bool
- */
- public function hasBulkLoader()
- {
- /**
- * COPY ?
- *
- * @link http://www.postgresql.org/docs/current/interactive/sql-copy.html
- */
- return false;
- }
-
- /**
- * Test error number
- *
- * @param Exception $e
- * @param string $errno
- * @return bool
- */
- public function isErrNo($e, $errno)
- {
- // map MySQL driver-specific error codes to PostgreSQL SQLSTATE
- $map = array(
- // MySQL: Unknown database '%s'
- // PostgreSQL: database "%s" does not exist
- '1049' => '08006',
-
- // MySQL: Table '%s' already exists
- // PostgreSQL: relation "%s" already exists
- '1050' => '42P07',
-
- // MySQL: Unknown column '%s' in '%s'
- // PostgreSQL: column "%s" does not exist
- '1054' => '42703',
-
- // MySQL: Duplicate column name '%s'
- // PostgreSQL: column "%s" of relation "%s" already exists
- '1060' => '42701',
-
- // MySQL: Duplicate key name '%s'
- // PostgreSQL: relation "%s" already exists
- '1061' => '42P07',
-
- // MySQL: Duplicate entry '%s' for key '%s'
- // PostgreSQL: duplicate key violates unique constraint
- '1062' => '23505',
-
- // MySQL: Can't DROP '%s'; check that column/key exists
- // PostgreSQL: index "%s" does not exist
- '1091' => '42704',
-
- // MySQL: Table '%s.%s' doesn't exist
- // PostgreSQL: relation "%s" does not exist
- '1146' => '42P01',
- );
-
- if(preg_match('/([0-9]{2}[0-9P][0-9]{2})/', $e->getMessage(), $match))
- {
- return $match[1] == $map[$errno];
- }
- return false;
- }
-
- /**
- * Is the connection character set equal to utf8?
- *
- * @return bool
- */
- public function isConnectionUTF8()
- {
- $charset = $this->fetchOne('SHOW client_encoding');
- return strtolower($charset) === 'utf8';
- }
-
- /**
- * Retrieve client version in PHP style
- *
- * @return string
- */
- public function getClientVersion()
- {
- $this->_connect();
- try {
- $version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
- $matches = null;
- if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
- return $matches[1];
- }
- } catch (PDOException $e) {
- // In case of the driver doesn't support getting attributes
- }
- return null;
- }
+ /**
+ * Reset the configuration variables in this adapter.
+ */
+ public function resetConfig()
+ {
+ $this->_config = array();
+ }
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 5432;
+ }
+
+ /**
+ * Check PostgreSQL version
+ *
+ * @throws Exception
+ */
+ public function checkServerVersion()
+ {
+ $databaseVersion = $this->getServerVersion();
+ $requiredVersion = Piwik_Config::getInstance()->General['minimum_pgsql_version'];
+ if (version_compare($databaseVersion, $requiredVersion) === -1) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionDatabaseVersion', array('PostgreSQL', $databaseVersion, $requiredVersion)));
+ }
+ }
+
+ /**
+ * Check client version compatibility against database server
+ */
+ public function checkClientVersion()
+ {
+ }
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ $extensions = @get_loaded_extensions();
+ return in_array('PDO', $extensions) && in_array('pdo_pgsql', $extensions);
+ }
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ // large objects must be loaded from a file using a non-SQL API
+ // and then referenced by the object ID (oid);
+ // the alternative, bytea fields, incur a space and time
+ // penalty for encoding/decoding
+ return false;
+ }
+
+ /**
+ * Returns true if this adapter supports bulk loading
+ *
+ * @return bool
+ */
+ public function hasBulkLoader()
+ {
+ /**
+ * COPY ?
+ *
+ * @link http://www.postgresql.org/docs/current/interactive/sql-copy.html
+ */
+ return false;
+ }
+
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ // map MySQL driver-specific error codes to PostgreSQL SQLSTATE
+ $map = array(
+ // MySQL: Unknown database '%s'
+ // PostgreSQL: database "%s" does not exist
+ '1049' => '08006',
+
+ // MySQL: Table '%s' already exists
+ // PostgreSQL: relation "%s" already exists
+ '1050' => '42P07',
+
+ // MySQL: Unknown column '%s' in '%s'
+ // PostgreSQL: column "%s" does not exist
+ '1054' => '42703',
+
+ // MySQL: Duplicate column name '%s'
+ // PostgreSQL: column "%s" of relation "%s" already exists
+ '1060' => '42701',
+
+ // MySQL: Duplicate key name '%s'
+ // PostgreSQL: relation "%s" already exists
+ '1061' => '42P07',
+
+ // MySQL: Duplicate entry '%s' for key '%s'
+ // PostgreSQL: duplicate key violates unique constraint
+ '1062' => '23505',
+
+ // MySQL: Can't DROP '%s'; check that column/key exists
+ // PostgreSQL: index "%s" does not exist
+ '1091' => '42704',
+
+ // MySQL: Table '%s.%s' doesn't exist
+ // PostgreSQL: relation "%s" does not exist
+ '1146' => '42P01',
+ );
+
+ if (preg_match('/([0-9]{2}[0-9P][0-9]{2})/', $e->getMessage(), $match)) {
+ return $match[1] == $map[$errno];
+ }
+ return false;
+ }
+
+ /**
+ * Is the connection character set equal to utf8?
+ *
+ * @return bool
+ */
+ public function isConnectionUTF8()
+ {
+ $charset = $this->fetchOne('SHOW client_encoding');
+ return strtolower($charset) === 'utf8';
+ }
+
+ /**
+ * Retrieve client version in PHP style
+ *
+ * @return string
+ */
+ public function getClientVersion()
+ {
+ $this->_connect();
+ try {
+ $version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
+ $matches = null;
+ if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
+ return $matches[1];
+ }
+ } catch (PDOException $e) {
+ // In case of the driver doesn't support getting attributes
+ }
+ return null;
+ }
}
diff --git a/core/Db/Schema.php b/core/Db/Schema.php
index fbce36722d..8cf56ccb85 100644
--- a/core/Db/Schema.php
+++ b/core/Db/Schema.php
@@ -19,254 +19,245 @@
*/
class Piwik_Db_Schema
{
- /**
- * Singleton instance
- *
- * @var Piwik_Db_Schema
- */
- static private $instance = null;
+ /**
+ * Singleton instance
+ *
+ * @var Piwik_Db_Schema
+ */
+ static private $instance = null;
- /**
- * Type of database schema
- *
- * @var string
- */
- private $schema = null;
+ /**
+ * Type of database schema
+ *
+ * @var string
+ */
+ private $schema = null;
- /**
- * Returns the singleton Piwik_Db_Schema
- *
- * @return Piwik_Db_Schema
- */
- static public function getInstance()
- {
- if (self::$instance === null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
+ /**
+ * Returns the singleton Piwik_Db_Schema
+ *
+ * @return Piwik_Db_Schema
+ */
+ static public function getInstance()
+ {
+ if (self::$instance === null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
- /**
- * Get schema class name
- *
- * @param string $schemaName
- * @return string
- */
- private static function getSchemaClassName($schemaName)
- {
- return 'Piwik_Db_Schema_' . str_replace(' ', '_', ucwords(str_replace('_', ' ', strtolower($schemaName))));
- }
+ /**
+ * Get schema class name
+ *
+ * @param string $schemaName
+ * @return string
+ */
+ private static function getSchemaClassName($schemaName)
+ {
+ return 'Piwik_Db_Schema_' . str_replace(' ', '_', ucwords(str_replace('_', ' ', strtolower($schemaName))));
+ }
- /**
- * Get list of schemas
- *
- * @param string $adapterName
- * @return array
- */
- public static function getSchemas($adapterName)
- {
- static $allSchemaNames = array(
- // MySQL storage engines
- 'MYSQL' => array(
- 'Myisam',
+ /**
+ * Get list of schemas
+ *
+ * @param string $adapterName
+ * @return array
+ */
+ public static function getSchemas($adapterName)
+ {
+ static $allSchemaNames = array(
+ // MySQL storage engines
+ 'MYSQL' => array(
+ 'Myisam',
// 'Innodb',
// 'Infinidb',
- ),
+ ),
- // Microsoft SQL Server
+ // Microsoft SQL Server
// 'MSSQL' => array( 'Mssql' ),
- // PostgreSQL
+ // PostgreSQL
// 'PDO_PGSQL' => array( 'Pgsql' ),
- // IBM DB2
+ // IBM DB2
// 'IBM' => array( 'Ibm' ),
- // Oracle
+ // Oracle
// 'OCI' => array( 'Oci' ),
- );
+ );
- $adapterName = strtoupper($adapterName);
- switch($adapterName)
- {
- case 'PDO_MYSQL':
- case 'MYSQLI':
- $adapterName = 'MYSQL';
- break;
+ $adapterName = strtoupper($adapterName);
+ switch ($adapterName) {
+ case 'PDO_MYSQL':
+ case 'MYSQLI':
+ $adapterName = 'MYSQL';
+ break;
- case 'PDO_MSSQL':
- case 'SQLSRV':
- $adapterName = 'MSSQL';
- break;
+ case 'PDO_MSSQL':
+ case 'SQLSRV':
+ $adapterName = 'MSSQL';
+ break;
- case 'PDO_IBM':
- case 'DB2':
- $adapterName = 'IBM';
- break;
+ case 'PDO_IBM':
+ case 'DB2':
+ $adapterName = 'IBM';
+ break;
- case 'PDO_OCI':
- case 'ORACLE':
- $adapterName = 'OCI';
- break;
- }
- $schemaNames = $allSchemaNames[$adapterName];
+ case 'PDO_OCI':
+ case 'ORACLE':
+ $adapterName = 'OCI';
+ break;
+ }
+ $schemaNames = $allSchemaNames[$adapterName];
- $schemas = array();
+ $schemas = array();
- foreach($schemaNames as $schemaName)
- {
- $className = 'Piwik_Db_Schema_'.$schemaName;
- if(call_user_func(array($className, 'isAvailable')))
- {
- $schemas[] = $schemaName;
- }
- }
+ foreach ($schemaNames as $schemaName) {
+ $className = 'Piwik_Db_Schema_' . $schemaName;
+ if (call_user_func(array($className, 'isAvailable'))) {
+ $schemas[] = $schemaName;
+ }
+ }
- return $schemas;
- }
+ return $schemas;
+ }
- /**
- * Load schema
- */
- private function loadSchema()
- {
- $schema = null;
- Piwik_PostEvent('Schema.loadSchema', $schema);
- if($schema === null)
- {
- $config = Piwik_Config::getInstance();
- $dbInfos = $config->database;
- if(isset($dbInfos['schema']))
- {
- $schemaName = $dbInfos['schema'];
- }
- else
- {
- $schemaName = 'Myisam';
- }
- $className = self::getSchemaClassName($schemaName);
- $schema = new $className();
- }
- $this->schema = $schema;
- }
+ /**
+ * Load schema
+ */
+ private function loadSchema()
+ {
+ $schema = null;
+ Piwik_PostEvent('Schema.loadSchema', $schema);
+ if ($schema === null) {
+ $config = Piwik_Config::getInstance();
+ $dbInfos = $config->database;
+ if (isset($dbInfos['schema'])) {
+ $schemaName = $dbInfos['schema'];
+ } else {
+ $schemaName = 'Myisam';
+ }
+ $className = self::getSchemaClassName($schemaName);
+ $schema = new $className();
+ }
+ $this->schema = $schema;
+ }
- /**
- * Returns an instance that subclasses Piwik_Db_Schema
- *
- * @return Piwik_Db_Schema_Interface
- */
- private function getSchema()
- {
- if ($this->schema === null)
- {
- $this->loadSchema();
- }
- return $this->schema;
- }
+ /**
+ * Returns an instance that subclasses Piwik_Db_Schema
+ *
+ * @return Piwik_Db_Schema_Interface
+ */
+ private function getSchema()
+ {
+ if ($this->schema === null) {
+ $this->loadSchema();
+ }
+ return $this->schema;
+ }
- /**
- * Get the SQL to create a specific Piwik table
- *
- * @param string $tableName name of the table to create
- * @return string SQL
- */
- public function getTableCreateSql( $tableName )
- {
- return $this->getSchema()->getTableCreateSql($tableName);
- }
+ /**
+ * Get the SQL to create a specific Piwik table
+ *
+ * @param string $tableName name of the table to create
+ * @return string SQL
+ */
+ public function getTableCreateSql($tableName)
+ {
+ return $this->getSchema()->getTableCreateSql($tableName);
+ }
- /**
- * Get the SQL to create Piwik tables
- *
- * @return array array of strings containing SQL
- */
- public function getTablesCreateSql()
- {
- return $this->getSchema()->getTablesCreateSql();
- }
+ /**
+ * Get the SQL to create Piwik tables
+ *
+ * @return array array of strings containing SQL
+ */
+ public function getTablesCreateSql()
+ {
+ return $this->getSchema()->getTablesCreateSql();
+ }
- /**
- * Create database
- *
- * @param null|string $dbName database name to create
- */
- public function createDatabase( $dbName = null )
- {
- $this->getSchema()->createDatabase($dbName);
- }
+ /**
+ * Create database
+ *
+ * @param null|string $dbName database name to create
+ */
+ public function createDatabase($dbName = null)
+ {
+ $this->getSchema()->createDatabase($dbName);
+ }
- /**
- * Drop database
- */
- public function dropDatabase()
- {
- $this->getSchema()->dropDatabase();
- }
+ /**
+ * Drop database
+ */
+ public function dropDatabase()
+ {
+ $this->getSchema()->dropDatabase();
+ }
- /**
- * Create all tables
- */
- public function createTables()
- {
- $this->getSchema()->createTables();
- }
+ /**
+ * Create all tables
+ */
+ public function createTables()
+ {
+ $this->getSchema()->createTables();
+ }
- /**
- * Creates an entry in the User table for the "anonymous" user.
- */
- public function createAnonymousUser()
- {
- $this->getSchema()->createAnonymousUser();
- }
+ /**
+ * Creates an entry in the User table for the "anonymous" user.
+ */
+ public function createAnonymousUser()
+ {
+ $this->getSchema()->createAnonymousUser();
+ }
- /**
- * Truncate all tables
- */
- public function truncateAllTables()
- {
- $this->getSchema()->truncateAllTables();
- }
+ /**
+ * Truncate all tables
+ */
+ public function truncateAllTables()
+ {
+ $this->getSchema()->truncateAllTables();
+ }
- /**
- * Drop specific tables
- *
- * @param array $doNotDelete
- */
- public function dropTables( $doNotDelete = array() )
- {
- $this->getSchema()->dropTables($doNotDelete);
- }
+ /**
+ * Drop specific tables
+ *
+ * @param array $doNotDelete
+ */
+ public function dropTables($doNotDelete = array())
+ {
+ $this->getSchema()->dropTables($doNotDelete);
+ }
- /**
- * Names of all the prefixed tables in piwik
- * Doesn't use the DB
- *
- * @return array Table names
- */
- public function getTablesNames()
- {
- return $this->getSchema()->getTablesNames();
- }
+ /**
+ * Names of all the prefixed tables in piwik
+ * Doesn't use the DB
+ *
+ * @return array Table names
+ */
+ public function getTablesNames()
+ {
+ return $this->getSchema()->getTablesNames();
+ }
- /**
- * Get list of tables installed
- *
- * @param bool $forceReload Invalidate cache
- * @return array installed tables
- */
- public function getTablesInstalled($forceReload = true)
- {
- return $this->getSchema()->getTablesInstalled($forceReload);
- }
+ /**
+ * Get list of tables installed
+ *
+ * @param bool $forceReload Invalidate cache
+ * @return array installed tables
+ */
+ public function getTablesInstalled($forceReload = true)
+ {
+ return $this->getSchema()->getTablesInstalled($forceReload);
+ }
- /**
- * Returns true if Piwik tables exist
- *
- * @return bool True if tables exist; false otherwise
- */
- public function hasTables()
- {
- return $this->getSchema()->hasTables();
- }
+ /**
+ * Returns true if Piwik tables exist
+ *
+ * @return bool True if tables exist; false otherwise
+ */
+ public function hasTables()
+ {
+ return $this->getSchema()->hasTables();
+ }
} \ No newline at end of file
diff --git a/core/Db/Schema/Interface.php b/core/Db/Schema/Interface.php
index 6bada30a70..a56f79eb03 100644
--- a/core/Db/Schema/Interface.php
+++ b/core/Db/Schema/Interface.php
@@ -17,82 +17,82 @@
*/
interface Piwik_Db_Schema_Interface
{
- /**
- * Is this schema available?
- *
- * @return bool True if schema is available; false otherwise
- */
- static public function isAvailable();
+ /**
+ * Is this schema available?
+ *
+ * @return bool True if schema is available; false otherwise
+ */
+ static public function isAvailable();
- /**
- * Get the SQL to create a specific Piwik table
- *
- * @param string $tableName
- * @return string SQL
- */
- public function getTableCreateSql($tableName);
+ /**
+ * Get the SQL to create a specific Piwik table
+ *
+ * @param string $tableName
+ * @return string SQL
+ */
+ public function getTableCreateSql($tableName);
- /**
- * Get the SQL to create Piwik tables
- *
- * @return array array of strings containing SQL
- */
- public function getTablesCreateSql();
+ /**
+ * Get the SQL to create Piwik tables
+ *
+ * @return array array of strings containing SQL
+ */
+ public function getTablesCreateSql();
- /**
- * Create database
- *
- * @param string $dbName Name of the database to create
- */
- public function createDatabase( $dbName = null );
+ /**
+ * Create database
+ *
+ * @param string $dbName Name of the database to create
+ */
+ public function createDatabase($dbName = null);
- /**
- * Drop database
- */
- public function dropDatabase();
+ /**
+ * Drop database
+ */
+ public function dropDatabase();
- /**
- * Create all tables
- */
- public function createTables();
+ /**
+ * Create all tables
+ */
+ public function createTables();
- /**
- * Creates an entry in the User table for the "anonymous" user.
- */
- public function createAnonymousUser();
+ /**
+ * Creates an entry in the User table for the "anonymous" user.
+ */
+ public function createAnonymousUser();
- /**
- * Truncate all tables
- */
- public function truncateAllTables();
+ /**
+ * Truncate all tables
+ */
+ public function truncateAllTables();
- /**
- * Drop specific tables
- *
- * @param array $doNotDelete Names of tables to not delete
- */
- public function dropTables( $doNotDelete = array() );
+ /**
+ * Drop specific tables
+ *
+ * @param array $doNotDelete Names of tables to not delete
+ */
+ public function dropTables($doNotDelete = array());
- /**
- * Names of all the prefixed tables in piwik
- * Doesn't use the DB
- *
- * @return array Table names
- */
- public function getTablesNames();
+ /**
+ * Names of all the prefixed tables in piwik
+ * Doesn't use the DB
+ *
+ * @return array Table names
+ */
+ public function getTablesNames();
- /**
- * Get list of tables installed
- *
- * @param bool $forceReload Invalidate cache
- * @return array installed Tables
- */
- public function getTablesInstalled($forceReload = true);
+ /**
+ * Get list of tables installed
+ *
+ * @param bool $forceReload Invalidate cache
+ * @return array installed Tables
+ */
+ public function getTablesInstalled($forceReload = true);
- /**
- * Checks whether any table exists
- *
- * @return bool True if tables exist; false otherwise
- */
- public function hasTables();
+ /**
+ * Checks whether any table exists
+ *
+ * @return bool True if tables exist; false otherwise
+ */
+ public function hasTables();
}
diff --git a/core/Db/Schema/Myisam.php b/core/Db/Schema/Myisam.php
index 1bbbb1940f..8ebacabcd9 100644
--- a/core/Db/Schema/Myisam.php
+++ b/core/Db/Schema/Myisam.php
@@ -17,45 +17,44 @@
*/
class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
{
- /**
- * Is this MySQL storage engine available?
- *
- * @param string $engineName
- * @return bool True if available and enabled; false otherwise
- */
- static private function hasStorageEngine($engineName)
- {
- $db = Zend_Registry::get('db');
- $allEngines = $db->fetchAssoc('SHOW ENGINES');
- if(array_key_exists($engineName, $allEngines))
- {
- $support = $allEngines[$engineName]['Support'];
- return $support == 'DEFAULT' || $support == 'YES';
- }
- return false;
- }
-
- /**
- * Is this schema available?
- *
- * @return bool True if schema is available; false otherwise
- */
- static public function isAvailable()
- {
- return self::hasStorageEngine('MyISAM');
- }
-
- /**
- * Get the SQL to create Piwik tables
- *
- * @return array array of strings containing SQL
- */
- public function getTablesCreateSql()
- {
- $config = Piwik_Config::getInstance();
- $prefixTables = $config->database['tables_prefix'];
- $tables = array(
- 'user' => "CREATE TABLE {$prefixTables}user (
+ /**
+ * Is this MySQL storage engine available?
+ *
+ * @param string $engineName
+ * @return bool True if available and enabled; false otherwise
+ */
+ static private function hasStorageEngine($engineName)
+ {
+ $db = Zend_Registry::get('db');
+ $allEngines = $db->fetchAssoc('SHOW ENGINES');
+ if (array_key_exists($engineName, $allEngines)) {
+ $support = $allEngines[$engineName]['Support'];
+ return $support == 'DEFAULT' || $support == 'YES';
+ }
+ return false;
+ }
+
+ /**
+ * Is this schema available?
+ *
+ * @return bool True if schema is available; false otherwise
+ */
+ static public function isAvailable()
+ {
+ return self::hasStorageEngine('MyISAM');
+ }
+
+ /**
+ * Get the SQL to create Piwik tables
+ *
+ * @return array array of strings containing SQL
+ */
+ public function getTablesCreateSql()
+ {
+ $config = Piwik_Config::getInstance();
+ $prefixTables = $config->database['tables_prefix'];
+ $tables = array(
+ 'user' => "CREATE TABLE {$prefixTables}user (
login VARCHAR(100) NOT NULL,
password CHAR(32) NOT NULL,
alias VARCHAR(45) NOT NULL,
@@ -67,7 +66,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'access' => "CREATE TABLE {$prefixTables}access (
+ 'access' => "CREATE TABLE {$prefixTables}access (
login VARCHAR(100) NOT NULL,
idsite INTEGER UNSIGNED NOT NULL,
access VARCHAR(10) NULL,
@@ -75,7 +74,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'site' => "CREATE TABLE {$prefixTables}site (
+ 'site' => "CREATE TABLE {$prefixTables}site (
idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(90) NOT NULL,
main_url VARCHAR(255) NOT NULL,
@@ -95,14 +94,14 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'site_url' => "CREATE TABLE {$prefixTables}site_url (
+ 'site_url' => "CREATE TABLE {$prefixTables}site_url (
idsite INTEGER(10) UNSIGNED NOT NULL,
url VARCHAR(255) NOT NULL,
PRIMARY KEY(idsite, url)
) DEFAULT CHARSET=utf8
",
- 'goal' => " CREATE TABLE `{$prefixTables}goal` (
+ 'goal' => " CREATE TABLE `{$prefixTables}goal` (
`idsite` int(11) NOT NULL,
`idgoal` int(11) NOT NULL,
`name` varchar(50) NOT NULL,
@@ -117,7 +116,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'logger_message' => "CREATE TABLE {$prefixTables}logger_message (
+ 'logger_message' => "CREATE TABLE {$prefixTables}logger_message (
idlogger_message INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
timestamp TIMESTAMP NULL,
message TEXT NULL,
@@ -125,7 +124,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'logger_api_call' => "CREATE TABLE {$prefixTables}logger_api_call (
+ 'logger_api_call' => "CREATE TABLE {$prefixTables}logger_api_call (
idlogger_api_call INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
class_name VARCHAR(255) NULL,
method_name VARCHAR(255) NULL,
@@ -139,7 +138,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'logger_error' => "CREATE TABLE {$prefixTables}logger_error (
+ 'logger_error' => "CREATE TABLE {$prefixTables}logger_error (
idlogger_error INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
timestamp TIMESTAMP NULL,
message TEXT NULL,
@@ -151,7 +150,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'logger_exception' => "CREATE TABLE {$prefixTables}logger_exception (
+ 'logger_exception' => "CREATE TABLE {$prefixTables}logger_exception (
idlogger_exception INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
timestamp TIMESTAMP NULL,
message TEXT NULL,
@@ -163,7 +162,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'log_action' => "CREATE TABLE {$prefixTables}log_action (
+ 'log_action' => "CREATE TABLE {$prefixTables}log_action (
idaction INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
name TEXT,
hash INTEGER(10) UNSIGNED NOT NULL,
@@ -174,7 +173,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'log_visit' => "CREATE TABLE {$prefixTables}log_visit (
+ '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,
@@ -237,8 +236,8 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
INDEX index_idsite_idvisitor (idsite, idvisitor)
) DEFAULT CHARSET=utf8
",
-
- 'log_conversion_item' => "CREATE TABLE `{$prefixTables}log_conversion_item` (
+
+ '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,
@@ -261,7 +260,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` (
+ 'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` (
idvisit int(10) unsigned NOT NULL,
idsite int(10) unsigned NOT NULL,
idvisitor BINARY(8) NOT NULL,
@@ -309,7 +308,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'log_link_visit_action' => "CREATE TABLE {$prefixTables}log_link_visit_action (
+ '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,
@@ -337,7 +336,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling (
+ 'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling (
query TEXT NOT NULL,
count INTEGER UNSIGNED NULL,
sum_time_ms FLOAT NULL,
@@ -345,7 +344,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'option' => "CREATE TABLE `{$prefixTables}option` (
+ 'option' => "CREATE TABLE `{$prefixTables}option` (
option_name VARCHAR( 255 ) NOT NULL,
option_value LONGTEXT NOT NULL,
autoload TINYINT NOT NULL DEFAULT '1',
@@ -354,7 +353,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'session' => "CREATE TABLE {$prefixTables}session (
+ 'session' => "CREATE TABLE {$prefixTables}session (
id CHAR(32) NOT NULL,
modified INTEGER,
lifetime INTEGER,
@@ -363,7 +362,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric (
+ 'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric (
idarchive INTEGER UNSIGNED NOT NULL,
name VARCHAR(255) NOT NULL,
idsite INTEGER UNSIGNED NULL,
@@ -378,7 +377,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
) DEFAULT CHARSET=utf8
",
- 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob (
+ 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob (
idarchive INTEGER UNSIGNED NOT NULL,
name VARCHAR(255) NOT NULL,
idsite INTEGER UNSIGNED NULL,
@@ -391,191 +390,183 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
INDEX index_period_archived(period, ts_archived)
) DEFAULT CHARSET=utf8
",
- );
- return $tables;
- }
-
- /**
- * Get the SQL to create a specific Piwik table
- *
- * @param string $tableName
- * @throws Exception
- * @return string SQL
- */
- public function getTableCreateSql( $tableName )
- {
- $tables = Piwik::getTablesCreateSql();
-
- if(!isset($tables[$tableName]))
- {
- throw new Exception("The table '$tableName' SQL creation code couldn't be found.");
- }
-
- return $tables[$tableName];
- }
-
- /**
- * Names of all the prefixed tables in piwik
- * Doesn't use the DB
- *
- * @return array Table names
- */
- public function getTablesNames()
- {
- $aTables = array_keys($this->getTablesCreateSql());
- $config = Piwik_Config::getInstance();
- $prefixTables = $config->database['tables_prefix'];
- $return = array();
- foreach($aTables as $table)
- {
- $return[] = $prefixTables.$table;
- }
- return $return;
- }
-
- private $tablesInstalled = null;
-
- /**
- * Get list of tables installed
- *
- * @param bool $forceReload Invalidate cache
- * @return array installed Tables
- */
- public function getTablesInstalled($forceReload = true)
- {
- if(is_null($this->tablesInstalled)
- || $forceReload === true)
- {
- $db = Zend_Registry::get('db');
- $config = Piwik_Config::getInstance();
- $prefixTables = $config->database['tables_prefix'];
-
- // '_' matches any character; force it to be literal
- $prefixTables = str_replace('_', '\_', $prefixTables);
-
- $allTables = $db->fetchCol("SHOW TABLES LIKE '".$prefixTables."%'");
-
- // all the tables to be installed
- $allMyTables = $this->getTablesNames();
-
- // we get the intersection between all the tables in the DB and the tables to be installed
- $tablesInstalled = array_intersect($allMyTables, $allTables);
-
- // at this point we have only the piwik tables which is good
- // but we still miss the piwik generated tables (using the class Piwik_TablePartitioning)
- $allArchiveNumeric = $db->fetchCol("SHOW TABLES LIKE '".$prefixTables."archive_numeric%'");
- $allArchiveBlob = $db->fetchCol("SHOW TABLES LIKE '".$prefixTables."archive_blob%'");
-
- $allTablesReallyInstalled = array_merge($tablesInstalled, $allArchiveNumeric, $allArchiveBlob);
-
- $this->tablesInstalled = $allTablesReallyInstalled;
- }
- return $this->tablesInstalled;
- }
-
- /**
- * Checks whether any table exists
- *
- * @return bool True if tables exist; false otherwise
- */
- public function hasTables()
- {
- return count($this->getTablesInstalled()) != 0;
- }
-
- /**
- * Create database
- *
- * @param string $dbName Name of the database to create
- */
- public function createDatabase( $dbName = null )
- {
- if(is_null($dbName))
- {
- $dbName = Piwik_Config::getInstance()->database['dbname'];
- }
- Piwik_Exec("CREATE DATABASE IF NOT EXISTS ".$dbName." DEFAULT CHARACTER SET utf8");
- }
-
- /**
- * Drop database
- */
- public function dropDatabase()
- {
- $dbName = Piwik_Config::getInstance()->database['dbname'];
- Piwik_Exec("DROP DATABASE IF EXISTS " . $dbName);
- }
-
- /**
- * Create all tables
- */
- public function createTables()
- {
- $db = Zend_Registry::get('db');
- $config = Piwik_Config::getInstance();
- $prefixTables = $config->database['tables_prefix'];
-
- $tablesAlreadyInstalled = $this->getTablesInstalled();
- $tablesToCreate = $this->getTablesCreateSql();
- unset($tablesToCreate['archive_blob']);
- unset($tablesToCreate['archive_numeric']);
-
- foreach($tablesToCreate as $tableName => $tableSql)
- {
- $tableName = $prefixTables . $tableName;
- if(!in_array($tableName, $tablesAlreadyInstalled))
- {
- $db->query( $tableSql );
- }
- }
- }
-
- /**
- * Creates an entry in the User table for the "anonymous" user.
- */
- public function createAnonymousUser()
- {
- // 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 = Zend_Registry::get('db');
- $db->query("INSERT INTO ". Piwik_Common::prefixTable("user") . "
- VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', '".Piwik_Date::factory('now')->getDatetime()."' );" );
- }
-
- /**
- * Truncate all tables
- */
- public function truncateAllTables()
- {
- $tablesAlreadyInstalled = $this->getTablesInstalled($forceReload = true);
- foreach($tablesAlreadyInstalled as $table)
- {
- Piwik_Query("TRUNCATE `$table`");
- }
- }
-
- /**
- * Drop specific tables
- *
- * @param array $doNotDelete Names of tables to not delete
- */
- public function dropTables( $doNotDelete = array() )
- {
- $tablesAlreadyInstalled = $this->getTablesInstalled();
- $db = Zend_Registry::get('db');
-
- $doNotDeletePattern = '/('.implode('|',$doNotDelete).')/';
-
- foreach($tablesAlreadyInstalled as $tableName)
- {
- if( count($doNotDelete) == 0
- || (!in_array($tableName,$doNotDelete)
- && !preg_match($doNotDeletePattern,$tableName)
- )
- )
- {
- $db->query("DROP TABLE `$tableName`");
- }
- }
- }
+ );
+ return $tables;
+ }
+
+ /**
+ * Get the SQL to create a specific Piwik table
+ *
+ * @param string $tableName
+ * @throws Exception
+ * @return string SQL
+ */
+ public function getTableCreateSql($tableName)
+ {
+ $tables = Piwik::getTablesCreateSql();
+
+ if (!isset($tables[$tableName])) {
+ throw new Exception("The table '$tableName' SQL creation code couldn't be found.");
+ }
+
+ return $tables[$tableName];
+ }
+
+ /**
+ * Names of all the prefixed tables in piwik
+ * Doesn't use the DB
+ *
+ * @return array Table names
+ */
+ public function getTablesNames()
+ {
+ $aTables = array_keys($this->getTablesCreateSql());
+ $config = Piwik_Config::getInstance();
+ $prefixTables = $config->database['tables_prefix'];
+ $return = array();
+ foreach ($aTables as $table) {
+ $return[] = $prefixTables . $table;
+ }
+ return $return;
+ }
+
+ private $tablesInstalled = null;
+
+ /**
+ * Get list of tables installed
+ *
+ * @param bool $forceReload Invalidate cache
+ * @return array installed Tables
+ */
+ public function getTablesInstalled($forceReload = true)
+ {
+ if (is_null($this->tablesInstalled)
+ || $forceReload === true
+ ) {
+ $db = Zend_Registry::get('db');
+ $config = Piwik_Config::getInstance();
+ $prefixTables = $config->database['tables_prefix'];
+
+ // '_' matches any character; force it to be literal
+ $prefixTables = str_replace('_', '\_', $prefixTables);
+
+ $allTables = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "%'");
+
+ // all the tables to be installed
+ $allMyTables = $this->getTablesNames();
+
+ // we get the intersection between all the tables in the DB and the tables to be installed
+ $tablesInstalled = array_intersect($allMyTables, $allTables);
+
+ // at this point we have only the piwik tables which is good
+ // but we still miss the piwik generated tables (using the class Piwik_TablePartitioning)
+ $allArchiveNumeric = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_numeric%'");
+ $allArchiveBlob = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_blob%'");
+
+ $allTablesReallyInstalled = array_merge($tablesInstalled, $allArchiveNumeric, $allArchiveBlob);
+
+ $this->tablesInstalled = $allTablesReallyInstalled;
+ }
+ return $this->tablesInstalled;
+ }
+
+ /**
+ * Checks whether any table exists
+ *
+ * @return bool True if tables exist; false otherwise
+ */
+ public function hasTables()
+ {
+ return count($this->getTablesInstalled()) != 0;
+ }
+
+ /**
+ * Create database
+ *
+ * @param string $dbName Name of the database to create
+ */
+ public function createDatabase($dbName = null)
+ {
+ if (is_null($dbName)) {
+ $dbName = Piwik_Config::getInstance()->database['dbname'];
+ }
+ Piwik_Exec("CREATE DATABASE IF NOT EXISTS " . $dbName . " DEFAULT CHARACTER SET utf8");
+ }
+
+ /**
+ * Drop database
+ */
+ public function dropDatabase()
+ {
+ $dbName = Piwik_Config::getInstance()->database['dbname'];
+ Piwik_Exec("DROP DATABASE IF EXISTS " . $dbName);
+ }
+
+ /**
+ * Create all tables
+ */
+ public function createTables()
+ {
+ $db = Zend_Registry::get('db');
+ $config = Piwik_Config::getInstance();
+ $prefixTables = $config->database['tables_prefix'];
+
+ $tablesAlreadyInstalled = $this->getTablesInstalled();
+ $tablesToCreate = $this->getTablesCreateSql();
+ unset($tablesToCreate['archive_blob']);
+ unset($tablesToCreate['archive_numeric']);
+
+ foreach ($tablesToCreate as $tableName => $tableSql) {
+ $tableName = $prefixTables . $tableName;
+ if (!in_array($tableName, $tablesAlreadyInstalled)) {
+ $db->query($tableSql);
+ }
+ }
+ }
+
+ /**
+ * Creates an entry in the User table for the "anonymous" user.
+ */
+ public function createAnonymousUser()
+ {
+ // 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 = Zend_Registry::get('db');
+ $db->query("INSERT INTO " . Piwik_Common::prefixTable("user") . "
+ VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', '" . Piwik_Date::factory('now')->getDatetime() . "' );");
+ }
+
+ /**
+ * Truncate all tables
+ */
+ public function truncateAllTables()
+ {
+ $tablesAlreadyInstalled = $this->getTablesInstalled($forceReload = true);
+ foreach ($tablesAlreadyInstalled as $table) {
+ Piwik_Query("TRUNCATE `$table`");
+ }
+ }
+
+ /**
+ * Drop specific tables
+ *
+ * @param array $doNotDelete Names of tables to not delete
+ */
+ public function dropTables($doNotDelete = array())
+ {
+ $tablesAlreadyInstalled = $this->getTablesInstalled();
+ $db = Zend_Registry::get('db');
+
+ $doNotDeletePattern = '/(' . implode('|', $doNotDelete) . ')/';
+
+ foreach ($tablesAlreadyInstalled as $tableName) {
+ if (count($doNotDelete) == 0
+ || (!in_array($tableName, $doNotDelete)
+ && !preg_match($doNotDeletePattern, $tableName)
+ )
+ ) {
+ $db->query("DROP TABLE `$tableName`");
+ }
+ }
+ }
}
diff --git a/core/ErrorHandler.php b/core/ErrorHandler.php
index cfb342babb..f2bcde168c 100644
--- a/core/ErrorHandler.php
+++ b/core/ErrorHandler.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,75 +12,68 @@
/**
* Error handler used to display nicely errors in Piwik
*
- * @param int $errno Error number
- * @param string $errstr Error message
- * @param string $errfile File name
- * @param int $errline Line number
+ * @param int $errno Error number
+ * @param string $errstr Error message
+ * @param string $errfile File name
+ * @param int $errline Line number
* @return void
*/
function Piwik_ErrorHandler($errno, $errstr, $errfile, $errline)
{
- // if the error has been suppressed by the @ we don't handle the error
- if( error_reporting() == 0 )
- {
- return;
- }
+ // if the error has been suppressed by the @ we don't handle the error
+ if (error_reporting() == 0) {
+ return;
+ }
+
+ if (function_exists('debug_backtrace')) {
+ $backtrace = '';
+ $bt = @debug_backtrace();
+ if ($bt !== null && isset($bt[0])) {
+ foreach ($bt as $i => $debug) {
+ $backtrace .= "#$i "
+ . (isset($debug['class']) ? $debug['class'] : '')
+ . (isset($debug['type']) ? $debug['type'] : '')
+ . (isset($debug['function']) ? $debug['function'] : '')
+ . '(...) called at ['
+ . (isset($debug['file']) ? $debug['file'] : '') . ':'
+ . (isset($debug['line']) ? $debug['line'] : '') . ']' . "\n";
+ }
+ }
+ } else {
+ ob_start();
+ @debug_print_backtrace();
+ $backtrace = ob_get_contents();
+ ob_end_clean();
+ }
- if(function_exists('debug_backtrace'))
- {
- $backtrace = '';
- $bt = @debug_backtrace();
- if($bt !== null && isset($bt[0]))
- {
- foreach($bt as $i => $debug)
- {
- $backtrace .= "#$i "
- .(isset($debug['class']) ? $debug['class'] : '')
- .(isset($debug['type']) ? $debug['type'] : '')
- .(isset($debug['function']) ? $debug['function'] : '')
- .'(...) called at ['
- .(isset($debug['file']) ? $debug['file'] : '').':'
- .(isset($debug['line']) ? $debug['line'] : '').']'."\n";
- }
- }
- }
- else
- {
- ob_start();
- @debug_print_backtrace();
- $backtrace = ob_get_contents();
- ob_end_clean();
- }
+ try {
+ Zend_Registry::get('logger_error')->logEvent($errno, $errstr, $errfile, $errline, $backtrace);
+ } catch (Exception $e) {
+ // in case the error occurs before the logger creation, we simply display it
+ print("<pre>$errstr \nin '$errfile' at the line $errline\n\n$backtrace\n</pre>");
+ exit;
+ }
+ switch ($errno) {
+ case E_ERROR:
+ case E_PARSE:
+ case E_CORE_ERROR:
+ case E_CORE_WARNING:
+ case E_COMPILE_ERROR:
+ case E_COMPILE_WARNING:
+ case E_USER_ERROR:
+ exit;
+ break;
- try {
- Zend_Registry::get('logger_error')->logEvent($errno, $errstr, $errfile, $errline, $backtrace);
- } catch(Exception $e) {
- // in case the error occurs before the logger creation, we simply display it
- print("<pre>$errstr \nin '$errfile' at the line $errline\n\n$backtrace\n</pre>");
- exit;
- }
- switch($errno)
- {
- case E_ERROR:
- case E_PARSE:
- case E_CORE_ERROR:
- case E_CORE_WARNING:
- case E_COMPILE_ERROR:
- case E_COMPILE_WARNING:
- case E_USER_ERROR:
- exit;
- break;
-
- case E_WARNING:
- case E_NOTICE:
- case E_USER_WARNING:
- case E_USER_NOTICE:
- case E_STRICT:
- case E_RECOVERABLE_ERROR:
- case E_DEPRECATED:
- case E_USER_DEPRECATED:
- default:
- // do not exit
- break;
- }
+ case E_WARNING:
+ case E_NOTICE:
+ case E_USER_WARNING:
+ case E_USER_NOTICE:
+ case E_STRICT:
+ case E_RECOVERABLE_ERROR:
+ case E_DEPRECATED:
+ case E_USER_DEPRECATED:
+ default:
+ // do not exit
+ break;
+ }
}
diff --git a/core/ExceptionHandler.php b/core/ExceptionHandler.php
index 7bae1057b2..b3dd6692fe 100644
--- a/core/ExceptionHandler.php
+++ b/core/ExceptionHandler.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,34 +12,33 @@
/**
* Exception handler used to display nicely exceptions in Piwik
*
- * @param Exception $exception
+ * @param Exception $exception
* @throws Exception
*/
-function Piwik_ExceptionHandler(Exception $exception)
+function Piwik_ExceptionHandler(Exception $exception)
{
- try {
- Zend_Registry::get('logger_exception')->logEvent($exception);
- } catch(Exception $e) {
-
- if(Piwik_FrontController::shouldRethrowException())
- {
- throw $exception;
- }
-
- // case when the exception is raised before the logger being ready
- // we handle the exception a la mano, but using the Logger formatting properties
- $event = array();
- $event['errno'] = $exception->getCode();
- $event['message'] = $exception->getMessage();
- $event['errfile'] = $exception->getFile();
- $event['errline'] = $exception->getLine();
- $event['backtrace'] = $exception->getTraceAsString();
+ try {
+ Zend_Registry::get('logger_exception')->logEvent($exception);
+ } catch (Exception $e) {
+
+ if (Piwik_FrontController::shouldRethrowException()) {
+ throw $exception;
+ }
+
+ // case when the exception is raised before the logger being ready
+ // we handle the exception a la mano, but using the Logger formatting properties
+ $event = array();
+ $event['errno'] = $exception->getCode();
+ $event['message'] = $exception->getMessage();
+ $event['errfile'] = $exception->getFile();
+ $event['errline'] = $exception->getLine();
+ $event['backtrace'] = $exception->getTraceAsString();
+
+ $formatter = new Piwik_Log_Exception_Formatter_ScreenFormatter();
+
+ $message = $formatter->format($event);
+ $message .= "<br /><br />And this exception raised another exception \"" . $e->getMessage() . "\"";
- $formatter = new Piwik_Log_Exception_Formatter_ScreenFormatter();
-
- $message = $formatter->format($event);
- $message .= "<br /><br />And this exception raised another exception \"". $e->getMessage()."\"";
-
- Piwik::exitWithErrorMessage( $message );
- }
+ Piwik::exitWithErrorMessage($message);
+ }
}
diff --git a/core/FrontController.php b/core/FrontController.php
index 203a911f8a..8f136acd65 100644
--- a/core/FrontController.php
+++ b/core/FrontController.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -22,356 +22,334 @@ require_once PIWIK_INCLUDE_PATH . '/core/Option.php';
* Front controller.
* This is the class hit in the first place.
* It dispatches the request to the right controller.
- *
+ *
* For a detailed explanation, see the documentation on http://piwik.org/docs/plugins/framework-overview
- *
+ *
* @package Piwik
* @subpackage Piwik_FrontController
*/
class Piwik_FrontController
{
- /**
- * Set to false and the Front Controller will not dispatch the request
- *
- * @var bool
- */
- static public $enableDispatch = true;
-
- static private $instance = null;
-
- /**
- * returns singleton
- *
- * @return Piwik_FrontController
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Dispatches the request to the right plugin and executes the requested action on the plugin controller.
- *
- * @throws Exception|Piwik_FrontController_PluginDeactivatedException in case the plugin doesn't exist, the action doesn't exist, there is not enough permission, etc.
- *
- * @param string $module
- * @param string $action
- * @param array $parameters
- * @return mixed The returned value of the calls, often nothing as the module print but don't return data
- * @see fetchDispatch()
- */
- function dispatch( $module = null, $action = null, $parameters = null)
- {
- if( self::$enableDispatch === false)
- {
- return;
- }
-
- if(is_null($module))
- {
- $defaultModule = 'CoreHome';
- $module = Piwik_Common::getRequestVar('module', $defaultModule, 'string');
- }
-
- if(is_null($action))
- {
- $action = Piwik_Common::getRequestVar('action', false);
- }
-
- if(!Piwik_Session::isFileBasedSessions()
- && ($module !== 'API' || ($action && $action !== 'index')))
- {
- Piwik_Session::start();
- }
-
- if(is_null($parameters))
- {
- $parameters = array();
- }
-
- if(!ctype_alnum($module))
- {
- throw new Exception("Invalid module name '$module'");
- }
-
- if( ! Piwik_PluginsManager::getInstance()->isPluginActivated( $module ))
- {
- throw new Piwik_FrontController_PluginDeactivatedException($module);
- }
-
- $controllerClassName = 'Piwik_'.$module.'_Controller';
-
- // FrontController's autoloader
- if(!class_exists($controllerClassName, false))
- {
- $moduleController = PIWIK_INCLUDE_PATH . '/plugins/' . $module . '/Controller.php';
- if(!is_readable($moduleController))
- {
- throw new Exception("Module controller $moduleController not found!");
- }
- require_once $moduleController; // prefixed by PIWIK_INCLUDE_PATH
- }
-
- $controller = new $controllerClassName();
- if($action === false)
- {
- $action = $controller->getDefaultAction();
- }
-
- if( !is_callable(array($controller, $action)))
- {
- throw new Exception("Action '$action' not found in the controller '$controllerClassName'.");
- }
-
- // Generic hook that plugins can use to modify any input to the function,
- // or even change the plugin being called
- $params = array($controller, $action, $parameters);
- Piwik_PostEvent('FrontController.dispatch', $params);
-
- try {
- return call_user_func_array( array($params[0], $params[1] ), $params[2]);
- } catch(Piwik_Access_NoAccessException $e) {
- Piwik_PostEvent('FrontController.NoAccessException', $e);
- } catch(Exception $e) {
- $debugTrace = $e->getTraceAsString();
- $message = Piwik_Common::sanitizeInputValue($e->getMessage());
- Piwik_ExitWithMessage($message, '' /* $debugTrace */, true);
- }
- }
-
- /**
- * Often plugins controller display stuff using echo/print.
- * Using this function instead of dispatch() returns the output string form the actions calls.
- *
- * @param string $controllerName
- * @param string $actionName
- * @param array $parameters
- * @return string
- */
- function fetchDispatch( $controllerName = null, $actionName = null, $parameters = null)
- {
- ob_start();
- $output = $this->dispatch( $controllerName, $actionName, $parameters);
- // if nothing returned we try to load something that was printed on the screen
- if(empty($output))
- {
- $output = ob_get_contents();
- }
- ob_end_clean();
- return $output;
- }
-
- /**
- * Called at the end of the page generation
- *
- */
- function __destruct()
- {
- try {
- Piwik::printSqlProfilingReportZend();
- Piwik::printQueryCount();
- Piwik::printTimer();
- } catch(Exception $e) {}
- }
-
- // Should we show exceptions messages directly rather than display an html error page?
- public static function shouldRethrowException()
- {
- // If we are in no dispatch mode, eg. a script reusing Piwik libs,
- // then we should return the exception directly, rather than trigger the event "bad config file"
- // which load the HTML page of the installer with the error.
- // This is at least required for misc/cron/archive.php and useful to all other scripts
- return (defined('PIWIK_ENABLE_DISPATCH') && !PIWIK_ENABLE_DISPATCH)
- || Piwik_Common::isPhpCliMode()
- || Piwik_Common::isArchivePhpTriggered()
- ;
- }
-
- /**
- * Loads the config file and assign to the global registry
- * This is overriden in tests to ensure test config file is used
- */
- protected function createConfigObject()
- {
- $exceptionToThrow = false;
- try {
- Piwik::createConfigObject();
- } catch(Exception $e) {
- Piwik_PostEvent('FrontController.NoConfigurationFile', $e, $info = array(), $pending = true);
- $exceptionToThrow = $e;
- }
- return $exceptionToThrow;
- }
-
- protected function createAccessObject()
- {
- Piwik::createAccessObject();
- }
-
- /**
- * Must be called before dispatch()
- * - checks that directories are writable,
- * - loads the configuration file,
- * - loads the plugin,
- * - inits the DB connection,
- * - etc.
- * @throws Exception
- * @throws Exception
- * @throws bool|Exception
- * @return
- */
- function init()
- {
- static $initialized = false;
- if($initialized)
- {
- return;
- }
- $initialized = true;
-
-
- try {
- Zend_Registry::set('timer', new Piwik_Timer);
-
- $directoriesToCheck = array(
- '/tmp/',
- '/tmp/templates_c/',
- '/tmp/cache/',
- '/tmp/assets/',
- '/tmp/tcpdf/'
- );
-
- Piwik::checkDirectoriesWritableOrDie($directoriesToCheck);
- Piwik_Common::assignCliParametersToRequest();
-
- Piwik_Translate::getInstance()->loadEnglishTranslation();
-
- $exceptionToThrow = $this->createConfigObject();
-
- if(Piwik_Session::isFileBasedSessions())
- {
- Piwik_Session::start();
- }
-
- $this->handleMaintenanceMode();
- $this->handleSSLRedirection();
-
- $pluginsManager = Piwik_PluginsManager::getInstance();
- $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
- $pluginsManager->loadPlugins( $pluginsToLoad );
-
- if($exceptionToThrow)
- {
- throw $exceptionToThrow;
- }
-
- try {
- Piwik::createDatabaseObject();
- } catch(Exception $e) {
- if(self::shouldRethrowException())
- {
- throw $e;
- }
- Piwik_PostEvent('FrontController.badConfigurationFile', $e, $info = array(), $pending = true);
- throw $e;
- }
-
- Piwik::createLogObject();
-
- // creating the access object, so that core/Updates/* can enforce Super User and use some APIs
- $this->createAccessObject();
- Piwik_PostEvent('FrontController.dispatchCoreAndPluginUpdatesScreen');
-
- Piwik_PluginsManager::getInstance()->installLoadedPlugins();
- Piwik::install();
-
- // ensure the current Piwik URL is known for later use
- if(method_exists('Piwik', 'getPiwikUrl'))
- {
- $host = Piwik::getPiwikUrl();
- }
-
- Piwik_PostEvent('FrontController.initAuthenticationObject');
- try {
- $authAdapter = Zend_Registry::get('auth');
- } catch(Exception $e){
- throw new Exception("Authentication object cannot be found in the Registry. Maybe the Login plugin is not activated?
+ /**
+ * Set to false and the Front Controller will not dispatch the request
+ *
+ * @var bool
+ */
+ static public $enableDispatch = true;
+
+ static private $instance = null;
+
+ /**
+ * returns singleton
+ *
+ * @return Piwik_FrontController
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Dispatches the request to the right plugin and executes the requested action on the plugin controller.
+ *
+ * @throws Exception|Piwik_FrontController_PluginDeactivatedException in case the plugin doesn't exist, the action doesn't exist, there is not enough permission, etc.
+ *
+ * @param string $module
+ * @param string $action
+ * @param array $parameters
+ * @return mixed The returned value of the calls, often nothing as the module print but don't return data
+ * @see fetchDispatch()
+ */
+ function dispatch($module = null, $action = null, $parameters = null)
+ {
+ if (self::$enableDispatch === false) {
+ return;
+ }
+
+ if (is_null($module)) {
+ $defaultModule = 'CoreHome';
+ $module = Piwik_Common::getRequestVar('module', $defaultModule, 'string');
+ }
+
+ if (is_null($action)) {
+ $action = Piwik_Common::getRequestVar('action', false);
+ }
+
+ if (!Piwik_Session::isFileBasedSessions()
+ && ($module !== 'API' || ($action && $action !== 'index'))
+ ) {
+ Piwik_Session::start();
+ }
+
+ if (is_null($parameters)) {
+ $parameters = array();
+ }
+
+ if (!ctype_alnum($module)) {
+ throw new Exception("Invalid module name '$module'");
+ }
+
+ if (!Piwik_PluginsManager::getInstance()->isPluginActivated($module)) {
+ throw new Piwik_FrontController_PluginDeactivatedException($module);
+ }
+
+ $controllerClassName = 'Piwik_' . $module . '_Controller';
+
+ // FrontController's autoloader
+ if (!class_exists($controllerClassName, false)) {
+ $moduleController = PIWIK_INCLUDE_PATH . '/plugins/' . $module . '/Controller.php';
+ if (!is_readable($moduleController)) {
+ throw new Exception("Module controller $moduleController not found!");
+ }
+ require_once $moduleController; // prefixed by PIWIK_INCLUDE_PATH
+ }
+
+ $controller = new $controllerClassName();
+ if ($action === false) {
+ $action = $controller->getDefaultAction();
+ }
+
+ if (!is_callable(array($controller, $action))) {
+ throw new Exception("Action '$action' not found in the controller '$controllerClassName'.");
+ }
+
+ // Generic hook that plugins can use to modify any input to the function,
+ // or even change the plugin being called
+ $params = array($controller, $action, $parameters);
+ Piwik_PostEvent('FrontController.dispatch', $params);
+
+ try {
+ return call_user_func_array(array($params[0], $params[1]), $params[2]);
+ } catch (Piwik_Access_NoAccessException $e) {
+ Piwik_PostEvent('FrontController.NoAccessException', $e);
+ } catch (Exception $e) {
+ $debugTrace = $e->getTraceAsString();
+ $message = Piwik_Common::sanitizeInputValue($e->getMessage());
+ Piwik_ExitWithMessage($message, '' /* $debugTrace */, true);
+ }
+ }
+
+ /**
+ * Often plugins controller display stuff using echo/print.
+ * Using this function instead of dispatch() returns the output string form the actions calls.
+ *
+ * @param string $controllerName
+ * @param string $actionName
+ * @param array $parameters
+ * @return string
+ */
+ function fetchDispatch($controllerName = null, $actionName = null, $parameters = null)
+ {
+ ob_start();
+ $output = $this->dispatch($controllerName, $actionName, $parameters);
+ // if nothing returned we try to load something that was printed on the screen
+ if (empty($output)) {
+ $output = ob_get_contents();
+ }
+ ob_end_clean();
+ return $output;
+ }
+
+ /**
+ * Called at the end of the page generation
+ *
+ */
+ function __destruct()
+ {
+ try {
+ Piwik::printSqlProfilingReportZend();
+ Piwik::printQueryCount();
+ Piwik::printTimer();
+ } catch (Exception $e) {
+ }
+ }
+
+ // Should we show exceptions messages directly rather than display an html error page?
+ public static function shouldRethrowException()
+ {
+ // If we are in no dispatch mode, eg. a script reusing Piwik libs,
+ // then we should return the exception directly, rather than trigger the event "bad config file"
+ // which load the HTML page of the installer with the error.
+ // This is at least required for misc/cron/archive.php and useful to all other scripts
+ return (defined('PIWIK_ENABLE_DISPATCH') && !PIWIK_ENABLE_DISPATCH)
+ || Piwik_Common::isPhpCliMode()
+ || Piwik_Common::isArchivePhpTriggered();
+ }
+
+ /**
+ * Loads the config file and assign to the global registry
+ * This is overriden in tests to ensure test config file is used
+ */
+ protected function createConfigObject()
+ {
+ $exceptionToThrow = false;
+ try {
+ Piwik::createConfigObject();
+ } catch (Exception $e) {
+ Piwik_PostEvent('FrontController.NoConfigurationFile', $e, $info = array(), $pending = true);
+ $exceptionToThrow = $e;
+ }
+ return $exceptionToThrow;
+ }
+
+ protected function createAccessObject()
+ {
+ Piwik::createAccessObject();
+ }
+
+ /**
+ * Must be called before dispatch()
+ * - checks that directories are writable,
+ * - loads the configuration file,
+ * - loads the plugin,
+ * - inits the DB connection,
+ * - etc.
+ * @throws Exception
+ * @throws Exception
+ * @throws bool|Exception
+ * @return
+ */
+ function init()
+ {
+ static $initialized = false;
+ if ($initialized) {
+ return;
+ }
+ $initialized = true;
+
+
+ try {
+ Zend_Registry::set('timer', new Piwik_Timer);
+
+ $directoriesToCheck = array(
+ '/tmp/',
+ '/tmp/templates_c/',
+ '/tmp/cache/',
+ '/tmp/assets/',
+ '/tmp/tcpdf/'
+ );
+
+ Piwik::checkDirectoriesWritableOrDie($directoriesToCheck);
+ Piwik_Common::assignCliParametersToRequest();
+
+ Piwik_Translate::getInstance()->loadEnglishTranslation();
+
+ $exceptionToThrow = $this->createConfigObject();
+
+ if (Piwik_Session::isFileBasedSessions()) {
+ Piwik_Session::start();
+ }
+
+ $this->handleMaintenanceMode();
+ $this->handleSSLRedirection();
+
+ $pluginsManager = Piwik_PluginsManager::getInstance();
+ $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
+ $pluginsManager->loadPlugins($pluginsToLoad);
+
+ if ($exceptionToThrow) {
+ throw $exceptionToThrow;
+ }
+
+ try {
+ Piwik::createDatabaseObject();
+ } catch (Exception $e) {
+ if (self::shouldRethrowException()) {
+ throw $e;
+ }
+ Piwik_PostEvent('FrontController.badConfigurationFile', $e, $info = array(), $pending = true);
+ throw $e;
+ }
+
+ Piwik::createLogObject();
+
+ // creating the access object, so that core/Updates/* can enforce Super User and use some APIs
+ $this->createAccessObject();
+ Piwik_PostEvent('FrontController.dispatchCoreAndPluginUpdatesScreen');
+
+ Piwik_PluginsManager::getInstance()->installLoadedPlugins();
+ Piwik::install();
+
+ // ensure the current Piwik URL is known for later use
+ if (method_exists('Piwik', 'getPiwikUrl')) {
+ $host = Piwik::getPiwikUrl();
+ }
+
+ Piwik_PostEvent('FrontController.initAuthenticationObject');
+ try {
+ $authAdapter = Zend_Registry::get('auth');
+ } catch (Exception $e) {
+ throw new Exception("Authentication object cannot be found in the Registry. Maybe the Login plugin is not activated?
<br />You can activate the plugin by adding:<br />
<code>Plugins[] = Login</code><br />
under the <code>[Plugins]</code> section in your config/config.ini.php");
- }
- Zend_Registry::get('access')->reloadAccess($authAdapter);
-
- // Force the auth to use the token_auth if specified, so that embed dashboard
- // and all other non widgetized controller methods works fine
- if(($token_auth = Piwik_Common::getRequestVar('token_auth', false, 'string')) !== false)
- {
- Piwik_API_Request::reloadAuthUsingTokenAuth();
- }
- Piwik::raiseMemoryLimitIfNecessary();
-
- Piwik_Translate::getInstance()->reloadLanguage();
- $pluginsManager->postLoadPlugins();
-
- Piwik_PostEvent('FrontController.checkForUpdates');
- } catch(Exception $e) {
-
- if(self::shouldRethrowException())
- {
- throw $e;
- }
-
- Piwik_ExitWithMessage($e->getMessage(), false, true);
- }
- }
-
- protected function handleMaintenanceMode()
- {
- if(Piwik_Config::getInstance()->General['maintenance_mode'] == 1
- && !Piwik_Common::isPhpCliMode())
- {
- $format = Piwik_Common::getRequestVar('format', '');
-
- $message = "Piwik is in scheduled maintenance. Please come back later."
- . " The administrator can disable maintenance by editing the file piwik/config/config.ini.php and removing the following: "
- . " maintenance_mode=1 ";
- if(Piwik_Config::getInstance()->Tracker['record_statistics'] == 0)
- {
- $message .= ' and record_statistics=0';
- }
-
- $exception = new Exception($message);
- // extend explain how to re-enable
- // show error message when record stats = 0
- if(empty($format))
- {
- throw $exception;
- }
- $response = new Piwik_API_ResponseBuilder( $format );
- echo $response->getResponseException( $exception );
- exit;
- }
- }
-
- protected function handleSSLRedirection()
- {
- if(!Piwik_Common::isPhpCliMode()
- && Piwik_Config::getInstance()->General['force_ssl'] == 1
- && !Piwik::isHttps()
- // Specifically disable for the opt out iframe
- && !(Piwik_Common::getRequestVar('module', '') == 'CoreAdminHome'
- && Piwik_Common::getRequestVar('action', '') == 'optOut')
- )
- {
- $url = Piwik_Url::getCurrentUrl();
- $url = str_replace("http://", "https://", $url);
- Piwik_Url::redirectToUrl($url);
- }
- }
+ }
+ Zend_Registry::get('access')->reloadAccess($authAdapter);
+
+ // Force the auth to use the token_auth if specified, so that embed dashboard
+ // and all other non widgetized controller methods works fine
+ if (($token_auth = Piwik_Common::getRequestVar('token_auth', false, 'string')) !== false) {
+ Piwik_API_Request::reloadAuthUsingTokenAuth();
+ }
+ Piwik::raiseMemoryLimitIfNecessary();
+
+ Piwik_Translate::getInstance()->reloadLanguage();
+ $pluginsManager->postLoadPlugins();
+
+ Piwik_PostEvent('FrontController.checkForUpdates');
+ } catch (Exception $e) {
+
+ if (self::shouldRethrowException()) {
+ throw $e;
+ }
+
+ Piwik_ExitWithMessage($e->getMessage(), false, true);
+ }
+ }
+
+ protected function handleMaintenanceMode()
+ {
+ if (Piwik_Config::getInstance()->General['maintenance_mode'] == 1
+ && !Piwik_Common::isPhpCliMode()
+ ) {
+ $format = Piwik_Common::getRequestVar('format', '');
+
+ $message = "Piwik is in scheduled maintenance. Please come back later."
+ . " The administrator can disable maintenance by editing the file piwik/config/config.ini.php and removing the following: "
+ . " maintenance_mode=1 ";
+ if (Piwik_Config::getInstance()->Tracker['record_statistics'] == 0) {
+ $message .= ' and record_statistics=0';
+ }
+
+ $exception = new Exception($message);
+ // extend explain how to re-enable
+ // show error message when record stats = 0
+ if (empty($format)) {
+ throw $exception;
+ }
+ $response = new Piwik_API_ResponseBuilder($format);
+ echo $response->getResponseException($exception);
+ exit;
+ }
+ }
+
+ protected function handleSSLRedirection()
+ {
+ if (!Piwik_Common::isPhpCliMode()
+ && Piwik_Config::getInstance()->General['force_ssl'] == 1
+ && !Piwik::isHttps()
+ // Specifically disable for the opt out iframe
+ && !(Piwik_Common::getRequestVar('module', '') == 'CoreAdminHome'
+ && Piwik_Common::getRequestVar('action', '') == 'optOut')
+ ) {
+ $url = Piwik_Url::getCurrentUrl();
+ $url = str_replace("http://", "https://", $url);
+ Piwik_Url::redirectToUrl($url);
+ }
+ }
}
/**
@@ -382,9 +360,9 @@ class Piwik_FrontController
*/
class Piwik_FrontController_PluginDeactivatedException extends Exception
{
- function __construct($module)
- {
- parent::__construct("The plugin $module is not enabled. You can activate the plugin on Settings > Plugins page in Piwik.");
- }
+ function __construct($module)
+ {
+ parent::__construct("The plugin $module is not enabled. You can activate the plugin on Settings > Plugins page in Piwik.");
+ }
}
diff --git a/core/HTMLPurifier.php b/core/HTMLPurifier.php
index bbc0f21a14..cb5db8e473 100644
--- a/core/HTMLPurifier.php
+++ b/core/HTMLPurifier.php
@@ -16,40 +16,35 @@
*/
class Piwik_HTMLPurifier
{
- static private $instance = null;
+ static private $instance = null;
- /**
- * Returns the singleton HTMLPurifier or a mock object
- *
- * @return HTMLPurifier|Piwik_HTMLPurifier
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- if(file_exists(PIWIK_INCLUDE_PATH . '/libs/HTMLPurifier.php'))
- {
- if(!class_exists('HTMLPurifier_Bootstrap', false))
- {
- HTMLPurifier_Bootstrap::registerAutoload();
- }
+ /**
+ * Returns the singleton HTMLPurifier or a mock object
+ *
+ * @return HTMLPurifier|Piwik_HTMLPurifier
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ if (file_exists(PIWIK_INCLUDE_PATH . '/libs/HTMLPurifier.php')) {
+ if (!class_exists('HTMLPurifier_Bootstrap', false)) {
+ HTMLPurifier_Bootstrap::registerAutoload();
+ }
- $config = HTMLPurifier_Config::createDefault();
- $config->set('Cache.SerializerPath', PIWIK_USER_PATH . '/tmp/purifier');
+ $config = HTMLPurifier_Config::createDefault();
+ $config->set('Cache.SerializerPath', PIWIK_USER_PATH . '/tmp/purifier');
- self::$instance = new HTMLPurifier($config);
- }
- else
- {
- $c = __CLASS__;
- self::$instance = new $c();
- }
- }
- return self::$instance;
- }
+ self::$instance = new HTMLPurifier($config);
+ } else {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ }
+ return self::$instance;
+ }
- public function purify($html, $config = null)
- {
- return $html;
- }
+ public function purify($html, $config = null)
+ {
+ return $html;
+ }
}
diff --git a/core/Http.php b/core/Http.php
index 7e921d354d..1b0e328184 100644
--- a/core/Http.php
+++ b/core/Http.php
@@ -17,752 +17,683 @@
*/
class Piwik_Http
{
- /**
- * Get "best" available transport method for sendHttpRequest() calls.
- *
- * @return string
- */
- static public function getTransportMethod()
- {
- $method = 'curl';
- if(!self::isCurlEnabled())
- {
- $method = 'fopen';
- if(@ini_get('allow_url_fopen') != '1')
- {
- $method = 'socket';
- if(!self::isSocketEnabled())
- {
- return null;
- }
- }
- }
- return $method;
- }
-
- protected static function isSocketEnabled()
- {
- return function_exists('fsockopen');
- }
-
- protected static function isCurlEnabled()
- {
- return function_exists('curl_init');
- }
-
- /**
- * Sends http request ensuring the request will fail before $timeout seconds
- *
- * If no $destinationPath is specified, the trimmed response (without header) is returned as a string.
- * If a $destinationPath is specified, the response (without header) is saved to a file.
- *
- * @param string $aUrl
- * @param int $timeout
- * @param string $userAgent
- * @param string $destinationPath
- * @param int $followDepth
- * @param bool $acceptLanguage
- * @param array $byteRange For Range: header. Should be two element array of bytes, eg, array(0, 1024)
- * Doesn't work w/ fopen method.
- * @param bool $getExtendedInfo True to return status code, headers & response, false if just response.
- * @param string $httpMethod The HTTP method to use. Defaults to 'GET'.
- * @throws Exception
- * @return bool true (or string) on success; false on HTTP response error code (1xx or 4xx)
- */
- static public function sendHttpRequest($aUrl, $timeout, $userAgent = null, $destinationPath = null, $followDepth = 0, $acceptLanguage = false, $byteRange = false, $getExtendedInfo = false, $httpMethod = 'GET'
-)
- {
- // create output file
- $file = null;
- if($destinationPath)
- {
- // Ensure destination directory exists
- Piwik_Common::mkdir(dirname($destinationPath));
- if (($file = @fopen($destinationPath, 'wb')) === false || !is_resource($file))
- {
- throw new Exception('Error while creating the file: ' . $destinationPath);
- }
- }
-
- $acceptLanguage = $acceptLanguage ? 'Accept-Language: '.$acceptLanguage : '';
- return self::sendHttpRequestBy(self::getTransportMethod(), $aUrl, $timeout, $userAgent, $destinationPath, $file, $followDepth, $acceptLanguage, $acceptInvalidSslCertificate = false, $byteRange, $getExtendedInfo, $httpMethod);
- }
-
- /**
- * Sends http request using the specified transport method
- *
- * @param string $method
- * @param string $aUrl
- * @param int $timeout
- * @param string $userAgent
- * @param string $destinationPath
- * @param resource $file
- * @param int $followDepth
- * @param bool|string $acceptLanguage Accept-language header
- * @param bool $acceptInvalidSslCertificate Only used with $method == 'curl'. If set to true (NOT recommended!) the SSL certificate will not be checked
- * @param array $byteRange For Range: header. Should be two element array of bytes, eg, array(0, 1024)
- * Doesn't work w/ fopen method.
- * @param bool $getExtendedInfo True to return status code, headers & response, false if just response.
- * @param string $httpMethod The HTTP method to use. Defaults to 'GET'.
- * @throws Exception
- * @return bool true (or string/array) on success; false on HTTP response error code (1xx or 4xx)
- */
- static public function sendHttpRequestBy(
- $method = 'socket',
- $aUrl,
- $timeout,
- $userAgent = null,
- $destinationPath = null,
- $file = null,
- $followDepth = 0,
- $acceptLanguage = false,
- $acceptInvalidSslCertificate = false,
- $byteRange = false,
- $getExtendedInfo = false,
- $httpMethod = 'GET'
- )
- {
- if ($followDepth > 5)
- {
- throw new Exception('Too many redirects ('.$followDepth.')');
- }
-
- $contentLength = 0;
- $fileLength = 0;
-
- // Piwik services behave like a proxy, so we should act like one.
- $xff = 'X-Forwarded-For: '
- . (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] . ',' : '')
- . Piwik_IP::getIpFromHeader();
-
- if(empty($userAgent))
- {
- $userAgent = self::getUserAgent();
- }
-
- $via = 'Via: '
- . (isset($_SERVER['HTTP_VIA']) && !empty($_SERVER['HTTP_VIA']) ? $_SERVER['HTTP_VIA'] . ', ' : '')
- . Piwik_Version::VERSION . ' '
- . ($userAgent ? " ($userAgent)" : '');
-
- // range header
- $rangeHeader = '';
- if (!empty($byteRange))
- {
- $rangeHeader = 'Range: bytes='.$byteRange[0].'-'.$byteRange[1]."\r\n";
- }
-
- // proxy configuration
- $proxyHost = Piwik_Config::getInstance()->proxy['host'];
- $proxyPort = Piwik_Config::getInstance()->proxy['port'];
- $proxyUser = Piwik_Config::getInstance()->proxy['username'];
- $proxyPassword = Piwik_Config::getInstance()->proxy['password'];
-
- // other result data
- $status = null;
- $headers = array();
-
- if($method == 'socket')
- {
- if(!self::isSocketEnabled()) {
- // can be triggered in tests
- throw new Exception("HTTP socket support is not enabled (php function fsockopen is not available) ");
- }
- // initialization
- $url = @parse_url($aUrl);
- if($url === false || !isset($url['scheme']))
- {
- throw new Exception('Malformed URL: '.$aUrl);
- }
-
- if($url['scheme'] != 'http')
- {
- throw new Exception('Invalid protocol/scheme: '.$url['scheme']);
- }
- $host = $url['host'];
- $port = isset($url['port)']) ? $url['port'] : 80;
- $path = isset($url['path']) ? $url['path'] : '/';
- if(isset($url['query']))
- {
- $path .= '?'.$url['query'];
- }
- $errno = null;
- $errstr = null;
-
- if ((!empty($proxyHost) && !empty($proxyPort))
- || !empty($byteRange))
- {
- $httpVer = '1.1';
- }
- else
- {
- $httpVer = '1.0';
- }
-
- $proxyAuth = null;
- if(!empty($proxyHost) && !empty($proxyPort))
- {
- $connectHost = $proxyHost;
- $connectPort = $proxyPort;
- if(!empty($proxyUser) && !empty($proxyPassword))
- {
- $proxyAuth = 'Proxy-Authorization: Basic '.base64_encode("$proxyUser:$proxyPassword") ."\r\n";
- }
- $requestHeader = "$httpMethod $aUrl HTTP/$httpVer\r\n";
- }
- else
- {
- $connectHost = $host;
- $connectPort = $port;
- $requestHeader = "$httpMethod $path HTTP/$httpVer\r\n";
- }
-
- // connection attempt
- if (($fsock = @fsockopen($connectHost, $connectPort, $errno, $errstr, $timeout)) === false || !is_resource($fsock))
- {
- if(is_resource($file)) { @fclose($file); }
- throw new Exception("Error while connecting to: $host. Please try again later. $errstr");
- }
-
- // send HTTP request header
- $requestHeader .=
- "Host: $host".($port != 80 ? ':'.$port : '')."\r\n"
- .($proxyAuth ? $proxyAuth : '')
- .'User-Agent: '.$userAgent."\r\n"
- . ($acceptLanguage ? $acceptLanguage ."\r\n" : '')
- .$xff."\r\n"
- .$via."\r\n"
- .$rangeHeader
- ."Connection: close\r\n"
- ."\r\n";
- fwrite($fsock, $requestHeader);
-
- $streamMetaData = array('timed_out' => false);
- @stream_set_blocking($fsock, true);
-
- if (function_exists('stream_set_timeout'))
- {
- @stream_set_timeout($fsock, $timeout);
- }
- elseif (function_exists('socket_set_timeout'))
- {
- @socket_set_timeout($fsock, $timeout);
- }
-
- // process header
- $status = null;
- $expectRedirect = false;
-
- while(!feof($fsock))
- {
- $line = fgets($fsock, 4096);
-
- $streamMetaData = @stream_get_meta_data($fsock);
- if($streamMetaData['timed_out'])
- {
- if(is_resource($file)) { @fclose($file); }
- @fclose($fsock);
- throw new Exception('Timed out waiting for server response');
- }
-
- // a blank line marks the end of the server response header
- if(rtrim($line, "\r\n") == '')
- {
- break;
- }
-
- // parse first line of server response header
- if(!$status)
- {
- // expect first line to be HTTP response status line, e.g., HTTP/1.1 200 OK
- if(!preg_match('~^HTTP/(\d\.\d)\s+(\d+)(\s*.*)?~', $line, $m))
- {
- if(is_resource($file)) { @fclose($file); }
- @fclose($fsock);
- throw new Exception('Expected server response code. Got '.rtrim($line, "\r\n"));
- }
-
- $status = (integer) $m[2];
-
- // Informational 1xx or Client Error 4xx
- if ($status < 200 || $status >= 400)
- {
- if(is_resource($file)) { @fclose($file); }
- @fclose($fsock);
-
- if (!$getExtendedInfo)
- {
- return false;
- }
- else
- {
- return array('status' => $status);
- }
- }
-
- continue;
- }
-
- // handle redirect
- if(preg_match('/^Location:\s*(.+)/', rtrim($line, "\r\n"), $m))
- {
- if(is_resource($file)) { @fclose($file); }
- @fclose($fsock);
- // Successful 2xx vs Redirect 3xx
- if($status < 300)
- {
- throw new Exception('Unexpected redirect to Location: '.rtrim($line).' for status code '.$status);
- }
- return self::sendHttpRequestBy(
- $method,
- trim($m[1]),
- $timeout,
- $userAgent,
- $destinationPath,
- $file,
- $followDepth+1,
- $acceptLanguage,
- $acceptInvalidSslCertificate = false,
- $byteRange,
- $getExtendedInfo,
- $httpMethod
- );
- }
-
- // save expected content length for later verification
- if(preg_match('/^Content-Length:\s*(\d+)/', $line, $m))
- {
- $contentLength = (integer) $m[1];
- }
-
- self::parseHeaderLine($headers, $line);
- }
-
- if (feof($fsock)
- && $httpMethod != 'HEAD')
- {
- throw new Exception('Unexpected end of transmission');
- }
-
- // process content/body
- $response = '';
-
- while(!feof($fsock))
- {
- $line = fread($fsock, 8192);
-
- $streamMetaData = @stream_get_meta_data($fsock);
- if($streamMetaData['timed_out'])
- {
- if(is_resource($file)) { @fclose($file); }
- @fclose($fsock);
- throw new Exception('Timed out waiting for server response');
- }
-
- $fileLength += Piwik_Common::strlen($line);
-
- if(is_resource($file))
- {
- // save to file
- fwrite($file, $line);
- }
- else
- {
- // concatenate to response string
- $response .= $line;
- }
- }
-
- // determine success or failure
- @fclose(@$fsock);
- }
- else if($method == 'fopen')
- {
- $response = false;
-
- // we make sure the request takes less than a few seconds to fail
- // we create a stream_context (works in php >= 5.2.1)
- // we also set the socket_timeout (for php < 5.2.1)
- $default_socket_timeout = @ini_get('default_socket_timeout');
- @ini_set('default_socket_timeout', $timeout);
-
- $ctx = null;
- if(function_exists('stream_context_create')) {
- $stream_options = array(
- 'http' => array(
- 'header' => 'User-Agent: '.$userAgent."\r\n"
- .($acceptLanguage ? $acceptLanguage."\r\n" : '')
- .$xff."\r\n"
- .$via."\r\n"
- .$rangeHeader,
- 'max_redirects' => 5, // PHP 5.1.0
- 'timeout' => $timeout, // PHP 5.2.1
- )
- );
-
- if(!empty($proxyHost) && !empty($proxyPort))
- {
- $stream_options['http']['proxy'] = 'tcp://'.$proxyHost.':'.$proxyPort;
- $stream_options['http']['request_fulluri'] = true; // required by squid proxy
- if(!empty($proxyUser) && !empty($proxyPassword))
- {
- $stream_options['http']['header'] .= 'Proxy-Authorization: Basic '.base64_encode("$proxyUser:$proxyPassword")."\r\n";
- }
- }
-
- $ctx = stream_context_create($stream_options);
- }
-
- // save to file
- if(is_resource($file))
- {
- $handle = fopen($aUrl, 'rb', false, $ctx);
- while(!feof($handle))
- {
- $response = fread($handle, 8192);
- $fileLength += Piwik_Common::strlen($response);
- fwrite($file, $response);
- }
- fclose($handle);
- }
- else
- {
- $response = file_get_contents($aUrl, 0, $ctx);
- $fileLength = Piwik_Common::strlen($response);
- }
-
- // restore the socket_timeout value
- if(!empty($default_socket_timeout))
- {
- @ini_set('default_socket_timeout', $default_socket_timeout);
- }
- }
- else if($method == 'curl')
- {
- if(!self::isCurlEnabled()) {
- // can be triggered in tests
- throw new Exception("CURL is not enabled in php.ini, but is being used.");
- }
- $ch = @curl_init();
-
- if(!empty($proxyHost) && !empty($proxyPort))
- {
- @curl_setopt($ch, CURLOPT_PROXY, $proxyHost.':'.$proxyPort);
- if(!empty($proxyUser) && !empty($proxyPassword))
- {
- // PROXYAUTH defaults to BASIC
- @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyUser.':'.$proxyPassword);
- }
- }
-
- $curl_options = array(
- // internal to ext/curl
- CURLOPT_BINARYTRANSFER => is_resource($file),
-
- // curl options (sorted oldest to newest)
- CURLOPT_URL => $aUrl,
- CURLOPT_USERAGENT => $userAgent,
- CURLOPT_HTTPHEADER => array(
- $xff,
- $via,
- $rangeHeader,
- $acceptLanguage
- ),
- // only get header info if not saving directly to file
- CURLOPT_HEADER => is_resource($file) ? false : true,
- CURLOPT_CONNECTTIMEOUT => $timeout,
- );
- // Case archive.php is triggering archiving on https:// and the certificate is not valid
- if($acceptInvalidSslCertificate)
- {
- $curl_options += array(
- CURLOPT_SSL_VERIFYHOST => false,
- CURLOPT_SSL_VERIFYPEER => false,
- );
- }
- @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $httpMethod);
- if ($httpMethod == 'HEAD')
- {
- @curl_setopt($ch, CURLOPT_NOBODY, true);
- }
-
- @curl_setopt_array($ch, $curl_options);
- self::configCurlCertificate($ch);
-
-
- /*
- * as of php 5.2.0, CURLOPT_FOLLOWLOCATION can't be set if
- * in safe_mode or open_basedir is set
- */
- if((string)ini_get('safe_mode') == '' && ini_get('open_basedir') == '')
- {
- $curl_options = array(
- // curl options (sorted oldest to newest)
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_MAXREDIRS => 5,
- );
- @curl_setopt_array($ch, $curl_options);
- }
-
- if(is_resource($file))
- {
- // write output directly to file
- @curl_setopt($ch, CURLOPT_FILE, $file);
- }
- else
- {
- // internal to ext/curl
- @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- }
-
- ob_start();
- $response = @curl_exec($ch);
- ob_end_clean();
-
- if($response === true)
- {
- $response = '';
- }
- else if($response === false)
- {
- $errstr = curl_error($ch);
- if($errstr != '')
- {
- throw new Exception('curl_exec: '.$errstr);
- }
- $response = '';
- }
- else
- {
- $header = '';
- // redirects are included in the output html, so we look for the last line that starts w/ HTTP/...
- // to split the response
- while (substr($response, 0, 5) == "HTTP/")
- {
- list($header, $response) = explode("\r\n\r\n", $response, 2);
- }
-
- foreach (explode("\r\n", $header) as $line)
- {
- self::parseHeaderLine($headers, $line);
- }
- }
-
- $contentLength = @curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
- $fileLength = is_resource($file) ? @curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD) : Piwik_Common::strlen($response);
- $status = @curl_getinfo($ch, CURLINFO_HTTP_CODE);
-
- @curl_close($ch);
- unset($ch);
- }
- else
- {
- throw new Exception('Invalid request method: '.$method);
- }
-
- if(is_resource($file))
- {
- fflush($file);
- @fclose($file);
-
- $fileSize = filesize($destinationPath);
- if((($contentLength > 0) && ($fileLength != $contentLength))
- || ($fileSize != $fileLength))
- {
- throw new Exception('File size error: '.$destinationPath.'; expected '.$contentLength.' bytes; received '.$fileLength.' bytes; saved '.$fileSize.' bytes to file');
- }
- return true;
- }
-
- if (!$getExtendedInfo)
- {
- return trim($response);
- }
- else
- {
- return array(
- 'status' => $status,
- 'headers' => $headers,
- 'data' => $response
- );
- }
- }
-
- /**
- * Downloads the next chunk of a specific file. The next chunk's byte range
- * is determined by the existing file's size and the expected file size, which
- * is stored in the piwik_option table before starting a download.
- *
- * Note this function uses the Range HTTP header to accomplish downloading in
- * parts.
- *
- * @param string $url The url to download from.
- * @param string $outputPath The path to the file to save/append to.
- * @param bool $isContinuation True if this is the continuation of a download,
- * or if we're starting a fresh one.
- */
- public static function downloadChunk( $url, $outputPath, $isContinuation )
- {
- // make sure file doesn't already exist if we're starting a new download
- if (!$isContinuation
- && file_exists($outputPath))
- {
- throw new Exception(
- Piwik_Translate('General_DownloadFail_FileExists', "'".$outputPath."'")
- . ' ' . Piwik_Translate('General_DownloadPleaseRemoveExisting'));
- }
-
- // if we're starting a download, get the expected file size & save as an option
- $downloadOption = $outputPath.'_expectedDownloadSize';
- if (!$isContinuation)
- {
- $expectedFileSizeResult = Piwik_Http::sendHttpRequest(
- $url,
- $timeout = 300,
- $userAgent = null,
- $destinationPath = null,
- $followDepth = 0,
- $acceptLanguage = false,
- $byteRange = false,
- $getExtendedInfo = true,
- $httpMethod = 'HEAD'
- );
-
- $expectedFileSize = 0;
- if (isset($expectedFileSizeResult['headers']['Content-Length']))
- {
- $expectedFileSize = (int)$expectedFileSizeResult['headers']['Content-Length'];
- }
-
- if ($expectedFileSize == 0)
- {
- Piwik::log(sprintf("HEAD request for '%s' failed, got following: %s", $url, print_r($expectedFileSizeResult, true)));
- throw new Exception(Piwik_Translate('General_DownloadFail_HttpRequestFail'));
- }
-
- Piwik_SetOption($downloadOption, $expectedFileSize);
- }
- else
- {
- $expectedFileSize = (int)Piwik_GetOption($downloadOption);
- if ($expectedFileSize === false) // sanity check
- {
- throw new Exception("Trying to continue a download that never started?! That's not supposed to happen...");
- }
- }
-
- // if existing file is already big enough, then fail so we don't accidentally overwrite
- // existing DB
- $existingSize = file_exists($outputPath) ? filesize($outputPath) : 0;
- if ($existingSize >= $expectedFileSize)
- {
- throw new Exception(
- Piwik_Translate('General_DownloadFail_FileExistsContinue', "'".$outputPath."'")
- . ' ' . Piwik_Translate('General_DownloadPleaseRemoveExisting'));
- }
-
- // download a chunk of the file
- $result = Piwik_Http::sendHttpRequest(
- $url,
- $timeout = 300,
- $userAgent = null,
- $destinationPath = null,
- $followDepth = 0,
- $acceptLanguage = false,
- $byteRange = array($existingSize, min($existingSize + 1024 * 1024 - 1, $expectedFileSize)),
- $getExtendedInfo = true
- );
-
- if ($result === false
- || $result['status'] < 200
- || $result['status'] > 299)
- {
- $result['data'] = self::truncateStr($result['data'], 1024);
- Piwik::log("Failed to download range '".$byteRange[0]."-".$byteRange[1]
- . "' of file from url '$url'. Got result: ".print_r($result, true));
-
- throw new Exception(Piwik_Translate('General_DownloadFail_HttpRequestFail'));
- }
-
- // write chunk to file
- $f = fopen($outputPath, 'ab');
- fwrite($f, $result['data']);
- fclose($f);
-
- clearstatcache($clear_realpath_cache = true, $outputPath);
- return array(
- 'current_size' => filesize($outputPath),
- 'expected_file_size' => $expectedFileSize,
- );
- }
-
- /**
- * Will configure CURL handle $ch
- * to use local list of Certificate Authorities,
- */
- public static function configCurlCertificate( &$ch )
- {
- if (file_exists(PIWIK_INCLUDE_PATH . '/core/DataFiles/cacert.pem'))
- {
- @curl_setopt($ch, CURLOPT_CAINFO, PIWIK_INCLUDE_PATH . '/core/DataFiles/cacert.pem');
- }
- }
-
- public static function getUserAgent()
- {
- return !empty($_SERVER['HTTP_USER_AGENT'])
- ? $_SERVER['HTTP_USER_AGENT']
- : 'Piwik/' . Piwik_Version::VERSION;
- }
-
- /**
- * Fetch the file at $url in the destination $destinationPath
- *
- * @param string $url
- * @param string $destinationPath
- * @param int $tries
- * @throws Exception
- * @return bool true on success, throws Exception on failure
- */
- static public function fetchRemoteFile($url, $destinationPath = null, $tries = 0)
- {
- @ignore_user_abort(true);
- Piwik::setMaxExecutionTime(0);
- return self::sendHttpRequest($url, 10, 'Update', $destinationPath, $tries);
- }
-
- /**
- * Utility function, parses an HTTP header line into key/value & sets header
- * array with them.
- *
- * @param array $headers
- * @param string $line
- */
- private static function parseHeaderLine( &$headers, $line )
- {
- $parts = explode(':', $line, 2);
- if (count($parts) == 1)
- {
- return;
- }
-
- list($name, $value) = $parts;
- $headers[trim($name)] = trim($value);
- }
-
- /**
- * Utility function that truncates a string to an arbitrary limit.
- *
- * @param string $str The string to truncate.
- * @param int $limit The maximum length of the truncated string.
- * @return string
- */
- private static function truncateStr( $str, $limit )
- {
- if (strlen($str) > $limit)
- {
- return substr($str, 0, $limit).'...';
- }
- return $str;
- }
+ /**
+ * Get "best" available transport method for sendHttpRequest() calls.
+ *
+ * @return string
+ */
+ static public function getTransportMethod()
+ {
+ $method = 'curl';
+ if (!self::isCurlEnabled()) {
+ $method = 'fopen';
+ if (@ini_get('allow_url_fopen') != '1') {
+ $method = 'socket';
+ if (!self::isSocketEnabled()) {
+ return null;
+ }
+ }
+ }
+ return $method;
+ }
+
+ protected static function isSocketEnabled()
+ {
+ return function_exists('fsockopen');
+ }
+
+ protected static function isCurlEnabled()
+ {
+ return function_exists('curl_init');
+ }
+
+ /**
+ * Sends http request ensuring the request will fail before $timeout seconds
+ *
+ * If no $destinationPath is specified, the trimmed response (without header) is returned as a string.
+ * If a $destinationPath is specified, the response (without header) is saved to a file.
+ *
+ * @param string $aUrl
+ * @param int $timeout
+ * @param string $userAgent
+ * @param string $destinationPath
+ * @param int $followDepth
+ * @param bool $acceptLanguage
+ * @param array $byteRange For Range: header. Should be two element array of bytes, eg, array(0, 1024)
+ * Doesn't work w/ fopen method.
+ * @param bool $getExtendedInfo True to return status code, headers & response, false if just response.
+ * @param string $httpMethod The HTTP method to use. Defaults to 'GET'.
+ * @throws Exception
+ * @return bool true (or string) on success; false on HTTP response error code (1xx or 4xx)
+ */
+ static public function sendHttpRequest($aUrl, $timeout, $userAgent = null, $destinationPath = null, $followDepth = 0, $acceptLanguage = false, $byteRange = false, $getExtendedInfo = false, $httpMethod = 'GET'
+ )
+ {
+ // create output file
+ $file = null;
+ if ($destinationPath) {
+ // Ensure destination directory exists
+ Piwik_Common::mkdir(dirname($destinationPath));
+ if (($file = @fopen($destinationPath, 'wb')) === false || !is_resource($file)) {
+ throw new Exception('Error while creating the file: ' . $destinationPath);
+ }
+ }
+
+ $acceptLanguage = $acceptLanguage ? 'Accept-Language: ' . $acceptLanguage : '';
+ return self::sendHttpRequestBy(self::getTransportMethod(), $aUrl, $timeout, $userAgent, $destinationPath, $file, $followDepth, $acceptLanguage, $acceptInvalidSslCertificate = false, $byteRange, $getExtendedInfo, $httpMethod);
+ }
+
+ /**
+ * Sends http request using the specified transport method
+ *
+ * @param string $method
+ * @param string $aUrl
+ * @param int $timeout
+ * @param string $userAgent
+ * @param string $destinationPath
+ * @param resource $file
+ * @param int $followDepth
+ * @param bool|string $acceptLanguage Accept-language header
+ * @param bool $acceptInvalidSslCertificate Only used with $method == 'curl'. If set to true (NOT recommended!) the SSL certificate will not be checked
+ * @param array $byteRange For Range: header. Should be two element array of bytes, eg, array(0, 1024)
+ * Doesn't work w/ fopen method.
+ * @param bool $getExtendedInfo True to return status code, headers & response, false if just response.
+ * @param string $httpMethod The HTTP method to use. Defaults to 'GET'.
+ * @throws Exception
+ * @return bool true (or string/array) on success; false on HTTP response error code (1xx or 4xx)
+ */
+ static public function sendHttpRequestBy(
+ $method = 'socket',
+ $aUrl,
+ $timeout,
+ $userAgent = null,
+ $destinationPath = null,
+ $file = null,
+ $followDepth = 0,
+ $acceptLanguage = false,
+ $acceptInvalidSslCertificate = false,
+ $byteRange = false,
+ $getExtendedInfo = false,
+ $httpMethod = 'GET'
+ )
+ {
+ if ($followDepth > 5) {
+ throw new Exception('Too many redirects (' . $followDepth . ')');
+ }
+
+ $contentLength = 0;
+ $fileLength = 0;
+
+ // Piwik services behave like a proxy, so we should act like one.
+ $xff = 'X-Forwarded-For: '
+ . (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] . ',' : '')
+ . Piwik_IP::getIpFromHeader();
+
+ if (empty($userAgent)) {
+ $userAgent = self::getUserAgent();
+ }
+
+ $via = 'Via: '
+ . (isset($_SERVER['HTTP_VIA']) && !empty($_SERVER['HTTP_VIA']) ? $_SERVER['HTTP_VIA'] . ', ' : '')
+ . Piwik_Version::VERSION . ' '
+ . ($userAgent ? " ($userAgent)" : '');
+
+ // range header
+ $rangeHeader = '';
+ if (!empty($byteRange)) {
+ $rangeHeader = 'Range: bytes=' . $byteRange[0] . '-' . $byteRange[1] . "\r\n";
+ }
+
+ // proxy configuration
+ $proxyHost = Piwik_Config::getInstance()->proxy['host'];
+ $proxyPort = Piwik_Config::getInstance()->proxy['port'];
+ $proxyUser = Piwik_Config::getInstance()->proxy['username'];
+ $proxyPassword = Piwik_Config::getInstance()->proxy['password'];
+
+ // other result data
+ $status = null;
+ $headers = array();
+
+ if ($method == 'socket') {
+ if (!self::isSocketEnabled()) {
+ // can be triggered in tests
+ throw new Exception("HTTP socket support is not enabled (php function fsockopen is not available) ");
+ }
+ // initialization
+ $url = @parse_url($aUrl);
+ if ($url === false || !isset($url['scheme'])) {
+ throw new Exception('Malformed URL: ' . $aUrl);
+ }
+
+ if ($url['scheme'] != 'http') {
+ throw new Exception('Invalid protocol/scheme: ' . $url['scheme']);
+ }
+ $host = $url['host'];
+ $port = isset($url['port)']) ? $url['port'] : 80;
+ $path = isset($url['path']) ? $url['path'] : '/';
+ if (isset($url['query'])) {
+ $path .= '?' . $url['query'];
+ }
+ $errno = null;
+ $errstr = null;
+
+ if ((!empty($proxyHost) && !empty($proxyPort))
+ || !empty($byteRange)
+ ) {
+ $httpVer = '1.1';
+ } else {
+ $httpVer = '1.0';
+ }
+
+ $proxyAuth = null;
+ if (!empty($proxyHost) && !empty($proxyPort)) {
+ $connectHost = $proxyHost;
+ $connectPort = $proxyPort;
+ if (!empty($proxyUser) && !empty($proxyPassword)) {
+ $proxyAuth = 'Proxy-Authorization: Basic ' . base64_encode("$proxyUser:$proxyPassword") . "\r\n";
+ }
+ $requestHeader = "$httpMethod $aUrl HTTP/$httpVer\r\n";
+ } else {
+ $connectHost = $host;
+ $connectPort = $port;
+ $requestHeader = "$httpMethod $path HTTP/$httpVer\r\n";
+ }
+
+ // connection attempt
+ if (($fsock = @fsockopen($connectHost, $connectPort, $errno, $errstr, $timeout)) === false || !is_resource($fsock)) {
+ if (is_resource($file)) {
+ @fclose($file);
+ }
+ throw new Exception("Error while connecting to: $host. Please try again later. $errstr");
+ }
+
+ // send HTTP request header
+ $requestHeader .=
+ "Host: $host" . ($port != 80 ? ':' . $port : '') . "\r\n"
+ . ($proxyAuth ? $proxyAuth : '')
+ . 'User-Agent: ' . $userAgent . "\r\n"
+ . ($acceptLanguage ? $acceptLanguage . "\r\n" : '')
+ . $xff . "\r\n"
+ . $via . "\r\n"
+ . $rangeHeader
+ . "Connection: close\r\n"
+ . "\r\n";
+ fwrite($fsock, $requestHeader);
+
+ $streamMetaData = array('timed_out' => false);
+ @stream_set_blocking($fsock, true);
+
+ if (function_exists('stream_set_timeout')) {
+ @stream_set_timeout($fsock, $timeout);
+ } elseif (function_exists('socket_set_timeout')) {
+ @socket_set_timeout($fsock, $timeout);
+ }
+
+ // process header
+ $status = null;
+ $expectRedirect = false;
+
+ while (!feof($fsock)) {
+ $line = fgets($fsock, 4096);
+
+ $streamMetaData = @stream_get_meta_data($fsock);
+ if ($streamMetaData['timed_out']) {
+ if (is_resource($file)) {
+ @fclose($file);
+ }
+ @fclose($fsock);
+ throw new Exception('Timed out waiting for server response');
+ }
+
+ // a blank line marks the end of the server response header
+ if (rtrim($line, "\r\n") == '') {
+ break;
+ }
+
+ // parse first line of server response header
+ if (!$status) {
+ // expect first line to be HTTP response status line, e.g., HTTP/1.1 200 OK
+ if (!preg_match('~^HTTP/(\d\.\d)\s+(\d+)(\s*.*)?~', $line, $m)) {
+ if (is_resource($file)) {
+ @fclose($file);
+ }
+ @fclose($fsock);
+ throw new Exception('Expected server response code. Got ' . rtrim($line, "\r\n"));
+ }
+
+ $status = (integer)$m[2];
+
+ // Informational 1xx or Client Error 4xx
+ if ($status < 200 || $status >= 400) {
+ if (is_resource($file)) {
+ @fclose($file);
+ }
+ @fclose($fsock);
+
+ if (!$getExtendedInfo) {
+ return false;
+ } else {
+ return array('status' => $status);
+ }
+ }
+
+ continue;
+ }
+
+ // handle redirect
+ if (preg_match('/^Location:\s*(.+)/', rtrim($line, "\r\n"), $m)) {
+ if (is_resource($file)) {
+ @fclose($file);
+ }
+ @fclose($fsock);
+ // Successful 2xx vs Redirect 3xx
+ if ($status < 300) {
+ throw new Exception('Unexpected redirect to Location: ' . rtrim($line) . ' for status code ' . $status);
+ }
+ return self::sendHttpRequestBy(
+ $method,
+ trim($m[1]),
+ $timeout,
+ $userAgent,
+ $destinationPath,
+ $file,
+ $followDepth + 1,
+ $acceptLanguage,
+ $acceptInvalidSslCertificate = false,
+ $byteRange,
+ $getExtendedInfo,
+ $httpMethod
+ );
+ }
+
+ // save expected content length for later verification
+ if (preg_match('/^Content-Length:\s*(\d+)/', $line, $m)) {
+ $contentLength = (integer)$m[1];
+ }
+
+ self::parseHeaderLine($headers, $line);
+ }
+
+ if (feof($fsock)
+ && $httpMethod != 'HEAD'
+ ) {
+ throw new Exception('Unexpected end of transmission');
+ }
+
+ // process content/body
+ $response = '';
+
+ while (!feof($fsock)) {
+ $line = fread($fsock, 8192);
+
+ $streamMetaData = @stream_get_meta_data($fsock);
+ if ($streamMetaData['timed_out']) {
+ if (is_resource($file)) {
+ @fclose($file);
+ }
+ @fclose($fsock);
+ throw new Exception('Timed out waiting for server response');
+ }
+
+ $fileLength += Piwik_Common::strlen($line);
+
+ if (is_resource($file)) {
+ // save to file
+ fwrite($file, $line);
+ } else {
+ // concatenate to response string
+ $response .= $line;
+ }
+ }
+
+ // determine success or failure
+ @fclose(@$fsock);
+ } else if ($method == 'fopen') {
+ $response = false;
+
+ // we make sure the request takes less than a few seconds to fail
+ // we create a stream_context (works in php >= 5.2.1)
+ // we also set the socket_timeout (for php < 5.2.1)
+ $default_socket_timeout = @ini_get('default_socket_timeout');
+ @ini_set('default_socket_timeout', $timeout);
+
+ $ctx = null;
+ if (function_exists('stream_context_create')) {
+ $stream_options = array(
+ 'http' => array(
+ 'header' => 'User-Agent: ' . $userAgent . "\r\n"
+ . ($acceptLanguage ? $acceptLanguage . "\r\n" : '')
+ . $xff . "\r\n"
+ . $via . "\r\n"
+ . $rangeHeader,
+ 'max_redirects' => 5, // PHP 5.1.0
+ 'timeout' => $timeout, // PHP 5.2.1
+ )
+ );
+
+ if (!empty($proxyHost) && !empty($proxyPort)) {
+ $stream_options['http']['proxy'] = 'tcp://' . $proxyHost . ':' . $proxyPort;
+ $stream_options['http']['request_fulluri'] = true; // required by squid proxy
+ if (!empty($proxyUser) && !empty($proxyPassword)) {
+ $stream_options['http']['header'] .= 'Proxy-Authorization: Basic ' . base64_encode("$proxyUser:$proxyPassword") . "\r\n";
+ }
+ }
+
+ $ctx = stream_context_create($stream_options);
+ }
+
+ // save to file
+ if (is_resource($file)) {
+ $handle = fopen($aUrl, 'rb', false, $ctx);
+ while (!feof($handle)) {
+ $response = fread($handle, 8192);
+ $fileLength += Piwik_Common::strlen($response);
+ fwrite($file, $response);
+ }
+ fclose($handle);
+ } else {
+ $response = file_get_contents($aUrl, 0, $ctx);
+ $fileLength = Piwik_Common::strlen($response);
+ }
+
+ // restore the socket_timeout value
+ if (!empty($default_socket_timeout)) {
+ @ini_set('default_socket_timeout', $default_socket_timeout);
+ }
+ } else if ($method == 'curl') {
+ if (!self::isCurlEnabled()) {
+ // can be triggered in tests
+ throw new Exception("CURL is not enabled in php.ini, but is being used.");
+ }
+ $ch = @curl_init();
+
+ if (!empty($proxyHost) && !empty($proxyPort)) {
+ @curl_setopt($ch, CURLOPT_PROXY, $proxyHost . ':' . $proxyPort);
+ if (!empty($proxyUser) && !empty($proxyPassword)) {
+ // PROXYAUTH defaults to BASIC
+ @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyUser . ':' . $proxyPassword);
+ }
+ }
+
+ $curl_options = array(
+ // internal to ext/curl
+ CURLOPT_BINARYTRANSFER => is_resource($file),
+
+ // curl options (sorted oldest to newest)
+ CURLOPT_URL => $aUrl,
+ CURLOPT_USERAGENT => $userAgent,
+ CURLOPT_HTTPHEADER => array(
+ $xff,
+ $via,
+ $rangeHeader,
+ $acceptLanguage
+ ),
+ // only get header info if not saving directly to file
+ CURLOPT_HEADER => is_resource($file) ? false : true,
+ CURLOPT_CONNECTTIMEOUT => $timeout,
+ );
+ // Case archive.php is triggering archiving on https:// and the certificate is not valid
+ if ($acceptInvalidSslCertificate) {
+ $curl_options += array(
+ CURLOPT_SSL_VERIFYHOST => false,
+ CURLOPT_SSL_VERIFYPEER => false,
+ );
+ }
+ @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $httpMethod);
+ if ($httpMethod == 'HEAD') {
+ @curl_setopt($ch, CURLOPT_NOBODY, true);
+ }
+
+ @curl_setopt_array($ch, $curl_options);
+ self::configCurlCertificate($ch);
+
+
+ /*
+ * as of php 5.2.0, CURLOPT_FOLLOWLOCATION can't be set if
+ * in safe_mode or open_basedir is set
+ */
+ if ((string)ini_get('safe_mode') == '' && ini_get('open_basedir') == '') {
+ $curl_options = array(
+ // curl options (sorted oldest to newest)
+ CURLOPT_FOLLOWLOCATION => true,
+ CURLOPT_MAXREDIRS => 5,
+ );
+ @curl_setopt_array($ch, $curl_options);
+ }
+
+ if (is_resource($file)) {
+ // write output directly to file
+ @curl_setopt($ch, CURLOPT_FILE, $file);
+ } else {
+ // internal to ext/curl
+ @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ }
+
+ ob_start();
+ $response = @curl_exec($ch);
+ ob_end_clean();
+
+ if ($response === true) {
+ $response = '';
+ } else if ($response === false) {
+ $errstr = curl_error($ch);
+ if ($errstr != '') {
+ throw new Exception('curl_exec: ' . $errstr);
+ }
+ $response = '';
+ } else {
+ $header = '';
+ // redirects are included in the output html, so we look for the last line that starts w/ HTTP/...
+ // to split the response
+ while (substr($response, 0, 5) == "HTTP/") {
+ list($header, $response) = explode("\r\n\r\n", $response, 2);
+ }
+
+ foreach (explode("\r\n", $header) as $line) {
+ self::parseHeaderLine($headers, $line);
+ }
+ }
+
+ $contentLength = @curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
+ $fileLength = is_resource($file) ? @curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD) : Piwik_Common::strlen($response);
+ $status = @curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+ @curl_close($ch);
+ unset($ch);
+ } else {
+ throw new Exception('Invalid request method: ' . $method);
+ }
+
+ if (is_resource($file)) {
+ fflush($file);
+ @fclose($file);
+
+ $fileSize = filesize($destinationPath);
+ if ((($contentLength > 0) && ($fileLength != $contentLength))
+ || ($fileSize != $fileLength)
+ ) {
+ throw new Exception('File size error: ' . $destinationPath . '; expected ' . $contentLength . ' bytes; received ' . $fileLength . ' bytes; saved ' . $fileSize . ' bytes to file');
+ }
+ return true;
+ }
+
+ if (!$getExtendedInfo) {
+ return trim($response);
+ } else {
+ return array(
+ 'status' => $status,
+ 'headers' => $headers,
+ 'data' => $response
+ );
+ }
+ }
+
+ /**
+ * Downloads the next chunk of a specific file. The next chunk's byte range
+ * is determined by the existing file's size and the expected file size, which
+ * is stored in the piwik_option table before starting a download.
+ *
+ * Note this function uses the Range HTTP header to accomplish downloading in
+ * parts.
+ *
+ * @param string $url The url to download from.
+ * @param string $outputPath The path to the file to save/append to.
+ * @param bool $isContinuation True if this is the continuation of a download,
+ * or if we're starting a fresh one.
+ */
+ public static function downloadChunk($url, $outputPath, $isContinuation)
+ {
+ // make sure file doesn't already exist if we're starting a new download
+ if (!$isContinuation
+ && file_exists($outputPath)
+ ) {
+ throw new Exception(
+ Piwik_Translate('General_DownloadFail_FileExists', "'" . $outputPath . "'")
+ . ' ' . Piwik_Translate('General_DownloadPleaseRemoveExisting'));
+ }
+
+ // if we're starting a download, get the expected file size & save as an option
+ $downloadOption = $outputPath . '_expectedDownloadSize';
+ if (!$isContinuation) {
+ $expectedFileSizeResult = Piwik_Http::sendHttpRequest(
+ $url,
+ $timeout = 300,
+ $userAgent = null,
+ $destinationPath = null,
+ $followDepth = 0,
+ $acceptLanguage = false,
+ $byteRange = false,
+ $getExtendedInfo = true,
+ $httpMethod = 'HEAD'
+ );
+
+ $expectedFileSize = 0;
+ if (isset($expectedFileSizeResult['headers']['Content-Length'])) {
+ $expectedFileSize = (int)$expectedFileSizeResult['headers']['Content-Length'];
+ }
+
+ if ($expectedFileSize == 0) {
+ Piwik::log(sprintf("HEAD request for '%s' failed, got following: %s", $url, print_r($expectedFileSizeResult, true)));
+ throw new Exception(Piwik_Translate('General_DownloadFail_HttpRequestFail'));
+ }
+
+ Piwik_SetOption($downloadOption, $expectedFileSize);
+ } else {
+ $expectedFileSize = (int)Piwik_GetOption($downloadOption);
+ if ($expectedFileSize === false) // sanity check
+ {
+ throw new Exception("Trying to continue a download that never started?! That's not supposed to happen...");
+ }
+ }
+
+ // if existing file is already big enough, then fail so we don't accidentally overwrite
+ // existing DB
+ $existingSize = file_exists($outputPath) ? filesize($outputPath) : 0;
+ if ($existingSize >= $expectedFileSize) {
+ throw new Exception(
+ Piwik_Translate('General_DownloadFail_FileExistsContinue', "'" . $outputPath . "'")
+ . ' ' . Piwik_Translate('General_DownloadPleaseRemoveExisting'));
+ }
+
+ // download a chunk of the file
+ $result = Piwik_Http::sendHttpRequest(
+ $url,
+ $timeout = 300,
+ $userAgent = null,
+ $destinationPath = null,
+ $followDepth = 0,
+ $acceptLanguage = false,
+ $byteRange = array($existingSize, min($existingSize + 1024 * 1024 - 1, $expectedFileSize)),
+ $getExtendedInfo = true
+ );
+
+ if ($result === false
+ || $result['status'] < 200
+ || $result['status'] > 299
+ ) {
+ $result['data'] = self::truncateStr($result['data'], 1024);
+ Piwik::log("Failed to download range '" . $byteRange[0] . "-" . $byteRange[1]
+ . "' of file from url '$url'. Got result: " . print_r($result, true));
+
+ throw new Exception(Piwik_Translate('General_DownloadFail_HttpRequestFail'));
+ }
+
+ // write chunk to file
+ $f = fopen($outputPath, 'ab');
+ fwrite($f, $result['data']);
+ fclose($f);
+
+ clearstatcache($clear_realpath_cache = true, $outputPath);
+ return array(
+ 'current_size' => filesize($outputPath),
+ 'expected_file_size' => $expectedFileSize,
+ );
+ }
+
+ /**
+ * Will configure CURL handle $ch
+ * to use local list of Certificate Authorities,
+ */
+ public static function configCurlCertificate(&$ch)
+ {
+ if (file_exists(PIWIK_INCLUDE_PATH . '/core/DataFiles/cacert.pem')) {
+ @curl_setopt($ch, CURLOPT_CAINFO, PIWIK_INCLUDE_PATH . '/core/DataFiles/cacert.pem');
+ }
+ }
+
+ public static function getUserAgent()
+ {
+ return !empty($_SERVER['HTTP_USER_AGENT'])
+ ? $_SERVER['HTTP_USER_AGENT']
+ : 'Piwik/' . Piwik_Version::VERSION;
+ }
+
+ /**
+ * Fetch the file at $url in the destination $destinationPath
+ *
+ * @param string $url
+ * @param string $destinationPath
+ * @param int $tries
+ * @throws Exception
+ * @return bool true on success, throws Exception on failure
+ */
+ static public function fetchRemoteFile($url, $destinationPath = null, $tries = 0)
+ {
+ @ignore_user_abort(true);
+ Piwik::setMaxExecutionTime(0);
+ return self::sendHttpRequest($url, 10, 'Update', $destinationPath, $tries);
+ }
+
+ /**
+ * Utility function, parses an HTTP header line into key/value & sets header
+ * array with them.
+ *
+ * @param array $headers
+ * @param string $line
+ */
+ private static function parseHeaderLine(&$headers, $line)
+ {
+ $parts = explode(':', $line, 2);
+ if (count($parts) == 1) {
+ return;
+ }
+
+ list($name, $value) = $parts;
+ $headers[trim($name)] = trim($value);
+ }
+
+ /**
+ * Utility function that truncates a string to an arbitrary limit.
+ *
+ * @param string $str The string to truncate.
+ * @param int $limit The maximum length of the truncated string.
+ * @return string
+ */
+ private static function truncateStr($str, $limit)
+ {
+ if (strlen($str) > $limit) {
+ return substr($str, 0, $limit) . '...';
+ }
+ return $str;
+ }
}
diff --git a/core/IP.php b/core/IP.php
index f830301814..33cce5cdbb 100644
--- a/core/IP.php
+++ b/core/IP.php
@@ -9,23 +9,27 @@
* @package Piwik
*/
-if(Piwik_Common::isWindows() || !function_exists('inet_ntop')) {
- function _inet_ntop($in_addr) {
- return php_compat_inet_ntop($in_addr);
- }
+if (Piwik_Common::isWindows() || !function_exists('inet_ntop')) {
+ function _inet_ntop($in_addr)
+ {
+ return php_compat_inet_ntop($in_addr);
+ }
} else {
- function _inet_ntop($in_addr) {
- return inet_ntop($in_addr);
- }
+ function _inet_ntop($in_addr)
+ {
+ return inet_ntop($in_addr);
+ }
}
-if(Piwik_Common::isWindows() || !function_exists('inet_pton')) {
- function _inet_pton($address) {
- return php_compat_inet_pton($address);
- }
+if (Piwik_Common::isWindows() || !function_exists('inet_pton')) {
+ function _inet_pton($address)
+ {
+ return php_compat_inet_pton($address);
+ }
} else {
- function _inet_pton($address) {
- return inet_pton($address);
- }
+ function _inet_pton($address)
+ {
+ return inet_pton($address);
+ }
}
/**
@@ -47,434 +51,397 @@ if(Piwik_Common::isWindows() || !function_exists('inet_pton')) {
*/
class Piwik_IP
{
- const MAPPED_IPv4_START = '::ffff:';
-
- /**
- * Sanitize human-readable IP address.
- *
- * @param string $ipString IP address
- * @return string|false
- */
- static public function sanitizeIp($ipString)
- {
- $ipString = trim($ipString);
-
- // CIDR notation, A.B.C.D/E
- $posSlash = strrpos($ipString, '/');
- if($posSlash !== false)
- {
- $ipString = substr($ipString, 0, $posSlash);
- }
-
- $posColon = strrpos($ipString, ':');
- $posDot = strrpos($ipString, '.');
- if($posColon !== false)
- {
- // IPv6 address with port, [A:B:C:D:E:F:G:H]:EEEE
- $posRBrac = strrpos($ipString, ']');
- if($posRBrac !== false && $ipString[0] == '[')
- {
- $ipString = substr($ipString, 1, $posRBrac - 1);
- }
-
- if($posDot !== false)
- {
- // IPv4 address with port, A.B.C.D:EEEE
- if($posColon > $posDot)
- {
- $ipString = substr($ipString, 0, $posColon);
- }
- // else: Dotted quad IPv6 address, A:B:C:D:E:F:G.H.I.J
- }
- else if(strpos($ipString, ':') === $posColon)
- {
- $ipString = substr($ipString, 0, $posColon);
- }
- // else: IPv6 address, A:B:C:D:E:F:G:H
- }
- // else: IPv4 address, A.B.C.D
-
- return $ipString;
- }
-
- /**
- * Sanitize human-readable (user-supplied) IP address range.
- *
- * Accepts the following formats for $ipRange:
- * - single IPv4 address, e.g., 127.0.0.1
- * - single IPv6 address, e.g., ::1/128
- * - IPv4 block using CIDR notation, e.g., 192.168.0.0/22 represents the IPv4 addresses from 192.168.0.0 to 192.168.3.255
- * - IPv6 block using CIDR notation, e.g., 2001:DB8::/48 represents the IPv6 addresses from 2001:DB8:0:0:0:0:0:0 to 2001:DB8:0:FFFF:FFFF:FFFF:FFFF:FFFF
- * - wildcards, e.g., 192.168.0.*
- *
- * @param string $ipRangeString IP address range
- * @return string|false IP address range in CIDR notation
- */
- static public function sanitizeIpRange($ipRangeString)
- {
- $ipRangeString = trim($ipRangeString);
- if(empty($ipRangeString))
- {
- return false;
- }
-
- // IPv4 address with wildcards '*'
- if(strpos($ipRangeString, '*') !== false)
- {
- if(preg_match('~(^|\.)\*\.\d+(\.|$)~D', $ipRangeString))
- {
- return false;
- }
-
- $bits = 32 - 8 * substr_count($ipRangeString, '*');
- $ipRangeString = str_replace('*', '0', $ipRangeString);
- }
-
- // CIDR
- if(($pos = strpos($ipRangeString, '/')) !== false)
- {
- $bits = substr($ipRangeString, $pos + 1);
- $ipRangeString = substr($ipRangeString, 0, $pos);
- }
-
- // single IP
- if(($ip = @_inet_pton($ipRangeString)) === false)
- return false;
-
- $maxbits = Piwik_Common::strlen($ip) * 8;
- if(!isset($bits))
- $bits = $maxbits;
-
- if($bits < 0 || $bits > $maxbits)
- {
- return false;
- }
-
- return "$ipRangeString/$bits";
- }
-
- /**
- * Convert presentation format IP address to network address format
- *
- * @param string $ipString IP address, either IPv4 or IPv6, e.g., "127.0.0.1"
- * @return string Binary-safe string, e.g., "\x7F\x00\x00\x01"
- */
- static public function P2N($ipString)
- {
- // use @inet_pton() because it throws an exception and E_WARNING on invalid input
- $ip = @_inet_pton($ipString);
- return $ip === false ? "\x00\x00\x00\x00" : $ip;
- }
-
- /**
- * Convert network address format to presentation format
- *
- * @see prettyPrint()
- *
- * @param string $ip IP address in network address format
- * @return string IP address in presentation format
- */
- static public function N2P($ip)
- {
- // use @inet_ntop() because it throws an exception and E_WARNING on invalid input
- $ipStr = @_inet_ntop($ip);
- return $ipStr === false ? '0.0.0.0' : $ipStr;
- }
-
- /**
- * Alias for N2P()
- *
- * @param string $ip IP address in network address format
- * @return string IP address in presentation format
- */
- static public function prettyPrint($ip)
- {
- return self::N2P($ip);
- }
-
- /**
- * Is this an IPv4, IPv4-compat, or IPv4-mapped address?
- *
- * @param string $ip IP address in network address format
- * @return bool True if IPv4, else false
- */
- static public function isIPv4($ip)
- {
- // in case mbstring overloads strlen and substr functions
- $strlen = function_exists('mb_orig_strlen') ? 'mb_orig_strlen' : 'strlen';
- $substr = function_exists('mb_orig_substr') ? 'mb_orig_substr' : 'substr';
-
- // IPv4
- if($strlen($ip) == 4)
- {
- return true;
- }
-
- // IPv6 - transitional address?
- if($strlen($ip) == 16)
- {
- if(substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff", 0, 12) === 0
- || substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0, 12) === 0)
- {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Convert IP address (in network address format) to presentation format.
- * This is a backward compatibility function for code that only expects
- * IPv4 addresses (i.e., doesn't support IPv6).
- *
- * This function does not support the long (or its string representation)
- * returned by the built-in ip2long() function, from Piwik 1.3 and earlier.
- *
- * @param string $ip IPv4 address in network address format
- * @return string IP address in presentation format
- */
- static public function long2ip($ip)
- {
- // IPv4
- if(Piwik_Common::strlen($ip) == 4)
- {
- return self::N2P($ip);
- }
-
- // IPv6 - transitional address?
- if(Piwik_Common::strlen($ip) == 16)
- {
- if(substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff", 0, 12) === 0
- || substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0, 12) === 0)
- {
- // remap 128-bit IPv4-mapped and IPv4-compat addresses
- return self::N2P(Piwik_Common::substr($ip, 12));
- }
- }
-
- return '0.0.0.0';
- }
-
- /**
- * Returns true if $ip is an IPv6 address, false if otherwise. This function does
- * a naive check. It assumes that whatever format $ip is in, it is well-formed.
- *
- * @param string $ip
- * @return bool
- */
- static public function isIPv6( $ip )
- {
- return strpos($ip, ':') !== false;
- }
-
- /**
- * Returns true if $ip is a IPv4 mapped address, false if otherwise.
- *
- * @param string $ip
- * @return bool
- */
- static public function isMappedIPv4($ip)
- {
- return substr($ip, 0, strlen(self::MAPPED_IPv4_START)) === self::MAPPED_IPv4_START;
- }
-
- /**
- * Returns
- */
- static public function getIPv4FromMappedIPv6($ip)
- {
- return substr($ip, strlen(self::MAPPED_IPv4_START));
- }
-
- /**
- * Get low and high IP addresses for a specified range.
- *
- * @param array $ipRange An IP address range in presentation format
- * @return array|false Array ($lowIp, $highIp) in network address format, or false if failure
- */
- static public function getIpsForRange($ipRange)
- {
- if(strpos($ipRange, '/') === false)
- {
- $ipRange = self::sanitizeIpRange($ipRange);
- }
- $pos = strpos($ipRange, '/');
-
- $bits = substr($ipRange, $pos + 1);
- $range = substr($ipRange, 0, $pos);
- $high = $low = @_inet_pton($range);
- if($low === false)
- {
- return false;
- }
-
- $lowLen = Piwik_Common::strlen($low);
- $i = $lowLen - 1;
- $bits = $lowLen * 8 - $bits;
-
- for($n = (int)($bits / 8); $n > 0; $n--, $i--)
- {
- $low[$i] = chr(0);
- $high[$i] = chr(255);
- }
-
- $n = $bits % 8;
- if($n)
- {
- $low[$i] = chr(ord($low[$i]) & ~((1 << $n) - 1));
- $high[$i] = chr(ord($high[$i]) | ((1 << $n) - 1));
- }
-
- return array($low, $high);
- }
-
- /**
- * Determines if an IP address is in a specified IP address range.
- *
- * An IPv4-mapped address should be range checked with an IPv4-mapped address range.
- *
- * @param string $ip IP address in network address format
- * @param array $ipRanges List of IP address ranges
- * @return bool True if in any of the specified IP address ranges; else false.
- */
- static public function isIpInRange($ip, $ipRanges)
- {
- $ipLen = Piwik_Common::strlen($ip);
- if(empty($ip) || empty($ipRanges) || ($ipLen != 4 && $ipLen != 16))
- {
- return false;
- }
-
- foreach($ipRanges as $range)
- {
- if(is_array($range))
- {
- // already split into low/high IP addresses
- $range[0] = self::P2N($range[0]);
- $range[1] = self::P2N($range[1]);
- }
- else
- {
- // expect CIDR format but handle some variations
- $range = self::getIpsForRange($range);
- }
- if($range === false)
- {
- continue;
- }
-
- $low = $range[0];
- $high = $range[1];
- if(Piwik_Common::strlen($low) != $ipLen)
- {
- continue;
- }
-
- // binary-safe string comparison
- if($ip >= $low && $ip <= $high)
- {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Returns the best possible IP of the current user, in the format A.B.C.D
- * For example, this could be the proxy client's IP address.
- *
- * @return string IP address in presentation format
- */
- static public function getIpFromHeader()
- {
- $clientHeaders = @Piwik_Config::getInstance()->General['proxy_client_headers'];
- if(!is_array($clientHeaders))
- {
- $clientHeaders = array();
- }
-
- $default = '0.0.0.0';
- if(isset($_SERVER['REMOTE_ADDR']))
- {
- $default = $_SERVER['REMOTE_ADDR'];
- }
-
- $ipString = self::getNonProxyIpFromHeader($default, $clientHeaders);
- return self::sanitizeIp($ipString);
- }
-
- /**
- * Returns a non-proxy IP address from header
- *
- * @param string $default Default value to return if no matching proxy header
- * @param array $proxyHeaders List of proxy headers
- * @return string
- */
- static public function getNonProxyIpFromHeader($default, $proxyHeaders)
- {
- $proxyIps = @Piwik_Config::getInstance()->General['proxy_ips'];
- if(!is_array($proxyIps))
- {
- $proxyIps = array();
- }
- $proxyIps[] = $default;
-
- // examine proxy headers
- foreach($proxyHeaders as $proxyHeader)
- {
- if(!empty($_SERVER[$proxyHeader]))
- {
- $proxyIp = self::getLastIpFromList($_SERVER[$proxyHeader], $proxyIps);
- if(strlen($proxyIp) && stripos($proxyIp, 'unknown') === false)
- {
- return $proxyIp;
- }
- }
- }
-
- return $default;
- }
-
- /**
- * Returns the last IP address in a comma separated list, subject to an optional exclusion list.
- *
- * @param string $csv Comma separated list of elements
- * @param array $excludedIps Optional list of excluded IP addresses (or IP address ranges)
- * @return string Last (non-excluded) IP address in the list
- */
- static public function getLastIpFromList($csv, $excludedIps = null)
- {
- $p = strrpos($csv, ',');
- if($p !== false)
- {
- $elements = explode(',', $csv);
- for($i = count($elements); $i--; )
- {
- $element = trim(Piwik_Common::sanitizeInputValue($elements[$i]));
- if(empty($excludedIps) || (!in_array($element, $excludedIps) && !self::isIpInRange(self::P2N(self::sanitizeIp($element)), $excludedIps)))
- {
- return $element;
- }
- }
- }
- return trim(Piwik_Common::sanitizeInputValue($csv));
- }
-
- /**
- * Get hostname for a given IP address
- *
- * @param string $ipStr Human-readable IP address
- * @return string Hostname or unmodified $ipStr if failure
- */
- static public function getHostByAddr($ipStr)
- {
- // PHP's reverse lookup supports ipv4 and ipv6
- // except on Windows before PHP 5.3
- $host = strtolower(@gethostbyaddr($ipStr));
- return $host === '' ? $ipStr : $host;
- }
+ const MAPPED_IPv4_START = '::ffff:';
+
+ /**
+ * Sanitize human-readable IP address.
+ *
+ * @param string $ipString IP address
+ * @return string|false
+ */
+ static public function sanitizeIp($ipString)
+ {
+ $ipString = trim($ipString);
+
+ // CIDR notation, A.B.C.D/E
+ $posSlash = strrpos($ipString, '/');
+ if ($posSlash !== false) {
+ $ipString = substr($ipString, 0, $posSlash);
+ }
+
+ $posColon = strrpos($ipString, ':');
+ $posDot = strrpos($ipString, '.');
+ if ($posColon !== false) {
+ // IPv6 address with port, [A:B:C:D:E:F:G:H]:EEEE
+ $posRBrac = strrpos($ipString, ']');
+ if ($posRBrac !== false && $ipString[0] == '[') {
+ $ipString = substr($ipString, 1, $posRBrac - 1);
+ }
+
+ if ($posDot !== false) {
+ // IPv4 address with port, A.B.C.D:EEEE
+ if ($posColon > $posDot) {
+ $ipString = substr($ipString, 0, $posColon);
+ }
+ // else: Dotted quad IPv6 address, A:B:C:D:E:F:G.H.I.J
+ } else if (strpos($ipString, ':') === $posColon) {
+ $ipString = substr($ipString, 0, $posColon);
+ }
+ // else: IPv6 address, A:B:C:D:E:F:G:H
+ }
+ // else: IPv4 address, A.B.C.D
+
+ return $ipString;
+ }
+
+ /**
+ * Sanitize human-readable (user-supplied) IP address range.
+ *
+ * Accepts the following formats for $ipRange:
+ * - single IPv4 address, e.g., 127.0.0.1
+ * - single IPv6 address, e.g., ::1/128
+ * - IPv4 block using CIDR notation, e.g., 192.168.0.0/22 represents the IPv4 addresses from 192.168.0.0 to 192.168.3.255
+ * - IPv6 block using CIDR notation, e.g., 2001:DB8::/48 represents the IPv6 addresses from 2001:DB8:0:0:0:0:0:0 to 2001:DB8:0:FFFF:FFFF:FFFF:FFFF:FFFF
+ * - wildcards, e.g., 192.168.0.*
+ *
+ * @param string $ipRangeString IP address range
+ * @return string|false IP address range in CIDR notation
+ */
+ static public function sanitizeIpRange($ipRangeString)
+ {
+ $ipRangeString = trim($ipRangeString);
+ if (empty($ipRangeString)) {
+ return false;
+ }
+
+ // IPv4 address with wildcards '*'
+ if (strpos($ipRangeString, '*') !== false) {
+ if (preg_match('~(^|\.)\*\.\d+(\.|$)~D', $ipRangeString)) {
+ return false;
+ }
+
+ $bits = 32 - 8 * substr_count($ipRangeString, '*');
+ $ipRangeString = str_replace('*', '0', $ipRangeString);
+ }
+
+ // CIDR
+ if (($pos = strpos($ipRangeString, '/')) !== false) {
+ $bits = substr($ipRangeString, $pos + 1);
+ $ipRangeString = substr($ipRangeString, 0, $pos);
+ }
+
+ // single IP
+ if (($ip = @_inet_pton($ipRangeString)) === false)
+ return false;
+
+ $maxbits = Piwik_Common::strlen($ip) * 8;
+ if (!isset($bits))
+ $bits = $maxbits;
+
+ if ($bits < 0 || $bits > $maxbits) {
+ return false;
+ }
+
+ return "$ipRangeString/$bits";
+ }
+
+ /**
+ * Convert presentation format IP address to network address format
+ *
+ * @param string $ipString IP address, either IPv4 or IPv6, e.g., "127.0.0.1"
+ * @return string Binary-safe string, e.g., "\x7F\x00\x00\x01"
+ */
+ static public function P2N($ipString)
+ {
+ // use @inet_pton() because it throws an exception and E_WARNING on invalid input
+ $ip = @_inet_pton($ipString);
+ return $ip === false ? "\x00\x00\x00\x00" : $ip;
+ }
+
+ /**
+ * Convert network address format to presentation format
+ *
+ * @see prettyPrint()
+ *
+ * @param string $ip IP address in network address format
+ * @return string IP address in presentation format
+ */
+ static public function N2P($ip)
+ {
+ // use @inet_ntop() because it throws an exception and E_WARNING on invalid input
+ $ipStr = @_inet_ntop($ip);
+ return $ipStr === false ? '0.0.0.0' : $ipStr;
+ }
+
+ /**
+ * Alias for N2P()
+ *
+ * @param string $ip IP address in network address format
+ * @return string IP address in presentation format
+ */
+ static public function prettyPrint($ip)
+ {
+ return self::N2P($ip);
+ }
+
+ /**
+ * Is this an IPv4, IPv4-compat, or IPv4-mapped address?
+ *
+ * @param string $ip IP address in network address format
+ * @return bool True if IPv4, else false
+ */
+ static public function isIPv4($ip)
+ {
+ // in case mbstring overloads strlen and substr functions
+ $strlen = function_exists('mb_orig_strlen') ? 'mb_orig_strlen' : 'strlen';
+ $substr = function_exists('mb_orig_substr') ? 'mb_orig_substr' : 'substr';
+
+ // IPv4
+ if ($strlen($ip) == 4) {
+ return true;
+ }
+
+ // IPv6 - transitional address?
+ if ($strlen($ip) == 16) {
+ if (substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff", 0, 12) === 0
+ || substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0, 12) === 0
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert IP address (in network address format) to presentation format.
+ * This is a backward compatibility function for code that only expects
+ * IPv4 addresses (i.e., doesn't support IPv6).
+ *
+ * This function does not support the long (or its string representation)
+ * returned by the built-in ip2long() function, from Piwik 1.3 and earlier.
+ *
+ * @param string $ip IPv4 address in network address format
+ * @return string IP address in presentation format
+ */
+ static public function long2ip($ip)
+ {
+ // IPv4
+ if (Piwik_Common::strlen($ip) == 4) {
+ return self::N2P($ip);
+ }
+
+ // IPv6 - transitional address?
+ if (Piwik_Common::strlen($ip) == 16) {
+ if (substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff", 0, 12) === 0
+ || substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0, 12) === 0
+ ) {
+ // remap 128-bit IPv4-mapped and IPv4-compat addresses
+ return self::N2P(Piwik_Common::substr($ip, 12));
+ }
+ }
+
+ return '0.0.0.0';
+ }
+
+ /**
+ * Returns true if $ip is an IPv6 address, false if otherwise. This function does
+ * a naive check. It assumes that whatever format $ip is in, it is well-formed.
+ *
+ * @param string $ip
+ * @return bool
+ */
+ static public function isIPv6($ip)
+ {
+ return strpos($ip, ':') !== false;
+ }
+
+ /**
+ * Returns true if $ip is a IPv4 mapped address, false if otherwise.
+ *
+ * @param string $ip
+ * @return bool
+ */
+ static public function isMappedIPv4($ip)
+ {
+ return substr($ip, 0, strlen(self::MAPPED_IPv4_START)) === self::MAPPED_IPv4_START;
+ }
+
+ /**
+ * Returns
+ */
+ static public function getIPv4FromMappedIPv6($ip)
+ {
+ return substr($ip, strlen(self::MAPPED_IPv4_START));
+ }
+
+ /**
+ * Get low and high IP addresses for a specified range.
+ *
+ * @param array $ipRange An IP address range in presentation format
+ * @return array|false Array ($lowIp, $highIp) in network address format, or false if failure
+ */
+ static public function getIpsForRange($ipRange)
+ {
+ if (strpos($ipRange, '/') === false) {
+ $ipRange = self::sanitizeIpRange($ipRange);
+ }
+ $pos = strpos($ipRange, '/');
+
+ $bits = substr($ipRange, $pos + 1);
+ $range = substr($ipRange, 0, $pos);
+ $high = $low = @_inet_pton($range);
+ if ($low === false) {
+ return false;
+ }
+
+ $lowLen = Piwik_Common::strlen($low);
+ $i = $lowLen - 1;
+ $bits = $lowLen * 8 - $bits;
+
+ for ($n = (int)($bits / 8); $n > 0; $n--, $i--) {
+ $low[$i] = chr(0);
+ $high[$i] = chr(255);
+ }
+
+ $n = $bits % 8;
+ if ($n) {
+ $low[$i] = chr(ord($low[$i]) & ~((1 << $n) - 1));
+ $high[$i] = chr(ord($high[$i]) | ((1 << $n) - 1));
+ }
+
+ return array($low, $high);
+ }
+
+ /**
+ * Determines if an IP address is in a specified IP address range.
+ *
+ * An IPv4-mapped address should be range checked with an IPv4-mapped address range.
+ *
+ * @param string $ip IP address in network address format
+ * @param array $ipRanges List of IP address ranges
+ * @return bool True if in any of the specified IP address ranges; else false.
+ */
+ static public function isIpInRange($ip, $ipRanges)
+ {
+ $ipLen = Piwik_Common::strlen($ip);
+ if (empty($ip) || empty($ipRanges) || ($ipLen != 4 && $ipLen != 16)) {
+ return false;
+ }
+
+ foreach ($ipRanges as $range) {
+ if (is_array($range)) {
+ // already split into low/high IP addresses
+ $range[0] = self::P2N($range[0]);
+ $range[1] = self::P2N($range[1]);
+ } else {
+ // expect CIDR format but handle some variations
+ $range = self::getIpsForRange($range);
+ }
+ if ($range === false) {
+ continue;
+ }
+
+ $low = $range[0];
+ $high = $range[1];
+ if (Piwik_Common::strlen($low) != $ipLen) {
+ continue;
+ }
+
+ // binary-safe string comparison
+ if ($ip >= $low && $ip <= $high) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the best possible IP of the current user, in the format A.B.C.D
+ * For example, this could be the proxy client's IP address.
+ *
+ * @return string IP address in presentation format
+ */
+ static public function getIpFromHeader()
+ {
+ $clientHeaders = @Piwik_Config::getInstance()->General['proxy_client_headers'];
+ if (!is_array($clientHeaders)) {
+ $clientHeaders = array();
+ }
+
+ $default = '0.0.0.0';
+ if (isset($_SERVER['REMOTE_ADDR'])) {
+ $default = $_SERVER['REMOTE_ADDR'];
+ }
+
+ $ipString = self::getNonProxyIpFromHeader($default, $clientHeaders);
+ return self::sanitizeIp($ipString);
+ }
+
+ /**
+ * Returns a non-proxy IP address from header
+ *
+ * @param string $default Default value to return if no matching proxy header
+ * @param array $proxyHeaders List of proxy headers
+ * @return string
+ */
+ static public function getNonProxyIpFromHeader($default, $proxyHeaders)
+ {
+ $proxyIps = @Piwik_Config::getInstance()->General['proxy_ips'];
+ if (!is_array($proxyIps)) {
+ $proxyIps = array();
+ }
+ $proxyIps[] = $default;
+
+ // examine proxy headers
+ foreach ($proxyHeaders as $proxyHeader) {
+ if (!empty($_SERVER[$proxyHeader])) {
+ $proxyIp = self::getLastIpFromList($_SERVER[$proxyHeader], $proxyIps);
+ if (strlen($proxyIp) && stripos($proxyIp, 'unknown') === false) {
+ return $proxyIp;
+ }
+ }
+ }
+
+ return $default;
+ }
+
+ /**
+ * Returns the last IP address in a comma separated list, subject to an optional exclusion list.
+ *
+ * @param string $csv Comma separated list of elements
+ * @param array $excludedIps Optional list of excluded IP addresses (or IP address ranges)
+ * @return string Last (non-excluded) IP address in the list
+ */
+ static public function getLastIpFromList($csv, $excludedIps = null)
+ {
+ $p = strrpos($csv, ',');
+ if ($p !== false) {
+ $elements = explode(',', $csv);
+ for ($i = count($elements); $i--;) {
+ $element = trim(Piwik_Common::sanitizeInputValue($elements[$i]));
+ if (empty($excludedIps) || (!in_array($element, $excludedIps) && !self::isIpInRange(self::P2N(self::sanitizeIp($element)), $excludedIps))) {
+ return $element;
+ }
+ }
+ }
+ return trim(Piwik_Common::sanitizeInputValue($csv));
+ }
+
+ /**
+ * Get hostname for a given IP address
+ *
+ * @param string $ipStr Human-readable IP address
+ * @return string Hostname or unmodified $ipStr if failure
+ */
+ static public function getHostByAddr($ipStr)
+ {
+ // PHP's reverse lookup supports ipv4 and ipv6
+ // except on Windows before PHP 5.3
+ $host = strtolower(@gethostbyaddr($ipStr));
+ return $host === '' ? $ipStr : $host;
+ }
}
/**
@@ -482,72 +449,67 @@ class Piwik_IP
*
* @link http://php.net/inet_ntop
*
- * @param string $in_addr 32-bit IPv4 or 128-bit IPv6 address
+ * @param string $in_addr 32-bit IPv4 or 128-bit IPv6 address
* @return string|false string representation of address or false on failure
*/
function php_compat_inet_ntop($in_addr)
{
- $r = bin2hex($in_addr);
-
- switch (Piwik_Common::strlen($in_addr))
- {
- case 4:
- // IPv4 address
- $prefix = '';
- break;
-
- case 16:
- // IPv4-mapped address
- if(substr_compare($r, '00000000000000000000ffff', 0, 24) === 0)
- {
- $prefix = '::ffff:';
- $r = substr($r, 24);
- break;
- }
-
- // IPv4-compat address
- if(substr_compare($r, '000000000000000000000000', 0, 24) === 0 &&
- substr_compare($r, '0000', 24, 4) !== 0)
- {
- $prefix = '::';
- $r = substr($r, 24);
- break;
- }
-
- $r = str_split($r, 4);
- $r = implode(':', $r);
-
- // compress leading zeros
- $r = preg_replace(
- '/(^|:)0{1,3}/',
- '$1',
- $r
- );
-
- // compress longest (and leftmost) consecutive groups of zeros
- if(preg_match_all('/(?:^|:)(0(:|$))+/D', $r, $matches))
- {
- $longestMatch = 0;
- foreach($matches[0] as $aMatch)
- {
- if(strlen($aMatch) > strlen($longestMatch))
- {
- $longestMatch = $aMatch;
- }
- }
- $r = substr_replace($r, '::', strpos($r, $longestMatch), strlen($longestMatch));
- }
-
- return $r;
-
- default:
- return false;
- }
-
- $r = str_split($r, 2);
- $r = array_map('hexdec', $r);
- $r = implode('.', $r);
- return $prefix . $r;
+ $r = bin2hex($in_addr);
+
+ switch (Piwik_Common::strlen($in_addr)) {
+ case 4:
+ // IPv4 address
+ $prefix = '';
+ break;
+
+ case 16:
+ // IPv4-mapped address
+ if (substr_compare($r, '00000000000000000000ffff', 0, 24) === 0) {
+ $prefix = '::ffff:';
+ $r = substr($r, 24);
+ break;
+ }
+
+ // IPv4-compat address
+ if (substr_compare($r, '000000000000000000000000', 0, 24) === 0 &&
+ substr_compare($r, '0000', 24, 4) !== 0
+ ) {
+ $prefix = '::';
+ $r = substr($r, 24);
+ break;
+ }
+
+ $r = str_split($r, 4);
+ $r = implode(':', $r);
+
+ // compress leading zeros
+ $r = preg_replace(
+ '/(^|:)0{1,3}/',
+ '$1',
+ $r
+ );
+
+ // compress longest (and leftmost) consecutive groups of zeros
+ if (preg_match_all('/(?:^|:)(0(:|$))+/D', $r, $matches)) {
+ $longestMatch = 0;
+ foreach ($matches[0] as $aMatch) {
+ if (strlen($aMatch) > strlen($longestMatch)) {
+ $longestMatch = $aMatch;
+ }
+ }
+ $r = substr_replace($r, '::', strpos($r, $longestMatch), strlen($longestMatch));
+ }
+
+ return $r;
+
+ default:
+ return false;
+ }
+
+ $r = str_split($r, 2);
+ $r = array_map('hexdec', $r);
+ $r = implode('.', $r);
+ return $prefix . $r;
}
/**
@@ -555,87 +517,79 @@ function php_compat_inet_ntop($in_addr)
*
* @link http://php.net/inet_pton
*
- * @param string $address a human readable IPv4 or IPv6 address
+ * @param string $address a human readable IPv4 or IPv6 address
* @return string in_addr representation or false on failure
*/
function php_compat_inet_pton($address)
{
- // IPv4 (or IPv4-compat, or IPv4-mapped)
- if(preg_match('/(^|:)([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/iD', $address, $matches))
- {
- for($i = count($matches); $i-- > 2; )
- {
- if($matches[$i] > 255 ||
- ($matches[$i][0] == '0' && strlen($matches[$i]) > 1))
- {
- return false;
- }
- }
-
- if(empty($matches[1]))
- {
- $r = ip2long($address);
- if($r === false)
- {
- return false;
- }
-
- return pack('N', $r);
- }
-
- $suffix = sprintf("%02x%02x:%02x%02x", $matches[2], $matches[3], $matches[4], $matches[5]);
- $address = substr_replace($address, $matches[1] . $suffix, strrpos($address, $matches[0]));
- }
-
- // IPv6
- if(strpos($address, ':') === false ||
- strspn($address, '01234567890abcdefABCDEF:') !== strlen($address))
- {
- return false;
- }
-
- if(substr($address, 0, 2) == '::')
- {
- $address = '0'.$address;
- }
-
- if(substr($address, -2) == '::')
- {
- $address .= '0';
- }
-
- $r = explode(':', $address);
- $count = count($r);
-
- // grouped zeros
- if(strpos($address, '::') !== false
- && $count < 8)
- {
- $zeroGroup = array_search('', $r, 1);
-
- // we're replacing this cell, so we splice (8 - $count + 1) cells containing '0'
- array_splice($r, $zeroGroup, 1, array_fill(0, 9 - $count, '0'));
- }
-
- // guard against excessive ':' or '::'
- if($count > 8 ||
- array_search('', $r, 1) !== false)
- {
- return false;
- }
-
- // leading zeros
- foreach($r as $v)
- {
- if(strlen(ltrim($v, '0')) > 4)
- {
- return false;
- }
- }
-
- $r = array_map('hexdec', $r);
- array_unshift($r, 'n*');
- $r = call_user_func_array('pack', $r);
-
- return $r;
+ // IPv4 (or IPv4-compat, or IPv4-mapped)
+ if (preg_match('/(^|:)([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/iD', $address, $matches)) {
+ for ($i = count($matches); $i-- > 2;) {
+ if ($matches[$i] > 255 ||
+ ($matches[$i][0] == '0' && strlen($matches[$i]) > 1)
+ ) {
+ return false;
+ }
+ }
+
+ if (empty($matches[1])) {
+ $r = ip2long($address);
+ if ($r === false) {
+ return false;
+ }
+
+ return pack('N', $r);
+ }
+
+ $suffix = sprintf("%02x%02x:%02x%02x", $matches[2], $matches[3], $matches[4], $matches[5]);
+ $address = substr_replace($address, $matches[1] . $suffix, strrpos($address, $matches[0]));
+ }
+
+ // IPv6
+ if (strpos($address, ':') === false ||
+ strspn($address, '01234567890abcdefABCDEF:') !== strlen($address)
+ ) {
+ return false;
+ }
+
+ if (substr($address, 0, 2) == '::') {
+ $address = '0' . $address;
+ }
+
+ if (substr($address, -2) == '::') {
+ $address .= '0';
+ }
+
+ $r = explode(':', $address);
+ $count = count($r);
+
+ // grouped zeros
+ if (strpos($address, '::') !== false
+ && $count < 8
+ ) {
+ $zeroGroup = array_search('', $r, 1);
+
+ // we're replacing this cell, so we splice (8 - $count + 1) cells containing '0'
+ array_splice($r, $zeroGroup, 1, array_fill(0, 9 - $count, '0'));
+ }
+
+ // guard against excessive ':' or '::'
+ if ($count > 8 ||
+ array_search('', $r, 1) !== false
+ ) {
+ return false;
+ }
+
+ // leading zeros
+ foreach ($r as $v) {
+ if (strlen(ltrim($v, '0')) > 4) {
+ return false;
+ }
+ }
+
+ $r = array_map('hexdec', $r);
+ array_unshift($r, 'n*');
+ $r = call_user_func_array('pack', $r);
+
+ return $r;
}
diff --git a/core/Loader.php b/core/Loader.php
index ae375d03a7..271e20c1a3 100644
--- a/core/Loader.php
+++ b/core/Loader.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -16,117 +16,101 @@
*/
class Piwik_Loader
{
- // our class search path; current directory is intentionally excluded
- protected static $dirs = array( '/core/', '/plugins/' );
+ // our class search path; current directory is intentionally excluded
+ protected static $dirs = array('/core/', '/plugins/');
- /**
- * Get class file name
- *
- * @param string $class Class name
- * @return string Class file name
- * @throws Exception if class name is invalid
- */
- protected static function getClassFileName($class)
- {
- if(!preg_match("/^[A-Za-z0-9_]+$/", $class))
- {
- throw new Exception("Invalid class name \"$class\".");
- }
+ /**
+ * Get class file name
+ *
+ * @param string $class Class name
+ * @return string Class file name
+ * @throws Exception if class name is invalid
+ */
+ protected static function getClassFileName($class)
+ {
+ if (!preg_match("/^[A-Za-z0-9_]+$/", $class)) {
+ throw new Exception("Invalid class name \"$class\".");
+ }
- $class = str_replace('_', '/', $class);
+ $class = str_replace('_', '/', $class);
- if($class == 'Piwik')
- {
- return $class;
- }
+ if ($class == 'Piwik') {
+ return $class;
+ }
- if(!strncmp($class, 'Piwik/', 6))
- {
- return substr($class, 6);
- }
+ if (!strncmp($class, 'Piwik/', 6)) {
+ return substr($class, 6);
+ }
- return $class;
- }
+ return $class;
+ }
- /**
- * Load class by name
- *
- * @param string $class Class name
- * @throws Exception if class not found
- */
- public static function loadClass($class)
- {
- $classPath = self::getClassFileName($class);
- if($class == 'Piwik' || !strncmp($class, 'Piwik_', 6))
- {
- // Piwik classes are in core/ or plugins/
- do
- {
- // auto-discover class location
- foreach(self::$dirs as $dir)
- {
- $path = PIWIK_INCLUDE_PATH . $dir . $classPath . '.php';
- if(file_exists($path))
- {
- require_once $path; // prefixed by PIWIK_INCLUDE_PATH
- if(class_exists($class, false) || interface_exists($class, false))
- {
- return;
- }
- }
- }
+ /**
+ * Load class by name
+ *
+ * @param string $class Class name
+ * @throws Exception if class not found
+ */
+ public static function loadClass($class)
+ {
+ $classPath = self::getClassFileName($class);
+ if ($class == 'Piwik' || !strncmp($class, 'Piwik_', 6)) {
+ // Piwik classes are in core/ or plugins/
+ do {
+ // auto-discover class location
+ foreach (self::$dirs as $dir) {
+ $path = PIWIK_INCLUDE_PATH . $dir . $classPath . '.php';
+ if (file_exists($path)) {
+ require_once $path; // prefixed by PIWIK_INCLUDE_PATH
+ if (class_exists($class, false) || interface_exists($class, false)) {
+ return;
+ }
+ }
+ }
- // truncate to find file with multiple class definitions
- $lastSlash = strrpos($classPath, '/');
- $classPath = ($lastSlash === false) ? '' : substr($classPath, 0, $lastSlash);
- } while(!empty($classPath));
- }
- else
- {
- // non-Piwik classes (e.g., Zend Framework) are in libs/
- $path = PIWIK_INCLUDE_PATH . '/libs/' . $classPath . '.php';
- if(file_exists($path))
- {
- require_once $path; // prefixed by PIWIK_INCLUDE_PATH
- if(class_exists($class, false) || interface_exists($class, false))
- {
- return;
- }
- }
- }
- throw new Exception("Class \"$class\" not found.");
- }
+ // truncate to find file with multiple class definitions
+ $lastSlash = strrpos($classPath, '/');
+ $classPath = ($lastSlash === false) ? '' : substr($classPath, 0, $lastSlash);
+ } while (!empty($classPath));
+ } else {
+ // non-Piwik classes (e.g., Zend Framework) are in libs/
+ $path = PIWIK_INCLUDE_PATH . '/libs/' . $classPath . '.php';
+ if (file_exists($path)) {
+ require_once $path; // prefixed by PIWIK_INCLUDE_PATH
+ if (class_exists($class, false) || interface_exists($class, false)) {
+ return;
+ }
+ }
+ }
+ throw new Exception("Class \"$class\" not found.");
+ }
- /**
- * Autoloader
- *
- * @param string $class Class name
- */
- public static function autoload($class)
- {
- try {
- self::loadClass($class);
- } catch (Exception $e) {
- }
- }
+ /**
+ * Autoloader
+ *
+ * @param string $class Class name
+ */
+ public static function autoload($class)
+ {
+ try {
+ self::loadClass($class);
+ } catch (Exception $e) {
+ }
+ }
}
// Note: only one __autoload per PHP instance
-if(function_exists('spl_autoload_register'))
-{
- // use the SPL autoload stack
- spl_autoload_register(array('Piwik_Loader', 'autoload'));
+if (function_exists('spl_autoload_register')) {
+ // use the SPL autoload stack
+ spl_autoload_register(array('Piwik_Loader', 'autoload'));
- // preserve any existing __autoload
- if(function_exists('__autoload'))
- {
- spl_autoload_register('__autoload');
- }
-}
-else
-{
- function __autoload($class)
- {
- Piwik_Loader::autoload($class);
- }
+ // preserve any existing __autoload
+ if (function_exists('__autoload')) {
+ spl_autoload_register('__autoload');
+ }
+} else {
+ function __autoload($class)
+ {
+ Piwik_Loader::autoload($class);
+ }
}
diff --git a/core/Log.php b/core/Log.php
index 871ae9d787..5e658094f6 100644
--- a/core/Log.php
+++ b/core/Log.php
@@ -1,200 +1,192 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- *
+ *
* @package Piwik
* @subpackage Piwik_Log
* @see Zend_Log, libs/Zend/Log.php
- * @link http://framework.zend.com/manual/en/zend.log.html
+ * @link http://framework.zend.com/manual/en/zend.log.html
*/
abstract class Piwik_Log extends Zend_Log
{
- protected $logToDatabaseTableName = null;
- protected $logToDatabaseColumnMapping = null;
- protected $logToFileFilename = null;
- protected $fileFormatter = null;
- protected $screenFormatter = null;
- protected $currentRequestKey;
-
- function __construct( $logToFileFilename,
- $fileFormatter,
- $screenFormatter,
- $logToDatabaseTableName,
- $logToDatabaseColumnMapping )
- {
- parent::__construct();
-
- $this->currentRequestKey = substr( Piwik_Common::generateUniqId(), 0, 8);
-
- $log_dir = Piwik_Config::getInstance()->log['logger_file_path'];
- if($log_dir[0] != '/' && $log_dir[0] != DIRECTORY_SEPARATOR)
- {
- $log_dir = PIWIK_USER_PATH . '/' . $log_dir;
- }
- $this->logToFileFilename = $log_dir . '/' . $logToFileFilename;
-
- $this->fileFormatter = $fileFormatter;
- $this->screenFormatter = $screenFormatter;
- $this->logToDatabaseTableName = Piwik_Common::prefixTable($logToDatabaseTableName);
- $this->logToDatabaseColumnMapping = $logToDatabaseColumnMapping;
- }
-
- function addWriteToFile()
- {
- Piwik_Common::mkdir(dirname($this->logToFileFilename));
- $writerFile = new Zend_Log_Writer_Stream($this->logToFileFilename);
- $writerFile->setFormatter( $this->fileFormatter );
- $this->addWriter($writerFile);
- }
-
- function addWriteToNull()
- {
- $this->addWriter( new Zend_Log_Writer_Null );
- }
-
- function addWriteToDatabase()
- {
- $writerDb = new Zend_Log_Writer_Db(
- Zend_Registry::get('db'),
- $this->logToDatabaseTableName,
- $this->logToDatabaseColumnMapping);
-
- $this->addWriter($writerDb);
- }
-
- function addWriteToScreen()
- {
- $writerScreen = new Zend_Log_Writer_Stream('php://output');
- $writerScreen->setFormatter( $this->screenFormatter );
- $this->addWriter($writerScreen);
- }
-
- public function getWritersCount()
- {
- return count($this->_writers);
- }
-
- /**
- * Log an event
- * @param string $event
- * @param int $priority
- * @param null $extras
- * @throws Zend_Log_Exception
- * @return void
- */
- public function log($event, $priority, $extras = null)
- {
- // sanity checks
- if (empty($this->_writers)) {
- throw new Zend_Log_Exception('No writers were added');
- }
-
- $event['timestamp'] = date('Y-m-d H:i:s');
- $event['requestKey'] = $this->currentRequestKey;
- // pack into event required by filters and writers
- $event = array_merge( $event, $this->_extras);
-
- // one message must stay on one line
- if(isset($event['message']))
- {
- $event['message'] = str_replace(array(PHP_EOL, "\n"), " ", $event['message']);
- }
-
- // Truncate the backtrace which can be too long to display in the browser
- if(!empty($event['backtrace']))
- {
- $maxSizeOutputBytes = 1024 * 1024; // no more than 1M output please
- $truncateBacktraceLineAfter = 1000;
- $maxLines = ceil($maxSizeOutputBytes / $truncateBacktraceLineAfter);
- $bt = explode("\n", $event['backtrace']);
- foreach($bt as $count => &$line)
- {
- if(strlen($line) > $truncateBacktraceLineAfter)
- {
- $line = substr($line, 0, $truncateBacktraceLineAfter) . '...';
- }
- if($count > $maxLines)
- {
- $line .= "\nTruncated error message.";
- break;
- }
- }
- $event['backtrace'] = implode("\n", $bt);
- }
- // abort if rejected by the global filters
- foreach ($this->_filters as $filter) {
- if (! $filter->accept($event)) {
- return;
- }
- }
-
- // send to each writer
- foreach ($this->_writers as $writer) {
- $writer->write($event);
- }
- }
+ protected $logToDatabaseTableName = null;
+ protected $logToDatabaseColumnMapping = null;
+ protected $logToFileFilename = null;
+ protected $fileFormatter = null;
+ protected $screenFormatter = null;
+ protected $currentRequestKey;
+
+ function __construct($logToFileFilename,
+ $fileFormatter,
+ $screenFormatter,
+ $logToDatabaseTableName,
+ $logToDatabaseColumnMapping)
+ {
+ parent::__construct();
+
+ $this->currentRequestKey = substr(Piwik_Common::generateUniqId(), 0, 8);
+
+ $log_dir = Piwik_Config::getInstance()->log['logger_file_path'];
+ if ($log_dir[0] != '/' && $log_dir[0] != DIRECTORY_SEPARATOR) {
+ $log_dir = PIWIK_USER_PATH . '/' . $log_dir;
+ }
+ $this->logToFileFilename = $log_dir . '/' . $logToFileFilename;
+
+ $this->fileFormatter = $fileFormatter;
+ $this->screenFormatter = $screenFormatter;
+ $this->logToDatabaseTableName = Piwik_Common::prefixTable($logToDatabaseTableName);
+ $this->logToDatabaseColumnMapping = $logToDatabaseColumnMapping;
+ }
+
+ function addWriteToFile()
+ {
+ Piwik_Common::mkdir(dirname($this->logToFileFilename));
+ $writerFile = new Zend_Log_Writer_Stream($this->logToFileFilename);
+ $writerFile->setFormatter($this->fileFormatter);
+ $this->addWriter($writerFile);
+ }
+
+ function addWriteToNull()
+ {
+ $this->addWriter(new Zend_Log_Writer_Null);
+ }
+
+ function addWriteToDatabase()
+ {
+ $writerDb = new Zend_Log_Writer_Db(
+ Zend_Registry::get('db'),
+ $this->logToDatabaseTableName,
+ $this->logToDatabaseColumnMapping);
+
+ $this->addWriter($writerDb);
+ }
+
+ function addWriteToScreen()
+ {
+ $writerScreen = new Zend_Log_Writer_Stream('php://output');
+ $writerScreen->setFormatter($this->screenFormatter);
+ $this->addWriter($writerScreen);
+ }
+
+ public function getWritersCount()
+ {
+ return count($this->_writers);
+ }
+
+ /**
+ * Log an event
+ * @param string $event
+ * @param int $priority
+ * @param null $extras
+ * @throws Zend_Log_Exception
+ * @return void
+ */
+ public function log($event, $priority, $extras = null)
+ {
+ // sanity checks
+ if (empty($this->_writers)) {
+ throw new Zend_Log_Exception('No writers were added');
+ }
+
+ $event['timestamp'] = date('Y-m-d H:i:s');
+ $event['requestKey'] = $this->currentRequestKey;
+ // pack into event required by filters and writers
+ $event = array_merge($event, $this->_extras);
+
+ // one message must stay on one line
+ if (isset($event['message'])) {
+ $event['message'] = str_replace(array(PHP_EOL, "\n"), " ", $event['message']);
+ }
+
+ // Truncate the backtrace which can be too long to display in the browser
+ if (!empty($event['backtrace'])) {
+ $maxSizeOutputBytes = 1024 * 1024; // no more than 1M output please
+ $truncateBacktraceLineAfter = 1000;
+ $maxLines = ceil($maxSizeOutputBytes / $truncateBacktraceLineAfter);
+ $bt = explode("\n", $event['backtrace']);
+ foreach ($bt as $count => &$line) {
+ if (strlen($line) > $truncateBacktraceLineAfter) {
+ $line = substr($line, 0, $truncateBacktraceLineAfter) . '...';
+ }
+ if ($count > $maxLines) {
+ $line .= "\nTruncated error message.";
+ break;
+ }
+ }
+ $event['backtrace'] = implode("\n", $bt);
+ }
+ // abort if rejected by the global filters
+ foreach ($this->_filters as $filter) {
+ if (!$filter->accept($event)) {
+ return;
+ }
+ }
+
+ // send to each writer
+ foreach ($this->_writers as $writer) {
+ $writer->write($event);
+ }
+ }
}
/**
- *
+ *
* @package Piwik
* @subpackage Piwik_Log
*/
class Piwik_Log_Formatter_FileFormatter implements Zend_Log_Formatter_Interface
-{
- /**
- * Formats data into a single line to be written by the writer.
- *
- * @param array $event event data
- * @return string formatted line to write to the log
- */
- public function format($event)
- {
- foreach($event as &$value)
- {
- $value = str_replace("\n", '\n', $value);
- $value = '"'.$value.'"';
- }
- $ts = $event['timestamp'];
- unset($event['timestamp']);
- return $ts . ' ' . implode(" ", $event) . "\n";
- }
+{
+ /**
+ * Formats data into a single line to be written by the writer.
+ *
+ * @param array $event event data
+ * @return string formatted line to write to the log
+ */
+ public function format($event)
+ {
+ foreach ($event as &$value) {
+ $value = str_replace("\n", '\n', $value);
+ $value = '"' . $value . '"';
+ }
+ $ts = $event['timestamp'];
+ unset($event['timestamp']);
+ return $ts . ' ' . implode(" ", $event) . "\n";
+ }
}
/**
- *
+ *
* @package Piwik
* @subpackage Piwik_Log
*/
class Piwik_Log_Formatter_ScreenFormatter implements Zend_Log_Formatter_Interface
{
- function formatEvent($event)
- {
- // no injection in error messages, backtrace when displayed on screen
- return array_map(array('Piwik_Common','sanitizeInputValue'), $event);
- }
-
- function format($string)
- {
- return self::getFormattedString($string);
- }
-
- static public function getFormattedString($string)
- {
- if(!Piwik_Common::isPhpCliMode())
- {
- @header('Content-Type: text/html; charset=utf-8');
- }
- return $string;
- }
+ function formatEvent($event)
+ {
+ // no injection in error messages, backtrace when displayed on screen
+ return array_map(array('Piwik_Common', 'sanitizeInputValue'), $event);
+ }
+
+ function format($string)
+ {
+ return self::getFormattedString($string);
+ }
+
+ static public function getFormattedString($string)
+ {
+ if (!Piwik_Common::isPhpCliMode()) {
+ @header('Content-Type: text/html; charset=utf-8');
+ }
+ return $string;
+ }
}
diff --git a/core/Log/APICall.php b/core/Log/APICall.php
index 53e40e55c3..07e5bb25ff 100644
--- a/core/Log/APICall.php
+++ b/core/Log/APICall.php
@@ -17,58 +17,58 @@
*/
class Piwik_Log_APICall extends Piwik_Log
{
- const ID = 'logger_api_call';
+ const ID = 'logger_api_call';
- /**
- * Constructor
- */
- function __construct()
- {
- $logToFileFilename = self::ID;
- $logToDatabaseTableName = self::ID;
- $logToDatabaseColumnMapping = array(
- 'class_name' => 'class_name',
- 'method_name' => 'method_name',
- 'parameter_names_default_values' => 'parameter_names_default_values',
- 'parameter_values' => 'parameter_values',
- 'execution_time' => 'execution_time',
- 'caller_ip' => 'caller_ip',
- 'timestamp' => 'timestamp',
- 'returned_value' => 'returned_value'
- );
- $screenFormatter = new Piwik_Log_APICall_Formatter_ScreenFormatter();
- $fileFormatter = new Piwik_Log_Formatter_FileFormatter();
+ /**
+ * Constructor
+ */
+ function __construct()
+ {
+ $logToFileFilename = self::ID;
+ $logToDatabaseTableName = self::ID;
+ $logToDatabaseColumnMapping = array(
+ 'class_name' => 'class_name',
+ 'method_name' => 'method_name',
+ 'parameter_names_default_values' => 'parameter_names_default_values',
+ 'parameter_values' => 'parameter_values',
+ 'execution_time' => 'execution_time',
+ 'caller_ip' => 'caller_ip',
+ 'timestamp' => 'timestamp',
+ 'returned_value' => 'returned_value'
+ );
+ $screenFormatter = new Piwik_Log_APICall_Formatter_ScreenFormatter();
+ $fileFormatter = new Piwik_Log_Formatter_FileFormatter();
- parent::__construct($logToFileFilename,
- $fileFormatter,
- $screenFormatter,
- $logToDatabaseTableName,
- $logToDatabaseColumnMapping );
+ parent::__construct($logToFileFilename,
+ $fileFormatter,
+ $screenFormatter,
+ $logToDatabaseTableName,
+ $logToDatabaseColumnMapping);
- $this->setEventItem('caller_ip', Piwik_IP::P2N(Piwik_IP::getIpFromHeader()) );
- }
+ $this->setEventItem('caller_ip', Piwik_IP::P2N(Piwik_IP::getIpFromHeader()));
+ }
- /**
- * Logs the given api call event with the parameters
- *
- * @param string $className
- * @param string $methodName
- * @param array $parameterNames
- * @param array $parameterValues
- * @param number $executionTime
- * @param mixed $returnedValue
- */
- public function logEvent($className, $methodName, $parameterNames, $parameterValues, $executionTime, $returnedValue)
- {
- $event = array();
- $event['class_name'] = $className;
- $event['method_name'] = $methodName;
- $event['parameter_names_default_values'] = serialize($parameterNames);
- $event['parameter_values'] = serialize($parameterValues);
- $event['execution_time'] = $executionTime;
- $event['returned_value'] = is_array($returnedValue) ? serialize($returnedValue) : $returnedValue;
- parent::log($event, Piwik_Log::INFO, null);
- }
+ /**
+ * Logs the given api call event with the parameters
+ *
+ * @param string $className
+ * @param string $methodName
+ * @param array $parameterNames
+ * @param array $parameterValues
+ * @param number $executionTime
+ * @param mixed $returnedValue
+ */
+ public function logEvent($className, $methodName, $parameterNames, $parameterValues, $executionTime, $returnedValue)
+ {
+ $event = array();
+ $event['class_name'] = $className;
+ $event['method_name'] = $methodName;
+ $event['parameter_names_default_values'] = serialize($parameterNames);
+ $event['parameter_values'] = serialize($parameterValues);
+ $event['execution_time'] = $executionTime;
+ $event['returned_value'] = is_array($returnedValue) ? serialize($returnedValue) : $returnedValue;
+ parent::log($event, Piwik_Log::INFO, null);
+ }
}
/**
@@ -79,60 +79,53 @@ class Piwik_Log_APICall extends Piwik_Log
*/
class Piwik_Log_APICall_Formatter_ScreenFormatter extends Piwik_Log_Formatter_ScreenFormatter
{
- /**
+ /**
* Formats data into a single line to be written by the writer.
*
- * @param array $event event data
+ * @param array $event event data
* @return string formatted line to write to the log
*/
public function format($event)
{
- $str = "\n<br /> ";
- $str .= "Called: {$event['class_name']}.{$event['method_name']} (took {$event['execution_time']}ms)\n<br /> ";
- $str .= "Parameters: ";
- $parameterNamesAndDefault = unserialize($event['parameter_names_default_values']);
- $parameterValues = unserialize($event['parameter_values']);
- $i = 0;
- foreach($parameterNamesAndDefault as $pName => $pDefault)
- {
- if(isset($parameterValues[$i]))
- {
- $currentValue = $parameterValues[$i];
- }
- else
- {
- $currentValue = $pDefault;
- }
- $currentValue = $this->formatValue($currentValue);
- $str .= "$pName = $currentValue, ";
+ $str = "\n<br /> ";
+ $str .= "Called: {$event['class_name']}.{$event['method_name']} (took {$event['execution_time']}ms)\n<br /> ";
+ $str .= "Parameters: ";
+ $parameterNamesAndDefault = unserialize($event['parameter_names_default_values']);
+ $parameterValues = unserialize($event['parameter_values']);
+ $i = 0;
+ foreach ($parameterNamesAndDefault as $pName => $pDefault) {
+ if (isset($parameterValues[$i])) {
+ $currentValue = $parameterValues[$i];
+ } else {
+ $currentValue = $pDefault;
+ }
+ $currentValue = $this->formatValue($currentValue);
+ $str .= "$pName = $currentValue, ";
- $i++;
- }
- $str .= "\n<br /> ";
- $str .= "\n<br /> ";
- return parent::format($str);
+ $i++;
+ }
+ $str .= "\n<br /> ";
+ $str .= "\n<br /> ";
+ return parent::format($str);
}
- /**
- * Converts the given value to a string
- *
- * @param mixed $value
- * @return string
- */
- private function formatValue( $value )
+ /**
+ * Converts the given value to a string
+ *
+ * @param mixed $value
+ * @return string
+ */
+ private function formatValue($value)
{
- if(is_string($value))
- {
- $value = "'$value'";
- }
- if(is_null($value))
- {
- $value= 'null';
- }
- if(is_array($value))
- {
- $value = "array( ".implode(", ", $value). ")";
- }
- return $value;
+ if (is_string($value)) {
+ $value = "'$value'";
+ }
+ if (is_null($value)) {
+ $value = 'null';
+ }
+ if (is_array($value)) {
+ $value = "array( " . implode(", ", $value) . ")";
+ }
+ return $value;
}
}
diff --git a/core/Log/Error.php b/core/Log/Error.php
index a424691a23..bd54c8e6f0 100644
--- a/core/Log/Error.php
+++ b/core/Log/Error.php
@@ -1,141 +1,172 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Class used to log an error event.
- *
+ *
* @package Piwik
* @subpackage Piwik_Log
*/
class Piwik_Log_Error extends Piwik_Log
{
- const ID = 'logger_error';
+ const ID = 'logger_error';
- /**
- * Constructor
- */
- function __construct()
- {
- $logToFileFilename = self::ID;
- $logToDatabaseTableName = self::ID;
- $logToDatabaseColumnMapping = array(
- 'timestamp' => 'timestamp',
- 'message' => 'message',
- 'errno' => 'errno',
- 'errline' => 'errline',
- 'errfile' => 'errfile',
- 'backtrace' => 'backtrace'
- );
- $screenFormatter = new Piwik_Log_Error_Formatter_ScreenFormatter();
- $fileFormatter = new Piwik_Log_Formatter_FileFormatter();
- parent::__construct($logToFileFilename,
- $fileFormatter,
- $screenFormatter,
- $logToDatabaseTableName,
- $logToDatabaseColumnMapping );
- }
+ /**
+ * Constructor
+ */
+ function __construct()
+ {
+ $logToFileFilename = self::ID;
+ $logToDatabaseTableName = self::ID;
+ $logToDatabaseColumnMapping = array(
+ 'timestamp' => 'timestamp',
+ 'message' => 'message',
+ 'errno' => 'errno',
+ 'errline' => 'errline',
+ 'errfile' => 'errfile',
+ 'backtrace' => 'backtrace'
+ );
+ $screenFormatter = new Piwik_Log_Error_Formatter_ScreenFormatter();
+ $fileFormatter = new Piwik_Log_Formatter_FileFormatter();
+ parent::__construct($logToFileFilename,
+ $fileFormatter,
+ $screenFormatter,
+ $logToDatabaseTableName,
+ $logToDatabaseColumnMapping);
+ }
- /**
- * Adds the writer
- */
- function addWriteToScreen()
- {
- parent::addWriteToScreen();
- $writerScreen = new Zend_Log_Writer_Stream('php://stderr');
- $writerScreen->setFormatter( $this->screenFormatter );
- $this->addWriter($writerScreen);
- }
+ /**
+ * Adds the writer
+ */
+ function addWriteToScreen()
+ {
+ parent::addWriteToScreen();
+ $writerScreen = new Zend_Log_Writer_Stream('php://stderr');
+ $writerScreen->setFormatter($this->screenFormatter);
+ $this->addWriter($writerScreen);
+ }
- /**
- * Logs the given error event
- *
- * @param int $errno
- * @param string $errstr
- * @param string $errfile
- * @param int $errline
- * @param string $backtrace
- */
- public function logEvent($errno, $errstr, $errfile, $errline, $backtrace)
- {
- $event = array();
- $event['errno'] = $errno;
- $event['message'] = $errstr;
- $event['errfile'] = $errfile;
- $event['errline'] = $errline;
- $event['backtrace'] = $backtrace;
+ /**
+ * Logs the given error event
+ *
+ * @param int $errno
+ * @param string $errstr
+ * @param string $errfile
+ * @param int $errline
+ * @param string $backtrace
+ */
+ public function logEvent($errno, $errstr, $errfile, $errline, $backtrace)
+ {
+ $event = array();
+ $event['errno'] = $errno;
+ $event['message'] = $errstr;
+ $event['errfile'] = $errfile;
+ $event['errline'] = $errline;
+ $event['backtrace'] = $backtrace;
- parent::log($event, Piwik_Log::ERR, null);
- }
+ parent::log($event, Piwik_Log::ERR, null);
+ }
}
/**
* Format an error event to be displayed on the screen.
- *
+ *
* @package Piwik
* @subpackage Piwik_Log
*/
class Piwik_Log_Error_Formatter_ScreenFormatter extends Piwik_Log_Formatter_ScreenFormatter
{
- /**
+ /**
* Formats data into a single line to be written by the writer.
*
- * @param array $event event data
+ * @param array $event event data
* @return string formatted line to write to the log
*/
public function format($event)
{
- $event = parent::formatEvent($event);
-
- $errno = $event['errno'] ;
- $errstr = $event['message'] ;
- $errfile = $event['errfile'] ;
- $errline = $event['errline'] ;
- $backtrace = $event['backtrace'] ;
-
- $strReturned = '';
- $errno = $errno & error_reporting();
-
- // problem when using error_reporting with the @ silent fail operator
- // it gives an errno 0, and in this case the objective is to NOT display anything on the screen!
- // is there any other case where the errno is zero at this point?
- if($errno == 0) return '';
- $strReturned .= "\n<div style='word-wrap: break-word; border: 3px solid red; padding:4px; width:70%; background-color:#FFFF96;'>
- <strong>There is an error. Please report the message (Piwik ". (class_exists('Piwik_Version') ? Piwik_Version::VERSION : '' ) .")
+ $event = parent::formatEvent($event);
+
+ $errno = $event['errno'];
+ $errstr = $event['message'];
+ $errfile = $event['errfile'];
+ $errline = $event['errline'];
+ $backtrace = $event['backtrace'];
+
+ $strReturned = '';
+ $errno = $errno & error_reporting();
+
+ // problem when using error_reporting with the @ silent fail operator
+ // it gives an errno 0, and in this case the objective is to NOT display anything on the screen!
+ // is there any other case where the errno is zero at this point?
+ if ($errno == 0) return '';
+ $strReturned .= "\n<div style='word-wrap: break-word; border: 3px solid red; padding:4px; width:70%; background-color:#FFFF96;'>
+ <strong>There is an error. Please report the message (Piwik " . (class_exists('Piwik_Version') ? Piwik_Version::VERSION : '') . ")
and full backtrace in the <a href='?module=Proxy&action=redirect&url=http://forum.piwik.org' target='_blank'>Piwik forums</a> (please do a Search first as it might have been reported already!).<br /><br/>
";
- switch($errno)
- {
- case E_ERROR: $strReturned .= "Error"; break;
- case E_WARNING: $strReturned .= "Warning"; break;
- case E_PARSE: $strReturned .= "Parse Error"; break;
- case E_NOTICE: $strReturned .= "Notice"; break;
- case E_CORE_ERROR: $strReturned .= "Core Error"; break;
- case E_CORE_WARNING: $strReturned .= "Core Warning"; break;
- case E_COMPILE_ERROR: $strReturned .= "Compile Error"; break;
- case E_COMPILE_WARNING: $strReturned .= "Compile Warning"; break;
- case E_USER_ERROR: $strReturned .= "User Error"; break;
- case E_USER_WARNING: $strReturned .= "User Warning"; break;
- case E_USER_NOTICE: $strReturned .= "User Notice"; break;
- case E_STRICT: $strReturned .= "Strict Notice"; break;
- case E_RECOVERABLE_ERROR: $strReturned .= "Recoverable Error"; break;
- case E_DEPRECATED: $strReturned .= "Deprecated"; break;
- case E_USER_DEPRECATED: $strReturned .= "User Deprecated"; break;
- default: $strReturned .= "Unknown error ($errno)"; break;
- }
- $strReturned .= ":</strong> <i>$errstr</i> in <b>$errfile</b> on line <b>$errline</b>\n";
- $strReturned .= "<br /><br />Backtrace --&gt;<div style=\"font-family:Courier;font-size:10pt\">";
- $strReturned .= str_replace(array("\n",'#'), array("<br />\n","<br />\n#"), $backtrace);
- $strReturned .= "</div><br />";
- $strReturned .= "\n </pre></div><br />";
-
- return parent::format($strReturned);
+ switch ($errno) {
+ case E_ERROR:
+ $strReturned .= "Error";
+ break;
+ case E_WARNING:
+ $strReturned .= "Warning";
+ break;
+ case E_PARSE:
+ $strReturned .= "Parse Error";
+ break;
+ case E_NOTICE:
+ $strReturned .= "Notice";
+ break;
+ case E_CORE_ERROR:
+ $strReturned .= "Core Error";
+ break;
+ case E_CORE_WARNING:
+ $strReturned .= "Core Warning";
+ break;
+ case E_COMPILE_ERROR:
+ $strReturned .= "Compile Error";
+ break;
+ case E_COMPILE_WARNING:
+ $strReturned .= "Compile Warning";
+ break;
+ case E_USER_ERROR:
+ $strReturned .= "User Error";
+ break;
+ case E_USER_WARNING:
+ $strReturned .= "User Warning";
+ break;
+ case E_USER_NOTICE:
+ $strReturned .= "User Notice";
+ break;
+ case E_STRICT:
+ $strReturned .= "Strict Notice";
+ break;
+ case E_RECOVERABLE_ERROR:
+ $strReturned .= "Recoverable Error";
+ break;
+ case E_DEPRECATED:
+ $strReturned .= "Deprecated";
+ break;
+ case E_USER_DEPRECATED:
+ $strReturned .= "User Deprecated";
+ break;
+ default:
+ $strReturned .= "Unknown error ($errno)";
+ break;
+ }
+ $strReturned .= ":</strong> <i>$errstr</i> in <b>$errfile</b> on line <b>$errline</b>\n";
+ $strReturned .= "<br /><br />Backtrace --&gt;<div style=\"font-family:Courier;font-size:10pt\">";
+ $strReturned .= str_replace(array("\n", '#'), array("<br />\n", "<br />\n#"), $backtrace);
+ $strReturned .= "</div><br />";
+ $strReturned .= "\n </pre></div><br />";
+
+ return parent::format($strReturned);
}
}
diff --git a/core/Log/Exception.php b/core/Log/Exception.php
index 5a2cb3e2ef..b18233b3e2 100644
--- a/core/Log/Exception.php
+++ b/core/Log/Exception.php
@@ -18,60 +18,60 @@
*/
class Piwik_Log_Exception extends Piwik_Log
{
- const ID = 'logger_exception';
+ const ID = 'logger_exception';
- /**
- * Constructor
- */
- function __construct()
- {
- $logToFileFilename = self::ID;
- $logToDatabaseTableName = self::ID;
- $logToDatabaseColumnMapping = array(
- 'timestamp' => 'timestamp',
- 'message' => 'message',
- 'errno' => 'errno',
- 'errline' => 'errline',
- 'errfile' => 'errfile',
- 'backtrace' => 'backtrace'
- );
- $screenFormatter = new Piwik_Log_Exception_Formatter_ScreenFormatter();
- $fileFormatter = new Piwik_Log_Formatter_FileFormatter();
+ /**
+ * Constructor
+ */
+ function __construct()
+ {
+ $logToFileFilename = self::ID;
+ $logToDatabaseTableName = self::ID;
+ $logToDatabaseColumnMapping = array(
+ 'timestamp' => 'timestamp',
+ 'message' => 'message',
+ 'errno' => 'errno',
+ 'errline' => 'errline',
+ 'errfile' => 'errfile',
+ 'backtrace' => 'backtrace'
+ );
+ $screenFormatter = new Piwik_Log_Exception_Formatter_ScreenFormatter();
+ $fileFormatter = new Piwik_Log_Formatter_FileFormatter();
- parent::__construct($logToFileFilename,
- $fileFormatter,
- $screenFormatter,
- $logToDatabaseTableName,
- $logToDatabaseColumnMapping );
- }
+ parent::__construct($logToFileFilename,
+ $fileFormatter,
+ $screenFormatter,
+ $logToDatabaseTableName,
+ $logToDatabaseColumnMapping);
+ }
- /**
- * Adds the writer
- */
- function addWriteToScreen()
- {
- parent::addWriteToScreen();
- $writerScreen = new Zend_Log_Writer_Stream('php://stderr');
- $writerScreen->setFormatter( $this->screenFormatter );
- $this->addWriter($writerScreen);
- }
+ /**
+ * Adds the writer
+ */
+ function addWriteToScreen()
+ {
+ parent::addWriteToScreen();
+ $writerScreen = new Zend_Log_Writer_Stream('php://stderr');
+ $writerScreen->setFormatter($this->screenFormatter);
+ $this->addWriter($writerScreen);
+ }
- /**
- * Logs the given exception event
- *
- * @param Exception $exception
- */
- public function logEvent($exception)
- {
- $event = array();
- $event['errno'] = $exception->getCode();
- $event['message'] = $exception->getMessage();
- $event['errfile'] = $exception->getFile();
- $event['errline'] = $exception->getLine();
- $event['backtrace'] = $exception->getTraceAsString();
+ /**
+ * Logs the given exception event
+ *
+ * @param Exception $exception
+ */
+ public function logEvent($exception)
+ {
+ $event = array();
+ $event['errno'] = $exception->getCode();
+ $event['message'] = $exception->getMessage();
+ $event['errfile'] = $exception->getFile();
+ $event['errline'] = $exception->getLine();
+ $event['backtrace'] = $exception->getTraceAsString();
- parent::log($event, Piwik_Log::CRIT, null);
- }
+ parent::log($event, Piwik_Log::CRIT, null);
+ }
}
/**
@@ -82,20 +82,20 @@ class Piwik_Log_Exception extends Piwik_Log
*/
class Piwik_Log_Exception_Formatter_ScreenFormatter extends Piwik_Log_Formatter_ScreenFormatter
{
- /**
- * Formats data into a single line to be written by the writer.
- *
- * @param array $event event data
- * @return string formatted line to write to the log
- */
- public function format($event)
- {
- $event = parent::formatEvent($event);
- $errstr = $event['message'] ;
+ /**
+ * Formats data into a single line to be written by the writer.
+ *
+ * @param array $event event data
+ * @return string formatted line to write to the log
+ */
+ public function format($event)
+ {
+ $event = parent::formatEvent($event);
+ $errstr = $event['message'];
- $outputFormat = strtolower(Piwik_Common::getRequestVar('format', 'html', 'string'));
- $response = new Piwik_API_ResponseBuilder($outputFormat);
- $message = $response->getResponseException(new Exception($errstr));
- return parent::format($message );
- }
+ $outputFormat = strtolower(Piwik_Common::getRequestVar('format', 'html', 'string'));
+ $response = new Piwik_API_ResponseBuilder($outputFormat);
+ $message = $response->getResponseException(new Exception($errstr));
+ return parent::format($message);
+ }
}
diff --git a/core/Log/Message.php b/core/Log/Message.php
index 23a6777e69..5e8c428e64 100644
--- a/core/Log/Message.php
+++ b/core/Log/Message.php
@@ -1,96 +1,91 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Class used to log a standard message event.
- *
+ *
* @package Piwik
* @subpackage Piwik_Log
*/
class Piwik_Log_Message extends Piwik_Log
{
- const ID = 'logger_message';
+ const ID = 'logger_message';
- /**
- * Constructor
- */
- function __construct()
- {
- $logToFileFilename = self::ID.".htm";
- $logToDatabaseTableName = self::ID;
- $logToDatabaseColumnMapping = array(
- 'message' => 'message',
- 'timestamp' => 'timestamp'
- );
- $screenFormatter = new Piwik_Log_Message_Formatter_ScreenFormatter();
- $fileFormatter = new Piwik_Log_Formatter_FileFormatter();
+ /**
+ * Constructor
+ */
+ function __construct()
+ {
+ $logToFileFilename = self::ID . ".htm";
+ $logToDatabaseTableName = self::ID;
+ $logToDatabaseColumnMapping = array(
+ 'message' => 'message',
+ 'timestamp' => 'timestamp'
+ );
+ $screenFormatter = new Piwik_Log_Message_Formatter_ScreenFormatter();
+ $fileFormatter = new Piwik_Log_Formatter_FileFormatter();
- parent::__construct($logToFileFilename,
- $fileFormatter,
- $screenFormatter,
- $logToDatabaseTableName,
- $logToDatabaseColumnMapping );
- }
+ parent::__construct($logToFileFilename,
+ $fileFormatter,
+ $screenFormatter,
+ $logToDatabaseTableName,
+ $logToDatabaseColumnMapping);
+ }
- /**
- * Logs the given message
- *
- * @param string $message
- */
- public function logEvent($message)
- {
- $event = array();
- $event['message'] = $message;
- parent::log($event, Piwik_Log::INFO, null);
- }
+ /**
+ * Logs the given message
+ *
+ * @param string $message
+ */
+ public function logEvent($message)
+ {
+ $event = array();
+ $event['message'] = $message;
+ parent::log($event, Piwik_Log::INFO, null);
+ }
}
/**
* Format a standard message event to be displayed on the screen.
* The message can be a PHP array or a string.
- *
+ *
* @package Piwik
* @subpackage Piwik_Log
*/
-class Piwik_Log_Message_Formatter_ScreenFormatter extends Piwik_Log_Formatter_ScreenFormatter
+class Piwik_Log_Message_Formatter_ScreenFormatter extends Piwik_Log_Formatter_ScreenFormatter
{
- /**
+ /**
* Formats data into a single line to be written by the writer.
*
- * @param array $event event data
+ * @param array $event event data
* @return string formatted line to write to the log
*/
public function format($event)
{
- if(is_array($event['message']))
- {
- $message = "<pre>".var_export($event['message'], true)."</pre>";
- }
- else
- {
- $message = $event['message'];
- }
- if(!Piwik_Common::isPhpCliMode())
- {
- $message .= "<br/>";
- }
- $message .= "\n";
-
- $memory = '';
- // Hacky: let's hide the memory usage in CLI to hide from the archive.php output
- if(!Piwik_Common::isPhpCliMode())
- {
- $memory = '['.Piwik::getMemoryUsage(). '] ';
- }
- $message = '['. $event['timestamp'] . '] ['.$event['requestKey'].'] ' .$memory.$message;
- return parent::format($message);
+ if (is_array($event['message'])) {
+ $message = "<pre>" . var_export($event['message'], true) . "</pre>";
+ } else {
+ $message = $event['message'];
+ }
+ if (!Piwik_Common::isPhpCliMode()) {
+ $message .= "<br/>";
+ }
+ $message .= "\n";
+
+ $memory = '';
+ // Hacky: let's hide the memory usage in CLI to hide from the archive.php output
+ if (!Piwik_Common::isPhpCliMode()) {
+ $memory = '[' . Piwik::getMemoryUsage() . '] ';
+ }
+ $message = '[' . $event['timestamp'] . '] [' . $event['requestKey'] . '] ' . $memory . $message;
+ return parent::format($message);
}
}
diff --git a/core/Mail.php b/core/Mail.php
index 583266e870..833e01fdb6 100644
--- a/core/Mail.php
+++ b/core/Mail.php
@@ -4,78 +4,78 @@
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Class for sending mails, for more information see:
+ * Class for sending mails, for more information see:
*
* @package Piwik
* @see Zend_Mail, libs/Zend/Mail.php
- * @link http://framework.zend.com/manual/en/zend.mail.html
+ * @link http://framework.zend.com/manual/en/zend.mail.html
*/
class Piwik_Mail extends Zend_Mail
{
- /**
- * Default charset utf-8
- *
- * @param string $charset charset, defaults to utf-8
- */
- public function __construct($charset = 'utf-8')
- {
- parent::__construct($charset);
- $this->initSmtpTransport();
- }
+ /**
+ * Default charset utf-8
+ *
+ * @param string $charset charset, defaults to utf-8
+ */
+ public function __construct($charset = 'utf-8')
+ {
+ parent::__construct($charset);
+ $this->initSmtpTransport();
+ }
+
+ /**
+ * Sets the sender to use
+ *
+ * @param string $email
+ * @param null|string $name
+ */
+ public function setFrom($email, $name = null)
+ {
+ $hostname = Piwik_Config::getInstance()->mail['defaultHostnameIfEmpty'];
+ $piwikHost = Piwik_Url::getCurrentHost($hostname);
+
+ // If known Piwik URL, use it instead of "localhost"
+ $piwikUrl = Piwik::getPiwikUrl();
+ $url = parse_url($piwikUrl);
+ if (isset($url['host'])
+ && $url['host'] != 'localhost'
+ && $url['host'] != '127.0.0.1'
+ ) {
+ $piwikHost = $url['host'];
+ }
+ $email = str_replace('{DOMAIN}', $piwikHost, $email);
+ parent::setFrom($email, $name);
+ }
- /**
- * Sets the sender to use
- *
- * @param string $email
- * @param null|string $name
- */
- public function setFrom($email, $name = null)
- {
- $hostname = Piwik_Config::getInstance()->mail['defaultHostnameIfEmpty'];
- $piwikHost = Piwik_Url::getCurrentHost($hostname);
-
- // If known Piwik URL, use it instead of "localhost"
- $piwikUrl = Piwik::getPiwikUrl();
- $url = parse_url($piwikUrl);
- if(isset($url['host'])
- && $url['host'] != 'localhost'
- && $url['host'] != '127.0.0.1')
- {
- $piwikHost = $url['host'];
- }
- $email = str_replace('{DOMAIN}', $piwikHost, $email);
- parent::setFrom($email, $name);
- }
+ /**
+ * @return void
+ */
+ private function initSmtpTransport()
+ {
+ $mailConfig = Piwik_Config::getInstance()->mail;
+ if (empty($mailConfig['host'])
+ || $mailConfig['transport'] != 'smtp'
+ ) {
+ return;
+ }
+ $smtpConfig = array();
+ if (!empty($mailConfig['type']))
+ $smtpConfig['auth'] = strtolower($mailConfig['type']);
+ if (!empty($mailConfig['username']))
+ $smtpConfig['username'] = $mailConfig['username'];
+ if (!empty($mailConfig['password']))
+ $smtpConfig['password'] = $mailConfig['password'];
+ if (!empty($mailConfig['encryption']))
+ $smtpConfig['ssl'] = $mailConfig['encryption'];
- /**
- * @return void
- */
- private function initSmtpTransport()
- {
- $mailConfig = Piwik_Config::getInstance()->mail;
- if ( empty($mailConfig['host'])
- || $mailConfig['transport'] != 'smtp')
- {
- return;
- }
- $smtpConfig = array();
- if (!empty($mailConfig['type']))
- $smtpConfig['auth'] = strtolower($mailConfig['type']);
- if (!empty($mailConfig['username']))
- $smtpConfig['username'] = $mailConfig['username'];
- if (!empty($mailConfig['password']))
- $smtpConfig['password'] = $mailConfig['password'];
- if (!empty($mailConfig['encryption']))
- $smtpConfig['ssl'] = $mailConfig['encryption'];
-
- $tr = new Zend_Mail_Transport_Smtp($mailConfig['host'], $smtpConfig);
- Piwik_Mail::setDefaultTransport($tr);
- ini_set("smtp_port", $mailConfig['port']);
- }
+ $tr = new Zend_Mail_Transport_Smtp($mailConfig['host'], $smtpConfig);
+ Piwik_Mail::setDefaultTransport($tr);
+ ini_set("smtp_port", $mailConfig['port']);
+ }
}
diff --git a/core/Menu/Abstract.php b/core/Menu/Abstract.php
index 3a0b49c62b..553a34489e 100644
--- a/core/Menu/Abstract.php
+++ b/core/Menu/Abstract.php
@@ -15,231 +15,212 @@
abstract class Piwik_Menu_Abstract
{
- protected $menu = null;
- protected $menuEntries = array();
- protected $edits = array();
- protected $renames = array();
- protected $orderingApplied = false;
-
- /*
- * Can't enforce static function in 5.2.
- */
- //abstract static public function getInstance();
-
- /**
- * Builds the menu, applies edits, renames
- * and orders the entries.
- *
- * @return Array
- */
- public function get()
- {
- $this->buildMenu();
- $this->applyEdits();
- $this->applyRenames();
- $this->applyOrdering();
- return $this->menu;
- }
-
- /**
- * Adds a new entry to the menu.
- *
- * @param string $menuName
- * @param string $subMenuName
- * @param string $url
- * @param bool $displayedForCurrentUser
- * @param int $order
- * @param string $tooltip Tooltip to display.
- */
- public function add($menuName, $subMenuName, $url, $displayedForCurrentUser, $order = 50, $tooltip = false)
- {
- if($displayedForCurrentUser)
- {
- // make sure the idSite value used is numeric (hack-y fix for #3426)
- if (!is_numeric(Piwik_Common::getRequestVar('idSite', false)))
- {
- $idSites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess();
- $url['idSite'] = reset($idSites);
- }
-
- $this->menuEntries[] = array(
- $menuName,
- $subMenuName,
- $url,
- $order,
- $tooltip
- );
- }
- }
-
- /**
- * Builds a single menu item
- *
- * @param string $menuName
- * @param string $subMenuName
- * @param string $url
- * @param int $order
- * @param string $tooltip Tooltip to display.
- */
- private function buildMenuItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false)
- {
- if (!isset($this->menu[$menuName]) || empty($subMenuName))
- {
- $this->menu[$menuName]['_url'] = $url;
- if(empty($subMenuName)) {
- $this->menu[$menuName]['_order'] = $order;
- }
- $this->menu[$menuName]['_name'] = $menuName;
- $this->menu[$menuName]['_hasSubmenu'] = false;
- $this->menu[$menuName]['_tooltip'] = $tooltip;
- }
- if (!empty($subMenuName))
- {
- $this->menu[$menuName][$subMenuName]['_url'] = $url;
- $this->menu[$menuName][$subMenuName]['_order'] = $order;
- $this->menu[$menuName][$subMenuName]['_name'] = $subMenuName;
- $this->menu[$menuName]['_hasSubmenu'] = true;
- $this->menu[$menuName]['_tooltip'] = $tooltip;
- }
- }
-
- /**
- * Builds the menu from the $this->menuEntries variable.
- */
- private function buildMenu()
- {
- foreach ($this->menuEntries as $menuEntry)
- {
- $this->buildMenuItem($menuEntry[0], $menuEntry[1], $menuEntry[2], $menuEntry[3], $menuEntry[4]);
- }
- }
-
- /**
- * Renames a single menu entry.
- *
- * @param $mainMenuOriginal
- * @param $subMenuOriginal
- * @param $mainMenuRenamed
- * @param $subMenuRenamed
- */
- public function rename($mainMenuOriginal, $subMenuOriginal, $mainMenuRenamed, $subMenuRenamed)
- {
- $this->renames[] = array($mainMenuOriginal, $subMenuOriginal,
- $mainMenuRenamed, $subMenuRenamed);
- }
-
- /**
- * Edits a URL of an existing menu entry.
- *
- * @param $mainMenuToEdit
- * @param $subMenuToEdit
- * @param $newUrl
- */
- public function editUrl($mainMenuToEdit, $subMenuToEdit, $newUrl)
- {
- $this->edits[] = array($mainMenuToEdit, $subMenuToEdit, $newUrl);
- }
-
- /**
- * Applies all edits to the menu.
- */
- private function applyEdits()
- {
- foreach ($this->edits as $edit)
- {
- $mainMenuToEdit = $edit[0];
- $subMenuToEdit = $edit[1];
- $newUrl = $edit[2];
- if (!isset($this->menu[$mainMenuToEdit][$subMenuToEdit]))
- {
- $this->buildMenuItem($mainMenuToEdit, $subMenuToEdit, $newUrl);
- }
- else
- {
- $this->menu[$mainMenuToEdit][$subMenuToEdit]['_url'] = $newUrl;
- }
- }
- }
-
- /**
- * Applies renames to the menu.
- */
- private function applyRenames()
- {
- foreach ($this->renames as $rename)
- {
- $mainMenuOriginal = $rename[0];
- $subMenuOriginal = $rename[1];
- $mainMenuRenamed = $rename[2];
- $subMenuRenamed = $rename[3];
- // Are we changing a submenu?
- if (!empty($subMenuOriginal))
- {
- if (isset($this->menu[$mainMenuOriginal][$subMenuOriginal]))
- {
- $save = $this->menu[$mainMenuOriginal][$subMenuOriginal];
- $save['_name'] = $subMenuRenamed;
- unset($this->menu[$mainMenuOriginal][$subMenuOriginal]);
- $this->menu[$mainMenuRenamed][$subMenuRenamed] = $save;
- }
- }
- // Changing a first-level element
- else if (isset($this->menu[$mainMenuOriginal]))
- {
- $save = $this->menu[$mainMenuOriginal];
- $save['_name'] = $mainMenuRenamed;
- unset($this->menu[$mainMenuOriginal]);
- $this->menu[$mainMenuRenamed] = $save;
- }
- }
- }
-
- /**
- * Orders the menu according to their order.
- */
- private function applyOrdering()
- {
- if (empty($this->menu)
- || $this->orderingApplied)
- {
- return;
- }
-
- uasort($this->menu, array($this, 'menuCompare'));
- foreach ($this->menu as $key => &$element)
- {
- if (is_null($element))
- {
- unset($this->menu[$key]);
- }
- else if ($element['_hasSubmenu'])
- {
- uasort($element, array($this, 'menuCompare'));
- }
- }
-
- $this->orderingApplied = true;
- }
-
- /**
- * Compares two menu entries. Used for ordering.
- *
- * @param array $itemOne
- * @param array $itemTwo
- * @return boolean
- */
- protected function menuCompare($itemOne, $itemTwo)
- {
- if (!is_array($itemOne) || !is_array($itemTwo)
- || !isset($itemOne['_order']) || !isset($itemTwo['_order']))
- {
- return 0;
- }
-
- if ($itemOne['_order'] == $itemTwo['_order'])
- {
- return strcmp($itemOne['_name'], $itemTwo['_name']);
- }
- return ($itemOne['_order'] < $itemTwo['_order']) ? -1 : 1;
- }
+ protected $menu = null;
+ protected $menuEntries = array();
+ protected $edits = array();
+ protected $renames = array();
+ protected $orderingApplied = false;
+
+ /*
+ * Can't enforce static function in 5.2.
+ */
+ //abstract static public function getInstance();
+
+ /**
+ * Builds the menu, applies edits, renames
+ * and orders the entries.
+ *
+ * @return Array
+ */
+ public function get()
+ {
+ $this->buildMenu();
+ $this->applyEdits();
+ $this->applyRenames();
+ $this->applyOrdering();
+ return $this->menu;
+ }
+
+ /**
+ * Adds a new entry to the menu.
+ *
+ * @param string $menuName
+ * @param string $subMenuName
+ * @param string $url
+ * @param bool $displayedForCurrentUser
+ * @param int $order
+ * @param string $tooltip Tooltip to display.
+ */
+ public function add($menuName, $subMenuName, $url, $displayedForCurrentUser, $order = 50, $tooltip = false)
+ {
+ if ($displayedForCurrentUser) {
+ // make sure the idSite value used is numeric (hack-y fix for #3426)
+ if (!is_numeric(Piwik_Common::getRequestVar('idSite', false))) {
+ $idSites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess();
+ $url['idSite'] = reset($idSites);
+ }
+
+ $this->menuEntries[] = array(
+ $menuName,
+ $subMenuName,
+ $url,
+ $order,
+ $tooltip
+ );
+ }
+ }
+
+ /**
+ * Builds a single menu item
+ *
+ * @param string $menuName
+ * @param string $subMenuName
+ * @param string $url
+ * @param int $order
+ * @param string $tooltip Tooltip to display.
+ */
+ private function buildMenuItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false)
+ {
+ if (!isset($this->menu[$menuName]) || empty($subMenuName)) {
+ $this->menu[$menuName]['_url'] = $url;
+ if (empty($subMenuName)) {
+ $this->menu[$menuName]['_order'] = $order;
+ }
+ $this->menu[$menuName]['_name'] = $menuName;
+ $this->menu[$menuName]['_hasSubmenu'] = false;
+ $this->menu[$menuName]['_tooltip'] = $tooltip;
+ }
+ if (!empty($subMenuName)) {
+ $this->menu[$menuName][$subMenuName]['_url'] = $url;
+ $this->menu[$menuName][$subMenuName]['_order'] = $order;
+ $this->menu[$menuName][$subMenuName]['_name'] = $subMenuName;
+ $this->menu[$menuName]['_hasSubmenu'] = true;
+ $this->menu[$menuName]['_tooltip'] = $tooltip;
+ }
+ }
+
+ /**
+ * Builds the menu from the $this->menuEntries variable.
+ */
+ private function buildMenu()
+ {
+ foreach ($this->menuEntries as $menuEntry) {
+ $this->buildMenuItem($menuEntry[0], $menuEntry[1], $menuEntry[2], $menuEntry[3], $menuEntry[4]);
+ }
+ }
+
+ /**
+ * Renames a single menu entry.
+ *
+ * @param $mainMenuOriginal
+ * @param $subMenuOriginal
+ * @param $mainMenuRenamed
+ * @param $subMenuRenamed
+ */
+ public function rename($mainMenuOriginal, $subMenuOriginal, $mainMenuRenamed, $subMenuRenamed)
+ {
+ $this->renames[] = array($mainMenuOriginal, $subMenuOriginal,
+ $mainMenuRenamed, $subMenuRenamed);
+ }
+
+ /**
+ * Edits a URL of an existing menu entry.
+ *
+ * @param $mainMenuToEdit
+ * @param $subMenuToEdit
+ * @param $newUrl
+ */
+ public function editUrl($mainMenuToEdit, $subMenuToEdit, $newUrl)
+ {
+ $this->edits[] = array($mainMenuToEdit, $subMenuToEdit, $newUrl);
+ }
+
+ /**
+ * Applies all edits to the menu.
+ */
+ private function applyEdits()
+ {
+ foreach ($this->edits as $edit) {
+ $mainMenuToEdit = $edit[0];
+ $subMenuToEdit = $edit[1];
+ $newUrl = $edit[2];
+ if (!isset($this->menu[$mainMenuToEdit][$subMenuToEdit])) {
+ $this->buildMenuItem($mainMenuToEdit, $subMenuToEdit, $newUrl);
+ } else {
+ $this->menu[$mainMenuToEdit][$subMenuToEdit]['_url'] = $newUrl;
+ }
+ }
+ }
+
+ /**
+ * Applies renames to the menu.
+ */
+ private function applyRenames()
+ {
+ foreach ($this->renames as $rename) {
+ $mainMenuOriginal = $rename[0];
+ $subMenuOriginal = $rename[1];
+ $mainMenuRenamed = $rename[2];
+ $subMenuRenamed = $rename[3];
+ // Are we changing a submenu?
+ if (!empty($subMenuOriginal)) {
+ if (isset($this->menu[$mainMenuOriginal][$subMenuOriginal])) {
+ $save = $this->menu[$mainMenuOriginal][$subMenuOriginal];
+ $save['_name'] = $subMenuRenamed;
+ unset($this->menu[$mainMenuOriginal][$subMenuOriginal]);
+ $this->menu[$mainMenuRenamed][$subMenuRenamed] = $save;
+ }
+ } // Changing a first-level element
+ else if (isset($this->menu[$mainMenuOriginal])) {
+ $save = $this->menu[$mainMenuOriginal];
+ $save['_name'] = $mainMenuRenamed;
+ unset($this->menu[$mainMenuOriginal]);
+ $this->menu[$mainMenuRenamed] = $save;
+ }
+ }
+ }
+
+ /**
+ * Orders the menu according to their order.
+ */
+ private function applyOrdering()
+ {
+ if (empty($this->menu)
+ || $this->orderingApplied
+ ) {
+ return;
+ }
+
+ uasort($this->menu, array($this, 'menuCompare'));
+ foreach ($this->menu as $key => &$element) {
+ if (is_null($element)) {
+ unset($this->menu[$key]);
+ } else if ($element['_hasSubmenu']) {
+ uasort($element, array($this, 'menuCompare'));
+ }
+ }
+
+ $this->orderingApplied = true;
+ }
+
+ /**
+ * Compares two menu entries. Used for ordering.
+ *
+ * @param array $itemOne
+ * @param array $itemTwo
+ * @return boolean
+ */
+ protected function menuCompare($itemOne, $itemTwo)
+ {
+ if (!is_array($itemOne) || !is_array($itemTwo)
+ || !isset($itemOne['_order']) || !isset($itemTwo['_order'])
+ ) {
+ return 0;
+ }
+
+ if ($itemOne['_order'] == $itemTwo['_order']) {
+ return strcmp($itemOne['_name'], $itemTwo['_name']);
+ }
+ return ($itemOne['_order'] < $itemTwo['_order']) ? -1 : 1;
+ }
}
diff --git a/core/Menu/Admin.php b/core/Menu/Admin.php
index 9463e2291d..f85259811e 100644
--- a/core/Menu/Admin.php
+++ b/core/Menu/Admin.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik_Menu
*/
@@ -14,33 +14,31 @@
*/
class Piwik_Menu_Admin extends Piwik_Menu_Abstract
{
- static private $instance = null;
+ static private $instance = null;
- /**
- * @return Piwik_Menu_Admin
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
+ /**
+ * @return Piwik_Menu_Admin
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
- /**
- * Triggers the AdminMenu.add hook and returns the menu.
- *
- * @return Array
- */
- public function get()
- {
- if(!$this->menu)
- {
- Piwik_PostEvent('AdminMenu.add');
- }
- return parent::get();
- }
+ /**
+ * Triggers the AdminMenu.add hook and returns the menu.
+ *
+ * @return Array
+ */
+ public function get()
+ {
+ if (!$this->menu) {
+ Piwik_PostEvent('AdminMenu.add');
+ }
+ return parent::get();
+ }
}
/**
@@ -50,21 +48,20 @@ class Piwik_Menu_Admin extends Piwik_Menu_Abstract
*/
function Piwik_GetCurrentAdminMenuName()
{
- $menu = Piwik_GetAdminMenu();
- $currentModule = Piwik::getModule();
- $currentAction = Piwik::getAction();
- foreach($menu as $name => $submenu)
- {
- foreach($submenu as $subMenuName => $parameters) {
- if(strpos($subMenuName, '_') !== 0 &&
- $parameters['_url']['module'] == $currentModule
- && $parameters['_url']['action'] == $currentAction)
- {
- return $subMenuName;
- }
- }
- }
- return false;
+ $menu = Piwik_GetAdminMenu();
+ $currentModule = Piwik::getModule();
+ $currentAction = Piwik::getAction();
+ foreach ($menu as $name => $submenu) {
+ foreach ($submenu as $subMenuName => $parameters) {
+ if (strpos($subMenuName, '_') !== 0 &&
+ $parameters['_url']['module'] == $currentModule
+ && $parameters['_url']['action'] == $currentAction
+ ) {
+ return $subMenuName;
+ }
+ }
+ }
+ return false;
}
/**
@@ -74,43 +71,43 @@ function Piwik_GetCurrentAdminMenuName()
*/
function Piwik_GetAdminMenu()
{
- return Piwik_Menu_Admin::getInstance()->get();
+ return Piwik_Menu_Admin::getInstance()->get();
}
/**
* Adds a new AdminMenu entry.
*
- * @param string $adminMenuName
- * @param string $url
- * @param boolean $displayedForCurrentUser
- * @param int $order
+ * @param string $adminMenuName
+ * @param string $url
+ * @param boolean $displayedForCurrentUser
+ * @param int $order
*/
-function Piwik_AddAdminMenu( $adminMenuName, $url, $displayedForCurrentUser = true, $order = 10 )
+function Piwik_AddAdminMenu($adminMenuName, $url, $displayedForCurrentUser = true, $order = 10)
{
- Piwik_Menu_Admin::getInstance()->add('General_Settings', $adminMenuName, $url, $displayedForCurrentUser, $order);
+ Piwik_Menu_Admin::getInstance()->add('General_Settings', $adminMenuName, $url, $displayedForCurrentUser, $order);
}
/**
* Adds a new AdminMenu entry with a submenu.
*
- * @param string $adminMenuName
- * @param string $adminSubMenuName
- * @param string $url
- * @param boolean $displayedForCurrentUser
- * @param int $order
+ * @param string $adminMenuName
+ * @param string $adminSubMenuName
+ * @param string $url
+ * @param boolean $displayedForCurrentUser
+ * @param int $order
*/
-function Piwik_AddAdminSubMenu( $adminMenuName, $adminSubMenuName, $url, $displayedForCurrentUser = true, $order = 10 )
+function Piwik_AddAdminSubMenu($adminMenuName, $adminSubMenuName, $url, $displayedForCurrentUser = true, $order = 10)
{
- Piwik_Menu_Admin::getInstance()->add($adminMenuName, $adminSubMenuName, $url, $displayedForCurrentUser, $order);
+ Piwik_Menu_Admin::getInstance()->add($adminMenuName, $adminSubMenuName, $url, $displayedForCurrentUser, $order);
}
/**
* Renames an AdminMenu entry.
*
- * @param string $adminMenuOriginal
- * @param string $adminMenuRenamed
+ * @param string $adminMenuOriginal
+ * @param string $adminMenuRenamed
*/
function Piwik_RenameAdminMenuEntry($adminMenuOriginal, $adminMenuRenamed)
{
- Piwik_Menu_Admin::getInstance()->rename($adminMenuOriginal, null, $adminMenuRenamed, null);
+ Piwik_Menu_Admin::getInstance()->rename($adminMenuOriginal, null, $adminMenuRenamed, null);
}
diff --git a/core/Menu/Main.php b/core/Menu/Main.php
index 66eb237c5d..fa21883a30 100644
--- a/core/Menu/Main.php
+++ b/core/Menu/Main.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik_Menu
*/
@@ -14,69 +14,64 @@
*/
class Piwik_Menu_Main extends Piwik_Menu_Abstract
{
- static private $instance = null;
-
- /**
- * @return Piwik_Menu_Abstract
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
+ static private $instance = null;
+
+ /**
+ * @return Piwik_Menu_Abstract
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Returns if the URL was found in the menu.
+ *
+ * @param string $url
+ * @return boolean
+ */
+ public function isUrlFound($url)
+ {
+ $menu = Piwik_Menu_Main::getInstance()->get();
- /**
- * Returns if the URL was found in the menu.
- *
- * @param string $url
- * @return boolean
- */
- public function isUrlFound($url)
- {
- $menu = Piwik_Menu_Main::getInstance()->get();
-
- foreach($menu as $mainMenuName => $subMenus)
- {
- foreach($subMenus as $subMenuName => $menuUrl)
- {
- if(strpos($subMenuName, '_') !== 0 && $menuUrl['_url'] == $url)
- {
- return true;
- }
- }
- }
- return false;
- }
+ foreach ($menu as $mainMenuName => $subMenus) {
+ foreach ($subMenus as $subMenuName => $menuUrl) {
+ if (strpos($subMenuName, '_') !== 0 && $menuUrl['_url'] == $url) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Triggers the Menu.add hook and returns the menu.
+ *
+ * @return Array
+ */
+ public function get()
+ {
+ // We trigger the Event only once!
+ if (!$this->menu) {
+ Piwik_PostEvent('Menu.add');
+ }
+ return parent::get();
+ }
- /**
- * Triggers the Menu.add hook and returns the menu.
- *
- * @return Array
- */
- public function get()
- {
- // We trigger the Event only once!
- if(!$this->menu)
- {
- Piwik_PostEvent('Menu.add');
- }
- return parent::get();
- }
-
}
/**
* Checks if an entry uses the URL $url.
- *
- * @param string $url
+ *
+ * @param string $url
* @return boolean
*/
function Piwik_IsMenuUrlFound($url)
{
- return Piwik_Menu_Main::getInstance()->isUrlFound($url);
+ return Piwik_Menu_Main::getInstance()->isUrlFound($url);
}
/**
@@ -86,45 +81,45 @@ function Piwik_IsMenuUrlFound($url)
*/
function Piwik_GetMenu()
{
- return Piwik_Menu_Main::getInstance()->get();
+ return Piwik_Menu_Main::getInstance()->get();
}
/**
* Adds a new entry to the MainMenu.
*
- * @param string $mainMenuName
- * @param string $subMenuName
- * @param string $url
- * @param boolean $displayedForCurrentUser
- * @param int $order
+ * @param string $mainMenuName
+ * @param string $subMenuName
+ * @param string $url
+ * @param boolean $displayedForCurrentUser
+ * @param int $order
*/
-function Piwik_AddMenu( $mainMenuName, $subMenuName, $url, $displayedForCurrentUser = true, $order = 10)
+function Piwik_AddMenu($mainMenuName, $subMenuName, $url, $displayedForCurrentUser = true, $order = 10)
{
- Piwik_Menu_Main::getInstance()->add($mainMenuName, $subMenuName, $url, $displayedForCurrentUser, $order);
+ Piwik_Menu_Main::getInstance()->add($mainMenuName, $subMenuName, $url, $displayedForCurrentUser, $order);
}
/**
* Renames a menu entry.
- *
- * @param string $mainMenuOriginal
- * @param string $subMenuOriginal
- * @param string $mainMenuRenamed
- * @param string $subMenuRenamed
+ *
+ * @param string $mainMenuOriginal
+ * @param string $subMenuOriginal
+ * @param string $mainMenuRenamed
+ * @param string $subMenuRenamed
*/
-function Piwik_RenameMenuEntry($mainMenuOriginal, $subMenuOriginal,
- $mainMenuRenamed, $subMenuRenamed)
+function Piwik_RenameMenuEntry($mainMenuOriginal, $subMenuOriginal,
+ $mainMenuRenamed, $subMenuRenamed)
{
- Piwik_Menu_Main::getInstance()->rename($mainMenuOriginal, $subMenuOriginal, $mainMenuRenamed, $subMenuRenamed);
+ Piwik_Menu_Main::getInstance()->rename($mainMenuOriginal, $subMenuOriginal, $mainMenuRenamed, $subMenuRenamed);
}
/**
* Edits the URL of a menu entry.
- *
- * @param string $mainMenuToEdit
- * @param string $subMenuToEdit
- * @param string $newUrl
+ *
+ * @param string $mainMenuToEdit
+ * @param string $subMenuToEdit
+ * @param string $newUrl
*/
-function Piwik_EditMenuUrl( $mainMenuToEdit, $subMenuToEdit, $newUrl )
+function Piwik_EditMenuUrl($mainMenuToEdit, $subMenuToEdit, $newUrl)
{
- Piwik_Menu_Main::getInstance()->editUrl($mainMenuToEdit, $subMenuToEdit, $newUrl);
+ Piwik_Menu_Main::getInstance()->editUrl($mainMenuToEdit, $subMenuToEdit, $newUrl);
}
diff --git a/core/Menu/Top.php b/core/Menu/Top.php
index cbb768bb48..f24822ac26 100644
--- a/core/Menu/Top.php
+++ b/core/Menu/Top.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik_Menu
*/
@@ -14,56 +14,52 @@
*/
class Piwik_Menu_Top extends Piwik_Menu_Abstract
{
- static private $instance = null;
+ static private $instance = null;
- /**
- * @return Piwik_Menu_Top
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
+ /**
+ * @return Piwik_Menu_Top
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
- /**
- * Directly adds a menu entry containing html.
- *
- * @param string $menuName
- * @param string $data
- * @param boolean $displayedForCurrentUser
- * @param int $order
- * @param string $tooltip Tooltip to display.
- */
- public function addHtml($menuName, $data, $displayedForCurrentUser, $order, $tooltip)
- {
- if($displayedForCurrentUser)
- {
- if(!isset($this->menu[$menuName]))
- {
- $this->menu[$menuName]['_html'] = $data;
- $this->menu[$menuName]['_order'] = $order;
- $this->menu[$menuName]['_hasSubmenu'] = false;
- $this->menu[$menuName]['_tooltip'] = $tooltip;
- }
- }
- }
+ /**
+ * Directly adds a menu entry containing html.
+ *
+ * @param string $menuName
+ * @param string $data
+ * @param boolean $displayedForCurrentUser
+ * @param int $order
+ * @param string $tooltip Tooltip to display.
+ */
+ public function addHtml($menuName, $data, $displayedForCurrentUser, $order, $tooltip)
+ {
+ if ($displayedForCurrentUser) {
+ if (!isset($this->menu[$menuName])) {
+ $this->menu[$menuName]['_html'] = $data;
+ $this->menu[$menuName]['_order'] = $order;
+ $this->menu[$menuName]['_hasSubmenu'] = false;
+ $this->menu[$menuName]['_tooltip'] = $tooltip;
+ }
+ }
+ }
- /**
- * Triggers the TopMenu.add hook and returns the menu.
- *
- * @return Array
- */
- public function get()
- {
- if(!$this->menu)
- {
- Piwik_PostEvent('TopMenu.add');
- }
- return parent::get();
- }
+ /**
+ * Triggers the TopMenu.add hook and returns the menu.
+ *
+ * @return Array
+ */
+ public function get()
+ {
+ if (!$this->menu) {
+ Piwik_PostEvent('TopMenu.add');
+ }
+ return parent::get();
+ }
}
/**
@@ -73,39 +69,36 @@ class Piwik_Menu_Top extends Piwik_Menu_Abstract
*/
function Piwik_GetTopMenu()
{
- return Piwik_Menu_Top::getInstance()->get();
+ return Piwik_Menu_Top::getInstance()->get();
}
/**
* Adds a new entry to the TopMenu.
*
- * @param string $topMenuName
- * @param string $data
- * @param boolean $displayedForCurrentUser
- * @param int $order
- * @param bool $isHTML
- * @param string $tooltip Tooltip to display.
+ * @param string $topMenuName
+ * @param string $data
+ * @param boolean $displayedForCurrentUser
+ * @param int $order
+ * @param bool $isHTML
+ * @param string $tooltip Tooltip to display.
*/
-function Piwik_AddTopMenu( $topMenuName, $data, $displayedForCurrentUser = true, $order = 10, $isHTML = false,
- $tooltip = false)
+function Piwik_AddTopMenu($topMenuName, $data, $displayedForCurrentUser = true, $order = 10, $isHTML = false,
+ $tooltip = false)
{
- if($isHTML)
- {
- Piwik_Menu_Top::getInstance()->addHtml($topMenuName, $data, $displayedForCurrentUser, $order, $tooltip);
- }
- else
- {
- Piwik_Menu_Top::getInstance()->add($topMenuName, null, $data, $displayedForCurrentUser, $order, $tooltip);
- }
+ if ($isHTML) {
+ Piwik_Menu_Top::getInstance()->addHtml($topMenuName, $data, $displayedForCurrentUser, $order, $tooltip);
+ } else {
+ Piwik_Menu_Top::getInstance()->add($topMenuName, null, $data, $displayedForCurrentUser, $order, $tooltip);
+ }
}
/**
* Renames a entry of the TopMenu
*
- * @param string $topMenuOriginal
- * @param string $topMenuRenamed
+ * @param string $topMenuOriginal
+ * @param string $topMenuRenamed
*/
function Piwik_RenameTopMenuEntry($topMenuOriginal, $topMenuRenamed)
{
- Piwik_Menu_Top::getInstance()->rename($topMenuOriginal, null, $topMenuRenamed, null);
+ Piwik_Menu_Top::getInstance()->rename($topMenuOriginal, null, $topMenuRenamed, null);
}
diff --git a/core/Nonce.php b/core/Nonce.php
index b76cbd16e9..f7763ee15b 100644
--- a/core/Nonce.php
+++ b/core/Nonce.php
@@ -24,128 +24,121 @@
*/
class Piwik_Nonce
{
- /**
- * Generate nonce
- *
- * @param string $id Unique id to avoid namespace conflicts, e.g., ModuleName.ActionName
- * @param int $ttl Optional time-to-live in seconds; default is 5 minutes
- * @return string Nonce
- */
- static public function getNonce($id, $ttl = 300)
- {
- // save session-dependent nonce
- $ns = new Piwik_Session_Namespace($id);
- $nonce = $ns->nonce;
-
- // re-use an unexpired nonce (a small deviation from the "used only once" principle, so long as we do not reset the expiration)
- // to handle browser pre-fetch or double fetch caused by some browser add-ons/extensions
- if(empty($nonce))
- {
- // generate a new nonce
- $nonce = md5(Piwik_Common::getSalt() . time() . Piwik_Common::generateUniqId());
- $ns->nonce = $nonce;
- $ns->setExpirationSeconds($ttl, 'nonce');
- }
-
- return $nonce;
- }
-
- /**
- * Verify nonce and check referrer (if present, i.e., it may be suppressed by the browser or a proxy/network).
- *
- * @param string $id Unique id
- * @param string $cnonce Nonce sent to client
- * @return bool true if valid; false otherwise
- */
- static public function verifyNonce($id, $cnonce)
- {
- $ns = new Piwik_Session_Namespace($id);
- $nonce = $ns->nonce;
-
- // validate token
- if(empty($cnonce) || $cnonce !== $nonce)
- {
- return false;
- }
-
- // validate referer
- $referer = Piwik_Url::getReferer();
- if(!empty($referer) && !Piwik_Url::isLocalUrl($referer))
- {
- return false;
- }
-
- // validate origin
- $origin = self::getOrigin();
- if(!empty($origin) &&
- ($origin == 'null'
- || !in_array($origin, self::getAcceptableOrigins())))
- {
- return false;
- }
-
- return true;
- }
-
- /**
- * Discard nonce ("now" as opposed to waiting for garbage collection)
- *
- * @param string $id Unique id
- */
- static public function discardNonce($id)
- {
- $ns = new Piwik_Session_Namespace($id);
- $ns->unsetAll();
- }
-
- /**
- * Get ORIGIN header, false if not found
- *
- * @return string|false
- */
- static public function getOrigin()
- {
- if(!empty($_SERVER['HTTP_ORIGIN']))
- {
- return $_SERVER['HTTP_ORIGIN'];
- }
- return false;
- }
-
- /**
- * Returns acceptable origins (not simply scheme://host) that
- * should handle a variety of proxy and web server (mis)configurations,.
- *
- * @return array
- */
- static public function getAcceptableOrigins()
- {
- $host = Piwik_Url::getCurrentHost(null);
- $port = '';
-
- // parse host:port
- if(preg_match('/^([^:]+):([0-9]+)$/D', $host, $matches))
- {
- $host = $matches[1];
- $port = $matches[2];
- }
-
- if(empty($host))
- {
- return array();
- }
-
- // standard ports
- $origins[] = 'http://'.$host;
- $origins[] = 'https://'.$host;
-
- // non-standard ports
- if(!empty($port) && $port != 80 && $port != 443)
- {
- $origins[] = 'http://'.$host.':'.$port;
- $origins[] = 'https://'.$host.':'.$port;
- }
-
- return $origins;
- }
+ /**
+ * Generate nonce
+ *
+ * @param string $id Unique id to avoid namespace conflicts, e.g., ModuleName.ActionName
+ * @param int $ttl Optional time-to-live in seconds; default is 5 minutes
+ * @return string Nonce
+ */
+ static public function getNonce($id, $ttl = 300)
+ {
+ // save session-dependent nonce
+ $ns = new Piwik_Session_Namespace($id);
+ $nonce = $ns->nonce;
+
+ // re-use an unexpired nonce (a small deviation from the "used only once" principle, so long as we do not reset the expiration)
+ // to handle browser pre-fetch or double fetch caused by some browser add-ons/extensions
+ if (empty($nonce)) {
+ // generate a new nonce
+ $nonce = md5(Piwik_Common::getSalt() . time() . Piwik_Common::generateUniqId());
+ $ns->nonce = $nonce;
+ $ns->setExpirationSeconds($ttl, 'nonce');
+ }
+
+ return $nonce;
+ }
+
+ /**
+ * Verify nonce and check referrer (if present, i.e., it may be suppressed by the browser or a proxy/network).
+ *
+ * @param string $id Unique id
+ * @param string $cnonce Nonce sent to client
+ * @return bool true if valid; false otherwise
+ */
+ static public function verifyNonce($id, $cnonce)
+ {
+ $ns = new Piwik_Session_Namespace($id);
+ $nonce = $ns->nonce;
+
+ // validate token
+ if (empty($cnonce) || $cnonce !== $nonce) {
+ return false;
+ }
+
+ // validate referer
+ $referer = Piwik_Url::getReferer();
+ if (!empty($referer) && !Piwik_Url::isLocalUrl($referer)) {
+ return false;
+ }
+
+ // validate origin
+ $origin = self::getOrigin();
+ if (!empty($origin) &&
+ ($origin == 'null'
+ || !in_array($origin, self::getAcceptableOrigins()))
+ ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Discard nonce ("now" as opposed to waiting for garbage collection)
+ *
+ * @param string $id Unique id
+ */
+ static public function discardNonce($id)
+ {
+ $ns = new Piwik_Session_Namespace($id);
+ $ns->unsetAll();
+ }
+
+ /**
+ * Get ORIGIN header, false if not found
+ *
+ * @return string|false
+ */
+ static public function getOrigin()
+ {
+ if (!empty($_SERVER['HTTP_ORIGIN'])) {
+ return $_SERVER['HTTP_ORIGIN'];
+ }
+ return false;
+ }
+
+ /**
+ * Returns acceptable origins (not simply scheme://host) that
+ * should handle a variety of proxy and web server (mis)configurations,.
+ *
+ * @return array
+ */
+ static public function getAcceptableOrigins()
+ {
+ $host = Piwik_Url::getCurrentHost(null);
+ $port = '';
+
+ // parse host:port
+ if (preg_match('/^([^:]+):([0-9]+)$/D', $host, $matches)) {
+ $host = $matches[1];
+ $port = $matches[2];
+ }
+
+ if (empty($host)) {
+ return array();
+ }
+
+ // standard ports
+ $origins[] = 'http://' . $host;
+ $origins[] = 'https://' . $host;
+
+ // non-standard ports
+ if (!empty($port) && $port != 80 && $port != 443) {
+ $origins[] = 'http://' . $host . ':' . $port;
+ $origins[] = 'https://' . $host . ':' . $port;
+ }
+
+ return $origins;
+ }
}
diff --git a/core/Option.php b/core/Option.php
index 7fa0b2b42c..4275b8afa1 100644
--- a/core/Option.php
+++ b/core/Option.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,189 +12,184 @@
/**
* Piwik_Option provides a very simple mechanism to save/retrieve key-values pair
* from the database (persistent key-value datastore).
- *
+ *
* This is useful to save Piwik-wide preferences, configuration values.
- *
+ *
* @package Piwik
*/
class Piwik_Option
{
- /**
- * @var array
- */
- private $all = array();
-
- /**
- * @var bool
- */
- private $loaded = false;
-
- /**
- * Singleton instance
- * @var self
- */
- static private $instance = null;
-
- /**
- * Returns Singleton instance
- *
- * @return Piwik_Option
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Private Constructor
- */
- private function __construct() {}
-
- /**
- * Returns the option value for the requested option $name, fetching from database, if not in cache.
- *
- * @param string $name Key
- * @return string|false Value or false, if not found
- */
- public function get($name)
- {
- $this->autoload();
- if(isset($this->all[$name]))
- {
- return $this->all[$name];
- }
- $value = Piwik_FetchOne( 'SELECT option_value '.
- 'FROM `' . Piwik_Common::prefixTable('option') . '`'.
- 'WHERE option_name = ?', $name);
- if($value === false)
- {
- return false;
- }
- $this->all[$name] = $value;
- return $value;
- }
-
- /**
- * Sets the option value in the database and cache
- *
- * @param string $name
- * @param string $value
- * @param int $autoload if set to 1, this option value will be automatically loaded; should be set to 1 for options that will always be used in the Piwik request.
- */
- public function set($name, $value, $autoload = 0)
- {
- $autoload = (int)$autoload;
- Piwik_Query('INSERT INTO `'. Piwik_Common::prefixTable('option') . '` (option_name, option_value, autoload) '.
- ' VALUES (?, ?, ?) '.
- ' ON DUPLICATE KEY UPDATE option_value = ?',
- array($name, $value, $autoload, $value));
- $this->all[$name] = $value;
- }
-
- /**
- * Delete key-value pair from database and reload cache.
- *
- * @param string $name Key to match exactly
- * @param string $value Optional value
- */
- public function delete($name, $value = null)
- {
- $sql = 'DELETE FROM `'. Piwik_Common::prefixTable('option') . '` WHERE option_name = ?';
- $bind[] = $name;
-
- if(isset($value))
- {
- $sql .= ' AND option_value = ?';
- $bind[] = $value;
- }
-
- Piwik_Query($sql, $bind);
-
- $this->clearCache();
- }
-
- /**
- * Delete key-value pair(s) from database and reload cache.
- * The supplied pattern should use '%' as wildcards, and literal '_' should be escaped.
- *
- * @param string $name Pattern of key to match.
- * @param string $value Optional value
- */
- public function deleteLike($name, $value = null)
- {
- $sql = 'DELETE FROM `'. Piwik_Common::prefixTable('option') . '` WHERE option_name LIKE ?';
- $bind[] = $name;
-
- if(isset($value))
- {
- $sql .= ' AND option_value = ?';
- $bind[] = $value;
- }
-
- Piwik_Query($sql, $bind);
-
- $this->clearCache();
- }
-
- /**
- * Initialize cache with autoload settings.
- *
- * @return void
- */
- private function autoload()
- {
- if($this->loaded)
- {
- return;
- }
-
- $all = Piwik_FetchAll('SELECT option_value, option_name
- FROM `'. Piwik_Common::prefixTable('option') . '`
+ /**
+ * @var array
+ */
+ private $all = array();
+
+ /**
+ * @var bool
+ */
+ private $loaded = false;
+
+ /**
+ * Singleton instance
+ * @var self
+ */
+ static private $instance = null;
+
+ /**
+ * Returns Singleton instance
+ *
+ * @return Piwik_Option
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Private Constructor
+ */
+ private function __construct()
+ {
+ }
+
+ /**
+ * Returns the option value for the requested option $name, fetching from database, if not in cache.
+ *
+ * @param string $name Key
+ * @return string|false Value or false, if not found
+ */
+ public function get($name)
+ {
+ $this->autoload();
+ if (isset($this->all[$name])) {
+ return $this->all[$name];
+ }
+ $value = Piwik_FetchOne('SELECT option_value ' .
+ 'FROM `' . Piwik_Common::prefixTable('option') . '`' .
+ 'WHERE option_name = ?', $name);
+ if ($value === false) {
+ return false;
+ }
+ $this->all[$name] = $value;
+ return $value;
+ }
+
+ /**
+ * Sets the option value in the database and cache
+ *
+ * @param string $name
+ * @param string $value
+ * @param int $autoload if set to 1, this option value will be automatically loaded; should be set to 1 for options that will always be used in the Piwik request.
+ */
+ public function set($name, $value, $autoload = 0)
+ {
+ $autoload = (int)$autoload;
+ Piwik_Query('INSERT INTO `' . Piwik_Common::prefixTable('option') . '` (option_name, option_value, autoload) ' .
+ ' VALUES (?, ?, ?) ' .
+ ' ON DUPLICATE KEY UPDATE option_value = ?',
+ array($name, $value, $autoload, $value));
+ $this->all[$name] = $value;
+ }
+
+ /**
+ * Delete key-value pair from database and reload cache.
+ *
+ * @param string $name Key to match exactly
+ * @param string $value Optional value
+ */
+ public function delete($name, $value = null)
+ {
+ $sql = 'DELETE FROM `' . Piwik_Common::prefixTable('option') . '` WHERE option_name = ?';
+ $bind[] = $name;
+
+ if (isset($value)) {
+ $sql .= ' AND option_value = ?';
+ $bind[] = $value;
+ }
+
+ Piwik_Query($sql, $bind);
+
+ $this->clearCache();
+ }
+
+ /**
+ * Delete key-value pair(s) from database and reload cache.
+ * The supplied pattern should use '%' as wildcards, and literal '_' should be escaped.
+ *
+ * @param string $name Pattern of key to match.
+ * @param string $value Optional value
+ */
+ public function deleteLike($name, $value = null)
+ {
+ $sql = 'DELETE FROM `' . Piwik_Common::prefixTable('option') . '` WHERE option_name LIKE ?';
+ $bind[] = $name;
+
+ if (isset($value)) {
+ $sql .= ' AND option_value = ?';
+ $bind[] = $value;
+ }
+
+ Piwik_Query($sql, $bind);
+
+ $this->clearCache();
+ }
+
+ /**
+ * Initialize cache with autoload settings.
+ *
+ * @return void
+ */
+ private function autoload()
+ {
+ if ($this->loaded) {
+ return;
+ }
+
+ $all = Piwik_FetchAll('SELECT option_value, option_name
+ FROM `' . Piwik_Common::prefixTable('option') . '`
WHERE autoload = 1');
- foreach($all as $option)
- {
- $this->all[$option['option_name']] = $option['option_value'];
- }
-
- $this->loaded = true;
- }
-
- /**
- * Clears the cache
- * Used in unit tests to reset the state of the object between tests
- *
- * @return void
- */
- public function clearCache()
- {
- $this->loaded = false;
- $this->all = array();
- }
+ foreach ($all as $option) {
+ $this->all[$option['option_name']] = $option['option_value'];
+ }
+
+ $this->loaded = true;
+ }
+
+ /**
+ * Clears the cache
+ * Used in unit tests to reset the state of the object between tests
+ *
+ * @return void
+ */
+ public function clearCache()
+ {
+ $this->loaded = false;
+ $this->all = array();
+ }
}
/**
* Returns the option value for the requested option $name
*
- * @param string $name Key
+ * @param string $name Key
* @return string|false Value or false, if not found
*/
function Piwik_GetOption($name)
{
- return Piwik_Option::getInstance()->get($name);
+ return Piwik_Option::getInstance()->get($name);
}
/**
* Sets the option value in the database
*
- * @param string $name
- * @param string $value
- * @param int $autoload if set to 1, this option value will be automatically loaded; should be set to 1 for options that will always be used in the Piwik request.
+ * @param string $name
+ * @param string $value
+ * @param int $autoload if set to 1, this option value will be automatically loaded; should be set to 1 for options that will always be used in the Piwik request.
*/
function Piwik_SetOption($name, $value, $autoload = 0)
{
- Piwik_Option::getInstance()->set($name, $value, $autoload);
+ Piwik_Option::getInstance()->set($name, $value, $autoload);
}
diff --git a/core/Period.php b/core/Period.php
index 81ab037539..6e6d4cf4d6 100644
--- a/core/Period.php
+++ b/core/Period.php
@@ -1,24 +1,24 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * Creating a new Piwik_Period subclass:
- *
+ * Creating a new Piwik_Period subclass:
+ *
* Every overloaded method must start with the code
- * if(!$this->subperiodsProcessed)
- * {
- * $this->generate();
- * }
- * that checks whether the subperiods have already been computed.
- * This is for performance improvements, computing the subperiods is done a per demand basis.
+ * if(!$this->subperiodsProcessed)
+ * {
+ * $this->generate();
+ * }
+ * that checks whether the subperiods have already been computed.
+ * This is for performance improvements, computing the subperiods is done a per demand basis.
*
* @package Piwik
* @subpackage Piwik_Period
@@ -30,151 +30,145 @@ abstract class Piwik_Period
* @var Piwik_Period[]
*/
protected $subperiods = array();
- protected $subperiodsProcessed = false;
+ protected $subperiodsProcessed = false;
/**
* @var string
*/
protected $label = null;
- /**
- * @var Piwik_Date
- */
- protected $date = null;
- static protected $errorAvailablePeriods = 'day, week, month, year, range';
+ /**
+ * @var Piwik_Date
+ */
+ protected $date = null;
+ static protected $errorAvailablePeriods = 'day, week, month, year, range';
/**
* Constructor
* @param Piwik_Date $date
*/
- public function __construct( $date )
- {
- $this->checkInputDate( $date );
- $this->date = clone $date;
- }
+ public function __construct($date)
+ {
+ $this->checkInputDate($date);
+ $this->date = clone $date;
+ }
+
+ /**
+ * @param string $strPeriod "day", "week", "month", "year"
+ * @param Piwik_Date $date Piwik_Date object
+ * @throws Exception
+ * @return Piwik_Period
+ */
+ static public function factory($strPeriod, Piwik_Date $date)
+ {
+ switch ($strPeriod) {
+ case 'day':
+ return new Piwik_Period_Day($date);
+ break;
+
+ case 'week':
+ return new Piwik_Period_Week($date);
+ break;
+
+ case 'month':
+ return new Piwik_Period_Month($date);
+ break;
+
+ case 'year':
+ return new Piwik_Period_Year($date);
+ break;
+
+ default:
+ throw new Exception(Piwik_TranslateException('General_ExceptionInvalidPeriod', array($strPeriod, self::$errorAvailablePeriods)));
+ break;
+ }
+ }
+
+ /**
+ * The advanced factory method is easier to use from the API than the factory
+ * method above. It doesn't require an instance of Piwik_Date and works for
+ * period=range. Generally speaking, anything that can be passed as period
+ * and range to the API methods can directly be forwarded to this factory
+ * method in order to get a suitable instance of Piwik_Period.
+ *
+ * @param string $strPeriod "day", "week", "month", "year", "range"
+ * @param string $strDate
+ * @return Piwik_Period
+ */
+ static public function advancedFactory($strPeriod, $strDate)
+ {
+ if (Piwik_Archive::isMultiplePeriod($strDate, $strPeriod) || $strPeriod == 'range') {
+ return new Piwik_Period_Range($strPeriod, $strDate);
+ }
+ return self::factory($strPeriod, Piwik_Date::factory($strDate));
+ }
+
+
+ /**
+ * Returns the first day of the period
+ *
+ * @return Piwik_Date First day of the period
+ */
+ public function getDateStart()
+ {
+ if (!$this->subperiodsProcessed) {
+ $this->generate();
+ }
+ if (count($this->subperiods) == 0) {
+ return $this->getDate();
+ }
+ $periods = $this->getSubperiods();
+ $currentPeriod = $periods[0];
+ while ($currentPeriod->getNumberOfSubperiods() > 0) {
+ $periods = $currentPeriod->getSubperiods();
+ $currentPeriod = $periods[0];
+ }
+ return $currentPeriod->getDate();
+ }
- /**
- * @param string $strPeriod "day", "week", "month", "year"
- * @param Piwik_Date $date Piwik_Date object
- * @throws Exception
- * @return Piwik_Period
- */
- static public function factory($strPeriod, Piwik_Date $date)
- {
- switch ($strPeriod) {
- case 'day':
- return new Piwik_Period_Day($date);
- break;
-
- case 'week':
- return new Piwik_Period_Week($date);
- break;
-
- case 'month':
- return new Piwik_Period_Month($date);
- break;
-
- case 'year':
- return new Piwik_Period_Year($date);
- break;
-
- default:
- throw new Exception(Piwik_TranslateException('General_ExceptionInvalidPeriod', array($strPeriod, self::$errorAvailablePeriods)));
- break;
- }
- }
-
- /**
- * The advanced factory method is easier to use from the API than the factory
- * method above. It doesn't require an instance of Piwik_Date and works for
- * period=range. Generally speaking, anything that can be passed as period
- * and range to the API methods can directly be forwarded to this factory
- * method in order to get a suitable instance of Piwik_Period.
- *
- * @param string $strPeriod "day", "week", "month", "year", "range"
- * @param string $strDate
- * @return Piwik_Period
- */
- static public function advancedFactory($strPeriod, $strDate) {
- if (Piwik_Archive::isMultiplePeriod($strDate, $strPeriod) || $strPeriod == 'range')
- {
- return new Piwik_Period_Range($strPeriod, $strDate);
- }
- return self::factory($strPeriod, Piwik_Date::factory($strDate));
- }
+ /**
+ * Returns the last day of the period ; can be a date in the future
+ *
+ * @return Piwik_Date Last day of the period
+ */
+ public function getDateEnd()
+ {
+ if (!$this->subperiodsProcessed) {
+ $this->generate();
+ }
+ if (count($this->subperiods) == 0) {
+ return $this->getDate();
+ }
+ $periods = $this->getSubperiods();
+ $currentPeriod = $periods[count($periods) - 1];
+ while ($currentPeriod->getNumberOfSubperiods() > 0) {
+ $periods = $currentPeriod->getSubperiods();
+ $currentPeriod = $periods[count($periods) - 1];
+ }
+ return $currentPeriod->getDate();
+ }
-
- /**
- * Returns the first day of the period
- *
- * @return Piwik_Date First day of the period
- */
- public function getDateStart()
- {
- if(!$this->subperiodsProcessed)
- {
- $this->generate();
- }
- if(count($this->subperiods) == 0)
- {
- return $this->getDate();
- }
- $periods = $this->getSubperiods();
- $currentPeriod = $periods[0];
- while( $currentPeriod->getNumberOfSubperiods() > 0 )
- {
- $periods = $currentPeriod->getSubperiods();
- $currentPeriod = $periods[0];
- }
- return $currentPeriod->getDate();
- }
-
- /**
- * Returns the last day of the period ; can be a date in the future
- *
- * @return Piwik_Date Last day of the period
- */
- public function getDateEnd()
- {
- if(!$this->subperiodsProcessed)
- {
- $this->generate();
- }
- if(count($this->subperiods) == 0)
- {
- return $this->getDate();
- }
- $periods = $this->getSubperiods();
- $currentPeriod = $periods[count($periods)-1];
- while( $currentPeriod->getNumberOfSubperiods() > 0 )
- {
- $periods = $currentPeriod->getSubperiods();
- $currentPeriod = $periods[count($periods)-1];
- }
- return $currentPeriod->getDate();
- }
-
- public function getId()
- {
- return Piwik::$idPeriods[$this->getLabel()];
- }
+ public function getId()
+ {
+ return Piwik::$idPeriods[$this->getLabel()];
+ }
/**
* Returns the label for the current period
* @return string
*/
public function getLabel()
- {
- return $this->label;
- }
-
- /**
- * @return Piwik_Date
- */
- protected function getDate()
- {
- return $this->date;
- }
+ {
+ return $this->label;
+ }
+
+ /**
+ * @return Piwik_Date
+ */
+ protected function getDate()
+ {
+ return $this->date;
+ }
/**
* Checks if the given date is an instance of Piwik_Date
@@ -184,57 +178,54 @@ abstract class Piwik_Period
* @throws Exception
*/
protected function checkInputDate($date)
- {
- if( !($date instanceof Piwik_Date))
- {
- throw new Exception("The date must be a Piwik_Date object. " . var_export($date,true));
- }
- }
-
- protected function generate()
- {
- $this->subperiodsProcessed = true;
- }
+ {
+ if (!($date instanceof Piwik_Date)) {
+ throw new Exception("The date must be a Piwik_Date object. " . var_export($date, true));
+ }
+ }
+
+ protected function generate()
+ {
+ $this->subperiodsProcessed = true;
+ }
/**
* Returns the number of available subperiods
* @return int
*/
public function getNumberOfSubperiods()
- {
- if(!$this->subperiodsProcessed)
- {
- $this->generate();
- }
- return count($this->subperiods);
- }
-
- /**
- * Returns Period_Day for a period made of days (week, month),
- * Period_Month for a period made of months (year)
- *
- * @return array Piwik_Period
- */
- public function getSubperiods()
- {
- if(!$this->subperiodsProcessed)
- {
- $this->generate();
- }
- return $this->subperiods;
- }
+ {
+ if (!$this->subperiodsProcessed) {
+ $this->generate();
+ }
+ return count($this->subperiods);
+ }
- /**
- * Add a date to the period.
- *
- * Protected because it not yet supported to add periods after the initialization
- *
- * @param Piwik_Period $period Valid Piwik_Period object
- */
- protected function addSubperiod( $period )
- {
- $this->subperiods[] = $period;
- }
+ /**
+ * Returns Period_Day for a period made of days (week, month),
+ * Period_Month for a period made of months (year)
+ *
+ * @return array Piwik_Period
+ */
+ public function getSubperiods()
+ {
+ if (!$this->subperiodsProcessed) {
+ $this->generate();
+ }
+ return $this->subperiods;
+ }
+
+ /**
+ * Add a date to the period.
+ *
+ * Protected because it not yet supported to add periods after the initialization
+ *
+ * @param Piwik_Period $period Valid Piwik_Period object
+ */
+ protected function addSubperiod($period)
+ {
+ $this->subperiods[] = $period;
+ }
/**
* Returns a string representing the current period
@@ -245,34 +236,33 @@ abstract class Piwik_Period
* @return array
*/
public function toString($format = "Y-m-d")
- {
- if(!$this->subperiodsProcessed)
- {
- $this->generate();
- }
- $dateString = array();
- foreach($this->subperiods as $period)
- {
- $dateString[] = $period->toString($format);
- }
- return $dateString;
- }
-
- public function __toString()
- {
- return implode(",", $this->toString());
- }
-
- public function get( $part= null )
- {
- if(!$this->subperiodsProcessed)
- {
- $this->generate();
- }
- return $this->date->toString($part);
- }
-
- abstract public function getPrettyString();
- abstract public function getLocalizedShortString();
- abstract public function getLocalizedLongString();
+ {
+ if (!$this->subperiodsProcessed) {
+ $this->generate();
+ }
+ $dateString = array();
+ foreach ($this->subperiods as $period) {
+ $dateString[] = $period->toString($format);
+ }
+ return $dateString;
+ }
+
+ public function __toString()
+ {
+ return implode(",", $this->toString());
+ }
+
+ public function get($part = null)
+ {
+ if (!$this->subperiodsProcessed) {
+ $this->generate();
+ }
+ return $this->date->toString($part);
+ }
+
+ abstract public function getPrettyString();
+
+ abstract public function getLocalizedShortString();
+
+ abstract public function getLocalizedLongString();
}
diff --git a/core/Period/Day.php b/core/Period/Day.php
index b5439d8edb..14cdd16bd7 100644
--- a/core/Period/Day.php
+++ b/core/Period/Day.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,87 +15,87 @@
*/
class Piwik_Period_Day extends Piwik_Period
{
- protected $label = 'day';
+ protected $label = 'day';
- /**
- * Returns the day of the period as a string
- *
- * @return string
- */
- public function getPrettyString()
- {
- $out = $this->getDateStart()->toString() ;
- return $out;
- }
+ /**
+ * Returns the day of the period as a string
+ *
+ * @return string
+ */
+ public function getPrettyString()
+ {
+ $out = $this->getDateStart()->toString();
+ return $out;
+ }
- /**
- * Returns the day of the period as a localized short string
- *
- * @return string
- */
- public function getLocalizedShortString()
- {
- //"Mon 15 Aug"
- $date = $this->getDateStart();
- $out = $date->getLocalized(Piwik_Translate('CoreHome_ShortDateFormat'));
- return $out;
- }
+ /**
+ * Returns the day of the period as a localized short string
+ *
+ * @return string
+ */
+ public function getLocalizedShortString()
+ {
+ //"Mon 15 Aug"
+ $date = $this->getDateStart();
+ $out = $date->getLocalized(Piwik_Translate('CoreHome_ShortDateFormat'));
+ return $out;
+ }
- /**
- * Returns the day of the period as a localized long string
- *
- * @return string
- */
- public function getLocalizedLongString()
- {
- //"Mon 15 Aug"
- $date = $this->getDateStart();
- $template = Piwik_Translate('CoreHome_DateFormat');
- $out = $date->getLocalized($template);
- return $out;
- }
+ /**
+ * Returns the day of the period as a localized long string
+ *
+ * @return string
+ */
+ public function getLocalizedLongString()
+ {
+ //"Mon 15 Aug"
+ $date = $this->getDateStart();
+ $template = Piwik_Translate('CoreHome_DateFormat');
+ $out = $date->getLocalized($template);
+ return $out;
+ }
- /**
- * Returns the number of subperiods
- * Always 0, in that case
- *
- * @return int
- */
- public function getNumberOfSubperiods()
- {
- return 0;
- }
+ /**
+ * Returns the number of subperiods
+ * Always 0, in that case
+ *
+ * @return int
+ */
+ public function getNumberOfSubperiods()
+ {
+ return 0;
+ }
- /**
- * Adds a subperiod
- * Not supported for day periods
- *
- * @param $date
- * @throws Exception
- */
- public function addSubperiod( $date )
- {
- throw new Exception("Adding a subperiod is not supported for Piwik_Period_Day");
- }
+ /**
+ * Adds a subperiod
+ * Not supported for day periods
+ *
+ * @param $date
+ * @throws Exception
+ */
+ public function addSubperiod($date)
+ {
+ throw new Exception("Adding a subperiod is not supported for Piwik_Period_Day");
+ }
- /**
- * Returns the day of the period in the given format
- *
- * @param string $format
- * @return string
- */
- public function toString($format = "Y-m-d")
- {
- return $this->date->toString($format);
- }
+ /**
+ * Returns the day of the period in the given format
+ *
+ * @param string $format
+ * @return string
+ */
+ public function toString($format = "Y-m-d")
+ {
+ return $this->date->toString($format);
+ }
- /**
- * Returns the current period as a string
- *
- * @return string
- */
- public function __toString()
- {
- return $this->toString();
- }
+ /**
+ * Returns the current period as a string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
}
diff --git a/core/Period/Month.php b/core/Period/Month.php
index f7b21cdda8..fdb8a78dbc 100644
--- a/core/Period/Month.php
+++ b/core/Period/Month.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,62 +15,60 @@
*/
class Piwik_Period_Month extends Piwik_Period
{
- protected $label = 'month';
+ protected $label = 'month';
- /**
- * Returns the current period as a localized short string
- *
- * @return string
- */
- public function getLocalizedShortString()
- {
- //"Aug 09"
- $out = $this->getDateStart()->getLocalized(Piwik_Translate('CoreHome_ShortMonthFormat'));
- return $out;
- }
+ /**
+ * Returns the current period as a localized short string
+ *
+ * @return string
+ */
+ public function getLocalizedShortString()
+ {
+ //"Aug 09"
+ $out = $this->getDateStart()->getLocalized(Piwik_Translate('CoreHome_ShortMonthFormat'));
+ return $out;
+ }
- /**
- * Returns the current period as a localized long string
- *
- * @return string
- */
- public function getLocalizedLongString()
- {
- //"August 2009"
- $out = $this->getDateStart()->getLocalized(Piwik_Translate('CoreHome_LongMonthFormat'));
- return $out;
- }
+ /**
+ * Returns the current period as a localized long string
+ *
+ * @return string
+ */
+ public function getLocalizedLongString()
+ {
+ //"August 2009"
+ $out = $this->getDateStart()->getLocalized(Piwik_Translate('CoreHome_LongMonthFormat'));
+ return $out;
+ }
- /**
- * Returns the current period as a string
- *
- * @return string
- */
- public function getPrettyString()
- {
- $out = $this->getDateStart()->toString('Y-m');
- return $out;
- }
+ /**
+ * Returns the current period as a string
+ *
+ * @return string
+ */
+ public function getPrettyString()
+ {
+ $out = $this->getDateStart()->toString('Y-m');
+ return $out;
+ }
- /**
- * Generates the subperiods (one for each day in the month)
- */
- protected function generate()
- {
- if($this->subperiodsProcessed)
- {
- return;
- }
- parent::generate();
-
- $date = $this->date;
-
- $startMonth = $date->setDay(1);
- $currentDay = clone $startMonth;
- while($currentDay->compareMonth($startMonth) == 0)
- {
- $this->addSubperiod(new Piwik_Period_Day($currentDay));
- $currentDay = $currentDay->addDay(1);
- }
- }
+ /**
+ * Generates the subperiods (one for each day in the month)
+ */
+ protected function generate()
+ {
+ if ($this->subperiodsProcessed) {
+ return;
+ }
+ parent::generate();
+
+ $date = $this->date;
+
+ $startMonth = $date->setDay(1);
+ $currentDay = clone $startMonth;
+ while ($currentDay->compareMonth($startMonth) == 0) {
+ $this->addSubperiod(new Piwik_Period_Day($currentDay));
+ $currentDay = $currentDay->addDay(1);
+ }
+ }
}
diff --git a/core/Period/Range.php b/core/Period/Range.php
index b77ffec9fa..38dd9b350e 100644
--- a/core/Period/Range.php
+++ b/core/Period/Range.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -17,402 +17,362 @@
*/
class Piwik_Period_Range extends Piwik_Period
{
- protected $label = 'range';
-
- public function __construct( $strPeriod, $strDate, $timezone = 'UTC', $today = false )
- {
- $this->strPeriod = $strPeriod;
- $this->strDate = $strDate;
- $this->defaultEndDate = null;
- $this->timezone = $timezone;
- if($today === false)
- {
- $today = Piwik_Date::factory('today', $this->timezone);
- }
- $this->today = $today;
- }
-
- /**
- * Returns the current period as a localized short string
- *
- * @return string
- */
- public function getLocalizedShortString()
- {
- //"30 Dec 08 - 26 Feb 09"
- $dateStart = $this->getDateStart();
- $dateEnd = $this->getDateEnd();
- $template = Piwik_Translate('CoreHome_ShortDateFormatWithYear');
- $shortDateStart = $dateStart->getLocalized($template);
- $shortDateEnd = $dateEnd->getLocalized($template);
- $out = "$shortDateStart - $shortDateEnd";
- return $out;
- }
-
- /**
- * Returns the current period as a localized long string
- *
- * @return string
- */
- public function getLocalizedLongString()
- {
- return $this->getLocalizedShortString();
- }
-
- /**
- * Returns the start date of the period
- *
- * @return Piwik_Date
- * @throws Exception
- */
- public function getDateStart()
- {
- $dateStart = parent::getDateStart();
- if(empty($dateStart))
- {
- throw new Exception("Specified date range is invalid.");
- }
- return $dateStart;
- }
-
- /**
- * Returns the current period as a string
- *
- * @return string
- */
- public function getPrettyString()
- {
- $out = Piwik_Translate('General_DateRangeFromTo', array($this->getDateStart()->toString(), $this->getDateEnd()->toString()));
- return $out;
- }
-
- /**
- *
- * @param string $period
- * @param Piwik_Date $date
- * @param int $n
- * @throws Exception
- * @return Piwik_Date
- */
- static public function removePeriod( $period, Piwik_Date $date, $n )
- {
- switch($period)
- {
- case 'day':
- $startDate = $date->subDay( $n );
- break;
-
- case 'week':
- $startDate = $date->subDay( $n * 7 );
- break;
-
- case 'month':
- $startDate = $date->subMonth( $n );
- break;
-
- case 'year':
- $startDate = $date->subMonth( 12 * $n );
- break;
- default:
- throw new Exception('The period parameter is invalid');
- break;
- }
- return $startDate;
- }
-
- protected function getMaxN($lastN)
- {
- switch($this->strPeriod)
- {
- case 'day':
- $lastN = min( $lastN, 5*365 );
- break;
-
- case 'week':
- $lastN = min( $lastN, 10*52 );
- break;
-
- case 'month':
- $lastN = min( $lastN, 10*12 );
- break;
-
- case 'year':
- $lastN = min( $lastN, 10 );
- break;
- }
- return $lastN;
- }
-
- /**
- * Sets the default end date of the period
- *
- * @param Piwik_Date $oDate
- */
- public function setDefaultEndDate( Piwik_Date $oDate)
- {
- $this->defaultEndDate = $oDate;
- }
-
- /**
- * Generates the subperiods
- *
- * @throws Exception
- */
- protected function generate()
- {
- if($this->subperiodsProcessed)
- {
- return;
- }
- parent::generate();
-
- if(preg_match('/(last|previous)([0-9]*)/', $this->strDate, $regs))
- {
- $lastN = $regs[2];
- $lastOrPrevious = $regs[1];
- if(!is_null($this->defaultEndDate))
- {
- $defaultEndDate = $this->defaultEndDate;
- }
- else
- {
- $defaultEndDate = Piwik_Date::factory('now', $this->timezone);
- }
-
- $period = $this->strPeriod;
- if($period == 'range')
- {
- $period = 'day';
- }
-
- if($lastOrPrevious == 'last')
- {
- $endDate = $defaultEndDate;
- }
- elseif($lastOrPrevious == 'previous')
- {
- $endDate = self::removePeriod($period, $defaultEndDate, 1);
- }
-
- $lastN = $this->getMaxN($lastN);
-
- // last1 means only one result ; last2 means 2 results so we remove only 1 to the days/weeks/etc
- $lastN--;
- $lastN = abs($lastN);
-
- $startDate = self::removePeriod($period, $endDate, $lastN);
- }
- elseif( $dateRange = Piwik_Period_Range::parseDateRange($this->strDate) )
- {
- $strDateStart = $dateRange[1];
- $strDateEnd = $dateRange[2];
- $startDate = Piwik_Date::factory($strDateStart);
-
- if($strDateEnd == 'today')
- {
- $strDateEnd = 'now';
- }
- elseif($strDateEnd == 'yesterday')
- {
- $strDateEnd = 'yesterdaySameTime';
- }
- // we set the timezone in the Date object only if the date is relative eg. 'today', 'yesterday', 'now'
- $timezone = null;
- if(strpos($strDateEnd, '-') === false)
- {
- $timezone = $this->timezone;
- }
- $endDate = Piwik_Date::factory($strDateEnd, $timezone);
- }
- else
- {
- throw new Exception(Piwik_TranslateException('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
- $this->endDate = $endDate;
- }
-
- /**
- * Given a date string, returns false if not a date range,
- * or returns the array containing date start, date end
- *
- * @param string $dateString
- * @return mixed array(1 => dateStartString, 2 => dateEndString ) or false if the input was not a date range
- */
- static public 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;
- }
-
- protected $endDate = null;
-
- /**
- * Returns the end date of the period
- *
- * @return null|Piwik_Date
- */
- public function getDateEnd()
- {
- if(!is_null($this->endDate))
- {
- return $this->endDate;
- }
- return parent::getDateEnd();
- }
-
- /**
- * Determine which kind of period is best to use
- * See Range.test.php
- *
- * @param $startDate
- * @param $endDate
- */
- protected function processOptimalSubperiods($startDate, $endDate)
- {
- while($startDate->isEarlier($endDate)
- || $startDate == $endDate)
- {
- $endOfPeriod = null;
-
- $month = new Piwik_Period_Month($startDate);
- $endOfMonth = $month->getDateEnd();
- $startOfMonth = $month->getDateStart();
- if($startDate == $startOfMonth
- && ($endOfMonth->isEarlier($endDate)
- || $endOfMonth == $endDate
- || $endOfMonth->isLater($this->today)
- )
- // We don't use the month if
- // the end day is in this month, is before today, and month not finished
- && !($endDate->isEarlier($this->today)
- && $this->today->toString('Y') == $endOfMonth->toString('Y')
- && $this->today->compareMonth($endOfMonth) == 0)
- )
- {
- $this->addSubperiod($month);
- $endOfPeriod = $endOfMonth;
- }
- else
- {
- // From start date,
- // Process end of week
- $week = new Piwik_Period_Week($startDate);
- $startOfWeek = $week->getDateStart();
- $endOfWeek = $week->getDateEnd();
-
- $useMonthsNextIteration = $startDate->addPeriod(2, 'month')->setDay(1)->isEarlier($endDate);
- if($useMonthsNextIteration
- && $endOfWeek->isLater($endOfMonth))
- {
- $this->fillArraySubPeriods($startDate, $endOfMonth, 'day');
- $endOfPeriod = $endOfMonth;
- }
- // If end of this week is later than end date, we use days
- elseif($endOfWeek->isLater($endDate)
- && ($endOfWeek->isEarlier($this->today)
- || $endDate->isEarlier($this->today))
- )
- {
- $this->fillArraySubPeriods($startDate, $endDate, 'day');
- break 1;
- }
- elseif($startOfWeek->isEarlier($startDate)
- && $endOfWeek->isEarlier($this->today))
- {
- $this->fillArraySubPeriods($startDate, $endOfWeek, 'day');
- $endOfPeriod = $endOfWeek;
- }
- else
- {
- $this->addSubperiod($week);
- $endOfPeriod = $endOfWeek;
- }
- }
- $startDate = $endOfPeriod->addDay(1);
- }
- }
-
- /**
- * Adds new subperiods
- *
- * @param Piwik_Date $startDate
- * @param Piwik_Date $endDate
- * @param string $period
- */
- protected function fillArraySubPeriods($startDate, $endDate, $period)
- {
- $arrayPeriods= array();
- $endSubperiod = Piwik_Period::factory($period, $endDate);
- $arrayPeriods[] = $endSubperiod;
-
- // set end date to start of end period since we're comparing against start date.
- $endDate = $endSubperiod->getDateStart();
- while($endDate->isLater($startDate) )
- {
- $endDate = self::removePeriod($period, $endDate, 1);
- $subPeriod = Piwik_Period::factory($period, $endDate);
- $arrayPeriods[] = $subPeriod;
- }
- $arrayPeriods = array_reverse($arrayPeriods);
- foreach($arrayPeriods as $period)
- {
- $this->addSubperiod($period);
- }
- }
-
- /**
- * Returns the date that is one period before the supplied date.
- *
- * @param string $date The date to get the last date of.
- * @param string $period The period to use (either 'day', 'week', 'month', 'year');
- * @return array An array with two elements, a string for the date before $date and
- * a Piwik_Period instance for the period before $date.
- */
- public static function getLastDate( $date = false, $period = false )
- {
- if ($date === false)
- {
- $date = Piwik_Common::getRequestVar('date');
- }
-
- if ($period === false)
- {
- $period = Piwik_Common::getRequestVar('period');
- }
-
- // can't get the last date for range periods & dates that use lastN/previousN
- $strLastDate = false;
- $lastPeriod = false;
- if ($period != 'range' && !preg_match('/(last|previous)([0-9]*)/', $date, $regs))
- {
- if (strpos($date, ',')) // date in the form of 2011-01-01,2011-02-02
- {
- $rangePeriod = new Piwik_Period_Range($period, $date);
-
- $lastStartDate = Piwik_Period_Range::removePeriod($period, $rangePeriod->getDateStart(), $n = 1);
- $lastEndDate = Piwik_Period_Range::removePeriod($period, $rangePeriod->getDateEnd(), $n = 1);
-
- $strLastDate = "$lastStartDate,$lastEndDate";
- }
- else
- {
- $lastPeriod = Piwik_Period_Range::removePeriod($period, Piwik_Date::factory($date), $n = 1);
- $strLastDate = $lastPeriod->toString();
- }
- }
-
- return array($strLastDate, $lastPeriod);
- }
+ protected $label = 'range';
+
+ public function __construct($strPeriod, $strDate, $timezone = 'UTC', $today = false)
+ {
+ $this->strPeriod = $strPeriod;
+ $this->strDate = $strDate;
+ $this->defaultEndDate = null;
+ $this->timezone = $timezone;
+ if ($today === false) {
+ $today = Piwik_Date::factory('today', $this->timezone);
+ }
+ $this->today = $today;
+ }
+
+ /**
+ * Returns the current period as a localized short string
+ *
+ * @return string
+ */
+ public function getLocalizedShortString()
+ {
+ //"30 Dec 08 - 26 Feb 09"
+ $dateStart = $this->getDateStart();
+ $dateEnd = $this->getDateEnd();
+ $template = Piwik_Translate('CoreHome_ShortDateFormatWithYear');
+ $shortDateStart = $dateStart->getLocalized($template);
+ $shortDateEnd = $dateEnd->getLocalized($template);
+ $out = "$shortDateStart - $shortDateEnd";
+ return $out;
+ }
+
+ /**
+ * Returns the current period as a localized long string
+ *
+ * @return string
+ */
+ public function getLocalizedLongString()
+ {
+ return $this->getLocalizedShortString();
+ }
+
+ /**
+ * Returns the start date of the period
+ *
+ * @return Piwik_Date
+ * @throws Exception
+ */
+ public function getDateStart()
+ {
+ $dateStart = parent::getDateStart();
+ if (empty($dateStart)) {
+ throw new Exception("Specified date range is invalid.");
+ }
+ return $dateStart;
+ }
+
+ /**
+ * Returns the current period as a string
+ *
+ * @return string
+ */
+ public function getPrettyString()
+ {
+ $out = Piwik_Translate('General_DateRangeFromTo', array($this->getDateStart()->toString(), $this->getDateEnd()->toString()));
+ return $out;
+ }
+
+ /**
+ *
+ * @param string $period
+ * @param Piwik_Date $date
+ * @param int $n
+ * @throws Exception
+ * @return Piwik_Date
+ */
+ static public function removePeriod($period, Piwik_Date $date, $n)
+ {
+ switch ($period) {
+ case 'day':
+ $startDate = $date->subDay($n);
+ break;
+
+ case 'week':
+ $startDate = $date->subDay($n * 7);
+ break;
+
+ case 'month':
+ $startDate = $date->subMonth($n);
+ break;
+
+ case 'year':
+ $startDate = $date->subMonth(12 * $n);
+ break;
+ default:
+ throw new Exception('The period parameter is invalid');
+ break;
+ }
+ return $startDate;
+ }
+
+ protected function getMaxN($lastN)
+ {
+ switch ($this->strPeriod) {
+ case 'day':
+ $lastN = min($lastN, 5 * 365);
+ break;
+
+ case 'week':
+ $lastN = min($lastN, 10 * 52);
+ break;
+
+ case 'month':
+ $lastN = min($lastN, 10 * 12);
+ break;
+
+ case 'year':
+ $lastN = min($lastN, 10);
+ break;
+ }
+ return $lastN;
+ }
+
+ /**
+ * Sets the default end date of the period
+ *
+ * @param Piwik_Date $oDate
+ */
+ public function setDefaultEndDate(Piwik_Date $oDate)
+ {
+ $this->defaultEndDate = $oDate;
+ }
+
+ /**
+ * Generates the subperiods
+ *
+ * @throws Exception
+ */
+ protected function generate()
+ {
+ if ($this->subperiodsProcessed) {
+ return;
+ }
+ parent::generate();
+
+ if (preg_match('/(last|previous)([0-9]*)/', $this->strDate, $regs)) {
+ $lastN = $regs[2];
+ $lastOrPrevious = $regs[1];
+ if (!is_null($this->defaultEndDate)) {
+ $defaultEndDate = $this->defaultEndDate;
+ } else {
+ $defaultEndDate = Piwik_Date::factory('now', $this->timezone);
+ }
+
+ $period = $this->strPeriod;
+ if ($period == 'range') {
+ $period = 'day';
+ }
+
+ if ($lastOrPrevious == 'last') {
+ $endDate = $defaultEndDate;
+ } elseif ($lastOrPrevious == 'previous') {
+ $endDate = self::removePeriod($period, $defaultEndDate, 1);
+ }
+
+ $lastN = $this->getMaxN($lastN);
+
+ // last1 means only one result ; last2 means 2 results so we remove only 1 to the days/weeks/etc
+ $lastN--;
+ $lastN = abs($lastN);
+
+ $startDate = self::removePeriod($period, $endDate, $lastN);
+ } elseif ($dateRange = Piwik_Period_Range::parseDateRange($this->strDate)) {
+ $strDateStart = $dateRange[1];
+ $strDateEnd = $dateRange[2];
+ $startDate = Piwik_Date::factory($strDateStart);
+
+ if ($strDateEnd == 'today') {
+ $strDateEnd = 'now';
+ } elseif ($strDateEnd == 'yesterday') {
+ $strDateEnd = 'yesterdaySameTime';
+ }
+ // we set the timezone in the Date object only if the date is relative eg. 'today', 'yesterday', 'now'
+ $timezone = null;
+ if (strpos($strDateEnd, '-') === false) {
+ $timezone = $this->timezone;
+ }
+ $endDate = Piwik_Date::factory($strDateEnd, $timezone);
+ } else {
+ throw new Exception(Piwik_TranslateException('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
+ $this->endDate = $endDate;
+ }
+
+ /**
+ * Given a date string, returns false if not a date range,
+ * or returns the array containing date start, date end
+ *
+ * @param string $dateString
+ * @return mixed array(1 => dateStartString, 2 => dateEndString ) or false if the input was not a date range
+ */
+ static public 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;
+ }
+
+ protected $endDate = null;
+
+ /**
+ * Returns the end date of the period
+ *
+ * @return null|Piwik_Date
+ */
+ public function getDateEnd()
+ {
+ if (!is_null($this->endDate)) {
+ return $this->endDate;
+ }
+ return parent::getDateEnd();
+ }
+
+ /**
+ * Determine which kind of period is best to use
+ * See Range.test.php
+ *
+ * @param $startDate
+ * @param $endDate
+ */
+ protected function processOptimalSubperiods($startDate, $endDate)
+ {
+ while ($startDate->isEarlier($endDate)
+ || $startDate == $endDate) {
+ $endOfPeriod = null;
+
+ $month = new Piwik_Period_Month($startDate);
+ $endOfMonth = $month->getDateEnd();
+ $startOfMonth = $month->getDateStart();
+ if ($startDate == $startOfMonth
+ && ($endOfMonth->isEarlier($endDate)
+ || $endOfMonth == $endDate
+ || $endOfMonth->isLater($this->today)
+ )
+ // We don't use the month if
+ // the end day is in this month, is before today, and month not finished
+ && !($endDate->isEarlier($this->today)
+ && $this->today->toString('Y') == $endOfMonth->toString('Y')
+ && $this->today->compareMonth($endOfMonth) == 0)
+ ) {
+ $this->addSubperiod($month);
+ $endOfPeriod = $endOfMonth;
+ } else {
+ // From start date,
+ // Process end of week
+ $week = new Piwik_Period_Week($startDate);
+ $startOfWeek = $week->getDateStart();
+ $endOfWeek = $week->getDateEnd();
+
+ $useMonthsNextIteration = $startDate->addPeriod(2, 'month')->setDay(1)->isEarlier($endDate);
+ if ($useMonthsNextIteration
+ && $endOfWeek->isLater($endOfMonth)
+ ) {
+ $this->fillArraySubPeriods($startDate, $endOfMonth, 'day');
+ $endOfPeriod = $endOfMonth;
+ } // If end of this week is later than end date, we use days
+ elseif ($endOfWeek->isLater($endDate)
+ && ($endOfWeek->isEarlier($this->today)
+ || $endDate->isEarlier($this->today))
+ ) {
+ $this->fillArraySubPeriods($startDate, $endDate, 'day');
+ break 1;
+ } elseif ($startOfWeek->isEarlier($startDate)
+ && $endOfWeek->isEarlier($this->today)
+ ) {
+ $this->fillArraySubPeriods($startDate, $endOfWeek, 'day');
+ $endOfPeriod = $endOfWeek;
+ } else {
+ $this->addSubperiod($week);
+ $endOfPeriod = $endOfWeek;
+ }
+ }
+ $startDate = $endOfPeriod->addDay(1);
+ }
+ }
+
+ /**
+ * Adds new subperiods
+ *
+ * @param Piwik_Date $startDate
+ * @param Piwik_Date $endDate
+ * @param string $period
+ */
+ protected function fillArraySubPeriods($startDate, $endDate, $period)
+ {
+ $arrayPeriods = array();
+ $endSubperiod = Piwik_Period::factory($period, $endDate);
+ $arrayPeriods[] = $endSubperiod;
+
+ // set end date to start of end period since we're comparing against start date.
+ $endDate = $endSubperiod->getDateStart();
+ while ($endDate->isLater($startDate)) {
+ $endDate = self::removePeriod($period, $endDate, 1);
+ $subPeriod = Piwik_Period::factory($period, $endDate);
+ $arrayPeriods[] = $subPeriod;
+ }
+ $arrayPeriods = array_reverse($arrayPeriods);
+ foreach ($arrayPeriods as $period) {
+ $this->addSubperiod($period);
+ }
+ }
+
+ /**
+ * Returns the date that is one period before the supplied date.
+ *
+ * @param string $date The date to get the last date of.
+ * @param string $period The period to use (either 'day', 'week', 'month', 'year');
+ * @return array An array with two elements, a string for the date before $date and
+ * a Piwik_Period instance for the period before $date.
+ */
+ public static function getLastDate($date = false, $period = false)
+ {
+ if ($date === false) {
+ $date = Piwik_Common::getRequestVar('date');
+ }
+
+ if ($period === false) {
+ $period = Piwik_Common::getRequestVar('period');
+ }
+
+ // can't get the last date for range periods & dates that use lastN/previousN
+ $strLastDate = false;
+ $lastPeriod = false;
+ if ($period != 'range' && !preg_match('/(last|previous)([0-9]*)/', $date, $regs)) {
+ if (strpos($date, ',')) // date in the form of 2011-01-01,2011-02-02
+ {
+ $rangePeriod = new Piwik_Period_Range($period, $date);
+
+ $lastStartDate = Piwik_Period_Range::removePeriod($period, $rangePeriod->getDateStart(), $n = 1);
+ $lastEndDate = Piwik_Period_Range::removePeriod($period, $rangePeriod->getDateEnd(), $n = 1);
+
+ $strLastDate = "$lastStartDate,$lastEndDate";
+ } else {
+ $lastPeriod = Piwik_Period_Range::removePeriod($period, Piwik_Date::factory($date), $n = 1);
+ $strLastDate = $lastPeriod->toString();
+ }
+ }
+
+ return array($strLastDate, $lastPeriod);
+ }
}
diff --git a/core/Period/Week.php b/core/Period/Week.php
index f5dd546764..9b84f05b11 100644
--- a/core/Period/Week.php
+++ b/core/Period/Week.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -15,83 +15,80 @@
*/
class Piwik_Period_Week extends Piwik_Period
{
- protected $label = 'week';
+ protected $label = 'week';
- /**
- * Returns the current period as a localized short string
- *
- * @return string
- */
- public function getLocalizedShortString()
- {
- //"30 Dec - 6 Jan 09"
- $dateStart = $this->getDateStart();
- $dateEnd = $this->getDateEnd();
-
- $string = Piwik_Translate('CoreHome_ShortWeekFormat');
- $string = self::getTranslatedRange($string, $dateStart, $dateEnd);
- return $string;
- }
+ /**
+ * Returns the current period as a localized short string
+ *
+ * @return string
+ */
+ public function getLocalizedShortString()
+ {
+ //"30 Dec - 6 Jan 09"
+ $dateStart = $this->getDateStart();
+ $dateEnd = $this->getDateEnd();
- /**
- * Returns the current period as a localized long string
- *
- * @return string
- */
- public function getLocalizedLongString()
- {
- $format = Piwik_Translate('CoreHome_LongWeekFormat');
- $string = self::getTranslatedRange($format, $this->getDateStart(), $this->getDateEnd());
- return Piwik_Translate('CoreHome_PeriodWeek') . " " . $string;
- }
+ $string = Piwik_Translate('CoreHome_ShortWeekFormat');
+ $string = self::getTranslatedRange($string, $dateStart, $dateEnd);
+ return $string;
+ }
- static protected function getTranslatedRange($format, $dateStart, $dateEnd)
- {
- $string = str_replace('From%', '%', $format);
- $string = $dateStart->getLocalized($string);
- $string = str_replace('To%', '%', $string);
- $string = $dateEnd->getLocalized($string);
- return $string;
- }
-
- /**
- * Returns the current period as a string
- *
- * @return string
- */
- public function getPrettyString()
- {
- $out = Piwik_Translate('General_DateRangeFromTo',
- array($this->getDateStart()->toString(),
- $this->getDateEnd()->toString())
+ /**
+ * Returns the current period as a localized long string
+ *
+ * @return string
+ */
+ public function getLocalizedLongString()
+ {
+ $format = Piwik_Translate('CoreHome_LongWeekFormat');
+ $string = self::getTranslatedRange($format, $this->getDateStart(), $this->getDateEnd());
+ return Piwik_Translate('CoreHome_PeriodWeek') . " " . $string;
+ }
+
+ static protected function getTranslatedRange($format, $dateStart, $dateEnd)
+ {
+ $string = str_replace('From%', '%', $format);
+ $string = $dateStart->getLocalized($string);
+ $string = str_replace('To%', '%', $string);
+ $string = $dateEnd->getLocalized($string);
+ return $string;
+ }
+
+ /**
+ * Returns the current period as a string
+ *
+ * @return string
+ */
+ public function getPrettyString()
+ {
+ $out = Piwik_Translate('General_DateRangeFromTo',
+ array($this->getDateStart()->toString(),
+ $this->getDateEnd()->toString())
);
- return $out;
- }
+ return $out;
+ }
+
+ /**
+ * Generates the subperiods - one for each day in the week
+ */
+ protected function generate()
+ {
+ if ($this->subperiodsProcessed) {
+ return;
+ }
+ parent::generate();
+ $date = $this->date;
+
+ if ($date->toString('N') > 1) {
+ $date = $date->subDay($date->toString('N') - 1);
+ }
+
+ $startWeek = $date;
- /**
- * Generates the subperiods - one for each day in the week
- */
- protected function generate()
- {
- if($this->subperiodsProcessed)
- {
- return;
- }
- parent::generate();
- $date = $this->date;
-
- if( $date->toString('N') > 1)
- {
- $date = $date->subDay($date->toString('N')-1);
- }
-
- $startWeek = $date;
-
- $currentDay = clone $startWeek;
- while($currentDay->compareWeek($startWeek) == 0)
- {
- $this->addSubperiod(new Piwik_Period_Day($currentDay) );
- $currentDay = $currentDay->addDay(1);
- }
- }
+ $currentDay = clone $startWeek;
+ while ($currentDay->compareWeek($startWeek) == 0) {
+ $this->addSubperiod(new Piwik_Period_Day($currentDay));
+ $currentDay = $currentDay->addDay(1);
+ }
+ }
}
diff --git a/core/Period/Year.php b/core/Period/Year.php
index 64c6cc4acb..7ab5efbabe 100644
--- a/core/Period/Year.php
+++ b/core/Period/Year.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -14,80 +14,76 @@
* @subpackage Piwik_Period
*/
class Piwik_Period_Year extends Piwik_Period
-{
- protected $label = 'year';
+{
+ protected $label = 'year';
- /**
- * Returns the current period as a localized short string
- *
- * @return string
- */
- public function getLocalizedShortString()
- {
- return $this->getLocalizedLongString();
- }
+ /**
+ * Returns the current period as a localized short string
+ *
+ * @return string
+ */
+ public function getLocalizedShortString()
+ {
+ return $this->getLocalizedLongString();
+ }
- /**
- * Returns the current period as a localized long string
- *
- * @return string
- */
- public function getLocalizedLongString()
- {
- //"2009"
- $out = $this->getDateStart()->getLocalized("%longYear%");
- return $out;
- }
+ /**
+ * Returns the current period as a localized long string
+ *
+ * @return string
+ */
+ public function getLocalizedLongString()
+ {
+ //"2009"
+ $out = $this->getDateStart()->getLocalized("%longYear%");
+ return $out;
+ }
- /**
- * Returns the current period as a string
- *
- * @return string
- */
- public function getPrettyString()
- {
- $out = $this->getDateStart()->toString('Y');
- return $out;
- }
+ /**
+ * Returns the current period as a string
+ *
+ * @return string
+ */
+ public function getPrettyString()
+ {
+ $out = $this->getDateStart()->toString('Y');
+ return $out;
+ }
- /**
- * Generates the subperiods (one for each month of the year)
- */
- protected function generate()
- {
- if($this->subperiodsProcessed)
- {
- return;
- }
- parent::generate();
-
- $year = $this->date->toString("Y");
- for($i=1; $i<=12; $i++)
- {
- $this->addSubperiod( new Piwik_Period_Month(
- Piwik_Date::factory("$year-$i-01")
- )
- );
- }
- }
+ /**
+ * Generates the subperiods (one for each month of the year)
+ */
+ protected function generate()
+ {
+ if ($this->subperiodsProcessed) {
+ return;
+ }
+ parent::generate();
- /**
- * Returns the current period as a string
- *
- * @param string $format
- * @return array
- */
- function toString($format = 'ignored')
- {
- if(!$this->subperiodsProcessed)
- {
- $this->generate();
- }
- $stringMonth = array();
- foreach($this->subperiods as $month)
- {
- $stringMonth[] = $month->get("Y")."-".$month->get("m")."-01";
- }
- return $stringMonth;
- }
+ $year = $this->date->toString("Y");
+ for ($i = 1; $i <= 12; $i++) {
+ $this->addSubperiod(new Piwik_Period_Month(
+ Piwik_Date::factory("$year-$i-01")
+ )
+ );
+ }
+ }
+
+ /**
+ * Returns the current period as a string
+ *
+ * @param string $format
+ * @return array
+ */
+ function toString($format = 'ignored')
+ {
+ if (!$this->subperiodsProcessed) {
+ $this->generate();
+ }
+ $stringMonth = array();
+ foreach ($this->subperiods as $month) {
+ $stringMonth[] = $month->get("Y") . "-" . $month->get("m") . "-01";
+ }
+ return $stringMonth;
+ }
}
diff --git a/core/Piwik.php b/core/Piwik.php
index 4a694b7b28..a1ffc1df27 100644
--- a/core/Piwik.php
+++ b/core/Piwik.php
@@ -22,591 +22,547 @@ require_once PIWIK_INCLUDE_PATH . '/core/Translate.php';
*/
class Piwik
{
- const CLASSES_PREFIX = 'Piwik_';
- const COMPRESSED_FILE_LOCATION = '/tmp/assets/';
-
- /**
- * Piwik periods
- * @var array
- */
- public static $idPeriods = array(
- 'day' => 1,
- 'week' => 2,
- 'month' => 3,
- 'year' => 4,
- 'range' => 5,
- );
-
- /**
- * Should we process and display Unique Visitors?
- * -> Always process for day/week/month periods
- * For Year and Range, only process if it was enabled in the config file,
- *
- * @param string $periodLabel Period label (e.g., 'day')
- * @return bool
- */
- static public function isUniqueVisitorsEnabled($periodLabel)
- {
- $generalSettings = Piwik_Config::getInstance()->General;
-
- $settingName = "enable_processing_unique_visitors_$periodLabel";
- $result = !empty($generalSettings[$settingName]) && $generalSettings[$settingName] == 1;
-
- // check enable_processing_unique_visitors_year_and_range for backwards compatibility
- if (($periodLabel == 'year' || $periodLabel == 'range')
- && isset($generalSettings['enable_processing_unique_visitors_year_and_range']))
- {
- $result |= $generalSettings['enable_processing_unique_visitors_year_and_range'] == 1;
- }
-
- return $result;
- }
-
- /**
- * Prefix class name (if needed)
- *
- * @param string $class
- * @return string
- */
- static public function prefixClass( $class )
- {
- if(!strncmp($class, Piwik::CLASSES_PREFIX, strlen(Piwik::CLASSES_PREFIX)))
- {
- return $class;
- }
- return Piwik::CLASSES_PREFIX.$class;
- }
-
- /**
- * Unprefix class name (if needed)
- *
- * @param string $class
- * @return string
- */
- static public function unprefixClass( $class )
- {
- $lenPrefix = strlen(Piwik::CLASSES_PREFIX);
- if(!strncmp($class, Piwik::CLASSES_PREFIX, $lenPrefix))
- {
- return substr($class, $lenPrefix);
- }
- return $class;
- }
-
- /**
- * Installation helper
- */
- static public function install()
- {
- Piwik_Common::mkdir(PIWIK_USER_PATH . '/' . Piwik_Config::getInstance()->smarty['compile_dir']);
- }
-
- /**
- * Uninstallation helper
- */
- static public function uninstall()
- {
- Piwik_Db_Schema::getInstance()->dropTables();
- }
-
- /**
- * Returns true if Piwik is installed
- *
- * @since 0.6.3
- *
- * @return bool True if installed; false otherwise
- */
- static public function isInstalled()
- {
- try {
- return Piwik_Db_Schema::getInstance()->hasTables();
- } catch(Exception $e) {
- return false;
- }
- }
-
- /**
- * Called on Core install, update, plugin enable/disable
- * Will clear all cache that could be affected by the change in configuration being made
- */
- static public function deleteAllCacheOnUpdate()
- {
- Piwik_AssetManager::removeMergedAssets();
- Piwik_View::clearCompiledTemplates();
- Piwik_Tracker_Cache::deleteTrackerCache();
- }
-
- /**
- * Cache for result of getPiwikUrl.
- * Can be overwritten for testing purposes only.
- *
- * @var string
- */
- static public $piwikUrlCache = null;
-
- /**
- * Returns the cached the Piwik URL, eg. http://demo.piwik.org/ or http://example.org/piwik/
- * If not found, then tries to cache it and returns the value.
- *
- * If the Piwik URL changes (eg. Piwik moved to new server), the value will automatically be refreshed in the cache.
- *
- * @return string
- */
- static public function getPiwikUrl()
- {
- // Only set in tests
- if (self::$piwikUrlCache !== null)
- {
- return self::$piwikUrlCache;
- }
-
- $key = 'piwikUrl';
- $url = Piwik_GetOption($key);
- if(Piwik_Common::isPhpCliMode()
- // in case archive.php is triggered with domain localhost
- || Piwik_Common::isArchivePhpTriggered()
- || defined('PIWIK_MODE_ARCHIVE'))
- {
- return $url;
- }
-
- $currentUrl = Piwik_Common::sanitizeInputValue(Piwik_Url::getCurrentUrlWithoutFileName());
-
- if(empty($url)
- // if URL changes, always update the cache
- || $currentUrl != $url)
- {
- if(strlen($currentUrl) >= strlen('http://a/'))
- {
- Piwik_SetOption($key, $currentUrl, $autoload = true);
- }
- $url = $currentUrl;
- }
- return $url;
- }
-
- /**
- * Returns true if this appears to be a secure HTTPS connection
- *
- * @return bool
- */
- static public function isHttps()
- {
- return Piwik_Url::getCurrentScheme() === 'https';
- }
-
- /**
- * Set response header, e.g., HTTP/1.0 200 Ok
- *
- * @param string $status Status
- * @return bool
- */
- static public function setHttpStatus($status)
- {
- if(substr_compare(PHP_SAPI, '-fcgi', -5))
- {
- @header($_SERVER['SERVER_PROTOCOL'] . ' ' . $status);
- }
- else
- {
- // FastCGI
- @header('Status: ' . $status);
- }
- }
-
- /**
- * Workaround IE bug when downloading certain document types over SSL and
- * cache control headers are present, e.g.,
- *
- * Cache-Control: no-cache
- * Cache-Control: no-store,max-age=0,must-revalidate
- * Pragma: no-cache
- *
- * @see http://support.microsoft.com/kb/316431/
- * @see RFC2616
- *
- * @param string $override One of "public", "private", "no-cache", or "no-store". (optional)
- */
- static public function overrideCacheControlHeaders($override = null)
- {
- if($override || self::isHttps())
- {
- @header('Pragma: ');
- @header('Expires: ');
- if(in_array($override, array('public', 'private', 'no-cache', 'no-store')))
- {
- @header("Cache-Control: $override, must-revalidate");
- }
- else
- {
- @header('Cache-Control: must-revalidate');
- }
- }
- }
-
-/*
+ const CLASSES_PREFIX = 'Piwik_';
+ const COMPRESSED_FILE_LOCATION = '/tmp/assets/';
+
+ /**
+ * Piwik periods
+ * @var array
+ */
+ public static $idPeriods = array(
+ 'day' => 1,
+ 'week' => 2,
+ 'month' => 3,
+ 'year' => 4,
+ 'range' => 5,
+ );
+
+ /**
+ * Should we process and display Unique Visitors?
+ * -> Always process for day/week/month periods
+ * For Year and Range, only process if it was enabled in the config file,
+ *
+ * @param string $periodLabel Period label (e.g., 'day')
+ * @return bool
+ */
+ static public function isUniqueVisitorsEnabled($periodLabel)
+ {
+ $generalSettings = Piwik_Config::getInstance()->General;
+
+ $settingName = "enable_processing_unique_visitors_$periodLabel";
+ $result = !empty($generalSettings[$settingName]) && $generalSettings[$settingName] == 1;
+
+ // check enable_processing_unique_visitors_year_and_range for backwards compatibility
+ if (($periodLabel == 'year' || $periodLabel == 'range')
+ && isset($generalSettings['enable_processing_unique_visitors_year_and_range'])
+ ) {
+ $result |= $generalSettings['enable_processing_unique_visitors_year_and_range'] == 1;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Prefix class name (if needed)
+ *
+ * @param string $class
+ * @return string
+ */
+ static public function prefixClass($class)
+ {
+ if (!strncmp($class, Piwik::CLASSES_PREFIX, strlen(Piwik::CLASSES_PREFIX))) {
+ return $class;
+ }
+ return Piwik::CLASSES_PREFIX . $class;
+ }
+
+ /**
+ * Unprefix class name (if needed)
+ *
+ * @param string $class
+ * @return string
+ */
+ static public function unprefixClass($class)
+ {
+ $lenPrefix = strlen(Piwik::CLASSES_PREFIX);
+ if (!strncmp($class, Piwik::CLASSES_PREFIX, $lenPrefix)) {
+ return substr($class, $lenPrefix);
+ }
+ return $class;
+ }
+
+ /**
+ * Installation helper
+ */
+ static public function install()
+ {
+ Piwik_Common::mkdir(PIWIK_USER_PATH . '/' . Piwik_Config::getInstance()->smarty['compile_dir']);
+ }
+
+ /**
+ * Uninstallation helper
+ */
+ static public function uninstall()
+ {
+ Piwik_Db_Schema::getInstance()->dropTables();
+ }
+
+ /**
+ * Returns true if Piwik is installed
+ *
+ * @since 0.6.3
+ *
+ * @return bool True if installed; false otherwise
+ */
+ static public function isInstalled()
+ {
+ try {
+ return Piwik_Db_Schema::getInstance()->hasTables();
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Called on Core install, update, plugin enable/disable
+ * Will clear all cache that could be affected by the change in configuration being made
+ */
+ static public function deleteAllCacheOnUpdate()
+ {
+ Piwik_AssetManager::removeMergedAssets();
+ Piwik_View::clearCompiledTemplates();
+ Piwik_Tracker_Cache::deleteTrackerCache();
+ }
+
+ /**
+ * Cache for result of getPiwikUrl.
+ * Can be overwritten for testing purposes only.
+ *
+ * @var string
+ */
+ static public $piwikUrlCache = null;
+
+ /**
+ * Returns the cached the Piwik URL, eg. http://demo.piwik.org/ or http://example.org/piwik/
+ * If not found, then tries to cache it and returns the value.
+ *
+ * If the Piwik URL changes (eg. Piwik moved to new server), the value will automatically be refreshed in the cache.
+ *
+ * @return string
+ */
+ static public function getPiwikUrl()
+ {
+ // Only set in tests
+ if (self::$piwikUrlCache !== null) {
+ return self::$piwikUrlCache;
+ }
+
+ $key = 'piwikUrl';
+ $url = Piwik_GetOption($key);
+ if (Piwik_Common::isPhpCliMode()
+ // in case archive.php is triggered with domain localhost
+ || Piwik_Common::isArchivePhpTriggered()
+ || defined('PIWIK_MODE_ARCHIVE')
+ ) {
+ return $url;
+ }
+
+ $currentUrl = Piwik_Common::sanitizeInputValue(Piwik_Url::getCurrentUrlWithoutFileName());
+
+ if (empty($url)
+ // if URL changes, always update the cache
+ || $currentUrl != $url
+ ) {
+ if (strlen($currentUrl) >= strlen('http://a/')) {
+ Piwik_SetOption($key, $currentUrl, $autoload = true);
+ }
+ $url = $currentUrl;
+ }
+ return $url;
+ }
+
+ /**
+ * Returns true if this appears to be a secure HTTPS connection
+ *
+ * @return bool
+ */
+ static public function isHttps()
+ {
+ return Piwik_Url::getCurrentScheme() === 'https';
+ }
+
+ /**
+ * Set response header, e.g., HTTP/1.0 200 Ok
+ *
+ * @param string $status Status
+ * @return bool
+ */
+ static public function setHttpStatus($status)
+ {
+ if (substr_compare(PHP_SAPI, '-fcgi', -5)) {
+ @header($_SERVER['SERVER_PROTOCOL'] . ' ' . $status);
+ } else {
+ // FastCGI
+ @header('Status: ' . $status);
+ }
+ }
+
+ /**
+ * Workaround IE bug when downloading certain document types over SSL and
+ * cache control headers are present, e.g.,
+ *
+ * Cache-Control: no-cache
+ * Cache-Control: no-store,max-age=0,must-revalidate
+ * Pragma: no-cache
+ *
+ * @see http://support.microsoft.com/kb/316431/
+ * @see RFC2616
+ *
+ * @param string $override One of "public", "private", "no-cache", or "no-store". (optional)
+ */
+ static public function overrideCacheControlHeaders($override = null)
+ {
+ if ($override || self::isHttps()) {
+ @header('Pragma: ');
+ @header('Expires: ');
+ if (in_array($override, array('public', 'private', 'no-cache', 'no-store'))) {
+ @header("Cache-Control: $override, must-revalidate");
+ } else {
+ @header('Cache-Control: must-revalidate');
+ }
+ }
+ }
+
+ /*
* File and directory operations
*/
- /**
- * Copy recursively from $source to $target.
- *
- * @param string $source eg. './tmp/latest'
- * @param string $target eg. '.'
- * @param bool $excludePhp
- */
- static public function copyRecursive($source, $target, $excludePhp=false )
- {
- if ( is_dir( $source ) )
- {
- Piwik_Common::mkdir( $target, false );
- $d = dir( $source );
- while ( false !== ( $entry = $d->read() ) )
- {
- if ( $entry == '.' || $entry == '..' )
- {
- continue;
- }
-
- $sourcePath = $source . '/' . $entry;
- if ( is_dir( $sourcePath ) )
- {
- self::copyRecursive( $sourcePath, $target . '/' . $entry, $excludePhp );
- continue;
- }
- $destPath = $target . '/' . $entry;
- self::copy($sourcePath, $destPath, $excludePhp);
- }
- $d->close();
- }
- else
- {
- self::copy($source, $target, $excludePhp);
- }
- }
-
- /**
- * Copy individual file from $source to $target.
- *
- * @param string $source eg. './tmp/latest/index.php'
- * @param string $dest eg. './index.php'
- * @param bool $excludePhp
- * @throws Exception
- * @return bool
- */
- static public function copy($source, $dest, $excludePhp=false)
- {
- static $phpExtensions = array('php', 'tpl');
-
- if($excludePhp)
- {
- $path_parts = pathinfo($source);
- if(in_array($path_parts['extension'], $phpExtensions))
- {
- return true;
- }
- }
-
- if(!@copy( $source, $dest ))
- {
- @chmod($dest, 0755);
- if(!@copy( $source, $dest ))
- {
- $message = "Error while creating/copying file to <code>$dest</code>. <br />"
- . self::getErrorMessageMissingPermissions(Piwik_Common::getPathToPiwikRoot());
- throw new Exception($message);
- }
- }
- return true;
- }
-
- /**
- * Returns friendly error message explaining how to fix permissions
- *
- * @param string $path to the directory missing permissions
- * @return string Error message
- */
- static public function getErrorMessageMissingPermissions($path)
- {
- $message = "Please check that the web server has enough permission to write to these files/directories:<br />";
-
- if(Piwik_Common::isWindows())
- {
- $message .= "On Windows, check that the folder is not read only and is writable.
+ /**
+ * Copy recursively from $source to $target.
+ *
+ * @param string $source eg. './tmp/latest'
+ * @param string $target eg. '.'
+ * @param bool $excludePhp
+ */
+ static public function copyRecursive($source, $target, $excludePhp = false)
+ {
+ if (is_dir($source)) {
+ Piwik_Common::mkdir($target, false);
+ $d = dir($source);
+ while (false !== ($entry = $d->read())) {
+ if ($entry == '.' || $entry == '..') {
+ continue;
+ }
+
+ $sourcePath = $source . '/' . $entry;
+ if (is_dir($sourcePath)) {
+ self::copyRecursive($sourcePath, $target . '/' . $entry, $excludePhp);
+ continue;
+ }
+ $destPath = $target . '/' . $entry;
+ self::copy($sourcePath, $destPath, $excludePhp);
+ }
+ $d->close();
+ } else {
+ self::copy($source, $target, $excludePhp);
+ }
+ }
+
+ /**
+ * Copy individual file from $source to $target.
+ *
+ * @param string $source eg. './tmp/latest/index.php'
+ * @param string $dest eg. './index.php'
+ * @param bool $excludePhp
+ * @throws Exception
+ * @return bool
+ */
+ static public function copy($source, $dest, $excludePhp = false)
+ {
+ static $phpExtensions = array('php', 'tpl');
+
+ if ($excludePhp) {
+ $path_parts = pathinfo($source);
+ if (in_array($path_parts['extension'], $phpExtensions)) {
+ return true;
+ }
+ }
+
+ if (!@copy($source, $dest)) {
+ @chmod($dest, 0755);
+ if (!@copy($source, $dest)) {
+ $message = "Error while creating/copying file to <code>$dest</code>. <br />"
+ . self::getErrorMessageMissingPermissions(Piwik_Common::getPathToPiwikRoot());
+ throw new Exception($message);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns friendly error message explaining how to fix permissions
+ *
+ * @param string $path to the directory missing permissions
+ * @return string Error message
+ */
+ static public function getErrorMessageMissingPermissions($path)
+ {
+ $message = "Please check that the web server has enough permission to write to these files/directories:<br />";
+
+ if (Piwik_Common::isWindows()) {
+ $message .= "On Windows, check that the folder is not read only and is writable.
You can try to execute:<br />";
- }
- else
- {
- $message .= "For example, on a Linux server if your Apache httpd user
+ } else {
+ $message .= "For example, on a Linux server if your Apache httpd user
is www-data, you can try to execute:<br />"
- . "<code>chown -R www-data:www-data ".$path."</code><br />";
- }
-
- $message .= self::getMakeWritableCommand($path);
-
- return $message;
- }
-
- /**
- * Recursively delete a directory
- *
- * @param string $dir Directory name
- * @param boolean $deleteRootToo Delete specified top-level directory as well
- */
- static public function unlinkRecursive($dir, $deleteRootToo)
- {
- if(!$dh = @opendir($dir))
- {
- return;
- }
- while (false !== ($obj = readdir($dh)))
- {
- if($obj == '.' || $obj == '..')
- {
- continue;
- }
-
- if (!@unlink($dir . '/' . $obj))
- {
- self::unlinkRecursive($dir.'/'.$obj, true);
- }
- }
- closedir($dh);
- if ($deleteRootToo)
- {
- @rmdir($dir);
- }
- return;
- }
-
- /**
- * Recursively find pathnames that match a pattern
- * @see glob()
- *
- * @param string $sDir directory
- * @param string $sPattern pattern
- * @param int $nFlags glob() flags
- * @return array
- */
- public static function globr($sDir, $sPattern, $nFlags = NULL)
- {
- if(($aFiles = _glob("$sDir/$sPattern", $nFlags)) == false)
- {
- $aFiles = array();
- }
- if(($aDirs = _glob("$sDir/*", GLOB_ONLYDIR)) != false)
- {
- foreach ($aDirs as $sSubDir)
- {
- $aSubFiles = self::globr($sSubDir, $sPattern, $nFlags);
- $aFiles = array_merge($aFiles, $aSubFiles);
- }
- }
- return $aFiles;
- }
-
- /**
- * Returns the help text displayed to suggest which command to run to give writable access to a file or directory
- *
- * @param string $realpath
- * @return string
- */
- static private function getMakeWritableCommand($realpath)
- {
- if(Piwik_Common::isWindows())
- {
- return "<code>cacls $realpath /t /g ".get_current_user().":f</code><br />";
- }
- return "<code>chmod -R 0755 $realpath</code><br />";
- }
-
- /**
- * Checks that the directories Piwik needs write access are actually writable
- * Displays a nice error page if permissions are missing on some directories
- *
- * @param array $directoriesToCheck Array of directory names to check
- */
- static public function checkDirectoriesWritableOrDie( $directoriesToCheck = null )
- {
- $resultCheck = Piwik::checkDirectoriesWritable( $directoriesToCheck );
- if( array_search(false, $resultCheck) === false )
- {
- return;
- }
-
- $directoryList = '';
- foreach($resultCheck as $dir => $bool)
- {
- $realpath = Piwik_Common::realpath($dir);
- if(!empty($realpath) && $bool === false)
- {
- $directoryList .= self::getMakeWritableCommand($realpath);
- }
- }
-
- // Also give the chown since the chmod is only 755
- if(!Piwik_Common::isWindows())
- {
- $realpath = Piwik_Common::realpath(PIWIK_INCLUDE_PATH . '/');
- $directoryList = "<code>chown -R www-data:www-data ".$realpath."</code><br/>" . $directoryList;
- }
-
- // The error message mentions chmod 777 in case users can't chown
- $directoryMessage = "<p><b>Piwik couldn't write to some directories</b>.</p>
+ . "<code>chown -R www-data:www-data " . $path . "</code><br />";
+ }
+
+ $message .= self::getMakeWritableCommand($path);
+
+ return $message;
+ }
+
+ /**
+ * Recursively delete a directory
+ *
+ * @param string $dir Directory name
+ * @param boolean $deleteRootToo Delete specified top-level directory as well
+ */
+ static public function unlinkRecursive($dir, $deleteRootToo)
+ {
+ if (!$dh = @opendir($dir)) {
+ return;
+ }
+ while (false !== ($obj = readdir($dh))) {
+ if ($obj == '.' || $obj == '..') {
+ continue;
+ }
+
+ if (!@unlink($dir . '/' . $obj)) {
+ self::unlinkRecursive($dir . '/' . $obj, true);
+ }
+ }
+ closedir($dh);
+ if ($deleteRootToo) {
+ @rmdir($dir);
+ }
+ return;
+ }
+
+ /**
+ * Recursively find pathnames that match a pattern
+ * @see glob()
+ *
+ * @param string $sDir directory
+ * @param string $sPattern pattern
+ * @param int $nFlags glob() flags
+ * @return array
+ */
+ public static function globr($sDir, $sPattern, $nFlags = NULL)
+ {
+ if (($aFiles = _glob("$sDir/$sPattern", $nFlags)) == false) {
+ $aFiles = array();
+ }
+ if (($aDirs = _glob("$sDir/*", GLOB_ONLYDIR)) != false) {
+ foreach ($aDirs as $sSubDir) {
+ $aSubFiles = self::globr($sSubDir, $sPattern, $nFlags);
+ $aFiles = array_merge($aFiles, $aSubFiles);
+ }
+ }
+ return $aFiles;
+ }
+
+ /**
+ * Returns the help text displayed to suggest which command to run to give writable access to a file or directory
+ *
+ * @param string $realpath
+ * @return string
+ */
+ static private function getMakeWritableCommand($realpath)
+ {
+ if (Piwik_Common::isWindows()) {
+ return "<code>cacls $realpath /t /g " . get_current_user() . ":f</code><br />";
+ }
+ return "<code>chmod -R 0755 $realpath</code><br />";
+ }
+
+ /**
+ * Checks that the directories Piwik needs write access are actually writable
+ * Displays a nice error page if permissions are missing on some directories
+ *
+ * @param array $directoriesToCheck Array of directory names to check
+ */
+ static public function checkDirectoriesWritableOrDie($directoriesToCheck = null)
+ {
+ $resultCheck = Piwik::checkDirectoriesWritable($directoriesToCheck);
+ if (array_search(false, $resultCheck) === false) {
+ return;
+ }
+
+ $directoryList = '';
+ foreach ($resultCheck as $dir => $bool) {
+ $realpath = Piwik_Common::realpath($dir);
+ if (!empty($realpath) && $bool === false) {
+ $directoryList .= self::getMakeWritableCommand($realpath);
+ }
+ }
+
+ // Also give the chown since the chmod is only 755
+ if (!Piwik_Common::isWindows()) {
+ $realpath = Piwik_Common::realpath(PIWIK_INCLUDE_PATH . '/');
+ $directoryList = "<code>chown -R www-data:www-data " . $realpath . "</code><br/>" . $directoryList;
+ }
+
+ // The error message mentions chmod 777 in case users can't chown
+ $directoryMessage = "<p><b>Piwik couldn't write to some directories</b>.</p>
<p>Try to Execute the following commands on your server, to allow Write access on these directories:</p>"
- . "<blockquote>$directoryList</blockquote>"
- . "<p>If this doesn't work, you can try to create the directories with your FTP software, and set the CHMOD to 0755 (or 0777 if 0755 is not enough). To do so with your FTP software, right click on the directories then click permissions.</p>"
- . "<p>After applying the modifications, you can <a href='index.php'>refresh the page</a>.</p>"
- . "<p>If you need more help, try <a href='?module=Proxy&action=redirect&url=http://piwik.org'>Piwik.org</a>.</p>";
-
- Piwik_ExitWithMessage($directoryMessage, false, true);
- }
-
- /**
- * Checks if directories are writable and create them if they do not exist.
- *
- * @param array $directoriesToCheck array of directories to check - if not given default Piwik directories that needs write permission are checked
- * @return array directory name => true|false (is writable)
- */
- static public function checkDirectoriesWritable($directoriesToCheck = null)
- {
- if( $directoriesToCheck == null )
- {
- $directoriesToCheck = array(
- '/config/',
- '/tmp/',
- '/tmp/templates_c/',
- '/tmp/cache/',
- '/tmp/assets/',
- '/tmp/latest/',
- '/tmp/tcpdf/',
- '/tmp/sessions/',
- );
- }
-
- $resultCheck = array();
- foreach($directoriesToCheck as $directoryToCheck)
- {
- if( !preg_match('/^'.preg_quote(PIWIK_USER_PATH, '/').'/', $directoryToCheck) )
- {
- $directoryToCheck = PIWIK_USER_PATH . $directoryToCheck;
- }
-
- if(!file_exists($directoryToCheck))
- {
- Piwik_Common::mkdir($directoryToCheck);
- }
-
- $directory = Piwik_Common::realpath($directoryToCheck);
- $resultCheck[$directory] = false;
- if($directory !== false // realpath() returns FALSE on failure
- && is_writable($directoryToCheck))
- {
- $resultCheck[$directory] = true;
- }
- }
- return $resultCheck;
- }
-
- /**
- * Check if this installation can be auto-updated.
- * For performance, we look for clues rather than an exhaustive test.
- *
- * @return bool
- */
- static public function canAutoUpdate()
- {
- if(!is_writable(PIWIK_INCLUDE_PATH . '/') ||
- !is_writable(PIWIK_DOCUMENT_ROOT . '/index.php') ||
- !is_writable(PIWIK_INCLUDE_PATH . '/core') ||
- !is_writable(PIWIK_USER_PATH . '/config/global.ini.php'))
- {
- return false;
- }
- return true;
- }
-
- /**
- * Returns the help message when the auto update can't run because of missing permissions
- *
- * @return string
- */
- static public function getAutoUpdateMakeWritableMessage()
- {
- $realpath = Piwik_Common::realpath(PIWIK_INCLUDE_PATH . '/');
- $message = '';
- $message .= "<code>chown -R www-data:www-data ".$realpath."</code><br />";
- $message .= "<code>chmod -R 0755 ".$realpath."</code><br />";
- $message .= 'After you execute these commands (or change permissions via your FTP software), refresh the page and you should be able to use the "Automatic Update" feature.';
- return $message;
- }
-
- /**
- * Generate default robots.txt, favicon.ico, etc to suppress
- * 404 (Not Found) errors in the web server logs, if Piwik
- * is installed in the web root (or top level of subdomain).
- *
- * @see misc/crossdomain.xml
- */
- static public function createWebRootFiles()
- {
- $filesToCreate = array(
- '/robots.txt',
- '/favicon.ico',
- );
- foreach($filesToCreate as $file)
- {
- @file_put_contents(PIWIK_DOCUMENT_ROOT . $file, '');
- }
- }
-
- /**
- * Generate Apache .htaccess files to restrict access
- */
- static public function createHtAccessFiles()
- {
- // deny access to these folders
- $directoriesToProtect = array(
- '/config',
- '/core',
- '/lang',
- '/tmp',
- );
- foreach($directoriesToProtect as $directoryToProtect)
- {
- Piwik_Common::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true);
- }
-
- // Allow/Deny lives in different modules depending on the Apache version
- $allow = "<IfModule mod_access.c>\nAllow from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nAllow from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nAllow from all\n</IfModule>\n";
- $deny = "<IfModule mod_access.c>\nDeny from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nDeny from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nDeny from all\n</IfModule>\n";
-
- // more selective allow/deny filters
- $allowAny = "<Files \"*\">\n".$allow."Satisfy any\n</Files>\n";
- $allowStaticAssets = "<Files ~ \"\\.(test\.php|gif|ico|jpg|png|svg|js|css|swf)$\">\n".$allow."Satisfy any\n</Files>\n";
- $denyDirectPhp = "<Files ~ \"\\.(php|php4|php5|inc|tpl|in)$\">\n".$deny."</Files>\n";
-
- $directoriesToProtect = array(
- '/js' => $allowAny,
- '/libs' => $denyDirectPhp . $allowStaticAssets,
- '/plugins' => $denyDirectPhp . $allowStaticAssets,
- '/themes' => $denyDirectPhp . $allowStaticAssets,
- );
- foreach($directoriesToProtect as $directoryToProtect => $content)
- {
- Piwik_Common::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true, $content);
- }
- }
-
- /**
- * Generate IIS web.config files to restrict access
- *
- * Note: for IIS 7 and above
- */
- static public function createWebConfigFiles()
- {
- @file_put_contents(PIWIK_INCLUDE_PATH . '/web.config',
-'<?xml version="1.0" encoding="UTF-8"?>
+ . "<blockquote>$directoryList</blockquote>"
+ . "<p>If this doesn't work, you can try to create the directories with your FTP software, and set the CHMOD to 0755 (or 0777 if 0755 is not enough). To do so with your FTP software, right click on the directories then click permissions.</p>"
+ . "<p>After applying the modifications, you can <a href='index.php'>refresh the page</a>.</p>"
+ . "<p>If you need more help, try <a href='?module=Proxy&action=redirect&url=http://piwik.org'>Piwik.org</a>.</p>";
+
+ Piwik_ExitWithMessage($directoryMessage, false, true);
+ }
+
+ /**
+ * Checks if directories are writable and create them if they do not exist.
+ *
+ * @param array $directoriesToCheck array of directories to check - if not given default Piwik directories that needs write permission are checked
+ * @return array directory name => true|false (is writable)
+ */
+ static public function checkDirectoriesWritable($directoriesToCheck = null)
+ {
+ if ($directoriesToCheck == null) {
+ $directoriesToCheck = array(
+ '/config/',
+ '/tmp/',
+ '/tmp/templates_c/',
+ '/tmp/cache/',
+ '/tmp/assets/',
+ '/tmp/latest/',
+ '/tmp/tcpdf/',
+ '/tmp/sessions/',
+ );
+ }
+
+ $resultCheck = array();
+ foreach ($directoriesToCheck as $directoryToCheck) {
+ if (!preg_match('/^' . preg_quote(PIWIK_USER_PATH, '/') . '/', $directoryToCheck)) {
+ $directoryToCheck = PIWIK_USER_PATH . $directoryToCheck;
+ }
+
+ if (!file_exists($directoryToCheck)) {
+ Piwik_Common::mkdir($directoryToCheck);
+ }
+
+ $directory = Piwik_Common::realpath($directoryToCheck);
+ $resultCheck[$directory] = false;
+ if ($directory !== false // realpath() returns FALSE on failure
+ && is_writable($directoryToCheck)
+ ) {
+ $resultCheck[$directory] = true;
+ }
+ }
+ return $resultCheck;
+ }
+
+ /**
+ * Check if this installation can be auto-updated.
+ * For performance, we look for clues rather than an exhaustive test.
+ *
+ * @return bool
+ */
+ static public function canAutoUpdate()
+ {
+ if (!is_writable(PIWIK_INCLUDE_PATH . '/') ||
+ !is_writable(PIWIK_DOCUMENT_ROOT . '/index.php') ||
+ !is_writable(PIWIK_INCLUDE_PATH . '/core') ||
+ !is_writable(PIWIK_USER_PATH . '/config/global.ini.php')
+ ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns the help message when the auto update can't run because of missing permissions
+ *
+ * @return string
+ */
+ static public function getAutoUpdateMakeWritableMessage()
+ {
+ $realpath = Piwik_Common::realpath(PIWIK_INCLUDE_PATH . '/');
+ $message = '';
+ $message .= "<code>chown -R www-data:www-data " . $realpath . "</code><br />";
+ $message .= "<code>chmod -R 0755 " . $realpath . "</code><br />";
+ $message .= 'After you execute these commands (or change permissions via your FTP software), refresh the page and you should be able to use the "Automatic Update" feature.';
+ return $message;
+ }
+
+ /**
+ * Generate default robots.txt, favicon.ico, etc to suppress
+ * 404 (Not Found) errors in the web server logs, if Piwik
+ * is installed in the web root (or top level of subdomain).
+ *
+ * @see misc/crossdomain.xml
+ */
+ static public function createWebRootFiles()
+ {
+ $filesToCreate = array(
+ '/robots.txt',
+ '/favicon.ico',
+ );
+ foreach ($filesToCreate as $file) {
+ @file_put_contents(PIWIK_DOCUMENT_ROOT . $file, '');
+ }
+ }
+
+ /**
+ * Generate Apache .htaccess files to restrict access
+ */
+ static public function createHtAccessFiles()
+ {
+ // deny access to these folders
+ $directoriesToProtect = array(
+ '/config',
+ '/core',
+ '/lang',
+ '/tmp',
+ );
+ foreach ($directoriesToProtect as $directoryToProtect) {
+ Piwik_Common::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true);
+ }
+
+ // Allow/Deny lives in different modules depending on the Apache version
+ $allow = "<IfModule mod_access.c>\nAllow from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nAllow from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nAllow from all\n</IfModule>\n";
+ $deny = "<IfModule mod_access.c>\nDeny from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nDeny from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nDeny from all\n</IfModule>\n";
+
+ // more selective allow/deny filters
+ $allowAny = "<Files \"*\">\n" . $allow . "Satisfy any\n</Files>\n";
+ $allowStaticAssets = "<Files ~ \"\\.(test\.php|gif|ico|jpg|png|svg|js|css|swf)$\">\n" . $allow . "Satisfy any\n</Files>\n";
+ $denyDirectPhp = "<Files ~ \"\\.(php|php4|php5|inc|tpl|in)$\">\n" . $deny . "</Files>\n";
+
+ $directoriesToProtect = array(
+ '/js' => $allowAny,
+ '/libs' => $denyDirectPhp . $allowStaticAssets,
+ '/plugins' => $denyDirectPhp . $allowStaticAssets,
+ '/themes' => $denyDirectPhp . $allowStaticAssets,
+ );
+ foreach ($directoriesToProtect as $directoryToProtect => $content) {
+ Piwik_Common::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true, $content);
+ }
+ }
+
+ /**
+ * Generate IIS web.config files to restrict access
+ *
+ * Note: for IIS 7 and above
+ */
+ static public function createWebConfigFiles()
+ {
+ @file_put_contents(PIWIK_INCLUDE_PATH . '/web.config',
+ '<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<security>
@@ -636,15 +592,14 @@ class Piwik
</system.webServer>
</configuration>');
- // deny direct access to .php files
- $directoriesToProtect = array(
- '/libs',
- '/plugins',
- );
- foreach($directoriesToProtect as $directoryToProtect)
- {
- @file_put_contents(PIWIK_INCLUDE_PATH . $directoryToProtect . '/web.config',
-'<?xml version="1.0" encoding="UTF-8"?>
+ // deny direct access to .php files
+ $directoriesToProtect = array(
+ '/libs',
+ '/plugins',
+ );
+ foreach ($directoriesToProtect as $directoryToProtect) {
+ @file_put_contents(PIWIK_INCLUDE_PATH . $directoryToProtect . '/web.config',
+ '<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<security>
@@ -656,2124 +611,1987 @@ class Piwik
</security>
</system.webServer>
</configuration>');
- }
- }
-
- /**
- * Get file integrity information (in PIWIK_INCLUDE_PATH).
- *
- * @return array(bool, string, ...) Return code (true/false), followed by zero or more error messages
- */
- static public function getFileIntegrityInformation()
- {
- $messages = array();
- $messages[] = true;
-
- // ignore dev environments
- if(file_exists(PIWIK_INCLUDE_PATH . '/.git'))
- {
- $messages[] = Piwik_Translate('General_WarningFileIntegritySkipped');
- return $messages;
- }
-
- $manifest = PIWIK_INCLUDE_PATH . '/config/manifest.inc.php';
- if(!file_exists($manifest))
- {
- $messages[] = Piwik_Translate('General_WarningFileIntegrityNoManifest');
- return $messages;
- }
-
- require_once $manifest;
-
- $files = Manifest::$files;
-
- $hasMd5file = function_exists('md5_file');
- $hasMd5 = function_exists('md5');
- foreach($files as $path => $props)
- {
- $file = PIWIK_INCLUDE_PATH . '/' . $path;
-
- if(!file_exists($file))
- {
- $messages[] = Piwik_Translate('General_ExceptionMissingFile', $file);
- }
- else if(filesize($file) != $props[0])
- {
- if(!$hasMd5 || in_array(substr($path, -4), array('.gif', '.ico', '.jpg', '.png', '.swf')))
- {
- // files that contain binary data (e.g., images) must match the file size
- $messages[] = Piwik_Translate('General_ExceptionFilesizeMismatch', array($file, $props[0], filesize($file)));
- }
- else
- {
- // convert end-of-line characters and re-test text files
- $content = @file_get_contents($file);
- $content = str_replace("\r\n", "\n", $content);
- if((strlen($content) != $props[0])
- || (@md5($content) !== $props[1]))
- {
- $messages[] = Piwik_Translate('General_ExceptionFilesizeMismatch', array($file, $props[0], filesize($file)));
- }
- }
- }
- else if($hasMd5file && (@md5_file($file) !== $props[1]))
- {
- $messages[] = Piwik_Translate('General_ExceptionFileIntegrity', $file);
- }
- }
-
- if(count($messages) > 1)
- {
- $messages[0] = false;
- }
-
- if(!$hasMd5file)
- {
- $messages[] = Piwik_Translate('General_WarningFileIntegrityNoMd5file');
- }
-
- return $messages;
- }
-
- /**
- * Test if php output is compressed
- *
- * @return bool True if php output is (or suspected/likely) to be compressed
- */
- static public function isPhpOutputCompressed()
- {
- // Off = ''; On = '1'; otherwise, it's a buffer size
- $zlibOutputCompression = ini_get('zlib.output_compression');
-
- // could be ob_gzhandler, ob_deflatehandler, etc
- $outputHandler = ini_get('output_handler');
-
- // output handlers can be stacked
- $obHandlers = array_filter( ob_list_handlers(), create_function('$var', 'return $var !== "default output handler";') );
-
- // user defined handler via wrapper
- $autoPrependFile = ini_get('auto_prepend_file');
- $autoAppendFile = ini_get('auto_append_file');
-
- return !empty($zlibOutputCompression) ||
- !empty($outputHandler) ||
- !empty($obHandlers) ||
- !empty($autoPrependFile) ||
- !empty($autoAppendFile);
- }
-
- /**
- * Serve static files through php proxy.
- *
- * It performs the following actions:
- * - Checks the file is readable or returns "HTTP/1.0 404 Not Found"
- * - Returns "HTTP/1.1 304 Not Modified" after comparing the HTTP_IF_MODIFIED_SINCE
- * with the modification date of the static file
- * - Will try to compress the static file according to HTTP_ACCEPT_ENCODING. Compressed files are store in
- * the /tmp directory. If compressing extensions are not available, a manually gzip compressed file
- * can be provided in the /tmp directory. It has to bear the same name with an added .gz extension.
- * Using manually compressed static files requires you to manually update the compressed file when
- * the static file is updated.
- * - Overrides server cache control config to allow caching
- * - Sends Very Accept-Encoding to tell proxies to store different version of the static file according
- * to users encoding capacities.
- *
- * Warning:
- * Compressed filed are stored in the /tmp directory.
- * If this method is used with two files bearing the same name but located in different locations,
- * there is a risk of conflict. One file could be served with the content of the other.
- * A future upgrade of this method would be to recreate the directory structure of the static file
- * within a /tmp/compressed-static-files directory.
- *
- * @param string $file The location of the static file to serve
- * @param string $contentType The content type of the static file.
- * @param bool $expireFarFuture If set to true, will set Expires: header in far future.
- * Should be set to false for files that don't have a cache buster (eg. piwik.js)
- */
- static public function serveStaticFile($file, $contentType, $expireFarFuture = true)
- {
- if (file_exists($file))
- {
- // conditional GET
- $modifiedSince = '';
- if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']))
- {
- $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
-
- // strip any trailing data appended to header
- if (false !== ($semicolon = strpos($modifiedSince, ';')))
- {
- $modifiedSince = substr($modifiedSince, 0, $semicolon);
- }
- }
-
- $fileModifiedTime = @filemtime($file);
- $lastModified = gmdate('D, d M Y H:i:s', $fileModifiedTime) . ' GMT';
-
- // set HTTP response headers
- self::overrideCacheControlHeaders('public');
- @header('Vary: Accept-Encoding');
- @header('Content-Disposition: inline; filename='.basename($file));
-
- if($expireFarFuture)
- {
- // Required by proxy caches potentially in between the browser and server to cache the request indeed
- @header("Expires: ".gmdate('D, d M Y H:i:s', time() + 86400 * 100) . ' GMT');
- }
-
- // Returns 304 if not modified since
- if ($modifiedSince === $lastModified)
- {
- self::setHttpStatus('304 Not Modified');
- }
- else
- {
- // optional compression
- $compressed = false;
- $encoding = '';
- $compressedFileLocation = PIWIK_USER_PATH . self::COMPRESSED_FILE_LOCATION . basename($file);
-
- $phpOutputCompressionEnabled = self::isPhpOutputCompressed();
- if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && !$phpOutputCompressionEnabled)
- {
- $acceptEncoding = $_SERVER['HTTP_ACCEPT_ENCODING'];
-
- if (extension_loaded('zlib') && function_exists('file_get_contents') && function_exists('file_put_contents'))
- {
- if (preg_match('/(?:^|, ?)(deflate)(?:,|$)/', $acceptEncoding, $matches))
- {
- $encoding = 'deflate';
- $filegz = $compressedFileLocation .'.deflate';
- }
- else if (preg_match('/(?:^|, ?)((x-)?gzip)(?:,|$)/', $acceptEncoding, $matches))
- {
- $encoding = $matches[1];
- $filegz = $compressedFileLocation .'.gz';
- }
-
- if (!empty($encoding))
- {
- // compress-on-demand and use cache
- if(!file_exists($filegz) || ($fileModifiedTime > @filemtime($filegz)))
- {
- $data = file_get_contents($file);
-
- if ($encoding == 'deflate')
- {
- $data = gzdeflate($data, 9);
- }
- else if ($encoding == 'gzip' || $encoding == 'x-gzip')
- {
- $data = gzencode($data, 9);
- }
-
- file_put_contents($filegz, $data);
- }
-
- $compressed = true;
- $file = $filegz;
- }
- }
- else
- {
- // manually compressed
- $filegz = $compressedFileLocation .'.gz';
- if (preg_match('/(?:^|, ?)((x-)?gzip)(?:,|$)/', $acceptEncoding, $matches) && file_exists($filegz) && ($fileModifiedTime < @filemtime($filegz)))
- {
- $encoding = $matches[1];
- $compressed = true;
- $file = $filegz;
- }
- }
- }
-
- @header('Last-Modified: ' . $lastModified);
-
- if(!$phpOutputCompressionEnabled)
- {
- @header('Content-Length: ' . filesize($file));
- }
-
- if(!empty($contentType))
- {
- @header('Content-Type: '.$contentType);
- }
-
- if($compressed)
- {
- @header('Content-Encoding: ' . $encoding);
- }
-
- if(!_readfile($file))
- {
- self::setHttpStatus('505 Internal server error');
- }
- }
- }
- else
- {
- self::setHttpStatus('404 Not Found');
- }
- }
-
- /**
- * Create CSV (or other delimited) files
- *
- * @param string $filePath filename to create
- * @param array $fileSpec File specifications (delimiter, line terminator, etc)
- * @param array $rows Array of array corresponding to rows of values
- * @throws Exception if unable to create or write to file
- */
- static public function createCSVFile($filePath, $fileSpec, $rows)
- {
- // Set up CSV delimiters, quotes, etc
- $delim = $fileSpec['delim'];
- $quote = $fileSpec['quote'];
- $eol = $fileSpec['eol'];
- $null = $fileSpec['null'];
- $escapespecial_cb = $fileSpec['escapespecial_cb'];
-
- $fp = @fopen($filePath, 'wb');
- if (!$fp)
- {
- throw new Exception('Error creating the tmp file '.$filePath.', please check that the webserver has write permission to write this file.');
- }
-
- foreach ($rows as $row)
- {
- $output = '';
- foreach($row as $value)
- {
- if(!isset($value) || is_null($value) || $value === false)
- {
- $output .= $null.$delim;
- }
- else
- {
- $output .= $quote.$escapespecial_cb($value).$quote.$delim;
- }
- }
-
- // Replace delim with eol
- $output = substr_replace($output, $eol, -1);
-
- $ret = fwrite($fp, $output);
- if (!$ret) {
- fclose($fp);
- throw new Exception('Error writing to the tmp file '.$filePath);
- }
- }
- fclose($fp);
-
- @chmod($filePath, 0777);
- }
-
-/*
+ }
+ }
+
+ /**
+ * Get file integrity information (in PIWIK_INCLUDE_PATH).
+ *
+ * @return array(bool, string, ...) Return code (true/false), followed by zero or more error messages
+ */
+ static public function getFileIntegrityInformation()
+ {
+ $messages = array();
+ $messages[] = true;
+
+ // ignore dev environments
+ if (file_exists(PIWIK_INCLUDE_PATH . '/.git')) {
+ $messages[] = Piwik_Translate('General_WarningFileIntegritySkipped');
+ return $messages;
+ }
+
+ $manifest = PIWIK_INCLUDE_PATH . '/config/manifest.inc.php';
+ if (!file_exists($manifest)) {
+ $messages[] = Piwik_Translate('General_WarningFileIntegrityNoManifest');
+ return $messages;
+ }
+
+ require_once $manifest;
+
+ $files = Manifest::$files;
+
+ $hasMd5file = function_exists('md5_file');
+ $hasMd5 = function_exists('md5');
+ foreach ($files as $path => $props) {
+ $file = PIWIK_INCLUDE_PATH . '/' . $path;
+
+ if (!file_exists($file)) {
+ $messages[] = Piwik_Translate('General_ExceptionMissingFile', $file);
+ } else if (filesize($file) != $props[0]) {
+ if (!$hasMd5 || in_array(substr($path, -4), array('.gif', '.ico', '.jpg', '.png', '.swf'))) {
+ // files that contain binary data (e.g., images) must match the file size
+ $messages[] = Piwik_Translate('General_ExceptionFilesizeMismatch', array($file, $props[0], filesize($file)));
+ } else {
+ // convert end-of-line characters and re-test text files
+ $content = @file_get_contents($file);
+ $content = str_replace("\r\n", "\n", $content);
+ if ((strlen($content) != $props[0])
+ || (@md5($content) !== $props[1])
+ ) {
+ $messages[] = Piwik_Translate('General_ExceptionFilesizeMismatch', array($file, $props[0], filesize($file)));
+ }
+ }
+ } else if ($hasMd5file && (@md5_file($file) !== $props[1])) {
+ $messages[] = Piwik_Translate('General_ExceptionFileIntegrity', $file);
+ }
+ }
+
+ if (count($messages) > 1) {
+ $messages[0] = false;
+ }
+
+ if (!$hasMd5file) {
+ $messages[] = Piwik_Translate('General_WarningFileIntegrityNoMd5file');
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Test if php output is compressed
+ *
+ * @return bool True if php output is (or suspected/likely) to be compressed
+ */
+ static public function isPhpOutputCompressed()
+ {
+ // Off = ''; On = '1'; otherwise, it's a buffer size
+ $zlibOutputCompression = ini_get('zlib.output_compression');
+
+ // could be ob_gzhandler, ob_deflatehandler, etc
+ $outputHandler = ini_get('output_handler');
+
+ // output handlers can be stacked
+ $obHandlers = array_filter(ob_list_handlers(), create_function('$var', 'return $var !== "default output handler";'));
+
+ // user defined handler via wrapper
+ $autoPrependFile = ini_get('auto_prepend_file');
+ $autoAppendFile = ini_get('auto_append_file');
+
+ return !empty($zlibOutputCompression) ||
+ !empty($outputHandler) ||
+ !empty($obHandlers) ||
+ !empty($autoPrependFile) ||
+ !empty($autoAppendFile);
+ }
+
+ /**
+ * Serve static files through php proxy.
+ *
+ * It performs the following actions:
+ * - Checks the file is readable or returns "HTTP/1.0 404 Not Found"
+ * - Returns "HTTP/1.1 304 Not Modified" after comparing the HTTP_IF_MODIFIED_SINCE
+ * with the modification date of the static file
+ * - Will try to compress the static file according to HTTP_ACCEPT_ENCODING. Compressed files are store in
+ * the /tmp directory. If compressing extensions are not available, a manually gzip compressed file
+ * can be provided in the /tmp directory. It has to bear the same name with an added .gz extension.
+ * Using manually compressed static files requires you to manually update the compressed file when
+ * the static file is updated.
+ * - Overrides server cache control config to allow caching
+ * - Sends Very Accept-Encoding to tell proxies to store different version of the static file according
+ * to users encoding capacities.
+ *
+ * Warning:
+ * Compressed filed are stored in the /tmp directory.
+ * If this method is used with two files bearing the same name but located in different locations,
+ * there is a risk of conflict. One file could be served with the content of the other.
+ * A future upgrade of this method would be to recreate the directory structure of the static file
+ * within a /tmp/compressed-static-files directory.
+ *
+ * @param string $file The location of the static file to serve
+ * @param string $contentType The content type of the static file.
+ * @param bool $expireFarFuture If set to true, will set Expires: header in far future.
+ * Should be set to false for files that don't have a cache buster (eg. piwik.js)
+ */
+ static public function serveStaticFile($file, $contentType, $expireFarFuture = true)
+ {
+ if (file_exists($file)) {
+ // conditional GET
+ $modifiedSince = '';
+ if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
+ $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
+
+ // strip any trailing data appended to header
+ if (false !== ($semicolon = strpos($modifiedSince, ';'))) {
+ $modifiedSince = substr($modifiedSince, 0, $semicolon);
+ }
+ }
+
+ $fileModifiedTime = @filemtime($file);
+ $lastModified = gmdate('D, d M Y H:i:s', $fileModifiedTime) . ' GMT';
+
+ // set HTTP response headers
+ self::overrideCacheControlHeaders('public');
+ @header('Vary: Accept-Encoding');
+ @header('Content-Disposition: inline; filename=' . basename($file));
+
+ if ($expireFarFuture) {
+ // Required by proxy caches potentially in between the browser and server to cache the request indeed
+ @header("Expires: " . gmdate('D, d M Y H:i:s', time() + 86400 * 100) . ' GMT');
+ }
+
+ // Returns 304 if not modified since
+ if ($modifiedSince === $lastModified) {
+ self::setHttpStatus('304 Not Modified');
+ } else {
+ // optional compression
+ $compressed = false;
+ $encoding = '';
+ $compressedFileLocation = PIWIK_USER_PATH . self::COMPRESSED_FILE_LOCATION . basename($file);
+
+ $phpOutputCompressionEnabled = self::isPhpOutputCompressed();
+ if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && !$phpOutputCompressionEnabled) {
+ $acceptEncoding = $_SERVER['HTTP_ACCEPT_ENCODING'];
+
+ if (extension_loaded('zlib') && function_exists('file_get_contents') && function_exists('file_put_contents')) {
+ if (preg_match('/(?:^|, ?)(deflate)(?:,|$)/', $acceptEncoding, $matches)) {
+ $encoding = 'deflate';
+ $filegz = $compressedFileLocation . '.deflate';
+ } else if (preg_match('/(?:^|, ?)((x-)?gzip)(?:,|$)/', $acceptEncoding, $matches)) {
+ $encoding = $matches[1];
+ $filegz = $compressedFileLocation . '.gz';
+ }
+
+ if (!empty($encoding)) {
+ // compress-on-demand and use cache
+ if (!file_exists($filegz) || ($fileModifiedTime > @filemtime($filegz))) {
+ $data = file_get_contents($file);
+
+ if ($encoding == 'deflate') {
+ $data = gzdeflate($data, 9);
+ } else if ($encoding == 'gzip' || $encoding == 'x-gzip') {
+ $data = gzencode($data, 9);
+ }
+
+ file_put_contents($filegz, $data);
+ }
+
+ $compressed = true;
+ $file = $filegz;
+ }
+ } else {
+ // manually compressed
+ $filegz = $compressedFileLocation . '.gz';
+ if (preg_match('/(?:^|, ?)((x-)?gzip)(?:,|$)/', $acceptEncoding, $matches) && file_exists($filegz) && ($fileModifiedTime < @filemtime($filegz))) {
+ $encoding = $matches[1];
+ $compressed = true;
+ $file = $filegz;
+ }
+ }
+ }
+
+ @header('Last-Modified: ' . $lastModified);
+
+ if (!$phpOutputCompressionEnabled) {
+ @header('Content-Length: ' . filesize($file));
+ }
+
+ if (!empty($contentType)) {
+ @header('Content-Type: ' . $contentType);
+ }
+
+ if ($compressed) {
+ @header('Content-Encoding: ' . $encoding);
+ }
+
+ if (!_readfile($file)) {
+ self::setHttpStatus('505 Internal server error');
+ }
+ }
+ } else {
+ self::setHttpStatus('404 Not Found');
+ }
+ }
+
+ /**
+ * Create CSV (or other delimited) files
+ *
+ * @param string $filePath filename to create
+ * @param array $fileSpec File specifications (delimiter, line terminator, etc)
+ * @param array $rows Array of array corresponding to rows of values
+ * @throws Exception if unable to create or write to file
+ */
+ static public function createCSVFile($filePath, $fileSpec, $rows)
+ {
+ // Set up CSV delimiters, quotes, etc
+ $delim = $fileSpec['delim'];
+ $quote = $fileSpec['quote'];
+ $eol = $fileSpec['eol'];
+ $null = $fileSpec['null'];
+ $escapespecial_cb = $fileSpec['escapespecial_cb'];
+
+ $fp = @fopen($filePath, 'wb');
+ if (!$fp) {
+ throw new Exception('Error creating the tmp file ' . $filePath . ', please check that the webserver has write permission to write this file.');
+ }
+
+ foreach ($rows as $row) {
+ $output = '';
+ foreach ($row as $value) {
+ if (!isset($value) || is_null($value) || $value === false) {
+ $output .= $null . $delim;
+ } else {
+ $output .= $quote . $escapespecial_cb($value) . $quote . $delim;
+ }
+ }
+
+ // Replace delim with eol
+ $output = substr_replace($output, $eol, -1);
+
+ $ret = fwrite($fp, $output);
+ if (!$ret) {
+ fclose($fp);
+ throw new Exception('Error writing to the tmp file ' . $filePath);
+ }
+ }
+ fclose($fp);
+
+ @chmod($filePath, 0777);
+ }
+
+ /*
* PHP environment settings
*/
- /**
- * Set maximum script execution time.
- *
- * @param int $executionTime max execution time in seconds (0 = no limit)
- */
- static public function setMaxExecutionTime($executionTime)
- {
- // in the event one or the other is disabled...
- @ini_set('max_execution_time', $executionTime);
- @set_time_limit($executionTime);
- }
-
- /**
- * Get php memory_limit (in Megabytes)
- *
- * Prior to PHP 5.2.1, or on Windows, --enable-memory-limit is not a
- * compile-time default, so ini_get('memory_limit') may return false.
- *
- * @see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
- * @return int|false memory limit in megabytes, or false if there is no limit
- */
- static public function getMemoryLimitValue()
- {
- if(($memory = ini_get('memory_limit')) > 0)
- {
- // handle shorthand byte options (case-insensitive)
- $shorthandByteOption = substr($memory, -1);
- switch($shorthandByteOption)
- {
- case 'G':
- case 'g':
- return substr($memory, 0, -1) * 1024;
- case 'M':
- case 'm':
- return substr($memory, 0, -1);
- case 'K':
- case 'k':
- return substr($memory, 0, -1) / 1024;
- }
- return $memory / 1048576;
- }
-
- // no memory limit
- return false;
- }
-
- /**
- * Set PHP memory limit
- *
- * Note: system settings may prevent scripts from overriding the master value
- *
- * @param int $minimumMemoryLimit
- * @return bool true if set; false otherwise
- */
- static public function setMemoryLimit($minimumMemoryLimit)
- {
- // in Megabytes
- $currentValue = self::getMemoryLimitValue();
- if( $currentValue === false
- || ($currentValue < $minimumMemoryLimit && @ini_set('memory_limit', $minimumMemoryLimit.'M')))
- {
- return true;
- }
- return false;
- }
-
- /**
- * Raise PHP memory limit if below the minimum required
- *
- * @return bool true if set; false otherwise
- */
- static public function raiseMemoryLimitIfNecessary()
- {
- $memoryLimit = self::getMemoryLimitValue();
- if($memoryLimit === false)
- {
- return false;
- }
- $minimumMemoryLimit = Piwik_Config::getInstance()->General['minimum_memory_limit'];
-
- if(Piwik_Common::isArchivePhpTriggered()
- && Piwik::isUserIsSuperUser())
- {
- // archive.php: no time limit, high memory limit
- self::setMaxExecutionTime(0);
- $minimumMemoryLimitWhenArchiving = Piwik_Config::getInstance()->General['minimum_memory_limit_when_archiving'];
- if($memoryLimit < $minimumMemoryLimitWhenArchiving)
- {
- return self::setMemoryLimit($minimumMemoryLimitWhenArchiving);
- }
- return false;
- }
- if($memoryLimit < $minimumMemoryLimit)
- {
- return self::setMemoryLimit($minimumMemoryLimit);
- }
- return false;
- }
-
-/*
+ /**
+ * Set maximum script execution time.
+ *
+ * @param int $executionTime max execution time in seconds (0 = no limit)
+ */
+ static public function setMaxExecutionTime($executionTime)
+ {
+ // in the event one or the other is disabled...
+ @ini_set('max_execution_time', $executionTime);
+ @set_time_limit($executionTime);
+ }
+
+ /**
+ * Get php memory_limit (in Megabytes)
+ *
+ * Prior to PHP 5.2.1, or on Windows, --enable-memory-limit is not a
+ * compile-time default, so ini_get('memory_limit') may return false.
+ *
+ * @see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
+ * @return int|false memory limit in megabytes, or false if there is no limit
+ */
+ static public function getMemoryLimitValue()
+ {
+ if (($memory = ini_get('memory_limit')) > 0) {
+ // handle shorthand byte options (case-insensitive)
+ $shorthandByteOption = substr($memory, -1);
+ switch ($shorthandByteOption) {
+ case 'G':
+ case 'g':
+ return substr($memory, 0, -1) * 1024;
+ case 'M':
+ case 'm':
+ return substr($memory, 0, -1);
+ case 'K':
+ case 'k':
+ return substr($memory, 0, -1) / 1024;
+ }
+ return $memory / 1048576;
+ }
+
+ // no memory limit
+ return false;
+ }
+
+ /**
+ * Set PHP memory limit
+ *
+ * Note: system settings may prevent scripts from overriding the master value
+ *
+ * @param int $minimumMemoryLimit
+ * @return bool true if set; false otherwise
+ */
+ static public function setMemoryLimit($minimumMemoryLimit)
+ {
+ // in Megabytes
+ $currentValue = self::getMemoryLimitValue();
+ if ($currentValue === false
+ || ($currentValue < $minimumMemoryLimit && @ini_set('memory_limit', $minimumMemoryLimit . 'M'))
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Raise PHP memory limit if below the minimum required
+ *
+ * @return bool true if set; false otherwise
+ */
+ static public function raiseMemoryLimitIfNecessary()
+ {
+ $memoryLimit = self::getMemoryLimitValue();
+ if ($memoryLimit === false) {
+ return false;
+ }
+ $minimumMemoryLimit = Piwik_Config::getInstance()->General['minimum_memory_limit'];
+
+ if (Piwik_Common::isArchivePhpTriggered()
+ && Piwik::isUserIsSuperUser()
+ ) {
+ // archive.php: no time limit, high memory limit
+ self::setMaxExecutionTime(0);
+ $minimumMemoryLimitWhenArchiving = Piwik_Config::getInstance()->General['minimum_memory_limit_when_archiving'];
+ if ($memoryLimit < $minimumMemoryLimitWhenArchiving) {
+ return self::setMemoryLimit($minimumMemoryLimitWhenArchiving);
+ }
+ return false;
+ }
+ if ($memoryLimit < $minimumMemoryLimit) {
+ return self::setMemoryLimit($minimumMemoryLimit);
+ }
+ return false;
+ }
+
+ /*
* Logging and error handling
*/
- public static $shouldLog = null;
-
- /**
- * Log a message
- *
- * @param string $message
- */
- static public function log($message = '')
- {
- if(is_null(self::$shouldLog))
- {
- self::$shouldLog = self::shouldLoggerLog();
- // It is possible that the logger is not setup:
- // - Tracker request, and debug disabled,
- // - and some scheduled tasks call code that tries and log something
- try {
- Zend_Registry::get('logger_message');
- } catch(Exception $e) {
- self::$shouldLog = false;
- }
- }
- if(self::$shouldLog)
- {
- Zend_Registry::get('logger_message')->logEvent($message);
- }
- }
-
- /**
- * Returns if logging should work
- * @return bool
- */
- static public function shouldLoggerLog()
- {
- try {
- $shouldLog = (Piwik_Common::isPhpCliMode()
- || Piwik_Config::getInstance()->log['log_only_when_cli'] == 0)
- &&
- ( Piwik_Config::getInstance()->log['log_only_when_debug_parameter'] == 0
- || isset($_REQUEST['debug']))
- ;
- } catch(Exception $e) {
- $shouldLog = false;
- }
- return $shouldLog;
- }
-
- /**
- * Trigger E_USER_ERROR with optional message
- *
- * @param string $message
- */
- static public function error($message = '')
- {
- trigger_error($message, E_USER_ERROR);
- }
-
- /**
- * Display the message in a nice red font with a nice icon
- * ... and dies
- *
- * @param string $message
- */
- static public function exitWithErrorMessage( $message )
- {
- $output = "<style>a{color:red;}</style>\n".
- "<div style='color:red;font-family:Georgia;font-size:120%'>".
- "<p><img src='themes/default/images/error_medium.png' style='vertical-align:middle; float:left;padding:20 20 20 20' />".
- $message.
- "</p></div>";
- print(Piwik_Log_Formatter_ScreenFormatter::getFormattedString($output));
- exit;
- }
-
-/*
+ public static $shouldLog = null;
+
+ /**
+ * Log a message
+ *
+ * @param string $message
+ */
+ static public function log($message = '')
+ {
+ if (is_null(self::$shouldLog)) {
+ self::$shouldLog = self::shouldLoggerLog();
+ // It is possible that the logger is not setup:
+ // - Tracker request, and debug disabled,
+ // - and some scheduled tasks call code that tries and log something
+ try {
+ Zend_Registry::get('logger_message');
+ } catch (Exception $e) {
+ self::$shouldLog = false;
+ }
+ }
+ if (self::$shouldLog) {
+ Zend_Registry::get('logger_message')->logEvent($message);
+ }
+ }
+
+ /**
+ * Returns if logging should work
+ * @return bool
+ */
+ static public function shouldLoggerLog()
+ {
+ try {
+ $shouldLog = (Piwik_Common::isPhpCliMode()
+ || Piwik_Config::getInstance()->log['log_only_when_cli'] == 0)
+ &&
+ (Piwik_Config::getInstance()->log['log_only_when_debug_parameter'] == 0
+ || isset($_REQUEST['debug']));
+ } catch (Exception $e) {
+ $shouldLog = false;
+ }
+ return $shouldLog;
+ }
+
+ /**
+ * Trigger E_USER_ERROR with optional message
+ *
+ * @param string $message
+ */
+ static public function error($message = '')
+ {
+ trigger_error($message, E_USER_ERROR);
+ }
+
+ /**
+ * Display the message in a nice red font with a nice icon
+ * ... and dies
+ *
+ * @param string $message
+ */
+ static public function exitWithErrorMessage($message)
+ {
+ $output = "<style>a{color:red;}</style>\n" .
+ "<div style='color:red;font-family:Georgia;font-size:120%'>" .
+ "<p><img src='themes/default/images/error_medium.png' style='vertical-align:middle; float:left;padding:20 20 20 20' />" .
+ $message .
+ "</p></div>";
+ print(Piwik_Log_Formatter_ScreenFormatter::getFormattedString($output));
+ exit;
+ }
+
+ /*
* Profiling
*/
- /**
- * Get total number of queries
- *
- * @return int number of queries
- */
- static public function getQueryCount()
- {
- $profiler = Zend_Registry::get('db')->getProfiler();
- return $profiler->getTotalNumQueries();
- }
-
- /**
- * Get total elapsed time (in seconds)
- *
- * @return int elapsed time
- */
- static public function getDbElapsedSecs()
- {
- $profiler = Zend_Registry::get('db')->getProfiler();
- return $profiler->getTotalElapsedSecs();
- }
-
- /**
- * Print number of queries and elapsed time
- */
- static public function printQueryCount()
- {
- $totalTime = self::getDbElapsedSecs();
- $queryCount = self::getQueryCount();
- Piwik::log(sprintf("Total queries = %d (total sql time = %.2fs)", $queryCount, $totalTime));
- }
-
- /**
- * Print profiling report for the tracker
- *
- * @param Piwik_Tracker_Db $db Tracker database object (or null)
- */
- static public function printSqlProfilingReportTracker( $db = null )
- {
- if(!function_exists('maxSumMsFirst'))
- {
- function maxSumMsFirst($a,$b)
- {
- return $a['sum_time_ms'] < $b['sum_time_ms'];
- }
- }
-
- if(is_null($db))
- {
- $db = Piwik_Tracker::getDatabase();
- }
- $tableName = Piwik_Common::prefixTable('log_profiling');
-
- $all = $db->fetchAll('SELECT * FROM '.$tableName );
- if($all === false)
- {
- return;
- }
- uasort($all, 'maxSumMsFirst');
-
- $infoIndexedByQuery = array();
- foreach($all as $infoQuery)
- {
- $query = $infoQuery['query'];
- $count = $infoQuery['count'];
- $sum_time_ms = $infoQuery['sum_time_ms'];
- $infoIndexedByQuery[$query] = array('count' => $count, 'sumTimeMs' => $sum_time_ms);
- }
- Piwik::getSqlProfilingQueryBreakdownOutput($infoIndexedByQuery);
- }
-
- /**
- * Outputs SQL Profiling reports
- * It is automatically called when enabling the SQL profiling in the config file enable_sql_profiler
- * @throws Exception
- */
- static function printSqlProfilingReportZend()
- {
- $profiler = Zend_Registry::get('db')->getProfiler();
-
- if(!$profiler->getEnabled())
- {
- throw new Exception("To display the profiler you should enable enable_sql_profiler on your config/config.ini.php file");
- }
-
- $infoIndexedByQuery = array();
- foreach($profiler->getQueryProfiles() as $query)
- {
- if(isset($infoIndexedByQuery[$query->getQuery()]))
- {
- $existing = $infoIndexedByQuery[$query->getQuery()];
- }
- else
- {
- $existing = array( 'count' => 0, 'sumTimeMs' => 0);
- }
- $new = array( 'count' => $existing['count'] + 1,
- 'sumTimeMs' => $existing['count'] + $query->getElapsedSecs() * 1000);
- $infoIndexedByQuery[$query->getQuery()] = $new;
- }
-
- if(!function_exists('sortTimeDesc'))
- {
- function sortTimeDesc($a,$b)
- {
- return $a['sumTimeMs'] < $b['sumTimeMs'];
- }
- }
- uasort( $infoIndexedByQuery, 'sortTimeDesc');
-
- $str = '<hr /><b>SQL Profiler</b><hr /><b>Summary</b><br/>';
- $totalTime = $profiler->getTotalElapsedSecs();
- $queryCount = $profiler->getTotalNumQueries();
- $longestTime = 0;
- $longestQuery = null;
- foreach ($profiler->getQueryProfiles() as $query) {
- if ($query->getElapsedSecs() > $longestTime) {
- $longestTime = $query->getElapsedSecs();
- $longestQuery = $query->getQuery();
- }
- }
- $str .= 'Executed ' . $queryCount . ' queries in ' . round($totalTime,3) . ' seconds';
- $str .= '(Average query length: ' . round($totalTime / $queryCount,3) . ' seconds)';
- $str .= '<br />Queries per second: ' . round($queryCount / $totalTime,1) ;
- $str .= '<br />Longest query length: ' . round($longestTime,3) . " seconds (<code>$longestQuery</code>)";
- Piwik::log($str);
- Piwik::getSqlProfilingQueryBreakdownOutput($infoIndexedByQuery);
- }
-
- /**
- * Log a breakdown by query
- *
- * @param array $infoIndexedByQuery
- */
- static private function getSqlProfilingQueryBreakdownOutput( $infoIndexedByQuery )
- {
- $output = '<hr /><b>Breakdown by query</b><br/>';
- foreach($infoIndexedByQuery as $query => $queryInfo)
- {
- $timeMs = round($queryInfo['sumTimeMs'],1);
- $count = $queryInfo['count'];
- $avgTimeString = '';
- if($count > 1)
- {
- $avgTimeMs = $timeMs / $count;
- $avgTimeString = " (average = <b>". round($avgTimeMs,1) . "ms</b>)";
- }
- $query = preg_replace('/([\t\n\r ]+)/', ' ', $query);
- $output .= "Executed <b>$count</b> time". ($count==1?'':'s') ." in <b>".$timeMs."ms</b> $avgTimeString <pre>\t$query</pre>";
- }
- Piwik::log($output);
- }
-
- /**
- * Print timer
- */
- static public function printTimer()
- {
- Piwik::log(Zend_Registry::get('timer'));
- }
-
- /**
- * Print memory leak
- *
- * @param string $prefix
- * @param string $suffix
- */
- static public function printMemoryLeak($prefix = '', $suffix = '<br />')
- {
- echo $prefix;
- echo Zend_Registry::get('timer')->getMemoryLeak();
- echo $suffix;
- }
-
- /**
- * Print memory usage
- *
- * @return string
- */
- static public function getMemoryUsage()
- {
- $memory = false;
- if(function_exists('xdebug_memory_usage'))
- {
- $memory = xdebug_memory_usage();
- }
- elseif(function_exists('memory_get_usage'))
- {
- $memory = memory_get_usage();
- }
- if($memory === false)
- {
- return "Memory usage function not found.";
- }
- $usage = number_format( round($memory / 1024 / 1024, 2), 2);
- return "$usage Mb";
- }
-
-/*
+ /**
+ * Get total number of queries
+ *
+ * @return int number of queries
+ */
+ static public function getQueryCount()
+ {
+ $profiler = Zend_Registry::get('db')->getProfiler();
+ return $profiler->getTotalNumQueries();
+ }
+
+ /**
+ * Get total elapsed time (in seconds)
+ *
+ * @return int elapsed time
+ */
+ static public function getDbElapsedSecs()
+ {
+ $profiler = Zend_Registry::get('db')->getProfiler();
+ return $profiler->getTotalElapsedSecs();
+ }
+
+ /**
+ * Print number of queries and elapsed time
+ */
+ static public function printQueryCount()
+ {
+ $totalTime = self::getDbElapsedSecs();
+ $queryCount = self::getQueryCount();
+ Piwik::log(sprintf("Total queries = %d (total sql time = %.2fs)", $queryCount, $totalTime));
+ }
+
+ /**
+ * Print profiling report for the tracker
+ *
+ * @param Piwik_Tracker_Db $db Tracker database object (or null)
+ */
+ static public function printSqlProfilingReportTracker($db = null)
+ {
+ if (!function_exists('maxSumMsFirst')) {
+ function maxSumMsFirst($a, $b)
+ {
+ return $a['sum_time_ms'] < $b['sum_time_ms'];
+ }
+ }
+
+ if (is_null($db)) {
+ $db = Piwik_Tracker::getDatabase();
+ }
+ $tableName = Piwik_Common::prefixTable('log_profiling');
+
+ $all = $db->fetchAll('SELECT * FROM ' . $tableName);
+ if ($all === false) {
+ return;
+ }
+ uasort($all, 'maxSumMsFirst');
+
+ $infoIndexedByQuery = array();
+ foreach ($all as $infoQuery) {
+ $query = $infoQuery['query'];
+ $count = $infoQuery['count'];
+ $sum_time_ms = $infoQuery['sum_time_ms'];
+ $infoIndexedByQuery[$query] = array('count' => $count, 'sumTimeMs' => $sum_time_ms);
+ }
+ Piwik::getSqlProfilingQueryBreakdownOutput($infoIndexedByQuery);
+ }
+
+ /**
+ * Outputs SQL Profiling reports
+ * It is automatically called when enabling the SQL profiling in the config file enable_sql_profiler
+ * @throws Exception
+ */
+ static function printSqlProfilingReportZend()
+ {
+ $profiler = Zend_Registry::get('db')->getProfiler();
+
+ if (!$profiler->getEnabled()) {
+ throw new Exception("To display the profiler you should enable enable_sql_profiler on your config/config.ini.php file");
+ }
+
+ $infoIndexedByQuery = array();
+ foreach ($profiler->getQueryProfiles() as $query) {
+ if (isset($infoIndexedByQuery[$query->getQuery()])) {
+ $existing = $infoIndexedByQuery[$query->getQuery()];
+ } else {
+ $existing = array('count' => 0, 'sumTimeMs' => 0);
+ }
+ $new = array('count' => $existing['count'] + 1,
+ 'sumTimeMs' => $existing['count'] + $query->getElapsedSecs() * 1000);
+ $infoIndexedByQuery[$query->getQuery()] = $new;
+ }
+
+ if (!function_exists('sortTimeDesc')) {
+ function sortTimeDesc($a, $b)
+ {
+ return $a['sumTimeMs'] < $b['sumTimeMs'];
+ }
+ }
+ uasort($infoIndexedByQuery, 'sortTimeDesc');
+
+ $str = '<hr /><b>SQL Profiler</b><hr /><b>Summary</b><br/>';
+ $totalTime = $profiler->getTotalElapsedSecs();
+ $queryCount = $profiler->getTotalNumQueries();
+ $longestTime = 0;
+ $longestQuery = null;
+ foreach ($profiler->getQueryProfiles() as $query) {
+ if ($query->getElapsedSecs() > $longestTime) {
+ $longestTime = $query->getElapsedSecs();
+ $longestQuery = $query->getQuery();
+ }
+ }
+ $str .= 'Executed ' . $queryCount . ' queries in ' . round($totalTime, 3) . ' seconds';
+ $str .= '(Average query length: ' . round($totalTime / $queryCount, 3) . ' seconds)';
+ $str .= '<br />Queries per second: ' . round($queryCount / $totalTime, 1);
+ $str .= '<br />Longest query length: ' . round($longestTime, 3) . " seconds (<code>$longestQuery</code>)";
+ Piwik::log($str);
+ Piwik::getSqlProfilingQueryBreakdownOutput($infoIndexedByQuery);
+ }
+
+ /**
+ * Log a breakdown by query
+ *
+ * @param array $infoIndexedByQuery
+ */
+ static private function getSqlProfilingQueryBreakdownOutput($infoIndexedByQuery)
+ {
+ $output = '<hr /><b>Breakdown by query</b><br/>';
+ foreach ($infoIndexedByQuery as $query => $queryInfo) {
+ $timeMs = round($queryInfo['sumTimeMs'], 1);
+ $count = $queryInfo['count'];
+ $avgTimeString = '';
+ if ($count > 1) {
+ $avgTimeMs = $timeMs / $count;
+ $avgTimeString = " (average = <b>" . round($avgTimeMs, 1) . "ms</b>)";
+ }
+ $query = preg_replace('/([\t\n\r ]+)/', ' ', $query);
+ $output .= "Executed <b>$count</b> time" . ($count == 1 ? '' : 's') . " in <b>" . $timeMs . "ms</b> $avgTimeString <pre>\t$query</pre>";
+ }
+ Piwik::log($output);
+ }
+
+ /**
+ * Print timer
+ */
+ static public function printTimer()
+ {
+ Piwik::log(Zend_Registry::get('timer'));
+ }
+
+ /**
+ * Print memory leak
+ *
+ * @param string $prefix
+ * @param string $suffix
+ */
+ static public function printMemoryLeak($prefix = '', $suffix = '<br />')
+ {
+ echo $prefix;
+ echo Zend_Registry::get('timer')->getMemoryLeak();
+ echo $suffix;
+ }
+
+ /**
+ * Print memory usage
+ *
+ * @return string
+ */
+ static public function getMemoryUsage()
+ {
+ $memory = false;
+ if (function_exists('xdebug_memory_usage')) {
+ $memory = xdebug_memory_usage();
+ } elseif (function_exists('memory_get_usage')) {
+ $memory = memory_get_usage();
+ }
+ if ($memory === false) {
+ return "Memory usage function not found.";
+ }
+ $usage = number_format(round($memory / 1024 / 1024, 2), 2);
+ return "$usage Mb";
+ }
+
+ /*
* Amounts, Percentages, Currency, Time, Math Operations, and Pretty Printing
*/
- /**
- * Returns a list of currency symbols
- *
- * @return array array( currencyCode => symbol, ... )
- */
- static public function getCurrencyList()
- {
- static $currenciesList = null;
- if(is_null($currenciesList))
- {
- require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Currencies.php';
- $currenciesList = $GLOBALS['Piwik_CurrencyList'];
- }
- return $currenciesList;
- }
-
- /**
- * Computes the division of i1 by i2. If either i1 or i2 are not number, or if i2 has a value of zero
- * we return 0 to avoid the division by zero.
- *
- * @param number $i1
- * @param number $i2
- * @return number The result of the division or zero
- */
- static public function secureDiv( $i1, $i2 )
- {
- if ( is_numeric($i1) && is_numeric($i2) && floatval($i2) != 0)
- {
- return $i1 / $i2;
- }
- return 0;
- }
-
- /**
- * Safely compute a percentage. Return 0 to avoid division by zero.
- *
- * @param number $dividend
- * @param number $divisor
- * @param int $precision
- * @return number
- */
- static public function getPercentageSafe($dividend, $divisor, $precision = 0)
- {
- if($divisor == 0)
- {
- return 0;
- }
- return round(100 * $dividend / $divisor, $precision);
- }
-
- /**
- * Get currency symbol for a site
- *
- * @param int $idSite
- * @return string
- */
- static public function getCurrency($idSite)
- {
- $symbols = self::getCurrencyList();
- $site = new Piwik_Site($idSite);
- $currency = $site->getCurrency();
- if(isset($symbols[$currency]))
- {
- return $symbols[$currency][0];
- }
-
- return '';
- }
-
- /**
- * For the given value, based on the column name, will apply: pretty time, pretty money
- * @param int $idSite
- * @param string $columnName
- * @param mixed $value
- * @param bool $htmlAllowed
- * @param string $timeAsSentence
- * @return string
- */
- static public function getPrettyValue($idSite, $columnName, $value, $htmlAllowed, $timeAsSentence)
- {
- // Display time in human readable
- if(strpos($columnName, 'time') !== false)
- {
- return Piwik::getPrettyTimeFromSeconds($value, $timeAsSentence);
- }
- // Add revenue symbol to revenues
- if(strpos($columnName, 'revenue') !== false && strpos($columnName, 'evolution') === false)
- {
- return Piwik::getPrettyMoney($value, $idSite, $htmlAllowed);
- }
- // Add % symbol to rates
- if(strpos($columnName, '_rate') !== false)
- {
- if(strpos($value, "%") === false)
- {
- return $value . "%";
- }
- }
- return $value;
- }
-
- /**
- * Pretty format monetary value for a site
- *
- * @param int|string $value
- * @param int $idSite
- * @param bool $htmlAllowed
- * @return string
- */
- static public function getPrettyMoney($value, $idSite, $htmlAllowed = true)
- {
- $currencyBefore = self::getCurrency($idSite);
-
- $space = ' ';
- if($htmlAllowed)
- {
- $space = '&nbsp;';
- }
-
- $currencyAfter = '';
- // manually put the currency symbol after the amount for euro
- // (maybe more currencies prefer this notation?)
- if(in_array($currencyBefore,array('€', 'kr')))
- {
- $currencyAfter = $space.$currencyBefore;
- $currencyBefore = '';
- }
-
- // if the input is a number (it could be a string or INPUT form),
- // and if this number is not an int, we round to precision 2
- if(is_numeric($value))
- {
- if($value == round($value))
- {
- // 0.0 => 0
- $value = round($value);
- }
- else
- {
- $precision = Piwik_Tracker_GoalManager::REVENUE_PRECISION;
- $value = sprintf( "%01.".$precision."f", $value);
- }
- }
- $prettyMoney = $currencyBefore . $space . $value . $currencyAfter;
- return $prettyMoney;
- }
-
- /**
- * Pretty format a memory size value
- *
- * @param number $size size in bytes
- * @param string $unit The specific unit to use, if any. If null, the unit is determined by $size.
- * @param int $precision The precision to use when rounding.
- * @return string
- */
- static public function getPrettySizeFromBytes( $size, $unit = null, $precision = 1 )
- {
- if ($size == 0)
- {
- return '0 M';
- }
-
- $units = array('B','K','M','G','T');
- foreach($units as $currentUnit)
- {
- if ($size >= 1024 && $unit != $currentUnit)
- {
- $size = $size / 1024;
- }
- else
- {
- break;
- }
- }
- return round($size, $precision)." ".$currentUnit;
- }
-
- /**
- * Pretty format a time
- *
- * @param int $numberOfSeconds
- * @param bool $displayTimeAsSentence If set to true, will output "5min 17s", if false "00:05:17"
- * @param bool $isHtml
- * @param bool $round to the full seconds
- * @return string
- */
- static public function getPrettyTimeFromSeconds($numberOfSeconds, $displayTimeAsSentence = true, $isHtml = true, $round = false)
- {
- $numberOfSeconds = $round ? (int)$numberOfSeconds : (float)$numberOfSeconds;
-
- // Display 01:45:17 time format
- if($displayTimeAsSentence === false)
- {
- $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);
- $milliSeconds = ($numberOfSeconds * 1000) % 1000;
- if ($milliSeconds) {
- $time .= '.' . $milliSeconds;
- }
- return $time;
- }
- $secondsInYear = 86400 * 365.25;
- $years = floor($numberOfSeconds / $secondsInYear);
- $minusYears = $numberOfSeconds - $years * $secondsInYear;
- $days = floor($minusYears / 86400);
-
- $minusDays = $numberOfSeconds - $days * 86400;
- $hours = floor($minusDays / 3600);
-
- $minusDaysAndHours = $minusDays - $hours * 3600;
- $minutes = floor($minusDaysAndHours / 60 );
-
- $seconds = $minusDaysAndHours - $minutes * 60;
-
- if($years > 0)
- {
- $return = sprintf(Piwik_Translate('General_YearsDays'), $years, $days);
- }
- elseif($days > 0)
- {
- $return = sprintf(Piwik_Translate('General_DaysHours'), $days, $hours);
- }
- elseif($hours > 0)
- {
- $return = sprintf(Piwik_Translate('General_HoursMinutes'), $hours, $minutes);
- }
- elseif($minutes > 0)
- {
- $return = sprintf(Piwik_Translate('General_MinutesSeconds'), $minutes, $seconds);
- }
- else
- {
- $return = sprintf(Piwik_Translate('General_Seconds'), $seconds);
- }
- if($isHtml)
- {
- return str_replace(' ', '&nbsp;', $return);
- }
- return $return;
- }
-
- /**
- * Gets a prettified string representation of a number. The result will have
- * thousands separators and a decimal point specific to the current locale.
- *
- * @param number $value
- * @return string
- */
- static public function getPrettyNumber( $value )
- {
- $locale = localeconv();
-
- $decimalPoint = $locale['decimal_point'];
- $thousandsSeparator = $locale['thousands_sep'];
-
- return number_format($value, 0, $decimalPoint, $thousandsSeparator);
- }
-
- /**
- * Returns the Javascript code to be inserted on every page to track
- *
- * @param int $idSite
- * @param string $piwikUrl http://path/to/piwik/directory/
- * @return string
- */
- static public function getJavascriptCode($idSite, $piwikUrl)
- {
- $jsCode = file_get_contents( PIWIK_INCLUDE_PATH . "/core/Tracker/javascriptCode.tpl");
- $jsCode = htmlentities($jsCode);
- preg_match('~^(http|https)://(.*)$~D', $piwikUrl, $matches);
- $piwikUrl = @$matches[2];
- $jsCode = str_replace('{$idSite}', $idSite, $jsCode);
- $jsCode = str_replace('{$piwikUrl}', Piwik_Common::sanitizeInputValue($piwikUrl), $jsCode);
- return $jsCode;
- }
-
- /**
- * Generate a title for image tags
- *
- * @return string
- */
- static public function getRandomTitle()
- {
- static $titles = array(
- 'Web analytics',
- 'Real Time Web Analytics',
- 'Analytics',
- 'Real Time Analytics',
- 'Analytics in Real time',
- 'Open Source Analytics',
- 'Open Source Web Analytics',
- 'Free Website Analytics',
- 'Free Web Analytics',
- 'Analytics Platform',
- );
- $id = abs(intval(md5(Piwik_Url::getCurrentHost())));
- $title = $titles[ $id % count($titles)];
- return $title;
- }
-
- /**
- * Number of websites to show in the Website selector
- *
- * @return int
- */
- static public function getWebsitesCountToDisplay()
- {
- $count = max(Piwik_Config::getInstance()->General['site_selector_max_sites'],
- Piwik_Config::getInstance()->General['autocomplete_min_sites']);
- return (int)$count;
- }
-
- /**
- * Segments to pre-process
- *
- * @return string
- */
- static public function getKnownSegmentsToArchive()
- {
- static $cachedResult = null;
-
- if (is_null($cachedResult))
- {
- $segments = Piwik_Config::getInstance()->Segments;
- $cachedResult = isset($segments['Segments']) ? $segments['Segments'] : '';
- }
-
- return $cachedResult;
- }
-
-/*
+ /**
+ * Returns a list of currency symbols
+ *
+ * @return array array( currencyCode => symbol, ... )
+ */
+ static public function getCurrencyList()
+ {
+ static $currenciesList = null;
+ if (is_null($currenciesList)) {
+ require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Currencies.php';
+ $currenciesList = $GLOBALS['Piwik_CurrencyList'];
+ }
+ return $currenciesList;
+ }
+
+ /**
+ * Computes the division of i1 by i2. If either i1 or i2 are not number, or if i2 has a value of zero
+ * we return 0 to avoid the division by zero.
+ *
+ * @param number $i1
+ * @param number $i2
+ * @return number The result of the division or zero
+ */
+ static public function secureDiv($i1, $i2)
+ {
+ if (is_numeric($i1) && is_numeric($i2) && floatval($i2) != 0) {
+ return $i1 / $i2;
+ }
+ return 0;
+ }
+
+ /**
+ * Safely compute a percentage. Return 0 to avoid division by zero.
+ *
+ * @param number $dividend
+ * @param number $divisor
+ * @param int $precision
+ * @return number
+ */
+ static public function getPercentageSafe($dividend, $divisor, $precision = 0)
+ {
+ if ($divisor == 0) {
+ return 0;
+ }
+ return round(100 * $dividend / $divisor, $precision);
+ }
+
+ /**
+ * Get currency symbol for a site
+ *
+ * @param int $idSite
+ * @return string
+ */
+ static public function getCurrency($idSite)
+ {
+ $symbols = self::getCurrencyList();
+ $site = new Piwik_Site($idSite);
+ $currency = $site->getCurrency();
+ if (isset($symbols[$currency])) {
+ return $symbols[$currency][0];
+ }
+
+ return '';
+ }
+
+ /**
+ * For the given value, based on the column name, will apply: pretty time, pretty money
+ * @param int $idSite
+ * @param string $columnName
+ * @param mixed $value
+ * @param bool $htmlAllowed
+ * @param string $timeAsSentence
+ * @return string
+ */
+ static public function getPrettyValue($idSite, $columnName, $value, $htmlAllowed, $timeAsSentence)
+ {
+ // Display time in human readable
+ if (strpos($columnName, 'time') !== false) {
+ return Piwik::getPrettyTimeFromSeconds($value, $timeAsSentence);
+ }
+ // Add revenue symbol to revenues
+ if (strpos($columnName, 'revenue') !== false && strpos($columnName, 'evolution') === false) {
+ return Piwik::getPrettyMoney($value, $idSite, $htmlAllowed);
+ }
+ // Add % symbol to rates
+ if (strpos($columnName, '_rate') !== false) {
+ if (strpos($value, "%") === false) {
+ return $value . "%";
+ }
+ }
+ return $value;
+ }
+
+ /**
+ * Pretty format monetary value for a site
+ *
+ * @param int|string $value
+ * @param int $idSite
+ * @param bool $htmlAllowed
+ * @return string
+ */
+ static public function getPrettyMoney($value, $idSite, $htmlAllowed = true)
+ {
+ $currencyBefore = self::getCurrency($idSite);
+
+ $space = ' ';
+ if ($htmlAllowed) {
+ $space = '&nbsp;';
+ }
+
+ $currencyAfter = '';
+ // manually put the currency symbol after the amount for euro
+ // (maybe more currencies prefer this notation?)
+ if (in_array($currencyBefore, array('€', 'kr'))) {
+ $currencyAfter = $space . $currencyBefore;
+ $currencyBefore = '';
+ }
+
+ // if the input is a number (it could be a string or INPUT form),
+ // and if this number is not an int, we round to precision 2
+ if (is_numeric($value)) {
+ if ($value == round($value)) {
+ // 0.0 => 0
+ $value = round($value);
+ } else {
+ $precision = Piwik_Tracker_GoalManager::REVENUE_PRECISION;
+ $value = sprintf("%01." . $precision . "f", $value);
+ }
+ }
+ $prettyMoney = $currencyBefore . $space . $value . $currencyAfter;
+ return $prettyMoney;
+ }
+
+ /**
+ * Pretty format a memory size value
+ *
+ * @param number $size size in bytes
+ * @param string $unit The specific unit to use, if any. If null, the unit is determined by $size.
+ * @param int $precision The precision to use when rounding.
+ * @return string
+ */
+ static public function getPrettySizeFromBytes($size, $unit = null, $precision = 1)
+ {
+ if ($size == 0) {
+ return '0 M';
+ }
+
+ $units = array('B', 'K', 'M', 'G', 'T');
+ foreach ($units as $currentUnit) {
+ if ($size >= 1024 && $unit != $currentUnit) {
+ $size = $size / 1024;
+ } else {
+ break;
+ }
+ }
+ return round($size, $precision) . " " . $currentUnit;
+ }
+
+ /**
+ * Pretty format a time
+ *
+ * @param int $numberOfSeconds
+ * @param bool $displayTimeAsSentence If set to true, will output "5min 17s", if false "00:05:17"
+ * @param bool $isHtml
+ * @param bool $round to the full seconds
+ * @return string
+ */
+ static public function getPrettyTimeFromSeconds($numberOfSeconds, $displayTimeAsSentence = true, $isHtml = true, $round = false)
+ {
+ $numberOfSeconds = $round ? (int)$numberOfSeconds : (float)$numberOfSeconds;
+
+ // Display 01:45:17 time format
+ if ($displayTimeAsSentence === false) {
+ $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);
+ $milliSeconds = ($numberOfSeconds * 1000) % 1000;
+ if ($milliSeconds) {
+ $time .= '.' . $milliSeconds;
+ }
+ return $time;
+ }
+ $secondsInYear = 86400 * 365.25;
+ $years = floor($numberOfSeconds / $secondsInYear);
+ $minusYears = $numberOfSeconds - $years * $secondsInYear;
+ $days = floor($minusYears / 86400);
+
+ $minusDays = $numberOfSeconds - $days * 86400;
+ $hours = floor($minusDays / 3600);
+
+ $minusDaysAndHours = $minusDays - $hours * 3600;
+ $minutes = floor($minusDaysAndHours / 60);
+
+ $seconds = $minusDaysAndHours - $minutes * 60;
+
+ if ($years > 0) {
+ $return = sprintf(Piwik_Translate('General_YearsDays'), $years, $days);
+ } elseif ($days > 0) {
+ $return = sprintf(Piwik_Translate('General_DaysHours'), $days, $hours);
+ } elseif ($hours > 0) {
+ $return = sprintf(Piwik_Translate('General_HoursMinutes'), $hours, $minutes);
+ } elseif ($minutes > 0) {
+ $return = sprintf(Piwik_Translate('General_MinutesSeconds'), $minutes, $seconds);
+ } else {
+ $return = sprintf(Piwik_Translate('General_Seconds'), $seconds);
+ }
+ if ($isHtml) {
+ return str_replace(' ', '&nbsp;', $return);
+ }
+ return $return;
+ }
+
+ /**
+ * Gets a prettified string representation of a number. The result will have
+ * thousands separators and a decimal point specific to the current locale.
+ *
+ * @param number $value
+ * @return string
+ */
+ static public function getPrettyNumber($value)
+ {
+ $locale = localeconv();
+
+ $decimalPoint = $locale['decimal_point'];
+ $thousandsSeparator = $locale['thousands_sep'];
+
+ return number_format($value, 0, $decimalPoint, $thousandsSeparator);
+ }
+
+ /**
+ * Returns the Javascript code to be inserted on every page to track
+ *
+ * @param int $idSite
+ * @param string $piwikUrl http://path/to/piwik/directory/
+ * @return string
+ */
+ static public function getJavascriptCode($idSite, $piwikUrl)
+ {
+ $jsCode = file_get_contents(PIWIK_INCLUDE_PATH . "/core/Tracker/javascriptCode.tpl");
+ $jsCode = htmlentities($jsCode);
+ preg_match('~^(http|https)://(.*)$~D', $piwikUrl, $matches);
+ $piwikUrl = @$matches[2];
+ $jsCode = str_replace('{$idSite}', $idSite, $jsCode);
+ $jsCode = str_replace('{$piwikUrl}', Piwik_Common::sanitizeInputValue($piwikUrl), $jsCode);
+ return $jsCode;
+ }
+
+ /**
+ * Generate a title for image tags
+ *
+ * @return string
+ */
+ static public function getRandomTitle()
+ {
+ static $titles = array(
+ 'Web analytics',
+ 'Real Time Web Analytics',
+ 'Analytics',
+ 'Real Time Analytics',
+ 'Analytics in Real time',
+ 'Open Source Analytics',
+ 'Open Source Web Analytics',
+ 'Free Website Analytics',
+ 'Free Web Analytics',
+ 'Analytics Platform',
+ );
+ $id = abs(intval(md5(Piwik_Url::getCurrentHost())));
+ $title = $titles[$id % count($titles)];
+ return $title;
+ }
+
+ /**
+ * Number of websites to show in the Website selector
+ *
+ * @return int
+ */
+ static public function getWebsitesCountToDisplay()
+ {
+ $count = max(Piwik_Config::getInstance()->General['site_selector_max_sites'],
+ Piwik_Config::getInstance()->General['autocomplete_min_sites']);
+ return (int)$count;
+ }
+
+ /**
+ * Segments to pre-process
+ *
+ * @return string
+ */
+ static public function getKnownSegmentsToArchive()
+ {
+ static $cachedResult = null;
+
+ if (is_null($cachedResult)) {
+ $segments = Piwik_Config::getInstance()->Segments;
+ $cachedResult = isset($segments['Segments']) ? $segments['Segments'] : '';
+ }
+
+ return $cachedResult;
+ }
+
+ /*
* Access
*/
- /**
- * Get current user email address
- *
- * @return string
- */
- static public function getCurrentUserEmail()
- {
- if(!Piwik::isUserIsSuperUser())
- {
- $user = Piwik_UsersManager_API::getInstance()->getUser(Piwik::getCurrentUserLogin());
- return $user['email'];
- }
- return self::getSuperUserEmail();
- }
-
- /**
- * Returns Super User login
- *
- * @return string
- */
- static public function getSuperUserLogin()
- {
- $superuser = Piwik_Config::getInstance()->superuser;
- return $superuser['login'];
- }
-
- /**
- * Returns Super User email
- *
- * @return string
- */
- static public function getSuperUserEmail()
- {
- $superuser = Piwik_Config::getInstance()->superuser;
- return $superuser['email'];
- }
-
- /**
- * Get current user login
- *
- * @return string login ID
- */
- static public function getCurrentUserLogin()
- {
- return Zend_Registry::get('access')->getLogin();
- }
-
- /**
- * Get current user's token auth
- *
- * @return string Token auth
- */
- static public function getCurrentUserTokenAuth()
- {
- return Zend_Registry::get('access')->getTokenAuth();
- }
-
- /**
- * Returns true if the current user is either the super user, or the user $theUser
- * Used when modifying user preference: this usually requires super user or being the user itself.
- *
- * @param string $theUser
- * @return bool
- */
- static public function isUserIsSuperUserOrTheUser( $theUser )
- {
- try{
- self::checkUserIsSuperUserOrTheUser( $theUser );
- return true;
- } catch( Exception $e){
- return false;
- }
- }
-
- /**
- * Check that current user is either the specified user or the superuser
- *
- * @param string $theUser
- * @throws Piwik_Access_NoAccessException if the user is neither the super user nor the user $theUser
- */
- static public function checkUserIsSuperUserOrTheUser( $theUser )
- {
- try{
- if( Piwik::getCurrentUserLogin() !== $theUser)
- {
- // or to the super user
- Piwik::checkUserIsSuperUser();
- }
- } catch( Piwik_Access_NoAccessException $e){
- throw new Piwik_Access_NoAccessException(Piwik_Translate('General_ExceptionCheckUserIsSuperUserOrTheUser', array($theUser)));
- }
- }
-
- /**
- * Returns true if the current user is the Super User
- *
- * @return bool
- */
- static public function isUserIsSuperUser()
- {
- try{
- self::checkUserIsSuperUser();
- return true;
- } catch( Exception $e){
- return false;
- }
- }
-
- /**
- * Is user the anonymous user?
- *
- * @return bool True if anonymouse; false otherwise
- */
- static public function isUserIsAnonymous()
- {
- return Piwik::getCurrentUserLogin() == 'anonymous';
- }
-
- /**
- * Checks if user is not the anonymous user.
- *
- * @throws Piwik_Access_NoAccessException if user is anonymous.
- */
- static public function checkUserIsNotAnonymous()
- {
- if(self::isUserIsAnonymous())
- {
- throw new Piwik_Access_NoAccessException(Piwik_Translate('General_YouMustBeLoggedIn'));
- }
- }
-
- /**
- * Helper method user to set the current as Super User.
- * This should be used with great care as this gives the user all permissions.
- *
- * @param bool $bool true to set current user as super user
- */
- static public function setUserIsSuperUser( $bool = true )
- {
- Zend_Registry::get('access')->setSuperUser($bool);
- }
-
- /**
- * Check that user is the superuser
- *
- * @throws Exception if not the superuser
- */
- static public function checkUserIsSuperUser()
- {
- Zend_Registry::get('access')->checkUserIsSuperUser();
- }
-
- /**
- * Returns true if the user has admin access to the sites
- *
- * @param mixed $idSites
- * @return bool
- */
- static public function isUserHasAdminAccess( $idSites )
- {
- try{
- self::checkUserHasAdminAccess( $idSites );
- return true;
- } catch( Exception $e){
- return false;
- }
- }
-
- /**
- * Check user has admin access to the sites
- *
- * @param mixed $idSites
- * @throws Exception if user doesn't have admin access to the sites
- */
- static public function checkUserHasAdminAccess( $idSites )
- {
- Zend_Registry::get('access')->checkUserHasAdminAccess( $idSites );
- }
-
- /**
- * Returns true if the user has admin access to any sites
- *
- * @return bool
- */
- static public function isUserHasSomeAdminAccess()
- {
- try{
- self::checkUserHasSomeAdminAccess();
- return true;
- } catch( Exception $e){
- return false;
- }
- }
-
- /**
- * Check user has admin access to any sites
- *
- * @throws Exception if user doesn't have admin access to any sites
- */
- static public function checkUserHasSomeAdminAccess()
- {
- Zend_Registry::get('access')->checkUserHasSomeAdminAccess();
- }
-
- /**
- * Returns true if the user has view access to the sites
- *
- * @param mixed $idSites
- * @return bool
- */
- static public function isUserHasViewAccess( $idSites )
- {
- try{
- self::checkUserHasViewAccess( $idSites );
- return true;
- } catch( Exception $e){
- return false;
- }
- }
-
- /**
- * Check user has view access to the sites
- *
- * @param mixed $idSites
- * @throws Exception if user doesn't have view access to sites
- */
- static public function checkUserHasViewAccess( $idSites )
- {
- Zend_Registry::get('access')->checkUserHasViewAccess( $idSites );
- }
-
- /**
- * Returns true if the user has view access to any sites
- *
- * @return bool
- */
- static public function isUserHasSomeViewAccess()
- {
- try{
- self::checkUserHasSomeViewAccess();
- return true;
- } catch( Exception $e){
- return false;
- }
- }
-
- /**
- * Check user has view access to any sites
- *
- * @throws Exception if user doesn't have view access to any sites
- */
- static public function checkUserHasSomeViewAccess()
- {
- Zend_Registry::get('access')->checkUserHasSomeViewAccess();
- }
-
-/*
+ /**
+ * Get current user email address
+ *
+ * @return string
+ */
+ static public function getCurrentUserEmail()
+ {
+ if (!Piwik::isUserIsSuperUser()) {
+ $user = Piwik_UsersManager_API::getInstance()->getUser(Piwik::getCurrentUserLogin());
+ return $user['email'];
+ }
+ return self::getSuperUserEmail();
+ }
+
+ /**
+ * Returns Super User login
+ *
+ * @return string
+ */
+ static public function getSuperUserLogin()
+ {
+ $superuser = Piwik_Config::getInstance()->superuser;
+ return $superuser['login'];
+ }
+
+ /**
+ * Returns Super User email
+ *
+ * @return string
+ */
+ static public function getSuperUserEmail()
+ {
+ $superuser = Piwik_Config::getInstance()->superuser;
+ return $superuser['email'];
+ }
+
+ /**
+ * Get current user login
+ *
+ * @return string login ID
+ */
+ static public function getCurrentUserLogin()
+ {
+ return Zend_Registry::get('access')->getLogin();
+ }
+
+ /**
+ * Get current user's token auth
+ *
+ * @return string Token auth
+ */
+ static public function getCurrentUserTokenAuth()
+ {
+ return Zend_Registry::get('access')->getTokenAuth();
+ }
+
+ /**
+ * Returns true if the current user is either the super user, or the user $theUser
+ * Used when modifying user preference: this usually requires super user or being the user itself.
+ *
+ * @param string $theUser
+ * @return bool
+ */
+ static public function isUserIsSuperUserOrTheUser($theUser)
+ {
+ try {
+ self::checkUserIsSuperUserOrTheUser($theUser);
+ return true;
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Check that current user is either the specified user or the superuser
+ *
+ * @param string $theUser
+ * @throws Piwik_Access_NoAccessException if the user is neither the super user nor the user $theUser
+ */
+ static public function checkUserIsSuperUserOrTheUser($theUser)
+ {
+ try {
+ if (Piwik::getCurrentUserLogin() !== $theUser) {
+ // or to the super user
+ Piwik::checkUserIsSuperUser();
+ }
+ } catch (Piwik_Access_NoAccessException $e) {
+ throw new Piwik_Access_NoAccessException(Piwik_Translate('General_ExceptionCheckUserIsSuperUserOrTheUser', array($theUser)));
+ }
+ }
+
+ /**
+ * Returns true if the current user is the Super User
+ *
+ * @return bool
+ */
+ static public function isUserIsSuperUser()
+ {
+ try {
+ self::checkUserIsSuperUser();
+ return true;
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Is user the anonymous user?
+ *
+ * @return bool True if anonymouse; false otherwise
+ */
+ static public function isUserIsAnonymous()
+ {
+ return Piwik::getCurrentUserLogin() == 'anonymous';
+ }
+
+ /**
+ * Checks if user is not the anonymous user.
+ *
+ * @throws Piwik_Access_NoAccessException if user is anonymous.
+ */
+ static public function checkUserIsNotAnonymous()
+ {
+ if (self::isUserIsAnonymous()) {
+ throw new Piwik_Access_NoAccessException(Piwik_Translate('General_YouMustBeLoggedIn'));
+ }
+ }
+
+ /**
+ * Helper method user to set the current as Super User.
+ * This should be used with great care as this gives the user all permissions.
+ *
+ * @param bool $bool true to set current user as super user
+ */
+ static public function setUserIsSuperUser($bool = true)
+ {
+ Zend_Registry::get('access')->setSuperUser($bool);
+ }
+
+ /**
+ * Check that user is the superuser
+ *
+ * @throws Exception if not the superuser
+ */
+ static public function checkUserIsSuperUser()
+ {
+ Zend_Registry::get('access')->checkUserIsSuperUser();
+ }
+
+ /**
+ * Returns true if the user has admin access to the sites
+ *
+ * @param mixed $idSites
+ * @return bool
+ */
+ static public function isUserHasAdminAccess($idSites)
+ {
+ try {
+ self::checkUserHasAdminAccess($idSites);
+ return true;
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Check user has admin access to the sites
+ *
+ * @param mixed $idSites
+ * @throws Exception if user doesn't have admin access to the sites
+ */
+ static public function checkUserHasAdminAccess($idSites)
+ {
+ Zend_Registry::get('access')->checkUserHasAdminAccess($idSites);
+ }
+
+ /**
+ * Returns true if the user has admin access to any sites
+ *
+ * @return bool
+ */
+ static public function isUserHasSomeAdminAccess()
+ {
+ try {
+ self::checkUserHasSomeAdminAccess();
+ return true;
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Check user has admin access to any sites
+ *
+ * @throws Exception if user doesn't have admin access to any sites
+ */
+ static public function checkUserHasSomeAdminAccess()
+ {
+ Zend_Registry::get('access')->checkUserHasSomeAdminAccess();
+ }
+
+ /**
+ * Returns true if the user has view access to the sites
+ *
+ * @param mixed $idSites
+ * @return bool
+ */
+ static public function isUserHasViewAccess($idSites)
+ {
+ try {
+ self::checkUserHasViewAccess($idSites);
+ return true;
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Check user has view access to the sites
+ *
+ * @param mixed $idSites
+ * @throws Exception if user doesn't have view access to sites
+ */
+ static public function checkUserHasViewAccess($idSites)
+ {
+ Zend_Registry::get('access')->checkUserHasViewAccess($idSites);
+ }
+
+ /**
+ * Returns true if the user has view access to any sites
+ *
+ * @return bool
+ */
+ static public function isUserHasSomeViewAccess()
+ {
+ try {
+ self::checkUserHasSomeViewAccess();
+ return true;
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Check user has view access to any sites
+ *
+ * @throws Exception if user doesn't have view access to any sites
+ */
+ static public function checkUserHasSomeViewAccess()
+ {
+ Zend_Registry::get('access')->checkUserHasSomeViewAccess();
+ }
+
+ /*
* Current module, action, plugin
*/
- /**
- * Returns the name of the Login plugin currently being used.
- * Must be used since it is not allowed to hardcode 'Login' in URLs
- * in case another Login plugin is being used.
- *
- * @return string
- */
- static public function getLoginPluginName()
- {
- return Zend_Registry::get('auth')->getName();
- }
-
- /**
- * Returns the plugin currently being used to display the page
- *
- * @return Piwik_Plugin
- */
- static public function getCurrentPlugin()
- {
- return Piwik_PluginsManager::getInstance()->getLoadedPlugin(Piwik::getModule());
- }
-
- /**
- * Returns the current module read from the URL (eg. 'API', 'UserSettings', etc.)
- *
- * @return string
- */
- static public function getModule()
- {
- return Piwik_Common::getRequestVar('module', '', 'string');
- }
-
- /**
- * Returns the current action read from the URL
- *
- * @return string
- */
- static public function getAction()
- {
- return Piwik_Common::getRequestVar('action', '', 'string');
- }
-
- /**
- * Helper method used in API function to introduce array elements in API parameters.
- * Array elements can be passed by comma separated values, or using the notation
- * array[]=value1&array[]=value2 in the URL.
- * This function will handle both cases and return the array.
- *
- * @param array|string $columns
- * @return array
- */
- static public function getArrayFromApiParameter($columns)
- {
- return $columns === false
- ? array()
- : (is_array($columns)
- ? $columns
- : explode(',', $columns)
- );
- }
-
- /**
- * Redirect to module (and action)
- *
- * @param string $newModule Target module
- * @param string $newAction Target action
- * @param array $parameters Parameters to modify in the URL
- * @return bool false if the URL to redirect to is already this URL
- */
- static public function redirectToModule( $newModule, $newAction = '', $parameters = array() )
- {
- $newUrl = 'index.php' . Piwik_Url::getCurrentQueryStringWithParametersModified(
- array('module' => $newModule, 'action' => $newAction)
- + $parameters
- );
- Piwik_Url::redirectToUrl($newUrl);
- }
-
-/*
+ /**
+ * Returns the name of the Login plugin currently being used.
+ * Must be used since it is not allowed to hardcode 'Login' in URLs
+ * in case another Login plugin is being used.
+ *
+ * @return string
+ */
+ static public function getLoginPluginName()
+ {
+ return Zend_Registry::get('auth')->getName();
+ }
+
+ /**
+ * Returns the plugin currently being used to display the page
+ *
+ * @return Piwik_Plugin
+ */
+ static public function getCurrentPlugin()
+ {
+ return Piwik_PluginsManager::getInstance()->getLoadedPlugin(Piwik::getModule());
+ }
+
+ /**
+ * Returns the current module read from the URL (eg. 'API', 'UserSettings', etc.)
+ *
+ * @return string
+ */
+ static public function getModule()
+ {
+ return Piwik_Common::getRequestVar('module', '', 'string');
+ }
+
+ /**
+ * Returns the current action read from the URL
+ *
+ * @return string
+ */
+ static public function getAction()
+ {
+ return Piwik_Common::getRequestVar('action', '', 'string');
+ }
+
+ /**
+ * Helper method used in API function to introduce array elements in API parameters.
+ * Array elements can be passed by comma separated values, or using the notation
+ * array[]=value1&array[]=value2 in the URL.
+ * This function will handle both cases and return the array.
+ *
+ * @param array|string $columns
+ * @return array
+ */
+ static public function getArrayFromApiParameter($columns)
+ {
+ return $columns === false
+ ? array()
+ : (is_array($columns)
+ ? $columns
+ : explode(',', $columns)
+ );
+ }
+
+ /**
+ * Redirect to module (and action)
+ *
+ * @param string $newModule Target module
+ * @param string $newAction Target action
+ * @param array $parameters Parameters to modify in the URL
+ * @return bool false if the URL to redirect to is already this URL
+ */
+ static public function redirectToModule($newModule, $newAction = '', $parameters = array())
+ {
+ $newUrl = 'index.php' . Piwik_Url::getCurrentQueryStringWithParametersModified(
+ array('module' => $newModule, 'action' => $newAction)
+ + $parameters
+ );
+ Piwik_Url::redirectToUrl($newUrl);
+ }
+
+ /*
* Global database object
*/
- /**
- * Create database object and connect to database
- * @param array|null $dbInfos
- */
- static public function createDatabaseObject( $dbInfos = null )
- {
- $config = Piwik_Config::getInstance();
-
- if(is_null($dbInfos))
- {
- $dbInfos = $config->database;
- }
-
- Piwik_PostEvent('Reporting.getDatabaseConfig', $dbInfos);
-
- $dbInfos['profiler'] = $config->Debug['enable_sql_profiler'];
-
- $db = null;
- Piwik_PostEvent('Reporting.createDatabase', $db);
- if(is_null($db))
- {
- $adapter = $dbInfos['adapter'];
- $db = @Piwik_Db_Adapter::factory($adapter, $dbInfos);
- }
- Zend_Registry::set('db', $db);
- }
-
- /**
- * Disconnect from database
- */
- static public function disconnectDatabase()
- {
- Zend_Registry::get('db')->closeConnection();
- }
-
- /**
- * Checks the database server version against the required minimum
- * version.
- *
- * @see config/global.ini.php
- * @since 0.4.4
- * @throws Exception if server version is less than the required version
- */
- static public function checkDatabaseVersion()
- {
- Zend_Registry::get('db')->checkServerVersion();
- }
-
- /**
- * Check database connection character set is utf8.
- *
- * @return bool True if it is (or doesn't matter); false otherwise
- */
- static public function isDatabaseConnectionUTF8()
- {
- return Zend_Registry::get('db')->isConnectionUTF8();
- }
-
-/*
+ /**
+ * Create database object and connect to database
+ * @param array|null $dbInfos
+ */
+ static public function createDatabaseObject($dbInfos = null)
+ {
+ $config = Piwik_Config::getInstance();
+
+ if (is_null($dbInfos)) {
+ $dbInfos = $config->database;
+ }
+
+ Piwik_PostEvent('Reporting.getDatabaseConfig', $dbInfos);
+
+ $dbInfos['profiler'] = $config->Debug['enable_sql_profiler'];
+
+ $db = null;
+ Piwik_PostEvent('Reporting.createDatabase', $db);
+ if (is_null($db)) {
+ $adapter = $dbInfos['adapter'];
+ $db = @Piwik_Db_Adapter::factory($adapter, $dbInfos);
+ }
+ Zend_Registry::set('db', $db);
+ }
+
+ /**
+ * Disconnect from database
+ */
+ static public function disconnectDatabase()
+ {
+ Zend_Registry::get('db')->closeConnection();
+ }
+
+ /**
+ * Checks the database server version against the required minimum
+ * version.
+ *
+ * @see config/global.ini.php
+ * @since 0.4.4
+ * @throws Exception if server version is less than the required version
+ */
+ static public function checkDatabaseVersion()
+ {
+ Zend_Registry::get('db')->checkServerVersion();
+ }
+
+ /**
+ * Check database connection character set is utf8.
+ *
+ * @return bool True if it is (or doesn't matter); false otherwise
+ */
+ static public function isDatabaseConnectionUTF8()
+ {
+ return Zend_Registry::get('db')->isConnectionUTF8();
+ }
+
+ /*
* Global log object
*/
- /**
- * Create log object
- * @throws Exception
- */
- static public function createLogObject()
- {
- $configAPI = Piwik_Config::getInstance()->log;
-
- $aLoggers = array(
- 'logger_api_call' => new Piwik_Log_APICall,
- 'logger_exception' => new Piwik_Log_Exception,
- 'logger_error' => new Piwik_Log_Error,
- 'logger_message' => new Piwik_Log_Message,
- );
-
- foreach($configAPI as $loggerType => $aRecordTo)
- {
- if(isset($aLoggers[$loggerType]))
- {
- $logger = $aLoggers[$loggerType];
-
- foreach($aRecordTo as $recordTo)
- {
- switch($recordTo)
- {
- case 'screen':
- $logger->addWriteToScreen();
- break;
-
- case 'database':
- $logger->addWriteToDatabase();
- break;
-
- case 'file':
- $logger->addWriteToFile();
- break;
-
- default:
- throw new Exception("'$recordTo' is not a valid Log type. Valid logger types are: screen, database, file.");
- break;
- }
- }
- }
- }
-
- foreach($aLoggers as $loggerType =>$logger)
- {
- if($logger->getWritersCount() == 0)
- {
- $logger->addWriteToNull();
- }
- Zend_Registry::set($loggerType, $logger);
- }
- }
-
-/*
+ /**
+ * Create log object
+ * @throws Exception
+ */
+ static public function createLogObject()
+ {
+ $configAPI = Piwik_Config::getInstance()->log;
+
+ $aLoggers = array(
+ 'logger_api_call' => new Piwik_Log_APICall,
+ 'logger_exception' => new Piwik_Log_Exception,
+ 'logger_error' => new Piwik_Log_Error,
+ 'logger_message' => new Piwik_Log_Message,
+ );
+
+ foreach ($configAPI as $loggerType => $aRecordTo) {
+ if (isset($aLoggers[$loggerType])) {
+ $logger = $aLoggers[$loggerType];
+
+ foreach ($aRecordTo as $recordTo) {
+ switch ($recordTo) {
+ case 'screen':
+ $logger->addWriteToScreen();
+ break;
+
+ case 'database':
+ $logger->addWriteToDatabase();
+ break;
+
+ case 'file':
+ $logger->addWriteToFile();
+ break;
+
+ default:
+ throw new Exception("'$recordTo' is not a valid Log type. Valid logger types are: screen, database, file.");
+ break;
+ }
+ }
+ }
+ }
+
+ foreach ($aLoggers as $loggerType => $logger) {
+ if ($logger->getWritersCount() == 0) {
+ $logger->addWriteToNull();
+ }
+ Zend_Registry::set($loggerType, $logger);
+ }
+ }
+
+ /*
* Global config object
*/
- /**
- * Create configuration object
- */
- static public function createConfigObject()
- {
- // for backward compatibility
- Zend_Registry::set('config', new Piwik_Config_Compat());
+ /**
+ * Create configuration object
+ */
+ static public function createConfigObject()
+ {
+ // for backward compatibility
+ Zend_Registry::set('config', new Piwik_Config_Compat());
- // instantiate the singleton
- $config = Piwik_Config::getInstance();
- $config->init();
- }
+ // instantiate the singleton
+ $config = Piwik_Config::getInstance();
+ $config->init();
+ }
-/*
+ /*
* Global access object
*/
- /**
- * Create access object
- */
- static public function createAccessObject()
- {
- Zend_Registry::set('access', new Piwik_Access());
- }
+ /**
+ * Create access object
+ */
+ static public function createAccessObject()
+ {
+ Zend_Registry::set('access', new Piwik_Access());
+ }
-/*
+ /*
* User input validation
*/
- /**
- * Returns true if the email is a valid email
- *
- * @param string $email
- * @return bool
- */
- static public function isValidEmailString( $email )
- {
- return (preg_match('/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9_.-]+\.[a-zA-Z]{2,7}$/D', $email) > 0);
- }
-
- /**
- * Returns true if the login is valid.
- * Warning: does not check if the login already exists! You must use UsersManager_API->userExists as well.
- *
- * @param string $userLogin
- * @throws Exception
- * @return bool
- */
- static public function checkValidLoginString( $userLogin )
- {
- if(!self::isChecksEnabled()
- && !empty($userLogin))
- {
- return;
- }
- $loginMinimumLength = 3;
- $loginMaximumLength = 100;
- $l = strlen($userLogin);
- if(!($l >= $loginMinimumLength
- && $l <= $loginMaximumLength
- && (preg_match('/^[A-Za-z0-9_.@+-]*$/D', $userLogin) > 0))
- )
- {
- throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidLoginFormat', array($loginMinimumLength, $loginMaximumLength)));
- }
- }
-
- /**
- * Should Piwik check that the login & password have minimum length and valid characters?
- *
- * @return bool True if checks enabled; false otherwise
- */
- static public function isChecksEnabled()
- {
- return Piwik_Config::getInstance()->General['disable_checks_usernames_attributes'] == 0;
- }
-
- /**
- * Is GD php extension (sparklines, graphs) available?
- *
- * @return bool
- */
- static public function isGdExtensionEnabled()
- {
- static $gd = null;
- if(is_null($gd))
- {
- $extensions = @get_loaded_extensions();
- $gd = in_array('gd', $extensions) && function_exists('imageftbbox');
- }
- return $gd;
- }
-
-/*
+ /**
+ * Returns true if the email is a valid email
+ *
+ * @param string $email
+ * @return bool
+ */
+ static public function isValidEmailString($email)
+ {
+ return (preg_match('/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9_.-]+\.[a-zA-Z]{2,7}$/D', $email) > 0);
+ }
+
+ /**
+ * Returns true if the login is valid.
+ * Warning: does not check if the login already exists! You must use UsersManager_API->userExists as well.
+ *
+ * @param string $userLogin
+ * @throws Exception
+ * @return bool
+ */
+ static public function checkValidLoginString($userLogin)
+ {
+ if (!self::isChecksEnabled()
+ && !empty($userLogin)
+ ) {
+ return;
+ }
+ $loginMinimumLength = 3;
+ $loginMaximumLength = 100;
+ $l = strlen($userLogin);
+ if (!($l >= $loginMinimumLength
+ && $l <= $loginMaximumLength
+ && (preg_match('/^[A-Za-z0-9_.@+-]*$/D', $userLogin) > 0))
+ ) {
+ throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidLoginFormat', array($loginMinimumLength, $loginMaximumLength)));
+ }
+ }
+
+ /**
+ * Should Piwik check that the login & password have minimum length and valid characters?
+ *
+ * @return bool True if checks enabled; false otherwise
+ */
+ static public function isChecksEnabled()
+ {
+ return Piwik_Config::getInstance()->General['disable_checks_usernames_attributes'] == 0;
+ }
+
+ /**
+ * Is GD php extension (sparklines, graphs) available?
+ *
+ * @return bool
+ */
+ static public function isGdExtensionEnabled()
+ {
+ static $gd = null;
+ if (is_null($gd)) {
+ $extensions = @get_loaded_extensions();
+ $gd = in_array('gd', $extensions) && function_exists('imageftbbox');
+ }
+ return $gd;
+ }
+
+ /*
* Date / Timezone
*/
- /**
- * Determine if this php version/build supports timezone manipulation
- * (e.g., php >= 5.2, or compiled with EXPERIMENTAL_DATE_SUPPORT=1 for
- * php < 5.2).
- *
- * @return bool True if timezones supported; false otherwise
- */
- static public function isTimezoneSupportEnabled()
- {
- return
- function_exists( 'date_create' ) &&
- function_exists( 'date_default_timezone_set' ) &&
- function_exists( 'timezone_identifiers_list' ) &&
- function_exists( 'timezone_open' ) &&
- function_exists( 'timezone_offset_get' );
- }
-
-/*
+ /**
+ * Determine if this php version/build supports timezone manipulation
+ * (e.g., php >= 5.2, or compiled with EXPERIMENTAL_DATE_SUPPORT=1 for
+ * php < 5.2).
+ *
+ * @return bool True if timezones supported; false otherwise
+ */
+ static public function isTimezoneSupportEnabled()
+ {
+ return
+ function_exists('date_create') &&
+ function_exists('date_default_timezone_set') &&
+ function_exists('timezone_identifiers_list') &&
+ function_exists('timezone_open') &&
+ function_exists('timezone_offset_get');
+ }
+
+ /*
* Database and table definition methods
*/
- /**
- * Is the schema available?
- *
- * @return bool True if schema is available; false otherwise
- */
- static public function isAvailable()
- {
- return Piwik_Db_Schema::getInstance()->isAvailable();
- }
-
- /**
- * Get the SQL to create a specific Piwik table
- *
- * @param string $tableName
- * @return string SQL
- */
- static public function getTableCreateSql( $tableName )
- {
- return Piwik_Db_Schema::getInstance()->getTableCreateSql($tableName);
- }
-
- /**
- * Get the SQL to create Piwik tables
- *
- * @return array array of strings containing SQL
- */
- static public function getTablesCreateSql()
- {
- return Piwik_Db_Schema::getInstance()->getTablesCreateSql();
- }
-
- /**
- * Create database
- *
- * @param string|null $dbName
- */
- static public function createDatabase( $dbName = null )
- {
- Piwik_Db_Schema::getInstance()->createDatabase($dbName);
- }
-
- /**
- * Drop database
- */
- static public function dropDatabase()
- {
- Piwik_Db_Schema::getInstance()->dropDatabase();
- }
-
- /**
- * Create all tables
- */
- static public function createTables()
- {
- Piwik_Db_Schema::getInstance()->createTables();
- }
-
- /**
- * Creates an entry in the User table for the "anonymous" user.
- */
- static public function createAnonymousUser()
- {
- Piwik_Db_Schema::getInstance()->createAnonymousUser();
- }
-
- /**
- * Truncate all tables
- */
- static public function truncateAllTables()
- {
- Piwik_Db_Schema::getInstance()->truncateAllTables();
- }
-
- /**
- * Drop specific tables
- *
- * @param array $doNotDelete Names of tables to not delete
- */
- static public function dropTables( $doNotDelete = array() )
- {
- Piwik_Db_Schema::getInstance()->dropTables($doNotDelete);
- }
-
- /**
- * Names of all the prefixed tables in piwik
- * Doesn't use the DB
- *
- * @return array Table names
- */
- static public function getTablesNames()
- {
- return Piwik_Db_Schema::getInstance()->getTablesNames();
- }
-
- /**
- * Get list of tables installed
- *
- * @param bool $forceReload Invalidate cache
- * @return array Tables installed
- */
- static public function getTablesInstalled($forceReload = true)
- {
- return Piwik_Db_Schema::getInstance()->getTablesInstalled($forceReload);
- }
-
- /**
- * Returns all table names archive_*
- *
- * @return array
- */
- static public function getTablesArchivesInstalled()
- {
- $archiveTables = array();
- $tables = Piwik::getTablesInstalled();
- foreach($tables as $table)
- {
- if(strpos($table, 'archive_') !== false)
- {
- $archiveTables[] = $table;
- }
- }
- return $archiveTables;
- }
-
- /**
- * Batch insert into table from CSV (or other delimited) file.
- *
- * @param string $tableName Name of table
- * @param array $fields Field names
- * @param string $filePath Path name of a file.
- * @param array $fileSpec File specifications (delimiter, line terminator, etc)
- * @param book $throwException Should re-throw any exception, used in system check
- * @return bool True if successful; false otherwise
- */
- static public function createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec)
- {
- // On Windows, MySQL expects forward slashes as directory separators
- if (Piwik_Common::isWindows()) {
- $filePath = str_replace('\\', '/', $filePath);
- }
-
- $query = "
+ /**
+ * Is the schema available?
+ *
+ * @return bool True if schema is available; false otherwise
+ */
+ static public function isAvailable()
+ {
+ return Piwik_Db_Schema::getInstance()->isAvailable();
+ }
+
+ /**
+ * Get the SQL to create a specific Piwik table
+ *
+ * @param string $tableName
+ * @return string SQL
+ */
+ static public function getTableCreateSql($tableName)
+ {
+ return Piwik_Db_Schema::getInstance()->getTableCreateSql($tableName);
+ }
+
+ /**
+ * Get the SQL to create Piwik tables
+ *
+ * @return array array of strings containing SQL
+ */
+ static public function getTablesCreateSql()
+ {
+ return Piwik_Db_Schema::getInstance()->getTablesCreateSql();
+ }
+
+ /**
+ * Create database
+ *
+ * @param string|null $dbName
+ */
+ static public function createDatabase($dbName = null)
+ {
+ Piwik_Db_Schema::getInstance()->createDatabase($dbName);
+ }
+
+ /**
+ * Drop database
+ */
+ static public function dropDatabase()
+ {
+ Piwik_Db_Schema::getInstance()->dropDatabase();
+ }
+
+ /**
+ * Create all tables
+ */
+ static public function createTables()
+ {
+ Piwik_Db_Schema::getInstance()->createTables();
+ }
+
+ /**
+ * Creates an entry in the User table for the "anonymous" user.
+ */
+ static public function createAnonymousUser()
+ {
+ Piwik_Db_Schema::getInstance()->createAnonymousUser();
+ }
+
+ /**
+ * Truncate all tables
+ */
+ static public function truncateAllTables()
+ {
+ Piwik_Db_Schema::getInstance()->truncateAllTables();
+ }
+
+ /**
+ * Drop specific tables
+ *
+ * @param array $doNotDelete Names of tables to not delete
+ */
+ static public function dropTables($doNotDelete = array())
+ {
+ Piwik_Db_Schema::getInstance()->dropTables($doNotDelete);
+ }
+
+ /**
+ * Names of all the prefixed tables in piwik
+ * Doesn't use the DB
+ *
+ * @return array Table names
+ */
+ static public function getTablesNames()
+ {
+ return Piwik_Db_Schema::getInstance()->getTablesNames();
+ }
+
+ /**
+ * Get list of tables installed
+ *
+ * @param bool $forceReload Invalidate cache
+ * @return array Tables installed
+ */
+ static public function getTablesInstalled($forceReload = true)
+ {
+ return Piwik_Db_Schema::getInstance()->getTablesInstalled($forceReload);
+ }
+
+ /**
+ * Returns all table names archive_*
+ *
+ * @return array
+ */
+ static public function getTablesArchivesInstalled()
+ {
+ $archiveTables = array();
+ $tables = Piwik::getTablesInstalled();
+ foreach ($tables as $table) {
+ if (strpos($table, 'archive_') !== false) {
+ $archiveTables[] = $table;
+ }
+ }
+ return $archiveTables;
+ }
+
+ /**
+ * Batch insert into table from CSV (or other delimited) file.
+ *
+ * @param string $tableName Name of table
+ * @param array $fields Field names
+ * @param string $filePath Path name of a file.
+ * @param array $fileSpec File specifications (delimiter, line terminator, etc)
+ * @param book $throwException Should re-throw any exception, used in system check
+ * @return bool True if successful; false otherwise
+ */
+ static public function createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec)
+ {
+ // On Windows, MySQL expects forward slashes as directory separators
+ if (Piwik_Common::isWindows()) {
+ $filePath = str_replace('\\', '/', $filePath);
+ }
+
+ $query = "
'$filePath'
REPLACE
INTO TABLE
- `".$tableName."`";
+ `" . $tableName . "`";
- if(isset($fileSpec['charset']))
- {
- $query .= ' CHARACTER SET '.$fileSpec['charset'];
- }
+ if (isset($fileSpec['charset'])) {
+ $query .= ' CHARACTER SET ' . $fileSpec['charset'];
+ }
- $fieldList = '('.join(',', $fields).')';
+ $fieldList = '(' . join(',', $fields) . ')';
- $query .= "
+ $query .= "
FIELDS TERMINATED BY
- '".$fileSpec['delim']."'
+ '" . $fileSpec['delim'] . "'
ENCLOSED BY
- '".$fileSpec['quote']."'
+ '" . $fileSpec['quote'] . "'
";
- if(isset($fileSpec['escape']))
- {
- $query .= " ESCAPED BY '".$fileSpec['escape']."'";
- }
- $query .= "
+ if (isset($fileSpec['escape'])) {
+ $query .= " ESCAPED BY '" . $fileSpec['escape'] . "'";
+ }
+ $query .= "
LINES TERMINATED BY
- '".$fileSpec['eol']."'
+ '" . $fileSpec['eol'] . "'
$fieldList
";
- /*
+ /*
* First attempt: assume web server and MySQL server are on the same machine;
* this requires that the db user have the FILE privilege; however, since this is
* a global privilege, it may not be granted due to security concerns
*/
- $keywords = array('');
+ $keywords = array('');
- /*
+ /*
* Second attempt: using the LOCAL keyword means the client reads the file and sends it to the server;
* the LOCAL keyword may trigger a known PHP PDO_MYSQL bug when MySQL not built with --enable-local-infile
* @see http://bugs.php.net/bug.php?id=54158
*/
- $openBaseDir = ini_get('open_basedir');
- $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 ';
- }
-
- $exceptions = array();
- foreach($keywords as $keyword)
- {
- try {
- $queryStart = 'LOAD DATA '.$keyword.'INFILE ';
- $sql = $queryStart.$query;
- $result = @Piwik_Exec($sql);
- if(empty($result) || $result < 0)
- {
- continue;
- }
-
- return true;
- } catch(Exception $e) {
+ $openBaseDir = ini_get('open_basedir');
+ $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 ';
+ }
+
+ $exceptions = array();
+ foreach ($keywords as $keyword) {
+ try {
+ $queryStart = 'LOAD DATA ' . $keyword . 'INFILE ';
+ $sql = $queryStart . $query;
+ $result = @Piwik_Exec($sql);
+ if (empty($result) || $result < 0) {
+ continue;
+ }
+
+ return true;
+ } catch (Exception $e) {
// echo $sql . ' ---- ' . $e->getMessage();
- $code = $e->getCode();
- $message = $e->getMessage() . ($code ? "[$code]" : '');
- if(!Zend_Registry::get('db')->isErrNo($e, '1148'))
- {
- Piwik::log(sprintf("LOAD DATA INFILE failed... Error was: %s", $message));
- }
- $exceptions[] = "\n Try #" . (count($exceptions)+1) . ': ' . $queryStart .": ". $message;
- }
- }
- if(count($exceptions))
- {
- throw new Exception( implode( ",", $exceptions ) );
- }
- return false;
- }
-
- /**
- * Performs a batch insert into a specific table using either LOAD DATA INFILE or plain INSERTs,
- * as a fallback. On MySQL, LOAD DATA INFILE is 20x faster than a series of plain INSERTs.
- *
- * @param string $tableName PREFIXED table name! you must call Piwik_Common::prefixTable() before passing the table name
- * @param array $fields array of unquoted field names
- * @param array $values array of data to be inserted
- * @param bool $throwException Whether to throw an exception that was caught while trying
- * LOAD DATA INFILE, or not.
- * @throws Exception
- * @return bool True if the bulk LOAD was used, false if we fallback to plain INSERTs
- */
- static public function tableInsertBatch($tableName, $fields, $values, $throwException = false)
- {
- $filePath = PIWIK_USER_PATH . '/' . Piwik_AssetManager::MERGED_FILE_DIR . $tableName . '-'.Piwik_Common::generateUniqId().'.csv';
-
- if(Zend_Registry::get('db')->hasBulkLoader())
- {
- try {
+ $code = $e->getCode();
+ $message = $e->getMessage() . ($code ? "[$code]" : '');
+ if (!Zend_Registry::get('db')->isErrNo($e, '1148')) {
+ Piwik::log(sprintf("LOAD DATA INFILE failed... Error was: %s", $message));
+ }
+ $exceptions[] = "\n Try #" . (count($exceptions) + 1) . ': ' . $queryStart . ": " . $message;
+ }
+ }
+ if (count($exceptions)) {
+ throw new Exception(implode(",", $exceptions));
+ }
+ return false;
+ }
+
+ /**
+ * Performs a batch insert into a specific table using either LOAD DATA INFILE or plain INSERTs,
+ * as a fallback. On MySQL, LOAD DATA INFILE is 20x faster than a series of plain INSERTs.
+ *
+ * @param string $tableName PREFIXED table name! you must call Piwik_Common::prefixTable() before passing the table name
+ * @param array $fields array of unquoted field names
+ * @param array $values array of data to be inserted
+ * @param bool $throwException Whether to throw an exception that was caught while trying
+ * LOAD DATA INFILE, or not.
+ * @throws Exception
+ * @return bool True if the bulk LOAD was used, false if we fallback to plain INSERTs
+ */
+ static public function tableInsertBatch($tableName, $fields, $values, $throwException = false)
+ {
+ $filePath = PIWIK_USER_PATH . '/' . Piwik_AssetManager::MERGED_FILE_DIR . $tableName . '-' . Piwik_Common::generateUniqId() . '.csv';
+
+ if (Zend_Registry::get('db')->hasBulkLoader()) {
+ try {
// throw new Exception('');
- $fileSpec = array(
- 'delim' => "\t",
- 'quote' => '"', // chr(34)
- 'escape' => '\\\\', // chr(92)
- 'escapespecial_cb' => create_function('$str', 'return str_replace(array(chr(92), chr(34)), array(chr(92).chr(92), chr(92).chr(34)), $str);'),
- 'eol' => "\r\n",
- 'null' => 'NULL',
- );
-
- // hack for charset mismatch
- if(!self::isDatabaseConnectionUTF8() && !isset(Piwik_Config::getInstance()->database['charset']))
- {
- $fileSpec['charset'] = 'latin1';
- }
-
- self::createCSVFile($filePath, $fileSpec, $values);
-
- if(!is_readable($filePath))
- {
- throw new Exception("File $filePath could not be read.");
- }
-
- $rc = self::createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec);
- if($rc) {
- unlink($filePath);
- return true;
- }
- } catch(Exception $e) {
- Piwik::log(sprintf("LOAD DATA INFILE failed or not supported, falling back to normal INSERTs... Error was: %s", $e->getMessage()));
-
- if ($throwException)
- {
- throw $e;
- }
- }
- }
-
- // if all else fails, fallback to a series of INSERTs
- @unlink($filePath);
- self::tableInsertBatchIterate($tableName, $fields, $values);
- return false;
- }
-
- /**
- * Performs a batch insert into a specific table by iterating through the data
- *
- * NOTE: you should use tableInsertBatch() which will fallback to this function if LOAD DATA INFILE not available
- *
- * @param string $tableName PREFIXED table name! you must call Piwik_Common::prefixTable() before passing the table name
- * @param array $fields array of unquoted field names
- * @param array $values array of data to be inserted
- * @param bool $ignoreWhenDuplicate Ignore new rows that contain unique key values that duplicate old rows
- */
- static public function tableInsertBatchIterate($tableName, $fields, $values, $ignoreWhenDuplicate = true)
- {
- $fieldList = '('.join(',', $fields).')';
- $ignore = $ignoreWhenDuplicate ? 'IGNORE' : '';
-
- foreach($values as $row) {
- $query = "INSERT $ignore
- INTO ".$tableName."
+ $fileSpec = array(
+ 'delim' => "\t",
+ 'quote' => '"', // chr(34)
+ 'escape' => '\\\\', // chr(92)
+ 'escapespecial_cb' => create_function('$str', 'return str_replace(array(chr(92), chr(34)), array(chr(92).chr(92), chr(92).chr(34)), $str);'),
+ 'eol' => "\r\n",
+ 'null' => 'NULL',
+ );
+
+ // hack for charset mismatch
+ if (!self::isDatabaseConnectionUTF8() && !isset(Piwik_Config::getInstance()->database['charset'])) {
+ $fileSpec['charset'] = 'latin1';
+ }
+
+ self::createCSVFile($filePath, $fileSpec, $values);
+
+ if (!is_readable($filePath)) {
+ throw new Exception("File $filePath could not be read.");
+ }
+
+ $rc = self::createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec);
+ if ($rc) {
+ unlink($filePath);
+ return true;
+ }
+ } catch (Exception $e) {
+ Piwik::log(sprintf("LOAD DATA INFILE failed or not supported, falling back to normal INSERTs... Error was: %s", $e->getMessage()));
+
+ if ($throwException) {
+ throw $e;
+ }
+ }
+ }
+
+ // if all else fails, fallback to a series of INSERTs
+ @unlink($filePath);
+ self::tableInsertBatchIterate($tableName, $fields, $values);
+ return false;
+ }
+
+ /**
+ * Performs a batch insert into a specific table by iterating through the data
+ *
+ * NOTE: you should use tableInsertBatch() which will fallback to this function if LOAD DATA INFILE not available
+ *
+ * @param string $tableName PREFIXED table name! you must call Piwik_Common::prefixTable() before passing the table name
+ * @param array $fields array of unquoted field names
+ * @param array $values array of data to be inserted
+ * @param bool $ignoreWhenDuplicate Ignore new rows that contain unique key values that duplicate old rows
+ */
+ static public function tableInsertBatchIterate($tableName, $fields, $values, $ignoreWhenDuplicate = true)
+ {
+ $fieldList = '(' . join(',', $fields) . ')';
+ $ignore = $ignoreWhenDuplicate ? 'IGNORE' : '';
+
+ foreach ($values as $row) {
+ $query = "INSERT $ignore
+ INTO " . $tableName . "
$fieldList
- VALUES (".Piwik_Common::getSqlStringFieldsArray($row).")";
- Piwik_Query($query, $row);
- }
- }
-
- /**
- * Generate advisory lock name
- *
- * @param int $idsite
- * @param Piwik_Period $period
- * @param Piwik_Segment $segment
- * @return string
- */
- static public function getArchiveProcessingLockName($idsite, $period, Piwik_Segment $segment)
- {
- $config = Piwik_Config::getInstance();
-
- $lockName = 'piwik.'
- . $config->database['dbname'] . '.'
- . $config->database['tables_prefix'] . '/'
- . $idsite . '/'
- . (!$segment->isEmpty() ? $segment->getHash().'/' : '' )
- . $period->getId() . '/'
- . $period->getDateStart()->toString('Y-m-d') . ','
- . $period->getDateEnd()->toString('Y-m-d');
- return $lockName .'/'. md5($lockName . Piwik_Common::getSalt());
- }
-
- /**
- * Get an advisory lock
- *
- * @param int $idsite
- * @param Piwik_Period $period
- * @param Piwik_Segment $segment
- * @return bool True if lock acquired; false otherwise
- */
- static public function getArchiveProcessingLock($idsite, $period, $segment)
- {
- $lockName = self::getArchiveProcessingLockName($idsite, $period, $segment);
- return Piwik_GetDbLock($lockName, $maxRetries = 30);
- }
-
- /**
- * Release an advisory lock
- *
- * @param int $idsite
- * @param Piwik_Period $period
- * @param Piwik_Segment $segment
- * @return bool True if lock released; false otherwise
- */
- static public function releaseArchiveProcessingLock($idsite, $period, $segment)
- {
- $lockName = self::getArchiveProcessingLockName($idsite, $period, $segment);
- return Piwik_ReleaseDbLock($lockName);
- }
-
- /**
- * Cached result of isLockprivilegeGranted function.
- *
- * Public so tests can simulate the situation where the lock tables privilege isn't granted.
- *
- * @var bool
- */
- static public $lockPrivilegeGranted = null;
-
- /**
- * Checks whether the database user is allowed to lock tables.
- *
- * @return bool
- */
- static public function isLockPrivilegeGranted()
- {
- if (is_null(self::$lockPrivilegeGranted))
- {
- try
- {
- Piwik_LockTables(Piwik_Common::prefixTable('log_visit'));
- Piwik_UnlockAllTables();
-
- self::$lockPrivilegeGranted = true;
- }
- catch (Exception $ex)
- {
- self::$lockPrivilegeGranted = false;
- }
- }
-
- return self::$lockPrivilegeGranted;
- }
-
- /**
- * Utility function that checks if an object type is in a set of types.
- *
- * @param mixed $o
- * @param array $types List of class names that $o is expected to be one of.
- * @throws Exception if $o is not an instance of the types contained in $types.
- */
- static public function checkObjectTypeIs( $o, $types )
- {
- foreach ($types as $type)
- {
- if ($o instanceof $type)
- {
- return;
- }
- }
-
- $oType = is_object($o) ? get_class($o) : gettype($o);
- throw new Exception("Invalid variable type '$oType', expected one of following: ".implode(', ', $types));
- }
-
- /**
- * Returns true if an array is an associative array, false if otherwise.
- *
- * This method determines if an array is associative by checking that the
- * first element's key is 0, and that each successive element's key is
- * one greater than the last.
- *
- * @param array $array
- * @return bool
- */
- static public function isAssociativeArray( $array )
- {
- reset($array);
- if (!is_numeric(key($array))
- || key($array) != 0) // first key must be 0
- {
- return true;
- }
-
- // check that each key is == next key - 1 w/o actually indexing the array
- while (true)
- {
- $current = key($array);
-
- next($array);
- $next = key($array);
-
- if ($next === null)
- {
- break;
- }
- else if ($current + 1 != $next)
- {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Checks if the filesystem Piwik stores sessions in is NFS or not. This
- * check is done in order to avoid using file based sessions on NFS system,
- * since on such a filesystem file locking can make file based sessions
- * incredibly slow.
- *
- * Note: In order to figure this out, we try to run the 'df' program. If
- * the 'exec' or 'shell_exec' functions are not available, we can't do
- * the check.
- *
- * @return bool True if on an NFS filesystem, false if otherwise or if we
- * can't use shell_exec or exec.
- */
- public static function checkIfFileSystemIsNFS()
- {
- $sessionsPath = Piwik_Session::getSessionsDirectory();
-
- // this command will display details for the filesystem that holds the $sessionsPath
- // path, but only if its type is NFS. if not NFS, df will return one or less lines
- // and the return code 1. if NFS, it will return 0 and at least 2 lines of text.
- $command = "df -T -t nfs \"$sessionsPath\" 2>&1";
-
- if (function_exists('exec')) // use exec
- {
- $output = $returnCode = null;
- @exec($command, $output, $returnCode);
-
- // check if filesystem is NFS
- if ($returnCode == 0
- && count($output) > 1)
- {
- return true;
- }
- }
- else if (function_exists('shell_exec')) // use shell_exec
- {
- $output = @shell_exec($command);
- if ($output)
- {
- $output = explode("\n", $output);
- if (count($output) > 1) // check if filesystem is NFS
- {
- return true;
- }
- }
- }
-
- return false; // not NFS, or we can't run a program to find out
- }
+ VALUES (" . Piwik_Common::getSqlStringFieldsArray($row) . ")";
+ Piwik_Query($query, $row);
+ }
+ }
+
+ /**
+ * Generate advisory lock name
+ *
+ * @param int $idsite
+ * @param Piwik_Period $period
+ * @param Piwik_Segment $segment
+ * @return string
+ */
+ static public function getArchiveProcessingLockName($idsite, $period, Piwik_Segment $segment)
+ {
+ $config = Piwik_Config::getInstance();
+
+ $lockName = 'piwik.'
+ . $config->database['dbname'] . '.'
+ . $config->database['tables_prefix'] . '/'
+ . $idsite . '/'
+ . (!$segment->isEmpty() ? $segment->getHash() . '/' : '')
+ . $period->getId() . '/'
+ . $period->getDateStart()->toString('Y-m-d') . ','
+ . $period->getDateEnd()->toString('Y-m-d');
+ return $lockName . '/' . md5($lockName . Piwik_Common::getSalt());
+ }
+
+ /**
+ * Get an advisory lock
+ *
+ * @param int $idsite
+ * @param Piwik_Period $period
+ * @param Piwik_Segment $segment
+ * @return bool True if lock acquired; false otherwise
+ */
+ static public function getArchiveProcessingLock($idsite, $period, $segment)
+ {
+ $lockName = self::getArchiveProcessingLockName($idsite, $period, $segment);
+ return Piwik_GetDbLock($lockName, $maxRetries = 30);
+ }
+
+ /**
+ * Release an advisory lock
+ *
+ * @param int $idsite
+ * @param Piwik_Period $period
+ * @param Piwik_Segment $segment
+ * @return bool True if lock released; false otherwise
+ */
+ static public function releaseArchiveProcessingLock($idsite, $period, $segment)
+ {
+ $lockName = self::getArchiveProcessingLockName($idsite, $period, $segment);
+ return Piwik_ReleaseDbLock($lockName);
+ }
+
+ /**
+ * Cached result of isLockprivilegeGranted function.
+ *
+ * Public so tests can simulate the situation where the lock tables privilege isn't granted.
+ *
+ * @var bool
+ */
+ static public $lockPrivilegeGranted = null;
+
+ /**
+ * Checks whether the database user is allowed to lock tables.
+ *
+ * @return bool
+ */
+ static public function isLockPrivilegeGranted()
+ {
+ if (is_null(self::$lockPrivilegeGranted)) {
+ try {
+ Piwik_LockTables(Piwik_Common::prefixTable('log_visit'));
+ Piwik_UnlockAllTables();
+
+ self::$lockPrivilegeGranted = true;
+ } catch (Exception $ex) {
+ self::$lockPrivilegeGranted = false;
+ }
+ }
+
+ return self::$lockPrivilegeGranted;
+ }
+
+ /**
+ * Utility function that checks if an object type is in a set of types.
+ *
+ * @param mixed $o
+ * @param array $types List of class names that $o is expected to be one of.
+ * @throws Exception if $o is not an instance of the types contained in $types.
+ */
+ static public function checkObjectTypeIs($o, $types)
+ {
+ foreach ($types as $type) {
+ if ($o instanceof $type) {
+ return;
+ }
+ }
+
+ $oType = is_object($o) ? get_class($o) : gettype($o);
+ throw new Exception("Invalid variable type '$oType', expected one of following: " . implode(', ', $types));
+ }
+
+ /**
+ * Returns true if an array is an associative array, false if otherwise.
+ *
+ * This method determines if an array is associative by checking that the
+ * first element's key is 0, and that each successive element's key is
+ * one greater than the last.
+ *
+ * @param array $array
+ * @return bool
+ */
+ static public function isAssociativeArray($array)
+ {
+ reset($array);
+ if (!is_numeric(key($array))
+ || key($array) != 0
+ ) // first key must be 0
+ {
+ return true;
+ }
+
+ // check that each key is == next key - 1 w/o actually indexing the array
+ while (true) {
+ $current = key($array);
+
+ next($array);
+ $next = key($array);
+
+ if ($next === null) {
+ break;
+ } else if ($current + 1 != $next) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if the filesystem Piwik stores sessions in is NFS or not. This
+ * check is done in order to avoid using file based sessions on NFS system,
+ * since on such a filesystem file locking can make file based sessions
+ * incredibly slow.
+ *
+ * Note: In order to figure this out, we try to run the 'df' program. If
+ * the 'exec' or 'shell_exec' functions are not available, we can't do
+ * the check.
+ *
+ * @return bool True if on an NFS filesystem, false if otherwise or if we
+ * can't use shell_exec or exec.
+ */
+ public static function checkIfFileSystemIsNFS()
+ {
+ $sessionsPath = Piwik_Session::getSessionsDirectory();
+
+ // this command will display details for the filesystem that holds the $sessionsPath
+ // path, but only if its type is NFS. if not NFS, df will return one or less lines
+ // and the return code 1. if NFS, it will return 0 and at least 2 lines of text.
+ $command = "df -T -t nfs \"$sessionsPath\" 2>&1";
+
+ if (function_exists('exec')) // use exec
+ {
+ $output = $returnCode = null;
+ @exec($command, $output, $returnCode);
+
+ // check if filesystem is NFS
+ if ($returnCode == 0
+ && count($output) > 1
+ ) {
+ return true;
+ }
+ } else if (function_exists('shell_exec')) // use shell_exec
+ {
+ $output = @shell_exec($command);
+ if ($output) {
+ $output = explode("\n", $output);
+ if (count($output) > 1) // check if filesystem is NFS
+ {
+ return true;
+ }
+ }
+ }
+
+ return false; // not NFS, or we can't run a program to find out
+ }
}
diff --git a/core/Plugin.php b/core/Plugin.php
index 793423f26e..cfc6a0cdb4 100644
--- a/core/Plugin.php
+++ b/core/Plugin.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,113 +12,113 @@
/**
* Abstract class to define a Piwik_Plugin.
* Any plugin has to at least implement the abstract methods of this class.
- *
+ *
* @package Piwik
*/
abstract class Piwik_Plugin
{
- /**
- * Returns the plugin details
- * - 'description' => string // 1-2 sentence description of the plugin
- * - 'author' => string // plugin author
- * - 'author_homepage' => string // author homepage URL (or email "mailto:youremail@example.org")
- * - 'homepage' => string // plugin homepage URL
- * - 'license' => string // plugin license
- * - 'license_homepage' => string // license homepage URL
- * - 'version' => string // plugin version number; examples and 3rd party plugins must not use Piwik_Version::VERSION; 3rd party plugins must increment the version number with each plugin release
- * - 'translationAvailable' => bool // is there a translation file in plugins/your-plugin/lang/* ?
- * - 'TrackerPlugin' => bool // should we load this plugin during the stats logging process?
- *
- * @return array
- */
- abstract public function getInformation();
+ /**
+ * Returns the plugin details
+ * - 'description' => string // 1-2 sentence description of the plugin
+ * - 'author' => string // plugin author
+ * - 'author_homepage' => string // author homepage URL (or email "mailto:youremail@example.org")
+ * - 'homepage' => string // plugin homepage URL
+ * - 'license' => string // plugin license
+ * - 'license_homepage' => string // license homepage URL
+ * - 'version' => string // plugin version number; examples and 3rd party plugins must not use Piwik_Version::VERSION; 3rd party plugins must increment the version number with each plugin release
+ * - 'translationAvailable' => bool // is there a translation file in plugins/your-plugin/lang/* ?
+ * - 'TrackerPlugin' => bool // should we load this plugin during the stats logging process?
+ *
+ * @return array
+ */
+ abstract public function getInformation();
- /**
- * Returns the list of hooks registered with the methods names
- *
- * @return array
- */
- public function getListHooksRegistered()
- {
- return array();
- }
+ /**
+ * Returns the list of hooks registered with the methods names
+ *
+ * @return array
+ */
+ public function getListHooksRegistered()
+ {
+ return array();
+ }
- /**
- * Executed after loading plugin and registering translations
- * Useful for code that uses translated strings from the plugin.
- */
- public function postLoad()
- {
- return;
- }
-
- /**
- * Install the plugin
- * - create tables
- * - update existing tables
- * - etc.
- */
- public function install()
- {
- return;
- }
-
- /**
- * Remove the created resources during the install
- */
- public function uninstall()
- {
- return;
- }
-
- /**
- * Executed every time the plugin is enabled
- */
- public function activate()
- {
- return;
- }
-
- /**
- * Executed every time the plugin is disabled
- */
- public function deactivate()
- {
- return;
- }
-
- /**
- * Returns the plugin version number
- *
- * @return string
- */
- public function getVersion()
- {
- $info = $this->getInformation();
- return $info['version'];
- }
-
- /**
- * Returns the plugin's base class name without the "Piwik_" prefix,
- * e.g., "UserCountry" when the plugin class is "Piwik_UserCountry"
- *
- * @return string
- */
- final public function getPluginName()
- {
- return Piwik::unprefixClass(get_class($this));
- }
+ /**
+ * Executed after loading plugin and registering translations
+ * Useful for code that uses translated strings from the plugin.
+ */
+ public function postLoad()
+ {
+ return;
+ }
- /**
- * Returns the plugin's base class name without the "Piwik_" prefix,
- * e.g., "UserCountry" when the plugin class is "Piwik_UserCountry"
- *
- * @deprecated since 1.2 - for backward compatibility
- *
- * @return string
- */
- final public function getClassName()
- {
- return $this->getPluginName();
- }
+ /**
+ * Install the plugin
+ * - create tables
+ * - update existing tables
+ * - etc.
+ */
+ public function install()
+ {
+ return;
+ }
+
+ /**
+ * Remove the created resources during the install
+ */
+ public function uninstall()
+ {
+ return;
+ }
+
+ /**
+ * Executed every time the plugin is enabled
+ */
+ public function activate()
+ {
+ return;
+ }
+
+ /**
+ * Executed every time the plugin is disabled
+ */
+ public function deactivate()
+ {
+ return;
+ }
+
+ /**
+ * Returns the plugin version number
+ *
+ * @return string
+ */
+ public function getVersion()
+ {
+ $info = $this->getInformation();
+ return $info['version'];
+ }
+
+ /**
+ * Returns the plugin's base class name without the "Piwik_" prefix,
+ * e.g., "UserCountry" when the plugin class is "Piwik_UserCountry"
+ *
+ * @return string
+ */
+ final public function getPluginName()
+ {
+ return Piwik::unprefixClass(get_class($this));
+ }
+
+ /**
+ * Returns the plugin's base class name without the "Piwik_" prefix,
+ * e.g., "UserCountry" when the plugin class is "Piwik_UserCountry"
+ *
+ * @deprecated since 1.2 - for backward compatibility
+ *
+ * @return string
+ */
+ final public function getClassName()
+ {
+ return $this->getPluginName();
+ }
}
diff --git a/core/Plugin/Config.php b/core/Plugin/Config.php
index 81df393b60..2b41f23d5a 100644
--- a/core/Plugin/Config.php
+++ b/core/Plugin/Config.php
@@ -1,55 +1,55 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Read / write local plugin-specific configuration
- *
+ *
* @package Piwik
*/
class Piwik_Plugin_Config
{
- private $pluginName;
- private $configFileName;
+ private $pluginName;
+ private $configFileName;
- /**
- * Constructor
- *
- * @param string $pluginName name of the plugin
- * @param string $configFileName name of the plugin file; defaults to local.config.php
- */
- public function __construct($pluginName, $configFileName = 'local.config.php')
- {
- $this->pluginName = $pluginName;
- $this->configFileName = $configFileName;
- }
+ /**
+ * Constructor
+ *
+ * @param string $pluginName name of the plugin
+ * @param string $configFileName name of the plugin file; defaults to local.config.php
+ */
+ public function __construct($pluginName, $configFileName = 'local.config.php')
+ {
+ $this->pluginName = $pluginName;
+ $this->configFileName = $configFileName;
+ }
- /**
- * Load local plugin configuration
- *
- * @return array
- */
- public function load()
- {
- $pluginConfig = @include(PIWIK_USER_PATH . '/plugins/' . $this->pluginName . '/config/' . $this->configFileName);
+ /**
+ * Load local plugin configuration
+ *
+ * @return array
+ */
+ public function load()
+ {
+ $pluginConfig = @include(PIWIK_USER_PATH . '/plugins/' . $this->pluginName . '/config/' . $this->configFileName);
- return $pluginConfig;
- }
+ return $pluginConfig;
+ }
- /**
- * Store local plugin configuration
- *
- * @param array $pluginConfig
- */
- public function store($pluginConfig)
- {
- file_put_contents(PIWIK_USER_PATH . '/plugins/' . $this->pluginName . '/config/' . $this->configFileName, "<?php\nreturn ".var_export($pluginConfig, true).";\n");
- }
+ /**
+ * Store local plugin configuration
+ *
+ * @param array $pluginConfig
+ */
+ public function store($pluginConfig)
+ {
+ file_put_contents(PIWIK_USER_PATH . '/plugins/' . $this->pluginName . '/config/' . $this->configFileName, "<?php\nreturn " . var_export($pluginConfig, true) . ";\n");
+ }
}
diff --git a/core/PluginsFunctions/Sql.php b/core/PluginsFunctions/Sql.php
index 886325f086..5b2f2ee45a 100644
--- a/core/PluginsFunctions/Sql.php
+++ b/core/PluginsFunctions/Sql.php
@@ -16,375 +16,375 @@
*/
class Piwik_Sql
{
- /**
- * Returns the database adapter to use
- *
- * @return Piwik_Tracker_Db|Piwik_Db_Adapter_Interface
- */
- static private function getDb()
- {
- $db = null;
- if (!empty($GLOBALS['PIWIK_TRACKER_MODE'])) {
- $db = Piwik_Tracker::getDatabase();
- }
- if ($db === null) {
- $db = Zend_Registry::get('db');
- }
- return $db;
- }
-
- /**
- * Executes an unprepared SQL query on the DB. Recommended for DDL statements, e.g., CREATE/DROP/ALTER.
- * The return result is DBMS-specific. For MySQLI, it returns the number of rows affected. For PDO, it returns the Zend_Db_Statement object
- * If you want to fetch data from the DB you should use the function Piwik_FetchAll()
- *
- * @param string $sql SQL Query
- * @return integer|Zend_Db_Statement
- */
- static public function exec($sql)
- {
- $db = Zend_Registry::get('db');
- $profiler = $db->getProfiler();
- $q = $profiler->queryStart($sql, Zend_Db_Profiler::INSERT);
- $return = self::getDb()->exec($sql);
- $profiler->queryEnd($q);
- return $return;
- }
-
- /**
- * Executes a SQL query on the DB and returns the Zend_Db_Statement object
- * If you want to fetch data from the DB you should use the function Piwik_FetchAll()
- *
- * See also http://framework.zend.com/manual/en/zend.db.statement.html
- *
- * @param string $sql SQL Query
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
- * @return Zend_Db_Statement
- */
- static public function query($sql, $parameters = array())
- {
- return self::getDb()->query($sql, $parameters);
- }
-
- /**
- * Executes the SQL Query and fetches all the rows from the database query
- *
- * @param string $sql SQL Query
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
- * @return array (one row in the array per row fetched in the DB)
- */
- static public function fetchAll($sql, $parameters = array())
- {
- return self::getDb()->fetchAll($sql, $parameters);
- }
-
- /**
- * Fetches first row of result from the database query
- *
- * @param string $sql SQL Query
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
- * @return array
- */
- static public function fetchRow($sql, $parameters = array())
- {
- return self::getDb()->fetchRow($sql, $parameters);
- }
-
- /**
- * Fetches first column of first row of result from the database query
- *
- * @param string $sql SQL Query
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
- * @return string
- */
- static public function fetchOne($sql, $parameters = array())
- {
- return self::getDb()->fetchOne($sql, $parameters);
- }
-
- /**
- * Fetches result from the database query as an array of associative arrays.
- *
- * @param string $sql SQL query
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
- * @return array
- */
- static public function fetchAssoc($sql, $parameters = array())
- {
- return self::getDb()->fetchAssoc($sql, $parameters);
- }
-
- /**
- * Deletes all desired rows in a table, while using a limit. This function will execute a
- * DELETE query until there are no more rows to delete.
- *
- * @param string $table The name of the table to delete from. Must be prefixed.
- * @param string $where The where clause of the query. Must include the WHERE keyword.
- * @param int $maxRowsPerQuery The maximum number of rows to delete per DELETE query.
- * @param array $parameters Parameters to bind in the query.
- * @return int The total number of rows deleted.
- */
- static public function deleteAllRows($table, $where, $maxRowsPerQuery = 100000, $parameters = array())
- {
- $sql = "DELETE FROM $table $where LIMIT " . (int)$maxRowsPerQuery;
-
- // delete rows w/ a limit
- $totalRowsDeleted = 0;
- do {
- $rowsDeleted = self::query($sql, $parameters)->rowCount();
-
- $totalRowsDeleted += $rowsDeleted;
- } while ($rowsDeleted >= $maxRowsPerQuery);
-
- return $totalRowsDeleted;
- }
-
- /**
- * Runs an OPTIMIZE TABLE query on the supplied table or tables. The table names must be prefixed.
- *
- * @param string|array $tables The name of the table to optimize or an array of tables to optimize.
- * @return Zend_Db_Statement
- */
- static public function optimizeTables($tables)
- {
- $optimize = Piwik_Config::getInstance()->General['enable_sql_optimize_queries'];
- if (empty($optimize)) {
- return;
- }
-
- if (empty($tables)) {
- return false;
- }
- if (!is_array($tables)) {
- $tables = array($tables);
- }
-
- // filter out all InnoDB tables
- $nonInnoDbTables = array();
- foreach (Piwik_FetchAll("SHOW TABLE STATUS") as $row) {
- if (strtolower($row['Engine']) != 'innodb'
- && in_array($row['Name'], $tables)
- ) {
- $nonInnoDbTables[] = $row['Name'];
- }
- }
-
- if (empty($nonInnoDbTables)) {
- return false;
- }
-
- // optimize the tables
- return self::query("OPTIMIZE TABLE " . implode(',', $nonInnoDbTables));
- }
-
- /**
- * Drops the supplied table or tables. The table names must be prefixed.
- *
- * @param string|array $tables The name of the table to drop or an array of table names to drop.
- * @return Zend_Db_Statement
- */
- static public function dropTables($tables)
- {
- if (!is_array($tables)) {
- $tables = array($tables);
- }
-
- return self::query("DROP TABLE " . implode(',', $tables));
- }
-
- /**
- * Locks the supplied table or tables. The table names must be prefixed.
- *
- * @param string|array $tablesToRead The table or tables to obtain 'read' locks on.
- * @param string|array $tablesToWrite The table or tables to obtain 'write' locks on.
- * @return Zend_Db_Statement
- */
- static public function lockTables($tablesToRead, $tablesToWrite = array())
- {
- if (!is_array($tablesToRead)) {
- $tablesToRead = array($tablesToRead);
- }
- if (!is_array($tablesToWrite)) {
- $tablesToWrite = array($tablesToWrite);
- }
-
- $lockExprs = array();
- foreach ($tablesToWrite as $table) {
- $lockExprs[] = $table . " WRITE";
- }
- foreach ($tablesToRead as $table) {
- $lockExprs[] = $table . " READ";
- }
-
- return self::exec("LOCK TABLES " . implode(', ', $lockExprs));
- }
-
- /**
- * Releases all table locks.
- *
- * @return Zend_Db_Statement
- */
- static public function unlockAllTables()
- {
- return self::exec("UNLOCK TABLES");
- }
-
- /**
- * Performs a SELECT on a table one chunk at a time and returns the first
- * fetched value.
- *
- * @param string $sql The SQL to perform. The last two conditions of the WHERE
- * expression must be as follows: 'id >= ? AND id < ?' where
- * 'id' is the int id of the table. If $step < 0, the condition
- * should be 'id <= ? AND id > ?'.
- * @param int $first The minimum ID to loop from.
- * @param int $last The maximum ID to loop to.
- * @param int $step The maximum number of rows to scan in each smaller SELECT.
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
- * @return array
- */
- static public function segmentedFetchFirst($sql, $first, $last, $step, $params)
- {
- $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)));
- }
- } else {
- for ($i = $first; $result === false && $i >= $last; $i += $step) {
- $result = self::fetchOne($sql, array_merge($params, array($i, $i + $step)));
- }
- }
- return $result;
- }
-
- /**
- * Performs a SELECT on a table one chunk at a time and returns an array
- * of every fetched value.
- *
- * @param string $sql The SQL to perform. The last two conditions of the WHERE
- * expression must be as follows: 'id >= ? AND id < ?' where
- * 'id' is the int id of the table.
- * @param int $first The minimum ID to loop from.
- * @param int $last The maximum ID to loop to.
- * @param int $step The maximum number of rows to scan in each smaller SELECT.
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
- * @return array
- */
- static public function segmentedFetchOne($sql, $first, $last, $step, $params)
- {
- $result = array();
- if ($step > 0) {
- for ($i = $first; $i <= $last; $i += $step) {
- $result[] = self::fetchOne($sql, array_merge($params, array($i, $i + $step)));
- }
- } else {
- for ($i = $first; $i >= $last; $i += $step) {
- $result[] = self::fetchOne($sql, array_merge($params, array($i, $i + $step)));
- }
- }
- return $result;
- }
-
- /**
- * Performs a SELECT on a table one chunk at a time and returns an array
- * of every fetched row.
- *
- * @param string $sql The SQL to perform. The last two conditions of the WHERE
- * expression must be as follows: 'id >= ? AND id < ?' where
- * 'id' is the int id of the table.
- * @param int $first The minimum ID to loop from.
- * @param int $last The maximum ID to loop to.
- * @param int $step The maximum number of rows to scan in each smaller SELECT.
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
- * @return array
- */
- static public function segmentedFetchAll($sql, $first, $last, $step, $params)
- {
- $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));
- }
- } else {
- for ($i = $first; $i >= $last; $i += $step) {
- $currentParams = array_merge($params, array($i, $i + $step));
- $result = array_merge($result, self::fetchAll($sql, $currentParams));
- }
- }
- return $result;
- }
-
- /**
- * Performs a non-SELECT query on a table one chunk at a time.
- *
- * @param string $sql The SQL to perform. The last two conditions of the WHERE
- * expression must be as follows: 'id >= ? AND id < ?' where
- * 'id' is the int id of the table.
- * @param int $first The minimum ID to loop from.
- * @param int $last The maximum ID to loop to.
- * @param int $step The maximum number of rows to scan in each smaller query.
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
- * @return array
- */
- static public function segmentedQuery($sql, $first, $last, $step, $params)
- {
- if ($step > 0) {
- for ($i = $first; $i <= $last; $i += $step) {
- $currentParams = array_merge($params, array($i, $i + $step));
- self::query($sql, $currentParams);
- }
- } else {
- for ($i = $first; $i >= $last; $i += $step) {
- $currentParams = array_merge($params, array($i, $i + $step));
- self::query($sql, $currentParams);
- }
- }
- }
-
- /**
- * Attempts to get a named lock. This function uses a timeout of 1s, but will
- * retry a set number of time.
- *
- * @param string $lockName The lock name.
- * @param int $maxRetries The max number of times to retry.
- * @return bool true if the lock was obtained, false if otherwise.
- */
- static public function getDbLock($lockName, $maxRetries = 30)
- {
- /*
- * the server (e.g., shared hosting) may have a low wait timeout
- * so instead of a single GET_LOCK() with a 30 second timeout,
- * we use a 1 second timeout and loop, to avoid losing our MySQL
- * connection
- */
- $sql = 'SELECT GET_LOCK(?, 1)';
-
- $db = Zend_Registry::get('db');
-
- while ($maxRetries > 0) {
- if ($db->fetchOne($sql, array($lockName)) == '1') {
- return true;
- }
- $maxRetries--;
- }
- return false;
- }
-
- /**
- * Releases a named lock.
- *
- * @param string $lockName The lock name.
- * @return bool true if the lock was released, false if otherwise.
- */
- static public function releaseDbLock($lockName)
- {
- $sql = 'SELECT RELEASE_LOCK(?)';
-
- $db = Zend_Registry::get('db');
- return $db->fetchOne($sql, array($lockName)) == '1';
- }
+ /**
+ * Returns the database adapter to use
+ *
+ * @return Piwik_Tracker_Db|Piwik_Db_Adapter_Interface
+ */
+ static private function getDb()
+ {
+ $db = null;
+ if (!empty($GLOBALS['PIWIK_TRACKER_MODE'])) {
+ $db = Piwik_Tracker::getDatabase();
+ }
+ if ($db === null) {
+ $db = Zend_Registry::get('db');
+ }
+ return $db;
+ }
+
+ /**
+ * Executes an unprepared SQL query on the DB. Recommended for DDL statements, e.g., CREATE/DROP/ALTER.
+ * The return result is DBMS-specific. For MySQLI, it returns the number of rows affected. For PDO, it returns the Zend_Db_Statement object
+ * If you want to fetch data from the DB you should use the function Piwik_FetchAll()
+ *
+ * @param string $sql SQL Query
+ * @return integer|Zend_Db_Statement
+ */
+ static public function exec($sql)
+ {
+ $db = Zend_Registry::get('db');
+ $profiler = $db->getProfiler();
+ $q = $profiler->queryStart($sql, Zend_Db_Profiler::INSERT);
+ $return = self::getDb()->exec($sql);
+ $profiler->queryEnd($q);
+ return $return;
+ }
+
+ /**
+ * Executes a SQL query on the DB and returns the Zend_Db_Statement object
+ * If you want to fetch data from the DB you should use the function Piwik_FetchAll()
+ *
+ * See also http://framework.zend.com/manual/en/zend.db.statement.html
+ *
+ * @param string $sql SQL Query
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @return Zend_Db_Statement
+ */
+ static public function query($sql, $parameters = array())
+ {
+ return self::getDb()->query($sql, $parameters);
+ }
+
+ /**
+ * Executes the SQL Query and fetches all the rows from the database query
+ *
+ * @param string $sql SQL Query
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @return array (one row in the array per row fetched in the DB)
+ */
+ static public function fetchAll($sql, $parameters = array())
+ {
+ return self::getDb()->fetchAll($sql, $parameters);
+ }
+
+ /**
+ * Fetches first row of result from the database query
+ *
+ * @param string $sql SQL Query
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @return array
+ */
+ static public function fetchRow($sql, $parameters = array())
+ {
+ return self::getDb()->fetchRow($sql, $parameters);
+ }
+
+ /**
+ * Fetches first column of first row of result from the database query
+ *
+ * @param string $sql SQL Query
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @return string
+ */
+ static public function fetchOne($sql, $parameters = array())
+ {
+ return self::getDb()->fetchOne($sql, $parameters);
+ }
+
+ /**
+ * Fetches result from the database query as an array of associative arrays.
+ *
+ * @param string $sql SQL query
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @return array
+ */
+ static public function fetchAssoc($sql, $parameters = array())
+ {
+ return self::getDb()->fetchAssoc($sql, $parameters);
+ }
+
+ /**
+ * Deletes all desired rows in a table, while using a limit. This function will execute a
+ * DELETE query until there are no more rows to delete.
+ *
+ * @param string $table The name of the table to delete from. Must be prefixed.
+ * @param string $where The where clause of the query. Must include the WHERE keyword.
+ * @param int $maxRowsPerQuery The maximum number of rows to delete per DELETE query.
+ * @param array $parameters Parameters to bind in the query.
+ * @return int The total number of rows deleted.
+ */
+ static public function deleteAllRows($table, $where, $maxRowsPerQuery = 100000, $parameters = array())
+ {
+ $sql = "DELETE FROM $table $where LIMIT " . (int)$maxRowsPerQuery;
+
+ // delete rows w/ a limit
+ $totalRowsDeleted = 0;
+ do {
+ $rowsDeleted = self::query($sql, $parameters)->rowCount();
+
+ $totalRowsDeleted += $rowsDeleted;
+ } while ($rowsDeleted >= $maxRowsPerQuery);
+
+ return $totalRowsDeleted;
+ }
+
+ /**
+ * Runs an OPTIMIZE TABLE query on the supplied table or tables. The table names must be prefixed.
+ *
+ * @param string|array $tables The name of the table to optimize or an array of tables to optimize.
+ * @return Zend_Db_Statement
+ */
+ static public function optimizeTables($tables)
+ {
+ $optimize = Piwik_Config::getInstance()->General['enable_sql_optimize_queries'];
+ if (empty($optimize)) {
+ return;
+ }
+
+ if (empty($tables)) {
+ return false;
+ }
+ if (!is_array($tables)) {
+ $tables = array($tables);
+ }
+
+ // filter out all InnoDB tables
+ $nonInnoDbTables = array();
+ foreach (Piwik_FetchAll("SHOW TABLE STATUS") as $row) {
+ if (strtolower($row['Engine']) != 'innodb'
+ && in_array($row['Name'], $tables)
+ ) {
+ $nonInnoDbTables[] = $row['Name'];
+ }
+ }
+
+ if (empty($nonInnoDbTables)) {
+ return false;
+ }
+
+ // optimize the tables
+ return self::query("OPTIMIZE TABLE " . implode(',', $nonInnoDbTables));
+ }
+
+ /**
+ * Drops the supplied table or tables. The table names must be prefixed.
+ *
+ * @param string|array $tables The name of the table to drop or an array of table names to drop.
+ * @return Zend_Db_Statement
+ */
+ static public function dropTables($tables)
+ {
+ if (!is_array($tables)) {
+ $tables = array($tables);
+ }
+
+ return self::query("DROP TABLE " . implode(',', $tables));
+ }
+
+ /**
+ * Locks the supplied table or tables. The table names must be prefixed.
+ *
+ * @param string|array $tablesToRead The table or tables to obtain 'read' locks on.
+ * @param string|array $tablesToWrite The table or tables to obtain 'write' locks on.
+ * @return Zend_Db_Statement
+ */
+ static public function lockTables($tablesToRead, $tablesToWrite = array())
+ {
+ if (!is_array($tablesToRead)) {
+ $tablesToRead = array($tablesToRead);
+ }
+ if (!is_array($tablesToWrite)) {
+ $tablesToWrite = array($tablesToWrite);
+ }
+
+ $lockExprs = array();
+ foreach ($tablesToWrite as $table) {
+ $lockExprs[] = $table . " WRITE";
+ }
+ foreach ($tablesToRead as $table) {
+ $lockExprs[] = $table . " READ";
+ }
+
+ return self::exec("LOCK TABLES " . implode(', ', $lockExprs));
+ }
+
+ /**
+ * Releases all table locks.
+ *
+ * @return Zend_Db_Statement
+ */
+ static public function unlockAllTables()
+ {
+ return self::exec("UNLOCK TABLES");
+ }
+
+ /**
+ * Performs a SELECT on a table one chunk at a time and returns the first
+ * fetched value.
+ *
+ * @param string $sql The SQL to perform. The last two conditions of the WHERE
+ * expression must be as follows: 'id >= ? AND id < ?' where
+ * 'id' is the int id of the table. If $step < 0, the condition
+ * should be 'id <= ? AND id > ?'.
+ * @param int $first The minimum ID to loop from.
+ * @param int $last The maximum ID to loop to.
+ * @param int $step The maximum number of rows to scan in each smaller SELECT.
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @return array
+ */
+ static public function segmentedFetchFirst($sql, $first, $last, $step, $params)
+ {
+ $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)));
+ }
+ } else {
+ for ($i = $first; $result === false && $i >= $last; $i += $step) {
+ $result = self::fetchOne($sql, array_merge($params, array($i, $i + $step)));
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Performs a SELECT on a table one chunk at a time and returns an array
+ * of every fetched value.
+ *
+ * @param string $sql The SQL to perform. The last two conditions of the WHERE
+ * expression must be as follows: 'id >= ? AND id < ?' where
+ * 'id' is the int id of the table.
+ * @param int $first The minimum ID to loop from.
+ * @param int $last The maximum ID to loop to.
+ * @param int $step The maximum number of rows to scan in each smaller SELECT.
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @return array
+ */
+ static public function segmentedFetchOne($sql, $first, $last, $step, $params)
+ {
+ $result = array();
+ if ($step > 0) {
+ for ($i = $first; $i <= $last; $i += $step) {
+ $result[] = self::fetchOne($sql, array_merge($params, array($i, $i + $step)));
+ }
+ } else {
+ for ($i = $first; $i >= $last; $i += $step) {
+ $result[] = self::fetchOne($sql, array_merge($params, array($i, $i + $step)));
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Performs a SELECT on a table one chunk at a time and returns an array
+ * of every fetched row.
+ *
+ * @param string $sql The SQL to perform. The last two conditions of the WHERE
+ * expression must be as follows: 'id >= ? AND id < ?' where
+ * 'id' is the int id of the table.
+ * @param int $first The minimum ID to loop from.
+ * @param int $last The maximum ID to loop to.
+ * @param int $step The maximum number of rows to scan in each smaller SELECT.
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @return array
+ */
+ static public function segmentedFetchAll($sql, $first, $last, $step, $params)
+ {
+ $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));
+ }
+ } else {
+ for ($i = $first; $i >= $last; $i += $step) {
+ $currentParams = array_merge($params, array($i, $i + $step));
+ $result = array_merge($result, self::fetchAll($sql, $currentParams));
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Performs a non-SELECT query on a table one chunk at a time.
+ *
+ * @param string $sql The SQL to perform. The last two conditions of the WHERE
+ * expression must be as follows: 'id >= ? AND id < ?' where
+ * 'id' is the int id of the table.
+ * @param int $first The minimum ID to loop from.
+ * @param int $last The maximum ID to loop to.
+ * @param int $step The maximum number of rows to scan in each smaller query.
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @return array
+ */
+ static public function segmentedQuery($sql, $first, $last, $step, $params)
+ {
+ if ($step > 0) {
+ for ($i = $first; $i <= $last; $i += $step) {
+ $currentParams = array_merge($params, array($i, $i + $step));
+ self::query($sql, $currentParams);
+ }
+ } else {
+ for ($i = $first; $i >= $last; $i += $step) {
+ $currentParams = array_merge($params, array($i, $i + $step));
+ self::query($sql, $currentParams);
+ }
+ }
+ }
+
+ /**
+ * Attempts to get a named lock. This function uses a timeout of 1s, but will
+ * retry a set number of time.
+ *
+ * @param string $lockName The lock name.
+ * @param int $maxRetries The max number of times to retry.
+ * @return bool true if the lock was obtained, false if otherwise.
+ */
+ static public function getDbLock($lockName, $maxRetries = 30)
+ {
+ /*
+ * the server (e.g., shared hosting) may have a low wait timeout
+ * so instead of a single GET_LOCK() with a 30 second timeout,
+ * we use a 1 second timeout and loop, to avoid losing our MySQL
+ * connection
+ */
+ $sql = 'SELECT GET_LOCK(?, 1)';
+
+ $db = Zend_Registry::get('db');
+
+ while ($maxRetries > 0) {
+ if ($db->fetchOne($sql, array($lockName)) == '1') {
+ return true;
+ }
+ $maxRetries--;
+ }
+ return false;
+ }
+
+ /**
+ * Releases a named lock.
+ *
+ * @param string $lockName The lock name.
+ * @return bool true if the lock was released, false if otherwise.
+ */
+ static public function releaseDbLock($lockName)
+ {
+ $sql = 'SELECT RELEASE_LOCK(?)';
+
+ $db = Zend_Registry::get('db');
+ return $db->fetchOne($sql, array($lockName)) == '1';
+ }
}
/**
@@ -394,12 +394,12 @@ class Piwik_Sql
*
* @see Piwik_Sql::exec
*
- * @param string $sqlQuery SQL Query
+ * @param string $sqlQuery SQL Query
* @return integer|Zend_Db_Statement
*/
function Piwik_Exec($sqlQuery)
{
- return Piwik_Sql::exec($sqlQuery);
+ return Piwik_Sql::exec($sqlQuery);
}
/**
@@ -410,13 +410,13 @@ function Piwik_Exec($sqlQuery)
*
* @see Piwik_Sql::query
*
- * @param string $sqlQuery SQL Query
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @param string $sqlQuery SQL Query
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
* @return Zend_Db_Statement
*/
function Piwik_Query($sqlQuery, $parameters = array())
{
- return Piwik_Sql::query($sqlQuery, $parameters);
+ return Piwik_Sql::query($sqlQuery, $parameters);
}
/**
@@ -424,13 +424,13 @@ function Piwik_Query($sqlQuery, $parameters = array())
*
* @see Piwik_Sql::fetchAll
*
- * @param string $sqlQuery SQL Query
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @param string $sqlQuery SQL Query
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
* @return array (one row in the array per row fetched in the DB)
*/
function Piwik_FetchAll($sqlQuery, $parameters = array())
{
- return Piwik_Sql::fetchAll($sqlQuery, $parameters);
+ return Piwik_Sql::fetchAll($sqlQuery, $parameters);
}
/**
@@ -438,13 +438,13 @@ function Piwik_FetchAll($sqlQuery, $parameters = array())
*
* @see Piwik_Sql::fetchRow
*
- * @param string $sqlQuery SQL Query
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @param string $sqlQuery SQL Query
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
* @return array
*/
function Piwik_FetchRow($sqlQuery, $parameters = array())
{
- return Piwik_Sql::fetchRow($sqlQuery, $parameters);
+ return Piwik_Sql::fetchRow($sqlQuery, $parameters);
}
/**
@@ -452,13 +452,13 @@ function Piwik_FetchRow($sqlQuery, $parameters = array())
*
* @see Piwik_Sql::fetchOne
*
- * @param string $sqlQuery SQL Query
- * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
+ * @param string $sqlQuery SQL Query
+ * @param array $parameters Parameters to bind in the query, array( param1 => value1, param2 => value2)
* @return string
*/
function Piwik_FetchOne($sqlQuery, $parameters = array())
{
- return Piwik_Sql::fetchOne($sqlQuery, $parameters);
+ return Piwik_Sql::fetchOne($sqlQuery, $parameters);
}
/**
@@ -470,7 +470,7 @@ function Piwik_FetchOne($sqlQuery, $parameters = array())
*/
function Piwik_FetchAssoc($sqlQuery, $parameters = array())
{
- return Piwik_Sql::fetchAssoc($sqlQuery, $parameters);
+ return Piwik_Sql::fetchAssoc($sqlQuery, $parameters);
}
/**
@@ -479,15 +479,15 @@ function Piwik_FetchAssoc($sqlQuery, $parameters = array())
*
* @see Piwik_Sql::deleteAllRows
*
- * @param string $table The name of the table to delete from. Must be prefixed.
- * @param string $where The where clause of the query. Must include the WHERE keyword.
- * @param int $maxRowsPerQuery The maximum number of rows to delete per DELETE query.
- * @param array $parameters Parameters to bind in the query.
+ * @param string $table The name of the table to delete from. Must be prefixed.
+ * @param string $where The where clause of the query. Must include the WHERE keyword.
+ * @param int $maxRowsPerQuery The maximum number of rows to delete per DELETE query.
+ * @param array $parameters Parameters to bind in the query.
* @return int The total number of rows deleted.
*/
function Piwik_DeleteAllRows($table, $where, $maxRowsPerQuery, $parameters = array())
{
- return Piwik_Sql::deleteAllRows($table, $where, $maxRowsPerQuery, $parameters);
+ return Piwik_Sql::deleteAllRows($table, $where, $maxRowsPerQuery, $parameters);
}
/**
@@ -495,12 +495,12 @@ function Piwik_DeleteAllRows($table, $where, $maxRowsPerQuery, $parameters = arr
*
* @see Piwik_Sql::optimizeTables
*
- * @param string|array $tables The name of the table to optimize or an array of tables to optimize.
+ * @param string|array $tables The name of the table to optimize or an array of tables to optimize.
* @return Zend_Db_Statement
*/
function Piwik_OptimizeTables($tables)
{
- return Piwik_Sql::optimizeTables($tables);
+ return Piwik_Sql::optimizeTables($tables);
}
/**
@@ -508,12 +508,12 @@ function Piwik_OptimizeTables($tables)
*
* @see Piwik_Sql::dropTables
*
- * @param string|array $tables The name of the table to drop or an array of table names to drop.
+ * @param string|array $tables The name of the table to drop or an array of table names to drop.
* @return Zend_Db_Statement
*/
function Piwik_DropTables($tables)
{
- return Piwik_Sql::dropTables($tables);
+ return Piwik_Sql::dropTables($tables);
}
/**
@@ -521,13 +521,13 @@ function Piwik_DropTables($tables)
*
* @see Piwik_Sql::lockTables
*
- * @param string|array $tablesToRead The table or tables to obtain 'read' locks on.
- * @param string|array $tablesToWrite The table or tables to obtain 'write' locks on.
+ * @param string|array $tablesToRead The table or tables to obtain 'read' locks on.
+ * @param string|array $tablesToWrite The table or tables to obtain 'write' locks on.
* @return Zend_Db_Statement
*/
function Piwik_LockTables($tablesToRead, $tablesToWrite = array())
{
- return Piwik_Sql::lockTables($tablesToRead, $tablesToWrite);
+ return Piwik_Sql::lockTables($tablesToRead, $tablesToWrite);
}
/**
@@ -539,7 +539,7 @@ function Piwik_LockTables($tablesToRead, $tablesToWrite = array())
*/
function Piwik_UnlockAllTables()
{
- return Piwik_Sql::unlockAllTables();
+ return Piwik_Sql::unlockAllTables();
}
/**
@@ -564,7 +564,7 @@ function Piwik_UnlockAllTables()
*/
function Piwik_SegmentedFetchFirst($sql, $first, $last, $step, $params = array())
{
- return Piwik_Sql::segmentedFetchFirst($sql, $first, $last, $step, $params);
+ return Piwik_Sql::segmentedFetchFirst($sql, $first, $last, $step, $params);
}
/**
@@ -589,7 +589,7 @@ function Piwik_SegmentedFetchFirst($sql, $first, $last, $step, $params = array()
*/
function Piwik_SegmentedFetchOne($sql, $first, $last, $step, $params = array())
{
- return Piwik_Sql::segmentedFetchOne($sql, $first, $last, $step, $params);
+ return Piwik_Sql::segmentedFetchOne($sql, $first, $last, $step, $params);
}
/**
@@ -614,7 +614,7 @@ function Piwik_SegmentedFetchOne($sql, $first, $last, $step, $params = array())
*/
function Piwik_SegmentedFetchAll($sql, $first, $last, $step, $params = array())
{
- return Piwik_Sql::segmentedFetchAll($sql, $first, $last, $step, $params);
+ return Piwik_Sql::segmentedFetchAll($sql, $first, $last, $step, $params);
}
/**
@@ -639,7 +639,7 @@ function Piwik_SegmentedFetchAll($sql, $first, $last, $step, $params = array())
*/
function Piwik_SegmentedQuery($sql, $first, $last, $step, $params = array())
{
- return Piwik_Sql::segmentedQuery($sql, $first, $last, $step, $params);
+ return Piwik_Sql::segmentedQuery($sql, $first, $last, $step, $params);
}
/**
@@ -654,7 +654,7 @@ function Piwik_SegmentedQuery($sql, $first, $last, $step, $params = array())
*/
function Piwik_GetDbLock($lockName, $maxRetries = 30)
{
- return Piwik_Sql::getDbLock($lockName, $maxRetries);
+ return Piwik_Sql::getDbLock($lockName, $maxRetries);
}
/**
@@ -667,6 +667,6 @@ function Piwik_GetDbLock($lockName, $maxRetries = 30)
*/
function Piwik_ReleaseDbLock($lockName)
{
- return Piwik_Sql::releaseDbLock($lockName);
+ return Piwik_Sql::releaseDbLock($lockName);
}
diff --git a/core/PluginsFunctions/WidgetsList.php b/core/PluginsFunctions/WidgetsList.php
index a0b080ef95..08701776b0 100644
--- a/core/PluginsFunctions/WidgetsList.php
+++ b/core/PluginsFunctions/WidgetsList.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package PluginsFunctions
*/
@@ -14,161 +14,157 @@
*/
class Piwik_WidgetsList
{
- /**
- * List of widgets
- *
- * @var array
- */
- static protected $widgets = null;
-
- /**
- * Indicates whether the hook was posted or not
- *
- * @var bool
- */
- static protected $hookCalled = false;
-
- /**
- * Returns all available widgets
- * The event WidgetsList.add is used to create the list
- *
- * @return array
- */
- static public function get()
- {
- self::addWidgets();
- Piwik_PostEvent('WidgetsList.get');
-
- uksort(self::$widgets, array('Piwik_WidgetsList', '_sortWidgetCategories'));
-
- $widgets = array();
- foreach(self::$widgets as $key => $v)
- {
- if(isset($widgets[Piwik_Translate($key)])) {
- $v = array_merge($widgets[Piwik_Translate($key)], $v);
- }
- $widgets[Piwik_Translate($key)] = $v;
- }
- return $widgets;
- }
-
- private static function addWidgets()
- {
- if (!self::$hookCalled) {
- self::$hookCalled = true;
- Piwik_PostEvent('WidgetsList.add');
- }
- }
-
- /**
- * Sorting method for widget categories
- *
- * @param string $a
- * @param string $b
- * @return bool
- */
- protected static function _sortWidgetCategories($a, $b)
- {
- $order = array(
- 'VisitsSummary_VisitsSummary',
- 'Live!',
- 'General_Visitors',
- 'UserSettings_VisitorSettings',
- 'Actions_Actions',
- 'Actions_SubmenuSitesearch',
- 'Referers_Referers',
- 'Goals_Goals',
- 'Goals_Ecommerce',
- '_others_',
- 'Example Widgets',
- 'ExamplePlugin_exampleWidgets',
- );
-
- if(($oa = array_search($a, $order)) === false) {
- $oa = array_search('_others_', $order);
- }
- if(($ob = array_search($b, $order)) === false) {
- $ob = array_search('_others_', $order);
- }
- return $oa > $ob;
- }
-
- /**
- * Adds an widget to the list
- *
- * @param string $widgetCategory
- * @param string $widgetName
- * @param string $controllerName
- * @param string $controllerAction
- * @param array $customParameters
- */
- static public function add($widgetCategory, $widgetName, $controllerName, $controllerAction, $customParameters)
- {
- $widgetName = Piwik_Translate($widgetName);
- $widgetUniqueId = 'widget' . $controllerName . $controllerAction;
- foreach($customParameters as $name => $value)
- {
- if (is_array($value))
- {
- // use 'Array' for backward compatibility;
- // could we switch to using $value[0]?
- $value = 'Array';
- }
- $widgetUniqueId .= $name . $value;
- }
- self::$widgets[$widgetCategory][] = array(
- 'name' => $widgetName,
- 'uniqueId' => $widgetUniqueId,
- 'parameters' => array ( 'module' => $controllerName,
- 'action' => $controllerAction
- ) + $customParameters
- );
- }
-
-
- static public function remove($widgetCategory, $widgetName = false)
- {
- if(empty($widgetName)) {
- unset(self::$widgets[$widgetCategory]);
- return;
- }
- foreach(self::$widgets[$widgetCategory] as $id => $widget) {
- if($widget['name'] == $widgetName) {
- unset(self::$widgets[$widgetCategory][$id]);
- return;
- }
- }
- }
-
- /**
- * Checks if the widget with the given parameters exists in der widget list
- *
- * @param string $controllerName
- * @param string $controllerAction
- * @return bool
- */
- static public function isDefined($controllerName, $controllerAction)
- {
- $widgetsList = self::get();
- foreach($widgetsList as $widgetCategory => $widgets)
- {
- foreach($widgets as $widget)
- {
- if($widget['parameters']['module'] == $controllerName
- && $widget['parameters']['action'] == $controllerAction)
- {
- return true;
- }
- }
- }
- return false;
- }
+ /**
+ * List of widgets
+ *
+ * @var array
+ */
+ static protected $widgets = null;
+
+ /**
+ * Indicates whether the hook was posted or not
+ *
+ * @var bool
+ */
+ static protected $hookCalled = false;
+
+ /**
+ * Returns all available widgets
+ * The event WidgetsList.add is used to create the list
+ *
+ * @return array
+ */
+ static public function get()
+ {
+ self::addWidgets();
+ Piwik_PostEvent('WidgetsList.get');
+
+ uksort(self::$widgets, array('Piwik_WidgetsList', '_sortWidgetCategories'));
+
+ $widgets = array();
+ foreach (self::$widgets as $key => $v) {
+ if (isset($widgets[Piwik_Translate($key)])) {
+ $v = array_merge($widgets[Piwik_Translate($key)], $v);
+ }
+ $widgets[Piwik_Translate($key)] = $v;
+ }
+ return $widgets;
+ }
+
+ private static function addWidgets()
+ {
+ if (!self::$hookCalled) {
+ self::$hookCalled = true;
+ Piwik_PostEvent('WidgetsList.add');
+ }
+ }
+
+ /**
+ * Sorting method for widget categories
+ *
+ * @param string $a
+ * @param string $b
+ * @return bool
+ */
+ protected static function _sortWidgetCategories($a, $b)
+ {
+ $order = array(
+ 'VisitsSummary_VisitsSummary',
+ 'Live!',
+ 'General_Visitors',
+ 'UserSettings_VisitorSettings',
+ 'Actions_Actions',
+ 'Actions_SubmenuSitesearch',
+ 'Referers_Referers',
+ 'Goals_Goals',
+ 'Goals_Ecommerce',
+ '_others_',
+ 'Example Widgets',
+ 'ExamplePlugin_exampleWidgets',
+ );
+
+ if (($oa = array_search($a, $order)) === false) {
+ $oa = array_search('_others_', $order);
+ }
+ if (($ob = array_search($b, $order)) === false) {
+ $ob = array_search('_others_', $order);
+ }
+ return $oa > $ob;
+ }
+
+ /**
+ * Adds an widget to the list
+ *
+ * @param string $widgetCategory
+ * @param string $widgetName
+ * @param string $controllerName
+ * @param string $controllerAction
+ * @param array $customParameters
+ */
+ static public function add($widgetCategory, $widgetName, $controllerName, $controllerAction, $customParameters)
+ {
+ $widgetName = Piwik_Translate($widgetName);
+ $widgetUniqueId = 'widget' . $controllerName . $controllerAction;
+ foreach ($customParameters as $name => $value) {
+ if (is_array($value)) {
+ // use 'Array' for backward compatibility;
+ // could we switch to using $value[0]?
+ $value = 'Array';
+ }
+ $widgetUniqueId .= $name . $value;
+ }
+ self::$widgets[$widgetCategory][] = array(
+ 'name' => $widgetName,
+ 'uniqueId' => $widgetUniqueId,
+ 'parameters' => array('module' => $controllerName,
+ 'action' => $controllerAction
+ ) + $customParameters
+ );
+ }
+
+
+ static public function remove($widgetCategory, $widgetName = false)
+ {
+ if (empty($widgetName)) {
+ unset(self::$widgets[$widgetCategory]);
+ return;
+ }
+ foreach (self::$widgets[$widgetCategory] as $id => $widget) {
+ if ($widget['name'] == $widgetName) {
+ unset(self::$widgets[$widgetCategory][$id]);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Checks if the widget with the given parameters exists in der widget list
+ *
+ * @param string $controllerName
+ * @param string $controllerAction
+ * @return bool
+ */
+ static public function isDefined($controllerName, $controllerAction)
+ {
+ $widgetsList = self::get();
+ foreach ($widgetsList as $widgetCategory => $widgets) {
+ foreach ($widgets as $widget) {
+ if ($widget['parameters']['module'] == $controllerName
+ && $widget['parameters']['action'] == $controllerAction
+ ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
/**
* Method to reset the widget list
* For testing only
*/
- public static function _reset() {
+ public static function _reset()
+ {
self::$widgets = null;
self::$hookCalled = false;
}
@@ -183,7 +179,7 @@ class Piwik_WidgetsList
*/
function Piwik_GetWidgetsList()
{
- return Piwik_WidgetsList::get();
+ return Piwik_WidgetsList::get();
}
/**
@@ -191,15 +187,15 @@ function Piwik_GetWidgetsList()
*
* @see Piwik_WidgetsList::add
*
- * @param string $widgetCategory
- * @param string $widgetName
- * @param string $controllerName
- * @param string $controllerAction
- * @param array $customParameters
+ * @param string $widgetCategory
+ * @param string $widgetName
+ * @param string $controllerName
+ * @param string $controllerAction
+ * @param array $customParameters
*/
-function Piwik_AddWidget( $widgetCategory, $widgetName, $controllerName, $controllerAction, $customParameters = array())
+function Piwik_AddWidget($widgetCategory, $widgetName, $controllerName, $controllerAction, $customParameters = array())
{
- Piwik_WidgetsList::add($widgetCategory, $widgetName, $controllerName, $controllerAction, $customParameters);
+ Piwik_WidgetsList::add($widgetCategory, $widgetName, $controllerName, $controllerAction, $customParameters);
}
/**
@@ -207,11 +203,11 @@ function Piwik_AddWidget( $widgetCategory, $widgetName, $controllerName, $contro
*
* @see Piwik_WidgetsList::isDefined
*
- * @param string $controllerName
- * @param string $controllerAction
+ * @param string $controllerName
+ * @param string $controllerAction
* @return bool
*/
function Piwik_IsWidgetDefined($controllerName, $controllerAction)
{
- return Piwik_WidgetsList::isDefined($controllerName, $controllerAction);
+ return Piwik_WidgetsList::isDefined($controllerName, $controllerAction);
}
diff --git a/core/PluginsManager.php b/core/PluginsManager.php
index de5a580d8a..5e4c7e3a5f 100644
--- a/core/PluginsManager.php
+++ b/core/PluginsManager.php
@@ -32,664 +32,614 @@ require_once PIWIK_INCLUDE_PATH . '/core/PluginsFunctions/Sql.php';
*/
class Piwik_PluginsManager
{
- /**
- * @var Event_Dispatcher
- */
- public $dispatcher;
-
- protected $pluginsToLoad = array();
-
- protected $doLoadPlugins = true;
- protected $loadedPlugins = array();
-
- protected $doLoadAlwaysActivatedPlugins = true;
- protected $pluginToAlwaysActivate = array(
- 'CoreHome',
- 'CoreUpdater',
- 'CoreAdminHome',
- 'CorePluginsAdmin',
- 'Installation',
- 'SitesManager',
- 'UsersManager',
- 'API',
- 'Proxy',
- 'LanguagesManager',
- );
-
- static private $instance = null;
-
- /**
- * Returns the singleton Piwik_PluginsManager
- *
- * @return Piwik_PluginsManager
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- private function __construct()
- {
- $this->dispatcher = Event_Dispatcher::getInstance();
- }
-
- /**
- * Update Plugins config
- *
- * @param array $plugins Plugins
- */
- private function updatePluginsConfig($plugins)
- {
- $section = Piwik_Config::getInstance()->Plugins;
- $section['Plugins'] = $plugins;
- Piwik_Config::getInstance()->Plugins = $section;
- }
-
- /**
- * Update Plugins_Tracker config
- *
- * @param array $plugins Plugins
- */
- private function updatePluginsTrackerConfig($plugins)
- {
- $section = Piwik_Config::getInstance()->Plugins_Tracker;
- $section['Plugins_Tracker'] = $plugins;
- Piwik_Config::getInstance()->Plugins_Tracker = $section;
- }
-
- /**
- * Update PluginsInstalled config
- *
- * @param array $plugins Plugins
- */
- private function updatePluginsInstalledConfig($plugins)
- {
- $section = Piwik_Config::getInstance()->PluginsInstalled;
- $section['PluginsInstalled'] = $plugins;
- Piwik_Config::getInstance()->PluginsInstalled = $section;
- }
-
- /**
- * Returns true if plugin is always activated
- *
- * @param string $name Name of plugin
- * @return bool
- */
- public function isPluginAlwaysActivated( $name )
- {
- return in_array( $name, $this->pluginToAlwaysActivate);
- }
-
- /**
- * Returns true if plugin has been activated
- *
- * @param string $name Name of plugin
- * @return bool
- */
- public function isPluginActivated( $name )
- {
- return in_array( $name, $this->pluginsToLoad)
- || $this->isPluginAlwaysActivated( $name );
- }
-
- /**
- * Returns true if plugin is loaded (in memory)
- *
- * @param string $name Name of plugin
- * @return bool
- */
- public function isPluginLoaded( $name )
- {
- return isset($this->loadedPlugins[$name]);
- }
-
- /**
- * Reads the directories inside the plugins/ directory and returns their names in an array
- *
- * @return array
- */
- public function readPluginsDirectory()
- {
- $pluginsName = _glob( PIWIK_INCLUDE_PATH . '/plugins/*', GLOB_ONLYDIR);
- $result = array();
- if ($pluginsName != false)
- {
- foreach ($pluginsName as $path)
- {
- $name = basename($path);
- if (file_exists($path.'/'.$name.'.php')) // only add folder if a Plugin/Plugin.php file exists
- {
- $result[] = $name;
- }
- }
- }
- return $result;
- }
-
- /**
- * Deactivate plugin
- *
- * @param string $pluginName Name of plugin
- */
- public function deactivatePlugin($pluginName)
- {
- $plugins = $this->pluginsToLoad;
- $key = array_search($pluginName, $plugins);
-
- $plugin = $this->loadPlugin($pluginName);
- if ($plugin !== null)
- {
- $plugin->deactivate();
- }
-
- if($key !== false)
- {
- unset($plugins[$key]);
- }
- $this->updatePluginsConfig($plugins);
-
- $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'];
- if(!is_null($pluginsTracker))
- {
- $key = array_search($pluginName, $pluginsTracker);
- if($key !== false)
- {
- unset($pluginsTracker[$key]);
- $this->updatePluginsTrackerConfig($pluginsTracker);
- }
- }
-
- // Delete merged js/css files to force regenerations to exclude the deactivated plugin
- Piwik_Config::getInstance()->forceSave();
- Piwik::deleteAllCacheOnUpdate();
- }
-
- /**
- * Install loaded plugins
- */
- public function installLoadedPlugins()
- {
- foreach($this->getLoadedPlugins() as $plugin)
- {
- try {
- $this->installPluginIfNecessary( $plugin );
- }catch(Exception $e){
- echo $e->getMessage();
- }
- }
- }
-
- /**
- * Activate the specified plugin and install (if needed)
- *
- * @param string $pluginName Name of plugin
- * @throws Exception
- */
- public function activatePlugin($pluginName)
- {
- $plugins = Piwik_Config::getInstance()->Plugins['Plugins'];
- if(in_array($pluginName, $plugins))
- {
- throw new Exception("Plugin '$pluginName' already activated.");
- }
-
- $existingPlugins = $this->readPluginsDirectory();
- if( array_search($pluginName, $existingPlugins) === false)
- {
- // ToDo: This fails in tracker-mode. We should log this however.
- //Piwik::log(sprintf("Unable to find the plugin '%s' in activatePlugin.", $pluginName));
- return;
- }
-
- $plugin = $this->loadPlugin($pluginName);
- if ($plugin === null)
- {
- return;
- }
-
- $this->installPluginIfNecessary($plugin);
-
- $plugin->activate();
-
- // we add the plugin to the list of activated plugins
- if(!in_array($pluginName, $plugins))
- {
- $plugins[] = $pluginName;
- }
- else
- {
- // clean up if we find a dupe
- $plugins = array_unique($plugins);
- }
-
- // the config file will automatically be saved with the new plugin
- $this->updatePluginsConfig($plugins);
- Piwik_Config::getInstance()->forceSave();
-
- // Delete merged js/css files to force regenerations to include the activated plugin
- Piwik::deleteAllCacheOnUpdate();
- }
-
- /**
- * Load the specified plugins
- *
- * @param array $pluginsToLoad Array of plugins to load
- */
- public function loadPlugins( array $pluginsToLoad )
- {
- // case no plugins to load
- if(is_null($pluginsToLoad))
- {
- $pluginsToLoad = array();
- }
- $this->pluginsToLoad = $pluginsToLoad;
- $this->reloadPlugins();
- }
-
- /**
- * Disable plugin loading
- */
- public function doNotLoadPlugins()
- {
- $this->doLoadPlugins = false;
- }
-
- /**
- * Disable loading of "always activated" plugins
- */
- public function doNotLoadAlwaysActivatedPlugins()
- {
- $this->doLoadAlwaysActivatedPlugins = false;
- }
-
- /**
- * Load translations for loaded plugins
- *
- * @param bool|string $language Optional language code
- */
- public function loadPluginTranslations($language = false)
- {
- if(empty($language))
- {
- $language = Piwik_Translate::getInstance()->getLanguageToLoad();
- }
- $plugins = $this->getLoadedPlugins();
-
- foreach($plugins as $plugin)
- {
- $this->loadTranslation( $plugin, $language );
- }
- }
-
- /**
- * Execute postLoad() hook for loaded plugins
- *
- * @see Piwik_Plugin::postLoad()
- */
- public function postLoadPlugins()
- {
- $plugins = $this->getLoadedPlugins();
- foreach($plugins as $plugin)
- {
- $plugin->postLoad();
- }
- }
-
- /**
- * Returns an array containing the plugins class names (eg. 'Piwik_UserCountry' and NOT 'UserCountry')
- *
- * @return array
- */
- public function getLoadedPluginsName()
- {
- return array_map('get_class', $this->getLoadedPlugins());
- }
-
- /**
- * Returns an array of key,value with the following format: array(
- * 'UserCountry' => Piwik_Plugin $pluginObject,
- * 'UserSettings' => Piwik_Plugin $pluginObject,
- * );
- *
- * @return array
- */
- public function getLoadedPlugins()
- {
- return $this->loadedPlugins;
- }
-
- /**
- * Returns the given Piwik_Plugin object
- *
- * @param string $name
- * @throws Exception
- * @return array
- */
- public function getLoadedPlugin($name)
- {
- if(!isset($this->loadedPlugins[$name]))
- {
- throw new Exception("The plugin '$name' has not been loaded.");
- }
- return $this->loadedPlugins[$name];
- }
-
- /**
- * Load the plugins classes installed.
- * Register the observers for every plugin.
- */
- private function reloadPlugins()
- {
- $this->pluginsToLoad = array_unique($this->pluginsToLoad);
-
- if($this->doLoadAlwaysActivatedPlugins)
- {
- $this->pluginsToLoad = array_merge($this->pluginsToLoad, $this->pluginToAlwaysActivate);
- }
-
- foreach($this->pluginsToLoad as $pluginName)
- {
- if(!$this->isPluginLoaded($pluginName))
- {
- $newPlugin = $this->loadPlugin($pluginName);
- if ($newPlugin === null)
- {
- continue;
- }
-
- if($this->doLoadPlugins
- && $this->isPluginActivated($pluginName))
- {
- $this->addPluginObservers( $newPlugin );
- }
- }
- }
- }
-
- /**
- * Loads the plugin filename and instantiates the plugin with the given name, eg. UserCountry
- * Do NOT give the class name ie. Piwik_UserCountry, but give the plugin name ie. UserCountry
- *
- * @param string $pluginName
- * @throws Exception
- * @return Piwik_Plugin|null
- */
- public function loadPlugin( $pluginName )
- {
- if(isset($this->loadedPlugins[$pluginName]))
- {
- return $this->loadedPlugins[$pluginName];
- }
- $pluginFileName = sprintf("%s/%s.php", $pluginName, $pluginName);
- $pluginClassName = sprintf('Piwik_%s', $pluginName);
-
- if( !Piwik_Common::isValidFilename($pluginName))
- {
- throw new Exception(sprintf("The plugin filename '%s' is not a valid filename", $pluginFileName));
- }
-
- $path = PIWIK_INCLUDE_PATH . '/plugins/' . $pluginFileName;
-
- if(!file_exists($path))
- {
- // ToDo: We should log this - but this will crash in Tracker mode since core/Piwik is not loaded
- //Piwik::log(sprintf("Unable to load plugin '%s' because '%s' couldn't be found.", $pluginName, $path));
- throw new Exception(sprintf("Unable to load plugin '%s' because '%s' couldn't be found.", $pluginName, $path));
- }
-
- // Don't remove this.
- // Our autoloader can't find plugins/PluginName/PluginName.php
- require_once $path; // prefixed by PIWIK_INCLUDE_PATH
-
- if(!class_exists($pluginClassName, false))
- {
- throw new Exception("The class $pluginClassName couldn't be found in the file '$path'");
- }
- $newPlugin = new $pluginClassName();
-
- if(!($newPlugin instanceof Piwik_Plugin))
- {
- throw new Exception("The plugin $pluginClassName in the file $path must inherit from Piwik_Plugin.");
- }
-
- $this->addLoadedPlugin( $pluginName, $newPlugin);
-
- return $newPlugin;
- }
-
- /**
- * Unload plugin
- *
- * @param Piwik_Plugin $plugin
- * @throws Exception
- */
- public function unloadPlugin( $plugin )
- {
- if(!($plugin instanceof Piwik_Plugin ))
- {
- $oPlugin = $this->loadPlugin( $plugin );
- if ($oPlugin === null)
- {
- unset($this->loadedPlugins[$plugin]);
- return;
- }
-
- $plugin = $oPlugin;
- }
- $hooks = $plugin->getListHooksRegistered();
-
- foreach($hooks as $hookName => $methodToCall)
- {
- $success = $this->dispatcher->removeObserver( array( $plugin, $methodToCall), $hookName );
- if($success !== true)
- {
- throw new Exception("Error unloading plugin = ".$plugin->getPluginName() . ", method = $methodToCall, hook = $hookName ");
- }
- }
- unset($this->loadedPlugins[$plugin->getPluginName()]);
- }
-
- /**
- * Unload all loaded plugins
- */
- public function unloadPlugins()
- {
- $pluginsLoaded = $this->getLoadedPlugins();
- foreach($pluginsLoaded as $plugin)
- {
- $this->unloadPlugin($plugin);
- }
- }
-
- /**
- * Install loaded plugins
- */
- private function installPlugins()
- {
- foreach($this->getLoadedPlugins() as $plugin)
- {
- $this->installPlugin($plugin);
- }
- }
-
- /**
- * Install a specific plugin
- *
- * @param Piwik_Plugin $plugin
- * @throws Piwik_PluginsManager_PluginException if installation fails
- */
- private function installPlugin( Piwik_Plugin $plugin )
- {
- try{
- $plugin->install();
- } catch(Exception $e) {
- throw new Piwik_PluginsManager_PluginException($plugin->getPluginName(), $e->getMessage());
- }
- }
-
-
- /**
- * For the given plugin, add all the observers of this plugin.
- *
- * @param Piwik_Plugin $plugin
- */
- private function addPluginObservers( Piwik_Plugin $plugin )
- {
- $hooks = $plugin->getListHooksRegistered();
-
- foreach($hooks as $hookName => $methodToCall)
- {
- $this->dispatcher->addObserver( array( $plugin, $methodToCall), $hookName );
- }
- }
-
- /**
- * Add a plugin in the loaded plugins array
- *
- * @param string $pluginName plugin name without prefix (eg. 'UserCountry')
- * @param Piwik_Plugin $newPlugin
- */
- private function addLoadedPlugin( $pluginName, Piwik_Plugin $newPlugin )
- {
- $this->loadedPlugins[$pluginName] = $newPlugin;
- }
-
- /**
- * Load translation
- *
- * @param Piwik_Plugin $plugin
- * @param string $langCode
- * @throws Exception
- * @return void
- */
- private function loadTranslation( $plugin, $langCode )
- {
- // we are in Tracker mode if Piwik_Loader is not (yet) loaded
- if(!class_exists('Piwik_Loader', false))
- {
- return ;
- }
-
- $infos = $plugin->getInformation();
- if(!isset($infos['translationAvailable']))
- {
- $infos['translationAvailable'] = false;
- }
- $translationAvailable = $infos['translationAvailable'];
-
- if(!$translationAvailable)
- {
- return;
- }
-
- $pluginName = $plugin->getPluginName();
-
- $path = PIWIK_INCLUDE_PATH . '/plugins/' . $pluginName .'/lang/%s.php';
-
- $defaultLangPath = sprintf($path, $langCode);
- $defaultEnglishLangPath = sprintf($path, 'en');
-
- $translations = array();
-
- if(file_exists($defaultLangPath))
- {
- require $defaultLangPath;
- }
- elseif(file_exists($defaultEnglishLangPath))
- {
- require $defaultEnglishLangPath;
- }
- else
- {
- throw new Exception("Language file not found for the plugin '$pluginName'.");
- }
- Piwik_Translate::getInstance()->mergeTranslationArray($translations);
- }
-
- /**
- * Return names of installed plugins
- *
- * @return array
- */
- public function getInstalledPluginsName()
- {
- $pluginNames = Piwik_Config::getInstance()->PluginsInstalled['PluginsInstalled'];
- return $pluginNames;
- }
-
- /**
- * Returns names of plugins that should be loaded, but cannot be since their
- * files cannot be found.
- *
- * @return array
- */
- public function getMissingPlugins()
- {
- $missingPlugins = array();
- if (isset(Piwik_Config::getInstance()->Plugins['Plugins']))
- {
- $plugins = Piwik_Config::getInstance()->Plugins['Plugins'];
- foreach ($plugins as $pluginName)
- {
- // if a plugin is listed in the config, but is not loaded, it does not exist in the folder
- if (!Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginName))
- {
- $missingPlugins[] = $pluginName;
- }
- }
- }
- return $missingPlugins;
- }
-
- /**
- * Install a plugin, if necessary
- *
- * @param Piwik_Plugin $plugin
- */
- private function installPluginIfNecessary( Piwik_Plugin $plugin )
- {
- $pluginName = $plugin->getPluginName();
-
- $saveConfig = false;
-
- // is the plugin already installed or is it the first time we activate it?
- $pluginsInstalled = $this->getInstalledPluginsName();
- if(!in_array($pluginName, $pluginsInstalled))
- {
- $this->installPlugin($plugin);
- $pluginsInstalled[] = $pluginName;
- $this->updatePluginsInstalledConfig($pluginsInstalled);
- $saveConfig = true;
- }
-
- $information = $plugin->getInformation();
-
- // if the plugin is to be loaded during the statistics logging
- if(isset($information['TrackerPlugin'])
- && $information['TrackerPlugin'] === true)
- {
- $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'];
- if(is_null($pluginsTracker))
- {
- $pluginsTracker = array();
- }
- if(!in_array($pluginName, $pluginsTracker))
- {
- $pluginsTracker[] = $pluginName;
- $this->updatePluginsTrackerConfig($pluginsTracker);
- $saveConfig = true;
- }
- }
-
- if($saveConfig)
- {
- Piwik_Config::getInstance()->forceSave();
- }
- }
+ /**
+ * @var Event_Dispatcher
+ */
+ public $dispatcher;
+
+ protected $pluginsToLoad = array();
+
+ protected $doLoadPlugins = true;
+ protected $loadedPlugins = array();
+
+ protected $doLoadAlwaysActivatedPlugins = true;
+ protected $pluginToAlwaysActivate = array(
+ 'CoreHome',
+ 'CoreUpdater',
+ 'CoreAdminHome',
+ 'CorePluginsAdmin',
+ 'Installation',
+ 'SitesManager',
+ 'UsersManager',
+ 'API',
+ 'Proxy',
+ 'LanguagesManager',
+ );
+
+ static private $instance = null;
+
+ /**
+ * Returns the singleton Piwik_PluginsManager
+ *
+ * @return Piwik_PluginsManager
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ private function __construct()
+ {
+ $this->dispatcher = Event_Dispatcher::getInstance();
+ }
+
+ /**
+ * Update Plugins config
+ *
+ * @param array $plugins Plugins
+ */
+ private function updatePluginsConfig($plugins)
+ {
+ $section = Piwik_Config::getInstance()->Plugins;
+ $section['Plugins'] = $plugins;
+ Piwik_Config::getInstance()->Plugins = $section;
+ }
+
+ /**
+ * Update Plugins_Tracker config
+ *
+ * @param array $plugins Plugins
+ */
+ private function updatePluginsTrackerConfig($plugins)
+ {
+ $section = Piwik_Config::getInstance()->Plugins_Tracker;
+ $section['Plugins_Tracker'] = $plugins;
+ Piwik_Config::getInstance()->Plugins_Tracker = $section;
+ }
+
+ /**
+ * Update PluginsInstalled config
+ *
+ * @param array $plugins Plugins
+ */
+ private function updatePluginsInstalledConfig($plugins)
+ {
+ $section = Piwik_Config::getInstance()->PluginsInstalled;
+ $section['PluginsInstalled'] = $plugins;
+ Piwik_Config::getInstance()->PluginsInstalled = $section;
+ }
+
+ /**
+ * Returns true if plugin is always activated
+ *
+ * @param string $name Name of plugin
+ * @return bool
+ */
+ public function isPluginAlwaysActivated($name)
+ {
+ return in_array($name, $this->pluginToAlwaysActivate);
+ }
+
+ /**
+ * Returns true if plugin has been activated
+ *
+ * @param string $name Name of plugin
+ * @return bool
+ */
+ public function isPluginActivated($name)
+ {
+ return in_array($name, $this->pluginsToLoad)
+ || $this->isPluginAlwaysActivated($name);
+ }
+
+ /**
+ * Returns true if plugin is loaded (in memory)
+ *
+ * @param string $name Name of plugin
+ * @return bool
+ */
+ public function isPluginLoaded($name)
+ {
+ return isset($this->loadedPlugins[$name]);
+ }
+
+ /**
+ * Reads the directories inside the plugins/ directory and returns their names in an array
+ *
+ * @return array
+ */
+ public function readPluginsDirectory()
+ {
+ $pluginsName = _glob(PIWIK_INCLUDE_PATH . '/plugins/*', GLOB_ONLYDIR);
+ $result = array();
+ if ($pluginsName != false) {
+ foreach ($pluginsName as $path) {
+ $name = basename($path);
+ if (file_exists($path . '/' . $name . '.php')) // only add folder if a Plugin/Plugin.php file exists
+ {
+ $result[] = $name;
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Deactivate plugin
+ *
+ * @param string $pluginName Name of plugin
+ */
+ public function deactivatePlugin($pluginName)
+ {
+ $plugins = $this->pluginsToLoad;
+ $key = array_search($pluginName, $plugins);
+
+ $plugin = $this->loadPlugin($pluginName);
+ if ($plugin !== null) {
+ $plugin->deactivate();
+ }
+
+ if ($key !== false) {
+ unset($plugins[$key]);
+ }
+ $this->updatePluginsConfig($plugins);
+
+ $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'];
+ if (!is_null($pluginsTracker)) {
+ $key = array_search($pluginName, $pluginsTracker);
+ if ($key !== false) {
+ unset($pluginsTracker[$key]);
+ $this->updatePluginsTrackerConfig($pluginsTracker);
+ }
+ }
+
+ // Delete merged js/css files to force regenerations to exclude the deactivated plugin
+ Piwik_Config::getInstance()->forceSave();
+ Piwik::deleteAllCacheOnUpdate();
+ }
+
+ /**
+ * Install loaded plugins
+ */
+ public function installLoadedPlugins()
+ {
+ foreach ($this->getLoadedPlugins() as $plugin) {
+ try {
+ $this->installPluginIfNecessary($plugin);
+ } catch (Exception $e) {
+ echo $e->getMessage();
+ }
+ }
+ }
+
+ /**
+ * Activate the specified plugin and install (if needed)
+ *
+ * @param string $pluginName Name of plugin
+ * @throws Exception
+ */
+ public function activatePlugin($pluginName)
+ {
+ $plugins = Piwik_Config::getInstance()->Plugins['Plugins'];
+ if (in_array($pluginName, $plugins)) {
+ throw new Exception("Plugin '$pluginName' already activated.");
+ }
+
+ $existingPlugins = $this->readPluginsDirectory();
+ if (array_search($pluginName, $existingPlugins) === false) {
+ // ToDo: This fails in tracker-mode. We should log this however.
+ //Piwik::log(sprintf("Unable to find the plugin '%s' in activatePlugin.", $pluginName));
+ return;
+ }
+
+ $plugin = $this->loadPlugin($pluginName);
+ if ($plugin === null) {
+ return;
+ }
+
+ $this->installPluginIfNecessary($plugin);
+
+ $plugin->activate();
+
+ // we add the plugin to the list of activated plugins
+ if (!in_array($pluginName, $plugins)) {
+ $plugins[] = $pluginName;
+ } else {
+ // clean up if we find a dupe
+ $plugins = array_unique($plugins);
+ }
+
+ // the config file will automatically be saved with the new plugin
+ $this->updatePluginsConfig($plugins);
+ Piwik_Config::getInstance()->forceSave();
+
+ // Delete merged js/css files to force regenerations to include the activated plugin
+ Piwik::deleteAllCacheOnUpdate();
+ }
+
+ /**
+ * Load the specified plugins
+ *
+ * @param array $pluginsToLoad Array of plugins to load
+ */
+ public function loadPlugins(array $pluginsToLoad)
+ {
+ // case no plugins to load
+ if (is_null($pluginsToLoad)) {
+ $pluginsToLoad = array();
+ }
+ $this->pluginsToLoad = $pluginsToLoad;
+ $this->reloadPlugins();
+ }
+
+ /**
+ * Disable plugin loading
+ */
+ public function doNotLoadPlugins()
+ {
+ $this->doLoadPlugins = false;
+ }
+
+ /**
+ * Disable loading of "always activated" plugins
+ */
+ public function doNotLoadAlwaysActivatedPlugins()
+ {
+ $this->doLoadAlwaysActivatedPlugins = false;
+ }
+
+ /**
+ * Load translations for loaded plugins
+ *
+ * @param bool|string $language Optional language code
+ */
+ public function loadPluginTranslations($language = false)
+ {
+ if (empty($language)) {
+ $language = Piwik_Translate::getInstance()->getLanguageToLoad();
+ }
+ $plugins = $this->getLoadedPlugins();
+
+ foreach ($plugins as $plugin) {
+ $this->loadTranslation($plugin, $language);
+ }
+ }
+
+ /**
+ * Execute postLoad() hook for loaded plugins
+ *
+ * @see Piwik_Plugin::postLoad()
+ */
+ public function postLoadPlugins()
+ {
+ $plugins = $this->getLoadedPlugins();
+ foreach ($plugins as $plugin) {
+ $plugin->postLoad();
+ }
+ }
+
+ /**
+ * Returns an array containing the plugins class names (eg. 'Piwik_UserCountry' and NOT 'UserCountry')
+ *
+ * @return array
+ */
+ public function getLoadedPluginsName()
+ {
+ return array_map('get_class', $this->getLoadedPlugins());
+ }
+
+ /**
+ * Returns an array of key,value with the following format: array(
+ * 'UserCountry' => Piwik_Plugin $pluginObject,
+ * 'UserSettings' => Piwik_Plugin $pluginObject,
+ * );
+ *
+ * @return array
+ */
+ public function getLoadedPlugins()
+ {
+ return $this->loadedPlugins;
+ }
+
+ /**
+ * Returns the given Piwik_Plugin object
+ *
+ * @param string $name
+ * @throws Exception
+ * @return array
+ */
+ public function getLoadedPlugin($name)
+ {
+ if (!isset($this->loadedPlugins[$name])) {
+ throw new Exception("The plugin '$name' has not been loaded.");
+ }
+ return $this->loadedPlugins[$name];
+ }
+
+ /**
+ * Load the plugins classes installed.
+ * Register the observers for every plugin.
+ */
+ private function reloadPlugins()
+ {
+ $this->pluginsToLoad = array_unique($this->pluginsToLoad);
+
+ if ($this->doLoadAlwaysActivatedPlugins) {
+ $this->pluginsToLoad = array_merge($this->pluginsToLoad, $this->pluginToAlwaysActivate);
+ }
+
+ foreach ($this->pluginsToLoad as $pluginName) {
+ if (!$this->isPluginLoaded($pluginName)) {
+ $newPlugin = $this->loadPlugin($pluginName);
+ if ($newPlugin === null) {
+ continue;
+ }
+
+ if ($this->doLoadPlugins
+ && $this->isPluginActivated($pluginName)
+ ) {
+ $this->addPluginObservers($newPlugin);
+ }
+ }
+ }
+ }
+
+ /**
+ * Loads the plugin filename and instantiates the plugin with the given name, eg. UserCountry
+ * Do NOT give the class name ie. Piwik_UserCountry, but give the plugin name ie. UserCountry
+ *
+ * @param string $pluginName
+ * @throws Exception
+ * @return Piwik_Plugin|null
+ */
+ public function loadPlugin($pluginName)
+ {
+ if (isset($this->loadedPlugins[$pluginName])) {
+ return $this->loadedPlugins[$pluginName];
+ }
+ $pluginFileName = sprintf("%s/%s.php", $pluginName, $pluginName);
+ $pluginClassName = sprintf('Piwik_%s', $pluginName);
+
+ if (!Piwik_Common::isValidFilename($pluginName)) {
+ throw new Exception(sprintf("The plugin filename '%s' is not a valid filename", $pluginFileName));
+ }
+
+ $path = PIWIK_INCLUDE_PATH . '/plugins/' . $pluginFileName;
+
+ if (!file_exists($path)) {
+ // ToDo: We should log this - but this will crash in Tracker mode since core/Piwik is not loaded
+ //Piwik::log(sprintf("Unable to load plugin '%s' because '%s' couldn't be found.", $pluginName, $path));
+ throw new Exception(sprintf("Unable to load plugin '%s' because '%s' couldn't be found.", $pluginName, $path));
+ }
+
+ // Don't remove this.
+ // Our autoloader can't find plugins/PluginName/PluginName.php
+ require_once $path; // prefixed by PIWIK_INCLUDE_PATH
+
+ if (!class_exists($pluginClassName, false)) {
+ throw new Exception("The class $pluginClassName couldn't be found in the file '$path'");
+ }
+ $newPlugin = new $pluginClassName();
+
+ if (!($newPlugin instanceof Piwik_Plugin)) {
+ throw new Exception("The plugin $pluginClassName in the file $path must inherit from Piwik_Plugin.");
+ }
+
+ $this->addLoadedPlugin($pluginName, $newPlugin);
+
+ return $newPlugin;
+ }
+
+ /**
+ * Unload plugin
+ *
+ * @param Piwik_Plugin $plugin
+ * @throws Exception
+ */
+ public function unloadPlugin($plugin)
+ {
+ if (!($plugin instanceof Piwik_Plugin)) {
+ $oPlugin = $this->loadPlugin($plugin);
+ if ($oPlugin === null) {
+ unset($this->loadedPlugins[$plugin]);
+ return;
+ }
+
+ $plugin = $oPlugin;
+ }
+ $hooks = $plugin->getListHooksRegistered();
+
+ foreach ($hooks as $hookName => $methodToCall) {
+ $success = $this->dispatcher->removeObserver(array($plugin, $methodToCall), $hookName);
+ if ($success !== true) {
+ throw new Exception("Error unloading plugin = " . $plugin->getPluginName() . ", method = $methodToCall, hook = $hookName ");
+ }
+ }
+ unset($this->loadedPlugins[$plugin->getPluginName()]);
+ }
+
+ /**
+ * Unload all loaded plugins
+ */
+ public function unloadPlugins()
+ {
+ $pluginsLoaded = $this->getLoadedPlugins();
+ foreach ($pluginsLoaded as $plugin) {
+ $this->unloadPlugin($plugin);
+ }
+ }
+
+ /**
+ * Install loaded plugins
+ */
+ private function installPlugins()
+ {
+ foreach ($this->getLoadedPlugins() as $plugin) {
+ $this->installPlugin($plugin);
+ }
+ }
+
+ /**
+ * Install a specific plugin
+ *
+ * @param Piwik_Plugin $plugin
+ * @throws Piwik_PluginsManager_PluginException if installation fails
+ */
+ private function installPlugin(Piwik_Plugin $plugin)
+ {
+ try {
+ $plugin->install();
+ } catch (Exception $e) {
+ throw new Piwik_PluginsManager_PluginException($plugin->getPluginName(), $e->getMessage());
+ }
+ }
+
+
+ /**
+ * For the given plugin, add all the observers of this plugin.
+ *
+ * @param Piwik_Plugin $plugin
+ */
+ private function addPluginObservers(Piwik_Plugin $plugin)
+ {
+ $hooks = $plugin->getListHooksRegistered();
+
+ foreach ($hooks as $hookName => $methodToCall) {
+ $this->dispatcher->addObserver(array($plugin, $methodToCall), $hookName);
+ }
+ }
+
+ /**
+ * Add a plugin in the loaded plugins array
+ *
+ * @param string $pluginName plugin name without prefix (eg. 'UserCountry')
+ * @param Piwik_Plugin $newPlugin
+ */
+ private function addLoadedPlugin($pluginName, Piwik_Plugin $newPlugin)
+ {
+ $this->loadedPlugins[$pluginName] = $newPlugin;
+ }
+
+ /**
+ * Load translation
+ *
+ * @param Piwik_Plugin $plugin
+ * @param string $langCode
+ * @throws Exception
+ * @return void
+ */
+ private function loadTranslation($plugin, $langCode)
+ {
+ // we are in Tracker mode if Piwik_Loader is not (yet) loaded
+ if (!class_exists('Piwik_Loader', false)) {
+ return;
+ }
+
+ $infos = $plugin->getInformation();
+ if (!isset($infos['translationAvailable'])) {
+ $infos['translationAvailable'] = false;
+ }
+ $translationAvailable = $infos['translationAvailable'];
+
+ if (!$translationAvailable) {
+ return;
+ }
+
+ $pluginName = $plugin->getPluginName();
+
+ $path = PIWIK_INCLUDE_PATH . '/plugins/' . $pluginName . '/lang/%s.php';
+
+ $defaultLangPath = sprintf($path, $langCode);
+ $defaultEnglishLangPath = sprintf($path, 'en');
+
+ $translations = array();
+
+ if (file_exists($defaultLangPath)) {
+ require $defaultLangPath;
+ } elseif (file_exists($defaultEnglishLangPath)) {
+ require $defaultEnglishLangPath;
+ } else {
+ throw new Exception("Language file not found for the plugin '$pluginName'.");
+ }
+ Piwik_Translate::getInstance()->mergeTranslationArray($translations);
+ }
+
+ /**
+ * Return names of installed plugins
+ *
+ * @return array
+ */
+ public function getInstalledPluginsName()
+ {
+ $pluginNames = Piwik_Config::getInstance()->PluginsInstalled['PluginsInstalled'];
+ return $pluginNames;
+ }
+
+ /**
+ * Returns names of plugins that should be loaded, but cannot be since their
+ * files cannot be found.
+ *
+ * @return array
+ */
+ public function getMissingPlugins()
+ {
+ $missingPlugins = array();
+ if (isset(Piwik_Config::getInstance()->Plugins['Plugins'])) {
+ $plugins = Piwik_Config::getInstance()->Plugins['Plugins'];
+ foreach ($plugins as $pluginName) {
+ // if a plugin is listed in the config, but is not loaded, it does not exist in the folder
+ if (!Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginName)) {
+ $missingPlugins[] = $pluginName;
+ }
+ }
+ }
+ return $missingPlugins;
+ }
+
+ /**
+ * Install a plugin, if necessary
+ *
+ * @param Piwik_Plugin $plugin
+ */
+ private function installPluginIfNecessary(Piwik_Plugin $plugin)
+ {
+ $pluginName = $plugin->getPluginName();
+
+ $saveConfig = false;
+
+ // is the plugin already installed or is it the first time we activate it?
+ $pluginsInstalled = $this->getInstalledPluginsName();
+ if (!in_array($pluginName, $pluginsInstalled)) {
+ $this->installPlugin($plugin);
+ $pluginsInstalled[] = $pluginName;
+ $this->updatePluginsInstalledConfig($pluginsInstalled);
+ $saveConfig = true;
+ }
+
+ $information = $plugin->getInformation();
+
+ // if the plugin is to be loaded during the statistics logging
+ if (isset($information['TrackerPlugin'])
+ && $information['TrackerPlugin'] === true
+ ) {
+ $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'];
+ if (is_null($pluginsTracker)) {
+ $pluginsTracker = array();
+ }
+ if (!in_array($pluginName, $pluginsTracker)) {
+ $pluginsTracker[] = $pluginName;
+ $this->updatePluginsTrackerConfig($pluginsTracker);
+ $saveConfig = true;
+ }
+ }
+
+ if ($saveConfig) {
+ Piwik_Config::getInstance()->forceSave();
+ }
+ }
}
/**
@@ -698,41 +648,41 @@ class Piwik_PluginsManager
*/
class Piwik_PluginsManager_PluginException extends Exception
{
- function __construct($pluginName, $message)
- {
- parent::__construct("There was a problem installing the plugin ". $pluginName . ": " . $message. "
+ function __construct($pluginName, $message)
+ {
+ parent::__construct("There was a problem installing the plugin " . $pluginName . ": " . $message . "
If this plugin has already been installed, and if you want to hide this message</b>, you must add the following line under the
[PluginsInstalled]
entry in your config/config.ini.php file:
- PluginsInstalled[] = $pluginName" );
- }
+ PluginsInstalled[] = $pluginName");
+ }
}
/**
* Post an event to the dispatcher which will notice the observers
*
- * @param string $eventName The event name
- * @param mixed $object Object, array or string that the listeners can read and/or modify.
+ * @param string $eventName The event name
+ * @param mixed $object Object, array or string that the listeners can read and/or modify.
* Listeners can call $object =& $notification->getNotificationObject(); to fetch and then modify this variable.
- * @param array $info Additional array of data that can be used by the listeners, but not edited
- * @param bool $pending Should the notification be posted to plugins that register after the notification was sent?
+ * @param array $info Additional array of data that can be used by the listeners, but not edited
+ * @param bool $pending Should the notification be posted to plugins that register after the notification was sent?
* @return void
*/
-function Piwik_PostEvent( $eventName, &$object = null, $info = array(), $pending = false )
+function Piwik_PostEvent($eventName, &$object = null, $info = array(), $pending = false)
{
- $notification = new Piwik_Event_Notification($object, $eventName, $info);
- Piwik_PluginsManager::getInstance()->dispatcher->postNotification( $notification, $pending, $bubble = false );
+ $notification = new Piwik_Event_Notification($object, $eventName, $info);
+ Piwik_PluginsManager::getInstance()->dispatcher->postNotification($notification, $pending, $bubble = false);
}
/**
* Register an action to execute for a given event
*
- * @param string $hookName Name of event
- * @param function $function Callback hook
+ * @param string $hookName Name of event
+ * @param function $function Callback hook
*/
-function Piwik_AddAction( $hookName, $function )
+function Piwik_AddAction($hookName, $function)
{
- Piwik_PluginsManager::getInstance()->dispatcher->addObserver( $function, $hookName );
+ Piwik_PluginsManager::getInstance()->dispatcher->addObserver($function, $hookName);
}
/**
@@ -745,27 +695,29 @@ function Piwik_AddAction( $hookName, $function )
*/
class Piwik_Event_Notification extends Event_Notification
{
- static $showProfiler = false;
-
- /**
- * Use notification counter to profile runtime execution
- * time and memory usage.
- */
- function increaseNotificationCount(/* array($className|object, $method) */) {
- parent::increaseNotificationCount();
- if(self::$showProfiler && func_num_args() == 1)
- {
- $callback = func_get_arg(0);
- if(is_array($callback)) {
- $className = is_object($callback[0]) ? get_class($callback[0]) : $callback[0];
- $method = $callback[1];
-
- echo "after $className -> $method <br />";
- echo "-"; Piwik::printTimer();
- echo "<br />";
- echo "-"; Piwik::printMemoryLeak();
- echo "<br />";
- }
- }
- }
+ static $showProfiler = false;
+
+ /**
+ * Use notification counter to profile runtime execution
+ * time and memory usage.
+ */
+ function increaseNotificationCount( /* array($className|object, $method) */)
+ {
+ parent::increaseNotificationCount();
+ if (self::$showProfiler && func_num_args() == 1) {
+ $callback = func_get_arg(0);
+ if (is_array($callback)) {
+ $className = is_object($callback[0]) ? get_class($callback[0]) : $callback[0];
+ $method = $callback[1];
+
+ echo "after $className -> $method <br />";
+ echo "-";
+ Piwik::printTimer();
+ echo "<br />";
+ echo "-";
+ Piwik::printMemoryLeak();
+ echo "<br />";
+ }
+ }
+ }
}
diff --git a/core/ProxyHeaders.php b/core/ProxyHeaders.php
index 14e1a7a0f0..5eb05b5076 100644
--- a/core/ProxyHeaders.php
+++ b/core/ProxyHeaders.php
@@ -16,85 +16,78 @@
*/
class Piwik_ProxyHeaders
{
- /**
- * Get protocol information, with the exception of HTTPS
- *
- * @return string protocol information
- */
- public static function getProtocolInformation()
- {
- if(Piwik_Common::getRequestVar('clientProtocol', 'http', 'string') == 'https')
- {
- return 'https';
- }
+ /**
+ * Get protocol information, with the exception of HTTPS
+ *
+ * @return string protocol information
+ */
+ public static function getProtocolInformation()
+ {
+ if (Piwik_Common::getRequestVar('clientProtocol', 'http', 'string') == 'https') {
+ return 'https';
+ }
- if(isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)
- {
- return 'SERVER_PORT=443';
- }
+ if (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
+ return 'SERVER_PORT=443';
+ }
- if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https')
- {
- return 'X-Forwarded-Proto';
- }
+ if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') {
+ return 'X-Forwarded-Proto';
+ }
- if(isset($_SERVER['HTTP_X_FORWARDED_SCHEME']) && strtolower($_SERVER['HTTP_X_FORWARDED_SCHEME']) == 'https')
- {
- return 'X-Forwarded-Scheme';
- }
+ if (isset($_SERVER['HTTP_X_FORWARDED_SCHEME']) && strtolower($_SERVER['HTTP_X_FORWARDED_SCHEME']) == 'https') {
+ return 'X-Forwarded-Scheme';
+ }
- if(isset($_SERVER['HTTP_X_URL_SCHEME']) && strtolower($_SERVER['HTTP_X_URL_SCHEME']) == 'https')
- {
- return 'X-Url-Scheme';
- }
+ if (isset($_SERVER['HTTP_X_URL_SCHEME']) && strtolower($_SERVER['HTTP_X_URL_SCHEME']) == 'https') {
+ return 'X-Url-Scheme';
+ }
- return null;
- }
+ return null;
+ }
- /**
- * Get headers present in the HTTP request
- *
- * @param array $recognizedHeaders
- * @return array HTTP headers
- */
- private static function getHeaders($recognizedHeaders)
- {
- $headers = array();
+ /**
+ * Get headers present in the HTTP request
+ *
+ * @param array $recognizedHeaders
+ * @return array HTTP headers
+ */
+ private static function getHeaders($recognizedHeaders)
+ {
+ $headers = array();
- foreach($recognizedHeaders as $header)
- {
- if(isset($_SERVER[$header]))
- {
- $headers[] = $header;
- }
- }
+ foreach ($recognizedHeaders as $header) {
+ if (isset($_SERVER[$header])) {
+ $headers[] = $header;
+ }
+ }
- return $headers;
- }
+ return $headers;
+ }
- /**
- * Detect proxy client headers
- *
- * @return array Proxy client HTTP headers
- */
- public static function getProxyClientHeaders()
- {
- return self::getHeaders(array(
- 'HTTP_CF_CONNECTING_IP',
- 'HTTP_CLIENT_IP',
- 'HTTP_X_FORWARDED_FOR',
- ));
- }
+ /**
+ * Detect proxy client headers
+ *
+ * @return array Proxy client HTTP headers
+ */
+ public static function getProxyClientHeaders()
+ {
+ return self::getHeaders(array(
+ 'HTTP_CF_CONNECTING_IP',
+ 'HTTP_CLIENT_IP',
+ 'HTTP_X_FORWARDED_FOR',
+ ));
+ }
- /**
- * Detect proxy host headers
- *
- * @return array Proxy host HTTP headers
- */
- public static function getProxyHostHeaders()
- {
- return self::getHeaders(array(
- 'HTTP_X_FORWARDED_HOST',
- ));
- }
+ /**
+ * Detect proxy host headers
+ *
+ * @return array Proxy host HTTP headers
+ */
+ public static function getProxyHostHeaders()
+ {
+ return self::getHeaders(array(
+ 'HTTP_X_FORWARDED_HOST',
+ ));
+ }
}
diff --git a/core/QuickForm2.php b/core/QuickForm2.php
index 63793c235e..a41f2e5eb9 100644
--- a/core/QuickForm2.php
+++ b/core/QuickForm2.php
@@ -1,135 +1,128 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Parent class for forms to be included in Smarty
- *
+ *
* For an example, @see Piwik_Login_FormLogin
- *
+ *
* @package Piwik
- * @see HTML_QuickForm2, libs/HTML/QuickForm2.php
+ * @see HTML_QuickForm2, libs/HTML/QuickForm2.php
* @link http://pear.php.net/package/HTML_QuickForm2/
*/
abstract class Piwik_QuickForm2 extends HTML_QuickForm2
{
- protected $a_formElements = array();
-
- function __construct( $id, $method = 'post', $attributes = null, $trackSubmit = false)
- {
- if(!isset($attributes['action']))
- {
- $attributes['action'] = Piwik_Url::getCurrentQueryString();
- }
- if(!isset($attributes['name']))
- {
- $attributes['name'] = $id;
- }
- parent::__construct($id, $method, $attributes, $trackSubmit);
+ protected $a_formElements = array();
- $this->init();
- }
+ function __construct($id, $method = 'post', $attributes = null, $trackSubmit = false)
+ {
+ if (!isset($attributes['action'])) {
+ $attributes['action'] = Piwik_Url::getCurrentQueryString();
+ }
+ if (!isset($attributes['name'])) {
+ $attributes['name'] = $id;
+ }
+ parent::__construct($id, $method, $attributes, $trackSubmit);
- /**
- * Class specific initialization
- */
- abstract function init();
+ $this->init();
+ }
- /**
- * The elements in this form
- *
- * @return array Element names
- */
- public function getElementList()
- {
- return $this->a_formElements;
- }
+ /**
+ * Class specific initialization
+ */
+ abstract function init();
- /**
- * Wrapper around HTML_QuickForm2_Container's addElement()
- *
- * @param string|HTML_QuickForm2_Node Either type name (treated
- * case-insensitively) or an element instance
- * @param mixed Element name
- * @param mixed Element attributes
- * @param array Element-specific data
- * @return HTML_QuickForm2_Node Added element
- * @throws HTML_QuickForm2_InvalidArgumentException
- * @throws HTML_QuickForm2_NotFoundException
- */
+ /**
+ * The elements in this form
+ *
+ * @return array Element names
+ */
+ public function getElementList()
+ {
+ return $this->a_formElements;
+ }
+
+ /**
+ * Wrapper around HTML_QuickForm2_Container's addElement()
+ *
+ * @param string|HTML_QuickForm2_Node Either type name (treated
+ * case-insensitively) or an element instance
+ * @param mixed Element name
+ * @param mixed Element attributes
+ * @param array Element-specific data
+ * @return HTML_QuickForm2_Node Added element
+ * @throws HTML_QuickForm2_InvalidArgumentException
+ * @throws HTML_QuickForm2_NotFoundException
+ */
public function addElement($elementOrType, $name = null, $attributes = null,
array $data = array())
- {
- if($name != 'submit')
- {
- $this->a_formElements[] = $name;
- }
+ {
+ if ($name != 'submit') {
+ $this->a_formElements[] = $name;
+ }
+
+ return parent::addElement($elementOrType, $name, $attributes, $data);
+ }
+
+ function setChecked($nameElement)
+ {
+ foreach ($this->_elements as $key => $value) {
+ if ($value->_attributes['name'] == $nameElement) {
+ $this->_elements[$key]->_attributes['checked'] = 'checked';
+ }
+ }
+ }
+
+ function setSelected($nameElement, $value)
+ {
+ foreach ($this->_elements as $key => $value) {
+ if ($value->_attributes['name'] == $nameElement) {
+ $this->_elements[$key]->_attributes['selected'] = 'selected';
+ }
+ }
+ }
+
+ /**
+ * Ported from HTML_QuickForm to minimize changes to Controllers
+ *
+ * @param string $elementName
+ * @return mixed
+ */
+ function getSubmitValue($elementName)
+ {
+ $value = $this->getValue();
+ return isset($value[$elementName]) ? $value[$elementName] : null;
+ }
+
+ /**
+ * Returns the rendered form as an array.
+ *
+ * @param bool $groupErrors Whether to group errors together or not.
+ * @return array
+ */
+ public function getFormData($groupErrors = true)
+ {
+ static $registered = false;
+ if (!$registered) {
+ HTML_QuickForm2_Renderer::register('smarty', 'HTML_QuickForm2_Renderer_Smarty');
+ $registered = true;
+ }
- return parent::addElement($elementOrType, $name, $attributes, $data);
- }
-
- function setChecked( $nameElement )
- {
- foreach( $this->_elements as $key => $value)
- {
- if($value->_attributes['name'] == $nameElement)
- {
- $this->_elements[$key]->_attributes['checked'] = 'checked';
- }
- }
- }
- function setSelected( $nameElement, $value )
- {
- foreach( $this->_elements as $key => $value)
- {
- if($value->_attributes['name'] == $nameElement)
- {
- $this->_elements[$key]->_attributes['selected'] = 'selected';
- }
- }
- }
+ // Create the renderer object
+ $renderer = HTML_QuickForm2_Renderer::factory('smarty');
+ $renderer->setOption('group_errors', $groupErrors);
- /**
- * Ported from HTML_QuickForm to minimize changes to Controllers
- *
- * @param string $elementName
- * @return mixed
- */
- function getSubmitValue($elementName)
- {
- $value = $this->getValue();
- return isset($value[$elementName]) ? $value[$elementName] : null;
- }
-
- /**
- * Returns the rendered form as an array.
- *
- * @param bool $groupErrors Whether to group errors together or not.
- * @return array
- */
- public function getFormData( $groupErrors = true )
- {
- static $registered = false;
- if(!$registered)
- {
- HTML_QuickForm2_Renderer::register('smarty', 'HTML_QuickForm2_Renderer_Smarty');
- $registered = true;
- }
-
- // Create the renderer object
- $renderer = HTML_QuickForm2_Renderer::factory('smarty');
- $renderer->setOption('group_errors', $groupErrors);
+ // build the HTML for the form
+ $this->render($renderer);
- // build the HTML for the form
- $this->render($renderer);
-
- return $renderer->toArray();
- }
+ return $renderer->toArray();
+ }
}
diff --git a/core/RankingQuery.php b/core/RankingQuery.php
index 2c1fe13c12..147f58e1bf 100644
--- a/core/RankingQuery.php
+++ b/core/RankingQuery.php
@@ -10,324 +10,297 @@
*/
/**
- * The ranking query class wraps an arbitrary SQL query with more SQL that limits
- * the number of results while grouping the rest to "Others" and allows for some
+ * The ranking query class wraps an arbitrary SQL query with more SQL that limits
+ * the number of results while grouping the rest to "Others" and allows for some
* more fancy things that can be configured via method calls of this class. The
* advanced use cases are explained in the doc comments of the methods.
- *
+ *
* The general use case looks like this:
- *
+ *
* // limit to 500 rows + "Others"
* $rankingQuery = new Piwik_RankingQuery(500);
- *
+ *
* // idaction_url will be "Others" in the row that contains the aggregated rest
* $rankingQuery->addLabelColumn('idaction_url');
- *
+ *
* // the actual query. it's important to sort it before the limit is applied
* $sql = 'SELECT idaction_url, COUNT(*) AS nb_hits
* FROM log_link_visit_action
* GROUP BY idaction_url
* ORDER BY nb_hits DESC';
- *
+ *
* // execute the query
* $rankingQuery->execute($sql);
- *
- *
+ *
+ *
* For more examples, see RankingQueryTest.php
- *
- *
+ *
+ *
* @package Piwik
*/
class Piwik_RankingQuery
{
-
- /**
- * Contains the labels of the inner query.
- * Format: "label" => true (to make sure labels don't appear twice)
- * @var array
- */
- private $labelColumns = array();
-
- /**
- * The columns of the inner query that are not labels
- * Format: "label" => "aggregation function" or false for no aggregation
- * @var array
- */
- private $additionalColumns = array();
-
- /**
- * The limit for each group
- * @var int
- */
- private $limit = 5;
-
- /**
- * The name of the columns that marks rows to be excluded from the limit
- * @var string
- */
- private $columnToMarkExcludedRows = false;
-
- /**
- * The column that is used to partition the result
- * @var bool|string
- */
- private $partitionColumn = false;
-
- /**
- * The possible values for the column $this->partitionColumn
- * @var array
- */
- private $partitionColumnValues = array();
-
- /**
- * The value to use in the label of the 'Others' row.
- * @var string
- */
- private $othersLabelValue = 'Others';
-
- /**
- * The constructor.
- * Can be used as a shortcut for setLimit()
- */
- public function __construct($limit = false)
- {
- if ($limit !== false)
- {
- $this->setLimit($limit);
- }
- }
-
- /**
- * Set the limit after which everything is grouped to "Others"
- *
- * @param $limit int
- */
- public function setLimit($limit)
- {
- $this->limit = $limit;
- }
-
- /**
- * Set the value to use for the label in the 'Others' row.
- *
- * @param $value string
- */
- public function setOthersLabel($value)
- {
- $this->othersLabelValue = $value;
- }
-
- /**
- * Add a label column.
- * Labels are the columns that are replaced with "Others" after the limit.
- *
- * @param $labelColumn string|array
- */
- public function addLabelColumn($labelColumn)
- {
- if (is_array($labelColumn))
- {
- foreach ($labelColumn as $label)
- {
- $this->addLabelColumn($label);
- }
- return;
- }
- $this->labelColumns[$labelColumn] = true;
- }
-
- /**
- * Add a column that has be added to the outer queries.
- *
- * @param $column
- * @param string|bool $aggregationFunction string
- * If set, this function is used to aggregate the values of "Others"
- */
- public function addColumn($column, $aggregationFunction=false)
- {
- if (is_array($column))
- {
- foreach ($column as $c)
- {
- $this->addColumn($c, $aggregationFunction);
- }
- return;
- }
- $this->additionalColumns[$column] = $aggregationFunction;
- }
-
- /**
- * The inner query can have a column that marks the rows that shall be excluded from limiting.
- * If the column contains 0, rows are handled as usual. For values greater than 0, separate
- * groups are made. If this method is used, generate() returns both the regular result and
- * the excluded columns separately.
- *
- * @param $column string name of the column
- * @throws Exception when method is used more than once
- */
- public function setColumnToMarkExcludedRows($column)
- {
- if ($this->columnToMarkExcludedRows !== false)
- {
- throw new Exception("setColumnToMarkExcludedRows can only be used once");
- }
-
- $this->columnToMarkExcludedRows = $column;
- $this->addColumn($this->columnToMarkExcludedRows);
- }
-
- /**
- * This method can be used to get multiple groups in one go. For example, one might query
- * the top following pages, outlinks and downloads in one go by using log_action.type as
- * the partition column and [TYPE_ACTION_URL, TYPE_OUTLINK, TYPE_DOWNLOAD] as the possible
- * values.
- * When this method has been used, generate() returns as array that contains one array
- * per group of data.
- *
- * @param $partitionColumn string
- * @param $possibleValues array of integers
- * @throws Exception when method is used more than once
- */
- public function partitionResultIntoMultipleGroups($partitionColumn, $possibleValues)
- {
- if ($this->partitionColumn !== false)
- {
- throw new Exception("partitionResultIntoMultipleGroups can only be used once");
- }
-
- $this->partitionColumn = $partitionColumn;
- $this->partitionColumnValues = $possibleValues;
- $this->addColumn($partitionColumn);
- }
-
- /**
- * Execute the query.
- * The object has to be configured first using the other methods.
- *
- * @param $innerQuery string The "payload" query. The result has be sorted as desired.
- * @param $bind array Bindings for the inner query.
- * @return array The format depends on which methods have been used
- * to configure the ranking query
- */
- public function execute($innerQuery, $bind=array())
- {
- $query = $this->generateQuery($innerQuery);
- $data = Piwik_FetchAll($query, $bind);
-
- if ($this->columnToMarkExcludedRows !== false)
- {
- // split the result into the regular result and the rows with special treatment
- $excludedFromLimit = array();
- $result = array();
- foreach ($data as &$row)
- {
- if ($row[$this->columnToMarkExcludedRows] != 0)
- {
- $excludedFromLimit[] = $row;
- }
- else
- {
- $result[] = $row;
- }
- }
- $data = array(
- 'result' => &$result,
- 'excludedFromLimit' => &$excludedFromLimit
- );
- }
-
- if ($this->partitionColumn !== false)
- {
- if ($this->columnToMarkExcludedRows !== false)
- {
- $data['result'] = $this->splitPartitions($data['result']);
- }
- else
- {
- $data = $this->splitPartitions($data);
- }
- }
-
- return $data;
- }
-
- private function splitPartitions(&$data)
- {
- $result = array();
- foreach ($data as &$row)
- {
- $partition = $row[$this->partitionColumn];
- if (!isset($result[$partition]))
- {
- $result[$partition] = array();
- }
- $result[$partition][] = &$row;
- }
- return $result;
- }
-
- /**
- * Generate the SQL code that does the magic.
- * If you want to get the result, use execute() instead. If you're interested in
- * the generated SQL code (e.g. for debugging), use this method.
- *
- * @param $innerQuery string SQL of the actual query
- * @return string entire ranking query SQL
- */
- public function generateQuery($innerQuery)
- {
- // +1 to include "Others"
- $limit = $this->limit + 1;
- $counterExpression = $this->getCounterExpression($limit);
-
- // generate select clauses for label columns
- $labelColumnsString = '`'.implode('`, `', array_keys($this->labelColumns)).'`';
- $labelColumnsOthersSwitch = array();
- foreach ($this->labelColumns as $column => $true)
- {
- $labelColumnsOthersSwitch[] = "
+
+ /**
+ * Contains the labels of the inner query.
+ * Format: "label" => true (to make sure labels don't appear twice)
+ * @var array
+ */
+ private $labelColumns = array();
+
+ /**
+ * The columns of the inner query that are not labels
+ * Format: "label" => "aggregation function" or false for no aggregation
+ * @var array
+ */
+ private $additionalColumns = array();
+
+ /**
+ * The limit for each group
+ * @var int
+ */
+ private $limit = 5;
+
+ /**
+ * The name of the columns that marks rows to be excluded from the limit
+ * @var string
+ */
+ private $columnToMarkExcludedRows = false;
+
+ /**
+ * The column that is used to partition the result
+ * @var bool|string
+ */
+ private $partitionColumn = false;
+
+ /**
+ * The possible values for the column $this->partitionColumn
+ * @var array
+ */
+ private $partitionColumnValues = array();
+
+ /**
+ * The value to use in the label of the 'Others' row.
+ * @var string
+ */
+ private $othersLabelValue = 'Others';
+
+ /**
+ * The constructor.
+ * Can be used as a shortcut for setLimit()
+ */
+ public function __construct($limit = false)
+ {
+ if ($limit !== false) {
+ $this->setLimit($limit);
+ }
+ }
+
+ /**
+ * Set the limit after which everything is grouped to "Others"
+ *
+ * @param $limit int
+ */
+ public function setLimit($limit)
+ {
+ $this->limit = $limit;
+ }
+
+ /**
+ * Set the value to use for the label in the 'Others' row.
+ *
+ * @param $value string
+ */
+ public function setOthersLabel($value)
+ {
+ $this->othersLabelValue = $value;
+ }
+
+ /**
+ * Add a label column.
+ * Labels are the columns that are replaced with "Others" after the limit.
+ *
+ * @param $labelColumn string|array
+ */
+ public function addLabelColumn($labelColumn)
+ {
+ if (is_array($labelColumn)) {
+ foreach ($labelColumn as $label) {
+ $this->addLabelColumn($label);
+ }
+ return;
+ }
+ $this->labelColumns[$labelColumn] = true;
+ }
+
+ /**
+ * Add a column that has be added to the outer queries.
+ *
+ * @param $column
+ * @param string|bool $aggregationFunction string
+ * If set, this function is used to aggregate the values of "Others"
+ */
+ public function addColumn($column, $aggregationFunction = false)
+ {
+ if (is_array($column)) {
+ foreach ($column as $c) {
+ $this->addColumn($c, $aggregationFunction);
+ }
+ return;
+ }
+ $this->additionalColumns[$column] = $aggregationFunction;
+ }
+
+ /**
+ * The inner query can have a column that marks the rows that shall be excluded from limiting.
+ * If the column contains 0, rows are handled as usual. For values greater than 0, separate
+ * groups are made. If this method is used, generate() returns both the regular result and
+ * the excluded columns separately.
+ *
+ * @param $column string name of the column
+ * @throws Exception when method is used more than once
+ */
+ public function setColumnToMarkExcludedRows($column)
+ {
+ if ($this->columnToMarkExcludedRows !== false) {
+ throw new Exception("setColumnToMarkExcludedRows can only be used once");
+ }
+
+ $this->columnToMarkExcludedRows = $column;
+ $this->addColumn($this->columnToMarkExcludedRows);
+ }
+
+ /**
+ * This method can be used to get multiple groups in one go. For example, one might query
+ * the top following pages, outlinks and downloads in one go by using log_action.type as
+ * the partition column and [TYPE_ACTION_URL, TYPE_OUTLINK, TYPE_DOWNLOAD] as the possible
+ * values.
+ * When this method has been used, generate() returns as array that contains one array
+ * per group of data.
+ *
+ * @param $partitionColumn string
+ * @param $possibleValues array of integers
+ * @throws Exception when method is used more than once
+ */
+ public function partitionResultIntoMultipleGroups($partitionColumn, $possibleValues)
+ {
+ if ($this->partitionColumn !== false) {
+ throw new Exception("partitionResultIntoMultipleGroups can only be used once");
+ }
+
+ $this->partitionColumn = $partitionColumn;
+ $this->partitionColumnValues = $possibleValues;
+ $this->addColumn($partitionColumn);
+ }
+
+ /**
+ * Execute the query.
+ * The object has to be configured first using the other methods.
+ *
+ * @param $innerQuery string The "payload" query. The result has be sorted as desired.
+ * @param $bind array Bindings for the inner query.
+ * @return array The format depends on which methods have been used
+ * to configure the ranking query
+ */
+ public function execute($innerQuery, $bind = array())
+ {
+ $query = $this->generateQuery($innerQuery);
+ $data = Piwik_FetchAll($query, $bind);
+
+ if ($this->columnToMarkExcludedRows !== false) {
+ // split the result into the regular result and the rows with special treatment
+ $excludedFromLimit = array();
+ $result = array();
+ foreach ($data as &$row) {
+ if ($row[$this->columnToMarkExcludedRows] != 0) {
+ $excludedFromLimit[] = $row;
+ } else {
+ $result[] = $row;
+ }
+ }
+ $data = array(
+ 'result' => &$result,
+ 'excludedFromLimit' => &$excludedFromLimit
+ );
+ }
+
+ if ($this->partitionColumn !== false) {
+ if ($this->columnToMarkExcludedRows !== false) {
+ $data['result'] = $this->splitPartitions($data['result']);
+ } else {
+ $data = $this->splitPartitions($data);
+ }
+ }
+
+ return $data;
+ }
+
+ private function splitPartitions(&$data)
+ {
+ $result = array();
+ foreach ($data as &$row) {
+ $partition = $row[$this->partitionColumn];
+ if (!isset($result[$partition])) {
+ $result[$partition] = array();
+ }
+ $result[$partition][] = & $row;
+ }
+ return $result;
+ }
+
+ /**
+ * Generate the SQL code that does the magic.
+ * If you want to get the result, use execute() instead. If you're interested in
+ * the generated SQL code (e.g. for debugging), use this method.
+ *
+ * @param $innerQuery string SQL of the actual query
+ * @return string entire ranking query SQL
+ */
+ public function generateQuery($innerQuery)
+ {
+ // +1 to include "Others"
+ $limit = $this->limit + 1;
+ $counterExpression = $this->getCounterExpression($limit);
+
+ // generate select clauses for label columns
+ $labelColumnsString = '`' . implode('`, `', array_keys($this->labelColumns)) . '`';
+ $labelColumnsOthersSwitch = array();
+ foreach ($this->labelColumns as $column => $true) {
+ $labelColumnsOthersSwitch[] = "
CASE
- WHEN counter = $limit THEN '".$this->othersLabelValue."'
+ WHEN counter = $limit THEN '" . $this->othersLabelValue . "'
ELSE `$column`
END AS `$column`
";
- }
- $labelColumnsOthersSwitch = implode(', ', $labelColumnsOthersSwitch);
-
- // generate select clauses for additional columns
- $additionalColumnsString = '';
- $additionalColumnsAggregatedString = '';
- foreach ($this->additionalColumns as $additionalColumn => $aggregation)
- {
- $additionalColumnsString .= ', `'.$additionalColumn.'`';
- if ($aggregation !== false)
- {
- $additionalColumnsAggregatedString .= ', '.$aggregation.'(`'.$additionalColumn.'`) AS `'.$additionalColumn.'`';
- }
- else
- {
- $additionalColumnsAggregatedString .= ', `'.$additionalColumn.'`';
- }
-
- }
-
- // initialize the counters
- if ($this->partitionColumn !== false)
- {
- $initCounter = '';
- foreach ($this->partitionColumnValues as $value)
- {
- $initCounter .= '( SELECT @counter'.intval($value).':=0 ) initCounter'.intval($value).', ';
- }
- }
- else
- {
- $initCounter = '( SELECT @counter:=0 ) initCounter,';
- }
-
- // add a counter to the query
- // we rely on the sorting of the inner query
- $withCounter = "
+ }
+ $labelColumnsOthersSwitch = implode(', ', $labelColumnsOthersSwitch);
+
+ // generate select clauses for additional columns
+ $additionalColumnsString = '';
+ $additionalColumnsAggregatedString = '';
+ foreach ($this->additionalColumns as $additionalColumn => $aggregation) {
+ $additionalColumnsString .= ', `' . $additionalColumn . '`';
+ if ($aggregation !== false) {
+ $additionalColumnsAggregatedString .= ', ' . $aggregation . '(`' . $additionalColumn . '`) AS `' . $additionalColumn . '`';
+ } else {
+ $additionalColumnsAggregatedString .= ', `' . $additionalColumn . '`';
+ }
+
+ }
+
+ // initialize the counters
+ if ($this->partitionColumn !== false) {
+ $initCounter = '';
+ foreach ($this->partitionColumnValues as $value) {
+ $initCounter .= '( SELECT @counter' . intval($value) . ':=0 ) initCounter' . intval($value) . ', ';
+ }
+ } else {
+ $initCounter = '( SELECT @counter:=0 ) initCounter,';
+ }
+
+ // add a counter to the query
+ // we rely on the sorting of the inner query
+ $withCounter = "
SELECT
$labelColumnsString,
$counterExpression AS counter
@@ -336,61 +309,55 @@ class Piwik_RankingQuery
$initCounter
( $innerQuery ) actualQuery
";
-
- // group by the counter - this groups "Others" because the counter stops at $limit
- $groupBy = 'counter';
- if ($this->partitionColumn !== false)
- {
- $groupBy .= ', `'.$this->partitionColumn.'`';
- }
- $groupOthers = "
+
+ // group by the counter - this groups "Others" because the counter stops at $limit
+ $groupBy = 'counter';
+ if ($this->partitionColumn !== false) {
+ $groupBy .= ', `' . $this->partitionColumn . '`';
+ }
+ $groupOthers = "
SELECT
$labelColumnsOthersSwitch
$additionalColumnsAggregatedString
FROM ( $withCounter ) AS withCounter
GROUP BY $groupBy
";
- return $groupOthers;
- }
-
- private function getCounterExpression($limit)
- {
- $whens = array();
-
- if ($this->columnToMarkExcludedRows !== false)
- {
- // when a row has been specified that marks which records should be excluded
- // from limiting, we don't give those rows the normal counter but -1 times the
- // value they had before. this way, they have a separate number space (i.e. negative
- // integers).
- $whens[] = "WHEN {$this->columnToMarkExcludedRows} != 0 THEN -1 * {$this->columnToMarkExcludedRows}";
- }
-
- if ($this->partitionColumn !== false)
- {
- // partition: one counter per possible value
- foreach ($this->partitionColumnValues as $value)
- {
- $isValue = '`'.$this->partitionColumn.'` = '.intval($value);
- $counter = '@counter'.intval($value);
- $whens[] = "WHEN $isValue AND $counter = $limit THEN $limit";
- $whens[] = "WHEN $isValue THEN $counter:=$counter+1";
- }
- $whens[] = "ELSE 0";
- }
- else
- {
- // no partitioning: add a single counter
- $whens[] = "WHEN @counter = $limit THEN $limit";
- $whens[] = "ELSE @counter:=@counter+1";
- }
-
- return "
+ return $groupOthers;
+ }
+
+ private function getCounterExpression($limit)
+ {
+ $whens = array();
+
+ if ($this->columnToMarkExcludedRows !== false) {
+ // when a row has been specified that marks which records should be excluded
+ // from limiting, we don't give those rows the normal counter but -1 times the
+ // value they had before. this way, they have a separate number space (i.e. negative
+ // integers).
+ $whens[] = "WHEN {$this->columnToMarkExcludedRows} != 0 THEN -1 * {$this->columnToMarkExcludedRows}";
+ }
+
+ if ($this->partitionColumn !== false) {
+ // partition: one counter per possible value
+ foreach ($this->partitionColumnValues as $value) {
+ $isValue = '`' . $this->partitionColumn . '` = ' . intval($value);
+ $counter = '@counter' . intval($value);
+ $whens[] = "WHEN $isValue AND $counter = $limit THEN $limit";
+ $whens[] = "WHEN $isValue THEN $counter:=$counter+1";
+ }
+ $whens[] = "ELSE 0";
+ } else {
+ // no partitioning: add a single counter
+ $whens[] = "WHEN @counter = $limit THEN $limit";
+ $whens[] = "ELSE @counter:=@counter+1";
+ }
+
+ return "
CASE
- ".implode("
- ", $whens)."
+ " . implode("
+ ", $whens) . "
END
";
- }
+ }
}
diff --git a/core/ReportRenderer.php b/core/ReportRenderer.php
index 7a94305047..9b26b7f236 100644
--- a/core/ReportRenderer.php
+++ b/core/ReportRenderer.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -18,238 +18,235 @@
*/
abstract class Piwik_ReportRenderer
{
- const DEFAULT_REPORT_FONT = 'dejavusans';
- const REPORT_TEXT_COLOR = "68,68,68";
- const REPORT_TITLE_TEXT_COLOR = "126,115,99";
- const TABLE_HEADER_BG_COLOR = "228,226,215";
- const TABLE_HEADER_TEXT_COLOR = "37,87,146";
- const TABLE_CELL_BORDER_COLOR = "231,231,231";
- const TABLE_BG_COLOR = "249,250,250";
-
- const HTML_FORMAT = 'html';
- const PDF_FORMAT = 'pdf';
-
- static private $availableReportRenderers = array(
- self::PDF_FORMAT,
- self::HTML_FORMAT,
- );
-
- /**
- * Return the ReportRenderer associated to the renderer type $rendererType
- *
- * @throws exception If the renderer is unknown
- * @param string $rendererType
- * @return Piwik_ReportRenderer
- */
- static public function factory($rendererType)
- {
- $name = ucfirst(strtolower($rendererType));
- $className = 'Piwik_ReportRenderer_' . $name;
-
- try {
- Piwik_Loader::loadClass($className);
- return new $className;
- } catch(Exception $e) {
-
- @header('Content-Type: text/html; charset=utf-8');
-
- throw new Exception(
- Piwik_TranslateException(
- 'General_ExceptionInvalidReportRendererFormat',
- array($name, implode(', ', self::$availableReportRenderers))
- )
- );
- }
- }
-
- /**
- * Initialize locale settings.
- * If not called, locale settings defaults to 'en'
- *
- * @param string $locale
- */
- abstract public function setLocale($locale);
-
- /**
- * Save rendering to disk
- *
- * @param string $filename without path & without format extension
- * @return string path of file
- */
- abstract public function sendToDisk($filename);
-
- /**
- * Send rendering to browser with a 'download file' prompt
- *
- * @param string $filename without path & without format extension
- */
- abstract public function sendToBrowserDownload($filename);
-
- /**
- * Output rendering to browser
- *
- * @param string $filename without path & without format extension
- */
- abstract public function sendToBrowserInline($filename);
-
- /**
- * Get rendered report
- */
- abstract public function getRenderedReport();
-
- /**
- * Generate the first page.
- *
- * @param string $websiteName
- * @param string $prettyDate formatted date
- * @param string $description
- * @param array $reportMetadata metadata for all reports
- */
- abstract public function renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata);
-
- /**
- * Render the provided report.
- * Multiple calls to this method before calling outputRendering appends each report content.
- *
- * @param array $processedReport @see Piwik_API_API::getProcessedReport()
- */
- abstract public function renderReport($processedReport);
-
- /**
- * Append $extension to $filename
- *
- * @static
- * @param $filename
- * @param $extension
- * @return filename with extension
- */
- protected static function appendExtension($filename, $extension)
- {
- return $filename.".".$extension;
- }
-
- /**
- * Return $filename with temp directory and delete file
- *
- * @static
- * @param $filename
- * @return string path of file in temp directory
- */
- protected static function getOutputPath($filename)
- {
- $outputFilename = PIWIK_USER_PATH . '/tmp/assets/' . $filename;
- @chmod($outputFilename, 0600);
- @unlink($outputFilename);
- return $outputFilename;
- }
-
- protected static function writeFile($filename, $extension, $content)
- {
- $filename = self::appendExtension($filename, $extension);
- $outputFilename = self::getOutputPath($filename);
-
- $emailReport = @fopen($outputFilename, "w");
-
- if (!$emailReport) {
- throw new Exception ("The file : " . $outputFilename . " can not be opened in write mode.");
- }
-
- fwrite($emailReport, $content);
- fclose($emailReport);
-
- return $outputFilename;
- }
-
- protected static function sendToBrowser($filename, $extension, $contentType, $content)
- {
- $filename = Piwik_ReportRenderer::appendExtension($filename, $extension);
-
- Piwik::overrideCacheControlHeaders();
- header('Content-Description: File Transfer');
- header('Content-Type: ' . $contentType);
- header('Content-Disposition: attachment; filename="'.str_replace('"', '\'', basename($filename)).'";');
- header('Content-Length: '.strlen($content));
-
- echo $content;
- }
-
- protected static function inlineToBrowser($contentType, $content)
- {
- header('Content-Type: ' . $contentType);
- echo $content;
- }
-
- /**
- * Convert a dimension-less report to a multi-row two-column data table
- *
- * @static
- * @param $reportMetadata array
- * @param $report Piwik_DataTable
- * @param $reportColumns array
- * @return array Piwik_DataTable $report & array $columns
- */
- protected static function processTableFormat($reportMetadata, $report, $reportColumns)
- {
- $finalReport = $report;
- if(empty($reportMetadata['dimension']))
- {
- $simpleReportMetrics = $report->getFirstRow();
- if($simpleReportMetrics)
- {
- $finalReport = new Piwik_DataTable_Simple();
- foreach($simpleReportMetrics->getColumns() as $metricId => $metric)
- {
- $newRow = new Piwik_DataTable_Row();
- $newRow->addColumn("label", $reportColumns[$metricId]);
- $newRow->addColumn("value", $metric);
- $finalReport->addRow($newRow);
- }
- }
-
- $reportColumns = array(
- 'label' => Piwik_Translate('General_Name'),
- 'value' => Piwik_Translate('General_Value'),
- );
- }
-
- return array(
- $finalReport,
- $reportColumns,
- );
- }
-
- public static function getStaticGraph($reportMetadata, $width, $height, $evolution) {
-
- $imageGraphUrl = $reportMetadata['imageGraphUrl'];
-
- if($evolution && !empty($reportMetadata['imageGraphEvolutionUrl']))
- {
- $imageGraphUrl = $reportMetadata['imageGraphEvolutionUrl'];
- }
-
- $request = new Piwik_API_Request(
- $imageGraphUrl .
- '&outputType='.Piwik_ImageGraph_API::GRAPH_OUTPUT_PHP.
- '&format=original&serialize=0'.
- '&filter_truncate='.
- '&width='.$width.
- '&height='.$height
- );
-
- try {
- $imageGraph = $request->process();
-
- // Get image data as string
- ob_start();
- imagepng($imageGraph);
- $imageGraphData = ob_get_contents();
- ob_end_clean();
- imagedestroy($imageGraph);
-
- return $imageGraphData;
-
- } catch(Exception $e) {
- throw new Exception("ImageGraph API returned an error: ".$e->getMessage()."\n");
- }
- }
+ const DEFAULT_REPORT_FONT = 'dejavusans';
+ const REPORT_TEXT_COLOR = "68,68,68";
+ const REPORT_TITLE_TEXT_COLOR = "126,115,99";
+ const TABLE_HEADER_BG_COLOR = "228,226,215";
+ const TABLE_HEADER_TEXT_COLOR = "37,87,146";
+ const TABLE_CELL_BORDER_COLOR = "231,231,231";
+ const TABLE_BG_COLOR = "249,250,250";
+
+ const HTML_FORMAT = 'html';
+ const PDF_FORMAT = 'pdf';
+
+ static private $availableReportRenderers = array(
+ self::PDF_FORMAT,
+ self::HTML_FORMAT,
+ );
+
+ /**
+ * Return the ReportRenderer associated to the renderer type $rendererType
+ *
+ * @throws exception If the renderer is unknown
+ * @param string $rendererType
+ * @return Piwik_ReportRenderer
+ */
+ static public function factory($rendererType)
+ {
+ $name = ucfirst(strtolower($rendererType));
+ $className = 'Piwik_ReportRenderer_' . $name;
+
+ try {
+ Piwik_Loader::loadClass($className);
+ return new $className;
+ } catch (Exception $e) {
+
+ @header('Content-Type: text/html; charset=utf-8');
+
+ throw new Exception(
+ Piwik_TranslateException(
+ 'General_ExceptionInvalidReportRendererFormat',
+ array($name, implode(', ', self::$availableReportRenderers))
+ )
+ );
+ }
+ }
+
+ /**
+ * Initialize locale settings.
+ * If not called, locale settings defaults to 'en'
+ *
+ * @param string $locale
+ */
+ abstract public function setLocale($locale);
+
+ /**
+ * Save rendering to disk
+ *
+ * @param string $filename without path & without format extension
+ * @return string path of file
+ */
+ abstract public function sendToDisk($filename);
+
+ /**
+ * Send rendering to browser with a 'download file' prompt
+ *
+ * @param string $filename without path & without format extension
+ */
+ abstract public function sendToBrowserDownload($filename);
+
+ /**
+ * Output rendering to browser
+ *
+ * @param string $filename without path & without format extension
+ */
+ abstract public function sendToBrowserInline($filename);
+
+ /**
+ * Get rendered report
+ */
+ abstract public function getRenderedReport();
+
+ /**
+ * Generate the first page.
+ *
+ * @param string $websiteName
+ * @param string $prettyDate formatted date
+ * @param string $description
+ * @param array $reportMetadata metadata for all reports
+ */
+ abstract public function renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata);
+
+ /**
+ * Render the provided report.
+ * Multiple calls to this method before calling outputRendering appends each report content.
+ *
+ * @param array $processedReport @see Piwik_API_API::getProcessedReport()
+ */
+ abstract public function renderReport($processedReport);
+
+ /**
+ * Append $extension to $filename
+ *
+ * @static
+ * @param $filename
+ * @param $extension
+ * @return filename with extension
+ */
+ protected static function appendExtension($filename, $extension)
+ {
+ return $filename . "." . $extension;
+ }
+
+ /**
+ * Return $filename with temp directory and delete file
+ *
+ * @static
+ * @param $filename
+ * @return string path of file in temp directory
+ */
+ protected static function getOutputPath($filename)
+ {
+ $outputFilename = PIWIK_USER_PATH . '/tmp/assets/' . $filename;
+ @chmod($outputFilename, 0600);
+ @unlink($outputFilename);
+ return $outputFilename;
+ }
+
+ protected static function writeFile($filename, $extension, $content)
+ {
+ $filename = self::appendExtension($filename, $extension);
+ $outputFilename = self::getOutputPath($filename);
+
+ $emailReport = @fopen($outputFilename, "w");
+
+ if (!$emailReport) {
+ throw new Exception ("The file : " . $outputFilename . " can not be opened in write mode.");
+ }
+
+ fwrite($emailReport, $content);
+ fclose($emailReport);
+
+ return $outputFilename;
+ }
+
+ protected static function sendToBrowser($filename, $extension, $contentType, $content)
+ {
+ $filename = Piwik_ReportRenderer::appendExtension($filename, $extension);
+
+ Piwik::overrideCacheControlHeaders();
+ header('Content-Description: File Transfer');
+ header('Content-Type: ' . $contentType);
+ header('Content-Disposition: attachment; filename="' . str_replace('"', '\'', basename($filename)) . '";');
+ header('Content-Length: ' . strlen($content));
+
+ echo $content;
+ }
+
+ protected static function inlineToBrowser($contentType, $content)
+ {
+ header('Content-Type: ' . $contentType);
+ echo $content;
+ }
+
+ /**
+ * Convert a dimension-less report to a multi-row two-column data table
+ *
+ * @static
+ * @param $reportMetadata array
+ * @param $report Piwik_DataTable
+ * @param $reportColumns array
+ * @return array Piwik_DataTable $report & array $columns
+ */
+ protected static function processTableFormat($reportMetadata, $report, $reportColumns)
+ {
+ $finalReport = $report;
+ if (empty($reportMetadata['dimension'])) {
+ $simpleReportMetrics = $report->getFirstRow();
+ if ($simpleReportMetrics) {
+ $finalReport = new Piwik_DataTable_Simple();
+ foreach ($simpleReportMetrics->getColumns() as $metricId => $metric) {
+ $newRow = new Piwik_DataTable_Row();
+ $newRow->addColumn("label", $reportColumns[$metricId]);
+ $newRow->addColumn("value", $metric);
+ $finalReport->addRow($newRow);
+ }
+ }
+
+ $reportColumns = array(
+ 'label' => Piwik_Translate('General_Name'),
+ 'value' => Piwik_Translate('General_Value'),
+ );
+ }
+
+ return array(
+ $finalReport,
+ $reportColumns,
+ );
+ }
+
+ public static function getStaticGraph($reportMetadata, $width, $height, $evolution)
+ {
+
+ $imageGraphUrl = $reportMetadata['imageGraphUrl'];
+
+ if ($evolution && !empty($reportMetadata['imageGraphEvolutionUrl'])) {
+ $imageGraphUrl = $reportMetadata['imageGraphEvolutionUrl'];
+ }
+
+ $request = new Piwik_API_Request(
+ $imageGraphUrl .
+ '&outputType=' . Piwik_ImageGraph_API::GRAPH_OUTPUT_PHP .
+ '&format=original&serialize=0' .
+ '&filter_truncate=' .
+ '&width=' . $width .
+ '&height=' . $height
+ );
+
+ try {
+ $imageGraph = $request->process();
+
+ // Get image data as string
+ ob_start();
+ imagepng($imageGraph);
+ $imageGraphData = ob_get_contents();
+ ob_end_clean();
+ imagedestroy($imageGraph);
+
+ return $imageGraphData;
+
+ } catch (Exception $e) {
+ throw new Exception("ImageGraph API returned an error: " . $e->getMessage() . "\n");
+ }
+ }
}
diff --git a/core/ReportRenderer/Html.php b/core/ReportRenderer/Html.php
index 1bf56eb341..45c14bffa4 100644
--- a/core/ReportRenderer/Html.php
+++ b/core/ReportRenderer/Html.php
@@ -16,141 +16,139 @@
*/
class Piwik_ReportRenderer_Html extends Piwik_ReportRenderer
{
- const IMAGE_GRAPH_WIDTH = 700;
- const IMAGE_GRAPH_HEIGHT = 200;
-
- const REPORT_TITLE_TEXT_SIZE = 11;
- const REPORT_TABLE_HEADER_TEXT_SIZE = 11;
- const REPORT_TABLE_ROW_TEXT_SIZE = 11;
- const REPORT_BACK_TO_TOP_TEXT_SIZE = 9;
-
- const HTML_CONTENT_TYPE = 'text/html';
- const HTML_FILE_EXTENSION = 'html';
-
- protected $renderImageInline = false;
-
- private $rendering = "";
-
- public function setLocale($locale)
- {
- //Nothing to do
- }
-
- /**
- * Currently only used for HTML reports.
- * When sent by mail, images are attached to the mail: renderImageInline = false
- * When downloaded, images are included base64 encoded in the report body: renderImageInline = true
- *
- * @param boolean $renderImageInline
- */
- public function setRenderImageInline($renderImageInline)
- {
- $this->renderImageInline = $renderImageInline;
- }
-
- public function sendToDisk($filename)
- {
- $this->epilogue();
-
- return Piwik_ReportRenderer::writeFile($filename, self::HTML_FILE_EXTENSION, $this->rendering);
- }
-
- public function sendToBrowserDownload($filename)
- {
- $this->epilogue();
-
- Piwik_ReportRenderer::sendToBrowser($filename, self::HTML_FILE_EXTENSION, self::HTML_CONTENT_TYPE, $this->rendering);
- }
-
- public function sendToBrowserInline($filename)
- {
- $this->epilogue();
-
- Piwik_ReportRenderer::inlineToBrowser(self::HTML_CONTENT_TYPE, $this->rendering);
- }
-
- public function getRenderedReport()
- {
- $this->epilogue();
-
- return $this->rendering;
- }
-
- private function epilogue()
- {
- $smarty = new Piwik_Smarty();
- $this->rendering .= $smarty->fetch(self::prefixTemplatePath("html_report_footer.tpl"));
- }
-
- public function renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata)
- {
- $smarty = new Piwik_Smarty();
- $this->assignCommonParameters($smarty);
-
- $smarty->assign("websiteName", $websiteName);
- $smarty->assign("prettyDate", $prettyDate);
- $smarty->assign("description", $description);
- $smarty->assign("reportMetadata", $reportMetadata);
-
- $this->rendering .= $smarty->fetch(self::prefixTemplatePath("html_report_header.tpl"));
- }
-
- private function assignCommonParameters($smarty)
- {
- $smarty->assign("reportTitleTextColor", Piwik_ReportRenderer::REPORT_TITLE_TEXT_COLOR);
- $smarty->assign("reportTitleTextSize", self::REPORT_TITLE_TEXT_SIZE);
- $smarty->assign("reportTextColor", Piwik_ReportRenderer::REPORT_TEXT_COLOR);
- $smarty->assign("tableHeaderBgColor", Piwik_ReportRenderer::TABLE_HEADER_BG_COLOR);
- $smarty->assign("tableHeaderTextColor", Piwik_ReportRenderer::TABLE_HEADER_TEXT_COLOR);
- $smarty->assign("tableCellBorderColor", Piwik_ReportRenderer::TABLE_CELL_BORDER_COLOR);
- $smarty->assign("tableBgColor", Piwik_ReportRenderer::TABLE_BG_COLOR);
- $smarty->assign("reportTableHeaderTextSize", self::REPORT_TABLE_HEADER_TEXT_SIZE);
- $smarty->assign("reportTableRowTextSize", self::REPORT_TABLE_ROW_TEXT_SIZE);
- $smarty->assign("reportBackToTopTextSize", self::REPORT_BACK_TO_TOP_TEXT_SIZE);
- $smarty->assign("currentPath", Piwik::getPiwikUrl());
- $smarty->assign("logoHeader", Piwik_API_API::getInstance()->getHeaderLogoUrl());
- }
-
- public function renderReport($processedReport)
- {
- $smarty = new Piwik_Smarty();
- $this->assignCommonParameters($smarty);
-
- $reportMetadata = $processedReport['metadata'];
- $reportData = $processedReport['reportData'];
- $columns = $processedReport['columns'];
- list($reportData, $columns) = self::processTableFormat($reportMetadata, $reportData, $columns);
-
- $smarty->assign("reportName", $reportMetadata['name']);
- $smarty->assign("reportId", $reportMetadata['uniqueId']);
- $smarty->assign("reportColumns", $columns);
- $smarty->assign("reportRows", $reportData->getRows());
- $smarty->assign("reportRowsMetadata", $processedReport['reportMetadata']->getRows());
- $smarty->assign("displayTable", $processedReport['displayTable']);
-
- $displayGraph = $processedReport['displayGraph'];
- $evolutionGraph = $processedReport['evolutionGraph'];
- $smarty->assign("displayGraph", $displayGraph);
-
- if($displayGraph)
- {
- $smarty->assign("graphWidth", self::IMAGE_GRAPH_WIDTH);
- $smarty->assign("graphHeight", self::IMAGE_GRAPH_HEIGHT);
- $smarty->assign("renderImageInline", $this->renderImageInline);
-
- if($this->renderImageInline)
- {
- $staticGraph = parent::getStaticGraph($reportMetadata, self::IMAGE_GRAPH_WIDTH, self::IMAGE_GRAPH_HEIGHT, $evolutionGraph);
- $smarty->assign("generatedImageGraph", base64_encode($staticGraph));
- unset($generatedImageGraph);
- }
- }
-
- $this->rendering .= $smarty->fetch(self::prefixTemplatePath("html_report_body.tpl"));
- }
-
- private static function prefixTemplatePath($templateFile)
- {
- return PIWIK_USER_PATH . "/plugins/CoreHome/templates/" . $templateFile;
- }
+ const IMAGE_GRAPH_WIDTH = 700;
+ const IMAGE_GRAPH_HEIGHT = 200;
+
+ const REPORT_TITLE_TEXT_SIZE = 11;
+ const REPORT_TABLE_HEADER_TEXT_SIZE = 11;
+ const REPORT_TABLE_ROW_TEXT_SIZE = 11;
+ const REPORT_BACK_TO_TOP_TEXT_SIZE = 9;
+
+ const HTML_CONTENT_TYPE = 'text/html';
+ const HTML_FILE_EXTENSION = 'html';
+
+ protected $renderImageInline = false;
+
+ private $rendering = "";
+
+ public function setLocale($locale)
+ {
+ //Nothing to do
+ }
+
+ /**
+ * Currently only used for HTML reports.
+ * When sent by mail, images are attached to the mail: renderImageInline = false
+ * When downloaded, images are included base64 encoded in the report body: renderImageInline = true
+ *
+ * @param boolean $renderImageInline
+ */
+ public function setRenderImageInline($renderImageInline)
+ {
+ $this->renderImageInline = $renderImageInline;
+ }
+
+ public function sendToDisk($filename)
+ {
+ $this->epilogue();
+
+ return Piwik_ReportRenderer::writeFile($filename, self::HTML_FILE_EXTENSION, $this->rendering);
+ }
+
+ public function sendToBrowserDownload($filename)
+ {
+ $this->epilogue();
+
+ Piwik_ReportRenderer::sendToBrowser($filename, self::HTML_FILE_EXTENSION, self::HTML_CONTENT_TYPE, $this->rendering);
+ }
+
+ public function sendToBrowserInline($filename)
+ {
+ $this->epilogue();
+
+ Piwik_ReportRenderer::inlineToBrowser(self::HTML_CONTENT_TYPE, $this->rendering);
+ }
+
+ public function getRenderedReport()
+ {
+ $this->epilogue();
+
+ return $this->rendering;
+ }
+
+ private function epilogue()
+ {
+ $smarty = new Piwik_Smarty();
+ $this->rendering .= $smarty->fetch(self::prefixTemplatePath("html_report_footer.tpl"));
+ }
+
+ public function renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata)
+ {
+ $smarty = new Piwik_Smarty();
+ $this->assignCommonParameters($smarty);
+
+ $smarty->assign("websiteName", $websiteName);
+ $smarty->assign("prettyDate", $prettyDate);
+ $smarty->assign("description", $description);
+ $smarty->assign("reportMetadata", $reportMetadata);
+
+ $this->rendering .= $smarty->fetch(self::prefixTemplatePath("html_report_header.tpl"));
+ }
+
+ private function assignCommonParameters($smarty)
+ {
+ $smarty->assign("reportTitleTextColor", Piwik_ReportRenderer::REPORT_TITLE_TEXT_COLOR);
+ $smarty->assign("reportTitleTextSize", self::REPORT_TITLE_TEXT_SIZE);
+ $smarty->assign("reportTextColor", Piwik_ReportRenderer::REPORT_TEXT_COLOR);
+ $smarty->assign("tableHeaderBgColor", Piwik_ReportRenderer::TABLE_HEADER_BG_COLOR);
+ $smarty->assign("tableHeaderTextColor", Piwik_ReportRenderer::TABLE_HEADER_TEXT_COLOR);
+ $smarty->assign("tableCellBorderColor", Piwik_ReportRenderer::TABLE_CELL_BORDER_COLOR);
+ $smarty->assign("tableBgColor", Piwik_ReportRenderer::TABLE_BG_COLOR);
+ $smarty->assign("reportTableHeaderTextSize", self::REPORT_TABLE_HEADER_TEXT_SIZE);
+ $smarty->assign("reportTableRowTextSize", self::REPORT_TABLE_ROW_TEXT_SIZE);
+ $smarty->assign("reportBackToTopTextSize", self::REPORT_BACK_TO_TOP_TEXT_SIZE);
+ $smarty->assign("currentPath", Piwik::getPiwikUrl());
+ $smarty->assign("logoHeader", Piwik_API_API::getInstance()->getHeaderLogoUrl());
+ }
+
+ public function renderReport($processedReport)
+ {
+ $smarty = new Piwik_Smarty();
+ $this->assignCommonParameters($smarty);
+
+ $reportMetadata = $processedReport['metadata'];
+ $reportData = $processedReport['reportData'];
+ $columns = $processedReport['columns'];
+ list($reportData, $columns) = self::processTableFormat($reportMetadata, $reportData, $columns);
+
+ $smarty->assign("reportName", $reportMetadata['name']);
+ $smarty->assign("reportId", $reportMetadata['uniqueId']);
+ $smarty->assign("reportColumns", $columns);
+ $smarty->assign("reportRows", $reportData->getRows());
+ $smarty->assign("reportRowsMetadata", $processedReport['reportMetadata']->getRows());
+ $smarty->assign("displayTable", $processedReport['displayTable']);
+
+ $displayGraph = $processedReport['displayGraph'];
+ $evolutionGraph = $processedReport['evolutionGraph'];
+ $smarty->assign("displayGraph", $displayGraph);
+
+ if ($displayGraph) {
+ $smarty->assign("graphWidth", self::IMAGE_GRAPH_WIDTH);
+ $smarty->assign("graphHeight", self::IMAGE_GRAPH_HEIGHT);
+ $smarty->assign("renderImageInline", $this->renderImageInline);
+
+ if ($this->renderImageInline) {
+ $staticGraph = parent::getStaticGraph($reportMetadata, self::IMAGE_GRAPH_WIDTH, self::IMAGE_GRAPH_HEIGHT, $evolutionGraph);
+ $smarty->assign("generatedImageGraph", base64_encode($staticGraph));
+ unset($generatedImageGraph);
+ }
+ }
+
+ $this->rendering .= $smarty->fetch(self::prefixTemplatePath("html_report_body.tpl"));
+ }
+
+ private static function prefixTemplatePath($templateFile)
+ {
+ return PIWIK_USER_PATH . "/plugins/CoreHome/templates/" . $templateFile;
+ }
} \ No newline at end of file
diff --git a/core/ReportRenderer/Pdf.php b/core/ReportRenderer/Pdf.php
index 95e7a39a5d..23d24ab18f 100644
--- a/core/ReportRenderer/Pdf.php
+++ b/core/ReportRenderer/Pdf.php
@@ -22,499 +22,481 @@ require_once PIWIK_INCLUDE_PATH . '/core/TCPDF.php';
*/
class Piwik_ReportRenderer_Pdf extends Piwik_ReportRenderer
{
- const IMAGE_GRAPH_WIDTH_LANDSCAPE = 1050;
- const IMAGE_GRAPH_WIDTH_PORTRAIT = 760;
- const IMAGE_GRAPH_HEIGHT = 220;
-
- const LANDSCAPE = 'L';
- const PORTRAIT = 'P';
-
- const MAX_ROW_COUNT = 28;
- const TABLE_HEADER_ROW_COUNT = 6;
- const NO_DATA_ROW_COUNT = 6;
- const MAX_GRAPH_REPORTS = 3;
- const MAX_2COL_TABLE_REPORTS = 2;
-
- const PDF_CONTENT_TYPE = 'pdf';
-
- private $reportFontStyle = '';
- private $reportSimpleFontSize = 9;
- private $reportHeaderFontSize = 16;
- private $cellHeight = 6;
- private $bottomMargin = 17;
- private $reportWidthPortrait = 195;
- private $reportWidthLandscape = 270;
- private $minWidthLabelCell = 100;
- private $maxColumnCountPortraitOrientation = 6;
- private $logoWidth = 16;
- private $logoHeight = 16;
- private $totalWidth;
- private $cellWidth;
- private $labelCellWidth;
- private $truncateAfter = 55;
- private $leftSpacesBeforeLogo = 7;
- private $logoImagePosition = array(10, 40);
- private $headerTextColor;
- private $reportTextColor;
- private $tableHeaderBackgroundColor;
- private $tableHeaderTextColor;
- private $tableCellBorderColor;
- private $tableBackgroundColor;
- private $rowTopBottomBorder = array(231, 231, 231);
- private $report;
- private $reportMetadata;
- private $displayGraph;
- private $evolutionGraph;
- private $displayTable;
- private $reportColumns;
- private $reportRowsMetadata;
- private $currentPage = 0;
- private $reportFont = Piwik_ReportRenderer::DEFAULT_REPORT_FONT;
- private $TCPDF;
- private $orientation = self::PORTRAIT;
-
- public function __construct()
- {
- $this->TCPDF = new Piwik_TCPDF();
- $this->headerTextColor = preg_split("/,/", Piwik_ReportRenderer::REPORT_TITLE_TEXT_COLOR);
- $this->reportTextColor = preg_split("/,/", Piwik_ReportRenderer::REPORT_TEXT_COLOR);
- $this->tableHeaderBackgroundColor = preg_split("/,/", Piwik_ReportRenderer::TABLE_HEADER_BG_COLOR);
- $this->tableHeaderTextColor = preg_split("/,/", Piwik_ReportRenderer::TABLE_HEADER_TEXT_COLOR);
- $this->tableCellBorderColor = preg_split("/,/", Piwik_ReportRenderer::TABLE_CELL_BORDER_COLOR);
- $this->tableBackgroundColor = preg_split("/,/", Piwik_ReportRenderer::TABLE_BG_COLOR);
- }
-
- public function setLocale($locale)
- {
- switch ($locale)
- {
- case 'zh-tw':
- $reportFont = 'msungstdlight';
- break;
-
- case 'ja':
- $reportFont = 'kozgopromedium';
- break;
-
- case 'zh-cn':
- $reportFont = 'stsongstdlight';
- break;
-
- case 'ko':
- $reportFont = 'hysmyeongjostdmedium';
- break;
-
- case 'ar':
- $reportFont = 'almohanad';
- break;
-
- case 'en':
- default:
- $reportFont = Piwik_ReportRenderer::DEFAULT_REPORT_FONT;
- break;
- }
- $this->reportFont = $reportFont;
- }
-
- public function sendToDisk($filename)
- {
- $filename = Piwik_ReportRenderer::appendExtension($filename, self::PDF_CONTENT_TYPE);
- $outputFilename = Piwik_ReportRenderer::getOutputPath($filename);
-
- $this->TCPDF->Output($outputFilename, 'F');
-
- return $outputFilename;
- }
-
- public function sendToBrowserDownload($filename)
- {
- $filename = Piwik_ReportRenderer::appendExtension($filename, self::PDF_CONTENT_TYPE);
- $this->TCPDF->Output($filename, 'D');
- }
-
- public function sendToBrowserInline($filename)
- {
- $filename = Piwik_ReportRenderer::appendExtension($filename, self::PDF_CONTENT_TYPE);
- $this->TCPDF->Output($filename, 'I');
- }
-
- public function getRenderedReport()
- {
- return $this->TCPDF->Output(null, 'S');
- }
-
- public function renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata)
- {
- $websiteTitle = $this->formatText($websiteName);
- $dateRange = $this->formatText(Piwik_Translate('General_DateRange') . " " . $prettyDate);
-
- //Setup Footer font and data
- $this->TCPDF->SetFooterFont(array($this->reportFont, $this->reportFontStyle, $this->reportSimpleFontSize));
- $this->TCPDF->SetFooterContent($websiteTitle . " | " . $dateRange . " | ");
-
- $this->TCPDF->setPrintHeader(false);
- // $this->SetMargins($left = , $top, $right=-1, $keepmargins=true)
- $this->TCPDF->AddPage(self::PORTRAIT);
- $this->TCPDF->AddFont($this->reportFont, '', '', false);
- $this->TCPDF->SetFont($this->reportFont, $this->reportFontStyle, $this->reportSimpleFontSize);
- //Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false) {
- $this->TCPDF->Bookmark(Piwik_Translate('PDFReports_FrontPage'));
- $this->TCPDF->Image(Piwik_API_API::getInstance()->getLogoUrl(true), $this->logoImagePosition[0], $this->logoImagePosition[1], 180 / $factor = 2, 0, $type = '', $link = '', $align = '', $resize = false, $dpi = 300);
- $this->TCPDF->Ln(8);
-
- $this->TCPDF->SetFont($this->reportFont, '', $this->reportHeaderFontSize + 5);
- $this->TCPDF->SetTextColor($this->headerTextColor[0], $this->headerTextColor[1], $this->headerTextColor[2]);
- $this->TCPDF->Cell(40, 210, $websiteTitle);
- $this->TCPDF->Ln(8 * 4);
-
- $this->TCPDF->SetFont($this->reportFont, '', $this->reportHeaderFontSize);
- $this->TCPDF->SetTextColor($this->reportTextColor[0], $this->reportTextColor[1], $this->reportTextColor[2]);
- $this->TCPDF->Cell(40, 210, $dateRange);
- $this->TCPDF->Ln(8 * 20);
- $this->TCPDF->Write(1, $this->formatText($description));
- $this->TCPDF->Ln(8);
- $this->TCPDF->SetFont($this->reportFont, '', $this->reportHeaderFontSize);
- $this->TCPDF->Ln();
- }
-
- /**
- * Generate a header of page.
- */
- private function paintReportHeader()
- {
- $isAggregateReport = !empty($this->reportMetadata['dimension']);
-
- // Graph-only report
- static $graphOnlyReportCount = 0;
- $graphOnlyReport = $isAggregateReport && $this->displayGraph && !$this->displayTable;
-
- // Table-only report
- $tableOnlyReport = $isAggregateReport
- && !$this->displayGraph
- && $this->displayTable;
-
- $columnCount = count($this->reportColumns);
-
- // Table-only 2-column report
- static $tableOnly2ColumnReportCount = 0;
- $tableOnly2ColumnReport = $tableOnlyReport
- && $columnCount == 2;
-
- // Table-only report with more than 2 columns
- static $tableOnlyManyColumnReportRowCount = 0;
- $tableOnlyManyColumnReport = $tableOnlyReport
- && $columnCount > 3;
-
- $reportHasData = $this->reportHasData();
-
- $rowCount = $reportHasData ? $this->report->getRowsCount() + self::TABLE_HEADER_ROW_COUNT : self::NO_DATA_ROW_COUNT;
-
- // Only a page break before if the current report has some data
- if ($reportHasData &&
- // and
- (
- // it is the first report
- $this->currentPage == 0
- // or, it is a graph-only report and it is the first of a series of self::MAX_GRAPH_REPORTS
- || ($graphOnlyReport && $graphOnlyReportCount == 0)
- // or, it is a table-only 2-column report and it is the first of a series of self::MAX_2COL_TABLE_REPORTS
- || ($tableOnly2ColumnReport && $tableOnly2ColumnReportCount == 0)
- // or it is a table-only report with more than 2 columns and it is the first of its series or there isn't enough space left on the page
- || ($tableOnlyManyColumnReport && ($tableOnlyManyColumnReportRowCount == 0 || $tableOnlyManyColumnReportRowCount + $rowCount >= self::MAX_ROW_COUNT))
- // or it is a report with both a table and a graph
- || !$graphOnlyReport && !$tableOnlyReport
- )
- )
- {
- $this->currentPage++;
- $this->TCPDF->AddPage();
-
- // Table-only reports with more than 2 columns are always landscape
- if ($tableOnlyManyColumnReport)
- {
- $tableOnlyManyColumnReportRowCount = 0;
- $this->orientation = self::LANDSCAPE;
- }
- else
- {
- // Graph-only reports are always portrait
- $this->orientation = $graphOnlyReport ? self::PORTRAIT : ($columnCount > $this->maxColumnCountPortraitOrientation ? self::LANDSCAPE : self::PORTRAIT);
- }
-
- $this->TCPDF->setPageOrientation($this->orientation, '', $this->bottomMargin);
- }
-
- $graphOnlyReportCount = ($graphOnlyReport && $reportHasData) ? ($graphOnlyReportCount + 1) % self::MAX_GRAPH_REPORTS : 0;
- $tableOnly2ColumnReportCount = ($tableOnly2ColumnReport && $reportHasData) ? ($tableOnly2ColumnReportCount + 1) % self::MAX_2COL_TABLE_REPORTS : 0;
- $tableOnlyManyColumnReportRowCount = $tableOnlyManyColumnReport ? ($tableOnlyManyColumnReportRowCount + $rowCount) : 0;
-
- $title = $this->formatText($this->reportMetadata['name']);
- $this->TCPDF->SetFont($this->reportFont, $this->reportFontStyle, $this->reportHeaderFontSize);
- $this->TCPDF->SetTextColor($this->headerTextColor[0], $this->headerTextColor[1], $this->headerTextColor[2]);
- $this->TCPDF->Bookmark($title);
- $this->TCPDF->Cell(40, 15, $title);
- $this->TCPDF->Ln();
- $this->TCPDF->SetFont($this->reportFont, '', $this->reportSimpleFontSize);
- $this->TCPDF->SetTextColor($this->reportTextColor[0], $this->reportTextColor[1], $this->reportTextColor[2]);
- }
-
- private function reportHasData()
- {
- return $this->report->getRowsCount() > 0;
- }
-
- private function setBorderColor()
- {
- $this->TCPDF->SetDrawColor($this->tableCellBorderColor[0], $this->tableCellBorderColor[1], $this->tableCellBorderColor[2]);
- }
-
- public function renderReport($processedReport)
- {
- $this->reportMetadata = $processedReport['metadata'];
- $this->reportRowsMetadata = $processedReport['reportMetadata'];
- $this->displayGraph = $processedReport['displayGraph'];
- $this->evolutionGraph = $processedReport['evolutionGraph'];
- $this->displayTable = $processedReport['displayTable'];
- list($this->report, $this->reportColumns) = self::processTableFormat($this->reportMetadata, $processedReport['reportData'], $processedReport['columns']);
-
- $this->paintReportHeader();
-
- if (!$this->reportHasData()) {
- $this->paintMessage(Piwik_Translate('CoreHome_ThereIsNoDataForThisReport'));
- return;
- }
-
- if($this->displayGraph)
- {
- $this->paintGraph();
- }
-
- if($this->displayGraph && $this->displayTable)
- {
- $this->TCPDF->Ln(5);
- }
-
- if($this->displayTable)
- {
- $this->paintReportTableHeader();
- $this->paintReportTable();
- }
- }
-
- private function formatText($text)
- {
- return Piwik_Common::unsanitizeInputValue($text);
- }
-
- private function paintReportTable()
- {
- //Color and font restoration
- $this->TCPDF->SetFillColor($this->tableBackgroundColor[0], $this->tableBackgroundColor[1], $this->tableBackgroundColor[2]);
- $this->TCPDF->SetTextColor($this->reportTextColor[0], $this->reportTextColor[1], $this->reportTextColor[2]);
- $this->TCPDF->SetFont('');
-
- $fill = false;
- $url = false;
- $leftSpacesBeforeLogo = str_repeat(' ', $this->leftSpacesBeforeLogo);
-
- $logoWidth = $this->logoWidth;
- $logoHeight = $this->logoHeight;
-
- $rowsMetadata = $this->reportRowsMetadata->getRows();
-
- // Draw a body of report table
- foreach ($this->report->getRows() as $rowId => $row)
- {
- $rowMetrics = $row->getColumns();
- $rowMetadata = isset($rowsMetadata[$rowId]) ? $rowsMetadata[$rowId]->getColumns() : array();
- if (isset($rowMetadata['url'])) {
- $url = $rowMetadata['url'];
- }
- foreach ($this->reportColumns as $columnId => $columnName)
- {
- // Label column
- if ($columnId == 'label') {
- $isLogoDisplayable = isset($rowMetadata['logo']);
- $text = '';
- $posX = $this->TCPDF->GetX();
- $posY = $this->TCPDF->GetY();
- if (isset($rowMetrics[$columnId])) {
- $text = substr($rowMetrics[$columnId], 0, $this->truncateAfter);
- if ($isLogoDisplayable) {
- $text = $leftSpacesBeforeLogo . $text;
- }
- }
- $text = $this->formatText($text);
-
- $this->TCPDF->Cell($this->labelCellWidth, $this->cellHeight, $text, 'LR', 0, 'L', $fill, $url);
-
- if ($isLogoDisplayable) {
- if (isset($rowMetadata['logoWidth'])) {
- $logoWidth = $rowMetadata['logoWidth'];
- }
- if (isset($rowMetadata['logoHeight'])) {
- $logoHeight = $rowMetadata['logoHeight'];
- }
- $restoreY = $this->TCPDF->getY();
- $restoreX = $this->TCPDF->getX();
- $this->TCPDF->SetY($posY);
- $this->TCPDF->SetX($posX);
- $topMargin = 1.3;
- // Country flags are not very high, force a bigger top margin
- if ($logoHeight < 16) {
- $topMargin = 2;
- }
- $path = Piwik_Common::getPathToPiwikRoot() . "/" . $rowMetadata['logo'];
- if(file_exists($path))
- {
- $this->TCPDF->Image($path, $posX + ($leftMargin = 2), $posY + $topMargin, $logoWidth / 4);
- }
- $this->TCPDF->SetXY($restoreX, $restoreY);
- }
- }
- // metrics column
- else
- {
- // No value means 0
- if (empty($rowMetrics[$columnId])) {
- $rowMetrics[$columnId] = 0;
- }
- $this->TCPDF->Cell($this->cellWidth, $this->cellHeight, $rowMetrics[$columnId], 'LR', 0, 'L', $fill);
- }
- }
-
- $this->TCPDF->Ln();
-
- // Top/Bottom grey border for all cells
- $this->TCPDF->SetDrawColor($this->rowTopBottomBorder[0], $this->rowTopBottomBorder[1], $this->rowTopBottomBorder[2]);
- $this->TCPDF->Cell($this->totalWidth, 0, '', 'T');
- $this->setBorderColor();
- $this->TCPDF->Ln(0.2);
-
- $fill = !$fill;
- }
- }
- private function paintGraph()
- {
- $imageGraph = parent::getStaticGraph(
- $this->reportMetadata,
- $this->orientation == self::PORTRAIT ? self::IMAGE_GRAPH_WIDTH_PORTRAIT : self::IMAGE_GRAPH_WIDTH_LANDSCAPE,
- self::IMAGE_GRAPH_HEIGHT,
- $this->evolutionGraph
- );
-
- $this->TCPDF->Image(
- '@'.$imageGraph,
- $x = '',
- $y = '',
- $w = 0,
- $h = 0,
- $type = '',
- $link = '',
- $align = 'N',
- $resize = false,
- $dpi = 72,
- $palign = '',
- $ismask = false,
- $imgmask = false,
- $order = 0,
- $fitbox = false,
- $hidden = false,
- $fitonpage = true,
- $alt = false,
- $altimgs = array()
- );
-
- unset($imageGraph);
- }
-
- /**
- * Draw the table header (first row)
- */
- private function paintReportTableHeader()
- {
- $initPosX = 10;
-
- // Get the longest column name
- $longestColumnName = '';
- foreach ($this->reportColumns as $columnName)
- {
- if (strlen($columnName) > strlen($longestColumnName)) {
- $longestColumnName = $columnName;
- }
- }
-
- $columnsCount = count($this->reportColumns);
- // Computes available column width
- if ($this->orientation == self::PORTRAIT
- && $columnsCount <= 3) {
- $totalWidth = $this->reportWidthPortrait * 2 / 3;
- }
- else if ($this->orientation == self::LANDSCAPE) {
- $totalWidth = $this->reportWidthLandscape;
- }
- else
- {
- $totalWidth = $this->reportWidthPortrait;
- }
- $this->totalWidth = $totalWidth;
- $this->labelCellWidth = max(round(($this->totalWidth / $columnsCount) ), $this->minWidthLabelCell);
- $this->cellWidth = round(($this->totalWidth - $this->labelCellWidth) / ($columnsCount - 1));
- $this->totalWidth = $this->labelCellWidth + ($columnsCount - 1) * $this->cellWidth;
-
- $this->TCPDF->SetFillColor($this->tableHeaderBackgroundColor[0], $this->tableHeaderBackgroundColor[1], $this->tableHeaderBackgroundColor[2]);
- $this->TCPDF->SetTextColor($this->tableHeaderTextColor[0], $this->tableHeaderTextColor[1], $this->tableHeaderTextColor[2]);
- $this->TCPDF->SetLineWidth(.3);
- $this->setBorderColor();
- $this->TCPDF->SetFont($this->reportFont, $this->reportFontStyle);
- $this->TCPDF->SetFillColor(255);
- $this->TCPDF->SetTextColor($this->tableHeaderBackgroundColor[0], $this->tableHeaderBackgroundColor[1], $this->tableHeaderBackgroundColor[2]);
- $this->TCPDF->SetDrawColor(255);
-
- $posY = $this->TCPDF->GetY();
- $this->TCPDF->MultiCell($this->cellWidth, $this->cellHeight, $longestColumnName, 1, 'C', true);
- $maxCellHeight = $this->TCPDF->GetY() - $posY;
-
- $this->TCPDF->SetFillColor($this->tableHeaderBackgroundColor[0], $this->tableHeaderBackgroundColor[1], $this->tableHeaderBackgroundColor[2]);
- $this->TCPDF->SetTextColor($this->tableHeaderTextColor[0], $this->tableHeaderTextColor[1], $this->tableHeaderTextColor[2]);
- $this->TCPDF->SetDrawColor($this->tableCellBorderColor[0], $this->tableCellBorderColor[1], $this->tableCellBorderColor[2]);
-
- $this->TCPDF->SetXY($initPosX, $posY);
-
- $countColumns = 0;
- $posX = $initPosX;
- foreach ($this->reportColumns as $columnName)
- {
- $columnName = $this->formatText($columnName);
- //Label column
- if ($countColumns == 0) {
- $this->TCPDF->MultiCell($this->labelCellWidth, $maxCellHeight, $columnName, 1, 'C', true);
- $this->TCPDF->SetXY($posX + $this->labelCellWidth, $posY);
- }
- else
- {
- $this->TCPDF->MultiCell($this->cellWidth, $maxCellHeight, $columnName, 1, 'C', true);
- $this->TCPDF->SetXY($posX + $this->cellWidth, $posY);
- }
- $countColumns++;
- $posX = $this->TCPDF->GetX();
- }
- $this->TCPDF->Ln();
- $this->TCPDF->SetXY($initPosX, $posY + $maxCellHeight);
- }
-
- /**
- * Prints a message
- *
- * @param string $message
- * @return void
- */
- private function paintMessage($message)
- {
- $this->TCPDF->SetFont($this->reportFont, $this->reportFontStyle, $this->reportSimpleFontSize);
- $this->TCPDF->SetTextColor($this->reportTextColor[0], $this->reportTextColor[1], $this->reportTextColor[2]);
- $message = $this->formatText($message);
- $this->TCPDF->Write("1em", $message);
- $this->TCPDF->Ln();
- }
+ const IMAGE_GRAPH_WIDTH_LANDSCAPE = 1050;
+ const IMAGE_GRAPH_WIDTH_PORTRAIT = 760;
+ const IMAGE_GRAPH_HEIGHT = 220;
+
+ const LANDSCAPE = 'L';
+ const PORTRAIT = 'P';
+
+ const MAX_ROW_COUNT = 28;
+ const TABLE_HEADER_ROW_COUNT = 6;
+ const NO_DATA_ROW_COUNT = 6;
+ const MAX_GRAPH_REPORTS = 3;
+ const MAX_2COL_TABLE_REPORTS = 2;
+
+ const PDF_CONTENT_TYPE = 'pdf';
+
+ private $reportFontStyle = '';
+ private $reportSimpleFontSize = 9;
+ private $reportHeaderFontSize = 16;
+ private $cellHeight = 6;
+ private $bottomMargin = 17;
+ private $reportWidthPortrait = 195;
+ private $reportWidthLandscape = 270;
+ private $minWidthLabelCell = 100;
+ private $maxColumnCountPortraitOrientation = 6;
+ private $logoWidth = 16;
+ private $logoHeight = 16;
+ private $totalWidth;
+ private $cellWidth;
+ private $labelCellWidth;
+ private $truncateAfter = 55;
+ private $leftSpacesBeforeLogo = 7;
+ private $logoImagePosition = array(10, 40);
+ private $headerTextColor;
+ private $reportTextColor;
+ private $tableHeaderBackgroundColor;
+ private $tableHeaderTextColor;
+ private $tableCellBorderColor;
+ private $tableBackgroundColor;
+ private $rowTopBottomBorder = array(231, 231, 231);
+ private $report;
+ private $reportMetadata;
+ private $displayGraph;
+ private $evolutionGraph;
+ private $displayTable;
+ private $reportColumns;
+ private $reportRowsMetadata;
+ private $currentPage = 0;
+ private $reportFont = Piwik_ReportRenderer::DEFAULT_REPORT_FONT;
+ private $TCPDF;
+ private $orientation = self::PORTRAIT;
+
+ public function __construct()
+ {
+ $this->TCPDF = new Piwik_TCPDF();
+ $this->headerTextColor = preg_split("/,/", Piwik_ReportRenderer::REPORT_TITLE_TEXT_COLOR);
+ $this->reportTextColor = preg_split("/,/", Piwik_ReportRenderer::REPORT_TEXT_COLOR);
+ $this->tableHeaderBackgroundColor = preg_split("/,/", Piwik_ReportRenderer::TABLE_HEADER_BG_COLOR);
+ $this->tableHeaderTextColor = preg_split("/,/", Piwik_ReportRenderer::TABLE_HEADER_TEXT_COLOR);
+ $this->tableCellBorderColor = preg_split("/,/", Piwik_ReportRenderer::TABLE_CELL_BORDER_COLOR);
+ $this->tableBackgroundColor = preg_split("/,/", Piwik_ReportRenderer::TABLE_BG_COLOR);
+ }
+
+ public function setLocale($locale)
+ {
+ switch ($locale) {
+ case 'zh-tw':
+ $reportFont = 'msungstdlight';
+ break;
+
+ case 'ja':
+ $reportFont = 'kozgopromedium';
+ break;
+
+ case 'zh-cn':
+ $reportFont = 'stsongstdlight';
+ break;
+
+ case 'ko':
+ $reportFont = 'hysmyeongjostdmedium';
+ break;
+
+ case 'ar':
+ $reportFont = 'almohanad';
+ break;
+
+ case 'en':
+ default:
+ $reportFont = Piwik_ReportRenderer::DEFAULT_REPORT_FONT;
+ break;
+ }
+ $this->reportFont = $reportFont;
+ }
+
+ public function sendToDisk($filename)
+ {
+ $filename = Piwik_ReportRenderer::appendExtension($filename, self::PDF_CONTENT_TYPE);
+ $outputFilename = Piwik_ReportRenderer::getOutputPath($filename);
+
+ $this->TCPDF->Output($outputFilename, 'F');
+
+ return $outputFilename;
+ }
+
+ public function sendToBrowserDownload($filename)
+ {
+ $filename = Piwik_ReportRenderer::appendExtension($filename, self::PDF_CONTENT_TYPE);
+ $this->TCPDF->Output($filename, 'D');
+ }
+
+ public function sendToBrowserInline($filename)
+ {
+ $filename = Piwik_ReportRenderer::appendExtension($filename, self::PDF_CONTENT_TYPE);
+ $this->TCPDF->Output($filename, 'I');
+ }
+
+ public function getRenderedReport()
+ {
+ return $this->TCPDF->Output(null, 'S');
+ }
+
+ public function renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata)
+ {
+ $websiteTitle = $this->formatText($websiteName);
+ $dateRange = $this->formatText(Piwik_Translate('General_DateRange') . " " . $prettyDate);
+
+ //Setup Footer font and data
+ $this->TCPDF->SetFooterFont(array($this->reportFont, $this->reportFontStyle, $this->reportSimpleFontSize));
+ $this->TCPDF->SetFooterContent($websiteTitle . " | " . $dateRange . " | ");
+
+ $this->TCPDF->setPrintHeader(false);
+ // $this->SetMargins($left = , $top, $right=-1, $keepmargins=true)
+ $this->TCPDF->AddPage(self::PORTRAIT);
+ $this->TCPDF->AddFont($this->reportFont, '', '', false);
+ $this->TCPDF->SetFont($this->reportFont, $this->reportFontStyle, $this->reportSimpleFontSize);
+ //Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false) {
+ $this->TCPDF->Bookmark(Piwik_Translate('PDFReports_FrontPage'));
+ $this->TCPDF->Image(Piwik_API_API::getInstance()->getLogoUrl(true), $this->logoImagePosition[0], $this->logoImagePosition[1], 180 / $factor = 2, 0, $type = '', $link = '', $align = '', $resize = false, $dpi = 300);
+ $this->TCPDF->Ln(8);
+
+ $this->TCPDF->SetFont($this->reportFont, '', $this->reportHeaderFontSize + 5);
+ $this->TCPDF->SetTextColor($this->headerTextColor[0], $this->headerTextColor[1], $this->headerTextColor[2]);
+ $this->TCPDF->Cell(40, 210, $websiteTitle);
+ $this->TCPDF->Ln(8 * 4);
+
+ $this->TCPDF->SetFont($this->reportFont, '', $this->reportHeaderFontSize);
+ $this->TCPDF->SetTextColor($this->reportTextColor[0], $this->reportTextColor[1], $this->reportTextColor[2]);
+ $this->TCPDF->Cell(40, 210, $dateRange);
+ $this->TCPDF->Ln(8 * 20);
+ $this->TCPDF->Write(1, $this->formatText($description));
+ $this->TCPDF->Ln(8);
+ $this->TCPDF->SetFont($this->reportFont, '', $this->reportHeaderFontSize);
+ $this->TCPDF->Ln();
+ }
+
+ /**
+ * Generate a header of page.
+ */
+ private function paintReportHeader()
+ {
+ $isAggregateReport = !empty($this->reportMetadata['dimension']);
+
+ // Graph-only report
+ static $graphOnlyReportCount = 0;
+ $graphOnlyReport = $isAggregateReport && $this->displayGraph && !$this->displayTable;
+
+ // Table-only report
+ $tableOnlyReport = $isAggregateReport
+ && !$this->displayGraph
+ && $this->displayTable;
+
+ $columnCount = count($this->reportColumns);
+
+ // Table-only 2-column report
+ static $tableOnly2ColumnReportCount = 0;
+ $tableOnly2ColumnReport = $tableOnlyReport
+ && $columnCount == 2;
+
+ // Table-only report with more than 2 columns
+ static $tableOnlyManyColumnReportRowCount = 0;
+ $tableOnlyManyColumnReport = $tableOnlyReport
+ && $columnCount > 3;
+
+ $reportHasData = $this->reportHasData();
+
+ $rowCount = $reportHasData ? $this->report->getRowsCount() + self::TABLE_HEADER_ROW_COUNT : self::NO_DATA_ROW_COUNT;
+
+ // Only a page break before if the current report has some data
+ if ($reportHasData &&
+ // and
+ (
+ // it is the first report
+ $this->currentPage == 0
+ // or, it is a graph-only report and it is the first of a series of self::MAX_GRAPH_REPORTS
+ || ($graphOnlyReport && $graphOnlyReportCount == 0)
+ // or, it is a table-only 2-column report and it is the first of a series of self::MAX_2COL_TABLE_REPORTS
+ || ($tableOnly2ColumnReport && $tableOnly2ColumnReportCount == 0)
+ // or it is a table-only report with more than 2 columns and it is the first of its series or there isn't enough space left on the page
+ || ($tableOnlyManyColumnReport && ($tableOnlyManyColumnReportRowCount == 0 || $tableOnlyManyColumnReportRowCount + $rowCount >= self::MAX_ROW_COUNT))
+ // or it is a report with both a table and a graph
+ || !$graphOnlyReport && !$tableOnlyReport
+ )
+ ) {
+ $this->currentPage++;
+ $this->TCPDF->AddPage();
+
+ // Table-only reports with more than 2 columns are always landscape
+ if ($tableOnlyManyColumnReport) {
+ $tableOnlyManyColumnReportRowCount = 0;
+ $this->orientation = self::LANDSCAPE;
+ } else {
+ // Graph-only reports are always portrait
+ $this->orientation = $graphOnlyReport ? self::PORTRAIT : ($columnCount > $this->maxColumnCountPortraitOrientation ? self::LANDSCAPE : self::PORTRAIT);
+ }
+
+ $this->TCPDF->setPageOrientation($this->orientation, '', $this->bottomMargin);
+ }
+
+ $graphOnlyReportCount = ($graphOnlyReport && $reportHasData) ? ($graphOnlyReportCount + 1) % self::MAX_GRAPH_REPORTS : 0;
+ $tableOnly2ColumnReportCount = ($tableOnly2ColumnReport && $reportHasData) ? ($tableOnly2ColumnReportCount + 1) % self::MAX_2COL_TABLE_REPORTS : 0;
+ $tableOnlyManyColumnReportRowCount = $tableOnlyManyColumnReport ? ($tableOnlyManyColumnReportRowCount + $rowCount) : 0;
+
+ $title = $this->formatText($this->reportMetadata['name']);
+ $this->TCPDF->SetFont($this->reportFont, $this->reportFontStyle, $this->reportHeaderFontSize);
+ $this->TCPDF->SetTextColor($this->headerTextColor[0], $this->headerTextColor[1], $this->headerTextColor[2]);
+ $this->TCPDF->Bookmark($title);
+ $this->TCPDF->Cell(40, 15, $title);
+ $this->TCPDF->Ln();
+ $this->TCPDF->SetFont($this->reportFont, '', $this->reportSimpleFontSize);
+ $this->TCPDF->SetTextColor($this->reportTextColor[0], $this->reportTextColor[1], $this->reportTextColor[2]);
+ }
+
+ private function reportHasData()
+ {
+ return $this->report->getRowsCount() > 0;
+ }
+
+ private function setBorderColor()
+ {
+ $this->TCPDF->SetDrawColor($this->tableCellBorderColor[0], $this->tableCellBorderColor[1], $this->tableCellBorderColor[2]);
+ }
+
+ public function renderReport($processedReport)
+ {
+ $this->reportMetadata = $processedReport['metadata'];
+ $this->reportRowsMetadata = $processedReport['reportMetadata'];
+ $this->displayGraph = $processedReport['displayGraph'];
+ $this->evolutionGraph = $processedReport['evolutionGraph'];
+ $this->displayTable = $processedReport['displayTable'];
+ list($this->report, $this->reportColumns) = self::processTableFormat($this->reportMetadata, $processedReport['reportData'], $processedReport['columns']);
+
+ $this->paintReportHeader();
+
+ if (!$this->reportHasData()) {
+ $this->paintMessage(Piwik_Translate('CoreHome_ThereIsNoDataForThisReport'));
+ return;
+ }
+
+ if ($this->displayGraph) {
+ $this->paintGraph();
+ }
+
+ if ($this->displayGraph && $this->displayTable) {
+ $this->TCPDF->Ln(5);
+ }
+
+ if ($this->displayTable) {
+ $this->paintReportTableHeader();
+ $this->paintReportTable();
+ }
+ }
+
+ private function formatText($text)
+ {
+ return Piwik_Common::unsanitizeInputValue($text);
+ }
+
+ private function paintReportTable()
+ {
+ //Color and font restoration
+ $this->TCPDF->SetFillColor($this->tableBackgroundColor[0], $this->tableBackgroundColor[1], $this->tableBackgroundColor[2]);
+ $this->TCPDF->SetTextColor($this->reportTextColor[0], $this->reportTextColor[1], $this->reportTextColor[2]);
+ $this->TCPDF->SetFont('');
+
+ $fill = false;
+ $url = false;
+ $leftSpacesBeforeLogo = str_repeat(' ', $this->leftSpacesBeforeLogo);
+
+ $logoWidth = $this->logoWidth;
+ $logoHeight = $this->logoHeight;
+
+ $rowsMetadata = $this->reportRowsMetadata->getRows();
+
+ // Draw a body of report table
+ foreach ($this->report->getRows() as $rowId => $row) {
+ $rowMetrics = $row->getColumns();
+ $rowMetadata = isset($rowsMetadata[$rowId]) ? $rowsMetadata[$rowId]->getColumns() : array();
+ if (isset($rowMetadata['url'])) {
+ $url = $rowMetadata['url'];
+ }
+ foreach ($this->reportColumns as $columnId => $columnName) {
+ // Label column
+ if ($columnId == 'label') {
+ $isLogoDisplayable = isset($rowMetadata['logo']);
+ $text = '';
+ $posX = $this->TCPDF->GetX();
+ $posY = $this->TCPDF->GetY();
+ if (isset($rowMetrics[$columnId])) {
+ $text = substr($rowMetrics[$columnId], 0, $this->truncateAfter);
+ if ($isLogoDisplayable) {
+ $text = $leftSpacesBeforeLogo . $text;
+ }
+ }
+ $text = $this->formatText($text);
+
+ $this->TCPDF->Cell($this->labelCellWidth, $this->cellHeight, $text, 'LR', 0, 'L', $fill, $url);
+
+ if ($isLogoDisplayable) {
+ if (isset($rowMetadata['logoWidth'])) {
+ $logoWidth = $rowMetadata['logoWidth'];
+ }
+ if (isset($rowMetadata['logoHeight'])) {
+ $logoHeight = $rowMetadata['logoHeight'];
+ }
+ $restoreY = $this->TCPDF->getY();
+ $restoreX = $this->TCPDF->getX();
+ $this->TCPDF->SetY($posY);
+ $this->TCPDF->SetX($posX);
+ $topMargin = 1.3;
+ // Country flags are not very high, force a bigger top margin
+ if ($logoHeight < 16) {
+ $topMargin = 2;
+ }
+ $path = Piwik_Common::getPathToPiwikRoot() . "/" . $rowMetadata['logo'];
+ if (file_exists($path)) {
+ $this->TCPDF->Image($path, $posX + ($leftMargin = 2), $posY + $topMargin, $logoWidth / 4);
+ }
+ $this->TCPDF->SetXY($restoreX, $restoreY);
+ }
+ } // metrics column
+ else {
+ // No value means 0
+ if (empty($rowMetrics[$columnId])) {
+ $rowMetrics[$columnId] = 0;
+ }
+ $this->TCPDF->Cell($this->cellWidth, $this->cellHeight, $rowMetrics[$columnId], 'LR', 0, 'L', $fill);
+ }
+ }
+
+ $this->TCPDF->Ln();
+
+ // Top/Bottom grey border for all cells
+ $this->TCPDF->SetDrawColor($this->rowTopBottomBorder[0], $this->rowTopBottomBorder[1], $this->rowTopBottomBorder[2]);
+ $this->TCPDF->Cell($this->totalWidth, 0, '', 'T');
+ $this->setBorderColor();
+ $this->TCPDF->Ln(0.2);
+
+ $fill = !$fill;
+ }
+ }
+
+ private function paintGraph()
+ {
+ $imageGraph = parent::getStaticGraph(
+ $this->reportMetadata,
+ $this->orientation == self::PORTRAIT ? self::IMAGE_GRAPH_WIDTH_PORTRAIT : self::IMAGE_GRAPH_WIDTH_LANDSCAPE,
+ self::IMAGE_GRAPH_HEIGHT,
+ $this->evolutionGraph
+ );
+
+ $this->TCPDF->Image(
+ '@' . $imageGraph,
+ $x = '',
+ $y = '',
+ $w = 0,
+ $h = 0,
+ $type = '',
+ $link = '',
+ $align = 'N',
+ $resize = false,
+ $dpi = 72,
+ $palign = '',
+ $ismask = false,
+ $imgmask = false,
+ $order = 0,
+ $fitbox = false,
+ $hidden = false,
+ $fitonpage = true,
+ $alt = false,
+ $altimgs = array()
+ );
+
+ unset($imageGraph);
+ }
+
+ /**
+ * Draw the table header (first row)
+ */
+ private function paintReportTableHeader()
+ {
+ $initPosX = 10;
+
+ // Get the longest column name
+ $longestColumnName = '';
+ foreach ($this->reportColumns as $columnName) {
+ if (strlen($columnName) > strlen($longestColumnName)) {
+ $longestColumnName = $columnName;
+ }
+ }
+
+ $columnsCount = count($this->reportColumns);
+ // Computes available column width
+ if ($this->orientation == self::PORTRAIT
+ && $columnsCount <= 3
+ ) {
+ $totalWidth = $this->reportWidthPortrait * 2 / 3;
+ } else if ($this->orientation == self::LANDSCAPE) {
+ $totalWidth = $this->reportWidthLandscape;
+ } else {
+ $totalWidth = $this->reportWidthPortrait;
+ }
+ $this->totalWidth = $totalWidth;
+ $this->labelCellWidth = max(round(($this->totalWidth / $columnsCount)), $this->minWidthLabelCell);
+ $this->cellWidth = round(($this->totalWidth - $this->labelCellWidth) / ($columnsCount - 1));
+ $this->totalWidth = $this->labelCellWidth + ($columnsCount - 1) * $this->cellWidth;
+
+ $this->TCPDF->SetFillColor($this->tableHeaderBackgroundColor[0], $this->tableHeaderBackgroundColor[1], $this->tableHeaderBackgroundColor[2]);
+ $this->TCPDF->SetTextColor($this->tableHeaderTextColor[0], $this->tableHeaderTextColor[1], $this->tableHeaderTextColor[2]);
+ $this->TCPDF->SetLineWidth(.3);
+ $this->setBorderColor();
+ $this->TCPDF->SetFont($this->reportFont, $this->reportFontStyle);
+ $this->TCPDF->SetFillColor(255);
+ $this->TCPDF->SetTextColor($this->tableHeaderBackgroundColor[0], $this->tableHeaderBackgroundColor[1], $this->tableHeaderBackgroundColor[2]);
+ $this->TCPDF->SetDrawColor(255);
+
+ $posY = $this->TCPDF->GetY();
+ $this->TCPDF->MultiCell($this->cellWidth, $this->cellHeight, $longestColumnName, 1, 'C', true);
+ $maxCellHeight = $this->TCPDF->GetY() - $posY;
+
+ $this->TCPDF->SetFillColor($this->tableHeaderBackgroundColor[0], $this->tableHeaderBackgroundColor[1], $this->tableHeaderBackgroundColor[2]);
+ $this->TCPDF->SetTextColor($this->tableHeaderTextColor[0], $this->tableHeaderTextColor[1], $this->tableHeaderTextColor[2]);
+ $this->TCPDF->SetDrawColor($this->tableCellBorderColor[0], $this->tableCellBorderColor[1], $this->tableCellBorderColor[2]);
+
+ $this->TCPDF->SetXY($initPosX, $posY);
+
+ $countColumns = 0;
+ $posX = $initPosX;
+ foreach ($this->reportColumns as $columnName) {
+ $columnName = $this->formatText($columnName);
+ //Label column
+ if ($countColumns == 0) {
+ $this->TCPDF->MultiCell($this->labelCellWidth, $maxCellHeight, $columnName, 1, 'C', true);
+ $this->TCPDF->SetXY($posX + $this->labelCellWidth, $posY);
+ } else {
+ $this->TCPDF->MultiCell($this->cellWidth, $maxCellHeight, $columnName, 1, 'C', true);
+ $this->TCPDF->SetXY($posX + $this->cellWidth, $posY);
+ }
+ $countColumns++;
+ $posX = $this->TCPDF->GetX();
+ }
+ $this->TCPDF->Ln();
+ $this->TCPDF->SetXY($initPosX, $posY + $maxCellHeight);
+ }
+
+ /**
+ * Prints a message
+ *
+ * @param string $message
+ * @return void
+ */
+ private function paintMessage($message)
+ {
+ $this->TCPDF->SetFont($this->reportFont, $this->reportFontStyle, $this->reportSimpleFontSize);
+ $this->TCPDF->SetTextColor($this->reportTextColor[0], $this->reportTextColor[1], $this->reportTextColor[2]);
+ $message = $this->formatText($message);
+ $this->TCPDF->Write("1em", $message);
+ $this->TCPDF->Ln();
+ }
}
diff --git a/core/ScheduledTask.php b/core/ScheduledTask.php
index 689f835d8e..585d793b91 100644
--- a/core/ScheduledTask.php
+++ b/core/ScheduledTask.php
@@ -1,153 +1,152 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Piwik_ScheduledTask is used by the task scheduler and by plugins to configure runnable tasks.
- *
+ *
* @package Piwik
* @subpackage Piwik_ScheduledTask
*/
class Piwik_ScheduledTask
{
- const LOWEST_PRIORITY = 12;
- const LOW_PRIORITY = 9;
- const NORMAL_PRIORITY = 6;
- const HIGH_PRIORITY = 3;
- const HIGHEST_PRIORITY = 0;
-
- /**
- * Object instance on which the method will be executed by the task scheduler
- * @var string
- */
- var $objectInstance;
-
- /**
- * Class name where the specified method is located
- * @var string
- */
- var $className;
-
- /**
- * Class method to run when task is scheduled
- * @var string
- */
- var $methodName;
-
- /**
- * Parameter to pass to the executed method
- * @var string
- */
- var $methodParameter;
-
- /**
- * The scheduled time policy
- * @var Piwik_ScheduledTime
- */
- var $scheduledTime;
-
- /**
- * The priority of a task. Affects the order in which this task will be run.
- * @var int
- */
- var $priority;
-
- function __construct( $_objectInstance, $_methodName, $_methodParameter, $_scheduledTime, $_priority = self::NORMAL_PRIORITY )
- {
- $this->className = get_class($_objectInstance);
-
- if ($_priority < self::HIGHEST_PRIORITY || $_priority > self::LOWEST_PRIORITY)
- {
- throw new Exception("Invalid priority for ScheduledTask '$this->className.$_methodName': $_priority");
- }
-
- $this->objectInstance = $_objectInstance;
- $this->methodName = $_methodName;
- $this->scheduledTime = $_scheduledTime;
- $this->methodParameter = $_methodParameter;
- $this->priority = $_priority;
- }
-
- /**
- * Return the object instance on which the method should be executed
- * @return string
- */
- public function getObjectInstance()
- {
- return $this->objectInstance;
- }
-
- /**
- * Return class name
- * @return string
- */
- public function getClassName()
- {
- return $this->className;
- }
-
- /**
- * Return method name
- * @return string
- */
- public function getMethodName()
- {
- return $this->methodName;
- }
-
- /**
- * Return method parameter
- * @return string
- */
- public function getMethodParameter()
- {
- return $this->methodParameter;
- }
-
-
- /**
- * Return scheduled time
- * @return Piwik_ScheduledTime
- */
- public function getScheduledTime()
- {
- return $this->scheduledTime;
- }
-
- /**
- * Return the rescheduled time in milliseconds
- * @return int
- */
- public function getRescheduledTime()
- {
- return $this->getScheduledTime()->getRescheduledTime();
- }
-
- /**
- * Return the task priority. The priority will be an integer whose value is
- * between Piwik_ScheduledTask::HIGH_PRIORITY and Piwik_ScheduledTask::LOW_PRIORITY.
- *
- * @return int
- */
- public function getPriority()
- {
- return $this->priority;
- }
-
- public function getName()
- {
- return self::getTaskName($this->getClassName(), $this->getMethodName(), $this->getMethodParameter());
- }
-
- static public function getTaskName($className, $methodName, $methodParameter = null)
- {
- return $className . '.' . $methodName . ($methodParameter == null ? '' : '_' . $methodParameter);
- }
+ const LOWEST_PRIORITY = 12;
+ const LOW_PRIORITY = 9;
+ const NORMAL_PRIORITY = 6;
+ const HIGH_PRIORITY = 3;
+ const HIGHEST_PRIORITY = 0;
+
+ /**
+ * Object instance on which the method will be executed by the task scheduler
+ * @var string
+ */
+ var $objectInstance;
+
+ /**
+ * Class name where the specified method is located
+ * @var string
+ */
+ var $className;
+
+ /**
+ * Class method to run when task is scheduled
+ * @var string
+ */
+ var $methodName;
+
+ /**
+ * Parameter to pass to the executed method
+ * @var string
+ */
+ var $methodParameter;
+
+ /**
+ * The scheduled time policy
+ * @var Piwik_ScheduledTime
+ */
+ var $scheduledTime;
+
+ /**
+ * The priority of a task. Affects the order in which this task will be run.
+ * @var int
+ */
+ var $priority;
+
+ function __construct($_objectInstance, $_methodName, $_methodParameter, $_scheduledTime, $_priority = self::NORMAL_PRIORITY)
+ {
+ $this->className = get_class($_objectInstance);
+
+ if ($_priority < self::HIGHEST_PRIORITY || $_priority > self::LOWEST_PRIORITY) {
+ throw new Exception("Invalid priority for ScheduledTask '$this->className.$_methodName': $_priority");
+ }
+
+ $this->objectInstance = $_objectInstance;
+ $this->methodName = $_methodName;
+ $this->scheduledTime = $_scheduledTime;
+ $this->methodParameter = $_methodParameter;
+ $this->priority = $_priority;
+ }
+
+ /**
+ * Return the object instance on which the method should be executed
+ * @return string
+ */
+ public function getObjectInstance()
+ {
+ return $this->objectInstance;
+ }
+
+ /**
+ * Return class name
+ * @return string
+ */
+ public function getClassName()
+ {
+ return $this->className;
+ }
+
+ /**
+ * Return method name
+ * @return string
+ */
+ public function getMethodName()
+ {
+ return $this->methodName;
+ }
+
+ /**
+ * Return method parameter
+ * @return string
+ */
+ public function getMethodParameter()
+ {
+ return $this->methodParameter;
+ }
+
+
+ /**
+ * Return scheduled time
+ * @return Piwik_ScheduledTime
+ */
+ public function getScheduledTime()
+ {
+ return $this->scheduledTime;
+ }
+
+ /**
+ * Return the rescheduled time in milliseconds
+ * @return int
+ */
+ public function getRescheduledTime()
+ {
+ return $this->getScheduledTime()->getRescheduledTime();
+ }
+
+ /**
+ * Return the task priority. The priority will be an integer whose value is
+ * between Piwik_ScheduledTask::HIGH_PRIORITY and Piwik_ScheduledTask::LOW_PRIORITY.
+ *
+ * @return int
+ */
+ public function getPriority()
+ {
+ return $this->priority;
+ }
+
+ public function getName()
+ {
+ return self::getTaskName($this->getClassName(), $this->getMethodName(), $this->getMethodParameter());
+ }
+
+ static public function getTaskName($className, $methodName, $methodParameter = null)
+ {
+ return $className . '.' . $methodName . ($methodParameter == null ? '' : '_' . $methodParameter);
+ }
}
diff --git a/core/ScheduledTime.php b/core/ScheduledTime.php
index 8dc8a483ad..0edcd839fd 100644
--- a/core/ScheduledTime.php
+++ b/core/ScheduledTime.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -19,97 +19,98 @@
*/
abstract class Piwik_ScheduledTime
{
- const PERIOD_NEVER = 'never';
- const PERIOD_DAY = 'day';
- const PERIOD_WEEK = 'week';
- const PERIOD_MONTH = 'month';
+ const PERIOD_NEVER = 'never';
+ const PERIOD_DAY = 'day';
+ const PERIOD_WEEK = 'week';
+ const PERIOD_MONTH = 'month';
+
+ /**
+ * @link http://php.net/manual/en/function.date.php, format string : 'G'
+ * Defaults to midnight
+ * @var integer
+ */
+ public $hour = 0;
- /**
- * @link http://php.net/manual/en/function.date.php, format string : 'G'
- * Defaults to midnight
- * @var integer
- */
- public $hour = 0;
-
- /**
- * For weekly scheduling : http://php.net/manual/en/function.date.php, format string : 'N', defaults to Monday
- * For monthly scheduling : day of the month (1 to 31) (note: will be capped at the latest day available the
- * month), defaults to first day of the month
- * @var integer
- */
- public $day = 1;
+ /**
+ * For weekly scheduling : http://php.net/manual/en/function.date.php, format string : 'N', defaults to Monday
+ * For monthly scheduling : day of the month (1 to 31) (note: will be capped at the latest day available the
+ * month), defaults to first day of the month
+ * @var integer
+ */
+ public $day = 1;
- static public function getScheduledTimeForPeriod($period)
- {
- switch($period)
- {
- case self::PERIOD_MONTH: return new Piwik_ScheduledTime_Monthly();
- case self::PERIOD_WEEK: return new Piwik_ScheduledTime_Weekly();
- case self::PERIOD_DAY: return new Piwik_ScheduledTime_Daily();
+ static public function getScheduledTimeForPeriod($period)
+ {
+ switch ($period) {
+ case self::PERIOD_MONTH:
+ return new Piwik_ScheduledTime_Monthly();
+ case self::PERIOD_WEEK:
+ return new Piwik_ScheduledTime_Weekly();
+ case self::PERIOD_DAY:
+ return new Piwik_ScheduledTime_Daily();
- default: throw new Exception('period ' . $period . 'is undefined.');
- }
- }
+ default:
+ throw new Exception('period ' . $period . 'is undefined.');
+ }
+ }
- /**
- * Returns the system time used by subclasses to compute schedulings.
- * This method has been introduced so unit tests can override the current system time.
+ /**
+ * Returns the system time used by subclasses to compute schedulings.
+ * This method has been introduced so unit tests can override the current system time.
* @return int
- */
- protected function getTime()
- {
- return time();
- }
+ */
+ protected function getTime()
+ {
+ return time();
+ }
- /**
- * Computes the next scheduled time based on the system time at which the method has been called and
- * the underlying scheduling interval.
- *
- * @abstract
- * @return integer Returns the rescheduled time measured in the number of seconds since the Unix Epoch
- */
- abstract public function getRescheduledTime();
+ /**
+ * Computes the next scheduled time based on the system time at which the method has been called and
+ * the underlying scheduling interval.
+ *
+ * @abstract
+ * @return integer Returns the rescheduled time measured in the number of seconds since the Unix Epoch
+ */
+ abstract public function getRescheduledTime();
- /**
+ /**
* @abstract
- * @param int $_day the day to set
- * @throws Exception if method not supported by subclass or parameter _day is invalid
- */
- abstract public function setDay($_day);
+ * @param int $_day the day to set
+ * @throws Exception if method not supported by subclass or parameter _day is invalid
+ */
+ abstract public function setDay($_day);
+
+ /**
+ * @param int $_hour the hour to set, has to be >= 0 and < 24
+ * @throws Exception if method not supported by subclass or parameter _hour is invalid
+ */
+ public function setHour($_hour)
+ {
+ if (!($_hour >= 0 && $_hour < 24)) {
+ throw new Exception ("Invalid hour parameter, must be >=0 and < 24");
+ }
- /**
- * @param int $_hour the hour to set, has to be >= 0 and < 24
- * @throws Exception if method not supported by subclass or parameter _hour is invalid
- */
- public function setHour($_hour)
- {
- if (!($_hour >=0 && $_hour < 24))
- {
- throw new Exception ("Invalid hour parameter, must be >=0 and < 24");
- }
+ $this->hour = $_hour;
+ }
- $this->hour = $_hour;
- }
-
- /**
- * Computes the delta in seconds needed to adjust the rescheduled time to the required hour.
- *
- * @param int $rescheduledTime The rescheduled time to be adjusted
- * @return int adjusted rescheduled time
- */
- protected function adjustHour ($rescheduledTime)
- {
- if ( $this->hour !== null )
- {
- // Reset the number of minutes and set the scheduled hour to the one specified with setHour()
- $rescheduledTime = mktime ( $this->hour,
- 0,
- date('s', $rescheduledTime),
- date('n', $rescheduledTime),
- date('j', $rescheduledTime),
- date('Y', $rescheduledTime)
- );
- }
- return $rescheduledTime;
- }
+ /**
+ * Computes the delta in seconds needed to adjust the rescheduled time to the required hour.
+ *
+ * @param int $rescheduledTime The rescheduled time to be adjusted
+ * @return int adjusted rescheduled time
+ */
+ protected function adjustHour($rescheduledTime)
+ {
+ if ($this->hour !== null) {
+ // Reset the number of minutes and set the scheduled hour to the one specified with setHour()
+ $rescheduledTime = mktime($this->hour,
+ 0,
+ date('s', $rescheduledTime),
+ date('n', $rescheduledTime),
+ date('j', $rescheduledTime),
+ date('Y', $rescheduledTime)
+ );
+ }
+ return $rescheduledTime;
+ }
}
diff --git a/core/ScheduledTime/Daily.php b/core/ScheduledTime/Daily.php
index ed228fe8cc..46efdd0ddf 100644
--- a/core/ScheduledTime/Daily.php
+++ b/core/ScheduledTime/Daily.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -17,28 +17,28 @@
* @subpackage Piwik_ScheduledTime
*/
class Piwik_ScheduledTime_Daily extends Piwik_ScheduledTime
-{
- public function getRescheduledTime()
- {
- $currentTime = $this->getTime();
-
- // Add one day
- $rescheduledTime = mktime ( date('H', $currentTime),
- date('i', $currentTime),
- date('s', $currentTime),
- date('n', $currentTime),
- date('j', $currentTime) + 1,
- date('Y', $currentTime)
- );
+{
+ public function getRescheduledTime()
+ {
+ $currentTime = $this->getTime();
+
+ // Add one day
+ $rescheduledTime = mktime(date('H', $currentTime),
+ date('i', $currentTime),
+ date('s', $currentTime),
+ date('n', $currentTime),
+ date('j', $currentTime) + 1,
+ date('Y', $currentTime)
+ );
+
+ // Adjusts the scheduled hour
+ $rescheduledTime = $this->adjustHour($rescheduledTime);
- // Adjusts the scheduled hour
- $rescheduledTime = $this->adjustHour($rescheduledTime);
+ return $rescheduledTime;
+ }
- return $rescheduledTime;
- }
-
- public function setDay($_day)
- {
- throw new Exception ("Method not supported");
- }
+ public function setDay($_day)
+ {
+ throw new Exception ("Method not supported");
+ }
}
diff --git a/core/ScheduledTime/Hourly.php b/core/ScheduledTime/Hourly.php
index 6c85d15964..fa5a4a3230 100644
--- a/core/ScheduledTime/Hourly.php
+++ b/core/ScheduledTime/Hourly.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -18,28 +18,28 @@
*/
class Piwik_ScheduledTime_Hourly extends Piwik_ScheduledTime
{
- public function getRescheduledTime()
- {
- $currentTime = $this->getTime();
-
- // Adds one hour and reset the number of minutes
- $rescheduledTime = mktime ( date('H', $currentTime) + 1,
- 0,
- date('s', $currentTime),
- date('n', $currentTime),
- date('j', $currentTime),
- date('Y', $currentTime)
- );
- return $rescheduledTime;
- }
+ public function getRescheduledTime()
+ {
+ $currentTime = $this->getTime();
+
+ // Adds one hour and reset the number of minutes
+ $rescheduledTime = mktime(date('H', $currentTime) + 1,
+ 0,
+ date('s', $currentTime),
+ date('n', $currentTime),
+ date('j', $currentTime),
+ date('Y', $currentTime)
+ );
+ return $rescheduledTime;
+ }
+
+ public function setHour($_hour)
+ {
+ throw new Exception ("Method not supported");
+ }
- public function setHour($_hour)
- {
- throw new Exception ("Method not supported");
- }
-
- public function setDay($_day)
- {
- throw new Exception ("Method not supported");
- }
+ public function setDay($_day)
+ {
+ throw new Exception ("Method not supported");
+ }
}
diff --git a/core/ScheduledTime/Monthly.php b/core/ScheduledTime/Monthly.php
index c259af0051..e09777a23a 100644
--- a/core/ScheduledTime/Monthly.php
+++ b/core/ScheduledTime/Monthly.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -18,106 +18,101 @@
*/
class Piwik_ScheduledTime_Monthly extends Piwik_ScheduledTime
{
- /**
- * Day of the week for scheduled time.
- *
- * @var int
- */
- private $dayOfWeek = null;
-
- /**
- * Week number for scheduled time.
- *
- * @var int
- */
- private $week = null;
-
+ /**
+ * Day of the week for scheduled time.
+ *
+ * @var int
+ */
+ private $dayOfWeek = null;
+
+ /**
+ * Week number for scheduled time.
+ *
+ * @var int
+ */
+ private $week = null;
+
/**
* @return int
*/
public function getRescheduledTime()
- {
- $currentTime = $this->getTime();
+ {
+ $currentTime = $this->getTime();
- // Adds one month
- $rescheduledTime = mktime ( date('H', $currentTime),
- date('i', $currentTime),
- date('s', $currentTime),
- date('n', $currentTime) + 1,
- 1,
- date('Y', $currentTime)
- );
+ // Adds one month
+ $rescheduledTime = mktime(date('H', $currentTime),
+ date('i', $currentTime),
+ date('s', $currentTime),
+ date('n', $currentTime) + 1,
+ 1,
+ date('Y', $currentTime)
+ );
- $nextMonthLength = date('t', $rescheduledTime);
+ $nextMonthLength = date('t', $rescheduledTime);
- // Sets scheduled day
+ // Sets scheduled day
$scheduledDay = date('j', $currentTime);
- if ( $this->day !== null )
- {
- $scheduledDay = $this->day;
- }
-
- if ($this->dayOfWeek !== null
- && $this->week !== null)
- {
- $newTime = $rescheduledTime + $this->week * 7 * 86400;
- while (date("w", $newTime) != $this->dayOfWeek % 7) // modulus for sanity check
- {
- $newTime += 86400;
- }
- $scheduledDay = ($newTime - $rescheduledTime) / 86400 + 1;
- }
-
- // Caps scheduled day
- if ( $scheduledDay > $nextMonthLength )
- {
- $scheduledDay = $nextMonthLength;
- }
-
- // Adjusts the scheduled day
- $rescheduledTime += ($scheduledDay - 1) * 86400;
-
- // Adjusts the scheduled hour
- $rescheduledTime = $this->adjustHour($rescheduledTime);
-
- return $rescheduledTime;
- }
-
- /**
- * @param int $_day the day to set, has to be >= 1 and < 32
- * @throws Exception if parameter _day is invalid
- */
- public function setDay($_day)
- {
- if (!($_day >=1 && $_day < 32))
- {
- throw new Exception ("Invalid day parameter, must be >=1 and < 32");
- }
-
- $this->day = $_day;
- }
-
- /**
- * Makes this scheduled time execute on a particular day of the week on each month.
- *
- * @param int $_day the day of the week to use, between 0-6 (inclusive). 0 -> Sunday
- * @param int $_week the week to use, between 0-3 (inclusive)
- * @throws Exception if either parameter is invalid
- */
- public function setDayOfWeek($_day, $_week)
- {
- if (!($_day >= 0 && $_day < 7))
- {
- throw new Exception("Invalid day of week parameter, must be >= 0 & < 7");
- }
-
- if (!($_week >= 0 && $_week < 4))
- {
- throw new Exception("Invalid week number, must be >= 1 & < 4");
- }
-
- $this->dayOfWeek = $_day;
- $this->week = $_week;
- }
+ if ($this->day !== null) {
+ $scheduledDay = $this->day;
+ }
+
+ if ($this->dayOfWeek !== null
+ && $this->week !== null
+ ) {
+ $newTime = $rescheduledTime + $this->week * 7 * 86400;
+ while (date("w", $newTime) != $this->dayOfWeek % 7) // modulus for sanity check
+ {
+ $newTime += 86400;
+ }
+ $scheduledDay = ($newTime - $rescheduledTime) / 86400 + 1;
+ }
+
+ // Caps scheduled day
+ if ($scheduledDay > $nextMonthLength) {
+ $scheduledDay = $nextMonthLength;
+ }
+
+ // Adjusts the scheduled day
+ $rescheduledTime += ($scheduledDay - 1) * 86400;
+
+ // Adjusts the scheduled hour
+ $rescheduledTime = $this->adjustHour($rescheduledTime);
+
+ return $rescheduledTime;
+ }
+
+ /**
+ * @param int $_day the day to set, has to be >= 1 and < 32
+ * @throws Exception if parameter _day is invalid
+ */
+ public function setDay($_day)
+ {
+ if (!($_day >= 1 && $_day < 32)) {
+ throw new Exception ("Invalid day parameter, must be >=1 and < 32");
+ }
+
+ $this->day = $_day;
+ }
+
+ /**
+ * Makes this scheduled time execute on a particular day of the week on each month.
+ *
+ * @param int $_day the day of the week to use, between 0-6 (inclusive). 0 -> Sunday
+ * @param int $_week the week to use, between 0-3 (inclusive)
+ * @throws Exception if either parameter is invalid
+ */
+ public function setDayOfWeek($_day, $_week)
+ {
+ if (!($_day >= 0 && $_day < 7)) {
+ throw new Exception("Invalid day of week parameter, must be >= 0 & < 7");
+ }
+
+ if (!($_week >= 0 && $_week < 4)) {
+ throw new Exception("Invalid week number, must be >= 1 & < 4");
+ }
+
+ $this->dayOfWeek = $_day;
+ $this->week = $_week;
+ }
}
diff --git a/core/ScheduledTime/Weekly.php b/core/ScheduledTime/Weekly.php
index fe2f0eee0f..d47cf53350 100644
--- a/core/ScheduledTime/Weekly.php
+++ b/core/ScheduledTime/Weekly.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -18,50 +18,48 @@
*/
class Piwik_ScheduledTime_Weekly extends Piwik_ScheduledTime
{
-
- public function getRescheduledTime()
- {
- $currentTime = $this->getTime();
-
- // Adds 7 days
- $rescheduledTime = mktime ( date('H', $currentTime),
- date('i', $currentTime),
- date('s', $currentTime),
- date('n', $currentTime),
- date('j', $currentTime) + 7,
- date('Y', $currentTime)
- );
-
- // Adjusts the scheduled hour
- $rescheduledTime = $this->adjustHour($rescheduledTime);
-
- // Adjusts the scheduled day
- if ( $this->day !== null )
- {
- // Removes or adds a number of days to set the scheduled day to the one specified with setDay()
- $rescheduledTime = mktime ( date('H', $rescheduledTime),
- date('i', $rescheduledTime),
- date('s', $rescheduledTime),
- date('n', $rescheduledTime),
- date('j', $rescheduledTime) - (date('N', $rescheduledTime) - $this->day),
- date('Y', $rescheduledTime)
- );
- }
-
- return $rescheduledTime;
- }
-
- /**
- * @param int $_day the day to set, has to be >= 1 and < 8
- * @throws Exception if parameter _day is invalid
- */
- public function setDay($_day)
- {
- if (!($_day >=1 && $_day < 8))
- {
- throw new Exception ("Invalid day parameter, must be >=1 and < 8");
- }
- $this->day = $_day;
- }
+ public function getRescheduledTime()
+ {
+ $currentTime = $this->getTime();
+
+ // Adds 7 days
+ $rescheduledTime = mktime(date('H', $currentTime),
+ date('i', $currentTime),
+ date('s', $currentTime),
+ date('n', $currentTime),
+ date('j', $currentTime) + 7,
+ date('Y', $currentTime)
+ );
+
+ // Adjusts the scheduled hour
+ $rescheduledTime = $this->adjustHour($rescheduledTime);
+
+ // Adjusts the scheduled day
+ if ($this->day !== null) {
+ // Removes or adds a number of days to set the scheduled day to the one specified with setDay()
+ $rescheduledTime = mktime(date('H', $rescheduledTime),
+ date('i', $rescheduledTime),
+ date('s', $rescheduledTime),
+ date('n', $rescheduledTime),
+ date('j', $rescheduledTime) - (date('N', $rescheduledTime) - $this->day),
+ date('Y', $rescheduledTime)
+ );
+ }
+
+ return $rescheduledTime;
+ }
+
+ /**
+ * @param int $_day the day to set, has to be >= 1 and < 8
+ * @throws Exception if parameter _day is invalid
+ */
+ public function setDay($_day)
+ {
+ if (!($_day >= 1 && $_day < 8)) {
+ throw new Exception ("Invalid day parameter, must be >=1 and < 8");
+ }
+
+ $this->day = $_day;
+ }
}
diff --git a/core/Segment.php b/core/Segment.php
index a72c7b0797..7802ba440d 100644
--- a/core/Segment.php
+++ b/core/Segment.php
@@ -19,24 +19,24 @@ class Piwik_Segment
* @var Piwik_SegmentExpression
*/
protected $segment = null;
-
+
/**
* Truncate the Segments to 4k
*/
const SEGMENT_TRUNCATE_LIMIT = 4096;
-
+
public function __construct($string, $idSites)
{
- $string = Piwik_Common::unsanitizeInputValue($string);
+ $string = Piwik_Common::unsanitizeInputValue($string);
$string = trim($string);
- if( !Piwik_Archive::isSegmentationEnabled()
- && !empty($string))
- {
- throw new Exception("The Super User has disabled the Segmentation feature.");
- }
+ if (!Piwik_Archive::isSegmentationEnabled()
+ && !empty($string)
+ ) {
+ throw new Exception("The Super User has disabled the Segmentation feature.");
+ }
// 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->idSites = $idSites;
$segment = new Piwik_SegmentExpression($string);
@@ -44,13 +44,12 @@ class Piwik_Segment
// parse segments
$expressions = $segment->parseSubExpressions();
-
+
// convert segments name to sql segment
// check that user is allowed to view this segment
// and apply a filter to the value to match if necessary (to map DB fields format)
$cleanedExpressions = array();
- foreach($expressions as $expression)
- {
+ foreach ($expressions as $expression) {
$operand = $expression[Piwik_SegmentExpression::INDEX_OPERAND];
$cleanedExpression = $this->getCleanedExpression($operand);
$expression[Piwik_SegmentExpression::INDEX_OPERAND] = $cleanedExpression;
@@ -58,16 +57,17 @@ class Piwik_Segment
}
$segment->setSubExpressionsAfterCleanup($cleanedExpressions);
}
-
+
public function getPrettyString()
{
- //@TODO segment.getPrettyString
+ //@TODO segment.getPrettyString
}
-
+
public function isEmpty()
{
return empty($this->string);
}
+
protected $availableSegments = array();
protected $segmentsHumanReadable = '';
@@ -75,353 +75,313 @@ class Piwik_Segment
{
$expressions = $this->segment->parsedSubExpressions;
$uniqueFields = array();
- foreach($expressions as $expression)
- {
- $uniqueFields[] = $expression[Piwik_SegmentExpression::INDEX_OPERAND][0];
+ foreach ($expressions as $expression) {
+ $uniqueFields[] = $expression[Piwik_SegmentExpression::INDEX_OPERAND][0];
}
return $uniqueFields;
}
-
+
protected function getCleanedExpression($expression)
{
- if(empty($this->availableSegments))
- {
+ if (empty($this->availableSegments)) {
$this->availableSegments = Piwik_API_API::getInstance()->getSegmentsMetadata($this->idSites, $_hideImplementationData = false);
}
-
+
$name = $expression[0];
$matchType = $expression[1];
$value = $expression[2];
$sqlName = '';
-
- foreach($this->availableSegments as $segment)
- {
- if($segment['segment'] != $name)
- {
+
+ foreach ($this->availableSegments as $segment) {
+ if ($segment['segment'] != $name) {
continue;
}
-
+
$sqlName = $segment['sqlSegment'];
-
+
// check permission
- if(isset($segment['permission'])
- && $segment['permission'] != 1)
- {
- throw new Exception("You do not have enough permission to access the segment ".$name);
+ if (isset($segment['permission'])
+ && $segment['permission'] != 1
+ ) {
+ throw new Exception("You do not have enough permission to access the segment " . $name);
}
-
+
// $this->segmentsHumanReadable[] = $segment['name'] . " " .
// $this->getNameForMatchType($matchType) .
// $value;
-
+
// apply presentation filter
- if(isset($segment['sqlFilter'])
- && !empty($segment['sqlFilter']))
- {
+ if (isset($segment['sqlFilter'])
+ && !empty($segment['sqlFilter'])
+ ) {
$value = call_user_func($segment['sqlFilter'], $value, $segment['sqlSegment'], $matchType);
-
+
// sqlFilter-callbacks might return arrays for more complex cases
// e.g. see Piwik_Actions::getIdActionFromSegment()
if (is_array($value)
- && isset($value['SQL']))
- {
+ && isset($value['SQL'])
+ ) {
// Special case: returned value is a sub sql expression!
$matchType = Piwik_SegmentExpression::MATCH_ACTIONS_CONTAINS;
}
}
break;
}
-
- if(empty($sqlName))
- {
+
+ if (empty($sqlName)) {
throw new Exception("Segment '$name' is not a supported segment.");
}
-
- return array( $sqlName, $matchType, $value );
+
+ return array($sqlName, $matchType, $value);
}
-
+
public function getString()
{
return $this->string;
}
-
+
public function getHash()
{
- if(empty($this->string))
- {
+ if (empty($this->string)) {
return '';
}
return md5($this->string);
}
- /**
- * Extend SQL query with segment expressions
- *
- * @param string $select select clause
- * @param array $from array of table names (without prefix)
- * @param bool|string $where (optional )where clause
- * @param array|string $bind (optional) params to bind
- * @param bool|string $orderBy (optional) order by clause
- * @param bool|string $groupBy (optional) group by clause
- * @return string entire select query
- */
- public function getSelectQuery($select, $from, $where=false, $bind=array(), $orderBy=false, $groupBy=false)
+ /**
+ * Extend SQL query with segment expressions
+ *
+ * @param string $select select clause
+ * @param array $from array of table names (without prefix)
+ * @param bool|string $where (optional )where clause
+ * @param array|string $bind (optional) params to bind
+ * @param bool|string $orderBy (optional) order by clause
+ * @param bool|string $groupBy (optional) group by clause
+ * @return string entire select query
+ */
+ public function getSelectQuery($select, $from, $where = false, $bind = array(), $orderBy = false, $groupBy = false)
{
- $joinWithSubSelect = false;
-
- if (!is_array($from))
- {
- $from = array($from);
- }
-
- if (!$this->isEmpty())
- {
- $this->segment->parseSubExpressionsIntoSqlExpressions($from);
-
- $joins = $this->generateJoins($from);
- $from = $joins['sql'];
- $joinWithSubSelect = $joins['joinWithSubSelect'];
-
- $segmentSql = $this->segment->getSql();
- $segmentWhere = $segmentSql['where'];
- if (!empty($segmentWhere))
- {
- if (!empty($where))
- {
- $where = "( $where )
+ $joinWithSubSelect = false;
+
+ if (!is_array($from)) {
+ $from = array($from);
+ }
+
+ if (!$this->isEmpty()) {
+ $this->segment->parseSubExpressionsIntoSqlExpressions($from);
+
+ $joins = $this->generateJoins($from);
+ $from = $joins['sql'];
+ $joinWithSubSelect = $joins['joinWithSubSelect'];
+
+ $segmentSql = $this->segment->getSql();
+ $segmentWhere = $segmentSql['where'];
+ if (!empty($segmentWhere)) {
+ if (!empty($where)) {
+ $where = "( $where )
AND
($segmentWhere)";
- }
- else
- {
- $where = $segmentWhere;
- }
- }
-
- $bind = array_merge($bind, $segmentSql['bind']);
- }
- else
- {
- $joins = $this->generateJoins($from);
- $from = $joins['sql'];
- $joinWithSubSelect = $joins['joinWithSubSelect'];
- }
-
- if ($joinWithSubSelect)
- {
- $sql = $this->buildWrappedSelectQuery($select, $from, $where, $orderBy, $groupBy);
- }
- else
- {
- $sql = $this->buildSelectQuery($select, $from, $where, $orderBy, $groupBy);
- }
-
- return array(
- 'sql' => $sql,
- 'bind' => $bind
- );
+ } else {
+ $where = $segmentWhere;
+ }
+ }
+
+ $bind = array_merge($bind, $segmentSql['bind']);
+ } else {
+ $joins = $this->generateJoins($from);
+ $from = $joins['sql'];
+ $joinWithSubSelect = $joins['joinWithSubSelect'];
+ }
+
+ if ($joinWithSubSelect) {
+ $sql = $this->buildWrappedSelectQuery($select, $from, $where, $orderBy, $groupBy);
+ } else {
+ $sql = $this->buildSelectQuery($select, $from, $where, $orderBy, $groupBy);
+ }
+
+ return array(
+ 'sql' => $sql,
+ 'bind' => $bind
+ );
}
- /**
- * Generate the join sql based on the needed tables
- * @param array $tables tables to join
- * @throws Exception if tables can't be joined
- * @return array
- */
+ /**
+ * Generate the join sql based on the needed tables
+ * @param array $tables tables to join
+ * @throws Exception if tables can't be joined
+ * @return array
+ */
private function generateJoins($tables)
{
- $knownTables = array("log_visit", "log_link_visit_action", "log_conversion");
- $visitsAvailable = $actionsAvailable = $conversionsAvailable = false;
- $joinWithSubSelect = false;
- $sql = '';
-
- // make sure the tables are joined in the right order
- // base table first, then action before conversion
- // this way, conversions can be joined on idlink_va
- $actionIndex = array_search("log_link_visit_action", $tables);
- $conversionIndex = array_search("log_conversion", $tables);
- if ($actionIndex > 0 && $conversionIndex > 0 && $actionIndex > $conversionIndex)
- {
- $tables[$actionIndex] = "log_conversion";
- $tables[$conversionIndex] = "log_link_visit_action";
- }
-
- // same as above: action before visit
- $actionIndex = array_search("log_link_visit_action", $tables);
- $visitIndex = array_search("log_visit", $tables);
- if ($actionIndex > 0 && $visitIndex > 0 && $actionIndex > $visitIndex)
- {
- $tables[$actionIndex] = "log_visit";
- $tables[$visitIndex] = "log_link_visit_action";
- }
-
- foreach ($tables as $i => $table)
- {
- if (is_array($table))
- {
- // join condition provided
- $alias = isset($table['tableAlias']) ? $table['tableAlias'] : $table['table'];
- $sql .= "
- LEFT JOIN ".Piwik_Common::prefixTable($table['table'])." AS ".$alias
- ." ON ".$table['joinOn'];
- continue;
- }
-
- if (!in_array($table, $knownTables))
- {
- throw new Exception("Table '$table' can't be used for segmentation");
- }
-
- $tableSql = Piwik_Common::prefixTable($table)." AS $table";
-
- if ($i == 0)
- {
- // first table
- $sql .= $tableSql;
- }
- else
- {
- $join = "";
-
- if ($actionsAvailable && $table == "log_conversion")
- {
- // have actions, need conversions => join on idlink_va
- $join = "log_conversion.idlink_va = log_link_visit_action.idlink_va "
- ."AND log_conversion.idsite = log_link_visit_action.idsite";
- }
- else if ($actionsAvailable && $table == "log_visit")
- {
- // have actions, need visits => join on idvisit
- $join = "log_visit.idvisit = log_link_visit_action.idvisit";
- }
- else if ($visitsAvailable && $table == "log_link_visit_action")
- {
- // have visits, need actions => we have to use a more complex join
- // we don't hande this here, we just return joinWithSubSelect=true in this case
- $joinWithSubSelect = true;
- $join = "log_link_visit_action.idvisit = log_visit.idvisit";
- }
- else if ($conversionsAvailable && $table == "log_link_visit_action")
- {
- // have conversions, need actions => join on idlink_va
- $join = "log_conversion.idlink_va = log_link_visit_action.idlink_va";
- }
- else if (($visitsAvailable && $table == "log_conversion")
- ||($conversionsAvailable && $table == "log_visit"))
- {
- // have visits, need conversion (or vice versa) => join on idvisit
- // notice that joining conversions on visits has lower priority than joining it on actions
- $join = "log_conversion.idvisit = log_visit.idvisit";
-
- // if conversions are joined on visits, we need a complex join
- if ($table == "log_conversion")
- {
- $joinWithSubSelect = true;
- }
- }
- else
- {
- throw new Exception("Table '$table', can't be joined for segmentation");
- }
-
- // the join sql the default way
- $sql .= "
+ $knownTables = array("log_visit", "log_link_visit_action", "log_conversion");
+ $visitsAvailable = $actionsAvailable = $conversionsAvailable = false;
+ $joinWithSubSelect = false;
+ $sql = '';
+
+ // make sure the tables are joined in the right order
+ // base table first, then action before conversion
+ // this way, conversions can be joined on idlink_va
+ $actionIndex = array_search("log_link_visit_action", $tables);
+ $conversionIndex = array_search("log_conversion", $tables);
+ if ($actionIndex > 0 && $conversionIndex > 0 && $actionIndex > $conversionIndex) {
+ $tables[$actionIndex] = "log_conversion";
+ $tables[$conversionIndex] = "log_link_visit_action";
+ }
+
+ // same as above: action before visit
+ $actionIndex = array_search("log_link_visit_action", $tables);
+ $visitIndex = array_search("log_visit", $tables);
+ if ($actionIndex > 0 && $visitIndex > 0 && $actionIndex > $visitIndex) {
+ $tables[$actionIndex] = "log_visit";
+ $tables[$visitIndex] = "log_link_visit_action";
+ }
+
+ foreach ($tables as $i => $table) {
+ if (is_array($table)) {
+ // join condition provided
+ $alias = isset($table['tableAlias']) ? $table['tableAlias'] : $table['table'];
+ $sql .= "
+ LEFT JOIN " . Piwik_Common::prefixTable($table['table']) . " AS " . $alias
+ . " ON " . $table['joinOn'];
+ continue;
+ }
+
+ if (!in_array($table, $knownTables)) {
+ throw new Exception("Table '$table' can't be used for segmentation");
+ }
+
+ $tableSql = Piwik_Common::prefixTable($table) . " AS $table";
+
+ if ($i == 0) {
+ // first table
+ $sql .= $tableSql;
+ } else {
+ $join = "";
+
+ if ($actionsAvailable && $table == "log_conversion") {
+ // have actions, need conversions => join on idlink_va
+ $join = "log_conversion.idlink_va = log_link_visit_action.idlink_va "
+ . "AND log_conversion.idsite = log_link_visit_action.idsite";
+ } else if ($actionsAvailable && $table == "log_visit") {
+ // have actions, need visits => join on idvisit
+ $join = "log_visit.idvisit = log_link_visit_action.idvisit";
+ } else if ($visitsAvailable && $table == "log_link_visit_action") {
+ // have visits, need actions => we have to use a more complex join
+ // we don't hande this here, we just return joinWithSubSelect=true in this case
+ $joinWithSubSelect = true;
+ $join = "log_link_visit_action.idvisit = log_visit.idvisit";
+ } else if ($conversionsAvailable && $table == "log_link_visit_action") {
+ // have conversions, need actions => join on idlink_va
+ $join = "log_conversion.idlink_va = log_link_visit_action.idlink_va";
+ } else if (($visitsAvailable && $table == "log_conversion")
+ || ($conversionsAvailable && $table == "log_visit")
+ ) {
+ // have visits, need conversion (or vice versa) => join on idvisit
+ // notice that joining conversions on visits has lower priority than joining it on actions
+ $join = "log_conversion.idvisit = log_visit.idvisit";
+
+ // if conversions are joined on visits, we need a complex join
+ if ($table == "log_conversion") {
+ $joinWithSubSelect = true;
+ }
+ } else {
+ throw new Exception("Table '$table', can't be joined for segmentation");
+ }
+
+ // the join sql the default way
+ $sql .= "
LEFT JOIN $tableSql ON $join";
- }
-
- // remember which tables are available
- $visitsAvailable = ($visitsAvailable || $table == "log_visit");
- $actionsAvailable = ($actionsAvailable || $table == "log_link_visit_action");
- $conversionsAvailable = ($conversionsAvailable || $table == "log_conversion");
- }
-
- return array(
- 'sql' => $sql,
- 'joinWithSubSelect' => $joinWithSubSelect
- );
+ }
+
+ // remember which tables are available
+ $visitsAvailable = ($visitsAvailable || $table == "log_visit");
+ $actionsAvailable = ($actionsAvailable || $table == "log_link_visit_action");
+ $conversionsAvailable = ($conversionsAvailable || $table == "log_conversion");
+ }
+
+ return array(
+ 'sql' => $sql,
+ 'joinWithSubSelect' => $joinWithSubSelect
+ );
}
- /**
- * Build select query the normal way
- * @param string $select fieldlist to be selected
- * @param string $from tablelist to select from
- * @param string $where where clause
- * @param string $orderBy order by clause
- * @param string $groupBy group by clause
- * @return string
- */
+ /**
+ * Build select query the normal way
+ * @param string $select fieldlist to be selected
+ * @param string $from tablelist to select from
+ * @param string $where where clause
+ * @param string $orderBy order by clause
+ * @param string $groupBy group by clause
+ * @return string
+ */
private function buildSelectQuery($select, $from, $where, $orderBy, $groupBy)
{
- $sql = "
+ $sql = "
SELECT
$select
FROM
$from";
-
- if ($where)
- {
- $sql .= "
+
+ if ($where) {
+ $sql .= "
WHERE
$where";
- }
+ }
- if ($groupBy)
- {
- $sql .= "
+ if ($groupBy) {
+ $sql .= "
GROUP BY
$groupBy";
- }
-
- if ($orderBy)
- {
- $sql .= "
+ }
+
+ if ($orderBy) {
+ $sql .= "
ORDER BY
$orderBy";
- }
-
- return $sql;
+ }
+
+ return $sql;
}
- /**
- * Build a select query where actions have to be joined on visits (or conversions)
- * In this case, the query gets wrapped in another query so that grouping by visit is possible
- * @param string $select
- * @param string $from
- * @param string $where
- * @param string $orderBy
- * @param string $groupBy
- * @throws Exception
- * @return string
- */
+ /**
+ * Build a select query where actions have to be joined on visits (or conversions)
+ * In this case, the query gets wrapped in another query so that grouping by visit is possible
+ * @param string $select
+ * @param string $from
+ * @param string $where
+ * @param string $orderBy
+ * @param string $groupBy
+ * @throws Exception
+ * @return string
+ */
private function buildWrappedSelectQuery($select, $from, $where, $orderBy, $groupBy)
- {
- preg_match_all("/(log_visit|log_conversion|log_action).[a-z0-9_\*]+/", $select, $matches);
- $neededFields = array_unique($matches[0]);
-
- if (count($neededFields) == 0)
- {
- throw new Exception("No needed fields found in select expression. "
- ."Please use a table prefix.");
- }
-
- $select = preg_replace('/(log_visit|log_conversion|log_action)\./', 'log_inner.', $select);
- $orderBy = preg_replace('/(log_visit|log_conversion|log_action)\./', 'log_inner.', $orderBy);
- $groupBy = preg_replace('/(log_visit|log_conversion|log_action)\./', 'log_inner.', $groupBy);
-
- $from = "(
+ {
+ preg_match_all("/(log_visit|log_conversion|log_action).[a-z0-9_\*]+/", $select, $matches);
+ $neededFields = array_unique($matches[0]);
+
+ if (count($neededFields) == 0) {
+ throw new Exception("No needed fields found in select expression. "
+ . "Please use a table prefix.");
+ }
+
+ $select = preg_replace('/(log_visit|log_conversion|log_action)\./', 'log_inner.', $select);
+ $orderBy = preg_replace('/(log_visit|log_conversion|log_action)\./', 'log_inner.', $orderBy);
+ $groupBy = preg_replace('/(log_visit|log_conversion|log_action)\./', 'log_inner.', $groupBy);
+
+ $from = "(
SELECT
- ".implode(",
- ", $neededFields)."
+ " . implode(",
+ ", $neededFields) . "
FROM
$from
WHERE
$where
GROUP BY log_visit.idvisit
) AS log_inner";
-
- $where = false;
- return $this->buildSelectQuery($select, $from, $where, $orderBy, $groupBy);
+
+ $where = false;
+ return $this->buildSelectQuery($select, $from, $where, $orderBy, $groupBy);
}
-
+
}
diff --git a/core/SegmentExpression.php b/core/SegmentExpression.php
index aaae328462..9340aec4bd 100644
--- a/core/SegmentExpression.php
+++ b/core/SegmentExpression.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -17,7 +17,7 @@ class Piwik_SegmentExpression
{
const AND_DELIMITER = ';';
const OR_DELIMITER = ',';
-
+
const MATCH_EQUAL = '==';
const MATCH_NOT_EQUAL = '!=';
const MATCH_GREATER_OR_EQUAL = '>=';
@@ -26,226 +26,222 @@ class Piwik_SegmentExpression
const MATCH_LESS = '<';
const MATCH_CONTAINS = '=@';
const MATCH_DOES_NOT_CONTAIN = '!@';
-
+
// Special case, since we look up Page URLs/Page titles in a sub SQL query
const MATCH_ACTIONS_CONTAINS = 'IN';
-
+
const INDEX_BOOL_OPERATOR = 0;
const INDEX_OPERAND = 1;
-
+
function __construct($string)
{
$this->string = $string;
$this->tree = $this->parseTree();
}
+
protected $joins = array();
protected $valuesBind = array();
protected $parsedTree = array();
protected $tree = array();
- protected $parsedSubExpressions = array();
-
- /**
- * Given the array of parsed filters containing, for each filter,
- * the boolean operator (AND/OR) and the operand,
- * Will return the array where the filters are in SQL representation
- *
- * @throws Exception
- * @return array
- */
+ protected $parsedSubExpressions = array();
+
+ /**
+ * Given the array of parsed filters containing, for each filter,
+ * the boolean operator (AND/OR) and the operand,
+ * Will return the array where the filters are in SQL representation
+ *
+ * @throws Exception
+ * @return array
+ */
public function parseSubExpressions()
{
$parsedSubExpressions = array();
- foreach($this->tree as $id => $leaf)
- {
+ foreach ($this->tree as $id => $leaf) {
$operand = $leaf[self::INDEX_OPERAND];
$operator = $leaf[self::INDEX_BOOL_OPERATOR];
- $pattern = '/^(.+?)(' .self::MATCH_EQUAL.'|'
- .self::MATCH_NOT_EQUAL.'|'
- .self::MATCH_GREATER_OR_EQUAL.'|'
- .self::MATCH_GREATER.'|'
- .self::MATCH_LESS_OR_EQUAL.'|'
- .self::MATCH_LESS.'|'
- .self::MATCH_CONTAINS.'|'
- .self::MATCH_DOES_NOT_CONTAIN
- .'){1}(.+)/';
- $match = preg_match( $pattern, $operand, $matches );
- if($match == 0)
- {
- throw new Exception('Segment parameter \''.$operand.'\' does not appear to have a valid format.');
+ $pattern = '/^(.+?)(' . self::MATCH_EQUAL . '|'
+ . self::MATCH_NOT_EQUAL . '|'
+ . self::MATCH_GREATER_OR_EQUAL . '|'
+ . self::MATCH_GREATER . '|'
+ . self::MATCH_LESS_OR_EQUAL . '|'
+ . self::MATCH_LESS . '|'
+ . self::MATCH_CONTAINS . '|'
+ . self::MATCH_DOES_NOT_CONTAIN
+ . '){1}(.+)/';
+ $match = preg_match($pattern, $operand, $matches);
+ if ($match == 0) {
+ throw new Exception('Segment parameter \'' . $operand . '\' does not appear to have a valid format.');
}
$leftMember = $matches[1];
$operation = $matches[2];
$valueRightMember = $matches[3];
- $parsedSubExpressions[] = array(
+ $parsedSubExpressions[] = array(
self::INDEX_BOOL_OPERATOR => $operator,
- self::INDEX_OPERAND => array(
+ self::INDEX_OPERAND => array(
$leftMember,
- $operation,
- $valueRightMember,
- ));
+ $operation,
+ $valueRightMember,
+ ));
}
$this->parsedSubExpressions = $parsedSubExpressions;
return $parsedSubExpressions;
}
- /**
- * Set the given expression
- * @param $parsedSubExpressions
- */
+ /**
+ * Set the given expression
+ * @param $parsedSubExpressions
+ */
public function setSubExpressionsAfterCleanup($parsedSubExpressions)
{
$this->parsedSubExpressions = $parsedSubExpressions;
}
- /**
- * Returns the current sub expression
- * @return array
- */
+ /**
+ * Returns the current sub expression
+ * @return array
+ */
public function getSubExpressions()
{
return $this->parsedSubExpressions;
}
- /**
- * @param array $availableTables
- */
- public function parseSubExpressionsIntoSqlExpressions(&$availableTables=array())
+ /**
+ * @param array $availableTables
+ */
+ public function parseSubExpressionsIntoSqlExpressions(&$availableTables = array())
{
$sqlSubExpressions = array();
$this->valuesBind = array();
$this->joins = array();
-
- foreach($this->parsedSubExpressions as $leaf)
- {
+
+ foreach ($this->parsedSubExpressions as $leaf) {
$operator = $leaf[self::INDEX_BOOL_OPERATOR];
$operandDefinition = $leaf[self::INDEX_OPERAND];
-
+
$operand = $this->getSqlMatchFromDefinition($operandDefinition, $availableTables);
-
+
if ($operand[1] !== null) {
$this->valuesBind[] = $operand[1];
}
$operand = $operand[0];
$sqlSubExpressions[] = array(
self::INDEX_BOOL_OPERATOR => $operator,
- self::INDEX_OPERAND => $operand,
- );
+ self::INDEX_OPERAND => $operand,
+ );
}
-
+
$this->tree = $sqlSubExpressions;
}
- /**
- * Given an array representing one filter operand ( left member , operation , right member)
- * Will return an array containing
- * - the SQL substring,
- * - the values to bind to this substring
- *
- * @param array $def
- * @param array $availableTables
- * @throws Exception
- * @return array
- */
+ /**
+ * Given an array representing one filter operand ( left member , operation , right member)
+ * Will return an array containing
+ * - the SQL substring,
+ * - the values to bind to this substring
+ *
+ * @param array $def
+ * @param array $availableTables
+ * @throws Exception
+ * @return array
+ */
// @todo case insensitive?
protected function getSqlMatchFromDefinition($def, &$availableTables)
{
- $field = $def[0];
- $matchType = $def[1];
+ $field = $def[0];
+ $matchType = $def[1];
$value = $def[2];
-
- switch($matchType)
- {
- case self::MATCH_EQUAL:
- $sqlMatch = '=';
- break;
- case self::MATCH_NOT_EQUAL:
- $sqlMatch = '<>';
- break;
- case self::MATCH_GREATER:
- $sqlMatch = '>';
- break;
- case self::MATCH_LESS:
- $sqlMatch = '<';
- break;
- case self::MATCH_GREATER_OR_EQUAL:
- $sqlMatch = '>=';
- break;
- case self::MATCH_LESS_OR_EQUAL:
- $sqlMatch = '<=';
- break;
- case self::MATCH_CONTAINS:
- $sqlMatch = 'LIKE';
- $value = '%'.$this->escapeLikeString($value).'%';
- break;
- case self::MATCH_DOES_NOT_CONTAIN:
- $sqlMatch = 'NOT LIKE';
- $value = '%'.$this->escapeLikeString($value).'%';
- break;
-
+
+ switch ($matchType) {
+ case self::MATCH_EQUAL:
+ $sqlMatch = '=';
+ break;
+ case self::MATCH_NOT_EQUAL:
+ $sqlMatch = '<>';
+ break;
+ case self::MATCH_GREATER:
+ $sqlMatch = '>';
+ break;
+ case self::MATCH_LESS:
+ $sqlMatch = '<';
+ break;
+ case self::MATCH_GREATER_OR_EQUAL:
+ $sqlMatch = '>=';
+ break;
+ case self::MATCH_LESS_OR_EQUAL:
+ $sqlMatch = '<=';
+ break;
+ case self::MATCH_CONTAINS:
+ $sqlMatch = 'LIKE';
+ $value = '%' . $this->escapeLikeString($value) . '%';
+ break;
+ case self::MATCH_DOES_NOT_CONTAIN:
+ $sqlMatch = 'NOT LIKE';
+ $value = '%' . $this->escapeLikeString($value) . '%';
+ break;
+
case self::MATCH_ACTIONS_CONTAINS:
// this match type is not accessible from the outside
// (it won't be matched in self::parseSubExpressions())
// it can be used internally to inject sub-expressions into the query.
// see Piwik_Segment::getCleanedExpression()
- $sqlMatch = 'IN ('.$value['SQL'].')';
+ $sqlMatch = 'IN (' . $value['SQL'] . ')';
$value = $this->escapeLikeString($value['bind']);
break;
- default:
- throw new Exception("Filter contains the match type '".$matchType."' which is not supported");
- break;
+ default:
+ throw new Exception("Filter contains the match type '" . $matchType . "' which is not supported");
+ break;
}
-
+
if ($matchType === self::MATCH_ACTIONS_CONTAINS) {
$sqlExpression = "$field $sqlMatch";
} else {
$sqlExpression = "$field $sqlMatch ?";
}
-
+
$this->checkFieldIsAvailable($field, $availableTables);
-
+
return array($sqlExpression, $value);
}
- /**
- * Check whether the field is available
- * If not, add it to the available tables
- *
- * @param string $field
- * @param array $availableTables
- */
+ /**
+ * Check whether the field is available
+ * If not, add it to the available tables
+ *
+ * @param string $field
+ * @param array $availableTables
+ */
private function checkFieldIsAvailable($field, &$availableTables)
{
$fieldParts = explode('.', $field);
-
+
$table = count($fieldParts) == 2 ? $fieldParts[0] : false;
-
+
// remove sql functions from field name
// example: `HOUR(log_visit.visit_last_action_time)` gets `HOUR(log_visit` => remove `HOUR(`
$table = preg_replace('/^[A-Z_]+\(/', '', $table);
$tableExists = !$table || in_array($table, $availableTables);
-
- if (!$tableExists)
- {
- $availableTables[] = $table;
+
+ if (!$tableExists) {
+ $availableTables[] = $table;
}
}
- /**
- * Escape the characters % and _ in the given string
- * @param string $str
- * @return string
- */
+ /**
+ * Escape the characters % and _ in the given string
+ * @param string $str
+ * @return string
+ */
private function escapeLikeString($str)
{
- $str = str_replace("%", "\%", $str);
- $str = str_replace("_", "\_", $str);
- return $str;
+ $str = str_replace("%", "\%", $str);
+ $str = str_replace("_", "\_", $str);
+ return $str;
}
-
+
/**
- * Given a filter string,
- * will parse it into an array where each row contains the boolean operator applied to it,
+ * Given a filter string,
+ * will parse it into an array where each row contains the boolean operator applied to it,
* and the operand
*
* @return array
@@ -253,7 +249,7 @@ class Piwik_SegmentExpression
protected function parseTree()
{
$string = $this->string;
- if(empty($string)) {
+ if (empty($string)) {
return array();
}
$tree = array();
@@ -261,101 +257,87 @@ class Piwik_SegmentExpression
$length = strlen($string);
$isBackslash = false;
$operand = '';
- while($i <= $length)
- {
+ while ($i <= $length) {
$char = $string[$i];
$isAND = ($char == self::AND_DELIMITER);
$isOR = ($char == self::OR_DELIMITER);
- $isEnd = ($length == $i+1);
-
- if($isEnd)
- {
- if($isBackslash && ($isAND || $isOR))
- {
- $operand = substr($operand, 0, -1);
- }
+ $isEnd = ($length == $i + 1);
+
+ if ($isEnd) {
+ if ($isBackslash && ($isAND || $isOR)) {
+ $operand = substr($operand, 0, -1);
+ }
$operand .= $char;
$tree[] = array(self::INDEX_BOOL_OPERATOR => '', self::INDEX_OPERAND => $operand);
break;
}
-
- if($isAND && !$isBackslash)
- {
- $tree[] = array(self::INDEX_BOOL_OPERATOR => 'AND', self::INDEX_OPERAND => $operand);
- $operand = '';
- }
- elseif($isOR && !$isBackslash)
- {
- $tree[] = array(self::INDEX_BOOL_OPERATOR => 'OR', self::INDEX_OPERAND => $operand);
- $operand = '';
- }
- else
- {
- if($isBackslash && ($isAND || $isOR))
- {
- $operand = substr($operand, 0, -1);
- }
- $operand .= $char;
- }
+
+ if ($isAND && !$isBackslash) {
+ $tree[] = array(self::INDEX_BOOL_OPERATOR => 'AND', self::INDEX_OPERAND => $operand);
+ $operand = '';
+ } elseif ($isOR && !$isBackslash) {
+ $tree[] = array(self::INDEX_BOOL_OPERATOR => 'OR', self::INDEX_OPERAND => $operand);
+ $operand = '';
+ } else {
+ if ($isBackslash && ($isAND || $isOR)) {
+ $operand = substr($operand, 0, -1);
+ }
+ $operand .= $char;
+ }
$isBackslash = ($char == "\\");
$i++;
}
return $tree;
}
- /**
- * Given the array of parsed boolean logic, will return
- * an array containing the full SQL string representing the filter,
- * the needed joins and the values to bind to the query
- *
- * @throws Exception
- * @return array SQL Query, Joins and Bind parameters
- */
+ /**
+ * Given the array of parsed boolean logic, will return
+ * an array containing the full SQL string representing the filter,
+ * the needed joins and the values to bind to the query
+ *
+ * @throws Exception
+ * @return array SQL Query, Joins and Bind parameters
+ */
public function getSql()
{
- if(count($this->tree) == 0)
- {
+ if (count($this->tree) == 0) {
throw new Exception("Invalid segment, please specify a valid segment.");
}
$bind = array();
$sql = '';
$subExpression = false;
- foreach($this->tree as $expression)
- {
+ foreach ($this->tree as $expression) {
$operator = $expression[self::INDEX_BOOL_OPERATOR];
$operand = $expression[self::INDEX_OPERAND];
-
- if($operator == 'OR'
- && !$subExpression)
- {
+
+ if ($operator == 'OR'
+ && !$subExpression
+ ) {
$sql .= ' (';
$subExpression = true;
- }
- else
- {
+ } else {
$sql .= ' ';
}
-
+
$sql .= $operand;
-
- if($operator == 'AND'
- && $subExpression)
- {
+
+ if ($operator == 'AND'
+ && $subExpression
+ ) {
$sql .= ')';
$subExpression = false;
}
-
+
$sql .= " $operator";
}
- if($subExpression)
- {
+ if ($subExpression) {
$sql .= ')';
}
return array(
- 'where' => $sql,
- 'bind' => $this->valuesBind,
- 'join' => implode(' ', $this->joins)
+ 'where' => $sql,
+ 'bind' => $this->valuesBind,
+ 'join' => implode(' ', $this->joins)
);
}
}
diff --git a/core/Session.php b/core/Session.php
index 8f5f5912ab..e102ef605e 100644
--- a/core/Session.php
+++ b/core/Session.php
@@ -1,133 +1,127 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Session initialization.
- *
+ *
* @package Piwik
* @subpackage Piwik_Session
*/
class Piwik_Session extends Zend_Session
{
- protected static $sessionStarted = false;
-
- /**
- * Are we using file-based session store?
- *
- * @return bool True if file-based; false otherwise
- */
- public static function isFileBasedSessions()
- {
- $config = Piwik_Config::getInstance();
- return !isset($config->General['session_save_handler'])
- || $config->General['session_save_handler'] === 'files';
- }
-
- /**
- * Start the session
- *
- * @param array|bool $options An array of configuration options; the auto-start (bool) setting is ignored
- * @return void
- */
- public static function start($options = false)
- {
- if(Piwik_Common::isPhpCliMode()
- || self::$sessionStarted
- || (defined('PIWIK_ENABLE_SESSION_START') && !PIWIK_ENABLE_SESSION_START))
- {
- return;
- }
- self::$sessionStarted = true;
-
- // use cookies to store session id on the client side
- @ini_set('session.use_cookies', '1');
-
- // prevent attacks involving session ids passed in URLs
- @ini_set('session.use_only_cookies', '1');
-
- // advise browser that session cookie should only be sent over secure connection
- if(Piwik::isHttps())
- {
- @ini_set('session.cookie_secure', '1');
- }
-
- // advise browser that session cookie should only be accessible through the HTTP protocol (i.e., not JavaScript)
- @ini_set('session.cookie_httponly', '1');
-
- // don't use the default: PHPSESSID
- $sessionName = defined('PIWIK_SESSION_NAME') ? PIWIK_SESSION_NAME : 'PIWIK_SESSID';
- @ini_set('session.name', $sessionName);
-
- // proxies may cause the referer check to fail and
- // incorrectly invalidate the session
- @ini_set('session.referer_check', '');
-
- $currentSaveHandler = ini_get('session.save_handler');
- $config = Piwik_Config::getInstance();
-
- if (self::isFileBasedSessions())
- {
- // Note: this handler doesn't work well in load-balanced environments and may have a concurrency issue with locked session files
-
- // for "files", use our own folder to prevent local session file hijacking
- $sessionPath = self::getSessionsDirectory();
- // We always call mkdir since it also chmods the directory which might help when permissions were reverted for some reasons
- Piwik_Common::mkdir($sessionPath);
-
- @ini_set('session.save_handler', 'files');
- @ini_set('session.save_path', $sessionPath);
- }
- else if ($config->General['session_save_handler'] === 'dbtable'
- || in_array($currentSaveHandler, array('user', 'mm')))
- {
- // We consider these to be misconfigurations, in that:
- // - user - we can't verify that user-defined session handler functions have already been set via session_set_save_handler()
- // - mm - this handler is not recommended, unsupported, not available for Windows, and has a potential concurrency issue
-
- $db = Zend_Registry::get('db');
-
- $config = array(
- 'name' => Piwik_Common::prefixTable('session'),
- 'primary' => 'id',
- 'modifiedColumn' => 'modified',
- 'dataColumn' => 'data',
- 'lifetimeColumn' => 'lifetime',
- 'db' => $db,
- );
-
- $saveHandler = new Piwik_Session_SaveHandler_DbTable($config);
- if($saveHandler)
- {
- self::setSaveHandler($saveHandler);
- }
- }
-
- // garbage collection may disabled by default (e.g., Debian)
- if(ini_get('session.gc_probability') == 0)
- {
- @ini_set('session.gc_probability', 1);
- }
-
- try {
- Zend_Session::start();
- register_shutdown_function(array('Zend_Session', 'writeClose'), true);
- } catch(Exception $e) {
- Piwik::log('Unable to start session: ' . $e->getMessage());
-
- $enableDbSessions = '';
- if(Piwik::isInstalled())
- {
- $enableDbSessions = "<br/>If you still experience issues after trying these changes,
+ protected static $sessionStarted = false;
+
+ /**
+ * Are we using file-based session store?
+ *
+ * @return bool True if file-based; false otherwise
+ */
+ public static function isFileBasedSessions()
+ {
+ $config = Piwik_Config::getInstance();
+ return !isset($config->General['session_save_handler'])
+ || $config->General['session_save_handler'] === 'files';
+ }
+
+ /**
+ * Start the session
+ *
+ * @param array|bool $options An array of configuration options; the auto-start (bool) setting is ignored
+ * @return void
+ */
+ public static function start($options = false)
+ {
+ if (Piwik_Common::isPhpCliMode()
+ || self::$sessionStarted
+ || (defined('PIWIK_ENABLE_SESSION_START') && !PIWIK_ENABLE_SESSION_START)
+ ) {
+ return;
+ }
+ self::$sessionStarted = true;
+
+ // use cookies to store session id on the client side
+ @ini_set('session.use_cookies', '1');
+
+ // prevent attacks involving session ids passed in URLs
+ @ini_set('session.use_only_cookies', '1');
+
+ // advise browser that session cookie should only be sent over secure connection
+ if (Piwik::isHttps()) {
+ @ini_set('session.cookie_secure', '1');
+ }
+
+ // advise browser that session cookie should only be accessible through the HTTP protocol (i.e., not JavaScript)
+ @ini_set('session.cookie_httponly', '1');
+
+ // don't use the default: PHPSESSID
+ $sessionName = defined('PIWIK_SESSION_NAME') ? PIWIK_SESSION_NAME : 'PIWIK_SESSID';
+ @ini_set('session.name', $sessionName);
+
+ // proxies may cause the referer check to fail and
+ // incorrectly invalidate the session
+ @ini_set('session.referer_check', '');
+
+ $currentSaveHandler = ini_get('session.save_handler');
+ $config = Piwik_Config::getInstance();
+
+ if (self::isFileBasedSessions()) {
+ // Note: this handler doesn't work well in load-balanced environments and may have a concurrency issue with locked session files
+
+ // for "files", use our own folder to prevent local session file hijacking
+ $sessionPath = self::getSessionsDirectory();
+ // We always call mkdir since it also chmods the directory which might help when permissions were reverted for some reasons
+ Piwik_Common::mkdir($sessionPath);
+
+ @ini_set('session.save_handler', 'files');
+ @ini_set('session.save_path', $sessionPath);
+ } else if ($config->General['session_save_handler'] === 'dbtable'
+ || in_array($currentSaveHandler, array('user', 'mm'))
+ ) {
+ // We consider these to be misconfigurations, in that:
+ // - user - we can't verify that user-defined session handler functions have already been set via session_set_save_handler()
+ // - mm - this handler is not recommended, unsupported, not available for Windows, and has a potential concurrency issue
+
+ $db = Zend_Registry::get('db');
+
+ $config = array(
+ 'name' => Piwik_Common::prefixTable('session'),
+ 'primary' => 'id',
+ 'modifiedColumn' => 'modified',
+ 'dataColumn' => 'data',
+ 'lifetimeColumn' => 'lifetime',
+ 'db' => $db,
+ );
+
+ $saveHandler = new Piwik_Session_SaveHandler_DbTable($config);
+ if ($saveHandler) {
+ self::setSaveHandler($saveHandler);
+ }
+ }
+
+ // garbage collection may disabled by default (e.g., Debian)
+ if (ini_get('session.gc_probability') == 0) {
+ @ini_set('session.gc_probability', 1);
+ }
+
+ try {
+ Zend_Session::start();
+ register_shutdown_function(array('Zend_Session', 'writeClose'), true);
+ } catch (Exception $e) {
+ Piwik::log('Unable to start session: ' . $e->getMessage());
+
+ $enableDbSessions = '';
+ if (Piwik::isInstalled()) {
+ $enableDbSessions = "<br/>If you still experience issues after trying these changes,
we recommend that you <a href='http://piwik.org/faq/how-to-install/#faq_133' target='_blank'>enable database session storage</a>.";
- }
+ }
$message = sprintf("Error: %s %s %s\n<pre>Debug: the original error was \n%s</pre>",
Piwik_Translate('General_ExceptionUnableToStartSession'),
@@ -136,17 +130,17 @@ class Piwik_Session extends Zend_Session
$e->getMessage()
);
- Piwik_ExitWithMessage($message);
- }
- }
-
- /**
- * Returns the directory session files are stored in.
- *
- * @return string
- */
- public static function getSessionsDirectory()
- {
- return PIWIK_USER_PATH . '/tmp/sessions';
- }
+ Piwik_ExitWithMessage($message);
+ }
+ }
+
+ /**
+ * Returns the directory session files are stored in.
+ *
+ * @return string
+ */
+ public static function getSessionsDirectory()
+ {
+ return PIWIK_USER_PATH . '/tmp/sessions';
+ }
}
diff --git a/core/Session/Namespace.php b/core/Session/Namespace.php
index 582363de4c..3c18aa06db 100644
--- a/core/Session/Namespace.php
+++ b/core/Session/Namespace.php
@@ -1,34 +1,33 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Session namespace.
- *
+ *
* @package Piwik
* @subpackage Piwik_Session
*/
class Piwik_Session_Namespace extends Zend_Session_Namespace
{
- /**
- * @param string $namespace
- * @param bool $singleInstance
- */
- public function __construct($namespace = 'Default', $singleInstance = false)
- {
- if(Piwik_Common::isPhpCliMode())
- {
- self::$_readable = true;
- return;
- }
+ /**
+ * @param string $namespace
+ * @param bool $singleInstance
+ */
+ public function __construct($namespace = 'Default', $singleInstance = false)
+ {
+ if (Piwik_Common::isPhpCliMode()) {
+ self::$_readable = true;
+ return;
+ }
- parent::__construct($namespace, $singleInstance);
- }
+ parent::__construct($namespace, $singleInstance);
+ }
}
diff --git a/core/Session/SaveHandler/DbTable.php b/core/Session/SaveHandler/DbTable.php
index 7582d0ef19..df00a920ea 100644
--- a/core/Session/SaveHandler/DbTable.php
+++ b/core/Session/SaveHandler/DbTable.php
@@ -17,127 +17,127 @@
*/
class Piwik_Session_SaveHandler_DbTable implements Zend_Session_SaveHandler_Interface
{
- protected $config;
- protected $maxLifetime;
-
- /**
- * @param array $config
- */
- function __construct($config)
- {
- $this->config = $config;
- $this->maxLifetime = ini_get('session.gc_maxlifetime');
- }
-
- /**
- * Destructor
- *
- * @return void
- */
- public function __destruct()
- {
- Zend_Session::writeClose();
- }
-
- /**
- * Open Session - retrieve resources
- *
- * @param string $save_path
- * @param string $name
- * @return boolean
- */
- public function open($save_path, $name)
- {
- $this->config['db']->getConnection();
-
- return true;
- }
-
- /**
- * Close Session - free resources
- *
- * @return boolean
- */
- public function close()
- {
- return true;
- }
-
- /**
- * Read session data
- *
- * @param string $id
- * @return string
- */
- public function read($id)
- {
- $sql = 'SELECT '.$this->config['dataColumn'].' FROM '.$this->config['name']
- .' WHERE '.$this->config['primary'].' = ?'
- .' AND '.$this->config['modifiedColumn'].' + '.$this->config['lifetimeColumn'].' >= ?';
-
- $result = $this->config['db']->fetchOne($sql, array($id, time()));
- if(!$result)
- $result = '';
-
- return $result;
- }
-
- /**
- * Write Session - commit data to resource
- *
- * @param string $id
- * @param mixed $data
- * @return boolean
- */
- public function write($id, $data)
- {
- $sql = 'INSERT INTO '.$this->config['name']
- .' ('.$this->config['primary'].','
- .$this->config['modifiedColumn'].','
- .$this->config['lifetimeColumn'].','
- .$this->config['dataColumn'].')'
- .' VALUES (?,?,?,?)'
- .' ON DUPLICATE KEY UPDATE '
- .$this->config['modifiedColumn'].' = ?,'
- .$this->config['lifetimeColumn'].' = ?,'
- .$this->config['dataColumn'].' = ?';
-
- $this->config['db']->query($sql, array($id, time(), $this->maxLifetime, $data, time(), $this->maxLifetime, $data));
-
- return true;
- }
-
- /**
- * Destroy Session - remove data from resource for
- * given session id
- *
- * @param string $id
- * @return boolean
- */
- public function destroy($id)
- {
- $sql = 'DELETE FROM '.$this->config['name']
- .' WHERE '.$this->config['primary'].' = ?';
-
- $this->config['db']->query($sql, array($id));
-
- return true;
- }
-
- /**
- * Garbage Collection - remove old session data older
- * than $maxlifetime (in seconds)
- *
- * @param int $maxlifetime timestamp in seconds
- * @return true
- */
- public function gc($maxlifetime)
- {
- $sql = 'DELETE FROM '.$this->config['name']
- .' WHERE '.$this->config['modifiedColumn'].' + '.$this->config['lifetimeColumn'].' < ?';
-
- $this->config['db']->query($sql, array(time()));
-
- return true;
- }
+ protected $config;
+ protected $maxLifetime;
+
+ /**
+ * @param array $config
+ */
+ function __construct($config)
+ {
+ $this->config = $config;
+ $this->maxLifetime = ini_get('session.gc_maxlifetime');
+ }
+
+ /**
+ * Destructor
+ *
+ * @return void
+ */
+ public function __destruct()
+ {
+ Zend_Session::writeClose();
+ }
+
+ /**
+ * Open Session - retrieve resources
+ *
+ * @param string $save_path
+ * @param string $name
+ * @return boolean
+ */
+ public function open($save_path, $name)
+ {
+ $this->config['db']->getConnection();
+
+ return true;
+ }
+
+ /**
+ * Close Session - free resources
+ *
+ * @return boolean
+ */
+ public function close()
+ {
+ return true;
+ }
+
+ /**
+ * Read session data
+ *
+ * @param string $id
+ * @return string
+ */
+ public function read($id)
+ {
+ $sql = 'SELECT ' . $this->config['dataColumn'] . ' FROM ' . $this->config['name']
+ . ' WHERE ' . $this->config['primary'] . ' = ?'
+ . ' AND ' . $this->config['modifiedColumn'] . ' + ' . $this->config['lifetimeColumn'] . ' >= ?';
+
+ $result = $this->config['db']->fetchOne($sql, array($id, time()));
+ if (!$result)
+ $result = '';
+
+ return $result;
+ }
+
+ /**
+ * Write Session - commit data to resource
+ *
+ * @param string $id
+ * @param mixed $data
+ * @return boolean
+ */
+ public function write($id, $data)
+ {
+ $sql = 'INSERT INTO ' . $this->config['name']
+ . ' (' . $this->config['primary'] . ','
+ . $this->config['modifiedColumn'] . ','
+ . $this->config['lifetimeColumn'] . ','
+ . $this->config['dataColumn'] . ')'
+ . ' VALUES (?,?,?,?)'
+ . ' ON DUPLICATE KEY UPDATE '
+ . $this->config['modifiedColumn'] . ' = ?,'
+ . $this->config['lifetimeColumn'] . ' = ?,'
+ . $this->config['dataColumn'] . ' = ?';
+
+ $this->config['db']->query($sql, array($id, time(), $this->maxLifetime, $data, time(), $this->maxLifetime, $data));
+
+ return true;
+ }
+
+ /**
+ * Destroy Session - remove data from resource for
+ * given session id
+ *
+ * @param string $id
+ * @return boolean
+ */
+ public function destroy($id)
+ {
+ $sql = 'DELETE FROM ' . $this->config['name']
+ . ' WHERE ' . $this->config['primary'] . ' = ?';
+
+ $this->config['db']->query($sql, array($id));
+
+ return true;
+ }
+
+ /**
+ * Garbage Collection - remove old session data older
+ * than $maxlifetime (in seconds)
+ *
+ * @param int $maxlifetime timestamp in seconds
+ * @return true
+ */
+ public function gc($maxlifetime)
+ {
+ $sql = 'DELETE FROM ' . $this->config['name']
+ . ' WHERE ' . $this->config['modifiedColumn'] . ' + ' . $this->config['lifetimeColumn'] . ' < ?';
+
+ $this->config['db']->query($sql, array(time()));
+
+ return true;
+ }
}
diff --git a/core/Site.php b/core/Site.php
index b5ef654117..ae62ae0519 100644
--- a/core/Site.php
+++ b/core/Site.php
@@ -1,365 +1,357 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- *
+ *
* @package Piwik
*/
class Piwik_Site
{
- /**
- * @var int|null
- */
- protected $id = null;
-
- /**
- * @var array
- */
- public static $infoSites = array();
-
- /**
- * @param int $idsite
- */
- function __construct($idsite)
- {
- $this->id = (int)$idsite;
- if(!isset(self::$infoSites[$this->id]))
- {
- self::$infoSites[$this->id] = Piwik_SitesManager_API::getInstance()->getSiteFromId($this->id);
- }
- }
-
- /**
- * Sets the cached Site data with an array that associates site IDs with
- * individual site data.
- *
- * @param array $sites The array of sites data. Indexed by site ID.
- */
- public static function setSites($sites)
- {
- self::$infoSites = $sites;
- }
-
- /**
- * Sets the cached Site data with a non-associated array of site data.
- *
- * @param array $sites The array of sites data.
- */
- public static function setSitesFromArray($sites)
- {
- $sitesById = array();
- foreach($sites as $site)
- {
- $sitesById[$site['idsite']] = $site;
- }
- self::setSites($sitesById);
- }
-
- /**
- * @return string
- */
- function __toString()
- {
- return "site id=".$this->getId().",
- name=".$this->getName() .",
- url = ". $this->getMainUrl() .",
- IPs excluded = ".$this->getExcludedIps().",
- timezone = ".$this->getTimezone().",
- currency = ".$this->getCurrency().",
- creation date = ".$this->getCreationDate();
- }
-
- /**
- * Returns the name of the site
- *
- * @return string
- */
- function getName()
- {
- return $this->get('name');
- }
-
- /**
- * Returns the main url of the site
- *
- * @return string
- */
- function getMainUrl()
- {
- return $this->get('main_url');
- }
-
- /**
- * Returns the id of the site
- *
- * @return int
- */
- function getId()
- {
- return $this->id;
- }
-
- /**
- * Returns a site property
- * @param string $name property to return
- * @return mixed
- * @throws Exception
- */
- protected function get( $name)
- {
- if(!isset(self::$infoSites[$this->id][$name]))
- {
- throw new Exception('The requested website id = '.(int)$this->id.' (or its property '.$name.') couldn\'t be found');
- }
- return self::$infoSites[$this->id][$name];
- }
-
- /**
- * Returns the creation date of the site
- *
- * @return Piwik_Date
- */
- function getCreationDate()
- {
- $date = $this->get('ts_created');
- return Piwik_Date::factory($date);
- }
-
- /**
- * Returns the timezone of the size
- *
- * @return string
- */
- function getTimezone()
- {
- return $this->get('timezone');
- }
-
- /**
- * Returns the currency of the site
- *
- * @return string
- */
- function getCurrency()
- {
- return $this->get('currency');
- }
-
- /**
- * Returns the excluded ips of the site
- *
- * @return string
- */
- function getExcludedIps()
- {
- return $this->get('excluded_ips');
- }
-
- /**
- * Returns the excluded query parameters of the site
- *
- * @return string
- */
- function getExcludedQueryParameters()
- {
- return $this->get('excluded_parameters');
- }
-
- /**
- * Returns whether ecommerce id enabled for the site
- *
- * @return bool
- */
- function isEcommerceEnabled()
- {
- return $this->get('ecommerce') == 1;
- }
-
- function getSearchKeywordParameters()
- {
- return $this->get('sitesearch_keyword_parameters');
- }
-
- function getSearchCategoryParameters()
- {
- return $this->get('sitesearch_category_parameters');
- }
-
- /**
- * Returns whether Site Search Tracking is enabled for the site
- *
- * @return bool
- */
- function isSiteSearchEnabled()
- {
- return $this->get('sitesearch') == 1;
- }
-
- /**
- * Checks the given string for valid site ids and returns them as an array
- *
- * @param string $ids comma separated idSite list
- * @return array of valid integer
- */
- static public function getIdSitesFromIdSitesString( $ids )
- {
- if ($ids === 'all')
- {
- return Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess();
- }
-
- if(!is_array($ids))
- {
- $ids = explode(',', $ids);
- }
- $validIds = array();
- foreach($ids as $id)
- {
- $id = trim($id);
- if(!empty($id) && is_numeric($id) && $id > 0)
- {
- $validIds[] = $id;
- }
- }
- $validIds = array_filter($validIds);
- $validIds = array_unique($validIds);
-
- return $validIds;
- }
-
- /**
- * Clears the site cache
- */
- static public function clearCache()
- {
- self::$infoSites = array();
- }
-
- /**
- * Utility function. Returns the value of the specified field for the
- * site with the specified ID.
- *
- * @param int|string $idsite The ID of the site whose data is being
- * accessed.
- * @param string $field The name of the field to get.
- * @return mixed
- */
- static protected function getFor($idsite, $field)
- {
- $idsite = (int)$idsite;
-
- if (!isset(self::$infoSites[$idsite]))
- {
- self::$infoSites[$idsite] = Piwik_SitesManager_API::getInstance()->getSiteFromId($idsite);
- }
-
- return self::$infoSites[$idsite][$field];
- }
-
- /**
- * Returns the name of the site with the specified ID.
- *
- * @param int $idsite The site ID.
- * @return string
- */
- static public function getNameFor($idsite)
- {
- return self::getFor($idsite, 'name');
- }
-
- /**
- * Returns the timezone of the site with the specified ID.
- *
- * @param int $idsite The site ID.
- * @return string
- */
- static public function getTimezoneFor($idsite)
- {
- return self::getFor($idsite, 'timezone');
- }
-
- /**
- * Returns the creation date of the site with the specified ID.
- *
- * @param int $idsite The site ID.
- * @return string
- */
- static public function getCreationDateFor($idsite)
- {
- return self::getFor($idsite, 'ts_created');
- }
-
- /**
- * Returns the url for the site with the specified ID.
- *
- * @param int $idsite The site ID.
- * @return string
- */
- static public function getMainUrlFor($idsite)
- {
- return self::getFor($idsite, 'main_url');
- }
-
- /**
- * Returns whether the site with the specified ID is ecommerce enabled
- *
- * @param int $idsite The site ID.
- * @return string
- */
- static public function isEcommerceEnabledFor($idsite)
- {
- return self::getFor($idsite, 'ecommerce') == 1;
- }
-
- /**
- * Returns whether the site with the specified ID is Site Search enabled
- *
- * @param int $idsite The site ID.
- * @return string
- */
- static public function isSiteSearchEnabledFor($idsite)
- {
- return self::getFor($idsite, 'sitesearch') == 1;
- }
-
- /**
- * Returns the currency of the site with the specified ID.
- *
- * @param int $idsite The site ID.
- * @return string
- */
- static public function getCurrencyFor($idsite)
- {
- return self::getFor($idsite, 'currency');
- }
-
- /**
- * Returns the excluded IP addresses of the site with the specified ID.
- *
- * @param int $idsite The site ID.
- * @return string
- */
- static public function getExcludedIpsFor($idsite)
- {
- return self::getFor($idsite, 'excluded_ips');
- }
-
- /**
- * Returns the excluded query parameters for the site with the specified ID.
- *
- * @param int $idsite The site ID.
- * @return string
- */
- static public function getExcludedQueryParametersFor($idsite)
- {
- return self::getFor($idsite, 'excluded_parameters');
- }
+ /**
+ * @var int|null
+ */
+ protected $id = null;
+
+ /**
+ * @var array
+ */
+ public static $infoSites = array();
+
+ /**
+ * @param int $idsite
+ */
+ function __construct($idsite)
+ {
+ $this->id = (int)$idsite;
+ if (!isset(self::$infoSites[$this->id])) {
+ self::$infoSites[$this->id] = Piwik_SitesManager_API::getInstance()->getSiteFromId($this->id);
+ }
+ }
+
+ /**
+ * Sets the cached Site data with an array that associates site IDs with
+ * individual site data.
+ *
+ * @param array $sites The array of sites data. Indexed by site ID.
+ */
+ public static function setSites($sites)
+ {
+ self::$infoSites = $sites;
+ }
+
+ /**
+ * Sets the cached Site data with a non-associated array of site data.
+ *
+ * @param array $sites The array of sites data.
+ */
+ public static function setSitesFromArray($sites)
+ {
+ $sitesById = array();
+ foreach ($sites as $site) {
+ $sitesById[$site['idsite']] = $site;
+ }
+ self::setSites($sitesById);
+ }
+
+ /**
+ * @return string
+ */
+ function __toString()
+ {
+ return "site id=" . $this->getId() . ",
+ name=" . $this->getName() . ",
+ url = " . $this->getMainUrl() . ",
+ IPs excluded = " . $this->getExcludedIps() . ",
+ timezone = " . $this->getTimezone() . ",
+ currency = " . $this->getCurrency() . ",
+ creation date = " . $this->getCreationDate();
+ }
+
+ /**
+ * Returns the name of the site
+ *
+ * @return string
+ */
+ function getName()
+ {
+ return $this->get('name');
+ }
+
+ /**
+ * Returns the main url of the site
+ *
+ * @return string
+ */
+ function getMainUrl()
+ {
+ return $this->get('main_url');
+ }
+
+ /**
+ * Returns the id of the site
+ *
+ * @return int
+ */
+ function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Returns a site property
+ * @param string $name property to return
+ * @return mixed
+ * @throws Exception
+ */
+ protected function get($name)
+ {
+ if (!isset(self::$infoSites[$this->id][$name])) {
+ throw new Exception('The requested website id = ' . (int)$this->id . ' (or its property ' . $name . ') couldn\'t be found');
+ }
+ return self::$infoSites[$this->id][$name];
+ }
+
+ /**
+ * Returns the creation date of the site
+ *
+ * @return Piwik_Date
+ */
+ function getCreationDate()
+ {
+ $date = $this->get('ts_created');
+ return Piwik_Date::factory($date);
+ }
+
+ /**
+ * Returns the timezone of the size
+ *
+ * @return string
+ */
+ function getTimezone()
+ {
+ return $this->get('timezone');
+ }
+
+ /**
+ * Returns the currency of the site
+ *
+ * @return string
+ */
+ function getCurrency()
+ {
+ return $this->get('currency');
+ }
+
+ /**
+ * Returns the excluded ips of the site
+ *
+ * @return string
+ */
+ function getExcludedIps()
+ {
+ return $this->get('excluded_ips');
+ }
+
+ /**
+ * Returns the excluded query parameters of the site
+ *
+ * @return string
+ */
+ function getExcludedQueryParameters()
+ {
+ return $this->get('excluded_parameters');
+ }
+
+ /**
+ * Returns whether ecommerce id enabled for the site
+ *
+ * @return bool
+ */
+ function isEcommerceEnabled()
+ {
+ return $this->get('ecommerce') == 1;
+ }
+
+ function getSearchKeywordParameters()
+ {
+ return $this->get('sitesearch_keyword_parameters');
+ }
+
+ function getSearchCategoryParameters()
+ {
+ return $this->get('sitesearch_category_parameters');
+ }
+
+ /**
+ * Returns whether Site Search Tracking is enabled for the site
+ *
+ * @return bool
+ */
+ function isSiteSearchEnabled()
+ {
+ return $this->get('sitesearch') == 1;
+ }
+
+ /**
+ * Checks the given string for valid site ids and returns them as an array
+ *
+ * @param string $ids comma separated idSite list
+ * @return array of valid integer
+ */
+ static public function getIdSitesFromIdSitesString($ids)
+ {
+ if ($ids === 'all') {
+ return Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess();
+ }
+
+ if (!is_array($ids)) {
+ $ids = explode(',', $ids);
+ }
+ $validIds = array();
+ foreach ($ids as $id) {
+ $id = trim($id);
+ if (!empty($id) && is_numeric($id) && $id > 0) {
+ $validIds[] = $id;
+ }
+ }
+ $validIds = array_filter($validIds);
+ $validIds = array_unique($validIds);
+
+ return $validIds;
+ }
+
+ /**
+ * Clears the site cache
+ */
+ static public function clearCache()
+ {
+ self::$infoSites = array();
+ }
+
+ /**
+ * Utility function. Returns the value of the specified field for the
+ * site with the specified ID.
+ *
+ * @param int|string $idsite The ID of the site whose data is being
+ * accessed.
+ * @param string $field The name of the field to get.
+ * @return mixed
+ */
+ static protected function getFor($idsite, $field)
+ {
+ $idsite = (int)$idsite;
+
+ if (!isset(self::$infoSites[$idsite])) {
+ self::$infoSites[$idsite] = Piwik_SitesManager_API::getInstance()->getSiteFromId($idsite);
+ }
+
+ return self::$infoSites[$idsite][$field];
+ }
+
+ /**
+ * Returns the name of the site with the specified ID.
+ *
+ * @param int $idsite The site ID.
+ * @return string
+ */
+ static public function getNameFor($idsite)
+ {
+ return self::getFor($idsite, 'name');
+ }
+
+ /**
+ * Returns the timezone of the site with the specified ID.
+ *
+ * @param int $idsite The site ID.
+ * @return string
+ */
+ static public function getTimezoneFor($idsite)
+ {
+ return self::getFor($idsite, 'timezone');
+ }
+
+ /**
+ * Returns the creation date of the site with the specified ID.
+ *
+ * @param int $idsite The site ID.
+ * @return string
+ */
+ static public function getCreationDateFor($idsite)
+ {
+ return self::getFor($idsite, 'ts_created');
+ }
+
+ /**
+ * Returns the url for the site with the specified ID.
+ *
+ * @param int $idsite The site ID.
+ * @return string
+ */
+ static public function getMainUrlFor($idsite)
+ {
+ return self::getFor($idsite, 'main_url');
+ }
+
+ /**
+ * Returns whether the site with the specified ID is ecommerce enabled
+ *
+ * @param int $idsite The site ID.
+ * @return string
+ */
+ static public function isEcommerceEnabledFor($idsite)
+ {
+ return self::getFor($idsite, 'ecommerce') == 1;
+ }
+
+ /**
+ * Returns whether the site with the specified ID is Site Search enabled
+ *
+ * @param int $idsite The site ID.
+ * @return string
+ */
+ static public function isSiteSearchEnabledFor($idsite)
+ {
+ return self::getFor($idsite, 'sitesearch') == 1;
+ }
+
+ /**
+ * Returns the currency of the site with the specified ID.
+ *
+ * @param int $idsite The site ID.
+ * @return string
+ */
+ static public function getCurrencyFor($idsite)
+ {
+ return self::getFor($idsite, 'currency');
+ }
+
+ /**
+ * Returns the excluded IP addresses of the site with the specified ID.
+ *
+ * @param int $idsite The site ID.
+ * @return string
+ */
+ static public function getExcludedIpsFor($idsite)
+ {
+ return self::getFor($idsite, 'excluded_ips');
+ }
+
+ /**
+ * Returns the excluded query parameters for the site with the specified ID.
+ *
+ * @param int $idsite The site ID.
+ * @return string
+ */
+ static public function getExcludedQueryParametersFor($idsite)
+ {
+ return self::getFor($idsite, 'excluded_parameters');
+ }
}
diff --git a/core/Smarty.php b/core/Smarty.php
index 3adbebd49d..91363d1cbc 100644
--- a/core/Smarty.php
+++ b/core/Smarty.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -23,116 +23,117 @@ require_once PIWIK_INCLUDE_PATH . '/libs/Smarty/Smarty.class.php';
* @see Smarty, libs/Smarty/Smarty.class.php
* @link http://smarty.net/manual/en/
*/
-class Piwik_Smarty extends Smarty
+class Piwik_Smarty extends Smarty
{
- function trigger_error($error_msg, $error_type = E_USER_WARNING)
- {
- throw new SmartyException($error_msg);
- }
-
- public function __construct($smConf = array(), $filter = true)
- {
- parent::__construct();
-
- $this->init($smConf, $filter);
- }
-
- public function init($smConf, $filter)
- {
- $this->initSettings($smConf);
- if ($filter) {
- $this->initFilters();
- }
- }
-
- protected function initSettings($smConf)
- {
- if (count($smConf) == 0) {
- $smConf = Piwik_Config::getInstance()->smarty;
- }
- foreach ($smConf as $key => $value) {
- $this->$key = $value;
- }
-
- $this->template_dir = $smConf['template_dir'];
- array_walk($this->template_dir, array("Piwik_Smarty", "addPiwikPath"), PIWIK_INCLUDE_PATH);
-
- $this->plugins_dir = $smConf['plugins_dir'];
- array_walk($this->plugins_dir, array("Piwik_Smarty", "addPiwikPath"), PIWIK_INCLUDE_PATH);
-
- $this->compile_dir = $smConf['compile_dir'];
- Piwik_Smarty::addPiwikPath($this->compile_dir, null, PIWIK_USER_PATH);
-
- $this->cache_dir = $smConf['cache_dir'];
- Piwik_Smarty::addPiwikPath($this->cache_dir, null, PIWIK_USER_PATH);
-
- $error_reporting = $smConf['error_reporting'];
- if ($error_reporting != (string)(int)$error_reporting) {
- $error_reporting = self::bitwise_eval($error_reporting);
- }
- $this->error_reporting = $error_reporting;
-
- Piwik_PostEvent('Smarty.initSettings', $this);
-
- }
-
- public function initFilters()
- {
- $this->load_filter('output', 'cachebuster');
-
- $use_ajax_cdn = Piwik_Config::getInstance()->General['use_ajax_cdn'];
- if ($use_ajax_cdn) {
- $this->load_filter('output', 'ajaxcdn');
- }
-
- $this->load_filter('output', 'trimwhitespace');
- }
-
- /**
- * Evaluate expression containing only bitwise operators.
- * Replaces defined constants with corresponding values.
- * Does not use eval().
- *
- * @param string $expression Expression.
- * @return string
- */
- static public function bitwise_eval($expression)
- {
- // replace defined constants
- $buf = get_defined_constants(true);
-
- // use only the 'Core' PHP constants, e.g., E_ALL, E_STRICT, ...
- $consts = isset($buf['Core']) ? $buf['Core'] : (isset($buf['mhash']) ? $buf['mhash'] : $buf['internal']);
- $expression = str_replace(' ', '', strtr($expression, $consts));
-
- // bitwise operators in order of precedence (highest to lowest)
- // note: boolean ! (NOT) and parentheses aren't handled
- $expression = preg_replace_callback('/~(-?[0-9]+)/', @create_function('$matches', 'return (string)((~(int)$matches[1]));'), $expression);
- $expression = preg_replace_callback('/(-?[0-9]+)&(-?[0-9]+)/', @create_function('$matches', 'return (string)((int)$matches[1]&(int)$matches[2]);'), $expression);
- $expression = preg_replace_callback('/(-?[0-9]+)\^(-?[0-9]+)/', @create_function('$matches', 'return (string)((int)$matches[1]^(int)$matches[2]);'), $expression);
- $expression = preg_replace_callback('/(-?[0-9]+)\|(-?[0-9]+)/', @create_function('$matches', 'return (string)((int)$matches[1]|(int)$matches[2]);'), $expression);
-
- return (string)((int)$expression & PHP_INT_MAX);
- }
-
- /**
- * Prepend relative paths with absolute Piwik path
- *
- * @param string $value relative path (pass by reference)
- * @param int $key (don't care)
- * @param string $path Piwik root
- */
- static public function addPiwikPath(&$value, $key, $path)
- {
- if($value[0] != '/' && $value[0] != DIRECTORY_SEPARATOR)
- {
- $value = $path ."/$value";
- }
- }
+ function trigger_error($error_msg, $error_type = E_USER_WARNING)
+ {
+ throw new SmartyException($error_msg);
+ }
+
+ public function __construct($smConf = array(), $filter = true)
+ {
+ parent::__construct();
+
+ $this->init($smConf, $filter);
+ }
+
+ public function init($smConf, $filter)
+ {
+ $this->initSettings($smConf);
+ if ($filter) {
+ $this->initFilters();
+ }
+ }
+
+ protected function initSettings($smConf)
+ {
+ if (count($smConf) == 0) {
+ $smConf = Piwik_Config::getInstance()->smarty;
+ }
+ foreach ($smConf as $key => $value) {
+ $this->$key = $value;
+ }
+
+ $this->template_dir = $smConf['template_dir'];
+ array_walk($this->template_dir, array("Piwik_Smarty", "addPiwikPath"), PIWIK_INCLUDE_PATH);
+
+ $this->plugins_dir = $smConf['plugins_dir'];
+ array_walk($this->plugins_dir, array("Piwik_Smarty", "addPiwikPath"), PIWIK_INCLUDE_PATH);
+
+ $this->compile_dir = $smConf['compile_dir'];
+ Piwik_Smarty::addPiwikPath($this->compile_dir, null, PIWIK_USER_PATH);
+
+ $this->cache_dir = $smConf['cache_dir'];
+ Piwik_Smarty::addPiwikPath($this->cache_dir, null, PIWIK_USER_PATH);
+
+ $error_reporting = $smConf['error_reporting'];
+ if ($error_reporting != (string)(int)$error_reporting) {
+ $error_reporting = self::bitwise_eval($error_reporting);
+ }
+ $this->error_reporting = $error_reporting;
+
+ Piwik_PostEvent('Smarty.initSettings', $this);
+
+ }
+
+ public function initFilters()
+ {
+ $this->load_filter('output', 'cachebuster');
+
+ $use_ajax_cdn = Piwik_Config::getInstance()->General['use_ajax_cdn'];
+ if ($use_ajax_cdn) {
+ $this->load_filter('output', 'ajaxcdn');
+ }
+
+ $this->load_filter('output', 'trimwhitespace');
+ }
+
+ /**
+ * Evaluate expression containing only bitwise operators.
+ * Replaces defined constants with corresponding values.
+ * Does not use eval().
+ *
+ * @param string $expression Expression.
+ * @return string
+ */
+ static public function bitwise_eval($expression)
+ {
+ // replace defined constants
+ $buf = get_defined_constants(true);
+
+ // use only the 'Core' PHP constants, e.g., E_ALL, E_STRICT, ...
+ $consts = isset($buf['Core']) ? $buf['Core'] : (isset($buf['mhash']) ? $buf['mhash'] : $buf['internal']);
+ $expression = str_replace(' ', '', strtr($expression, $consts));
+
+ // bitwise operators in order of precedence (highest to lowest)
+ // note: boolean ! (NOT) and parentheses aren't handled
+ $expression = preg_replace_callback('/~(-?[0-9]+)/', @create_function('$matches', 'return (string)((~(int)$matches[1]));'), $expression);
+ $expression = preg_replace_callback('/(-?[0-9]+)&(-?[0-9]+)/', @create_function('$matches', 'return (string)((int)$matches[1]&(int)$matches[2]);'), $expression);
+ $expression = preg_replace_callback('/(-?[0-9]+)\^(-?[0-9]+)/', @create_function('$matches', 'return (string)((int)$matches[1]^(int)$matches[2]);'), $expression);
+ $expression = preg_replace_callback('/(-?[0-9]+)\|(-?[0-9]+)/', @create_function('$matches', 'return (string)((int)$matches[1]|(int)$matches[2]);'), $expression);
+
+ return (string)((int)$expression & PHP_INT_MAX);
+ }
+
+ /**
+ * Prepend relative paths with absolute Piwik path
+ *
+ * @param string $value relative path (pass by reference)
+ * @param int $key (don't care)
+ * @param string $path Piwik root
+ */
+ static public function addPiwikPath(&$value, $key, $path)
+ {
+ if ($value[0] != '/' && $value[0] != DIRECTORY_SEPARATOR) {
+ $value = $path . "/$value";
+ }
+ }
}
/**
* @package Piwik
* @subpackage Piwik_Smarty
*/
-class SmartyException extends Exception {}
+class SmartyException extends Exception
+{
+}
diff --git a/core/SmartyPlugins/block.purify.php b/core/SmartyPlugins/block.purify.php
index 8768b5aea8..be168dbcdd 100644
--- a/core/SmartyPlugins/block.purify.php
+++ b/core/SmartyPlugins/block.purify.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -31,28 +31,25 @@
*/
function smarty_block_purify($params, $content, &$smarty)
{
- if (is_null($content))
- {
- return;
- }
+ if (is_null($content)) {
+ return;
+ }
- $assign = null;
+ $assign = null;
- foreach ($params as $_key => $_val)
- {
- switch ($_key)
- {
- case 'assign':
- $$_key = (string)$_val;
- break;
+ foreach ($params as $_key => $_val) {
+ switch ($_key) {
+ case 'assign':
+ $$_key = (string)$_val;
+ break;
- default:
- $smarty->trigger_error("purify: unknown attribute '$_key'");
- }
- }
+ default:
+ $smarty->trigger_error("purify: unknown attribute '$_key'");
+ }
+ }
- $purifier = Piwik_HTMLPurifier::getInstance();
- $output = $purifier->purify($content);
+ $purifier = Piwik_HTMLPurifier::getInstance();
+ $output = $purifier->purify($content);
- return $assign ? $smarty->assign($assign, $output) : $output;
+ return $assign ? $smarty->assign($assign, $output) : $output;
}
diff --git a/core/SmartyPlugins/function.ajaxErrorDiv.php b/core/SmartyPlugins/function.ajaxErrorDiv.php
index 79521d18af..886873f518 100644
--- a/core/SmartyPlugins/function.ajaxErrorDiv.php
+++ b/core/SmartyPlugins/function.ajaxErrorDiv.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -18,13 +18,10 @@
*/
function smarty_function_ajaxErrorDiv($params, &$smarty)
{
- if(empty($params['id']))
- {
- $id = 'ajaxError';
- }
- else
- {
- $id = $params['id'];
- }
- return '<div class="ajaxError" id="'.$id.'" style="display:none"></div>';
+ if (empty($params['id'])) {
+ $id = 'ajaxError';
+ } else {
+ $id = $params['id'];
+ }
+ return '<div class="ajaxError" id="' . $id . '" style="display:none"></div>';
}
diff --git a/core/SmartyPlugins/function.ajaxLoadingDiv.php b/core/SmartyPlugins/function.ajaxLoadingDiv.php
index 776f928c97..bd64a4f000 100644
--- a/core/SmartyPlugins/function.ajaxLoadingDiv.php
+++ b/core/SmartyPlugins/function.ajaxLoadingDiv.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -18,17 +18,14 @@
*/
function smarty_function_ajaxLoadingDiv($params, &$smarty)
{
- if(empty($params['id']))
- {
- $id = 'ajaxLoading';
- }
- else
- {
- $id = $params['id'];
- }
- return '<div id="'.$id.'" style="display:none">'.
- '<div class="loadingPiwik"><img src="themes/default/images/loading-blue.gif" alt="" /> '.
- Piwik_Translate('General_LoadingData') .
- ' </div>'.
- '</div>';
+ if (empty($params['id'])) {
+ $id = 'ajaxLoading';
+ } else {
+ $id = $params['id'];
+ }
+ return '<div id="' . $id . '" style="display:none">' .
+ '<div class="loadingPiwik"><img src="themes/default/images/loading-blue.gif" alt="" /> ' .
+ Piwik_Translate('General_LoadingData') .
+ ' </div>' .
+ '</div>';
}
diff --git a/core/SmartyPlugins/function.ajaxRequestErrorDiv.php b/core/SmartyPlugins/function.ajaxRequestErrorDiv.php
index 5636f5c431..a1d697010e 100644
--- a/core/SmartyPlugins/function.ajaxRequestErrorDiv.php
+++ b/core/SmartyPlugins/function.ajaxRequestErrorDiv.php
@@ -1,21 +1,21 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
/**
- * Outputs the generic Ajax request error div
+ * Outputs the generic Ajax request error div
* will be displayed when the ajax request fails (connectivity, server error, etc)
- *
- * @return string Html of the div
+ *
+ * @return string Html of the div
*/
function smarty_function_ajaxRequestErrorDiv()
{
- return '<div id="loadingError">'.Piwik_Translate('General_ErrorRequest').'</div>';
+ return '<div id="loadingError">' . Piwik_Translate('General_ErrorRequest') . '</div>';
}
diff --git a/core/SmartyPlugins/function.hiddenurl.php b/core/SmartyPlugins/function.hiddenurl.php
index 54beff93d3..91b1bbd89b 100644
--- a/core/SmartyPlugins/function.hiddenurl.php
+++ b/core/SmartyPlugins/function.hiddenurl.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -12,32 +12,31 @@
/**
* Smarty {hiddenurl} function plugin.
* Writes an input Hidden field for every parameter in the URL.
- * Useful when using GET forms because we need to print the current parameters
+ * Useful when using GET forms because we need to print the current parameters
* in hidden input so they are to the next URL after the form is submitted.
*
- *
+ *
* Examples:
* <pre>
* {hiddenurl module="API"} with a URL 'index.php?action=test&module=CoreHome' will output
* <input type=hidden name=action value=test>
* <input type=hidden name=module value=API>
* </pre>
- *
+ *
* Set a value to null if you want this value not to be passed in the submitted form.
- *
- * @param array
- * @param Smarty
- * @return string
+ *
+ * @param array
+ * @param Smarty
+ * @return string
*/
function smarty_function_hiddenurl($params, &$smarty)
{
- $queryStringModified = Piwik_Url::getCurrentQueryStringWithParametersModified( $params );
- $urlValues = Piwik_Common::getArrayFromQueryString($queryStringModified);
-
- $out = '';
- foreach($urlValues as $name => $value)
- {
- $out .= '<input type="hidden" name="'.$name.'" value="'.$value.'" />';
- }
- return $out;
+ $queryStringModified = Piwik_Url::getCurrentQueryStringWithParametersModified($params);
+ $urlValues = Piwik_Common::getArrayFromQueryString($queryStringModified);
+
+ $out = '';
+ foreach ($urlValues as $name => $value) {
+ $out .= '<input type="hidden" name="' . $name . '" value="' . $value . '" />';
+ }
+ return $out;
}
diff --git a/core/SmartyPlugins/function.includeAssets.php b/core/SmartyPlugins/function.includeAssets.php
index 4d0169790e..168e1ad5ef 100644
--- a/core/SmartyPlugins/function.includeAssets.php
+++ b/core/SmartyPlugins/function.includeAssets.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -24,23 +24,21 @@
*/
function smarty_function_includeAssets($params, &$smarty)
{
- if(!isset($params['type']))
- {
- throw new Exception("The smarty function includeAssets needs a 'type' parameter.");
- }
-
- $assetType = strtolower($params['type']);
- switch ( $assetType )
- {
- case 'css':
-
- return Piwik_AssetManager::getCssAssets();
-
- case 'js':
-
- return Piwik_AssetManager::getJsAssets();
-
- default:
- throw new Exception("The smarty function includeAssets 'type' parameter needs to be either 'css' or 'js'.");
- }
+ if (!isset($params['type'])) {
+ throw new Exception("The smarty function includeAssets needs a 'type' parameter.");
+ }
+
+ $assetType = strtolower($params['type']);
+ switch ($assetType) {
+ case 'css':
+
+ return Piwik_AssetManager::getCssAssets();
+
+ case 'js':
+
+ return Piwik_AssetManager::getJsAssets();
+
+ default:
+ throw new Exception("The smarty function includeAssets 'type' parameter needs to be either 'css' or 'js'.");
+ }
}
diff --git a/core/SmartyPlugins/function.loadJavascriptTranslations.php b/core/SmartyPlugins/function.loadJavascriptTranslations.php
index 0152e16cf6..38e851ee1e 100644
--- a/core/SmartyPlugins/function.loadJavascriptTranslations.php
+++ b/core/SmartyPlugins/function.loadJavascriptTranslations.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -33,29 +33,24 @@
* @throws Exception
* @return string
*/
-function smarty_function_loadJavascriptTranslations($params, &$smarty)
+function smarty_function_loadJavascriptTranslations($params, &$smarty)
{
- static $pluginTranslationsAlreadyLoaded = array();
- if(!isset($params['plugins']))
- {
- throw new Exception("The smarty function loadJavascriptTranslations needs a 'plugins' parameter.");
- }
- if(in_array($params['plugins'], $pluginTranslationsAlreadyLoaded))
- {
- return;
- }
- $pluginTranslationsAlreadyLoaded[] = $params['plugins'];
- $jsTranslations = Piwik_Translate::getInstance()->getJavascriptTranslations(explode(' ',$params['plugins']));
- $jsCode = '';
- if( isset($params['disableOutputScriptTag']) )
- {
- $jsCode .= $jsTranslations;
- }
- else
- {
- $jsCode .= '<script type="text/javascript">';
- $jsCode .= $jsTranslations;
- $jsCode .= '</script>';
- }
- return $jsCode;
+ static $pluginTranslationsAlreadyLoaded = array();
+ if (!isset($params['plugins'])) {
+ throw new Exception("The smarty function loadJavascriptTranslations needs a 'plugins' parameter.");
+ }
+ if (in_array($params['plugins'], $pluginTranslationsAlreadyLoaded)) {
+ return;
+ }
+ $pluginTranslationsAlreadyLoaded[] = $params['plugins'];
+ $jsTranslations = Piwik_Translate::getInstance()->getJavascriptTranslations(explode(' ', $params['plugins']));
+ $jsCode = '';
+ if (isset($params['disableOutputScriptTag'])) {
+ $jsCode .= $jsTranslations;
+ } else {
+ $jsCode .= '<script type="text/javascript">';
+ $jsCode .= $jsTranslations;
+ $jsCode .= '</script>';
+ }
+ return $jsCode;
}
diff --git a/core/SmartyPlugins/function.logoHtml.php b/core/SmartyPlugins/function.logoHtml.php
index acff49b34c..5de794330c 100644
--- a/core/SmartyPlugins/function.logoHtml.php
+++ b/core/SmartyPlugins/function.logoHtml.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -24,22 +24,18 @@
*/
function smarty_function_logoHtml($params, &$smarty)
{
- if(!isset($params['metadata']['logo']))
- {
- return;
- }
- $width = $height = $alt = '';
- if(isset($params['metadata']['logoWidth']))
- {
- $width = "width=".$params['metadata']['logoWidth'];
- }
- if(isset($params['metadata']['logoHeight']))
- {
- $height = "height=".$params['metadata']['logoHeight'];
- }
- if(isset($params['alt']))
- {
- $alt = "title='".$params['alt']."' alt='".$params['alt']."'";
- }
- return " <img $alt $width $height src='".$params['metadata']['logo']."' />";
+ if (!isset($params['metadata']['logo'])) {
+ return;
+ }
+ $width = $height = $alt = '';
+ if (isset($params['metadata']['logoWidth'])) {
+ $width = "width=" . $params['metadata']['logoWidth'];
+ }
+ if (isset($params['metadata']['logoHeight'])) {
+ $height = "height=" . $params['metadata']['logoHeight'];
+ }
+ if (isset($params['alt'])) {
+ $alt = "title='" . $params['alt'] . "' alt='" . $params['alt'] . "'";
+ }
+ return " <img $alt $width $height src='" . $params['metadata']['logo'] . "' />";
}
diff --git a/core/SmartyPlugins/function.postEvent.php b/core/SmartyPlugins/function.postEvent.php
index ff43fd91fe..079822e530 100644
--- a/core/SmartyPlugins/function.postEvent.php
+++ b/core/SmartyPlugins/function.postEvent.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -30,13 +30,12 @@
*/
function smarty_function_postEvent($params, &$smarty)
{
- if(!isset($params['name']))
- {
- throw new Exception("The smarty function postEvent needs a 'name' parameter.");
- }
- $eventName = $params['name'];
-
- $str = '';
- Piwik_PostEvent($eventName, $str);
- return $str;
+ if (!isset($params['name'])) {
+ throw new Exception("The smarty function postEvent needs a 'name' parameter.");
+ }
+ $eventName = $params['name'];
+
+ $str = '';
+ Piwik_PostEvent($eventName, $str);
+ return $str;
}
diff --git a/core/SmartyPlugins/function.sparkline.php b/core/SmartyPlugins/function.sparkline.php
index af1cab0914..af339efc88 100644
--- a/core/SmartyPlugins/function.sparkline.php
+++ b/core/SmartyPlugins/function.sparkline.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -16,9 +16,9 @@
*/
function smarty_function_sparkline($params, &$smarty = false)
{
- $src = $params['src'];
- $graph = new Piwik_Visualization_Sparkline();
- $width = $graph->getWidth();
- $height = $graph->getHeight();
- return "<img class=\"sparkline\" alt=\"\" src=\"$src\" width=\"$width\" height=\"$height\" />";
+ $src = $params['src'];
+ $graph = new Piwik_Visualization_Sparkline();
+ $width = $graph->getWidth();
+ $height = $graph->getHeight();
+ return "<img class=\"sparkline\" alt=\"\" src=\"$src\" width=\"$width\" height=\"$height\" />";
}
diff --git a/core/SmartyPlugins/function.url.php b/core/SmartyPlugins/function.url.php
index 9e20e93d1e..ef8ca0bf6c 100644
--- a/core/SmartyPlugins/function.url.php
+++ b/core/SmartyPlugins/function.url.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -18,14 +18,14 @@
* {url module="API"} will rewrite the URL modifying the module GET parameter
* {url module="API" method="getKeywords"} will rewrite the URL modifying the parameters module=API method=getKeywords
* </pre>
- *
+ *
* @see Piwik_Url::getCurrentQueryStringWithParametersModified()
*
* @param array $params $name=>$value pairs of the parameters to modify in the generated URL
* @param Smarty &smarty Smarty object
- * @return string Something like index.php?module=X&action=Y
+ * @return string Something like index.php?module=X&action=Y
*/
function smarty_function_url($params, &$smarty)
{
- return Piwik_Common::sanitizeInputValue('index.php' . Piwik_Url::getCurrentQueryStringWithParametersModified( $params ));
+ return Piwik_Common::sanitizeInputValue('index.php' . Piwik_Url::getCurrentQueryStringWithParametersModified($params));
}
diff --git a/core/SmartyPlugins/modifier.inlineHelp.php b/core/SmartyPlugins/modifier.inlineHelp.php
index 7cc7531f4a..a0ad93bed4 100644
--- a/core/SmartyPlugins/modifier.inlineHelp.php
+++ b/core/SmartyPlugins/modifier.inlineHelp.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -16,11 +16,11 @@
*/
function smarty_modifier_inlineHelp($text)
{
- return
- '<div class="ui-widget">'.
- '<div class="ui-inline-help ui-state-highlight ui-corner-all">'.
- '<span class="ui-icon ui-icon-info" style="float:left;margin-right:.3em;"></span>'.
- $text.
- '</div>'.
- '</div>';
+ return
+ '<div class="ui-widget">' .
+ '<div class="ui-inline-help ui-state-highlight ui-corner-all">' .
+ '<span class="ui-icon ui-icon-info" style="float:left;margin-right:.3em;"></span>' .
+ $text .
+ '</div>' .
+ '</div>';
}
diff --git a/core/SmartyPlugins/modifier.money.php b/core/SmartyPlugins/modifier.money.php
index 7d9eaa7afe..29bd853566 100644
--- a/core/SmartyPlugins/modifier.money.php
+++ b/core/SmartyPlugins/modifier.money.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -18,11 +18,10 @@
*/
function smarty_modifier_money($amount)
{
- if(func_num_args() != 2)
- {
- throw new Exception('the smarty modifier money expects one parameter: the idSite.');
- }
- $idSite = func_get_args();
- $idSite = $idSite[1];
- return Piwik::getPrettyMoney($amount, $idSite);
+ if (func_num_args() != 2) {
+ throw new Exception('the smarty modifier money expects one parameter: the idSite.');
+ }
+ $idSite = func_get_args();
+ $idSite = $idSite[1];
+ return Piwik::getPrettyMoney($amount, $idSite);
}
diff --git a/core/SmartyPlugins/modifier.stripeol.php b/core/SmartyPlugins/modifier.stripeol.php
index aa8bdbbce4..754ce4df06 100644
--- a/core/SmartyPlugins/modifier.stripeol.php
+++ b/core/SmartyPlugins/modifier.stripeol.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
diff --git a/core/SmartyPlugins/modifier.sumtime.php b/core/SmartyPlugins/modifier.sumtime.php
index cbd50f4618..2573719039 100644
--- a/core/SmartyPlugins/modifier.sumtime.php
+++ b/core/SmartyPlugins/modifier.sumtime.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -26,5 +26,5 @@
*/
function smarty_modifier_sumtime($numberOfSeconds)
{
- return Piwik::getPrettyTimeFromSeconds($numberOfSeconds);
+ return Piwik::getPrettyTimeFromSeconds($numberOfSeconds);
}
diff --git a/core/SmartyPlugins/modifier.translate.php b/core/SmartyPlugins/modifier.translate.php
index 0b25c714dc..6c2fcefca0 100644
--- a/core/SmartyPlugins/modifier.translate.php
+++ b/core/SmartyPlugins/modifier.translate.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -27,20 +27,17 @@
*/
function smarty_modifier_translate($stringToken)
{
- if(func_num_args() <= 1)
- {
- $aValues = array();
- }
- else
- {
- $aValues = func_get_args();
- array_shift($aValues);
- }
-
- try {
- $stringTranslated = Piwik_Translate($stringToken, $aValues);
- } catch( Exception $e) {
- $stringTranslated = $stringToken;
- }
- return $stringTranslated;
+ if (func_num_args() <= 1) {
+ $aValues = array();
+ } else {
+ $aValues = func_get_args();
+ array_shift($aValues);
+ }
+
+ try {
+ $stringTranslated = Piwik_Translate($stringToken, $aValues);
+ } catch (Exception $e) {
+ $stringTranslated = $stringToken;
+ }
+ return $stringTranslated;
}
diff --git a/core/SmartyPlugins/modifier.unescape.php b/core/SmartyPlugins/modifier.unescape.php
index 860841422d..971e9eabcc 100644
--- a/core/SmartyPlugins/modifier.unescape.php
+++ b/core/SmartyPlugins/modifier.unescape.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -21,7 +21,7 @@
*/
function smarty_modifier_unescape($string, $char_set = 'UTF-8')
{
- return html_entity_decode($string, ENT_QUOTES, $char_set);
+ return html_entity_decode($string, ENT_QUOTES, $char_set);
}
/* vim: set expandtab: */
diff --git a/core/SmartyPlugins/modifier.urlRewriteBasicView.php b/core/SmartyPlugins/modifier.urlRewriteBasicView.php
index 4de05453e8..023897dcd3 100644
--- a/core/SmartyPlugins/modifier.urlRewriteBasicView.php
+++ b/core/SmartyPlugins/modifier.urlRewriteBasicView.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -18,23 +18,20 @@
*/
function smarty_modifier_urlRewriteBasicView($parameters)
{
- // replace module=X by moduleToLoad=X
- // replace action=Y by actionToLoad=Y
- $parameters['moduleToLoad'] = $parameters['module'];
- unset($parameters['module']);
+ // replace module=X by moduleToLoad=X
+ // replace action=Y by actionToLoad=Y
+ $parameters['moduleToLoad'] = $parameters['module'];
+ unset($parameters['module']);
- if(isset( $parameters['action']))
- {
- $parameters['actionToLoad'] = $parameters['action'];
- unset($parameters['action']);
- }
- else
- {
- $parameters['actionToLoad'] = null;
- }
- $url = Piwik_Url::getCurrentQueryStringWithParametersModified($parameters);
+ if (isset($parameters['action'])) {
+ $parameters['actionToLoad'] = $parameters['action'];
+ unset($parameters['action']);
+ } else {
+ $parameters['actionToLoad'] = null;
+ }
+ $url = Piwik_Url::getCurrentQueryStringWithParametersModified($parameters);
- // add module=CoreHome&action=showInContext
- $url = $url . '&amp;module=CoreHome&amp;action=showInContext';
- return Piwik_Common::sanitizeInputValue($url);
+ // add module=CoreHome&action=showInContext
+ $url = $url . '&amp;module=CoreHome&amp;action=showInContext';
+ return Piwik_Common::sanitizeInputValue($url);
}
diff --git a/core/SmartyPlugins/modifier.urlRewriteWithParameters.php b/core/SmartyPlugins/modifier.urlRewriteWithParameters.php
index 69d8d37484..50ec31cac4 100644
--- a/core/SmartyPlugins/modifier.urlRewriteWithParameters.php
+++ b/core/SmartyPlugins/modifier.urlRewriteWithParameters.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -18,7 +18,7 @@
*/
function smarty_modifier_urlRewriteWithParameters($parameters)
{
- $parameters['updated'] = null;
- $url = Piwik_Url::getCurrentQueryStringWithParametersModified($parameters);
- return Piwik_Common::sanitizeInputValue($url);
+ $parameters['updated'] = null;
+ $url = Piwik_Url::getCurrentQueryStringWithParametersModified($parameters);
+ return Piwik_Common::sanitizeInputValue($url);
}
diff --git a/core/SmartyPlugins/outputfilter.ajaxcdn.php b/core/SmartyPlugins/outputfilter.ajaxcdn.php
index 2aa7006e48..5852541f04 100644
--- a/core/SmartyPlugins/outputfilter.ajaxcdn.php
+++ b/core/SmartyPlugins/outputfilter.ajaxcdn.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -27,24 +27,24 @@
*/
function smarty_outputfilter_ajaxcdn($source, &$smarty)
{
- $jquery_version = Piwik_Config::getInstance()->General['jquery_version'];
- $jqueryui_version = Piwik_Config::getInstance()->General['jqueryui_version'];
+ $jquery_version = Piwik_Config::getInstance()->General['jquery_version'];
+ $jqueryui_version = Piwik_Config::getInstance()->General['jqueryui_version'];
- $pattern = array(
- '~<link rel="stylesheet" type="text/css" href="libs/jquery/themes/([^"]*)" />~',
- '~<script type="text/javascript" src="libs/jquery/jquery\.js([^"]*)">~',
- '~<script type="text/javascript" src="libs/jquery/jquery-ui\.js([^"]*)">~',
- '~<script type="text/javascript" src="libs/jquery/jquery-ui-18n\.js([^"]*)">~',
- );
+ $pattern = array(
+ '~<link rel="stylesheet" type="text/css" href="libs/jquery/themes/([^"]*)" />~',
+ '~<script type="text/javascript" src="libs/jquery/jquery\.js([^"]*)">~',
+ '~<script type="text/javascript" src="libs/jquery/jquery-ui\.js([^"]*)">~',
+ '~<script type="text/javascript" src="libs/jquery/jquery-ui-18n\.js([^"]*)">~',
+ );
- // IE7 and IE8 bug: downloads css twice if scheme not specified
- $requestMethod = Piwik_Url::getCurrentScheme();
- $replace = array(
- '<link rel="stylesheet" type="text/css" href="'.$requestMethod.'://ajax.googleapis.com/ajax/libs/jqueryui/'.$jqueryui_version.'/themes/\\1" />',
- '<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/'.$jquery_version.'/jquery.min.js">',
- '<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/'.$jqueryui_version.'/jquery-ui.min.js">',
- '<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/'.$jqueryui_version.'/i18n/jquery-ui-18n.min.js">',
- );
+ // IE7 and IE8 bug: downloads css twice if scheme not specified
+ $requestMethod = Piwik_Url::getCurrentScheme();
+ $replace = array(
+ '<link rel="stylesheet" type="text/css" href="' . $requestMethod . '://ajax.googleapis.com/ajax/libs/jqueryui/' . $jqueryui_version . '/themes/\\1" />',
+ '<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/' . $jquery_version . '/jquery.min.js">',
+ '<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/' . $jqueryui_version . '/jquery-ui.min.js">',
+ '<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/' . $jqueryui_version . '/i18n/jquery-ui-18n.min.js">',
+ );
- return preg_replace($pattern, $replace, $source);
+ return preg_replace($pattern, $replace, $source);
}
diff --git a/core/SmartyPlugins/outputfilter.cachebuster.php b/core/SmartyPlugins/outputfilter.cachebuster.php
index 4431ac6230..a51c3af937 100644
--- a/core/SmartyPlugins/outputfilter.cachebuster.php
+++ b/core/SmartyPlugins/outputfilter.cachebuster.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
@@ -30,21 +30,21 @@
*/
function smarty_outputfilter_cachebuster($source, &$smarty)
{
- $tag = 'cb=' . $smarty->get_template_vars('cacheBuster');
+ $tag = 'cb=' . $smarty->get_template_vars('cacheBuster');
- $pattern = array(
- '~<script type=[\'"]text/javascript[\'"] src=[\'"]([^\'"]+)[\'"]>~',
- '~<script src=[\'"]([^\'"]+)[\'"] type=[\'"]text/javascript[\'"]>~',
- '~<link rel=[\'"]stylesheet[\'"] type=[\'"]text/css[\'"] href=[\'"]([^\'"]+)[\'"] ?/?>~',
- '~(src|href)=\"index.php\?module=([A-Za-z0-9_]+)&action=([A-Za-z0-9_]+)\?cb=~',
- );
+ $pattern = array(
+ '~<script type=[\'"]text/javascript[\'"] src=[\'"]([^\'"]+)[\'"]>~',
+ '~<script src=[\'"]([^\'"]+)[\'"] type=[\'"]text/javascript[\'"]>~',
+ '~<link rel=[\'"]stylesheet[\'"] type=[\'"]text/css[\'"] href=[\'"]([^\'"]+)[\'"] ?/?>~',
+ '~(src|href)=\"index.php\?module=([A-Za-z0-9_]+)&action=([A-Za-z0-9_]+)\?cb=~',
+ );
- $replace = array(
- '<script type="text/javascript" src="$1?'. $tag .'">',
- '<script type="text/javascript" src="$1?'. $tag .'">',
- '<link rel="stylesheet" type="text/css" href="$1?'. $tag .'" />',
- '$1="index.php?module=$2&amp;action=$3&amp;cb=',
- );
+ $replace = array(
+ '<script type="text/javascript" src="$1?' . $tag . '">',
+ '<script type="text/javascript" src="$1?' . $tag . '">',
+ '<link rel="stylesheet" type="text/css" href="$1?' . $tag . '" />',
+ '$1="index.php?module=$2&amp;action=$3&amp;cb=',
+ );
- return preg_replace($pattern, $replace, $source);
+ return preg_replace($pattern, $replace, $source);
}
diff --git a/core/TCPDF.php b/core/TCPDF.php
index 77b5a792b4..2be9e09ad9 100644
--- a/core/TCPDF.php
+++ b/core/TCPDF.php
@@ -4,7 +4,7 @@
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -21,74 +21,70 @@ require_once PIWIK_INCLUDE_PATH . '/libs/tcpdf/tcpdf.php';
*/
class Piwik_TCPDF extends TCPDF
{
- protected $footerContent = null;
- protected $currentPageNo = null;
+ protected $footerContent = null;
+ protected $currentPageNo = null;
+
+ /**
+ * Render page footer
+ *
+ * @see TCPDF::Footer()
+ */
+ function Footer()
+ {
+ //Don't show footer on the frontPage
+ if ($this->currentPageNo > 1) {
+ $this->SetY(-15);
+ $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
+ $this->Cell(0, 10, $this->footerContent . Piwik_Translate('PDFReports_Pagination', array($this->getAliasNumPage(), $this->getAliasNbPages())), 0, false, 'C', 0, '', 0, false, 'T', 'M');
+ }
+ }
- /**
- * Render page footer
- *
- * @see TCPDF::Footer()
- */
- function Footer()
- {
- //Don't show footer on the frontPage
- if ($this->currentPageNo > 1)
- {
- $this->SetY(-15);
- $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
- $this->Cell(0, 10, $this->footerContent . Piwik_Translate('PDFReports_Pagination', array($this->getAliasNumPage(), $this->getAliasNbPages())), 0, false, 'C', 0, '', 0, false, 'T', 'M');
- }
- }
+ /**
+ * @see TCPDF::Error()
+ * @param $msg
+ * @throws Exception
+ */
+ function Error($msg)
+ {
+ $this->_destroy(true);
+ throw new Exception($msg);
+ }
- /**
- * @see TCPDF::Error()
- * @param $msg
- * @throws Exception
- */
- function Error($msg)
- {
- $this->_destroy(true);
- throw new Exception($msg);
- }
-
- /**
- * Set current page number
- */
- function setCurrentPageNo()
- {
- if (empty($this->currentPageNo))
- {
- $this->currentPageNo = 1;
- }
- else
- {
- $this->currentPageNo++;
- }
- }
+ /**
+ * Set current page number
+ */
+ function setCurrentPageNo()
+ {
+ if (empty($this->currentPageNo)) {
+ $this->currentPageNo = 1;
+ } else {
+ $this->currentPageNo++;
+ }
+ }
- /**
- * Add page to document
- *
- * @see TCPDF::AddPage()
- *
- * @param string $orientation
- * @param mixed $format
- * @param bool $keepmargins
- * @param bool $tocpage
- */
- function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false)
- {
- parent::AddPage($orientation);
- $this->setCurrentPageNo();
- }
+ /**
+ * Add page to document
+ *
+ * @see TCPDF::AddPage()
+ *
+ * @param string $orientation
+ * @param mixed $format
+ * @param bool $keepmargins
+ * @param bool $tocpage
+ */
+ function AddPage($orientation = '', $format = '', $keepmargins = false, $tocpage = false)
+ {
+ parent::AddPage($orientation);
+ $this->setCurrentPageNo();
+ }
- /**
- * Set footer content
- *
- * @param string $footerContent
- */
- function SetFooterContent($footerContent)
- {
- $this->footerContent = $footerContent;
- }
+ /**
+ * Set footer content
+ *
+ * @param string $footerContent
+ */
+ function SetFooterContent($footerContent)
+ {
+ $this->footerContent = $footerContent;
+ }
}
diff --git a/core/TablePartitioning.php b/core/TablePartitioning.php
index 0f4df6b777..40bcec40d0 100644
--- a/core/TablePartitioning.php
+++ b/core/TablePartitioning.php
@@ -1,133 +1,130 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- *
+ *
* NB: When a new table is partitionned using this class, we have to update the method
* Piwik::getTablesInstalled() to add the new table to the list of tablename_* to fetch
- *
+ *
* @package Piwik
* @subpackage Piwik_TablePartitioning
*/
abstract class Piwik_TablePartitioning
{
- protected $tableName = null;
- protected $generatedTableName = null;
- protected $timestamp = null;
-
- static public $tablesAlreadyInstalled = null;
-
- public function __construct( $tableName )
- {
- $this->tableName = $tableName;
- }
-
- abstract protected function generateTableName() ;
-
- public function setTimestamp( $timestamp )
- {
- $this->timestamp = $timestamp;
- $this->generatedTableName = null;
- $this->getTableName();
- }
-
- public function getTableName()
- {
- // table name already processed
- if(!is_null($this->generatedTableName))
- {
- return $this->generatedTableName;
- }
-
- if(is_null($this->timestamp))
- {
- throw new Exception("You have to specify a timestamp for a Table Partitioning by date.");
- }
-
- // generate table name
- $this->generatedTableName = $this->generateTableName();
-
- // we make sure the table already exists
- $this->checkTableExists();
- }
-
- protected function checkTableExists()
- {
- if(is_null(self::$tablesAlreadyInstalled))
- {
- self::$tablesAlreadyInstalled = Piwik::getTablesInstalled($forceReload = false);
- }
-
- if(!in_array($this->generatedTableName, self::$tablesAlreadyInstalled))
- {
- $db = Zend_Registry::get('db');
- $sql = Piwik::getTableCreateSql($this->tableName);
-
- $config = Piwik_Config::getInstance();
- $prefixTables = $config->database['tables_prefix'];
- $sql = str_replace( $prefixTables . $this->tableName, $this->generatedTableName, $sql);
- try {
- $db->query( $sql );
- } catch(Exception $e) {
- // mysql error 1050: table already exists
- if(! $db->isErrNo($e, '1050'))
- {
- // failed for some other reason
- throw $e;
- }
- }
-
- self::$tablesAlreadyInstalled[] = $this->generatedTableName;
- }
- }
-
- public function __toString()
- {
- return $this->getTableName();
- }
+ protected $tableName = null;
+ protected $generatedTableName = null;
+ protected $timestamp = null;
+
+ static public $tablesAlreadyInstalled = null;
+
+ public function __construct($tableName)
+ {
+ $this->tableName = $tableName;
+ }
+
+ abstract protected function generateTableName();
+
+ public function setTimestamp($timestamp)
+ {
+ $this->timestamp = $timestamp;
+ $this->generatedTableName = null;
+ $this->getTableName();
+ }
+
+ public function getTableName()
+ {
+ // table name already processed
+ if (!is_null($this->generatedTableName)) {
+ return $this->generatedTableName;
+ }
+
+ if (is_null($this->timestamp)) {
+ throw new Exception("You have to specify a timestamp for a Table Partitioning by date.");
+ }
+
+ // generate table name
+ $this->generatedTableName = $this->generateTableName();
+
+ // we make sure the table already exists
+ $this->checkTableExists();
+ }
+
+ protected function checkTableExists()
+ {
+ if (is_null(self::$tablesAlreadyInstalled)) {
+ self::$tablesAlreadyInstalled = Piwik::getTablesInstalled($forceReload = false);
+ }
+
+ if (!in_array($this->generatedTableName, self::$tablesAlreadyInstalled)) {
+ $db = Zend_Registry::get('db');
+ $sql = Piwik::getTableCreateSql($this->tableName);
+
+ $config = Piwik_Config::getInstance();
+ $prefixTables = $config->database['tables_prefix'];
+ $sql = str_replace($prefixTables . $this->tableName, $this->generatedTableName, $sql);
+ try {
+ $db->query($sql);
+ } catch (Exception $e) {
+ // mysql error 1050: table already exists
+ if (!$db->isErrNo($e, '1050')) {
+ // failed for some other reason
+ throw $e;
+ }
+ }
+
+ self::$tablesAlreadyInstalled[] = $this->generatedTableName;
+ }
+ }
+
+ public function __toString()
+ {
+ return $this->getTableName();
+ }
}
/**
- *
+ *
* @package Piwik
* @subpackage Piwik_TablePartitioning
*/
class Piwik_TablePartitioning_Monthly extends Piwik_TablePartitioning
{
- public function __construct( $tableName )
- {
- parent::__construct($tableName);
- }
- protected function generateTableName()
- {
- $config = Piwik_Config::getInstance();
- return $config->database['tables_prefix'] . $this->tableName . "_" . date("Y_m", $this->timestamp);
- }
-
+ public function __construct($tableName)
+ {
+ parent::__construct($tableName);
+ }
+
+ protected function generateTableName()
+ {
+ $config = Piwik_Config::getInstance();
+ return $config->database['tables_prefix'] . $this->tableName . "_" . date("Y_m", $this->timestamp);
+ }
+
}
/**
- *
+ *
* @package Piwik
* @subpackage Piwik_TablePartitioning
*/
class Piwik_TablePartitioning_Daily extends Piwik_TablePartitioning
{
- public function __construct( $tableName )
- {
- parent::__construct($tableName);
- }
- protected function generateTableName()
- {
- $config = Piwik_Config::getInstance();
- return $config->database['tables_prefix'] . $this->tableName . "_" . date("Y_m_d", $this->timestamp);
- }
+ public function __construct($tableName)
+ {
+ parent::__construct($tableName);
+ }
+
+ protected function generateTableName()
+ {
+ $config = Piwik_Config::getInstance();
+ return $config->database['tables_prefix'] . $this->tableName . "_" . date("Y_m_d", $this->timestamp);
+ }
}
diff --git a/core/TaskScheduler.php b/core/TaskScheduler.php
index a2f8810930..1a29780f72 100644
--- a/core/TaskScheduler.php
+++ b/core/TaskScheduler.php
@@ -16,7 +16,7 @@ define('DEBUG_FORCE_SCHEDULED_TASKS', false);
* Piwik_TaskScheduler is the class used to manage the execution of periodicaly planned task.
*
* It performs the following actions :
- * - Identifies tasks of Piwik
+ * - Identifies tasks of Piwik
* - Runs tasks
*
* @package Piwik
@@ -24,151 +24,142 @@ define('DEBUG_FORCE_SCHEDULED_TASKS', false);
class Piwik_TaskScheduler
{
- const GET_TASKS_EVENT = "TaskScheduler.getScheduledTasks";
- const TIMETABLE_OPTION_STRING = "TaskScheduler.timetable";
- static private $running = false;
-
- /**
- * runTasks collects tasks defined within piwik plugins, runs them if they are scheduled and reschedules
- * the tasks that have been executed.
- *
- * @return array
- */
- static public function runTasks()
- {
- // get the array where rescheduled timetables are stored
- $timetable = self::getTimetableFromOptionTable();
-
- // collect tasks
- $tasks = array();
- Piwik_PostEvent(self::GET_TASKS_EVENT, $tasks);
-
- // remove from timetable tasks that are not active anymore
- $activeTaskNames = array();
- foreach($tasks as $task)
- {
- $activeTaskNames[] = $task->getName();
- }
- foreach(array_keys($timetable) as $taskName)
- {
- if(!in_array($taskName, $activeTaskNames))
- {
- unset($timetable[$taskName]);
- }
- }
-
- // for every priority level, starting with the highest and concluding with the lowest
- $executionResults = array();
- for ($priority = Piwik_ScheduledTask::HIGHEST_PRIORITY;
- $priority <= Piwik_ScheduledTask::LOWEST_PRIORITY;
- ++$priority)
- {
- // loop through each task
- foreach ($tasks as $task)
- {
- // if the task does not have the current priority level, don't execute it yet
- if ($task->getPriority() != $priority)
- {
- continue;
- }
-
- $taskName = $task->getName();
- if (self::taskShouldBeExecuted($taskName, $timetable))
- {
- self::$running = true;
- $message = self::executeTask($task);
- self::$running = false;
-
- $executionResults[] = array('task' => $taskName, 'output' => $message);
- }
-
- if(self::taskShouldBeRescheduled($taskName, $timetable))
- {
- // update the scheduled time
- $timetable[$taskName] = $task->getRescheduledTime();
- Piwik_SetOption(self::TIMETABLE_OPTION_STRING, serialize($timetable));
- }
- }
- }
-
- return $executionResults;
- }
-
- static public function isTaskBeingExecuted()
- {
- return self::$running;
- }
-
+ const GET_TASKS_EVENT = "TaskScheduler.getScheduledTasks";
+ const TIMETABLE_OPTION_STRING = "TaskScheduler.timetable";
+ static private $running = false;
+
+ /**
+ * runTasks collects tasks defined within piwik plugins, runs them if they are scheduled and reschedules
+ * the tasks that have been executed.
+ *
+ * @return array
+ */
+ static public function runTasks()
+ {
+ // get the array where rescheduled timetables are stored
+ $timetable = self::getTimetableFromOptionTable();
+
+ // collect tasks
+ $tasks = array();
+ Piwik_PostEvent(self::GET_TASKS_EVENT, $tasks);
+
+ // remove from timetable tasks that are not active anymore
+ $activeTaskNames = array();
+ foreach ($tasks as $task) {
+ $activeTaskNames[] = $task->getName();
+ }
+ foreach (array_keys($timetable) as $taskName) {
+ if (!in_array($taskName, $activeTaskNames)) {
+ unset($timetable[$taskName]);
+ }
+ }
+
+ // for every priority level, starting with the highest and concluding with the lowest
+ $executionResults = array();
+ for ($priority = Piwik_ScheduledTask::HIGHEST_PRIORITY;
+ $priority <= Piwik_ScheduledTask::LOWEST_PRIORITY;
+ ++$priority) {
+ // loop through each task
+ foreach ($tasks as $task) {
+ // if the task does not have the current priority level, don't execute it yet
+ if ($task->getPriority() != $priority) {
+ continue;
+ }
+
+ $taskName = $task->getName();
+ if (self::taskShouldBeExecuted($taskName, $timetable)) {
+ self::$running = true;
+ $message = self::executeTask($task);
+ self::$running = false;
+
+ $executionResults[] = array('task' => $taskName, 'output' => $message);
+ }
+
+ if (self::taskShouldBeRescheduled($taskName, $timetable)) {
+ // update the scheduled time
+ $timetable[$taskName] = $task->getRescheduledTime();
+ Piwik_SetOption(self::TIMETABLE_OPTION_STRING, serialize($timetable));
+ }
+ }
+ }
+
+ return $executionResults;
+ }
+
+ static public function isTaskBeingExecuted()
+ {
+ return self::$running;
+ }
+
/**
* return the next task schedule for a given class and method name
- *
+ *
* @param string $className
* @param string $methodName
* @param string $methodParameter
* @return mixed int|bool the next schedule in miliseconds, false if task has never been run
*/
- static public function getScheduledTimeForMethod($className, $methodName, $methodParameter = null) {
-
- // get the array where rescheduled timetables are stored
- $timetable = self::getTimetableFromOptionTable();
-
- $taskName = Piwik_ScheduledTask::getTaskName($className, $methodName, $methodParameter);
-
- return self::taskHasBeenScheduledOnce($taskName, $timetable) ? $timetable[$taskName] : false;
- }
-
- /*
- * Task has to be executed if :
- * - the task has already been scheduled once and the current system time is greater than the scheduled time.
- * - execution is forced, see $forceTaskExecution
- */
- static private function taskShouldBeExecuted($taskName, $timetable)
- {
- $forceTaskExecution =
- (isset($GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) && $GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS'])
- || DEBUG_FORCE_SCHEDULED_TASKS;
-
- return $forceTaskExecution || (self::taskHasBeenScheduledOnce($taskName, $timetable) && time() >= $timetable[$taskName]);
- }
-
- /*
- * Task has to be rescheduled if :
- * - the task has to be executed
- * - the task has never been scheduled before
- */
- static private function taskShouldBeRescheduled($taskName, $timetable)
- {
- return !self::taskHasBeenScheduledOnce($taskName, $timetable) || self::taskShouldBeExecuted($taskName, $timetable);
- }
-
- static private function taskHasBeenScheduledOnce($taskName, $timetable)
- {
- return isset($timetable[$taskName]);
- }
-
- static private function getTimetableFromOptionValue($option) {
- $unserializedTimetable = @unserialize($option);
- return $unserializedTimetable === false ? array() : $unserializedTimetable;
+ static public function getScheduledTimeForMethod($className, $methodName, $methodParameter = null)
+ {
+
+ // get the array where rescheduled timetables are stored
+ $timetable = self::getTimetableFromOptionTable();
+
+ $taskName = Piwik_ScheduledTask::getTaskName($className, $methodName, $methodParameter);
+
+ return self::taskHasBeenScheduledOnce($taskName, $timetable) ? $timetable[$taskName] : false;
+ }
+
+ /*
+ * Task has to be executed if :
+ * - the task has already been scheduled once and the current system time is greater than the scheduled time.
+ * - execution is forced, see $forceTaskExecution
+ */
+ static private function taskShouldBeExecuted($taskName, $timetable)
+ {
+ $forceTaskExecution =
+ (isset($GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) && $GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS'])
+ || DEBUG_FORCE_SCHEDULED_TASKS;
+
+ return $forceTaskExecution || (self::taskHasBeenScheduledOnce($taskName, $timetable) && time() >= $timetable[$taskName]);
+ }
+
+ /*
+ * Task has to be rescheduled if :
+ * - the task has to be executed
+ * - the task has never been scheduled before
+ */
+ static private function taskShouldBeRescheduled($taskName, $timetable)
+ {
+ return !self::taskHasBeenScheduledOnce($taskName, $timetable) || self::taskShouldBeExecuted($taskName, $timetable);
+ }
+
+ static private function taskHasBeenScheduledOnce($taskName, $timetable)
+ {
+ return isset($timetable[$taskName]);
+ }
+
+ static private function getTimetableFromOptionValue($option)
+ {
+ $unserializedTimetable = @unserialize($option);
+ return $unserializedTimetable === false ? array() : $unserializedTimetable;
}
- static private function getTimetableFromOptionTable()
- {
- return self::getTimetableFromOptionValue(Piwik_GetOption(self::TIMETABLE_OPTION_STRING));
- }
-
- static private function executeTask($task)
- {
- try
- {
- $timer = new Piwik_Timer();
- call_user_func(array($task->getObjectInstance(), $task->getMethodName()), $task->getMethodParameter());
- $message = $timer->__toString();
- }
- catch(Exception $e)
- {
- $message = 'ERROR: ' . $e->getMessage();
- }
-
- return $message;
- }
+ static private function getTimetableFromOptionTable()
+ {
+ return self::getTimetableFromOptionValue(Piwik_GetOption(self::TIMETABLE_OPTION_STRING));
+ }
+
+ static private function executeTask($task)
+ {
+ try {
+ $timer = new Piwik_Timer();
+ call_user_func(array($task->getObjectInstance(), $task->getMethodName()), $task->getMethodParameter());
+ $message = $timer->__toString();
+ } catch (Exception $e) {
+ $message = 'ERROR: ' . $e->getMessage();
+ }
+
+ return $message;
+ }
}
diff --git a/core/Timer.php b/core/Timer.php
index 7e59cc674f..e747c6d3bd 100644
--- a/core/Timer.php
+++ b/core/Timer.php
@@ -1,66 +1,65 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- *
+ *
* @package Piwik
*/
class Piwik_Timer
{
- private $timerStart;
- private $memoryStart;
+ private $timerStart;
+ private $memoryStart;
- public function __construct()
- {
- $this->init();
- }
+ public function __construct()
+ {
+ $this->init();
+ }
- public function init()
- {
- $this->timerStart = $this->getMicrotime();
- $this->memoryStart = $this->getMemoryUsage();
- }
+ public function init()
+ {
+ $this->timerStart = $this->getMicrotime();
+ $this->memoryStart = $this->getMemoryUsage();
+ }
- public function getTime($decimals = 3)
- {
- return number_format($this->getMicrotime() - $this->timerStart, $decimals, '.', '');
- }
-
- public function getTimeMs($decimals = 3)
- {
- return number_format(1000*($this->getMicrotime() - $this->timerStart), $decimals, '.', '');
- }
+ public function getTime($decimals = 3)
+ {
+ return number_format($this->getMicrotime() - $this->timerStart, $decimals, '.', '');
+ }
- public function getMemoryLeak()
- {
- return "Memory delta: ".Piwik::getPrettySizeFromBytes($this->getMemoryUsage() - $this->memoryStart);
- }
-
- public function __toString()
- {
- return "Time elapsed: ". $this->getTime() ."s";
- }
-
- private function getMicrotime()
- {
- list($micro_seconds, $seconds) = explode(" ", microtime());
- return ((float)$micro_seconds + (float)$seconds);
- }
+ public function getTimeMs($decimals = 3)
+ {
+ return number_format(1000 * ($this->getMicrotime() - $this->timerStart), $decimals, '.', '');
+ }
- private function getMemoryUsage()
- {
- if(function_exists('memory_get_usage'))
- {
- return memory_get_usage();
- }
- return 0;
- }
+ public function getMemoryLeak()
+ {
+ return "Memory delta: " . Piwik::getPrettySizeFromBytes($this->getMemoryUsage() - $this->memoryStart);
+ }
+
+ public function __toString()
+ {
+ return "Time elapsed: " . $this->getTime() . "s";
+ }
+
+ private function getMicrotime()
+ {
+ list($micro_seconds, $seconds) = explode(" ", microtime());
+ return ((float)$micro_seconds + (float)$seconds);
+ }
+
+ private function getMemoryUsage()
+ {
+ if (function_exists('memory_get_usage')) {
+ return memory_get_usage();
+ }
+ return 0;
+ }
}
diff --git a/core/Tracker.php b/core/Tracker.php
index 5255492734..935ffbf3ca 100644
--- a/core/Tracker.php
+++ b/core/Tracker.php
@@ -21,827 +21,760 @@
*/
class Piwik_Tracker
{
- protected $stateValid = self::STATE_NOTHING_TO_NOTICE;
- /**
- * @var Piwik_Tracker_Db
- */
- protected static $db = null;
-
- const STATE_NOTHING_TO_NOTICE = 1;
- const STATE_LOGGING_DISABLE = 10;
- const STATE_EMPTY_REQUEST = 11;
- const STATE_NOSCRIPT_REQUEST = 13;
-
- // We use hex ID that are 16 chars in length, ie. 64 bits IDs
- const LENGTH_HEX_ID_STRING = 16;
- const LENGTH_BINARY_ID = 8;
-
- // These are also hardcoded in the Javascript
- const MAX_CUSTOM_VARIABLES = 5;
- const MAX_LENGTH_CUSTOM_VARIABLE = 200;
-
- protected $authenticated = false;
- static protected $forcedDateTime = null;
- static protected $forcedIpString = null;
- static protected $forcedVisitorId = null;
-
- static protected $pluginsNotToLoad = array();
-
- /**
- * The set of visits to track.
- *
- * @var array
- */
- private $requests = array();
-
- /**
- * The token auth supplied with a bulk visits POST.
- *
- * @var string
- */
- private $tokenAuth = null;
-
- /**
- * Whether we're currently using bulk tracking or not.
- *
- * @var bool
- */
- private $usingBulkTracking = false;
-
- /**
- * The number of requests that have been successfully logged.
- *
- * @var int
- */
- private $countOfLoggedRequests = 0;
-
- public function clear()
- {
- self::$forcedIpString = null;
- self::$forcedDateTime = null;
- self::$forcedVisitorId = null;
- $this->stateValid = self::STATE_NOTHING_TO_NOTICE;
- $this->authenticated = false;
- }
-
- public static function setForceIp($ipString)
- {
- self::$forcedIpString = $ipString;
- }
- public static function setForceDateTime( $dateTime )
- {
- self::$forcedDateTime = $dateTime;
- }
-
- public static function setForceVisitorId($visitorId)
- {
- self::$forcedVisitorId = $visitorId;
- }
-
- public function getCurrentTimestamp()
- {
- if(!is_null(self::$forcedDateTime))
- {
- return strtotime(self::$forcedDateTime);
- }
- return time();
- }
-
- /**
- * Do not load the specified plugins (used during testing, to disable Provider plugin)
- * @param array $plugins
- */
- static public function setPluginsNotToLoad($plugins)
- {
- self::$pluginsNotToLoad = $plugins;
- }
-
- /**
- * Get list of plugins to not load
- *
- * @return array
- */
- static public function getPluginsNotToLoad()
- {
- return self::$pluginsNotToLoad;
- }
-
- /**
- * Update Tracker config
- *
- * @param string $name Setting name
- * @param mixed $value Value
- */
- static private function updateTrackerConfig($name, $value)
- {
- $section = Piwik_Config::getInstance()->Tracker;
- $section[$name] = $value;
- Piwik_Config::getInstance()->Tracker = $section;
- }
-
- protected function initRequests($args)
- {
- $rawData = file_get_contents("php://input");
- if (!empty($rawData))
- {
- $this->usingBulkTracking = strpos($rawData, '"requests"') || strpos($rawData, "'requests'");
- if($this->usingBulkTracking)
- {
- return $this->initBulkTrackingRequests($rawData);
- }
- }
-
- // Not using bulk tracking
- $this->requests = $args ? $args : (!empty($_GET) || !empty($_POST) ? array($_GET + $_POST) : array());
- }
-
- private function initBulkTrackingRequests($rawData)
- {
- // POST data can be array of string URLs or array of arrays w/ visit info
- $jsonData = Piwik_Common::json_decode($rawData, $assoc = true);
-
- if (isset($jsonData['requests']))
- {
- $this->requests = $jsonData['requests'];
- }
- $this->tokenAuth = Piwik_Common::getRequestVar('token_auth', false, null, $jsonData);
- if(empty($this->tokenAuth))
- {
- throw new Exception(" token_auth must be specified when using Bulk Tracking Import. See <a href='http://piwik.org/docs/tracking-api/reference/'>Tracking Doc</a>");
- }
- if (!empty($this->requests))
- {
- $idSiteForAuthentication = 0;
-
- foreach ($this->requests as &$request)
- {
- // if a string is sent, we assume its a URL and try to parse it
- if (is_string($request))
- {
- $params = array();
-
- $url = @parse_url($request);
- if (!empty($url))
- {
- @parse_str($url['query'], $params);
- $request = $params;
- if(isset($request['idsite']) && !$idSiteForAuthentication)
- {
- $idSiteForAuthentication = $request['idsite'];
- }
- }
- }
- }
-
- // a Bulk Tracking request that is not authenticated should fail
- if(!$this->authenticateSuperUserOrAdmin(array('idsite' => $idSiteForAuthentication)))
- {
- throw new Exception(" token_auth specified is not valid for site ". intval($idSiteForAuthentication));
- }
- }
- }
- /**
- * Main - tracks the visit/action
- *
- * @param array $args Optional Request Array
- */
- public function main($args = null)
- {
- $displayedGIF = false;
- $this->initRequests($args);
- if (!empty($this->requests))
- {
- // handle all visits
- foreach ($this->requests as $request)
- {
- $this->init($request);
-
- if(!$displayedGIF && !$this->authenticated)
- {
- $this->outputTransparentGif();
- $displayedGIF = true;
- }
-
- try
- {
- if ($this->isVisitValid())
- {
- self::connectDatabaseIfNotConnected();
-
- $visit = $this->getNewVisitObject();
- $visit->setRequest($request);
- $visit->handle();
- unset($visit);
- }
- else
- {
- printDebug("The request is invalid: empty request, or maybe tracking is disabled in the config.ini.php via record_statistics=0");
- }
- } catch (Piwik_Tracker_Db_Exception $e) {
- printDebug("<b>".$e->getMessage()."</b>");
- $this->exitWithException($e, $this->authenticated);
- } catch(Piwik_Tracker_Visit_Excluded $e) {
- } catch(Exception $e) {
- $this->exitWithException($e, $this->authenticated);
- }
- $this->clear();
-
- // increment successfully logged request count. make sure to do this after try-catch,
- // since an excluded visit is considered 'successfully logged'
- ++$this->countOfLoggedRequests;
- }
-
- if(!$displayedGIF)
- {
- $this->outputTransparentGif();
- $displayedGIF = true;
- }
- }
- else
- {
- $this->handleEmptyRequest($_GET + $_POST);
- }
-
- // run scheduled task
- try
- {
- if($this->shouldRunScheduledTasks())
- {
- self::runScheduledTasks($now = $this->getCurrentTimestamp());
- }
- }
- catch (Exception $e)
- {
- $this->exitWithException($e, $this->authenticated);
- }
-
- $this->end();
- }
-
- protected function shouldRunScheduledTasks()
- {
- // don't run scheduled tasks in CLI mode from Tracker, this is the case
- // where we bulk load logs & don't want to lose time with tasks
- return !Piwik_Common::isPhpCliMode()
- && !$this->authenticated
- && $this->getState() != self::STATE_LOGGING_DISABLE;
- }
-
- /**
- * Tracker requests will automatically trigger the Scheduled tasks.
- * This is useful for users who don't setup the cron,
- * but still want daily/weekly/monthly PDF reports emailed automatically.
- *
- * This is similar to calling the API CoreAdminHome.runScheduledTasks (see misc/cron/archive.php)
- *
- * @param int $now Current timestamp
- */
- protected static function runScheduledTasks($now)
- {
- // Currently, there is no hourly tasks. When there are some,
- // this could be too agressive minimum interval (some hours would be skipped in case of low traffic)
- $minimumInterval = Piwik_Config::getInstance()->Tracker['scheduled_tasks_min_interval'];
-
- // If the user disabled browser archiving, he has already setup a cron
- // To avoid parallel requests triggering the Scheduled Tasks,
- // Get last time tasks started executing
- $cache = Piwik_Tracker_Cache::getCacheGeneral();
- if($minimumInterval <= 0
- || empty($cache['isBrowserTriggerArchivingEnabled']))
- {
- printDebug("-> Scheduled tasks not running in Tracker: Browser archiving is disabled.");
- return;
- }
- $nextRunTime = $cache['lastTrackerCronRun'] + $minimumInterval;
- if( (isset($GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) && $GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS'])
- || $cache['lastTrackerCronRun'] === false
- || $nextRunTime < $now )
- {
- $cache['lastTrackerCronRun'] = $now;
- Piwik_Tracker_Cache::setCacheGeneral( $cache );
- self::initCorePiwikInTrackerMode();
- Piwik_SetOption('lastTrackerCronRun', $cache['lastTrackerCronRun']);
- printDebug('-> Scheduled Tasks: Starting...');
-
- // save current user privilege and temporarily assume super user privilege
- $isSuperUser = Piwik::isUserIsSuperUser();
-
- // Scheduled tasks assume Super User is running
- Piwik::setUserIsSuperUser();
-
- // While each plugins should ensure that necessary languages are loaded,
- // we ensure English translations at least are loaded
- Piwik_Translate::getInstance()->loadEnglishTranslation();
-
- $resultTasks = Piwik_TaskScheduler::runTasks();
-
- // restore original user privilege
- Piwik::setUserIsSuperUser($isSuperUser);
-
- printDebug($resultTasks);
- printDebug('Finished Scheduled Tasks.');
- }
- else
- {
- printDebug("-> Scheduled tasks not triggered.");
- }
- printDebug("Next run will be from: ". date('Y-m-d H:i:s', $nextRunTime) .' UTC');
- }
-
- static public $initTrackerMode = false;
-
- /*
- * Used to initialize core Piwik components on a piwik.php request
- * Eg. when cache is missed and we will be calling some APIs to generate cache
- */
- static public function initCorePiwikInTrackerMode()
- {
- if(!empty($GLOBALS['PIWIK_TRACKER_MODE'])
- && self::$initTrackerMode === false)
- {
- self::$initTrackerMode = true;
- require_once PIWIK_INCLUDE_PATH . '/core/Loader.php';
- require_once PIWIK_INCLUDE_PATH . '/core/Option.php';
- try {
- $access = Zend_Registry::get('access');
- } catch (Exception $e) {
- Piwik::createAccessObject();
- }
- try {
- $config = Piwik_Config::getInstance();
- } catch (Exception $e) {
- Piwik::createConfigObject();
- }
- try {
- $db = Zend_Registry::get('db');
- } catch (Exception $e) {
- Piwik::createDatabaseObject();
- }
-
- $pluginsManager = Piwik_PluginsManager::getInstance();
- $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
- $pluginsForcedNotToLoad = Piwik_Tracker::getPluginsNotToLoad();
- $pluginsToLoad = array_diff($pluginsToLoad, $pluginsForcedNotToLoad);
- $pluginsManager->loadPlugins( $pluginsToLoad );
- }
- }
-
- /**
- * Echos an error message & other information, then exits.
- *
- * @param Exception $e
- * @param bool $authenticated
- */
- protected function exitWithException($e, $authenticated)
- {
- if ($this->usingBulkTracking)
- {
- // when doing bulk tracking we return JSON so the caller will know how many succeeded
- $result = array('succeeded' => $this->countOfLoggedRequests);
-
- // send error when in debug mode or when authenticated (which happens when doing log importing,
- // for example)
- if ((isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) || $authenticated)
- {
- $result['error'] = Piwik_Tracker_GetErrorMessage($e);
- }
-
- echo Piwik_Common::json_encode($result);
-
- exit;
- }
- else
- {
- Piwik_Tracker_ExitWithException($e, $authenticated);
- }
- }
-
- /**
- * Returns the date in the "Y-m-d H:i:s" PHP format
- *
- * @param int $timestamp
- * @return string
- */
- public static function getDatetimeFromTimestamp($timestamp)
- {
- return date("Y-m-d H:i:s", $timestamp);
- }
-
- /**
- * Initialization
- */
- protected function init( $request )
- {
- $this->handleTrackingApi($request);
- $this->loadTrackerPlugins($request);
- $this->handleDisabledTracker();
- $this->handleEmptyRequest($request);
-
- printDebug("Current datetime: ".date("Y-m-d H:i:s", $this->getCurrentTimestamp()));
- }
-
- /**
- * Cleanup
- */
- protected function end()
- {
- switch($this->getState())
- {
- case self::STATE_LOGGING_DISABLE:
- printDebug("Logging disabled, display transparent logo");
- break;
-
- case self::STATE_EMPTY_REQUEST:
- printDebug("Empty request => Piwik page");
- echo "<a href='/'>Piwik</a> is a free open source web <a href='http://piwik.org'>analytics</a> that lets you keep control of your data.";
- break;
-
- case self::STATE_NOSCRIPT_REQUEST:
- case self::STATE_NOTHING_TO_NOTICE:
- default:
- printDebug("Nothing to notice => default behaviour");
- break;
- }
- printDebug("End of the page.");
-
- if($GLOBALS['PIWIK_TRACKER_DEBUG'] === true)
- {
- if(isset(self::$db)) {
- self::$db->recordProfiling();
- Piwik::printSqlProfilingReportTracker(self::$db);
- }
- }
-
- self::disconnectDatabase();
- }
-
- /**
- * Factory to create database objects
- *
- * @param array $configDb Database configuration
- * @throws Exception
- * @return Piwik_Tracker_Db_Mysqli|Piwik_Tracker_Db_Pdo_Mysql
- */
- public static function factory($configDb)
- {
- switch($configDb['adapter'])
- {
- case 'PDO_MYSQL':
- require_once PIWIK_INCLUDE_PATH .'/core/Tracker/Db/Pdo/Mysql.php';
- return new Piwik_Tracker_Db_Pdo_Mysql($configDb);
-
- case 'MYSQLI':
- require_once PIWIK_INCLUDE_PATH .'/core/Tracker/Db/Mysqli.php';
- return new Piwik_Tracker_Db_Mysqli($configDb);
- }
-
- throw new Exception('Unsupported database adapter '.$configDb['adapter']);
- }
-
- public static function connectPiwikTrackerDb()
- {
- $db = null;
- $configDb = Piwik_Config::getInstance()->database;
-
- if(!isset($configDb['port']))
- {
- // before 0.2.4 there is no port specified in config file
- $configDb['port'] = '3306';
- }
-
- Piwik_PostEvent('Tracker.getDatabaseConfig', $configDb);
-
- $db = self::factory( $configDb );
- $db->connect();
-
- return $db;
- }
-
- public static function connectDatabaseIfNotConnected()
- {
- if( !is_null(self::$db))
- {
- return;
- }
-
- try {
- $db = null;
- Piwik_PostEvent('Tracker.createDatabase', $db);
- if(is_null($db))
- {
- $db = self::connectPiwikTrackerDb();
- }
- self::$db = $db;
- } catch(Exception $e) {
- throw new Piwik_Tracker_Db_Exception($e->getMessage(), $e->getCode());
- }
- }
-
- /**
- * @return Piwik_Tracker_Db
- */
- public static function getDatabase()
- {
- return self::$db;
- }
-
- public static function disconnectDatabase()
- {
- if(isset(self::$db))
- {
- self::$db->disconnect();
- self::$db = null;
- }
- }
-
- /**
- * Returns the Tracker_Visit object.
- * This method can be overwritten to use a different Tracker_Visit object
- *
- * @throws Exception
- * @return Piwik_Tracker_Visit
- */
- protected function getNewVisitObject()
- {
- $visit = null;
- Piwik_PostEvent('Tracker.getNewVisitObject', $visit);
-
- if(is_null($visit))
- {
- $visit = new Piwik_Tracker_Visit( self::$forcedIpString, self::$forcedDateTime, $this->authenticated );
- $visit->setForcedVisitorId(self::$forcedVisitorId);
- }
- elseif(!($visit instanceof Piwik_Tracker_Visit_Interface ))
- {
- throw new Exception("The Visit object set in the plugin must implement Piwik_Tracker_Visit_Interface");
- }
- return $visit;
- }
-
- protected function outputTransparentGif()
- {
- if( !isset($GLOBALS['PIWIK_TRACKER_DEBUG']) || !$GLOBALS['PIWIK_TRACKER_DEBUG'] )
- {
- $trans_gif_64 = "R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
- $this->sendHeader('Content-Type: image/gif');
-
- $requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
-
- if ($requestMethod !== 'GET')
- {
- $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '*';
- $this->sendHeader('Access-Control-Allow-Origin: ' . $origin);
- $this->sendHeader('Access-Control-Allow-Credentials: true');
- }
-
- print(base64_decode($trans_gif_64));
- }
- }
-
- protected function sendHeader($header)
- {
- Piwik_Common::sendHeader($header);
- }
-
- protected function isVisitValid()
- {
- return $this->stateValid !== self::STATE_LOGGING_DISABLE
- && $this->stateValid !== self::STATE_EMPTY_REQUEST;
- }
-
- protected function getState()
- {
- return $this->stateValid;
- }
-
- protected function setState( $value )
- {
- $this->stateValid = $value;
- }
-
- protected function loadTrackerPlugins( $request )
- {
- // Adding &dp=1 will disable the provider plugin, if token_auth is used (used to speed up bulk imports)
- if(isset($request['dp'])
- && !empty($request['dp'])
- && $this->authenticated)
- {
- Piwik_Tracker::setPluginsNotToLoad(array('Provider'));
- }
-
- try {
- $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker;
- if(is_array($pluginsTracker)
- && count($pluginsTracker) != 0)
- {
- $pluginsTracker['Plugins_Tracker'] = array_diff($pluginsTracker['Plugins_Tracker'], self::getPluginsNotToLoad());
- Piwik_PluginsManager::getInstance()->doNotLoadAlwaysActivatedPlugins();
- Piwik_PluginsManager::getInstance()->loadPlugins( $pluginsTracker['Plugins_Tracker'] );
-
- printDebug("Loading plugins: { ". implode(",", $pluginsTracker['Plugins_Tracker']) . " }");
- }
- } catch(Exception $e) {
- printDebug("ERROR: ".$e->getMessage());
- }
- }
-
- protected function handleEmptyRequest( $request )
- {
- $countParameters = count($request);
- if($countParameters == 0)
- {
- $this->setState(self::STATE_EMPTY_REQUEST);
- }
- if($countParameters == 1 )
- {
- $this->setState(self::STATE_NOSCRIPT_REQUEST);
- }
- }
-
- protected function handleDisabledTracker()
- {
- $saveStats = Piwik_Config::getInstance()->Tracker['record_statistics'];
- if($saveStats == 0)
- {
- $this->setState(self::STATE_LOGGING_DISABLE);
- }
- }
-
- protected function authenticateSuperUserOrAdmin( $request )
- {
- $tokenAuth = $this->getTokenAuth();
-
- if( !$tokenAuth )
- {
- return false;
- }
- $superUserLogin = Piwik_Config::getInstance()->superuser['login'];
- $superUserPassword = Piwik_Config::getInstance()->superuser['password'];
- if( md5($superUserLogin . $superUserPassword ) == $tokenAuth )
- {
- $this->authenticated = true;
- return true;
- }
-
- // Now checking the list of admin token_auth cached in the Tracker config file
- $idSite = Piwik_Common::getRequestVar('idsite', false, 'int', $request);
- if(!empty($idSite)
- && $idSite > 0)
- {
- $website = Piwik_Tracker_Cache::getCacheWebsiteAttributes( $idSite );
- $adminTokenAuth = $website['admin_token_auth'];
- if(in_array($tokenAuth, $adminTokenAuth))
- {
- $this->authenticated = true;
- return true;
- }
- }
- printDebug("WARNING! token_auth = $tokenAuth is not valid, Super User / Admin was NOT authenticated");
-
- return false;
- }
-
- protected function getTokenAuth()
- {
- if (!is_null($this->tokenAuth))
- {
- return $this->tokenAuth;
- }
-
- return Piwik_Common::getRequestVar('token_auth', false);
- }
-
- /**
- * This method allows to set custom IP + server time + visitor ID, when using Tracking API.
- * These two attributes can be only set by the Super User (passing token_auth).
- */
- protected function handleTrackingApi( $request )
- {
- $shouldAuthenticate = Piwik_Config::getInstance()->Tracker['tracking_requests_require_authentication'];
- if($shouldAuthenticate)
- {
- if(!$this->authenticateSuperUserOrAdmin($request))
- {
- return;
- }
- printDebug("token_auth is authenticated!");
- }
- else
- {
- printDebug("token_auth authentication not required");
- }
-
- // Custom IP to use for this visitor
- $customIp = Piwik_Common::getRequestVar('cip', false, 'string', $request);
- if(!empty($customIp))
- {
- $this->setForceIp($customIp);
- }
-
- // Custom server date time to use
- $customDatetime = Piwik_Common::getRequestVar('cdt', false, 'string', $request);
- if(!empty($customDatetime))
- {
- $this->setForceDateTime($customDatetime);
- }
-
- // Forced Visitor ID to record the visit / action
- $customVisitorId = Piwik_Common::getRequestVar('cid', false, 'string', $request);
- if(!empty($customVisitorId))
- {
- $this->setForceVisitorId($customVisitorId);
- }
- }
-
- public static function setTestEnvironment( $args = null, $requestMethod = null )
- {
- if (is_null($args))
- {
- $args = $_GET + $_POST;
- }
- if (is_null($requestMethod))
- {
- $requestMethod = $_SERVER['REQUEST_METHOD'];
- }
-
- // Do not run scheduled tasks during tests
- self::updateTrackerConfig('scheduled_tasks_min_interval', 0);
-
- // if nothing found in _GET/_POST and we're doing a POST, assume bulk request. in which case,
- // we have to bypass authentication
- if (empty($args) && $requestMethod == 'POST')
- {
- self::updateTrackerConfig('tracking_requests_require_authentication', 0);
- }
-
- // Tests can force the use of 3rd party cookie for ID visitor
- if(Piwik_Common::getRequestVar('forceUseThirdPartyCookie', false, null, $args) == 1)
- {
- self::updateTrackerConfig('use_third_party_id_cookie', 1);
- }
-
- // Tests can force the enabling of IP anonymization
- $forceIpAnonymization = false;
- if (Piwik_Common::getRequestVar('forceIpAnonymization', false, null, $args) == 1)
- {
- self::updateTrackerConfig('ip_address_mask_length', 2);
-
- $section = Piwik_Config::getInstance()->Plugins_Tracker;
- $section['Plugins_Tracker'][] = "AnonymizeIP";
- Piwik_Config::getInstance()->Plugins_Tracker = $section;
-
- $forceIpAnonymization = true;
- }
-
- // Custom IP to use for this visitor
- $customIp = Piwik_Common::getRequestVar('cip', false, null, $args);
- if(!empty($customIp))
- {
- self::setForceIp($customIp);
- }
-
- // Custom server date time to use
- $customDatetime = Piwik_Common::getRequestVar('cdt', false, null, $args);
- if(!empty($customDatetime))
- {
- self::setForceDateTime($customDatetime);
- }
-
- // Custom visitor id
- $customVisitorId = Piwik_Common::getRequestVar('cid', false, null, $args);
- if(!empty($customVisitorId))
- {
- self::setForceVisitorId($customVisitorId);
- }
- $pluginsDisabled = array('Provider');
- if(!$forceIpAnonymization)
- {
- $pluginsDisabled[] = 'AnonymizeIP';
- }
-
- // Disable provider plugin, because it is so slow to do reverse ip lookup in dev environment somehow
- self::setPluginsNotToLoad($pluginsDisabled);
- }
+ protected $stateValid = self::STATE_NOTHING_TO_NOTICE;
+ /**
+ * @var Piwik_Tracker_Db
+ */
+ protected static $db = null;
+
+ const STATE_NOTHING_TO_NOTICE = 1;
+ const STATE_LOGGING_DISABLE = 10;
+ const STATE_EMPTY_REQUEST = 11;
+ const STATE_NOSCRIPT_REQUEST = 13;
+
+ // We use hex ID that are 16 chars in length, ie. 64 bits IDs
+ const LENGTH_HEX_ID_STRING = 16;
+ const LENGTH_BINARY_ID = 8;
+
+ // These are also hardcoded in the Javascript
+ const MAX_CUSTOM_VARIABLES = 5;
+ const MAX_LENGTH_CUSTOM_VARIABLE = 200;
+
+ protected $authenticated = false;
+ static protected $forcedDateTime = null;
+ static protected $forcedIpString = null;
+ static protected $forcedVisitorId = null;
+
+ static protected $pluginsNotToLoad = array();
+
+ /**
+ * The set of visits to track.
+ *
+ * @var array
+ */
+ private $requests = array();
+
+ /**
+ * The token auth supplied with a bulk visits POST.
+ *
+ * @var string
+ */
+ private $tokenAuth = null;
+
+ /**
+ * Whether we're currently using bulk tracking or not.
+ *
+ * @var bool
+ */
+ private $usingBulkTracking = false;
+
+ /**
+ * The number of requests that have been successfully logged.
+ *
+ * @var int
+ */
+ private $countOfLoggedRequests = 0;
+
+ public function clear()
+ {
+ self::$forcedIpString = null;
+ self::$forcedDateTime = null;
+ self::$forcedVisitorId = null;
+ $this->stateValid = self::STATE_NOTHING_TO_NOTICE;
+ $this->authenticated = false;
+ }
+
+ public static function setForceIp($ipString)
+ {
+ self::$forcedIpString = $ipString;
+ }
+
+ public static function setForceDateTime($dateTime)
+ {
+ self::$forcedDateTime = $dateTime;
+ }
+
+ public static function setForceVisitorId($visitorId)
+ {
+ self::$forcedVisitorId = $visitorId;
+ }
+
+ public function getCurrentTimestamp()
+ {
+ if (!is_null(self::$forcedDateTime)) {
+ return strtotime(self::$forcedDateTime);
+ }
+ return time();
+ }
+
+ /**
+ * Do not load the specified plugins (used during testing, to disable Provider plugin)
+ * @param array $plugins
+ */
+ static public function setPluginsNotToLoad($plugins)
+ {
+ self::$pluginsNotToLoad = $plugins;
+ }
+
+ /**
+ * Get list of plugins to not load
+ *
+ * @return array
+ */
+ static public function getPluginsNotToLoad()
+ {
+ return self::$pluginsNotToLoad;
+ }
+
+ /**
+ * Update Tracker config
+ *
+ * @param string $name Setting name
+ * @param mixed $value Value
+ */
+ static private function updateTrackerConfig($name, $value)
+ {
+ $section = Piwik_Config::getInstance()->Tracker;
+ $section[$name] = $value;
+ Piwik_Config::getInstance()->Tracker = $section;
+ }
+
+ protected function initRequests($args)
+ {
+ $rawData = file_get_contents("php://input");
+ if (!empty($rawData)) {
+ $this->usingBulkTracking = strpos($rawData, '"requests"') || strpos($rawData, "'requests'");
+ if ($this->usingBulkTracking) {
+ return $this->initBulkTrackingRequests($rawData);
+ }
+ }
+
+ // Not using bulk tracking
+ $this->requests = $args ? $args : (!empty($_GET) || !empty($_POST) ? array($_GET + $_POST) : array());
+ }
+
+ private function initBulkTrackingRequests($rawData)
+ {
+ // POST data can be array of string URLs or array of arrays w/ visit info
+ $jsonData = Piwik_Common::json_decode($rawData, $assoc = true);
+
+ if (isset($jsonData['requests'])) {
+ $this->requests = $jsonData['requests'];
+ }
+ $this->tokenAuth = Piwik_Common::getRequestVar('token_auth', false, null, $jsonData);
+ if (empty($this->tokenAuth)) {
+ throw new Exception(" token_auth must be specified when using Bulk Tracking Import. See <a href='http://piwik.org/docs/tracking-api/reference/'>Tracking Doc</a>");
+ }
+ if (!empty($this->requests)) {
+ $idSiteForAuthentication = 0;
+
+ foreach ($this->requests as &$request) {
+ // if a string is sent, we assume its a URL and try to parse it
+ if (is_string($request)) {
+ $params = array();
+
+ $url = @parse_url($request);
+ if (!empty($url)) {
+ @parse_str($url['query'], $params);
+ $request = $params;
+ if (isset($request['idsite']) && !$idSiteForAuthentication) {
+ $idSiteForAuthentication = $request['idsite'];
+ }
+ }
+ }
+ }
+
+ // a Bulk Tracking request that is not authenticated should fail
+ if (!$this->authenticateSuperUserOrAdmin(array('idsite' => $idSiteForAuthentication))) {
+ throw new Exception(" token_auth specified is not valid for site " . intval($idSiteForAuthentication));
+ }
+ }
+ }
+
+ /**
+ * Main - tracks the visit/action
+ *
+ * @param array $args Optional Request Array
+ */
+ public function main($args = null)
+ {
+ $displayedGIF = false;
+ $this->initRequests($args);
+ if (!empty($this->requests)) {
+ // handle all visits
+ foreach ($this->requests as $request) {
+ $this->init($request);
+
+ if (!$displayedGIF && !$this->authenticated) {
+ $this->outputTransparentGif();
+ $displayedGIF = true;
+ }
+
+ try {
+ if ($this->isVisitValid()) {
+ self::connectDatabaseIfNotConnected();
+
+ $visit = $this->getNewVisitObject();
+ $visit->setRequest($request);
+ $visit->handle();
+ unset($visit);
+ } else {
+ printDebug("The request is invalid: empty request, or maybe tracking is disabled in the config.ini.php via record_statistics=0");
+ }
+ } catch (Piwik_Tracker_Db_Exception $e) {
+ printDebug("<b>" . $e->getMessage() . "</b>");
+ $this->exitWithException($e, $this->authenticated);
+ } catch (Piwik_Tracker_Visit_Excluded $e) {
+ } catch (Exception $e) {
+ $this->exitWithException($e, $this->authenticated);
+ }
+ $this->clear();
+
+ // increment successfully logged request count. make sure to do this after try-catch,
+ // since an excluded visit is considered 'successfully logged'
+ ++$this->countOfLoggedRequests;
+ }
+
+ if (!$displayedGIF) {
+ $this->outputTransparentGif();
+ $displayedGIF = true;
+ }
+ } else {
+ $this->handleEmptyRequest($_GET + $_POST);
+ }
+
+ // run scheduled task
+ try {
+ if ($this->shouldRunScheduledTasks()) {
+ self::runScheduledTasks($now = $this->getCurrentTimestamp());
+ }
+ } catch (Exception $e) {
+ $this->exitWithException($e, $this->authenticated);
+ }
+
+ $this->end();
+ }
+
+ protected function shouldRunScheduledTasks()
+ {
+ // don't run scheduled tasks in CLI mode from Tracker, this is the case
+ // where we bulk load logs & don't want to lose time with tasks
+ return !Piwik_Common::isPhpCliMode()
+ && !$this->authenticated
+ && $this->getState() != self::STATE_LOGGING_DISABLE;
+ }
+
+ /**
+ * Tracker requests will automatically trigger the Scheduled tasks.
+ * This is useful for users who don't setup the cron,
+ * but still want daily/weekly/monthly PDF reports emailed automatically.
+ *
+ * This is similar to calling the API CoreAdminHome.runScheduledTasks (see misc/cron/archive.php)
+ *
+ * @param int $now Current timestamp
+ */
+ protected static function runScheduledTasks($now)
+ {
+ // Currently, there is no hourly tasks. When there are some,
+ // this could be too agressive minimum interval (some hours would be skipped in case of low traffic)
+ $minimumInterval = Piwik_Config::getInstance()->Tracker['scheduled_tasks_min_interval'];
+
+ // If the user disabled browser archiving, he has already setup a cron
+ // To avoid parallel requests triggering the Scheduled Tasks,
+ // Get last time tasks started executing
+ $cache = Piwik_Tracker_Cache::getCacheGeneral();
+ if ($minimumInterval <= 0
+ || empty($cache['isBrowserTriggerArchivingEnabled'])
+ ) {
+ printDebug("-> Scheduled tasks not running in Tracker: Browser archiving is disabled.");
+ return;
+ }
+ $nextRunTime = $cache['lastTrackerCronRun'] + $minimumInterval;
+ if ((isset($GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) && $GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS'])
+ || $cache['lastTrackerCronRun'] === false
+ || $nextRunTime < $now
+ ) {
+ $cache['lastTrackerCronRun'] = $now;
+ Piwik_Tracker_Cache::setCacheGeneral($cache);
+ self::initCorePiwikInTrackerMode();
+ Piwik_SetOption('lastTrackerCronRun', $cache['lastTrackerCronRun']);
+ printDebug('-> Scheduled Tasks: Starting...');
+
+ // save current user privilege and temporarily assume super user privilege
+ $isSuperUser = Piwik::isUserIsSuperUser();
+
+ // Scheduled tasks assume Super User is running
+ Piwik::setUserIsSuperUser();
+
+ // While each plugins should ensure that necessary languages are loaded,
+ // we ensure English translations at least are loaded
+ Piwik_Translate::getInstance()->loadEnglishTranslation();
+
+ $resultTasks = Piwik_TaskScheduler::runTasks();
+
+ // restore original user privilege
+ Piwik::setUserIsSuperUser($isSuperUser);
+
+ printDebug($resultTasks);
+ printDebug('Finished Scheduled Tasks.');
+ } else {
+ printDebug("-> Scheduled tasks not triggered.");
+ }
+ printDebug("Next run will be from: " . date('Y-m-d H:i:s', $nextRunTime) . ' UTC');
+ }
+
+ static public $initTrackerMode = false;
+
+ /*
+ * Used to initialize core Piwik components on a piwik.php request
+ * Eg. when cache is missed and we will be calling some APIs to generate cache
+ */
+ static public function initCorePiwikInTrackerMode()
+ {
+ if (!empty($GLOBALS['PIWIK_TRACKER_MODE'])
+ && self::$initTrackerMode === false
+ ) {
+ self::$initTrackerMode = true;
+ require_once PIWIK_INCLUDE_PATH . '/core/Loader.php';
+ require_once PIWIK_INCLUDE_PATH . '/core/Option.php';
+ try {
+ $access = Zend_Registry::get('access');
+ } catch (Exception $e) {
+ Piwik::createAccessObject();
+ }
+ try {
+ $config = Piwik_Config::getInstance();
+ } catch (Exception $e) {
+ Piwik::createConfigObject();
+ }
+ try {
+ $db = Zend_Registry::get('db');
+ } catch (Exception $e) {
+ Piwik::createDatabaseObject();
+ }
+
+ $pluginsManager = Piwik_PluginsManager::getInstance();
+ $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
+ $pluginsForcedNotToLoad = Piwik_Tracker::getPluginsNotToLoad();
+ $pluginsToLoad = array_diff($pluginsToLoad, $pluginsForcedNotToLoad);
+ $pluginsManager->loadPlugins($pluginsToLoad);
+ }
+ }
+
+ /**
+ * Echos an error message & other information, then exits.
+ *
+ * @param Exception $e
+ * @param bool $authenticated
+ */
+ protected function exitWithException($e, $authenticated)
+ {
+ if ($this->usingBulkTracking) {
+ // when doing bulk tracking we return JSON so the caller will know how many succeeded
+ $result = array('succeeded' => $this->countOfLoggedRequests);
+
+ // send error when in debug mode or when authenticated (which happens when doing log importing,
+ // for example)
+ if ((isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) || $authenticated) {
+ $result['error'] = Piwik_Tracker_GetErrorMessage($e);
+ }
+
+ echo Piwik_Common::json_encode($result);
+
+ exit;
+ } else {
+ Piwik_Tracker_ExitWithException($e, $authenticated);
+ }
+ }
+
+ /**
+ * Returns the date in the "Y-m-d H:i:s" PHP format
+ *
+ * @param int $timestamp
+ * @return string
+ */
+ public static function getDatetimeFromTimestamp($timestamp)
+ {
+ return date("Y-m-d H:i:s", $timestamp);
+ }
+
+ /**
+ * Initialization
+ */
+ protected function init($request)
+ {
+ $this->handleTrackingApi($request);
+ $this->loadTrackerPlugins($request);
+ $this->handleDisabledTracker();
+ $this->handleEmptyRequest($request);
+
+ printDebug("Current datetime: " . date("Y-m-d H:i:s", $this->getCurrentTimestamp()));
+ }
+
+ /**
+ * Cleanup
+ */
+ protected function end()
+ {
+ switch ($this->getState()) {
+ case self::STATE_LOGGING_DISABLE:
+ printDebug("Logging disabled, display transparent logo");
+ break;
+
+ case self::STATE_EMPTY_REQUEST:
+ printDebug("Empty request => Piwik page");
+ echo "<a href='/'>Piwik</a> is a free open source web <a href='http://piwik.org'>analytics</a> that lets you keep control of your data.";
+ break;
+
+ case self::STATE_NOSCRIPT_REQUEST:
+ case self::STATE_NOTHING_TO_NOTICE:
+ default:
+ printDebug("Nothing to notice => default behaviour");
+ break;
+ }
+ printDebug("End of the page.");
+
+ if ($GLOBALS['PIWIK_TRACKER_DEBUG'] === true) {
+ if (isset(self::$db)) {
+ self::$db->recordProfiling();
+ Piwik::printSqlProfilingReportTracker(self::$db);
+ }
+ }
+
+ self::disconnectDatabase();
+ }
+
+ /**
+ * Factory to create database objects
+ *
+ * @param array $configDb Database configuration
+ * @throws Exception
+ * @return Piwik_Tracker_Db_Mysqli|Piwik_Tracker_Db_Pdo_Mysql
+ */
+ public static function factory($configDb)
+ {
+ switch ($configDb['adapter']) {
+ case 'PDO_MYSQL':
+ require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db/Pdo/Mysql.php';
+ return new Piwik_Tracker_Db_Pdo_Mysql($configDb);
+
+ case 'MYSQLI':
+ require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db/Mysqli.php';
+ return new Piwik_Tracker_Db_Mysqli($configDb);
+ }
+
+ throw new Exception('Unsupported database adapter ' . $configDb['adapter']);
+ }
+
+ public static function connectPiwikTrackerDb()
+ {
+ $db = null;
+ $configDb = Piwik_Config::getInstance()->database;
+
+ if (!isset($configDb['port'])) {
+ // before 0.2.4 there is no port specified in config file
+ $configDb['port'] = '3306';
+ }
+
+ Piwik_PostEvent('Tracker.getDatabaseConfig', $configDb);
+
+ $db = self::factory($configDb);
+ $db->connect();
+
+ return $db;
+ }
+
+ public static function connectDatabaseIfNotConnected()
+ {
+ if (!is_null(self::$db)) {
+ return;
+ }
+
+ try {
+ $db = null;
+ Piwik_PostEvent('Tracker.createDatabase', $db);
+ if (is_null($db)) {
+ $db = self::connectPiwikTrackerDb();
+ }
+ self::$db = $db;
+ } catch (Exception $e) {
+ throw new Piwik_Tracker_Db_Exception($e->getMessage(), $e->getCode());
+ }
+ }
+
+ /**
+ * @return Piwik_Tracker_Db
+ */
+ public static function getDatabase()
+ {
+ return self::$db;
+ }
+
+ public static function disconnectDatabase()
+ {
+ if (isset(self::$db)) {
+ self::$db->disconnect();
+ self::$db = null;
+ }
+ }
+
+ /**
+ * Returns the Tracker_Visit object.
+ * This method can be overwritten to use a different Tracker_Visit object
+ *
+ * @throws Exception
+ * @return Piwik_Tracker_Visit
+ */
+ protected function getNewVisitObject()
+ {
+ $visit = null;
+ Piwik_PostEvent('Tracker.getNewVisitObject', $visit);
+
+ if (is_null($visit)) {
+ $visit = new Piwik_Tracker_Visit(self::$forcedIpString, self::$forcedDateTime, $this->authenticated);
+ $visit->setForcedVisitorId(self::$forcedVisitorId);
+ } elseif (!($visit instanceof Piwik_Tracker_Visit_Interface)) {
+ throw new Exception("The Visit object set in the plugin must implement Piwik_Tracker_Visit_Interface");
+ }
+ return $visit;
+ }
+
+ protected function outputTransparentGif()
+ {
+ if (!isset($GLOBALS['PIWIK_TRACKER_DEBUG']) || !$GLOBALS['PIWIK_TRACKER_DEBUG']) {
+ $trans_gif_64 = "R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
+ $this->sendHeader('Content-Type: image/gif');
+
+ $requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
+
+ if ($requestMethod !== 'GET') {
+ $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '*';
+ $this->sendHeader('Access-Control-Allow-Origin: ' . $origin);
+ $this->sendHeader('Access-Control-Allow-Credentials: true');
+ }
+
+ print(base64_decode($trans_gif_64));
+ }
+ }
+
+ protected function sendHeader($header)
+ {
+ Piwik_Common::sendHeader($header);
+ }
+
+ protected function isVisitValid()
+ {
+ return $this->stateValid !== self::STATE_LOGGING_DISABLE
+ && $this->stateValid !== self::STATE_EMPTY_REQUEST;
+ }
+
+ protected function getState()
+ {
+ return $this->stateValid;
+ }
+
+ protected function setState($value)
+ {
+ $this->stateValid = $value;
+ }
+
+ protected function loadTrackerPlugins($request)
+ {
+ // Adding &dp=1 will disable the provider plugin, if token_auth is used (used to speed up bulk imports)
+ if (isset($request['dp'])
+ && !empty($request['dp'])
+ && $this->authenticated
+ ) {
+ Piwik_Tracker::setPluginsNotToLoad(array('Provider'));
+ }
+
+ try {
+ $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker;
+ if (is_array($pluginsTracker)
+ && count($pluginsTracker) != 0
+ ) {
+ $pluginsTracker['Plugins_Tracker'] = array_diff($pluginsTracker['Plugins_Tracker'], self::getPluginsNotToLoad());
+ Piwik_PluginsManager::getInstance()->doNotLoadAlwaysActivatedPlugins();
+ Piwik_PluginsManager::getInstance()->loadPlugins($pluginsTracker['Plugins_Tracker']);
+
+ printDebug("Loading plugins: { " . implode(",", $pluginsTracker['Plugins_Tracker']) . " }");
+ }
+ } catch (Exception $e) {
+ printDebug("ERROR: " . $e->getMessage());
+ }
+ }
+
+ protected function handleEmptyRequest($request)
+ {
+ $countParameters = count($request);
+ if ($countParameters == 0) {
+ $this->setState(self::STATE_EMPTY_REQUEST);
+ }
+ if ($countParameters == 1) {
+ $this->setState(self::STATE_NOSCRIPT_REQUEST);
+ }
+ }
+
+ protected function handleDisabledTracker()
+ {
+ $saveStats = Piwik_Config::getInstance()->Tracker['record_statistics'];
+ if ($saveStats == 0) {
+ $this->setState(self::STATE_LOGGING_DISABLE);
+ }
+ }
+
+ protected function authenticateSuperUserOrAdmin($request)
+ {
+ $tokenAuth = $this->getTokenAuth();
+
+ if (!$tokenAuth) {
+ return false;
+ }
+ $superUserLogin = Piwik_Config::getInstance()->superuser['login'];
+ $superUserPassword = Piwik_Config::getInstance()->superuser['password'];
+ if (md5($superUserLogin . $superUserPassword) == $tokenAuth) {
+ $this->authenticated = true;
+ return true;
+ }
+
+ // Now checking the list of admin token_auth cached in the Tracker config file
+ $idSite = Piwik_Common::getRequestVar('idsite', false, 'int', $request);
+ if (!empty($idSite)
+ && $idSite > 0
+ ) {
+ $website = Piwik_Tracker_Cache::getCacheWebsiteAttributes($idSite);
+ $adminTokenAuth = $website['admin_token_auth'];
+ if (in_array($tokenAuth, $adminTokenAuth)) {
+ $this->authenticated = true;
+ return true;
+ }
+ }
+ printDebug("WARNING! token_auth = $tokenAuth is not valid, Super User / Admin was NOT authenticated");
+
+ return false;
+ }
+
+ protected function getTokenAuth()
+ {
+ if (!is_null($this->tokenAuth)) {
+ return $this->tokenAuth;
+ }
+
+ return Piwik_Common::getRequestVar('token_auth', false);
+ }
+
+ /**
+ * This method allows to set custom IP + server time + visitor ID, when using Tracking API.
+ * These two attributes can be only set by the Super User (passing token_auth).
+ */
+ protected function handleTrackingApi($request)
+ {
+ $shouldAuthenticate = Piwik_Config::getInstance()->Tracker['tracking_requests_require_authentication'];
+ if ($shouldAuthenticate) {
+ if (!$this->authenticateSuperUserOrAdmin($request)) {
+ return;
+ }
+ printDebug("token_auth is authenticated!");
+ } else {
+ printDebug("token_auth authentication not required");
+ }
+
+ // Custom IP to use for this visitor
+ $customIp = Piwik_Common::getRequestVar('cip', false, 'string', $request);
+ if (!empty($customIp)) {
+ $this->setForceIp($customIp);
+ }
+
+ // Custom server date time to use
+ $customDatetime = Piwik_Common::getRequestVar('cdt', false, 'string', $request);
+ if (!empty($customDatetime)) {
+ $this->setForceDateTime($customDatetime);
+ }
+
+ // Forced Visitor ID to record the visit / action
+ $customVisitorId = Piwik_Common::getRequestVar('cid', false, 'string', $request);
+ if (!empty($customVisitorId)) {
+ $this->setForceVisitorId($customVisitorId);
+ }
+ }
+
+ public static function setTestEnvironment($args = null, $requestMethod = null)
+ {
+ if (is_null($args)) {
+ $args = $_GET + $_POST;
+ }
+ if (is_null($requestMethod)) {
+ $requestMethod = $_SERVER['REQUEST_METHOD'];
+ }
+
+ // Do not run scheduled tasks during tests
+ self::updateTrackerConfig('scheduled_tasks_min_interval', 0);
+
+ // if nothing found in _GET/_POST and we're doing a POST, assume bulk request. in which case,
+ // we have to bypass authentication
+ if (empty($args) && $requestMethod == 'POST') {
+ self::updateTrackerConfig('tracking_requests_require_authentication', 0);
+ }
+
+ // Tests can force the use of 3rd party cookie for ID visitor
+ if (Piwik_Common::getRequestVar('forceUseThirdPartyCookie', false, null, $args) == 1) {
+ self::updateTrackerConfig('use_third_party_id_cookie', 1);
+ }
+
+ // Tests can force the enabling of IP anonymization
+ $forceIpAnonymization = false;
+ if (Piwik_Common::getRequestVar('forceIpAnonymization', false, null, $args) == 1) {
+ self::updateTrackerConfig('ip_address_mask_length', 2);
+
+ $section = Piwik_Config::getInstance()->Plugins_Tracker;
+ $section['Plugins_Tracker'][] = "AnonymizeIP";
+ Piwik_Config::getInstance()->Plugins_Tracker = $section;
+
+ $forceIpAnonymization = true;
+ }
+
+ // Custom IP to use for this visitor
+ $customIp = Piwik_Common::getRequestVar('cip', false, null, $args);
+ if (!empty($customIp)) {
+ self::setForceIp($customIp);
+ }
+
+ // Custom server date time to use
+ $customDatetime = Piwik_Common::getRequestVar('cdt', false, null, $args);
+ if (!empty($customDatetime)) {
+ self::setForceDateTime($customDatetime);
+ }
+
+ // Custom visitor id
+ $customVisitorId = Piwik_Common::getRequestVar('cid', false, null, $args);
+ if (!empty($customVisitorId)) {
+ self::setForceVisitorId($customVisitorId);
+ }
+ $pluginsDisabled = array('Provider');
+ if (!$forceIpAnonymization) {
+ $pluginsDisabled[] = 'AnonymizeIP';
+ }
+
+ // Disable provider plugin, because it is so slow to do reverse ip lookup in dev environment somehow
+ self::setPluginsNotToLoad($pluginsDisabled);
+ }
}
/**
* Gets the error message to output when a tracking request fails.
- *
+ *
* @param Exception $e
* @return string
*/
-function Piwik_Tracker_GetErrorMessage( $e )
+function Piwik_Tracker_GetErrorMessage($e)
{
- // Note: duplicated from FormDatabaseSetup.isAccessDenied
- // Avoid leaking the username/db name when access denied
- if($e->getCode() == 1044 || $e->getCode() == 42000)
- {
- return "Error while connecting to the Piwik database - please check your credentials in config/config.ini.php file";
- }
- else
- {
- return $e->getMessage();
- }
+ // Note: duplicated from FormDatabaseSetup.isAccessDenied
+ // Avoid leaking the username/db name when access denied
+ if ($e->getCode() == 1044 || $e->getCode() == 42000) {
+ return "Error while connecting to the Piwik database - please check your credentials in config/config.ini.php file";
+ } else {
+ return $e->getMessage();
+ }
}
/**
@@ -851,22 +784,19 @@ function Piwik_Tracker_GetErrorMessage( $e )
*/
function Piwik_Tracker_ExitWithException($e, $authenticated = false)
{
- Piwik_Common::sendHeader('Content-Type: text/html; charset=utf-8');
-
- if(isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG'])
- {
- $trailer = '<span style="color: #888888">Backtrace:<br /><pre>'.$e->getTraceAsString().'</pre></span>';
- $headerPage = file_get_contents(PIWIK_INCLUDE_PATH . '/themes/default/simple_structure_header.tpl');
- $footerPage = file_get_contents(PIWIK_INCLUDE_PATH . '/themes/default/simple_structure_footer.tpl');
- $headerPage = str_replace('{$HTML_TITLE}', 'Piwik &rsaquo; Error', $headerPage);
-
- echo $headerPage . '<p>' . Piwik_Tracker_GetErrorMessage($e) . '</p>' . $trailer . $footerPage;
- }
- // If not debug, but running authenticated (eg. during log import) then we display raw errors
- elseif($authenticated)
- {
- echo Piwik_Tracker_GetErrorMessage($e);
- }
- exit;
+ Piwik_Common::sendHeader('Content-Type: text/html; charset=utf-8');
+
+ if (isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) {
+ $trailer = '<span style="color: #888888">Backtrace:<br /><pre>' . $e->getTraceAsString() . '</pre></span>';
+ $headerPage = file_get_contents(PIWIK_INCLUDE_PATH . '/themes/default/simple_structure_header.tpl');
+ $footerPage = file_get_contents(PIWIK_INCLUDE_PATH . '/themes/default/simple_structure_footer.tpl');
+ $headerPage = str_replace('{$HTML_TITLE}', 'Piwik &rsaquo; Error', $headerPage);
+
+ echo $headerPage . '<p>' . Piwik_Tracker_GetErrorMessage($e) . '</p>' . $trailer . $footerPage;
+ } // If not debug, but running authenticated (eg. during log import) then we display raw errors
+ elseif ($authenticated) {
+ echo Piwik_Tracker_GetErrorMessage($e);
+ }
+ exit;
}
diff --git a/core/Tracker/Action.php b/core/Tracker/Action.php
index 3b7df131ef..7974fab927 100644
--- a/core/Tracker/Action.php
+++ b/core/Tracker/Action.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -12,1106 +12,1081 @@
/**
* Interface of the Action object.
* New Action classes can be defined in plugins and used instead of the default one.
- *
+ *
* @package Piwik
* @subpackage Piwik_Tracker
*/
-interface Piwik_Tracker_Action_Interface {
- const TYPE_ACTION_URL = 1;
- const TYPE_OUTLINK = 2;
- const TYPE_DOWNLOAD = 3;
- const TYPE_ACTION_NAME = 4;
- const TYPE_ECOMMERCE_ITEM_SKU = 5;
- const TYPE_ECOMMERCE_ITEM_NAME = 6;
- const TYPE_ECOMMERCE_ITEM_CATEGORY = 7;
- const TYPE_SITE_SEARCH = 8;
-
- public function setRequest($requestArray);
- public function setIdSite( $idSite );
- public function init();
- public function getActionUrl();
- public function getActionName();
- public function getActionType();
- public function record( $idVisit, $visitorIdCookie, $idRefererActionUrl, $idRefererActionName, $timeSpentRefererAction );
- public function getIdActionUrl();
- public function getIdActionName();
- public function getIdLinkVisitAction();
+interface Piwik_Tracker_Action_Interface
+{
+ const TYPE_ACTION_URL = 1;
+ const TYPE_OUTLINK = 2;
+ const TYPE_DOWNLOAD = 3;
+ const TYPE_ACTION_NAME = 4;
+ const TYPE_ECOMMERCE_ITEM_SKU = 5;
+ const TYPE_ECOMMERCE_ITEM_NAME = 6;
+ const TYPE_ECOMMERCE_ITEM_CATEGORY = 7;
+ const TYPE_SITE_SEARCH = 8;
+
+ public function setRequest($requestArray);
+
+ public function setIdSite($idSite);
+
+ public function init();
+
+ public function getActionUrl();
+
+ public function getActionName();
+
+ public function getActionType();
+
+ public function record($idVisit, $visitorIdCookie, $idRefererActionUrl, $idRefererActionName, $timeSpentRefererAction);
+
+ public function getIdActionUrl();
+
+ public function getIdActionName();
+
+ public function getIdLinkVisitAction();
}
/**
* Handles an action (page view, download or outlink) by the visitor.
* Parses the action name and URL from the request array, then records the action in the log table.
- *
+ *
* @package Piwik
* @subpackage Piwik_Tracker
*/
class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface
{
- private $request;
- private $idSite;
- private $timestamp;
- private $idLinkVisitAction;
- private $idActionName = false;
- private $idActionUrl = false;
-
- private $actionName;
- private $actionType;
- private $actionUrl;
-
- private $searchCategory = false;
- private $searchCount = false;
-
- private $timeGeneration = false;
-
- /**
- * Encoding of HTML page being viewed. See reencodeParameters for more info.
- *
- * @var string
- */
- private $pageEncoding = false;
-
- static private $queryParametersToExclude = array('phpsessid', 'jsessionid', 'sessionid', 'aspsessionid', 'fb_xd_fragment', 'fb_comment_id', 'doing_wp_cron');
-
- /* Custom Variable names & slots used for Site Search metadata (category, results count) */
- const CVAR_KEY_SEARCH_CATEGORY = '_pk_scat';
- const CVAR_KEY_SEARCH_COUNT = '_pk_scount';
- const CVAR_INDEX_SEARCH_CATEGORY = '4';
- const CVAR_INDEX_SEARCH_COUNT = '5';
-
- /* Tracking API Parameters used to force a site search request */
- const PARAMETER_NAME_SEARCH_COUNT = 'search_count';
- const PARAMETER_NAME_SEARCH_CATEGORY = 'search_cat';
- const PARAMETER_NAME_SEARCH_KEYWORD = 'search';
-
- /* Custom Variables names & slots plus Tracking API Parameters for performance analytics */
- const DB_COLUMN_TIME_GENERATION = 'custom_float_1';
- const PARAMETER_NAME_TIME_GENERATION = 'generation_time_ms';
-
- /**
- * Map URL prefixes to integers.
- * @see self::normalizeUrl(), self::reconstructNormalizedUrl()
- */
- static public $urlPrefixMap = array(
- 'http://www.' => 1,
- 'http://' => 0,
- 'https://www.' => 3,
- 'https://' => 2
- );
-
- /**
- * Extract the prefix from a URL.
- * Return the prefix ID and the rest.
- *
- * @param string $url
- * @return array
- */
- static public function normalizeUrl($url)
- {
- 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);
- }
-
- /**
- * Build the full URL from the prefix ID and the rest.
- *
- * @param string $url
- * @param integer $prefixId
- * @return string
- */
- static public function reconstructNormalizedUrl($url, $prefixId)
- {
- $map = array_flip(self::$urlPrefixMap);
- if ($prefixId !== null && isset($map[$prefixId]))
- {
- $fullUrl = $map[$prefixId].$url;
- }
- else
- {
- $fullUrl = $url;
- }
-
- // Clean up host & hash tags, for URLs
- $parsedUrl = @parse_url($fullUrl);
- $parsedUrl = self::cleanupHostAndHashTag($parsedUrl);
- $url = Piwik_Common::getParseUrlReverse($parsedUrl);
- if(!empty($url))
- {
- return $url;
- }
-
- return $fullUrl;
- }
-
-
- /**
- * Set request parameters
- *
- * @param array $requestArray
- */
- public function setRequest($requestArray)
- {
- $this->request = $requestArray;
- }
-
- /**
- * Returns the current set request parameters
- *
- * @return array
- */
- public function getRequest()
- {
- return $this->request;
- }
-
- /**
- * Returns URL of the page currently being tracked, or the file being downloaded, or the outlink being clicked
- *
- * @return string
- */
- public function getActionUrl()
- {
- return $this->actionUrl;
- }
- public function getActionName()
- {
- return $this->actionName;
- }
- public function getActionType()
- {
- return $this->actionType;
- }
- public function getActionNameType()
- {
- $actionNameType = null;
-
- // we can add here action types for names of other actions than page views (like downloads, outlinks)
- switch( $this->getActionType() )
- {
- case Piwik_Tracker_Action_Interface::TYPE_ACTION_URL:
- $actionNameType = Piwik_Tracker_Action_Interface::TYPE_ACTION_NAME;
- break;
-
- case Piwik_Tracker_Action_Interface::TYPE_SITE_SEARCH:
- $actionNameType = Piwik_Tracker_Action_Interface::TYPE_SITE_SEARCH;
- break;
- }
-
- return $actionNameType;
- }
-
- public function getIdActionUrl()
- {
- $idUrl = $this->idActionUrl;
- if(!empty($idUrl)) {
- return $idUrl;
- }
- // Site Search, by default, will not track URL. We do not want URL to appear as "Page URL not defined"
- // so we specifically set it to NULL in the table (the archiving query does IS NOT NULL)
- if($this->getActionType() == self::TYPE_SITE_SEARCH) {
- return null;
- }
-
- // However, for other cases, we record idaction_url = 0 which will be displayed as "Page URL Not Defined"
- return 0;
- }
- public function getIdActionName()
- {
- return $this->idActionName;
- }
-
- protected function setActionName($name)
- {
- $name = self::cleanupString($name);
- $this->actionName = $name;
- }
-
- protected function setActionType($type)
- {
- $this->actionType = $type;
- }
-
- protected function setActionUrl($url)
- {
- $this->actionUrl = $url;
- }
-
- /**
- * Converts Matrix URL format
- * from http://example.org/thing;paramA=1;paramB=6542
- * to http://example.org/thing?paramA=1&paramB=6542
- *
- * @param string $url
- */
- static public function convertMatrixUrl($originalUrl)
- {
- $posFirstSemiColon = strpos($originalUrl,";");
- if($posFirstSemiColon === false)
- {
- return $originalUrl;
- }
- $posQuestionMark = strpos($originalUrl,"?");
- $replace = ($posQuestionMark === false);
- 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;
- }
-
- static public function cleanupUrl($url)
- {
- $url = Piwik_Common::unsanitizeInputValue($url);
- $url = self::cleanupString($url);
- $url = self::convertMatrixUrl($url);
- return $url;
- }
-
- /**
- * Will cleanup the hostname (some browser do not strolower the hostname),
- * and deal ith the hash tag on incoming URLs based on website setting.
- *
- * @param $parsedUrl
- * @param $idSite int|false The site ID of the current visit. This parameter is
- * only used by the tracker to see if we should remove
- * the URL fragment for this site.
- * @return array
- */
- static public function cleanupHostAndHashTag($parsedUrl, $idSite = false)
- {
- if(empty($parsedUrl))
- {
- return $parsedUrl;
- }
- if(!empty($parsedUrl['host']))
- {
- $parsedUrl['host'] = mb_strtolower($parsedUrl['host'], 'UTF-8');
- }
-
- if(!empty($parsedUrl['fragment']))
- {
- $parsedUrl['fragment'] = self::processUrlFragment($parsedUrl['fragment'], $idSite);
- }
-
- return $parsedUrl;
- }
-
- /**
- * Cleans and/or removes the URL fragment of a URL.
- *
- * @param $urlFragment string The URL fragment to process.
- * @param $idSite int|false If not false, this function will check if URL fragments
- * should be removed for the site w/ this ID and if so,
- * the returned processed fragment will be empty.
- * @return string The processed URL fragment.
- */
- public static function processUrlFragment($urlFragment, $idSite = false)
- {
- // if we should discard the url fragment for this site, return an empty string as
- // the processed url fragment
- if ($idSite !== false
- && self::shouldRemoveURLFragmentFor($idSite))
- {
- return '';
- }
- else
- {
- // Remove trailing Hash tag in ?query#hash#
- if (substr($urlFragment, -1) == '#')
- {
- $urlFragment = substr($urlFragment, 0, strlen($urlFragment) - 1);
- }
- return $urlFragment;
- }
- }
-
- /**
- * Returns true if URL fragments should be removed for a specific site,
- * false if otherwise.
- *
- * This function uses the Tracker cache and not the MySQL database.
- *
- * @param $idSite int The ID of the site to check for.
- * @return bool
- */
- public static function shouldRemoveURLFragmentFor( $idSite )
- {
- $websiteAttributes = Piwik_Tracker_Cache::getCacheWebsiteAttributes($idSite);
- return !$websiteAttributes['keep_url_fragment'];
- }
-
- /**
- * Given the Input URL, will exclude all query parameters set for this site
- * Note: Site Search parameters are excluded in detectSiteSearch()
- * @static
- * @param $originalUrl
- * @param $idSite
- * @return bool|string
- */
- static public function excludeQueryParametersFromUrl($originalUrl, $idSite)
- {
- $originalUrl = self::cleanupUrl($originalUrl);
-
- $parsedUrl = @parse_url($originalUrl);
- $parsedUrl = self::cleanupHostAndHashTag($parsedUrl, $idSite);
- $parametersToExclude = self::getQueryParametersToExclude($idSite);
-
- if(empty($parsedUrl['query']))
- {
- if(empty($parsedUrl['fragment']))
- {
- return Piwik_Common::getParseUrlReverse($parsedUrl);
- }
- // Exclude from the hash tag as well
- $queryParameters = Piwik_Common::getArrayFromQueryString($parsedUrl['fragment']);
- $parsedUrl['fragment'] = self::getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude);
- $url = Piwik_Common::getParseUrlReverse($parsedUrl);
- return $url;
- }
- $queryParameters = Piwik_Common::getArrayFromQueryString($parsedUrl['query']);
- $parsedUrl['query'] = self::getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude);
- $url = Piwik_Common::getParseUrlReverse($parsedUrl);
- return $url;
- }
-
- /**
- * Returns the array of parameters names that must be excluded from the Query String in all tracked URLs
- * @static
- * @param $idSite
- * @return array
- */
- public static function getQueryParametersToExclude($idSite)
- {
- $campaignTrackingParameters = Piwik_Common::getCampaignParameters();
-
- $campaignTrackingParameters = array_merge(
- $campaignTrackingParameters[0], // campaign name parameters
- $campaignTrackingParameters[1] // campaign keyword parameters
- );
-
- $website = Piwik_Tracker_Cache::getCacheWebsiteAttributes($idSite);
- $excludedParameters = isset($website['excluded_parameters'])
- ? $website['excluded_parameters']
- : array();
-
- if(!empty($excludedParameters)) {
- printDebug('Excluding parameters "' . implode(',', $excludedParameters) . '" from URL');
- }
-
- $parametersToExclude = array_merge($excludedParameters,
- self::$queryParametersToExclude,
- $campaignTrackingParameters,
- array(self::PARAMETER_NAME_TIME_GENERATION));
-
- $parametersToExclude = array_map('strtolower', $parametersToExclude);
- return $parametersToExclude;
- }
-
- /**
- * Returns a Query string,
- * Given an array of input parameters, and an array of parameter names to exclude
- *
- * @static
- * @param $queryParameters
- * @param $parametersToExclude
- * @return string
- */
- public static function getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude)
- {
- $validQuery = '';
- $separator = '&';
- foreach ($queryParameters as $name => $value) {
- // decode encoded square brackets
- $name = str_replace(array('%5B', '%5D'), array('[', ']'), $name);
-
- if (!in_array(strtolower($name), $parametersToExclude)) {
- if (is_array($value)) {
- foreach ($value as $param) {
- if ($param === false) {
- $validQuery .= $name . '[]' . $separator;
- } else {
- $validQuery .= $name . '[]=' . $param . $separator;
- }
- }
- } else if ($value === false) {
- $validQuery .= $name . $separator;
- } else {
- $validQuery .= $name . '=' . $value . $separator;
- }
- }
- }
- $validQuery = substr($validQuery,0,-strlen($separator));
- return $validQuery;
- }
-
- public function init()
- {
- $this->pageEncoding = Piwik_Common::getRequestVar('cs', false, null, $this->request);
-
- $info = $this->extractUrlAndActionNameFromRequest();
-
- $originalUrl = $info['url'];
- $info['url'] = self::excludeQueryParametersFromUrl($originalUrl, $this->idSite);
-
- if($originalUrl != $info['url'])
- {
- printDebug(' Before was "'.$originalUrl.'"');
- printDebug(' After is "'.$info['url'].'"');
- }
-
- // Set Final attributes for this Action (Pageview, Search, etc.)
- $this->setActionName($info['name']);
- $this->setActionType($info['type']);
- $this->setActionUrl($info['url']);
- }
-
- static public function getSqlSelectActionId()
- {
- $sql = "SELECT idaction, type, name
- FROM ".Piwik_Common::prefixTable('log_action')
- ." WHERE "
- ." ( hash = CRC32(?) AND name = ? AND type = ? ) ";
- return $sql;
- }
-
- /**
- * This function will find the idaction from the lookup table piwik_log_action,
- * given an Action name and type.
- *
- * This is used to record Page URLs, Page Titles, Ecommerce items SKUs, item names, item categories
- *
- * If the action name does not exist in the lookup table, it will INSERT it
- * @param array $actionNamesAndTypes Array of one or many (name,type)
- * @return array Returns the input array, with the idaction appended ie. Array of one or many (name,type,idaction)
- */
- static public function loadActionId( $actionNamesAndTypes )
- {
- // First, we try and select the actions that are already recorded
- $sql = self::getSqlSelectActionId();
- $bind = array();
- $normalizedUrls = array();
- $i = 0;
- foreach($actionNamesAndTypes as $index => &$actionNameType)
- {
- list($name,$type) = $actionNameType;
- if(empty($name))
- {
- $actionNameType[] = false;
- continue;
- }
- if($i > 0)
- {
- $sql .= " OR ( hash = CRC32(?) AND name = ? AND type = ? ) ";
- }
- if ($type == Piwik_Tracker_Action::TYPE_ACTION_URL)
- {
- // normalize urls by stripping protocol and www
- $normalizedUrls[$index] = self::normalizeUrl($name);
- $name = $normalizedUrls[$index]['url'];
- }
- $bind[] = $name;
- $bind[] = $name;
- $bind[] = $type;
- $i++;
- }
- // Case URL & Title are empty
- if(empty($bind))
- {
- return $actionNamesAndTypes;
- }
- $actionIds = Piwik_Tracker::getDatabase()->fetchAll($sql, $bind);
-
- // For the Actions found in the lookup table, add the idaction in the array,
- // If not found in lookup table, queue for INSERT
- $actionsToInsert = array();
- foreach($actionNamesAndTypes as $index => &$actionNameType)
- {
- list($name,$type) = $actionNameType;
- if(empty($name)) { continue; }
- if(isset($normalizedUrls[$index]))
- {
- $name = $normalizedUrls[$index]['url'];
- }
- $found = false;
- foreach($actionIds as $row)
- {
- if($name == $row['name']
- && $type == $row['type'])
- {
- $found = true;
- $actionNameType[] = $row['idaction'];
- continue;
- }
- }
- if(!$found)
- {
- $actionsToInsert[] = $index;
- }
- }
-
- $sql = "INSERT INTO ". Piwik_Common::prefixTable('log_action').
- "( name, hash, type, url_prefix ) VALUES (?,CRC32(?),?,?)";
- // Then, we insert all new actions in the lookup table
- foreach($actionsToInsert as $actionToInsert)
- {
- list($name,$type) = $actionNamesAndTypes[$actionToInsert];
-
- $urlPrefix = null;
- if(isset($normalizedUrls[$actionToInsert]))
- {
- $name = $normalizedUrls[$actionToInsert]['url'];
- $urlPrefix = $normalizedUrls[$actionToInsert]['prefixId'];
- }
-
- Piwik_Tracker::getDatabase()->query($sql, array($name, $name, $type, $urlPrefix));
- $actionId = Piwik_Tracker::getDatabase()->lastInsertId();
- printDebug("Recorded a new action (".self::getActionTypeName($type).") in the lookup table: ". $name . " (idaction = ".$actionId.")");
-
- $actionNamesAndTypes[$actionToInsert][] = $actionId;
- }
- return $actionNamesAndTypes;
- }
-
- static public function getActionTypeName($type)
- {
- switch($type)
- {
- case self::TYPE_ACTION_URL: return 'Page URL'; break;
- case self::TYPE_OUTLINK: return 'Outlink URL'; break;
- case self::TYPE_DOWNLOAD: return 'Download URL'; break;
- case self::TYPE_ACTION_NAME: return 'Page Title'; break;
- case self::TYPE_SITE_SEARCH: return 'Site Search'; break;
- case self::TYPE_ECOMMERCE_ITEM_SKU: return 'Ecommerce Item SKU'; break;
- case self::TYPE_ECOMMERCE_ITEM_NAME: return 'Ecommerce Item Name'; break;
- case self::TYPE_ECOMMERCE_ITEM_CATEGORY: return 'Ecommerce Item Category'; break;
- default: throw new Exception("Unexpected action type ".$type); break;
- }
- }
-
- /**
- * Loads the idaction of the current action name and the current action url.
- * These idactions are used in the visitor logging table to link the visit information
- * (entry action, exit action) to the actions.
- * These idactions are also used in the table that links the visits and their actions.
- *
- * The methods takes care of creating a new record(s) in the action table if the existing
- * action name and action url doesn't exist yet.
- */
- function loadIdActionNameAndUrl()
- {
- if( $this->idActionUrl !== false
- && $this->idActionName !== false )
- {
- return;
- }
- $actions = array();
- $nameType = $this->getActionNameType();
- $action = array($this->getActionName(), $nameType);
- if(!is_null($action[1]))
- {
- $actions[] = $action;
- }
-
- $urlType = $this->getActionType();
- $url = $this->getActionUrl();
- // this code is a mess, but basically, getActionType() returns SITE_SEARCH,
- // but we do want to record the site search URL as an ACTION_URL
- if($nameType == Piwik_Tracker_Action::TYPE_SITE_SEARCH)
- {
- $urlType = Piwik_Tracker_Action::TYPE_ACTION_URL;
-
- // By default, Site Search does not record the URL for the Search Result page, to slightly improve performance
- if(empty(Piwik_Config::getInstance()->Tracker['action_sitesearch_record_url']))
- {
- $url = false;
- }
- }
- if(!is_null($urlType) && !empty($url))
- {
- $actions[] = array($url, $urlType);
- }
-
- $loadedActionIds = self::loadActionId($actions);
-
- foreach($loadedActionIds as $loadedActionId)
- {
- list($name, $type, $actionId) = $loadedActionId;
- if($type == Piwik_Tracker_Action::TYPE_ACTION_NAME
- || $type == Piwik_Tracker_Action::TYPE_SITE_SEARCH)
- {
- $this->idActionName = $actionId;
- }
- else
- {
- $this->idActionUrl = $actionId;
- }
- }
- }
-
- /**
- * @param int $idSite
- */
- function setIdSite($idSite)
- {
- $this->idSite = $idSite;
- }
-
- function setTimestamp($timestamp)
- {
- $this->timestamp = $timestamp;
- }
-
-
- /**
- * Records in the DB the association between the visit and this action.
- *
- * @param int $idVisit is the ID of the current visit in the DB table log_visit
- * @param $visitorIdCookie
- * @param int $idRefererActionUrl is the ID of the last action done by the current visit.
- * @param $idRefererActionName
- * @param int $timeSpentRefererAction is the number of seconds since the last action was done.
- * It is directly related to idRefererActionUrl.
- */
- public function record( $idVisit, $visitorIdCookie, $idRefererActionUrl, $idRefererActionName, $timeSpentRefererAction)
- {
- $this->loadIdActionNameAndUrl();
-
- $idActionName = in_array($this->getActionType(), array( Piwik_Tracker_Action::TYPE_ACTION_NAME,
- Piwik_Tracker_Action::TYPE_ACTION_URL,
- Piwik_Tracker_Action::TYPE_SITE_SEARCH))
- ? (int)$this->getIdActionName()
- : null;
-
-
- $insert = array(
- 'idvisit' => $idVisit,
- 'idsite' => $this->idSite,
- 'idvisitor' => $visitorIdCookie,
- 'server_time' => Piwik_Tracker::getDatetimeFromTimestamp($this->timestamp),
- 'idaction_url' => $this->getIdActionUrl(),
- 'idaction_name' => $idActionName,
- 'idaction_url_ref' => $idRefererActionUrl,
- 'idaction_name_ref' => $idRefererActionName,
- 'time_spent_ref_action' => $timeSpentRefererAction
- );
-
- if (!empty($this->timeGeneration)) {
- $insert[self::DB_COLUMN_TIME_GENERATION] = (int)$this->timeGeneration;
- }
-
- $customVariables = $this->getCustomVariables();
-
- $insert = array_merge($insert, $customVariables);
-
- // Mysqli apparently does not like NULL inserts?
- $insertWithoutNulls = array();
- foreach($insert as $column => $value)
- {
- if(!is_null($value) || $column == 'idaction_url_ref')
- {
- $insertWithoutNulls[$column] = $value;
- }
- }
-
- $fields = implode(", ", array_keys($insertWithoutNulls));
- $bind = array_values($insertWithoutNulls);
- $values = Piwik_Common::getSqlStringFieldsArray($insertWithoutNulls);
-
- $sql = "INSERT INTO ".Piwik_Common::prefixTable('log_link_visit_action'). " ($fields) VALUES ($values)";
- Piwik_Tracker::getDatabase()->query( $sql, $bind );
-
- $this->idLinkVisitAction = Piwik_Tracker::getDatabase()->lastInsertId();
-
- $info = array(
- 'idSite' => $this->idSite,
- 'idLinkVisitAction' => $this->idLinkVisitAction,
- 'idVisit' => $idVisit,
- 'idRefererActionUrl' => $idRefererActionUrl,
- 'idRefererActionName' => $idRefererActionName,
- 'timeSpentRefererAction' => $timeSpentRefererAction,
- );
- printDebug($insertWithoutNulls);
-
- /*
- * send the Action object ($this) and the list of ids ($info) as arguments to the event
- */
- Piwik_PostEvent('Tracker.Action.record', $this, $info);
- }
-
- public function getCustomVariables()
- {
- $customVariables = Piwik_Tracker_Visit::getCustomVariables($scope = 'page', $this->request);
-
- // Enrich Site Search actions with Custom Variables, overwriting existing values
- if (!empty($this->searchCategory)) {
- if (!empty($customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_CATEGORY])) {
- printDebug("WARNING: Overwriting existing Custom Variable in slot " . self::CVAR_INDEX_SEARCH_CATEGORY . " for this page view");
- }
- $customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_CATEGORY] = self::CVAR_KEY_SEARCH_CATEGORY;
- $customVariables['custom_var_v' . self::CVAR_INDEX_SEARCH_CATEGORY] = Piwik_Tracker_Visit::truncateCustomVariable($this->searchCategory);
- }
- if ($this->searchCount !== false) {
- if (!empty($customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_COUNT])) {
- printDebug("WARNING: Overwriting existing Custom Variable in slot " . self::CVAR_INDEX_SEARCH_COUNT . " for this page view");
- }
- $customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_COUNT] = self::CVAR_KEY_SEARCH_COUNT;
- $customVariables['custom_var_v' . self::CVAR_INDEX_SEARCH_COUNT] = (int)$this->searchCount;
- }
-
- if (!empty($customVariables))
- {
- printDebug("Page level Custom Variables: ");
- printDebug($customVariables);
- }
- return $customVariables;
- }
-
- /**
- * Returns the ID of the newly created record in the log_link_visit_action table
- *
- * @return int | false
- */
- public function getIdLinkVisitAction()
- {
- return $this->idLinkVisitAction;
- }
-
- /**
- * Generates the name of the action from the URL or the specified name.
- * Sets the name as $this->actionName
- *
- * @return array
- */
- protected function extractUrlAndActionNameFromRequest()
- {
- $actionName = null;
-
- // download?
- $downloadUrl = Piwik_Common::getRequestVar( 'download', '', 'string', $this->request);
- if(!empty($downloadUrl))
- {
- $actionType = self::TYPE_DOWNLOAD;
- $url = $downloadUrl;
- }
-
- // outlink?
- if(empty($actionType))
- {
- $outlinkUrl = Piwik_Common::getRequestVar( 'link', '', 'string', $this->request);
- if(!empty($outlinkUrl))
- {
- $actionType = self::TYPE_OUTLINK;
- $url = $outlinkUrl;
- }
- }
-
- // handle encoding
- $actionName = Piwik_Common::getRequestVar( 'action_name', '', 'string', $this->request);
-
- // defaults to page view
- if(empty($actionType))
- {
- $actionType = self::TYPE_ACTION_URL;
- $url = Piwik_Common::getRequestVar( 'url', '', 'string', $this->request);
-
- // get the delimiter, by default '/'; BC, we read the old action_category_delimiter first (see #1067)
- $actionCategoryDelimiter = isset(Piwik_Config::getInstance()->General['action_category_delimiter'])
- ? Piwik_Config::getInstance()->General['action_category_delimiter']
- : Piwik_Config::getInstance()->General['action_url_category_delimiter'];
-
- // create an array of the categories delimited by the delimiter
- $split = explode($actionCategoryDelimiter, $actionName);
-
- // trim every category
- $split = array_map('trim', $split);
-
- // remove empty categories
- $split = array_filter($split, 'strlen');
-
- // rebuild the name from the array of cleaned categories
- $actionName = implode($actionCategoryDelimiter, $split);
- }
- $url = self::cleanupString($url);
-
- if(!Piwik_Common::isLookLikeUrl($url))
- {
- printDebug("WARNING: URL looks invalid and is discarded");
- $url = '';
- }
-
- // Site search?
- if($actionType == self::TYPE_ACTION_URL)
- {
- // Look in tracked URL for the Site Search parameters
- $siteSearch = $this->detectSiteSearch($url);
- if(!empty($siteSearch))
- {
- $actionType = self::TYPE_SITE_SEARCH;
- list($actionName, $url) = $siteSearch;
- }
- // Look for performance analytics parameters
- $this->detectPerformanceAnalyticsParameters();
- }
- $actionName = self::cleanupString($actionName);
-
- return array(
- 'name' => empty($actionName) ? '' : $actionName,
- 'type' => $actionType,
- 'url' => $url,
- );
- }
-
- protected function detectSiteSearch($originalUrl)
- {
- $website = Piwik_Tracker_Cache::getCacheWebsiteAttributes($this->idSite);
- if(empty($website['sitesearch']))
- {
- printDebug("Internal 'Site Search' tracking is not enabled for this site. ");
- return false;
- }
- $actionName = $url = $categoryName = $count = false;
- $doTrackUrlForSiteSearch = !empty(Piwik_Config::getInstance()->Tracker['action_sitesearch_record_url']);
-
- $originalUrl = self::cleanupUrl($originalUrl);
-
-
- // Detect Site search from Tracking API parameters rather than URL
- $searchKwd = Piwik_Common::getRequestVar( self::PARAMETER_NAME_SEARCH_KEYWORD, '', 'string', $this->request);
- if(!empty($searchKwd))
- {
- $actionName = $searchKwd;
- if($doTrackUrlForSiteSearch) {
- $url = $originalUrl;
- }
- $isCategoryName = Piwik_Common::getRequestVar( self::PARAMETER_NAME_SEARCH_CATEGORY, false, 'string', $this->request);
- if(!empty($isCategoryName)) {
- $categoryName = $isCategoryName;
- }
- $isCount = Piwik_Common::getRequestVar( self::PARAMETER_NAME_SEARCH_COUNT, -1, 'int', $this->request);
- if($this->isValidSearchCount($isCount)) {
- $count = $isCount;
- }
- }
-
- if(empty($actionName))
- {
- $parsedUrl = @parse_url($originalUrl);
-
- // Detect Site Search from URL query parameters
- if(!empty($parsedUrl['query']) || !empty($parsedUrl['fragment']))
- {
- // array($url, $actionName, $categoryName, $count);
- $searchInfo = $this->detectSiteSearchFromUrl($website, $parsedUrl);
- if(!empty($searchInfo)) {
- list ($url, $actionName, $categoryName, $count) = $searchInfo;
- }
- }
- }
-
- if(empty($actionName))
- {
- printDebug("(this is not a Site Search request)");
- return false;
- }
-
- printDebug("Detected Site Search keyword '$actionName'. ");
- if (!empty($categoryName))
- {
- printDebug("- Detected Site Search Category '$categoryName'. ");
- }
- if ($count !== false)
- {
- printDebug("- Search Results Count was '$count'. ");
- }
- if($url != $originalUrl) {
- printDebug("NOTE: The Page URL was changed / removed, during the Site Search detection, was '$originalUrl', now is '$url'");
- }
-
- if(!empty($categoryName) || $count !== false) {
- $this->setActionSearchMetadata($categoryName, $count);
- }
- return array(
- $actionName,
- $url
- );
- }
-
- protected function isValidSearchCount($count)
- {
- return is_numeric($count) && $count >= 0;
- }
-
-
- protected function setActionSearchMetadata($category, $count)
- {
- if(!empty($category)) {
- $this->searchCategory = trim($category);
- }
- if($count !== false) {
- $this->searchCount = $count;
- }
- }
-
- protected function detectSiteSearchFromUrl($website, $parsedUrl)
- {
- $doRemoveSearchParametersFromUrl = false;
- $separator = '&';
- $count = $actionName = $categoryName = false;
-
- $keywordParameters = isset($website['sitesearch_keyword_parameters'])
- ? $website['sitesearch_keyword_parameters']
- : array();
- $queryString = (!empty($parsedUrl['query']) ? $parsedUrl['query'] : '') . (!empty($parsedUrl['fragment']) ? $separator . $parsedUrl['fragment'] : '');
- $parametersRaw = Piwik_Common::getArrayFromQueryString($queryString);
-
- // strtolower the parameter names for smooth site search detection
- $parameters = array();
- foreach($parametersRaw as $k => $v) {
- $parameters[Piwik_Common::mb_strtolower($k)] = $v;
- }
- // decode values if they were sent from a client using another charset
- self::reencodeParameters($parameters, $this->pageEncoding);
-
- // Detect Site Search keyword
- foreach ($keywordParameters as $keywordParameterRaw) {
- $keywordParameter = Piwik_Common::mb_strtolower($keywordParameterRaw);
- if (!empty($parameters[$keywordParameter])) {
- $actionName = $parameters[$keywordParameter];
- break;
- }
- }
-
- if (empty($actionName))
- {
- return false;
- }
-
- $categoryParameters = isset($website['sitesearch_category_parameters'])
- ? $website['sitesearch_category_parameters']
- : array();
-
- foreach ($categoryParameters as $categoryParameterRaw) {
- $categoryParameter = Piwik_Common::mb_strtolower($categoryParameterRaw);
- if (!empty($parameters[$categoryParameter])) {
- $categoryName = $parameters[$categoryParameter];
- break;
- }
- }
-
- if(isset($parameters[self::PARAMETER_NAME_SEARCH_COUNT])
- && $this->isValidSearchCount($parameters[self::PARAMETER_NAME_SEARCH_COUNT]))
- {
- $count = $parameters[self::PARAMETER_NAME_SEARCH_COUNT];
- }
- // Remove search kwd from URL
- if ($doRemoveSearchParametersFromUrl)
- {
- // @see excludeQueryParametersFromUrl()
- // Excluded the detected parameters from the URL
- $parametersToExclude = array($categoryParameterRaw, $keywordParameterRaw);
- $parsedUrl['query'] = self::getQueryStringWithExcludedParameters(Piwik_Common::getArrayFromQueryString($parsedUrl['query']), $parametersToExclude);
- $parsedUrl['fragment'] = self::getQueryStringWithExcludedParameters(Piwik_Common::getArrayFromQueryString($parsedUrl['fragment']), $parametersToExclude);
- }
- $url = Piwik_Common::getParseUrlReverse($parsedUrl);
- if(is_array($actionName)) {
- $actionName = reset($actionName);
- }
- $actionName = trim(urldecode($actionName));
- if(empty($actionName)) {
- return false;
- }
- if(is_array($categoryName)) {
- $categoryName = reset($categoryName);
- }
- $categoryName = trim(urldecode($categoryName));
- return array($url, $actionName, $categoryName, $count);
- }
-
- protected function detectPerformanceAnalyticsParameters()
- {
- $generationTime = Piwik_Common::getRequestVar(self::PARAMETER_NAME_TIME_GENERATION, -1, 'int', $this->request);
- if ($generationTime > 0) {
- $this->timeGeneration = $generationTime;
- }
- }
-
- /**
- * Clean up string contents (filter, truncate, ...)
- *
- * @param string $string Dirty string
- * @return string
- */
- protected static function cleanupString($string)
- {
- $string = trim($string);
- $string = str_replace(array("\n", "\r", "\0"), '', $string);
-
- $limit = Piwik_Config::getInstance()->Tracker['page_maximum_length'];
- return substr($string, 0, $limit);
- }
-
- /**
- * Checks if query parameters are of a non-UTF-8 encoding and converts the values
- * from the specified encoding to UTF-8.
- *
- * This method is used to workaround browser/webapp bugs (see #3450). When
- * browsers fail to encode query parameters in UTF-8, the tracker will send the
- * charset of the page viewed and we can sometimes work around invalid data
- * being stored.
- *
- * @param array $queryParameters Name/value mapping of query parameters.
- * @param string|false $encoding of the HTML page the URL is for. Used to workaround
- * browser bugs & mis-coded webapps. See #3450.
- */
- private static function reencodeParameters( &$queryParameters, $encoding = false )
- {
- // 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'
- && function_exists('mb_check_encoding'))
- {
- $queryParameters = self::reencodeParametersArray($queryParameters, $encoding);
- }
- return $queryParameters;
- }
-
- private static function reencodeParametersArray($queryParameters, $encoding)
- {
- foreach($queryParameters as &$value)
- {
- if(is_array($value)) {
- $value = self::reencodeParametersArray($value, $encoding);
- } else {
- $value = self::reencodeParameterValue($value, $encoding);
- }
- }
- return $queryParameters;
- }
-
- private static function reencodeParameterValue($value, $encoding)
- {
- if(is_string($value))
- {
- $decoded = urldecode($value);
- if (@mb_check_encoding($decoded, $encoding)) {
- $value = urlencode(mb_convert_encoding($decoded, 'UTF-8', $encoding));
- }
- }
- return $value;
- }
+ private $request;
+ private $idSite;
+ private $timestamp;
+ private $idLinkVisitAction;
+ private $idActionName = false;
+ private $idActionUrl = false;
+
+ private $actionName;
+ private $actionType;
+ private $actionUrl;
+
+ private $searchCategory = false;
+ private $searchCount = false;
+
+ private $timeGeneration = false;
+
+ /**
+ * Encoding of HTML page being viewed. See reencodeParameters for more info.
+ *
+ * @var string
+ */
+ private $pageEncoding = false;
+
+ static private $queryParametersToExclude = array('phpsessid', 'jsessionid', 'sessionid', 'aspsessionid', 'fb_xd_fragment', 'fb_comment_id', 'doing_wp_cron');
+
+ /* Custom Variable names & slots used for Site Search metadata (category, results count) */
+ const CVAR_KEY_SEARCH_CATEGORY = '_pk_scat';
+ const CVAR_KEY_SEARCH_COUNT = '_pk_scount';
+ const CVAR_INDEX_SEARCH_CATEGORY = '4';
+ const CVAR_INDEX_SEARCH_COUNT = '5';
+
+ /* Tracking API Parameters used to force a site search request */
+ const PARAMETER_NAME_SEARCH_COUNT = 'search_count';
+ const PARAMETER_NAME_SEARCH_CATEGORY = 'search_cat';
+ const PARAMETER_NAME_SEARCH_KEYWORD = 'search';
+
+ /* Custom Variables names & slots plus Tracking API Parameters for performance analytics */
+ const DB_COLUMN_TIME_GENERATION = 'custom_float_1';
+ const PARAMETER_NAME_TIME_GENERATION = 'generation_time_ms';
+
+ /**
+ * Map URL prefixes to integers.
+ * @see self::normalizeUrl(), self::reconstructNormalizedUrl()
+ */
+ static public $urlPrefixMap = array(
+ 'http://www.' => 1,
+ 'http://' => 0,
+ 'https://www.' => 3,
+ 'https://' => 2
+ );
+
+ /**
+ * Extract the prefix from a URL.
+ * Return the prefix ID and the rest.
+ *
+ * @param string $url
+ * @return array
+ */
+ static public function normalizeUrl($url)
+ {
+ 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);
+ }
+
+ /**
+ * Build the full URL from the prefix ID and the rest.
+ *
+ * @param string $url
+ * @param integer $prefixId
+ * @return string
+ */
+ static public function reconstructNormalizedUrl($url, $prefixId)
+ {
+ $map = array_flip(self::$urlPrefixMap);
+ if ($prefixId !== null && isset($map[$prefixId])) {
+ $fullUrl = $map[$prefixId] . $url;
+ } else {
+ $fullUrl = $url;
+ }
+
+ // Clean up host & hash tags, for URLs
+ $parsedUrl = @parse_url($fullUrl);
+ $parsedUrl = self::cleanupHostAndHashTag($parsedUrl);
+ $url = Piwik_Common::getParseUrlReverse($parsedUrl);
+ if (!empty($url)) {
+ return $url;
+ }
+
+ return $fullUrl;
+ }
+
+
+ /**
+ * Set request parameters
+ *
+ * @param array $requestArray
+ */
+ public function setRequest($requestArray)
+ {
+ $this->request = $requestArray;
+ }
+
+ /**
+ * Returns the current set request parameters
+ *
+ * @return array
+ */
+ public function getRequest()
+ {
+ return $this->request;
+ }
+
+ /**
+ * Returns URL of the page currently being tracked, or the file being downloaded, or the outlink being clicked
+ *
+ * @return string
+ */
+ public function getActionUrl()
+ {
+ return $this->actionUrl;
+ }
+
+ public function getActionName()
+ {
+ return $this->actionName;
+ }
+
+ public function getActionType()
+ {
+ return $this->actionType;
+ }
+
+ public function getActionNameType()
+ {
+ $actionNameType = null;
+
+ // we can add here action types for names of other actions than page views (like downloads, outlinks)
+ switch ($this->getActionType()) {
+ case Piwik_Tracker_Action_Interface::TYPE_ACTION_URL:
+ $actionNameType = Piwik_Tracker_Action_Interface::TYPE_ACTION_NAME;
+ break;
+
+ case Piwik_Tracker_Action_Interface::TYPE_SITE_SEARCH:
+ $actionNameType = Piwik_Tracker_Action_Interface::TYPE_SITE_SEARCH;
+ break;
+ }
+
+ return $actionNameType;
+ }
+
+ public function getIdActionUrl()
+ {
+ $idUrl = $this->idActionUrl;
+ if (!empty($idUrl)) {
+ return $idUrl;
+ }
+ // Site Search, by default, will not track URL. We do not want URL to appear as "Page URL not defined"
+ // so we specifically set it to NULL in the table (the archiving query does IS NOT NULL)
+ if ($this->getActionType() == self::TYPE_SITE_SEARCH) {
+ return null;
+ }
+
+ // However, for other cases, we record idaction_url = 0 which will be displayed as "Page URL Not Defined"
+ return 0;
+ }
+
+ public function getIdActionName()
+ {
+ return $this->idActionName;
+ }
+
+ protected function setActionName($name)
+ {
+ $name = self::cleanupString($name);
+ $this->actionName = $name;
+ }
+
+ protected function setActionType($type)
+ {
+ $this->actionType = $type;
+ }
+
+ protected function setActionUrl($url)
+ {
+ $this->actionUrl = $url;
+ }
+
+ /**
+ * Converts Matrix URL format
+ * from http://example.org/thing;paramA=1;paramB=6542
+ * to http://example.org/thing?paramA=1&paramB=6542
+ *
+ * @param string $url
+ */
+ static public function convertMatrixUrl($originalUrl)
+ {
+ $posFirstSemiColon = strpos($originalUrl, ";");
+ if ($posFirstSemiColon === false) {
+ return $originalUrl;
+ }
+ $posQuestionMark = strpos($originalUrl, "?");
+ $replace = ($posQuestionMark === false);
+ 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;
+ }
+
+ static public function cleanupUrl($url)
+ {
+ $url = Piwik_Common::unsanitizeInputValue($url);
+ $url = self::cleanupString($url);
+ $url = self::convertMatrixUrl($url);
+ return $url;
+ }
+
+ /**
+ * Will cleanup the hostname (some browser do not strolower the hostname),
+ * and deal ith the hash tag on incoming URLs based on website setting.
+ *
+ * @param $parsedUrl
+ * @param $idSite int|false The site ID of the current visit. This parameter is
+ * only used by the tracker to see if we should remove
+ * the URL fragment for this site.
+ * @return array
+ */
+ static public function cleanupHostAndHashTag($parsedUrl, $idSite = false)
+ {
+ if (empty($parsedUrl)) {
+ return $parsedUrl;
+ }
+ if (!empty($parsedUrl['host'])) {
+ $parsedUrl['host'] = mb_strtolower($parsedUrl['host'], 'UTF-8');
+ }
+
+ if (!empty($parsedUrl['fragment'])) {
+ $parsedUrl['fragment'] = self::processUrlFragment($parsedUrl['fragment'], $idSite);
+ }
+
+ return $parsedUrl;
+ }
+
+ /**
+ * Cleans and/or removes the URL fragment of a URL.
+ *
+ * @param $urlFragment string The URL fragment to process.
+ * @param $idSite int|false If not false, this function will check if URL fragments
+ * should be removed for the site w/ this ID and if so,
+ * the returned processed fragment will be empty.
+ * @return string The processed URL fragment.
+ */
+ public static function processUrlFragment($urlFragment, $idSite = false)
+ {
+ // if we should discard the url fragment for this site, return an empty string as
+ // the processed url fragment
+ if ($idSite !== false
+ && self::shouldRemoveURLFragmentFor($idSite)
+ ) {
+ return '';
+ } else {
+ // Remove trailing Hash tag in ?query#hash#
+ if (substr($urlFragment, -1) == '#') {
+ $urlFragment = substr($urlFragment, 0, strlen($urlFragment) - 1);
+ }
+ return $urlFragment;
+ }
+ }
+
+ /**
+ * Returns true if URL fragments should be removed for a specific site,
+ * false if otherwise.
+ *
+ * This function uses the Tracker cache and not the MySQL database.
+ *
+ * @param $idSite int The ID of the site to check for.
+ * @return bool
+ */
+ public static function shouldRemoveURLFragmentFor($idSite)
+ {
+ $websiteAttributes = Piwik_Tracker_Cache::getCacheWebsiteAttributes($idSite);
+ return !$websiteAttributes['keep_url_fragment'];
+ }
+
+ /**
+ * Given the Input URL, will exclude all query parameters set for this site
+ * Note: Site Search parameters are excluded in detectSiteSearch()
+ * @static
+ * @param $originalUrl
+ * @param $idSite
+ * @return bool|string
+ */
+ static public function excludeQueryParametersFromUrl($originalUrl, $idSite)
+ {
+ $originalUrl = self::cleanupUrl($originalUrl);
+
+ $parsedUrl = @parse_url($originalUrl);
+ $parsedUrl = self::cleanupHostAndHashTag($parsedUrl, $idSite);
+ $parametersToExclude = self::getQueryParametersToExclude($idSite);
+
+ if (empty($parsedUrl['query'])) {
+ if (empty($parsedUrl['fragment'])) {
+ return Piwik_Common::getParseUrlReverse($parsedUrl);
+ }
+ // Exclude from the hash tag as well
+ $queryParameters = Piwik_Common::getArrayFromQueryString($parsedUrl['fragment']);
+ $parsedUrl['fragment'] = self::getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude);
+ $url = Piwik_Common::getParseUrlReverse($parsedUrl);
+ return $url;
+ }
+ $queryParameters = Piwik_Common::getArrayFromQueryString($parsedUrl['query']);
+ $parsedUrl['query'] = self::getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude);
+ $url = Piwik_Common::getParseUrlReverse($parsedUrl);
+ return $url;
+ }
+
+ /**
+ * Returns the array of parameters names that must be excluded from the Query String in all tracked URLs
+ * @static
+ * @param $idSite
+ * @return array
+ */
+ public static function getQueryParametersToExclude($idSite)
+ {
+ $campaignTrackingParameters = Piwik_Common::getCampaignParameters();
+
+ $campaignTrackingParameters = array_merge(
+ $campaignTrackingParameters[0], // campaign name parameters
+ $campaignTrackingParameters[1] // campaign keyword parameters
+ );
+
+ $website = Piwik_Tracker_Cache::getCacheWebsiteAttributes($idSite);
+ $excludedParameters = isset($website['excluded_parameters'])
+ ? $website['excluded_parameters']
+ : array();
+
+ if (!empty($excludedParameters)) {
+ printDebug('Excluding parameters "' . implode(',', $excludedParameters) . '" from URL');
+ }
+
+ $parametersToExclude = array_merge($excludedParameters,
+ self::$queryParametersToExclude,
+ $campaignTrackingParameters,
+ array(self::PARAMETER_NAME_TIME_GENERATION));
+
+ $parametersToExclude = array_map('strtolower', $parametersToExclude);
+ return $parametersToExclude;
+ }
+
+ /**
+ * Returns a Query string,
+ * Given an array of input parameters, and an array of parameter names to exclude
+ *
+ * @static
+ * @param $queryParameters
+ * @param $parametersToExclude
+ * @return string
+ */
+ public static function getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude)
+ {
+ $validQuery = '';
+ $separator = '&';
+ foreach ($queryParameters as $name => $value) {
+ // decode encoded square brackets
+ $name = str_replace(array('%5B', '%5D'), array('[', ']'), $name);
+
+ if (!in_array(strtolower($name), $parametersToExclude)) {
+ if (is_array($value)) {
+ foreach ($value as $param) {
+ if ($param === false) {
+ $validQuery .= $name . '[]' . $separator;
+ } else {
+ $validQuery .= $name . '[]=' . $param . $separator;
+ }
+ }
+ } else if ($value === false) {
+ $validQuery .= $name . $separator;
+ } else {
+ $validQuery .= $name . '=' . $value . $separator;
+ }
+ }
+ }
+ $validQuery = substr($validQuery, 0, -strlen($separator));
+ return $validQuery;
+ }
+
+ public function init()
+ {
+ $this->pageEncoding = Piwik_Common::getRequestVar('cs', false, null, $this->request);
+
+ $info = $this->extractUrlAndActionNameFromRequest();
+
+ $originalUrl = $info['url'];
+ $info['url'] = self::excludeQueryParametersFromUrl($originalUrl, $this->idSite);
+
+ if ($originalUrl != $info['url']) {
+ printDebug(' Before was "' . $originalUrl . '"');
+ printDebug(' After is "' . $info['url'] . '"');
+ }
+
+ // Set Final attributes for this Action (Pageview, Search, etc.)
+ $this->setActionName($info['name']);
+ $this->setActionType($info['type']);
+ $this->setActionUrl($info['url']);
+ }
+
+ static public function getSqlSelectActionId()
+ {
+ $sql = "SELECT idaction, type, name
+ FROM " . Piwik_Common::prefixTable('log_action')
+ . " WHERE "
+ . " ( hash = CRC32(?) AND name = ? AND type = ? ) ";
+ return $sql;
+ }
+
+ /**
+ * This function will find the idaction from the lookup table piwik_log_action,
+ * given an Action name and type.
+ *
+ * This is used to record Page URLs, Page Titles, Ecommerce items SKUs, item names, item categories
+ *
+ * If the action name does not exist in the lookup table, it will INSERT it
+ * @param array $actionNamesAndTypes Array of one or many (name,type)
+ * @return array Returns the input array, with the idaction appended ie. Array of one or many (name,type,idaction)
+ */
+ static public function loadActionId($actionNamesAndTypes)
+ {
+ // First, we try and select the actions that are already recorded
+ $sql = self::getSqlSelectActionId();
+ $bind = array();
+ $normalizedUrls = array();
+ $i = 0;
+ foreach ($actionNamesAndTypes as $index => &$actionNameType) {
+ list($name, $type) = $actionNameType;
+ if (empty($name)) {
+ $actionNameType[] = false;
+ continue;
+ }
+ if ($i > 0) {
+ $sql .= " OR ( hash = CRC32(?) AND name = ? AND type = ? ) ";
+ }
+ if ($type == Piwik_Tracker_Action::TYPE_ACTION_URL) {
+ // normalize urls by stripping protocol and www
+ $normalizedUrls[$index] = self::normalizeUrl($name);
+ $name = $normalizedUrls[$index]['url'];
+ }
+ $bind[] = $name;
+ $bind[] = $name;
+ $bind[] = $type;
+ $i++;
+ }
+ // Case URL & Title are empty
+ if (empty($bind)) {
+ return $actionNamesAndTypes;
+ }
+ $actionIds = Piwik_Tracker::getDatabase()->fetchAll($sql, $bind);
+
+ // For the Actions found in the lookup table, add the idaction in the array,
+ // If not found in lookup table, queue for INSERT
+ $actionsToInsert = array();
+ foreach ($actionNamesAndTypes as $index => &$actionNameType) {
+ list($name, $type) = $actionNameType;
+ if (empty($name)) {
+ continue;
+ }
+ if (isset($normalizedUrls[$index])) {
+ $name = $normalizedUrls[$index]['url'];
+ }
+ $found = false;
+ foreach ($actionIds as $row) {
+ if ($name == $row['name']
+ && $type == $row['type']
+ ) {
+ $found = true;
+ $actionNameType[] = $row['idaction'];
+ continue;
+ }
+ }
+ if (!$found) {
+ $actionsToInsert[] = $index;
+ }
+ }
+
+ $sql = "INSERT INTO " . Piwik_Common::prefixTable('log_action') .
+ "( name, hash, type, url_prefix ) VALUES (?,CRC32(?),?,?)";
+ // Then, we insert all new actions in the lookup table
+ foreach ($actionsToInsert as $actionToInsert) {
+ list($name, $type) = $actionNamesAndTypes[$actionToInsert];
+
+ $urlPrefix = null;
+ if (isset($normalizedUrls[$actionToInsert])) {
+ $name = $normalizedUrls[$actionToInsert]['url'];
+ $urlPrefix = $normalizedUrls[$actionToInsert]['prefixId'];
+ }
+
+ Piwik_Tracker::getDatabase()->query($sql, array($name, $name, $type, $urlPrefix));
+ $actionId = Piwik_Tracker::getDatabase()->lastInsertId();
+ printDebug("Recorded a new action (" . self::getActionTypeName($type) . ") in the lookup table: " . $name . " (idaction = " . $actionId . ")");
+
+ $actionNamesAndTypes[$actionToInsert][] = $actionId;
+ }
+ return $actionNamesAndTypes;
+ }
+
+ static public function getActionTypeName($type)
+ {
+ switch ($type) {
+ case self::TYPE_ACTION_URL:
+ return 'Page URL';
+ break;
+ case self::TYPE_OUTLINK:
+ return 'Outlink URL';
+ break;
+ case self::TYPE_DOWNLOAD:
+ return 'Download URL';
+ break;
+ case self::TYPE_ACTION_NAME:
+ return 'Page Title';
+ break;
+ case self::TYPE_SITE_SEARCH:
+ return 'Site Search';
+ break;
+ case self::TYPE_ECOMMERCE_ITEM_SKU:
+ return 'Ecommerce Item SKU';
+ break;
+ case self::TYPE_ECOMMERCE_ITEM_NAME:
+ return 'Ecommerce Item Name';
+ break;
+ case self::TYPE_ECOMMERCE_ITEM_CATEGORY:
+ return 'Ecommerce Item Category';
+ break;
+ default:
+ throw new Exception("Unexpected action type " . $type);
+ break;
+ }
+ }
+
+ /**
+ * Loads the idaction of the current action name and the current action url.
+ * These idactions are used in the visitor logging table to link the visit information
+ * (entry action, exit action) to the actions.
+ * These idactions are also used in the table that links the visits and their actions.
+ *
+ * The methods takes care of creating a new record(s) in the action table if the existing
+ * action name and action url doesn't exist yet.
+ */
+ function loadIdActionNameAndUrl()
+ {
+ if ($this->idActionUrl !== false
+ && $this->idActionName !== false
+ ) {
+ return;
+ }
+ $actions = array();
+ $nameType = $this->getActionNameType();
+ $action = array($this->getActionName(), $nameType);
+ if (!is_null($action[1])) {
+ $actions[] = $action;
+ }
+
+ $urlType = $this->getActionType();
+ $url = $this->getActionUrl();
+ // this code is a mess, but basically, getActionType() returns SITE_SEARCH,
+ // but we do want to record the site search URL as an ACTION_URL
+ if ($nameType == Piwik_Tracker_Action::TYPE_SITE_SEARCH) {
+ $urlType = Piwik_Tracker_Action::TYPE_ACTION_URL;
+
+ // By default, Site Search does not record the URL for the Search Result page, to slightly improve performance
+ if (empty(Piwik_Config::getInstance()->Tracker['action_sitesearch_record_url'])) {
+ $url = false;
+ }
+ }
+ if (!is_null($urlType) && !empty($url)) {
+ $actions[] = array($url, $urlType);
+ }
+
+ $loadedActionIds = self::loadActionId($actions);
+
+ foreach ($loadedActionIds as $loadedActionId) {
+ list($name, $type, $actionId) = $loadedActionId;
+ if ($type == Piwik_Tracker_Action::TYPE_ACTION_NAME
+ || $type == Piwik_Tracker_Action::TYPE_SITE_SEARCH
+ ) {
+ $this->idActionName = $actionId;
+ } else {
+ $this->idActionUrl = $actionId;
+ }
+ }
+ }
+
+ /**
+ * @param int $idSite
+ */
+ function setIdSite($idSite)
+ {
+ $this->idSite = $idSite;
+ }
+
+ function setTimestamp($timestamp)
+ {
+ $this->timestamp = $timestamp;
+ }
+
+
+ /**
+ * Records in the DB the association between the visit and this action.
+ *
+ * @param int $idVisit is the ID of the current visit in the DB table log_visit
+ * @param $visitorIdCookie
+ * @param int $idRefererActionUrl is the ID of the last action done by the current visit.
+ * @param $idRefererActionName
+ * @param int $timeSpentRefererAction is the number of seconds since the last action was done.
+ * It is directly related to idRefererActionUrl.
+ */
+ public function record($idVisit, $visitorIdCookie, $idRefererActionUrl, $idRefererActionName, $timeSpentRefererAction)
+ {
+ $this->loadIdActionNameAndUrl();
+
+ $idActionName = in_array($this->getActionType(), array(Piwik_Tracker_Action::TYPE_ACTION_NAME,
+ Piwik_Tracker_Action::TYPE_ACTION_URL,
+ Piwik_Tracker_Action::TYPE_SITE_SEARCH))
+ ? (int)$this->getIdActionName()
+ : null;
+
+
+ $insert = array(
+ 'idvisit' => $idVisit,
+ 'idsite' => $this->idSite,
+ 'idvisitor' => $visitorIdCookie,
+ 'server_time' => Piwik_Tracker::getDatetimeFromTimestamp($this->timestamp),
+ 'idaction_url' => $this->getIdActionUrl(),
+ 'idaction_name' => $idActionName,
+ 'idaction_url_ref' => $idRefererActionUrl,
+ 'idaction_name_ref' => $idRefererActionName,
+ 'time_spent_ref_action' => $timeSpentRefererAction
+ );
+
+ if (!empty($this->timeGeneration)) {
+ $insert[self::DB_COLUMN_TIME_GENERATION] = (int)$this->timeGeneration;
+ }
+
+ $customVariables = $this->getCustomVariables();
+
+ $insert = array_merge($insert, $customVariables);
+
+ // Mysqli apparently does not like NULL inserts?
+ $insertWithoutNulls = array();
+ foreach ($insert as $column => $value) {
+ if (!is_null($value) || $column == 'idaction_url_ref') {
+ $insertWithoutNulls[$column] = $value;
+ }
+ }
+
+ $fields = implode(", ", array_keys($insertWithoutNulls));
+ $bind = array_values($insertWithoutNulls);
+ $values = Piwik_Common::getSqlStringFieldsArray($insertWithoutNulls);
+
+ $sql = "INSERT INTO " . Piwik_Common::prefixTable('log_link_visit_action') . " ($fields) VALUES ($values)";
+ Piwik_Tracker::getDatabase()->query($sql, $bind);
+
+ $this->idLinkVisitAction = Piwik_Tracker::getDatabase()->lastInsertId();
+
+ $info = array(
+ 'idSite' => $this->idSite,
+ 'idLinkVisitAction' => $this->idLinkVisitAction,
+ 'idVisit' => $idVisit,
+ 'idRefererActionUrl' => $idRefererActionUrl,
+ 'idRefererActionName' => $idRefererActionName,
+ 'timeSpentRefererAction' => $timeSpentRefererAction,
+ );
+ printDebug($insertWithoutNulls);
+
+ /*
+ * send the Action object ($this) and the list of ids ($info) as arguments to the event
+ */
+ Piwik_PostEvent('Tracker.Action.record', $this, $info);
+ }
+
+ public function getCustomVariables()
+ {
+ $customVariables = Piwik_Tracker_Visit::getCustomVariables($scope = 'page', $this->request);
+
+ // Enrich Site Search actions with Custom Variables, overwriting existing values
+ if (!empty($this->searchCategory)) {
+ if (!empty($customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_CATEGORY])) {
+ printDebug("WARNING: Overwriting existing Custom Variable in slot " . self::CVAR_INDEX_SEARCH_CATEGORY . " for this page view");
+ }
+ $customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_CATEGORY] = self::CVAR_KEY_SEARCH_CATEGORY;
+ $customVariables['custom_var_v' . self::CVAR_INDEX_SEARCH_CATEGORY] = Piwik_Tracker_Visit::truncateCustomVariable($this->searchCategory);
+ }
+ if ($this->searchCount !== false) {
+ if (!empty($customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_COUNT])) {
+ printDebug("WARNING: Overwriting existing Custom Variable in slot " . self::CVAR_INDEX_SEARCH_COUNT . " for this page view");
+ }
+ $customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_COUNT] = self::CVAR_KEY_SEARCH_COUNT;
+ $customVariables['custom_var_v' . self::CVAR_INDEX_SEARCH_COUNT] = (int)$this->searchCount;
+ }
+
+ if (!empty($customVariables)) {
+ printDebug("Page level Custom Variables: ");
+ printDebug($customVariables);
+ }
+ return $customVariables;
+ }
+
+ /**
+ * Returns the ID of the newly created record in the log_link_visit_action table
+ *
+ * @return int | false
+ */
+ public function getIdLinkVisitAction()
+ {
+ return $this->idLinkVisitAction;
+ }
+
+ /**
+ * Generates the name of the action from the URL or the specified name.
+ * Sets the name as $this->actionName
+ *
+ * @return array
+ */
+ protected function extractUrlAndActionNameFromRequest()
+ {
+ $actionName = null;
+
+ // download?
+ $downloadUrl = Piwik_Common::getRequestVar('download', '', 'string', $this->request);
+ if (!empty($downloadUrl)) {
+ $actionType = self::TYPE_DOWNLOAD;
+ $url = $downloadUrl;
+ }
+
+ // outlink?
+ if (empty($actionType)) {
+ $outlinkUrl = Piwik_Common::getRequestVar('link', '', 'string', $this->request);
+ if (!empty($outlinkUrl)) {
+ $actionType = self::TYPE_OUTLINK;
+ $url = $outlinkUrl;
+ }
+ }
+
+ // handle encoding
+ $actionName = Piwik_Common::getRequestVar('action_name', '', 'string', $this->request);
+
+ // defaults to page view
+ if (empty($actionType)) {
+ $actionType = self::TYPE_ACTION_URL;
+ $url = Piwik_Common::getRequestVar('url', '', 'string', $this->request);
+
+ // get the delimiter, by default '/'; BC, we read the old action_category_delimiter first (see #1067)
+ $actionCategoryDelimiter = isset(Piwik_Config::getInstance()->General['action_category_delimiter'])
+ ? Piwik_Config::getInstance()->General['action_category_delimiter']
+ : Piwik_Config::getInstance()->General['action_url_category_delimiter'];
+
+ // create an array of the categories delimited by the delimiter
+ $split = explode($actionCategoryDelimiter, $actionName);
+
+ // trim every category
+ $split = array_map('trim', $split);
+
+ // remove empty categories
+ $split = array_filter($split, 'strlen');
+
+ // rebuild the name from the array of cleaned categories
+ $actionName = implode($actionCategoryDelimiter, $split);
+ }
+ $url = self::cleanupString($url);
+
+ if (!Piwik_Common::isLookLikeUrl($url)) {
+ printDebug("WARNING: URL looks invalid and is discarded");
+ $url = '';
+ }
+
+ // Site search?
+ if ($actionType == self::TYPE_ACTION_URL) {
+ // Look in tracked URL for the Site Search parameters
+ $siteSearch = $this->detectSiteSearch($url);
+ if (!empty($siteSearch)) {
+ $actionType = self::TYPE_SITE_SEARCH;
+ list($actionName, $url) = $siteSearch;
+ }
+ // Look for performance analytics parameters
+ $this->detectPerformanceAnalyticsParameters();
+ }
+ $actionName = self::cleanupString($actionName);
+
+ return array(
+ 'name' => empty($actionName) ? '' : $actionName,
+ 'type' => $actionType,
+ 'url' => $url,
+ );
+ }
+
+ protected function detectSiteSearch($originalUrl)
+ {
+ $website = Piwik_Tracker_Cache::getCacheWebsiteAttributes($this->idSite);
+ if (empty($website['sitesearch'])) {
+ printDebug("Internal 'Site Search' tracking is not enabled for this site. ");
+ return false;
+ }
+ $actionName = $url = $categoryName = $count = false;
+ $doTrackUrlForSiteSearch = !empty(Piwik_Config::getInstance()->Tracker['action_sitesearch_record_url']);
+
+ $originalUrl = self::cleanupUrl($originalUrl);
+
+
+ // Detect Site search from Tracking API parameters rather than URL
+ $searchKwd = Piwik_Common::getRequestVar(self::PARAMETER_NAME_SEARCH_KEYWORD, '', 'string', $this->request);
+ if (!empty($searchKwd)) {
+ $actionName = $searchKwd;
+ if ($doTrackUrlForSiteSearch) {
+ $url = $originalUrl;
+ }
+ $isCategoryName = Piwik_Common::getRequestVar(self::PARAMETER_NAME_SEARCH_CATEGORY, false, 'string', $this->request);
+ if (!empty($isCategoryName)) {
+ $categoryName = $isCategoryName;
+ }
+ $isCount = Piwik_Common::getRequestVar(self::PARAMETER_NAME_SEARCH_COUNT, -1, 'int', $this->request);
+ if ($this->isValidSearchCount($isCount)) {
+ $count = $isCount;
+ }
+ }
+
+ if (empty($actionName)) {
+ $parsedUrl = @parse_url($originalUrl);
+
+ // Detect Site Search from URL query parameters
+ if (!empty($parsedUrl['query']) || !empty($parsedUrl['fragment'])) {
+ // array($url, $actionName, $categoryName, $count);
+ $searchInfo = $this->detectSiteSearchFromUrl($website, $parsedUrl);
+ if (!empty($searchInfo)) {
+ list ($url, $actionName, $categoryName, $count) = $searchInfo;
+ }
+ }
+ }
+
+ if (empty($actionName)) {
+ printDebug("(this is not a Site Search request)");
+ return false;
+ }
+
+ printDebug("Detected Site Search keyword '$actionName'. ");
+ if (!empty($categoryName)) {
+ printDebug("- Detected Site Search Category '$categoryName'. ");
+ }
+ if ($count !== false) {
+ printDebug("- Search Results Count was '$count'. ");
+ }
+ if ($url != $originalUrl) {
+ printDebug("NOTE: The Page URL was changed / removed, during the Site Search detection, was '$originalUrl', now is '$url'");
+ }
+
+ if (!empty($categoryName) || $count !== false) {
+ $this->setActionSearchMetadata($categoryName, $count);
+ }
+ return array(
+ $actionName,
+ $url
+ );
+ }
+
+ protected function isValidSearchCount($count)
+ {
+ return is_numeric($count) && $count >= 0;
+ }
+
+
+ protected function setActionSearchMetadata($category, $count)
+ {
+ if (!empty($category)) {
+ $this->searchCategory = trim($category);
+ }
+ if ($count !== false) {
+ $this->searchCount = $count;
+ }
+ }
+
+ protected function detectSiteSearchFromUrl($website, $parsedUrl)
+ {
+ $doRemoveSearchParametersFromUrl = false;
+ $separator = '&';
+ $count = $actionName = $categoryName = false;
+
+ $keywordParameters = isset($website['sitesearch_keyword_parameters'])
+ ? $website['sitesearch_keyword_parameters']
+ : array();
+ $queryString = (!empty($parsedUrl['query']) ? $parsedUrl['query'] : '') . (!empty($parsedUrl['fragment']) ? $separator . $parsedUrl['fragment'] : '');
+ $parametersRaw = Piwik_Common::getArrayFromQueryString($queryString);
+
+ // strtolower the parameter names for smooth site search detection
+ $parameters = array();
+ foreach ($parametersRaw as $k => $v) {
+ $parameters[Piwik_Common::mb_strtolower($k)] = $v;
+ }
+ // decode values if they were sent from a client using another charset
+ self::reencodeParameters($parameters, $this->pageEncoding);
+
+ // Detect Site Search keyword
+ foreach ($keywordParameters as $keywordParameterRaw) {
+ $keywordParameter = Piwik_Common::mb_strtolower($keywordParameterRaw);
+ if (!empty($parameters[$keywordParameter])) {
+ $actionName = $parameters[$keywordParameter];
+ break;
+ }
+ }
+
+ if (empty($actionName)) {
+ return false;
+ }
+
+ $categoryParameters = isset($website['sitesearch_category_parameters'])
+ ? $website['sitesearch_category_parameters']
+ : array();
+
+ foreach ($categoryParameters as $categoryParameterRaw) {
+ $categoryParameter = Piwik_Common::mb_strtolower($categoryParameterRaw);
+ if (!empty($parameters[$categoryParameter])) {
+ $categoryName = $parameters[$categoryParameter];
+ break;
+ }
+ }
+
+ if (isset($parameters[self::PARAMETER_NAME_SEARCH_COUNT])
+ && $this->isValidSearchCount($parameters[self::PARAMETER_NAME_SEARCH_COUNT])
+ ) {
+ $count = $parameters[self::PARAMETER_NAME_SEARCH_COUNT];
+ }
+ // Remove search kwd from URL
+ if ($doRemoveSearchParametersFromUrl) {
+ // @see excludeQueryParametersFromUrl()
+ // Excluded the detected parameters from the URL
+ $parametersToExclude = array($categoryParameterRaw, $keywordParameterRaw);
+ $parsedUrl['query'] = self::getQueryStringWithExcludedParameters(Piwik_Common::getArrayFromQueryString($parsedUrl['query']), $parametersToExclude);
+ $parsedUrl['fragment'] = self::getQueryStringWithExcludedParameters(Piwik_Common::getArrayFromQueryString($parsedUrl['fragment']), $parametersToExclude);
+ }
+ $url = Piwik_Common::getParseUrlReverse($parsedUrl);
+ if (is_array($actionName)) {
+ $actionName = reset($actionName);
+ }
+ $actionName = trim(urldecode($actionName));
+ if (empty($actionName)) {
+ return false;
+ }
+ if (is_array($categoryName)) {
+ $categoryName = reset($categoryName);
+ }
+ $categoryName = trim(urldecode($categoryName));
+ return array($url, $actionName, $categoryName, $count);
+ }
+
+ protected function detectPerformanceAnalyticsParameters()
+ {
+ $generationTime = Piwik_Common::getRequestVar(self::PARAMETER_NAME_TIME_GENERATION, -1, 'int', $this->request);
+ if ($generationTime > 0) {
+ $this->timeGeneration = $generationTime;
+ }
+ }
+
+ /**
+ * Clean up string contents (filter, truncate, ...)
+ *
+ * @param string $string Dirty string
+ * @return string
+ */
+ protected static function cleanupString($string)
+ {
+ $string = trim($string);
+ $string = str_replace(array("\n", "\r", "\0"), '', $string);
+
+ $limit = Piwik_Config::getInstance()->Tracker['page_maximum_length'];
+ return substr($string, 0, $limit);
+ }
+
+ /**
+ * Checks if query parameters are of a non-UTF-8 encoding and converts the values
+ * from the specified encoding to UTF-8.
+ *
+ * This method is used to workaround browser/webapp bugs (see #3450). When
+ * browsers fail to encode query parameters in UTF-8, the tracker will send the
+ * charset of the page viewed and we can sometimes work around invalid data
+ * being stored.
+ *
+ * @param array $queryParameters Name/value mapping of query parameters.
+ * @param string|false $encoding of the HTML page the URL is for. Used to workaround
+ * browser bugs & mis-coded webapps. See #3450.
+ */
+ private static function reencodeParameters(&$queryParameters, $encoding = false)
+ {
+ // 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'
+ && function_exists('mb_check_encoding')
+ ) {
+ $queryParameters = self::reencodeParametersArray($queryParameters, $encoding);
+ }
+ return $queryParameters;
+ }
+
+ private static function reencodeParametersArray($queryParameters, $encoding)
+ {
+ foreach ($queryParameters as &$value) {
+ if (is_array($value)) {
+ $value = self::reencodeParametersArray($value, $encoding);
+ } else {
+ $value = self::reencodeParameterValue($value, $encoding);
+ }
+ }
+ return $queryParameters;
+ }
+
+ private static function reencodeParameterValue($value, $encoding)
+ {
+ if (is_string($value)) {
+ $decoded = urldecode($value);
+ if (@mb_check_encoding($decoded, $encoding)) {
+ $value = urlencode(mb_convert_encoding($decoded, 'UTF-8', $encoding));
+ }
+ }
+ return $value;
+ }
}
diff --git a/core/Tracker/Cache.php b/core/Tracker/Cache.php
index 49526a6bb8..b537cc31a8 100644
--- a/core/Tracker/Cache.php
+++ b/core/Tracker/Cache.php
@@ -17,141 +17,138 @@
*/
class Piwik_Tracker_Cache
{
- /**
- * Public for tests only
- * @var Piwik_CacheFile
- */
- static public $trackerCache = null;
-
- static protected function getInstance()
- {
- if(is_null(self::$trackerCache)) {
- $ttl = Piwik_Config::getInstance()->Tracker['tracker_cache_file_ttl'];
- self::$trackerCache = new Piwik_CacheFile('tracker', $ttl);
- }
- return self::$trackerCache;
- }
-
- /**
- * Returns array containing data about the website: goals, URLs, etc.
- *
- * @param int $idSite
- * @return array
- */
- static function getCacheWebsiteAttributes( $idSite )
- {
- $idSite = (int)$idSite;
-
- $cache = self::getInstance();
- if(($cacheContent = $cache->get($idSite)) !== false)
- {
- return $cacheContent;
- }
-
- Piwik_Tracker::initCorePiwikInTrackerMode();
-
- // save current user privilege and temporarily assume super user privilege
- $isSuperUser = Piwik::isUserIsSuperUser();
- Piwik::setUserIsSuperUser();
-
- $content = array();
- Piwik_PostEvent('Common.fetchWebsiteAttributes', $content, $idSite);
-
- // restore original user privilege
- Piwik::setUserIsSuperUser($isSuperUser);
-
- // 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;
- }
-
- /**
- * Clear general (global) cache
- */
- static public function clearCacheGeneral()
- {
- self::getInstance()->delete('general');
- }
-
- /**
- * Returns contents of general (global) cache.
- * If the cache file tmp/cache/tracker/general.php does not exist yet, create it
- *
- * @return array
- */
- static public function getCacheGeneral()
- {
- $cache = self::getInstance();
- $cacheId = 'general';
- $expectedRows = 3;
- if(($cacheContent = $cache->get($cacheId)) !== false
- && count($cacheContent) == $expectedRows)
- {
- return $cacheContent;
- }
-
- Piwik_Tracker::initCorePiwikInTrackerMode();
- $cacheContent = array (
- 'isBrowserTriggerArchivingEnabled' => Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled(),
- 'lastTrackerCronRun' => Piwik_GetOption('lastTrackerCronRun'),
- 'currentLocationProviderId' => Piwik_UserCountry_LocationProvider::getCurrentProviderId(),
- );
- self::setCacheGeneral( $cacheContent );
- return $cacheContent;
- }
-
- /**
- * Store data in general (global cache)
- *
- * @param mixed $value
- * @return bool
- */
- static public function setCacheGeneral($value)
- {
- $cache = self::getInstance();
- $cacheId = 'general';
- $cache->set($cacheId, $value);
- return true;
- }
-
- /**
- * Regenerate Tracker cache files
- *
- * @param array $idSites Array of idSites to clear cache for
- */
- static public function regenerateCacheWebsiteAttributes($idSites = array())
- {
- if(!is_array($idSites))
- {
- $idSites = array( $idSites );
- }
- foreach($idSites as $idSite) {
- self::deleteCacheWebsiteAttributes($idSite);
- self::getCacheWebsiteAttributes($idSite);
- }
- }
-
- /**
- * Delete existing Tracker cache
- *
- * @param string $idSite (website ID of the site to clear cache for
- */
- static public function deleteCacheWebsiteAttributes( $idSite )
- {
- $idSite = (int)$idSite;
- self::getInstance()->delete($idSite);
- }
-
- /**
- * Deletes all Tracker cache files
- */
- static public function deleteTrackerCache()
- {
- self::getInstance()->deleteAll();
- }
+ /**
+ * Public for tests only
+ * @var Piwik_CacheFile
+ */
+ static public $trackerCache = null;
+
+ static protected function getInstance()
+ {
+ if (is_null(self::$trackerCache)) {
+ $ttl = Piwik_Config::getInstance()->Tracker['tracker_cache_file_ttl'];
+ self::$trackerCache = new Piwik_CacheFile('tracker', $ttl);
+ }
+ return self::$trackerCache;
+ }
+
+ /**
+ * Returns array containing data about the website: goals, URLs, etc.
+ *
+ * @param int $idSite
+ * @return array
+ */
+ static function getCacheWebsiteAttributes($idSite)
+ {
+ $idSite = (int)$idSite;
+
+ $cache = self::getInstance();
+ if (($cacheContent = $cache->get($idSite)) !== false) {
+ return $cacheContent;
+ }
+
+ Piwik_Tracker::initCorePiwikInTrackerMode();
+
+ // save current user privilege and temporarily assume super user privilege
+ $isSuperUser = Piwik::isUserIsSuperUser();
+ Piwik::setUserIsSuperUser();
+
+ $content = array();
+ Piwik_PostEvent('Common.fetchWebsiteAttributes', $content, $idSite);
+
+ // restore original user privilege
+ Piwik::setUserIsSuperUser($isSuperUser);
+
+ // 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;
+ }
+
+ /**
+ * Clear general (global) cache
+ */
+ static public function clearCacheGeneral()
+ {
+ self::getInstance()->delete('general');
+ }
+
+ /**
+ * Returns contents of general (global) cache.
+ * If the cache file tmp/cache/tracker/general.php does not exist yet, create it
+ *
+ * @return array
+ */
+ static public function getCacheGeneral()
+ {
+ $cache = self::getInstance();
+ $cacheId = 'general';
+ $expectedRows = 3;
+ if (($cacheContent = $cache->get($cacheId)) !== false
+ && count($cacheContent) == $expectedRows
+ ) {
+ return $cacheContent;
+ }
+
+ Piwik_Tracker::initCorePiwikInTrackerMode();
+ $cacheContent = array(
+ 'isBrowserTriggerArchivingEnabled' => Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled(),
+ 'lastTrackerCronRun' => Piwik_GetOption('lastTrackerCronRun'),
+ 'currentLocationProviderId' => Piwik_UserCountry_LocationProvider::getCurrentProviderId(),
+ );
+ self::setCacheGeneral($cacheContent);
+ return $cacheContent;
+ }
+
+ /**
+ * Store data in general (global cache)
+ *
+ * @param mixed $value
+ * @return bool
+ */
+ static public function setCacheGeneral($value)
+ {
+ $cache = self::getInstance();
+ $cacheId = 'general';
+ $cache->set($cacheId, $value);
+ return true;
+ }
+
+ /**
+ * Regenerate Tracker cache files
+ *
+ * @param array $idSites Array of idSites to clear cache for
+ */
+ static public function regenerateCacheWebsiteAttributes($idSites = array())
+ {
+ if (!is_array($idSites)) {
+ $idSites = array($idSites);
+ }
+ foreach ($idSites as $idSite) {
+ self::deleteCacheWebsiteAttributes($idSite);
+ self::getCacheWebsiteAttributes($idSite);
+ }
+ }
+
+ /**
+ * Delete existing Tracker cache
+ *
+ * @param string $idSite (website ID of the site to clear cache for
+ */
+ static public function deleteCacheWebsiteAttributes($idSite)
+ {
+ $idSite = (int)$idSite;
+ self::getInstance()->delete($idSite);
+ }
+
+ /**
+ * Deletes all Tracker cache files
+ */
+ static public function deleteTrackerCache()
+ {
+ self::getInstance()->deleteAll();
+ }
} \ No newline at end of file
diff --git a/core/Tracker/Config.php b/core/Tracker/Config.php
index 90fe0e1f45..ed9bb9fb2c 100644
--- a/core/Tracker/Config.php
+++ b/core/Tracker/Config.php
@@ -14,7 +14,7 @@
* DO NOT USE
*
* Use this notation to fetch a config file value:
- * Piwik_Config::getInstance()->General['enable_browser_archiving_triggering']
+ * Piwik_Config::getInstance()->General['enable_browser_archiving_triggering']
*
* @todo remove this in 2.0
* @since 1.7
@@ -25,13 +25,13 @@
*/
class Piwik_Tracker_Config
{
- /**
- * Returns the singleton Piwik_Config
- *
- * @return Piwik_Config
- */
- static public function getInstance()
- {
- return Piwik_Config::getInstance();
- }
+ /**
+ * Returns the singleton Piwik_Config
+ *
+ * @return Piwik_Config
+ */
+ static public function getInstance()
+ {
+ return Piwik_Config::getInstance();
+ }
}
diff --git a/core/Tracker/Db.php b/core/Tracker/Db.php
index b00ce82585..acccd4e858 100644
--- a/core/Tracker/Db.php
+++ b/core/Tracker/Db.php
@@ -12,213 +12,211 @@
/**
* Simple database wrapper.
* We can't afford to have a dependency with the Zend_Db module in Tracker.
- * We wrote this simple class
+ * We wrote this simple class
*
* @package Piwik
* @subpackage Piwik_Tracker
*/
abstract class Piwik_Tracker_Db
{
- protected static $profiling = false;
-
- protected $queriesProfiling = array();
-
- protected $connection = null;
-
- /**
- * Enables the SQL profiling.
- * For each query, saves in the DB the time spent on this query.
- * Very useful to see the slow query under heavy load.
- * You can then use Piwik::printSqlProfilingReportTracker();
- * to display the SQLProfiling report and see which queries take time, etc.
- */
- public static function enableProfiling()
- {
- self::$profiling = true;
- }
-
- /**
- * Disables the SQL profiling logging.
- */
- public static function disableProfiling()
- {
- self::$profiling = false;
- }
-
- /**
- * Returns true if the SQL profiler is enabled
- * Only used by the unit test that tests that the profiler is off on a production server
- *
- * @return bool
- */
- public static function isProfilingEnabled()
- {
- return self::$profiling;
- }
-
- /**
- * Initialize profiler
- *
- * @return Piwik_Timer
- */
- protected function initProfiler()
- {
- return new Piwik_Timer;
- }
-
- /**
- * Record query profile
- *
- * @param string $query
- * @param Piwik_Timer $timer
- */
- protected function recordQueryProfile( $query, $timer )
- {
- 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);
- }
-
- /**
- * When destroyed, if SQL profiled enabled, logs the SQL profiling information
- */
- public function recordProfiling()
- {
- if(is_null($this->connection))
- {
- return;
- }
-
- // turn off the profiler so we don't profile the following queries
- self::$profiling = false;
-
- foreach($this->queriesProfiling as $query => $info)
- {
- $time = $info['sum_time_ms'];
- $count = $info['count'];
-
- $queryProfiling = "INSERT INTO ".Piwik_Common::prefixTable('log_profiling')."
+ protected static $profiling = false;
+
+ protected $queriesProfiling = array();
+
+ protected $connection = null;
+
+ /**
+ * Enables the SQL profiling.
+ * For each query, saves in the DB the time spent on this query.
+ * Very useful to see the slow query under heavy load.
+ * You can then use Piwik::printSqlProfilingReportTracker();
+ * to display the SQLProfiling report and see which queries take time, etc.
+ */
+ public static function enableProfiling()
+ {
+ self::$profiling = true;
+ }
+
+ /**
+ * Disables the SQL profiling logging.
+ */
+ public static function disableProfiling()
+ {
+ self::$profiling = false;
+ }
+
+ /**
+ * Returns true if the SQL profiler is enabled
+ * Only used by the unit test that tests that the profiler is off on a production server
+ *
+ * @return bool
+ */
+ public static function isProfilingEnabled()
+ {
+ return self::$profiling;
+ }
+
+ /**
+ * Initialize profiler
+ *
+ * @return Piwik_Timer
+ */
+ protected function initProfiler()
+ {
+ return new Piwik_Timer;
+ }
+
+ /**
+ * Record query profile
+ *
+ * @param string $query
+ * @param Piwik_Timer $timer
+ */
+ protected function recordQueryProfile($query, $timer)
+ {
+ 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);
+ }
+
+ /**
+ * When destroyed, if SQL profiled enabled, logs the SQL profiling information
+ */
+ public function recordProfiling()
+ {
+ if (is_null($this->connection)) {
+ return;
+ }
+
+ // turn off the profiler so we don't profile the following queries
+ self::$profiling = false;
+
+ foreach ($this->queriesProfiling as $query => $info) {
+ $time = $info['sum_time_ms'];
+ $count = $info['count'];
+
+ $queryProfiling = "INSERT INTO " . Piwik_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";
- $this->query($queryProfiling,array($query));
- }
-
- // turn back on profiling
- self::$profiling = true;
- }
-
- /**
- * Connects to the DB
- *
- * @throws Piwik_Tracker_Db_Exception if there was an error connecting the DB
- */
- abstract public function connect();
-
- /**
- * Disconnects from the server
- */
- public function disconnect()
- {
- $this->connection = null;
- }
-
- /**
- * Returns an array containing all the rows of a query result, using optional bound parameters.
- *
- * @param string $query Query
- * @param array $parameters Parameters to bind
- * @see query()
- * @throws Piwik_Tracker_Db_Exception if an exception occurred
- */
- abstract public function fetchAll( $query, $parameters = array() );
-
- /**
- * Returns the first row of a query result, using optional bound parameters.
- *
- * @param string $query Query
- * @param array $parameters Parameters to bind
- * @see also query()
- *
- * @throws Piwik_Tracker_Db_Exception if an exception occurred
- */
- abstract public function fetch( $query, $parameters = array() );
-
- /**
- * This function is a proxy to fetch(), used to maintain compatibility with Zend_Db interface
- *
- * @see fetch()
- * @param string $query Query
- * @param array $parameters Parameters to bind
- * @return
- */
- public function fetchRow( $query, $parameters = array() )
- {
- return $this->fetch($query, $parameters);
- }
-
- /**
- * This function is a proxy to fetch(), used to maintain compatibility with Zend_Db interface
- *
- * @see fetch()
- * @param string $query Query
- * @param array $parameters Parameters to bind
- * @return bool|mixed
- */
- public function fetchOne( $query, $parameters = array() )
- {
- $result = $this->fetch($query, $parameters);
- return is_array($result) && !empty($result) ? reset($result) : false;
- }
-
- /**
- * This function is a proxy to fetch(), used to maintain compatibility with Zend_Db + PDO interface
- *
- * @see fetch()
- * @param string $query Query
- * @param array $parameters Parameters to bind
- * @return
- */
- public function exec( $query, $parameters = array() )
- {
- return $this->fetch($query, $parameters);
- }
-
- /**
- * Return number of affected rows in last query
- *
- * @param mixed $queryResult Result from query()
- * @return int
- */
- abstract public function rowCount($queryResult);
-
- /**
- * Executes a query, using optional bound parameters.
- *
- * @param string $query Query
- * @param array $parameters Parameters to bind array('idsite'=> 1)
- *
- * @return PDOStatement or false if failed
- * @throws Piwik_Tracker_Db_Exception if an exception occurred
- */
- abstract public function query($query, $parameters = array());
-
- /**
- * Returns the last inserted ID in the DB
- * Wrapper of PDO::lastInsertId()
- *
- * @return int
- */
- abstract public function lastInsertId();
-
- /**
- * Test error number
- *
- * @param Exception $e
- * @param string $errno
- * @return bool True if error number matches; false otherwise
- */
- abstract public function isErrNo($e, $errno);
+ $this->query($queryProfiling, array($query));
+ }
+
+ // turn back on profiling
+ self::$profiling = true;
+ }
+
+ /**
+ * Connects to the DB
+ *
+ * @throws Piwik_Tracker_Db_Exception if there was an error connecting the DB
+ */
+ abstract public function connect();
+
+ /**
+ * Disconnects from the server
+ */
+ public function disconnect()
+ {
+ $this->connection = null;
+ }
+
+ /**
+ * Returns an array containing all the rows of a query result, using optional bound parameters.
+ *
+ * @param string $query Query
+ * @param array $parameters Parameters to bind
+ * @see query()
+ * @throws Piwik_Tracker_Db_Exception if an exception occurred
+ */
+ abstract public function fetchAll($query, $parameters = array());
+
+ /**
+ * Returns the first row of a query result, using optional bound parameters.
+ *
+ * @param string $query Query
+ * @param array $parameters Parameters to bind
+ * @see also query()
+ *
+ * @throws Piwik_Tracker_Db_Exception if an exception occurred
+ */
+ abstract public function fetch($query, $parameters = array());
+
+ /**
+ * This function is a proxy to fetch(), used to maintain compatibility with Zend_Db interface
+ *
+ * @see fetch()
+ * @param string $query Query
+ * @param array $parameters Parameters to bind
+ * @return
+ */
+ public function fetchRow($query, $parameters = array())
+ {
+ return $this->fetch($query, $parameters);
+ }
+
+ /**
+ * This function is a proxy to fetch(), used to maintain compatibility with Zend_Db interface
+ *
+ * @see fetch()
+ * @param string $query Query
+ * @param array $parameters Parameters to bind
+ * @return bool|mixed
+ */
+ public function fetchOne($query, $parameters = array())
+ {
+ $result = $this->fetch($query, $parameters);
+ return is_array($result) && !empty($result) ? reset($result) : false;
+ }
+
+ /**
+ * This function is a proxy to fetch(), used to maintain compatibility with Zend_Db + PDO interface
+ *
+ * @see fetch()
+ * @param string $query Query
+ * @param array $parameters Parameters to bind
+ * @return
+ */
+ public function exec($query, $parameters = array())
+ {
+ return $this->fetch($query, $parameters);
+ }
+
+ /**
+ * Return number of affected rows in last query
+ *
+ * @param mixed $queryResult Result from query()
+ * @return int
+ */
+ abstract public function rowCount($queryResult);
+
+ /**
+ * Executes a query, using optional bound parameters.
+ *
+ * @param string $query Query
+ * @param array $parameters Parameters to bind array('idsite'=> 1)
+ *
+ * @return PDOStatement or false if failed
+ * @throws Piwik_Tracker_Db_Exception if an exception occurred
+ */
+ abstract public function query($query, $parameters = array());
+
+ /**
+ * Returns the last inserted ID in the DB
+ * Wrapper of PDO::lastInsertId()
+ *
+ * @return int
+ */
+ abstract public function lastInsertId();
+
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool True if error number matches; false otherwise
+ */
+ abstract public function isErrNo($e, $errno);
} \ No newline at end of file
diff --git a/core/Tracker/Db/Exception.php b/core/Tracker/Db/Exception.php
index 7b04400d6f..3958a1e008 100644
--- a/core/Tracker/Db/Exception.php
+++ b/core/Tracker/Db/Exception.php
@@ -15,5 +15,6 @@
* @package Piwik
* @subpackage Piwik_Tracker
*/
-class Piwik_Tracker_Db_Exception extends Exception {
+class Piwik_Tracker_Db_Exception extends Exception
+{
}
diff --git a/core/Tracker/Db/Mysqli.php b/core/Tracker/Db/Mysqli.php
index e9d4414253..193bdff203 100644
--- a/core/Tracker/Db/Mysqli.php
+++ b/core/Tracker/Db/Mysqli.php
@@ -1,7 +1,7 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
@@ -17,281 +17,260 @@
*/
class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
{
- protected $connection = null;
- protected $host;
- protected $port;
- protected $socket;
- protected $dbname;
- protected $username;
- protected $password;
- protected $charset;
+ protected $connection = null;
+ protected $host;
+ protected $port;
+ protected $socket;
+ protected $dbname;
+ protected $username;
+ protected $password;
+ protected $charset;
+
+ /**
+ * Builds the DB object
+ *
+ * @param array $dbInfo
+ * @param string $driverName
+ */
+ public function __construct($dbInfo, $driverName = 'mysql')
+ {
+ if (isset($dbInfo['unix_socket']) && $dbInfo['unix_socket'][0] == '/') {
+ $this->host = null;
+ $this->port = null;
+ $this->socket = $dbInfo['unix_socket'];
+ } else if ($dbInfo['port'][0] == '/') {
+ $this->host = null;
+ $this->port = null;
+ $this->socket = $dbInfo['port'];
+ } else {
+ $this->host = $dbInfo['host'];
+ $this->port = $dbInfo['port'];
+ $this->socket = null;
+ }
+ $this->dbname = $dbInfo['dbname'];
+ $this->username = $dbInfo['username'];
+ $this->password = $dbInfo['password'];
+ $this->charset = isset($dbInfo['charset']) ? $dbInfo['charset'] : null;
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ $this->connection = null;
+ }
+
+ /**
+ * Connects to the DB
+ *
+ * @throws Exception|Piwik_Tracker_Db_Exception if there was an error connecting the DB
+ */
+ public function connect()
+ {
+ if (self::$profiling) {
+ $timer = $this->initProfiler();
+ }
+
+ $this->connection = mysqli_connect($this->host, $this->username, $this->password, $this->dbname, $this->port, $this->socket);
+ if (!$this->connection || mysqli_connect_errno()) {
+ throw new Piwik_Tracker_Db_Exception("Connect failed: " . mysqli_connect_error());
+ }
+
+ if ($this->charset && !mysqli_set_charset($this->connection, $this->charset)) {
+ throw new Piwik_Tracker_Db_Exception("Set Charset failed: " . mysqli_error($this->connection));
+ }
- /**
- * Builds the DB object
- *
- * @param array $dbInfo
- * @param string $driverName
- */
- public function __construct( $dbInfo, $driverName = 'mysql')
- {
- if(isset($dbInfo['unix_socket']) && $dbInfo['unix_socket'][0] == '/')
- {
- $this->host = null;
- $this->port = null;
- $this->socket = $dbInfo['unix_socket'];
- }
- else if ($dbInfo['port'][0] == '/')
- {
- $this->host = null;
- $this->port = null;
- $this->socket = $dbInfo['port'];
- }
- else
- {
- $this->host = $dbInfo['host'];
- $this->port = $dbInfo['port'];
- $this->socket = null;
- }
- $this->dbname = $dbInfo['dbname'];
- $this->username = $dbInfo['username'];
- $this->password = $dbInfo['password'];
- $this->charset = isset($dbInfo['charset']) ? $dbInfo['charset'] : null;
- }
+ $this->password = '';
- /**
- * Destructor
- */
- public function __destruct()
- {
- $this->connection = null;
- }
+ if (self::$profiling) {
+ $this->recordQueryProfile('connect', $timer);
+ }
+ }
- /**
- * Connects to the DB
- *
- * @throws Exception|Piwik_Tracker_Db_Exception if there was an error connecting the DB
- */
- public function connect()
- {
- if(self::$profiling)
- {
- $timer = $this->initProfiler();
- }
-
- $this->connection = mysqli_connect($this->host, $this->username, $this->password, $this->dbname, $this->port, $this->socket);
- if(!$this->connection || mysqli_connect_errno())
- {
- throw new Piwik_Tracker_Db_Exception("Connect failed: " . mysqli_connect_error());
- }
+ /**
+ * Disconnects from the server
+ */
+ public function disconnect()
+ {
+ mysqli_close($this->connection);
+ $this->connection = null;
+ }
- if($this->charset && !mysqli_set_charset($this->connection, $this->charset))
- {
- throw new Piwik_Tracker_Db_Exception("Set Charset failed: " . mysqli_error($this->connection));
- }
+ /**
+ * Returns an array containing all the rows of a query result, using optional bound parameters.
+ *
+ * @see query()
+ *
+ * @param string $query Query
+ * @param array $parameters Parameters to bind
+ * @return array
+ * @throws Exception|Piwik_Tracker_Db_Exception if an exception occured
+ */
+ public function fetchAll($query, $parameters = array())
+ {
+ try {
+ if (self::$profiling) {
+ $timer = $this->initProfiler();
+ }
- $this->password = '';
-
- if(self::$profiling)
- {
- $this->recordQueryProfile('connect', $timer);
- }
- }
-
- /**
- * Disconnects from the server
- */
- public function disconnect()
- {
- mysqli_close($this->connection);
- $this->connection = null;
- }
-
- /**
- * Returns an array containing all the rows of a query result, using optional bound parameters.
- *
- * @see query()
- *
- * @param string $query Query
- * @param array $parameters Parameters to bind
- * @return array
- * @throws Exception|Piwik_Tracker_Db_Exception if an exception occured
- */
- public function fetchAll( $query, $parameters = array() )
- {
- try {
- if(self::$profiling)
- {
- $timer = $this->initProfiler();
- }
+ $rows = array();
+ $query = $this->prepare($query, $parameters);
+ $rs = mysqli_query($this->connection, $query);
+ if (is_bool($rs)) {
+ throw new Piwik_Tracker_Db_Exception('fetchAll() failed: ' . mysqli_error($this->connection) . ' : ' . $query);
+ }
- $rows = array();
- $query = $this->prepare( $query, $parameters );
- $rs = mysqli_query($this->connection, $query);
- if(is_bool($rs))
- {
- throw new Piwik_Tracker_Db_Exception('fetchAll() failed: ' . mysqli_error($this->connection) . ' : ' . $query);
- }
+ while ($row = mysqli_fetch_array($rs, MYSQLI_ASSOC)) {
+ $rows[] = $row;
+ }
+ mysqli_free_result($rs);
- while($row = mysqli_fetch_array($rs, MYSQLI_ASSOC))
- {
- $rows[] = $row;
- }
- mysqli_free_result($rs);
+ if (self::$profiling) {
+ $this->recordQueryProfile($query, $timer);
+ }
+ return $rows;
+ } catch (Exception $e) {
+ throw new Piwik_Tracker_Db_Exception("Error query: " . $e->getMessage());
+ }
+ }
- if(self::$profiling)
- {
- $this->recordQueryProfile($query, $timer);
- }
- return $rows;
- } catch (Exception $e) {
- throw new Piwik_Tracker_Db_Exception("Error query: ".$e->getMessage());
- }
- }
-
- /**
- * Returns the first row of a query result, using optional bound parameters.
- *
- * @see query()
- *
- * @param string $query Query
- * @param array $parameters Parameters to bind
- * @throws Piwik_Tracker_Db_Exception if an exception occurred
- */
- public function fetch( $query, $parameters = array() )
- {
- try {
- if(self::$profiling)
- {
- $timer = $this->initProfiler();
- }
+ /**
+ * Returns the first row of a query result, using optional bound parameters.
+ *
+ * @see query()
+ *
+ * @param string $query Query
+ * @param array $parameters Parameters to bind
+ * @throws Piwik_Tracker_Db_Exception if an exception occurred
+ */
+ public function fetch($query, $parameters = array())
+ {
+ try {
+ if (self::$profiling) {
+ $timer = $this->initProfiler();
+ }
- $query = $this->prepare( $query, $parameters );
- $rs = mysqli_query($this->connection, $query);
- if(is_bool($rs))
- {
- throw new Piwik_Tracker_Db_Exception('fetch() failed: ' . mysqli_error($this->connection) . ' : ' . $query);
- }
+ $query = $this->prepare($query, $parameters);
+ $rs = mysqli_query($this->connection, $query);
+ if (is_bool($rs)) {
+ throw new Piwik_Tracker_Db_Exception('fetch() failed: ' . mysqli_error($this->connection) . ' : ' . $query);
+ }
- $row = mysqli_fetch_array($rs, MYSQLI_ASSOC);
- mysqli_free_result($rs);
+ $row = mysqli_fetch_array($rs, MYSQLI_ASSOC);
+ mysqli_free_result($rs);
- if(self::$profiling)
- {
- $this->recordQueryProfile($query, $timer);
- }
- return $row;
- } catch (Exception $e) {
- throw new Piwik_Tracker_Db_Exception("Error query: ".$e->getMessage());
- }
- }
-
- /**
- * Executes a query, using optional bound parameters.
- *
- * @param string $query Query
- * @param array|string $parameters Parameters to bind array('idsite'=> 1)
- *
- * @return bool|resource false if failed
- * @throws Piwik_Tracker_Db_Exception if an exception occurred
- */
- public function query($query, $parameters = array())
- {
- if(is_null($this->connection))
- {
- return false;
- }
+ if (self::$profiling) {
+ $this->recordQueryProfile($query, $timer);
+ }
+ return $row;
+ } catch (Exception $e) {
+ throw new Piwik_Tracker_Db_Exception("Error query: " . $e->getMessage());
+ }
+ }
- try {
- if(self::$profiling)
- {
- $timer = $this->initProfiler();
- }
-
- $query = $this->prepare( $query, $parameters );
- $result = mysqli_query($this->connection, $query);
- if(!is_bool($result))
- {
- mysqli_free_result($result);
- }
-
- if(self::$profiling)
- {
- $this->recordQueryProfile($query, $timer);
- }
- return $result;
- } catch (Exception $e) {
- throw new Piwik_Tracker_Db_Exception("Error query: ".$e->getMessage() . "
+ /**
+ * Executes a query, using optional bound parameters.
+ *
+ * @param string $query Query
+ * @param array|string $parameters Parameters to bind array('idsite'=> 1)
+ *
+ * @return bool|resource false if failed
+ * @throws Piwik_Tracker_Db_Exception if an exception occurred
+ */
+ public function query($query, $parameters = array())
+ {
+ if (is_null($this->connection)) {
+ return false;
+ }
+
+ try {
+ if (self::$profiling) {
+ $timer = $this->initProfiler();
+ }
+
+ $query = $this->prepare($query, $parameters);
+ $result = mysqli_query($this->connection, $query);
+ if (!is_bool($result)) {
+ mysqli_free_result($result);
+ }
+
+ if (self::$profiling) {
+ $this->recordQueryProfile($query, $timer);
+ }
+ return $result;
+ } catch (Exception $e) {
+ throw new Piwik_Tracker_Db_Exception("Error query: " . $e->getMessage() . "
In query: $query
- Parameters: ".var_export($parameters, true));
- }
- }
+ Parameters: " . var_export($parameters, true));
+ }
+ }
+
+ /**
+ * Returns the last inserted ID in the DB
+ *
+ * @return int
+ */
+ public function lastInsertId()
+ {
+ return mysqli_insert_id($this->connection);
+ }
+
+ /**
+ * Input is a prepared SQL statement and parameters
+ * Returns the SQL statement
+ *
+ * @param string $query
+ * @param array $parameters
+ * @return string
+ */
+ private function prepare($query, $parameters)
+ {
+ if (!$parameters) {
+ $parameters = array();
+ } else if (!is_array($parameters)) {
+ $parameters = array($parameters);
+ }
+
+ $this->paramNb = 0;
+ $this->params = & $parameters;
+ $query = preg_replace_callback('/\?/', array($this, 'replaceParam'), $query);
- /**
- * Returns the last inserted ID in the DB
- *
- * @return int
- */
- public function lastInsertId()
- {
- return mysqli_insert_id($this->connection);
- }
+ return $query;
+ }
- /**
- * Input is a prepared SQL statement and parameters
- * Returns the SQL statement
- *
- * @param string $query
- * @param array $parameters
- * @return string
- */
- private function prepare($query, $parameters) {
- if(!$parameters)
- {
- $parameters = array();
- }
- else if(!is_array($parameters))
- {
- $parameters = array( $parameters );
- }
+ public function replaceParam($match)
+ {
+ $param = & $this->params[$this->paramNb];
+ $this->paramNb++;
- $this->paramNb = 0;
- $this->params = &$parameters;
- $query = preg_replace_callback('/\?/', array($this, 'replaceParam'), $query);
-
- return $query;
- }
-
- public function replaceParam($match) {
- $param = &$this->params[$this->paramNb];
- $this->paramNb++;
-
- if ($param === null) {
- return 'NULL';
- } else {
- return "'".addslashes($param)."'";
- }
- }
+ if ($param === null) {
+ return 'NULL';
+ } else {
+ return "'" . addslashes($param) . "'";
+ }
+ }
- /**
- * Test error number
- *
- * @param Exception $e
- * @param string $errno
- * @return bool
- */
- public function isErrNo($e, $errno)
- {
- return mysqli_errno($this->_connection) == $errno;
- }
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ return mysqli_errno($this->_connection) == $errno;
+ }
- /**
- * Return number of affected rows in last query
- *
- * @param mixed $queryResult Result from query()
- * @return int
- */
- public function rowCount($queryResult)
- {
- return mysqli_affected_rows($this->connection);
- }
+ /**
+ * Return number of affected rows in last query
+ *
+ * @param mixed $queryResult Result from query()
+ * @return int
+ */
+ public function rowCount($queryResult)
+ {
+ return mysqli_affected_rows($this->connection);
+ }
}
diff --git a/core/Tracker/Db/Pdo/Mysql.php b/core/Tracker/Db/Pdo/Mysql.php
index 82a390ec28..23f87d4d85 100644
--- a/core/Tracker/Db/Pdo/Mysql.php
+++ b/core/Tracker/Db/Pdo/Mysql.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -17,207 +17,192 @@
*/
class Piwik_Tracker_Db_Pdo_Mysql extends Piwik_Tracker_Db
{
- protected $connection = null;
- protected $dsn;
- protected $username;
- protected $password;
- protected $charset;
-
- /**
- * Builds the DB object
- *
- * @param array $dbInfo
- * @param string $driverName
- */
- public function __construct( $dbInfo, $driverName = 'mysql')
- {
- if(isset($dbInfo['unix_socket']) && $dbInfo['unix_socket'][0] == '/')
- {
- $this->dsn = $driverName . ':dbname=' . $dbInfo['dbname'] . ';unix_socket=' . $dbInfo['unix_socket'];
- }
- else if ($dbInfo['port'][0] == '/')
- {
- $this->dsn = $driverName . ':dbname=' . $dbInfo['dbname'] . ';unix_socket=' . $dbInfo['port'];
- }
- else
- {
- $this->dsn = $driverName . ':dbname=' . $dbInfo['dbname'] . ';host=' . $dbInfo['host'] . ';port=' . $dbInfo['port'];
- }
- $this->username = $dbInfo['username'];
- $this->password = $dbInfo['password'];
- $this->charset = isset($dbInfo['charset']) ? $dbInfo['charset'] : null;
- }
-
- public function __destruct()
- {
- $this->connection = null;
- }
-
- /**
- * Connects to the DB
- *
- * @throws Exception if there was an error connecting the DB
- */
- public function connect()
- {
- if(self::$profiling)
- {
- $timer = $this->initProfiler();
- }
-
- $this->connection = @new PDO($this->dsn, $this->username, $this->password, $config = array());
- $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
- // 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
- $this->password = '';
-
- /*
- * Lazy initialization via MYSQL_ATTR_INIT_COMMAND depends
- * on mysqlnd support, PHP version, and OS.
- * see ZF-7428 and http://bugs.php.net/bug.php?id=47224
- */
- if(!empty($this->charset))
- {
- $sql = "SET NAMES '" . $this->charset . "'";
- $this->connection->exec($sql);
- }
-
- if(self::$profiling)
- {
- $this->recordQueryProfile('connect', $timer);
- }
- }
-
- /**
- * Disconnects from the server
- */
- public function disconnect()
- {
- $this->connection = null;
- }
-
- /**
- * Returns an array containing all the rows of a query result, using optional bound parameters.
- *
- * @param string $query Query
- * @param array $parameters Parameters to bind
- * @return array|bool
- * @see query()
- * @throws Exception|Piwik_Tracker_Db_Exception if an exception occurred
- */
- public function fetchAll( $query, $parameters = array() )
- {
- try {
- $sth = $this->query( $query, $parameters );
- if($sth === false)
- {
- return false;
- }
- return $sth->fetchAll(PDO::FETCH_ASSOC);
- } catch (PDOException $e) {
- throw new Piwik_Tracker_Db_Exception("Error query: ".$e->getMessage());
- }
- }
-
- /**
- * Returns the first row of a query result, using optional bound parameters.
- *
- * @param string $query Query
- * @param array $parameters Parameters to bind
- * @return bool|mixed
- * @see query()
- * @throws Exception|Piwik_Tracker_Db_Exception if an exception occurred
- */
- public function fetch( $query, $parameters = array() )
- {
- try {
- $sth = $this->query( $query, $parameters );
- if($sth === false)
- {
- return false;
- }
- return $sth->fetch(PDO::FETCH_ASSOC);
- } catch (PDOException $e) {
- throw new Piwik_Tracker_Db_Exception("Error query: ".$e->getMessage());
- }
- }
-
- /**
- * Executes a query, using optional bound parameters.
- *
- * @param string $query Query
- * @param array|string $parameters Parameters to bind array('idsite'=> 1)
- * @return PDOStatement|bool PDOStatement or false if failed
- * @throws Piwik_Tracker_Db_Exception if an exception occured
- */
- public function query($query, $parameters = array())
- {
- if(is_null($this->connection))
- {
- return false;
- }
-
- try {
- if(self::$profiling)
- {
- $timer = $this->initProfiler();
- }
-
- if(!is_array($parameters))
- {
- $parameters = array( $parameters );
- }
- $sth = $this->connection->prepare($query);
- $sth->execute( $parameters );
-
- if(self::$profiling)
- {
- $this->recordQueryProfile($query, $timer);
- }
- return $sth;
- } catch (PDOException $e) {
- throw new Piwik_Tracker_Db_Exception("Error query: ".$e->getMessage() . "
+ protected $connection = null;
+ protected $dsn;
+ protected $username;
+ protected $password;
+ protected $charset;
+
+ /**
+ * Builds the DB object
+ *
+ * @param array $dbInfo
+ * @param string $driverName
+ */
+ public function __construct($dbInfo, $driverName = 'mysql')
+ {
+ if (isset($dbInfo['unix_socket']) && $dbInfo['unix_socket'][0] == '/') {
+ $this->dsn = $driverName . ':dbname=' . $dbInfo['dbname'] . ';unix_socket=' . $dbInfo['unix_socket'];
+ } else if ($dbInfo['port'][0] == '/') {
+ $this->dsn = $driverName . ':dbname=' . $dbInfo['dbname'] . ';unix_socket=' . $dbInfo['port'];
+ } else {
+ $this->dsn = $driverName . ':dbname=' . $dbInfo['dbname'] . ';host=' . $dbInfo['host'] . ';port=' . $dbInfo['port'];
+ }
+ $this->username = $dbInfo['username'];
+ $this->password = $dbInfo['password'];
+ $this->charset = isset($dbInfo['charset']) ? $dbInfo['charset'] : null;
+ }
+
+ public function __destruct()
+ {
+ $this->connection = null;
+ }
+
+ /**
+ * Connects to the DB
+ *
+ * @throws Exception if there was an error connecting the DB
+ */
+ public function connect()
+ {
+ if (self::$profiling) {
+ $timer = $this->initProfiler();
+ }
+
+ $this->connection = @new PDO($this->dsn, $this->username, $this->password, $config = array());
+ $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ // 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
+ $this->password = '';
+
+ /*
+ * Lazy initialization via MYSQL_ATTR_INIT_COMMAND depends
+ * on mysqlnd support, PHP version, and OS.
+ * see ZF-7428 and http://bugs.php.net/bug.php?id=47224
+ */
+ if (!empty($this->charset)) {
+ $sql = "SET NAMES '" . $this->charset . "'";
+ $this->connection->exec($sql);
+ }
+
+ if (self::$profiling) {
+ $this->recordQueryProfile('connect', $timer);
+ }
+ }
+
+ /**
+ * Disconnects from the server
+ */
+ public function disconnect()
+ {
+ $this->connection = null;
+ }
+
+ /**
+ * Returns an array containing all the rows of a query result, using optional bound parameters.
+ *
+ * @param string $query Query
+ * @param array $parameters Parameters to bind
+ * @return array|bool
+ * @see query()
+ * @throws Exception|Piwik_Tracker_Db_Exception if an exception occurred
+ */
+ public function fetchAll($query, $parameters = array())
+ {
+ try {
+ $sth = $this->query($query, $parameters);
+ if ($sth === false) {
+ return false;
+ }
+ return $sth->fetchAll(PDO::FETCH_ASSOC);
+ } catch (PDOException $e) {
+ throw new Piwik_Tracker_Db_Exception("Error query: " . $e->getMessage());
+ }
+ }
+
+ /**
+ * Returns the first row of a query result, using optional bound parameters.
+ *
+ * @param string $query Query
+ * @param array $parameters Parameters to bind
+ * @return bool|mixed
+ * @see query()
+ * @throws Exception|Piwik_Tracker_Db_Exception if an exception occurred
+ */
+ public function fetch($query, $parameters = array())
+ {
+ try {
+ $sth = $this->query($query, $parameters);
+ if ($sth === false) {
+ return false;
+ }
+ return $sth->fetch(PDO::FETCH_ASSOC);
+ } catch (PDOException $e) {
+ throw new Piwik_Tracker_Db_Exception("Error query: " . $e->getMessage());
+ }
+ }
+
+ /**
+ * Executes a query, using optional bound parameters.
+ *
+ * @param string $query Query
+ * @param array|string $parameters Parameters to bind array('idsite'=> 1)
+ * @return PDOStatement|bool PDOStatement or false if failed
+ * @throws Piwik_Tracker_Db_Exception if an exception occured
+ */
+ public function query($query, $parameters = array())
+ {
+ if (is_null($this->connection)) {
+ return false;
+ }
+
+ try {
+ if (self::$profiling) {
+ $timer = $this->initProfiler();
+ }
+
+ if (!is_array($parameters)) {
+ $parameters = array($parameters);
+ }
+ $sth = $this->connection->prepare($query);
+ $sth->execute($parameters);
+
+ if (self::$profiling) {
+ $this->recordQueryProfile($query, $timer);
+ }
+ return $sth;
+ } catch (PDOException $e) {
+ throw new Piwik_Tracker_Db_Exception("Error query: " . $e->getMessage() . "
In query: $query
- Parameters: ".var_export($parameters, true));
- }
- }
-
- /**
- * Returns the last inserted ID in the DB
- * Wrapper of PDO::lastInsertId()
- *
- * @return int
- */
- public function lastInsertId()
- {
- return $this->connection->lastInsertId();
- }
-
- /**
- * Test error number
- *
- * @param Exception $e
- * @param string $errno
- * @return bool
- */
- public function isErrNo($e, $errno)
- {
- if(preg_match('/([0-9]{4})/', $e->getMessage(), $match))
- {
- return $match[1] == $errno;
- }
- return false;
- }
-
- /**
- * Return number of affected rows in last query
- *
- * @param mixed $queryResult Result from query()
- * @return int
- */
- public function rowCount($queryResult)
- {
- return $queryResult->rowCount();
- }
+ Parameters: " . var_export($parameters, true));
+ }
+ }
+
+ /**
+ * Returns the last inserted ID in the DB
+ * Wrapper of PDO::lastInsertId()
+ *
+ * @return int
+ */
+ public function lastInsertId()
+ {
+ return $this->connection->lastInsertId();
+ }
+
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ if (preg_match('/([0-9]{4})/', $e->getMessage(), $match)) {
+ return $match[1] == $errno;
+ }
+ return false;
+ }
+
+ /**
+ * Return number of affected rows in last query
+ *
+ * @param mixed $queryResult Result from query()
+ * @return int
+ */
+ public function rowCount($queryResult)
+ {
+ return $queryResult->rowCount();
+ }
}
diff --git a/core/Tracker/Db/Pdo/Pgsql.php b/core/Tracker/Db/Pdo/Pgsql.php
index 22a020c359..c03246b348 100644
--- a/core/Tracker/Db/Pdo/Pgsql.php
+++ b/core/Tracker/Db/Pdo/Pgsql.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -17,104 +17,100 @@
*/
class Piwik_Tracker_Db_Pdo_Pgsql extends Piwik_Tracker_Db_Pdo_Mysql
{
- /**
- * Builds the DB object
- *
- * @param array $dbInfo
- * @param string $driverName
- */
- public function __construct( $dbInfo, $driverName = 'pgsql')
- {
- parent::__construct( $dbInfo, $driverName );
- }
+ /**
+ * Builds the DB object
+ *
+ * @param array $dbInfo
+ * @param string $driverName
+ */
+ public function __construct($dbInfo, $driverName = 'pgsql')
+ {
+ parent::__construct($dbInfo, $driverName);
+ }
+
+ /**
+ * Connects to the DB
+ *
+ * @throws Exception if there was an error connecting the DB
+ */
+ public function connect()
+ {
+ if (self::$profiling) {
+ $timer = $this->initProfiler();
+ }
+
- /**
- * Connects to the DB
- *
- * @throws Exception if there was an error connecting the DB
- */
- public function connect()
- {
- if(self::$profiling)
- {
- $timer = $this->initProfiler();
- }
+ $this->connection = new PDO($this->dsn, $this->username, $this->password, $config = array());
+ $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ // 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
+ $this->password = '';
-
- $this->connection = new PDO($this->dsn, $this->username, $this->password, $config = array());
- $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
- // 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
- $this->password = '';
+ if (!empty($this->charset)) {
+ $sql = "SET NAMES '" . $this->charset . "'";
+ $this->connection->exec($sql);
+ }
- if(!empty($this->charset))
- {
- $sql = "SET NAMES '" . $this->charset . "'";
- $this->connection->exec($sql);
- }
+ if (self::$profiling) {
+ $this->recordQueryProfile('connect', $timer);
+ }
+ }
- if(self::$profiling)
- {
- $this->recordQueryProfile('connect', $timer);
- }
- }
-
- /**
- * Test error number
- *
- * @param Exception $e
- * @param string $errno
- * @return bool
- */
- public function isErrNo($e, $errno)
- {
- // map MySQL driver-specific error codes to PostgreSQL SQLSTATE
- $map = array(
- // MySQL: Unknown database '%s'
- // PostgreSQL: database "%s" does not exist
- '1049' => '08006',
+ /**
+ * Test error number
+ *
+ * @param Exception $e
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($e, $errno)
+ {
+ // map MySQL driver-specific error codes to PostgreSQL SQLSTATE
+ $map = array(
+ // MySQL: Unknown database '%s'
+ // PostgreSQL: database "%s" does not exist
+ '1049' => '08006',
- // MySQL: Table '%s' already exists
- // PostgreSQL: relation "%s" already exists
- '1050' => '42P07',
+ // MySQL: Table '%s' already exists
+ // PostgreSQL: relation "%s" already exists
+ '1050' => '42P07',
- // MySQL: Unknown column '%s' in '%s'
- // PostgreSQL: column "%s" does not exist
- '1054' => '42703',
+ // MySQL: Unknown column '%s' in '%s'
+ // PostgreSQL: column "%s" does not exist
+ '1054' => '42703',
- // MySQL: Duplicate column name '%s'
- // PostgreSQL: column "%s" of relation "%s" already exists
- '1060' => '42701',
+ // MySQL: Duplicate column name '%s'
+ // PostgreSQL: column "%s" of relation "%s" already exists
+ '1060' => '42701',
- // MySQL: Duplicate entry '%s' for key '%s'
- // PostgreSQL: duplicate key violates unique constraint
- '1062' => '23505',
+ // MySQL: Duplicate entry '%s' for key '%s'
+ // PostgreSQL: duplicate key violates unique constraint
+ '1062' => '23505',
- // MySQL: Can't DROP '%s'; check that column/key exists
- // PostgreSQL: index "%s" does not exist
- '1091' => '42704',
+ // MySQL: Can't DROP '%s'; check that column/key exists
+ // PostgreSQL: index "%s" does not exist
+ '1091' => '42704',
- // MySQL: Table '%s.%s' doesn't exist
- // PostgreSQL: relation "%s" does not exist
- '1146' => '42P01',
- );
+ // MySQL: Table '%s.%s' doesn't exist
+ // PostgreSQL: relation "%s" does not exist
+ '1146' => '42P01',
+ );
- if(preg_match('/([0-9]{2}[0-9P][0-9]{2})/', $e->getMessage(), $match))
- {
- return $match[1] == $map[$errno];
- }
- return false;
- }
+ if (preg_match('/([0-9]{2}[0-9P][0-9]{2})/', $e->getMessage(), $match)) {
+ return $match[1] == $map[$errno];
+ }
+ return false;
+ }
- /**
- * Return number of affected rows in last query
- *
- * @param mixed $queryResult Result from query()
- * @return int
- */
- public function rowCount($queryResult)
- {
- return $queryResult->rowCount();
- }
+ /**
+ * Return number of affected rows in last query
+ *
+ * @param mixed $queryResult Result from query()
+ * @return int
+ */
+ public function rowCount($queryResult)
+ {
+ return $queryResult->rowCount();
+ }
}
diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php
index 9e77ab75e9..a00c49ad3f 100644
--- a/core/Tracker/GoalManager.php
+++ b/core/Tracker/GoalManager.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -13,860 +13,799 @@
* @package Piwik
* @subpackage Piwik_Tracker
*/
-class Piwik_Tracker_GoalManager
+class Piwik_Tracker_GoalManager
{
- // log_visit.visit_goal_buyer
- const TYPE_BUYER_NONE = 0;
- const TYPE_BUYER_ORDERED = 1;
- const TYPE_BUYER_OPEN_CART = 2;
- const TYPE_BUYER_ORDERED_AND_OPEN_CART = 3;
-
- // log_conversion.idorder is NULLable, but not log_conversion_item which defaults to zero for carts
- const ITEM_IDORDER_ABANDONED_CART = 0;
-
- // log_conversion.idgoal special values
- const IDGOAL_CART = -1;
- const IDGOAL_ORDER = 0;
-
- const REVENUE_PRECISION = 2;
-
- const MAXIMUM_PRODUCT_CATEGORIES = 5;
- public $idGoal;
- public $requestIsEcommerce;
- public $isGoalAnOrder;
-
- /**
- * @var Piwik_Tracker_Action
- */
- protected $action = null;
- protected $convertedGoals = array();
- protected $isThereExistingCartInVisit = false;
- protected $request;
- protected $orderId;
-
- function init($request)
- {
- $this->request = $request;
- $this->orderId = Piwik_Common::getRequestVar('ec_id', false, 'string', $this->request);
- $this->isGoalAnOrder = !empty($this->orderId);
- $this->idGoal = Piwik_Common::getRequestVar('idgoal', -1, 'int', $this->request);
- $this->requestIsEcommerce = ($this->idGoal == 0);
- }
-
- function getBuyerType($existingType = Piwik_Tracker_GoalManager::TYPE_BUYER_NONE)
- {
- // Was there a Cart for this visit prior to the order?
- $this->isThereExistingCartInVisit = in_array($existingType,
- array( Piwik_Tracker_GoalManager::TYPE_BUYER_OPEN_CART,
- Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED_AND_OPEN_CART));
-
- if(!$this->requestIsEcommerce)
- {
- return $existingType;
- }
- if($this->isGoalAnOrder)
- {
- return self::TYPE_BUYER_ORDERED;
- }
- // request is Add to Cart
- if($existingType == self::TYPE_BUYER_ORDERED
- || $existingType == self::TYPE_BUYER_ORDERED_AND_OPEN_CART)
- {
- return self::TYPE_BUYER_ORDERED_AND_OPEN_CART;
- }
- return self::TYPE_BUYER_OPEN_CART;
- }
-
- static public function getGoalDefinitions( $idSite )
- {
- $websiteAttributes = Piwik_Tracker_Cache::getCacheWebsiteAttributes( $idSite );
- if(isset($websiteAttributes['goals']))
- {
- return $websiteAttributes['goals'];
- }
- return array();
- }
-
- static public function getGoalDefinition( $idSite, $idGoal )
- {
- $goals = self::getGoalDefinitions( $idSite );
- foreach($goals as $goal)
- {
- if($goal['idgoal'] == $idGoal)
- {
- return $goal;
- }
- }
- throw new Exception('Goal not found');
- }
-
- static public function getGoalIds( $idSite )
- {
- $goals = self::getGoalDefinitions( $idSite );
- $goalIds = array();
- foreach($goals as $goal)
- {
- $goalIds[] = $goal['idgoal'];
- }
- return $goalIds;
- }
-
- /**
- * Look at the URL or Page Title and sees if it matches any existing Goal definition
- *
- * @param int $idSite
- * @param Piwik_Tracker_Action $action
- * @throws Exception
- * @return int Number of goals matched
- */
- function detectGoalsMatchingUrl($idSite, $action)
- {
- if(!Piwik_Common::isGoalPluginEnabled())
- {
- return false;
- }
-
- $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( ($actionType == Piwik_Tracker_Action::TYPE_ACTION_URL && $attribute != 'url' && $attribute != 'title')
- || ($actionType == Piwik_Tracker_Action::TYPE_DOWNLOAD && $attribute != 'file')
- || ($actionType == Piwik_Tracker_Action::TYPE_OUTLINK && $attribute != 'external_website')
- || ($attribute == 'manually')
- )
- {
- continue;
- }
-
- $url = $decodedActionUrl;
- // Matching on Page Title
- if($attribute == 'title')
- {
- $url = $action->getActionName();
- }
- $pattern_type = $goal['pattern_type'];
-
- switch($pattern_type)
- {
- case 'regex':
- $pattern = $goal['pattern'];
- if(strpos($pattern, '/') !== false
- && strpos($pattern, '\\/') === false)
- {
- $pattern = str_replace('/', '\\/', $pattern);
- }
- $pattern = '/' . $pattern . '/';
- if(!$goal['case_sensitive'])
- {
- $pattern .= 'i';
- }
- $match = (@preg_match($pattern, $url) == 1);
- break;
- case 'contains':
- if($goal['case_sensitive'])
- {
- $matched = strpos($url, $goal['pattern']);
- }
- else
- {
- $matched = stripos($url, $goal['pattern']);
- }
- $match = ($matched !== false);
- break;
- case 'exact':
- if($goal['case_sensitive'])
- {
- $matched = strcmp($goal['pattern'], $url);
- }
- else
- {
- $matched = strcasecmp($goal['pattern'], $url);
- }
- $match = ($matched == 0);
- break;
- default:
- throw new Exception(Piwik_TranslateException('General_ExceptionInvalidGoalPattern', array($pattern_type)));
- break;
- }
- if($match)
- {
- $goal['url'] = $decodedActionUrl;
- $this->convertedGoals[] = $goal;
- }
- }
- return count($this->convertedGoals) > 0;
- }
-
- function detectGoalId($idSite)
- {
- if(!Piwik_Common::isGoalPluginEnabled())
- {
- return false;
- }
- $goals = $this->getGoalDefinitions($idSite);
- if(!isset($goals[$this->idGoal]))
- {
- return false;
- }
- $goal = $goals[$this->idGoal];
-
- $url = Piwik_Common::getRequestVar( 'url', '', 'string', $this->request);
- $goal['url'] = Piwik_Tracker_Action::excludeQueryParametersFromUrl($url, $idSite);
- $goal['revenue'] = $this->getRevenue(Piwik_Common::getRequestVar('revenue', $goal['revenue'], 'float', $this->request));
- $this->convertedGoals[] = $goal;
- return true;
- }
-
- /**
- * Records one or several goals matched in this request.
- * @param int $idSite
- * @param array $visitorInformation
- * @param array $visitCustomVariables
- * @param string $action
- * @param $referrerTimestamp
- * @param string $referrerUrl
- * @param string $referrerCampaignName
- * @param string $referrerCampaignKeyword
- */
- public function recordGoals($idSite, $visitorInformation, $visitCustomVariables, $action, $referrerTimestamp, $referrerUrl, $referrerCampaignName, $referrerCampaignKeyword, $browserLanguage)
- {
- $location_country = isset($visitorInformation['location_country'])
- ? $visitorInformation['location_country']
- : Piwik_Common::getCountry(
- $browserLanguage,
- $enableLanguageToCountryGuess = Piwik_Config::getInstance()->Tracker['enable_language_to_country_guess'],
- $visitorInformation['location_ip']
- );
-
- $goal = array(
- 'idvisit' => $visitorInformation['idvisit'],
- 'idsite' => $idSite,
- 'idvisitor' => $visitorInformation['idvisitor'],
- 'server_time' => Piwik_Tracker::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time']),
- 'location_country' => $location_country,
- 'visitor_returning' => $visitorInformation['visitor_returning'],
- 'visitor_days_since_first' => $visitorInformation['visitor_days_since_first'],
- 'visitor_days_since_order' => $visitorInformation['visitor_days_since_order'],
- 'visitor_count_visits' => $visitorInformation['visitor_count_visits'],
- );
-
- $extraLocationCols = array('location_region', 'location_city', 'location_latitude', 'location_longitude');
- foreach ($extraLocationCols as $col)
- {
- if (isset($visitorInformation[$col]))
- {
- $goal[$col] = $visitorInformation[$col];
- }
- }
-
- // Copy Custom Variables from Visit row to the Goal conversion
- for($i=1; $i<=Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++)
- {
- if(isset($visitorInformation['custom_var_k'.$i])
- && strlen($visitorInformation['custom_var_k'.$i]))
- {
- $goal['custom_var_k'.$i] = $visitorInformation['custom_var_k'.$i];
- }
- if(isset($visitorInformation['custom_var_v'.$i])
- && strlen($visitorInformation['custom_var_v'.$i]))
- {
- $goal['custom_var_v'.$i] = $visitorInformation['custom_var_v'.$i];
- }
- }
- // Otherwise, set the Custom Variables found in the cookie sent with this request
- $goal += $visitCustomVariables;
-
- // Attributing the correct Referrer to this conversion.
- // Priority order is as follows:
- // 0) In some cases, the campaign is not passed from the JS so we look it up from the current visit
- // 1) Campaign name/kwd parsed in the JS
- // 2) Referrer URL stored in the _ref cookie
- // 3) If no info from the cookie, attribute to the current visit referrer
-
- // 3) Default values: current referrer
+ // log_visit.visit_goal_buyer
+ const TYPE_BUYER_NONE = 0;
+ const TYPE_BUYER_ORDERED = 1;
+ const TYPE_BUYER_OPEN_CART = 2;
+ const TYPE_BUYER_ORDERED_AND_OPEN_CART = 3;
+
+ // log_conversion.idorder is NULLable, but not log_conversion_item which defaults to zero for carts
+ const ITEM_IDORDER_ABANDONED_CART = 0;
+
+ // log_conversion.idgoal special values
+ const IDGOAL_CART = -1;
+ const IDGOAL_ORDER = 0;
+
+ const REVENUE_PRECISION = 2;
+
+ const MAXIMUM_PRODUCT_CATEGORIES = 5;
+ public $idGoal;
+ public $requestIsEcommerce;
+ public $isGoalAnOrder;
+
+ /**
+ * @var Piwik_Tracker_Action
+ */
+ protected $action = null;
+ protected $convertedGoals = array();
+ protected $isThereExistingCartInVisit = false;
+ protected $request;
+ protected $orderId;
+
+ function init($request)
+ {
+ $this->request = $request;
+ $this->orderId = Piwik_Common::getRequestVar('ec_id', false, 'string', $this->request);
+ $this->isGoalAnOrder = !empty($this->orderId);
+ $this->idGoal = Piwik_Common::getRequestVar('idgoal', -1, 'int', $this->request);
+ $this->requestIsEcommerce = ($this->idGoal == 0);
+ }
+
+ function getBuyerType($existingType = Piwik_Tracker_GoalManager::TYPE_BUYER_NONE)
+ {
+ // Was there a Cart for this visit prior to the order?
+ $this->isThereExistingCartInVisit = in_array($existingType,
+ array(Piwik_Tracker_GoalManager::TYPE_BUYER_OPEN_CART,
+ Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED_AND_OPEN_CART));
+
+ if (!$this->requestIsEcommerce) {
+ return $existingType;
+ }
+ if ($this->isGoalAnOrder) {
+ return self::TYPE_BUYER_ORDERED;
+ }
+ // request is Add to Cart
+ if ($existingType == self::TYPE_BUYER_ORDERED
+ || $existingType == self::TYPE_BUYER_ORDERED_AND_OPEN_CART
+ ) {
+ return self::TYPE_BUYER_ORDERED_AND_OPEN_CART;
+ }
+ return self::TYPE_BUYER_OPEN_CART;
+ }
+
+ static public function getGoalDefinitions($idSite)
+ {
+ $websiteAttributes = Piwik_Tracker_Cache::getCacheWebsiteAttributes($idSite);
+ if (isset($websiteAttributes['goals'])) {
+ return $websiteAttributes['goals'];
+ }
+ return array();
+ }
+
+ static public function getGoalDefinition($idSite, $idGoal)
+ {
+ $goals = self::getGoalDefinitions($idSite);
+ foreach ($goals as $goal) {
+ if ($goal['idgoal'] == $idGoal) {
+ return $goal;
+ }
+ }
+ throw new Exception('Goal not found');
+ }
+
+ static public function getGoalIds($idSite)
+ {
+ $goals = self::getGoalDefinitions($idSite);
+ $goalIds = array();
+ foreach ($goals as $goal) {
+ $goalIds[] = $goal['idgoal'];
+ }
+ return $goalIds;
+ }
+
+ /**
+ * Look at the URL or Page Title and sees if it matches any existing Goal definition
+ *
+ * @param int $idSite
+ * @param Piwik_Tracker_Action $action
+ * @throws Exception
+ * @return int Number of goals matched
+ */
+ function detectGoalsMatchingUrl($idSite, $action)
+ {
+ if (!Piwik_Common::isGoalPluginEnabled()) {
+ return false;
+ }
+
+ $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 (($actionType == Piwik_Tracker_Action::TYPE_ACTION_URL && $attribute != 'url' && $attribute != 'title')
+ || ($actionType == Piwik_Tracker_Action::TYPE_DOWNLOAD && $attribute != 'file')
+ || ($actionType == Piwik_Tracker_Action::TYPE_OUTLINK && $attribute != 'external_website')
+ || ($attribute == 'manually')
+ ) {
+ continue;
+ }
+
+ $url = $decodedActionUrl;
+ // Matching on Page Title
+ if ($attribute == 'title') {
+ $url = $action->getActionName();
+ }
+ $pattern_type = $goal['pattern_type'];
+
+ switch ($pattern_type) {
+ case 'regex':
+ $pattern = $goal['pattern'];
+ if (strpos($pattern, '/') !== false
+ && strpos($pattern, '\\/') === false
+ ) {
+ $pattern = str_replace('/', '\\/', $pattern);
+ }
+ $pattern = '/' . $pattern . '/';
+ if (!$goal['case_sensitive']) {
+ $pattern .= 'i';
+ }
+ $match = (@preg_match($pattern, $url) == 1);
+ break;
+ case 'contains':
+ if ($goal['case_sensitive']) {
+ $matched = strpos($url, $goal['pattern']);
+ } else {
+ $matched = stripos($url, $goal['pattern']);
+ }
+ $match = ($matched !== false);
+ break;
+ case 'exact':
+ if ($goal['case_sensitive']) {
+ $matched = strcmp($goal['pattern'], $url);
+ } else {
+ $matched = strcasecmp($goal['pattern'], $url);
+ }
+ $match = ($matched == 0);
+ break;
+ default:
+ throw new Exception(Piwik_TranslateException('General_ExceptionInvalidGoalPattern', array($pattern_type)));
+ break;
+ }
+ if ($match) {
+ $goal['url'] = $decodedActionUrl;
+ $this->convertedGoals[] = $goal;
+ }
+ }
+ return count($this->convertedGoals) > 0;
+ }
+
+ function detectGoalId($idSite)
+ {
+ if (!Piwik_Common::isGoalPluginEnabled()) {
+ return false;
+ }
+ $goals = $this->getGoalDefinitions($idSite);
+ if (!isset($goals[$this->idGoal])) {
+ return false;
+ }
+ $goal = $goals[$this->idGoal];
+
+ $url = Piwik_Common::getRequestVar('url', '', 'string', $this->request);
+ $goal['url'] = Piwik_Tracker_Action::excludeQueryParametersFromUrl($url, $idSite);
+ $goal['revenue'] = $this->getRevenue(Piwik_Common::getRequestVar('revenue', $goal['revenue'], 'float', $this->request));
+ $this->convertedGoals[] = $goal;
+ return true;
+ }
+
+ /**
+ * Records one or several goals matched in this request.
+ * @param int $idSite
+ * @param array $visitorInformation
+ * @param array $visitCustomVariables
+ * @param string $action
+ * @param $referrerTimestamp
+ * @param string $referrerUrl
+ * @param string $referrerCampaignName
+ * @param string $referrerCampaignKeyword
+ */
+ public function recordGoals($idSite, $visitorInformation, $visitCustomVariables, $action, $referrerTimestamp, $referrerUrl, $referrerCampaignName, $referrerCampaignKeyword, $browserLanguage)
+ {
+ $location_country = isset($visitorInformation['location_country'])
+ ? $visitorInformation['location_country']
+ : Piwik_Common::getCountry(
+ $browserLanguage,
+ $enableLanguageToCountryGuess = Piwik_Config::getInstance()->Tracker['enable_language_to_country_guess'],
+ $visitorInformation['location_ip']
+ );
+
+ $goal = array(
+ 'idvisit' => $visitorInformation['idvisit'],
+ 'idsite' => $idSite,
+ 'idvisitor' => $visitorInformation['idvisitor'],
+ 'server_time' => Piwik_Tracker::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time']),
+ 'location_country' => $location_country,
+ 'visitor_returning' => $visitorInformation['visitor_returning'],
+ 'visitor_days_since_first' => $visitorInformation['visitor_days_since_first'],
+ 'visitor_days_since_order' => $visitorInformation['visitor_days_since_order'],
+ 'visitor_count_visits' => $visitorInformation['visitor_count_visits'],
+ );
+
+ $extraLocationCols = array('location_region', 'location_city', 'location_latitude', 'location_longitude');
+ foreach ($extraLocationCols as $col) {
+ if (isset($visitorInformation[$col])) {
+ $goal[$col] = $visitorInformation[$col];
+ }
+ }
+
+ // Copy Custom Variables from Visit row to the Goal conversion
+ for ($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++) {
+ if (isset($visitorInformation['custom_var_k' . $i])
+ && strlen($visitorInformation['custom_var_k' . $i])
+ ) {
+ $goal['custom_var_k' . $i] = $visitorInformation['custom_var_k' . $i];
+ }
+ if (isset($visitorInformation['custom_var_v' . $i])
+ && strlen($visitorInformation['custom_var_v' . $i])
+ ) {
+ $goal['custom_var_v' . $i] = $visitorInformation['custom_var_v' . $i];
+ }
+ }
+ // Otherwise, set the Custom Variables found in the cookie sent with this request
+ $goal += $visitCustomVariables;
+
+ // Attributing the correct Referrer to this conversion.
+ // Priority order is as follows:
+ // 0) In some cases, the campaign is not passed from the JS so we look it up from the current visit
+ // 1) Campaign name/kwd parsed in the JS
+ // 2) Referrer URL stored in the _ref cookie
+ // 3) If no info from the cookie, attribute to the current visit referrer
+
+ // 3) Default values: current referrer
$type = $visitorInformation['referer_type'];
$name = $visitorInformation['referer_name'];
$keyword = $visitorInformation['referer_keyword'];
$time = $visitorInformation['visit_first_action_time'];
-
+
// 0) In some (unknown!?) cases the campaign is not found in the attribution cookie, but the URL ref was found.
- // In this case we look up if the current visit is credited to a campaign and will credit this campaign rather than the URL ref (since campaigns have higher priority)
- if(empty($referrerCampaignName)
- && $type == Piwik_Common::REFERER_TYPE_CAMPAIGN
- && !empty($name)
- )
- {
- // Use default values per above
- }
- // 1) Campaigns from 1st party cookie
- elseif(!empty($referrerCampaignName))
- {
- $type = Piwik_Common::REFERER_TYPE_CAMPAIGN;
- $name = $referrerCampaignName;
- $keyword = $referrerCampaignKeyword;
- $time = $referrerTimestamp;
- }
- // 2) Referrer URL parsing
- elseif(!empty($referrerUrl))
- {
- $referrer = new Piwik_Tracker_Visit_Referer();
+ // In this case we look up if the current visit is credited to a campaign and will credit this campaign rather than the URL ref (since campaigns have higher priority)
+ if (empty($referrerCampaignName)
+ && $type == Piwik_Common::REFERER_TYPE_CAMPAIGN
+ && !empty($name)
+ ) {
+ // Use default values per above
+ } // 1) Campaigns from 1st party cookie
+ elseif (!empty($referrerCampaignName)) {
+ $type = Piwik_Common::REFERER_TYPE_CAMPAIGN;
+ $name = $referrerCampaignName;
+ $keyword = $referrerCampaignKeyword;
+ $time = $referrerTimestamp;
+ } // 2) Referrer URL parsing
+ elseif (!empty($referrerUrl)) {
+ $referrer = new Piwik_Tracker_Visit_Referer();
$referrer = $referrer->getRefererInformation($referrerUrl, $currentUrl = '', $idSite);
-
+
// if the parsed referer is interesting enough, ie. website or search engine
- if(in_array($referrer['referer_type'], array(Piwik_Common::REFERER_TYPE_SEARCH_ENGINE, Piwik_Common::REFERER_TYPE_WEBSITE)))
- {
- $type = $referrer['referer_type'];
- $name = $referrer['referer_name'];
- $keyword = $referrer['referer_keyword'];
- $time = $referrerTimestamp;
+ if (in_array($referrer['referer_type'], array(Piwik_Common::REFERER_TYPE_SEARCH_ENGINE, Piwik_Common::REFERER_TYPE_WEBSITE))) {
+ $type = $referrer['referer_type'];
+ $name = $referrer['referer_name'];
+ $keyword = $referrer['referer_keyword'];
+ $time = $referrerTimestamp;
}
- }
- $goal += array(
- 'referer_type' => $type,
- 'referer_name' => $name,
- 'referer_keyword' => $keyword,
- // this field is currently unused
- 'referer_visit_server_date' => date("Y-m-d", $time),
- );
-
- // some goals are converted, so must be ecommerce Order or Cart Update
- if($this->requestIsEcommerce)
- {
- $this->recordEcommerceGoal($goal, $visitorInformation);
- }
- else
- {
- $this->recordStandardGoals($goal, $action, $visitorInformation);
- }
- }
-
- /**
- * Returns rounded decimal revenue, or if revenue is integer, then returns as is.
- *
- * @param int|float $revenue
- * @return int|float
- */
- protected function getRevenue($revenue)
- {
- if(round($revenue) == $revenue)
- {
- return $revenue;
- }
- return round($revenue, self::REVENUE_PRECISION);
- }
-
- /**
- * Records an Ecommerce conversion in the DB. Deals with Items found in the request.
- * Will deal with 2 types of conversions: Ecommerce Order and Ecommerce Cart update (Add to cart, Update Cart etc).
- *
- * @param array $goal
- * @param array $visitorInformation
- */
- protected function recordEcommerceGoal($goal, $visitorInformation)
- {
- // Is the transaction a Cart Update or an Ecommerce order?
- $updateWhere = array(
- 'idvisit' => $visitorInformation['idvisit'],
- 'idgoal' => self::IDGOAL_CART,
- 'buster' => 0,
- );
-
- if($this->isThereExistingCartInVisit)
- {
- printDebug("There is an existing cart for this visit");
- }
- if($this->isGoalAnOrder)
- {
- $orderIdNumeric = Piwik_Common::hashStringToInt($this->orderId);
- $goal['idgoal'] = self::IDGOAL_ORDER;
- $goal['idorder'] = $this->orderId;
- $goal['buster'] = $orderIdNumeric;
- $goal['revenue_subtotal'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_st', false, 'float', $this->request));
- $goal['revenue_tax'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_tx', false, 'float', $this->request));
- $goal['revenue_shipping'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_sh', false, 'float', $this->request));
- $goal['revenue_discount'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_dt', false, 'float', $this->request));
-
- $debugMessage = 'The conversion is an Ecommerce order';
- }
- // If Cart update, select current items in the previous Cart
- else
- {
- $goal['buster'] = 0;
- $goal['idgoal'] = self::IDGOAL_CART;
- $debugMessage = 'The conversion is an Ecommerce Cart Update';
- }
- $goal['revenue'] = $this->getRevenue(Piwik_Common::getRequestVar('revenue', 0, 'float', $this->request));
-
- printDebug($debugMessage . ':' . var_export($goal, true));
-
- // INSERT or Sync items in the Cart / Order for this visit & order
- $items = $this->getEcommerceItemsFromRequest();
- if($items === false)
- {
- return;
- }
-
- $itemsCount = 0;
- foreach($items as $item)
- {
- $itemsCount += $item[self::INTERNAL_ITEM_QUANTITY];
- }
- $goal['items'] = $itemsCount;
-
- // If there is already a cart for this visit
- // 1) If conversion is Order, we update the cart into an Order
- // 2) If conversion is Cart Update, we update the cart
- $recorded = $this->recordGoal($goal, $this->isThereExistingCartInVisit, $updateWhere);
- if($recorded)
- {
- $this->recordEcommerceItems($goal, $items);
- }
-
- Piwik_PostEvent('Tracker.recordEcommerceGoal', $goal);
- }
-
- /**
- * Returns Items read from the request string
- * @return array|false
- */
- protected function getEcommerceItemsFromRequest()
- {
- $items = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('ec_items', '', 'string', $this->request));
- if(empty($items))
- {
- printDebug("There are no Ecommerce items in the request");
- // we still record an Ecommerce order without any item in it
- return array();
- }
- $items = Piwik_Common::json_decode($items, $assoc = true);
- if(!is_array($items))
- {
- printDebug("Error while json_decode the Ecommerce items = ".var_export($items, true));
- return false;
- }
-
- $cleanedItems = $this->getCleanedEcommerceItems($items);
- return $cleanedItems;
- }
-
- /**
- * Loads the Ecommerce items from the request and records them in the DB
- *
- * @param array $goal
- * @param array $items
- * @throws Exception
- * @return int Number of items in the cart
- */
- protected function recordEcommerceItems($goal, $items)
- {
- $itemInCartBySku = array();
- foreach($items as $item)
- {
- $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 ". Piwik_Common::prefixTable('log_conversion_item') . "
+ }
+ $goal += array(
+ 'referer_type' => $type,
+ 'referer_name' => $name,
+ 'referer_keyword' => $keyword,
+ // this field is currently unused
+ 'referer_visit_server_date' => date("Y-m-d", $time),
+ );
+
+ // some goals are converted, so must be ecommerce Order or Cart Update
+ if ($this->requestIsEcommerce) {
+ $this->recordEcommerceGoal($goal, $visitorInformation);
+ } else {
+ $this->recordStandardGoals($goal, $action, $visitorInformation);
+ }
+ }
+
+ /**
+ * Returns rounded decimal revenue, or if revenue is integer, then returns as is.
+ *
+ * @param int|float $revenue
+ * @return int|float
+ */
+ protected function getRevenue($revenue)
+ {
+ if (round($revenue) == $revenue) {
+ return $revenue;
+ }
+ return round($revenue, self::REVENUE_PRECISION);
+ }
+
+ /**
+ * Records an Ecommerce conversion in the DB. Deals with Items found in the request.
+ * Will deal with 2 types of conversions: Ecommerce Order and Ecommerce Cart update (Add to cart, Update Cart etc).
+ *
+ * @param array $goal
+ * @param array $visitorInformation
+ */
+ protected function recordEcommerceGoal($goal, $visitorInformation)
+ {
+ // Is the transaction a Cart Update or an Ecommerce order?
+ $updateWhere = array(
+ 'idvisit' => $visitorInformation['idvisit'],
+ 'idgoal' => self::IDGOAL_CART,
+ 'buster' => 0,
+ );
+
+ if ($this->isThereExistingCartInVisit) {
+ printDebug("There is an existing cart for this visit");
+ }
+ if ($this->isGoalAnOrder) {
+ $orderIdNumeric = Piwik_Common::hashStringToInt($this->orderId);
+ $goal['idgoal'] = self::IDGOAL_ORDER;
+ $goal['idorder'] = $this->orderId;
+ $goal['buster'] = $orderIdNumeric;
+ $goal['revenue_subtotal'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_st', false, 'float', $this->request));
+ $goal['revenue_tax'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_tx', false, 'float', $this->request));
+ $goal['revenue_shipping'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_sh', false, 'float', $this->request));
+ $goal['revenue_discount'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_dt', false, 'float', $this->request));
+
+ $debugMessage = 'The conversion is an Ecommerce order';
+ } // If Cart update, select current items in the previous Cart
+ else {
+ $goal['buster'] = 0;
+ $goal['idgoal'] = self::IDGOAL_CART;
+ $debugMessage = 'The conversion is an Ecommerce Cart Update';
+ }
+ $goal['revenue'] = $this->getRevenue(Piwik_Common::getRequestVar('revenue', 0, 'float', $this->request));
+
+ printDebug($debugMessage . ':' . var_export($goal, true));
+
+ // INSERT or Sync items in the Cart / Order for this visit & order
+ $items = $this->getEcommerceItemsFromRequest();
+ if ($items === false) {
+ return;
+ }
+
+ $itemsCount = 0;
+ foreach ($items as $item) {
+ $itemsCount += $item[self::INTERNAL_ITEM_QUANTITY];
+ }
+ $goal['items'] = $itemsCount;
+
+ // If there is already a cart for this visit
+ // 1) If conversion is Order, we update the cart into an Order
+ // 2) If conversion is Cart Update, we update the cart
+ $recorded = $this->recordGoal($goal, $this->isThereExistingCartInVisit, $updateWhere);
+ if ($recorded) {
+ $this->recordEcommerceItems($goal, $items);
+ }
+
+ Piwik_PostEvent('Tracker.recordEcommerceGoal', $goal);
+ }
+
+ /**
+ * Returns Items read from the request string
+ * @return array|false
+ */
+ protected function getEcommerceItemsFromRequest()
+ {
+ $items = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('ec_items', '', 'string', $this->request));
+ if (empty($items)) {
+ printDebug("There are no Ecommerce items in the request");
+ // we still record an Ecommerce order without any item in it
+ return array();
+ }
+ $items = Piwik_Common::json_decode($items, $assoc = true);
+ if (!is_array($items)) {
+ printDebug("Error while json_decode the Ecommerce items = " . var_export($items, true));
+ return false;
+ }
+
+ $cleanedItems = $this->getCleanedEcommerceItems($items);
+ return $cleanedItems;
+ }
+
+ /**
+ * Loads the Ecommerce items from the request and records them in the DB
+ *
+ * @param array $goal
+ * @param array $items
+ * @throws Exception
+ * @return int Number of items in the cart
+ */
+ protected function recordEcommerceItems($goal, $items)
+ {
+ $itemInCartBySku = array();
+ foreach ($items as $item) {
+ $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 " . Piwik_Common::prefixTable('log_conversion_item') . "
WHERE idvisit = ?
AND (idorder = ? OR idorder = ?)";
-
- $bind = array( $goal['idvisit'],
- isset($goal['idorder']) ? $goal['idorder'] : self::ITEM_IDORDER_ABANDONED_CART,
- self::ITEM_IDORDER_ABANDONED_CART
- );
-
- $itemsInDb = Piwik_Tracker::getDatabase()->fetchAll($sql, $bind);
-
- printDebug("Items found in current cart, for conversion_item (visit,idorder)=" . var_export($bind,true));
- 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'];
-
- // Ensure price comparisons will have the same assumption
- $itemInDb['price'] = $this->getRevenue($itemInDb['price']);
- $itemInDbOriginal = $itemInDb;
- $itemInDb = array_values($itemInDb);
-
- // Cast all as string, because what comes out of the fetchAll() are strings
- $itemInDb = $this->getItemRowCast($itemInDb);
-
- //Item in the cart in the DB, but not anymore in the cart
- if(!isset($itemInCartBySku[$itemInDb[0]]))
- {
- $itemToUpdate = array_merge($itemInDb,
- array( 'deleted' => 1,
- 'idorder_original_value' => $itemInDbOriginal['idorder_original_value']
- )
- );
-
- $itemsToUpdate[] = $itemToUpdate;
- printDebug("Item found in the previous Cart, but no in the current cart/order");
- printDebug($itemToUpdate);
- continue;
- }
-
- $newItem = $itemInCartBySku[$itemInDb[0]];
- $newItem = $this->getItemRowCast($newItem);
-
- if(count($itemInDb) != count($newItem))
- {
- printDebug("ERROR: Different format in items from cart and DB");
- throw new Exception(" Item in DB and Item in cart have a different format, this is not expected... ".var_export($itemInDb, true) . var_export($newItem, true));
- }
- printDebug("Item has changed since the last cart. Previous item stored in cart in database:");
- printDebug($itemInDb);
- printDebug("New item to UPDATE the previous row:");
- $newItem['idorder_original_value'] = $itemInDbOriginal['idorder_original_value'];
- printDebug($newItem);
- $itemsToUpdate[] = $newItem;
- }
-
- // Items to UPDATE
- $this->updateEcommerceItems($goal, $itemsToUpdate);
-
- // Items to INSERT
- $itemsToInsert = array();
- foreach($items as $item)
- {
- if(!in_array($item[0], $skuFoundInDb))
- {
- $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.
- *
- * @param array $items
- * @return array $cleanedItems
- */
- protected function getCleanedEcommerceItems($items)
- {
- // Clean up the items array
- $cleanedItems = array();
- foreach($items as $item)
- {
- $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;
- }
-
- $sku = $item[self::INDEX_ITEM_SKU];
- if(!empty($item[self::INDEX_ITEM_NAME])) {
- $name = $item[self::INDEX_ITEM_NAME];
- }
-
- if(!empty($item[self::INDEX_ITEM_CATEGORY])) {
- $category = $item[self::INDEX_ITEM_CATEGORY];
- }
-
- if(isset($item[self::INDEX_ITEM_PRICE])
- && is_numeric($item[self::INDEX_ITEM_PRICE])) {
- $price = $this->getRevenue($item[self::INDEX_ITEM_PRICE]);
- }
- if(!empty($item[self::INDEX_ITEM_QUANTITY])
- && is_numeric($item[self::INDEX_ITEM_QUANTITY])) {
- $quantity = (int)$item[self::INDEX_ITEM_QUANTITY];
- }
-
- // self::INDEX_ITEM_* are in order
- $cleanedItems[] = array(
- self::INTERNAL_ITEM_SKU => $sku,
- self::INTERNAL_ITEM_NAME => $name,
- self::INTERNAL_ITEM_CATEGORY => $category,
- self::INTERNAL_ITEM_CATEGORY2 => $category2,
- self::INTERNAL_ITEM_CATEGORY3 => $category3,
- self::INTERNAL_ITEM_CATEGORY4 => $category4,
- self::INTERNAL_ITEM_CATEGORY5 => $category5,
- self::INTERNAL_ITEM_PRICE => $price,
- self::INTERNAL_ITEM_QUANTITY => $quantity
- );
- }
-
- // Lookup Item SKUs, Names & Categories Ids
- $actionsToLookupAllItems = array();
-
- // Each item has 7 potential "ids" to lookup in the lookup table
- $columnsInEachRow = 1 + 1 + self::MAXIMUM_PRODUCT_CATEGORIES;
-
- foreach($cleanedItems as $item)
- {
- $actionsToLookup = array();
- list($sku, $name, $category, $price, $quantity) = $item;
- $actionsToLookup[] = array(trim($sku), Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_SKU);
- $actionsToLookup[] = array(trim($name), Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_NAME);
-
- // Only one category
- if(!is_array($category))
- {
- $actionsToLookup[] = array(trim($category), Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_CATEGORY);
- }
- // Multiple categories
- else
- {
- $countCategories = 0;
- foreach($category as $productCategory) {
- $productCategory = trim($productCategory);
- if(empty($productCategory)) {
- continue;
- }
- $countCategories++;
- if($countCategories > self::MAXIMUM_PRODUCT_CATEGORIES) {
- break;
- }
- $actionsToLookup[] = array($productCategory, Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_CATEGORY);
- }
- }
- // Ensure that each row has the same number of columns, fill in the blanks
- for($i = count($actionsToLookup); $i < $columnsInEachRow; $i++) {
- $actionsToLookup[] = array(false, Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_CATEGORY);
- }
- $actionsToLookupAllItems = array_merge($actionsToLookupAllItems, $actionsToLookup);
- }
-
- $actionsLookedUp = Piwik_Tracker_Action::loadActionId($actionsToLookupAllItems);
-
- // Replace SKU, name & category by their ID action
- foreach($cleanedItems as $index => &$item)
- {
- // SKU
- $item[0] = $actionsLookedUp[ $index * $columnsInEachRow + 0][2];
- // Name
- $item[1] = $actionsLookedUp[ $index * $columnsInEachRow + 1][2];
- // Categories
- $item[2] = $actionsLookedUp[ $index * $columnsInEachRow + 2][2];
- $item[3] = $actionsLookedUp[ $index * $columnsInEachRow + 3][2];
- $item[4] = $actionsLookedUp[ $index * $columnsInEachRow + 4][2];
- $item[5] = $actionsLookedUp[ $index * $columnsInEachRow + 5][2];
- $item[6] = $actionsLookedUp[ $index * $columnsInEachRow + 6][2];
- }
- return $cleanedItems;
- }
-
- /**
- * Updates the cart items in the DB
- * that have been modified since the last cart update
- * @param $goal
- * @param array $itemsToUpdate
- * @return
- */
- protected function updateEcommerceItems($goal, $itemsToUpdate)
- {
- if(empty($itemsToUpdate))
- {
- return;
- }
- printDebug("Goal data used to update ecommerce items:");
- printDebug($goal);
-
- foreach($itemsToUpdate as $item)
- {
- $newRow = $this->getItemRowEnriched($goal, $item);
- printDebug($newRow);
- $updateParts = $sqlBind = array();
- foreach($newRow AS $name => $value)
- {
- $updateParts[] = $name." = ?";
- $sqlBind[] = $value;
- }
- $sql = 'UPDATE ' . Piwik_Common::prefixTable('log_conversion_item') . "
- SET ".implode($updateParts, ', ')."
+
+ $bind = array($goal['idvisit'],
+ isset($goal['idorder']) ? $goal['idorder'] : self::ITEM_IDORDER_ABANDONED_CART,
+ self::ITEM_IDORDER_ABANDONED_CART
+ );
+
+ $itemsInDb = Piwik_Tracker::getDatabase()->fetchAll($sql, $bind);
+
+ printDebug("Items found in current cart, for conversion_item (visit,idorder)=" . var_export($bind, true));
+ 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'];
+
+ // Ensure price comparisons will have the same assumption
+ $itemInDb['price'] = $this->getRevenue($itemInDb['price']);
+ $itemInDbOriginal = $itemInDb;
+ $itemInDb = array_values($itemInDb);
+
+ // Cast all as string, because what comes out of the fetchAll() are strings
+ $itemInDb = $this->getItemRowCast($itemInDb);
+
+ //Item in the cart in the DB, but not anymore in the cart
+ if (!isset($itemInCartBySku[$itemInDb[0]])) {
+ $itemToUpdate = array_merge($itemInDb,
+ array('deleted' => 1,
+ 'idorder_original_value' => $itemInDbOriginal['idorder_original_value']
+ )
+ );
+
+ $itemsToUpdate[] = $itemToUpdate;
+ printDebug("Item found in the previous Cart, but no in the current cart/order");
+ printDebug($itemToUpdate);
+ continue;
+ }
+
+ $newItem = $itemInCartBySku[$itemInDb[0]];
+ $newItem = $this->getItemRowCast($newItem);
+
+ if (count($itemInDb) != count($newItem)) {
+ printDebug("ERROR: Different format in items from cart and DB");
+ throw new Exception(" Item in DB and Item in cart have a different format, this is not expected... " . var_export($itemInDb, true) . var_export($newItem, true));
+ }
+ printDebug("Item has changed since the last cart. Previous item stored in cart in database:");
+ printDebug($itemInDb);
+ printDebug("New item to UPDATE the previous row:");
+ $newItem['idorder_original_value'] = $itemInDbOriginal['idorder_original_value'];
+ printDebug($newItem);
+ $itemsToUpdate[] = $newItem;
+ }
+
+ // Items to UPDATE
+ $this->updateEcommerceItems($goal, $itemsToUpdate);
+
+ // Items to INSERT
+ $itemsToInsert = array();
+ foreach ($items as $item) {
+ if (!in_array($item[0], $skuFoundInDb)) {
+ $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.
+ *
+ * @param array $items
+ * @return array $cleanedItems
+ */
+ protected function getCleanedEcommerceItems($items)
+ {
+ // Clean up the items array
+ $cleanedItems = array();
+ foreach ($items as $item) {
+ $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;
+ }
+
+ $sku = $item[self::INDEX_ITEM_SKU];
+ if (!empty($item[self::INDEX_ITEM_NAME])) {
+ $name = $item[self::INDEX_ITEM_NAME];
+ }
+
+ if (!empty($item[self::INDEX_ITEM_CATEGORY])) {
+ $category = $item[self::INDEX_ITEM_CATEGORY];
+ }
+
+ if (isset($item[self::INDEX_ITEM_PRICE])
+ && is_numeric($item[self::INDEX_ITEM_PRICE])
+ ) {
+ $price = $this->getRevenue($item[self::INDEX_ITEM_PRICE]);
+ }
+ if (!empty($item[self::INDEX_ITEM_QUANTITY])
+ && is_numeric($item[self::INDEX_ITEM_QUANTITY])
+ ) {
+ $quantity = (int)$item[self::INDEX_ITEM_QUANTITY];
+ }
+
+ // self::INDEX_ITEM_* are in order
+ $cleanedItems[] = array(
+ self::INTERNAL_ITEM_SKU => $sku,
+ self::INTERNAL_ITEM_NAME => $name,
+ self::INTERNAL_ITEM_CATEGORY => $category,
+ self::INTERNAL_ITEM_CATEGORY2 => $category2,
+ self::INTERNAL_ITEM_CATEGORY3 => $category3,
+ self::INTERNAL_ITEM_CATEGORY4 => $category4,
+ self::INTERNAL_ITEM_CATEGORY5 => $category5,
+ self::INTERNAL_ITEM_PRICE => $price,
+ self::INTERNAL_ITEM_QUANTITY => $quantity
+ );
+ }
+
+ // Lookup Item SKUs, Names & Categories Ids
+ $actionsToLookupAllItems = array();
+
+ // Each item has 7 potential "ids" to lookup in the lookup table
+ $columnsInEachRow = 1 + 1 + self::MAXIMUM_PRODUCT_CATEGORIES;
+
+ foreach ($cleanedItems as $item) {
+ $actionsToLookup = array();
+ list($sku, $name, $category, $price, $quantity) = $item;
+ $actionsToLookup[] = array(trim($sku), Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_SKU);
+ $actionsToLookup[] = array(trim($name), Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_NAME);
+
+ // Only one category
+ if (!is_array($category)) {
+ $actionsToLookup[] = array(trim($category), Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_CATEGORY);
+ } // Multiple categories
+ else {
+ $countCategories = 0;
+ foreach ($category as $productCategory) {
+ $productCategory = trim($productCategory);
+ if (empty($productCategory)) {
+ continue;
+ }
+ $countCategories++;
+ if ($countCategories > self::MAXIMUM_PRODUCT_CATEGORIES) {
+ break;
+ }
+ $actionsToLookup[] = array($productCategory, Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_CATEGORY);
+ }
+ }
+ // Ensure that each row has the same number of columns, fill in the blanks
+ for ($i = count($actionsToLookup); $i < $columnsInEachRow; $i++) {
+ $actionsToLookup[] = array(false, Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_CATEGORY);
+ }
+ $actionsToLookupAllItems = array_merge($actionsToLookupAllItems, $actionsToLookup);
+ }
+
+ $actionsLookedUp = Piwik_Tracker_Action::loadActionId($actionsToLookupAllItems);
+
+ // Replace SKU, name & category by their ID action
+ foreach ($cleanedItems as $index => &$item) {
+ // SKU
+ $item[0] = $actionsLookedUp[$index * $columnsInEachRow + 0][2];
+ // Name
+ $item[1] = $actionsLookedUp[$index * $columnsInEachRow + 1][2];
+ // Categories
+ $item[2] = $actionsLookedUp[$index * $columnsInEachRow + 2][2];
+ $item[3] = $actionsLookedUp[$index * $columnsInEachRow + 3][2];
+ $item[4] = $actionsLookedUp[$index * $columnsInEachRow + 4][2];
+ $item[5] = $actionsLookedUp[$index * $columnsInEachRow + 5][2];
+ $item[6] = $actionsLookedUp[$index * $columnsInEachRow + 6][2];
+ }
+ return $cleanedItems;
+ }
+
+ /**
+ * Updates the cart items in the DB
+ * that have been modified since the last cart update
+ * @param $goal
+ * @param array $itemsToUpdate
+ * @return
+ */
+ protected function updateEcommerceItems($goal, $itemsToUpdate)
+ {
+ if (empty($itemsToUpdate)) {
+ return;
+ }
+ printDebug("Goal data used to update ecommerce items:");
+ printDebug($goal);
+
+ foreach ($itemsToUpdate as $item) {
+ $newRow = $this->getItemRowEnriched($goal, $item);
+ printDebug($newRow);
+ $updateParts = $sqlBind = array();
+ foreach ($newRow AS $name => $value) {
+ $updateParts[] = $name . " = ?";
+ $sqlBind[] = $value;
+ }
+ $sql = 'UPDATE ' . Piwik_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'];
- Piwik_Tracker::getDatabase()->query($sql, $sqlBind);
- }
- }
-
- /**
- * Inserts in the cart in the DB the new items
- * that were not previously in the cart
- * @param $goal
- * @param array $itemsToInsert
- * @return
- */
- protected function insertEcommerceItems($goal, $itemsToInsert)
- {
- if(empty($itemsToInsert))
- {
- return;
- }
- printDebug("Ecommerce items that are added to the cart/order");
- printDebug($itemsToInsert);
-
- $sql = "INSERT INTO " . Piwik_Common::prefixTable('log_conversion_item') . "
+ $sqlBind[] = $newRow['idvisit'];
+ $sqlBind[] = $item['idorder_original_value'];
+ $sqlBind[] = $newRow['idaction_sku'];
+ Piwik_Tracker::getDatabase()->query($sql, $sqlBind);
+ }
+ }
+
+ /**
+ * Inserts in the cart in the DB the new items
+ * that were not previously in the cart
+ * @param $goal
+ * @param array $itemsToInsert
+ * @return
+ */
+ protected function insertEcommerceItems($goal, $itemsToInsert)
+ {
+ if (empty($itemsToInsert)) {
+ return;
+ }
+ printDebug("Ecommerce items that are added to the cart/order");
+ printDebug($itemsToInsert);
+
+ $sql = "INSERT INTO " . Piwik_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();
- foreach($itemsToInsert as $item)
- {
- if($i > 0) { $sql .= ','; }
- $newRow = array_values($this->getItemRowEnriched($goal, $item));
- $sql .= " ( ". Piwik_Common::getSqlStringFieldsArray($newRow) . " ) ";
- $i++;
- $bind = array_merge($bind, $newRow);
- }
- Piwik_Tracker::getDatabase()->query($sql, $bind);
- printDebug($sql);printDebug($bind);
- }
-
- protected function getItemRowEnriched($goal, $item)
- {
- $newRow = array(
- 'idaction_sku' => (int)$item[self::INTERNAL_ITEM_SKU],
- 'idaction_name' => (int)$item[self::INTERNAL_ITEM_NAME],
- 'idaction_category' => (int)$item[self::INTERNAL_ITEM_CATEGORY],
- 'idaction_category2' => (int)$item[self::INTERNAL_ITEM_CATEGORY2],
- 'idaction_category3' => (int)$item[self::INTERNAL_ITEM_CATEGORY3],
- 'idaction_category4' => (int)$item[self::INTERNAL_ITEM_CATEGORY4],
- 'idaction_category5' => (int)$item[self::INTERNAL_ITEM_CATEGORY5],
- 'price' => $item[self::INTERNAL_ITEM_PRICE],
- 'quantity' => $item[self::INTERNAL_ITEM_QUANTITY],
- 'deleted' => isset($item['deleted']) ? $item['deleted'] : 0, //deleted
- 'idorder' => isset($goal['idorder']) ? $goal['idorder'] : self::ITEM_IDORDER_ABANDONED_CART, //idorder = 0 in log_conversion_item for carts
- 'idsite' => $goal['idsite'],
- 'idvisitor' => $goal['idvisitor'],
- 'server_time' => $goal['server_time'],
- 'idvisit' => $goal['idvisit']
- );
- return $newRow;
- }
-
- /**
- * Records a standard non-Ecommerce goal in the DB (URL/Title matching),
- * linking the conversion to the action that triggered it
- * @param $goal
- * @param Piwik_Tracker_Action $action
- * @param $visitorInformation
- */
- protected function recordStandardGoals($goal, $action, $visitorInformation)
- {
- foreach($this->convertedGoals as $convertedGoal)
- {
- printDebug("- Goal ".$convertedGoal['idgoal'] ." matched. Recording...");
- $newGoal = $goal;
- $newGoal['idgoal'] = $convertedGoal['idgoal'];
- $newGoal['url'] = $convertedGoal['url'];
- $newGoal['revenue'] = $this->getRevenue($convertedGoal['revenue']);
-
- if(!is_null($action))
- {
- $newGoal['idaction_url'] = $action->getIdActionUrl();
- $newGoal['idlink_va'] = $action->getIdLinkVisitAction();
- }
-
- // If multiple Goal conversions per visit, set a cache buster
- $newGoal['buster'] = $convertedGoal['allow_multiple'] == 0
- ? '0'
- : $visitorInformation['visit_last_action_time'];
-
- $this->recordGoal($newGoal);
-
- Piwik_PostEvent('Tracker.recordStandardGoals', $newGoal);
- }
- }
- /**
- * Helper function used by other record* methods which will INSERT or UPDATE the conversion in the DB
- *
- * @param array $newGoal
- * @param bool $mustUpdateNotInsert If set to true, the previous conversion will be UPDATEd. This is used for the Cart Update conversion (only one cart per visit)
- * @param array $updateWhere
- * @return bool
- */
- protected function recordGoal($newGoal, $mustUpdateNotInsert = false, $updateWhere = array())
- {
- $newGoalDebug = $newGoal;
- $newGoalDebug['idvisitor'] = bin2hex($newGoalDebug['idvisitor']);
- printDebug($newGoalDebug);
-
- $fields = implode(", ", array_keys($newGoal));
- $bindFields = Piwik_Common::getSqlStringFieldsArray($newGoal);
-
- if($mustUpdateNotInsert)
- {
- $updateParts = $sqlBind = $updateWhereParts = array();
- foreach($newGoal AS $name => $value)
- {
- $updateParts[] = $name." = ?";
- $sqlBind[] = $value;
- }
- foreach($updateWhere as $name => $value)
- {
- $updateWhereParts[] = $name." = ?";
- $sqlBind[] = $value;
- }
- $sql = 'UPDATE ' . Piwik_Common::prefixTable('log_conversion') . "
- SET ".implode($updateParts, ', ')."
- WHERE ".implode($updateWhereParts, ' AND ');
- Piwik_Tracker::getDatabase()->query($sql, $sqlBind);
- return true;
- }
- else
- {
- $sql = 'INSERT IGNORE INTO ' . Piwik_Common::prefixTable('log_conversion') . "
+ $i = 0;
+ $bind = array();
+ foreach ($itemsToInsert as $item) {
+ if ($i > 0) {
+ $sql .= ',';
+ }
+ $newRow = array_values($this->getItemRowEnriched($goal, $item));
+ $sql .= " ( " . Piwik_Common::getSqlStringFieldsArray($newRow) . " ) ";
+ $i++;
+ $bind = array_merge($bind, $newRow);
+ }
+ Piwik_Tracker::getDatabase()->query($sql, $bind);
+ printDebug($sql);
+ printDebug($bind);
+ }
+
+ protected function getItemRowEnriched($goal, $item)
+ {
+ $newRow = array(
+ 'idaction_sku' => (int)$item[self::INTERNAL_ITEM_SKU],
+ 'idaction_name' => (int)$item[self::INTERNAL_ITEM_NAME],
+ 'idaction_category' => (int)$item[self::INTERNAL_ITEM_CATEGORY],
+ 'idaction_category2' => (int)$item[self::INTERNAL_ITEM_CATEGORY2],
+ 'idaction_category3' => (int)$item[self::INTERNAL_ITEM_CATEGORY3],
+ 'idaction_category4' => (int)$item[self::INTERNAL_ITEM_CATEGORY4],
+ 'idaction_category5' => (int)$item[self::INTERNAL_ITEM_CATEGORY5],
+ 'price' => $item[self::INTERNAL_ITEM_PRICE],
+ 'quantity' => $item[self::INTERNAL_ITEM_QUANTITY],
+ 'deleted' => isset($item['deleted']) ? $item['deleted'] : 0, //deleted
+ 'idorder' => isset($goal['idorder']) ? $goal['idorder'] : self::ITEM_IDORDER_ABANDONED_CART, //idorder = 0 in log_conversion_item for carts
+ 'idsite' => $goal['idsite'],
+ 'idvisitor' => $goal['idvisitor'],
+ 'server_time' => $goal['server_time'],
+ 'idvisit' => $goal['idvisit']
+ );
+ return $newRow;
+ }
+
+ /**
+ * Records a standard non-Ecommerce goal in the DB (URL/Title matching),
+ * linking the conversion to the action that triggered it
+ * @param $goal
+ * @param Piwik_Tracker_Action $action
+ * @param $visitorInformation
+ */
+ protected function recordStandardGoals($goal, $action, $visitorInformation)
+ {
+ foreach ($this->convertedGoals as $convertedGoal) {
+ printDebug("- Goal " . $convertedGoal['idgoal'] . " matched. Recording...");
+ $newGoal = $goal;
+ $newGoal['idgoal'] = $convertedGoal['idgoal'];
+ $newGoal['url'] = $convertedGoal['url'];
+ $newGoal['revenue'] = $this->getRevenue($convertedGoal['revenue']);
+
+ if (!is_null($action)) {
+ $newGoal['idaction_url'] = $action->getIdActionUrl();
+ $newGoal['idlink_va'] = $action->getIdLinkVisitAction();
+ }
+
+ // If multiple Goal conversions per visit, set a cache buster
+ $newGoal['buster'] = $convertedGoal['allow_multiple'] == 0
+ ? '0'
+ : $visitorInformation['visit_last_action_time'];
+
+ $this->recordGoal($newGoal);
+
+ Piwik_PostEvent('Tracker.recordStandardGoals', $newGoal);
+ }
+ }
+
+ /**
+ * Helper function used by other record* methods which will INSERT or UPDATE the conversion in the DB
+ *
+ * @param array $newGoal
+ * @param bool $mustUpdateNotInsert If set to true, the previous conversion will be UPDATEd. This is used for the Cart Update conversion (only one cart per visit)
+ * @param array $updateWhere
+ * @return bool
+ */
+ protected function recordGoal($newGoal, $mustUpdateNotInsert = false, $updateWhere = array())
+ {
+ $newGoalDebug = $newGoal;
+ $newGoalDebug['idvisitor'] = bin2hex($newGoalDebug['idvisitor']);
+ printDebug($newGoalDebug);
+
+ $fields = implode(", ", array_keys($newGoal));
+ $bindFields = Piwik_Common::getSqlStringFieldsArray($newGoal);
+
+ if ($mustUpdateNotInsert) {
+ $updateParts = $sqlBind = $updateWhereParts = array();
+ foreach ($newGoal AS $name => $value) {
+ $updateParts[] = $name . " = ?";
+ $sqlBind[] = $value;
+ }
+ foreach ($updateWhere as $name => $value) {
+ $updateWhereParts[] = $name . " = ?";
+ $sqlBind[] = $value;
+ }
+ $sql = 'UPDATE ' . Piwik_Common::prefixTable('log_conversion') . "
+ SET " . implode($updateParts, ', ') . "
+ WHERE " . implode($updateWhereParts, ' AND ');
+ Piwik_Tracker::getDatabase()->query($sql, $sqlBind);
+ return true;
+ } else {
+ $sql = 'INSERT IGNORE INTO ' . Piwik_Common::prefixTable('log_conversion') . "
($fields) VALUES ($bindFields) ";
- $bind = array_values($newGoal);
- $result = Piwik_Tracker::getDatabase()->query($sql, $bind);
-
- // If a record was inserted, we return true
- return Piwik_Tracker::getDatabase()->rowCount($result) > 0;
- }
- }
-
- /**
- * Casts the item array so that array comparisons work nicely
- * @param array $row
- * @return array
- */
- protected function getItemRowCast($row)
- {
- return array(
- (string)(int)$row[self::INTERNAL_ITEM_SKU],
- (string)(int)$row[self::INTERNAL_ITEM_NAME],
- (string)(int)$row[self::INTERNAL_ITEM_CATEGORY],
- (string)(int)$row[self::INTERNAL_ITEM_CATEGORY2],
- (string)(int)$row[self::INTERNAL_ITEM_CATEGORY3],
- (string)(int)$row[self::INTERNAL_ITEM_CATEGORY4],
- (string)(int)$row[self::INTERNAL_ITEM_CATEGORY5],
- (string)$row[self::INTERNAL_ITEM_PRICE],
- (string)$row[self::INTERNAL_ITEM_QUANTITY],
- );
- }
+ $bind = array_values($newGoal);
+ $result = Piwik_Tracker::getDatabase()->query($sql, $bind);
+
+ // If a record was inserted, we return true
+ return Piwik_Tracker::getDatabase()->rowCount($result) > 0;
+ }
+ }
+
+ /**
+ * Casts the item array so that array comparisons work nicely
+ * @param array $row
+ * @return array
+ */
+ protected function getItemRowCast($row)
+ {
+ return array(
+ (string)(int)$row[self::INTERNAL_ITEM_SKU],
+ (string)(int)$row[self::INTERNAL_ITEM_NAME],
+ (string)(int)$row[self::INTERNAL_ITEM_CATEGORY],
+ (string)(int)$row[self::INTERNAL_ITEM_CATEGORY2],
+ (string)(int)$row[self::INTERNAL_ITEM_CATEGORY3],
+ (string)(int)$row[self::INTERNAL_ITEM_CATEGORY4],
+ (string)(int)$row[self::INTERNAL_ITEM_CATEGORY5],
+ (string)$row[self::INTERNAL_ITEM_PRICE],
+ (string)$row[self::INTERNAL_ITEM_QUANTITY],
+ );
+ }
}
diff --git a/core/Tracker/IgnoreCookie.php b/core/Tracker/IgnoreCookie.php
index 510e21a042..3819fa1e15 100644
--- a/core/Tracker/IgnoreCookie.php
+++ b/core/Tracker/IgnoreCookie.php
@@ -1,76 +1,73 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Tracking cookies.
- *
+ *
* @package Piwik
* @subpackage Piwik_Tracker
*/
class Piwik_Tracker_IgnoreCookie
{
- /**
- * Get tracking cookie
- *
- * @return Piwik_Cookie
- */
- static public function getTrackingCookie()
- {
- $cookie_name = @Piwik_Config::getInstance()->Tracker['cookie_name'];
- $cookie_path = @Piwik_Config::getInstance()->Tracker['cookie_path'];
+ /**
+ * Get tracking cookie
+ *
+ * @return Piwik_Cookie
+ */
+ static public function getTrackingCookie()
+ {
+ $cookie_name = @Piwik_Config::getInstance()->Tracker['cookie_name'];
+ $cookie_path = @Piwik_Config::getInstance()->Tracker['cookie_path'];
- return new Piwik_Cookie($cookie_name, null, $cookie_path);
- }
+ return new Piwik_Cookie($cookie_name, null, $cookie_path);
+ }
- /**
- * Get ignore (visit) cookie
- *
- * @return Piwik_Cookie
- */
- static public function getIgnoreCookie()
- {
- $cookie_name = @Piwik_Config::getInstance()->Tracker['ignore_visits_cookie_name'];
- $cookie_path = @Piwik_Config::getInstance()->Tracker['cookie_path'];
+ /**
+ * Get ignore (visit) cookie
+ *
+ * @return Piwik_Cookie
+ */
+ static public function getIgnoreCookie()
+ {
+ $cookie_name = @Piwik_Config::getInstance()->Tracker['ignore_visits_cookie_name'];
+ $cookie_path = @Piwik_Config::getInstance()->Tracker['cookie_path'];
- return new Piwik_Cookie($cookie_name, null, $cookie_path);
- }
+ return new Piwik_Cookie($cookie_name, null, $cookie_path);
+ }
- /**
- * Set ignore (visit) cookie or deletes it if already present
- */
- static public function setIgnoreCookie()
- {
- $ignoreCookie = self::getIgnoreCookie();
- if($ignoreCookie->isCookieFound())
- {
- $ignoreCookie->delete();
- }
- else
- {
- $ignoreCookie->set('ignore', '*');
- $ignoreCookie->save();
+ /**
+ * Set ignore (visit) cookie or deletes it if already present
+ */
+ static public function setIgnoreCookie()
+ {
+ $ignoreCookie = self::getIgnoreCookie();
+ if ($ignoreCookie->isCookieFound()) {
+ $ignoreCookie->delete();
+ } else {
+ $ignoreCookie->set('ignore', '*');
+ $ignoreCookie->save();
- $trackingCookie = self::getTrackingCookie();
- $trackingCookie->delete();
- }
- }
+ $trackingCookie = self::getTrackingCookie();
+ $trackingCookie->delete();
+ }
+ }
- /**
- * Returns true if ignore (visit) cookie is present
- *
- * @return bool True if ignore cookie found; false otherwise
- */
- static public function isIgnoreCookieFound()
- {
- $cookie = self::getIgnoreCookie();
- return $cookie->isCookieFound() && $cookie->get('ignore') === '*';
- }
+ /**
+ * Returns true if ignore (visit) cookie is present
+ *
+ * @return bool True if ignore cookie found; false otherwise
+ */
+ static public function isIgnoreCookieFound()
+ {
+ $cookie = self::getIgnoreCookie();
+ return $cookie->isCookieFound() && $cookie->get('ignore') === '*';
+ }
}
diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php
index ef8b9e3cee..c4a661ec62 100644
--- a/core/Tracker/Visit.php
+++ b/core/Tracker/Visit.php
@@ -13,9 +13,11 @@
* @package Piwik
* @subpackage Piwik_Tracker
*/
-interface Piwik_Tracker_Visit_Interface {
- function setRequest($requestArray);
- function handle();
+interface Piwik_Tracker_Visit_Interface
+{
+ function setRequest($requestArray);
+
+ function handle();
}
/**
@@ -34,12 +36,12 @@ interface Piwik_Tracker_Visit_Interface {
*/
class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
{
- const UNKNOWN_CODE = 'xx';
-
- /**
- * @var Piwik_Cookie
- */
- protected $cookie = null;
+ const UNKNOWN_CODE = 'xx';
+
+ /**
+ * @var Piwik_Cookie
+ */
+ protected $cookie = null;
protected $visitorInfo = array();
protected $userSettingsInformation = null;
protected $visitorCustomVariables = array();
@@ -53,722 +55,673 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
protected $ip;
protected $authenticated = false;
- // Set to true when we set some custom variables from the cookie
- protected $customVariablesSetFromRequest = false;
-
- /**
- * @var Piwik_Tracker_GoalManager
- */
- protected $goalManager;
-
- public function __construct($forcedIpString = null, $forcedDateTime = null, $authenticated = false)
- {
- $this->timestamp = time();
- if(!empty($forcedDateTime))
- {
- if(!is_numeric($forcedDateTime))
- {
- $forcedDateTime = strtotime($forcedDateTime);
- }
- $this->timestamp = $forcedDateTime;
- }
- $ipString = $forcedIpString;
- if(empty($ipString))
- {
- $ipString = Piwik_IP::getIpFromHeader();
- }
-
- $ip = Piwik_IP::P2N($ipString);
- $this->ip = $ip;
-
- $this->authenticated = $authenticated;
- }
-
- function setForcedVisitorId($visitorId)
- {
- $this->forcedVisitorId = $visitorId;
- }
-
- function setRequest($requestArray)
- {
- $this->request = $requestArray;
-
- $idsite = Piwik_Common::getRequestVar('idsite', 0, 'int', $this->request);
- Piwik_PostEvent('Tracker.setRequest.idSite', $idsite, $requestArray);
- if($idsite <= 0)
- {
- throw new Exception('Invalid idSite');
- }
- $this->idsite = $idsite;
-
- // When the 'url' and referer url parameter are not given, we might be in the 'Simple Image Tracker' mode.
- // The URL can default to the Referer, which will be in this case
- // the URL of the page containing the Simple Image beacon
- if(empty($this->request['urlref'])
- && empty($this->request['url']))
- {
- $this->request['url'] = @$_SERVER['HTTP_REFERER'];
- }
- }
-
- /**
- * Main algorithm to handle the visit.
- *
- * Once we have the visitor information, we have to determine if the visit is a new or a known visit.
- *
- * 1) When the last action was done more than 30min ago,
- * or if the visitor is new, then this is a new visit.
- *
- * 2) If the last action is less than 30min ago, then the same visit is going on.
- * Because the visit goes on, we can get the time spent during the last action.
- *
- * NB:
- * - In the case of a new visit, then the time spent
- * during the last action of the previous visit is unknown.
- *
- * - In the case of a new visit but with a known visitor,
- * we can set the 'returning visitor' flag.
- *
- * In all the cases we set a cookie to the visitor with the new information.
- */
- public function handle()
- {
- // the IP is needed by isExcluded() and GoalManager->recordGoals()
- $this->visitorInfo['location_ip'] = $this->ip;
-
- if($this->isExcluded())
- {
- return;
- }
-
- // Anonymize IP (after testing for IP exclusion)
- $ip = $this->ip;
- Piwik_PostEvent('Tracker.Visit.setVisitorIp', $ip);
- $this->visitorInfo['location_ip'] = $ip;
-
- $this->visitorCustomVariables = self::getCustomVariables($scope = 'visit', $this->request);
- if(!empty($this->visitorCustomVariables))
- {
- printDebug("Visit level Custom Variables: ");
- printDebug($this->visitorCustomVariables);
- $this->customVariablesSetFromRequest = true;
- }
-
- $this->goalManager = new Piwik_Tracker_GoalManager();
-
- $someGoalsConverted = $visitIsConverted = false;
- $idActionUrl = $idActionName = $actionType = false;
- $action = null;
-
- $this->goalManager->init($this->request);
-
- $requestIsManualGoalConversion = ($this->goalManager->idGoal > 0);
- $requestIsEcommerce = $this->goalManager->requestIsEcommerce;
- if($requestIsEcommerce)
- {
- $someGoalsConverted = true;
-
- // Mark the visit as Converted only if it is an order (not for a Cart update)
- if($this->goalManager->isGoalAnOrder)
- {
- $visitIsConverted = true;
- }
- }
- // this request is from the JS call to piwikTracker.trackGoal()
- elseif($requestIsManualGoalConversion)
- {
- $someGoalsConverted = $this->goalManager->detectGoalId($this->idsite);
- $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)
- {
- printDebug('Invalid goal tracking request for goal id = '.$this->goalManager->idGoal);
- unset($this->goalManager);
- return;
- }
- }
- // normal page view, potentially triggering a URL matching goal
- else
- {
- $action = $this->newAction();
- $this->handleAction($action);
- $someGoalsConverted = $this->goalManager->detectGoalsMatchingUrl($this->idsite, $action);
- $visitIsConverted = $someGoalsConverted;
-
- $action->loadIdActionNameAndUrl();
- $idActionUrl = $action->getIdActionUrl();
- if ($idActionUrl !== null) {
- $idActionUrl = (int)$idActionUrl;
- }
- $idActionName = (int)$action->getIdActionName();
- $actionType = $action->getActionType();
- }
-
- // the visitor and session
- $this->recognizeTheVisitor();
-
- $isLastActionInTheSameVisit = $this->isLastActionInTheSameVisit();
-
- if(!$isLastActionInTheSameVisit)
- {
- 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
- // - the visitor doesn't have the Piwik cookie but could be match using heuristics @see recognizeTheVisitor()
- // )
- // AND
- // - the last page view for this visitor was less than 30 minutes ago @see isLastActionInTheSameVisit()
- if( $this->isVisitorKnown()
- && $isLastActionInTheSameVisit)
- {
- $idRefererActionUrl = $this->visitorInfo['visit_exit_idaction_url'];
- $idRefererActionName = $this->visitorInfo['visit_exit_idaction_name'];
- try {
- $this->handleKnownVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted);
- if(!is_null($action))
- {
- $action->record( $this->visitorInfo['idvisit'],
- $this->visitorInfo['idvisitor'],
- $idRefererActionUrl,
- $idRefererActionName,
- $this->visitorInfo['time_spent_ref_action']
- );
- }
- } catch(Piwik_Tracker_Visit_VisitorNotFoundInDatabase $e) {
-
- // There is an edge case when:
- // - two manual goal conversions happen in the same second
- // - which result in handleKnownVisit throwing the exception
- // 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
- || $requestIsEcommerce)
- {
- $someGoalsConverted = $visitIsConverted = false;
- }
- // When the row wasn't found in the logs, and this is a pageview or
- // goal matching URL, we force a new visitor
- else
- {
- $this->visitorKnown = false;
- }
- }
- }
-
- // New visit when:
- // - the visitor has the Piwik cookie but the last action was performed more than 30 min ago @see isLastActionInTheSameVisit()
- // - the visitor doesn't have the Piwik cookie, and couldn't be matched in @see recognizeTheVisitor()
- // - the visitor does have the Piwik cookie but the idcookie and idvisit found in the cookie didn't match to any existing visit in the DB
- if(!$this->isVisitorKnown()
- || !$isLastActionInTheSameVisit)
- {
- $this->handleNewVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted);
- if(!is_null($action))
- {
- $action->record( $this->visitorInfo['idvisit'], $this->visitorInfo['idvisitor'], 0, 0, 0 );
- }
- }
-
- // update the cookie with the new visit information
- $this->setThirdPartyCookie();
-
- // record the goals if applicable
- if($someGoalsConverted)
- {
- $refererTimestamp = Piwik_Common::getRequestVar('_refts', 0, 'int', $this->request);
- $refererUrl = Piwik_Common::getRequestVar('_ref', '', 'string', $this->request);
- $refererCampaignName = trim(urldecode(Piwik_Common::getRequestVar('_rcn', '', 'string', $this->request)));
- $refererCampaignKeyword = trim(urldecode(Piwik_Common::getRequestVar('_rck', '', 'string', $this->request)));
-
- $this->goalManager->recordGoals(
- $this->idsite,
- $this->visitorInfo,
- $this->visitorCustomVariables,
- $action,
- $refererTimestamp,
- $refererUrl,
- $refererCampaignName,
- $refererCampaignKeyword,
- $this->getBrowserLanguage()
- );
- }
- unset($this->goalManager);
- unset($action);
- $this->printCookie();
- }
-
- protected function printCookie()
- {
- printDebug($this->cookie);
- }
-
- protected function handleAction($action)
- {
- $action->setIdSite($this->idsite);
- $action->setRequest($this->request);
- $action->setTimestamp($this->getCurrentTimestamp());
- $action->init();
-
- if($this->detectActionIsOutlinkOnAliasHost($action))
- {
- printDebug("Info: The outlink URL host is one of the known host for this website. ");
- }
- if(isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG'])
- {
- $type = Piwik_Tracker_Action::getActionTypeName($action->getActionType());
- printDebug("Action is a $type,
- Action name = ". $action->getActionName() .",
- Action URL = ". $action->getActionUrl() );
- }
- }
-
- /**
- * In the case of a known visit, we have to do the following actions:
- *
- * 1) Insert the new action
- * 2) Update the visit information
- *
- * This method triggers two events:
- *
- * Tracker.knownVisitorUpdate is triggered before the visit information is updated
- * Event data is an array with the values to be updated (could be changed by plugins)
- *
- * Tracker.knownVisitorInformation is triggered after saving the new visit data
- * Even data is an array with updated information about the visit
- * @param $idActionUrl
- * @param $idActionName
- * @param $actionType
- * @param $visitIsConverted
- * @throws Piwik_Tracker_Visit_VisitorNotFoundInDatabase
- */
- protected function handleKnownVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted)
- {
- // gather information that needs to be updated
- $valuesToUpdate = array();
- $incrementActions = false;
- $sqlActionUpdate = '';
-
- if(!empty($idActionName))
- {
- $valuesToUpdate['visit_exit_idaction_name'] = (int)$idActionName;
- }
- if($idActionUrl !== false)
- {
- $valuesToUpdate['visit_exit_idaction_url'] = $idActionUrl;
- $incrementActions = true;
- }
- if($actionType == Piwik_Tracker_Action::TYPE_SITE_SEARCH)
- {
- $sqlActionUpdate .= "visit_total_searches = visit_total_searches + 1, ";
- $incrementActions = true;
- }
- if($incrementActions)
- {
- $sqlActionUpdate .= "visit_total_actions = visit_total_actions + 1, ";
- }
-
- $datetimeServer = Piwik_Tracker::getDatetimeFromTimestamp($this->getCurrentTimestamp());
- printDebug("Visit is known (IP = ".Piwik_IP::N2P($this->getVisitorIp()).")");
+ // Set to true when we set some custom variables from the cookie
+ protected $customVariablesSetFromRequest = false;
+
+ /**
+ * @var Piwik_Tracker_GoalManager
+ */
+ protected $goalManager;
+
+ public function __construct($forcedIpString = null, $forcedDateTime = null, $authenticated = false)
+ {
+ $this->timestamp = time();
+ if (!empty($forcedDateTime)) {
+ if (!is_numeric($forcedDateTime)) {
+ $forcedDateTime = strtotime($forcedDateTime);
+ }
+ $this->timestamp = $forcedDateTime;
+ }
+ $ipString = $forcedIpString;
+ if (empty($ipString)) {
+ $ipString = Piwik_IP::getIpFromHeader();
+ }
+
+ $ip = Piwik_IP::P2N($ipString);
+ $this->ip = $ip;
+
+ $this->authenticated = $authenticated;
+ }
+
+ function setForcedVisitorId($visitorId)
+ {
+ $this->forcedVisitorId = $visitorId;
+ }
+
+ function setRequest($requestArray)
+ {
+ $this->request = $requestArray;
+
+ $idsite = Piwik_Common::getRequestVar('idsite', 0, 'int', $this->request);
+ Piwik_PostEvent('Tracker.setRequest.idSite', $idsite, $requestArray);
+ if ($idsite <= 0) {
+ throw new Exception('Invalid idSite');
+ }
+ $this->idsite = $idsite;
+
+ // When the 'url' and referer url parameter are not given, we might be in the 'Simple Image Tracker' mode.
+ // The URL can default to the Referer, which will be in this case
+ // the URL of the page containing the Simple Image beacon
+ if (empty($this->request['urlref'])
+ && empty($this->request['url'])
+ ) {
+ $this->request['url'] = @$_SERVER['HTTP_REFERER'];
+ }
+ }
+
+ /**
+ * Main algorithm to handle the visit.
+ *
+ * Once we have the visitor information, we have to determine if the visit is a new or a known visit.
+ *
+ * 1) When the last action was done more than 30min ago,
+ * or if the visitor is new, then this is a new visit.
+ *
+ * 2) If the last action is less than 30min ago, then the same visit is going on.
+ * Because the visit goes on, we can get the time spent during the last action.
+ *
+ * NB:
+ * - In the case of a new visit, then the time spent
+ * during the last action of the previous visit is unknown.
+ *
+ * - In the case of a new visit but with a known visitor,
+ * we can set the 'returning visitor' flag.
+ *
+ * In all the cases we set a cookie to the visitor with the new information.
+ */
+ public function handle()
+ {
+ // the IP is needed by isExcluded() and GoalManager->recordGoals()
+ $this->visitorInfo['location_ip'] = $this->ip;
+
+ if ($this->isExcluded()) {
+ return;
+ }
+
+ // Anonymize IP (after testing for IP exclusion)
+ $ip = $this->ip;
+ Piwik_PostEvent('Tracker.Visit.setVisitorIp', $ip);
+ $this->visitorInfo['location_ip'] = $ip;
+
+ $this->visitorCustomVariables = self::getCustomVariables($scope = 'visit', $this->request);
+ if (!empty($this->visitorCustomVariables)) {
+ printDebug("Visit level Custom Variables: ");
+ printDebug($this->visitorCustomVariables);
+ $this->customVariablesSetFromRequest = true;
+ }
+
+ $this->goalManager = new Piwik_Tracker_GoalManager();
+
+ $someGoalsConverted = $visitIsConverted = false;
+ $idActionUrl = $idActionName = $actionType = false;
+ $action = null;
+
+ $this->goalManager->init($this->request);
+
+ $requestIsManualGoalConversion = ($this->goalManager->idGoal > 0);
+ $requestIsEcommerce = $this->goalManager->requestIsEcommerce;
+ if ($requestIsEcommerce) {
+ $someGoalsConverted = true;
+
+ // Mark the visit as Converted only if it is an order (not for a Cart update)
+ if ($this->goalManager->isGoalAnOrder) {
+ $visitIsConverted = true;
+ }
+ } // this request is from the JS call to piwikTracker.trackGoal()
+ elseif ($requestIsManualGoalConversion) {
+ $someGoalsConverted = $this->goalManager->detectGoalId($this->idsite);
+ $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) {
+ printDebug('Invalid goal tracking request for goal id = ' . $this->goalManager->idGoal);
+ unset($this->goalManager);
+ return;
+ }
+ } // normal page view, potentially triggering a URL matching goal
+ else {
+ $action = $this->newAction();
+ $this->handleAction($action);
+ $someGoalsConverted = $this->goalManager->detectGoalsMatchingUrl($this->idsite, $action);
+ $visitIsConverted = $someGoalsConverted;
+
+ $action->loadIdActionNameAndUrl();
+ $idActionUrl = $action->getIdActionUrl();
+ if ($idActionUrl !== null) {
+ $idActionUrl = (int)$idActionUrl;
+ }
+ $idActionName = (int)$action->getIdActionName();
+ $actionType = $action->getActionType();
+ }
+
+ // the visitor and session
+ $this->recognizeTheVisitor();
+
+ $isLastActionInTheSameVisit = $this->isLastActionInTheSameVisit();
+
+ if (!$isLastActionInTheSameVisit) {
+ 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
+ // - the visitor doesn't have the Piwik cookie but could be match using heuristics @see recognizeTheVisitor()
+ // )
+ // AND
+ // - the last page view for this visitor was less than 30 minutes ago @see isLastActionInTheSameVisit()
+ if ($this->isVisitorKnown()
+ && $isLastActionInTheSameVisit
+ ) {
+ $idRefererActionUrl = $this->visitorInfo['visit_exit_idaction_url'];
+ $idRefererActionName = $this->visitorInfo['visit_exit_idaction_name'];
+ try {
+ $this->handleKnownVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted);
+ if (!is_null($action)) {
+ $action->record($this->visitorInfo['idvisit'],
+ $this->visitorInfo['idvisitor'],
+ $idRefererActionUrl,
+ $idRefererActionName,
+ $this->visitorInfo['time_spent_ref_action']
+ );
+ }
+ } catch (Piwik_Tracker_Visit_VisitorNotFoundInDatabase $e) {
+
+ // There is an edge case when:
+ // - two manual goal conversions happen in the same second
+ // - which result in handleKnownVisit throwing the exception
+ // 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
+ || $requestIsEcommerce
+ ) {
+ $someGoalsConverted = $visitIsConverted = false;
+ } // When the row wasn't found in the logs, and this is a pageview or
+ // goal matching URL, we force a new visitor
+ else {
+ $this->visitorKnown = false;
+ }
+ }
+ }
+
+ // New visit when:
+ // - the visitor has the Piwik cookie but the last action was performed more than 30 min ago @see isLastActionInTheSameVisit()
+ // - the visitor doesn't have the Piwik cookie, and couldn't be matched in @see recognizeTheVisitor()
+ // - the visitor does have the Piwik cookie but the idcookie and idvisit found in the cookie didn't match to any existing visit in the DB
+ if (!$this->isVisitorKnown()
+ || !$isLastActionInTheSameVisit
+ ) {
+ $this->handleNewVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted);
+ if (!is_null($action)) {
+ $action->record($this->visitorInfo['idvisit'], $this->visitorInfo['idvisitor'], 0, 0, 0);
+ }
+ }
+
+ // update the cookie with the new visit information
+ $this->setThirdPartyCookie();
+
+ // record the goals if applicable
+ if ($someGoalsConverted) {
+ $refererTimestamp = Piwik_Common::getRequestVar('_refts', 0, 'int', $this->request);
+ $refererUrl = Piwik_Common::getRequestVar('_ref', '', 'string', $this->request);
+ $refererCampaignName = trim(urldecode(Piwik_Common::getRequestVar('_rcn', '', 'string', $this->request)));
+ $refererCampaignKeyword = trim(urldecode(Piwik_Common::getRequestVar('_rck', '', 'string', $this->request)));
+
+ $this->goalManager->recordGoals(
+ $this->idsite,
+ $this->visitorInfo,
+ $this->visitorCustomVariables,
+ $action,
+ $refererTimestamp,
+ $refererUrl,
+ $refererCampaignName,
+ $refererCampaignKeyword,
+ $this->getBrowserLanguage()
+ );
+ }
+ unset($this->goalManager);
+ unset($action);
+ $this->printCookie();
+ }
+
+ protected function printCookie()
+ {
+ printDebug($this->cookie);
+ }
+
+ protected function handleAction($action)
+ {
+ $action->setIdSite($this->idsite);
+ $action->setRequest($this->request);
+ $action->setTimestamp($this->getCurrentTimestamp());
+ $action->init();
+
+ if ($this->detectActionIsOutlinkOnAliasHost($action)) {
+ printDebug("Info: The outlink URL host is one of the known host for this website. ");
+ }
+ if (isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) {
+ $type = Piwik_Tracker_Action::getActionTypeName($action->getActionType());
+ printDebug("Action is a $type,
+ Action name = " . $action->getActionName() . ",
+ Action URL = " . $action->getActionUrl());
+ }
+ }
+
+ /**
+ * In the case of a known visit, we have to do the following actions:
+ *
+ * 1) Insert the new action
+ * 2) Update the visit information
+ *
+ * This method triggers two events:
+ *
+ * Tracker.knownVisitorUpdate is triggered before the visit information is updated
+ * Event data is an array with the values to be updated (could be changed by plugins)
+ *
+ * Tracker.knownVisitorInformation is triggered after saving the new visit data
+ * Even data is an array with updated information about the visit
+ * @param $idActionUrl
+ * @param $idActionName
+ * @param $actionType
+ * @param $visitIsConverted
+ * @throws Piwik_Tracker_Visit_VisitorNotFoundInDatabase
+ */
+ protected function handleKnownVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted)
+ {
+ // gather information that needs to be updated
+ $valuesToUpdate = array();
+ $incrementActions = false;
+ $sqlActionUpdate = '';
+
+ if (!empty($idActionName)) {
+ $valuesToUpdate['visit_exit_idaction_name'] = (int)$idActionName;
+ }
+ if ($idActionUrl !== false) {
+ $valuesToUpdate['visit_exit_idaction_url'] = $idActionUrl;
+ $incrementActions = true;
+ }
+ if ($actionType == Piwik_Tracker_Action::TYPE_SITE_SEARCH) {
+ $sqlActionUpdate .= "visit_total_searches = visit_total_searches + 1, ";
+ $incrementActions = true;
+ }
+ if ($incrementActions) {
+ $sqlActionUpdate .= "visit_total_actions = visit_total_actions + 1, ";
+ }
+
+ $datetimeServer = Piwik_Tracker::getDatetimeFromTimestamp($this->getCurrentTimestamp());
+ printDebug("Visit is known (IP = " . Piwik_IP::N2P($this->getVisitorIp()) . ")");
// Add 1 so it's always > 0
- $visitTotalTime = 1 + $this->getCurrentTimestamp() - $this->visitorInfo['visit_first_action_time'];
- $valuesToUpdate['visit_last_action_time'] = $datetimeServer;
- $valuesToUpdate['visit_total_time'] = self::cleanupVisitTotalTime($visitTotalTime);
-
- // Goal conversion
- if($visitIsConverted)
- {
- $valuesToUpdate['visit_goal_converted'] = 1;
- // If a pageview and goal conversion in the same second, with previously a goal conversion recorded
- // the request would not "update" the row since all values are the same as previous
- // therefore the request below throws exception, instead we make sure the UPDATE will affect the row
- $valuesToUpdate['visit_total_time'] = self::cleanupVisitTotalTime(
- $valuesToUpdate['visit_total_time']
- + $this->goalManager->idGoal
- // +2 to offset idgoal=-1 and idgoal=0
- + 2 );
- }
-
- // Might update the idvisitor when it was forced or overwritten for this visit
- if(strlen($this->visitorInfo['idvisitor']) == Piwik_Tracker::LENGTH_BINARY_ID)
- {
- $valuesToUpdate['idvisitor'] = $this->visitorInfo['idvisitor'];
- }
-
- // Ecommerce buyer status
- $valuesToUpdate['visit_goal_buyer'] = $this->goalManager->getBuyerType($this->visitorInfo['visit_goal_buyer']);
-
- // Custom Variables overwrite previous values on each page view
- $valuesToUpdate = array_merge($valuesToUpdate, $this->visitorCustomVariables);
-
- // trigger event before update
- Piwik_PostEvent('Tracker.knownVisitorUpdate', $valuesToUpdate);
-
- // Will be updated in cookie
- $timeSpentRefererAction = $this->getCurrentTimestamp() - $this->visitorInfo['visit_last_action_time'];
- if($timeSpentRefererAction > Piwik_Config::getInstance()->Tracker['visit_standard_length'])
- {
- $timeSpentRefererAction = 0;
- }
- $this->visitorInfo['time_spent_ref_action'] = $timeSpentRefererAction;
-
- // update visitorInfo
- foreach($valuesToUpdate AS $name => $value)
- {
- $this->visitorInfo[$name] = $value;
- }
-
- // build sql query
- $updateParts = $sqlBind = array();
-
- foreach($valuesToUpdate AS $name => $value)
- {
- $updateParts[] = $name." = ?";
- $sqlBind[] = $value;
- }
- $sqlQuery = "UPDATE ". Piwik_Common::prefixTable('log_visit')."
- SET $sqlActionUpdate ".implode($updateParts, ', ')."
+ $visitTotalTime = 1 + $this->getCurrentTimestamp() - $this->visitorInfo['visit_first_action_time'];
+ $valuesToUpdate['visit_last_action_time'] = $datetimeServer;
+ $valuesToUpdate['visit_total_time'] = self::cleanupVisitTotalTime($visitTotalTime);
+
+ // Goal conversion
+ if ($visitIsConverted) {
+ $valuesToUpdate['visit_goal_converted'] = 1;
+ // If a pageview and goal conversion in the same second, with previously a goal conversion recorded
+ // the request would not "update" the row since all values are the same as previous
+ // therefore the request below throws exception, instead we make sure the UPDATE will affect the row
+ $valuesToUpdate['visit_total_time'] = self::cleanupVisitTotalTime(
+ $valuesToUpdate['visit_total_time']
+ + $this->goalManager->idGoal
+ // +2 to offset idgoal=-1 and idgoal=0
+ + 2);
+ }
+
+ // Might update the idvisitor when it was forced or overwritten for this visit
+ if (strlen($this->visitorInfo['idvisitor']) == Piwik_Tracker::LENGTH_BINARY_ID) {
+ $valuesToUpdate['idvisitor'] = $this->visitorInfo['idvisitor'];
+ }
+
+ // Ecommerce buyer status
+ $valuesToUpdate['visit_goal_buyer'] = $this->goalManager->getBuyerType($this->visitorInfo['visit_goal_buyer']);
+
+ // Custom Variables overwrite previous values on each page view
+ $valuesToUpdate = array_merge($valuesToUpdate, $this->visitorCustomVariables);
+
+ // trigger event before update
+ Piwik_PostEvent('Tracker.knownVisitorUpdate', $valuesToUpdate);
+
+ // Will be updated in cookie
+ $timeSpentRefererAction = $this->getCurrentTimestamp() - $this->visitorInfo['visit_last_action_time'];
+ if ($timeSpentRefererAction > Piwik_Config::getInstance()->Tracker['visit_standard_length']) {
+ $timeSpentRefererAction = 0;
+ }
+ $this->visitorInfo['time_spent_ref_action'] = $timeSpentRefererAction;
+
+ // update visitorInfo
+ foreach ($valuesToUpdate AS $name => $value) {
+ $this->visitorInfo[$name] = $value;
+ }
+
+ // build sql query
+ $updateParts = $sqlBind = array();
+
+ foreach ($valuesToUpdate AS $name => $value) {
+ $updateParts[] = $name . " = ?";
+ $sqlBind[] = $value;
+ }
+ $sqlQuery = "UPDATE " . Piwik_Common::prefixTable('log_visit') . "
+ SET $sqlActionUpdate " . implode($updateParts, ', ') . "
WHERE idsite = ?
AND idvisit = ?";
- array_push($sqlBind, $this->idsite, (int)$this->visitorInfo['idvisit'] );
-
- $result = Piwik_Tracker::getDatabase()->query($sqlQuery, $sqlBind);
-
- $this->visitorInfo['visit_last_action_time'] = $this->getCurrentTimestamp();
-
- // Debug output
- if(isset($valuesToUpdate['idvisitor']))
- {
- $valuesToUpdate['idvisitor'] = bin2hex($valuesToUpdate['idvisitor']);
- }
- printDebug('Updating existing visit: '. var_export($valuesToUpdate, true) );
-
- if(Piwik_Tracker::getDatabase()->rowCount($result) == 0)
- {
- printDebug("Visitor with this idvisit wasn't found in the DB.");
- printDebug("$sqlQuery --- ");printDebug($sqlBind);
- throw new Piwik_Tracker_Visit_VisitorNotFoundInDatabase(
- "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");
- }
-
- Piwik_PostEvent('Tracker.knownVisitorInformation', $this->visitorInfo);
- }
-
- protected function isTimestampValid($time)
- {
- return $time <= $this->getCurrentTimestamp()
- && $time > $this->getCurrentTimestamp() - 10*365*86400;
- }
-
- /**
- * In the case of a new visit, we have to do the following actions:
- *
- * 1) Insert the new action
- *
- * 2) Insert the visit information
- * @param $idActionUrl
- * @param $idActionName
- * @param $actionType
- * @param $visitIsConverted
- */
- protected function handleNewVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted)
- {
- printDebug("New Visit (IP = ".Piwik_IP::N2P($this->getVisitorIp()).")");
-
- $localTimes = array(
- 'h' => (string) Piwik_Common::getRequestVar( 'h', $this->getCurrentDate("H"), 'int', $this->request),
- 'i' => (string) Piwik_Common::getRequestVar( 'm', $this->getCurrentDate("i"), 'int', $this->request),
- 's' => (string) Piwik_Common::getRequestVar( 's', $this->getCurrentDate("s"), 'int', $this->request)
- );
- foreach($localTimes as $k => $time)
- {
- if(strlen($time) == 1)
- {
- $localTimes[$k] = '0' . $time;
- }
- }
- $localTime = $localTimes['h'] .':'. $localTimes['i'] .':'. $localTimes['s'];
-
- $idcookie = $this->getVisitorIdcookie();
-
- $defaultTimeOnePageVisit = Piwik_Config::getInstance()->Tracker['default_time_one_page_visit'];
-
- // Days since first visit
- $cookieFirstVisitTimestamp = Piwik_Common::getRequestVar('_idts', 0, 'int', $this->request);
- if(!$this->isTimestampValid($cookieFirstVisitTimestamp))
- {
- $cookieFirstVisitTimestamp = $this->getCurrentTimestamp();
- }
- $daysSinceFirstVisit = round(($this->getCurrentTimestamp() - $cookieFirstVisitTimestamp)/86400, $precision = 0);
- if($daysSinceFirstVisit < 0) $daysSinceFirstVisit = 0;
-
- // Number of Visits
- $visitCount = Piwik_Common::getRequestVar('_idvc', 1, 'int', $this->request);
- if($visitCount < 1) $visitCount = 1;
-
- // Days since last visit
- $daysSinceLastVisit = 0;
- $lastVisitTimestamp = Piwik_Common::getRequestVar('_viewts', 0, 'int', $this->request);
- if($this->isTimestampValid($lastVisitTimestamp))
- {
- $daysSinceLastVisit = round(($this->getCurrentTimestamp() - $lastVisitTimestamp)/86400, $precision = 0);
- if($daysSinceLastVisit < 0) $daysSinceLastVisit = 0;
- }
-
- $daysSinceLastOrder = 0;
- $isReturningCustomer = false;
- $lastOrderTimestamp = Piwik_Common::getRequestVar('_ects', 0, 'int', $this->request);
- if($this->isTimestampValid($lastOrderTimestamp))
- {
- $daysSinceLastOrder = round(($this->getCurrentTimestamp() - $lastOrderTimestamp)/86400, $precision = 0);
- if($daysSinceLastOrder < 0)
- {
- $daysSinceLastOrder = 0;
- }
- $isReturningCustomer = true;
- }
-
- // User settings
- $userInfo = $this->getUserSettingsInformation();
-
- // Referrer data
- $referrer = new Piwik_Tracker_Visit_Referer();
- $refererUrl = Piwik_Common::getRequestVar( 'urlref', '', 'string', $this->request);
- $currentUrl = Piwik_Common::getRequestVar( 'url', '', 'string', $this->request);
- $refererInfo = $referrer->getRefererInformation($refererUrl, $currentUrl, $this->idsite);
-
- $visitorReturning = $isReturningCustomer
- ? 2 /* Returning customer */
- : ($visitCount > 1 || $this->isVisitorKnown() || $daysSinceLastVisit > 0
- ? 1 /* Returning */
- : 0 /* New */ );
- /**
- * Save the visitor
- */
- $this->visitorInfo = array(
- 'idsite' => $this->idsite,
- 'visitor_localtime' => $localTime,
- 'idvisitor' => $idcookie,
- 'visitor_returning' => $visitorReturning,
- 'visitor_count_visits' => $visitCount,
- 'visitor_days_since_last' => $daysSinceLastVisit,
- 'visitor_days_since_order' => $daysSinceLastOrder,
- 'visitor_days_since_first' => $daysSinceFirstVisit,
- 'visit_first_action_time' => Piwik_Tracker::getDatetimeFromTimestamp($this->getCurrentTimestamp()),
- 'visit_last_action_time' => Piwik_Tracker::getDatetimeFromTimestamp($this->getCurrentTimestamp()),
- 'visit_entry_idaction_url' => (int)$idActionUrl,
- 'visit_entry_idaction_name' => (int)$idActionName,
- 'visit_exit_idaction_url' => (int)$idActionUrl,
- 'visit_exit_idaction_name' => (int)$idActionName,
- 'visit_total_actions' => in_array($actionType,
- array(Piwik_Tracker_Action::TYPE_ACTION_URL,
- Piwik_Tracker_Action::TYPE_DOWNLOAD,
- Piwik_Tracker_Action::TYPE_OUTLINK,
- Piwik_Tracker_Action::TYPE_SITE_SEARCH))
- ? 1 : 0, // if visit starts with something else (e.g. ecommerce order), don't record as an action
- 'visit_total_searches' => $actionType == Piwik_Tracker_Action::TYPE_SITE_SEARCH ? 1 : 0,
- 'visit_total_time' => self::cleanupVisitTotalTime($defaultTimeOnePageVisit),
- 'visit_goal_converted' => $visitIsConverted ? 1: 0,
- 'visit_goal_buyer' => $this->goalManager->getBuyerType(),
- 'referer_type' => $refererInfo['referer_type'],
- 'referer_name' => $refererInfo['referer_name'],
- 'referer_url' => $refererInfo['referer_url'],
- 'referer_keyword' => $refererInfo['referer_keyword'],
- 'config_id' => $userInfo['config_id'],
- 'config_os' => $userInfo['config_os'],
- 'config_browser_name' => $userInfo['config_browser_name'],
- 'config_browser_version' => $userInfo['config_browser_version'],
- 'config_resolution' => $userInfo['config_resolution'],
- 'config_pdf' => $userInfo['config_pdf'],
- 'config_flash' => $userInfo['config_flash'],
- 'config_java' => $userInfo['config_java'],
- 'config_director' => $userInfo['config_director'],
- 'config_quicktime' => $userInfo['config_quicktime'],
- 'config_realplayer' => $userInfo['config_realplayer'],
- 'config_windowsmedia' => $userInfo['config_windowsmedia'],
- 'config_gears' => $userInfo['config_gears'],
- 'config_silverlight' => $userInfo['config_silverlight'],
- 'config_cookie' => $userInfo['config_cookie'],
- 'location_ip' => $this->getVisitorIp(),
- 'location_browser_lang' => $userInfo['location_browser_lang'],
- );
-
- // add optional location components
- $location = $this->getVisitorLocation($userInfo['location_browser_lang']);
- $this->updateVisitInfoWithLocation($location);
-
- // Add Custom variable key,value to the visitor array
- $this->visitorInfo = array_merge($this->visitorInfo, $this->visitorCustomVariables);
-
- Piwik_PostEvent('Tracker.newVisitorInformation', $this->visitorInfo);
-
- $debugVisitInfo = $this->visitorInfo;
- $debugVisitInfo['idvisitor'] = bin2hex($debugVisitInfo['idvisitor']);
- $debugVisitInfo['config_id'] = bin2hex($debugVisitInfo['config_id']);
- printDebug($debugVisitInfo);
-
- $this->saveVisitorInformation();
- }
+ array_push($sqlBind, $this->idsite, (int)$this->visitorInfo['idvisit']);
+
+ $result = Piwik_Tracker::getDatabase()->query($sqlQuery, $sqlBind);
+
+ $this->visitorInfo['visit_last_action_time'] = $this->getCurrentTimestamp();
+
+ // Debug output
+ if (isset($valuesToUpdate['idvisitor'])) {
+ $valuesToUpdate['idvisitor'] = bin2hex($valuesToUpdate['idvisitor']);
+ }
+ printDebug('Updating existing visit: ' . var_export($valuesToUpdate, true));
+
+ if (Piwik_Tracker::getDatabase()->rowCount($result) == 0) {
+ printDebug("Visitor with this idvisit wasn't found in the DB.");
+ printDebug("$sqlQuery --- ");
+ printDebug($sqlBind);
+ throw new Piwik_Tracker_Visit_VisitorNotFoundInDatabase(
+ "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");
+ }
+
+ Piwik_PostEvent('Tracker.knownVisitorInformation', $this->visitorInfo);
+ }
+
+ protected function isTimestampValid($time)
+ {
+ return $time <= $this->getCurrentTimestamp()
+ && $time > $this->getCurrentTimestamp() - 10 * 365 * 86400;
+ }
+
+ /**
+ * In the case of a new visit, we have to do the following actions:
+ *
+ * 1) Insert the new action
+ *
+ * 2) Insert the visit information
+ * @param $idActionUrl
+ * @param $idActionName
+ * @param $actionType
+ * @param $visitIsConverted
+ */
+ protected function handleNewVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted)
+ {
+ printDebug("New Visit (IP = " . Piwik_IP::N2P($this->getVisitorIp()) . ")");
+
+ $localTimes = array(
+ 'h' => (string)Piwik_Common::getRequestVar('h', $this->getCurrentDate("H"), 'int', $this->request),
+ 'i' => (string)Piwik_Common::getRequestVar('m', $this->getCurrentDate("i"), 'int', $this->request),
+ 's' => (string)Piwik_Common::getRequestVar('s', $this->getCurrentDate("s"), 'int', $this->request)
+ );
+ foreach ($localTimes as $k => $time) {
+ if (strlen($time) == 1) {
+ $localTimes[$k] = '0' . $time;
+ }
+ }
+ $localTime = $localTimes['h'] . ':' . $localTimes['i'] . ':' . $localTimes['s'];
+
+ $idcookie = $this->getVisitorIdcookie();
+
+ $defaultTimeOnePageVisit = Piwik_Config::getInstance()->Tracker['default_time_one_page_visit'];
+
+ // Days since first visit
+ $cookieFirstVisitTimestamp = Piwik_Common::getRequestVar('_idts', 0, 'int', $this->request);
+ if (!$this->isTimestampValid($cookieFirstVisitTimestamp)) {
+ $cookieFirstVisitTimestamp = $this->getCurrentTimestamp();
+ }
+ $daysSinceFirstVisit = round(($this->getCurrentTimestamp() - $cookieFirstVisitTimestamp) / 86400, $precision = 0);
+ if ($daysSinceFirstVisit < 0) $daysSinceFirstVisit = 0;
+
+ // Number of Visits
+ $visitCount = Piwik_Common::getRequestVar('_idvc', 1, 'int', $this->request);
+ if ($visitCount < 1) $visitCount = 1;
+
+ // Days since last visit
+ $daysSinceLastVisit = 0;
+ $lastVisitTimestamp = Piwik_Common::getRequestVar('_viewts', 0, 'int', $this->request);
+ if ($this->isTimestampValid($lastVisitTimestamp)) {
+ $daysSinceLastVisit = round(($this->getCurrentTimestamp() - $lastVisitTimestamp) / 86400, $precision = 0);
+ if ($daysSinceLastVisit < 0) $daysSinceLastVisit = 0;
+ }
+
+ $daysSinceLastOrder = 0;
+ $isReturningCustomer = false;
+ $lastOrderTimestamp = Piwik_Common::getRequestVar('_ects', 0, 'int', $this->request);
+ if ($this->isTimestampValid($lastOrderTimestamp)) {
+ $daysSinceLastOrder = round(($this->getCurrentTimestamp() - $lastOrderTimestamp) / 86400, $precision = 0);
+ if ($daysSinceLastOrder < 0) {
+ $daysSinceLastOrder = 0;
+ }
+ $isReturningCustomer = true;
+ }
+
+ // User settings
+ $userInfo = $this->getUserSettingsInformation();
+
+ // Referrer data
+ $referrer = new Piwik_Tracker_Visit_Referer();
+ $refererUrl = Piwik_Common::getRequestVar('urlref', '', 'string', $this->request);
+ $currentUrl = Piwik_Common::getRequestVar('url', '', 'string', $this->request);
+ $refererInfo = $referrer->getRefererInformation($refererUrl, $currentUrl, $this->idsite);
+
+ $visitorReturning = $isReturningCustomer
+ ? 2 /* Returning customer */
+ : ($visitCount > 1 || $this->isVisitorKnown() || $daysSinceLastVisit > 0
+ ? 1 /* Returning */
+ : 0 /* New */);
+ /**
+ * Save the visitor
+ */
+ $this->visitorInfo = array(
+ 'idsite' => $this->idsite,
+ 'visitor_localtime' => $localTime,
+ 'idvisitor' => $idcookie,
+ 'visitor_returning' => $visitorReturning,
+ 'visitor_count_visits' => $visitCount,
+ 'visitor_days_since_last' => $daysSinceLastVisit,
+ 'visitor_days_since_order' => $daysSinceLastOrder,
+ 'visitor_days_since_first' => $daysSinceFirstVisit,
+ 'visit_first_action_time' => Piwik_Tracker::getDatetimeFromTimestamp($this->getCurrentTimestamp()),
+ 'visit_last_action_time' => Piwik_Tracker::getDatetimeFromTimestamp($this->getCurrentTimestamp()),
+ 'visit_entry_idaction_url' => (int)$idActionUrl,
+ 'visit_entry_idaction_name' => (int)$idActionName,
+ 'visit_exit_idaction_url' => (int)$idActionUrl,
+ 'visit_exit_idaction_name' => (int)$idActionName,
+ 'visit_total_actions' => in_array($actionType,
+ array(Piwik_Tracker_Action::TYPE_ACTION_URL,
+ Piwik_Tracker_Action::TYPE_DOWNLOAD,
+ Piwik_Tracker_Action::TYPE_OUTLINK,
+ Piwik_Tracker_Action::TYPE_SITE_SEARCH))
+ ? 1 : 0, // if visit starts with something else (e.g. ecommerce order), don't record as an action
+ 'visit_total_searches' => $actionType == Piwik_Tracker_Action::TYPE_SITE_SEARCH ? 1 : 0,
+ 'visit_total_time' => self::cleanupVisitTotalTime($defaultTimeOnePageVisit),
+ 'visit_goal_converted' => $visitIsConverted ? 1 : 0,
+ 'visit_goal_buyer' => $this->goalManager->getBuyerType(),
+ 'referer_type' => $refererInfo['referer_type'],
+ 'referer_name' => $refererInfo['referer_name'],
+ 'referer_url' => $refererInfo['referer_url'],
+ 'referer_keyword' => $refererInfo['referer_keyword'],
+ 'config_id' => $userInfo['config_id'],
+ 'config_os' => $userInfo['config_os'],
+ 'config_browser_name' => $userInfo['config_browser_name'],
+ 'config_browser_version' => $userInfo['config_browser_version'],
+ 'config_resolution' => $userInfo['config_resolution'],
+ 'config_pdf' => $userInfo['config_pdf'],
+ 'config_flash' => $userInfo['config_flash'],
+ 'config_java' => $userInfo['config_java'],
+ 'config_director' => $userInfo['config_director'],
+ 'config_quicktime' => $userInfo['config_quicktime'],
+ 'config_realplayer' => $userInfo['config_realplayer'],
+ 'config_windowsmedia' => $userInfo['config_windowsmedia'],
+ 'config_gears' => $userInfo['config_gears'],
+ 'config_silverlight' => $userInfo['config_silverlight'],
+ 'config_cookie' => $userInfo['config_cookie'],
+ 'location_ip' => $this->getVisitorIp(),
+ 'location_browser_lang' => $userInfo['location_browser_lang'],
+ );
+
+ // add optional location components
+ $location = $this->getVisitorLocation($userInfo['location_browser_lang']);
+ $this->updateVisitInfoWithLocation($location);
+
+ // Add Custom variable key,value to the visitor array
+ $this->visitorInfo = array_merge($this->visitorInfo, $this->visitorCustomVariables);
+
+ Piwik_PostEvent('Tracker.newVisitorInformation', $this->visitorInfo);
+
+ $debugVisitInfo = $this->visitorInfo;
+ $debugVisitInfo['idvisitor'] = bin2hex($debugVisitInfo['idvisitor']);
+ $debugVisitInfo['config_id'] = bin2hex($debugVisitInfo['config_id']);
+ printDebug($debugVisitInfo);
+
+ $this->saveVisitorInformation();
+ }
static private function cleanupVisitTotalTime($t)
{
$t = (int)$t;
$smallintMysqlLimit = 65534;
- if($t > $smallintMysqlLimit) {
+ if ($t > $smallintMysqlLimit) {
$t = $smallintMysqlLimit;
}
return $t;
}
-
- /**
- * Returns the location of the visitor, based on the visitor's IP and browser language.
- *
- * @param string $browserLang
- * @return array See Piwik_UserCountry_LocationProvider::getLocation for more info.
- */
- private function getVisitorLocation( $browserLang )
- {
- $location = array();
- $userInfo = array('lang' => $browserLang, 'ip' => Piwik_IP::N2P($this->getVisitorIp()));
- Piwik_PostEvent('Tracker.getVisitorLocation', $location, $userInfo);
-
- if($this->authenticated)
- {
- // check for location override query parameters (ie, lat, long, country, region, city)
- $locationOverrideParams = array(
- 'country' => array('string', Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY),
- 'region' => array('string', Piwik_UserCountry_LocationProvider::REGION_CODE_KEY),
- 'city' => array('string', Piwik_UserCountry_LocationProvider::CITY_NAME_KEY),
- 'lat' => array('float', Piwik_UserCountry_LocationProvider::LATITUDE_KEY),
- 'long' => array('float', Piwik_UserCountry_LocationProvider::LONGITUDE_KEY),
- );
- foreach ($locationOverrideParams as $queryParamName => $info)
- {
- list($type, $locationResultKey) = $info;
-
- $value = Piwik_Common::getRequestVar($queryParamName, false, $type, $this->request);
- if (!empty($value))
- {
- $location[$locationResultKey] = $value;
- }
- }
- }
-
- if (empty($location['country_code'])) // sanity check
- {
- $location['country_code'] = self::UNKNOWN_CODE;
- }
-
- return $location;
- }
-
- /**
- * Sets visitor info array with location info.
- *
- * @param array $location See Piwik_UserCountry_LocationProvider::getLocation for more info.
- */
- private function updateVisitInfoWithLocation( $location )
- {
- static $logVisitToLowerLocationMapping = array(
- 'location_country' => Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY,
- );
-
- static $logVisitToLocationMapping = array(
- 'location_region' => Piwik_UserCountry_LocationProvider::REGION_CODE_KEY,
- 'location_city' => Piwik_UserCountry_LocationProvider::CITY_NAME_KEY,
- 'location_latitude' => Piwik_UserCountry_LocationProvider::LATITUDE_KEY,
- 'location_longitude' => Piwik_UserCountry_LocationProvider::LONGITUDE_KEY,
- );
-
- foreach ($logVisitToLowerLocationMapping as $column => $locationKey)
- {
- if (!empty($location[$locationKey]))
- {
- $this->visitorInfo[$column] = strtolower($location[$locationKey]);
- }
- }
-
- foreach ($logVisitToLocationMapping as $column => $locationKey)
- {
- if (!empty($location[$locationKey]))
- {
- $this->visitorInfo[$column] = $location[$locationKey];
- }
- }
-
- // if the location has provider/organization info, set it
- if (!empty($location[Piwik_UserCountry_LocationProvider::ISP_KEY]))
- {
- $providerValue = $location[Piwik_UserCountry_LocationProvider::ISP_KEY];
-
- // if the org is set and not the same as the isp, add it to the provider value
- if (!empty($location[Piwik_UserCountry_LocationProvider::ORG_KEY])
- && $location[Piwik_UserCountry_LocationProvider::ORG_KEY] != $providerValue)
- {
- $providerValue .= ' - ' . $location[Piwik_UserCountry_LocationProvider::ORG_KEY];
- }
- }
- else if (!empty($location[Piwik_UserCountry_LocationProvider::ORG_KEY]))
- {
- $providerValue = $location[Piwik_UserCountry_LocationProvider::ORG_KEY];
- }
-
- if (isset($providerValue))
- {
- $this->visitorInfo['location_provider'] = $providerValue;
- }
- }
-
- /**
- * Save new visitor information to log_visit table.
- * Provides pre- and post- event hooks (Tracker.saveVisitorInformation and Tracker.saveVisitorInformation.end) for plugins
- */
- protected function saveVisitorInformation()
- {
- Piwik_PostEvent('Tracker.saveVisitorInformation', $this->visitorInfo);
-
- $this->visitorInfo['location_browser_lang'] = substr($this->visitorInfo['location_browser_lang'], 0, 20);
- $this->visitorInfo['referer_name'] = substr($this->visitorInfo['referer_name'], 0, 70);
- $this->visitorInfo['referer_keyword'] = substr($this->visitorInfo['referer_keyword'], 0, 255);
- $this->visitorInfo['config_resolution'] = substr($this->visitorInfo['config_resolution'], 0, 9);
-
- $fields = implode(", ", array_keys($this->visitorInfo));
- $values = Piwik_Common::getSqlStringFieldsArray($this->visitorInfo);
-
- $sql = "INSERT INTO ".Piwik_Common::prefixTable('log_visit'). " ($fields) VALUES ($values)";
- $bind = array_values($this->visitorInfo);
- Piwik_Tracker::getDatabase()->query( $sql, $bind);
-
- $idVisit = Piwik_Tracker::getDatabase()->lastInsertId();
- $this->visitorInfo['idvisit'] = $idVisit;
-
- $this->visitorInfo['visit_first_action_time'] = $this->getCurrentTimestamp();
- $this->visitorInfo['visit_last_action_time'] = $this->getCurrentTimestamp();
-
- Piwik_PostEvent('Tracker.saveVisitorInformation.end', $this->visitorInfo);
- }
-
- /**
- * Returns visitor cookie
- *
- * @return binary
- */
- protected function getVisitorIdcookie()
- {
- if($this->isVisitorKnown())
- {
- return $this->visitorInfo['idvisitor'];
- }
- // If the visitor had a first party ID cookie, then we use this value
- if(!empty($this->visitorInfo['idvisitor'])
- && strlen($this->visitorInfo['idvisitor']) == Piwik_Tracker::LENGTH_BINARY_ID)
- {
- return $this->visitorInfo['idvisitor'];
- }
+
+ /**
+ * Returns the location of the visitor, based on the visitor's IP and browser language.
+ *
+ * @param string $browserLang
+ * @return array See Piwik_UserCountry_LocationProvider::getLocation for more info.
+ */
+ private function getVisitorLocation($browserLang)
+ {
+ $location = array();
+ $userInfo = array('lang' => $browserLang, 'ip' => Piwik_IP::N2P($this->getVisitorIp()));
+ Piwik_PostEvent('Tracker.getVisitorLocation', $location, $userInfo);
+
+ if ($this->authenticated) {
+ // check for location override query parameters (ie, lat, long, country, region, city)
+ $locationOverrideParams = array(
+ 'country' => array('string', Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY),
+ 'region' => array('string', Piwik_UserCountry_LocationProvider::REGION_CODE_KEY),
+ 'city' => array('string', Piwik_UserCountry_LocationProvider::CITY_NAME_KEY),
+ 'lat' => array('float', Piwik_UserCountry_LocationProvider::LATITUDE_KEY),
+ 'long' => array('float', Piwik_UserCountry_LocationProvider::LONGITUDE_KEY),
+ );
+ foreach ($locationOverrideParams as $queryParamName => $info) {
+ list($type, $locationResultKey) = $info;
+
+ $value = Piwik_Common::getRequestVar($queryParamName, false, $type, $this->request);
+ if (!empty($value)) {
+ $location[$locationResultKey] = $value;
+ }
+ }
+ }
+
+ if (empty($location['country_code'])) // sanity check
+ {
+ $location['country_code'] = self::UNKNOWN_CODE;
+ }
+
+ return $location;
+ }
+
+ /**
+ * Sets visitor info array with location info.
+ *
+ * @param array $location See Piwik_UserCountry_LocationProvider::getLocation for more info.
+ */
+ private function updateVisitInfoWithLocation($location)
+ {
+ static $logVisitToLowerLocationMapping = array(
+ 'location_country' => Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY,
+ );
+
+ static $logVisitToLocationMapping = array(
+ 'location_region' => Piwik_UserCountry_LocationProvider::REGION_CODE_KEY,
+ 'location_city' => Piwik_UserCountry_LocationProvider::CITY_NAME_KEY,
+ 'location_latitude' => Piwik_UserCountry_LocationProvider::LATITUDE_KEY,
+ 'location_longitude' => Piwik_UserCountry_LocationProvider::LONGITUDE_KEY,
+ );
+
+ foreach ($logVisitToLowerLocationMapping as $column => $locationKey) {
+ if (!empty($location[$locationKey])) {
+ $this->visitorInfo[$column] = strtolower($location[$locationKey]);
+ }
+ }
+
+ foreach ($logVisitToLocationMapping as $column => $locationKey) {
+ if (!empty($location[$locationKey])) {
+ $this->visitorInfo[$column] = $location[$locationKey];
+ }
+ }
+
+ // if the location has provider/organization info, set it
+ if (!empty($location[Piwik_UserCountry_LocationProvider::ISP_KEY])) {
+ $providerValue = $location[Piwik_UserCountry_LocationProvider::ISP_KEY];
+
+ // if the org is set and not the same as the isp, add it to the provider value
+ if (!empty($location[Piwik_UserCountry_LocationProvider::ORG_KEY])
+ && $location[Piwik_UserCountry_LocationProvider::ORG_KEY] != $providerValue
+ ) {
+ $providerValue .= ' - ' . $location[Piwik_UserCountry_LocationProvider::ORG_KEY];
+ }
+ } else if (!empty($location[Piwik_UserCountry_LocationProvider::ORG_KEY])) {
+ $providerValue = $location[Piwik_UserCountry_LocationProvider::ORG_KEY];
+ }
+
+ if (isset($providerValue)) {
+ $this->visitorInfo['location_provider'] = $providerValue;
+ }
+ }
+
+ /**
+ * Save new visitor information to log_visit table.
+ * Provides pre- and post- event hooks (Tracker.saveVisitorInformation and Tracker.saveVisitorInformation.end) for plugins
+ */
+ protected function saveVisitorInformation()
+ {
+ Piwik_PostEvent('Tracker.saveVisitorInformation', $this->visitorInfo);
+
+ $this->visitorInfo['location_browser_lang'] = substr($this->visitorInfo['location_browser_lang'], 0, 20);
+ $this->visitorInfo['referer_name'] = substr($this->visitorInfo['referer_name'], 0, 70);
+ $this->visitorInfo['referer_keyword'] = substr($this->visitorInfo['referer_keyword'], 0, 255);
+ $this->visitorInfo['config_resolution'] = substr($this->visitorInfo['config_resolution'], 0, 9);
+
+ $fields = implode(", ", array_keys($this->visitorInfo));
+ $values = Piwik_Common::getSqlStringFieldsArray($this->visitorInfo);
+
+ $sql = "INSERT INTO " . Piwik_Common::prefixTable('log_visit') . " ($fields) VALUES ($values)";
+ $bind = array_values($this->visitorInfo);
+ Piwik_Tracker::getDatabase()->query($sql, $bind);
+
+ $idVisit = Piwik_Tracker::getDatabase()->lastInsertId();
+ $this->visitorInfo['idvisit'] = $idVisit;
+
+ $this->visitorInfo['visit_first_action_time'] = $this->getCurrentTimestamp();
+ $this->visitorInfo['visit_last_action_time'] = $this->getCurrentTimestamp();
+
+ Piwik_PostEvent('Tracker.saveVisitorInformation.end', $this->visitorInfo);
+ }
+
+ /**
+ * Returns visitor cookie
+ *
+ * @return binary
+ */
+ protected function getVisitorIdcookie()
+ {
+ if ($this->isVisitorKnown()) {
+ return $this->visitorInfo['idvisitor'];
+ }
+ // If the visitor had a first party ID cookie, then we use this value
+ if (!empty($this->visitorInfo['idvisitor'])
+ && strlen($this->visitorInfo['idvisitor']) == Piwik_Tracker::LENGTH_BINARY_ID
+ ) {
+ return $this->visitorInfo['idvisitor'];
+ }
return Piwik_Common::hex2bin($this->generateUniqueVisitorId());
}
@@ -782,333 +735,310 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
}
/**
- * Returns the visitor's IP address
- *
- * @return long
- */
- protected function getVisitorIp()
- {
- return $this->visitorInfo['location_ip'];
- }
-
- /**
- * Returns the visitor's browser (user agent)
- *
- * @return string
- */
- static public function getUserAgent($request)
- {
- $default = @$_SERVER['HTTP_USER_AGENT'];
- return Piwik_Common::getRequestVar('ua', is_null($default) ? false : $default, 'string', $request);
- }
-
- /**
- * Returns the language the visitor is viewing.
- *
- * @return string browser language code, eg. "en-gb,en;q=0.5"
- */
- protected function getBrowserLanguage()
- {
- return Piwik_Common::getRequestVar('lang', Piwik_Common::getBrowserLanguage(), 'string', $this->request);
- }
-
- /**
- * Returns the current date in the "Y-m-d" PHP format
- *
- * @param string $format
- * @return string
- */
- protected function getCurrentDate( $format = "Y-m-d")
- {
- return date($format, $this->getCurrentTimestamp() );
- }
-
- /**
- * Returns the current Timestamp
- *
- * @return int
- */
- protected function getCurrentTimestamp()
- {
- return $this->timestamp;
- }
-
- /**
- * Test if the current visitor is excluded from the statistics.
- *
- * Plugins can for example exclude visitors based on the
- * - IP
- * - If a given cookie is found
- *
- * @return bool True if the visit must not be saved, false otherwise
- */
- protected function isExcluded()
- {
- $excluded = false;
-
- $ip = $this->getVisitorIp();
- $ua = $this->getUserAgent($this->request);
-
- /*
- * Live/Bing/MSN bot and Googlebot are evolving to detect cloaked websites.
- * As a result, these sophisticated bots exhibit characteristics of
- * browsers (cookies enabled, executing JavaScript, etc).
- */
- $allowBots = Piwik_Common::getRequestVar('bots', false) != false;
- if ( !$allowBots
- && (strpos($ua, 'Googlebot') !== false // Googlebot
- || strpos($ua, 'Google Web Preview') !== false // Google Instant
- || strpos($ua, 'bingbot') !== false // Bingbot
- || strpos($ua, 'YottaaMonitor') !== false // Yottaa
- || Piwik_IP::isIpInRange($ip,
- array(
- // Live/Bing/MSN
- '64.4.0.0/18',
- '65.52.0.0/14',
- '157.54.0.0/15',
- '157.56.0.0/14',
- '157.60.0.0/16',
- '207.46.0.0/16',
- '207.68.128.0/18',
- '207.68.192.0/20',
- '131.253.26.0/20',
- '131.253.24.0/20',
- // Yahoo
- '72.30.198.0/20',
- '72.30.196.0/20',
- '98.137.207.0/20',
- // Chinese bot hammering websites
- '1.202.218.8'
- ))))
- {
- printDebug('Search bot detected, visit excluded');
- $excluded = true;
- }
-
- /*
- * Requests built with piwik.js will contain a rec=1 parameter. This is used as
- * an indication that the request is made by a JS enabled device. By default, Piwik
- * doesn't track non-JS visitors.
- */
- if(!$excluded)
- {
- $parameterForceRecord = 'rec';
- $toRecord = Piwik_Common::getRequestVar($parameterForceRecord, false, 'int', $this->request);
- if(!$toRecord)
- {
- printDebug(@$_SERVER['REQUEST_METHOD'].' parameter '.$parameterForceRecord.' not found in URL, request excluded');
- $excluded = true;
- printDebug("'$parameterForceRecord' parameter not found.");
- }
- }
-
- /* custom filters can override the built-in filters above */
- Piwik_PostEvent('Tracker.Visit.isExcluded', $excluded);
-
- /*
- * Following exclude operations happen after the hook.
- * These are of higher priority and should not be overwritten by plugins.
- */
-
- // Checking if the Piwik ignore cookie is set
- if(!$excluded)
- {
- $excluded = $this->isIgnoreCookieFound();
- if($excluded)
- {
- printDebug("Ignore cookie found.");
- }
- }
-
- // Checking for excluded IPs
- if(!$excluded)
- {
- $excluded = $this->isVisitorIpExcluded($ip);
- if($excluded)
- {
- printDebug("IP excluded.");
- }
- }
-
- // Check if user agent should be excluded
- if (!$excluded)
- {
- $excluded = $this->isUserAgentExcluded($ua);
- if ($excluded)
- {
- printDebug("User agent excluded.");
- }
- }
-
- if(!$excluded)
- {
- if( (isset($_SERVER["HTTP_X_PURPOSE"])
- && in_array($_SERVER["HTTP_X_PURPOSE"], array("preview", "instant")))
- || (isset($_SERVER['HTTP_X_MOZ'])
- && $_SERVER['HTTP_X_MOZ'] == "prefetch"))
- {
- $excluded = true;
- printDebug("Prefetch request detected, not a real visit so we Ignore this visit/pageview");
- }
- }
-
- if($excluded)
- {
- printDebug("Visitor excluded.");
- return true;
- }
-
- return false;
- }
-
- /**
- * Looks for the ignore cookie that users can set in the Piwik admin screen.
- * @return bool
- */
- protected function isIgnoreCookieFound()
- {
- if(Piwik_Tracker_IgnoreCookie::isIgnoreCookieFound())
- {
- printDebug('Piwik ignore cookie was found, visit not tracked.');
- return true;
- }
- return false;
- }
-
- /**
- * Checks if the visitor ip is in the excluded list
- *
- * @param string $ip Long IP
- * @return bool
- */
- protected function isVisitorIpExcluded($ip)
- {
- $websiteAttributes = Piwik_Tracker_Cache::getCacheWebsiteAttributes( $this->idsite );
- if(!empty($websiteAttributes['excluded_ips']))
- {
- if(Piwik_IP::isIpInRange($ip, $websiteAttributes['excluded_ips']))
- {
- printDebug('Visitor IP '.Piwik_IP::N2P($ip).' is excluded from being tracked');
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns true if the specified user agent should be excluded for the current site or not.
- *
- * Visits whose user agent string contains one of the excluded_user_agents strings for the
- * site being tracked (or one of the global strings) will be excluded.
- *
- * @param string $ua The user agent string.
- * @return bool
- */
- protected function isUserAgentExcluded( $ua )
- {
- $websiteAttributes = Piwik_Tracker_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
- if (stripos($ua, $excludedUserAgent) !== false)
- {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Returns the cookie name used for the Piwik Tracker cookie
- *
- * @return string
- */
- protected function getCookieName()
- {
- return Piwik_Config::getInstance()->Tracker['cookie_name'];
- }
-
- /**
- * Returns the cookie expiration date.
- *
- * @return int
- */
- protected function getCookieExpire()
- {
- return $this->getCurrentTimestamp() + Piwik_Config::getInstance()->Tracker['cookie_expire'];
- }
-
- /**
- * Returns cookie path
- *
- * @return string
- */
- protected function getCookiePath()
- {
- return Piwik_Config::getInstance()->Tracker['cookie_path'];
- }
-
- protected function shouldUseThirdPartyCookie()
- {
- return (bool)Piwik_Config::getInstance()->Tracker['use_third_party_id_cookie'];
- }
-
- /**
- * Is the request for a known VisitorId, based on 1st party, 3rd party (optional) cookies or Tracking API forced Visitor ID
- * @throws Exception
- */
- protected function assignVisitorIdFromRequest()
- {
- $found = false;
-
- // Was a Visitor ID "forced" (@see Tracking API setVisitorId()) for this request?
- $idVisitor = $this->getForcedVisitorId();
- if(!empty($idVisitor))
- {
- if(strlen($idVisitor) != Piwik_Tracker::LENGTH_HEX_ID_STRING)
- {
- throw new Exception("Visitor ID (cid) $idVisitor must be ".Piwik_Tracker::LENGTH_HEX_ID_STRING." characters long");
- }
- printDebug("Request will be recorded for this idvisitor = ".$idVisitor);
- $found = true;
- }
-
- // - If set to use 3rd party cookies for Visit ID, read the cookie
- if(!$found)
- {
- // - By default, reads the first party cookie ID
- $useThirdPartyCookie = $this->shouldUseThirdPartyCookie();
- if($useThirdPartyCookie)
- {
- $idVisitor = $this->cookie->get(0);
- if($idVisitor !== false
- && strlen($idVisitor) == Piwik_Tracker::LENGTH_HEX_ID_STRING)
- {
- $found = true;
- }
- }
- }
- // If a third party cookie was not found, we default to the first party cookie
- if(!$found)
- {
- $idVisitor = Piwik_Common::getRequestVar('_id', '', 'string', $this->request);
- $found = strlen($idVisitor) >= Piwik_Tracker::LENGTH_HEX_ID_STRING;
- }
-
- if( $found )
- {
- $truncated = substr($idVisitor, 0, Piwik_Tracker::LENGTH_HEX_ID_STRING);
- $binVisitorId = @Piwik_Common::hex2bin($truncated);
- if(!empty($binVisitorId))
- {
- $this->visitorInfo['idvisitor'] = $binVisitorId;
- }
-
- }
- }
+ * Returns the visitor's IP address
+ *
+ * @return long
+ */
+ protected function getVisitorIp()
+ {
+ return $this->visitorInfo['location_ip'];
+ }
+
+ /**
+ * Returns the visitor's browser (user agent)
+ *
+ * @return string
+ */
+ static public function getUserAgent($request)
+ {
+ $default = @$_SERVER['HTTP_USER_AGENT'];
+ return Piwik_Common::getRequestVar('ua', is_null($default) ? false : $default, 'string', $request);
+ }
+
+ /**
+ * Returns the language the visitor is viewing.
+ *
+ * @return string browser language code, eg. "en-gb,en;q=0.5"
+ */
+ protected function getBrowserLanguage()
+ {
+ return Piwik_Common::getRequestVar('lang', Piwik_Common::getBrowserLanguage(), 'string', $this->request);
+ }
+
+ /**
+ * Returns the current date in the "Y-m-d" PHP format
+ *
+ * @param string $format
+ * @return string
+ */
+ protected function getCurrentDate($format = "Y-m-d")
+ {
+ return date($format, $this->getCurrentTimestamp());
+ }
+
+ /**
+ * Returns the current Timestamp
+ *
+ * @return int
+ */
+ protected function getCurrentTimestamp()
+ {
+ return $this->timestamp;
+ }
+
+ /**
+ * Test if the current visitor is excluded from the statistics.
+ *
+ * Plugins can for example exclude visitors based on the
+ * - IP
+ * - If a given cookie is found
+ *
+ * @return bool True if the visit must not be saved, false otherwise
+ */
+ protected function isExcluded()
+ {
+ $excluded = false;
+
+ $ip = $this->getVisitorIp();
+ $ua = $this->getUserAgent($this->request);
+
+ /*
+ * Live/Bing/MSN bot and Googlebot are evolving to detect cloaked websites.
+ * As a result, these sophisticated bots exhibit characteristics of
+ * browsers (cookies enabled, executing JavaScript, etc).
+ */
+ $allowBots = Piwik_Common::getRequestVar('bots', false) != false;
+ if (!$allowBots
+ && (strpos($ua, 'Googlebot') !== false // Googlebot
+ || strpos($ua, 'Google Web Preview') !== false // Google Instant
+ || strpos($ua, 'bingbot') !== false // Bingbot
+ || strpos($ua, 'YottaaMonitor') !== false // Yottaa
+ || Piwik_IP::isIpInRange($ip,
+ array(
+ // Live/Bing/MSN
+ '64.4.0.0/18',
+ '65.52.0.0/14',
+ '157.54.0.0/15',
+ '157.56.0.0/14',
+ '157.60.0.0/16',
+ '207.46.0.0/16',
+ '207.68.128.0/18',
+ '207.68.192.0/20',
+ '131.253.26.0/20',
+ '131.253.24.0/20',
+ // Yahoo
+ '72.30.198.0/20',
+ '72.30.196.0/20',
+ '98.137.207.0/20',
+ // Chinese bot hammering websites
+ '1.202.218.8'
+ )))
+ ) {
+ printDebug('Search bot detected, visit excluded');
+ $excluded = true;
+ }
+
+ /*
+ * Requests built with piwik.js will contain a rec=1 parameter. This is used as
+ * an indication that the request is made by a JS enabled device. By default, Piwik
+ * doesn't track non-JS visitors.
+ */
+ if (!$excluded) {
+ $parameterForceRecord = 'rec';
+ $toRecord = Piwik_Common::getRequestVar($parameterForceRecord, false, 'int', $this->request);
+ if (!$toRecord) {
+ printDebug(@$_SERVER['REQUEST_METHOD'] . ' parameter ' . $parameterForceRecord . ' not found in URL, request excluded');
+ $excluded = true;
+ printDebug("'$parameterForceRecord' parameter not found.");
+ }
+ }
+
+ /* custom filters can override the built-in filters above */
+ Piwik_PostEvent('Tracker.Visit.isExcluded', $excluded);
+
+ /*
+ * Following exclude operations happen after the hook.
+ * These are of higher priority and should not be overwritten by plugins.
+ */
+
+ // Checking if the Piwik ignore cookie is set
+ if (!$excluded) {
+ $excluded = $this->isIgnoreCookieFound();
+ if ($excluded) {
+ printDebug("Ignore cookie found.");
+ }
+ }
+
+ // Checking for excluded IPs
+ if (!$excluded) {
+ $excluded = $this->isVisitorIpExcluded($ip);
+ if ($excluded) {
+ printDebug("IP excluded.");
+ }
+ }
+
+ // Check if user agent should be excluded
+ if (!$excluded) {
+ $excluded = $this->isUserAgentExcluded($ua);
+ if ($excluded) {
+ printDebug("User agent excluded.");
+ }
+ }
+
+ if (!$excluded) {
+ if ((isset($_SERVER["HTTP_X_PURPOSE"])
+ && in_array($_SERVER["HTTP_X_PURPOSE"], array("preview", "instant")))
+ || (isset($_SERVER['HTTP_X_MOZ'])
+ && $_SERVER['HTTP_X_MOZ'] == "prefetch")
+ ) {
+ $excluded = true;
+ printDebug("Prefetch request detected, not a real visit so we Ignore this visit/pageview");
+ }
+ }
+
+ if ($excluded) {
+ printDebug("Visitor excluded.");
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Looks for the ignore cookie that users can set in the Piwik admin screen.
+ * @return bool
+ */
+ protected function isIgnoreCookieFound()
+ {
+ if (Piwik_Tracker_IgnoreCookie::isIgnoreCookieFound()) {
+ printDebug('Piwik ignore cookie was found, visit not tracked.');
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the visitor ip is in the excluded list
+ *
+ * @param string $ip Long IP
+ * @return bool
+ */
+ protected function isVisitorIpExcluded($ip)
+ {
+ $websiteAttributes = Piwik_Tracker_Cache::getCacheWebsiteAttributes($this->idsite);
+ if (!empty($websiteAttributes['excluded_ips'])) {
+ if (Piwik_IP::isIpInRange($ip, $websiteAttributes['excluded_ips'])) {
+ printDebug('Visitor IP ' . Piwik_IP::N2P($ip) . ' is excluded from being tracked');
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the specified user agent should be excluded for the current site or not.
+ *
+ * Visits whose user agent string contains one of the excluded_user_agents strings for the
+ * site being tracked (or one of the global strings) will be excluded.
+ *
+ * @param string $ua The user agent string.
+ * @return bool
+ */
+ protected function isUserAgentExcluded($ua)
+ {
+ $websiteAttributes = Piwik_Tracker_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
+ if (stripos($ua, $excludedUserAgent) !== false) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the cookie name used for the Piwik Tracker cookie
+ *
+ * @return string
+ */
+ protected function getCookieName()
+ {
+ return Piwik_Config::getInstance()->Tracker['cookie_name'];
+ }
+
+ /**
+ * Returns the cookie expiration date.
+ *
+ * @return int
+ */
+ protected function getCookieExpire()
+ {
+ return $this->getCurrentTimestamp() + Piwik_Config::getInstance()->Tracker['cookie_expire'];
+ }
+
+ /**
+ * Returns cookie path
+ *
+ * @return string
+ */
+ protected function getCookiePath()
+ {
+ return Piwik_Config::getInstance()->Tracker['cookie_path'];
+ }
+
+ protected function shouldUseThirdPartyCookie()
+ {
+ return (bool)Piwik_Config::getInstance()->Tracker['use_third_party_id_cookie'];
+ }
+
+ /**
+ * Is the request for a known VisitorId, based on 1st party, 3rd party (optional) cookies or Tracking API forced Visitor ID
+ * @throws Exception
+ */
+ protected function assignVisitorIdFromRequest()
+ {
+ $found = false;
+
+ // Was a Visitor ID "forced" (@see Tracking API setVisitorId()) for this request?
+ $idVisitor = $this->getForcedVisitorId();
+ if (!empty($idVisitor)) {
+ if (strlen($idVisitor) != Piwik_Tracker::LENGTH_HEX_ID_STRING) {
+ throw new Exception("Visitor ID (cid) $idVisitor must be " . Piwik_Tracker::LENGTH_HEX_ID_STRING . " characters long");
+ }
+ printDebug("Request will be recorded for this idvisitor = " . $idVisitor);
+ $found = true;
+ }
+
+ // - If set to use 3rd party cookies for Visit ID, read the cookie
+ if (!$found) {
+ // - By default, reads the first party cookie ID
+ $useThirdPartyCookie = $this->shouldUseThirdPartyCookie();
+ if ($useThirdPartyCookie) {
+ $idVisitor = $this->cookie->get(0);
+ if ($idVisitor !== false
+ && strlen($idVisitor) == Piwik_Tracker::LENGTH_HEX_ID_STRING
+ ) {
+ $found = true;
+ }
+ }
+ }
+ // If a third party cookie was not found, we default to the first party cookie
+ if (!$found) {
+ $idVisitor = Piwik_Common::getRequestVar('_id', '', 'string', $this->request);
+ $found = strlen($idVisitor) >= Piwik_Tracker::LENGTH_HEX_ID_STRING;
+ }
+
+ if ($found) {
+ $truncated = substr($idVisitor, 0, Piwik_Tracker::LENGTH_HEX_ID_STRING);
+ $binVisitorId = @Piwik_Common::hex2bin($truncated);
+ if (!empty($binVisitorId)) {
+ $this->visitorInfo['idvisitor'] = $binVisitorId;
+ }
+
+ }
+ }
protected function getForcedVisitorId()
{
@@ -1116,49 +1046,45 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
}
/**
- * This methods tries to see if the visitor has visited the website before.
- *
- * We have to split the visitor into one of the category
- * - Known visitor
- * - New visitor
- */
- protected function recognizeTheVisitor()
- {
- $this->visitorKnown = false;
- $this->setCookie( new Piwik_Cookie(
- $this->getCookieName(),
- $this->getCookieExpire(),
- $this->getCookiePath()) );
- $this->printCookie();
-
- $userInfo = $this->getUserSettingsInformation();
- $configId = $userInfo['config_id'];
-
- $this->assignVisitorIdFromRequest();
- $isVisitorIdToLookup = !empty($this->visitorInfo['idvisitor']);
-
- if($isVisitorIdToLookup)
- {
- printDebug("Matching visitors with: visitorId=".bin2hex($this->visitorInfo['idvisitor'])." OR configId=".bin2hex($configId));
- }
- else
- {
- 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
- if(!$this->customVariablesSetFromRequest)
- {
- $selectCustomVariables = ',
+ * This methods tries to see if the visitor has visited the website before.
+ *
+ * We have to split the visitor into one of the category
+ * - Known visitor
+ * - New visitor
+ */
+ protected function recognizeTheVisitor()
+ {
+ $this->visitorKnown = false;
+ $this->setCookie(new Piwik_Cookie(
+ $this->getCookieName(),
+ $this->getCookieExpire(),
+ $this->getCookiePath()));
+ $this->printCookie();
+
+ $userInfo = $this->getUserSettingsInformation();
+ $configId = $userInfo['config_id'];
+
+ $this->assignVisitorIdFromRequest();
+ $isVisitorIdToLookup = !empty($this->visitorInfo['idvisitor']);
+
+ if ($isVisitorIdToLookup) {
+ printDebug("Matching visitors with: visitorId=" . bin2hex($this->visitorInfo['idvisitor']) . " OR configId=" . bin2hex($configId));
+ } else {
+ 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
+ if (!$this->customVariablesSetFromRequest) {
+ $selectCustomVariables = ',
custom_var_k1, custom_var_v1,
custom_var_k2, custom_var_v2,
custom_var_k3, custom_var_v3,
custom_var_k4, custom_var_v4,
custom_var_k5, custom_var_v5';
- }
-
- $select = "SELECT idvisitor,
+ }
+
+ $select = "SELECT idvisitor,
visit_last_action_time,
visit_first_action_time,
idvisit,
@@ -1179,9 +1105,9 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
visit_goal_buyer
$selectCustomVariables
";
- $from = "FROM ".Piwik_Common::prefixTable('log_visit');
+ $from = "FROM " . Piwik_Common::prefixTable('log_visit');
- $bindSql = array();
+ $bindSql = array();
$timeLookBack = $this->getWindowLookupPreviousVisit();
@@ -1189,142 +1115,132 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
$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, etc.
- // We can use config_id heuristics to try find the visitor in the 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),
+ // 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, etc.
+ // We can use config_id heuristics to try find the visitor in the 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
- if($shouldMatchOneFieldOnly)
- {
- $where = "visit_last_action_time >= ? AND idsite = ?";
- $bindSql[] = $timeLookBack;
- $bindSql[] = $this->idsite;
- if(!$isVisitorIdToLookup)
- {
- $where .= ' AND config_id = ?';
- $bindSql[] = $configId;
- }
- else
- {
- $where .= ' AND idvisitor = ?';
- $bindSql[] = $this->visitorInfo['idvisitor'];
- }
-
- $sql = "$select
+ if ($shouldMatchOneFieldOnly) {
+ $where = "visit_last_action_time >= ? AND idsite = ?";
+ $bindSql[] = $timeLookBack;
+ $bindSql[] = $this->idsite;
+ if (!$isVisitorIdToLookup) {
+ $where .= ' AND config_id = ?';
+ $bindSql[] = $configId;
+ } else {
+ $where .= ' AND idvisitor = ?';
+ $bindSql[] = $this->visitorInfo['idvisitor'];
+ }
+
+ $sql = "$select
$from
- WHERE ".$where."
+ WHERE " . $where . "
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
- {
- $whereSameBothQueries = "visit_last_action_time >= ? AND idsite = ?";
-
-
- // will use INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time)
- $bindSql[] = $timeLookBack;
- $bindSql[] = $this->idsite;
- $where = ' AND config_id = ?';
- $bindSql[] = $configId;
- $sqlConfigId = "$select ,
+ } // 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 {
+ $whereSameBothQueries = "visit_last_action_time >= ? AND idsite = ?";
+
+
+ // will use INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time)
+ $bindSql[] = $timeLookBack;
+ $bindSql[] = $this->idsite;
+ $where = ' AND config_id = ?';
+ $bindSql[] = $configId;
+ $sqlConfigId = "$select ,
0 as priority
$from
WHERE $whereSameBothQueries $where
ORDER BY visit_last_action_time DESC
LIMIT 1
";
-
- // will use INDEX index_idsite_idvisitor (idsite, idvisitor)
- $bindSql[] = $timeLookBack;
- $bindSql[] = $this->idsite;
- $where = ' AND idvisitor = ?';
- $bindSql[] = $this->visitorInfo['idvisitor'];
- $sqlVisitorId = "$select ,
+
+ // will use INDEX index_idsite_idvisitor (idsite, idvisitor)
+ $bindSql[] = $timeLookBack;
+ $bindSql[] = $this->idsite;
+ $where = ' AND idvisitor = ?';
+ $bindSql[] = $this->visitorInfo['idvisitor'];
+ $sqlVisitorId = "$select ,
1 as priority
$from
WHERE $whereSameBothQueries $where
LIMIT 1
";
-
- // We join both queries and favor the one matching the visitor_id if it did match
- $sql = " ( $sqlConfigId )
+
+ // 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 = Piwik_Tracker::getDatabase()->fetch($sql, $bindSql);
-
- if( !Piwik_Config::getInstance()->Debug['tracker_always_new_visitor']
- && $visitRow
- && count($visitRow) > 0)
- {
- // These values will be used throughout the request
- $this->visitorInfo['visit_last_action_time'] = strtotime($visitRow['visit_last_action_time']);
- $this->visitorInfo['visit_first_action_time'] = strtotime($visitRow['visit_first_action_time']);
-
- // We always keep the first idvisitor seen for this visit (so that all page views for this visit have the same idvisitor)
- $this->visitorInfo['idvisitor'] = $visitRow['idvisitor'];
- $this->visitorInfo['idvisit'] = $visitRow['idvisit'];
- $this->visitorInfo['visit_exit_idaction_url'] = $visitRow['visit_exit_idaction_url'];
- $this->visitorInfo['visit_exit_idaction_name'] = $visitRow['visit_exit_idaction_name'];
- $this->visitorInfo['visitor_returning'] = $visitRow['visitor_returning'];
- $this->visitorInfo['visitor_days_since_first'] = $visitRow['visitor_days_since_first'];
- $this->visitorInfo['visitor_days_since_order'] = $visitRow['visitor_days_since_order'];
- $this->visitorInfo['visitor_count_visits'] = $visitRow['visitor_count_visits'];
- $this->visitorInfo['visit_goal_buyer'] = $visitRow['visit_goal_buyer'];
- $this->visitorInfo['location_country'] = $visitRow['location_country'];
- $this->visitorInfo['location_region'] = $visitRow['location_region'];
- $this->visitorInfo['location_city'] = $visitRow['location_city'];
- $this->visitorInfo['location_latitude'] = $visitRow['location_latitude'];
- $this->visitorInfo['location_longitude'] = $visitRow['location_longitude'];
-
- // Referer information will be potentially used for Goal Conversion attribution
- $this->visitorInfo['referer_name'] = $visitRow['referer_name'];
- $this->visitorInfo['referer_keyword'] = $visitRow['referer_keyword'];
- $this->visitorInfo['referer_type'] = $visitRow['referer_type'];
-
- // Custom Variables copied from Visit in potential later conversion
- if(!empty($selectCustomVariables))
- {
- for($i=1; $i<=Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++)
- {
- if(isset($visitRow['custom_var_k'.$i])
- && strlen($visitRow['custom_var_k'.$i]))
- {
- $this->visitorInfo['custom_var_k'.$i] = $visitRow['custom_var_k'.$i];
- }
- if(isset($visitRow['custom_var_v'.$i])
- && strlen($visitRow['custom_var_v'.$i]))
- {
- $this->visitorInfo['custom_var_v'.$i] = $visitRow['custom_var_v'.$i];
- }
- }
- }
-
- $this->visitorKnown = true;
- printDebug("The visitor is known (idvisitor = ".bin2hex($this->visitorInfo['idvisitor']).",
- config_id = ".bin2hex($configId).",
+ }
+
+
+ $visitRow = Piwik_Tracker::getDatabase()->fetch($sql, $bindSql);
+
+ if (!Piwik_Config::getInstance()->Debug['tracker_always_new_visitor']
+ && $visitRow
+ && count($visitRow) > 0
+ ) {
+ // These values will be used throughout the request
+ $this->visitorInfo['visit_last_action_time'] = strtotime($visitRow['visit_last_action_time']);
+ $this->visitorInfo['visit_first_action_time'] = strtotime($visitRow['visit_first_action_time']);
+
+ // We always keep the first idvisitor seen for this visit (so that all page views for this visit have the same idvisitor)
+ $this->visitorInfo['idvisitor'] = $visitRow['idvisitor'];
+ $this->visitorInfo['idvisit'] = $visitRow['idvisit'];
+ $this->visitorInfo['visit_exit_idaction_url'] = $visitRow['visit_exit_idaction_url'];
+ $this->visitorInfo['visit_exit_idaction_name'] = $visitRow['visit_exit_idaction_name'];
+ $this->visitorInfo['visitor_returning'] = $visitRow['visitor_returning'];
+ $this->visitorInfo['visitor_days_since_first'] = $visitRow['visitor_days_since_first'];
+ $this->visitorInfo['visitor_days_since_order'] = $visitRow['visitor_days_since_order'];
+ $this->visitorInfo['visitor_count_visits'] = $visitRow['visitor_count_visits'];
+ $this->visitorInfo['visit_goal_buyer'] = $visitRow['visit_goal_buyer'];
+ $this->visitorInfo['location_country'] = $visitRow['location_country'];
+ $this->visitorInfo['location_region'] = $visitRow['location_region'];
+ $this->visitorInfo['location_city'] = $visitRow['location_city'];
+ $this->visitorInfo['location_latitude'] = $visitRow['location_latitude'];
+ $this->visitorInfo['location_longitude'] = $visitRow['location_longitude'];
+
+ // Referer information will be potentially used for Goal Conversion attribution
+ $this->visitorInfo['referer_name'] = $visitRow['referer_name'];
+ $this->visitorInfo['referer_keyword'] = $visitRow['referer_keyword'];
+ $this->visitorInfo['referer_type'] = $visitRow['referer_type'];
+
+ // Custom Variables copied from Visit in potential later conversion
+ if (!empty($selectCustomVariables)) {
+ for ($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++) {
+ if (isset($visitRow['custom_var_k' . $i])
+ && strlen($visitRow['custom_var_k' . $i])
+ ) {
+ $this->visitorInfo['custom_var_k' . $i] = $visitRow['custom_var_k' . $i];
+ }
+ if (isset($visitRow['custom_var_v' . $i])
+ && strlen($visitRow['custom_var_v' . $i])
+ ) {
+ $this->visitorInfo['custom_var_v' . $i] = $visitRow['custom_var_v' . $i];
+ }
+ }
+ }
+
+ $this->visitorKnown = true;
+ printDebug("The visitor is known (idvisitor = " . bin2hex($this->visitorInfo['idvisitor']) . ",
+ config_id = " . bin2hex($configId) . ",
idvisit = {$this->visitorInfo['idvisit']},
- 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'].")");
- }
- else
- {
- printDebug("The visitor was not matched with an existing 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'] . ")");
+ } else {
+ printDebug("The visitor was not matched with an existing visitor...");
+ }
+ }
/**
* By default, we look back 30 minutes to find a previous visitor (for performance reasons).
@@ -1356,280 +1272,266 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
$isForcedVisitorIdMustMatch = ($this->getForcedVisitorId() != null);
$shouldMatchOneFieldOnly = (($isVisitorIdToLookup && $trustCookiesOnly)
- || $isForcedVisitorIdMustMatch
- || !$isVisitorIdToLookup);
+ || $isForcedVisitorIdMustMatch
+ || !$isVisitorIdToLookup);
return $shouldMatchOneFieldOnly;
}
static public function getCustomVariables($scope, $request)
- {
- if($scope == 'visit') {
- $parameter = '_cvar';
- } else {
- $parameter = 'cvar';
- }
-
- $customVar = Piwik_Common::unsanitizeInputValues(Piwik_Common::getRequestVar($parameter, '', 'json', $request));
- if(!is_array($customVar))
- {
- return array();
- }
- $customVariables = array();
- foreach($customVar as $id => $keyValue)
- {
- $id = (int)$id;
- if($id < 1
- || $id > Piwik_Tracker::MAX_CUSTOM_VARIABLES
- || count($keyValue) != 2
- || (!is_string($keyValue[0]) && !is_numeric($keyValue[0]))
- )
- {
- 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]);
- $value = self::truncateCustomVariable($keyValue[1]);
- $customVariables['custom_var_k'.$id] = $key;
- $customVariables['custom_var_v'.$id] = $value;
- }
-
- return $customVariables;
- }
-
- static public function truncateCustomVariable($input)
- {
- return substr(trim($input), 0, Piwik_Tracker::MAX_LENGTH_CUSTOM_VARIABLE);
- }
-
- /**
- * Gets the UserSettings information and returns them in an array of name => value
- *
- * @return array
- */
- protected function getUserSettingsInformation()
- {
- // we already called this method before, simply returns the result
- if(is_array($this->userSettingsInformation))
- {
- return $this->userSettingsInformation;
- }
- require_once PIWIK_INCLUDE_PATH . '/libs/UserAgentParser/UserAgentParser.php';
-
- $plugin_Flash = Piwik_Common::getRequestVar( 'fla', 0, 'int', $this->request);
- $plugin_Java = Piwik_Common::getRequestVar( 'java', 0, 'int', $this->request);
- $plugin_Director = Piwik_Common::getRequestVar( 'dir', 0, 'int', $this->request);
- $plugin_Quicktime = Piwik_Common::getRequestVar( 'qt', 0, 'int', $this->request);
- $plugin_RealPlayer = Piwik_Common::getRequestVar( 'realp', 0, 'int', $this->request);
- $plugin_PDF = Piwik_Common::getRequestVar( 'pdf', 0, 'int', $this->request);
- $plugin_WindowsMedia = Piwik_Common::getRequestVar( 'wma', 0, 'int', $this->request);
- $plugin_Gears = Piwik_Common::getRequestVar( 'gears', 0, 'int', $this->request);
- $plugin_Silverlight = Piwik_Common::getRequestVar( 'ag', 0, 'int', $this->request);
- $plugin_Cookie = Piwik_Common::getRequestVar( 'cookie', 0, 'int', $this->request);
-
- $userAgent = $this->getUserAgent($this->request);
- $aBrowserInfo = UserAgentParser::getBrowser($userAgent);
-
- $browserName = ($aBrowserInfo !== false && $aBrowserInfo['id'] !== false) ? $aBrowserInfo['id'] : 'UNK';
- $browserVersion = ($aBrowserInfo !== false && $aBrowserInfo['version'] !== false) ? $aBrowserInfo['version'] : '';
-
- $os = UserAgentParser::getOperatingSystem($userAgent);
- $os = $os === false ? 'UNK' : $os['id'];
-
- $resolution = Piwik_Common::getRequestVar('res', 'unknown', 'string', $this->request);
-
- $browserLang = $this->getBrowserLanguage();
-
- $configurationHash = $this->getConfigHash(
- $os,
- $browserName,
- $browserVersion,
- $resolution,
- $plugin_Flash,
- $plugin_Java,
- $plugin_Director,
- $plugin_Quicktime,
- $plugin_RealPlayer,
- $plugin_PDF,
- $plugin_WindowsMedia,
- $plugin_Gears,
- $plugin_Silverlight,
- $plugin_Cookie,
- $this->getVisitorIp(),
- $browserLang);
-
- $this->userSettingsInformation = array(
- 'config_id' => $configurationHash,
- 'config_os' => $os,
- 'config_browser_name' => $browserName,
- 'config_browser_version' => $browserVersion,
- 'config_resolution' => $resolution,
- 'config_pdf' => $plugin_PDF,
- 'config_flash' => $plugin_Flash,
- 'config_java' => $plugin_Java,
- 'config_director' => $plugin_Director,
- 'config_quicktime' => $plugin_Quicktime,
- 'config_realplayer' => $plugin_RealPlayer,
- 'config_windowsmedia' => $plugin_WindowsMedia,
- 'config_gears' => $plugin_Gears,
- 'config_silverlight' => $plugin_Silverlight,
- 'config_cookie' => $plugin_Cookie,
- 'location_browser_lang' => $browserLang,
- );
-
- return $this->userSettingsInformation;
- }
-
- /**
- * Returns true if the last action was done during the last 30 minutes
- * @return bool
- */
- protected function isLastActionInTheSameVisit()
- {
- return isset($this->visitorInfo['visit_last_action_time'])
- && ($this->visitorInfo['visit_last_action_time']
- > ($this->getCurrentTimestamp() - Piwik_Config::getInstance()->Tracker['visit_standard_length']));
- }
-
- /**
- * Returns true if the recognizeTheVisitor() method did recognize the visitor
- * @return bool
- */
- protected function isVisitorKnown()
- {
- return $this->visitorKnown === true;
- }
-
- /**
- * Update the cookie information.
- */
- protected function setThirdPartyCookie()
- {
- if(!$this->shouldUseThirdPartyCookie())
- {
- return;
- }
- printDebug("We manage the cookie...");
-
- // idcookie has been generated in handleNewVisit or we simply propagate the old value
- $this->cookie->set(0, bin2hex($this->visitorInfo['idvisitor']) );
- $this->cookie->save();
- }
-
- /**
- * Returns an object able to handle the current action
- * Plugins can return an override Action that for example, does not record the action in the DB
- *
- * @throws Exception
- * @return Piwik_Tracker_Action child or fake but with same public interface
- */
- protected function newAction()
- {
- $action = null;
- Piwik_PostEvent('Tracker.newAction', $action);
-
- if(is_null($action))
- {
- $action = new Piwik_Tracker_Action();
- }
- elseif(!($action instanceof Piwik_Tracker_Action_Interface))
- {
- throw new Exception("The Action object set in the plugin must implement the interface Piwik_Tracker_Action_Interface");
- }
- return $action;
- }
-
- /**
- * Detect whether action is an outlink given host aliases
- *
- * @param Piwik_Tracker_Action_Interface $action
- * @return bool true if the outlink the visitor clicked on points to one of the known hosts for this website
- */
- protected function detectActionIsOutlinkOnAliasHost(Piwik_Tracker_Action_Interface $action)
- {
- if($action->getActionType() != Piwik_Tracker_Action_Interface::TYPE_OUTLINK)
- {
- return false;
- }
- $decodedActionUrl = $action->getActionUrl();
- $actionUrlParsed = @parse_url($decodedActionUrl);
- if(!isset($actionUrlParsed['host']))
- {
- return false;
- }
- return Piwik_Tracker_Visit::isHostKnownAliasHost($actionUrlParsed['host'], $this->idsite);
- }
-
- /**
- * Returns a 64-bit hash of all the configuration settings
- * @param $os
- * @param $browserName
- * @param $browserVersion
- * @param $resolution
- * @param $plugin_Flash
- * @param $plugin_Java
- * @param $plugin_Director
- * @param $plugin_Quicktime
- * @param $plugin_RealPlayer
- * @param $plugin_PDF
- * @param $plugin_WindowsMedia
- * @param $plugin_Gears
- * @param $plugin_Silverlight
- * @param $plugin_Cookie
- * @param $ip
- * @param $browserLang
- * @return string
- */
- protected function getConfigHash( $os, $browserName, $browserVersion, $resolution, $plugin_Flash, $plugin_Java, $plugin_Director, $plugin_Quicktime, $plugin_RealPlayer, $plugin_PDF, $plugin_WindowsMedia, $plugin_Gears, $plugin_Silverlight, $plugin_Cookie, $ip, $browserLang)
- {
- $hash = md5( $os . $browserName . $browserVersion . $plugin_Flash . $plugin_Java . $plugin_Director . $plugin_Quicktime . $plugin_RealPlayer . $plugin_PDF . $plugin_WindowsMedia . $plugin_Gears . $plugin_Silverlight . $plugin_Cookie . $ip . $browserLang, $raw_output = true );
- return Piwik_Common::substr( $hash, 0, Piwik_Tracker::LENGTH_BINARY_ID );
- }
-
- /**
- * Returns either
- * - "-1" for a known visitor
- * - at least 16 char identifier in hex @see Piwik_Common::generateUniqId()
- * @return int|string
- */
- protected function getVisitorUniqueId()
- {
- if($this->isVisitorKnown())
- {
- return -1;
- }
- return Piwik_Common::generateUniqId();
- }
-
- protected function setCookie( $cookie )
- {
- $this->cookie = $cookie;
- }
-
- // is the referer host any of the registered URLs for this website?
- static public function isHostKnownAliasHost($urlHost, $idSite)
- {
- $websiteData = Piwik_Tracker_Cache::getCacheWebsiteAttributes($idSite);
- if(isset($websiteData['hosts']))
- {
- $canonicalHosts = array();
- foreach($websiteData['hosts'] as $host) {
- $canonicalHosts[] = str_replace('www.', '' , mb_strtolower($host, 'UTF-8'));
- }
- $canonicalHost = str_replace('www.', '', mb_strtolower($urlHost, 'UTF-8'));
- if(in_array($canonicalHost, $canonicalHosts))
- {
- return true;
- }
- }
- return false;
- }
+ {
+ if ($scope == 'visit') {
+ $parameter = '_cvar';
+ } else {
+ $parameter = 'cvar';
+ }
+
+ $customVar = Piwik_Common::unsanitizeInputValues(Piwik_Common::getRequestVar($parameter, '', 'json', $request));
+ if (!is_array($customVar)) {
+ return array();
+ }
+ $customVariables = array();
+ foreach ($customVar as $id => $keyValue) {
+ $id = (int)$id;
+ if ($id < 1
+ || $id > Piwik_Tracker::MAX_CUSTOM_VARIABLES
+ || count($keyValue) != 2
+ || (!is_string($keyValue[0]) && !is_numeric($keyValue[0]))
+ ) {
+ 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]);
+ $value = self::truncateCustomVariable($keyValue[1]);
+ $customVariables['custom_var_k' . $id] = $key;
+ $customVariables['custom_var_v' . $id] = $value;
+ }
+
+ return $customVariables;
+ }
+
+ static public function truncateCustomVariable($input)
+ {
+ return substr(trim($input), 0, Piwik_Tracker::MAX_LENGTH_CUSTOM_VARIABLE);
+ }
+
+ /**
+ * Gets the UserSettings information and returns them in an array of name => value
+ *
+ * @return array
+ */
+ protected function getUserSettingsInformation()
+ {
+ // we already called this method before, simply returns the result
+ if (is_array($this->userSettingsInformation)) {
+ return $this->userSettingsInformation;
+ }
+ require_once PIWIK_INCLUDE_PATH . '/libs/UserAgentParser/UserAgentParser.php';
+
+ $plugin_Flash = Piwik_Common::getRequestVar('fla', 0, 'int', $this->request);
+ $plugin_Java = Piwik_Common::getRequestVar('java', 0, 'int', $this->request);
+ $plugin_Director = Piwik_Common::getRequestVar('dir', 0, 'int', $this->request);
+ $plugin_Quicktime = Piwik_Common::getRequestVar('qt', 0, 'int', $this->request);
+ $plugin_RealPlayer = Piwik_Common::getRequestVar('realp', 0, 'int', $this->request);
+ $plugin_PDF = Piwik_Common::getRequestVar('pdf', 0, 'int', $this->request);
+ $plugin_WindowsMedia = Piwik_Common::getRequestVar('wma', 0, 'int', $this->request);
+ $plugin_Gears = Piwik_Common::getRequestVar('gears', 0, 'int', $this->request);
+ $plugin_Silverlight = Piwik_Common::getRequestVar('ag', 0, 'int', $this->request);
+ $plugin_Cookie = Piwik_Common::getRequestVar('cookie', 0, 'int', $this->request);
+
+ $userAgent = $this->getUserAgent($this->request);
+ $aBrowserInfo = UserAgentParser::getBrowser($userAgent);
+
+ $browserName = ($aBrowserInfo !== false && $aBrowserInfo['id'] !== false) ? $aBrowserInfo['id'] : 'UNK';
+ $browserVersion = ($aBrowserInfo !== false && $aBrowserInfo['version'] !== false) ? $aBrowserInfo['version'] : '';
+
+ $os = UserAgentParser::getOperatingSystem($userAgent);
+ $os = $os === false ? 'UNK' : $os['id'];
+
+ $resolution = Piwik_Common::getRequestVar('res', 'unknown', 'string', $this->request);
+
+ $browserLang = $this->getBrowserLanguage();
+
+ $configurationHash = $this->getConfigHash(
+ $os,
+ $browserName,
+ $browserVersion,
+ $resolution,
+ $plugin_Flash,
+ $plugin_Java,
+ $plugin_Director,
+ $plugin_Quicktime,
+ $plugin_RealPlayer,
+ $plugin_PDF,
+ $plugin_WindowsMedia,
+ $plugin_Gears,
+ $plugin_Silverlight,
+ $plugin_Cookie,
+ $this->getVisitorIp(),
+ $browserLang);
+
+ $this->userSettingsInformation = array(
+ 'config_id' => $configurationHash,
+ 'config_os' => $os,
+ 'config_browser_name' => $browserName,
+ 'config_browser_version' => $browserVersion,
+ 'config_resolution' => $resolution,
+ 'config_pdf' => $plugin_PDF,
+ 'config_flash' => $plugin_Flash,
+ 'config_java' => $plugin_Java,
+ 'config_director' => $plugin_Director,
+ 'config_quicktime' => $plugin_Quicktime,
+ 'config_realplayer' => $plugin_RealPlayer,
+ 'config_windowsmedia' => $plugin_WindowsMedia,
+ 'config_gears' => $plugin_Gears,
+ 'config_silverlight' => $plugin_Silverlight,
+ 'config_cookie' => $plugin_Cookie,
+ 'location_browser_lang' => $browserLang,
+ );
+
+ return $this->userSettingsInformation;
+ }
+
+ /**
+ * Returns true if the last action was done during the last 30 minutes
+ * @return bool
+ */
+ protected function isLastActionInTheSameVisit()
+ {
+ return isset($this->visitorInfo['visit_last_action_time'])
+ && ($this->visitorInfo['visit_last_action_time']
+ > ($this->getCurrentTimestamp() - Piwik_Config::getInstance()->Tracker['visit_standard_length']));
+ }
+
+ /**
+ * Returns true if the recognizeTheVisitor() method did recognize the visitor
+ * @return bool
+ */
+ protected function isVisitorKnown()
+ {
+ return $this->visitorKnown === true;
+ }
+
+ /**
+ * Update the cookie information.
+ */
+ protected function setThirdPartyCookie()
+ {
+ if (!$this->shouldUseThirdPartyCookie()) {
+ return;
+ }
+ printDebug("We manage the cookie...");
+
+ // idcookie has been generated in handleNewVisit or we simply propagate the old value
+ $this->cookie->set(0, bin2hex($this->visitorInfo['idvisitor']));
+ $this->cookie->save();
+ }
+
+ /**
+ * Returns an object able to handle the current action
+ * Plugins can return an override Action that for example, does not record the action in the DB
+ *
+ * @throws Exception
+ * @return Piwik_Tracker_Action child or fake but with same public interface
+ */
+ protected function newAction()
+ {
+ $action = null;
+ Piwik_PostEvent('Tracker.newAction', $action);
+
+ if (is_null($action)) {
+ $action = new Piwik_Tracker_Action();
+ } elseif (!($action instanceof Piwik_Tracker_Action_Interface)) {
+ throw new Exception("The Action object set in the plugin must implement the interface Piwik_Tracker_Action_Interface");
+ }
+ return $action;
+ }
+
+ /**
+ * Detect whether action is an outlink given host aliases
+ *
+ * @param Piwik_Tracker_Action_Interface $action
+ * @return bool true if the outlink the visitor clicked on points to one of the known hosts for this website
+ */
+ protected function detectActionIsOutlinkOnAliasHost(Piwik_Tracker_Action_Interface $action)
+ {
+ if ($action->getActionType() != Piwik_Tracker_Action_Interface::TYPE_OUTLINK) {
+ return false;
+ }
+ $decodedActionUrl = $action->getActionUrl();
+ $actionUrlParsed = @parse_url($decodedActionUrl);
+ if (!isset($actionUrlParsed['host'])) {
+ return false;
+ }
+ return Piwik_Tracker_Visit::isHostKnownAliasHost($actionUrlParsed['host'], $this->idsite);
+ }
+
+ /**
+ * Returns a 64-bit hash of all the configuration settings
+ * @param $os
+ * @param $browserName
+ * @param $browserVersion
+ * @param $resolution
+ * @param $plugin_Flash
+ * @param $plugin_Java
+ * @param $plugin_Director
+ * @param $plugin_Quicktime
+ * @param $plugin_RealPlayer
+ * @param $plugin_PDF
+ * @param $plugin_WindowsMedia
+ * @param $plugin_Gears
+ * @param $plugin_Silverlight
+ * @param $plugin_Cookie
+ * @param $ip
+ * @param $browserLang
+ * @return string
+ */
+ protected function getConfigHash($os, $browserName, $browserVersion, $resolution, $plugin_Flash, $plugin_Java, $plugin_Director, $plugin_Quicktime, $plugin_RealPlayer, $plugin_PDF, $plugin_WindowsMedia, $plugin_Gears, $plugin_Silverlight, $plugin_Cookie, $ip, $browserLang)
+ {
+ $hash = md5($os . $browserName . $browserVersion . $plugin_Flash . $plugin_Java . $plugin_Director . $plugin_Quicktime . $plugin_RealPlayer . $plugin_PDF . $plugin_WindowsMedia . $plugin_Gears . $plugin_Silverlight . $plugin_Cookie . $ip . $browserLang, $raw_output = true);
+ return Piwik_Common::substr($hash, 0, Piwik_Tracker::LENGTH_BINARY_ID);
+ }
+
+ /**
+ * Returns either
+ * - "-1" for a known visitor
+ * - at least 16 char identifier in hex @see Piwik_Common::generateUniqId()
+ * @return int|string
+ */
+ protected function getVisitorUniqueId()
+ {
+ if ($this->isVisitorKnown()) {
+ return -1;
+ }
+ return Piwik_Common::generateUniqId();
+ }
+
+ protected function setCookie($cookie)
+ {
+ $this->cookie = $cookie;
+ }
+
+ // is the referer host any of the registered URLs for this website?
+ static public function isHostKnownAliasHost($urlHost, $idSite)
+ {
+ $websiteData = Piwik_Tracker_Cache::getCacheWebsiteAttributes($idSite);
+ if (isset($websiteData['hosts'])) {
+ $canonicalHosts = array();
+ foreach ($websiteData['hosts'] as $host) {
+ $canonicalHosts[] = str_replace('www.', '', mb_strtolower($host, 'UTF-8'));
+ }
+ $canonicalHost = str_replace('www.', '', mb_strtolower($urlHost, 'UTF-8'));
+ if (in_array($canonicalHost, $canonicalHosts)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
/**
@@ -1638,273 +1540,256 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
*/
class Piwik_Tracker_Visit_Referer
{
- // @see detect*() referer methods
- protected $typeRefererAnalyzed;
- protected $nameRefererAnalyzed;
- protected $keywordRefererAnalyzed;
- protected $refererHost;
- protected $refererUrl;
- protected $refererUrlParse;
- protected $currentUrlParse;
- protected $idsite;
-
- // Used to prefix when a adsense referer is detected
- const LABEL_PREFIX_ADSENSE_KEYWORD = '(adsense) ';
-
-
- /**
- * Returns an array containing the following information:
- * - referer_type
- * - direct -- absence of referer URL OR referer URL has the same host
- * - site -- based on the referer URL
- * - search_engine -- based on the referer URL
- * - campaign -- based on campaign URL parameter
- *
- * - referer_name
- * - ()
- * - piwik.net -- site host name
- * - google.fr -- search engine host name
- * - adwords-search -- campaign name
- *
- * - referer_keyword
- * - ()
- * - ()
- * - my keyword
- * - my paid keyword
- * - ()
- * - ()
- *
- * - referer_url : the same for all the referer types
- *
- * @param $refererUrl must be URL Encoded
- * @param $currentUrl
- * @param $idSite
- * @return array
- */
- public function getRefererInformation($refererUrl, $currentUrl, $idSite)
- {
- $this->idsite = $idSite;
-
- // default values for the referer_* fields
- $refererUrl = Piwik_Common::unsanitizeInputValue($refererUrl);
- if(!empty($refererUrl)
- && !Piwik_Common::isLookLikeUrl($refererUrl))
- {
- $refererUrl = '';
- }
-
- $currentUrl = Piwik_Tracker_Action::cleanupUrl($currentUrl);
-
- $this->refererUrl = $refererUrl;
- $this->refererUrlParse = @parse_url($this->refererUrl);
- $this->currentUrlParse = @parse_url($currentUrl);
- $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
- $this->nameRefererAnalyzed = '';
- $this->keywordRefererAnalyzed = '';
- $this->refererHost = '';
-
- if(isset($this->refererUrlParse['host']))
- {
- $this->refererHost = $this->refererUrlParse['host'];
- }
-
- $refererDetected = false;
-
- if( !empty($this->currentUrlParse['host'])
- && $this->detectRefererCampaign() )
- {
- $refererDetected = true;
- }
-
- if(!$refererDetected)
- {
- if( $this->detectRefererDirectEntry()
- || $this->detectRefererSearchEngine() )
- {
- $refererDetected = true;
- }
- }
-
- if(!empty($this->refererHost)
- && !$refererDetected)
- {
- $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_WEBSITE;
- $this->nameRefererAnalyzed = mb_strtolower($this->refererHost, 'UTF-8');
- }
-
- $refererInformation = array(
- 'referer_type' => $this->typeRefererAnalyzed,
- 'referer_name' => $this->nameRefererAnalyzed,
- 'referer_keyword' => $this->keywordRefererAnalyzed,
- 'referer_url' => $this->refererUrl,
- );
-
- return $refererInformation;
- }
-
- /**
- * Search engine detection
+ // @see detect*() referer methods
+ protected $typeRefererAnalyzed;
+ protected $nameRefererAnalyzed;
+ protected $keywordRefererAnalyzed;
+ protected $refererHost;
+ protected $refererUrl;
+ protected $refererUrlParse;
+ protected $currentUrlParse;
+ protected $idsite;
+
+ // Used to prefix when a adsense referer is detected
+ const LABEL_PREFIX_ADSENSE_KEYWORD = '(adsense) ';
+
+
+ /**
+ * Returns an array containing the following information:
+ * - referer_type
+ * - direct -- absence of referer URL OR referer URL has the same host
+ * - site -- based on the referer URL
+ * - search_engine -- based on the referer URL
+ * - campaign -- based on campaign URL parameter
+ *
+ * - referer_name
+ * - ()
+ * - piwik.net -- site host name
+ * - google.fr -- search engine host name
+ * - adwords-search -- campaign name
+ *
+ * - referer_keyword
+ * - ()
+ * - ()
+ * - my keyword
+ * - my paid keyword
+ * - ()
+ * - ()
+ *
+ * - referer_url : the same for all the referer types
+ *
+ * @param $refererUrl must be URL Encoded
+ * @param $currentUrl
+ * @param $idSite
+ * @return array
+ */
+ public function getRefererInformation($refererUrl, $currentUrl, $idSite)
+ {
+ $this->idsite = $idSite;
+
+ // default values for the referer_* fields
+ $refererUrl = Piwik_Common::unsanitizeInputValue($refererUrl);
+ if (!empty($refererUrl)
+ && !Piwik_Common::isLookLikeUrl($refererUrl)
+ ) {
+ $refererUrl = '';
+ }
+
+ $currentUrl = Piwik_Tracker_Action::cleanupUrl($currentUrl);
+
+ $this->refererUrl = $refererUrl;
+ $this->refererUrlParse = @parse_url($this->refererUrl);
+ $this->currentUrlParse = @parse_url($currentUrl);
+ $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
+ $this->nameRefererAnalyzed = '';
+ $this->keywordRefererAnalyzed = '';
+ $this->refererHost = '';
+
+ if (isset($this->refererUrlParse['host'])) {
+ $this->refererHost = $this->refererUrlParse['host'];
+ }
+
+ $refererDetected = false;
+
+ if (!empty($this->currentUrlParse['host'])
+ && $this->detectRefererCampaign()
+ ) {
+ $refererDetected = true;
+ }
+
+ if (!$refererDetected) {
+ if ($this->detectRefererDirectEntry()
+ || $this->detectRefererSearchEngine()
+ ) {
+ $refererDetected = true;
+ }
+ }
+
+ if (!empty($this->refererHost)
+ && !$refererDetected
+ ) {
+ $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_WEBSITE;
+ $this->nameRefererAnalyzed = mb_strtolower($this->refererHost, 'UTF-8');
+ }
+
+ $refererInformation = array(
+ 'referer_type' => $this->typeRefererAnalyzed,
+ 'referer_name' => $this->nameRefererAnalyzed,
+ 'referer_keyword' => $this->keywordRefererAnalyzed,
+ 'referer_url' => $this->refererUrl,
+ );
+
+ return $refererInformation;
+ }
+
+ /**
+ * Search engine detection
* @return bool
- */
- protected function detectRefererSearchEngine()
- {
- $searchEngineInformation = Piwik_Common::extractSearchEngineInformationFromUrl($this->refererUrl);
- Piwik_PostEvent('Tracker.detectRefererSearchEngine', $searchEngineInformation, $this->refererUrl);
- if($searchEngineInformation === false)
- {
- return false;
- }
- $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_SEARCH_ENGINE;
- $this->nameRefererAnalyzed = $searchEngineInformation['name'];
- $this->keywordRefererAnalyzed = $searchEngineInformation['keywords'];
- return true;
- }
+ */
+ protected function detectRefererSearchEngine()
+ {
+ $searchEngineInformation = Piwik_Common::extractSearchEngineInformationFromUrl($this->refererUrl);
+ Piwik_PostEvent('Tracker.detectRefererSearchEngine', $searchEngineInformation, $this->refererUrl);
+ if ($searchEngineInformation === false) {
+ return false;
+ }
+ $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_SEARCH_ENGINE;
+ $this->nameRefererAnalyzed = $searchEngineInformation['name'];
+ $this->keywordRefererAnalyzed = $searchEngineInformation['keywords'];
+ return true;
+ }
/**
* @param string $string
* @return bool
*/
protected function detectCampaignFromString($string)
- {
- foreach($this->campaignNames as $campaignNameParameter)
- {
- $campaignName = trim(urldecode(Piwik_Common::getParameterFromQueryString($string, $campaignNameParameter)));
- if( !empty($campaignName))
- {
- break;
- }
- }
-
- if(!empty($campaignName))
- {
- $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_CAMPAIGN;
- $this->nameRefererAnalyzed = $campaignName;
-
- foreach($this->campaignKeywords as $campaignKeywordParameter)
- {
- $campaignKeyword = Piwik_Common::getParameterFromQueryString($string, $campaignKeywordParameter);
- if( !empty($campaignKeyword))
- {
- $this->keywordRefererAnalyzed = trim(urldecode($campaignKeyword));
- break;
- }
- }
-
- // if the campaign keyword is empty, try to get a keyword from the referrer URL
- if (empty($this->keywordRefererAnalyzed))
- {
- // Set the Campaign keyword to the keyword found in the Referer URL if any
- $referrerUrlInfo = Piwik_Common::extractSearchEngineInformationFromUrl($this->refererUrl);
- if (!empty($referrerUrlInfo['keywords']))
- {
- $this->keywordRefererAnalyzed = $referrerUrlInfo['keywords'];
- }
-
- // Set the keyword, to the hostname found, in a Adsense Referer URL '&url=' parameter
- if(empty($this->keywordRefererAnalyzed)
- && !empty($this->refererUrlParse['query'])
- && !empty($this->refererHost)
- && (strpos($this->refererHost, 'google') !== false || strpos($this->refererHost,'doubleclick') !== false)
- )
- {
- // This parameter sometimes is found & contains the page with the adsense ad bringing visitor to our site
- $adsenseReferrerParameter = 'url';
- $value = trim(urldecode(Piwik_Common::getParameterFromQueryString($this->refererUrlParse['query'], $adsenseReferrerParameter)));
- if(!empty($value))
- {
- $parsedAdsenseReferrerUrl = parse_url($value);
- if(!empty($parsedAdsenseReferrerUrl['host']))
- {
- $this->keywordRefererAnalyzed = self::LABEL_PREFIX_ADSENSE_KEYWORD . $parsedAdsenseReferrerUrl['host'];
- }
- }
- }
-
- // or we default to the referrer hostname otherwise
- if(empty($this->keywordRefererAnalyzed))
- {
- $this->keywordRefererAnalyzed = $this->refererHost;
- }
- }
-
- return true;
- }
- return false;
- }
-
- /**
- * Campaign analysis
+ {
+ foreach ($this->campaignNames as $campaignNameParameter) {
+ $campaignName = trim(urldecode(Piwik_Common::getParameterFromQueryString($string, $campaignNameParameter)));
+ if (!empty($campaignName)) {
+ break;
+ }
+ }
+
+ if (!empty($campaignName)) {
+ $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_CAMPAIGN;
+ $this->nameRefererAnalyzed = $campaignName;
+
+ foreach ($this->campaignKeywords as $campaignKeywordParameter) {
+ $campaignKeyword = Piwik_Common::getParameterFromQueryString($string, $campaignKeywordParameter);
+ if (!empty($campaignKeyword)) {
+ $this->keywordRefererAnalyzed = trim(urldecode($campaignKeyword));
+ break;
+ }
+ }
+
+ // if the campaign keyword is empty, try to get a keyword from the referrer URL
+ if (empty($this->keywordRefererAnalyzed)) {
+ // Set the Campaign keyword to the keyword found in the Referer URL if any
+ $referrerUrlInfo = Piwik_Common::extractSearchEngineInformationFromUrl($this->refererUrl);
+ if (!empty($referrerUrlInfo['keywords'])) {
+ $this->keywordRefererAnalyzed = $referrerUrlInfo['keywords'];
+ }
+
+ // Set the keyword, to the hostname found, in a Adsense Referer URL '&url=' parameter
+ if (empty($this->keywordRefererAnalyzed)
+ && !empty($this->refererUrlParse['query'])
+ && !empty($this->refererHost)
+ && (strpos($this->refererHost, 'google') !== false || strpos($this->refererHost, 'doubleclick') !== false)
+ ) {
+ // This parameter sometimes is found & contains the page with the adsense ad bringing visitor to our site
+ $adsenseReferrerParameter = 'url';
+ $value = trim(urldecode(Piwik_Common::getParameterFromQueryString($this->refererUrlParse['query'], $adsenseReferrerParameter)));
+ if (!empty($value)) {
+ $parsedAdsenseReferrerUrl = parse_url($value);
+ if (!empty($parsedAdsenseReferrerUrl['host'])) {
+ $this->keywordRefererAnalyzed = self::LABEL_PREFIX_ADSENSE_KEYWORD . $parsedAdsenseReferrerUrl['host'];
+ }
+ }
+ }
+
+ // or we default to the referrer hostname otherwise
+ if (empty($this->keywordRefererAnalyzed)) {
+ $this->keywordRefererAnalyzed = $this->refererHost;
+ }
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Campaign analysis
* @return bool
- */
- protected function detectRefererCampaign()
- {
- if(!isset($this->currentUrlParse['query'])
- && !isset($this->currentUrlParse['fragment']))
- {
- return false;
- }
- $campaignParameters = Piwik_Common::getCampaignParameters();
- $this->campaignNames = $campaignParameters[0];
- $this->campaignKeywords = $campaignParameters[1];
-
- $found = false;
-
- // 1) Detect campaign from query string
- if(isset($this->currentUrlParse['query']))
- {
- $found = $this->detectCampaignFromString($this->currentUrlParse['query']);
- }
-
- // 2) Detect from fragment #hash
- if(!$found
- && isset($this->currentUrlParse['fragment']))
- {
- $found = $this->detectCampaignFromString($this->currentUrlParse['fragment']);
- }
- return $found;
- }
-
- /**
- * We have previously tried to detect the campaign variables in the URL
- * so at this stage, if the referer host is the current host,
- * or if the referer host is any of the registered URL for this website,
- * it is considered a direct entry
+ */
+ protected function detectRefererCampaign()
+ {
+ if (!isset($this->currentUrlParse['query'])
+ && !isset($this->currentUrlParse['fragment'])
+ ) {
+ return false;
+ }
+ $campaignParameters = Piwik_Common::getCampaignParameters();
+ $this->campaignNames = $campaignParameters[0];
+ $this->campaignKeywords = $campaignParameters[1];
+
+ $found = false;
+
+ // 1) Detect campaign from query string
+ if (isset($this->currentUrlParse['query'])) {
+ $found = $this->detectCampaignFromString($this->currentUrlParse['query']);
+ }
+
+ // 2) Detect from fragment #hash
+ if (!$found
+ && isset($this->currentUrlParse['fragment'])
+ ) {
+ $found = $this->detectCampaignFromString($this->currentUrlParse['fragment']);
+ }
+ return $found;
+ }
+
+ /**
+ * We have previously tried to detect the campaign variables in the URL
+ * so at this stage, if the referer host is the current host,
+ * or if the referer host is any of the registered URL for this website,
+ * it is considered a direct entry
* @return bool
- */
- protected function detectRefererDirectEntry()
- {
- if(!empty($this->refererHost))
- {
- // is the referer host the current host?
- if(isset($this->currentUrlParse['host']))
- {
- $currentHost = mb_strtolower($this->currentUrlParse['host'], 'UTF-8');
- if($currentHost == mb_strtolower($this->refererHost, 'UTF-8'))
- {
- $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
- return true;
- }
- }
- if(Piwik_Tracker_Visit::isHostKnownAliasHost($this->refererHost, $this->idsite))
- {
- $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
- return true;
- }
- }
- return false;
- }
+ */
+ protected function detectRefererDirectEntry()
+ {
+ if (!empty($this->refererHost)) {
+ // is the referer host the current host?
+ if (isset($this->currentUrlParse['host'])) {
+ $currentHost = mb_strtolower($this->currentUrlParse['host'], 'UTF-8');
+ if ($currentHost == mb_strtolower($this->refererHost, 'UTF-8')) {
+ $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
+ return true;
+ }
+ }
+ if (Piwik_Tracker_Visit::isHostKnownAliasHost($this->refererHost, $this->idsite)) {
+ $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
+ return true;
+ }
+ }
+ return false;
+ }
}
/**
* @package Piwik
* @subpackage Piwik_Tracker
*/
-class Piwik_Tracker_Visit_VisitorNotFoundInDatabase extends Exception {
+class Piwik_Tracker_Visit_VisitorNotFoundInDatabase extends Exception
+{
}
/**
* @package Piwik
* @subpackage Piwik_Tracker
*/
-class Piwik_Tracker_Visit_Excluded extends Exception {
+class Piwik_Tracker_Visit_Excluded extends Exception
+{
}
diff --git a/core/Translate.php b/core/Translate.php
index 693c4cc916..e19ea422fc 100644
--- a/core/Translate.php
+++ b/core/Translate.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -14,178 +14,167 @@
*/
class Piwik_Translate
{
- static private $instance = null;
- static private $languageToLoad = null;
- private $loadedLanguage = false;
-
- /**
- * @return Piwik_Translate
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- public function loadEnglishTranslation()
- {
- $this->loadTranslation('en');
- }
-
- public function unloadEnglishTranslation()
- {
- $GLOBALS['Piwik_translations'] = array();
- }
-
- public function reloadLanguage($language = false)
- {
- if(empty($language))
- {
- $language = $this->getLanguageToLoad();
- }
- $this->unloadEnglishTranslation();
- $this->loadEnglishTranslation();
- $this->loadCoreTranslation($language);
- Piwik_PluginsManager::getInstance()->loadPluginTranslations($language);
- }
-
- /**
- * Reads the specified code translation file in memory.
- *
- * @param bool|string $language 2 letter language code. If not specified, will detect current user translation, or load default translation.
- * @return void
- */
- public function loadCoreTranslation($language = false)
- {
- if(empty($language))
- {
- $language = $this->getLanguageToLoad();
- }
- if($this->loadedLanguage == $language)
- {
- return;
- }
- $this->loadTranslation($language);
- }
-
- private function loadTranslation($language)
- {
- $path = PIWIK_INCLUDE_PATH . '/lang/' . $language . '.php';
- if(!Piwik_Common::isValidFilename($language) || !is_readable($path))
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($language)));
- }
- require $path;
- $this->mergeTranslationArray($translations);
- $this->setLocale();
- $this->loadedLanguage = $language;
- }
-
- public function mergeTranslationArray($translation)
- {
- if(!isset($GLOBALS['Piwik_translations']))
- {
- $GLOBALS['Piwik_translations'] = array();
- }
- // we could check that no string overlap here
- $GLOBALS['Piwik_translations'] = array_merge($GLOBALS['Piwik_translations'], array_filter($translation, 'strlen'));
- }
-
- /**
- * @return string the language filename prefix, eg 'en' for english
- * @throws exception if the language set is not a valid filename
- */
- public function getLanguageToLoad()
- {
- if(is_null(self::$languageToLoad))
- {
- $lang = Piwik_Common::getRequestVar('language', '', 'string');
-
- Piwik_PostEvent('Translate.getLanguageToLoad', $lang);
-
- self::$languageToLoad = $lang;
- }
-
- return self::$languageToLoad;
- }
-
- /** Reset the cached language to load. Used in tests. */
- static public function reset()
- {
- self::$languageToLoad = null;
- }
-
- public function getLanguageLoaded()
- {
- return $this->loadedLanguage;
- }
-
- public function getLanguageDefault()
- {
- return Piwik_Config::getInstance()->General['default_language'];
- }
-
- /**
- * Generate javascript translations array
- *
- * @param array $moduleList
- * @return string containing javascript code with translations array (including <script> tag)
- */
- public function getJavascriptTranslations(array $moduleList)
- {
- if(!in_array('General', $moduleList))
- {
- $moduleList[] = 'General';
- }
-
- $js = 'var translations = {';
-
- $moduleRegex = '#^(';
- foreach($moduleList as $module)
- {
- $moduleRegex .= $module.'|';
- }
- $moduleRegex = substr($moduleRegex, 0, -1);
- $moduleRegex .= ')_.*_js$#i';
-
- // Hack: common translations used in JS but not only, force as them to be defined in JS
- $translations = $GLOBALS['Piwik_translations'];
- $toSetInJs = array('General_Save', 'General_OrCancel');
- foreach($toSetInJs as $toSetId)
- {
- $translations[$toSetId.'_js'] = $translations[$toSetId];
- }
- foreach($translations as $key => $value)
- {
- if( preg_match($moduleRegex,$key) ) {
- $js .= '"'.$key.'": "'.str_replace('"','\"',$value).'",';
- }
- }
- $js = substr($js,0,-1);
- $js .= '};';
- $js .= "\n".'if(typeof(piwik_translations) == \'undefined\') { var piwik_translations = new Object; }'.
- 'for(var i in translations) { piwik_translations[i] = translations[i];} ';
- $js .= 'function _pk_translate(translationStringId) { '.
- 'if( typeof(piwik_translations[translationStringId]) != \'undefined\' ){ return piwik_translations[translationStringId]; }'.
- 'return "The string "+translationStringId+" was not loaded in javascript. Make sure it is suffixed with _js and that you called %7BloadJavascriptTranslations plugins=\'\$YOUR_PLUGIN_NAME\'%7D before your javascript code.";}';
- return $js;
- }
-
- /**
- * Set locale
- *
- * @see http://php.net/setlocale
- */
- private function setLocale()
- {
- $locale = $GLOBALS['Piwik_translations']['General_Locale'];
- $locale_variant = str_replace('UTF-8', 'UTF8', $locale);
- setlocale(LC_ALL, $locale, $locale_variant);
- setlocale(LC_CTYPE, '');
- }
+ static private $instance = null;
+ static private $languageToLoad = null;
+ private $loadedLanguage = false;
+
+ /**
+ * @return Piwik_Translate
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ public function loadEnglishTranslation()
+ {
+ $this->loadTranslation('en');
+ }
+
+ public function unloadEnglishTranslation()
+ {
+ $GLOBALS['Piwik_translations'] = array();
+ }
+
+ public function reloadLanguage($language = false)
+ {
+ if (empty($language)) {
+ $language = $this->getLanguageToLoad();
+ }
+ $this->unloadEnglishTranslation();
+ $this->loadEnglishTranslation();
+ $this->loadCoreTranslation($language);
+ Piwik_PluginsManager::getInstance()->loadPluginTranslations($language);
+ }
+
+ /**
+ * Reads the specified code translation file in memory.
+ *
+ * @param bool|string $language 2 letter language code. If not specified, will detect current user translation, or load default translation.
+ * @return void
+ */
+ public function loadCoreTranslation($language = false)
+ {
+ if (empty($language)) {
+ $language = $this->getLanguageToLoad();
+ }
+ if ($this->loadedLanguage == $language) {
+ return;
+ }
+ $this->loadTranslation($language);
+ }
+
+ private function loadTranslation($language)
+ {
+ $path = PIWIK_INCLUDE_PATH . '/lang/' . $language . '.php';
+ if (!Piwik_Common::isValidFilename($language) || !is_readable($path)) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($language)));
+ }
+ require $path;
+ $this->mergeTranslationArray($translations);
+ $this->setLocale();
+ $this->loadedLanguage = $language;
+ }
+
+ public function mergeTranslationArray($translation)
+ {
+ if (!isset($GLOBALS['Piwik_translations'])) {
+ $GLOBALS['Piwik_translations'] = array();
+ }
+ // we could check that no string overlap here
+ $GLOBALS['Piwik_translations'] = array_merge($GLOBALS['Piwik_translations'], array_filter($translation, 'strlen'));
+ }
+
+ /**
+ * @return string the language filename prefix, eg 'en' for english
+ * @throws exception if the language set is not a valid filename
+ */
+ public function getLanguageToLoad()
+ {
+ if (is_null(self::$languageToLoad)) {
+ $lang = Piwik_Common::getRequestVar('language', '', 'string');
+
+ Piwik_PostEvent('Translate.getLanguageToLoad', $lang);
+
+ self::$languageToLoad = $lang;
+ }
+
+ return self::$languageToLoad;
+ }
+
+ /** Reset the cached language to load. Used in tests. */
+ static public function reset()
+ {
+ self::$languageToLoad = null;
+ }
+
+ public function getLanguageLoaded()
+ {
+ return $this->loadedLanguage;
+ }
+
+ public function getLanguageDefault()
+ {
+ return Piwik_Config::getInstance()->General['default_language'];
+ }
+
+ /**
+ * Generate javascript translations array
+ *
+ * @param array $moduleList
+ * @return string containing javascript code with translations array (including <script> tag)
+ */
+ public function getJavascriptTranslations(array $moduleList)
+ {
+ if (!in_array('General', $moduleList)) {
+ $moduleList[] = 'General';
+ }
+
+ $js = 'var translations = {';
+
+ $moduleRegex = '#^(';
+ foreach ($moduleList as $module) {
+ $moduleRegex .= $module . '|';
+ }
+ $moduleRegex = substr($moduleRegex, 0, -1);
+ $moduleRegex .= ')_.*_js$#i';
+
+ // Hack: common translations used in JS but not only, force as them to be defined in JS
+ $translations = $GLOBALS['Piwik_translations'];
+ $toSetInJs = array('General_Save', 'General_OrCancel');
+ foreach ($toSetInJs as $toSetId) {
+ $translations[$toSetId . '_js'] = $translations[$toSetId];
+ }
+ foreach ($translations as $key => $value) {
+ if (preg_match($moduleRegex, $key)) {
+ $js .= '"' . $key . '": "' . str_replace('"', '\"', $value) . '",';
+ }
+ }
+ $js = substr($js, 0, -1);
+ $js .= '};';
+ $js .= "\n" . 'if(typeof(piwik_translations) == \'undefined\') { var piwik_translations = new Object; }' .
+ 'for(var i in translations) { piwik_translations[i] = translations[i];} ';
+ $js .= 'function _pk_translate(translationStringId) { ' .
+ 'if( typeof(piwik_translations[translationStringId]) != \'undefined\' ){ return piwik_translations[translationStringId]; }' .
+ 'return "The string "+translationStringId+" was not loaded in javascript. Make sure it is suffixed with _js and that you called %7BloadJavascriptTranslations plugins=\'\$YOUR_PLUGIN_NAME\'%7D before your javascript code.";}';
+ return $js;
+ }
+
+ /**
+ * Set locale
+ *
+ * @see http://php.net/setlocale
+ */
+ private function setLocale()
+ {
+ $locale = $GLOBALS['Piwik_translations']['General_Locale'];
+ $locale_variant = str_replace('UTF-8', 'UTF8', $locale);
+ setlocale(LC_ALL, $locale, $locale_variant);
+ setlocale(LC_CTYPE, '');
+ }
}
/**
@@ -197,19 +186,16 @@ class Piwik_Translate
*/
function Piwik_Translate($string, $args = array())
{
- if(!is_array($args))
- {
- $args = array($args);
- }
- if(isset($GLOBALS['Piwik_translations'][$string]))
- {
- $string = $GLOBALS['Piwik_translations'][$string];
- }
- if(count($args) == 0)
- {
- return $string;
- }
- return vsprintf($string, $args);
+ if (!is_array($args)) {
+ $args = array($args);
+ }
+ if (isset($GLOBALS['Piwik_translations'][$string])) {
+ $string = $GLOBALS['Piwik_translations'][$string];
+ }
+ if (count($args) == 0) {
+ return $string;
+ }
+ return vsprintf($string, $args);
}
/**
@@ -222,11 +208,9 @@ function Piwik_Translate($string, $args = array())
*/
function Piwik_TranslateException($message, $args = array())
{
- try {
- return Piwik_Translate($message, $args);
- }
- catch(Exception $e)
- {
- return $message;
- }
+ try {
+ return Piwik_Translate($message, $args);
+ } catch (Exception $e) {
+ return $message;
+ }
}
diff --git a/core/TranslationWriter.php b/core/TranslationWriter.php
index 911044a96f..e2fbb9578c 100644
--- a/core/TranslationWriter.php
+++ b/core/TranslationWriter.php
@@ -1,13 +1,13 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
- *
+ *
*/
/**
@@ -17,120 +17,118 @@
*/
class Piwik_TranslationWriter
{
- static private $baseTranslation = null;
+ static private $baseTranslation = null;
- /**
- * Clean a string that may contain HTML special chars, single/double quotes, HTML entities, leading/trailing whitespace
- *
- * @param string $s
- * @return string
- */
- static public function clean($s)
- {
- return html_entity_decode(trim($s), ENT_QUOTES, 'UTF-8');
- }
+ /**
+ * Clean a string that may contain HTML special chars, single/double quotes, HTML entities, leading/trailing whitespace
+ *
+ * @param string $s
+ * @return string
+ */
+ static public function clean($s)
+ {
+ return html_entity_decode(trim($s), ENT_QUOTES, 'UTF-8');
+ }
- /**
- * Quote a "C" string
- *
- * @param string $s
- * @return string
- */
- static public function quote($s)
- {
- return "'" . addcslashes($s, "'") . "'";
- }
+ /**
+ * Quote a "C" string
+ *
+ * @param string $s
+ * @return string
+ */
+ static public function quote($s)
+ {
+ return "'" . addcslashes($s, "'") . "'";
+ }
- /**
- * Get translation file path
- *
- * @param string $lang ISO 639-1 alpha-2 language code
- * @param string $base Optional base directory (either 'lang' or 'tmp')
- * @throws Exception
- * @return string path
- */
- static public function getTranslationPath($lang, $base = 'lang')
- {
- if(!Piwik_Common::isValidFilename($lang) ||
- ($base !== 'lang' && $base !== 'tmp'))
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($lang)));
- }
+ /**
+ * Get translation file path
+ *
+ * @param string $lang ISO 639-1 alpha-2 language code
+ * @param string $base Optional base directory (either 'lang' or 'tmp')
+ * @throws Exception
+ * @return string path
+ */
+ static public function getTranslationPath($lang, $base = 'lang')
+ {
+ if (!Piwik_Common::isValidFilename($lang) ||
+ ($base !== 'lang' && $base !== 'tmp')
+ ) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($lang)));
+ }
- return PIWIK_INCLUDE_PATH . '/' . $base . '/' . $lang . '.php';
- }
+ return PIWIK_INCLUDE_PATH . '/' . $base . '/' . $lang . '.php';
+ }
- /**
- * Load translations from file
- *
- * @param string $lang ISO 639-1 alpha-2 language code
- * @throws Exception
- * @return array $translations Array of translations ( key => translated string )
- */
- static public function loadTranslation($lang)
- {
- $path = self::getTranslationPath($lang);
- if(!is_readable($path))
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($lang)));
- }
+ /**
+ * Load translations from file
+ *
+ * @param string $lang ISO 639-1 alpha-2 language code
+ * @throws Exception
+ * @return array $translations Array of translations ( key => translated string )
+ */
+ static public function loadTranslation($lang)
+ {
+ $path = self::getTranslationPath($lang);
+ if (!is_readable($path)) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($lang)));
+ }
- require $path;
- return $translations;
- }
+ require $path;
+ return $translations;
+ }
- /**
- * Output translations to string
- *
- * @param array $translations Array of translations ( key => translated string )
- * @return string
- */
- static public function outputTranslation($translations)
- {
- if(!self::$baseTranslation)
- {
- self::$baseTranslation = self::loadTranslation('en');
- }
- $en = self::$baseTranslation;
+ /**
+ * Output translations to string
+ *
+ * @param array $translations Array of translations ( key => translated string )
+ * @return string
+ */
+ static public function outputTranslation($translations)
+ {
+ if (!self::$baseTranslation) {
+ self::$baseTranslation = self::loadTranslation('en');
+ }
+ $en = self::$baseTranslation;
- $output = array('<?php', '$translations = array(');
- $deferoutput = array();
+ $output = array('<?php', '$translations = array(');
+ $deferoutput = array();
- /* write all the translations that exist in en.php */
- foreach($en as $key => $en_translation) {
- if(isset($translations[$key]) && !empty($translations[$key])) {
- $tmp = self::quote($translations[$key]);
- $output[] = "\t'$key' => $tmp,";
- }
- }
+ /* write all the translations that exist in en.php */
+ foreach ($en as $key => $en_translation) {
+ if (isset($translations[$key]) && !empty($translations[$key])) {
+ $tmp = self::quote($translations[$key]);
+ $output[] = "\t'$key' => $tmp,";
+ }
+ }
- /* write the remaining translations (that don't exist in en.php) */
- foreach($translations as $key => $translation) {
- if(!isset($en[$key]) && !empty($translation)) {
- $tmp = self::quote($translation);
- $deferoutput[] = "\t'$key' => $tmp,";
- }
- }
+ /* write the remaining translations (that don't exist in en.php) */
+ foreach ($translations as $key => $translation) {
+ if (!isset($en[$key]) && !empty($translation)) {
+ $tmp = self::quote($translation);
+ $deferoutput[] = "\t'$key' => $tmp,";
+ }
+ }
- if(count($deferoutput) > 0) {
- $output[] = "\n\t// FOR REVIEW";
- $output = array_merge($output, $deferoutput);
- }
+ if (count($deferoutput) > 0) {
+ $output[] = "\n\t// FOR REVIEW";
+ $output = array_merge($output, $deferoutput);
+ }
- $output[] = ');';
+ $output[] = ');';
- return implode($output, "\n") . "\n";
- }
+ return implode($output, "\n") . "\n";
+ }
- /**
- * Save translations to file; translations should already be cleansed.
- *
- * @param array $translations Array of translations ( key => translated string )
- * @param string $destinationPath Path of file to save translations to
- * @return bool|int False if failure, or number of bytes written
- */
- static public function saveTranslation($translations, $destinationPath)
- {
- return file_put_contents($destinationPath, self::outputTranslation($translations));
- }
+ /**
+ * Save translations to file; translations should already be cleansed.
+ *
+ * @param array $translations Array of translations ( key => translated string )
+ * @param string $destinationPath Path of file to save translations to
+ * @return bool|int False if failure, or number of bytes written
+ */
+ static public function saveTranslation($translations, $destinationPath)
+ {
+ return file_put_contents($destinationPath, self::outputTranslation($translations));
+ }
}
diff --git a/core/Unzip.php b/core/Unzip.php
index 5b21542519..ed915b27f7 100644
--- a/core/Unzip.php
+++ b/core/Unzip.php
@@ -1,7 +1,7 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
@@ -16,34 +16,33 @@
*/
class Piwik_Unzip
{
- /**
- * Factory method to create an unarchiver
- *
- * @param string $name Name of unarchiver
- * @param string $filename Name of .zip archive
- * @return Piwik_Unzip_Interface
- */
- static public function factory($name, $filename)
- {
- switch($name)
- {
- case 'ZipArchive':
- if(class_exists('ZipArchive', false))
- return new Piwik_Unzip_ZipArchive($filename);
- break;
- case 'tar.gz':
- return new Piwik_Unzip_Tar($filename, 'gz');
- case 'tar.bz2':
- return new Piwik_Unzip_Tar($filename, 'bz2');
- case 'gz':
- if (function_exists('gzopen'))
- return new Piwik_Unzip_Gzip($filename);
- break;
- case 'PclZip':
- default:
- return new Piwik_Unzip_PclZip($filename);
- }
-
- return new Piwik_Unzip_PclZip($filename);
- }
+ /**
+ * Factory method to create an unarchiver
+ *
+ * @param string $name Name of unarchiver
+ * @param string $filename Name of .zip archive
+ * @return Piwik_Unzip_Interface
+ */
+ static public function factory($name, $filename)
+ {
+ switch ($name) {
+ case 'ZipArchive':
+ if (class_exists('ZipArchive', false))
+ return new Piwik_Unzip_ZipArchive($filename);
+ break;
+ case 'tar.gz':
+ return new Piwik_Unzip_Tar($filename, 'gz');
+ case 'tar.bz2':
+ return new Piwik_Unzip_Tar($filename, 'bz2');
+ case 'gz':
+ if (function_exists('gzopen'))
+ return new Piwik_Unzip_Gzip($filename);
+ break;
+ case 'PclZip':
+ default:
+ return new Piwik_Unzip_PclZip($filename);
+ }
+
+ return new Piwik_Unzip_PclZip($filename);
+ }
}
diff --git a/core/Unzip/Gzip.php b/core/Unzip/Gzip.php
index 71badb87b4..b7d7c14a35 100755
--- a/core/Unzip/Gzip.php
+++ b/core/Unzip/Gzip.php
@@ -1,7 +1,7 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
@@ -11,76 +11,73 @@
/**
* Unzip implementation for .gz files.
- *
+ *
* @package Piwik
* @subpackage Piwik_Unzip
*/
class Piwik_Unzip_Gzip implements Piwik_Unzip_Interface
{
- /**
- * 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;
- }
+ /**
+ * 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;
- }
+ /**
+ * Get error status string for the latest error.
+ *
+ * @return string
+ */
+ public function errorInfo()
+ {
+ return $this->error;
+ }
}
diff --git a/core/Unzip/Interface.php b/core/Unzip/Interface.php
index 4935789f02..5c9bccd82d 100644
--- a/core/Unzip/Interface.php
+++ b/core/Unzip/Interface.php
@@ -1,7 +1,7 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
@@ -17,25 +17,25 @@
*/
interface Piwik_Unzip_Interface
{
- /**
- * Constructor
- *
- * @param string $filename Name of the .zip archive
- */
- public function __construct($filename);
+ /**
+ * 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);
+ /**
+ * 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();
+ /**
+ * Get error status string for the latest error
+ *
+ * @return string
+ */
+ public function errorInfo();
}
diff --git a/core/Unzip/PclZip.php b/core/Unzip/PclZip.php
index 1bcb83c29d..d6351cdca9 100644
--- a/core/Unzip/PclZip.php
+++ b/core/Unzip/PclZip.php
@@ -1,7 +1,7 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
@@ -22,69 +22,71 @@ require_once PIWIK_INCLUDE_PATH . '/libs/PclZip/pclzip.lib.php';
*/
class Piwik_Unzip_PclZip implements Piwik_Unzip_Interface
{
- /**
- * @var PclZip
- */
- private $pclzip;
- /**
- * @var string
- */
- public $filename;
+ /**
+ * @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;
- }
+ /**
+ * 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;
- }
+ /**
+ * 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);
+ 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;
- }
- }
+ 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, create_function(
- '$p_event, &$p_header',
- "return strncmp(\$p_header['filename'], '$pathExtracted', strlen('$pathExtracted')) ? 0 : 1;"
- )
- );
- }
+ // 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, create_function(
+ '$p_event, &$p_header',
+ "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);
- }
+ /**
+ * 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
index e565b12482..ad7aebf6b9 100755
--- a/core/Unzip/Tar.php
+++ b/core/Unzip/Tar.php
@@ -1,7 +1,7 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
@@ -16,70 +16,70 @@ require_once PIWIK_INCLUDE_PATH . '/libs/Archive_Tar/Tar.php';
/**
* Unzip implementation for Archive_Tar PEAR lib.
- *
+ *
* @package Piwik
* @subpackage Piwik_Unzip
*/
class Piwik_Unzip_Tar implements Piwik_Unzip_Interface
{
- /**
- * 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();
- }
+ /**
+ * 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();
- }
+ /**
+ * Get error status string for the latest error.
+ *
+ * @return string
+ */
+ public function errorInfo()
+ {
+ return $this->tarArchive->error_object->getMessage();
+ }
}
diff --git a/core/Unzip/ZipArchive.php b/core/Unzip/ZipArchive.php
index eb25a79cd4..c542f5f259 100644
--- a/core/Unzip/ZipArchive.php
+++ b/core/Unzip/ZipArchive.php
@@ -1,7 +1,7 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
@@ -17,117 +17,117 @@
*/
class Piwik_Unzip_ZipArchive implements Piwik_Unzip_Interface
{
- /**
- * @var ZipArchive
- */
- private $ziparchive;
- /**
- * @var string
- */
- public $filename;
+ /**
+ * @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);
- }
- }
+ /**
+ * 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_compare($pathExtracted, '/', -1))
- $pathExtracted .= '/';
+ /**
+ * 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_compare($pathExtracted, '/', -1))
+ $pathExtracted .= '/';
- $fileselector = array();
- $list = array();
- $count = $this->ziparchive->numFiles;
- if ($count === 0)
- {
- return 0;
- }
+ $fileselector = array();
+ $list = array();
+ $count = $this->ziparchive->numFiles;
+ if ($count === 0) {
+ return 0;
+ }
- for($i = 0; $i < $count; $i++) {
- $entry = $this->ziparchive->statIndex($i);
+ for ($i = 0; $i < $count; $i++) {
+ $entry = $this->ziparchive->statIndex($i);
- $filename = str_replace('\\', '/', $entry['name']);
- $parts = explode('/', $filename);
+ $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'],
- );
- }
+ 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;
- }
+ $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',
- );
+ /**
+ * 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 . ')';
- }
+ 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 2597ea2956..bbe2ad786d 100644
--- a/core/UpdateCheck.php
+++ b/core/UpdateCheck.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -14,79 +14,76 @@
*
* @package Piwik
*/
-class Piwik_UpdateCheck
+class Piwik_UpdateCheck
{
- const CHECK_INTERVAL = 28800; // every 8 hours
- const UI_CLICK_CHECK_INTERVAL = 10; // every 10s when user clicks UI link
- const LAST_TIME_CHECKED = 'UpdateCheck_LastTimeChecked';
- const LATEST_VERSION = 'UpdateCheck_LatestVersion';
- const SOCKET_TIMEOUT = 2;
+ const CHECK_INTERVAL = 28800; // every 8 hours
+ const UI_CLICK_CHECK_INTERVAL = 10; // every 10s when user clicks UI link
+ const LAST_TIME_CHECKED = 'UpdateCheck_LastTimeChecked';
+ const LATEST_VERSION = 'UpdateCheck_LatestVersion';
+ const SOCKET_TIMEOUT = 2;
+
+ /**
+ * Check for a newer version
+ *
+ * @param bool $force Force check
+ */
+ public static function check($force = false, $interval = null)
+ {
+ if ($interval === null) {
+ $interval = self::CHECK_INTERVAL;
+ }
+
+ $lastTimeChecked = Piwik_GetOption(self::LAST_TIME_CHECKED);
+ if ($force
+ || $lastTimeChecked === false
+ || time() - $interval > $lastTimeChecked
+ ) {
+ // set the time checked first, so that parallel Piwik requests don't all trigger the http requests
+ Piwik_SetOption(self::LAST_TIME_CHECKED, time(), $autoload = 1);
+ $parameters = array(
+ 'piwik_version' => Piwik_Version::VERSION,
+ 'php_version' => PHP_VERSION,
+ 'url' => Piwik_Url::getCurrentUrlWithoutQueryString(),
+ 'trigger' => Piwik_Common::getRequestVar('module', '', 'string'),
+ 'timezone' => Piwik_SitesManager_API::getInstance()->getDefaultTimezone(),
+ );
+
+ $url = Piwik_Config::getInstance()->General['api_service_url']
+ . '/1.0/getLatestVersion/'
+ . '?' . http_build_query($parameters, '', '&');
+ $timeout = self::SOCKET_TIMEOUT;
+
+ if (@Piwik_Config::getInstance()->Debug['allow_upgrades_to_beta']) {
+ $url = 'http://builds.piwik.org/LATEST_BETA';
+ }
- /**
- * Check for a newer version
- *
- * @param bool $force Force check
- */
- public static function check($force = false, $interval = null)
- {
- if ($interval === null)
- {
- $interval = self::CHECK_INTERVAL;
- }
-
- $lastTimeChecked = Piwik_GetOption(self::LAST_TIME_CHECKED);
- if($force
- || $lastTimeChecked === false
- || time() - $interval > $lastTimeChecked )
- {
- // set the time checked first, so that parallel Piwik requests don't all trigger the http requests
- Piwik_SetOption(self::LAST_TIME_CHECKED, time(), $autoload = 1);
- $parameters = array(
- 'piwik_version' => Piwik_Version::VERSION,
- 'php_version' => PHP_VERSION,
- 'url' => Piwik_Url::getCurrentUrlWithoutQueryString(),
- 'trigger' => Piwik_Common::getRequestVar('module','','string'),
- 'timezone' => Piwik_SitesManager_API::getInstance()->getDefaultTimezone(),
- );
+ try {
+ $latestVersion = Piwik_Http::sendHttpRequest($url, $timeout);
+ if (!preg_match('~^[0-9][0-9a-zA-Z_.-]*$~D', $latestVersion)) {
+ $latestVersion = '';
+ }
+ } catch (Exception $e) {
+ // e.g., disable_functions = fsockopen; allow_url_open = Off
+ $latestVersion = '';
+ }
+ Piwik_SetOption(self::LATEST_VERSION, $latestVersion);
+ }
+ }
- $url = Piwik_Config::getInstance()->General['api_service_url']
- . '/1.0/getLatestVersion/'
- . '?' . http_build_query($parameters, '', '&');
- $timeout = self::SOCKET_TIMEOUT;
-
- if(@Piwik_Config::getInstance()->Debug['allow_upgrades_to_beta'])
- {
- $url = 'http://builds.piwik.org/LATEST_BETA';
- }
-
- try {
- $latestVersion = Piwik_Http::sendHttpRequest($url, $timeout);
- if (!preg_match('~^[0-9][0-9a-zA-Z_.-]*$~D', $latestVersion))
- {
- $latestVersion = '';
- }
- } catch(Exception $e) {
- // e.g., disable_functions = fsockopen; allow_url_open = Off
- $latestVersion = '';
- }
- Piwik_SetOption(self::LATEST_VERSION, $latestVersion);
- }
- }
-
- /**
- * Returns version number of a newer Piwik release.
- *
- * @return string|false false if current version is the latest available,
- * or the latest version number if a newest release is available
- */
- public static function isNewestVersionAvailable()
- {
- $latestVersion = Piwik_GetOption(self::LATEST_VERSION);
- if(!empty($latestVersion)
- && version_compare(Piwik_Version::VERSION, $latestVersion) == -1)
- {
- return $latestVersion;
- }
- return false;
- }
+ /**
+ * Returns version number of a newer Piwik release.
+ *
+ * @return string|false false if current version is the latest available,
+ * or the latest version number if a newest release is available
+ */
+ public static function isNewestVersionAvailable()
+ {
+ $latestVersion = Piwik_GetOption(self::LATEST_VERSION);
+ if (!empty($latestVersion)
+ && version_compare(Piwik_Version::VERSION, $latestVersion) == -1
+ ) {
+ return $latestVersion;
+ }
+ return false;
+ }
}
diff --git a/core/Updater.php b/core/Updater.php
index 47dec55198..1b91f70349 100644
--- a/core/Updater.php
+++ b/core/Updater.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -22,310 +22,282 @@ require_once PIWIK_INCLUDE_PATH . '/core/Option.php';
*/
class Piwik_Updater
{
- const INDEX_CURRENT_VERSION = 0;
- const INDEX_NEW_VERSION = 1;
-
- public $pathUpdateFileCore;
- public $pathUpdateFilePlugins;
- private $componentsToCheck = array();
- private $hasMajorDbUpdate = false;
-
- public function __construct()
- {
- $this->pathUpdateFileCore = PIWIK_INCLUDE_PATH . '/core/Updates/';
- $this->pathUpdateFilePlugins = PIWIK_INCLUDE_PATH . '/plugins/%s/Updates/';
- }
+ const INDEX_CURRENT_VERSION = 0;
+ const INDEX_NEW_VERSION = 1;
+
+ public $pathUpdateFileCore;
+ public $pathUpdateFilePlugins;
+ private $componentsToCheck = array();
+ private $hasMajorDbUpdate = false;
+
+ public function __construct()
+ {
+ $this->pathUpdateFileCore = PIWIK_INCLUDE_PATH . '/core/Updates/';
+ $this->pathUpdateFilePlugins = PIWIK_INCLUDE_PATH . '/plugins/%s/Updates/';
+ }
+
+ /**
+ * Add component to check
+ *
+ * @param string $name
+ * @param string $version
+ */
+ public function addComponentToCheck($name, $version)
+ {
+ $this->componentsToCheck[$name] = $version;
+ }
+
+ /**
+ * Record version of successfully completed component update
+ *
+ * @param string $name
+ * @param string $version
+ */
+ public function recordComponentSuccessfullyUpdated($name, $version)
+ {
+ try {
+ Piwik_SetOption($this->getNameInOptionTable($name), $version, $autoload = 1);
+ } catch (Exception $e) {
+ // case when the option table is not yet created (before 0.2.10)
+ }
+ }
+
+ /**
+ * Returns the flag name to use in the option table to record current schema version
+ * @param string $name
+ * @return string
+ */
+ private function getNameInOptionTable($name)
+ {
+ return 'version_' . $name;
+ }
+
+ /**
+ * Returns a list of components (core | plugin) that need to run through the upgrade process.
+ *
+ * @return array( componentName => array( file1 => version1, [...]), [...])
+ */
+ public function getComponentsWithUpdateFile()
+ {
+ $this->componentsWithNewVersion = $this->getComponentsWithNewVersion();
+ $this->componentsWithUpdateFile = $this->loadComponentsWithUpdateFile();
+ return $this->componentsWithUpdateFile;
+ }
+
+ /**
+ * Component has a new version?
+ *
+ * @param string $componentName
+ * @return bool TRUE if compoment is to be updated; FALSE if not
+ */
+ public function hasNewVersion($componentName)
+ {
+ return isset($this->componentsWithNewVersion) &&
+ isset($this->componentsWithNewVersion[$componentName]);
+ }
- /**
- * Add component to check
- *
- * @param string $name
- * @param string $version
- */
- public function addComponentToCheck($name, $version)
- {
- $this->componentsToCheck[$name] = $version;
- }
-
- /**
- * Record version of successfully completed component update
- *
- * @param string $name
- * @param string $version
- */
- public function recordComponentSuccessfullyUpdated($name, $version)
- {
- try {
- Piwik_SetOption($this->getNameInOptionTable($name), $version, $autoload = 1);
- } catch(Exception $e) {
- // case when the option table is not yet created (before 0.2.10)
- }
- }
-
- /**
- * Returns the flag name to use in the option table to record current schema version
- * @param string $name
- * @return string
- */
- private function getNameInOptionTable($name)
- {
- return 'version_'.$name;
- }
-
- /**
- * Returns a list of components (core | plugin) that need to run through the upgrade process.
- *
- * @return array( componentName => array( file1 => version1, [...]), [...])
- */
- public function getComponentsWithUpdateFile()
- {
- $this->componentsWithNewVersion = $this->getComponentsWithNewVersion();
- $this->componentsWithUpdateFile = $this->loadComponentsWithUpdateFile();
- return $this->componentsWithUpdateFile;
- }
+ /**
+ * Does one of the new versions involve a major database update?
+ * Note: getSqlQueriesToExecute() must be called before this method!
+ *
+ * @return bool
+ */
+ public function hasMajorDbUpdate()
+ {
+ return $this->hasMajorDbUpdate;
+ }
- /**
- * Component has a new version?
- *
- * @param string $componentName
- * @return bool TRUE if compoment is to be updated; FALSE if not
- */
- public function hasNewVersion($componentName)
- {
- return isset($this->componentsWithNewVersion) &&
- isset($this->componentsWithNewVersion[$componentName]);
- }
+ /**
+ * Returns the list of SQL queries that would be executed during the update
+ *
+ * @return array of SQL queries
+ */
+ public function getSqlQueriesToExecute()
+ {
+ $queries = array();
+ foreach ($this->componentsWithUpdateFile as $componentName => $componentUpdateInfo) {
+ foreach ($componentUpdateInfo as $file => $fileVersion) {
+ require_once $file; // prefixed by PIWIK_INCLUDE_PATH
- /**
- * Does one of the new versions involve a major database update?
- * Note: getSqlQueriesToExecute() must be called before this method!
- *
- * @return bool
- */
- public function hasMajorDbUpdate()
- {
- return $this->hasMajorDbUpdate;
- }
+ $className = $this->getUpdateClassName($componentName, $fileVersion);
+ if (class_exists($className, false)) {
+ $queriesForComponent = call_user_func(array($className, 'getSql'));
+ foreach ($queriesForComponent as $query => $error) {
+ $queries[] = $query . ';';
+ }
- /**
- * Returns the list of SQL queries that would be executed during the update
- *
- * @return array of SQL queries
- */
- public function getSqlQueriesToExecute()
- {
- $queries = array();
- foreach($this->componentsWithUpdateFile as $componentName => $componentUpdateInfo)
- {
- foreach($componentUpdateInfo as $file => $fileVersion)
- {
- require_once $file; // prefixed by PIWIK_INCLUDE_PATH
+ $this->hasMajorDbUpdate = $this->hasMajorDbUpdate || call_user_func(array($className, 'isMajorUpdate'));
+ }
+ }
+ // unfortunately had to extract this query from the Piwik_Option class
+ $queries[] = 'UPDATE `' . Piwik_Common::prefixTable('option') . '`
+ SET option_value = \'' . $fileVersion . '\'
+ WHERE option_name = \'' . $this->getNameInOptionTable($componentName) . '\';';
+ }
+ return $queries;
+ }
- $className = $this->getUpdateClassName($componentName, $fileVersion);
- if(class_exists($className, false))
- {
- $queriesForComponent = call_user_func( array($className, 'getSql'));
- foreach($queriesForComponent as $query => $error) {
- $queries[] = $query.';';
- }
-
- $this->hasMajorDbUpdate = $this->hasMajorDbUpdate || call_user_func( array($className, 'isMajorUpdate'));
- }
- }
- // unfortunately had to extract this query from the Piwik_Option class
- $queries[] = 'UPDATE `'.Piwik_Common::prefixTable('option').'`
- SET option_value = \'' .$fileVersion.'\'
- WHERE option_name = \''. $this->getNameInOptionTable($componentName).'\';';
- }
- return $queries;
- }
-
- private function getUpdateClassName($componentName, $fileVersion)
- {
- $suffix = strtolower(str_replace(array('-','.'), '_', $fileVersion));
- if($componentName == 'core')
- {
- return 'Piwik_Updates_' . $suffix;
- }
- return 'Piwik_'. $componentName .'_Updates_' . $suffix;
- }
+ private function getUpdateClassName($componentName, $fileVersion)
+ {
+ $suffix = strtolower(str_replace(array('-', '.'), '_', $fileVersion));
+ if ($componentName == 'core') {
+ return 'Piwik_Updates_' . $suffix;
+ }
+ return 'Piwik_' . $componentName . '_Updates_' . $suffix;
+ }
- /**
- * Update the named component
- *
- * @param string $componentName 'core', or plugin name
- * @throws Exception|Piwik_Updater_UpdateErrorException
- * @return array of warning strings if applicable
- */
- public function update($componentName)
- {
- $warningMessages = array();
- foreach($this->componentsWithUpdateFile[$componentName] as $file => $fileVersion)
- {
- try {
- require_once $file; // prefixed by PIWIK_INCLUDE_PATH
+ /**
+ * Update the named component
+ *
+ * @param string $componentName 'core', or plugin name
+ * @throws Exception|Piwik_Updater_UpdateErrorException
+ * @return array of warning strings if applicable
+ */
+ public function update($componentName)
+ {
+ $warningMessages = array();
+ foreach ($this->componentsWithUpdateFile[$componentName] as $file => $fileVersion) {
+ try {
+ require_once $file; // prefixed by PIWIK_INCLUDE_PATH
- $className = $this->getUpdateClassName($componentName, $fileVersion);
- if(class_exists($className, false))
- {
- call_user_func( array($className, 'update') );
- }
+ $className = $this->getUpdateClassName($componentName, $fileVersion);
+ if (class_exists($className, false)) {
+ call_user_func(array($className, 'update'));
+ }
- $this->recordComponentSuccessfullyUpdated($componentName, $fileVersion);
- } catch( Piwik_Updater_UpdateErrorException $e) {
- throw $e;
- } catch( Exception $e) {
- $warningMessages[] = $e->getMessage();
- }
- }
-
- // to debug, create core/Updates/X.php, update the core/Version.php, throw an Exception in the try, and comment the following line
- $this->recordComponentSuccessfullyUpdated($componentName, $this->componentsWithNewVersion[$componentName][self::INDEX_NEW_VERSION]);
- return $warningMessages;
- }
+ $this->recordComponentSuccessfullyUpdated($componentName, $fileVersion);
+ } catch (Piwik_Updater_UpdateErrorException $e) {
+ throw $e;
+ } catch (Exception $e) {
+ $warningMessages[] = $e->getMessage();
+ }
+ }
- /**
- * Construct list of update files for the outdated components
- *
- * @return array( componentName => array( file1 => version1, [...]), [...])
- */
- private function loadComponentsWithUpdateFile()
- {
- $componentsWithUpdateFile = array();
- foreach($this->componentsWithNewVersion as $name => $versions)
- {
- $currentVersion = $versions[self::INDEX_CURRENT_VERSION];
- $newVersion = $versions[self::INDEX_NEW_VERSION];
-
- if($name == 'core')
- {
- $pathToUpdates = $this->pathUpdateFileCore . '*.php';
- }
- else
- {
- $pathToUpdates = sprintf($this->pathUpdateFilePlugins, $name) . '*.php';
- }
-
- $files = _glob( $pathToUpdates );
- if($files == false)
- {
- $files = array();
- }
+ // to debug, create core/Updates/X.php, update the core/Version.php, throw an Exception in the try, and comment the following line
+ $this->recordComponentSuccessfullyUpdated($componentName, $this->componentsWithNewVersion[$componentName][self::INDEX_NEW_VERSION]);
+ return $warningMessages;
+ }
- foreach( $files as $file)
- {
- $fileVersion = basename($file, '.php');
- if( // if the update is from a newer version
- version_compare($currentVersion, $fileVersion) == -1
- // but we don't execute updates from non existing future releases
- && version_compare($fileVersion, $newVersion) <= 0)
- {
- $componentsWithUpdateFile[$name][$file] = $fileVersion;
- }
- }
-
- if(isset($componentsWithUpdateFile[$name]))
- {
- // order the update files by version asc
- uasort($componentsWithUpdateFile[$name], "version_compare");
- }
- else
- {
- // there are no update file => nothing to do, update to the new version is successful
- $this->recordComponentSuccessfullyUpdated($name, $newVersion);
- }
- }
- return $componentsWithUpdateFile;
- }
+ /**
+ * Construct list of update files for the outdated components
+ *
+ * @return array( componentName => array( file1 => version1, [...]), [...])
+ */
+ private function loadComponentsWithUpdateFile()
+ {
+ $componentsWithUpdateFile = array();
+ foreach ($this->componentsWithNewVersion as $name => $versions) {
+ $currentVersion = $versions[self::INDEX_CURRENT_VERSION];
+ $newVersion = $versions[self::INDEX_NEW_VERSION];
- /**
- * Construct list of outdated components
- *
- * @throws Exception
- * @return array array( componentName => array( oldVersion, newVersion), [...])
- */
- public function getComponentsWithNewVersion()
- {
- $componentsToUpdate = array();
-
- // we make sure core updates are processed before any plugin updates
- if(isset($this->componentsToCheck['core']))
- {
- $coreVersions = $this->componentsToCheck['core'];
- unset($this->componentsToCheck['core']);
- $this->componentsToCheck = array_merge( array('core' => $coreVersions), $this->componentsToCheck);
- }
-
- foreach($this->componentsToCheck as $name => $version)
- {
- try {
- $currentVersion = Piwik_GetOption('version_'.$name);
- } catch( Exception $e) {
- // mysql error 1146: table doesn't exist
- if(Zend_Registry::get('db')->isErrNo($e, '1146'))
- {
- // case when the option table is not yet created (before 0.2.10)
- $currentVersion = false;
- }
- else
- {
- // failed for some other reason
- throw $e;
- }
- }
- if($currentVersion === false)
- {
- if($name === 'core')
- {
- // This should not happen
- $currentVersion = Piwik_Version::VERSION;
- }
- else
- {
- $currentVersion = '0.0.1';
- }
- $this->recordComponentSuccessfullyUpdated($name, $currentVersion);
- }
+ if ($name == 'core') {
+ $pathToUpdates = $this->pathUpdateFileCore . '*.php';
+ } else {
+ $pathToUpdates = sprintf($this->pathUpdateFilePlugins, $name) . '*.php';
+ }
- $versionCompare = version_compare($currentVersion, $version);
- if($versionCompare == -1)
- {
- $componentsToUpdate[$name] = array(
- self::INDEX_CURRENT_VERSION => $currentVersion,
- self::INDEX_NEW_VERSION => $version
- );
- }
- else if($versionCompare == 1)
- {
- // the version in the DB is newest.. we choose to ignore (for the time being)
- }
- }
- return $componentsToUpdate;
- }
+ $files = _glob($pathToUpdates);
+ if ($files == false) {
+ $files = array();
+ }
- /**
- * Performs database update(s)
- *
- * @param string $file Update script filename
- * @param array $sqlarray An array of SQL queries to be executed
- * @throws Piwik_Updater_UpdateErrorException
- */
- static function updateDatabase($file, $sqlarray)
- {
- foreach($sqlarray as $update => $ignoreError)
- {
- try {
- Piwik_Exec( $update );
- } catch(Exception $e) {
- if(($ignoreError === false)
- || !Zend_Registry::get('db')->isErrNo($e, $ignoreError))
- {
- $message = $file .":\nError trying to execute the query '". $update ."'.\nThe error was: ". $e->getMessage();
- throw new Piwik_Updater_UpdateErrorException($message);
- }
- }
- }
- }
+ foreach ($files as $file) {
+ $fileVersion = basename($file, '.php');
+ if ( // if the update is from a newer version
+ version_compare($currentVersion, $fileVersion) == -1
+ // but we don't execute updates from non existing future releases
+ && version_compare($fileVersion, $newVersion) <= 0
+ ) {
+ $componentsWithUpdateFile[$name][$file] = $fileVersion;
+ }
+ }
+
+ if (isset($componentsWithUpdateFile[$name])) {
+ // order the update files by version asc
+ uasort($componentsWithUpdateFile[$name], "version_compare");
+ } else {
+ // there are no update file => nothing to do, update to the new version is successful
+ $this->recordComponentSuccessfullyUpdated($name, $newVersion);
+ }
+ }
+ return $componentsWithUpdateFile;
+ }
+
+ /**
+ * Construct list of outdated components
+ *
+ * @throws Exception
+ * @return array array( componentName => array( oldVersion, newVersion), [...])
+ */
+ public function getComponentsWithNewVersion()
+ {
+ $componentsToUpdate = array();
+
+ // we make sure core updates are processed before any plugin updates
+ if (isset($this->componentsToCheck['core'])) {
+ $coreVersions = $this->componentsToCheck['core'];
+ unset($this->componentsToCheck['core']);
+ $this->componentsToCheck = array_merge(array('core' => $coreVersions), $this->componentsToCheck);
+ }
+
+ foreach ($this->componentsToCheck as $name => $version) {
+ try {
+ $currentVersion = Piwik_GetOption('version_' . $name);
+ } catch (Exception $e) {
+ // mysql error 1146: table doesn't exist
+ if (Zend_Registry::get('db')->isErrNo($e, '1146')) {
+ // case when the option table is not yet created (before 0.2.10)
+ $currentVersion = false;
+ } else {
+ // failed for some other reason
+ throw $e;
+ }
+ }
+ if ($currentVersion === false) {
+ if ($name === 'core') {
+ // This should not happen
+ $currentVersion = Piwik_Version::VERSION;
+ } else {
+ $currentVersion = '0.0.1';
+ }
+ $this->recordComponentSuccessfullyUpdated($name, $currentVersion);
+ }
+
+ $versionCompare = version_compare($currentVersion, $version);
+ if ($versionCompare == -1) {
+ $componentsToUpdate[$name] = array(
+ self::INDEX_CURRENT_VERSION => $currentVersion,
+ self::INDEX_NEW_VERSION => $version
+ );
+ } else if ($versionCompare == 1) {
+ // the version in the DB is newest.. we choose to ignore (for the time being)
+ }
+ }
+ return $componentsToUpdate;
+ }
+
+ /**
+ * Performs database update(s)
+ *
+ * @param string $file Update script filename
+ * @param array $sqlarray An array of SQL queries to be executed
+ * @throws Piwik_Updater_UpdateErrorException
+ */
+ static function updateDatabase($file, $sqlarray)
+ {
+ foreach ($sqlarray as $update => $ignoreError) {
+ try {
+ Piwik_Exec($update);
+ } catch (Exception $e) {
+ if (($ignoreError === false)
+ || !Zend_Registry::get('db')->isErrNo($e, $ignoreError)
+ ) {
+ $message = $file . ":\nError trying to execute the query '" . $update . "'.\nThe error was: " . $e->getMessage();
+ throw new Piwik_Updater_UpdateErrorException($message);
+ }
+ }
+ }
+ }
}
/**
@@ -334,4 +306,6 @@ class Piwik_Updater
* @package Piwik
* @subpackage Piwik_Updater
*/
-class Piwik_Updater_UpdateErrorException extends Exception {}
+class Piwik_Updater_UpdateErrorException extends Exception
+{
+}
diff --git a/core/Updates.php b/core/Updates.php
index 386f0abc89..91bdca17e7 100644
--- a/core/Updates.php
+++ b/core/Updates.php
@@ -17,102 +17,100 @@
*/
abstract class Piwik_Updates
{
- /**
- * Return SQL to be executed in this update
- *
- * @param string $schema Schema name
- * @return array(
- * 'ALTER .... ' => '1234', // if the query fails, it will be ignored if the error code is 1234
- * 'ALTER .... ' => false, // if an error occurs, the update will stop and fail
- * // and user will have to manually run the query
- * )
- */
- static function getSql($schema = 'Myisam')
- {
- return array();
- }
-
- /**
- * Incremental version update
- */
- static function update()
- {
- }
-
- /**
- * Tell the updater that this is a major update.
- * Leads to a more visible notice.
- */
- static function isMajorUpdate()
- {
- return false;
- }
-
- /**
- * Helper method to enable maintenance mode during large updates
- */
- static function enableMaintenanceMode()
- {
- $config = Piwik_Config::getInstance();
- $config->init();
-
- $tracker = $config->Tracker;
- $tracker['record_statistics'] = 0;
- $config->Tracker = $tracker;
-
- $general = $config->General;
- $general['maintenance_mode'] = 1;
- $config->General = $general;
-
- $config->forceSave();
- }
-
- /**
- * Helper method to disable maintenance mode after large updates
- */
- static function disableMaintenanceMode()
- {
- $config = Piwik_Config::getInstance();
- $config->init();
-
- $tracker = $config->Tracker;
- $tracker['record_statistics'] = 1;
- $config->Tracker = $tracker;
-
- $general = $config->General;
- $general['maintenance_mode'] = 0;
- $config->General = $general;
-
- $config->forceSave();
- }
-
-
-
- public static function deletePluginFromConfigFile($pluginToDelete)
- {
- $config = Piwik_Config::getInstance();
- $config->init();
- if (isset($config->Plugins['Plugins']))
- {
- $plugins = $config->Plugins['Plugins'];
- if (($key = array_search($pluginToDelete, $plugins)) !== false) {
- unset($plugins[$key]);
- }
- $config->Plugins['Plugins'] = $plugins;
-
- $pluginsInstalled = $config->PluginsInstalled['PluginsInstalled'];
- if (($key = array_search($pluginToDelete, $pluginsInstalled)) !== false) {
- unset($pluginsInstalled[$key]);
- }
- $config->PluginsInstalled = $pluginsInstalled;
-
- $config->forceSave();
- }
- }
-
- public static function deletePluginFromFilesystem($plugin)
- {
- Piwik::unlinkRecursive( PIWIK_INCLUDE_PATH . '/plugins/' . $plugin, $deleteRootToo = true);
- }
+ /**
+ * Return SQL to be executed in this update
+ *
+ * @param string $schema Schema name
+ * @return array(
+ * 'ALTER .... ' => '1234', // if the query fails, it will be ignored if the error code is 1234
+ * 'ALTER .... ' => false, // if an error occurs, the update will stop and fail
+ * // and user will have to manually run the query
+ * )
+ */
+ static function getSql($schema = 'Myisam')
+ {
+ return array();
+ }
+
+ /**
+ * Incremental version update
+ */
+ static function update()
+ {
+ }
+
+ /**
+ * Tell the updater that this is a major update.
+ * Leads to a more visible notice.
+ */
+ static function isMajorUpdate()
+ {
+ return false;
+ }
+
+ /**
+ * Helper method to enable maintenance mode during large updates
+ */
+ static function enableMaintenanceMode()
+ {
+ $config = Piwik_Config::getInstance();
+ $config->init();
+
+ $tracker = $config->Tracker;
+ $tracker['record_statistics'] = 0;
+ $config->Tracker = $tracker;
+
+ $general = $config->General;
+ $general['maintenance_mode'] = 1;
+ $config->General = $general;
+
+ $config->forceSave();
+ }
+
+ /**
+ * Helper method to disable maintenance mode after large updates
+ */
+ static function disableMaintenanceMode()
+ {
+ $config = Piwik_Config::getInstance();
+ $config->init();
+
+ $tracker = $config->Tracker;
+ $tracker['record_statistics'] = 1;
+ $config->Tracker = $tracker;
+
+ $general = $config->General;
+ $general['maintenance_mode'] = 0;
+ $config->General = $general;
+
+ $config->forceSave();
+ }
+
+
+ public static function deletePluginFromConfigFile($pluginToDelete)
+ {
+ $config = Piwik_Config::getInstance();
+ $config->init();
+ if (isset($config->Plugins['Plugins'])) {
+ $plugins = $config->Plugins['Plugins'];
+ if (($key = array_search($pluginToDelete, $plugins)) !== false) {
+ unset($plugins[$key]);
+ }
+ $config->Plugins['Plugins'] = $plugins;
+
+ $pluginsInstalled = $config->PluginsInstalled['PluginsInstalled'];
+ if (($key = array_search($pluginToDelete, $pluginsInstalled)) !== false) {
+ unset($pluginsInstalled[$key]);
+ }
+ $config->PluginsInstalled = $pluginsInstalled;
+
+ $config->forceSave();
+ }
+ }
+
+ public static function deletePluginFromFilesystem($plugin)
+ {
+ Piwik::unlinkRecursive(PIWIK_INCLUDE_PATH . '/plugins/' . $plugin, $deleteRootToo = true);
+ }
}
diff --git a/core/Updates/0.2.10.php b/core/Updates/0.2.10.php
index a411c2ad4e..aece273630 100644
--- a/core/Updates/0.2.10.php
+++ b/core/Updates/0.2.10.php
@@ -14,59 +14,56 @@
*/
class Piwik_Updates_0_2_10 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'CREATE TABLE `'. Piwik_Common::prefixTable('option') .'` (
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'CREATE TABLE `' . Piwik_Common::prefixTable('option') . '` (
idoption BIGINT NOT NULL AUTO_INCREMENT ,
option_name VARCHAR( 64 ) NOT NULL ,
option_value LONGTEXT NOT NULL ,
PRIMARY KEY ( idoption , option_name )
- )' => false,
+ )' => false,
- // 0.1.7 [463]
- 'ALTER IGNORE TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ // 0.1.7 [463]
+ 'ALTER IGNORE TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
CHANGE `location_provider` `location_provider` VARCHAR( 100 ) DEFAULT NULL' => '1054',
- // 0.1.7 [470]
- 'ALTER TABLE `'. Piwik_Common::prefixTable('logger_api_call') .'`
+ // 0.1.7 [470]
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('logger_api_call') . '`
CHANGE `parameter_names_default_values` `parameter_names_default_values` TEXT,
CHANGE `parameter_values` `parameter_values` TEXT,
- CHANGE `returned_value` `returned_value` TEXT' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('logger_error') .'`
- CHANGE `message` `message` TEXT' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('logger_exception') .'`
- CHANGE `message` `message` TEXT' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('logger_message') .'`
- CHANGE `message` `message` TEXT' => false,
+ CHANGE `returned_value` `returned_value` TEXT' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('logger_error') . '`
+ CHANGE `message` `message` TEXT' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('logger_exception') . '`
+ CHANGE `message` `message` TEXT' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('logger_message') . '`
+ CHANGE `message` `message` TEXT' => false,
- // 0.2.2 [489]
- 'ALTER IGNORE TABLE `'. Piwik_Common::prefixTable('site') .'`
- CHANGE `feedburnerName` `feedburnerName` VARCHAR( 100 ) DEFAULT NULL' => '1054',
- );
- }
+ // 0.2.2 [489]
+ 'ALTER IGNORE TABLE `' . Piwik_Common::prefixTable('site') . '`
+ CHANGE `feedburnerName` `feedburnerName` VARCHAR( 100 ) DEFAULT NULL' => '1054',
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- $obsoleteFile = '/plugins/ExamplePlugin/API.php';
- if(file_exists(PIWIK_INCLUDE_PATH . $obsoleteFile))
- {
- @unlink(PIWIK_INCLUDE_PATH . $obsoleteFile);
- }
+ $obsoleteFile = '/plugins/ExamplePlugin/API.php';
+ if (file_exists(PIWIK_INCLUDE_PATH . $obsoleteFile)) {
+ @unlink(PIWIK_INCLUDE_PATH . $obsoleteFile);
+ }
- $obsoleteDirectories = array(
- '/plugins/AdminHome',
- '/plugins/Home',
- '/plugins/PluginsAdmin',
- );
- foreach($obsoleteDirectories as $dir)
- {
- if(file_exists(PIWIK_INCLUDE_PATH . $dir))
- {
- Piwik::unlinkRecursive(PIWIK_INCLUDE_PATH . $dir, true);
- }
- }
- }
+ $obsoleteDirectories = array(
+ '/plugins/AdminHome',
+ '/plugins/Home',
+ '/plugins/PluginsAdmin',
+ );
+ foreach ($obsoleteDirectories as $dir) {
+ if (file_exists(PIWIK_INCLUDE_PATH . $dir)) {
+ Piwik::unlinkRecursive(PIWIK_INCLUDE_PATH . $dir, true);
+ }
+ }
+ }
}
diff --git a/core/Updates/0.2.12.php b/core/Updates/0.2.12.php
index c4cc194449..f6ac41e839 100644
--- a/core/Updates/0.2.12.php
+++ b/core/Updates/0.2.12.php
@@ -14,22 +14,22 @@
*/
class Piwik_Updates_0_2_12 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('site') .'`
- CHANGE `ts_created` `ts_created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('site') . '`
+ CHANGE `ts_created` `ts_created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
DROP `config_color_depth`' => false,
- // 0.2.12 [673]
- // Note: requires INDEX privilege
- 'DROP INDEX index_idaction ON `'. Piwik_Common::prefixTable('log_action') .'`' => '1091',
- );
- }
+ // 0.2.12 [673]
+ // Note: requires INDEX privilege
+ 'DROP INDEX index_idaction ON `' . Piwik_Common::prefixTable('log_action') . '`' => '1091',
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.2.13.php b/core/Updates/0.2.13.php
index a6b4293344..1572cd73bd 100644
--- a/core/Updates/0.2.13.php
+++ b/core/Updates/0.2.13.php
@@ -14,22 +14,22 @@
*/
class Piwik_Updates_0_2_13 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'DROP TABLE IF EXISTS `'. Piwik_Common::prefixTable('option') .'`' => false,
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'DROP TABLE IF EXISTS `' . Piwik_Common::prefixTable('option') . '`' => false,
- 'CREATE TABLE `'. Piwik_Common::prefixTable('option') ."` (
+ 'CREATE TABLE `' . Piwik_Common::prefixTable('option') . "` (
option_name VARCHAR( 64 ) NOT NULL ,
option_value LONGTEXT NOT NULL ,
autoload TINYINT NOT NULL DEFAULT '1',
PRIMARY KEY ( option_name )
)" => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.2.24.php b/core/Updates/0.2.24.php
index c6dbfdcb7a..594a461805 100644
--- a/core/Updates/0.2.24.php
+++ b/core/Updates/0.2.24.php
@@ -14,20 +14,20 @@
*/
class Piwik_Updates_0_2_24 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'CREATE INDEX index_type_name
- ON '. Piwik_Common::prefixTable('log_action') .' (type, name(15))' => false,
- 'CREATE INDEX index_idsite_date
- ON '. Piwik_Common::prefixTable('log_visit') .' (idsite, visit_server_date)' => false,
- 'DROP INDEX index_idsite ON '. Piwik_Common::prefixTable('log_visit') => false,
- 'DROP INDEX index_visit_server_date ON '. Piwik_Common::prefixTable('log_visit') => false,
- );
- }
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'CREATE INDEX index_type_name
+ ON ' . Piwik_Common::prefixTable('log_action') . ' (type, name(15))' => false,
+ 'CREATE INDEX index_idsite_date
+ ON ' . Piwik_Common::prefixTable('log_visit') . ' (idsite, visit_server_date)' => false,
+ 'DROP INDEX index_idsite ON ' . Piwik_Common::prefixTable('log_visit') => false,
+ 'DROP INDEX index_visit_server_date ON ' . Piwik_Common::prefixTable('log_visit') => false,
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.2.27.php b/core/Updates/0.2.27.php
index 766cde3014..052033e9bf 100644
--- a/core/Updates/0.2.27.php
+++ b/core/Updates/0.2.27.php
@@ -14,16 +14,16 @@
*/
class Piwik_Updates_0_2_27 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- $sqlarray = array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
- ADD `visit_goal_converted` VARCHAR( 1 ) NOT NULL AFTER `visit_total_time`' => false,
- // 0.2.27 [826]
- 'ALTER IGNORE TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ $sqlarray = array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
+ ADD `visit_goal_converted` VARCHAR( 1 ) NOT NULL AFTER `visit_total_time`' => false,
+ // 0.2.27 [826]
+ 'ALTER IGNORE TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
CHANGE `visit_goal_converted` `visit_goal_converted` TINYINT(1) NOT NULL' => false,
- 'CREATE TABLE `'. Piwik_Common::prefixTable('goal') ."` (
+ 'CREATE TABLE `' . Piwik_Common::prefixTable('goal') . "` (
`idsite` int(11) NOT NULL,
`idgoal` int(11) NOT NULL,
`name` varchar(50) NOT NULL,
@@ -34,9 +34,9 @@ class Piwik_Updates_0_2_27 extends Piwik_Updates
`revenue` float NOT NULL,
`deleted` tinyint(4) NOT NULL default '0',
PRIMARY KEY (`idsite`,`idgoal`)
- )" => false,
+ )" => false,
- 'CREATE TABLE `'. Piwik_Common::prefixTable('log_conversion') .'` (
+ 'CREATE TABLE `' . Piwik_Common::prefixTable('log_conversion') . '` (
`idvisit` int(10) unsigned NOT NULL,
`idsite` int(10) unsigned NOT NULL,
`visitor_idcookie` char(32) NOT NULL,
@@ -57,23 +57,21 @@ class Piwik_Updates_0_2_27 extends Piwik_Updates
`revenue` float default NULL,
PRIMARY KEY (`idvisit`,`idgoal`),
KEY `index_idsite_date` (`idsite`,`visit_server_date`)
- )' => false,
- );
+ )' => false,
+ );
- $tables = Piwik::getTablesInstalled();
- foreach($tables as $tableName)
- {
- if(preg_match('/archive_/', $tableName) == 1)
- {
- $sqlarray[ 'CREATE INDEX index_all ON '. $tableName .' (`idsite`,`date1`,`date2`,`name`,`ts_archived`)' ] = false;
- }
- }
+ $tables = Piwik::getTablesInstalled();
+ foreach ($tables as $tableName) {
+ if (preg_match('/archive_/', $tableName) == 1) {
+ $sqlarray['CREATE INDEX index_all ON ' . $tableName . ' (`idsite`,`date1`,`date2`,`name`,`ts_archived`)'] = false;
+ }
+ }
- return $sqlarray;
- }
+ return $sqlarray;
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.2.32.php b/core/Updates/0.2.32.php
index d471b336e3..7a3bbfac42 100644
--- a/core/Updates/0.2.32.php
+++ b/core/Updates/0.2.32.php
@@ -14,23 +14,23 @@
*/
class Piwik_Updates_0_2_32 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- // 0.2.32 [941]
- 'ALTER TABLE `'. Piwik_Common::prefixTable('access') .'`
- CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('user') .'`
- CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('user_dashboard') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ // 0.2.32 [941]
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('access') . '`
+ CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('user') . '`
+ CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('user_dashboard') . '`
CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => '1146',
- 'ALTER TABLE `'. Piwik_Common::prefixTable('user_language') .'`
- CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => '1146',
- );
- }
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('user_language') . '`
+ CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => '1146',
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.2.33.php b/core/Updates/0.2.33.php
index 1b0f798957..0ad0b04b1b 100644
--- a/core/Updates/0.2.33.php
+++ b/core/Updates/0.2.33.php
@@ -14,28 +14,28 @@
*/
class Piwik_Updates_0_2_33 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- $sqlarray = array(
- // 0.2.33 [1020]
- 'ALTER TABLE `'. Piwik_Common::prefixTable('user_dashboard') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ $sqlarray = array(
+ // 0.2.33 [1020]
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('user_dashboard') . '`
+ CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci ' => '1146',
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('user_language') . '`
CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci ' => '1146',
- 'ALTER TABLE `'. Piwik_Common::prefixTable('user_language') .'`
- CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci ' => '1146',
- );
+ );
- // alter table to set the utf8 collation
- $tablesToAlter = Piwik::getTablesInstalled(true);
- foreach($tablesToAlter as $table) {
- $sqlarray[ 'ALTER TABLE `'. $table .'`
- CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci ' ] = false;
- }
+ // alter table to set the utf8 collation
+ $tablesToAlter = Piwik::getTablesInstalled(true);
+ foreach ($tablesToAlter as $table) {
+ $sqlarray['ALTER TABLE `' . $table . '`
+ CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci '] = false;
+ }
- return $sqlarray;
- }
+ return $sqlarray;
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.2.34.php b/core/Updates/0.2.34.php
index 3da37d68d6..cf52c90ff6 100644
--- a/core/Updates/0.2.34.php
+++ b/core/Updates/0.2.34.php
@@ -14,11 +14,11 @@
*/
class Piwik_Updates_0_2_34 extends Piwik_Updates
{
- static function update($schema = 'Myisam')
- {
- // force regeneration of cache files following #648
- Piwik::setUserIsSuperUser();
- $allSiteIds = Piwik_SitesManager_API::getInstance()->getAllSitesId();
- Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($allSiteIds);
- }
+ static function update($schema = 'Myisam')
+ {
+ // force regeneration of cache files following #648
+ Piwik::setUserIsSuperUser();
+ $allSiteIds = Piwik_SitesManager_API::getInstance()->getAllSitesId();
+ Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($allSiteIds);
+ }
}
diff --git a/core/Updates/0.2.35.php b/core/Updates/0.2.35.php
index 442ef141f9..9f0f80abe7 100644
--- a/core/Updates/0.2.35.php
+++ b/core/Updates/0.2.35.php
@@ -14,16 +14,16 @@
*/
class Piwik_Updates_0_2_35 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('user_dashboard') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('user_dashboard') . '`
CHANGE `layout` `layout` TEXT NOT NULL' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.2.37.php b/core/Updates/0.2.37.php
index eab56fb2bf..ca859c5f94 100644
--- a/core/Updates/0.2.37.php
+++ b/core/Updates/0.2.37.php
@@ -14,17 +14,17 @@
*/
class Piwik_Updates_0_2_37 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'DELETE FROM `'. Piwik_Common::prefixTable('user_dashboard') ."`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'DELETE FROM `' . Piwik_Common::prefixTable('user_dashboard') . "`
WHERE layout LIKE '%.getLastVisitsGraph%'
OR layout LIKE '%.getLastVisitsReturningGraph%'" => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.4.1.php b/core/Updates/0.4.1.php
index 64f7694d27..60223f845e 100644
--- a/core/Updates/0.4.1.php
+++ b/core/Updates/0.4.1.php
@@ -14,18 +14,18 @@
*/
class Piwik_Updates_0_4_1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_conversion') .'`
- CHANGE `idlink_va` `idlink_va` INT(11) DEFAULT NULL' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_conversion') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_conversion') . '`
+ CHANGE `idlink_va` `idlink_va` INT(11) DEFAULT NULL' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_conversion') . '`
CHANGE `idaction` `idaction` INT(11) DEFAULT NULL' => '1054',
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.4.2.php b/core/Updates/0.4.2.php
index 7ca2c5720f..e21135068b 100644
--- a/core/Updates/0.4.2.php
+++ b/core/Updates/0.4.2.php
@@ -14,22 +14,22 @@
*/
class Piwik_Updates_0_4_2 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
- ADD `config_java` TINYINT(1) NOT NULL AFTER `config_flash`' => '1060',
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
+ ADD `config_java` TINYINT(1) NOT NULL AFTER `config_flash`' => '1060',
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
ADD `config_quicktime` TINYINT(1) NOT NULL AFTER `config_director`' => '1060',
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
ADD `config_gears` TINYINT(1) NOT NULL AFTER `config_windowsmedia`,
- ADD `config_silverlight` TINYINT(1) NOT NULL AFTER `config_gears`' => false,
- );
- }
+ ADD `config_silverlight` TINYINT(1) NOT NULL AFTER `config_gears`' => false,
+ );
+ }
- // when restoring (possibly) previousy dropped columns, ignore mysql code error 1060: duplicate column
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ // when restoring (possibly) previousy dropped columns, ignore mysql code error 1060: duplicate column
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.4.4.php b/core/Updates/0.4.4.php
index d21fccefa7..b63731c5e7 100644
--- a/core/Updates/0.4.4.php
+++ b/core/Updates/0.4.4.php
@@ -14,16 +14,14 @@
*/
class Piwik_Updates_0_4_4 extends Piwik_Updates
{
- static function update()
- {
- $obsoleteFile = PIWIK_DOCUMENT_ROOT . '/libs/open-flash-chart/php-ofc-library/ofc_upload_image.php';
- if(file_exists($obsoleteFile))
- {
- $rc = @unlink($obsoleteFile);
- if(!$rc)
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionUndeletableFile', array($obsoleteFile)));
- }
- }
- }
+ static function update()
+ {
+ $obsoleteFile = PIWIK_DOCUMENT_ROOT . '/libs/open-flash-chart/php-ofc-library/ofc_upload_image.php';
+ if (file_exists($obsoleteFile)) {
+ $rc = @unlink($obsoleteFile);
+ if (!$rc) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionUndeletableFile', array($obsoleteFile)));
+ }
+ }
+ }
}
diff --git a/core/Updates/0.4.php b/core/Updates/0.4.php
index ce8ecd6643..dcbfffd578 100644
--- a/core/Updates/0.4.php
+++ b/core/Updates/0.4.php
@@ -14,23 +14,23 @@
*/
class Piwik_Updates_0_4 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- // 0.4 [1140]
- 'UPDATE `'. Piwik_Common::prefixTable('log_visit') .'`
- SET location_ip=location_ip+CAST(POW(2,32) AS UNSIGNED) WHERE location_ip < 0' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
- CHANGE `location_ip` `location_ip` BIGINT UNSIGNED NOT NULL' => false,
- 'UPDATE `'. Piwik_Common::prefixTable('logger_api_call') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ // 0.4 [1140]
+ 'UPDATE `' . Piwik_Common::prefixTable('log_visit') . '`
+ SET location_ip=location_ip+CAST(POW(2,32) AS UNSIGNED) WHERE location_ip < 0' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
+ CHANGE `location_ip` `location_ip` BIGINT UNSIGNED NOT NULL' => false,
+ 'UPDATE `' . Piwik_Common::prefixTable('logger_api_call') . '`
SET caller_ip=caller_ip+CAST(POW(2,32) AS UNSIGNED) WHERE caller_ip < 0' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('logger_api_call') .'`
- CHANGE `caller_ip` `caller_ip` BIGINT UNSIGNED' => false,
- );
- }
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('logger_api_call') . '`
+ CHANGE `caller_ip` `caller_ip` BIGINT UNSIGNED' => false,
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.5.4.php b/core/Updates/0.5.4.php
index 8ca779dc49..733fccfd3d 100644
--- a/core/Updates/0.5.4.php
+++ b/core/Updates/0.5.4.php
@@ -14,56 +14,48 @@
*/
class Piwik_Updates_0_5_4 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_action') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_action') . '`
CHANGE `name` `name` TEXT' => false,
- );
- }
+ );
+ }
- static function update()
- {
- $salt = Piwik_Common::generateUniqId();
+ static function update()
+ {
+ $salt = Piwik_Common::generateUniqId();
$config = Piwik_Config::getInstance();
$superuser = $config->superuser;
- if(!isset($superuser['salt']))
- {
- try {
- if(is_writable( Piwik_Config::getLocalConfigPath() ))
- {
- $superuser['salt'] = $salt;
+ if (!isset($superuser['salt'])) {
+ try {
+ if (is_writable(Piwik_Config::getLocalConfigPath())) {
+ $superuser['salt'] = $salt;
$config->superuser = $superuser;
- $config->forceSave();
- }
- else
- {
- throw new Exception('mandatory update failed');
- }
- } catch(Exception $e) {
- throw new Piwik_Updater_UpdateErrorException("Please edit your config/config.ini.php file and add below <code>[superuser]</code> the following line: <br /><code>salt = $salt</code>");
- }
- }
+ $config->forceSave();
+ } else {
+ throw new Exception('mandatory update failed');
+ }
+ } catch (Exception $e) {
+ throw new Piwik_Updater_UpdateErrorException("Please edit your config/config.ini.php file and add below <code>[superuser]</code> the following line: <br /><code>salt = $salt</code>");
+ }
+ }
- $plugins = $config->Plugins;
- if(!in_array('MultiSites', $plugins))
- {
- try {
- if(is_writable( Piwik_Config::getLocalConfigPath() ))
- {
- $plugins[] = 'MultiSites';
+ $plugins = $config->Plugins;
+ if (!in_array('MultiSites', $plugins)) {
+ try {
+ if (is_writable(Piwik_Config::getLocalConfigPath())) {
+ $plugins[] = 'MultiSites';
$config->Plugins = $plugins;
- $config->forceSave();
- }
- else
- {
- throw new Exception('optional update failed');
- }
- } catch(Exception $e) {
- throw new Exception("You can now enable the new MultiSites plugin in the Plugins screen in the Piwik admin!");
- }
- }
-
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ $config->forceSave();
+ } else {
+ throw new Exception('optional update failed');
+ }
+ } catch (Exception $e) {
+ throw new Exception("You can now enable the new MultiSites plugin in the Plugins screen in the Piwik admin!");
+ }
+ }
+
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.5.5.php b/core/Updates/0.5.5.php
index d0d4225ae5..a2db73c613 100644
--- a/core/Updates/0.5.5.php
+++ b/core/Updates/0.5.5.php
@@ -14,32 +14,29 @@
*/
class Piwik_Updates_0_5_5 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- $sqlarray = array(
- 'DROP INDEX index_idsite_date ON ' . Piwik_Common::prefixTable('log_visit') => '1091',
- 'CREATE INDEX index_idsite_date_config ON ' . Piwik_Common::prefixTable('log_visit') . ' (idsite, visit_server_date, config_md5config(8))' => '1061',
- );
+ static function getSql($schema = 'Myisam')
+ {
+ $sqlarray = array(
+ 'DROP INDEX index_idsite_date ON ' . Piwik_Common::prefixTable('log_visit') => '1091',
+ 'CREATE INDEX index_idsite_date_config ON ' . Piwik_Common::prefixTable('log_visit') . ' (idsite, visit_server_date, config_md5config(8))' => '1061',
+ );
- $tables = Piwik::getTablesInstalled();
- foreach($tables as $tableName)
- {
- if(preg_match('/archive_/', $tableName) == 1)
- {
- $sqlarray[ 'DROP INDEX index_all ON '. $tableName ] = '1091';
- }
- if(preg_match('/archive_numeric_/', $tableName) == 1)
- {
- $sqlarray[ 'CREATE INDEX index_idsite_dates_period ON '. $tableName .' (idsite, date1, date2, period)' ] = '1061';
- }
- }
+ $tables = Piwik::getTablesInstalled();
+ foreach ($tables as $tableName) {
+ if (preg_match('/archive_/', $tableName) == 1) {
+ $sqlarray['DROP INDEX index_all ON ' . $tableName] = '1091';
+ }
+ if (preg_match('/archive_numeric_/', $tableName) == 1) {
+ $sqlarray['CREATE INDEX index_idsite_dates_period ON ' . $tableName . ' (idsite, date1, date2, period)'] = '1061';
+ }
+ }
- return $sqlarray;
- }
+ return $sqlarray;
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
-
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+
+ }
}
diff --git a/core/Updates/0.5.php b/core/Updates/0.5.php
index 1040b22729..7877fba142 100644
--- a/core/Updates/0.5.php
+++ b/core/Updates/0.5.php
@@ -14,24 +14,24 @@
*/
class Piwik_Updates_0_5 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_action') . ' ADD COLUMN `hash` INTEGER(10) UNSIGNED NOT NULL AFTER `name`;' => '1060',
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_visit') . ' CHANGE visit_exit_idaction visit_exit_idaction_url INTEGER(11) NOT NULL;' => '1054',
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_visit') . ' CHANGE visit_entry_idaction visit_entry_idaction_url INTEGER(11) NOT NULL;' => '1054',
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_link_visit_action') . ' CHANGE `idaction_ref` `idaction_url_ref` INTEGER(10) UNSIGNED NOT NULL;' => '1054',
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_link_visit_action') . ' CHANGE `idaction` `idaction_url` INTEGER(10) UNSIGNED NOT NULL;' => '1054',
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_link_visit_action') . ' ADD COLUMN `idaction_name` INTEGER(10) UNSIGNED AFTER `idaction_url_ref`;' => '1060',
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_conversion') . ' CHANGE `idaction` `idaction_url` INTEGER(11) UNSIGNED NOT NULL;' => '1054',
- 'UPDATE ' . Piwik_Common::prefixTable('log_action') . ' SET `hash` = CRC32(name);' => false,
- 'CREATE INDEX index_type_hash ON ' . Piwik_Common::prefixTable('log_action') . ' (type, hash);' => '1061',
- 'DROP INDEX index_type_name ON ' . Piwik_Common::prefixTable('log_action') . ';' => '1091',
- );
- }
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_action') . ' ADD COLUMN `hash` INTEGER(10) UNSIGNED NOT NULL AFTER `name`;' => '1060',
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_visit') . ' CHANGE visit_exit_idaction visit_exit_idaction_url INTEGER(11) NOT NULL;' => '1054',
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_visit') . ' CHANGE visit_entry_idaction visit_entry_idaction_url INTEGER(11) NOT NULL;' => '1054',
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_link_visit_action') . ' CHANGE `idaction_ref` `idaction_url_ref` INTEGER(10) UNSIGNED NOT NULL;' => '1054',
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_link_visit_action') . ' CHANGE `idaction` `idaction_url` INTEGER(10) UNSIGNED NOT NULL;' => '1054',
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_link_visit_action') . ' ADD COLUMN `idaction_name` INTEGER(10) UNSIGNED AFTER `idaction_url_ref`;' => '1060',
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_conversion') . ' CHANGE `idaction` `idaction_url` INTEGER(11) UNSIGNED NOT NULL;' => '1054',
+ 'UPDATE ' . Piwik_Common::prefixTable('log_action') . ' SET `hash` = CRC32(name);' => false,
+ 'CREATE INDEX index_type_hash ON ' . Piwik_Common::prefixTable('log_action') . ' (type, hash);' => '1061',
+ 'DROP INDEX index_type_name ON ' . Piwik_Common::prefixTable('log_action') . ';' => '1091',
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.6-rc1.php b/core/Updates/0.6-rc1.php
index 23e896ccb5..e4dc34d522 100644
--- a/core/Updates/0.6-rc1.php
+++ b/core/Updates/0.6-rc1.php
@@ -14,54 +14,51 @@
*/
class Piwik_Updates_0_6_rc1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- $defaultTimezone = 'UTC';
- $defaultCurrency = 'USD';
- return array(
- 'ALTER TABLE ' . Piwik_Common::prefixTable('user') . ' CHANGE date_registered date_registered TIMESTAMP NULL' => false,
- 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . ' CHANGE ts_created ts_created TIMESTAMP NULL' => false,
- 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . ' ADD `timezone` VARCHAR( 50 ) NOT NULL AFTER `ts_created` ;' => false,
- 'UPDATE ' . Piwik_Common::prefixTable('site') . ' SET `timezone` = "'.$defaultTimezone.'";' => false,
- 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . ' ADD currency CHAR( 3 ) NOT NULL AFTER `timezone` ;' => false,
- 'UPDATE ' . Piwik_Common::prefixTable('site') . ' SET `currency` = "'.$defaultCurrency.'";' => false,
- 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . ' ADD `excluded_ips` TEXT NOT NULL AFTER `currency` ;' => false,
- 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . ' ADD excluded_parameters VARCHAR( 255 ) NOT NULL AFTER `excluded_ips` ;' => false,
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_visit') . ' ADD INDEX `index_idsite_datetime_config` ( `idsite` , `visit_last_action_time` , `config_md5config` ( 8 ) ) ;' => false,
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_visit') . ' ADD INDEX index_idsite_idvisit (idsite, idvisit) ;' => false,
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_conversion') . ' DROP INDEX index_idsite_date' => false,
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_conversion') . ' DROP visit_server_date;' => false,
- 'ALTER TABLE ' . Piwik_Common::prefixTable('log_conversion') . ' ADD INDEX index_idsite_datetime ( `idsite` , `server_time` )' => false,
- );
- }
+ static function getSql($schema = 'Myisam')
+ {
+ $defaultTimezone = 'UTC';
+ $defaultCurrency = 'USD';
+ return array(
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('user') . ' CHANGE date_registered date_registered TIMESTAMP NULL' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . ' CHANGE ts_created ts_created TIMESTAMP NULL' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . ' ADD `timezone` VARCHAR( 50 ) NOT NULL AFTER `ts_created` ;' => false,
+ 'UPDATE ' . Piwik_Common::prefixTable('site') . ' SET `timezone` = "' . $defaultTimezone . '";' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . ' ADD currency CHAR( 3 ) NOT NULL AFTER `timezone` ;' => false,
+ 'UPDATE ' . Piwik_Common::prefixTable('site') . ' SET `currency` = "' . $defaultCurrency . '";' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . ' ADD `excluded_ips` TEXT NOT NULL AFTER `currency` ;' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . ' ADD excluded_parameters VARCHAR( 255 ) NOT NULL AFTER `excluded_ips` ;' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_visit') . ' ADD INDEX `index_idsite_datetime_config` ( `idsite` , `visit_last_action_time` , `config_md5config` ( 8 ) ) ;' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_visit') . ' ADD INDEX index_idsite_idvisit (idsite, idvisit) ;' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_conversion') . ' DROP INDEX index_idsite_date' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_conversion') . ' DROP visit_server_date;' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_conversion') . ' ADD INDEX index_idsite_datetime ( `idsite` , `server_time` )' => false,
+ );
+ }
- static function update()
- {
- // first we disable the plugins and keep an array of warnings messages
- $pluginsToDisableMessage = array(
- 'SearchEnginePosition' => "SearchEnginePosition plugin was disabled, because it is not compatible with the new Piwik 0.6. \n You can download the latest version of the plugin, compatible with Piwik 0.6.\n<a target='_blank' href='?module=Proxy&action=redirect&url=http://dev.piwik.org/trac/ticket/502'>Click here.</a>",
- 'GeoIP' => "GeoIP plugin was disabled, because it is not compatible with the new Piwik 0.6. \nYou can download the latest version of the plugin, compatible with Piwik 0.6.\n<a target='_blank' href='?module=Proxy&action=redirect&url=http://dev.piwik.org/trac/ticket/45'>Click here.</a>"
- );
- $disabledPlugins = array();
- foreach($pluginsToDisableMessage as $pluginToDisable => $warningMessage)
- {
- if(Piwik_PluginsManager::getInstance()->isPluginActivated($pluginToDisable))
- {
- Piwik_PluginsManager::getInstance()->deactivatePlugin($pluginToDisable);
- $disabledPlugins[] = $warningMessage;
- }
- }
-
- // Run the SQL
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
-
- // Outputs warning message, pointing users to the plugin download page
- if(!empty($disabledPlugins))
- {
- throw new Exception("The following plugins were disabled during the upgrade:"
- ."<ul><li>" .
- implode('</li><li>', $disabledPlugins) .
- "</li></ul>");
- }
- }
+ static function update()
+ {
+ // first we disable the plugins and keep an array of warnings messages
+ $pluginsToDisableMessage = array(
+ 'SearchEnginePosition' => "SearchEnginePosition plugin was disabled, because it is not compatible with the new Piwik 0.6. \n You can download the latest version of the plugin, compatible with Piwik 0.6.\n<a target='_blank' href='?module=Proxy&action=redirect&url=http://dev.piwik.org/trac/ticket/502'>Click here.</a>",
+ 'GeoIP' => "GeoIP plugin was disabled, because it is not compatible with the new Piwik 0.6. \nYou can download the latest version of the plugin, compatible with Piwik 0.6.\n<a target='_blank' href='?module=Proxy&action=redirect&url=http://dev.piwik.org/trac/ticket/45'>Click here.</a>"
+ );
+ $disabledPlugins = array();
+ foreach ($pluginsToDisableMessage as $pluginToDisable => $warningMessage) {
+ if (Piwik_PluginsManager::getInstance()->isPluginActivated($pluginToDisable)) {
+ Piwik_PluginsManager::getInstance()->deactivatePlugin($pluginToDisable);
+ $disabledPlugins[] = $warningMessage;
+ }
+ }
+
+ // Run the SQL
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+
+ // Outputs warning message, pointing users to the plugin download page
+ if (!empty($disabledPlugins)) {
+ throw new Exception("The following plugins were disabled during the upgrade:"
+ . "<ul><li>" .
+ implode('</li><li>', $disabledPlugins) .
+ "</li></ul>");
+ }
+ }
}
diff --git a/core/Updates/0.6.2.php b/core/Updates/0.6.2.php
index 0e0e88c27a..0273fe82c2 100644
--- a/core/Updates/0.6.2.php
+++ b/core/Updates/0.6.2.php
@@ -14,33 +14,29 @@
*/
class Piwik_Updates_0_6_2 extends Piwik_Updates
{
- static function update()
- {
- $obsoleteFiles = array(
- PIWIK_INCLUDE_PATH . '/core/Db/Mysqli.php',
- );
- foreach($obsoleteFiles as $obsoleteFile)
- {
- if(file_exists($obsoleteFile))
- {
- @unlink($obsoleteFile);
- }
- }
+ 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))
- {
- Piwik::unlinkRecursive($dir, true);
- }
- }
+ $obsoleteDirectories = array(
+ PIWIK_INCLUDE_PATH . '/core/Db/Pdo',
+ );
+ foreach ($obsoleteDirectories as $dir) {
+ if (file_exists($dir)) {
+ Piwik::unlinkRecursive($dir, true);
+ }
+ }
- // force regeneration of cache files
- Piwik::setUserIsSuperUser();
- $allSiteIds = Piwik_SitesManager_API::getInstance()->getAllSitesId();
- Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($allSiteIds);
- }
+ // force regeneration of cache files
+ Piwik::setUserIsSuperUser();
+ $allSiteIds = Piwik_SitesManager_API::getInstance()->getAllSitesId();
+ Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($allSiteIds);
+ }
}
diff --git a/core/Updates/0.6.3.php b/core/Updates/0.6.3.php
index 283f3f327d..21bb715b44 100644
--- a/core/Updates/0.6.3.php
+++ b/core/Updates/0.6.3.php
@@ -14,38 +14,34 @@
*/
class Piwik_Updates_0_6_3 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
- CHANGE `location_ip` `location_ip` INT UNSIGNED NOT NULL' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('logger_api_call') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
+ CHANGE `location_ip` `location_ip` INT UNSIGNED NOT NULL' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('logger_api_call') . '`
CHANGE `caller_ip` `caller_ip` INT UNSIGNED' => false,
- );
- }
+ );
+ }
- static function update()
- {
- $config = Piwik_Config::getInstance();
- $dbInfos = $config->database;
- if(!isset($dbInfos['schema']))
- {
- try {
- if(is_writable( Piwik_Config::getLocalConfigPath() ))
- {
- $dbInfos['schema'] = 'Myisam';
- $config->database = $dbInfos;
- $config->forceSave();
- }
- else
- {
- throw new Exception('mandatory update failed');
- }
- } catch(Exception $e) {
- throw new Piwik_Updater_UpdateErrorException("Please edit your config/config.ini.php file and add below <code>[database]</code> the following line: <br /><code>schema = Myisam</code>");
- }
- }
+ static function update()
+ {
+ $config = Piwik_Config::getInstance();
+ $dbInfos = $config->database;
+ if (!isset($dbInfos['schema'])) {
+ try {
+ if (is_writable(Piwik_Config::getLocalConfigPath())) {
+ $dbInfos['schema'] = 'Myisam';
+ $config->database = $dbInfos;
+ $config->forceSave();
+ } else {
+ throw new Exception('mandatory update failed');
+ }
+ } catch (Exception $e) {
+ throw new Piwik_Updater_UpdateErrorException("Please edit your config/config.ini.php file and add below <code>[database]</code> the following line: <br /><code>schema = Myisam</code>");
+ }
+ }
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.7.php b/core/Updates/0.7.php
index 8f68edba23..685fe7c962 100644
--- a/core/Updates/0.7.php
+++ b/core/Updates/0.7.php
@@ -14,16 +14,16 @@
*/
class Piwik_Updates_0_7 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('option') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('option') . '`
CHANGE `option_name` `option_name` VARCHAR(255) NOT NULL' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/0.9.1.php b/core/Updates/0.9.1.php
index 2ad423b8d3..24e6c27c60 100644
--- a/core/Updates/0.9.1.php
+++ b/core/Updates/0.9.1.php
@@ -14,42 +14,40 @@
*/
class Piwik_Updates_0_9_1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- if(!Piwik::isTimezoneSupportEnabled())
- {
- return array();
- }
- // @see http://bugs.php.net/46111
- $timezones = timezone_identifiers_list();
- $brokenTZ = array();
+ static function getSql($schema = 'Myisam')
+ {
+ if (!Piwik::isTimezoneSupportEnabled()) {
+ return array();
+ }
+ // @see http://bugs.php.net/46111
+ $timezones = timezone_identifiers_list();
+ $brokenTZ = array();
- foreach ($timezones as $timezone) {
- $testDate = "2008-08-19 13:00:00 " . $timezone;
-
- if (!strtotime($testDate)) {
- $brokenTZ[] = $timezone;
- }
- }
- $timezoneList = '"'. implode('","', $brokenTZ) . '"';
+ foreach ($timezones as $timezone) {
+ $testDate = "2008-08-19 13:00:00 " . $timezone;
- return array(
- 'UPDATE '. Piwik_Common::prefixTable('site') .'
+ if (!strtotime($testDate)) {
+ $brokenTZ[] = $timezone;
+ }
+ }
+ $timezoneList = '"' . implode('","', $brokenTZ) . '"';
+
+ return array(
+ 'UPDATE ' . Piwik_Common::prefixTable('site') . '
SET timezone = "UTC"
- WHERE timezone IN ('. $timezoneList .')' => false,
+ WHERE timezone IN (' . $timezoneList . ')' => false,
- 'UPDATE `'. Piwik_Common::prefixTable('option') .'`
+ 'UPDATE `' . Piwik_Common::prefixTable('option') . '`
SET option_value = "UTC"
WHERE option_name = "SitesManager_DefaultTimezone"
- AND option_value IN ('. $timezoneList .')' => false,
- );
- }
+ AND option_value IN (' . $timezoneList . ')' => false,
+ );
+ }
- static function update()
- {
- if(Piwik::isTimezoneSupportEnabled())
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
- }
+ static function update()
+ {
+ if (Piwik::isTimezoneSupportEnabled()) {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
+ }
}
diff --git a/core/Updates/1.1.php b/core/Updates/1.1.php
index d5ad681e1a..a98e4dc2f8 100644
--- a/core/Updates/1.1.php
+++ b/core/Updates/1.1.php
@@ -14,18 +14,18 @@
*/
class Piwik_Updates_1_1 extends Piwik_Updates
{
- static function update($schema = 'Myisam')
- {
- $config = Piwik_Config::getInstance();
+ static function update($schema = 'Myisam')
+ {
+ $config = Piwik_Config::getInstance();
- $rootLogin = $config->superuser['login'];
- try {
- // throws an exception if invalid
- Piwik::checkValidLoginString($rootLogin);
- } catch(Exception $e) {
- throw new Exception('Superuser login name "' . $rootLogin . '" is no longer a valid format. '
- . $e->getMessage()
- . ' Edit your config/config.ini.php to change it.');
- }
- }
+ $rootLogin = $config->superuser['login'];
+ try {
+ // throws an exception if invalid
+ Piwik::checkValidLoginString($rootLogin);
+ } catch (Exception $e) {
+ throw new Exception('Superuser login name "' . $rootLogin . '" is no longer a valid format. '
+ . $e->getMessage()
+ . ' Edit your config/config.ini.php to change it.');
+ }
+ }
}
diff --git a/core/Updates/1.10-b4.php b/core/Updates/1.10-b4.php
index eed44302d2..b34c3c8b5f 100755
--- a/core/Updates/1.10-b4.php
+++ b/core/Updates/1.10-b4.php
@@ -14,20 +14,17 @@
*/
class Piwik_Updates_1_10_b4 extends Piwik_Updates
{
- static function isMajorUpdate()
- {
- return false;
- }
-
- static function update()
- {
- try
- {
- Piwik_PluginsManager::getInstance()->activatePlugin('MobileMessaging');
- }
- catch(Exception $e)
- {
- // pass
- }
- }
+ static function isMajorUpdate()
+ {
+ return false;
+ }
+
+ static function update()
+ {
+ try {
+ Piwik_PluginsManager::getInstance()->activatePlugin('MobileMessaging');
+ } catch (Exception $e) {
+ // pass
+ }
+ }
} \ No newline at end of file
diff --git a/core/Updates/1.10.1.php b/core/Updates/1.10.1.php
index ee416d0edd..ac00818c2f 100755
--- a/core/Updates/1.10.1.php
+++ b/core/Updates/1.10.1.php
@@ -14,20 +14,17 @@
*/
class Piwik_Updates_1_10_1 extends Piwik_Updates
{
- static function isMajorUpdate()
- {
- return false;
- }
-
- static function update()
- {
- try
- {
- Piwik_PluginsManager::getInstance()->activatePlugin('Overlay');
- }
- catch(Exception $e)
- {
- // pass
- }
- }
+ static function isMajorUpdate()
+ {
+ return false;
+ }
+
+ static function update()
+ {
+ try {
+ Piwik_PluginsManager::getInstance()->activatePlugin('Overlay');
+ } catch (Exception $e) {
+ // pass
+ }
+ }
} \ No newline at end of file
diff --git a/core/Updates/1.10.2-b1.php b/core/Updates/1.10.2-b1.php
index c4482e383e..1796863297 100755
--- a/core/Updates/1.10.2-b1.php
+++ b/core/Updates/1.10.2-b1.php
@@ -14,17 +14,17 @@
*/
class Piwik_Updates_1_10_2_b1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- // ignore existing column name error (1060)
- 'ALTER TABLE '.Piwik_Common::prefixTable('report')
- . " ADD COLUMN hour tinyint NOT NULL default 0 AFTER period" => 1060,
- );
- }
-
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ // ignore existing column name error (1060)
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('report')
+ . " ADD COLUMN hour tinyint NOT NULL default 0 AFTER period" => 1060,
+ );
+ }
+
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.10.2-b2.php b/core/Updates/1.10.2-b2.php
index c18bfd677d..26cfd3e004 100644
--- a/core/Updates/1.10.2-b2.php
+++ b/core/Updates/1.10.2-b2.php
@@ -14,17 +14,17 @@
*/
class Piwik_Updates_1_10_2_b2 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- // ignore existing column name error (1060)
- 'ALTER TABLE '.Piwik_Common::prefixTable('site')
- . " ADD COLUMN `keep_url_fragment` TINYINT NOT NULL DEFAULT 0 AFTER `group`" => 1060,
- );
- }
-
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ // ignore existing column name error (1060)
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('site')
+ . " ADD COLUMN `keep_url_fragment` TINYINT NOT NULL DEFAULT 0 AFTER `group`" => 1060,
+ );
+ }
+
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.11-b1.php b/core/Updates/1.11-b1.php
index d38b123605..80bce9ce22 100644
--- a/core/Updates/1.11-b1.php
+++ b/core/Updates/1.11-b1.php
@@ -14,20 +14,17 @@
*/
class Piwik_Updates_1_11_b1 extends Piwik_Updates
{
- static function isMajorUpdate()
- {
- return false;
- }
-
- static function update()
- {
- try
- {
- Piwik_PluginsManager::getInstance()->activatePlugin('UserCountryMap');
- }
- catch(Exception $e)
- {
- // pass
- }
- }
+ static function isMajorUpdate()
+ {
+ return false;
+ }
+
+ static function update()
+ {
+ try {
+ Piwik_PluginsManager::getInstance()->activatePlugin('UserCountryMap');
+ } catch (Exception $e) {
+ // pass
+ }
+ }
} \ No newline at end of file
diff --git a/core/Updates/1.12-b1.php b/core/Updates/1.12-b1.php
index 5f15ac0439..21412a24c0 100644
--- a/core/Updates/1.12-b1.php
+++ b/core/Updates/1.12-b1.php
@@ -14,22 +14,22 @@
*/
class Piwik_Updates_1_12_b1 extends Piwik_Updates
{
- static function isMajorUpdate()
- {
- return true;
- }
-
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_link_visit_action') .'`
+ static function isMajorUpdate()
+ {
+ return true;
+ }
+
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_link_visit_action') . '`
ADD `custom_float_1` FLOAT NULL DEFAULT NULL' => false
- );
- }
-
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
-
+ );
+ }
+
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
+
} \ No newline at end of file
diff --git a/core/Updates/1.2-rc1.php b/core/Updates/1.2-rc1.php
index a6c8d15eeb..ac6bbcb0e3 100644
--- a/core/Updates/1.2-rc1.php
+++ b/core/Updates/1.2-rc1.php
@@ -14,11 +14,11 @@
*/
class Piwik_Updates_1_2_rc1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- // Various performance improvements schema updates
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ // Various performance improvements schema updates
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
DROP `visit_server_date`,
DROP INDEX `index_idsite_date_config`,
DROP INDEX `index_idsite_datetime_config`,
@@ -42,16 +42,16 @@ class Piwik_Updates_1_2_rc1 extends Piwik_Updates
ADD custom_var_v4 VARCHAR(100) DEFAULT NULL,
ADD custom_var_k5 VARCHAR(100) DEFAULT NULL,
ADD custom_var_v5 VARCHAR(100) DEFAULT NULL
- ' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_link_visit_action') .'`
+ ' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_link_visit_action') . '`
ADD `idsite` INT( 10 ) UNSIGNED NOT NULL AFTER `idlink_va` ,
ADD `server_time` DATETIME AFTER `idsite`,
ADD `idvisitor` BINARY(8) NOT NULL AFTER `idsite`,
ADD `idaction_name_ref` INT UNSIGNED NOT NULL AFTER `idaction_name`,
ADD INDEX `index_idsite_servertime` ( `idsite` , `server_time` )
- ' => false,
+ ' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_conversion') .'`
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_conversion') . '`
DROP `referer_idvisit`,
ADD `idvisitor` BINARY(8) NOT NULL AFTER `idsite`,
ADD visitor_count_visits SMALLINT(5) UNSIGNED NOT NULL,
@@ -66,81 +66,78 @@ class Piwik_Updates_1_2_rc1 extends Piwik_Updates
ADD custom_var_v4 VARCHAR(100) DEFAULT NULL,
ADD custom_var_k5 VARCHAR(100) DEFAULT NULL,
ADD custom_var_v5 VARCHAR(100) DEFAULT NULL
- ' => false,
-
- // Migrate 128bits IDs inefficiently stored as 8bytes (256 bits) into 64bits
- 'UPDATE '.Piwik_Common::prefixTable('log_visit') .'
+ ' => false,
+
+ // Migrate 128bits IDs inefficiently stored as 8bytes (256 bits) into 64bits
+ 'UPDATE ' . Piwik_Common::prefixTable('log_visit') . '
SET idvisitor = binary(unhex(substring(visitor_idcookie,1,16))),
config_id = binary(unhex(substring(config_md5config,1,16)))
- ' => false,
- 'UPDATE '.Piwik_Common::prefixTable('log_conversion') .'
+ ' => false,
+ 'UPDATE ' . Piwik_Common::prefixTable('log_conversion') . '
SET idvisitor = binary(unhex(substring(visitor_idcookie,1,16)))
- ' => false,
-
- // Drop migrated fields
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ ' => false,
+
+ // Drop migrated fields
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
DROP visitor_idcookie,
DROP config_md5config
- ' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_conversion') .'`
+ ' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_conversion') . '`
DROP visitor_idcookie
- ' => false,
-
- // Recreate INDEX on new field
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ ' => false,
+
+ // Recreate INDEX on new field
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
ADD INDEX `index_idsite_datetime_config` (idsite, visit_last_action_time, config_id)
- ' => false,
-
- // Backfill action logs as best as we can
- 'UPDATE '.Piwik_Common::prefixTable('log_link_visit_action') .' as action,
- '.Piwik_Common::prefixTable('log_visit') .' as visit
+ ' => false,
+
+ // Backfill action logs as best as we can
+ 'UPDATE ' . Piwik_Common::prefixTable('log_link_visit_action') . ' as action,
+ ' . Piwik_Common::prefixTable('log_visit') . ' as visit
SET action.idsite = visit.idsite,
action.server_time = visit.visit_last_action_time,
action.idvisitor = visit.idvisitor
WHERE action.idvisit=visit.idvisit
- ' => false,
-
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_link_visit_action') .'`
+ ' => false,
+
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_link_visit_action') . '`
CHANGE `server_time` `server_time` DATETIME NOT NULL
- ' => false,
+ ' => false,
+
+ // New index used max once per request, in case this table grows significantly in the future
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('option') . '` ADD INDEX ( `autoload` ) ' => false,
+
+ // new field for websites
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('site') . '` ADD `group` VARCHAR( 250 ) NOT NULL' => false,
+ );
+ }
+
+ static function update()
+ {
+ // first we disable the plugins and keep an array of warnings messages
+ $pluginsToDisableMessage = array(
+ 'GeoIP' => "GeoIP plugin was disabled, because it is not compatible with the new Piwik 1.2. \nYou can download the latest version of the plugin, compatible with Piwik 1.2.\n<a target='_blank' href='?module=Proxy&action=redirect&url=http://dev.piwik.org/trac/ticket/45'>Click here.</a>",
+ 'EntryPage' => "EntryPage plugin is not compatible with this version of Piwik, it was disabled.",
+ );
+ $disabledPlugins = array();
+ foreach ($pluginsToDisableMessage as $pluginToDisable => $warningMessage) {
+ if (Piwik_PluginsManager::getInstance()->isPluginActivated($pluginToDisable)) {
+ Piwik_PluginsManager::getInstance()->deactivatePlugin($pluginToDisable);
+ $disabledPlugins[] = $warningMessage;
+ }
+ }
+
+ // Run the SQL
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- // New index used max once per request, in case this table grows significantly in the future
- 'ALTER TABLE `'. Piwik_Common::prefixTable('option') .'` ADD INDEX ( `autoload` ) ' => false,
-
- // new field for websites
- 'ALTER TABLE `'. Piwik_Common::prefixTable('site') .'` ADD `group` VARCHAR( 250 ) NOT NULL' => false,
- );
- }
+ // Outputs warning message, pointing users to the plugin download page
+ if (!empty($disabledPlugins)) {
+ throw new Exception("The following plugins were disabled during the upgrade:"
+ . "<ul><li>" .
+ implode('</li><li>', $disabledPlugins) .
+ "</li></ul>");
+ }
- static function update()
- {
- // first we disable the plugins and keep an array of warnings messages
- $pluginsToDisableMessage = array(
- 'GeoIP' => "GeoIP plugin was disabled, because it is not compatible with the new Piwik 1.2. \nYou can download the latest version of the plugin, compatible with Piwik 1.2.\n<a target='_blank' href='?module=Proxy&action=redirect&url=http://dev.piwik.org/trac/ticket/45'>Click here.</a>",
- 'EntryPage' => "EntryPage plugin is not compatible with this version of Piwik, it was disabled.",
- );
- $disabledPlugins = array();
- foreach($pluginsToDisableMessage as $pluginToDisable => $warningMessage)
- {
- if(Piwik_PluginsManager::getInstance()->isPluginActivated($pluginToDisable))
- {
- Piwik_PluginsManager::getInstance()->deactivatePlugin($pluginToDisable);
- $disabledPlugins[] = $warningMessage;
- }
- }
-
- // Run the SQL
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
-
- // Outputs warning message, pointing users to the plugin download page
- if(!empty($disabledPlugins))
- {
- throw new Exception("The following plugins were disabled during the upgrade:"
- ."<ul><li>" .
- implode('</li><li>', $disabledPlugins) .
- "</li></ul>");
- }
-
- }
+ }
}
diff --git a/core/Updates/1.2-rc2.php b/core/Updates/1.2-rc2.php
index 352a9ba174..b3afa13cbe 100644
--- a/core/Updates/1.2-rc2.php
+++ b/core/Updates/1.2-rc2.php
@@ -14,12 +14,12 @@
*/
class Piwik_Updates_1_2_rc2 extends Piwik_Updates
{
- static function update()
- {
- try {
- Piwik_PluginsManager::getInstance()->activatePlugin('CustomVariables');
- } catch(Exception $e) {
- }
- }
+ static function update()
+ {
+ try {
+ Piwik_PluginsManager::getInstance()->activatePlugin('CustomVariables');
+ } catch (Exception $e) {
+ }
+ }
}
diff --git a/core/Updates/1.2.3.php b/core/Updates/1.2.3.php
index b2b73ed9c1..f4a1104e9f 100644
--- a/core/Updates/1.2.3.php
+++ b/core/Updates/1.2.3.php
@@ -14,24 +14,24 @@
*/
class Piwik_Updates_1_2_3 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- // LOAD DATA INFILE uses the database's charset
- 'ALTER DATABASE `'. Piwik_Config::getInstance()->database['dbname'] .'` DEFAULT CHARACTER SET utf8' => false,
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ // LOAD DATA INFILE uses the database's charset
+ 'ALTER DATABASE `' . Piwik_Config::getInstance()->database['dbname'] . '` DEFAULT CHARACTER SET utf8' => false,
- // Various performance improvements schema updates
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ // Various performance improvements schema updates
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
DROP INDEX index_idsite_datetime_config,
DROP INDEX index_idsite_idvisit,
ADD INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time),
ADD INDEX index_idsite_datetime (idsite, visit_last_action_time)' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.2.5-rc1.php b/core/Updates/1.2.5-rc1.php
index f666c5bff3..90b0399589 100644
--- a/core/Updates/1.2.5-rc1.php
+++ b/core/Updates/1.2.5-rc1.php
@@ -14,21 +14,21 @@
*/
class Piwik_Updates_1_2_5_rc1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('goal') .'`
- ADD `allow_multiple` tinyint(4) NOT NULL AFTER case_sensitive' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_conversion') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('goal') . '`
+ ADD `allow_multiple` tinyint(4) NOT NULL AFTER case_sensitive' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_conversion') . '`
ADD buster int unsigned NOT NULL AFTER revenue,
DROP PRIMARY KEY,
ADD PRIMARY KEY (idvisit, idgoal, buster)' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.2.5-rc7.php b/core/Updates/1.2.5-rc7.php
index c79e7f44ee..aafc70c7a9 100644
--- a/core/Updates/1.2.5-rc7.php
+++ b/core/Updates/1.2.5-rc7.php
@@ -14,18 +14,18 @@
*/
class Piwik_Updates_1_2_5_rc7 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
ADD INDEX index_idsite_idvisitor (idsite, idvisitor)' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.4-rc1.php b/core/Updates/1.4-rc1.php
index b293d7e425..ce99c40497 100644
--- a/core/Updates/1.4-rc1.php
+++ b/core/Updates/1.4-rc1.php
@@ -14,21 +14,21 @@
*/
class Piwik_Updates_1_4_rc1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('pdf') .'`
- ADD COLUMN `format` VARCHAR(10)' => false,
- 'UPDATE `'. Piwik_Common::prefixTable('pdf') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('pdf') . '`
+ ADD COLUMN `format` VARCHAR(10)' => false,
+ 'UPDATE `' . Piwik_Common::prefixTable('pdf') . '`
SET format = "pdf"' => false,
- );
- }
+ );
+ }
- static function update()
- {
- try {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
- catch(Exception $e){}
- }
+ static function update()
+ {
+ try {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ } catch (Exception $e) {
+ }
+ }
}
diff --git a/core/Updates/1.4-rc2.php b/core/Updates/1.4-rc2.php
index f91541b42a..b5273b0505 100644
--- a/core/Updates/1.4-rc2.php
+++ b/core/Updates/1.4-rc2.php
@@ -14,28 +14,28 @@
*/
class Piwik_Updates_1_4_rc2 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- "SET sql_mode=''" => false,
- // this converts the 32-bit UNSIGNED INT column to a 16 byte VARBINARY;
- // _but_ MySQL does string conversion! (e.g., integer 1 is converted to 49 -- the ASCII code for "1")
- 'ALTER TABLE '. Piwik_Common::prefixTable('log_visit') .'
- MODIFY location_ip VARBINARY(16) NOT NULL' => false,
- 'ALTER TABLE '. Piwik_Common::prefixTable('logger_api_call') .'
- MODIFY caller_ip VARBINARY(16) NOT NULL' => false,
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ "SET sql_mode=''" => false,
+ // this converts the 32-bit UNSIGNED INT column to a 16 byte VARBINARY;
+ // _but_ MySQL does string conversion! (e.g., integer 1 is converted to 49 -- the ASCII code for "1")
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('log_visit') . '
+ MODIFY location_ip VARBINARY(16) NOT NULL' => false,
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('logger_api_call') . '
+ MODIFY caller_ip VARBINARY(16) NOT NULL' => false,
- // fortunately, 2^32 is 10 digits long and fits in the VARBINARY(16) without truncation;
- // to fix this, we cast to an integer, convert to hex, pad out leading zeros, and unhex it
- 'UPDATE '. Piwik_Common::prefixTable('log_visit') ."
- SET location_ip = UNHEX(LPAD(HEX(CONVERT(location_ip, UNSIGNED)), 8, '0'))" => false,
- 'UPDATE '. Piwik_Common::prefixTable('logger_api_call') ."
+ // fortunately, 2^32 is 10 digits long and fits in the VARBINARY(16) without truncation;
+ // to fix this, we cast to an integer, convert to hex, pad out leading zeros, and unhex it
+ 'UPDATE ' . Piwik_Common::prefixTable('log_visit') . "
+ SET location_ip = UNHEX(LPAD(HEX(CONVERT(location_ip, UNSIGNED)), 8, '0'))" => false,
+ 'UPDATE ' . Piwik_Common::prefixTable('logger_api_call') . "
SET caller_ip = UNHEX(LPAD(HEX(CONVERT(caller_ip, UNSIGNED)), 8, '0'))" => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.5-b1.php b/core/Updates/1.5-b1.php
index 3290d4fa38..7156719e3d 100644
--- a/core/Updates/1.5-b1.php
+++ b/core/Updates/1.5-b1.php
@@ -14,10 +14,10 @@
*/
class Piwik_Updates_1_5_b1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'CREATE TABLE `'. Piwik_Common::prefixTable('log_conversion_item') .'` (
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'CREATE TABLE `' . Piwik_Common::prefixTable('log_conversion_item') . '` (
idsite int(10) UNSIGNED NOT NULL,
idvisitor BINARY(8) NOT NULL,
server_time DATETIME NOT NULL,
@@ -33,13 +33,13 @@ class Piwik_Updates_1_5_b1 extends Piwik_Updates
PRIMARY KEY(idvisit, idorder, idaction_sku),
INDEX index_idsite_servertime ( idsite, server_time )
- ) DEFAULT CHARSET=utf8 '=> false,
+ ) DEFAULT CHARSET=utf8 ' => false,
- 'ALTER IGNORE TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ 'ALTER IGNORE TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
ADD visitor_days_since_order SMALLINT(5) UNSIGNED NOT NULL AFTER visitor_days_since_last,
ADD visit_goal_buyer TINYINT(1) NOT NULL AFTER visit_goal_converted' => false,
- 'ALTER IGNORE TABLE `'. Piwik_Common::prefixTable('log_conversion') .'`
+ 'ALTER IGNORE TABLE `' . Piwik_Common::prefixTable('log_conversion') . '`
ADD visitor_days_since_order SMALLINT(5) UNSIGNED NOT NULL AFTER visitor_days_since_first,
ADD idorder varchar(100) default NULL AFTER buster,
ADD items SMALLINT UNSIGNED DEFAULT NULL,
@@ -48,12 +48,12 @@ class Piwik_Updates_1_5_b1 extends Piwik_Updates
ADD revenue_shipping float default NULL,
ADD revenue_discount float default NULL,
ADD UNIQUE KEY unique_idsite_idorder (idsite, idorder),
- MODIFY idgoal int(10) NOT NULL' => false,
- );
- }
+ MODIFY idgoal int(10) NOT NULL' => false,
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.5-b2.php b/core/Updates/1.5-b2.php
index 10524c7dd4..fee8f86e9a 100644
--- a/core/Updates/1.5-b2.php
+++ b/core/Updates/1.5-b2.php
@@ -14,10 +14,10 @@
*/
class Piwik_Updates_1_5_b2 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_link_visit_action') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_link_visit_action') . '`
ADD custom_var_k1 VARCHAR(100) DEFAULT NULL AFTER time_spent_ref_action,
ADD custom_var_v1 VARCHAR(100) DEFAULT NULL,
ADD custom_var_k2 VARCHAR(100) DEFAULT NULL,
@@ -28,11 +28,11 @@ class Piwik_Updates_1_5_b2 extends Piwik_Updates
ADD custom_var_v4 VARCHAR(100) DEFAULT NULL,
ADD custom_var_k5 VARCHAR(100) DEFAULT NULL,
ADD custom_var_v5 VARCHAR(100) DEFAULT NULL' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.5-b3.php b/core/Updates/1.5-b3.php
index 470b4806c9..e7d76bd2e5 100644
--- a/core/Updates/1.5-b3.php
+++ b/core/Updates/1.5-b3.php
@@ -14,10 +14,10 @@
*/
class Piwik_Updates_1_5_b3 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
CHANGE custom_var_k1 custom_var_k1 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_v1 custom_var_v1 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_k2 custom_var_k2 VARCHAR(100) DEFAULT NULL,
@@ -27,8 +27,8 @@ class Piwik_Updates_1_5_b3 extends Piwik_Updates
CHANGE custom_var_k4 custom_var_k4 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_v4 custom_var_v4 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_k5 custom_var_k5 VARCHAR(100) DEFAULT NULL,
- CHANGE custom_var_v5 custom_var_v5 VARCHAR(100) DEFAULT NULL' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_conversion') .'`
+ CHANGE custom_var_v5 custom_var_v5 VARCHAR(100) DEFAULT NULL' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_conversion') . '`
CHANGE custom_var_k1 custom_var_k1 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_v1 custom_var_v1 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_k2 custom_var_k2 VARCHAR(100) DEFAULT NULL,
@@ -38,8 +38,8 @@ class Piwik_Updates_1_5_b3 extends Piwik_Updates
CHANGE custom_var_k4 custom_var_k4 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_v4 custom_var_v4 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_k5 custom_var_k5 VARCHAR(100) DEFAULT NULL,
- CHANGE custom_var_v5 custom_var_v5 VARCHAR(100) DEFAULT NULL' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_link_visit_action') .'`
+ CHANGE custom_var_v5 custom_var_v5 VARCHAR(100) DEFAULT NULL' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_link_visit_action') . '`
CHANGE custom_var_k1 custom_var_k1 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_v1 custom_var_v1 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_k2 custom_var_k2 VARCHAR(100) DEFAULT NULL,
@@ -50,11 +50,11 @@ class Piwik_Updates_1_5_b3 extends Piwik_Updates
CHANGE custom_var_v4 custom_var_v4 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_k5 custom_var_k5 VARCHAR(100) DEFAULT NULL,
CHANGE custom_var_v5 custom_var_v5 VARCHAR(100) DEFAULT NULL' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.5-b4.php b/core/Updates/1.5-b4.php
index ff889ea278..15479fb029 100644
--- a/core/Updates/1.5-b4.php
+++ b/core/Updates/1.5-b4.php
@@ -14,16 +14,16 @@
*/
class Piwik_Updates_1_5_b4 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('site') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('site') . '`
ADD ecommerce TINYINT DEFAULT 0' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.5-b5.php b/core/Updates/1.5-b5.php
index 65e95ca70f..c00eac3dce 100644
--- a/core/Updates/1.5-b5.php
+++ b/core/Updates/1.5-b5.php
@@ -14,21 +14,21 @@
*/
class Piwik_Updates_1_5_b5 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'CREATE TABLE `'. Piwik_Common::prefixTable('session') .'` (
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'CREATE TABLE `' . Piwik_Common::prefixTable('session') . '` (
id CHAR(32) NOT NULL,
modified INTEGER,
lifetime INTEGER,
data TEXT,
PRIMARY KEY ( id )
) DEFAULT CHARSET=utf8' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.5-rc6.php b/core/Updates/1.5-rc6.php
index f7bd1136db..af99ebe80f 100644
--- a/core/Updates/1.5-rc6.php
+++ b/core/Updates/1.5-rc6.php
@@ -14,12 +14,12 @@
*/
class Piwik_Updates_1_5_rc6 extends Piwik_Updates
{
- static function update()
- {
- try {
- Piwik_PluginsManager::getInstance()->activatePlugin('PrivacyManager');
- } catch(Exception $e) {
- }
- }
+ static function update()
+ {
+ try {
+ Piwik_PluginsManager::getInstance()->activatePlugin('PrivacyManager');
+ } catch (Exception $e) {
+ }
+ }
}
diff --git a/core/Updates/1.6-b1.php b/core/Updates/1.6-b1.php
index edaadabfa3..57e1deb889 100644
--- a/core/Updates/1.6-b1.php
+++ b/core/Updates/1.6-b1.php
@@ -14,15 +14,15 @@
*/
class Piwik_Updates_1_6_b1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_conversion_item') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_conversion_item') . '`
ADD idaction_category2 INTEGER(10) UNSIGNED NOT NULL AFTER idaction_category,
ADD idaction_category3 INTEGER(10) UNSIGNED NOT NULL,
ADD idaction_category4 INTEGER(10) UNSIGNED NOT NULL,
- ADD idaction_category5 INTEGER(10) UNSIGNED NOT NULL' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ ADD idaction_category5 INTEGER(10) UNSIGNED NOT NULL' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
CHANGE custom_var_k1 custom_var_k1 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_v1 custom_var_v1 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_k2 custom_var_k2 VARCHAR(200) DEFAULT NULL,
@@ -32,8 +32,8 @@ class Piwik_Updates_1_6_b1 extends Piwik_Updates
CHANGE custom_var_k4 custom_var_k4 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_v4 custom_var_v4 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_k5 custom_var_k5 VARCHAR(200) DEFAULT NULL,
- CHANGE custom_var_v5 custom_var_v5 VARCHAR(200) DEFAULT NULL' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_conversion') .'`
+ CHANGE custom_var_v5 custom_var_v5 VARCHAR(200) DEFAULT NULL' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_conversion') . '`
CHANGE custom_var_k1 custom_var_k1 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_v1 custom_var_v1 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_k2 custom_var_k2 VARCHAR(200) DEFAULT NULL,
@@ -43,8 +43,8 @@ class Piwik_Updates_1_6_b1 extends Piwik_Updates
CHANGE custom_var_k4 custom_var_k4 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_v4 custom_var_v4 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_k5 custom_var_k5 VARCHAR(200) DEFAULT NULL,
- CHANGE custom_var_v5 custom_var_v5 VARCHAR(200) DEFAULT NULL' => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_link_visit_action') .'`
+ CHANGE custom_var_v5 custom_var_v5 VARCHAR(200) DEFAULT NULL' => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_link_visit_action') . '`
CHANGE custom_var_k1 custom_var_k1 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_v1 custom_var_v1 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_k2 custom_var_k2 VARCHAR(200) DEFAULT NULL,
@@ -55,11 +55,11 @@ class Piwik_Updates_1_6_b1 extends Piwik_Updates
CHANGE custom_var_v4 custom_var_v4 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_k5 custom_var_k5 VARCHAR(200) DEFAULT NULL,
CHANGE custom_var_v5 custom_var_v5 VARCHAR(200) DEFAULT NULL' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.6-rc1.php b/core/Updates/1.6-rc1.php
index 4671d88d5f..e66edc8992 100644
--- a/core/Updates/1.6-rc1.php
+++ b/core/Updates/1.6-rc1.php
@@ -14,12 +14,12 @@
*/
class Piwik_Updates_1_6_rc1 extends Piwik_Updates
{
- static function update()
- {
- try {
- Piwik_PluginsManager::getInstance()->activatePlugin('ImageGraph');
- } catch(Exception $e) {
- }
- }
+ static function update()
+ {
+ try {
+ Piwik_PluginsManager::getInstance()->activatePlugin('ImageGraph');
+ } catch (Exception $e) {
+ }
+ }
}
diff --git a/core/Updates/1.7-b1.php b/core/Updates/1.7-b1.php
index cb5bb46432..f5f084f892 100644
--- a/core/Updates/1.7-b1.php
+++ b/core/Updates/1.7-b1.php
@@ -14,21 +14,21 @@
*/
class Piwik_Updates_1_7_b1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('pdf') .'`
- ADD COLUMN `aggregate_reports_format` TINYINT(1) NOT NULL AFTER `reports`' => false,
- 'UPDATE `'. Piwik_Common::prefixTable('pdf') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('pdf') . '`
+ ADD COLUMN `aggregate_reports_format` TINYINT(1) NOT NULL AFTER `reports`' => false,
+ 'UPDATE `' . Piwik_Common::prefixTable('pdf') . '`
SET `aggregate_reports_format` = 1' => false,
- );
- }
+ );
+ }
- static function update()
- {
- try {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
- catch(Exception $e){}
- }
+ static function update()
+ {
+ try {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ } catch (Exception $e) {
+ }
+ }
}
diff --git a/core/Updates/1.7.2-rc5.php b/core/Updates/1.7.2-rc5.php
index f6f400a3cf..6d9e88a8b2 100644
--- a/core/Updates/1.7.2-rc5.php
+++ b/core/Updates/1.7.2-rc5.php
@@ -14,19 +14,19 @@
*/
class Piwik_Updates_1_7_2_rc5 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('pdf') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('pdf') . '`
CHANGE `aggregate_reports_format` `display_format` TINYINT(1) NOT NULL' => false
- );
- }
+ );
+ }
- static function update()
- {
- try {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
- catch(Exception $e){}
- }
+ static function update()
+ {
+ try {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ } catch (Exception $e) {
+ }
+ }
}
diff --git a/core/Updates/1.7.2-rc7.php b/core/Updates/1.7.2-rc7.php
index 3e93192936..c901f37a7d 100755
--- a/core/Updates/1.7.2-rc7.php
+++ b/core/Updates/1.7.2-rc7.php
@@ -14,28 +14,28 @@
*/
class Piwik_Updates_1_7_2_rc7 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('user_dashboard') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('user_dashboard') . '`
ADD `name` VARCHAR( 100 ) NULL DEFAULT NULL AFTER `iddashboard`' => false,
- );
- }
+ );
+ }
- static function update()
- {
- try {
- $dashboards = Piwik_FetchAll('SELECT * FROM `'. Piwik_Common::prefixTable('user_dashboard') .'`');
- foreach($dashboards AS $dashboard) {
- $idDashboard = $dashboard['iddashboard'];
- $login = $dashboard['login'];
- $layout = $dashboard['layout'];
- $layout = html_entity_decode($layout);
- $layout = str_replace("\\\"", "\"", $layout);
- Piwik_Query('UPDATE `'. Piwik_Common::prefixTable('user_dashboard') .'` SET layout = ? WHERE iddashboard = ? AND login = ?', array($layout, $idDashboard, $login));
- }
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
- catch(Exception $e){}
- }
+ static function update()
+ {
+ try {
+ $dashboards = Piwik_FetchAll('SELECT * FROM `' . Piwik_Common::prefixTable('user_dashboard') . '`');
+ foreach ($dashboards AS $dashboard) {
+ $idDashboard = $dashboard['iddashboard'];
+ $login = $dashboard['login'];
+ $layout = $dashboard['layout'];
+ $layout = html_entity_decode($layout);
+ $layout = str_replace("\\\"", "\"", $layout);
+ Piwik_Query('UPDATE `' . Piwik_Common::prefixTable('user_dashboard') . '` SET layout = ? WHERE iddashboard = ? AND login = ?', array($layout, $idDashboard, $login));
+ }
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ } catch (Exception $e) {
+ }
+ }
}
diff --git a/core/Updates/1.8.3-b1.php b/core/Updates/1.8.3-b1.php
index 2f8c1c4860..2dfe336559 100644
--- a/core/Updates/1.8.3-b1.php
+++ b/core/Updates/1.8.3-b1.php
@@ -15,13 +15,13 @@
class Piwik_Updates_1_8_3_b1 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('site') .'`
- CHANGE `excluded_parameters` `excluded_parameters` TEXT NOT NULL' => false,
-
- 'CREATE TABLE `'.Piwik_Common::prefixTable('report').'` (
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('site') . '`
+ CHANGE `excluded_parameters` `excluded_parameters` TEXT NOT NULL' => false,
+
+ 'CREATE TABLE `' . Piwik_Common::prefixTable('report') . '` (
`idreport` INT(11) NOT NULL AUTO_INCREMENT,
`idsite` INTEGER(11) NOT NULL,
`login` VARCHAR(100) NOT NULL,
@@ -36,77 +36,75 @@ class Piwik_Updates_1_8_3_b1 extends Piwik_Updates
`deleted` tinyint(4) NOT NULL default 0,
PRIMARY KEY (`idreport`)
) DEFAULT CHARSET=utf8' => false,
- );
- }
-
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- if(!Piwik_PluginsManager::getInstance()->isPluginLoaded('PDFReports'))
- {
- return;
- }
-
- try {
+ );
+ }
+
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ if (!Piwik_PluginsManager::getInstance()->isPluginLoaded('PDFReports')) {
+ return;
+ }
- // Piwik_Common::prefixTable('pdf') has been heavily refactored to be more generic
- // The following actions are taken in this update script :
- // - create the new generic report table Piwik_Common::prefixTable('report')
- // - migrate previous reports, if any, from Piwik_Common::prefixTable('pdf') to Piwik_Common::prefixTable('report')
- // - delete Piwik_Common::prefixTable('pdf')
+ try {
- $reports = Piwik_FetchAll('SELECT * FROM `'. Piwik_Common::prefixTable('pdf') .'`');
- foreach($reports AS $report) {
+ // Piwik_Common::prefixTable('pdf') has been heavily refactored to be more generic
+ // The following actions are taken in this update script :
+ // - create the new generic report table Piwik_Common::prefixTable('report')
+ // - migrate previous reports, if any, from Piwik_Common::prefixTable('pdf') to Piwik_Common::prefixTable('report')
+ // - delete Piwik_Common::prefixTable('pdf')
- $idreport = $report['idreport'];
- $idsite = $report['idsite'];
- $login = $report['login'];
- $description = $report['description'];
- $period = $report['period'];
- $format = $report['format'];
- $display_format = $report['display_format'];
- $email_me = $report['email_me'];
- $additional_emails = $report['additional_emails'];
- $reports = $report['reports'];
- $ts_created = $report['ts_created'];
- $ts_last_sent = $report['ts_last_sent'];
- $deleted = $report['deleted'];
+ $reports = Piwik_FetchAll('SELECT * FROM `' . Piwik_Common::prefixTable('pdf') . '`');
+ foreach ($reports AS $report) {
- $parameters = array();
+ $idreport = $report['idreport'];
+ $idsite = $report['idsite'];
+ $login = $report['login'];
+ $description = $report['description'];
+ $period = $report['period'];
+ $format = $report['format'];
+ $display_format = $report['display_format'];
+ $email_me = $report['email_me'];
+ $additional_emails = $report['additional_emails'];
+ $reports = $report['reports'];
+ $ts_created = $report['ts_created'];
+ $ts_last_sent = $report['ts_last_sent'];
+ $deleted = $report['deleted'];
- if(!is_null($additional_emails))
- {
- $parameters[Piwik_PDFReports::ADDITIONAL_EMAILS_PARAMETER] = preg_split('/,/', $additional_emails);
- }
+ $parameters = array();
- $parameters[Piwik_PDFReports::EMAIL_ME_PARAMETER] = is_null($email_me) ? Piwik_PDFReports::EMAIL_ME_PARAMETER_DEFAULT_VALUE : (bool)$email_me;
- $parameters[Piwik_PDFReports::DISPLAY_FORMAT_PARAMETER] = $display_format;
+ if (!is_null($additional_emails)) {
+ $parameters[Piwik_PDFReports::ADDITIONAL_EMAILS_PARAMETER] = preg_split('/,/', $additional_emails);
+ }
- Piwik_Query(
- 'INSERT INTO `' . Piwik_Common::prefixTable('report') . '` SET
+ $parameters[Piwik_PDFReports::EMAIL_ME_PARAMETER] = is_null($email_me) ? Piwik_PDFReports::EMAIL_ME_PARAMETER_DEFAULT_VALUE : (bool)$email_me;
+ $parameters[Piwik_PDFReports::DISPLAY_FORMAT_PARAMETER] = $display_format;
+
+ Piwik_Query(
+ 'INSERT INTO `' . Piwik_Common::prefixTable('report') . '` SET
idreport = ?, idsite = ?, login = ?, description = ?, period = ?,
type = ?, format = ?, reports = ?, parameters = ?, ts_created = ?,
ts_last_sent = ?, deleted = ?',
- array(
- $idreport,
- $idsite,
- $login,
- $description,
- is_null($period) ? Piwik_PDFReports::DEFAULT_PERIOD : $period,
- Piwik_PDFReports::EMAIL_TYPE,
- is_null($format) ? Piwik_PDFReports::DEFAULT_REPORT_FORMAT : $format,
- Piwik_Common::json_encode(preg_split('/,/', $reports)),
- Piwik_Common::json_encode($parameters),
- $ts_created,
- $ts_last_sent,
- $deleted
- )
- );
- }
+ array(
+ $idreport,
+ $idsite,
+ $login,
+ $description,
+ is_null($period) ? Piwik_PDFReports::DEFAULT_PERIOD : $period,
+ Piwik_PDFReports::EMAIL_TYPE,
+ is_null($format) ? Piwik_PDFReports::DEFAULT_REPORT_FORMAT : $format,
+ Piwik_Common::json_encode(preg_split('/,/', $reports)),
+ Piwik_Common::json_encode($parameters),
+ $ts_created,
+ $ts_last_sent,
+ $deleted
+ )
+ );
+ }
+
+ Piwik_Query('DROP TABLE `' . Piwik_Common::prefixTable('pdf') . '`');
+ } catch (Exception $e) {
+ }
- Piwik_Query('DROP TABLE `'. Piwik_Common::prefixTable('pdf') .'`');
- }
- catch(Exception $e){}
-
- }
+ }
}
diff --git a/core/Updates/1.8.4-b1.php b/core/Updates/1.8.4-b1.php
index 2ae6698f78..2c0b5138c7 100644
--- a/core/Updates/1.8.4-b1.php
+++ b/core/Updates/1.8.4-b1.php
@@ -14,29 +14,29 @@
*/
class Piwik_Updates_1_8_4_b1 extends Piwik_Updates
{
-
- static function isMajorUpdate()
- {
- return true;
- }
-
- static function getSql($schema = 'Myisam')
- {
- $action = Piwik_Common::prefixTable('log_action');
- $duplicates = Piwik_Common::prefixTable('log_action_duplicates');
- $visitAction = Piwik_Common::prefixTable('log_link_visit_action');
- $conversion = Piwik_Common::prefixTable('log_conversion');
- $visit = Piwik_Common::prefixTable('log_visit');
-
- return array(
-
- // add url_prefix column
- " ALTER TABLE `$action`
+
+ static function isMajorUpdate()
+ {
+ return true;
+ }
+
+ static function getSql($schema = 'Myisam')
+ {
+ $action = Piwik_Common::prefixTable('log_action');
+ $duplicates = Piwik_Common::prefixTable('log_action_duplicates');
+ $visitAction = Piwik_Common::prefixTable('log_link_visit_action');
+ $conversion = Piwik_Common::prefixTable('log_conversion');
+ $visit = Piwik_Common::prefixTable('log_visit');
+
+ return array(
+
+ // add url_prefix column
+ " ALTER TABLE `$action`
ADD `url_prefix` TINYINT(2) NULL AFTER `type`;
- " => 1060, // ignore error 1060 Duplicate column name 'url_prefix'
-
- // remove protocol and www and store information in url_prefix
- " UPDATE `$action`
+ " => 1060, // ignore error 1060 Duplicate column name 'url_prefix'
+
+ // remove protocol and www and store information in url_prefix
+ " UPDATE `$action`
SET
url_prefix = IF (
LEFT(name, 11) = 'http://www.', 1, IF (
@@ -60,21 +60,21 @@ class Piwik_Updates_1_8_4_b1 extends Piwik_Updates
WHERE
type = 1 AND
url_prefix IS NULL;
- " => false,
-
- // find duplicates
- " DROP TABLE IF EXISTS `$duplicates`;
- " => false,
- " CREATE TABLE `$duplicates` (
+ " => false,
+
+ // find duplicates
+ " DROP TABLE IF EXISTS `$duplicates`;
+ " => false,
+ " CREATE TABLE `$duplicates` (
`before` int(10) unsigned NOT NULL,
`after` int(10) unsigned NOT NULL,
KEY `mainkey` (`before`)
) ENGINE=MyISAM;
- " => false,
+ " => false,
- // grouping by name only would be case-insensitive, so we GROUP BY name,hash
- // ON (action.type = 1 AND canonical.hash = action.hash) will use index (type, hash)
- " INSERT INTO `$duplicates` (
+ // grouping by name only would be case-insensitive, so we GROUP BY name,hash
+ // ON (action.type = 1 AND canonical.hash = action.hash) will use index (type, hash)
+ " INSERT INTO `$duplicates` (
SELECT
action.idaction AS `before`,
canonical.idaction AS `after`
@@ -100,9 +100,9 @@ class Piwik_Updates_1_8_4_b1 extends Piwik_Updates
AND canonical.idaction != action.idaction
);
" => false,
-
- // replace idaction in log_link_visit_action
- " UPDATE
+
+ // replace idaction in log_link_visit_action
+ " UPDATE
`$visitAction` AS link
LEFT JOIN
`$duplicates` AS duplicates_idaction_url
@@ -111,8 +111,8 @@ class Piwik_Updates_1_8_4_b1 extends Piwik_Updates
link.idaction_url = duplicates_idaction_url.after
WHERE
duplicates_idaction_url.after IS NOT NULL;
- " => false,
- " UPDATE
+ " => false,
+ " UPDATE
`$visitAction` AS link
LEFT JOIN
`$duplicates` AS duplicates_idaction_url_ref
@@ -121,10 +121,10 @@ class Piwik_Updates_1_8_4_b1 extends Piwik_Updates
link.idaction_url_ref = duplicates_idaction_url_ref.after
WHERE
duplicates_idaction_url_ref.after IS NOT NULL;
- " => false,
-
- // replace idaction in log_conversion
- " UPDATE
+ " => false,
+
+ // replace idaction in log_conversion
+ " UPDATE
`$conversion` AS conversion
LEFT JOIN
`$duplicates` AS duplicates
@@ -133,10 +133,10 @@ class Piwik_Updates_1_8_4_b1 extends Piwik_Updates
conversion.idaction_url = duplicates.after
WHERE
duplicates.after IS NOT NULL;
- " => false,
-
- // replace idaction in log_visit
- " UPDATE
+ " => false,
+
+ // replace idaction in log_visit
+ " UPDATE
`$visit` AS visit
LEFT JOIN
`$duplicates` AS duplicates_entry
@@ -145,8 +145,8 @@ class Piwik_Updates_1_8_4_b1 extends Piwik_Updates
visit.visit_entry_idaction_url = duplicates_entry.after
WHERE
duplicates_entry.after IS NOT NULL;
- " => false,
- " UPDATE
+ " => false,
+ " UPDATE
`$visit` AS visit
LEFT JOIN
`$duplicates` AS duplicates_exit
@@ -155,36 +155,33 @@ class Piwik_Updates_1_8_4_b1 extends Piwik_Updates
visit.visit_exit_idaction_url = duplicates_exit.after
WHERE
duplicates_exit.after IS NOT NULL;
- " => false,
-
- // remove duplicates from log_action
- " DELETE action FROM
+ " => false,
+
+ // remove duplicates from log_action
+ " DELETE action FROM
`$action` AS action
LEFT JOIN
`$duplicates` AS duplicates
ON action.idaction = duplicates.before
WHERE
duplicates.after IS NOT NULL;
- " => false,
-
- // remove the duplicates table
- " DROP TABLE `$duplicates`;
- " => false
- );
- }
+ " => false,
+
+ // remove the duplicates table
+ " DROP TABLE `$duplicates`;
+ " => false
+ );
+ }
- static function update()
- {
- try
- {
- self::enableMaintenanceMode();
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- self::disableMaintenanceMode();
- }
- catch(Exception $e)
- {
- self::disableMaintenanceMode();
- throw $e;
- }
- }
+ static function update()
+ {
+ try {
+ self::enableMaintenanceMode();
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ self::disableMaintenanceMode();
+ } catch (Exception $e) {
+ self::disableMaintenanceMode();
+ throw $e;
+ }
+ }
}
diff --git a/core/Updates/1.9-b16.php b/core/Updates/1.9-b16.php
index ccac385e9e..b4ec364801 100755
--- a/core/Updates/1.9-b16.php
+++ b/core/Updates/1.9-b16.php
@@ -14,38 +14,38 @@
*/
class Piwik_Updates_1_9_b16 extends Piwik_Updates
{
- static function isMajorUpdate()
- {
- return true;
- }
-
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_link_visit_action') .'`
+ static function isMajorUpdate()
+ {
+ return true;
+ }
+
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_link_visit_action') . '`
CHANGE `idaction_url` `idaction_url` INT( 10 ) UNSIGNED NULL DEFAULT NULL'
- => false,
+ => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
ADD visit_total_searches SMALLINT(5) UNSIGNED NOT NULL AFTER `visit_total_actions`'
- => 1060,
+ => 1060,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('site') .'`
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('site') . '`
ADD sitesearch TINYINT DEFAULT 1 AFTER `excluded_parameters`,
ADD sitesearch_keyword_parameters TEXT NOT NULL AFTER `sitesearch`,
ADD sitesearch_category_parameters TEXT NOT NULL AFTER `sitesearch_keyword_parameters`'
- => 1060,
+ => 1060,
- // enable Site Search for all websites, users can manually disable the setting
- 'UPDATE `'. Piwik_Common::prefixTable('site') .'`
+ // enable Site Search for all websites, users can manually disable the setting
+ 'UPDATE `' . Piwik_Common::prefixTable('site') . '`
SET `sitesearch` = 1' => false,
- );
- }
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Updates/1.9-b19.php b/core/Updates/1.9-b19.php
index 9701a9cbbf..e5fc17f1ba 100755
--- a/core/Updates/1.9-b19.php
+++ b/core/Updates/1.9-b19.php
@@ -14,27 +14,27 @@
*/
class Piwik_Updates_1_9_b19 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_link_visit_action') .'`
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_link_visit_action') . '`
CHANGE `idaction_url_ref` `idaction_url_ref` INT( 10 ) UNSIGNED NULL DEFAULT 0'
- => false,
- 'ALTER TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+ => false,
+ 'ALTER TABLE `' . Piwik_Common::prefixTable('log_visit') . '`
CHANGE `visit_exit_idaction_url` `visit_exit_idaction_url` INT( 10 ) UNSIGNED NULL DEFAULT 0'
- => false
- );
- }
+ => false
+ );
+ }
- static function update()
- {
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ static function update()
+ {
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- try {
- Piwik_PluginsManager::getInstance()->activatePlugin('Transitions');
- } catch(Exception $e) {
- }
- }
+ try {
+ Piwik_PluginsManager::getInstance()->activatePlugin('Transitions');
+ } catch (Exception $e) {
+ }
+ }
}
diff --git a/core/Updates/1.9-b9.php b/core/Updates/1.9-b9.php
index 53e45e276a..f986381d97 100755
--- a/core/Updates/1.9-b9.php
+++ b/core/Updates/1.9-b9.php
@@ -14,44 +14,41 @@
*/
class Piwik_Updates_1_9_b9 extends Piwik_Updates
{
- static function isMajorUpdate()
- {
- return true;
- }
-
- static function getSql($schema = 'Myisam')
- {
- $logVisit = Piwik_Common::prefixTable('log_visit');
- $logConversion = Piwik_Common::prefixTable('log_conversion');
-
- $addColumns = "DROP `location_continent`,
+ static function isMajorUpdate()
+ {
+ return true;
+ }
+
+ static function getSql($schema = 'Myisam')
+ {
+ $logVisit = Piwik_Common::prefixTable('log_visit');
+ $logConversion = Piwik_Common::prefixTable('log_conversion');
+
+ $addColumns = "DROP `location_continent`,
ADD `location_region` CHAR(2) NULL AFTER `location_country`,
ADD `location_city` VARCHAR(255) NULL AFTER `location_region`,
ADD `location_latitude` FLOAT(10, 6) NULL AFTER `location_city`,
ADD `location_longitude` FLOAT(10, 6) NULL AFTER `location_latitude`";
-
- return array(
- // add geoip columns to log_visit
- "ALTER TABLE `$logVisit` $addColumns" => 1091,
-
- // add geoip columns to log_conversion
- "ALTER TABLE `$logConversion` $addColumns" => 1091,
- );
- }
- static function update()
- {
- try
- {
- self::enableMaintenanceMode();
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- self::disableMaintenanceMode();
- }
- catch (Exception $e)
- {
- self::disableMaintenanceMode();
- throw $e;
- }
- }
+ return array(
+ // add geoip columns to log_visit
+ "ALTER TABLE `$logVisit` $addColumns" => 1091,
+
+ // add geoip columns to log_conversion
+ "ALTER TABLE `$logConversion` $addColumns" => 1091,
+ );
+ }
+
+ static function update()
+ {
+ try {
+ self::enableMaintenanceMode();
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ self::disableMaintenanceMode();
+ } catch (Exception $e) {
+ self::disableMaintenanceMode();
+ throw $e;
+ }
+ }
}
diff --git a/core/Updates/1.9.1-b2.php b/core/Updates/1.9.1-b2.php
index 219f5cd357..af9954be9a 100644
--- a/core/Updates/1.9.1-b2.php
+++ b/core/Updates/1.9.1-b2.php
@@ -14,20 +14,20 @@
*/
class Piwik_Updates_1_9_1_b2 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- 'ALTER TABLE '.Piwik_Common::prefixTable('site'). " DROP `feedburnerName`" => 1091
- );
- }
-
- static function update()
- {
- // manually remove ExampleFeedburner column
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('site') . " DROP `feedburnerName`" => 1091
+ );
+ }
- // remove ExampleFeedburner plugin
- $pluginToDelete = 'ExampleFeedburner';
- self::deletePluginFromConfigFile($pluginToDelete);
- }
+ static function update()
+ {
+ // manually remove ExampleFeedburner column
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+
+ // remove ExampleFeedburner plugin
+ $pluginToDelete = 'ExampleFeedburner';
+ self::deletePluginFromConfigFile($pluginToDelete);
+ }
}
diff --git a/core/Updates/1.9.3-b10.php b/core/Updates/1.9.3-b10.php
index 974c359872..6ceecbb67c 100755
--- a/core/Updates/1.9.3-b10.php
+++ b/core/Updates/1.9.3-b10.php
@@ -14,20 +14,17 @@
*/
class Piwik_Updates_1_9_3_b10 extends Piwik_Updates
{
- static function isMajorUpdate()
- {
- return false;
- }
-
- static function update()
- {
- try
- {
- Piwik_PluginsManager::getInstance()->activatePlugin('Annotations');
- }
- catch(Exception $e)
- {
- // pass
- }
- }
+ static function isMajorUpdate()
+ {
+ return false;
+ }
+
+ static function update()
+ {
+ try {
+ Piwik_PluginsManager::getInstance()->activatePlugin('Annotations');
+ } catch (Exception $e) {
+ // pass
+ }
+ }
}
diff --git a/core/Updates/1.9.3-b3.php b/core/Updates/1.9.3-b3.php
index 2c4819b7a7..65338a44eb 100644
--- a/core/Updates/1.9.3-b3.php
+++ b/core/Updates/1.9.3-b3.php
@@ -14,14 +14,14 @@
*/
class Piwik_Updates_1_9_3_b3 extends Piwik_Updates
{
- static function update()
- {
- // Insight was a temporary code name for Overlay
- $pluginToDelete = 'Insight';
- self::deletePluginFromConfigFile($pluginToDelete);
- self::deletePluginFromFilesystem($pluginToDelete);
+ static function update()
+ {
+ // Insight was a temporary code name for Overlay
+ $pluginToDelete = 'Insight';
+ self::deletePluginFromConfigFile($pluginToDelete);
+ self::deletePluginFromFilesystem($pluginToDelete);
- // We also clean up 1.9.1 and delete Feedburner plugin
- self::deletePluginFromFilesystem('Feedburner');
- }
+ // We also clean up 1.9.1 and delete Feedburner plugin
+ self::deletePluginFromFilesystem('Feedburner');
+ }
}
diff --git a/core/Updates/1.9.3-b8.php b/core/Updates/1.9.3-b8.php
index fe223daf3d..e0d29e0acd 100755
--- a/core/Updates/1.9.3-b8.php
+++ b/core/Updates/1.9.3-b8.php
@@ -14,18 +14,18 @@
*/
class Piwik_Updates_1_9_3_b8 extends Piwik_Updates
{
- static function getSql($schema = 'Myisam')
- {
- return array(
- // ignore existing column name error (1060)
- 'ALTER TABLE '.Piwik_Common::prefixTable('site')
- . " ADD COLUMN excluded_user_agents TEXT NOT NULL AFTER excluded_parameters" => 1060,
- );
- }
-
- static function update()
- {
- // add excluded_user_agents column to site table
- Piwik_Updater::updateDatabase(__FILE__, self::getSql());
- }
+ static function getSql($schema = 'Myisam')
+ {
+ return array(
+ // ignore existing column name error (1060)
+ 'ALTER TABLE ' . Piwik_Common::prefixTable('site')
+ . " ADD COLUMN excluded_user_agents TEXT NOT NULL AFTER excluded_parameters" => 1060,
+ );
+ }
+
+ static function update()
+ {
+ // add excluded_user_agents column to site table
+ Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+ }
}
diff --git a/core/Url.php b/core/Url.php
index 748d87f716..b0e74f0067 100644
--- a/core/Url.php
+++ b/core/Url.php
@@ -15,463 +15,429 @@
*
* @package Piwik
*/
-class Piwik_Url
+class Piwik_Url
{
- /**
- * List of hosts that are never checked for validity.
- */
- private static $alwaysTrustedHosts = array('localhost', '127.0.0.1', '::1', '[::1]');
-
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- *
- * @return string
- */
- static public function getCurrentUrl()
- {
- return self::getCurrentScheme() . '://'
- . self::getCurrentHost()
- . self::getCurrentScriptName()
- . self::getCurrentQueryString();
- }
-
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return "http://example.org/dir1/dir2/index.php"
- *
- * @param bool $checkTrustedHost Whether to do trusted host check. Should ALWAYS be true,
- * except in Piwik_Controller.
- * @return string
- */
- static public function getCurrentUrlWithoutQueryString( $checkTrustedHost = true )
- {
- return self::getCurrentScheme() . '://'
- . self::getCurrentHost($default = 'unknown', $checkTrustedHost)
- . self::getCurrentScriptName();
- }
-
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return "http://example.org/dir1/dir2/"
- *
- * @return string with trailing slash
- */
- static public function getCurrentUrlWithoutFileName()
- {
- return self::getCurrentScheme() . '://'
- . self::getCurrentHost()
- . self::getCurrentScriptPath();
- }
-
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return "/dir1/dir2/"
- *
- * @return string with trailing slash
- */
- static public function getCurrentScriptPath()
- {
- $queryString = self::getCurrentScriptName() ;
-
- //add a fake letter case /test/test2/ returns /test which is not expected
- $urlDir = dirname ($queryString . 'x');
- $urlDir = str_replace('\\', '/', $urlDir);
- // if we are in a subpath we add a trailing slash
- if(strlen($urlDir) > 1)
- {
- $urlDir .= '/';
- }
- return $urlDir;
- }
-
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return "/dir1/dir2/index.php"
- *
- * @return string
- */
- static public function getCurrentScriptName()
- {
- $url = '';
-
- if( !empty($_SERVER['REQUEST_URI']) )
- {
- $url = $_SERVER['REQUEST_URI'];
-
- // strip http://host (Apache+Rails anomaly)
- if(preg_match('~^https?://[^/]+($|/.*)~D', $url, $matches))
- {
- $url = $matches[1];
- }
-
- // strip parameters
- if(($pos = strpos($url, "?")) !== false)
- {
- $url = substr($url, 0, $pos);
- }
-
- // strip path_info
- if(isset($_SERVER['PATH_INFO']))
- {
- $url = substr($url, 0, -strlen($_SERVER['PATH_INFO']));
- }
- }
-
- /**
- * SCRIPT_NAME is our fallback, though it may not be set correctly
- *
- * @see http://php.net/manual/en/reserved.variables.php
- */
- if(empty($url))
- {
- if(isset($_SERVER['SCRIPT_NAME']))
- {
- $url = $_SERVER['SCRIPT_NAME'];
- }
- elseif(isset($_SERVER['SCRIPT_FILENAME']))
- {
- $url = $_SERVER['SCRIPT_FILENAME'];
- }
- elseif(isset($_SERVER['argv']))
- {
- $url = $_SERVER['argv'][0];
- }
- }
-
- if(!isset($url[0]) || $url[0] !== '/')
- {
- $url = '/' . $url;
- }
- return $url;
- }
-
- /**
- * If the current URL is 'http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return 'http'
- *
- * @return string 'https' or 'http'
- */
- static public function getCurrentScheme()
- {
- try {
- $assume_secure_protocol = @Piwik_Config::getInstance()->General['assume_secure_protocol'];
- } catch(Exception $e) {
- $assume_secure_protocol = false;
- }
- if($assume_secure_protocol
- || (isset($_SERVER['HTTPS'])
- && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true))
- || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
- )
- {
- return 'https';
- }
- return 'http';
- }
-
- /**
- * Validate "Host" (untrusted user input)
- *
- * @param string|false $host Contents of Host: header from Request. If false, gets the
- * value from the request.
- *
- * @return boolean True if valid; false otherwise
- */
- static public function isValidHost($host = false)
- {
- // only do trusted host check if it's enabled
- if (isset(Piwik_Config::getInstance()->General['enable_trusted_host_check'])
- && Piwik_Config::getInstance()->General['enable_trusted_host_check'] == 0)
- {
- return true;
- }
-
- if ($host === false)
- {
- $host = @$_SERVER['HTTP_HOST'];
- if (empty($host)) // if no current host, assume valid
- {
- return true;
- }
- }
- // if host is in hardcoded whitelist, assume it's valid
- if (in_array($host, self::$alwaysTrustedHosts))
- {
- return true;
- }
-
- $trustedHosts = @Piwik_Config::getInstance()->General['trusted_hosts'];
- // if no trusted hosts, just assume it's valid
- if (empty($trustedHosts))
- {
- self::saveTrustedHostnameInConfig($host);
- return true;
- }
-
- // Only punctuation we allow is '[', ']', ':', '.' and '-'
- $hostLength = Piwik_Common::strlen($host);
- if ($hostLength !== strcspn($host, '`~!@#$%^&*()_+={}\\|;"\'<>,?/ '))
- {
- return false;
- }
-
- foreach ($trustedHosts as &$trustedHost)
- {
- $trustedHost = preg_quote($trustedHost);
- }
- $untrustedHost = Piwik_Common::mb_strtolower($host);
- $untrustedHost = rtrim($untrustedHost, '.');
- $hostRegex = Piwik_Common::mb_strtolower('/(^|.)' . implode('|', $trustedHosts) . '$/');
- $result = preg_match($hostRegex, $untrustedHost);
- return 0 !== $result;
- }
-
- /**
- * Records one host, or an array of hosts in the config file,
- * if user is super user
- *
- * @static
- * @param $host string|array
- */
- public static function saveTrustedHostnameInConfig($host)
- {
- if (Piwik::isUserIsSuperUser()
- && file_exists(Piwik_Config::getLocalConfigPath()))
- {
- $general = Piwik_Config::getInstance()->General;
- if(!is_array($host))
- {
- $host = array($host);
- }
- $host = array_filter($host);
- if(empty($host)) {
- return false;
- }
- $general['trusted_hosts'] = $host;
- Piwik_Config::getInstance()->General = $general;
- Piwik_Config::getInstance()->forceSave();
- }
- }
-
- /**
- * Get host
- *
- * @param bool $checkIfTrusted Whether to do trusted host check. Should ALWAYS be true,
- * except in Piwik_Controller.
- * @return string|false
- */
- static public function getHost( $checkIfTrusted = true )
- {
- // HTTP/1.1 request
- if (isset($_SERVER['HTTP_HOST'])
- && strlen($host = $_SERVER['HTTP_HOST'])
- && (!$checkIfTrusted
- || self::isValidHost($host)))
- {
- return $host;
- }
-
- // HTTP/1.0 request doesn't include Host: header
- if (isset($_SERVER['SERVER_ADDR']))
- {
- return $_SERVER['SERVER_ADDR'];
- }
-
- return false;
- }
-
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return "example.org"
- *
- * @param string $default Default value to return if host unknown
- * @param bool $checkTrustedHost Whether to do trusted host check. Should ALWAYS be true,
- * except in Piwik_Controller.
- * @return string
- */
- static public function getCurrentHost($default = 'unknown', $checkTrustedHost = true)
- {
- $hostHeaders = @Piwik_Config::getInstance()->General['proxy_host_headers'];
- if(!is_array($hostHeaders))
- {
- $hostHeaders = array();
- }
-
- $host = self::getHost($checkTrustedHost);
- $default = Piwik_Common::sanitizeInputValue($host ? $host : $default);
-
- return Piwik_IP::getNonProxyIpFromHeader($default, $hostHeaders);
- }
-
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return "?param1=value1&param2=value2"
- *
- * @return string
- */
- static public function getCurrentQueryString()
- {
- $url = '';
- if(isset($_SERVER['QUERY_STRING'])
- && !empty($_SERVER['QUERY_STRING']))
- {
- $url .= "?".$_SERVER['QUERY_STRING'];
- }
- return $url;
- }
-
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return
- * array
- * 'param1' => string 'value1'
- * 'param2' => string 'value2'
- *
- * @return array
- */
- static function getArrayFromCurrentQueryString()
- {
- $queryString = self::getCurrentQueryString();
- $urlValues = Piwik_Common::getArrayFromQueryString($queryString);
- return $urlValues;
- }
-
- /**
- * Given an array of name-values, it will return the current query string
- * with the new requested parameter key-values;
- * If a parameter wasn't found in the current query string, the new key-value will be added to the returned query string.
- *
- * @param array $params array ( 'param3' => 'value3' )
- * @return string ?param2=value2&param3=value3
- */
- static function getCurrentQueryStringWithParametersModified( $params )
- {
- $urlValues = self::getArrayFromCurrentQueryString();
- foreach($params as $key => $value)
- {
- $urlValues[$key] = $value;
- }
- $query = self::getQueryStringFromParameters($urlValues);
- if(strlen($query) > 0)
- {
- return '?'.$query;
- }
- return '';
- }
-
- /**
- * Given an array of parameters name->value, returns the query string.
- * Also works with array values using the php array syntax for GET parameters.
- *
- * @param array $parameters eg. array( 'param1' => 10, 'param2' => array(1,2))
- * @return string eg. "param1=10&param2[]=1&param2[]=2"
- */
- static public function getQueryStringFromParameters($parameters)
- {
- $query = '';
- foreach($parameters as $name => $value)
- {
- if(is_null($value)
- || $value === false)
- {
- continue;
- }
- if(is_array($value))
- {
- foreach($value as $theValue)
- {
- $query .= $name . "[]=" . $theValue . "&";
- }
- }
- else
- {
- $query .= $name . "=" . $value . "&";
- }
- }
- $query = substr($query, 0, -1);
- return $query;
- }
-
- /**
- * Redirects the user to the referrer if found.
- * If the user doesn't have a referrer set, it redirects to the current URL without query string.
- */
- static public function redirectToReferer()
- {
- $referrer = self::getReferer();
- if($referrer !== false)
- {
- self::redirectToUrl($referrer);
- }
- self::redirectToUrl(self::getCurrentUrlWithoutQueryString());
- }
-
- /**
- * Redirects the user to the specified URL
- *
- * @param string $url
- */
- static public function redirectToUrl( $url )
- {
- if(Piwik_Common::isLookLikeUrl($url)
- || strpos($url, 'index.php') === 0)
- {
- @header("Location: $url");
- }
- else
- {
- echo "Invalid URL to redirect to.";
- }
- exit;
- }
-
- /**
- * Returns the HTTP_REFERER header, false if not found.
- *
- * @return string|false
- */
- static public function getReferer()
- {
- if(!empty($_SERVER['HTTP_REFERER']))
- {
- return $_SERVER['HTTP_REFERER'];
- }
- return false;
- }
-
- /**
- * Is the URL on the same host?
- *
- * @param string $url
- * @return bool True if local; false otherwise.
- */
- static public function isLocalUrl($url)
- {
- if(empty($url))
- {
- return true;
- }
-
- // handle host name mangling
- $requestUri = isset($_SERVER['SCRIPT_URI']) ? $_SERVER['SCRIPT_URI'] : '';
- $parseRequest = @parse_url($requestUri);
- $hosts = array( self::getHost(), self::getCurrentHost() );
- if(!empty($parseRequest['host']))
- {
- $hosts[] = $parseRequest['host'];
- }
-
- // drop port numbers from hostnames and IP addresses
- $hosts = array_map(array('Piwik_IP', 'sanitizeIp'), $hosts);
-
- $disableHostCheck = Piwik_Config::getInstance()->General['enable_trusted_host_check'] == 0;
- // compare scheme and host
- $parsedUrl = @parse_url($url);
- $host = Piwik_IP::sanitizeIp(@$parsedUrl['host']);
- return !empty($host)
- && ($disableHostCheck || in_array($host, $hosts))
- && !empty($parsedUrl['scheme'])
- && in_array($parsedUrl['scheme'], array('http', 'https'));
- }
+ /**
+ * List of hosts that are never checked for validity.
+ */
+ private static $alwaysTrustedHosts = array('localhost', '127.0.0.1', '::1', '[::1]');
+
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ *
+ * @return string
+ */
+ static public function getCurrentUrl()
+ {
+ return self::getCurrentScheme() . '://'
+ . self::getCurrentHost()
+ . self::getCurrentScriptName()
+ . self::getCurrentQueryString();
+ }
+
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return "http://example.org/dir1/dir2/index.php"
+ *
+ * @param bool $checkTrustedHost Whether to do trusted host check. Should ALWAYS be true,
+ * except in Piwik_Controller.
+ * @return string
+ */
+ static public function getCurrentUrlWithoutQueryString($checkTrustedHost = true)
+ {
+ return self::getCurrentScheme() . '://'
+ . self::getCurrentHost($default = 'unknown', $checkTrustedHost)
+ . self::getCurrentScriptName();
+ }
+
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return "http://example.org/dir1/dir2/"
+ *
+ * @return string with trailing slash
+ */
+ static public function getCurrentUrlWithoutFileName()
+ {
+ return self::getCurrentScheme() . '://'
+ . self::getCurrentHost()
+ . self::getCurrentScriptPath();
+ }
+
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return "/dir1/dir2/"
+ *
+ * @return string with trailing slash
+ */
+ static public function getCurrentScriptPath()
+ {
+ $queryString = self::getCurrentScriptName();
+
+ //add a fake letter case /test/test2/ returns /test which is not expected
+ $urlDir = dirname($queryString . 'x');
+ $urlDir = str_replace('\\', '/', $urlDir);
+ // if we are in a subpath we add a trailing slash
+ if (strlen($urlDir) > 1) {
+ $urlDir .= '/';
+ }
+ return $urlDir;
+ }
+
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return "/dir1/dir2/index.php"
+ *
+ * @return string
+ */
+ static public function getCurrentScriptName()
+ {
+ $url = '';
+
+ if (!empty($_SERVER['REQUEST_URI'])) {
+ $url = $_SERVER['REQUEST_URI'];
+
+ // strip http://host (Apache+Rails anomaly)
+ if (preg_match('~^https?://[^/]+($|/.*)~D', $url, $matches)) {
+ $url = $matches[1];
+ }
+
+ // strip parameters
+ if (($pos = strpos($url, "?")) !== false) {
+ $url = substr($url, 0, $pos);
+ }
+
+ // strip path_info
+ if (isset($_SERVER['PATH_INFO'])) {
+ $url = substr($url, 0, -strlen($_SERVER['PATH_INFO']));
+ }
+ }
+
+ /**
+ * SCRIPT_NAME is our fallback, though it may not be set correctly
+ *
+ * @see http://php.net/manual/en/reserved.variables.php
+ */
+ if (empty($url)) {
+ if (isset($_SERVER['SCRIPT_NAME'])) {
+ $url = $_SERVER['SCRIPT_NAME'];
+ } elseif (isset($_SERVER['SCRIPT_FILENAME'])) {
+ $url = $_SERVER['SCRIPT_FILENAME'];
+ } elseif (isset($_SERVER['argv'])) {
+ $url = $_SERVER['argv'][0];
+ }
+ }
+
+ if (!isset($url[0]) || $url[0] !== '/') {
+ $url = '/' . $url;
+ }
+ return $url;
+ }
+
+ /**
+ * If the current URL is 'http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return 'http'
+ *
+ * @return string 'https' or 'http'
+ */
+ static public function getCurrentScheme()
+ {
+ try {
+ $assume_secure_protocol = @Piwik_Config::getInstance()->General['assume_secure_protocol'];
+ } catch (Exception $e) {
+ $assume_secure_protocol = false;
+ }
+ if ($assume_secure_protocol
+ || (isset($_SERVER['HTTPS'])
+ && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true))
+ || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
+ ) {
+ return 'https';
+ }
+ return 'http';
+ }
+
+ /**
+ * Validate "Host" (untrusted user input)
+ *
+ * @param string|false $host Contents of Host: header from Request. If false, gets the
+ * value from the request.
+ *
+ * @return boolean True if valid; false otherwise
+ */
+ static public function isValidHost($host = false)
+ {
+ // only do trusted host check if it's enabled
+ if (isset(Piwik_Config::getInstance()->General['enable_trusted_host_check'])
+ && Piwik_Config::getInstance()->General['enable_trusted_host_check'] == 0
+ ) {
+ return true;
+ }
+
+ if ($host === false) {
+ $host = @$_SERVER['HTTP_HOST'];
+ if (empty($host)) // if no current host, assume valid
+ {
+ return true;
+ }
+ }
+ // if host is in hardcoded whitelist, assume it's valid
+ if (in_array($host, self::$alwaysTrustedHosts)) {
+ return true;
+ }
+
+ $trustedHosts = @Piwik_Config::getInstance()->General['trusted_hosts'];
+ // if no trusted hosts, just assume it's valid
+ if (empty($trustedHosts)) {
+ self::saveTrustedHostnameInConfig($host);
+ return true;
+ }
+
+ // Only punctuation we allow is '[', ']', ':', '.' and '-'
+ $hostLength = Piwik_Common::strlen($host);
+ if ($hostLength !== strcspn($host, '`~!@#$%^&*()_+={}\\|;"\'<>,?/ ')) {
+ return false;
+ }
+
+ foreach ($trustedHosts as &$trustedHost) {
+ $trustedHost = preg_quote($trustedHost);
+ }
+ $untrustedHost = Piwik_Common::mb_strtolower($host);
+ $untrustedHost = rtrim($untrustedHost, '.');
+ $hostRegex = Piwik_Common::mb_strtolower('/(^|.)' . implode('|', $trustedHosts) . '$/');
+ $result = preg_match($hostRegex, $untrustedHost);
+ return 0 !== $result;
+ }
+
+ /**
+ * Records one host, or an array of hosts in the config file,
+ * if user is super user
+ *
+ * @static
+ * @param $host string|array
+ */
+ public static function saveTrustedHostnameInConfig($host)
+ {
+ if (Piwik::isUserIsSuperUser()
+ && file_exists(Piwik_Config::getLocalConfigPath())
+ ) {
+ $general = Piwik_Config::getInstance()->General;
+ if (!is_array($host)) {
+ $host = array($host);
+ }
+ $host = array_filter($host);
+ if (empty($host)) {
+ return false;
+ }
+ $general['trusted_hosts'] = $host;
+ Piwik_Config::getInstance()->General = $general;
+ Piwik_Config::getInstance()->forceSave();
+ }
+ }
+
+ /**
+ * Get host
+ *
+ * @param bool $checkIfTrusted Whether to do trusted host check. Should ALWAYS be true,
+ * except in Piwik_Controller.
+ * @return string|false
+ */
+ static public function getHost($checkIfTrusted = true)
+ {
+ // HTTP/1.1 request
+ if (isset($_SERVER['HTTP_HOST'])
+ && strlen($host = $_SERVER['HTTP_HOST'])
+ && (!$checkIfTrusted
+ || self::isValidHost($host))
+ ) {
+ return $host;
+ }
+
+ // HTTP/1.0 request doesn't include Host: header
+ if (isset($_SERVER['SERVER_ADDR'])) {
+ return $_SERVER['SERVER_ADDR'];
+ }
+
+ return false;
+ }
+
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return "example.org"
+ *
+ * @param string $default Default value to return if host unknown
+ * @param bool $checkTrustedHost Whether to do trusted host check. Should ALWAYS be true,
+ * except in Piwik_Controller.
+ * @return string
+ */
+ static public function getCurrentHost($default = 'unknown', $checkTrustedHost = true)
+ {
+ $hostHeaders = @Piwik_Config::getInstance()->General['proxy_host_headers'];
+ if (!is_array($hostHeaders)) {
+ $hostHeaders = array();
+ }
+
+ $host = self::getHost($checkTrustedHost);
+ $default = Piwik_Common::sanitizeInputValue($host ? $host : $default);
+
+ return Piwik_IP::getNonProxyIpFromHeader($default, $hostHeaders);
+ }
+
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return "?param1=value1&param2=value2"
+ *
+ * @return string
+ */
+ static public function getCurrentQueryString()
+ {
+ $url = '';
+ if (isset($_SERVER['QUERY_STRING'])
+ && !empty($_SERVER['QUERY_STRING'])
+ ) {
+ $url .= "?" . $_SERVER['QUERY_STRING'];
+ }
+ return $url;
+ }
+
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return
+ * array
+ * 'param1' => string 'value1'
+ * 'param2' => string 'value2'
+ *
+ * @return array
+ */
+ static function getArrayFromCurrentQueryString()
+ {
+ $queryString = self::getCurrentQueryString();
+ $urlValues = Piwik_Common::getArrayFromQueryString($queryString);
+ return $urlValues;
+ }
+
+ /**
+ * Given an array of name-values, it will return the current query string
+ * with the new requested parameter key-values;
+ * If a parameter wasn't found in the current query string, the new key-value will be added to the returned query string.
+ *
+ * @param array $params array ( 'param3' => 'value3' )
+ * @return string ?param2=value2&param3=value3
+ */
+ static function getCurrentQueryStringWithParametersModified($params)
+ {
+ $urlValues = self::getArrayFromCurrentQueryString();
+ foreach ($params as $key => $value) {
+ $urlValues[$key] = $value;
+ }
+ $query = self::getQueryStringFromParameters($urlValues);
+ if (strlen($query) > 0) {
+ return '?' . $query;
+ }
+ return '';
+ }
+
+ /**
+ * Given an array of parameters name->value, returns the query string.
+ * Also works with array values using the php array syntax for GET parameters.
+ *
+ * @param array $parameters eg. array( 'param1' => 10, 'param2' => array(1,2))
+ * @return string eg. "param1=10&param2[]=1&param2[]=2"
+ */
+ static public function getQueryStringFromParameters($parameters)
+ {
+ $query = '';
+ foreach ($parameters as $name => $value) {
+ if (is_null($value)
+ || $value === false
+ ) {
+ continue;
+ }
+ if (is_array($value)) {
+ foreach ($value as $theValue) {
+ $query .= $name . "[]=" . $theValue . "&";
+ }
+ } else {
+ $query .= $name . "=" . $value . "&";
+ }
+ }
+ $query = substr($query, 0, -1);
+ return $query;
+ }
+
+ /**
+ * Redirects the user to the referrer if found.
+ * If the user doesn't have a referrer set, it redirects to the current URL without query string.
+ */
+ static public function redirectToReferer()
+ {
+ $referrer = self::getReferer();
+ if ($referrer !== false) {
+ self::redirectToUrl($referrer);
+ }
+ self::redirectToUrl(self::getCurrentUrlWithoutQueryString());
+ }
+
+ /**
+ * Redirects the user to the specified URL
+ *
+ * @param string $url
+ */
+ static public function redirectToUrl($url)
+ {
+ if (Piwik_Common::isLookLikeUrl($url)
+ || strpos($url, 'index.php') === 0
+ ) {
+ @header("Location: $url");
+ } else {
+ echo "Invalid URL to redirect to.";
+ }
+ exit;
+ }
+
+ /**
+ * Returns the HTTP_REFERER header, false if not found.
+ *
+ * @return string|false
+ */
+ static public function getReferer()
+ {
+ if (!empty($_SERVER['HTTP_REFERER'])) {
+ return $_SERVER['HTTP_REFERER'];
+ }
+ return false;
+ }
+
+ /**
+ * Is the URL on the same host?
+ *
+ * @param string $url
+ * @return bool True if local; false otherwise.
+ */
+ static public function isLocalUrl($url)
+ {
+ if (empty($url)) {
+ return true;
+ }
+
+ // handle host name mangling
+ $requestUri = isset($_SERVER['SCRIPT_URI']) ? $_SERVER['SCRIPT_URI'] : '';
+ $parseRequest = @parse_url($requestUri);
+ $hosts = array(self::getHost(), self::getCurrentHost());
+ if (!empty($parseRequest['host'])) {
+ $hosts[] = $parseRequest['host'];
+ }
+
+ // drop port numbers from hostnames and IP addresses
+ $hosts = array_map(array('Piwik_IP', 'sanitizeIp'), $hosts);
+
+ $disableHostCheck = Piwik_Config::getInstance()->General['enable_trusted_host_check'] == 0;
+ // compare scheme and host
+ $parsedUrl = @parse_url($url);
+ $host = Piwik_IP::sanitizeIp(@$parsedUrl['host']);
+ return !empty($host)
+ && ($disableHostCheck || in_array($host, $hosts))
+ && !empty($parsedUrl['scheme'])
+ && in_array($parsedUrl['scheme'], array('http', 'https'));
+ }
}
diff --git a/core/Version.php b/core/Version.php
index 545823c3b6..d6e1bca6ba 100644
--- a/core/Version.php
+++ b/core/Version.php
@@ -16,9 +16,9 @@
*/
final class Piwik_Version
{
- /**
- * Current Piwik version
- * @var string
- */
- const VERSION = '1.12-b1';
+ /**
+ * Current Piwik version
+ * @var string
+ */
+ const VERSION = '1.12-b1';
}
diff --git a/core/View.php b/core/View.php
index 982e4d381c..cb26fff689 100644
--- a/core/View.php
+++ b/core/View.php
@@ -12,9 +12,8 @@
/**
* Transition for pre-Piwik 0.4.4
*/
-if(!defined('PIWIK_USER_PATH'))
-{
- define('PIWIK_USER_PATH', PIWIK_INCLUDE_PATH);
+if (!defined('PIWIK_USER_PATH')) {
+ define('PIWIK_USER_PATH', PIWIK_INCLUDE_PATH);
}
/**
@@ -24,236 +23,220 @@ if(!defined('PIWIK_USER_PATH'))
*/
class Piwik_View implements Piwik_View_Interface
{
- const COREUPDATER_ONE_CLICK_DONE = 'update_one_click_done';
-
-
- private $template = '';
- private $smarty = false;
- private $contentType = 'text/html; charset=utf-8';
- private $xFrameOptions = null;
-
- public function __construct( $templateFile, $smConf = array(), $filter = true )
- {
- $this->template = $templateFile;
- $this->smarty = new Piwik_Smarty($smConf, $filter);
-
- // global value accessible to all templates: the piwik base URL for the current request
- $this->piwik_version = Piwik_Version::VERSION;
- $this->cacheBuster = md5(Piwik_Common::getSalt() . PHP_VERSION . Piwik_Version::VERSION);
- $this->piwikUrl = Piwik_Common::sanitizeInputValue(Piwik_Url::getCurrentUrlWithoutFileName());
- }
-
- /**
- * Directly assigns a variable to the view script.
- * VAR names may not be prefixed with '_'.
- *
- * @param string $key The variable name.
- * @param mixed $val The variable value.
- */
- public function __set($key, $val)
- {
- $this->smarty->assign($key, $val);
- }
-
- /**
- * Retrieves an assigned variable.
- * VAR names may not be prefixed with '_'.
- *
- * @param string $key The variable name.
- * @return mixed The variable value.
- */
- public function __get($key)
- {
- return $this->smarty->get_template_vars($key);
- }
-
- /**
- * Renders the current view.
- *
- * @return string Generated template
- */
- public function render()
- {
- try {
- $this->currentModule = Piwik::getModule();
- $this->currentAction = Piwik::getAction();
- $userLogin = Piwik::getCurrentUserLogin();
- $this->userLogin = $userLogin;
-
- $count = Piwik::getWebsitesCountToDisplay();
-
- $sites = Piwik_SitesManager_API::getInstance()->getSitesWithAtLeastViewAccess($count);
- usort($sites, create_function('$site1, $site2', 'return strcasecmp($site1["name"], $site2["name"]);'));
- $this->sites = $sites;
- $this->url = Piwik_Common::sanitizeInputValue(Piwik_Url::getCurrentUrl());
- $this->token_auth = Piwik::getCurrentUserTokenAuth();
- $this->userHasSomeAdminAccess = Piwik::isUserHasSomeAdminAccess();
- $this->userIsSuperUser = Piwik::isUserIsSuperUser();
- $this->latest_version_available = Piwik_UpdateCheck::isNewestVersionAvailable();
- $this->disableLink = Piwik_Common::getRequestVar('disableLink', 0, 'int');
- $this->isWidget = Piwik_Common::getRequestVar('widget', 0, 'int');
- if(Piwik_Config::getInstance()->General['autocomplete_min_sites'] <= count($sites))
- {
- $this->show_autocompleter = true;
- }
- else
- {
- $this->show_autocompleter = false;
- }
-
- $this->loginModule = Piwik::getLoginPluginName();
-
- $user = Piwik_UsersManager_API::getInstance()->getUser($userLogin);
- $this->userAlias = $user['alias'];
-
- } catch(Exception $e) {
- // can fail, for example at installation (no plugin loaded yet)
- }
-
- $this->totalTimeGeneration = Zend_Registry::get('timer')->getTime();
- try {
- $this->totalNumberOfQueries = Piwik::getQueryCount();
- }
- catch(Exception $e){
- $this->totalNumberOfQueries = 0;
- }
-
- Piwik::overrideCacheControlHeaders('no-store');
-
- @header('Content-Type: '.$this->contentType);
- // always sending this header, sometimes empty, to ensure that Dashboard embed loads (which could call this header() multiple times, the last one will prevail)
- @header('X-Frame-Options: '. (string)$this->xFrameOptions);
-
- return $this->smarty->fetch($this->template);
- }
-
- /**
- * Set Content-Type field in HTTP response.
- * Since PHP 5.1.2, header() protects against header injection attacks.
- *
- * @param string $contentType
- */
- public function setContentType( $contentType )
- {
- $this->contentType = $contentType;
- }
-
- /**
- * Set X-Frame-Options field in the HTTP response.
- *
- * @param string $option ('deny' or 'sameorigin')
- */
- public function setXFrameOptions( $option = 'deny' )
- {
- if($option === 'deny' || $option === 'sameorigin')
- {
- $this->xFrameOptions = $option;
- }
- if($option == 'allow')
- {
- $this->xFrameOptions = null;
- }
- }
-
- /**
- * Add form to view
- *
- * @param Piwik_QuickForm2 $form
- */
- public function addForm( $form )
- {
- if($form instanceof Piwik_QuickForm2)
- {
- // assign array with form data
- $this->smarty->assign('form_data', $form->getFormData());
- $this->smarty->assign('element_list', $form->getElementList());
- }
- }
+ const COREUPDATER_ONE_CLICK_DONE = 'update_one_click_done';
+
+
+ private $template = '';
+ private $smarty = false;
+ private $contentType = 'text/html; charset=utf-8';
+ private $xFrameOptions = null;
+
+ public function __construct($templateFile, $smConf = array(), $filter = true)
+ {
+ $this->template = $templateFile;
+ $this->smarty = new Piwik_Smarty($smConf, $filter);
+
+ // global value accessible to all templates: the piwik base URL for the current request
+ $this->piwik_version = Piwik_Version::VERSION;
+ $this->cacheBuster = md5(Piwik_Common::getSalt() . PHP_VERSION . Piwik_Version::VERSION);
+ $this->piwikUrl = Piwik_Common::sanitizeInputValue(Piwik_Url::getCurrentUrlWithoutFileName());
+ }
+
+ /**
+ * Directly assigns a variable to the view script.
+ * VAR names may not be prefixed with '_'.
+ *
+ * @param string $key The variable name.
+ * @param mixed $val The variable value.
+ */
+ public function __set($key, $val)
+ {
+ $this->smarty->assign($key, $val);
+ }
+
+ /**
+ * Retrieves an assigned variable.
+ * VAR names may not be prefixed with '_'.
+ *
+ * @param string $key The variable name.
+ * @return mixed The variable value.
+ */
+ public function __get($key)
+ {
+ return $this->smarty->get_template_vars($key);
+ }
+
+ /**
+ * Renders the current view.
+ *
+ * @return string Generated template
+ */
+ public function render()
+ {
+ try {
+ $this->currentModule = Piwik::getModule();
+ $this->currentAction = Piwik::getAction();
+ $userLogin = Piwik::getCurrentUserLogin();
+ $this->userLogin = $userLogin;
+
+ $count = Piwik::getWebsitesCountToDisplay();
+
+ $sites = Piwik_SitesManager_API::getInstance()->getSitesWithAtLeastViewAccess($count);
+ usort($sites, create_function('$site1, $site2', 'return strcasecmp($site1["name"], $site2["name"]);'));
+ $this->sites = $sites;
+ $this->url = Piwik_Common::sanitizeInputValue(Piwik_Url::getCurrentUrl());
+ $this->token_auth = Piwik::getCurrentUserTokenAuth();
+ $this->userHasSomeAdminAccess = Piwik::isUserHasSomeAdminAccess();
+ $this->userIsSuperUser = Piwik::isUserIsSuperUser();
+ $this->latest_version_available = Piwik_UpdateCheck::isNewestVersionAvailable();
+ $this->disableLink = Piwik_Common::getRequestVar('disableLink', 0, 'int');
+ $this->isWidget = Piwik_Common::getRequestVar('widget', 0, 'int');
+ if (Piwik_Config::getInstance()->General['autocomplete_min_sites'] <= count($sites)) {
+ $this->show_autocompleter = true;
+ } else {
+ $this->show_autocompleter = false;
+ }
- /**
- * Assign value to a variable for use in Smarty template
- *
- * @param string|array $var
- * @param mixed $value
- */
- public function assign($var, $value=null)
- {
- if (is_string($var))
- {
- $this->smarty->assign($var, $value);
- }
- elseif (is_array($var))
- {
- foreach ($var as $key => $value)
- {
- $this->smarty->assign($key, $value);
- }
- }
- }
+ $this->loginModule = Piwik::getLoginPluginName();
- /**
- * Clear compiled Smarty templates
- */
- static public function clearCompiledTemplates()
- {
- $view = new Piwik_View(null);
- $view->smarty->clear_compiled_tpl();
- }
+ $user = Piwik_UsersManager_API::getInstance()->getUser($userLogin);
+ $this->userAlias = $user['alias'];
- /**
- * Render the single report template
- *
- * @param string $title Report title
- * @param string $reportHtml Report body
- * @param bool $fetch If true, return report contents as a string; else echo to screen
- * @return string Report contents if $fetch == true
- */
- static public function singleReport($title, $reportHtml, $fetch = false)
- {
- $view = new Piwik_View('CoreHome/templates/single_report.tpl');
- $view->title = $title;
- $view->report = $reportHtml;
+ } catch (Exception $e) {
+ // can fail, for example at installation (no plugin loaded yet)
+ }
- if ($fetch)
- {
- return $view->render();
- }
- echo $view->render();
- }
+ $this->totalTimeGeneration = Zend_Registry::get('timer')->getTime();
+ try {
+ $this->totalNumberOfQueries = Piwik::getQueryCount();
+ } catch (Exception $e) {
+ $this->totalNumberOfQueries = 0;
+ }
- /**
- * View factory method
- *
- * @param string $templateName Template name (e.g., 'index')
- * @throws Exception
- * @return Piwik_View|Piwik_View_OneClickDone
- */
- static public function factory( $templateName = null )
- {
- if ($templateName == self::COREUPDATER_ONE_CLICK_DONE)
- {
- return new Piwik_View_OneClickDone(Piwik::getCurrentUserTokenAuth());
- }
+ Piwik::overrideCacheControlHeaders('no-store');
+
+ @header('Content-Type: ' . $this->contentType);
+ // always sending this header, sometimes empty, to ensure that Dashboard embed loads (which could call this header() multiple times, the last one will prevail)
+ @header('X-Frame-Options: ' . (string)$this->xFrameOptions);
+
+ return $this->smarty->fetch($this->template);
+ }
+
+ /**
+ * Set Content-Type field in HTTP response.
+ * Since PHP 5.1.2, header() protects against header injection attacks.
+ *
+ * @param string $contentType
+ */
+ public function setContentType($contentType)
+ {
+ $this->contentType = $contentType;
+ }
+
+ /**
+ * Set X-Frame-Options field in the HTTP response.
+ *
+ * @param string $option ('deny' or 'sameorigin')
+ */
+ public function setXFrameOptions($option = 'deny')
+ {
+ if ($option === 'deny' || $option === 'sameorigin') {
+ $this->xFrameOptions = $option;
+ }
+ if ($option == 'allow') {
+ $this->xFrameOptions = null;
+ }
+ }
+
+ /**
+ * Add form to view
+ *
+ * @param Piwik_QuickForm2 $form
+ */
+ public function addForm($form)
+ {
+ if ($form instanceof Piwik_QuickForm2) {
+ // assign array with form data
+ $this->smarty->assign('form_data', $form->getFormData());
+ $this->smarty->assign('element_list', $form->getElementList());
+ }
+ }
+
+ /**
+ * Assign value to a variable for use in Smarty template
+ *
+ * @param string|array $var
+ * @param mixed $value
+ */
+ public function assign($var, $value = null)
+ {
+ if (is_string($var)) {
+ $this->smarty->assign($var, $value);
+ } elseif (is_array($var)) {
+ foreach ($var as $key => $value) {
+ $this->smarty->assign($key, $value);
+ }
+ }
+ }
+
+ /**
+ * Clear compiled Smarty templates
+ */
+ static public function clearCompiledTemplates()
+ {
+ $view = new Piwik_View(null);
+ $view->smarty->clear_compiled_tpl();
+ }
+
+ /**
+ * Render the single report template
+ *
+ * @param string $title Report title
+ * @param string $reportHtml Report body
+ * @param bool $fetch If true, return report contents as a string; else echo to screen
+ * @return string Report contents if $fetch == true
+ */
+ static public function singleReport($title, $reportHtml, $fetch = false)
+ {
+ $view = new Piwik_View('CoreHome/templates/single_report.tpl');
+ $view->title = $title;
+ $view->report = $reportHtml;
+
+ if ($fetch) {
+ return $view->render();
+ }
+ echo $view->render();
+ }
+
+ /**
+ * View factory method
+ *
+ * @param string $templateName Template name (e.g., 'index')
+ * @throws Exception
+ * @return Piwik_View|Piwik_View_OneClickDone
+ */
+ static public function factory($templateName = null)
+ {
+ if ($templateName == self::COREUPDATER_ONE_CLICK_DONE) {
+ return new Piwik_View_OneClickDone(Piwik::getCurrentUserTokenAuth());
+ }
- Piwik_PostEvent('View.getViewType', $viewType);
+ Piwik_PostEvent('View.getViewType', $viewType);
- // get caller
- $bt = @debug_backtrace();
- if($bt === null || !isset($bt[0]))
- {
- throw new Exception("View factory cannot be invoked");
- }
- $path = basename(dirname($bt[0]['file']));
+ // get caller
+ $bt = @debug_backtrace();
+ if ($bt === null || !isset($bt[0])) {
+ throw new Exception("View factory cannot be invoked");
+ }
+ $path = basename(dirname($bt[0]['file']));
- if(Piwik_Common::isPhpCliMode())
- {
- $templateFile = $path.'/templates/cli_'.$templateName.'.tpl';
- if(file_exists( PIWIK_INCLUDE_PATH . '/plugins/' . $templateFile))
- {
- return new Piwik_View($templateFile, array(), false);
+ if (Piwik_Common::isPhpCliMode()) {
+ $templateFile = $path . '/templates/cli_' . $templateName . '.tpl';
+ if (file_exists(PIWIK_INCLUDE_PATH . '/plugins/' . $templateFile)) {
+ return new Piwik_View($templateFile, array(), false);
}
}
- $templateFile = $path.'/templates/'.$templateName.'.tpl';
+ $templateFile = $path . '/templates/' . $templateName . '.tpl';
return new Piwik_View($templateFile);
- }
+ }
}
diff --git a/core/View/Interface.php b/core/View/Interface.php
index c1861a1216..1998ead861 100644
--- a/core/View/Interface.php
+++ b/core/View/Interface.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -16,10 +16,10 @@
*/
interface Piwik_View_Interface
{
- /**
- * Outputs the data.
- *
- * @return mixed (image, array, html...)
- */
- function render();
+ /**
+ * Outputs the data.
+ *
+ * @return mixed (image, array, html...)
+ */
+ function render();
}
diff --git a/core/View/OneClickDone.php b/core/View/OneClickDone.php
index 5ed1111718..bafebb7085 100644
--- a/core/View/OneClickDone.php
+++ b/core/View/OneClickDone.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -23,46 +23,46 @@
*/
class Piwik_View_OneClickDone
{
- /**
- * @var string
- */
- private $tokenAuth;
+ /**
+ * @var string
+ */
+ private $tokenAuth;
- /**
- * @var string
- */
- public $coreError;
+ /**
+ * @var string
+ */
+ public $coreError;
- /**
- * @var array
- */
- public $feedbackMessages;
+ /**
+ * @var array
+ */
+ public $feedbackMessages;
- public function __construct($tokenAuth)
- {
- $this->tokenAuth = $tokenAuth;
- }
+ public function __construct($tokenAuth)
+ {
+ $this->tokenAuth = $tokenAuth;
+ }
- /**
- * Outputs the data.
- *
- * @return string html
- */
- public function render()
- {
- // set response headers
- @header('Content-Type: text/html; charset=UTF-8');
- @header('Pragma: ');
- @header('Expires: ');
- @header('Cache-Control: must-revalidate');
- @header('X-Frame-Options: deny');
+ /**
+ * Outputs the data.
+ *
+ * @return string html
+ */
+ public function render()
+ {
+ // set response headers
+ @header('Content-Type: text/html; charset=UTF-8');
+ @header('Pragma: ');
+ @header('Expires: ');
+ @header('Cache-Control: must-revalidate');
+ @header('X-Frame-Options: deny');
- $error = htmlspecialchars($this->coreError, ENT_QUOTES, 'UTF-8');
- $messages = htmlspecialchars(serialize($this->feedbackMessages), ENT_QUOTES, 'UTF-8');
- $tokenAuth = $this->tokenAuth;
+ $error = htmlspecialchars($this->coreError, ENT_QUOTES, 'UTF-8');
+ $messages = htmlspecialchars(serialize($this->feedbackMessages), ENT_QUOTES, 'UTF-8');
+ $tokenAuth = $this->tokenAuth;
- // use a heredoc instead of an external file
- echo <<<END_OF_TEMPLATE
+ // use a heredoc instead of an external file
+ echo <<<END_OF_TEMPLATE
<!DOCTYPE html>
<html>
<head>
@@ -85,5 +85,5 @@ class Piwik_View_OneClickDone
</body>
</html>
END_OF_TEMPLATE;
- }
+ }
}
diff --git a/core/View/ReportsByDimension.php b/core/View/ReportsByDimension.php
index 3c6919fbb5..e02d428af6 100644
--- a/core/View/ReportsByDimension.php
+++ b/core/View/ReportsByDimension.php
@@ -1,107 +1,104 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package SmartyPlugins
*/
/**
* A facade that makes it easier to use the 'reports_by_dimension.tpl' template.
- *
+ *
* This view will output HTML that displays a list of report names by category and
* loads them by AJAX when clicked. The loaded report is displayed to the right
* of the report listing.
*/
class Piwik_View_ReportsByDimension extends Piwik_View
{
- /**
- * Constructor.
- */
- public function __construct()
- {
- parent::__construct(PIWIK_INCLUDE_PATH.'/plugins/CoreHome/templates/reports_by_dimension.tpl');
- $this->dimensionCategories = array();
- }
-
- /**
- * Adds a report to the list of reports to display.
- *
- * @param string $category The report's category. Can be a i18n token.
- * @param string $title The report's title. Can be a i18n token.
- * @param string $action The controller action used to load the report, ie, Referrers.getAll
- * @param array $params The list of query parameters to use when loading the report.
- * This list overrides query parameters currently in use. For example,
- * array('idSite' => 2, 'viewDataTable' => 'goalsTable')
- * would mean the goals report for site w/ ID=2 will always be loaded.
- */
- public function addReport( $category, $title, $action, $params = array() )
- {
- list($module, $action) = explode('.', $action);
- $params = array('module' => $module, 'action' => $action) + $params;
-
- $categories = $this->dimensionCategories;
- $categories[$category][] = array(
- 'title' => $title,
- 'params' => $params,
- 'url' => Piwik_Url::getCurrentQueryStringWithParametersModified($params)
- );
- $this->dimensionCategories = $categories;
- }
-
- /**
- * Adds a set of reports to the list of reports to display.
- *
- * @param array $reports An array containing report information. The array requires
- * the 'category', 'title', 'action' and 'params' elements.
- * For information on what they should contain, @see addReport.
- */
- public function addReports( $reports )
- {
- foreach ($reports as $report)
- {
- $this->addReport($report['category'], $report['title'], $report['action'], $report['params']);
- }
- }
-
- /**
- * Renders this view.
- *
- * @return string The rendered view.
- */
- public function render()
- {
- $this->firstReport = "";
-
- // if there are reports & report categories added, render the first one so we can
- // display it initially
- $categories = $this->dimensionCategories;
- if (!empty($categories))
- {
- $firstCategory = reset($categories);
- $firstReportInfo = reset($firstCategory);
-
- $oldGet = $_GET;
- $oldPost = $_POST;
-
- foreach ($firstReportInfo['params'] as $key => $value)
- {
- $_GET[$key] = $value;
- }
-
- $_POST = array();
-
- $module = $firstReportInfo['params']['module'];
- $action = $firstReportInfo['params']['action'];
- $this->firstReport = Piwik_FrontController::getInstance()->fetchDispatch($module, $action);
-
- $_GET = $oldGet;
- $_POST = $oldPost;
- }
-
- return parent::render();
- }
+ /**
+ * Constructor.
+ */
+ public function __construct()
+ {
+ parent::__construct(PIWIK_INCLUDE_PATH . '/plugins/CoreHome/templates/reports_by_dimension.tpl');
+ $this->dimensionCategories = array();
+ }
+
+ /**
+ * Adds a report to the list of reports to display.
+ *
+ * @param string $category The report's category. Can be a i18n token.
+ * @param string $title The report's title. Can be a i18n token.
+ * @param string $action The controller action used to load the report, ie, Referrers.getAll
+ * @param array $params The list of query parameters to use when loading the report.
+ * This list overrides query parameters currently in use. For example,
+ * array('idSite' => 2, 'viewDataTable' => 'goalsTable')
+ * would mean the goals report for site w/ ID=2 will always be loaded.
+ */
+ public function addReport($category, $title, $action, $params = array())
+ {
+ list($module, $action) = explode('.', $action);
+ $params = array('module' => $module, 'action' => $action) + $params;
+
+ $categories = $this->dimensionCategories;
+ $categories[$category][] = array(
+ 'title' => $title,
+ 'params' => $params,
+ 'url' => Piwik_Url::getCurrentQueryStringWithParametersModified($params)
+ );
+ $this->dimensionCategories = $categories;
+ }
+
+ /**
+ * Adds a set of reports to the list of reports to display.
+ *
+ * @param array $reports An array containing report information. The array requires
+ * the 'category', 'title', 'action' and 'params' elements.
+ * For information on what they should contain, @see addReport.
+ */
+ public function addReports($reports)
+ {
+ foreach ($reports as $report) {
+ $this->addReport($report['category'], $report['title'], $report['action'], $report['params']);
+ }
+ }
+
+ /**
+ * Renders this view.
+ *
+ * @return string The rendered view.
+ */
+ public function render()
+ {
+ $this->firstReport = "";
+
+ // if there are reports & report categories added, render the first one so we can
+ // display it initially
+ $categories = $this->dimensionCategories;
+ if (!empty($categories)) {
+ $firstCategory = reset($categories);
+ $firstReportInfo = reset($firstCategory);
+
+ $oldGet = $_GET;
+ $oldPost = $_POST;
+
+ foreach ($firstReportInfo['params'] as $key => $value) {
+ $_GET[$key] = $value;
+ }
+
+ $_POST = array();
+
+ $module = $firstReportInfo['params']['module'];
+ $action = $firstReportInfo['params']['action'];
+ $this->firstReport = Piwik_FrontController::getInstance()->fetchDispatch($module, $action);
+
+ $_GET = $oldGet;
+ $_POST = $oldPost;
+ }
+
+ return parent::render();
+ }
}
diff --git a/core/ViewDataTable.php b/core/ViewDataTable.php
index e433b6abf8..bbe27ded80 100644
--- a/core/ViewDataTable.php
+++ b/core/ViewDataTable.php
@@ -19,21 +19,21 @@
* the label in the HTML output.
* - 'html_label_suffix': If this metadata is present on a row, it's contents will be appended
* after the label in the HTML output.
- *
+ *
* Example:
* In the Controller of the plugin VisitorInterest
* <pre>
- * function getNumberOfVisitsPerVisitDuration( $fetch = false)
+ * function getNumberOfVisitsPerVisitDuration( $fetch = false)
* {
- * $view = Piwik_ViewDataTable::factory( 'cloud' );
- * $view->init( $this->pluginName, __FUNCTION__, 'VisitorInterest.getNumberOfVisitsPerVisitDuration' );
- * $view->setColumnsToDisplay( array('label','nb_visits') );
- * $view->disableSort();
- * $view->disableExcludeLowPopulation();
- * $view->disableOffsetInformation();
+ * $view = Piwik_ViewDataTable::factory( 'cloud' );
+ * $view->init( $this->pluginName, __FUNCTION__, 'VisitorInterest.getNumberOfVisitsPerVisitDuration' );
+ * $view->setColumnsToDisplay( array('label','nb_visits') );
+ * $view->disableSort();
+ * $view->disableExcludeLowPopulation();
+ * $view->disableOffsetInformation();
*
- * return $this->renderView($view, $fetch);
- * }
+ * return $this->renderView($view, $fetch);
+ * }
* </pre>
*
* @see factory() for all the available output (cloud tags, html table, pie chart, vertical bar chart)
@@ -42,1486 +42,1405 @@
*/
abstract class Piwik_ViewDataTable
{
- /**
- * Template file that will be loaded for this view.
- * Usually set in the Piwik_ViewDataTable_*
- *
- * @var string eg. 'CoreHome/templates/cloud.tpl'
- */
- protected $dataTableTemplate = null;
-
- /**
- * Flag used to make sure the main() is only executed once
- *
- * @var bool
- */
- protected $mainAlreadyExecuted = false;
-
- /**
- * Contains the values set for the parameters
- *
- * @see getJavascriptVariablesToSet()
- * @var array
- */
- protected $variablesDefault = array();
-
- /**
- * Array of properties that are available in the view (from smarty)
- * Used to store UI properties, eg. "show_footer", "show_search", etc.
- *
- * @var array
- */
- protected $viewProperties = array();
-
- /**
- * If the current dataTable refers to a subDataTable (eg. keywordsBySearchEngineId for id=X) this variable is set to the Id
- *
- * @var bool|int
- */
- protected $idSubtable = false;
-
- /**
- * DataTable loaded from the API for this ViewDataTable.
- *
- * @var Piwik_DataTable
- */
- protected $dataTable = null;
-
-
- /**
- * List of filters to apply after the data has been loaded from the API
- *
- * @var array
- */
- protected $queuedFilters = array();
-
- /**
- * List of filter to apply just before the 'Generic' filters
- * These filters should delete rows from the table
- * @var array
- */
- protected $queuedFiltersPriority = array();
-
- /**
- * @see init()
- * @var string
- */
- protected $currentControllerAction;
-
- /**
- * @see init()
- * @var string
- */
- protected $currentControllerName;
-
- /**
- * @see init()
- * @var string
- */
- protected $controllerActionCalledWhenRequestSubTable = null;
-
- /**
- * @see init()
- * @var string
- */
- protected $apiMethodToRequestDataTable;
-
- /**
- * This view should be an implementation of the Interface Piwik_View_Interface
- * The $view object should be created in the main() method.
- *
- * @var Piwik_View_Interface
- */
- protected $view = null;
-
- /**
- * Array of columns names translations
- *
- * @var array
- */
- protected $columnsTranslations = array();
-
- /**
- * Documentation for the metrics used in the current report.
- * Received from the Plugin API, used for inline help.
- *
- * @var array
- */
- protected $metricsDocumentation = false;
-
- /**
- * Documentation for the report.
- * Received from the Plugin API, used for inline help.
- *
- * @var array
- */
- protected $documentation = false;
-
- /**
- * Array of columns set to display
- *
- * @var array
- */
- protected $columnsToDisplay = array();
-
- /**
- * Variable that is used as the DIV ID in the rendered HTML
- *
- * @var string
- */
- protected $uniqIdTable = null;
-
- /**
- * Method to be implemented by the ViewDataTable_*.
- * This method should create and initialize a $this->view object @see Piwik_View_Interface
- *
- * @return mixed either prints the result or returns the output string
- */
- abstract public function main();
-
- /**
- * Unique string ID that defines the format of the dataTable, eg. "pieChart", "table", etc.
- *
- * @return string
- */
- abstract protected function getViewDataTableId();
-
- /**
- * Returns a Piwik_ViewDataTable_* object.
- * By default it will return a ViewDataTable_Html
- * If there is a viewDataTable parameter in the URL, a ViewDataTable of this 'viewDataTable' type will be returned.
- * If defaultType is specified and if there is no 'viewDataTable' in the URL, a ViewDataTable of this $defaultType will be returned.
- * If force is set to true, a ViewDataTable of the $defaultType will be returned in all cases.
- *
- * @param string $defaultType Any of these: table, cloud, graphPie, graphVerticalBar, graphEvolution, sparkline, generateDataChart*
- * @param bool $force If set to true, returns a ViewDataTable of the $defaultType
- * @return Piwik_ViewDataTable
- */
- static public function factory( $defaultType = null, $force = false)
- {
- if(is_null($defaultType))
- {
- $defaultType = 'table';
- }
-
- if($force === true)
- {
- $type = $defaultType;
- }
- else
- {
- $type = Piwik_Common::getRequestVar('viewDataTable', $defaultType, 'string');
- }
- switch($type)
- {
- case 'cloud':
- return new Piwik_ViewDataTable_Cloud();
- break;
-
- case 'graphPie':
- return new Piwik_ViewDataTable_GenerateGraphHTML_ChartPie();
- break;
-
- case 'graphVerticalBar':
- return new Piwik_ViewDataTable_GenerateGraphHTML_ChartVerticalBar();
- break;
-
- case 'graphEvolution':
- return new Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution();
- break;
-
- case 'sparkline':
- return new Piwik_ViewDataTable_Sparkline();
- break;
-
- case 'generateDataChartVerticalBar':
- return new Piwik_ViewDataTable_GenerateGraphData_ChartVerticalBar();
- break;
-
- case 'generateDataChartPie':
- return new Piwik_ViewDataTable_GenerateGraphData_ChartPie();
- break;
-
- case 'generateDataChartEvolution':
- return new Piwik_ViewDataTable_GenerateGraphData_ChartEvolution();
- break;
-
- case 'tableAllColumns':
- return new Piwik_ViewDataTable_HtmlTable_AllColumns();
- break;
-
- case 'tableGoals':
- return new Piwik_ViewDataTable_HtmlTable_Goals();
- break;
-
- case 'table':
- default:
- return new Piwik_ViewDataTable_HtmlTable();
- break;
- }
- }
-
- /**
- * Inits the object given the $currentControllerName, $currentControllerAction of
- * the calling controller action, eg. 'Referers' 'getLongListOfKeywords'.
- * The initialization also requires the $apiMethodToRequestDataTable of the API method
- * to call in order to get the DataTable, eg. 'Referers.getKeywords'.
- * The optional $controllerActionCalledWhenRequestSubTable defines the method name of the API to call when there is a idSubtable.
- * This value would be used by the javascript code building the GET request to the API.
- *
- * Example:
- * For the keywords listing, a click on the row loads the subTable of the Search Engines for this row.
- * In this case $controllerActionCalledWhenRequestSubTable = 'getSearchEnginesFromKeywordId'.
- * The GET request will hit 'Referers.getSearchEnginesFromKeywordId'.
- *
- * @param string $currentControllerName eg. 'Referers'
- * @param string $currentControllerAction eg. 'getKeywords'
- * @param string $apiMethodToRequestDataTable eg. 'Referers.getKeywords'
- * @param string $controllerActionCalledWhenRequestSubTable eg. 'getSearchEnginesFromKeywordId'
- */
- public function init( $currentControllerName,
- $currentControllerAction,
- $apiMethodToRequestDataTable,
- $controllerActionCalledWhenRequestSubTable = null)
- {
- $this->currentControllerName = $currentControllerName;
- $this->currentControllerAction = $currentControllerAction;
- $this->apiMethodToRequestDataTable = $apiMethodToRequestDataTable;
- $this->controllerActionCalledWhenRequestSubTable = $controllerActionCalledWhenRequestSubTable;
- $this->idSubtable = Piwik_Common::getRequestVar('idSubtable', false, 'int');
-
- $this->viewProperties['show_goals'] = false;
- $this->viewProperties['show_ecommerce'] = false;
- $this->viewProperties['show_search'] = Piwik_Common::getRequestVar('show_search', true);
- $this->viewProperties['show_table'] = Piwik_Common::getRequestVar('show_table', true);
- $this->viewProperties['show_table_all_columns'] = Piwik_Common::getRequestVar('show_table_all_columns', true);
- $this->viewProperties['show_all_views_icons'] = Piwik_Common::getRequestVar('show_all_views_icons', true);
- $this->viewProperties['hide_all_views_icons'] = Piwik_Common::getRequestVar('hide_all_views_icons', false);
- $this->viewProperties['hide_annotations_view'] = Piwik_Common::getRequestVar('hide_annotations_view', true);
- $this->viewProperties['show_bar_chart'] = Piwik_Common::getRequestVar('show_barchart', true);
- $this->viewProperties['show_pie_chart'] = Piwik_Common::getRequestVar('show_piechart', true);
- $this->viewProperties['show_tag_cloud'] = Piwik_Common::getRequestVar('show_tag_cloud', true);
- $this->viewProperties['show_export_as_image_icon'] = Piwik_Common::getRequestVar('show_export_as_image_icon', false);
- $this->viewProperties['show_export_as_rss_feed'] = Piwik_Common::getRequestVar('show_export_as_rss_feed', true);
- $this->viewProperties['show_exclude_low_population'] = Piwik_Common::getRequestVar('show_exclude_low_population', true);
- $this->viewProperties['show_offset_information'] = Piwik_Common::getRequestVar('show_offset_information', true);
- $this->viewProperties['show_pagination_control'] = Piwik_Common::getRequestVar('show_pagination_control', true);
- $this->viewProperties['show_limit_control'] = false;
- $this->viewProperties['show_footer'] = Piwik_Common::getRequestVar('show_footer', true);
- $this->viewProperties['show_footer_icons'] = ($this->idSubtable == false);
- $this->viewProperties['show_related_reports'] = Piwik_Common::getRequestVar('show_related_reports', true);
- $this->viewProperties['apiMethodToRequestDataTable'] = $this->apiMethodToRequestDataTable;
- $this->viewProperties['uniqueId'] = $this->getUniqueIdViewDataTable();
- $this->viewProperties['exportLimit'] = Piwik_Config::getInstance()->General['API_datatable_default_limit'];
-
- $this->viewProperties['highlight_summary_row'] = false;
- $this->viewProperties['metadata'] = array();
-
- $this->viewProperties['relatedReports'] = array();
- $this->viewProperties['title'] = 'unknown';
- $this->viewProperties['self_url'] = $this->getBaseReportUrl($currentControllerName, $currentControllerAction);
- $this->viewProperties['tooltip_metadata_name'] = false;
-
- $standardColumnNameToTranslation = array_merge(
- Piwik_API_API::getInstance()->getDefaultMetrics(),
- Piwik_API_API::getInstance()->getDefaultProcessedMetrics()
- );
- $this->setColumnsTranslations($standardColumnNameToTranslation);
- }
-
- /**
- * Forces the View to use a given template.
- * Usually the template to use is set in the specific ViewDataTable_*
- * eg. 'CoreHome/templates/cloud.tpl'
- * But some users may want to force this template to some other value
- *
- * @param string $tpl eg .'MyPlugin/templates/templateToUse.tpl'
- */
- public function setTemplate( $tpl )
- {
- $this->dataTableTemplate = $tpl;
- }
-
- /**
- * Returns the View_Interface.
- * You can then call render() on this object.
- *
- * @return Piwik_View_Interface
- * @throws exception if the view object was not created
- */
- public function getView()
- {
- if(is_null($this->view))
- {
- throw new Exception('The $this->view object has not been created.
+ /**
+ * Template file that will be loaded for this view.
+ * Usually set in the Piwik_ViewDataTable_*
+ *
+ * @var string eg. 'CoreHome/templates/cloud.tpl'
+ */
+ protected $dataTableTemplate = null;
+
+ /**
+ * Flag used to make sure the main() is only executed once
+ *
+ * @var bool
+ */
+ protected $mainAlreadyExecuted = false;
+
+ /**
+ * Contains the values set for the parameters
+ *
+ * @see getJavascriptVariablesToSet()
+ * @var array
+ */
+ protected $variablesDefault = array();
+
+ /**
+ * Array of properties that are available in the view (from smarty)
+ * Used to store UI properties, eg. "show_footer", "show_search", etc.
+ *
+ * @var array
+ */
+ protected $viewProperties = array();
+
+ /**
+ * If the current dataTable refers to a subDataTable (eg. keywordsBySearchEngineId for id=X) this variable is set to the Id
+ *
+ * @var bool|int
+ */
+ protected $idSubtable = false;
+
+ /**
+ * DataTable loaded from the API for this ViewDataTable.
+ *
+ * @var Piwik_DataTable
+ */
+ protected $dataTable = null;
+
+
+ /**
+ * List of filters to apply after the data has been loaded from the API
+ *
+ * @var array
+ */
+ protected $queuedFilters = array();
+
+ /**
+ * List of filter to apply just before the 'Generic' filters
+ * These filters should delete rows from the table
+ * @var array
+ */
+ protected $queuedFiltersPriority = array();
+
+ /**
+ * @see init()
+ * @var string
+ */
+ protected $currentControllerAction;
+
+ /**
+ * @see init()
+ * @var string
+ */
+ protected $currentControllerName;
+
+ /**
+ * @see init()
+ * @var string
+ */
+ protected $controllerActionCalledWhenRequestSubTable = null;
+
+ /**
+ * @see init()
+ * @var string
+ */
+ protected $apiMethodToRequestDataTable;
+
+ /**
+ * This view should be an implementation of the Interface Piwik_View_Interface
+ * The $view object should be created in the main() method.
+ *
+ * @var Piwik_View_Interface
+ */
+ protected $view = null;
+
+ /**
+ * Array of columns names translations
+ *
+ * @var array
+ */
+ protected $columnsTranslations = array();
+
+ /**
+ * Documentation for the metrics used in the current report.
+ * Received from the Plugin API, used for inline help.
+ *
+ * @var array
+ */
+ protected $metricsDocumentation = false;
+
+ /**
+ * Documentation for the report.
+ * Received from the Plugin API, used for inline help.
+ *
+ * @var array
+ */
+ protected $documentation = false;
+
+ /**
+ * Array of columns set to display
+ *
+ * @var array
+ */
+ protected $columnsToDisplay = array();
+
+ /**
+ * Variable that is used as the DIV ID in the rendered HTML
+ *
+ * @var string
+ */
+ protected $uniqIdTable = null;
+
+ /**
+ * Method to be implemented by the ViewDataTable_*.
+ * This method should create and initialize a $this->view object @see Piwik_View_Interface
+ *
+ * @return mixed either prints the result or returns the output string
+ */
+ abstract public function main();
+
+ /**
+ * Unique string ID that defines the format of the dataTable, eg. "pieChart", "table", etc.
+ *
+ * @return string
+ */
+ abstract protected function getViewDataTableId();
+
+ /**
+ * Returns a Piwik_ViewDataTable_* object.
+ * By default it will return a ViewDataTable_Html
+ * If there is a viewDataTable parameter in the URL, a ViewDataTable of this 'viewDataTable' type will be returned.
+ * If defaultType is specified and if there is no 'viewDataTable' in the URL, a ViewDataTable of this $defaultType will be returned.
+ * If force is set to true, a ViewDataTable of the $defaultType will be returned in all cases.
+ *
+ * @param string $defaultType Any of these: table, cloud, graphPie, graphVerticalBar, graphEvolution, sparkline, generateDataChart*
+ * @param bool $force If set to true, returns a ViewDataTable of the $defaultType
+ * @return Piwik_ViewDataTable
+ */
+ static public function factory($defaultType = null, $force = false)
+ {
+ if (is_null($defaultType)) {
+ $defaultType = 'table';
+ }
+
+ if ($force === true) {
+ $type = $defaultType;
+ } else {
+ $type = Piwik_Common::getRequestVar('viewDataTable', $defaultType, 'string');
+ }
+ switch ($type) {
+ case 'cloud':
+ return new Piwik_ViewDataTable_Cloud();
+ break;
+
+ case 'graphPie':
+ return new Piwik_ViewDataTable_GenerateGraphHTML_ChartPie();
+ break;
+
+ case 'graphVerticalBar':
+ return new Piwik_ViewDataTable_GenerateGraphHTML_ChartVerticalBar();
+ break;
+
+ case 'graphEvolution':
+ return new Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution();
+ break;
+
+ case 'sparkline':
+ return new Piwik_ViewDataTable_Sparkline();
+ break;
+
+ case 'generateDataChartVerticalBar':
+ return new Piwik_ViewDataTable_GenerateGraphData_ChartVerticalBar();
+ break;
+
+ case 'generateDataChartPie':
+ return new Piwik_ViewDataTable_GenerateGraphData_ChartPie();
+ break;
+
+ case 'generateDataChartEvolution':
+ return new Piwik_ViewDataTable_GenerateGraphData_ChartEvolution();
+ break;
+
+ case 'tableAllColumns':
+ return new Piwik_ViewDataTable_HtmlTable_AllColumns();
+ break;
+
+ case 'tableGoals':
+ return new Piwik_ViewDataTable_HtmlTable_Goals();
+ break;
+
+ case 'table':
+ default:
+ return new Piwik_ViewDataTable_HtmlTable();
+ break;
+ }
+ }
+
+ /**
+ * Inits the object given the $currentControllerName, $currentControllerAction of
+ * the calling controller action, eg. 'Referers' 'getLongListOfKeywords'.
+ * The initialization also requires the $apiMethodToRequestDataTable of the API method
+ * to call in order to get the DataTable, eg. 'Referers.getKeywords'.
+ * The optional $controllerActionCalledWhenRequestSubTable defines the method name of the API to call when there is a idSubtable.
+ * This value would be used by the javascript code building the GET request to the API.
+ *
+ * Example:
+ * For the keywords listing, a click on the row loads the subTable of the Search Engines for this row.
+ * In this case $controllerActionCalledWhenRequestSubTable = 'getSearchEnginesFromKeywordId'.
+ * The GET request will hit 'Referers.getSearchEnginesFromKeywordId'.
+ *
+ * @param string $currentControllerName eg. 'Referers'
+ * @param string $currentControllerAction eg. 'getKeywords'
+ * @param string $apiMethodToRequestDataTable eg. 'Referers.getKeywords'
+ * @param string $controllerActionCalledWhenRequestSubTable eg. 'getSearchEnginesFromKeywordId'
+ */
+ public function init($currentControllerName,
+ $currentControllerAction,
+ $apiMethodToRequestDataTable,
+ $controllerActionCalledWhenRequestSubTable = null)
+ {
+ $this->currentControllerName = $currentControllerName;
+ $this->currentControllerAction = $currentControllerAction;
+ $this->apiMethodToRequestDataTable = $apiMethodToRequestDataTable;
+ $this->controllerActionCalledWhenRequestSubTable = $controllerActionCalledWhenRequestSubTable;
+ $this->idSubtable = Piwik_Common::getRequestVar('idSubtable', false, 'int');
+
+ $this->viewProperties['show_goals'] = false;
+ $this->viewProperties['show_ecommerce'] = false;
+ $this->viewProperties['show_search'] = Piwik_Common::getRequestVar('show_search', true);
+ $this->viewProperties['show_table'] = Piwik_Common::getRequestVar('show_table', true);
+ $this->viewProperties['show_table_all_columns'] = Piwik_Common::getRequestVar('show_table_all_columns', true);
+ $this->viewProperties['show_all_views_icons'] = Piwik_Common::getRequestVar('show_all_views_icons', true);
+ $this->viewProperties['hide_all_views_icons'] = Piwik_Common::getRequestVar('hide_all_views_icons', false);
+ $this->viewProperties['hide_annotations_view'] = Piwik_Common::getRequestVar('hide_annotations_view', true);
+ $this->viewProperties['show_bar_chart'] = Piwik_Common::getRequestVar('show_barchart', true);
+ $this->viewProperties['show_pie_chart'] = Piwik_Common::getRequestVar('show_piechart', true);
+ $this->viewProperties['show_tag_cloud'] = Piwik_Common::getRequestVar('show_tag_cloud', true);
+ $this->viewProperties['show_export_as_image_icon'] = Piwik_Common::getRequestVar('show_export_as_image_icon', false);
+ $this->viewProperties['show_export_as_rss_feed'] = Piwik_Common::getRequestVar('show_export_as_rss_feed', true);
+ $this->viewProperties['show_exclude_low_population'] = Piwik_Common::getRequestVar('show_exclude_low_population', true);
+ $this->viewProperties['show_offset_information'] = Piwik_Common::getRequestVar('show_offset_information', true);
+ $this->viewProperties['show_pagination_control'] = Piwik_Common::getRequestVar('show_pagination_control', true);
+ $this->viewProperties['show_limit_control'] = false;
+ $this->viewProperties['show_footer'] = Piwik_Common::getRequestVar('show_footer', true);
+ $this->viewProperties['show_footer_icons'] = ($this->idSubtable == false);
+ $this->viewProperties['show_related_reports'] = Piwik_Common::getRequestVar('show_related_reports', true);
+ $this->viewProperties['apiMethodToRequestDataTable'] = $this->apiMethodToRequestDataTable;
+ $this->viewProperties['uniqueId'] = $this->getUniqueIdViewDataTable();
+ $this->viewProperties['exportLimit'] = Piwik_Config::getInstance()->General['API_datatable_default_limit'];
+
+ $this->viewProperties['highlight_summary_row'] = false;
+ $this->viewProperties['metadata'] = array();
+
+ $this->viewProperties['relatedReports'] = array();
+ $this->viewProperties['title'] = 'unknown';
+ $this->viewProperties['self_url'] = $this->getBaseReportUrl($currentControllerName, $currentControllerAction);
+ $this->viewProperties['tooltip_metadata_name'] = false;
+
+ $standardColumnNameToTranslation = array_merge(
+ Piwik_API_API::getInstance()->getDefaultMetrics(),
+ Piwik_API_API::getInstance()->getDefaultProcessedMetrics()
+ );
+ $this->setColumnsTranslations($standardColumnNameToTranslation);
+ }
+
+ /**
+ * Forces the View to use a given template.
+ * Usually the template to use is set in the specific ViewDataTable_*
+ * eg. 'CoreHome/templates/cloud.tpl'
+ * But some users may want to force this template to some other value
+ *
+ * @param string $tpl eg .'MyPlugin/templates/templateToUse.tpl'
+ */
+ public function setTemplate($tpl)
+ {
+ $this->dataTableTemplate = $tpl;
+ }
+
+ /**
+ * Returns the View_Interface.
+ * You can then call render() on this object.
+ *
+ * @return Piwik_View_Interface
+ * @throws exception if the view object was not created
+ */
+ public function getView()
+ {
+ if (is_null($this->view)) {
+ throw new Exception('The $this->view object has not been created.
It should be created in the main() method of the Piwik_ViewDataTable_* subclass you are using.');
- }
- return $this->view;
- }
-
- public function getCurrentControllerAction()
- {
- return $this->currentControllerAction;
- }
-
- public function getCurrentControllerName()
- {
- return $this->currentControllerName;
- }
-
- public function getApiMethodToRequestDataTable()
- {
- return $this->apiMethodToRequestDataTable;
- }
-
- public function getControllerActionCalledWhenRequestSubTable()
- {
- return $this->controllerActionCalledWhenRequestSubTable;
- }
-
- /**
- * Returns the DataTable loaded from the API
- *
- * @return Piwik_DataTable
- * @throws exception if not yet defined
- */
- public function getDataTable()
- {
- if(is_null($this->dataTable))
- {
- throw new Exception("The DataTable object has not yet been created");
- }
- return $this->dataTable;
- }
-
- /**
- * To prevent calling an API multiple times, the DataTable can be set directly.
- * It won't be loaded again from the API in this case
- *
- * @param $dataTable
- * @return void $dataTable Piwik_DataTable
- */
- public function setDataTable($dataTable)
- {
- $this->dataTable = $dataTable;
- }
-
- /**
- * Function called by the ViewDataTable objects in order to fetch data from the API.
- * The function init() must have been called before, so that the object knows which API module and action to call.
- * It builds the API request string and uses Piwik_API_Request to call the API.
- * The requested Piwik_DataTable object is stored in $this->dataTable.
- */
- protected function loadDataTableFromAPI()
- {
- if(!is_null($this->dataTable))
- {
- // data table is already there
- // this happens when setDataTable has been used
- return;
- }
-
- // we build the request string (URL) to call the API
- $requestString = $this->getRequestString();
-
- // we make the request to the API
- $request = new Piwik_API_Request($requestString);
-
- // and get the DataTable structure
- $dataTable = $request->process();
-
- $this->dataTable = $dataTable;
- }
-
- /**
- * Checks that the API returned a normal DataTable (as opposed to DataTable_Array)
- * @throws Exception
- * @return void
- */
- protected function checkStandardDataTable()
- {
- Piwik::checkObjectTypeIs($this->dataTable, array('Piwik_DataTable'));
- }
-
- /**
- * Hook called after the dataTable has been loaded from the API
- * Can be used to add, delete or modify the data freshly loaded
- *
- * @return bool
- */
- protected function postDataTableLoadedFromAPI()
- {
- if(empty($this->dataTable))
- {
- return false;
- }
-
- // deal w/ table metadata
- if ($this->dataTable instanceof Piwik_DataTable)
- {
- $this->viewProperties['metadata'] = $this->dataTable->getAllTableMetadata();
-
- if (isset($this->viewProperties['metadata'][Piwik_DataTable::ARCHIVED_DATE_METADATA_NAME]))
- {
- $this->viewProperties['metadata'][Piwik_DataTable::ARCHIVED_DATE_METADATA_NAME] =
- $this->makePrettyArchivedOnText();
- }
- }
-
- // First, filters that delete rows
- foreach($this->queuedFiltersPriority as $filter)
- {
- $filterName = $filter[0];
- $filterParameters = $filter[1];
- $this->dataTable->filter($filterName, $filterParameters);
- }
-
- if (!$this->areGenericFiltersDisabled())
- {
- // Second, generic filters (Sort, Limit, Replace Column Names, etc.)
- $requestString = $this->getRequestString();
- $request = Piwik_API_Request::getRequestArrayFromString($requestString);
-
- if(!empty($this->variablesDefault['enable_sort'])
- && $this->variablesDefault['enable_sort'] === 'false')
- {
- $request['filter_sort_column'] = $request['filter_sort_order'] = '';
- }
-
- $genericFilter = new Piwik_API_DataTableGenericFilter($request);
- $genericFilter->filter($this->dataTable);
- }
-
- if (!$this->areQueuedFiltersDisabled())
- {
- // Finally, apply datatable filters that were queued (should be 'presentation' filters that
- // do not affect the number of rows)
- foreach($this->queuedFilters as $filter)
- {
- $filterName = $filter[0];
- $filterParameters = $filter[1];
- $this->dataTable->filter($filterName, $filterParameters);
- }
- }
- return true;
- }
-
- /**
- * Returns true if generic filters have been disabled, false if otherwise.
- *
- * @return bool
- */
- private function areGenericFiltersDisabled()
- {
- // if disable_generic_filters query param is set to '1', generic filters are disabled
- if (Piwik_Common::getRequestVar('disable_generic_filters', '0', 'string') == 1)
- {
- return true;
- }
-
- // if $this->disableGenericFilters() was called, generic filters are disabled
- if (isset($this->variablesDefault['disable_generic_filters'])
- && $this->variablesDefault['disable_generic_filters'] === true)
- {
- return true;
- }
-
- return false;
- }
-
- /**
- * Returns true if queued filters have been disabled, false if otherwise.
- *
- * @return bool
- */
- private function areQueuedFiltersDisabled()
- {
- return isset($this->variablesDefault['disable_queued_filters'])
- && $this->variablesDefault['disable_queued_filters'];
- }
-
- /**
- * Returns prettified and translated text that describes when a report was last updated.
- *
- * @return string
- */
- private function makePrettyArchivedOnText()
- {
- $dateText = $this->viewProperties['metadata'][Piwik_DataTable::ARCHIVED_DATE_METADATA_NAME];
- $date = Piwik_Date::factory($dateText);
- $today = mktime(0,0,0);
- if ($date->getTimestamp() > $today)
- {
- $elapsedSeconds = time() - $date->getTimestamp();
- $timeAgo = Piwik::getPrettyTimeFromSeconds( $elapsedSeconds );
-
- return Piwik_Translate('CoreHome_ReportGeneratedXAgo', $timeAgo);
- }
-
- $prettyDate = $date->getLocalized("%longYear%, %longMonth% %day%") . $date->toString('S');
- return Piwik_Translate('CoreHome_ReportGeneratedOn', $prettyDate);
- }
-
- /**
- * @return string URL to call the API, eg. "method=Referers.getKeywords&period=day&date=yesterday"...
- */
- protected function getRequestString()
- {
- // we prepare the string to give to the API Request
- // we setup the method and format variable
- // - we request the method to call to get this specific DataTable
- // - the format = original specifies that we want to get the original DataTable structure itself, not rendered
- $requestString = 'method='.$this->apiMethodToRequestDataTable;
- $requestString .= '&format=original';
- $requestString .= '&disable_generic_filters='.Piwik_Common::getRequestVar('disable_generic_filters', 1, 'int');
-
- $toSetEventually = array(
- 'filter_limit',
- 'keep_summary_row',
- 'filter_sort_column',
- 'filter_sort_order',
- 'filter_excludelowpop',
- 'filter_excludelowpop_value',
- 'filter_column',
- 'filter_pattern',
- 'disable_queued_filters',
- );
-
- foreach($toSetEventually as $varToSet)
- {
- $value = $this->getDefaultOrCurrent($varToSet);
- if( false !== $value )
- {
- if( is_array($value) )
- {
- foreach($value as $v)
- {
- $requestString .= "&".$varToSet.'[]='.$v;
- }
- }
- else
- {
- $requestString .= '&'.$varToSet.'='.$value;
- }
- }
- }
- return $requestString;
- }
-
- /**
- * For convenience, the client code can call methods that are defined in a specific children class
- * without testing the children class type, which would trigger an error with a different children class.
- *
- * Example:
- * ViewDataTable/Html.php defines a setColumnsToDisplay(). The client code calls this methods even if
- * the ViewDataTable object is a ViewDataTable_Cloud instance (he doesn't know because of the factory()).
- * But ViewDataTable_Cloud doesn't define the setColumnsToDisplay() method.
- * Because we don't want to force users to test for the object type we simply catch these
- * calls when they are not defined in the child and do nothing.
- *
- * @param string $function
- * @param array $args
- */
- public function __call($function, $args)
- {
- }
-
- /**
- * Returns a unique ID for this ViewDataTable.
- * This unique ID is used in the Javascript code:
- * Any ajax loaded data is loaded within a DIV that has id=$unique_id
- * The jquery code then replaces the existing html div id=$unique_id in the code with this data.
- *
- * @see datatable.js
- * @return string
- */
- protected function loadUniqueIdViewDataTable()
- {
- // if we request a subDataTable the $this->currentControllerAction DIV ID is already there in the page
- // we make the DIV ID really unique by appending the ID of the subtable requested
- if( $this->idSubtable != 0 // parent DIV has a idSubtable = 0 but the html DIV must have the name of the module.action
- && $this->idSubtable !== false // case there is no idSubtable
- )
- {
- // see also datatable.js (the ID has to match with the html ID created to be replaced by the result of the ajax call)
- $uniqIdTable = 'subDataTable_' . $this->idSubtable;
- }
- else
- {
- // the $uniqIdTable variable is used as the DIV ID in the rendered HTML
- // we use the current Controller action name as it is supposed to be unique in the rendered page
- $uniqIdTable = $this->currentControllerName . $this->currentControllerAction;
- }
- return $uniqIdTable;
- }
-
- /**
- * Sets the $uniqIdTable variable that is used as the DIV ID in the rendered HTML
- * @param $uniqIdTable
- */
- public function setUniqueIdViewDataTable($uniqIdTable)
- {
- $this->viewProperties['uniqueId'] = $uniqIdTable;
- $this->uniqIdTable = $uniqIdTable;
- }
-
- /**
- * Returns current value of $uniqIdTable variable that is used as the DIV ID in the rendered HTML
- * @return null|string
- */
- public function getUniqueIdViewDataTable()
- {
- if( $this->uniqIdTable == null )
- {
- $this->uniqIdTable = $this->loadUniqueIdViewDataTable();
- }
- return $this->uniqIdTable;
- }
-
- /**
- * Returns array of properties, eg. "show_footer", "show_search", etc.
- *
- * @return array of boolean
- */
- protected function getViewProperties()
- {
- return $this->viewProperties;
- }
-
- /**
- * This functions reads the customization values for the DataTable and returns an array (name,value) to be printed in Javascript.
- * This array defines things such as:
- * - name of the module & action to call to request data for this table
- * - optional filters information, eg. filter_limit and filter_offset
- * - etc.
- *
- * The values are loaded:
- * - from the generic filters that are applied by default @see Piwik_API_DataTableGenericFilter.php::getGenericFiltersInformation()
- * - from the values already available in the GET array
- * - from the values set using methods from this class (eg. setSearchPattern(), setLimit(), etc.)
- *
- * @return array eg. array('show_offset_information' => 0, 'show_...
- */
- protected function getJavascriptVariablesToSet()
- {
- // build javascript variables to set
- $javascriptVariablesToSet = array();
-
- $genericFilters = Piwik_API_DataTableGenericFilter::getGenericFiltersInformation();
- foreach($genericFilters as $filter)
- {
- foreach($filter as $filterVariableName => $filterInfo)
- {
- // if there is a default value for this filter variable we set it
- // so that it is propagated to the javascript
- if(isset($filterInfo[1]))
- {
- $javascriptVariablesToSet[$filterVariableName] = $filterInfo[1];
-
- // we set the default specified column and Order to sort by
- // when this javascript variable is not set already
- // for example during an AJAX call this variable will be set in the URL
- // so this will not be executed (and the default sorted not be used as the sorted column might have changed in the meanwhile)
- if( false !== ($defaultValue = $this->getDefault($filterVariableName)))
- {
- $javascriptVariablesToSet[$filterVariableName] = $defaultValue;
- }
- }
- }
- }
-
- foreach($_GET as $name => $value)
- {
- try {
- $requestValue = Piwik_Common::getRequestVar($name);
- }
- catch(Exception $e) {
- $requestValue = '';
- }
- $javascriptVariablesToSet[$name] = $requestValue;
- }
-
- // at this point there are some filters values we may have not set,
- // case of the filter without default values and parameters set directly in this class
- // for example setExcludeLowPopulation
- // we go through all the $this->variablesDefault array and set the variables not set yet
- foreach($this->variablesDefault as $name => $value)
- {
- if(!isset($javascriptVariablesToSet[$name] ))
- {
- $javascriptVariablesToSet[$name] = $value;
- }
- }
-
- if($this->dataTable instanceof Piwik_DataTable)
- {
- // we override the filter_sort_column with the column used for sorting,
- // which can be different from the one specified (eg. if the column doesn't exist)
- $javascriptVariablesToSet['filter_sort_column'] = $this->dataTable->getSortedByColumnName();
- // datatable can return "2" but we want to write "nb_visits" in the js
- if(isset(Piwik_Archive::$mappingFromIdToName[$javascriptVariablesToSet['filter_sort_column']]))
- {
- $javascriptVariablesToSet['filter_sort_column'] = Piwik_Archive::$mappingFromIdToName[$javascriptVariablesToSet['filter_sort_column']];
- }
- }
-
- $javascriptVariablesToSet['module'] = $this->currentControllerName;
- $javascriptVariablesToSet['action'] = $this->currentControllerAction;
- $javascriptVariablesToSet['viewDataTable'] = $this->getViewDataTableId();
- $javascriptVariablesToSet['controllerActionCalledWhenRequestSubTable'] = $this->controllerActionCalledWhenRequestSubTable;
-
- if($this->dataTable &&
- // Piwik_DataTable_Array doesn't have the method
- !($this->dataTable instanceof Piwik_DataTable_Array)
- && empty($javascriptVariablesToSet['totalRows'])
- )
- {
- $javascriptVariablesToSet['totalRows'] = $this->dataTable->getRowsCountBeforeLimitFilter();
- }
-
- // we escape the values that will be displayed in the javascript footer of each datatable
- // to make sure there is no malicious code injected (the value are already htmlspecialchar'ed as they
- // are loaded with Piwik_Common::getRequestVar()
- foreach($javascriptVariablesToSet as &$value)
- {
- if(is_array($value))
- {
- $value = array_map('addslashes',$value);
- }
- else
- {
- $value = addslashes($value);
- }
- }
-
- $deleteFromJavascriptVariables = array(
- 'filter_excludelowpop',
- 'filter_excludelowpop_value',
- );
- foreach($deleteFromJavascriptVariables as $name)
- {
- if(isset($javascriptVariablesToSet[$name]))
- {
- unset($javascriptVariablesToSet[$name]);
- }
- }
- return $javascriptVariablesToSet;
- }
-
- /**
- * Returns, for a given parameter, the value of this parameter in the REQUEST array.
- * If not set, returns the default value for this parameter @see getDefault()
- *
- * @param string $nameVar
- * @return string|mixed Value of this parameter
- */
- protected function getDefaultOrCurrent( $nameVar )
- {
- if(isset($_GET[$nameVar]))
- {
- return Piwik_Common::sanitizeInputValue($_GET[$nameVar]);
- }
- $default = $this->getDefault($nameVar);
- return $default;
- }
-
- /**
- * Returns the default value for a given parameter.
- * For example, these default values can be set using the disable* methods.
- *
- * @param string $nameVar
- * @return mixed
- */
- protected function getDefault($nameVar)
- {
- if(!isset($this->variablesDefault[$nameVar]))
- {
- return false;
- }
- return $this->variablesDefault[$nameVar];
- }
-
- /**
- * The generic filters (limit, offset, sort by visit desc) will not be applied to this datatable.
- */
- public function disableGenericFilters()
- {
- $this->variablesDefault['disable_generic_filters'] = true;
- }
-
- /**
- * The queued filters (replace column names, enhance column with percentage signs, add logo metadata information, etc.)
- * will not be applied to this datatable. They can be manually applied by calling applyQueuedFilters on the datatable.
- */
- public function disableQueuedFilters()
- {
- $this->variablesDefault['disable_queued_filters'] = true;
- }
-
- /**
- * The "X-Y of Z" and the "< Previous / Next >"-Buttons won't be displayed under this table
- */
- public function disableOffsetInformationAndPaginationControls()
- {
- $this->viewProperties['show_offset_information'] = false;
- $this->viewProperties['show_pagination_control'] = false;
- }
-
- /**
- * The "< Previous / Next >"-Buttons won't be displayed under this table
- */
- public function disableShowPaginationControl()
- {
- $this->viewProperties['show_pagination_control'] = false;
- }
-
- /**
- * Ensures the limit dropdown will always be shown, even if pagination is disabled.
- */
- public function alwaysShowLimitDropdown()
- {
- $this->viewProperties['show_limit_control'] = true;
- }
-
- /**
- * The "X-Y of Z" won't be displayed under this table
- */
- public function disableOffsetInformation()
- {
- $this->viewProperties['show_offset_information'] = false;
- }
-
- /**
- * The search box won't be displayed under this table
- */
- public function disableSearchBox()
- {
- $this->viewProperties['show_search'] = false;
- }
-
- /**
- * Do not sort this table, leave it as it comes out of the API
- */
- public function disableSort()
- {
- $this->variablesDefault['enable_sort'] = 'false';
- }
-
- /**
- * Do not show the footer icons (show all columns icon, "plus" icon)
- */
- public function disableFooterIcons()
- {
- $this->viewProperties['show_footer_icons'] = false;
- }
-
- /**
- * When this method is called, the output will not contain the template datatable_footer.tpl
- */
- public function disableFooter()
- {
- $this->viewProperties['show_footer'] = false;
- }
-
- /**
- * The "Include low population" link won't be displayed under this table
- */
- public function disableExcludeLowPopulation()
- {
- $this->viewProperties['show_exclude_low_population'] = false;
- }
-
- /**
- * Whether or not to show the "View table" icon
- */
- public function disableShowTable()
- {
- $this->viewProperties['show_table'] = false;
- }
-
- /**
- * Whether or not to show the "View more data" icon
- */
- public function disableShowAllColumns()
- {
- $this->viewProperties['show_table_all_columns'] = false;
- }
-
- /**
- * Whether or not to show the tag cloud, pie charts, bar chart icons
- */
- public function disableShowAllViewsIcons()
- {
- $this->viewProperties['show_all_views_icons'] = false;
- }
-
- /**
- * Whether or not to hide view icons altogether.
- * The difference to disableShowAllViewsIcons is that not even the single icon
- * will be shown. This icon might cause trouble because it reloads the graph on click.
- */
- public function hideAllViewsIcons()
- {
- $this->viewProperties['show_all_views_icons'] = false;
- $this->viewProperties['hide_all_views_icons'] = true;
- }
-
- /**
- * Whether or not to show the annotations view. This method has no effect if
- * the Annotations plugin is not loaded.
- */
- public function showAnnotationsView()
- {
- if (!Piwik_PluginsManager::getInstance()->isPluginLoaded('Annotations'))
- {
- return;
- }
-
- $this->viewProperties['hide_annotations_view'] = false;
- }
-
- /**
- * Whether or not to show the bar chart icon.
- */
- public function disableShowBarChart()
- {
- $this->viewProperties['show_bar_chart'] = false;
- }
-
- /**
- * Whether or not to show the pie chart icon.
- */
- public function disableShowPieChart()
- {
- $this->viewProperties['show_pie_chart'] = false;
- }
-
- /**
- * Whether or not to show the tag cloud icon.
- */
- public function disableTagCloud()
- {
- $this->viewProperties['show_tag_cloud'] = false;
- }
-
- /**
- * Whether or not to show related reports in the footer
- */
- public function disableShowRelatedReports()
- {
- $this->viewProperties['show_related_reports'] = false;
- }
-
- /**
- * Whether or not to show the export to RSS feed icon
- */
- public function disableShowExportAsRssFeed()
- {
- $this->viewProperties['show_export_as_rss_feed'] = false;
- }
-
- /**
- * Whether or not to show the "goal" icon
- */
- public function enableShowGoals()
- {
- if(Piwik_PluginsManager::getInstance()->isPluginActivated('Goals'))
- {
- $this->viewProperties['show_goals'] = true;
- }
- }
-
- /**
- * Whether or not to show the "Ecommerce orders/cart" icons
- */
- public function enableShowEcommerce()
- {
- $this->viewProperties['show_ecommerce'] = true;
- }
-
- /**
- * Whether or not to show the summary row on every page of results. The default behavior
- * is to treat the summary row like any other row.
- */
- public function alwaysShowSummaryRow()
- {
- $this->variablesDefault['keep_summary_row'] = true;
- }
-
- /**
- * Sets the value to use for the Exclude low population filter.
- *
- * @param int|float If a row value is less than this value, it will be removed from the dataTable
- * @param string The name of the column for which we compare the value to $minValue
- */
- public function setExcludeLowPopulation( $columnName = null, $minValue = null )
- {
- if(is_null($columnName))
- {
- $columnName = 'nb_visits';
- }
- $this->variablesDefault['filter_excludelowpop'] = $columnName;
- $this->variablesDefault['filter_excludelowpop_value'] = $minValue;
- }
-
- /**
- * Sets the pattern to look for in the table (only rows matching the pattern will be kept)
- *
- * @param string $pattern to look for
- * @param string $column to compare the pattern to
- */
- public function setSearchPattern($pattern, $column)
- {
- $this->variablesDefault['filter_pattern'] = $pattern;
- $this->variablesDefault['filter_column'] = $column;
- }
-
- /**
- * Sets the maximum number of rows of the table
- *
- * @param int $limit
- */
- public function setLimit( $limit )
- {
- if($limit !== 0)
- {
- $this->variablesDefault['filter_limit'] = $limit;
- }
- }
-
- /**
- * Will display a message in the DataTable footer.
- *
- * @param string $message Message
- */
- public function setFooterMessage( $message )
- {
- $this->viewProperties['show_footer_message'] = $message;
- }
-
- /**
- * Sets the dataTable column to sort by. This sorting will be applied before applying the (offset, limit) filter.
- *
- * @param int|string $columnId eg. 'nb_visits' for some tables, or Piwik_Archive::INDEX_NB_VISITS for others
- * @param string $order desc or asc
- */
- public function setSortedColumn( $columnId, $order = 'desc')
- {
+ }
+ return $this->view;
+ }
+
+ public function getCurrentControllerAction()
+ {
+ return $this->currentControllerAction;
+ }
+
+ public function getCurrentControllerName()
+ {
+ return $this->currentControllerName;
+ }
+
+ public function getApiMethodToRequestDataTable()
+ {
+ return $this->apiMethodToRequestDataTable;
+ }
+
+ public function getControllerActionCalledWhenRequestSubTable()
+ {
+ return $this->controllerActionCalledWhenRequestSubTable;
+ }
+
+ /**
+ * Returns the DataTable loaded from the API
+ *
+ * @return Piwik_DataTable
+ * @throws exception if not yet defined
+ */
+ public function getDataTable()
+ {
+ if (is_null($this->dataTable)) {
+ throw new Exception("The DataTable object has not yet been created");
+ }
+ return $this->dataTable;
+ }
+
+ /**
+ * To prevent calling an API multiple times, the DataTable can be set directly.
+ * It won't be loaded again from the API in this case
+ *
+ * @param $dataTable
+ * @return void $dataTable Piwik_DataTable
+ */
+ public function setDataTable($dataTable)
+ {
+ $this->dataTable = $dataTable;
+ }
+
+ /**
+ * Function called by the ViewDataTable objects in order to fetch data from the API.
+ * The function init() must have been called before, so that the object knows which API module and action to call.
+ * It builds the API request string and uses Piwik_API_Request to call the API.
+ * The requested Piwik_DataTable object is stored in $this->dataTable.
+ */
+ protected function loadDataTableFromAPI()
+ {
+ if (!is_null($this->dataTable)) {
+ // data table is already there
+ // this happens when setDataTable has been used
+ return;
+ }
+
+ // we build the request string (URL) to call the API
+ $requestString = $this->getRequestString();
+
+ // we make the request to the API
+ $request = new Piwik_API_Request($requestString);
+
+ // and get the DataTable structure
+ $dataTable = $request->process();
+
+ $this->dataTable = $dataTable;
+ }
+
+ /**
+ * Checks that the API returned a normal DataTable (as opposed to DataTable_Array)
+ * @throws Exception
+ * @return void
+ */
+ protected function checkStandardDataTable()
+ {
+ Piwik::checkObjectTypeIs($this->dataTable, array('Piwik_DataTable'));
+ }
+
+ /**
+ * Hook called after the dataTable has been loaded from the API
+ * Can be used to add, delete or modify the data freshly loaded
+ *
+ * @return bool
+ */
+ protected function postDataTableLoadedFromAPI()
+ {
+ if (empty($this->dataTable)) {
+ return false;
+ }
+
+ // deal w/ table metadata
+ if ($this->dataTable instanceof Piwik_DataTable) {
+ $this->viewProperties['metadata'] = $this->dataTable->getAllTableMetadata();
+
+ if (isset($this->viewProperties['metadata'][Piwik_DataTable::ARCHIVED_DATE_METADATA_NAME])) {
+ $this->viewProperties['metadata'][Piwik_DataTable::ARCHIVED_DATE_METADATA_NAME] =
+ $this->makePrettyArchivedOnText();
+ }
+ }
+
+ // First, filters that delete rows
+ foreach ($this->queuedFiltersPriority as $filter) {
+ $filterName = $filter[0];
+ $filterParameters = $filter[1];
+ $this->dataTable->filter($filterName, $filterParameters);
+ }
+
+ if (!$this->areGenericFiltersDisabled()) {
+ // Second, generic filters (Sort, Limit, Replace Column Names, etc.)
+ $requestString = $this->getRequestString();
+ $request = Piwik_API_Request::getRequestArrayFromString($requestString);
+
+ if (!empty($this->variablesDefault['enable_sort'])
+ && $this->variablesDefault['enable_sort'] === 'false'
+ ) {
+ $request['filter_sort_column'] = $request['filter_sort_order'] = '';
+ }
+
+ $genericFilter = new Piwik_API_DataTableGenericFilter($request);
+ $genericFilter->filter($this->dataTable);
+ }
+
+ if (!$this->areQueuedFiltersDisabled()) {
+ // Finally, apply datatable filters that were queued (should be 'presentation' filters that
+ // do not affect the number of rows)
+ foreach ($this->queuedFilters as $filter) {
+ $filterName = $filter[0];
+ $filterParameters = $filter[1];
+ $this->dataTable->filter($filterName, $filterParameters);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if generic filters have been disabled, false if otherwise.
+ *
+ * @return bool
+ */
+ private function areGenericFiltersDisabled()
+ {
+ // if disable_generic_filters query param is set to '1', generic filters are disabled
+ if (Piwik_Common::getRequestVar('disable_generic_filters', '0', 'string') == 1) {
+ return true;
+ }
+
+ // if $this->disableGenericFilters() was called, generic filters are disabled
+ if (isset($this->variablesDefault['disable_generic_filters'])
+ && $this->variablesDefault['disable_generic_filters'] === true
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if queued filters have been disabled, false if otherwise.
+ *
+ * @return bool
+ */
+ private function areQueuedFiltersDisabled()
+ {
+ return isset($this->variablesDefault['disable_queued_filters'])
+ && $this->variablesDefault['disable_queued_filters'];
+ }
+
+ /**
+ * Returns prettified and translated text that describes when a report was last updated.
+ *
+ * @return string
+ */
+ private function makePrettyArchivedOnText()
+ {
+ $dateText = $this->viewProperties['metadata'][Piwik_DataTable::ARCHIVED_DATE_METADATA_NAME];
+ $date = Piwik_Date::factory($dateText);
+ $today = mktime(0, 0, 0);
+ if ($date->getTimestamp() > $today) {
+ $elapsedSeconds = time() - $date->getTimestamp();
+ $timeAgo = Piwik::getPrettyTimeFromSeconds($elapsedSeconds);
+
+ return Piwik_Translate('CoreHome_ReportGeneratedXAgo', $timeAgo);
+ }
+
+ $prettyDate = $date->getLocalized("%longYear%, %longMonth% %day%") . $date->toString('S');
+ return Piwik_Translate('CoreHome_ReportGeneratedOn', $prettyDate);
+ }
+
+ /**
+ * @return string URL to call the API, eg. "method=Referers.getKeywords&period=day&date=yesterday"...
+ */
+ protected function getRequestString()
+ {
+ // we prepare the string to give to the API Request
+ // we setup the method and format variable
+ // - we request the method to call to get this specific DataTable
+ // - the format = original specifies that we want to get the original DataTable structure itself, not rendered
+ $requestString = 'method=' . $this->apiMethodToRequestDataTable;
+ $requestString .= '&format=original';
+ $requestString .= '&disable_generic_filters=' . Piwik_Common::getRequestVar('disable_generic_filters', 1, 'int');
+
+ $toSetEventually = array(
+ 'filter_limit',
+ 'keep_summary_row',
+ 'filter_sort_column',
+ 'filter_sort_order',
+ 'filter_excludelowpop',
+ 'filter_excludelowpop_value',
+ 'filter_column',
+ 'filter_pattern',
+ 'disable_queued_filters',
+ );
+
+ foreach ($toSetEventually as $varToSet) {
+ $value = $this->getDefaultOrCurrent($varToSet);
+ if (false !== $value) {
+ if (is_array($value)) {
+ foreach ($value as $v) {
+ $requestString .= "&" . $varToSet . '[]=' . $v;
+ }
+ } else {
+ $requestString .= '&' . $varToSet . '=' . $value;
+ }
+ }
+ }
+ return $requestString;
+ }
+
+ /**
+ * For convenience, the client code can call methods that are defined in a specific children class
+ * without testing the children class type, which would trigger an error with a different children class.
+ *
+ * Example:
+ * ViewDataTable/Html.php defines a setColumnsToDisplay(). The client code calls this methods even if
+ * the ViewDataTable object is a ViewDataTable_Cloud instance (he doesn't know because of the factory()).
+ * But ViewDataTable_Cloud doesn't define the setColumnsToDisplay() method.
+ * Because we don't want to force users to test for the object type we simply catch these
+ * calls when they are not defined in the child and do nothing.
+ *
+ * @param string $function
+ * @param array $args
+ */
+ public function __call($function, $args)
+ {
+ }
+
+ /**
+ * Returns a unique ID for this ViewDataTable.
+ * This unique ID is used in the Javascript code:
+ * Any ajax loaded data is loaded within a DIV that has id=$unique_id
+ * The jquery code then replaces the existing html div id=$unique_id in the code with this data.
+ *
+ * @see datatable.js
+ * @return string
+ */
+ protected function loadUniqueIdViewDataTable()
+ {
+ // if we request a subDataTable the $this->currentControllerAction DIV ID is already there in the page
+ // we make the DIV ID really unique by appending the ID of the subtable requested
+ if ($this->idSubtable != 0 // parent DIV has a idSubtable = 0 but the html DIV must have the name of the module.action
+ && $this->idSubtable !== false // case there is no idSubtable
+ ) {
+ // see also datatable.js (the ID has to match with the html ID created to be replaced by the result of the ajax call)
+ $uniqIdTable = 'subDataTable_' . $this->idSubtable;
+ } else {
+ // the $uniqIdTable variable is used as the DIV ID in the rendered HTML
+ // we use the current Controller action name as it is supposed to be unique in the rendered page
+ $uniqIdTable = $this->currentControllerName . $this->currentControllerAction;
+ }
+ return $uniqIdTable;
+ }
+
+ /**
+ * Sets the $uniqIdTable variable that is used as the DIV ID in the rendered HTML
+ * @param $uniqIdTable
+ */
+ public function setUniqueIdViewDataTable($uniqIdTable)
+ {
+ $this->viewProperties['uniqueId'] = $uniqIdTable;
+ $this->uniqIdTable = $uniqIdTable;
+ }
+
+ /**
+ * Returns current value of $uniqIdTable variable that is used as the DIV ID in the rendered HTML
+ * @return null|string
+ */
+ public function getUniqueIdViewDataTable()
+ {
+ if ($this->uniqIdTable == null) {
+ $this->uniqIdTable = $this->loadUniqueIdViewDataTable();
+ }
+ return $this->uniqIdTable;
+ }
+
+ /**
+ * Returns array of properties, eg. "show_footer", "show_search", etc.
+ *
+ * @return array of boolean
+ */
+ protected function getViewProperties()
+ {
+ return $this->viewProperties;
+ }
+
+ /**
+ * This functions reads the customization values for the DataTable and returns an array (name,value) to be printed in Javascript.
+ * This array defines things such as:
+ * - name of the module & action to call to request data for this table
+ * - optional filters information, eg. filter_limit and filter_offset
+ * - etc.
+ *
+ * The values are loaded:
+ * - from the generic filters that are applied by default @see Piwik_API_DataTableGenericFilter.php::getGenericFiltersInformation()
+ * - from the values already available in the GET array
+ * - from the values set using methods from this class (eg. setSearchPattern(), setLimit(), etc.)
+ *
+ * @return array eg. array('show_offset_information' => 0, 'show_...
+ */
+ protected function getJavascriptVariablesToSet()
+ {
+ // build javascript variables to set
+ $javascriptVariablesToSet = array();
+
+ $genericFilters = Piwik_API_DataTableGenericFilter::getGenericFiltersInformation();
+ foreach ($genericFilters as $filter) {
+ foreach ($filter as $filterVariableName => $filterInfo) {
+ // if there is a default value for this filter variable we set it
+ // so that it is propagated to the javascript
+ if (isset($filterInfo[1])) {
+ $javascriptVariablesToSet[$filterVariableName] = $filterInfo[1];
+
+ // we set the default specified column and Order to sort by
+ // when this javascript variable is not set already
+ // for example during an AJAX call this variable will be set in the URL
+ // so this will not be executed (and the default sorted not be used as the sorted column might have changed in the meanwhile)
+ if (false !== ($defaultValue = $this->getDefault($filterVariableName))) {
+ $javascriptVariablesToSet[$filterVariableName] = $defaultValue;
+ }
+ }
+ }
+ }
+
+ foreach ($_GET as $name => $value) {
+ try {
+ $requestValue = Piwik_Common::getRequestVar($name);
+ } catch (Exception $e) {
+ $requestValue = '';
+ }
+ $javascriptVariablesToSet[$name] = $requestValue;
+ }
+
+ // at this point there are some filters values we may have not set,
+ // case of the filter without default values and parameters set directly in this class
+ // for example setExcludeLowPopulation
+ // we go through all the $this->variablesDefault array and set the variables not set yet
+ foreach ($this->variablesDefault as $name => $value) {
+ if (!isset($javascriptVariablesToSet[$name])) {
+ $javascriptVariablesToSet[$name] = $value;
+ }
+ }
+
+ if ($this->dataTable instanceof Piwik_DataTable) {
+ // we override the filter_sort_column with the column used for sorting,
+ // which can be different from the one specified (eg. if the column doesn't exist)
+ $javascriptVariablesToSet['filter_sort_column'] = $this->dataTable->getSortedByColumnName();
+ // datatable can return "2" but we want to write "nb_visits" in the js
+ if (isset(Piwik_Archive::$mappingFromIdToName[$javascriptVariablesToSet['filter_sort_column']])) {
+ $javascriptVariablesToSet['filter_sort_column'] = Piwik_Archive::$mappingFromIdToName[$javascriptVariablesToSet['filter_sort_column']];
+ }
+ }
+
+ $javascriptVariablesToSet['module'] = $this->currentControllerName;
+ $javascriptVariablesToSet['action'] = $this->currentControllerAction;
+ $javascriptVariablesToSet['viewDataTable'] = $this->getViewDataTableId();
+ $javascriptVariablesToSet['controllerActionCalledWhenRequestSubTable'] = $this->controllerActionCalledWhenRequestSubTable;
+
+ if ($this->dataTable &&
+ // Piwik_DataTable_Array doesn't have the method
+ !($this->dataTable instanceof Piwik_DataTable_Array)
+ && empty($javascriptVariablesToSet['totalRows'])
+ ) {
+ $javascriptVariablesToSet['totalRows'] = $this->dataTable->getRowsCountBeforeLimitFilter();
+ }
+
+ // we escape the values that will be displayed in the javascript footer of each datatable
+ // to make sure there is no malicious code injected (the value are already htmlspecialchar'ed as they
+ // are loaded with Piwik_Common::getRequestVar()
+ foreach ($javascriptVariablesToSet as &$value) {
+ if (is_array($value)) {
+ $value = array_map('addslashes', $value);
+ } else {
+ $value = addslashes($value);
+ }
+ }
+
+ $deleteFromJavascriptVariables = array(
+ 'filter_excludelowpop',
+ 'filter_excludelowpop_value',
+ );
+ foreach ($deleteFromJavascriptVariables as $name) {
+ if (isset($javascriptVariablesToSet[$name])) {
+ unset($javascriptVariablesToSet[$name]);
+ }
+ }
+ return $javascriptVariablesToSet;
+ }
+
+ /**
+ * Returns, for a given parameter, the value of this parameter in the REQUEST array.
+ * If not set, returns the default value for this parameter @see getDefault()
+ *
+ * @param string $nameVar
+ * @return string|mixed Value of this parameter
+ */
+ protected function getDefaultOrCurrent($nameVar)
+ {
+ if (isset($_GET[$nameVar])) {
+ return Piwik_Common::sanitizeInputValue($_GET[$nameVar]);
+ }
+ $default = $this->getDefault($nameVar);
+ return $default;
+ }
+
+ /**
+ * Returns the default value for a given parameter.
+ * For example, these default values can be set using the disable* methods.
+ *
+ * @param string $nameVar
+ * @return mixed
+ */
+ protected function getDefault($nameVar)
+ {
+ if (!isset($this->variablesDefault[$nameVar])) {
+ return false;
+ }
+ return $this->variablesDefault[$nameVar];
+ }
+
+ /**
+ * The generic filters (limit, offset, sort by visit desc) will not be applied to this datatable.
+ */
+ public function disableGenericFilters()
+ {
+ $this->variablesDefault['disable_generic_filters'] = true;
+ }
+
+ /**
+ * The queued filters (replace column names, enhance column with percentage signs, add logo metadata information, etc.)
+ * will not be applied to this datatable. They can be manually applied by calling applyQueuedFilters on the datatable.
+ */
+ public function disableQueuedFilters()
+ {
+ $this->variablesDefault['disable_queued_filters'] = true;
+ }
+
+ /**
+ * The "X-Y of Z" and the "< Previous / Next >"-Buttons won't be displayed under this table
+ */
+ public function disableOffsetInformationAndPaginationControls()
+ {
+ $this->viewProperties['show_offset_information'] = false;
+ $this->viewProperties['show_pagination_control'] = false;
+ }
+
+ /**
+ * The "< Previous / Next >"-Buttons won't be displayed under this table
+ */
+ public function disableShowPaginationControl()
+ {
+ $this->viewProperties['show_pagination_control'] = false;
+ }
+
+ /**
+ * Ensures the limit dropdown will always be shown, even if pagination is disabled.
+ */
+ public function alwaysShowLimitDropdown()
+ {
+ $this->viewProperties['show_limit_control'] = true;
+ }
+
+ /**
+ * The "X-Y of Z" won't be displayed under this table
+ */
+ public function disableOffsetInformation()
+ {
+ $this->viewProperties['show_offset_information'] = false;
+ }
+
+ /**
+ * The search box won't be displayed under this table
+ */
+ public function disableSearchBox()
+ {
+ $this->viewProperties['show_search'] = false;
+ }
+
+ /**
+ * Do not sort this table, leave it as it comes out of the API
+ */
+ public function disableSort()
+ {
+ $this->variablesDefault['enable_sort'] = 'false';
+ }
+
+ /**
+ * Do not show the footer icons (show all columns icon, "plus" icon)
+ */
+ public function disableFooterIcons()
+ {
+ $this->viewProperties['show_footer_icons'] = false;
+ }
+
+ /**
+ * When this method is called, the output will not contain the template datatable_footer.tpl
+ */
+ public function disableFooter()
+ {
+ $this->viewProperties['show_footer'] = false;
+ }
+
+ /**
+ * The "Include low population" link won't be displayed under this table
+ */
+ public function disableExcludeLowPopulation()
+ {
+ $this->viewProperties['show_exclude_low_population'] = false;
+ }
+
+ /**
+ * Whether or not to show the "View table" icon
+ */
+ public function disableShowTable()
+ {
+ $this->viewProperties['show_table'] = false;
+ }
+
+ /**
+ * Whether or not to show the "View more data" icon
+ */
+ public function disableShowAllColumns()
+ {
+ $this->viewProperties['show_table_all_columns'] = false;
+ }
+
+ /**
+ * Whether or not to show the tag cloud, pie charts, bar chart icons
+ */
+ public function disableShowAllViewsIcons()
+ {
+ $this->viewProperties['show_all_views_icons'] = false;
+ }
+
+ /**
+ * Whether or not to hide view icons altogether.
+ * The difference to disableShowAllViewsIcons is that not even the single icon
+ * will be shown. This icon might cause trouble because it reloads the graph on click.
+ */
+ public function hideAllViewsIcons()
+ {
+ $this->viewProperties['show_all_views_icons'] = false;
+ $this->viewProperties['hide_all_views_icons'] = true;
+ }
+
+ /**
+ * Whether or not to show the annotations view. This method has no effect if
+ * the Annotations plugin is not loaded.
+ */
+ public function showAnnotationsView()
+ {
+ if (!Piwik_PluginsManager::getInstance()->isPluginLoaded('Annotations')) {
+ return;
+ }
+
+ $this->viewProperties['hide_annotations_view'] = false;
+ }
+
+ /**
+ * Whether or not to show the bar chart icon.
+ */
+ public function disableShowBarChart()
+ {
+ $this->viewProperties['show_bar_chart'] = false;
+ }
+
+ /**
+ * Whether or not to show the pie chart icon.
+ */
+ public function disableShowPieChart()
+ {
+ $this->viewProperties['show_pie_chart'] = false;
+ }
+
+ /**
+ * Whether or not to show the tag cloud icon.
+ */
+ public function disableTagCloud()
+ {
+ $this->viewProperties['show_tag_cloud'] = false;
+ }
+
+ /**
+ * Whether or not to show related reports in the footer
+ */
+ public function disableShowRelatedReports()
+ {
+ $this->viewProperties['show_related_reports'] = false;
+ }
+
+ /**
+ * Whether or not to show the export to RSS feed icon
+ */
+ public function disableShowExportAsRssFeed()
+ {
+ $this->viewProperties['show_export_as_rss_feed'] = false;
+ }
+
+ /**
+ * Whether or not to show the "goal" icon
+ */
+ public function enableShowGoals()
+ {
+ if (Piwik_PluginsManager::getInstance()->isPluginActivated('Goals')) {
+ $this->viewProperties['show_goals'] = true;
+ }
+ }
+
+ /**
+ * Whether or not to show the "Ecommerce orders/cart" icons
+ */
+ public function enableShowEcommerce()
+ {
+ $this->viewProperties['show_ecommerce'] = true;
+ }
+
+ /**
+ * Whether or not to show the summary row on every page of results. The default behavior
+ * is to treat the summary row like any other row.
+ */
+ public function alwaysShowSummaryRow()
+ {
+ $this->variablesDefault['keep_summary_row'] = true;
+ }
+
+ /**
+ * Sets the value to use for the Exclude low population filter.
+ *
+ * @param int|float If a row value is less than this value, it will be removed from the dataTable
+ * @param string The name of the column for which we compare the value to $minValue
+ */
+ public function setExcludeLowPopulation($columnName = null, $minValue = null)
+ {
+ if (is_null($columnName)) {
+ $columnName = 'nb_visits';
+ }
+ $this->variablesDefault['filter_excludelowpop'] = $columnName;
+ $this->variablesDefault['filter_excludelowpop_value'] = $minValue;
+ }
+
+ /**
+ * Sets the pattern to look for in the table (only rows matching the pattern will be kept)
+ *
+ * @param string $pattern to look for
+ * @param string $column to compare the pattern to
+ */
+ public function setSearchPattern($pattern, $column)
+ {
+ $this->variablesDefault['filter_pattern'] = $pattern;
+ $this->variablesDefault['filter_column'] = $column;
+ }
+
+ /**
+ * Sets the maximum number of rows of the table
+ *
+ * @param int $limit
+ */
+ public function setLimit($limit)
+ {
+ if ($limit !== 0) {
+ $this->variablesDefault['filter_limit'] = $limit;
+ }
+ }
+
+ /**
+ * Will display a message in the DataTable footer.
+ *
+ * @param string $message Message
+ */
+ public function setFooterMessage($message)
+ {
+ $this->viewProperties['show_footer_message'] = $message;
+ }
+
+ /**
+ * Sets the dataTable column to sort by. This sorting will be applied before applying the (offset, limit) filter.
+ *
+ * @param int|string $columnId eg. 'nb_visits' for some tables, or Piwik_Archive::INDEX_NB_VISITS for others
+ * @param string $order desc or asc
+ */
+ public function setSortedColumn($columnId, $order = 'desc')
+ {
// debug_print_backtrace();
- $this->variablesDefault['filter_sort_column'] = $columnId;
- $this->variablesDefault['filter_sort_order'] = $order;
- }
-
- /**
- * Returns the column name on which the table will be sorted
- *
- * @return string
- */
- public function getSortedColumn()
- {
- return isset($this->variablesDefault['filter_sort_column']) ? $this->variablesDefault['filter_sort_column'] : false;
- }
-
- /**
- * Sets translation string for given column
- *
- * @param string $columnName column name
- * @param string $columnTranslation column name translation
- * @throws Exception
- */
- public function setColumnTranslation( $columnName, $columnTranslation )
- {
- if(empty($columnTranslation))
- {
- throw new Exception('Unknown column: '.$columnName);
- }
-
- $this->columnsTranslations[$columnName] = $columnTranslation;
- }
-
- /**
- * Returns column translation if available, in other case given column name
- *
- * @param string $columnName column name
- * @return string
- */
- public function getColumnTranslation( $columnName )
- {
- if( isset($this->columnsTranslations[$columnName]) )
- {
- return $this->columnsTranslations[$columnName];
- }
- return $columnName;
- }
-
- /**
- * Set the documentation of a metric used in the report.
- * Please note, that the default way of doing this is by using
- * getReportMetadata. Only use this method, if you have a good
- * reason to do so.
- *
- * @param string $metricIdentifier The idenentifier string of
- * the metric
- * @param string $documentation The metric documentation as a
- * translated string
- */
- public function setMetricDocumentation($metricIdentifier, $documentation) {
- $this->metricsDocumentation[$metricIdentifier] = $documentation;
- }
-
- /**
- * Returns metric documentation, or false
- *
- * @param string $columnName column name
- * @return bool
- */
- public function getMetricDocumentation($columnName)
- {
- if ($this->metricsDocumentation === false)
- {
- $this->loadDocumentation();
- }
-
- if (!empty($this->metricsDocumentation[$columnName]))
- {
- return $this->metricsDocumentation[$columnName];
- }
-
- return false;
- }
-
- /**
- * Set the documentation of the report.
- * Please note, that the default way of doing this is by using
- * getReportMetadata. Only use this method, if you have a good
- * reason to do so.
- *
- * @param string $documentation The report documentation as a
- * translated string
- */
- public function setReportDocumentation($documentation) {
- $this->documentation = $documentation;
- }
-
- /**
- * Returns report documentation, or false
- * @return array|bool
- */
- public function getReportDocumentation()
- {
- if ($this->metricsDocumentation === false)
- {
- $this->loadDocumentation();
- }
-
- return $this->documentation;
- }
-
- /** Load documentation from the API */
- private function loadDocumentation()
- {
- $this->metricsDocumentation = array();
-
- $report = Piwik_API_API::getInstance()->getMetadata(0, $this->currentControllerName, $this->currentControllerAction);
- $report = $report[0];
-
- if (isset($report['metricsDocumentation']))
- {
- $this->metricsDocumentation = $report['metricsDocumentation'];
- }
-
- if (isset($report['documentation']))
- {
- $this->documentation = $report['documentation'];
- }
- }
-
- /**
- * Sets the columns that will be displayed in the HTML output
- * By default all columns are displayed ($columnsNames = array() will display all columns)
- *
- * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits')
- */
- public function setColumnsToDisplay( $columnsNames )
- {
- if(!is_array($columnsNames))
- {
- if (strpos($columnsNames, ',') !== false)
- {
- // array values are comma separated
- $columnsNames = explode(',', $columnsNames);
- }
- else
- {
- $columnsNames = array($columnsNames);
- }
- }
- $this->columnsToDisplay = array_filter($columnsNames);
- }
-
- /**
- * Returns columns names to display, in order.
- * If no columns were specified to be displayed, return all columns found in the first row.
- * If the data table has empty_columns meta data set, those columns will be removed.
- * @param array PHP array conversion of the data table
- * @return array
- */
- public function getColumnsToDisplay()
- {
- if(empty($this->columnsToDisplay))
- {
- $row = $this->dataTable->getFirstRow();
- if(empty($row))
- {
- return array();
- }
-
- return array_keys($row->getColumns());
- }
-
- $this->columnsToDisplay = array_filter($this->columnsToDisplay);
-
- if ($this->dataTable instanceof Piwik_DataTable_Array) {
- $emptyColumns = $this->dataTable->getMetadataIntersectArray(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME);
- } else {
- $emptyColumns = $this->dataTable->getMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME);
- }
- if (is_array($emptyColumns))
- {
- foreach ($emptyColumns as $emptyColumn)
- {
- $key = array_search($emptyColumn, $this->columnsToDisplay);
- if ($key !== false)
- {
- unset($this->columnsToDisplay[$key]);
- }
- }
- $this->columnsToDisplay = array_values($this->columnsToDisplay);
- }
-
- return $this->columnsToDisplay;
- }
-
- /**
- * Set whether to highlight the summary row or not. If not highlighted, it will
- * look like every other row.
- */
- public function setHighlightSummaryRow( $highlightSummaryRow )
- {
- $this->viewProperties['highlight_summary_row'] = $highlightSummaryRow;
- }
-
- /**
- * Sets the name of the metadata to use for a custom tooltip.
- */
- public function setTooltipMetadataName( $metadataName )
- {
- $this->viewProperties['tooltip_metadata_name'] = $metadataName;
- }
-
- /**
- * Sets columns translations array.
- *
- * @param array $columnsTranslations An associative array indexed by column names, eg. array('nb_visit'=>"Numer of visits")
- */
- public function setColumnsTranslations( $columnsTranslations )
- {
- $this->columnsTranslations += $columnsTranslations;
- }
-
- /**
- * Sets a custom parameter, that will be printed in the javascript array associated with each datatable
- *
- * @param string $parameter name
- * @param mixed $value
- * @throws Exception
- */
- public function setCustomParameter($parameter, $value)
- {
- if(isset($this->variablesDefault[$parameter]))
- {
- throw new Exception("$parameter is already defined for this DataTable.");
- }
- $this->variablesDefault[$parameter] = $value;
- }
-
- /**
- * Queues a Datatable filter, that will be applied once the datatable is loaded from the API.
- * Useful when the controller needs to add columns, or decorate existing columns, when these filters don't
- * necessarily make sense directly in the API.
- *
- * @param string $filterName
- * @param mixed $parameters
- * @param bool $runBeforeGenericFilters Set to true if the filter will delete rows from the table,
- * and should therefore be ran before Sort, Limit, etc.
- * @return void
- */
- public function queueFilter($filterName, $parameters, $runBeforeGenericFilters = false)
- {
- if($runBeforeGenericFilters)
- {
- $this->queuedFiltersPriority[] = array($filterName, $parameters);
- }
- else
- {
- $this->queuedFilters[] = array($filterName, $parameters);
- }
- }
-
- /**
- * Adds one report to the set of reports that are related to this one. Related reports
- * are displayed in the footer as links. When they are clicked, the report will change to
- * the related report.
- *
- * Make sure to call setReportTitle so this report will be displayed correctly.
- *
- * @param string $module The report's controller name, ie, 'UserSettings'.
- * @param string $action The report's controller action, ie, 'getBrowser'.
- * @param string $title The text used to describe the related report.
- * @param array $queryParams Any specific query params to use when loading the report.
- * This can be used to, for example, make a goal report a related
- * report (by adding an idGoal parameter).
- */
- public function addRelatedReport( $module, $action, $title, $queryParams = array() )
- {
- // don't add the related report if it references this report
- if ($this->currentControllerName == $module && $this->currentControllerAction == $action)
- {
- return;
- }
-
- $url = $this->getBaseReportUrl($module, $action, $queryParams);
- $this->viewProperties['relatedReports'][$url] = $title;
- }
-
- /**
- * Adds a set of reports that are related to this one. Related reports are displayed in
- * the footer as links. When they are clicked, the report will change to the related report.
- *
- * If you need to associate specific query params with a report, use the addRelatedReport
- * method instead of this one.
- *
- * @param string $thisReportTitle The title of this report.
- * @param array $relatedReports An array mapping report IDs ('Controller.methodName') with
- * display text.
- */
- public function addRelatedReports( $thisReportTitle, $relatedReports )
- {
- $this->setReportTitle($thisReportTitle);
- foreach ($relatedReports as $report => $title)
- {
- list($module, $action) = explode('.', $report);
- $this->addRelatedReport($module, $action, $title);
- }
- }
-
- /**
- * Sets the title of this report.
- *
- * @param string $title
- */
- public function setReportTitle( $title )
- {
- $this->viewProperties['title'] = $title;
- }
-
- /**
- * Sets a custom URL to use to reference this report.
- *
- * @param string $url
- */
- public function setReportUrl( $module, $action, $queryParams = array() )
- {
- $this->viewProperties['self_url'] = $this->getBaseReportUrl($module, $action, $queryParams);
- }
-
- /**
- * Returns true if it is likely that the data for this report has been purged and if the
- * user should be told about that.
- *
- * In order for this function to return true, the following must also be true:
- * - The data table for this report must either be empty or not have been fetched.
- * - The period of this report is not a multiple period.
- * - The date of this report must be older than the delete_reports_older_than config option.
- * @return bool
- */
- public function hasReportBeenPurged()
- {
- $strPeriod = Piwik_Common::getRequestVar('period', false);
- $strDate = Piwik_Common::getRequestVar('date', false);
-
- if ($strPeriod !== false
- && $strDate !== false
- && (is_null($this->dataTable) || $this->dataTable->getRowsCount() == 0))
- {
- // if range, only look at the first date
- if ($strPeriod == 'range')
- {
- $idSite = Piwik_Common::getRequestVar('idSite', '');
- if (intval($idSite) != 0)
- {
- $site = new Piwik_Site($idSite);
- $timezone = $site->getTimezone();
- }
- else
- {
- $timezone = 'UTC';
- }
-
- $period = new Piwik_Period_Range('range', $strDate, $timezone);
- $reportDate = $period->getDateStart();
- }
- // if a multiple period, this function is irrelevant
- else if (Piwik_Archive::isMultiplePeriod($strDate, $strPeriod))
- {
- return false;
- }
- // otherwise, use the date as given
- else
- {
- $reportDate = Piwik_Date::factory($strDate);
- }
-
- $reportYear = $reportDate->toString('Y');
- $reportMonth = $reportDate->toString('m');
-
- if (class_exists('Piwik_PrivacyManager')
- && Piwik_PrivacyManager::shouldReportBePurged($reportYear, $reportMonth))
- {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Returns URL for this report w/o any filter parameters.
- *
- * @param string $module
- * @param string $action
- * @param array $queryParams
- */
- private function getBaseReportUrl( $module, $action, $queryParams = array() )
- {
- $params = array_merge($queryParams, array('module' => $module, 'action' => $action));
-
- // unset all filter query params so the related report will show up in its default state,
- // unless the filter param was in $queryParams
- $genericFiltersInfo = Piwik_API_DataTableGenericFilter::getGenericFiltersInformation();
- foreach ($genericFiltersInfo as $filter)
- {
- foreach ($filter as $queryParamName => $queryParamInfo)
- {
- if (!isset($params[$queryParamName]))
- {
- $params[$queryParamName] = null;
- }
- }
- }
-
- // add the related report
- $url = Piwik_Url::getCurrentQueryStringWithParametersModified($params);
- return $url;
- }
+ $this->variablesDefault['filter_sort_column'] = $columnId;
+ $this->variablesDefault['filter_sort_order'] = $order;
+ }
+
+ /**
+ * Returns the column name on which the table will be sorted
+ *
+ * @return string
+ */
+ public function getSortedColumn()
+ {
+ return isset($this->variablesDefault['filter_sort_column']) ? $this->variablesDefault['filter_sort_column'] : false;
+ }
+
+ /**
+ * Sets translation string for given column
+ *
+ * @param string $columnName column name
+ * @param string $columnTranslation column name translation
+ * @throws Exception
+ */
+ public function setColumnTranslation($columnName, $columnTranslation)
+ {
+ if (empty($columnTranslation)) {
+ throw new Exception('Unknown column: ' . $columnName);
+ }
+
+ $this->columnsTranslations[$columnName] = $columnTranslation;
+ }
+
+ /**
+ * Returns column translation if available, in other case given column name
+ *
+ * @param string $columnName column name
+ * @return string
+ */
+ public function getColumnTranslation($columnName)
+ {
+ if (isset($this->columnsTranslations[$columnName])) {
+ return $this->columnsTranslations[$columnName];
+ }
+ return $columnName;
+ }
+
+ /**
+ * Set the documentation of a metric used in the report.
+ * Please note, that the default way of doing this is by using
+ * getReportMetadata. Only use this method, if you have a good
+ * reason to do so.
+ *
+ * @param string $metricIdentifier The idenentifier string of
+ * the metric
+ * @param string $documentation The metric documentation as a
+ * translated string
+ */
+ public function setMetricDocumentation($metricIdentifier, $documentation)
+ {
+ $this->metricsDocumentation[$metricIdentifier] = $documentation;
+ }
+
+ /**
+ * Returns metric documentation, or false
+ *
+ * @param string $columnName column name
+ * @return bool
+ */
+ public function getMetricDocumentation($columnName)
+ {
+ if ($this->metricsDocumentation === false) {
+ $this->loadDocumentation();
+ }
+
+ if (!empty($this->metricsDocumentation[$columnName])) {
+ return $this->metricsDocumentation[$columnName];
+ }
+
+ return false;
+ }
+
+ /**
+ * Set the documentation of the report.
+ * Please note, that the default way of doing this is by using
+ * getReportMetadata. Only use this method, if you have a good
+ * reason to do so.
+ *
+ * @param string $documentation The report documentation as a
+ * translated string
+ */
+ public function setReportDocumentation($documentation)
+ {
+ $this->documentation = $documentation;
+ }
+
+ /**
+ * Returns report documentation, or false
+ * @return array|bool
+ */
+ public function getReportDocumentation()
+ {
+ if ($this->metricsDocumentation === false) {
+ $this->loadDocumentation();
+ }
+
+ return $this->documentation;
+ }
+
+ /** Load documentation from the API */
+ private function loadDocumentation()
+ {
+ $this->metricsDocumentation = array();
+
+ $report = Piwik_API_API::getInstance()->getMetadata(0, $this->currentControllerName, $this->currentControllerAction);
+ $report = $report[0];
+
+ if (isset($report['metricsDocumentation'])) {
+ $this->metricsDocumentation = $report['metricsDocumentation'];
+ }
+
+ if (isset($report['documentation'])) {
+ $this->documentation = $report['documentation'];
+ }
+ }
+
+ /**
+ * Sets the columns that will be displayed in the HTML output
+ * By default all columns are displayed ($columnsNames = array() will display all columns)
+ *
+ * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits')
+ */
+ public function setColumnsToDisplay($columnsNames)
+ {
+ if (!is_array($columnsNames)) {
+ if (strpos($columnsNames, ',') !== false) {
+ // array values are comma separated
+ $columnsNames = explode(',', $columnsNames);
+ } else {
+ $columnsNames = array($columnsNames);
+ }
+ }
+ $this->columnsToDisplay = array_filter($columnsNames);
+ }
+
+ /**
+ * Returns columns names to display, in order.
+ * If no columns were specified to be displayed, return all columns found in the first row.
+ * If the data table has empty_columns meta data set, those columns will be removed.
+ * @param array PHP array conversion of the data table
+ * @return array
+ */
+ public function getColumnsToDisplay()
+ {
+ if (empty($this->columnsToDisplay)) {
+ $row = $this->dataTable->getFirstRow();
+ if (empty($row)) {
+ return array();
+ }
+
+ return array_keys($row->getColumns());
+ }
+
+ $this->columnsToDisplay = array_filter($this->columnsToDisplay);
+
+ if ($this->dataTable instanceof Piwik_DataTable_Array) {
+ $emptyColumns = $this->dataTable->getMetadataIntersectArray(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME);
+ } else {
+ $emptyColumns = $this->dataTable->getMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME);
+ }
+ if (is_array($emptyColumns)) {
+ foreach ($emptyColumns as $emptyColumn) {
+ $key = array_search($emptyColumn, $this->columnsToDisplay);
+ if ($key !== false) {
+ unset($this->columnsToDisplay[$key]);
+ }
+ }
+ $this->columnsToDisplay = array_values($this->columnsToDisplay);
+ }
+
+ return $this->columnsToDisplay;
+ }
+
+ /**
+ * Set whether to highlight the summary row or not. If not highlighted, it will
+ * look like every other row.
+ */
+ public function setHighlightSummaryRow($highlightSummaryRow)
+ {
+ $this->viewProperties['highlight_summary_row'] = $highlightSummaryRow;
+ }
+
+ /**
+ * Sets the name of the metadata to use for a custom tooltip.
+ */
+ public function setTooltipMetadataName($metadataName)
+ {
+ $this->viewProperties['tooltip_metadata_name'] = $metadataName;
+ }
+
+ /**
+ * Sets columns translations array.
+ *
+ * @param array $columnsTranslations An associative array indexed by column names, eg. array('nb_visit'=>"Numer of visits")
+ */
+ public function setColumnsTranslations($columnsTranslations)
+ {
+ $this->columnsTranslations += $columnsTranslations;
+ }
+
+ /**
+ * Sets a custom parameter, that will be printed in the javascript array associated with each datatable
+ *
+ * @param string $parameter name
+ * @param mixed $value
+ * @throws Exception
+ */
+ public function setCustomParameter($parameter, $value)
+ {
+ if (isset($this->variablesDefault[$parameter])) {
+ throw new Exception("$parameter is already defined for this DataTable.");
+ }
+ $this->variablesDefault[$parameter] = $value;
+ }
+
+ /**
+ * Queues a Datatable filter, that will be applied once the datatable is loaded from the API.
+ * Useful when the controller needs to add columns, or decorate existing columns, when these filters don't
+ * necessarily make sense directly in the API.
+ *
+ * @param string $filterName
+ * @param mixed $parameters
+ * @param bool $runBeforeGenericFilters Set to true if the filter will delete rows from the table,
+ * and should therefore be ran before Sort, Limit, etc.
+ * @return void
+ */
+ public function queueFilter($filterName, $parameters, $runBeforeGenericFilters = false)
+ {
+ if ($runBeforeGenericFilters) {
+ $this->queuedFiltersPriority[] = array($filterName, $parameters);
+ } else {
+ $this->queuedFilters[] = array($filterName, $parameters);
+ }
+ }
+
+ /**
+ * Adds one report to the set of reports that are related to this one. Related reports
+ * are displayed in the footer as links. When they are clicked, the report will change to
+ * the related report.
+ *
+ * Make sure to call setReportTitle so this report will be displayed correctly.
+ *
+ * @param string $module The report's controller name, ie, 'UserSettings'.
+ * @param string $action The report's controller action, ie, 'getBrowser'.
+ * @param string $title The text used to describe the related report.
+ * @param array $queryParams Any specific query params to use when loading the report.
+ * This can be used to, for example, make a goal report a related
+ * report (by adding an idGoal parameter).
+ */
+ public function addRelatedReport($module, $action, $title, $queryParams = array())
+ {
+ // don't add the related report if it references this report
+ if ($this->currentControllerName == $module && $this->currentControllerAction == $action) {
+ return;
+ }
+
+ $url = $this->getBaseReportUrl($module, $action, $queryParams);
+ $this->viewProperties['relatedReports'][$url] = $title;
+ }
+
+ /**
+ * Adds a set of reports that are related to this one. Related reports are displayed in
+ * the footer as links. When they are clicked, the report will change to the related report.
+ *
+ * If you need to associate specific query params with a report, use the addRelatedReport
+ * method instead of this one.
+ *
+ * @param string $thisReportTitle The title of this report.
+ * @param array $relatedReports An array mapping report IDs ('Controller.methodName') with
+ * display text.
+ */
+ public function addRelatedReports($thisReportTitle, $relatedReports)
+ {
+ $this->setReportTitle($thisReportTitle);
+ foreach ($relatedReports as $report => $title) {
+ list($module, $action) = explode('.', $report);
+ $this->addRelatedReport($module, $action, $title);
+ }
+ }
+
+ /**
+ * Sets the title of this report.
+ *
+ * @param string $title
+ */
+ public function setReportTitle($title)
+ {
+ $this->viewProperties['title'] = $title;
+ }
+
+ /**
+ * Sets a custom URL to use to reference this report.
+ *
+ * @param string $url
+ */
+ public function setReportUrl($module, $action, $queryParams = array())
+ {
+ $this->viewProperties['self_url'] = $this->getBaseReportUrl($module, $action, $queryParams);
+ }
+
+ /**
+ * Returns true if it is likely that the data for this report has been purged and if the
+ * user should be told about that.
+ *
+ * In order for this function to return true, the following must also be true:
+ * - The data table for this report must either be empty or not have been fetched.
+ * - The period of this report is not a multiple period.
+ * - The date of this report must be older than the delete_reports_older_than config option.
+ * @return bool
+ */
+ public function hasReportBeenPurged()
+ {
+ $strPeriod = Piwik_Common::getRequestVar('period', false);
+ $strDate = Piwik_Common::getRequestVar('date', false);
+
+ if ($strPeriod !== false
+ && $strDate !== false
+ && (is_null($this->dataTable) || $this->dataTable->getRowsCount() == 0)
+ ) {
+ // if range, only look at the first date
+ if ($strPeriod == 'range') {
+ $idSite = Piwik_Common::getRequestVar('idSite', '');
+ if (intval($idSite) != 0) {
+ $site = new Piwik_Site($idSite);
+ $timezone = $site->getTimezone();
+ } else {
+ $timezone = 'UTC';
+ }
+
+ $period = new Piwik_Period_Range('range', $strDate, $timezone);
+ $reportDate = $period->getDateStart();
+ } // if a multiple period, this function is irrelevant
+ else if (Piwik_Archive::isMultiplePeriod($strDate, $strPeriod)) {
+ return false;
+ } // otherwise, use the date as given
+ else {
+ $reportDate = Piwik_Date::factory($strDate);
+ }
+
+ $reportYear = $reportDate->toString('Y');
+ $reportMonth = $reportDate->toString('m');
+
+ if (class_exists('Piwik_PrivacyManager')
+ && Piwik_PrivacyManager::shouldReportBePurged($reportYear, $reportMonth)
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns URL for this report w/o any filter parameters.
+ *
+ * @param string $module
+ * @param string $action
+ * @param array $queryParams
+ */
+ private function getBaseReportUrl($module, $action, $queryParams = array())
+ {
+ $params = array_merge($queryParams, array('module' => $module, 'action' => $action));
+
+ // unset all filter query params so the related report will show up in its default state,
+ // unless the filter param was in $queryParams
+ $genericFiltersInfo = Piwik_API_DataTableGenericFilter::getGenericFiltersInformation();
+ foreach ($genericFiltersInfo as $filter) {
+ foreach ($filter as $queryParamName => $queryParamInfo) {
+ if (!isset($params[$queryParamName])) {
+ $params[$queryParamName] = null;
+ }
+ }
+ }
+
+ // add the related report
+ $url = Piwik_Url::getCurrentQueryStringWithParametersModified($params);
+ return $url;
+ }
}
diff --git a/core/ViewDataTable/Cloud.php b/core/ViewDataTable/Cloud.php
index ec83741dc8..7b106f3a24 100644
--- a/core/ViewDataTable/Cloud.php
+++ b/core/ViewDataTable/Cloud.php
@@ -18,61 +18,60 @@
*/
class Piwik_ViewDataTable_Cloud extends Piwik_ViewDataTable
{
- protected $displayLogoInsteadOfLabel = false;
+ protected $displayLogoInsteadOfLabel = false;
- public function setDisplayLogoInTagCloud($bool)
- {
- $this->displayLogoInsteadOfLabel = $bool;
- }
-
- protected function getViewDataTableId()
- {
- return 'cloud';
- }
+ public function setDisplayLogoInTagCloud($bool)
+ {
+ $this->displayLogoInsteadOfLabel = $bool;
+ }
- /**
- * @see Piwik_ViewDataTable::init()
- * @param string $currentControllerName
- * @param string $currentControllerAction
- * @param string $apiMethodToRequestDataTable
- * @param null|string $controllerActionCalledWhenRequestSubTable
- */
- function init($currentControllerName,
- $currentControllerAction,
- $apiMethodToRequestDataTable,
- $controllerActionCalledWhenRequestSubTable = null)
- {
- parent::init($currentControllerName,
- $currentControllerAction,
- $apiMethodToRequestDataTable,
- $controllerActionCalledWhenRequestSubTable);
- $this->dataTableTemplate = 'CoreHome/templates/cloud.tpl';
- $this->disableOffsetInformation();
- $this->disableExcludeLowPopulation();
- }
-
- /**
- * @see Piwik_ViewDataTable::main()
- *
- * @return null
- */
- public function main()
- {
- if($this->mainAlreadyExecuted)
- {
- return;
- }
- $this->mainAlreadyExecuted = true;
+ protected function getViewDataTableId()
+ {
+ return 'cloud';
+ }
- $this->isDataAvailable = true;
- try {
- $this->loadDataTableFromAPI();
- } catch(Exception $e) {
- $this->isDataAvailable = false;
- }
- $this->checkStandardDataTable();
- $this->view = $this->buildView();
- }
+ /**
+ * @see Piwik_ViewDataTable::init()
+ * @param string $currentControllerName
+ * @param string $currentControllerAction
+ * @param string $apiMethodToRequestDataTable
+ * @param null|string $controllerActionCalledWhenRequestSubTable
+ */
+ function init($currentControllerName,
+ $currentControllerAction,
+ $apiMethodToRequestDataTable,
+ $controllerActionCalledWhenRequestSubTable = null)
+ {
+ parent::init($currentControllerName,
+ $currentControllerAction,
+ $apiMethodToRequestDataTable,
+ $controllerActionCalledWhenRequestSubTable);
+ $this->dataTableTemplate = 'CoreHome/templates/cloud.tpl';
+ $this->disableOffsetInformation();
+ $this->disableExcludeLowPopulation();
+ }
+
+ /**
+ * @see Piwik_ViewDataTable::main()
+ *
+ * @return null
+ */
+ public function main()
+ {
+ if ($this->mainAlreadyExecuted) {
+ return;
+ }
+ $this->mainAlreadyExecuted = true;
+
+ $this->isDataAvailable = true;
+ try {
+ $this->loadDataTableFromAPI();
+ } catch (Exception $e) {
+ $this->isDataAvailable = false;
+ }
+ $this->checkStandardDataTable();
+ $this->view = $this->buildView();
+ }
/**
* Returns the name of the first numeric column to be displayed
@@ -81,61 +80,54 @@ class Piwik_ViewDataTable_Cloud extends Piwik_ViewDataTable
* @return string
*/
public function getColumnToDisplay()
- {
- $columns = parent::getColumnsToDisplay();
- // not label, but the first numeric column
- return $columns[1];
- }
-
- protected function buildView()
- {
- $view = new Piwik_View($this->dataTableTemplate);
- if(!$this->isDataAvailable)
- {
- $view->cloudValues = array();
- }
- else
- {
- $columnToDisplay = $this->getColumnToDisplay();
- $columnTranslation = $this->getColumnTranslation($columnToDisplay);
- $values = $this->dataTable->getColumn($columnToDisplay);
- $labels = $this->dataTable->getColumn('label');
- $labelMetadata = array();
- foreach($this->dataTable->getRows() as $row)
- {
- $logo = false;
- if($this->displayLogoInsteadOfLabel)
- {
- $logo = $row->getMetadata('logo');
- }
- $labelMetadata[$row->getColumn('label')] = array(
- 'logo' => $logo,
- 'url' => $row->getMetadata('url'),
- );
- }
- $cloud = new Piwik_Visualization_Cloud();
- foreach($labels as $i => $label)
- {
- $cloud->addWord($label, $values[$i]);
- }
- $cloudValues = $cloud->render('array');
- foreach($cloudValues as &$value)
- {
- $value['logoWidth'] = round(max(16, $value['percent']));
- }
- $view->columnTranslation = $columnTranslation;
- $view->labelMetadata = $labelMetadata;
- $view->cloudValues = $cloudValues;
- }
- $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet();
- $view->properties = $this->getViewProperties();
- $view->reportDocumentation = $this->getReportDocumentation();
-
- // if it's likely that the report data for this data table has been purged,
- // set whether we should display a message to that effect.
- $view->showReportDataWasPurgedMessage = $this->hasReportBeenPurged();
- $view->deleteReportsOlderThan = Piwik_GetOption('delete_reports_older_than');
-
- return $view;
- }
+ {
+ $columns = parent::getColumnsToDisplay();
+ // not label, but the first numeric column
+ return $columns[1];
+ }
+
+ protected function buildView()
+ {
+ $view = new Piwik_View($this->dataTableTemplate);
+ if (!$this->isDataAvailable) {
+ $view->cloudValues = array();
+ } else {
+ $columnToDisplay = $this->getColumnToDisplay();
+ $columnTranslation = $this->getColumnTranslation($columnToDisplay);
+ $values = $this->dataTable->getColumn($columnToDisplay);
+ $labels = $this->dataTable->getColumn('label');
+ $labelMetadata = array();
+ foreach ($this->dataTable->getRows() as $row) {
+ $logo = false;
+ if ($this->displayLogoInsteadOfLabel) {
+ $logo = $row->getMetadata('logo');
+ }
+ $labelMetadata[$row->getColumn('label')] = array(
+ 'logo' => $logo,
+ 'url' => $row->getMetadata('url'),
+ );
+ }
+ $cloud = new Piwik_Visualization_Cloud();
+ foreach ($labels as $i => $label) {
+ $cloud->addWord($label, $values[$i]);
+ }
+ $cloudValues = $cloud->render('array');
+ foreach ($cloudValues as &$value) {
+ $value['logoWidth'] = round(max(16, $value['percent']));
+ }
+ $view->columnTranslation = $columnTranslation;
+ $view->labelMetadata = $labelMetadata;
+ $view->cloudValues = $cloudValues;
+ }
+ $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet();
+ $view->properties = $this->getViewProperties();
+ $view->reportDocumentation = $this->getReportDocumentation();
+
+ // if it's likely that the report data for this data table has been purged,
+ // set whether we should display a message to that effect.
+ $view->showReportDataWasPurgedMessage = $this->hasReportBeenPurged();
+ $view->deleteReportsOlderThan = Piwik_GetOption('delete_reports_older_than');
+
+ return $view;
+ }
}
diff --git a/core/ViewDataTable/GenerateGraphData.php b/core/ViewDataTable/GenerateGraphData.php
index ad1779b64b..51b2956cfc 100644
--- a/core/ViewDataTable/GenerateGraphData.php
+++ b/core/ViewDataTable/GenerateGraphData.php
@@ -15,15 +15,15 @@
* You can set the number of elements to appear in the graph using: setGraphLimit();
* Example:
* <pre>
- * function getWebsites( $fetch = false)
- * {
- * $view = Piwik_ViewDataTable::factory();
- * $view->init( $this->pluginName, 'getWebsites', 'Referers.getWebsites', 'getUrlsFromWebsiteId' );
- * $view->setColumnsToDisplay( array('label','nb_visits') );
- * $view->setLimit(10);
- * $view->setGraphLimit(12);
- * return $this->renderView($view, $fetch);
- * }
+ * function getWebsites( $fetch = false)
+ * {
+ * $view = Piwik_ViewDataTable::factory();
+ * $view->init( $this->pluginName, 'getWebsites', 'Referers.getWebsites', 'getUrlsFromWebsiteId' );
+ * $view->setColumnsToDisplay( array('label','nb_visits') );
+ * $view->setLimit(10);
+ * $view->setGraphLimit(12);
+ * return $this->renderView($view, $fetch);
+ * }
* </pre>
*
* @package Piwik
@@ -31,223 +31,212 @@
*/
abstract class Piwik_ViewDataTable_GenerateGraphData extends Piwik_ViewDataTable
{
- /**
- * Number of elements to display in the graph.
- * @var int
- */
- protected $graphLimit = null;
- protected $yAxisUnit = '';
-
- // used for the series picker
- protected $selectableColumns = array();
-
- public function setAxisYUnit($unit)
- {
- $this->yAxisUnit = $unit;
- }
-
- /**
- * Sets the number max of elements to display (number of pie slice, vertical bars, etc.)
- * If the data has more elements than $limit then the last part of the data will be the sum of all the remaining data.
- *
- * @param int $limit
- */
- public function setGraphLimit( $limit )
- {
- $this->graphLimit = $limit;
- }
-
- /**
- * Returns numbers of elemnts to display in the graph
- *
- * @return int
- */
- public function getGraphLimit()
- {
- return $this->graphLimit;
- }
-
- protected $displayPercentageInTooltip = true;
-
- /**
- * The percentage in tooltips is computed based on the sum of all values for the plotted column.
- * If the sum of the column in the data set is not the number of elements in the data set,
- * for example when plotting visits that have a given plugin enabled:
- * one visit can have several plugins, hence the sum is much greater than the number of visits.
- * In this case displaying the percentage doesn't make sense.
- */
- public function disallowPercentageInGraphTooltip()
- {
- $this->displayPercentageInTooltip = false;
- }
-
- /**
- * Sets the columns that can be added/removed by the user
- * This is done on data level (not html level) because the columns might change after reloading via sparklines
- * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits')
- */
- public function setSelectableColumns($columnsNames)
- {
- // the array contains values if enableShowGoals() has been used
- // add $columnsNames to the beginning of the array
- $this->selectableColumns = array_merge($columnsNames, $this->selectableColumns);
- }
-
- /**
- * The implementation of this method in Piwik_ViewDataTable passes to the graph whether the
- * goals icon should be displayed or not. Here, we use it to implicitly add the goal metrics
- * to the metrics picker.
- */
- public function enableShowGoals()
- {
- parent::enableShowGoals();
-
- $goalMetrics = array('nb_conversions', 'revenue');
- $this->selectableColumns = array_merge($this->selectableColumns, $goalMetrics);
-
- $this->setColumnTranslation('nb_conversions', Piwik_Translate('Goals_ColumnConversions'));
- $this->setColumnTranslation('revenue', Piwik_Translate('General_TotalRevenue'));
- }
-
- /**
- * Used in initChartObjectData to add the series picker config to the view object
- * @param bool $multiSelect
- */
- protected function addSeriesPickerToView($multiSelect=true)
- {
- if (count($this->selectableColumns)
- && Piwik_Common::getRequestVar('showSeriesPicker', 1) == 1)
- {
- // build the final configuration for the series picker
- $columnsToDisplay = $this->getColumnsToDisplay();
- $selectableColumns = array();
-
- foreach ($this->selectableColumns as $column)
- {
- $selectableColumns[] = array(
- 'column' => $column,
- 'translation' => $this->getColumnTranslation($column),
- 'displayed' => in_array($column, $columnsToDisplay)
- );
- }
- $this->view->setSelectableColumns($selectableColumns, $multiSelect);
- }
- }
-
- protected function getUnitsForColumnsToDisplay()
- {
- // derive units from column names
- $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
- $units = $this->deriveUnitsFromRequestedColumnNames($this->getColumnsToDisplay(), $idSite);
- if(!empty($this->yAxisUnit))
- {
- // force unit to the value set via $this->setAxisYUnit()
- foreach ($units as &$unit)
- {
- $unit = $this->yAxisUnit;
- }
- }
-
- return $units;
- }
-
- protected function deriveUnitsFromRequestedColumnNames($requestedColumnNames, $idSite)
- {
- $units = array();
- foreach($requestedColumnNames as $columnName)
- {
- $derivedUnit = Piwik_API_API::getUnit($columnName, $idSite);
- $units[$columnName] = empty($derivedUnit) ? false : $derivedUnit;
- }
- return $units;
- }
-
- public function main()
- {
- if($this->mainAlreadyExecuted)
- {
- return;
- }
- $this->mainAlreadyExecuted = true;
-
- // Graphs require the full dataset, setting limit to null (same as 'no limit')
- $this->setLimit(null);
-
- // the queued filters will be manually applied later. This is to ensure that filtering using search
- // will be done on the table before the labels are enhanced (see ReplaceColumnNames)
- $this->disableQueuedFilters();
-
- // throws exception if no view access
- $this->loadDataTableFromAPI();
- $this->checkStandardDataTable();
- $this->postDataTableLoadedFromAPI();
-
- $graphLimit = $this->getGraphLimit();
- if(!empty($graphLimit))
- {
- $offsetStartSummary = $this->getGraphLimit() - 1;
- $this->dataTable->filter('AddSummaryRow',
- array($offsetStartSummary,
- Piwik_Translate('General_Others'),
-
- // Column to sort by, before truncation
- $this->dataTable->getSortedByColumnName()
- ? $this->dataTable->getSortedByColumnName()
- : Piwik_Archive::INDEX_NB_VISITS
- )
- );
- }
- $this->isDataAvailable = $this->dataTable->getRowsCount() != 0;
-
- // if addTotalRow was called in GenerateGraphHTML, add a row containing totals of
- // different metrics
- if (Piwik_Common::getRequestVar('add_total_row', 0) == 1)
- {
- $this->dataTable->queueFilter('AddSummaryRow', array(0, Piwik_Translate('General_Total'), null, false));
- }
-
- if($this->isDataAvailable)
- {
- $this->initChartObjectData();
- }
- $this->view->customizeChartProperties();
- }
-
- protected function initChartObjectData()
- {
- $this->dataTable->applyQueuedFilters();
-
- // We apply a filter to the DataTable, decoding the label column (useful for keywords for example)
- $this->dataTable->filter('ColumnCallbackReplace', array('label','urldecode'));
-
- $xLabels = $this->dataTable->getColumn('label');
- $columnNames = parent::getColumnsToDisplay();
- if(($labelColumnFound = array_search('label',$columnNames)) !== false)
- {
- unset($columnNames[$labelColumnFound]);
- }
-
- $columnNameToTranslation = $columnNameToValue = array();
- foreach($columnNames as $columnName)
- {
- $columnNameToTranslation[$columnName] = $this->getColumnTranslation($columnName);
- $columnNameToValue[$columnName] = $this->dataTable->getColumn($columnName);
- }
- $this->view->setAxisXLabels($xLabels);
- $this->view->setAxisYValues($columnNameToValue);
- $this->view->setAxisYLabels($columnNameToTranslation);
- $this->view->setAxisYUnit($this->yAxisUnit);
- $this->view->setDisplayPercentageInTooltip($this->displayPercentageInTooltip);
-
- // show_all_ticks is not real query param, it is set by GenerateGraphHTML.
- if (Piwik_Common::getRequestVar('show_all_ticks', 0) == 1)
- {
- $this->view->showAllTicks();
- }
-
- $units = $this->getUnitsForColumnsToDisplay();
- $this->view->setAxisYUnits($units);
-
- $this->addSeriesPickerToView();
- }
+ /**
+ * Number of elements to display in the graph.
+ * @var int
+ */
+ protected $graphLimit = null;
+ protected $yAxisUnit = '';
+
+ // used for the series picker
+ protected $selectableColumns = array();
+
+ public function setAxisYUnit($unit)
+ {
+ $this->yAxisUnit = $unit;
+ }
+
+ /**
+ * Sets the number max of elements to display (number of pie slice, vertical bars, etc.)
+ * If the data has more elements than $limit then the last part of the data will be the sum of all the remaining data.
+ *
+ * @param int $limit
+ */
+ public function setGraphLimit($limit)
+ {
+ $this->graphLimit = $limit;
+ }
+
+ /**
+ * Returns numbers of elemnts to display in the graph
+ *
+ * @return int
+ */
+ public function getGraphLimit()
+ {
+ return $this->graphLimit;
+ }
+
+ protected $displayPercentageInTooltip = true;
+
+ /**
+ * The percentage in tooltips is computed based on the sum of all values for the plotted column.
+ * If the sum of the column in the data set is not the number of elements in the data set,
+ * for example when plotting visits that have a given plugin enabled:
+ * one visit can have several plugins, hence the sum is much greater than the number of visits.
+ * In this case displaying the percentage doesn't make sense.
+ */
+ public function disallowPercentageInGraphTooltip()
+ {
+ $this->displayPercentageInTooltip = false;
+ }
+
+ /**
+ * Sets the columns that can be added/removed by the user
+ * This is done on data level (not html level) because the columns might change after reloading via sparklines
+ * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits')
+ */
+ public function setSelectableColumns($columnsNames)
+ {
+ // the array contains values if enableShowGoals() has been used
+ // add $columnsNames to the beginning of the array
+ $this->selectableColumns = array_merge($columnsNames, $this->selectableColumns);
+ }
+
+ /**
+ * The implementation of this method in Piwik_ViewDataTable passes to the graph whether the
+ * goals icon should be displayed or not. Here, we use it to implicitly add the goal metrics
+ * to the metrics picker.
+ */
+ public function enableShowGoals()
+ {
+ parent::enableShowGoals();
+
+ $goalMetrics = array('nb_conversions', 'revenue');
+ $this->selectableColumns = array_merge($this->selectableColumns, $goalMetrics);
+
+ $this->setColumnTranslation('nb_conversions', Piwik_Translate('Goals_ColumnConversions'));
+ $this->setColumnTranslation('revenue', Piwik_Translate('General_TotalRevenue'));
+ }
+
+ /**
+ * Used in initChartObjectData to add the series picker config to the view object
+ * @param bool $multiSelect
+ */
+ protected function addSeriesPickerToView($multiSelect = true)
+ {
+ if (count($this->selectableColumns)
+ && Piwik_Common::getRequestVar('showSeriesPicker', 1) == 1
+ ) {
+ // build the final configuration for the series picker
+ $columnsToDisplay = $this->getColumnsToDisplay();
+ $selectableColumns = array();
+
+ foreach ($this->selectableColumns as $column) {
+ $selectableColumns[] = array(
+ 'column' => $column,
+ 'translation' => $this->getColumnTranslation($column),
+ 'displayed' => in_array($column, $columnsToDisplay)
+ );
+ }
+ $this->view->setSelectableColumns($selectableColumns, $multiSelect);
+ }
+ }
+
+ protected function getUnitsForColumnsToDisplay()
+ {
+ // derive units from column names
+ $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
+ $units = $this->deriveUnitsFromRequestedColumnNames($this->getColumnsToDisplay(), $idSite);
+ if (!empty($this->yAxisUnit)) {
+ // force unit to the value set via $this->setAxisYUnit()
+ foreach ($units as &$unit) {
+ $unit = $this->yAxisUnit;
+ }
+ }
+
+ return $units;
+ }
+
+ protected function deriveUnitsFromRequestedColumnNames($requestedColumnNames, $idSite)
+ {
+ $units = array();
+ foreach ($requestedColumnNames as $columnName) {
+ $derivedUnit = Piwik_API_API::getUnit($columnName, $idSite);
+ $units[$columnName] = empty($derivedUnit) ? false : $derivedUnit;
+ }
+ return $units;
+ }
+
+ public function main()
+ {
+ if ($this->mainAlreadyExecuted) {
+ return;
+ }
+ $this->mainAlreadyExecuted = true;
+
+ // Graphs require the full dataset, setting limit to null (same as 'no limit')
+ $this->setLimit(null);
+
+ // the queued filters will be manually applied later. This is to ensure that filtering using search
+ // will be done on the table before the labels are enhanced (see ReplaceColumnNames)
+ $this->disableQueuedFilters();
+
+ // throws exception if no view access
+ $this->loadDataTableFromAPI();
+ $this->checkStandardDataTable();
+ $this->postDataTableLoadedFromAPI();
+
+ $graphLimit = $this->getGraphLimit();
+ if (!empty($graphLimit)) {
+ $offsetStartSummary = $this->getGraphLimit() - 1;
+ $this->dataTable->filter('AddSummaryRow',
+ array($offsetStartSummary,
+ Piwik_Translate('General_Others'),
+
+ // Column to sort by, before truncation
+ $this->dataTable->getSortedByColumnName()
+ ? $this->dataTable->getSortedByColumnName()
+ : Piwik_Archive::INDEX_NB_VISITS
+ )
+ );
+ }
+ $this->isDataAvailable = $this->dataTable->getRowsCount() != 0;
+
+ // if addTotalRow was called in GenerateGraphHTML, add a row containing totals of
+ // different metrics
+ if (Piwik_Common::getRequestVar('add_total_row', 0) == 1) {
+ $this->dataTable->queueFilter('AddSummaryRow', array(0, Piwik_Translate('General_Total'), null, false));
+ }
+
+ if ($this->isDataAvailable) {
+ $this->initChartObjectData();
+ }
+ $this->view->customizeChartProperties();
+ }
+
+ protected function initChartObjectData()
+ {
+ $this->dataTable->applyQueuedFilters();
+
+ // We apply a filter to the DataTable, decoding the label column (useful for keywords for example)
+ $this->dataTable->filter('ColumnCallbackReplace', array('label', 'urldecode'));
+
+ $xLabels = $this->dataTable->getColumn('label');
+ $columnNames = parent::getColumnsToDisplay();
+ if (($labelColumnFound = array_search('label', $columnNames)) !== false) {
+ unset($columnNames[$labelColumnFound]);
+ }
+
+ $columnNameToTranslation = $columnNameToValue = array();
+ foreach ($columnNames as $columnName) {
+ $columnNameToTranslation[$columnName] = $this->getColumnTranslation($columnName);
+ $columnNameToValue[$columnName] = $this->dataTable->getColumn($columnName);
+ }
+ $this->view->setAxisXLabels($xLabels);
+ $this->view->setAxisYValues($columnNameToValue);
+ $this->view->setAxisYLabels($columnNameToTranslation);
+ $this->view->setAxisYUnit($this->yAxisUnit);
+ $this->view->setDisplayPercentageInTooltip($this->displayPercentageInTooltip);
+
+ // show_all_ticks is not real query param, it is set by GenerateGraphHTML.
+ if (Piwik_Common::getRequestVar('show_all_ticks', 0) == 1) {
+ $this->view->showAllTicks();
+ }
+
+ $units = $this->getUnitsForColumnsToDisplay();
+ $this->view->setAxisYUnits($units);
+
+ $this->addSeriesPickerToView();
+ }
}
diff --git a/core/ViewDataTable/GenerateGraphData/ChartEvolution.php b/core/ViewDataTable/GenerateGraphData/ChartEvolution.php
index a505cff5dc..65d7988216 100644
--- a/core/ViewDataTable/GenerateGraphData/ChartEvolution.php
+++ b/core/ViewDataTable/GenerateGraphData/ChartEvolution.php
@@ -1,340 +1,309 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Piwik_ViewDataTable_GenerateGraphData for the Evolution graph (eg. Last 30 days visits) using Piwik_Visualization_Chart_Evolution
- *
+ *
* @package Piwik
* @subpackage Piwik_ViewDataTable
*/
class Piwik_ViewDataTable_GenerateGraphData_ChartEvolution extends Piwik_ViewDataTable_GenerateGraphData
{
-
- // used for the row picker
- // (the series picker configuration resides in the parent class)
- protected $rowPicker = false;
- protected $visibleRows = array();
- protected $rowPickerConfig = array();
-
- protected function checkStandardDataTable()
- {
- // DataTable_Array and DataTable allowed for the evolution chart
- Piwik::checkObjectTypeIs($this->dataTable, array('Piwik_DataTable_Array', 'Piwik_DataTable'));
- }
-
- protected function getViewDataTableId()
- {
- return 'generateDataChartEvolution';
- }
-
- function __construct()
- {
- $this->view = new Piwik_Visualization_Chart_Evolution();
- }
-
- /**
- * Adds the same series picker as parent::setSelectableColumns but the selectable series are not
- * columns of a single row but the same column across multiple rows, e.g. the number of visits
- * for each referrer type.
- * @param array $visibleRows the rows that are initially visible
- * @param string $matchBy the way the items in $visibleRows are matched with the data. possible values:
- * - label: matches the label of the row
- */
- public function addRowPicker($visibleRows, $matchBy='label')
- {
- $this->rowPicker = $matchBy;
-
- if (!is_array($visibleRows))
- {
- $visibleRows = array($visibleRows);
- }
- $this->visibleRows = $visibleRows;
- }
-
- /**
- * This method is called for every row of every table in the DataTable_Array.
- * It incrementally builds the row picker configuration and determines whether
- * the row is initially visible or not.
- * @param string $rowLabel
- * @return bool
- */
- protected function handleRowForRowPicker(&$rowLabel)
- {
- // determine whether row is visible
- $isVisible = true;
- switch ($this->rowPicker)
- {
- case 'label':
- $isVisible = in_array($rowLabel, $this->visibleRows);
- break;
- }
-
- // build config
- if (!isset($this->rowPickerConfig[$rowLabel]))
- {
- $this->rowPickerConfig[$rowLabel] = array(
- 'label' => $rowLabel,
- 'matcher' => $rowLabel,
- 'displayed' => $isVisible
- );
- }
-
- return $isVisible;
- }
-
- protected function loadDataTableFromAPI()
- {
- $period = Piwik_Common::getRequestVar('period');
- // period will be overridden when 'range' is requested in the UI
- // but the graph will display for each day of the range.
- // Default 'range' behavior is to return the 'sum' for the range
- if($period == 'range')
- {
- $_GET['period'] = 'day';
- }
- // throws exception if no view access
- parent::loadDataTableFromAPI();
- if($period == 'range')
- {
- $_GET['period'] = $period;
- }
- }
-
- protected function initChartObjectData()
- {
- // if the loaded datatable is a simple DataTable, it is most likely a plugin plotting some custom data
- // we don't expect plugin developers to return a well defined Piwik_DataTable_Array
- if($this->dataTable instanceof Piwik_DataTable)
- {
- return parent::initChartObjectData();
- }
-
- $this->dataTable->applyQueuedFilters();
- if(!($this->dataTable instanceof Piwik_DataTable_Array))
- {
- throw new Exception("Expecting a DataTable_Array with custom format to draw an evolution chart");
- }
-
- // the X label is extracted from the 'period' object in the table's metadata
- $xLabels = $uniqueIdsDataTable = array();
- foreach($this->dataTable->getArray() as $idDataTable => $metadataDataTable)
- {
- //eg. "Aug 2009"
- $xLabels[] = $metadataDataTable->getMetadata('period')->getLocalizedShortString();
- // we keep track of all unique data table that we need to set a Y value for
- $uniqueIdsDataTable[] = $idDataTable;
- }
-
- $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
- $requestedColumnNames = $this->getColumnsToDisplay();
- $units = $this->getUnitsForColumnsToDisplay();
-
- $yAxisLabelToUnit = array();
- $yAxisLabelToValue = array();
- foreach($this->dataTable->getArray() as $idDataTable => $dataTable)
- {
- foreach($dataTable->getRows() as $row)
- {
- $rowLabel = $row->getColumn('label');
-
- // put together configuration for row picker.
- // do this for every data table in the array because rows do not
- // have to present for each date.
- if ($this->rowPicker !== false)
- {
- $rowVisible = $this->handleRowForRowPicker($rowLabel);
- if (!$rowVisible)
- {
- continue;
- }
- }
-
- // build data for request columns
- foreach($requestedColumnNames as $requestedColumnName)
- {
- $yAxisLabel = $this->getSeriesLabel($rowLabel, $requestedColumnName);
- if(($columnValue = $row->getColumn($requestedColumnName)) !== false)
- {
- $yAxisLabelToValue[$yAxisLabel][$idDataTable] = $columnValue;
- $yAxisLabelToUnit[$yAxisLabel] = $units[$requestedColumnName];
- }
- }
- }
- }
-
- // make sure all column values are set to at least zero (no gap in the graph)
- $yAxisLabelToValueCleaned = array();
- foreach($uniqueIdsDataTable as $uniqueIdDataTable)
- {
- foreach($yAxisLabelToValue as $yAxisLabel => $idDataTableToColumnValue)
- {
- if(isset($idDataTableToColumnValue[$uniqueIdDataTable]))
- {
- $columnValue = $idDataTableToColumnValue[$uniqueIdDataTable];
- }
- else
- {
- $columnValue = 0;
- }
- $yAxisLabelToValueCleaned[$yAxisLabel][] = $columnValue;
- }
- }
-
- $this->view->setAxisXLabels($xLabels);
- $this->view->setAxisYValues($yAxisLabelToValueCleaned);
- $this->view->setAxisYUnits($yAxisLabelToUnit);
-
- $countGraphElements = $this->dataTable->getRowsCount();
- $dataTables = $this->dataTable->getArray();
- $firstDatatable = reset($dataTables);
- $period = $firstDatatable->getMetadata('period');
-
- $stepSize = $this->getXAxisStepSize($period->getLabel(), $countGraphElements);
- $this->view->setXSteps($stepSize);
-
- if($this->isLinkEnabled())
- {
- $axisXOnClick = array();
- $queryStringAsHash = $this->getQueryStringAsHash();
- foreach($this->dataTable->getArray() as $idDataTable => $metadataDataTable)
- {
- $period = $metadataDataTable->getMetadata('period');
- $dateInUrl = $period->getDateStart();
- $parameters = array(
- 'idSite' => $idSite,
- 'period' => $period->getLabel(),
- 'date' => $dateInUrl->toString(),
- 'segment' => Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', false))
- );
- $hash = '';
- if(!empty($queryStringAsHash))
- {
- $hash = '#' . Piwik_Url::getQueryStringFromParameters( $queryStringAsHash + $parameters);
- }
- $link = 'index.php?' .
- Piwik_Url::getQueryStringFromParameters( array(
- 'module' => 'CoreHome',
- 'action' => 'index',
- ) + $parameters)
- . $hash;
- $axisXOnClick[] = $link;
- }
- $this->view->setAxisXOnClick($axisXOnClick);
- }
-
- $this->addSeriesPickerToView();
- if ($this->rowPicker !== false)
- {
- // configure the row picker
- $this->view->setSelectableRows(array_values($this->rowPickerConfig));
- }
- }
-
- /**
- * Derive the series label from the row label and the column name.
- * If the row label is set, both the label and the column name are displayed.
- * @param string $rowLabel
- * @param string $columnName
- * @return string
- */
- private function getSeriesLabel($rowLabel, $columnName)
- {
- $metricLabel = $this->getColumnTranslation($columnName);
-
- if($rowLabel !== false)
- {
- // eg. "Yahoo! (Visits)"
- $label = "$rowLabel ($metricLabel)";
- }
- else
- {
- // eg. "Visits"
- $label = $metricLabel;
- }
-
- return $label;
- }
-
- /**
- * We link the graph dots to the same report as currently being displayed (only the date would change).
- *
- * In some cases the widget is loaded within a report that doesn't exist as such.
- * For example, the dashboards loads the 'Last visits graph' widget which can't be directly linked to.
- * Instead, the graph must link back to the dashboard.
- *
- * In other cases, like Visitors>Overview or the Goals graphs, we can link the graph clicks to the same report.
- *
- * To detect whether or not we can link to a report, we simply check if the current URL from which it was loaded
- * belongs to the menu or not. If it doesn't belong to the menu, we do not append the hash to the URL,
- * which results in loading the dashboard.
- *
- * @return array Query string array to append to the URL hash or false if the dashboard should be displayed
- */
- private function getQueryStringAsHash()
- {
- $queryString = Piwik_Url::getArrayFromCurrentQueryString();
- $piwikParameters = array('idSite', 'date', 'period', 'XDEBUG_SESSION_START', 'KEY');
- foreach($piwikParameters as $parameter)
- {
- unset($queryString[$parameter]);
- }
- if(Piwik_IsMenuUrlFound($queryString))
- {
- return $queryString;
- }
- return false;
- }
-
- private function isLinkEnabled()
- {
- static $linkEnabled;
- if(!isset($linkEnabled))
- {
- // 1) Custom Date Range always have link disabled, otherwise
- // the graph data set is way too big and fails to display
- // 2) disableLink parameter is set in the Widgetize "embed" code
- $linkEnabled = !Piwik_Common::getRequestVar('disableLink', 0, 'int')
- && Piwik_Common::getRequestVar('period', 'day') != 'range';
- }
- return $linkEnabled;
- }
-
- private function getXAxisStepSize( $periodLabel, $countGraphElements )
- {
- // when the number of elements plotted can be small, make sure the X legend is useful
- if ($countGraphElements <= 7)
- {
- return 1;
- }
-
- switch ($periodLabel)
- {
- case 'day':
- $steps = 5;
- break;
- case 'week':
- $steps = 4;
- break;
- case 'month':
- $steps = 5;
- break;
- case 'year':
- $steps = 5;
- break;
- default:
- $steps = 5;
- break;
- }
-
- $paddedCount = $countGraphElements + 2; // pad count so last label won't be cut off
- return ceil($paddedCount / $steps);
- }
+
+ // used for the row picker
+ // (the series picker configuration resides in the parent class)
+ protected $rowPicker = false;
+ protected $visibleRows = array();
+ protected $rowPickerConfig = array();
+
+ protected function checkStandardDataTable()
+ {
+ // DataTable_Array and DataTable allowed for the evolution chart
+ Piwik::checkObjectTypeIs($this->dataTable, array('Piwik_DataTable_Array', 'Piwik_DataTable'));
+ }
+
+ protected function getViewDataTableId()
+ {
+ return 'generateDataChartEvolution';
+ }
+
+ function __construct()
+ {
+ $this->view = new Piwik_Visualization_Chart_Evolution();
+ }
+
+ /**
+ * Adds the same series picker as parent::setSelectableColumns but the selectable series are not
+ * columns of a single row but the same column across multiple rows, e.g. the number of visits
+ * for each referrer type.
+ * @param array $visibleRows the rows that are initially visible
+ * @param string $matchBy the way the items in $visibleRows are matched with the data. possible values:
+ * - label: matches the label of the row
+ */
+ public function addRowPicker($visibleRows, $matchBy = 'label')
+ {
+ $this->rowPicker = $matchBy;
+
+ if (!is_array($visibleRows)) {
+ $visibleRows = array($visibleRows);
+ }
+ $this->visibleRows = $visibleRows;
+ }
+
+ /**
+ * This method is called for every row of every table in the DataTable_Array.
+ * It incrementally builds the row picker configuration and determines whether
+ * the row is initially visible or not.
+ * @param string $rowLabel
+ * @return bool
+ */
+ protected function handleRowForRowPicker(&$rowLabel)
+ {
+ // determine whether row is visible
+ $isVisible = true;
+ switch ($this->rowPicker) {
+ case 'label':
+ $isVisible = in_array($rowLabel, $this->visibleRows);
+ break;
+ }
+
+ // build config
+ if (!isset($this->rowPickerConfig[$rowLabel])) {
+ $this->rowPickerConfig[$rowLabel] = array(
+ 'label' => $rowLabel,
+ 'matcher' => $rowLabel,
+ 'displayed' => $isVisible
+ );
+ }
+
+ return $isVisible;
+ }
+
+ protected function loadDataTableFromAPI()
+ {
+ $period = Piwik_Common::getRequestVar('period');
+ // period will be overridden when 'range' is requested in the UI
+ // but the graph will display for each day of the range.
+ // Default 'range' behavior is to return the 'sum' for the range
+ if ($period == 'range') {
+ $_GET['period'] = 'day';
+ }
+ // throws exception if no view access
+ parent::loadDataTableFromAPI();
+ if ($period == 'range') {
+ $_GET['period'] = $period;
+ }
+ }
+
+ protected function initChartObjectData()
+ {
+ // if the loaded datatable is a simple DataTable, it is most likely a plugin plotting some custom data
+ // we don't expect plugin developers to return a well defined Piwik_DataTable_Array
+ if ($this->dataTable instanceof Piwik_DataTable) {
+ return parent::initChartObjectData();
+ }
+
+ $this->dataTable->applyQueuedFilters();
+ if (!($this->dataTable instanceof Piwik_DataTable_Array)) {
+ throw new Exception("Expecting a DataTable_Array with custom format to draw an evolution chart");
+ }
+
+ // the X label is extracted from the 'period' object in the table's metadata
+ $xLabels = $uniqueIdsDataTable = array();
+ foreach ($this->dataTable->getArray() as $idDataTable => $metadataDataTable) {
+ //eg. "Aug 2009"
+ $xLabels[] = $metadataDataTable->getMetadata('period')->getLocalizedShortString();
+ // we keep track of all unique data table that we need to set a Y value for
+ $uniqueIdsDataTable[] = $idDataTable;
+ }
+
+ $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
+ $requestedColumnNames = $this->getColumnsToDisplay();
+ $units = $this->getUnitsForColumnsToDisplay();
+
+ $yAxisLabelToUnit = array();
+ $yAxisLabelToValue = array();
+ foreach ($this->dataTable->getArray() as $idDataTable => $dataTable) {
+ foreach ($dataTable->getRows() as $row) {
+ $rowLabel = $row->getColumn('label');
+
+ // put together configuration for row picker.
+ // do this for every data table in the array because rows do not
+ // have to present for each date.
+ if ($this->rowPicker !== false) {
+ $rowVisible = $this->handleRowForRowPicker($rowLabel);
+ if (!$rowVisible) {
+ continue;
+ }
+ }
+
+ // build data for request columns
+ foreach ($requestedColumnNames as $requestedColumnName) {
+ $yAxisLabel = $this->getSeriesLabel($rowLabel, $requestedColumnName);
+ if (($columnValue = $row->getColumn($requestedColumnName)) !== false) {
+ $yAxisLabelToValue[$yAxisLabel][$idDataTable] = $columnValue;
+ $yAxisLabelToUnit[$yAxisLabel] = $units[$requestedColumnName];
+ }
+ }
+ }
+ }
+
+ // make sure all column values are set to at least zero (no gap in the graph)
+ $yAxisLabelToValueCleaned = array();
+ foreach ($uniqueIdsDataTable as $uniqueIdDataTable) {
+ foreach ($yAxisLabelToValue as $yAxisLabel => $idDataTableToColumnValue) {
+ if (isset($idDataTableToColumnValue[$uniqueIdDataTable])) {
+ $columnValue = $idDataTableToColumnValue[$uniqueIdDataTable];
+ } else {
+ $columnValue = 0;
+ }
+ $yAxisLabelToValueCleaned[$yAxisLabel][] = $columnValue;
+ }
+ }
+
+ $this->view->setAxisXLabels($xLabels);
+ $this->view->setAxisYValues($yAxisLabelToValueCleaned);
+ $this->view->setAxisYUnits($yAxisLabelToUnit);
+
+ $countGraphElements = $this->dataTable->getRowsCount();
+ $dataTables = $this->dataTable->getArray();
+ $firstDatatable = reset($dataTables);
+ $period = $firstDatatable->getMetadata('period');
+
+ $stepSize = $this->getXAxisStepSize($period->getLabel(), $countGraphElements);
+ $this->view->setXSteps($stepSize);
+
+ if ($this->isLinkEnabled()) {
+ $axisXOnClick = array();
+ $queryStringAsHash = $this->getQueryStringAsHash();
+ foreach ($this->dataTable->getArray() as $idDataTable => $metadataDataTable) {
+ $period = $metadataDataTable->getMetadata('period');
+ $dateInUrl = $period->getDateStart();
+ $parameters = array(
+ 'idSite' => $idSite,
+ 'period' => $period->getLabel(),
+ 'date' => $dateInUrl->toString(),
+ 'segment' => Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', false))
+ );
+ $hash = '';
+ if (!empty($queryStringAsHash)) {
+ $hash = '#' . Piwik_Url::getQueryStringFromParameters($queryStringAsHash + $parameters);
+ }
+ $link = 'index.php?' .
+ Piwik_Url::getQueryStringFromParameters(array(
+ 'module' => 'CoreHome',
+ 'action' => 'index',
+ ) + $parameters)
+ . $hash;
+ $axisXOnClick[] = $link;
+ }
+ $this->view->setAxisXOnClick($axisXOnClick);
+ }
+
+ $this->addSeriesPickerToView();
+ if ($this->rowPicker !== false) {
+ // configure the row picker
+ $this->view->setSelectableRows(array_values($this->rowPickerConfig));
+ }
+ }
+
+ /**
+ * Derive the series label from the row label and the column name.
+ * If the row label is set, both the label and the column name are displayed.
+ * @param string $rowLabel
+ * @param string $columnName
+ * @return string
+ */
+ private function getSeriesLabel($rowLabel, $columnName)
+ {
+ $metricLabel = $this->getColumnTranslation($columnName);
+
+ if ($rowLabel !== false) {
+ // eg. "Yahoo! (Visits)"
+ $label = "$rowLabel ($metricLabel)";
+ } else {
+ // eg. "Visits"
+ $label = $metricLabel;
+ }
+
+ return $label;
+ }
+
+ /**
+ * We link the graph dots to the same report as currently being displayed (only the date would change).
+ *
+ * In some cases the widget is loaded within a report that doesn't exist as such.
+ * For example, the dashboards loads the 'Last visits graph' widget which can't be directly linked to.
+ * Instead, the graph must link back to the dashboard.
+ *
+ * In other cases, like Visitors>Overview or the Goals graphs, we can link the graph clicks to the same report.
+ *
+ * To detect whether or not we can link to a report, we simply check if the current URL from which it was loaded
+ * belongs to the menu or not. If it doesn't belong to the menu, we do not append the hash to the URL,
+ * which results in loading the dashboard.
+ *
+ * @return array Query string array to append to the URL hash or false if the dashboard should be displayed
+ */
+ private function getQueryStringAsHash()
+ {
+ $queryString = Piwik_Url::getArrayFromCurrentQueryString();
+ $piwikParameters = array('idSite', 'date', 'period', 'XDEBUG_SESSION_START', 'KEY');
+ foreach ($piwikParameters as $parameter) {
+ unset($queryString[$parameter]);
+ }
+ if (Piwik_IsMenuUrlFound($queryString)) {
+ return $queryString;
+ }
+ return false;
+ }
+
+ private function isLinkEnabled()
+ {
+ static $linkEnabled;
+ if (!isset($linkEnabled)) {
+ // 1) Custom Date Range always have link disabled, otherwise
+ // the graph data set is way too big and fails to display
+ // 2) disableLink parameter is set in the Widgetize "embed" code
+ $linkEnabled = !Piwik_Common::getRequestVar('disableLink', 0, 'int')
+ && Piwik_Common::getRequestVar('period', 'day') != 'range';
+ }
+ return $linkEnabled;
+ }
+
+ private function getXAxisStepSize($periodLabel, $countGraphElements)
+ {
+ // when the number of elements plotted can be small, make sure the X legend is useful
+ if ($countGraphElements <= 7) {
+ return 1;
+ }
+
+ switch ($periodLabel) {
+ case 'day':
+ $steps = 5;
+ break;
+ case 'week':
+ $steps = 4;
+ break;
+ case 'month':
+ $steps = 5;
+ break;
+ case 'year':
+ $steps = 5;
+ break;
+ default:
+ $steps = 5;
+ break;
+ }
+
+ $paddedCount = $countGraphElements + 2; // pad count so last label won't be cut off
+ return ceil($paddedCount / $steps);
+ }
}
diff --git a/core/ViewDataTable/GenerateGraphData/ChartPie.php b/core/ViewDataTable/GenerateGraphData/ChartPie.php
index 91661ef485..6acd8f440e 100644
--- a/core/ViewDataTable/GenerateGraphData/ChartPie.php
+++ b/core/ViewDataTable/GenerateGraphData/ChartPie.php
@@ -1,43 +1,43 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Piwik_ViewDataTable_GenerateGraphData for the pie chart, using Piwik_Visualization_Chart_Pie
- *
+ *
* @package Piwik
* @subpackage Piwik_ViewDataTable
*/
class Piwik_ViewDataTable_GenerateGraphData_ChartPie extends Piwik_ViewDataTable_GenerateGraphData
{
- protected $graphLimit = 6;
-
- protected function getViewDataTableId()
- {
- return 'generateDataChartPie';
- }
-
- function __construct()
- {
- $this->view = new Piwik_Visualization_Chart_Pie();
- }
+ protected $graphLimit = 6;
+
+ protected function getViewDataTableId()
+ {
+ return 'generateDataChartPie';
+ }
+
+ function __construct()
+ {
+ $this->view = new Piwik_Visualization_Chart_Pie();
+ }
+
+ /**
+ * Manipulate the configuration of the series picker since only one metric is selectable
+ * for pie charts
+ * @param bool $multiSelect
+ */
+ protected function addSeriesPickerToView($multiSelect = false)
+ {
+ // force $multiSelect=false
+ parent::addSeriesPickerToView(false);
+ }
- /**
- * Manipulate the configuration of the series picker since only one metric is selectable
- * for pie charts
- * @param bool $multiSelect
- */
- protected function addSeriesPickerToView($multiSelect=false)
- {
- // force $multiSelect=false
- parent::addSeriesPickerToView(false);
- }
-
}
diff --git a/core/ViewDataTable/GenerateGraphData/ChartVerticalBar.php b/core/ViewDataTable/GenerateGraphData/ChartVerticalBar.php
index 4188a39091..b4c462b7ba 100644
--- a/core/ViewDataTable/GenerateGraphData/ChartVerticalBar.php
+++ b/core/ViewDataTable/GenerateGraphData/ChartVerticalBar.php
@@ -1,40 +1,40 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Piwik_ViewDataTable_GenerateGraphData for the vertical bar graph, using Piwik_Visualization_Chart_VerticalBar
- *
+ *
* @package Piwik
* @subpackage Piwik_ViewDataTable
*/
class Piwik_ViewDataTable_GenerateGraphData_ChartVerticalBar extends Piwik_ViewDataTable_GenerateGraphData
{
- protected $graphLimit = 6;
-
- protected function getViewDataTableId()
- {
- return 'generateDataChartVerticalBar';
- }
-
- function __construct()
- {
- $this->view = new Piwik_Visualization_Chart_VerticalBar();
- }
-
- protected function getUnitsForColumnsToDisplay()
- {
- // the bar charts contain the labels a first series
- // this series has to be removed from the units
- $units = parent::getUnitsForColumnsToDisplay();
- array_shift($units);
- return $units;
- }
+ protected $graphLimit = 6;
+
+ protected function getViewDataTableId()
+ {
+ return 'generateDataChartVerticalBar';
+ }
+
+ function __construct()
+ {
+ $this->view = new Piwik_Visualization_Chart_VerticalBar();
+ }
+
+ protected function getUnitsForColumnsToDisplay()
+ {
+ // the bar charts contain the labels a first series
+ // this series has to be removed from the units
+ $units = parent::getUnitsForColumnsToDisplay();
+ array_shift($units);
+ return $units;
+ }
}
diff --git a/core/ViewDataTable/GenerateGraphHTML.php b/core/ViewDataTable/GenerateGraphHTML.php
index ce3cd191bf..7a7550bbb4 100644
--- a/core/ViewDataTable/GenerateGraphHTML.php
+++ b/core/ViewDataTable/GenerateGraphHTML.php
@@ -18,184 +18,181 @@
*/
abstract class Piwik_ViewDataTable_GenerateGraphHTML extends Piwik_ViewDataTable
{
-
- protected $width = '100%';
- protected $height = 250;
- protected $graphType = 'unknown';
-
- /**
- * Parameters to send to GenerateGraphData instance. Parameters are passed
- * via the $_GET array.
- *
- * @var array
- */
- protected $generateGraphDataParams = array();
-
- /**
- * @see Piwik_ViewDataTable::init()
- * @param string $currentControllerName
- * @param string $currentControllerAction
- * @param string $apiMethodToRequestDataTable
- * @param null $controllerActionCalledWhenRequestSubTable
- */
- function init($currentControllerName,
- $currentControllerAction,
- $apiMethodToRequestDataTable,
- $controllerActionCalledWhenRequestSubTable = null)
- {
- parent::init($currentControllerName,
- $currentControllerAction,
- $apiMethodToRequestDataTable,
- $controllerActionCalledWhenRequestSubTable);
-
- $this->dataTableTemplate = 'CoreHome/templates/graph.tpl';
-
- $this->disableOffsetInformationAndPaginationControls();
- $this->disableExcludeLowPopulation();
- $this->disableSearchBox();
- $this->enableShowExportAsImageIcon();
-
- $this->parametersToModify = array(
- 'viewDataTable' => $this->getViewDataTableIdToLoad(),
- // in the case this controller is being executed by another controller
- // eg. when being widgetized in an IFRAME
- // we need to put in the URL of the graph data the real module and action
- 'module' => $currentControllerName,
- 'action' => $currentControllerAction,
- );
- }
-
- public function enableShowExportAsImageIcon()
- {
- $this->viewProperties['show_export_as_image_icon'] = true;
- }
-
- public function addRowEvolutionSeriesToggle($initiallyShowAllMetrics) {
- $this->viewProperties['externalSeriesToggle'] = 'RowEvolutionSeriesToggle';
- $this->viewProperties['externalSeriesToggleShowAll'] = $initiallyShowAllMetrics;
- }
-
- /**
- * Sets parameters to modify in the future generated URL
- * @param array $array array('nameParameter' => $newValue, ...)
- */
- public function setParametersToModify($array)
- {
- $this->parametersToModify = array_merge($this->parametersToModify, $array);
- }
-
- /**
- * Show every x-axis tick instead of just every other one.
- */
- public function showAllTicks()
- {
- $this->generateGraphDataParams['show_all_ticks'] = 1;
- }
-
- /**
- * Adds a row to the report containing totals for contained metrics. Mainly useful
- * for evolution graphs where displaying the totals w/ the metrics is useful.
- */
- public function addTotalRow()
- {
- $this->generateGraphDataParams['add_total_row'] = 1;
- }
-
- /**
- * We persist the parametersToModify values in the javascript footer.
- * This is used by the "export links" that use the "date" attribute
- * from the json properties array in the datatable footer.
- * @return array
- */
- protected function getJavascriptVariablesToSet()
- {
- $original = parent::getJavascriptVariablesToSet();
- $originalViewDataTable = $original['viewDataTable'];
-
- $result = $this->parametersToModify + $original;;
- $result['viewDataTable'] = $originalViewDataTable;
-
- return $result;
- }
-
- /**
- * @see Piwik_ViewDataTable::main()
- * @return null
- */
- public function main()
- {
- if($this->mainAlreadyExecuted)
- {
- return;
- }
- $this->mainAlreadyExecuted = true;
-
- $this->view = $this->buildView();
- }
-
- protected function buildView()
- {
- // access control
- $idSite = Piwik_Common::getRequestVar('idSite', 1, 'int');
- Piwik_API_Request::reloadAuthUsingTokenAuth();
- if(!Piwik::isUserHasViewAccess($idSite))
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionPrivilegeAccessWebsite',array("'view'", $idSite)));
- }
-
- // collect data
- $this->parametersToModify['action'] = $this->currentControllerAction;
- $this->parametersToModify = array_merge($this->variablesDefault, $this->parametersToModify);
- $this->graphData = $this->getGraphData();
-
- // build view
- $view = new Piwik_View($this->dataTableTemplate);
-
- $view->width = $this->width;
- $view->height = $this->height;
- $view->chartDivId = $this->getUniqueIdViewDataTable()."Chart";
- $view->graphType = $this->graphType;
-
- $view->data = $this->graphData;
- $view->isDataAvailable = strpos($this->graphData, '"series":[]') === false;
-
- $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet();
- $view->properties = $this->getViewProperties();
-
- $view->reportDocumentation = $this->getReportDocumentation();
-
- // if it's likely that the report data for this data table has been purged,
- // set whether we should display a message to that effect.
- $view->showReportDataWasPurgedMessage = $this->hasReportBeenPurged();
- $view->deleteReportsOlderThan = Piwik_GetOption('delete_reports_older_than');
-
- return $view;
- }
-
- protected function getGraphData()
- {
- $saveGet = $_GET;
-
- $params = array_merge($this->generateGraphDataParams, $this->parametersToModify);
- foreach($params as $key => $val)
- {
- // We do not forward filter data to the graph controller.
- // This would cause the graph to have filter_limit=5 set by default,
- // which would break them (graphs need the full dataset to build the "Others" aggregate value)
- if(strpos($key, 'filter_') !== false)
- {
- continue;
- }
- if (is_array($val))
- {
- $val = implode(',', $val);
- }
- $_GET[$key] = $val;
- }
- $content = Piwik_FrontController::getInstance()->fetchDispatch($this->currentControllerName, $this->currentControllerAction, array());
-
- $_GET = $saveGet;
-
- return str_replace(array("\r", "\n"), '', $content);
- }
+
+ protected $width = '100%';
+ protected $height = 250;
+ protected $graphType = 'unknown';
+
+ /**
+ * Parameters to send to GenerateGraphData instance. Parameters are passed
+ * via the $_GET array.
+ *
+ * @var array
+ */
+ protected $generateGraphDataParams = array();
+
+ /**
+ * @see Piwik_ViewDataTable::init()
+ * @param string $currentControllerName
+ * @param string $currentControllerAction
+ * @param string $apiMethodToRequestDataTable
+ * @param null $controllerActionCalledWhenRequestSubTable
+ */
+ function init($currentControllerName,
+ $currentControllerAction,
+ $apiMethodToRequestDataTable,
+ $controllerActionCalledWhenRequestSubTable = null)
+ {
+ parent::init($currentControllerName,
+ $currentControllerAction,
+ $apiMethodToRequestDataTable,
+ $controllerActionCalledWhenRequestSubTable);
+
+ $this->dataTableTemplate = 'CoreHome/templates/graph.tpl';
+
+ $this->disableOffsetInformationAndPaginationControls();
+ $this->disableExcludeLowPopulation();
+ $this->disableSearchBox();
+ $this->enableShowExportAsImageIcon();
+
+ $this->parametersToModify = array(
+ 'viewDataTable' => $this->getViewDataTableIdToLoad(),
+ // in the case this controller is being executed by another controller
+ // eg. when being widgetized in an IFRAME
+ // we need to put in the URL of the graph data the real module and action
+ 'module' => $currentControllerName,
+ 'action' => $currentControllerAction,
+ );
+ }
+
+ public function enableShowExportAsImageIcon()
+ {
+ $this->viewProperties['show_export_as_image_icon'] = true;
+ }
+
+ public function addRowEvolutionSeriesToggle($initiallyShowAllMetrics)
+ {
+ $this->viewProperties['externalSeriesToggle'] = 'RowEvolutionSeriesToggle';
+ $this->viewProperties['externalSeriesToggleShowAll'] = $initiallyShowAllMetrics;
+ }
+
+ /**
+ * Sets parameters to modify in the future generated URL
+ * @param array $array array('nameParameter' => $newValue, ...)
+ */
+ public function setParametersToModify($array)
+ {
+ $this->parametersToModify = array_merge($this->parametersToModify, $array);
+ }
+
+ /**
+ * Show every x-axis tick instead of just every other one.
+ */
+ public function showAllTicks()
+ {
+ $this->generateGraphDataParams['show_all_ticks'] = 1;
+ }
+
+ /**
+ * Adds a row to the report containing totals for contained metrics. Mainly useful
+ * for evolution graphs where displaying the totals w/ the metrics is useful.
+ */
+ public function addTotalRow()
+ {
+ $this->generateGraphDataParams['add_total_row'] = 1;
+ }
+
+ /**
+ * We persist the parametersToModify values in the javascript footer.
+ * This is used by the "export links" that use the "date" attribute
+ * from the json properties array in the datatable footer.
+ * @return array
+ */
+ protected function getJavascriptVariablesToSet()
+ {
+ $original = parent::getJavascriptVariablesToSet();
+ $originalViewDataTable = $original['viewDataTable'];
+
+ $result = $this->parametersToModify + $original;
+ ;
+ $result['viewDataTable'] = $originalViewDataTable;
+
+ return $result;
+ }
+
+ /**
+ * @see Piwik_ViewDataTable::main()
+ * @return null
+ */
+ public function main()
+ {
+ if ($this->mainAlreadyExecuted) {
+ return;
+ }
+ $this->mainAlreadyExecuted = true;
+
+ $this->view = $this->buildView();
+ }
+
+ protected function buildView()
+ {
+ // access control
+ $idSite = Piwik_Common::getRequestVar('idSite', 1, 'int');
+ Piwik_API_Request::reloadAuthUsingTokenAuth();
+ if (!Piwik::isUserHasViewAccess($idSite)) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionPrivilegeAccessWebsite', array("'view'", $idSite)));
+ }
+
+ // collect data
+ $this->parametersToModify['action'] = $this->currentControllerAction;
+ $this->parametersToModify = array_merge($this->variablesDefault, $this->parametersToModify);
+ $this->graphData = $this->getGraphData();
+
+ // build view
+ $view = new Piwik_View($this->dataTableTemplate);
+
+ $view->width = $this->width;
+ $view->height = $this->height;
+ $view->chartDivId = $this->getUniqueIdViewDataTable() . "Chart";
+ $view->graphType = $this->graphType;
+
+ $view->data = $this->graphData;
+ $view->isDataAvailable = strpos($this->graphData, '"series":[]') === false;
+
+ $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet();
+ $view->properties = $this->getViewProperties();
+
+ $view->reportDocumentation = $this->getReportDocumentation();
+
+ // if it's likely that the report data for this data table has been purged,
+ // set whether we should display a message to that effect.
+ $view->showReportDataWasPurgedMessage = $this->hasReportBeenPurged();
+ $view->deleteReportsOlderThan = Piwik_GetOption('delete_reports_older_than');
+
+ return $view;
+ }
+
+ protected function getGraphData()
+ {
+ $saveGet = $_GET;
+
+ $params = array_merge($this->generateGraphDataParams, $this->parametersToModify);
+ foreach ($params as $key => $val) {
+ // We do not forward filter data to the graph controller.
+ // This would cause the graph to have filter_limit=5 set by default,
+ // which would break them (graphs need the full dataset to build the "Others" aggregate value)
+ if (strpos($key, 'filter_') !== false) {
+ continue;
+ }
+ if (is_array($val)) {
+ $val = implode(',', $val);
+ }
+ $_GET[$key] = $val;
+ }
+ $content = Piwik_FrontController::getInstance()->fetchDispatch($this->currentControllerName, $this->currentControllerAction, array());
+
+ $_GET = $saveGet;
+
+ return str_replace(array("\r", "\n"), '', $content);
+ }
}
diff --git a/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php b/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php
index 26cad88ea0..2a3f921405 100644
--- a/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php
+++ b/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php
@@ -18,189 +18,181 @@
class Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution extends Piwik_ViewDataTable_GenerateGraphHTML
{
- protected $height = 170;
- protected $graphType = 'evolution';
-
- /**
- * The value of the date query parameter (or a default value) before it is turned
- * into a date range. Set in 'calculateEvolutionDateRange' and used by
- * 'getJavascriptVariablesToSet'.
- *
- * @var string
- */
- private $originalDate;
-
- protected function getViewDataTableId()
- {
- return 'graphEvolution';
- }
-
- protected function getViewDataTableIdToLoad()
- {
- return 'generateDataChartEvolution';
- }
-
- function init($currentControllerName,
- $currentControllerAction,
- $apiMethodToRequestDataTable,
- $controllerActionCalledWhenRequestSubTable = null)
- {
- parent::init($currentControllerName,
- $currentControllerAction,
- $apiMethodToRequestDataTable,
- $controllerActionCalledWhenRequestSubTable);
-
- $this->calculateEvolutionDateRange();
- $this->disableShowAllViewsIcons();
- $this->disableShowTable();
- $this->disableShowAllColumns();
- $this->showAnnotationsView();
- }
-
- /**
- * Makes sure 'date' parameter is not overridden.
- */
- protected function getJavascriptVariablesToSet()
- {
- $result = parent::getJavascriptVariablesToSet();
-
- // Graphs use a Range instead of the input date - we will use this same range for "Export" icons
- $result['dateUsedInGraph'] = $result['date'];
-
- // Other datatable features may require the original input date (eg. the limit dropdown below evolution graph)
- $result['date'] = $this->originalDate;
- return $result;
- }
-
- /**
+ protected $height = 170;
+ protected $graphType = 'evolution';
+
+ /**
+ * The value of the date query parameter (or a default value) before it is turned
+ * into a date range. Set in 'calculateEvolutionDateRange' and used by
+ * 'getJavascriptVariablesToSet'.
+ *
+ * @var string
+ */
+ private $originalDate;
+
+ protected function getViewDataTableId()
+ {
+ return 'graphEvolution';
+ }
+
+ protected function getViewDataTableIdToLoad()
+ {
+ return 'generateDataChartEvolution';
+ }
+
+ function init($currentControllerName,
+ $currentControllerAction,
+ $apiMethodToRequestDataTable,
+ $controllerActionCalledWhenRequestSubTable = null)
+ {
+ parent::init($currentControllerName,
+ $currentControllerAction,
+ $apiMethodToRequestDataTable,
+ $controllerActionCalledWhenRequestSubTable);
+
+ $this->calculateEvolutionDateRange();
+ $this->disableShowAllViewsIcons();
+ $this->disableShowTable();
+ $this->disableShowAllColumns();
+ $this->showAnnotationsView();
+ }
+
+ /**
+ * Makes sure 'date' parameter is not overridden.
+ */
+ protected function getJavascriptVariablesToSet()
+ {
+ $result = parent::getJavascriptVariablesToSet();
+
+ // Graphs use a Range instead of the input date - we will use this same range for "Export" icons
+ $result['dateUsedInGraph'] = $result['date'];
+
+ // Other datatable features may require the original input date (eg. the limit dropdown below evolution graph)
+ $result['date'] = $this->originalDate;
+ return $result;
+ }
+
+ /**
* We ensure that the graph for a given Goal has a different ID than the 'Goals Overview' graph
* so that both can display on the dashboard at the same time
- * @return null|string
- */
- public function getUniqueIdViewDataTable()
- {
- $id = parent::getUniqueIdViewDataTable();
- if (!empty($this->parametersToModify['idGoal']))
- {
- $id .= $this->parametersToModify['idGoal'];
- }
- return $id;
- }
-
- /**
- * Sets the columns that will be displayed on output evolution chart
- * By default all columns are displayed ($columnsNames = array() will display all columns)
- *
- * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits')
- */
- public function setColumnsToDisplay($columnsNames)
- {
- if (!is_array($columnsNames))
- {
- if (strpos($columnsNames, ',') !== false)
- {
- // array values are comma separated
- $columnsNames = explode(',', $columnsNames);
- }
- else
- {
- $columnsNames = array($columnsNames);
- }
- }
- $this->setParametersToModify(array('columns' => $columnsNames));
- }
-
- /**
- * Based on the period, date and evolution_{$period}_last_n query parameters,
- * calculates the date range this evolution chart will display data for.
- */
- private function calculateEvolutionDateRange()
- {
- $period = Piwik_Common::getRequestVar('period');
-
- $defaultLastN = self::getDefaultLastN($period);
- $this->originalDate = Piwik_Common::getRequestVar('date', 'last'.$defaultLastN, 'string');
-
- if ($period != 'range') // show evolution limit if the period is not a range
- {
- $this->alwaysShowLimitDropdown();
-
- // set the evolution_{$period}_last_n query param
- if (Piwik_Period_Range::parseDateRange($this->originalDate)) // if a multiple period
- {
- // overwrite last_n param using the date range
- $oPeriod = new Piwik_Period_Range($period, $this->originalDate);
- $lastN = count($oPeriod->getSubperiods());
- }
- else // if not a multiple period
- {
- list($newDate, $lastN) = self::getDateRangeAndLastN($period, $this->originalDate, $defaultLastN);
- $this->setParametersToModify(array('date' => $newDate));
- }
- $lastNParamName = self::getLastNParamName($period);
- $this->setParametersToModify(array($lastNParamName => $lastN));
- }
- }
-
- /**
- * Returns the entire date range and lastN value for the current request, based on
- * a period type and end date.
- *
- * @param string $period The period type, 'day', 'week', 'month' or 'year'
- * @param string $endDate The end date.
- * @param int|null $defaultLastN The default lastN to use. If null, the result of
- * getDefaultLastN is used.
- * @return array An array w/ two elements. The first is a whole date range and the second
- * is the lastN number used, ie, array('2010-01-01,2012-01-02', 2).
- */
- public static function getDateRangeAndLastN( $period, $endDate, $defaultLastN = null )
- {
- if ($defaultLastN === null)
- {
- $defaultLastN = self::getDefaultLastN($period);
- }
-
- $lastNParamName = self::getLastNParamName($period);
- $lastN = Piwik_Common::getRequestVar($lastNParamName, $defaultLastN, 'int');
-
- $site = new Piwik_Site(Piwik_Common::getRequestVar('idSite'));
-
- $dateRange = Piwik_Controller::getDateRangeRelativeToEndDate($period, 'last'.$lastN, $endDate, $site);
-
- return array($dateRange, $lastN);
- }
-
- /**
- * Returns the default last N number of dates to display for a given period.
- *
- * @param string $period 'day', 'week', 'month' or 'year'
- * @return int
- */
- public static function getDefaultLastN( $period )
- {
- switch ($period)
- {
- case 'week':
- return 26;
- case 'month':
- return 24;
- case 'year':
- return 5;
- case 'day':
- default:
- return 30;
- }
- }
-
- /**
- * Returns the query parameter that stores the lastN number of periods to get for
- * the evolution graph.
- *
- * @param string $period The period type, 'day', 'week', 'month' or 'year'.
- * @return string
- */
- public static function getLastNParamName( $period )
- {
- return "evolution_{$period}_last_n";
- }
+ * @return null|string
+ */
+ public function getUniqueIdViewDataTable()
+ {
+ $id = parent::getUniqueIdViewDataTable();
+ if (!empty($this->parametersToModify['idGoal'])) {
+ $id .= $this->parametersToModify['idGoal'];
+ }
+ return $id;
+ }
+
+ /**
+ * Sets the columns that will be displayed on output evolution chart
+ * By default all columns are displayed ($columnsNames = array() will display all columns)
+ *
+ * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits')
+ */
+ public function setColumnsToDisplay($columnsNames)
+ {
+ if (!is_array($columnsNames)) {
+ if (strpos($columnsNames, ',') !== false) {
+ // array values are comma separated
+ $columnsNames = explode(',', $columnsNames);
+ } else {
+ $columnsNames = array($columnsNames);
+ }
+ }
+ $this->setParametersToModify(array('columns' => $columnsNames));
+ }
+
+ /**
+ * Based on the period, date and evolution_{$period}_last_n query parameters,
+ * calculates the date range this evolution chart will display data for.
+ */
+ private function calculateEvolutionDateRange()
+ {
+ $period = Piwik_Common::getRequestVar('period');
+
+ $defaultLastN = self::getDefaultLastN($period);
+ $this->originalDate = Piwik_Common::getRequestVar('date', 'last' . $defaultLastN, 'string');
+
+ if ($period != 'range') // show evolution limit if the period is not a range
+ {
+ $this->alwaysShowLimitDropdown();
+
+ // set the evolution_{$period}_last_n query param
+ if (Piwik_Period_Range::parseDateRange($this->originalDate)) // if a multiple period
+ {
+ // overwrite last_n param using the date range
+ $oPeriod = new Piwik_Period_Range($period, $this->originalDate);
+ $lastN = count($oPeriod->getSubperiods());
+ } else // if not a multiple period
+ {
+ list($newDate, $lastN) = self::getDateRangeAndLastN($period, $this->originalDate, $defaultLastN);
+ $this->setParametersToModify(array('date' => $newDate));
+ }
+ $lastNParamName = self::getLastNParamName($period);
+ $this->setParametersToModify(array($lastNParamName => $lastN));
+ }
+ }
+
+ /**
+ * Returns the entire date range and lastN value for the current request, based on
+ * a period type and end date.
+ *
+ * @param string $period The period type, 'day', 'week', 'month' or 'year'
+ * @param string $endDate The end date.
+ * @param int|null $defaultLastN The default lastN to use. If null, the result of
+ * getDefaultLastN is used.
+ * @return array An array w/ two elements. The first is a whole date range and the second
+ * is the lastN number used, ie, array('2010-01-01,2012-01-02', 2).
+ */
+ public static function getDateRangeAndLastN($period, $endDate, $defaultLastN = null)
+ {
+ if ($defaultLastN === null) {
+ $defaultLastN = self::getDefaultLastN($period);
+ }
+
+ $lastNParamName = self::getLastNParamName($period);
+ $lastN = Piwik_Common::getRequestVar($lastNParamName, $defaultLastN, 'int');
+
+ $site = new Piwik_Site(Piwik_Common::getRequestVar('idSite'));
+
+ $dateRange = Piwik_Controller::getDateRangeRelativeToEndDate($period, 'last' . $lastN, $endDate, $site);
+
+ return array($dateRange, $lastN);
+ }
+
+ /**
+ * Returns the default last N number of dates to display for a given period.
+ *
+ * @param string $period 'day', 'week', 'month' or 'year'
+ * @return int
+ */
+ public static function getDefaultLastN($period)
+ {
+ switch ($period) {
+ case 'week':
+ return 26;
+ case 'month':
+ return 24;
+ case 'year':
+ return 5;
+ case 'day':
+ default:
+ return 30;
+ }
+ }
+
+ /**
+ * Returns the query parameter that stores the lastN number of periods to get for
+ * the evolution graph.
+ *
+ * @param string $period The period type, 'day', 'week', 'month' or 'year'.
+ * @return string
+ */
+ public static function getLastNParamName($period)
+ {
+ return "evolution_{$period}_last_n";
+ }
}
diff --git a/core/ViewDataTable/GenerateGraphHTML/ChartPie.php b/core/ViewDataTable/GenerateGraphHTML/ChartPie.php
index 7a847050c4..ccc40d0c3f 100644
--- a/core/ViewDataTable/GenerateGraphHTML/ChartPie.php
+++ b/core/ViewDataTable/GenerateGraphHTML/ChartPie.php
@@ -18,16 +18,16 @@
class Piwik_ViewDataTable_GenerateGraphHTML_ChartPie extends Piwik_ViewDataTable_GenerateGraphHTML
{
-
- protected $graphType = 'pie';
-
- protected function getViewDataTableId()
- {
- return 'graphPie';
- }
-
- protected function getViewDataTableIdToLoad()
- {
- return 'generateDataChartPie';
- }
+
+ protected $graphType = 'pie';
+
+ protected function getViewDataTableId()
+ {
+ return 'graphPie';
+ }
+
+ protected function getViewDataTableIdToLoad()
+ {
+ return 'generateDataChartPie';
+ }
}
diff --git a/core/ViewDataTable/GenerateGraphHTML/ChartVerticalBar.php b/core/ViewDataTable/GenerateGraphHTML/ChartVerticalBar.php
index fd629d238d..3ee015f3e5 100644
--- a/core/ViewDataTable/GenerateGraphHTML/ChartVerticalBar.php
+++ b/core/ViewDataTable/GenerateGraphHTML/ChartVerticalBar.php
@@ -19,16 +19,16 @@
class Piwik_ViewDataTable_GenerateGraphHTML_ChartVerticalBar extends Piwik_ViewDataTable_GenerateGraphHTML
{
-
- protected $graphType = 'bar';
-
- protected function getViewDataTableId()
- {
- return 'graphVerticalBar';
- }
-
- protected function getViewDataTableIdToLoad()
- {
- return 'generateDataChartVerticalBar';
- }
+
+ protected $graphType = 'bar';
+
+ protected function getViewDataTableId()
+ {
+ return 'graphVerticalBar';
+ }
+
+ protected function getViewDataTableIdToLoad()
+ {
+ return 'generateDataChartVerticalBar';
+ }
}
diff --git a/core/ViewDataTable/HtmlTable.php b/core/ViewDataTable/HtmlTable.php
index 962d2dec07..a371230b86 100644
--- a/core/ViewDataTable/HtmlTable.php
+++ b/core/ViewDataTable/HtmlTable.php
@@ -19,221 +19,212 @@
*/
class Piwik_ViewDataTable_HtmlTable extends Piwik_ViewDataTable
{
- /**
- * Set to true when the DataTable must be loaded along with all its children subtables
- * Useful when searching for a pattern in the DataTable Actions (we display the full hierarchy)
- *
- * @var bool
- */
- protected $recursiveDataTableLoad = false;
-
- /**
- * PHP array conversion of the Piwik_DataTable
- *
- * @var array
- */
- public $arrayDataTable; // phpArray
-
- /**
- * @see Piwik_ViewDataTable::init()
- * @param string $currentControllerName
- * @param string $currentControllerAction
- * @param string $apiMethodToRequestDataTable
- * @param null|string $controllerActionCalledWhenRequestSubTable
- */
- function init($currentControllerName,
- $currentControllerAction,
- $apiMethodToRequestDataTable,
- $controllerActionCalledWhenRequestSubTable = null)
- {
- parent::init($currentControllerName,
- $currentControllerAction,
- $apiMethodToRequestDataTable,
- $controllerActionCalledWhenRequestSubTable);
- $this->dataTableTemplate = 'CoreHome/templates/datatable.tpl';
- $this->variablesDefault['enable_sort'] = '1';
- $this->setSortedColumn('nb_visits', 'desc');
- $this->setLimit(Piwik_Config::getInstance()->General['datatable_default_limit']);
- $this->handleLowPopulation();
- }
-
- protected function getViewDataTableId()
- {
- return 'table';
- }
-
- /**
- * @see Piwik_ViewDataTable::main()
- * @throws Exception|Piwik_Access_NoAccessException
- * @return null
- */
- public function main()
- {
- if($this->mainAlreadyExecuted)
- {
- return;
- }
- $this->mainAlreadyExecuted = true;
-
- $this->isDataAvailable = true;
- try {
- $this->loadDataTableFromAPI();
- } catch(Piwik_Access_NoAccessException $e) {
- throw $e;
- } catch(Exception $e) {
- Piwik::log("Failed to get data from API: ".$e->getMessage());
-
- $this->isDataAvailable = false;
- }
-
- $this->postDataTableLoadedFromAPI();
- $this->view = $this->buildView();
- }
-
- /**
- * @return Piwik_View with all data set
- */
- protected function buildView()
- {
- $view = new Piwik_View($this->dataTableTemplate);
-
- if(!$this->isDataAvailable)
- {
- $view->arrayDataTable = array();
- }
- else
- {
- $columns = $this->getColumnsToDisplay();
- $columnTranslations = $columnDocumentation = array();
- foreach($columns as $columnName)
- {
- $columnTranslations[$columnName] = $this->getColumnTranslation($columnName);
- $columnDocumentation[$columnName] = $this->getMetricDocumentation($columnName);
- }
- $nbColumns = count($columns);
- // case no data in the array we use the number of columns set to be displayed
- if($nbColumns == 0)
- {
- $nbColumns = count($this->columnsToDisplay);
- }
-
- $view->arrayDataTable = $this->getPHPArrayFromDataTable();
- $view->dataTableColumns = $columns;
- $view->reportDocumentation = $this->getReportDocumentation();
- $view->columnTranslations = $columnTranslations;
- $view->columnDocumentation = $columnDocumentation;
- $view->nbColumns = $nbColumns;
- $view->defaultWhenColumnValueNotDefined = '-';
-
- // if it's likely that the report data for this data table has been purged,
- // set whether we should display a message to that effect.
- $view->showReportDataWasPurgedMessage = $this->hasReportBeenPurged();
- $view->deleteReportsOlderThan = Piwik_GetOption('delete_reports_older_than');
- }
- $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet();
- $view->properties = $this->getViewProperties();
- return $view;
- }
-
- protected function handleLowPopulation( $columnToApplyFilter = null)
- {
- if(Piwik_Common::getRequestVar('enable_filter_excludelowpop', '0', 'string' ) == '0')
- {
- return;
- }
- if(is_null($columnToApplyFilter))
- {
- $columnToApplyFilter = 'nb_visits';
- }
- $this->setExcludeLowPopulation( $columnToApplyFilter);
- }
-
- /**
- * Returns friendly php array from the Piwik_DataTable
- * @see Piwik_DataTable_Renderer_Php
- * @return array
- */
- protected function getPHPArrayFromDataTable()
- {
- $renderer = Piwik_DataTable_Renderer::factory('php');
- $renderer->setTable($this->dataTable);
- $renderer->setSerialize( false );
- // we get the php array from the datatable but conserving the original datatable format,
- // ie. rows 'columns', 'metadata' and 'idsubdatatable'
- $phpArray = $renderer->originalRender();
- return $phpArray;
- }
-
-
- /**
- * Adds a column to the list of columns to be displayed
- *
- * @param string $columnName
- */
- public function addColumnToDisplay( $columnName )
- {
- $this->columnsToDisplay[] = $columnName;
- }
-
- /**
- * Sets the search on a table to be recursive (also searches in subtables)
- * Works only on Actions/Downloads/Outlinks tables.
- *
- * @return bool If the pattern for a recursive search was set in the URL
- */
- public function setSearchRecursive()
- {
- $this->variablesDefault['search_recursive'] = true;
- return $this->setRecursiveLoadDataTableIfSearchingForPattern();
- }
-
- protected function getRequestString()
- {
- $requestString = parent::getRequestString();
- if ($this->recursiveDataTableLoad
- && !Piwik_Common::getRequestVar('flat', false))
- {
- $requestString .= '&expanded=1';
- }
- return $requestString;
- }
-
- /**
- * Set the flag to load the datatable recursively so we can search on subtables as well
- *
- * @return bool if recursive search is enabled
- */
- protected function setRecursiveLoadDataTableIfSearchingForPattern()
- {
- try{
- $requestValue = Piwik_Common::getRequestVar('filter_column_recursive');
- $requestValue = Piwik_Common::getRequestVar('filter_pattern_recursive');
- // if the 2 variables are set we are searching for something.
- // we have to load all the children subtables in this case
-
- $this->recursiveDataTableLoad = true;
- return true;
- }
- catch(Exception $e) {
- $this->recursiveDataTableLoad = false;
- return false;
- }
- }
-
- /**
- * Disable the row evolution feature which is enabled by default
- */
- public function disableRowEvolution()
- {
- $this->variablesDefault['disable_row_evolution'] = true;
- }
-
- /**
- * Disables row actions.
- */
- public function disableRowActions()
- {
- $this->variablesDefault['disable_row_actions'] = true;
- }
-
+ /**
+ * Set to true when the DataTable must be loaded along with all its children subtables
+ * Useful when searching for a pattern in the DataTable Actions (we display the full hierarchy)
+ *
+ * @var bool
+ */
+ protected $recursiveDataTableLoad = false;
+
+ /**
+ * PHP array conversion of the Piwik_DataTable
+ *
+ * @var array
+ */
+ public $arrayDataTable; // phpArray
+
+ /**
+ * @see Piwik_ViewDataTable::init()
+ * @param string $currentControllerName
+ * @param string $currentControllerAction
+ * @param string $apiMethodToRequestDataTable
+ * @param null|string $controllerActionCalledWhenRequestSubTable
+ */
+ function init($currentControllerName,
+ $currentControllerAction,
+ $apiMethodToRequestDataTable,
+ $controllerActionCalledWhenRequestSubTable = null)
+ {
+ parent::init($currentControllerName,
+ $currentControllerAction,
+ $apiMethodToRequestDataTable,
+ $controllerActionCalledWhenRequestSubTable);
+ $this->dataTableTemplate = 'CoreHome/templates/datatable.tpl';
+ $this->variablesDefault['enable_sort'] = '1';
+ $this->setSortedColumn('nb_visits', 'desc');
+ $this->setLimit(Piwik_Config::getInstance()->General['datatable_default_limit']);
+ $this->handleLowPopulation();
+ }
+
+ protected function getViewDataTableId()
+ {
+ return 'table';
+ }
+
+ /**
+ * @see Piwik_ViewDataTable::main()
+ * @throws Exception|Piwik_Access_NoAccessException
+ * @return null
+ */
+ public function main()
+ {
+ if ($this->mainAlreadyExecuted) {
+ return;
+ }
+ $this->mainAlreadyExecuted = true;
+
+ $this->isDataAvailable = true;
+ try {
+ $this->loadDataTableFromAPI();
+ } catch (Piwik_Access_NoAccessException $e) {
+ throw $e;
+ } catch (Exception $e) {
+ Piwik::log("Failed to get data from API: " . $e->getMessage());
+
+ $this->isDataAvailable = false;
+ }
+
+ $this->postDataTableLoadedFromAPI();
+ $this->view = $this->buildView();
+ }
+
+ /**
+ * @return Piwik_View with all data set
+ */
+ protected function buildView()
+ {
+ $view = new Piwik_View($this->dataTableTemplate);
+
+ if (!$this->isDataAvailable) {
+ $view->arrayDataTable = array();
+ } else {
+ $columns = $this->getColumnsToDisplay();
+ $columnTranslations = $columnDocumentation = array();
+ foreach ($columns as $columnName) {
+ $columnTranslations[$columnName] = $this->getColumnTranslation($columnName);
+ $columnDocumentation[$columnName] = $this->getMetricDocumentation($columnName);
+ }
+ $nbColumns = count($columns);
+ // case no data in the array we use the number of columns set to be displayed
+ if ($nbColumns == 0) {
+ $nbColumns = count($this->columnsToDisplay);
+ }
+
+ $view->arrayDataTable = $this->getPHPArrayFromDataTable();
+ $view->dataTableColumns = $columns;
+ $view->reportDocumentation = $this->getReportDocumentation();
+ $view->columnTranslations = $columnTranslations;
+ $view->columnDocumentation = $columnDocumentation;
+ $view->nbColumns = $nbColumns;
+ $view->defaultWhenColumnValueNotDefined = '-';
+
+ // if it's likely that the report data for this data table has been purged,
+ // set whether we should display a message to that effect.
+ $view->showReportDataWasPurgedMessage = $this->hasReportBeenPurged();
+ $view->deleteReportsOlderThan = Piwik_GetOption('delete_reports_older_than');
+ }
+ $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet();
+ $view->properties = $this->getViewProperties();
+ return $view;
+ }
+
+ protected function handleLowPopulation($columnToApplyFilter = null)
+ {
+ if (Piwik_Common::getRequestVar('enable_filter_excludelowpop', '0', 'string') == '0') {
+ return;
+ }
+ if (is_null($columnToApplyFilter)) {
+ $columnToApplyFilter = 'nb_visits';
+ }
+ $this->setExcludeLowPopulation($columnToApplyFilter);
+ }
+
+ /**
+ * Returns friendly php array from the Piwik_DataTable
+ * @see Piwik_DataTable_Renderer_Php
+ * @return array
+ */
+ protected function getPHPArrayFromDataTable()
+ {
+ $renderer = Piwik_DataTable_Renderer::factory('php');
+ $renderer->setTable($this->dataTable);
+ $renderer->setSerialize(false);
+ // we get the php array from the datatable but conserving the original datatable format,
+ // ie. rows 'columns', 'metadata' and 'idsubdatatable'
+ $phpArray = $renderer->originalRender();
+ return $phpArray;
+ }
+
+
+ /**
+ * Adds a column to the list of columns to be displayed
+ *
+ * @param string $columnName
+ */
+ public function addColumnToDisplay($columnName)
+ {
+ $this->columnsToDisplay[] = $columnName;
+ }
+
+ /**
+ * Sets the search on a table to be recursive (also searches in subtables)
+ * Works only on Actions/Downloads/Outlinks tables.
+ *
+ * @return bool If the pattern for a recursive search was set in the URL
+ */
+ public function setSearchRecursive()
+ {
+ $this->variablesDefault['search_recursive'] = true;
+ return $this->setRecursiveLoadDataTableIfSearchingForPattern();
+ }
+
+ protected function getRequestString()
+ {
+ $requestString = parent::getRequestString();
+ if ($this->recursiveDataTableLoad
+ && !Piwik_Common::getRequestVar('flat', false)
+ ) {
+ $requestString .= '&expanded=1';
+ }
+ return $requestString;
+ }
+
+ /**
+ * Set the flag to load the datatable recursively so we can search on subtables as well
+ *
+ * @return bool if recursive search is enabled
+ */
+ protected function setRecursiveLoadDataTableIfSearchingForPattern()
+ {
+ try {
+ $requestValue = Piwik_Common::getRequestVar('filter_column_recursive');
+ $requestValue = Piwik_Common::getRequestVar('filter_pattern_recursive');
+ // if the 2 variables are set we are searching for something.
+ // we have to load all the children subtables in this case
+
+ $this->recursiveDataTableLoad = true;
+ return true;
+ } catch (Exception $e) {
+ $this->recursiveDataTableLoad = false;
+ return false;
+ }
+ }
+
+ /**
+ * Disable the row evolution feature which is enabled by default
+ */
+ public function disableRowEvolution()
+ {
+ $this->variablesDefault['disable_row_evolution'] = true;
+ }
+
+ /**
+ * Disables row actions.
+ */
+ public function disableRowActions()
+ {
+ $this->variablesDefault['disable_row_actions'] = true;
+ }
+
}
diff --git a/core/ViewDataTable/HtmlTable/AllColumns.php b/core/ViewDataTable/HtmlTable/AllColumns.php
index 10802b052a..dcd754de49 100644
--- a/core/ViewDataTable/HtmlTable/AllColumns.php
+++ b/core/ViewDataTable/HtmlTable/AllColumns.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
@@ -13,55 +13,53 @@
* @package Piwik
* @subpackage Piwik_ViewDataTable
*/
-class Piwik_ViewDataTable_HtmlTable_AllColumns extends Piwik_ViewDataTable_HtmlTable
+class Piwik_ViewDataTable_HtmlTable_AllColumns extends Piwik_ViewDataTable_HtmlTable
{
- protected function getViewDataTableId()
- {
- return 'tableAllColumns';
- }
-
- public function main()
- {
- $this->viewProperties['show_exclude_low_population'] = true;
- parent::main();
- }
-
- protected function getRequestString()
- {
- $requestString = parent::getRequestString();
- return $requestString . '&filter_add_columns_when_show_all_columns=1';
- }
-
- protected function postDataTableLoadedFromAPI()
- {
- $valid = parent::postDataTableLoadedFromAPI();
- if(!$valid) return false;
-
- Piwik_Controller::setPeriodVariablesView($this);
- $columnUniqueVisitors = false;
- if($this->period == 'day')
- {
- $columnUniqueVisitors = 'nb_uniq_visitors';
- }
-
- // only display conversion rate for the plugins that do not provide "per goal" metrics
- // otherwise, conversion rate is meaningless as a whole (since we don't process 'cross goals' conversions)
- $columnConversionRate = false;
- if(empty($this->viewProperties['show_goals']))
- {
- $columnConversionRate = 'conversion_rate';
- }
- $this->setColumnsToDisplay(array('label',
- 'nb_visits',
- $columnUniqueVisitors,
- 'nb_actions',
- 'nb_actions_per_visit',
- 'avg_time_on_site',
- 'bounce_rate',
- $columnConversionRate
- ));
- $this->dataTable->filter('ColumnCallbackReplace', array('avg_time_on_site', create_function('$averageTimeOnSite', 'return Piwik::getPrettyTimeFromSeconds($averageTimeOnSite);')));
-
- return true;
- }
+ protected function getViewDataTableId()
+ {
+ return 'tableAllColumns';
+ }
+
+ public function main()
+ {
+ $this->viewProperties['show_exclude_low_population'] = true;
+ parent::main();
+ }
+
+ protected function getRequestString()
+ {
+ $requestString = parent::getRequestString();
+ return $requestString . '&filter_add_columns_when_show_all_columns=1';
+ }
+
+ protected function postDataTableLoadedFromAPI()
+ {
+ $valid = parent::postDataTableLoadedFromAPI();
+ if (!$valid) return false;
+
+ Piwik_Controller::setPeriodVariablesView($this);
+ $columnUniqueVisitors = false;
+ if ($this->period == 'day') {
+ $columnUniqueVisitors = 'nb_uniq_visitors';
+ }
+
+ // only display conversion rate for the plugins that do not provide "per goal" metrics
+ // otherwise, conversion rate is meaningless as a whole (since we don't process 'cross goals' conversions)
+ $columnConversionRate = false;
+ if (empty($this->viewProperties['show_goals'])) {
+ $columnConversionRate = 'conversion_rate';
+ }
+ $this->setColumnsToDisplay(array('label',
+ 'nb_visits',
+ $columnUniqueVisitors,
+ 'nb_actions',
+ 'nb_actions_per_visit',
+ 'avg_time_on_site',
+ 'bounce_rate',
+ $columnConversionRate
+ ));
+ $this->dataTable->filter('ColumnCallbackReplace', array('avg_time_on_site', create_function('$averageTimeOnSite', 'return Piwik::getPrettyTimeFromSeconds($averageTimeOnSite);')));
+
+ return true;
+ }
}
diff --git a/core/ViewDataTable/HtmlTable/Goals.php b/core/ViewDataTable/HtmlTable/Goals.php
index 1fce3b19a8..a2ff04a12f 100644
--- a/core/ViewDataTable/HtmlTable/Goals.php
+++ b/core/ViewDataTable/HtmlTable/Goals.php
@@ -15,270 +15,246 @@
*/
class Piwik_ViewDataTable_HtmlTable_Goals extends Piwik_ViewDataTable_HtmlTable
{
- protected function getViewDataTableId()
- {
- return 'tableGoals';
- }
-
- public function main()
- {
- $this->idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
- $this->processOnlyIdGoal = Piwik_Common::getRequestVar('idGoal', Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW, 'string');
- $this->isEcommerce = $this->processOnlyIdGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER;
- $this->viewProperties['show_exclude_low_population'] = true;
- $this->viewProperties['show_goals'] = true;
-
- if (Piwik_Common::getRequestVar('documentationForGoalsPage', 0, 'int') == 1) {
- $this->setReportDocumentation(Piwik_Translate('Goals_ConversionByTypeReportDocumentation',
- array('<br />', '<br />', '<a href="http://piwik.org/docs/tracking-goals-web-analytics/" target="_blank">', '</a>')));
- }
-
-
- $this->setMetricDocumentation('nb_visits', Piwik_Translate('Goals_ColumnVisits'));
- if($this->isEcommerce)
- {
- $this->setMetricDocumentation('revenue_per_visit', Piwik_Translate('Goals_ColumnRevenuePerVisitDocumentation', Piwik_Translate('General_EcommerceOrders') ));
- $this->setColumnsTranslations( array(
- 'goal_%s_conversion_rate' => Piwik_Translate('Goals_ConversionRate'),
- 'goal_%s_nb_conversions' => Piwik_Translate('General_EcommerceOrders'),
- 'goal_%s_revenue' => Piwik_Translate('General_TotalRevenue'),
- 'goal_%s_revenue_per_visit' => Piwik_Translate('General_ColumnValuePerVisit'),
- 'goal_%s_avg_order_revenue' => Piwik_Translate('General_AverageOrderValue'),
- 'goal_%s_items' => Piwik_Translate('General_PurchasedProducts'),
- ));
- $this->setColumnsToDisplay( array(
- 'label',
- 'nb_visits',
- 'goal_%s_nb_conversions',
- 'goal_%s_revenue',
- 'goal_%s_conversion_rate',
- 'goal_%s_avg_order_revenue',
- 'goal_%s_items',
- 'goal_%s_revenue_per_visit',
- ));
-
- // Default sort column
- $this->setSortedColumn('goal_ecommerceOrder_revenue', 'desc');
- }
- else
- {
- $this->setMetricDocumentation('revenue_per_visit', Piwik_Translate('Goals_ColumnRevenuePerVisitDocumentation', Piwik_Translate('Goals_EcommerceAndGoalsMenu') ));
- $this->setColumnsTranslations( array(
- 'goal_%s_conversion_rate' => Piwik_Translate('Goals_ConversionRate'),
- 'goal_%s_nb_conversions' => Piwik_Translate('Goals_Conversions'),
- 'goal_%s_revenue' => '%s ' . Piwik_Translate('Goals_ColumnRevenue'),
- 'goal_%s_revenue_per_visit' => '%s ' . Piwik_Translate('General_ColumnValuePerVisit'),
-
- 'nb_conversions' => Piwik_Translate('Goals_ColumnConversions'),
- 'conversion_rate' => Piwik_Translate('General_ColumnConversionRate'),
- 'revenue' => Piwik_Translate('Goals_ColumnRevenue'),
- 'revenue_per_visit' => Piwik_Translate('General_ColumnValuePerVisit'),
- ));
- $this->setColumnsToDisplay( array(
- 'label',
- 'nb_visits',
- 'goal_%s_nb_conversions',
- 'goal_%s_conversion_rate',
- 'goal_%s_revenue',
- 'goal_%s_revenue_per_visit',
- 'revenue_per_visit',
- ));
-
- // Default sort column
- $columnsToDisplay = $this->getColumnsToDisplay();
- $columnNbConversionsCurrentGoal = $columnsToDisplay[2];
- if($this->processOnlyIdGoal > 0
- && strpos($columnNbConversionsCurrentGoal, '_nb_conversions') !== false)
- {
- $this->setSortedColumn($columnNbConversionsCurrentGoal, 'desc');
- }
- }
-
- parent::main();
- }
-
- public function disableSubTableWhenShowGoals()
- {
- $this->controllerActionCalledWhenRequestSubTable = null;
- }
-
- public function setColumnsToDisplay($columnsNames)
- {
- $newColumnsNames = array();
- $goals = array();
- $idSite = $this->getIdSite();
- if($idSite)
- {
- $goals = Piwik_Goals_API::getInstance()->getGoals( $idSite );
-
- $ecommerceGoal = array(
- 'idgoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER,
- 'name' => Piwik_Translate('Goals_EcommerceOrder')
- );
-
- $site = new Piwik_Site($idSite);
- //Case Ecommerce report table
- if($this->isEcommerce)
- {
- $goals = array($ecommerceGoal);
- }
- // Case tableGoals
- elseif($site->isEcommerceEnabled())
- {
- $goals = array_merge(
- array($ecommerceGoal),
- $goals
- );
- }
- }
- foreach($columnsNames as $columnName)
- {
- if(in_array($columnName, array(
- 'goal_%s_conversion_rate',
- 'goal_%s_nb_conversions',
- 'goal_%s_revenue_per_visit',
- 'goal_%s_revenue',
- 'goal_%s_avg_order_revenue',
- 'goal_%s_items',
-
- )))
- {
- foreach($goals as $goal)
- {
- $idgoal = $goal['idgoal'];
+ protected function getViewDataTableId()
+ {
+ return 'tableGoals';
+ }
- // Columns names are escaped in smarty via | escape:'html'
- $goal['name'] = Piwik_Common::unsanitizeInputValue($goal['name']);
+ public function main()
+ {
+ $this->idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
+ $this->processOnlyIdGoal = Piwik_Common::getRequestVar('idGoal', Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW, 'string');
+ $this->isEcommerce = $this->processOnlyIdGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER;
+ $this->viewProperties['show_exclude_low_population'] = true;
+ $this->viewProperties['show_goals'] = true;
- if($this->processOnlyIdGoal > Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE
- && $this->processOnlyIdGoal != $idgoal
- && !$this->isEcommerce)
- {
- continue;
- }
- $name = Piwik_Translate($this->getColumnTranslation($columnName), $goal['name']);
- $columnNameGoal = str_replace('%s', $idgoal, $columnName);
- $this->setColumnTranslation($columnNameGoal, $name);
- $this->setDynamicMetricDocumentation($columnName, $columnNameGoal, $goal['name'], $goal['idgoal']);
- if(strpos($columnNameGoal, '_rate') === false
- // For the goal table (when the flag icon is clicked), we only display the per Goal Conversion rate
- && $this->processOnlyIdGoal == Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW)
- {
- continue;
- }
+ if (Piwik_Common::getRequestVar('documentationForGoalsPage', 0, 'int') == 1) {
+ $this->setReportDocumentation(Piwik_Translate('Goals_ConversionByTypeReportDocumentation',
+ array('<br />', '<br />', '<a href="http://piwik.org/docs/tracking-goals-web-analytics/" target="_blank">', '</a>')));
+ }
- if(strstr($columnNameGoal, '_revenue') !== false)
- {
- $this->columnsToRevenueFilter[] = $columnNameGoal;
- }
- else
- {
- $this->columnsToConversionFilter[] = $columnNameGoal;
- }
- $newColumnsNames[] = $columnNameGoal;
- }
- }
- else
- {
- $newColumnsNames[] = $columnName;
- }
- }
- parent::setColumnsToDisplay($newColumnsNames);
- }
- /**
- * Find the appropriate metric documentation for a goal column
- * @param string $genericMetricName
- * @param string $metricName
- * @param string $goalName
- * @param int $idGoal
- */
- private function setDynamicMetricDocumentation($genericMetricName, $metricName, $goalName, $idGoal)
- {
- if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
- {
- $goalName = Piwik_Translate('General_EcommerceOrders');
- }
- else
- {
- $goalName = '"'.$goalName.'"';
- }
-
- $langString = false;
- switch ($genericMetricName)
- {
- case 'goal_%s_nb_conversions':
- $langString = 'Goals_ColumnConversionsDocumentation';
- break;
- case 'goal_%s_conversion_rate':
- $langString = 'Goals_ColumnConversionRateDocumentation';
- break;
- case 'goal_%s_revenue_per_visit':
- $langString = 'Goals_ColumnRevenuePerVisitDocumentation';
- break;
- case 'goal_%s_revenue':
- $langString = 'Goals_ColumnRevenueDocumentation';
- break;
- case 'goal_%s_avg_order_revenue':
- $langString = 'Goals_ColumnAverageOrderRevenueDocumentation';
- break;
- case 'goal_%s_items':
- $langString = 'Goals_ColumnPurchasedProductsDocumentation';
- break;
- }
-
- if ($langString)
- {
- $doc = Piwik_Translate($langString, $goalName);
- $this->setMetricDocumentation($metricName, $doc);
- }
- }
-
- protected function getRequestString()
- {
- $requestString = parent::getRequestString();
- if($this->processOnlyIdGoal > Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE
- || $this->isEcommerce
- )
- {
- $requestString .= "&idGoal=".$this->processOnlyIdGoal;
- }
- return $requestString . '&filter_update_columns_when_show_all_goals=1';
- }
-
- protected $columnsToRevenueFilter = array();
- protected $columnsToConversionFilter = array();
- protected $idSite = false;
-
- private function getIdSite()
- {
- return $this->idSite;
- }
-
- protected function postDataTableLoadedFromAPI()
- {
- $valid = parent::postDataTableLoadedFromAPI();
- if($valid === false) return false;
-
- foreach($this->getColumnsToDisplay() as $columnName)
- {
- if(strpos($columnName, 'conversion_rate'))
- {
- $this->dataTable->filter('ColumnCallbackReplace', array($columnName, create_function('$rate', 'if($rate==0) return "0%"; else return $rate;')));
- }
- }
- $this->columnsToRevenueFilter[] = 'revenue_per_visit';
- foreach($this->columnsToRevenueFilter as $columnName)
- {
- $this->dataTable->filter('ColumnCallbackReplace', array($columnName, create_function('$value', 'return sprintf("%.1f",$value);')));
- $this->dataTable->filter('ColumnCallbackReplace', array($columnName, array("Piwik", "getPrettyMoney"), array($this->getIdSite())));
- }
-
- foreach($this->columnsToConversionFilter as $columnName)
- {
- // this ensures that the value is set to zero for all rows where the value was not set (no conversion)
- $this->dataTable->filter('ColumnCallbackReplace', array($columnName, create_function('$value', 'return $value;')));
- }
- return true;
- }
+ $this->setMetricDocumentation('nb_visits', Piwik_Translate('Goals_ColumnVisits'));
+ if ($this->isEcommerce) {
+ $this->setMetricDocumentation('revenue_per_visit', Piwik_Translate('Goals_ColumnRevenuePerVisitDocumentation', Piwik_Translate('General_EcommerceOrders')));
+ $this->setColumnsTranslations(array(
+ 'goal_%s_conversion_rate' => Piwik_Translate('Goals_ConversionRate'),
+ 'goal_%s_nb_conversions' => Piwik_Translate('General_EcommerceOrders'),
+ 'goal_%s_revenue' => Piwik_Translate('General_TotalRevenue'),
+ 'goal_%s_revenue_per_visit' => Piwik_Translate('General_ColumnValuePerVisit'),
+ 'goal_%s_avg_order_revenue' => Piwik_Translate('General_AverageOrderValue'),
+ 'goal_%s_items' => Piwik_Translate('General_PurchasedProducts'),
+ ));
+ $this->setColumnsToDisplay(array(
+ 'label',
+ 'nb_visits',
+ 'goal_%s_nb_conversions',
+ 'goal_%s_revenue',
+ 'goal_%s_conversion_rate',
+ 'goal_%s_avg_order_revenue',
+ 'goal_%s_items',
+ 'goal_%s_revenue_per_visit',
+ ));
+
+ // Default sort column
+ $this->setSortedColumn('goal_ecommerceOrder_revenue', 'desc');
+ } else {
+ $this->setMetricDocumentation('revenue_per_visit', Piwik_Translate('Goals_ColumnRevenuePerVisitDocumentation', Piwik_Translate('Goals_EcommerceAndGoalsMenu')));
+ $this->setColumnsTranslations(array(
+ 'goal_%s_conversion_rate' => Piwik_Translate('Goals_ConversionRate'),
+ 'goal_%s_nb_conversions' => Piwik_Translate('Goals_Conversions'),
+ 'goal_%s_revenue' => '%s ' . Piwik_Translate('Goals_ColumnRevenue'),
+ 'goal_%s_revenue_per_visit' => '%s ' . Piwik_Translate('General_ColumnValuePerVisit'),
+
+ 'nb_conversions' => Piwik_Translate('Goals_ColumnConversions'),
+ 'conversion_rate' => Piwik_Translate('General_ColumnConversionRate'),
+ 'revenue' => Piwik_Translate('Goals_ColumnRevenue'),
+ 'revenue_per_visit' => Piwik_Translate('General_ColumnValuePerVisit'),
+ ));
+ $this->setColumnsToDisplay(array(
+ 'label',
+ 'nb_visits',
+ 'goal_%s_nb_conversions',
+ 'goal_%s_conversion_rate',
+ 'goal_%s_revenue',
+ 'goal_%s_revenue_per_visit',
+ 'revenue_per_visit',
+ ));
+
+ // Default sort column
+ $columnsToDisplay = $this->getColumnsToDisplay();
+ $columnNbConversionsCurrentGoal = $columnsToDisplay[2];
+ if ($this->processOnlyIdGoal > 0
+ && strpos($columnNbConversionsCurrentGoal, '_nb_conversions') !== false
+ ) {
+ $this->setSortedColumn($columnNbConversionsCurrentGoal, 'desc');
+ }
+ }
+
+ parent::main();
+ }
+
+ public function disableSubTableWhenShowGoals()
+ {
+ $this->controllerActionCalledWhenRequestSubTable = null;
+ }
+
+ public function setColumnsToDisplay($columnsNames)
+ {
+ $newColumnsNames = array();
+ $goals = array();
+ $idSite = $this->getIdSite();
+ if ($idSite) {
+ $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
+
+ $ecommerceGoal = array(
+ 'idgoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER,
+ 'name' => Piwik_Translate('Goals_EcommerceOrder')
+ );
+
+ $site = new Piwik_Site($idSite);
+ //Case Ecommerce report table
+ if ($this->isEcommerce) {
+ $goals = array($ecommerceGoal);
+ } // Case tableGoals
+ elseif ($site->isEcommerceEnabled()) {
+ $goals = array_merge(
+ array($ecommerceGoal),
+ $goals
+ );
+ }
+ }
+ foreach ($columnsNames as $columnName) {
+ if (in_array($columnName, array(
+ 'goal_%s_conversion_rate',
+ 'goal_%s_nb_conversions',
+ 'goal_%s_revenue_per_visit',
+ 'goal_%s_revenue',
+ 'goal_%s_avg_order_revenue',
+ 'goal_%s_items',
+
+ ))
+ ) {
+ foreach ($goals as $goal) {
+ $idgoal = $goal['idgoal'];
+
+ // Columns names are escaped in smarty via | escape:'html'
+ $goal['name'] = Piwik_Common::unsanitizeInputValue($goal['name']);
+
+ if ($this->processOnlyIdGoal > Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE
+ && $this->processOnlyIdGoal != $idgoal
+ && !$this->isEcommerce
+ ) {
+ continue;
+ }
+ $name = Piwik_Translate($this->getColumnTranslation($columnName), $goal['name']);
+ $columnNameGoal = str_replace('%s', $idgoal, $columnName);
+ $this->setColumnTranslation($columnNameGoal, $name);
+ $this->setDynamicMetricDocumentation($columnName, $columnNameGoal, $goal['name'], $goal['idgoal']);
+ if (strpos($columnNameGoal, '_rate') === false
+ // For the goal table (when the flag icon is clicked), we only display the per Goal Conversion rate
+ && $this->processOnlyIdGoal == Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW
+ ) {
+ continue;
+ }
+
+ if (strstr($columnNameGoal, '_revenue') !== false) {
+ $this->columnsToRevenueFilter[] = $columnNameGoal;
+ } else {
+ $this->columnsToConversionFilter[] = $columnNameGoal;
+ }
+ $newColumnsNames[] = $columnNameGoal;
+ }
+ } else {
+ $newColumnsNames[] = $columnName;
+ }
+ }
+ parent::setColumnsToDisplay($newColumnsNames);
+ }
+
+ /**
+ * Find the appropriate metric documentation for a goal column
+ * @param string $genericMetricName
+ * @param string $metricName
+ * @param string $goalName
+ * @param int $idGoal
+ */
+ private function setDynamicMetricDocumentation($genericMetricName, $metricName, $goalName, $idGoal)
+ {
+ if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ $goalName = Piwik_Translate('General_EcommerceOrders');
+ } else {
+ $goalName = '"' . $goalName . '"';
+ }
+
+ $langString = false;
+ switch ($genericMetricName) {
+ case 'goal_%s_nb_conversions':
+ $langString = 'Goals_ColumnConversionsDocumentation';
+ break;
+ case 'goal_%s_conversion_rate':
+ $langString = 'Goals_ColumnConversionRateDocumentation';
+ break;
+ case 'goal_%s_revenue_per_visit':
+ $langString = 'Goals_ColumnRevenuePerVisitDocumentation';
+ break;
+ case 'goal_%s_revenue':
+ $langString = 'Goals_ColumnRevenueDocumentation';
+ break;
+ case 'goal_%s_avg_order_revenue':
+ $langString = 'Goals_ColumnAverageOrderRevenueDocumentation';
+ break;
+ case 'goal_%s_items':
+ $langString = 'Goals_ColumnPurchasedProductsDocumentation';
+ break;
+ }
+
+ if ($langString) {
+ $doc = Piwik_Translate($langString, $goalName);
+ $this->setMetricDocumentation($metricName, $doc);
+ }
+ }
+
+ protected function getRequestString()
+ {
+ $requestString = parent::getRequestString();
+ if ($this->processOnlyIdGoal > Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE
+ || $this->isEcommerce
+ ) {
+ $requestString .= "&idGoal=" . $this->processOnlyIdGoal;
+ }
+ return $requestString . '&filter_update_columns_when_show_all_goals=1';
+ }
+
+ protected $columnsToRevenueFilter = array();
+ protected $columnsToConversionFilter = array();
+ protected $idSite = false;
+
+ private function getIdSite()
+ {
+ return $this->idSite;
+ }
+
+ protected function postDataTableLoadedFromAPI()
+ {
+ $valid = parent::postDataTableLoadedFromAPI();
+ if ($valid === false) return false;
+
+ foreach ($this->getColumnsToDisplay() as $columnName) {
+ if (strpos($columnName, 'conversion_rate')) {
+ $this->dataTable->filter('ColumnCallbackReplace', array($columnName, create_function('$rate', 'if($rate==0) return "0%"; else return $rate;')));
+ }
+ }
+ $this->columnsToRevenueFilter[] = 'revenue_per_visit';
+ foreach ($this->columnsToRevenueFilter as $columnName) {
+ $this->dataTable->filter('ColumnCallbackReplace', array($columnName, create_function('$value', 'return sprintf("%.1f",$value);')));
+ $this->dataTable->filter('ColumnCallbackReplace', array($columnName, array("Piwik", "getPrettyMoney"), array($this->getIdSite())));
+ }
+
+ foreach ($this->columnsToConversionFilter as $columnName) {
+ // this ensures that the value is set to zero for all rows where the value was not set (no conversion)
+ $this->dataTable->filter('ColumnCallbackReplace', array($columnName, create_function('$value', 'return $value;')));
+ }
+ return true;
+ }
}
diff --git a/core/ViewDataTable/Sparkline.php b/core/ViewDataTable/Sparkline.php
index 842227d395..062a9267b2 100644
--- a/core/ViewDataTable/Sparkline.php
+++ b/core/ViewDataTable/Sparkline.php
@@ -1,59 +1,56 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
* Reads the requested DataTable from the API and prepare data for the Sparkline view.
- *
+ *
* @package Piwik
* @subpackage Piwik_ViewDataTable
*/
class Piwik_ViewDataTable_Sparkline extends Piwik_ViewDataTable
{
- protected function getViewDataTableId()
- {
- return 'sparkline';
- }
+ protected function getViewDataTableId()
+ {
+ return 'sparkline';
+ }
- /**
- * @see Piwik_ViewDataTable::main()
- * @return mixed
- */
- public function main()
- {
- if($this->mainAlreadyExecuted)
- {
- return;
- }
- $this->mainAlreadyExecuted = true;
-
- // If period=range, we force the sparkline to draw daily data points
- $period = Piwik_Common::getRequestVar('period');
- if($period == 'range')
- {
- $_GET['period'] = 'day';
- }
- $this->loadDataTableFromAPI();
- // then revert the hack for potentially subsequent getRequestVar
- $_GET['period'] = $period;
-
- $values = $this->getValuesFromDataTable($this->dataTable);
- $this->isDataAvailable = true;
- if(empty($values))
- {
- $values = array_fill(0, 30, 0);
- $this->isDataAvailable = false;
- }
+ /**
+ * @see Piwik_ViewDataTable::main()
+ * @return mixed
+ */
+ public function main()
+ {
+ if ($this->mainAlreadyExecuted) {
+ return;
+ }
+ $this->mainAlreadyExecuted = true;
+
+ // If period=range, we force the sparkline to draw daily data points
+ $period = Piwik_Common::getRequestVar('period');
+ if ($period == 'range') {
+ $_GET['period'] = 'day';
+ }
+ $this->loadDataTableFromAPI();
+ // then revert the hack for potentially subsequent getRequestVar
+ $_GET['period'] = $period;
- $graph = new Piwik_Visualization_Sparkline();
- $graph->setValues($values);
+ $values = $this->getValuesFromDataTable($this->dataTable);
+ $this->isDataAvailable = true;
+ if (empty($values)) {
+ $values = array_fill(0, 30, 0);
+ $this->isDataAvailable = false;
+ }
+
+ $graph = new Piwik_Visualization_Sparkline();
+ $graph->setValues($values);
$height = Piwik_Common::getRequestVar('height', 0, 'int');
if (!empty($height)) {
@@ -66,60 +63,50 @@ class Piwik_ViewDataTable_Sparkline extends Piwik_ViewDataTable
}
$graph->main();
-
- $this->view = $graph;
- }
-
- protected function getValuesFromDataTableArray( $dataTableArray, $columnToPlot )
- {
- $dataTableArray->applyQueuedFilters();
- $values = array();
- foreach($dataTableArray->getArray() as $table)
- {
- if($table->getRowsCount() > 1)
- {
- throw new Exception("Expecting only one row per DataTable");
- }
- $value = 0;
- $onlyRow = $table->getFirstRow();
- if($onlyRow !== false)
- {
- if(!empty($columnToPlot))
- {
- $value = $onlyRow->getColumn($columnToPlot);
- }
- // if not specified, we load by default the first column found
- // eg. case of getLastDistinctCountriesGraph
- else
- {
- $columns = $onlyRow->getColumns();
- $value = current($columns);
- }
- }
- $values[] = $value;
- }
- return $values;
- }
-
- protected function getValuesFromDataTable( $dataTable )
- {
- $columns = $this->getColumnsToDisplay();
- $columnToPlot = false;
- if(!empty($columns))
- {
- $columnToPlot = $columns[0];
- }
- $values = false;
- // a Piwik_DataTable_Array is returned when using the normal code path to request data from Archives, in all core plugins
- // however plugins can also return simple datatable, hence why the sparkline can accept both data types
- if($this->dataTable instanceof Piwik_DataTable_Array)
- {
- $values = $this->getValuesFromDataTableArray($dataTable, $columnToPlot);
- }
- elseif($this->dataTable instanceof Piwik_DataTable)
- {
- $values = $this->dataTable->getColumn($columnToPlot);
- }
- return $values;
- }
+
+ $this->view = $graph;
+ }
+
+ protected function getValuesFromDataTableArray($dataTableArray, $columnToPlot)
+ {
+ $dataTableArray->applyQueuedFilters();
+ $values = array();
+ foreach ($dataTableArray->getArray() as $table) {
+ if ($table->getRowsCount() > 1) {
+ throw new Exception("Expecting only one row per DataTable");
+ }
+ $value = 0;
+ $onlyRow = $table->getFirstRow();
+ if ($onlyRow !== false) {
+ if (!empty($columnToPlot)) {
+ $value = $onlyRow->getColumn($columnToPlot);
+ } // if not specified, we load by default the first column found
+ // eg. case of getLastDistinctCountriesGraph
+ else {
+ $columns = $onlyRow->getColumns();
+ $value = current($columns);
+ }
+ }
+ $values[] = $value;
+ }
+ return $values;
+ }
+
+ protected function getValuesFromDataTable($dataTable)
+ {
+ $columns = $this->getColumnsToDisplay();
+ $columnToPlot = false;
+ if (!empty($columns)) {
+ $columnToPlot = $columns[0];
+ }
+ $values = false;
+ // a Piwik_DataTable_Array is returned when using the normal code path to request data from Archives, in all core plugins
+ // however plugins can also return simple datatable, hence why the sparkline can accept both data types
+ if ($this->dataTable instanceof Piwik_DataTable_Array) {
+ $values = $this->getValuesFromDataTableArray($dataTable, $columnToPlot);
+ } elseif ($this->dataTable instanceof Piwik_DataTable) {
+ $values = $this->dataTable->getColumn($columnToPlot);
+ }
+ return $values;
+ }
}
diff --git a/core/Visualization/Chart.php b/core/Visualization/Chart.php
index e3e664b473..5971c6c106 100644
--- a/core/Visualization/Chart.php
+++ b/core/Visualization/Chart.php
@@ -17,177 +17,166 @@
*/
abstract class Piwik_Visualization_Chart implements Piwik_View_Interface
{
-
- // the data kept here conforms to the jqplot data layout
- // @see http://www.jqplot.com/docs/files/jqPlotOptions-txt.html
- protected $series = array();
- protected $data = array();
- protected $axes = array();
- protected $tooltip = array();
- protected $seriesColors = array('#000000');
- protected $seriesPicker = array();
-
- // other attributes (not directly used for jqplot)
- protected $maxValue;
- protected $yUnit = '';
- protected $displayPercentageInTooltip = true;
- protected $xSteps = 2;
-
- /**
- * Whether to show every x-axis tick or only every other one.
- */
- protected $showAllTicks = false;
-
- public function setAxisXLabels(&$xLabels)
- {
- $this->axes['xaxis']['ticks'] = &$xLabels;
- }
-
- public function setAxisXOnClick(&$onClick)
- {
- $this->axes['xaxis']['onclick'] = &$onClick;
- }
-
- public function setAxisYValues(&$values)
- {
- foreach ($values as $label => &$data)
- {
- $this->series[] = array(
- 'label' => $label,
- 'internalLabel' => $label
- );
-
- array_walk($data, create_function('&$v', '$v = (float)$v;'));
- $this->data[] = &$data;
- }
- }
-
- protected function addTooltipToValue($seriesIndex, $valueIndex, $tooltipTitle, $tooltipText)
- {
- $this->tooltip[$seriesIndex][$valueIndex] = array($tooltipTitle, $tooltipText);
- }
-
- public function setAxisYUnit($yUnit)
- {
- $yUnits = array();
- for ($i = 0; $i < count($this->data); $i++)
- {
- $yUnits[] = $yUnit;
- }
- $this->setAxisYUnits($yUnits);
- }
-
- public function setAxisYUnits($yUnits)
- {
- // generate an axis config for each unit
- $axesIds = array();
- // associate each series with the appropriate axis
- $seriesAxes = array();
- // units for tooltips
- $seriesUnits = array();
- foreach ($yUnits as $unit)
- {
- // handle axes ids: first y[]axis, then y[2]axis, y[3]axis...
- $nextAxisId = empty($axesIds) ? '' : count($axesIds) + 1;
-
- $unit = $unit ? $unit : '';
- if (!isset($axesIds[$unit]))
- {
- $axesIds[$unit] = array('id' => $nextAxisId, 'unit' => $unit);
- $seriesAxes[] = 'y'.$nextAxisId.'axis';
- }
- else
- {
- // reuse existing axis
- $seriesAxes[] = 'y'.$axesIds[$unit]['id'].'axis';
- }
- $seriesUnits[] = $unit;
- }
-
- // generate jqplot axes config
- foreach ($axesIds as $axis) {
- $axisKey = 'y'.$axis['id'].'axis';
- $this->axes[$axisKey]['tickOptions']['formatString'] = '%s'.$axis['unit'];
- }
-
- $this->tooltip['yUnits'] = $seriesUnits;
-
- // add axis config to series
- foreach ($seriesAxes as $i => $axisName) {
- $this->series[$i]['yaxis'] = $axisName;
- }
- }
-
- public function setAxisYLabels($labels)
- {
- foreach ($this->series as &$series)
- {
- $label = $series['internalLabel'];
- if (isset($labels[$label]))
- {
- $series['label'] = $labels[$label];
- }
- }
- }
-
- public function setSelectableColumns($selectableColumns, $multiSelect=true)
- {
- $this->seriesPicker['selectableColumns'] = $selectableColumns;
- $this->seriesPicker['multiSelect'] = $multiSelect;
- }
-
- public function setDisplayPercentageInTooltip($display)
- {
- $this->displayPercentageInTooltip = $display;
- }
-
- public function setXSteps($steps)
- {
- $this->xSteps = $steps;
- }
-
- /**
- * Show every x-axis tick instead of just every other one.
- */
- public function showAllTicks()
- {
- $this->showAllTicks = true;
- }
-
- public function render()
- {
- Piwik::overrideCacheControlHeaders();
+
+ // the data kept here conforms to the jqplot data layout
+ // @see http://www.jqplot.com/docs/files/jqPlotOptions-txt.html
+ protected $series = array();
+ protected $data = array();
+ protected $axes = array();
+ protected $tooltip = array();
+ protected $seriesColors = array('#000000');
+ protected $seriesPicker = array();
+
+ // other attributes (not directly used for jqplot)
+ protected $maxValue;
+ protected $yUnit = '';
+ protected $displayPercentageInTooltip = true;
+ protected $xSteps = 2;
+
+ /**
+ * Whether to show every x-axis tick or only every other one.
+ */
+ protected $showAllTicks = false;
+
+ public function setAxisXLabels(&$xLabels)
+ {
+ $this->axes['xaxis']['ticks'] = & $xLabels;
+ }
+
+ public function setAxisXOnClick(&$onClick)
+ {
+ $this->axes['xaxis']['onclick'] = & $onClick;
+ }
+
+ public function setAxisYValues(&$values)
+ {
+ foreach ($values as $label => &$data) {
+ $this->series[] = array(
+ 'label' => $label,
+ 'internalLabel' => $label
+ );
+
+ array_walk($data, create_function('&$v', '$v = (float)$v;'));
+ $this->data[] = & $data;
+ }
+ }
+
+ protected function addTooltipToValue($seriesIndex, $valueIndex, $tooltipTitle, $tooltipText)
+ {
+ $this->tooltip[$seriesIndex][$valueIndex] = array($tooltipTitle, $tooltipText);
+ }
+
+ public function setAxisYUnit($yUnit)
+ {
+ $yUnits = array();
+ for ($i = 0; $i < count($this->data); $i++) {
+ $yUnits[] = $yUnit;
+ }
+ $this->setAxisYUnits($yUnits);
+ }
+
+ public function setAxisYUnits($yUnits)
+ {
+ // generate an axis config for each unit
+ $axesIds = array();
+ // associate each series with the appropriate axis
+ $seriesAxes = array();
+ // units for tooltips
+ $seriesUnits = array();
+ foreach ($yUnits as $unit) {
+ // handle axes ids: first y[]axis, then y[2]axis, y[3]axis...
+ $nextAxisId = empty($axesIds) ? '' : count($axesIds) + 1;
+
+ $unit = $unit ? $unit : '';
+ if (!isset($axesIds[$unit])) {
+ $axesIds[$unit] = array('id' => $nextAxisId, 'unit' => $unit);
+ $seriesAxes[] = 'y' . $nextAxisId . 'axis';
+ } else {
+ // reuse existing axis
+ $seriesAxes[] = 'y' . $axesIds[$unit]['id'] . 'axis';
+ }
+ $seriesUnits[] = $unit;
+ }
+
+ // generate jqplot axes config
+ foreach ($axesIds as $axis) {
+ $axisKey = 'y' . $axis['id'] . 'axis';
+ $this->axes[$axisKey]['tickOptions']['formatString'] = '%s' . $axis['unit'];
+ }
+
+ $this->tooltip['yUnits'] = $seriesUnits;
+
+ // add axis config to series
+ foreach ($seriesAxes as $i => $axisName) {
+ $this->series[$i]['yaxis'] = $axisName;
+ }
+ }
+
+ public function setAxisYLabels($labels)
+ {
+ foreach ($this->series as &$series) {
+ $label = $series['internalLabel'];
+ if (isset($labels[$label])) {
+ $series['label'] = $labels[$label];
+ }
+ }
+ }
+
+ public function setSelectableColumns($selectableColumns, $multiSelect = true)
+ {
+ $this->seriesPicker['selectableColumns'] = $selectableColumns;
+ $this->seriesPicker['multiSelect'] = $multiSelect;
+ }
+
+ public function setDisplayPercentageInTooltip($display)
+ {
+ $this->displayPercentageInTooltip = $display;
+ }
+
+ public function setXSteps($steps)
+ {
+ $this->xSteps = $steps;
+ }
+
+ /**
+ * Show every x-axis tick instead of just every other one.
+ */
+ public function showAllTicks()
+ {
+ $this->showAllTicks = true;
+ }
+
+ public function render()
+ {
+ Piwik::overrideCacheControlHeaders();
// See http://www.jqplot.com/docs/files/jqPlotOptions-txt.html
- $data = array(
- 'params' => array(
- 'axes' => &$this->axes,
- 'series' => &$this->series,
- 'seriesColors' => &$this->seriesColors
- ),
- 'data' => &$this->data,
- 'tooltip' => &$this->tooltip,
- 'seriesPicker' => &$this->seriesPicker
- );
+ $data = array(
+ 'params' => array(
+ 'axes' => &$this->axes,
+ 'series' => &$this->series,
+ 'seriesColors' => &$this->seriesColors
+ ),
+ 'data' => &$this->data,
+ 'tooltip' => &$this->tooltip,
+ 'seriesPicker' => &$this->seriesPicker
+ );
Piwik_PostEvent('Visualization_Chart.render', $data);
- return Piwik_Common::json_encode($data);
- }
-
- public function customizeChartProperties()
- {
- // x axis labels with steps
- if (isset($this->axes['xaxis']['ticks']))
- {
- foreach ($this->axes['xaxis']['ticks'] as $i => &$xLabel)
- {
- $this->axes['xaxis']['labels'][$i] = $xLabel;
- if (!$this->showAllTicks && ($i % $this->xSteps) != 0)
- {
- $xLabel = ' ';
- }
- }
- }
- }
-
+ return Piwik_Common::json_encode($data);
+ }
+
+ public function customizeChartProperties()
+ {
+ // x axis labels with steps
+ if (isset($this->axes['xaxis']['ticks'])) {
+ foreach ($this->axes['xaxis']['ticks'] as $i => &$xLabel) {
+ $this->axes['xaxis']['labels'][$i] = $xLabel;
+ if (!$this->showAllTicks && ($i % $this->xSteps) != 0) {
+ $xLabel = ' ';
+ }
+ }
+ }
+ }
+
}
diff --git a/core/Visualization/Chart/Evolution.php b/core/Visualization/Chart/Evolution.php
index f7dad557a1..e8e82527cf 100644
--- a/core/Visualization/Chart/Evolution.php
+++ b/core/Visualization/Chart/Evolution.php
@@ -10,38 +10,38 @@
*/
/**
- * Customize the Evolution chart style
+ * Customize the Evolution chart style
*
* @package Piwik
* @subpackage Piwik_Visualization
*/
class Piwik_Visualization_Chart_Evolution extends Piwik_Visualization_Chart
{
-
- protected $seriesColors = array('#5170AE','#F29007', '#CC3399', '#9933CC', '#80a033',
- '#246AD2', '#FD16EA', '#49C100');
-
- public function customizeChartProperties()
- {
- parent::customizeChartProperties();
-
- // if one column is a percentage we set the grid accordingly
- // note: it is invalid to plot a percentage dataset along with a numeric dataset
- if ($this->yUnit == '%'
- && $this->maxValue > 90)
- {
- $this->axes['yaxis']['ticks'] = array(0, 50, 100);
- }
- }
-
- public function getSeriesColors()
- {
- return $this->seriesColors;
- }
- public function setSelectableRows($selectableRows)
- {
- $this->seriesPicker['selectableRows'] = $selectableRows;
- }
-
+ protected $seriesColors = array('#5170AE', '#F29007', '#CC3399', '#9933CC', '#80a033',
+ '#246AD2', '#FD16EA', '#49C100');
+
+ public function customizeChartProperties()
+ {
+ parent::customizeChartProperties();
+
+ // if one column is a percentage we set the grid accordingly
+ // note: it is invalid to plot a percentage dataset along with a numeric dataset
+ if ($this->yUnit == '%'
+ && $this->maxValue > 90
+ ) {
+ $this->axes['yaxis']['ticks'] = array(0, 50, 100);
+ }
+ }
+
+ public function getSeriesColors()
+ {
+ return $this->seriesColors;
+ }
+
+ public function setSelectableRows($selectableRows)
+ {
+ $this->seriesPicker['selectableRows'] = $selectableRows;
+ }
+
}
diff --git a/core/Visualization/Chart/Pie.php b/core/Visualization/Chart/Pie.php
index 00985db7c1..f37b079ddc 100644
--- a/core/Visualization/Chart/Pie.php
+++ b/core/Visualization/Chart/Pie.php
@@ -17,42 +17,38 @@
*/
class Piwik_Visualization_Chart_Pie extends Piwik_Visualization_Chart
{
-
- protected $seriesColors = array('#59727F', '#7DAAC0', '#7F7259', '#C09E7D', '#9BB39B',
- '#B1D8B3', '#B39BA7', '#D8B1C5', '#A5A5A5');
-
- function customizeChartProperties()
- {
- if (count($this->data) == 0)
- {
- return;
- }
-
- // make sure we only have one series
- $series = &$this->series[0];
- $this->series = array(&$series);
-
- $data = &$this->data[0];
- $this->data = array(&$data);
-
- // we never plot empty pie slices (eg. visits by server time pie chart)
- foreach ($data as $i => $value)
- {
- if ($value <= 0)
- {
- unset($data[$i]);
- unset($this->axes['xaxis']['ticks'][$i]);
- }
- }
- $data = array_values($data);
- $this->axes['xaxis']['ticks'] = array_values($this->axes['xaxis']['ticks']);
-
- // prepare percentages for tooltip
- $sum = array_sum($data);
- foreach ($data as $i => $value)
- {
- $value = (float) $value;
- $this->tooltip['percentages'][0][$i] = round(100 * $value / $sum);
- }
- }
+
+ protected $seriesColors = array('#59727F', '#7DAAC0', '#7F7259', '#C09E7D', '#9BB39B',
+ '#B1D8B3', '#B39BA7', '#D8B1C5', '#A5A5A5');
+
+ function customizeChartProperties()
+ {
+ if (count($this->data) == 0) {
+ return;
+ }
+
+ // make sure we only have one series
+ $series = & $this->series[0];
+ $this->series = array(&$series);
+
+ $data = & $this->data[0];
+ $this->data = array(&$data);
+
+ // we never plot empty pie slices (eg. visits by server time pie chart)
+ foreach ($data as $i => $value) {
+ if ($value <= 0) {
+ unset($data[$i]);
+ unset($this->axes['xaxis']['ticks'][$i]);
+ }
+ }
+ $data = array_values($data);
+ $this->axes['xaxis']['ticks'] = array_values($this->axes['xaxis']['ticks']);
+
+ // prepare percentages for tooltip
+ $sum = array_sum($data);
+ foreach ($data as $i => $value) {
+ $value = (float)$value;
+ $this->tooltip['percentages'][0][$i] = round(100 * $value / $sum);
+ }
+ }
}
diff --git a/core/Visualization/Chart/VerticalBar.php b/core/Visualization/Chart/VerticalBar.php
index 7259683ad5..89fe7fb9a1 100644
--- a/core/Visualization/Chart/VerticalBar.php
+++ b/core/Visualization/Chart/VerticalBar.php
@@ -17,34 +17,30 @@
*/
class Piwik_Visualization_Chart_VerticalBar extends Piwik_Visualization_Chart
{
-
- protected $seriesColors = array('#5170AE','#F3A010', '#CC3399', '#9933CC', '#80a033',
- '#246AD2', '#FD16EA', '#49C100');
-
- public function customizeChartProperties()
- {
- parent::customizeChartProperties();
-
- if ($this->displayPercentageInTooltip)
- {
- foreach ($this->data as $seriesIndex => &$series)
- {
- $sum = array_sum($series);
-
- foreach ($series as $valueIndex => $value)
- {
- $value = (float) $value;
-
- $percentage = 0;
- if ($sum > 0)
- {
- $percentage = round(100 * $value / $sum);
- }
-
- $this->tooltip['percentages'][$seriesIndex][$valueIndex] = $percentage;
- }
- }
- }
- }
-
+
+ protected $seriesColors = array('#5170AE', '#F3A010', '#CC3399', '#9933CC', '#80a033',
+ '#246AD2', '#FD16EA', '#49C100');
+
+ public function customizeChartProperties()
+ {
+ parent::customizeChartProperties();
+
+ if ($this->displayPercentageInTooltip) {
+ foreach ($this->data as $seriesIndex => &$series) {
+ $sum = array_sum($series);
+
+ foreach ($series as $valueIndex => $value) {
+ $value = (float)$value;
+
+ $percentage = 0;
+ if ($sum > 0) {
+ $percentage = round(100 * $value / $sum);
+ }
+
+ $this->tooltip['percentages'][$seriesIndex][$valueIndex] = $percentage;
+ }
+ }
+ }
+ }
+
}
diff --git a/core/Visualization/Cloud.php b/core/Visualization/Cloud.php
index ba3a3ebabd..bf7a2227ee 100644
--- a/core/Visualization/Cloud.php
+++ b/core/Visualization/Cloud.php
@@ -11,20 +11,20 @@
/**
* Generates a tag cloud from a given data array.
- * The generated tag cloud can be in PHP format, or in HTML.
+ * The generated tag cloud can be in PHP format, or in HTML.
*
* Inspired from Derek Harvey (www.derekharvey.co.uk)
- *
+ *
* @package Piwik
* @subpackage Piwik_Visualization
*/
class Piwik_Visualization_Cloud implements Piwik_View_Interface
{
- /** Used by integration tests to make sure output is consistent. */
- public static $debugDisableShuffle = false;
+ /** Used by integration tests to make sure output is consistent. */
+ public static $debugDisableShuffle = false;
- protected $wordsArray = array();
- public $truncatingLimit = 50;
+ protected $wordsArray = array();
+ public $truncatingLimit = 50;
/**
* Assign word to array
@@ -32,94 +32,82 @@ class Piwik_Visualization_Cloud implements Piwik_View_Interface
* @param int $value
* @return string
*/
- function addWord($word, $value = 1)
- {
- if (isset($this->wordsArray[$word]))
- {
- $this->wordsArray[$word] += $value;
- }
- else
- {
- $this->wordsArray[$word] = $value;
- }
- }
+ function addWord($word, $value = 1)
+ {
+ if (isset($this->wordsArray[$word])) {
+ $this->wordsArray[$word] += $value;
+ } else {
+ $this->wordsArray[$word] = $value;
+ }
+ }
+
+ public function render()
+ {
+ $this->shuffleCloud();
+ $return = array();
+ if (empty($this->wordsArray)) {
+ return array();
+ }
+ $maxValue = max($this->wordsArray);
+ foreach ($this->wordsArray as $word => $popularity) {
+ $wordTruncated = $word;
+ if (Piwik_Common::mb_strlen($word) > $this->truncatingLimit) {
+ $wordTruncated = Piwik_Common::mb_substr($word, 0, $this->truncatingLimit - 3) . '...';
+ }
+
+ // case hideFutureHoursWhenToday=1 shows hours with no visits
+ if ($maxValue == 0) {
+ $percent = 0;
+ } else {
+ $percent = ($popularity / $maxValue) * 100;
+ }
+ // CSS style value
+ $sizeRange = $this->getClassFromPercent($percent);
+
+ $return[$word] = array(
+ 'word' => $word,
+ 'wordTruncated' => $wordTruncated,
+ 'value' => $popularity,
+ 'size' => $sizeRange,
+ 'percent' => $percent,
+ );
+ }
+ return $return;
+ }
+
+ /**
+ * Shuffle associated names in array
+ */
+ protected function shuffleCloud()
+ {
+ if (self::$debugDisableShuffle) {
+ return;
+ }
+
+ $keys = array_keys($this->wordsArray);
- public function render()
- {
- $this->shuffleCloud();
- $return = array();
- if(empty($this->wordsArray)) {
- return array();
- }
- $maxValue = max($this->wordsArray);
- foreach ($this->wordsArray as $word => $popularity)
- {
- $wordTruncated = $word;
- if(Piwik_Common::mb_strlen($word) > $this->truncatingLimit)
- {
- $wordTruncated = Piwik_Common::mb_substr($word, 0, $this->truncatingLimit - 3).'...';
- }
-
- // case hideFutureHoursWhenToday=1 shows hours with no visits
- if($maxValue == 0)
- {
- $percent = 0;
- }
- else
- {
- $percent = ($popularity / $maxValue) * 100;
- }
- // CSS style value
- $sizeRange = $this->getClassFromPercent($percent);
+ shuffle($keys);
- $return[$word] = array(
- 'word' => $word,
- 'wordTruncated' => $wordTruncated,
- 'value' => $popularity,
- 'size' => $sizeRange,
- 'percent' => $percent,
- );
- }
- return $return;
- }
-
- /**
- * Shuffle associated names in array
- */
- protected function shuffleCloud()
- {
- if (self::$debugDisableShuffle)
- {
- return;
- }
+ if (count($keys) && is_array($keys)) {
+ $tmpArray = $this->wordsArray;
+ $this->wordsArray = array();
+ foreach ($keys as $key => $value)
+ $this->wordsArray[$value] = $tmpArray[$value];
+ }
+ }
- $keys = array_keys($this->wordsArray);
-
- shuffle($keys);
-
- if (count($keys) && is_array($keys))
- {
- $tmpArray = $this->wordsArray;
- $this->wordsArray = array();
- foreach ($keys as $key => $value)
- $this->wordsArray[$value] = $tmpArray[$value];
- }
- }
-
- /**
- * Get the class range using a percentage
- *
- * @return int $class
- */
- protected function getClassFromPercent($percent)
- {
- $mapping = array(95, 70, 50, 30, 15, 5, 0);
- foreach($mapping as $key => $value)
- {
- if($percent >= $value)
- {
- return $key;
- }
- }
- }
+ /**
+ * Get the class range using a percentage
+ *
+ * @return int $class
+ */
+ protected function getClassFromPercent($percent)
+ {
+ $mapping = array(95, 70, 50, 30, 15, 5, 0);
+ foreach ($mapping as $key => $value) {
+ if ($percent >= $value) {
+ return $key;
+ }
+ }
+ }
}
diff --git a/core/Visualization/Sparkline.php b/core/Visualization/Sparkline.php
index d728bc877c..5ea0ca1571 100644
--- a/core/Visualization/Sparkline.php
+++ b/core/Visualization/Sparkline.php
@@ -17,8 +17,8 @@ require_once PIWIK_INCLUDE_PATH . '/libs/sparkline/lib/Sparkline_Line.php';
/**
* Renders a sparkline image given a PHP data array.
- * Using the Sparkline PHP Graphing Library sparkline.org
- *
+ * Using the Sparkline PHP Graphing Library sparkline.org
+ *
* @package Piwik
* @subpackage Piwik_Visualization
*/
@@ -36,39 +36,41 @@ class Piwik_Visualization_Sparkline implements Piwik_View_Interface
*/
protected $_height = 25;
- /**
- * Array with format: array( x, y, z, ... )
- * @param array $data
- */
- function setValues($data)
- {
- $this->values = $data;
- }
+ /**
+ * Array with format: array( x, y, z, ... )
+ * @param array $data
+ */
+ function setValues($data)
+ {
+ $this->values = $data;
+ }
/**
* Sets the height of the sparkline
* @param int $height
*/
- public function setHeight($height) {
+ public function setHeight($height)
+ {
if (!is_numeric($height) || $height <= 0) {
return;
}
- $this->_height = (int) $height;
+ $this->_height = (int)$height;
}
/**
* Sets the width of the sparkline
* @param int $width
*/
- public function setWidth($width) {
+ public function setWidth($width)
+ {
if (!is_numeric($width) || $width <= 0) {
return;
}
- $this->_width = (int) $width;
+ $this->_width = (int)$width;
}
/**
@@ -76,71 +78,67 @@ class Piwik_Visualization_Sparkline implements Piwik_View_Interface
* @return int
*/
public function getWidth()
- {
- return $this->_width;
- }
+ {
+ return $this->_width;
+ }
/**
* Returns the height of the sparkline
* @return int
*/
public function getHeight()
- {
- return $this->_height;
- }
-
- function main()
- {
- $width = $this->getWidth();
- $height = $this->getHeight();
-
- $sparkline = new Sparkline_Line();
- $sparkline->SetColor('lineColor', 22, 44, 74); // dark blue
- $sparkline->SetColorHtml('red', '#FF7F7F');
- $sparkline->SetColorHtml('blue', '#55AAFF');
- $sparkline->SetColorHtml('green', '#75BF7C');
-
- $min = $max = $last = null;
- $i = 0;
- $toRemove = array('%', str_replace('%s', '', Piwik_Translate('General_Seconds')));
- foreach($this->values as $value)
- {
- // 50% and 50s should be plotted as 50
- $value = str_replace($toRemove, '', $value);
- // replace localized decimal separator
- $value = str_replace(',', '.', $value);
- if ($value == '')
- {
- $value = 0;
- }
-
- $sparkline->SetData($i, $value);
-
- if( null == $min || $value <= $min[1])
- {
- $min = array($i, $value);
- }
- if(null == $max || $value >= $max[1])
- {
- $max = array($i, $value);
- }
- $last = array($i, $value);
- $i++;
- }
- $sparkline->SetYMin(0);
- $sparkline->SetYMax($max[1]);
- $sparkline->SetPadding( 3, 0, 2, 0 ); // top, right, bottom, left
- $sparkline->SetFeaturePoint($min[0], $min[1], 'red', 5);
- $sparkline->SetFeaturePoint($max[0], $max[1], 'green', 5);
- $sparkline->SetFeaturePoint($last[0], $last[1], 'blue', 5);
- $sparkline->SetLineSize(3); // for renderresampled, linesize is on virtual image
- $ratio = 1;
- $sparkline->RenderResampled($width*$ratio, $height*$ratio);
- $this->sparkline = $sparkline;
- }
-
- function render()
- {
- $this->sparkline->Output();
- }
+ {
+ return $this->_height;
+ }
+
+ function main()
+ {
+ $width = $this->getWidth();
+ $height = $this->getHeight();
+
+ $sparkline = new Sparkline_Line();
+ $sparkline->SetColor('lineColor', 22, 44, 74); // dark blue
+ $sparkline->SetColorHtml('red', '#FF7F7F');
+ $sparkline->SetColorHtml('blue', '#55AAFF');
+ $sparkline->SetColorHtml('green', '#75BF7C');
+
+ $min = $max = $last = null;
+ $i = 0;
+ $toRemove = array('%', str_replace('%s', '', Piwik_Translate('General_Seconds')));
+ foreach ($this->values as $value) {
+ // 50% and 50s should be plotted as 50
+ $value = str_replace($toRemove, '', $value);
+ // replace localized decimal separator
+ $value = str_replace(',', '.', $value);
+ if ($value == '') {
+ $value = 0;
+ }
+
+ $sparkline->SetData($i, $value);
+
+ if (null == $min || $value <= $min[1]) {
+ $min = array($i, $value);
+ }
+ if (null == $max || $value >= $max[1]) {
+ $max = array($i, $value);
+ }
+ $last = array($i, $value);
+ $i++;
+ }
+ $sparkline->SetYMin(0);
+ $sparkline->SetYMax($max[1]);
+ $sparkline->SetPadding(3, 0, 2, 0); // top, right, bottom, left
+ $sparkline->SetFeaturePoint($min[0], $min[1], 'red', 5);
+ $sparkline->SetFeaturePoint($max[0], $max[1], 'green', 5);
+ $sparkline->SetFeaturePoint($last[0], $last[1], 'blue', 5);
+ $sparkline->SetLineSize(3); // for renderresampled, linesize is on virtual image
+ $ratio = 1;
+ $sparkline->RenderResampled($width * $ratio, $height * $ratio);
+ $this->sparkline = $sparkline;
+ }
+
+ function render()
+ {
+ $this->sparkline->Output();
+ }
}
diff --git a/core/testMinimumPhpVersion.php b/core/testMinimumPhpVersion.php
index 508602e5d0..4646cdbc95 100644
--- a/core/testMinimumPhpVersion.php
+++ b/core/testMinimumPhpVersion.php
@@ -1,16 +1,16 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik
* @package Piwik
*/
/**
- * This file is executed before anything else.
+ * This file is executed before anything else.
* It checks the minimum PHP version required to run Piwik.
* This file must be compatible PHP4.
*/
@@ -20,48 +20,41 @@ $piwik_errorMessage = '';
// Minimum requirement: ->newInstanceArgs in 5.1.3
$piwik_minimumPHPVersion = '5.1.3RC';
$piwik_currentPHPVersion = PHP_VERSION;
-$minimumPhpInvalid = version_compare($piwik_minimumPHPVersion , $piwik_currentPHPVersion ) > 0;
-if( $minimumPhpInvalid )
-{
- $piwik_errorMessage .= "<p><b>To run Piwik you need at least PHP version $piwik_minimumPHPVersion</b></p>
+$minimumPhpInvalid = version_compare($piwik_minimumPHPVersion, $piwik_currentPHPVersion) > 0;
+if ($minimumPhpInvalid) {
+ $piwik_errorMessage .= "<p><b>To run Piwik you need at least PHP version $piwik_minimumPHPVersion</b></p>
<p>Unfortunately it seems your webserver is using PHP version $piwik_currentPHPVersion. </p>
<p>Please try to update your PHP version, Piwik is really worth it! Nowadays most web hosts
support PHP $piwik_minimumPHPVersion.</p>
<p>Also see the FAQ: <a href='http://piwik.org/faq/how-to-install/#faq_77'>My Web host supports PHP4 by default. How can I enable PHP5?</a></p>";
-}
-else
-{
- $piwik_zend_compatibility_mode = ini_get("zend.ze1_compatibility_mode");
- if($piwik_zend_compatibility_mode == 1)
- {
- $piwik_errorMessage .= "<p><b>Piwik is not compatible with the directive <code>zend.ze1_compatibility_mode = On</code></b></p>
+} else {
+ $piwik_zend_compatibility_mode = ini_get("zend.ze1_compatibility_mode");
+ if ($piwik_zend_compatibility_mode == 1) {
+ $piwik_errorMessage .= "<p><b>Piwik is not compatible with the directive <code>zend.ze1_compatibility_mode = On</code></b></p>
<p>It seems your php.ini file has <pre>zend.ze1_compatibility_mode = On</pre>It makes PHP5 behave like PHP4.
If you want to use Piwik you need to set <pre>zend.ze1_compatibility_mode = Off</pre> in your php.ini configuration file, and restart your web server. You may have to ask your system administrator.</p>";
- }
+ }
- if(!class_exists('ArrayObject'))
- {
- $piwik_errorMessage .= "<p><b>Piwik and Zend Framework require the SPL extension</b></p>
+ if (!class_exists('ArrayObject')) {
+ $piwik_errorMessage .= "<p><b>Piwik and Zend Framework require the SPL extension</b></p>
<p>It appears your PHP was compiled with <pre>--disable-spl</pre>.
To enjoy Piwik, you need PHP compiled without that configure option.</p>";
- }
+ }
- if(!extension_loaded('session'))
- {
- $piwik_errorMessage .= "<p><b>Piwik and Zend_Session require the session extension</b></p>
+ if (!extension_loaded('session')) {
+ $piwik_errorMessage .= "<p><b>Piwik and Zend_Session require the session extension</b></p>
<p>It appears your PHP was compiled with <pre>--disable-session</pre>.
To enjoy Piwik, you need PHP compiled without that configure option.</p>";
- }
+ }
- if(!function_exists('ini_set'))
- {
- $piwik_errorMessage .= "<p><b>Piwik and Zend_Session require the <code>ini_set()</code> function</b></p>
+ if (!function_exists('ini_set')) {
+ $piwik_errorMessage .= "<p><b>Piwik and Zend_Session require the <code>ini_set()</code> function</b></p>
<p>It appears your PHP has disabled this function.
To enjoy Piwik, you need remove <pre>ini_set</pre> from your <pre>disable_functions</pre> directive in php.ini, and restart your webserver.</p>";
- }
+ }
}
-if(!function_exists('Piwik_ExitWithMessage')) {
+if (!function_exists('Piwik_ExitWithMessage')) {
/**
* Displays info/warning/error message in a friendly UI and exits.
*
@@ -72,12 +65,10 @@ if(!function_exists('Piwik_ExitWithMessage')) {
function Piwik_ExitWithMessage($message, $optionalTrace = false, $optionalLinks = false)
{
@header('Content-Type: text/html; charset=utf-8');
- if($optionalTrace)
- {
- $optionalTrace = '<span style="color:#888888">Backtrace:<br /><pre>'.$optionalTrace.'</pre></span>';
+ if ($optionalTrace) {
+ $optionalTrace = '<span style="color:#888888">Backtrace:<br /><pre>' . $optionalTrace . '</pre></span>';
}
- if($optionalLinks)
- {
+ if ($optionalLinks) {
$optionalLinks = '<ul>
<li><a target="_blank" href="http://piwik.org">Piwik.org homepage</a></li>
<li><a target="_blank" href="http://piwik.org/faq/">Piwik Frequently Asked Questions</a></li>
@@ -90,17 +81,16 @@ if(!function_exists('Piwik_ExitWithMessage')) {
$footerPage = file_get_contents(PIWIK_INCLUDE_PATH . '/themes/default/simple_structure_footer.tpl');
$headerPage = str_replace('{$HTML_TITLE}', 'Piwik &rsaquo; Error', $headerPage);
- $content = '<p>'.$message.'</p>
+ $content = '<p>' . $message . '</p>
<p><a href="index.php">Go to Piwik</a><br/>
<a href="index.php?module=Login">Login</a></p>
- '. $optionalTrace .' '. $optionalLinks;
+ ' . $optionalTrace . ' ' . $optionalLinks;
echo $headerPage . $content . $footerPage;
exit;
}
}
-if(!empty($piwik_errorMessage))
-{
- Piwik_ExitWithMessage($piwik_errorMessage, false, true);
+if (!empty($piwik_errorMessage)) {
+ Piwik_ExitWithMessage($piwik_errorMessage, false, true);
}
diff --git a/index.php b/index.php
index ce36320bae..09281fd3de 100644
--- a/index.php
+++ b/index.php
@@ -8,24 +8,21 @@
* @package Piwik
*/
-define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__)=='/'?'':dirname(__FILE__));
-if(file_exists(PIWIK_DOCUMENT_ROOT . '/bootstrap.php'))
-{
- require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php';
+define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__) == '/' ? '' : dirname(__FILE__));
+if (file_exists(PIWIK_DOCUMENT_ROOT . '/bootstrap.php')) {
+ require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php';
}
-error_reporting(E_ALL|E_NOTICE);
-@ini_set('display_errors', defined('PIWIK_DISPLAY_ERRORS') ? PIWIK_DISPLAY_ERRORS : @ini_get('display_errors'));
+error_reporting(E_ALL | E_NOTICE);
+@ini_set('display_errors', defined('PIWIK_DISPLAY_ERRORS') ? PIWIK_DISPLAY_ERRORS : @ini_get('display_errors'));
@ini_set('xdebug.show_exception_trace', 0);
@ini_set('magic_quotes_runtime', 0);
-if(!defined('PIWIK_USER_PATH'))
-{
- define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT);
+if (!defined('PIWIK_USER_PATH')) {
+ define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT);
}
-if(!defined('PIWIK_INCLUDE_PATH'))
-{
- define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT);
+if (!defined('PIWIK_INCLUDE_PATH')) {
+ define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT);
}
require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php';
@@ -35,19 +32,17 @@ require_once PIWIK_INCLUDE_PATH . '/core/testMinimumPhpVersion.php';
session_cache_limiter('nocache');
@date_default_timezone_set('UTC');
-require_once PIWIK_INCLUDE_PATH .'/core/Loader.php';
-
-if(!defined('PIWIK_ENABLE_ERROR_HANDLER') || PIWIK_ENABLE_ERROR_HANDLER)
-{
- require_once PIWIK_INCLUDE_PATH .'/core/ErrorHandler.php';
- require_once PIWIK_INCLUDE_PATH .'/core/ExceptionHandler.php';
- set_error_handler('Piwik_ErrorHandler');
- set_exception_handler('Piwik_ExceptionHandler');
+require_once PIWIK_INCLUDE_PATH . '/core/Loader.php';
+
+if (!defined('PIWIK_ENABLE_ERROR_HANDLER') || PIWIK_ENABLE_ERROR_HANDLER) {
+ require_once PIWIK_INCLUDE_PATH . '/core/ErrorHandler.php';
+ require_once PIWIK_INCLUDE_PATH . '/core/ExceptionHandler.php';
+ set_error_handler('Piwik_ErrorHandler');
+ set_exception_handler('Piwik_ExceptionHandler');
}
-if(!defined('PIWIK_ENABLE_DISPATCH') || PIWIK_ENABLE_DISPATCH)
-{
- $controller = Piwik_FrontController::getInstance();
- $controller->init();
- $controller->dispatch();
+if (!defined('PIWIK_ENABLE_DISPATCH') || PIWIK_ENABLE_DISPATCH) {
+ $controller = Piwik_FrontController::getInstance();
+ $controller->init();
+ $controller->dispatch();
}
diff --git a/libs/PiwikTracker/PiwikTracker.php b/libs/PiwikTracker/PiwikTracker.php
index 7810fb196f..97e5acbc82 100644
--- a/libs/PiwikTracker/PiwikTracker.php
+++ b/libs/PiwikTracker/PiwikTracker.php
@@ -24,160 +24,158 @@
*/
class PiwikTracker
{
- /**
- * Piwik base URL, for example http://example.org/piwik/
- * Must be set before using the class by calling
- * PiwikTracker::$URL = 'http://yourwebsite.org/piwik/';
- *
- * @var string
- */
- static public $URL = '';
-
- /**
- * API Version
- *
- * @ignore
- * @var int
- */
- const VERSION = 1;
-
- /**
- * @ignore
- */
- public $DEBUG_APPEND_URL = '';
-
- /**
- * Visitor ID length
- *
- * @ignore
- */
- const LENGTH_VISITOR_ID = 16;
-
- /**
- * Charset
- * @see setPageCharset
- * @ignore
- */
- const DEFAULT_CHARSET_PARAMETER_VALUES = 'utf-8';
-
- /**
- * Builds a PiwikTracker object, used to track visits, pages and Goal conversions
- * for a specific website, by using the Piwik Tracking API.
- *
- * @param int $idSite Id site to be tracked
- * @param string $apiUrl "http://example.org/piwik/" or "http://piwik.example.org/"
- * If set, will overwrite PiwikTracker::$URL
- */
- function __construct( $idSite, $apiUrl = '' )
+ /**
+ * Piwik base URL, for example http://example.org/piwik/
+ * Must be set before using the class by calling
+ * PiwikTracker::$URL = 'http://yourwebsite.org/piwik/';
+ *
+ * @var string
+ */
+ static public $URL = '';
+
+ /**
+ * API Version
+ *
+ * @ignore
+ * @var int
+ */
+ const VERSION = 1;
+
+ /**
+ * @ignore
+ */
+ public $DEBUG_APPEND_URL = '';
+
+ /**
+ * Visitor ID length
+ *
+ * @ignore
+ */
+ const LENGTH_VISITOR_ID = 16;
+
+ /**
+ * Charset
+ * @see setPageCharset
+ * @ignore
+ */
+ const DEFAULT_CHARSET_PARAMETER_VALUES = 'utf-8';
+
+ /**
+ * Builds a PiwikTracker object, used to track visits, pages and Goal conversions
+ * for a specific website, by using the Piwik Tracking API.
+ *
+ * @param int $idSite Id site to be tracked
+ * @param string $apiUrl "http://example.org/piwik/" or "http://piwik.example.org/"
+ * If set, will overwrite PiwikTracker::$URL
+ */
+ function __construct($idSite, $apiUrl = '')
{
- $this->cookieSupport = true;
- $this->userAgent = false;
- $this->localHour = false;
- $this->localMinute = false;
- $this->localSecond = false;
- $this->hasCookies = false;
- $this->plugins = false;
- $this->visitorCustomVar = false;
- $this->pageCustomVar = false;
- $this->customData = false;
- $this->forcedDatetime = false;
- $this->token_auth = false;
- $this->attributionInfo = false;
- $this->ecommerceLastOrderTimestamp = false;
- $this->ecommerceItems = array();
- $this->generationTime = false;
-
- $this->requestCookie = '';
- $this->idSite = $idSite;
- $this->urlReferrer = @$_SERVER['HTTP_REFERER'];
- $this->pageCharset = self::DEFAULT_CHARSET_PARAMETER_VALUES;
- $this->pageUrl = self::getCurrentUrl();
- $this->ip = @$_SERVER['REMOTE_ADDR'];
- $this->acceptLanguage = @$_SERVER['HTTP_ACCEPT_LANGUAGE'];
- $this->userAgent = @$_SERVER['HTTP_USER_AGENT'];
- if(!empty($apiUrl))
- {
- self::$URL = $apiUrl;
- }
- $this->setNewVisitorId();
-
- // Allow debug while blocking the request
- $this->requestTimeout = 600;
- $this->doBulkRequests = false;
- $this->storedTrackingActions = array();
+ $this->cookieSupport = true;
+ $this->userAgent = false;
+ $this->localHour = false;
+ $this->localMinute = false;
+ $this->localSecond = false;
+ $this->hasCookies = false;
+ $this->plugins = false;
+ $this->visitorCustomVar = false;
+ $this->pageCustomVar = false;
+ $this->customData = false;
+ $this->forcedDatetime = false;
+ $this->token_auth = false;
+ $this->attributionInfo = false;
+ $this->ecommerceLastOrderTimestamp = false;
+ $this->ecommerceItems = array();
+ $this->generationTime = false;
+
+ $this->requestCookie = '';
+ $this->idSite = $idSite;
+ $this->urlReferrer = @$_SERVER['HTTP_REFERER'];
+ $this->pageCharset = self::DEFAULT_CHARSET_PARAMETER_VALUES;
+ $this->pageUrl = self::getCurrentUrl();
+ $this->ip = @$_SERVER['REMOTE_ADDR'];
+ $this->acceptLanguage = @$_SERVER['HTTP_ACCEPT_LANGUAGE'];
+ $this->userAgent = @$_SERVER['HTTP_USER_AGENT'];
+ if (!empty($apiUrl)) {
+ self::$URL = $apiUrl;
+ }
+ $this->setNewVisitorId();
+
+ // Allow debug while blocking the request
+ $this->requestTimeout = 600;
+ $this->doBulkRequests = false;
+ $this->storedTrackingActions = array();
}
- /**
- * By default, Piwik expects utf-8 encoded values, for example
- * for the page URL parameter values, Page Title, etc.
- * It is recommended to only send UTF-8 data to Piwik.
- * If required though, you can also specify another charset using this function.
- *
- * @param string $charset
- */
- public function setPageCharset( $charset = '' )
- {
- $this->pageCharset = $charset;
- }
+ /**
+ * By default, Piwik expects utf-8 encoded values, for example
+ * for the page URL parameter values, Page Title, etc.
+ * It is recommended to only send UTF-8 data to Piwik.
+ * If required though, you can also specify another charset using this function.
+ *
+ * @param string $charset
+ */
+ public function setPageCharset($charset = '')
+ {
+ $this->pageCharset = $charset;
+ }
/**
* Sets the current URL being tracked
- *
+ *
* @param string $url Raw URL (not URL encoded)
*/
- public function setUrl( $url )
+ public function setUrl($url)
{
- $this->pageUrl = $url;
+ $this->pageUrl = $url;
}
/**
* Sets the URL referrer used to track Referrers details for new visits.
- *
+ *
* @param string $url Raw URL (not URL encoded)
*/
- public function setUrlReferrer( $url )
+ public function setUrlReferrer($url)
{
- $this->urlReferrer = $url;
+ $this->urlReferrer = $url;
}
- /**
- * Sets the time that generating the document on the server side took.
- *
- * @param int $timeMs Generation time in ms
- */
- public function setGenerationTime( $timeMs )
- {
- $this->generationTime = $timeMs;
- }
-
- /**
- * @deprecated
+ /**
+ * Sets the time that generating the document on the server side took.
+ *
+ * @param int $timeMs Generation time in ms
+ */
+ public function setGenerationTime($timeMs)
+ {
+ $this->generationTime = $timeMs;
+ }
+
+ /**
+ * @deprecated
* @ignore
*/
- public function setUrlReferer( $url )
+ public function setUrlReferer($url)
{
- $this->setUrlReferrer($url);
+ $this->setUrlReferrer($url);
}
-
+
/**
- * Sets the attribution information to the visit, so that subsequent Goal conversions are
+ * Sets the attribution information to the visit, so that subsequent Goal conversions are
* properly attributed to the right Referrer URL, timestamp, Campaign Name & Keyword.
- *
- * This must be a JSON encoded string that would typically be fetched from the JS API:
- * piwikTracker.getAttributionInfo() and that you have JSON encoded via JSON2.stringify()
+ *
+ * This must be a JSON encoded string that would typically be fetched from the JS API:
+ * piwikTracker.getAttributionInfo() and that you have JSON encoded via JSON2.stringify()
*
* @param string $jsonEncoded JSON encoded array containing Attribution info
- * @throws Exception
+ * @throws Exception
* @see function getAttributionInfo() in https://github.com/piwik/piwik/blob/master/js/piwik.js
*/
- public function setAttributionInfo( $jsonEncoded )
+ public function setAttributionInfo($jsonEncoded)
{
- $decoded = json_decode($jsonEncoded, $assoc = true);
- if(!is_array($decoded))
- {
- throw new Exception("setAttributionInfo() is expecting a JSON encoded string, $jsonEncoded given");
- }
- $this->attributionInfo = $decoded;
+ $decoded = json_decode($jsonEncoded, $assoc = true);
+ if (!is_array($decoded)) {
+ throw new Exception("setAttributionInfo() is expecting a JSON encoded string, $jsonEncoded given");
+ }
+ $this->attributionInfo = $decoded;
}
/**
@@ -188,115 +186,103 @@ class PiwikTracker
* @param string $name Custom variable name
* @param string $value Custom variable value
* @param string $scope Custom variable scope. Possible values: visit, page
- * @throws Exception
+ * @throws Exception
*/
public function setCustomVariable($id, $name, $value, $scope = 'visit')
{
- if(!is_int($id))
- {
- throw new Exception("Parameter id to setCustomVariable should be an integer");
- }
- if($scope == 'page')
- {
- $this->pageCustomVar[$id] = array($name, $value);
- }
- elseif($scope == 'visit')
- {
- $this->visitorCustomVar[$id] = array($name, $value);
- }
- else
- {
- throw new Exception("Invalid 'scope' parameter value");
- }
+ if (!is_int($id)) {
+ throw new Exception("Parameter id to setCustomVariable should be an integer");
+ }
+ if ($scope == 'page') {
+ $this->pageCustomVar[$id] = array($name, $value);
+ } elseif ($scope == 'visit') {
+ $this->visitorCustomVar[$id] = array($name, $value);
+ } else {
+ throw new Exception("Invalid 'scope' parameter value");
+ }
}
-
+
/**
* Returns the currently assigned Custom Variable stored in a first party cookie.
- *
+ *
* This function will only work if the user is initiating the current request, and his cookies
* can be read by PHP from the $_COOKIE array.
- *
+ *
* @param int $id Custom Variable integer index to fetch from cookie. Should be a value from 1 to 5
* @param string $scope Custom variable scope. Possible values: visit, page
*
- * @throws Exception
+ * @throws Exception
* @return mixed An array with this format: array( 0 => CustomVariableName, 1 => CustomVariableValue ) or false
* @see Piwik.js getCustomVariable()
*/
public function getCustomVariable($id, $scope = 'visit')
{
- if($scope == 'page')
- {
- return isset($this->pageCustomVar[$id]) ? $this->pageCustomVar[$id] : false;
- }
- else if($scope != 'visit')
- {
- throw new Exception("Invalid 'scope' parameter value");
- }
- if(!empty($this->visitorCustomVar[$id]))
- {
- return $this->visitorCustomVar[$id];
- }
- $customVariablesCookie = 'cvar.'.$this->idSite.'.';
- $cookie = $this->getCookieMatchingName($customVariablesCookie);
- if(!$cookie)
- {
- return false;
- }
- if(!is_int($id))
- {
- throw new Exception("Parameter to getCustomVariable should be an integer");
- }
- $cookieDecoded = json_decode($cookie, $assoc = true);
- if(!is_array($cookieDecoded)
- || !isset($cookieDecoded[$id])
- || !is_array($cookieDecoded[$id])
- || count($cookieDecoded[$id]) != 2)
- {
- return false;
- }
- return $cookieDecoded[$id];
+ if ($scope == 'page') {
+ return isset($this->pageCustomVar[$id]) ? $this->pageCustomVar[$id] : false;
+ } else if ($scope != 'visit') {
+ throw new Exception("Invalid 'scope' parameter value");
+ }
+ if (!empty($this->visitorCustomVar[$id])) {
+ return $this->visitorCustomVar[$id];
+ }
+ $customVariablesCookie = 'cvar.' . $this->idSite . '.';
+ $cookie = $this->getCookieMatchingName($customVariablesCookie);
+ if (!$cookie) {
+ return false;
+ }
+ if (!is_int($id)) {
+ throw new Exception("Parameter to getCustomVariable should be an integer");
+ }
+ $cookieDecoded = json_decode($cookie, $assoc = true);
+ if (!is_array($cookieDecoded)
+ || !isset($cookieDecoded[$id])
+ || !is_array($cookieDecoded[$id])
+ || count($cookieDecoded[$id]) != 2
+ ) {
+ return false;
+ }
+ return $cookieDecoded[$id];
}
-
+
/**
* Sets the current visitor ID to a random new one.
*/
public function setNewVisitorId()
{
- $this->visitorId = substr(md5(uniqid(rand(), true)), 0, self::LENGTH_VISITOR_ID);
+ $this->visitorId = substr(md5(uniqid(rand(), true)), 0, self::LENGTH_VISITOR_ID);
}
-
+
/**
* Sets the current site ID.
- *
+ *
* @param int $idSite
*/
- public function setIdSite( $idSite )
+ public function setIdSite($idSite)
{
- $this->idSite = $idSite;
+ $this->idSite = $idSite;
}
-
+
/**
* Sets the Browser language. Used to guess visitor countries when GeoIP is not enabled
- *
+ *
* @param string $acceptLanguage For example "fr-fr"
*/
- public function setBrowserLanguage( $acceptLanguage )
+ public function setBrowserLanguage($acceptLanguage)
{
- $this->acceptLanguage = $acceptLanguage;
+ $this->acceptLanguage = $acceptLanguage;
}
/**
* Sets the user agent, used to detect OS and browser.
* If this function is not called, the User Agent will default to the current user agent.
- *
+ *
* @param string $userAgent
*/
public function setUserAgent($userAgent)
{
- $this->userAgent = $userAgent;
+ $this->userAgent = $userAgent;
}
-
+
/**
* Sets the country of the visitor. If not used, Piwik will try to find the country
* using either the visitor's IP address or language.
@@ -306,9 +292,9 @@ class PiwikTracker
*/
public function setCountry($country)
{
- $this->country = $country;
+ $this->country = $country;
}
-
+
/**
* Sets the region of the visitor. If not used, Piwik may try to find the region
* using the visitor's IP address (if configured to do so).
@@ -318,9 +304,9 @@ class PiwikTracker
*/
public function setRegion($region)
{
- $this->region = $region;
+ $this->region = $region;
}
-
+
/**
* Sets the city of the visitor. If not used, Piwik may try to find the city
* using the visitor's IP address (if configured to do so).
@@ -330,9 +316,9 @@ class PiwikTracker
*/
public function setCity($city)
{
- $this->city = $city;
+ $this->city = $city;
}
-
+
/**
* Sets the latitude of the visitor. If not used, Piwik may try to find the visitor's
* latitude using the visitor's IP address (if configured to do so).
@@ -342,9 +328,9 @@ class PiwikTracker
*/
public function setLatitude($lat)
{
- $this->lat = $lat;
+ $this->lat = $lat;
}
-
+
/**
* Sets the longitude of the visitor. If not used, Piwik may try to find the visitor's
* longitude using the visitor's IP address (if configured to do so).
@@ -354,62 +340,62 @@ class PiwikTracker
*/
public function setLongitude($long)
{
- $this->long = $long;
+ $this->long = $long;
}
-
- /**
- * Enables the bulk request feature. When used, each tracking action is stored until the
- * doBulkTrack method is called. This method will send all tracking data at once.
- */
- public function enableBulkTracking()
- {
- $this->doBulkRequests = true;
- }
-
+
+ /**
+ * Enables the bulk request feature. When used, each tracking action is stored until the
+ * doBulkTrack method is called. This method will send all tracking data at once.
+ */
+ public function enableBulkTracking()
+ {
+ $this->doBulkRequests = true;
+ }
+
/**
* Tracks a page view
- *
+ *
* @param string $documentTitle Page title as it will appear in the Actions > Page titles report
* @return mixed Response string or true if using bulk requests.
*/
- public function doTrackPageView( $documentTitle )
+ public function doTrackPageView($documentTitle)
{
- $url = $this->getUrlTrackPageView($documentTitle);
- return $this->sendRequest($url);
+ $url = $this->getUrlTrackPageView($documentTitle);
+ return $this->sendRequest($url);
}
- /**
- * Tracks an internal Site Search query, and optionally tracks the Search Category, and Search results Count.
- * These are used to populate reports in Actions > Site Search.
- *
- * @param string $keyword Searched query on the site
- * @param string $category Optional, Search engine category if applicable
- * @param int $countResults results displayed on the search result page. Used to track "zero result" keywords.
- *
- * @return mixed Response or true if using bulk requests.
- */
- public function doTrackSiteSearch( $keyword, $category = '', $countResults = false )
- {
- $url = $this->getUrlTrackSiteSearch($keyword, $category, $countResults);
- return $this->sendRequest($url);
- }
+ /**
+ * Tracks an internal Site Search query, and optionally tracks the Search Category, and Search results Count.
+ * These are used to populate reports in Actions > Site Search.
+ *
+ * @param string $keyword Searched query on the site
+ * @param string $category Optional, Search engine category if applicable
+ * @param int $countResults results displayed on the search result page. Used to track "zero result" keywords.
+ *
+ * @return mixed Response or true if using bulk requests.
+ */
+ public function doTrackSiteSearch($keyword, $category = '', $countResults = false)
+ {
+ $url = $this->getUrlTrackSiteSearch($keyword, $category, $countResults);
+ return $this->sendRequest($url);
+ }
/**
* Records a Goal conversion
- *
+ *
* @param int $idGoal Id Goal to record a conversion
* @param float $revenue Revenue for this conversion
* @return mixed Response or true if using bulk request
*/
public function doTrackGoal($idGoal, $revenue = 0.0)
{
- $url = $this->getUrlTrackGoal($idGoal, $revenue);
- return $this->sendRequest($url);
+ $url = $this->getUrlTrackGoal($idGoal, $revenue);
+ return $this->sendRequest($url);
}
-
+
/**
* Tracks a download or outlink
- *
+ *
* @param string $actionUrl URL of the download or outlink
* @param string $actionType Type of the action: 'download' or 'link'
* @return mixed Response or true if using bulk request
@@ -417,270 +403,257 @@ class PiwikTracker
public function doTrackAction($actionUrl, $actionType)
{
// Referrer could be udpated to be the current URL temporarily (to mimic JS behavior)
- $url = $this->getUrlTrackAction($actionUrl, $actionType);
- return $this->sendRequest($url);
+ $url = $this->getUrlTrackAction($actionUrl, $actionType);
+ return $this->sendRequest($url);
}
/**
* Adds an item in the Ecommerce order.
- *
+ *
* This should be called before doTrackEcommerceOrder(), or before doTrackEcommerceCartUpdate().
* This function can be called for all individual products in the cart (or order).
* SKU parameter is mandatory. Other parameters are optional (set to false if value not known).
* Ecommerce items added via this function are automatically cleared when doTrackEcommerceOrder() or getUrlTrackEcommerceOrder() is called.
- *
- * @param string $sku (required) SKU, Product identifier
+ *
+ * @param string $sku (required) SKU, Product identifier
* @param string $name (optional) Product name
* @param string|array $category (optional) Product category, or array of product categories (up to 5 categories can be specified for a given product)
* @param float|int $price (optional) Individual product price (supports integer and decimal prices)
* @param int $quantity (optional) Product quantity. If not specified, will default to 1 in the Reports
- * @throws Exception
+ * @throws Exception
*/
public function addEcommerceItem($sku, $name = '', $category = '', $price = 0.0, $quantity = 1)
{
- if(empty($sku))
- {
- throw new Exception("You must specify a SKU for the Ecommerce item");
- }
- $this->ecommerceItems[$sku] = array( $sku, $name, $category, $price, $quantity );
+ if (empty($sku)) {
+ throw new Exception("You must specify a SKU for the Ecommerce item");
+ }
+ $this->ecommerceItems[$sku] = array($sku, $name, $category, $price, $quantity);
}
-
- /**
- * Tracks a Cart Update (add item, remove item, update item).
- *
- * On every Cart update, you must call addEcommerceItem() for each item (product) in the cart,
- * including the items that haven't been updated since the last cart update.
- * Items which were in the previous cart and are not sent in later Cart updates will be deleted from the cart (in the database).
- *
- * @param float $grandTotal Cart grandTotal (typically the sum of all items' prices)
- * @return mixed Response or true if using bulk request
- */
+
+ /**
+ * Tracks a Cart Update (add item, remove item, update item).
+ *
+ * On every Cart update, you must call addEcommerceItem() for each item (product) in the cart,
+ * including the items that haven't been updated since the last cart update.
+ * Items which were in the previous cart and are not sent in later Cart updates will be deleted from the cart (in the database).
+ *
+ * @param float $grandTotal Cart grandTotal (typically the sum of all items' prices)
+ * @return mixed Response or true if using bulk request
+ */
public function doTrackEcommerceCartUpdate($grandTotal)
{
- $url = $this->getUrlTrackEcommerceCartUpdate($grandTotal);
- return $this->sendRequest($url);
+ $url = $this->getUrlTrackEcommerceCartUpdate($grandTotal);
+ return $this->sendRequest($url);
}
-
+
/**
* Sends all stored tracking actions at once. Only has an effect if bulk tracking is enabled.
- *
+ *
* To enable bulk tracking, call enableBulkTracking().
*
- * @throws Exception
+ * @throws Exception
* @return string Response
*/
public function doBulkTrack()
{
- if (empty($this->token_auth))
- {
- throw new Exception("Token auth is required for bulk tracking.");
- }
-
- if (empty($this->storedTrackingActions))
- {
- throw new Exception("Error: you must call the function doTrackPageView or doTrackGoal from this class, before calling this method doBulkTrack()");
- }
-
- $data = array('requests' => $this->storedTrackingActions, 'token_auth' => $this->token_auth);
-
- $postData = json_encode($data);
- $response = $this->sendRequest($this->getBaseUrl(), 'POST', $postData, $force = true);
-
- $this->storedTrackingActions = array();
-
- return $response;
+ if (empty($this->token_auth)) {
+ throw new Exception("Token auth is required for bulk tracking.");
+ }
+
+ if (empty($this->storedTrackingActions)) {
+ throw new Exception("Error: you must call the function doTrackPageView or doTrackGoal from this class, before calling this method doBulkTrack()");
+ }
+
+ $data = array('requests' => $this->storedTrackingActions, 'token_auth' => $this->token_auth);
+
+ $postData = json_encode($data);
+ $response = $this->sendRequest($this->getBaseUrl(), 'POST', $postData, $force = true);
+
+ $this->storedTrackingActions = array();
+
+ return $response;
}
-
- /**
- * Tracks an Ecommerce order.
- *
- * If the Ecommerce order contains items (products), you must call first the addEcommerceItem() for each item in the order.
- * All revenues (grandTotal, subTotal, tax, shipping, discount) will be individually summed and reported in Piwik reports.
- * Only the parameters $orderId and $grandTotal are required.
- *
- * @param string|int $orderId (required) Unique Order ID.
- * This will be used to count this order only once in the event the order page is reloaded several times.
- * orderId must be unique for each transaction, even on different days, or the transaction will not be recorded by Piwik.
- * @param float $grandTotal (required) Grand Total revenue of the transaction (including tax, shipping, etc.)
- * @param float $subTotal (optional) Sub total amount, typically the sum of items prices for all items in this order (before Tax and Shipping costs are applied)
- * @param float $tax (optional) Tax amount for this order
- * @param float $shipping (optional) Shipping amount for this order
- * @param float $discount (optional) Discounted amount in this order
- * @return mixed Response or true if using bulk request
+
+ /**
+ * Tracks an Ecommerce order.
+ *
+ * If the Ecommerce order contains items (products), you must call first the addEcommerceItem() for each item in the order.
+ * All revenues (grandTotal, subTotal, tax, shipping, discount) will be individually summed and reported in Piwik reports.
+ * Only the parameters $orderId and $grandTotal are required.
+ *
+ * @param string|int $orderId (required) Unique Order ID.
+ * This will be used to count this order only once in the event the order page is reloaded several times.
+ * orderId must be unique for each transaction, even on different days, or the transaction will not be recorded by Piwik.
+ * @param float $grandTotal (required) Grand Total revenue of the transaction (including tax, shipping, etc.)
+ * @param float $subTotal (optional) Sub total amount, typically the sum of items prices for all items in this order (before Tax and Shipping costs are applied)
+ * @param float $tax (optional) Tax amount for this order
+ * @param float $shipping (optional) Shipping amount for this order
+ * @param float $discount (optional) Discounted amount in this order
+ * @return mixed Response or true if using bulk request
*/
public function doTrackEcommerceOrder($orderId, $grandTotal, $subTotal = 0.0, $tax = 0.0, $shipping = 0.0, $discount = 0.0)
{
- $url = $this->getUrlTrackEcommerceOrder($orderId, $grandTotal, $subTotal, $tax, $shipping, $discount);
- return $this->sendRequest($url);
+ $url = $this->getUrlTrackEcommerceOrder($orderId, $grandTotal, $subTotal, $tax, $shipping, $discount);
+ return $this->sendRequest($url);
}
-
+
/**
* Sets the current page view as an item (product) page view, or an Ecommerce Category page view.
- *
- * This must be called before doTrackPageView() on this product/category page.
+ *
+ * This must be called before doTrackPageView() on this product/category page.
* It will set 3 custom variables of scope "page" with the SKU, Name and Category for this page view.
* Note: Custom Variables of scope "page" slots 3, 4 and 5 will be used.
- *
+ *
* On a category page, you may set the parameter $category only and set the other parameters to false.
- *
- * Tracking Product/Category page views will allow Piwik to report on Product & Categories
+ *
+ * Tracking Product/Category page views will allow Piwik to report on Product & Categories
* conversion rates (Conversion rate = Ecommerce orders containing this product or category / Visits to the product or category)
- *
+ *
* @param string $sku Product SKU being viewed
* @param string $name Product Name being viewed
- * @param string|array $category Category being viewed. On a Product page, this is the product's category.
- * You can also specify an array of up to 5 categories for a given page view.
+ * @param string|array $category Category being viewed. On a Product page, this is the product's category.
+ * You can also specify an array of up to 5 categories for a given page view.
* @param float $price Specify the price at which the item was displayed
*/
public function setEcommerceView($sku = '', $name = '', $category = '', $price = 0.0)
{
- if(!empty($category)) {
- if(is_array($category)) {
- $category = json_encode($category);
- }
- } else {
- $category = "";
- }
- $this->pageCustomVar[5] = array('_pkc', $category);
-
- if(!empty($price)) {
- $this->pageCustomVar[2] = array('_pkp', (float)$price);
- }
-
- // On a category page, do not record "Product name not defined"
- if(empty($sku) && empty($name))
- {
- return;
- }
- if(!empty($sku)) {
- $this->pageCustomVar[3] = array('_pks', $sku);
- }
- if(empty($name)) {
- $name = "";
- }
- $this->pageCustomVar[4] = array('_pkn', $name);
+ if (!empty($category)) {
+ if (is_array($category)) {
+ $category = json_encode($category);
+ }
+ } else {
+ $category = "";
+ }
+ $this->pageCustomVar[5] = array('_pkc', $category);
+
+ if (!empty($price)) {
+ $this->pageCustomVar[2] = array('_pkp', (float)$price);
+ }
+
+ // On a category page, do not record "Product name not defined"
+ if (empty($sku) && empty($name)) {
+ return;
+ }
+ if (!empty($sku)) {
+ $this->pageCustomVar[3] = array('_pks', $sku);
+ }
+ if (empty($name)) {
+ $name = "";
+ }
+ $this->pageCustomVar[4] = array('_pkn', $name);
}
-
+
/**
* Returns URL used to track Ecommerce Cart updates
- * Calling this function will reinitializes the property ecommerceItems to empty array
- * so items will have to be added again via addEcommerceItem()
+ * Calling this function will reinitializes the property ecommerceItems to empty array
+ * so items will have to be added again via addEcommerceItem()
* @ignore
*/
public function getUrlTrackEcommerceCartUpdate($grandTotal)
{
- $url = $this->getUrlTrackEcommerce($grandTotal);
- return $url;
+ $url = $this->getUrlTrackEcommerce($grandTotal);
+ return $url;
}
-
+
/**
* Returns URL used to track Ecommerce Orders
- * Calling this function will reinitializes the property ecommerceItems to empty array
- * so items will have to be added again via addEcommerceItem()
+ * Calling this function will reinitializes the property ecommerceItems to empty array
+ * so items will have to be added again via addEcommerceItem()
* @ignore
*/
public function getUrlTrackEcommerceOrder($orderId, $grandTotal, $subTotal = 0.0, $tax = 0.0, $shipping = 0.0, $discount = 0.0)
{
- if(empty($orderId))
- {
- throw new Exception("You must specifiy an orderId for the Ecommerce order");
- }
- $url = $this->getUrlTrackEcommerce($grandTotal, $subTotal, $tax, $shipping, $discount);
- $url .= '&ec_id=' . urlencode($orderId);
- $this->ecommerceLastOrderTimestamp = $this->getTimestamp();
- return $url;
+ if (empty($orderId)) {
+ throw new Exception("You must specifiy an orderId for the Ecommerce order");
+ }
+ $url = $this->getUrlTrackEcommerce($grandTotal, $subTotal, $tax, $shipping, $discount);
+ $url .= '&ec_id=' . urlencode($orderId);
+ $this->ecommerceLastOrderTimestamp = $this->getTimestamp();
+ return $url;
}
-
+
/**
* Returns URL used to track Ecommerce orders
- * Calling this function will reinitializes the property ecommerceItems to empty array
- * so items will have to be added again via addEcommerceItem()
+ * Calling this function will reinitializes the property ecommerceItems to empty array
+ * so items will have to be added again via addEcommerceItem()
* @ignore
*/
protected function getUrlTrackEcommerce($grandTotal, $subTotal = 0.0, $tax = 0.0, $shipping = 0.0, $discount = 0.0)
{
- if(!is_numeric($grandTotal))
- {
- throw new Exception("You must specifiy a grandTotal for the Ecommerce order (or Cart update)");
- }
-
- $url = $this->getRequest( $this->idSite );
- $url .= '&idgoal=0';
- if(!empty($grandTotal))
- {
- $url .= '&revenue='.$grandTotal;
- }
- if(!empty($subTotal))
- {
- $url .= '&ec_st='.$subTotal;
- }
- if(!empty($tax))
- {
- $url .= '&ec_tx='.$tax;
- }
- if(!empty($shipping))
- {
- $url .= '&ec_sh='.$shipping;
- }
- if(!empty($discount))
- {
- $url .= '&ec_dt='.$discount;
- }
- if(!empty($this->ecommerceItems))
- {
- // Removing the SKU index in the array before JSON encoding
- $items = array();
- foreach($this->ecommerceItems as $item)
- {
- $items[] = $item;
- }
- $url .= '&ec_items='. urlencode(json_encode($items));
- }
- $this->ecommerceItems = array();
- return $url;
+ if (!is_numeric($grandTotal)) {
+ throw new Exception("You must specifiy a grandTotal for the Ecommerce order (or Cart update)");
+ }
+
+ $url = $this->getRequest($this->idSite);
+ $url .= '&idgoal=0';
+ if (!empty($grandTotal)) {
+ $url .= '&revenue=' . $grandTotal;
+ }
+ if (!empty($subTotal)) {
+ $url .= '&ec_st=' . $subTotal;
+ }
+ if (!empty($tax)) {
+ $url .= '&ec_tx=' . $tax;
+ }
+ if (!empty($shipping)) {
+ $url .= '&ec_sh=' . $shipping;
+ }
+ if (!empty($discount)) {
+ $url .= '&ec_dt=' . $discount;
+ }
+ if (!empty($this->ecommerceItems)) {
+ // Removing the SKU index in the array before JSON encoding
+ $items = array();
+ foreach ($this->ecommerceItems as $item) {
+ $items[] = $item;
+ }
+ $url .= '&ec_items=' . urlencode(json_encode($items));
+ }
+ $this->ecommerceItems = array();
+ return $url;
}
-
+
/**
- * Builds URL to track a page view.
- *
+ * Builds URL to track a page view.
+ *
* @see doTrackPageView()
* @param string $documentTitle Page view name as it will appear in Piwik reports
* @return string URL to piwik.php with all parameters set to track the pageview
*/
- public function getUrlTrackPageView( $documentTitle = '' )
+ public function getUrlTrackPageView($documentTitle = '')
{
- $url = $this->getRequest( $this->idSite );
- if(strlen($documentTitle) > 0) {
- $url .= '&action_name=' . urlencode($documentTitle);
- }
- return $url;
+ $url = $this->getRequest($this->idSite);
+ if (strlen($documentTitle) > 0) {
+ $url .= '&action_name=' . urlencode($documentTitle);
+ }
+ return $url;
}
- /**
- * Builds URL to track a site search.
- *
- * @see doTrackSiteSearch()
- * @param string $keyword
- * @param string $category
- * @param int $countResults
- * @return string
- */
- public function getUrlTrackSiteSearch($keyword, $category, $countResults)
- {
- $url = $this->getRequest( $this->idSite );
- $url .= '&search=' . urlencode($keyword);
- if(strlen($category) > 0) {
- $url .= '&search_cat=' . urlencode($category);
- }
- if(!empty($countResults) || $countResults === 0) {
- $url .= '&search_count=' . (int)$countResults;
- }
- return $url;
- }
-
- /**
- * Builds URL to track a goal with idGoal and revenue.
- *
+ /**
+ * Builds URL to track a site search.
+ *
+ * @see doTrackSiteSearch()
+ * @param string $keyword
+ * @param string $category
+ * @param int $countResults
+ * @return string
+ */
+ public function getUrlTrackSiteSearch($keyword, $category, $countResults)
+ {
+ $url = $this->getRequest($this->idSite);
+ $url .= '&search=' . urlencode($keyword);
+ if (strlen($category) > 0) {
+ $url .= '&search_cat=' . urlencode($category);
+ }
+ if (!empty($countResults) || $countResults === 0) {
+ $url .= '&search_count=' . (int)$countResults;
+ }
+ return $url;
+ }
+
+ /**
+ * Builds URL to track a goal with idGoal and revenue.
+ *
* @see doTrackGoal()
* @param int $idGoal Id Goal to record a conversion
* @param float $revenue Revenue for this conversion
@@ -688,17 +661,17 @@ class PiwikTracker
*/
public function getUrlTrackGoal($idGoal, $revenue = 0.0)
{
- $url = $this->getRequest( $this->idSite );
- $url .= '&idgoal=' . $idGoal;
- if(!empty($revenue)) {
- $url .= '&revenue=' . $revenue;
- }
- return $url;
+ $url = $this->getRequest($this->idSite);
+ $url .= '&idgoal=' . $idGoal;
+ if (!empty($revenue)) {
+ $url .= '&revenue=' . $revenue;
+ }
+ return $url;
}
-
+
/**
- * Builds URL to track a new action.
- *
+ * Builds URL to track a new action.
+ *
* @see doTrackAction()
* @param string $actionUrl URL of the download or outlink
* @param string $actionType Type of the action: 'download' or 'link'
@@ -706,140 +679,137 @@ class PiwikTracker
*/
public function getUrlTrackAction($actionUrl, $actionType)
{
- $url = $this->getRequest( $this->idSite );
- $url .= '&'.$actionType.'=' . $actionUrl;
- return $url;
+ $url = $this->getRequest($this->idSite);
+ $url .= '&' . $actionType . '=' . $actionUrl;
+ return $url;
}
/**
- * Overrides server date and time for the tracking requests.
- * By default Piwik will track requests for the "current datetime" but this function allows you
+ * Overrides server date and time for the tracking requests.
+ * By default Piwik will track requests for the "current datetime" but this function allows you
* to track visits in the past. All times are in UTC.
- *
+ *
* Allowed only for Super User, must be used along with setTokenAuth()
* @see setTokenAuth()
* @param string $dateTime Date with the format 'Y-m-d H:i:s', or a UNIX timestamp
*/
public function setForceVisitDateTime($dateTime)
{
- $this->forcedDatetime = $dateTime;
+ $this->forcedDatetime = $dateTime;
}
-
+
/**
* Overrides IP address
- *
+ *
* Allowed only for Super User, must be used along with setTokenAuth()
* @see setTokenAuth()
* @param string $ip IP string, eg. 130.54.2.1
*/
public function setIp($ip)
{
- $this->ip = $ip;
+ $this->ip = $ip;
}
-
+
/**
* Forces the requests to be recorded for the specified Visitor ID
* rather than using the heuristics based on IP and other attributes.
- *
+ *
* This is typically used with the Javascript getVisitorId() function.
- *
+ *
* Allowed only for Admin/Super User, must be used along with setTokenAuth().
* @see setTokenAuth()
* @param string $visitorId 16 hexadecimal characters visitor ID, eg. "33c31e01394bdc63"
- * @throws Exception
+ * @throws Exception
*/
public function setVisitorId($visitorId)
{
$hexChars = '01234567890abcdefABCDEF';
- if(strlen($visitorId) != self::LENGTH_VISITOR_ID
- || strspn($visitorId, $hexChars) !== strlen($visitorId))
- {
- throw new Exception("setVisitorId() expects a "
- .self::LENGTH_VISITOR_ID
- ." characters hexadecimal string (containing only the following: "
- .$hexChars
- .")");
- }
- $this->forcedVisitorId = $visitorId;
+ if (strlen($visitorId) != self::LENGTH_VISITOR_ID
+ || strspn($visitorId, $hexChars) !== strlen($visitorId)
+ ) {
+ throw new Exception("setVisitorId() expects a "
+ . self::LENGTH_VISITOR_ID
+ . " characters hexadecimal string (containing only the following: "
+ . $hexChars
+ . ")");
+ }
+ $this->forcedVisitorId = $visitorId;
}
-
+
/**
- * If the user initiating the request has the Piwik first party cookie,
+ * If the user initiating the request has the Piwik first party cookie,
* this function will try and return the ID parsed from this first party cookie (found in $_COOKIE).
- *
+ *
* If you call this function from a server, where the call is triggered by a cron or script
- * not initiated by the actual visitor being tracked, then it will return
+ * not initiated by the actual visitor being tracked, then it will return
* the random Visitor ID that was assigned to this visit object.
- *
+ *
* This can be used if you wish to record more visits, actions or goals for this visitor ID later on.
- *
+ *
* @return string 16 hex chars visitor ID string
*/
public function getVisitorId()
{
- if(!empty($this->forcedVisitorId))
- {
- return $this->forcedVisitorId;
- }
-
- $idCookieName = 'id.'.$this->idSite.'.';
- $idCookie = $this->getCookieMatchingName($idCookieName);
- if($idCookie !== false)
- {
- $visitorId = substr($idCookie, 0, strpos($idCookie, '.'));
- if(strlen($visitorId) == self::LENGTH_VISITOR_ID)
- {
- return $visitorId;
- }
- }
- return $this->visitorId;
+ if (!empty($this->forcedVisitorId)) {
+ return $this->forcedVisitorId;
+ }
+
+ $idCookieName = 'id.' . $this->idSite . '.';
+ $idCookie = $this->getCookieMatchingName($idCookieName);
+ if ($idCookie !== false) {
+ $visitorId = substr($idCookie, 0, strpos($idCookie, '.'));
+ if (strlen($visitorId) == self::LENGTH_VISITOR_ID) {
+ return $visitorId;
+ }
+ }
+ return $this->visitorId;
}
/**
* Returns the currently assigned Attribution Information stored in a first party cookie.
- *
+ *
* This function will only work if the user is initiating the current request, and his cookies
* can be read by PHP from the $_COOKIE array.
- *
+ *
* @return string JSON Encoded string containing the Referer information for Goal conversion attribution.
* Will return false if the cookie could not be found
* @see Piwik.js getAttributionInfo()
*/
public function getAttributionInfo()
{
- $attributionCookieName = 'ref.'.$this->idSite.'.';
- return $this->getCookieMatchingName($attributionCookieName);
+ $attributionCookieName = 'ref.' . $this->idSite . '.';
+ return $this->getCookieMatchingName($attributionCookieName);
+ }
+
+ /**
+ * Some Tracking API functionnality requires express authentication, using either the
+ * Super User token_auth, or a user with 'admin' access to the website.
+ *
+ * The following features require access:
+ * - force the visitor IP
+ * - force the date & time of the tracking requests rather than track for the current datetime
+ * - force Piwik to track the requests to a specific VisitorId rather than use the standard visitor matching heuristic
+ *
+ * @param string $token_auth token_auth 32 chars token_auth string
+ */
+ public function setTokenAuth($token_auth)
+ {
+ $this->token_auth = $token_auth;
}
-
- /**
- * Some Tracking API functionnality requires express authentication, using either the
- * Super User token_auth, or a user with 'admin' access to the website.
- *
- * The following features require access:
- * - force the visitor IP
- * - force the date & time of the tracking requests rather than track for the current datetime
- * - force Piwik to track the requests to a specific VisitorId rather than use the standard visitor matching heuristic
- *
- * @param string $token_auth token_auth 32 chars token_auth string
- */
- public function setTokenAuth($token_auth)
- {
- $this->token_auth = $token_auth;
- }
/**
* Sets local visitor time
- *
+ *
* @param string $time HH:MM:SS format
*/
public function setLocalTime($time)
{
- list($hour, $minute, $second) = explode(':', $time);
- $this->localHour = (int)$hour;
- $this->localMinute = (int)$minute;
- $this->localSecond = (int)$second;
+ list($hour, $minute, $second) = explode(':', $time);
+ $this->localHour = (int)$hour;
+ $this->localMinute = (int)$minute;
+ $this->localSecond = (int)$second;
}
-
+
/**
* Sets user resolution width and height.
*
@@ -848,32 +818,32 @@ class PiwikTracker
*/
public function setResolution($width, $height)
{
- $this->width = $width;
- $this->height = $height;
+ $this->width = $width;
+ $this->height = $height;
}
-
+
/**
- * Sets if the browser supports cookies
+ * Sets if the browser supports cookies
* This is reported in "List of plugins" report in Piwik.
*
* @param bool $bool
*/
- public function setBrowserHasCookies( $bool )
+ public function setBrowserHasCookies($bool)
{
- $this->hasCookies = $bool ;
+ $this->hasCookies = $bool;
}
-
+
/**
- * Will append a custom string at the end of the Tracking request.
+ * Will append a custom string at the end of the Tracking request.
* @param string $string
*/
- public function setDebugStringAppend( $string )
+ public function setDebugStringAppend($string)
{
- $this->DEBUG_APPEND_URL = $string;
+ $this->DEBUG_APPEND_URL = $string;
}
-
+
/**
- * Sets visitor browser supported plugins
+ * Sets visitor browser supported plugins
*
* @param bool $flash
* @param bool $java
@@ -887,374 +857,352 @@ class PiwikTracker
*/
public function setPlugins($flash = false, $java = false, $director = false, $quickTime = false, $realPlayer = false, $pdf = false, $windowsMedia = false, $gears = false, $silverlight = false)
{
- $this->plugins =
- '&fla='.(int)$flash.
- '&java='.(int)$java.
- '&dir='.(int)$director.
- '&qt='.(int)$quickTime.
- '&realp='.(int)$realPlayer.
- '&pdf='.(int)$pdf.
- '&wma='.(int)$windowsMedia.
- '&gears='.(int)$gears.
- '&ag='.(int)$silverlight
- ;
+ $this->plugins =
+ '&fla=' . (int)$flash .
+ '&java=' . (int)$java .
+ '&dir=' . (int)$director .
+ '&qt=' . (int)$quickTime .
+ '&realp=' . (int)$realPlayer .
+ '&pdf=' . (int)$pdf .
+ '&wma=' . (int)$windowsMedia .
+ '&gears=' . (int)$gears .
+ '&ag=' . (int)$silverlight;
}
- /**
- * By default, PiwikTracker will read third party cookies
- * from the response and sets them in the next request.
- * This can be disabled by calling this function.
- */
+ /**
+ * By default, PiwikTracker will read third party cookies
+ * from the response and sets them in the next request.
+ * This can be disabled by calling this function.
+ */
public function disableCookieSupport()
{
- $this->cookieSupport = false;
+ $this->cookieSupport = false;
}
-
+
/**
* Returns the maximum number of seconds the tracker will spend waiting for a response
* from Piwik. Defaults to 600 seconds.
*/
public function getRequestTimeout()
{
- return $this->requestTimeout;
+ return $this->requestTimeout;
}
-
- /**
- * Sets the maximum number of seconds that the tracker will spend waiting for a response
- * from Piwik.
- *
- * @param int $timeout
- * @throws Exception
- */
- public function setRequestTimeout( $timeout )
+
+ /**
+ * Sets the maximum number of seconds that the tracker will spend waiting for a response
+ * from Piwik.
+ *
+ * @param int $timeout
+ * @throws Exception
+ */
+ public function setRequestTimeout($timeout)
{
- if (!is_int($timeout) || $timeout < 0)
- {
- throw new Exception("Invalid value supplied for request timeout: $timeout");
- }
-
- $this->requestTimeout = $timeout;
+ if (!is_int($timeout) || $timeout < 0) {
+ throw new Exception("Invalid value supplied for request timeout: $timeout");
+ }
+
+ $this->requestTimeout = $timeout;
}
-
+
/**
* @ignore
*/
- protected function sendRequest( $url, $method = 'GET', $data = null, $force = false )
+ protected function sendRequest($url, $method = 'GET', $data = null, $force = false)
{
- // if doing a bulk request, store the url
- if ($this->doBulkRequests && !$force)
- {
- $this->storedTrackingActions[]
- = $url
- . (!empty($this->userAgent) ? ('&ua='.urlencode($this->userAgent)) : '')
- . (!empty($this->acceptLanguage) ? ('&lang='.urlencode($this->acceptLanguage)) : '')
- ;
- return true;
- }
-
- $response = '';
-
- if(!$this->cookieSupport)
- {
- $this->requestCookie = '';
- }
- if(function_exists('curl_init'))
- {
- $options = array(
- CURLOPT_URL => $url,
- CURLOPT_USERAGENT => $this->userAgent,
- CURLOPT_HEADER => true,
- CURLOPT_TIMEOUT => $this->requestTimeout,
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_HTTPHEADER => array(
- 'Accept-Language: ' . $this->acceptLanguage,
- 'Cookie: '. $this->requestCookie,
- ));
-
- switch ($method)
- {
- case 'POST':
- $options[CURLOPT_POST] = TRUE;
- break;
- default:
- break;
- }
-
- // only supports JSON data
- if (!empty($data))
- {
- $options[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json';
- $options[CURLOPT_HTTPHEADER][] = 'Expect:';
- $options[CURLOPT_POSTFIELDS] = $data;
- }
-
- $ch = curl_init();
- curl_setopt_array($ch, $options);
- ob_start();
- $response = @curl_exec($ch);
- ob_end_clean();
- $header = $content = '';
- if(!empty($response))
- {
- list($header,$content) = explode("\r\n\r\n", $response, $limitCount = 2);
- }
- }
- else if(function_exists('stream_context_create'))
- {
- $stream_options = array(
- 'http' => array(
- 'method' => $method,
- 'user_agent' => $this->userAgent,
- 'header' => "Accept-Language: " . $this->acceptLanguage . "\r\n" .
- "Cookie: ".$this->requestCookie. "\r\n" ,
- 'timeout' => $this->requestTimeout, // PHP 5.2.1
- )
- );
-
- // only supports JSON data
- if (!empty($data))
- {
- $stream_options['http']['header'] .= "Content-Type: application/json \r\n";
- $stream_options['http']['content'] = $data;
- }
-
- $ctx = stream_context_create($stream_options);
- $response = file_get_contents($url, 0, $ctx);
- $header = implode("\r\n", $http_response_header);
- $content = $response;
- }
- // The cookie in the response will be set in the next request
- preg_match_all('/^Set-Cookie: (.*?);/m', $header, $cookie);
- if(!empty($cookie[1]))
- {
- // in case several cookies returned, we keep only the latest one (ie. XDEBUG puts its cookie first in the list)
- if(is_array($cookie[1]))
- {
- $cookie = end($cookie[1]);
- }
- else
- {
- $cookie = $cookie[1];
- }
- // XDEBUG is a PHP Debugger
- if(strpos($cookie, 'XDEBUG') === false)
- {
- $this->requestCookie = $cookie;
- }
- }
-
- return $content;
+ // if doing a bulk request, store the url
+ if ($this->doBulkRequests && !$force) {
+ $this->storedTrackingActions[]
+ = $url
+ . (!empty($this->userAgent) ? ('&ua=' . urlencode($this->userAgent)) : '')
+ . (!empty($this->acceptLanguage) ? ('&lang=' . urlencode($this->acceptLanguage)) : '');
+ return true;
+ }
+
+ $response = '';
+
+ if (!$this->cookieSupport) {
+ $this->requestCookie = '';
+ }
+ if (function_exists('curl_init')) {
+ $options = array(
+ CURLOPT_URL => $url,
+ CURLOPT_USERAGENT => $this->userAgent,
+ CURLOPT_HEADER => true,
+ CURLOPT_TIMEOUT => $this->requestTimeout,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HTTPHEADER => array(
+ 'Accept-Language: ' . $this->acceptLanguage,
+ 'Cookie: ' . $this->requestCookie,
+ ));
+
+ switch ($method) {
+ case 'POST':
+ $options[CURLOPT_POST] = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ // only supports JSON data
+ if (!empty($data)) {
+ $options[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json';
+ $options[CURLOPT_HTTPHEADER][] = 'Expect:';
+ $options[CURLOPT_POSTFIELDS] = $data;
+ }
+
+ $ch = curl_init();
+ curl_setopt_array($ch, $options);
+ ob_start();
+ $response = @curl_exec($ch);
+ ob_end_clean();
+ $header = $content = '';
+ if (!empty($response)) {
+ list($header, $content) = explode("\r\n\r\n", $response, $limitCount = 2);
+ }
+ } else if (function_exists('stream_context_create')) {
+ $stream_options = array(
+ 'http' => array(
+ 'method' => $method,
+ 'user_agent' => $this->userAgent,
+ 'header' => "Accept-Language: " . $this->acceptLanguage . "\r\n" .
+ "Cookie: " . $this->requestCookie . "\r\n",
+ 'timeout' => $this->requestTimeout, // PHP 5.2.1
+ )
+ );
+
+ // only supports JSON data
+ if (!empty($data)) {
+ $stream_options['http']['header'] .= "Content-Type: application/json \r\n";
+ $stream_options['http']['content'] = $data;
+ }
+
+ $ctx = stream_context_create($stream_options);
+ $response = file_get_contents($url, 0, $ctx);
+ $header = implode("\r\n", $http_response_header);
+ $content = $response;
+ }
+ // The cookie in the response will be set in the next request
+ preg_match_all('/^Set-Cookie: (.*?);/m', $header, $cookie);
+ if (!empty($cookie[1])) {
+ // in case several cookies returned, we keep only the latest one (ie. XDEBUG puts its cookie first in the list)
+ if (is_array($cookie[1])) {
+ $cookie = end($cookie[1]);
+ } else {
+ $cookie = $cookie[1];
+ }
+ // XDEBUG is a PHP Debugger
+ if (strpos($cookie, 'XDEBUG') === false) {
+ $this->requestCookie = $cookie;
+ }
+ }
+
+ return $content;
}
-
+
/**
* Returns current timestamp, or forced timestamp/datetime if it was set
* @return string|int
*/
protected function getTimestamp()
{
- return !empty($this->forcedDatetime)
- ? strtotime($this->forcedDatetime)
- : time();
+ return !empty($this->forcedDatetime)
+ ? strtotime($this->forcedDatetime)
+ : time();
}
-
+
/**
* Returns the base URL for the piwik server.
*/
protected function getBaseUrl()
{
- if(empty(self::$URL))
- {
- throw new Exception('You must first set the Piwik Tracker URL by calling PiwikTracker::$URL = \'http://your-website.org/piwik/\';');
- }
- if(strpos(self::$URL, '/piwik.php') === false
- && strpos(self::$URL, '/proxy-piwik.php') === false)
- {
- self::$URL .= '/piwik.php';
- }
- return self::$URL;
+ if (empty(self::$URL)) {
+ throw new Exception('You must first set the Piwik Tracker URL by calling PiwikTracker::$URL = \'http://your-website.org/piwik/\';');
+ }
+ if (strpos(self::$URL, '/piwik.php') === false
+ && strpos(self::$URL, '/proxy-piwik.php') === false
+ ) {
+ self::$URL .= '/piwik.php';
+ }
+ return self::$URL;
}
-
+
/**
* @ignore
*/
- protected function getRequest( $idSite )
+ protected function getRequest($idSite)
{
- $url = $this->getBaseUrl() .
- '?idsite=' . $idSite .
- '&rec=1' .
- '&apiv=' . self::VERSION .
- '&r=' . substr(strval(mt_rand()), 2, 6) .
-
- // XDEBUG_SESSIONS_START and KEY are related to the PHP Debugger, this can be ignored in other languages
- (!empty($_GET['XDEBUG_SESSION_START']) ? '&XDEBUG_SESSION_START=' . @urlencode($_GET['XDEBUG_SESSION_START']) : '') .
- (!empty($_GET['KEY']) ? '&KEY=' . @urlencode($_GET['KEY']) : '') .
-
- // Only allowed for Super User, token_auth required,
- (!empty($this->ip) ? '&cip=' . $this->ip : '') .
- (!empty($this->forcedVisitorId) ? '&cid=' . $this->forcedVisitorId : '&_id=' . $this->visitorId) .
- (!empty($this->forcedDatetime) ? '&cdt=' . urlencode($this->forcedDatetime) : '') .
- ((!empty($this->token_auth) && !$this->doBulkRequests) ? '&token_auth=' . urlencode($this->token_auth) : '') .
-
- // These parameters are set by the JS, but optional when using API
- (!empty($this->plugins) ? $this->plugins : '') .
- (($this->localHour !== false && $this->localMinute !== false && $this->localSecond !== false) ? '&h=' . $this->localHour . '&m=' . $this->localMinute . '&s=' . $this->localSecond : '' ).
- (!empty($this->width) && !empty($this->height) ? '&res=' . $this->width . 'x' . $this->height : '') .
- (!empty($this->hasCookies) ? '&cookie=' . $this->hasCookies : '') .
- (!empty($this->ecommerceLastOrderTimestamp) ? '&_ects=' . urlencode($this->ecommerceLastOrderTimestamp) : '') .
-
- // Various important attributes
- (!empty($this->customData) ? '&data=' . $this->customData : '') .
- (!empty($this->visitorCustomVar) ? '&_cvar=' . urlencode(json_encode($this->visitorCustomVar)) : '') .
- (!empty($this->pageCustomVar) ? '&cvar=' . urlencode(json_encode($this->pageCustomVar)) : '') .
- (!empty($this->generationTime) ? '&generation_time_ms=' . ((int)$this->generationTime) : '') .
-
- // URL parameters
- '&url=' . urlencode($this->pageUrl) .
- '&urlref=' . urlencode($this->urlReferrer) .
- ((!empty($this->pageCharset) && $this->pageCharset != self::DEFAULT_CHARSET_PARAMETER_VALUES) ? '&cs=' . $this->pageCharset : '') .
-
- // Attribution information, so that Goal conversions are attributed to the right referrer or campaign
- // Campaign name
- (!empty($this->attributionInfo[0]) ? '&_rcn=' . urlencode($this->attributionInfo[0]) : '') .
- // Campaign keyword
- (!empty($this->attributionInfo[1]) ? '&_rck=' . urlencode($this->attributionInfo[1]) : '') .
- // Timestamp at which the referrer was set
- (!empty($this->attributionInfo[2]) ? '&_refts=' . $this->attributionInfo[2] : '') .
- // Referrer URL
- (!empty($this->attributionInfo[3]) ? '&_ref=' . urlencode($this->attributionInfo[3]) : '') .
-
- // custom location info
- (!empty($this->country) ? '&country='.urlencode($this->country) : '') .
- (!empty($this->region) ? '&region='.urlencode($this->region) : '') .
- (!empty($this->city) ? '&city='.urlencode($this->city) : '') .
- (!empty($this->lat) ? '&lat='.urlencode($this->lat) : '') .
- (!empty($this->long) ? '&long='.urlencode($this->long) : '') .
-
- // DEBUG
- $this->DEBUG_APPEND_URL
- ;
- // Reset page level custom variables after this page view
- $this->pageCustomVar = false;
-
- return $url;
+ $url = $this->getBaseUrl() .
+ '?idsite=' . $idSite .
+ '&rec=1' .
+ '&apiv=' . self::VERSION .
+ '&r=' . substr(strval(mt_rand()), 2, 6) .
+
+ // XDEBUG_SESSIONS_START and KEY are related to the PHP Debugger, this can be ignored in other languages
+ (!empty($_GET['XDEBUG_SESSION_START']) ? '&XDEBUG_SESSION_START=' . @urlencode($_GET['XDEBUG_SESSION_START']) : '') .
+ (!empty($_GET['KEY']) ? '&KEY=' . @urlencode($_GET['KEY']) : '') .
+
+ // Only allowed for Super User, token_auth required,
+ (!empty($this->ip) ? '&cip=' . $this->ip : '') .
+ (!empty($this->forcedVisitorId) ? '&cid=' . $this->forcedVisitorId : '&_id=' . $this->visitorId) .
+ (!empty($this->forcedDatetime) ? '&cdt=' . urlencode($this->forcedDatetime) : '') .
+ ((!empty($this->token_auth) && !$this->doBulkRequests) ? '&token_auth=' . urlencode($this->token_auth) : '') .
+
+ // These parameters are set by the JS, but optional when using API
+ (!empty($this->plugins) ? $this->plugins : '') .
+ (($this->localHour !== false && $this->localMinute !== false && $this->localSecond !== false) ? '&h=' . $this->localHour . '&m=' . $this->localMinute . '&s=' . $this->localSecond : '') .
+ (!empty($this->width) && !empty($this->height) ? '&res=' . $this->width . 'x' . $this->height : '') .
+ (!empty($this->hasCookies) ? '&cookie=' . $this->hasCookies : '') .
+ (!empty($this->ecommerceLastOrderTimestamp) ? '&_ects=' . urlencode($this->ecommerceLastOrderTimestamp) : '') .
+
+ // Various important attributes
+ (!empty($this->customData) ? '&data=' . $this->customData : '') .
+ (!empty($this->visitorCustomVar) ? '&_cvar=' . urlencode(json_encode($this->visitorCustomVar)) : '') .
+ (!empty($this->pageCustomVar) ? '&cvar=' . urlencode(json_encode($this->pageCustomVar)) : '') .
+ (!empty($this->generationTime) ? '&generation_time_ms=' . ((int)$this->generationTime) : '') .
+
+ // URL parameters
+ '&url=' . urlencode($this->pageUrl) .
+ '&urlref=' . urlencode($this->urlReferrer) .
+ ((!empty($this->pageCharset) && $this->pageCharset != self::DEFAULT_CHARSET_PARAMETER_VALUES) ? '&cs=' . $this->pageCharset : '') .
+
+ // Attribution information, so that Goal conversions are attributed to the right referrer or campaign
+ // Campaign name
+ (!empty($this->attributionInfo[0]) ? '&_rcn=' . urlencode($this->attributionInfo[0]) : '') .
+ // Campaign keyword
+ (!empty($this->attributionInfo[1]) ? '&_rck=' . urlencode($this->attributionInfo[1]) : '') .
+ // Timestamp at which the referrer was set
+ (!empty($this->attributionInfo[2]) ? '&_refts=' . $this->attributionInfo[2] : '') .
+ // Referrer URL
+ (!empty($this->attributionInfo[3]) ? '&_ref=' . urlencode($this->attributionInfo[3]) : '') .
+
+ // custom location info
+ (!empty($this->country) ? '&country=' . urlencode($this->country) : '') .
+ (!empty($this->region) ? '&region=' . urlencode($this->region) : '') .
+ (!empty($this->city) ? '&city=' . urlencode($this->city) : '') .
+ (!empty($this->lat) ? '&lat=' . urlencode($this->lat) : '') .
+ (!empty($this->long) ? '&long=' . urlencode($this->long) : '') .
+
+ // DEBUG
+ $this->DEBUG_APPEND_URL;
+ // Reset page level custom variables after this page view
+ $this->pageCustomVar = false;
+
+ return $url;
}
-
-
+
+
/**
* Returns a first party cookie which name contains $name
- *
+ *
* @param string $name
* @return string String value of cookie, or false if not found
* @ignore
*/
protected function getCookieMatchingName($name)
{
- // Piwik cookie names use dots separators in piwik.js,
- // but PHP Replaces . with _ http://www.php.net/manual/en/language.variables.predefined.php#72571
- $name = str_replace('.', '_', $name);
- foreach($_COOKIE as $cookieName => $cookieValue)
- {
- if(strpos($cookieName, $name) !== false)
- {
- return $cookieValue;
- }
- }
- return false;
+ // Piwik cookie names use dots separators in piwik.js,
+ // but PHP Replaces . with _ http://www.php.net/manual/en/language.variables.predefined.php#72571
+ $name = str_replace('.', '_', $name);
+ foreach ($_COOKIE as $cookieName => $cookieValue) {
+ if (strpos($cookieName, $name) !== false) {
+ return $cookieValue;
+ }
+ }
+ return false;
}
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return "/dir1/dir2/index.php"
- *
- * @return string
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return "/dir1/dir2/index.php"
+ *
+ * @return string
* @ignore
- */
- static protected function getCurrentScriptName()
- {
- $url = '';
- if( !empty($_SERVER['PATH_INFO']) ) {
- $url = $_SERVER['PATH_INFO'];
- }
- else if( !empty($_SERVER['REQUEST_URI']) ) {
- if( ($pos = strpos($_SERVER['REQUEST_URI'], '?')) !== false ) {
- $url = substr($_SERVER['REQUEST_URI'], 0, $pos);
- } else {
- $url = $_SERVER['REQUEST_URI'];
- }
- }
- if(empty($url)) {
- $url = $_SERVER['SCRIPT_NAME'];
- }
-
- if($url[0] !== '/') {
- $url = '/' . $url;
- }
- return $url;
- }
-
- /**
- * If the current URL is 'http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return 'http'
- *
- * @return string 'https' or 'http'
+ */
+ static protected function getCurrentScriptName()
+ {
+ $url = '';
+ if (!empty($_SERVER['PATH_INFO'])) {
+ $url = $_SERVER['PATH_INFO'];
+ } else if (!empty($_SERVER['REQUEST_URI'])) {
+ if (($pos = strpos($_SERVER['REQUEST_URI'], '?')) !== false) {
+ $url = substr($_SERVER['REQUEST_URI'], 0, $pos);
+ } else {
+ $url = $_SERVER['REQUEST_URI'];
+ }
+ }
+ if (empty($url)) {
+ $url = $_SERVER['SCRIPT_NAME'];
+ }
+
+ if ($url[0] !== '/') {
+ $url = '/' . $url;
+ }
+ return $url;
+ }
+
+ /**
+ * If the current URL is 'http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return 'http'
+ *
+ * @return string 'https' or 'http'
* @ignore
- */
- static protected function getCurrentScheme()
- {
- if(isset($_SERVER['HTTPS'])
- && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true))
- {
- return 'https';
- }
- return 'http';
- }
-
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return "http://example.org"
- *
- * @return string
+ */
+ static protected function getCurrentScheme()
+ {
+ if (isset($_SERVER['HTTPS'])
+ && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true)
+ ) {
+ return 'https';
+ }
+ return 'http';
+ }
+
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return "http://example.org"
+ *
+ * @return string
* @ignore
- */
- static protected function getCurrentHost()
- {
- if(isset($_SERVER['HTTP_HOST'])) {
- return $_SERVER['HTTP_HOST'];
- }
- return 'unknown';
- }
-
- /**
- * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
- * will return "?param1=value1&param2=value2"
- *
- * @return string
+ */
+ static protected function getCurrentHost()
+ {
+ if (isset($_SERVER['HTTP_HOST'])) {
+ return $_SERVER['HTTP_HOST'];
+ }
+ return 'unknown';
+ }
+
+ /**
+ * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"
+ * will return "?param1=value1&param2=value2"
+ *
+ * @return string
* @ignore
- */
- static protected function getCurrentQueryString()
- {
- $url = '';
- if(isset($_SERVER['QUERY_STRING'])
- && !empty($_SERVER['QUERY_STRING']))
- {
- $url .= '?'.$_SERVER['QUERY_STRING'];
- }
- return $url;
- }
-
- /**
- * Returns the current full URL (scheme, host, path and query string.
- *
- * @return string
+ */
+ static protected function getCurrentQueryString()
+ {
+ $url = '';
+ if (isset($_SERVER['QUERY_STRING'])
+ && !empty($_SERVER['QUERY_STRING'])
+ ) {
+ $url .= '?' . $_SERVER['QUERY_STRING'];
+ }
+ return $url;
+ }
+
+ /**
+ * Returns the current full URL (scheme, host, path and query string.
+ *
+ * @return string
* @ignore
- */
+ */
static protected function getCurrentUrl()
{
- return self::getCurrentScheme() . '://'
- . self::getCurrentHost()
- . self::getCurrentScriptName()
- . self::getCurrentQueryString();
- }
+ return self::getCurrentScheme() . '://'
+ . self::getCurrentHost()
+ . self::getCurrentScriptName()
+ . self::getCurrentQueryString();
+ }
}
/**
@@ -1264,10 +1212,10 @@ class PiwikTracker
* @param string $documentTitle
* @return string
*/
-function Piwik_getUrlTrackPageView( $idSite, $documentTitle = '' )
+function Piwik_getUrlTrackPageView($idSite, $documentTitle = '')
{
- $tracker = new PiwikTracker($idSite);
- return $tracker->getUrlTrackPageView($documentTitle);
+ $tracker = new PiwikTracker($idSite);
+ return $tracker->getUrlTrackPageView($documentTitle);
}
/**
@@ -1280,7 +1228,7 @@ function Piwik_getUrlTrackPageView( $idSite, $documentTitle = '' )
*/
function Piwik_getUrlTrackGoal($idSite, $idGoal, $revenue = 0.0)
{
- $tracker = new PiwikTracker($idSite);
- return $tracker->getUrlTrackGoal($idGoal, $revenue);
+ $tracker = new PiwikTracker($idSite);
+ return $tracker->getUrlTrackGoal($idGoal, $revenue);
}
diff --git a/libs/UserAgentParser/UserAgentParser.php b/libs/UserAgentParser/UserAgentParser.php
index 03a1f695cd..bac59cea08 100644
--- a/libs/UserAgentParser/UserAgentParser.php
+++ b/libs/UserAgentParser/UserAgentParser.php
@@ -36,581 +36,571 @@
/**
* Example usage
- *
+ *
* Browser info:
* var_dump(UserAgentParser::getBrowser($_SERVER['HTTP_USER_AGENT']));
- *
+ *
* Outputs:
* array
- * 'id' => 'FF'
+ * 'id' => 'FF'
* 'name' => 'Firefox'
* 'short_name' => 'Firefox'
* 'version' => '3.0'
* 'major_number' => '3'
* 'minor_number' => '0'
- *
+ *
* Operating System info:
* var_dump(UserAgentParser::getOperatingSystem($_SERVER['HTTP_USER_AGENT']));
*
* Outputs:
* array
* 'id' => 'WXP'
- * 'name' => 'Windows XP'
- * 'short_name' => 'Win XP'
- *
+ * 'name' => 'Windows XP'
+ * 'short_name' => 'Win XP'
+ *
*/
-class UserAgentParser
+class UserAgentParser
{
- // browser regex => browser ID
- // if there are aliases, the common name should be last
- static protected $browsers = array(
- 'abrowse' => 'AB',
- 'amaya' => 'AM',
- 'amigavoyager' => 'AV',
- 'amiga-aweb' => 'AW',
- 'arora' => 'AR',
- 'beonex' => 'BE',
-
- // BlackBerry smartphones and tablets
- 'blackberry' => 'BB', // BlackBerry 6 and PlayBook adopted webkit
- 'bb10' => 'B2', // BlackBerry 10
- 'playbook' => 'BP',
-
- 'browsex' => 'BX',
-
- // Camino (and earlier incarnation)
- 'chimera' => 'CA',
- 'camino' => 'CA',
-
- 'cheshire' => 'CS',
-
- // Chrome, Chromium, and ChromePlus
- 'crmo' => 'CH',
- 'chrome' => 'CH',
-
- // Chrome Frame
- 'chromeframe' => 'CF',
-
- 'cometbird' => 'CO',
- 'dillo' => 'DI',
- 'elinks' => 'EL',
- 'epiphany' => 'EP',
- 'fennec' => 'FE',
-
- // Dolfin (or Dolphin)
- 'dolfin' => 'DF',
-
- // Firefox (in its many incarnations and rebranded versions)
- 'phoenix' => 'PX',
- 'mozilla firebird' => 'FB',
- 'firebird' => 'FB',
- 'bonecho' => 'FF',
- 'minefield' => 'FF',
- 'namoroka' => 'FF',
- 'shiretoko' => 'FF',
- 'granparadiso' => 'FF',
- 'iceweasel' => 'FF',
- 'icecat' => 'FF',
- 'firefox' => 'FF',
-
- 'thunderbird' => 'TB',
-
- 'flock' => 'FL',
- 'fluid' => 'FD',
- 'galeon' => 'GA',
- 'google earth' => 'GE',
- 'hana' => 'HA',
- 'hotjava' => 'HJ',
- 'ibrowse' => 'IB',
- 'icab' => 'IC',
-
- // IE (including shells: Acoo, AOL, Avant, Crazy Browser, Green Browser, KKMAN, Maxathon)
- 'msie' => 'IE',
- 'microsoft internet explorer' => 'IE',
- 'internet explorer' => 'IE',
-
- 'iron' => 'IR',
- 'kapiko' => 'KP',
- 'kazehakase' => 'KZ',
- 'k-meleon' => 'KM',
- 'konqueror' => 'KO',
- 'links' => 'LI',
- 'lynx' => 'LX',
- 'midori' => 'MI',
-
- // SeaMonkey (formerly Mozilla Suite) (and rebranded versions)
- 'mozilla' => 'MO',
- 'gnuzilla' => 'SM',
- 'iceape' => 'SM',
- 'seamonkey' => 'SM',
-
- // NCSA Mosaic (and incarnations)
- 'mosaic' => 'MC',
- 'ncsa mosaic' => 'MC',
-
- // Netscape Navigator
- 'navigator' => 'NS',
- 'netscape6' => 'NS',
- 'netscape' => 'NS',
-
- 'nx' => 'NF',
- 'netfront' => 'NF',
-
- 'omniweb' => 'OW',
-
- // Opera
- 'nitro) opera' => 'OP',
- 'opera' => 'OP',
-
- 'rekonq' => 'RK',
-
- // Safari
- 'safari' => 'SF',
- 'applewebkit' => 'SF',
-
- 'titanium' => 'TI',
-
- 'webos' => 'WO',
- 'webpro' => 'WP',
- );
-
- // browser family (by layout engine)
- static protected $browserType = array(
- 'ie' => array('IE'),
- 'gecko' => array('NS', 'PX', 'FF', 'FB', 'CA', 'GA', 'KM', 'MO', 'SM', 'CO', 'FE', 'KP', 'KZ', 'TB'),
- 'khtml' => array('KO'),
- 'webkit' => array('SF', 'CH', 'OW', 'AR', 'EP', 'FL', 'WO', 'AB', 'IR', 'CS', 'FD', 'HA', 'MI', 'GE', 'DF', 'BB', 'BP', 'TI', 'CF', 'RK', 'B2', 'NF'),
- 'opera' => array('OP'),
- );
-
- // WebKit version numbers to Apple Safari version numbers (if Version/X.Y.Z not present)
- static protected $safariVersions = array(
- '536.25' => array('6', '0'),
- '534.48' => array('5', '1'),
- '533.16' => array('5', '0'),
- '533.4' => array('4', '1'),
- '526.11.2' => array('4', '0'),
- '525.26' => array('3', '2'),
- '525.13' => array('3', '1'),
- '522.11' => array('3', '0'),
- '412' => array('2', '0'),
- '312' => array('1', '3'),
- '125' => array('1', '2'),
- '100' => array('1', '1'),
- '85' => array('1', '0'),
- '73' => array('0', '9'),
- '48' => array('0', '8'),
- );
-
- // OmniWeb build numbers to OmniWeb version numbers (if Version/X.Y.Z not present)
- static protected $omniWebVersions = array(
- '622.15' => array('5', '11'),
- '622.10' => array('5', '10'),
- '622.8' => array('5', '9'),
- '622.3' => array('5', '8'),
- '621' => array('5', '7'),
- '613' => array('5', '6'),
- '607' => array('5', '5'),
- '563.34' => array('5', '1'),
- '558.36' => array('5', '0'),
- '496' => array('4', '5'),
- );
-
- // OS regex => OS ID
- static protected $operatingSystems = array(
- 'Android' => 'AND',
- 'Maemo' => 'MAE',
- 'CrOS ' => 'LIN',
- 'Linux' => 'LIN',
-
- 'Xbox' => 'XBX',
-
- // workaround for vendors who changed the WinPhone 7 user agent
- 'WP7' => 'WPH',
-
- 'CYGWIN_NT-6.2' => 'WI8',
- 'Windows NT 6.2' => 'WI8',
- 'Windows 8' => 'WI8',
- 'CYGWIN_NT-6.1' => 'WI7',
- 'Windows NT 6.1' => 'WI7',
- 'Windows 7' => 'WI7',
- 'CYGWIN_NT-6.0' => 'WVI',
- 'Windows NT 6.0' => 'WVI',
- 'Windows Vista' => 'WVI',
- 'CYGWIN_NT-5.2' => 'WS3',
- 'Windows NT 5.2' => 'WS3',
- 'Windows Server 2003 / XP x64' => 'WS3',
- 'CYGWIN_NT-5.1' => 'WXP',
- 'Windows NT 5.1' => 'WXP',
- 'Windows XP' => 'WXP',
- 'CYGWIN_NT-5.0' => 'W2K',
- 'Windows NT 5.0' => 'W2K',
- 'Windows 2000' => 'W2K',
- 'CYGWIN_NT-4.0' => 'WNT',
- 'Windows NT 4.0' => 'WNT',
- 'WinNT' => 'WNT',
- 'Windows NT' => 'WNT',
- 'CYGWIN_ME-4.90' => 'WME',
- 'Win 9x 4.90' => 'WME',
- 'Windows ME' => 'WME',
- 'CYGWIN_98-4.10' => 'W98',
- 'Win98' => 'W98',
- 'Windows 98' => 'W98',
- 'CYGWIN_95-4.0' => 'W95',
- 'Win32' => 'W95',
- 'Win95' => 'W95',
- 'Windows 95' => 'W95',
-
- // Windows Phone OS 7 and above
- 'Windows Phone OS' => 'WPH',
-
- // Windows Mobile 6.x and some later versions of Windows Mobile 5
- 'IEMobile' => 'WMO', // fallback
- 'Windows Mobile' => 'WMO',
-
- // Windows CE, Pocket PC, and Windows Mobile 5 are indistinguishable without vendor/device specific detection
- 'Windows CE' => 'WCE',
-
- 'iPod' => 'IPD',
- 'iPad' => 'IPA',
- 'iPhone' => 'IPH',
+ // browser regex => browser ID
+ // if there are aliases, the common name should be last
+ static protected $browsers = array(
+ 'abrowse' => 'AB',
+ 'amaya' => 'AM',
+ 'amigavoyager' => 'AV',
+ 'amiga-aweb' => 'AW',
+ 'arora' => 'AR',
+ 'beonex' => 'BE',
+
+ // BlackBerry smartphones and tablets
+ 'blackberry' => 'BB', // BlackBerry 6 and PlayBook adopted webkit
+ 'bb10' => 'B2', // BlackBerry 10
+ 'playbook' => 'BP',
+
+ 'browsex' => 'BX',
+
+ // Camino (and earlier incarnation)
+ 'chimera' => 'CA',
+ 'camino' => 'CA',
+
+ 'cheshire' => 'CS',
+
+ // Chrome, Chromium, and ChromePlus
+ 'crmo' => 'CH',
+ 'chrome' => 'CH',
+
+ // Chrome Frame
+ 'chromeframe' => 'CF',
+
+ 'cometbird' => 'CO',
+ 'dillo' => 'DI',
+ 'elinks' => 'EL',
+ 'epiphany' => 'EP',
+ 'fennec' => 'FE',
+
+ // Dolfin (or Dolphin)
+ 'dolfin' => 'DF',
+
+ // Firefox (in its many incarnations and rebranded versions)
+ 'phoenix' => 'PX',
+ 'mozilla firebird' => 'FB',
+ 'firebird' => 'FB',
+ 'bonecho' => 'FF',
+ 'minefield' => 'FF',
+ 'namoroka' => 'FF',
+ 'shiretoko' => 'FF',
+ 'granparadiso' => 'FF',
+ 'iceweasel' => 'FF',
+ 'icecat' => 'FF',
+ 'firefox' => 'FF',
+
+ 'thunderbird' => 'TB',
+
+ 'flock' => 'FL',
+ 'fluid' => 'FD',
+ 'galeon' => 'GA',
+ 'google earth' => 'GE',
+ 'hana' => 'HA',
+ 'hotjava' => 'HJ',
+ 'ibrowse' => 'IB',
+ 'icab' => 'IC',
+
+ // IE (including shells: Acoo, AOL, Avant, Crazy Browser, Green Browser, KKMAN, Maxathon)
+ 'msie' => 'IE',
+ 'microsoft internet explorer' => 'IE',
+ 'internet explorer' => 'IE',
+
+ 'iron' => 'IR',
+ 'kapiko' => 'KP',
+ 'kazehakase' => 'KZ',
+ 'k-meleon' => 'KM',
+ 'konqueror' => 'KO',
+ 'links' => 'LI',
+ 'lynx' => 'LX',
+ 'midori' => 'MI',
+
+ // SeaMonkey (formerly Mozilla Suite) (and rebranded versions)
+ 'mozilla' => 'MO',
+ 'gnuzilla' => 'SM',
+ 'iceape' => 'SM',
+ 'seamonkey' => 'SM',
+
+ // NCSA Mosaic (and incarnations)
+ 'mosaic' => 'MC',
+ 'ncsa mosaic' => 'MC',
+
+ // Netscape Navigator
+ 'navigator' => 'NS',
+ 'netscape6' => 'NS',
+ 'netscape' => 'NS',
+
+ 'nx' => 'NF',
+ 'netfront' => 'NF',
+
+ 'omniweb' => 'OW',
+
+ // Opera
+ 'nitro) opera' => 'OP',
+ 'opera' => 'OP',
+
+ 'rekonq' => 'RK',
+
+ // Safari
+ 'safari' => 'SF',
+ 'applewebkit' => 'SF',
+
+ 'titanium' => 'TI',
+
+ 'webos' => 'WO',
+ 'webpro' => 'WP',
+ );
+
+ // browser family (by layout engine)
+ static protected $browserType = array(
+ 'ie' => array('IE'),
+ 'gecko' => array('NS', 'PX', 'FF', 'FB', 'CA', 'GA', 'KM', 'MO', 'SM', 'CO', 'FE', 'KP', 'KZ', 'TB'),
+ 'khtml' => array('KO'),
+ 'webkit' => array('SF', 'CH', 'OW', 'AR', 'EP', 'FL', 'WO', 'AB', 'IR', 'CS', 'FD', 'HA', 'MI', 'GE', 'DF', 'BB', 'BP', 'TI', 'CF', 'RK', 'B2', 'NF'),
+ 'opera' => array('OP'),
+ );
+
+ // WebKit version numbers to Apple Safari version numbers (if Version/X.Y.Z not present)
+ static protected $safariVersions = array(
+ '536.25' => array('6', '0'),
+ '534.48' => array('5', '1'),
+ '533.16' => array('5', '0'),
+ '533.4' => array('4', '1'),
+ '526.11.2' => array('4', '0'),
+ '525.26' => array('3', '2'),
+ '525.13' => array('3', '1'),
+ '522.11' => array('3', '0'),
+ '412' => array('2', '0'),
+ '312' => array('1', '3'),
+ '125' => array('1', '2'),
+ '100' => array('1', '1'),
+ '85' => array('1', '0'),
+ '73' => array('0', '9'),
+ '48' => array('0', '8'),
+ );
+
+ // OmniWeb build numbers to OmniWeb version numbers (if Version/X.Y.Z not present)
+ static protected $omniWebVersions = array(
+ '622.15' => array('5', '11'),
+ '622.10' => array('5', '10'),
+ '622.8' => array('5', '9'),
+ '622.3' => array('5', '8'),
+ '621' => array('5', '7'),
+ '613' => array('5', '6'),
+ '607' => array('5', '5'),
+ '563.34' => array('5', '1'),
+ '558.36' => array('5', '0'),
+ '496' => array('4', '5'),
+ );
+
+ // OS regex => OS ID
+ static protected $operatingSystems = array(
+ 'Android' => 'AND',
+ 'Maemo' => 'MAE',
+ 'CrOS ' => 'LIN',
+ 'Linux' => 'LIN',
+
+ 'Xbox' => 'XBX',
+
+ // workaround for vendors who changed the WinPhone 7 user agent
+ 'WP7' => 'WPH',
+
+ 'CYGWIN_NT-6.2' => 'WI8',
+ 'Windows NT 6.2' => 'WI8',
+ 'Windows 8' => 'WI8',
+ 'CYGWIN_NT-6.1' => 'WI7',
+ 'Windows NT 6.1' => 'WI7',
+ 'Windows 7' => 'WI7',
+ 'CYGWIN_NT-6.0' => 'WVI',
+ 'Windows NT 6.0' => 'WVI',
+ 'Windows Vista' => 'WVI',
+ 'CYGWIN_NT-5.2' => 'WS3',
+ 'Windows NT 5.2' => 'WS3',
+ 'Windows Server 2003 / XP x64' => 'WS3',
+ 'CYGWIN_NT-5.1' => 'WXP',
+ 'Windows NT 5.1' => 'WXP',
+ 'Windows XP' => 'WXP',
+ 'CYGWIN_NT-5.0' => 'W2K',
+ 'Windows NT 5.0' => 'W2K',
+ 'Windows 2000' => 'W2K',
+ 'CYGWIN_NT-4.0' => 'WNT',
+ 'Windows NT 4.0' => 'WNT',
+ 'WinNT' => 'WNT',
+ 'Windows NT' => 'WNT',
+ 'CYGWIN_ME-4.90' => 'WME',
+ 'Win 9x 4.90' => 'WME',
+ 'Windows ME' => 'WME',
+ 'CYGWIN_98-4.10' => 'W98',
+ 'Win98' => 'W98',
+ 'Windows 98' => 'W98',
+ 'CYGWIN_95-4.0' => 'W95',
+ 'Win32' => 'W95',
+ 'Win95' => 'W95',
+ 'Windows 95' => 'W95',
+
+ // Windows Phone OS 7 and above
+ 'Windows Phone OS' => 'WPH',
+
+ // Windows Mobile 6.x and some later versions of Windows Mobile 5
+ 'IEMobile' => 'WMO', // fallback
+ 'Windows Mobile' => 'WMO',
+
+ // Windows CE, Pocket PC, and Windows Mobile 5 are indistinguishable without vendor/device specific detection
+ 'Windows CE' => 'WCE',
+
+ 'iPod' => 'IPD',
+ 'iPad' => 'IPA',
+ 'iPhone' => 'IPH',
// 'iOS' => 'IOS',
- 'Darwin' => 'MAC',
- 'Macintosh' => 'MAC',
- 'Power Macintosh' => 'MAC',
- 'Mac_PowerPC' => 'MAC',
- 'Mac PPC' => 'MAC',
- 'PPC' => 'MAC',
- 'Mac PowerPC' => 'MAC',
- 'Mac OS' => 'MAC',
-
- 'webOS' => 'WOS',
- 'Palm webOS' => 'WOS',
- 'PalmOS' => 'POS',
- 'Palm OS' => 'POS',
-
- 'BB10' => 'BBX',
- 'BlackBerry' => 'BLB',
- 'RIM Tablet OS' => 'QNX',
- 'QNX' => 'QNX',
-
- 'SymbOS' => 'SYM',
- 'Symbian OS' => 'SYM',
- 'SymbianOS' => 'SYM',
-
- 'bada' => 'SBA',
-
- 'SunOS' => 'SOS',
- 'AIX' => 'AIX',
- 'HP-UX' => 'HPX',
- 'OpenVMS' => 'VMS',
-
- 'FreeBSD' => 'BSD',
- 'NetBSD' => 'NBS',
- 'OpenBSD' => 'OBS',
- 'DragonFly' => 'DFB',
- 'Syllable' => 'SYL',
-
- 'Nintendo WiiU' => 'WIU',
- 'Nintendo Wii' => 'WII',
- 'Nitro' => 'NDS',
- 'Nintendo DSi' => 'DSI',
- 'Nintendo DS' => 'NDS',
- 'Nintendo 3DS' => '3DS',
-
- 'PlayStation Vita' => 'PSV',
- 'PlayStation Portable' => 'PSP',
- 'PlayStation 3' => 'PS3',
-
- 'IRIX' => 'IRI',
- 'OSF1' => 'T64',
- 'OS/2' => 'OS2',
- 'BEOS' => 'BEO',
- 'Amiga' => 'AMI',
- 'AmigaOS' => 'AMI',
- );
-
- // os family
- // NOTE: The keys in this array are used by plugins/UserSettings/functions.php . Any changes
- // made here should also be made in that file.
- static protected $osType = array(
- 'Windows' => array('WI8', 'WI7', 'WVI', 'WS3', 'WXP', 'W2K', 'WNT', 'WME', 'W98', 'W95'),
- 'Linux' => array('LIN'),
- 'Mac' => array('MAC'),
- 'iOS' => array('IPD', 'IPA', 'IPH'),
- 'Android' => array('AND'),
- 'Windows Mobile' => array('WPH', 'WMO', 'WCE'),
- 'Gaming Console' => array('WII', 'WIU', 'PS3', 'XBX'),
- 'Mobile Gaming Console' => array('PSP', 'PSV', 'NDS', 'DSI', '3DS'),
- 'Unix' => array('SOS', 'AIX', 'HP-UX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64'),
- 'Other Mobile' => array('MAE', 'WOS', 'POS', 'BLB', 'QNX', 'SYM', 'SBA'),
- 'Other' => array('VMS', 'OS2', 'BEOS', 'AMI')
- );
-
- static protected $browserIdToName;
- static protected $browserIdToShortName;
- static protected $operatingSystemsIdToName;
- static protected $operatingSystemsIdToShortName;
- static private $init = false;
-
- /**
- * Returns an array of the OS for the submitted user agent
- * 'id' => '',
- * 'name' => '',
- * 'short_name' => '',
- *
- * @param string $userAgent
- * @return string false if OS couldn't be identified, or 3 letters ID (eg. WXP)
- * @see UserAgentParser/OperatingSystems.php for the list of OS (also available in self::$operatingSystems)
- */
- static public function getOperatingSystem($userAgent)
- {
- $userAgent = self::cleanupUserAgent($userAgent);
- self::init();
- $info = array(
- 'id' => '',
- 'name' => '',
- 'short_name' => '',
- );
- foreach(self::$operatingSystems as $key => $value) {
- if (stristr($userAgent, $key) !== false) {
- $info['id'] = $value;
- break;
- }
- }
- if(empty($info['id'])) {
- return false;
- }
- $info['name'] = self::getOperatingSystemNameFromId($info['id']);
- $info['short_name'] = self::getOperatingSystemShortNameFromId($info['id']);
- return $info;
- }
-
- static protected function cleanupUserAgent($userAgent)
- {
- // in case value is URL encoded
- return urldecode($userAgent);
- }
-
- /**
- * Returns the browser information array, given a user agent string.
- *
- * @param string $userAgent
- * @return array false if the browser is "unknown", or
- * array( 'id' => '', // 2 letters ID, eg. FF
- * 'name' => '', // 2 letters ID, eg. FF
- * 'short_name' => '', // 2 letters ID, eg. FF
- * 'major_number' => '', // 2 in firefox 2.0.12
- * 'minor_number' => '', // 0 in firefox 2.0.12
- * 'version' => '', // major_number.minor_number
- * );
- * @see self::$browsers for the list of OS
- */
- static public function getBrowser($userAgent)
- {
- $userAgent = self::cleanupUserAgent($userAgent);
-
- self::init();
-
- $info = array(
- 'id' => '',
- 'name' => '',
- 'short_name' => '',
- 'major_number' => '',
- 'minor_number' => '',
- 'version' => '',
- );
-
- $browsers = self::$browsers;
-
- // derivative browsers often clone the base browser's useragent
- unset($browsers['firefox']);
- unset($browsers['mozilla']);
- unset($browsers['safari']);
- unset($browsers['applewebkit']);
-
- $browsersPattern = str_replace(')', '\)', implode('|', array_keys($browsers)));
-
- $results = array();
-
- // Misbehaving IE add-ons
- $userAgent = preg_replace('/[; ]Mozilla\/[0-9.]+ \([^)]+\)/', '', $userAgent);
-
- // Clean-up BlackBerry device UAs
- $userAgent = preg_replace('~^BlackBerry\d+/~', 'BlackBerry/', $userAgent);
-
- if (preg_match_all("/($browsersPattern)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results)
- || (strpos($userAgent, 'Shiira') === false && preg_match_all("/(firefox|thunderbird|safari)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results))
- || preg_match_all("/(applewebkit)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results)
- || preg_match_all("/^(mozilla)\/([0-9]+)([\.0-9a-z-]+)?(?: \[[a-z]{2}\])? (?:\([^)]*\))$/i", $userAgent, $results)
- || preg_match_all("/^(mozilla)\/[0-9]+(?:[\.0-9a-z-]+)?\s\(.* rv:([0-9]+)([.0-9a-z]+)\) gecko(\/[0-9]{8}|$)(?:.*)/i", $userAgent, $results)
+ 'Darwin' => 'MAC',
+ 'Macintosh' => 'MAC',
+ 'Power Macintosh' => 'MAC',
+ 'Mac_PowerPC' => 'MAC',
+ 'Mac PPC' => 'MAC',
+ 'PPC' => 'MAC',
+ 'Mac PowerPC' => 'MAC',
+ 'Mac OS' => 'MAC',
+
+ 'webOS' => 'WOS',
+ 'Palm webOS' => 'WOS',
+ 'PalmOS' => 'POS',
+ 'Palm OS' => 'POS',
+
+ 'BB10' => 'BBX',
+ 'BlackBerry' => 'BLB',
+ 'RIM Tablet OS' => 'QNX',
+ 'QNX' => 'QNX',
+
+ 'SymbOS' => 'SYM',
+ 'Symbian OS' => 'SYM',
+ 'SymbianOS' => 'SYM',
+
+ 'bada' => 'SBA',
+
+ 'SunOS' => 'SOS',
+ 'AIX' => 'AIX',
+ 'HP-UX' => 'HPX',
+ 'OpenVMS' => 'VMS',
+
+ 'FreeBSD' => 'BSD',
+ 'NetBSD' => 'NBS',
+ 'OpenBSD' => 'OBS',
+ 'DragonFly' => 'DFB',
+ 'Syllable' => 'SYL',
+
+ 'Nintendo WiiU' => 'WIU',
+ 'Nintendo Wii' => 'WII',
+ 'Nitro' => 'NDS',
+ 'Nintendo DSi' => 'DSI',
+ 'Nintendo DS' => 'NDS',
+ 'Nintendo 3DS' => '3DS',
+
+ 'PlayStation Vita' => 'PSV',
+ 'PlayStation Portable' => 'PSP',
+ 'PlayStation 3' => 'PS3',
+
+ 'IRIX' => 'IRI',
+ 'OSF1' => 'T64',
+ 'OS/2' => 'OS2',
+ 'BEOS' => 'BEO',
+ 'Amiga' => 'AMI',
+ 'AmigaOS' => 'AMI',
+ );
+
+ // os family
+ // NOTE: The keys in this array are used by plugins/UserSettings/functions.php . Any changes
+ // made here should also be made in that file.
+ static protected $osType = array(
+ 'Windows' => array('WI8', 'WI7', 'WVI', 'WS3', 'WXP', 'W2K', 'WNT', 'WME', 'W98', 'W95'),
+ 'Linux' => array('LIN'),
+ 'Mac' => array('MAC'),
+ 'iOS' => array('IPD', 'IPA', 'IPH'),
+ 'Android' => array('AND'),
+ 'Windows Mobile' => array('WPH', 'WMO', 'WCE'),
+ 'Gaming Console' => array('WII', 'WIU', 'PS3', 'XBX'),
+ 'Mobile Gaming Console' => array('PSP', 'PSV', 'NDS', 'DSI', '3DS'),
+ 'Unix' => array('SOS', 'AIX', 'HP-UX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64'),
+ 'Other Mobile' => array('MAE', 'WOS', 'POS', 'BLB', 'QNX', 'SYM', 'SBA'),
+ 'Other' => array('VMS', 'OS2', 'BEOS', 'AMI')
+ );
+
+ static protected $browserIdToName;
+ static protected $browserIdToShortName;
+ static protected $operatingSystemsIdToName;
+ static protected $operatingSystemsIdToShortName;
+ static private $init = false;
+
+ /**
+ * Returns an array of the OS for the submitted user agent
+ * 'id' => '',
+ * 'name' => '',
+ * 'short_name' => '',
+ *
+ * @param string $userAgent
+ * @return string false if OS couldn't be identified, or 3 letters ID (eg. WXP)
+ * @see UserAgentParser/OperatingSystems.php for the list of OS (also available in self::$operatingSystems)
+ */
+ static public function getOperatingSystem($userAgent)
+ {
+ $userAgent = self::cleanupUserAgent($userAgent);
+ self::init();
+ $info = array(
+ 'id' => '',
+ 'name' => '',
+ 'short_name' => '',
+ );
+ foreach (self::$operatingSystems as $key => $value) {
+ if (stristr($userAgent, $key) !== false) {
+ $info['id'] = $value;
+ break;
+ }
+ }
+ if (empty($info['id'])) {
+ return false;
+ }
+ $info['name'] = self::getOperatingSystemNameFromId($info['id']);
+ $info['short_name'] = self::getOperatingSystemShortNameFromId($info['id']);
+ return $info;
+ }
+
+ static protected function cleanupUserAgent($userAgent)
+ {
+ // in case value is URL encoded
+ return urldecode($userAgent);
+ }
+
+ /**
+ * Returns the browser information array, given a user agent string.
+ *
+ * @param string $userAgent
+ * @return array false if the browser is "unknown", or
+ * array( 'id' => '', // 2 letters ID, eg. FF
+ * 'name' => '', // 2 letters ID, eg. FF
+ * 'short_name' => '', // 2 letters ID, eg. FF
+ * 'major_number' => '', // 2 in firefox 2.0.12
+ * 'minor_number' => '', // 0 in firefox 2.0.12
+ * 'version' => '', // major_number.minor_number
+ * );
+ * @see self::$browsers for the list of OS
+ */
+ static public function getBrowser($userAgent)
+ {
+ $userAgent = self::cleanupUserAgent($userAgent);
+
+ self::init();
+
+ $info = array(
+ 'id' => '',
+ 'name' => '',
+ 'short_name' => '',
+ 'major_number' => '',
+ 'minor_number' => '',
+ 'version' => '',
+ );
+
+ $browsers = self::$browsers;
+
+ // derivative browsers often clone the base browser's useragent
+ unset($browsers['firefox']);
+ unset($browsers['mozilla']);
+ unset($browsers['safari']);
+ unset($browsers['applewebkit']);
+
+ $browsersPattern = str_replace(')', '\)', implode('|', array_keys($browsers)));
+
+ $results = array();
+
+ // Misbehaving IE add-ons
+ $userAgent = preg_replace('/[; ]Mozilla\/[0-9.]+ \([^)]+\)/', '', $userAgent);
+
+ // Clean-up BlackBerry device UAs
+ $userAgent = preg_replace('~^BlackBerry\d+/~', 'BlackBerry/', $userAgent);
+
+ if (preg_match_all("/($browsersPattern)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results)
+ || (strpos($userAgent, 'Shiira') === false && preg_match_all("/(firefox|thunderbird|safari)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results))
+ || preg_match_all("/(applewebkit)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results)
+ || preg_match_all("/^(mozilla)\/([0-9]+)([\.0-9a-z-]+)?(?: \[[a-z]{2}\])? (?:\([^)]*\))$/i", $userAgent, $results)
+ || preg_match_all("/^(mozilla)\/[0-9]+(?:[\.0-9a-z-]+)?\s\(.* rv:([0-9]+)([.0-9a-z]+)\) gecko(\/[0-9]{8}|$)(?:.*)/i", $userAgent, $results)
|| (strpos($userAgent, 'Nintendo 3DS') !== false && preg_match_all("/^(mozilla).*version\/([0-9]+)([.0-9a-z]+)?/i", $userAgent, $results))
- )
- {
- // browser code (usually the first match)
- $count = 0;
- $info['id'] = self::$browsers[strtolower($results[1][0])];
-
- // sometimes there's a better match at the end
- if(strpos($userAgent, 'chromeframe') !== false) {
- $count = count($results[0]) - 1;
- $info['id'] = 'CF';
- }
- elseif(($info['id'] == 'IE' || $info['id'] == 'LX') && (count($results[0]) > 1)) {
- $count = count($results[0]) - 1;
- $info['id'] = self::$browsers[strtolower($results[1][$count])];
- }
-
- // Netscape fix
- if($info['id'] == 'MO' && $count == 0) {
- if(stripos($userAgent, 'PlayStation') !== false) {
- return false;
- }
- if(strpos($userAgent, 'Nintendo 3DS') !== false) {
+ ) {
+ // browser code (usually the first match)
+ $count = 0;
+ $info['id'] = self::$browsers[strtolower($results[1][0])];
+
+ // sometimes there's a better match at the end
+ if (strpos($userAgent, 'chromeframe') !== false) {
+ $count = count($results[0]) - 1;
+ $info['id'] = 'CF';
+ } elseif (($info['id'] == 'IE' || $info['id'] == 'LX') && (count($results[0]) > 1)) {
+ $count = count($results[0]) - 1;
+ $info['id'] = self::$browsers[strtolower($results[1][$count])];
+ }
+
+ // Netscape fix
+ if ($info['id'] == 'MO' && $count == 0) {
+ if (stripos($userAgent, 'PlayStation') !== false) {
+ return false;
+ }
+ if (strpos($userAgent, 'Nintendo 3DS') !== false) {
$info['id'] = 'NF';
+ } elseif (count($results) == 4) {
+ $info['id'] = 'NS';
+ }
+ } // BlackBerry devices
+ elseif (strpos($userAgent, 'BlackBerry') !== false) {
+ $info['id'] = 'BB';
+ } elseif (strpos($userAgent, 'RIM Tablet OS') !== false) {
+ $info['id'] = 'BP';
+ } elseif (strpos($userAgent, 'BB10') !== false) {
+ $info['id'] = 'B2';
+ } elseif (strpos($userAgent, 'Playstation Vita') !== false) {
+ $info['id'] = 'NF';
+
+ if (preg_match_all("/(silk)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $newResults)) {
+ $results = $newResults;
+ $count = count($results[0]) - 1;
+ }
+ }
+
+ // Version/X.Y.Z override
+ if (preg_match_all("/(version)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $newResults)) {
+ $results = $newResults;
+ $count = count($results[0]) - 1;
+ }
+
+ // major version number (1 in mozilla 1.7)
+ $info['major_number'] = $results[2][$count];
+
+ // is an minor version number ? If not, 0
+ $match = array();
+
+ preg_match('/([.\0-9]+)?([\.a-z0-9]+)?/i', $results[3][$count], $match);
+
+ if (isset($match[1])) {
+ // find minor version number (7 in mozilla 1.7, 9 in firefox 0.9.3)
+ $dot = strpos(substr($match[1], 1), '.');
+ if ($dot !== false) {
+ $info['minor_number'] = substr($match[1], 1, $dot);
+ } else {
+ $info['minor_number'] = substr($match[1], 1);
+ }
+ } else {
+ $info['minor_number'] = '0';
+ }
+ $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
+
+ // IE compatibility mode
+ if ($info['id'] == 'IE'
+ && strncmp($userAgent, 'Mozilla/4.0', 11) == 0
+ && preg_match('~ Trident/([0-9]+)\.[0-9]+~', $userAgent, $tridentVersion)
+ ) {
+ $info['major_number'] = $tridentVersion[1] + 4;
+ $info['minor_number'] = '0';
+ $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
+ }
+
+ // Safari fix
+ if ($info['id'] == 'SF') {
+ foreach (self::$safariVersions as $buildVersion => $productVersion) {
+ if (version_compare($info['version'], $buildVersion) >= 0) {
+ $info['major_number'] = $productVersion[0];
+ $info['minor_number'] = $productVersion[1];
+ $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
+ break;
+ }
+ }
+ }
+
+ // OmniWeb fix
+ if ($info['id'] == 'OW') {
+ foreach (self::$omniWebVersions as $buildVersion => $productVersion) {
+ if (version_compare($info['version'], $buildVersion) >= 0) {
+ $info['major_number'] = $productVersion[0];
+ $info['minor_number'] = $productVersion[1];
+ $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
+ break;
+ }
}
- elseif(count($results) == 4) {
- $info['id'] = 'NS';
- }
- }
- // BlackBerry devices
- elseif(strpos($userAgent, 'BlackBerry') !== false) {
- $info['id'] = 'BB';
- }
- elseif(strpos($userAgent, 'RIM Tablet OS') !== false) {
- $info['id'] = 'BP';
- }
- elseif(strpos($userAgent, 'BB10') !== false) {
- $info['id'] = 'B2';
- }
- elseif(strpos($userAgent, 'Playstation Vita') !== false) {
- $info['id'] = 'NF';
-
- if(preg_match_all("/(silk)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $newResults))
- {
- $results = $newResults;
- $count = count($results[0])-1;
- }
- }
-
- // Version/X.Y.Z override
- if(preg_match_all("/(version)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $newResults))
- {
- $results = $newResults;
- $count = count($results[0])-1;
- }
-
- // major version number (1 in mozilla 1.7)
- $info['major_number'] = $results[2][$count];
-
- // is an minor version number ? If not, 0
- $match = array();
-
- preg_match('/([.\0-9]+)?([\.a-z0-9]+)?/i', $results[3][$count], $match);
-
- if(isset($match[1])) {
- // find minor version number (7 in mozilla 1.7, 9 in firefox 0.9.3)
- $dot = strpos(substr($match[1], 1), '.');
- if($dot !== false) {
- $info['minor_number'] = substr($match[1], 1, $dot);
- }
- else {
- $info['minor_number'] = substr($match[1], 1);
- }
- }
- else {
- $info['minor_number'] = '0';
- }
- $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
-
- // IE compatibility mode
- if($info['id'] == 'IE'
- && strncmp($userAgent, 'Mozilla/4.0', 11) == 0
- && preg_match('~ Trident/([0-9]+)\.[0-9]+~', $userAgent, $tridentVersion))
- {
- $info['major_number'] = $tridentVersion[1] + 4;
- $info['minor_number'] = '0';
- $info['version'] = $info['major_number'] . '.' .$info['minor_number'];
- }
-
- // Safari fix
- if($info['id'] == 'SF') {
- foreach(self::$safariVersions as $buildVersion => $productVersion) {
- if(version_compare($info['version'], $buildVersion) >= 0) {
- $info['major_number'] = $productVersion[0];
- $info['minor_number'] = $productVersion[1];
- $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
- break;
- }
- }
- }
-
- // OmniWeb fix
- if($info['id'] == 'OW') {
- foreach(self::$omniWebVersions as $buildVersion => $productVersion) {
- if(version_compare($info['version'], $buildVersion) >= 0) {
- $info['major_number'] = $productVersion[0];
- $info['minor_number'] = $productVersion[1];
- $info['version'] = $info['major_number'] . '.' . $info['minor_number'];
- break;
- }
- }
- }
-
- // SeaMonkey fix
- if($info['id'] == 'MO' && $info['version'] == '1.9') {
- $info['id'] = 'SM';
- }
-
- $info['name'] = self::getBrowserNameFromId($info['id']);
- $info['short_name'] = self::getBrowserShortNameFromId($info['id']);
-
- return $info;
- }
-
- return false;
- }
-
- static protected function init() {
- if(self::$init) {
- return;
- }
- self::$init = true;
-
- // init browser names and short names
- self::$browserIdToName = array_map('ucwords',array_flip(self::$browsers));
- self::$browserIdToName['AB'] = 'ABrowse';
- self::$browserIdToName['AV'] = 'AmigaVoyager';
- self::$browserIdToName['AW'] = 'Amiga AWeb';
- self::$browserIdToName['BB'] = 'BlackBerry';
- self::$browserIdToName['BP'] = 'PlayBook';
- self::$browserIdToName['B2'] = 'BlackBerry';
- self::$browserIdToName['BX'] = 'BrowseX';
- self::$browserIdToName['CF'] = 'Chrome Frame';
- self::$browserIdToName['CO'] = 'CometBird';
- self::$browserIdToName['EL'] = 'ELinks';
- self::$browserIdToName['FF'] = 'Firefox';
- self::$browserIdToName['HJ'] = 'HotJava';
- self::$browserIdToName['IB'] = 'IBrowse';
- self::$browserIdToName['IC'] = 'iCab';
- self::$browserIdToName['KM'] = 'K-Meleon';
- self::$browserIdToName['MC'] = 'NCSA Mosaic';
- self::$browserIdToName['NF'] = 'NetFront';
- self::$browserIdToName['OW'] = 'OmniWeb';
- self::$browserIdToName['SF'] = 'Safari';
- self::$browserIdToName['SM'] = 'SeaMonkey';
- self::$browserIdToName['WO'] = 'Palm webOS';
- self::$browserIdToName['WP'] = 'WebPro';
-
- self::$browserIdToShortName = self::$browserIdToName;
- self::$browserIdToShortName['AW'] = 'AWeb';
- self::$browserIdToShortName['FB'] = 'Firebird';
- self::$browserIdToShortName['IE'] = 'IE';
- self::$browserIdToShortName['MC'] = 'Mosaic';
- self::$browserIdToShortName['BP'] = 'PlayBook';
- self::$browserIdToShortName['WO'] = 'webOS';
-
- // init OS names and short names
+ }
+
+ // SeaMonkey fix
+ if ($info['id'] == 'MO' && $info['version'] == '1.9') {
+ $info['id'] = 'SM';
+ }
+
+ $info['name'] = self::getBrowserNameFromId($info['id']);
+ $info['short_name'] = self::getBrowserShortNameFromId($info['id']);
+
+ return $info;
+ }
+
+ return false;
+ }
+
+ static protected function init()
+ {
+ if (self::$init) {
+ return;
+ }
+ self::$init = true;
+
+ // init browser names and short names
+ self::$browserIdToName = array_map('ucwords', array_flip(self::$browsers));
+ self::$browserIdToName['AB'] = 'ABrowse';
+ self::$browserIdToName['AV'] = 'AmigaVoyager';
+ self::$browserIdToName['AW'] = 'Amiga AWeb';
+ self::$browserIdToName['BB'] = 'BlackBerry';
+ self::$browserIdToName['BP'] = 'PlayBook';
+ self::$browserIdToName['B2'] = 'BlackBerry';
+ self::$browserIdToName['BX'] = 'BrowseX';
+ self::$browserIdToName['CF'] = 'Chrome Frame';
+ self::$browserIdToName['CO'] = 'CometBird';
+ self::$browserIdToName['EL'] = 'ELinks';
+ self::$browserIdToName['FF'] = 'Firefox';
+ self::$browserIdToName['HJ'] = 'HotJava';
+ self::$browserIdToName['IB'] = 'IBrowse';
+ self::$browserIdToName['IC'] = 'iCab';
+ self::$browserIdToName['KM'] = 'K-Meleon';
+ self::$browserIdToName['MC'] = 'NCSA Mosaic';
+ self::$browserIdToName['NF'] = 'NetFront';
+ self::$browserIdToName['OW'] = 'OmniWeb';
+ self::$browserIdToName['SF'] = 'Safari';
+ self::$browserIdToName['SM'] = 'SeaMonkey';
+ self::$browserIdToName['WO'] = 'Palm webOS';
+ self::$browserIdToName['WP'] = 'WebPro';
+
+ self::$browserIdToShortName = self::$browserIdToName;
+ self::$browserIdToShortName['AW'] = 'AWeb';
+ self::$browserIdToShortName['FB'] = 'Firebird';
+ self::$browserIdToShortName['IE'] = 'IE';
+ self::$browserIdToShortName['MC'] = 'Mosaic';
+ self::$browserIdToShortName['BP'] = 'PlayBook';
+ self::$browserIdToShortName['WO'] = 'webOS';
+
+ // init OS names and short names
$operatingSystemsIdToName = array(
'IPD' => 'iPod',
'IPA' => 'iPad',
@@ -655,74 +645,70 @@ class UserAgentParser
'UNK' => 'Unknown',
);
self::$operatingSystemsIdToShortName = array_merge(self::$operatingSystemsIdToName, $operatingSystemsIdToShortName);
- }
-
- static public function getBrowserNameFromId($browserId)
- {
- self::init();
- if(isset(self::$browserIdToName[$browserId])) {
- return self::$browserIdToName[$browserId];
- }
- return false;
- }
-
- static public function getBrowserShortNameFromId($browserId)
- {
- self::init();
- if(isset(self::$browserIdToShortName[$browserId])) {
- return self::$browserIdToShortName[$browserId];
- }
- return false;
- }
-
- static public function getBrowserFamilyFromId($browserId)
- {
- self::init();
- $familyNameToUse = 'unknown';
- foreach(self::$browserType as $familyName => $aBrowsers)
- {
- if(in_array($browserId, $aBrowsers))
- {
- $familyNameToUse = $familyName;
- break;
- }
- }
- return $familyNameToUse;
- }
-
- static public function getOperatingSystemNameFromId($osId)
- {
- self::init();
- if(isset(self::$operatingSystemsIdToName[$osId])) {
- return self::$operatingSystemsIdToName[$osId];
- }
- return false;
- }
-
- static public function getOperatingSystemShortNameFromId($osId)
- {
- self::init();
- if(isset(self::$operatingSystemsIdToShortName[$osId])) {
- return self::$operatingSystemsIdToShortName[$osId];
- }
- return false;
- }
-
- static public function getOperatingSystemIdFromName($osName)
- {
- return isset(self::$operatingSystems[$osName]) ? self::$operatingSystems[$osName] : false;
- }
-
- static public function getOperatingSystemFamilyFromId($osId)
- {
- self::init();
- foreach (self::$osType as $familyName => $aSystems)
- {
- if (in_array($osId, $aSystems))
- {
- return $familyName;
- }
- }
- return 'unknown';
- }
+ }
+
+ static public function getBrowserNameFromId($browserId)
+ {
+ self::init();
+ if (isset(self::$browserIdToName[$browserId])) {
+ return self::$browserIdToName[$browserId];
+ }
+ return false;
+ }
+
+ static public function getBrowserShortNameFromId($browserId)
+ {
+ self::init();
+ if (isset(self::$browserIdToShortName[$browserId])) {
+ return self::$browserIdToShortName[$browserId];
+ }
+ return false;
+ }
+
+ static public function getBrowserFamilyFromId($browserId)
+ {
+ self::init();
+ $familyNameToUse = 'unknown';
+ foreach (self::$browserType as $familyName => $aBrowsers) {
+ if (in_array($browserId, $aBrowsers)) {
+ $familyNameToUse = $familyName;
+ break;
+ }
+ }
+ return $familyNameToUse;
+ }
+
+ static public function getOperatingSystemNameFromId($osId)
+ {
+ self::init();
+ if (isset(self::$operatingSystemsIdToName[$osId])) {
+ return self::$operatingSystemsIdToName[$osId];
+ }
+ return false;
+ }
+
+ static public function getOperatingSystemShortNameFromId($osId)
+ {
+ self::init();
+ if (isset(self::$operatingSystemsIdToShortName[$osId])) {
+ return self::$operatingSystemsIdToShortName[$osId];
+ }
+ return false;
+ }
+
+ static public function getOperatingSystemIdFromName($osName)
+ {
+ return isset(self::$operatingSystems[$osName]) ? self::$operatingSystems[$osName] : false;
+ }
+
+ static public function getOperatingSystemFamilyFromId($osId)
+ {
+ self::init();
+ foreach (self::$osType as $familyName => $aSystems) {
+ if (in_array($osId, $aSystems)) {
+ return $familyName;
+ }
+ }
+ return 'unknown';
+ }
}
diff --git a/libs/UserAgentParser/UserAgentParser.test.php b/libs/UserAgentParser/UserAgentParser.test.php
index f5468c1b6b..cd23f36e99 100644
--- a/libs/UserAgentParser/UserAgentParser.test.php
+++ b/libs/UserAgentParser/UserAgentParser.test.php
@@ -1,48 +1,48 @@
<?php
-if(!isset($_GET['setUserAgent']) && !isset($_SERVER['HTTP_USER_AGENT'])) die;
+if (!isset($_GET['setUserAgent']) && !isset($_SERVER['HTTP_USER_AGENT'])) die;
-require_once dirname(__FILE__).'/UserAgentParser.php';
+require_once dirname(__FILE__) . '/UserAgentParser.php';
echo "<h2>UserAgentParser php library test</h2>";
-$testUserAgent = array(
- 'my user agent' => '',
- 'ie8 on win7' => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 3.0.04506; .NET CLR 3.5.21022; InfoPath.2; SLCC1; Zune 3.0)',
- 'ie8 on vista (compatibility view)' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0)',
- 'ie8 on vista' => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)',
- 'chrome on winxp' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.48 Safari/525.19',
- 'IE6 on winxp' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648)',
- 'safari on winxp' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21',
- 'FF3 on winxp' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6',
- 'opera 9.63 on winxp' => 'Opera/9.63 (Windows NT 5.1; U; en) Presto/2.1.1',
- 'Blackberry' => 'BlackBerry8700/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1',
- 'opera 9.30 on Nintendo Wii' => 'Opera/9.30 (Nintendo Wii; U; ; 2047-7; en)',
- 'iphone' => 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_2 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5G77 Safari/525.20',
- 'iPod touch' => 'Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3A100a Safari/419.3',
- 'iPod' => 'Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11a Safari/525.20',
- 'Android' => 'Mozilla/5.0 (Linux; U; Android 1.1; en-us; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2',
- 'PalmOS' => 'Mozilla/5.0 [en] (PalmOS; U; WebPro/3.5; Palm-Zi72) ',
- 'safari on mac os X' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/527.3+ (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1',
- 'opera 9.64 on win ME' => 'Opera/9.64 (Windows ME; U; en) Presto/2.1.1',
- 'opera 10.00 on XP' => 'Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.00',
- 'iron on win7' => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/531.0 (KHTML, like Gecko) Iron/3.0.189.0 Safari/531.0',
- 'firefox 3.6 alpha on vista' => 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2a2pre) Gecko/20090826 Namoroka/3.6a2pre',
- 'firefox 3.5 alpha on win7' => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1b4pre) Gecko/20090420 Shiretoko/3.5b4pre (.NET CLR 3.5.30729)',
- 'firefox nightly build' => 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:2.0a1pre) Gecko/2008060602 Minefield/4.0a1pre',
- 'thunderbird 14.0 with lightning 1.6' => 'Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20120713 Thunderbird/14.0 Lightning/1.6',
+$testUserAgent = array(
+ 'my user agent' => '',
+ 'ie8 on win7' => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET CLR 3.0.04506; .NET CLR 3.5.21022; InfoPath.2; SLCC1; Zune 3.0)',
+ 'ie8 on vista (compatibility view)' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0)',
+ 'ie8 on vista' => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)',
+ 'chrome on winxp' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.48 Safari/525.19',
+ 'IE6 on winxp' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648)',
+ 'safari on winxp' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21',
+ 'FF3 on winxp' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6',
+ 'opera 9.63 on winxp' => 'Opera/9.63 (Windows NT 5.1; U; en) Presto/2.1.1',
+ 'Blackberry' => 'BlackBerry8700/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1',
+ 'opera 9.30 on Nintendo Wii' => 'Opera/9.30 (Nintendo Wii; U; ; 2047-7; en)',
+ 'iphone' => 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_2 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5G77 Safari/525.20',
+ 'iPod touch' => 'Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3A100a Safari/419.3',
+ 'iPod' => 'Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11a Safari/525.20',
+ 'Android' => 'Mozilla/5.0 (Linux; U; Android 1.1; en-us; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2',
+ 'PalmOS' => 'Mozilla/5.0 [en] (PalmOS; U; WebPro/3.5; Palm-Zi72) ',
+ 'safari on mac os X' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/527.3+ (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1',
+ 'opera 9.64 on win ME' => 'Opera/9.64 (Windows ME; U; en) Presto/2.1.1',
+ 'opera 10.00 on XP' => 'Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.00',
+ 'iron on win7' => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/531.0 (KHTML, like Gecko) Iron/3.0.189.0 Safari/531.0',
+ 'firefox 3.6 alpha on vista' => 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2a2pre) Gecko/20090826 Namoroka/3.6a2pre',
+ 'firefox 3.5 alpha on win7' => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1b4pre) Gecko/20090420 Shiretoko/3.5b4pre (.NET CLR 3.5.30729)',
+ 'firefox nightly build' => 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:2.0a1pre) Gecko/2008060602 Minefield/4.0a1pre',
+ 'thunderbird 14.0 with lightning 1.6' => 'Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20120713 Thunderbird/14.0 Lightning/1.6',
);
echo "Test with another user agent: ";
-foreach($testUserAgent as $name => $userAgent) {
- echo "<a href='?setUserAgent=".urlencode($userAgent)."'>$name</a>, ";
+foreach ($testUserAgent as $name => $userAgent) {
+ echo "<a href='?setUserAgent=" . urlencode($userAgent) . "'>$name</a>, ";
}
echo "<hr>";
-if(isset($_GET['setUserAgent']) && !empty($_GET['setUserAgent'])) {
- echo "User Agent:";
- $userAgent = urldecode($_GET['setUserAgent']);
+if (isset($_GET['setUserAgent']) && !empty($_GET['setUserAgent'])) {
+ echo "User Agent:";
+ $userAgent = urldecode($_GET['setUserAgent']);
} else {
- echo "Your user agent:";
- $userAgent = $_SERVER['HTTP_USER_AGENT'];
+ echo "Your user agent:";
+ $userAgent = $_SERVER['HTTP_USER_AGENT'];
}
-echo " <b>".htmlentities($userAgent)."</b><br><br>";
+echo " <b>" . htmlentities($userAgent) . "</b><br><br>";
echo "Browser info:<pre>";
var_dump(UserAgentParser::getBrowser($userAgent));
diff --git a/misc/How to install Piwik.html b/misc/How to install Piwik.html
index 12978d9412..5a26b7d34e 100644
--- a/misc/How to install Piwik.html
+++ b/misc/How to install Piwik.html
@@ -1,3 +1,7 @@
-<html><head>
-<meta http-equiv="refresh" content="0;url=http://piwik.org/docs/installation/"/>
-</head><body>You will be redirected to the Piwik Installation documentation on <a href='http://piwik.org/docs/installation/'>http://piwik.org/docs/installation/</a></body></html>
+<html>
+<head>
+ <meta http-equiv="refresh" content="0;url=http://piwik.org/docs/installation/"/>
+</head>
+<body>You will be redirected to the Piwik Installation documentation on <a href='http://piwik.org/docs/installation/'>http://piwik.org/docs/installation/</a>
+</body>
+</html>
diff --git a/misc/cron/archive.php b/misc/cron/archive.php
index 9899e81eca..452cd499e8 100644
--- a/misc/cron/archive.php
+++ b/misc/cron/archive.php
@@ -1,7 +1,7 @@
<?php
$USAGE = "
Usage:
- /path/to/cli/php \"".@$_SERVER['argv'][0]."\" --url=http://your-website.org/path/to/piwik/ [arguments]
+ /path/to/cli/php \"" . @$_SERVER['argv'][0] . "\" --url=http://your-website.org/path/to/piwik/ [arguments]
Arguments:
--url=[piwik-server-url]
@@ -46,7 +46,7 @@ Ideas for improvements:
- Core: check that on first day of month, if request last month from UI,
it returns last temporary monthly report generated, if the last month haven't yet been processed / finalized
*/
-define('PIWIK_INCLUDE_PATH', realpath( dirname(__FILE__)."/../.." ));
+define('PIWIK_INCLUDE_PATH', realpath(dirname(__FILE__) . "/../.."));
define('PIWIK_USER_PATH', PIWIK_INCLUDE_PATH);
define('PIWIK_ENABLE_DISPATCH', false);
define('PIWIK_ENABLE_ERROR_HANDLER', false);
@@ -56,348 +56,326 @@ require_once PIWIK_INCLUDE_PATH . "/index.php";
require_once PIWIK_INCLUDE_PATH . "/core/API/Request.php";
try {
- $archiving = new Archiving;
- $archiving->init();
- $archiving->run();
- $archiving->end();
-} catch(Exception $e) {
- $archiving->logFatalError($e->getMessage());
+ $archiving = new Archiving;
+ $archiving->init();
+ $archiving->run();
+ $archiving->end();
+} catch (Exception $e) {
+ $archiving->logFatalError($e->getMessage());
}
class Archiving
{
- const OPTION_ARCHIVING_FINISHED_TS = "LastCompletedFullArchiving";
- const TRUNCATE_ERROR_MESSAGE_SUMMARY = 400;
-
- // Seconds window to look back to define "active websites" to archive on the first archive.php script execution
- protected $firstRunActiveWebsitesWithTraffic = 604800; // 7 days
-
- // By default, we only process the current week/month/year at most once an hour
- protected $processPeriodsMaximumEverySeconds = 3600;
-
- protected $websiteDayHasFinishedSinceLastRun = array();
- protected $idSitesInvalidatedOldReports = array();
- protected $piwikUrl = false;
- protected $token_auth = false;
- protected $visits = 0;
- protected $requests = 0;
- protected $output = '';
- protected $shouldResetState = false;
- protected $shouldArchiveAllWebsites = false;
- protected $acceptInvalidSSLCertificate = false;
- /**
- * By default, will process last 52 days/weeks/months/year.
- * It will be overwritten by the number of days since last archiving ran until completion.
- */
- const DEFAULT_DATE_LAST = 52;
- // Since weeks are not used in yearly archives, we make sure that all possible weeks are processed
- const DEFAULT_DATE_LAST_WEEKS = 520;
-
- protected $timeLastCompleted = false;
- protected $requestPrepend = '&trigger=archivephp';
- protected $errors = array();
-
- public function init()
- {
- $this->initCore();
- $this->initTokenAuth();
- $this->initCheckCli();
- $this->initLog();
- $this->displayHelp();
- $this->initPiwikHost();
- $this->initStateFromParameters();
- Piwik::setUserIsSuperUser(true);
-
- $this->logSection("INIT");
- $this->log("Querying Piwik API at: {$this->piwikUrl}");
- $this->log("Running Piwik ". Piwik_Version::VERSION ." as Super User: " . $this->login);
-
- $this->acceptInvalidSSLCertificate = $this->isParameterSet("accept-invalid-ssl-certificate");
-
- // Test the specified piwik URL is valid
- $response = $this->request("?module=API&method=API.getDefaultMetricTranslations&format=original&serialize=1");
- $responseUnserialized = @unserialize($response);
- if($response === false
- || !is_array($responseUnserialized))
- {
- $this->logFatalError("The Piwik URL {$this->piwikUrl} does not seem to be pointing to a Piwik server. Response was '$response'.");
- }
-
- $this->log("Notes");
- // Information about timeout
- $this->todayArchiveTimeToLive = Piwik_ArchiveProcessing::getTodayArchiveTimeToLive();
- $this->log("- Reports for today will be processed at most every ".Piwik_ArchiveProcessing::getTodayArchiveTimeToLive()
- ." seconds. You can change this value in Piwik UI > Settings > General Settings.");
- $this->log("- Reports for the current week/month/year will be refreshed at most every "
- .$this->processPeriodsMaximumEverySeconds." seconds.");
- // Fetching segments to process
- $this->segments = Piwik_CoreAdminHome_API::getInstance()->getKnownSegmentsToArchive();
- if(empty($this->segments)) $this->segments = array();
- if(!empty($this->segments))
- {
- $this->log("- Segments to pre-process for each website and each period: ". implode(", ", $this->segments));
- }
-
- // Try and not request older data we know is already archived
- if($this->timeLastCompleted !== false)
- {
- $dateLast = time() - $this->timeLastCompleted;
- $this->log("- Archiving was last executed without error ".Piwik::getPrettyTimeFromSeconds($dateLast, true, $isHtml = false)." ago");
- }
-
- $this->initWebsitesToProcess();
- flush();
- }
-
- /**
- * Returns URL to process reports for the $idsite on a given period with no segment
- */
- protected function getVisitsRequestUrl($idsite, $period, $lastTimestampWebsiteProcessed = false)
- {
- if(empty($lastTimestampWebsiteProcessed))
- {
- $dateLast = self::DEFAULT_DATE_LAST;
- if($period == 'week')
- {
- $dateLast = self::DEFAULT_DATE_LAST_WEEKS;
- }
- }
- else
- {
- // Enforcing last2 at minimum to work around timing issues and ensure we make most archives available
- $dateLast = floor( (time() - $lastTimestampWebsiteProcessed) / 86400) + 2;
- if($dateLast > self::DEFAULT_DATE_LAST)
- {
- $dateLast = self::DEFAULT_DATE_LAST;
- }
- }
- return "?module=API&method=VisitsSummary.getVisits&idSite=$idsite&period=$period&date=last".$dateLast."&format=php&token_auth=".$this->token_auth;
- }
-
- protected function lastRunKey($idsite, $period)
- {
- return "lastRunArchive". $period ."_". $idsite;
- }
-
- /**
- * Main function, runs archiving on all websites with new activity
- */
- public function run()
- {
- $websitesWithVisitsSinceLastRun =
- $skippedPeriodsArchivesWebsite =
- $skippedDayArchivesWebsites =
- $skipped =
- $processed =
- $archivedPeriodsArchivesWebsite = 0;
- $timer = new Piwik_Timer;
-
- $this->logSection("START");
- $this->log("Starting Piwik reports archiving...");
-
- $this->websites = array_unique($this->websites);
- foreach ($this->websites as $idsite)
- {
- flush();
- $requestsBefore = $this->requests;
- if ($idsite <= 0)
- {
- continue;
- }
-
- $timerWebsite = new Piwik_Timer;
-
- $lastTimestampWebsiteProcessedPeriods = $lastTimestampWebsiteProcessedDay = false;
- if(!$this->shouldResetState)
- {
- $lastTimestampWebsiteProcessedPeriods = Piwik_GetOption( $this->lastRunKey($idsite, "periods") );
- $lastTimestampWebsiteProcessedDay = Piwik_GetOption( $this->lastRunKey($idsite, "day") );
- }
-
- // For period other than days, we only re-process the reports at most
- // 1) every $processPeriodsMaximumEverySeconds
- $secondsSinceLastExecution = time() - $lastTimestampWebsiteProcessedPeriods;
-
- // if timeout is more than 10 min, we account for a 5 min processing time, and allow trigger 1 min earlier
- 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
- $shouldArchivePeriods = true;
- }
-
-
- // (*) If the website is archived because it is a new day in its timezone
- // We make sure all periods are archived, even if there is 0 visit today
- $dayHasEndedMustReprocess = in_array($idsite, $this->websiteDayHasFinishedSinceLastRun);
- if($dayHasEndedMustReprocess)
- {
- $shouldArchivePeriods = true;
- }
-
- // (*) If there was some old reports invalidated for this website
- // we make sure all these old reports are triggered at least once
- $websiteIsOldDataInvalidate = in_array($idsite, $this->idSitesInvalidatedOldReports);
- if($websiteIsOldDataInvalidate)
- {
- $shouldArchivePeriods = true;
- }
-
- // Test if we should process this website at all
- $elapsedSinceLastArchiving = time() - $lastTimestampWebsiteProcessedDay;
- if( !$websiteIsOldDataInvalidate // Invalidate old website forces the archiving for this site
- && !$dayHasEndedMustReprocess // Also reprocess when day has ended since last run
- && $elapsedSinceLastArchiving < $this->todayArchiveTimeToLive)
- {
- $this->log("Skipped website id $idsite, already processed today's report in recent run, "
- .Piwik::getPrettyTimeFromSeconds($elapsedSinceLastArchiving, true, $isHtml = false)
- ." ago, ".$timerWebsite->__toString());
- $skippedDayArchivesWebsites++;
- $skipped++;
- continue;
- }
-
- // Fake that the request is already done, so that other archive.php
- // running do not grab the same website from the queue
- Piwik_SetOption( $this->lastRunKey($idsite, "day"), time() );
-
- $url = $this->getVisitsRequestUrl($idsite, "day",
- // when some data was purged from this website
- // we make sure we query all previous days/weeks/months
- ($websiteIsOldDataInvalidate
- // when --force-all-websites option,
- // also forces to archive last52 days to be safe
- || $this->shouldArchiveAllWebsites)
- ? false
- : $lastTimestampWebsiteProcessedDay
- );
- $content = $this->request($url);
- $response = @unserialize($content);
-
- if(empty($content)
- || !is_array($response)
- || count($response) == 0)
- {
- // cancel the succesful run flag
- Piwik_SetOption( $this->lastRunKey($idsite, "day"), 0 );
-
- $this->log("WARNING: Empty or invalid response '$content' for website id $idsite, ".$timerWebsite->__toString().", skipping");
- $skipped++;
- continue;
- }
+ const OPTION_ARCHIVING_FINISHED_TS = "LastCompletedFullArchiving";
+ const TRUNCATE_ERROR_MESSAGE_SUMMARY = 400;
+
+ // Seconds window to look back to define "active websites" to archive on the first archive.php script execution
+ protected $firstRunActiveWebsitesWithTraffic = 604800; // 7 days
+
+ // By default, we only process the current week/month/year at most once an hour
+ protected $processPeriodsMaximumEverySeconds = 3600;
+
+ protected $websiteDayHasFinishedSinceLastRun = array();
+ protected $idSitesInvalidatedOldReports = array();
+ protected $piwikUrl = false;
+ protected $token_auth = false;
+ protected $visits = 0;
+ protected $requests = 0;
+ protected $output = '';
+ protected $shouldResetState = false;
+ protected $shouldArchiveAllWebsites = false;
+ protected $acceptInvalidSSLCertificate = false;
+ /**
+ * By default, will process last 52 days/weeks/months/year.
+ * It will be overwritten by the number of days since last archiving ran until completion.
+ */
+ const DEFAULT_DATE_LAST = 52;
+ // Since weeks are not used in yearly archives, we make sure that all possible weeks are processed
+ const DEFAULT_DATE_LAST_WEEKS = 520;
+
+ protected $timeLastCompleted = false;
+ protected $requestPrepend = '&trigger=archivephp';
+ protected $errors = array();
+
+ public function init()
+ {
+ $this->initCore();
+ $this->initTokenAuth();
+ $this->initCheckCli();
+ $this->initLog();
+ $this->displayHelp();
+ $this->initPiwikHost();
+ $this->initStateFromParameters();
+ Piwik::setUserIsSuperUser(true);
+
+ $this->logSection("INIT");
+ $this->log("Querying Piwik API at: {$this->piwikUrl}");
+ $this->log("Running Piwik " . Piwik_Version::VERSION . " as Super User: " . $this->login);
+
+ $this->acceptInvalidSSLCertificate = $this->isParameterSet("accept-invalid-ssl-certificate");
+
+ // Test the specified piwik URL is valid
+ $response = $this->request("?module=API&method=API.getDefaultMetricTranslations&format=original&serialize=1");
+ $responseUnserialized = @unserialize($response);
+ if ($response === false
+ || !is_array($responseUnserialized)
+ ) {
+ $this->logFatalError("The Piwik URL {$this->piwikUrl} does not seem to be pointing to a Piwik server. Response was '$response'.");
+ }
+
+ $this->log("Notes");
+ // Information about timeout
+ $this->todayArchiveTimeToLive = Piwik_ArchiveProcessing::getTodayArchiveTimeToLive();
+ $this->log("- Reports for today will be processed at most every " . Piwik_ArchiveProcessing::getTodayArchiveTimeToLive()
+ . " seconds. You can change this value in Piwik UI > Settings > General Settings.");
+ $this->log("- Reports for the current week/month/year will be refreshed at most every "
+ . $this->processPeriodsMaximumEverySeconds . " seconds.");
+ // Fetching segments to process
+ $this->segments = Piwik_CoreAdminHome_API::getInstance()->getKnownSegmentsToArchive();
+ if (empty($this->segments)) $this->segments = array();
+ if (!empty($this->segments)) {
+ $this->log("- Segments to pre-process for each website and each period: " . implode(", ", $this->segments));
+ }
+
+ // Try and not request older data we know is already archived
+ if ($this->timeLastCompleted !== false) {
+ $dateLast = time() - $this->timeLastCompleted;
+ $this->log("- Archiving was last executed without error " . Piwik::getPrettyTimeFromSeconds($dateLast, true, $isHtml = false) . " ago");
+ }
+
+ $this->initWebsitesToProcess();
+ flush();
+ }
+
+ /**
+ * Returns URL to process reports for the $idsite on a given period with no segment
+ */
+ protected function getVisitsRequestUrl($idsite, $period, $lastTimestampWebsiteProcessed = false)
+ {
+ if (empty($lastTimestampWebsiteProcessed)) {
+ $dateLast = self::DEFAULT_DATE_LAST;
+ if ($period == 'week') {
+ $dateLast = self::DEFAULT_DATE_LAST_WEEKS;
+ }
+ } else {
+ // Enforcing last2 at minimum to work around timing issues and ensure we make most archives available
+ $dateLast = floor((time() - $lastTimestampWebsiteProcessed) / 86400) + 2;
+ if ($dateLast > self::DEFAULT_DATE_LAST) {
+ $dateLast = self::DEFAULT_DATE_LAST;
+ }
+ }
+ return "?module=API&method=VisitsSummary.getVisits&idSite=$idsite&period=$period&date=last" . $dateLast . "&format=php&token_auth=" . $this->token_auth;
+ }
+
+ protected function lastRunKey($idsite, $period)
+ {
+ return "lastRunArchive" . $period . "_" . $idsite;
+ }
+
+ /**
+ * Main function, runs archiving on all websites with new activity
+ */
+ public function run()
+ {
+ $websitesWithVisitsSinceLastRun =
+ $skippedPeriodsArchivesWebsite =
+ $skippedDayArchivesWebsites =
+ $skipped =
+ $processed =
+ $archivedPeriodsArchivesWebsite = 0;
+ $timer = new Piwik_Timer;
+
+ $this->logSection("START");
+ $this->log("Starting Piwik reports archiving...");
+
+ $this->websites = array_unique($this->websites);
+ foreach ($this->websites as $idsite) {
+ flush();
+ $requestsBefore = $this->requests;
+ if ($idsite <= 0) {
+ continue;
+ }
+
+ $timerWebsite = new Piwik_Timer;
+
+ $lastTimestampWebsiteProcessedPeriods = $lastTimestampWebsiteProcessedDay = false;
+ if (!$this->shouldResetState) {
+ $lastTimestampWebsiteProcessedPeriods = Piwik_GetOption($this->lastRunKey($idsite, "periods"));
+ $lastTimestampWebsiteProcessedDay = Piwik_GetOption($this->lastRunKey($idsite, "day"));
+ }
+
+ // For period other than days, we only re-process the reports at most
+ // 1) every $processPeriodsMaximumEverySeconds
+ $secondsSinceLastExecution = time() - $lastTimestampWebsiteProcessedPeriods;
+
+ // if timeout is more than 10 min, we account for a 5 min processing time, and allow trigger 1 min earlier
+ 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
+ $shouldArchivePeriods = true;
+ }
+
+
+ // (*) If the website is archived because it is a new day in its timezone
+ // We make sure all periods are archived, even if there is 0 visit today
+ $dayHasEndedMustReprocess = in_array($idsite, $this->websiteDayHasFinishedSinceLastRun);
+ if ($dayHasEndedMustReprocess) {
+ $shouldArchivePeriods = true;
+ }
+
+ // (*) If there was some old reports invalidated for this website
+ // we make sure all these old reports are triggered at least once
+ $websiteIsOldDataInvalidate = in_array($idsite, $this->idSitesInvalidatedOldReports);
+ if ($websiteIsOldDataInvalidate) {
+ $shouldArchivePeriods = true;
+ }
+
+ // Test if we should process this website at all
+ $elapsedSinceLastArchiving = time() - $lastTimestampWebsiteProcessedDay;
+ if (!$websiteIsOldDataInvalidate // Invalidate old website forces the archiving for this site
+ && !$dayHasEndedMustReprocess // Also reprocess when day has ended since last run
+ && $elapsedSinceLastArchiving < $this->todayArchiveTimeToLive
+ ) {
+ $this->log("Skipped website id $idsite, already processed today's report in recent run, "
+ . Piwik::getPrettyTimeFromSeconds($elapsedSinceLastArchiving, true, $isHtml = false)
+ . " ago, " . $timerWebsite->__toString());
+ $skippedDayArchivesWebsites++;
+ $skipped++;
+ continue;
+ }
+
+ // Fake that the request is already done, so that other archive.php
+ // running do not grab the same website from the queue
+ Piwik_SetOption($this->lastRunKey($idsite, "day"), time());
+
+ $url = $this->getVisitsRequestUrl($idsite, "day",
+ // when some data was purged from this website
+ // we make sure we query all previous days/weeks/months
+ ($websiteIsOldDataInvalidate
+ // when --force-all-websites option,
+ // also forces to archive last52 days to be safe
+ || $this->shouldArchiveAllWebsites)
+ ? false
+ : $lastTimestampWebsiteProcessedDay
+ );
+ $content = $this->request($url);
+ $response = @unserialize($content);
+
+ if (empty($content)
+ || !is_array($response)
+ || count($response) == 0
+ ) {
+ // cancel the succesful run flag
+ Piwik_SetOption($this->lastRunKey($idsite, "day"), 0);
+
+ $this->log("WARNING: Empty or invalid response '$content' for website id $idsite, " . $timerWebsite->__toString() . ", skipping");
+ $skipped++;
+ continue;
+ }
$visitsToday = end($response);
- $this->requests++;
- $processed++;
-
- // If there is no visit today and we don't need to process this website, we can skip remaining archives
- if($visitsToday <= 0
- && !$shouldArchivePeriods)
- {
- $this->log("Skipped website id $idsite, no visit today, ".$timerWebsite->__toString());
- $skipped++;
- continue;
- }
-
- $visitsAllDays = array_sum($response);
- if($visitsAllDays == 0
- && !$shouldArchivePeriods
- && $this->shouldArchiveAllWebsites
- )
- {
- $this->log("Skipped website id $idsite, no visits in the last ".count($response)." days, ".$timerWebsite->__toString());
- $skipped++;
- continue;
- }
- $this->visits += $visitsToday;
- $websitesWithVisitsSinceLastRun++;
- $this->archiveVisitsAndSegments($idsite, "day", $lastTimestampWebsiteProcessedDay, $timerWebsite);
-
- if($shouldArchivePeriods)
- {
- $success = true;
- foreach (array('week', 'month', 'year') as $period)
- {
- $success = $this->archiveVisitsAndSegments($idsite, $period, $lastTimestampWebsiteProcessedPeriods) && $success;
- }
- // Record succesful run of this website's periods archiving
- if($success)
- {
- Piwik_SetOption( $this->lastRunKey($idsite, "periods"), time() );
-
- // Remove this website from the list of websites to be invalidated
- // since it's now just been re-processing the reports, job is done!
- if( $websiteIsOldDataInvalidate )
- {
- $websiteIdsInvalidated = Piwik_CoreAdminHome_API::getWebsiteIdsToInvalidate();
- if(count($websiteIdsInvalidated))
- {
- $found = array_search($idsite, $websiteIdsInvalidated);
- if($found!==false)
- {
- unset($websiteIdsInvalidated[$found]);
+ $this->requests++;
+ $processed++;
+
+ // If there is no visit today and we don't need to process this website, we can skip remaining archives
+ if ($visitsToday <= 0
+ && !$shouldArchivePeriods
+ ) {
+ $this->log("Skipped website id $idsite, no visit today, " . $timerWebsite->__toString());
+ $skipped++;
+ continue;
+ }
+
+ $visitsAllDays = array_sum($response);
+ if ($visitsAllDays == 0
+ && !$shouldArchivePeriods
+ && $this->shouldArchiveAllWebsites
+ ) {
+ $this->log("Skipped website id $idsite, no visits in the last " . count($response) . " days, " . $timerWebsite->__toString());
+ $skipped++;
+ continue;
+ }
+ $this->visits += $visitsToday;
+ $websitesWithVisitsSinceLastRun++;
+ $this->archiveVisitsAndSegments($idsite, "day", $lastTimestampWebsiteProcessedDay, $timerWebsite);
+
+ if ($shouldArchivePeriods) {
+ $success = true;
+ foreach (array('week', 'month', 'year') as $period) {
+ $success = $this->archiveVisitsAndSegments($idsite, $period, $lastTimestampWebsiteProcessedPeriods) && $success;
+ }
+ // Record succesful run of this website's periods archiving
+ if ($success) {
+ Piwik_SetOption($this->lastRunKey($idsite, "periods"), time());
+
+ // Remove this website from the list of websites to be invalidated
+ // since it's now just been re-processing the reports, job is done!
+ if ($websiteIsOldDataInvalidate) {
+ $websiteIdsInvalidated = Piwik_CoreAdminHome_API::getWebsiteIdsToInvalidate();
+ if (count($websiteIdsInvalidated)) {
+ $found = array_search($idsite, $websiteIdsInvalidated);
+ if ($found !== false) {
+ unset($websiteIdsInvalidated[$found]);
// $this->log("Websites left to invalidate: " . implode(", ", $websiteIdsInvalidated));
- Piwik_SetOption(Piwik_CoreAdminHome_API::OPTION_INVALIDATED_IDSITES, serialize($websiteIdsInvalidated));
- }
- }
- }
- }
- }
- $archivedPeriodsArchivesWebsite++;
-
- $requestsWebsite = $this->requests - $requestsBefore;
- $debug = $this->shouldArchiveAllWebsites ? ", last days = $visitsAllDays visits" : "";
- Piwik::log("Archived website id = $idsite, today = $visitsToday visits"
- .$debug.", $requestsWebsite API requests, "
- . $timerWebsite->__toString()
- ." [" . ($websitesWithVisitsSinceLastRun+$skipped) . "/"
- . count($this->websites)
- . " done]" );
-
- }
-
- $this->log("Done archiving!");
-
- $this->logSection("SUMMARY");
- $this->log("Total daily visits archived: ". $this->visits);
-
- $totalWebsites = count($this->allWebsites);
- $skipped = $totalWebsites - $websitesWithVisitsSinceLastRun;
- $this->log("Archived today's reports for $websitesWithVisitsSinceLastRun websites");
- $this->log("Archived week/month/year for $archivedPeriodsArchivesWebsite websites. ");
- $this->log("Skipped $skipped websites: no new visit since the last script execution");
- $this->log("Skipped $skippedDayArchivesWebsites websites day archiving: existing daily reports are less than {$this->todayArchiveTimeToLive} seconds old");
- $this->log("Skipped $skippedPeriodsArchivesWebsite websites week/month/year archiving: existing periods reports are less than {$this->processPeriodsMaximumEverySeconds} seconds old");
- $this->log("Total API requests: $this->requests");
-
- //DONE: done/total, visits, wtoday, wperiods, reqs, time, errors[count]: first eg.
- $percent = count($this->websites) == 0
- ? ""
- : " ".round($processed * 100 / count($this->websites),0) ."%";
- $otherInParallel = $skippedDayArchivesWebsites;
- $this->log("done: ".
- $processed ."/". count($this->websites) . "" . $percent. ", ".
- $this->visits." v, $websitesWithVisitsSinceLastRun wtoday, $archivedPeriodsArchivesWebsite wperiods, ".
- $this->requests." req, ".round($timer->getTimeMs())." ms, ".
- (empty($this->errors)
- ? "no error"
- : (count($this->errors) . " errors. eg. '". reset($this->errors)."'" ))
- );
- $this->log($timer->__toString());
- $this->logSection("SCHEDULED TASKS");
- $this->log("Starting Scheduled tasks... ");
-
- $tasksOutput = $this->request("?module=API&method=CoreAdminHome.runScheduledTasks&format=csv&convertToUnicode=0&token_auth=".$this->token_auth);
- if($tasksOutput == "No data available")
- {
- $tasksOutput = " No task to run";
- }
- $this->log($tasksOutput);
- $this->log("done");
- }
+ Piwik_SetOption(Piwik_CoreAdminHome_API::OPTION_INVALIDATED_IDSITES, serialize($websiteIdsInvalidated));
+ }
+ }
+ }
+ }
+ }
+ $archivedPeriodsArchivesWebsite++;
+
+ $requestsWebsite = $this->requests - $requestsBefore;
+ $debug = $this->shouldArchiveAllWebsites ? ", last days = $visitsAllDays visits" : "";
+ Piwik::log("Archived website id = $idsite, today = $visitsToday visits"
+ . $debug . ", $requestsWebsite API requests, "
+ . $timerWebsite->__toString()
+ . " [" . ($websitesWithVisitsSinceLastRun + $skipped) . "/"
+ . count($this->websites)
+ . " done]");
+
+ }
+
+ $this->log("Done archiving!");
+
+ $this->logSection("SUMMARY");
+ $this->log("Total daily visits archived: " . $this->visits);
+
+ $totalWebsites = count($this->allWebsites);
+ $skipped = $totalWebsites - $websitesWithVisitsSinceLastRun;
+ $this->log("Archived today's reports for $websitesWithVisitsSinceLastRun websites");
+ $this->log("Archived week/month/year for $archivedPeriodsArchivesWebsite websites. ");
+ $this->log("Skipped $skipped websites: no new visit since the last script execution");
+ $this->log("Skipped $skippedDayArchivesWebsites websites day archiving: existing daily reports are less than {$this->todayArchiveTimeToLive} seconds old");
+ $this->log("Skipped $skippedPeriodsArchivesWebsite websites week/month/year archiving: existing periods reports are less than {$this->processPeriodsMaximumEverySeconds} seconds old");
+ $this->log("Total API requests: $this->requests");
+
+ //DONE: done/total, visits, wtoday, wperiods, reqs, time, errors[count]: first eg.
+ $percent = count($this->websites) == 0
+ ? ""
+ : " " . round($processed * 100 / count($this->websites), 0) . "%";
+ $otherInParallel = $skippedDayArchivesWebsites;
+ $this->log("done: " .
+ $processed . "/" . count($this->websites) . "" . $percent . ", " .
+ $this->visits . " v, $websitesWithVisitsSinceLastRun wtoday, $archivedPeriodsArchivesWebsite wperiods, " .
+ $this->requests . " req, " . round($timer->getTimeMs()) . " ms, " .
+ (empty($this->errors)
+ ? "no error"
+ : (count($this->errors) . " errors. eg. '" . reset($this->errors) . "'"))
+ );
+ $this->log($timer->__toString());
+ $this->logSection("SCHEDULED TASKS");
+ $this->log("Starting Scheduled tasks... ");
+
+ $tasksOutput = $this->request("?module=API&method=CoreAdminHome.runScheduledTasks&format=csv&convertToUnicode=0&token_auth=" . $this->token_auth);
+ if ($tasksOutput == "No data available") {
+ $tasksOutput = " No task to run";
+ }
+ $this->log($tasksOutput);
+ $this->log("done");
+ }
/**
* Archive visits and segments.
@@ -408,50 +386,48 @@ class Archiving
* @param Piwik_Timer $timerWebsite
* @return bool True on success, false if some request failed
*/
- private function archiveVisitsAndSegments($idsite, $period, $lastTimestampWebsiteProcessed, Piwik_Timer $timerWebsite = null)
- {
- $timer = new Piwik_Timer;
- $aCurl = array();
- $mh = false;
- $url = $this->piwikUrl . $this->getVisitsRequestUrl($idsite, $period, $lastTimestampWebsiteProcessed) . $this->requestPrepend;
-
- // already processed above for "day"
- if($period != "day")
- {
- $ch = $this->getNewCurlHandle($url);
+ private function archiveVisitsAndSegments($idsite, $period, $lastTimestampWebsiteProcessed, Piwik_Timer $timerWebsite = null)
+ {
+ $timer = new Piwik_Timer;
+ $aCurl = array();
+ $mh = false;
+ $url = $this->piwikUrl . $this->getVisitsRequestUrl($idsite, $period, $lastTimestampWebsiteProcessed) . $this->requestPrepend;
+
+ // already processed above for "day"
+ if ($period != "day") {
+ $ch = $this->getNewCurlHandle($url);
$this->addCurlHandleToMulti($mh, $ch);
- $aCurl[$url] = $ch;
- $this->requests++;
- }
- $urlNoSegment = $url;
- foreach ($this->segments as $segment) {
- $segmentUrl = $url.'&segment='.urlencode($segment);
- $ch = $this->getNewCurlHandle($segmentUrl);
+ $aCurl[$url] = $ch;
+ $this->requests++;
+ }
+ $urlNoSegment = $url;
+ foreach ($this->segments as $segment) {
+ $segmentUrl = $url . '&segment=' . urlencode($segment);
+ $ch = $this->getNewCurlHandle($segmentUrl);
$this->addCurlHandleToMulti($mh, $ch);
- $aCurl[$segmentUrl] = $ch;
- $this->requests++;
- }
+ $aCurl[$segmentUrl] = $ch;
+ $this->requests++;
+ }
$success = true;
$visitsAllDaysInPeriod = false;
- if(!empty($aCurl)) {
- $running=null;
+ if (!empty($aCurl)) {
+ $running = null;
do {
usleep(10000);
curl_multi_exec($mh, $running);
} while ($running > 0);
- foreach($aCurl as $url => $ch){
+ foreach ($aCurl as $url => $ch) {
$content = curl_multi_getcontent($ch);
$successResponse = $this->checkResponse($content, $url);
$success = $successResponse && $success;
- if($url == $urlNoSegment
- && $successResponse)
- {
+ if ($url == $urlNoSegment
+ && $successResponse
+ ) {
$stats = unserialize($content);
- if(!is_array($stats))
- {
+ if (!is_array($stats)) {
$this->logError("Error unserializing the following response: " . $content);
}
$visitsAllDaysInPeriod = @array_sum($stats);
@@ -464,11 +440,11 @@ class Archiving
curl_multi_close($mh);
}
- $this->log("Archived website id = $idsite, period = $period, "
- . ($period != "day" ? (int)$visitsAllDaysInPeriod. " visits, " : "" )
- . (!empty($timerWebsite) ? $timerWebsite->__toString() : $timer->__toString()));
- return $success;
- }
+ $this->log("Archived website id = $idsite, period = $period, "
+ . ($period != "day" ? (int)$visitsAllDaysInPeriod . " visits, " : "")
+ . (!empty($timerWebsite) ? $timerWebsite->__toString() : $timer->__toString()));
+ return $success;
+ }
private function addCurlHandleToMulti(&$mh, $ch)
{
@@ -479,363 +455,337 @@ class Archiving
}
private function getNewCurlHandle($url)
- {
- $ch = curl_init($url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-
- if ($this->acceptInvalidSSLCertificate) {
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
- }
- curl_setopt($ch, CURLOPT_USERAGENT, Piwik_Http::getUserAgent());
- Piwik_Http::configCurlCertificate($ch);
- return $ch;
- }
-
-
- /**
- * Logs a section in the output
- */
- private function logSection($title="")
- {
- $this->log("---------------------------");
- $this->log($title);
- }
-
- /**
- * End of the script
- */
- public function end()
- {
- // How to test the error handling code?
- // - Generate some hits since last archive.php run
- // - Start the script, in the middle, shutdown apache, then restore
- // Some errors should be logged and script should successfully finish and then report the errors and trigger a PHP error
- if(!empty($this->errors))
- {
- $this->logSection("SUMMARY OF ERRORS");
-
- foreach($this->errors as $error) {
- $this->log("Error: ". $error);
- }
- $summary = count($this->errors) . " total errors during this script execution, please investigate and try and fix these errors";
- $this->log($summary);
-
- $summary .= '. First error was: '. reset($this->errors);
- $this->logFatalError($summary);
- }
- else
- {
- // No error -> Logs the successful script execution until completion
- Piwik_SetOption(self::OPTION_ARCHIVING_FINISHED_TS, time());
- }
- }
-
+ {
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- private function log($m)
- {
- $this->output .= $m . "\n";
- Piwik::log($m);
- }
-
- /**
- * Issues a request to $url
- */
- protected function request($url)
- {
- $url = $this->piwikUrl. $url . $this->requestPrepend;
- //$this->log($url);
- try {
- $response = Piwik_Http::sendHttpRequestBy('curl', $url, $timeout = 300, $userAgent = null, $destinationPath = null, $file = null, $followDepth = 0, $acceptLanguage = false, $acceptInvalidSSLCertificate = $this->acceptInvalidSSLCertificate);
- } catch(Exception $e) {
- return $this->logNetworkError($url, $e->getMessage());
- }
- if($this->checkResponse($response, $url))
- {
- return $response;
- }
- return false;
- }
-
- private function checkResponse($response, $url)
- {
- if(empty($response)
- || stripos($response, 'error')) {
- return $this->logNetworkError($url, $response);
- }
- return true;
- }
-
- private function logError($m)
- {
- $this->errors[] = substr($m, 0, self::TRUNCATE_ERROR_MESSAGE_SUMMARY);
- $this->log("ERROR: $m");
- }
-
- public function logFatalError($m, $backtrace = true)
- {
- $this->logError($m);
- $fe = fopen('php://stderr', 'w');
- fwrite($fe, "Error in the last Piwik archive.php run: \n" . $m
- . ($backtrace ? "\n\n Here is the full errors output:\n\n" . $this->output : '')
- );
- trigger_error($m, E_USER_ERROR);
- exit;
- }
-
- private function logNetworkError($url, $response)
- {
- $message = "Got invalid response from API request: $url. ";
- if(empty($response))
- {
- $message .= "The response was empty. This usually means a server error. This solution to this error is generally to increase the value of 'memory_limit' in your php.ini file. Please check your Web server Error Log file for more details.";
- }
- else
- {
- $message .= "Response was '$response'";
- }
- $this->logError($message);
- return false;
- }
-
- /**
- * Displays script usage
- */
- protected function usage()
- {
- global $USAGE;
- $this->logLines($USAGE);
- }
-
- private function logLines($t)
- {
- foreach(explode(PHP_EOL, $t) as $line)
- {
- $this->log($line);
- }
- }
-
- private function initLog()
- {
- $config = Piwik_Config::getInstance();
- $config->log['log_only_when_debug_parameter'] = 0;
- $config->log['logger_message'] = array("logger_message" => "screen");
- Piwik::createLogObject();
-
- if(!function_exists("curl_multi_init")) {
- $this->log("ERROR: this script requires curl extension php_curl enabled in your CLI php.ini");
- $this->usage();
- exit;
- }
- }
-
- /**
- * Script does run on http:// ONLY if the SU token is specified
- */
- private function initCheckCli()
- {
- if(!Piwik_Common::isPhpCliMode())
- {
- $token_auth = Piwik_Common::getRequestVar('token_auth', '', 'string');
- if($token_auth != $this->token_auth
- || strlen($token_auth) != 32)
- {
- die('<b>You must specify the Super User token_auth as a parameter to this script, eg. <code>?token_auth=XYZ</code> if you wish to run this script through the browser. </b><br>
+ if ($this->acceptInvalidSSLCertificate) {
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ }
+ curl_setopt($ch, CURLOPT_USERAGENT, Piwik_Http::getUserAgent());
+ Piwik_Http::configCurlCertificate($ch);
+ return $ch;
+ }
+
+
+ /**
+ * Logs a section in the output
+ */
+ private function logSection($title = "")
+ {
+ $this->log("---------------------------");
+ $this->log($title);
+ }
+
+ /**
+ * End of the script
+ */
+ public function end()
+ {
+ // How to test the error handling code?
+ // - Generate some hits since last archive.php run
+ // - Start the script, in the middle, shutdown apache, then restore
+ // Some errors should be logged and script should successfully finish and then report the errors and trigger a PHP error
+ if (!empty($this->errors)) {
+ $this->logSection("SUMMARY OF ERRORS");
+
+ foreach ($this->errors as $error) {
+ $this->log("Error: " . $error);
+ }
+ $summary = count($this->errors) . " total errors during this script execution, please investigate and try and fix these errors";
+ $this->log($summary);
+
+ $summary .= '. First error was: ' . reset($this->errors);
+ $this->logFatalError($summary);
+ } else {
+ // No error -> Logs the successful script execution until completion
+ Piwik_SetOption(self::OPTION_ARCHIVING_FINISHED_TS, time());
+ }
+ }
+
+
+ private function log($m)
+ {
+ $this->output .= $m . "\n";
+ Piwik::log($m);
+ }
+
+ /**
+ * Issues a request to $url
+ */
+ protected function request($url)
+ {
+ $url = $this->piwikUrl . $url . $this->requestPrepend;
+ //$this->log($url);
+ try {
+ $response = Piwik_Http::sendHttpRequestBy('curl', $url, $timeout = 300, $userAgent = null, $destinationPath = null, $file = null, $followDepth = 0, $acceptLanguage = false, $acceptInvalidSSLCertificate = $this->acceptInvalidSSLCertificate);
+ } catch (Exception $e) {
+ return $this->logNetworkError($url, $e->getMessage());
+ }
+ if ($this->checkResponse($response, $url)) {
+ return $response;
+ }
+ return false;
+ }
+
+ private function checkResponse($response, $url)
+ {
+ if (empty($response)
+ || stripos($response, 'error')
+ ) {
+ return $this->logNetworkError($url, $response);
+ }
+ return true;
+ }
+
+ private function logError($m)
+ {
+ $this->errors[] = substr($m, 0, self::TRUNCATE_ERROR_MESSAGE_SUMMARY);
+ $this->log("ERROR: $m");
+ }
+
+ public function logFatalError($m, $backtrace = true)
+ {
+ $this->logError($m);
+ $fe = fopen('php://stderr', 'w');
+ fwrite($fe, "Error in the last Piwik archive.php run: \n" . $m
+ . ($backtrace ? "\n\n Here is the full errors output:\n\n" . $this->output : '')
+ );
+ trigger_error($m, E_USER_ERROR);
+ exit;
+ }
+
+ private function logNetworkError($url, $response)
+ {
+ $message = "Got invalid response from API request: $url. ";
+ if (empty($response)) {
+ $message .= "The response was empty. This usually means a server error. This solution to this error is generally to increase the value of 'memory_limit' in your php.ini file. Please check your Web server Error Log file for more details.";
+ } else {
+ $message .= "Response was '$response'";
+ }
+ $this->logError($message);
+ return false;
+ }
+
+ /**
+ * Displays script usage
+ */
+ protected function usage()
+ {
+ global $USAGE;
+ $this->logLines($USAGE);
+ }
+
+ private function logLines($t)
+ {
+ foreach (explode(PHP_EOL, $t) as $line) {
+ $this->log($line);
+ }
+ }
+
+ private function initLog()
+ {
+ $config = Piwik_Config::getInstance();
+ $config->log['log_only_when_debug_parameter'] = 0;
+ $config->log['logger_message'] = array("logger_message" => "screen");
+ Piwik::createLogObject();
+
+ if (!function_exists("curl_multi_init")) {
+ $this->log("ERROR: this script requires curl extension php_curl enabled in your CLI php.ini");
+ $this->usage();
+ exit;
+ }
+ }
+
+ /**
+ * Script does run on http:// ONLY if the SU token is specified
+ */
+ private function initCheckCli()
+ {
+ if (!Piwik_Common::isPhpCliMode()) {
+ $token_auth = Piwik_Common::getRequestVar('token_auth', '', 'string');
+ if ($token_auth != $this->token_auth
+ || strlen($token_auth) != 32
+ ) {
+ die('<b>You must specify the Super User token_auth as a parameter to this script, eg. <code>?token_auth=XYZ</code> if you wish to run this script through the browser. </b><br>
However it is recommended to run it <a href="http://piwik.org/docs/setup-auto-archiving/">via cron in the command line</a>, since it can take a long time to run.<br/>
In a shell, execute for example the following to trigger archiving on the local Piwik server:<br/>
<code>$ /path/to/php /path/to/piwik/misc/cron/archive.php --url=http://your-website.org/path/to/piwik/</code>');
- }
- }
- }
-
- /**
- * Init Piwik, connect DB, create log & config objects, etc.
- */
- private function initCore()
- {
- try {
- Piwik_FrontController::getInstance()->init();
- } catch(Exception $e) {
- echo "ERROR: During Piwik init, Message: ".$e->getMessage();
- exit;
- }
- }
-
- private function displayHelp()
- {
- $displayHelp = $this->isParameterSet('help') || $this->isParameterSet('h');
- if ($displayHelp)
- {
- $this->usage();
- exit;
- }
- }
-
- protected function initStateFromParameters()
- {
- // Detect parameters
- $reset = $this->isParameterSet("force-all-periods", $valuePossible = true);
- $forceAll = $this->isParameterSet("force-all-websites");
- $forceTimeoutPeriod = $this->isParameterSet("force-timeout-for-periods", $valuePossible = true);
- if(!empty($forceTimeoutPeriod)
- && $forceTimeoutPeriod !== true) // in case --force-timeout-for-periods= without [seconds] specified
- {
- // Ensure the cache for periods is at least as high as cache for today
- $todayTTL = Piwik_ArchiveProcessing::getTodayArchiveTimeToLive();
- if($forceTimeoutPeriod < $todayTTL)
- {
- $this->log("WARNING: Automatically increasing --force-timeout-for-periods from $forceTimeoutPeriod to "
- . $todayTTL
- . " to match the cache timeout for Today's report specified in Piwik UI > Settings > General Settings");
- $forceTimeoutPeriod = $todayTTL;
- }
- $this->processPeriodsMaximumEverySeconds = $forceTimeoutPeriod;
- }
-
- // Recommend to disable browser archiving when using this script
- if( Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled() )
- {
- $this->log("NOTE: if you execute this script at least once per hour (or more often) in a crontab, you may disable 'Browser trigger archiving' in Piwik UI > Settings > General Settings. ");
- $this->log(" see doc at: http://piwik.org/docs/setup-auto-archiving/");
- }
-
- if($reset)
- {
- $this->log("--force-all-periods was detected: the script will run as if it was its first run, and will trigger archiving for all periods.");
- $this->shouldResetState = true;
-
- if(!$forceAll
- && is_numeric($reset)
- && $reset > 0)
- {
- $this->firstRunActiveWebsitesWithTraffic = (int)$reset;
- }
- }
-
- if($forceAll)
- {
- $this->log("--force-all-websites was detected: the script will archive all websites and all periods sequentially");
- $this->shouldArchiveAllWebsites = true;
- }
-
- $this->timeLastCompleted = Piwik_GetOption(self::OPTION_ARCHIVING_FINISHED_TS);
- if($this->shouldResetState)
- {
- $this->timeLastCompleted = false;
- }
- }
-
- // Fetching websites to process
- protected function initWebsitesToProcess()
- {
- $this->allWebsites = Piwik_SitesManager_API::getInstance()->getAllSitesId();
-
- if($this->shouldArchiveAllWebsites)
- {
- $this->websites = $this->allWebsites;
- $this->log("Will process ". count($this->websites). " websites");
- }
- else
- {
- // 1) All websites with visits since the last archive.php execution
- $timestampActiveTraffic = $this->timeLastCompleted;
- if(empty($timestampActiveTraffic))
- {
- $timestampActiveTraffic = time() - $this->firstRunActiveWebsitesWithTraffic;
- $this->log("--force-all-periods was detected: we will process websites with visits in the last "
- . Piwik::getPrettyTimeFromSeconds($this->firstRunActiveWebsitesWithTraffic, true, false)
- );
- }
- $this->websites = Piwik_SitesManager_API::getInstance()->getSitesIdWithVisits( $timestampActiveTraffic );
- $websiteIds = !empty($this->websites) ? ", IDs: ".implode(", ", $this->websites) : "";
- $prettySeconds = Piwik::getPrettyTimeFromSeconds( empty($this->timeLastCompleted)
- ? $this->firstRunActiveWebsitesWithTraffic
- : (time() - $this->timeLastCompleted),
- true, false);
- $this->log("Will process ". count($this->websites). " websites with new visits since "
- . $prettySeconds
- . " "
- . $websiteIds);
-
- // 2) All websites that had reports in the past invalidated recently
- // eg. when using Python log import script
- $this->idSitesInvalidatedOldReports = Piwik_CoreAdminHome_API::getWebsiteIdsToInvalidate();
- $this->idSitesInvalidatedOldReports = array_intersect($this->idSitesInvalidatedOldReports, $this->allWebsites);
-
- if(count($this->idSitesInvalidatedOldReports) > 0)
- {
- $websiteIds = ", IDs: ".implode(", ", $this->idSitesInvalidatedOldReports);
- $this->log("Will process ". count($this->idSitesInvalidatedOldReports). " other websites because some old data reports have been invalidated (eg. using the Log Import script) " . $websiteIds);
- $this->websites = array_merge($this->websites, $this->idSitesInvalidatedOldReports);
- }
-
- // 3) Also process all other websites which days have finished since the last run.
- // This ensures we process the previous day/week/month/year that just finished, even if there was no new visit
- $uniqueTimezones = Piwik_SitesManager_API::getInstance()->getUniqueSiteTimezones();
- $timezoneToProcess = array();
- foreach($uniqueTimezones as &$timezone)
- {
- $processedDateInTz = Piwik_Date::factory((int)$timestampActiveTraffic, $timezone);
- $currentDateInTz = Piwik_Date::factory('now', $timezone);
-
- if($processedDateInTz->toString() != $currentDateInTz->toString() )
- {
- $timezoneToProcess[] = $timezone;
- }
- }
-
- $websiteDayHasFinishedSinceLastRun = Piwik_SitesManager_API::getInstance()->getSitesIdFromTimezones($timezoneToProcess);
- $websiteDayHasFinishedSinceLastRun = array_diff($websiteDayHasFinishedSinceLastRun, $this->websites);
- $this->websiteDayHasFinishedSinceLastRun = $websiteDayHasFinishedSinceLastRun;
- if(count($websiteDayHasFinishedSinceLastRun) > 0)
- {
- $websiteIds = !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) " . $websiteIds);
-
- $this->websites = array_merge($this->websites, $websiteDayHasFinishedSinceLastRun);
- }
- }
- }
-
- protected function initTokenAuth()
- {
- $login = Piwik_Config::getInstance()->superuser['login'];
- $md5Password = Piwik_Config::getInstance()->superuser['password'];
- $this->token_auth = md5( $login . $md5Password ) ;
- $this->login = $login;
- }
-
- private function initPiwikHost()
- {
- // If archive.php run as a web cron, we use the current hostname
- if(!Piwik_Common::isPhpCliMode())
- {
- // example.org/piwik/misc/cron/
- $piwikUrl = Piwik_Common::sanitizeInputValue(Piwik_Url::getCurrentUrlWithoutFileName());
- // example.org/piwik/
- $piwikUrl = $piwikUrl . "../../";
- }
- // If archive.php run as CLI/shell we require the piwik url to be set
- else
- {
- $piwikUrl = $this->isParameterSet("url", true);
- if(!$piwikUrl
- || !Piwik_Common::isLookLikeUrl($piwikUrl))
- {
- $this->logFatalError("archive.php expects the argument --url to be set to your Piwik URL, for example: --url=http://example.org/piwik/ ", $backtrace = false);
- }
- // ensure there is a trailing slash
- if($piwikUrl[strlen($piwikUrl)-1] != '/')
- {
- $piwikUrl .= '/';
- }
- }
- if(Piwik_Config::getInstance()->General['force_ssl'] == 1)
- {
- $piwikUrl = str_replace('http://', 'https://', $piwikUrl);
- }
- $this->piwikUrl = $piwikUrl . "index.php";
- }
+ }
+ }
+ }
+
+ /**
+ * Init Piwik, connect DB, create log & config objects, etc.
+ */
+ private function initCore()
+ {
+ try {
+ Piwik_FrontController::getInstance()->init();
+ } catch (Exception $e) {
+ echo "ERROR: During Piwik init, Message: " . $e->getMessage();
+ exit;
+ }
+ }
+
+ private function displayHelp()
+ {
+ $displayHelp = $this->isParameterSet('help') || $this->isParameterSet('h');
+ if ($displayHelp) {
+ $this->usage();
+ exit;
+ }
+ }
+
+ protected function initStateFromParameters()
+ {
+ // Detect parameters
+ $reset = $this->isParameterSet("force-all-periods", $valuePossible = true);
+ $forceAll = $this->isParameterSet("force-all-websites");
+ $forceTimeoutPeriod = $this->isParameterSet("force-timeout-for-periods", $valuePossible = true);
+ if (!empty($forceTimeoutPeriod)
+ && $forceTimeoutPeriod !== true
+ ) // in case --force-timeout-for-periods= without [seconds] specified
+ {
+ // Ensure the cache for periods is at least as high as cache for today
+ $todayTTL = Piwik_ArchiveProcessing::getTodayArchiveTimeToLive();
+ if ($forceTimeoutPeriod < $todayTTL) {
+ $this->log("WARNING: Automatically increasing --force-timeout-for-periods from $forceTimeoutPeriod to "
+ . $todayTTL
+ . " to match the cache timeout for Today's report specified in Piwik UI > Settings > General Settings");
+ $forceTimeoutPeriod = $todayTTL;
+ }
+ $this->processPeriodsMaximumEverySeconds = $forceTimeoutPeriod;
+ }
+
+ // Recommend to disable browser archiving when using this script
+ if (Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled()) {
+ $this->log("NOTE: if you execute this script at least once per hour (or more often) in a crontab, you may disable 'Browser trigger archiving' in Piwik UI > Settings > General Settings. ");
+ $this->log(" see doc at: http://piwik.org/docs/setup-auto-archiving/");
+ }
+
+ if ($reset) {
+ $this->log("--force-all-periods was detected: the script will run as if it was its first run, and will trigger archiving for all periods.");
+ $this->shouldResetState = true;
+
+ if (!$forceAll
+ && is_numeric($reset)
+ && $reset > 0
+ ) {
+ $this->firstRunActiveWebsitesWithTraffic = (int)$reset;
+ }
+ }
+
+ if ($forceAll) {
+ $this->log("--force-all-websites was detected: the script will archive all websites and all periods sequentially");
+ $this->shouldArchiveAllWebsites = true;
+ }
+
+ $this->timeLastCompleted = Piwik_GetOption(self::OPTION_ARCHIVING_FINISHED_TS);
+ if ($this->shouldResetState) {
+ $this->timeLastCompleted = false;
+ }
+ }
+
+ // Fetching websites to process
+ protected function initWebsitesToProcess()
+ {
+ $this->allWebsites = Piwik_SitesManager_API::getInstance()->getAllSitesId();
+
+ if ($this->shouldArchiveAllWebsites) {
+ $this->websites = $this->allWebsites;
+ $this->log("Will process " . count($this->websites) . " websites");
+ } else {
+ // 1) All websites with visits since the last archive.php execution
+ $timestampActiveTraffic = $this->timeLastCompleted;
+ if (empty($timestampActiveTraffic)) {
+ $timestampActiveTraffic = time() - $this->firstRunActiveWebsitesWithTraffic;
+ $this->log("--force-all-periods was detected: we will process websites with visits in the last "
+ . Piwik::getPrettyTimeFromSeconds($this->firstRunActiveWebsitesWithTraffic, true, false)
+ );
+ }
+ $this->websites = Piwik_SitesManager_API::getInstance()->getSitesIdWithVisits($timestampActiveTraffic);
+ $websiteIds = !empty($this->websites) ? ", IDs: " . implode(", ", $this->websites) : "";
+ $prettySeconds = Piwik::getPrettyTimeFromSeconds(empty($this->timeLastCompleted)
+ ? $this->firstRunActiveWebsitesWithTraffic
+ : (time() - $this->timeLastCompleted),
+ true, false);
+ $this->log("Will process " . count($this->websites) . " websites with new visits since "
+ . $prettySeconds
+ . " "
+ . $websiteIds);
+
+ // 2) All websites that had reports in the past invalidated recently
+ // eg. when using Python log import script
+ $this->idSitesInvalidatedOldReports = Piwik_CoreAdminHome_API::getWebsiteIdsToInvalidate();
+ $this->idSitesInvalidatedOldReports = array_intersect($this->idSitesInvalidatedOldReports, $this->allWebsites);
+
+ if (count($this->idSitesInvalidatedOldReports) > 0) {
+ $websiteIds = ", IDs: " . implode(", ", $this->idSitesInvalidatedOldReports);
+ $this->log("Will process " . count($this->idSitesInvalidatedOldReports) . " other websites because some old data reports have been invalidated (eg. using the Log Import script) " . $websiteIds);
+ $this->websites = array_merge($this->websites, $this->idSitesInvalidatedOldReports);
+ }
+
+ // 3) Also process all other websites which days have finished since the last run.
+ // This ensures we process the previous day/week/month/year that just finished, even if there was no new visit
+ $uniqueTimezones = Piwik_SitesManager_API::getInstance()->getUniqueSiteTimezones();
+ $timezoneToProcess = array();
+ foreach ($uniqueTimezones as &$timezone) {
+ $processedDateInTz = Piwik_Date::factory((int)$timestampActiveTraffic, $timezone);
+ $currentDateInTz = Piwik_Date::factory('now', $timezone);
+
+ if ($processedDateInTz->toString() != $currentDateInTz->toString()) {
+ $timezoneToProcess[] = $timezone;
+ }
+ }
+
+ $websiteDayHasFinishedSinceLastRun = Piwik_SitesManager_API::getInstance()->getSitesIdFromTimezones($timezoneToProcess);
+ $websiteDayHasFinishedSinceLastRun = array_diff($websiteDayHasFinishedSinceLastRun, $this->websites);
+ $this->websiteDayHasFinishedSinceLastRun = $websiteDayHasFinishedSinceLastRun;
+ if (count($websiteDayHasFinishedSinceLastRun) > 0) {
+ $websiteIds = !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) " . $websiteIds);
+
+ $this->websites = array_merge($this->websites, $websiteDayHasFinishedSinceLastRun);
+ }
+ }
+ }
+
+ protected function initTokenAuth()
+ {
+ $login = Piwik_Config::getInstance()->superuser['login'];
+ $md5Password = Piwik_Config::getInstance()->superuser['password'];
+ $this->token_auth = md5($login . $md5Password);
+ $this->login = $login;
+ }
+
+ private function initPiwikHost()
+ {
+ // If archive.php run as a web cron, we use the current hostname
+ if (!Piwik_Common::isPhpCliMode()) {
+ // example.org/piwik/misc/cron/
+ $piwikUrl = Piwik_Common::sanitizeInputValue(Piwik_Url::getCurrentUrlWithoutFileName());
+ // example.org/piwik/
+ $piwikUrl = $piwikUrl . "../../";
+ } // If archive.php run as CLI/shell we require the piwik url to be set
+ else {
+ $piwikUrl = $this->isParameterSet("url", true);
+ if (!$piwikUrl
+ || !Piwik_Common::isLookLikeUrl($piwikUrl)
+ ) {
+ $this->logFatalError("archive.php expects the argument --url to be set to your Piwik URL, for example: --url=http://example.org/piwik/ ", $backtrace = false);
+ }
+ // ensure there is a trailing slash
+ if ($piwikUrl[strlen($piwikUrl) - 1] != '/') {
+ $piwikUrl .= '/';
+ }
+ }
+ if (Piwik_Config::getInstance()->General['force_ssl'] == 1) {
+ $piwikUrl = str_replace('http://', 'https://', $piwikUrl);
+ }
+ $this->piwikUrl = $piwikUrl . "index.php";
+ }
/**
@@ -847,40 +797,33 @@ class Archiving
* @param bool $valuePossible
* @return true or the value (int,string) if set, false otherwise
*/
- private function isParameterSet($parameter, $valuePossible = false)
- {
- if(!Piwik_Common::isPhpCliMode())
- {
- return false;
- }
- $parameters = array(
- "--$parameter",
- "-$parameter",
- $parameter
- );
- foreach($parameters as $parameter)
- {
- foreach($_SERVER['argv'] as $arg)
- {
- if( strpos($arg, $parameter) === 0)
- {
- if($valuePossible)
- {
- $parameterFound = $arg;
- if(($posEqual = strpos($parameterFound, '=')) !== false)
- {
- $return = substr($parameterFound, $posEqual+1);
- if($return !== false)
- {
- return $return;
- }
- }
- }
- return true;
- }
- }
- }
- return false;
- }
-
+ private function isParameterSet($parameter, $valuePossible = false)
+ {
+ if (!Piwik_Common::isPhpCliMode()) {
+ return false;
+ }
+ $parameters = array(
+ "--$parameter",
+ "-$parameter",
+ $parameter
+ );
+ foreach ($parameters as $parameter) {
+ foreach ($_SERVER['argv'] as $arg) {
+ if (strpos($arg, $parameter) === 0) {
+ if ($valuePossible) {
+ $parameterFound = $arg;
+ if (($posEqual = strpos($parameterFound, '=')) !== false) {
+ $return = substr($parameterFound, $posEqual + 1);
+ if ($return !== false) {
+ return $return;
+ }
+ }
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
}
diff --git a/misc/others/ExamplePiwikTracker.php b/misc/others/ExamplePiwikTracker.php
index 036831289d..f787a0847a 100644
--- a/misc/others/ExamplePiwikTracker.php
+++ b/misc/others/ExamplePiwikTracker.php
@@ -4,7 +4,7 @@
require_once '../../libs/PiwikTracker/PiwikTracker.php';
PiwikTracker::$URL = 'http://localhost/trunk/';
-$piwikTracker = new PiwikTracker( $idSite = 1 );
+$piwikTracker = new PiwikTracker($idSite = 1);
// You can manually set the Visitor details (resolution, time, plugins)
// See all other ->set* functions available in the PiwikTracker class
$piwikTracker->setResolution(1600, 1400);
diff --git a/misc/others/api_rest_call.php b/misc/others/api_rest_call.php
index f7a7bfe995..d286e92f49 100644
--- a/misc/others/api_rest_call.php
+++ b/misc/others/api_rest_call.php
@@ -16,17 +16,15 @@ $fetched = file_get_contents($url);
$content = unserialize($fetched);
// case error
-if(!$content)
-{
- print("Error, content fetched = ".$fetched);
+if (!$content) {
+ print("Error, content fetched = " . $fetched);
}
print("<h1>Keywords for the last month</h1>");
-foreach($content as $row)
-{
- $keyword = htmlspecialchars(html_entity_decode(urldecode($row['label']), ENT_QUOTES), ENT_QUOTES);
- $hits = $row['nb_visits'];
-
- print("<b>$keyword</b> ($hits hits)<br>");
+foreach ($content as $row) {
+ $keyword = htmlspecialchars(html_entity_decode(urldecode($row['label']), ENT_QUOTES), ENT_QUOTES);
+ $hits = $row['nb_visits'];
+
+ print("<b>$keyword</b> ($hits hits)<br>");
}
diff --git a/misc/others/geoipUpdateRows.php b/misc/others/geoipUpdateRows.php
index 9028651ac2..37c841947b 100755
--- a/misc/others/geoipUpdateRows.php
+++ b/misc/others/geoipUpdateRows.php
@@ -1,19 +1,16 @@
<?php
ini_set("memory_limit", "512M");
-error_reporting(E_ALL|E_NOTICE);
+error_reporting(E_ALL | E_NOTICE);
-define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__)=='/'?'':dirname(__FILE__) .'/../..');
-if(file_exists(PIWIK_DOCUMENT_ROOT . '/bootstrap.php'))
-{
- require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php';
+define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__) == '/' ? '' : dirname(__FILE__) . '/../..');
+if (file_exists(PIWIK_DOCUMENT_ROOT . '/bootstrap.php')) {
+ require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php';
}
-if(!defined('PIWIK_USER_PATH'))
-{
- define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT);
+if (!defined('PIWIK_USER_PATH')) {
+ define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT);
}
-if(!defined('PIWIK_INCLUDE_PATH'))
-{
- define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT);
+if (!defined('PIWIK_INCLUDE_PATH')) {
+ define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT);
}
ignore_user_abort(true);
@@ -30,93 +27,85 @@ define('PIWIK_ENABLE_DISPATCH', false);
Piwik_Config::getInstance()->log['logger_message'][] = 'screen';
Piwik_FrontController::getInstance()->init();
-$query = "SELECT count(*) FROM ".Piwik_Common::prefixTable('log_visit');
+$query = "SELECT count(*) FROM " . Piwik_Common::prefixTable('log_visit');
$count = Piwik_FetchOne($query);
// when script run via browser, check for Super User & output html page to do conversion via AJAX
-if (!Piwik_Common::isPhpCliMode())
-{
- try {
- Piwik::checkUserIsSuperUser();
- } catch(Exception $e) {
- Piwik::log('[error] You must be logged in as Super User to run this script. Please login in to Piwik and refresh this page.');
- exit;
- }
- // the 'start' query param will be supplied by the AJAX requests, so if it's not there, the
- // user is viewing the page in the browser.
- if (Piwik_Common::getRequestVar('start', false) === false)
- {
- // output HTML page that runs update via AJAX
- ?>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
-<head>
- <script type="text/javascript" src="../../libs/jquery/jquery.js"></script>
- <script type="text/javascript">
- (function($){
- var count = <?php echo $count; ?>;
- var doIteration = function(start)
- {
- if (start >= count)
- {
- return;
- }
-
- var end = Math.min(start + 100, count);
- $.ajax({
- type: 'POST',
- url: 'geoipUpdateRows.php',
- data: {
- start: start,
- end: end
- },
- async: true,
- error: function(xhr, status, error) {
- $('body')
- .append(xhr.responseText)
- .append('<div style="color:red"><strong>An error occured!</strong></div>');
- },
- success: function(response) {
- doIteration(end);
- $('body').append(response);
- var body = $('body')[0];
- body.scrollTop = body.scrollHeight;
- }
- });
- };
-
- doIteration(0);
- }(jQuery));
- </script>
-</head>
-<body>
-</body>
-</html>
- <?php
- exit;
- }
- else
- {
- $start = Piwik_Common::getRequestVar('start', 0, 'int');
- $end = min($count, Piwik_Common::getRequestVar('end', $count, 'int'));
- $limit = $end - $start;
- }
-}
-else // command line
+if (!Piwik_Common::isPhpCliMode()) {
+ try {
+ Piwik::checkUserIsSuperUser();
+ } catch (Exception $e) {
+ Piwik::log('[error] You must be logged in as Super User to run this script. Please login in to Piwik and refresh this page.');
+ exit;
+ }
+ // the 'start' query param will be supplied by the AJAX requests, so if it's not there, the
+ // user is viewing the page in the browser.
+ if (Piwik_Common::getRequestVar('start', false) === false) {
+ // output HTML page that runs update via AJAX
+ ?>
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+ <html>
+ <head>
+ <script type="text/javascript" src="../../libs/jquery/jquery.js"></script>
+ <script type="text/javascript">
+ (function ($) {
+ var count = <?php echo $count; ?>;
+ var doIteration = function (start) {
+ if (start >= count) {
+ return;
+ }
+
+ var end = Math.min(start + 100, count);
+ $.ajax({
+ type: 'POST',
+ url: 'geoipUpdateRows.php',
+ data: {
+ start: start,
+ end: end
+ },
+ async: true,
+ error: function (xhr, status, error) {
+ $('body')
+ .append(xhr.responseText)
+ .append('<div style="color:red"><strong>An error occured!</strong></div>');
+ },
+ success: function (response) {
+ doIteration(end);
+ $('body').append(response);
+ var body = $('body')[0];
+ body.scrollTop = body.scrollHeight;
+ }
+ });
+ };
+
+ doIteration(0);
+ }(jQuery));
+ </script>
+ </head>
+ <body>
+ </body>
+ </html>
+ <?php
+ exit;
+ } else {
+ $start = Piwik_Common::getRequestVar('start', 0, 'int');
+ $end = min($count, Piwik_Common::getRequestVar('end', $count, 'int'));
+ $limit = $end - $start;
+ }
+} else // command line
{
- $start = 0;
- $end = $count;
- $limit = 1000;
+ $start = 0;
+ $end = $count;
+ $limit = 1000;
}
function geoipUpdateError($message)
{
- Piwik::log($message);
- if (!Piwik_Common::isPhpCliMode())
- {
- @header('HTTP/1.1 500 Internal Server Error', $replace = true, $responseCode = 500);
- }
- exit;
+ Piwik::log($message);
+ if (!Piwik_Common::isPhpCliMode()) {
+ @header('HTTP/1.1 500 Internal Server Error', $replace = true, $responseCode = 500);
+ }
+ exit;
}
// only display notes if on command line (where start will == 0 for that part of script) or on
@@ -125,162 +114,134 @@ $displayNotes = $start == 0;
// try getting the pecl location provider
$provider = new Piwik_UserCountry_LocationProvider_GeoIp_Pecl();
-if (!$provider->isAvailable())
-{
- if ($displayNotes)
- {
- Piwik::log("[note] The GeoIP PECL extension is not installed.");
- }
-
- $provider = null;
-}
-else
-{
- $workingOrError = $provider->isWorking();
- if ($workingOrError !== true)
- {
- if ($displayNotes)
- {
- Piwik::log("[note] The GeoIP PECL extension is broken: $workingOrError");
- }
- if (Piwik_Common::isPhpCliMode())
- {
- Piwik::log("[note] Make sure your command line PHP is configured to use the PECL extension.");
- }
- $provider = null;
- }
+if (!$provider->isAvailable()) {
+ if ($displayNotes) {
+ Piwik::log("[note] The GeoIP PECL extension is not installed.");
+ }
+
+ $provider = null;
+} else {
+ $workingOrError = $provider->isWorking();
+ if ($workingOrError !== true) {
+ if ($displayNotes) {
+ Piwik::log("[note] The GeoIP PECL extension is broken: $workingOrError");
+ }
+ if (Piwik_Common::isPhpCliMode()) {
+ Piwik::log("[note] Make sure your command line PHP is configured to use the PECL extension.");
+ }
+ $provider = null;
+ }
}
// use php api if pecl extension cannot be used
-if (is_null($provider))
-{
- if ($displayNotes)
- {
- Piwik::log("[note] Falling back to PHP API. This may become too slow for you. If so, you can read this link on how to install the PECL extension: http://piwik.org/faq/how-to/#faq_164");
- }
-
- $provider = new Piwik_UserCountry_LocationProvider_GeoIp_Php();
- if (!$provider->isAvailable())
- {
- if ($displayNotes)
- {
- Piwik::log("[note] The GeoIP PHP API is not available. This means you do not have a GeoIP location database in your ./misc directory. The database must be named either GeoIP.dat or GeoIPCity.dat based on the type of database it is.");
- }
- $provider = null;
- }
- else
- {
- $workingOrError = $provider->isWorking();
- if ($workingOrError !== true)
- {
- if ($displayNotes)
- {
- Piwik::log("[note] The GeoIP PHP API is broken: $workingOrError");
- }
- $provider = null;
- }
- }
+if (is_null($provider)) {
+ if ($displayNotes) {
+ Piwik::log("[note] Falling back to PHP API. This may become too slow for you. If so, you can read this link on how to install the PECL extension: http://piwik.org/faq/how-to/#faq_164");
+ }
+
+ $provider = new Piwik_UserCountry_LocationProvider_GeoIp_Php();
+ if (!$provider->isAvailable()) {
+ if ($displayNotes) {
+ Piwik::log("[note] The GeoIP PHP API is not available. This means you do not have a GeoIP location database in your ./misc directory. The database must be named either GeoIP.dat or GeoIPCity.dat based on the type of database it is.");
+ }
+ $provider = null;
+ } else {
+ $workingOrError = $provider->isWorking();
+ if ($workingOrError !== true) {
+ if ($displayNotes) {
+ Piwik::log("[note] The GeoIP PHP API is broken: $workingOrError");
+ }
+ $provider = null;
+ }
+ }
}
-if (is_null($provider))
-{
- geoipUpdateError("\n[error] There is no location provider that can be used with this script. Only the GeoIP PECL module or the GeoIP PHP API can be used at present. Please install and configure one of these first.");
+if (is_null($provider)) {
+ geoipUpdateError("\n[error] There is no location provider that can be used with this script. Only the GeoIP PECL module or the GeoIP PHP API can be used at present. Please install and configure one of these first.");
}
$info = $provider->getInfo();
-if ($displayNotes)
-{
- Piwik::log("[note] Found working provider: {$info['id']}");
+if ($displayNotes) {
+ Piwik::log("[note] Found working provider: {$info['id']}");
}
// perform update
$logVisitFieldsToUpdate = array('location_country' => Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY,
- 'location_region' => Piwik_UserCountry_LocationProvider::REGION_CODE_KEY,
- 'location_city' => Piwik_UserCountry_LocationProvider::CITY_NAME_KEY,
- 'location_latitude' => Piwik_UserCountry_LocationProvider::LATITUDE_KEY,
- 'location_longitude' => Piwik_UserCountry_LocationProvider::LONGITUDE_KEY);
-
-if ($displayNotes)
-{
- Piwik::log("\n$count rows to process in ".Piwik_Common::prefixTable('log_visit')
- . " and ".Piwik_Common::prefixTable('log_conversion')."...");
+ 'location_region' => Piwik_UserCountry_LocationProvider::REGION_CODE_KEY,
+ 'location_city' => Piwik_UserCountry_LocationProvider::CITY_NAME_KEY,
+ 'location_latitude' => Piwik_UserCountry_LocationProvider::LATITUDE_KEY,
+ 'location_longitude' => Piwik_UserCountry_LocationProvider::LONGITUDE_KEY);
+
+if ($displayNotes) {
+ Piwik::log("\n$count rows to process in " . Piwik_Common::prefixTable('log_visit')
+ . " and " . Piwik_Common::prefixTable('log_conversion') . "...");
}
flush();
-for (; $start < $end; $start += $limit)
-{
- $rows = Piwik_FetchAll("SELECT idvisit, location_ip, ".implode(',', array_keys($logVisitFieldsToUpdate))."
- FROM ".Piwik_Common::prefixTable('log_visit')."
+for (; $start < $end; $start += $limit) {
+ $rows = Piwik_FetchAll("SELECT idvisit, location_ip, " . implode(',', array_keys($logVisitFieldsToUpdate)) . "
+ FROM " . Piwik_Common::prefixTable('log_visit') . "
LIMIT $start, $limit");
- if(!count($rows))
- {
- continue;
- }
-
- foreach ( $rows as $i => $row )
- {
- $fieldsToSet = array();
- foreach ($logVisitFieldsToUpdate as $field => $ignore)
- {
- if (empty($fieldsToSet[$field]))
- {
- $fieldsToSet[] = $field;
- }
- }
-
- // skip if it already has a location
- if (empty($fieldsToSet))
- {
- continue;
- }
-
- $ip = Piwik_IP::N2P($row['location_ip']);
- $location = $provider->getLocation(array('ip' => $ip));
-
- if (!empty($location[Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY]))
- {
- $location[Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY] =
- strtolower($location[Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY]);
- }
- $row['location_country'] = strtolower($row['location_country']);
-
- $columnsToSet = array();
- $bind = array();
- foreach ($logVisitFieldsToUpdate as $column => $locationKey)
- {
- if (!empty($location[$locationKey])
- && $location[$locationKey] != $row[$column])
- {
- $columnsToSet[] = $column.' = ?';
- $bind[] = $location[$locationKey];
- }
- }
-
- if (empty($columnsToSet))
- {
- continue;
- }
-
- $bind[] = $row['idvisit'];
-
- // update log_visit
- $sql = "UPDATE ".Piwik_Common::prefixTable('log_visit')."
- SET ".implode(', ', $columnsToSet)."
+ if (!count($rows)) {
+ continue;
+ }
+
+ foreach ($rows as $i => $row) {
+ $fieldsToSet = array();
+ foreach ($logVisitFieldsToUpdate as $field => $ignore) {
+ if (empty($fieldsToSet[$field])) {
+ $fieldsToSet[] = $field;
+ }
+ }
+
+ // skip if it already has a location
+ if (empty($fieldsToSet)) {
+ continue;
+ }
+
+ $ip = Piwik_IP::N2P($row['location_ip']);
+ $location = $provider->getLocation(array('ip' => $ip));
+
+ if (!empty($location[Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY])) {
+ $location[Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY] =
+ strtolower($location[Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY]);
+ }
+ $row['location_country'] = strtolower($row['location_country']);
+
+ $columnsToSet = array();
+ $bind = array();
+ foreach ($logVisitFieldsToUpdate as $column => $locationKey) {
+ if (!empty($location[$locationKey])
+ && $location[$locationKey] != $row[$column]
+ ) {
+ $columnsToSet[] = $column . ' = ?';
+ $bind[] = $location[$locationKey];
+ }
+ }
+
+ if (empty($columnsToSet)) {
+ continue;
+ }
+
+ $bind[] = $row['idvisit'];
+
+ // update log_visit
+ $sql = "UPDATE " . Piwik_Common::prefixTable('log_visit') . "
+ SET " . implode(', ', $columnsToSet) . "
WHERE idvisit = ?";
- Piwik_Query($sql, $bind);
-
- // update log_conversion
- $sql = "UPDATE ".Piwik_Common::prefixTable('log_conversion')."
- SET ".implode(', ', $columnsToSet)."
+ Piwik_Query($sql, $bind);
+
+ // update log_conversion
+ $sql = "UPDATE " . Piwik_Common::prefixTable('log_conversion') . "
+ SET " . implode(', ', $columnsToSet) . "
WHERE idvisit = ?";
- Piwik_Query($sql, $bind);
- }
- Piwik::log(round($start * 100 / $count) . "% done...");
- flush();
+ Piwik_Query($sql, $bind);
+ }
+ Piwik::log(round($start * 100 / $count) . "% done...");
+ flush();
}
-if ($start >= $count)
-{
- Piwik::log("100% done!");
- Piwik::log("");
- Piwik::log("[note] Now that you've geolocated your old visits, you need to force your reports to be re-processed. See this FAQ entry: http://piwik.org/faq/how-to/#faq_59");
+if ($start >= $count) {
+ Piwik::log("100% done!");
+ Piwik::log("");
+ Piwik::log("[note] Now that you've geolocated your old visits, you need to force your reports to be re-processed. See this FAQ entry: http://piwik.org/faq/how-to/#faq_59");
}
diff --git a/misc/others/iframeWidget.htm b/misc/others/iframeWidget.htm
index f26a8ba463..9b7c4261f1 100644
--- a/misc/others/iframeWidget.htm
+++ b/misc/others/iframeWidget.htm
@@ -2,6 +2,12 @@
<body>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'></script>
<h3 style="color:#143974">Embedding the Piwik Country widget in an Iframe</h3>
-<div id="widgetIframe"><iframe width="500" height="350" src="http://demo.piwik.org/index.php?module=Widgetize&action=iframe&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry&idSite=1&period=month&date=2010-08-31&disableLink=1&token_auth=960d9a24b89ba4feb99be754c5aac15bx" scrolling="no" frameborder="0" marginheight="0" marginwidth="0"></iframe></div>
-</body></html>
+<div id="widgetIframe">
+ <iframe width="500" height="350"
+ src="http://demo.piwik.org/index.php?module=Widgetize&action=iframe&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry&idSite=1&period=month&date=2010-08-31&disableLink=1&token_auth=960d9a24b89ba4feb99be754c5aac15bx"
+ scrolling="no" frameborder="0" marginheight="0" marginwidth="0"></iframe>
+</div>
+
+</body>
+</html>
diff --git a/misc/others/iframeWidget_localhost.php b/misc/others/iframeWidget_localhost.php
index ad105a7c9c..d01ba89227 100644
--- a/misc/others/iframeWidget_localhost.php
+++ b/misc/others/iframeWidget_localhost.php
@@ -9,9 +9,13 @@ $url = "http://localhost/trunk/index.php?token_auth=0b809661490d605bfd77f57ed11f
<html>
<body>
<h3 style="color:#143974">Embedding the Piwik Country widget in an Iframe</h3>
-<p>Loads a widget from localhost/trunk/ with login=root, pwd=test. <a href='<?=$url?>'>Widget URL</a></p>
-<div id="widgetIframe"><iframe width="500" height="350"
-src="<?php echo $url; ?>" scrolling="no" frameborder="0" marginheight="0" marginwidth="0"></iframe></div>
+
+<p>Loads a widget from localhost/trunk/ with login=root, pwd=test. <a href='<?= $url ?>'>Widget URL</a></p>
+
+<div id="widgetIframe">
+ <iframe width="500" height="350"
+ src="<?php echo $url; ?>" scrolling="no" frameborder="0" marginheight="0" marginwidth="0"></iframe>
+</div>
<br/>
@@ -26,32 +30,29 @@ require_once PIWIK_INCLUDE_PATH . "/core/API/Request.php";
Piwik_FrontController::getInstance()->init();
$widgets = Piwik_GetWidgetsList();
-foreach($widgets as $category => $widgetsInCategory)
-{
- echo '<h2>'.$category . '</h2>';
- foreach($widgetsInCategory as $widget)
- {
- echo '<h3>'.$widget['name'].'</h3>';
- $widgetUrl = Piwik_Common::getArrayFromQueryString($url);
- $widgetUrl['moduleToWidgetize'] = $widget['parameters']['module'];
- $widgetUrl['actionToWidgetize'] = $widget['parameters']['action'];
- $parameters = $widget['parameters'];
- unset($parameters['module']);
- unset($parameters['action']);
- foreach($parameters as $name => $value)
- {
- if(is_array($value))
- {
- $value = current($value);
- }
- $widgetUrl[$name] = $value;
- }
- $widgetUrl = Piwik_Url::getQueryStringFromParameters($widgetUrl);
-
- echo '<div id="widgetIframe"><iframe width="500" height="350"
- src="'.$widgetUrl.'" scrolling="no" frameborder="0" marginheight="0" marginwidth="0"></iframe></div>';
-
- }
+foreach ($widgets as $category => $widgetsInCategory) {
+ echo '<h2>' . $category . '</h2>';
+ foreach ($widgetsInCategory as $widget) {
+ echo '<h3>' . $widget['name'] . '</h3>';
+ $widgetUrl = Piwik_Common::getArrayFromQueryString($url);
+ $widgetUrl['moduleToWidgetize'] = $widget['parameters']['module'];
+ $widgetUrl['actionToWidgetize'] = $widget['parameters']['action'];
+ $parameters = $widget['parameters'];
+ unset($parameters['module']);
+ unset($parameters['action']);
+ foreach ($parameters as $name => $value) {
+ if (is_array($value)) {
+ $value = current($value);
+ }
+ $widgetUrl[$name] = $value;
+ }
+ $widgetUrl = Piwik_Url::getQueryStringFromParameters($widgetUrl);
+
+ echo '<div id="widgetIframe"><iframe width="500" height="350"
+ src="' . $widgetUrl . '" scrolling="no" frameborder="0" marginheight="0" marginwidth="0"></iframe></div>';
+
+ }
}
?>
-</body></html>
+</body>
+</html>
diff --git a/misc/others/test_cookies_GenerateHundredsWebsitesAndVisits.php b/misc/others/test_cookies_GenerateHundredsWebsitesAndVisits.php
index 8ce69cdf5b..4f51391b96 100644
--- a/misc/others/test_cookies_GenerateHundredsWebsitesAndVisits.php
+++ b/misc/others/test_cookies_GenerateHundredsWebsitesAndVisits.php
@@ -14,10 +14,9 @@ require_once PIWIK_INCLUDE_PATH . "/libs/PiwikTracker/PiwikTracker.php";
Piwik_FrontController::getInstance()->init();
Piwik::setUserIsSuperUser();
$count = 100;
-for($i = 0; $i <= $count; $i++)
-{
- $id = Piwik_SitesManager_API::getInstance()->addSite(Piwik_Common::getRandomString(), 'http://piwik.org');
+for ($i = 0; $i <= $count; $i++) {
+ $id = Piwik_SitesManager_API::getInstance()->addSite(Piwik_Common::getRandomString(), 'http://piwik.org');
$t = new PiwikTracker($id, 'http://localhost/trunk/piwik.php');
- echo $id . " <img width=100 height=10 border=1 src='".$t->getUrlTrackPageView('title') ."'><br/>";
+ echo $id . " <img width=100 height=10 border=1 src='" . $t->getUrlTrackPageView('title') . "'><br/>";
}
diff --git a/misc/others/test_generateLotsVisitsWebsites.php b/misc/others/test_generateLotsVisitsWebsites.php
index e8819bb9c8..eeb5b22e5a 100644
--- a/misc/others/test_generateLotsVisitsWebsites.php
+++ b/misc/others/test_generateLotsVisitsWebsites.php
@@ -1,5 +1,5 @@
<?php
-define('PIWIK_INCLUDE_PATH', realpath( dirname(__FILE__)."/../.." ));
+define('PIWIK_INCLUDE_PATH', realpath(dirname(__FILE__) . "/../.."));
define('PIWIK_ENABLE_DISPATCH', false);
define('PIWIK_ENABLE_ERROR_HANDLER', false);
define('PIWIK_ENABLE_SESSION_START', false);
@@ -10,7 +10,9 @@ require_once PIWIK_INCLUDE_PATH . "/libs/PiwikTracker/PiwikTracker.php";
Piwik_FrontController::getInstance()->init();
// SECURITY: DO NOT DELETE THIS LINE!
-if(!Piwik_Common::isPhpCliMode()) { die("ERROR: Must be executed in CLI"); }
+if (!Piwik_Common::isPhpCliMode()) {
+ die("ERROR: Must be executed in CLI");
+}
$process = new Piwik_StressTests_CopyLogs;
$process->init();
@@ -19,118 +21,121 @@ $process->run();
class Piwik_StressTests_CopyLogs
{
- function init()
- {
- $config = Piwik_Config::getInstance();
- $config->log['log_only_when_debug_parameter'] = 0;
- $config->log['logger_message'] = array("logger_message" => "screen");
- Piwik::createLogObject();
- }
-
- function run()
- {
- // Copy all visits in date range into TODAY
+ function init()
+ {
+ $config = Piwik_Config::getInstance();
+ $config->log['log_only_when_debug_parameter'] = 0;
+ $config->log['logger_message'] = array("logger_message" => "screen");
+ Piwik::createLogObject();
+ }
+
+ function run()
+ {
+ // Copy all visits in date range into TODAY
$startDate = '2011-08-12';
$endDate = '2011-08-12';
-
+
$this->log("Starting...");
- $db = Zend_Registry::get('db');
-
- $initial = $this->getVisitsToday();
- $this->log(" Visits today so far: " . $initial);
- $initialActions = $this->getActionsToday();
- $this->log(" Actions today: " . $initialActions);
- $initialPurchasedItems = $this->getConversionItemsToday();
- $this->log(" Purchased items today: " . $initialPurchasedItems);
- $initialConversions = $this->getConversionsToday();
- $this->log(" Conversions today: " . $initialConversions);
-
- $this->log(" Now copying visits between '$startDate' and '$endDate'...");
- $sql = "INSERT INTO ". Piwik_Common::prefixTable('log_visit')." (`idsite`, `idvisitor`, `visitor_localtime`, `visitor_returning`, `visitor_count_visits`, `visit_first_action_time`, `visit_last_action_time`, `visit_exit_idaction_url`, `visit_exit_idaction_name`, `visit_entry_idaction_url`, `visit_entry_idaction_name`, `visit_total_actions`, `visit_total_time`, `visit_goal_converted`, `visit_goal_buyer`, `referer_type`, `referer_name`, `referer_url`, `referer_keyword`, `config_id`, `config_os`, `config_browser_name`, `config_browser_version`, `config_resolution`, `config_pdf`, `config_flash`, `config_java`, `config_director`, `config_quicktime`, `config_realplayer`, `config_windowsmedia`, `config_gears`, `config_silverlight`, `config_cookie`, `location_ip`, `location_browser_lang`, `location_country`, `location_provider`, `custom_var_k1`, `custom_var_v1`, `custom_var_k2`, `custom_var_v2`, `custom_var_k3`, `custom_var_v3`, `custom_var_k4`, `custom_var_v4`, `custom_var_k5`, `custom_var_v5`, `visitor_days_since_last`, `visitor_days_since_order`, `visitor_days_since_first`)
+ $db = Zend_Registry::get('db');
+
+ $initial = $this->getVisitsToday();
+ $this->log(" Visits today so far: " . $initial);
+ $initialActions = $this->getActionsToday();
+ $this->log(" Actions today: " . $initialActions);
+ $initialPurchasedItems = $this->getConversionItemsToday();
+ $this->log(" Purchased items today: " . $initialPurchasedItems);
+ $initialConversions = $this->getConversionsToday();
+ $this->log(" Conversions today: " . $initialConversions);
+
+ $this->log(" Now copying visits between '$startDate' and '$endDate'...");
+ $sql = "INSERT INTO " . Piwik_Common::prefixTable('log_visit') . " (`idsite`, `idvisitor`, `visitor_localtime`, `visitor_returning`, `visitor_count_visits`, `visit_first_action_time`, `visit_last_action_time`, `visit_exit_idaction_url`, `visit_exit_idaction_name`, `visit_entry_idaction_url`, `visit_entry_idaction_name`, `visit_total_actions`, `visit_total_time`, `visit_goal_converted`, `visit_goal_buyer`, `referer_type`, `referer_name`, `referer_url`, `referer_keyword`, `config_id`, `config_os`, `config_browser_name`, `config_browser_version`, `config_resolution`, `config_pdf`, `config_flash`, `config_java`, `config_director`, `config_quicktime`, `config_realplayer`, `config_windowsmedia`, `config_gears`, `config_silverlight`, `config_cookie`, `location_ip`, `location_browser_lang`, `location_country`, `location_provider`, `custom_var_k1`, `custom_var_v1`, `custom_var_k2`, `custom_var_v2`, `custom_var_k3`, `custom_var_v3`, `custom_var_k4`, `custom_var_v4`, `custom_var_k5`, `custom_var_v5`, `visitor_days_since_last`, `visitor_days_since_order`, `visitor_days_since_first`)
SELECT `idsite`, `idvisitor`, `visitor_localtime`, `visitor_returning`, `visitor_count_visits`, CONCAT(CURRENT_DATE() , \" \", FLOOR(RAND()*24) , \":\",FLOOR(RAND()*60),\":\",FLOOR(RAND()*60)), CONCAT(CURRENT_DATE() , \" \", FLOOR(RAND()*24) , \":\",FLOOR(RAND()*60),\":\",FLOOR(RAND()*60)), `visit_exit_idaction_url`, `visit_exit_idaction_name`, `visit_entry_idaction_url`, `visit_entry_idaction_name`, `visit_total_actions`, `visit_total_time`, `visit_goal_converted`, `visit_goal_buyer`, `referer_type`, `referer_name`, `referer_url`, `referer_keyword`, `config_id`, `config_os`, `config_browser_name`, `config_browser_version`, `config_resolution`, `config_pdf`, `config_flash`, `config_java`, `config_director`, `config_quicktime`, `config_realplayer`, `config_windowsmedia`, `config_gears`, `config_silverlight`, `config_cookie`, `location_ip`, `location_browser_lang`, `location_country`, `location_provider`, `custom_var_k1`, `custom_var_v1`, `custom_var_k2`, `custom_var_v2`, `custom_var_k3`, `custom_var_v3`, `custom_var_k4`, `custom_var_v4`, `custom_var_k5`, `custom_var_v5`, `visitor_days_since_last`, `visitor_days_since_order`, `visitor_days_since_first`
- FROM `". Piwik_Common::prefixTable('log_visit')."`
+ FROM `" . Piwik_Common::prefixTable('log_visit') . "`
WHERE idsite >= 1 AND date(visit_last_action_time) between '$startDate' and '$endDate' ;";
- $result = $db->query($sql);
-
- $this->log(" Copying actions...");
- $sql = "INSERT INTO ". Piwik_Common::prefixTable('log_link_visit_action')." (`idsite`, `idvisitor`, `server_time`, `idvisit`, `idaction_url`, `idaction_url_ref`, `idaction_name`, `idaction_name_ref`, `time_spent_ref_action`, `custom_var_k1`, `custom_var_v1`, `custom_var_k2`, `custom_var_v2`, `custom_var_k3`, `custom_var_v3`, `custom_var_k4`, `custom_var_v4`, `custom_var_k5`, `custom_var_v5`)
+ $result = $db->query($sql);
+
+ $this->log(" Copying actions...");
+ $sql = "INSERT INTO " . Piwik_Common::prefixTable('log_link_visit_action') . " (`idsite`, `idvisitor`, `server_time`, `idvisit`, `idaction_url`, `idaction_url_ref`, `idaction_name`, `idaction_name_ref`, `time_spent_ref_action`, `custom_var_k1`, `custom_var_v1`, `custom_var_k2`, `custom_var_v2`, `custom_var_k3`, `custom_var_v3`, `custom_var_k4`, `custom_var_v4`, `custom_var_k5`, `custom_var_v5`)
SELECT `idsite`, `idvisitor`, CONCAT(CURRENT_DATE() , \" \", FLOOR(RAND()*24) , \":\",FLOOR(RAND()*60),\":\",FLOOR(RAND()*60)), `idvisit`, `idaction_url`, `idaction_url_ref`, `idaction_name`, `idaction_name_ref`, `time_spent_ref_action`, `custom_var_k1`, `custom_var_v1`, `custom_var_k2`, `custom_var_v2`, `custom_var_k3`, `custom_var_v3`, `custom_var_k4`, `custom_var_v4`, `custom_var_k5`, `custom_var_v5`
- FROM `". Piwik_Common::prefixTable('log_link_visit_action')."`
+ FROM `" . Piwik_Common::prefixTable('log_link_visit_action') . "`
WHERE idsite >= 1 AND date(server_time) between '$startDate' and '$endDate'
;"; // LIMIT 1000000
- $result = $db->query($sql);
-
- $this->log(" Copying conversions...");
- $sql = "INSERT IGNORE INTO `". Piwik_Common::prefixTable('log_conversion')."` (`idvisit`, `idsite`, `visitor_days_since_first`, `visitor_days_since_order`, `visitor_count_visits`, `idvisitor`, `server_time`, `idaction_url`, `idlink_va`, `referer_visit_server_date`, `referer_type`, `referer_name`, `referer_keyword`, `visitor_returning`, `location_country`, `url`, `idgoal`, `revenue`, `buster`, `idorder`, `custom_var_k1`, `custom_var_v1`, `custom_var_k2`, `custom_var_v2`, `custom_var_k3`, `custom_var_v3`, `custom_var_k4`, `custom_var_v4`, `custom_var_k5`, `custom_var_v5`, `items`, `revenue_subtotal`, `revenue_tax`, `revenue_shipping`, `revenue_discount`)
+ $result = $db->query($sql);
+
+ $this->log(" Copying conversions...");
+ $sql = "INSERT IGNORE INTO `" . Piwik_Common::prefixTable('log_conversion') . "` (`idvisit`, `idsite`, `visitor_days_since_first`, `visitor_days_since_order`, `visitor_count_visits`, `idvisitor`, `server_time`, `idaction_url`, `idlink_va`, `referer_visit_server_date`, `referer_type`, `referer_name`, `referer_keyword`, `visitor_returning`, `location_country`, `url`, `idgoal`, `revenue`, `buster`, `idorder`, `custom_var_k1`, `custom_var_v1`, `custom_var_k2`, `custom_var_v2`, `custom_var_k3`, `custom_var_v3`, `custom_var_k4`, `custom_var_v4`, `custom_var_k5`, `custom_var_v5`, `items`, `revenue_subtotal`, `revenue_tax`, `revenue_shipping`, `revenue_discount`)
SELECT `idvisit`, `idsite`, `visitor_days_since_first`, `visitor_days_since_order`, `visitor_count_visits`, `idvisitor`, CONCAT(CURRENT_DATE() , \" \", FLOOR(RAND()*24) , \":\",FLOOR(RAND()*60),\":\",FLOOR(RAND()*60)), `idaction_url`, `idlink_va`, `referer_visit_server_date`, `referer_type`, `referer_name`, `referer_keyword`, `visitor_returning`, `location_country`, `url`, `idgoal`, `revenue`, FLOOR(`buster` * RAND()), CONCAT(`idorder`,SUBSTRING(MD5(RAND()) FROM 1 FOR 9)) , `custom_var_k1`, `custom_var_v1`, `custom_var_k2`, `custom_var_v2`, `custom_var_k3`, `custom_var_v3`, `custom_var_k4`, `custom_var_v4`, `custom_var_k5`, `custom_var_v5`, `items`, `revenue_subtotal`, `revenue_tax`, `revenue_shipping`, `revenue_discount`
- FROM `". Piwik_Common::prefixTable('log_conversion')."`
+ FROM `" . Piwik_Common::prefixTable('log_conversion') . "`
WHERE idsite >= 1 AND date(server_time) between '$startDate' and '$endDate' ;";
- $result = $db->query($sql);
-
- $this->log(" Copying purchased items...");
- $sql = "INSERT INTO `". Piwik_Common::prefixTable('log_conversion_item')."` (`idsite`, `idvisitor`, `server_time`, `idvisit`, `idorder`, `idaction_sku`, `idaction_name`, `idaction_category`, `price`, `quantity`, `deleted`)
+ $result = $db->query($sql);
+
+ $this->log(" Copying purchased items...");
+ $sql = "INSERT INTO `" . Piwik_Common::prefixTable('log_conversion_item') . "` (`idsite`, `idvisitor`, `server_time`, `idvisit`, `idorder`, `idaction_sku`, `idaction_name`, `idaction_category`, `price`, `quantity`, `deleted`)
SELECT `idsite`, `idvisitor`, CONCAT(CURRENT_DATE() , \" \", TIME(`server_time`)), `idvisit`, CONCAT(`idorder`,SUBSTRING(MD5(RAND()) FROM 1 FOR 9)) , `idaction_sku`, `idaction_name`, `idaction_category`, `price`, `quantity`, `deleted`
- FROM `". Piwik_Common::prefixTable('log_conversion_item')."`
+ FROM `" . Piwik_Common::prefixTable('log_conversion_item') . "`
WHERE idsite >= 1 AND date(server_time) between '$startDate' and '$endDate' ;";
- $result = $db->query($sql);
-
- $now = $this->getVisitsToday();
- $actions = $this->getActionsToday();
- $purchasedItems = $this->getConversionItemsToday();
- $conversions = $this->getConversionsToday();
-
- $this->log(" -------------------------------------");
- $this->log(" Today visits after import: " . $now);
- $this->log(" Actions: " . $actions);
- $this->log(" Purchased items: " . $purchasedItems);
- $this->log(" Conversions: " . $conversions);
- $this->log(" - New visits created: " . ($now-$initial));
- $this->log(" - Actions created: " . ($actions-$initialActions));
- $this->log(" - New conversions created: " . ($conversions-$initialConversions));
- $this->log(" - New purchased items created: " . ($purchasedItems-$initialPurchasedItems));
+ $result = $db->query($sql);
+
+ $now = $this->getVisitsToday();
+ $actions = $this->getActionsToday();
+ $purchasedItems = $this->getConversionItemsToday();
+ $conversions = $this->getConversionsToday();
+
+ $this->log(" -------------------------------------");
+ $this->log(" Today visits after import: " . $now);
+ $this->log(" Actions: " . $actions);
+ $this->log(" Purchased items: " . $purchasedItems);
+ $this->log(" Conversions: " . $conversions);
+ $this->log(" - New visits created: " . ($now - $initial));
+ $this->log(" - Actions created: " . ($actions - $initialActions));
+ $this->log(" - New conversions created: " . ($conversions - $initialConversions));
+ $this->log(" - New purchased items created: " . ($purchasedItems - $initialPurchasedItems));
$this->log("done");
- }
-
- function delete()
- {
- $this->log("Deleting logs for today...");
- $db = Zend_Registry::get('db');
- $sql = "DELETE FROM ". Piwik_Common::prefixTable('log_visit')."
+ }
+
+ function delete()
+ {
+ $this->log("Deleting logs for today...");
+ $db = Zend_Registry::get('db');
+ $sql = "DELETE FROM " . Piwik_Common::prefixTable('log_visit') . "
WHERE date(visit_last_action_time) = CURRENT_DATE();";
- $db->query($sql);
- foreach(array('log_link_visit_action', 'log_conversion', 'log_conversion_item') as $table)
- {
- $sql = "DELETE FROM ".Piwik_Common::prefixTable($table)."
+ $db->query($sql);
+ foreach (array('log_link_visit_action', 'log_conversion', 'log_conversion_item') as $table) {
+ $sql = "DELETE FROM " . Piwik_Common::prefixTable($table) . "
WHERE date(server_time) = CURRENT_DATE();";
- $db->query($sql);
- }
- $sql = "OPTIMIZE TABLE ". Piwik_Common::prefixTable('log_link_visit_action').", ". Piwik_Common::prefixTable('log_conversion').", ". Piwik_Common::prefixTable('log_conversion_item').", ". Piwik_Common::prefixTable('log_visit')."";
- $db->query($sql);
- $this->log("done");
- }
-
- function log($m)
- {
- Piwik::log($m);
- }
- function getVisitsToday()
- {
- $sql = "SELECT count(*) FROM `". Piwik_Common::prefixTable('log_visit')."` WHERE idsite >= 1 AND DATE(`visit_last_action_time`) = CURRENT_DATE;";
- return Zend_Registry::get('db')->fetchOne($sql);
- }
- function getConversionItemsToday($table = 'log_conversion_item')
- {
- $sql = "SELECT count(*) FROM `".Piwik_Common::prefixTable($table)."` WHERE idsite >= 1 AND DATE(`server_time`) = CURRENT_DATE;";
- return Zend_Registry::get('db')->fetchOne($sql);
- }
- function getConversionsToday()
- {
- return $this->getConversionItemsToday($table = "log_conversion");
- }
- function getActionsToday()
- {
- $sql = "SELECT count(*) FROM `". Piwik_Common::prefixTable('log_link_visit_action')."` WHERE idsite >= 1 AND DATE(`server_time`) = CURRENT_DATE;";
- return Zend_Registry::get('db')->fetchOne($sql);
- }
+ $db->query($sql);
+ }
+ $sql = "OPTIMIZE TABLE " . Piwik_Common::prefixTable('log_link_visit_action') . ", " . Piwik_Common::prefixTable('log_conversion') . ", " . Piwik_Common::prefixTable('log_conversion_item') . ", " . Piwik_Common::prefixTable('log_visit') . "";
+ $db->query($sql);
+ $this->log("done");
+ }
+
+ function log($m)
+ {
+ Piwik::log($m);
+ }
+
+ function getVisitsToday()
+ {
+ $sql = "SELECT count(*) FROM `" . Piwik_Common::prefixTable('log_visit') . "` WHERE idsite >= 1 AND DATE(`visit_last_action_time`) = CURRENT_DATE;";
+ return Zend_Registry::get('db')->fetchOne($sql);
+ }
+
+ function getConversionItemsToday($table = 'log_conversion_item')
+ {
+ $sql = "SELECT count(*) FROM `" . Piwik_Common::prefixTable($table) . "` WHERE idsite >= 1 AND DATE(`server_time`) = CURRENT_DATE;";
+ return Zend_Registry::get('db')->fetchOne($sql);
+ }
+
+ function getConversionsToday()
+ {
+ return $this->getConversionItemsToday($table = "log_conversion");
+ }
+
+ function getActionsToday()
+ {
+ $sql = "SELECT count(*) FROM `" . Piwik_Common::prefixTable('log_link_visit_action') . "` WHERE idsite >= 1 AND DATE(`server_time`) = CURRENT_DATE;";
+ return Zend_Registry::get('db')->fetchOne($sql);
+ }
}
diff --git a/misc/others/tracker_simpleImageTracker.php b/misc/others/tracker_simpleImageTracker.php
index ae7502874a..373ed31474 100644
--- a/misc/others/tracker_simpleImageTracker.php
+++ b/misc/others/tracker_simpleImageTracker.php
@@ -1,4 +1,5 @@
-<html><body>
+<html>
+<body>
This page loads a Simple Tracker request to Piwik website id=1
@@ -7,7 +8,8 @@ This page loads a Simple Tracker request to Piwik website id=1
require_once "../libs/PiwikTracker/PiwikTracker.php";
PiwikTracker::$URL = 'http://example.org/piwik/';
// Example 1: Tracks a pageview for Website id = {$IDSITE}
-$trackingURL = Piwik_getUrlTrackPageView( $idSite = 1, $customTitle = 'This title will appear in the report Actions > Page titles');
-echo '<img src="'. htmlentities($trackingURL) . '" alt="" />';
+$trackingURL = Piwik_getUrlTrackPageView($idSite = 1, $customTitle = 'This title will appear in the report Actions > Page titles');
+echo '<img src="' . htmlentities($trackingURL) . '" alt="" />';
?>
-</body></html> \ No newline at end of file
+</body>
+</html> \ No newline at end of file
diff --git a/misc/others/uninstall-delete-piwik-directory.php b/misc/others/uninstall-delete-piwik-directory.php
index b51ae63577..e05f70259c 100644
--- a/misc/others/uninstall-delete-piwik-directory.php
+++ b/misc/others/uninstall-delete-piwik-directory.php
@@ -9,18 +9,18 @@
// please let us know at hello@piwik.org - we are interested by your experience
function unlinkRecursive($dir)
{
- if(!$dh = @opendir($dir)) return "Warning: folder $dir couldn't be read by PHP";
- while (false !== ($obj = readdir($dh))) {
- if($obj == '.' || $obj == '..') {
- continue;
- }
- if (!@unlink($dir . '/' . $obj)) {
- unlinkRecursive($dir.'/'.$obj, true);
- }
- }
- closedir($dh);
- @rmdir($dir);
- return "Folder $dir deleted!";
+ if (!$dh = @opendir($dir)) return "Warning: folder $dir couldn't be read by PHP";
+ while (false !== ($obj = readdir($dh))) {
+ if ($obj == '.' || $obj == '..') {
+ continue;
+ }
+ if (!@unlink($dir . '/' . $obj)) {
+ unlinkRecursive($dir . '/' . $obj, true);
+ }
+ }
+ closedir($dh);
+ @rmdir($dir);
+ return "Folder $dir deleted!";
}
echo unlinkRecursive('piwik/');
diff --git a/misc/others/widget_example_lastvisits.html b/misc/others/widget_example_lastvisits.html
index 403a3d4f88..a515255d03 100644
--- a/misc/others/widget_example_lastvisits.html
+++ b/misc/others/widget_example_lastvisits.html
@@ -1,5 +1,11 @@
-<html><body>
+<html>
+<body>
<p>Number of visits per week for the last 52 weeks</p>
-<div id="widgetIframe"><iframe width="800" height="450" src="http://piwik.org/demo/index.php?module=Widgetize&action=iframe&moduleToWidgetize=VisitsSummary&actionToWidgetize=getEvolutionGraph&idSite=1&period=week&date=last52&columns[]=nb_visits&disableLink=1" scrolling="no" frameborder="0" marginheight="0" marginwidth="0"></iframe></div>
+
+<div id="widgetIframe">
+ <iframe width="800" height="450"
+ src="http://piwik.org/demo/index.php?module=Widgetize&action=iframe&moduleToWidgetize=VisitsSummary&actionToWidgetize=getEvolutionGraph&idSite=1&period=week&date=last52&columns[]=nb_visits&disableLink=1"
+ scrolling="no" frameborder="0" marginheight="0" marginwidth="0"></iframe>
+</div>
</body>
</html>
diff --git a/misc/proxy-hide-piwik-url/piwik.php b/misc/proxy-hide-piwik-url/piwik.php
index 9bce43bb2b..eabac8c097 100644
--- a/misc/proxy-hide-piwik-url/piwik.php
+++ b/misc/proxy-hide-piwik-url/piwik.php
@@ -29,45 +29,45 @@ $timeout = 5;
// ---------------------------
// 1) PIWIK.JS PROXY: No _GET parameter, we serve the JS file
if (empty($_GET)) {
- $modifiedSince = false;
- if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
- $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
- // strip any trailing data appended to header
- if (false !== ($semicolon = strpos($modifiedSince, ';'))) {
- $modifiedSince = strtotime(substr($modifiedSince, 0, $semicolon));
- }
- }
- // Re-download the piwik.js once a day maximum
- $lastModified = time()-86400;
+ $modifiedSince = false;
+ if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
+ $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
+ // strip any trailing data appended to header
+ if (false !== ($semicolon = strpos($modifiedSince, ';'))) {
+ $modifiedSince = strtotime(substr($modifiedSince, 0, $semicolon));
+ }
+ }
+ // Re-download the piwik.js once a day maximum
+ $lastModified = time() - 86400;
- // set HTTP response headers
- header('Vary: Accept-Encoding');
+ // set HTTP response headers
+ header('Vary: Accept-Encoding');
- // Returns 304 if not modified since
- if (!empty($modifiedSince) && $modifiedSince < $lastModified) {
- header(sprintf("%s 304 Not Modified", $_SERVER['SERVER_PROTOCOL']));
- } else {
- header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
- @header('Content-Type: application/javascript; charset=UTF-8');
- if ($piwikJs = file_get_contents($PIWIK_URL.'piwik.js')) {
- echo $piwikJs;
- } else {
- header($_SERVER['SERVER_PROTOCOL'] . '505 Internal server error');
- }
- }
- exit;
+ // Returns 304 if not modified since
+ if (!empty($modifiedSince) && $modifiedSince < $lastModified) {
+ header(sprintf("%s 304 Not Modified", $_SERVER['SERVER_PROTOCOL']));
+ } else {
+ header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
+ @header('Content-Type: application/javascript; charset=UTF-8');
+ if ($piwikJs = file_get_contents($PIWIK_URL . 'piwik.js')) {
+ echo $piwikJs;
+ } else {
+ header($_SERVER['SERVER_PROTOCOL'] . '505 Internal server error');
+ }
+ }
+ exit;
}
// 2) PIWIK.PHP PROXY: GET parameters found, this is a tracking request, we redirect it to Piwik
$url = sprintf("%spiwik.php?cip=%s&token_auth=%s&", $PIWIK_URL, @$_SERVER['REMOTE_ADDR'], $TOKEN_AUTH);
-foreach($_GET as $key=>$value) {
- $url .= $key .'='.urlencode($value).'&';
+foreach ($_GET as $key => $value) {
+ $url .= $key . '=' . urlencode($value) . '&';
}
header("Content-Type: image/gif");
$stream_options = array('http' => array(
- 'user_agent' => @$_SERVER['HTTP_USER_AGENT'],
- 'header' => sprintf("Accept-Language: %s%s\r\n", @str_replace(array("\n","\t","\r"), "", $_SERVER['HTTP_ACCEPT_LANGUAGE'])),
- 'timeout' => $timeout
+ 'user_agent' => @$_SERVER['HTTP_USER_AGENT'],
+ 'header' => sprintf("Accept-Language: %s%s\r\n", @str_replace(array("\n", "\t", "\r"), "", $_SERVER['HTTP_ACCEPT_LANGUAGE'])),
+ 'timeout' => $timeout
));
$ctx = stream_context_create($stream_options);
echo file_get_contents($url, 0, $ctx);
diff --git a/piwik.php b/piwik.php
index 14e5e5d518..6633313410 100644
--- a/piwik.php
+++ b/piwik.php
@@ -11,81 +11,74 @@ $GLOBALS['PIWIK_TRACKER_DEBUG'] = false;
$GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS'] = false;
define('PIWIK_ENABLE_TRACKING', true);
-define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__)=='/'?'':dirname(__FILE__));
-if(file_exists(PIWIK_DOCUMENT_ROOT . '/bootstrap.php'))
-{
- require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php';
+define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__) == '/' ? '' : dirname(__FILE__));
+if (file_exists(PIWIK_DOCUMENT_ROOT . '/bootstrap.php')) {
+ require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php';
}
$GLOBALS['PIWIK_TRACKER_MODE'] = true;
-error_reporting(E_ALL|E_NOTICE);
+error_reporting(E_ALL | E_NOTICE);
@ini_set('xdebug.show_exception_trace', 0);
@ini_set('magic_quotes_runtime', 0);
-if(!defined('PIWIK_USER_PATH'))
-{
- define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT);
+if (!defined('PIWIK_USER_PATH')) {
+ define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT);
}
-if(!defined('PIWIK_INCLUDE_PATH'))
-{
- define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT);
+if (!defined('PIWIK_INCLUDE_PATH')) {
+ define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT);
}
@ignore_user_abort(true);
-require_once PIWIK_INCLUDE_PATH .'/libs/upgradephp/upgrade.php';
-require_once PIWIK_INCLUDE_PATH .'/libs/Event/Dispatcher.php';
-require_once PIWIK_INCLUDE_PATH .'/libs/Event/Notification.php';
-require_once PIWIK_INCLUDE_PATH .'/core/PluginsManager.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Plugin.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Common.php';
-require_once PIWIK_INCLUDE_PATH .'/core/IP.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Tracker.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Config.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Translate.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Tracker/Cache.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Tracker/Db.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Tracker/Db/Exception.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Tracker/IgnoreCookie.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Tracker/Visit.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Tracker/GoalManager.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Tracker/Action.php';
-require_once PIWIK_INCLUDE_PATH .'/core/CacheFile.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Cookie.php';
+require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php';
+require_once PIWIK_INCLUDE_PATH . '/libs/Event/Dispatcher.php';
+require_once PIWIK_INCLUDE_PATH . '/libs/Event/Notification.php';
+require_once PIWIK_INCLUDE_PATH . '/core/PluginsManager.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Plugin.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Common.php';
+require_once PIWIK_INCLUDE_PATH . '/core/IP.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Config.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Translate.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Cache.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db/Exception.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/IgnoreCookie.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Visit.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/GoalManager.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Action.php';
+require_once PIWIK_INCLUDE_PATH . '/core/CacheFile.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Cookie.php';
session_cache_limiter('nocache');
@date_default_timezone_set('UTC');
-if(!defined('PIWIK_ENABLE_TRACKING') || PIWIK_ENABLE_TRACKING)
-{
- ob_start();
+if (!defined('PIWIK_ENABLE_TRACKING') || PIWIK_ENABLE_TRACKING) {
+ ob_start();
}
-if($GLOBALS['PIWIK_TRACKER_DEBUG'] === true)
-{
- require_once PIWIK_INCLUDE_PATH .'/core/Loader.php';
- require_once PIWIK_INCLUDE_PATH .'/core/ErrorHandler.php';
- require_once PIWIK_INCLUDE_PATH .'/core/ExceptionHandler.php';
- $timer = new Piwik_Timer();
- set_error_handler('Piwik_ErrorHandler');
- set_exception_handler('Piwik_ExceptionHandler');
- printDebug("Debug enabled - Input parameters: <br/>" . var_export($_GET, true));
- Piwik_Tracker_Db::enableProfiling();
- Piwik::createConfigObject();
- Piwik::createLogObject();
+if ($GLOBALS['PIWIK_TRACKER_DEBUG'] === true) {
+ require_once PIWIK_INCLUDE_PATH . '/core/Loader.php';
+ require_once PIWIK_INCLUDE_PATH . '/core/ErrorHandler.php';
+ require_once PIWIK_INCLUDE_PATH . '/core/ExceptionHandler.php';
+ $timer = new Piwik_Timer();
+ set_error_handler('Piwik_ErrorHandler');
+ set_exception_handler('Piwik_ExceptionHandler');
+ printDebug("Debug enabled - Input parameters: <br/>" . var_export($_GET, true));
+ Piwik_Tracker_Db::enableProfiling();
+ Piwik::createConfigObject();
+ Piwik::createLogObject();
}
-if(!defined('PIWIK_ENABLE_TRACKING') || PIWIK_ENABLE_TRACKING)
-{
- $process = new Piwik_Tracker();
- try {
- $process->main();
- } catch(Exception $e) {
- echo "Error:" . $e->getMessage();
- }
- ob_end_flush();
- if($GLOBALS['PIWIK_TRACKER_DEBUG'] === true)
- {
- printDebug($_COOKIE);
- printDebug($timer);
- }
+if (!defined('PIWIK_ENABLE_TRACKING') || PIWIK_ENABLE_TRACKING) {
+ $process = new Piwik_Tracker();
+ try {
+ $process->main();
+ } catch (Exception $e) {
+ echo "Error:" . $e->getMessage();
+ }
+ ob_end_flush();
+ if ($GLOBALS['PIWIK_TRACKER_DEBUG'] === true) {
+ printDebug($_COOKIE);
+ printDebug($timer);
+ }
}
diff --git a/plugins/API/API.php b/plugins/API/API.php
index 0181bd6996..211122da30 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -13,56 +13,58 @@
/**
* @package Piwik_API
*/
-class Piwik_API extends Piwik_Plugin {
-
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('API_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- public function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'TopMenu.add' => 'addTopMenu',
- );
- }
-
- public function addTopMenu()
- {
- $apiUrlParams = array('module' => 'API', 'action' => 'listAllAPI');
- $tooltip = Piwik_Translate('API_TopLinkTooltip');
-
- Piwik_AddTopMenu('General_API', $apiUrlParams, true, 7, $isHTML = false, $tooltip);
-
- $this->addTopMenuMobileApp();
- }
-
- protected function addTopMenuMobileApp()
- {
- if (empty($_SERVER['HTTP_USER_AGENT'])) {
- return;
- }
- require_once PIWIK_INCLUDE_PATH . '/libs/UserAgentParser/UserAgentParser.php';
- $os = UserAgentParser::getOperatingSystem($_SERVER['HTTP_USER_AGENT']);
- if ($os && in_array($os['id'], array('AND', 'IPD', 'IPA', 'IPH'))) {
- Piwik_AddTopMenu('Piwik Mobile App', array('module' => 'Proxy', 'action' => 'redirect', 'url' => 'http://piwik.org/mobile/'), true, 4);
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getCssFiles($notification) {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "plugins/API/css/styles.css";
- }
+class Piwik_API extends Piwik_Plugin
+{
+
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('API_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'TopMenu.add' => 'addTopMenu',
+ );
+ }
+
+ public function addTopMenu()
+ {
+ $apiUrlParams = array('module' => 'API', 'action' => 'listAllAPI');
+ $tooltip = Piwik_Translate('API_TopLinkTooltip');
+
+ Piwik_AddTopMenu('General_API', $apiUrlParams, true, 7, $isHTML = false, $tooltip);
+
+ $this->addTopMenuMobileApp();
+ }
+
+ protected function addTopMenuMobileApp()
+ {
+ if (empty($_SERVER['HTTP_USER_AGENT'])) {
+ return;
+ }
+ require_once PIWIK_INCLUDE_PATH . '/libs/UserAgentParser/UserAgentParser.php';
+ $os = UserAgentParser::getOperatingSystem($_SERVER['HTTP_USER_AGENT']);
+ if ($os && in_array($os['id'], array('AND', 'IPD', 'IPA', 'IPH'))) {
+ Piwik_AddTopMenu('Piwik Mobile App', array('module' => 'Proxy', 'action' => 'redirect', 'url' => 'http://piwik.org/mobile/'), true, 4);
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+
+ $cssFiles[] = "plugins/API/css/styles.css";
+ }
}
@@ -85,1619 +87,1488 @@ class Piwik_API extends Piwik_Plugin {
*/
class Piwik_API_API
{
- static private $instance = null;
-
- /**
- * @return Piwik_API_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Get Piwik version
- * @return string
- */
- public function getPiwikVersion()
- {
- Piwik::checkUserHasSomeViewAccess();
- return Piwik_Version::VERSION;
- }
-
- /**
- * Returns the section [APISettings] if defined in config.ini.php
- * @return array
- */
- public function getSettings()
- {
- return Piwik_Config::getInstance()->APISettings;
- }
-
- /**
- * Derive the unit name from a column name
- * @param $column
- * @param $idSite
- * @return string
- * @ignore
- */
- static public function getUnit($column, $idSite)
- {
- $nameToUnit = array(
- '_rate' => '%',
- 'revenue' => Piwik::getCurrency($idSite),
- '_time_' => 's'
- );
-
- foreach ($nameToUnit as $pattern => $type)
- {
- if (strpos($column, $pattern) !== false)
- {
- return $type;
- }
- }
-
- return '';
- }
-
- /**
- * Is a lower value for a given column better?
- * @param $column
- * @return bool
- *
- * @ignore
- */
- static public function isLowerValueBetter($column)
- {
- $lowerIsBetterPatterns = array(
- 'bounce', 'exit'
- );
-
- foreach ($lowerIsBetterPatterns as $pattern)
- {
- if (strpos($column, $pattern) !== false)
- {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Default translations for many core metrics.
- * This is used for exports with translated labels. The exports contain columns that
- * are not visible in the UI and not present in the API meta data. These columns are
- * translated here.
- * @return array
- */
- static public function getDefaultMetricTranslations()
- {
- $trans = array(
- 'label' => 'General_ColumnLabel',
- 'date' => 'General_Date',
- 'avg_time_on_page' => 'General_ColumnAverageTimeOnPage',
- 'sum_time_spent' => 'General_ColumnSumVisitLength',
- 'sum_visit_length' => 'General_ColumnSumVisitLength',
- 'bounce_count' => 'General_ColumnBounces',
- 'bounce_count_returning' => 'VisitFrequency_ColumnBounceCountForReturningVisits',
- 'max_actions' => 'General_ColumnMaxActions',
- 'max_actions_returning' => 'VisitFrequency_ColumnMaxActionsInReturningVisit',
- 'nb_visits_converted_returning' => 'VisitFrequency_ColumnNbReturningVisitsConverted',
- 'sum_visit_length_returning' => 'VisitFrequency_ColumnSumVisitLengthReturning',
- 'nb_visits_converted' => 'General_ColumnVisitsWithConversions',
- 'nb_conversions' => 'Goals_ColumnConversions',
- 'revenue' => 'Goals_ColumnRevenue',
- 'nb_hits' => 'General_ColumnPageviews',
- 'entry_nb_visits' => 'General_ColumnEntrances',
- 'entry_nb_uniq_visitors' => 'General_ColumnUniqueEntrances',
- 'exit_nb_visits' => 'General_ColumnExits',
- 'exit_nb_uniq_visitors' => 'General_ColumnUniqueExits',
- 'entry_bounce_count' => 'General_ColumnBounces',
- 'exit_bounce_count' => 'General_ColumnBounces',
- 'exit_rate' => 'General_ColumnExitRate'
- );
-
- $trans = array_map('Piwik_Translate', $trans);
-
- $dailySum = ' ('.Piwik_Translate('General_DailySum').')';
- $afterEntry = ' '.Piwik_Translate('General_AfterEntry');
-
- $trans['sum_daily_nb_uniq_visitors'] = Piwik_Translate('General_ColumnNbUniqVisitors').$dailySum;
- $trans['sum_daily_entry_nb_uniq_visitors'] = Piwik_Translate('General_ColumnUniqueEntrances').$dailySum;
- $trans['sum_daily_exit_nb_uniq_visitors'] = Piwik_Translate('General_ColumnUniqueExits').$dailySum;
- $trans['entry_nb_actions'] = Piwik_Translate('General_ColumnNbActions').$afterEntry;
- $trans['entry_sum_visit_length'] = Piwik_Translate('General_ColumnSumVisitLength').$afterEntry;
-
- $api = self::getInstance();
- $trans = array_merge($api->getDefaultMetrics(), $api->getDefaultProcessedMetrics(), $trans);
-
- return $trans;
- }
-
- public function getDefaultMetrics()
- {
- $translations = array(
- // Standard metrics
- 'nb_visits' => 'General_ColumnNbVisits',
- 'nb_uniq_visitors' => 'General_ColumnNbUniqVisitors',
- 'nb_actions' => 'General_ColumnNbActions',
+ static private $instance = null;
+
+ /**
+ * @return Piwik_API_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Get Piwik version
+ * @return string
+ */
+ public function getPiwikVersion()
+ {
+ Piwik::checkUserHasSomeViewAccess();
+ return Piwik_Version::VERSION;
+ }
+
+ /**
+ * Returns the section [APISettings] if defined in config.ini.php
+ * @return array
+ */
+ public function getSettings()
+ {
+ return Piwik_Config::getInstance()->APISettings;
+ }
+
+ /**
+ * Derive the unit name from a column name
+ * @param $column
+ * @param $idSite
+ * @return string
+ * @ignore
+ */
+ static public function getUnit($column, $idSite)
+ {
+ $nameToUnit = array(
+ '_rate' => '%',
+ 'revenue' => Piwik::getCurrency($idSite),
+ '_time_' => 's'
+ );
+
+ foreach ($nameToUnit as $pattern => $type) {
+ if (strpos($column, $pattern) !== false) {
+ return $type;
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Is a lower value for a given column better?
+ * @param $column
+ * @return bool
+ *
+ * @ignore
+ */
+ static public function isLowerValueBetter($column)
+ {
+ $lowerIsBetterPatterns = array(
+ 'bounce', 'exit'
+ );
+
+ foreach ($lowerIsBetterPatterns as $pattern) {
+ if (strpos($column, $pattern) !== false) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Default translations for many core metrics.
+ * This is used for exports with translated labels. The exports contain columns that
+ * are not visible in the UI and not present in the API meta data. These columns are
+ * translated here.
+ * @return array
+ */
+ static public function getDefaultMetricTranslations()
+ {
+ $trans = array(
+ 'label' => 'General_ColumnLabel',
+ 'date' => 'General_Date',
+ 'avg_time_on_page' => 'General_ColumnAverageTimeOnPage',
+ 'sum_time_spent' => 'General_ColumnSumVisitLength',
+ 'sum_visit_length' => 'General_ColumnSumVisitLength',
+ 'bounce_count' => 'General_ColumnBounces',
+ 'bounce_count_returning' => 'VisitFrequency_ColumnBounceCountForReturningVisits',
+ 'max_actions' => 'General_ColumnMaxActions',
+ 'max_actions_returning' => 'VisitFrequency_ColumnMaxActionsInReturningVisit',
+ 'nb_visits_converted_returning' => 'VisitFrequency_ColumnNbReturningVisitsConverted',
+ 'sum_visit_length_returning' => 'VisitFrequency_ColumnSumVisitLengthReturning',
+ 'nb_visits_converted' => 'General_ColumnVisitsWithConversions',
+ 'nb_conversions' => 'Goals_ColumnConversions',
+ 'revenue' => 'Goals_ColumnRevenue',
+ 'nb_hits' => 'General_ColumnPageviews',
+ 'entry_nb_visits' => 'General_ColumnEntrances',
+ 'entry_nb_uniq_visitors' => 'General_ColumnUniqueEntrances',
+ 'exit_nb_visits' => 'General_ColumnExits',
+ 'exit_nb_uniq_visitors' => 'General_ColumnUniqueExits',
+ 'entry_bounce_count' => 'General_ColumnBounces',
+ 'exit_bounce_count' => 'General_ColumnBounces',
+ 'exit_rate' => 'General_ColumnExitRate'
+ );
+
+ $trans = array_map('Piwik_Translate', $trans);
+
+ $dailySum = ' (' . Piwik_Translate('General_DailySum') . ')';
+ $afterEntry = ' ' . Piwik_Translate('General_AfterEntry');
+
+ $trans['sum_daily_nb_uniq_visitors'] = Piwik_Translate('General_ColumnNbUniqVisitors') . $dailySum;
+ $trans['sum_daily_entry_nb_uniq_visitors'] = Piwik_Translate('General_ColumnUniqueEntrances') . $dailySum;
+ $trans['sum_daily_exit_nb_uniq_visitors'] = Piwik_Translate('General_ColumnUniqueExits') . $dailySum;
+ $trans['entry_nb_actions'] = Piwik_Translate('General_ColumnNbActions') . $afterEntry;
+ $trans['entry_sum_visit_length'] = Piwik_Translate('General_ColumnSumVisitLength') . $afterEntry;
+
+ $api = self::getInstance();
+ $trans = array_merge($api->getDefaultMetrics(), $api->getDefaultProcessedMetrics(), $trans);
+
+ return $trans;
+ }
+
+ public function getDefaultMetrics()
+ {
+ $translations = array(
+ // Standard metrics
+ 'nb_visits' => 'General_ColumnNbVisits',
+ 'nb_uniq_visitors' => 'General_ColumnNbUniqVisitors',
+ 'nb_actions' => 'General_ColumnNbActions',
// Do not display these in reports, as they are not so relevant
// They are used to process metrics below
// 'nb_visits_converted' => 'General_ColumnVisitsWithConversions',
// 'max_actions' => 'General_ColumnMaxActions',
// 'sum_visit_length' => 'General_ColumnSumVisitLength',
// 'bounce_count'
- );
- $translations = array_map('Piwik_Translate', $translations);
- return $translations;
- }
-
- public function getDefaultProcessedMetrics()
- {
- $translations = array(
- // Processed in AddColumnsProcessedMetrics
- 'nb_actions_per_visit' => 'General_ColumnActionsPerVisit',
- 'avg_time_on_site' => 'General_ColumnAvgTimeOnSite',
- 'bounce_rate' => 'General_ColumnBounceRate',
- 'conversion_rate' => 'General_ColumnConversionRate',
- );
- return array_map('Piwik_Translate', $translations);
- }
-
- public function getDefaultMetricsDocumentation()
- {
- $documentation = array(
- 'nb_visits' => 'General_ColumnNbVisitsDocumentation',
- 'nb_uniq_visitors' => 'General_ColumnNbUniqVisitorsDocumentation',
- 'nb_actions' => 'General_ColumnNbActionsDocumentation',
- 'nb_actions_per_visit' => 'General_ColumnActionsPerVisitDocumentation',
- 'avg_time_on_site' => 'General_ColumnAvgTimeOnSiteDocumentation',
- 'bounce_rate' => 'General_ColumnBounceRateDocumentation',
- 'conversion_rate' => 'General_ColumnConversionRateDocumentation',
- 'avg_time_on_page' => 'General_ColumnAverageTimeOnPageDocumentation',
- 'nb_hits' => 'General_ColumnPageviewsDocumentation',
- 'exit_rate' => 'General_ColumnExitRateDocumentation'
- );
- return array_map('Piwik_Translate', $documentation);
- }
-
- public function getSegmentsMetadata($idSites = array(), $_hideImplementationData = true)
- {
- $segments = array();
- Piwik_PostEvent('API.getSegmentsMetadata', $segments, $idSites);
-
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => 'General_VisitorIP',
- 'segment' => 'visitIp',
- 'acceptedValues' => '13.54.122.1, etc.',
- 'sqlSegment' => 'log_visit.location_ip',
- 'sqlFilter' => array('Piwik_IP', 'P2N'),
- 'permission' => Piwik::isUserHasAdminAccess($idSites),
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => 'General_VisitorID',
- 'segment' => 'visitorId',
- 'acceptedValues' => '34c31e04394bdc63 - any 16 Hexadecimal chars ID, which can be fetched using the Tracking API function getVisitorId()',
- 'sqlSegment' => 'log_visit.idvisitor',
- 'sqlFilter' => array('Piwik_Common', 'convertVisitorIdToBin'),
- );
- $segments[] = array(
- 'type' => 'metric',
- 'category' => 'Visit',
- 'name' => 'General_NbActions',
- 'segment' => 'actions',
- 'sqlSegment' => 'log_visit.visit_total_actions',
- );
- $segments[] = array(
- 'type' => 'metric',
- 'category' => 'Visit',
- 'name' => 'General_NbSearches',
- 'segment' => 'searches',
- 'sqlSegment' => 'log_visit.visit_total_searches',
- 'acceptedValues' => 'To select all visits who used internal Site Search, use: &segment=searches>0',
- );
- $segments[] = array(
- 'type' => 'metric',
- 'category' => 'Visit',
- 'name' => 'General_ColumnVisitDuration',
- 'segment' => 'visitDuration',
- 'sqlSegment' => 'log_visit.visit_total_time',
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => Piwik_Translate('General_VisitType') . ". ".Piwik_Translate('General_VisitTypeExample', '"&segment=visitorType==returning,visitorType==returningCustomer"'),
- 'segment' => 'visitorType',
- 'acceptedValues' => 'new, returning, returningCustomer',
- 'sqlSegment' => 'log_visit.visitor_returning',
- 'sqlFilter' => create_function('$type', 'return $type == "new" ? 0 : ($type == "returning" ? 1 : 2);'),
- );
- $segments[] = array(
- 'type' => 'metric',
- 'category' => 'Visit',
- 'name' => 'General_DaysSinceLastVisit',
- 'segment' => 'daysSinceLastVisit',
- 'sqlSegment' => 'log_visit.visitor_days_since_last',
- );
- $segments[] = array(
- 'type' => 'metric',
- 'category' => 'Visit',
- 'name' => 'General_DaysSinceFirstVisit',
- 'segment' => 'daysSinceFirstVisit',
- 'sqlSegment' => 'log_visit.visitor_days_since_first',
- );
- $segments[] = array(
- 'type' => 'metric',
- 'category' => 'Visit',
- 'name' => 'General_NumberOfVisits',
- 'segment' => 'visitCount',
- 'sqlSegment' => 'log_visit.visitor_count_visits',
- );
-
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => 'General_VisitConvertedGoal',
- 'segment' => 'visitConverted',
- 'acceptedValues' => '0, 1',
- 'sqlSegment' => 'log_visit.visit_goal_converted',
- );
-
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => Piwik_Translate('General_EcommerceVisitStatus', '"&segment=visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart"'),
- 'segment' => 'visitEcommerceStatus',
- 'acceptedValues' => implode(", ", self::$visitEcommerceStatus),
- 'sqlSegment' => 'log_visit.visit_goal_buyer',
- 'sqlFilter' => array('Piwik_API_API', 'getVisitEcommerceStatus'),
- );
-
- $segments[] = array(
- 'type' => 'metric',
- 'category' => 'Visit',
- 'name' => 'General_DaysSinceLastEcommerceOrder',
- 'segment' => 'daysSinceLastEcommerceOrder',
- 'sqlSegment' => 'log_visit.visitor_days_since_order',
- );
-
- foreach ($segments as &$segment)
- {
- $segment['name'] = Piwik_Translate($segment['name']);
- $segment['category'] = Piwik_Translate($segment['category']);
-
- if($_hideImplementationData)
- {
- unset($segment['sqlFilter']);
- unset($segment['sqlSegment']);
- }
- }
-
- usort($segments, array($this, 'sortSegments'));
- return $segments;
- }
-
- static protected $visitEcommerceStatus = array(
- Piwik_Tracker_GoalManager::TYPE_BUYER_NONE => 'none',
- Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED => 'ordered',
- Piwik_Tracker_GoalManager::TYPE_BUYER_OPEN_CART => 'abandonedCart',
- Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED_AND_OPEN_CART => 'orderedThenAbandonedCart',
- );
-
- /**
- * @ignore
- */
- static public function getVisitEcommerceStatusFromId($id)
- {
- if(!isset(self::$visitEcommerceStatus[$id]))
- {
- throw new Exception("Unexpected ECommerce status value ");
- }
- return self::$visitEcommerceStatus[$id];
- }
-
- /**
- * @ignore
- */
- static public function getVisitEcommerceStatus($status)
- {
- $id = array_search($status, self::$visitEcommerceStatus);
- if($id === false)
- {
- throw new Exception("Invalid 'visitEcommerceStatus' segment value");
- }
- return $id;
- }
-
- private function sortSegments($row1, $row2)
- {
- $columns = array('type', 'category', 'name', 'segment');
- foreach($columns as $column)
- {
- // Keep segments ordered alphabetically inside categories..
- $type = -1;
- if($column == 'name') $type = 1;
- $compare = $type * strcmp($row1[$column], $row2[$column]);
-
- // hack so that custom variables "page" are grouped together in the doc
- if($row1['category'] == Piwik_Translate('CustomVariables_CustomVariables')
- && $row1['category'] == $row2['category']) {
- $compare = strcmp($row1['segment'], $row2['segment']);
- return $compare;
- }
- if($compare != 0){
- return $compare;
- }
- }
- return $compare;
- }
-
- /**
- * Returns the url to application logo (~280x110px)
- *
- * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
- * @return string
- */
- public function getLogoUrl($pathOnly=false)
- {
- $logo = 'themes/default/images/logo.png';
- if(Piwik_Config::getInstance()->branding['use_custom_logo'] == 1
- && file_exists(Piwik_Common::getPathToPiwikRoot() .'/themes/logo.png'))
- {
- $logo = 'themes/logo.png';
- }
- if(!$pathOnly) {
- return Piwik::getPiwikUrl() . $logo;
- }
- return Piwik_Common::getPathToPiwikRoot() .'/'. $logo;
- }
-
- /**
- * Returns the url to header logo (~127x50px)
- *
- * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
- * @return string
- */
- public function getHeaderLogoUrl($pathOnly=false)
- {
- $logo = 'themes/default/images/logo-header.png';
- if(Piwik_Config::getInstance()->branding['use_custom_logo'] == 1
- && file_exists(Piwik_Common::getPathToPiwikRoot() .'/themes/logo-header.png'))
- {
- $logo = 'themes/logo-header.png';
- }
- if(!$pathOnly) {
- return Piwik::getPiwikUrl() . $logo;
- }
- return Piwik_Common::getPathToPiwikRoot() .'/'. $logo;
- }
-
- /**
- * Returns the URL to application SVG Logo
- *
- * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
- * @return string
- */
- public function getSVGLogoUrl($pathOnly=false)
- {
- $logo = 'themes/default/images/logo.svg';
- if(Piwik_Config::getInstance()->branding['use_custom_logo'] == 1
- && file_exists(Piwik_Common::getPathToPiwikRoot() .'/themes/logo.svg'))
- {
- $logo = 'themes/logo.svg';
- }
- if(!$pathOnly) {
- return Piwik::getPiwikUrl() . $logo;
- }
- return Piwik_Common::getPathToPiwikRoot() .'/'. $logo;
- }
-
- /**
- * Returns whether there is an SVG Logo available.
- *
- * @return bool
- */
- public function hasSVGLogo() {
- if (Piwik_Config::getInstance()->branding['use_custom_logo'] == 0) {
- /* We always have our application logo */
- return true;
- } else if(Piwik_Config::getInstance()->branding['use_custom_logo'] == 1
- && file_exists(Piwik_Common::getPathToPiwikRoot() .'/themes/logo.svg'))
- {
- return true;
- }
-
- return false;
- }
-
- /**
- * Loads reports metadata, then return the requested one,
- * matching optional API parameters.
- */
- public function getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array(), $language = false,
- $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false)
- {
- Piwik_Translate::getInstance()->reloadLanguage($language);
- $reportsMetadata = $this->getReportMetadata($idSite, $period, $date, $hideMetricsDoc, $showSubtableReports);
-
- foreach($reportsMetadata as $report)
- {
- // See ArchiveProcessing/Period.php - unique visitors are not processed for period != day
- if(($period && $period != 'day') && !($apiModule == 'VisitsSummary' && $apiAction == 'get'))
- {
- unset($report['metrics']['nb_uniq_visitors']);
- }
- if($report['module'] == $apiModule
- && $report['action'] == $apiAction)
- {
- // No custom parameters
- if(empty($apiParameters)
- && empty($report['parameters']))
- {
- return array($report);
- }
- if(empty($report['parameters']))
- {
- continue;
- }
- $diff = array_diff($report['parameters'], $apiParameters);
- if(empty($diff))
- {
- return array($report);
- }
- }
- }
- return false;
- }
-
- /**
- * Triggers a hook to ask plugins for available Reports.
- * Returns metadata information about each report (category, name, dimension, metrics, etc.)
- *
- * @param string $idSites Comma separated list of website Ids
- * @return array
- */
- public function getReportMetadata($idSites = '', $period = false, $date = false, $hideMetricsDoc = false,
- $showSubtableReports = false)
- {
- $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites);
- if(!empty($idSites))
- {
- Piwik::checkUserHasViewAccess($idSites);
- }
-
- $parameters = array( 'idSites' => $idSites, 'period' => $period, 'date' => $date);
-
- $availableReports = array();
- Piwik_PostEvent('API.getReportMetadata', $availableReports, $parameters);
- foreach ($availableReports as &$availableReport) {
- if (!isset($availableReport['metrics'])) {
- $availableReport['metrics'] = $this->getDefaultMetrics();
- }
- if (!isset($availableReport['processedMetrics'])) {
- $availableReport['processedMetrics'] = $this->getDefaultProcessedMetrics();
- }
-
- if ($hideMetricsDoc) // remove metric documentation if it's not wanted
- {
- unset($availableReport['metricsDocumentation']);
- }
- else if (!isset($availableReport['metricsDocumentation']))
- {
- // set metric documentation to default if it's not set
- $availableReport['metricsDocumentation'] = $this->getDefaultMetricsDocumentation();
- }
-
- // if hide/show columns specified, hide/show metrics & docs
- $availableReport['metrics'] = $this->hideShowMetrics($availableReport['metrics']);
- $availableReport['processedMetrics'] = $this->hideShowMetrics($availableReport['processedMetrics']);
- if (isset($availableReport['metricsDocumentation']))
- {
- $availableReport['metricsDocumentation'] =
- $this->hideShowMetrics($availableReport['metricsDocumentation']);
- }
- }
-
- // Some plugins need to add custom metrics after all plugins hooked in
- Piwik_PostEvent('API.getReportMetadata.end', $availableReports, $parameters);
- // Oh this is not pretty! Until we have event listeners order parameter...
- Piwik_PostEvent('API.getReportMetadata.end.end', $availableReports, $parameters);
-
- // Sort results to ensure consistent order
- usort($availableReports, array($this, 'sort'));
-
- // Add the magic API.get report metadata aggregating all plugins API.get API calls automatically
- $this->addApiGetMetdata($availableReports);
-
- $knownMetrics = array_merge( $this->getDefaultMetrics(), $this->getDefaultProcessedMetrics() );
- foreach($availableReports as &$availableReport)
- {
- // Ensure all metrics have a translation
- $metrics = $availableReport['metrics'];
- $cleanedMetrics = array();
- foreach($metrics as $metricId => $metricTranslation)
- {
- // When simply the column name was given, ie 'metric' => array( 'nb_visits' )
- // $metricTranslation is in this case nb_visits. We look for a known translation.
- if(is_numeric($metricId)
- && isset($knownMetrics[$metricTranslation]))
- {
- $metricId = $metricTranslation;
- $metricTranslation = $knownMetrics[$metricTranslation];
- }
- $cleanedMetrics[$metricId] = $metricTranslation;
- }
- $availableReport['metrics'] = $cleanedMetrics;
-
- // Remove array elements that are false (to clean up API output)
- foreach($availableReport as $attributeName => $attributeValue)
- {
- if(empty($attributeValue))
- {
- unset($availableReport[$attributeName]);
- }
- }
- // when there are per goal metrics, don't display conversion_rate since it can differ from per goal sum
- if(isset($availableReport['metricsGoal']))
- {
- unset($availableReport['processedMetrics']['conversion_rate']);
- unset($availableReport['metricsGoal']['conversion_rate']);
- }
-
- // Processing a uniqueId for each report,
- // can be used by UIs as a key to match a given report
- $uniqueId = $availableReport['module'] . '_' . $availableReport['action'];
- if(!empty($availableReport['parameters']))
- {
- foreach($availableReport['parameters'] as $key => $value)
- {
- $uniqueId .= '_' . $key . '--' . $value;
- }
- }
- $availableReport['uniqueId'] = $uniqueId;
-
- // Order is used to order reports internally, but not meant to be used outside
- unset($availableReport['order']);
- }
-
- // remove subtable reports
- if (!$showSubtableReports)
- {
- foreach ($availableReports as $idx => $report)
- {
- if (isset($report['isSubtableReport']) && $report['isSubtableReport'])
- {
- unset($availableReports[$idx]);
- }
- }
- }
-
- return array_values($availableReports); // make sure array has contiguous key values
- }
-
-
- /**
- * Add the metadata for the API.get report
- * In other plugins, this would hook on 'API.getReportMetadata'
- */
- private function addApiGetMetdata(&$availableReports)
- {
- $metadata = array(
- 'category' => Piwik_Translate('General_API'),
- 'name' => Piwik_Translate('General_MainMetrics'),
- 'module' => 'API',
- 'action' => 'get',
- 'metrics' => array(),
- 'processedMetrics' => array(),
- 'metricsDocumentation' => array(),
- 'order' => 1
- );
-
- $indexesToMerge = array('metrics', 'processedMetrics', 'metricsDocumentation');
-
- foreach ($availableReports as $report)
- {
- if ($report['action'] == 'get')
- {
- foreach ($indexesToMerge as $index)
- {
- if (isset($report[$index])
- && is_array($report[$index]))
- {
- $metadata[$index] = array_merge($metadata[$index], $report[$index]);
- }
- }
- }
- }
-
- $availableReports[] = $metadata;
- }
-
- public function getProcessedReport( $idSite, $period, $date, $apiModule, $apiAction, $segment = false,
- $apiParameters = false, $idGoal = false, $language = false,
- $showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false)
+ );
+ $translations = array_map('Piwik_Translate', $translations);
+ return $translations;
+ }
+
+ public function getDefaultProcessedMetrics()
+ {
+ $translations = array(
+ // Processed in AddColumnsProcessedMetrics
+ 'nb_actions_per_visit' => 'General_ColumnActionsPerVisit',
+ 'avg_time_on_site' => 'General_ColumnAvgTimeOnSite',
+ 'bounce_rate' => 'General_ColumnBounceRate',
+ 'conversion_rate' => 'General_ColumnConversionRate',
+ );
+ return array_map('Piwik_Translate', $translations);
+ }
+
+ public function getDefaultMetricsDocumentation()
+ {
+ $documentation = array(
+ 'nb_visits' => 'General_ColumnNbVisitsDocumentation',
+ 'nb_uniq_visitors' => 'General_ColumnNbUniqVisitorsDocumentation',
+ 'nb_actions' => 'General_ColumnNbActionsDocumentation',
+ 'nb_actions_per_visit' => 'General_ColumnActionsPerVisitDocumentation',
+ 'avg_time_on_site' => 'General_ColumnAvgTimeOnSiteDocumentation',
+ 'bounce_rate' => 'General_ColumnBounceRateDocumentation',
+ 'conversion_rate' => 'General_ColumnConversionRateDocumentation',
+ 'avg_time_on_page' => 'General_ColumnAverageTimeOnPageDocumentation',
+ 'nb_hits' => 'General_ColumnPageviewsDocumentation',
+ 'exit_rate' => 'General_ColumnExitRateDocumentation'
+ );
+ return array_map('Piwik_Translate', $documentation);
+ }
+
+ public function getSegmentsMetadata($idSites = array(), $_hideImplementationData = true)
{
- $timer = new Piwik_Timer();
- if($apiParameters === false)
- {
- $apiParameters = array();
- }
- if(!empty($idGoal)
- && empty($apiParameters['idGoal']))
- {
- $apiParameters['idGoal'] = $idGoal;
- }
+ $segments = array();
+ Piwik_PostEvent('API.getSegmentsMetadata', $segments, $idSites);
+
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit',
+ 'name' => 'General_VisitorIP',
+ 'segment' => 'visitIp',
+ 'acceptedValues' => '13.54.122.1, etc.',
+ 'sqlSegment' => 'log_visit.location_ip',
+ 'sqlFilter' => array('Piwik_IP', 'P2N'),
+ 'permission' => Piwik::isUserHasAdminAccess($idSites),
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit',
+ 'name' => 'General_VisitorID',
+ 'segment' => 'visitorId',
+ 'acceptedValues' => '34c31e04394bdc63 - any 16 Hexadecimal chars ID, which can be fetched using the Tracking API function getVisitorId()',
+ 'sqlSegment' => 'log_visit.idvisitor',
+ 'sqlFilter' => array('Piwik_Common', 'convertVisitorIdToBin'),
+ );
+ $segments[] = array(
+ 'type' => 'metric',
+ 'category' => 'Visit',
+ 'name' => 'General_NbActions',
+ 'segment' => 'actions',
+ 'sqlSegment' => 'log_visit.visit_total_actions',
+ );
+ $segments[] = array(
+ 'type' => 'metric',
+ 'category' => 'Visit',
+ 'name' => 'General_NbSearches',
+ 'segment' => 'searches',
+ 'sqlSegment' => 'log_visit.visit_total_searches',
+ 'acceptedValues' => 'To select all visits who used internal Site Search, use: &segment=searches>0',
+ );
+ $segments[] = array(
+ 'type' => 'metric',
+ 'category' => 'Visit',
+ 'name' => 'General_ColumnVisitDuration',
+ 'segment' => 'visitDuration',
+ 'sqlSegment' => 'log_visit.visit_total_time',
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit',
+ 'name' => Piwik_Translate('General_VisitType') . ". " . Piwik_Translate('General_VisitTypeExample', '"&segment=visitorType==returning,visitorType==returningCustomer"'),
+ 'segment' => 'visitorType',
+ 'acceptedValues' => 'new, returning, returningCustomer',
+ 'sqlSegment' => 'log_visit.visitor_returning',
+ 'sqlFilter' => create_function('$type', 'return $type == "new" ? 0 : ($type == "returning" ? 1 : 2);'),
+ );
+ $segments[] = array(
+ 'type' => 'metric',
+ 'category' => 'Visit',
+ 'name' => 'General_DaysSinceLastVisit',
+ 'segment' => 'daysSinceLastVisit',
+ 'sqlSegment' => 'log_visit.visitor_days_since_last',
+ );
+ $segments[] = array(
+ 'type' => 'metric',
+ 'category' => 'Visit',
+ 'name' => 'General_DaysSinceFirstVisit',
+ 'segment' => 'daysSinceFirstVisit',
+ 'sqlSegment' => 'log_visit.visitor_days_since_first',
+ );
+ $segments[] = array(
+ 'type' => 'metric',
+ 'category' => 'Visit',
+ 'name' => 'General_NumberOfVisits',
+ 'segment' => 'visitCount',
+ 'sqlSegment' => 'log_visit.visitor_count_visits',
+ );
+
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit',
+ 'name' => 'General_VisitConvertedGoal',
+ 'segment' => 'visitConverted',
+ 'acceptedValues' => '0, 1',
+ 'sqlSegment' => 'log_visit.visit_goal_converted',
+ );
+
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit',
+ 'name' => Piwik_Translate('General_EcommerceVisitStatus', '"&segment=visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart"'),
+ 'segment' => 'visitEcommerceStatus',
+ 'acceptedValues' => implode(", ", self::$visitEcommerceStatus),
+ 'sqlSegment' => 'log_visit.visit_goal_buyer',
+ 'sqlFilter' => array('Piwik_API_API', 'getVisitEcommerceStatus'),
+ );
+
+ $segments[] = array(
+ 'type' => 'metric',
+ 'category' => 'Visit',
+ 'name' => 'General_DaysSinceLastEcommerceOrder',
+ 'segment' => 'daysSinceLastEcommerceOrder',
+ 'sqlSegment' => 'log_visit.visitor_days_since_order',
+ );
+
+ foreach ($segments as &$segment) {
+ $segment['name'] = Piwik_Translate($segment['name']);
+ $segment['category'] = Piwik_Translate($segment['category']);
+
+ if ($_hideImplementationData) {
+ unset($segment['sqlFilter']);
+ unset($segment['sqlSegment']);
+ }
+ }
+
+ usort($segments, array($this, 'sortSegments'));
+ return $segments;
+ }
+
+ static protected $visitEcommerceStatus = array(
+ Piwik_Tracker_GoalManager::TYPE_BUYER_NONE => 'none',
+ Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED => 'ordered',
+ Piwik_Tracker_GoalManager::TYPE_BUYER_OPEN_CART => 'abandonedCart',
+ Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED_AND_OPEN_CART => 'orderedThenAbandonedCart',
+ );
+
+ /**
+ * @ignore
+ */
+ static public function getVisitEcommerceStatusFromId($id)
+ {
+ if (!isset(self::$visitEcommerceStatus[$id])) {
+ throw new Exception("Unexpected ECommerce status value ");
+ }
+ return self::$visitEcommerceStatus[$id];
+ }
+
+ /**
+ * @ignore
+ */
+ static public function getVisitEcommerceStatus($status)
+ {
+ $id = array_search($status, self::$visitEcommerceStatus);
+ if ($id === false) {
+ throw new Exception("Invalid 'visitEcommerceStatus' segment value");
+ }
+ return $id;
+ }
+
+ private function sortSegments($row1, $row2)
+ {
+ $columns = array('type', 'category', 'name', 'segment');
+ foreach ($columns as $column) {
+ // Keep segments ordered alphabetically inside categories..
+ $type = -1;
+ if ($column == 'name') $type = 1;
+ $compare = $type * strcmp($row1[$column], $row2[$column]);
+
+ // hack so that custom variables "page" are grouped together in the doc
+ if ($row1['category'] == Piwik_Translate('CustomVariables_CustomVariables')
+ && $row1['category'] == $row2['category']
+ ) {
+ $compare = strcmp($row1['segment'], $row2['segment']);
+ return $compare;
+ }
+ if ($compare != 0) {
+ return $compare;
+ }
+ }
+ return $compare;
+ }
+
+ /**
+ * Returns the url to application logo (~280x110px)
+ *
+ * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
+ * @return string
+ */
+ public function getLogoUrl($pathOnly = false)
+ {
+ $logo = 'themes/default/images/logo.png';
+ if (Piwik_Config::getInstance()->branding['use_custom_logo'] == 1
+ && file_exists(Piwik_Common::getPathToPiwikRoot() . '/themes/logo.png')
+ ) {
+ $logo = 'themes/logo.png';
+ }
+ if (!$pathOnly) {
+ return Piwik::getPiwikUrl() . $logo;
+ }
+ return Piwik_Common::getPathToPiwikRoot() . '/' . $logo;
+ }
+
+ /**
+ * Returns the url to header logo (~127x50px)
+ *
+ * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
+ * @return string
+ */
+ public function getHeaderLogoUrl($pathOnly = false)
+ {
+ $logo = 'themes/default/images/logo-header.png';
+ if (Piwik_Config::getInstance()->branding['use_custom_logo'] == 1
+ && file_exists(Piwik_Common::getPathToPiwikRoot() . '/themes/logo-header.png')
+ ) {
+ $logo = 'themes/logo-header.png';
+ }
+ if (!$pathOnly) {
+ return Piwik::getPiwikUrl() . $logo;
+ }
+ return Piwik_Common::getPathToPiwikRoot() . '/' . $logo;
+ }
+
+ /**
+ * Returns the URL to application SVG Logo
+ *
+ * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
+ * @return string
+ */
+ public function getSVGLogoUrl($pathOnly = false)
+ {
+ $logo = 'themes/default/images/logo.svg';
+ if (Piwik_Config::getInstance()->branding['use_custom_logo'] == 1
+ && file_exists(Piwik_Common::getPathToPiwikRoot() . '/themes/logo.svg')
+ ) {
+ $logo = 'themes/logo.svg';
+ }
+ if (!$pathOnly) {
+ return Piwik::getPiwikUrl() . $logo;
+ }
+ return Piwik_Common::getPathToPiwikRoot() . '/' . $logo;
+ }
+
+ /**
+ * Returns whether there is an SVG Logo available.
+ *
+ * @return bool
+ */
+ public function hasSVGLogo()
+ {
+ if (Piwik_Config::getInstance()->branding['use_custom_logo'] == 0) {
+ /* We always have our application logo */
+ return true;
+ } else if (Piwik_Config::getInstance()->branding['use_custom_logo'] == 1
+ && file_exists(Piwik_Common::getPathToPiwikRoot() . '/themes/logo.svg')
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Loads reports metadata, then return the requested one,
+ * matching optional API parameters.
+ */
+ public function getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array(), $language = false,
+ $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false)
+ {
+ Piwik_Translate::getInstance()->reloadLanguage($language);
+ $reportsMetadata = $this->getReportMetadata($idSite, $period, $date, $hideMetricsDoc, $showSubtableReports);
+
+ foreach ($reportsMetadata as $report) {
+ // See ArchiveProcessing/Period.php - unique visitors are not processed for period != day
+ if (($period && $period != 'day') && !($apiModule == 'VisitsSummary' && $apiAction == 'get')) {
+ unset($report['metrics']['nb_uniq_visitors']);
+ }
+ if ($report['module'] == $apiModule
+ && $report['action'] == $apiAction
+ ) {
+ // No custom parameters
+ if (empty($apiParameters)
+ && empty($report['parameters'])
+ ) {
+ return array($report);
+ }
+ if (empty($report['parameters'])) {
+ continue;
+ }
+ $diff = array_diff($report['parameters'], $apiParameters);
+ if (empty($diff)) {
+ return array($report);
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Triggers a hook to ask plugins for available Reports.
+ * Returns metadata information about each report (category, name, dimension, metrics, etc.)
+ *
+ * @param string $idSites Comma separated list of website Ids
+ * @return array
+ */
+ public function getReportMetadata($idSites = '', $period = false, $date = false, $hideMetricsDoc = false,
+ $showSubtableReports = false)
+ {
+ $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites);
+ if (!empty($idSites)) {
+ Piwik::checkUserHasViewAccess($idSites);
+ }
+
+ $parameters = array('idSites' => $idSites, 'period' => $period, 'date' => $date);
+
+ $availableReports = array();
+ Piwik_PostEvent('API.getReportMetadata', $availableReports, $parameters);
+ foreach ($availableReports as &$availableReport) {
+ if (!isset($availableReport['metrics'])) {
+ $availableReport['metrics'] = $this->getDefaultMetrics();
+ }
+ if (!isset($availableReport['processedMetrics'])) {
+ $availableReport['processedMetrics'] = $this->getDefaultProcessedMetrics();
+ }
+
+ if ($hideMetricsDoc) // remove metric documentation if it's not wanted
+ {
+ unset($availableReport['metricsDocumentation']);
+ } else if (!isset($availableReport['metricsDocumentation'])) {
+ // set metric documentation to default if it's not set
+ $availableReport['metricsDocumentation'] = $this->getDefaultMetricsDocumentation();
+ }
+
+ // if hide/show columns specified, hide/show metrics & docs
+ $availableReport['metrics'] = $this->hideShowMetrics($availableReport['metrics']);
+ $availableReport['processedMetrics'] = $this->hideShowMetrics($availableReport['processedMetrics']);
+ if (isset($availableReport['metricsDocumentation'])) {
+ $availableReport['metricsDocumentation'] =
+ $this->hideShowMetrics($availableReport['metricsDocumentation']);
+ }
+ }
+
+ // Some plugins need to add custom metrics after all plugins hooked in
+ Piwik_PostEvent('API.getReportMetadata.end', $availableReports, $parameters);
+ // Oh this is not pretty! Until we have event listeners order parameter...
+ Piwik_PostEvent('API.getReportMetadata.end.end', $availableReports, $parameters);
+
+ // Sort results to ensure consistent order
+ usort($availableReports, array($this, 'sort'));
+
+ // Add the magic API.get report metadata aggregating all plugins API.get API calls automatically
+ $this->addApiGetMetdata($availableReports);
+
+ $knownMetrics = array_merge($this->getDefaultMetrics(), $this->getDefaultProcessedMetrics());
+ foreach ($availableReports as &$availableReport) {
+ // Ensure all metrics have a translation
+ $metrics = $availableReport['metrics'];
+ $cleanedMetrics = array();
+ foreach ($metrics as $metricId => $metricTranslation) {
+ // When simply the column name was given, ie 'metric' => array( 'nb_visits' )
+ // $metricTranslation is in this case nb_visits. We look for a known translation.
+ if (is_numeric($metricId)
+ && isset($knownMetrics[$metricTranslation])
+ ) {
+ $metricId = $metricTranslation;
+ $metricTranslation = $knownMetrics[$metricTranslation];
+ }
+ $cleanedMetrics[$metricId] = $metricTranslation;
+ }
+ $availableReport['metrics'] = $cleanedMetrics;
+
+ // Remove array elements that are false (to clean up API output)
+ foreach ($availableReport as $attributeName => $attributeValue) {
+ if (empty($attributeValue)) {
+ unset($availableReport[$attributeName]);
+ }
+ }
+ // when there are per goal metrics, don't display conversion_rate since it can differ from per goal sum
+ if (isset($availableReport['metricsGoal'])) {
+ unset($availableReport['processedMetrics']['conversion_rate']);
+ unset($availableReport['metricsGoal']['conversion_rate']);
+ }
+
+ // Processing a uniqueId for each report,
+ // can be used by UIs as a key to match a given report
+ $uniqueId = $availableReport['module'] . '_' . $availableReport['action'];
+ if (!empty($availableReport['parameters'])) {
+ foreach ($availableReport['parameters'] as $key => $value) {
+ $uniqueId .= '_' . $key . '--' . $value;
+ }
+ }
+ $availableReport['uniqueId'] = $uniqueId;
+
+ // Order is used to order reports internally, but not meant to be used outside
+ unset($availableReport['order']);
+ }
+
+ // remove subtable reports
+ if (!$showSubtableReports) {
+ foreach ($availableReports as $idx => $report) {
+ if (isset($report['isSubtableReport']) && $report['isSubtableReport']) {
+ unset($availableReports[$idx]);
+ }
+ }
+ }
+
+ return array_values($availableReports); // make sure array has contiguous key values
+ }
+
+
+ /**
+ * Add the metadata for the API.get report
+ * In other plugins, this would hook on 'API.getReportMetadata'
+ */
+ private function addApiGetMetdata(&$availableReports)
+ {
+ $metadata = array(
+ 'category' => Piwik_Translate('General_API'),
+ 'name' => Piwik_Translate('General_MainMetrics'),
+ 'module' => 'API',
+ 'action' => 'get',
+ 'metrics' => array(),
+ 'processedMetrics' => array(),
+ 'metricsDocumentation' => array(),
+ 'order' => 1
+ );
+
+ $indexesToMerge = array('metrics', 'processedMetrics', 'metricsDocumentation');
+
+ foreach ($availableReports as $report) {
+ if ($report['action'] == 'get') {
+ foreach ($indexesToMerge as $index) {
+ if (isset($report[$index])
+ && is_array($report[$index])
+ ) {
+ $metadata[$index] = array_merge($metadata[$index], $report[$index]);
+ }
+ }
+ }
+ }
+
+ $availableReports[] = $metadata;
+ }
+
+ public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false,
+ $apiParameters = false, $idGoal = false, $language = false,
+ $showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false)
+ {
+ $timer = new Piwik_Timer();
+ if ($apiParameters === false) {
+ $apiParameters = array();
+ }
+ if (!empty($idGoal)
+ && empty($apiParameters['idGoal'])
+ ) {
+ $apiParameters['idGoal'] = $idGoal;
+ }
// Is this report found in the Metadata available reports?
$reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language,
- $period, $date, $hideMetricsDoc, $showSubtableReports = true);
- if(empty($reportMetadata))
- {
- throw new Exception("Requested report $apiModule.$apiAction for Website id=$idSite not found in the list of available reports. \n");
+ $period, $date, $hideMetricsDoc, $showSubtableReports = true);
+ if (empty($reportMetadata)) {
+ throw new Exception("Requested report $apiModule.$apiAction for Website id=$idSite not found in the list of available reports. \n");
}
$reportMetadata = reset($reportMetadata);
- // Generate Api call URL passing custom parameters
- $parameters = array_merge( $apiParameters, array(
- 'method' => $apiModule.'.'.$apiAction,
- 'idSite' => $idSite,
- 'period' => $period,
- 'date' => $date,
- 'format' => 'original',
- 'serialize' => '0',
- 'language' => $language,
- 'idSubtable' => $idSubtable,
- ));
- if(!empty($segment)) $parameters['segment'] = $segment;
-
- $url = Piwik_Url::getQueryStringFromParameters($parameters);
+ // Generate Api call URL passing custom parameters
+ $parameters = array_merge($apiParameters, array(
+ 'method' => $apiModule . '.' . $apiAction,
+ 'idSite' => $idSite,
+ 'period' => $period,
+ 'date' => $date,
+ 'format' => 'original',
+ 'serialize' => '0',
+ 'language' => $language,
+ 'idSubtable' => $idSubtable,
+ ));
+ if (!empty($segment)) $parameters['segment'] = $segment;
+
+ $url = Piwik_Url::getQueryStringFromParameters($parameters);
$request = new Piwik_API_Request($url);
try {
- /** @var Piwik_DataTable */
- $dataTable = $request->process();
- } catch(Exception $e) {
- throw new Exception("API returned an error: ".$e->getMessage()."\n");
- }
-
- list($newReport, $columns, $rowsMetadata) = $this->handleTableReport($idSite, $dataTable, $reportMetadata, isset($reportMetadata['dimension']), $showRawMetrics);
- foreach($columns as $columnId => &$name)
- {
- $name = ucfirst($name);
- }
- $website = new Piwik_Site($idSite);
+ /** @var Piwik_DataTable */
+ $dataTable = $request->process();
+ } catch (Exception $e) {
+ throw new Exception("API returned an error: " . $e->getMessage() . "\n");
+ }
+
+ list($newReport, $columns, $rowsMetadata) = $this->handleTableReport($idSite, $dataTable, $reportMetadata, isset($reportMetadata['dimension']), $showRawMetrics);
+ foreach ($columns as $columnId => &$name) {
+ $name = ucfirst($name);
+ }
+ $website = new Piwik_Site($idSite);
// $segment = new Piwik_Segment($segment, $idSite);
- $period = Piwik_Period::advancedFactory($period, $date);
- $period = $period->getLocalizedLongString();
+ $period = Piwik_Period::advancedFactory($period, $date);
+ $period = $period->getLocalizedLongString();
- $return = array(
- 'website' => $website->getName(),
- 'prettyDate' => $period,
+ $return = array(
+ 'website' => $website->getName(),
+ 'prettyDate' => $period,
// 'prettySegment' => $segment->getPrettyString(),
- 'metadata' => $reportMetadata,
- 'columns' => $columns,
- 'reportData' => $newReport,
- 'reportMetadata' => $rowsMetadata,
- );
- if($showTimer)
- {
- $return['timerMillis'] = $timer->getTimeMs(0);
- }
- return $return;
+ 'metadata' => $reportMetadata,
+ 'columns' => $columns,
+ 'reportData' => $newReport,
+ 'reportMetadata' => $rowsMetadata,
+ );
+ if ($showTimer) {
+ $return['timerMillis'] = $timer->getTimeMs(0);
+ }
+ return $return;
}
- /**
- * Enhance a $dataTable using metadata :
- *
- * - remove metrics based on $reportMetadata['metrics']
- * - add 0 valued metrics if $dataTable doesn't provide all $reportMetadata['metrics']
- * - format metric values to a 'human readable' format
- * - extract row metadata to a separate Piwik_DataTable_Simple|Piwik_DataTable_Array : $rowsMetadata
- * - translate metric names to a separate array : $columns
- *
- * @param int $idSite enables monetary value formatting based on site currency
- * @param Piwik_DataTable|Piwik_DataTable_Array $dataTable
- * @param array $reportMetadata
- * @param boolean $hasDimension
- * @return array Piwik_DataTable_Simple|Piwik_DataTable_Array $newReport with human readable format & array $columns list of translated column names & Piwik_DataTable_Simple|Piwik_DataTable_Array $rowsMetadata
- **/
+ /**
+ * Enhance a $dataTable using metadata :
+ *
+ * - remove metrics based on $reportMetadata['metrics']
+ * - add 0 valued metrics if $dataTable doesn't provide all $reportMetadata['metrics']
+ * - format metric values to a 'human readable' format
+ * - extract row metadata to a separate Piwik_DataTable_Simple|Piwik_DataTable_Array : $rowsMetadata
+ * - translate metric names to a separate array : $columns
+ *
+ * @param int $idSite enables monetary value formatting based on site currency
+ * @param Piwik_DataTable|Piwik_DataTable_Array $dataTable
+ * @param array $reportMetadata
+ * @param boolean $hasDimension
+ * @return array Piwik_DataTable_Simple|Piwik_DataTable_Array $newReport with human readable format & array $columns list of translated column names & Piwik_DataTable_Simple|Piwik_DataTable_Array $rowsMetadata
+ **/
private function handleTableReport($idSite, $dataTable, &$reportMetadata, $hasDimension, $showRawMetrics = false)
{
- $columns = $reportMetadata['metrics'];
-
- if($hasDimension)
- {
- $columns = array_merge(
- array('label' => $reportMetadata['dimension'] ),
- $columns
- );
-
- if(isset($reportMetadata['processedMetrics']))
- {
- $processedMetricsAdded = $this->getDefaultProcessedMetrics();
- foreach($processedMetricsAdded as $processedMetricId => $processedMetricTranslation)
- {
- // this processed metric can be displayed for this report
- if(isset($reportMetadata['processedMetrics'][$processedMetricId]))
- {
- $columns[$processedMetricId] = $processedMetricTranslation;
- }
- }
- }
-
- // Display the global Goal metrics
- if(isset($reportMetadata['metricsGoal']))
- {
- $metricsGoalDisplay = array('revenue');
- // Add processed metrics to be displayed for this report
- foreach($metricsGoalDisplay as $goalMetricId)
- {
- if(isset($reportMetadata['metricsGoal'][$goalMetricId]))
- {
- $columns[$goalMetricId] = $reportMetadata['metricsGoal'][$goalMetricId];
- }
- }
- }
-
- if(isset($reportMetadata['processedMetrics']))
- {
- // Add processed metrics
- $dataTable->filter('AddColumnsProcessedMetrics', array($deleteRowsWithNoVisit = false));
- }
- }
-
- $columns = $this->hideShowMetrics($columns);
-
- // $dataTable is an instance of Piwik_DataTable_Array when multiple periods requested
- if ($dataTable instanceof Piwik_DataTable_Array)
- {
- // Need a new Piwik_DataTable_Array to store the 'human readable' values
- $newReport = new Piwik_DataTable_Array();
- $newReport->setKeyName("prettyDate");
-
- // Need a new Piwik_DataTable_Array to store report metadata
- $rowsMetadata = new Piwik_DataTable_Array();
- $rowsMetadata->setKeyName("prettyDate");
-
- // Process each Piwik_DataTable_Simple entry
- foreach($dataTable->getArray() as $label => $simpleDataTable)
- {
- $this->removeEmptyColumns($columns, $reportMetadata, $simpleDataTable);
-
- list($enhancedSimpleDataTable, $rowMetadata) = $this->handleSimpleDataTable($idSite, $simpleDataTable, $columns, $hasDimension, $showRawMetrics);
- $enhancedSimpleDataTable->metadata = $simpleDataTable->metadata;
-
- $period = $simpleDataTable->metadata['period']->getLocalizedLongString();
- $newReport->addTable($enhancedSimpleDataTable, $period);
- $rowsMetadata->addTable($rowMetadata, $period);
- }
- }
- else
- {
- $this->removeEmptyColumns($columns, $reportMetadata, $dataTable);
- list($newReport, $rowsMetadata) = $this->handleSimpleDataTable($idSite, $dataTable, $columns, $hasDimension, $showRawMetrics);
- }
-
- return array(
- $newReport,
- $columns,
- $rowsMetadata
- );
+ $columns = $reportMetadata['metrics'];
+
+ if ($hasDimension) {
+ $columns = array_merge(
+ array('label' => $reportMetadata['dimension']),
+ $columns
+ );
+
+ if (isset($reportMetadata['processedMetrics'])) {
+ $processedMetricsAdded = $this->getDefaultProcessedMetrics();
+ foreach ($processedMetricsAdded as $processedMetricId => $processedMetricTranslation) {
+ // this processed metric can be displayed for this report
+ if (isset($reportMetadata['processedMetrics'][$processedMetricId])) {
+ $columns[$processedMetricId] = $processedMetricTranslation;
+ }
+ }
+ }
+
+ // Display the global Goal metrics
+ if (isset($reportMetadata['metricsGoal'])) {
+ $metricsGoalDisplay = array('revenue');
+ // Add processed metrics to be displayed for this report
+ foreach ($metricsGoalDisplay as $goalMetricId) {
+ if (isset($reportMetadata['metricsGoal'][$goalMetricId])) {
+ $columns[$goalMetricId] = $reportMetadata['metricsGoal'][$goalMetricId];
+ }
+ }
+ }
+
+ if (isset($reportMetadata['processedMetrics'])) {
+ // Add processed metrics
+ $dataTable->filter('AddColumnsProcessedMetrics', array($deleteRowsWithNoVisit = false));
+ }
+ }
+
+ $columns = $this->hideShowMetrics($columns);
+
+ // $dataTable is an instance of Piwik_DataTable_Array when multiple periods requested
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ // Need a new Piwik_DataTable_Array to store the 'human readable' values
+ $newReport = new Piwik_DataTable_Array();
+ $newReport->setKeyName("prettyDate");
+
+ // Need a new Piwik_DataTable_Array to store report metadata
+ $rowsMetadata = new Piwik_DataTable_Array();
+ $rowsMetadata->setKeyName("prettyDate");
+
+ // Process each Piwik_DataTable_Simple entry
+ foreach ($dataTable->getArray() as $label => $simpleDataTable) {
+ $this->removeEmptyColumns($columns, $reportMetadata, $simpleDataTable);
+
+ list($enhancedSimpleDataTable, $rowMetadata) = $this->handleSimpleDataTable($idSite, $simpleDataTable, $columns, $hasDimension, $showRawMetrics);
+ $enhancedSimpleDataTable->metadata = $simpleDataTable->metadata;
+
+ $period = $simpleDataTable->metadata['period']->getLocalizedLongString();
+ $newReport->addTable($enhancedSimpleDataTable, $period);
+ $rowsMetadata->addTable($rowMetadata, $period);
+ }
+ } else {
+ $this->removeEmptyColumns($columns, $reportMetadata, $dataTable);
+ list($newReport, $rowsMetadata) = $this->handleSimpleDataTable($idSite, $dataTable, $columns, $hasDimension, $showRawMetrics);
+ }
+
+ return array(
+ $newReport,
+ $columns,
+ $rowsMetadata
+ );
+ }
+
+ /**
+ * Removes metrics from the list of columns and the report meta data if they are marked empty
+ * in the data table meta data.
+ */
+ private function removeEmptyColumns(&$columns, &$reportMetadata, $dataTable)
+ {
+ $emptyColumns = $dataTable->getMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME);
+
+ if (!is_array($emptyColumns)) {
+ return;
+ }
+
+ $columns = $this->hideShowMetrics($columns, $emptyColumns);
+
+ if (isset($reportMetadata['metrics'])) {
+ $reportMetadata['metrics'] = $this->hideShowMetrics($reportMetadata['metrics'], $emptyColumns);
+ }
+
+ if (isset($reportMetadata['metricsDocumentation'])) {
+ $reportMetadata['metricsDocumentation'] = $this->hideShowMetrics($reportMetadata['metricsDocumentation'], $emptyColumns);
+ }
}
- /**
- * Removes metrics from the list of columns and the report meta data if they are marked empty
- * in the data table meta data.
- */
- private function removeEmptyColumns( &$columns, &$reportMetadata, $dataTable )
- {
- $emptyColumns = $dataTable->getMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME);
-
- if (!is_array($emptyColumns))
- {
- return;
- }
-
- $columns = $this->hideShowMetrics($columns, $emptyColumns);
-
- if (isset($reportMetadata['metrics']))
- {
- $reportMetadata['metrics'] = $this->hideShowMetrics($reportMetadata['metrics'], $emptyColumns);
- }
-
- if (isset($reportMetadata['metricsDocumentation']))
- {
- $reportMetadata['metricsDocumentation'] = $this->hideShowMetrics($reportMetadata['metricsDocumentation'], $emptyColumns);
- }
- }
-
- /**
+ /**
* Removes column names from an array based on the values in the hideColumns,
* showColumns query parameters. This is a hack that provides the ColumnDelete
* filter functionality in processed reports.
*
* @param array $columns List of metrics shown in a processed report.
- * @param array $emptyColumns Empty columns from the data table meta data.
+ * @param array $emptyColumns Empty columns from the data table meta data.
* @return array Filtered list of metrics.
*/
- private function hideShowMetrics( $columns, $emptyColumns = array() )
+ private function hideShowMetrics($columns, $emptyColumns = array())
+ {
+ if (!is_array($columns)) {
+ return $columns;
+ }
+
+ // remove columns if hideColumns query parameters exist
+ $columnsToRemove = Piwik_Common::getRequestVar('hideColumns', '');
+ if ($columnsToRemove != '') {
+ $columnsToRemove = explode(',', $columnsToRemove);
+ foreach ($columnsToRemove as $name) {
+ // if a column to remove is in the column list, remove it
+ if (isset($columns[$name])) {
+ unset($columns[$name]);
+ }
+ }
+ }
+
+ // remove columns if showColumns query parameters exist
+ $columnsToKeep = Piwik_Common::getRequestVar('showColumns', '');
+ if ($columnsToKeep != '') {
+ $columnsToKeep = explode(',', $columnsToKeep);
+ $columnsToKeep[] = 'label';
+
+ foreach ($columns as $name => $ignore) {
+ // if the current column should not be kept, remove it
+ $idx = array_search($name, $columnsToKeep);
+ if ($idx === FALSE) // if $name is not in $columnsToKeep
+ {
+ unset($columns[$name]);
+ }
+ }
+ }
+
+ // remove empty columns
+ if (is_array($emptyColumns)) {
+ foreach ($emptyColumns as $column) {
+ if (isset($columns[$column])) {
+ unset($columns[$column]);
+ }
+ }
+ }
+
+ return $columns;
+ }
+
+ /**
+ * Enhance $simpleDataTable using metadata :
+ *
+ * - remove metrics based on $reportMetadata['metrics']
+ * - add 0 valued metrics if $simpleDataTable doesn't provide all $reportMetadata['metrics']
+ * - format metric values to a 'human readable' format
+ * - extract row metadata to a separate Piwik_DataTable_Simple $rowsMetadata
+ *
+ * @param int $idSite enables monetary value formatting based on site currency
+ * @param Piwik_DataTable_Simple $simpleDataTable
+ * @param array $metadataColumns
+ * @param boolean $hasDimension
+ * @param bool $returnRawMetrics If set to true, the original metrics will be returned
+ *
+ * @return array Piwik_DataTable $enhancedDataTable filtered metrics with human readable format & Piwik_DataTable_Simple $rowsMetadata
+ */
+ private function handleSimpleDataTable($idSite, $simpleDataTable, $metadataColumns, $hasDimension, $returnRawMetrics = false)
+ {
+ // new DataTable to store metadata
+ $rowsMetadata = new Piwik_DataTable();
+
+ // new DataTable to store 'human readable' values
+ if ($hasDimension) {
+ $enhancedDataTable = new Piwik_DataTable();
+ } else {
+ $enhancedDataTable = new Piwik_DataTable_Simple();
+ }
+
+ // add missing metrics
+ foreach ($simpleDataTable->getRows() as $row) {
+ $rowMetrics = $row->getColumns();
+ foreach ($metadataColumns as $id => $name) {
+ if (!isset($rowMetrics[$id])) {
+ $row->addColumn($id, 0);
+ }
+ }
+ }
+
+ foreach ($simpleDataTable->getRows() as $row) {
+ $enhancedRow = new Piwik_DataTable_Row();
+ $enhancedDataTable->addRow($enhancedRow);
+ $rowMetrics = $row->getColumns();
+ foreach ($rowMetrics as $columnName => $columnValue) {
+ // filter metrics according to metadata definition
+ if (isset($metadataColumns[$columnName])) {
+ // generate 'human readable' metric values
+ $prettyValue = Piwik::getPrettyValue($idSite, $columnName, $columnValue, $htmlAllowed = false, $timeAsSentence = false);
+ $enhancedRow->addColumn($columnName, $prettyValue);
+ } // For example the Maps Widget requires the raw metrics to do advanced datavis
+ elseif ($returnRawMetrics) {
+ $enhancedRow->addColumn($columnName, $columnValue);
+ }
+ }
+
+ // If report has a dimension, extract metadata into a distinct DataTable
+ if ($hasDimension) {
+ $rowMetadata = $row->getMetadata();
+ $idSubDataTable = $row->getIdSubDataTable();
+
+ // Create a row metadata only if there are metadata to insert
+ if (count($rowMetadata) > 0 || !is_null($idSubDataTable)) {
+ $metadataRow = new Piwik_DataTable_Row();
+ $rowsMetadata->addRow($metadataRow);
+
+ foreach ($rowMetadata as $metadataKey => $metadataValue) {
+ $metadataRow->addColumn($metadataKey, $metadataValue);
+ }
+
+ if (!is_null($idSubDataTable)) {
+ $metadataRow->addColumn('idsubdatatable', $idSubDataTable);
+ }
+ }
+ }
+ }
+
+ return array(
+ $enhancedDataTable,
+ $rowsMetadata
+ );
+ }
+
+ /**
+ * API metadata are sorted by category/name,
+ * with a little tweak to replicate the standard Piwik category ordering
+ *
+ * @param string $a
+ * @param string $b
+ * @return int
+ */
+ private function sort($a, $b)
{
- if (!is_array($columns))
- {
- return $columns;
- }
-
- // remove columns if hideColumns query parameters exist
- $columnsToRemove = Piwik_Common::getRequestVar('hideColumns', '');
- if ($columnsToRemove != '')
- {
- $columnsToRemove = explode(',', $columnsToRemove);
- foreach ($columnsToRemove as $name)
- {
- // if a column to remove is in the column list, remove it
- if (isset($columns[$name]))
- {
- unset($columns[$name]);
- }
- }
- }
-
- // remove columns if showColumns query parameters exist
- $columnsToKeep = Piwik_Common::getRequestVar('showColumns', '');
- if ($columnsToKeep != '')
- {
- $columnsToKeep = explode(',', $columnsToKeep);
- $columnsToKeep[] = 'label';
-
- foreach ($columns as $name => $ignore)
- {
- // if the current column should not be kept, remove it
- $idx = array_search($name, $columnsToKeep);
- if ($idx === FALSE) // if $name is not in $columnsToKeep
- {
- unset($columns[$name]);
- }
- }
- }
-
- // remove empty columns
- if (is_array($emptyColumns))
- {
- foreach ($emptyColumns as $column)
- {
- if (isset($columns[$column]))
- {
- unset($columns[$column]);
- }
- }
- }
-
- return $columns;
+ static $order = null;
+ if (is_null($order)) {
+ $order = array(
+ Piwik_Translate('General_MultiSitesSummary'),
+ Piwik_Translate('VisitsSummary_VisitsSummary'),
+ Piwik_Translate('Goals_Ecommerce'),
+ Piwik_Translate('Actions_Actions'),
+ Piwik_Translate('Actions_SubmenuSitesearch'),
+ Piwik_Translate('Referers_Referers'),
+ Piwik_Translate('Goals_Goals'),
+ Piwik_Translate('General_Visitors'),
+ Piwik_Translate('UserSettings_VisitorSettings'),
+ );
+ }
+ return ($category = strcmp(array_search($a['category'], $order), array_search($b['category'], $order))) == 0
+ ? (@$a['order'] < @$b['order'] ? -1 : 1)
+ : $category;
+ }
+
+ /**
+ * Get a combined report of the *.get API methods.
+ */
+ public function get($idSite, $period, $date, $segment = false, $columns = false)
+ {
+ $columns = Piwik::getArrayFromApiParameter($columns);
+
+ // build columns map for faster checks later on
+ $columnsMap = array();
+ foreach ($columns as $column) {
+ $columnsMap[$column] = true;
+ }
+
+ // find out which columns belong to which plugin
+ $columnsByPlugin = array();
+ $meta = Piwik_API_API::getInstance()->getReportMetadata($idSite, $period, $date);
+ foreach ($meta as $reportMeta) {
+ // scan all *.get reports
+ if ($reportMeta['action'] == 'get' && !isset($reportMeta['parameters'])
+ && $reportMeta['module'] != 'API'
+ ) {
+ $plugin = $reportMeta['module'];
+ foreach ($reportMeta['metrics'] as $column => $columnTranslation) {
+ // a metric from this report has been requested
+ if (isset($columnsMap[$column])
+ // or by default, return all metrics
+ || empty($columnsMap)
+ ) {
+ $columnsByPlugin[$plugin][] = $column;
+ }
+ }
+ }
+ }
+ krsort($columnsByPlugin);
+
+ $mergedDataTable = false;
+ $params = compact('idSite', 'period', 'date', 'segment', 'idGoal');
+ foreach ($columnsByPlugin as $plugin => $columns) {
+ // load the data
+ $className = 'Piwik_' . $plugin . '_API';
+ $params['columns'] = implode(',', $columns);
+ $dataTable = Piwik_API_Proxy::getInstance()->call($className, 'get', $params);
+ // make sure the table has all columns
+ $array = ($dataTable instanceof Piwik_DataTable_Array ? $dataTable->getArray() : array($dataTable));
+ foreach ($array as $table) {
+ // we don't support idSites=all&date=DATE1,DATE2
+ if ($table instanceof Piwik_DataTable) {
+ $firstRow = $table->getFirstRow();
+ if (!$firstRow) {
+ $firstRow = new Piwik_DataTable_Row;
+ $table->addRow($firstRow);
+ }
+ foreach ($columns as $column) {
+ if ($firstRow->getColumn($column) === false) {
+ $firstRow->setColumn($column, 0);
+ }
+ }
+ }
+ }
+
+ // merge reports
+ if ($mergedDataTable === false) {
+ $mergedDataTable = $dataTable;
+ } else {
+ $this->mergeDataTables($mergedDataTable, $dataTable);
+ }
+ }
+ return $mergedDataTable;
+ }
+
+
+ /**
+ * Merge the columns of two data tables.
+ * Manipulates the first table.
+ */
+ private function mergeDataTables($table1, $table2)
+ {
+ // handle table arrays
+ if ($table1 instanceof Piwik_DataTable_Array && $table2 instanceof Piwik_DataTable_Array) {
+ $subTables2 = $table2->getArray();
+ foreach ($table1->getArray() as $index => $subTable1) {
+ $subTable2 = $subTables2[$index];
+ $this->mergeDataTables($subTable1, $subTable2);
+ }
+ return;
+ }
+
+ $firstRow1 = $table1->getFirstRow();
+ $firstRow2 = $table2->getFirstRow();
+ if ($firstRow2 instanceof Piwik_DataTable_Row) {
+ foreach ($firstRow2->getColumns() as $metric => $value) {
+ $firstRow1->setColumn($metric, $value);
+ }
+ }
+ }
+
+
+ /**
+ * Given an API report to query (eg. "Referers.getKeywords", and a Label (eg. "free%20software"),
+ * this function will query the API for the previous days/weeks/etc. and will return
+ * a ready to use data structure containing the metrics for the requested Label, along with enriched information (min/max values, etc.)
+ *
+ * @return array
+ */
+ public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true)
+ {
+ // validation of requested $period & $date
+ if ($period == 'range') {
+ // load days in the range
+ $period = 'day';
+ }
+
+ if (!Piwik_Archive::isMultiplePeriod($date, $period)) {
+ throw new Exception("Row evolutions can not be processed with this combination of \'date\' and \'period\' parameters.");
+ }
+
+ // this is needed because Piwik_API_Proxy uses Piwik_Common::getRequestVar which in turn
+ // uses Piwik_Common::sanitizeInputValue. This causes the > that separates recursive labels
+ // to become &gt; and we need to undo that here.
+ $label = Piwik_Common::unsanitizeInputValue($label);
+
+ if ($label) {
+ $labels = explode(',', $label);
+ $labels = array_unique($labels);
+ } else {
+ $labels = array();
+ }
+
+ $dataTable = $this->loadRowEvolutionDataFromAPI($idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal);
+
+ if (empty($labels)) {
+ // if no labels specified, use all possible labels as list
+ foreach ($dataTable->getArray() as $table) {
+ $labels = array_merge($labels, $table->getColumn('label'));
+ }
+ $labels = array_unique($labels);
+ }
+
+ if (count($labels) > 1) {
+ $data = $this->getMultiRowEvolution(
+ $dataTable,
+ $idSite,
+ $period,
+ $date,
+ $apiModule,
+ $apiAction,
+ $labels,
+ $segment,
+ $column,
+ $language,
+ $idGoal,
+ $legendAppendMetric,
+ $labelUseAbsoluteUrl
+ );
+ } else {
+ $data = $this->getSingleRowEvolution(
+ $dataTable,
+ $idSite,
+ $period,
+ $date,
+ $apiModule,
+ $apiAction,
+ $labels[0],
+ $segment,
+ $language,
+ $idGoal,
+ $labelUseAbsoluteUrl
+ );
+ }
+ return $data;
}
- /**
- * Enhance $simpleDataTable using metadata :
- *
- * - remove metrics based on $reportMetadata['metrics']
- * - add 0 valued metrics if $simpleDataTable doesn't provide all $reportMetadata['metrics']
- * - format metric values to a 'human readable' format
- * - extract row metadata to a separate Piwik_DataTable_Simple $rowsMetadata
- *
- * @param int $idSite enables monetary value formatting based on site currency
- * @param Piwik_DataTable_Simple $simpleDataTable
- * @param array $metadataColumns
- * @param boolean $hasDimension
- * @param bool $returnRawMetrics If set to true, the original metrics will be returned
- *
- * @return array Piwik_DataTable $enhancedDataTable filtered metrics with human readable format & Piwik_DataTable_Simple $rowsMetadata
- */
- private function handleSimpleDataTable($idSite, $simpleDataTable, $metadataColumns, $hasDimension, $returnRawMetrics = false)
- {
- // new DataTable to store metadata
- $rowsMetadata = new Piwik_DataTable();
-
- // new DataTable to store 'human readable' values
- if($hasDimension)
- {
- $enhancedDataTable = new Piwik_DataTable();
- }
- else
- {
- $enhancedDataTable = new Piwik_DataTable_Simple();
- }
-
- // add missing metrics
- foreach($simpleDataTable->getRows() as $row)
- {
- $rowMetrics = $row->getColumns();
- foreach($metadataColumns as $id => $name)
- {
- if(!isset($rowMetrics[$id]))
- {
- $row->addColumn($id, 0);
- }
- }
- }
-
- foreach($simpleDataTable->getRows() as $row)
- {
- $enhancedRow = new Piwik_DataTable_Row();
- $enhancedDataTable->addRow($enhancedRow);
- $rowMetrics = $row->getColumns();
- foreach($rowMetrics as $columnName => $columnValue)
- {
- // filter metrics according to metadata definition
- if(isset($metadataColumns[$columnName]))
- {
- // generate 'human readable' metric values
- $prettyValue = Piwik::getPrettyValue($idSite, $columnName, $columnValue, $htmlAllowed = false, $timeAsSentence = false);
- $enhancedRow->addColumn($columnName, $prettyValue);
- }
- // For example the Maps Widget requires the raw metrics to do advanced datavis
- elseif($returnRawMetrics)
- {
- $enhancedRow->addColumn($columnName, $columnValue);
- }
- }
-
- // If report has a dimension, extract metadata into a distinct DataTable
- if($hasDimension)
- {
- $rowMetadata = $row->getMetadata();
- $idSubDataTable = $row->getIdSubDataTable();
-
- // Create a row metadata only if there are metadata to insert
- if(count($rowMetadata) > 0 || !is_null($idSubDataTable))
- {
- $metadataRow = new Piwik_DataTable_Row();
- $rowsMetadata->addRow($metadataRow);
-
- foreach($rowMetadata as $metadataKey => $metadataValue)
- {
- $metadataRow->addColumn($metadataKey, $metadataValue);
- }
-
- if(!is_null($idSubDataTable))
- {
- $metadataRow->addColumn('idsubdatatable', $idSubDataTable);
- }
- }
- }
- }
-
- return array(
- $enhancedDataTable,
- $rowsMetadata
- );
- }
-
- /**
- * API metadata are sorted by category/name,
- * with a little tweak to replicate the standard Piwik category ordering
- *
- * @param string $a
- * @param string $b
- * @return int
- */
- private function sort($a, $b)
- {
- static $order = null;
- if(is_null($order))
- {
- $order = array(
- Piwik_Translate('General_MultiSitesSummary'),
- Piwik_Translate('VisitsSummary_VisitsSummary'),
- Piwik_Translate('Goals_Ecommerce'),
- Piwik_Translate('Actions_Actions'),
- Piwik_Translate('Actions_SubmenuSitesearch'),
- Piwik_Translate('Referers_Referers'),
- Piwik_Translate('Goals_Goals'),
- Piwik_Translate('General_Visitors'),
- Piwik_Translate('UserSettings_VisitorSettings'),
- );
- }
- return ($category = strcmp(array_search($a['category'], $order), array_search($b['category'], $order))) == 0
- ? (@$a['order'] < @$b['order'] ? -1 : 1)
- : $category;
- }
-
- /**
- * Get a combined report of the *.get API methods.
- */
- public function get( $idSite, $period, $date, $segment = false, $columns = false)
- {
- $columns = Piwik::getArrayFromApiParameter($columns);
-
- // build columns map for faster checks later on
- $columnsMap = array();
- foreach ($columns as $column) {
- $columnsMap[$column] = true;
- }
-
- // find out which columns belong to which plugin
- $columnsByPlugin = array();
- $meta = Piwik_API_API::getInstance()->getReportMetadata($idSite, $period, $date);
- foreach ($meta as $reportMeta)
- {
- // scan all *.get reports
- if ($reportMeta['action'] == 'get' && !isset($reportMeta['parameters'])
- && $reportMeta['module'] != 'API')
- {
- $plugin = $reportMeta['module'];
- foreach ($reportMeta['metrics'] as $column => $columnTranslation)
- {
- // a metric from this report has been requested
- if (isset($columnsMap[$column])
- // or by default, return all metrics
- || empty($columnsMap))
- {
- $columnsByPlugin[$plugin][] = $column;
- }
- }
- }
- }
- krsort($columnsByPlugin);
-
- $mergedDataTable = false;
- $params = compact('idSite', 'period', 'date', 'segment', 'idGoal');
- foreach ($columnsByPlugin as $plugin => $columns)
- {
- // load the data
- $className = 'Piwik_'.$plugin.'_API';
- $params['columns'] = implode(',', $columns);
- $dataTable = Piwik_API_Proxy::getInstance()->call($className, 'get', $params);
- // make sure the table has all columns
- $array = ($dataTable instanceof Piwik_DataTable_Array ? $dataTable->getArray() : array($dataTable));
- foreach ($array as $table)
- {
- // we don't support idSites=all&date=DATE1,DATE2
- if($table instanceof Piwik_DataTable)
- {
- $firstRow = $table->getFirstRow();
- if(!$firstRow)
- {
- $firstRow = new Piwik_DataTable_Row;
- $table->addRow($firstRow);
- }
- foreach ($columns as $column)
- {
- if ($firstRow->getColumn($column) === false)
- {
- $firstRow->setColumn($column, 0);
- }
- }
- }
- }
-
- // merge reports
- if($mergedDataTable === false)
- {
- $mergedDataTable = $dataTable;
- }
- else
- {
- $this->mergeDataTables($mergedDataTable, $dataTable);
- }
- }
- return $mergedDataTable;
- }
-
-
- /**
- * Merge the columns of two data tables.
- * Manipulates the first table.
- */
- private function mergeDataTables($table1, $table2)
- {
- // handle table arrays
- if ($table1 instanceof Piwik_DataTable_Array && $table2 instanceof Piwik_DataTable_Array)
- {
- $subTables2 = $table2->getArray();
- foreach ($table1->getArray() as $index => $subTable1)
- {
- $subTable2 = $subTables2[$index];
- $this->mergeDataTables($subTable1, $subTable2);
- }
- return;
- }
-
- $firstRow1 = $table1->getFirstRow();
- $firstRow2 = $table2->getFirstRow();
- if($firstRow2 instanceof Piwik_DataTable_Row)
- {
- foreach ($firstRow2->getColumns() as $metric => $value)
- {
- $firstRow1->setColumn($metric, $value);
- }
- }
- }
-
-
- /**
- * Given an API report to query (eg. "Referers.getKeywords", and a Label (eg. "free%20software"),
- * this function will query the API for the previous days/weeks/etc. and will return
- * a ready to use data structure containing the metrics for the requested Label, along with enriched information (min/max values, etc.)
- *
- * @return array
- */
- public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true)
+ /**
+ * Get row evolution for a single label
+ * @return array containing report data, metadata, label, logo
+ */
+ private function getSingleRowEvolution($dataTable, $idSite, $period, $date, $apiModule, $apiAction, $label, $segment, $language = false, $idGoal = false, $labelUseAbsoluteUrl = true)
{
- // validation of requested $period & $date
- if ($period == 'range')
- {
- // load days in the range
- $period = 'day';
- }
-
- if(!Piwik_Archive::isMultiplePeriod($date, $period))
- {
- throw new Exception("Row evolutions can not be processed with this combination of \'date\' and \'period\' parameters.");
- }
-
- // this is needed because Piwik_API_Proxy uses Piwik_Common::getRequestVar which in turn
- // uses Piwik_Common::sanitizeInputValue. This causes the > that separates recursive labels
- // to become &gt; and we need to undo that here.
- $label = Piwik_Common::unsanitizeInputValue($label);
-
- if($label)
- {
- $labels = explode(',', $label);
- $labels = array_unique($labels);
- }
- else
- {
- $labels = array();
- }
-
- $dataTable = $this->loadRowEvolutionDataFromAPI($idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal);
-
- if (empty($labels))
- {
- // if no labels specified, use all possible labels as list
- foreach ($dataTable->getArray() as $table)
- {
- $labels = array_merge($labels, $table->getColumn('label'));
- }
- $labels = array_unique($labels);
- }
-
- if (count($labels) > 1)
- {
- $data = $this->getMultiRowEvolution(
- $dataTable,
- $idSite,
- $period,
- $date,
- $apiModule,
- $apiAction,
- $labels,
- $segment,
- $column,
- $language,
- $idGoal,
- $legendAppendMetric,
- $labelUseAbsoluteUrl
- );
- }
- else
- {
- $data = $this->getSingleRowEvolution(
- $dataTable,
- $idSite,
- $period,
- $date,
- $apiModule,
- $apiAction,
- $labels[0],
- $segment,
- $language,
- $idGoal,
- $labelUseAbsoluteUrl
- );
- }
- return $data;
- }
-
- /**
- * Get row evolution for a single label
- * @return array containing report data, metadata, label, logo
- */
- private function getSingleRowEvolution($dataTable, $idSite, $period, $date, $apiModule, $apiAction, $label, $segment, $language=false, $idGoal = false, $labelUseAbsoluteUrl = true)
- {
- $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal);
- $metricNames = array_keys($metadata['metrics']);
-
- $logo = $actualLabel = false;
- $urlFound = false;
- foreach ($dataTable->getArray() as $date => $subTable)
- {
- /** @var $subTable Piwik_DataTable */
- $subTable->applyQueuedFilters();
- if ($subTable->getRowsCount() > 0)
- {
- /** @var $row Piwik_DataTable_Row */
- $row = $subTable->getFirstRow();
-
- if (!$actualLabel)
- {
- $logo = $row->getMetadata('logo');
-
- $actualLabel = $this->getRowUrlForEvolutionLabel($row, $apiModule, $apiAction, $labelUseAbsoluteUrl);
- $urlFound = $actualLabel !== false;
- if(empty($actualLabel))
- {
- $actualLabel = $row->getColumn('label');
- }
-
- }
-
- // remove all columns that are not in the available metrics.
- // this removes the label as well (which is desired for two reasons: (1) it was passed
- // in the request, (2) it would cause the evolution graph to show the label in the legend).
- foreach ($row->getColumns() as $column => $value)
- {
- if (!in_array($column, $metricNames))
- {
- $row->deleteColumn($column);
- }
- }
-
- $row->deleteMetadata();
- }
- }
-
- $this->enhanceRowEvolutionMetaData($metadata, $dataTable);
-
- // if we have a recursive label and no url, use the path
- if (!$urlFound)
- {
- $actualLabel = str_replace(Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL, ' - ', $label);
- }
-
- $return = array(
- 'label' => Piwik_DataTable_Filter_SafeDecodeLabel::safeDecodeLabel($actualLabel),
- 'reportData' => $dataTable,
- 'metadata' => $metadata
- );
- if(!empty($logo)){
- $return['logo'] = $logo;
- }
- return $return;
- }
-
- private function getRowUrlForEvolutionLabel($row, $apiModule, $apiAction, $labelUseAbsoluteUrl)
- {
- $url = $row->getMetadata('url');
- if ($url
- && ($apiModule == 'Actions'
- || ($apiModule == 'Referers'
- && $apiAction == 'getWebsites'))
- && $labelUseAbsoluteUrl
- ) {
- $actualLabel = preg_replace(';^http(s)?://(www.)?;i', '', $url);
- return $actualLabel;
- }
- return false;
- }
-
- /**
- * @param $idSite
- * @param $period
- * @param $date
- * @param $apiModule
- * @param $apiAction
- * @param $label
- * @param $segment
- * @param $idGoal
- * @throws Exception
- * @return Piwik_DataTable_Array|Piwik_DataTable
- */
- private function loadRowEvolutionDataFromAPI($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false)
- {
- if (!is_array($label))
- {
- $label = array($label);
- }
- $label = array_map('rawurlencode', $label);
-
- $parameters = array(
- 'method' => $apiModule.'.'.$apiAction,
- 'label' => $label,
- 'idSite' => $idSite,
- 'period' => $period,
- 'date' => $date,
- 'format' => 'original',
- 'serialize' => '0',
- 'segment' => $segment,
- 'idGoal' => $idGoal,
-
- // if more than one label is used, we add empty rows for labels we can't
- // find to ensure we know the order of the rows in the returned data table
- 'labelFilterAddEmptyRows' => count($label) > 1 ? 1 : 0,
- );
-
- // add "processed metrics" like actions per visit or bounce rate
- // note: some reports should not be filtered with AddColumnProcessedMetrics
- // specifically, reports without the Piwik_Archive::INDEX_NB_VISITS metric such as Goals.getVisitsUntilConversion & Goal.getDaysToConversion
- // this is because the AddColumnProcessedMetrics filter removes all datable rows lacking this metric
- if
- (
- $apiModule != 'Actions'
- &&
- ($apiModule != 'Goals' || ($apiAction != 'getVisitsUntilConversion' && $apiAction != 'getDaysToConversion'))
- && $label // do not request processed metrics when retrieving top n labels
- )
- {
- $parameters['filter_add_columns_when_show_all_columns'] = '1';
- }
-
- $url = Piwik_Url::getQueryStringFromParameters($parameters);
-
+ $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal);
+ $metricNames = array_keys($metadata['metrics']);
+
+ $logo = $actualLabel = false;
+ $urlFound = false;
+ foreach ($dataTable->getArray() as $date => $subTable) {
+ /** @var $subTable Piwik_DataTable */
+ $subTable->applyQueuedFilters();
+ if ($subTable->getRowsCount() > 0) {
+ /** @var $row Piwik_DataTable_Row */
+ $row = $subTable->getFirstRow();
+
+ if (!$actualLabel) {
+ $logo = $row->getMetadata('logo');
+
+ $actualLabel = $this->getRowUrlForEvolutionLabel($row, $apiModule, $apiAction, $labelUseAbsoluteUrl);
+ $urlFound = $actualLabel !== false;
+ if (empty($actualLabel)) {
+ $actualLabel = $row->getColumn('label');
+ }
+
+ }
+
+ // remove all columns that are not in the available metrics.
+ // this removes the label as well (which is desired for two reasons: (1) it was passed
+ // in the request, (2) it would cause the evolution graph to show the label in the legend).
+ foreach ($row->getColumns() as $column => $value) {
+ if (!in_array($column, $metricNames)) {
+ $row->deleteColumn($column);
+ }
+ }
+
+ $row->deleteMetadata();
+ }
+ }
+
+ $this->enhanceRowEvolutionMetaData($metadata, $dataTable);
+
+ // if we have a recursive label and no url, use the path
+ if (!$urlFound) {
+ $actualLabel = str_replace(Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL, ' - ', $label);
+ }
+
+ $return = array(
+ 'label' => Piwik_DataTable_Filter_SafeDecodeLabel::safeDecodeLabel($actualLabel),
+ 'reportData' => $dataTable,
+ 'metadata' => $metadata
+ );
+ if (!empty($logo)) {
+ $return['logo'] = $logo;
+ }
+ return $return;
+ }
+
+ private function getRowUrlForEvolutionLabel($row, $apiModule, $apiAction, $labelUseAbsoluteUrl)
+ {
+ $url = $row->getMetadata('url');
+ if ($url
+ && ($apiModule == 'Actions'
+ || ($apiModule == 'Referers'
+ && $apiAction == 'getWebsites'))
+ && $labelUseAbsoluteUrl
+ ) {
+ $actualLabel = preg_replace(';^http(s)?://(www.)?;i', '', $url);
+ return $actualLabel;
+ }
+ return false;
+ }
+
+ /**
+ * @param $idSite
+ * @param $period
+ * @param $date
+ * @param $apiModule
+ * @param $apiAction
+ * @param $label
+ * @param $segment
+ * @param $idGoal
+ * @throws Exception
+ * @return Piwik_DataTable_Array|Piwik_DataTable
+ */
+ private function loadRowEvolutionDataFromAPI($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false)
+ {
+ if (!is_array($label)) {
+ $label = array($label);
+ }
+ $label = array_map('rawurlencode', $label);
+
+ $parameters = array(
+ 'method' => $apiModule . '.' . $apiAction,
+ 'label' => $label,
+ 'idSite' => $idSite,
+ 'period' => $period,
+ 'date' => $date,
+ 'format' => 'original',
+ 'serialize' => '0',
+ 'segment' => $segment,
+ 'idGoal' => $idGoal,
+
+ // if more than one label is used, we add empty rows for labels we can't
+ // find to ensure we know the order of the rows in the returned data table
+ 'labelFilterAddEmptyRows' => count($label) > 1 ? 1 : 0,
+ );
+
+ // add "processed metrics" like actions per visit or bounce rate
+ // note: some reports should not be filtered with AddColumnProcessedMetrics
+ // specifically, reports without the Piwik_Archive::INDEX_NB_VISITS metric such as Goals.getVisitsUntilConversion & Goal.getDaysToConversion
+ // this is because the AddColumnProcessedMetrics filter removes all datable rows lacking this metric
+ if
+ (
+ $apiModule != 'Actions'
+ &&
+ ($apiModule != 'Goals' || ($apiAction != 'getVisitsUntilConversion' && $apiAction != 'getDaysToConversion'))
+ && $label // do not request processed metrics when retrieving top n labels
+ ) {
+ $parameters['filter_add_columns_when_show_all_columns'] = '1';
+ }
+
+ $url = Piwik_Url::getQueryStringFromParameters($parameters);
+
$request = new Piwik_API_Request($url);
- try {
- $dataTable = $request->process();
- } catch (Exception $e) {
- throw new Exception("API returned an error: ".$e->getMessage()."\n");
- }
-
- return $dataTable;
- }
-
- /**
- * For a given API report, returns a simpler version
- * of the metadata (will return only the metrics and the dimension name)
- * @param $idSite
- * @param $period
- * @param $date
- * @param $apiModule
- * @param $apiAction
- * @param $language
- * @param $idGoal
- * @throws Exception
- * @return array
- */
- private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false)
- {
- $apiParameters = array();
- if(!empty($idGoal) && $idGoal > 0 ) {
- $apiParameters = array( 'idGoal' => $idGoal);
- }
- $reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language, $period, $date, $hideMetricsDoc = false, $showSubtableReports = true);
-
- if (empty($reportMetadata))
- {
- throw new Exception("Requested report $apiModule.$apiAction for Website id=$idSite "
- . "not found in the list of available reports. \n");
- }
-
- $reportMetadata = reset($reportMetadata);
-
- $metrics = $reportMetadata['metrics'];
- if (isset($reportMetadata['processedMetrics']) && is_array($reportMetadata['processedMetrics']))
- {
- $metrics = $metrics + $reportMetadata['processedMetrics'];
- }
-
- $dimension = $reportMetadata['dimension'];
-
- return compact('metrics', 'dimension');
- }
-
- /**
- * Given the Row evolution dataTable, and the associated metadata,
- * enriches the metadata with min/max values, and % change between the first period and the last one
- * @param array $metadata
- * @param Piwik_DataTable_Array $dataTable
- */
- private function enhanceRowEvolutionMetaData(&$metadata, $dataTable)
- {
- // prepare result array for metrics
- $metricsResult = array();
- foreach ($metadata['metrics'] as $metric => $name)
- {
- $metricsResult[$metric] = array('name' => $name);
-
- if(!empty($metadata['logos'][$metric])) {
- $metricsResult[$metric]['logo'] = $metadata['logos'][$metric];
- }
- }
- unset($metadata['logos']);
-
- $subDataTables = $dataTable->getArray();
- $firstDataTable = reset($subDataTables);
- $firstDataTableRow = $firstDataTable->getFirstRow();
- $lastDataTable = end($subDataTables);
- $lastDataTableRow = $lastDataTable->getFirstRow();
-
- // Process min/max values
- $firstNonZeroFound = array();
- foreach ($subDataTables as $subDataTable)
- {
- // $subDataTable is the report for one period, it has only one row
- $firstRow = $subDataTable->getFirstRow();
- foreach ($metadata['metrics'] as $metric => $label)
- {
- $value = $firstRow ? floatval($firstRow->getColumn($metric)) : 0;
- if ($value > 0)
- {
- $firstNonZeroFound[$metric] = true;
- }
- else if (!isset($firstNonZeroFound[$metric]))
- {
- continue;
- }
- if (!isset($metricsResult[$metric]['min'])
- || $metricsResult[$metric]['min'] > $value)
- {
- $metricsResult[$metric]['min'] = $value;
- }
- if (!isset($metricsResult[$metric]['max'])
- || $metricsResult[$metric]['max'] < $value)
- {
- $metricsResult[$metric]['max'] = $value;
- }
- }
- }
-
- // Process % change between first/last values
- foreach ($metadata['metrics'] as $metric => $label)
- {
- $first = $firstDataTableRow ? floatval($firstDataTableRow->getColumn($metric)) : 0;
- $last = $lastDataTableRow ? floatval($lastDataTableRow->getColumn($metric)) : 0;
-
- // do not calculate evolution if the first value is 0 (to avoid divide-by-zero)
- if ($first == 0)
- {
- continue;
- }
-
- $change = Piwik_DataTable_Filter_CalculateEvolutionFilter::calculate($last, $first, $quotientPrecision = 0);
+ try {
+ $dataTable = $request->process();
+ } catch (Exception $e) {
+ throw new Exception("API returned an error: " . $e->getMessage() . "\n");
+ }
+
+ return $dataTable;
+ }
+
+ /**
+ * For a given API report, returns a simpler version
+ * of the metadata (will return only the metrics and the dimension name)
+ * @param $idSite
+ * @param $period
+ * @param $date
+ * @param $apiModule
+ * @param $apiAction
+ * @param $language
+ * @param $idGoal
+ * @throws Exception
+ * @return array
+ */
+ private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false)
+ {
+ $apiParameters = array();
+ if (!empty($idGoal) && $idGoal > 0) {
+ $apiParameters = array('idGoal' => $idGoal);
+ }
+ $reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language, $period, $date, $hideMetricsDoc = false, $showSubtableReports = true);
+
+ if (empty($reportMetadata)) {
+ throw new Exception("Requested report $apiModule.$apiAction for Website id=$idSite "
+ . "not found in the list of available reports. \n");
+ }
+
+ $reportMetadata = reset($reportMetadata);
+
+ $metrics = $reportMetadata['metrics'];
+ if (isset($reportMetadata['processedMetrics']) && is_array($reportMetadata['processedMetrics'])) {
+ $metrics = $metrics + $reportMetadata['processedMetrics'];
+ }
+
+ $dimension = $reportMetadata['dimension'];
+
+ return compact('metrics', 'dimension');
+ }
+
+ /**
+ * Given the Row evolution dataTable, and the associated metadata,
+ * enriches the metadata with min/max values, and % change between the first period and the last one
+ * @param array $metadata
+ * @param Piwik_DataTable_Array $dataTable
+ */
+ private function enhanceRowEvolutionMetaData(&$metadata, $dataTable)
+ {
+ // prepare result array for metrics
+ $metricsResult = array();
+ foreach ($metadata['metrics'] as $metric => $name) {
+ $metricsResult[$metric] = array('name' => $name);
+
+ if (!empty($metadata['logos'][$metric])) {
+ $metricsResult[$metric]['logo'] = $metadata['logos'][$metric];
+ }
+ }
+ unset($metadata['logos']);
+
+ $subDataTables = $dataTable->getArray();
+ $firstDataTable = reset($subDataTables);
+ $firstDataTableRow = $firstDataTable->getFirstRow();
+ $lastDataTable = end($subDataTables);
+ $lastDataTableRow = $lastDataTable->getFirstRow();
+
+ // Process min/max values
+ $firstNonZeroFound = array();
+ foreach ($subDataTables as $subDataTable) {
+ // $subDataTable is the report for one period, it has only one row
+ $firstRow = $subDataTable->getFirstRow();
+ foreach ($metadata['metrics'] as $metric => $label) {
+ $value = $firstRow ? floatval($firstRow->getColumn($metric)) : 0;
+ if ($value > 0) {
+ $firstNonZeroFound[$metric] = true;
+ } else if (!isset($firstNonZeroFound[$metric])) {
+ continue;
+ }
+ if (!isset($metricsResult[$metric]['min'])
+ || $metricsResult[$metric]['min'] > $value
+ ) {
+ $metricsResult[$metric]['min'] = $value;
+ }
+ if (!isset($metricsResult[$metric]['max'])
+ || $metricsResult[$metric]['max'] < $value
+ ) {
+ $metricsResult[$metric]['max'] = $value;
+ }
+ }
+ }
+
+ // Process % change between first/last values
+ foreach ($metadata['metrics'] as $metric => $label) {
+ $first = $firstDataTableRow ? floatval($firstDataTableRow->getColumn($metric)) : 0;
+ $last = $lastDataTableRow ? floatval($lastDataTableRow->getColumn($metric)) : 0;
+
+ // do not calculate evolution if the first value is 0 (to avoid divide-by-zero)
+ if ($first == 0) {
+ continue;
+ }
+
+ $change = Piwik_DataTable_Filter_CalculateEvolutionFilter::calculate($last, $first, $quotientPrecision = 0);
$change = Piwik_DataTable_Filter_CalculateEvolutionFilter::prependPlusSignToNumber($change);
- $metricsResult[$metric]['change'] = $change;
- }
-
- $metadata['metrics'] = $metricsResult;
- }
-
- /** Get row evolution for a multiple labels */
- private function getMultiRowEvolution($dataTable, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $column, $language=false, $idGoal=false, $legendAppendMetric=true, $labelUseAbsoluteUrl=true)
- {
- $actualLabels = $logos = array();
-
- $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal);
-
- if (!isset($metadata['metrics'][$column]))
- {
- // invalid column => use the first one that's available
- $metrics = array_keys($metadata['metrics']);
- $column = reset($metrics);
- }
-
- // get the processed label and logo (if any) for every requested label
- $actualLabels = $logos = array();
- foreach ($labels as $labelIdx => $label)
- {
- foreach ($dataTable->getArray() as $table)
- {
- // find row for this label. LabelFilter will add empty rows and
- // keep them ordered in the same way the labels array is, so we
- // assume the $labelIdx is also the row Id
- $labelRow = $table->getRowFromId($labelIdx);
-
- if ($labelRow)
- {
- $actualLabels[$labelIdx] = $this->getRowUrlForEvolutionLabel(
- $labelRow, $apiModule, $apiAction, $labelUseAbsoluteUrl);
-
- $logos[$labelIdx] = $labelRow->getMetadata('logo');
-
- if (!empty($actualLabels[$labelIdx]))
- {
- break;
- }
- }
- }
-
- if (empty($actualLabels[$labelIdx]))
- {
- $actualLabels[$labelIdx] = $this->cleanOriginalLabel($label);
- }
- }
-
- // convert rows to be array($column.'_'.$labelIdx => $value) as opposed to
- // array('label' => $label, 'column' => $value).
- $dataTableMulti = $dataTable->getEmptyClone();
- foreach ($dataTable->getArray() as $tableLabel => $table)
- {
- $newRow = new Piwik_DataTable_Row();
-
- foreach ($table->getRows() as $rowId => $row)
- {
- $value = $row->getColumn($column);
- $value = floatVal(str_replace(',', '.', $value));
- if ($value == '')
- {
- $value = 0;
- }
-
- $newLabel = $column.'_'.$rowId; // $rowId corresponds to the label index
-
- $newRow->addColumn($newLabel, $value);
- }
-
- $newTable = $table->getEmptyClone();
- $newTable->addRow($newRow);
- $dataTableMulti->addTable($newTable, $tableLabel);
- }
-
- // the available metrics for the report are returned as metadata / columns
- $metadata['columns'] = $metadata['metrics'];
-
- // metadata / metrics should document the rows that are compared
- // this way, UI code can be reused
- $metadata['metrics'] = array();
- foreach ($actualLabels as $labelIndex => $label)
- {
- if($legendAppendMetric)
- {
- $label .= ' ('.$metadata['columns'][$column].')';
- }
- $metricName = $column.'_'.$labelIndex;
- $metadata['metrics'][$metricName] = Piwik_DataTable_Filter_SafeDecodeLabel::safeDecodeLabel($label);
-
- if(!empty($logos[$labelIndex]))
- {
- $metadata['logos'][$metricName] = $logos[$labelIndex];
- }
- }
-
- $this->enhanceRowEvolutionMetaData($metadata, $dataTableMulti);
-
- return array(
- 'column' => $column,
- 'reportData' => $dataTableMulti,
- 'metadata' => $metadata
- );
- }
-
- /**
- * Returns a prettier, more comprehensible version of a row evolution label
- * for display.
- */
- private function cleanOriginalLabel( $label )
- {
- return str_replace(Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL, ' - ', $label);
- }
-
- /**
- * Performs multiple API requests at once and returns every result.
- *
- * @param array $urls The array of API requests.
- */
- public function getBulkRequest( $urls )
- {
- if (empty($urls))
- {
- return array();
- }
-
- $urls = Piwik_Common::unsanitizeInputValues($urls);
-
- $result = array();
- foreach ($urls as $url)
- {
- $req = new Piwik_API_Request($url);
- $result[] = $req->process();
- }
- return $result;
- }
+ $metricsResult[$metric]['change'] = $change;
+ }
+
+ $metadata['metrics'] = $metricsResult;
+ }
+
+ /** Get row evolution for a multiple labels */
+ private function getMultiRowEvolution($dataTable, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $column, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true)
+ {
+ $actualLabels = $logos = array();
+
+ $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal);
+
+ if (!isset($metadata['metrics'][$column])) {
+ // invalid column => use the first one that's available
+ $metrics = array_keys($metadata['metrics']);
+ $column = reset($metrics);
+ }
+
+ // get the processed label and logo (if any) for every requested label
+ $actualLabels = $logos = array();
+ foreach ($labels as $labelIdx => $label) {
+ foreach ($dataTable->getArray() as $table) {
+ // find row for this label. LabelFilter will add empty rows and
+ // keep them ordered in the same way the labels array is, so we
+ // assume the $labelIdx is also the row Id
+ $labelRow = $table->getRowFromId($labelIdx);
+
+ if ($labelRow) {
+ $actualLabels[$labelIdx] = $this->getRowUrlForEvolutionLabel(
+ $labelRow, $apiModule, $apiAction, $labelUseAbsoluteUrl);
+
+ $logos[$labelIdx] = $labelRow->getMetadata('logo');
+
+ if (!empty($actualLabels[$labelIdx])) {
+ break;
+ }
+ }
+ }
+
+ if (empty($actualLabels[$labelIdx])) {
+ $actualLabels[$labelIdx] = $this->cleanOriginalLabel($label);
+ }
+ }
+
+ // convert rows to be array($column.'_'.$labelIdx => $value) as opposed to
+ // array('label' => $label, 'column' => $value).
+ $dataTableMulti = $dataTable->getEmptyClone();
+ foreach ($dataTable->getArray() as $tableLabel => $table) {
+ $newRow = new Piwik_DataTable_Row();
+
+ foreach ($table->getRows() as $rowId => $row) {
+ $value = $row->getColumn($column);
+ $value = floatVal(str_replace(',', '.', $value));
+ if ($value == '') {
+ $value = 0;
+ }
+
+ $newLabel = $column . '_' . $rowId; // $rowId corresponds to the label index
+
+ $newRow->addColumn($newLabel, $value);
+ }
+
+ $newTable = $table->getEmptyClone();
+ $newTable->addRow($newRow);
+ $dataTableMulti->addTable($newTable, $tableLabel);
+ }
+
+ // the available metrics for the report are returned as metadata / columns
+ $metadata['columns'] = $metadata['metrics'];
+
+ // metadata / metrics should document the rows that are compared
+ // this way, UI code can be reused
+ $metadata['metrics'] = array();
+ foreach ($actualLabels as $labelIndex => $label) {
+ if ($legendAppendMetric) {
+ $label .= ' (' . $metadata['columns'][$column] . ')';
+ }
+ $metricName = $column . '_' . $labelIndex;
+ $metadata['metrics'][$metricName] = Piwik_DataTable_Filter_SafeDecodeLabel::safeDecodeLabel($label);
+
+ if (!empty($logos[$labelIndex])) {
+ $metadata['logos'][$metricName] = $logos[$labelIndex];
+ }
+ }
+
+ $this->enhanceRowEvolutionMetaData($metadata, $dataTableMulti);
+
+ return array(
+ 'column' => $column,
+ 'reportData' => $dataTableMulti,
+ 'metadata' => $metadata
+ );
+ }
+
+ /**
+ * Returns a prettier, more comprehensible version of a row evolution label
+ * for display.
+ */
+ private function cleanOriginalLabel($label)
+ {
+ return str_replace(Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL, ' - ', $label);
+ }
+
+ /**
+ * Performs multiple API requests at once and returns every result.
+ *
+ * @param array $urls The array of API requests.
+ */
+ public function getBulkRequest($urls)
+ {
+ if (empty($urls)) {
+ return array();
+ }
+
+ $urls = Piwik_Common::unsanitizeInputValues($urls);
+
+ $result = array();
+ foreach ($urls as $url) {
+ $req = new Piwik_API_Request($url);
+ $result[] = $req->process();
+ }
+ return $result;
+ }
}
diff --git a/plugins/API/Controller.php b/plugins/API/Controller.php
index 134b055715..6c3a5424fa 100644
--- a/plugins/API/Controller.php
+++ b/plugins/API/Controller.php
@@ -1,109 +1,105 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_API
*/
/**
- *
+ *
* @package Piwik_API
*/
class Piwik_API_Controller extends Piwik_Controller
{
- function index()
- {
- // when calling the API through http, we limit the number of returned results
- if(!isset($_GET['filter_limit']))
- {
- $_GET['filter_limit'] = Piwik_Config::getInstance()->General['API_datatable_default_limit'];
- }
- $request = new Piwik_API_Request('token_auth='.Piwik_Common::getRequestVar('token_auth', 'anonymous', 'string'));
- echo $request->process();
- }
+ function index()
+ {
+ // when calling the API through http, we limit the number of returned results
+ if (!isset($_GET['filter_limit'])) {
+ $_GET['filter_limit'] = Piwik_Config::getInstance()->General['API_datatable_default_limit'];
+ }
+ $request = new Piwik_API_Request('token_auth=' . Piwik_Common::getRequestVar('token_auth', 'anonymous', 'string'));
+ echo $request->process();
+ }
- public function listAllMethods()
- {
- $ApiDocumentation = new Piwik_API_DocumentationGenerator();
- echo $ApiDocumentation->getAllInterfaceString( $outputExampleUrls = true, $prefixUrls = Piwik_Common::getRequestVar('prefixUrl', '') );
- }
-
- public function listAllAPI()
- {
- $view = Piwik_View::factory("listAllAPI");
- $this->setGeneralVariablesView($view);
-
- $ApiDocumentation = new Piwik_API_DocumentationGenerator();
- $view->countLoadedAPI = Piwik_API_Proxy::getInstance()->getCountRegisteredClasses();
- $view->list_api_methods_with_links = $ApiDocumentation->getAllInterfaceString();
- echo $view->render();
- }
-
- public function listSegments()
- {
- $segments = Piwik_API_API::getInstance()->getSegmentsMetadata($this->idSite);
-
- $tableDimensions = $tableMetrics = '';
- $customVariables=0;
- $lastCategory=array();
- foreach($segments as $segment)
- {
- $onlyDisplay = array('customVariableName1', 'customVariableName2', 'customVariableValue1', 'customVariableValue2', 'customVariablePageName1', 'customVariablePageValue1');
- $customVariableWillBeDisplayed = in_array($segment['segment'], $onlyDisplay);
- // Don't display more than 4 custom variables name/value rows
- if($segment['category'] == 'Custom Variables'
- && !$customVariableWillBeDisplayed)
- {
- continue;
- }
-
- $thisCategory = $segment['category'];
- $output = '';
- if(empty($lastCategory[$segment['type']])
- || $lastCategory[$segment['type']] != $thisCategory)
- {
- $output .= '<tr><td class="segmentCategory" colspan="2"><b>'.$thisCategory.'</b></td></tr>';
- }
-
- $lastCategory[$segment['type']] = $thisCategory;
-
- $exampleValues = isset($segment['acceptedValues'])
- ? 'Example values: <code>'.$segment['acceptedValues'].'</code>'
- : '';
- $restrictedToAdmin = isset($segment['permission']) ? '<br/>Note: This segment can only be used by an Admin user' : '';
- $output .= '<tr>
- <td class="segmentString">'.$segment['segment'].'</td>
- <td class="segmentName">'.$segment['name'] .$restrictedToAdmin.'<br/>'.$exampleValues.' </td>
+ public function listAllMethods()
+ {
+ $ApiDocumentation = new Piwik_API_DocumentationGenerator();
+ echo $ApiDocumentation->getAllInterfaceString($outputExampleUrls = true, $prefixUrls = Piwik_Common::getRequestVar('prefixUrl', ''));
+ }
+
+ public function listAllAPI()
+ {
+ $view = Piwik_View::factory("listAllAPI");
+ $this->setGeneralVariablesView($view);
+
+ $ApiDocumentation = new Piwik_API_DocumentationGenerator();
+ $view->countLoadedAPI = Piwik_API_Proxy::getInstance()->getCountRegisteredClasses();
+ $view->list_api_methods_with_links = $ApiDocumentation->getAllInterfaceString();
+ echo $view->render();
+ }
+
+ public function listSegments()
+ {
+ $segments = Piwik_API_API::getInstance()->getSegmentsMetadata($this->idSite);
+
+ $tableDimensions = $tableMetrics = '';
+ $customVariables = 0;
+ $lastCategory = array();
+ foreach ($segments as $segment) {
+ $onlyDisplay = array('customVariableName1', 'customVariableName2', 'customVariableValue1', 'customVariableValue2', 'customVariablePageName1', 'customVariablePageValue1');
+ $customVariableWillBeDisplayed = in_array($segment['segment'], $onlyDisplay);
+ // Don't display more than 4 custom variables name/value rows
+ if ($segment['category'] == 'Custom Variables'
+ && !$customVariableWillBeDisplayed
+ ) {
+ continue;
+ }
+
+ $thisCategory = $segment['category'];
+ $output = '';
+ if (empty($lastCategory[$segment['type']])
+ || $lastCategory[$segment['type']] != $thisCategory
+ ) {
+ $output .= '<tr><td class="segmentCategory" colspan="2"><b>' . $thisCategory . '</b></td></tr>';
+ }
+
+ $lastCategory[$segment['type']] = $thisCategory;
+
+ $exampleValues = isset($segment['acceptedValues'])
+ ? 'Example values: <code>' . $segment['acceptedValues'] . '</code>'
+ : '';
+ $restrictedToAdmin = isset($segment['permission']) ? '<br/>Note: This segment can only be used by an Admin user' : '';
+ $output .= '<tr>
+ <td class="segmentString">' . $segment['segment'] . '</td>
+ <td class="segmentName">' . $segment['name'] . $restrictedToAdmin . '<br/>' . $exampleValues . ' </td>
</tr>';
-
- // Show only 2 custom variables and display message for rest
- if($customVariableWillBeDisplayed)
- {
- $customVariables++;
- if($customVariables == count($onlyDisplay))
- {
- $output .= '<tr><td colspan="2"> There are 5 custom variables available, so you can segment across any segment name and value range.
+
+ // Show only 2 custom variables and display message for rest
+ if ($customVariableWillBeDisplayed) {
+ $customVariables++;
+ if ($customVariables == count($onlyDisplay)) {
+ $output .= '<tr><td colspan="2"> There are 5 custom variables available, so you can segment across any segment name and value range.
<br/>For example, <code>customVariableName1==Type;customVariableValue1==Customer</code>
<br/>Returns all visitors that have the Custom Variable "Type" set to "Customer".
<br/>Custom Variables of scope "page" can be queried separately. For example, to query the Custom Variable of scope "page",
<br/>stored in index 1, you would use the segment <code>customVariablePageName1==ArticleLanguage;customVariablePageValue1==FR</code>
</td></tr>';
- }
- }
-
-
- if($segment['type'] == 'dimension') {
- $tableDimensions .= $output;
- } else {
- $tableMetrics .= $output;
- }
- }
-
- echo "
+ }
+ }
+
+
+ if ($segment['type'] == 'dimension') {
+ $tableDimensions .= $output;
+ } else {
+ $tableMetrics .= $output;
+ }
+ }
+
+ echo "
<b>Dimensions</b>
<table>
$tableDimensions
@@ -114,5 +110,5 @@ class Piwik_API_Controller extends Piwik_Controller
$tableMetrics
</table>
";
- }
+ }
}
diff --git a/plugins/API/css/styles.css b/plugins/API/css/styles.css
index 2b2f547da6..06a3c9f435 100644
--- a/plugins/API/css/styles.css
+++ b/plugins/API/css/styles.css
@@ -1,45 +1,48 @@
-
#token_auth {
- background-color:#E8FFE9;
- border:1px solid #00CC3A;
- margin: 0 0 16px 8px;
- padding: 12px;
- line-height:4em;
+ background-color: #E8FFE9;
+ border: 1px solid #00CC3A;
+ margin: 0 0 16px 8px;
+ padding: 12px;
+ line-height: 4em;
}
+
.example, .example A {
- color:#9E9E9E;
+ color: #9E9E9E;
}
-.page_api{
- padding:0 15px 0 15px;
- font-size:13px;
+.page_api {
+ padding: 0 15px 0 15px;
+ font-size: 13px;
}
.page_api h2 {
- border-bottom:1px solid #DADADA;
- margin:10px -15px 15px 0;
- padding:0 0 5px 0;
- font-size:24px;
+ border-bottom: 1px solid #DADADA;
+ margin: 10px -15px 15px 0;
+ padding: 0 0 5px 0;
+ font-size: 24px;
}
.page_api p {
- line-height:140%;
- padding-bottom:20px;
+ line-height: 140%;
+ padding-bottom: 20px;
}
.apiFirstLine {
- font-weight:bold;
- padding-bottom:10px;
+ font-weight: bold;
+ padding-bottom: 10px;
}
+
.page_api ul {
list-style: disc outside none;
margin-left: 25px;
}
+
.apiDescription {
- line-height:1.5em;
- padding-bottom:1em;
+ line-height: 1.5em;
+ padding-bottom: 1em;
}
+
.apiMethod {
- margin-bottom:5px;
- margin-left:20px;
+ margin-bottom: 5px;
+ margin-left: 20px;
} \ No newline at end of file
diff --git a/plugins/API/templates/listAllAPI.tpl b/plugins/API/templates/listAllAPI.tpl
index d59d651500..70f02ef4a9 100644
--- a/plugins/API/templates/listAllAPI.tpl
+++ b/plugins/API/templates/listAllAPI.tpl
@@ -7,24 +7,28 @@
<div class="top_controls_inner">
{include file="CoreHome/templates/period_select.tpl"}
</div>
-
+
<h2>{'API_QuickDocumentationTitle'|translate}</h2>
+
<p>{'API_PluginDescription'|translate}</p>
-
+
{if $isSuperUser}
<p>{'API_GenerateVisits'|translate:'VisitorGenerator':'VisitorGenerator'}</p>
{/if}
-
- <p><b>{'API_MoreInformation'|translate:"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/analytics-api'>":"</a>":"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/analytics-api/reference'>":"</a>"}</b></p>
-
+
+ <p>
+ <b>{'API_MoreInformation'|translate:"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/analytics-api'>":"</a>":"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/analytics-api/reference'>":"</a>"}</b>
+ </p>
+
<h2>{'API_UserAuthentication'|translate}</h2>
+
<p>
- {'API_UsingTokenAuth'|translate:'<b>':'</b>':""}<br />
- <span id='token_auth'>&amp;token_auth=<b>{$token_auth}</b></span><br />
- {'API_KeepTokenSecret'|translate:'<b>':'</b>'}
- <!-- {'API_LoadedAPIs'|translate:$countLoadedAPI} -->
- {$list_api_methods_with_links}
- <br />
+ {'API_UsingTokenAuth'|translate:'<b>':'</b>':""}<br/>
+ <span id='token_auth'>&amp;token_auth=<b>{$token_auth}</b></span><br/>
+ {'API_KeepTokenSecret'|translate:'<b>':'</b>'}
+ <!-- {'API_LoadedAPIs'|translate:$countLoadedAPI} -->
+ {$list_api_methods_with_links}
+ <br/>
</div>
{include file="CoreHome/templates/footer.tpl"}
diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php
index bb0367c742..060912f19f 100644
--- a/plugins/Actions/API.php
+++ b/plugins/Actions/API.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Actions
*/
@@ -12,543 +12,518 @@
/**
* The Actions API lets you request reports for all your Visitor Actions: Page URLs, Page titles (Piwik Events),
* File Downloads and Clicks on external websites.
- *
+ *
* For example, "getPageTitles" will return all your page titles along with standard <a href='http://piwik.org/docs/analytics-api/reference/#toc-metric-definitions' target='_blank'>Actions metrics</a> for each row.
- *
- * It is also possible to request data for a specific Page Title with "getPageTitle"
- * and setting the parameter pageName to the page title you wish to request.
- * Similarly, you can request metrics for a given Page URL via "getPageUrl", a Download file via "getDownload"
+ *
+ * It is also possible to request data for a specific Page Title with "getPageTitle"
+ * and setting the parameter pageName to the page title you wish to request.
+ * Similarly, you can request metrics for a given Page URL via "getPageUrl", a Download file via "getDownload"
* and an outlink via "getOutlink".
- *
+ *
* Note: pageName, pageUrl, outlinkUrl, downloadUrl parameters must be URL encoded before you call the API.
* @package Piwik_Actions
*/
class Piwik_Actions_API
{
- static private $instance = null;
-
- /**
- * @return Piwik_Actions_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
-
- /**
- * Backward compatibility. Fallsback to getPageTitles() instead.
- * @deprecated Deprecated since Piwik 0.5
- * @ignore
- *
- * @param int $idSite
- * @param string $period
- * @param $date
- * @param bool $segment
- * @param bool $expanded
- * @param bool|int $idSubtable
- * @return Piwik_DataTable|Piwik_DataTable_Array
- */
- public function getActions( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false )
- {
- return $this->getPageTitles( $idSite, $period, $date, $segment, $expanded, $idSubtable );
- }
-
- /**
- * Returns the list of metrics (pages, downloads, outlinks)
- *
- * @param int $idSite
- * @param string $period
- * @param string $date
- * @param bool|string $segment
- * @param bool|array $columns
- * @return Piwik_DataTable
- */
- public function get( $idSite, $period, $date, $segment = false, $columns = false)
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build( $idSite, $period, $date, $segment );
-
- $metrics = array(
- 'Actions_nb_pageviews' => 'nb_pageviews',
- 'Actions_nb_uniq_pageviews' => 'nb_uniq_pageviews',
- 'Actions_nb_downloads' => 'nb_downloads',
- 'Actions_nb_uniq_downloads' => 'nb_uniq_downloads',
- 'Actions_nb_outlinks' => 'nb_outlinks',
- 'Actions_nb_uniq_outlinks' => 'nb_uniq_outlinks',
- 'Actions_nb_searches' => 'nb_searches',
- 'Actions_nb_keywords' => 'nb_keywords',
- );
-
- // get requested columns
- $columns = Piwik::getArrayFromApiParameter($columns);
- if(!empty($columns))
- {
- // get the columns that are available and requested
- $columns = array_intersect($columns, array_values($metrics));
- $columns = array_values($columns); // make sure indexes are right
- $nameReplace = array();
- foreach ($columns as $i => $column)
- {
- $fullColumn = array_search($column, $metrics);
- $columns[$i] = $fullColumn;
- $nameReplace[$fullColumn] = $column;
- }
- }
- else
- {
- // get all columns
- $columns = array_keys($metrics);
- $nameReplace = &$metrics;
- }
-
- $table = $archive->getDataTableFromNumeric($columns);
-
- // replace labels (remove Actions_)
- $table->filter('ReplaceColumnNames', array($nameReplace));
-
- return $table;
- }
+ static private $instance = null;
/**
- * @param int $idSite
- * @param string $period
- * @param Piwik_Date $date
- * @param bool $segment
- * @param bool $expanded
- * @param bool $idSubtable
+ * @return Piwik_Actions_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+
+ /**
+ * Backward compatibility. Fallsback to getPageTitles() instead.
+ * @deprecated Deprecated since Piwik 0.5
+ * @ignore
*
+ * @param int $idSite
+ * @param string $period
+ * @param $date
+ * @param bool $segment
+ * @param bool $expanded
+ * @param bool|int $idSubtable
* @return Piwik_DataTable|Piwik_DataTable_Array
*/
- public function getPageUrls( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false )
- {
- $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $idSubtable );
- $this->filterPageDatatable($dataTable);
- $this->filterActionsDataTable($dataTable, $expanded);
- return $dataTable;
- }
+ public function getActions($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ {
+ return $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable);
+ }
/**
- * @param int $idSite
- * @param string $period
- * @param Piwik_Date $date
- * @param bool $segment
- * @param bool $expanded
- * @param bool $idSubtable
+ * Returns the list of metrics (pages, downloads, outlinks)
+ *
+ * @param int $idSite
+ * @param string $period
+ * @param string $date
+ * @param bool|string $segment
+ * @param bool|array $columns
+ * @return Piwik_DataTable
+ */
+ public function get($idSite, $period, $date, $segment = false, $columns = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+
+ $metrics = array(
+ 'Actions_nb_pageviews' => 'nb_pageviews',
+ 'Actions_nb_uniq_pageviews' => 'nb_uniq_pageviews',
+ 'Actions_nb_downloads' => 'nb_downloads',
+ 'Actions_nb_uniq_downloads' => 'nb_uniq_downloads',
+ 'Actions_nb_outlinks' => 'nb_outlinks',
+ 'Actions_nb_uniq_outlinks' => 'nb_uniq_outlinks',
+ 'Actions_nb_searches' => 'nb_searches',
+ 'Actions_nb_keywords' => 'nb_keywords',
+ );
+
+ // get requested columns
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ if (!empty($columns)) {
+ // get the columns that are available and requested
+ $columns = array_intersect($columns, array_values($metrics));
+ $columns = array_values($columns); // make sure indexes are right
+ $nameReplace = array();
+ foreach ($columns as $i => $column) {
+ $fullColumn = array_search($column, $metrics);
+ $columns[$i] = $fullColumn;
+ $nameReplace[$fullColumn] = $column;
+ }
+ } else {
+ // get all columns
+ $columns = array_keys($metrics);
+ $nameReplace = & $metrics;
+ }
+
+ $table = $archive->getDataTableFromNumeric($columns);
+
+ // replace labels (remove Actions_)
+ $table->filter('ReplaceColumnNames', array($nameReplace));
+
+ return $table;
+ }
+
+ /**
+ * @param int $idSite
+ * @param string $period
+ * @param Piwik_Date $date
+ * @param bool $segment
+ * @param bool $expanded
+ * @param bool $idSubtable
+ *
+ * @return Piwik_DataTable|Piwik_DataTable_Array
+ */
+ public function getPageUrls($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ {
+ $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $this->filterPageDatatable($dataTable);
+ $this->filterActionsDataTable($dataTable, $expanded);
+ return $dataTable;
+ }
+
+ /**
+ * @param int $idSite
+ * @param string $period
+ * @param Piwik_Date $date
+ * @param bool $segment
+ * @param bool $expanded
+ * @param bool $idSubtable
*
* @return Piwik_DataTable|Piwik_DataTable_Array
*/
- public function getPageUrlsFollowingSiteSearch( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false )
- {
- $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable);
- $this->keepPagesFollowingSearch($dataTable);
- return $dataTable;
- }
+ public function getPageUrlsFollowingSiteSearch($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ {
+ $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $this->keepPagesFollowingSearch($dataTable);
+ return $dataTable;
+ }
/**
- * @param int $idSite
- * @param string $period
- * @param Piwik_Date $date
- * @param bool $segment
- * @param bool $expanded
- * @param bool $idSubtable
+ * @param int $idSite
+ * @param string $period
+ * @param Piwik_Date $date
+ * @param bool $segment
+ * @param bool $expanded
+ * @param bool $idSubtable
*
* @return Piwik_DataTable|Piwik_DataTable_Array
*/
- public function getPageTitlesFollowingSiteSearch( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false )
- {
- $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable);
- $this->keepPagesFollowingSearch($dataTable);
- return $dataTable;
- }
+ public function getPageTitlesFollowingSiteSearch($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ {
+ $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $this->keepPagesFollowingSearch($dataTable);
+ return $dataTable;
+ }
/**
* @param Piwik_DataTable $dataTable
*/
protected function keepPagesFollowingSearch($dataTable)
- {
- // Keep only pages which are following site search
- $dataTable->filter('ColumnCallbackDeleteRow', array(
- 'nb_hits_following_search',
- create_function('$value', 'return $value > 0;')
- ));
- }
-
- /**
- * Returns a DataTable with analytics information for every unique entry page URL, for
- * the specified site, period & segment.
- */
- public function getEntryPageUrls( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false )
- {
- $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable);
- $this->filterNonEntryActions($dataTable);
- return $dataTable;
- }
-
- /**
- * Returns a DataTable with analytics information for every unique exit page URL, for
- * the specified site, period & segment.
- */
- public function getExitPageUrls( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false )
- {
- $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable);
- $this->filterNonExitActions($dataTable);
- return $dataTable;
- }
-
- public function getPageUrl( $pageUrl, $idSite, $period, $date, $segment = false)
- {
- $callBackParameters = array('Actions_actions_url', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false );
- $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageUrl, Piwik_Tracker_Action::TYPE_ACTION_URL);
- $this->filterPageDatatable($dataTable);
- $this->filterActionsDataTable($dataTable);
- return $dataTable;
- }
-
- public function getPageTitles( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
- {
- $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_actions', $idSite, $period, $date, $segment, $expanded, $idSubtable);
- $this->filterPageDatatable($dataTable);
- $this->filterActionsDataTable($dataTable, $expanded);
- return $dataTable;
- }
-
- /**
- * Returns a Piwik_DataTable with analytics information for every unique entry page title
- * for the given site, time period & segment.
- */
- public function getEntryPageTitles( $idSite, $period, $date, $segment = false, $expanded = false,
- $idSubtable = false )
- {
- $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable);
- $this->filterNonEntryActions($dataTable);
- return $dataTable;
- }
-
- /**
- * Returns a Piwik_DataTable with analytics information for every unique exit page title
- * for the given site, time period & segment.
- */
- public function getExitPageTitles( $idSite, $period, $date, $segment = false, $expanded = false,
- $idSubtable = false )
- {
- $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable);
- $this->filterNonExitActions($dataTable);
- return $dataTable;
- }
-
- public function getPageTitle( $pageName, $idSite, $period, $date, $segment = false)
- {
- $callBackParameters = array('Actions_actions', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false );
- $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageName, Piwik_Tracker_Action::TYPE_ACTION_NAME);
- $this->filterPageDatatable($dataTable);
- $this->filterActionsDataTable($dataTable);
- return $dataTable;
- }
-
- public function getDownloads( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false )
- {
- $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_downloads', $idSite, $period, $date, $segment, $expanded, $idSubtable );
- $this->filterActionsDataTable($dataTable, $expanded);
- return $dataTable;
- }
-
- public function getDownload( $downloadUrl, $idSite, $period, $date, $segment = false)
- {
- $callBackParameters = array('Actions_downloads', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false );
- $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $downloadUrl, Piwik_Tracker_Action::TYPE_DOWNLOAD);
- $this->filterActionsDataTable($dataTable);
- return $dataTable;
- }
-
- public function getOutlinks( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false )
- {
- $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_outlink', $idSite, $period, $date, $segment, $expanded, $idSubtable );
- $this->filterActionsDataTable($dataTable, $expanded);
- return $dataTable;
- }
-
- public function getOutlink( $outlinkUrl, $idSite, $period, $date, $segment = false)
- {
- $callBackParameters = array('Actions_outlink', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false );
- $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $outlinkUrl, Piwik_Tracker_Action::TYPE_OUTLINK);
- $this->filterActionsDataTable($dataTable);
- return $dataTable;
- }
-
- public function getSiteSearchKeywords( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment);
- $dataTable->deleteColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT);
- $this->filterPageDatatable($dataTable);
- $this->filterActionsDataTable($dataTable);
- $this->addPagesPerSearchColumn($dataTable);
- return $dataTable;
- }
-
- //Visitors can search, and then click "next" to view more results. This is the average number of search results pages viewed for this keyword.
- public function addPagesPerSearchColumn($dataTable, $columnToRead = 'nb_hits')
- {
- $dataTable->filter('ColumnCallbackAddColumnQuotient', array('nb_pages_per_search', $columnToRead, 'nb_visits', $precision = 1));
- }
-
- protected function getSiteSearchKeywordsRaw($idSite, $period, $date, $segment)
- {
- $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_sitesearch', $idSite, $period, $date, $segment, $expanded = false);
- return $dataTable;
- }
-
- public function getSiteSearchNoResultKeywords( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment);
- // Delete all rows that have some results
- $dataTable->filter('ColumnCallbackDeleteRow',
- array(
- Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT,
- create_function ( '$value', 'return $value >= 1;')
- ));
- $dataTable->deleteRow(Piwik_DataTable::ID_SUMMARY_ROW);
- $dataTable->deleteColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT);
- $this->filterPageDatatable($dataTable);
- $this->filterActionsDataTable($dataTable);
- $this->addPagesPerSearchColumn($dataTable);
- return $dataTable;
- }
+ {
+ // Keep only pages which are following site search
+ $dataTable->filter('ColumnCallbackDeleteRow', array(
+ 'nb_hits_following_search',
+ create_function('$value', 'return $value > 0;')
+ ));
+ }
+
+ /**
+ * Returns a DataTable with analytics information for every unique entry page URL, for
+ * the specified site, period & segment.
+ */
+ public function getEntryPageUrls($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ {
+ $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $this->filterNonEntryActions($dataTable);
+ return $dataTable;
+ }
/**
- * @param int $idSite
- * @param string $period
- * @param Piwik_Date $date
- * @param bool $segment
+ * Returns a DataTable with analytics information for every unique exit page URL, for
+ * the specified site, period & segment.
+ */
+ public function getExitPageUrls($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ {
+ $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $this->filterNonExitActions($dataTable);
+ return $dataTable;
+ }
+
+ public function getPageUrl($pageUrl, $idSite, $period, $date, $segment = false)
+ {
+ $callBackParameters = array('Actions_actions_url', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
+ $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageUrl, Piwik_Tracker_Action::TYPE_ACTION_URL);
+ $this->filterPageDatatable($dataTable);
+ $this->filterActionsDataTable($dataTable);
+ return $dataTable;
+ }
+
+ public function getPageTitles($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ {
+ $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_actions', $idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $this->filterPageDatatable($dataTable);
+ $this->filterActionsDataTable($dataTable, $expanded);
+ return $dataTable;
+ }
+
+ /**
+ * Returns a Piwik_DataTable with analytics information for every unique entry page title
+ * for the given site, time period & segment.
+ */
+ public function getEntryPageTitles($idSite, $period, $date, $segment = false, $expanded = false,
+ $idSubtable = false)
+ {
+ $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $this->filterNonEntryActions($dataTable);
+ return $dataTable;
+ }
+
+ /**
+ * Returns a Piwik_DataTable with analytics information for every unique exit page title
+ * for the given site, time period & segment.
+ */
+ public function getExitPageTitles($idSite, $period, $date, $segment = false, $expanded = false,
+ $idSubtable = false)
+ {
+ $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $this->filterNonExitActions($dataTable);
+ return $dataTable;
+ }
+
+ public function getPageTitle($pageName, $idSite, $period, $date, $segment = false)
+ {
+ $callBackParameters = array('Actions_actions', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
+ $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageName, Piwik_Tracker_Action::TYPE_ACTION_NAME);
+ $this->filterPageDatatable($dataTable);
+ $this->filterActionsDataTable($dataTable);
+ return $dataTable;
+ }
+
+ public function getDownloads($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ {
+ $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_downloads', $idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $this->filterActionsDataTable($dataTable, $expanded);
+ return $dataTable;
+ }
+
+ public function getDownload($downloadUrl, $idSite, $period, $date, $segment = false)
+ {
+ $callBackParameters = array('Actions_downloads', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
+ $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $downloadUrl, Piwik_Tracker_Action::TYPE_DOWNLOAD);
+ $this->filterActionsDataTable($dataTable);
+ return $dataTable;
+ }
+
+ public function getOutlinks($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ {
+ $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_outlink', $idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $this->filterActionsDataTable($dataTable, $expanded);
+ return $dataTable;
+ }
+
+ public function getOutlink($outlinkUrl, $idSite, $period, $date, $segment = false)
+ {
+ $callBackParameters = array('Actions_outlink', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
+ $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $outlinkUrl, Piwik_Tracker_Action::TYPE_OUTLINK);
+ $this->filterActionsDataTable($dataTable);
+ return $dataTable;
+ }
+
+ public function getSiteSearchKeywords($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment);
+ $dataTable->deleteColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT);
+ $this->filterPageDatatable($dataTable);
+ $this->filterActionsDataTable($dataTable);
+ $this->addPagesPerSearchColumn($dataTable);
+ return $dataTable;
+ }
+
+ //Visitors can search, and then click "next" to view more results. This is the average number of search results pages viewed for this keyword.
+ public function addPagesPerSearchColumn($dataTable, $columnToRead = 'nb_hits')
+ {
+ $dataTable->filter('ColumnCallbackAddColumnQuotient', array('nb_pages_per_search', $columnToRead, 'nb_visits', $precision = 1));
+ }
+
+ protected function getSiteSearchKeywordsRaw($idSite, $period, $date, $segment)
+ {
+ $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_sitesearch', $idSite, $period, $date, $segment, $expanded = false);
+ return $dataTable;
+ }
+
+ public function getSiteSearchNoResultKeywords($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment);
+ // Delete all rows that have some results
+ $dataTable->filter('ColumnCallbackDeleteRow',
+ array(
+ Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT,
+ create_function('$value', 'return $value >= 1;')
+ ));
+ $dataTable->deleteRow(Piwik_DataTable::ID_SUMMARY_ROW);
+ $dataTable->deleteColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT);
+ $this->filterPageDatatable($dataTable);
+ $this->filterActionsDataTable($dataTable);
+ $this->addPagesPerSearchColumn($dataTable);
+ return $dataTable;
+ }
+
+ /**
+ * @param int $idSite
+ * @param string $period
+ * @param Piwik_Date $date
+ * @param bool $segment
*
* @return Piwik_DataTable|Piwik_DataTable_Array
*/
- public function getSiteSearchCategories( $idSite, $period, $date, $segment = false )
- {
- Piwik_Actions::checkCustomVariablesPluginEnabled();
- $customVariables = Piwik_CustomVariables_API::getInstance()->getCustomVariables($idSite, $period, $date, $segment, $expanded = false, $_leavePiwikCoreVariables = true);
-
- $customVarNameToLookFor = Piwik_Tracker_Action::CVAR_KEY_SEARCH_CATEGORY;
-
- $dataTable = new Piwik_DataTable();
- // Handle case where date=last30&period=day
- // TODO: this logic should really be refactored somewhere, this is ugly!
- if($customVariables instanceof Piwik_DataTable_Array)
- {
- $dataTable = $customVariables->getEmptyClone();
-
- $customVariableDatatables = $customVariables->getArray();
- $dataTables = $dataTable->getArray();
- foreach($customVariableDatatables as $key => $customVariableTableForDate)
- {
- // we do not enter the IF, in the case idSite=1,3 AND period=day&date=datefrom,dateto,
- if(isset($customVariableTableForDate->metadata['period']))
- {
- $row = $customVariableTableForDate->getRowFromLabel($customVarNameToLookFor);
- if($row)
- {
- $dateRewrite = $customVariableTableForDate->metadata['period']->getDateStart()->toString();
- $idSubtable = $row->getIdSubDataTable();
- $categories = Piwik_CustomVariables_API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $dateRewrite, $idSubtable, $segment);
- $dataTable->addTable($categories, $key);
- }
- }
- }
- }
- elseif($customVariables instanceof Piwik_DataTable)
- {
- $row = $customVariables->getRowFromLabel($customVarNameToLookFor);
- if($row)
- {
- $idSubtable = $row->getIdSubDataTable();
- $dataTable = Piwik_CustomVariables_API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $date, $idSubtable, $segment);
- }
- }
- $this->filterActionsDataTable($dataTable);
- $this->addPagesPerSearchColumn($dataTable, $columnToRead = 'nb_actions');
- return $dataTable;
- }
-
- /**
- * Will search in the DataTable for a Label matching the searched string
- * and return only the matching row, or an empty datatable
- */
- protected function getFilterPageDatatableSearch($callBackParameters, $search, $actionType, $table = false,
- $searchTree = false)
- {
- if ($searchTree === false)
- {
- // build the query parts that are searched inside the tree
- if($actionType == Piwik_Tracker_Action::TYPE_ACTION_NAME)
- {
- $searchedString = Piwik_Common::unsanitizeInputValue($search);
- }
- else
- {
- $idSite = $callBackParameters[1];
- try {
- $searchedString = Piwik_Tracker_Action::excludeQueryParametersFromUrl($search, $idSite);
- } catch(Exception $e) {
- $searchedString = $search;
- }
- }
- Piwik_Actions_ArchivingHelper::reloadConfig();
- $searchTree = Piwik_Actions_ArchivingHelper::getActionExplodedNames($searchedString, $actionType);
- }
-
- if ($table === false)
- {
- // fetch the data table
- $table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters);
-
- if ($table instanceof Piwik_DataTable_Array)
- {
- // search an array of tables, e.g. when using date=last30
- // note that if the root is an array, we filter all children
- // if an array occurs inside the nested table, we only look for the first match (see below)
- $newTableArray = $table->getEmptyClone();
-
- foreach ($table->getArray() as $label => $subTable)
- {
- $newSubTable = $this->doFilterPageDatatableSearch($callBackParameters, $subTable, $searchTree);
-
- $newTableArray->addTable($newSubTable, $label);
- }
-
- return $newTableArray;
- }
-
- }
-
- return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree);
- }
-
- /**
- * This looks very similar to LabelFilter.php should it be refactored somehow? FIXME
- */
- protected function doFilterPageDatatableSearch($callBackParameters, $table, $searchTree)
- {
- // filter a data table array
- if ($table instanceof Piwik_DataTable_Array)
- {
- foreach ($table->getArray() as $subTable)
- {
- $filteredSubTable = $this->doFilterPageDatatableSearch($callBackParameters, $subTable, $searchTree);
-
- if ($filteredSubTable->getRowsCount() > 0)
- {
- // match found in a sub table, return and stop searching the others
- return $filteredSubTable;
- }
- }
-
- // nothing found in all sub tables
- return new Piwik_DataTable;
- }
-
- // filter regular data table
- if ($table instanceof Piwik_DataTable)
- {
- // search for the first part of the tree search
- $search = array_shift($searchTree);
- $row = $table->getRowFromLabel($search);
- if ($row === false)
- {
- // not found
- $result = new Piwik_DataTable;
- $result->metadata = $table->metadata;
- return $result;
- }
-
- // end of tree search reached
- if (count($searchTree) == 0)
- {
- $result = new Piwik_DataTable();
- $result->addRow($row);
- $result->metadata = $table->metadata;
- return $result;
- }
-
- // match found on this level and more levels remaining: go deeper
- $idSubTable = $row->getIdSubDataTable();
- $callBackParameters[6] = $idSubTable;
- $table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters);
- return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree);
- }
-
- throw new Exception("For this API function, DataTable ".get_class($table)." is not supported");
- }
-
- /**
- * Common filters for Page URLs and Page Titles
- */
- protected function filterPageDatatable($dataTable)
- {
- // Average time on page = total time on page / number visits on that page
- $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_time_on_page', 'sum_time_spent', 'nb_visits', 0));
-
- // Bounce rate = single page visits on this page / visits started on this page
- $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('bounce_rate', 'entry_bounce_count', 'entry_nb_visits', 0));
-
- // % Exit = Number of visits that finished on this page / visits on this page
- $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('exit_rate', 'exit_nb_visits', 'nb_visits', 0));
-
- // Handle performance analytics
- $hasTimeGeneration = (array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION)) > 0);
- if ($hasTimeGeneration) {
- // Average generation time = total generation time / number of pageviews
- $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_time_generation', 'sum_time_generation', 'nb_hits_with_time_generation', 3));
- } else {
- // No generation time: remove it from the API output and add it to empty_columns metadata, so that
- // the columns can also be removed from the view
- $dataTable->filter('ColumnDelete', array(array(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION)));
- if ($dataTable instanceof Piwik_DataTable) {
- $emptyColumns = $dataTable->getMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME);
- if (!is_array($emptyColumns)) {
- $emptyColumns = array();
- }
- $emptyColumns[] = 'sum_time_generation';
- $emptyColumns[] = 'avg_time_generation';
- $dataTable->setMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME, $emptyColumns);
- }
- }
- }
-
- /**
- * Common filters for all Actions API getters
- */
- protected function filterActionsDataTable($dataTable, $expanded = false)
- {
- // Must be applied before Sort in this case, since the DataTable can contain both int and strings indexes
- // (in the transition period between pre 1.2 and post 1.2 datatable structure)
- $dataTable->filter('ReplaceColumnNames');
- $dataTable->filter('Sort', array('nb_visits', 'desc', $naturalSort = false, $expanded));
-
- $dataTable->queueFilter('ReplaceSummaryRowLabel');
- }
-
- /**
- * Removes DataTable rows referencing actions that were never the first action of a visit.
- *
- * @param Piwik_DataTable $dataTable
- */
- private function filterNonEntryActions( $dataTable )
- {
- $dataTable->filter('ColumnCallbackDeleteRow', array('entry_nb_visits', 'strlen'));
- }
-
- /**
- * Removes DataTable rows referencing actions that were never the last action of a visit.
- *
- * @param Piwik_DataTable $dataTable
- */
- private function filterNonExitActions( $dataTable )
- {
- $dataTable->filter('ColumnCallbackDeleteRow', array('exit_nb_visits', 'strlen'));
- }
+ public function getSiteSearchCategories($idSite, $period, $date, $segment = false)
+ {
+ Piwik_Actions::checkCustomVariablesPluginEnabled();
+ $customVariables = Piwik_CustomVariables_API::getInstance()->getCustomVariables($idSite, $period, $date, $segment, $expanded = false, $_leavePiwikCoreVariables = true);
+
+ $customVarNameToLookFor = Piwik_Tracker_Action::CVAR_KEY_SEARCH_CATEGORY;
+
+ $dataTable = new Piwik_DataTable();
+ // Handle case where date=last30&period=day
+ // TODO: this logic should really be refactored somewhere, this is ugly!
+ if ($customVariables instanceof Piwik_DataTable_Array) {
+ $dataTable = $customVariables->getEmptyClone();
+
+ $customVariableDatatables = $customVariables->getArray();
+ $dataTables = $dataTable->getArray();
+ foreach ($customVariableDatatables as $key => $customVariableTableForDate) {
+ // we do not enter the IF, in the case idSite=1,3 AND period=day&date=datefrom,dateto,
+ if (isset($customVariableTableForDate->metadata['period'])) {
+ $row = $customVariableTableForDate->getRowFromLabel($customVarNameToLookFor);
+ if ($row) {
+ $dateRewrite = $customVariableTableForDate->metadata['period']->getDateStart()->toString();
+ $idSubtable = $row->getIdSubDataTable();
+ $categories = Piwik_CustomVariables_API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $dateRewrite, $idSubtable, $segment);
+ $dataTable->addTable($categories, $key);
+ }
+ }
+ }
+ } elseif ($customVariables instanceof Piwik_DataTable) {
+ $row = $customVariables->getRowFromLabel($customVarNameToLookFor);
+ if ($row) {
+ $idSubtable = $row->getIdSubDataTable();
+ $dataTable = Piwik_CustomVariables_API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $date, $idSubtable, $segment);
+ }
+ }
+ $this->filterActionsDataTable($dataTable);
+ $this->addPagesPerSearchColumn($dataTable, $columnToRead = 'nb_actions');
+ return $dataTable;
+ }
+
+ /**
+ * Will search in the DataTable for a Label matching the searched string
+ * and return only the matching row, or an empty datatable
+ */
+ protected function getFilterPageDatatableSearch($callBackParameters, $search, $actionType, $table = false,
+ $searchTree = false)
+ {
+ if ($searchTree === false) {
+ // build the query parts that are searched inside the tree
+ if ($actionType == Piwik_Tracker_Action::TYPE_ACTION_NAME) {
+ $searchedString = Piwik_Common::unsanitizeInputValue($search);
+ } else {
+ $idSite = $callBackParameters[1];
+ try {
+ $searchedString = Piwik_Tracker_Action::excludeQueryParametersFromUrl($search, $idSite);
+ } catch (Exception $e) {
+ $searchedString = $search;
+ }
+ }
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+ $searchTree = Piwik_Actions_ArchivingHelper::getActionExplodedNames($searchedString, $actionType);
+ }
+
+ if ($table === false) {
+ // fetch the data table
+ $table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters);
+
+ if ($table instanceof Piwik_DataTable_Array) {
+ // search an array of tables, e.g. when using date=last30
+ // note that if the root is an array, we filter all children
+ // if an array occurs inside the nested table, we only look for the first match (see below)
+ $newTableArray = $table->getEmptyClone();
+
+ foreach ($table->getArray() as $label => $subTable) {
+ $newSubTable = $this->doFilterPageDatatableSearch($callBackParameters, $subTable, $searchTree);
+
+ $newTableArray->addTable($newSubTable, $label);
+ }
+
+ return $newTableArray;
+ }
+
+ }
+
+ return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree);
+ }
+
+ /**
+ * This looks very similar to LabelFilter.php should it be refactored somehow? FIXME
+ */
+ protected function doFilterPageDatatableSearch($callBackParameters, $table, $searchTree)
+ {
+ // filter a data table array
+ if ($table instanceof Piwik_DataTable_Array) {
+ foreach ($table->getArray() as $subTable) {
+ $filteredSubTable = $this->doFilterPageDatatableSearch($callBackParameters, $subTable, $searchTree);
+
+ if ($filteredSubTable->getRowsCount() > 0) {
+ // match found in a sub table, return and stop searching the others
+ return $filteredSubTable;
+ }
+ }
+
+ // nothing found in all sub tables
+ return new Piwik_DataTable;
+ }
+
+ // filter regular data table
+ if ($table instanceof Piwik_DataTable) {
+ // search for the first part of the tree search
+ $search = array_shift($searchTree);
+ $row = $table->getRowFromLabel($search);
+ if ($row === false) {
+ // not found
+ $result = new Piwik_DataTable;
+ $result->metadata = $table->metadata;
+ return $result;
+ }
+
+ // end of tree search reached
+ if (count($searchTree) == 0) {
+ $result = new Piwik_DataTable();
+ $result->addRow($row);
+ $result->metadata = $table->metadata;
+ return $result;
+ }
+
+ // match found on this level and more levels remaining: go deeper
+ $idSubTable = $row->getIdSubDataTable();
+ $callBackParameters[6] = $idSubTable;
+ $table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters);
+ return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree);
+ }
+
+ throw new Exception("For this API function, DataTable " . get_class($table) . " is not supported");
+ }
+
+ /**
+ * Common filters for Page URLs and Page Titles
+ */
+ protected function filterPageDatatable($dataTable)
+ {
+ // Average time on page = total time on page / number visits on that page
+ $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_time_on_page', 'sum_time_spent', 'nb_visits', 0));
+
+ // Bounce rate = single page visits on this page / visits started on this page
+ $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('bounce_rate', 'entry_bounce_count', 'entry_nb_visits', 0));
+
+ // % Exit = Number of visits that finished on this page / visits on this page
+ $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('exit_rate', 'exit_nb_visits', 'nb_visits', 0));
+
+ // Handle performance analytics
+ $hasTimeGeneration = (array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION)) > 0);
+ if ($hasTimeGeneration) {
+ // Average generation time = total generation time / number of pageviews
+ $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_time_generation', 'sum_time_generation', 'nb_hits_with_time_generation', 3));
+ } else {
+ // No generation time: remove it from the API output and add it to empty_columns metadata, so that
+ // the columns can also be removed from the view
+ $dataTable->filter('ColumnDelete', array(array(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION)));
+ if ($dataTable instanceof Piwik_DataTable) {
+ $emptyColumns = $dataTable->getMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME);
+ if (!is_array($emptyColumns)) {
+ $emptyColumns = array();
+ }
+ $emptyColumns[] = 'sum_time_generation';
+ $emptyColumns[] = 'avg_time_generation';
+ $dataTable->setMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME, $emptyColumns);
+ }
+ }
+ }
+
+ /**
+ * Common filters for all Actions API getters
+ */
+ protected function filterActionsDataTable($dataTable, $expanded = false)
+ {
+ // Must be applied before Sort in this case, since the DataTable can contain both int and strings indexes
+ // (in the transition period between pre 1.2 and post 1.2 datatable structure)
+ $dataTable->filter('ReplaceColumnNames');
+ $dataTable->filter('Sort', array('nb_visits', 'desc', $naturalSort = false, $expanded));
+
+ $dataTable->queueFilter('ReplaceSummaryRowLabel');
+ }
+
+ /**
+ * Removes DataTable rows referencing actions that were never the first action of a visit.
+ *
+ * @param Piwik_DataTable $dataTable
+ */
+ private function filterNonEntryActions($dataTable)
+ {
+ $dataTable->filter('ColumnCallbackDeleteRow', array('entry_nb_visits', 'strlen'));
+ }
+
+ /**
+ * Removes DataTable rows referencing actions that were never the last action of a visit.
+ *
+ * @param Piwik_DataTable $dataTable
+ */
+ private function filterNonExitActions($dataTable)
+ {
+ $dataTable->filter('ColumnCallbackDeleteRow', array('exit_nb_visits', 'strlen'));
+ }
}
diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php
index 230968e66f..635b0d95c4 100644
--- a/plugins/Actions/Actions.php
+++ b/plugins/Actions/Actions.php
@@ -8,7 +8,7 @@
* @category Piwik_Plugins
* @package Piwik_Actions
*/
-
+
/**
* Actions plugin
*
@@ -18,592 +18,585 @@
*/
class Piwik_Actions extends Piwik_Plugin
{
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('Actions_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- return $info;
- }
-
-
- public function getListHooksRegistered()
- {
- $hooks = array(
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenus',
- 'API.getReportMetadata' => 'getReportMetadata',
- 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
- );
- return $hooks;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getSegmentsMetadata($notification)
- {
- $segments =& $notification->getNotificationObject();
- $sqlFilter = array($this, 'getIdActionFromSegment');
-
- // entry and exit pages of visit
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Actions_Actions',
- 'name' => 'Actions_ColumnEntryPageURL',
- 'segment' => 'entryPageUrl',
- 'sqlSegment' => 'log_visit.visit_entry_idaction_url',
- 'sqlFilter' => $sqlFilter,
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('Actions_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ return $info;
+ }
+
+
+ public function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenus',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ );
+ return $hooks;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ $segments =& $notification->getNotificationObject();
+ $sqlFilter = array($this, 'getIdActionFromSegment');
+
+ // entry and exit pages of visit
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Actions_Actions',
+ 'name' => 'Actions_ColumnEntryPageURL',
+ 'segment' => 'entryPageUrl',
+ 'sqlSegment' => 'log_visit.visit_entry_idaction_url',
+ 'sqlFilter' => $sqlFilter,
);
$segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Actions_Actions',
- 'name' => 'Actions_ColumnEntryPageTitle',
- 'segment' => 'entryPageTitle',
- 'sqlSegment' => 'log_visit.visit_entry_idaction_name',
- 'sqlFilter' => $sqlFilter,
+ 'type' => 'dimension',
+ 'category' => 'Actions_Actions',
+ 'name' => 'Actions_ColumnEntryPageTitle',
+ 'segment' => 'entryPageTitle',
+ 'sqlSegment' => 'log_visit.visit_entry_idaction_name',
+ 'sqlFilter' => $sqlFilter,
);
$segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Actions_Actions',
- 'name' => 'Actions_ColumnExitPageURL',
- 'segment' => 'exitPageUrl',
- 'sqlSegment' => 'log_visit.visit_exit_idaction_url',
- 'sqlFilter' => $sqlFilter,
+ 'type' => 'dimension',
+ 'category' => 'Actions_Actions',
+ 'name' => 'Actions_ColumnExitPageURL',
+ 'segment' => 'exitPageUrl',
+ 'sqlSegment' => 'log_visit.visit_exit_idaction_url',
+ 'sqlFilter' => $sqlFilter,
);
$segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Actions_Actions',
- 'name' => 'Actions_ColumnExitPageTitle',
- 'segment' => 'exitPageTitle',
- 'sqlSegment' => 'log_visit.visit_exit_idaction_name',
- 'sqlFilter' => $sqlFilter,
+ 'type' => 'dimension',
+ 'category' => 'Actions_Actions',
+ 'name' => 'Actions_ColumnExitPageTitle',
+ 'segment' => 'exitPageTitle',
+ 'sqlSegment' => 'log_visit.visit_exit_idaction_name',
+ 'sqlFilter' => $sqlFilter,
);
-
+
// single pages
$segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Actions_Actions',
- 'name' => 'Actions_ColumnPageURL',
- 'segment' => 'pageUrl',
- 'sqlSegment' => 'log_link_visit_action.idaction_url',
- 'sqlFilter' => $sqlFilter,
- 'acceptedValues' => "All these segments must be URL encoded, for example: ".urlencode('http://example.com/path/page?query'),
+ 'type' => 'dimension',
+ 'category' => 'Actions_Actions',
+ 'name' => 'Actions_ColumnPageURL',
+ 'segment' => 'pageUrl',
+ 'sqlSegment' => 'log_link_visit_action.idaction_url',
+ 'sqlFilter' => $sqlFilter,
+ 'acceptedValues' => "All these segments must be URL encoded, for example: " . urlencode('http://example.com/path/page?query'),
);
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Actions_Actions',
- 'name' => 'Actions_ColumnPageName',
- 'segment' => 'pageTitle',
- 'sqlSegment' => 'log_link_visit_action.idaction_name',
- 'sqlFilter' => $sqlFilter,
- );
- // TODO here could add keyword segment and hack $sqlFilter to make it select the right idaction
- }
-
- /**
- * Convert segment expression to an action ID or an SQL expression.
- *
- * This method is used as a sqlFilter-callback for the segments of this plugin.
- * Usually, these callbacks only return a value that should be compared to the
- * column in the database. In this case, that doesn't work since multiple IDs
- * can match an expression (e.g. "pageUrl=@foo").
- * @param string $string
- * @param string $sqlField
- * @param string $matchType
- * @throws Exception
- * @return array|int|string
- */
- public function getIdActionFromSegment($string, $sqlField, $matchType='==')
- {
- // Field is visit_*_idaction_url or visit_*_idaction_name
- $actionType = strpos($sqlField, '_name') === false
- ? Piwik_Tracker_Action::TYPE_ACTION_URL
- : Piwik_Tracker_Action::TYPE_ACTION_NAME;
-
- if ($actionType == Piwik_Tracker_Action::TYPE_ACTION_URL)
- {
- // for urls trim protocol and www because it is not recorded in the db
- $string = preg_replace('@^http[s]?://(www\.)?@i', '', $string);
- }
-
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Actions_Actions',
+ 'name' => 'Actions_ColumnPageName',
+ 'segment' => 'pageTitle',
+ 'sqlSegment' => 'log_link_visit_action.idaction_name',
+ 'sqlFilter' => $sqlFilter,
+ );
+ // TODO here could add keyword segment and hack $sqlFilter to make it select the right idaction
+ }
+
+ /**
+ * Convert segment expression to an action ID or an SQL expression.
+ *
+ * This method is used as a sqlFilter-callback for the segments of this plugin.
+ * Usually, these callbacks only return a value that should be compared to the
+ * column in the database. In this case, that doesn't work since multiple IDs
+ * can match an expression (e.g. "pageUrl=@foo").
+ * @param string $string
+ * @param string $sqlField
+ * @param string $matchType
+ * @throws Exception
+ * @return array|int|string
+ */
+ public function getIdActionFromSegment($string, $sqlField, $matchType = '==')
+ {
+ // Field is visit_*_idaction_url or visit_*_idaction_name
+ $actionType = strpos($sqlField, '_name') === false
+ ? Piwik_Tracker_Action::TYPE_ACTION_URL
+ : Piwik_Tracker_Action::TYPE_ACTION_NAME;
+
+ if ($actionType == Piwik_Tracker_Action::TYPE_ACTION_URL) {
+ // for urls trim protocol and www because it is not recorded in the db
+ $string = preg_replace('@^http[s]?://(www\.)?@i', '', $string);
+ }
+
// exact matches work by returning the id directly
- if ($matchType == Piwik_SegmentExpression::MATCH_EQUAL
- || $matchType == Piwik_SegmentExpression::MATCH_NOT_EQUAL)
- {
+ if ($matchType == Piwik_SegmentExpression::MATCH_EQUAL
+ || $matchType == Piwik_SegmentExpression::MATCH_NOT_EQUAL
+ ) {
$sql = Piwik_Tracker_Action::getSqlSelectActionId();
$bind = array($string, $string, $actionType);
$idAction = Piwik_FetchOne($sql, $bind);
// 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))
- {
+ if (empty($idAction)) {
$idAction = -100;
}
return $idAction;
}
-
+
// now, we handle the cases =@ (contains) and !@ (does not contain)
-
+
// build the expression based on the match type
- $sql = 'SELECT idaction FROM '.Piwik_Common::prefixTable('log_action').' WHERE ';
- switch ($matchType)
- {
+ $sql = 'SELECT idaction FROM ' . Piwik_Common::prefixTable('log_action') . ' WHERE ';
+ switch ($matchType) {
case '=@':
// use concat to make sure, no %s occurs because some plugins use %s in their sql
- $sql .= '( name LIKE CONCAT("%", ?, "%") AND type = '.$actionType.' )';
+ $sql .= '( name LIKE CONCAT("%", ?, "%") AND type = ' . $actionType . ' )';
break;
case '!@':
- $sql .= '( name NOT LIKE CONCAT("%", ?, "%") AND type = '.$actionType.' )';
+ $sql .= '( name NOT LIKE CONCAT("%", ?, "%") AND type = ' . $actionType . ' )';
break;
default:
throw new Exception("This match type is not available for action-segments.");
break;
}
-
+
return array(
// mark that the returned value is an sql-expression instead of a literal value
- 'SQL' => $sql,
- 'bind' => $string
+ 'SQL' => $sql,
+ 'bind' => $string
);
- }
-
- /**
- * Returns metadata for available reports
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $reports = &$notification->getNotificationObject();
-
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_Actions'),
- 'name' => Piwik_Translate('Actions_Actions') . ' - ' . Piwik_Translate('General_MainMetrics'),
- 'module' => 'Actions',
- 'action' => 'get',
- 'metrics' => array(
- 'nb_pageviews' => Piwik_Translate('General_ColumnPageviews'),
- 'nb_uniq_pageviews' => Piwik_Translate('General_ColumnUniquePageviews'),
- 'nb_downloads' => Piwik_Translate('Actions_ColumnDownloads'),
- 'nb_uniq_downloads' => Piwik_Translate('Actions_ColumnUniqueDownloads'),
- 'nb_outlinks' => Piwik_Translate('Actions_ColumnOutlinks'),
- 'nb_uniq_outlinks' => Piwik_Translate('Actions_ColumnUniqueOutlinks'),
- 'nb_searches' => Piwik_Translate('Actions_ColumnSearches'),
- 'nb_keywords' => Piwik_Translate('Actions_ColumnSiteSearchKeywords'),
- ),
- 'metricsDocumentation' => array(
- 'nb_pageviews' => Piwik_Translate('General_ColumnPageviewsDocumentation'),
- 'nb_uniq_pageviews' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'),
- 'nb_downloads' => Piwik_Translate('Actions_ColumnClicksDocumentation'),
- 'nb_uniq_downloads' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'),
- 'nb_outlinks' => Piwik_Translate('Actions_ColumnClicksDocumentation'),
- 'nb_uniq_outlinks' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'),
- 'nb_searches' => Piwik_Translate('Actions_ColumnSearchesDocumentation'),
+ }
+
+ /**
+ * Returns metadata for available reports
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $reports = & $notification->getNotificationObject();
+
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_Actions'),
+ 'name' => Piwik_Translate('Actions_Actions') . ' - ' . Piwik_Translate('General_MainMetrics'),
+ 'module' => 'Actions',
+ 'action' => 'get',
+ 'metrics' => array(
+ 'nb_pageviews' => Piwik_Translate('General_ColumnPageviews'),
+ 'nb_uniq_pageviews' => Piwik_Translate('General_ColumnUniquePageviews'),
+ 'nb_downloads' => Piwik_Translate('Actions_ColumnDownloads'),
+ 'nb_uniq_downloads' => Piwik_Translate('Actions_ColumnUniqueDownloads'),
+ 'nb_outlinks' => Piwik_Translate('Actions_ColumnOutlinks'),
+ 'nb_uniq_outlinks' => Piwik_Translate('Actions_ColumnUniqueOutlinks'),
+ 'nb_searches' => Piwik_Translate('Actions_ColumnSearches'),
+ 'nb_keywords' => Piwik_Translate('Actions_ColumnSiteSearchKeywords'),
+ ),
+ 'metricsDocumentation' => array(
+ 'nb_pageviews' => Piwik_Translate('General_ColumnPageviewsDocumentation'),
+ 'nb_uniq_pageviews' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'),
+ 'nb_downloads' => Piwik_Translate('Actions_ColumnClicksDocumentation'),
+ 'nb_uniq_downloads' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'),
+ 'nb_outlinks' => Piwik_Translate('Actions_ColumnClicksDocumentation'),
+ 'nb_uniq_outlinks' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'),
+ 'nb_searches' => Piwik_Translate('Actions_ColumnSearchesDocumentation'),
// 'nb_keywords' => Piwik_Translate('Actions_ColumnSiteSearchKeywords'),
- ),
- 'processedMetrics' => false,
- 'order' => 1
- );
-
- $metrics = array(
- 'nb_hits' => Piwik_Translate('General_ColumnPageviews'),
- 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'),
- 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'),
- 'avg_time_on_page' => Piwik_Translate('General_ColumnAverageTimeOnPage'),
- 'exit_rate' => Piwik_Translate('General_ColumnExitRate'),
- 'avg_time_generation' => Piwik_Translate('General_ColumnAverageGenerationTime')
- );
-
- $documentation = array(
- 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'),
- 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'),
- 'bounce_rate' => Piwik_Translate('General_ColumnPageBounceRateDocumentation'),
- 'avg_time_on_page' => Piwik_Translate('General_ColumnAverageTimeOnPageDocumentation'),
- 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation'),
- 'avg_time_generation' => Piwik_Translate('General_ColumnAverageGenerationTimeDocumentation'),
- );
-
- // pages report
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_Actions'),
- 'name' => Piwik_Translate('Actions_PageUrls'),
- 'module' => 'Actions',
- 'action' => 'getPageUrls',
- 'dimension' => Piwik_Translate('Actions_ColumnPageURL'),
- 'metrics' => $metrics,
- 'metricsDocumentation' => $documentation,
- 'documentation' => Piwik_Translate('Actions_PagesReportDocumentation', '<br />')
- .'<br />'.Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
- 'processedMetrics' => false,
- 'actionToLoadSubTables' => 'getPageUrls',
- 'order' => 2
- );
-
- // entry pages report
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_Actions'),
- 'name' => Piwik_Translate('Actions_SubmenuPagesEntry'),
- 'module' => 'Actions',
- 'action' => 'getEntryPageUrls',
- 'dimension' => Piwik_Translate('Actions_ColumnPageURL'),
- 'metrics' => array(
- 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrances'),
- 'entry_bounce_count' => Piwik_Translate('General_ColumnBounces'),
- 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'),
- ),
- 'metricsDocumentation' => array(
- 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrancesDocumentation'),
- 'entry_bounce_count' => Piwik_Translate('General_ColumnBouncesDocumentation'),
- 'bounce_rate' => Piwik_Translate('General_ColumnBounceRateForPageDocumentation')
- ),
- 'documentation' => Piwik_Translate('Actions_EntryPagesReportDocumentation', '<br />')
- .' '.Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
- 'processedMetrics' => false,
- 'actionToLoadSubTables' => 'getEntryPageUrls',
- 'order' => 3
- );
-
- // exit pages report
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_Actions'),
- 'name' => Piwik_Translate('Actions_SubmenuPagesExit'),
- 'module' => 'Actions',
- 'action' => 'getExitPageUrls',
- 'dimension' => Piwik_Translate('Actions_ColumnPageURL'),
- 'metrics' => array(
- 'exit_nb_visits' => Piwik_Translate('General_ColumnExits'),
- 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'),
- 'exit_rate' => Piwik_Translate('General_ColumnExitRate')
- ),
- 'metricsDocumentation' => array(
- 'exit_nb_visits' => Piwik_Translate('General_ColumnExitsDocumentation'),
- 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'),
- 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation')
- ),
- 'documentation' => Piwik_Translate('Actions_ExitPagesReportDocumentation', '<br />')
- .' '.Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
- 'processedMetrics' => false,
- 'actionToLoadSubTables' => 'getExitPageUrls',
- 'order' => 4
- );
-
- // page titles report
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_Actions'),
- 'name' => Piwik_Translate('Actions_SubmenuPageTitles'),
- 'module' => 'Actions',
- 'action' => 'getPageTitles',
- 'dimension' => Piwik_Translate('Actions_ColumnPageName'),
- 'metrics' => $metrics,
- 'metricsDocumentation' => $documentation,
- 'documentation' => Piwik_Translate('Actions_PageTitlesReportDocumentation', array('<br />', htmlentities('<title>'))),
- 'processedMetrics' => false,
- 'actionToLoadSubTables' => 'getPageTitles',
- 'order' => 5,
-
- );
-
- // entry page titles report
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_Actions'),
- 'name' => Piwik_Translate('Actions_EntryPageTitles'),
- 'module' => 'Actions',
- 'action' => 'getEntryPageTitles',
- 'dimension' => Piwik_Translate('Actions_ColumnPageName'),
- 'metrics' => array(
- 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrances'),
- 'entry_bounce_count' => Piwik_Translate('General_ColumnBounces'),
- 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'),
- ),
- 'metricsDocumentation' => array(
- 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrancesDocumentation'),
- 'entry_bounce_count' => Piwik_Translate('General_ColumnBouncesDocumentation'),
- 'bounce_rate' => Piwik_Translate('General_ColumnBounceRateForPageDocumentation')
- ),
- 'documentation' => Piwik_Translate('Actions_ExitPageTitlesReportDocumentation', '<br />')
- .' '.Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
- 'processedMetrics' => false,
- 'actionToLoadSubTables' => 'getEntryPageTitles',
- 'order' => 6
- );
-
- // exit page titles report
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_Actions'),
- 'name' => Piwik_Translate('Actions_ExitPageTitles'),
- 'module' => 'Actions',
- 'action' => 'getExitPageTitles',
- 'dimension' => Piwik_Translate('Actions_ColumnPageName'),
- 'metrics' => array(
- 'exit_nb_visits' => Piwik_Translate('General_ColumnExits'),
- 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'),
- 'exit_rate' => Piwik_Translate('General_ColumnExitRate')
- ),
- 'metricsDocumentation' => array(
- 'exit_nb_visits' => Piwik_Translate('General_ColumnExitsDocumentation'),
- 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'),
- 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation')
- ),
- 'documentation' => Piwik_Translate('Actions_EntryPageTitlesReportDocumentation', '<br />')
- .' '.Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
- 'processedMetrics' => false,
- 'actionToLoadSubTables' => 'getExitPageTitles',
- 'order' => 7
- );
-
- $documentation = array(
- 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'),
- 'nb_hits' => Piwik_Translate('Actions_ColumnClicksDocumentation')
- );
-
- // outlinks report
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_Actions'),
- 'name' => Piwik_Translate('Actions_SubmenuOutlinks'),
- 'module' => 'Actions',
- 'action' => 'getOutlinks',
- 'dimension' => Piwik_Translate('Actions_ColumnClickedURL'),
- 'metrics' => array(
- 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueClicks'),
- 'nb_hits' => Piwik_Translate('Actions_ColumnClicks')
- ),
- 'metricsDocumentation' => $documentation,
- 'documentation' => Piwik_Translate('Actions_OutlinksReportDocumentation').' '
- .Piwik_Translate('Actions_OutlinkDocumentation').'<br />'
- .Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
- 'processedMetrics' => false,
- 'actionToLoadSubTables' => 'getOutlinks',
- 'order' => 8,
- );
-
- // downloads report
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_Actions'),
- 'name' => Piwik_Translate('Actions_SubmenuDownloads'),
- 'module' => 'Actions',
- 'action' => 'getDownloads',
- 'dimension' => Piwik_Translate('Actions_ColumnDownloadURL'),
- 'metrics' => array(
- 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueDownloads'),
- 'nb_hits' => Piwik_Translate('Actions_ColumnDownloads')
- ),
- 'metricsDocumentation' => $documentation,
- 'documentation' => Piwik_Translate('Actions_DownloadsReportDocumentation', '<br />'),
- 'processedMetrics' => false,
- 'actionToLoadSubTables' => 'getDownloads',
- 'order' => 9,
- );
-
- if($this->isSiteSearchEnabled())
- {
- // Search Keywords
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_SubmenuSitesearch'),
- 'name' => Piwik_Translate('Actions_WidgetSearchKeywords'),
- 'module' => 'Actions',
- 'action' => 'getSiteSearchKeywords',
- 'dimension' => Piwik_Translate('Actions_ColumnSearchKeyword'),
- 'metrics' => array(
- 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'),
- 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearch'),
- 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'),
- ),
- 'metricsDocumentation' => array(
- 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'),
- 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearchDocumentation'),
- 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'),
- ),
- 'documentation' => Piwik_Translate('Actions_SiteSearchKeywordsDocumentation') . '<br/><br/>' . Piwik_Translate('Actions_SiteSearchIntro') . '<br/><br/>'
- . '<a href="http://piwik.org/docs/site-search/" target="_blank">'. Piwik_Translate('Actions_LearnMoreAboutSiteSearchLink') . '</a>',
- 'processedMetrics' => false,
- 'order' => 15
- );
- // No Result Search Keywords
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_SubmenuSitesearch'),
- 'name' => Piwik_Translate('Actions_WidgetSearchNoResultKeywords'),
- 'module' => 'Actions',
- 'action' => 'getSiteSearchNoResultKeywords',
- 'dimension' => Piwik_Translate('Actions_ColumnNoResultKeyword'),
- 'metrics' => array(
- 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'),
- 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'),
- ),
- 'metricsDocumentation' => array(
- 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'),
- 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'),
- ),
- 'documentation' => Piwik_Translate('Actions_SiteSearchIntro'). '<br /><br />' . Piwik_Translate('Actions_SiteSearchKeywordsNoResultDocumentation'),
- 'processedMetrics' => false,
- 'order' => 16
- );
-
- if(self::isCustomVariablesPluginsEnabled()) {
- // Search Categories
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_SubmenuSitesearch'),
- 'name' => Piwik_Translate('Actions_WidgetSearchCategories'),
- 'module' => 'Actions',
- 'action' => 'getSiteSearchCategories',
- 'dimension' => Piwik_Translate('Actions_ColumnSearchCategory'),
- 'metrics' => array(
- 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'),
- 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearch'),
- 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'),
- ),
- 'metricsDocumentation' => array(
- 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'),
- 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearchDocumentation'),
- 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'),
- ),
- 'documentation' => Piwik_Translate('Actions_SiteSearchCategories1') . '<br/>' . Piwik_Translate('Actions_SiteSearchCategories2'),
- 'processedMetrics' => false,
- 'order' => 17
- );
- }
-
- $documentation = Piwik_Translate('Actions_SiteSearchFollowingPagesDoc') .'<br/>'.Piwik_Translate('General_UsePlusMinusIconsDocumentation');
- // Pages URLs following Search
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_SubmenuSitesearch'),
- 'name' => Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'),
- 'module' => 'Actions',
- 'action' => 'getPageUrlsFollowingSiteSearch',
- 'dimension' => Piwik_Translate('General_ColumnDestinationPage'),
- 'metrics' => array(
- 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearch'),
- 'nb_hits' => Piwik_Translate('General_ColumnTotalPageviews'),
- ),
- 'metricsDocumentation' => array(
- 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearchDocumentation'),
- 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'),
- ),
- 'documentation' => $documentation,
- 'processedMetrics' => false,
- 'order' => 18
- );
- // Pages Titles following Search
- $reports[] = array(
- 'category' => Piwik_Translate('Actions_SubmenuSitesearch'),
- 'name' => Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'),
- 'module' => 'Actions',
- 'action' => 'getPageTitlesFollowingSiteSearch',
- 'dimension' => Piwik_Translate('General_ColumnDestinationPage'),
- 'metrics' => array(
- 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearch'),
- 'nb_hits' => Piwik_Translate('General_ColumnTotalPageviews'),
- ),
- 'metricsDocumentation' => array(
- 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearchDocumentation'),
- 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'),
- ),
- 'documentation' => $documentation,
- 'processedMetrics' => false,
- 'order' => 19
- );
- }
- }
-
- function addWidgets()
- {
- Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuPages', 'Actions', 'getPageUrls');
- Piwik_AddWidget( 'Actions_Actions', 'Actions_WidgetPageTitles', 'Actions', 'getPageTitles');
- Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuOutlinks', 'Actions', 'getOutlinks');
- Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuDownloads', 'Actions', 'getDownloads');
- Piwik_AddWidget( 'Actions_Actions', 'Actions_WidgetPagesEntry', 'Actions', 'getEntryPageUrls');
- Piwik_AddWidget( 'Actions_Actions', 'Actions_WidgetPagesExit', 'Actions', 'getExitPageUrls');
- Piwik_AddWidget( 'Actions_Actions', 'Actions_WidgetEntryPageTitles', 'Actions', 'getEntryPageTitles' );
- Piwik_AddWidget( 'Actions_Actions', 'Actions_WidgetExitPageTitles', 'Actions', 'getExitPageTitles' );
-
- if($this->isSiteSearchEnabled())
- {
- Piwik_AddWidget( 'Actions_SubmenuSitesearch', 'Actions_WidgetSearchKeywords', 'Actions', 'getSiteSearchKeywords');
-
- if(self::isCustomVariablesPluginsEnabled()) {
- Piwik_AddWidget( 'Actions_SubmenuSitesearch', 'Actions_WidgetSearchCategories', 'Actions', 'getSiteSearchCategories');
- }
- Piwik_AddWidget( 'Actions_SubmenuSitesearch', 'Actions_WidgetSearchNoResultKeywords', 'Actions', 'getSiteSearchNoResultKeywords');
- Piwik_AddWidget( 'Actions_SubmenuSitesearch', 'Actions_WidgetPageUrlsFollowingSearch', 'Actions', 'getPageUrlsFollowingSiteSearch');
- Piwik_AddWidget( 'Actions_SubmenuSitesearch', 'Actions_WidgetPageTitlesFollowingSearch', 'Actions', 'getPageTitlesFollowingSiteSearch');
- }
- }
-
- function addMenus()
- {
- Piwik_AddMenu('Actions_Actions', '', array('module' => 'Actions', 'action' => 'indexPageUrls'), true, 15);
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPages', array('module' => 'Actions', 'action' => 'indexPageUrls'), true, 1);
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPagesEntry', array('module' => 'Actions', 'action' => 'indexEntryPageUrls'), true, 2);
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPagesExit', array('module' => 'Actions', 'action' => 'indexExitPageUrls'), true, 3);
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPageTitles', array('module' => 'Actions', 'action' => 'indexPageTitles'), true, 4);
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuOutlinks', array('module' => 'Actions', 'action' => 'indexOutlinks'), true, 6);
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'indexDownloads'), true, 7);
-
- if($this->isSiteSearchEnabled())
- {
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuSitesearch', array('module' => 'Actions', 'action' => 'indexSiteSearch'), true, 5);
- }
- }
-
- protected function isSiteSearchEnabled()
- {
- $idSite = Piwik_Common::getRequestVar('idSite', 0, 'int');
- if($idSite == 0 ) {
- return false;
- }
- return Piwik_Site::isSiteSearchEnabledFor($idSite);
- }
-
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archivePeriod( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $actionsArchiving = new Piwik_Actions_Archiving($archiveProcessing->idsite);
- return $actionsArchiving->archivePeriod($archiveProcessing);
- }
-
- /**
- * Compute all the actions along with their hierarchies.
- *
- * For each action we process the "interest statistics" :
- * visits, unique visitors, bounce count, sum visit length.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function archiveDay( $notification )
- {
- /* @var $archiveProcessing Piwik_ArchiveProcessing_Day */
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $actionsArchiving = new Piwik_Actions_Archiving($archiveProcessing->idsite);
- return $actionsArchiving->archiveDay($archiveProcessing);
- }
-
- static public function checkCustomVariablesPluginEnabled()
- {
- if(!self::isCustomVariablesPluginsEnabled())
- {
- throw new Exception("To Track Site Search Categories, please ask the Piwik Administrator to enable the 'Custom Variables' plugin in Settings > Plugins.");
- }
- }
-
- static protected function isCustomVariablesPluginsEnabled()
- {
- return Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables');
- }
+ ),
+ 'processedMetrics' => false,
+ 'order' => 1
+ );
+
+ $metrics = array(
+ 'nb_hits' => Piwik_Translate('General_ColumnPageviews'),
+ 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'),
+ 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'),
+ 'avg_time_on_page' => Piwik_Translate('General_ColumnAverageTimeOnPage'),
+ 'exit_rate' => Piwik_Translate('General_ColumnExitRate'),
+ 'avg_time_generation' => Piwik_Translate('General_ColumnAverageGenerationTime')
+ );
+
+ $documentation = array(
+ 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'),
+ 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'),
+ 'bounce_rate' => Piwik_Translate('General_ColumnPageBounceRateDocumentation'),
+ 'avg_time_on_page' => Piwik_Translate('General_ColumnAverageTimeOnPageDocumentation'),
+ 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation'),
+ 'avg_time_generation' => Piwik_Translate('General_ColumnAverageGenerationTimeDocumentation'),
+ );
+
+ // pages report
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_Actions'),
+ 'name' => Piwik_Translate('Actions_PageUrls'),
+ 'module' => 'Actions',
+ 'action' => 'getPageUrls',
+ 'dimension' => Piwik_Translate('Actions_ColumnPageURL'),
+ 'metrics' => $metrics,
+ 'metricsDocumentation' => $documentation,
+ 'documentation' => Piwik_Translate('Actions_PagesReportDocumentation', '<br />')
+ . '<br />' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
+ 'processedMetrics' => false,
+ 'actionToLoadSubTables' => 'getPageUrls',
+ 'order' => 2
+ );
+
+ // entry pages report
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_Actions'),
+ 'name' => Piwik_Translate('Actions_SubmenuPagesEntry'),
+ 'module' => 'Actions',
+ 'action' => 'getEntryPageUrls',
+ 'dimension' => Piwik_Translate('Actions_ColumnPageURL'),
+ 'metrics' => array(
+ 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrances'),
+ 'entry_bounce_count' => Piwik_Translate('General_ColumnBounces'),
+ 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'),
+ ),
+ 'metricsDocumentation' => array(
+ 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrancesDocumentation'),
+ 'entry_bounce_count' => Piwik_Translate('General_ColumnBouncesDocumentation'),
+ 'bounce_rate' => Piwik_Translate('General_ColumnBounceRateForPageDocumentation')
+ ),
+ 'documentation' => Piwik_Translate('Actions_EntryPagesReportDocumentation', '<br />')
+ . ' ' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
+ 'processedMetrics' => false,
+ 'actionToLoadSubTables' => 'getEntryPageUrls',
+ 'order' => 3
+ );
+
+ // exit pages report
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_Actions'),
+ 'name' => Piwik_Translate('Actions_SubmenuPagesExit'),
+ 'module' => 'Actions',
+ 'action' => 'getExitPageUrls',
+ 'dimension' => Piwik_Translate('Actions_ColumnPageURL'),
+ 'metrics' => array(
+ 'exit_nb_visits' => Piwik_Translate('General_ColumnExits'),
+ 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'),
+ 'exit_rate' => Piwik_Translate('General_ColumnExitRate')
+ ),
+ 'metricsDocumentation' => array(
+ 'exit_nb_visits' => Piwik_Translate('General_ColumnExitsDocumentation'),
+ 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'),
+ 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation')
+ ),
+ 'documentation' => Piwik_Translate('Actions_ExitPagesReportDocumentation', '<br />')
+ . ' ' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
+ 'processedMetrics' => false,
+ 'actionToLoadSubTables' => 'getExitPageUrls',
+ 'order' => 4
+ );
+
+ // page titles report
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_Actions'),
+ 'name' => Piwik_Translate('Actions_SubmenuPageTitles'),
+ 'module' => 'Actions',
+ 'action' => 'getPageTitles',
+ 'dimension' => Piwik_Translate('Actions_ColumnPageName'),
+ 'metrics' => $metrics,
+ 'metricsDocumentation' => $documentation,
+ 'documentation' => Piwik_Translate('Actions_PageTitlesReportDocumentation', array('<br />', htmlentities('<title>'))),
+ 'processedMetrics' => false,
+ 'actionToLoadSubTables' => 'getPageTitles',
+ 'order' => 5,
+
+ );
+
+ // entry page titles report
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_Actions'),
+ 'name' => Piwik_Translate('Actions_EntryPageTitles'),
+ 'module' => 'Actions',
+ 'action' => 'getEntryPageTitles',
+ 'dimension' => Piwik_Translate('Actions_ColumnPageName'),
+ 'metrics' => array(
+ 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrances'),
+ 'entry_bounce_count' => Piwik_Translate('General_ColumnBounces'),
+ 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'),
+ ),
+ 'metricsDocumentation' => array(
+ 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrancesDocumentation'),
+ 'entry_bounce_count' => Piwik_Translate('General_ColumnBouncesDocumentation'),
+ 'bounce_rate' => Piwik_Translate('General_ColumnBounceRateForPageDocumentation')
+ ),
+ 'documentation' => Piwik_Translate('Actions_ExitPageTitlesReportDocumentation', '<br />')
+ . ' ' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
+ 'processedMetrics' => false,
+ 'actionToLoadSubTables' => 'getEntryPageTitles',
+ 'order' => 6
+ );
+
+ // exit page titles report
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_Actions'),
+ 'name' => Piwik_Translate('Actions_ExitPageTitles'),
+ 'module' => 'Actions',
+ 'action' => 'getExitPageTitles',
+ 'dimension' => Piwik_Translate('Actions_ColumnPageName'),
+ 'metrics' => array(
+ 'exit_nb_visits' => Piwik_Translate('General_ColumnExits'),
+ 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'),
+ 'exit_rate' => Piwik_Translate('General_ColumnExitRate')
+ ),
+ 'metricsDocumentation' => array(
+ 'exit_nb_visits' => Piwik_Translate('General_ColumnExitsDocumentation'),
+ 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'),
+ 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation')
+ ),
+ 'documentation' => Piwik_Translate('Actions_EntryPageTitlesReportDocumentation', '<br />')
+ . ' ' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
+ 'processedMetrics' => false,
+ 'actionToLoadSubTables' => 'getExitPageTitles',
+ 'order' => 7
+ );
+
+ $documentation = array(
+ 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'),
+ 'nb_hits' => Piwik_Translate('Actions_ColumnClicksDocumentation')
+ );
+
+ // outlinks report
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_Actions'),
+ 'name' => Piwik_Translate('Actions_SubmenuOutlinks'),
+ 'module' => 'Actions',
+ 'action' => 'getOutlinks',
+ 'dimension' => Piwik_Translate('Actions_ColumnClickedURL'),
+ 'metrics' => array(
+ 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueClicks'),
+ 'nb_hits' => Piwik_Translate('Actions_ColumnClicks')
+ ),
+ 'metricsDocumentation' => $documentation,
+ 'documentation' => Piwik_Translate('Actions_OutlinksReportDocumentation') . ' '
+ . Piwik_Translate('Actions_OutlinkDocumentation') . '<br />'
+ . Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
+ 'processedMetrics' => false,
+ 'actionToLoadSubTables' => 'getOutlinks',
+ 'order' => 8,
+ );
+
+ // downloads report
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_Actions'),
+ 'name' => Piwik_Translate('Actions_SubmenuDownloads'),
+ 'module' => 'Actions',
+ 'action' => 'getDownloads',
+ 'dimension' => Piwik_Translate('Actions_ColumnDownloadURL'),
+ 'metrics' => array(
+ 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueDownloads'),
+ 'nb_hits' => Piwik_Translate('Actions_ColumnDownloads')
+ ),
+ 'metricsDocumentation' => $documentation,
+ 'documentation' => Piwik_Translate('Actions_DownloadsReportDocumentation', '<br />'),
+ 'processedMetrics' => false,
+ 'actionToLoadSubTables' => 'getDownloads',
+ 'order' => 9,
+ );
+
+ if ($this->isSiteSearchEnabled()) {
+ // Search Keywords
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_SubmenuSitesearch'),
+ 'name' => Piwik_Translate('Actions_WidgetSearchKeywords'),
+ 'module' => 'Actions',
+ 'action' => 'getSiteSearchKeywords',
+ 'dimension' => Piwik_Translate('Actions_ColumnSearchKeyword'),
+ 'metrics' => array(
+ 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'),
+ 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearch'),
+ 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'),
+ ),
+ 'metricsDocumentation' => array(
+ 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'),
+ 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearchDocumentation'),
+ 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'),
+ ),
+ 'documentation' => Piwik_Translate('Actions_SiteSearchKeywordsDocumentation') . '<br/><br/>' . Piwik_Translate('Actions_SiteSearchIntro') . '<br/><br/>'
+ . '<a href="http://piwik.org/docs/site-search/" target="_blank">' . Piwik_Translate('Actions_LearnMoreAboutSiteSearchLink') . '</a>',
+ 'processedMetrics' => false,
+ 'order' => 15
+ );
+ // No Result Search Keywords
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_SubmenuSitesearch'),
+ 'name' => Piwik_Translate('Actions_WidgetSearchNoResultKeywords'),
+ 'module' => 'Actions',
+ 'action' => 'getSiteSearchNoResultKeywords',
+ 'dimension' => Piwik_Translate('Actions_ColumnNoResultKeyword'),
+ 'metrics' => array(
+ 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'),
+ 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'),
+ ),
+ 'metricsDocumentation' => array(
+ 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'),
+ 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'),
+ ),
+ 'documentation' => Piwik_Translate('Actions_SiteSearchIntro') . '<br /><br />' . Piwik_Translate('Actions_SiteSearchKeywordsNoResultDocumentation'),
+ 'processedMetrics' => false,
+ 'order' => 16
+ );
+
+ if (self::isCustomVariablesPluginsEnabled()) {
+ // Search Categories
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_SubmenuSitesearch'),
+ 'name' => Piwik_Translate('Actions_WidgetSearchCategories'),
+ 'module' => 'Actions',
+ 'action' => 'getSiteSearchCategories',
+ 'dimension' => Piwik_Translate('Actions_ColumnSearchCategory'),
+ 'metrics' => array(
+ 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'),
+ 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearch'),
+ 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'),
+ ),
+ 'metricsDocumentation' => array(
+ 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'),
+ 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearchDocumentation'),
+ 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'),
+ ),
+ 'documentation' => Piwik_Translate('Actions_SiteSearchCategories1') . '<br/>' . Piwik_Translate('Actions_SiteSearchCategories2'),
+ 'processedMetrics' => false,
+ 'order' => 17
+ );
+ }
+
+ $documentation = Piwik_Translate('Actions_SiteSearchFollowingPagesDoc') . '<br/>' . Piwik_Translate('General_UsePlusMinusIconsDocumentation');
+ // Pages URLs following Search
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_SubmenuSitesearch'),
+ 'name' => Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'),
+ 'module' => 'Actions',
+ 'action' => 'getPageUrlsFollowingSiteSearch',
+ 'dimension' => Piwik_Translate('General_ColumnDestinationPage'),
+ 'metrics' => array(
+ 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearch'),
+ 'nb_hits' => Piwik_Translate('General_ColumnTotalPageviews'),
+ ),
+ 'metricsDocumentation' => array(
+ 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearchDocumentation'),
+ 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'),
+ ),
+ 'documentation' => $documentation,
+ 'processedMetrics' => false,
+ 'order' => 18
+ );
+ // Pages Titles following Search
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_SubmenuSitesearch'),
+ 'name' => Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'),
+ 'module' => 'Actions',
+ 'action' => 'getPageTitlesFollowingSiteSearch',
+ 'dimension' => Piwik_Translate('General_ColumnDestinationPage'),
+ 'metrics' => array(
+ 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearch'),
+ 'nb_hits' => Piwik_Translate('General_ColumnTotalPageviews'),
+ ),
+ 'metricsDocumentation' => array(
+ 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearchDocumentation'),
+ 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'),
+ ),
+ 'documentation' => $documentation,
+ 'processedMetrics' => false,
+ 'order' => 19
+ );
+ }
+ }
+
+ function addWidgets()
+ {
+ Piwik_AddWidget('Actions_Actions', 'Actions_SubmenuPages', 'Actions', 'getPageUrls');
+ Piwik_AddWidget('Actions_Actions', 'Actions_WidgetPageTitles', 'Actions', 'getPageTitles');
+ Piwik_AddWidget('Actions_Actions', 'Actions_SubmenuOutlinks', 'Actions', 'getOutlinks');
+ Piwik_AddWidget('Actions_Actions', 'Actions_SubmenuDownloads', 'Actions', 'getDownloads');
+ Piwik_AddWidget('Actions_Actions', 'Actions_WidgetPagesEntry', 'Actions', 'getEntryPageUrls');
+ Piwik_AddWidget('Actions_Actions', 'Actions_WidgetPagesExit', 'Actions', 'getExitPageUrls');
+ Piwik_AddWidget('Actions_Actions', 'Actions_WidgetEntryPageTitles', 'Actions', 'getEntryPageTitles');
+ Piwik_AddWidget('Actions_Actions', 'Actions_WidgetExitPageTitles', 'Actions', 'getExitPageTitles');
+
+ if ($this->isSiteSearchEnabled()) {
+ Piwik_AddWidget('Actions_SubmenuSitesearch', 'Actions_WidgetSearchKeywords', 'Actions', 'getSiteSearchKeywords');
+
+ if (self::isCustomVariablesPluginsEnabled()) {
+ Piwik_AddWidget('Actions_SubmenuSitesearch', 'Actions_WidgetSearchCategories', 'Actions', 'getSiteSearchCategories');
+ }
+ Piwik_AddWidget('Actions_SubmenuSitesearch', 'Actions_WidgetSearchNoResultKeywords', 'Actions', 'getSiteSearchNoResultKeywords');
+ Piwik_AddWidget('Actions_SubmenuSitesearch', 'Actions_WidgetPageUrlsFollowingSearch', 'Actions', 'getPageUrlsFollowingSiteSearch');
+ Piwik_AddWidget('Actions_SubmenuSitesearch', 'Actions_WidgetPageTitlesFollowingSearch', 'Actions', 'getPageTitlesFollowingSiteSearch');
+ }
+ }
+
+ function addMenus()
+ {
+ Piwik_AddMenu('Actions_Actions', '', array('module' => 'Actions', 'action' => 'indexPageUrls'), true, 15);
+ Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPages', array('module' => 'Actions', 'action' => 'indexPageUrls'), true, 1);
+ Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPagesEntry', array('module' => 'Actions', 'action' => 'indexEntryPageUrls'), true, 2);
+ Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPagesExit', array('module' => 'Actions', 'action' => 'indexExitPageUrls'), true, 3);
+ Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPageTitles', array('module' => 'Actions', 'action' => 'indexPageTitles'), true, 4);
+ Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuOutlinks', array('module' => 'Actions', 'action' => 'indexOutlinks'), true, 6);
+ Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'indexDownloads'), true, 7);
+
+ if ($this->isSiteSearchEnabled()) {
+ Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuSitesearch', array('module' => 'Actions', 'action' => 'indexSiteSearch'), true, 5);
+ }
+ }
+
+ protected function isSiteSearchEnabled()
+ {
+ $idSite = Piwik_Common::getRequestVar('idSite', 0, 'int');
+ if ($idSite == 0) {
+ return false;
+ }
+ return Piwik_Site::isSiteSearchEnabledFor($idSite);
+ }
+
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ function archivePeriod($notification)
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $actionsArchiving = new Piwik_Actions_Archiving($archiveProcessing->idsite);
+ return $actionsArchiving->archivePeriod($archiveProcessing);
+ }
+
+ /**
+ * Compute all the actions along with their hierarchies.
+ *
+ * For each action we process the "interest statistics" :
+ * visits, unique visitors, bounce count, sum visit length.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function archiveDay($notification)
+ {
+ /* @var $archiveProcessing Piwik_ArchiveProcessing_Day */
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $actionsArchiving = new Piwik_Actions_Archiving($archiveProcessing->idsite);
+ return $actionsArchiving->archiveDay($archiveProcessing);
+ }
+
+ static public function checkCustomVariablesPluginEnabled()
+ {
+ if (!self::isCustomVariablesPluginsEnabled()) {
+ throw new Exception("To Track Site Search Categories, please ask the Piwik Administrator to enable the 'Custom Variables' plugin in Settings > Plugins.");
+ }
+ }
+
+ static protected function isCustomVariablesPluginsEnabled()
+ {
+ return Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables');
+ }
}
diff --git a/plugins/Actions/Archiving.php b/plugins/Actions/Archiving.php
index c0d36d1083..db71faf788 100644
--- a/plugins/Actions/Archiving.php
+++ b/plugins/Actions/Archiving.php
@@ -16,100 +16,100 @@
*/
class Piwik_Actions_Archiving
{
- protected $actionsTablesByType = null;
-
- public static $actionTypes = array(
- Piwik_Tracker_Action::TYPE_ACTION_URL,
- Piwik_Tracker_Action::TYPE_OUTLINK,
- Piwik_Tracker_Action::TYPE_DOWNLOAD,
- Piwik_Tracker_Action::TYPE_ACTION_NAME,
- Piwik_Tracker_Action::TYPE_SITE_SEARCH,
- );
-
- static protected $invalidSummedColumnNameToRenamedNameFromPeriodArchive = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS,
- );
-
- static protected $invalidSummedColumnNameToDeleteFromDayArchive = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
- );
-
- protected $isSiteSearchEnabled = false;
-
- function __construct($idSite)
- {
- $this->isSiteSearchEnabled = Piwik_Site::isSiteSearchEnabledFor($idSite);
- }
-
- /**
- * Archives Actions reports for a Period
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @return bool
- */
- public function archivePeriod(Piwik_ArchiveProcessing $archiveProcessing)
- {
- Piwik_Actions_ArchivingHelper::reloadConfig();
- $dataTableToSum = array(
- 'Actions_actions',
- 'Actions_downloads',
- 'Actions_outlink',
- 'Actions_actions_url',
- 'Actions_sitesearch',
- );
- $nameToCount = $archiveProcessing->archiveDataTable($dataTableToSum,
- self::$invalidSummedColumnNameToRenamedNameFromPeriodArchive,
- Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero,
- Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable,
- Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
-
- $archiveProcessing->archiveNumericValuesSum(array(
- 'Actions_nb_pageviews',
- 'Actions_nb_uniq_pageviews',
- 'Actions_nb_downloads',
- 'Actions_nb_uniq_downloads',
- 'Actions_nb_outlinks',
- 'Actions_nb_uniq_outlinks',
- 'Actions_nb_searches',
- ));
-
- // Unique Keywords can't be summed, instead we take the RowsCount() of the keyword table
- $archiveProcessing->insertNumericRecord('Actions_nb_keywords', $nameToCount['Actions_sitesearch']['level0']);
- return true;
- }
-
- /**
- * Archives Actions reports for a Day
- *
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @return bool
- */
- public function archiveDay(Piwik_ArchiveProcessing $archiveProcessing)
- {
- $rankingQueryLimit = self::getRankingQueryLimit();
- Piwik_Actions_ArchivingHelper::reloadConfig();
-
- $this->initActionsTables();
- $this->archiveDayActions($archiveProcessing, $rankingQueryLimit);
- $this->archiveDayEntryActions($archiveProcessing, $rankingQueryLimit);
- $this->archiveDayExitActions($archiveProcessing, $rankingQueryLimit);
- $this->archiveDayActionsTime($archiveProcessing, $rankingQueryLimit);
-
- // Record the final datasets
- $this->archiveDayRecordInDatabase($archiveProcessing);
-
- return true;
- }
-
- /*
- * Page URLs and Page names, general stats
- */
- protected function archiveDayActions($archiveProcessing, $rankingQueryLimit)
- {
- $select = "log_action.name,
+ protected $actionsTablesByType = null;
+
+ public static $actionTypes = array(
+ Piwik_Tracker_Action::TYPE_ACTION_URL,
+ Piwik_Tracker_Action::TYPE_OUTLINK,
+ Piwik_Tracker_Action::TYPE_DOWNLOAD,
+ Piwik_Tracker_Action::TYPE_ACTION_NAME,
+ Piwik_Tracker_Action::TYPE_SITE_SEARCH,
+ );
+
+ static protected $invalidSummedColumnNameToRenamedNameFromPeriodArchive = array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS,
+ );
+
+ static protected $invalidSummedColumnNameToDeleteFromDayArchive = array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
+ );
+
+ protected $isSiteSearchEnabled = false;
+
+ function __construct($idSite)
+ {
+ $this->isSiteSearchEnabled = Piwik_Site::isSiteSearchEnabledFor($idSite);
+ }
+
+ /**
+ * Archives Actions reports for a Period
+ * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @return bool
+ */
+ public function archivePeriod(Piwik_ArchiveProcessing $archiveProcessing)
+ {
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+ $dataTableToSum = array(
+ 'Actions_actions',
+ 'Actions_downloads',
+ 'Actions_outlink',
+ 'Actions_actions_url',
+ 'Actions_sitesearch',
+ );
+ $nameToCount = $archiveProcessing->archiveDataTable($dataTableToSum,
+ self::$invalidSummedColumnNameToRenamedNameFromPeriodArchive,
+ Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero,
+ Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable,
+ Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
+
+ $archiveProcessing->archiveNumericValuesSum(array(
+ 'Actions_nb_pageviews',
+ 'Actions_nb_uniq_pageviews',
+ 'Actions_nb_downloads',
+ 'Actions_nb_uniq_downloads',
+ 'Actions_nb_outlinks',
+ 'Actions_nb_uniq_outlinks',
+ 'Actions_nb_searches',
+ ));
+
+ // Unique Keywords can't be summed, instead we take the RowsCount() of the keyword table
+ $archiveProcessing->insertNumericRecord('Actions_nb_keywords', $nameToCount['Actions_sitesearch']['level0']);
+ return true;
+ }
+
+ /**
+ * Archives Actions reports for a Day
+ *
+ * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @return bool
+ */
+ public function archiveDay(Piwik_ArchiveProcessing $archiveProcessing)
+ {
+ $rankingQueryLimit = self::getRankingQueryLimit();
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+
+ $this->initActionsTables();
+ $this->archiveDayActions($archiveProcessing, $rankingQueryLimit);
+ $this->archiveDayEntryActions($archiveProcessing, $rankingQueryLimit);
+ $this->archiveDayExitActions($archiveProcessing, $rankingQueryLimit);
+ $this->archiveDayActionsTime($archiveProcessing, $rankingQueryLimit);
+
+ // Record the final datasets
+ $this->archiveDayRecordInDatabase($archiveProcessing);
+
+ return true;
+ }
+
+ /*
+ * Page URLs and Page names, general stats
+ */
+ protected function archiveDayActions($archiveProcessing, $rankingQueryLimit)
+ {
+ $select = "log_action.name,
log_action.type,
log_action.idaction,
log_action.url_prefix,
@@ -117,432 +117,422 @@ class Piwik_Actions_Archiving
count(distinct log_link_visit_action.idvisitor) as `" . Piwik_Archive::INDEX_NB_UNIQ_VISITORS . "`,
count(*) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS . "`,
sum(
- case when " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION ." is null
+ case when " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . " is null
then 0
- else " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION ."
+ else " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . "
end
) / 1000 as `" . Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION . "`,
sum(
- case when " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION ." is null
+ case when " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . " is null
then 0
else 1
end
) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION . "`";
- $from = array(
- "log_link_visit_action",
- array(
- "table" => "log_action",
- "joinOn" => "log_link_visit_action.%s = log_action.idaction"
- )
- );
+ $from = array(
+ "log_link_visit_action",
+ array(
+ "table" => "log_action",
+ "joinOn" => "log_link_visit_action.%s = log_action.idaction"
+ )
+ );
- $where = "log_link_visit_action.server_time >= ?
+ $where = "log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?
AND log_link_visit_action.%s IS NOT NULL";
- $groupBy = "log_action.idaction";
- $orderBy = "`" . Piwik_Archive::INDEX_PAGE_NB_HITS . "` DESC, name ASC";
-
- $rankingQuery = false;
- if ($rankingQueryLimit > 0)
- {
- $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
- $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
- $rankingQuery->addLabelColumn(array('idaction', 'name'));
- $rankingQuery->addColumn(array('url_prefix', Piwik_Archive::INDEX_NB_UNIQ_VISITORS));
- $rankingQuery->addColumn(array(Piwik_Archive::INDEX_PAGE_NB_HITS, Piwik_Archive::INDEX_NB_VISITS), 'sum');
- if ($this->isSiteSearchEnabled())
- {
- $rankingQuery->addColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, 'sum');
- }
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, 'sum');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, 'sum');
- $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
- }
-
- // Special Magic to get
- // 1) No result Keywords
- // 2) For each page view, count number of times the referrer page was a Site Search
- if($this->isSiteSearchEnabled())
- {
- $selectFlagNoResultKeywords = ",
- CASE WHEN (MAX(log_link_visit_action.custom_var_v". Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT.") = 0 AND log_link_visit_action.custom_var_k". Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT." = '". Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT. "') THEN 1 ELSE 0 END AS `" . Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT . "`";
-
- //we need an extra JOIN to know whether the referrer "idaction_name_ref" was a Site Search request
- $from[] = array(
- "table" => "log_action",
- "tableAlias" => "log_action_name_ref",
- "joinOn" => "log_link_visit_action.idaction_name_ref = log_action_name_ref.idaction"
- );
-
- $selectSiteSearchFollowingPages = ",
- SUM(CASE WHEN log_action_name_ref.type = " . Piwik_Tracker_Action::TYPE_SITE_SEARCH . " THEN 1 ELSE 0 END) AS `". Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS."`";
-
- $select .= $selectFlagNoResultKeywords
- . $selectSiteSearchFollowingPages;
- // Not working yet
+ $groupBy = "log_action.idaction";
+ $orderBy = "`" . Piwik_Archive::INDEX_PAGE_NB_HITS . "` DESC, name ASC";
+
+ $rankingQuery = false;
+ if ($rankingQueryLimit > 0) {
+ $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
+ $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
+ $rankingQuery->addLabelColumn(array('idaction', 'name'));
+ $rankingQuery->addColumn(array('url_prefix', Piwik_Archive::INDEX_NB_UNIQ_VISITORS));
+ $rankingQuery->addColumn(array(Piwik_Archive::INDEX_PAGE_NB_HITS, Piwik_Archive::INDEX_NB_VISITS), 'sum');
+ if ($this->isSiteSearchEnabled()) {
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min');
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, 'sum');
+ }
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, 'sum');
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, 'sum');
+ $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
+ }
+
+ // Special Magic to get
+ // 1) No result Keywords
+ // 2) For each page view, count number of times the referrer page was a Site Search
+ if ($this->isSiteSearchEnabled()) {
+ $selectFlagNoResultKeywords = ",
+ CASE WHEN (MAX(log_link_visit_action.custom_var_v" . Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT . ") = 0 AND log_link_visit_action.custom_var_k" . Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT . " = '" . Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT . "') THEN 1 ELSE 0 END AS `" . Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT . "`";
+
+ //we need an extra JOIN to know whether the referrer "idaction_name_ref" was a Site Search request
+ $from[] = array(
+ "table" => "log_action",
+ "tableAlias" => "log_action_name_ref",
+ "joinOn" => "log_link_visit_action.idaction_name_ref = log_action_name_ref.idaction"
+ );
+
+ $selectSiteSearchFollowingPages = ",
+ SUM(CASE WHEN log_action_name_ref.type = " . Piwik_Tracker_Action::TYPE_SITE_SEARCH . " THEN 1 ELSE 0 END) AS `" . Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS . "`";
+
+ $select .= $selectFlagNoResultKeywords
+ . $selectSiteSearchFollowingPages;
+ // Not working yet
// $selectRefPageIsStartingSiteSearch = ",
// SUM(CASE WHEN log_action_name_ref.type = " . Piwik_Tracker_Action::TYPE_ACTION_NAME . " THEN 1 ELSE 0 END) AS `". Piwik_Archive::INDEX_PAGE_STARTING_SITE_SEARCH_NB_HITS."`";
// . $selectRefPageIsStartingSiteSearch
// . ", idaction_url_ref, idaction_name_ref"
- }
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_name", $archiveProcessing, $rankingQuery);
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_url", $archiveProcessing, $rankingQuery);
- }
-
- /**
- * Entry actions for Page URLs and Page names
- */
- protected function archiveDayEntryActions($archiveProcessing, $rankingQueryLimit)
- {
- $rankingQuery = false;
- if ($rankingQueryLimit > 0) {
- $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
- $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
- $rankingQuery->addLabelColumn('idaction');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS);
- $rankingQuery->addColumn(array(Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS,
- Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
- Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT), 'sum');
- $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
-
- $extraSelects = 'log_action.type, log_action.name,';
- $from = array(
- "log_visit",
- array(
- "table" => "log_action",
- "joinOn" => "log_visit.%s = log_action.idaction"
- )
- );
- $orderBy = "`" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS . "` DESC, log_action.name ASC";
- } else {
- $extraSelects = false;
- $from = "log_visit";
- $orderBy = false;
- }
-
- $select = "log_visit.%s as idaction, $extraSelects
+ }
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "idaction_name", $archiveProcessing, $rankingQuery);
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "idaction_url", $archiveProcessing, $rankingQuery);
+ }
+
+ /**
+ * Entry actions for Page URLs and Page names
+ */
+ protected function archiveDayEntryActions($archiveProcessing, $rankingQueryLimit)
+ {
+ $rankingQuery = false;
+ if ($rankingQueryLimit > 0) {
+ $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
+ $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
+ $rankingQuery->addLabelColumn('idaction');
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS);
+ $rankingQuery->addColumn(array(Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
+ Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT), 'sum');
+ $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
+
+ $extraSelects = 'log_action.type, log_action.name,';
+ $from = array(
+ "log_visit",
+ array(
+ "table" => "log_action",
+ "joinOn" => "log_visit.%s = log_action.idaction"
+ )
+ );
+ $orderBy = "`" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS . "` DESC, log_action.name ASC";
+ } else {
+ $extraSelects = false;
+ $from = "log_visit";
+ $orderBy = false;
+ }
+
+ $select = "log_visit.%s as idaction, $extraSelects
count(distinct log_visit.idvisitor) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS . "`,
count(*) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS . "`,
sum(log_visit.visit_total_actions) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS . "`,
sum(log_visit.visit_total_time) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH . "`,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT . "`";
- $where = "log_visit.visit_last_action_time >= ?
+ $where = "log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?
AND log_visit.%s > 0";
- $groupBy = "log_visit.%s, idaction";
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_entry_idaction_url", $archiveProcessing, $rankingQuery);
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_entry_idaction_name", $archiveProcessing, $rankingQuery);
- }
-
-
- /**
- * Time per action
- */
- protected function archiveDayActionsTime($archiveProcessing, $rankingQueryLimit)
- {
- $rankingQuery = false;
- if ($rankingQueryLimit > 0)
- {
- $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
- $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
- $rankingQuery->addLabelColumn('idaction');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT, 'sum');
- $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
-
- $extraSelects = "log_action.type, log_action.name, count(*) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS . "`,";
- $from = array(
- "log_link_visit_action",
- array(
- "table" => "log_action",
- "joinOn" => "log_link_visit_action.%s = log_action.idaction"
- )
- );
- $orderBy = "`" . Piwik_Archive::INDEX_PAGE_NB_HITS . "` DESC, log_action.name ASC";
- } else {
- $extraSelects = false;
- $from = "log_link_visit_action";
- $orderBy = false;
- }
-
- $select = "log_link_visit_action.%s as idaction, $extraSelects
+ $groupBy = "log_visit.%s, idaction";
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "visit_entry_idaction_url", $archiveProcessing, $rankingQuery);
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "visit_entry_idaction_name", $archiveProcessing, $rankingQuery);
+ }
+
+
+ /**
+ * Time per action
+ */
+ protected function archiveDayActionsTime($archiveProcessing, $rankingQueryLimit)
+ {
+ $rankingQuery = false;
+ if ($rankingQueryLimit > 0) {
+ $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
+ $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
+ $rankingQuery->addLabelColumn('idaction');
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT, 'sum');
+ $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
+
+ $extraSelects = "log_action.type, log_action.name, count(*) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS . "`,";
+ $from = array(
+ "log_link_visit_action",
+ array(
+ "table" => "log_action",
+ "joinOn" => "log_link_visit_action.%s = log_action.idaction"
+ )
+ );
+ $orderBy = "`" . Piwik_Archive::INDEX_PAGE_NB_HITS . "` DESC, log_action.name ASC";
+ } else {
+ $extraSelects = false;
+ $from = "log_link_visit_action";
+ $orderBy = false;
+ }
+
+ $select = "log_link_visit_action.%s as idaction, $extraSelects
sum(log_link_visit_action.time_spent_ref_action) as `" . Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT . "`";
- $where = "log_link_visit_action.server_time >= ?
+ $where = "log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?
AND log_link_visit_action.time_spent_ref_action > 0
AND log_link_visit_action.%s > 0";
- $groupBy = "log_link_visit_action.%s, idaction";
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_url_ref", $archiveProcessing, $rankingQuery);
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_name_ref", $archiveProcessing, $rankingQuery);
- }
-
- /**
- * Exit actions
- */
- public function archiveDayExitActions($archiveProcessing, $rankingQueryLimit)
- {
- $rankingQuery = false;
- if ($rankingQueryLimit > 0) {
- $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
- $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
- $rankingQuery->addLabelColumn('idaction');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS);
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS, 'sum');
- $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
-
- $extraSelects = 'log_action.type, log_action.name,';
- $from = array(
- "log_visit",
- array(
- "table" => "log_action",
- "joinOn" => "log_visit.%s = log_action.idaction"
- )
- );
- $orderBy = "`" . Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS . "` DESC, log_action.name ASC";
- } else {
- $extraSelects = false;
- $from = "log_visit";
- $orderBy = false;
- }
-
- $select = "log_visit.%s as idaction, $extraSelects
+ $groupBy = "log_link_visit_action.%s, idaction";
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "idaction_url_ref", $archiveProcessing, $rankingQuery);
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "idaction_name_ref", $archiveProcessing, $rankingQuery);
+ }
+
+ /**
+ * Exit actions
+ */
+ public function archiveDayExitActions($archiveProcessing, $rankingQueryLimit)
+ {
+ $rankingQuery = false;
+ if ($rankingQueryLimit > 0) {
+ $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
+ $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
+ $rankingQuery->addLabelColumn('idaction');
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS);
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS, 'sum');
+ $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
+
+ $extraSelects = 'log_action.type, log_action.name,';
+ $from = array(
+ "log_visit",
+ array(
+ "table" => "log_action",
+ "joinOn" => "log_visit.%s = log_action.idaction"
+ )
+ );
+ $orderBy = "`" . Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS . "` DESC, log_action.name ASC";
+ } else {
+ $extraSelects = false;
+ $from = "log_visit";
+ $orderBy = false;
+ }
+
+ $select = "log_visit.%s as idaction, $extraSelects
count(distinct log_visit.idvisitor) as `" . Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS . "`,
count(*) as `" . Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS . "`";
- $where = "log_visit.visit_last_action_time >= ?
+ $where = "log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?
AND log_visit.%s > 0";
- $groupBy = "log_visit.%s, idaction";
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_exit_idaction_url", $archiveProcessing, $rankingQuery);
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_exit_idaction_name", $archiveProcessing, $rankingQuery);
- return array($rankingQuery, $extraSelects, $from, $orderBy, $select, $where, $groupBy);
- }
-
-
- /**
- * Records in the DB the archived reports for Page views, Downloads, Outlinks, and Page titles
- *
- * @param $archiveProcessing
- */
- protected function archiveDayRecordInDatabase($archiveProcessing)
- {
- Piwik_Actions_ArchivingHelper::clearActionsCache();
-
- /** @var Piwik_DataTable $dataTable */
- $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_URL];
- self::deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized( Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation );
- $archiveProcessing->insertBlobRecord('Actions_actions_url', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_pageviews', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_uniq_pageviews', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- destroy($dataTable);
-
- $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_DOWNLOAD];
- self::deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation );
- $archiveProcessing->insertBlobRecord('Actions_downloads', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_downloads', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_uniq_downloads', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- destroy($dataTable);
-
- $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_OUTLINK];
- self::deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized( Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation );
- $archiveProcessing->insertBlobRecord('Actions_outlink', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_outlinks', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_uniq_outlinks', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- destroy($dataTable);
-
- $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_NAME];
- self::deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized( Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation );
- $archiveProcessing->insertBlobRecord('Actions_actions', $s);
- destroy($dataTable);
-
- $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_SITE_SEARCH];
- self::deleteInvalidSummedColumnsFromDataTable($dataTable);
- $this->deleteUnusedColumnsFromKeywordsDataTable($dataTable);
- $s = $dataTable->getSerialized( Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation );
- $archiveProcessing->insertBlobRecord('Actions_sitesearch', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_searches', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_keywords', $dataTable->getRowsCount());
- destroy($dataTable);
-
- destroy($this->actionsTablesByType);
- }
-
- protected function deleteUnusedColumnsFromKeywordsDataTable($dataTable)
- {
- $columnsToDelete = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS,
- Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS,
- Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT,
- Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
- );
- $dataTable->deleteColumns($columnsToDelete);
- }
-
- static protected function removeEmptyColumns($dataTable)
- {
- // Delete all columns that have a value of zero
- $dataTable->filter('ColumnDelete', array(
- $columnsToRemove = array(Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS),
- $columnsToKeep = array(),
- $deleteIfZeroOnly = true
- ));
- }
-
-
- /**
- * Returns the limit to use with RankingQuery for this plugin.
- *
- * @return int
- */
- private static function getRankingQueryLimit()
- {
- $configGeneral = Piwik_Config::getInstance()->General;
- $configLimit = $configGeneral['archiving_ranking_query_row_limit'];
- return $configLimit == 0 ? 0 : max(
- $configLimit,
- $configGeneral['datatable_archiving_maximum_rows_actions'],
- $configGeneral['datatable_archiving_maximum_rows_subtable_actions']
- );
- }
-
-
- /**
- * @param $select
- * @param $from
- * @param $where
- * @param $orderBy
- * @param $groupBy
- * @param $sprintfField
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @param Piwik_RankingQuery|false $rankingQuery
- * @return int
- */
- protected function archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- $sprintfField, $archiveProcessing, $rankingQuery = false)
- {
- // idaction field needs to be set in select clause before calling getSelectQuery().
- // if a complex segmentation join is needed, the field needs to be propagated
- // to the outer select. therefore, $segment needs to know about it.
- $select = sprintf($select, $sprintfField);
-
- $bind = array();
-
- // get query with segmentation
- $query = $archiveProcessing->getSegment()->getSelectQuery(
- $select, $from, $where, $bind, $orderBy, $groupBy);
-
- // extend bindings
- $bind = array_merge(array( $archiveProcessing->getStartDatetimeUTC(),
- $archiveProcessing->getEndDatetimeUTC(),
- $archiveProcessing->idsite
- ),
- $query['bind']
- );
-
- // replace the rest of the %s
- $querySql = str_replace("%s", $sprintfField, $query['sql']);
-
- // apply ranking query
- if ($rankingQuery)
- {
- $querySql = $rankingQuery->generateQuery($querySql);
- }
-
- // get result
- $resultSet = $archiveProcessing->db->query($querySql, $bind);
- $modified = Piwik_Actions_ArchivingHelper::updateActionsTableWithRowQuery($resultSet, $sprintfField, $this->actionsTablesByType);
- return $modified;
- }
-
-
- /**
- * For rows which have subtables (eg. directories with sub pages),
- * deletes columns which don't make sense when all values of sub pages are summed.
- *
- * @param $dataTable Piwik_DataTable
- */
- static public function deleteInvalidSummedColumnsFromDataTable($dataTable)
- {
- foreach($dataTable->getRows() as $id => $row)
- {
- if(($idSubtable = $row->getIdSubDataTable()) !== null
- || $id === Piwik_DataTable::ID_SUMMARY_ROW)
- {
- if ($idSubtable !== null)
- {
- $subtable = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable);
- self::deleteInvalidSummedColumnsFromDataTable($subtable);
- }
-
- if ($row instanceof Piwik_DataTable_Row_DataTableSummary)
- {
- $row->recalculate();
- }
-
- foreach(self::$invalidSummedColumnNameToDeleteFromDayArchive as $name)
- {
- $row->deleteColumn($name);
- }
- }
- }
-
- // And this as well
- self::removeEmptyColumns($dataTable);
- }
-
- /**
- * Initializes the DataTables created by the archiveDay function.
- */
- private function initActionsTables()
- {
- $this->actionsTablesByType = array();
- foreach (self::$actionTypes as $type)
- {
- $dataTable = new Piwik_DataTable();
- $dataTable->setMaximumAllowedRows(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero);
-
- $this->actionsTablesByType[$type] = $dataTable;
- }
- }
-
- protected function isSiteSearchEnabled()
- {
- return $this->isSiteSearchEnabled;
- }
+ $groupBy = "log_visit.%s, idaction";
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "visit_exit_idaction_url", $archiveProcessing, $rankingQuery);
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "visit_exit_idaction_name", $archiveProcessing, $rankingQuery);
+ return array($rankingQuery, $extraSelects, $from, $orderBy, $select, $where, $groupBy);
+ }
+
+
+ /**
+ * Records in the DB the archived reports for Page views, Downloads, Outlinks, and Page titles
+ *
+ * @param $archiveProcessing
+ */
+ protected function archiveDayRecordInDatabase($archiveProcessing)
+ {
+ Piwik_Actions_ArchivingHelper::clearActionsCache();
+
+ /** @var Piwik_DataTable $dataTable */
+ $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_URL];
+ self::deleteInvalidSummedColumnsFromDataTable($dataTable);
+ $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
+ $archiveProcessing->insertBlobRecord('Actions_actions_url', $s);
+ $archiveProcessing->insertNumericRecord('Actions_nb_pageviews', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
+ $archiveProcessing->insertNumericRecord('Actions_nb_uniq_pageviews', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
+ destroy($dataTable);
+
+ $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_DOWNLOAD];
+ self::deleteInvalidSummedColumnsFromDataTable($dataTable);
+ $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
+ $archiveProcessing->insertBlobRecord('Actions_downloads', $s);
+ $archiveProcessing->insertNumericRecord('Actions_nb_downloads', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
+ $archiveProcessing->insertNumericRecord('Actions_nb_uniq_downloads', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
+ destroy($dataTable);
+
+ $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_OUTLINK];
+ self::deleteInvalidSummedColumnsFromDataTable($dataTable);
+ $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
+ $archiveProcessing->insertBlobRecord('Actions_outlink', $s);
+ $archiveProcessing->insertNumericRecord('Actions_nb_outlinks', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
+ $archiveProcessing->insertNumericRecord('Actions_nb_uniq_outlinks', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
+ destroy($dataTable);
+
+ $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_NAME];
+ self::deleteInvalidSummedColumnsFromDataTable($dataTable);
+ $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
+ $archiveProcessing->insertBlobRecord('Actions_actions', $s);
+ destroy($dataTable);
+
+ $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_SITE_SEARCH];
+ self::deleteInvalidSummedColumnsFromDataTable($dataTable);
+ $this->deleteUnusedColumnsFromKeywordsDataTable($dataTable);
+ $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
+ $archiveProcessing->insertBlobRecord('Actions_sitesearch', $s);
+ $archiveProcessing->insertNumericRecord('Actions_nb_searches', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
+ $archiveProcessing->insertNumericRecord('Actions_nb_keywords', $dataTable->getRowsCount());
+ destroy($dataTable);
+
+ destroy($this->actionsTablesByType);
+ }
+
+ protected function deleteUnusedColumnsFromKeywordsDataTable($dataTable)
+ {
+ $columnsToDelete = array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT,
+ Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
+ );
+ $dataTable->deleteColumns($columnsToDelete);
+ }
+
+ static protected function removeEmptyColumns($dataTable)
+ {
+ // Delete all columns that have a value of zero
+ $dataTable->filter('ColumnDelete', array(
+ $columnsToRemove = array(Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS),
+ $columnsToKeep = array(),
+ $deleteIfZeroOnly = true
+ ));
+ }
+
+
+ /**
+ * Returns the limit to use with RankingQuery for this plugin.
+ *
+ * @return int
+ */
+ private static function getRankingQueryLimit()
+ {
+ $configGeneral = Piwik_Config::getInstance()->General;
+ $configLimit = $configGeneral['archiving_ranking_query_row_limit'];
+ return $configLimit == 0 ? 0 : max(
+ $configLimit,
+ $configGeneral['datatable_archiving_maximum_rows_actions'],
+ $configGeneral['datatable_archiving_maximum_rows_subtable_actions']
+ );
+ }
+
+
+ /**
+ * @param $select
+ * @param $from
+ * @param $where
+ * @param $orderBy
+ * @param $groupBy
+ * @param $sprintfField
+ * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @param Piwik_RankingQuery|false $rankingQuery
+ * @return int
+ */
+ protected function archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ $sprintfField, $archiveProcessing, $rankingQuery = false)
+ {
+ // idaction field needs to be set in select clause before calling getSelectQuery().
+ // if a complex segmentation join is needed, the field needs to be propagated
+ // to the outer select. therefore, $segment needs to know about it.
+ $select = sprintf($select, $sprintfField);
+
+ $bind = array();
+
+ // get query with segmentation
+ $query = $archiveProcessing->getSegment()->getSelectQuery(
+ $select, $from, $where, $bind, $orderBy, $groupBy);
+
+ // extend bindings
+ $bind = array_merge(array($archiveProcessing->getStartDatetimeUTC(),
+ $archiveProcessing->getEndDatetimeUTC(),
+ $archiveProcessing->idsite
+ ),
+ $query['bind']
+ );
+
+ // replace the rest of the %s
+ $querySql = str_replace("%s", $sprintfField, $query['sql']);
+
+ // apply ranking query
+ if ($rankingQuery) {
+ $querySql = $rankingQuery->generateQuery($querySql);
+ }
+
+ // get result
+ $resultSet = $archiveProcessing->db->query($querySql, $bind);
+ $modified = Piwik_Actions_ArchivingHelper::updateActionsTableWithRowQuery($resultSet, $sprintfField, $this->actionsTablesByType);
+ return $modified;
+ }
+
+
+ /**
+ * For rows which have subtables (eg. directories with sub pages),
+ * deletes columns which don't make sense when all values of sub pages are summed.
+ *
+ * @param $dataTable Piwik_DataTable
+ */
+ static public function deleteInvalidSummedColumnsFromDataTable($dataTable)
+ {
+ foreach ($dataTable->getRows() as $id => $row) {
+ if (($idSubtable = $row->getIdSubDataTable()) !== null
+ || $id === Piwik_DataTable::ID_SUMMARY_ROW
+ ) {
+ if ($idSubtable !== null) {
+ $subtable = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable);
+ self::deleteInvalidSummedColumnsFromDataTable($subtable);
+ }
+
+ if ($row instanceof Piwik_DataTable_Row_DataTableSummary) {
+ $row->recalculate();
+ }
+
+ foreach (self::$invalidSummedColumnNameToDeleteFromDayArchive as $name) {
+ $row->deleteColumn($name);
+ }
+ }
+ }
+
+ // And this as well
+ self::removeEmptyColumns($dataTable);
+ }
+
+ /**
+ * Initializes the DataTables created by the archiveDay function.
+ */
+ private function initActionsTables()
+ {
+ $this->actionsTablesByType = array();
+ foreach (self::$actionTypes as $type) {
+ $dataTable = new Piwik_DataTable();
+ $dataTable->setMaximumAllowedRows(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero);
+
+ $this->actionsTablesByType[$type] = $dataTable;
+ }
+ }
+
+ protected function isSiteSearchEnabled()
+ {
+ return $this->isSiteSearchEnabled;
+ }
}
diff --git a/plugins/Actions/ArchivingHelper.php b/plugins/Actions/ArchivingHelper.php
index 30fb58b555..739e8a2fef 100644
--- a/plugins/Actions/ArchivingHelper.php
+++ b/plugins/Actions/ArchivingHelper.php
@@ -19,495 +19,452 @@
class Piwik_Actions_ArchivingHelper
{
- const OTHERS_ROW_KEY = '';
-
- /**
- * @param Zend_Db_Statement|PDOStatement $query
- * @param string|bool $fieldQueried
- * @param array $actionsTablesByType
- * @return int
- */
- static public function updateActionsTableWithRowQuery($query, $fieldQueried, & $actionsTablesByType)
- {
- $rowsProcessed = 0;
- while( $row = $query->fetch() )
- {
- if(empty($row['idaction']))
- {
- $row['type'] = ($fieldQueried == 'idaction_url' ? Piwik_Tracker_Action::TYPE_ACTION_URL : Piwik_Tracker_Action::TYPE_ACTION_NAME);
- // This will be replaced with 'X not defined' later
- $row['name'] = '';
- // Yes, this is kind of a hack, so we don't mix 'page url not defined' with 'page title not defined' etc.
- $row['idaction'] = -$row['type'];
- }
-
- if($row['type'] != Piwik_Tracker_Action::TYPE_SITE_SEARCH)
- {
- unset($row[Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT]);
- }
-
- // This will appear as <url /> in the API, which is actually very important to keep
- // eg. When there's at least one row in a report that does not have a URL, not having this <url/> would break HTML/PDF reports.
- $url = '';
- if($row['type'] == Piwik_Tracker_Action::TYPE_SITE_SEARCH
- || $row['type'] == Piwik_Tracker_Action::TYPE_ACTION_NAME)
- {
- $url = null;
- }
- elseif(!empty($row['name'])
- && $row['name'] != Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- $url = Piwik_Tracker_Action::reconstructNormalizedUrl((string)$row['name'], $row['url_prefix']);
- }
-
- if(isset($row['name'])
- && isset($row['type']))
- {
- $actionName = $row['name'];
- $actionType = $row['type'];
- $urlPrefix = $row['url_prefix'];
- $idaction = $row['idaction'];
-
- // in some unknown case, the type field is NULL, as reported in #1082 - we ignore this page view
- if(empty($actionType))
- {
- if ($idaction != Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- self::setCachedActionRow($idaction, $actionType, false);
- }
- continue;
- }
-
- $actionRow = self::getActionRow($actionName, $actionType, $urlPrefix, $actionsTablesByType);
-
- self::setCachedActionRow($idaction, $actionType, $actionRow);
- }
- else
- {
- $actionRow = self::getCachedActionRow($row['idaction'], $row['type']);
-
- // Action processed as "to skip" for some reasons
- if($actionRow === false)
- {
- continue;
- }
- }
-
-
- if (is_null($actionRow))
- {
- continue;
- }
-
- // Here we do ensure that, the Metadata URL set for a given row, is the one from the Pageview with the most hits.
- // This is to ensure that when, different URLs are loaded with the same page name.
- // For example http://piwik.org and http://id.piwik.org are reported in Piwik > Actions > Pages with /index
- // But, we must make sure http://piwik.org is used to link & for transitions
- // Note: this code is partly duplicated from Piwik_DataTable_Row->sumRowMetadata()
- if( !is_null($url)
- && !$actionRow->isSummaryRow())
- {
- if(($existingUrl = $actionRow->getMetadata('url')) !== false)
- {
- if( !empty($row[Piwik_Archive::INDEX_PAGE_NB_HITS])
- && $row[Piwik_Archive::INDEX_PAGE_NB_HITS] > $actionRow->maxVisitsSummed)
- {
- $actionRow->setMetadata('url', $url);
- $actionRow->maxVisitsSummed = $row[Piwik_Archive::INDEX_PAGE_NB_HITS];
- }
- }
- else
- {
- $actionRow->setMetadata('url', $url);
- $actionRow->maxVisitsSummed = !empty($row[Piwik_Archive::INDEX_PAGE_NB_HITS]) ? $row[Piwik_Archive::INDEX_PAGE_NB_HITS] : 0;
- }
- }
-
- if ($row['type'] != Piwik_Tracker_Action::TYPE_ACTION_URL
- && $row['type'] != Piwik_Tracker_Action::TYPE_ACTION_NAME) {
- // only keep performance metrics when they're used (i.e. for URLs and page titles)
- if (array_key_exists(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, $row)) {
- unset($row[Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION]);
- }
- if (array_key_exists(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, $row)) {
- unset($row[Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION]);
- }
- }
-
- unset($row['name']);
- unset($row['type']);
- unset($row['idaction']);
- unset($row['url_prefix']);
-
- foreach($row as $name => $value)
- {
- // in some edge cases, we have twice the same action name with 2 different idaction
- // - this happens when 2 visitors visit the same new page at the same time, and 2 actions get recorded for the same name
- // - this could also happen when 2 URLs end up having the same label (eg. 2 subdomains get aggregated to the "/index" page name)
- if(($alreadyValue = $actionRow->getColumn($name)) !== false)
- {
- $actionRow->setColumn($name, $alreadyValue+$value);
- }
- else
- {
- $actionRow->addColumn($name, $value);
- }
- }
-
- // if the exit_action was not recorded properly in the log_link_visit_action
- // there would be an error message when getting the nb_hits column
- // we must fake the record and add the columns
- if($actionRow->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS) === false)
- {
- // to test this code: delete the entries in log_link_action_visit for
- // a given exit_idaction_url
- foreach(self::getDefaultRow()->getColumns() as $name => $value)
- {
- $actionRow->addColumn($name, $value);
- }
- }
- $rowsProcessed++;
- }
-
- // just to make sure php copies the last $actionRow in the $parentTable array
- $actionRow =& $actionsTablesByType;
- return $rowsProcessed;
- }
-
- static public $maximumRowsInDataTableLevelZero;
- static public $maximumRowsInSubDataTable;
- static public $columnToSortByBeforeTruncation;
-
- static protected $actionUrlCategoryDelimiter = null;
- static protected $actionTitleCategoryDelimiter = null;
- static protected $defaultActionName = null;
- static protected $defaultActionNameWhenNotDefined = null;
- static protected $defaultActionUrlWhenNotDefined = null;
-
- static public function reloadConfig()
- {
- // for BC, we read the old style delimiter first (see #1067)Row
- $actionDelimiter = @Piwik_Config::getInstance()->General['action_category_delimiter'];
- if(empty($actionDelimiter))
- {
- self::$actionUrlCategoryDelimiter = Piwik_Config::getInstance()->General['action_url_category_delimiter'];
- self::$actionTitleCategoryDelimiter = Piwik_Config::getInstance()->General['action_title_category_delimiter'];
- }
- else
- {
- self::$actionUrlCategoryDelimiter = self::$actionTitleCategoryDelimiter = $actionDelimiter;
- }
-
- self::$defaultActionName = Piwik_Config::getInstance()->General['action_default_name'];
- self::$columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
- self::$maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_actions'];
- self::$maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_actions'];
-
- Piwik_DataTable::setMaximumDepthLevelAllowedAtLeast(self::getSubCategoryLevelLimit() + 1);
- }
-
-
- /**
- * The default row is used when archiving, if data is inconsistent in the DB,
- * there could be pages that have exit/entry hits, but don't yet
- * have a record in the table (or the record was truncated).
- *
- * @return Piwik_DataTable_Row
- */
- static private function getDefaultRow()
- {
- static $row = false;
- if($row === false) {
- // This row is used in the case where an action is know as an exit_action
- // but this action was not properly recorded when it was hit in the first place
- // so we add this fake row information to make sure there is a nb_hits, etc. column for every action
- $row = new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => array(
- Piwik_Archive::INDEX_NB_VISITS => 1,
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 1,
- Piwik_Archive::INDEX_PAGE_NB_HITS => 1,
- )));
- }
- return $row;
- }
-
- /**
- * Given a page name and type, builds a recursive datatable where
- * each level of the tree is a category, based on the page name split by a delimiter (slash / by default)
- *
- * @param string $actionName
- * @param int $actionType
- * @param int $urlPrefix
- * @param array $actionsTablesByType
- * @return Piwik_DataTable
- */
- protected static function getActionRow( $actionName, $actionType, $urlPrefix=null, &$actionsTablesByType )
- {
- // we work on the root table of the given TYPE (either ACTION_URL or DOWNLOAD or OUTLINK etc.)
- /* @var Piwik_DataTable $currentTable */
- $currentTable =& $actionsTablesByType[$actionType];
-
- // check for ranking query cut-off
- if ($actionName == Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- $summaryRow = $currentTable->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW);
- if ($summaryRow === false)
- {
- $summaryRow = $currentTable->addSummaryRow(self::createSummaryRow());
- }
- return $summaryRow;
- }
-
- // go to the level of the subcategory
- $actionExplodedNames = self::getActionExplodedNames($actionName, $actionType, $urlPrefix);
- list($row, $level) = $currentTable->walkPath(
- $actionExplodedNames, self::getDefaultRowColumns(), self::$maximumRowsInSubDataTable);
-
- return $row;
- }
-
- /**
- * Explodes action name into an array of elements.
- *
- * NOTE: before calling this function make sure Piwik_Actions_ArchivingHelper::reloadConfig(); is called
- *
- * for downloads:
- * we explode link http://piwik.org/some/path/piwik.zip into an array( 'piwik.org', '/some/path/piwik.zip' );
- *
- * for outlinks:
- * we explode link http://dev.piwik.org/some/path into an array( 'dev.piwik.org', '/some/path' );
- *
- * for action urls:
- * we explode link http://piwik.org/some/path into an array( 'some', 'path' );
- *
- * for action names:
- * we explode name 'Piwik / Category 1 / Category 2' into an array('Piwik', 'Category 1', 'Category 2');
- *
- * @param string action name
- * @param int action type
- * @param int url prefix (only used for TYPE_ACTION_URL)
- * @return array of exploded elements from $name
- */
- static public function getActionExplodedNames($name, $type, $urlPrefix=null)
- {
- // Site Search does not split Search keywords
- if($type == Piwik_Tracker_Action::TYPE_SITE_SEARCH)
- {
- return array($name);
- }
-
- $matches = array();
- $isUrl = false;
- $name = str_replace("\n", "", $name);
-
- $urlRegexAfterDomain = '([^/]+)[/]?([^#]*)[#]?(.*)';
- if ($urlPrefix === null)
- {
- // match url with protocol (used for outlinks / downloads)
- $urlRegex = '@^http[s]?://'.$urlRegexAfterDomain.'$@i';
- }
- else
- {
- // the name is a url that does not contain protocol and www anymore
- // we know that normalization has been done on db level because $urlPrefix is set
- $urlRegex = '@^'.$urlRegexAfterDomain.'$@i';
- }
-
- preg_match($urlRegex, $name, $matches);
- if( count($matches) )
- {
- $isUrl = true;
- $urlHost = $matches[1];
- $urlPath = $matches[2];
- $urlFragment = $matches[3];
- }
-
- if($type == Piwik_Tracker_Action::TYPE_DOWNLOAD
- || $type == Piwik_Tracker_Action::TYPE_OUTLINK)
- {
- if( $isUrl )
- {
- return array(trim($urlHost), '/' . trim($urlPath));
- }
- }
-
- if( $isUrl )
- {
- $name = $urlPath;
-
- if( $name === '' || substr($name, -1) == '/' )
- {
- $name .= self::$defaultActionName;
- }
- }
-
- if($type == Piwik_Tracker_Action::TYPE_ACTION_NAME)
- {
- $categoryDelimiter = self::$actionTitleCategoryDelimiter;
- }
- else
- {
- $categoryDelimiter = self::$actionUrlCategoryDelimiter;
- }
-
-
- if( $isUrl )
- {
- $urlFragment = Piwik_Tracker_Action::processUrlFragment($urlFragment);
- if(!empty($urlFragment)) {
- $name .= '#' . $urlFragment;
- }
- }
-
- if(empty($categoryDelimiter))
- {
- return array( trim($name) );
- }
-
- $split = explode($categoryDelimiter, $name, self::getSubCategoryLevelLimit());
-
- // trim every category and remove empty categories
- $split = array_map('trim', $split);
- $split = array_filter($split, 'strlen');
-
- // forces array key to start at 0
- $split = array_values($split);
-
- if( empty($split) )
- {
- $defaultName = self::getUnknownActionName($type);
- return array( trim($defaultName) );
- }
-
- $lastPageName = end($split);
- // we are careful to prefix the page URL / name with some value
- // so that if a page has the same name as a category
- // we don't merge both entries
- if($type != Piwik_Tracker_Action::TYPE_ACTION_NAME )
- {
- $lastPageName = '/' . $lastPageName;
- }
- else
- {
- $lastPageName = ' ' . $lastPageName;
- }
- $split[count($split)-1] = $lastPageName;
- return array_values( $split );
- }
-
- /**
- * Gets the key for the cache of action rows from an action ID and type.
- *
- * @param int $idAction
- * @param int $actionType
- * @return string|int
- */
- private static function getCachedActionRowKey( $idAction, $actionType )
- {
- return $idAction == Piwik_DataTable::LABEL_SUMMARY_ROW
- ? $actionType.'_others'
- : $idAction;
- }
-
- /**
- * Returns the configured sub-category level limit.
- *
- * @return int
- */
- public static function getSubCategoryLevelLimit()
- {
- return Piwik_Config::getInstance()->General['action_category_level_limit'];
- }
-
- /**
- * Returns default label for the action type
- *
- * @param $type
- * @return string
- */
- static public function getUnknownActionName($type)
- {
- if(empty(self::$defaultActionNameWhenNotDefined))
- {
- self::$defaultActionNameWhenNotDefined = Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageName'));
- self::$defaultActionUrlWhenNotDefined = Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageURL'));
- }
- if($type == Piwik_Tracker_Action::TYPE_ACTION_NAME)
- {
- return self::$defaultActionNameWhenNotDefined;
- }
- return self::$defaultActionUrlWhenNotDefined;
- }
-
- /**
- * Static cache to store Rows during processing
- */
- static protected $cacheParsedAction = array();
-
- public static function clearActionsCache()
- {
- self::$cacheParsedAction = array();
- }
-
- /**
- * Get cached action row by id & type. If $idAction is set to -1, the 'Others' row
- * for the specific action type will be returned.
- *
- * @param int $idAction
- * @param int $actionType
- * @return Piwik_DataTable_Row|false
- */
- private static function getCachedActionRow( $idAction, $actionType )
- {
- $cacheLabel = self::getCachedActionRowKey($idAction, $actionType);
-
- if (!isset(self::$cacheParsedAction[$cacheLabel]))
- {
- // This can happen when
- // - We select an entry page ID that was only seen yesterday, so wasn't selected in the first query
- // - We count time spent on a page, when this page was only seen yesterday
- return false;
- }
-
- return self::$cacheParsedAction[$cacheLabel];
- }
-
- /**
- * Set cached action row for an id & type.
- *
- * @param int $idAction
- * @param int $actionType
- * @param Piwik_DataTable_Row
- */
- private static function setCachedActionRow( $idAction, $actionType, $actionRow )
- {
- $cacheLabel = self::getCachedActionRowKey($idAction, $actionType);
- self::$cacheParsedAction[$cacheLabel] = $actionRow;
- }
-
- /**
- * Returns the default columns for a row in an Actions DataTable.
- *
- * @return array
- */
- private static function getDefaultRowColumns()
- {
- return array(Piwik_Archive::INDEX_NB_VISITS => 0,
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
- Piwik_Archive::INDEX_PAGE_NB_HITS => 0,
- Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT => 0);
- }
-
- /**
- * Creates a summary row for an Actions DataTable.
- *
- * @return Piwik_DataTable_Row
- */
- private static function createSummaryRow()
- {
- return new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS =>
- array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW) + self::getDefaultRowColumns()
- ));
- }
+ const OTHERS_ROW_KEY = '';
+
+ /**
+ * @param Zend_Db_Statement|PDOStatement $query
+ * @param string|bool $fieldQueried
+ * @param array $actionsTablesByType
+ * @return int
+ */
+ static public function updateActionsTableWithRowQuery($query, $fieldQueried, & $actionsTablesByType)
+ {
+ $rowsProcessed = 0;
+ while ($row = $query->fetch()) {
+ if (empty($row['idaction'])) {
+ $row['type'] = ($fieldQueried == 'idaction_url' ? Piwik_Tracker_Action::TYPE_ACTION_URL : Piwik_Tracker_Action::TYPE_ACTION_NAME);
+ // This will be replaced with 'X not defined' later
+ $row['name'] = '';
+ // Yes, this is kind of a hack, so we don't mix 'page url not defined' with 'page title not defined' etc.
+ $row['idaction'] = -$row['type'];
+ }
+
+ if ($row['type'] != Piwik_Tracker_Action::TYPE_SITE_SEARCH) {
+ unset($row[Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT]);
+ }
+
+ // This will appear as <url /> in the API, which is actually very important to keep
+ // eg. When there's at least one row in a report that does not have a URL, not having this <url/> would break HTML/PDF reports.
+ $url = '';
+ if ($row['type'] == Piwik_Tracker_Action::TYPE_SITE_SEARCH
+ || $row['type'] == Piwik_Tracker_Action::TYPE_ACTION_NAME
+ ) {
+ $url = null;
+ } elseif (!empty($row['name'])
+ && $row['name'] != Piwik_DataTable::LABEL_SUMMARY_ROW
+ ) {
+ $url = Piwik_Tracker_Action::reconstructNormalizedUrl((string)$row['name'], $row['url_prefix']);
+ }
+
+ if (isset($row['name'])
+ && isset($row['type'])
+ ) {
+ $actionName = $row['name'];
+ $actionType = $row['type'];
+ $urlPrefix = $row['url_prefix'];
+ $idaction = $row['idaction'];
+
+ // in some unknown case, the type field is NULL, as reported in #1082 - we ignore this page view
+ if (empty($actionType)) {
+ if ($idaction != Piwik_DataTable::LABEL_SUMMARY_ROW) {
+ self::setCachedActionRow($idaction, $actionType, false);
+ }
+ continue;
+ }
+
+ $actionRow = self::getActionRow($actionName, $actionType, $urlPrefix, $actionsTablesByType);
+
+ self::setCachedActionRow($idaction, $actionType, $actionRow);
+ } else {
+ $actionRow = self::getCachedActionRow($row['idaction'], $row['type']);
+
+ // Action processed as "to skip" for some reasons
+ if ($actionRow === false) {
+ continue;
+ }
+ }
+
+
+ if (is_null($actionRow)) {
+ continue;
+ }
+
+ // Here we do ensure that, the Metadata URL set for a given row, is the one from the Pageview with the most hits.
+ // This is to ensure that when, different URLs are loaded with the same page name.
+ // For example http://piwik.org and http://id.piwik.org are reported in Piwik > Actions > Pages with /index
+ // But, we must make sure http://piwik.org is used to link & for transitions
+ // Note: this code is partly duplicated from Piwik_DataTable_Row->sumRowMetadata()
+ if (!is_null($url)
+ && !$actionRow->isSummaryRow()
+ ) {
+ if (($existingUrl = $actionRow->getMetadata('url')) !== false) {
+ if (!empty($row[Piwik_Archive::INDEX_PAGE_NB_HITS])
+ && $row[Piwik_Archive::INDEX_PAGE_NB_HITS] > $actionRow->maxVisitsSummed
+ ) {
+ $actionRow->setMetadata('url', $url);
+ $actionRow->maxVisitsSummed = $row[Piwik_Archive::INDEX_PAGE_NB_HITS];
+ }
+ } else {
+ $actionRow->setMetadata('url', $url);
+ $actionRow->maxVisitsSummed = !empty($row[Piwik_Archive::INDEX_PAGE_NB_HITS]) ? $row[Piwik_Archive::INDEX_PAGE_NB_HITS] : 0;
+ }
+ }
+
+ if ($row['type'] != Piwik_Tracker_Action::TYPE_ACTION_URL
+ && $row['type'] != Piwik_Tracker_Action::TYPE_ACTION_NAME
+ ) {
+ // only keep performance metrics when they're used (i.e. for URLs and page titles)
+ if (array_key_exists(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, $row)) {
+ unset($row[Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION]);
+ }
+ if (array_key_exists(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, $row)) {
+ unset($row[Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION]);
+ }
+ }
+
+ unset($row['name']);
+ unset($row['type']);
+ unset($row['idaction']);
+ unset($row['url_prefix']);
+
+ foreach ($row as $name => $value) {
+ // in some edge cases, we have twice the same action name with 2 different idaction
+ // - this happens when 2 visitors visit the same new page at the same time, and 2 actions get recorded for the same name
+ // - this could also happen when 2 URLs end up having the same label (eg. 2 subdomains get aggregated to the "/index" page name)
+ if (($alreadyValue = $actionRow->getColumn($name)) !== false) {
+ $actionRow->setColumn($name, $alreadyValue + $value);
+ } else {
+ $actionRow->addColumn($name, $value);
+ }
+ }
+
+ // if the exit_action was not recorded properly in the log_link_visit_action
+ // there would be an error message when getting the nb_hits column
+ // we must fake the record and add the columns
+ if ($actionRow->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS) === false) {
+ // to test this code: delete the entries in log_link_action_visit for
+ // a given exit_idaction_url
+ foreach (self::getDefaultRow()->getColumns() as $name => $value) {
+ $actionRow->addColumn($name, $value);
+ }
+ }
+ $rowsProcessed++;
+ }
+
+ // just to make sure php copies the last $actionRow in the $parentTable array
+ $actionRow =& $actionsTablesByType;
+ return $rowsProcessed;
+ }
+
+ static public $maximumRowsInDataTableLevelZero;
+ static public $maximumRowsInSubDataTable;
+ static public $columnToSortByBeforeTruncation;
+
+ static protected $actionUrlCategoryDelimiter = null;
+ static protected $actionTitleCategoryDelimiter = null;
+ static protected $defaultActionName = null;
+ static protected $defaultActionNameWhenNotDefined = null;
+ static protected $defaultActionUrlWhenNotDefined = null;
+
+ static public function reloadConfig()
+ {
+ // for BC, we read the old style delimiter first (see #1067)Row
+ $actionDelimiter = @Piwik_Config::getInstance()->General['action_category_delimiter'];
+ if (empty($actionDelimiter)) {
+ self::$actionUrlCategoryDelimiter = Piwik_Config::getInstance()->General['action_url_category_delimiter'];
+ self::$actionTitleCategoryDelimiter = Piwik_Config::getInstance()->General['action_title_category_delimiter'];
+ } else {
+ self::$actionUrlCategoryDelimiter = self::$actionTitleCategoryDelimiter = $actionDelimiter;
+ }
+
+ self::$defaultActionName = Piwik_Config::getInstance()->General['action_default_name'];
+ self::$columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
+ self::$maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_actions'];
+ self::$maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_actions'];
+
+ Piwik_DataTable::setMaximumDepthLevelAllowedAtLeast(self::getSubCategoryLevelLimit() + 1);
+ }
+
+
+ /**
+ * The default row is used when archiving, if data is inconsistent in the DB,
+ * there could be pages that have exit/entry hits, but don't yet
+ * have a record in the table (or the record was truncated).
+ *
+ * @return Piwik_DataTable_Row
+ */
+ static private function getDefaultRow()
+ {
+ static $row = false;
+ if ($row === false) {
+ // This row is used in the case where an action is know as an exit_action
+ // but this action was not properly recorded when it was hit in the first place
+ // so we add this fake row information to make sure there is a nb_hits, etc. column for every action
+ $row = new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => array(
+ Piwik_Archive::INDEX_NB_VISITS => 1,
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 1,
+ Piwik_Archive::INDEX_PAGE_NB_HITS => 1,
+ )));
+ }
+ return $row;
+ }
+
+ /**
+ * Given a page name and type, builds a recursive datatable where
+ * each level of the tree is a category, based on the page name split by a delimiter (slash / by default)
+ *
+ * @param string $actionName
+ * @param int $actionType
+ * @param int $urlPrefix
+ * @param array $actionsTablesByType
+ * @return Piwik_DataTable
+ */
+ protected static function getActionRow($actionName, $actionType, $urlPrefix = null, &$actionsTablesByType)
+ {
+ // we work on the root table of the given TYPE (either ACTION_URL or DOWNLOAD or OUTLINK etc.)
+ /* @var Piwik_DataTable $currentTable */
+ $currentTable =& $actionsTablesByType[$actionType];
+
+ // check for ranking query cut-off
+ if ($actionName == Piwik_DataTable::LABEL_SUMMARY_ROW) {
+ $summaryRow = $currentTable->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW);
+ if ($summaryRow === false) {
+ $summaryRow = $currentTable->addSummaryRow(self::createSummaryRow());
+ }
+ return $summaryRow;
+ }
+
+ // go to the level of the subcategory
+ $actionExplodedNames = self::getActionExplodedNames($actionName, $actionType, $urlPrefix);
+ list($row, $level) = $currentTable->walkPath(
+ $actionExplodedNames, self::getDefaultRowColumns(), self::$maximumRowsInSubDataTable);
+
+ return $row;
+ }
+
+ /**
+ * Explodes action name into an array of elements.
+ *
+ * NOTE: before calling this function make sure Piwik_Actions_ArchivingHelper::reloadConfig(); is called
+ *
+ * for downloads:
+ * we explode link http://piwik.org/some/path/piwik.zip into an array( 'piwik.org', '/some/path/piwik.zip' );
+ *
+ * for outlinks:
+ * we explode link http://dev.piwik.org/some/path into an array( 'dev.piwik.org', '/some/path' );
+ *
+ * for action urls:
+ * we explode link http://piwik.org/some/path into an array( 'some', 'path' );
+ *
+ * for action names:
+ * we explode name 'Piwik / Category 1 / Category 2' into an array('Piwik', 'Category 1', 'Category 2');
+ *
+ * @param string action name
+ * @param int action type
+ * @param int url prefix (only used for TYPE_ACTION_URL)
+ * @return array of exploded elements from $name
+ */
+ static public function getActionExplodedNames($name, $type, $urlPrefix = null)
+ {
+ // Site Search does not split Search keywords
+ if ($type == Piwik_Tracker_Action::TYPE_SITE_SEARCH) {
+ return array($name);
+ }
+
+ $matches = array();
+ $isUrl = false;
+ $name = str_replace("\n", "", $name);
+
+ $urlRegexAfterDomain = '([^/]+)[/]?([^#]*)[#]?(.*)';
+ if ($urlPrefix === null) {
+ // match url with protocol (used for outlinks / downloads)
+ $urlRegex = '@^http[s]?://' . $urlRegexAfterDomain . '$@i';
+ } else {
+ // the name is a url that does not contain protocol and www anymore
+ // we know that normalization has been done on db level because $urlPrefix is set
+ $urlRegex = '@^' . $urlRegexAfterDomain . '$@i';
+ }
+
+ preg_match($urlRegex, $name, $matches);
+ if (count($matches)) {
+ $isUrl = true;
+ $urlHost = $matches[1];
+ $urlPath = $matches[2];
+ $urlFragment = $matches[3];
+ }
+
+ if ($type == Piwik_Tracker_Action::TYPE_DOWNLOAD
+ || $type == Piwik_Tracker_Action::TYPE_OUTLINK
+ ) {
+ if ($isUrl) {
+ return array(trim($urlHost), '/' . trim($urlPath));
+ }
+ }
+
+ if ($isUrl) {
+ $name = $urlPath;
+
+ if ($name === '' || substr($name, -1) == '/') {
+ $name .= self::$defaultActionName;
+ }
+ }
+
+ if ($type == Piwik_Tracker_Action::TYPE_ACTION_NAME) {
+ $categoryDelimiter = self::$actionTitleCategoryDelimiter;
+ } else {
+ $categoryDelimiter = self::$actionUrlCategoryDelimiter;
+ }
+
+
+ if ($isUrl) {
+ $urlFragment = Piwik_Tracker_Action::processUrlFragment($urlFragment);
+ if (!empty($urlFragment)) {
+ $name .= '#' . $urlFragment;
+ }
+ }
+
+ if (empty($categoryDelimiter)) {
+ return array(trim($name));
+ }
+
+ $split = explode($categoryDelimiter, $name, self::getSubCategoryLevelLimit());
+
+ // trim every category and remove empty categories
+ $split = array_map('trim', $split);
+ $split = array_filter($split, 'strlen');
+
+ // forces array key to start at 0
+ $split = array_values($split);
+
+ if (empty($split)) {
+ $defaultName = self::getUnknownActionName($type);
+ return array(trim($defaultName));
+ }
+
+ $lastPageName = end($split);
+ // we are careful to prefix the page URL / name with some value
+ // so that if a page has the same name as a category
+ // we don't merge both entries
+ if ($type != Piwik_Tracker_Action::TYPE_ACTION_NAME) {
+ $lastPageName = '/' . $lastPageName;
+ } else {
+ $lastPageName = ' ' . $lastPageName;
+ }
+ $split[count($split) - 1] = $lastPageName;
+ return array_values($split);
+ }
+
+ /**
+ * Gets the key for the cache of action rows from an action ID and type.
+ *
+ * @param int $idAction
+ * @param int $actionType
+ * @return string|int
+ */
+ private static function getCachedActionRowKey($idAction, $actionType)
+ {
+ return $idAction == Piwik_DataTable::LABEL_SUMMARY_ROW
+ ? $actionType . '_others'
+ : $idAction;
+ }
+
+ /**
+ * Returns the configured sub-category level limit.
+ *
+ * @return int
+ */
+ public static function getSubCategoryLevelLimit()
+ {
+ return Piwik_Config::getInstance()->General['action_category_level_limit'];
+ }
+
+ /**
+ * Returns default label for the action type
+ *
+ * @param $type
+ * @return string
+ */
+ static public function getUnknownActionName($type)
+ {
+ if (empty(self::$defaultActionNameWhenNotDefined)) {
+ self::$defaultActionNameWhenNotDefined = Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageName'));
+ self::$defaultActionUrlWhenNotDefined = Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageURL'));
+ }
+ if ($type == Piwik_Tracker_Action::TYPE_ACTION_NAME) {
+ return self::$defaultActionNameWhenNotDefined;
+ }
+ return self::$defaultActionUrlWhenNotDefined;
+ }
+
+ /**
+ * Static cache to store Rows during processing
+ */
+ static protected $cacheParsedAction = array();
+
+ public static function clearActionsCache()
+ {
+ self::$cacheParsedAction = array();
+ }
+
+ /**
+ * Get cached action row by id & type. If $idAction is set to -1, the 'Others' row
+ * for the specific action type will be returned.
+ *
+ * @param int $idAction
+ * @param int $actionType
+ * @return Piwik_DataTable_Row|false
+ */
+ private static function getCachedActionRow($idAction, $actionType)
+ {
+ $cacheLabel = self::getCachedActionRowKey($idAction, $actionType);
+
+ if (!isset(self::$cacheParsedAction[$cacheLabel])) {
+ // This can happen when
+ // - We select an entry page ID that was only seen yesterday, so wasn't selected in the first query
+ // - We count time spent on a page, when this page was only seen yesterday
+ return false;
+ }
+
+ return self::$cacheParsedAction[$cacheLabel];
+ }
+
+ /**
+ * Set cached action row for an id & type.
+ *
+ * @param int $idAction
+ * @param int $actionType
+ * @param Piwik_DataTable_Row
+ */
+ private static function setCachedActionRow($idAction, $actionType, $actionRow)
+ {
+ $cacheLabel = self::getCachedActionRowKey($idAction, $actionType);
+ self::$cacheParsedAction[$cacheLabel] = $actionRow;
+ }
+
+ /**
+ * Returns the default columns for a row in an Actions DataTable.
+ *
+ * @return array
+ */
+ private static function getDefaultRowColumns()
+ {
+ return array(Piwik_Archive::INDEX_NB_VISITS => 0,
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
+ Piwik_Archive::INDEX_PAGE_NB_HITS => 0,
+ Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT => 0);
+ }
+
+ /**
+ * Creates a summary row for an Actions DataTable.
+ *
+ * @return Piwik_DataTable_Row
+ */
+ private static function createSummaryRow()
+ {
+ return new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS =>
+ array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW) + self::getDefaultRowColumns()
+ ));
+ }
}
diff --git a/plugins/Actions/Controller.php b/plugins/Actions/Controller.php
index 00eb49a804..aed8efd683 100644
--- a/plugins/Actions/Controller.php
+++ b/plugins/Actions/Controller.php
@@ -16,513 +16,502 @@
*/
class Piwik_Actions_Controller extends Piwik_Controller
{
- const ACTIONS_REPORT_ROWS_DISPLAY = 100;
-
- protected function getPageUrlsView($currentAction, $controllerActionSubtable, $apiAction)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init($this->pluginName, $currentAction, $apiAction, $controllerActionSubtable);
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageURL'));
- return $view;
- }
-
- /**
- * PAGES
- * @param bool $fetch
- * @return string
- */
-
- public function indexPageUrls($fetch = false)
- {
- return Piwik_View::singleReport(
- Piwik_Translate('Actions_SubmenuPages'),
- $this->getPageUrls(true), $fetch);
- }
-
- public function getPageUrls($fetch = false)
- {
- $view = $this->getPageUrlsView(__FUNCTION__, 'getPageUrls', 'Actions.getPageUrls');
- $this->configureViewPages($view);
- $this->configureViewActions($view);
- return $this->renderView($view, $fetch);
- }
-
- protected function configureViewPages($view)
- {
- $view->setColumnsToDisplay( array('label','nb_hits','nb_visits', 'bounce_rate', 'avg_time_on_page', 'exit_rate', 'avg_time_generation') );
- }
-
- /**
- * ENTRY PAGES
- * @param bool $fetch
- * @return string|void
- */
- public function indexEntryPageUrls($fetch = false)
- {
- return Piwik_View::singleReport(
- Piwik_Translate('Actions_SubmenuPagesEntry'),
- $this->getEntryPageUrls(true), $fetch);
- }
-
- public function getEntryPageUrls($fetch = false)
- {
- $view = $this->getPageUrlsView(__FUNCTION__, 'getEntryPageUrls', 'Actions.getEntryPageUrls');
- $this->configureViewEntryPageUrls($view);
- $this->configureViewActions($view);
- return $this->renderView($view, $fetch);
- }
-
- protected function configureViewEntryPageUrls($view)
- {
- $view->setSortedColumn('entry_nb_visits');
- $view->setColumnsToDisplay( array('label','entry_nb_visits', 'entry_bounce_count', 'bounce_rate') );
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnEntryPageURL'));
- $view->setColumnTranslation('entry_bounce_count', Piwik_Translate('General_ColumnBounces'));
- $view->setColumnTranslation('entry_nb_visits', Piwik_Translate('General_ColumnEntrances'));
- $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPagesEntry'), array(
- 'Actions.getEntryPageTitles' => Piwik_Translate('Actions_EntryPageTitles')
- ));
- $view->setReportUrl('Actions', $this->getEntryPageUrlActionForLink());
- }
-
- /*
- * EXIT PAGES
- */
- public function indexExitPageUrls($fetch = false)
- {
- return Piwik_View::singleReport(
- Piwik_Translate('Actions_SubmenuPagesExit'),
- $this->getExitPageUrls(true), $fetch);
- }
-
- public function getExitPageUrls($fetch = false)
- {
- $view = $this->getPageUrlsView(__FUNCTION__, 'getExitPageUrls', 'Actions.getExitPageUrls');
- $this->configureViewExitPageUrls($view);
- $this->configureViewActions($view);
- return $this->renderView($view, $fetch);
- }
-
- protected function configureViewExitPageUrls($view)
- {
- $view->setSortedColumn('exit_nb_visits');
- $view->setColumnsToDisplay( array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate') );
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnExitPageURL'));
- $view->setColumnTranslation('exit_nb_visits', Piwik_Translate('General_ColumnExits'));
- $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPagesExit'), array(
- 'Actions.getExitPageTitles' => Piwik_Translate('Actions_ExitPageTitles')
- ));
- $view->setReportUrl('Actions', $this->getExitPageUrlActionForLink());
- }
-
- /*
- * SITE SEARCH
- */
- public function indexSiteSearch()
- {
- $view = Piwik_View::factory('indexSiteSearch');
-
- $view->keywords = $this->getSiteSearchKeywords( true );
- $view->noResultKeywords = $this->getSiteSearchNoResultKeywords( true );
- $view->pagesUrlsFollowingSiteSearch = $this->getPageUrlsFollowingSiteSearch( true );
-
- $categoryTrackingEnabled = Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables');
- if($categoryTrackingEnabled)
- {
- $view->categories = $this->getSiteSearchCategories( true );
- }
-
- echo $view->render();
- }
-
- public function getSiteSearchKeywords($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__, 'Actions.getSiteSearchKeywords' );
- $this->configureViewSiteSearchKeywords($view);
- return $this->renderView($view, $fetch);
- }
-
- public function getSiteSearchNoResultKeywords($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__, 'Actions.getSiteSearchNoResultKeywords' );
- $this->configureViewSiteSearchKeywords($view);
- $view->setColumnsToDisplay(array('label', 'nb_visits', 'exit_rate'));
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnNoResultKeyword'));
- return $this->renderView($view, $fetch);
- }
-
- public function configureViewSiteSearchKeywords(Piwik_ViewDataTable $view)
- {
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnSearchKeyword'));
- $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_pages_per_search', 'exit_rate'));
- $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnSearches'));
- $view->setColumnTranslation('exit_rate', str_replace("% ", "%&nbsp;", Piwik_Translate('Actions_ColumnSearchExits')));
- $view->setColumnTranslation('nb_pages_per_search', Piwik_Translate('Actions_ColumnPagesPerSearch'));
- $view->disableShowBarChart();
- $view->disableShowAllColumns();
- }
-
- public function getSiteSearchCategories($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__, 'Actions.getSiteSearchCategories' );
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnSearchCategory'));
- $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnSearches'));
- $view->setColumnsToDisplay( array('label','nb_visits', 'nb_pages_per_search') );
- $view->setColumnTranslation('nb_pages_per_search', Piwik_Translate('Actions_ColumnPagesPerSearch'));
- $view->disableShowAllColumns();
- $view->disableShowBarChart();
- $view->disableRowEvolution();
- return $this->renderView($view, $fetch);
- }
-
-
- public function getPageUrlsFollowingSiteSearch($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__, 'Actions.getPageUrlsFollowingSiteSearch', 'getPageUrlsFollowingSiteSearch' );
- $view->addRelatedReports(Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'), array(
- 'Actions.getPageTitlesFollowingSiteSearch' => Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'),
- ));
- $view = $this->configureViewPagesFollowingSiteSearch($view);
- return $this->renderView($view, $fetch);
- }
-
- public function getPageTitlesFollowingSiteSearch($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__, 'Actions.getPageTitlesFollowingSiteSearch', 'getPageTitlesFollowingSiteSearch' );
- $view->addRelatedReports(Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'), array(
- 'Actions.getPageUrlsFollowingSiteSearch' => Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'),
- ));
- $view = $this->configureViewPagesFollowingSiteSearch($view);
- return $this->renderView($view, $fetch);
- }
-
- public function configureViewPagesFollowingSiteSearch($view)
- {
- $view->setColumnsToDisplay(array('label', 'nb_hits_following_search', 'nb_hits'));
- $view->setColumnTranslation('nb_hits_following_search', Piwik_Translate('General_ColumnViewedAfterSearch'));
- $view->setColumnTranslation('label', Piwik_Translate('General_ColumnDestinationPage'));
- $view->setSortedColumn('nb_hits_following_search');
- $view->setColumnTranslation('nb_hits', Piwik_Translate('General_ColumnTotalPageviews'));
- $view->disableExcludeLowPopulation();
- $view = $this->configureViewActions($view, $doSetTranslations = false);
- return $view;
- }
-
- /*
- * PAGE TITLES
- */
- public function indexPageTitles($fetch = false)
- {
- return Piwik_View::singleReport(
- Piwik_Translate('Actions_SubmenuPageTitles'),
- $this->getPageTitles(true), $fetch);
- }
-
- public function getPageTitles($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName,
- __FUNCTION__,
- 'Actions.getPageTitles',
- 'getPageTitlesSubDataTable' );
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageName'));
- $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPageTitles'), array(
- 'Actions.getEntryPageTitles' => Piwik_Translate('Actions_EntryPageTitles'),
- 'Actions.getExitPageTitles' => Piwik_Translate('Actions_ExitPageTitles'),
- ));
- $view->setReportUrl('Actions', $this->getPageTitlesActionForLink());
- $this->configureViewPages($view);
- $this->configureViewActions($view);
- return $this->renderView($view, $fetch);
- }
-
- public function getPageTitlesSubDataTable($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName,
- __FUNCTION__,
- 'Actions.getPageTitles',
- 'getPageTitlesSubDataTable' );
- $this->configureViewPages($view);
- $this->configureViewActions($view);
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Echos or returns a report displaying analytics data for every unique entry
- * page title.
- *
- * @param bool $fetch True to return the view as a string, false to echo it.
- * @return string
- */
- public function getEntryPageTitles( $fetch = false )
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init($this->pluginName, __FUNCTION__, 'Actions.getEntryPageTitles', __FUNCTION__);
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnEntryPageTitle'));
- $view->setColumnTranslation('entry_bounce_count', Piwik_Translate('General_ColumnBounces'));
- $view->setColumnTranslation('entry_nb_visits', Piwik_Translate('General_ColumnEntrances'));
- $view->setColumnsToDisplay( array('label','entry_nb_visits', 'entry_bounce_count', 'bounce_rate') );
-
- $entryPageUrlAction = $this->getEntryPageUrlActionForLink();
- $view->addRelatedReports(Piwik_Translate('Actions_EntryPageTitles'), array(
- 'Actions.getPageTitles' => Piwik_Translate('Actions_SubmenuPageTitles'),
- "Actions.$entryPageUrlAction" => Piwik_Translate('Actions_SubmenuPagesEntry'),
- ));
-
- $this->configureViewActions($view);
-
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Echos or returns a report displaying analytics data for every unique exit
- * page title.
- *
- * @param bool $fetch True to return the view as a string, false to echo it.
- * @return string
- */
- public function getExitPageTitles( $fetch = false )
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init($this->pluginName, __FUNCTION__, 'Actions.getExitPageTitles', __FUNCTION__);
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnExitPageTitle'));
- $view->setColumnTranslation('exit_nb_visits', Piwik_Translate('General_ColumnExits'));
- $view->setColumnsToDisplay( array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate') );
-
- $exitPageUrlAction = $this->getExitPageUrlActionForLink();
- $view->addRelatedReports(Piwik_Translate('Actions_ExitPageTitles'), array(
- 'Actions.getPageTitles' => Piwik_Translate('Actions_SubmenuPageTitles'),
- "Actions.$exitPageUrlAction" => Piwik_Translate('Actions_SubmenuPagesExit'),
- ));
-
- $this->configureViewActions($view);
-
- return $this->renderView($view, $fetch);
- }
-
- /*
- * DOWNLOADS
- */
-
- public function indexDownloads($fetch = false)
- {
- return Piwik_View::singleReport(
- Piwik_Translate('Actions_SubmenuDownloads'),
- $this->getDownloads(true), $fetch);
- }
-
- public function getDownloads($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName,
- __FUNCTION__,
- 'Actions.getDownloads',
- 'getDownloadsSubDataTable' );
-
- $this->configureViewDownloads($view);
- return $this->renderView($view, $fetch);
- }
-
- public function getDownloadsSubDataTable($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName,
- __FUNCTION__,
- 'Actions.getDownloads',
- 'getDownloadsSubDataTable');
- $this->configureViewDownloads($view);
- return $this->renderView($view, $fetch);
- }
-
-
- /*
- * OUTLINKS
- */
-
- public function indexOutlinks($fetch = false)
- {
- return Piwik_View::singleReport(
- Piwik_Translate('Actions_SubmenuOutlinks'),
- $this->getOutlinks(true), $fetch);
- }
-
- public function getOutlinks($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName,
- __FUNCTION__,
- 'Actions.getOutlinks',
- 'getOutlinksSubDataTable' );
- $this->configureViewOutlinks($view);
- return $this->renderView($view, $fetch);
- }
-
- public function getOutlinksSubDataTable($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName,
- __FUNCTION__,
- 'Actions.getOutlinks',
- 'getOutlinksSubDataTable');
- $this->configureViewOutlinks($view);
- return $this->renderView($view, $fetch);
- }
-
- /*
- * Page titles & Page URLs reports
- */
- protected function configureViewActions($view, $doSetTranslations = true)
- {
- if($doSetTranslations)
- {
- $view->setColumnTranslation('nb_hits', Piwik_Translate('General_ColumnPageviews'));
- $view->setColumnTranslation('nb_visits', Piwik_Translate('General_ColumnUniquePageviews'));
- $view->setColumnTranslation('avg_time_on_page', Piwik_Translate('General_ColumnAverageTimeOnPage'));
- $view->setColumnTranslation('bounce_rate', Piwik_Translate('General_ColumnBounceRate'));
- $view->setColumnTranslation('exit_rate', Piwik_Translate('General_ColumnExitRate'));
- $view->setColumnTranslation('avg_time_generation', Piwik_Translate('General_ColumnAverageGenerationTime'));
- $view->queueFilter('ColumnCallbackReplace', array('avg_time_on_page', array('Piwik', 'getPrettyTimeFromSeconds')));
- $view->queueFilter('ColumnCallbackReplace', array('avg_time_generation',
- create_function('$averageTimeOnSite', 'return $averageTimeOnSite ? Piwik::getPrettyTimeFromSeconds($averageTimeOnSite, true, true, false) : "-";')));
- }
-
- if(Piwik_Common::getRequestVar('enable_filter_excludelowpop', '0', 'string' ) != '0')
- {
- // computing minimum value to exclude
- $visitsInfo = Piwik_VisitsSummary_Controller::getVisitsSummary();
- $visitsInfo = $visitsInfo->getFirstRow();
- $nbActions = $visitsInfo->getColumn('nb_actions');
- $nbActionsLowPopulationThreshold = floor(0.02 * $nbActions); // 2 percent of the total number of actions
- // we remove 1 to make sure some actions/downloads are displayed in the case we have a very few of them
- // and each of them has 1 or 2 hits...
- $nbActionsLowPopulationThreshold = min($visitsInfo->getColumn('max_actions')-1, $nbActionsLowPopulationThreshold-1);
-
- $view->setExcludeLowPopulation( 'nb_hits', $nbActionsLowPopulationThreshold );
- }
-
- $this->configureGenericViewActions($view);
- return $view;
- }
-
- /*
- * Downloads report
- */
- protected function configureViewDownloads($view)
- {
- $view->setColumnsToDisplay( array('label','nb_visits','nb_hits') );
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnDownloadURL'));
- $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnUniqueDownloads'));
- $view->setColumnTranslation('nb_hits', Piwik_Translate('Actions_ColumnDownloads'));
- $view->disableExcludeLowPopulation();
- $this->configureGenericViewActions($view);
- }
-
- /*
- * Outlinks report
- */
- protected function configureViewOutlinks($view)
- {
- $view->setColumnsToDisplay( array('label','nb_visits','nb_hits') );
- $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnClickedURL'));
- $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnUniqueClicks'));
- $view->setColumnTranslation('nb_hits', Piwik_Translate('Actions_ColumnClicks'));
- $view->disableExcludeLowPopulation();
- $this->configureGenericViewActions($view);
- }
-
- /*
- * Common to all Actions reports, how to use the custom Actions Datatable html
- */
- protected function configureGenericViewActions($view)
- {
- $view->setTemplate('CoreHome/templates/datatable_actions.tpl');
- if(Piwik_Common::getRequestVar('idSubtable', -1) != -1)
- {
- $view->setTemplate('CoreHome/templates/datatable_actions_subdatable.tpl');
- }
- $currentlySearching = $view->setSearchRecursive();
- if($currentlySearching)
- {
- $view->setTemplate('CoreHome/templates/datatable_actions_recursive.tpl');
- }
- // disable Footer icons
- $view->disableShowAllViewsIcons();
- $view->disableShowAllColumns();
-
- $view->setLimit( self::ACTIONS_REPORT_ROWS_DISPLAY );
-
- // if the flat parameter is not provided, make sure it is set to 0 in the URL,
- // so users can see that they can set it to 1 (see #3365)
- if (Piwik_Common::getRequestVar('flat', false) === false)
- {
- $view->setCustomParameter('flat', 0);
- }
-
- $view->main();
- // we need to rewrite the phpArray so it contains all the recursive arrays
- if($currentlySearching)
- {
- $phpArrayRecursive = $this->getArrayFromRecursiveDataTable($view->getDataTable());
- $view->getView()->arrayDataTable = $phpArrayRecursive;
- }
- }
-
- protected function getArrayFromRecursiveDataTable( $dataTable, $depth = 0 )
- {
- $table = array();
- foreach($dataTable->getRows() as $row)
- {
- $phpArray = array();
- if(($idSubtable = $row->getIdSubDataTable()) !== null)
- {
- $subTable = Piwik_DataTable_Manager::getInstance()->getTable( $idSubtable );
-
- if($subTable->getRowsCount() > 0)
- {
- $phpArray = $this->getArrayFromRecursiveDataTable( $subTable, $depth + 1 );
- }
- }
-
- $newRow = array(
- 'level' => $depth,
- 'columns' => $row->getColumns(),
- 'metadata' => $row->getMetadata(),
- 'idsubdatatable' => $row->getIdSubDataTable()
- );
- $table[] = $newRow;
- if(count($phpArray) > 0)
- {
- $table = array_merge( $table, $phpArray);
- }
- }
- return $table;
- }
-
- /** Returns action to use when linking to the exit page URLs report. */
- private function getExitPageUrlActionForLink()
- {
- // link to the page not, just the report, but only if not a widget
- return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexExitPageUrls' : 'getExitPageUrls';
- }
-
-
- /** Returns action to use when linking to the entry page URLs report. */
- private function getEntryPageUrlActionForLink()
- {
- // link to the page not, just the report, but only if not a widget
- return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexEntryPageUrls' : 'getEntryPageUrls';
- }
-
- /** Returns action to use when linking to the page titles report. */
- private function getPageTitlesActionForLink()
- {
- // link to the page not, just the report, but only if not a widget
- return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexPageTitles' : 'getPageTitles';
- }
+ const ACTIONS_REPORT_ROWS_DISPLAY = 100;
+
+ protected function getPageUrlsView($currentAction, $controllerActionSubtable, $apiAction)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, $currentAction, $apiAction, $controllerActionSubtable);
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageURL'));
+ return $view;
+ }
+
+ /**
+ * PAGES
+ * @param bool $fetch
+ * @return string
+ */
+
+ public function indexPageUrls($fetch = false)
+ {
+ return Piwik_View::singleReport(
+ Piwik_Translate('Actions_SubmenuPages'),
+ $this->getPageUrls(true), $fetch);
+ }
+
+ public function getPageUrls($fetch = false)
+ {
+ $view = $this->getPageUrlsView(__FUNCTION__, 'getPageUrls', 'Actions.getPageUrls');
+ $this->configureViewPages($view);
+ $this->configureViewActions($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ protected function configureViewPages($view)
+ {
+ $view->setColumnsToDisplay(array('label', 'nb_hits', 'nb_visits', 'bounce_rate', 'avg_time_on_page', 'exit_rate', 'avg_time_generation'));
+ }
+
+ /**
+ * ENTRY PAGES
+ * @param bool $fetch
+ * @return string|void
+ */
+ public function indexEntryPageUrls($fetch = false)
+ {
+ return Piwik_View::singleReport(
+ Piwik_Translate('Actions_SubmenuPagesEntry'),
+ $this->getEntryPageUrls(true), $fetch);
+ }
+
+ public function getEntryPageUrls($fetch = false)
+ {
+ $view = $this->getPageUrlsView(__FUNCTION__, 'getEntryPageUrls', 'Actions.getEntryPageUrls');
+ $this->configureViewEntryPageUrls($view);
+ $this->configureViewActions($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ protected function configureViewEntryPageUrls($view)
+ {
+ $view->setSortedColumn('entry_nb_visits');
+ $view->setColumnsToDisplay(array('label', 'entry_nb_visits', 'entry_bounce_count', 'bounce_rate'));
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnEntryPageURL'));
+ $view->setColumnTranslation('entry_bounce_count', Piwik_Translate('General_ColumnBounces'));
+ $view->setColumnTranslation('entry_nb_visits', Piwik_Translate('General_ColumnEntrances'));
+ $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPagesEntry'), array(
+ 'Actions.getEntryPageTitles' => Piwik_Translate('Actions_EntryPageTitles')
+ ));
+ $view->setReportUrl('Actions', $this->getEntryPageUrlActionForLink());
+ }
+
+ /*
+ * EXIT PAGES
+ */
+ public function indexExitPageUrls($fetch = false)
+ {
+ return Piwik_View::singleReport(
+ Piwik_Translate('Actions_SubmenuPagesExit'),
+ $this->getExitPageUrls(true), $fetch);
+ }
+
+ public function getExitPageUrls($fetch = false)
+ {
+ $view = $this->getPageUrlsView(__FUNCTION__, 'getExitPageUrls', 'Actions.getExitPageUrls');
+ $this->configureViewExitPageUrls($view);
+ $this->configureViewActions($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ protected function configureViewExitPageUrls($view)
+ {
+ $view->setSortedColumn('exit_nb_visits');
+ $view->setColumnsToDisplay(array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate'));
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnExitPageURL'));
+ $view->setColumnTranslation('exit_nb_visits', Piwik_Translate('General_ColumnExits'));
+ $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPagesExit'), array(
+ 'Actions.getExitPageTitles' => Piwik_Translate('Actions_ExitPageTitles')
+ ));
+ $view->setReportUrl('Actions', $this->getExitPageUrlActionForLink());
+ }
+
+ /*
+ * SITE SEARCH
+ */
+ public function indexSiteSearch()
+ {
+ $view = Piwik_View::factory('indexSiteSearch');
+
+ $view->keywords = $this->getSiteSearchKeywords(true);
+ $view->noResultKeywords = $this->getSiteSearchNoResultKeywords(true);
+ $view->pagesUrlsFollowingSiteSearch = $this->getPageUrlsFollowingSiteSearch(true);
+
+ $categoryTrackingEnabled = Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables');
+ if ($categoryTrackingEnabled) {
+ $view->categories = $this->getSiteSearchCategories(true);
+ }
+
+ echo $view->render();
+ }
+
+ public function getSiteSearchKeywords($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Actions.getSiteSearchKeywords');
+ $this->configureViewSiteSearchKeywords($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getSiteSearchNoResultKeywords($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Actions.getSiteSearchNoResultKeywords');
+ $this->configureViewSiteSearchKeywords($view);
+ $view->setColumnsToDisplay(array('label', 'nb_visits', 'exit_rate'));
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnNoResultKeyword'));
+ return $this->renderView($view, $fetch);
+ }
+
+ public function configureViewSiteSearchKeywords(Piwik_ViewDataTable $view)
+ {
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnSearchKeyword'));
+ $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_pages_per_search', 'exit_rate'));
+ $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnSearches'));
+ $view->setColumnTranslation('exit_rate', str_replace("% ", "%&nbsp;", Piwik_Translate('Actions_ColumnSearchExits')));
+ $view->setColumnTranslation('nb_pages_per_search', Piwik_Translate('Actions_ColumnPagesPerSearch'));
+ $view->disableShowBarChart();
+ $view->disableShowAllColumns();
+ }
+
+ public function getSiteSearchCategories($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Actions.getSiteSearchCategories');
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnSearchCategory'));
+ $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnSearches'));
+ $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_pages_per_search'));
+ $view->setColumnTranslation('nb_pages_per_search', Piwik_Translate('Actions_ColumnPagesPerSearch'));
+ $view->disableShowAllColumns();
+ $view->disableShowBarChart();
+ $view->disableRowEvolution();
+ return $this->renderView($view, $fetch);
+ }
+
+
+ public function getPageUrlsFollowingSiteSearch($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Actions.getPageUrlsFollowingSiteSearch', 'getPageUrlsFollowingSiteSearch');
+ $view->addRelatedReports(Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'), array(
+ 'Actions.getPageTitlesFollowingSiteSearch' => Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'),
+ ));
+ $view = $this->configureViewPagesFollowingSiteSearch($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getPageTitlesFollowingSiteSearch($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Actions.getPageTitlesFollowingSiteSearch', 'getPageTitlesFollowingSiteSearch');
+ $view->addRelatedReports(Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'), array(
+ 'Actions.getPageUrlsFollowingSiteSearch' => Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'),
+ ));
+ $view = $this->configureViewPagesFollowingSiteSearch($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ public function configureViewPagesFollowingSiteSearch($view)
+ {
+ $view->setColumnsToDisplay(array('label', 'nb_hits_following_search', 'nb_hits'));
+ $view->setColumnTranslation('nb_hits_following_search', Piwik_Translate('General_ColumnViewedAfterSearch'));
+ $view->setColumnTranslation('label', Piwik_Translate('General_ColumnDestinationPage'));
+ $view->setSortedColumn('nb_hits_following_search');
+ $view->setColumnTranslation('nb_hits', Piwik_Translate('General_ColumnTotalPageviews'));
+ $view->disableExcludeLowPopulation();
+ $view = $this->configureViewActions($view, $doSetTranslations = false);
+ return $view;
+ }
+
+ /*
+ * PAGE TITLES
+ */
+ public function indexPageTitles($fetch = false)
+ {
+ return Piwik_View::singleReport(
+ Piwik_Translate('Actions_SubmenuPageTitles'),
+ $this->getPageTitles(true), $fetch);
+ }
+
+ public function getPageTitles($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName,
+ __FUNCTION__,
+ 'Actions.getPageTitles',
+ 'getPageTitlesSubDataTable');
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageName'));
+ $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPageTitles'), array(
+ 'Actions.getEntryPageTitles' => Piwik_Translate('Actions_EntryPageTitles'),
+ 'Actions.getExitPageTitles' => Piwik_Translate('Actions_ExitPageTitles'),
+ ));
+ $view->setReportUrl('Actions', $this->getPageTitlesActionForLink());
+ $this->configureViewPages($view);
+ $this->configureViewActions($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getPageTitlesSubDataTable($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName,
+ __FUNCTION__,
+ 'Actions.getPageTitles',
+ 'getPageTitlesSubDataTable');
+ $this->configureViewPages($view);
+ $this->configureViewActions($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Echos or returns a report displaying analytics data for every unique entry
+ * page title.
+ *
+ * @param bool $fetch True to return the view as a string, false to echo it.
+ * @return string
+ */
+ public function getEntryPageTitles($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Actions.getEntryPageTitles', __FUNCTION__);
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnEntryPageTitle'));
+ $view->setColumnTranslation('entry_bounce_count', Piwik_Translate('General_ColumnBounces'));
+ $view->setColumnTranslation('entry_nb_visits', Piwik_Translate('General_ColumnEntrances'));
+ $view->setColumnsToDisplay(array('label', 'entry_nb_visits', 'entry_bounce_count', 'bounce_rate'));
+
+ $entryPageUrlAction = $this->getEntryPageUrlActionForLink();
+ $view->addRelatedReports(Piwik_Translate('Actions_EntryPageTitles'), array(
+ 'Actions.getPageTitles' => Piwik_Translate('Actions_SubmenuPageTitles'),
+ "Actions.$entryPageUrlAction" => Piwik_Translate('Actions_SubmenuPagesEntry'),
+ ));
+
+ $this->configureViewActions($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Echos or returns a report displaying analytics data for every unique exit
+ * page title.
+ *
+ * @param bool $fetch True to return the view as a string, false to echo it.
+ * @return string
+ */
+ public function getExitPageTitles($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Actions.getExitPageTitles', __FUNCTION__);
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnExitPageTitle'));
+ $view->setColumnTranslation('exit_nb_visits', Piwik_Translate('General_ColumnExits'));
+ $view->setColumnsToDisplay(array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate'));
+
+ $exitPageUrlAction = $this->getExitPageUrlActionForLink();
+ $view->addRelatedReports(Piwik_Translate('Actions_ExitPageTitles'), array(
+ 'Actions.getPageTitles' => Piwik_Translate('Actions_SubmenuPageTitles'),
+ "Actions.$exitPageUrlAction" => Piwik_Translate('Actions_SubmenuPagesExit'),
+ ));
+
+ $this->configureViewActions($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /*
+ * DOWNLOADS
+ */
+
+ public function indexDownloads($fetch = false)
+ {
+ return Piwik_View::singleReport(
+ Piwik_Translate('Actions_SubmenuDownloads'),
+ $this->getDownloads(true), $fetch);
+ }
+
+ public function getDownloads($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName,
+ __FUNCTION__,
+ 'Actions.getDownloads',
+ 'getDownloadsSubDataTable');
+
+ $this->configureViewDownloads($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getDownloadsSubDataTable($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName,
+ __FUNCTION__,
+ 'Actions.getDownloads',
+ 'getDownloadsSubDataTable');
+ $this->configureViewDownloads($view);
+ return $this->renderView($view, $fetch);
+ }
+
+
+ /*
+ * OUTLINKS
+ */
+
+ public function indexOutlinks($fetch = false)
+ {
+ return Piwik_View::singleReport(
+ Piwik_Translate('Actions_SubmenuOutlinks'),
+ $this->getOutlinks(true), $fetch);
+ }
+
+ public function getOutlinks($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName,
+ __FUNCTION__,
+ 'Actions.getOutlinks',
+ 'getOutlinksSubDataTable');
+ $this->configureViewOutlinks($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getOutlinksSubDataTable($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName,
+ __FUNCTION__,
+ 'Actions.getOutlinks',
+ 'getOutlinksSubDataTable');
+ $this->configureViewOutlinks($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ /*
+ * Page titles & Page URLs reports
+ */
+ protected function configureViewActions($view, $doSetTranslations = true)
+ {
+ if ($doSetTranslations) {
+ $view->setColumnTranslation('nb_hits', Piwik_Translate('General_ColumnPageviews'));
+ $view->setColumnTranslation('nb_visits', Piwik_Translate('General_ColumnUniquePageviews'));
+ $view->setColumnTranslation('avg_time_on_page', Piwik_Translate('General_ColumnAverageTimeOnPage'));
+ $view->setColumnTranslation('bounce_rate', Piwik_Translate('General_ColumnBounceRate'));
+ $view->setColumnTranslation('exit_rate', Piwik_Translate('General_ColumnExitRate'));
+ $view->setColumnTranslation('avg_time_generation', Piwik_Translate('General_ColumnAverageGenerationTime'));
+ $view->queueFilter('ColumnCallbackReplace', array('avg_time_on_page', array('Piwik', 'getPrettyTimeFromSeconds')));
+ $view->queueFilter('ColumnCallbackReplace', array('avg_time_generation',
+ create_function('$averageTimeOnSite', 'return $averageTimeOnSite ? Piwik::getPrettyTimeFromSeconds($averageTimeOnSite, true, true, false) : "-";')));
+ }
+
+ if (Piwik_Common::getRequestVar('enable_filter_excludelowpop', '0', 'string') != '0') {
+ // computing minimum value to exclude
+ $visitsInfo = Piwik_VisitsSummary_Controller::getVisitsSummary();
+ $visitsInfo = $visitsInfo->getFirstRow();
+ $nbActions = $visitsInfo->getColumn('nb_actions');
+ $nbActionsLowPopulationThreshold = floor(0.02 * $nbActions); // 2 percent of the total number of actions
+ // we remove 1 to make sure some actions/downloads are displayed in the case we have a very few of them
+ // and each of them has 1 or 2 hits...
+ $nbActionsLowPopulationThreshold = min($visitsInfo->getColumn('max_actions') - 1, $nbActionsLowPopulationThreshold - 1);
+
+ $view->setExcludeLowPopulation('nb_hits', $nbActionsLowPopulationThreshold);
+ }
+
+ $this->configureGenericViewActions($view);
+ return $view;
+ }
+
+ /*
+ * Downloads report
+ */
+ protected function configureViewDownloads($view)
+ {
+ $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_hits'));
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnDownloadURL'));
+ $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnUniqueDownloads'));
+ $view->setColumnTranslation('nb_hits', Piwik_Translate('Actions_ColumnDownloads'));
+ $view->disableExcludeLowPopulation();
+ $this->configureGenericViewActions($view);
+ }
+
+ /*
+ * Outlinks report
+ */
+ protected function configureViewOutlinks($view)
+ {
+ $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_hits'));
+ $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnClickedURL'));
+ $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnUniqueClicks'));
+ $view->setColumnTranslation('nb_hits', Piwik_Translate('Actions_ColumnClicks'));
+ $view->disableExcludeLowPopulation();
+ $this->configureGenericViewActions($view);
+ }
+
+ /*
+ * Common to all Actions reports, how to use the custom Actions Datatable html
+ */
+ protected function configureGenericViewActions($view)
+ {
+ $view->setTemplate('CoreHome/templates/datatable_actions.tpl');
+ if (Piwik_Common::getRequestVar('idSubtable', -1) != -1) {
+ $view->setTemplate('CoreHome/templates/datatable_actions_subdatable.tpl');
+ }
+ $currentlySearching = $view->setSearchRecursive();
+ if ($currentlySearching) {
+ $view->setTemplate('CoreHome/templates/datatable_actions_recursive.tpl');
+ }
+ // disable Footer icons
+ $view->disableShowAllViewsIcons();
+ $view->disableShowAllColumns();
+
+ $view->setLimit(self::ACTIONS_REPORT_ROWS_DISPLAY);
+
+ // if the flat parameter is not provided, make sure it is set to 0 in the URL,
+ // so users can see that they can set it to 1 (see #3365)
+ if (Piwik_Common::getRequestVar('flat', false) === false) {
+ $view->setCustomParameter('flat', 0);
+ }
+
+ $view->main();
+ // we need to rewrite the phpArray so it contains all the recursive arrays
+ if ($currentlySearching) {
+ $phpArrayRecursive = $this->getArrayFromRecursiveDataTable($view->getDataTable());
+ $view->getView()->arrayDataTable = $phpArrayRecursive;
+ }
+ }
+
+ protected function getArrayFromRecursiveDataTable($dataTable, $depth = 0)
+ {
+ $table = array();
+ foreach ($dataTable->getRows() as $row) {
+ $phpArray = array();
+ if (($idSubtable = $row->getIdSubDataTable()) !== null) {
+ $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable);
+
+ if ($subTable->getRowsCount() > 0) {
+ $phpArray = $this->getArrayFromRecursiveDataTable($subTable, $depth + 1);
+ }
+ }
+
+ $newRow = array(
+ 'level' => $depth,
+ 'columns' => $row->getColumns(),
+ 'metadata' => $row->getMetadata(),
+ 'idsubdatatable' => $row->getIdSubDataTable()
+ );
+ $table[] = $newRow;
+ if (count($phpArray) > 0) {
+ $table = array_merge($table, $phpArray);
+ }
+ }
+ return $table;
+ }
+
+ /** Returns action to use when linking to the exit page URLs report. */
+ private function getExitPageUrlActionForLink()
+ {
+ // link to the page not, just the report, but only if not a widget
+ return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexExitPageUrls' : 'getExitPageUrls';
+ }
+
+
+ /** Returns action to use when linking to the entry page URLs report. */
+ private function getEntryPageUrlActionForLink()
+ {
+ // link to the page not, just the report, but only if not a widget
+ return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexEntryPageUrls' : 'getEntryPageUrls';
+ }
+
+ /** Returns action to use when linking to the page titles report. */
+ private function getPageTitlesActionForLink()
+ {
+ // link to the page not, just the report, but only if not a widget
+ return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexPageTitles' : 'getPageTitles';
+ }
}
diff --git a/plugins/Actions/templates/indexSiteSearch.tpl b/plugins/Actions/templates/indexSiteSearch.tpl
index 3af0d5c0a4..a692fdd697 100644
--- a/plugins/Actions/templates/indexSiteSearch.tpl
+++ b/plugins/Actions/templates/indexSiteSearch.tpl
@@ -1,19 +1,19 @@
<div id='leftcolumn'>
- <h2>{'Actions_WidgetSearchKeywords'|translate}</h2>
-{$keywords}
+ <h2>{'Actions_WidgetSearchKeywords'|translate}</h2>
+ {$keywords}
- <h2>{'Actions_WidgetSearchNoResultKeywords'|translate}</h2>
-{$noResultKeywords}
+ <h2>{'Actions_WidgetSearchNoResultKeywords'|translate}</h2>
+ {$noResultKeywords}
-{if isset($categories)}
- <h2>{'Actions_WidgetSearchCategories'|translate}</h2>
-{$categories}
-{/if}
+ {if isset($categories)}
+ <h2>{'Actions_WidgetSearchCategories'|translate}</h2>
+ {$categories}
+ {/if}
</div>
<div id='rightcolumn'>
- <h2>{'Actions_WidgetPageUrlsFollowingSearch'|translate}</h2>
-{$pagesUrlsFollowingSiteSearch}
+ <h2>{'Actions_WidgetPageUrlsFollowingSearch'|translate}</h2>
+ {$pagesUrlsFollowingSiteSearch}
</div>
diff --git a/plugins/Annotations/API.php b/plugins/Annotations/API.php
index e03f5f5dba..7750c816e3 100755
--- a/plugins/Annotations/API.php
+++ b/plugins/Annotations/API.php
@@ -12,365 +12,350 @@
/**
* @see plugins/Annotations/AnnotationList.php
*/
-require_once PIWIK_INCLUDE_PATH.'/plugins/Annotations/AnnotationList.php';
+require_once PIWIK_INCLUDE_PATH . '/plugins/Annotations/AnnotationList.php';
/**
* API for annotations plugin. Provides methods to create, modify, delete & query
* annotations.
- *
+ *
* @package Piwik_Annotations
*/
class Piwik_Annotations_API
{
- static private $instance = null;
-
- /**
- * Returns this API's singleton instance.
- *
- * @return Piwik_Annotations_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Create a new annotation for a site.
- *
- * @param string $idSite The site ID to add the annotation to.
- * @param string $date The date the annotation is attached to.
- * @param string $note The text of the annotation.
- * @param string $starred Either 0 or 1. Whether the annotation should be starred.
- * @return array Returns an array of two elements. The first element (indexed by
- * 'annotation') is the new annotation. The second element (indexed
- * by 'idNote' is the new note's ID).
- */
- public function add( $idSite, $date, $note, $starred = 0 )
- {
- $this->checkSingleIdSite($idSite, $extraMessage = "Note: Cannot add one note to multiple sites.");
- $this->checkDateIsValid($date);
- $this->checkUserCanAddNotesFor($idSite);
-
- // add, save & return a new annotation
- $annotations = new Piwik_Annotations_AnnotationList($idSite);
-
- $newAnnotation = $annotations->add($idSite, $date, $note, $starred);
- $annotations->save($idSite);
-
- return $newAnnotation;
- }
-
- /**
- * Modifies an annotation for a site and returns the modified annotation
- * and its ID.
- *
- * If the current user is not allowed to modify an annotation, an exception
- * will be thrown. A user can modify a note if:
- * - the user has admin access for the site, OR
- * - the user has view access, is not the anonymous user and is the user that
- * created the note
- *
- * @param string $idSite The site ID to add the annotation to.
- * @param string $idNote The ID of the note.
- * @param string|null $date The date the annotation is attached to. If null, the annotation's
- * date is not modified.
- * @param string|null $note The text of the annotation. If null, the annotation's text
- * is not modified.
- * @param string|null $starred Either 0 or 1. Whether the annotation should be starred.
- * If null, the annotation is not starred/un-starred.
- * @return array Returns an array of two elements. The first element (indexed by
- * 'annotation') is the new annotation. The second element (indexed
- * by 'idNote' is the new note's ID).
- */
- public function save( $idSite, $idNote, $date = null, $note = null, $starred = null )
- {
- $this->checkSingleIdSite($idSite, $extraMessage = "Note: Cannot modify more than one note at a time.");
- $this->checkDateIsValid($date, $canBeNull = true);
-
- // get the annotations for the site
- $annotations = new Piwik_Annotations_AnnotationList($idSite);
-
- // check permissions
- $this->checkUserCanModifyOrDelete($idSite, $annotations->get($idSite, $idNote));
-
- // modify the annotation, and save the whole list
- $annotations->update($idSite, $idNote, $date, $note, $starred);
- $annotations->save($idSite);
-
- return $annotations->get($idSite, $idNote);
- }
-
- /**
- * Removes an annotation from a site's list of annotations.
- *
- * If the current user is not allowed to delete the annotation, an exception
- * will be thrown. A user can delete a note if:
- * - the user has admin access for the site, OR
- * - the user has view access, is not the anonymous user and is the user that
- * created the note
- *
- * @param string $idSite The site ID to add the annotation to.
- * @param string $idNote The ID of the note to delete.
- */
- public function delete( $idSite, $idNote )
- {
- $this->checkSingleIdSite($idSite, $extraMessage = "Note: Cannot delete multiple notes.");
-
- $annotations = new Piwik_Annotations_AnnotationList($idSite);
-
- // check permissions
- $this->checkUserCanModifyOrDelete($idSite, $annotations->get($idSite, $idNote));
-
- // remove the note & save the list
- $annotations->remove($idSite, $idNote);
- $annotations->save($idSite);
- }
-
- /**
- * Returns a single note for one site.
- *
- * @param string $idSite The site ID to add the annotation to.
- * @param string $idNote The ID of the note to get.
- * @return array The annotation. It will contain the following properties:
- * - date: The date the annotation was recorded for.
- * - note: The note text.
- * - starred: Whether the note is starred or not.
- * - user: The user that created the note.
- * - canEditOrDelete: Whether the user that called this method can edit or
- * delete the annotation returned.
- */
- public function get( $idSite, $idNote )
- {
- $this->checkSingleIdSite($idSite, $extraMessage = "Note: Specify only one site ID when getting ONE note.");
- Piwik::checkUserHasViewAccess($idSite);
-
- // get single annotation
- $annotations = new Piwik_Annotations_AnnotationList($idSite);
- return $annotations->get($idSite, $idNote);
- }
-
- /**
- * Returns every annotation for a specific site within a specific date range.
- * The date range is specified by a date, the period type (day/week/month/year)
- * and an optional number of N periods in the past to include.
- *
- * @param string $idSite The site ID to add the annotation to. Can be one ID or
- * a list of site IDs.
- * @param string|false $date The date of the period.
- * @param string $period The period type.
- * @param int|false $lastN Whether to include the last N number of periods in the
- * date range or not.
- * @return array An array that indexes arrays of annotations by site ID. ie,
- * array(
- * 5 => array(
- * array(...), // annotation #1
- * array(...), // annotation #2
- * ),
- * 8 => array(...)
- * )
- */
- public function getAll( $idSite, $date = false, $period = 'day', $lastN = false )
- {
- Piwik::checkUserHasViewAccess($idSite);
-
- $annotations = new Piwik_Annotations_AnnotationList($idSite);
-
- // if date/period are supplied, determine start/end date for search
- list($startDate, $endDate) = self::getDateRangeForPeriod($date, $period, $lastN);
-
- return $annotations->search($startDate, $endDate);
- }
-
- /**
- * Returns the count of annotations for a list of periods, including the count of
- * starred annotations.
- *
- * @param string $idSite The site ID to add the annotation to.
- * @param string|false $date The date of the period.
- * @param string $period The period type.
- * @param int|false $lastN Whether to get counts for the last N number of periods or not.
- * @return array An array mapping site IDs to arrays holding dates & the count of
- * annotations made for those dates. eg,
- * array(
- * 5 => array(
- * array('2012-01-02', array('count' => 4, 'starred' => 2)),
- * array('2012-01-03', array('count' => 0, 'starred' => 0)),
- * array('2012-01-04', array('count' => 2, 'starred' => 0)),
- * ),
- * 6 => array(
- * array('2012-01-02', array('count' => 1, 'starred' => 0)),
- * array('2012-01-03', array('count' => 4, 'starred' => 3)),
- * array('2012-01-04', array('count' => 2, 'starred' => 0)),
- * ),
- * ...
- * )
- */
- public function getAnnotationCountForDates( $idSite, $date, $period, $lastN = false, $getAnnotationText = false )
- {
- Piwik::checkUserHasViewAccess($idSite);
-
- // get start & end date for request. lastN is ignored if $period == 'range'
- list($startDate, $endDate) = self::getDateRangeForPeriod($date, $period, $lastN);
- if ($period == 'range')
- {
- $period = 'day';
- }
-
- // create list of dates
- $dates = array();
- for (; $startDate->getTimestamp() <= $endDate->getTimestamp(); $startDate = $startDate->addPeriod(1, $period))
- {
- $dates[] = $startDate;
- }
- // we add one for the end of the last period (used in for loop below to bound annotation dates)
- $dates[] = $startDate;
-
- // get annotations for the site
- $annotations = new Piwik_Annotations_AnnotationList($idSite);
-
- // create result w/ 0-counts
- $result = array();
- for ($i = 0; $i != count($dates) - 1; ++$i)
- {
- $date = $dates[$i];
- $nextDate = $dates[$i + 1];
- $strDate = $date->toString();
-
- foreach ($annotations->getIdSites() as $idSite)
- {
- $result[$idSite][$strDate] = $annotations->count($idSite, $date, $nextDate);
-
- // if only one annotation, return the one annotation's text w/ the counts
- if ($getAnnotationText
- && $result[$idSite][$strDate]['count'] == 1)
- {
- $annotationsForSite = $annotations->search(
- $date, Piwik_Date::factory($nextDate->getTimestamp() - 1), $idSite);
- $annotation = reset($annotationsForSite[$idSite]);
-
- $result[$idSite][$strDate]['note'] = $annotation['note'];
- }
- }
- }
-
- // convert associative array into array of pairs (so it can be traversed by index)
- $pairResult = array();
- foreach ($result as $idSite => $counts)
- {
- foreach ($counts as $date => $count)
- {
- $pairResult[$idSite][] = array($date, $count);
- }
- }
- return $pairResult;
- }
-
- /**
- * Throws if the current user is not allowed to modify or delete an annotation.
- *
- * @param int $idSite The site ID the annotation belongs to.
- * @param array $annotation The annotation.
- * @throws Exception if the current user is not allowed to modify/delete $annotation.
- */
- private function checkUserCanModifyOrDelete( $idSite, $annotation )
- {
- if (!$annotation['canEditOrDelete'])
- {
- throw new Exception(Piwik_Translate('Annotations_YouCannotModifyThisNote'));
- }
- }
-
- /**
- * Throws if the current user is not allowed to create annotations for a site.
- *
- * @param int $idSite The site ID.
- * @throws Exception if the current user is anonymous or does not have view access
- * for site w/ id=$idSite.
- */
- private static function checkUserCanAddNotesFor( $idSite )
- {
- if (!Piwik_Annotations_AnnotationList::canUserAddNotesFor($idSite))
- {
- throw new Exception("The current user is not allowed to add notes for site #$idSite.");
- }
- }
-
- /**
- * Returns start & end dates for the range described by a period and optional lastN
- * argument.
- *
- * @param string $date|false The start date of the period (or the date range of a range
- * period).
- * @param string $period The period type ('day', 'week', 'month', 'year' or 'range').
- * @param int|false $lastN Whether to include the last N periods in the range or not.
- * Ignored if period == range.
- *
- * @ignore
- */
- public static function getDateRangeForPeriod( $date, $period, $lastN = false )
- {
- if ($date === false)
- {
- return array(false, false);
- }
-
- // if the range is just a normal period (or the period is a range in which case lastN is ignored)
- if ($lastN === false
- || $period == 'range')
- {
- if ($period == 'range')
- {
- $oPeriod = new Piwik_Period_Range('day', $date);
- }
- else
- {
- $oPeriod = Piwik_Period::factory($period, Piwik_Date::factory($date));
- }
-
- $startDate = $oPeriod->getDateStart();
- $endDate = $oPeriod->getDateEnd();
- }
- else // if the range includes the last N periods
- {
- list($date, $lastN) =
- Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution::getDateRangeAndLastN($period, $date, $lastN);
- list($startDate, $endDate) = explode(',', $date);
-
- $startDate = Piwik_Date::factory($startDate);
- $endDate = Piwik_Date::factory($endDate);
- }
- return array($startDate, $endDate);
- }
-
- /**
- * Utility function, makes sure idSite string has only one site ID and throws if
- * otherwise.
- */
- private function checkSingleIdSite( $idSite, $extraMessage )
- {
- // can only add a note to one site
- if (!is_numeric($idSite))
- {
- throw new Exception("Invalid idSite: '$idSite'. $extraMessage");
- }
- }
-
- /**
- * Utility function, makes sure date string is valid date, and throws if
- * otherwise.
- */
- private function checkDateIsValid( $date, $canBeNull = false )
- {
- if ($date === null
- && $canBeNull)
- {
- return;
- }
-
- Piwik_Date::factory($date);
- }
+ static private $instance = null;
+
+ /**
+ * Returns this API's singleton instance.
+ *
+ * @return Piwik_Annotations_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Create a new annotation for a site.
+ *
+ * @param string $idSite The site ID to add the annotation to.
+ * @param string $date The date the annotation is attached to.
+ * @param string $note The text of the annotation.
+ * @param string $starred Either 0 or 1. Whether the annotation should be starred.
+ * @return array Returns an array of two elements. The first element (indexed by
+ * 'annotation') is the new annotation. The second element (indexed
+ * by 'idNote' is the new note's ID).
+ */
+ public function add($idSite, $date, $note, $starred = 0)
+ {
+ $this->checkSingleIdSite($idSite, $extraMessage = "Note: Cannot add one note to multiple sites.");
+ $this->checkDateIsValid($date);
+ $this->checkUserCanAddNotesFor($idSite);
+
+ // add, save & return a new annotation
+ $annotations = new Piwik_Annotations_AnnotationList($idSite);
+
+ $newAnnotation = $annotations->add($idSite, $date, $note, $starred);
+ $annotations->save($idSite);
+
+ return $newAnnotation;
+ }
+
+ /**
+ * Modifies an annotation for a site and returns the modified annotation
+ * and its ID.
+ *
+ * If the current user is not allowed to modify an annotation, an exception
+ * will be thrown. A user can modify a note if:
+ * - the user has admin access for the site, OR
+ * - the user has view access, is not the anonymous user and is the user that
+ * created the note
+ *
+ * @param string $idSite The site ID to add the annotation to.
+ * @param string $idNote The ID of the note.
+ * @param string|null $date The date the annotation is attached to. If null, the annotation's
+ * date is not modified.
+ * @param string|null $note The text of the annotation. If null, the annotation's text
+ * is not modified.
+ * @param string|null $starred Either 0 or 1. Whether the annotation should be starred.
+ * If null, the annotation is not starred/un-starred.
+ * @return array Returns an array of two elements. The first element (indexed by
+ * 'annotation') is the new annotation. The second element (indexed
+ * by 'idNote' is the new note's ID).
+ */
+ public function save($idSite, $idNote, $date = null, $note = null, $starred = null)
+ {
+ $this->checkSingleIdSite($idSite, $extraMessage = "Note: Cannot modify more than one note at a time.");
+ $this->checkDateIsValid($date, $canBeNull = true);
+
+ // get the annotations for the site
+ $annotations = new Piwik_Annotations_AnnotationList($idSite);
+
+ // check permissions
+ $this->checkUserCanModifyOrDelete($idSite, $annotations->get($idSite, $idNote));
+
+ // modify the annotation, and save the whole list
+ $annotations->update($idSite, $idNote, $date, $note, $starred);
+ $annotations->save($idSite);
+
+ return $annotations->get($idSite, $idNote);
+ }
+
+ /**
+ * Removes an annotation from a site's list of annotations.
+ *
+ * If the current user is not allowed to delete the annotation, an exception
+ * will be thrown. A user can delete a note if:
+ * - the user has admin access for the site, OR
+ * - the user has view access, is not the anonymous user and is the user that
+ * created the note
+ *
+ * @param string $idSite The site ID to add the annotation to.
+ * @param string $idNote The ID of the note to delete.
+ */
+ public function delete($idSite, $idNote)
+ {
+ $this->checkSingleIdSite($idSite, $extraMessage = "Note: Cannot delete multiple notes.");
+
+ $annotations = new Piwik_Annotations_AnnotationList($idSite);
+
+ // check permissions
+ $this->checkUserCanModifyOrDelete($idSite, $annotations->get($idSite, $idNote));
+
+ // remove the note & save the list
+ $annotations->remove($idSite, $idNote);
+ $annotations->save($idSite);
+ }
+
+ /**
+ * Returns a single note for one site.
+ *
+ * @param string $idSite The site ID to add the annotation to.
+ * @param string $idNote The ID of the note to get.
+ * @return array The annotation. It will contain the following properties:
+ * - date: The date the annotation was recorded for.
+ * - note: The note text.
+ * - starred: Whether the note is starred or not.
+ * - user: The user that created the note.
+ * - canEditOrDelete: Whether the user that called this method can edit or
+ * delete the annotation returned.
+ */
+ public function get($idSite, $idNote)
+ {
+ $this->checkSingleIdSite($idSite, $extraMessage = "Note: Specify only one site ID when getting ONE note.");
+ Piwik::checkUserHasViewAccess($idSite);
+
+ // get single annotation
+ $annotations = new Piwik_Annotations_AnnotationList($idSite);
+ return $annotations->get($idSite, $idNote);
+ }
+
+ /**
+ * Returns every annotation for a specific site within a specific date range.
+ * The date range is specified by a date, the period type (day/week/month/year)
+ * and an optional number of N periods in the past to include.
+ *
+ * @param string $idSite The site ID to add the annotation to. Can be one ID or
+ * a list of site IDs.
+ * @param string|false $date The date of the period.
+ * @param string $period The period type.
+ * @param int|false $lastN Whether to include the last N number of periods in the
+ * date range or not.
+ * @return array An array that indexes arrays of annotations by site ID. ie,
+ * array(
+ * 5 => array(
+ * array(...), // annotation #1
+ * array(...), // annotation #2
+ * ),
+ * 8 => array(...)
+ * )
+ */
+ public function getAll($idSite, $date = false, $period = 'day', $lastN = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $annotations = new Piwik_Annotations_AnnotationList($idSite);
+
+ // if date/period are supplied, determine start/end date for search
+ list($startDate, $endDate) = self::getDateRangeForPeriod($date, $period, $lastN);
+
+ return $annotations->search($startDate, $endDate);
+ }
+
+ /**
+ * Returns the count of annotations for a list of periods, including the count of
+ * starred annotations.
+ *
+ * @param string $idSite The site ID to add the annotation to.
+ * @param string|false $date The date of the period.
+ * @param string $period The period type.
+ * @param int|false $lastN Whether to get counts for the last N number of periods or not.
+ * @return array An array mapping site IDs to arrays holding dates & the count of
+ * annotations made for those dates. eg,
+ * array(
+ * 5 => array(
+ * array('2012-01-02', array('count' => 4, 'starred' => 2)),
+ * array('2012-01-03', array('count' => 0, 'starred' => 0)),
+ * array('2012-01-04', array('count' => 2, 'starred' => 0)),
+ * ),
+ * 6 => array(
+ * array('2012-01-02', array('count' => 1, 'starred' => 0)),
+ * array('2012-01-03', array('count' => 4, 'starred' => 3)),
+ * array('2012-01-04', array('count' => 2, 'starred' => 0)),
+ * ),
+ * ...
+ * )
+ */
+ public function getAnnotationCountForDates($idSite, $date, $period, $lastN = false, $getAnnotationText = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+
+ // get start & end date for request. lastN is ignored if $period == 'range'
+ list($startDate, $endDate) = self::getDateRangeForPeriod($date, $period, $lastN);
+ if ($period == 'range') {
+ $period = 'day';
+ }
+
+ // create list of dates
+ $dates = array();
+ for (; $startDate->getTimestamp() <= $endDate->getTimestamp(); $startDate = $startDate->addPeriod(1, $period)) {
+ $dates[] = $startDate;
+ }
+ // we add one for the end of the last period (used in for loop below to bound annotation dates)
+ $dates[] = $startDate;
+
+ // get annotations for the site
+ $annotations = new Piwik_Annotations_AnnotationList($idSite);
+
+ // create result w/ 0-counts
+ $result = array();
+ for ($i = 0; $i != count($dates) - 1; ++$i) {
+ $date = $dates[$i];
+ $nextDate = $dates[$i + 1];
+ $strDate = $date->toString();
+
+ foreach ($annotations->getIdSites() as $idSite) {
+ $result[$idSite][$strDate] = $annotations->count($idSite, $date, $nextDate);
+
+ // if only one annotation, return the one annotation's text w/ the counts
+ if ($getAnnotationText
+ && $result[$idSite][$strDate]['count'] == 1
+ ) {
+ $annotationsForSite = $annotations->search(
+ $date, Piwik_Date::factory($nextDate->getTimestamp() - 1), $idSite);
+ $annotation = reset($annotationsForSite[$idSite]);
+
+ $result[$idSite][$strDate]['note'] = $annotation['note'];
+ }
+ }
+ }
+
+ // convert associative array into array of pairs (so it can be traversed by index)
+ $pairResult = array();
+ foreach ($result as $idSite => $counts) {
+ foreach ($counts as $date => $count) {
+ $pairResult[$idSite][] = array($date, $count);
+ }
+ }
+ return $pairResult;
+ }
+
+ /**
+ * Throws if the current user is not allowed to modify or delete an annotation.
+ *
+ * @param int $idSite The site ID the annotation belongs to.
+ * @param array $annotation The annotation.
+ * @throws Exception if the current user is not allowed to modify/delete $annotation.
+ */
+ private function checkUserCanModifyOrDelete($idSite, $annotation)
+ {
+ if (!$annotation['canEditOrDelete']) {
+ throw new Exception(Piwik_Translate('Annotations_YouCannotModifyThisNote'));
+ }
+ }
+
+ /**
+ * Throws if the current user is not allowed to create annotations for a site.
+ *
+ * @param int $idSite The site ID.
+ * @throws Exception if the current user is anonymous or does not have view access
+ * for site w/ id=$idSite.
+ */
+ private static function checkUserCanAddNotesFor($idSite)
+ {
+ if (!Piwik_Annotations_AnnotationList::canUserAddNotesFor($idSite)) {
+ throw new Exception("The current user is not allowed to add notes for site #$idSite.");
+ }
+ }
+
+ /**
+ * Returns start & end dates for the range described by a period and optional lastN
+ * argument.
+ *
+ * @param string $date|false The start date of the period (or the date range of a range
+ * period).
+ * @param string $period The period type ('day', 'week', 'month', 'year' or 'range').
+ * @param int|false $lastN Whether to include the last N periods in the range or not.
+ * Ignored if period == range.
+ *
+ * @ignore
+ */
+ public static function getDateRangeForPeriod($date, $period, $lastN = false)
+ {
+ if ($date === false) {
+ return array(false, false);
+ }
+
+ // if the range is just a normal period (or the period is a range in which case lastN is ignored)
+ if ($lastN === false
+ || $period == 'range'
+ ) {
+ if ($period == 'range') {
+ $oPeriod = new Piwik_Period_Range('day', $date);
+ } else {
+ $oPeriod = Piwik_Period::factory($period, Piwik_Date::factory($date));
+ }
+
+ $startDate = $oPeriod->getDateStart();
+ $endDate = $oPeriod->getDateEnd();
+ } else // if the range includes the last N periods
+ {
+ list($date, $lastN) =
+ Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution::getDateRangeAndLastN($period, $date, $lastN);
+ list($startDate, $endDate) = explode(',', $date);
+
+ $startDate = Piwik_Date::factory($startDate);
+ $endDate = Piwik_Date::factory($endDate);
+ }
+ return array($startDate, $endDate);
+ }
+
+ /**
+ * Utility function, makes sure idSite string has only one site ID and throws if
+ * otherwise.
+ */
+ private function checkSingleIdSite($idSite, $extraMessage)
+ {
+ // can only add a note to one site
+ if (!is_numeric($idSite)) {
+ throw new Exception("Invalid idSite: '$idSite'. $extraMessage");
+ }
+ }
+
+ /**
+ * Utility function, makes sure date string is valid date, and throws if
+ * otherwise.
+ */
+ private function checkDateIsValid($date, $canBeNull = false)
+ {
+ if ($date === null
+ && $canBeNull
+ ) {
+ return;
+ }
+
+ Piwik_Date::factory($date);
+ }
}
diff --git a/plugins/Annotations/AnnotationList.php b/plugins/Annotations/AnnotationList.php
index 47d897f2dc..daed59a554 100755
--- a/plugins/Annotations/AnnotationList.php
+++ b/plugins/Annotations/AnnotationList.php
@@ -12,440 +12,418 @@
/**
* This class can be used to query & modify annotations for multiple sites
* at once.
- *
+ *
* Example use:
* $annotations = new Piwik_Annotations_AnnotationList($idSites = "1,2,5");
* $annotation = $annotations->get($idSite = 1, $idNote = 4);
* // do stuff w/ annotation
* $annotations->update($idSite = 2, $idNote = 4, $note = "This is the new text.");
* $annotations->save($idSite);
- *
+ *
* Note: There is a concurrency issue w/ this code. If two users try to save
* an annotation for the same site, it's possible one of their changes will
* never get made (as it will be overwritten by the other's).
- *
+ *
* @package Piwik_Annotations
*/
class Piwik_Annotations_AnnotationList
{
- const ANNOTATION_COLLECTION_OPTION_SUFFIX = '_annotations';
-
- /**
- * List of site IDs this instance holds annotations for.
- *
- * @var array
- */
- private $idSites;
-
- /**
- * Array that associates lists of annotations with site IDs.
- *
- * @var array
- */
- private $annotations;
-
- /**
- * Constructor. Loads annotations from the database.
- *
- * @param string|int $idSites The list of site IDs to load annotations for.
- */
- public function __construct( $idSites )
- {
- $this->idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites);
- $this->annotations = $this->getAnnotationsForSite();
- }
-
- /**
- * Returns the list of site IDs this list contains annotations for.
- *
- * @return array
- */
- public function getIdSites()
- {
- return $this->idSites;
- }
-
- /**
- * Creates a new annotation for a site. This method does not perist the result.
- * To save the new annotation in the database, call $this->save.
- *
- * @param int $idSite The ID of the site to add an annotation to.
- * @param string $date The date the annotation is in reference to.
- * @param string $note The text of the new annotation.
- * @param int $starred Either 1 or 0. If 1, the new annotation has been starred,
- * otherwise it will start out unstarred.
- * @return array The added annotation.
- * @throws Exception if $idSite is not an ID that was supplied upon construction.
- */
- public function add($idSite, $date, $note, $starred = 0)
- {
- $this->checkIdSiteIsLoaded($idSite);
-
- $this->annotations[$idSite][] = self::makeAnnotation($date, $note, $starred);
-
- // get the id of the new annotation
- end($this->annotations[$idSite]);
- $newNoteId = key($this->annotations[$idSite]);
-
- return $this->get($idSite, $newNoteId);
- }
-
- /**
- * Persists the annotations list for a site, overwriting whatever exists.
- *
- * @param int $idSite The ID of the site to save annotations for.
- * @throws Exception if $idSite is not an ID that was supplied upon construction.
- */
- public function save($idSite)
- {
- $this->checkIdSiteIsLoaded($idSite);
-
- $optionName = self::getAnnotationCollectionOptionName($idSite);
- Piwik_SetOption($optionName, serialize($this->annotations[$idSite]));
- }
-
- /**
- * Modifies an annotation in this instance's collection of annotations.
- *
- * Note: This method does not perist the change in the DB. The save method must
- * be called for that.
- *
- * @param int $idSite The ID of the site whose annotation will be updated.
- * @param int $idNote The ID of the note.
- * @param string|null $date The new date of the annotation, eg '2012-01-01'. If
- * null, no change is made.
- * @param string|null $note The new text of the annotation. If null, no change
- * is made.
- * @param int|null $starred Either 1 or 0, whether the annotation should be
- * starred or not. If null, no change is made.
- * @throws Exception if $idSite is not an ID that was supplied upon construction.
- * @throws Exception if $idNote does not refer to valid note for the site.
- */
- public function update( $idSite, $idNote, $date = null, $note = null, $starred = null )
- {
- $this->checkIdSiteIsLoaded($idSite);
- $this->checkNoteExists($idSite, $idNote);
-
- $annotation =& $this->annotations[$idSite][$idNote];
- if ($date !== null)
- {
- $annotation['date'] = $date;
- }
- if ($note !== null)
- {
- $annotation['note'] = $note;
- }
- if ($starred !== null)
- {
- $annotation['starred'] = $starred;
- }
- }
-
- /**
- * Removes a note from a site's collection of annotations.
- *
- * Note: This method does not perist the change in the DB. The save method must
- * be called for that.
- *
- * @param int $idSite The ID of the site whose annotation will be updated.
- * @param int $idNote The ID of the note.
- * @throws Exception if $idSite is not an ID that was supplied upon construction.
- * @throws Exception if $idNote does not refer to valid note for the site.
- */
- public function remove( $idSite, $idNote )
- {
- $this->checkIdSiteIsLoaded($idSite);
- $this->checkNoteExists($idSite, $idNote);
-
- unset($this->annotations[$idSite][$idNote]);
- }
-
- /**
- * Retrieves an annotation by ID.
- *
- * This function returns an array with the following elements:
- * - idNote: The ID of the annotation.
- * - date: The date of the annotation.
- * - note: The text of the annotation.
- * - starred: 1 or 0, whether the annotation is stared;
- * - user: (unless current user is anonymous) The user that created the annotation.
- * - canEditOrDelete: True if the user can edit/delete the annotation.
- *
- * @param int $idSite The ID of the site to get an annotation for.
- * @param int $idNote The ID of the note to get.
- * @param array The annotation.
- * @throws Exception if $idSite is not an ID that was supplied upon construction.
- * @throws Exception if $idNote does not refer to valid note for the site.
- */
- public function get( $idSite, $idNote )
- {
- $this->checkIdSiteIsLoaded($idSite);
- $this->checkNoteExists($idSite, $idNote);
-
- $annotation = $this->annotations[$idSite][$idNote];
- $this->augmentAnnotationData($idSite, $idNote, $annotation);
- return $annotation;
- }
-
- /**
- * Returns all annotations within a specific date range. The result is
- * an array that maps site IDs with arrays of annotations within the range.
- *
- * Note: The date range is inclusive.
- *
- * @see self::get for info on what attributes stored within annotations.
- *
- * @param Piwik_Date|false $startDate The start of the date range.
- * @param Piwik_Date|false $endDate The end of the date range.
- * @param string|int|array|false $idSite IDs of the sites whose annotations to
- * search through.
- * @return array Array mapping site IDs with arrays of annotations, eg:
- * array(
- * '5' => array(
- * array(...), // annotation
- * array(...), // annotation
- * ...
- * ),
- * '6' => array(
- * array(...), // annotation
- * array(...), // annotation
- * ...
- * ),
- * )
- */
- public function search( $startDate, $endDate, $idSite = false )
- {
- if ($idSite)
- {
- $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSite);
- }
- else
- {
- $idSites = array_keys($this->annotations);
- }
-
- // collect annotations that are within the right date range & belong to the right
- // site
- $result = array();
- foreach ($idSites as $idSite)
- {
- if (!isset($this->annotations[$idSite]))
- {
- continue;
- }
-
- foreach ($this->annotations[$idSite] as $idNote => $annotation)
- {
- if ($startDate !== false)
- {
- $annotationDate = Piwik_Date::factory($annotation['date']);
- if ($annotationDate->getTimestamp() < $startDate->getTimestamp()
- || $annotationDate->getTimestamp() > $endDate->getTimestamp())
- {
- continue;
- }
- }
-
- $this->augmentAnnotationData($idSite, $idNote, $annotation);
- $result[$idSite][] = $annotation;
- }
-
- // sort by annotation date
- if (!empty($result[$idSite]))
- {
- uasort($result[$idSite], array($this, 'compareAnnotationDate'));
- }
- }
- return $result;
- }
-
- /**
- * Counts annotations & starred annotations within a date range and returns
- * the counts. The date range includes the start date, but not the end date.
- *
- * @param int $idSite The ID of the site to count annotations for.
- * @param string|false $startDate The start date of the range or false if no
- * range check is desired.
- * @param string|false $endDate The end date of the range or false if no
- * range check is desired.
- * @return array eg, array('count' => 5, 'starred' => 2)
- */
- public function count( $idSite, $startDate, $endDate )
- {
- $this->checkIdSiteIsLoaded($idSite);
-
- // search includes end date, and count should not, so subtract one from the timestamp
- $annotations = $this->search($startDate, Piwik_Date::factory($endDate->getTimestamp() - 1));
-
- // count the annotations
- $count = $starred = 0;
- if (!empty($annotations[$idSite]))
- {
- $count = count($annotations[$idSite]);
- foreach ($annotations[$idSite] as $annotation)
- {
- if ($annotation['starred'])
- {
- ++$starred;
- }
- }
- }
-
- return array('count' => $count, 'starred' => $starred);
- }
-
- /**
- * Utility function. Creates a new annotation.
- *
- * @param string $date
- * @param string $note
- * @param int $starred
- */
- private function makeAnnotation( $date, $note, $starred = 0 )
- {
- return array('date' => $date,
- 'note' => $note,
- 'starred' => (int)$starred,
- 'user' => Piwik::getCurrentUserLogin());
- }
-
- /**
- * Retrieves annotations from the database for the sites supplied to the
- * constructor.
- *
- * @return array Lists of annotations mapped by site ID.
- */
- private function getAnnotationsForSite()
- {
- $result = array();
- foreach ($this->idSites as $id)
- {
- $optionName = self::getAnnotationCollectionOptionName($id);
- $serialized = Piwik_GetOption($optionName);
-
- if ($serialized !== false)
- {
- $result[$id] = unserialize($serialized);
- }
- else
- {
- $result[$id] = array();
- }
- }
- return $result;
- }
-
- /**
- * Utility function that checks if a site ID was supplied and if not,
- * throws an exception.
- *
- * We can only modify/read annotations for sites that we've actually
- * loaded the annotations for.
- *
- * @param int $idSite
- * @throws Exception
- */
- private function checkIdSiteIsLoaded( $idSite )
- {
- if (!in_array($idSite, $this->idSites))
- {
- throw new Exception("This AnnotationList was not initialized with idSite '$idSite'.");
- }
- }
-
- /**
- * Utility function that checks if a note exists for a site, and if not,
- * throws an exception.
- *
- * @param int $idSite
- * @param int $idNote
- * @throws Exception
- */
- private function checkNoteExists( $idSite, $idNote )
- {
- if (empty($this->annotations[$idSite][$idNote]))
- {
- throw new Exception("There is no note with id '$idNote' for site with id '$idSite'.");
- }
- }
-
- /**
- * Returns true if the current user can modify or delete a specific annotation.
- *
- * A user can modify/delete a note if the user has admin access for the site OR
- * the user has view access, is not the anonymous user and is the user that
- * created the note in question.
- *
- * @param int $idSite The site ID the annotation belongs to.
- * @param array $annotation The annotation.
- * @return bool
- */
- public static function canUserModifyOrDelete( $idSite, $annotation )
- {
- // user can save if user is admin or if has view access, is not anonymous & is user who wrote note
- $canEdit = Piwik::isUserHasAdminAccess($idSite)
- || (!Piwik::isUserIsAnonymous()
- && Piwik::getCurrentUserLogin() == $annotation['user']);
- return $canEdit;
- }
-
- /**
- * Adds extra data to an annotation, including the annotation's ID and whether
- * the current user can edit or delete it.
- *
- * Also, if the current user is anonymous, the user attribute is removed.
- *
- * @param int $idSite
- * @param int $idNote
- * @param array $annotation
- */
- private function augmentAnnotationData( $idSite, $idNote, &$annotation )
- {
- $annotation['idNote'] = $idNote;
- $annotation['canEditOrDelete'] = self::canUserModifyOrDelete($idSite, $annotation);
-
- // we don't supply user info if the current user is anonymous
- if (Piwik::isUserIsAnonymous())
- {
- unset($annotation['user']);
- }
- }
-
- /**
- * Utility function that compares two annotations.
- *
- * @param array $lhs An annotation.
- * @param array $rhs An annotation.
- * @return int -1, 0 or 1
- */
- public function compareAnnotationDate( $lhs, $rhs )
- {
- if ($lhs['date'] == $rhs['date'])
- {
- return $lhs['idNote'] <= $rhs['idNote'] ? -1 : 1;
- }
-
- return $lhs['date'] < $rhs['date'] ? -1 : 1; // string comparison works because date format should be YYYY-MM-DD
- }
-
- /**
- * Returns true if the current user can add notes for a specific site.
- *
- * @param int $idSite The site to add notes to.
- */
- public static function canUserAddNotesFor( $idSite )
- {
- return Piwik::isUserHasViewAccess($idSite)
- && !Piwik::isUserIsAnonymous($idSite);
- }
-
- /**
- * Returns the option name used to store annotations for a site.
- *
- * @param int $idSite The site ID.
- */
- public static function getAnnotationCollectionOptionName( $idSite )
- {
- return $idSite.self::ANNOTATION_COLLECTION_OPTION_SUFFIX;
- }
+ const ANNOTATION_COLLECTION_OPTION_SUFFIX = '_annotations';
+
+ /**
+ * List of site IDs this instance holds annotations for.
+ *
+ * @var array
+ */
+ private $idSites;
+
+ /**
+ * Array that associates lists of annotations with site IDs.
+ *
+ * @var array
+ */
+ private $annotations;
+
+ /**
+ * Constructor. Loads annotations from the database.
+ *
+ * @param string|int $idSites The list of site IDs to load annotations for.
+ */
+ public function __construct($idSites)
+ {
+ $this->idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites);
+ $this->annotations = $this->getAnnotationsForSite();
+ }
+
+ /**
+ * Returns the list of site IDs this list contains annotations for.
+ *
+ * @return array
+ */
+ public function getIdSites()
+ {
+ return $this->idSites;
+ }
+
+ /**
+ * Creates a new annotation for a site. This method does not perist the result.
+ * To save the new annotation in the database, call $this->save.
+ *
+ * @param int $idSite The ID of the site to add an annotation to.
+ * @param string $date The date the annotation is in reference to.
+ * @param string $note The text of the new annotation.
+ * @param int $starred Either 1 or 0. If 1, the new annotation has been starred,
+ * otherwise it will start out unstarred.
+ * @return array The added annotation.
+ * @throws Exception if $idSite is not an ID that was supplied upon construction.
+ */
+ public function add($idSite, $date, $note, $starred = 0)
+ {
+ $this->checkIdSiteIsLoaded($idSite);
+
+ $this->annotations[$idSite][] = self::makeAnnotation($date, $note, $starred);
+
+ // get the id of the new annotation
+ end($this->annotations[$idSite]);
+ $newNoteId = key($this->annotations[$idSite]);
+
+ return $this->get($idSite, $newNoteId);
+ }
+
+ /**
+ * Persists the annotations list for a site, overwriting whatever exists.
+ *
+ * @param int $idSite The ID of the site to save annotations for.
+ * @throws Exception if $idSite is not an ID that was supplied upon construction.
+ */
+ public function save($idSite)
+ {
+ $this->checkIdSiteIsLoaded($idSite);
+
+ $optionName = self::getAnnotationCollectionOptionName($idSite);
+ Piwik_SetOption($optionName, serialize($this->annotations[$idSite]));
+ }
+
+ /**
+ * Modifies an annotation in this instance's collection of annotations.
+ *
+ * Note: This method does not perist the change in the DB. The save method must
+ * be called for that.
+ *
+ * @param int $idSite The ID of the site whose annotation will be updated.
+ * @param int $idNote The ID of the note.
+ * @param string|null $date The new date of the annotation, eg '2012-01-01'. If
+ * null, no change is made.
+ * @param string|null $note The new text of the annotation. If null, no change
+ * is made.
+ * @param int|null $starred Either 1 or 0, whether the annotation should be
+ * starred or not. If null, no change is made.
+ * @throws Exception if $idSite is not an ID that was supplied upon construction.
+ * @throws Exception if $idNote does not refer to valid note for the site.
+ */
+ public function update($idSite, $idNote, $date = null, $note = null, $starred = null)
+ {
+ $this->checkIdSiteIsLoaded($idSite);
+ $this->checkNoteExists($idSite, $idNote);
+
+ $annotation =& $this->annotations[$idSite][$idNote];
+ if ($date !== null) {
+ $annotation['date'] = $date;
+ }
+ if ($note !== null) {
+ $annotation['note'] = $note;
+ }
+ if ($starred !== null) {
+ $annotation['starred'] = $starred;
+ }
+ }
+
+ /**
+ * Removes a note from a site's collection of annotations.
+ *
+ * Note: This method does not perist the change in the DB. The save method must
+ * be called for that.
+ *
+ * @param int $idSite The ID of the site whose annotation will be updated.
+ * @param int $idNote The ID of the note.
+ * @throws Exception if $idSite is not an ID that was supplied upon construction.
+ * @throws Exception if $idNote does not refer to valid note for the site.
+ */
+ public function remove($idSite, $idNote)
+ {
+ $this->checkIdSiteIsLoaded($idSite);
+ $this->checkNoteExists($idSite, $idNote);
+
+ unset($this->annotations[$idSite][$idNote]);
+ }
+
+ /**
+ * Retrieves an annotation by ID.
+ *
+ * This function returns an array with the following elements:
+ * - idNote: The ID of the annotation.
+ * - date: The date of the annotation.
+ * - note: The text of the annotation.
+ * - starred: 1 or 0, whether the annotation is stared;
+ * - user: (unless current user is anonymous) The user that created the annotation.
+ * - canEditOrDelete: True if the user can edit/delete the annotation.
+ *
+ * @param int $idSite The ID of the site to get an annotation for.
+ * @param int $idNote The ID of the note to get.
+ * @param array The annotation.
+ * @throws Exception if $idSite is not an ID that was supplied upon construction.
+ * @throws Exception if $idNote does not refer to valid note for the site.
+ */
+ public function get($idSite, $idNote)
+ {
+ $this->checkIdSiteIsLoaded($idSite);
+ $this->checkNoteExists($idSite, $idNote);
+
+ $annotation = $this->annotations[$idSite][$idNote];
+ $this->augmentAnnotationData($idSite, $idNote, $annotation);
+ return $annotation;
+ }
+
+ /**
+ * Returns all annotations within a specific date range. The result is
+ * an array that maps site IDs with arrays of annotations within the range.
+ *
+ * Note: The date range is inclusive.
+ *
+ * @see self::get for info on what attributes stored within annotations.
+ *
+ * @param Piwik_Date|false $startDate The start of the date range.
+ * @param Piwik_Date|false $endDate The end of the date range.
+ * @param string|int|array|false $idSite IDs of the sites whose annotations to
+ * search through.
+ * @return array Array mapping site IDs with arrays of annotations, eg:
+ * array(
+ * '5' => array(
+ * array(...), // annotation
+ * array(...), // annotation
+ * ...
+ * ),
+ * '6' => array(
+ * array(...), // annotation
+ * array(...), // annotation
+ * ...
+ * ),
+ * )
+ */
+ public function search($startDate, $endDate, $idSite = false)
+ {
+ if ($idSite) {
+ $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSite);
+ } else {
+ $idSites = array_keys($this->annotations);
+ }
+
+ // collect annotations that are within the right date range & belong to the right
+ // site
+ $result = array();
+ foreach ($idSites as $idSite) {
+ if (!isset($this->annotations[$idSite])) {
+ continue;
+ }
+
+ foreach ($this->annotations[$idSite] as $idNote => $annotation) {
+ if ($startDate !== false) {
+ $annotationDate = Piwik_Date::factory($annotation['date']);
+ if ($annotationDate->getTimestamp() < $startDate->getTimestamp()
+ || $annotationDate->getTimestamp() > $endDate->getTimestamp()
+ ) {
+ continue;
+ }
+ }
+
+ $this->augmentAnnotationData($idSite, $idNote, $annotation);
+ $result[$idSite][] = $annotation;
+ }
+
+ // sort by annotation date
+ if (!empty($result[$idSite])) {
+ uasort($result[$idSite], array($this, 'compareAnnotationDate'));
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Counts annotations & starred annotations within a date range and returns
+ * the counts. The date range includes the start date, but not the end date.
+ *
+ * @param int $idSite The ID of the site to count annotations for.
+ * @param string|false $startDate The start date of the range or false if no
+ * range check is desired.
+ * @param string|false $endDate The end date of the range or false if no
+ * range check is desired.
+ * @return array eg, array('count' => 5, 'starred' => 2)
+ */
+ public function count($idSite, $startDate, $endDate)
+ {
+ $this->checkIdSiteIsLoaded($idSite);
+
+ // search includes end date, and count should not, so subtract one from the timestamp
+ $annotations = $this->search($startDate, Piwik_Date::factory($endDate->getTimestamp() - 1));
+
+ // count the annotations
+ $count = $starred = 0;
+ if (!empty($annotations[$idSite])) {
+ $count = count($annotations[$idSite]);
+ foreach ($annotations[$idSite] as $annotation) {
+ if ($annotation['starred']) {
+ ++$starred;
+ }
+ }
+ }
+
+ return array('count' => $count, 'starred' => $starred);
+ }
+
+ /**
+ * Utility function. Creates a new annotation.
+ *
+ * @param string $date
+ * @param string $note
+ * @param int $starred
+ */
+ private function makeAnnotation($date, $note, $starred = 0)
+ {
+ return array('date' => $date,
+ 'note' => $note,
+ 'starred' => (int)$starred,
+ 'user' => Piwik::getCurrentUserLogin());
+ }
+
+ /**
+ * Retrieves annotations from the database for the sites supplied to the
+ * constructor.
+ *
+ * @return array Lists of annotations mapped by site ID.
+ */
+ private function getAnnotationsForSite()
+ {
+ $result = array();
+ foreach ($this->idSites as $id) {
+ $optionName = self::getAnnotationCollectionOptionName($id);
+ $serialized = Piwik_GetOption($optionName);
+
+ if ($serialized !== false) {
+ $result[$id] = unserialize($serialized);
+ } else {
+ $result[$id] = array();
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Utility function that checks if a site ID was supplied and if not,
+ * throws an exception.
+ *
+ * We can only modify/read annotations for sites that we've actually
+ * loaded the annotations for.
+ *
+ * @param int $idSite
+ * @throws Exception
+ */
+ private function checkIdSiteIsLoaded($idSite)
+ {
+ if (!in_array($idSite, $this->idSites)) {
+ throw new Exception("This AnnotationList was not initialized with idSite '$idSite'.");
+ }
+ }
+
+ /**
+ * Utility function that checks if a note exists for a site, and if not,
+ * throws an exception.
+ *
+ * @param int $idSite
+ * @param int $idNote
+ * @throws Exception
+ */
+ private function checkNoteExists($idSite, $idNote)
+ {
+ if (empty($this->annotations[$idSite][$idNote])) {
+ throw new Exception("There is no note with id '$idNote' for site with id '$idSite'.");
+ }
+ }
+
+ /**
+ * Returns true if the current user can modify or delete a specific annotation.
+ *
+ * A user can modify/delete a note if the user has admin access for the site OR
+ * the user has view access, is not the anonymous user and is the user that
+ * created the note in question.
+ *
+ * @param int $idSite The site ID the annotation belongs to.
+ * @param array $annotation The annotation.
+ * @return bool
+ */
+ public static function canUserModifyOrDelete($idSite, $annotation)
+ {
+ // user can save if user is admin or if has view access, is not anonymous & is user who wrote note
+ $canEdit = Piwik::isUserHasAdminAccess($idSite)
+ || (!Piwik::isUserIsAnonymous()
+ && Piwik::getCurrentUserLogin() == $annotation['user']);
+ return $canEdit;
+ }
+
+ /**
+ * Adds extra data to an annotation, including the annotation's ID and whether
+ * the current user can edit or delete it.
+ *
+ * Also, if the current user is anonymous, the user attribute is removed.
+ *
+ * @param int $idSite
+ * @param int $idNote
+ * @param array $annotation
+ */
+ private function augmentAnnotationData($idSite, $idNote, &$annotation)
+ {
+ $annotation['idNote'] = $idNote;
+ $annotation['canEditOrDelete'] = self::canUserModifyOrDelete($idSite, $annotation);
+
+ // we don't supply user info if the current user is anonymous
+ if (Piwik::isUserIsAnonymous()) {
+ unset($annotation['user']);
+ }
+ }
+
+ /**
+ * Utility function that compares two annotations.
+ *
+ * @param array $lhs An annotation.
+ * @param array $rhs An annotation.
+ * @return int -1, 0 or 1
+ */
+ public function compareAnnotationDate($lhs, $rhs)
+ {
+ if ($lhs['date'] == $rhs['date']) {
+ return $lhs['idNote'] <= $rhs['idNote'] ? -1 : 1;
+ }
+
+ return $lhs['date'] < $rhs['date'] ? -1 : 1; // string comparison works because date format should be YYYY-MM-DD
+ }
+
+ /**
+ * Returns true if the current user can add notes for a specific site.
+ *
+ * @param int $idSite The site to add notes to.
+ */
+ public static function canUserAddNotesFor($idSite)
+ {
+ return Piwik::isUserHasViewAccess($idSite)
+ && !Piwik::isUserIsAnonymous($idSite);
+ }
+
+ /**
+ * Returns the option name used to store annotations for a site.
+ *
+ * @param int $idSite The site ID.
+ */
+ public static function getAnnotationCollectionOptionName($idSite)
+ {
+ return $idSite . self::ANNOTATION_COLLECTION_OPTION_SUFFIX;
+ }
}
diff --git a/plugins/Annotations/Annotations.php b/plugins/Annotations/Annotations.php
index 1b0503862a..937500053a 100755
--- a/plugins/Annotations/Annotations.php
+++ b/plugins/Annotations/Annotations.php
@@ -12,58 +12,58 @@
/**
* Annotations plugins. Provides the ability to attach text notes to
* dates for each sites. Notes can be viewed, modified, deleted or starred.
- *
+ *
* @package Piwik_Annotations
*/
class Piwik_Annotations extends Piwik_Plugin
{
- /**
- * Returns information about this plugin.
- *
- * @return array
- */
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('Annotations_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- /**
- * Returns list of event hooks.
- *
- * @return array
- */
- public function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'AssetManager.getJsFiles' => 'getJsFiles'
- );
- }
+ /**
+ * Returns information about this plugin.
+ *
+ * @return array
+ */
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('Annotations_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ /**
+ * Returns list of event hooks.
+ *
+ * @return array
+ */
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'AssetManager.getJsFiles' => 'getJsFiles'
+ );
+ }
- /**
- * Adds css files for this plugin to the list in the event notification.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
- $cssFiles[] = "plugins/Annotations/templates/styles.css";
- }
+ /**
+ * Adds css files for this plugin to the list in the event notification.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+ $cssFiles[] = "plugins/Annotations/templates/styles.css";
+ }
- /**
- * Adds js files for this plugin to the list in the event notification.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
- $jsFiles[] = "plugins/Annotations/templates/annotations.js";
- }
+ /**
+ * Adds js files for this plugin to the list in the event notification.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+ $jsFiles[] = "plugins/Annotations/templates/annotations.js";
+ }
}
diff --git a/plugins/Annotations/Controller.php b/plugins/Annotations/Controller.php
index 0a235a2200..9b9f9e352c 100755
--- a/plugins/Annotations/Controller.php
+++ b/plugins/Annotations/Controller.php
@@ -11,216 +11,206 @@
/**
* Controller for the Annotations plugin.
- *
+ *
* @package Piwik_Annotations
*/
class Piwik_Annotations_Controller extends Piwik_Controller
{
- /**
- * Controller action that returns HTML displaying annotations for a site and
- * specific date range.
- *
- * Query Param Input:
- * - idSite: The ID of the site to get annotations for. Only one allowed.
- * - date: The date to get annotations for. If lastN is not supplied, this is the start date,
- * otherwise the start date in the last period.
- * - period: The period type.
- * - lastN: If supplied, the last N # of periods will be included w/ the range specified
- * by date + period.
- *
- * Output:
- * - HTML displaying annotations for a specific range.
- *
- * @param bool $fetch True if the annotation manager should be returned as a string,
- * false if it should be echo-ed.
- * @param string $date Override for 'date' query parameter.
- * @param string $period Override for 'period' query parameter.
- * @param string $lastN Override for 'lastN' query parameter.
- * @return string|void
- */
- public function getAnnotationManager( $fetch = false, $date = false, $period = false, $lastN = false )
- {
- $idSite = Piwik_Common::getRequestVar('idSite');
-
- if ($date === false)
- {
- $date = Piwik_Common::getRequestVar('date', false);
- }
-
- if ($period === false)
- {
- $period = Piwik_Common::getRequestVar('period', 'day');
- }
-
- if ($lastN === false)
- {
- $lastN = Piwik_Common::getRequestVar('lastN', false);
- }
-
- // create & render the view
- $view = Piwik_View::factory('annotationManager');
-
- $allAnnotations = Piwik_API_Request::processRequest(
- 'Annotations.getAll', array('date' => $date, 'period' => $period, 'lastN' => $lastN));
- $view->annotations = empty($allAnnotations[$idSite]) ? array() : $allAnnotations[$idSite];
-
- $view->period = $period;
- $view->lastN = $lastN;
-
- list($startDate, $endDate) = Piwik_Annotations_API::getDateRangeForPeriod($date, $period, $lastN);
- $view->startDate = $startDate->toString();
- $view->endDate = $endDate->toString();
-
- $dateFormat = Piwik_Translate('CoreHome_ShortDateFormatWithYear');
- $view->startDatePretty = $startDate->getLocalized($dateFormat);
- $view->endDatePretty = $endDate->getLocalized($dateFormat);
-
- $view->canUserAddNotes = Piwik_Annotations_AnnotationList::canUserAddNotesFor($idSite);
-
- if ($fetch)
- {
- return $view->render();
- }
- else
- {
- echo $view->render();
- }
- }
-
- /**
- * Controller action that modifies an annotation and returns HTML displaying
- * the modified annotation.
- *
- * Query Param Input:
- * - idSite: The ID of the site the annotation belongs to. Only one ID is allowed.
- * - idNote: The ID of the annotation.
- * - date: The new date value for the annotation. (optional)
- * - note: The new text for the annotation. (optional)
- * - starred: Either 1 or 0. Whether the note should be starred or not. (optional)
- *
- * Output:
- * - HTML displaying modified annotation.
- *
- * If an optional query param is not supplied, that part of the annotation is
- * not modified.
- */
- public function saveAnnotation()
- {
- if ($_SERVER["REQUEST_METHOD"] == "POST")
- {
- $this->checkTokenInUrl();
-
- $view = Piwik_View::factory('annotation');
-
- // NOTE: permissions checked in API method
- // save the annotation
- $view->annotation = Piwik_API_Request::processRequest("Annotations.save");
-
- echo $view->render();
- }
- }
-
- /**
- * Controller action that adds a new annotation for a site and returns new
- * annotation manager HTML for the site and date range.
- *
- * Query Param Input:
- * - idSite: The ID of the site to add an annotation to.
- * - date: The date for the new annotation.
- * - note: The text of the annotation.
- * - starred: Either 1 or 0, whether the annotation should be starred or not.
- * Defaults to 0.
- * - managerDate: The date for the annotation manager. If a range is given, the start
- * date is used for the new annotation.
- * - managerPeriod: For rendering the annotation manager. @see self::getAnnotationManager
- * for more info.
- * - lastN: For rendering the annotation manager. @see self::getAnnotationManager
- * for more info.
- * Output:
- * - @see self::getAnnotationManager
- */
- public function addAnnotation()
- {
- if ($_SERVER["REQUEST_METHOD"] == "POST")
- {
- $this->checkTokenInUrl();
-
- // the date used is for the annotation manager HTML that gets echo'd. we
- // use this date for the new annotation, unless it is a date range, in
- // which case we use the first date of the range.
- $date = Piwik_Common::getRequestVar('date');
- if (strpos($date, ',') !== false)
- {
- $date = reset(explode(',', $date));
- }
-
- // add the annotation. NOTE: permissions checked in API method
- Piwik_API_Request::processRequest("Annotations.add", array('date' => $date));
-
- $managerDate = Piwik_Common::getRequestVar('managerDate', false);
- $managerPeriod = Piwik_Common::getRequestVar('managerPeriod', false);
- echo $this->getAnnotationManager($fetch = true, $managerDate, $managerPeriod);
- }
- }
-
- /**
- * Controller action that deletes an annotation and returns new annotation
- * manager HTML for the site & date range.
- *
- * Query Param Input:
- * - idSite: The ID of the site this annotation belongs to.
- * - idNote: The ID of the annotation to delete.
- * - date: For rendering the annotation manager. @see self::getAnnotationManager
- * for more info.
- * - period: For rendering the annotation manager. @see self::getAnnotationManager
- * for more info.
- * - lastN: For rendering the annotation manager. @see self::getAnnotationManager
- * for more info.
- *
- * Output:
- * - @see self::getAnnotationManager
- */
- public function deleteAnnotation()
- {
- if ($_SERVER["REQUEST_METHOD"] == "POST")
- {
- $this->checkTokenInUrl();
-
- // delete annotation. NOTE: permissions checked in API method
- Piwik_API_Request::processRequest("Annotations.delete");
-
- echo $this->getAnnotationManager($fetch = true);
- }
- }
-
- /**
- * Controller action that echo's HTML that displays marker icons for an
- * evolution graph's x-axis. The marker icons still need to be positioned
- * by the JavaScript.
- *
- * Query Param Input:
- * - idSite: The ID of the site this annotation belongs to. Only one is allowed.
- * - date: The date to check for annotations. If lastN is not supplied, this is
- * the start of the date range used to check for annotations. If supplied,
- * this is the start of the last period in the date range.
- * - period: The period type.
- * - lastN: If supplied, the last N # of periods are included in the date range
- * used to check for annotations.
- *
- * Output:
- * - HTML that displays marker icons for an evolution graph based on the
- * number of annotations & starred annotations in the graph's date range.
- */
- public function getEvolutionIcons()
- {
- // get annotation the count
- $annotationCounts = Piwik_API_Request::processRequest(
- "Annotations.getAnnotationCountForDates", array('getAnnotationText' => 1));
-
- // create & render the view
- $view = Piwik_View::factory('evolutionAnnotations');
- $view->annotationCounts = reset($annotationCounts); // only one idSite allowed for this action
-
- echo $view->render();
- }
+ /**
+ * Controller action that returns HTML displaying annotations for a site and
+ * specific date range.
+ *
+ * Query Param Input:
+ * - idSite: The ID of the site to get annotations for. Only one allowed.
+ * - date: The date to get annotations for. If lastN is not supplied, this is the start date,
+ * otherwise the start date in the last period.
+ * - period: The period type.
+ * - lastN: If supplied, the last N # of periods will be included w/ the range specified
+ * by date + period.
+ *
+ * Output:
+ * - HTML displaying annotations for a specific range.
+ *
+ * @param bool $fetch True if the annotation manager should be returned as a string,
+ * false if it should be echo-ed.
+ * @param string $date Override for 'date' query parameter.
+ * @param string $period Override for 'period' query parameter.
+ * @param string $lastN Override for 'lastN' query parameter.
+ * @return string|void
+ */
+ public function getAnnotationManager($fetch = false, $date = false, $period = false, $lastN = false)
+ {
+ $idSite = Piwik_Common::getRequestVar('idSite');
+
+ if ($date === false) {
+ $date = Piwik_Common::getRequestVar('date', false);
+ }
+
+ if ($period === false) {
+ $period = Piwik_Common::getRequestVar('period', 'day');
+ }
+
+ if ($lastN === false) {
+ $lastN = Piwik_Common::getRequestVar('lastN', false);
+ }
+
+ // create & render the view
+ $view = Piwik_View::factory('annotationManager');
+
+ $allAnnotations = Piwik_API_Request::processRequest(
+ 'Annotations.getAll', array('date' => $date, 'period' => $period, 'lastN' => $lastN));
+ $view->annotations = empty($allAnnotations[$idSite]) ? array() : $allAnnotations[$idSite];
+
+ $view->period = $period;
+ $view->lastN = $lastN;
+
+ list($startDate, $endDate) = Piwik_Annotations_API::getDateRangeForPeriod($date, $period, $lastN);
+ $view->startDate = $startDate->toString();
+ $view->endDate = $endDate->toString();
+
+ $dateFormat = Piwik_Translate('CoreHome_ShortDateFormatWithYear');
+ $view->startDatePretty = $startDate->getLocalized($dateFormat);
+ $view->endDatePretty = $endDate->getLocalized($dateFormat);
+
+ $view->canUserAddNotes = Piwik_Annotations_AnnotationList::canUserAddNotesFor($idSite);
+
+ if ($fetch) {
+ return $view->render();
+ } else {
+ echo $view->render();
+ }
+ }
+
+ /**
+ * Controller action that modifies an annotation and returns HTML displaying
+ * the modified annotation.
+ *
+ * Query Param Input:
+ * - idSite: The ID of the site the annotation belongs to. Only one ID is allowed.
+ * - idNote: The ID of the annotation.
+ * - date: The new date value for the annotation. (optional)
+ * - note: The new text for the annotation. (optional)
+ * - starred: Either 1 or 0. Whether the note should be starred or not. (optional)
+ *
+ * Output:
+ * - HTML displaying modified annotation.
+ *
+ * If an optional query param is not supplied, that part of the annotation is
+ * not modified.
+ */
+ public function saveAnnotation()
+ {
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ $this->checkTokenInUrl();
+
+ $view = Piwik_View::factory('annotation');
+
+ // NOTE: permissions checked in API method
+ // save the annotation
+ $view->annotation = Piwik_API_Request::processRequest("Annotations.save");
+
+ echo $view->render();
+ }
+ }
+
+ /**
+ * Controller action that adds a new annotation for a site and returns new
+ * annotation manager HTML for the site and date range.
+ *
+ * Query Param Input:
+ * - idSite: The ID of the site to add an annotation to.
+ * - date: The date for the new annotation.
+ * - note: The text of the annotation.
+ * - starred: Either 1 or 0, whether the annotation should be starred or not.
+ * Defaults to 0.
+ * - managerDate: The date for the annotation manager. If a range is given, the start
+ * date is used for the new annotation.
+ * - managerPeriod: For rendering the annotation manager. @see self::getAnnotationManager
+ * for more info.
+ * - lastN: For rendering the annotation manager. @see self::getAnnotationManager
+ * for more info.
+ * Output:
+ * - @see self::getAnnotationManager
+ */
+ public function addAnnotation()
+ {
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ $this->checkTokenInUrl();
+
+ // the date used is for the annotation manager HTML that gets echo'd. we
+ // use this date for the new annotation, unless it is a date range, in
+ // which case we use the first date of the range.
+ $date = Piwik_Common::getRequestVar('date');
+ if (strpos($date, ',') !== false) {
+ $date = reset(explode(',', $date));
+ }
+
+ // add the annotation. NOTE: permissions checked in API method
+ Piwik_API_Request::processRequest("Annotations.add", array('date' => $date));
+
+ $managerDate = Piwik_Common::getRequestVar('managerDate', false);
+ $managerPeriod = Piwik_Common::getRequestVar('managerPeriod', false);
+ echo $this->getAnnotationManager($fetch = true, $managerDate, $managerPeriod);
+ }
+ }
+
+ /**
+ * Controller action that deletes an annotation and returns new annotation
+ * manager HTML for the site & date range.
+ *
+ * Query Param Input:
+ * - idSite: The ID of the site this annotation belongs to.
+ * - idNote: The ID of the annotation to delete.
+ * - date: For rendering the annotation manager. @see self::getAnnotationManager
+ * for more info.
+ * - period: For rendering the annotation manager. @see self::getAnnotationManager
+ * for more info.
+ * - lastN: For rendering the annotation manager. @see self::getAnnotationManager
+ * for more info.
+ *
+ * Output:
+ * - @see self::getAnnotationManager
+ */
+ public function deleteAnnotation()
+ {
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ $this->checkTokenInUrl();
+
+ // delete annotation. NOTE: permissions checked in API method
+ Piwik_API_Request::processRequest("Annotations.delete");
+
+ echo $this->getAnnotationManager($fetch = true);
+ }
+ }
+
+ /**
+ * Controller action that echo's HTML that displays marker icons for an
+ * evolution graph's x-axis. The marker icons still need to be positioned
+ * by the JavaScript.
+ *
+ * Query Param Input:
+ * - idSite: The ID of the site this annotation belongs to. Only one is allowed.
+ * - date: The date to check for annotations. If lastN is not supplied, this is
+ * the start of the date range used to check for annotations. If supplied,
+ * this is the start of the last period in the date range.
+ * - period: The period type.
+ * - lastN: If supplied, the last N # of periods are included in the date range
+ * used to check for annotations.
+ *
+ * Output:
+ * - HTML that displays marker icons for an evolution graph based on the
+ * number of annotations & starred annotations in the graph's date range.
+ */
+ public function getEvolutionIcons()
+ {
+ // get annotation the count
+ $annotationCounts = Piwik_API_Request::processRequest(
+ "Annotations.getAnnotationCountForDates", array('getAnnotationText' => 1));
+
+ // create & render the view
+ $view = Piwik_View::factory('evolutionAnnotations');
+ $view->annotationCounts = reset($annotationCounts); // only one idSite allowed for this action
+
+ echo $view->render();
+ }
}
diff --git a/plugins/Annotations/templates/annotation.tpl b/plugins/Annotations/templates/annotation.tpl
index e1cd5f6013..cc0d909a3b 100755
--- a/plugins/Annotations/templates/annotation.tpl
+++ b/plugins/Annotations/templates/annotation.tpl
@@ -1,43 +1,46 @@
<tr class="annotation" data-id="{$annotation.idNote}" data-date="{$annotation.date}">
- <td class="annotation-meta">
- <div class="annotation-star{if $annotation.canEditOrDelete} annotation-star-changeable{/if}" data-starred="{$annotation.starred}" {if $annotation.canEditOrDelete}title="{'Annotations_ClickToStarOrUnstar'|translate}"{/if}>
- {if $annotation.starred}
- <img src="themes/default/images/star.png"/>
- {else}
- <img src="themes/default/images/star_empty.png"/>
- {/if}
- </div>
- <div class="annotation-period {if $annotation.canEditOrDelete}annotation-enter-edit-mode{/if}">({$annotation.date})</div>
- {if $annotation.canEditOrDelete}
- <div class="annotation-period-edit" style="display:none">
- <a href="#">{$annotation.date}</a>
- <div class="datepicker" style="display:none"/>
- </div>
- {/if}
- </td>
- <td class="annotation-value">
- <div class="annotation-view-mode">
- <span {if $annotation.canEditOrDelete}title="{'Annotations_ClickToEdit'|translate}" class="annotation-enter-edit-mode"{/if}>{$annotation.note|unescape|escape:'html'}</span>
- {if $annotation.canEditOrDelete}
- <a href="#" class="edit-annotation annotation-enter-edit-mode" title="{'Annotations_ClickToEdit'|translate}">{'General_Edit'|translate}...</a>
- {/if}
- </div>
- {if $annotation.canEditOrDelete}
- <div class="annotation-edit-mode" style="display:none">
- <input class="annotation-edit" type="text" value="{$annotation.note|unescape|escape:'html'}"/>
- <br/>
- <input class="annotation-save submit" type="button" value="{'General_Save'|translate}"/>
- <input class="annotation-cancel submit" type="button" value="{'General_Cancel'|translate}"/>
- </div>
- {/if}
- </td>
- {if isset($annotation.user) && $userLogin != 'anonymous'}
- <td class="annotation-user-cell">
- <span class="annotation-user">{$annotation.user|unescape|escape:'html'}</span><br/>
- {if $annotation.canEditOrDelete}
- <a href="#" class="delete-annotation" style="display:none" title="{'Annotations_ClickToDelete'|translate}">{'General_Delete'|translate}</a>
- {/if}
- </td>
- {/if}
+ <td class="annotation-meta">
+ <div class="annotation-star{if $annotation.canEditOrDelete} annotation-star-changeable{/if}" data-starred="{$annotation.starred}"
+ {if $annotation.canEditOrDelete}title="{'Annotations_ClickToStarOrUnstar'|translate}"{/if}>
+ {if $annotation.starred}
+ <img src="themes/default/images/star.png"/>
+ {else}
+ <img src="themes/default/images/star_empty.png"/>
+ {/if}
+ </div>
+ <div class="annotation-period {if $annotation.canEditOrDelete}annotation-enter-edit-mode{/if}">({$annotation.date})</div>
+ {if $annotation.canEditOrDelete}
+ <div class="annotation-period-edit" style="display:none">
+ <a href="#">{$annotation.date}</a>
+
+ <div class="datepicker" style="display:none"/>
+ </div>
+ {/if}
+ </td>
+ <td class="annotation-value">
+ <div class="annotation-view-mode">
+ <span {if $annotation.canEditOrDelete}title="{'Annotations_ClickToEdit'|translate}"
+ class="annotation-enter-edit-mode"{/if}>{$annotation.note|unescape|escape:'html'}</span>
+ {if $annotation.canEditOrDelete}
+ <a href="#" class="edit-annotation annotation-enter-edit-mode" title="{'Annotations_ClickToEdit'|translate}">{'General_Edit'|translate}...</a>
+ {/if}
+ </div>
+ {if $annotation.canEditOrDelete}
+ <div class="annotation-edit-mode" style="display:none">
+ <input class="annotation-edit" type="text" value="{$annotation.note|unescape|escape:'html'}"/>
+ <br/>
+ <input class="annotation-save submit" type="button" value="{'General_Save'|translate}"/>
+ <input class="annotation-cancel submit" type="button" value="{'General_Cancel'|translate}"/>
+ </div>
+ {/if}
+ </td>
+ {if isset($annotation.user) && $userLogin != 'anonymous'}
+ <td class="annotation-user-cell">
+ <span class="annotation-user">{$annotation.user|unescape|escape:'html'}</span><br/>
+ {if $annotation.canEditOrDelete}
+ <a href="#" class="delete-annotation" style="display:none" title="{'Annotations_ClickToDelete'|translate}">{'General_Delete'|translate}</a>
+ {/if}
+ </td>
+ {/if}
</tr>
diff --git a/plugins/Annotations/templates/annotationManager.tpl b/plugins/Annotations/templates/annotationManager.tpl
index 852c09bbc4..401c2458c9 100755
--- a/plugins/Annotations/templates/annotationManager.tpl
+++ b/plugins/Annotations/templates/annotationManager.tpl
@@ -1,27 +1,27 @@
<div class="annotation-manager"
- {if $startDate neq $endDate}data-date="{$startDate},{$endDate}" data-period="range"
- {else}data-date="{$startDate}" data-period="{$period}"
- {/if}>
+ {if $startDate neq $endDate}data-date="{$startDate},{$endDate}" data-period="range"
+ {else}data-date="{$startDate}" data-period="{$period}"
+ {/if}>
-<div class="annotations-header">
- <span>{'Annotations_Annotations'|translate}</span>
-</div>
+ <div class="annotations-header">
+ <span>{'Annotations_Annotations'|translate}</span>
+ </div>
-<div class="annotation-list-range">{$startDatePretty}{if $startDate neq $endDate} &mdash; {$endDatePretty}{/if}</div>
+ <div class="annotation-list-range">{$startDatePretty}{if $startDate neq $endDate} &mdash; {$endDatePretty}{/if}</div>
-<div class="annotation-list">
-{include file="Annotations/templates/annotations.tpl"}
+ <div class="annotation-list">
+ {include file="Annotations/templates/annotations.tpl"}
-<span class="loadingPiwik" style="display:none"><img src="themes/default/images/loading-blue.gif"/>{'General_Loading_js'|translate}</span>
+ <span class="loadingPiwik" style="display:none"><img src="themes/default/images/loading-blue.gif"/>{'General_Loading_js'|translate}</span>
-</div>
+ </div>
-<div class="annotation-controls">
- {if $canUserAddNotes}
- <a href="#" class="add-annotation" title="{'Annotations_CreateNewAnnotation'|translate}">{'Annotations_CreateNewAnnotation'|translate}</a>
- {elseif $userLogin eq 'anonymous'}
- <a href="index.php?module=Login">{'Annotations_LoginToAnnotate'|translate}</a>
- {/if}
-</div>
+ <div class="annotation-controls">
+ {if $canUserAddNotes}
+ <a href="#" class="add-annotation" title="{'Annotations_CreateNewAnnotation'|translate}">{'Annotations_CreateNewAnnotation'|translate}</a>
+ {elseif $userLogin eq 'anonymous'}
+ <a href="index.php?module=Login">{'Annotations_LoginToAnnotate'|translate}</a>
+ {/if}
+ </div>
</div>
diff --git a/plugins/Annotations/templates/annotations.js b/plugins/Annotations/templates/annotations.js
index 62f63a59aa..85097ad1d6 100755
--- a/plugins/Annotations/templates/annotations.js
+++ b/plugins/Annotations/templates/annotations.js
@@ -5,628 +5,586 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function($, piwik) {
-
-var annotationsApi = {
-
- // calls Annotations.getAnnotationManager
- getAnnotationManager: function(idSite, date, period, lastN, callback)
- {
- var ajaxParams =
- {
- module: 'Annotations',
- action: 'getAnnotationManager',
- idSite: idSite,
- date: date,
- period: period
- };
- if (lastN)
- {
- ajaxParams.lastN = lastN;
- }
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams(ajaxParams, 'get');
- ajaxRequest.setCallback(callback);
- ajaxRequest.setFormat('html');
- ajaxRequest.send(false);
- },
-
- // calls Annotations.addAnnotation
- addAnnotation: function(idSite, managerDate, managerPeriod, date, note, callback)
- {
- var ajaxParams =
- {
- module: 'Annotations',
- action: 'addAnnotation',
- idSite: idSite,
- date: date,
- managerDate: managerDate,
- managerPeriod: managerPeriod,
- note: note
- };
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams(ajaxParams, 'get');
- ajaxRequest.addParams({token_auth: piwik.token_auth}, 'post');
- ajaxRequest.setCallback(callback);
- ajaxRequest.setFormat('html');
- ajaxRequest.send(false);
- },
-
- // calls Annotations.saveAnnotation
- saveAnnotation: function(idSite, idNote, date, noteData, callback)
- {
- var ajaxParams =
- {
- module: 'Annotations',
- action: 'saveAnnotation',
- idSite: idSite,
- idNote: idNote,
- date: date
- };
-
- for (var key in noteData)
- {
- ajaxParams[key] = noteData[key];
- }
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams(ajaxParams, 'get');
- ajaxRequest.addParams({token_auth: piwik.token_auth}, 'post');
- ajaxRequest.setCallback(callback);
- ajaxRequest.setFormat('html');
- ajaxRequest.send(false);
- },
-
- // calls Annotations.deleteAnnotation
- deleteAnnotation: function(idSite, idNote, managerDate, managerPeriod, callback)
- {
- var ajaxParams =
- {
- module: 'Annotations',
- action: 'deleteAnnotation',
- idSite: idSite,
- idNote: idNote,
- date: managerDate,
- period: managerPeriod
- };
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams(ajaxParams, 'get');
- ajaxRequest.addParams({token_auth: piwik.token_auth}, 'post');
- ajaxRequest.setCallback(callback);
- ajaxRequest.setFormat('html');
- ajaxRequest.send(false);
- },
-
- // calls Annotations.getEvolutionIcons
- getEvolutionIcons: function(idSite, date, period, lastN, callback)
- {
- var ajaxParams =
- {
- module: 'Annotations',
- action: 'getEvolutionIcons',
- idSite: idSite,
- date: date,
- period: period
- };
- if (lastN)
- {
- ajaxParams.lastN = lastN;
- }
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams(ajaxParams, 'get');
- ajaxRequest.setFormat('html');
- ajaxRequest.setCallback(callback);
- ajaxRequest.send(false);
- }
-};
-
-var today = new Date();
-
-/**
- * Returns options to configure an annotation's datepicker shown in edit mode.
- *
- * @param {Element} annotation The annotation element.
- */
-var getDatePickerOptions = function(annotation)
-{
- var annotationDateStr = annotation.attr('data-date'),
- parts = annotationDateStr.split('-'),
- annotationDate = new Date(parts[0], parts[1] - 1, parts[2]);
-
- var result = piwik.getBaseDatePickerOptions(annotationDate);
-
- // make sure days before site start & after today cannot be selected
- var piwikMinDate = result.minDate;
- result.beforeShowDay = function (date)
- {
- var valid = true;
-
- // if date is after today or before date of site creation, it cannot be selected
- if (date > today
- || date < piwikMinDate)
- {
- valid = false;
- }
-
- return [valid, ''];
- };
-
- // on select a date, change the text of the edit date link
- result.onSelect = function (dateText)
- {
- $('.annotation-period-edit>a', annotation).text(dateText);
- $('.datepicker', annotation).hide();
- };
-
- return result;
-};
-
-/**
- * Switches the current mode of an annotation between the view/edit modes.
- *
- * @param {Element} inAnnotationElement An element within the annotation to toggle the mode of.
- * Should be two levels nested in the .annotation-value
- * element.
- * @return {Element} The .annotation-value element.
- */
-var toggleAnnotationMode = function(inAnnotationElement)
-{
- var annotation = $(inAnnotationElement).closest('.annotation');
- $('.annotation-period,.annotation-period-edit,.delete-annotation,' +
- '.annotation-edit-mode,.annotation-view-mode', annotation).toggle();
-
- return $(inAnnotationElement).find('.annotation-value');
-};
-
-/**
- * Creates the datepicker for an annotation element.
- *
- * @param {Element} annotation The annotation element.
- */
-var createDatePicker = function ( annotation )
-{
- $('.datepicker', annotation).datepicker(getDatePickerOptions(annotation)).hide();
-};
-
-/**
- * Creates datepickers for every period edit in an annotation manager.
- *
- * @param {Element} manager The annotation manager element.
- */
-var createDatePickers = function ( manager )
-{
- $('.annotation-period-edit', manager).each(function() {
- createDatePicker($(this).parent().parent());
- });
-}
-
-/**
- * Replaces the HTML of an annotation manager element, and resets date/period
- * attributes.
- *
- * @param {Element} manager The annotation manager.
- * @param {string} tml The HTML of the new annotation manager.
- */
-var replaceAnnotationManager = function(manager, html)
-{
- var newManager = $(html);
- manager.html(newManager.html())
- .attr('data-date', newManager.attr('data-date'))
- .attr('data-period', newManager.attr('data-period'));
- createDatePickers(manager);
-};
-
-/**
- * Returns true if an annotation element is starred, false if otherwise.
- *
- * @param {Element} annotation The annotation element.
- * @return {bool}
- */
-var isAnnotationStarred = function(annotation)
-{
- return +$('.annotation-star', annotation).attr('data-starred') == 1 ? true : false;
-};
-
-/**
- * Replaces the HTML of an annotation element with HTML returned from Piwik, and
- * makes sure the data attributes are correct.
- *
- * @param {Element} annotation The annotation element.
- * @param {string} html The replacement HTML (or alternatively, the replacement
- * element/jQuery object).
- */
-var replaceAnnotationHtml = function ( annotation, html )
-{
- var newHtml = $(html);
- annotation.html(newHtml.html()).attr('data-date', newHtml.attr('data-date'));
- createDatePicker(annotation);
-}
-
-/**
- * Binds events to an annotation manager element.
- *
- * @param {Element} manager The annotation manager.
- * @param {int} idSite The site ID the manager is showing annotations for.
- * @param {function} onAnnotationCountChange Callback that is called when there is a change
- * in the number of annotations and/or starred annotations,
- * eg, when a user adds a new one or deletes an existing one.
- */
-var bindAnnotationManagerEvents = function(manager, idSite, onAnnotationCountChange)
-{
- if (!onAnnotationCountChange)
- {
- onAnnotationCountChange = function() {};
- }
-
- // show new annotation row if create new annotation link is clicked
- manager.on('click', '.add-annotation', function(e) {
- e.preventDefault();
-
- $('.new-annotation-row', manager).show();
- $(this).hide();
-
- return false;
- });
-
- // hide new annotation row if cancel button clicked
- manager.on('click', '.new-annotation-cancel', function() {
- var newAnnotationRow = $(this).parent().parent();
- newAnnotationRow.hide();
-
- $('.add-annotation', newAnnotationRow.closest('.annotation-manager')).show();
- });
-
- // save new annotation when new annotation row save is clicked
- manager.on('click', '.new-annotation-save', function() {
- var addRow = $(this).parent().parent(),
- addNoteInput = addRow.find('.new-annotation-edit'),
- noteDate = addRow.find('.annotation-period-edit>a').text();
-
- // do nothing if input is empty
- if (!addNoteInput.val())
- {
- return;
- }
-
- // disable input & link
- addNoteInput.attr('disabled', 'disabled');
- $(this).attr('disabled', 'disabled');
-
- // add a new annotation for the site, date & period
- annotationsApi.addAnnotation(
- idSite,
- manager.attr('data-date'),
- manager.attr('data-period'),
- noteDate,
- addNoteInput.val(),
- function(response) {
- replaceAnnotationManager(manager, response);
-
- // increment annotation count for this date
- onAnnotationCountChange(noteDate, 1, 0);
- }
- );
- });
-
- // add new annotation when enter key pressed on new annotation input
- manager.on('keypress', '.new-annotation-edit', function(e) {
- if (e.which == 13)
- {
- $(this).parent().find('.new-annotation-save').click();
- }
- });
-
- // show annotation editor if edit link, annotation text or period text is clicked
- manager.on('click', '.annotation-enter-edit-mode', function(e) {
- e.preventDefault();
-
- var annotationContent = toggleAnnotationMode(this);
- annotationContent.find('.annotation-edit').focus();
-
- return false;
- });
-
- // hide annotation editor if cancel button is clicked
- manager.on('click', '.annotation-cancel', function() {
- toggleAnnotationMode(this);
- });
-
- // save annotation if save button clicked
- manager.on('click', '.annotation-edit-mode .annotation-save', function() {
- var annotation = $(this).parent().parent().parent(),
- input = $('.annotation-edit', annotation),
- dateEditText = $('.annotation-period-edit>a', annotation).text();
-
- // if annotation value/date has not changed, just show the view mode instead of edit
- if (input[0].defaultValue == input.val()
- && dateEditText == annotation.attr('data-date'))
- {
- toggleAnnotationMode(this);
- return;
- }
-
- // disable input while ajax is happening
- input.attr('disabled', 'disabled');
- $(this).attr('disabled', 'disabled');
-
- // save the note w/ the new note text & date
- annotationsApi.saveAnnotation(
- idSite,
- annotation.attr('data-id'),
- dateEditText,
- {
- note: input.val()
- },
- function(response) {
- response = $(response);
-
- var newDate = response.attr('data-date'),
- isStarred = isAnnotationStarred(response),
- originalDate = annotation.attr('data-date');
-
- replaceAnnotationHtml(annotation, response);
-
- // if the date has been changed, update the evolution icon counts to reflect the change
- if (originalDate != newDate)
- {
- // reduce count for original date
- onAnnotationCountChange(originalDate, -1, isStarred ? -1 : 0);
-
- // increase count for new date
- onAnnotationCountChange(newDate, 1, isStarred ? 1 : 0);
- }
- }
- );
- });
-
- // save annotation if 'enter' pressed on input
- manager.on('keypress', '.annotation-value input', function(e) {
- if (e.which == 13)
- {
- $(this).parent().find('.annotation-save').click();
- }
- });
-
- // delete annotation if delete link clicked
- manager.on('click', '.delete-annotation', function(e) {
- e.preventDefault();
-
- var annotation = $(this).parent().parent();
- $(this).attr('disabled', 'disabled');
-
- // delete annotation by ajax
- annotationsApi.deleteAnnotation(
- idSite,
- annotation.attr('data-id'),
- manager.attr('data-date'),
- manager.attr('data-period'),
- function (response) {
- replaceAnnotationManager(manager, response);
-
- // update evolution icons
- var isStarred = isAnnotationStarred(annotation);
- onAnnotationCountChange(annotation.attr('data-date'), -1, isStarred ? -1 : 0);
- }
- );
-
- return false;
- });
-
- // star/unstar annotation if star clicked
- manager.on('click', '.annotation-star-changeable', function(e) {
- var annotation = $(this).parent().parent(),
- newStarredVal = $(this).attr('data-starred') == 0 ? 1 : 0 // flip existing 'starred' value
- ;
-
- // perform ajax request to star annotation
- annotationsApi.saveAnnotation(
- idSite,
- annotation.attr('data-id'),
- annotation.attr('data-date'),
- {
- starred: newStarredVal
- },
- function (response) {
- replaceAnnotationHtml(annotation, response);
-
- // change starred count for this annotation in evolution graph based on what we're
- // changing the starred value to
- onAnnotationCountChange(annotation.attr('data-date'), 0, newStarredVal == 0 ? -1 : 1);
- }
- );
- });
-
- // when period edit is clicked, show datepicker
- manager.on('click', '.annotation-period-edit>a', function(e) {
- e.preventDefault();
- $('.datepicker', $(this).parent()).toggle();
- return false;
- });
-
- // make sure datepicker popups are closed if someone clicks elsewhere
- $('body').on('mouseup', function(e) {
- var container = $('.annotation-period-edit>.datepicker:visible').parent();
-
- if (!container.has(e.target).length)
- {
- container.find('.datepicker').hide();
- }
- });
-};
+(function ($, piwik) {
+
+ var annotationsApi = {
+
+ // calls Annotations.getAnnotationManager
+ getAnnotationManager: function (idSite, date, period, lastN, callback) {
+ var ajaxParams =
+ {
+ module: 'Annotations',
+ action: 'getAnnotationManager',
+ idSite: idSite,
+ date: date,
+ period: period
+ };
+ if (lastN) {
+ ajaxParams.lastN = lastN;
+ }
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams(ajaxParams, 'get');
+ ajaxRequest.setCallback(callback);
+ ajaxRequest.setFormat('html');
+ ajaxRequest.send(false);
+ },
+
+ // calls Annotations.addAnnotation
+ addAnnotation: function (idSite, managerDate, managerPeriod, date, note, callback) {
+ var ajaxParams =
+ {
+ module: 'Annotations',
+ action: 'addAnnotation',
+ idSite: idSite,
+ date: date,
+ managerDate: managerDate,
+ managerPeriod: managerPeriod,
+ note: note
+ };
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams(ajaxParams, 'get');
+ ajaxRequest.addParams({token_auth: piwik.token_auth}, 'post');
+ ajaxRequest.setCallback(callback);
+ ajaxRequest.setFormat('html');
+ ajaxRequest.send(false);
+ },
+
+ // calls Annotations.saveAnnotation
+ saveAnnotation: function (idSite, idNote, date, noteData, callback) {
+ var ajaxParams =
+ {
+ module: 'Annotations',
+ action: 'saveAnnotation',
+ idSite: idSite,
+ idNote: idNote,
+ date: date
+ };
+
+ for (var key in noteData) {
+ ajaxParams[key] = noteData[key];
+ }
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams(ajaxParams, 'get');
+ ajaxRequest.addParams({token_auth: piwik.token_auth}, 'post');
+ ajaxRequest.setCallback(callback);
+ ajaxRequest.setFormat('html');
+ ajaxRequest.send(false);
+ },
+
+ // calls Annotations.deleteAnnotation
+ deleteAnnotation: function (idSite, idNote, managerDate, managerPeriod, callback) {
+ var ajaxParams =
+ {
+ module: 'Annotations',
+ action: 'deleteAnnotation',
+ idSite: idSite,
+ idNote: idNote,
+ date: managerDate,
+ period: managerPeriod
+ };
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams(ajaxParams, 'get');
+ ajaxRequest.addParams({token_auth: piwik.token_auth}, 'post');
+ ajaxRequest.setCallback(callback);
+ ajaxRequest.setFormat('html');
+ ajaxRequest.send(false);
+ },
+
+ // calls Annotations.getEvolutionIcons
+ getEvolutionIcons: function (idSite, date, period, lastN, callback) {
+ var ajaxParams =
+ {
+ module: 'Annotations',
+ action: 'getEvolutionIcons',
+ idSite: idSite,
+ date: date,
+ period: period
+ };
+ if (lastN) {
+ ajaxParams.lastN = lastN;
+ }
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams(ajaxParams, 'get');
+ ajaxRequest.setFormat('html');
+ ajaxRequest.setCallback(callback);
+ ajaxRequest.send(false);
+ }
+ };
+
+ var today = new Date();
+
+ /**
+ * Returns options to configure an annotation's datepicker shown in edit mode.
+ *
+ * @param {Element} annotation The annotation element.
+ */
+ var getDatePickerOptions = function (annotation) {
+ var annotationDateStr = annotation.attr('data-date'),
+ parts = annotationDateStr.split('-'),
+ annotationDate = new Date(parts[0], parts[1] - 1, parts[2]);
+
+ var result = piwik.getBaseDatePickerOptions(annotationDate);
+
+ // make sure days before site start & after today cannot be selected
+ var piwikMinDate = result.minDate;
+ result.beforeShowDay = function (date) {
+ var valid = true;
+
+ // if date is after today or before date of site creation, it cannot be selected
+ if (date > today
+ || date < piwikMinDate) {
+ valid = false;
+ }
+
+ return [valid, ''];
+ };
+
+ // on select a date, change the text of the edit date link
+ result.onSelect = function (dateText) {
+ $('.annotation-period-edit>a', annotation).text(dateText);
+ $('.datepicker', annotation).hide();
+ };
+
+ return result;
+ };
+
+ /**
+ * Switches the current mode of an annotation between the view/edit modes.
+ *
+ * @param {Element} inAnnotationElement An element within the annotation to toggle the mode of.
+ * Should be two levels nested in the .annotation-value
+ * element.
+ * @return {Element} The .annotation-value element.
+ */
+ var toggleAnnotationMode = function (inAnnotationElement) {
+ var annotation = $(inAnnotationElement).closest('.annotation');
+ $('.annotation-period,.annotation-period-edit,.delete-annotation,' +
+ '.annotation-edit-mode,.annotation-view-mode', annotation).toggle();
+
+ return $(inAnnotationElement).find('.annotation-value');
+ };
+
+ /**
+ * Creates the datepicker for an annotation element.
+ *
+ * @param {Element} annotation The annotation element.
+ */
+ var createDatePicker = function (annotation) {
+ $('.datepicker', annotation).datepicker(getDatePickerOptions(annotation)).hide();
+ };
+
+ /**
+ * Creates datepickers for every period edit in an annotation manager.
+ *
+ * @param {Element} manager The annotation manager element.
+ */
+ var createDatePickers = function (manager) {
+ $('.annotation-period-edit', manager).each(function () {
+ createDatePicker($(this).parent().parent());
+ });
+ }
+
+ /**
+ * Replaces the HTML of an annotation manager element, and resets date/period
+ * attributes.
+ *
+ * @param {Element} manager The annotation manager.
+ * @param {string} tml The HTML of the new annotation manager.
+ */
+ var replaceAnnotationManager = function (manager, html) {
+ var newManager = $(html);
+ manager.html(newManager.html())
+ .attr('data-date', newManager.attr('data-date'))
+ .attr('data-period', newManager.attr('data-period'));
+ createDatePickers(manager);
+ };
+
+ /**
+ * Returns true if an annotation element is starred, false if otherwise.
+ *
+ * @param {Element} annotation The annotation element.
+ * @return {bool}
+ */
+ var isAnnotationStarred = function (annotation) {
+ return +$('.annotation-star', annotation).attr('data-starred') == 1 ? true : false;
+ };
+
+ /**
+ * Replaces the HTML of an annotation element with HTML returned from Piwik, and
+ * makes sure the data attributes are correct.
+ *
+ * @param {Element} annotation The annotation element.
+ * @param {string} html The replacement HTML (or alternatively, the replacement
+ * element/jQuery object).
+ */
+ var replaceAnnotationHtml = function (annotation, html) {
+ var newHtml = $(html);
+ annotation.html(newHtml.html()).attr('data-date', newHtml.attr('data-date'));
+ createDatePicker(annotation);
+ }
+
+ /**
+ * Binds events to an annotation manager element.
+ *
+ * @param {Element} manager The annotation manager.
+ * @param {int} idSite The site ID the manager is showing annotations for.
+ * @param {function} onAnnotationCountChange Callback that is called when there is a change
+ * in the number of annotations and/or starred annotations,
+ * eg, when a user adds a new one or deletes an existing one.
+ */
+ var bindAnnotationManagerEvents = function (manager, idSite, onAnnotationCountChange) {
+ if (!onAnnotationCountChange) {
+ onAnnotationCountChange = function () {};
+ }
+
+ // show new annotation row if create new annotation link is clicked
+ manager.on('click', '.add-annotation', function (e) {
+ e.preventDefault();
+
+ $('.new-annotation-row', manager).show();
+ $(this).hide();
+
+ return false;
+ });
+
+ // hide new annotation row if cancel button clicked
+ manager.on('click', '.new-annotation-cancel', function () {
+ var newAnnotationRow = $(this).parent().parent();
+ newAnnotationRow.hide();
+
+ $('.add-annotation', newAnnotationRow.closest('.annotation-manager')).show();
+ });
+
+ // save new annotation when new annotation row save is clicked
+ manager.on('click', '.new-annotation-save', function () {
+ var addRow = $(this).parent().parent(),
+ addNoteInput = addRow.find('.new-annotation-edit'),
+ noteDate = addRow.find('.annotation-period-edit>a').text();
+
+ // do nothing if input is empty
+ if (!addNoteInput.val()) {
+ return;
+ }
+
+ // disable input & link
+ addNoteInput.attr('disabled', 'disabled');
+ $(this).attr('disabled', 'disabled');
+
+ // add a new annotation for the site, date & period
+ annotationsApi.addAnnotation(
+ idSite,
+ manager.attr('data-date'),
+ manager.attr('data-period'),
+ noteDate,
+ addNoteInput.val(),
+ function (response) {
+ replaceAnnotationManager(manager, response);
+
+ // increment annotation count for this date
+ onAnnotationCountChange(noteDate, 1, 0);
+ }
+ );
+ });
+
+ // add new annotation when enter key pressed on new annotation input
+ manager.on('keypress', '.new-annotation-edit', function (e) {
+ if (e.which == 13) {
+ $(this).parent().find('.new-annotation-save').click();
+ }
+ });
+
+ // show annotation editor if edit link, annotation text or period text is clicked
+ manager.on('click', '.annotation-enter-edit-mode', function (e) {
+ e.preventDefault();
+
+ var annotationContent = toggleAnnotationMode(this);
+ annotationContent.find('.annotation-edit').focus();
+
+ return false;
+ });
+
+ // hide annotation editor if cancel button is clicked
+ manager.on('click', '.annotation-cancel', function () {
+ toggleAnnotationMode(this);
+ });
+
+ // save annotation if save button clicked
+ manager.on('click', '.annotation-edit-mode .annotation-save', function () {
+ var annotation = $(this).parent().parent().parent(),
+ input = $('.annotation-edit', annotation),
+ dateEditText = $('.annotation-period-edit>a', annotation).text();
+
+ // if annotation value/date has not changed, just show the view mode instead of edit
+ if (input[0].defaultValue == input.val()
+ && dateEditText == annotation.attr('data-date')) {
+ toggleAnnotationMode(this);
+ return;
+ }
+
+ // disable input while ajax is happening
+ input.attr('disabled', 'disabled');
+ $(this).attr('disabled', 'disabled');
+
+ // save the note w/ the new note text & date
+ annotationsApi.saveAnnotation(
+ idSite,
+ annotation.attr('data-id'),
+ dateEditText,
+ {
+ note: input.val()
+ },
+ function (response) {
+ response = $(response);
+
+ var newDate = response.attr('data-date'),
+ isStarred = isAnnotationStarred(response),
+ originalDate = annotation.attr('data-date');
+
+ replaceAnnotationHtml(annotation, response);
+
+ // if the date has been changed, update the evolution icon counts to reflect the change
+ if (originalDate != newDate) {
+ // reduce count for original date
+ onAnnotationCountChange(originalDate, -1, isStarred ? -1 : 0);
+
+ // increase count for new date
+ onAnnotationCountChange(newDate, 1, isStarred ? 1 : 0);
+ }
+ }
+ );
+ });
+
+ // save annotation if 'enter' pressed on input
+ manager.on('keypress', '.annotation-value input', function (e) {
+ if (e.which == 13) {
+ $(this).parent().find('.annotation-save').click();
+ }
+ });
+
+ // delete annotation if delete link clicked
+ manager.on('click', '.delete-annotation', function (e) {
+ e.preventDefault();
+
+ var annotation = $(this).parent().parent();
+ $(this).attr('disabled', 'disabled');
+
+ // delete annotation by ajax
+ annotationsApi.deleteAnnotation(
+ idSite,
+ annotation.attr('data-id'),
+ manager.attr('data-date'),
+ manager.attr('data-period'),
+ function (response) {
+ replaceAnnotationManager(manager, response);
+
+ // update evolution icons
+ var isStarred = isAnnotationStarred(annotation);
+ onAnnotationCountChange(annotation.attr('data-date'), -1, isStarred ? -1 : 0);
+ }
+ );
+
+ return false;
+ });
+
+ // star/unstar annotation if star clicked
+ manager.on('click', '.annotation-star-changeable', function (e) {
+ var annotation = $(this).parent().parent(),
+ newStarredVal = $(this).attr('data-starred') == 0 ? 1 : 0 // flip existing 'starred' value
+ ;
+
+ // perform ajax request to star annotation
+ annotationsApi.saveAnnotation(
+ idSite,
+ annotation.attr('data-id'),
+ annotation.attr('data-date'),
+ {
+ starred: newStarredVal
+ },
+ function (response) {
+ replaceAnnotationHtml(annotation, response);
+
+ // change starred count for this annotation in evolution graph based on what we're
+ // changing the starred value to
+ onAnnotationCountChange(annotation.attr('data-date'), 0, newStarredVal == 0 ? -1 : 1);
+ }
+ );
+ });
+
+ // when period edit is clicked, show datepicker
+ manager.on('click', '.annotation-period-edit>a', function (e) {
+ e.preventDefault();
+ $('.datepicker', $(this).parent()).toggle();
+ return false;
+ });
+
+ // make sure datepicker popups are closed if someone clicks elsewhere
+ $('body').on('mouseup', function (e) {
+ var container = $('.annotation-period-edit>.datepicker:visible').parent();
+
+ if (!container.has(e.target).length) {
+ container.find('.datepicker').hide();
+ }
+ });
+ };
// used in below function
-var loadingAnnotationManager = false;
-
-/**
- * Shows an annotation manager under a report for a specific site & date range.
- *
- * @param {Element} domElem The element of the report to show the annotation manger
- * under.
- * @param {int} idSite The ID of the site to show the annotations of.
- * @param {string} date The start date of the period.
- * @param {string} period The period type.
- * @param {int} Whether to include the last N periods in the date range or not. Can
- * be undefined.
- */
-var showAnnotationViewer = function(domElem, idSite, date, period, lastN, callback)
-{
- var addToAnnotationCount = function(date, amt, starAmt)
- {
- if (date.indexOf(',') != -1)
- {
- date = date.split(',')[0];
- }
-
- $('.evolution-annotations>span', domElem).each(function() {
- if ($(this).attr('data-date') == date)
- {
- // get counts from attributes (and convert them to ints)
- var starredCount = +$(this).attr('data-starred'),
- annotationCount = +$(this).attr('data-count');
-
- // modify the starred count & make sure the correct image is used
- var newStarCount = starredCount + starAmt;
- if(newStarCount > 0) {
- var newImg = 'themes/default/images/yellow_marker.png';
- } else {
- var newImg = 'themes/default/images/grey_marker.png';
+ var loadingAnnotationManager = false;
+
+ /**
+ * Shows an annotation manager under a report for a specific site & date range.
+ *
+ * @param {Element} domElem The element of the report to show the annotation manger
+ * under.
+ * @param {int} idSite The ID of the site to show the annotations of.
+ * @param {string} date The start date of the period.
+ * @param {string} period The period type.
+ * @param {int} Whether to include the last N periods in the date range or not. Can
+ * be undefined.
+ */
+ var showAnnotationViewer = function (domElem, idSite, date, period, lastN, callback) {
+ var addToAnnotationCount = function (date, amt, starAmt) {
+ if (date.indexOf(',') != -1) {
+ date = date.split(',')[0];
+ }
+
+ $('.evolution-annotations>span', domElem).each(function () {
+ if ($(this).attr('data-date') == date) {
+ // get counts from attributes (and convert them to ints)
+ var starredCount = +$(this).attr('data-starred'),
+ annotationCount = +$(this).attr('data-count');
+
+ // modify the starred count & make sure the correct image is used
+ var newStarCount = starredCount + starAmt;
+ if (newStarCount > 0) {
+ var newImg = 'themes/default/images/yellow_marker.png';
+ } else {
+ var newImg = 'themes/default/images/grey_marker.png';
+ }
+ $(this).attr('data-starred', newStarCount).find('img').attr('src', newImg);
+
+ // modify the annotation count & hide/show based on new count
+ var newCount = annotationCount + amt;
+ $(this).attr('data-count', newCount).css('opacity', newCount > 0 ? 1 : 0);
+
+ return false;
+ }
+ });
+ };
+
+ var manager = $('.annotation-manager', domElem);
+ if (manager.length) {
+ // if annotations for the requested date + period are already loaded, then just toggle the
+ // visibility of the annotation viewer. otherwise, we reload the annotations.
+ if (manager.attr('data-date') == date
+ && manager.attr('data-period') == period) {
+ // toggle manager view
+ if (manager.is(':hidden')) {
+ manager.slideDown('slow', function () { if (callback) callback(manager) });
+ }
+ else {
+ manager.slideUp('slow', function () { if (callback) callback(manager) });
+ }
+ }
+ else {
+ // show nothing but the loading gif
+ $('.annotations', manager).html('');
+ $('.loadingPiwik', manager).show();
+
+ // reload annotation manager for new date/period
+ annotationsApi.getAnnotationManager(idSite, date, period, lastN, function (response) {
+ replaceAnnotationManager(manager, response);
+
+ createDatePickers(manager);
+
+ // show if hidden
+ if (manager.is(':hidden')) {
+ manager.slideDown('slow', function () { if (callback) callback(manager) });
+ }
+ else {
+ if (callback) {
+ callback(manager);
+ }
+ }
+ });
+ }
}
- $(this).attr('data-starred', newStarCount).find('img').attr('src', newImg);
-
- // modify the annotation count & hide/show based on new count
- var newCount = annotationCount + amt;
- $(this).attr('data-count', newCount).css('opacity', newCount > 0 ? 1 : 0);
-
- return false;
- }
- });
- };
-
- var manager = $('.annotation-manager', domElem);
- if (manager.length)
- {
- // if annotations for the requested date + period are already loaded, then just toggle the
- // visibility of the annotation viewer. otherwise, we reload the annotations.
- if (manager.attr('data-date') == date
- && manager.attr('data-period') == period)
- {
- // toggle manager view
- if (manager.is(':hidden'))
- {
- manager.slideDown('slow', function () { if (callback) callback(manager) });
- }
- else
- {
- manager.slideUp('slow', function () { if (callback) callback(manager) });
- }
- }
- else
- {
- // show nothing but the loading gif
- $('.annotations', manager).html('');
- $('.loadingPiwik', manager).show();
-
- // reload annotation manager for new date/period
- annotationsApi.getAnnotationManager(idSite, date, period, lastN, function(response) {
- replaceAnnotationManager(manager, response);
-
- createDatePickers(manager);
-
- // show if hidden
- if (manager.is(':hidden'))
- {
- manager.slideDown('slow', function () { if (callback) callback(manager) });
- }
- else
- {
- if (callback)
- {
- callback(manager);
- }
- }
- });
- }
- }
- else
- {
- // if we are already loading the annotation manager, don't load it again
- if (loadingAnnotationManager)
- {
- return;
- }
-
- loadingAnnotationManager = true;
-
- var loading = $('.loadingPiwikBelow', domElem).css({display: 'block'});
-
- // the annotations for this report have not been retrieved yet, so do an ajax request
- // & show the result
- annotationsApi.getAnnotationManager(idSite, date, period, lastN, function(response) {
- var manager = $(response).hide();
-
- // if an error occurred (and response does not contain the annotation manager), do nothing
- if (!manager.hasClass('annotation-manager'))
- {
- return;
- }
-
- // create datepickers for each shown annotation
- createDatePickers(manager);
-
- bindAnnotationManagerEvents(manager, idSite, addToAnnotationCount);
-
- loading.css('visibility', 'hidden');
-
- // add & show annotation manager
- $('.dataTableFeatures', domElem).append(manager);
- manager.slideDown('slow', function() {
- loading.hide().css('visibility', 'visible');
- loadingAnnotationManager = false;
-
- if (callback) callback(manager)
- });
- });
- }
-};
-
-/**
- * Determines the x-coordinates of a set of evolution annotation icons.
- *
- * @param {Element} annotations The '.evolution-annotations' element.
- * @param {Element} graphElem The evolution graph's datatable element.
- */
-var placeEvolutionIcons = function (annotations, graphElem)
-{
- var canvases = $('.piwik-graph .jqplot-xaxis canvas', graphElem),
- noteSize = 16;
-
- // if no graph available, hide all icons
- if (!canvases || canvases.length == 0) {
- $('span', annotations).hide();
- return true;
- }
-
- // set position of each individual icon
- $('span', annotations).each(function(i) {
- var canvas = $(canvases[i]),
- canvasCenterX = canvas.position().left + (canvas.width() / 2);
- $(this).css({
- left: canvasCenterX - noteSize / 2,
- // show if there are annotations for this x-axis tick
- opacity: +$(this).attr('data-count') > 0 ? 1 : 0
- });
- });
-};
+ else {
+ // if we are already loading the annotation manager, don't load it again
+ if (loadingAnnotationManager) {
+ return;
+ }
+
+ loadingAnnotationManager = true;
+
+ var loading = $('.loadingPiwikBelow', domElem).css({display: 'block'});
+
+ // the annotations for this report have not been retrieved yet, so do an ajax request
+ // & show the result
+ annotationsApi.getAnnotationManager(idSite, date, period, lastN, function (response) {
+ var manager = $(response).hide();
+
+ // if an error occurred (and response does not contain the annotation manager), do nothing
+ if (!manager.hasClass('annotation-manager')) {
+ return;
+ }
+
+ // create datepickers for each shown annotation
+ createDatePickers(manager);
+
+ bindAnnotationManagerEvents(manager, idSite, addToAnnotationCount);
+
+ loading.css('visibility', 'hidden');
+
+ // add & show annotation manager
+ $('.dataTableFeatures', domElem).append(manager);
+ manager.slideDown('slow', function () {
+ loading.hide().css('visibility', 'visible');
+ loadingAnnotationManager = false;
+
+ if (callback) callback(manager)
+ });
+ });
+ }
+ };
+
+ /**
+ * Determines the x-coordinates of a set of evolution annotation icons.
+ *
+ * @param {Element} annotations The '.evolution-annotations' element.
+ * @param {Element} graphElem The evolution graph's datatable element.
+ */
+ var placeEvolutionIcons = function (annotations, graphElem) {
+ var canvases = $('.piwik-graph .jqplot-xaxis canvas', graphElem),
+ noteSize = 16;
+
+ // if no graph available, hide all icons
+ if (!canvases || canvases.length == 0) {
+ $('span', annotations).hide();
+ return true;
+ }
+
+ // set position of each individual icon
+ $('span', annotations).each(function (i) {
+ var canvas = $(canvases[i]),
+ canvasCenterX = canvas.position().left + (canvas.width() / 2);
+ $(this).css({
+ left: canvasCenterX - noteSize / 2,
+ // show if there are annotations for this x-axis tick
+ opacity: +$(this).attr('data-count') > 0 ? 1 : 0
+ });
+ });
+ };
// make showAnnotationViewer, placeEvolutionIcons & annotationsApi globally accessible
-piwik.annotations = {
- showAnnotationViewer: showAnnotationViewer,
- placeEvolutionIcons: placeEvolutionIcons,
- api: annotationsApi
-};
+ piwik.annotations = {
+ showAnnotationViewer: showAnnotationViewer,
+ placeEvolutionIcons: placeEvolutionIcons,
+ api: annotationsApi
+ };
}(jQuery, piwik));
diff --git a/plugins/Annotations/templates/annotations.tpl b/plugins/Annotations/templates/annotations.tpl
index f15750e548..26c1f708f1 100755
--- a/plugins/Annotations/templates/annotations.tpl
+++ b/plugins/Annotations/templates/annotations.tpl
@@ -1,30 +1,29 @@
<div class="annotations">
-{if empty($annotations)}
+ {if empty($annotations)}
+ <div class="empty-annotation-list">{'Annotations_NoAnnotations'|translate}</div>
+ {/if}
-<div class="empty-annotation-list">{'Annotations_NoAnnotations'|translate}</div>
+ <table>
+ {foreach from=$annotations item=annotation}
+ {include file="Annotations/templates/annotation.tpl"}
+ {/foreach}
+ <tr class="new-annotation-row" style="display:none" data-date="{$startDate}">
+ <td class="annotation-meta">
+ <div class="annotation-star">&nbsp;</div>
+ <div class="annotation-period-edit">
+ <a href="#">{$startDate}</a>
-{/if}
-
-<table>
-{foreach from=$annotations item=annotation}
-{include file="Annotations/templates/annotation.tpl"}
-{/foreach}
-<tr class="new-annotation-row" style="display:none" data-date="{$startDate}">
- <td class="annotation-meta">
- <div class="annotation-star">&nbsp;</div>
- <div class="annotation-period-edit">
- <a href="#">{$startDate}</a>
- <div class="datepicker" style="display:none"/>
- </div>
- </td>
- <td class="annotation-value">
- <input type="text" value="" class="new-annotation-edit" placeholder="{'Annotations_EnterAnnotationText'|translate}"/><br/>
- <input type="button" class="submit new-annotation-save" value="{'General_Save'|translate}"/>
- <input type="button" class="submit new-annotation-cancel" value="{'General_Cancel'|translate}"/>
- </td>
- <td class="annotation-user-cell"><span class="annotation-user">{$userLogin}</span></td>
-</tr>
-</table>
+ <div class="datepicker" style="display:none"/>
+ </div>
+ </td>
+ <td class="annotation-value">
+ <input type="text" value="" class="new-annotation-edit" placeholder="{'Annotations_EnterAnnotationText'|translate}"/><br/>
+ <input type="button" class="submit new-annotation-save" value="{'General_Save'|translate}"/>
+ <input type="button" class="submit new-annotation-cancel" value="{'General_Cancel'|translate}"/>
+ </td>
+ <td class="annotation-user-cell"><span class="annotation-user">{$userLogin}</span></td>
+ </tr>
+ </table>
</div>
diff --git a/plugins/Annotations/templates/evolutionAnnotations.tpl b/plugins/Annotations/templates/evolutionAnnotations.tpl
index 8da13d35f0..eaf6f9d9d3 100755
--- a/plugins/Annotations/templates/evolutionAnnotations.tpl
+++ b/plugins/Annotations/templates/evolutionAnnotations.tpl
@@ -1,15 +1,15 @@
<div class="evolution-annotations">
-{foreach from=$annotationCounts item=dateCountPair}
- {assign var=date value=$dateCountPair[0]}
- {assign var=counts value=$dateCountPair[1]}
- <span data-date="{$date}" data-count="{$counts.count}" data-starred="{$counts.starred}"
- {if $counts.count eq 0}title="{'Annotations_AddAnnotationsFor_js'|translate:$date}"
- {elseif $counts.count eq 1}title="{'Annotations_AnnotationOnDate'|translate:$date:$counts.note}
+ {foreach from=$annotationCounts item=dateCountPair}
+ {assign var=date value=$dateCountPair[0]}
+ {assign var=counts value=$dateCountPair[1]}
+ <span data-date="{$date}" data-count="{$counts.count}" data-starred="{$counts.starred}"
+ {if $counts.count eq 0}title="{'Annotations_AddAnnotationsFor_js'|translate:$date}"
+ {elseif $counts.count eq 1}title="{'Annotations_AnnotationOnDate'|translate:$date:$counts.note}
{'Annotations_ClickToEditOrAdd'|translate}"
- {else}title="{'Annotations_ViewAndAddAnnotations_js'|translate:$date}"
- {/if}>
+ {else}title="{'Annotations_ViewAndAddAnnotations_js'|translate:$date}"
+ {/if}>
<img src="themes/default/images/{if $counts.starred > 0}yellow_marker.png{else}grey_marker.png{/if}" width="16" height="16"/>
</span>
-{/foreach}
+ {/foreach}
</div>
diff --git a/plugins/Annotations/templates/styles.css b/plugins/Annotations/templates/styles.css
index 4831bafcb1..089a8ef6c3 100755
--- a/plugins/Annotations/templates/styles.css
+++ b/plugins/Annotations/templates/styles.css
@@ -1,194 +1,198 @@
.evolution-annotations {
- position: relative;
- height: 16px;
- width: 100%;
- margin-top: 12px;
- margin-bottom: -28px;
- cursor: pointer;
+ position: relative;
+ height: 16px;
+ width: 100%;
+ margin-top: 12px;
+ margin-bottom: -28px;
+ cursor: pointer;
}
.evolution-annotations > span {
- position: absolute;
+ position: absolute;
}
.annotation-manager {
- text-align: left;
- margin-top: -18px;
+ text-align: left;
+ margin-top: -18px;
}
.annotations-header {
- display: inline-block;
- width: 128px;
- text-align: right;
- font-size: 12px;
- font-style: italic;
- margin-bottom: 8px;
- vertical-align: top;
- color: #666;
+ display: inline-block;
+ width: 128px;
+ text-align: right;
+ font-size: 12px;
+ font-style: italic;
+ margin-bottom: 8px;
+ vertical-align: top;
+ color: #666;
}
.annotation-controls {
- display:inline-block;
- margin-left: 132px;
+ display: inline-block;
+ margin-left: 132px;
}
.annotation-controls>a {
- font-size: 11px;
- font-style: italic;
- color: #666;
- cursor:pointer;
- padding:3px 0 6px 0;
- display:inline-block;
+ font-size: 11px;
+ font-style: italic;
+ color: #666;
+ cursor: pointer;
+ padding: 3px 0 6px 0;
+ display: inline-block;
}
.annotation-controls>a:hover {
- text-decoration:none;
+ text-decoration: none;
}
.annotation-list {
- margin-left: 8px;
+ margin-left: 8px;
}
.annotation-list table {
- width: 100%;
+ width: 100%;
}
.annotation-list-range {
- display: inline-block;
- font-size: 12px;
- font-style: italic;
- color: #666;
- vertical-align: top;
- margin: 0 0 8px 8px;
+ display: inline-block;
+ font-size: 12px;
+ font-style: italic;
+ color: #666;
+ vertical-align: top;
+ margin: 0 0 8px 8px;
}
-.empty-annotation-list,.annotation-list .loadingPiwik {
- display: block;
-
- font-style: italic;
- color: #666;
- margin: 0 0 12px 140px;
+.empty-annotation-list, .annotation-list .loadingPiwik {
+ display: block;
+
+ font-style: italic;
+ color: #666;
+ margin: 0 0 12px 140px;
}
.annotation-meta {
- width: 128px;
- text-align: right;
- vertical-align: top;
- font-size:14px;
+ width: 128px;
+ text-align: right;
+ vertical-align: top;
+ font-size: 14px;
}
.annotation-user {
- font-style: italic;
- font-size: 11px;
- color:#444;
+ font-style: italic;
+ font-size: 11px;
+ color: #444;
}
.annotation-user-cell {
- vertical-align: top;
- width: 92px;
+ vertical-align: top;
+ width: 92px;
}
.annotation-period {
- display:inline-block;
- font-style: italic;
- margin: 0 8px 8px 8px;
- vertical-align: top;
+ display: inline-block;
+ font-style: italic;
+ margin: 0 8px 8px 8px;
+ vertical-align: top;
}
.annotation-value {
- margin: 0 12px 12px 8px;
- vertical-align: top;
- position: relative;
- font-size:14px;
+ margin: 0 12px 12px 8px;
+ vertical-align: top;
+ position: relative;
+ font-size: 14px;
}
.annotation-enter-edit-mode {
- cursor: pointer;
+ cursor: pointer;
}
-.annotation-edit,.new-annotation-edit {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- width:98%;
+.annotation-edit, .new-annotation-edit {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 98%;
}
.annotation-star {
- display: inline-block;
- margin: 0 8px 8px 0;
- width: 16px;
+ display: inline-block;
+ margin: 0 8px 8px 0;
+ width: 16px;
}
.annotation-star-changeable {
- cursor: pointer;
+ cursor: pointer;
}
.delete-annotation {
- font-size:12px;
- font-style: italic;
- color: red;
- text-decoration: none;
- display: inline-block;
+ font-size: 12px;
+ font-style: italic;
+ color: red;
+ text-decoration: none;
+ display: inline-block;
}
.delete-annotation:hover {
- text-decoration: underline;
+ text-decoration: underline;
}
.annotation-manager .submit {
- float:none;
+ float: none;
}
.edit-annotation {
- font-size:10px;
- color:#666;
- font-style:italic;
+ font-size: 10px;
+ color: #666;
+ font-style: italic;
}
+
.edit-annotation:hover {
- text-decoration:none;
+ text-decoration: none;
}
.annotationView {
- float: right;
+ float: right;
margin-left: 5px;
position: relative;
- cursor:pointer;
+ cursor: pointer;
}
.annotationView > span {
- font-style: italic;
- display: inline-block;
- margin: 4px 4px 0 4px;
+ font-style: italic;
+ display: inline-block;
+ margin: 4px 4px 0 4px;
}
.annotation-period-edit {
- display:inline-block;
- background:white;
- color:#444;
- font-size:12px;
- border: 1px solid #e4e5e4;
- padding:5px 5px 6px 3px;
- border-radius:4px;
- -moz-border-radius:4px;
- -webkit-border-radius:4px;
+ display: inline-block;
+ background: white;
+ color: #444;
+ font-size: 12px;
+ border: 1px solid #e4e5e4;
+ padding: 5px 5px 6px 3px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
}
+
.annotation-period-edit:hover {
- background:#f1f0eb;
- border-color:#a9a399;
+ background: #f1f0eb;
+ border-color: #a9a399;
}
+
.annotation-period-edit>a {
- text-decoration:none;
- cursor:pointer;
- display:block;
+ text-decoration: none;
+ cursor: pointer;
+ display: block;
}
+
.annotation-period-edit>.datepicker {
- position:absolute;
- margin-top:6px;
- margin-left:-5px;
- z-index:15;
- background:white;
- border: 1px solid #e4e5e4;
- border-radius:4px;
- -moz-border-radius:4px;
- -webkit-border-radius:4px;
+ position: absolute;
+ margin-top: 6px;
+ margin-left: -5px;
+ z-index: 15;
+ background: white;
+ border: 1px solid #e4e5e4;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
}
diff --git a/plugins/AnonymizeIP/AnonymizeIP.php b/plugins/AnonymizeIP/AnonymizeIP.php
index ebb0c29180..e7b311fc26 100644
--- a/plugins/AnonymizeIP/AnonymizeIP.php
+++ b/plugins/AnonymizeIP/AnonymizeIP.php
@@ -16,63 +16,61 @@
*/
class Piwik_AnonymizeIP extends Piwik_Plugin
{
- /**
- * Get plugin information
- * @return array
- */
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('AnonymizeIP_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- 'TrackerPlugin' => true,
- );
- }
+ /**
+ * Get plugin information
+ * @return array
+ */
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('AnonymizeIP_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ 'TrackerPlugin' => true,
+ );
+ }
- /**
- * Get list of hooks to register
- * @return array
- */
- public function getListHooksRegistered()
- {
- return array(
- 'Tracker.Visit.setVisitorIp' => 'setVisitorIpAddress',
- );
- }
+ /**
+ * Get list of hooks to register
+ * @return array
+ */
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'Tracker.Visit.setVisitorIp' => 'setVisitorIpAddress',
+ );
+ }
- /**
- * Internal function to mask portions of the visitor IP address
- *
- * @param string $ip IP address in network address format
- * @param int $maskLength Number of octets to reset
- * @return string
- */
- static public function applyIPMask($ip, $maskLength)
- {
- $i = Piwik_Common::strlen($ip);
- if($maskLength > $i)
- {
- $maskLength = $i;
- }
+ /**
+ * Internal function to mask portions of the visitor IP address
+ *
+ * @param string $ip IP address in network address format
+ * @param int $maskLength Number of octets to reset
+ * @return string
+ */
+ static public function applyIPMask($ip, $maskLength)
+ {
+ $i = Piwik_Common::strlen($ip);
+ if ($maskLength > $i) {
+ $maskLength = $i;
+ }
- while($maskLength-- > 0)
- {
- $ip[--$i] = chr(0);
- }
+ while ($maskLength-- > 0) {
+ $ip[--$i] = chr(0);
+ }
- return $ip;
- }
+ return $ip;
+ }
- /**
- * Hook on Tracker.Visit.setVisitorIp to anonymize visitor IP addresses
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function setVisitorIpAddress($notification)
- {
- $ip =& $notification->getNotificationObject();
- $ip = self::applyIPMask($ip, Piwik_Config::getInstance()->Tracker['ip_address_mask_length']);
- }
+ /**
+ * Hook on Tracker.Visit.setVisitorIp to anonymize visitor IP addresses
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function setVisitorIpAddress($notification)
+ {
+ $ip =& $notification->getNotificationObject();
+ $ip = self::applyIPMask($ip, Piwik_Config::getInstance()->Tracker['ip_address_mask_length']);
+ }
}
diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php
index 97842dd943..eded6d451f 100644
--- a/plugins/CoreAdminHome/API.php
+++ b/plugins/CoreAdminHome/API.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_CoreAdminHome
*/
@@ -12,222 +12,211 @@
/**
* @package Piwik_CoreAdminHome
*/
-class Piwik_CoreAdminHome_API
+class Piwik_CoreAdminHome_API
{
- static private $instance = null;
- /**
- * @return Piwik_CoreAdminHome_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Will run all scheduled tasks due to run at this time.
- *
- * @return array
- */
- public function runScheduledTasks()
- {
- Piwik::checkUserIsSuperUser();
- return Piwik_TaskScheduler::runTasks();
- }
-
- public function getKnownSegmentsToArchive()
- {
- Piwik::checkUserIsSuperUser();
- return Piwik::getKnownSegmentsToArchive();
- }
-
- /*
- * stores the list of websites IDs to re-reprocess in archive.php
- */
- const OPTION_INVALIDATED_IDSITES = 'InvalidatedOldReports_WebsiteIds';
-
- /**
- * When tracking data in the past (using Tracking API), this function
- * can be used to invalidate reports for the idSites and dates where new data
- * was added.
- * DEV: If you call this API, the UI should display the data correctly, but will process
- * in real time, which could be very slow after large data imports.
- * After calling this function via REST, you can manually force all data
- * to be reprocessed by visiting the script as the Super User:
- * http://example.net/piwik/misc/cron/archive.php?token_auth=$SUPER_USER_TOKEN_AUTH_HERE
- * REQUIREMENTS: On large piwik setups, you will need in PHP configuration: max_execution_time = 0
- * We recommend to use an hourly schedule of the script at misc/cron/archive.php
- * More information: http://piwik.org/setup-auto-archiving/
- *
- * @param string $idSites Comma separated list of idSite that have had data imported for the specified dates
- * @param string $dates Comma separated list of dates to invalidate for all these websites
- * @return array
- */
- public function invalidateArchivedReports($idSites, $dates)
- {
- $idSites = Piwik_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);
-
- // Ensure the specified dates are valid
- $toInvalidate = $invalidDates = array();
- $dates = explode(',', $dates);
- $dates = array_unique($dates);
- foreach($dates as $theDate)
- {
- try {
- $date = Piwik_Date::factory($theDate);
- } catch(Exception $e) {
- $invalidDates[] = $theDate;
- continue;
- }
- if($date->toString() == $theDate)
- {
- $toInvalidate[] = $date;
- }
- else
- {
- $invalidDates[] = $theDate;
- }
- }
-
- // Lookup archive tables
- $tables = Piwik::getTablesInstalled();
- $archiveTables = Piwik::getTablesArchivesInstalled();
-
- // If using the feature "Delete logs older than N days"...
- $logsAreDeletedBeforeThisDate = Piwik_Config::getInstance()->Deletelogs['delete_logs_schedule_lowest_interval'];
- $logsDeleteEnabled = Piwik_Config::getInstance()->Deletelogs['delete_logs_enable'];
- $minimumDateWithLogs = false;
- if($logsDeleteEnabled
- && $logsAreDeletedBeforeThisDate)
- {
- $minimumDateWithLogs = Piwik_Date::factory('today')->subDay($logsAreDeletedBeforeThisDate);
- }
-
- // Given the list of dates, process which tables they should be deleted from
- $minDate = false;
- $warningDates = $processedDates = array();
- /* @var $date Piwik_Date */
- foreach($toInvalidate as $date)
- {
- // we should only delete reports for dates that are more recent than N days
- if($minimumDateWithLogs
- && $date->isEarlier($minimumDateWithLogs))
- {
- $warningDates[] = $date->toString();
- }
- else
- {
- $processedDates[] = $date->toString();
- }
-
- $month = $date->toString('Y_m');
- // For a given date, we must invalidate in the monthly archive table
- $datesByMonth[$month][] = $date->toString();
-
- // But also the year stored in January
- $year = $date->toString('Y_01');
- $datesByMonth[$year][] = $date->toString();
-
- // but also weeks overlapping several months stored in the month where the week is starting
- /* @var $week Piwik_Period_Week */
- $week = Piwik_Period::factory('week', $date);
- $week = $week->getDateStart()->toString('Y_m');
- $datesByMonth[$week][] = $date->toString();
-
- // Keep track of the minimum date for each website
- if($minDate === false
- || $date->isEarlier($minDate))
- {
- $minDate = $date;
- }
- }
-
- // In each table, invalidate day/week/month/year containing this date
- $sqlIdSites = implode(",", $idSites);
- foreach($archiveTables as $table)
- {
- // Extract Y_m from table name
- $suffix = str_replace(array('archive_numeric_','archive_blob_'), '', Piwik_Common::unprefixTable($table));
-
- if(!isset($datesByMonth[$suffix]))
- {
- continue;
- }
- // Dates which are to be deleted from this table
- $datesToDeleteInTable = $datesByMonth[$suffix];
-
- // Build one statement to delete all dates from the given table
- $sql = $bind = array();
- $datesToDeleteInTable = array_unique($datesToDeleteInTable);
- foreach($datesToDeleteInTable as $dateToDelete)
- {
- $sql[] = '(date1 <= ? AND ? <= date2)';
- $bind[] = $dateToDelete;
- $bind[] = $dateToDelete;
- }
- $sql = implode(" OR ", $sql);
-
- $query = "DELETE FROM $table ".
- " WHERE ( $sql ) ".
- " AND idsite IN (". $sqlIdSites .")";
- Piwik_Query($query, $bind);
- }
-
- // Update piwik_site.ts_created
- $query = "UPDATE " . Piwik_Common::prefixTable("site") .
- " SET ts_created = ?".
- " WHERE idsite IN ( $sqlIdSites )
+ static private $instance = null;
+
+ /**
+ * @return Piwik_CoreAdminHome_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Will run all scheduled tasks due to run at this time.
+ *
+ * @return array
+ */
+ public function runScheduledTasks()
+ {
+ Piwik::checkUserIsSuperUser();
+ return Piwik_TaskScheduler::runTasks();
+ }
+
+ public function getKnownSegmentsToArchive()
+ {
+ Piwik::checkUserIsSuperUser();
+ return Piwik::getKnownSegmentsToArchive();
+ }
+
+ /*
+ * stores the list of websites IDs to re-reprocess in archive.php
+ */
+ const OPTION_INVALIDATED_IDSITES = 'InvalidatedOldReports_WebsiteIds';
+
+ /**
+ * When tracking data in the past (using Tracking API), this function
+ * can be used to invalidate reports for the idSites and dates where new data
+ * was added.
+ * DEV: If you call this API, the UI should display the data correctly, but will process
+ * in real time, which could be very slow after large data imports.
+ * After calling this function via REST, you can manually force all data
+ * to be reprocessed by visiting the script as the Super User:
+ * http://example.net/piwik/misc/cron/archive.php?token_auth=$SUPER_USER_TOKEN_AUTH_HERE
+ * REQUIREMENTS: On large piwik setups, you will need in PHP configuration: max_execution_time = 0
+ * We recommend to use an hourly schedule of the script at misc/cron/archive.php
+ * More information: http://piwik.org/setup-auto-archiving/
+ *
+ * @param string $idSites Comma separated list of idSite that have had data imported for the specified dates
+ * @param string $dates Comma separated list of dates to invalidate for all these websites
+ * @return array
+ */
+ public function invalidateArchivedReports($idSites, $dates)
+ {
+ $idSites = Piwik_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);
+
+ // Ensure the specified dates are valid
+ $toInvalidate = $invalidDates = array();
+ $dates = explode(',', $dates);
+ $dates = array_unique($dates);
+ foreach ($dates as $theDate) {
+ try {
+ $date = Piwik_Date::factory($theDate);
+ } catch (Exception $e) {
+ $invalidDates[] = $theDate;
+ continue;
+ }
+ if ($date->toString() == $theDate) {
+ $toInvalidate[] = $date;
+ } else {
+ $invalidDates[] = $theDate;
+ }
+ }
+
+ // Lookup archive tables
+ $tables = Piwik::getTablesInstalled();
+ $archiveTables = Piwik::getTablesArchivesInstalled();
+
+ // If using the feature "Delete logs older than N days"...
+ $logsAreDeletedBeforeThisDate = Piwik_Config::getInstance()->Deletelogs['delete_logs_schedule_lowest_interval'];
+ $logsDeleteEnabled = Piwik_Config::getInstance()->Deletelogs['delete_logs_enable'];
+ $minimumDateWithLogs = false;
+ if ($logsDeleteEnabled
+ && $logsAreDeletedBeforeThisDate
+ ) {
+ $minimumDateWithLogs = Piwik_Date::factory('today')->subDay($logsAreDeletedBeforeThisDate);
+ }
+
+ // Given the list of dates, process which tables they should be deleted from
+ $minDate = false;
+ $warningDates = $processedDates = array();
+ /* @var $date Piwik_Date */
+ foreach ($toInvalidate as $date) {
+ // we should only delete reports for dates that are more recent than N days
+ if ($minimumDateWithLogs
+ && $date->isEarlier($minimumDateWithLogs)
+ ) {
+ $warningDates[] = $date->toString();
+ } else {
+ $processedDates[] = $date->toString();
+ }
+
+ $month = $date->toString('Y_m');
+ // For a given date, we must invalidate in the monthly archive table
+ $datesByMonth[$month][] = $date->toString();
+
+ // But also the year stored in January
+ $year = $date->toString('Y_01');
+ $datesByMonth[$year][] = $date->toString();
+
+ // but also weeks overlapping several months stored in the month where the week is starting
+ /* @var $week Piwik_Period_Week */
+ $week = Piwik_Period::factory('week', $date);
+ $week = $week->getDateStart()->toString('Y_m');
+ $datesByMonth[$week][] = $date->toString();
+
+ // Keep track of the minimum date for each website
+ if ($minDate === false
+ || $date->isEarlier($minDate)
+ ) {
+ $minDate = $date;
+ }
+ }
+
+ // In each table, invalidate day/week/month/year containing this date
+ $sqlIdSites = implode(",", $idSites);
+ foreach ($archiveTables as $table) {
+ // Extract Y_m from table name
+ $suffix = str_replace(array('archive_numeric_', 'archive_blob_'), '', Piwik_Common::unprefixTable($table));
+
+ if (!isset($datesByMonth[$suffix])) {
+ continue;
+ }
+ // Dates which are to be deleted from this table
+ $datesToDeleteInTable = $datesByMonth[$suffix];
+
+ // Build one statement to delete all dates from the given table
+ $sql = $bind = array();
+ $datesToDeleteInTable = array_unique($datesToDeleteInTable);
+ foreach ($datesToDeleteInTable as $dateToDelete) {
+ $sql[] = '(date1 <= ? AND ? <= date2)';
+ $bind[] = $dateToDelete;
+ $bind[] = $dateToDelete;
+ }
+ $sql = implode(" OR ", $sql);
+
+ $query = "DELETE FROM $table " .
+ " WHERE ( $sql ) " .
+ " AND idsite IN (" . $sqlIdSites . ")";
+ Piwik_Query($query, $bind);
+ }
+
+ // Update piwik_site.ts_created
+ $query = "UPDATE " . Piwik_Common::prefixTable("site") .
+ " SET ts_created = ?" .
+ " WHERE idsite IN ( $sqlIdSites )
AND ts_created > ?";
- $minDateSql = $minDate->subDay(1)->getDatetime();
- $bind = array($minDateSql,$minDateSql);
- Piwik_Query($query, $bind);
-
- // Force to re-process data for these websites in the next archive.php cron run
- $invalidatedIdSites = Piwik_CoreAdminHome_API::getWebsiteIdsToInvalidate();
- $invalidatedIdSites = array_merge($invalidatedIdSites, $idSites);
- $invalidatedIdSites = array_unique($invalidatedIdSites);
- $invalidatedIdSites = array_values($invalidatedIdSites);
- Piwik_SetOption(self::OPTION_INVALIDATED_IDSITES, serialize($invalidatedIdSites));
-
- Piwik_Site::clearCache();
-
- $output = array();
- // output logs
- if($warningDates)
- {
- $output[] = 'Warning: the following Dates have not been invalidated, because they are earlier than your Log Deletion limit: '.
- implode(", ", $warningDates).
- "\n The last day with logs is " . $minimumDateWithLogs. ". ".
- "\n Please disable 'Delete old Logs' or set it to a higher deletion threshold (eg. 180 days or 365 years).'.";
- }
- $output[] = "Success. The following dates were invalidated successfully: ".
- implode(", ", $processedDates);
- return $output;
- }
-
- /**
- * Returns array of idSites to force re-process next time archive.php runs
- *
- * @ignore
- * @return mixed
- */
- static public function getWebsiteIdsToInvalidate()
- {
- Piwik::checkUserHasSomeAdminAccess();
- $invalidatedIdSites = Piwik_GetOption(self::OPTION_INVALIDATED_IDSITES);
- if($invalidatedIdSites
- && ($invalidatedIdSites = unserialize($invalidatedIdSites))
- && count($invalidatedIdSites))
- {
- return $invalidatedIdSites;
- }
- return array();
- }
+ $minDateSql = $minDate->subDay(1)->getDatetime();
+ $bind = array($minDateSql, $minDateSql);
+ Piwik_Query($query, $bind);
+
+ // Force to re-process data for these websites in the next archive.php cron run
+ $invalidatedIdSites = Piwik_CoreAdminHome_API::getWebsiteIdsToInvalidate();
+ $invalidatedIdSites = array_merge($invalidatedIdSites, $idSites);
+ $invalidatedIdSites = array_unique($invalidatedIdSites);
+ $invalidatedIdSites = array_values($invalidatedIdSites);
+ Piwik_SetOption(self::OPTION_INVALIDATED_IDSITES, serialize($invalidatedIdSites));
+
+ Piwik_Site::clearCache();
+
+ $output = array();
+ // output logs
+ if ($warningDates) {
+ $output[] = 'Warning: the following Dates have not been invalidated, because they are earlier than your Log Deletion limit: ' .
+ implode(", ", $warningDates) .
+ "\n The last day with logs is " . $minimumDateWithLogs . ". " .
+ "\n Please disable 'Delete old Logs' or set it to a higher deletion threshold (eg. 180 days or 365 years).'.";
+ }
+ $output[] = "Success. The following dates were invalidated successfully: " .
+ implode(", ", $processedDates);
+ return $output;
+ }
+
+ /**
+ * Returns array of idSites to force re-process next time archive.php runs
+ *
+ * @ignore
+ * @return mixed
+ */
+ static public function getWebsiteIdsToInvalidate()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ $invalidatedIdSites = Piwik_GetOption(self::OPTION_INVALIDATED_IDSITES);
+ if ($invalidatedIdSites
+ && ($invalidatedIdSites = unserialize($invalidatedIdSites))
+ && count($invalidatedIdSites)
+ ) {
+ return $invalidatedIdSites;
+ }
+ return array();
+ }
}
diff --git a/plugins/CoreAdminHome/Controller.php b/plugins/CoreAdminHome/Controller.php
index f5e5d83a9c..c915205bc2 100644
--- a/plugins/CoreAdminHome/Controller.php
+++ b/plugins/CoreAdminHome/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_CoreAdminHome
*/
@@ -15,228 +15,218 @@
*/
class Piwik_CoreAdminHome_Controller extends Piwik_Controller_Admin
{
- const LOGO_HEIGHT = 300;
- const LOGO_SMALL_HEIGHT = 100;
-
- public function index()
- {
- return $this->redirectToIndex('UsersManager', 'userSettings');
- }
-
- public function generalSettings()
- {
- Piwik::checkUserHasSomeAdminAccess();
- $view = Piwik_View::factory('generalSettings');
-
- if(Piwik::isUserIsSuperUser())
- {
- $enableBrowserTriggerArchiving = Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled();
- $todayArchiveTimeToLive = Piwik_ArchiveProcessing::getTodayArchiveTimeToLive();
- $showWarningCron = false;
- if(!$enableBrowserTriggerArchiving
- && $todayArchiveTimeToLive < 3600)
- {
- $showWarningCron = true;
- }
- $view->showWarningCron = $showWarningCron;
- $view->todayArchiveTimeToLive = $todayArchiveTimeToLive;
- $view->enableBrowserTriggerArchiving = $enableBrowserTriggerArchiving;
-
- $config = Piwik_Config::getInstance();
-
- if(!$config->isFileWritable())
- {
- $view->configFileNotWritable = true;
- }
-
- $debug = $config->Debug;
- $view->enableBetaReleaseCheck = $debug['allow_upgrades_to_beta'];
-
- $view->mail = $config->mail;
-
- $view->branding = $config->branding;
-
- $directoryWritable = is_writable(PIWIK_DOCUMENT_ROOT.'/themes/');
- $logoFilesWriteable = is_writeable(PIWIK_DOCUMENT_ROOT.'/themes/logo.png') && is_writeable(PIWIK_DOCUMENT_ROOT.'/themes/logo-header.png');
- $view->logosWriteable = ($logoFilesWriteable || $directoryWritable) && ini_get('file_uploads') == 1;
-
- $trustedHosts = array();
- if (isset($config->General['trusted_hosts']))
- {
- $trustedHosts = $config->General['trusted_hosts'];
- }
- $view->trustedHosts = $trustedHosts;
- }
-
- $view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
- $this->setBasicVariablesView($view);
- $view->topMenu = Piwik_GetTopMenu();
- $view->menu = Piwik_GetAdminMenu();
- echo $view->render();
- }
-
- public function setGeneralSettings()
- {
- Piwik::checkUserIsSuperUser();
- $response = new Piwik_API_ResponseBuilder(Piwik_Common::getRequestVar('format'));
- try {
- $this->checkTokenInUrl();
- $enableBrowserTriggerArchiving = Piwik_Common::getRequestVar('enableBrowserTriggerArchiving');
- $todayArchiveTimeToLive = Piwik_Common::getRequestVar('todayArchiveTimeToLive');
-
- Piwik_ArchiveProcessing::setBrowserTriggerArchiving((bool)$enableBrowserTriggerArchiving);
- Piwik_ArchiveProcessing::setTodayArchiveTimeToLive($todayArchiveTimeToLive);
-
- // Update email settings
- $mail = array();
- $mail['transport'] = (Piwik_Common::getRequestVar('mailUseSmtp') == '1') ? 'smtp' : '';
- $mail['port'] = Piwik_Common::getRequestVar('mailPort', '');
- $mail['host'] = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('mailHost', ''));
- $mail['type'] = Piwik_Common::getRequestVar('mailType', '');
- $mail['username'] = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('mailUsername', ''));
- $mail['password'] = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('mailPassword', ''));
- $mail['encryption'] = Piwik_Common::getRequestVar('mailEncryption', '');
-
- $config = Piwik_Config::getInstance();
- $config->mail = $mail;
-
- // update branding settings
- $branding = $config->branding;
- $branding['use_custom_logo'] = Piwik_Common::getRequestVar('useCustomLogo', '0');
- $config->branding = $branding;
-
- // update beta channel setting
- $debug = $config->Debug;
- $debug['allow_upgrades_to_beta'] = Piwik_Common::getRequestVar('enableBetaReleaseCheck', '0', 'int');
- $config->Debug = $debug;
- // update trusted host settings
- $trustedHosts = Piwik_Common::getRequestVar('trustedHosts', false, 'json');
- if ($trustedHosts !== false)
- {
- Piwik_Url::saveTrustedHostnameInConfig($trustedHosts);
- }
-
- $config->forceSave();
-
- $toReturn = $response->getResponse();
- } catch(Exception $e ) {
- $toReturn = $response->getResponseException( $e );
- }
- echo $toReturn;
- }
-
- /**
- * Renders and echo's an admin page that lets users generate custom JavaScript
- * tracking code and custom image tracker links.
- */
- public function trackingCodeGenerator()
- {
- $view = Piwik_View::factory('jsTrackingGenerator');
- $this->setBasicVariablesView($view);
- $view->topMenu = Piwik_GetTopMenu();
- $view->menu = Piwik_GetAdminMenu();
-
- $viewableIdSites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess();
-
- $defaultIdSite = reset($viewableIdSites);
- $view->idSite = Piwik_Common::getRequestVar('idSite', $defaultIdSite, 'int');
-
- $view->defaultReportSiteName = Piwik_Site::getNameFor($view->idSite);
- $view->defaultSiteRevenue = Piwik::getCurrency($view->idSite);
-
- $allUrls = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($view->idSite);
- if (isset($allUrls[1]))
- {
- $aliasUrl = $allUrls[1];
- }
- else
- {
- $aliasUrl = 'x.domain.com';
- }
- $view->defaultReportSiteAlias = $aliasUrl;
-
- $mainUrl = Piwik_Site::getMainUrlFor($view->idSite);
- $view->defaultReportSiteDomain = @parse_url($mainUrl, PHP_URL_HOST);
-
- // get currencies for each viewable site
- $view->currencySymbols = Piwik_SitesManager_API::getInstance()->getCurrencySymbols();
-
- $view->serverSideDoNotTrackEnabled = Piwik_PrivacyManager_Controller::isDntSupported();
-
- echo $view->render();
- }
-
- /**
- * Shows the "Track Visits" checkbox.
- */
- public function optOut()
- {
- $trackVisits = !Piwik_Tracker_IgnoreCookie::isIgnoreCookieFound();
-
- $nonce = Piwik_Common::getRequestVar('nonce', false);
- $language = Piwik_Common::getRequestVar('language', '');
- if($nonce !== false && Piwik_Nonce::verifyNonce('Piwik_OptOut', $nonce))
- {
- Piwik_Nonce::discardNonce('Piwik_OptOut');
- Piwik_Tracker_IgnoreCookie::setIgnoreCookie();
- $trackVisits = !$trackVisits;
- }
-
- $view = Piwik_View::factory('optOut');
- $view->trackVisits = $trackVisits;
- $view->nonce = Piwik_Nonce::getNonce('Piwik_OptOut', 3600);
- $view->language = Piwik_LanguagesManager_API::getInstance()->isLanguageAvailable($language)
- ? $language
- : Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
- echo $view->render();
- }
-
- public function uploadCustomLogo()
- {
- Piwik::checkUserIsSuperUser();
- if(empty($_FILES['customLogo'])
- || !empty($_FILES['customLogo']['error'])
- )
- {
- echo '0';
- return;
- }
-
- $file = $_FILES['customLogo']['tmp_name'];
- if(!file_exists($file))
- {
- echo '0';
- return;
- }
- $error = false;
-
- list($width, $height) = getimagesize($file);
- switch($_FILES['customLogo']['type']) {
- case 'image/jpeg':
- $image = imagecreatefromjpeg($file);
- break;
- case 'image/png':
- $image = imagecreatefrompng($file);
- break;
- case 'image/gif':
- $image = imagecreatefromgif($file);
- break;
- default:
- echo '0';
- return;
- }
-
- $widthExpected = round($width * self::LOGO_HEIGHT / $height);
- $smallWidthExpected = round($width * self::LOGO_SMALL_HEIGHT / $height);
-
- $logo = imagecreatetruecolor($widthExpected, self::LOGO_HEIGHT);
- $logoSmall = imagecreatetruecolor($smallWidthExpected, self::LOGO_SMALL_HEIGHT);
- imagecopyresized($logo, $image, 0, 0, 0, 0, $widthExpected, self::LOGO_HEIGHT, $width, $height);
- imagecopyresized($logoSmall, $image, 0, 0, 0, 0, $smallWidthExpected, self::LOGO_SMALL_HEIGHT, $width, $height);
-
- imagepng($logo, PIWIK_DOCUMENT_ROOT.'/themes/logo.png', 3);
- imagepng($logoSmall, PIWIK_DOCUMENT_ROOT.'/themes/logo-header.png', 3);
- echo '1';
- return;
- }
+ const LOGO_HEIGHT = 300;
+ const LOGO_SMALL_HEIGHT = 100;
+
+ public function index()
+ {
+ return $this->redirectToIndex('UsersManager', 'userSettings');
+ }
+
+ public function generalSettings()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ $view = Piwik_View::factory('generalSettings');
+
+ if (Piwik::isUserIsSuperUser()) {
+ $enableBrowserTriggerArchiving = Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled();
+ $todayArchiveTimeToLive = Piwik_ArchiveProcessing::getTodayArchiveTimeToLive();
+ $showWarningCron = false;
+ if (!$enableBrowserTriggerArchiving
+ && $todayArchiveTimeToLive < 3600
+ ) {
+ $showWarningCron = true;
+ }
+ $view->showWarningCron = $showWarningCron;
+ $view->todayArchiveTimeToLive = $todayArchiveTimeToLive;
+ $view->enableBrowserTriggerArchiving = $enableBrowserTriggerArchiving;
+
+ $config = Piwik_Config::getInstance();
+
+ if (!$config->isFileWritable()) {
+ $view->configFileNotWritable = true;
+ }
+
+ $debug = $config->Debug;
+ $view->enableBetaReleaseCheck = $debug['allow_upgrades_to_beta'];
+
+ $view->mail = $config->mail;
+
+ $view->branding = $config->branding;
+
+ $directoryWritable = is_writable(PIWIK_DOCUMENT_ROOT . '/themes/');
+ $logoFilesWriteable = is_writeable(PIWIK_DOCUMENT_ROOT . '/themes/logo.png') && is_writeable(PIWIK_DOCUMENT_ROOT . '/themes/logo-header.png');
+ $view->logosWriteable = ($logoFilesWriteable || $directoryWritable) && ini_get('file_uploads') == 1;
+
+ $trustedHosts = array();
+ if (isset($config->General['trusted_hosts'])) {
+ $trustedHosts = $config->General['trusted_hosts'];
+ }
+ $view->trustedHosts = $trustedHosts;
+ }
+
+ $view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
+ $this->setBasicVariablesView($view);
+ $view->topMenu = Piwik_GetTopMenu();
+ $view->menu = Piwik_GetAdminMenu();
+ echo $view->render();
+ }
+
+ public function setGeneralSettings()
+ {
+ Piwik::checkUserIsSuperUser();
+ $response = new Piwik_API_ResponseBuilder(Piwik_Common::getRequestVar('format'));
+ try {
+ $this->checkTokenInUrl();
+ $enableBrowserTriggerArchiving = Piwik_Common::getRequestVar('enableBrowserTriggerArchiving');
+ $todayArchiveTimeToLive = Piwik_Common::getRequestVar('todayArchiveTimeToLive');
+
+ Piwik_ArchiveProcessing::setBrowserTriggerArchiving((bool)$enableBrowserTriggerArchiving);
+ Piwik_ArchiveProcessing::setTodayArchiveTimeToLive($todayArchiveTimeToLive);
+
+ // Update email settings
+ $mail = array();
+ $mail['transport'] = (Piwik_Common::getRequestVar('mailUseSmtp') == '1') ? 'smtp' : '';
+ $mail['port'] = Piwik_Common::getRequestVar('mailPort', '');
+ $mail['host'] = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('mailHost', ''));
+ $mail['type'] = Piwik_Common::getRequestVar('mailType', '');
+ $mail['username'] = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('mailUsername', ''));
+ $mail['password'] = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('mailPassword', ''));
+ $mail['encryption'] = Piwik_Common::getRequestVar('mailEncryption', '');
+
+ $config = Piwik_Config::getInstance();
+ $config->mail = $mail;
+
+ // update branding settings
+ $branding = $config->branding;
+ $branding['use_custom_logo'] = Piwik_Common::getRequestVar('useCustomLogo', '0');
+ $config->branding = $branding;
+
+ // update beta channel setting
+ $debug = $config->Debug;
+ $debug['allow_upgrades_to_beta'] = Piwik_Common::getRequestVar('enableBetaReleaseCheck', '0', 'int');
+ $config->Debug = $debug;
+ // update trusted host settings
+ $trustedHosts = Piwik_Common::getRequestVar('trustedHosts', false, 'json');
+ if ($trustedHosts !== false) {
+ Piwik_Url::saveTrustedHostnameInConfig($trustedHosts);
+ }
+
+ $config->forceSave();
+
+ $toReturn = $response->getResponse();
+ } catch (Exception $e) {
+ $toReturn = $response->getResponseException($e);
+ }
+ echo $toReturn;
+ }
+
+ /**
+ * Renders and echo's an admin page that lets users generate custom JavaScript
+ * tracking code and custom image tracker links.
+ */
+ public function trackingCodeGenerator()
+ {
+ $view = Piwik_View::factory('jsTrackingGenerator');
+ $this->setBasicVariablesView($view);
+ $view->topMenu = Piwik_GetTopMenu();
+ $view->menu = Piwik_GetAdminMenu();
+
+ $viewableIdSites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess();
+
+ $defaultIdSite = reset($viewableIdSites);
+ $view->idSite = Piwik_Common::getRequestVar('idSite', $defaultIdSite, 'int');
+
+ $view->defaultReportSiteName = Piwik_Site::getNameFor($view->idSite);
+ $view->defaultSiteRevenue = Piwik::getCurrency($view->idSite);
+
+ $allUrls = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($view->idSite);
+ if (isset($allUrls[1])) {
+ $aliasUrl = $allUrls[1];
+ } else {
+ $aliasUrl = 'x.domain.com';
+ }
+ $view->defaultReportSiteAlias = $aliasUrl;
+
+ $mainUrl = Piwik_Site::getMainUrlFor($view->idSite);
+ $view->defaultReportSiteDomain = @parse_url($mainUrl, PHP_URL_HOST);
+
+ // get currencies for each viewable site
+ $view->currencySymbols = Piwik_SitesManager_API::getInstance()->getCurrencySymbols();
+
+ $view->serverSideDoNotTrackEnabled = Piwik_PrivacyManager_Controller::isDntSupported();
+
+ echo $view->render();
+ }
+
+ /**
+ * Shows the "Track Visits" checkbox.
+ */
+ public function optOut()
+ {
+ $trackVisits = !Piwik_Tracker_IgnoreCookie::isIgnoreCookieFound();
+
+ $nonce = Piwik_Common::getRequestVar('nonce', false);
+ $language = Piwik_Common::getRequestVar('language', '');
+ if ($nonce !== false && Piwik_Nonce::verifyNonce('Piwik_OptOut', $nonce)) {
+ Piwik_Nonce::discardNonce('Piwik_OptOut');
+ Piwik_Tracker_IgnoreCookie::setIgnoreCookie();
+ $trackVisits = !$trackVisits;
+ }
+
+ $view = Piwik_View::factory('optOut');
+ $view->trackVisits = $trackVisits;
+ $view->nonce = Piwik_Nonce::getNonce('Piwik_OptOut', 3600);
+ $view->language = Piwik_LanguagesManager_API::getInstance()->isLanguageAvailable($language)
+ ? $language
+ : Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
+ echo $view->render();
+ }
+
+ public function uploadCustomLogo()
+ {
+ Piwik::checkUserIsSuperUser();
+ if (empty($_FILES['customLogo'])
+ || !empty($_FILES['customLogo']['error'])
+ ) {
+ echo '0';
+ return;
+ }
+
+ $file = $_FILES['customLogo']['tmp_name'];
+ if (!file_exists($file)) {
+ echo '0';
+ return;
+ }
+ $error = false;
+
+ list($width, $height) = getimagesize($file);
+ switch ($_FILES['customLogo']['type']) {
+ case 'image/jpeg':
+ $image = imagecreatefromjpeg($file);
+ break;
+ case 'image/png':
+ $image = imagecreatefrompng($file);
+ break;
+ case 'image/gif':
+ $image = imagecreatefromgif($file);
+ break;
+ default:
+ echo '0';
+ return;
+ }
+
+ $widthExpected = round($width * self::LOGO_HEIGHT / $height);
+ $smallWidthExpected = round($width * self::LOGO_SMALL_HEIGHT / $height);
+
+ $logo = imagecreatetruecolor($widthExpected, self::LOGO_HEIGHT);
+ $logoSmall = imagecreatetruecolor($smallWidthExpected, self::LOGO_SMALL_HEIGHT);
+ imagecopyresized($logo, $image, 0, 0, 0, 0, $widthExpected, self::LOGO_HEIGHT, $width, $height);
+ imagecopyresized($logoSmall, $image, 0, 0, 0, 0, $smallWidthExpected, self::LOGO_SMALL_HEIGHT, $width, $height);
+
+ imagepng($logo, PIWIK_DOCUMENT_ROOT . '/themes/logo.png', 3);
+ imagepng($logoSmall, PIWIK_DOCUMENT_ROOT . '/themes/logo-header.png', 3);
+ echo '1';
+ return;
+ }
}
diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php
index 7dbf1b6ee9..3164811cca 100644
--- a/plugins/CoreAdminHome/CoreAdminHome.php
+++ b/plugins/CoreAdminHome/CoreAdminHome.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_CoreAdminHome
*/
@@ -15,114 +15,112 @@
*/
class Piwik_CoreAdminHome extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('CoreAdminHome_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('CoreAdminHome_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'AdminMenu.add' => 'addMenu',
+ 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
+ );
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getScheduledTasks($notification)
+ {
+ $tasks = & $notification->getNotificationObject();
+
+ // general data purge on older archive tables, executed daily
+ $purgeArchiveTablesTask = new Piwik_ScheduledTask ($this,
+ 'purgeOutdatedArchives',
+ null,
+ new Piwik_ScheduledTime_Daily(),
+ Piwik_ScheduledTask::HIGH_PRIORITY);
+ $tasks[] = $purgeArchiveTablesTask;
+
+ // lowest priority since tables should be optimized after they are modified
+ $optimizeArchiveTableTask = new Piwik_ScheduledTask ($this,
+ 'optimizeArchiveTable',
+ null,
+ new Piwik_ScheduledTime_Daily(),
+ Piwik_ScheduledTask::LOWEST_PRIORITY);
+ $tasks[] = $optimizeArchiveTableTask;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+
+ $cssFiles[] = "libs/jquery/themes/base/jquery-ui.css";
+ $cssFiles[] = "plugins/CoreAdminHome/templates/menu.css";
+ $cssFiles[] = "themes/default/common.css";
+ $cssFiles[] = "plugins/CoreAdminHome/templates/styles.css";
+ $cssFiles[] = "plugins/CoreHome/templates/donate.css";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+
+ $jsFiles[] = "libs/jquery/jquery.js";
+ $jsFiles[] = "libs/jquery/jquery-ui.js";
+ $jsFiles[] = "libs/javascript/sprintf.js";
+ $jsFiles[] = "themes/default/common.js";
+ $jsFiles[] = "themes/default/ajaxHelper.js";
+ $jsFiles[] = "libs/jquery/jquery.history.js";
+ $jsFiles[] = "plugins/CoreHome/templates/broadcast.js";
+ $jsFiles[] = "plugins/CoreAdminHome/templates/generalSettings.js";
+ $jsFiles[] = "plugins/CoreHome/templates/donate.js";
+ }
- public function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'AdminMenu.add' => 'addMenu',
- 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
- );
- }
+ function addMenu()
+ {
+ Piwik_AddAdminSubMenu('CoreAdminHome_MenuManage', NULL, "", Piwik::isUserHasSomeAdminAccess(), $order = 1);
+ Piwik_AddAdminSubMenu('CoreAdminHome_MenuCommunity', NULL, "", Piwik::isUserHasSomeAdminAccess(), $order = 3);
+ Piwik_AddAdminSubMenu('CoreAdminHome_MenuDiagnostic', NULL, "", Piwik::isUserHasSomeAdminAccess(), $order = 20);
+ Piwik_AddAdminSubMenu('General_Settings', NULL, "", Piwik::isUserHasSomeAdminAccess(), $order = 5);
+ Piwik_AddAdminSubMenu('General_Settings', 'CoreAdminHome_MenuGeneralSettings',
+ array('module' => 'CoreAdminHome', 'action' => 'generalSettings'),
+ Piwik::isUserHasSomeAdminAccess(),
+ $order = 6);
+ Piwik_AddAdminSubMenu('CoreAdminHome_MenuManage', 'CoreAdminHome_TrackingCode',
+ array('module' => 'CoreAdminHome', 'action' => 'trackingCodeGenerator'),
+ Piwik::isUserHasSomeAdminAccess(),
+ $order = 4);
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getScheduledTasks ( $notification )
- {
- $tasks = &$notification->getNotificationObject();
-
- // general data purge on older archive tables, executed daily
- $purgeArchiveTablesTask = new Piwik_ScheduledTask ( $this,
- 'purgeOutdatedArchives',
- null,
- new Piwik_ScheduledTime_Daily(),
- Piwik_ScheduledTask::HIGH_PRIORITY);
- $tasks[] = $purgeArchiveTablesTask;
-
- // lowest priority since tables should be optimized after they are modified
- $optimizeArchiveTableTask = new Piwik_ScheduledTask ( $this,
- 'optimizeArchiveTable',
- null,
- new Piwik_ScheduledTime_Daily(),
- Piwik_ScheduledTask::LOWEST_PRIORITY);
- $tasks[] = $optimizeArchiveTableTask;
- }
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "libs/jquery/themes/base/jquery-ui.css";
- $cssFiles[] = "plugins/CoreAdminHome/templates/menu.css";
- $cssFiles[] = "themes/default/common.css";
- $cssFiles[] = "plugins/CoreAdminHome/templates/styles.css";
- $cssFiles[] = "plugins/CoreHome/templates/donate.css";
- }
+ function purgeOutdatedArchives()
+ {
+ $archiveTables = Piwik::getTablesArchivesInstalled();
+ foreach ($archiveTables as $table) {
+ if (strpos($table, 'numeric') !== false) {
+ Piwik_ArchiveProcessing_Period::doPurgeOutdatedArchives($table);
+ }
+ }
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles ( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
-
- $jsFiles[] = "libs/jquery/jquery.js";
- $jsFiles[] = "libs/jquery/jquery-ui.js";
- $jsFiles[] = "libs/javascript/sprintf.js";
- $jsFiles[] = "themes/default/common.js";
- $jsFiles[] = "themes/default/ajaxHelper.js";
- $jsFiles[] = "libs/jquery/jquery.history.js";
- $jsFiles[] = "plugins/CoreHome/templates/broadcast.js";
- $jsFiles[] = "plugins/CoreAdminHome/templates/generalSettings.js";
- $jsFiles[] = "plugins/CoreHome/templates/donate.js";
- }
-
- function addMenu()
- {
- Piwik_AddAdminSubMenu('CoreAdminHome_MenuManage', NULL, "", Piwik::isUserHasSomeAdminAccess(), $order = 1);
- Piwik_AddAdminSubMenu('CoreAdminHome_MenuCommunity', NULL, "", Piwik::isUserHasSomeAdminAccess(), $order = 3);
- Piwik_AddAdminSubMenu('CoreAdminHome_MenuDiagnostic', NULL, "", Piwik::isUserHasSomeAdminAccess(), $order = 20);
- Piwik_AddAdminSubMenu('General_Settings', NULL, "", Piwik::isUserHasSomeAdminAccess(), $order = 5);
- Piwik_AddAdminSubMenu('General_Settings', 'CoreAdminHome_MenuGeneralSettings',
- array('module' => 'CoreAdminHome', 'action' => 'generalSettings'),
- Piwik::isUserHasSomeAdminAccess(),
- $order = 6);
- Piwik_AddAdminSubMenu('CoreAdminHome_MenuManage', 'CoreAdminHome_TrackingCode',
- array('module' => 'CoreAdminHome', 'action' => 'trackingCodeGenerator'),
- Piwik::isUserHasSomeAdminAccess(),
- $order = 4);
-
- }
-
- function purgeOutdatedArchives()
- {
- $archiveTables = Piwik::getTablesArchivesInstalled();
- foreach($archiveTables as $table)
- {
- if(strpos($table, 'numeric') !== false)
- {
- Piwik_ArchiveProcessing_Period::doPurgeOutdatedArchives($table);
- }
- }
- }
-
- function optimizeArchiveTable()
- {
- $archiveTables = Piwik::getTablesArchivesInstalled();
- Piwik_OptimizeTables($archiveTables);
- }
+ function optimizeArchiveTable()
+ {
+ $archiveTables = Piwik::getTablesArchivesInstalled();
+ Piwik_OptimizeTables($archiveTables);
+ }
}
diff --git a/plugins/CoreAdminHome/templates/generalSettings.js b/plugins/CoreAdminHome/templates/generalSettings.js
index 8adcad271d..6a493626c7 100644
--- a/plugins/CoreAdminHome/templates/generalSettings.js
+++ b/plugins/CoreAdminHome/templates/generalSettings.js
@@ -5,33 +5,32 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function sendGeneralSettingsAJAX()
-{
- var enableBrowserTriggerArchiving = $('input[name=enableBrowserTriggerArchiving]:checked').val();
- var enableBetaReleaseCheck = $('input[name=enableBetaReleaseCheck]:checked').val();
- var todayArchiveTimeToLive = $('#todayArchiveTimeToLive').val();
+function sendGeneralSettingsAJAX() {
+ var enableBrowserTriggerArchiving = $('input[name=enableBrowserTriggerArchiving]:checked').val();
+ var enableBetaReleaseCheck = $('input[name=enableBetaReleaseCheck]:checked').val();
+ var todayArchiveTimeToLive = $('#todayArchiveTimeToLive').val();
- var trustedHosts = [];
- $('input[name=trusted_host]').each(function () {
- trustedHosts.push($(this).val());
- });
+ var trustedHosts = [];
+ $('input[name=trusted_host]').each(function () {
+ trustedHosts.push($(this).val());
+ });
var ajaxHandler = new ajaxHelper();
ajaxHandler.setLoadingElement();
ajaxHandler.addParams({
- format: 'json',
- enableBrowserTriggerArchiving: enableBrowserTriggerArchiving,
- enableBetaReleaseCheck: enableBetaReleaseCheck,
- todayArchiveTimeToLive: todayArchiveTimeToLive,
- mailUseSmtp: isSmtpEnabled(),
- mailPort: $('#mailPort').val(),
- mailHost: $('#mailHost').val(),
- mailType: $('#mailType').val(),
- mailUsername: $('#mailUsername').val(),
- mailPassword: $('#mailPassword').val(),
- mailEncryption: $('#mailEncryption').val(),
- useCustomLogo: isCustomLogoEnabled(),
- trustedHosts: JSON.stringify(trustedHosts)
+ format: 'json',
+ enableBrowserTriggerArchiving: enableBrowserTriggerArchiving,
+ enableBetaReleaseCheck: enableBetaReleaseCheck,
+ todayArchiveTimeToLive: todayArchiveTimeToLive,
+ mailUseSmtp: isSmtpEnabled(),
+ mailPort: $('#mailPort').val(),
+ mailHost: $('#mailHost').val(),
+ mailType: $('#mailType').val(),
+ mailUsername: $('#mailUsername').val(),
+ mailPassword: $('#mailPassword').val(),
+ mailEncryption: $('#mailEncryption').val(),
+ useCustomLogo: isCustomLogoEnabled(),
+ trustedHosts: JSON.stringify(trustedHosts)
}, 'POST');
ajaxHandler.addParams({
module: 'CoreAdminHome',
@@ -40,111 +39,103 @@ function sendGeneralSettingsAJAX()
ajaxHandler.redirectOnSuccess();
ajaxHandler.send(true);
}
-function showSmtpSettings(value)
-{
- $('#smtpSettings').toggle(value==1);
+function showSmtpSettings(value) {
+ $('#smtpSettings').toggle(value == 1);
}
-function isSmtpEnabled()
-{
- return $('input[name="mailUseSmtp"]:checked').val();
+function isSmtpEnabled() {
+ return $('input[name="mailUseSmtp"]:checked').val();
}
-function showCustomLogoSettings(value)
-{
- $('#logoSettings').toggle(value==1);
+function showCustomLogoSettings(value) {
+ $('#logoSettings').toggle(value == 1);
}
-function isCustomLogoEnabled()
-{
- return $('input[name="useCustomLogo"]:checked').val();
+function isCustomLogoEnabled() {
+ return $('input[name="useCustomLogo"]:checked').val();
}
function refreshCustomLogo() {
- var imageDiv = $("#currentLogo");
- if(imageDiv && imageDiv.attr("src")) {
- var logoUrl = imageDiv.attr("src").split("?")[0];
- imageDiv.attr("src", logoUrl+"?"+ (new Date()).getTime());
- }
+ var imageDiv = $("#currentLogo");
+ if (imageDiv && imageDiv.attr("src")) {
+ var logoUrl = imageDiv.attr("src").split("?")[0];
+ imageDiv.attr("src", logoUrl + "?" + (new Date()).getTime());
+ }
}
-$(document).ready( function() {
- var originalTrustedHostCount = $('input[name=trusted_host]').length;
-
- showSmtpSettings(isSmtpEnabled());
- showCustomLogoSettings(isCustomLogoEnabled());
- $('#generalSettingsSubmit').click( function() {
- var doSubmit = function()
- {
- sendGeneralSettingsAJAX();
- };
-
- var hasTrustedHostsChanged = false,
- hosts = $('input[name=trusted_host]');
- if (hosts.length != originalTrustedHostCount)
- {
- hasTrustedHostsChanged = true;
- }
- else
- {
- hosts.each(function() {
- hasTrustedHostsChanged |= this.defaultValue != this.value;
- });
- }
-
- // if trusted hosts have changed, make sure to ask for confirmation
- if (hasTrustedHostsChanged)
- {
- piwikHelper.modalConfirm('#confirmTrustedHostChange', {yes: doSubmit});
- }
- else
- {
- doSubmit();
- }
- });
+$(document).ready(function () {
+ var originalTrustedHostCount = $('input[name=trusted_host]').length;
- $('input[name=mailUseSmtp]').click(function(){
- showSmtpSettings($(this).val());
- });
- $('input[name=useCustomLogo]').click(function(){
- refreshCustomLogo();
- showCustomLogoSettings($(this).val());
- });
- $('input').keypress( function(e) {
- var key=e.keyCode || e.which;
- if (key==13) {
- $('#generalSettingsSubmit').click();
- }
- }
- );
-
- $("#logoUploadForm").submit( function(data) {
- var submittingForm = $( this );
- var frameName = "upload"+(new Date()).getTime();
- var uploadFrame = $("<iframe name=\""+frameName+"\" />");
- uploadFrame.css("display", "none");
- uploadFrame.load(function(data){
- setTimeout(function(){
- refreshCustomLogo();
- uploadFrame.remove();},1000);
- });
- $("body:first").append(uploadFrame);
- submittingForm.attr("target", frameName);
- });
-
- $('#customLogo').change(function(){$("#logoUploadForm").submit()});
-
- // trusted hosts event handling
- $('#trustedHostSettings .adminTable').on('click', '.remove-trusted-host', function(e) {
- e.preventDefault();
- $(this).parent().parent().remove();
- return false;
- });
- $('#trustedHostSettings .add-trusted-host').click(function(e) {
- e.preventDefault();
-
- // append new row to the table
- $('#trustedHostSettings tbody').append('<tr>'
- + '<td><input name="trusted_host" type="text" value=""/></td>'
- + '<td><a href="#" class="remove-trusted-host">x</a></td>'
- + '</tr>');
- return false;
- });
+ showSmtpSettings(isSmtpEnabled());
+ showCustomLogoSettings(isCustomLogoEnabled());
+ $('#generalSettingsSubmit').click(function () {
+ var doSubmit = function () {
+ sendGeneralSettingsAJAX();
+ };
+
+ var hasTrustedHostsChanged = false,
+ hosts = $('input[name=trusted_host]');
+ if (hosts.length != originalTrustedHostCount) {
+ hasTrustedHostsChanged = true;
+ }
+ else {
+ hosts.each(function () {
+ hasTrustedHostsChanged |= this.defaultValue != this.value;
+ });
+ }
+
+ // if trusted hosts have changed, make sure to ask for confirmation
+ if (hasTrustedHostsChanged) {
+ piwikHelper.modalConfirm('#confirmTrustedHostChange', {yes: doSubmit});
+ }
+ else {
+ doSubmit();
+ }
+ });
+
+ $('input[name=mailUseSmtp]').click(function () {
+ showSmtpSettings($(this).val());
+ });
+ $('input[name=useCustomLogo]').click(function () {
+ refreshCustomLogo();
+ showCustomLogoSettings($(this).val());
+ });
+ $('input').keypress(function (e) {
+ var key = e.keyCode || e.which;
+ if (key == 13) {
+ $('#generalSettingsSubmit').click();
+ }
+ }
+ );
+
+ $("#logoUploadForm").submit(function (data) {
+ var submittingForm = $(this);
+ var frameName = "upload" + (new Date()).getTime();
+ var uploadFrame = $("<iframe name=\"" + frameName + "\" />");
+ uploadFrame.css("display", "none");
+ uploadFrame.load(function (data) {
+ setTimeout(function () {
+ refreshCustomLogo();
+ uploadFrame.remove();
+ }, 1000);
+ });
+ $("body:first").append(uploadFrame);
+ submittingForm.attr("target", frameName);
+ });
+
+ $('#customLogo').change(function () {$("#logoUploadForm").submit()});
+
+ // trusted hosts event handling
+ $('#trustedHostSettings .adminTable').on('click', '.remove-trusted-host', function (e) {
+ e.preventDefault();
+ $(this).parent().parent().remove();
+ return false;
+ });
+ $('#trustedHostSettings .add-trusted-host').click(function (e) {
+ e.preventDefault();
+
+ // append new row to the table
+ $('#trustedHostSettings tbody').append('<tr>'
+ + '<td><input name="trusted_host" type="text" value=""/></td>'
+ + '<td><a href="#" class="remove-trusted-host">x</a></td>'
+ + '</tr>');
+ return false;
+ });
});
diff --git a/plugins/CoreAdminHome/templates/generalSettings.tpl b/plugins/CoreAdminHome/templates/generalSettings.tpl
index b4aa4c28d4..5a97d6fe91 100644
--- a/plugins/CoreAdminHome/templates/generalSettings.tpl
+++ b/plugins/CoreAdminHome/templates/generalSettings.tpl
@@ -2,245 +2,247 @@
{loadJavascriptTranslations plugins='UsersManager'}
{if $isSuperUser}
-<h2>{'General_GeneralSettings'|translate}</h2>
-
-{ajaxErrorDiv id=ajaxError}
-{ajaxLoadingDiv id=ajaxLoading}
-
-<table class="adminTable" style='width:900px;'>
-<tr>
- <td style='width:400px'>{'General_AllowPiwikArchivingToTriggerBrowser'|translate}</td>
- <td style='width:220px'>
- <fieldset>
- <label><input type="radio" value="1" name="enableBrowserTriggerArchiving"{if $enableBrowserTriggerArchiving==1} checked="checked"{/if} />
- {'General_Yes'|translate} <br />
- <span class="form-description">{'General_Default'|translate}</span>
- </label><br /><br />
-
- <label><input type="radio" value="0" name="enableBrowserTriggerArchiving"{if $enableBrowserTriggerArchiving==0} checked="checked"{/if} />
- {'General_No'|translate} <br />
- <span class="form-description">{'General_ArchivingTriggerDescription'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>":"</a>"}</span>
- </label>
- </fieldset>
- <td>
- {capture assign=browserArchivingHelp}
- {'General_ArchivingInlineHelp'|translate}<br />
- {'General_SeeTheOfficialDocumentationForMoreInformation'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>":"</a>"}
- {/capture}
- {$browserArchivingHelp|inlineHelp}
- </td>
-</tr>
-<tr>
- <td><label for="todayArchiveTimeToLive">{'General_ReportsContainingTodayWillBeProcessedAtMostEvery'|translate}</label></td>
- <td>
- {'General_NSeconds'|translate:"<input size='3' value='$todayArchiveTimeToLive' id='todayArchiveTimeToLive' />"}
- </td>
- <td width='450px'>
- {capture assign=archiveTodayTTLHelp}
- {if $showWarningCron}
- <strong>
- {'General_NewReportsWillBeProcessedByCron'|translate}<br/>
- {'General_ReportsWillBeProcessedAtMostEveryHour'|translate}
- {'General_IfArchivingIsFastYouCanSetupCronRunMoreOften'|translate}<br/>
- </strong>
- {/if}
- {'General_SmallTrafficYouCanLeaveDefault'|translate:10}<br />
- {'General_MediumToHighTrafficItIsRecommendedTo'|translate:1800:3600}
- {/capture}
- {$archiveTodayTTLHelp|inlineHelp}
- </td>
-</tr>
-<tr>
- <td style='width:400px'>{'CoreAdminHome_CheckReleaseGetVersion'|translate}</td>
- <td style='width:220px'>
- <fieldset>
- <label><input type="radio" value="0" name="enableBetaReleaseCheck"{if $enableBetaReleaseCheck==0} checked="checked"{/if} />
- {'CoreAdminHome_LatestStableRelease'|translate} <br />
- <span class="form-description">{'General_Recommended'|translate}</span>
- </label><br /><br />
-
- <label><input type="radio" value="1" name="enableBetaReleaseCheck"{if $enableBetaReleaseCheck==1} checked="checked"{/if} />
- {'CoreAdminHome_LatestBetaRelease'|translate} <br />
- <span class="form-description">{'CoreAdminHome_ForBetaTestersOnly'|translate}</span>
- </label>
- </fieldset>
- <td>
- {capture assign=checkReleaseHelp}
- {'CoreAdminHome_DevelopmentProcess'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/participate/development-process/' target='_blank'>":"</a>"}<br />
- {'CoreAdminHome_StableReleases'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/participate/user-feedback/' target='_blank'>":"</a>"}
- {/capture}
- {$checkReleaseHelp|inlineHelp}
- </td>
-</tr>
-</table>
+ <h2>{'General_GeneralSettings'|translate}</h2>
+ {ajaxErrorDiv id=ajaxError}
+ {ajaxLoadingDiv id=ajaxLoading}
+ <table class="adminTable" style='width:900px;'>
+ <tr>
+ <td style='width:400px'>{'General_AllowPiwikArchivingToTriggerBrowser'|translate}</td>
+ <td style='width:220px'>
+ <fieldset>
+ <label><input type="radio" value="1" name="enableBrowserTriggerArchiving"{if $enableBrowserTriggerArchiving==1} checked="checked"{/if} />
+ {'General_Yes'|translate} <br/>
+ <span class="form-description">{'General_Default'|translate}</span>
+ </label><br/><br/>
-<h2>{'CoreAdminHome_EmailServerSettings'|translate}</h2>
-<div id='emailSettings'>
-<table class="adminTable" style='width:600px;'>
- <tr>
- <td>{'General_UseSMTPServerForEmail'|translate}<br />
- <span class="form-description">{'General_SelectYesIfYouWantToSendEmailsViaServer'|translate}</span>
- </td>
- <td style='width:200px'>
- <label><input type="radio" name="mailUseSmtp" value="1" {if $mail.transport eq 'smtp'} checked {/if}/> {'General_Yes'|translate}</label>
- <label><input type="radio" name="mailUseSmtp" value="0" style ="margin-left:20px;" {if $mail.transport eq ''} checked {/if}/> {'General_No'|translate}</label>
- </td>
- </tr>
-</table>
-</div>
+ <label><input type="radio" value="0" name="enableBrowserTriggerArchiving"{if $enableBrowserTriggerArchiving==0} checked="checked"{/if} />
+ {'General_No'|translate} <br/>
+ <span class="form-description">{'General_ArchivingTriggerDescription'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>":"</a>"}</span>
+ </label>
+ </fieldset>
+ <td>
+ {capture assign=browserArchivingHelp}
+ {'General_ArchivingInlineHelp'|translate}
+ <br/>
+ {'General_SeeTheOfficialDocumentationForMoreInformation'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>":"</a>"}
+ {/capture}
+ {$browserArchivingHelp|inlineHelp}
+ </td>
+ </tr>
+ <tr>
+ <td><label for="todayArchiveTimeToLive">{'General_ReportsContainingTodayWillBeProcessedAtMostEvery'|translate}</label></td>
+ <td>
+ {'General_NSeconds'|translate:"<input size='3' value='$todayArchiveTimeToLive' id='todayArchiveTimeToLive' />"}
+ </td>
+ <td width='450px'>
+ {capture assign=archiveTodayTTLHelp}
+ {if $showWarningCron}
+ <strong>
+ {'General_NewReportsWillBeProcessedByCron'|translate}<br/>
+ {'General_ReportsWillBeProcessedAtMostEveryHour'|translate}
+ {'General_IfArchivingIsFastYouCanSetupCronRunMoreOften'|translate}<br/>
+ </strong>
+ {/if}
+ {'General_SmallTrafficYouCanLeaveDefault'|translate:10}
+ <br/>
+ {'General_MediumToHighTrafficItIsRecommendedTo'|translate:1800:3600}
+ {/capture}
+ {$archiveTodayTTLHelp|inlineHelp}
+ </td>
+ </tr>
+ <tr>
+ <td style='width:400px'>{'CoreAdminHome_CheckReleaseGetVersion'|translate}</td>
+ <td style='width:220px'>
+ <fieldset>
+ <label><input type="radio" value="0" name="enableBetaReleaseCheck"{if $enableBetaReleaseCheck==0} checked="checked"{/if} />
+ {'CoreAdminHome_LatestStableRelease'|translate} <br/>
+ <span class="form-description">{'General_Recommended'|translate}</span>
+ </label><br/><br/>
-<div id='smtpSettings'>
- <table class="adminTable" style='width:550px;'>
- <tr>
- <td><label for="mailHost">{'General_SmtpServerAddress'|translate}</label></td>
- <td style='width:200px'><input type="text" id="mailHost" value="{$mail.host|escape}"></td>
- </tr>
- <tr>
- <td><label for="mailPort">{'General_SmtpPort'|translate}</label><br />
- <span class="form-description">{'General_OptionalSmtpPort'|translate}</span></td>
- <td><input type="text" id="mailPort" value="{$mail.port}"></td>
- </tr>
- <tr>
- <td><label for="mailType">{'General_AuthenticationMethodSmtp'|translate}</label><br />
- <span class="form-description">{'General_OnlyUsedIfUserPwdIsSet'|translate}</span>
- </td>
- <td>
- <select id="mailType">
- <option value="" {if $mail.type eq ''} selected="selected" {/if}></option>
- <option id="plain" {if $mail.type eq 'Plain'} selected="selected" {/if} value="Plain">Plain</option>
- <option id="login" {if $mail.type eq 'Login'} selected="selected" {/if} value="Login"> Login</option>
- <option id="cram-md5" {if $mail.type eq 'Crammd5'} selected="selected" {/if} value="Crammd5"> Crammd5</option>
- </select>
- </td>
- </tr>
- <tr>
- <td><label for="mailUsername">{'General_SmtpUsername'|translate}</label><br />
- <span class="form-description">{'General_OnlyEnterIfRequired'|translate}</span></td>
- <td>
- <input type="text" id="mailUsername" value = "{$mail.username|escape}" />
- </td>
- </tr>
- <tr>
- <td><label for="mailPassword">{'General_SmtpPassword'|translate}</label><br />
+ <label><input type="radio" value="1" name="enableBetaReleaseCheck"{if $enableBetaReleaseCheck==1} checked="checked"{/if} />
+ {'CoreAdminHome_LatestBetaRelease'|translate} <br/>
+ <span class="form-description">{'CoreAdminHome_ForBetaTestersOnly'|translate}</span>
+ </label>
+ </fieldset>
+ <td>
+ {capture assign=checkReleaseHelp}
+ {'CoreAdminHome_DevelopmentProcess'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/participate/development-process/' target='_blank'>":"</a>"}
+ <br/>
+ {'CoreAdminHome_StableReleases'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/participate/user-feedback/' target='_blank'>":"</a>"}
+ {/capture}
+ {$checkReleaseHelp|inlineHelp}
+ </td>
+ </tr>
+ </table>
+ <h2>{'CoreAdminHome_EmailServerSettings'|translate}</h2>
+ <div id='emailSettings'>
+ <table class="adminTable" style='width:600px;'>
+ <tr>
+ <td>{'General_UseSMTPServerForEmail'|translate}<br/>
+ <span class="form-description">{'General_SelectYesIfYouWantToSendEmailsViaServer'|translate}</span>
+ </td>
+ <td style='width:200px'>
+ <label><input type="radio" name="mailUseSmtp" value="1" {if $mail.transport eq 'smtp'} checked {/if}/> {'General_Yes'|translate}</label>
+ <label><input type="radio" name="mailUseSmtp" value="0"
+ style="margin-left:20px;" {if $mail.transport eq ''} checked {/if}/> {'General_No'|translate}</label>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id='smtpSettings'>
+ <table class="adminTable" style='width:550px;'>
+ <tr>
+ <td><label for="mailHost">{'General_SmtpServerAddress'|translate}</label></td>
+ <td style='width:200px'><input type="text" id="mailHost" value="{$mail.host|escape}"></td>
+ </tr>
+ <tr>
+ <td><label for="mailPort">{'General_SmtpPort'|translate}</label><br/>
+ <span class="form-description">{'General_OptionalSmtpPort'|translate}</span></td>
+ <td><input type="text" id="mailPort" value="{$mail.port}"></td>
+ </tr>
+ <tr>
+ <td><label for="mailType">{'General_AuthenticationMethodSmtp'|translate}</label><br/>
+ <span class="form-description">{'General_OnlyUsedIfUserPwdIsSet'|translate}</span>
+ </td>
+ <td>
+ <select id="mailType">
+ <option value="" {if $mail.type eq ''} selected="selected" {/if}></option>
+ <option id="plain" {if $mail.type eq 'Plain'} selected="selected" {/if} value="Plain">Plain</option>
+ <option id="login" {if $mail.type eq 'Login'} selected="selected" {/if} value="Login"> Login</option>
+ <option id="cram-md5" {if $mail.type eq 'Crammd5'} selected="selected" {/if} value="Crammd5"> Crammd5</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td><label for="mailUsername">{'General_SmtpUsername'|translate}</label><br/>
+ <span class="form-description">{'General_OnlyEnterIfRequired'|translate}</span></td>
+ <td>
+ <input type="text" id="mailUsername" value="{$mail.username|escape}"/>
+ </td>
+ </tr>
+ <tr>
+ <td><label for="mailPassword">{'General_SmtpPassword'|translate}</label><br/>
<span class="form-description">{'General_OnlyEnterIfRequiredPassword'|translate}<br/>
- {'General_WarningPasswordStored'|translate:"<strong>":"</strong>"}</span>
- </td>
- <td>
- <input type="password" id="mailPassword" value = "{$mail.password|escape}" />
- </td>
- </tr>
- <tr>
- <td><label for="mailEncryption">{'General_SmtpEncryption'|translate}</label><br />
- <span class="form-description">{'General_EncryptedSmtpTransport'|translate}</span></td>
- <td>
- <select id="mailEncryption">
- <option value="" {if $mail.encryption eq ''} selected="selected" {/if}></option>
- <option id="ssl" {if $mail.encryption eq 'ssl'} selected="selected" {/if} value="ssl">SSL</option>
- <option id="tls" {if $mail.encryption eq 'tls'} selected="selected" {/if} value="tls">TLS</option>
- </select>
- </td>
- </tr>
- </table>
-</div>
-
-<div class="ui-confirm" id="confirmTrustedHostChange">
- <h2>{'CoreAdminHome_TrustedHostConfirm'|translate}</h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_No'|translate}" />
-</div>
-
-<h2 id="trustedHostsSection">{'CoreAdminHome_TrustedHostSettings'|translate}</h2>
-<div id='trustedHostSettings'>
-{* untrusted host warning (display again) *}
- {if isset($isValidHost) && isset($invalidHostMessage) && !$isValidHost}
- <div class="ajaxSuccess">
- <a style="float:right" href="http://piwik.org/faq/troubleshooting/#faq_171" target="_blank"><img src="themes/default/images/help_grey.png" /></a>
- <strong>{'General_Warning'|translate}:&nbsp;</strong>{$invalidHostMessage}
- </div>
- {/if}
- {if count($trustedHosts) eq 1 && (!isset($isValidHost) || $isValidHost)}
- {'CoreAdminHome_PiwikIsInstalledAt'|translate}:&nbsp;&nbsp;<input name="trusted_host" type="text" value="{$trustedHosts[0]}"/>
- {else}
- <p>{'CoreAdminHome_PiwikIsInstalledAt'|translate}:</p>
- <table class="adminTable">
+ {'General_WarningPasswordStored'|translate:"<strong>":"</strong>"}</span>
+ </td>
+ <td>
+ <input type="password" id="mailPassword" value="{$mail.password|escape}"/>
+ </td>
+ </tr>
<tr>
- <th style="width:250px">{'CoreAdminHome_ValidPiwikHostname'|translate}</th>
- <th style="width:10px">&nbsp;</th>
+ <td><label for="mailEncryption">{'General_SmtpEncryption'|translate}</label><br/>
+ <span class="form-description">{'General_EncryptedSmtpTransport'|translate}</span></td>
+ <td>
+ <select id="mailEncryption">
+ <option value="" {if $mail.encryption eq ''} selected="selected" {/if}></option>
+ <option id="ssl" {if $mail.encryption eq 'ssl'} selected="selected" {/if} value="ssl">SSL</option>
+ <option id="tls" {if $mail.encryption eq 'tls'} selected="selected" {/if} value="tls">TLS</option>
+ </select>
+ </td>
</tr>
- {foreach from=$trustedHosts item=host key=hostIdx}
+ </table>
+ </div>
+ <div class="ui-confirm" id="confirmTrustedHostChange">
+ <h2>{'CoreAdminHome_TrustedHostConfirm'|translate}</h2>
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_No'|translate}"/>
+ </div>
+ <h2 id="trustedHostsSection">{'CoreAdminHome_TrustedHostSettings'|translate}</h2>
+ <div id='trustedHostSettings'>
+ {* untrusted host warning (display again) *}
+ {if isset($isValidHost) && isset($invalidHostMessage) && !$isValidHost}
+ <div class="ajaxSuccess">
+ <a style="float:right" href="http://piwik.org/faq/troubleshooting/#faq_171" target="_blank"><img src="themes/default/images/help_grey.png"/></a>
+ <strong>{'General_Warning'|translate}:&nbsp;</strong>{$invalidHostMessage}
+ </div>
+ {/if}
+ {if count($trustedHosts) eq 1 && (!isset($isValidHost) || $isValidHost)}
+ {'CoreAdminHome_PiwikIsInstalledAt'|translate}:&nbsp;&nbsp;
+ <input name="trusted_host" type="text" value="{$trustedHosts[0]}"/>
+ {else}
+ <p>{'CoreAdminHome_PiwikIsInstalledAt'|translate}:</p>
+ <table class="adminTable">
<tr>
- <td><input name="trusted_host" type="text" value="{$host}"/></td>
- <td>
- <a href="#" class="remove-trusted-host">x</a>
- </td>
+ <th style="width:250px">{'CoreAdminHome_ValidPiwikHostname'|translate}</th>
+ <th style="width:10px">&nbsp;</th>
</tr>
- {/foreach}
+ {foreach from=$trustedHosts item=host key=hostIdx}
+ <tr>
+ <td><input name="trusted_host" type="text" value="{$host}"/></td>
+ <td>
+ <a href="#" class="remove-trusted-host">x</a>
+ </td>
+ </tr>
+ {/foreach}
+ </table>
+ <div class="adminTable add-trusted-host-container">
+ <a href="#" class="add-trusted-host"><em>{'General_Add'|translate}</em></a>
+ </div>
+ {/if}
+ </div>
+ <h2>{'CoreAdminHome_BrandingSettings'|translate}</h2>
+ <div id='brandSettings'>
+ {'CoreAdminHome_CustomLogoHelpText'|translate}
+ <table class="adminTable" style='width:600px;'>
+ <tr>
+ <td> {'CoreAdminHome_UseCustomLogo'|translate}</td>
+ <td style='width:200px'>
+ <label><input type="radio" name="useCustomLogo" value="1" {if $branding.use_custom_logo == 1} checked {/if}/> {'General_Yes'|translate}
+ </label>
+ <label><input type="radio" name="useCustomLogo" value="0"
+ style="margin-left:20px;" {if $branding.use_custom_logo == 0} checked {/if}/> {'General_No'|translate}</label>
+ </td>
+ </tr>
</table>
- <div class="adminTable add-trusted-host-container">
- <a href="#" class="add-trusted-host"><em>{'General_Add'|translate}</em></a>
- </div>
- {/if}
-</div>
-
-<h2>{'CoreAdminHome_BrandingSettings'|translate}</h2>
-<div id='brandSettings'>
-{'CoreAdminHome_CustomLogoHelpText'|translate}
-<table class="adminTable" style='width:600px;'>
- <tr>
- <td> {'CoreAdminHome_UseCustomLogo'|translate}</td>
- <td style='width:200px'>
- <label><input type="radio" name="useCustomLogo" value="1" {if $branding.use_custom_logo == 1} checked {/if}/> {'General_Yes'|translate}</label>
- <label><input type="radio" name="useCustomLogo" value="0" style ="margin-left:20px;" {if $branding.use_custom_logo == 0} checked {/if}/> {'General_No'|translate}</label>
- </td>
- </tr>
-</table>
-</div>
-
-<div id='logoSettings'>
- {capture assign=giveUsFeedbackText}"{'General_GiveUsYourFeedback'|translate}"{/capture}
- {capture assign=customLogoHelp}
- {'CoreAdminHome_CustomLogoFeedbackInfo'|translate:$giveUsFeedbackText:"<a href='?module=CorePluginsAdmin&action=index' target='_blank'>":"</a>"}
- {/capture}
- {$customLogoHelp|inlineHelp}
- <form id="logoUploadForm" method="post" enctype="multipart/form-data" action="index.php?module=CoreAdminHome&format=json&action=uploadCustomLogo">
- <table class="adminTable" style='width:550px;'>
- <tr>
- {if $logosWriteable}
- <td><label for="customLogo">{'CoreAdminHome_LogoUpload'|translate}:<br />
- <span class="form-description">{'CoreAdminHome_LogoUploadDescription'|translate:"JPG / PNG / GIF":110}</span></label></td>
- <td style='width:200px'>
- <input name="customLogo" type="file" id="customLogo" /><img src="themes/logo.png?r={math equation='rand(10,1000)'}" id="currentLogo" height="150"/>
- </td>
- {else}
- <td><span class="ajaxSuccess">{'CoreAdminHome_LogoNotWriteable'|translate:"<ul style='list-style: disc inside;'><li>/themes/</li><li>/themes/logo.png</li><li>/themes/logo-header.png</li></ul>"}</span></td>
- {/if}
- </tr>
- </table>
- </form>
-</div>
-
-<input type="submit" value="{'General_Save'|translate}" id="generalSettingsSubmit" class="submit" />
-<br /><br />
-
-{capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteDataSettings'|translate}{/capture}
-<h2>{'PrivacyManager_DeleteDataSettings'|translate}</h2>
-<p>
- {'PrivacyManager_DeleteDataDescription'|translate} {'PrivacyManager_DeleteDataDescription2'|translate}
-<br/>
- <a href='{url module="PrivacyManager" action="privacySettings"}#deleteLogsAnchor'>
- {'PrivacyManager_ClickHereSettings'|translate:"'$clickDeleteLogSettings'"}
- </a>
-</p>
+ </div>
+ <div id='logoSettings'>
+ {capture assign=giveUsFeedbackText}"{'General_GiveUsYourFeedback'|translate}"{/capture}
+ {capture assign=customLogoHelp}
+ {'CoreAdminHome_CustomLogoFeedbackInfo'|translate:$giveUsFeedbackText:"<a href='?module=CorePluginsAdmin&action=index' target='_blank'>":"</a>"}
+ {/capture}
+ {$customLogoHelp|inlineHelp}
+ <form id="logoUploadForm" method="post" enctype="multipart/form-data" action="index.php?module=CoreAdminHome&format=json&action=uploadCustomLogo">
+ <table class="adminTable" style='width:550px;'>
+ <tr>
+ {if $logosWriteable}
+ <td><label for="customLogo">{'CoreAdminHome_LogoUpload'|translate}:<br/>
+ <span class="form-description">{'CoreAdminHome_LogoUploadDescription'|translate:"JPG / PNG / GIF":110}</span></label></td>
+ <td style='width:200px'>
+ <input name="customLogo" type="file" id="customLogo"/><img src="themes/logo.png?r={math equation='rand(10,1000)'}" id="currentLogo"
+ height="150"/>
+ </td>
+ {else}
+ <td>
+ <span class="ajaxSuccess">{'CoreAdminHome_LogoNotWriteable'|translate:"<ul style='list-style: disc inside;'><li>/themes/</li><li>/themes/logo.png</li><li>/themes/logo-header.png</li></ul>"}</span>
+ </td>
+ {/if}
+ </tr>
+ </table>
+ </form>
+ </div>
+ <input type="submit" value="{'General_Save'|translate}" id="generalSettingsSubmit" class="submit"/>
+ <br/>
+ <br/>
+ {capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteDataSettings'|translate}{/capture}
+ <h2>{'PrivacyManager_DeleteDataSettings'|translate}</h2>
+ <p>
+ {'PrivacyManager_DeleteDataDescription'|translate} {'PrivacyManager_DeleteDataDescription2'|translate}
+ <br/>
+ <a href='{url module="PrivacyManager" action="privacySettings"}#deleteLogsAnchor'>
+ {'PrivacyManager_ClickHereSettings'|translate:"'$clickDeleteLogSettings'"}
+ </a>
+ </p>
{/if}
<h2>{'CoreAdminHome_OptOutForYourVisitors'|translate}</h2>
<p>{'CoreAdminHome_OptOutExplanation'|translate}
-{capture name=optOutUrl}{$piwikUrl}index.php?module=CoreAdminHome&action=optOut&language={$language}{/capture}
-{assign var=optOutUrl value=$smarty.capture.optOutUrl}
-{capture name=iframeOptOut}<iframe frameborder="no" width="600px" height="200px" src="{$smarty.capture.optOutUrl}"></iframe>{/capture}
-<code>{$smarty.capture.iframeOptOut|escape:'html'}</code>
-<br/>
-{'CoreAdminHome_OptOutExplanationBis'|translate:"<a href='$optOutUrl' target='_blank'>":"</a>"}
+ {capture name=optOutUrl}{$piwikUrl}index.php?module=CoreAdminHome&action=optOut&language={$language}{/capture}
+ {assign var=optOutUrl value=$smarty.capture.optOutUrl}
+ {capture name=iframeOptOut}
+ <iframe frameborder="no" width="600px" height="200px" src="{$smarty.capture.optOutUrl}"></iframe>{/capture}
+ <code>{$smarty.capture.iframeOptOut|escape:'html'}</code>
+ <br/>
+ {'CoreAdminHome_OptOutExplanationBis'|translate:"<a href='$optOutUrl' target='_blank'>":"</a>"}
</p>
{include file="CoreAdminHome/templates/footer.tpl"}
diff --git a/plugins/CoreAdminHome/templates/header.tpl b/plugins/CoreAdminHome/templates/header.tpl
index acfa47e3d4..84a8407b0a 100644
--- a/plugins/CoreAdminHome/templates/header.tpl
+++ b/plugins/CoreAdminHome/templates/header.tpl
@@ -1,80 +1,83 @@
<!DOCTYPE html>
-<!--[if lt IE 9 ]><html class="old-ie"> <![endif]-->
-<!--[if (gte IE 9)|!(IE)]><!--><html><!--<![endif]-->
+<!--[if lt IE 9 ]>
+<html class="old-ie"> <![endif]-->
+<!--[if (gte IE 9)|!(IE)]><!-->
+<html><!--<![endif]-->
<head>
-<title>{if !$isCustomLogo}Piwik &rsaquo; {/if}{'CoreAdminHome_Administration'|translate}</title>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<meta name="generator" content="Piwik - Open Source Web Analytics" />
-<link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico" />
+ <title>{if !$isCustomLogo}Piwik &rsaquo; {/if}{'CoreAdminHome_Administration'|translate}</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <meta name="generator" content="Piwik - Open Source Web Analytics"/>
+ <link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico"/>
-{loadJavascriptTranslations plugins='CoreAdminHome CoreHome'}
+ {loadJavascriptTranslations plugins='CoreAdminHome CoreHome'}
-{include file="CoreHome/templates/js_global_variables.tpl"}
-{include file="CoreHome/templates/js_css_includes.tpl"}
-<!--[if IE]>
-<link rel="stylesheet" type="text/css" href="themes/default/ieonly.css" />
-<![endif]-->
-{include file="CoreHome/templates/iframe_buster_header.tpl"}
+ {include file="CoreHome/templates/js_global_variables.tpl"}
+ {include file="CoreHome/templates/js_css_includes.tpl"}
+ <!--[if IE]>
+ <link rel="stylesheet" type="text/css" href="themes/default/ieonly.css"/>
+ <![endif]-->
+ {include file="CoreHome/templates/iframe_buster_header.tpl"}
</head>
<body>
{include file="CoreHome/templates/iframe_buster_body.tpl"}
<div id="root">
-{if !isset($showTopMenu) || $showTopMenu}
-{assign var=showSitesSelection value=false}
-{assign var=showPeriodSelection value=false}
-{include file="CoreHome/templates/top_bar.tpl"}
-{/if}
+ {if !isset($showTopMenu) || $showTopMenu}
+ {assign var=showSitesSelection value=false}
+ {assign var=showPeriodSelection value=false}
+ {include file="CoreHome/templates/top_bar.tpl"}
+ {/if}
-<div id="header">
-{include file="CoreHome/templates/logo.tpl"}
-{if $showPeriodSelection}{include file="CoreHome/templates/period_select.tpl"}{/if}
-{include file="CoreHome/templates/js_disabled_notice.tpl"}
-</div>
+ <div id="header">
+ {include file="CoreHome/templates/logo.tpl"}
+ {if $showPeriodSelection}{include file="CoreHome/templates/period_select.tpl"}{/if}
+ {include file="CoreHome/templates/js_disabled_notice.tpl"}
+ </div>
-{ajaxRequestErrorDiv}
-<div id="container">
-{if !isset($showMenu) || $showMenu}
- {include file="CoreAdminHome/templates/menu.tpl"}
-{/if}
+ {ajaxRequestErrorDiv}
+ <div id="container">
+ {if !isset($showMenu) || $showMenu}
+ {include file="CoreAdminHome/templates/menu.tpl"}
+ {/if}
-<div id="content" class="admin">
+ <div id="content" class="admin">
-{include file="CoreHome/templates/header_message.tpl"}
+ {include file="CoreHome/templates/header_message.tpl"}
-{if !empty($configFileNotWritable)}
- <div class="ajaxSuccess" style="display:inline-block">
- {'General_ConfigFileIsNotWritable'|translate:"(config/config.ini.php)":"<br/>"}
- </div>
-{elseif preg_match('/updated=[1-9]/', $url)}
- <div class="ajaxSuccess" style="display:inline-block">
- {'General_YourChangesHaveBeenSaved'|translate}
- </div>
-{/if}
+ {if !empty($configFileNotWritable)}
+ <div class="ajaxSuccess" style="display:inline-block">
+ {'General_ConfigFileIsNotWritable'|translate:"(config/config.ini.php)":"<br/>"}
+ </div>
+ {elseif preg_match('/updated=[1-9]/', $url)}
+ <div class="ajaxSuccess" style="display:inline-block">
+ {'General_YourChangesHaveBeenSaved'|translate}
+ </div>
+ {/if}
-{if !empty($statisticsNotRecorded)}
- <div class="ajaxSuccess" style="display:inline-block">
- {'General_StatisticsAreNotRecorded'|translate}
- </div>
-{/if}
+ {if !empty($statisticsNotRecorded)}
+ <div class="ajaxSuccess" style="display:inline-block">
+ {'General_StatisticsAreNotRecorded'|translate}
+ </div>
+ {/if}
-<div class="ui-confirm" id="alert">
- <h2></h2>
- <input role="no" type="button" value="{'General_Ok'|translate}" />
-</div>
+ <div class="ui-confirm" id="alert">
+ <h2></h2>
+ <input role="no" type="button" value="{'General_Ok'|translate}"/>
+ </div>
-{include file="CoreHome/templates/warning_invalid_host.tpl"}
+ {include file="CoreHome/templates/warning_invalid_host.tpl"}
-{* missing plugins warning *}
-{if $isSuperUser && !empty($missingPluginsWarning)}
-<div class="ajaxSuccess">
- <strong>{'General_Warning'|translate}:&nbsp;</strong>{$missingPluginsWarning}
-</div>
-{/if}
+ {* missing plugins warning *}
+ {if $isSuperUser && !empty($missingPluginsWarning)}
+ <div class="ajaxSuccess">
+ <strong>{'General_Warning'|translate}:&nbsp;</strong>{$missingPluginsWarning}
+ </div>
+ {/if}
-{* old GeoIP plugin warning *}
-{if $isSuperUser && !empty($usingOldGeoIPPlugin)}
-<div class="ajaxSuccess">
- <strong>{'General_Warning'|translate}:&nbsp;</strong>{'UserCountry_OldGeoIPWarning'|translate:'<a href="index.php?module=CorePluginsAdmin&action=index&idSite=1&period=day&date=yesterday">':'</a>':'<a href="index.php?module=UserCountry&action=adminIndex&idSite=1&period=day&date=yesterday#location-providers">':'</a>':'<a href="http://piwik.org/faq/how-to/#faq_167">':'</a>':'<a href="http://piwik.org/faq/how-to/#faq_59">':'</a>'}
-</div>
-{/if}
+ {* old GeoIP plugin warning *}
+ {if $isSuperUser && !empty($usingOldGeoIPPlugin)}
+ <div class="ajaxSuccess">
+ <strong>{'General_Warning'|translate}
+ :&nbsp;</strong>{'UserCountry_OldGeoIPWarning'|translate:'<a href="index.php?module=CorePluginsAdmin&action=index&idSite=1&period=day&date=yesterday">':'</a>':'<a href="index.php?module=UserCountry&action=adminIndex&idSite=1&period=day&date=yesterday#location-providers">':'</a>':'<a href="http://piwik.org/faq/how-to/#faq_167">':'</a>':'<a href="http://piwik.org/faq/how-to/#faq_59">':'</a>'}
+ </div>
+ {/if}
diff --git a/plugins/CoreAdminHome/templates/jsTrackingGenerator.css b/plugins/CoreAdminHome/templates/jsTrackingGenerator.css
index 7f242ddcc1..2aeafbbe6b 100644
--- a/plugins/CoreAdminHome/templates/jsTrackingGenerator.css
+++ b/plugins/CoreAdminHome/templates/jsTrackingGenerator.css
@@ -1,8 +1,8 @@
-#javascript-output-section textarea,#image-link-output-section textarea {
- width:100%;
- display: block;
- color: #111;
- font-family: "Courier New", Courier, monospace;
+#javascript-output-section textarea, #image-link-output-section textarea {
+ width: 100%;
+ display: block;
+ color: #111;
+ font-family: "Courier New", Courier, monospace;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
@@ -10,67 +10,70 @@
}
#javascript-output-section textarea {
- height: 256px;
+ height: 256px;
}
#image-link-output-section textarea {
- height: 128px;
+ height: 128px;
}
label {
- margin-right: .35em;
- display:inline-block;
+ margin-right: .35em;
+ display: inline-block;
}
-#optional-js-tracking-options>tbody>tr>td,#image-tracking-section>tbody>tr>td {
- width:488px;
- max-width:488px;
+#optional-js-tracking-options>tbody>tr>td, #image-tracking-section>tbody>tr>td {
+ width: 488px;
+ max-width: 488px;
}
-.custom-variable-name,.custom-variable-value {
- width:100px;
+.custom-variable-name, .custom-variable-value {
+ width: 100px;
}
h3 {
- margin-top:0;
+ margin-top: 0;
}
.small-form-description {
- color:#666;
- font-size:1em;
- font-style:italic;
- margin-left:4em;
+ color: #666;
+ font-size: 1em;
+ font-style: italic;
+ margin-left: 4em;
}
.tracking-option-section {
- margin-bottom:1.5em;
+ margin-bottom: 1.5em;
}
-#javascript-output-section,#image-link-output-section {
- padding-top:1em;
+#javascript-output-section, #image-link-output-section {
+ padding-top: 1em;
}
-#optional-js-tracking-options th,#image-tracking-section th {
- text-align:left;
+#optional-js-tracking-options th, #image-tracking-section th {
+ text-align: left;
}
-#js-visitor-cv-extra,#js-page-cv-extra,#js-campaign-query-param-extra {
- margin-left:1em;
+#js-visitor-cv-extra, #js-page-cv-extra, #js-campaign-query-param-extra {
+ margin-left: 1em;
}
-#js-visitor-cv-extra td,#js-page-cv-extra td,#js-campaign-query-param-extra td {
- vertical-align:middle;
+
+#js-visitor-cv-extra td, #js-page-cv-extra td, #js-campaign-query-param-extra td {
+ vertical-align: middle;
}
.revenue {
- width:32px;
+ width: 32px;
}
+
.goal-picker {
- height:1.2em;
+ height: 1.2em;
}
+
.goal-picker select {
- width:128px;
+ width: 128px;
}
#js-campaign-query-param-extra input {
- width:72px;
+ width: 72px;
}
diff --git a/plugins/CoreAdminHome/templates/jsTrackingGenerator.js b/plugins/CoreAdminHome/templates/jsTrackingGenerator.js
index 10080fd814..d94a037356 100644
--- a/plugins/CoreAdminHome/templates/jsTrackingGenerator.js
+++ b/plugins/CoreAdminHome/templates/jsTrackingGenerator.js
@@ -5,228 +5,204 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function($) {
-
-$(document).ready(function() {
-
- var piwikHost = window.location.host,
- piwikPath = location.pathname.substring(0, location.pathname.lastIndexOf('/'));
-
- //
- // utility methods
- //
-
- // returns JavaScript code for tracking custom variables based on an array of
- // custom variable name-value pairs (so an array of 2-element arrays) and
- // a scope (either 'visit' or 'page')
- var getCustomVariableJS = function(customVariables, scope)
- {
- var result = '';
- for (var i = 0; i != 5; ++i)
- {
- if (customVariables[i])
- {
- var key = customVariables[i][0],
- value = customVariables[i][1];
- result += ' _paq.push(["setCustomVariable", ' + (i + 1) + ', ' + JSON.stringify(key) + ', '
- + JSON.stringify(value) + ', ' + JSON.stringify(scope) + ']);\n';
- }
- }
- return result;
- };
-
- // gets the list of custom variables entered by the user in a custom variable
- // section
- var getCustomVariables = function (sectionId)
- {
- var customVariableNames = $('.custom-variable-name', '#' + sectionId),
- customVariableValues = $('.custom-variable-value', '#' + sectionId);
-
- var result = [];
- if ($('.section-toggler-link', '#' + sectionId).is(':checked'))
- {
- for (var i = 0; i != customVariableNames.length; ++i)
- {
- var name = $(customVariableNames[i]).val();
-
- result[i] = null;
- if (name)
- {
- result[i] = [name, $(customVariableValues[i]).val()];
- }
- }
- }
- return result;
- };
-
- // quickly gets the host + port from a url
- var getHostNameFromUrl = function(url)
- {
- var element = $('<a></a>')[0];
- element.href = url;
- return element.hostname;
- };
-
- // get preloaded server-side data necessary for code generation
- var dataElement = $('#js-tracking-generator-data'),
- currencySymbols = JSON.parse(dataElement.attr('data-currencies')),
- siteUrls = {},
- siteCurrencies = {},
- allGoals = {},
- noneText = $('#image-tracker-goal>option').text();
-
- // queries Piwik for needed site info for one site
- var getSiteData = function (idSite, sectionSelect, callback)
- {
- // if data is already loaded, don't do an AJAX request
- if (siteUrls[idSite]
- && siteCurrencies[idSite]
- && typeof allGoals[idSite] !== 'undefined')
- {
- callback();
- return;
- }
-
- // disable section
- $(sectionSelect).find('input,select,textarea').attr('disabled', 'disabled');
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.setBulkRequests(
- // get site info (for currency)
- {
- module: 'API',
- method: 'SitesManager.getSiteFromId',
- idSite: idSite
- },
-
- // get site urls
- {
- module: 'API',
- method: 'SitesManager.getSiteUrlsFromId',
- idSite: idSite
- },
-
- // get site goals
- {
- module: 'API',
- method: 'Goals.getGoals',
- idSite: idSite
- }
- );
- ajaxRequest.setCallback(function (data) {
- for (var i = 0; i != data.length; ++i)
- {
- data[i] = JSON.parse(data[i]);
- }
-
- // set data
- var currency = data[0][0].currency || '';
- siteCurrencies[idSite] = currencySymbols[currency.toUpperCase()];
- siteUrls[idSite] = data[1] || [];
- allGoals[idSite] = data[2] || [];
-
- // re-enable controls
- $(sectionSelect).find('input,select,textarea').removeAttr('disabled');
-
- callback();
- });
- ajaxRequest.setFormat('json');
- ajaxRequest.send(false);
- };
-
- // resets the select options of a goal select using a site ID
- var resetGoalSelectItems = function (idsite, id)
- {
- var selectElement = $('#' + id).html('');
-
- selectElement.append($('<option value=""></option>').text(noneText));
-
- var goals = allGoals[idsite] || [];
- for (var key in goals)
- {
- var goal = goals[key];
- selectElement.append($('<option/>').attr('value', goal.idgoal).text(goal.name));
- }
-
- // set currency string
- $('#' + id).parent().find('.currency').text(siteCurrencies[idsite]);
- };
-
- // function that generates JS code
- var generateJsCode = function()
- {
- // get data
- var idSite = $('#js-tracker-website .custom_select_main_link').attr('siteid'),
- groupPageTitlesByDomain = $('#javascript-tracking-group-by-domain').is(':checked'),
- mergeSubdomains = $('#javascript-tracking-all-subdomains').is(':checked'),
- mergeAliasUrls = $('#javascript-tracking-all-aliases').is(':checked'),
- visitorCustomVariables = getCustomVariables('javascript-tracking-visitor-cv'),
- pageCustomVariables = getCustomVariables('javascript-tracking-page-cv'),
- customCampaignNameQueryParam = null,
- customCampaignKeywordParam = null,
- doNotTrack = $('#javascript-tracking-do-not-track').is(':checked');
-
- if ($('#custom-campaign-query-params-check').is(':checked'))
- {
- customCampaignNameQueryParam = $('#custom-campaign-name-query-param').val();
- customCampaignKeywordParam = $('#custom-campaign-keyword-query-param').val();
- }
-
- // generate JS
- var result = '<!-- Piwik -->\n\
+(function ($) {
+
+ $(document).ready(function () {
+
+ var piwikHost = window.location.host,
+ piwikPath = location.pathname.substring(0, location.pathname.lastIndexOf('/'));
+
+ //
+ // utility methods
+ //
+
+ // returns JavaScript code for tracking custom variables based on an array of
+ // custom variable name-value pairs (so an array of 2-element arrays) and
+ // a scope (either 'visit' or 'page')
+ var getCustomVariableJS = function (customVariables, scope) {
+ var result = '';
+ for (var i = 0; i != 5; ++i) {
+ if (customVariables[i]) {
+ var key = customVariables[i][0],
+ value = customVariables[i][1];
+ result += ' _paq.push(["setCustomVariable", ' + (i + 1) + ', ' + JSON.stringify(key) + ', '
+ + JSON.stringify(value) + ', ' + JSON.stringify(scope) + ']);\n';
+ }
+ }
+ return result;
+ };
+
+ // gets the list of custom variables entered by the user in a custom variable
+ // section
+ var getCustomVariables = function (sectionId) {
+ var customVariableNames = $('.custom-variable-name', '#' + sectionId),
+ customVariableValues = $('.custom-variable-value', '#' + sectionId);
+
+ var result = [];
+ if ($('.section-toggler-link', '#' + sectionId).is(':checked')) {
+ for (var i = 0; i != customVariableNames.length; ++i) {
+ var name = $(customVariableNames[i]).val();
+
+ result[i] = null;
+ if (name) {
+ result[i] = [name, $(customVariableValues[i]).val()];
+ }
+ }
+ }
+ return result;
+ };
+
+ // quickly gets the host + port from a url
+ var getHostNameFromUrl = function (url) {
+ var element = $('<a></a>')[0];
+ element.href = url;
+ return element.hostname;
+ };
+
+ // get preloaded server-side data necessary for code generation
+ var dataElement = $('#js-tracking-generator-data'),
+ currencySymbols = JSON.parse(dataElement.attr('data-currencies')),
+ siteUrls = {},
+ siteCurrencies = {},
+ allGoals = {},
+ noneText = $('#image-tracker-goal>option').text();
+
+ // queries Piwik for needed site info for one site
+ var getSiteData = function (idSite, sectionSelect, callback) {
+ // if data is already loaded, don't do an AJAX request
+ if (siteUrls[idSite]
+ && siteCurrencies[idSite]
+ && typeof allGoals[idSite] !== 'undefined') {
+ callback();
+ return;
+ }
+
+ // disable section
+ $(sectionSelect).find('input,select,textarea').attr('disabled', 'disabled');
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.setBulkRequests(
+ // get site info (for currency)
+ {
+ module: 'API',
+ method: 'SitesManager.getSiteFromId',
+ idSite: idSite
+ },
+
+ // get site urls
+ {
+ module: 'API',
+ method: 'SitesManager.getSiteUrlsFromId',
+ idSite: idSite
+ },
+
+ // get site goals
+ {
+ module: 'API',
+ method: 'Goals.getGoals',
+ idSite: idSite
+ }
+ );
+ ajaxRequest.setCallback(function (data) {
+ for (var i = 0; i != data.length; ++i) {
+ data[i] = JSON.parse(data[i]);
+ }
+
+ // set data
+ var currency = data[0][0].currency || '';
+ siteCurrencies[idSite] = currencySymbols[currency.toUpperCase()];
+ siteUrls[idSite] = data[1] || [];
+ allGoals[idSite] = data[2] || [];
+
+ // re-enable controls
+ $(sectionSelect).find('input,select,textarea').removeAttr('disabled');
+
+ callback();
+ });
+ ajaxRequest.setFormat('json');
+ ajaxRequest.send(false);
+ };
+
+ // resets the select options of a goal select using a site ID
+ var resetGoalSelectItems = function (idsite, id) {
+ var selectElement = $('#' + id).html('');
+
+ selectElement.append($('<option value=""></option>').text(noneText));
+
+ var goals = allGoals[idsite] || [];
+ for (var key in goals) {
+ var goal = goals[key];
+ selectElement.append($('<option/>').attr('value', goal.idgoal).text(goal.name));
+ }
+
+ // set currency string
+ $('#' + id).parent().find('.currency').text(siteCurrencies[idsite]);
+ };
+
+ // function that generates JS code
+ var generateJsCode = function () {
+ // get data
+ var idSite = $('#js-tracker-website .custom_select_main_link').attr('siteid'),
+ groupPageTitlesByDomain = $('#javascript-tracking-group-by-domain').is(':checked'),
+ mergeSubdomains = $('#javascript-tracking-all-subdomains').is(':checked'),
+ mergeAliasUrls = $('#javascript-tracking-all-aliases').is(':checked'),
+ visitorCustomVariables = getCustomVariables('javascript-tracking-visitor-cv'),
+ pageCustomVariables = getCustomVariables('javascript-tracking-page-cv'),
+ customCampaignNameQueryParam = null,
+ customCampaignKeywordParam = null,
+ doNotTrack = $('#javascript-tracking-do-not-track').is(':checked');
+
+ if ($('#custom-campaign-query-params-check').is(':checked')) {
+ customCampaignNameQueryParam = $('#custom-campaign-name-query-param').val();
+ customCampaignKeywordParam = $('#custom-campaign-keyword-query-param').val();
+ }
+
+ // generate JS
+ var result = '<!-- Piwik -->\n\
<script type="text/javascript">\n\
var _paq = _paq || [];\n';
-
- if (groupPageTitlesByDomain)
- {
- result += ' _paq.push(["setDocumentTitle", document.domain + "/" + document.title]);\n';
- }
-
- if (mergeSubdomains)
- {
- var mainHostAllSub = '*.' + getHostNameFromUrl(siteUrls[idSite][0]);
- result += ' _paq.push(["setCookieDomain", ' + JSON.stringify(mainHostAllSub) + ']);\n';
- }
-
- if (mergeAliasUrls)
- {
- var siteHosts = [];
- for (var i = 0; i != siteUrls[idSite].length; ++i)
- {
- siteHosts[i] = '*.' + getHostNameFromUrl(siteUrls[idSite][i]);
- }
- result += ' _paq.push(["setDomains", ' + JSON.stringify(siteHosts) + ']);\n';
- }
-
- if (visitorCustomVariables.length)
- {
- result += ' // you can set up to 5 custom variables for each visitor\n';
- result += getCustomVariableJS(visitorCustomVariables, 'visit');
- }
-
- if (pageCustomVariables.length)
- {
- result += ' // you can set up to 5 custom variables for each action (page view, ' +
- 'download, click, site search)\n';
- result += getCustomVariableJS(pageCustomVariables, 'page');
- }
-
- if (customCampaignNameQueryParam)
- {
- result += ' _paq.push(["setCampaignNameKey", ' + JSON.stringify(customCampaignNameQueryParam) + ']);\n';
- }
-
- if (customCampaignKeywordParam)
- {
- result += ' _paq.push(["setCampaignKeywordKey", ' + JSON.stringify(customCampaignKeywordParam) + ']);\n';
- }
-
- if (doNotTrack)
- {
- result += ' _paq.push(["setDoNotTrack", true]);\n';
- }
-
- result += ' _paq.push(["trackPageView"]);\n\
+
+ if (groupPageTitlesByDomain) {
+ result += ' _paq.push(["setDocumentTitle", document.domain + "/" + document.title]);\n';
+ }
+
+ if (mergeSubdomains) {
+ var mainHostAllSub = '*.' + getHostNameFromUrl(siteUrls[idSite][0]);
+ result += ' _paq.push(["setCookieDomain", ' + JSON.stringify(mainHostAllSub) + ']);\n';
+ }
+
+ if (mergeAliasUrls) {
+ var siteHosts = [];
+ for (var i = 0; i != siteUrls[idSite].length; ++i) {
+ siteHosts[i] = '*.' + getHostNameFromUrl(siteUrls[idSite][i]);
+ }
+ result += ' _paq.push(["setDomains", ' + JSON.stringify(siteHosts) + ']);\n';
+ }
+
+ if (visitorCustomVariables.length) {
+ result += ' // you can set up to 5 custom variables for each visitor\n';
+ result += getCustomVariableJS(visitorCustomVariables, 'visit');
+ }
+
+ if (pageCustomVariables.length) {
+ result += ' // you can set up to 5 custom variables for each action (page view, ' +
+ 'download, click, site search)\n';
+ result += getCustomVariableJS(pageCustomVariables, 'page');
+ }
+
+ if (customCampaignNameQueryParam) {
+ result += ' _paq.push(["setCampaignNameKey", ' + JSON.stringify(customCampaignNameQueryParam) + ']);\n';
+ }
+
+ if (customCampaignKeywordParam) {
+ result += ' _paq.push(["setCampaignKeywordKey", ' + JSON.stringify(customCampaignKeywordParam) + ']);\n';
+ }
+
+ if (doNotTrack) {
+ result += ' _paq.push(["setDoNotTrack", true]);\n';
+ }
+
+ result += ' _paq.push(["trackPageView"]);\n\
_paq.push(["enableLinkTracking"]);\n\n\
(function() {\n\
var u=(("https:" == document.location.protocol) ? "https" : "http") + "://' + piwikHost + piwikPath + '/";\n\
@@ -237,139 +213,132 @@ $(document).ready(function() {
})();\n\
</script>\n\
<!-- End Piwik Code -->';
-
- $('#javascript-text textarea').val(result)
- };
-
- // function that generates image tracker link
- var generateImageTrackerLink = function()
- {
- // get data ( (("https:" == document.location.protocol)?"https://' + piwikHost + '":"http://' + piwikHost + '") )
- var idSite = $('#image-tracker-website .custom_select_main_link').attr('siteid'),
- path = document.location.pathname,
- piwikURL = ("https:" == document.location.protocol ? "https://" + piwikHost : "http://" + piwikHost) + path.substring(0, path.lastIndexOf('/')) + '/piwik.php',
- actionName = $('#image-tracker-action-name').val(),
- idGoal = null,
- revenue = null;
-
- if ($('#image-tracking-goal-check').is(':checked'))
- {
- idGoal = $('#image-tracker-goal').val();
- if (idGoal)
- {
- revenue = $('#image-tracker-advanced-options .revenue').val();
- }
- }
-
- // generate link HTML
- var params = {
- idsite: idSite,
- rec: 1
- };
-
- if (actionName)
- {
- params.action_name = actionName;
- }
-
- if (idGoal)
- {
- params.idGoal = idGoal;
- if (revenue)
- {
- params.revenue = revenue;
- }
- }
-
- var result = '<!-- Piwik Image Tracker -->\n\
+
+ $('#javascript-text textarea').val(result)
+ };
+
+ // function that generates image tracker link
+ var generateImageTrackerLink = function () {
+ // get data ( (("https:" == document.location.protocol)?"https://' + piwikHost + '":"http://' + piwikHost + '") )
+ var idSite = $('#image-tracker-website .custom_select_main_link').attr('siteid'),
+ path = document.location.pathname,
+ piwikURL = ("https:" == document.location.protocol ? "https://" + piwikHost : "http://" + piwikHost) + path.substring(0, path.lastIndexOf('/')) + '/piwik.php',
+ actionName = $('#image-tracker-action-name').val(),
+ idGoal = null,
+ revenue = null;
+
+ if ($('#image-tracking-goal-check').is(':checked')) {
+ idGoal = $('#image-tracker-goal').val();
+ if (idGoal) {
+ revenue = $('#image-tracker-advanced-options .revenue').val();
+ }
+ }
+
+ // generate link HTML
+ var params = {
+ idsite: idSite,
+ rec: 1
+ };
+
+ if (actionName) {
+ params.action_name = actionName;
+ }
+
+ if (idGoal) {
+ params.idGoal = idGoal;
+ if (revenue) {
+ params.revenue = revenue;
+ }
+ }
+
+ var result = '<!-- Piwik Image Tracker -->\n\
<img src="' + piwikURL + '?' + $.param(params) + '" style="border:0" alt="" />\n\
<!-- End Piwik -->';
- result = result.replace("&", "&amp;", "g");
- $('#image-tracking-link textarea').val(result);
- };
-
- // on image link tracker site change, change available goals
- $('#image-tracker-website').bind('piwik:siteSelected', function(e, site) {
- getSiteData(site.id, '#image-tracking-code-options', function() {
- resetGoalSelectItems(site.id, 'image-tracker-goal');
- generateImageTrackerLink();
- });
- });
-
- // on js link tracker site change, change available goals
- $('#js-tracker-website').bind('piwik:siteSelected', function(e, site) {
- $('.current-site-name', '#optional-js-tracking-options').each(function() {
- $(this).text(site.name);
- });
-
- getSiteData(site.id, '#js-code-options', function() {
- var siteHost = getHostNameFromUrl(siteUrls[site.id][0]);
- $('.current-site-host', '#optional-js-tracking-options').each(function() {
- $(this).text(siteHost);
- });
-
- var defaultAliasUrl = 'x.' + siteHost;
- $('.current-site-alias').text(siteUrls[site.id][1] || defaultAliasUrl);
-
- resetGoalSelectItems(site.id, 'js-tracker-goal');
- generateJsCode();
- });
- });
-
- // on click 'add' link in custom variable section, add a new row, but only
- // allow 5 custom variable entry rows
- $('.add-custom-variable').click(function(e) {
- e.preventDefault();
-
- var newRow = '<tr>\
+ result = result.replace("&", "&amp;", "g");
+ $('#image-tracking-link textarea').val(result);
+ };
+
+ // on image link tracker site change, change available goals
+ $('#image-tracker-website').bind('piwik:siteSelected', function (e, site) {
+ getSiteData(site.id, '#image-tracking-code-options', function () {
+ resetGoalSelectItems(site.id, 'image-tracker-goal');
+ generateImageTrackerLink();
+ });
+ });
+
+ // on js link tracker site change, change available goals
+ $('#js-tracker-website').bind('piwik:siteSelected', function (e, site) {
+ $('.current-site-name', '#optional-js-tracking-options').each(function () {
+ $(this).text(site.name);
+ });
+
+ getSiteData(site.id, '#js-code-options', function () {
+ var siteHost = getHostNameFromUrl(siteUrls[site.id][0]);
+ $('.current-site-host', '#optional-js-tracking-options').each(function () {
+ $(this).text(siteHost);
+ });
+
+ var defaultAliasUrl = 'x.' + siteHost;
+ $('.current-site-alias').text(siteUrls[site.id][1] || defaultAliasUrl);
+
+ resetGoalSelectItems(site.id, 'js-tracker-goal');
+ generateJsCode();
+ });
+ });
+
+ // on click 'add' link in custom variable section, add a new row, but only
+ // allow 5 custom variable entry rows
+ $('.add-custom-variable').click(function (e) {
+ e.preventDefault();
+
+ var newRow = '<tr>\
<td>&nbsp;</td>\
<td><input type="textbox" class="custom-variable-name"/></td>\
<td>&nbsp;</td>\
<td><input type="textbox" class="custom-variable-value"/></td>\
</tr>',
- row = $(this).closest('tr');
-
- row.before(newRow);
-
- // hide add button if max # of custom variables has been reached
- // (5 custom variables + 1 row for add new row)
- if ($('tr', row.parent()).length == 6)
- {
- $(this).hide();
- }
-
- return false;
- });
-
- // when any input in the JS tracking options section changes, regenerate JS code
- $('#optional-js-tracking-options').on('change', 'input', function() {
- generateJsCode();
- });
-
- // when any input/select in the image tracking options section changes, regenerate
- // image tracker link
- $('#image-tracking-section').on('change', 'input,select', function() {
- generateImageTrackerLink();
- });
-
- // on click generated code textareas, select the text so it can be easily copied
- $('#javascript-text>textarea,#image-tracking-link>textarea').click(function() {
- $(this).select();
- });
-
- // initial generation
- getSiteData($(
- '#js-tracker-website .custom_select_main_link').attr('siteid'),
- '#js-code-options,#image-tracking-code-options',
- function() {
- var imageTrackerSiteId = $('#image-tracker-website .custom_select_main_link').attr('siteid');
- resetGoalSelectItems(imageTrackerSiteId, 'image-tracker-goal');
-
- generateJsCode();
- generateImageTrackerLink();
- }
- );
-});
+ row = $(this).closest('tr');
+
+ row.before(newRow);
+
+ // hide add button if max # of custom variables has been reached
+ // (5 custom variables + 1 row for add new row)
+ if ($('tr', row.parent()).length == 6) {
+ $(this).hide();
+ }
+
+ return false;
+ });
+
+ // when any input in the JS tracking options section changes, regenerate JS code
+ $('#optional-js-tracking-options').on('change', 'input', function () {
+ generateJsCode();
+ });
+
+ // when any input/select in the image tracking options section changes, regenerate
+ // image tracker link
+ $('#image-tracking-section').on('change', 'input,select', function () {
+ generateImageTrackerLink();
+ });
+
+ // on click generated code textareas, select the text so it can be easily copied
+ $('#javascript-text>textarea,#image-tracking-link>textarea').click(function () {
+ $(this).select();
+ });
+
+ // initial generation
+ getSiteData($(
+ '#js-tracker-website .custom_select_main_link').attr('siteid'),
+ '#js-code-options,#image-tracking-code-options',
+ function () {
+ var imageTrackerSiteId = $('#image-tracker-website .custom_select_main_link').attr('siteid');
+ resetGoalSelectItems(imageTrackerSiteId, 'image-tracker-goal');
+
+ generateJsCode();
+ generateImageTrackerLink();
+ }
+ );
+ });
}(jQuery));
diff --git a/plugins/CoreAdminHome/templates/jsTrackingGenerator.tpl b/plugins/CoreAdminHome/templates/jsTrackingGenerator.tpl
index 529516c3b5..0348d620b9 100644
--- a/plugins/CoreAdminHome/templates/jsTrackingGenerator.tpl
+++ b/plugins/CoreAdminHome/templates/jsTrackingGenerator.tpl
@@ -3,238 +3,247 @@
<script type="text/javascript" src="plugins/CoreAdminHome/templates/jsTrackingGenerator.js"></script>
<div id="js-tracking-generator-data"
- data-currencies = "{$currencySymbols|@json_encode|escape:'html'}"/>
+ data-currencies="{$currencySymbols|@json_encode|escape:'html'}"/>
<h2>{'CoreAdminHome_JavaScriptTracking'|translate}</h2>
<div id="js-code-options" class="adminTable">
-<p>
- {'CoreAdminHome_JSTrackingIntro1'|translate}
- <br/><br/>
- {'CoreAdminHome_JSTrackingIntro2'|translate} {'CoreAdminHome_JSTrackingIntro3'|translate:'<a href="http://piwik.org/integrate/" target="_blank">':'</a>'}
- <br/><br/>
- {'CoreAdminHome_JSTrackingIntro4'|translate:'<a href="#image-tracking-link">':'</a>'}
- <br/><br/>
- {'CoreAdminHome_JSTrackingIntro5'|translate:'<a target="_blank" href="http://piwik.org/docs/javascript-tracking/">':'</a>'}
-</p>
-
-<div>
- {* website *}
- <label class="website-label"><strong>{'General_Website'|translate}</strong></label>
- {include file="CoreHome/templates/sites_selection.tpl"
- siteName=$defaultReportSiteName idSite=$idSite showAllSitesItem=false switchSiteOnSelect=false
- siteSelectorId="js-tracker-website"}
+ <p>
+ {'CoreAdminHome_JSTrackingIntro1'|translate}
+ <br/><br/>
+ {'CoreAdminHome_JSTrackingIntro2'|translate} {'CoreAdminHome_JSTrackingIntro3'|translate:'<a href="http://piwik.org/integrate/" target="_blank">':'</a>'}
+ <br/><br/>
+ {'CoreAdminHome_JSTrackingIntro4'|translate:'<a href="#image-tracking-link">':'</a>'}
+ <br/><br/>
+ {'CoreAdminHome_JSTrackingIntro5'|translate:'<a target="_blank" href="http://piwik.org/docs/javascript-tracking/">':'</a>'}
+ </p>
+
+ <div>
+ {* website *}
+ <label class="website-label"><strong>{'General_Website'|translate}</strong></label>
+ {include file="CoreHome/templates/sites_selection.tpl"
+ siteName=$defaultReportSiteName idSite=$idSite showAllSitesItem=false switchSiteOnSelect=false
+ siteSelectorId="js-tracker-website"}
+
+ <br/><br/><br/>
+ </div>
+
+ <table id="optional-js-tracking-options" class="adminTable" style='width:1024px'>
+ <tr>
+ <th>{'General_Options'|translate}</th>
+ <th>{'Mobile_Advanced'|translate} <a href="#" class="section-toggler-link"
+ data-section-id="javascript-advanced-options">({'General_Show_js'|translate})</a></th>
+ </tr>
+ <tr>
+ <td>
+ {* track across all subdomains *}
+ <div class="tracking-option-section">
+ <input type="checkbox" id="javascript-tracking-all-subdomains"/>
+ <label for="javascript-tracking-all-subdomains">{'CoreAdminHome_JSTracking_MergeSubdomains'|translate} <span
+ class='current-site-name'>{$defaultReportSiteName}</span></label>
+
+ <div class="small-form-description">
+ {'CoreAdminHome_JSTracking_MergeSubdomainsDesc'|translate:"x.<span class='current-site-host'>$defaultReportSiteDomain</span>":"y.<span class='current-site-host'>$defaultReportSiteDomain</span>"}
+ </div>
+ </div>
+
+ {* group page titles by site domain *}
+ <div class="tracking-option-section">
+ <input type="checkbox" id="javascript-tracking-group-by-domain"/>
+ <label for="javascript-tracking-group-by-domain">{'CoreAdminHome_JSTracking_GroupPageTitlesByDomain'|translate}</label>
+
+ <div class="small-form-description">
+ {'CoreAdminHome_JSTracking_GroupPageTitlesByDomainDesc1'|translate:"<span class='current-site-host'>$defaultReportSiteDomain</span>"}
+ </div>
+ </div>
+
+ {* track across all site aliases *}
+ <div class="tracking-option-section">
+ <input type="checkbox" id="javascript-tracking-all-aliases"/>
+ <label for="javascript-tracking-all-aliases">{'CoreAdminHome_JSTracking_MergeAliases'|translate} <span
+ class='current-site-name'>{$defaultReportSiteName}</span></label>
+
+ <div class="small-form-description">
+ {'CoreAdminHome_JSTracking_MergeAliasesDesc'|translate:"<span class='current-site-alias'>$defaultReportSiteAlias</span>"}
+ </div>
+ </div>
+
+ </td>
+ <td>
+ <div id="javascript-advanced-options" style="display:none">
+ {* visitor custom variable *}
+ <div class="custom-variable tracking-option-section" id="javascript-tracking-visitor-cv">
+ <input class="section-toggler-link" type="checkbox" id="javascript-tracking-visitor-cv-check" data-section-id="js-visitor-cv-extra"/>
+ <label for="javascript-tracking-visitor-cv-check">{'CoreAdminHome_JSTracking_VisitorCustomVars'|translate}</label>
+
+ <div class="small-form-description">
+ {'CoreAdminHome_JSTracking_VisitorCustomVarsDesc'|translate}
+ </div>
+
+ <table style="display:none" id="js-visitor-cv-extra">
+ <tr>
+ <td><strong>{'General_Name'|translate}</strong></td>
+ <td><input type="textbox" class="custom-variable-name" placeholder="e.g. Type"/></td>
+ <td><strong>{'General_Value'|translate}</strong></td>
+ <td><input type="textbox" class="custom-variable-value" placeholder="e.g. Customer"/></td>
+ </tr>
+ <tr>
+ <td colspan="4" style="text-align:right">
+ <a href="#" class="add-custom-variable">{'General_Add'|translate}</a>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ {* page view custom variable *}
+ <div class="custom-variable tracking-option-section" id="javascript-tracking-page-cv">
+ <input class="section-toggler-link" type="checkbox" id="javascript-tracking-page-cv-check" data-section-id="js-page-cv-extra"/>
+ <label for="javascript-tracking-page-cv-check">{'CoreAdminHome_JSTracking_PageCustomVars'|translate}</label>
+
+ <div class="small-form-description">
+ {'CoreAdminHome_JSTracking_PageCustomVarsDesc'|translate}
+ </div>
+
+ <table style="display:none" id="js-page-cv-extra">
+ <tr>
+ <td><strong>{'General_Name'|translate}</strong></td>
+ <td><input type="textbox" class="custom-variable-name" placeholder="e.g. Category"/></td>
+ <td><strong>{'General_Value'|translate}</strong></td>
+ <td><input type="textbox" class="custom-variable-value" placeholder="e.g. White Papers"/></td>
+ </tr>
+ <tr>
+ <td colspan="4" style="text-align:right">
+ <a href="#" class="add-custom-variable">{'General_Add'|translate}</a>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ {* do not track support *}
+ <div class="tracking-option-section">
+ <input type="checkbox" id="javascript-tracking-do-not-track"/>
+ <label for="javascript-tracking-do-not-track">{'CoreAdminHome_JSTracking_EnableDoNotTrack'|translate}</label>
+
+ <div class="small-form-description">
+ {'CoreAdminHome_JSTracking_EnableDoNotTrackDesc'|translate}
+ {if $serverSideDoNotTrackEnabled}
+ <br/>
+ <br/>
+ {'CoreAdminHome_JSTracking_EnableDoNotTrack_AlreadyEnabled'|translate}
+ {/if}
+ </div>
+ </div>
+
+ {* custom campaign name/keyword query params *}
+ <div class="tracking-option-section">
+ <input class="section-toggler-link" type="checkbox" id="custom-campaign-query-params-check"
+ data-section-id="js-campaign-query-param-extra"/>
+ <label for="custom-campaign-query-params-check">{'CoreAdminHome_JSTracking_CustomCampaignQueryParam'|translate}</label>
+
+ <div class="small-form-description">
+ {'CoreAdminHome_JSTracking_CustomCampaignQueryParamDesc'|translate:'<a href="http://piwik.org/faq/general/#faq_119" target="_blank">':'</a>'}
+ </div>
+
+ <table style="display:none" id="js-campaign-query-param-extra">
+ <tr>
+ <td><strong>{'CoreAdminHome_JSTracking_CampaignNameParam'|translate}</strong></td>
+ <td><input type="text" id="custom-campaign-name-query-param"/></td>
+ </tr>
+ <tr>
+ <td><strong>{'CoreAdminHome_JSTracking_CampaignKwdParam'|translate}</strong></td>
+ <td><input type="text" id="custom-campaign-keyword-query-param"/></td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </td>
+ </tr>
+ </table>
- <br/><br/><br/>
</div>
-<table id="optional-js-tracking-options" class="adminTable" style='width:1024px'>
-<tr>
- <th>{'General_Options'|translate}</th>
- <th>{'Mobile_Advanced'|translate} <a href="#" class="section-toggler-link" data-section-id="javascript-advanced-options">({'General_Show_js'|translate})</a></th>
-</tr>
-<tr>
-<td>
- {* track across all subdomains *}
- <div class="tracking-option-section">
- <input type="checkbox" id="javascript-tracking-all-subdomains"/>
- <label for="javascript-tracking-all-subdomains">{'CoreAdminHome_JSTracking_MergeSubdomains'|translate} <span class='current-site-name'>{$defaultReportSiteName}</span></label>
-
- <div class="small-form-description">
- {'CoreAdminHome_JSTracking_MergeSubdomainsDesc'|translate:"x.<span class='current-site-host'>$defaultReportSiteDomain</span>":"y.<span class='current-site-host'>$defaultReportSiteDomain</span>"}
- </div>
- </div>
-
- {* group page titles by site domain *}
- <div class="tracking-option-section">
- <input type="checkbox" id="javascript-tracking-group-by-domain"/>
- <label for="javascript-tracking-group-by-domain">{'CoreAdminHome_JSTracking_GroupPageTitlesByDomain'|translate}</label>
-
- <div class="small-form-description">
- {'CoreAdminHome_JSTracking_GroupPageTitlesByDomainDesc1'|translate:"<span class='current-site-host'>$defaultReportSiteDomain</span>"}
- </div>
- </div>
-
- {* track across all site aliases *}
- <div class="tracking-option-section">
- <input type="checkbox" id="javascript-tracking-all-aliases"/>
- <label for="javascript-tracking-all-aliases">{'CoreAdminHome_JSTracking_MergeAliases'|translate} <span class='current-site-name'>{$defaultReportSiteName}</span></label>
-
- <div class="small-form-description">
- {'CoreAdminHome_JSTracking_MergeAliasesDesc'|translate:"<span class='current-site-alias'>$defaultReportSiteAlias</span>"}
- </div>
- </div>
-
-</td>
-<td>
- <div id="javascript-advanced-options" style="display:none">
- {* visitor custom variable *}
- <div class="custom-variable tracking-option-section" id="javascript-tracking-visitor-cv">
- <input class="section-toggler-link" type="checkbox" id="javascript-tracking-visitor-cv-check" data-section-id="js-visitor-cv-extra"/>
- <label for="javascript-tracking-visitor-cv-check">{'CoreAdminHome_JSTracking_VisitorCustomVars'|translate}</label>
-
- <div class="small-form-description">
- {'CoreAdminHome_JSTracking_VisitorCustomVarsDesc'|translate}
- </div>
-
- <table style="display:none" id="js-visitor-cv-extra">
- <tr>
- <td><strong>{'General_Name'|translate}</strong></td>
- <td><input type="textbox" class="custom-variable-name" placeholder="e.g. Type"/></td>
- <td><strong>{'General_Value'|translate}</strong></td>
- <td><input type="textbox" class="custom-variable-value" placeholder="e.g. Customer"/></td>
- </tr>
- <tr>
- <td colspan="4" style="text-align:right">
- <a href="#" class="add-custom-variable">{'General_Add'|translate}</a>
- </td>
- </tr>
- </table>
- </div>
-
- {* page view custom variable *}
- <div class="custom-variable tracking-option-section" id="javascript-tracking-page-cv">
- <input class="section-toggler-link" type="checkbox" id="javascript-tracking-page-cv-check" data-section-id="js-page-cv-extra"/>
- <label for="javascript-tracking-page-cv-check">{'CoreAdminHome_JSTracking_PageCustomVars'|translate}</label>
-
- <div class="small-form-description">
- {'CoreAdminHome_JSTracking_PageCustomVarsDesc'|translate}
- </div>
-
- <table style="display:none" id="js-page-cv-extra">
- <tr>
- <td><strong>{'General_Name'|translate}</strong></td>
- <td><input type="textbox" class="custom-variable-name" placeholder="e.g. Category"/></td>
- <td><strong>{'General_Value'|translate}</strong></td>
- <td><input type="textbox" class="custom-variable-value" placeholder="e.g. White Papers"/></td>
- </tr>
- <tr>
- <td colspan="4" style="text-align:right">
- <a href="#" class="add-custom-variable">{'General_Add'|translate}</a>
- </td>
- </tr>
- </table>
- </div>
-
- {* do not track support *}
- <div class="tracking-option-section">
- <input type="checkbox" id="javascript-tracking-do-not-track"/>
- <label for="javascript-tracking-do-not-track">{'CoreAdminHome_JSTracking_EnableDoNotTrack'|translate}</label>
-
- <div class="small-form-description">
- {'CoreAdminHome_JSTracking_EnableDoNotTrackDesc'|translate}
- {if $serverSideDoNotTrackEnabled}
- <br/><br/>
- {'CoreAdminHome_JSTracking_EnableDoNotTrack_AlreadyEnabled'|translate}
- {/if}
- </div>
- </div>
-
- {* custom campaign name/keyword query params *}
- <div class="tracking-option-section">
- <input class="section-toggler-link" type="checkbox" id="custom-campaign-query-params-check" data-section-id="js-campaign-query-param-extra"/>
- <label for="custom-campaign-query-params-check">{'CoreAdminHome_JSTracking_CustomCampaignQueryParam'|translate}</label>
-
- <div class="small-form-description">
- {'CoreAdminHome_JSTracking_CustomCampaignQueryParamDesc'|translate:'<a href="http://piwik.org/faq/general/#faq_119" target="_blank">':'</a>'}
- </div>
-
- <table style="display:none" id="js-campaign-query-param-extra">
- <tr>
- <td><strong>{'CoreAdminHome_JSTracking_CampaignNameParam'|translate}</strong></td>
- <td><input type="text" id="custom-campaign-name-query-param"/></td>
- </tr>
- <tr>
- <td><strong>{'CoreAdminHome_JSTracking_CampaignKwdParam'|translate}</strong></td>
- <td><input type="text" id="custom-campaign-keyword-query-param"/></td>
- </tr>
- </table>
- </div>
- </div>
-</td>
-</tr>
-</table>
+<div id="javascript-output-section">
+ <h3>{'Installation_JsTag'|translate}</h3>
-</div>
+ <p class="form-description">{'CoreAdminHome_JSTracking_CodeNote'|translate:"&lt;/body&gt;"}</p>
-<div id="javascript-output-section">
- <h3>{'Installation_JsTag'|translate}</h3>
- <p class="form-description">{'CoreAdminHome_JSTracking_CodeNote'|translate:"&lt;/body&gt;"}</p>
- <div id="javascript-text">
- <textarea> </textarea>
- </div>
- <br/>
+ <div id="javascript-text">
+ <textarea> </textarea>
+ </div>
+ <br/>
</div>
<h2 id="image-tracking-link">{'CoreAdminHome_ImageTracking'|translate}</h2>
<div id="image-tracking-code-options" class="adminTable">
-<p>
- {'CoreAdminHome_ImageTrackingIntro1'|translate} {'CoreAdminHome_ImageTrackingIntro2'|translate:"<em>&lt;noscript&gt;&lt;/noscript&gt;</em>"}
- <br/><br/>
- {'CoreAdminHome_ImageTrackingIntro3'|translate:'<a href="http://piwik.org/docs/tracking-api/reference/" target="_blank">':'</a>'}
-</p>
-
-<div>
- {* website *}
- <label class="website-label"><strong>{'General_Website'|translate}</strong></label>
- {include file="CoreHome/templates/sites_selection.tpl"
- siteName=$defaultReportSiteName idSite=$idSite showAllSitesItem=false switchSiteOnSelect=false
- siteSelectorId="image-tracker-website"}
-
- <br/><br/><br/>
-</div>
-
-<table id="image-tracking-section" class="adminTable" style='width:1024px;'>
-<tr>
- <th>{'General_Options'|translate}</th>
- <th>{'Mobile_Advanced'|translate} <a href="#" class="section-toggler-link" data-section-id="image-tracker-advanced-options">({'General_Show_js'|translate})</a></th>
-</tr>
-<tr>
-<td>
- {* action_name *}
- <div class="tracking-option-section">
- <label for="image-tracker-action-name">{'Actions_ColumnPageName'|translate}</label>
- <input type="text" id="image-tracker-action-name"/>
- </div>
-</td>
-<td>
- <div id="image-tracker-advanced-options" style="display:none">
- {* goal *}
- <div class="goal-picker tracking-option-section">
- <input class="section-toggler-link" type="checkbox" id="image-tracking-goal-check" data-section-id="image-goal-picker-extra"/>
- <label for="image-tracking-goal-check">{'CoreAdminHome_TrackAGoal'|translate}</label>
-
- <div style="display:none" id="image-goal-picker-extra">
- <select id="image-tracker-goal">
- <option value="">{'UserCountryMap_None'|translate}</option>
- </select>
- <span>{'CoreAdminHome_WithOptionalRevenue'|translate}</span>
- <span class="currency">{$defaultSiteRevenue|escape:'html'}</span>
- <input type="text" class="revenue" value=""/>
- </div>
- </div>
- </div>
-</td>
-</tr>
-</table>
-
-<div id="image-link-output-section" width="560px">
- <h3>{'CoreAdminHome_ImageTrackingLink'|translate}</h3><br/><br/>
- <div id="image-tracking-link">
- <textarea> </textarea>
- </div>
- <br/>
-</div>
+ <p>
+ {'CoreAdminHome_ImageTrackingIntro1'|translate} {'CoreAdminHome_ImageTrackingIntro2'|translate:"<em>&lt;noscript&gt;&lt;/noscript&gt;</em>"}
+ <br/><br/>
+ {'CoreAdminHome_ImageTrackingIntro3'|translate:'<a href="http://piwik.org/docs/tracking-api/reference/" target="_blank">':'</a>'}
+ </p>
+
+ <div>
+ {* website *}
+ <label class="website-label"><strong>{'General_Website'|translate}</strong></label>
+ {include file="CoreHome/templates/sites_selection.tpl"
+ siteName=$defaultReportSiteName idSite=$idSite showAllSitesItem=false switchSiteOnSelect=false
+ siteSelectorId="image-tracker-website"}
+
+ <br/><br/><br/>
+ </div>
+
+ <table id="image-tracking-section" class="adminTable" style='width:1024px;'>
+ <tr>
+ <th>{'General_Options'|translate}</th>
+ <th>{'Mobile_Advanced'|translate} <a href="#" class="section-toggler-link"
+ data-section-id="image-tracker-advanced-options">({'General_Show_js'|translate})</a></th>
+ </tr>
+ <tr>
+ <td>
+ {* action_name *}
+ <div class="tracking-option-section">
+ <label for="image-tracker-action-name">{'Actions_ColumnPageName'|translate}</label>
+ <input type="text" id="image-tracker-action-name"/>
+ </div>
+ </td>
+ <td>
+ <div id="image-tracker-advanced-options" style="display:none">
+ {* goal *}
+ <div class="goal-picker tracking-option-section">
+ <input class="section-toggler-link" type="checkbox" id="image-tracking-goal-check" data-section-id="image-goal-picker-extra"/>
+ <label for="image-tracking-goal-check">{'CoreAdminHome_TrackAGoal'|translate}</label>
+
+ <div style="display:none" id="image-goal-picker-extra">
+ <select id="image-tracker-goal">
+ <option value="">{'UserCountryMap_None'|translate}</option>
+ </select>
+ <span>{'CoreAdminHome_WithOptionalRevenue'|translate}</span>
+ <span class="currency">{$defaultSiteRevenue|escape:'html'}</span>
+ <input type="text" class="revenue" value=""/>
+ </div>
+ </div>
+ </div>
+ </td>
+ </tr>
+ </table>
+
+ <div id="image-link-output-section" width="560px">
+ <h3>{'CoreAdminHome_ImageTrackingLink'|translate}</h3><br/><br/>
+
+ <div id="image-tracking-link">
+ <textarea> </textarea>
+ </div>
+ <br/>
+ </div>
</div>
<h2>{'CoreAdminHome_ImportingServerLogs'|translate}</h2>
<p>
-{'CoreAdminHome_ImportingServerLogsDesc'|translate:'<a href="http://piwik.org/log-analytics/" target="_blank">':'</a>'}
+ {'CoreAdminHome_ImportingServerLogsDesc'|translate:'<a href="http://piwik.org/log-analytics/" target="_blank">':'</a>'}
</p>
{include file="CoreAdminHome/templates/footer.tpl"}
diff --git a/plugins/CoreAdminHome/templates/menu.css b/plugins/CoreAdminHome/templates/menu.css
index 5f9b2943b4..207263f86a 100644
--- a/plugins/CoreAdminHome/templates/menu.css
+++ b/plugins/CoreAdminHome/templates/menu.css
@@ -1,81 +1,72 @@
-#menu{
- padding:0 0 0 0;
- float: left;
- width: 240px;
- position: absolute;
+#menu {
+ padding: 0 0 0 0;
+ float: left;
+ width: 240px;
+ position: absolute;
}
#tablist {
- background-image: linear-gradient(top, #FECB00 0%, #FE9800 25%, #FE6702 50%, #CA0000 75%, #670002 100%);
- background-image: -o-linear-gradient(top, #FECB00 0%, #FE9800 25%, #FE6702 50%, #CA0000 75%, #670002 100%);
- background-image: -moz-linear-gradient(top, #FECB00 0%, #FE9800 25%, #FE6702 50%, #CA0000 75%, #670002 100%);
- background-image: -webkit-linear-gradient(top, #FECB00 0%, #FE9800 25%, #FE6702 50%, #CA0000 75%, #670002 100%);
- background-image: -ms-linear-gradient(top, #FECB00 0%, #FE9800 25%, #FE6702 50%, #CA0000 75%, #670002 100%);
+ background-image: linear-gradient(top, #FECB00 0%, #FE9800 25%, #FE6702 50%, #CA0000 75%, #670002 100%);
+ background-image: -o-linear-gradient(top, #FECB00 0%, #FE9800 25%, #FE6702 50%, #CA0000 75%, #670002 100%);
+ background-image: -moz-linear-gradient(top, #FECB00 0%, #FE9800 25%, #FE6702 50%, #CA0000 75%, #670002 100%);
+ background-image: -webkit-linear-gradient(top, #FECB00 0%, #FE9800 25%, #FE6702 50%, #CA0000 75%, #670002 100%);
+ background-image: -ms-linear-gradient(top, #FECB00 0%, #FE9800 25%, #FE6702 50%, #CA0000 75%, #670002 100%);
- background-image: -webkit-gradient(
- linear,
- left top,
- left bottom,
- color-stop(0, #FECB00),
- color-stop(0.25, #FE9800),
- color-stop(0.5, #FE6702),
- color-stop(0.75, #CA0000),
- color-stop(1, #670002)
- );
+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #FECB00), color-stop(0.25, #FE9800), color-stop(0.5, #FE6702), color-stop(0.75, #CA0000), color-stop(1, #670002));
- -moz-background-size:5px 100%;
- background-size:5px 100%;
- background-position:0 0, 100% 0;
- background-repeat:no-repeat;
+ -moz-background-size: 5px 100%;
+ background-size: 5px 100%;
+ background-position: 0 0, 100% 0;
+ background-repeat: no-repeat;
}
#tablist {
- padding-left: 5px;
- margin-bottom: 0;
- margin-top: 0.1em;
- border: 1px solid #ddd;
- border-radius: 5px;
+ padding-left: 5px;
+ margin-bottom: 0;
+ margin-top: 0.1em;
+ border: 1px solid #ddd;
+ border-radius: 5px;
}
#tablist li {
- list-style: none;
- margin: 0;
+ list-style: none;
+ margin: 0;
}
#tablist > li {
- padding-bottom: 5px;
+ padding-bottom: 5px;
}
#tablist > li > span {
- border-bottom: 1px dotted #778;
- display: block;
- padding: 5px 10px;
- font-size: 18px;
- color: #7E7363;
+ border-bottom: 1px dotted #778;
+ display: block;
+ padding: 5px 10px;
+ font-size: 18px;
+ color: #7E7363;
}
#tablist li a {
- text-decoration: none;
- padding: 0.6em 0.9em;
- background: white;
- font: 14px Arial, Helvetica, sans-serif;
- display: block;
+ text-decoration: none;
+ padding: 0.6em 0.9em;
+ background: white;
+ font: 14px Arial, Helvetica, sans-serif;
+ display: block;
}
-#tablist li a:link,#tablist li a:visited {
- color: #000;
+#tablist li a:link, #tablist li a:visited {
+ color: #000;
}
#tablist li a:hover, #tablist li a.active {
- color: #e87500;
- background: #f1f1f1;
- border-color: #000;
+ color: #e87500;
+ background: #f1f1f1;
+ border-color: #000;
}
#tablist li a:hover {
- text-decoration: underline;
+ text-decoration: underline;
}
#tablist li a.current {
- background: #defdbb;
+ background: #defdbb;
}
diff --git a/plugins/CoreAdminHome/templates/menu.tpl b/plugins/CoreAdminHome/templates/menu.tpl
index ade09008b4..fba9efd87e 100644
--- a/plugins/CoreAdminHome/templates/menu.tpl
+++ b/plugins/CoreAdminHome/templates/menu.tpl
@@ -1,20 +1,21 @@
{if count($menu) > 1}
-<div id="menu">
-<ul id="tablist">
-{foreach from=$menu key=name item=submenu name=menu}
-{if $submenu._hasSubmenu}
- <li>
- <span>{$name|translate}</span>
- <ul>
- {foreach from=$submenu key=sname item=url name=submenu}
- {if strpos($sname, '_') !== 0}
- <li><a href='index.php{$url._url|@urlRewriteWithParameters}' {if isset($currentAdminMenuName) && $sname==$currentAdminMenuName}class='active'{/if}>{$sname|translate}</a></li>
- {/if}
- {/foreach}
- </ul>
- </li>
-{/if}
-{/foreach}
-</ul>
-</div>
+ <div id="menu">
+ <ul id="tablist">
+ {foreach from=$menu key=name item=submenu name=menu}
+ {if $submenu._hasSubmenu}
+ <li>
+ <span>{$name|translate}</span>
+ <ul>
+ {foreach from=$submenu key=sname item=url name=submenu}
+ {if strpos($sname, '_') !== 0}
+ <li><a href='index.php{$url._url|@urlRewriteWithParameters}'
+ {if isset($currentAdminMenuName) && $sname==$currentAdminMenuName}class='active'{/if}>{$sname|translate}</a></li>
+ {/if}
+ {/foreach}
+ </ul>
+ </li>
+ {/if}
+ {/foreach}
+ </ul>
+ </div>
{/if}
diff --git a/plugins/CoreAdminHome/templates/optOut.tpl b/plugins/CoreAdminHome/templates/optOut.tpl
index 10c336d8a6..f53204c5d5 100644
--- a/plugins/CoreAdminHome/templates/optOut.tpl
+++ b/plugins/CoreAdminHome/templates/optOut.tpl
@@ -1,27 +1,28 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- </head>
- <body>
- {if !$trackVisits}{'CoreAdminHome_OptOutComplete'|translate}
- <br/>
- {'CoreAdminHome_OptOutCompleteBis'|translate}
- {else}
- {'CoreAdminHome_YouMayOptOut'|translate}
- <br/>
- {'CoreAdminHome_YouMayOptOutBis'|translate}
- {/if}
- <br/><br/>
- <form method="post" action="?module=CoreAdminHome&amp;action=optOut{if $language}&amp;language={$language}{/if}">
- <input type="hidden" name="nonce" value="{$nonce}" ></input>
- <input type="hidden" name="fuzz" value="{$smarty.now}"></input>
- <input onclick="this.form.submit()" type="checkbox" id="trackVisits" name="trackVisits" {if $trackVisits}checked="checked"{/if}></input>
- <label for="trackVisits"><strong>
- {if $trackVisits}{'CoreAdminHome_YouAreOptedIn'|translate} {'CoreAdminHome_ClickHereToOptOut'|translate}
- {else}{'CoreAdminHome_YouAreOptedOut'|translate} {'CoreAdminHome_ClickHereToOptIn'|translate}{/if}
- </strong></label>
- </form>
- </body>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+</head>
+<body>
+{if !$trackVisits}{'CoreAdminHome_OptOutComplete'|translate}
+ <br/>
+ {'CoreAdminHome_OptOutCompleteBis'|translate}
+{else}
+ {'CoreAdminHome_YouMayOptOut'|translate}
+ <br/>
+ {'CoreAdminHome_YouMayOptOutBis'|translate}
+{/if}
+<br/><br/>
+
+<form method="post" action="?module=CoreAdminHome&amp;action=optOut{if $language}&amp;language={$language}{/if}">
+ <input type="hidden" name="nonce" value="{$nonce}"></input>
+ <input type="hidden" name="fuzz" value="{$smarty.now}"></input>
+ <input onclick="this.form.submit()" type="checkbox" id="trackVisits" name="trackVisits" {if $trackVisits}checked="checked"{/if}></input>
+ <label for="trackVisits"><strong>
+ {if $trackVisits}{'CoreAdminHome_YouAreOptedIn'|translate} {'CoreAdminHome_ClickHereToOptOut'|translate}
+ {else}{'CoreAdminHome_YouAreOptedOut'|translate} {'CoreAdminHome_ClickHereToOptIn'|translate}{/if}
+ </strong></label>
+</form>
+</body>
</html>
diff --git a/plugins/CoreAdminHome/templates/styles.css b/plugins/CoreAdminHome/templates/styles.css
index 73f5b75195..71871038b0 100644
--- a/plugins/CoreAdminHome/templates/styles.css
+++ b/plugins/CoreAdminHome/templates/styles.css
@@ -1,164 +1,179 @@
.admin img {
- vertical-align: middle;;
+ vertical-align: middle;;
}
+
.admin a {
- color: black;
+ color: black;
}
#content.admin {
- margin:0 15px 0 270px;
+ margin: 0 15px 0 270px;
padding: 0 0 40px;
- font:13px Arial, Helvetica, sans-serif;
+ font: 13px Arial, Helvetica, sans-serif;
}
-.admin #header_message{
- margin-top:-10px;
+.admin #header_message {
+ margin-top: -10px;
}
table.admin {
- font-size: 0.9em;
- font-family: Arial, Helvetica, verdana sans-serif;
- background-color: #fff;
- border-collapse: collapse;
+ font-size: 0.9em;
+ font-family: Arial, Helvetica, verdana sans-serif;
+ background-color: #fff;
+ border-collapse: collapse;
}
table.admin thead th {
- border-right: 1px solid #fff;
- color: #fff;
- text-align: center;
- padding: 5px;
- text-transform: uppercase;
- height: 25px;
- background-color: #a3c159;
- font-weight:bold;
+ border-right: 1px solid #fff;
+ color: #fff;
+ text-align: center;
+ padding: 5px;
+ text-transform: uppercase;
+ height: 25px;
+ background-color: #a3c159;
+ font-weight: bold;
}
table.admin tbody tr {
- background-color: #fff;
- border-bottom: 1px solid #f0f0f0;
+ background-color: #fff;
+ border-bottom: 1px solid #f0f0f0;
}
table.admin tbody td {
- color: #414141;
- text-align: left;
- vertical-align:top;
+ color: #414141;
+ text-align: left;
+ vertical-align: top;
}
table.admin tbody th {
- text-align: left;
- padding: 2px;
+ text-align: left;
+ padding: 2px;
}
+
table.admin tbody td, table.admin tbody th {
- color: #536C2A;
- text-decoration: none;
- font-weight: normal;
- padding: 10px;
+ color: #536C2A;
+ text-decoration: none;
+ font-weight: normal;
+ padding: 10px;
}
table.admin tbody td:hover, table.admin tbody th:hover {
- color: #009193;
- text-decoration: none;
+ color: #009193;
+ text-decoration: none;
}
.warning {
- border: 1px dotted gray;
- padding: 15px;
- font-size: .8em;
+ border: 1px dotted gray;
+ padding: 15px;
+ font-size: .8em;
}
.warning ul {
- margin-left: 50px;
+ margin-left: 50px;
}
.access_error {
- font-size: .7em;
- padding: 15px;
+ font-size: .7em;
+ padding: 15px;
}
.admin h2 {
- border-bottom:1px solid #DADADA;
- margin:15px -15px 20px 0;
- padding:0 0 5px 0;
- font-size:24px;
+ border-bottom: 1px solid #DADADA;
+ margin: 15px -15px 20px 0;
+ padding: 0 0 5px 0;
+ font-size: 24px;
}
-.admin p,.admin section {
- margin-top:10px;
- line-height:140%;
- padding-bottom:20px;
+.admin p, .admin section {
+ margin-top: 10px;
+ line-height: 140%;
+ padding-bottom: 20px;
}
+
.adminTable {
- width: 100%;
- clear: both;
- margin: 0;
+ width: 100%;
+ clear: both;
+ margin: 0;
}
+
.adminTable a {
- text-decoration: none;
- color:#2B5C97;
+ text-decoration: none;
+ color: #2B5C97;
}
+
.adminTable abbr {
- white-space: nowrap;
+ white-space: nowrap;
}
+
.adminTable td {
- font-size: 13px;
- vertical-align: top;
- padding: 7px 15px 9px 10px;
- vertical-align: top;
+ font-size: 13px;
+ vertical-align: top;
+ padding: 7px 15px 9px 10px;
+ vertical-align: top;
}
+
.adminTable td.action-links {
- text-align: right;
+ text-align: right;
}
+
.adminTable .check-column {
- text-align: right;
- width: 1.5em;
- padding: 0;
+ text-align: right;
+ width: 1.5em;
+ padding: 0;
}
+
.adminTable .num {
- text-align: center;
+ text-align: center;
}
+
.adminTable .name {
- font-weight: bold;
+ font-weight: bold;
}
+
.adminTable .ui-inline-help {
- margin-top:0;
- width:100%;
+ margin-top: 0;
+ width: 100%;
}
#logoSettings .ui-inline-help {
- width:550px;
+ width: 550px;
}
/* other styles */
.form-description {
- color:#666666;
- font-style:italic;
- margin-left:10px;
+ color: #666666;
+ font-style: italic;
+ margin-left: 10px;
}
#logoSettings, #smtpSettings {
- margin-left:50px;
+ margin-left: 50px;
}
/* to override .admin a */
.admin .sites_autocomplete a {
- color: #255792;
+ color: #255792;
}
/* trusted host styles */
#trustedHostSettings .adminTable {
- width:300px;
+ width: 300px;
}
+
#trustedHostSettings .adminTable td {
- vertical-align: middle;
- padding-bottom: 0;
+ vertical-align: middle;
+ padding-bottom: 0;
}
+
#trustedHostSettings .adminTable tr td:last-child {
- padding: 0 0 0 0;
+ padding: 0 0 0 0;
}
+
#trustedHostSettings input {
- width:238px;
+ width: 238px;
}
+
#trustedHostSettings .add-trusted-host-container {
- padding: 12px 24px;
+ padding: 12px 24px;
}
diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php
index fa08fb98ec..8070d536a1 100644
--- a/plugins/CoreHome/Controller.php
+++ b/plugins/CoreHome/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_CoreHome
*/
@@ -15,215 +15,208 @@
*/
class Piwik_CoreHome_Controller extends Piwik_Controller
{
- function getDefaultAction()
- {
- return 'redirectToCoreHomeIndex';
- }
-
- function redirectToCoreHomeIndex()
- {
- $defaultReport = Piwik_UsersManager_API::getInstance()->getUserPreference(Piwik::getCurrentUserLogin(), Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT);
- $module = 'CoreHome';
- $action = 'index';
-
- // User preference: default report to load is the All Websites dashboard
- if($defaultReport == 'MultiSites'
- && Piwik_PluginsManager::getInstance()->isPluginActivated('MultiSites'))
- {
- $module = 'MultiSites';
- }
- if($defaultReport == Piwik::getLoginPluginName())
- {
- $module = Piwik::getLoginPluginName();
- }
- $idSite = Piwik_Common::getRequestVar('idSite', false, 'int');
-
- parent::redirectToIndex($module, $action, !empty($idSite) ? $idSite : null );
- }
-
- public function showInContext()
- {
- $controllerName = Piwik_Common::getRequestVar('moduleToLoad');
- $actionName = Piwik_Common::getRequestVar('actionToLoad', 'index');
- if($actionName == 'showInContext') {
- throw new Exception("Preventing infinite recursion...");
- }
- $view = $this->getDefaultIndexView();
- $view->content = Piwik_FrontController::getInstance()->fetchDispatch( $controllerName, $actionName );
- echo $view->render();
- }
-
- protected function getDefaultIndexView()
- {
- $view = Piwik_View::factory('index');
- $this->setGeneralVariablesView($view);
- $view->menu = Piwik_GetMenu();
- $view->content = '';
- return $view;
- }
-
- protected function setDateTodayIfWebsiteCreatedToday()
- {
- $date = Piwik_Common::getRequestVar('date', false);
- if($date == 'today'
- || Piwik_Common::getRequestVar('period', false) == 'range')
- {
- return;
- }
- $websiteId = Piwik_Common::getRequestVar('idSite', false, 'int');
- if ($websiteId)
- {
- $website = new Piwik_Site($websiteId);
- $datetimeCreationDate = $this->site->getCreationDate()->getDatetime();
- $creationDateLocalTimezone = Piwik_Date::factory($datetimeCreationDate, $website->getTimezone())->toString('Y-m-d');
- $todayLocalTimezone = Piwik_Date::factory('now', $website->getTimezone())->toString('Y-m-d');
- if( $creationDateLocalTimezone == $todayLocalTimezone )
- {
- Piwik::redirectToModule( 'CoreHome', 'index',
- array( 'date' => 'today',
- 'idSite' => $websiteId,
- 'period' => Piwik_Common::getRequestVar('period'))
- );
- }
- }
- }
-
- public function index()
- {
- $this->setDateTodayIfWebsiteCreatedToday();
- $view = $this->getDefaultIndexView();
- echo $view->render();
- }
-
- /*
- * This method is called when the asset manager is configured in merged mode.
- * It returns the content of the css merged file.
- *
- * @see core/AssetManager.php
- */
- public function getCss ()
- {
- $cssMergedFile = Piwik_AssetManager::getMergedCssFileLocation();
- Piwik::serveStaticFile($cssMergedFile, "text/css");
- }
-
- /*
- * This method is called when the asset manager is configured in merged mode.
- * It returns the content of the js merged file.
- *
- * @see core/AssetManager.php
- */
- public function getJs ()
- {
- $jsMergedFile = Piwik_AssetManager::getMergedJsFileLocation();
- Piwik::serveStaticFile($jsMergedFile, "application/javascript; charset=UTF-8");
- }
-
-
- // --------------------------------------------------------
- // ROW EVOLUTION
- // The following methods render the popover that shows the
- // evolution of a singe or multiple rows in a data table
- // --------------------------------------------------------
-
- /**
- * This static cache is necessary because the signature cannot be modified
- * if the method renders a ViewDataTable. So we use it to pass information
- * to getRowEvolutionGraph()
- * @var Piwik_CoreHome_DataTableAction_Evolution
- */
- private static $rowEvolutionCache = null;
-
- /** Render the entire row evolution popover for a single row */
- public function getRowEvolutionPopover()
- {
- $rowEvolution = $this->makeRowEvolution($isMulti = false);
- self::$rowEvolutionCache = $rowEvolution;
- $view = Piwik_View::factory('popover_rowevolution');
- echo $rowEvolution->renderPopover($this, $view);
- }
-
- /** Render the entire row evolution popover for multiple rows */
- public function getMultiRowEvolutionPopover()
- {
- $rowEvolution = $this->makeRowEvolution($isMulti = true);
- self::$rowEvolutionCache = $rowEvolution;
- $view = Piwik_View::factory('popover_multirowevolution');
- echo $rowEvolution->renderPopover($this, $view);
- }
-
- /** Generic method to get an evolution graph or a sparkline for the row evolution popover */
- public function getRowEvolutionGraph($fetch = false)
- {
- $rowEvolution = self::$rowEvolutionCache;
- if ($rowEvolution === null)
- {
- $paramName = Piwik_CoreHome_DataTableRowAction_MultiRowEvolution::IS_MULTI_EVOLUTION_PARAM;
- $isMultiRowEvolution = Piwik_Common::getRequestVar($paramName, false, 'int');
-
- $rowEvolution = $this->makeRowEvolution($isMultiRowEvolution, $graphType = 'graphEvolution');
- $rowEvolution->useAvailableMetrics();
- self::$rowEvolutionCache = $rowEvolution;
- }
-
- $view = $rowEvolution->getRowEvolutionGraph();
- return $this->renderView($view, $fetch);
- }
-
- /** Utility function. Creates a RowEvolution instance. */
- private function makeRowEvolution( $isMultiRowEvolution, $graphType = null )
- {
- if ($isMultiRowEvolution)
- {
- return new Piwik_CoreHome_DataTableRowAction_MultiRowEvolution($this->idSite, $this->date, $graphType);
- }
- else
- {
- return new Piwik_CoreHome_DataTableRowAction_RowEvolution($this->idSite, $this->date, $graphType);
- }
- }
-
- /**
- * Forces a check for updates and re-renders the header message.
- *
- * This will check piwik.org at most once per 10s.
- */
- public function checkForUpdates()
- {
- Piwik::checkUserHasSomeAdminAccess();
- $this->checkTokenInUrl();
-
- // perform check (but only once every 10s)
- Piwik_UpdateCheck::check($force = false, Piwik_UpdateCheck::UI_CLICK_CHECK_INTERVAL);
-
- $view = Piwik_View::factory('header_message');
- $this->setGeneralVariablesView($view);
- echo $view->render();
- }
-
- /**
- * Renders and echo's the in-app donate form w/ slider.
- */
- public function getDonateForm()
- {
- $view = Piwik_View::factory('donate');
- if (Piwik_Common::getRequestVar('widget', false)
- && Piwik::isUserIsSuperUser())
- {
- $view->footerMessage = Piwik_Translate('CoreHome_OnlyForAdmin');
- }
- echo $view->render();
- }
-
- /**
- * Renders and echo's HTML that displays the Piwik promo video.
- */
- public function getPromoVideo()
- {
- $view = Piwik_View::factory('promo_video');
- $view->shareText = Piwik_Translate('CoreHome_SharePiwikShort');
- $view->shareTextLong = Piwik_Translate('CoreHome_SharePiwikLong');
- $view->promoVideoUrl = 'http://www.youtube.com/watch?v=OslfF_EH81g';
- echo $view->render();
- }
+ function getDefaultAction()
+ {
+ return 'redirectToCoreHomeIndex';
+ }
+
+ function redirectToCoreHomeIndex()
+ {
+ $defaultReport = Piwik_UsersManager_API::getInstance()->getUserPreference(Piwik::getCurrentUserLogin(), Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT);
+ $module = 'CoreHome';
+ $action = 'index';
+
+ // User preference: default report to load is the All Websites dashboard
+ if ($defaultReport == 'MultiSites'
+ && Piwik_PluginsManager::getInstance()->isPluginActivated('MultiSites')
+ ) {
+ $module = 'MultiSites';
+ }
+ if ($defaultReport == Piwik::getLoginPluginName()) {
+ $module = Piwik::getLoginPluginName();
+ }
+ $idSite = Piwik_Common::getRequestVar('idSite', false, 'int');
+
+ parent::redirectToIndex($module, $action, !empty($idSite) ? $idSite : null);
+ }
+
+ public function showInContext()
+ {
+ $controllerName = Piwik_Common::getRequestVar('moduleToLoad');
+ $actionName = Piwik_Common::getRequestVar('actionToLoad', 'index');
+ if ($actionName == 'showInContext') {
+ throw new Exception("Preventing infinite recursion...");
+ }
+ $view = $this->getDefaultIndexView();
+ $view->content = Piwik_FrontController::getInstance()->fetchDispatch($controllerName, $actionName);
+ echo $view->render();
+ }
+
+ protected function getDefaultIndexView()
+ {
+ $view = Piwik_View::factory('index');
+ $this->setGeneralVariablesView($view);
+ $view->menu = Piwik_GetMenu();
+ $view->content = '';
+ return $view;
+ }
+
+ protected function setDateTodayIfWebsiteCreatedToday()
+ {
+ $date = Piwik_Common::getRequestVar('date', false);
+ if ($date == 'today'
+ || Piwik_Common::getRequestVar('period', false) == 'range'
+ ) {
+ return;
+ }
+ $websiteId = Piwik_Common::getRequestVar('idSite', false, 'int');
+ if ($websiteId) {
+ $website = new Piwik_Site($websiteId);
+ $datetimeCreationDate = $this->site->getCreationDate()->getDatetime();
+ $creationDateLocalTimezone = Piwik_Date::factory($datetimeCreationDate, $website->getTimezone())->toString('Y-m-d');
+ $todayLocalTimezone = Piwik_Date::factory('now', $website->getTimezone())->toString('Y-m-d');
+ if ($creationDateLocalTimezone == $todayLocalTimezone) {
+ Piwik::redirectToModule('CoreHome', 'index',
+ array('date' => 'today',
+ 'idSite' => $websiteId,
+ 'period' => Piwik_Common::getRequestVar('period'))
+ );
+ }
+ }
+ }
+
+ public function index()
+ {
+ $this->setDateTodayIfWebsiteCreatedToday();
+ $view = $this->getDefaultIndexView();
+ echo $view->render();
+ }
+
+ /*
+ * This method is called when the asset manager is configured in merged mode.
+ * It returns the content of the css merged file.
+ *
+ * @see core/AssetManager.php
+ */
+ public function getCss()
+ {
+ $cssMergedFile = Piwik_AssetManager::getMergedCssFileLocation();
+ Piwik::serveStaticFile($cssMergedFile, "text/css");
+ }
+
+ /*
+ * This method is called when the asset manager is configured in merged mode.
+ * It returns the content of the js merged file.
+ *
+ * @see core/AssetManager.php
+ */
+ public function getJs()
+ {
+ $jsMergedFile = Piwik_AssetManager::getMergedJsFileLocation();
+ Piwik::serveStaticFile($jsMergedFile, "application/javascript; charset=UTF-8");
+ }
+
+
+ // --------------------------------------------------------
+ // ROW EVOLUTION
+ // The following methods render the popover that shows the
+ // evolution of a singe or multiple rows in a data table
+ // --------------------------------------------------------
+
+ /**
+ * This static cache is necessary because the signature cannot be modified
+ * if the method renders a ViewDataTable. So we use it to pass information
+ * to getRowEvolutionGraph()
+ * @var Piwik_CoreHome_DataTableAction_Evolution
+ */
+ private static $rowEvolutionCache = null;
+
+ /** Render the entire row evolution popover for a single row */
+ public function getRowEvolutionPopover()
+ {
+ $rowEvolution = $this->makeRowEvolution($isMulti = false);
+ self::$rowEvolutionCache = $rowEvolution;
+ $view = Piwik_View::factory('popover_rowevolution');
+ echo $rowEvolution->renderPopover($this, $view);
+ }
+
+ /** Render the entire row evolution popover for multiple rows */
+ public function getMultiRowEvolutionPopover()
+ {
+ $rowEvolution = $this->makeRowEvolution($isMulti = true);
+ self::$rowEvolutionCache = $rowEvolution;
+ $view = Piwik_View::factory('popover_multirowevolution');
+ echo $rowEvolution->renderPopover($this, $view);
+ }
+
+ /** Generic method to get an evolution graph or a sparkline for the row evolution popover */
+ public function getRowEvolutionGraph($fetch = false)
+ {
+ $rowEvolution = self::$rowEvolutionCache;
+ if ($rowEvolution === null) {
+ $paramName = Piwik_CoreHome_DataTableRowAction_MultiRowEvolution::IS_MULTI_EVOLUTION_PARAM;
+ $isMultiRowEvolution = Piwik_Common::getRequestVar($paramName, false, 'int');
+
+ $rowEvolution = $this->makeRowEvolution($isMultiRowEvolution, $graphType = 'graphEvolution');
+ $rowEvolution->useAvailableMetrics();
+ self::$rowEvolutionCache = $rowEvolution;
+ }
+
+ $view = $rowEvolution->getRowEvolutionGraph();
+ return $this->renderView($view, $fetch);
+ }
+
+ /** Utility function. Creates a RowEvolution instance. */
+ private function makeRowEvolution($isMultiRowEvolution, $graphType = null)
+ {
+ if ($isMultiRowEvolution) {
+ return new Piwik_CoreHome_DataTableRowAction_MultiRowEvolution($this->idSite, $this->date, $graphType);
+ } else {
+ return new Piwik_CoreHome_DataTableRowAction_RowEvolution($this->idSite, $this->date, $graphType);
+ }
+ }
+
+ /**
+ * Forces a check for updates and re-renders the header message.
+ *
+ * This will check piwik.org at most once per 10s.
+ */
+ public function checkForUpdates()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ $this->checkTokenInUrl();
+
+ // perform check (but only once every 10s)
+ Piwik_UpdateCheck::check($force = false, Piwik_UpdateCheck::UI_CLICK_CHECK_INTERVAL);
+
+ $view = Piwik_View::factory('header_message');
+ $this->setGeneralVariablesView($view);
+ echo $view->render();
+ }
+
+ /**
+ * Renders and echo's the in-app donate form w/ slider.
+ */
+ public function getDonateForm()
+ {
+ $view = Piwik_View::factory('donate');
+ if (Piwik_Common::getRequestVar('widget', false)
+ && Piwik::isUserIsSuperUser()
+ ) {
+ $view->footerMessage = Piwik_Translate('CoreHome_OnlyForAdmin');
+ }
+ echo $view->render();
+ }
+
+ /**
+ * Renders and echo's HTML that displays the Piwik promo video.
+ */
+ public function getPromoVideo()
+ {
+ $view = Piwik_View::factory('promo_video');
+ $view->shareText = Piwik_Translate('CoreHome_SharePiwikShort');
+ $view->shareTextLong = Piwik_Translate('CoreHome_SharePiwikLong');
+ $view->promoVideoUrl = 'http://www.youtube.com/watch?v=OslfF_EH81g';
+ echo $view->render();
+ }
}
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php
index 1d965a06bf..fda7884a6c 100644
--- a/plugins/CoreHome/CoreHome.php
+++ b/plugins/CoreHome/CoreHome.php
@@ -15,87 +15,87 @@
*/
class Piwik_CoreHome extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('CoreHome_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'WidgetsList.add' => 'addWidgets',
- );
- }
-
- /**
- * Adds the donate form widget.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function addWidgets()
- {
- Piwik_AddWidget('Example Widgets', 'CoreHome_SupportPiwik', 'CoreHome', 'getDonateForm');
- Piwik_AddWidget('Example Widgets', 'Installation_Welcome', 'CoreHome', 'getPromoVideo');
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('CoreHome_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "libs/jquery/themes/base/jquery-ui.css";
- $cssFiles[] = "themes/default/common.css";
- $cssFiles[] = "plugins/CoreHome/templates/styles.css";
- $cssFiles[] = "plugins/CoreHome/templates/menu.css";
- $cssFiles[] = "plugins/CoreHome/templates/datatable.css";
- $cssFiles[] = "plugins/CoreHome/templates/cloud.css";
- $cssFiles[] = "plugins/CoreHome/templates/jquery.ui.autocomplete.css";
- $cssFiles[] = "plugins/CoreHome/templates/jqplot.css";
- $cssFiles[] = "plugins/CoreHome/templates/donate.css";
- }
+ function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'WidgetsList.add' => 'addWidgets',
+ );
+ }
+
+ /**
+ * Adds the donate form widget.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function addWidgets()
+ {
+ Piwik_AddWidget('Example Widgets', 'CoreHome_SupportPiwik', 'CoreHome', 'getDonateForm');
+ Piwik_AddWidget('Example Widgets', 'Installation_Welcome', 'CoreHome', 'getPromoVideo');
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+
+ $cssFiles[] = "libs/jquery/themes/base/jquery-ui.css";
+ $cssFiles[] = "themes/default/common.css";
+ $cssFiles[] = "plugins/CoreHome/templates/styles.css";
+ $cssFiles[] = "plugins/CoreHome/templates/menu.css";
+ $cssFiles[] = "plugins/CoreHome/templates/datatable.css";
+ $cssFiles[] = "plugins/CoreHome/templates/cloud.css";
+ $cssFiles[] = "plugins/CoreHome/templates/jquery.ui.autocomplete.css";
+ $cssFiles[] = "plugins/CoreHome/templates/jqplot.css";
+ $cssFiles[] = "plugins/CoreHome/templates/donate.css";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+
+ $jsFiles[] = "libs/jquery/jquery.js";
+ $jsFiles[] = "libs/jquery/jquery-ui.js";
+ $jsFiles[] = "libs/jquery/jquery.tooltip.js";
+ $jsFiles[] = "libs/jquery/jquery.truncate.js";
+ $jsFiles[] = "libs/jquery/jquery.scrollTo.js";
+ $jsFiles[] = "libs/jquery/jquery.history.js";
+ $jsFiles[] = "libs/javascript/sprintf.js";
+ $jsFiles[] = "themes/default/common.js";
+ $jsFiles[] = "themes/default/ajaxHelper.js";
+ $jsFiles[] = "plugins/CoreHome/templates/tooltip.js";
+ $jsFiles[] = "plugins/CoreHome/templates/datatable.js";
+ $jsFiles[] = "plugins/CoreHome/templates/datatable_rowactions.js";
+ $jsFiles[] = "plugins/CoreHome/templates/popover.js";
+ $jsFiles[] = "plugins/CoreHome/templates/broadcast.js";
+ $jsFiles[] = "plugins/CoreHome/templates/menu.js";
+ $jsFiles[] = "plugins/CoreHome/templates/menu_init.js";
+ $jsFiles[] = "plugins/CoreHome/templates/calendar.js";
+ $jsFiles[] = "plugins/CoreHome/templates/date.js";
+ $jsFiles[] = "plugins/CoreHome/templates/autocomplete.js";
+ $jsFiles[] = "plugins/CoreHome/templates/sparkline.js";
+ $jsFiles[] = "plugins/CoreHome/templates/misc.js";
+ $jsFiles[] = "plugins/CoreHome/templates/datatable_manager.js";
+ $jsFiles[] = "plugins/CoreHome/templates/donate.js";
+
+ $jsFiles[] = "plugins/CoreHome/templates/jqplot.js";
+ $jsFiles[] = "libs/jqplot/jqplot-custom.min.js";
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
-
- $jsFiles[] = "libs/jquery/jquery.js";
- $jsFiles[] = "libs/jquery/jquery-ui.js";
- $jsFiles[] = "libs/jquery/jquery.tooltip.js";
- $jsFiles[] = "libs/jquery/jquery.truncate.js";
- $jsFiles[] = "libs/jquery/jquery.scrollTo.js";
- $jsFiles[] = "libs/jquery/jquery.history.js";
- $jsFiles[] = "libs/javascript/sprintf.js";
- $jsFiles[] = "themes/default/common.js";
- $jsFiles[] = "themes/default/ajaxHelper.js";
- $jsFiles[] = "plugins/CoreHome/templates/tooltip.js";
- $jsFiles[] = "plugins/CoreHome/templates/datatable.js";
- $jsFiles[] = "plugins/CoreHome/templates/datatable_rowactions.js";
- $jsFiles[] = "plugins/CoreHome/templates/popover.js";
- $jsFiles[] = "plugins/CoreHome/templates/broadcast.js";
- $jsFiles[] = "plugins/CoreHome/templates/menu.js";
- $jsFiles[] = "plugins/CoreHome/templates/menu_init.js";
- $jsFiles[] = "plugins/CoreHome/templates/calendar.js";
- $jsFiles[] = "plugins/CoreHome/templates/date.js";
- $jsFiles[] = "plugins/CoreHome/templates/autocomplete.js";
- $jsFiles[] = "plugins/CoreHome/templates/sparkline.js";
- $jsFiles[] = "plugins/CoreHome/templates/misc.js";
- $jsFiles[] = "plugins/CoreHome/templates/datatable_manager.js";
- $jsFiles[] = "plugins/CoreHome/templates/donate.js";
-
- $jsFiles[] = "plugins/CoreHome/templates/jqplot.js";
- $jsFiles[] = "libs/jqplot/jqplot-custom.min.js";
- }
-
}
diff --git a/plugins/CoreHome/DataTableRowAction/MultiRowEvolution.php b/plugins/CoreHome/DataTableRowAction/MultiRowEvolution.php
index 659030a67e..4c4a964c2e 100644
--- a/plugins/CoreHome/DataTableRowAction/MultiRowEvolution.php
+++ b/plugins/CoreHome/DataTableRowAction/MultiRowEvolution.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_CoreHome
*/
@@ -15,71 +15,71 @@
* @package Piwik_CoreHome
*/
class Piwik_CoreHome_DataTableRowAction_MultiRowEvolution
- extends Piwik_CoreHome_DataTableRowAction_RowEvolution
+ extends Piwik_CoreHome_DataTableRowAction_RowEvolution
{
- const IS_MULTI_EVOLUTION_PARAM = 'is_multi_evolution';
-
- /** The requested metric */
- protected $metric;
+ const IS_MULTI_EVOLUTION_PARAM = 'is_multi_evolution';
- /** Show all metrics in the evolution graph when the popover opens */
- protected $initiallyShowAllMetrics = true;
-
- /** The metrics available in the metrics select */
- protected $metricsForSelect;
-
- /**
- * The constructor
- * @param int
- * @param Piwik_Date ($this->date from controller)
- */
- public function __construct($idSite, $date)
- {
- $this->metric = Piwik_Common::getRequestVar('column', '', 'string');
- parent::__construct($idSite, $date);
- }
-
- protected function loadEvolutionReport($column = false)
- {
- // set the "column" parameter for the API.getRowEvolution call
- parent::loadEvolutionReport($this->metric);
- }
-
- protected function extractEvolutionReport($report)
- {
- $this->metric = $report['column'];
- $this->dataTable = $report['reportData'];
- $this->availableMetrics = $report['metadata']['metrics'];
- $this->metricsForSelect = $report['metadata']['columns'];
- $this->dimension = $report['metadata']['dimension'];
- }
-
- /**
- * Render the popover
- * @param Piwik_CoreHome_Controller
- * @param Piwik_View (the popover_rowevolution template)
- */
- public function renderPopover($controller, $view)
- {
- // add data for metric select box
- $view->availableMetrics = $this->metricsForSelect;
- $view->selectedMetric = $this->metric;
-
- $view->availableRecordsText = $this->dimension.': '
- .Piwik_Translate('RowEvolution_ComparingRecords', array(count($this->availableMetrics)));
-
- return parent::renderPopover($controller, $view);
- }
-
- /**
- * Generic method to get an evolution graph or a sparkline for the row evolution popover.
- * Do as much as possible from outside the controller.
- * @return Piwik_ViewDataTable
- */
- public function getRowEvolutionGraph()
- {
- $view = parent::getRowEvolutionGraph();
- $view->setCustomParameter(self::IS_MULTI_EVOLUTION_PARAM, true);
- return $view;
- }
+ /** The requested metric */
+ protected $metric;
+
+ /** Show all metrics in the evolution graph when the popover opens */
+ protected $initiallyShowAllMetrics = true;
+
+ /** The metrics available in the metrics select */
+ protected $metricsForSelect;
+
+ /**
+ * The constructor
+ * @param int
+ * @param Piwik_Date ($this->date from controller)
+ */
+ public function __construct($idSite, $date)
+ {
+ $this->metric = Piwik_Common::getRequestVar('column', '', 'string');
+ parent::__construct($idSite, $date);
+ }
+
+ protected function loadEvolutionReport($column = false)
+ {
+ // set the "column" parameter for the API.getRowEvolution call
+ parent::loadEvolutionReport($this->metric);
+ }
+
+ protected function extractEvolutionReport($report)
+ {
+ $this->metric = $report['column'];
+ $this->dataTable = $report['reportData'];
+ $this->availableMetrics = $report['metadata']['metrics'];
+ $this->metricsForSelect = $report['metadata']['columns'];
+ $this->dimension = $report['metadata']['dimension'];
+ }
+
+ /**
+ * Render the popover
+ * @param Piwik_CoreHome_Controller
+ * @param Piwik_View (the popover_rowevolution template)
+ */
+ public function renderPopover($controller, $view)
+ {
+ // add data for metric select box
+ $view->availableMetrics = $this->metricsForSelect;
+ $view->selectedMetric = $this->metric;
+
+ $view->availableRecordsText = $this->dimension . ': '
+ . Piwik_Translate('RowEvolution_ComparingRecords', array(count($this->availableMetrics)));
+
+ return parent::renderPopover($controller, $view);
+ }
+
+ /**
+ * Generic method to get an evolution graph or a sparkline for the row evolution popover.
+ * Do as much as possible from outside the controller.
+ * @return Piwik_ViewDataTable
+ */
+ public function getRowEvolutionGraph()
+ {
+ $view = parent::getRowEvolutionGraph();
+ $view->setCustomParameter(self::IS_MULTI_EVOLUTION_PARAM, true);
+ return $view;
+ }
}
diff --git a/plugins/CoreHome/DataTableRowAction/RowEvolution.php b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
index bb56d3e52e..8561d8734a 100644
--- a/plugins/CoreHome/DataTableRowAction/RowEvolution.php
+++ b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_CoreHome
*/
@@ -16,283 +16,269 @@
*/
class Piwik_CoreHome_DataTableRowAction_RowEvolution
{
-
- /** The current site id */
- protected $idSite;
-
- /** The api method to get the data. Format: Plugin.apiAction */
- protected $apiMethod;
-
- /** The label of the requested row */
- protected $label;
-
- /** The requested period */
- protected $period;
-
- /** The requested date */
- protected $date;
-
- /** The request segment */
- protected $segment;
-
- /** The metrics that are available for the requested report and period */
- protected $availableMetrics;
-
- /** The name of the dimension of the current report */
- protected $dimension;
-
- /**
- * The data
- * @var Piwik_DataTable_Array
- */
- protected $dataTable;
-
- /** The label of the current record */
- protected $rowLabel;
-
- /** The icon of the current record */
- protected $rowIcon;
-
- /** The type of graph that has been requested last */
- protected $graphType;
-
- /** The metrics for the graph that has been requested last */
- protected $graphMetrics;
-
- /** Whether or not to show all metrics in the evolution graph when to popover opens */
- protected $initiallyShowAllMetrics = false;
-
- /**
- * The constructor
- * Initialize some local variables from the request
- * @param int $idSite
- * @param Piwik_Date $date ($this->date from controller)
- * @throws Exception
- */
- public function __construct($idSite, $date, $graphType = null)
- {
- $this->apiMethod = Piwik_Common::getRequestVar('apiMethod', '', 'string');
- if (empty($this->apiMethod)) throw new Exception("Parameter apiMethod not set.");
-
- $this->label = Piwik_Common::getRequestVar('label', '', 'string');
- $this->label = Piwik_Common::unsanitizeInputValue($this->label);
- if ($this->label === '') throw new Exception("Parameter label not set.");
-
- $this->period = Piwik_Common::getRequestVar('period', '', 'string');
- if (empty($this->period)) throw new Exception("Parameter period not set.");
-
- $this->idSite = $idSite;
- $this->graphType = $graphType;
-
- if ($this->period != 'range')
- {
- // handle day, week, month and year: display last X periods
- $end = $date->toString();
- list($this->date, $lastN) =
- Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution::getDateRangeAndLastN($this->period, $end);
- }
- $this->segment = Piwik_Common::getRequestVar('segment', '', 'string');
-
- $this->loadEvolutionReport();
- }
-
- /**
- * Render the popover
- * @param Piwik_CoreHome_Controller
- * @param Piwik_View (the popover_rowevolution template)
- */
- public function renderPopover($controller, $view)
- {
- // render main evolution graph
- $this->graphType = 'graphEvolution';
- $this->graphMetrics = $this->availableMetrics;
- $view->graph = $controller->getRowEvolutionGraph(true);
-
- // render metrics overview
- $view->metrics = $this->getMetricsToggles($controller);
-
- // available metrics text
- $metricsText = Piwik_Translate('RowEvolution_AvailableMetrics');
- $popoverTitle = '';
- if ($this->rowLabel)
- {
- $icon = $this->rowIcon ? '<img src="'.$this->rowIcon.'" alt="">' : '';
- $rowLabel = str_replace('/', '<wbr>/', str_replace('&', '<wbr>&', $this->rowLabel));
- $metricsText = sprintf(Piwik_Translate('RowEvolution_MetricsFor'), $this->dimension.': '.$icon.' '.$rowLabel);
- $popoverTitle = $icon.' '.$rowLabel;
- }
-
- $view->availableMetricsText = $metricsText;
- $view->popoverTitle = $popoverTitle;
-
- return $view->render();
- }
-
- protected function loadEvolutionReport($column = false)
- {
- list($apiModule, $apiAction) = explode('.', $this->apiMethod);
-
- $parameters = array(
- 'method' => 'API.getRowEvolution',
- 'label' => urlencode($this->label),
- 'apiModule' => $apiModule,
- 'apiAction' => $apiAction,
- 'idSite' => $this->idSite,
- 'period' => $this->period,
- 'date' => $this->date,
- 'format' => 'original',
- 'serialize' => '0'
- );
- if(!empty($this->segment))
- {
- $parameters['segment'] = $this->segment;
- }
-
- if ($column !== false)
- {
- $parameters['column'] = $column;
- }
-
- $url = Piwik_Url::getQueryStringFromParameters($parameters);
-
- $request = new Piwik_API_Request($url);
- $report = $request->process();
-
- $this->extractEvolutionReport($report);
- }
-
- protected function extractEvolutionReport($report)
- {
- $this->dataTable = $report['reportData'];
- $this->rowLabel = Piwik_Common::sanitizeInputValue($report['label']);
- $this->rowIcon = !empty($report['logo']) ? $report['logo'] : false;
- $this->availableMetrics = $report['metadata']['metrics'];
- $this->dimension = $report['metadata']['dimension'];
- }
-
- /**
- * Generic method to get an evolution graph or a sparkline for the row evolution popover.
- * Do as much as possible from outside the controller.
- * @return Piwik_ViewDataTable
- */
- public function getRowEvolutionGraph()
- {
- // set up the view data table
- $view = Piwik_ViewDataTable::factory($this->graphType);
- $view->setDataTable($this->dataTable);
- $view->init('CoreHome', 'getRowEvolutionGraph', $this->apiMethod);
-
- if(!empty($this->graphMetrics)) // In row Evolution popover, this is empty
- {
- $view->setColumnsToDisplay(array_keys($this->graphMetrics));
- }
- $view->hideAllViewsIcons();
-
- foreach ($this->availableMetrics as $metric => $metadata)
- {
- $view->setColumnTranslation($metric, $metadata['name']);
- }
-
- if (method_exists($view, 'addRowEvolutionSeriesToggle'))
- {
- $view->addRowEvolutionSeriesToggle($this->initiallyShowAllMetrics);
- }
-
- return $view;
- }
-
- /**
- * Prepare metrics toggles with spark lines
- * @param $controller
- * @return array
- */
- protected function getMetricsToggles($controller)
- {
- $chart = new Piwik_Visualization_Chart_Evolution;
- $colors = $chart->getSeriesColors();
-
- $i = 0;
- $metrics = array();
- foreach ($this->availableMetrics as $metric => $metricData)
- {
- $max = isset($metricData['max']) ? $metricData['max'] : 0;
- $min = isset($metricData['min']) ? $metricData['min'] : 0;
- $change = isset($metricData['change']) ? $metricData['change'] : false;
-
- $unit = Piwik_API_API::getUnit($metric, $this->idSite);
- $min .= $unit;
- $max .= $unit;
-
- $details = Piwik_Translate('RowEvolution_MetricBetweenText', array($min, $max));
-
- if ($change !== false)
- {
- $lowerIsBetter = Piwik_API_API::isLowerValueBetter($metric);
- if (substr($change, 0, 1) == '+')
- {
- $changeClass = $lowerIsBetter ? 'bad' : 'good';
- $changeImage = $lowerIsBetter ? 'arrow_up_red' : 'arrow_up';
- }
- else if (substr($change, 0, 1) == '-')
- {
- $changeClass = $lowerIsBetter ? 'good' : 'bad';
- $changeImage = $lowerIsBetter ? 'arrow_down_green' : 'arrow_down';
- }
- else
- {
- $changeClass = 'neutral';
- $changeImage = false;
- }
-
- $change = '<span class="'.$changeClass.'">'
- .($changeImage ? '<img src="plugins/MultiSites/images/'.$changeImage.'.png" /> ' : '')
- .$change.'</span>';
-
- $details .= ', '.Piwik_Translate('RowEvolution_MetricChangeText', $change);
- }
-
- $color = $colors[ $i % count($colors) ];
- $newMetric = array(
- 'label' => $metricData['name'],
- 'color' => $color,
- 'details' => $details,
- 'sparkline' => $this->getSparkline($metric, $controller),
- );
- // Multi Rows, each metric can be for a particular row and display an icon
- if(!empty($metricData['logo']))
- {
- $newMetric['logo'] = $metricData['logo'];
- }
- $metrics[] = $newMetric;
- $i++;
- }
-
- return $metrics;
- }
-
- /** Get the img tag for a sparkline showing a single metric */
- protected function getSparkline($metric, $controller)
- {
- $this->graphType = 'sparkline';
- $this->graphMetrics = array($metric => $metric);
-
- // sparkline is always echoed, so we need to buffer the output
- ob_start();
- $controller->getRowEvolutionGraph();
- $spark = ob_get_contents();
- ob_end_clean();
-
- // undo header change by sparkline renderer
- header('Content-type: text/html');
-
- // base64 encode the image and put it in an img tag
- $spark = base64_encode($spark);
- return '<img src="data:image/png;base64,'.$spark.'" />';
- }
-
- /** Use the available metrics for the metrics of the last requested graph. */
- public function useAvailableMetrics()
- {
- $this->graphMetrics = $this->availableMetrics;
- }
+
+ /** The current site id */
+ protected $idSite;
+
+ /** The api method to get the data. Format: Plugin.apiAction */
+ protected $apiMethod;
+
+ /** The label of the requested row */
+ protected $label;
+
+ /** The requested period */
+ protected $period;
+
+ /** The requested date */
+ protected $date;
+
+ /** The request segment */
+ protected $segment;
+
+ /** The metrics that are available for the requested report and period */
+ protected $availableMetrics;
+
+ /** The name of the dimension of the current report */
+ protected $dimension;
+
+ /**
+ * The data
+ * @var Piwik_DataTable_Array
+ */
+ protected $dataTable;
+
+ /** The label of the current record */
+ protected $rowLabel;
+
+ /** The icon of the current record */
+ protected $rowIcon;
+
+ /** The type of graph that has been requested last */
+ protected $graphType;
+
+ /** The metrics for the graph that has been requested last */
+ protected $graphMetrics;
+
+ /** Whether or not to show all metrics in the evolution graph when to popover opens */
+ protected $initiallyShowAllMetrics = false;
+
+ /**
+ * The constructor
+ * Initialize some local variables from the request
+ * @param int $idSite
+ * @param Piwik_Date $date ($this->date from controller)
+ * @throws Exception
+ */
+ public function __construct($idSite, $date, $graphType = null)
+ {
+ $this->apiMethod = Piwik_Common::getRequestVar('apiMethod', '', 'string');
+ if (empty($this->apiMethod)) throw new Exception("Parameter apiMethod not set.");
+
+ $this->label = Piwik_Common::getRequestVar('label', '', 'string');
+ $this->label = Piwik_Common::unsanitizeInputValue($this->label);
+ if ($this->label === '') throw new Exception("Parameter label not set.");
+
+ $this->period = Piwik_Common::getRequestVar('period', '', 'string');
+ if (empty($this->period)) throw new Exception("Parameter period not set.");
+
+ $this->idSite = $idSite;
+ $this->graphType = $graphType;
+
+ if ($this->period != 'range') {
+ // handle day, week, month and year: display last X periods
+ $end = $date->toString();
+ list($this->date, $lastN) =
+ Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution::getDateRangeAndLastN($this->period, $end);
+ }
+ $this->segment = Piwik_Common::getRequestVar('segment', '', 'string');
+
+ $this->loadEvolutionReport();
+ }
+
+ /**
+ * Render the popover
+ * @param Piwik_CoreHome_Controller
+ * @param Piwik_View (the popover_rowevolution template)
+ */
+ public function renderPopover($controller, $view)
+ {
+ // render main evolution graph
+ $this->graphType = 'graphEvolution';
+ $this->graphMetrics = $this->availableMetrics;
+ $view->graph = $controller->getRowEvolutionGraph(true);
+
+ // render metrics overview
+ $view->metrics = $this->getMetricsToggles($controller);
+
+ // available metrics text
+ $metricsText = Piwik_Translate('RowEvolution_AvailableMetrics');
+ $popoverTitle = '';
+ if ($this->rowLabel) {
+ $icon = $this->rowIcon ? '<img src="' . $this->rowIcon . '" alt="">' : '';
+ $rowLabel = str_replace('/', '<wbr>/', str_replace('&', '<wbr>&', $this->rowLabel));
+ $metricsText = sprintf(Piwik_Translate('RowEvolution_MetricsFor'), $this->dimension . ': ' . $icon . ' ' . $rowLabel);
+ $popoverTitle = $icon . ' ' . $rowLabel;
+ }
+
+ $view->availableMetricsText = $metricsText;
+ $view->popoverTitle = $popoverTitle;
+
+ return $view->render();
+ }
+
+ protected function loadEvolutionReport($column = false)
+ {
+ list($apiModule, $apiAction) = explode('.', $this->apiMethod);
+
+ $parameters = array(
+ 'method' => 'API.getRowEvolution',
+ 'label' => urlencode($this->label),
+ 'apiModule' => $apiModule,
+ 'apiAction' => $apiAction,
+ 'idSite' => $this->idSite,
+ 'period' => $this->period,
+ 'date' => $this->date,
+ 'format' => 'original',
+ 'serialize' => '0'
+ );
+ if (!empty($this->segment)) {
+ $parameters['segment'] = $this->segment;
+ }
+
+ if ($column !== false) {
+ $parameters['column'] = $column;
+ }
+
+ $url = Piwik_Url::getQueryStringFromParameters($parameters);
+
+ $request = new Piwik_API_Request($url);
+ $report = $request->process();
+
+ $this->extractEvolutionReport($report);
+ }
+
+ protected function extractEvolutionReport($report)
+ {
+ $this->dataTable = $report['reportData'];
+ $this->rowLabel = Piwik_Common::sanitizeInputValue($report['label']);
+ $this->rowIcon = !empty($report['logo']) ? $report['logo'] : false;
+ $this->availableMetrics = $report['metadata']['metrics'];
+ $this->dimension = $report['metadata']['dimension'];
+ }
+
+ /**
+ * Generic method to get an evolution graph or a sparkline for the row evolution popover.
+ * Do as much as possible from outside the controller.
+ * @return Piwik_ViewDataTable
+ */
+ public function getRowEvolutionGraph()
+ {
+ // set up the view data table
+ $view = Piwik_ViewDataTable::factory($this->graphType);
+ $view->setDataTable($this->dataTable);
+ $view->init('CoreHome', 'getRowEvolutionGraph', $this->apiMethod);
+
+ if (!empty($this->graphMetrics)) // In row Evolution popover, this is empty
+ {
+ $view->setColumnsToDisplay(array_keys($this->graphMetrics));
+ }
+ $view->hideAllViewsIcons();
+
+ foreach ($this->availableMetrics as $metric => $metadata) {
+ $view->setColumnTranslation($metric, $metadata['name']);
+ }
+
+ if (method_exists($view, 'addRowEvolutionSeriesToggle')) {
+ $view->addRowEvolutionSeriesToggle($this->initiallyShowAllMetrics);
+ }
+
+ return $view;
+ }
+
+ /**
+ * Prepare metrics toggles with spark lines
+ * @param $controller
+ * @return array
+ */
+ protected function getMetricsToggles($controller)
+ {
+ $chart = new Piwik_Visualization_Chart_Evolution;
+ $colors = $chart->getSeriesColors();
+
+ $i = 0;
+ $metrics = array();
+ foreach ($this->availableMetrics as $metric => $metricData) {
+ $max = isset($metricData['max']) ? $metricData['max'] : 0;
+ $min = isset($metricData['min']) ? $metricData['min'] : 0;
+ $change = isset($metricData['change']) ? $metricData['change'] : false;
+
+ $unit = Piwik_API_API::getUnit($metric, $this->idSite);
+ $min .= $unit;
+ $max .= $unit;
+
+ $details = Piwik_Translate('RowEvolution_MetricBetweenText', array($min, $max));
+
+ if ($change !== false) {
+ $lowerIsBetter = Piwik_API_API::isLowerValueBetter($metric);
+ if (substr($change, 0, 1) == '+') {
+ $changeClass = $lowerIsBetter ? 'bad' : 'good';
+ $changeImage = $lowerIsBetter ? 'arrow_up_red' : 'arrow_up';
+ } else if (substr($change, 0, 1) == '-') {
+ $changeClass = $lowerIsBetter ? 'good' : 'bad';
+ $changeImage = $lowerIsBetter ? 'arrow_down_green' : 'arrow_down';
+ } else {
+ $changeClass = 'neutral';
+ $changeImage = false;
+ }
+
+ $change = '<span class="' . $changeClass . '">'
+ . ($changeImage ? '<img src="plugins/MultiSites/images/' . $changeImage . '.png" /> ' : '')
+ . $change . '</span>';
+
+ $details .= ', ' . Piwik_Translate('RowEvolution_MetricChangeText', $change);
+ }
+
+ $color = $colors[$i % count($colors)];
+ $newMetric = array(
+ 'label' => $metricData['name'],
+ 'color' => $color,
+ 'details' => $details,
+ 'sparkline' => $this->getSparkline($metric, $controller),
+ );
+ // Multi Rows, each metric can be for a particular row and display an icon
+ if (!empty($metricData['logo'])) {
+ $newMetric['logo'] = $metricData['logo'];
+ }
+ $metrics[] = $newMetric;
+ $i++;
+ }
+
+ return $metrics;
+ }
+
+ /** Get the img tag for a sparkline showing a single metric */
+ protected function getSparkline($metric, $controller)
+ {
+ $this->graphType = 'sparkline';
+ $this->graphMetrics = array($metric => $metric);
+
+ // sparkline is always echoed, so we need to buffer the output
+ ob_start();
+ $controller->getRowEvolutionGraph();
+ $spark = ob_get_contents();
+ ob_end_clean();
+
+ // undo header change by sparkline renderer
+ header('Content-type: text/html');
+
+ // base64 encode the image and put it in an img tag
+ $spark = base64_encode($spark);
+ return '<img src="data:image/png;base64,' . $spark . '" />';
+ }
+
+ /** Use the available metrics for the metrics of the last requested graph. */
+ public function useAvailableMetrics()
+ {
+ $this->graphMetrics = $this->availableMetrics;
+ }
}
diff --git a/plugins/CoreHome/templates/autocomplete.js b/plugins/CoreHome/templates/autocomplete.js
index b9ca092420..37af24e72c 100644
--- a/plugins/CoreHome/templates/autocomplete.js
+++ b/plugins/CoreHome/templates/autocomplete.js
@@ -5,248 +5,229 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function switchSite(id, name, showAjaxLoading, idCanBeAll)
-{
- if (id == 'all'
- && !idCanBeAll)
- {
- broadcast.propagateNewPage('module=MultiSites&action=index');
- }
- else
- {
- $('.sites_autocomplete input').val(id);
- $('.custom_select_main_link').text(name);
- $('.custom_select_main_link').addClass('custom_select_loading');
- broadcast.propagateNewPage('idSite='+id, showAjaxLoading);
- }
+function switchSite(id, name, showAjaxLoading, idCanBeAll) {
+ if (id == 'all'
+ && !idCanBeAll) {
+ broadcast.propagateNewPage('module=MultiSites&action=index');
+ }
+ else {
+ $('.sites_autocomplete input').val(id);
+ $('.custom_select_main_link').text(name);
+ $('.custom_select_main_link').addClass('custom_select_loading');
+ broadcast.propagateNewPage('idSite=' + id, showAjaxLoading);
+ }
return false;
}
-$(function() {
-
- var reset = function(selector)
- {
- $('.websiteSearch', selector).val('');
- $('.custom_select_ul_list', selector).show();
- $(".siteSelect.ui-autocomplete,.reset", selector).hide();
- };
-
- // sets up every un-inited site selector widget
- piwik.initSiteSelectors = function()
- {
- $('.sites_autocomplete').each(function() {
- var selector = $(this);
-
- if (selector.attr('data-inited') == 1)
- {
- return;
- }
-
- selector.attr('data-inited', 1);
-
- var websiteSearch = $('.websiteSearch', selector);
-
- // when the search input is clicked, clear the input
- websiteSearch.click(function() {
- $(this).val('');
- });
-
- // when a key is released over the search input when empty, reset the selector
- //
- websiteSearch.keyup(function(e) {
- if (e.keyCode == 27)
- {
- $('.custom_select_block', selector).removeClass('custom_select_block_show');
- return false;
- }
-
- if (!$(this).val())
- {
- reset(selector);
- }
- });
-
- // setup the autocompleter
- websiteSearch.autocomplete({
- minLength: 1,
- source: '?module=SitesManager&action=getSitesForAutocompleter',
- appendTo: $('.custom_select_container', selector),
- select: function(event, ui) {
- if (ui.item.id > 0)
- {
- // set attributes of selected site display (what shows in the box)
- $('.custom_select_main_link', selector)
- .attr('siteid', ui.item.id)
- .text(ui.item.name);
-
- // hide the dropdown
- $('.custom_select_block', selector).removeClass('custom_select_block_show');
-
- // fire the site selected event
- selector.trigger('piwik:siteSelected', ui.item);
- }
- else
- {
- reset(selector);
- }
-
- return false;
- },
- focus: function(event, ui) {
- $('.websiteSearch', selector).val(ui.item.name);
- return false;
- },
- search: function(event, ui) {
- $('.reset', selector).show();
- $('.custom_select_main_link', selector).addClass('custom_select_loading');
- },
- open: function(event, ui) {
- var widthSitesSelection = +$('.custom_select_ul_list', selector).width();
-
- $('.custom_select_main_link', selector).removeClass('custom_select_loading');
-
- var maxSitenameWidth = $('.max_sitename_width', selector);
- if (widthSitesSelection > maxSitenameWidth.val())
- {
- maxSitenameWidth.val(widthSitesSelection);
- }
- else
- {
- maxSitenameWidth = +maxSitenameWidth.val(); // convert to int
- }
-
- $('.custom_select_ul_list', selector).hide();
-
- // customize jquery-ui's autocomplete positioning
- var cssToRemove = {float: 'none', position: 'static'};
- $('.siteSelect.ui-autocomplete', selector)
- .show().width(widthSitesSelection).css(cssToRemove)
- .find('li,a').each(function() {
- $(this).css(cssToRemove);
- });
-
- $('.custom_select_block_show', selector).width(widthSitesSelection);
- }
- }).data("autocomplete")._renderItem = function (ul, item) {
- $(ul).addClass('siteSelect');
-
- var idSiteParam = 'idSite=' + item.id,
- hash = broadcast.isHashExists() ? broadcast.getHashFromUrl().replace(/idSite=[0-9]+/, idSiteParam) : "",
- linkUrl = piwikHelper.getCurrentQueryStringWithParametersModified(idSiteParam) + hash,
- link = $("<a></a>").html(item.label).attr('href', linkUrl),
- listItem = $('<li></li>');
-
- listItem.data("item.autocomplete", item)
- .append(link)
- .appendTo(ul);
-
- link.click(function(e) {
- // in ie8, the event would bubble up and cause an error
- e.stopPropagation();
- return true;
- });
-
- return listItem;
- };
-
- // when the reset button is clicked, reset the site selector
- $('.reset', selector).click(reset);
-
- // when mouse button is released on body, check if it is not over the site selector, and if not
- // close it
- $('body').on('mouseup', function(e) {
- var closestSelector = $(e.target).closest('.sites_autocomplete');
- if (closestSelector != selector)
- {
- reset(selector);
- $('.custom_select_block', selector).removeClass('custom_select_block_show');
- }
- });
-
- // set event handling code for non-jquery-autocomplete parts of widget
- if ($('li', selector).length > 1)
- {
- // event handler for when site selector is clicked. shows dropdown w/ first X sites
- $(".custom_select_main_link", selector).click(function() {
- $(".custom_select_block", selector).addClass("custom_select_block_show");
- $('.custom_select_ul_list', selector).show();
- $('.websiteSearch', selector).val('').focus();
- return false;
- });
-
- $('.custom_select_block', selector).on('mouseenter', function() {
- $('.custom_select_ul_list li a', selector).each(function(){
- var hash = broadcast.getHashFromUrl();
- hash = hash ? hash.replace(/idSite=[0-9]+/, 'idSite='+$(this).attr('siteid')) : "";
-
- var queryString = piwikHelper.getCurrentQueryStringWithParametersModified(
- 'idSite=' + $(this).attr('siteid'));
- $(this).attr('href', queryString + hash);
- });
- });
-
- // change selection. fire's site selector's on select event and modifies the attributes
- // of the selected link
- $('.custom_select_ul_list li a', selector).each(function() {
- $(this).click(function (e) {
- var idsite = $(this).attr('siteid'),
- name = $(this).text();
-
- $(".custom_select_main_link", selector)
- .attr('siteid', idsite)
- .text(name);
-
- selector.trigger('piwik:siteSelected', {id: idsite, name: name});
-
- // close the dropdown
- $(".custom_select_block", selector).removeClass("custom_select_block_show");
-
- e.preventDefault();
- });
- });
-
- var inlinePaddingWidth = 22, staticPaddingWidth = 34;
- if ($(".custom_select_block ul", selector)[0])
- {
- var widthSitesSelection = Math.max(
- $(".custom_select_block ul", selector).width()+inlinePaddingWidth,
- $(".custom_select_main_link", selector).width()+staticPaddingWidth
- );
- $(".custom_select_block", selector).css('width', widthSitesSelection);
- }
- }
- else
- {
- $('.custom_select_main_link', selector).addClass('noselect');
- }
-
- // handle multi-sites link click (triggers site selected event w/ id=all)
- $('.custom_select_all', selector).click(function () {
- $(".custom_select_block", selector).toggleClass("custom_select_block_show");
-
- selector.trigger('piwik:siteSelected', {id: 'all', name: $('.custom_select_all>a', selector).text()});
- });
-
- // handle submit button click
- $('.but', selector).on('click', function(e) {
- if (websiteSearch.val() != '')
- {
- websiteSearch.autocomplete('search', websiteSearch.val() + '%%%');
- }
- return false;
- });
-
- // if the data-switch-site-on-select attribute is set to 1 on the selector, set
- // a default handler for piwik:siteSelected that switches the current site
- if (selector.attr('data-switch-site-on-select') == 1)
- {
- selector.bind('piwik:siteSelected', function (e, site) {
- if (piwik.idSite !== site.id)
- {
- switchSite(site.id, site.name);
- }
- });
- }
- });
- };
+$(function () {
+
+ var reset = function (selector) {
+ $('.websiteSearch', selector).val('');
+ $('.custom_select_ul_list', selector).show();
+ $(".siteSelect.ui-autocomplete,.reset", selector).hide();
+ };
+
+ // sets up every un-inited site selector widget
+ piwik.initSiteSelectors = function () {
+ $('.sites_autocomplete').each(function () {
+ var selector = $(this);
+
+ if (selector.attr('data-inited') == 1) {
+ return;
+ }
+
+ selector.attr('data-inited', 1);
+
+ var websiteSearch = $('.websiteSearch', selector);
+
+ // when the search input is clicked, clear the input
+ websiteSearch.click(function () {
+ $(this).val('');
+ });
+
+ // when a key is released over the search input when empty, reset the selector
+ //
+ websiteSearch.keyup(function (e) {
+ if (e.keyCode == 27) {
+ $('.custom_select_block', selector).removeClass('custom_select_block_show');
+ return false;
+ }
+
+ if (!$(this).val()) {
+ reset(selector);
+ }
+ });
+
+ // setup the autocompleter
+ websiteSearch.autocomplete({
+ minLength: 1,
+ source: '?module=SitesManager&action=getSitesForAutocompleter',
+ appendTo: $('.custom_select_container', selector),
+ select: function (event, ui) {
+ if (ui.item.id > 0) {
+ // set attributes of selected site display (what shows in the box)
+ $('.custom_select_main_link', selector)
+ .attr('siteid', ui.item.id)
+ .text(ui.item.name);
+
+ // hide the dropdown
+ $('.custom_select_block', selector).removeClass('custom_select_block_show');
+
+ // fire the site selected event
+ selector.trigger('piwik:siteSelected', ui.item);
+ }
+ else {
+ reset(selector);
+ }
+
+ return false;
+ },
+ focus: function (event, ui) {
+ $('.websiteSearch', selector).val(ui.item.name);
+ return false;
+ },
+ search: function (event, ui) {
+ $('.reset', selector).show();
+ $('.custom_select_main_link', selector).addClass('custom_select_loading');
+ },
+ open: function (event, ui) {
+ var widthSitesSelection = +$('.custom_select_ul_list', selector).width();
+
+ $('.custom_select_main_link', selector).removeClass('custom_select_loading');
+
+ var maxSitenameWidth = $('.max_sitename_width', selector);
+ if (widthSitesSelection > maxSitenameWidth.val()) {
+ maxSitenameWidth.val(widthSitesSelection);
+ }
+ else {
+ maxSitenameWidth = +maxSitenameWidth.val(); // convert to int
+ }
+
+ $('.custom_select_ul_list', selector).hide();
+
+ // customize jquery-ui's autocomplete positioning
+ var cssToRemove = {float: 'none', position: 'static'};
+ $('.siteSelect.ui-autocomplete', selector)
+ .show().width(widthSitesSelection).css(cssToRemove)
+ .find('li,a').each(function () {
+ $(this).css(cssToRemove);
+ });
+
+ $('.custom_select_block_show', selector).width(widthSitesSelection);
+ }
+ }).data("autocomplete")._renderItem = function (ul, item) {
+ $(ul).addClass('siteSelect');
+
+ var idSiteParam = 'idSite=' + item.id,
+ hash = broadcast.isHashExists() ? broadcast.getHashFromUrl().replace(/idSite=[0-9]+/, idSiteParam) : "",
+ linkUrl = piwikHelper.getCurrentQueryStringWithParametersModified(idSiteParam) + hash,
+ link = $("<a></a>").html(item.label).attr('href', linkUrl),
+ listItem = $('<li></li>');
+
+ listItem.data("item.autocomplete", item)
+ .append(link)
+ .appendTo(ul);
+
+ link.click(function (e) {
+ // in ie8, the event would bubble up and cause an error
+ e.stopPropagation();
+ return true;
+ });
+
+ return listItem;
+ };
+
+ // when the reset button is clicked, reset the site selector
+ $('.reset', selector).click(reset);
+
+ // when mouse button is released on body, check if it is not over the site selector, and if not
+ // close it
+ $('body').on('mouseup', function (e) {
+ var closestSelector = $(e.target).closest('.sites_autocomplete');
+ if (closestSelector != selector) {
+ reset(selector);
+ $('.custom_select_block', selector).removeClass('custom_select_block_show');
+ }
+ });
+
+ // set event handling code for non-jquery-autocomplete parts of widget
+ if ($('li', selector).length > 1) {
+ // event handler for when site selector is clicked. shows dropdown w/ first X sites
+ $(".custom_select_main_link", selector).click(function () {
+ $(".custom_select_block", selector).addClass("custom_select_block_show");
+ $('.custom_select_ul_list', selector).show();
+ $('.websiteSearch', selector).val('').focus();
+ return false;
+ });
+
+ $('.custom_select_block', selector).on('mouseenter', function () {
+ $('.custom_select_ul_list li a', selector).each(function () {
+ var hash = broadcast.getHashFromUrl();
+ hash = hash ? hash.replace(/idSite=[0-9]+/, 'idSite=' + $(this).attr('siteid')) : "";
+
+ var queryString = piwikHelper.getCurrentQueryStringWithParametersModified(
+ 'idSite=' + $(this).attr('siteid'));
+ $(this).attr('href', queryString + hash);
+ });
+ });
+
+ // change selection. fire's site selector's on select event and modifies the attributes
+ // of the selected link
+ $('.custom_select_ul_list li a', selector).each(function () {
+ $(this).click(function (e) {
+ var idsite = $(this).attr('siteid'),
+ name = $(this).text();
+
+ $(".custom_select_main_link", selector)
+ .attr('siteid', idsite)
+ .text(name);
+
+ selector.trigger('piwik:siteSelected', {id: idsite, name: name});
+
+ // close the dropdown
+ $(".custom_select_block", selector).removeClass("custom_select_block_show");
+
+ e.preventDefault();
+ });
+ });
+
+ var inlinePaddingWidth = 22, staticPaddingWidth = 34;
+ if ($(".custom_select_block ul", selector)[0]) {
+ var widthSitesSelection = Math.max(
+ $(".custom_select_block ul", selector).width() + inlinePaddingWidth,
+ $(".custom_select_main_link", selector).width() + staticPaddingWidth
+ );
+ $(".custom_select_block", selector).css('width', widthSitesSelection);
+ }
+ }
+ else {
+ $('.custom_select_main_link', selector).addClass('noselect');
+ }
+
+ // handle multi-sites link click (triggers site selected event w/ id=all)
+ $('.custom_select_all', selector).click(function () {
+ $(".custom_select_block", selector).toggleClass("custom_select_block_show");
+
+ selector.trigger('piwik:siteSelected', {id: 'all', name: $('.custom_select_all>a', selector).text()});
+ });
+
+ // handle submit button click
+ $('.but', selector).on('click', function (e) {
+ if (websiteSearch.val() != '') {
+ websiteSearch.autocomplete('search', websiteSearch.val() + '%%%');
+ }
+ return false;
+ });
+
+ // if the data-switch-site-on-select attribute is set to 1 on the selector, set
+ // a default handler for piwik:siteSelected that switches the current site
+ if (selector.attr('data-switch-site-on-select') == 1) {
+ selector.bind('piwik:siteSelected', function (e, site) {
+ if (piwik.idSite !== site.id) {
+ switchSite(site.id, site.name);
+ }
+ });
+ }
+ });
+ };
});
diff --git a/plugins/CoreHome/templates/broadcast.js b/plugins/CoreHome/templates/broadcast.js
index d57bca82d9..9b5a9ffdf8 100644
--- a/plugins/CoreHome/templates/broadcast.js
+++ b/plugins/CoreHome/templates/broadcast.js
@@ -27,25 +27,25 @@ var broadcast = {
*/
_isInit: false,
- /**
- * Last known hash url without popover parameter
- */
- currentHashUrl: false,
-
- /**
- * Last known popover parameter
- */
- currentPopoverParameter: false,
-
- /**
- * Callbacks for popover parameter change
- */
- popoverHandlers: [],
-
- /**
- * Force reload once
- */
- forceReload: false,
+ /**
+ * Last known hash url without popover parameter
+ */
+ currentHashUrl: false,
+
+ /**
+ * Last known popover parameter
+ */
+ currentPopoverParameter: false,
+
+ /**
+ * Callbacks for popover parameter change
+ */
+ popoverHandlers: [],
+
+ /**
+ * Force reload once
+ */
+ forceReload: false,
/**
* Suppress content update on hash changing
@@ -56,8 +56,8 @@ var broadcast = {
* Initializes broadcast object
* @return {void}
*/
- init: function() {
- if(broadcast._isInit) {
+ init: function () {
+ if (broadcast._isInit) {
return;
}
broadcast._isInit = true;
@@ -75,15 +75,14 @@ var broadcast = {
* 1. after calling $.history.init();
* 2. after calling $.history.load(); //look at broadcast.changeParameter();
* 3. after pushing "Go Back" button of a browser
- *
- * * Note: the method is manipulated in Overlay/templates/index.js - keep this in mind when making changes.
+ *
+ * * Note: the method is manipulated in Overlay/templates/index.js - keep this in mind when making changes.
*
* @param {string} hash to load page with
* @return {void}
*/
- pageload: function( hash )
- {
- broadcast.init();
+ pageload: function (hash) {
+ broadcast.init();
// Unbind any previously attached resize handlers
$(window).off('resize');
@@ -93,66 +92,66 @@ var broadcast = {
broadcast.updateHashOnly = false;
return;
}
-
- // hash doesn't contain the first # character.
- if( hash ) {
-
- var hashParts = hash.split('&popover=');
- var hashUrl = hashParts[0];
- var popoverParam = '';
- if (hashParts.length > 1) {
- popoverParam = hashParts[1];
- // in case the $ was encoded (e.g. when using copy&paste on urls in some browsers)
- popoverParam = decodeURIComponent(popoverParam);
- // revert special encoding from broadcast.propagateNewPopoverParameter()
- popoverParam = popoverParam.replace(/\$/g, '%');
- popoverParam = decodeURIComponent(popoverParam);
- }
-
- var pageUrlUpdated = (popoverParam == '' ||
- (broadcast.currentHashUrl !== false && broadcast.currentHashUrl != hashUrl));
-
- var popoverParamUpdated = (popoverParam != '' && hashUrl == broadcast.currentHashUrl);
-
- if (broadcast.currentHashUrl === false) {
- // new page load
- pageUrlUpdated = true;
- popoverParamUpdated = (popoverParam != '');
- }
-
- if (pageUrlUpdated || broadcast.forceReload) {
- Piwik_Popover.close();
-
- if (hashUrl != broadcast.currentHashUrl || broadcast.forceReload) {
- // restore ajax loaded state
- broadcast.loadAjaxContent(hashUrl);
-
- // make sure the "Widgets & Dashboard" is deleted on reload
- $('#dashboardSettings').remove();
- $('#dashboardWidgetsArea').dashboard('destroy');
- }
- }
-
- broadcast.forceReload = false;
- broadcast.currentHashUrl = hashUrl;
- broadcast.currentPopoverParameter = popoverParam;
-
- if (popoverParamUpdated && popoverParam == '') {
- Piwik_Popover.close();
- } else if (popoverParamUpdated) {
- var popoverParamParts = popoverParam.split(':');
- var handlerName = popoverParamParts[0];
- popoverParamParts.shift();
- var param = popoverParamParts.join(':');
- if (typeof broadcast.popoverHandlers[handlerName] != 'undefined') {
- broadcast.popoverHandlers[handlerName](param);
- }
- }
-
- } else {
- // start page
- $('#content').empty();
- }
+
+ // hash doesn't contain the first # character.
+ if (hash) {
+
+ var hashParts = hash.split('&popover=');
+ var hashUrl = hashParts[0];
+ var popoverParam = '';
+ if (hashParts.length > 1) {
+ popoverParam = hashParts[1];
+ // in case the $ was encoded (e.g. when using copy&paste on urls in some browsers)
+ popoverParam = decodeURIComponent(popoverParam);
+ // revert special encoding from broadcast.propagateNewPopoverParameter()
+ popoverParam = popoverParam.replace(/\$/g, '%');
+ popoverParam = decodeURIComponent(popoverParam);
+ }
+
+ var pageUrlUpdated = (popoverParam == '' ||
+ (broadcast.currentHashUrl !== false && broadcast.currentHashUrl != hashUrl));
+
+ var popoverParamUpdated = (popoverParam != '' && hashUrl == broadcast.currentHashUrl);
+
+ if (broadcast.currentHashUrl === false) {
+ // new page load
+ pageUrlUpdated = true;
+ popoverParamUpdated = (popoverParam != '');
+ }
+
+ if (pageUrlUpdated || broadcast.forceReload) {
+ Piwik_Popover.close();
+
+ if (hashUrl != broadcast.currentHashUrl || broadcast.forceReload) {
+ // restore ajax loaded state
+ broadcast.loadAjaxContent(hashUrl);
+
+ // make sure the "Widgets & Dashboard" is deleted on reload
+ $('#dashboardSettings').remove();
+ $('#dashboardWidgetsArea').dashboard('destroy');
+ }
+ }
+
+ broadcast.forceReload = false;
+ broadcast.currentHashUrl = hashUrl;
+ broadcast.currentPopoverParameter = popoverParam;
+
+ if (popoverParamUpdated && popoverParam == '') {
+ Piwik_Popover.close();
+ } else if (popoverParamUpdated) {
+ var popoverParamParts = popoverParam.split(':');
+ var handlerName = popoverParamParts[0];
+ popoverParamParts.shift();
+ var param = popoverParamParts.join(':');
+ if (typeof broadcast.popoverHandlers[handlerName] != 'undefined') {
+ broadcast.popoverHandlers[handlerName](param);
+ }
+ }
+
+ } else {
+ // start page
+ $('#content').empty();
+ }
},
/**
@@ -166,11 +165,10 @@ var broadcast = {
* NOTE: this method will only make ajax call and replacing main content.
*
* @param {string} ajaxUrl querystring with parameters to be updated
- * @param {boolean} disableHistory the hash change won't be available in the browser history
+ * @param {boolean} disableHistory the hash change won't be available in the browser history
* @return {void}
*/
- propagateAjax: function (ajaxUrl, disableHistory)
- {
+ propagateAjax: function (ajaxUrl, disableHistory) {
broadcast.init();
// abort all existing ajax requests
@@ -179,40 +177,35 @@ var broadcast = {
// available in global scope
var currentHashStr = broadcast.getHash();
- ajaxUrl = ajaxUrl.replace(/^\?|&#/,'');
-
+ ajaxUrl = ajaxUrl.replace(/^\?|&#/, '');
+
var params_vals = ajaxUrl.split("&");
- for( var i=0; i<params_vals.length; i++ )
- {
- currentHashStr = broadcast.updateParamValue(params_vals[i],currentHashStr);
+ for (var i = 0; i < params_vals.length; i++) {
+ currentHashStr = broadcast.updateParamValue(params_vals[i], currentHashStr);
}
// if the module is not 'Goals', we specifically unset the 'idGoal' parameter
// this is to ensure that the URLs are clean (and that clicks on graphs work as expected - they are broken with the extra parameter)
var action = broadcast.getParamValue('action', currentHashStr);
- if( action != 'goalReport' && action != 'ecommerceReport')
- {
+ if (action != 'goalReport' && action != 'ecommerceReport') {
currentHashStr = broadcast.updateParamValue('idGoal=', currentHashStr);
}
// unset idDashboard if use doesn't display a dashboard
var module = broadcast.getParamValue('module', currentHashStr);
- if( module != 'Dashboard')
- {
+ if (module != 'Dashboard') {
currentHashStr = broadcast.updateParamValue('idDashboard=', currentHashStr);
}
-
- if (disableHistory)
- {
- var newLocation = window.location.href.split('#')[0] + '#' + currentHashStr;
- // window.location.replace changes the current url without pushing it on the browser's history stack
- window.location.replace(newLocation);
- }
- else
- {
- // Let history know about this new Hash and load it.
- broadcast.forceReload = true;
- $.history.load(currentHashStr);
- }
+
+ if (disableHistory) {
+ var newLocation = window.location.href.split('#')[0] + '#' + currentHashStr;
+ // window.location.replace changes the current url without pushing it on the browser's history stack
+ window.location.replace(newLocation);
+ }
+ else {
+ // Let history know about this new Hash and load it.
+ broadcast.forceReload = true;
+ $.history.load(currentHashStr);
+ }
},
/**
@@ -238,14 +231,12 @@ var broadcast = {
* @param {bool} showAjaxLoading whether to show the ajax loading gif or not.
* @return {void}
*/
- propagateNewPage: function (str, showAjaxLoading)
- {
+ propagateNewPage: function (str, showAjaxLoading) {
// abort all existing ajax requests
globalAjaxQueue.abort();
-
- if (typeof showAjaxLoading === 'undefined' || showAjaxLoading)
- {
- piwikHelper.showAjaxLoading();
+
+ if (typeof showAjaxLoading === 'undefined' || showAjaxLoading) {
+ piwikHelper.showAjaxLoading();
}
var params_vals = str.split("&");
@@ -255,22 +246,22 @@ var broadcast = {
var currentHashStr = broadcast.getHashFromUrl();
var oldUrl = currentSearchStr + currentHashStr;
- for( var i=0; i<params_vals.length; i++ ) {
+ for (var i = 0; i < params_vals.length; i++) {
// update both the current search query and hash string
- currentSearchStr = broadcast.updateParamValue(params_vals[i],currentSearchStr);
+ currentSearchStr = broadcast.updateParamValue(params_vals[i], currentSearchStr);
- if(currentHashStr.length != 0 ) {
- currentHashStr = broadcast.updateParamValue(params_vals[i],currentHashStr);
+ if (currentHashStr.length != 0) {
+ currentHashStr = broadcast.updateParamValue(params_vals[i], currentHashStr);
}
}
// Now load the new page.
var newUrl = currentSearchStr + currentHashStr;
- if(oldUrl == newUrl) {
+ if (oldUrl == newUrl) {
window.location.reload();
} else {
- this.forceReload = true;
+ this.forceReload = true;
window.location.href = newUrl;
}
return false;
@@ -296,79 +287,75 @@ var broadcast = {
* @param {string} urlStr url to be updated
* @return {string} urlStr with updated param
*/
- updateParamValue: function(newParamValue, urlStr)
- {
+ updateParamValue: function (newParamValue, urlStr) {
var p_v = newParamValue.split("=");
var paramName = p_v[0];
- var valFromUrl = broadcast.getParamValue(paramName,urlStr);
+ var valFromUrl = broadcast.getParamValue(paramName, urlStr);
// if set 'idGoal=' then we remove the parameter from the URL automatically (rather than passing an empty value)
var paramValue = p_v[1];
- if(paramValue == '')
- {
+ if (paramValue == '') {
newParamValue = '';
}
- if( valFromUrl != '') {
+ if (valFromUrl != '') {
// replacing current param=value to newParamValue;
- valFromUrl = valFromUrl.replace(/\$/g, '\\$');
- valFromUrl = valFromUrl.replace(/\./g, '\\.');
+ valFromUrl = valFromUrl.replace(/\$/g, '\\$');
+ valFromUrl = valFromUrl.replace(/\./g, '\\.');
var regToBeReplace = new RegExp(paramName + '=' + valFromUrl, 'ig');
- if(newParamValue == '') {
+ if (newParamValue == '') {
// if new value is empty remove leading &, aswell
regToBeReplace = new RegExp('[\&]?' + paramName + '=' + valFromUrl, 'ig');
}
- urlStr = urlStr.replace( regToBeReplace, newParamValue );
- } else if(newParamValue != '') {
+ urlStr = urlStr.replace(regToBeReplace, newParamValue);
+ } else if (newParamValue != '') {
urlStr += (urlStr == '') ? newParamValue : '&' + newParamValue;
}
return urlStr;
},
- /**
- * Update the part after the second hash
- */
- propagateNewPopoverParameter: function(handlerName, value)
- {
- var hash = broadcast.getHashFromUrl(window.location.href);
- var hashParts = hash.split('&popover=');
-
- var newHash = hashParts[0];
- if (handlerName) {
- var popover = handlerName + ':' + value;
-
- // between jquery.history and different browser bugs, it's impossible to ensure
- // that the parameter is en- and decoded the same number of times. in order to
- // make sure it doesn't change, we have to manipulate the url encoding a bit.
- popover = encodeURIComponent(popover);
- popover = popover.replace(/%/g, '\$');
- newHash = hashParts[0] + '&popover=' + popover;
- }
-
- window.location.href = 'index.php' + window.location.search + newHash;
- },
-
- /**
- * Add a handler for the popover parameter
- */
- addPopoverHandler: function(handlerName, callback) {
- this.popoverHandlers[handlerName] = callback;
- },
+ /**
+ * Update the part after the second hash
+ */
+ propagateNewPopoverParameter: function (handlerName, value) {
+ var hash = broadcast.getHashFromUrl(window.location.href);
+ var hashParts = hash.split('&popover=');
+
+ var newHash = hashParts[0];
+ if (handlerName) {
+ var popover = handlerName + ':' + value;
+
+ // between jquery.history and different browser bugs, it's impossible to ensure
+ // that the parameter is en- and decoded the same number of times. in order to
+ // make sure it doesn't change, we have to manipulate the url encoding a bit.
+ popover = encodeURIComponent(popover);
+ popover = popover.replace(/%/g, '\$');
+ newHash = hashParts[0] + '&popover=' + popover;
+ }
+
+ window.location.href = 'index.php' + window.location.search + newHash;
+ },
+
+ /**
+ * Add a handler for the popover parameter
+ */
+ addPopoverHandler: function (handlerName, callback) {
+ this.popoverHandlers[handlerName] = callback;
+ },
/**
* Loads the given url with ajax and replaces the content
- *
- * Note: the method is replaced in Overlay/templates/index.js - keep this in mind when making changes.
+ *
+ * Note: the method is replaced in Overlay/templates/index.js - keep this in mind when making changes.
*
* @param {string} urlAjax url to load
* @return {Boolean}
*/
- loadAjaxContent: function(urlAjax)
- {
+ loadAjaxContent: function (urlAjax) {
piwikMenu.activateMenu(
- broadcast.getParamValue('module', urlAjax),
- broadcast.getParamValue('action', urlAjax),
- broadcast.getParamValue('idGoal', urlAjax) || broadcast.getParamValue('idDashboard', urlAjax)
+ broadcast.getParamValue('module', urlAjax),
+ broadcast.getParamValue('action', urlAjax),
+ broadcast.getParamValue('idGoal', urlAjax) || broadcast.getParamValue('idDashboard', urlAjax)
);
piwikHelper.hideAjaxError('loadingError');
@@ -378,21 +365,20 @@ var broadcast = {
urlAjax = urlAjax.match(/^\?/) ? urlAjax : "?" + urlAjax;
broadcast.lastUrlRequested = urlAjax;
- function sectionLoaded(content)
- {
- // if content is whole HTML document, do not show it, otherwise recursive page load could occur
- var htmlDocType = '<!DOCTYPE';
- if (content.substring(0, htmlDocType.length) == htmlDocType)
- {
- return;
- }
-
- if(urlAjax == broadcast.lastUrlRequested) {
- $('#content').html( content ).show();
+ function sectionLoaded(content) {
+ // if content is whole HTML document, do not show it, otherwise recursive page load could occur
+ var htmlDocType = '<!DOCTYPE';
+ if (content.substring(0, htmlDocType.length) == htmlDocType) {
+ return;
+ }
+
+ if (urlAjax == broadcast.lastUrlRequested) {
+ $('#content').html(content).show();
piwikHelper.hideAjaxLoading();
broadcast.lastUrlRequested = null;
}
}
+
var ajaxRequest = {
type: 'POST',
url: urlAjax,
@@ -402,7 +388,7 @@ var broadcast = {
success: sectionLoaded, // Callback when the request succeeds
data: new Object
};
- globalAjaxQueue.push( $.ajax(ajaxRequest) );
+ globalAjaxQueue.push($.ajax(ajaxRequest));
return false;
},
@@ -412,8 +398,7 @@ var broadcast = {
* @param {string} status
* @return {void}
*/
- customAjaxHandleError: function (deferred, status)
- {
+ customAjaxHandleError: function (deferred, status) {
broadcast.lastUrlRequested = null;
piwikHelper.ajaxHandleError(deferred, status);
},
@@ -424,11 +409,10 @@ var broadcast = {
*
* @return {string|false}
*/
- isHashExists: function()
- {
+ isHashExists: function () {
var hashStr = broadcast.getHashFromUrl();
- if ( hashStr != "" ) {
+ if (hashStr != "") {
return hashStr;
} else {
return false;
@@ -442,12 +426,11 @@ var broadcast = {
* @param {string} url
* @return {string} the hash part of the given url
*/
- getHashFromUrl: function(url)
- {
+ getHashFromUrl: function (url) {
var hashStr = "";
// If url provided, give back the hash from url, else get hash from current address.
- if( url && url.match('#') ) {
- hashStr = url.substring(url.indexOf("#"),url.length);
+ if (url && url.match('#')) {
+ hashStr = url.substring(url.indexOf("#"), url.length);
}
else {
hashStr = decodeURIComponent(location.hash);
@@ -463,37 +446,34 @@ var broadcast = {
* @param {string} url
* @return {string} the query part of the given url
*/
- getSearchFromUrl: function(url)
- {
+ getSearchFromUrl: function (url) {
var searchStr = "";
// If url provided, give back the query string from url, else get query string from current address.
- if( url && url.match(/\?/) ) {
- searchStr = url.substring(url.indexOf("?"),url.length);
+ if (url && url.match(/\?/)) {
+ searchStr = url.substring(url.indexOf("?"), url.length);
} else {
searchStr = location.search;
}
return searchStr;
},
-
+
/**
* Returns all key-value pairs in query string of url.
- *
+ *
* @param {string} url url to check. if undefined, null or empty, current url is used.
* @return {object} key value pair describing query string parameters
*/
- getValuesFromUrl: function(url)
- {
- var searchString = this._removeHashFromUrl(url).split('?')[1] || '',
- pairs = searchString.split('&');
-
- var result = {};
- for (var i = 0; i != pairs.length; ++i)
- {
- var pair = pairs[i].split(/=(.+)?/); // split only on first '='
- result[pair[0]] = pair[1];
- }
- return result;
+ getValuesFromUrl: function (url) {
+ var searchString = this._removeHashFromUrl(url).split('?')[1] || '',
+ pairs = searchString.split('&');
+
+ var result = {};
+ for (var i = 0; i != pairs.length; ++i) {
+ var pair = pairs[i].split(/=(.+)?/); // split only on first '='
+ result[pair[0]] = pair[1];
+ }
+ return result;
},
/**
@@ -506,10 +486,9 @@ var broadcast = {
* @param {string} url url to check
* @return {string} value of the given param within the given url
*/
- getValueFromUrl: function (param, url)
- {
+ getValueFromUrl: function (param, url) {
var searchString = this._removeHashFromUrl(url);
- return broadcast.getParamValue(param,searchString);
+ return broadcast.getParamValue(param, searchString);
},
/**
@@ -519,15 +498,14 @@ var broadcast = {
* @param {string} url url to check
* @return {string} value of the given param within the hash part of the given url
*/
- getValueFromHash: function(param, url)
- {
+ getValueFromHash: function (param, url) {
var hashStr = broadcast.getHashFromUrl(url);
- if (hashStr.substr(0, 1) == '#') {
- hashStr = hashStr.substr(1);
- }
- hashStr = hashStr.split('#')[0];
+ if (hashStr.substr(0, 1) == '#') {
+ hashStr = hashStr.substr(1);
+ }
+ hashStr = hashStr.split('#')[0];
- return broadcast.getParamValue(param,hashStr);
+ return broadcast.getParamValue(param, hashStr);
},
@@ -541,17 +519,16 @@ var broadcast = {
* @param {string} url url to check
* @return {string} value of the given param within the given url
*/
- getParamValue: function (param, url)
- {
+ getParamValue: function (param, url) {
var lookFor = param + '=';
var startStr = url.indexOf(lookFor);
- if( startStr >= 0 ) {
+ if (startStr >= 0) {
var endStr = url.indexOf("&", startStr);
- if( endStr == -1 ) {
+ if (endStr == -1) {
endStr = url.length;
}
- var value = url.substring(startStr + param.length +1,endStr);
+ var value = url.substring(startStr + param.length + 1, endStr);
// sanitize values
value = value.replace(/[^_%\+\-\<\>!@\$\.=,;0-9a-zA-Z]/gi, '');
@@ -565,26 +542,24 @@ var broadcast = {
* Returns the hash without the starting #
* @return {string} hash part of the current url
*/
- getHash: function ()
- {
+ getHash: function () {
return broadcast.getHashFromUrl().replace(/^#/, '').split('#')[0];
},
-
- /**
- * Removes the hash portion of a URL and returns the rest.
- *
- * @param {string} url
- * @return {string} url w/o hash
- */
- _removeHashFromUrl: function(url)
- {
+
+ /**
+ * Removes the hash portion of a URL and returns the rest.
+ *
+ * @param {string} url
+ * @return {string} url w/o hash
+ */
+ _removeHashFromUrl: function (url) {
var searchString = '';
- if( url ) {
+ if (url) {
var urlParts = url.split('#');
searchString = urlParts[0];
} else {
searchString = location.search;
}
return searchString;
- }
+ }
};
diff --git a/plugins/CoreHome/templates/calendar.js b/plugins/CoreHome/templates/calendar.js
index f1805ea4be..b6e7060bdc 100644
--- a/plugins/CoreHome/templates/calendar.js
+++ b/plugins/CoreHome/templates/calendar.js
@@ -4,575 +4,537 @@
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function($) {
-
-Date.prototype.getWeek = function() {
- var onejan = new Date(this.getFullYear(),0,1), // needed for getDay()
-
- // use UTC times since getTime() can differ based on user's timezone
- onejan_utc = Date.UTC(this.getFullYear(),0,1),
- this_utc = Date.UTC(this.getFullYear(),this.getMonth(),this.getDate()),
-
- daysSinceYearStart = (this_utc - onejan_utc) / 86400000; // constant is millisecs in one day
-
- return Math.ceil((daysSinceYearStart + onejan.getDay()) / 7);
-}
-
-var currentYear, currentMonth, currentDay, currentDate, currentWeek;
-function setCurrentDate( dateStr )
-{
- var splitDate = dateStr.split("-");
- currentYear = splitDate[0];
- currentMonth = splitDate[1] - 1;
- currentDay = splitDate[2];
- currentDate = new Date(currentYear, currentMonth, currentDay);
- currentWeek = currentDate.getWeek();
-}
-
-setCurrentDate(piwik.currentDateString);
-
-var todayDate = new Date;
-var todayMonth = todayDate.getMonth();
-var todayYear = todayDate.getFullYear();
-var todayDay = todayDate.getDate();
+(function ($) {
+
+ Date.prototype.getWeek = function () {
+ var onejan = new Date(this.getFullYear(), 0, 1), // needed for getDay()
+
+ // use UTC times since getTime() can differ based on user's timezone
+ onejan_utc = Date.UTC(this.getFullYear(), 0, 1),
+ this_utc = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate()),
+
+ daysSinceYearStart = (this_utc - onejan_utc) / 86400000; // constant is millisecs in one day
+
+ return Math.ceil((daysSinceYearStart + onejan.getDay()) / 7);
+ }
+
+ var currentYear, currentMonth, currentDay, currentDate, currentWeek;
+
+ function setCurrentDate(dateStr) {
+ var splitDate = dateStr.split("-");
+ currentYear = splitDate[0];
+ currentMonth = splitDate[1] - 1;
+ currentDay = splitDate[2];
+ currentDate = new Date(currentYear, currentMonth, currentDay);
+ currentWeek = currentDate.getWeek();
+ }
+
+ setCurrentDate(piwik.currentDateString);
+
+ var todayDate = new Date;
+ var todayMonth = todayDate.getMonth();
+ var todayYear = todayDate.getFullYear();
+ var todayDay = todayDate.getDate();
// min/max date for picker
-var piwikMinDate = new Date(piwik.minDateYear, piwik.minDateMonth - 1, piwik.minDateDay),
- piwikMaxDate = new Date(piwik.maxDateYear, piwik.maxDateMonth - 1, piwik.maxDateDay);
+ var piwikMinDate = new Date(piwik.minDateYear, piwik.minDateMonth - 1, piwik.minDateDay),
+ piwikMaxDate = new Date(piwik.maxDateYear, piwik.maxDateMonth - 1, piwik.maxDateDay);
// we start w/ the current period
-var selectedPeriod = piwik.period;
-
-function isDateInCurrentPeriod( date )
-{
- // if the selected period isn't the current period, don't highlight any dates
- if (selectedPeriod != piwik.period)
- {
- return [true, ''];
- }
-
- var valid = false;
-
- var dateMonth = date.getMonth();
- var dateYear = date.getFullYear();
- var dateDay = date.getDate();
- var style = '';
-
- // we don't color dates in the future
- if( dateMonth == todayMonth
- && dateYear == todayYear
- && dateDay > todayDay
- )
- {
- return [true, ''];
- }
-
- // we don't color dates before the minimum date
- if( dateYear < piwik.minDateYear
- || ( dateYear == piwik.minDateYear
- &&
- (
- (dateMonth == piwik.minDateMonth - 1
- && dateDay < piwik.minDateDay)
- || (dateMonth < piwik.minDateMonth - 1)
- )
- )
- )
- {
- return [true, ''];
- }
-
- // we color all day of the month for the same year for the month period
- if(piwik.period == "month"
- && dateMonth == currentMonth
- && dateYear == currentYear
- )
- {
- valid = true;
- }
- // we color all day of the year for the year period
- else if(piwik.period == "year"
- && dateYear == currentYear
- )
- {
- valid = true;
- }
- else if(piwik.period == "week"
- && date.getWeek() == currentWeek
- && dateYear == currentYear
- )
- {
- valid = true;
- }
- else if( piwik.period == "day"
- && dateDay == currentDay
- && dateMonth == currentMonth
- && dateYear == currentYear
- )
- {
- valid = true;
- }
-
- if(valid)
- {
- return [true, 'ui-datepicker-current-period'];
- }
-
- return [true, ''];
-}
-
-piwik.getBaseDatePickerOptions = function(defaultDate)
-{
- return {
- showOtherMonths: false,
- dateFormat: 'yy-mm-dd',
- firstDay: 1,
- minDate: piwikMinDate,
- maxDate: piwikMaxDate,
- prevText: "",
- nextText: "",
- currentText: "",
- defaultDate: defaultDate,
- changeMonth: true,
- changeYear: true,
- stepMonths: 1,
- // jquery-ui-i18n 1.7.2 lacks some translations, so we use our own
- dayNamesMin: [
- _pk_translate('General_DaySu_js'),
- _pk_translate('General_DayMo_js'),
- _pk_translate('General_DayTu_js'),
- _pk_translate('General_DayWe_js'),
- _pk_translate('General_DayTh_js'),
- _pk_translate('General_DayFr_js'),
- _pk_translate('General_DaySa_js')],
- dayNamesShort: [
- _pk_translate('General_ShortDay_1_js'),
- _pk_translate('General_ShortDay_2_js'),
- _pk_translate('General_ShortDay_3_js'),
- _pk_translate('General_ShortDay_4_js'),
- _pk_translate('General_ShortDay_5_js'),
- _pk_translate('General_ShortDay_6_js'),
- _pk_translate('General_ShortDay_7_js')],
- dayNames: [
- _pk_translate('General_LongDay_1_js'),
- _pk_translate('General_LongDay_2_js'),
- _pk_translate('General_LongDay_3_js'),
- _pk_translate('General_LongDay_4_js'),
- _pk_translate('General_LongDay_5_js'),
- _pk_translate('General_LongDay_6_js'),
- _pk_translate('General_LongDay_7_js')],
- monthNamesShort: [
- _pk_translate('General_ShortMonth_1_js'),
- _pk_translate('General_ShortMonth_2_js'),
- _pk_translate('General_ShortMonth_3_js'),
- _pk_translate('General_ShortMonth_4_js'),
- _pk_translate('General_ShortMonth_5_js'),
- _pk_translate('General_ShortMonth_6_js'),
- _pk_translate('General_ShortMonth_7_js'),
- _pk_translate('General_ShortMonth_8_js'),
- _pk_translate('General_ShortMonth_9_js'),
- _pk_translate('General_ShortMonth_10_js'),
- _pk_translate('General_ShortMonth_11_js'),
- _pk_translate('General_ShortMonth_12_js')],
- monthNames: [
- _pk_translate('General_MonthJanuary_js'),
- _pk_translate('General_MonthFebruary_js'),
- _pk_translate('General_MonthMarch_js'),
- _pk_translate('General_MonthApril_js'),
- _pk_translate('General_MonthMay_js'),
- _pk_translate('General_MonthJune_js'),
- _pk_translate('General_MonthJuly_js'),
- _pk_translate('General_MonthAugust_js'),
- _pk_translate('General_MonthSeptember_js'),
- _pk_translate('General_MonthOctober_js'),
- _pk_translate('General_MonthNovember_js'),
- _pk_translate('General_MonthDecember_js')]
- };
-};
-
-var updateDate;
-function getDatePickerOptions()
-{
- var result = piwik.getBaseDatePickerOptions(currentDate);
- result.beforeShowDay = isDateInCurrentPeriod;
- result.stepMonths = selectedPeriod == 'year' ? 12 : 1;
- result.onSelect = function () { updateDate.apply(this, arguments); };
- return result;
-};
-
-$(document).ready(function() {
-
- var datepickerElem = $('#datepicker').datepicker(getDatePickerOptions()),
- periodLabels = $('#periodString .period-type label'),
- periodTooltip = $('#periodString .period-click-tooltip').html();
-
- var toggleWhitespaceHighlighting = function (klass, toggleTop, toggleBottom)
- {
- var viewedYear = $('.ui-datepicker-year', datepickerElem).val(),
- viewedMonth = +$('.ui-datepicker-month', datepickerElem).val(), // convert to int w/ '+'
- firstOfViewedMonth = new Date(viewedYear, viewedMonth, 1),
- lastOfViewedMonth = new Date(viewedYear, viewedMonth + 1, 0);
-
- // only highlight dates between piwik.minDate... & piwik.maxDate...
- // we select the cells to highlight by checking whether the first & last of the
- // currently viewed month are within the min/max dates.
- if (firstOfViewedMonth >= piwikMinDate)
- {
- $('tbody>tr:first-child td.ui-datepicker-other-month', datepickerElem).toggleClass(klass, toggleTop);
- }
- if (lastOfViewedMonth < piwikMaxDate)
- {
- $('tbody>tr:last-child td.ui-datepicker-other-month', datepickerElem).toggleClass(klass, toggleBottom);
- }
- };
-
- // 'this' is the table cell
- var highlightCurrentPeriod = function ()
- {
- switch (selectedPeriod)
- {
- case 'day':
- // highlight this link
- $('a', $(this)).addClass('ui-state-hover');
- break;
- case 'week':
- var row = $(this).parent();
-
- // highlight parent row (the week)
- $('a', row).addClass('ui-state-hover');
-
- // toggle whitespace if week goes into previous or next month. we check if week is on
- // top or bottom row.
- var toggleTop = row.is(':first-child'),
- toggleBottom = row.is(':last-child');
- toggleWhitespaceHighlighting('ui-state-hover', toggleTop, toggleBottom);
- break;
- case 'month':
- // highlight all parent rows (the month)
- $('a', $(this).parent().parent()).addClass('ui-state-hover');
- break;
- case 'year':
- // highlight table (month + whitespace)
- $('a', $(this).parent().parent()).addClass('ui-state-hover');
- toggleWhitespaceHighlighting('ui-state-hover', true, true);
- break;
- }
- };
-
- var unhighlightAllDates = function ()
- {
- // make sure nothing is highlighted
- $('.ui-state-active,.ui-state-hover', datepickerElem).removeClass('ui-state-active ui-state-hover');
-
- // color whitespace
- if (piwik.period == 'year')
- {
- var viewedYear = $('.ui-datepicker-year', datepickerElem).val(),
- toggle = selectedPeriod == 'year' && currentYear == viewedYear;
- toggleWhitespaceHighlighting('ui-datepicker-current-period', toggle, toggle);
- }
- else if (piwik.period == 'week')
- {
- var toggleTop = $('tr:first-child a', datepickerElem).parent().hasClass('ui-datepicker-current-period'),
- toggleBottom = $('tr:last-child a', datepickerElem).parent().hasClass('ui-datepicker-current-period');
- toggleWhitespaceHighlighting('ui-datepicker-current-period', toggleTop, toggleBottom);
- }
- };
-
- updateDate = function (dateText)
- {
- piwikHelper.showAjaxLoading('ajaxLoadingCalendar');
- var date = dateText;
-
- // select new dates in calendar
- setCurrentDate(dateText);
- piwik.period = selectedPeriod;
-
- // make sure it's called after jquery-ui is done, otherwise everything we do will
- // be undone.
- setTimeout(unhighlightAllDates, 1);
-
- datepickerElem.datepicker('refresh');
-
- // Let broadcast do its job:
- // It will replace date value to both search query and hash and load the new page.
- broadcast.propagateNewPage('date=' + date + '&period=' + selectedPeriod);
- };
-
- var toggleMonthDropdown = function (disable)
- {
- if (typeof disable === 'undefined')
- {
- disable = selectedPeriod == 'year';
- }
-
- // enable/disable month dropdown based on period == year
- $('.ui-datepicker-month', datepickerElem).attr('disabled', disable);
- };
-
- var togglePeriodPickers = function (showSingle)
- {
- $('#periodString .period-date').toggle(showSingle);
- $('#periodString .period-range').toggle(!showSingle);
- $('#calendarRangeApply').toggle(!showSingle);
- };
-
- //
- // setup datepicker
- //
-
- unhighlightAllDates();
-
- //
- // hook up event slots
- //
-
- // highlight current period when mouse enters date
- datepickerElem.on('mouseenter', 'tbody td', function() {
- if ($(this).hasClass('ui-state-hover')) // if already highlighted, do nothing
- {
- return;
- }
-
- // unhighlight if cell is disabled/blank, unless the period is year
- if ($(this).hasClass('ui-state-disabled') && selectedPeriod != 'year')
- {
- unhighlightAllDates();
-
- // if period is week, then highlight the current week
- if (selectedPeriod == 'week')
- {
- highlightCurrentPeriod.call(this);
- }
- }
- else
- {
- highlightCurrentPeriod.call(this);
- }
- });
-
- // make sure cell stays highlighted when mouse leaves cell (overrides jquery-ui behavior)
- datepickerElem.on('mouseleave', 'tbody td', function () {
- $('a', this).addClass('ui-state-hover');
- });
-
- // unhighlight everything when mouse leaves table body (can't do event on tbody, for some reason
- // that fails, so we do two events, one on the table & one on thead)
- datepickerElem.on('mouseleave', 'table', unhighlightAllDates)
- .on('mouseenter', 'thead', unhighlightAllDates);
-
- // make sure whitespace is clickable when the period makes it appropriate
- datepickerElem.on('click', 'tbody td.ui-datepicker-other-month', function () {
- if ($(this).hasClass('ui-state-hover'))
- {
- var row = $(this).parent(), tbody = row.parent();
-
- if (row.is(':first-child'))
- {
- // click on first of the month
- $('a', tbody).first().click();
- }
- else
- {
- // click on last of month
- $('a', tbody).last().click();
- }
- }
- });
-
- // Hack to get around firefox bug. When double clicking a label in firefox, the 'click'
- // event of its associated input will not be fired twice. We want to change the period
- // if clicking the select period's label OR input, so we catch the click event on the
- // label & the input.
- var reloading = false;
- var changePeriodOnClick = function(periodInput)
- {
- if (reloading) // if a click event resulted in reloading, don't reload again
- {
- return;
- }
-
- var url = periodInput.val(),
- period = broadcast.getValueFromUrl('period', url);
-
- // if clicking on the selected period, change the period but not the date
- if (selectedPeriod == period && selectedPeriod != 'range')
- {
- // only reload if current period is different from selected
- if (piwik.period != selectedPeriod && !reloading)
- {
- reloading = true;
- selectedPeriod = period;
- updateDate(piwik.currentDateString);
- }
- return true;
- }
-
- return false;
- };
-
- $("#otherPeriods label").on('click', function(e) {
- var id = $(e.target).attr('for');
- changePeriodOnClick($('#' + id));
- });
-
- // when non-range period is clicked, change the period & refresh the date picker
- $("#otherPeriods input").on('click', function(e) {
- var request_URL = $(e.target).val(),
- period = broadcast.getValueFromUrl('period', request_URL),
- lastPeriod = selectedPeriod;
-
- if (changePeriodOnClick($(e.target)))
- {
- return true;
- }
-
- // switch the selected period
- selectedPeriod = period;
-
- // remove tooltips from the period inputs
- periodLabels.each(function() { $(this).attr('title', '').removeClass('selected-period-label'); });
-
- // range periods are handled in an event handler below
- if (period == 'range')
- {
- return true;
- }
-
- // set the tooltip of the current period
- if (period != piwik.period) // don't add tooltip for current period
- {
- $(this).parent().find('label[for=period_id_'+period+']')
- .attr('title', periodTooltip).addClass('selected-period-label');
- }
-
- // toggle the right selector controls (show period selector datepicker & hide 'apply range' button)
- togglePeriodPickers(true);
-
- // set months step to 12 for year period (or set back to 1 if leaving year period)
- if (selectedPeriod == 'year' || lastPeriod == 'year')
- {
- // setting stepMonths will change the month in view back to the selected date. to avoid
- // we set the selected date to the month in view.
- var currentMonth = $('.ui-datepicker-month', datepickerElem).val(),
- currentYear = $('.ui-datepicker-year', datepickerElem).val();
-
- datepickerElem
- .datepicker('option', 'stepMonths', selectedPeriod == 'year' ? 12 : 1)
- .datepicker('setDate', new Date(currentYear, currentMonth));
- }
-
- datepickerElem.datepicker('refresh'); // must be last datepicker call, otherwise cells get highlighted
-
- unhighlightAllDates();
- toggleMonthDropdown();
-
- return true;
- });
-
- // clicking left/right re-enables the month dropdown, so we disable it again
- $(datepickerElem).on('click', '.ui-datepicker-next,.ui-datepicker-prev', function() {
- unhighlightAllDates(); // make sure today's date isn't highlighted & toggle extra year highlighting
- toggleMonthDropdown(selectedPeriod == 'year');
- });
-
- // reset date/period when opening calendar
- var firstClick = true;
- $('#periodString #date').click(function() {
- if (!firstClick)
- {
- datepickerElem.datepicker('setDate', currentDate);
- $('#period_id_' + piwik.period).click();
- }
-
- firstClick = false;
- });
-
- function onDateRangeSelect(dateText, inst)
- {
- var toOrFrom = inst.id == 'calendarFrom' ? 'From' : 'To';
- $('#inputCalendar'+toOrFrom).val(dateText);
- }
-
- // this will trigger to change only the period value on search query and hash string.
- $("#period_id_range").on('click', function(e) {
- togglePeriodPickers(false);
-
- var options = getDatePickerOptions();
-
- // Custom Date range callback
- options.onSelect = onDateRangeSelect;
- // Do not highlight the period
- options.beforeShowDay = '';
- // Create both calendars
- options.defaultDate = piwik.startDateString;
- $('#calendarFrom').datepicker(options).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', piwik.startDateString));
-
- // Technically we should trigger the onSelect event on the calendar, but I couldn't find how to do that
- // So calling the onSelect bind function manually...
- //$('#calendarFrom').trigger('dateSelected'); // or onSelect
- onDateRangeSelect(piwik.startDateString, { "id": "calendarFrom" } );
-
- // Same code for the other calendar
- options.defaultDate = piwik.endDateString;
- $('#calendarTo').datepicker(options).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', piwik.endDateString));
- onDateRangeSelect(piwik.endDateString, { "id": "calendarTo" });
-
-
- // If not called, the first date appears light brown instead of dark brown
- $('.ui-state-hover').removeClass('ui-state-hover');
-
- // Apply date range button will reload the page with the selected range
- $('#calendarRangeApply')
- .on('click', function() {
- var request_URL = $(e.target).val();
- var dateFrom = $('#inputCalendarFrom').val(),
- dateTo = $('#inputCalendarTo').val(),
- oDateFrom = $.datepicker.parseDate('yy-mm-dd', dateFrom),
- oDateTo = $.datepicker.parseDate('yy-mm-dd', dateTo);
-
- if( !isValidDate(oDateFrom )
- || !isValidDate(oDateTo )
- || oDateFrom > oDateTo )
- {
- $('#alert h2').text(_pk_translate('General_InvalidDateRange_js'));
- piwikHelper.modalConfirm('#alert', {});
- return false;
- }
- piwikHelper.showAjaxLoading('ajaxLoadingCalendar');
- broadcast.propagateNewPage('period=range&date='+dateFrom+','+dateTo);
- })
- .show();
-
-
- // Bind the input fields to update the calendar's date when date is manually changed
- $('#inputCalendarFrom, #inputCalendarTo')
- .keyup( function (e) {
- var fromOrTo = this.id == 'inputCalendarFrom' ? 'From' : 'To';
- var dateInput = $(this).val();
- try {
- var newDate = $.datepicker.parseDate('yy-mm-dd', dateInput);
- } catch (e) {
- return;
- }
- $("#calendar"+fromOrTo).datepicker("setDate", newDate);
- if(e.keyCode == 13) {
- $('#calendarRangeApply').click();
- }
- });
- return true;
- });
- function isValidDate(d) {
- if ( Object.prototype.toString.call(d) !== "[object Date]" )
- return false;
- return !isNaN(d.getTime());
- }
-
- var period = broadcast.getValueFromUrl('period');
- if (period == 'range')
- {
- $("#period_id_range").click();
- }
-});
+ var selectedPeriod = piwik.period;
+
+ function isDateInCurrentPeriod(date) {
+ // if the selected period isn't the current period, don't highlight any dates
+ if (selectedPeriod != piwik.period) {
+ return [true, ''];
+ }
+
+ var valid = false;
+
+ var dateMonth = date.getMonth();
+ var dateYear = date.getFullYear();
+ var dateDay = date.getDate();
+ var style = '';
+
+ // we don't color dates in the future
+ if (dateMonth == todayMonth
+ && dateYear == todayYear
+ && dateDay > todayDay
+ ) {
+ return [true, ''];
+ }
+
+ // we don't color dates before the minimum date
+ if (dateYear < piwik.minDateYear
+ || ( dateYear == piwik.minDateYear
+ &&
+ (
+ (dateMonth == piwik.minDateMonth - 1
+ && dateDay < piwik.minDateDay)
+ || (dateMonth < piwik.minDateMonth - 1)
+ )
+ )
+ ) {
+ return [true, ''];
+ }
+
+ // we color all day of the month for the same year for the month period
+ if (piwik.period == "month"
+ && dateMonth == currentMonth
+ && dateYear == currentYear
+ ) {
+ valid = true;
+ }
+ // we color all day of the year for the year period
+ else if (piwik.period == "year"
+ && dateYear == currentYear
+ ) {
+ valid = true;
+ }
+ else if (piwik.period == "week"
+ && date.getWeek() == currentWeek
+ && dateYear == currentYear
+ ) {
+ valid = true;
+ }
+ else if (piwik.period == "day"
+ && dateDay == currentDay
+ && dateMonth == currentMonth
+ && dateYear == currentYear
+ ) {
+ valid = true;
+ }
+
+ if (valid) {
+ return [true, 'ui-datepicker-current-period'];
+ }
+
+ return [true, ''];
+ }
+
+ piwik.getBaseDatePickerOptions = function (defaultDate) {
+ return {
+ showOtherMonths: false,
+ dateFormat: 'yy-mm-dd',
+ firstDay: 1,
+ minDate: piwikMinDate,
+ maxDate: piwikMaxDate,
+ prevText: "",
+ nextText: "",
+ currentText: "",
+ defaultDate: defaultDate,
+ changeMonth: true,
+ changeYear: true,
+ stepMonths: 1,
+ // jquery-ui-i18n 1.7.2 lacks some translations, so we use our own
+ dayNamesMin: [
+ _pk_translate('General_DaySu_js'),
+ _pk_translate('General_DayMo_js'),
+ _pk_translate('General_DayTu_js'),
+ _pk_translate('General_DayWe_js'),
+ _pk_translate('General_DayTh_js'),
+ _pk_translate('General_DayFr_js'),
+ _pk_translate('General_DaySa_js')],
+ dayNamesShort: [
+ _pk_translate('General_ShortDay_1_js'),
+ _pk_translate('General_ShortDay_2_js'),
+ _pk_translate('General_ShortDay_3_js'),
+ _pk_translate('General_ShortDay_4_js'),
+ _pk_translate('General_ShortDay_5_js'),
+ _pk_translate('General_ShortDay_6_js'),
+ _pk_translate('General_ShortDay_7_js')],
+ dayNames: [
+ _pk_translate('General_LongDay_1_js'),
+ _pk_translate('General_LongDay_2_js'),
+ _pk_translate('General_LongDay_3_js'),
+ _pk_translate('General_LongDay_4_js'),
+ _pk_translate('General_LongDay_5_js'),
+ _pk_translate('General_LongDay_6_js'),
+ _pk_translate('General_LongDay_7_js')],
+ monthNamesShort: [
+ _pk_translate('General_ShortMonth_1_js'),
+ _pk_translate('General_ShortMonth_2_js'),
+ _pk_translate('General_ShortMonth_3_js'),
+ _pk_translate('General_ShortMonth_4_js'),
+ _pk_translate('General_ShortMonth_5_js'),
+ _pk_translate('General_ShortMonth_6_js'),
+ _pk_translate('General_ShortMonth_7_js'),
+ _pk_translate('General_ShortMonth_8_js'),
+ _pk_translate('General_ShortMonth_9_js'),
+ _pk_translate('General_ShortMonth_10_js'),
+ _pk_translate('General_ShortMonth_11_js'),
+ _pk_translate('General_ShortMonth_12_js')],
+ monthNames: [
+ _pk_translate('General_MonthJanuary_js'),
+ _pk_translate('General_MonthFebruary_js'),
+ _pk_translate('General_MonthMarch_js'),
+ _pk_translate('General_MonthApril_js'),
+ _pk_translate('General_MonthMay_js'),
+ _pk_translate('General_MonthJune_js'),
+ _pk_translate('General_MonthJuly_js'),
+ _pk_translate('General_MonthAugust_js'),
+ _pk_translate('General_MonthSeptember_js'),
+ _pk_translate('General_MonthOctober_js'),
+ _pk_translate('General_MonthNovember_js'),
+ _pk_translate('General_MonthDecember_js')]
+ };
+ };
+
+ var updateDate;
+
+ function getDatePickerOptions() {
+ var result = piwik.getBaseDatePickerOptions(currentDate);
+ result.beforeShowDay = isDateInCurrentPeriod;
+ result.stepMonths = selectedPeriod == 'year' ? 12 : 1;
+ result.onSelect = function () { updateDate.apply(this, arguments); };
+ return result;
+ };
+
+ $(document).ready(function () {
+
+ var datepickerElem = $('#datepicker').datepicker(getDatePickerOptions()),
+ periodLabels = $('#periodString .period-type label'),
+ periodTooltip = $('#periodString .period-click-tooltip').html();
+
+ var toggleWhitespaceHighlighting = function (klass, toggleTop, toggleBottom) {
+ var viewedYear = $('.ui-datepicker-year', datepickerElem).val(),
+ viewedMonth = +$('.ui-datepicker-month', datepickerElem).val(), // convert to int w/ '+'
+ firstOfViewedMonth = new Date(viewedYear, viewedMonth, 1),
+ lastOfViewedMonth = new Date(viewedYear, viewedMonth + 1, 0);
+
+ // only highlight dates between piwik.minDate... & piwik.maxDate...
+ // we select the cells to highlight by checking whether the first & last of the
+ // currently viewed month are within the min/max dates.
+ if (firstOfViewedMonth >= piwikMinDate) {
+ $('tbody>tr:first-child td.ui-datepicker-other-month', datepickerElem).toggleClass(klass, toggleTop);
+ }
+ if (lastOfViewedMonth < piwikMaxDate) {
+ $('tbody>tr:last-child td.ui-datepicker-other-month', datepickerElem).toggleClass(klass, toggleBottom);
+ }
+ };
+
+ // 'this' is the table cell
+ var highlightCurrentPeriod = function () {
+ switch (selectedPeriod) {
+ case 'day':
+ // highlight this link
+ $('a', $(this)).addClass('ui-state-hover');
+ break;
+ case 'week':
+ var row = $(this).parent();
+
+ // highlight parent row (the week)
+ $('a', row).addClass('ui-state-hover');
+
+ // toggle whitespace if week goes into previous or next month. we check if week is on
+ // top or bottom row.
+ var toggleTop = row.is(':first-child'),
+ toggleBottom = row.is(':last-child');
+ toggleWhitespaceHighlighting('ui-state-hover', toggleTop, toggleBottom);
+ break;
+ case 'month':
+ // highlight all parent rows (the month)
+ $('a', $(this).parent().parent()).addClass('ui-state-hover');
+ break;
+ case 'year':
+ // highlight table (month + whitespace)
+ $('a', $(this).parent().parent()).addClass('ui-state-hover');
+ toggleWhitespaceHighlighting('ui-state-hover', true, true);
+ break;
+ }
+ };
+
+ var unhighlightAllDates = function () {
+ // make sure nothing is highlighted
+ $('.ui-state-active,.ui-state-hover', datepickerElem).removeClass('ui-state-active ui-state-hover');
+
+ // color whitespace
+ if (piwik.period == 'year') {
+ var viewedYear = $('.ui-datepicker-year', datepickerElem).val(),
+ toggle = selectedPeriod == 'year' && currentYear == viewedYear;
+ toggleWhitespaceHighlighting('ui-datepicker-current-period', toggle, toggle);
+ }
+ else if (piwik.period == 'week') {
+ var toggleTop = $('tr:first-child a', datepickerElem).parent().hasClass('ui-datepicker-current-period'),
+ toggleBottom = $('tr:last-child a', datepickerElem).parent().hasClass('ui-datepicker-current-period');
+ toggleWhitespaceHighlighting('ui-datepicker-current-period', toggleTop, toggleBottom);
+ }
+ };
+
+ updateDate = function (dateText) {
+ piwikHelper.showAjaxLoading('ajaxLoadingCalendar');
+ var date = dateText;
+
+ // select new dates in calendar
+ setCurrentDate(dateText);
+ piwik.period = selectedPeriod;
+
+ // make sure it's called after jquery-ui is done, otherwise everything we do will
+ // be undone.
+ setTimeout(unhighlightAllDates, 1);
+
+ datepickerElem.datepicker('refresh');
+
+ // Let broadcast do its job:
+ // It will replace date value to both search query and hash and load the new page.
+ broadcast.propagateNewPage('date=' + date + '&period=' + selectedPeriod);
+ };
+
+ var toggleMonthDropdown = function (disable) {
+ if (typeof disable === 'undefined') {
+ disable = selectedPeriod == 'year';
+ }
+
+ // enable/disable month dropdown based on period == year
+ $('.ui-datepicker-month', datepickerElem).attr('disabled', disable);
+ };
+
+ var togglePeriodPickers = function (showSingle) {
+ $('#periodString .period-date').toggle(showSingle);
+ $('#periodString .period-range').toggle(!showSingle);
+ $('#calendarRangeApply').toggle(!showSingle);
+ };
+
+ //
+ // setup datepicker
+ //
+
+ unhighlightAllDates();
+
+ //
+ // hook up event slots
+ //
+
+ // highlight current period when mouse enters date
+ datepickerElem.on('mouseenter', 'tbody td', function () {
+ if ($(this).hasClass('ui-state-hover')) // if already highlighted, do nothing
+ {
+ return;
+ }
+
+ // unhighlight if cell is disabled/blank, unless the period is year
+ if ($(this).hasClass('ui-state-disabled') && selectedPeriod != 'year') {
+ unhighlightAllDates();
+
+ // if period is week, then highlight the current week
+ if (selectedPeriod == 'week') {
+ highlightCurrentPeriod.call(this);
+ }
+ }
+ else {
+ highlightCurrentPeriod.call(this);
+ }
+ });
+
+ // make sure cell stays highlighted when mouse leaves cell (overrides jquery-ui behavior)
+ datepickerElem.on('mouseleave', 'tbody td', function () {
+ $('a', this).addClass('ui-state-hover');
+ });
+
+ // unhighlight everything when mouse leaves table body (can't do event on tbody, for some reason
+ // that fails, so we do two events, one on the table & one on thead)
+ datepickerElem.on('mouseleave', 'table', unhighlightAllDates)
+ .on('mouseenter', 'thead', unhighlightAllDates);
+
+ // make sure whitespace is clickable when the period makes it appropriate
+ datepickerElem.on('click', 'tbody td.ui-datepicker-other-month', function () {
+ if ($(this).hasClass('ui-state-hover')) {
+ var row = $(this).parent(), tbody = row.parent();
+
+ if (row.is(':first-child')) {
+ // click on first of the month
+ $('a', tbody).first().click();
+ }
+ else {
+ // click on last of month
+ $('a', tbody).last().click();
+ }
+ }
+ });
+
+ // Hack to get around firefox bug. When double clicking a label in firefox, the 'click'
+ // event of its associated input will not be fired twice. We want to change the period
+ // if clicking the select period's label OR input, so we catch the click event on the
+ // label & the input.
+ var reloading = false;
+ var changePeriodOnClick = function (periodInput) {
+ if (reloading) // if a click event resulted in reloading, don't reload again
+ {
+ return;
+ }
+
+ var url = periodInput.val(),
+ period = broadcast.getValueFromUrl('period', url);
+
+ // if clicking on the selected period, change the period but not the date
+ if (selectedPeriod == period && selectedPeriod != 'range') {
+ // only reload if current period is different from selected
+ if (piwik.period != selectedPeriod && !reloading) {
+ reloading = true;
+ selectedPeriod = period;
+ updateDate(piwik.currentDateString);
+ }
+ return true;
+ }
+
+ return false;
+ };
+
+ $("#otherPeriods label").on('click', function (e) {
+ var id = $(e.target).attr('for');
+ changePeriodOnClick($('#' + id));
+ });
+
+ // when non-range period is clicked, change the period & refresh the date picker
+ $("#otherPeriods input").on('click', function (e) {
+ var request_URL = $(e.target).val(),
+ period = broadcast.getValueFromUrl('period', request_URL),
+ lastPeriod = selectedPeriod;
+
+ if (changePeriodOnClick($(e.target))) {
+ return true;
+ }
+
+ // switch the selected period
+ selectedPeriod = period;
+
+ // remove tooltips from the period inputs
+ periodLabels.each(function () { $(this).attr('title', '').removeClass('selected-period-label'); });
+
+ // range periods are handled in an event handler below
+ if (period == 'range') {
+ return true;
+ }
+
+ // set the tooltip of the current period
+ if (period != piwik.period) // don't add tooltip for current period
+ {
+ $(this).parent().find('label[for=period_id_' + period + ']')
+ .attr('title', periodTooltip).addClass('selected-period-label');
+ }
+
+ // toggle the right selector controls (show period selector datepicker & hide 'apply range' button)
+ togglePeriodPickers(true);
+
+ // set months step to 12 for year period (or set back to 1 if leaving year period)
+ if (selectedPeriod == 'year' || lastPeriod == 'year') {
+ // setting stepMonths will change the month in view back to the selected date. to avoid
+ // we set the selected date to the month in view.
+ var currentMonth = $('.ui-datepicker-month', datepickerElem).val(),
+ currentYear = $('.ui-datepicker-year', datepickerElem).val();
+
+ datepickerElem
+ .datepicker('option', 'stepMonths', selectedPeriod == 'year' ? 12 : 1)
+ .datepicker('setDate', new Date(currentYear, currentMonth));
+ }
+
+ datepickerElem.datepicker('refresh'); // must be last datepicker call, otherwise cells get highlighted
+
+ unhighlightAllDates();
+ toggleMonthDropdown();
+
+ return true;
+ });
+
+ // clicking left/right re-enables the month dropdown, so we disable it again
+ $(datepickerElem).on('click', '.ui-datepicker-next,.ui-datepicker-prev', function () {
+ unhighlightAllDates(); // make sure today's date isn't highlighted & toggle extra year highlighting
+ toggleMonthDropdown(selectedPeriod == 'year');
+ });
+
+ // reset date/period when opening calendar
+ var firstClick = true;
+ $('#periodString #date').click(function () {
+ if (!firstClick) {
+ datepickerElem.datepicker('setDate', currentDate);
+ $('#period_id_' + piwik.period).click();
+ }
+
+ firstClick = false;
+ });
+
+ function onDateRangeSelect(dateText, inst) {
+ var toOrFrom = inst.id == 'calendarFrom' ? 'From' : 'To';
+ $('#inputCalendar' + toOrFrom).val(dateText);
+ }
+
+ // this will trigger to change only the period value on search query and hash string.
+ $("#period_id_range").on('click', function (e) {
+ togglePeriodPickers(false);
+
+ var options = getDatePickerOptions();
+
+ // Custom Date range callback
+ options.onSelect = onDateRangeSelect;
+ // Do not highlight the period
+ options.beforeShowDay = '';
+ // Create both calendars
+ options.defaultDate = piwik.startDateString;
+ $('#calendarFrom').datepicker(options).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', piwik.startDateString));
+
+ // Technically we should trigger the onSelect event on the calendar, but I couldn't find how to do that
+ // So calling the onSelect bind function manually...
+ //$('#calendarFrom').trigger('dateSelected'); // or onSelect
+ onDateRangeSelect(piwik.startDateString, { "id": "calendarFrom" });
+
+ // Same code for the other calendar
+ options.defaultDate = piwik.endDateString;
+ $('#calendarTo').datepicker(options).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', piwik.endDateString));
+ onDateRangeSelect(piwik.endDateString, { "id": "calendarTo" });
+
+
+ // If not called, the first date appears light brown instead of dark brown
+ $('.ui-state-hover').removeClass('ui-state-hover');
+
+ // Apply date range button will reload the page with the selected range
+ $('#calendarRangeApply')
+ .on('click', function () {
+ var request_URL = $(e.target).val();
+ var dateFrom = $('#inputCalendarFrom').val(),
+ dateTo = $('#inputCalendarTo').val(),
+ oDateFrom = $.datepicker.parseDate('yy-mm-dd', dateFrom),
+ oDateTo = $.datepicker.parseDate('yy-mm-dd', dateTo);
+
+ if (!isValidDate(oDateFrom)
+ || !isValidDate(oDateTo)
+ || oDateFrom > oDateTo) {
+ $('#alert h2').text(_pk_translate('General_InvalidDateRange_js'));
+ piwikHelper.modalConfirm('#alert', {});
+ return false;
+ }
+ piwikHelper.showAjaxLoading('ajaxLoadingCalendar');
+ broadcast.propagateNewPage('period=range&date=' + dateFrom + ',' + dateTo);
+ })
+ .show();
+
+
+ // Bind the input fields to update the calendar's date when date is manually changed
+ $('#inputCalendarFrom, #inputCalendarTo')
+ .keyup(function (e) {
+ var fromOrTo = this.id == 'inputCalendarFrom' ? 'From' : 'To';
+ var dateInput = $(this).val();
+ try {
+ var newDate = $.datepicker.parseDate('yy-mm-dd', dateInput);
+ } catch (e) {
+ return;
+ }
+ $("#calendar" + fromOrTo).datepicker("setDate", newDate);
+ if (e.keyCode == 13) {
+ $('#calendarRangeApply').click();
+ }
+ });
+ return true;
+ });
+ function isValidDate(d) {
+ if (Object.prototype.toString.call(d) !== "[object Date]")
+ return false;
+ return !isNaN(d.getTime());
+ }
+
+ var period = broadcast.getValueFromUrl('period');
+ if (period == 'range') {
+ $("#period_id_range").click();
+ }
+ });
}(jQuery));
diff --git a/plugins/CoreHome/templates/cloud.css b/plugins/CoreHome/templates/cloud.css
index a1dba84562..b17741e1cf 100644
--- a/plugins/CoreHome/templates/cloud.css
+++ b/plugins/CoreHome/templates/cloud.css
@@ -1,45 +1,56 @@
.tagCloud {
- padding:10px;
+ padding: 10px;
}
+
.tagCloud img {
- border:0;
+ border: 0;
}
+
.tagCloud .word a {
- text-decoration:none;
+ text-decoration: none;
}
+
.tagCloud .word {
- padding: 4px 8px 4px 0;
- white-space: nowrap;
+ padding: 4px 8px 4px 0;
+ white-space: nowrap;
}
+
.tagCloud .valueIsZero {
- text-decoration: line-through;
+ text-decoration: line-through;
}
+
.tagCloud span.size0, .tagCloud span.size0 a {
- color: #255792;
- font-size: 28px;
+ color: #255792;
+ font-size: 28px;
}
+
.tagCloud span.size1, .tagCloud span.size1 a {
- color: #255792;
- font-size: 24px;
+ color: #255792;
+ font-size: 24px;
}
+
.tagCloud span.size2, .tagCloud span.size2 a {
- color: #255792;
- font-size:20px;
+ color: #255792;
+ font-size: 20px;
}
+
.tagCloud span.size3, .tagCloud span.size3 a {
- color: #255792;
- font-size: 16px;
+ color: #255792;
+ font-size: 16px;
}
+
.tagCloud span.size4, .tagCloud span.size4 a {
- color: #255792;
- font-size: 15px;
+ color: #255792;
+ font-size: 15px;
}
+
.tagCloud span.size5, .tagCloud span.size5 a {
- color: #255792;
- font-size: 14px;
+ color: #255792;
+ font-size: 14px;
}
+
.tagCloud span.size6, .tagCloud span.size6 a {
- color: #255792;
- font-size: 11px;
+ color: #255792;
+ font-size: 11px;
}
diff --git a/plugins/CoreHome/templates/cloud.tpl b/plugins/CoreHome/templates/cloud.tpl
index efcfbeac5f..9aabeedcc2 100644
--- a/plugins/CoreHome/templates/cloud.tpl
+++ b/plugins/CoreHome/templates/cloud.tpl
@@ -1,25 +1,26 @@
<div class="dataTable" data-report="{$properties.uniqueId}" data-params="{$javascriptVariablesToSet|@json_encode|escape:'html'}">
- {if !empty($reportDocumentation) && $javascriptVariablesToSet.viewDataTable != 'tableGoals'}
- <div class="reportDocumentation"><p>{$reportDocumentation}</p></div>
- {/if}
- <div class="tagCloud">
- {if count($cloudValues) == 0}
- {if $showReportDataWasPurgedMessage}
- <div class="pk-emptyDataTable">{'General_DataForThisTagCloudHasBeenPurged'|translate:$deleteReportsOlderThan}</div>
- {else}
- <div class="pk-emptyDataTable">{'General_NoDataForTagCloud'|translate}</div>
- {/if}
- {else}
- {foreach from=$cloudValues key=word item=value}
- <span title="{$value.word} ({$value.value} {$columnTranslation})" class="word size{$value.size} {* we strike tags with 0 hits *} {if $value.value == 0}valueIsZero{/if}">
+ {if !empty($reportDocumentation) && $javascriptVariablesToSet.viewDataTable != 'tableGoals'}
+ <div class="reportDocumentation"><p>{$reportDocumentation}</p></div>
+ {/if}
+ <div class="tagCloud">
+ {if count($cloudValues) == 0}
+ {if $showReportDataWasPurgedMessage}
+ <div class="pk-emptyDataTable">{'General_DataForThisTagCloudHasBeenPurged'|translate:$deleteReportsOlderThan}</div>
+ {else}
+ <div class="pk-emptyDataTable">{'General_NoDataForTagCloud'|translate}</div>
+ {/if}
+ {else}
+ {foreach from=$cloudValues key=word item=value}
+ <span title="{$value.word} ({$value.value} {$columnTranslation})"
+ class="word size{$value.size} {* we strike tags with 0 hits *} {if $value.value == 0}valueIsZero{/if}">
{if false !== $labelMetadata[$value.word].url}<a href="{$labelMetadata[$value.word].url}" target="_blank">{/if}
- {if false !== $labelMetadata[$value.word].logo}<img src="{$labelMetadata[$value.word].logo}" width="{$value.logoWidth}" />{else}
- {$value.wordTruncated}{/if}{if false !== $labelMetadata[$value.word].url}</a>{/if}</span>
- {/foreach}
- {/if}
- </div>
- {if $properties.show_footer}
- {include file="CoreHome/templates/datatable_footer.tpl"}
- {/if}
- {include file="CoreHome/templates/datatable_js.tpl"}
+ {if false !== $labelMetadata[$value.word].logo}<img src="{$labelMetadata[$value.word].logo}" width="{$value.logoWidth}" />{else}
+ {$value.wordTruncated}{/if}{if false !== $labelMetadata[$value.word].url}</a>{/if}</span>
+ {/foreach}
+ {/if}
+ </div>
+ {if $properties.show_footer}
+ {include file="CoreHome/templates/datatable_footer.tpl"}
+ {/if}
+ {include file="CoreHome/templates/datatable_js.tpl"}
</div>
diff --git a/plugins/CoreHome/templates/datatable.css b/plugins/CoreHome/templates/datatable.css
index 7975c819f4..c71c2f6a31 100644
--- a/plugins/CoreHome/templates/datatable.css
+++ b/plugins/CoreHome/templates/datatable.css
@@ -1,99 +1,102 @@
-
/*Overriding some dataTable css for better dashboard display*/
-.widget .dataTableWrapper,
+.widget .dataTableWrapper,
.widget .dataTableAllColumnsWrapper,
.widget .dataTableGraphWrapper,
.widget .dataTableActionsWrapper,
.widget .dataTableGraphEvolutionWrapper {
- width: 100%;
+ width: 100%;
}
.widget {
- z-index:1;
+ z-index: 1;
}
.widget p {
- margin-left:10px;
+ margin-left: 10px;
}
+
/* container of each table */
.dataTableWrapper {
- width: 450px;
- /* not more than 450px to make sure 2 tables can fit horizontally on a 1024 screen */
+ width: 450px;
+ /* not more than 450px to make sure 2 tables can fit horizontally on a 1024 screen */
}
+
.dataTableAllColumnsWrapper {
- width: 535px;
+ width: 535px;
}
-.subdataTableWrapper{
- width: 95%;
+.subdataTableWrapper {
+ width: 95%;
}
+
.subdataTableAllColumnsWrapper {
- width: 95%;
+ width: 95%;
}
.dataTableActionsWrapper {
- width: 500px;
- min-height:1px;
+ width: 500px;
+ min-height: 1px;
}
.dataTableGraphWrapper {
- width: 500px;
- min-height:1px;
+ width: 500px;
+ min-height: 1px;
}
.dataTableGraphEvolutionWrapper {
- width: 100%;
+ width: 100%;
}
/* main data table */
table.dataTable {
- border:0;
- width: 100%;
- padding: 0;
- border-spacing: 0;
- margin: 0;
+ border: 0;
+ width: 100%;
+ padding: 0;
+ border-spacing: 0;
+ margin: 0;
}
table.dataTable td.label,
table.subDataTable td.label,
table.dataTableActions td.label {
- width: 100%;
- white-space:nowrap;
+ width: 100%;
+ white-space: nowrap;
}
table.dataTable img,
table.subDataTable img,
table.dataTableActions img {
- vertical-align: middle;
+ vertical-align: middle;
}
+
#UserCountrygetCountry table.dataTable img {
- vertical-align: baseline;
+ vertical-align: baseline;
}
+
table.dataTable img {
- border: 0;
- margin-right: 1em;
- margin-left: 0.5em;
+ border: 0;
+ margin-right: 1em;
+ margin-left: 0.5em;
}
table.dataTable tr.subDataTable {
- cursor: pointer;
+ cursor: pointer;
}
-
-
table.dataTable th {
- margin: 0;
- color: #255792;
- text-align: left;
- padding: 6px 6px 6px 12px;
- background: #e4e2d7;
- font-size:12px;
- font-weight: normal;
- border-left: 1px solid #d4d0c4;
- vertical-align: top;
-}
-table.dataTable th.sortable{
- cursor:pointer;
+ margin: 0;
+ color: #255792;
+ text-align: left;
+ padding: 6px 6px 6px 12px;
+ background: #e4e2d7;
+ font-size: 12px;
+ font-weight: normal;
+ border-left: 1px solid #d4d0c4;
+ vertical-align: top;
+}
+
+table.dataTable th.sortable {
+ cursor: pointer;
}
table.dataTable th.first {
@@ -101,72 +104,73 @@ table.dataTable th.first {
table.dataTable th.last {
}
+
table.dataTable th.columnSorted {
- font-weight: bold;
- padding-right: 20px;
- background: #d5d3c8;
+ font-weight: bold;
+ padding-right: 20px;
+ background: #d5d3c8;
}
table.dataTable td {
- padding: 5px 5px 5px 12px;
- background: #fff;
- border-left:1px solid #e7e7e7;
- border-bottom:1px solid #e7e7e7;
+ padding: 5px 5px 5px 12px;
+ background: #fff;
+ border-left: 1px solid #e7e7e7;
+ border-bottom: 1px solid #e7e7e7;
}
-table.dataTable td,table.dataTable td a {
- margin: 0;
- text-decoration: none;
- color: #444;
+table.dataTable td, table.dataTable td a {
+ margin: 0;
+ text-decoration: none;
+ color: #444;
}
-
-
table.dataTable tr:hover>td,
-table.dataTable tr:hover>td .dataTableRowActions{
- background-color:#FFFFF7;
+table.dataTable tr:hover>td .dataTableRowActions {
+ background-color: #FFFFF7;
}
+
table.dataTable tr.subDataTable:hover>td,
-table.dataTable tr.subDataTable:hover>td .dataTableRowActions{
- background-color:#ffffcb;
+table.dataTable tr.subDataTable:hover>td .dataTableRowActions {
+ background-color: #ffffcb;
}
+
table.dataTable tr:hover>td.cellSubDataTable
-table.dataTable tr:hover>td.cellSubDataTable .dataTableRowActions{
- background-color:#fff;
+table.dataTable tr:hover>td.cellSubDataTable .dataTableRowActions {
+ background-color: #fff;
}
-td.clean{
- background-color:#fff!important;
+td.clean {
+ background-color: #fff !important;
}
-
table.dataTable td.columneven {
- background: #efeeec;
+ background: #efeeec;
}
+
table.dataTable td.columnodd {
- background: #f6f5f3;
+ background: #f6f5f3;
}
table.dataTable td.labeleven {
- background: #F9FAFA url(images/bullet2.gif) no-repeat;
+ background: #F9FAFA url(images/bullet2.gif) no-repeat;
}
table.dataTable td.labelodd {
- background: #fff url(images/bullet1.gif) no-repeat;
+ background: #fff url(images/bullet1.gif) no-repeat;
}
.dataTable tr.highlight td {
- background-color: #ECF9DD;
- font-weight: bold;
+ background-color: #ECF9DD;
+ font-weight: bold;
}
-table.dataTable td.label,table.subActionsDataTable td.label,table.actionsDataTable td.label {
- border-top: 0;
- border-left: 0;
+table.dataTable td.label, table.subActionsDataTable td.label, table.actionsDataTable td.label {
+ border-top: 0;
+ border-left: 0;
}
table.dataTable th.label {
- border-left: 0;
+ border-left: 0;
}
table.dataTableActions th.label {
@@ -175,465 +179,474 @@ table.dataTableActions th.label {
}
table.dataTable span.label.highlighted {
- font-style: italic;
+ font-style: italic;
}
/* the cell containing the subdatatable */
table.dataTable .cellSubDataTable {
- margin: 0;
- border-left:0;
+ margin: 0;
+ border-left: 0;
}
/* A link in a column in the DataTable */
table.dataTable td #urlLink {
- display: none;
+ display: none;
}
-/* SUBDATATABLE */ /* a datatable inside another datatable */
+/* SUBDATATABLE */
+/* a datatable inside another datatable */
table.subDataTable {
- background: #FFFFFF;
- margin: 5px 10px;
+ background: #FFFFFF;
+ margin: 5px 10px;
}
table.subDataTable td {
- border: 0;
+ border: 0;
}
table.subDataTable thead th {
- font-weight: normal;
- font-size: 12px;
- text-align: left;
- padding: .3em 1em;
- background: #f1f0eb;
- border: 0;
- border-top: 1px solid #e7e7e7;
- border-bottom: 1px solid #e7e7e7;
+ font-weight: normal;
+ font-size: 12px;
+ text-align: left;
+ padding: .3em 1em;
+ background: #f1f0eb;
+ border: 0;
+ border-top: 1px solid #e7e7e7;
+ border-bottom: 1px solid #e7e7e7;
}
-
table.subDataTable th.columnSorted {
- background: #e3e1d9;
+ background: #e3e1d9;
}
-table.subDataTable td.labeleven,table.subDataTable td.labelodd {
- background-image: none;
+table.subDataTable td.labeleven, table.subDataTable td.labelodd {
+ background-image: none;
}
table.subDataTable td {
- border-bottom: 1px solid #e7e7e7;
- border-left: 0;
+ border-bottom: 1px solid #e7e7e7;
+ border-left: 0;
}
-table.subDataTable td,table.subDataTable td a {
- color: #615B53;
+table.subDataTable td, table.subDataTable td a {
+ color: #615B53;
}
-table.subDataTable td.labeleven,table.subDataTable td.columneven {
- color: #2D2A27;
+table.subDataTable td.labeleven, table.subDataTable td.columneven {
+ color: #2D2A27;
}
table.subDataTable td.label {
- width: 80%;
+ width: 80%;
}
-table.subDataTable td.labelodd,table.subDataTable td.labelodd a {
- background: #fff;
+table.subDataTable td.labelodd, table.subDataTable td.labelodd a {
+ background: #fff;
}
+
table.subDataTable td.label {
- padding: 5px;
+ padding: 5px;
}
+
table.dataTable img {
- margin-left:0;
+ margin-left: 0;
}
+
/* misc SPAN and DIV */
table thead div {
-
+
}
#sortIconContainer {
- float: right;
- position: relative;
+ float: right;
+ position: relative;
}
#sortIcon {
- margin: 0;
- position: absolute;
+ margin: 0;
+ position: absolute;
}
.datatableFooterMessage {
- color: #888;
- text-align:left;
- margin: 10px;
+ color: #888;
+ text-align: left;
+ margin: 10px;
}
.dataTablePages {
- color: #BFBFBF;
- font-weight: bold;
- margin: 10px;
- font-size: 12px;
+ color: #BFBFBF;
+ font-weight: bold;
+ margin: 10px;
+ font-size: 12px;
}
.dataTableSearchPattern {
- margin:8px 0 0 0;
- height:30px;
- display: block!important;
- white-space: nowrap;
- background: url(../../../themes/default/images/search_bg.png) no-repeat center 0;
- text-align:center;
+ margin: 8px 0 0 0;
+ height: 30px;
+ display: block !important;
+ white-space: nowrap;
+ background: url(../../../themes/default/images/search_bg.png) no-repeat center 0;
+ text-align: center;
}
.dataTableSearchPattern input {
- vertical-align:top;
- font-size:10px!important;
- color:#454545!important;
- border:0!important;
- background:transparent!important;
- width:21px;
- height:17px;
- overflow:hidden;
- opacity:0;
- filter:Alpha(opacity:0);
- cursor:pointer;
+ vertical-align: top;
+ font-size: 10px !important;
+ color: #454545 !important;
+ border: 0 !important;
+ background: transparent !important;
+ width: 21px;
+ height: 17px;
+ overflow: hidden;
+ opacity: 0;
+ filter: Alpha(opacity:0);
+ cursor: pointer;
}
.dataTableSearchPattern input:hover {
-
+
}
.dataTableSearchPattern #keyword {
- width:114px;
- height:auto;
- overflow:visible;
- padding:2px 6px;
- opacity:1;
- cursor:text;
- filter:Alpha(opacity:100);
+ width: 114px;
+ height: auto;
+ overflow: visible;
+ padding: 2px 6px;
+ opacity: 1;
+ cursor: text;
+ filter: Alpha(opacity:100);
}
-.dataTableNext,.dataTablePrevious {
- font-size: 12px;
- color: #184A83;
- cursor: pointer;
+.dataTableNext, .dataTablePrevious {
+ font-size: 12px;
+ color: #184A83;
+ cursor: pointer;
}
.datatableRelatedReports {
- color: #888;
- font-size: 11px;
+ color: #888;
+ font-size: 11px;
padding-bottom: 5px;
}
.datatableRelatedReports span {
- cursor: pointer;
- font-weight:bold;
+ cursor: pointer;
+ font-weight: bold;
}
/* are the following two supposed to be together? */
.subDataTable.dataTableFeatures {
- padding-top: 0;
- padding-bottom: 5px;
+ padding-top: 0;
+ padding-bottom: 5px;
}
+
.dataTableFeatures {
- padding-top: 10px;
- text-align: center;
+ padding-top: 10px;
+ text-align: center;
}
-.dataTableNext,.dataTablePrevious,.dataTableSearchPattern
-{
- display: none;
+.dataTableNext, .dataTablePrevious, .dataTableSearchPattern {
+ display: none;
}
+
.dataTableFeatures .loadingPiwik {
- font-size:0.9em;
+ font-size: 0.9em;
}
+
.subDataTable .dataTableFooterIcons {
- height: 0;
+ height: 0;
}
.dataTable .loadingPiwikBelow {
- padding-bottom:5px;
- display:block;
- text-align:center;
+ padding-bottom: 5px;
+ display: block;
+ text-align: center;
}
.dataTableFooterIcons {
- display:block;
- height: 20px;
- white-space:nowrap;
- font-size:10px;
- padding:6px 5px;
- border-top:1px solid #B6B0A6;
+ display: block;
+ height: 20px;
+ white-space: nowrap;
+ font-size: 10px;
+ padding: 6px 5px;
+ border-top: 1px solid #B6B0A6;
}
-.dataTableFooterWrap{
- position:relative;
+.dataTableFooterWrap {
+ position: relative;
float: left;
}
.dataTableFooterWrap select {
- float: left;
- margin: 1px 0 1px 10px;
+ float: left;
+ margin: 1px 0 1px 10px;
}
-.tableIcon{
- background:#f2f1ed;
- display:inline-block;
- float:left;
- margin:0 1px 0 0;
- padding:2px;
- border-radius:2px;
- -moz-border-radius:2px;
- -webkit-border-radius:2px;
+.tableIcon {
+ background: #f2f1ed;
+ display: inline-block;
+ float: left;
+ margin: 0 1px 0 0;
+ padding: 2px;
+ border-radius: 2px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
}
-.tableIcon:hover{
- background:#e9e8e1;
+
+.tableIcon:hover {
+ background: #e9e8e1;
}
-.activeIcon{
- background:#e9e8e1;
+.activeIcon {
+ background: #e9e8e1;
}
-.dataTableFooterActiveItem{
- position:absolute;
- top:-6px;
- left:0;
+.dataTableFooterActiveItem {
+ position: absolute;
+ top: -6px;
+ left: 0;
}
-.exportToFormatItems{
- background:#dcdacf;
- float:left;
- margin:0 1px 0 0;
- padding:4px 6px 3px 6px;
- color: #968d7f;
- border-radius:2px;
- -moz-border-radius:2px;
- -webkit-border-radius:2px;
+.exportToFormatItems {
+ background: #dcdacf;
+ float: left;
+ margin: 0 1px 0 0;
+ padding: 4px 6px 3px 6px;
+ color: #968d7f;
+ border-radius: 2px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
}
-.exportToFormatItems img{
- vertical-align:middle;
- margin:-4px -3px -2px 2px;
+.exportToFormatItems img {
+ vertical-align: middle;
+ margin: -4px -3px -2px 2px;
}
-.tableIconsGroup{
- float:left;
- padding-right:4px;
+.tableIconsGroup {
+ float: left;
+ padding-right: 4px;
}
+
.tableIconsGroup .tableIcon span {
- margin-right:5px;
- margin-left:5px;
+ margin-right: 5px;
+ margin-left: 5px;
}
-.tableIconsGroup img{
- vertical-align:bottom;
+
+.tableIconsGroup img {
+ vertical-align: bottom;
}
-.tableIconsGroupActive{
- display:block;
- float:left;
- background:#dcdacf;
- border-radius:2px;
- -moz-border-radius:2px;
- -webkit-border-radius:2px;
+.tableIconsGroupActive {
+ display: block;
+ float: left;
+ background: #dcdacf;
+ border-radius: 2px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
}
-.tableIconsGroupActive .tableIcon{
- background:none;
+
+.tableIconsGroupActive .tableIcon {
+ background: none;
}
-.tableIconsGroupActive .tableIcon:hover{
- background:#e9e8e1;
+
+.tableIconsGroupActive .tableIcon:hover {
+ background: #e9e8e1;
}
.exportToFormatIcons {
- float: left;
+ float: left;
}
.dataTableFooterIconsShow {
- float: left;
+ float: left;
}
-.dataTableFooterIcons,.dataTableFooterIcons a {
- text-decoration: none;
- color: #255792;
+.dataTableFooterIcons, .dataTableFooterIcons a {
+ text-decoration: none;
+ color: #255792;
}
.dataTableSpacer {
- clear: both;
+ clear: both;
}
/* Actions table */
table.dataTableActions tr td.labelodd {
- background-image: none;
+ background-image: none;
}
-
/* levels higher than 4 have a default padding left */
-tr.subActionsDataTable td.label,tr.actionsDataTable td.label {
- padding-left: 7em;
+tr.subActionsDataTable td.label, tr.actionsDataTable td.label {
+ padding-left: 7em;
}
-tr.subActionsDataTable{
- cursor:pointer;
+tr.subActionsDataTable {
+ cursor: pointer;
}
tr.level0 td.label {
- padding-left: 1.5em;
+ padding-left: 1.5em;
}
tr.level1 td.label {
- padding-left: 2.5em;
+ padding-left: 2.5em;
}
tr.level2 td.label {
- padding-left: 3.5em;
+ padding-left: 3.5em;
}
tr.level3 td.label {
- padding-left: 4.5em;
+ padding-left: 4.5em;
}
tr.level4 td.label {
- padding-left: 5em;
+ padding-left: 5em;
}
/* less right margins for the link image in the Pa*/
table.dataTableActions img.link {
- margin-right: 0.3em;
- margin-left:-0.5em;
+ margin-right: 0.3em;
+ margin-left: -0.5em;
}
+
tr td.label img.plusMinus {
- margin-left:-1em;
- margin-right: 3px;
+ margin-left: -1em;
+ margin-right: 3px;
}
.pk-emptyDataTable {
- padding-top: 20px;
- padding-bottom: 10px;
- text-align: center;
- font-style: italic;
+ padding-top: 20px;
+ padding-bottom: 10px;
+ text-align: center;
+ font-style: italic;
}
-
/* Documentation */
table.dataTable th .columnDocumentation {
- display: none;
- width: 165px;
- text-align: left;
- background: #f7f7f7;
- color: #444;
- font-size: 11px;
- font-weight: normal;
- border: 1px solid #e4e5e4;
- padding: 5px 10px 6px 10px;
- border-radius: 4px;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- z-index: 125;
- position: absolute;
- -moz-box-shadow: 0 0 4px #e4e5e4;
- -webkit-box-shadow: 0 0 4px #e4e5e4;
- box-shadow: 0 0 4px #e4e5e4;
- cursor: default;
+ display: none;
+ width: 165px;
+ text-align: left;
+ background: #f7f7f7;
+ color: #444;
+ font-size: 11px;
+ font-weight: normal;
+ border: 1px solid #e4e5e4;
+ padding: 5px 10px 6px 10px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ z-index: 125;
+ position: absolute;
+ -moz-box-shadow: 0 0 4px #e4e5e4;
+ -webkit-box-shadow: 0 0 4px #e4e5e4;
+ box-shadow: 0 0 4px #e4e5e4;
+ cursor: default;
}
table.dataTable th .columnDocumentationTitle {
- background: url(../../../themes/default/images/help.png) no-repeat;
- line-height: 14px;
- padding: 2px 0 3px 21px;
- font-weight: bold;
+ background: url(../../../themes/default/images/help.png) no-repeat;
+ line-height: 14px;
+ padding: 2px 0 3px 21px;
+ font-weight: bold;
}
.reportDocumentation {
- display: none;
- background: #f7f7f7;
- font-size: 12px;
- font-weight: normal;
- border: 1px solid #e4e5e4;
- margin: 0 0 10px 0;
- padding: 0;
- border-radius: 4px;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- max-width: 500px;
+ display: none;
+ background: #f7f7f7;
+ font-size: 12px;
+ font-weight: normal;
+ border: 1px solid #e4e5e4;
+ margin: 0 0 10px 0;
+ padding: 0;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ max-width: 500px;
}
.reportDocumentation p {
- padding: 5px 10px 6px 10px;
- margin: 0;
- color: #444;
- font-size: 12px;
- }
-
+ padding: 5px 10px 6px 10px;
+ margin: 0;
+ color: #444;
+ font-size: 12px;
+}
+
.reportDocumentationIcon {
- display: block;
- width: 16px;
- height: 16px;
- margin: 10px 0;
- background: url(../../../themes/default/images/help.png) no-repeat;
+ display: block;
+ width: 16px;
+ height: 16px;
+ margin: 10px 0;
+ background: url(../../../themes/default/images/help.png) no-repeat;
}
h2 .reportDocumentationIcon {
- position: absolute;
- margin: 4px 0 0 0;
- display: none;
- background: url(../../../themes/default/images/help_grey.png) no-repeat;
+ position: absolute;
+ margin: 4px 0 0 0;
+ display: none;
+ background: url(../../../themes/default/images/help_grey.png) no-repeat;
}
h2 .reportDocumentationIcon.hidden {
- background: none;
+ background: none;
}
.helpDate {
- color: #777777;
+ color: #777777;
font-size: 11px;
- font-style:italic;
+ font-style: italic;
padding: 4px;
text-align: right;
- display:block;
+ display: block;
}
table.dataTable .dataTableRowActions {
- position: absolute;
- display: none;
- overflow: hidden;
- margin-top: -5px;
+ position: absolute;
+ display: none;
+ overflow: hidden;
+ margin-top: -5px;
}
+
*+html table.dataTable .dataTableRowActions {
- margin-top: -7px;
+ margin-top: -7px;
}
table.dataTable .dataTableRowActions a {
- display: block;
- float: left;
- padding: 6px 4px 6px 0;
- margin: 0;
+ display: block;
+ float: left;
+ padding: 6px 4px 6px 0;
+ margin: 0;
}
table.dataTable .dataTableRowActions a.leftmost {
- padding-left: 4px;
+ padding-left: 4px;
}
table.dataTable .dataTableRowActions a.rightmost {
- padding-right: 8px;
+ padding-right: 8px;
}
table.dataTable .dataTableRowActions a img {
- margin: 0;
- padding: 0;
- border: 0;
- width: 20px;
- height: 17px;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ width: 20px;
+ height: 17px;
}
body .piwik-tooltip.rowActionTooltip {
- font-size: 11px;
- padding: 3px 5px 3px 6px;
+ font-size: 11px;
+ padding: 3px 5px 3px 6px;
}
-
.limitSelection {
float: right;
position: relative;
margin-left: 5px;
min-height: 20px;
- z-index:1;
+ z-index: 1;
}
.limitSelection.hidden {
@@ -656,7 +669,7 @@ body .piwik-tooltip.rowActionTooltip {
.limitSelection.disabled div {
opacity: 0.5;
cursor: not-allowed;
- filter:Alpha(opacity:50);
+ filter: Alpha(opacity:50);
}
.limitSelection.visible div {
@@ -703,46 +716,45 @@ body .piwik-tooltip.rowActionTooltip {
display: inline-block;
}
-
.tableConfiguration {
float: right;
position: relative;
margin-left: 5px;
min-height: 20px;
- min-width: 25px;
+ min-width: 25px;
}
a.tableConfigurationIcon {
- display: block;
- width: 30px;
- height: 22px;
- background: url(../../../themes/default/images/configure.png) no-repeat center 2px;
- position: absolute;
- z-index: 9;
- right: 0;
+ display: block;
+ width: 30px;
+ height: 22px;
+ background: url(../../../themes/default/images/configure.png) no-repeat center 2px;
+ position: absolute;
+ z-index: 9;
+ right: 0;
}
a.tableConfigurationIcon.highlighted {
- display: block;
- width: 30px;
- height: 22px;
- background-image: url(../../../themes/default/images/configure-highlight.png);
- position: absolute;
- z-index: 9;
- right: 0;
+ display: block;
+ width: 30px;
+ height: 22px;
+ background-image: url(../../../themes/default/images/configure-highlight.png);
+ position: absolute;
+ z-index: 9;
+ right: 0;
}
.tableConfiguration ul {
overflow: visible;
background-color: #fff;
- display: none;
- position: relative;
- z-index: 8;
- text-align: left;
+ display: none;
+ position: relative;
+ z-index: 8;
+ text-align: left;
}
.tableConfiguration ul.open {
- display: block;
+ display: block;
}
.tableConfiguration ul li {
@@ -761,13 +773,13 @@ a.tableConfigurationIcon.highlighted {
border-radius: 0 0 4px 4px;
-moz-border-radius: 0 0 4px 4px;
-webkit-border-radius: 0 0 4px 4px;
- height: 25px;
- cursor: default;
- margin-top: -4px;
+ height: 25px;
+ cursor: default;
+ margin-top: -4px;
}
.tableConfiguration ul li.first {
- margin-top: -65px;
+ margin-top: -65px;
}
.tableConfiguration ul li.last {
@@ -780,8 +792,8 @@ a.tableConfigurationIcon.highlighted {
.tableConfiguration div.configItem {
cursor: pointer;
padding: 5px 10px;
- line-height: 15px;
- color: #444;
+ line-height: 15px;
+ color: #444;
}
.tableConfiguration div.configItem:hover {
@@ -789,5 +801,5 @@ a.tableConfigurationIcon.highlighted {
}
.tableConfiguration div.configItem span.action {
- color: #255792;
+ color: #255792;
}
diff --git a/plugins/CoreHome/templates/datatable.js b/plugins/CoreHome/templates/datatable.js
index 755ddcbbf1..1476a2e711 100644
--- a/plugins/CoreHome/templates/datatable.js
+++ b/plugins/CoreHome/templates/datatable.js
@@ -10,150 +10,129 @@
//-----------------------------------------------------------------------------
//DataTable constructor
-function dataTable()
-{
- this.param = {};
+function dataTable() {
+ this.param = {};
}
//Prototype of the DataTable object
dataTable.prototype =
{
- //initialisation function
- init: function(workingDivId, domElem)
- {
- if(typeof domElem == "undefined")
- {
- domElem = $('#'+workingDivId);
- }
-
- this.workingDivId = workingDivId;
- this.loadedSubDataTable = {};
- this.isEmpty = $('.pk-emptyDataTable', domElem).length > 0;
- this.bindEventsAndApplyStyle(domElem);
- this.initialized = true;
-
- domElem.data('piwikDataTable', this);
- },
-
- //function triggered when user click on column sort
- onClickSort: function(domElem)
- {
- var self = this;
- var newColumnToSort = $(domElem).attr('id');
- // we lookup if the column to sort was already this one, if it is the case then we switch from desc <-> asc
- if(self.param.filter_sort_column == newColumnToSort)
- {
- // toggle the sorted order
- if(this.param.filter_sort_order == 'asc')
- {
- self.param.filter_sort_order = 'desc';
- }
- else
- {
- self.param.filter_sort_order = 'asc';
- }
- }
- self.param.filter_offset = 0;
- self.param.filter_sort_column = newColumnToSort;
- self.reloadAjaxDataTable();
- },
-
- setGraphedColumn: function( columnName )
- {
- this.param.columns = columnName;
- },
-
- //Reset DataTable filters (used before a reload or view change)
- resetAllFilters: function()
- {
- var self = this;
- var FiltersToRestore = new Array();
- filters = [
- 'filter_column',
- 'filter_pattern',
- 'filter_column_recursive',
- 'filter_pattern_recursive',
- 'enable_filter_excludelowpop',
- 'filter_offset',
- 'filter_sort_column',
- 'filter_sort_order',
- 'disable_generic_filters',
- 'columns',
- 'flat',
- 'include_aggregate_rows'
- ];
-
- for(var key in filters)
- {
- var value = filters[key];
- FiltersToRestore[value] = self.param[value];
- delete self.param[value];
- }
-
- return FiltersToRestore;
- },
-
- //Restores the filters to the values given in the array in parameters
- restoreAllFilters: function(FiltersToRestore)
- {
- var self = this;
- for(key in FiltersToRestore)
- {
- self.param[key] = FiltersToRestore[key];
- }
- },
-
- //Translate string parameters to javascript builtins
- //'true' -> true, 'false' -> false
- //it simplifies condition tests in the code
- cleanParams: function()
- {
- var self = this;
- for(var key in self.param)
- {
- if(self.param[key] == 'true') self.param[key]=true;
- if(self.param[key] == 'false') self.param[key]=false;
- }
- },
-
- // Function called to trigger the AJAX request
- // The ajax request contains the function callback to trigger if the request is successful or failed
- // displayLoading = false When we don't want to display the Loading... DIV .loadingPiwik
- // for example when the script add a Loading... it self and doesn't want to display the generic Loading
- reloadAjaxDataTable: function(displayLoading, callbackSuccess)
- {
- var self = this;
-
- if (typeof displayLoading == "undefined")
- {
- displayLoading = true;
- }
- if (typeof callbackSuccess == "undefined")
- {
- callbackSuccess = function (response)
- {
- self.dataTableLoaded(response, self.workingDivId);
- };
- }
-
- if(displayLoading)
- {
- $('#'+self.workingDivId+' .loadingPiwik').last().css('display','block');
- }
-
- // when switching to display graphs, reset limit
- if (self.param.viewDataTable && self.param.viewDataTable.indexOf('graph') === 0)
- {
- delete self.param.filter_offset;
- delete self.param.filter_limit;
- }
-
- var container = $('#'+self.workingDivId+' .piwik-graph');
+ //initialisation function
+ init: function (workingDivId, domElem) {
+ if (typeof domElem == "undefined") {
+ domElem = $('#' + workingDivId);
+ }
+
+ this.workingDivId = workingDivId;
+ this.loadedSubDataTable = {};
+ this.isEmpty = $('.pk-emptyDataTable', domElem).length > 0;
+ this.bindEventsAndApplyStyle(domElem);
+ this.initialized = true;
+
+ domElem.data('piwikDataTable', this);
+ },
+
+ //function triggered when user click on column sort
+ onClickSort: function (domElem) {
+ var self = this;
+ var newColumnToSort = $(domElem).attr('id');
+ // we lookup if the column to sort was already this one, if it is the case then we switch from desc <-> asc
+ if (self.param.filter_sort_column == newColumnToSort) {
+ // toggle the sorted order
+ if (this.param.filter_sort_order == 'asc') {
+ self.param.filter_sort_order = 'desc';
+ }
+ else {
+ self.param.filter_sort_order = 'asc';
+ }
+ }
+ self.param.filter_offset = 0;
+ self.param.filter_sort_column = newColumnToSort;
+ self.reloadAjaxDataTable();
+ },
+
+ setGraphedColumn: function (columnName) {
+ this.param.columns = columnName;
+ },
+
+ //Reset DataTable filters (used before a reload or view change)
+ resetAllFilters: function () {
+ var self = this;
+ var FiltersToRestore = new Array();
+ filters = [
+ 'filter_column',
+ 'filter_pattern',
+ 'filter_column_recursive',
+ 'filter_pattern_recursive',
+ 'enable_filter_excludelowpop',
+ 'filter_offset',
+ 'filter_sort_column',
+ 'filter_sort_order',
+ 'disable_generic_filters',
+ 'columns',
+ 'flat',
+ 'include_aggregate_rows'
+ ];
+
+ for (var key in filters) {
+ var value = filters[key];
+ FiltersToRestore[value] = self.param[value];
+ delete self.param[value];
+ }
+
+ return FiltersToRestore;
+ },
+
+ //Restores the filters to the values given in the array in parameters
+ restoreAllFilters: function (FiltersToRestore) {
+ var self = this;
+ for (key in FiltersToRestore) {
+ self.param[key] = FiltersToRestore[key];
+ }
+ },
+
+ //Translate string parameters to javascript builtins
+ //'true' -> true, 'false' -> false
+ //it simplifies condition tests in the code
+ cleanParams: function () {
+ var self = this;
+ for (var key in self.param) {
+ if (self.param[key] == 'true') self.param[key] = true;
+ if (self.param[key] == 'false') self.param[key] = false;
+ }
+ },
+
+ // Function called to trigger the AJAX request
+ // The ajax request contains the function callback to trigger if the request is successful or failed
+ // displayLoading = false When we don't want to display the Loading... DIV .loadingPiwik
+ // for example when the script add a Loading... it self and doesn't want to display the generic Loading
+ reloadAjaxDataTable: function (displayLoading, callbackSuccess) {
+ var self = this;
+
+ if (typeof displayLoading == "undefined") {
+ displayLoading = true;
+ }
+ if (typeof callbackSuccess == "undefined") {
+ callbackSuccess = function (response) {
+ self.dataTableLoaded(response, self.workingDivId);
+ };
+ }
+
+ if (displayLoading) {
+ $('#' + self.workingDivId + ' .loadingPiwik').last().css('display', 'block');
+ }
+
+ // when switching to display graphs, reset limit
+ if (self.param.viewDataTable && self.param.viewDataTable.indexOf('graph') === 0) {
+ delete self.param.filter_offset;
+ delete self.param.filter_limit;
+ }
+
+ var container = $('#' + self.workingDivId + ' .piwik-graph');
var params = {};
- for(var key in self.param)
- {
- if(typeof self.param[key] != "undefined" && self.param[key] != '')
+ for (var key in self.param) {
+ if (typeof self.param[key] != "undefined" && self.param[key] != '')
params[key] = self.param[key];
}
@@ -168,1511 +147,1373 @@ dataTable.prototype =
);
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
- },
-
- // Function called when the AJAX request is successful
- // it looks for the ID of the response and replace the very same ID
- // in the current page with the AJAX response
- dataTableLoaded: function(response, workingDivId)
- {
- var content = $(response);
-
- var idToReplace = workingDivId || $(content).attr('id');
- var dataTableSel = $('#'+idToReplace);
-
- // keep the original list of related reports
- var oldReportsElem = $('.datatableRelatedReports', dataTableSel);
- $('.datatableRelatedReports', content).replaceWith(oldReportsElem);
-
- // if the current dataTable is located inside another datatable
- table = $(content).parents('table.dataTable');
- if(dataTableSel.parents('.dataTable').is('table'))
- {
- // we add class to the table so that we can give a different style to the subtable
- $(content).find('table.dataTable').addClass('subDataTable');
- $(content).find('.dataTableFeatures').addClass('subDataTable');
-
- //we force the initialisation of subdatatables
- dataTableSel.replaceWith(content);
- }
- else
- {
- dataTableSel.find('object').remove();
- dataTableSel.replaceWith(content);
- }
-
- piwikHelper.lazyScrollTo(content[0], 400);
-
- return content;
- },
-
- /* This method is triggered when a new DIV is loaded, which happens
- - at the first loading of the page
- - after any AJAX loading of a DataTable
-
- This method basically add features to the DataTable,
- - such as column sorting, searching in the rows, displaying Next / Previous links, etc.
- - add styles to the cells and rows (odd / even styles)
- - modify some rows to add images if a span img is found, or add a link if a span urlLink is found
- or truncate the labels when they are too big
- - bind new events onclick / hover / etc. to trigger AJAX requests,
- nice hovertip boxes for truncated cells
- */
- bindEventsAndApplyStyle: function(domElem)
- {
- var self = this;
- self.cleanParams();
- self.handleSort(domElem);
- self.handleLimit(domElem);
- self.handleSearchBox(domElem);
- self.handleOffsetInformation(domElem);
- self.handleAnnotationsButton(domElem);
- self.handleEvolutionAnnotations(domElem);
- self.handleExportBox(domElem);
- self.applyCosmetics(domElem);
- self.handleSubDataTable(domElem);
- self.handleConfigurationBox(domElem);
- self.handleColumnDocumentation(domElem);
- self.handleReportDocumentation(domElem);
- self.handleRowActions(domElem);
- self.handleRelatedReports(domElem);
- self.handleTriggeredEvents(domElem);
- },
-
- handleLimit: function(domElem)
- {
- var tableRowLimits = [5, 10, 25, 50, 100, 250, 500],
- evolutionLimits =
- {
- day: [30, 60, 90, 180, 365, 500],
- week: [4, 12, 26, 52, 104, 500],
- month: [3, 6, 12, 24, 36, 120],
- year: [3, 5, 10]
- };
-
- var self = this;
- if( typeof self.parentId != "undefined" && self.parentId != '')
- {
- // no limit selector for subtables
- $('.limitSelection', domElem).remove();
- return;
- }
-
- // configure limit control
- var setLimitValue, numbers, limitParamName;
- if (self.param.viewDataTable == 'graphEvolution')
- {
- limitParamName = 'evolution_' + self.param.period + '_last_n';
- numbers = evolutionLimits[self.param.period] || tableRowLimits;
-
- setLimitValue = function (params, limit)
- {
- params[limitParamName] = limit;
- };
- }
- else
- {
- numbers = tableRowLimits;
- limitParamName = 'filter_limit';
-
- setLimitValue = function (params, value)
- {
- params.filter_limit = value;
- params.filter_offset = 0;
- };
- }
-
- // setup limit control
- $('.limitSelection', domElem).append('<div><span>'+self.param[limitParamName]+'</span></div><ul></ul>');
-
- if (self.param.viewDataTable == 'table'
- || self.param.viewDataTable == 'tableAllColumns'
- || self.param.viewDataTable == 'tableGoals'
- || self.param.viewDataTable == 'ecommerceOrder'
- || self.param.viewDataTable == 'ecommerceAbandonedCart'
- || self.param.viewDataTable == 'graphEvolution')
- {
- $('.limitSelection ul', domElem).hide();
- for(var i=0; i<numbers.length; i++)
- {
- $('.limitSelection ul', domElem).append('<li value="'+numbers[i]+'"><span>'+numbers[i]+'</span></li>');
- }
- $('.limitSelection ul li:last', domElem).addClass('last');
-
- if (!self.isEmpty)
- {
- var show = function() {
- $('.limitSelection ul', domElem).show();
- $('.limitSelection', domElem).addClass('visible');
- $(document).on('mouseup.limitSelection',function(e) {
- if ((!$(e.target).parents('.limitSelection').length
- || $(e.target).parents('.limitSelection') != $('.limitSelection', domElem))
- && !$(e.target).is('.limitSelection'))
- {
- hide();
- }
- });
- }
- var hide = function() {
- $('.limitSelection ul', domElem).hide();
- $('.limitSelection', domElem).removeClass('visible');
- $(document).off('mouseup.limitSelection');
- }
- $('.limitSelection div', domElem).on('click', function(){
- $('.limitSelection', domElem).is('.visible') ? hide() : show();
- });
- $('.limitSelection ul li', domElem).on('click', function(event){
- var limit = parseInt($(event.target).text());
-
- hide();
- if (limit != self.param[limitParamName])
- {
- setLimitValue(self.param, limit);
- $('.limitSelection>div>span', domElem).text(limit);
- self.reloadAjaxDataTable();
-
- var data = {};
- data[limitParamName] = self.param[limitParamName];
- self.notifyWidgetParametersChange(domElem, data);
- }
- });
- }
- else
- {
- $('.limitSelection', domElem).toggleClass('disabled');
- }
- }
- else
- {
- $('.limitSelection', domElem).hide();
- }
- },
-
- // if sorting the columns is enabled, when clicking on a column,
- // - if this column was already the one used for sorting, we revert the order desc<->asc
- // - we send the ajax request with the new sorting information
- handleSort: function(domElem)
- {
- var self = this;
-
- function getSortImageSrc() {
- var imageSortSrc = false;
- if (currentIsSubDataTable) {
- if (self.param.filter_sort_order == 'asc') {
- imageSortSrc = 'themes/default/images/sort_subtable_asc.png';
- } else {
- imageSortSrc = 'themes/default/images/sort_subtable_desc.png';
+ },
+
+ // Function called when the AJAX request is successful
+ // it looks for the ID of the response and replace the very same ID
+ // in the current page with the AJAX response
+ dataTableLoaded: function (response, workingDivId) {
+ var content = $(response);
+
+ var idToReplace = workingDivId || $(content).attr('id');
+ var dataTableSel = $('#' + idToReplace);
+
+ // keep the original list of related reports
+ var oldReportsElem = $('.datatableRelatedReports', dataTableSel);
+ $('.datatableRelatedReports', content).replaceWith(oldReportsElem);
+
+ // if the current dataTable is located inside another datatable
+ table = $(content).parents('table.dataTable');
+ if (dataTableSel.parents('.dataTable').is('table')) {
+ // we add class to the table so that we can give a different style to the subtable
+ $(content).find('table.dataTable').addClass('subDataTable');
+ $(content).find('.dataTableFeatures').addClass('subDataTable');
+
+ //we force the initialisation of subdatatables
+ dataTableSel.replaceWith(content);
}
- } else {
- if (self.param.filter_sort_order == 'asc') {
- imageSortSrc = 'themes/default/images/sortasc.png';
- } else {
- imageSortSrc = 'themes/default/images/sortdesc.png';
+ else {
+ dataTableSel.find('object').remove();
+ dataTableSel.replaceWith(content);
}
- }
- return imageSortSrc;
- }
- if( self.param.enable_sort )
- {
- $('.sortable', domElem).off('click.dataTableSort').on('click.dataTableSort',
- function()
- {
- $(this).off('click.dataTableSort');
- self.onClickSort(this);
- }
- );
-
- if (self.param.filter_sort_column != '')
- {
- // are we in a subdatatable?
- var currentIsSubDataTable = $(domElem).parent().hasClass('cellSubDataTable');
- var imageSortSrc = getSortImageSrc();
- var imageSortWidth = 16;
- var imageSortHeight = 16;
- // we change the style of the column currently used as sort column
- // adding an image and the class columnSorted to the TD
- $(".sortable#"+self.param.filter_sort_column+' #thDIV', domElem).parent()
- .addClass('columnSorted')
- .prepend('<div id="sortIconContainer"><img id="sortIcon" width="'+imageSortWidth+'" height="'+imageSortHeight+'" src="'+ imageSortSrc +'" /></div>');
- }
- }
- },
-
- //behaviour for the DataTable 'search box'
- handleSearchBox: function(domElem, callbackSuccess)
- {
- var self = this;
-
- var currentPattern = self.param.filter_pattern;
- if(typeof self.param.filter_pattern != "undefined"
- && self.param.filter_pattern.length > 0)
- {
- currentPattern = self.param.filter_pattern;
- }
- else if(typeof self.param.filter_pattern_recursive != "undefined"
- && self.param.filter_pattern_recursive.length > 0)
- {
- currentPattern = self.param.filter_pattern_recursive;
- }
- else
- {
- currentPattern = '';
- }
- currentPattern = piwikHelper.htmlDecode(currentPattern);
-
- $('.dataTableSearchPattern', domElem)
- .show()
- .each(function(){
- // when enter is pressed in the input field we submit the form
- $('#keyword', this)
- .on("keyup",
- function(e)
- {
- if(isEnterKey(e))
- {
- $(this).siblings(':submit').submit();
- }
- }
- )
- .val(currentPattern)
- ;
-
- $(':submit', this).submit(
- function()
- {
- var keyword = $(this).siblings('#keyword').val();
- self.param.filter_offset = 0;
-
- if(self.param.search_recursive)
- {
- self.param.filter_column_recursive = 'label';
- self.param.filter_pattern_recursive = keyword;
- }
- else
- {
- self.param.filter_column = 'label';
- self.param.filter_pattern = keyword;
- }
- self.reloadAjaxDataTable(true, callbackSuccess);
- }
- );
-
- $(':submit', this)
- .click( function(){ $(this).submit(); })
- ;
-
- // in the case there is a searched keyword we display the RESET image
- if(currentPattern)
- {
- var target = this;
- var clearImg = $('<span style="position: relative;">\
+ piwikHelper.lazyScrollTo(content[0], 400);
+
+ return content;
+ },
+
+ /* This method is triggered when a new DIV is loaded, which happens
+ - at the first loading of the page
+ - after any AJAX loading of a DataTable
+
+ This method basically add features to the DataTable,
+ - such as column sorting, searching in the rows, displaying Next / Previous links, etc.
+ - add styles to the cells and rows (odd / even styles)
+ - modify some rows to add images if a span img is found, or add a link if a span urlLink is found
+ or truncate the labels when they are too big
+ - bind new events onclick / hover / etc. to trigger AJAX requests,
+ nice hovertip boxes for truncated cells
+ */
+ bindEventsAndApplyStyle: function (domElem) {
+ var self = this;
+ self.cleanParams();
+ self.handleSort(domElem);
+ self.handleLimit(domElem);
+ self.handleSearchBox(domElem);
+ self.handleOffsetInformation(domElem);
+ self.handleAnnotationsButton(domElem);
+ self.handleEvolutionAnnotations(domElem);
+ self.handleExportBox(domElem);
+ self.applyCosmetics(domElem);
+ self.handleSubDataTable(domElem);
+ self.handleConfigurationBox(domElem);
+ self.handleColumnDocumentation(domElem);
+ self.handleReportDocumentation(domElem);
+ self.handleRowActions(domElem);
+ self.handleRelatedReports(domElem);
+ self.handleTriggeredEvents(domElem);
+ },
+
+ handleLimit: function (domElem) {
+ var tableRowLimits = [5, 10, 25, 50, 100, 250, 500],
+ evolutionLimits =
+ {
+ day: [30, 60, 90, 180, 365, 500],
+ week: [4, 12, 26, 52, 104, 500],
+ month: [3, 6, 12, 24, 36, 120],
+ year: [3, 5, 10]
+ };
+
+ var self = this;
+ if (typeof self.parentId != "undefined" && self.parentId != '') {
+ // no limit selector for subtables
+ $('.limitSelection', domElem).remove();
+ return;
+ }
+
+ // configure limit control
+ var setLimitValue, numbers, limitParamName;
+ if (self.param.viewDataTable == 'graphEvolution') {
+ limitParamName = 'evolution_' + self.param.period + '_last_n';
+ numbers = evolutionLimits[self.param.period] || tableRowLimits;
+
+ setLimitValue = function (params, limit) {
+ params[limitParamName] = limit;
+ };
+ }
+ else {
+ numbers = tableRowLimits;
+ limitParamName = 'filter_limit';
+
+ setLimitValue = function (params, value) {
+ params.filter_limit = value;
+ params.filter_offset = 0;
+ };
+ }
+
+ // setup limit control
+ $('.limitSelection', domElem).append('<div><span>' + self.param[limitParamName] + '</span></div><ul></ul>');
+
+ if (self.param.viewDataTable == 'table'
+ || self.param.viewDataTable == 'tableAllColumns'
+ || self.param.viewDataTable == 'tableGoals'
+ || self.param.viewDataTable == 'ecommerceOrder'
+ || self.param.viewDataTable == 'ecommerceAbandonedCart'
+ || self.param.viewDataTable == 'graphEvolution') {
+ $('.limitSelection ul', domElem).hide();
+ for (var i = 0; i < numbers.length; i++) {
+ $('.limitSelection ul', domElem).append('<li value="' + numbers[i] + '"><span>' + numbers[i] + '</span></li>');
+ }
+ $('.limitSelection ul li:last', domElem).addClass('last');
+
+ if (!self.isEmpty) {
+ var show = function () {
+ $('.limitSelection ul', domElem).show();
+ $('.limitSelection', domElem).addClass('visible');
+ $(document).on('mouseup.limitSelection', function (e) {
+ if ((!$(e.target).parents('.limitSelection').length
+ || $(e.target).parents('.limitSelection') != $('.limitSelection', domElem))
+ && !$(e.target).is('.limitSelection')) {
+ hide();
+ }
+ });
+ }
+ var hide = function () {
+ $('.limitSelection ul', domElem).hide();
+ $('.limitSelection', domElem).removeClass('visible');
+ $(document).off('mouseup.limitSelection');
+ }
+ $('.limitSelection div', domElem).on('click', function () {
+ $('.limitSelection', domElem).is('.visible') ? hide() : show();
+ });
+ $('.limitSelection ul li', domElem).on('click', function (event) {
+ var limit = parseInt($(event.target).text());
+
+ hide();
+ if (limit != self.param[limitParamName]) {
+ setLimitValue(self.param, limit);
+ $('.limitSelection>div>span', domElem).text(limit);
+ self.reloadAjaxDataTable();
+
+ var data = {};
+ data[limitParamName] = self.param[limitParamName];
+ self.notifyWidgetParametersChange(domElem, data);
+ }
+ });
+ }
+ else {
+ $('.limitSelection', domElem).toggleClass('disabled');
+ }
+ }
+ else {
+ $('.limitSelection', domElem).hide();
+ }
+ },
+
+ // if sorting the columns is enabled, when clicking on a column,
+ // - if this column was already the one used for sorting, we revert the order desc<->asc
+ // - we send the ajax request with the new sorting information
+ handleSort: function (domElem) {
+ var self = this;
+
+ function getSortImageSrc() {
+ var imageSortSrc = false;
+ if (currentIsSubDataTable) {
+ if (self.param.filter_sort_order == 'asc') {
+ imageSortSrc = 'themes/default/images/sort_subtable_asc.png';
+ } else {
+ imageSortSrc = 'themes/default/images/sort_subtable_desc.png';
+ }
+ } else {
+ if (self.param.filter_sort_order == 'asc') {
+ imageSortSrc = 'themes/default/images/sortasc.png';
+ } else {
+ imageSortSrc = 'themes/default/images/sortdesc.png';
+ }
+ }
+ return imageSortSrc;
+ }
+
+ if (self.param.enable_sort) {
+ $('.sortable', domElem).off('click.dataTableSort').on('click.dataTableSort',
+ function () {
+ $(this).off('click.dataTableSort');
+ self.onClickSort(this);
+ }
+ );
+
+ if (self.param.filter_sort_column != '') {
+ // are we in a subdatatable?
+ var currentIsSubDataTable = $(domElem).parent().hasClass('cellSubDataTable');
+ var imageSortSrc = getSortImageSrc();
+ var imageSortWidth = 16;
+ var imageSortHeight = 16;
+ // we change the style of the column currently used as sort column
+ // adding an image and the class columnSorted to the TD
+ $(".sortable#" + self.param.filter_sort_column + ' #thDIV', domElem).parent()
+ .addClass('columnSorted')
+ .prepend('<div id="sortIconContainer"><img id="sortIcon" width="' + imageSortWidth + '" height="' + imageSortHeight + '" src="' + imageSortSrc + '" /></div>');
+ }
+ }
+ },
+
+ //behaviour for the DataTable 'search box'
+ handleSearchBox: function (domElem, callbackSuccess) {
+ var self = this;
+
+ var currentPattern = self.param.filter_pattern;
+ if (typeof self.param.filter_pattern != "undefined"
+ && self.param.filter_pattern.length > 0) {
+ currentPattern = self.param.filter_pattern;
+ }
+ else if (typeof self.param.filter_pattern_recursive != "undefined"
+ && self.param.filter_pattern_recursive.length > 0) {
+ currentPattern = self.param.filter_pattern_recursive;
+ }
+ else {
+ currentPattern = '';
+ }
+ currentPattern = piwikHelper.htmlDecode(currentPattern);
+
+ $('.dataTableSearchPattern', domElem)
+ .show()
+ .each(function () {
+ // when enter is pressed in the input field we submit the form
+ $('#keyword', this)
+ .on("keyup",
+ function (e) {
+ if (isEnterKey(e)) {
+ $(this).siblings(':submit').submit();
+ }
+ }
+ )
+ .val(currentPattern)
+ ;
+
+ $(':submit', this).submit(
+ function () {
+ var keyword = $(this).siblings('#keyword').val();
+ self.param.filter_offset = 0;
+
+ if (self.param.search_recursive) {
+ self.param.filter_column_recursive = 'label';
+ self.param.filter_pattern_recursive = keyword;
+ }
+ else {
+ self.param.filter_column = 'label';
+ self.param.filter_pattern = keyword;
+ }
+ self.reloadAjaxDataTable(true, callbackSuccess);
+ }
+ );
+
+ $(':submit', this)
+ .click(function () { $(this).submit(); })
+ ;
+
+ // in the case there is a searched keyword we display the RESET image
+ if (currentPattern) {
+ var target = this;
+ var clearImg = $('<span style="position: relative;">\
<img src="plugins/CoreHome/templates/images/reset_search.png" style="position: absolute; top: 4px; left: -15px; cursor: pointer; display: inline;" title="Clear" />\
</span>')
- .click( function() {
- $('#keyword', target).val('');
- $(':submit', target).submit();
- });
- $('#keyword',this).after(clearImg);
-
- }
- }
- );
- },
-
- //behaviour for '< prev' 'next >' links and page count
- handleOffsetInformation: function(domElem)
- {
- var self = this;
-
- $('.dataTablePages', domElem).each(
- function(){
- var offset = 1+Number(self.param.filter_offset);
- var offsetEnd = Number(self.param.filter_offset) + Number(self.param.filter_limit);
- var totalRows = Number(self.param.totalRows);
- offsetEndDisp = offsetEnd;
-
- if (self.param.keep_summary_row == 1) --totalRows;
-
- if(offsetEnd > totalRows) offsetEndDisp = totalRows;
-
- // only show this string if there is some rows in the datatable
- if(totalRows != 0)
- {
- var str = sprintf(_pk_translate('CoreHome_PageOf_js'),offset + '-' + offsetEndDisp,totalRows);
- $(this).text(str);
- }
- }
- );
-
- // Display the next link if the total Rows is greater than the current end row
- $('.dataTableNext', domElem)
- .each(function(){
- var offsetEnd = Number(self.param.filter_offset)
- + Number(self.param.filter_limit);
- var totalRows = Number(self.param.totalRows);
- if (self.param.keep_summary_row == 1) --totalRows;
- if(offsetEnd < totalRows)
- {
- $(this).css('display','inline');
- }
- })
- // bind the click event to trigger the ajax request with the new offset
- .click(function(){
- $(this).off('click');
- self.param.filter_offset = Number(self.param.filter_offset) + Number(self.param.filter_limit);
- self.reloadAjaxDataTable();
- })
- ;
-
- // Display the previous link if the current offset is not zero
- $('.dataTablePrevious', domElem)
- .each(function(){
- var offset = 1+Number(self.param.filter_offset);
- if(offset != 1)
- {
- $(this).css('display','inline');
- }
- }
- )
- // bind the click event to trigger the ajax request with the new offset
- // take care of the negative offset, we setup 0
- .click(
- function(){
- $(this).off('click');
- var offset = Number(self.param.filter_offset) - Number(self.param.filter_limit);
- if(offset < 0) { offset = 0; }
- self.param.filter_offset = offset;
- self.param.previous = 1;
- self.reloadAjaxDataTable();
- }
- );
- },
-
- handleEvolutionAnnotations: function(domElem)
- {
- var self = this;
- if (self.param.viewDataTable == 'graphEvolution'
- && $('.annotationView', domElem).length > 0)
- {
- // get dates w/ annotations across evolution period (have to do it through AJAX since we
- // determine placement using the elements created by jqplot)
- piwik.annotations.api.getEvolutionIcons(
- self.param.idSite,
- self.param.date,
- self.param.period,
- self.param['evolution_' + self.param.period + '_last_n'],
- function (response)
- {
- var annotations = $(response),
- datatableFeatures = $('.dataTableFeatures', domElem),
- noteSize = 16,
- annotationAxisHeight = 30 // css height + padding + margin
- ;
-
- // set position of evolution annotation icons
- annotations.css({
- top: -datatableFeatures.height() - annotationAxisHeight + noteSize / 2,
- left: 6 // padding-left of .jqplot-evolution element (in graph.tpl)
- });
-
- piwik.annotations.placeEvolutionIcons(annotations, domElem);
-
- // add new section under axis
- datatableFeatures.append(annotations);
-
- // reposition annotation icons every time the graph is resized
- $('.piwik-graph', domElem).on('resizeGraph', function() {
- piwik.annotations.placeEvolutionIcons(annotations, domElem);
- });
-
- // on hover of x-axis, show note icon over correct part of x-axis
- $('span', annotations).hover(
- function() { $(this).css('opacity', 1); },
- function() {
- if ($(this).attr('data-count') == 0) // only hide if there are no annotations for this note
- {
- $(this).css('opacity', 0);
- }
- }
- );
-
- // when clicking an annotation, show the annotation viewer for that period
- $('span', annotations).click(function() {
- var spanSelf = $(this),
- date = spanSelf.attr('data-date'),
- oldDate = $('.annotation-manager', domElem).attr('data-date');
- if (date)
- {
- var period = self.param.period;
- if (period == 'range')
- {
- period = 'day';
- }
-
- piwik.annotations.showAnnotationViewer(
- domElem,
- self.param.idSite,
- date,
- period,
- undefined, // lastN
- function (manager) {
- manager.attr('data-is-range', 0);
- $('.annotationView img', domElem)
- .attr('title', _pk_translate('Annotations_IconDesc_js'));
-
- var viewAndAdd = _pk_translate('Annotations_ViewAndAddAnnotations_js'),
- hideNotes = _pk_translate('Annotations_HideAnnotationsFor_js');
-
- // change the tooltip of the previously clicked evolution icon (if any)
- if (oldDate)
- {
- $('span', annotations).each(function() {
- if ($(this).attr('data-date') == oldDate)
- {
- $(this).attr('title', viewAndAdd.replace("%s", oldDate));
- return false;
- }
- });
- }
-
- // change the tooltip of the clicked evolution icon
- if (manager.is(':hidden'))
- {
- spanSelf.attr('title', viewAndAdd.replace("%s", date));
- }
- else
- {
- spanSelf.attr('title', hideNotes.replace("%s", date));
- }
- }
- );
- }
- });
-
- // when hover over annotation in annotation manager, highlight the annotation
- // icon
- var runningAnimation = null;
- domElem.on('mouseenter', '.annotation', function(e) {
- var date = $(this).attr('data-date');
-
- // find the icon for this annotation
- var icon = $();
- $('span', annotations).each(function() {
- if ($(this).attr('data-date') == date)
- {
- icon = $('img', this);
- return false;
- }
- });
-
- if (icon[0] == runningAnimation) // if the animation is already running, do nothing
- {
- return;
- }
-
- // stop ongoing animations
- $('span', annotations).each(function() {
- $('img', this).removeAttr('style');
- });
-
- // start a bounce animation
- icon.effect("bounce", {times: 1, distance: 10}, 1000);
- runningAnimation = icon[0];
- });
-
- // reset running animation item when leaving annotations list
- domElem.on('mouseleave', '.annotations', function(e) {
- runningAnimation = null;
- });
- }
- );
- }
- },
-
- handleAnnotationsButton: function(domElem)
- {
- var self = this;
- if (self.param.idSubtable) // no annotations for subtables, just whole reports
- {
- return;
- }
-
- // show the annotations view on click
- $('.annotationView', domElem).click(function() {
- var annotationManager = $('.annotation-manager', domElem);
-
- if (annotationManager.length > 0
- && annotationManager.attr('data-is-range') == 1)
- {
- if (annotationManager.is(':hidden'))
- {
- annotationManager.slideDown('slow'); // showing
- $('img', this).attr('title', _pk_translate('Annotations_IconDescHideNotes_js'));
- }
- else
- {
- annotationManager.slideUp('slow'); // hiding
- $('img', this).attr('title', _pk_translate('Annotations_IconDesc_js'));
- }
- }
- else
- {
- // show the annotation viewer for the whole date range
- var lastN = self.param['evolution_' + self.param.period + '_last_n'];
- piwik.annotations.showAnnotationViewer(
- domElem,
- self.param.idSite,
- self.param.date,
- self.param.period,
- lastN,
- function(manager) {
- manager.attr('data-is-range', 1);
- }
- );
-
- // change the tooltip of the view annotation icon
- $('img', this).attr('title', _pk_translate('Annotations_IconDescHideNotes_js'));
- }
- });
- },
-
- // DataTable view box (simple table, all columns table, Goals table, pie graph, tag cloud, graph, ...)
- handleExportBox: function(domElem)
- {
- var self = this;
- if( self.param.idSubtable )
- {
- // no view box for subtables
- return;
- }
-
- // When the (+) image is hovered, the export buttons are displayed
- $('.dataTableFooterIconsShow', domElem)
- .show()
- .hover( function() {
- $(this).fadeOut('slow');
- $('.exportToFormatIcons', $(this).parent()).show('slow');
- }, function(){}
- );
-
- //footer arrow position element name
- self.jsViewDataTable=$('.dataTableFooterWrap', domElem).attr('var');
-
- $('.tableAllColumnsSwitch a', domElem)
- .show()
- .click(
- function(){
- // we only reset the limit filter, in case switch to table view from cloud view where limit is custom set to 30
- // this value is stored in config file General->datatable_default_limit but this is more an edge case so ok to set it to 10
-
- self.setActiveIcon(this, domElem);
-
- var viewDataTable = $(this).attr('format');
- self.param.viewDataTable = viewDataTable;
-
- //self.resetAllFilters();
-
- // when switching to display simple table, do not exclude low pop by default
- delete self.param.enable_filter_excludelowpop;
- delete self.param.filter_sort_column;
- delete self.param.filter_sort_order;
- delete columns;
- self.reloadAjaxDataTable();
- self.notifyWidgetParametersChange($(this), {viewDataTable: self.param.viewDataTable});
- }
- )
-
- //handle Graph View icons
- $('.tableGraphViews a', domElem)
- .click(function(){
- var viewDataTable = $(this).attr('format');
- self.setActiveIcon(this, domElem);
-
- var filters = self.resetAllFilters();
- self.param.flat = filters.flat;
- self.param.columns = filters.columns;
-
- self.param.viewDataTable = viewDataTable;
- self.reloadAjaxDataTable();
- self.notifyWidgetParametersChange($(this), {viewDataTable: self.param.viewDataTable});
- });
-
- //Graph icon Collapsed functionality
- self.currentGraphViewIcon=0;
- self.graphViewEnabled=0;
- self.graphViewStartingThreads=0;
- self.graphViewStartingKeep=false; //show keep flag
-
- //define collapsed icons
- $('.tableGraphCollapsed a', domElem)
- .each(function(i){
- if(self.jsViewDataTable==$(this).attr('var')){
- self.currentGraphViewIcon=i;
- self.graphViewEnabled=true;
- }
- })
- .each(function(i){
- if(self.currentGraphViewIcon!=i) $(this).hide();
- });
-
- $('.tableGraphCollapsed', domElem).hover(
- function(){
- //Graph icon onmouseover
- if(self.graphViewStartingThreads>0) return self.graphViewStartingKeep=true; //exit if animation is not finished
- $(this).addClass('tableIconsGroupActive');
- $('a', this).each(function(i){
- if(self.currentGraphViewIcon!=i || self.graphViewEnabled){
- self.graphViewStartingThreads++;
- }
- if(self.currentGraphViewIcon!=i){
- //show other icons
- $(this).show('fast', function(){self.graphViewStartingThreads--});
- }
- else if (self.graphViewEnabled){
- //set footer arrow position
- $('.dataTableFooterActiveItem', domElem).animate({left:$(this).parent().position().left+i*(this.offsetWidth+1)}, "fast", function(){self.graphViewStartingThreads--});
- }
- });
- self.exportToFormatHide(domElem);
- },
- function(){
- //Graph icon onmouseout
- if(self.graphViewStartingKeep) return self.graphViewStartingKeep=false; //exit while icons animate
- $('a', this).each(function(i){
- if(self.currentGraphViewIcon!=i){
- //hide other icons
- $(this).hide('fast');
- }
- else if (self.graphViewEnabled){
- //set footer arrow position
- $('.dataTableFooterActiveItem', domElem).animate({left:$(this).parent().position().left}, "fast");
- }
- });
- $(this).removeClass('tableIconsGroupActive');
- }
- );
-
- //handle exportToFormat icons
- self.exportToFormat=null;
- $('.exportToFormatIcons a', domElem).click(function(){
- self.exportToFormat={};
- self.exportToFormat.lastActiveIcon=self.setActiveIcon(this, domElem);
- self.exportToFormat.target=$(this).parent().siblings('.exportToFormatItems').show('fast');
- self.exportToFormat.obj=$(this).hide();
- });
-
- //close exportToFormat onClickOutside
- $('body').on('mouseup',function(e){
- if(self.exportToFormat){
- self.exportToFormatHide(domElem);
- }
- });
-
-
- $('.exportToFormatItems a', domElem)
- // prevent click jacking attacks by dynamically adding the token auth when the link is clicked
- .click( function() {
- $(this).attr('href', function() {
- return $(this).attr('href') +'&token_auth='+piwik.token_auth;
- })
- })
- .attr( 'href', function(){
- var format = $(this).attr('format');
- var method = $(this).attr('methodToCall');
- var filter_limit = $(this).attr('filter_limit');
- var segment = self.param.segment;
- var label = self.param.label;
- var idGoal = self.param.idGoal;
- var param_date = self.param.date;
- var date = $(this).attr('date');
- if(typeof date != 'undefined') {
- param_date = date;
- }
- if( typeof self.param.dateUsedInGraph != 'undefined') {
+ .click(function () {
+ $('#keyword', target).val('');
+ $(':submit', target).submit();
+ });
+ $('#keyword', this).after(clearImg);
+
+ }
+ }
+ );
+ },
+
+ //behaviour for '< prev' 'next >' links and page count
+ handleOffsetInformation: function (domElem) {
+ var self = this;
+
+ $('.dataTablePages', domElem).each(
+ function () {
+ var offset = 1 + Number(self.param.filter_offset);
+ var offsetEnd = Number(self.param.filter_offset) + Number(self.param.filter_limit);
+ var totalRows = Number(self.param.totalRows);
+ offsetEndDisp = offsetEnd;
+
+ if (self.param.keep_summary_row == 1) --totalRows;
+
+ if (offsetEnd > totalRows) offsetEndDisp = totalRows;
+
+ // only show this string if there is some rows in the datatable
+ if (totalRows != 0) {
+ var str = sprintf(_pk_translate('CoreHome_PageOf_js'), offset + '-' + offsetEndDisp, totalRows);
+ $(this).text(str);
+ }
+ }
+ );
+
+ // Display the next link if the total Rows is greater than the current end row
+ $('.dataTableNext', domElem)
+ .each(function () {
+ var offsetEnd = Number(self.param.filter_offset)
+ + Number(self.param.filter_limit);
+ var totalRows = Number(self.param.totalRows);
+ if (self.param.keep_summary_row == 1) --totalRows;
+ if (offsetEnd < totalRows) {
+ $(this).css('display', 'inline');
+ }
+ })
+ // bind the click event to trigger the ajax request with the new offset
+ .click(function () {
+ $(this).off('click');
+ self.param.filter_offset = Number(self.param.filter_offset) + Number(self.param.filter_limit);
+ self.reloadAjaxDataTable();
+ })
+ ;
+
+ // Display the previous link if the current offset is not zero
+ $('.dataTablePrevious', domElem)
+ .each(function () {
+ var offset = 1 + Number(self.param.filter_offset);
+ if (offset != 1) {
+ $(this).css('display', 'inline');
+ }
+ }
+ )
+ // bind the click event to trigger the ajax request with the new offset
+ // take care of the negative offset, we setup 0
+ .click(
+ function () {
+ $(this).off('click');
+ var offset = Number(self.param.filter_offset) - Number(self.param.filter_limit);
+ if (offset < 0) { offset = 0; }
+ self.param.filter_offset = offset;
+ self.param.previous = 1;
+ self.reloadAjaxDataTable();
+ }
+ );
+ },
+
+ handleEvolutionAnnotations: function (domElem) {
+ var self = this;
+ if (self.param.viewDataTable == 'graphEvolution'
+ && $('.annotationView', domElem).length > 0) {
+ // get dates w/ annotations across evolution period (have to do it through AJAX since we
+ // determine placement using the elements created by jqplot)
+ piwik.annotations.api.getEvolutionIcons(
+ self.param.idSite,
+ self.param.date,
+ self.param.period,
+ self.param['evolution_' + self.param.period + '_last_n'],
+ function (response) {
+ var annotations = $(response),
+ datatableFeatures = $('.dataTableFeatures', domElem),
+ noteSize = 16,
+ annotationAxisHeight = 30 // css height + padding + margin
+ ;
+
+ // set position of evolution annotation icons
+ annotations.css({
+ top: -datatableFeatures.height() - annotationAxisHeight + noteSize / 2,
+ left: 6 // padding-left of .jqplot-evolution element (in graph.tpl)
+ });
+
+ piwik.annotations.placeEvolutionIcons(annotations, domElem);
+
+ // add new section under axis
+ datatableFeatures.append(annotations);
+
+ // reposition annotation icons every time the graph is resized
+ $('.piwik-graph', domElem).on('resizeGraph', function () {
+ piwik.annotations.placeEvolutionIcons(annotations, domElem);
+ });
+
+ // on hover of x-axis, show note icon over correct part of x-axis
+ $('span', annotations).hover(
+ function () { $(this).css('opacity', 1); },
+ function () {
+ if ($(this).attr('data-count') == 0) // only hide if there are no annotations for this note
+ {
+ $(this).css('opacity', 0);
+ }
+ }
+ );
+
+ // when clicking an annotation, show the annotation viewer for that period
+ $('span', annotations).click(function () {
+ var spanSelf = $(this),
+ date = spanSelf.attr('data-date'),
+ oldDate = $('.annotation-manager', domElem).attr('data-date');
+ if (date) {
+ var period = self.param.period;
+ if (period == 'range') {
+ period = 'day';
+ }
+
+ piwik.annotations.showAnnotationViewer(
+ domElem,
+ self.param.idSite,
+ date,
+ period,
+ undefined, // lastN
+ function (manager) {
+ manager.attr('data-is-range', 0);
+ $('.annotationView img', domElem)
+ .attr('title', _pk_translate('Annotations_IconDesc_js'));
+
+ var viewAndAdd = _pk_translate('Annotations_ViewAndAddAnnotations_js'),
+ hideNotes = _pk_translate('Annotations_HideAnnotationsFor_js');
+
+ // change the tooltip of the previously clicked evolution icon (if any)
+ if (oldDate) {
+ $('span', annotations).each(function () {
+ if ($(this).attr('data-date') == oldDate) {
+ $(this).attr('title', viewAndAdd.replace("%s", oldDate));
+ return false;
+ }
+ });
+ }
+
+ // change the tooltip of the clicked evolution icon
+ if (manager.is(':hidden')) {
+ spanSelf.attr('title', viewAndAdd.replace("%s", date));
+ }
+ else {
+ spanSelf.attr('title', hideNotes.replace("%s", date));
+ }
+ }
+ );
+ }
+ });
+
+ // when hover over annotation in annotation manager, highlight the annotation
+ // icon
+ var runningAnimation = null;
+ domElem.on('mouseenter', '.annotation', function (e) {
+ var date = $(this).attr('data-date');
+
+ // find the icon for this annotation
+ var icon = $();
+ $('span', annotations).each(function () {
+ if ($(this).attr('data-date') == date) {
+ icon = $('img', this);
+ return false;
+ }
+ });
+
+ if (icon[0] == runningAnimation) // if the animation is already running, do nothing
+ {
+ return;
+ }
+
+ // stop ongoing animations
+ $('span', annotations).each(function () {
+ $('img', this).removeAttr('style');
+ });
+
+ // start a bounce animation
+ icon.effect("bounce", {times: 1, distance: 10}, 1000);
+ runningAnimation = icon[0];
+ });
+
+ // reset running animation item when leaving annotations list
+ domElem.on('mouseleave', '.annotations', function (e) {
+ runningAnimation = null;
+ });
+ }
+ );
+ }
+ },
+
+ handleAnnotationsButton: function (domElem) {
+ var self = this;
+ if (self.param.idSubtable) // no annotations for subtables, just whole reports
+ {
+ return;
+ }
+
+ // show the annotations view on click
+ $('.annotationView', domElem).click(function () {
+ var annotationManager = $('.annotation-manager', domElem);
+
+ if (annotationManager.length > 0
+ && annotationManager.attr('data-is-range') == 1) {
+ if (annotationManager.is(':hidden')) {
+ annotationManager.slideDown('slow'); // showing
+ $('img', this).attr('title', _pk_translate('Annotations_IconDescHideNotes_js'));
+ }
+ else {
+ annotationManager.slideUp('slow'); // hiding
+ $('img', this).attr('title', _pk_translate('Annotations_IconDesc_js'));
+ }
+ }
+ else {
+ // show the annotation viewer for the whole date range
+ var lastN = self.param['evolution_' + self.param.period + '_last_n'];
+ piwik.annotations.showAnnotationViewer(
+ domElem,
+ self.param.idSite,
+ self.param.date,
+ self.param.period,
+ lastN,
+ function (manager) {
+ manager.attr('data-is-range', 1);
+ }
+ );
+
+ // change the tooltip of the view annotation icon
+ $('img', this).attr('title', _pk_translate('Annotations_IconDescHideNotes_js'));
+ }
+ });
+ },
+
+ // DataTable view box (simple table, all columns table, Goals table, pie graph, tag cloud, graph, ...)
+ handleExportBox: function (domElem) {
+ var self = this;
+ if (self.param.idSubtable) {
+ // no view box for subtables
+ return;
+ }
+
+ // When the (+) image is hovered, the export buttons are displayed
+ $('.dataTableFooterIconsShow', domElem)
+ .show()
+ .hover(function () {
+ $(this).fadeOut('slow');
+ $('.exportToFormatIcons', $(this).parent()).show('slow');
+ }, function () {}
+ );
+
+ //footer arrow position element name
+ self.jsViewDataTable = $('.dataTableFooterWrap', domElem).attr('var');
+
+ $('.tableAllColumnsSwitch a', domElem)
+ .show()
+ .click(
+ function () {
+ // we only reset the limit filter, in case switch to table view from cloud view where limit is custom set to 30
+ // this value is stored in config file General->datatable_default_limit but this is more an edge case so ok to set it to 10
+
+ self.setActiveIcon(this, domElem);
+
+ var viewDataTable = $(this).attr('format');
+ self.param.viewDataTable = viewDataTable;
+
+ //self.resetAllFilters();
+
+ // when switching to display simple table, do not exclude low pop by default
+ delete self.param.enable_filter_excludelowpop;
+ delete self.param.filter_sort_column;
+ delete self.param.filter_sort_order;
+ delete columns;
+ self.reloadAjaxDataTable();
+ self.notifyWidgetParametersChange($(this), {viewDataTable: self.param.viewDataTable});
+ }
+ )
+
+ //handle Graph View icons
+ $('.tableGraphViews a', domElem)
+ .click(function () {
+ var viewDataTable = $(this).attr('format');
+ self.setActiveIcon(this, domElem);
+
+ var filters = self.resetAllFilters();
+ self.param.flat = filters.flat;
+ self.param.columns = filters.columns;
+
+ self.param.viewDataTable = viewDataTable;
+ self.reloadAjaxDataTable();
+ self.notifyWidgetParametersChange($(this), {viewDataTable: self.param.viewDataTable});
+ });
+
+ //Graph icon Collapsed functionality
+ self.currentGraphViewIcon = 0;
+ self.graphViewEnabled = 0;
+ self.graphViewStartingThreads = 0;
+ self.graphViewStartingKeep = false; //show keep flag
+
+ //define collapsed icons
+ $('.tableGraphCollapsed a', domElem)
+ .each(function (i) {
+ if (self.jsViewDataTable == $(this).attr('var')) {
+ self.currentGraphViewIcon = i;
+ self.graphViewEnabled = true;
+ }
+ })
+ .each(function (i) {
+ if (self.currentGraphViewIcon != i) $(this).hide();
+ });
+
+ $('.tableGraphCollapsed', domElem).hover(
+ function () {
+ //Graph icon onmouseover
+ if (self.graphViewStartingThreads > 0) return self.graphViewStartingKeep = true; //exit if animation is not finished
+ $(this).addClass('tableIconsGroupActive');
+ $('a', this).each(function (i) {
+ if (self.currentGraphViewIcon != i || self.graphViewEnabled) {
+ self.graphViewStartingThreads++;
+ }
+ if (self.currentGraphViewIcon != i) {
+ //show other icons
+ $(this).show('fast', function () {self.graphViewStartingThreads--});
+ }
+ else if (self.graphViewEnabled) {
+ //set footer arrow position
+ $('.dataTableFooterActiveItem', domElem).animate({left: $(this).parent().position().left + i * (this.offsetWidth + 1)}, "fast", function () {self.graphViewStartingThreads--});
+ }
+ });
+ self.exportToFormatHide(domElem);
+ },
+ function () {
+ //Graph icon onmouseout
+ if (self.graphViewStartingKeep) return self.graphViewStartingKeep = false; //exit while icons animate
+ $('a', this).each(function (i) {
+ if (self.currentGraphViewIcon != i) {
+ //hide other icons
+ $(this).hide('fast');
+ }
+ else if (self.graphViewEnabled) {
+ //set footer arrow position
+ $('.dataTableFooterActiveItem', domElem).animate({left: $(this).parent().position().left}, "fast");
+ }
+ });
+ $(this).removeClass('tableIconsGroupActive');
+ }
+ );
+
+ //handle exportToFormat icons
+ self.exportToFormat = null;
+ $('.exportToFormatIcons a', domElem).click(function () {
+ self.exportToFormat = {};
+ self.exportToFormat.lastActiveIcon = self.setActiveIcon(this, domElem);
+ self.exportToFormat.target = $(this).parent().siblings('.exportToFormatItems').show('fast');
+ self.exportToFormat.obj = $(this).hide();
+ });
+
+ //close exportToFormat onClickOutside
+ $('body').on('mouseup', function (e) {
+ if (self.exportToFormat) {
+ self.exportToFormatHide(domElem);
+ }
+ });
+
+
+ $('.exportToFormatItems a', domElem)
+ // prevent click jacking attacks by dynamically adding the token auth when the link is clicked
+ .click(function () {
+ $(this).attr('href', function () {
+ return $(this).attr('href') + '&token_auth=' + piwik.token_auth;
+ })
+ })
+ .attr('href', function () {
+ var format = $(this).attr('format');
+ var method = $(this).attr('methodToCall');
+ var filter_limit = $(this).attr('filter_limit');
+ var segment = self.param.segment;
+ var label = self.param.label;
+ var idGoal = self.param.idGoal;
+ var param_date = self.param.date;
+ var date = $(this).attr('date');
+ if (typeof date != 'undefined') {
+ param_date = date;
+ }
+ if (typeof self.param.dateUsedInGraph != 'undefined') {
param_date = self.param.dateUsedInGraph;
}
- var period = self.param.period;
-
- // RSS does not work for period=range
- if(format == 'RSS'
- && self.param.period == 'range') {
- period = 'day';
- }
- var str = 'index.php?module=API'
- +'&method='+method
- +'&format='+format
- +'&idSite='+self.param.idSite
- +'&period='+period
- +'&date='+ param_date
- + ( typeof self.param.filter_pattern != "undefined" ? '&filter_pattern=' + self.param.filter_pattern : '')
- + ( typeof self.param.filter_pattern_recursive != "undefined" ? '&filter_pattern_recursive=' + self.param.filter_pattern_recursive : '');
-
- if (typeof self.param.flat != "undefined") {
- str += '&flat=' + (self.param.flat == 0 ? '0' : '1');
- if (typeof self.param.include_aggregate_rows != "undefined" && self.param.include_aggregate_rows) {
- str += '&include_aggregate_rows=1';
- }
- } else {
- str += '&expanded=1';
- }
- if (format == 'CSV' || format == 'TSV' || format == 'RSS') {
- str += '&translateColumnNames=1&language='+piwik.language;
- }
- if(typeof segment != 'undefined') {
- str += '&segment='+segment;
- }
- // Export Goals specific reports
- if(typeof idGoal != 'undefined'
- && idGoal != '-1') {
- str += '&idGoal='+idGoal;
- }
- if(filter_limit)
- {
- str += '&filter_limit='+filter_limit;
- }
- if(label)
- {
- str += '&label='+encodeURIComponent(label);
- }
- return str;
- }
- );
-
- // Initialize arrow footer to correct icon
- $('.dataTableFooterWrap a.tableIcon', domElem).each(function(){
- if(self.jsViewDataTable==$(this).attr('var')) self.setActiveIcon(this, domElem);
- });
-
- },
-
- exportToFormatHide: function(domElem, noAnimation)
- {
- var self=this;
- if(self.exportToFormat){
- self.setActiveIcon(self.exportToFormat.lastActiveIcon, domElem);
- var animationSpeed = noAnimation ? 0 : 'fast';
- self.exportToFormat.target.hide(animationSpeed);
- self.exportToFormat.obj.show(animationSpeed);
- self.exportToFormat=null;
- }
- },
-
- handleConfigurationBox: function(domElem, callbackSuccess)
- {
- var self = this;
-
- if (typeof self.parentId != "undefined" && self.parentId != '')
- {
- // no manipulation when loading subtables
- return;
- }
-
- if ((typeof self.numberOfSubtables == 'undefined' || self.numberOfSubtables == 0)
- && (typeof self.param.flat == 'undefined' || self.param.flat != 1))
- {
- // if there are no subtables, remove the flatten action
- $('.dataTableFlatten', domElem).parent().remove();
- }
-
- var ul = $('div.tableConfiguration ul', domElem);
-
- function hideConfigurationIcon()
- {
- // hide the icon when there are no actions available or we're not in a table view
- $('div.tableConfiguration', domElem).remove();
- }
-
- if (ul.find('li').size() == 0)
- {
- hideConfigurationIcon();
- return;
- }
-
- var icon = $('a.tableConfigurationIcon', domElem);
- icon.click(function() { return false; });
- var iconHighlighted = false;
-
- ul.find('li:first').addClass('first');
- ul.find('li:last').addClass('last');
- ul.prepend('<li class="firstDummy"></li>');
-
- // open and close the box
- var open = function() {
- self.exportToFormatHide(domElem, true);
- ul.addClass('open');
- icon.css('opacity', 1);
- };
- var close = function() {
- ul.removeClass('open');
- icon.css('opacity', icon.hasClass('highlighted') ? .85 : .6);
- };
- $('div.tableConfiguration', domElem).hover(open, close);
-
- var generateClickCallback = function(paramName, callbackAfterToggle)
- {
- return function()
- {
- close();
- self.param[paramName] = 1 - self.param[paramName];
- self.param.filter_offset = 0;
- if (callbackAfterToggle) callbackAfterToggle();
- self.reloadAjaxDataTable(true, callbackSuccess);
+ var period = self.param.period;
+
+ // RSS does not work for period=range
+ if (format == 'RSS'
+ && self.param.period == 'range') {
+ period = 'day';
+ }
+ var str = 'index.php?module=API'
+ + '&method=' + method
+ + '&format=' + format
+ + '&idSite=' + self.param.idSite
+ + '&period=' + period
+ + '&date=' + param_date
+ + ( typeof self.param.filter_pattern != "undefined" ? '&filter_pattern=' + self.param.filter_pattern : '')
+ + ( typeof self.param.filter_pattern_recursive != "undefined" ? '&filter_pattern_recursive=' + self.param.filter_pattern_recursive : '');
+
+ if (typeof self.param.flat != "undefined") {
+ str += '&flat=' + (self.param.flat == 0 ? '0' : '1');
+ if (typeof self.param.include_aggregate_rows != "undefined" && self.param.include_aggregate_rows) {
+ str += '&include_aggregate_rows=1';
+ }
+ } else {
+ str += '&expanded=1';
+ }
+ if (format == 'CSV' || format == 'TSV' || format == 'RSS') {
+ str += '&translateColumnNames=1&language=' + piwik.language;
+ }
+ if (typeof segment != 'undefined') {
+ str += '&segment=' + segment;
+ }
+ // Export Goals specific reports
+ if (typeof idGoal != 'undefined'
+ && idGoal != '-1') {
+ str += '&idGoal=' + idGoal;
+ }
+ if (filter_limit) {
+ str += '&filter_limit=' + filter_limit;
+ }
+ if (label) {
+ str += '&label=' + encodeURIComponent(label);
+ }
+ return str;
+ }
+ );
+
+ // Initialize arrow footer to correct icon
+ $('.dataTableFooterWrap a.tableIcon', domElem).each(function () {
+ if (self.jsViewDataTable == $(this).attr('var')) self.setActiveIcon(this, domElem);
+ });
+
+ },
+
+ exportToFormatHide: function (domElem, noAnimation) {
+ var self = this;
+ if (self.exportToFormat) {
+ self.setActiveIcon(self.exportToFormat.lastActiveIcon, domElem);
+ var animationSpeed = noAnimation ? 0 : 'fast';
+ self.exportToFormat.target.hide(animationSpeed);
+ self.exportToFormat.obj.show(animationSpeed);
+ self.exportToFormat = null;
+ }
+ },
+
+ handleConfigurationBox: function (domElem, callbackSuccess) {
+ var self = this;
+
+ if (typeof self.parentId != "undefined" && self.parentId != '') {
+ // no manipulation when loading subtables
+ return;
+ }
+
+ if ((typeof self.numberOfSubtables == 'undefined' || self.numberOfSubtables == 0)
+ && (typeof self.param.flat == 'undefined' || self.param.flat != 1)) {
+ // if there are no subtables, remove the flatten action
+ $('.dataTableFlatten', domElem).parent().remove();
+ }
+
+ var ul = $('div.tableConfiguration ul', domElem);
+
+ function hideConfigurationIcon() {
+ // hide the icon when there are no actions available or we're not in a table view
+ $('div.tableConfiguration', domElem).remove();
+ }
+
+ if (ul.find('li').size() == 0) {
+ hideConfigurationIcon();
+ return;
+ }
+
+ var icon = $('a.tableConfigurationIcon', domElem);
+ icon.click(function () { return false; });
+ var iconHighlighted = false;
+
+ ul.find('li:first').addClass('first');
+ ul.find('li:last').addClass('last');
+ ul.prepend('<li class="firstDummy"></li>');
+
+ // open and close the box
+ var open = function () {
+ self.exportToFormatHide(domElem, true);
+ ul.addClass('open');
+ icon.css('opacity', 1);
+ };
+ var close = function () {
+ ul.removeClass('open');
+ icon.css('opacity', icon.hasClass('highlighted') ? .85 : .6);
+ };
+ $('div.tableConfiguration', domElem).hover(open, close);
+
+ var generateClickCallback = function (paramName, callbackAfterToggle) {
+ return function () {
+ close();
+ self.param[paramName] = 1 - self.param[paramName];
+ self.param.filter_offset = 0;
+ if (callbackAfterToggle) callbackAfterToggle();
+ self.reloadAjaxDataTable(true, callbackSuccess);
var data = {};
- data[paramName] = self.param[paramName];
+ data[paramName] = self.param[paramName];
self.notifyWidgetParametersChange(domElem, data);
- };
- };
-
- var getText = function(text, addDefault)
- {
- text = _pk_translate(text);
- if (text.indexOf('%s') > 0)
- {
- text = text.replace('%s', '<br /><span class="action">&raquo; ');
- if (addDefault) text += ' (' + _pk_translate('CoreHome_Default_js') + ')';
- text += '</span>';
- }
- return text;
- };
-
- var setText = function(el, paramName, textA, textB)
- {
- if (typeof self.param[paramName] != 'undefined' && self.param[paramName] == 1)
- {
- $(el).html(getText(textA, true));
- iconHighlighted = true;
- }
- else
- {
- self.param[paramName] = 0;
- $(el).html(getText(textB));
- }
- };
-
- // handle low population
- $('.dataTableExcludeLowPopulation', domElem)
- .each(function()
- {
- // Set the text, either "Exclude low pop" or "Include all"
- if(typeof self.param.enable_filter_excludelowpop == 'undefined')
- {
- self.param.enable_filter_excludelowpop = 0;
- }
- if(Number(self.param.enable_filter_excludelowpop) != 0)
- {
- string = getText('CoreHome_IncludeRowsWithLowPopulation_js', true);
- self.param.enable_filter_excludelowpop = 1;
- iconHighlighted = true;
- }
- else
- {
- string = getText('CoreHome_ExcludeRowsWithLowPopulation_js');
- self.param.enable_filter_excludelowpop = 0;
- }
- $(this).html(string);
- })
- .click( generateClickCallback('enable_filter_excludelowpop') );
-
- // handle flatten
- $('.dataTableFlatten', domElem)
- .each( function() {
- setText(this, 'flat', 'CoreHome_UnFlattenDataTable_js', 'CoreHome_FlattenDataTable_js');
- })
- .click( generateClickCallback('flat') );
-
- $('.dataTableIncludeAggregateRows', domElem)
- .each( function() {
- setText(this, 'include_aggregate_rows', 'CoreHome_DataTableExcludeAggregateRows_js',
- 'CoreHome_DataTableIncludeAggregateRows_js');
- })
- .click( generateClickCallback('include_aggregate_rows', function() {
- if (self.param.include_aggregate_rows == 1)
- {
- // when including aggregate rows is enabled, we remove the sorting
- // this way, the aggregate rows appear directly before their children
- self.param.filter_sort_column = '';
+ };
+ };
+
+ var getText = function (text, addDefault) {
+ text = _pk_translate(text);
+ if (text.indexOf('%s') > 0) {
+ text = text.replace('%s', '<br /><span class="action">&raquo; ');
+ if (addDefault) text += ' (' + _pk_translate('CoreHome_Default_js') + ')';
+ text += '</span>';
+ }
+ return text;
+ };
+
+ var setText = function (el, paramName, textA, textB) {
+ if (typeof self.param[paramName] != 'undefined' && self.param[paramName] == 1) {
+ $(el).html(getText(textA, true));
+ iconHighlighted = true;
+ }
+ else {
+ self.param[paramName] = 0;
+ $(el).html(getText(textB));
+ }
+ };
+
+ // handle low population
+ $('.dataTableExcludeLowPopulation', domElem)
+ .each(function () {
+ // Set the text, either "Exclude low pop" or "Include all"
+ if (typeof self.param.enable_filter_excludelowpop == 'undefined') {
+ self.param.enable_filter_excludelowpop = 0;
+ }
+ if (Number(self.param.enable_filter_excludelowpop) != 0) {
+ string = getText('CoreHome_IncludeRowsWithLowPopulation_js', true);
+ self.param.enable_filter_excludelowpop = 1;
+ iconHighlighted = true;
+ }
+ else {
+ string = getText('CoreHome_ExcludeRowsWithLowPopulation_js');
+ self.param.enable_filter_excludelowpop = 0;
+ }
+ $(this).html(string);
+ })
+ .click(generateClickCallback('enable_filter_excludelowpop'));
+
+ // handle flatten
+ $('.dataTableFlatten', domElem)
+ .each(function () {
+ setText(this, 'flat', 'CoreHome_UnFlattenDataTable_js', 'CoreHome_FlattenDataTable_js');
+ })
+ .click(generateClickCallback('flat'));
+
+ $('.dataTableIncludeAggregateRows', domElem)
+ .each(function () {
+ setText(this, 'include_aggregate_rows', 'CoreHome_DataTableExcludeAggregateRows_js',
+ 'CoreHome_DataTableIncludeAggregateRows_js');
+ })
+ .click(generateClickCallback('include_aggregate_rows', function () {
+ if (self.param.include_aggregate_rows == 1) {
+ // when including aggregate rows is enabled, we remove the sorting
+ // this way, the aggregate rows appear directly before their children
+ self.param.filter_sort_column = '';
self.notifyWidgetParametersChange(domElem, {filter_sort_column: ''});
- }
- }));
-
- // handle highlighted icon
- if (iconHighlighted)
- {
- icon.addClass('highlighted');
- }
- close();
-
- if( !iconHighlighted
- && !(self.param.viewDataTable == 'table'
- || self.param.viewDataTable == 'tableAllColumns'
- || self.param.viewDataTable == 'tableGoals'))
- {
- hideConfigurationIcon();
- return;
- }
-
- // fix a css bug of ie7
- if (document.all && !window.opera && window.XMLHttpRequest)
- {
- window.setTimeout(function() {
- open();
- var width = 0;
- ul.find('li').each(function() {
- width = Math.max(width, $(this).width());
- }).width(width);
- close();
- }, 400);
- }
- },
-
- //footer arrow position handler
- setActiveIcon: function(obj, domElem)
- {
- if(!obj) return false;
-
- var lastActiveIcon=this.lastActiveIcon;
-
- if(lastActiveIcon){
- $(lastActiveIcon).removeClass("activeIcon");
- }
-
- $(obj).addClass("activeIcon");
- this.lastActiveIcon=obj;
-
- var target=$('.dataTableFooterActiveItem', domElem);
-
- //set arrow position with delay (for ajax widget loading)
- setTimeout(function(){
- target.css({left:$(obj).position().left});
- },100);
-
- return lastActiveIcon;
-
- },
-
- // Tell parent widget that the parameters of this table was updated,
- notifyWidgetParametersChange: function(domWidget, parameters)
- {
+ }
+ }));
+
+ // handle highlighted icon
+ if (iconHighlighted) {
+ icon.addClass('highlighted');
+ }
+ close();
+
+ if (!iconHighlighted
+ && !(self.param.viewDataTable == 'table'
+ || self.param.viewDataTable == 'tableAllColumns'
+ || self.param.viewDataTable == 'tableGoals')) {
+ hideConfigurationIcon();
+ return;
+ }
+
+ // fix a css bug of ie7
+ if (document.all && !window.opera && window.XMLHttpRequest) {
+ window.setTimeout(function () {
+ open();
+ var width = 0;
+ ul.find('li').each(function () {
+ width = Math.max(width, $(this).width());
+ }).width(width);
+ close();
+ }, 400);
+ }
+ },
+
+ //footer arrow position handler
+ setActiveIcon: function (obj, domElem) {
+ if (!obj) return false;
+
+ var lastActiveIcon = this.lastActiveIcon;
+
+ if (lastActiveIcon) {
+ $(lastActiveIcon).removeClass("activeIcon");
+ }
+
+ $(obj).addClass("activeIcon");
+ this.lastActiveIcon = obj;
+
+ var target = $('.dataTableFooterActiveItem', domElem);
+
+ //set arrow position with delay (for ajax widget loading)
+ setTimeout(function () {
+ target.css({left: $(obj).position().left});
+ }, 100);
+
+ return lastActiveIcon;
+
+ },
+
+ // Tell parent widget that the parameters of this table was updated,
+ notifyWidgetParametersChange: function (domWidget, parameters) {
var widget = $(domWidget).parents('[widgetId]');
// trigger setParameters event on base element
widget.trigger('setParameters', parameters);
- },
-
- truncate: function(domElemToTruncate, truncationOffset)
- {
- var self = this;
-
- domElemToTruncate = $(domElemToTruncate);
-
- if (typeof domElemToTruncate.data('originalText') != 'undefined')
- {
- // truncate only once. otherwise, the tooltip will show the truncated text as well.
- return;
- }
-
- // make the original text (before truncation) available for others.
- // the .truncate plugins adds a title to the dom element but the .tooltip
- // plugin removes that again.
- domElemToTruncate.data('originalText', domElemToTruncate.text());
-
- if (typeof truncationOffset == 'undefined')
- {
- truncationOffset = 0;
- }
- var truncationLimit = 50;
-
- if (typeof self.param.idSubtable == 'undefined'
- && self.param.viewDataTable == 'tableAllColumns')
- {
- // when showing all columns in a subtable, space is restricted
- truncationLimit = 25;
- }
-
- truncationLimit += truncationOffset;
- domElemToTruncate.truncate(truncationLimit);
-
- var tooltipElem = $('.truncated', domElemToTruncate),
- customToolTipText = domElemToTruncate.attr('title');
-
- // if there's a title on the dom element, use this as the tooltip instead of
- // the one set by the truncate plugin
- if (customToolTipText)
- {
- // make sure browser doesn't add its own tooltip for the truncated element
- if (tooltipElem[0])
- {
- tooltipElem.removeAttr('title');
- }
-
- tooltipElem = domElemToTruncate;
- tooltipElem.attr('title', customToolTipText);
- }
-
- // use tooltip (tooltip text determined by the 'title' attribute)
- tooltipElem.tooltip();
- },
-
- //Apply some miscelleaneous style to the DataTable
- applyCosmetics: function(domElem)
- {
- var self = this;
-
- // Add some styles on the cells even/odd
- // label (first column of a data row) or not
- $("th:first-child", domElem).addClass('label');
- $("td:first-child:odd", domElem).addClass('label labeleven');
- $("td:first-child:even", domElem).addClass('label labelodd');
- $("tr:odd td", domElem).slice(1).addClass('columnodd');
- $("tr:even td", domElem).slice(1).addClass('columneven');
-
- $('td span.label', domElem).each(function(){ self.truncate($(this)); } );
-
- },
-
- //behaviour for 'nested DataTable' (DataTable loaded on a click on a row)
- handleSubDataTable: function(domElem)
- {
- var self = this;
- // When the TR has a subDataTable class it means that this row has a link to a subDataTable
- this.numberOfSubtables = $('tr.subDataTable', domElem)
- .click(
- function()
- {
- // get the idSubTable
- var idSubTable = $(this).attr('id');
- var divIdToReplaceWithSubTable = 'subDataTable_'+idSubTable;
-
- // if the subDataTable is not already loaded
- if (typeof self.loadedSubDataTable[divIdToReplaceWithSubTable] == "undefined")
- {
- var numberOfColumns = $(this).children().length;
-
- // at the end of the query it will replace the ID matching the new HTML table #ID
- // we need to create this ID first
- $(this).after(
- '<tr>'+
- '<td colspan="'+numberOfColumns+'" class="cellSubDataTable">'+
- '<div id="'+divIdToReplaceWithSubTable+'">'+
- '<span class="loadingPiwik" style="display:inline"><img src="themes/default/images/loading-blue.gif" />'+ _pk_translate('General_Loading_js') +'</span>'+
- '</div>'+
- '</td>'+
- '</tr>'
- );
-
- var savedActionVariable = self.param.action;
-
- // reset all the filters from the Parent table
- var filtersToRestore = self.resetAllFilters();
- // do not ignore the exclude low population click
- self.param.enable_filter_excludelowpop = filtersToRestore.enable_filter_excludelowpop;
-
- self.param.idSubtable = idSubTable;
- self.param.action = self.param.controllerActionCalledWhenRequestSubTable;
- self.reloadAjaxDataTable(false);
-
- self.param.action = savedActionVariable;
- delete self.param.idSubtable;
- self.restoreAllFilters(filtersToRestore);
-
- self.loadedSubDataTable[divIdToReplaceWithSubTable] = true;
-
- $(this).next().toggle();
-
- // when "loading..." is displayed, hide actions
- // repositioning after loading is not easily possible
- $(this).find('div.dataTableRowActions').hide();
- }
-
- $(this).next().toggle();
- self.repositionRowActions($(this));
- }
- ).size();
- },
-
- // tooltip for column documentation
- handleColumnDocumentation: function(domElem)
- {
- if ($('#dashboard').size() > 0) {
- // don't display column documentation in dashboard
- // it causes trouble in full screen view
- return;
- }
-
- var self = this;
-
- $('th:has(.columnDocumentation)', domElem).each(function()
- {
- var th = $(this);
- var tooltip = th.find('.columnDocumentation');
-
- tooltip.next().hover(function()
- {
- var left = (-1 * tooltip.outerWidth() / 2) + th.width() / 2;
- var top = -1 * (tooltip.outerHeight() + 10);
-
- if (th.next().size() == 0)
- {
- left = (-1 * tooltip.outerWidth()) + th.width() +
- parseInt(th.css('padding-right'), 10);
- }
-
- tooltip.css({
- marginLeft: left,
- marginTop: top
- });
-
- tooltip.stop(true, true).fadeIn(250);
- },
- function()
- {
- $(this).prev().stop(true, true).fadeOut(400);
- });
- });
- },
-
- // documentation for report
- handleReportDocumentation: function(domElem)
- {
- // don't display report documentation in dashboard
- if ($('#dashboard').size() > 0
- // or in Widgetize screen
- || $('.widgetContent').size() > 0
- // or in Widget export
- || $('.widget').size() > 0
- ) {
- return;
- }
- domElem = $(domElem);
- var doc = domElem.find('.reportDocumentation');
-
- var h2 = this._findReportHeader(domElem);
- if (doc.size() == 0 || doc.children().size() == 0) // if we can't find the element, or the element is empty
- {
- if (h2 && h2.size() > 0)
- {
- h2.find('a.reportDocumentationIcon').addClass('hidden');
- }
- return;
- }
-
- var icon = $('<a href="#"></a>');
- var docShown = false;
-
- icon.click(function()
- {
- if (docShown)
- {
- doc.stop(true, true).fadeOut(250);
- }
- else
- {
- var widthOrientation = domElem.find('table, canvas, object').eq(0);
- if (widthOrientation.size() > 0)
- {
- var width = Math.min(widthOrientation.width(), doc.parent().innerWidth());
- doc.css('width', (width - 2) + 'px');
- }
- doc.stop(true, true).fadeIn(250);
- }
- docShown = !docShown;
- return false;
- });
-
- icon.addClass('reportDocumentationIcon');
- if (h2 && h2.size() > 0)
- {
- // handle previously added icon
- var existingIcon = h2.find('a.reportDocumentationIcon');
- if (existingIcon.size() > 0)
- {
- existingIcon.replaceWith(icon);
- }
- else
- {
- // add icon
- h2.append('&nbsp;&nbsp;&nbsp;');
- h2.append(icon);
-
- h2.hover(function()
- {
- $(this).find('a.reportDocumentationIcon').show();
- },
- function()
- {
- $(this).find('a.reportDocumentationIcon').hide();
- })
- .click(
- function()
- {
- $(this).find('a.reportDocumentationIcon').click();
- })
- .css('cursor', 'pointer');
- }
- }
- else
- {
- //domElem.prepend(icon);
- }
- },
-
- handleRowActions: function(domElem)
- {
- this.doHandleRowActions(domElem.find('table > tbody > tr'));
- },
-
- handleRelatedReports: function(domElem)
- {
- var self = this,
- hideShowRelatedReports = function(thisReport)
- {
- $('span', $(thisReport).parent().parent()).each(function () {
- if (thisReport == this)
- $(this).hide();
- else
- $(this).show();
- });
- },
- // 'this' report must be hidden in datatable output
- thisReport = $('.datatableRelatedReports span:hidden', domElem)[0];
-
- hideShowRelatedReports(thisReport);
- $('.datatableRelatedReports span', domElem).each(function() {
- var clicked = this;
- $(this).unbind('click').click(function(e) {
- var url = $(this).attr('href');
-
- // if this url is also the url of a menu item, better to click that menu item instead of
- // doing AJAX request
- var menuItem = null;
- $("#root>ul.nav a").each(function () {
- if ($(this).attr('name') == url)
- {
- menuItem = this;
- return false
- }
- });
-
- if (menuItem)
- {
- $(menuItem).click();
- return;
- }
-
- // modify parameters
- self.resetAllFilters();
- var newParams = broadcast.getValuesFromUrl(url);
- for (var key in newParams)
- {
- self.param[key] = decodeURIComponent(newParams[key]);
- }
-
- // do ajax request
- self.reloadAjaxDataTable(true, function(newReport) {
- var newDomElem = self.dataTableLoaded(newReport, self.workingDivId);
- hideShowRelatedReports(clicked);
-
- // update header, if we can find it
- var h2 = self._findReportHeader(newDomElem);
- if (h2)
- h2.text($(clicked).text());
- });
- });
- });
- },
-
- /**
- * Handle events that other code triggers on this table.
- *
- * You can trigger one of these events to get the datatable to do things,
- * such as reload its data.
- *
- * Events handled:
- * - reload: Triggering 'reload' on a datatable DOM element will
- * reload the datatable's data. You can pass in an object mapping
- * parameters to set before reloading data.
- *
- * $(datatableDomElem).trigger('reload', {columns: 'nb_visits,nb_actions', idSite: 2});
- */
- handleTriggeredEvents: function(domElem)
- {
- var self = this;
-
- // reload datatable w/ new params if desired (NOTE: must use 'bind', not 'on')
- $(domElem).bind('reload', function(e, paramOverride) {
- paramOverride = paramOverride || {};
- for (var name in paramOverride)
- {
- self.param[name] = paramOverride[name];
- };
-
- self.reloadAjaxDataTable(true);
- });
- },
-
- // also used in action data table
- doHandleRowActions: function(trs)
- {
- var self = this;
-
- var availableActionsForReport = DataTable_RowActions_Registry
- .getAvailableActionsForReport(self.param);
-
- if (availableActionsForReport.length == 0)
- {
- return;
- }
-
- var actionInstances = {};
- for (var i = 0; i < availableActionsForReport.length; i++)
- {
- var action = availableActionsForReport[i];
- actionInstances[action.name] = action.createInstance(self);
- }
-
- trs.each(function()
- {
- var tr = $(this);
- var td = tr.find('td:first');
-
- // call initTr on all actions that are available for the report
- for (var i = 0; i < availableActionsForReport.length; i++)
- {
- var action = availableActionsForReport[i];
- actionInstances[action.name].initTr(tr);
- }
-
- // if there are row actions, make sure the first column is not too narrow
- td.css('minWidth', '145px');
-
- // show actions that are available for the row on hover
- var actionsDom = null;
- tr.hover(function()
- {
- if (actionsDom === null)
- {
- // create dom nodes on the fly
- actionsDom = self.createRowActions(availableActionsForReport, tr, actionInstances);
- td.prepend(actionsDom);
- }
- // reposition and show the actions
- self.repositionRowActions(tr);
- actionsDom.show();
- },
- function()
- {
- if (actionsDom !== null)
- {
- actionsDom.hide();
- }
- });
- });
- },
-
- createRowActions: function(availableActionsForReport, tr, actionInstances)
- {
- var container = $(document.createElement('div')).addClass('dataTableRowActions');
-
- for (var i = availableActionsForReport.length - 1; i >= 0; i--)
- {
- var action = availableActionsForReport[i];
-
- if (!action.isAvailableOnRow(this.param, tr)) {
- continue;
- }
-
- var actionEl = $(document.createElement('a')).attr({href: '#'}).addClass('action' + action.name);
- actionEl.append($(document.createElement('img')).attr({src: action.dataTableIcon}));
- container.append(actionEl);
-
- if (i == availableActionsForReport.length - 1) {
- actionEl.addClass('leftmost');
- }
- if (i == 0) {
- actionEl.addClass('rightmost');
- }
-
- actionEl.click((function(action, el)
- {
- return function(e)
- {
- $(this).blur();
- container.hide();
- Piwik_Tooltip.hide();
- if (typeof actionInstances[action.name].onClick == 'function') {
- return actionInstances[action.name].onClick(el, tr, e);
- }
- actionInstances[action.name].trigger(tr, e);
- return false;
- }
- })(action, actionEl));
-
- if (typeof action.dataTableIconHover != 'undefined')
- {
- actionEl.append($(document.createElement('img')).attr({src: action.dataTableIconHover}).hide());
-
- actionEl.hover(function()
- {
- var img = $(this).find('img');
- img.eq(0).hide();
- img.eq(1).show();
- },
- function()
- {
- var img = $(this).find('img');
- img.eq(1).hide();
- img.eq(0).show();
- });
- }
-
- if (typeof action.dataTableIconTooltip != 'undefined')
- {
- actionEl.hover((function(action)
- {
- return function() {
- Piwik_Tooltip.showWithTitle(
- action.dataTableIconTooltip[0],
- action.dataTableIconTooltip[1],
- 'rowActionTooltip');
- };
- })(action), function()
- {
- Piwik_Tooltip.hide();
- });
- }
- }
-
- return container;
- },
-
- repositionRowActions: function(tr) {
- var td = tr.find('td:first');
- var actions = tr.find('div.dataTableRowActions');
- actions.height(tr.innerHeight() - 2);
- actions.css('marginLeft', (td.width() + 5 - actions.outerWidth()) + 'px');
- },
-
- _findReportHeader: function(domElem) {
- var h2 = false;
- if (domElem.prev().is('h2'))
- {
- h2 = domElem.prev();
- }
- else if (this.param.viewDataTable == 'tableGoals')
- {
- h2 = $('#titleGoalsByDimension');
- }
- else if( $('h2', domElem))
- {
- h2 = $('h2', domElem);
- }
- return h2;
- }
-};
+ },
+
+ truncate: function (domElemToTruncate, truncationOffset) {
+ var self = this;
+
+ domElemToTruncate = $(domElemToTruncate);
+
+ if (typeof domElemToTruncate.data('originalText') != 'undefined') {
+ // truncate only once. otherwise, the tooltip will show the truncated text as well.
+ return;
+ }
+
+ // make the original text (before truncation) available for others.
+ // the .truncate plugins adds a title to the dom element but the .tooltip
+ // plugin removes that again.
+ domElemToTruncate.data('originalText', domElemToTruncate.text());
+
+ if (typeof truncationOffset == 'undefined') {
+ truncationOffset = 0;
+ }
+ var truncationLimit = 50;
+
+ if (typeof self.param.idSubtable == 'undefined'
+ && self.param.viewDataTable == 'tableAllColumns') {
+ // when showing all columns in a subtable, space is restricted
+ truncationLimit = 25;
+ }
+
+ truncationLimit += truncationOffset;
+ domElemToTruncate.truncate(truncationLimit);
+
+ var tooltipElem = $('.truncated', domElemToTruncate),
+ customToolTipText = domElemToTruncate.attr('title');
+
+ // if there's a title on the dom element, use this as the tooltip instead of
+ // the one set by the truncate plugin
+ if (customToolTipText) {
+ // make sure browser doesn't add its own tooltip for the truncated element
+ if (tooltipElem[0]) {
+ tooltipElem.removeAttr('title');
+ }
+
+ tooltipElem = domElemToTruncate;
+ tooltipElem.attr('title', customToolTipText);
+ }
+
+ // use tooltip (tooltip text determined by the 'title' attribute)
+ tooltipElem.tooltip();
+ },
+
+ //Apply some miscelleaneous style to the DataTable
+ applyCosmetics: function (domElem) {
+ var self = this;
+
+ // Add some styles on the cells even/odd
+ // label (first column of a data row) or not
+ $("th:first-child", domElem).addClass('label');
+ $("td:first-child:odd", domElem).addClass('label labeleven');
+ $("td:first-child:even", domElem).addClass('label labelodd');
+ $("tr:odd td", domElem).slice(1).addClass('columnodd');
+ $("tr:even td", domElem).slice(1).addClass('columneven');
+
+ $('td span.label', domElem).each(function () { self.truncate($(this)); });
+
+ },
+
+ //behaviour for 'nested DataTable' (DataTable loaded on a click on a row)
+ handleSubDataTable: function (domElem) {
+ var self = this;
+ // When the TR has a subDataTable class it means that this row has a link to a subDataTable
+ this.numberOfSubtables = $('tr.subDataTable', domElem)
+ .click(
+ function () {
+ // get the idSubTable
+ var idSubTable = $(this).attr('id');
+ var divIdToReplaceWithSubTable = 'subDataTable_' + idSubTable;
+
+ // if the subDataTable is not already loaded
+ if (typeof self.loadedSubDataTable[divIdToReplaceWithSubTable] == "undefined") {
+ var numberOfColumns = $(this).children().length;
+
+ // at the end of the query it will replace the ID matching the new HTML table #ID
+ // we need to create this ID first
+ $(this).after(
+ '<tr>' +
+ '<td colspan="' + numberOfColumns + '" class="cellSubDataTable">' +
+ '<div id="' + divIdToReplaceWithSubTable + '">' +
+ '<span class="loadingPiwik" style="display:inline"><img src="themes/default/images/loading-blue.gif" />' + _pk_translate('General_Loading_js') + '</span>' +
+ '</div>' +
+ '</td>' +
+ '</tr>'
+ );
+
+ var savedActionVariable = self.param.action;
+
+ // reset all the filters from the Parent table
+ var filtersToRestore = self.resetAllFilters();
+ // do not ignore the exclude low population click
+ self.param.enable_filter_excludelowpop = filtersToRestore.enable_filter_excludelowpop;
+
+ self.param.idSubtable = idSubTable;
+ self.param.action = self.param.controllerActionCalledWhenRequestSubTable;
+ self.reloadAjaxDataTable(false);
+
+ self.param.action = savedActionVariable;
+ delete self.param.idSubtable;
+ self.restoreAllFilters(filtersToRestore);
+
+ self.loadedSubDataTable[divIdToReplaceWithSubTable] = true;
+
+ $(this).next().toggle();
+
+ // when "loading..." is displayed, hide actions
+ // repositioning after loading is not easily possible
+ $(this).find('div.dataTableRowActions').hide();
+ }
+
+ $(this).next().toggle();
+ self.repositionRowActions($(this));
+ }
+ ).size();
+ },
+
+ // tooltip for column documentation
+ handleColumnDocumentation: function (domElem) {
+ if ($('#dashboard').size() > 0) {
+ // don't display column documentation in dashboard
+ // it causes trouble in full screen view
+ return;
+ }
+
+ var self = this;
+
+ $('th:has(.columnDocumentation)', domElem).each(function () {
+ var th = $(this);
+ var tooltip = th.find('.columnDocumentation');
+
+ tooltip.next().hover(function () {
+ var left = (-1 * tooltip.outerWidth() / 2) + th.width() / 2;
+ var top = -1 * (tooltip.outerHeight() + 10);
+
+ if (th.next().size() == 0) {
+ left = (-1 * tooltip.outerWidth()) + th.width() +
+ parseInt(th.css('padding-right'), 10);
+ }
+
+ tooltip.css({
+ marginLeft: left,
+ marginTop: top
+ });
+
+ tooltip.stop(true, true).fadeIn(250);
+ },
+ function () {
+ $(this).prev().stop(true, true).fadeOut(400);
+ });
+ });
+ },
+
+ // documentation for report
+ handleReportDocumentation: function (domElem) {
+ // don't display report documentation in dashboard
+ if ($('#dashboard').size() > 0
+ // or in Widgetize screen
+ || $('.widgetContent').size() > 0
+ // or in Widget export
+ || $('.widget').size() > 0
+ ) {
+ return;
+ }
+ domElem = $(domElem);
+ var doc = domElem.find('.reportDocumentation');
+
+ var h2 = this._findReportHeader(domElem);
+ if (doc.size() == 0 || doc.children().size() == 0) // if we can't find the element, or the element is empty
+ {
+ if (h2 && h2.size() > 0) {
+ h2.find('a.reportDocumentationIcon').addClass('hidden');
+ }
+ return;
+ }
+
+ var icon = $('<a href="#"></a>');
+ var docShown = false;
+
+ icon.click(function () {
+ if (docShown) {
+ doc.stop(true, true).fadeOut(250);
+ }
+ else {
+ var widthOrientation = domElem.find('table, canvas, object').eq(0);
+ if (widthOrientation.size() > 0) {
+ var width = Math.min(widthOrientation.width(), doc.parent().innerWidth());
+ doc.css('width', (width - 2) + 'px');
+ }
+ doc.stop(true, true).fadeIn(250);
+ }
+ docShown = !docShown;
+ return false;
+ });
+
+ icon.addClass('reportDocumentationIcon');
+ if (h2 && h2.size() > 0) {
+ // handle previously added icon
+ var existingIcon = h2.find('a.reportDocumentationIcon');
+ if (existingIcon.size() > 0) {
+ existingIcon.replaceWith(icon);
+ }
+ else {
+ // add icon
+ h2.append('&nbsp;&nbsp;&nbsp;');
+ h2.append(icon);
+
+ h2.hover(function () {
+ $(this).find('a.reportDocumentationIcon').show();
+ },
+ function () {
+ $(this).find('a.reportDocumentationIcon').hide();
+ })
+ .click(
+ function () {
+ $(this).find('a.reportDocumentationIcon').click();
+ })
+ .css('cursor', 'pointer');
+ }
+ }
+ else {
+ //domElem.prepend(icon);
+ }
+ },
+
+ handleRowActions: function (domElem) {
+ this.doHandleRowActions(domElem.find('table > tbody > tr'));
+ },
+
+ handleRelatedReports: function (domElem) {
+ var self = this,
+ hideShowRelatedReports = function (thisReport) {
+ $('span', $(thisReport).parent().parent()).each(function () {
+ if (thisReport == this)
+ $(this).hide();
+ else
+ $(this).show();
+ });
+ },
+ // 'this' report must be hidden in datatable output
+ thisReport = $('.datatableRelatedReports span:hidden', domElem)[0];
+
+ hideShowRelatedReports(thisReport);
+ $('.datatableRelatedReports span', domElem).each(function () {
+ var clicked = this;
+ $(this).unbind('click').click(function (e) {
+ var url = $(this).attr('href');
+
+ // if this url is also the url of a menu item, better to click that menu item instead of
+ // doing AJAX request
+ var menuItem = null;
+ $("#root>ul.nav a").each(function () {
+ if ($(this).attr('name') == url) {
+ menuItem = this;
+ return false
+ }
+ });
+
+ if (menuItem) {
+ $(menuItem).click();
+ return;
+ }
+
+ // modify parameters
+ self.resetAllFilters();
+ var newParams = broadcast.getValuesFromUrl(url);
+ for (var key in newParams) {
+ self.param[key] = decodeURIComponent(newParams[key]);
+ }
+
+ // do ajax request
+ self.reloadAjaxDataTable(true, function (newReport) {
+ var newDomElem = self.dataTableLoaded(newReport, self.workingDivId);
+ hideShowRelatedReports(clicked);
+
+ // update header, if we can find it
+ var h2 = self._findReportHeader(newDomElem);
+ if (h2)
+ h2.text($(clicked).text());
+ });
+ });
+ });
+ },
+
+ /**
+ * Handle events that other code triggers on this table.
+ *
+ * You can trigger one of these events to get the datatable to do things,
+ * such as reload its data.
+ *
+ * Events handled:
+ * - reload: Triggering 'reload' on a datatable DOM element will
+ * reload the datatable's data. You can pass in an object mapping
+ * parameters to set before reloading data.
+ *
+ * $(datatableDomElem).trigger('reload', {columns: 'nb_visits,nb_actions', idSite: 2});
+ */
+ handleTriggeredEvents: function (domElem) {
+ var self = this;
+
+ // reload datatable w/ new params if desired (NOTE: must use 'bind', not 'on')
+ $(domElem).bind('reload', function (e, paramOverride) {
+ paramOverride = paramOverride || {};
+ for (var name in paramOverride) {
+ self.param[name] = paramOverride[name];
+ }
+ ;
+
+ self.reloadAjaxDataTable(true);
+ });
+ },
+
+ // also used in action data table
+ doHandleRowActions: function (trs) {
+ var self = this;
+
+ var availableActionsForReport = DataTable_RowActions_Registry
+ .getAvailableActionsForReport(self.param);
+
+ if (availableActionsForReport.length == 0) {
+ return;
+ }
+
+ var actionInstances = {};
+ for (var i = 0; i < availableActionsForReport.length; i++) {
+ var action = availableActionsForReport[i];
+ actionInstances[action.name] = action.createInstance(self);
+ }
+
+ trs.each(function () {
+ var tr = $(this);
+ var td = tr.find('td:first');
+ // call initTr on all actions that are available for the report
+ for (var i = 0; i < availableActionsForReport.length; i++) {
+ var action = availableActionsForReport[i];
+ actionInstances[action.name].initTr(tr);
+ }
+
+ // if there are row actions, make sure the first column is not too narrow
+ td.css('minWidth', '145px');
+
+ // show actions that are available for the row on hover
+ var actionsDom = null;
+ tr.hover(function () {
+ if (actionsDom === null) {
+ // create dom nodes on the fly
+ actionsDom = self.createRowActions(availableActionsForReport, tr, actionInstances);
+ td.prepend(actionsDom);
+ }
+ // reposition and show the actions
+ self.repositionRowActions(tr);
+ actionsDom.show();
+ },
+ function () {
+ if (actionsDom !== null) {
+ actionsDom.hide();
+ }
+ });
+ });
+ },
+
+ createRowActions: function (availableActionsForReport, tr, actionInstances) {
+ var container = $(document.createElement('div')).addClass('dataTableRowActions');
+
+ for (var i = availableActionsForReport.length - 1; i >= 0; i--) {
+ var action = availableActionsForReport[i];
+
+ if (!action.isAvailableOnRow(this.param, tr)) {
+ continue;
+ }
+
+ var actionEl = $(document.createElement('a')).attr({href: '#'}).addClass('action' + action.name);
+ actionEl.append($(document.createElement('img')).attr({src: action.dataTableIcon}));
+ container.append(actionEl);
+
+ if (i == availableActionsForReport.length - 1) {
+ actionEl.addClass('leftmost');
+ }
+ if (i == 0) {
+ actionEl.addClass('rightmost');
+ }
+
+ actionEl.click((function (action, el) {
+ return function (e) {
+ $(this).blur();
+ container.hide();
+ Piwik_Tooltip.hide();
+ if (typeof actionInstances[action.name].onClick == 'function') {
+ return actionInstances[action.name].onClick(el, tr, e);
+ }
+ actionInstances[action.name].trigger(tr, e);
+ return false;
+ }
+ })(action, actionEl));
+
+ if (typeof action.dataTableIconHover != 'undefined') {
+ actionEl.append($(document.createElement('img')).attr({src: action.dataTableIconHover}).hide());
+
+ actionEl.hover(function () {
+ var img = $(this).find('img');
+ img.eq(0).hide();
+ img.eq(1).show();
+ },
+ function () {
+ var img = $(this).find('img');
+ img.eq(1).hide();
+ img.eq(0).show();
+ });
+ }
+
+ if (typeof action.dataTableIconTooltip != 'undefined') {
+ actionEl.hover((function (action) {
+ return function () {
+ Piwik_Tooltip.showWithTitle(
+ action.dataTableIconTooltip[0],
+ action.dataTableIconTooltip[1],
+ 'rowActionTooltip');
+ };
+ })(action), function () {
+ Piwik_Tooltip.hide();
+ });
+ }
+ }
+ return container;
+ },
+ repositionRowActions: function (tr) {
+ var td = tr.find('td:first');
+ var actions = tr.find('div.dataTableRowActions');
+ actions.height(tr.innerHeight() - 2);
+ actions.css('marginLeft', (td.width() + 5 - actions.outerWidth()) + 'px');
+ },
+ _findReportHeader: function (domElem) {
+ var h2 = false;
+ if (domElem.prev().is('h2')) {
+ h2 = domElem.prev();
+ }
+ else if (this.param.viewDataTable == 'tableGoals') {
+ h2 = $('#titleGoalsByDimension');
+ }
+ else if ($('h2', domElem)) {
+ h2 = $('h2', domElem);
+ }
+ return h2;
+ }
+};
//-----------------------------------------------------------------------------
@@ -1685,364 +1526,334 @@ actionDataTable.prototype = new dataTable;
actionDataTable.prototype.constructor = actionDataTable;
//actionDataTable constructor
-function actionDataTable()
-{
- dataTable.call(this);
- this.parentAttributeParent = '';
- this.parentId = '';
- this.disabledRowDom = {}; //to handle double click on '+' row
+function actionDataTable() {
+ dataTable.call(this);
+ this.parentAttributeParent = '';
+ this.parentId = '';
+ this.disabledRowDom = {}; //to handle double click on '+' row
}
//Prototype of the actionDataTable object
actionDataTable.prototype =
-{
- //method inheritance
- cleanParams: dataTable.prototype.cleanParams,
- reloadAjaxDataTable: dataTable.prototype.reloadAjaxDataTable,
- handleConfigurationBox: dataTable.prototype.handleConfigurationBox,
- handleSearchBox: dataTable.prototype.handleSearchBox,
- handleAnnotationsButton: dataTable.prototype.handleAnnotationsButton,
- handleExportBox: dataTable.prototype.handleExportBox,
- handleSort: dataTable.prototype.handleSort,
- handleColumnDocumentation: dataTable.prototype.handleColumnDocumentation,
- handleReportDocumentation: dataTable.prototype.handleReportDocumentation,
- doHandleRowActions: dataTable.prototype.doHandleRowActions,
- createRowActions: dataTable.prototype.createRowActions,
- repositionRowActions: dataTable.prototype.repositionRowActions,
- onClickSort: dataTable.prototype.onClickSort,
- truncate: dataTable.prototype.truncate,
- handleOffsetInformation: dataTable.prototype.handleOffsetInformation,
- setActiveIcon: dataTable.prototype.setActiveIcon,
- resetAllFilters: dataTable.prototype.resetAllFilters,
- restoreAllFilters: dataTable.prototype.restoreAllFilters,
- exportToFormatHide: dataTable.prototype.exportToFormatHide,
- handleLimit: dataTable.prototype.handleLimit,
- notifyWidgetParametersChange: dataTable.prototype.notifyWidgetParametersChange,
- handleRelatedReports: dataTable.prototype.handleRelatedReports,
- handleTriggeredEvents: dataTable.prototype.handleTriggeredEvents,
- _findReportHeader: dataTable.prototype._findReportHeader,
-
- //initialisation of the actionDataTable
- init: function(workingDivId, domElem)
- {
- if(typeof domElem == "undefined"
- || domElem.length == 0 ) // needed for actions subtables where truncating was not working otherwise
- {
- domElem = $('#'+workingDivId);
- }
- this.workingDivId = workingDivId;
- this.bindEventsAndApplyStyle(domElem);
- this.initialized = true;
-
- domElem.data('piwikDataTable', this);
- },
-
- //see dataTable::bindEventsAndApplyStyle
- bindEventsAndApplyStyle: function(domElem)
- {
- var self = this;
-
- self.cleanParams();
-
- // we dont display the link on the row with subDataTable when we are already
- // printing all the subTables (case of recursive search when the content is
- // including recursively all the subtables
- if(!self.param.filter_pattern_recursive)
- {
- self.numberOfSubtables = $('tr.subActionsDataTable.rowToProcess').click( function() {
- self.onClickActionSubDataTable(this)
- }).size();
- }
-
- self.applyCosmetics(domElem);
- self.handleRowActions(domElem);
- self.handleLimit(domElem);
- self.handleAnnotationsButton(domElem);
- self.handleExportBox(domElem);
- self.handleSort(domElem);
- self.handleOffsetInformation(domElem);
- if( self.workingDivId != undefined)
- {
- var dataTableLoadedProxy = function (response) {
- self.dataTableLoaded(response, self.workingDivId);
- };
-
- self.handleSearchBox(domElem, dataTableLoadedProxy);
- self.handleConfigurationBox(domElem, dataTableLoadedProxy);
- }
-
- self.handleColumnDocumentation(domElem);
- self.handleReportDocumentation(domElem);
- self.handleRelatedReports(domElem);
- self.handleTriggeredEvents(domElem);
- },
-
- //see dataTable::applyCosmetics
- applyCosmetics: function(domElem)
- {
- var self = this;
-
- $('tr.subActionsDataTable.rowToProcess')
- .css('font-weight','bold');
-
- $("th:first-child", domElem).addClass('label');
- $('td span.label', domElem).each(function(){ self.truncate($(this)); } );
- var imagePlusMinusWidth = 12;
- var imagePlusMinusHeight = 12;
- $('tr.subActionsDataTable.rowToProcess td:first-child')
- .each( function(){
- $(this).prepend('<img width="'+imagePlusMinusWidth+'" height="'+imagePlusMinusHeight+'" class="plusMinus" src="" />');
- if(self.param.filter_pattern_recursive)
- {
- setImageMinus(this);
- }
- else
- {
- setImagePlus(this);
- }
- });
-
- $('tr.rowToProcess')
- .each( function() {
- // we add the CSS style depending on the level of the current loading category
- // we look at the style of the parent row
- var style = $(this).prev().attr('class');
- var currentStyle = $(this).attr('class');
-
- if( (typeof currentStyle != 'undefined')
- && currentStyle.indexOf('level') >= 0 )
- {
- }
- else
- {
- var level = getNextLevelFromClass( style );
- $(this).addClass('level'+ level);
- }
-
- // we add an attribute parent that contains the ID of all the parent categories
- // this ID is used when collapsing a parent row, it searches for all children rows
- // which 'parent' attribute's value contains the collapsed row ID
- $(this).prop('parent', function(){
- return self.parentAttributeParent + ' ' + self.parentId;
- }
- );
-
- // Add some styles on the cells even/odd
- // label (first column of a data row) or not
- $("td:first-child:odd", this).addClass('label labeleven');
- $("td:first-child:even", this).addClass('label labelodd');
- });
- },
-
- handleRowActions: function(domElem)
- {
- var rowsToProcess = $('tr.rowToProcess').removeClass('rowToProcess');
- this.doHandleRowActions(rowsToProcess);
- },
-
- // Called when the user click on an actionDataTable row
- onClickActionSubDataTable: function(domElem)
- {
- var self = this;
-
- // get the idSubTable
- var idSubTable = $(domElem).attr('id');
-
- var divIdToReplaceWithSubTable = 'subDataTable_'+idSubTable;
-
- var NextStyle = $(domElem).next().attr('class');
- var CurrentStyle = $(domElem).attr('class');
-
- var currentRowLevel = getLevelFromClass(CurrentStyle);
- var nextRowLevel = getLevelFromClass(NextStyle);
-
- // if the row has not been clicked
- // which is the same as saying that the next row level is equal or less than the current row
- // because when we click a row the level of the next rows is higher (level2 row gives level3 rows)
- if(currentRowLevel >= nextRowLevel)
- {
- //unbind click to avoid double click problem
- $(domElem).off('click');
- self.disabledRowDom = $(domElem);
-
- var numberOfColumns = $(domElem).children().length;
- $(domElem).after( '\
- <tr id="'+divIdToReplaceWithSubTable+'" class="cellSubDataTable">\
- <td colspan="'+numberOfColumns+'">\
+{
+ //method inheritance
+ cleanParams: dataTable.prototype.cleanParams,
+ reloadAjaxDataTable: dataTable.prototype.reloadAjaxDataTable,
+ handleConfigurationBox: dataTable.prototype.handleConfigurationBox,
+ handleSearchBox: dataTable.prototype.handleSearchBox,
+ handleAnnotationsButton: dataTable.prototype.handleAnnotationsButton,
+ handleExportBox: dataTable.prototype.handleExportBox,
+ handleSort: dataTable.prototype.handleSort,
+ handleColumnDocumentation: dataTable.prototype.handleColumnDocumentation,
+ handleReportDocumentation: dataTable.prototype.handleReportDocumentation,
+ doHandleRowActions: dataTable.prototype.doHandleRowActions,
+ createRowActions: dataTable.prototype.createRowActions,
+ repositionRowActions: dataTable.prototype.repositionRowActions,
+ onClickSort: dataTable.prototype.onClickSort,
+ truncate: dataTable.prototype.truncate,
+ handleOffsetInformation: dataTable.prototype.handleOffsetInformation,
+ setActiveIcon: dataTable.prototype.setActiveIcon,
+ resetAllFilters: dataTable.prototype.resetAllFilters,
+ restoreAllFilters: dataTable.prototype.restoreAllFilters,
+ exportToFormatHide: dataTable.prototype.exportToFormatHide,
+ handleLimit: dataTable.prototype.handleLimit,
+ notifyWidgetParametersChange: dataTable.prototype.notifyWidgetParametersChange,
+ handleRelatedReports: dataTable.prototype.handleRelatedReports,
+ handleTriggeredEvents: dataTable.prototype.handleTriggeredEvents,
+ _findReportHeader: dataTable.prototype._findReportHeader,
+
+ //initialisation of the actionDataTable
+ init: function (workingDivId, domElem) {
+ if (typeof domElem == "undefined"
+ || domElem.length == 0) // needed for actions subtables where truncating was not working otherwise
+ {
+ domElem = $('#' + workingDivId);
+ }
+ this.workingDivId = workingDivId;
+ this.bindEventsAndApplyStyle(domElem);
+ this.initialized = true;
+
+ domElem.data('piwikDataTable', this);
+ },
+
+ //see dataTable::bindEventsAndApplyStyle
+ bindEventsAndApplyStyle: function (domElem) {
+ var self = this;
+
+ self.cleanParams();
+
+ // we dont display the link on the row with subDataTable when we are already
+ // printing all the subTables (case of recursive search when the content is
+ // including recursively all the subtables
+ if (!self.param.filter_pattern_recursive) {
+ self.numberOfSubtables = $('tr.subActionsDataTable.rowToProcess').click(function () {
+ self.onClickActionSubDataTable(this)
+ }).size();
+ }
+
+ self.applyCosmetics(domElem);
+ self.handleRowActions(domElem);
+ self.handleLimit(domElem);
+ self.handleAnnotationsButton(domElem);
+ self.handleExportBox(domElem);
+ self.handleSort(domElem);
+ self.handleOffsetInformation(domElem);
+ if (self.workingDivId != undefined) {
+ var dataTableLoadedProxy = function (response) {
+ self.dataTableLoaded(response, self.workingDivId);
+ };
+
+ self.handleSearchBox(domElem, dataTableLoadedProxy);
+ self.handleConfigurationBox(domElem, dataTableLoadedProxy);
+ }
+
+ self.handleColumnDocumentation(domElem);
+ self.handleReportDocumentation(domElem);
+ self.handleRelatedReports(domElem);
+ self.handleTriggeredEvents(domElem);
+ },
+
+ //see dataTable::applyCosmetics
+ applyCosmetics: function (domElem) {
+ var self = this;
+
+ $('tr.subActionsDataTable.rowToProcess')
+ .css('font-weight', 'bold');
+
+ $("th:first-child", domElem).addClass('label');
+ $('td span.label', domElem).each(function () { self.truncate($(this)); });
+ var imagePlusMinusWidth = 12;
+ var imagePlusMinusHeight = 12;
+ $('tr.subActionsDataTable.rowToProcess td:first-child')
+ .each(function () {
+ $(this).prepend('<img width="' + imagePlusMinusWidth + '" height="' + imagePlusMinusHeight + '" class="plusMinus" src="" />');
+ if (self.param.filter_pattern_recursive) {
+ setImageMinus(this);
+ }
+ else {
+ setImagePlus(this);
+ }
+ });
+
+ $('tr.rowToProcess')
+ .each(function () {
+ // we add the CSS style depending on the level of the current loading category
+ // we look at the style of the parent row
+ var style = $(this).prev().attr('class');
+ var currentStyle = $(this).attr('class');
+
+ if ((typeof currentStyle != 'undefined')
+ && currentStyle.indexOf('level') >= 0) {
+ }
+ else {
+ var level = getNextLevelFromClass(style);
+ $(this).addClass('level' + level);
+ }
+
+ // we add an attribute parent that contains the ID of all the parent categories
+ // this ID is used when collapsing a parent row, it searches for all children rows
+ // which 'parent' attribute's value contains the collapsed row ID
+ $(this).prop('parent', function () {
+ return self.parentAttributeParent + ' ' + self.parentId;
+ }
+ );
+
+ // Add some styles on the cells even/odd
+ // label (first column of a data row) or not
+ $("td:first-child:odd", this).addClass('label labeleven');
+ $("td:first-child:even", this).addClass('label labelodd');
+ });
+ },
+
+ handleRowActions: function (domElem) {
+ var rowsToProcess = $('tr.rowToProcess').removeClass('rowToProcess');
+ this.doHandleRowActions(rowsToProcess);
+ },
+
+ // Called when the user click on an actionDataTable row
+ onClickActionSubDataTable: function (domElem) {
+ var self = this;
+
+ // get the idSubTable
+ var idSubTable = $(domElem).attr('id');
+
+ var divIdToReplaceWithSubTable = 'subDataTable_' + idSubTable;
+
+ var NextStyle = $(domElem).next().attr('class');
+ var CurrentStyle = $(domElem).attr('class');
+
+ var currentRowLevel = getLevelFromClass(CurrentStyle);
+ var nextRowLevel = getLevelFromClass(NextStyle);
+
+ // if the row has not been clicked
+ // which is the same as saying that the next row level is equal or less than the current row
+ // because when we click a row the level of the next rows is higher (level2 row gives level3 rows)
+ if (currentRowLevel >= nextRowLevel) {
+ //unbind click to avoid double click problem
+ $(domElem).off('click');
+ self.disabledRowDom = $(domElem);
+
+ var numberOfColumns = $(domElem).children().length;
+ $(domElem).after('\
+ <tr id="' + divIdToReplaceWithSubTable + '" class="cellSubDataTable">\
+ <td colspan="' + numberOfColumns + '">\
<span class="loadingPiwik" style="display:inline"><img src="themes/default/images/loading-blue.gif" /> Loading...</span>\
</td>\
</tr>\
');
- var savedActionVariable = self.param.action;
-
- // reset all the filters from the Parent table
- var filtersToRestore = self.resetAllFilters();
-
- // Do not reset the sorting filters that must be applied to sub tables
- this.param['filter_sort_column'] = filtersToRestore['filter_sort_column'];
- this.param['filter_sort_order'] = filtersToRestore['filter_sort_order'];
- this.param['enable_filter_excludelowpop'] = filtersToRestore['enable_filter_excludelowpop'];
-
- self.param.idSubtable = idSubTable;
- self.param.action = self.param.controllerActionCalledWhenRequestSubTable;
-
- self.reloadAjaxDataTable(false, function(resp){
- self.actionsSubDataTableLoaded(resp);
- self.repositionRowActions($(domElem));
- });
- self.param.action = savedActionVariable;
-
- self.restoreAllFilters(filtersToRestore);
-
- delete self.param.idSubtable;
- }
- // else we toggle all these rows
- else
- {
- var plusDetected = $('td img.plusMinus', domElem).attr('src').indexOf('plus') >= 0;
-
- $(domElem).siblings().each( function(){
- var parents = $(this).prop('parent').split(' ');
- if(parents)
- {
- if(parents.indexOf(idSubTable) >= 0
- || parents.indexOf('subDataTable_'+idSubTable) >= 0)
- {
- if(plusDetected)
- {
- $(this).css('display','');
-
- //unroll everything and display '-' sign
- //if the row is already opened
- var NextStyle = $(this).next().attr('class');
- var CurrentStyle = $(this).attr('class');
-
- var currentRowLevel = getLevelFromClass(CurrentStyle);
- var nextRowLevel = getLevelFromClass(NextStyle);
-
- if(currentRowLevel < nextRowLevel)
- setImageMinus(this);
- }
- else
- {
- $(this).css('display','none');
- }
- self.repositionRowActions($(domElem));
- }
- }
- });
- }
-
- // toggle the +/- image
- var plusDetected = $('td img.plusMinus', domElem).attr('src').indexOf('plus') >= 0;
- if(plusDetected)
- {
- setImageMinus(domElem);
- }
- else
- {
- setImagePlus(domElem);
- }
- },
-
- //called when the full table actions is loaded
- dataTableLoaded: function(response, workingDivId)
- {
- var content = $(response);
- var idToReplace = workingDivId || $(content).attr('id');
-
- //reset parents id
- self.parentAttributeParent = '';
- self.parentId = '';
-
- var dataTableSel = $('#'+idToReplace);
-
- // keep the original list of related reports
- var oldReportsElem = $('.datatableRelatedReports', dataTableSel);
- $('.datatableRelatedReports', content).replaceWith(oldReportsElem);
-
- dataTableSel.replaceWith(content);
- piwikHelper.lazyScrollTo(content[0], 400);
-
- return content;
- },
-
- // Called when a set of rows for a category of actions is loaded
- actionsSubDataTableLoaded: function(response)
- {
- var self = this;
- var idToReplace = $(response).attr('id');
-
- // remove the first row of results which is only used to get the Id
- var response = $(response).filter('tr').slice(1).addClass('rowToProcess');
- self.parentAttributeParent = $('tr#'+idToReplace).prev().prop('parent');
- self.parentId = idToReplace;
-
- $('tr#'+idToReplace).after( response ).remove();
-
- var missingColumns = (response.prev().find('td').size() - response.find('td').size());
- for (var i = 0; i < missingColumns; i++) {
- // if the subtable has fewer columns than the parent table, add some columns.
- // this happens for example, when the parent table has performance metrics and the subtable doesn't.
- response.append('<td>-</td>');
- }
-
- var re = /subDataTable_(\d+)/;
- ok = re.exec(self.parentId);
- if(ok)
- {
- self.parentId = ok[1];
- }
-
- // we execute the bindDataTableEvent function for the new DIV
- self.init(self.workingDivId, $('#'+idToReplace));
-
- //bind back the click event (disabled to avoid double-click problem)
- self.disabledRowDom.click(
- function()
- {
- self.onClickActionSubDataTable(this)
- });
- }
+ var savedActionVariable = self.param.action;
+
+ // reset all the filters from the Parent table
+ var filtersToRestore = self.resetAllFilters();
+
+ // Do not reset the sorting filters that must be applied to sub tables
+ this.param['filter_sort_column'] = filtersToRestore['filter_sort_column'];
+ this.param['filter_sort_order'] = filtersToRestore['filter_sort_order'];
+ this.param['enable_filter_excludelowpop'] = filtersToRestore['enable_filter_excludelowpop'];
+
+ self.param.idSubtable = idSubTable;
+ self.param.action = self.param.controllerActionCalledWhenRequestSubTable;
+
+ self.reloadAjaxDataTable(false, function (resp) {
+ self.actionsSubDataTableLoaded(resp);
+ self.repositionRowActions($(domElem));
+ });
+ self.param.action = savedActionVariable;
+
+ self.restoreAllFilters(filtersToRestore);
+
+ delete self.param.idSubtable;
+ }
+ // else we toggle all these rows
+ else {
+ var plusDetected = $('td img.plusMinus', domElem).attr('src').indexOf('plus') >= 0;
+
+ $(domElem).siblings().each(function () {
+ var parents = $(this).prop('parent').split(' ');
+ if (parents) {
+ if (parents.indexOf(idSubTable) >= 0
+ || parents.indexOf('subDataTable_' + idSubTable) >= 0) {
+ if (plusDetected) {
+ $(this).css('display', '');
+
+ //unroll everything and display '-' sign
+ //if the row is already opened
+ var NextStyle = $(this).next().attr('class');
+ var CurrentStyle = $(this).attr('class');
+
+ var currentRowLevel = getLevelFromClass(CurrentStyle);
+ var nextRowLevel = getLevelFromClass(NextStyle);
+
+ if (currentRowLevel < nextRowLevel)
+ setImageMinus(this);
+ }
+ else {
+ $(this).css('display', 'none');
+ }
+ self.repositionRowActions($(domElem));
+ }
+ }
+ });
+ }
+
+ // toggle the +/- image
+ var plusDetected = $('td img.plusMinus', domElem).attr('src').indexOf('plus') >= 0;
+ if (plusDetected) {
+ setImageMinus(domElem);
+ }
+ else {
+ setImagePlus(domElem);
+ }
+ },
+
+ //called when the full table actions is loaded
+ dataTableLoaded: function (response, workingDivId) {
+ var content = $(response);
+ var idToReplace = workingDivId || $(content).attr('id');
+
+ //reset parents id
+ self.parentAttributeParent = '';
+ self.parentId = '';
+
+ var dataTableSel = $('#' + idToReplace);
+
+ // keep the original list of related reports
+ var oldReportsElem = $('.datatableRelatedReports', dataTableSel);
+ $('.datatableRelatedReports', content).replaceWith(oldReportsElem);
+
+ dataTableSel.replaceWith(content);
+ piwikHelper.lazyScrollTo(content[0], 400);
+
+ return content;
+ },
+
+ // Called when a set of rows for a category of actions is loaded
+ actionsSubDataTableLoaded: function (response) {
+ var self = this;
+ var idToReplace = $(response).attr('id');
+
+ // remove the first row of results which is only used to get the Id
+ var response = $(response).filter('tr').slice(1).addClass('rowToProcess');
+ self.parentAttributeParent = $('tr#' + idToReplace).prev().prop('parent');
+ self.parentId = idToReplace;
+
+ $('tr#' + idToReplace).after(response).remove();
+
+ var missingColumns = (response.prev().find('td').size() - response.find('td').size());
+ for (var i = 0; i < missingColumns; i++) {
+ // if the subtable has fewer columns than the parent table, add some columns.
+ // this happens for example, when the parent table has performance metrics and the subtable doesn't.
+ response.append('<td>-</td>');
+ }
+
+ var re = /subDataTable_(\d+)/;
+ ok = re.exec(self.parentId);
+ if (ok) {
+ self.parentId = ok[1];
+ }
+
+ // we execute the bindDataTableEvent function for the new DIV
+ self.init(self.workingDivId, $('#' + idToReplace));
+
+ //bind back the click event (disabled to avoid double-click problem)
+ self.disabledRowDom.click(
+ function () {
+ self.onClickActionSubDataTable(this)
+ });
+ }
};
//helper function for actionDataTable
-function getLevelFromClass( style)
-{
- if (!style || typeof style == "undefined") return 0;
-
- var currentLevelIndex = style.indexOf('level');
- var currentLevel = 0;
- if( currentLevelIndex >= 0)
- {
- currentLevel = Number(style.substr(currentLevelIndex+5,1));
- }
- return currentLevel;
+function getLevelFromClass(style) {
+ if (!style || typeof style == "undefined") return 0;
+
+ var currentLevelIndex = style.indexOf('level');
+ var currentLevel = 0;
+ if (currentLevelIndex >= 0) {
+ currentLevel = Number(style.substr(currentLevelIndex + 5, 1));
+ }
+ return currentLevel;
}
//helper function for actionDataTable
-function getNextLevelFromClass( style )
-{
- if (!style || typeof style == "undefined") return 0;
- currentLevel = getLevelFromClass(style);
- newLevel = currentLevel;
- // if this is not a row to process so
- if( style.indexOf('rowToProcess') < 0 )
- {
- newLevel = currentLevel + 1;
- }
- return newLevel;
+function getNextLevelFromClass(style) {
+ if (!style || typeof style == "undefined") return 0;
+ currentLevel = getLevelFromClass(style);
+ newLevel = currentLevel;
+ // if this is not a row to process so
+ if (style.indexOf('rowToProcess') < 0) {
+ newLevel = currentLevel + 1;
+ }
+ return newLevel;
}
//helper function for actionDataTable
-function setImageMinus( domElem )
-{
- $('img.plusMinus',domElem).attr('src', 'themes/default/images/minus.png');
+function setImageMinus(domElem) {
+ $('img.plusMinus', domElem).attr('src', 'themes/default/images/minus.png');
}
//helper function for actionDataTable
-function setImagePlus( domElem )
-{
- $('img.plusMinus',domElem).attr('src', 'themes/default/images/plus.png');
+function setImagePlus(domElem) {
+ $('img.plusMinus', domElem).attr('src', 'themes/default/images/plus.png');
}
diff --git a/plugins/CoreHome/templates/datatable.tpl b/plugins/CoreHome/templates/datatable.tpl
index e596519e45..9b05930cf7 100644
--- a/plugins/CoreHome/templates/datatable.tpl
+++ b/plugins/CoreHome/templates/datatable.tpl
@@ -1,57 +1,58 @@
<div class="dataTable" data-report="{$properties.uniqueId}" data-params="{$javascriptVariablesToSet|@json_encode|escape:'html'}">
- <div class="reportDocumentation">
- {if !empty($reportDocumentation)}<p>{$reportDocumentation}</p>{/if}
- {if isset($properties.metadata.archived_date)}<span class='helpDate'>{$properties.metadata.archived_date}</span>{/if}
- </div>
- <div class="{if isset($javascriptVariablesToSet.idSubtable)&& $javascriptVariablesToSet.idSubtable!=0}sub{/if}{if $javascriptVariablesToSet.viewDataTable=='tableAllColumns'}dataTableAllColumnsWrapper{elseif $javascriptVariablesToSet.viewDataTable=='tableGoals'}dataTableAllColumnsWrapper{else}dataTableWrapper{/if}">
- {if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
- {$arrayDataTable.message}
- {else}
- {if count($arrayDataTable) == 0}
- {if isset($showReportDataWasPurgedMessage) && $showReportDataWasPurgedMessage}
- <div class="pk-emptyDataTable">{'CoreHome_DataForThisReportHasBeenPurged'|translate:$deleteReportsOlderThan}</div>
- {else}
- <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
- {/if}
- {else}
- <a name="{$properties.uniqueId}"></a>
- <table cellspacing="0" class="dataTable">
- <thead>
- <tr>
- {foreach from=$dataTableColumns item=column name=head}
- <th class="sortable {if $smarty.foreach.head.first}first{elseif $smarty.foreach.head.last}last{/if}" id="{$column}">
- {if !empty($columnDocumentation[$column])}
- <div class="columnDocumentation">
- <div class="columnDocumentationTitle">
- {$columnTranslations[$column]|escape:'html'|replace:"&amp;nbsp;":"&nbsp;"}
- </div>
- {$columnDocumentation[$column]|escape:'html'}
- </div>
- {/if}
- <div id="thDIV">{$columnTranslations[$column]|escape:'html'|replace:"&amp;nbsp;":"&nbsp;"}</div>
- </th>
- {/foreach}
- </tr>
- </thead>
-
- <tbody>
- {foreach from=$arrayDataTable item=row}
- <tr {if $row.idsubdatatable && $javascriptVariablesToSet.controllerActionCalledWhenRequestSubTable != null}class="subDataTable" id="{$row.idsubdatatable}"{/if}{if isset($row.issummaryrow) && $row.issummaryrow && $properties.highlight_summary_row} class="highlight"{/if}>
- {foreach from=$dataTableColumns item=column}
- <td>
- {include file="CoreHome/templates/datatable_cell.tpl"}
- </td>
- {/foreach}
- </tr>
- {/foreach}
- </tbody>
- </table>
- {/if}
-
- {if $properties.show_footer}
- {include file="CoreHome/templates/datatable_footer.tpl"}
- {/if}
- {include file="CoreHome/templates/datatable_js.tpl"}
- {/if}
- </div>
+ <div class="reportDocumentation">
+ {if !empty($reportDocumentation)}<p>{$reportDocumentation}</p>{/if}
+ {if isset($properties.metadata.archived_date)}<span class='helpDate'>{$properties.metadata.archived_date}</span>{/if}
+ </div>
+ <div class="{if isset($javascriptVariablesToSet.idSubtable)&& $javascriptVariablesToSet.idSubtable!=0}sub{/if}{if $javascriptVariablesToSet.viewDataTable=='tableAllColumns'}dataTableAllColumnsWrapper{elseif $javascriptVariablesToSet.viewDataTable=='tableGoals'}dataTableAllColumnsWrapper{else}dataTableWrapper{/if}">
+ {if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
+ {$arrayDataTable.message}
+ {else}
+ {if count($arrayDataTable) == 0}
+ {if isset($showReportDataWasPurgedMessage) && $showReportDataWasPurgedMessage}
+ <div class="pk-emptyDataTable">{'CoreHome_DataForThisReportHasBeenPurged'|translate:$deleteReportsOlderThan}</div>
+ {else}
+ <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
+ {/if}
+ {else}
+ <a name="{$properties.uniqueId}"></a>
+ <table cellspacing="0" class="dataTable">
+ <thead>
+ <tr>
+ {foreach from=$dataTableColumns item=column name=head}
+ <th class="sortable {if $smarty.foreach.head.first}first{elseif $smarty.foreach.head.last}last{/if}" id="{$column}">
+ {if !empty($columnDocumentation[$column])}
+ <div class="columnDocumentation">
+ <div class="columnDocumentationTitle">
+ {$columnTranslations[$column]|escape:'html'|replace:"&amp;nbsp;":"&nbsp;"}
+ </div>
+ {$columnDocumentation[$column]|escape:'html'}
+ </div>
+ {/if}
+ <div id="thDIV">{$columnTranslations[$column]|escape:'html'|replace:"&amp;nbsp;":"&nbsp;"}</div>
+ </th>
+ {/foreach}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$arrayDataTable item=row}
+ <tr {if $row.idsubdatatable && $javascriptVariablesToSet.controllerActionCalledWhenRequestSubTable != null}class="subDataTable"
+ id="{$row.idsubdatatable}"{/if}{if isset($row.issummaryrow) && $row.issummaryrow && $properties.highlight_summary_row} class="highlight"{/if}>
+ {foreach from=$dataTableColumns item=column}
+ <td>
+ {include file="CoreHome/templates/datatable_cell.tpl"}
+ </td>
+ {/foreach}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ {/if}
+
+ {if $properties.show_footer}
+ {include file="CoreHome/templates/datatable_footer.tpl"}
+ {/if}
+ {include file="CoreHome/templates/datatable_js.tpl"}
+ {/if}
+ </div>
</div>
diff --git a/plugins/CoreHome/templates/datatable_actions.tpl b/plugins/CoreHome/templates/datatable_actions.tpl
index f9a48cf17e..976555262d 100644
--- a/plugins/CoreHome/templates/datatable_actions.tpl
+++ b/plugins/CoreHome/templates/datatable_actions.tpl
@@ -1,52 +1,53 @@
<div class="dataTable" data-report="{$properties.uniqueId}" data-params="{$javascriptVariablesToSet|@json_encode|escape:'html'}">
- <div class="reportDocumentation">
- {if !empty($reportDocumentation)}<p>{$reportDocumentation}</p>{/if}
- {if isset($properties.metadata.archived_date)}<span class='helpDate'>{$properties.metadata.archived_date}</span>{/if}
- </div>
- <div class="dataTableActionsWrapper">
- {if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
- {$arrayDataTable.message}
- {else}
- {if count($arrayDataTable) == 0}
- <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
- {else}
- <table cellspacing="0" class="dataTable dataTableActions">
- <thead>
- <tr>
- {foreach from=$dataTableColumns item=column name=head}
- <th class="sortable {if $smarty.foreach.head.first}first{elseif $smarty.foreach.head.last}last{/if}" id="{$column}">
- {if !empty($columnDocumentation[$column])}
- <div class="columnDocumentation">
- <div class="columnDocumentationTitle">
- {$columnTranslations[$column]|escape:'html'|replace:"&amp;nbsp;":"&nbsp;"}
- </div>
- {$columnDocumentation[$column]|escape:'html'}
- </div>
- {/if}
- <div id="thDIV">{$columnTranslations[$column]|escape:'html'}</div>
- </th>
- {/foreach}
- </tr>
- </thead>
-
- <tbody>
- {foreach from=$arrayDataTable item=row}
- <tr {if $row.idsubdatatable}class="rowToProcess subActionsDataTable" id="{$row.idsubdatatable}"{else}class="actionsDataTable rowToProcess"{/if}>
- {foreach from=$dataTableColumns item=column}
- <td>
- {include file="CoreHome/templates/datatable_cell.tpl"}
- </td>
- {/foreach}
- </tr>
- {/foreach}
- </tbody>
- </table>
- {/if}
-
- {if $properties.show_footer}
- {include file="CoreHome/templates/datatable_footer.tpl"}
- {/if}
- {include file="CoreHome/templates/datatable_actions_js.tpl"}
- {/if}
- </div>
+ <div class="reportDocumentation">
+ {if !empty($reportDocumentation)}<p>{$reportDocumentation}</p>{/if}
+ {if isset($properties.metadata.archived_date)}<span class='helpDate'>{$properties.metadata.archived_date}</span>{/if}
+ </div>
+ <div class="dataTableActionsWrapper">
+ {if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
+ {$arrayDataTable.message}
+ {else}
+ {if count($arrayDataTable) == 0}
+ <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
+ {else}
+ <table cellspacing="0" class="dataTable dataTableActions">
+ <thead>
+ <tr>
+ {foreach from=$dataTableColumns item=column name=head}
+ <th class="sortable {if $smarty.foreach.head.first}first{elseif $smarty.foreach.head.last}last{/if}" id="{$column}">
+ {if !empty($columnDocumentation[$column])}
+ <div class="columnDocumentation">
+ <div class="columnDocumentationTitle">
+ {$columnTranslations[$column]|escape:'html'|replace:"&amp;nbsp;":"&nbsp;"}
+ </div>
+ {$columnDocumentation[$column]|escape:'html'}
+ </div>
+ {/if}
+ <div id="thDIV">{$columnTranslations[$column]|escape:'html'}</div>
+ </th>
+ {/foreach}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$arrayDataTable item=row}
+ <tr {if $row.idsubdatatable}class="rowToProcess subActionsDataTable" id="{$row.idsubdatatable}"
+ {else}class="actionsDataTable rowToProcess"{/if}>
+ {foreach from=$dataTableColumns item=column}
+ <td>
+ {include file="CoreHome/templates/datatable_cell.tpl"}
+ </td>
+ {/foreach}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ {/if}
+
+ {if $properties.show_footer}
+ {include file="CoreHome/templates/datatable_footer.tpl"}
+ {/if}
+ {include file="CoreHome/templates/datatable_actions_js.tpl"}
+ {/if}
+ </div>
</div>
diff --git a/plugins/CoreHome/templates/datatable_actions_recursive.tpl b/plugins/CoreHome/templates/datatable_actions_recursive.tpl
index 00d8b11cd3..eb3d6f4736 100644
--- a/plugins/CoreHome/templates/datatable_actions_recursive.tpl
+++ b/plugins/CoreHome/templates/datatable_actions_recursive.tpl
@@ -1,38 +1,39 @@
<div id="{$properties.uniqueId}">
- <div class="dataTableActionsWrapper">
- {if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
- {$arrayDataTable.message}
- {else}
- {if count($arrayDataTable) == 0}
- <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
- {else}
- <table cellspacing="0" class="dataTable dataTableActions">
- <thead>
- <tr>
- {foreach from=$dataTableColumns item=column}
- <th class="sortable" id="{$column}">{$columnTranslations[$column]|escape:'html'}</th>
- {/foreach}
- </tr>
- </thead>
-
- <tbody>
- {foreach from=$arrayDataTable item=row}
- <tr {if $row.idsubdatatable}class="level{$row.level} rowToProcess subActionsDataTable" id="{$row.idsubdatatable}"{else}class="actionsDataTable rowToProcess level{$row.level}"{/if}>
- {foreach from=$dataTableColumns item=column}
- <td>
- {include file="CoreHome/templates/datatable_cell.tpl"}
- </td>
- {/foreach}
- </tr>
- {/foreach}
- </tbody>
- </table>
- {/if}
-
- {if $properties.show_footer}
- {include file="CoreHome/templates/datatable_footer.tpl"}
+ <div class="dataTableActionsWrapper">
+ {if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
+ {$arrayDataTable.message}
+ {else}
+ {if count($arrayDataTable) == 0}
+ <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
+ {else}
+ <table cellspacing="0" class="dataTable dataTableActions">
+ <thead>
+ <tr>
+ {foreach from=$dataTableColumns item=column}
+ <th class="sortable" id="{$column}">{$columnTranslations[$column]|escape:'html'}</th>
+ {/foreach}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$arrayDataTable item=row}
+ <tr {if $row.idsubdatatable}class="level{$row.level} rowToProcess subActionsDataTable" id="{$row.idsubdatatable}"
+ {else}class="actionsDataTable rowToProcess level{$row.level}"{/if}>
+ {foreach from=$dataTableColumns item=column}
+ <td>
+ {include file="CoreHome/templates/datatable_cell.tpl"}
+ </td>
+ {/foreach}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ {/if}
+
+ {if $properties.show_footer}
+ {include file="CoreHome/templates/datatable_footer.tpl"}
+ {/if}
+ {include file="CoreHome/templates/datatable_actions_js.tpl"}
{/if}
- {include file="CoreHome/templates/datatable_actions_js.tpl"}
- {/if}
- </div>
+ </div>
</div>
diff --git a/plugins/CoreHome/templates/datatable_actions_subdatable.tpl b/plugins/CoreHome/templates/datatable_actions_subdatable.tpl
index 10c0921b69..c7d71c7105 100644
--- a/plugins/CoreHome/templates/datatable_actions_subdatable.tpl
+++ b/plugins/CoreHome/templates/datatable_actions_subdatable.tpl
@@ -1,18 +1,20 @@
<tr id="{$properties.uniqueId}"></tr>
{if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
- {$arrayDataTable.message}
+ {$arrayDataTable.message}
{else}
- {if count($arrayDataTable) == 0}
- <tr><td colspan="{$nbColumns}">{'CoreHome_CategoryNoData'|translate}</td></tr>
- {else}
- {foreach from=$arrayDataTable item=row}
- <tr {if $row.idsubdatatable}class="subActionsDataTable" id="{$row.idsubdatatable}"{else}class="actionsDataTable"{/if}>
- {foreach from=$dataTableColumns item=column}
- <td>
- {include file="CoreHome/templates/datatable_cell.tpl"}
- </td>
- {/foreach}
- </tr>
- {/foreach}
- {/if}
+ {if count($arrayDataTable) == 0}
+ <tr>
+ <td colspan="{$nbColumns}">{'CoreHome_CategoryNoData'|translate}</td>
+ </tr>
+ {else}
+ {foreach from=$arrayDataTable item=row}
+ <tr {if $row.idsubdatatable}class="subActionsDataTable" id="{$row.idsubdatatable}" {else}class="actionsDataTable"{/if}>
+ {foreach from=$dataTableColumns item=column}
+ <td>
+ {include file="CoreHome/templates/datatable_cell.tpl"}
+ </td>
+ {/foreach}
+ </tr>
+ {/foreach}
+ {/if}
{/if}
diff --git a/plugins/CoreHome/templates/datatable_cell.tpl b/plugins/CoreHome/templates/datatable_cell.tpl
index d19dbc0b9e..3b1ec5cd98 100644
--- a/plugins/CoreHome/templates/datatable_cell.tpl
+++ b/plugins/CoreHome/templates/datatable_cell.tpl
@@ -1,17 +1,18 @@
{if !$row.idsubdatatable && $column=='label' && !empty($row.metadata.url)}
<a target="_blank" href='{if !in_array(substr($row.metadata.url,0,4), array('http','ftp:'))}http://{/if}{$row.metadata.url|escape:'html'}'>
- {if empty($row.metadata.logo)}
- <img class="link" width="10" height="9" src="themes/default/images/link.gif" />
- {/if}
-{/if}
-{if $column=='label'}
- {logoHtml metadata=$row.metadata alt=$row.columns.label}
- {if !empty($row.metadata.html_label_prefix)}<span class='label-prefix'>{$row.metadata.html_label_prefix}</span>{/if}
- <span class='label{if !empty($row.metadata.is_aggregate) && $row.metadata.is_aggregate } highlighted{/if}' {if !empty($properties.tooltip_metadata_name)}title="{$row.metadata[$properties.tooltip_metadata_name]}"{/if}>{* make sure there are no whitespaces inside the span
+ {if empty($row.metadata.logo)}
+ <img class="link" width="10" height="9" src="themes/default/images/link.gif"/>
+ {/if}
+ {/if}
+ {if $column=='label'}
+ {logoHtml metadata=$row.metadata alt=$row.columns.label}
+ {if !empty($row.metadata.html_label_prefix)}<span class='label-prefix'>{$row.metadata.html_label_prefix}</span>{/if}
+ <span class='label{if !empty($row.metadata.is_aggregate) && $row.metadata.is_aggregate } highlighted{/if}'
+ {if !empty($properties.tooltip_metadata_name)}title="{$row.metadata[$properties.tooltip_metadata_name]}"{/if}>{* make sure there are no whitespaces inside the span
*}{if !empty($row.metadata.html_label_suffix)}<span class='label-suffix'>{$row.metadata.html_label_suffix}</span>{/if}
-{/if}{*
+ {/if}{*
*}{if isset($row.columns[$column])}{$row.columns[$column]}{else}{$defaultWhenColumnValueNotDefined}{/if}{*
*}{if $column=='label'}</span>{/if}
-{if !$row.idsubdatatable && $column=='label' && !empty($row.metadata.url)}
- </a>
+ {if !$row.idsubdatatable && $column=='label' && !empty($row.metadata.url)}
+</a>
{/if}
diff --git a/plugins/CoreHome/templates/datatable_footer.tpl b/plugins/CoreHome/templates/datatable_footer.tpl
index a37031a08f..8aef75b99a 100644
--- a/plugins/CoreHome/templates/datatable_footer.tpl
+++ b/plugins/CoreHome/templates/datatable_footer.tpl
@@ -1,126 +1,152 @@
<div class="dataTableFeatures">
-{if $properties.show_offset_information}
-<span>
+ {if $properties.show_offset_information}
+ <span>
<span class="dataTablePages"></span>
</span>
-{/if}
+ {/if}
-{if $properties.show_pagination_control}
-<span>
+ {if $properties.show_pagination_control}
+ <span>
<span class="dataTablePrevious">&lsaquo; {if isset($javascriptVariablesToSet.dataTablePreviousIsFirst)}{'General_First'|translate}{else}{'General_Previous'|translate}{/if} </span>
<span class="dataTableNext">{'General_Next'|translate} &rsaquo;</span>
</span>
-{/if}
+ {/if}
-{if $properties.show_search}
-<span class="dataTableSearchPattern">
- <input id="keyword" type="text" length="15" />
- <input type="submit" value="{'General_Search'|translate}" />
+ {if $properties.show_search}
+ <span class="dataTableSearchPattern">
+ <input id="keyword" type="text" length="15"/>
+ <input type="submit" value="{'General_Search'|translate}"/>
</span>
-{/if}
+ {/if}
-<span class="loadingPiwik" style='display:none'><img src="themes/default/images/loading-blue.gif" /> {'General_LoadingData'|translate}</span>
-{if $properties.show_footer_icons}
- <div class="dataTableFooterIcons">
- <div class="dataTableFooterWrap" var="{$javascriptVariablesToSet.viewDataTable}">
- {if !$properties.hide_all_views_icons}
- <img src="themes/default/images/data_table_footer_active_item.png" class="dataTableFooterActiveItem" />
- {/if}
- <div class="tableIconsGroup">
+ <span class="loadingPiwik" style='display:none'><img src="themes/default/images/loading-blue.gif"/> {'General_LoadingData'|translate}</span>
+ {if $properties.show_footer_icons}
+ <div class="dataTableFooterIcons">
+ <div class="dataTableFooterWrap" var="{$javascriptVariablesToSet.viewDataTable}">
+ {if !$properties.hide_all_views_icons}
+ <img src="themes/default/images/data_table_footer_active_item.png" class="dataTableFooterActiveItem"/>
+ {/if}
+ <div class="tableIconsGroup">
<span class="tableAllColumnsSwitch">
{if $properties.show_table}
- <a class="tableIcon" format="table" var="table"><img title="{'General_DisplaySimpleTable'|translate}" src="themes/default/images/table.png" /></a>
+ <a class="tableIcon" format="table" var="table"><img title="{'General_DisplaySimpleTable'|translate}"
+ src="themes/default/images/table.png"/></a>
{/if}
{if $properties.show_table_all_columns}
- <a class="tableIcon" format="tableAllColumns" var="tableAllColumns"><img title="{'General_DisplayTableWithMoreMetrics'|translate}" src="themes/default/images/table_more.png" /></a>
+ <a class="tableIcon" format="tableAllColumns" var="tableAllColumns"><img title="{'General_DisplayTableWithMoreMetrics'|translate}"
+ src="themes/default/images/table_more.png"/></a>
{/if}
{if $properties.show_goals}
- <a class="tableIcon" format="tableGoals" var="tableGoals"><img title="{'General_DisplayTableWithGoalMetrics'|translate}" src="themes/default/images/{if isset($javascriptVariablesToSet.idGoal) && $javascriptVariablesToSet.idGoal=='ecommerceOrder'}ecommerceOrder.gif{else}goal.png{/if}" /></a>
+ <a class="tableIcon" format="tableGoals" var="tableGoals"><img title="{'General_DisplayTableWithGoalMetrics'|translate}"
+ src="themes/default/images/{if isset($javascriptVariablesToSet.idGoal) && $javascriptVariablesToSet.idGoal=='ecommerceOrder'}ecommerceOrder.gif{else}goal.png{/if}"/></a>
{/if}
{if $properties.show_ecommerce}
- <a class="tableIcon" format="ecommerceOrder" var="ecommerceOrder"><img title="{'General_EcommerceOrders'|translate}" src="themes/default/images/ecommerceOrder.gif" /> <span>{'General_EcommerceOrders'|translate}</span></a>
- <a class="tableIcon" format="ecommerceAbandonedCart" var="ecommerceAbandonedCart"><img title="{'General_AbandonedCarts'|translate}" src="themes/default/images/ecommerceAbandonedCart.gif" /> <span>{'General_AbandonedCarts'|translate}</span></a>
+ <a class="tableIcon" format="ecommerceOrder" var="ecommerceOrder"><img title="{'General_EcommerceOrders'|translate}"
+ src="themes/default/images/ecommerceOrder.gif"/>
+ <span>{'General_EcommerceOrders'|translate}</span></a>
+ <a class="tableIcon" format="ecommerceAbandonedCart" var="ecommerceAbandonedCart"><img title="{'General_AbandonedCarts'|translate}"
+ src="themes/default/images/ecommerceAbandonedCart.gif"/>
+ <span>{'General_AbandonedCarts'|translate}</span></a>
{/if}
</span>
- </div>
- {if $properties.show_all_views_icons}
- <div class="tableIconsGroup">
+ </div>
+ {if $properties.show_all_views_icons}
+ <div class="tableIconsGroup">
<span class="tableGraphViews tableGraphCollapsed">
- {if $properties.show_bar_chart}<a class="tableIcon" format="graphVerticalBar" var="graphVerticalBar"><img width="16" height="16" src="themes/default/images/chart_bar.png" title="{'General_VBarGraph'|translate}" /></a>{/if}
- {if $properties.show_pie_chart}<a class="tableIcon" format="graphPie" var="graphPie"><img width="16" height="16" src="themes/default/images/chart_pie.png" title="{'General_Piechart'|translate}" /></a>{/if}
- {if $properties.show_tag_cloud}<a class="tableIcon" format="cloud" var="cloud"><img width="16" height="16" src="themes/default/images/tagcloud.png" title="{'General_TagCloud'|translate}" /></a>{/if}
+ {if $properties.show_bar_chart}<a class="tableIcon" format="graphVerticalBar" var="graphVerticalBar"><img width="16" height="16"
+ src="themes/default/images/chart_bar.png"
+ title="{'General_VBarGraph'|translate}"/>
+ </a>{/if}
+ {if $properties.show_pie_chart}<a class="tableIcon" format="graphPie" var="graphPie"><img width="16" height="16"
+ src="themes/default/images/chart_pie.png"
+ title="{'General_Piechart'|translate}"/></a>{/if}
+ {if $properties.show_tag_cloud}<a class="tableIcon" format="cloud" var="cloud"><img width="16" height="16"
+ src="themes/default/images/tagcloud.png"
+ title="{'General_TagCloud'|translate}"/></a>{/if}
</span>
- </div>
- {elseif !$properties.hide_all_views_icons && $javascriptVariablesToSet.viewDataTable == "generateDataChartEvolution"}
- <div class="tableIconsGroup">
+ </div>
+ {elseif !$properties.hide_all_views_icons && $javascriptVariablesToSet.viewDataTable == "generateDataChartEvolution"}
+ <div class="tableIconsGroup">
<span class="tableGraphViews">
- <a class="tableIcon" format="graphEvolution" var="graphEvolution"><img width="16" height="16" src="themes/default/images/chart_bar.png" title="{'General_VBarGraph'|translate}" /></a>
+ <a class="tableIcon" format="graphEvolution" var="graphEvolution"><img width="16" height="16" src="themes/default/images/chart_bar.png"
+ title="{'General_VBarGraph'|translate}"/></a>
</span>
- </div>
-
- {/if}
- <div class="tableIconsGroup">
- <span class="exportToFormatIcons"><a class="tableIcon" var="export"><img width="16" height="16" src="themes/default/images/export.png" title="{'General_ExportThisReport'|translate}" /></a></span>
+ </div>
+ {/if}
+ <div class="tableIconsGroup">
+ <span class="exportToFormatIcons"><a class="tableIcon" var="export"><img width="16" height="16" src="themes/default/images/export.png"
+ title="{'General_ExportThisReport'|translate}"/></a></span>
<span class="exportToFormatItems" style="display:none">
{'General_Export'|translate}:
<a target="_blank" methodToCall="{$properties.apiMethodToRequestDataTable}" format="CSV" filter_limit="{$properties.exportLimit}">CSV</a> |
- <a target="_blank" methodToCall="{$properties.apiMethodToRequestDataTable}" format="TSV" filter_limit="{$properties.exportLimit}">TSV (Excel)</a> |
+ <a target="_blank" methodToCall="{$properties.apiMethodToRequestDataTable}" format="TSV" filter_limit="{$properties.exportLimit}">TSV
+ (Excel)</a> |
<a target="_blank" methodToCall="{$properties.apiMethodToRequestDataTable}" format="XML" filter_limit="{$properties.exportLimit}">XML</a> |
<a target="_blank" methodToCall="{$properties.apiMethodToRequestDataTable}" format="JSON" filter_limit="{$properties.exportLimit}">Json</a> |
<a target="_blank" methodToCall="{$properties.apiMethodToRequestDataTable}" format="PHP" filter_limit="{$properties.exportLimit}">Php</a>
- {if $properties.show_export_as_rss_feed}
- | <a target="_blank" methodToCall="{$properties.apiMethodToRequestDataTable}" format="RSS" filter_limit="{$properties.exportLimit}" date="last10"><img border="0" src="themes/default/images/feed.png" /></a>
- {/if}
+ {if $properties.show_export_as_rss_feed}
+ |
+ <a target="_blank" methodToCall="{$properties.apiMethodToRequestDataTable}" format="RSS" filter_limit="{$properties.exportLimit}"
+ date="last10"><img border="0" src="themes/default/images/feed.png"/></a>
+ {/if}
</span>
- {if $properties.show_export_as_image_icon}
- <span id="dataTableFooterExportAsImageIcon">
- <a class="tableIcon" href="#" onclick="$('#{$chartDivId}').trigger('piwikExportAsImage'); return false;"><img title="{'General_ExportAsImage_js'|translate}" src="themes/default/images/image.png" /></a>
+ {if $properties.show_export_as_image_icon}
+ <span id="dataTableFooterExportAsImageIcon">
+ <a class="tableIcon" href="#" onclick="$('#{$chartDivId}').trigger('piwikExportAsImage'); return false;"><img
+ title="{'General_ExportAsImage_js'|translate}" src="themes/default/images/image.png"/></a>
</span>
- {/if}
- </div>
-
- </div>
- <div class="limitSelection {if !$properties.show_pagination_control && !$properties.show_limit_control} hidden{/if}" title="{'General_RowsToDisplay'|translate:escape:'html'}"></div>
- <div class="tableConfiguration">
- <a class="tableConfigurationIcon" href="#"></a>
- <ul>
- {if isset($javascriptVariablesToSet.flat) && $javascriptVariablesToSet.flat == 1}
- <li><div class="configItem dataTableIncludeAggregateRows"></div></li>
- {/if}
- <li><div class="configItem dataTableFlatten"></div></li>
- {if $properties.show_exclude_low_population}
- <li><div class="configItem dataTableExcludeLowPopulation"></div></li>
- {/if}
- </ul>
- </div>
- {if !$properties.hide_annotations_view}
- <div class="annotationView" title="{'Annotations_IconDesc_js'|translate}">
- <a class="tableIcon"><img width="16" height="16" src="themes/default/images/grey_marker.png"/></a>
- <span>{'Annotations_Annotations'|translate}</span>
- </div>
- {/if}
- </div>
-{/if}
+ {/if}
+ </div>
-<div class="datatableRelatedReports">
- {if !empty($properties.relatedReports) && (!empty($arrayDataTable) || !empty($cloudValues) || (isset($isDataAvailable) && $isDataAvailable)) && $properties.show_related_reports}
- {if count($properties.relatedReports) == 1}{'General_RelatedReport'|translate}{else}{'General_RelatedReports'|translate}{/if}:
- <ul style="list-style:none;{if count($properties.relatedReports) == 1}display:inline-block;{/if}">
- <li><span href="{$properties.self_url}" style="display:none;">{$properties.title}</span></li>
- {foreach from=$properties.relatedReports key=reportUrl item=reportTitle}
- <li><span href="{$reportUrl}">{$reportTitle}</span></li>
- {/foreach}
- </ul>
- {/if}
-</div>
+ </div>
+ <div class="limitSelection {if !$properties.show_pagination_control && !$properties.show_limit_control} hidden{/if}"
+ title="{'General_RowsToDisplay'|translate:escape:'html'}"></div>
+ <div class="tableConfiguration">
+ <a class="tableConfigurationIcon" href="#"></a>
+ <ul>
+ {if isset($javascriptVariablesToSet.flat) && $javascriptVariablesToSet.flat == 1}
+ <li>
+ <div class="configItem dataTableIncludeAggregateRows"></div>
+ </li>
+ {/if}
+ <li>
+ <div class="configItem dataTableFlatten"></div>
+ </li>
+ {if $properties.show_exclude_low_population}
+ <li>
+ <div class="configItem dataTableExcludeLowPopulation"></div>
+ </li>
+ {/if}
+ </ul>
+ </div>
+ {if !$properties.hide_annotations_view}
+ <div class="annotationView" title="{'Annotations_IconDesc_js'|translate}">
+ <a class="tableIcon"><img width="16" height="16" src="themes/default/images/grey_marker.png"/></a>
+ <span>{'Annotations_Annotations'|translate}</span>
+ </div>
+ {/if}
+ </div>
+ {/if}
+
+ <div class="datatableRelatedReports">
+ {if !empty($properties.relatedReports) && (!empty($arrayDataTable) || !empty($cloudValues) || (isset($isDataAvailable) && $isDataAvailable)) && $properties.show_related_reports}
+ {if count($properties.relatedReports) == 1}{'General_RelatedReport'|translate}{else}{'General_RelatedReports'|translate}{/if}:
+ <ul style="list-style:none;{if count($properties.relatedReports) == 1}display:inline-block;{/if}">
+ <li><span href="{$properties.self_url}" style="display:none;">{$properties.title}</span></li>
+ {foreach from=$properties.relatedReports key=reportUrl item=reportTitle}
+ <li><span href="{$reportUrl}">{$reportTitle}</span></li>
+ {/foreach}
+ </ul>
+ {/if}
+ </div>
-{if !empty($properties.show_footer_message)}
- <div class='datatableFooterMessage'>{$properties.show_footer_message}</div>
-{/if}
+ {if !empty($properties.show_footer_message)}
+ <div class='datatableFooterMessage'>{$properties.show_footer_message}</div>
+ {/if}
</div>
-<span class="loadingPiwikBelow" style='display:none'><img src="themes/default/images/loading-blue.gif" /> {'General_LoadingData'|translate}</span>
+<span class="loadingPiwikBelow" style='display:none'><img src="themes/default/images/loading-blue.gif"/> {'General_LoadingData'|translate}</span>
<div class="dataTableSpacer"></div>
diff --git a/plugins/CoreHome/templates/datatable_js.tpl b/plugins/CoreHome/templates/datatable_js.tpl
index 39fccf447d..ec434ec8cc 100644
--- a/plugins/CoreHome/templates/datatable_js.tpl
+++ b/plugins/CoreHome/templates/datatable_js.tpl
@@ -1,6 +1,7 @@
{if !isset($dataTableClassName)}{assign var=dataTableClassName value=dataTable}{/if}
<script type="text/javascript" defer="defer">
-$(document).ready(function(){literal}{{/literal}
- piwik.DataTableManager.initNewDataTables({$dataTableClassName});
-{literal}}{/literal});
+ $(document).ready(function () {literal}{{/literal}
+ piwik.DataTableManager.initNewDataTables({$dataTableClassName});
+ {literal}
+ }{/literal});
</script>
diff --git a/plugins/CoreHome/templates/datatable_manager.js b/plugins/CoreHome/templates/datatable_manager.js
index d5b48a7041..d7b5cc12e1 100644
--- a/plugins/CoreHome/templates/datatable_manager.js
+++ b/plugins/CoreHome/templates/datatable_manager.js
@@ -5,189 +5,175 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function($) {
-
- /**
- * The DataTableManager class manages the initialization of JS dataTable
- * instances. It's main purpose is to give each dataTable div a unique ID
- * when it's possible that any report will be loaded via AJAX, some more
- * than once.
- *
- * The singleton instance can be accessed via piwik.DataTableManager.
- */
- var DataTableManager = function()
- {
- this.nextId = 0;
- };
-
- DataTableManager.prototype = {
-
- /**
- * Gets the next available dataTable ID.
- *
- * @return {string}
- */
- getNextId: function()
- {
- this.nextId += 1;
- return 'dataTable_' + this.nextId;
- },
-
- /**
- * Gets the ID used for the last table or 0 if a DataTable hasn't been
- * initialized yet.
- */
- getLastId: function()
- {
- return 'dataTable_' + this.nextId;
- },
-
- /**
- * Initializes all uninitialized datatable elements. Uninitialized
- * datatable elements do not have an ID set.
- *
- * @param {Function} The DataTable's JS class.
- */
- initNewDataTables: function(klass)
- {
- var self = this;
-
- // find each datatable that hasn't been initialized (has no id attribute),
- // and initialize it
- $('div.dataTable').each(function() {
- if (!$(this).attr('id'))
- {
- var params = JSON.parse($(this).attr('data-params') || '{}');
-
- // convert values in params that are arrays to comma separated string lists
- for (var key in params)
- {
- if (params[key] instanceof Array)
- {
- params[key] = params[key].join(',');
- }
- }
-
- self.initSingleDataTable(this, klass, params);
- }
- });
- },
-
- /**
- * Initializes a single datatable element.
- *
- * @param {Element} domElem The DataTable div element.
- * @param {Function} klass The DataTable's JS class.
- * @param {Object} params The request params used.
- */
- initSingleDataTable: function(domElem, klass, params)
- {
- var newId = this.getNextId();
-
- $(domElem).attr('id', newId);
-
- var table = new klass();
- $(domElem).data('dataTableInstance', table);
-
- table.param = params;
- table.init(newId);
-
- // if the datatable has a graph, init the graph
- var graphElement = $('.piwik-graph', domElem);
- if (graphElement[0])
- {
- this.initJQPlotGraph(graphElement, newId);
- }
- },
-
- /**
- * Initializes and renders a JQPlot graph contained in a
- * dataTable.
- *
- * @param {Element} graphElement The empty graph div element. Will
- * usually have the .piwik-graph class.
- * @param {String} dataTableId The ID of the containing datatable.
- */
- initJQPlotGraph: function(graphElement, dataTableId)
- {
- graphElement = $(graphElement);
-
- // set a unique ID for the graph element
- var graphId = dataTableId + 'Chart';
- graphElement.attr('id', graphId);
-
- var graphData;
- try {
- graphData = JSON.parse(graphElement.attr('data-data'));
- } catch(e) {
- console.error('JSON.parse Error: "' + e + "\" in:\n" + graphElement.attr('data-data'));
- return;
- }
-
- var plot = new JQPlot(graphData, dataTableId);
-
- // add external series toggle if it should be added
- var externalSeriesToggle = graphElement.attr('data-external-series-toggle');
- if (externalSeriesToggle)
- {
- plot.addExternalSeriesToggle(
- window[externalSeriesToggle], // get the function w/ string name
- graphId,
- graphElement.attr('data-external-series-show-all') == 1
- );
- }
-
- // render the graph (setTimeout is required, otherwise the graph will not
- // render initially)
- setTimeout(function () {
- plot.render(graphElement.attr('data-graph-type'), graphId, {
- noData: _pk_translate('General_NoDataForGraph_js'),
- exportTitle: _pk_translate('General_ExportAsImage_js'),
- exportText: _pk_translate('General_SaveImageOnYourComputer_js'),
- metricsToPlot: _pk_translate('General_MetricsToPlot_js'),
- metricToPlot: _pk_translate('General_MetricToPlot_js'),
- recordsToPlot: _pk_translate('General_RecordsToPlot_js'),
- });
- }, 1);
- },
-
- /**
- * Returns the first datatable div displaying a specific report.
- *
- * @param {string} The report, eg, UserSettings.getWideScreen
- * @return {Element} The datatable div displaying the report, or undefined if
- * it cannot be found.
- */
- getDataTableByReport: function(report)
- {
- var reportWithoutDot = report.replace('.', '');
-
- var result = undefined;
- $('.dataTable').each(function() {
- if ($(this).attr('data-report') == reportWithoutDot)
- {
- result = this;
- return false;
- }
- });
- return result;
- },
-
- /**
- * Returns the datatable instance of the first datatable div displaying
- * a specific report.
- *
- * @param {string} The report, eg, UserSettings.getWideScrren
- * @return {DataTable} The DataTable instance created for the element, if
- * the element can be found. undefined, if it can't be found.
- */
- getDataTableInstanceByReport: function(report)
- {
- var dataTableElement = this.getDataTableByReport(report);
- return dataTableElement ? $(dataTableElement).data('dataTableInstance') : undefined;
- },
- };
-
- piwik.DataTableManager = new DataTableManager();
-
+(function ($) {
+
+ /**
+ * The DataTableManager class manages the initialization of JS dataTable
+ * instances. It's main purpose is to give each dataTable div a unique ID
+ * when it's possible that any report will be loaded via AJAX, some more
+ * than once.
+ *
+ * The singleton instance can be accessed via piwik.DataTableManager.
+ */
+ var DataTableManager = function () {
+ this.nextId = 0;
+ };
+
+ DataTableManager.prototype = {
+
+ /**
+ * Gets the next available dataTable ID.
+ *
+ * @return {string}
+ */
+ getNextId: function () {
+ this.nextId += 1;
+ return 'dataTable_' + this.nextId;
+ },
+
+ /**
+ * Gets the ID used for the last table or 0 if a DataTable hasn't been
+ * initialized yet.
+ */
+ getLastId: function () {
+ return 'dataTable_' + this.nextId;
+ },
+
+ /**
+ * Initializes all uninitialized datatable elements. Uninitialized
+ * datatable elements do not have an ID set.
+ *
+ * @param {Function} The DataTable's JS class.
+ */
+ initNewDataTables: function (klass) {
+ var self = this;
+
+ // find each datatable that hasn't been initialized (has no id attribute),
+ // and initialize it
+ $('div.dataTable').each(function () {
+ if (!$(this).attr('id')) {
+ var params = JSON.parse($(this).attr('data-params') || '{}');
+
+ // convert values in params that are arrays to comma separated string lists
+ for (var key in params) {
+ if (params[key] instanceof Array) {
+ params[key] = params[key].join(',');
+ }
+ }
+
+ self.initSingleDataTable(this, klass, params);
+ }
+ });
+ },
+
+ /**
+ * Initializes a single datatable element.
+ *
+ * @param {Element} domElem The DataTable div element.
+ * @param {Function} klass The DataTable's JS class.
+ * @param {Object} params The request params used.
+ */
+ initSingleDataTable: function (domElem, klass, params) {
+ var newId = this.getNextId();
+
+ $(domElem).attr('id', newId);
+
+ var table = new klass();
+ $(domElem).data('dataTableInstance', table);
+
+ table.param = params;
+ table.init(newId);
+
+ // if the datatable has a graph, init the graph
+ var graphElement = $('.piwik-graph', domElem);
+ if (graphElement[0]) {
+ this.initJQPlotGraph(graphElement, newId);
+ }
+ },
+
+ /**
+ * Initializes and renders a JQPlot graph contained in a
+ * dataTable.
+ *
+ * @param {Element} graphElement The empty graph div element. Will
+ * usually have the .piwik-graph class.
+ * @param {String} dataTableId The ID of the containing datatable.
+ */
+ initJQPlotGraph: function (graphElement, dataTableId) {
+ graphElement = $(graphElement);
+
+ // set a unique ID for the graph element
+ var graphId = dataTableId + 'Chart';
+ graphElement.attr('id', graphId);
+
+ var graphData;
+ try {
+ graphData = JSON.parse(graphElement.attr('data-data'));
+ } catch (e) {
+ console.error('JSON.parse Error: "' + e + "\" in:\n" + graphElement.attr('data-data'));
+ return;
+ }
+
+ var plot = new JQPlot(graphData, dataTableId);
+
+ // add external series toggle if it should be added
+ var externalSeriesToggle = graphElement.attr('data-external-series-toggle');
+ if (externalSeriesToggle) {
+ plot.addExternalSeriesToggle(
+ window[externalSeriesToggle], // get the function w/ string name
+ graphId,
+ graphElement.attr('data-external-series-show-all') == 1
+ );
+ }
+
+ // render the graph (setTimeout is required, otherwise the graph will not
+ // render initially)
+ setTimeout(function () {
+ plot.render(graphElement.attr('data-graph-type'), graphId, {
+ noData: _pk_translate('General_NoDataForGraph_js'),
+ exportTitle: _pk_translate('General_ExportAsImage_js'),
+ exportText: _pk_translate('General_SaveImageOnYourComputer_js'),
+ metricsToPlot: _pk_translate('General_MetricsToPlot_js'),
+ metricToPlot: _pk_translate('General_MetricToPlot_js'),
+ recordsToPlot: _pk_translate('General_RecordsToPlot_js'),
+ });
+ }, 1);
+ },
+
+ /**
+ * Returns the first datatable div displaying a specific report.
+ *
+ * @param {string} The report, eg, UserSettings.getWideScreen
+ * @return {Element} The datatable div displaying the report, or undefined if
+ * it cannot be found.
+ */
+ getDataTableByReport: function (report) {
+ var reportWithoutDot = report.replace('.', '');
+
+ var result = undefined;
+ $('.dataTable').each(function () {
+ if ($(this).attr('data-report') == reportWithoutDot) {
+ result = this;
+ return false;
+ }
+ });
+ return result;
+ },
+
+ /**
+ * Returns the datatable instance of the first datatable div displaying
+ * a specific report.
+ *
+ * @param {string} The report, eg, UserSettings.getWideScrren
+ * @return {DataTable} The DataTable instance created for the element, if
+ * the element can be found. undefined, if it can't be found.
+ */
+ getDataTableInstanceByReport: function (report) {
+ var dataTableElement = this.getDataTableByReport(report);
+ return dataTableElement ? $(dataTableElement).data('dataTableInstance') : undefined;
+ },
+ };
+
+ piwik.DataTableManager = new DataTableManager();
+
}(jQuery));
diff --git a/plugins/CoreHome/templates/datatable_rowactions.js b/plugins/CoreHome/templates/datatable_rowactions.js
index e28a676f55..afe31cc61b 100644
--- a/plugins/CoreHome/templates/datatable_rowactions.js
+++ b/plugins/CoreHome/templates/datatable_rowactions.js
@@ -13,100 +13,100 @@
*/
var DataTable_RowActions_Registry = {
- registry: [],
-
- register: function(action) {
- var createInstance = action.createInstance;
- action.createInstance = function(dataTable, param) {
- var instance = createInstance(dataTable, param);
- instance.actionName = action.name;
- return instance;
- };
-
- this.registry.push(action);
- },
-
- getAvailableActionsForReport: function(dataTableParams, tr) {
- if (dataTableParams.disable_row_actions == '1') {
- return [];
- }
-
- var available = [];
- for (var i = 0; i < this.registry.length; i++) {
- if (this.registry[i].isAvailableOnReport(dataTableParams, tr)) {
- available.push(this.registry[i]);
- }
- }
- available.sort(function(a, b) {
- return b.order - a.order;
- });
- return available;
- },
-
- getActionByName: function(name) {
- for (var i = 0; i < this.registry.length; i++) {
- if (this.registry[i].name == name) {
- return this.registry[i];
- }
- }
- return false;
- }
+ registry: [],
+
+ register: function (action) {
+ var createInstance = action.createInstance;
+ action.createInstance = function (dataTable, param) {
+ var instance = createInstance(dataTable, param);
+ instance.actionName = action.name;
+ return instance;
+ };
+
+ this.registry.push(action);
+ },
+
+ getAvailableActionsForReport: function (dataTableParams, tr) {
+ if (dataTableParams.disable_row_actions == '1') {
+ return [];
+ }
+
+ var available = [];
+ for (var i = 0; i < this.registry.length; i++) {
+ if (this.registry[i].isAvailableOnReport(dataTableParams, tr)) {
+ available.push(this.registry[i]);
+ }
+ }
+ available.sort(function (a, b) {
+ return b.order - a.order;
+ });
+ return available;
+ },
+
+ getActionByName: function (name) {
+ for (var i = 0; i < this.registry.length; i++) {
+ if (this.registry[i].name == name) {
+ return this.registry[i];
+ }
+ }
+ return false;
+ }
};
// Register Row Evolution (also servers as example)
DataTable_RowActions_Registry.register({
- name: 'RowEvolution',
-
- dataTableIcon: 'themes/default/images/row_evolution.png',
- dataTableIconHover: 'themes/default/images/row_evolution_hover.png',
-
- order: 50,
-
- dataTableIconTooltip: [
- _pk_translate('General_RowEvolutionRowActionTooltipTitle_js'),
- _pk_translate('General_RowEvolutionRowActionTooltip_js')
- ],
-
- createInstance: function(dataTable, param) {
- if (dataTable !== null && typeof dataTable.rowEvolutionActionInstance != 'undefined') {
- return dataTable.rowEvolutionActionInstance;
- }
-
- if (dataTable === null && param) {
- // when row evolution is triggered from the url (not a click on the data table)
- // we look for the data table instance in the dom
- var report = param.split(':')[0];
- var div = $(piwik.DataTableManager.getDataTableByReport(report));
- if (div.size() > 0 && div.data('piwikDataTable')) {
- dataTable = div.data('piwikDataTable');
- if (typeof dataTable.rowEvolutionActionInstance != 'undefined') {
- return dataTable.rowEvolutionActionInstance;
- }
- }
- }
-
- var instance = new DataTable_RowActions_RowEvolution(dataTable);
- if (dataTable !== null) {
- dataTable.rowEvolutionActionInstance = instance;
- }
- return instance;
- },
-
- isAvailableOnReport: function(dataTableParams) {
- return (
- typeof dataTableParams.disable_row_evolution == 'undefined'
- || dataTableParams.disable_row_evolution == "0"
- ) && (
- typeof dataTableParams.flat == 'undefined'
- || dataTableParams.flat == "0"
- );
- },
-
- isAvailableOnRow: function(dataTableParams, tr) {
- return true;
- }
+ name: 'RowEvolution',
+
+ dataTableIcon: 'themes/default/images/row_evolution.png',
+ dataTableIconHover: 'themes/default/images/row_evolution_hover.png',
+
+ order: 50,
+
+ dataTableIconTooltip: [
+ _pk_translate('General_RowEvolutionRowActionTooltipTitle_js'),
+ _pk_translate('General_RowEvolutionRowActionTooltip_js')
+ ],
+
+ createInstance: function (dataTable, param) {
+ if (dataTable !== null && typeof dataTable.rowEvolutionActionInstance != 'undefined') {
+ return dataTable.rowEvolutionActionInstance;
+ }
+
+ if (dataTable === null && param) {
+ // when row evolution is triggered from the url (not a click on the data table)
+ // we look for the data table instance in the dom
+ var report = param.split(':')[0];
+ var div = $(piwik.DataTableManager.getDataTableByReport(report));
+ if (div.size() > 0 && div.data('piwikDataTable')) {
+ dataTable = div.data('piwikDataTable');
+ if (typeof dataTable.rowEvolutionActionInstance != 'undefined') {
+ return dataTable.rowEvolutionActionInstance;
+ }
+ }
+ }
+
+ var instance = new DataTable_RowActions_RowEvolution(dataTable);
+ if (dataTable !== null) {
+ dataTable.rowEvolutionActionInstance = instance;
+ }
+ return instance;
+ },
+
+ isAvailableOnReport: function (dataTableParams) {
+ return (
+ typeof dataTableParams.disable_row_evolution == 'undefined'
+ || dataTableParams.disable_row_evolution == "0"
+ ) && (
+ typeof dataTableParams.flat == 'undefined'
+ || dataTableParams.flat == "0"
+ );
+ },
+
+ isAvailableOnRow: function (dataTableParams, tr) {
+ return true;
+ }
});
@@ -131,116 +131,116 @@ DataTable_RowActions_Registry.register({
//
function DataTable_RowAction(dataTable) {
- this.dataTable = dataTable;
+ this.dataTable = dataTable;
- // has to be overridden in subclasses
- this.trEventName = 'piwikTriggerRowAction';
+ // has to be overridden in subclasses
+ this.trEventName = 'piwikTriggerRowAction';
- // set in registry
- this.actionName = 'RowAction';
+ // set in registry
+ this.actionName = 'RowAction';
}
/** Initialize a row when the table is loaded */
-DataTable_RowAction.prototype.initTr = function(tr) {
- var self = this;
-
- // For subtables, we need to make sure that the actions are always triggered on the
- // action instance connected to the root table. Otherwise sharing data (e.g. for
- // for multi-row evolution) wouldn't be possible. Also, sub-tables might have different
- // API actions. For the label filter to work, we need to use the parent action.
- // We use jQuery events to let subtables access their parents.
- tr.bind(self.trEventName, function(e, params) {
- self.trigger($(this), params.originalEvent, params.label);
- });
+DataTable_RowAction.prototype.initTr = function (tr) {
+ var self = this;
+
+ // For subtables, we need to make sure that the actions are always triggered on the
+ // action instance connected to the root table. Otherwise sharing data (e.g. for
+ // for multi-row evolution) wouldn't be possible. Also, sub-tables might have different
+ // API actions. For the label filter to work, we need to use the parent action.
+ // We use jQuery events to let subtables access their parents.
+ tr.bind(self.trEventName, function (e, params) {
+ self.trigger($(this), params.originalEvent, params.label);
+ });
};
/**
* This method is called from the click event and the tr event (see this.trEventName).
* It derives the label and calls performAction.
*/
-DataTable_RowAction.prototype.trigger = function(tr, e, subTableLabel) {
- var label = this.getLabelFromTr(tr);
-
- label = label.trim();
- // if we have received the event from the sub table, add the label
- if (subTableLabel) {
- var separator = ' > '; // Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL
- label += separator + subTableLabel.trim();
- }
-
- // handle sub tables in nested reports: forward to parent
- var subtable = tr.closest('table');
- if (subtable.is('.subDataTable')) {
- subtable.closest('tr').prev().trigger(this.trEventName, {
- label: label,
- originalEvent: e
- });
- return;
- }
-
- // ascend in action reports
- if (tr.hasClass('actionsDataTable') || tr.hasClass('subActionsDataTable')) {
- var allClasses = tr.attr('class');
- var matches = allClasses.match(/level[0-9]+/);
- var level = parseInt(matches[0].substring(5, matches[0].length), 10);
- if (level > 0) {
- // .prev(.levelX) does not work for some reason => do it "by hand"
- var findLevel = 'level' + (level - 1);
- var ptr = tr;
- while ((ptr = ptr.prev()).size() > 0) {
- if (!ptr.hasClass(findLevel)) {
- continue;
- }
- ptr.trigger(this.trEventName, {
- label: label,
- originalEvent: e
- });
- return;
- }
- }
- }
-
- this.performAction(label, tr, e);
+DataTable_RowAction.prototype.trigger = function (tr, e, subTableLabel) {
+ var label = this.getLabelFromTr(tr);
+
+ label = label.trim();
+ // if we have received the event from the sub table, add the label
+ if (subTableLabel) {
+ var separator = ' > '; // Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL
+ label += separator + subTableLabel.trim();
+ }
+
+ // handle sub tables in nested reports: forward to parent
+ var subtable = tr.closest('table');
+ if (subtable.is('.subDataTable')) {
+ subtable.closest('tr').prev().trigger(this.trEventName, {
+ label: label,
+ originalEvent: e
+ });
+ return;
+ }
+
+ // ascend in action reports
+ if (tr.hasClass('actionsDataTable') || tr.hasClass('subActionsDataTable')) {
+ var allClasses = tr.attr('class');
+ var matches = allClasses.match(/level[0-9]+/);
+ var level = parseInt(matches[0].substring(5, matches[0].length), 10);
+ if (level > 0) {
+ // .prev(.levelX) does not work for some reason => do it "by hand"
+ var findLevel = 'level' + (level - 1);
+ var ptr = tr;
+ while ((ptr = ptr.prev()).size() > 0) {
+ if (!ptr.hasClass(findLevel)) {
+ continue;
+ }
+ ptr.trigger(this.trEventName, {
+ label: label,
+ originalEvent: e
+ });
+ return;
+ }
+ }
+ }
+
+ this.performAction(label, tr, e);
};
/** Get the label string from a tr dom element */
-DataTable_RowAction.prototype.getLabelFromTr = function(tr) {
- var label = tr.find('span.label');
+DataTable_RowAction.prototype.getLabelFromTr = function (tr) {
+ var label = tr.find('span.label');
- // handle truncation
- var value = label.data('originalText');
+ // handle truncation
+ var value = label.data('originalText');
- if (!value) {
- value = label.text();
- }
+ if (!value) {
+ value = label.text();
+ }
- return encodeURIComponent(value);
+ return encodeURIComponent(value);
};
/**
* Base method for opening popovers.
* This method will remember the parameter in the url and call doOpenPopover().
*/
-DataTable_RowAction.prototype.openPopover = function(parameter) {
- broadcast.propagateNewPopoverParameter('RowAction', this.actionName + ':' + parameter);
+DataTable_RowAction.prototype.openPopover = function (parameter) {
+ broadcast.propagateNewPopoverParameter('RowAction', this.actionName + ':' + parameter);
};
-broadcast.addPopoverHandler('RowAction', function(param) {
- var paramParts = param.split(':');
- var rowActionName = paramParts[0];
- paramParts.shift();
- param = paramParts.join(':');
+broadcast.addPopoverHandler('RowAction', function (param) {
+ var paramParts = param.split(':');
+ var rowActionName = paramParts[0];
+ paramParts.shift();
+ param = paramParts.join(':');
- var rowAction = DataTable_RowActions_Registry.getActionByName(rowActionName);
- if (rowAction) {
- rowAction.createInstance(null, param).doOpenPopover(param);
- }
+ var rowAction = DataTable_RowActions_Registry.getActionByName(rowActionName);
+ if (rowAction) {
+ rowAction.createInstance(null, param).doOpenPopover(param);
+ }
});
/** To be overridden */
-DataTable_RowAction.prototype.performAction = function(label, tr, e) {
+DataTable_RowAction.prototype.performAction = function (label, tr, e) {
};
-DataTable_RowAction.prototype.doOpenPopover = function(parameter) {
+DataTable_RowAction.prototype.doOpenPopover = function (parameter) {
};
@@ -249,129 +249,129 @@ DataTable_RowAction.prototype.doOpenPopover = function(parameter) {
//
function DataTable_RowActions_RowEvolution(dataTable) {
- this.dataTable = dataTable;
- this.trEventName = 'piwikTriggerRowEvolution';
+ this.dataTable = dataTable;
+ this.trEventName = 'piwikTriggerRowEvolution';
- /** The rows to be compared in multi row evolution */
- this.multiEvolutionRows = [];
+ /** The rows to be compared in multi row evolution */
+ this.multiEvolutionRows = [];
}
/** Static helper method to launch row evolution from anywhere */
-DataTable_RowActions_RowEvolution.launch = function(apiMethod, label) {
- var param = 'RowEvolution:' + apiMethod + ':0:' + label;
- broadcast.propagateNewPopoverParameter('RowAction', param);
+DataTable_RowActions_RowEvolution.launch = function (apiMethod, label) {
+ var param = 'RowEvolution:' + apiMethod + ':0:' + label;
+ broadcast.propagateNewPopoverParameter('RowAction', param);
};
DataTable_RowActions_RowEvolution.prototype = new DataTable_RowAction;
-DataTable_RowActions_RowEvolution.prototype.performAction = function(label, tr, e) {
- if (e.shiftKey) {
- // only mark for multi row evolution if shift key is pressed
- this.addMultiEvolutionRow(label);
- return;
- }
-
- // check whether we have rows marked for multi row evolution
- var isMultiRowEvolution = '0';
- this.addMultiEvolutionRow(label);
- if (this.multiEvolutionRows.length > 1) {
- isMultiRowEvolution = '1';
- label = this.multiEvolutionRows.join(',');
- }
-
- var apiMethod = this.dataTable.param.module + '.' + this.dataTable.param.action;
- this.openPopover(apiMethod, isMultiRowEvolution, label);
+DataTable_RowActions_RowEvolution.prototype.performAction = function (label, tr, e) {
+ if (e.shiftKey) {
+ // only mark for multi row evolution if shift key is pressed
+ this.addMultiEvolutionRow(label);
+ return;
+ }
+
+ // check whether we have rows marked for multi row evolution
+ var isMultiRowEvolution = '0';
+ this.addMultiEvolutionRow(label);
+ if (this.multiEvolutionRows.length > 1) {
+ isMultiRowEvolution = '1';
+ label = this.multiEvolutionRows.join(',');
+ }
+
+ var apiMethod = this.dataTable.param.module + '.' + this.dataTable.param.action;
+ this.openPopover(apiMethod, isMultiRowEvolution, label);
};
-DataTable_RowActions_RowEvolution.prototype.addMultiEvolutionRow = function(label) {
- if ($.inArray(label, this.multiEvolutionRows) == -1) {
- this.multiEvolutionRows.push(label);
- }
+DataTable_RowActions_RowEvolution.prototype.addMultiEvolutionRow = function (label) {
+ if ($.inArray(label, this.multiEvolutionRows) == -1) {
+ this.multiEvolutionRows.push(label);
+ }
};
-DataTable_RowActions_RowEvolution.prototype.openPopover = function(apiMethod, multiRowEvolutionParam, label) {
- var urlParam = apiMethod + ':' + multiRowEvolutionParam + ':' + label;
- DataTable_RowAction.prototype.openPopover.apply(this, [urlParam]);
+DataTable_RowActions_RowEvolution.prototype.openPopover = function (apiMethod, multiRowEvolutionParam, label) {
+ var urlParam = apiMethod + ':' + multiRowEvolutionParam + ':' + label;
+ DataTable_RowAction.prototype.openPopover.apply(this, [urlParam]);
};
-DataTable_RowActions_RowEvolution.prototype.doOpenPopover = function(urlParam) {
- var urlParamParts = urlParam.split(':');
+DataTable_RowActions_RowEvolution.prototype.doOpenPopover = function (urlParam) {
+ var urlParamParts = urlParam.split(':');
- var apiMethod = urlParamParts[0];
- urlParamParts.shift();
+ var apiMethod = urlParamParts[0];
+ urlParamParts.shift();
- var multiRowEvolutionParam = urlParamParts[0];
- urlParamParts.shift();
+ var multiRowEvolutionParam = urlParamParts[0];
+ urlParamParts.shift();
- var label = urlParamParts.join(':');
+ var label = urlParamParts.join(':');
- this.showRowEvolution(apiMethod, label, multiRowEvolutionParam);
+ this.showRowEvolution(apiMethod, label, multiRowEvolutionParam);
};
/** Open the row evolution popover */
-DataTable_RowActions_RowEvolution.prototype.showRowEvolution = function(apiMethod, label, multiRowEvolutionParam) {
-
- var self = this;
-
- // open the popover
- var box = Piwik_Popover.showLoading('Row Evolution');
- box.addClass('rowEvolutionPopover');
-
- // prepare loading the popover contents
- var requestParams = {
- apiMethod: apiMethod,
- label: label,
- disableLink: 1
- };
-
- // derive api action and requested column from multiRowEvolutionParam
- var action;
- if (multiRowEvolutionParam == '0') {
- action = 'getRowEvolutionPopover';
- } else if (multiRowEvolutionParam == '1') {
- action = 'getMultiRowEvolutionPopover';
- } else {
- action = 'getMultiRowEvolutionPopover';
- requestParams.column = multiRowEvolutionParam;
- }
-
- var callback = function(html) {
- Piwik_Popover.setContent(html);
-
- // use the popover title returned from the server
- var title = box.find('div.popover-title');
- if (title.size() > 0) {
- Piwik_Popover.setTitle(title.html());
- title.remove();
- }
-
- Piwik_Popover.onClose(function() {
- // reset rows marked for multi row evolution on close
- self.multiEvolutionRows = [];
- });
-
- if (self.dataTable !== null) {
- // remember label for multi row evolution
- box.find('a.rowevolution-startmulti').click(function() {
- Piwik_Popover.onClose(false); // unbind listener that resets multiEvolutionRows
- Piwik_Popover.close();
- return false;
- });
- } else {
- // when the popover is launched by copy&pasting a url, we don't have the data table.
- // in this case, we can't remember the row marked for multi row evolution so
- // we disable the picker.
- box.find('.compare-container, .rowevolution-startmulti').remove();
- }
-
- // switch metric in multi row evolution
- box.find('select.multirowevoltion-metric').change(function() {
- var metric = $(this).val();
- Piwik_Popover.onClose(false); // unbind listener that resets multiEvolutionRows
- self.openPopover(apiMethod, metric, label);
- return true;
- });
- };
+DataTable_RowActions_RowEvolution.prototype.showRowEvolution = function (apiMethod, label, multiRowEvolutionParam) {
+
+ var self = this;
+
+ // open the popover
+ var box = Piwik_Popover.showLoading('Row Evolution');
+ box.addClass('rowEvolutionPopover');
+
+ // prepare loading the popover contents
+ var requestParams = {
+ apiMethod: apiMethod,
+ label: label,
+ disableLink: 1
+ };
+
+ // derive api action and requested column from multiRowEvolutionParam
+ var action;
+ if (multiRowEvolutionParam == '0') {
+ action = 'getRowEvolutionPopover';
+ } else if (multiRowEvolutionParam == '1') {
+ action = 'getMultiRowEvolutionPopover';
+ } else {
+ action = 'getMultiRowEvolutionPopover';
+ requestParams.column = multiRowEvolutionParam;
+ }
+
+ var callback = function (html) {
+ Piwik_Popover.setContent(html);
+
+ // use the popover title returned from the server
+ var title = box.find('div.popover-title');
+ if (title.size() > 0) {
+ Piwik_Popover.setTitle(title.html());
+ title.remove();
+ }
+
+ Piwik_Popover.onClose(function () {
+ // reset rows marked for multi row evolution on close
+ self.multiEvolutionRows = [];
+ });
+
+ if (self.dataTable !== null) {
+ // remember label for multi row evolution
+ box.find('a.rowevolution-startmulti').click(function () {
+ Piwik_Popover.onClose(false); // unbind listener that resets multiEvolutionRows
+ Piwik_Popover.close();
+ return false;
+ });
+ } else {
+ // when the popover is launched by copy&pasting a url, we don't have the data table.
+ // in this case, we can't remember the row marked for multi row evolution so
+ // we disable the picker.
+ box.find('.compare-container, .rowevolution-startmulti').remove();
+ }
+
+ // switch metric in multi row evolution
+ box.find('select.multirowevoltion-metric').change(function () {
+ var metric = $(this).val();
+ Piwik_Popover.onClose(false); // unbind listener that resets multiEvolutionRows
+ self.openPopover(apiMethod, metric, label);
+ return true;
+ });
+ };
requestParams.module = 'CoreHome';
requestParams.action = action;
diff --git a/plugins/CoreHome/templates/date.js b/plugins/CoreHome/templates/date.js
index 2715113740..1c6c5971bd 100644
--- a/plugins/CoreHome/templates/date.js
+++ b/plugins/CoreHome/templates/date.js
@@ -5,43 +5,42 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-$(document).ready(function(){
-
- //period widget handler
- var periodWidget={
- show:function(){
- this.isOpen=1;
- $("#periodMore").show();
- },
- hide:function(){
- this.isOpen=0;
- $("#periodMore").hide();
- },
- toggle:function(e){
- if(!this.isOpen) this.show();
- else this.hide();
- }
- };
+$(document).ready(function () {
- $("#periodString #date")
- .hover( function(){
- $(this).css({ cursor: "pointer"});
- }, function(){
-
- })
- .click(function(){
- periodWidget.toggle();
- if($("#periodMore").is(":visible"))
- {
- $("#periodMore .ui-state-highlight").removeClass('ui-state-highlight');
- }
- });
-
- //close periodString onClickOutside
- $('body').on('mouseup',function(e){
- if(!$(e.target).parents('#periodString').length && !$(e.target).is('#periodString') && !$(e.target).is('option') && periodWidget.isOpen) {
- periodWidget.hide();
- }
- });
-
-} );
+ //period widget handler
+ var periodWidget = {
+ show: function () {
+ this.isOpen = 1;
+ $("#periodMore").show();
+ },
+ hide: function () {
+ this.isOpen = 0;
+ $("#periodMore").hide();
+ },
+ toggle: function (e) {
+ if (!this.isOpen) this.show();
+ else this.hide();
+ }
+ };
+
+ $("#periodString #date")
+ .hover(function () {
+ $(this).css({ cursor: "pointer"});
+ }, function () {
+
+ })
+ .click(function () {
+ periodWidget.toggle();
+ if ($("#periodMore").is(":visible")) {
+ $("#periodMore .ui-state-highlight").removeClass('ui-state-highlight');
+ }
+ });
+
+ //close periodString onClickOutside
+ $('body').on('mouseup', function (e) {
+ if (!$(e.target).parents('#periodString').length && !$(e.target).is('#periodString') && !$(e.target).is('option') && periodWidget.isOpen) {
+ periodWidget.hide();
+ }
+ });
+
+});
diff --git a/plugins/CoreHome/templates/donate.css b/plugins/CoreHome/templates/donate.css
index a805ac5538..cff3068992 100755
--- a/plugins/CoreHome/templates/donate.css
+++ b/plugins/CoreHome/templates/donate.css
@@ -1,126 +1,126 @@
.piwik-donate-call {
- padding: 1em 1em 1em 1em;
- border: 1px solid #CCC;
-
- border-radius:4px;
- -moz-border-radius:4px;
- -webkit-border-radius:4px;
-
- max-width: 432px;
- position:relative;
+ padding: 1em 1em 1em 1em;
+ border: 1px solid #CCC;
+
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+
+ max-width: 432px;
+ position: relative;
}
#piwik-worth {
- font-size:1.2em;
- font-weight:bold;
- font-style:italic;
- display:block;
- margin: 0 1em 0 1em;
+ font-size: 1.2em;
+ font-weight: bold;
+ font-style: italic;
+ display: block;
+ margin: 0 1em 0 1em;
}
.piwik-donate-slider {
- height:56px;
- margin: 1em 0 1em 1em;
+ height: 56px;
+ margin: 1em 0 1em 1em;
}
.piwik-donate-slider > .slider-range {
- vertical-align:top;
- position:relative;
- display: inline-block;
- border: 1px solid #999;
-
- background-color: #f7f7f7;
-
- border-radius:6px;
- -moz-border-radius:6px;
- -webkit-border-radius:6px;
-
- height: 14px;
- width: 270px;
- margin:22px 8px 0 0;
- cursor:pointer;
+ vertical-align: top;
+ position: relative;
+ display: inline-block;
+ border: 1px solid #999;
+
+ background-color: #f7f7f7;
+
+ border-radius: 6px;
+ -moz-border-radius: 6px;
+ -webkit-border-radius: 6px;
+
+ height: 14px;
+ width: 270px;
+ margin: 22px 8px 0 0;
+ cursor: pointer;
}
.piwik-donate-slider .slider-position {
- border: 1px solid #999;
- background-color:#CCC;
+ border: 1px solid #999;
+ background-color: #CCC;
+
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
- border-radius:3px;
- -moz-border-radius:3px;
- -webkit-border-radius:3px;
-
- height:18px;
- width:10px;
-
- position:absolute;
- top:-3px;
- left:-1px;
+ height: 18px;
+ width: 10px;
+
+ position: absolute;
+ top: -3px;
+ left: -1px;
}
.piwik-donate-slider .slider-donate-amount {
- display:inline-block;
-
- padding: .3em .5em .3em .5em;
- margin:16px 8px 0 0;
- vertical-align:top;
- width:48px;
- text-align:center;
- background-color: #CCC;
- color:#333;
- cursor:pointer;
+ display: inline-block;
+
+ padding: .3em .5em .3em .5em;
+ margin: 16px 8px 0 0;
+ vertical-align: top;
+ width: 48px;
+ text-align: center;
+ background-color: #CCC;
+ color: #333;
+ cursor: pointer;
}
.piwik-donate-slider .slider-smiley-face {
- margin:8px 0 8px 0;
- display:inline-block;
- cursor:pointer;
+ margin: 8px 0 8px 0;
+ display: inline-block;
+ cursor: pointer;
}
.piwik-donate-call .donate-submit {
- height:55px;
- position:relative;
+ height: 55px;
+ position: relative;
}
.piwik-donate-call .donate-submit input {
- margin-left:13px;
- border-style:none;
- background-image:none;
- padding:0;
+ margin-left: 13px;
+ border-style: none;
+ background-image: none;
+ padding: 0;
}
.piwik-donate-call .donate-submit a {
- display:inline-block;
- position:absolute;
- bottom:.5em;
- right:1.2em;
- margin-left:1.2em;
- font-size:1em;
- font-style:italic;
+ display: inline-block;
+ position: absolute;
+ bottom: .5em;
+ right: 1.2em;
+ margin-left: 1.2em;
+ font-size: 1em;
+ font-style: italic;
}
.piwik-donate-call > .piwik-donate-message {
- margin-bottom:.5em;
+ margin-bottom: .5em;
}
.piwik-donate-call > .piwik-donate-message p {
- margin-left: 1em;
+ margin-left: 1em;
}
.piwik-donate-call > .form-description {
- margin-top:1.25em;
+ margin-top: 1.25em;
}
.donate-form-instructions {
- font-size:.8em;
- margin: 0 1.25em 0 1.25em;
- color:#666;
- font-style:italic;
+ font-size: .8em;
+ margin: 0 1.25em 0 1.25em;
+ color: #666;
+ font-style: italic;
}
.widget .piwik-donate-call {
- border-style:none;
+ border-style: none;
}
.widget .piwik-donate-slider > .slider-range {
- width: 240px;
+ width: 240px;
}
diff --git a/plugins/CoreHome/templates/donate.js b/plugins/CoreHome/templates/donate.js
index 2e667ccf8a..f6348db41c 100755
--- a/plugins/CoreHome/templates/donate.js
+++ b/plugins/CoreHome/templates/donate.js
@@ -4,153 +4,139 @@
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function($) {
-
-$(document).ready(function() {
-
- var donateAmounts = [0, 30, 60, 90, 120];
-
- // returns the space between each donation amount in the donation slider
- var getTickWidth = function(slider)
- {
- var effectiveSliderWidth = $('.slider-range', slider).width() - $('.slider-position', slider).width();
- return effectiveSliderWidth / (donateAmounts.length - 1);
- };
-
- // returns the position index on a slider based on a x coordinate value
- var getPositionFromPageCoord = function(slider, pageX)
- {
- return Math.round((pageX - $('.slider-range', slider).offset().left) / getTickWidth(slider));
- };
-
- // set's the correct amount text & smiley face image based on the position of the slider
- var setSmileyFaceAndAmount = function(slider, pos)
- {
- // set text yearly amount
- $('.slider-donate-amount', slider).text('$' + donateAmounts[pos] + '/' + _pk_translate('General_YearShort_js'));
-
- // set the right smiley face
- $('.slider-smiley-face').attr('src', 'themes/default/images/smileyprog_' + pos + '.png');
-
- // set the hidden option input for paypal
- var option = Math.max(1, pos);
- $('.piwik-donate-call input[name=os0]').val("Option " + option);
- };
-
- // move's a slider's position to a specific spot
- var moveSliderPosition = function(slider, to)
- {
- // make sure 'to' is valid
- if (to < 0)
- {
- to = 0;
- }
- else if (to >= donateAmounts.length)
- {
- to = donateAmounts.length - 1;
- }
-
- // set the slider position
- var left = to * getTickWidth(slider);
- if (left == 0)
- {
- left = -1; // at position 0 we move one pixel left to cover up some of slider bar
- }
-
- $('.slider-position', slider).css({
- left: left + 'px'
- });
-
- // reset the smiley face & amount based on the new position
- setSmileyFaceAndAmount(slider, to);
- };
-
- // when a slider is clicked, set the amount & smiley face appropriately
- $('body').on('click', '.piwik-donate-slider>.slider-range', function(e) {
- var slider = $(this).parent(),
- currentPageX = $('.slider-position', this).offset().left,
- currentPos = getPositionFromPageCoord(slider, currentPageX),
- pos = getPositionFromPageCoord(slider, e.pageX);
-
- // if the closest position is the current one, use the other position since
- // the user obviously wants to move the slider.
- if (currentPos == pos)
- {
- // if click is to right, go forward one, else backwards one
- if (e.pageX > currentPageX)
- {
- ++pos;
- }
- else
- {
- --pos;
- }
- }
-
- moveSliderPosition(slider, pos);
- });
-
- // when the smiley icon is clicked, move the position up one to demonstrate how to use the slider
- $('body').on('click', '.piwik-donate-slider .slider-smiley-face,.piwik-donate-slider .slider-donate-amount',
- function(e) {
- var slider = $(this).closest('.piwik-donate-slider'),
- currentPageX = $('.slider-position', slider).offset().left,
- currentPos = getPositionFromPageCoord(slider, currentPageX);
-
- moveSliderPosition(slider, currentPos + 1);
- }
- );
-
- // stores the current slider being dragged
- var draggingSlider = false;
-
- // start dragging on mousedown for a slider's position bar
- $('body').on('mousedown', '.piwik-donate-slider .slider-position', function () {
- draggingSlider = $(this).parent().parent();
- });
-
- // move the slider position if currently dragging when the mouse moves anywhere over the entire page
- $('body').on('mousemove', function(e) {
- if (draggingSlider)
- {
- var slider = draggingSlider.find('.slider-range'),
- sliderPos = slider.find('.slider-position'),
- left = e.pageX - slider.offset().left;
-
- // only move slider if the mouse x-coord is still on the slider (w/ some padding for borders)
- if (left <= slider.width() - sliderPos.width() + 2
- && left >= -2)
- {
- sliderPos.css({left: left + 'px'});
-
- var closestPos = Math.round(left / getTickWidth(draggingSlider));
- setSmileyFaceAndAmount(draggingSlider, closestPos);
- }
- }
- });
-
- // stop dragging and normalize a slider's position on mouseup over the entire page
- $('body').on('mouseup', function() {
- if (draggingSlider)
- {
- var sliderPos = $('.slider-position', draggingSlider),
- slider = sliderPos.parent();
-
- if (sliderPos.length)
- {
- // move the slider to the nearest donation amount position
- var pos = getPositionFromPageCoord(draggingSlider, sliderPos.offset().left);
- moveSliderPosition(draggingSlider, pos);
- }
-
- draggingSlider = false; // stop dragging
- }
- });
-
- // event for programatically changing the position
- $('body').on('piwik:changePosition', '.piwik-donate-slider', function(e, data) {
- moveSliderPosition(this, data.position);
- });
-});
+(function ($) {
+
+ $(document).ready(function () {
+
+ var donateAmounts = [0, 30, 60, 90, 120];
+
+ // returns the space between each donation amount in the donation slider
+ var getTickWidth = function (slider) {
+ var effectiveSliderWidth = $('.slider-range', slider).width() - $('.slider-position', slider).width();
+ return effectiveSliderWidth / (donateAmounts.length - 1);
+ };
+
+ // returns the position index on a slider based on a x coordinate value
+ var getPositionFromPageCoord = function (slider, pageX) {
+ return Math.round((pageX - $('.slider-range', slider).offset().left) / getTickWidth(slider));
+ };
+
+ // set's the correct amount text & smiley face image based on the position of the slider
+ var setSmileyFaceAndAmount = function (slider, pos) {
+ // set text yearly amount
+ $('.slider-donate-amount', slider).text('$' + donateAmounts[pos] + '/' + _pk_translate('General_YearShort_js'));
+
+ // set the right smiley face
+ $('.slider-smiley-face').attr('src', 'themes/default/images/smileyprog_' + pos + '.png');
+
+ // set the hidden option input for paypal
+ var option = Math.max(1, pos);
+ $('.piwik-donate-call input[name=os0]').val("Option " + option);
+ };
+
+ // move's a slider's position to a specific spot
+ var moveSliderPosition = function (slider, to) {
+ // make sure 'to' is valid
+ if (to < 0) {
+ to = 0;
+ }
+ else if (to >= donateAmounts.length) {
+ to = donateAmounts.length - 1;
+ }
+
+ // set the slider position
+ var left = to * getTickWidth(slider);
+ if (left == 0) {
+ left = -1; // at position 0 we move one pixel left to cover up some of slider bar
+ }
+
+ $('.slider-position', slider).css({
+ left: left + 'px'
+ });
+
+ // reset the smiley face & amount based on the new position
+ setSmileyFaceAndAmount(slider, to);
+ };
+
+ // when a slider is clicked, set the amount & smiley face appropriately
+ $('body').on('click', '.piwik-donate-slider>.slider-range', function (e) {
+ var slider = $(this).parent(),
+ currentPageX = $('.slider-position', this).offset().left,
+ currentPos = getPositionFromPageCoord(slider, currentPageX),
+ pos = getPositionFromPageCoord(slider, e.pageX);
+
+ // if the closest position is the current one, use the other position since
+ // the user obviously wants to move the slider.
+ if (currentPos == pos) {
+ // if click is to right, go forward one, else backwards one
+ if (e.pageX > currentPageX) {
+ ++pos;
+ }
+ else {
+ --pos;
+ }
+ }
+
+ moveSliderPosition(slider, pos);
+ });
+
+ // when the smiley icon is clicked, move the position up one to demonstrate how to use the slider
+ $('body').on('click', '.piwik-donate-slider .slider-smiley-face,.piwik-donate-slider .slider-donate-amount',
+ function (e) {
+ var slider = $(this).closest('.piwik-donate-slider'),
+ currentPageX = $('.slider-position', slider).offset().left,
+ currentPos = getPositionFromPageCoord(slider, currentPageX);
+
+ moveSliderPosition(slider, currentPos + 1);
+ }
+ );
+
+ // stores the current slider being dragged
+ var draggingSlider = false;
+
+ // start dragging on mousedown for a slider's position bar
+ $('body').on('mousedown', '.piwik-donate-slider .slider-position', function () {
+ draggingSlider = $(this).parent().parent();
+ });
+
+ // move the slider position if currently dragging when the mouse moves anywhere over the entire page
+ $('body').on('mousemove', function (e) {
+ if (draggingSlider) {
+ var slider = draggingSlider.find('.slider-range'),
+ sliderPos = slider.find('.slider-position'),
+ left = e.pageX - slider.offset().left;
+
+ // only move slider if the mouse x-coord is still on the slider (w/ some padding for borders)
+ if (left <= slider.width() - sliderPos.width() + 2
+ && left >= -2) {
+ sliderPos.css({left: left + 'px'});
+
+ var closestPos = Math.round(left / getTickWidth(draggingSlider));
+ setSmileyFaceAndAmount(draggingSlider, closestPos);
+ }
+ }
+ });
+
+ // stop dragging and normalize a slider's position on mouseup over the entire page
+ $('body').on('mouseup', function () {
+ if (draggingSlider) {
+ var sliderPos = $('.slider-position', draggingSlider),
+ slider = sliderPos.parent();
+
+ if (sliderPos.length) {
+ // move the slider to the nearest donation amount position
+ var pos = getPositionFromPageCoord(draggingSlider, sliderPos.offset().left);
+ moveSliderPosition(draggingSlider, pos);
+ }
+
+ draggingSlider = false; // stop dragging
+ }
+ });
+
+ // event for programatically changing the position
+ $('body').on('piwik:changePosition', '.piwik-donate-slider', function (e, data) {
+ moveSliderPosition(this, data.position);
+ });
+ });
}(jQuery));
diff --git a/plugins/CoreHome/templates/donate.tpl b/plugins/CoreHome/templates/donate.tpl
index 6f33cc0470..e94a1d1221 100755
--- a/plugins/CoreHome/templates/donate.tpl
+++ b/plugins/CoreHome/templates/donate.tpl
@@ -1,61 +1,64 @@
<div class="piwik-donate-call">
- <div class="piwik-donate-message">
- {if isset($msg)}
- {$msg}
- {else}
- <p>{'CoreHome_DonateCall1'|translate}</p>
- <p><strong><em>{'CoreHome_DonateCall2'|translate}</em></strong></p>
- <p>{'CoreHome_DonateCall3'|translate:'<em><strong>':'</strong></em>'}</p>
- {/if}
- </div>
-
- <span id="piwik-worth">{'CoreHome_HowMuchIsPiwikWorth'|translate}</span>
- <div class="donate-form-instructions">({'CoreHome_DonateFormInstructions'|translate})</div>
-
- <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
- <input type="hidden" name="cmd" value="_s-xclick"/>
- <input type="hidden" name="hosted_button_id" value="DVKLY73RS7JTE"/>
- <input type="hidden" name="currency_code" value="USD"/>
- <input type="hidden" name="on0" value="Piwik Supporter"/>
-
- <div class="piwik-donate-slider">
- <div class="slider-range">
- <div class="slider-position"></div>
- </div>
- <div style="display:inline-block">
- <div class="slider-donate-amount">$30/{'General_YearShort_js'|translate}</div>
-
- <img class="slider-smiley-face" width="40" height="40" src="themes/default/images/smileyprog_1.png"/>
- </div>
-
- <input type="hidden" name="os0" value="Option 1"/>
- </div>
-
- <div class="donate-submit">
- <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RPL23NJURMTFA&bb2_screener_=1357583494+83.233.186.82" target="_blank">{'CoreHome_MakeOneTimeDonation'|translate}</a>
- <input type="image" src="themes/default/images/paypal_subscribe.gif" border="0" name="submit" title="{'CoreHome_SubscribeAndBecomePiwikSupporter'|translate}"/>
- </div>
-
- <!-- to cache images -->
- <img style="display:none" src="themes/default/images/smileyprog_0.png"/>
- <img style="display:none" src="themes/default/images/smileyprog_1.png"/>
- <img style="display:none" src="themes/default/images/smileyprog_2.png"/>
- <img style="display:none" src="themes/default/images/smileyprog_3.png"/>
- <img style="display:none" src="themes/default/images/smileyprog_4.png"/>
- </form>
- {if isset($footerMessage)}
- <div class="form-description">
- {$footerMessage}
- </div>
- {/if}
+ <div class="piwik-donate-message">
+ {if isset($msg)}
+ {$msg}
+ {else}
+ <p>{'CoreHome_DonateCall1'|translate}</p>
+ <p><strong><em>{'CoreHome_DonateCall2'|translate}</em></strong></p>
+ <p>{'CoreHome_DonateCall3'|translate:'<em><strong>':'</strong></em>'}</p>
+ {/if}
+ </div>
+
+ <span id="piwik-worth">{'CoreHome_HowMuchIsPiwikWorth'|translate}</span>
+
+ <div class="donate-form-instructions">({'CoreHome_DonateFormInstructions'|translate})</div>
+
+ <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
+ <input type="hidden" name="cmd" value="_s-xclick"/>
+ <input type="hidden" name="hosted_button_id" value="DVKLY73RS7JTE"/>
+ <input type="hidden" name="currency_code" value="USD"/>
+ <input type="hidden" name="on0" value="Piwik Supporter"/>
+
+ <div class="piwik-donate-slider">
+ <div class="slider-range">
+ <div class="slider-position"></div>
+ </div>
+ <div style="display:inline-block">
+ <div class="slider-donate-amount">$30/{'General_YearShort_js'|translate}</div>
+
+ <img class="slider-smiley-face" width="40" height="40" src="themes/default/images/smileyprog_1.png"/>
+ </div>
+
+ <input type="hidden" name="os0" value="Option 1"/>
+ </div>
+
+ <div class="donate-submit">
+ <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RPL23NJURMTFA&bb2_screener_=1357583494+83.233.186.82"
+ target="_blank">{'CoreHome_MakeOneTimeDonation'|translate}</a>
+ <input type="image" src="themes/default/images/paypal_subscribe.gif" border="0" name="submit"
+ title="{'CoreHome_SubscribeAndBecomePiwikSupporter'|translate}"/>
+ </div>
+
+ <!-- to cache images -->
+ <img style="display:none" src="themes/default/images/smileyprog_0.png"/>
+ <img style="display:none" src="themes/default/images/smileyprog_1.png"/>
+ <img style="display:none" src="themes/default/images/smileyprog_2.png"/>
+ <img style="display:none" src="themes/default/images/smileyprog_3.png"/>
+ <img style="display:none" src="themes/default/images/smileyprog_4.png"/>
+ </form>
+ {if isset($footerMessage)}
+ <div class="form-description">
+ {$footerMessage}
+ </div>
+ {/if}
</div>
{literal}
-<script type="text/javascript">
-$(document).ready(function() {
- // Note: this will cause problems if more than one donate form is on the page
- $('.piwik-donate-slider').each(function() {
- $(this).trigger('piwik:changePosition', {position: 1});
- });
-});
-</script>
+ <script type="text/javascript">
+ $(document).ready(function () {
+ // Note: this will cause problems if more than one donate form is on the page
+ $('.piwik-donate-slider').each(function () {
+ $(this).trigger('piwik:changePosition', {position: 1});
+ });
+ });
+ </script>
{/literal}
diff --git a/plugins/CoreHome/templates/footer.tpl b/plugins/CoreHome/templates/footer.tpl
index d4e06c6e0c..e7a1377624 100644
--- a/plugins/CoreHome/templates/footer.tpl
+++ b/plugins/CoreHome/templates/footer.tpl
@@ -1,6 +1,6 @@
</div> {* <div id="root"> *}
- {include file="CoreHome/templates/piwik_tag.tpl"}
+{include file="CoreHome/templates/piwik_tag.tpl"}
</body>
</html>
diff --git a/plugins/CoreHome/templates/graph.tpl b/plugins/CoreHome/templates/graph.tpl
index e3483ac9b2..5f46b2f32f 100644
--- a/plugins/CoreHome/templates/graph.tpl
+++ b/plugins/CoreHome/templates/graph.tpl
@@ -1,42 +1,40 @@
<div class="dataTable" data-report="{$properties.uniqueId}" data-params="{$javascriptVariablesToSet|@json_encode|escape:'html'}">
-
- <div class="reportDocumentation">
- {if !empty($reportDocumentation)}<p>{$reportDocumentation}</p>{/if}
- {if isset($properties.metadata.archived_date)}<p>{$properties.metadata.archived_date}</p>{/if}
- </div>
-
- <div class="{if $graphType=='evolution'}dataTableGraphEvolutionWrapper{else}dataTableGraphWrapper{/if}">
- {if $isDataAvailable}
-
- <div class="jqplot-{$graphType}" style="padding-left: 6px;">
- <div class="piwik-graph"
- style="position: relative; width: {$width}{if substr($width, -1) != '%'}px{/if}; height: {$height}{if substr($height, -1) != '%'}px{/if};"
- data-data="{$data|escape:'html'}"
- data-graph-type="{$graphType|escape:'html'}"
- {if isset($properties.externalSeriesToggle) && $properties.externalSeriesToggle}
- data-external-series-toggle="{$properties.externalSeriesToggle|escape:'html'}"
- data-external-series-show-all="{if $properties.externalSeriesToggleShowAll}1{else}0{/if}"
- {/if}>
- </div>
- </div>
-
- {else}
-
- <div><div class="pk-emptyGraph">
- {if $showReportDataWasPurgedMessage}
- {'General_DataForThisGraphHasBeenPurged'|translate:$deleteReportsOlderThan}
- {else}
- {'General_NoDataForGraph_js'|translate}
- {/if}
- </div></div>
-
- {/if}
+ <div class="reportDocumentation">
+ {if !empty($reportDocumentation)}<p>{$reportDocumentation}</p>{/if}
+ {if isset($properties.metadata.archived_date)}<p>{$properties.metadata.archived_date}</p>{/if}
+ </div>
- {if $properties.show_footer}
- {include file="CoreHome/templates/datatable_footer.tpl"}
- {include file="CoreHome/templates/datatable_js.tpl"}
- {/if}
-
- </div>
+ <div class="{if $graphType=='evolution'}dataTableGraphEvolutionWrapper{else}dataTableGraphWrapper{/if}">
+
+ {if $isDataAvailable}
+ <div class="jqplot-{$graphType}" style="padding-left: 6px;">
+ <div class="piwik-graph"
+ style="position: relative; width: {$width}{if substr($width, -1) != '%'}px{/if}; height: {$height}{if substr($height, -1) != '%'}px{/if};"
+ data-data="{$data|escape:'html'}"
+ data-graph-type="{$graphType|escape:'html'}"
+ {if isset($properties.externalSeriesToggle) && $properties.externalSeriesToggle}
+ data-external-series-toggle="{$properties.externalSeriesToggle|escape:'html'}"
+ data-external-series-show-all="{if $properties.externalSeriesToggleShowAll}1{else}0{/if}"
+ {/if}>
+ </div>
+ </div>
+ {else}
+ <div>
+ <div class="pk-emptyGraph">
+ {if $showReportDataWasPurgedMessage}
+ {'General_DataForThisGraphHasBeenPurged'|translate:$deleteReportsOlderThan}
+ {else}
+ {'General_NoDataForGraph_js'|translate}
+ {/if}
+ </div>
+ </div>
+ {/if}
+
+ {if $properties.show_footer}
+ {include file="CoreHome/templates/datatable_footer.tpl"}
+ {include file="CoreHome/templates/datatable_js.tpl"}
+ {/if}
+
+ </div>
</div>
diff --git a/plugins/CoreHome/templates/header.tpl b/plugins/CoreHome/templates/header.tpl
index d98bd78880..461de49918 100644
--- a/plugins/CoreHome/templates/header.tpl
+++ b/plugins/CoreHome/templates/header.tpl
@@ -1,27 +1,29 @@
<!DOCTYPE html>
-<!--[if lt IE 9 ]><html class="old-ie"> <![endif]-->
-<!--[if (gte IE 9)|!(IE)]><!--><html><!--<![endif]-->
+<!--[if lt IE 9 ]>
+<html class="old-ie"> <![endif]-->
+<!--[if (gte IE 9)|!(IE)]><!-->
+<html><!--<![endif]-->
<head>
-<title>{$siteName} - {if !$isCustomLogo}Piwik &rsaquo; {/if} {'CoreHome_WebAnalyticsReports'|translate}</title>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<meta name="generator" content="Piwik - Open Source Web Analytics" />
-<meta name="description" content="Web Analytics report for '{$siteName}' - Piwik" />
-<link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico" />
-{loadJavascriptTranslations plugins='CoreHome Annotations'}
-{include file="CoreHome/templates/js_global_variables.tpl"}
-<!--[if lt IE 9]>
-<script language="javascript" type="text/javascript" src="libs/jqplot/excanvas.min.js"></script>
-<![endif]-->
-{include file="CoreHome/templates/js_css_includes.tpl"}
-<!--[if IE]>
-<link rel="stylesheet" type="text/css" href="themes/default/ieonly.css" />
-<![endif]-->
-{include file="CoreHome/templates/iframe_buster_header.tpl"}
-{if isset($addToHead)}{$addToHead}{/if}
+ <title>{$siteName} - {if !$isCustomLogo}Piwik &rsaquo; {/if} {'CoreHome_WebAnalyticsReports'|translate}</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <meta name="generator" content="Piwik - Open Source Web Analytics"/>
+ <meta name="description" content="Web Analytics report for '{$siteName}' - Piwik"/>
+ <link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico"/>
+ {loadJavascriptTranslations plugins='CoreHome Annotations'}
+ {include file="CoreHome/templates/js_global_variables.tpl"}
+ <!--[if lt IE 9]>
+ <script language="javascript" type="text/javascript" src="libs/jqplot/excanvas.min.js"></script>
+ <![endif]-->
+ {include file="CoreHome/templates/js_css_includes.tpl"}
+ <!--[if IE]>
+ <link rel="stylesheet" type="text/css" href="themes/default/ieonly.css"/>
+ <![endif]-->
+ {include file="CoreHome/templates/iframe_buster_header.tpl"}
+ {if isset($addToHead)}{$addToHead}{/if}
</head>
<body>
{include file="CoreHome/templates/iframe_buster_body.tpl"}
<div id="root">
-{include file="CoreHome/templates/index_before_menu.tpl"}
+ {include file="CoreHome/templates/index_before_menu.tpl"}
diff --git a/plugins/CoreHome/templates/header_message.tpl b/plugins/CoreHome/templates/header_message.tpl
index 3943ccbe8a..88860a7f60 100644
--- a/plugins/CoreHome/templates/header_message.tpl
+++ b/plugins/CoreHome/templates/header_message.tpl
@@ -5,35 +5,37 @@
<span id="header_message" class="{if $piwikUrl == 'http://demo.piwik.org/' || !$latest_version_available}header_info{else}header_alert{/if}">
<span class="header_short">
{if $piwikUrl == 'http://demo.piwik.org/'}
- {'General_YouAreViewingDemoShortMessage'|translate}
- {elseif $latest_version_available}
- {'General_NewUpdatePiwikX'|translate:$latest_version_available}
- {else}
- {'General_AboutPiwikX'|translate:$piwik_version}
- {/if}
+ {'General_YouAreViewingDemoShortMessage'|translate}
+ {elseif $latest_version_available}
+ {'General_NewUpdatePiwikX'|translate:$latest_version_available}
+ {else}
+ {'General_AboutPiwikX'|translate:$piwik_version}
+ {/if}
</span>
<span class="header_full">
{if $piwikUrl == 'http://demo.piwik.org/'}
- {'General_YouAreViewingDemoShortMessage'|translate}<br/>
- {'General_DownloadFullVersion'|translate:"<a href='http://piwik.org/'>":"</a>":"<a href='http://piwik.org'>piwik.org</a>"}
- {elseif $latest_version_available}
- {if $isSuperUser}
- {'General_PiwikXIsAvailablePleaseUpdateNow'|translate:$latest_version_available:"<br /><a href='index.php?module=CoreUpdater&amp;action=newVersionAvailable'>":"</a>":"<a href='?module=Proxy&amp;action=redirect&amp;url=http://piwik.org/changelog/' target='_blank'>":"</a>"}
- <br/>{'General_YouAreCurrentlyUsing'|translate:$piwik_version}
- {else}
- {'General_PiwikXIsAvailablePleaseNotifyPiwikAdmin'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/' target='_blank'>Piwik</a> <a href='?module=Proxy&action=redirect&url=http://piwik.org/changelog/' target='_blank'>$latest_version_available</a>"}
- {/if}
- {else}
- {'General_PiwikIsACollaborativeProjectYouCanContributeAndDonate'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org' target='_blank'>":"$piwik_version</a>":"<br />":"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/contribute/'>":"</a>":'<br/>':"<a href='http://piwik.org/donate/' target='_blank'><strong><em>":"</em></strong></a>"}
- {/if}
- {if !empty($hasSomeAdminAccess)}
- <br/>
- <div id="updateCheckLinkContainer">
- <span class='loadingPiwik' style="display:none"><img src='./themes/default/images/loading-blue.gif'/></span>
- <img src="themes/default/images/reload.png"/>
- <a href="#" id="checkForUpdates"><em>{'CoreHome_CheckForUpdates'|translate}</em></a>
- </div>
- {/if}
+ {'General_YouAreViewingDemoShortMessage'|translate}
+ <br/>
+ {'General_DownloadFullVersion'|translate:"<a href='http://piwik.org/'>":"</a>":"<a href='http://piwik.org'>piwik.org</a>"}
+ {elseif $latest_version_available}
+ {if $isSuperUser}
+ {'General_PiwikXIsAvailablePleaseUpdateNow'|translate:$latest_version_available:"<br /><a href='index.php?module=CoreUpdater&amp;action=newVersionAvailable'>":"</a>":"<a href='?module=Proxy&amp;action=redirect&amp;url=http://piwik.org/changelog/' target='_blank'>":"</a>"}
+ <br/>
+ {'General_YouAreCurrentlyUsing'|translate:$piwik_version}
+ {else}
+ {'General_PiwikXIsAvailablePleaseNotifyPiwikAdmin'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/' target='_blank'>Piwik</a> <a href='?module=Proxy&action=redirect&url=http://piwik.org/changelog/' target='_blank'>$latest_version_available</a>"}
+ {/if}
+ {else}
+ {'General_PiwikIsACollaborativeProjectYouCanContributeAndDonate'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org' target='_blank'>":"$piwik_version</a>":"<br />":"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/contribute/'>":"</a>":'<br/>':"<a href='http://piwik.org/donate/' target='_blank'><strong><em>":"</em></strong></a>"}
+ {/if}
+ {if !empty($hasSomeAdminAccess)}
+ <br/>
+ <div id="updateCheckLinkContainer">
+ <span class='loadingPiwik' style="display:none"><img src='./themes/default/images/loading-blue.gif'/></span>
+ <img src="themes/default/images/reload.png"/>
+ <a href="#" id="checkForUpdates"><em>{'CoreHome_CheckForUpdates'|translate}</em></a>
+ </div>
+ {/if}
</span>
</span>
diff --git a/plugins/CoreHome/templates/html_report_body.tpl b/plugins/CoreHome/templates/html_report_body.tpl
index 4d1bf67353..5a6b7a33b2 100644
--- a/plugins/CoreHome/templates/html_report_body.tpl
+++ b/plugins/CoreHome/templates/html_report_body.tpl
@@ -1,81 +1,81 @@
-<a name ="{$reportId}"/>
+<a name="{$reportId}"/>
<h2 style="color: rgb({$reportTitleTextColor}); font-size: {$reportTitleTextSize}pt;">
- {$reportName|escape:"html"}
+ {$reportName|escape:"html"}
</h2>
{if empty($reportRows)}
- {'CoreHome_ThereIsNoDataForThisReport'|translate}
+ {'CoreHome_ThereIsNoDataForThisReport'|translate}
{else}
- {if $displayGraph}
- <img
- alt=""
- {if $renderImageInline}
- src="data:image/png;base64,{$generatedImageGraph}"
- {else}
- src="cid:{$reportId}"
- {/if}
- height="{$graphHeight}"
- width="{$graphWidth}" />
- {/if}
+ {if $displayGraph}
+ <img
+ alt=""
+ {if $renderImageInline}
+ src="data:image/png;base64,{$generatedImageGraph}"
+ {else}
+ src="cid:{$reportId}"
+ {/if}
+ height="{$graphHeight}"
+ width="{$graphWidth}"/>
+ {/if}
- {if $displayGraph && $displayTable}
- <br/>
- <br/>
- {/if}
+ {if $displayGraph && $displayTable}
+ <br/>
+ <br/>
+ {/if}
- {if $displayTable}
- <table style="border-collapse:collapse; margin-left: 5px">
- <thead style="background-color: rgb({$tableHeaderBgColor}); color: rgb({$tableHeaderTextColor}); font-size: {$reportTableHeaderTextSize}pt;">
- {foreach from=$reportColumns item=columnName}
- <th style="padding: 6px 0;">
- &nbsp;{$columnName}&nbsp;&nbsp;
- </th>
- {/foreach}
- </thead>
- <tbody>
- {cycle name='tr-background-color' delimiter=';' values=";background-color: rgb(`$tableBgColor`)" print=false reset=true advance=false}
- {foreach from=$reportRows item=row key=rowId}
+ {if $displayTable}
+ <table style="border-collapse:collapse; margin-left: 5px">
+ <thead style="background-color: rgb({$tableHeaderBgColor}); color: rgb({$tableHeaderTextColor}); font-size: {$reportTableHeaderTextSize}pt;">
+ {foreach from=$reportColumns item=columnName}
+ <th style="padding: 6px 0;">
+ &nbsp;{$columnName}&nbsp;&nbsp;
+ </th>
+ {/foreach}
+ </thead>
+ <tbody>
+ {cycle name='tr-background-color' delimiter=';' values=";background-color: rgb(`$tableBgColor`)" print=false reset=true advance=false}
+ {foreach from=$reportRows item=row key=rowId}
- {assign var=rowMetrics value=$row->getColumns()}
+ {assign var=rowMetrics value=$row->getColumns()}
- {if isset($reportRowsMetadata[$rowId])}
- {assign var=rowMetadata value=$reportRowsMetadata[$rowId]->getColumns()}
- {else}
- {assign var=rowMetadata value=null}
- {/if}
-
- <tr style="{cycle name='tr-background-color'}">
- {foreach from=$reportColumns key=columnId item=columnName}
- <td style="font-size: {$reportTableRowTextSize}pt; border-bottom: 1px solid rgb({$tableCellBorderColor}); padding: 5px 0 5px 5px;">
- {if $columnId eq 'label'}
- {if isset($rowMetrics[$columnId])}
- {if isset($rowMetadata.logo)}
- <img src='{$currentPath}{$rowMetadata.logo}'>&nbsp;
- {/if}
- {if isset($rowMetadata.url)}
- <a style="color: rgb({$reportTextColor});" href='{if !in_array(substr($rowMetadata.url,0,4), array('http','ftp:'))}http://{/if}{$rowMetadata.url|escape:'html'}'>
- {/if}
- {$rowMetrics[$columnId]}
- {if isset($rowMetadata.url)}
- </a>
- {/if}
- {/if}
- {else}
- {if empty($rowMetrics[$columnId])}
- 0
- {else}
- {$rowMetrics[$columnId]}
- {/if}
- {/if}
- </td>
- {/foreach}
- </tr>
- {/foreach}
- </tbody>
- </table>
- {/if}
- <br/>
- <a style="text-decoration:none; color: rgb({$reportTitleTextColor}); font-size: {$reportBackToTopTextSize}pt" href="#reportTop">
- {'PDFReports_TopOfReport'|translate}
- </a>
+ {if isset($reportRowsMetadata[$rowId])}
+ {assign var=rowMetadata value=$reportRowsMetadata[$rowId]->getColumns()}
+ {else}
+ {assign var=rowMetadata value=null}
+ {/if}
+ <tr style="{cycle name='tr-background-color'}">
+ {foreach from=$reportColumns key=columnId item=columnName}
+ <td style="font-size: {$reportTableRowTextSize}pt; border-bottom: 1px solid rgb({$tableCellBorderColor}); padding: 5px 0 5px 5px;">
+ {if $columnId eq 'label'}
+ {if isset($rowMetrics[$columnId])}
+ {if isset($rowMetadata.logo)}
+ <img src='{$currentPath}{$rowMetadata.logo}'>
+ &nbsp;
+ {/if}
+ {if isset($rowMetadata.url)}
+ <a style="color: rgb({$reportTextColor});" href='{if !in_array(substr($rowMetadata.url,0,4), array('http','ftp:'))}http://{/if}{$rowMetadata.url|escape:'html'}'>
+ {/if}
+ {$rowMetrics[$columnId]}
+ {if isset($rowMetadata.url)}
+ </a>
+ {/if}
+ {/if}
+ {else}
+ {if empty($rowMetrics[$columnId])}
+ 0
+ {else}
+ {$rowMetrics[$columnId]}
+ {/if}
+ {/if}
+ </td>
+ {/foreach}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ {/if}
+ <br/>
+ <a style="text-decoration:none; color: rgb({$reportTitleTextColor}); font-size: {$reportBackToTopTextSize}pt" href="#reportTop">
+ {'PDFReports_TopOfReport'|translate}
+ </a>
{/if} \ No newline at end of file
diff --git a/plugins/CoreHome/templates/html_report_footer.tpl b/plugins/CoreHome/templates/html_report_footer.tpl
index 4d4a7e5285..691287b6e3 100644
--- a/plugins/CoreHome/templates/html_report_footer.tpl
+++ b/plugins/CoreHome/templates/html_report_footer.tpl
@@ -1,2 +1,2 @@
- </body>
+</body>
</html> \ No newline at end of file
diff --git a/plugins/CoreHome/templates/html_report_header.tpl b/plugins/CoreHome/templates/html_report_header.tpl
index c40bcd0e80..a8c289a060 100644
--- a/plugins/CoreHome/templates/html_report_header.tpl
+++ b/plugins/CoreHome/templates/html_report_header.tpl
@@ -1,34 +1,31 @@
<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- </head>
- <body style="color: rgb({$reportTextColor});">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+</head>
+<body style="color: rgb({$reportTextColor});">
- <a name="reportTop"/>
- <a target="_blank" href="{$currentPath}"><img title="{'General_GoTo'|translate:"Piwik"}" border="0" alt="Piwik" src='{$logoHeader}' /></a>
+<a name="reportTop"/>
+<a target="_blank" href="{$currentPath}"><img title="{'General_GoTo'|translate:"Piwik"}" border="0" alt="Piwik" src='{$logoHeader}'/></a>
- <h1 style="color: rgb({$reportTitleTextColor}); font-size: {$reportTitleTextSize}pt;">
- {$websiteName}
- </h1>
+<h1 style="color: rgb({$reportTitleTextColor}); font-size: {$reportTitleTextSize}pt;">
+ {$websiteName}
+</h1>
- <p>
- {$description} - {'General_DateRange'|translate} {$prettyDate}
- </p>
+<p>
+ {$description} - {'General_DateRange'|translate} {$prettyDate}
+</p>
- {if sizeof($reportMetadata) > 1}
-
- <h2 style="color: rgb({$reportTitleTextColor}); font-size: {$reportTitleTextSize}pt;">
- {'PDFReports_TableOfContent'|translate}
- </h2>
-
- <ul>
- {foreach from=$reportMetadata item=metadata}
- <li>
- <a href="#{$metadata.uniqueId}" style="text-decoration:none; color: rgb({$reportTextColor});">
- {$metadata.name|escape:"html"}
- </a>
- </li>
- {/foreach}
- </ul>
-
- {/if}
+{if sizeof($reportMetadata) > 1}
+ <h2 style="color: rgb({$reportTitleTextColor}); font-size: {$reportTitleTextSize}pt;">
+ {'PDFReports_TableOfContent'|translate}
+ </h2>
+ <ul>
+ {foreach from=$reportMetadata item=metadata}
+ <li>
+ <a href="#{$metadata.uniqueId}" style="text-decoration:none; color: rgb({$reportTextColor});">
+ {$metadata.name|escape:"html"}
+ </a>
+ </li>
+ {/foreach}
+ </ul>
+{/if}
diff --git a/plugins/CoreHome/templates/iframe_buster_body.tpl b/plugins/CoreHome/templates/iframe_buster_body.tpl
index 8fadfc3af7..3b00a2dae5 100644
--- a/plugins/CoreHome/templates/iframe_buster_body.tpl
+++ b/plugins/CoreHome/templates/iframe_buster_body.tpl
@@ -1,3 +1,7 @@
{if isset($enableFrames) && !$enableFrames}
-{literal}<script type="text/javascript">if(self == top) { var theBody = document.getElementsByTagName('body')[0]; theBody.style.display = 'block'; } else { top.location = self.location; }</script>
+{literal}
+ <script type="text/javascript">if (self == top) {
+ var theBody = document.getElementsByTagName('body')[0];
+ theBody.style.display = 'block';
+ } else { top.location = self.location; }</script>
{/literal}{/if} \ No newline at end of file
diff --git a/plugins/CoreHome/templates/iframe_buster_header.tpl b/plugins/CoreHome/templates/iframe_buster_header.tpl
index 319aa7db66..afa65b46f0 100644
--- a/plugins/CoreHome/templates/iframe_buster_header.tpl
+++ b/plugins/CoreHome/templates/iframe_buster_header.tpl
@@ -1,5 +1,7 @@
{if isset($enableFrames) && !$enableFrames}
-{literal}
- <style type="text/css">body { display : none; }</style>
-{/literal}
+ {literal}
+ <style type="text/css">body {
+ display: none;
+ }</style>
+ {/literal}
{/if} \ No newline at end of file
diff --git a/plugins/CoreHome/templates/index_before_menu.tpl b/plugins/CoreHome/templates/index_before_menu.tpl
index c6e0ac5f2c..7d76db2e52 100644
--- a/plugins/CoreHome/templates/index_before_menu.tpl
+++ b/plugins/CoreHome/templates/index_before_menu.tpl
@@ -1,12 +1,12 @@
{include file="CoreHome/templates/warning_invalid_host.tpl"}
{if !isset($showTopMenu) || $showTopMenu}
-{include file="CoreHome/templates/top_bar.tpl"}
+ {include file="CoreHome/templates/top_bar.tpl"}
{/if}
{include file="CoreHome/templates/top_screen.tpl"}
<div class="ui-confirm" id="alert">
- <h2></h2>
- <input role="yes" type="button" value="{'General_Ok'|translate}" />
+ <h2></h2>
+ <input role="yes" type="button" value="{'General_Ok'|translate}"/>
</div>
diff --git a/plugins/CoreHome/templates/index_content.tpl b/plugins/CoreHome/templates/index_content.tpl
index 04211fc48c..1d9727eed6 100644
--- a/plugins/CoreHome/templates/index_content.tpl
+++ b/plugins/CoreHome/templates/index_content.tpl
@@ -1,20 +1,19 @@
-
<div class="page">
- <div class="pageWrap">
- <div class="nav_sep"></div>
- <div class="top_controls">
- {include file="CoreHome/templates/period_select.tpl"}
- {include file="CoreHome/templates/header_message.tpl"}
- {ajaxRequestErrorDiv}
- </div>
+ <div class="pageWrap">
+ <div class="nav_sep"></div>
+ <div class="top_controls">
+ {include file="CoreHome/templates/period_select.tpl"}
+ {include file="CoreHome/templates/header_message.tpl"}
+ {ajaxRequestErrorDiv}
+ </div>
- {ajaxLoadingDiv}
+ {ajaxLoadingDiv}
- <div id="content" class="home">
- {if $content}{$content}{/if}
- </div>
- <div class="clear"></div>
- </div>
+ <div id="content" class="home">
+ {if $content}{$content}{/if}
+ </div>
+ <div class="clear"></div>
+ </div>
</div>
<br/><br/> \ No newline at end of file
diff --git a/plugins/CoreHome/templates/jqplot.css b/plugins/CoreHome/templates/jqplot.css
index 70f7742ba5..33014a7178 100644
--- a/plugins/CoreHome/templates/jqplot.css
+++ b/plugins/CoreHome/templates/jqplot.css
@@ -1,8 +1,7 @@
-
.jqplot-loading {
- background: url(../../../themes/default/images/loading-blue.gif) no-repeat center center white;
- position: absolute;
- z-index: 10;
+ background: url(../../../themes/default/images/loading-blue.gif) no-repeat center center white;
+ position: absolute;
+ z-index: 10;
}
.jqplot-target {
@@ -25,7 +24,7 @@
.jqplot-y2axis,
.jqplot-y3axis {
- margin: 0 3px 0 7px;
+ margin: 0 3px 0 7px;
}
.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick {
@@ -45,11 +44,11 @@
}
.jqplot-yaxis-tick.jqplot-breakTick {
- right: -20px;
- margin-right: 0;
- padding:1px 5px 1px 5px;
- z-index: 2;
- font-size: 1.5em;
+ right: -20px;
+ margin-right: 0;
+ padding: 1px 5px 1px 5px;
+ z-index: 2;
+ font-size: 1.5em;
}
.jqplot-xaxis-label {
@@ -71,190 +70,192 @@
font-size: 1.2em;
}
-
/**
* ROW EVOLUTION POPUP
*/
.rowevolution {
- position: relative;
- overflow: hidden;
- text-align: left
+ position: relative;
+ overflow: hidden;
+ text-align: left
}
.rowevolution h2 {
- font-size: 16px;
- margin: 0;
- padding: 0;
+ font-size: 16px;
+ margin: 0;
+ padding: 0;
}
.rowevolution .metrics-container {
- padding: 11px 0 5px 0;
+ padding: 11px 0 5px 0;
}
.rowevolution table.metrics {
- border-spacing: 0;
+ border-spacing: 0;
}
+
.multirowevolution table.metrics {
- margin-bottom: 12px;
+ margin-bottom: 12px;
}
+
.rowevolution table.metrics,
.multirowevolution table.metrics {
- /* prevent select for shift-click on metric toggles */
- user-select: none; /* CSS3 */
+ /* prevent select for shift-click on metric toggles */
+ user-select: none; /* CSS3 */
-moz-user-select: none; /* Gecko (Firefox) */
- -khtml-user-select:none; /* Webkit (Safari, Chrome) */
+ -khtml-user-select: none; /* Webkit (Safari, Chrome) */
}
.rowevolution table.metrics tr {
- margin: 0;
- padding: 0;
- cursor: pointer;
+ margin: 0;
+ padding: 0;
+ cursor: pointer;
}
.rowevolution table.metrics td {
- vertical-align: middle;
- text-align: left;
- margin: 0;
- padding: 4px 0;
- cursor: pointer;
+ vertical-align: middle;
+ text-align: left;
+ margin: 0;
+ padding: 4px 0;
+ cursor: pointer;
}
.rowevolution table.metrics td.sparkline {
- width: 120px;
+ width: 120px;
}
+
.multirowevolution table.metrics td.sparkline {
- padding-top: 15px;
+ padding-top: 15px;
}
/** IE7 does not support inline image data, which is needed for spark lines */
*+html .multirowevolution table.metrics td.sparkline,
*+html .rowevolution table.metrics td.sparkline {
- display: none;
+ display: none;
}
.rowevolution table.metrics td.text {
- font-size: 13px;
- line-height: 18px;
- color: #7E7363;
- font-weight: bold;
+ font-size: 13px;
+ line-height: 18px;
+ color: #7E7363;
+ font-weight: bold;
}
+
.multirowevolution table.metrics td.text {
- padding-top: 8px;
+ padding-top: 8px;
}
.rowevolution table.metrics td.text span.details {
- font-weight: normal;
- color: #444;
+ font-weight: normal;
+ color: #444;
}
.rowevolution table.metrics td.text span.change {
- display: block;
- float: left;
- padding-left: 15px;
+ display: block;
+ float: left;
+ padding-left: 15px;
}
.rowevolution table.metrics td.text span.good {
- color: #008000;
+ color: #008000;
}
.rowevolution table.metrics td.text span.bad {
- color: #f00;
+ color: #f00;
}
.rowevolution-documentation {
- font-size: 12px;
- margin: 2px 0 5px 0;
- padding: 5px 0 5px 23px;
- color: #888;
- background: url(../../../themes/default/images/help.png) no-repeat left center;
+ font-size: 12px;
+ margin: 2px 0 5px 0;
+ padding: 5px 0 5px 23px;
+ color: #888;
+ background: url(../../../themes/default/images/help.png) no-repeat left center;
}
.rowevolution .metric-selectbox,
.rowevolution .compare-container {
- padding: 15px 0 5px 0;
+ padding: 15px 0 5px 0;
}
.rowevolution .metric-selectbox select {
- font-size: 13px;
- color: #444;
- margin: 8px 0 0 0;
- padding: 0;
+ font-size: 13px;
+ color: #444;
+ margin: 8px 0 0 0;
+ padding: 0;
}
a.rowevolution-startmulti {
- font-size: 12px;
- color: #7E7363;
- font-weight: bold;
- text-decoration: none;
+ font-size: 12px;
+ color: #7E7363;
+ font-weight: bold;
+ text-decoration: none;
}
a.rowevolution-startmulti:hover {
- color: #444;
+ color: #444;
}
-
/**
* SERIES PICKER FOR CHARTS
*/
-
+
.jqplot-seriespicker {
- display: block;
- position: absolute;
- z-index: 9;
- width: 24px;
- height: 16px;
- margin-top: 3px;
- background: url(../../../themes/default/images/chart_line_edit.png) no-repeat center center;
- overflow: hidden;
- text-indent: -999px;
+ display: block;
+ position: absolute;
+ z-index: 9;
+ width: 24px;
+ height: 16px;
+ margin-top: 3px;
+ background: url(../../../themes/default/images/chart_line_edit.png) no-repeat center center;
+ overflow: hidden;
+ text-indent: -999px;
}
.jqplock-seriespicker-popover {
- display: block;
- position: absolute;
- z-index: 1010; /* must be above ui dialog */
- margin-top: -2px;
- background: url(../../../themes/default/images/chart_line_edit.png) no-repeat 7px 4px #f7f7f7;
- font-size: 11px;
- font-weight: normal;
- border: 1px solid #e4e5e4;
- padding: 6px 9px;
- border-radius: 4px;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- -moz-box-shadow: 1px 1px 2px #666;
- -webkit-box-shadow: 1px 1px 2px #666;
- box-shadow: 1px 1px 2px #666;
+ display: block;
+ position: absolute;
+ z-index: 1010; /* must be above ui dialog */
+ margin-top: -2px;
+ background: url(../../../themes/default/images/chart_line_edit.png) no-repeat 7px 4px #f7f7f7;
+ font-size: 11px;
+ font-weight: normal;
+ border: 1px solid #e4e5e4;
+ padding: 6px 9px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ -moz-box-shadow: 1px 1px 2px #666;
+ -webkit-box-shadow: 1px 1px 2px #666;
+ box-shadow: 1px 1px 2px #666;
}
.jqplock-seriespicker-popover p {
- margin: 0;
- padding: 0 4px 0 0;
- line-height: 15px;
- vertical-align:middle;
+ margin: 0;
+ padding: 0 4px 0 0;
+ line-height: 15px;
+ vertical-align: middle;
}
.jqplock-seriespicker-popover p.headline {
- font-weight: bold;
- font-size: 12px;
- padding: 0 0 6px 22px;
- color: #7E7363;
+ font-weight: bold;
+ font-size: 12px;
+ padding: 0 0 6px 22px;
+ color: #7E7363;
}
.jqplock-seriespicker-popover p.headline.recordsToPlot {
- padding: 8px 0 3px 0;
+ padding: 8px 0 3px 0;
}
.jqplock-seriespicker-popover.alignright p.headline {
- padding: 0 22px 6px 0;
+ padding: 0 22px 6px 0;
}
.jqplock-seriespicker-popover input.select {
- margin-right: 8px;
+ margin-right: 8px;
}
.jqplock-seriespicker-popover p.pickColumn,
.jqplock-seriespicker-popover p.pickRow {
- cursor: pointer;
+ cursor: pointer;
} \ No newline at end of file
diff --git a/plugins/CoreHome/templates/jqplot.js b/plugins/CoreHome/templates/jqplot.js
index 375b5b38ed..36d37a569e 100644
--- a/plugins/CoreHome/templates/jqplot.js
+++ b/plugins/CoreHome/templates/jqplot.js
@@ -14,574 +14,575 @@
* @param the data that would be passed to open flash chart
*/
function JQPlot(data, dataTableId) {
- this.init(data, dataTableId);
+ this.init(data, dataTableId);
}
JQPlot.prototype = {
- /** Generic init function */
- init: function(data, dataTableId) {
- this.dataTableId = dataTableId;
- this.originalData = data;
- this.data = data.data;
-
- defaultParams = {};
- defaultParams.grid = {
- drawGridLines: false,
- background: '#fff',
- borderColor: '#f00',
- borderWidth: 0,
- shadow: false
- };
+ /** Generic init function */
+ init: function (data, dataTableId) {
+ this.dataTableId = dataTableId;
+ this.originalData = data;
+ this.data = data.data;
- defaultParams.title = {
- show: false
- };
+ defaultParams = {};
+ defaultParams.grid = {
+ drawGridLines: false,
+ background: '#fff',
+ borderColor: '#f00',
+ borderWidth: 0,
+ shadow: false
+ };
- defaultParams.axesDefaults = {
- pad: 1.0,
- tickRenderer: $.jqplot.CanvasAxisTickRenderer,
- tickOptions: {
- showMark: false,
- fontSize: '11px',
- fontFamily: 'Arial'
- }
- };
+ defaultParams.title = {
+ show: false
+ };
- this.params = $.extend(true, {}, defaultParams, data.params);
+ defaultParams.axesDefaults = {
+ pad: 1.0,
+ tickRenderer: $.jqplot.CanvasAxisTickRenderer,
+ tickOptions: {
+ showMark: false,
+ fontSize: '11px',
+ fontFamily: 'Arial'
+ }
+ };
- this.tooltip = data.tooltip;
- this.seriesPicker = data.seriesPicker;
+ this.params = $.extend(true, {}, defaultParams, data.params);
- if (typeof this.params.axes.yaxis == 'undefined') {
- this.params.axes.yaxis = {};
- }
- if (typeof this.params.axes.yaxis.tickOptions == 'undefined') {
- this.params.yaxis.tickOptions = {
- formatString: '%d'
- };
- }
- },
-
- /** Generic render function */
- render: function(type, targetDivId, lang) {
- // preapare the appropriate chart type
- switch (type) {
- case 'evolution':
- this.prepareEvolutionChart(targetDivId, lang);
- break;
- case 'bar':
- this.prepareBarChart(targetDivId, lang);
- break;
- case 'pie':
- this.preparePieChart(targetDivId, lang);
- break;
- default:
- return;
- }
-
- // handle replot
- // this has be bound before the check for an empty graph.
- // otherwise clicking on sparklines won't work anymore after an empty
- // report has been displayed.
- var self = this;
- var target = $('#' + targetDivId)
- .on('replot', function(e, data) {
- target.trigger('piwikDestroyPlot');
- if (target.data('oldHeight') > 0) {
- // handle replot after empty report
- target.height(target.data('oldHeight'));
- target.data('oldHeight', 0);
- this.innerHTML = '';
- }
-
- (new JQPlot(data, self.dataTableId)).render(type, targetDivId, lang);
- });
-
- // show loading
- target.bind('showLoading', function() {
- var loading = $(document.createElement('div')).addClass('jqplot-loading');
- loading.css({
- width: target.innerWidth()+'px',
- height: target.innerHeight()+'px',
- opacity: 0
- });
- target.prepend(loading);
- loading.css({opacity: .7});
- });
-
- // change series
- target.bind('changeColumns', function(e, columns) {
- target.trigger('changeSeries', [columns, []]);
- });
- target.bind('changeSeries', function(e, columns, rows) {
- target.trigger('showLoading');
- if (typeof columns == 'string') {
- columns = columns.split(',');
- }
- if (typeof rows == 'undefined') {
- rows = [];
- }
- else if (typeof rows == 'string') {
- rows = rows.split(',');
- }
- var dataTable = $('#' + self.dataTableId).data('dataTableInstance');
- dataTable.param.columns = columns.join(',');
- dataTable.param.rows = rows.join(',');
- delete dataTable.param.filter_limit;
- delete dataTable.param.totalRows;
- if( dataTable.param.filter_sort_column != 'label' ) {
- dataTable.param.filter_sort_column = columns[0];
- }
- dataTable.param.disable_generic_filters = '0';
- dataTable.reloadAjaxDataTable(false);
- });
-
- // this case happens when there is no data for a line chart
- if (this.data.length == 0) {
- target.addClass('pk-emptyGraph');
- target.data('oldHeight', target.height());
- target.css('height', 'auto').html(lang.noData);
- return;
- }
-
- // create jqplot chart
- try {
- var plot = $.jqplot(targetDivId, this.data, this.params);
- } catch(e) {
- // this is thrown when refreshing piwik in the browser
- if (e != "No plot target specified") {
- throw e;
- }
- }
-
- // bind tooltip
- var self = this;
- target.on('jqplotDataHighlight', function(e, s, i, d) {
- if (type == 'bar') {
- self.showBarChartTooltip(s, i);
- } else if (type == 'pie') {
- self.showPieChartTooltip(i);
- }
- })
- .on('jqplotDataUnhighlight', function(e, s, i, d){
- if (type != 'evolution') {
- self.hideTooltip();
- }
- });
-
- // handle window resize
- var plotWidth = target.innerWidth();
- var timeout = false;
- target.on('resizeGraph', function() {
- var width = target.innerWidth();
- if (width > 0 && Math.abs(plotWidth - width) >= 5) {
- plotWidth = width;
- target.trigger('piwikDestroyPlot');
- (new JQPlot(self.originalData, self.dataTableId))
- .render(type, targetDivId, lang);
- }
- });
- var resizeListener = function() {
- if (timeout) {
- window.clearTimeout(timeout);
- }
- timeout = window.setTimeout(function() {
- target.trigger('resizeGraph');
- }, 300);
- };
- $(window).on('resize', resizeListener);
-
- // export as image
- target.on('piwikExportAsImage', function(e) {
- self.exportAsImage(target, lang);
- });
-
- // manage resources
- target.on('piwikDestroyPlot', function() {
- $(window).off('resize', resizeListener);
- plot.destroy();
- for (var i = 0; i < $.jqplot.visiblePlots.length; i++) {
- if ($.jqplot.visiblePlots[i] == plot) {
- $.jqplot.visiblePlots[i] = null;
- }
- }
- $(this).off();
- });
-
- if (typeof $.jqplot.visiblePlots == 'undefined') {
- $.jqplot.visiblePlots = [];
- $('ul.nav').on('piwikSwitchPage', function() {
- for (var i = 0; i < $.jqplot.visiblePlots.length; i++) {
- if ($.jqplot.visiblePlots[i] == null) {
- continue;
- }
- $.jqplot.visiblePlots[i].destroy();
- }
- $.jqplot.visiblePlots = [];
- });
- }
-
- if (typeof plot != 'undefined') {
- $.jqplot.visiblePlots.push(plot);
- }
- },
-
- /** Export the chart as an image */
- exportAsImage: function(container, lang) {
- var exportCanvas = document.createElement('canvas');
- exportCanvas.width = container.width();
- exportCanvas.height = container.height();
-
- if(!exportCanvas.getContext) { alert("Sorry, not supported in your browser. Please upgrade your browser :)"); return; }
- var exportCtx = exportCanvas.getContext('2d');
-
- var canvases = container.find('canvas');
-
- for (var i=0; i < canvases.length; i++) {
- var canvas = canvases.eq(i);
- var position = canvas.position();
- var parent = canvas.parent();
- if (parent.hasClass('jqplot-axis')) {
- var addPosition = parent.position();
- position.left += addPosition.left;
- position.top += addPosition.top + parseInt(parent.css('marginTop'), 10);
- }
- exportCtx.drawImage(canvas[0], Math.round(position.left), Math.round(position.top));
- }
-
- var exported = exportCanvas.toDataURL("image/png");
-
- var img = document.createElement('img');
- img.src = exported;
-
- img = $(img).css({
- width: exportCanvas.width + 'px',
- height: exportCanvas.height + 'px'
- });
-
- $(document.createElement('div'))
- .append('<div style="font-size: 13px; margin-bottom: 10px;">'
- + lang.exportText + '</div>').append($(img))
- .dialog({
- title: lang.exportTitle,
- modal: true,
- width: 'auto',
- position: ['center', 'center'],
- resizable: false,
- autoOpen: true,
- close: function(event, ui) {
- $(this).dialog("destroy").remove();
- }
- });
- },
-
-
- // ------------------------------------------------------------
- // EVOLUTION CHART
- // ------------------------------------------------------------
-
- prepareEvolutionChart: function(targetDivId, lang) {
- this.setYTicks();
- this.addSeriesPicker(targetDivId, lang);
-
- defaultParams.axes = {
- xaxis: {
- pad: 1.0,
- renderer: $.jqplot.CategoryAxisRenderer,
- tickOptions: {
- showGridline: false
+ this.tooltip = data.tooltip;
+ this.seriesPicker = data.seriesPicker;
+
+ if (typeof this.params.axes.yaxis == 'undefined') {
+ this.params.axes.yaxis = {};
+ }
+ if (typeof this.params.axes.yaxis.tickOptions == 'undefined') {
+ this.params.yaxis.tickOptions = {
+ formatString: '%d'
+ };
+ }
+ },
+
+ /** Generic render function */
+ render: function (type, targetDivId, lang) {
+ // preapare the appropriate chart type
+ switch (type) {
+ case 'evolution':
+ this.prepareEvolutionChart(targetDivId, lang);
+ break;
+ case 'bar':
+ this.prepareBarChart(targetDivId, lang);
+ break;
+ case 'pie':
+ this.preparePieChart(targetDivId, lang);
+ break;
+ default:
+ return;
}
- }
- };
- defaultParams.seriesDefaults = {
- lineWidth: 1,
- markerOptions: {
- style: "filledCircle",
- size: 6,
- shadow: false
- }
- };
+ // handle replot
+ // this has be bound before the check for an empty graph.
+ // otherwise clicking on sparklines won't work anymore after an empty
+ // report has been displayed.
+ var self = this;
+ var target = $('#' + targetDivId)
+ .on('replot', function (e, data) {
+ target.trigger('piwikDestroyPlot');
+ if (target.data('oldHeight') > 0) {
+ // handle replot after empty report
+ target.height(target.data('oldHeight'));
+ target.data('oldHeight', 0);
+ this.innerHTML = '';
+ }
- defaultParams.piwikTicks = {
- showTicks: true,
- showGrid: true,
- showHighlight: true
- };
+ (new JQPlot(data, self.dataTableId)).render(type, targetDivId, lang);
+ });
- this.params = $.extend(true, {}, defaultParams, this.params);
+ // show loading
+ target.bind('showLoading', function () {
+ var loading = $(document.createElement('div')).addClass('jqplot-loading');
+ loading.css({
+ width: target.innerWidth() + 'px',
+ height: target.innerHeight() + 'px',
+ opacity: 0
+ });
+ target.prepend(loading);
+ loading.css({opacity: .7});
+ });
- var self = this;
- var lastTick = false;
-
- $('#' + targetDivId)
- .on('jqplotMouseLeave', function(e, s, i, d){
- self.hideTooltip();
- $(this).css('cursor', 'default');
- })
- .on('jqplotClick', function(e, s, i, d){
- if (lastTick !== false && typeof self.params.axes.xaxis.onclick != 'undefined'
- && typeof self.params.axes.xaxis.onclick[lastTick] == 'string') {
- var url = self.params.axes.xaxis.onclick[lastTick];
- piwikHelper.redirectToUrl(url);
- }
- })
- .on('jqplotPiwikTickOver', function(e, tick){
- lastTick = tick;
- self.showEvolutionChartTooltip(tick);
- if (typeof self.params.axes.xaxis.onclick != 'undefined'
- && typeof self.params.axes.xaxis.onclick[lastTick] == 'string') {
- $(this).css('cursor', 'pointer');
- }
- });
-
- this.params.legend = {
- show: false
- };
- this.params.canvasLegend = {
- show: true
- };
- },
-
- showEvolutionChartTooltip: function(i) {
- var label;
- if (typeof this.params.axes.xaxis.labels != 'undefined') {
- label = this.params.axes.xaxis.labels[i];
- } else {
- label = this.params.axes.xaxis.ticks[i];
- }
-
- var text = [];
- for (var d = 0; d < this.data.length; d++) {
- var value = this.formatY(this.data[d][i], d);
- var series = this.params.series[d].label;
- text.push('<b>' + value + '</b> ' + series);
- }
-
- this.showTooltip(label, text.join('<br />'));
- },
-
-
- // ------------------------------------------------------------
- // PIE CHART
- // ------------------------------------------------------------
-
- preparePieChart: function(targetDivId, lang) {
- this.addSeriesPicker(targetDivId, lang);
-
- this.params.seriesDefaults = {
- renderer: $.jqplot.PieRenderer,
- rendererOptions: {
- shadow: false,
- showDataLabels: false,
- sliceMargin: 1,
- startAngle: 35
- }
- };
-
- this.params.piwikTicks = {
- showTicks: false,
- showGrid: false,
- showHighlight: false
- };
-
- this.params.legend = {
- show: false
- };
- this.params.pieLegend = {
- show: true
- };
- this.params.canvasLegend = {
- show: true,
- singleMetric: true
- };
-
- // pie charts have a different data format
- if (!(this.data[0][0] instanceof Array)) { // check if already in different format
- for (var i = 0; i < this.data[0].length; i++) {
- this.data[0][i] = [this.params.axes.xaxis.ticks[i], this.data[0][i]];
- }
- }
- },
-
- showPieChartTooltip: function(i) {
- var value = this.formatY(this.data[0][i][1], 1); // series index 1 because 0 is the label
- var series = this.params.series[0].label;
- var percentage = this.tooltip.percentages[0][i];
-
- var label = this.data[0][i][0];
-
- var text = '<b>' + percentage + '%</b> (' + value + ' ' + series + ')';
- this.showTooltip(label, text);
- },
-
-
- // ------------------------------------------------------------
- // BAR CHART
- // ------------------------------------------------------------
-
- prepareBarChart: function(targetDivId, lang) {
- this.setYTicks();
- this.addSeriesPicker(targetDivId, lang);
-
- this.params.seriesDefaults = {
- renderer: $.jqplot.BarRenderer,
- rendererOptions: {
- shadowOffset: 1,
- shadowDepth: 2,
- shadowAlpha: .2,
- fillToZero: true,
- barMargin: this.data[0].length > 10 ? 2 : 10
- }
- };
-
- this.params.piwikTicks = {
- showTicks: true,
- showGrid: false,
- showHighlight: false
- };
-
- this.params.axes.xaxis.renderer = $.jqplot.CategoryAxisRenderer;
- this.params.axes.xaxis.tickOptions = {
- showGridline: false
- };
-
- this.params.canvasLegend = {
- show: true
- };
- },
-
- showBarChartTooltip: function(s, i) {
- var value = this.formatY(this.data[s][i], s);
- var series = this.params.series[s].label;
-
- var percentage = '';
- if (typeof this.tooltip.percentages != 'undefined') {
- var percentage = this.tooltip.percentages[s][i];
- percentage = ' (' + percentage + '%)';
- }
-
- var label = this.params.axes.xaxis.labels[i];
- var text = '<b>' + value + '</b> ' + series + percentage;
- this.showTooltip(label, text);
- },
-
-
- // ------------------------------------------------------------
- // HELPER METHODS
- // ------------------------------------------------------------
-
- /** Generate ticks in y direction */
- setYTicks: function() {
- // default axis
- this.setYTicksForAxis('yaxis', this.params.axes.yaxis);
- // other axes: y2axis, y3axis...
- for (var i = 2; typeof this.params.axes['y'+i+'axis'] != 'undefined'; i++) {
- this.setYTicksForAxis('y'+i+'axis', this.params.axes['y'+i+'axis']);
- }
- },
-
- setYTicksForAxis: function(axisName, axis) {
- // calculate maximum x value of all data sets
- var maxCrossDataSets = 0;
- for (var i = 0; i < this.data.length; i++) {
- if (this.params.series[i].yaxis == axisName) {
- maxValue = Math.max.apply(Math, this.data[i]);
- if (maxValue > maxCrossDataSets) {
- maxCrossDataSets = maxValue;
- }
- maxCrossDataSets = parseFloat(maxCrossDataSets);
- }
- }
-
- // add little padding on top
- maxCrossDataSets += Math.max(1, Math.round(maxCrossDataSets * .03));
-
- // round to the nearest multiple of ten
- if (maxCrossDataSets > 15) {
- maxCrossDataSets = maxCrossDataSets + 10 - maxCrossDataSets % 10;
- }
-
- if (maxCrossDataSets == 0) {
- maxCrossDataSets = 1;
- }
-
- // make sure percent axes don't go above 100%
- if (axis.tickOptions.formatString.substring(2, 3) == '%' && maxCrossDataSets > 100) {
- maxCrossDataSets = 100;
- }
-
- // calculate y-values for ticks
- ticks = [];
- numberOfTicks = 2;
- tickDistance = Math.ceil(maxCrossDataSets / numberOfTicks);
- for (var i = 0; i <= numberOfTicks; i++) {
- ticks.push(i * tickDistance);
- }
- axis.ticks = ticks;
- },
-
- /** Get a formatted y values (with unit) */
- formatY: function(value, seriesIndex) {
- var floatVal = parseFloat(value);
- var intVal = parseInt(value, 10);
- if (Math.abs(floatVal - intVal) >= 0.005) {
- value = Math.round(floatVal * 100) / 100;
- } else if (parseFloat(intVal) == floatVal) {
- value = intVal;
- } else {
- value = floatVal;
- }
- if (typeof this.tooltip.yUnits[seriesIndex] != 'undefined') {
- value += this.tooltip.yUnits[seriesIndex];
- }
-
- return value;
- },
-
- /** Show the tppltip. The DOM element is created on the fly. */
- showTooltip: function(head, text) {
- Piwik_Tooltip.showWithTitle(head, text);
- },
-
- /** Hide the tooltip */
- hideTooltip: function() {
- Piwik_Tooltip.hide();
- },
-
- addSeriesPicker: function(targetDivId, lang) {
- this.params.seriesPicker = {
- show: typeof this.seriesPicker.selectableColumns == 'object'
- || typeof this.seriesPicker.selectableRows == 'object',
- selectableColumns: this.seriesPicker.selectableColumns,
- selectableRows: this.seriesPicker.selectableRows,
- multiSelect: this.seriesPicker.multiSelect,
- targetDivId: targetDivId,
- dataTableId: this.dataTableId,
- lang: lang
- };
- },
-
- /**
- * Add an external series toggle.
- * As opposed to addSeriesPicker, the external series toggle can only show/hide
- * series that are already loaded.
- * @param seriesPickerClass a subclass of JQPlotExternalSeriesToggle
- */
- addExternalSeriesToggle: function(seriesPickerClass, targetDivId, initiallyShowAll) {
- new seriesPickerClass(targetDivId, this.originalData, initiallyShowAll);
-
- if (!initiallyShowAll) {
- // initially, show only the first series
- this.data = [this.data[0]];
- this.params.series = [this.params.series[0]];
- }
- }
-
-};
+ // change series
+ target.bind('changeColumns', function (e, columns) {
+ target.trigger('changeSeries', [columns, []]);
+ });
+ target.bind('changeSeries', function (e, columns, rows) {
+ target.trigger('showLoading');
+ if (typeof columns == 'string') {
+ columns = columns.split(',');
+ }
+ if (typeof rows == 'undefined') {
+ rows = [];
+ }
+ else if (typeof rows == 'string') {
+ rows = rows.split(',');
+ }
+ var dataTable = $('#' + self.dataTableId).data('dataTableInstance');
+ dataTable.param.columns = columns.join(',');
+ dataTable.param.rows = rows.join(',');
+ delete dataTable.param.filter_limit;
+ delete dataTable.param.totalRows;
+ if (dataTable.param.filter_sort_column != 'label') {
+ dataTable.param.filter_sort_column = columns[0];
+ }
+ dataTable.param.disable_generic_filters = '0';
+ dataTable.reloadAjaxDataTable(false);
+ });
+
+ // this case happens when there is no data for a line chart
+ if (this.data.length == 0) {
+ target.addClass('pk-emptyGraph');
+ target.data('oldHeight', target.height());
+ target.css('height', 'auto').html(lang.noData);
+ return;
+ }
+
+ // create jqplot chart
+ try {
+ var plot = $.jqplot(targetDivId, this.data, this.params);
+ } catch (e) {
+ // this is thrown when refreshing piwik in the browser
+ if (e != "No plot target specified") {
+ throw e;
+ }
+ }
+
+ // bind tooltip
+ var self = this;
+ target.on('jqplotDataHighlight', function (e, s, i, d) {
+ if (type == 'bar') {
+ self.showBarChartTooltip(s, i);
+ } else if (type == 'pie') {
+ self.showPieChartTooltip(i);
+ }
+ })
+ .on('jqplotDataUnhighlight', function (e, s, i, d) {
+ if (type != 'evolution') {
+ self.hideTooltip();
+ }
+ });
+
+ // handle window resize
+ var plotWidth = target.innerWidth();
+ var timeout = false;
+ target.on('resizeGraph', function () {
+ var width = target.innerWidth();
+ if (width > 0 && Math.abs(plotWidth - width) >= 5) {
+ plotWidth = width;
+ target.trigger('piwikDestroyPlot');
+ (new JQPlot(self.originalData, self.dataTableId))
+ .render(type, targetDivId, lang);
+ }
+ });
+ var resizeListener = function () {
+ if (timeout) {
+ window.clearTimeout(timeout);
+ }
+ timeout = window.setTimeout(function () {
+ target.trigger('resizeGraph');
+ }, 300);
+ };
+ $(window).on('resize', resizeListener);
+
+ // export as image
+ target.on('piwikExportAsImage', function (e) {
+ self.exportAsImage(target, lang);
+ });
+
+ // manage resources
+ target.on('piwikDestroyPlot', function () {
+ $(window).off('resize', resizeListener);
+ plot.destroy();
+ for (var i = 0; i < $.jqplot.visiblePlots.length; i++) {
+ if ($.jqplot.visiblePlots[i] == plot) {
+ $.jqplot.visiblePlots[i] = null;
+ }
+ }
+ $(this).off();
+ });
+
+ if (typeof $.jqplot.visiblePlots == 'undefined') {
+ $.jqplot.visiblePlots = [];
+ $('ul.nav').on('piwikSwitchPage', function () {
+ for (var i = 0; i < $.jqplot.visiblePlots.length; i++) {
+ if ($.jqplot.visiblePlots[i] == null) {
+ continue;
+ }
+ $.jqplot.visiblePlots[i].destroy();
+ }
+ $.jqplot.visiblePlots = [];
+ });
+ }
+
+ if (typeof plot != 'undefined') {
+ $.jqplot.visiblePlots.push(plot);
+ }
+ },
+
+ /** Export the chart as an image */
+ exportAsImage: function (container, lang) {
+ var exportCanvas = document.createElement('canvas');
+ exportCanvas.width = container.width();
+ exportCanvas.height = container.height();
+
+ if (!exportCanvas.getContext) {
+ alert("Sorry, not supported in your browser. Please upgrade your browser :)");
+ return;
+ }
+ var exportCtx = exportCanvas.getContext('2d');
+
+ var canvases = container.find('canvas');
+
+ for (var i = 0; i < canvases.length; i++) {
+ var canvas = canvases.eq(i);
+ var position = canvas.position();
+ var parent = canvas.parent();
+ if (parent.hasClass('jqplot-axis')) {
+ var addPosition = parent.position();
+ position.left += addPosition.left;
+ position.top += addPosition.top + parseInt(parent.css('marginTop'), 10);
+ }
+ exportCtx.drawImage(canvas[0], Math.round(position.left), Math.round(position.top));
+ }
+
+ var exported = exportCanvas.toDataURL("image/png");
+
+ var img = document.createElement('img');
+ img.src = exported;
+
+ img = $(img).css({
+ width: exportCanvas.width + 'px',
+ height: exportCanvas.height + 'px'
+ });
+
+ $(document.createElement('div'))
+ .append('<div style="font-size: 13px; margin-bottom: 10px;">'
+ + lang.exportText + '</div>').append($(img))
+ .dialog({
+ title: lang.exportTitle,
+ modal: true,
+ width: 'auto',
+ position: ['center', 'center'],
+ resizable: false,
+ autoOpen: true,
+ close: function (event, ui) {
+ $(this).dialog("destroy").remove();
+ }
+ });
+ },
+
+
+ // ------------------------------------------------------------
+ // EVOLUTION CHART
+ // ------------------------------------------------------------
+ prepareEvolutionChart: function (targetDivId, lang) {
+ this.setYTicks();
+ this.addSeriesPicker(targetDivId, lang);
+ defaultParams.axes = {
+ xaxis: {
+ pad: 1.0,
+ renderer: $.jqplot.CategoryAxisRenderer,
+ tickOptions: {
+ showGridline: false
+ }
+ }
+ };
+
+ defaultParams.seriesDefaults = {
+ lineWidth: 1,
+ markerOptions: {
+ style: "filledCircle",
+ size: 6,
+ shadow: false
+ }
+ };
+
+ defaultParams.piwikTicks = {
+ showTicks: true,
+ showGrid: true,
+ showHighlight: true
+ };
+
+ this.params = $.extend(true, {}, defaultParams, this.params);
+
+ var self = this;
+ var lastTick = false;
+
+ $('#' + targetDivId)
+ .on('jqplotMouseLeave', function (e, s, i, d) {
+ self.hideTooltip();
+ $(this).css('cursor', 'default');
+ })
+ .on('jqplotClick', function (e, s, i, d) {
+ if (lastTick !== false && typeof self.params.axes.xaxis.onclick != 'undefined'
+ && typeof self.params.axes.xaxis.onclick[lastTick] == 'string') {
+ var url = self.params.axes.xaxis.onclick[lastTick];
+ piwikHelper.redirectToUrl(url);
+ }
+ })
+ .on('jqplotPiwikTickOver', function (e, tick) {
+ lastTick = tick;
+ self.showEvolutionChartTooltip(tick);
+ if (typeof self.params.axes.xaxis.onclick != 'undefined'
+ && typeof self.params.axes.xaxis.onclick[lastTick] == 'string') {
+ $(this).css('cursor', 'pointer');
+ }
+ });
+
+ this.params.legend = {
+ show: false
+ };
+ this.params.canvasLegend = {
+ show: true
+ };
+ },
+
+ showEvolutionChartTooltip: function (i) {
+ var label;
+ if (typeof this.params.axes.xaxis.labels != 'undefined') {
+ label = this.params.axes.xaxis.labels[i];
+ } else {
+ label = this.params.axes.xaxis.ticks[i];
+ }
+
+ var text = [];
+ for (var d = 0; d < this.data.length; d++) {
+ var value = this.formatY(this.data[d][i], d);
+ var series = this.params.series[d].label;
+ text.push('<b>' + value + '</b> ' + series);
+ }
+
+ this.showTooltip(label, text.join('<br />'));
+ },
+
+
+ // ------------------------------------------------------------
+ // PIE CHART
+ // ------------------------------------------------------------
+
+ preparePieChart: function (targetDivId, lang) {
+ this.addSeriesPicker(targetDivId, lang);
+
+ this.params.seriesDefaults = {
+ renderer: $.jqplot.PieRenderer,
+ rendererOptions: {
+ shadow: false,
+ showDataLabels: false,
+ sliceMargin: 1,
+ startAngle: 35
+ }
+ };
+
+ this.params.piwikTicks = {
+ showTicks: false,
+ showGrid: false,
+ showHighlight: false
+ };
+
+ this.params.legend = {
+ show: false
+ };
+ this.params.pieLegend = {
+ show: true
+ };
+ this.params.canvasLegend = {
+ show: true,
+ singleMetric: true
+ };
+
+ // pie charts have a different data format
+ if (!(this.data[0][0] instanceof Array)) { // check if already in different format
+ for (var i = 0; i < this.data[0].length; i++) {
+ this.data[0][i] = [this.params.axes.xaxis.ticks[i], this.data[0][i]];
+ }
+ }
+ },
+
+ showPieChartTooltip: function (i) {
+ var value = this.formatY(this.data[0][i][1], 1); // series index 1 because 0 is the label
+ var series = this.params.series[0].label;
+ var percentage = this.tooltip.percentages[0][i];
+
+ var label = this.data[0][i][0];
+
+ var text = '<b>' + percentage + '%</b> (' + value + ' ' + series + ')';
+ this.showTooltip(label, text);
+ },
+
+
+ // ------------------------------------------------------------
+ // BAR CHART
+ // ------------------------------------------------------------
+
+ prepareBarChart: function (targetDivId, lang) {
+ this.setYTicks();
+ this.addSeriesPicker(targetDivId, lang);
+
+ this.params.seriesDefaults = {
+ renderer: $.jqplot.BarRenderer,
+ rendererOptions: {
+ shadowOffset: 1,
+ shadowDepth: 2,
+ shadowAlpha: .2,
+ fillToZero: true,
+ barMargin: this.data[0].length > 10 ? 2 : 10
+ }
+ };
+
+ this.params.piwikTicks = {
+ showTicks: true,
+ showGrid: false,
+ showHighlight: false
+ };
+
+ this.params.axes.xaxis.renderer = $.jqplot.CategoryAxisRenderer;
+ this.params.axes.xaxis.tickOptions = {
+ showGridline: false
+ };
+
+ this.params.canvasLegend = {
+ show: true
+ };
+ },
+
+ showBarChartTooltip: function (s, i) {
+ var value = this.formatY(this.data[s][i], s);
+ var series = this.params.series[s].label;
+
+ var percentage = '';
+ if (typeof this.tooltip.percentages != 'undefined') {
+ var percentage = this.tooltip.percentages[s][i];
+ percentage = ' (' + percentage + '%)';
+ }
+
+ var label = this.params.axes.xaxis.labels[i];
+ var text = '<b>' + value + '</b> ' + series + percentage;
+ this.showTooltip(label, text);
+ },
+
+
+ // ------------------------------------------------------------
+ // HELPER METHODS
+ // ------------------------------------------------------------
+
+ /** Generate ticks in y direction */
+ setYTicks: function () {
+ // default axis
+ this.setYTicksForAxis('yaxis', this.params.axes.yaxis);
+ // other axes: y2axis, y3axis...
+ for (var i = 2; typeof this.params.axes['y' + i + 'axis'] != 'undefined'; i++) {
+ this.setYTicksForAxis('y' + i + 'axis', this.params.axes['y' + i + 'axis']);
+ }
+ },
+
+ setYTicksForAxis: function (axisName, axis) {
+ // calculate maximum x value of all data sets
+ var maxCrossDataSets = 0;
+ for (var i = 0; i < this.data.length; i++) {
+ if (this.params.series[i].yaxis == axisName) {
+ maxValue = Math.max.apply(Math, this.data[i]);
+ if (maxValue > maxCrossDataSets) {
+ maxCrossDataSets = maxValue;
+ }
+ maxCrossDataSets = parseFloat(maxCrossDataSets);
+ }
+ }
+
+ // add little padding on top
+ maxCrossDataSets += Math.max(1, Math.round(maxCrossDataSets * .03));
+
+ // round to the nearest multiple of ten
+ if (maxCrossDataSets > 15) {
+ maxCrossDataSets = maxCrossDataSets + 10 - maxCrossDataSets % 10;
+ }
+
+ if (maxCrossDataSets == 0) {
+ maxCrossDataSets = 1;
+ }
+
+ // make sure percent axes don't go above 100%
+ if (axis.tickOptions.formatString.substring(2, 3) == '%' && maxCrossDataSets > 100) {
+ maxCrossDataSets = 100;
+ }
+
+ // calculate y-values for ticks
+ ticks = [];
+ numberOfTicks = 2;
+ tickDistance = Math.ceil(maxCrossDataSets / numberOfTicks);
+ for (var i = 0; i <= numberOfTicks; i++) {
+ ticks.push(i * tickDistance);
+ }
+ axis.ticks = ticks;
+ },
+
+ /** Get a formatted y values (with unit) */
+ formatY: function (value, seriesIndex) {
+ var floatVal = parseFloat(value);
+ var intVal = parseInt(value, 10);
+ if (Math.abs(floatVal - intVal) >= 0.005) {
+ value = Math.round(floatVal * 100) / 100;
+ } else if (parseFloat(intVal) == floatVal) {
+ value = intVal;
+ } else {
+ value = floatVal;
+ }
+ if (typeof this.tooltip.yUnits[seriesIndex] != 'undefined') {
+ value += this.tooltip.yUnits[seriesIndex];
+ }
+
+ return value;
+ },
+
+ /** Show the tppltip. The DOM element is created on the fly. */
+ showTooltip: function (head, text) {
+ Piwik_Tooltip.showWithTitle(head, text);
+ },
+
+ /** Hide the tooltip */
+ hideTooltip: function () {
+ Piwik_Tooltip.hide();
+ },
+
+ addSeriesPicker: function (targetDivId, lang) {
+ this.params.seriesPicker = {
+ show: typeof this.seriesPicker.selectableColumns == 'object'
+ || typeof this.seriesPicker.selectableRows == 'object',
+ selectableColumns: this.seriesPicker.selectableColumns,
+ selectableRows: this.seriesPicker.selectableRows,
+ multiSelect: this.seriesPicker.multiSelect,
+ targetDivId: targetDivId,
+ dataTableId: this.dataTableId,
+ lang: lang
+ };
+ },
+
+ /**
+ * Add an external series toggle.
+ * As opposed to addSeriesPicker, the external series toggle can only show/hide
+ * series that are already loaded.
+ * @param seriesPickerClass a subclass of JQPlotExternalSeriesToggle
+ */
+ addExternalSeriesToggle: function (seriesPickerClass, targetDivId, initiallyShowAll) {
+ new seriesPickerClass(targetDivId, this.originalData, initiallyShowAll);
+
+ if (!initiallyShowAll) {
+ // initially, show only the first series
+ this.data = [this.data[0]];
+ this.params.series = [this.params.series[0]];
+ }
+ }
+
+};
// ----------------------------------------------------------------
@@ -590,101 +591,101 @@ JQPlot.prototype = {
// ----------------------------------------------------------------
function JQPlotExternalSeriesToggle(targetDivId, originalConfig, initiallyShowAll) {
- this.init(targetDivId, originalConfig, initiallyShowAll);
+ this.init(targetDivId, originalConfig, initiallyShowAll);
}
JQPlotExternalSeriesToggle.prototype = {
-
- init: function(targetDivId, originalConfig, initiallyShowAll) {
- this.targetDivId = targetDivId;
- this.originalConfig = originalConfig;
- this.originalData = originalConfig.data;
- this.originalSeries = originalConfig.params.series;
- this.originalAxes = originalConfig.params.axes;
- this.originalTooltipUnits = originalConfig.tooltip.yUnits;
- this.originalSeriesColors = originalConfig.params.seriesColors;
- this.initiallyShowAll = initiallyShowAll;
-
- this.activated = [];
- this.target = $('#'+targetDivId);
-
- this.attachEvents();
- },
-
- // can be overridden
- attachEvents: function() {},
-
- // show a single series
- showSeries: function(i) {
- for (var j = 0; j < this.activated.length; j++) {
- this.activated[j] = (i == j);
- }
- this.replot();
- },
-
- // toggle a series (make plotting multiple series possible)
- toggleSeries: function(i) {
- var activatedCount = 0;
- for (var k = 0; k < this.activated.length; k++) {
- if (this.activated[k]) {
- activatedCount++;
- }
- }
- if (activatedCount == 1 && this.activated[i]) {
- // prevent removing the only visible metric
- return;
- }
-
- this.activated[i] = !this.activated[i];
- this.replot();
- },
-
- replot: function() {
- this.beforeReplot();
-
- // build new config and replot
- var usedAxes = [];
- var config = this.originalConfig;
- config.data = [];
- config.params.series = [];
- config.params.axes = {xaxis: this.originalAxes.xaxis};
- config.tooltip.yUnits = [];
- config.params.seriesColors = [];
- for (var j = 0; j < this.activated.length; j++) {
- if (!this.activated[j]) {
- continue;
- }
- config.data.push(this.originalData[j]);
- config.tooltip.yUnits.push(this.originalTooltipUnits[j]);
- config.params.seriesColors.push(this.originalSeriesColors[j]);
- config.params.series.push($.extend(true, {}, this.originalSeries[j]));
- // build array of used axes
- var axis = this.originalSeries[j].yaxis;
- if ($.inArray(axis, usedAxes) == -1) {
- usedAxes.push(axis);
- }
- }
-
- // build new axes config
- var replaceAxes = {};
- for (j = 0; j < usedAxes.length; j++) {
- var originalAxisName = usedAxes[j];
- var newAxisName = (j == 0 ? 'yaxis' : 'y' + (j+1) + 'axis');
- replaceAxes[originalAxisName] = newAxisName;
- config.params.axes[newAxisName] = this.originalAxes[originalAxisName];
- }
-
- // replace axis names in series config
- for (j = 0; j < config.params.series.length; j++) {
- var series = config.params.series[j];
- series.yaxis = replaceAxes[series.yaxis];
- }
-
- this.target.trigger('replot', config);
- },
-
- // can be overridden
- beforeReplot: function() {}
+
+ init: function (targetDivId, originalConfig, initiallyShowAll) {
+ this.targetDivId = targetDivId;
+ this.originalConfig = originalConfig;
+ this.originalData = originalConfig.data;
+ this.originalSeries = originalConfig.params.series;
+ this.originalAxes = originalConfig.params.axes;
+ this.originalTooltipUnits = originalConfig.tooltip.yUnits;
+ this.originalSeriesColors = originalConfig.params.seriesColors;
+ this.initiallyShowAll = initiallyShowAll;
+
+ this.activated = [];
+ this.target = $('#' + targetDivId);
+
+ this.attachEvents();
+ },
+
+ // can be overridden
+ attachEvents: function () {},
+
+ // show a single series
+ showSeries: function (i) {
+ for (var j = 0; j < this.activated.length; j++) {
+ this.activated[j] = (i == j);
+ }
+ this.replot();
+ },
+
+ // toggle a series (make plotting multiple series possible)
+ toggleSeries: function (i) {
+ var activatedCount = 0;
+ for (var k = 0; k < this.activated.length; k++) {
+ if (this.activated[k]) {
+ activatedCount++;
+ }
+ }
+ if (activatedCount == 1 && this.activated[i]) {
+ // prevent removing the only visible metric
+ return;
+ }
+
+ this.activated[i] = !this.activated[i];
+ this.replot();
+ },
+
+ replot: function () {
+ this.beforeReplot();
+
+ // build new config and replot
+ var usedAxes = [];
+ var config = this.originalConfig;
+ config.data = [];
+ config.params.series = [];
+ config.params.axes = {xaxis: this.originalAxes.xaxis};
+ config.tooltip.yUnits = [];
+ config.params.seriesColors = [];
+ for (var j = 0; j < this.activated.length; j++) {
+ if (!this.activated[j]) {
+ continue;
+ }
+ config.data.push(this.originalData[j]);
+ config.tooltip.yUnits.push(this.originalTooltipUnits[j]);
+ config.params.seriesColors.push(this.originalSeriesColors[j]);
+ config.params.series.push($.extend(true, {}, this.originalSeries[j]));
+ // build array of used axes
+ var axis = this.originalSeries[j].yaxis;
+ if ($.inArray(axis, usedAxes) == -1) {
+ usedAxes.push(axis);
+ }
+ }
+
+ // build new axes config
+ var replaceAxes = {};
+ for (j = 0; j < usedAxes.length; j++) {
+ var originalAxisName = usedAxes[j];
+ var newAxisName = (j == 0 ? 'yaxis' : 'y' + (j + 1) + 'axis');
+ replaceAxes[originalAxisName] = newAxisName;
+ config.params.axes[newAxisName] = this.originalAxes[originalAxisName];
+ }
+
+ // replace axis names in series config
+ for (j = 0; j < config.params.series.length; j++) {
+ var series = config.params.series[j];
+ series.yaxis = replaceAxes[series.yaxis];
+ }
+
+ this.target.trigger('replot', config);
+ },
+
+ // can be overridden
+ beforeReplot: function () {}
};
@@ -692,59 +693,56 @@ JQPlotExternalSeriesToggle.prototype = {
// ROW EVOLUTION SERIES TOGGLE
function RowEvolutionSeriesToggle(targetDivId, originalConfig, initiallyShowAll) {
- this.init(targetDivId, originalConfig, initiallyShowAll);
+ this.init(targetDivId, originalConfig, initiallyShowAll);
}
RowEvolutionSeriesToggle.prototype = JQPlotExternalSeriesToggle.prototype;
-RowEvolutionSeriesToggle.prototype.attachEvents = function() {
- var self = this;
- this.seriesPickers = this.target.closest('.rowevolution').find('table.metrics tr');
-
- this.seriesPickers.each(function(i) {
- var el = $(this);
- el.click(function(e) {
- if (e.shiftKey) {
- self.toggleSeries(i);
- } else {
- self.showSeries(i);
- }
- return false;
- });
-
- if (i == 0 || self.initiallyShowAll) {
- // show the active series
- // if initiallyShowAll, all are active; otherwise only the first one
- self.activated.push(true);
- } else {
- // fade out the others
- el.find('td').css('opacity', .5);
- self.activated.push(false);
- }
-
- // prevent selecting in ie & opera (they don't support doing this via css)
- if ($.browser.msie) {
- this.ondrag = function() { return false; };
- this.onselectstart = function() { return false; };
- } else if ($.browser.opera) {
- $(this).attr('unselectable', 'on');
- }
- });
-};
+RowEvolutionSeriesToggle.prototype.attachEvents = function () {
+ var self = this;
+ this.seriesPickers = this.target.closest('.rowevolution').find('table.metrics tr');
-RowEvolutionSeriesToggle.prototype.beforeReplot = function() {
- // fade out if not activated
- for (var i = 0; i < this.activated.length; i++) {
- if (this.activated[i]) {
- this.seriesPickers.eq(i).find('td').css('opacity', 1);
- } else {
- this.seriesPickers.eq(i).find('td').css('opacity', .5);
- }
- }
-};
+ this.seriesPickers.each(function (i) {
+ var el = $(this);
+ el.click(function (e) {
+ if (e.shiftKey) {
+ self.toggleSeries(i);
+ } else {
+ self.showSeries(i);
+ }
+ return false;
+ });
+ if (i == 0 || self.initiallyShowAll) {
+ // show the active series
+ // if initiallyShowAll, all are active; otherwise only the first one
+ self.activated.push(true);
+ } else {
+ // fade out the others
+ el.find('td').css('opacity', .5);
+ self.activated.push(false);
+ }
+ // prevent selecting in ie & opera (they don't support doing this via css)
+ if ($.browser.msie) {
+ this.ondrag = function () { return false; };
+ this.onselectstart = function () { return false; };
+ } else if ($.browser.opera) {
+ $(this).attr('unselectable', 'on');
+ }
+ });
+};
+RowEvolutionSeriesToggle.prototype.beforeReplot = function () {
+ // fade out if not activated
+ for (var i = 0; i < this.activated.length; i++) {
+ if (this.activated[i]) {
+ this.seriesPickers.eq(i).find('td').css('opacity', 1);
+ } else {
+ this.seriesPickers.eq(i).find('td').css('opacity', .5);
+ }
+ }
+};
// ------------------------------------------------------------
@@ -752,160 +750,159 @@ RowEvolutionSeriesToggle.prototype.beforeReplot = function() {
// Handle ticks the piwik way...
// ------------------------------------------------------------
-(function($) {
-
- $.jqplot.PiwikTicks = function(options) {
- // canvas for the grid
- this.piwikTicksCanvas = null;
- // canvas for the highlight
- this.piwikHighlightCanvas = null;
- // renderer used to draw the marker of the highlighted point
- this.markerRenderer = new $.jqplot.MarkerRenderer({
- shadow: false
- });
- // the x tick the mouse is over
- this.currentXTick = false;
- // show the highlight around markers
- this.showHighlight = false;
- // show the grid
- this.showGrid = false;
- // show the ticks
- this.showTicks = false;
-
- $.extend(true, this, options);
- };
-
- $.jqplot.PiwikTicks.init = function(target, data, opts) {
- // add plugin as an attribute to the plot
- var options = opts || {};
- this.plugins.piwikTicks = new $.jqplot.PiwikTicks(options.piwikTicks);
-
- if (typeof $.jqplot.PiwikTicks.init.eventsBound == 'undefined') {
- $.jqplot.PiwikTicks.init.eventsBound = true;
- $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMouseMove]);
- $.jqplot.eventListenerHooks.push(['jqplotMouseLeave', handleMouseLeave]);
- }
- };
-
- // draw the grid
- // called with context of plot
- $.jqplot.PiwikTicks.postDraw = function() {
- var c = this.plugins.piwikTicks;
-
- // highligh canvas
- if (c.showHighlight) {
- c.piwikHighlightCanvas = new $.jqplot.GenericCanvas();
-
- this.eventCanvas._elem.before(c.piwikHighlightCanvas.createElement(
- this._gridPadding, 'jqplot-piwik-highlight-canvas', this._plotDimensions, this));
- c.piwikHighlightCanvas.setContext();
- }
-
- // grid canvas
- if (c.showTicks) {
- var dimensions = this._plotDimensions;
- dimensions.height += 6;
- c.piwikTicksCanvas = new $.jqplot.GenericCanvas();
- this.series[0].shadowCanvas._elem.before(c.piwikTicksCanvas.createElement(
- this._gridPadding, 'jqplot-piwik-ticks-canvas', dimensions, this));
- c.piwikTicksCanvas.setContext();
-
- var ctx = c.piwikTicksCanvas._ctx;
-
- var ticks = this.data[0];
- var totalWidth = ctx.canvas.width;
- var tickWidth = totalWidth / ticks.length;
-
- var xaxisLabels = this.axes.xaxis.ticks;
-
- for (var i = 0; i < ticks.length; i++) {
- var pos = Math.round(i * tickWidth + tickWidth / 2);
- var full = xaxisLabels[i] && xaxisLabels[i] != ' ';
- drawLine(ctx, pos, full, c.showGrid);
- }
- }
- };
-
- $.jqplot.preInitHooks.push($.jqplot.PiwikTicks.init);
- $.jqplot.postDrawHooks.push($.jqplot.PiwikTicks.postDraw);
-
- // draw a 1px line
- function drawLine(ctx, x, full, showGrid) {
- ctx.save();
- ctx.strokeStyle = '#cccccc';
-
- ctx.beginPath();
- ctx.lineWidth = 2;
- var top = 0;
- if ((full && !showGrid) || !full) {
- top = ctx.canvas.height - 5;
- }
- ctx.moveTo(x, top);
- ctx.lineTo(x, full ? ctx.canvas.height : ctx.canvas.height - 2);
- ctx.stroke();
-
- // canvas renders line slightly too large
- ctx.clearRect(x, 0, x + 1, ctx.canvas.height);
-
- ctx.restore();
- }
-
- // tigger the event jqplotPiwikTickOver when the mosue enters
- // and new tick. this is used for tooltips.
- function handleMouseMove(ev, gridpos, datapos, neighbor, plot) {
- var c = plot.plugins.piwikTicks;
-
- var tick = Math.floor(datapos.xaxis + 0.5) - 1;
- if (tick !== c.currentXTick) {
- c.currentXTick = tick;
- plot.target.trigger('jqplotPiwikTickOver', [tick]);
- highlight(plot, tick);
- }
- }
-
- function handleMouseLeave(ev, gridpos, datapos, neighbor, plot) {
- unHighlight(plot);
- plot.plugins.piwikTicks.currentXTick = false;
- }
-
- // highlight a marker
- function highlight(plot, tick) {
- var c = plot.plugins.piwikTicks;
-
- if (!c.showHighlight) {
- return;
- }
-
- unHighlight(plot);
-
- for (var i = 0; i < plot.series.length; i++) {
- var series = plot.series[i];
- var seriesMarkerRenderer = series.markerRenderer;
-
- c.markerRenderer.style = seriesMarkerRenderer.style;
- c.markerRenderer.size = seriesMarkerRenderer.size + 5;
-
- var rgba = $.jqplot.getColorComponents(seriesMarkerRenderer.color);
- var newrgb = [rgba[0], rgba[1], rgba[2]];
- var alpha = rgba[3] * .4;
- c.markerRenderer.color = 'rgba(' + newrgb[0] + ',' + newrgb[1] + ',' + newrgb[2] + ',' + alpha + ')';
- c.markerRenderer.init();
-
- var position = series.gridData[tick];
- c.markerRenderer.draw(position[0], position[1], c.piwikHighlightCanvas._ctx);
- }
- }
-
- function unHighlight(plot) {
- var canvas = plot.plugins.piwikTicks.piwikHighlightCanvas;
- if (canvas !== null) {
- var ctx = canvas._ctx;
- ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
- }
- }
-
-})(jQuery);
+(function ($) {
+ $.jqplot.PiwikTicks = function (options) {
+ // canvas for the grid
+ this.piwikTicksCanvas = null;
+ // canvas for the highlight
+ this.piwikHighlightCanvas = null;
+ // renderer used to draw the marker of the highlighted point
+ this.markerRenderer = new $.jqplot.MarkerRenderer({
+ shadow: false
+ });
+ // the x tick the mouse is over
+ this.currentXTick = false;
+ // show the highlight around markers
+ this.showHighlight = false;
+ // show the grid
+ this.showGrid = false;
+ // show the ticks
+ this.showTicks = false;
+
+ $.extend(true, this, options);
+ };
+
+ $.jqplot.PiwikTicks.init = function (target, data, opts) {
+ // add plugin as an attribute to the plot
+ var options = opts || {};
+ this.plugins.piwikTicks = new $.jqplot.PiwikTicks(options.piwikTicks);
+
+ if (typeof $.jqplot.PiwikTicks.init.eventsBound == 'undefined') {
+ $.jqplot.PiwikTicks.init.eventsBound = true;
+ $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMouseMove]);
+ $.jqplot.eventListenerHooks.push(['jqplotMouseLeave', handleMouseLeave]);
+ }
+ };
+
+ // draw the grid
+ // called with context of plot
+ $.jqplot.PiwikTicks.postDraw = function () {
+ var c = this.plugins.piwikTicks;
+
+ // highligh canvas
+ if (c.showHighlight) {
+ c.piwikHighlightCanvas = new $.jqplot.GenericCanvas();
+
+ this.eventCanvas._elem.before(c.piwikHighlightCanvas.createElement(
+ this._gridPadding, 'jqplot-piwik-highlight-canvas', this._plotDimensions, this));
+ c.piwikHighlightCanvas.setContext();
+ }
+
+ // grid canvas
+ if (c.showTicks) {
+ var dimensions = this._plotDimensions;
+ dimensions.height += 6;
+ c.piwikTicksCanvas = new $.jqplot.GenericCanvas();
+ this.series[0].shadowCanvas._elem.before(c.piwikTicksCanvas.createElement(
+ this._gridPadding, 'jqplot-piwik-ticks-canvas', dimensions, this));
+ c.piwikTicksCanvas.setContext();
+
+ var ctx = c.piwikTicksCanvas._ctx;
+
+ var ticks = this.data[0];
+ var totalWidth = ctx.canvas.width;
+ var tickWidth = totalWidth / ticks.length;
+
+ var xaxisLabels = this.axes.xaxis.ticks;
+
+ for (var i = 0; i < ticks.length; i++) {
+ var pos = Math.round(i * tickWidth + tickWidth / 2);
+ var full = xaxisLabels[i] && xaxisLabels[i] != ' ';
+ drawLine(ctx, pos, full, c.showGrid);
+ }
+ }
+ };
+
+ $.jqplot.preInitHooks.push($.jqplot.PiwikTicks.init);
+ $.jqplot.postDrawHooks.push($.jqplot.PiwikTicks.postDraw);
+
+ // draw a 1px line
+ function drawLine(ctx, x, full, showGrid) {
+ ctx.save();
+ ctx.strokeStyle = '#cccccc';
+
+ ctx.beginPath();
+ ctx.lineWidth = 2;
+ var top = 0;
+ if ((full && !showGrid) || !full) {
+ top = ctx.canvas.height - 5;
+ }
+ ctx.moveTo(x, top);
+ ctx.lineTo(x, full ? ctx.canvas.height : ctx.canvas.height - 2);
+ ctx.stroke();
+
+ // canvas renders line slightly too large
+ ctx.clearRect(x, 0, x + 1, ctx.canvas.height);
+
+ ctx.restore();
+ }
+
+ // tigger the event jqplotPiwikTickOver when the mosue enters
+ // and new tick. this is used for tooltips.
+ function handleMouseMove(ev, gridpos, datapos, neighbor, plot) {
+ var c = plot.plugins.piwikTicks;
+
+ var tick = Math.floor(datapos.xaxis + 0.5) - 1;
+ if (tick !== c.currentXTick) {
+ c.currentXTick = tick;
+ plot.target.trigger('jqplotPiwikTickOver', [tick]);
+ highlight(plot, tick);
+ }
+ }
+
+ function handleMouseLeave(ev, gridpos, datapos, neighbor, plot) {
+ unHighlight(plot);
+ plot.plugins.piwikTicks.currentXTick = false;
+ }
+
+ // highlight a marker
+ function highlight(plot, tick) {
+ var c = plot.plugins.piwikTicks;
+
+ if (!c.showHighlight) {
+ return;
+ }
+
+ unHighlight(plot);
+
+ for (var i = 0; i < plot.series.length; i++) {
+ var series = plot.series[i];
+ var seriesMarkerRenderer = series.markerRenderer;
+
+ c.markerRenderer.style = seriesMarkerRenderer.style;
+ c.markerRenderer.size = seriesMarkerRenderer.size + 5;
+
+ var rgba = $.jqplot.getColorComponents(seriesMarkerRenderer.color);
+ var newrgb = [rgba[0], rgba[1], rgba[2]];
+ var alpha = rgba[3] * .4;
+ c.markerRenderer.color = 'rgba(' + newrgb[0] + ',' + newrgb[1] + ',' + newrgb[2] + ',' + alpha + ')';
+ c.markerRenderer.init();
+
+ var position = series.gridData[tick];
+ c.markerRenderer.draw(position[0], position[1], c.piwikHighlightCanvas._ctx);
+ }
+ }
+
+ function unHighlight(plot) {
+ var canvas = plot.plugins.piwikTicks.piwikHighlightCanvas;
+ if (canvas !== null) {
+ var ctx = canvas._ctx;
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ }
+ }
+
+})(jQuery);
// ------------------------------------------------------------
@@ -913,100 +910,98 @@ RowEvolutionSeriesToggle.prototype.beforeReplot = function() {
// Render legend on canvas
// ------------------------------------------------------------
-(function($) {
-
- $.jqplot.CanvasLegendRenderer = function(options) {
- // canvas for the legend
- this.legendCanvas = null;
- // is it a legend for a single metric only (pie chart)?
- this.singleMetric = false;
- // render the legend?
- this.show = false;
-
- $.extend(true, this, options);
- };
-
- $.jqplot.CanvasLegendRenderer.init = function(target, data, opts) {
- // add plugin as an attribute to the plot
- var options = opts || {};
- this.plugins.canvasLegend = new $.jqplot.CanvasLegendRenderer(options.canvasLegend);
-
- // add padding above the grid
- // legend will be put there
- if (this.plugins.canvasLegend.show) {
- options.gridPadding = {
- top: 21
- };
- }
-
- };
-
- // render the legend
- $.jqplot.CanvasLegendRenderer.postDraw = function() {
- var plot = this;
- var legend = plot.plugins.canvasLegend;
-
- if (!legend.show) {
- return;
- }
-
- // initialize legend canvas
- var padding = {top: 0, right: this._gridPadding.right, bottom: 0, left: this._gridPadding.left};
- var dimensions = {width: this._plotDimensions.width, height: this._gridPadding.top};
- var width = this._plotDimensions.width - this._gridPadding.left - this._gridPadding.right;
-
- legend.legendCanvas = new $.jqplot.GenericCanvas();
- this.eventCanvas._elem.before(legend.legendCanvas.createElement(
- padding, 'jqplot-legend-canvas', dimensions, plot));
- legend.legendCanvas.setContext();
-
- var ctx = legend.legendCanvas._ctx;
- ctx.save();
- ctx.font = '11px Arial';
-
- // render series names
- var x = 0;
- var series = plot.legend._series;
- for (i = 0; i < series.length; i++) {
- var s = series[i];
- var label;
- if (legend.labels && legend.labels[i]) {
- label = legend.labels[i];
- } else {
- label = s.label.toString();
- }
-
- ctx.fillStyle = s.color;
- if (legend.singleMetric)
- {
- ctx.fillStyle = '#666666';
- }
-
- ctx.fillRect(x, 10, 10, 2);
- x += 15;
-
- var nextX = x + ctx.measureText(label).width + 20;
-
- if (nextX + 70 > width) {
- ctx.fillText("[...]", x, 15);
- x += ctx.measureText("[...]").width + 20;
- break;
- }
-
- ctx.fillText(label, x, 15);
- x = nextX;
- }
-
- legend.width = x;
-
- ctx.restore();
- };
-
- $.jqplot.preInitHooks.push($.jqplot.CanvasLegendRenderer.init);
- $.jqplot.postDrawHooks.push($.jqplot.CanvasLegendRenderer.postDraw);
-
-})(jQuery);
+(function ($) {
+
+ $.jqplot.CanvasLegendRenderer = function (options) {
+ // canvas for the legend
+ this.legendCanvas = null;
+ // is it a legend for a single metric only (pie chart)?
+ this.singleMetric = false;
+ // render the legend?
+ this.show = false;
+
+ $.extend(true, this, options);
+ };
+ $.jqplot.CanvasLegendRenderer.init = function (target, data, opts) {
+ // add plugin as an attribute to the plot
+ var options = opts || {};
+ this.plugins.canvasLegend = new $.jqplot.CanvasLegendRenderer(options.canvasLegend);
+
+ // add padding above the grid
+ // legend will be put there
+ if (this.plugins.canvasLegend.show) {
+ options.gridPadding = {
+ top: 21
+ };
+ }
+
+ };
+
+ // render the legend
+ $.jqplot.CanvasLegendRenderer.postDraw = function () {
+ var plot = this;
+ var legend = plot.plugins.canvasLegend;
+
+ if (!legend.show) {
+ return;
+ }
+
+ // initialize legend canvas
+ var padding = {top: 0, right: this._gridPadding.right, bottom: 0, left: this._gridPadding.left};
+ var dimensions = {width: this._plotDimensions.width, height: this._gridPadding.top};
+ var width = this._plotDimensions.width - this._gridPadding.left - this._gridPadding.right;
+
+ legend.legendCanvas = new $.jqplot.GenericCanvas();
+ this.eventCanvas._elem.before(legend.legendCanvas.createElement(
+ padding, 'jqplot-legend-canvas', dimensions, plot));
+ legend.legendCanvas.setContext();
+
+ var ctx = legend.legendCanvas._ctx;
+ ctx.save();
+ ctx.font = '11px Arial';
+
+ // render series names
+ var x = 0;
+ var series = plot.legend._series;
+ for (i = 0; i < series.length; i++) {
+ var s = series[i];
+ var label;
+ if (legend.labels && legend.labels[i]) {
+ label = legend.labels[i];
+ } else {
+ label = s.label.toString();
+ }
+
+ ctx.fillStyle = s.color;
+ if (legend.singleMetric) {
+ ctx.fillStyle = '#666666';
+ }
+
+ ctx.fillRect(x, 10, 10, 2);
+ x += 15;
+
+ var nextX = x + ctx.measureText(label).width + 20;
+
+ if (nextX + 70 > width) {
+ ctx.fillText("[...]", x, 15);
+ x += ctx.measureText("[...]").width + 20;
+ break;
+ }
+
+ ctx.fillText(label, x, 15);
+ x = nextX;
+ }
+
+ legend.width = x;
+
+ ctx.restore();
+ };
+
+ $.jqplot.preInitHooks.push($.jqplot.CanvasLegendRenderer.init);
+ $.jqplot.postDrawHooks.push($.jqplot.CanvasLegendRenderer.postDraw);
+
+})(jQuery);
// ------------------------------------------------------------
@@ -1014,242 +1009,243 @@ RowEvolutionSeriesToggle.prototype.beforeReplot = function() {
// For line charts
// ------------------------------------------------------------
-(function($) {
-
- $.jqplot.SeriesPicker = function(options) {
- // dom element
- this.domElem = null;
- // render the picker?
- this.show = false;
- // the columns that can be selected
- this.selectableColumns = null;
- // the rows that can be selected
- this.selectableRows = null;
- // can multiple rows we selected?
- this.multiSelect = true;
- // css id of the target div dom element
- this.targetDivId = "";
- // the id of the current data table
- this.dataTableId = "";
- // language strings
- this.lang = {};
-
- $.extend(true, this, options);
- };
-
- $.jqplot.SeriesPicker.init = function(target, data, opts) {
- // add plugin as an attribute to the plot
- var options = opts || {};
- this.plugins.seriesPicker = new $.jqplot.SeriesPicker(options.seriesPicker);
- };
-
- // render the link to add series
- $.jqplot.SeriesPicker.postDraw = function() {
- var plot = this;
- var picker = plot.plugins.seriesPicker;
-
- if (!picker.show) {
- return;
- }
-
- // initialize dom element
- picker.domElem = $(document.createElement('a'))
- .addClass('jqplot-seriespicker')
- .attr('href', '#').html('+')
- .css('marginLeft', (plot._gridPadding.left + plot.plugins.canvasLegend.width - 1) + 'px');
-
- picker.domElem.on('hide', function() {
- $(this).css('opacity', .55);
- }).trigger('hide');
-
- plot.baseCanvas._elem.before(picker.domElem);
-
- // show picker on hover
- picker.domElem.hover(function() {
- picker.domElem.css('opacity', 1);
- if (!picker.domElem.hasClass('open')) {
- picker.domElem.addClass('open');
- showPicker(picker, plot._width);
- }
- }, function() {
- // do nothing on mouseout because using this event doesn't work properly.
- // instead, the timeout check beneath is used (checkPickerLeave()).
- }).click(function() {
- return false;
- });
- };
-
- // show the series picker
- function showPicker(picker, plotWidth) {
- var pickerLink = picker.domElem;
- var pickerPopover = $(document.createElement('div'))
- .addClass('jqplock-seriespicker-popover');
-
- var pickerState = {manipulated: false};
-
- // headline
- var title = picker.multiSelect ? picker.lang.metricsToPlot : picker.lang.metricToPlot;
- pickerPopover.append($(document.createElement('p'))
- .addClass('headline').html(title));
-
- if (picker.selectableColumns !== null) {
- // render the selectable columns
- for (var i = 0; i < picker.selectableColumns.length; i++) {
- var column = picker.selectableColumns[i];
- pickerPopover.append(createPickerPopupItem(picker, column, 'column', pickerState, pickerPopover, pickerLink));
- }
- }
-
- if (picker.selectableRows !== null) {
- // "records to plot" subheadline
- pickerPopover.append($(document.createElement('p'))
- .addClass('headline').addClass('recordsToPlot')
- .html(picker.lang.recordsToPlot));
-
- // render the selectable rows
- for (var i = 0; i < picker.selectableRows.length; i++) {
- var row = picker.selectableRows[i];
- pickerPopover.append(createPickerPopupItem(picker, row, 'row', pickerState, pickerPopover, pickerLink));
- }
- }
-
- $('body').prepend(pickerPopover.hide());
- var neededSpace = pickerPopover.outerWidth() + 10;
-
- // try to display popover to the right
- var linkOffset = pickerLink.offset();
- if (navigator.appVersion.indexOf("MSIE 7.") != -1) {
- linkOffset.left -= 10;
- }
- var margin = (parseInt(pickerLink.css('marginLeft'), 10) - 4);
- if (margin + neededSpace < plotWidth
- // make sure it's not too far to the left
- || margin - neededSpace + 60 < 0) {
- pickerPopover.css('marginLeft', (linkOffset.left - 4) + 'px').show();
- } else {
- // display to the left
- pickerPopover.addClass('alignright')
- .css('marginLeft', (linkOffset.left - neededSpace + 38) + 'px')
- .css('backgroundPosition', (pickerPopover.outerWidth() - 25) + 'px 4px')
- .show();
- }
- pickerPopover.css('marginTop', (linkOffset.top - 5) + 'px').show();
-
- // hide and replot on mouse leave
- checkPickerLeave(pickerPopover, function() {
- var replot = pickerState.manipulated;
- hidePicker(picker, pickerPopover, pickerLink, replot);
- });
- }
-
- function createPickerPopupItem(picker, config, type, pickerState, pickerPopover, pickerLink) {
- var checkbox = $(document.createElement('input')).addClass('select')
- .attr('type', picker.multiSelect ? 'checkbox' : 'radio');
-
- if (config.displayed && !(!picker.multiSelect && pickerState.oneChecked)) {
- checkbox.prop('checked', true);
- pickerState.oneChecked = true;
- }
-
- // if we are rendering a column, remember the column name
- // if it's a row, remember the string that can be used to match the row
- checkbox.data('name', type == 'column' ? config.column : config.matcher);
-
- var el = $(document.createElement('p'))
- .append(checkbox)
- .append(type == 'column' ? config.translation : config.label)
- .addClass(type == 'column' ? 'pickColumn' : 'pickRow');
-
- var replot = function() {
- unbindPickerLeaveCheck();
- hidePicker(picker, pickerPopover, pickerLink, true);
- };
-
- var checkBox = function(box) {
- if (!picker.multiSelect) {
- pickerPopover.find('input.select:not(.current)').prop('checked', false);
- }
- box.prop('checked', true);
- replot();
- };
-
- el.click(function(e) {
- pickerState.manipulated = true;
- var box = $(this).find('input.select');
- if (!$(e.target).is('input.select')) {
- if (box.is(':checked')) {
- box.prop('checked', false);
- } else {
- checkBox(box);
- }
- } else {
- if (box.is(':checked')) {
- checkBox(box);
- }
- }
- });
-
- return el;
- }
-
- // check whether the mouse has left the picker
- var onMouseMove;
- function checkPickerLeave(pickerPopover, onLeaveCallback) {
- var offset = pickerPopover.offset();
- var minX = offset.left;
- var minY = offset.top;
- var maxX = minX + pickerPopover.outerWidth();
- var maxY = minY + pickerPopover.outerHeight();
- var currentX, currentY;
- onMouseMove = function(e) {
- currentX = e.pageX;
- currentY = e.pageY;
- if (currentX < minX || currentX > maxX
- || currentY < minY || currentY > maxY) {
- unbindPickerLeaveCheck();
- onLeaveCallback();
- }
- };
- $(document).mousemove(onMouseMove);
- }
- function unbindPickerLeaveCheck() {
- $(document).unbind('mousemove', onMouseMove);
- }
-
- function hidePicker(picker, pickerPopover, pickerLink, replot) {
- // hide picker
- pickerPopover.hide();
- pickerLink.trigger('hide').removeClass('open');
-
- // replot
- if (replot) {
- var columns = [];
- var rows = [];
- pickerPopover.find('input:checked').each(function() {
- if ($(this).closest('p').hasClass('pickRow')) {
- rows.push($(this).data('name'));
- } else {
- columns.push($(this).data('name'));
- }
- });
- var noRowSelected = pickerPopover.find('.pickRow').size() > 0
- && pickerPopover.find('.pickRow input:checked').size() == 0;
- if (columns.length > 0 && !noRowSelected) {
-
- $('#'+picker.targetDivId).trigger('changeSeries', [columns, rows]);
- // inform dashboard widget about changed parameters (to be restored on reload)
- $('#'+picker.targetDivId).parents('[widgetId]').trigger('setParameters', {columns: columns, rows: rows});
- }
- }
-
- pickerPopover.remove();
- }
-
- $.jqplot.preInitHooks.push($.jqplot.SeriesPicker.init);
- $.jqplot.postDrawHooks.push($.jqplot.SeriesPicker.postDraw);
-
-})(jQuery);
+(function ($) {
+
+ $.jqplot.SeriesPicker = function (options) {
+ // dom element
+ this.domElem = null;
+ // render the picker?
+ this.show = false;
+ // the columns that can be selected
+ this.selectableColumns = null;
+ // the rows that can be selected
+ this.selectableRows = null;
+ // can multiple rows we selected?
+ this.multiSelect = true;
+ // css id of the target div dom element
+ this.targetDivId = "";
+ // the id of the current data table
+ this.dataTableId = "";
+ // language strings
+ this.lang = {};
+
+ $.extend(true, this, options);
+ };
+
+ $.jqplot.SeriesPicker.init = function (target, data, opts) {
+ // add plugin as an attribute to the plot
+ var options = opts || {};
+ this.plugins.seriesPicker = new $.jqplot.SeriesPicker(options.seriesPicker);
+ };
+
+ // render the link to add series
+ $.jqplot.SeriesPicker.postDraw = function () {
+ var plot = this;
+ var picker = plot.plugins.seriesPicker;
+
+ if (!picker.show) {
+ return;
+ }
+
+ // initialize dom element
+ picker.domElem = $(document.createElement('a'))
+ .addClass('jqplot-seriespicker')
+ .attr('href', '#').html('+')
+ .css('marginLeft', (plot._gridPadding.left + plot.plugins.canvasLegend.width - 1) + 'px');
+
+ picker.domElem.on('hide',function () {
+ $(this).css('opacity', .55);
+ }).trigger('hide');
+
+ plot.baseCanvas._elem.before(picker.domElem);
+
+ // show picker on hover
+ picker.domElem.hover(function () {
+ picker.domElem.css('opacity', 1);
+ if (!picker.domElem.hasClass('open')) {
+ picker.domElem.addClass('open');
+ showPicker(picker, plot._width);
+ }
+ },function () {
+ // do nothing on mouseout because using this event doesn't work properly.
+ // instead, the timeout check beneath is used (checkPickerLeave()).
+ }).click(function () {
+ return false;
+ });
+ };
+
+ // show the series picker
+ function showPicker(picker, plotWidth) {
+ var pickerLink = picker.domElem;
+ var pickerPopover = $(document.createElement('div'))
+ .addClass('jqplock-seriespicker-popover');
+
+ var pickerState = {manipulated: false};
+ // headline
+ var title = picker.multiSelect ? picker.lang.metricsToPlot : picker.lang.metricToPlot;
+ pickerPopover.append($(document.createElement('p'))
+ .addClass('headline').html(title));
+
+ if (picker.selectableColumns !== null) {
+ // render the selectable columns
+ for (var i = 0; i < picker.selectableColumns.length; i++) {
+ var column = picker.selectableColumns[i];
+ pickerPopover.append(createPickerPopupItem(picker, column, 'column', pickerState, pickerPopover, pickerLink));
+ }
+ }
+
+ if (picker.selectableRows !== null) {
+ // "records to plot" subheadline
+ pickerPopover.append($(document.createElement('p'))
+ .addClass('headline').addClass('recordsToPlot')
+ .html(picker.lang.recordsToPlot));
+
+ // render the selectable rows
+ for (var i = 0; i < picker.selectableRows.length; i++) {
+ var row = picker.selectableRows[i];
+ pickerPopover.append(createPickerPopupItem(picker, row, 'row', pickerState, pickerPopover, pickerLink));
+ }
+ }
+
+ $('body').prepend(pickerPopover.hide());
+ var neededSpace = pickerPopover.outerWidth() + 10;
+
+ // try to display popover to the right
+ var linkOffset = pickerLink.offset();
+ if (navigator.appVersion.indexOf("MSIE 7.") != -1) {
+ linkOffset.left -= 10;
+ }
+ var margin = (parseInt(pickerLink.css('marginLeft'), 10) - 4);
+ if (margin + neededSpace < plotWidth
+ // make sure it's not too far to the left
+ || margin - neededSpace + 60 < 0) {
+ pickerPopover.css('marginLeft', (linkOffset.left - 4) + 'px').show();
+ } else {
+ // display to the left
+ pickerPopover.addClass('alignright')
+ .css('marginLeft', (linkOffset.left - neededSpace + 38) + 'px')
+ .css('backgroundPosition', (pickerPopover.outerWidth() - 25) + 'px 4px')
+ .show();
+ }
+ pickerPopover.css('marginTop', (linkOffset.top - 5) + 'px').show();
+
+ // hide and replot on mouse leave
+ checkPickerLeave(pickerPopover, function () {
+ var replot = pickerState.manipulated;
+ hidePicker(picker, pickerPopover, pickerLink, replot);
+ });
+ }
+
+ function createPickerPopupItem(picker, config, type, pickerState, pickerPopover, pickerLink) {
+ var checkbox = $(document.createElement('input')).addClass('select')
+ .attr('type', picker.multiSelect ? 'checkbox' : 'radio');
+
+ if (config.displayed && !(!picker.multiSelect && pickerState.oneChecked)) {
+ checkbox.prop('checked', true);
+ pickerState.oneChecked = true;
+ }
+
+ // if we are rendering a column, remember the column name
+ // if it's a row, remember the string that can be used to match the row
+ checkbox.data('name', type == 'column' ? config.column : config.matcher);
+
+ var el = $(document.createElement('p'))
+ .append(checkbox)
+ .append(type == 'column' ? config.translation : config.label)
+ .addClass(type == 'column' ? 'pickColumn' : 'pickRow');
+
+ var replot = function () {
+ unbindPickerLeaveCheck();
+ hidePicker(picker, pickerPopover, pickerLink, true);
+ };
+
+ var checkBox = function (box) {
+ if (!picker.multiSelect) {
+ pickerPopover.find('input.select:not(.current)').prop('checked', false);
+ }
+ box.prop('checked', true);
+ replot();
+ };
+
+ el.click(function (e) {
+ pickerState.manipulated = true;
+ var box = $(this).find('input.select');
+ if (!$(e.target).is('input.select')) {
+ if (box.is(':checked')) {
+ box.prop('checked', false);
+ } else {
+ checkBox(box);
+ }
+ } else {
+ if (box.is(':checked')) {
+ checkBox(box);
+ }
+ }
+ });
+
+ return el;
+ }
+
+ // check whether the mouse has left the picker
+ var onMouseMove;
+
+ function checkPickerLeave(pickerPopover, onLeaveCallback) {
+ var offset = pickerPopover.offset();
+ var minX = offset.left;
+ var minY = offset.top;
+ var maxX = minX + pickerPopover.outerWidth();
+ var maxY = minY + pickerPopover.outerHeight();
+ var currentX, currentY;
+ onMouseMove = function (e) {
+ currentX = e.pageX;
+ currentY = e.pageY;
+ if (currentX < minX || currentX > maxX
+ || currentY < minY || currentY > maxY) {
+ unbindPickerLeaveCheck();
+ onLeaveCallback();
+ }
+ };
+ $(document).mousemove(onMouseMove);
+ }
+
+ function unbindPickerLeaveCheck() {
+ $(document).unbind('mousemove', onMouseMove);
+ }
+
+ function hidePicker(picker, pickerPopover, pickerLink, replot) {
+ // hide picker
+ pickerPopover.hide();
+ pickerLink.trigger('hide').removeClass('open');
+
+ // replot
+ if (replot) {
+ var columns = [];
+ var rows = [];
+ pickerPopover.find('input:checked').each(function () {
+ if ($(this).closest('p').hasClass('pickRow')) {
+ rows.push($(this).data('name'));
+ } else {
+ columns.push($(this).data('name'));
+ }
+ });
+ var noRowSelected = pickerPopover.find('.pickRow').size() > 0
+ && pickerPopover.find('.pickRow input:checked').size() == 0;
+ if (columns.length > 0 && !noRowSelected) {
+
+ $('#' + picker.targetDivId).trigger('changeSeries', [columns, rows]);
+ // inform dashboard widget about changed parameters (to be restored on reload)
+ $('#' + picker.targetDivId).parents('[widgetId]').trigger('setParameters', {columns: columns, rows: rows});
+ }
+ }
+
+ pickerPopover.remove();
+ }
+
+ $.jqplot.preInitHooks.push($.jqplot.SeriesPicker.init);
+ $.jqplot.postDrawHooks.push($.jqplot.SeriesPicker.postDraw);
+
+})(jQuery);
// ------------------------------------------------------------
@@ -1257,136 +1253,136 @@ RowEvolutionSeriesToggle.prototype.beforeReplot = function() {
// Render legend inside the pie graph
// ------------------------------------------------------------
-(function($) {
-
- $.jqplot.PieLegend = function(options) {
- // canvas for the legend
- this.pieLegendCanvas = null;
- // render the legend?
- this.show = false;
-
- $.extend(true, this, options);
- };
-
- $.jqplot.PieLegend.init = function(target, data, opts) {
- // add plugin as an attribute to the plot
- var options = opts || {};
- this.plugins.pieLegend = new $.jqplot.PieLegend(options.pieLegend);
- };
-
- // render the legend
- $.jqplot.PieLegend.postDraw = function() {
- var plot = this;
- var legend = plot.plugins.pieLegend;
-
- if (!legend.show) {
- return;
- }
-
- var series = plot.series[0];
- var angles = series._sliceAngles;
- var radius = series._diameter / 2;
- var center = series._center;
- var colors = this.seriesColors;
-
- // concentric line angles
- var lineAngles = [];
- for (var i = 0; i < angles.length; i++) {
- lineAngles.push((angles[i][0] + angles[i][1]) / 2 + Math.PI / 2);
- }
-
- // labels
- var labels = [];
- var data = series._plotData;
- for (i = 0; i < data.length; i++) {
- labels.push(data[i][0]);
- }
-
- // initialize legend canvas
- legend.pieLegendCanvas = new $.jqplot.GenericCanvas();
- plot.series[0].canvas._elem.before(legend.pieLegendCanvas.createElement(
- plot._gridPadding, 'jqplot-pie-legend-canvas', plot._plotDimensions, plot));
- legend.pieLegendCanvas.setContext();
-
- var ctx = legend.pieLegendCanvas._ctx;
- ctx.save();
-
- ctx.font = '11px Arial';
-
- // render labels
- var height = legend.pieLegendCanvas._elem.height();
- var x1, x2, y1, y2, lastY2 = false, right, lastRight = false;
- for (i = 0; i < labels.length; i++) {
- var label = labels[i];
-
- ctx.strokeStyle = colors[i % colors.length];
- ctx.lineCap = 'round';
- ctx.lineWidth = 1;
-
- // concentric line
- x1 = center[0] + Math.sin(lineAngles[i]) * (radius);
- y1 = center[1] - Math.cos(lineAngles[i]) * (radius);
-
- x2 = center[0] + Math.sin(lineAngles[i]) * (radius + 7);
- y2 = center[1] - Math.cos(lineAngles[i]) * (radius + 7);
-
- right = x2 > center[0];
-
- // move close labels
- if (lastY2 !== false && lastRight == right && (
- (right && y2 - lastY2 < 13) ||
- (!right && lastY2 - y2 < 13))) {
-
- if (x1 > center[0]) {
- // move down if the label is in the right half of the graph
- y2 = lastY2 + 13;
- } else {
- // move up if in left halt
- y2 = lastY2 - 13;
- }
- }
-
- if (y2 < 4 || y2 + 4 > height) {
- continue;
- }
-
- ctx.beginPath();
- ctx.moveTo(x1, y1);
- ctx.lineTo(x2, y2);
-
- ctx.closePath();
- ctx.stroke();
-
- // horizontal line
- ctx.beginPath();
- ctx.moveTo(x2, y2);
- if (right) {
- ctx.lineTo(x2 + 5, y2);
- } else {
- ctx.lineTo(x2 - 5, y2);
- }
-
- ctx.closePath();
- ctx.stroke();
-
- lastY2 = y2;
- lastRight = right;
-
- // text
- if (right) {
- x = x2 + 9;
- } else {
- x = x2 - 9 - ctx.measureText(label).width;
- }
-
- ctx.fillStyle = '#666666';
- ctx.fillText(label, x, y2 + 3);
- }
-
- ctx.restore();
- };
-
- $.jqplot.preInitHooks.push($.jqplot.PieLegend.init);
- $.jqplot.postDrawHooks.push($.jqplot.PieLegend.postDraw);
-
+(function ($) {
+
+ $.jqplot.PieLegend = function (options) {
+ // canvas for the legend
+ this.pieLegendCanvas = null;
+ // render the legend?
+ this.show = false;
+
+ $.extend(true, this, options);
+ };
+
+ $.jqplot.PieLegend.init = function (target, data, opts) {
+ // add plugin as an attribute to the plot
+ var options = opts || {};
+ this.plugins.pieLegend = new $.jqplot.PieLegend(options.pieLegend);
+ };
+
+ // render the legend
+ $.jqplot.PieLegend.postDraw = function () {
+ var plot = this;
+ var legend = plot.plugins.pieLegend;
+
+ if (!legend.show) {
+ return;
+ }
+
+ var series = plot.series[0];
+ var angles = series._sliceAngles;
+ var radius = series._diameter / 2;
+ var center = series._center;
+ var colors = this.seriesColors;
+
+ // concentric line angles
+ var lineAngles = [];
+ for (var i = 0; i < angles.length; i++) {
+ lineAngles.push((angles[i][0] + angles[i][1]) / 2 + Math.PI / 2);
+ }
+
+ // labels
+ var labels = [];
+ var data = series._plotData;
+ for (i = 0; i < data.length; i++) {
+ labels.push(data[i][0]);
+ }
+
+ // initialize legend canvas
+ legend.pieLegendCanvas = new $.jqplot.GenericCanvas();
+ plot.series[0].canvas._elem.before(legend.pieLegendCanvas.createElement(
+ plot._gridPadding, 'jqplot-pie-legend-canvas', plot._plotDimensions, plot));
+ legend.pieLegendCanvas.setContext();
+
+ var ctx = legend.pieLegendCanvas._ctx;
+ ctx.save();
+
+ ctx.font = '11px Arial';
+
+ // render labels
+ var height = legend.pieLegendCanvas._elem.height();
+ var x1, x2, y1, y2, lastY2 = false, right, lastRight = false;
+ for (i = 0; i < labels.length; i++) {
+ var label = labels[i];
+
+ ctx.strokeStyle = colors[i % colors.length];
+ ctx.lineCap = 'round';
+ ctx.lineWidth = 1;
+
+ // concentric line
+ x1 = center[0] + Math.sin(lineAngles[i]) * (radius);
+ y1 = center[1] - Math.cos(lineAngles[i]) * (radius);
+
+ x2 = center[0] + Math.sin(lineAngles[i]) * (radius + 7);
+ y2 = center[1] - Math.cos(lineAngles[i]) * (radius + 7);
+
+ right = x2 > center[0];
+
+ // move close labels
+ if (lastY2 !== false && lastRight == right && (
+ (right && y2 - lastY2 < 13) ||
+ (!right && lastY2 - y2 < 13))) {
+
+ if (x1 > center[0]) {
+ // move down if the label is in the right half of the graph
+ y2 = lastY2 + 13;
+ } else {
+ // move up if in left halt
+ y2 = lastY2 - 13;
+ }
+ }
+
+ if (y2 < 4 || y2 + 4 > height) {
+ continue;
+ }
+
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+
+ ctx.closePath();
+ ctx.stroke();
+
+ // horizontal line
+ ctx.beginPath();
+ ctx.moveTo(x2, y2);
+ if (right) {
+ ctx.lineTo(x2 + 5, y2);
+ } else {
+ ctx.lineTo(x2 - 5, y2);
+ }
+
+ ctx.closePath();
+ ctx.stroke();
+
+ lastY2 = y2;
+ lastRight = right;
+
+ // text
+ if (right) {
+ x = x2 + 9;
+ } else {
+ x = x2 - 9 - ctx.measureText(label).width;
+ }
+
+ ctx.fillStyle = '#666666';
+ ctx.fillText(label, x, y2 + 3);
+ }
+
+ ctx.restore();
+ };
+
+ $.jqplot.preInitHooks.push($.jqplot.PieLegend.init);
+ $.jqplot.postDrawHooks.push($.jqplot.PieLegend.postDraw);
+
})(jQuery);
diff --git a/plugins/CoreHome/templates/jquery.ui.autocomplete.css b/plugins/CoreHome/templates/jquery.ui.autocomplete.css
index 9c10a02c22..dfe6df80e8 100644
--- a/plugins/CoreHome/templates/jquery.ui.autocomplete.css
+++ b/plugins/CoreHome/templates/jquery.ui.autocomplete.css
@@ -12,62 +12,65 @@
/* workarounds */
* html .ui-autocomplete {
/* without this, the menu expands to 100% in IE6 */
- width:1px;
+ width: 1px;
}
/* Menu
----------------------------------*/
.ui-menu {
- list-style:none;
- padding: 6px;
- margin: 0;
- display:block;
- position: relative;
- font-family: Arial, Verdana, Arial, Helvetica, sans-serif;
+ list-style: none;
+ padding: 6px;
+ margin: 0;
+ display: block;
+ position: relative;
+ font-family: Arial, Verdana, Arial, Helvetica, sans-serif;
}
+
.ui-menu .ui-menu {
- margin-top: -3px;
+ margin-top: -3px;
margin-bottom: 8px;
}
+
.ui-menu .ui-menu-item {
- line-height:18px;
- padding:0;
- height:auto;
- display:block;
- text-decoration:none;
- white-space:nowrap;
+ line-height: 18px;
+ padding: 0;
+ height: auto;
+ display: block;
+ text-decoration: none;
+ white-space: nowrap;
}
+
.ui-menu .ui-menu-item a {
- line-height:18px;
- color:#255792;
- font-size: 12px;
- padding: 0 5px 0 5px;
- position: relative;
+ line-height: 18px;
+ color: #255792;
+ font-size: 12px;
+ padding: 0 5px 0 5px;
+ position: relative;
}
.ui-menu .ui-menu-item a.ui-state-hover,
.ui-menu .ui-menu-item a.ui-state-active {
- font-weight: normal;
- margin: 0;
+ font-weight: normal;
+ margin: 0;
}
-
-.ui-widget-content {
- background:url(../../../libs/jquery/themes/base/images/ui-bg_flat_75_ffffff_40x100.png) repeat-x scroll 50% 50% #FFFFFF;
+.ui-widget-content {
+ background: url(../../../libs/jquery/themes/base/images/ui-bg_flat_75_ffffff_40x100.png) repeat-x scroll 50% 50% #FFFFFF;
border: 0 solid #D4D4D4;
- color:#222;
+ color: #222;
}
+
.ui-corner-all {
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
}
-.ui-menu .ui-menu-item a.ui-state-hover{
- background:#ebeae6;
- border: 0;
+.ui-menu .ui-menu-item a.ui-state-hover {
+ background: #ebeae6;
+ border: 0;
border-radius: 0;
- -moz-border-radius: 0;
+ -moz-border-radius: 0;
-webkit-radius-radius: 0;
}
diff --git a/plugins/CoreHome/templates/js_css_includes.tpl b/plugins/CoreHome/templates/js_css_includes.tpl
index 7470a67c18..d89841fd36 100644
--- a/plugins/CoreHome/templates/js_css_includes.tpl
+++ b/plugins/CoreHome/templates/js_css_includes.tpl
@@ -1,5 +1,5 @@
{includeAssets type="css"}
{includeAssets type="js"}
{if 'General_LayoutDirection'|translate =='rtl'}
-<link rel="stylesheet" type="text/css" href="themes/default/rtl.css" />
+ <link rel="stylesheet" type="text/css" href="themes/default/rtl.css"/>
{/if}
diff --git a/plugins/CoreHome/templates/js_disabled_notice.tpl b/plugins/CoreHome/templates/js_disabled_notice.tpl
index bd3bd838df..50d08e0f56 100644
--- a/plugins/CoreHome/templates/js_disabled_notice.tpl
+++ b/plugins/CoreHome/templates/js_disabled_notice.tpl
@@ -1 +1,3 @@
-<noscript><div id="javascriptDisabled">{'CoreHome_JavascriptDisabled'|translate:'<a href="">':'</a>'}</div></noscript>
+<noscript>
+ <div id="javascriptDisabled">{'CoreHome_JavascriptDisabled'|translate:'<a href="">':'</a>'}</div>
+</noscript>
diff --git a/plugins/CoreHome/templates/js_global_variables.tpl b/plugins/CoreHome/templates/js_global_variables.tpl
index f5533c6634..cf8ce09ae2 100644
--- a/plugins/CoreHome/templates/js_global_variables.tpl
+++ b/plugins/CoreHome/templates/js_global_variables.tpl
@@ -1,29 +1,43 @@
<script type="text/javascript">
- var piwik = {literal}{}{/literal};
- piwik.token_auth = "{$token_auth}";
- piwik.piwik_url = "{$piwikUrl}";
- {if isset($userLogin)}piwik.userLogin = "{$userLogin|escape:'javascript'}";{/if}
- {if isset($idSite)}piwik.idSite = "{$idSite}";{/if}
- {if isset($siteName)}piwik.siteName = "{$siteName|escape:'javascript'}";{/if}
- {if isset($siteMainUrl)}piwik.siteMainUrl = "{$siteMainUrl|escape:'javascript'}";{/if}
- {if isset($period)}piwik.period = "{$period}";{/if}
- {* piwik.currentDateString should not be used other than by the calendar Javascript
- (it is not set to the expected value when period=range)
- Use broadcast.getValueFromUrl('date') instead
- *}
- piwik.currentDateString = "{if isset($date)}{$date}{elseif isset($endDate)}{$endDate}{/if}";
- {if isset($startDate)}piwik.startDateString = "{$startDate}";{/if}
- {if isset($endDate)}piwik.endDateString = "{$endDate}";{/if}
- {if isset($minDateYear)}piwik.minDateYear = {$minDateYear};{/if}
- {if isset($minDateMonth)}piwik.minDateMonth = parseInt("{$minDateMonth}", 10);{/if}
- {if isset($minDateDay)}piwik.minDateDay = parseInt("{$minDateDay}", 10);{/if}
- {if isset($maxDateYear)}piwik.maxDateYear = {$maxDateYear};{/if}
- {if isset($maxDateMonth)}piwik.maxDateMonth = parseInt("{$maxDateMonth}", 10);{/if}
- {if isset($maxDateDay)}piwik.maxDateDay = parseInt("{$maxDateDay}", 10);{/if}
- {if isset($language)}piwik.language = "{$language}";{/if}
- {if !empty($config_action_url_category_delimiter)}
- piwik.config = {literal}{}{/literal};
- piwik.config.action_url_category_delimiter = "{$config_action_url_category_delimiter}";
- {/if}
+ var piwik = {literal}{}{/literal};
+ piwik.token_auth = "{$token_auth}";
+ piwik.piwik_url = "{$piwikUrl}";
+ {if isset($userLogin)}piwik.userLogin = "{$userLogin|escape:'javascript'}";
+ {/if}
+ {if isset($idSite)}piwik.idSite = "{$idSite}";
+ {/if}
+ {if isset($siteName)}piwik.siteName = "{$siteName|escape:'javascript'}";
+ {/if}
+ {if isset($siteMainUrl)}piwik.siteMainUrl = "{$siteMainUrl|escape:'javascript'}";
+ {/if}
+ {if isset($period)}piwik.period = "{$period}";
+ {/if}
+ {* piwik.currentDateString should not be used other than by the calendar Javascript
+ (it is not set to the expected value when period=range)
+ Use broadcast.getValueFromUrl('date') instead
+ *}
+ piwik.currentDateString = "{if isset($date)}{$date}{elseif isset($endDate)}{$endDate}{/if}";
+ {if isset($startDate)}piwik.startDateString = "{$startDate}";
+ {/if}
+ {if isset($endDate)}piwik.endDateString = "{$endDate}";
+ {/if}
+ {if isset($minDateYear)}piwik.minDateYear = {$minDateYear};
+ {/if}
+ {if isset($minDateMonth)}piwik.minDateMonth = parseInt("{$minDateMonth}", 10);
+ {/if}
+ {if isset($minDateDay)}piwik.minDateDay = parseInt("{$minDateDay}", 10);
+ {/if}
+ {if isset($maxDateYear)}piwik.maxDateYear = {$maxDateYear};
+ {/if}
+ {if isset($maxDateMonth)}piwik.maxDateMonth = parseInt("{$maxDateMonth}", 10);
+ {/if}
+ {if isset($maxDateDay)}piwik.maxDateDay = parseInt("{$maxDateDay}", 10);
+ {/if}
+ {if isset($language)}piwik.language = "{$language}";
+ {/if}
+ {if !empty($config_action_url_category_delimiter)}
+ piwik.config = {literal}{}{/literal};
+ piwik.config.action_url_category_delimiter = "{$config_action_url_category_delimiter}";
+ {/if}
</script>
-<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=IE8" />
+<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=IE8"/>
diff --git a/plugins/CoreHome/templates/logo.tpl b/plugins/CoreHome/templates/logo.tpl
index 7a95a3e410..50a309f328 100644
--- a/plugins/CoreHome/templates/logo.tpl
+++ b/plugins/CoreHome/templates/logo.tpl
@@ -1,10 +1,11 @@
<span id="logo">
-<a href="index.php" title="{if $isCustomLogo}{'General_PoweredBy'|translate} {/if}Piwik # {'General_OpenSourceWebAnalytics'|translate}" style="text-decoration: none;">
- {if $hasSVGLogo}
- <img src='{$logoSVG}' alt="{if $isCustomLogo}{'General_PoweredBy'|translate} {/if}Piwik" style='margin-left: 10px' height='40' class="ie-hide" />
- <!--[if lt IE 9]>
- {/if}
- <img src='{$logoHeader}' alt="{if $isCustomLogo}{'General_PoweredBy'|translate} {/if}Piwik" style='margin-left:10px' height='50' />
- {if $hasSVGLogo}<![endif]-->{/if}
+<a href="index.php" title="{if $isCustomLogo}{'General_PoweredBy'|translate} {/if}Piwik # {'General_OpenSourceWebAnalytics'|translate}"
+ style="text-decoration: none;">
+ {if $hasSVGLogo}
+<img src='{$logoSVG}' alt="{if $isCustomLogo}{'General_PoweredBy'|translate} {/if}Piwik" style='margin-left: 10px' height='40' class="ie-hide"/>
+ <!--[if lt IE 9]>
+ {/if}
+ <img src='{$logoHeader}' alt="{if $isCustomLogo}{'General_PoweredBy'|translate} {/if}Piwik" style='margin-left:10px' height='50'/>
+ {if $hasSVGLogo}<![endif]-->{/if}
</a>
</span>
diff --git a/plugins/CoreHome/templates/menu.js b/plugins/CoreHome/templates/menu.js
index d9067268c5..3be160b1ca 100644
--- a/plugins/CoreHome/templates/menu.js
+++ b/plugins/CoreHome/templates/menu.js
@@ -5,81 +5,72 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function menu()
-{
+function menu() {
this.param = {};
}
menu.prototype =
-{
+{
resetTimer: null,
-
- overMainLI: function ()
- {
+
+ overMainLI: function () {
$(this).siblings().removeClass('sfHover');
$(this).addClass('sfHover');
clearTimeout(menu.prototype.resetTimer);
},
-
- outMainLI: function ()
- {
+
+ outMainLI: function () {
clearTimeout(menu.prototype.resetTimer);
- menu.prototype.resetTimer = setTimeout(function(){
+ menu.prototype.resetTimer = setTimeout(function () {
$('.nav>.sfHover').removeClass('sfHover');
$('.nav>.sfActive').addClass('sfHover');
}, 2000);
},
-
- onItemClick: function (item)
- {
+
+ onItemClick: function (item) {
$('ul.nav').trigger('piwikSwitchPage', item);
broadcast.propagateAjax($(item).attr('name'));
return false;
},
-
- init: function()
- {
+
+ init: function () {
this.menuNode = $('.nav');
-
+
//sub LI auto height
- $('.nav li li a').each(function(){$(this).css({width:$(this).width()+30, paddingLeft:0, paddingRight:0});});
-
+ $('.nav li li a').each(function () {$(this).css({width: $(this).width() + 30, paddingLeft: 0, paddingRight: 0});});
+
this.menuNode.find("li:has(ul)").hover(this.overMainLI, this.outMainLI);
-
+
// add id to all li menu to support menu identification.
// for all sub menu we want to have a unique id based on their module and action
// for main menu we want to add just the module as its id.
- this.menuNode.find('li').each(function(){
+ this.menuNode.find('li').each(function () {
var url = $(this).find('a').attr('name');
- var module = broadcast.getValueFromUrl("module",url);
- var action = broadcast.getValueFromUrl("action",url);
- var moduleId = broadcast.getValueFromUrl("idGoal",url) || broadcast.getValueFromUrl("idDashboard",url);
+ var module = broadcast.getValueFromUrl("module", url);
+ var action = broadcast.getValueFromUrl("action", url);
+ var moduleId = broadcast.getValueFromUrl("idGoal", url) || broadcast.getValueFromUrl("idDashboard", url);
var main_menu = $(this).parent().hasClass('nav') ? true : false;
- if(main_menu)
- {
+ if (main_menu) {
$(this).attr({id: module});
}
// if there's a idGoal or idDashboard, use this in the ID
- else if(moduleId != '')
- {
+ else if (moduleId != '') {
$(this).attr({id: module + '_' + action + '_' + moduleId});
}
- else
- {
+ else {
$(this).attr({id: module + '_' + action});
}
});
},
- activateMenu : function(module,action,id)
- {
+ activateMenu: function (module, action, id) {
this.menuNode.find('li').removeClass('sfHover').removeClass('sfActive');
var $li = this.getSubmenuID(module, id, action);
var mainLi = $("#" + module);
- if(!mainLi.length) {
+ if (!mainLi.length) {
mainLi = $li.parents('li');
}
-
+
mainLi.addClass('sfActive').addClass('sfHover');
$li.addClass('sfHover');
@@ -101,9 +92,8 @@ menu.prototype =
return $li;
},
- loadFirstSection: function()
- {
- if(broadcast.isHashExists() == false) {
+ loadFirstSection: function () {
+ if (broadcast.isHashExists() == false) {
$('li:first a:first', this.menuNode).click().addClass('sfHover').addClass('sfActive');
}
}
diff --git a/plugins/CoreHome/templates/menu.tpl b/plugins/CoreHome/templates/menu.tpl
index e1ae70c720..e9e2fcd853 100644
--- a/plugins/CoreHome/templates/menu.tpl
+++ b/plugins/CoreHome/templates/menu.tpl
@@ -1,14 +1,16 @@
<ul class="nav">
-{foreach from=$menu key=level1 item=level2 name=menu}
-<li>
- <a name='{$level2._url|@urlRewriteWithParameters}' href='#{$level2._url|@urlRewriteWithParameters|substr:1}' onclick='return piwikMenu.onItemClick(this);'>{$level1|translate}</a>
- <ul>
- {foreach from=$level2 key=name item=urlParameters name=level2}
- {if strpos($name, '_') !== 0}
- <li><a name='{$urlParameters._url|@urlRewriteWithParameters}' href='#{$urlParameters._url|@urlRewriteWithParameters|substr:1}' onclick='return piwikMenu.onItemClick(this);'>{$name|translate|escape:'html'}</a></li>
- {/if}
- {/foreach}
- </ul>
-</li>
-{/foreach}
+ {foreach from=$menu key=level1 item=level2 name=menu}
+ <li>
+ <a name='{$level2._url|@urlRewriteWithParameters}' href='#{$level2._url|@urlRewriteWithParameters|substr:1}'
+ onclick='return piwikMenu.onItemClick(this);'>{$level1|translate}</a>
+ <ul>
+ {foreach from=$level2 key=name item=urlParameters name=level2}
+ {if strpos($name, '_') !== 0}
+ <li><a name='{$urlParameters._url|@urlRewriteWithParameters}' href='#{$urlParameters._url|@urlRewriteWithParameters|substr:1}'
+ onclick='return piwikMenu.onItemClick(this);'>{$name|translate|escape:'html'}</a></li>
+ {/if}
+ {/foreach}
+ </ul>
+ </li>
+ {/foreach}
</ul>
diff --git a/plugins/CoreHome/templates/menu_init.js b/plugins/CoreHome/templates/menu_init.js
index 91403b562b..4e9c325301 100644
--- a/plugins/CoreHome/templates/menu_init.js
+++ b/plugins/CoreHome/templates/menu_init.js
@@ -1,7 +1,5 @@
-
-
-$(document).ready( function(){
- if($('.nav').size()) {
+$(document).ready(function () {
+ if ($('.nav').size()) {
piwikMenu = new menu();
piwikMenu.init();
piwikMenu.loadFirstSection();
diff --git a/plugins/CoreHome/templates/misc.js b/plugins/CoreHome/templates/misc.js
index f3e59c5652..7c2ede9c2f 100755
--- a/plugins/CoreHome/templates/misc.js
+++ b/plugins/CoreHome/templates/misc.js
@@ -5,172 +5,157 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function($) {
-
-$(document).ready(function() {
-
- //
- // 'check for updates' behavior
- //
-
- var headerMessageParent = $('#header_message').parent();
-
- // when 'check for updates...' link is clicked, force a check & display the result
- headerMessageParent.on('click', '#updateCheckLinkContainer', function(e) {
- e.preventDefault();
-
- var headerMessage = $(this).closest('#header_message');
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.setLoadingElement('#header_message .loadingPiwik');
- ajaxRequest.addParams({
- module: 'CoreHome',
- action: 'checkForUpdates',
- token_auth: piwik.token_auth
- }, 'get');
- ajaxRequest.setCallback(function(response) {
- headerMessage.fadeOut('slow', function() {
- response = $(response);
-
- var newVersionAvailable = response.hasClass('header_alert');
- if (newVersionAvailable)
- {
- headerMessage.replaceWith(response);
- }
- else
- {
- headerMessage.html(_pk_translate('CoreHome_YouAreUsingTheLatestVersion_js')).show();
- setTimeout(function() {
- headerMessage.fadeOut('slow', function() {
- headerMessage.replaceWith(response);
- });
- }, 4000);
- }
- });
- });
- ajaxRequest.setFormat('html');
- ajaxRequest.send(false);
-
- return false;
- });
-
- // when clicking the header message, show the long message w/o needing to hover
- headerMessageParent.on('click', '#header_message', function(e) {
- if (e.target.tagName.toLowerCase() != 'a')
- {
- $(this).toggleClass('active');
- }
- });
-
- //
- // section toggler behavior
- //
-
- var handleSectionToggle = function (self, showType, doHide)
- {
- var sectionId = $(self).attr('data-section-id'),
- section = $('#' + sectionId),
- showText = _pk_translate('General_Show_js'),
- hideText = _pk_translate('General_Hide_js');
-
- if (typeof(doHide) == 'undefined')
- {
- doHide = section.is(':visible');
- }
-
- if (doHide)
- {
- var newText = $(self).text().replace(hideText, showText),
- afterHide = function() { $(self).text(newText); };
-
- if (showType == 'slide')
- {
- section.slideUp(afterHide);
- }
- else if (showType == 'inline')
- {
- section.hide();
- afterHide();
- }
- else
- {
- section.hide(afterHide);
- }
- }
- else
- {
- var newText = $(self).text().replace(showText, hideText);
- $(self).text(newText);
-
- if (showType == 'slide')
- {
- section.slideDown();
- }
- else if (showType == 'inline')
- {
- section.css('display', 'inline-block');
- }
- else
- {
- section.show();
- }
- }
- };
-
- // when click section toggler link, toggle the visibility of the associated section
- $('body').on('click', 'a.section-toggler-link', function (e) {
- e.preventDefault();
- handleSectionToggle(this, 'slide');
- return false;
- });
-
- $('body').on('change', 'input.section-toggler-link', function (e) {
- handleSectionToggle(this, 'inline', !$(this).is(':checked'));
- });
-
- //
- // reports by dimension list behavior
- //
-
- // when a report dimension is clicked, load the appropriate report
- var currentWidgetLoading = null;
- $('body').on('click', '.reportDimension', function (e) {
- var view = $(this).closest('.reportsByDimensionView'),
- report = $('.dimensionReport', view),
- loading = $('.loadingPiwik', view);
-
- // make this dimension the active one
- $('.activeDimension', view).removeClass('activeDimension');
- $(this).addClass('activeDimension');
-
- // hide the visible report & show the loading elem
- report.hide();
- loading.show();
-
- // load the report using the data-url attribute (which holds the URL to the report)
- var widgetParams = broadcast.getValuesFromUrl($(this).attr('data-url'));
- for (var key in widgetParams)
- {
- widgetParams[key] = decodeURIComponent(widgetParams[key]);
- }
-
- var widgetUniqueId = widgetParams.module + widgetParams.action;
- currentWidgetLoading = widgetUniqueId;
-
- widgetsHelper.loadWidgetAjax(widgetUniqueId, widgetParams, function(response) {
- // if the widget that was loaded was not for the latest clicked link, do nothing w/ the response
- if (widgetUniqueId != currentWidgetLoading)
- {
- return;
- }
-
- loading.hide();
- report.html($(response)).css('display', 'inline-block');
-
- // scroll to report
- piwikHelper.lazyScrollTo(report, 400);
- });
- });
-});
+(function ($) {
+
+ $(document).ready(function () {
+
+ //
+ // 'check for updates' behavior
+ //
+
+ var headerMessageParent = $('#header_message').parent();
+
+ // when 'check for updates...' link is clicked, force a check & display the result
+ headerMessageParent.on('click', '#updateCheckLinkContainer', function (e) {
+ e.preventDefault();
+
+ var headerMessage = $(this).closest('#header_message');
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.setLoadingElement('#header_message .loadingPiwik');
+ ajaxRequest.addParams({
+ module: 'CoreHome',
+ action: 'checkForUpdates',
+ token_auth: piwik.token_auth
+ }, 'get');
+ ajaxRequest.setCallback(function (response) {
+ headerMessage.fadeOut('slow', function () {
+ response = $(response);
+
+ var newVersionAvailable = response.hasClass('header_alert');
+ if (newVersionAvailable) {
+ headerMessage.replaceWith(response);
+ }
+ else {
+ headerMessage.html(_pk_translate('CoreHome_YouAreUsingTheLatestVersion_js')).show();
+ setTimeout(function () {
+ headerMessage.fadeOut('slow', function () {
+ headerMessage.replaceWith(response);
+ });
+ }, 4000);
+ }
+ });
+ });
+ ajaxRequest.setFormat('html');
+ ajaxRequest.send(false);
+
+ return false;
+ });
+
+ // when clicking the header message, show the long message w/o needing to hover
+ headerMessageParent.on('click', '#header_message', function (e) {
+ if (e.target.tagName.toLowerCase() != 'a') {
+ $(this).toggleClass('active');
+ }
+ });
+
+ //
+ // section toggler behavior
+ //
+
+ var handleSectionToggle = function (self, showType, doHide) {
+ var sectionId = $(self).attr('data-section-id'),
+ section = $('#' + sectionId),
+ showText = _pk_translate('General_Show_js'),
+ hideText = _pk_translate('General_Hide_js');
+
+ if (typeof(doHide) == 'undefined') {
+ doHide = section.is(':visible');
+ }
+
+ if (doHide) {
+ var newText = $(self).text().replace(hideText, showText),
+ afterHide = function () { $(self).text(newText); };
+
+ if (showType == 'slide') {
+ section.slideUp(afterHide);
+ }
+ else if (showType == 'inline') {
+ section.hide();
+ afterHide();
+ }
+ else {
+ section.hide(afterHide);
+ }
+ }
+ else {
+ var newText = $(self).text().replace(showText, hideText);
+ $(self).text(newText);
+
+ if (showType == 'slide') {
+ section.slideDown();
+ }
+ else if (showType == 'inline') {
+ section.css('display', 'inline-block');
+ }
+ else {
+ section.show();
+ }
+ }
+ };
+
+ // when click section toggler link, toggle the visibility of the associated section
+ $('body').on('click', 'a.section-toggler-link', function (e) {
+ e.preventDefault();
+ handleSectionToggle(this, 'slide');
+ return false;
+ });
+
+ $('body').on('change', 'input.section-toggler-link', function (e) {
+ handleSectionToggle(this, 'inline', !$(this).is(':checked'));
+ });
+
+ //
+ // reports by dimension list behavior
+ //
+
+ // when a report dimension is clicked, load the appropriate report
+ var currentWidgetLoading = null;
+ $('body').on('click', '.reportDimension', function (e) {
+ var view = $(this).closest('.reportsByDimensionView'),
+ report = $('.dimensionReport', view),
+ loading = $('.loadingPiwik', view);
+
+ // make this dimension the active one
+ $('.activeDimension', view).removeClass('activeDimension');
+ $(this).addClass('activeDimension');
+
+ // hide the visible report & show the loading elem
+ report.hide();
+ loading.show();
+
+ // load the report using the data-url attribute (which holds the URL to the report)
+ var widgetParams = broadcast.getValuesFromUrl($(this).attr('data-url'));
+ for (var key in widgetParams) {
+ widgetParams[key] = decodeURIComponent(widgetParams[key]);
+ }
+
+ var widgetUniqueId = widgetParams.module + widgetParams.action;
+ currentWidgetLoading = widgetUniqueId;
+
+ widgetsHelper.loadWidgetAjax(widgetUniqueId, widgetParams, function (response) {
+ // if the widget that was loaded was not for the latest clicked link, do nothing w/ the response
+ if (widgetUniqueId != currentWidgetLoading) {
+ return;
+ }
+
+ loading.hide();
+ report.html($(response)).css('display', 'inline-block');
+
+ // scroll to report
+ piwikHelper.lazyScrollTo(report, 400);
+ });
+ });
+ });
}(jQuery));
diff --git a/plugins/CoreHome/templates/period_select.tpl b/plugins/CoreHome/templates/period_select.tpl
index e083f136a8..726b277e4d 100644
--- a/plugins/CoreHome/templates/period_select.tpl
+++ b/plugins/CoreHome/templates/period_select.tpl
@@ -1,33 +1,37 @@
{loadJavascriptTranslations plugins='CoreHome'}
<div id="periodString">
- <div id="date">{'General_DateRange'|translate} <b>{$prettyDate}</b> <img src='themes/default/images/icon-calendar.gif' alt="" /></div>
- <div id="periodMore">
- <div class="period-date">
- <h6>{'General_Date'|translate}</h6>
- <div id="datepicker"></div>
- </div>
- <div class="period-range" style="display:none;">
- <div id="calendarRangeFrom">
- <h6>{'General_DateRangeFrom_js'|translate}<input tabindex="1" type="text" id="inputCalendarFrom" name="inputCalendarFrom"/></h6>
- <div id="calendarFrom"></div>
- </div>
- <div id="calendarRangeTo">
- <h6>{'General_DateRangeTo_js'|translate}<input tabindex="2" type="text" id="inputCalendarTo" name="inputCalendarTo"/></h6>
- <div id="calendarTo"></div>
- </div>
- </div>
- <div class="period-type">
- <h6>{'General_Period'|translate}</h6>
+ <div id="date">{'General_DateRange'|translate} <b>{$prettyDate}</b> <img src='themes/default/images/icon-calendar.gif' alt=""/></div>
+ <div id="periodMore">
+ <div class="period-date">
+ <h6>{'General_Date'|translate}</h6>
+
+ <div id="datepicker"></div>
+ </div>
+ <div class="period-range" style="display:none;">
+ <div id="calendarRangeFrom">
+ <h6>{'General_DateRangeFrom_js'|translate}<input tabindex="1" type="text" id="inputCalendarFrom" name="inputCalendarFrom"/></h6>
+
+ <div id="calendarFrom"></div>
+ </div>
+ <div id="calendarRangeTo">
+ <h6>{'General_DateRangeTo_js'|translate}<input tabindex="2" type="text" id="inputCalendarTo" name="inputCalendarTo"/></h6>
+
+ <div id="calendarTo"></div>
+ </div>
+ </div>
+ <div class="period-type">
+ <h6>{'General_Period'|translate}</h6>
<span id="otherPeriods">
{foreach from=$periodsNames key=label item=thisPeriod}
- <input type="radio" name="period" id="period_id_{$label}" value="{url period=$label}"{if $label==$period} checked="checked"{/if} />
- <label for="period_id_{$label}" >{$thisPeriod.singular}</label><br />
- {/foreach}
+ <input type="radio" name="period" id="period_id_{$label}" value="{url period=$label}"{if $label==$period} checked="checked"{/if} />
+ <label for="period_id_{$label}">{$thisPeriod.singular}</label>
+ <br/>
+ {/foreach}
</span>
- <input tabindex="3" type="submit" value="{'General_ApplyDateRange'|translate}" id="calendarRangeApply" />
- {ajaxLoadingDiv id=ajaxLoadingCalendar}
- </div>
- </div>
- <div class="period-click-tooltip" style="display:none;">{'General_ClickToChangePeriod'|translate}</div>
+ <input tabindex="3" type="submit" value="{'General_ApplyDateRange'|translate}" id="calendarRangeApply"/>
+ {ajaxLoadingDiv id=ajaxLoadingCalendar}
+ </div>
+ </div>
+ <div class="period-click-tooltip" style="display:none;">{'General_ClickToChangePeriod'|translate}</div>
</div>
diff --git a/plugins/CoreHome/templates/piwik_tag.tpl b/plugins/CoreHome/templates/piwik_tag.tpl
index e97ca2da76..cacc97d281 100644
--- a/plugins/CoreHome/templates/piwik_tag.tpl
+++ b/plugins/CoreHome/templates/piwik_tag.tpl
@@ -1,36 +1,35 @@
{* Disabled by default, tracks activity of this Piwik instance *}
{if $piwikUrl == 'http://demo.piwik.org/' || $debugTrackVisitsInsidePiwikUI}
+ <div class="clear"></div>
+ {literal}
+ <!-- Piwik -->
+ <script type="text/javascript">
+ var _paq = _paq || [];
+ _paq.push(['setTrackerUrl', 'piwik.php']);
+ _paq.push(['setSiteId', 1]);
+ {/literal}
+_paq.push(['setCookieDomain', '*.piwik.org']);
+ {literal}
+ // set the domain the visitor landed on, in the Custom Variable
+ _paq.push([function () {
+ if (!this.getCustomVariable(1))
+ {
+ this.setCustomVariable(1, "Domain landed", document.domain);
+ }
+ }]);
+ // Set the selected Piwik language in a custom var
+ _paq.push(['setCustomVariable', 2, "Demo language", piwik.languageName]);
+ _paq.push(['setDocumentTitle', document.domain + "/" + document.title]);
+ _paq.push(['trackPageView']);
+ _paq.push(['enableLinkTracking']);
-<div class="clear"></div>
-{literal}
-<!-- Piwik -->
-<script type="text/javascript">
- var _paq = _paq || [];
- _paq.push(['setTrackerUrl', 'piwik.php']);
- _paq.push(['setSiteId', 1]);
-{/literal}{if $piwikUrl == 'http://demo.piwik.org/'}{literal}
- _paq.push(['setCookieDomain', '*.piwik.org']);
-{/literal}{/if}{literal}
- // set the domain the visitor landed on, in the Custom Variable
- _paq.push([function () {
- if (!this.getCustomVariable(1))
- {
- this.setCustomVariable(1, "Domain landed", document.domain);
- }
- }]);
- // Set the selected Piwik language in a custom var
- _paq.push(['setCustomVariable', 2, "Demo language", piwik.languageName]);
- _paq.push(['setDocumentTitle', document.domain + "/" + document.title]);
- _paq.push(['trackPageView']);
- _paq.push(['enableLinkTracking']);
-
- (function() {
- var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript';
- g.defer=true; g.async=true; g.src='js/piwik.js'; s.parentNode.insertBefore(g,s);
- })();
-</script>
-<!-- End Piwik Code -->
-{/literal}
+ (function() {
+ var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript';
+ g.defer=true; g.async=true; g.src='js/piwik.js'; s.parentNode.insertBefore(g,s);
+ })();
+ </script>
+ <!-- End Piwik Code -->
+ {/literal}
{/if}
diff --git a/plugins/CoreHome/templates/popover.js b/plugins/CoreHome/templates/popover.js
index 4704233a63..d91059ec21 100644
--- a/plugins/CoreHome/templates/popover.js
+++ b/plugins/CoreHome/templates/popover.js
@@ -5,206 +5,207 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-var Piwik_Popover = (function() {
-
- var container = false;
- var isOpen = false;
- var closeCallback = false;
-
- var createContainer = function() {
- if (container === false) {
- container = $(document.createElement('div')).attr('id', 'Piwik_Popover');
- }
- };
-
- var openPopover = function(title) {
- createContainer();
-
- container.dialog({
- title: title,
- modal: true,
- width: '950px',
- position: ['center', 'center'],
- resizable: false,
- autoOpen: true,
- open: function(event, ui) {
- $('.ui-widget-overlay').on('click.popover', function() {
- container.dialog('close');
- });
- },
- close: function(event, ui) {
- container.find('div.jqplot-target').trigger('piwikDestroyPlot');
- container[0].innerHTML = ''; // IE8 fix
- container.dialog('destroy').remove();
- globalAjaxQueue.abort();
- $('.ui-widget-overlay').off('click.popover');
- isOpen = false;
- broadcast.propagateNewPopoverParameter(false);
- if (typeof closeCallback == 'function') {
- closeCallback();
- closeCallback = false;
- }
- }
- });
-
- isOpen = true;
- };
-
- var centerPopover = function() {
- if (container !== false) {
- container.dialog({position: ['center', 'center']});
- }
- };
-
- return {
-
- /**
- * Open the popover with a loading message
- *
- * @param popoverName string name of the popover
- * @param popoverSubject string subject of the popover (e.g. url, optional)
- * @param height int height of the popover in px (optional)
- */
- showLoading: function(popoverName, popoverSubject, height) {
- var loading = $(document.createElement('div')).addClass('Piwik_Popover_Loading');
-
- var loadingMessage = popoverSubject ? translations.General_LoadingPopoverFor_js :
- translations.General_LoadingPopover_js;
-
- loadingMessage = loadingMessage.replace(/%s/, popoverName);
-
- var p1 = $(document.createElement('p')).addClass('Piwik_Popover_Loading_Name');
- loading.append(p1.text(loadingMessage));
-
- var p2;
- if (popoverSubject) {
- popoverSubject = piwikHelper.addBreakpointsToUrl(popoverSubject);
- p1.addClass('Piwik_Popover_Loading_NameWithSubject');
- p2 = $(document.createElement('p')).addClass('Piwik_Popover_Loading_Subject');
- loading.append(p2.html(popoverSubject));
- }
-
- if (height) {
- loading.height(height);
- }
-
- if (!isOpen) {
- openPopover();
- }
-
- this.setContent(loading);
- this.setTitle('');
-
- if (height) {
- var offset = loading.height() - p1.outerHeight();
- if (popoverSubject) {
- offset -= p2.outerHeight();
- }
- var spacingEl = $(document.createElement('div'));
- spacingEl.height(Math.round(offset / 2));
- loading.prepend(spacingEl);
- }
-
- return container;
- },
-
- /** Add a help button to the current popover */
- addHelpButton: function(helpUrl) {
- if (!isOpen) {
- return;
- }
-
- var titlebar = container.parent().find('.ui-dialog-titlebar');
-
- var button = $(document.createElement('a')).addClass('ui-dialog-titlebar-help');
- button.attr({href: helpUrl, target: '_blank'});
-
- titlebar.append(button);
- },
-
- /** Set the title of the popover */
- setTitle: function(titleHtml) {
- container.dialog({title: titleHtml});
- },
-
- /** Set inner HTML of the popover */
- setContent: function(html) {
- if (typeof closeCallback == 'function') {
- closeCallback();
- closeCallback = false;
- }
-
- container[0].innerHTML = ''; // IE8 fix
- container.html(html);
- centerPopover();
- },
-
- /** Show an error message. All params are HTML! */
- showError: function(title, message, backLabel) {
- var error = $(document.createElement('div')).addClass('Piwik_Popover_Error');
-
- var p = $(document.createElement('p')).addClass('Piwik_Popover_Error_Title');
- error.append(p.html(title));
-
- if (message) {
- p = $(document.createElement('p')).addClass('Piwik_Popover_Error_Message');
- error.append(p.html(message));
- }
-
- if (backLabel) {
- var back = $(document.createElement('a')).addClass('Piwik_Popover_Error_Back');
- back.attr('href', '#').click(function() {
- history.back();
- return false;
- });
- error.append(back.html(backLabel));
- }
-
- if (!isOpen) {
- openPopover();
- }
-
- this.setContent(error);
- },
-
- /** Add a callback for the next time the popover is closed or the content changes */
- onClose: function(callback) {
- closeCallback = callback;
- },
-
- /** Close the popover */
- close: function() {
- if (isOpen) {
- container.dialog('close');
- }
- },
-
- /**
- * Create a Popover and load the specified URL in it
- * @param url
- */
- createPopupAndLoadUrl: function(url, loadingName) {
- // open the popover
- var box = Piwik_Popover.showLoading(loadingName);
-
- var callback = function(html) {
- function setPopoverTitleIfOneFoundInContainer() {
- var title = $('h1,h2', container);
- if (title.length == 1) {
- Piwik_Popover.setTitle(title.text());
- $(title).hide();
- }
+var Piwik_Popover = (function () {
+
+ var container = false;
+ var isOpen = false;
+ var closeCallback = false;
+
+ var createContainer = function () {
+ if (container === false) {
+ container = $(document.createElement('div')).attr('id', 'Piwik_Popover');
+ }
+ };
+
+ var openPopover = function (title) {
+ createContainer();
+
+ container.dialog({
+ title: title,
+ modal: true,
+ width: '950px',
+ position: ['center', 'center'],
+ resizable: false,
+ autoOpen: true,
+ open: function (event, ui) {
+ $('.ui-widget-overlay').on('click.popover', function () {
+ container.dialog('close');
+ });
+ },
+ close: function (event, ui) {
+ container.find('div.jqplot-target').trigger('piwikDestroyPlot');
+ container[0].innerHTML = ''; // IE8 fix
+ container.dialog('destroy').remove();
+ globalAjaxQueue.abort();
+ $('.ui-widget-overlay').off('click.popover');
+ isOpen = false;
+ broadcast.propagateNewPopoverParameter(false);
+ if (typeof closeCallback == 'function') {
+ closeCallback();
+ closeCallback = false;
+ }
+ }
+ });
+
+ isOpen = true;
+ };
+
+ var centerPopover = function () {
+ if (container !== false) {
+ container.dialog({position: ['center', 'center']});
+ }
+ };
+
+ return {
+
+ /**
+ * Open the popover with a loading message
+ *
+ * @param popoverName string name of the popover
+ * @param popoverSubject string subject of the popover (e.g. url, optional)
+ * @param height int height of the popover in px (optional)
+ */
+ showLoading: function (popoverName, popoverSubject, height) {
+ var loading = $(document.createElement('div')).addClass('Piwik_Popover_Loading');
+
+ var loadingMessage = popoverSubject ? translations.General_LoadingPopoverFor_js :
+ translations.General_LoadingPopover_js;
+
+ loadingMessage = loadingMessage.replace(/%s/, popoverName);
+
+ var p1 = $(document.createElement('p')).addClass('Piwik_Popover_Loading_Name');
+ loading.append(p1.text(loadingMessage));
+
+ var p2;
+ if (popoverSubject) {
+ popoverSubject = piwikHelper.addBreakpointsToUrl(popoverSubject);
+ p1.addClass('Piwik_Popover_Loading_NameWithSubject');
+ p2 = $(document.createElement('p')).addClass('Piwik_Popover_Loading_Subject');
+ loading.append(p2.html(popoverSubject));
+ }
+
+ if (height) {
+ loading.height(height);
+ }
+
+ if (!isOpen) {
+ openPopover();
+ }
+
+ this.setContent(loading);
+ this.setTitle('');
+
+ if (height) {
+ var offset = loading.height() - p1.outerHeight();
+ if (popoverSubject) {
+ offset -= p2.outerHeight();
+ }
+ var spacingEl = $(document.createElement('div'));
+ spacingEl.height(Math.round(offset / 2));
+ loading.prepend(spacingEl);
+ }
+
+ return container;
+ },
+
+ /** Add a help button to the current popover */
+ addHelpButton: function (helpUrl) {
+ if (!isOpen) {
+ return;
+ }
+
+ var titlebar = container.parent().find('.ui-dialog-titlebar');
+
+ var button = $(document.createElement('a')).addClass('ui-dialog-titlebar-help');
+ button.attr({href: helpUrl, target: '_blank'});
+
+ titlebar.append(button);
+ },
+
+ /** Set the title of the popover */
+ setTitle: function (titleHtml) {
+ container.dialog({title: titleHtml});
+ },
+
+ /** Set inner HTML of the popover */
+ setContent: function (html) {
+ if (typeof closeCallback == 'function') {
+ closeCallback();
+ closeCallback = false;
+ }
+
+ container[0].innerHTML = ''; // IE8 fix
+ container.html(html);
+ centerPopover();
+ },
+
+ /** Show an error message. All params are HTML! */
+ showError: function (title, message, backLabel) {
+ var error = $(document.createElement('div')).addClass('Piwik_Popover_Error');
+
+ var p = $(document.createElement('p')).addClass('Piwik_Popover_Error_Title');
+ error.append(p.html(title));
+
+ if (message) {
+ p = $(document.createElement('p')).addClass('Piwik_Popover_Error_Message');
+ error.append(p.html(message));
+ }
+
+ if (backLabel) {
+ var back = $(document.createElement('a')).addClass('Piwik_Popover_Error_Back');
+ back.attr('href', '#').click(function () {
+ history.back();
+ return false;
+ });
+ error.append(back.html(backLabel));
+ }
+
+ if (!isOpen) {
+ openPopover();
+ }
+
+ this.setContent(error);
+ },
+
+ /** Add a callback for the next time the popover is closed or the content changes */
+ onClose: function (callback) {
+ closeCallback = callback;
+ },
+
+ /** Close the popover */
+ close: function () {
+ if (isOpen) {
+ container.dialog('close');
+ }
+ },
+
+ /**
+ * Create a Popover and load the specified URL in it
+ * @param url
+ */
+ createPopupAndLoadUrl: function (url, loadingName) {
+ // open the popover
+ var box = Piwik_Popover.showLoading(loadingName);
+
+ var callback = function (html) {
+ function setPopoverTitleIfOneFoundInContainer() {
+ var title = $('h1,h2', container);
+ if (title.length == 1) {
+ Piwik_Popover.setTitle(title.text());
+ $(title).hide();
+ }
+ }
+
+ Piwik_Popover.setContent(html);
+ setPopoverTitleIfOneFoundInContainer();
+ }
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams(piwikHelper.getArrayFromQueryString(url), 'get');
+ ajaxRequest.setCallback(callback);
+ ajaxRequest.setFormat('html');
+ ajaxRequest.send(false);
}
- Piwik_Popover.setContent(html);
- setPopoverTitleIfOneFoundInContainer();
- }
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams(piwikHelper.getArrayFromQueryString(url), 'get');
- ajaxRequest.setCallback(callback);
- ajaxRequest.setFormat('html');
- ajaxRequest.send(false);
- }
- };
+ };
})();
diff --git a/plugins/CoreHome/templates/popover_multirowevolution.tpl b/plugins/CoreHome/templates/popover_multirowevolution.tpl
index c060f96d8a..50115c4bf7 100644
--- a/plugins/CoreHome/templates/popover_multirowevolution.tpl
+++ b/plugins/CoreHome/templates/popover_multirowevolution.tpl
@@ -1,35 +1,35 @@
<div class="rowevolution multirowevolution">
- <div class="popover-title">{'RowEvolution_MultiRowEvolutionTitle'|translate|escape:'html'}</div>
- <div class="graph">
- {$graph}
- </div>
- <div class="metrics-container">
- <h2>{$availableRecordsText|translate}</h2>
- <table class="metrics" border="0" cellpadding="0" cellspacing="0">
- {foreach from=$metrics item=metric}
- <tr>
- <td class="sparkline">
- {$metric.sparkline}
- </td>
- <td class="text">
- {logoHtml metadata=$metric alt=""} <span style="color:{$metric.color}">{$metric.label|escape:'html'}</span><br />
- <span class="details">{$metric.details}</span>
- </td>
- </tr>
- {/foreach}
- </table>
- <a href="#" class="rowevolution-startmulti">&raquo; {'RowEvolution_PickAnotherRow'|translate}</a>
- </div>
- {if count($availableMetrics) > 1}
- <div class="metric-selectbox">
- <h2>{'RowEvolution_AvailableMetrics'|translate}</h2>
- <select name="metric" class="multirowevoltion-metric">
- {foreach from=$availableMetrics item=metricName key=metric}
- <option value="{$metric|escape:'html'}"{if $selectedMetric == $metric} selected="selected"{/if}>
- {$metricName|escape:'html'}
- </option>
- {/foreach}
- </select>
- </div>
- {/if}
+ <div class="popover-title">{'RowEvolution_MultiRowEvolutionTitle'|translate|escape:'html'}</div>
+ <div class="graph">
+ {$graph}
+ </div>
+ <div class="metrics-container">
+ <h2>{$availableRecordsText|translate}</h2>
+ <table class="metrics" border="0" cellpadding="0" cellspacing="0">
+ {foreach from=$metrics item=metric}
+ <tr>
+ <td class="sparkline">
+ {$metric.sparkline}
+ </td>
+ <td class="text">
+ {logoHtml metadata=$metric alt=""} <span style="color:{$metric.color}">{$metric.label|escape:'html'}</span><br/>
+ <span class="details">{$metric.details}</span>
+ </td>
+ </tr>
+ {/foreach}
+ </table>
+ <a href="#" class="rowevolution-startmulti">&raquo; {'RowEvolution_PickAnotherRow'|translate}</a>
+ </div>
+ {if count($availableMetrics) > 1}
+ <div class="metric-selectbox">
+ <h2>{'RowEvolution_AvailableMetrics'|translate}</h2>
+ <select name="metric" class="multirowevoltion-metric">
+ {foreach from=$availableMetrics item=metricName key=metric}
+ <option value="{$metric|escape:'html'}"{if $selectedMetric == $metric} selected="selected"{/if}>
+ {$metricName|escape:'html'}
+ </option>
+ {/foreach}
+ </select>
+ </div>
+ {/if}
</div> \ No newline at end of file
diff --git a/plugins/CoreHome/templates/popover_rowevolution.tpl b/plugins/CoreHome/templates/popover_rowevolution.tpl
index c3f6599ca4..a62af4b032 100644
--- a/plugins/CoreHome/templates/popover_rowevolution.tpl
+++ b/plugins/CoreHome/templates/popover_rowevolution.tpl
@@ -1,32 +1,34 @@
<div class="rowevolution">
- <div class="popover-title">{$popoverTitle}</div>
- <div class="graph">
- {$graph}
- </div>
- <div class="metrics-container">
- <h2>{$availableMetricsText}</h2>
- <div class="rowevolution-documentation">
- {'RowEvolution_Documentation'|translate}
- </div>
- <table class="metrics" border="0" cellpadding="0" cellspacing="0">
- {foreach from=$metrics item=metric}
- <tr>
- <td class="sparkline">
- {$metric.sparkline}
- </td>
- <td class="text">
- <span style="color:{$metric.color}">{$metric.label|escape:'html'}</span>{if $metric.details}:
- <span class="details">{$metric.details}</span>{/if}
- </td>
- </tr>
- {/foreach}
- </table>
- </div>
- <div class="compare-container">
- <h2>{'RowEvolution_CompareRows'|translate}</h2>
- <div class="rowevolution-documentation">
- {'RowEvolution_CompareDocumentation'|translate}
- </div>
- <a href="#" class="rowevolution-startmulti">&raquo; {'RowEvolution_PickARow'|translate}</a>
- </div>
+ <div class="popover-title">{$popoverTitle}</div>
+ <div class="graph">
+ {$graph}
+ </div>
+ <div class="metrics-container">
+ <h2>{$availableMetricsText}</h2>
+
+ <div class="rowevolution-documentation">
+ {'RowEvolution_Documentation'|translate}
+ </div>
+ <table class="metrics" border="0" cellpadding="0" cellspacing="0">
+ {foreach from=$metrics item=metric}
+ <tr>
+ <td class="sparkline">
+ {$metric.sparkline}
+ </td>
+ <td class="text">
+ <span style="color:{$metric.color}">{$metric.label|escape:'html'}</span>{if $metric.details}:
+ <span class="details">{$metric.details}</span>{/if}
+ </td>
+ </tr>
+ {/foreach}
+ </table>
+ </div>
+ <div class="compare-container">
+ <h2>{'RowEvolution_CompareRows'|translate}</h2>
+
+ <div class="rowevolution-documentation">
+ {'RowEvolution_CompareDocumentation'|translate}
+ </div>
+ <a href="#" class="rowevolution-startmulti">&raquo; {'RowEvolution_PickARow'|translate}</a>
+ </div>
</div> \ No newline at end of file
diff --git a/plugins/CoreHome/templates/promo_video.tpl b/plugins/CoreHome/templates/promo_video.tpl
index 58494b8299..5b7cb00df4 100755
--- a/plugins/CoreHome/templates/promo_video.tpl
+++ b/plugins/CoreHome/templates/promo_video.tpl
@@ -1,126 +1,130 @@
{literal}
-<script type="text/javascript">
- $(document).ready(function() {
- $('#piwik-promo-thumbnail').click(function() {
- var promoEmbed = $('#piwik-promo-embed'),
- widgetWidth = $(this).closest('.widgetContent').width(),
- height = (266 * widgetWidth) / 421,
- embedHtml = '<iframe width="100%" height="' + height + '" src="http://www.youtube.com/embed/OslfF_EH81g?autoplay=1&vq=hd720&wmode=transparent" frameborder="0" wmode="Opaque"></iframe>';
-
- $(this).hide();
- promoEmbed.height(height).html(embedHtml);
- promoEmbed.show();
- });
- });
-</script>
-<style type="text/css">
-#piwik-promo-thumbnail {
- background: #fff url(plugins/CoreHome/templates/images/promo_splash.png) no-repeat 0 0;
- background-position: center;
- width:321px;
- margin:0 auto 0 auto;
-}
-
-#piwik-promo-embed {
- margin-left:1px;
-}
-
-#piwik-promo-embed>iframe {
- z-index:0;
-}
-
-#piwik-promo-thumbnail {
- height:178px;
-}
-
-#piwik-promo-thumbnail:hover {
- opacity:.75;
- cursor:pointer;
-}
-
-#piwik-promo-thumbnail>img {
- display:block;
- position:relative;
- top:53px;
- left:125px;
-}
-
-#piwik-promo-video {
- margin: 2em 0 2em 0;
-}
-
-#piwik-widget-footer {
- margin: 0 1em 1em 1em;
-}
-
-#piwik-promo-share {
- margin: 0 2em 1em 0;
- background-color: #CCC;
-
- border:1px solid #CCC;
- -webkit-border-radius: 6px;
- -moz-border-radius: 6px;
- border-radius: 6px;
-
- display:inline-block;
-
- padding:0 .5em 0 .5em;
-
- float: right;
-}
-
-#piwik-promo-share > a {
- margin-left: .5em;
- margin-top:4px;
- display:inline-block;
-}
-
-#piwik-promo-share>span {
- display:inline-block;
- vertical-align:top;
- margin-top:4px;
-}
-
-#piwik-promo-videos-link {
- font-size:.8em;
- font-style:italic;
- margin: 1em 0 0 1.25em;
- color:#666;
- display:inline-block;
-}
-#piwik-promo-videos-link:hover {
- text-decoration:none;
-}
-</style>
+ <script type="text/javascript">
+ $(document).ready(function () {
+ $('#piwik-promo-thumbnail').click(function () {
+ var promoEmbed = $('#piwik-promo-embed'),
+ widgetWidth = $(this).closest('.widgetContent').width(),
+ height = (266 * widgetWidth) / 421,
+ embedHtml = '<iframe width="100%" height="' + height + '" src="http://www.youtube.com/embed/OslfF_EH81g?autoplay=1&vq=hd720&wmode=transparent" frameborder="0" wmode="Opaque"></iframe>';
+
+ $(this).hide();
+ promoEmbed.height(height).html(embedHtml);
+ promoEmbed.show();
+ });
+ });
+ </script>
+ <style type="text/css">
+ #piwik-promo-thumbnail {
+ background: #fff url(plugins/CoreHome/templates/images/promo_splash.png) no-repeat 0 0;
+ background-position: center;
+ width: 321px;
+ margin: 0 auto 0 auto;
+ }
+
+ #piwik-promo-embed {
+ margin-left: 1px;
+ }
+
+ #piwik-promo-embed>iframe {
+ z-index: 0;
+ }
+
+ #piwik-promo-thumbnail {
+ height: 178px;
+ }
+
+ #piwik-promo-thumbnail:hover {
+ opacity: .75;
+ cursor: pointer;
+ }
+
+ #piwik-promo-thumbnail>img {
+ display: block;
+ position: relative;
+ top: 53px;
+ left: 125px;
+ }
+
+ #piwik-promo-video {
+ margin: 2em 0 2em 0;
+ }
+
+ #piwik-widget-footer {
+ margin: 0 1em 1em 1em;
+ }
+
+ #piwik-promo-share {
+ margin: 0 2em 1em 0;
+ background-color: #CCC;
+
+ border: 1px solid #CCC;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+
+ display: inline-block;
+
+ padding: 0 .5em 0 .5em;
+
+ float: right;
+ }
+
+ #piwik-promo-share > a {
+ margin-left: .5em;
+ margin-top: 4px;
+ display: inline-block;
+ }
+
+ #piwik-promo-share>span {
+ display: inline-block;
+ vertical-align: top;
+ margin-top: 4px;
+ }
+
+ #piwik-promo-videos-link {
+ font-size: .8em;
+ font-style: italic;
+ margin: 1em 0 0 1.25em;
+ color: #666;
+ display: inline-block;
+ }
+
+ #piwik-promo-videos-link:hover {
+ text-decoration: none;
+ }
+ </style>
{/literal}
<div id="piwik-promo">
- <div id="piwik-promo-video">
- <div id="piwik-promo-thumbnail">
- <img src="themes/default/images/video_play.png"/>
- </div>
-
- <div id="piwik-promo-embed" style="display:none">
- </div>
- </div>
-
- <a id="piwik-promo-videos-link" href="http://piwik.org/blog/2012/12/piwik-how-to-videos/" target="_blank">
- {'CoreHome_ViewAllPiwikVideoTutorials'|translate}
- </a>
-
- <div id="piwik-promo-share">
- <span>{'CoreHome_ShareThis'|translate}:</span>
-
- {* facebook *}
- <a href="http://www.facebook.com/sharer.php?u={$promoVideoUrl|urlencode|escape:'html'}" target="_blank"><img src="plugins/Referers/images/socials/facebook.com.png"/></a>
-
- {* twitter *}
- <a href="http://twitter.com/share?text={$shareText|urlencode|escape:'html'}&url={$promoVideoUrl|urlencode|escape:'html'}" target="_blank"><img src="plugins/Referers/images/socials/twitter.com.png"/></a>
-
- {* email *}
- <a href="mailto:?body={$shareTextLong|rawurlencode|escape:'html'}&subject={$shareText|rawurlencode|escape:'html'}" target="_blank"><img src="themes/default/images/email.png"/></a>
- </div>
-
- <div style="clear:both"></div>
-
- <div id="piwik-widget-footer" style='color:#666'>{'CoreHome_CloseWidgetDirections'|translate}</div>
+ <div id="piwik-promo-video">
+ <div id="piwik-promo-thumbnail">
+ <img src="themes/default/images/video_play.png"/>
+ </div>
+
+ <div id="piwik-promo-embed" style="display:none">
+ </div>
+ </div>
+
+ <a id="piwik-promo-videos-link" href="http://piwik.org/blog/2012/12/piwik-how-to-videos/" target="_blank">
+ {'CoreHome_ViewAllPiwikVideoTutorials'|translate}
+ </a>
+
+ <div id="piwik-promo-share">
+ <span>{'CoreHome_ShareThis'|translate}:</span>
+
+ {* facebook *}
+ <a href="http://www.facebook.com/sharer.php?u={$promoVideoUrl|urlencode|escape:'html'}" target="_blank"><img
+ src="plugins/Referers/images/socials/facebook.com.png"/></a>
+
+ {* twitter *}
+ <a href="http://twitter.com/share?text={$shareText|urlencode|escape:'html'}&url={$promoVideoUrl|urlencode|escape:'html'}" target="_blank"><img
+ src="plugins/Referers/images/socials/twitter.com.png"/></a>
+
+ {* email *}
+ <a href="mailto:?body={$shareTextLong|rawurlencode|escape:'html'}&subject={$shareText|rawurlencode|escape:'html'}" target="_blank"><img
+ src="themes/default/images/email.png"/></a>
+ </div>
+
+ <div style="clear:both"></div>
+
+ <div id="piwik-widget-footer" style='color:#666'>{'CoreHome_CloseWidgetDirections'|translate}</div>
</div>
diff --git a/plugins/CoreHome/templates/reports_by_dimension.tpl b/plugins/CoreHome/templates/reports_by_dimension.tpl
index fd7689138a..e18f560958 100644
--- a/plugins/CoreHome/templates/reports_by_dimension.tpl
+++ b/plugins/CoreHome/templates/reports_by_dimension.tpl
@@ -1,27 +1,28 @@
<div class="reportsByDimensionView">
-<div class="entityList">
-{foreach from=$dimensionCategories key=category item=dimensions name=dimensionCategories}
- <div class='dimensionCategory'>
- {$category|translate}
- <ul class='listCircle'>
- {foreach from=$dimensions key=idx item=dimension}
- <li class="reportDimension {if $idx eq 0 && $smarty.foreach.dimensionCategories.index eq 0}activeDimension{/if}" data-url="{$dimension.url}">
- <span class='dimension'>{$dimension.title|translate}</span>
- </li>
- {/foreach}
- </ul>
- </div>
-{/foreach}
-</div>
+ <div class="entityList">
+ {foreach from=$dimensionCategories key=category item=dimensions name=dimensionCategories}
+ <div class='dimensionCategory'>
+ {$category|translate}
+ <ul class='listCircle'>
+ {foreach from=$dimensions key=idx item=dimension}
+ <li class="reportDimension {if $idx eq 0 && $smarty.foreach.dimensionCategories.index eq 0}activeDimension{/if}"
+ data-url="{$dimension.url}">
+ <span class='dimension'>{$dimension.title|translate}</span>
+ </li>
+ {/foreach}
+ </ul>
+ </div>
+ {/foreach}
+ </div>
-<div style="float:left;">
- <div class="loadingPiwik" style="display:none">
- <img src="themes/default/images/loading-blue.gif" alt="" />{'General_LoadingData'|translate}
- </div>
-
- <div class="dimensionReport">{$firstReport}</div>
-</div>
-<div class="clear"></div>
+ <div style="float:left;">
+ <div class="loadingPiwik" style="display:none">
+ <img src="themes/default/images/loading-blue.gif" alt=""/>{'General_LoadingData'|translate}
+ </div>
+
+ <div class="dimensionReport">{$firstReport}</div>
+ </div>
+ <div class="clear"></div>
</div>
diff --git a/plugins/CoreHome/templates/sites_selection.tpl b/plugins/CoreHome/templates/sites_selection.tpl
index c716bea2fb..f61df0174a 100644
--- a/plugins/CoreHome/templates/sites_selection.tpl
+++ b/plugins/CoreHome/templates/sites_selection.tpl
@@ -14,42 +14,47 @@
* - $idSite The currently selected idSite. Defaults to the first id in $sites set by Piwik_View.
*}
{capture name=sitesSelector_allWebsitesLink assign=sitesSelector_allWebsitesLink}
-<div class="custom_select_all" style="clear: both">
- <a href="#" {if isset($showAllSitesItem) && $showAllSitesItem eq false}style="display:none;"{/if}>
- {if isset($allSitesItemText)}{$allSitesItemText}{else}{'General_MultiSitesSummary'|translate}{/if}
- </a>
-</div>
+ <div class="custom_select_all" style="clear: both">
+ <a href="#" {if isset($showAllSitesItem) && $showAllSitesItem eq false}style="display:none;"{/if}>
+ {if isset($allSitesItemText)}{$allSitesItemText}{else}{'General_MultiSitesSummary'|translate}{/if}
+ </a>
+ </div>
{/capture}
<div class="sites_autocomplete" {if isset($siteSelectorId)}id="{$siteSelectorId}"{/if}
- {if !isset($switchSiteOnSelect) || $switchSiteOnSelect eq true}data-switch-site-on-select="1"{/if}>
+ {if !isset($switchSiteOnSelect) || $switchSiteOnSelect eq true}data-switch-site-on-select="1"{/if}>
<div class="custom_select">
-
- <a href="#" onclick="return false" class="custom_select_main_link" siteid="{if isset($idSite)}{$idSite}{else}{$sites[0].idsite}{/if}">{if isset($siteName)}{$siteName}{else}{$sites[0].name}{/if}</a>
-
+
+ <a href="#" onclick="return false" class="custom_select_main_link"
+ siteid="{if isset($idSite)}{$idSite}{else}{$sites[0].idsite}{/if}">{if isset($siteName)}{$siteName}{else}{$sites[0].name}{/if}</a>
+
<div class="custom_select_block">
{if isset($allWebsitesLinkLocation) && $allWebsitesLinkLocation eq 'top'}
- {$sitesSelector_allWebsitesLink}
+ {$sitesSelector_allWebsitesLink}
{/if}
<div class="custom_select_container">
- <ul class="custom_select_ul_list" >
- {foreach from=$sites item=info}
- <li {if (!isset($showSelectedSite) || $showSelectedSite eq false) && $idSite==$info.idsite} style="display: none"{/if}><a href="#" siteid="{$info.idsite}">{$info.name}</a></li>
- {/foreach}
- </ul>
+ <ul class="custom_select_ul_list">
+ {foreach from=$sites item=info}
+ <li {if (!isset($showSelectedSite) || $showSelectedSite eq false) && $idSite==$info.idsite} style="display: none"{/if}><a href="#"
+ siteid="{$info.idsite}">{$info.name}</a>
+ </li>
+ {/foreach}
+ </ul>
</div>
{if !isset($allWebsitesLinkLocation) || $allWebsitesLinkLocation eq 'bottom'}
- {$sitesSelector_allWebsitesLink}
+ {$sitesSelector_allWebsitesLink}
{/if}
<div class="custom_select_search" {if !$show_autocompleter}style="display:none;"{/if}>
<input type="text" length="15" class="websiteSearch inp"/>
- <input type="hidden" class="max_sitename_width" value="130" />
+ <input type="hidden" class="max_sitename_width" value="130"/>
<input type="submit" value="Search" class="but"/>
- <img title="Clear" class="reset" style="position: relative; top: 4px; left: -44px; cursor: pointer; display: none;" src="plugins/CoreHome/templates/images/reset_search.png"/>
+ <img title="Clear" class="reset" style="position: relative; top: 4px; left: -44px; cursor: pointer; display: none;"
+ src="plugins/CoreHome/templates/images/reset_search.png"/>
</div>
</div>
- </div>
- {if isset($inputName)}<input type="hidden" name="{$inputName}" value="{if isset($idSite)}{$idSite}{else}{$sites[0].idsite}{/if}"/>{/if}
+ </div>
+ {if isset($inputName)}<input type="hidden" name="{$inputName}" value="{if isset($idSite)}{$idSite}{else}{$sites[0].idsite}{/if}"/>{/if}
</div>
<script type="text/javascript">
- {literal}$(document).ready(function() { piwik.initSiteSelectors(); });{/literal}
+ {literal}$(document).ready(function () { piwik.initSiteSelectors(); });
+ {/literal}
</script>
diff --git a/plugins/CoreHome/templates/sparkline.js b/plugins/CoreHome/templates/sparkline.js
index ad5bfd3a88..dcf9d59d7b 100644
--- a/plugins/CoreHome/templates/sparkline.js
+++ b/plugins/CoreHome/templates/sparkline.js
@@ -5,58 +5,54 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function initializeSparklines () {
- var sparklineUrlParamsToIgnore = ['module', 'action', 'idSite', 'period', 'date', 'viewDataTable'];
-
- $("a[name='evolutionGraph']").each(function() {
- var graph = $(this);
-
- // try to find sparklines and add them clickable behaviour
- graph.parent().find('div.sparkline').each(function() {
- // find the sparkline and get it's src attribute
- var sparklineUrl = $('img.sparkline', this).attr('src');
-
- if (sparklineUrl != "")
- {
- var params = broadcast.getValuesFromUrl(sparklineUrl);
- for (var i = 0; i != sparklineUrlParamsToIgnore.length; ++i)
- {
- delete params[sparklineUrlParamsToIgnore[i]];
- }
- for (var key in params)
- {
- params[key] = decodeURIComponent(params[key]);
- }
-
- // on click, reload the graph with the new url
- $(this).click(function() {
- var idDataTable = graph.attr('graphId'),
- dataTable = $('#' + idDataTable);
-
- // when the metrics picker is used, the id of the data table might be updated (which is correct behavior).
- // for example, in goal reports it might change from GoalsgetEvolutionGraph to GoalsgetEvolutionGraph1.
- // if this happens, we can't find the graph using $('#'+idDataTable+"Chart");
- // instead, we just use the first evolution graph we can find.
- if (dataTable.length == 0)
- {
- dataTable = $('div.dataTableGraphEvolutionWrapper').first().closest('.dataTable');
- }
-
- // reload the datatable w/ a new column & scroll to the graph
- dataTable.trigger('reload', params);
- });
- $(this).hover(
- function() {
- $(this).css({
- "cursor": "pointer",
- "border-bottom": "1px dashed #C3C3C3"
- });
- },
- function(){
- $(this).css({"border-bottom":"1px solid white"});
- }
- );
- }
- });
- });
+function initializeSparklines() {
+ var sparklineUrlParamsToIgnore = ['module', 'action', 'idSite', 'period', 'date', 'viewDataTable'];
+
+ $("a[name='evolutionGraph']").each(function () {
+ var graph = $(this);
+
+ // try to find sparklines and add them clickable behaviour
+ graph.parent().find('div.sparkline').each(function () {
+ // find the sparkline and get it's src attribute
+ var sparklineUrl = $('img.sparkline', this).attr('src');
+
+ if (sparklineUrl != "") {
+ var params = broadcast.getValuesFromUrl(sparklineUrl);
+ for (var i = 0; i != sparklineUrlParamsToIgnore.length; ++i) {
+ delete params[sparklineUrlParamsToIgnore[i]];
+ }
+ for (var key in params) {
+ params[key] = decodeURIComponent(params[key]);
+ }
+
+ // on click, reload the graph with the new url
+ $(this).click(function () {
+ var idDataTable = graph.attr('graphId'),
+ dataTable = $('#' + idDataTable);
+
+ // when the metrics picker is used, the id of the data table might be updated (which is correct behavior).
+ // for example, in goal reports it might change from GoalsgetEvolutionGraph to GoalsgetEvolutionGraph1.
+ // if this happens, we can't find the graph using $('#'+idDataTable+"Chart");
+ // instead, we just use the first evolution graph we can find.
+ if (dataTable.length == 0) {
+ dataTable = $('div.dataTableGraphEvolutionWrapper').first().closest('.dataTable');
+ }
+
+ // reload the datatable w/ a new column & scroll to the graph
+ dataTable.trigger('reload', params);
+ });
+ $(this).hover(
+ function () {
+ $(this).css({
+ "cursor": "pointer",
+ "border-bottom": "1px dashed #C3C3C3"
+ });
+ },
+ function () {
+ $(this).css({"border-bottom": "1px solid white"});
+ }
+ );
+ }
+ });
+ });
}
diff --git a/plugins/CoreHome/templates/sparkline_footer.tpl b/plugins/CoreHome/templates/sparkline_footer.tpl
index 068acec3e9..71231c74b5 100644
--- a/plugins/CoreHome/templates/sparkline_footer.tpl
+++ b/plugins/CoreHome/templates/sparkline_footer.tpl
@@ -1,7 +1,7 @@
{literal}
-<script type="text/javascript">
- $(document).ready(function() {
- initializeSparklines();
- });
-</script>
+ <script type="text/javascript">
+ $(document).ready(function () {
+ initializeSparklines();
+ });
+ </script>
{/literal}
diff --git a/plugins/CoreHome/templates/styles.css b/plugins/CoreHome/templates/styles.css
index b98b82b386..005e8840bc 100644
--- a/plugins/CoreHome/templates/styles.css
+++ b/plugins/CoreHome/templates/styles.css
@@ -1,148 +1,147 @@
h1 {
- font-size: 18px;
- font-weight:bold;
- color: #7e7363;
- padding:3px 0 7px 0;
- clear:both;
+ font-size: 18px;
+ font-weight: bold;
+ color: #7e7363;
+ padding: 3px 0 7px 0;
+ clear: both;
}
h2 {
- font-size: 18px;
- font-weight:normal;
- color: #7e7363;
- padding:12px 0 7px 0;
- clear:both;
+ font-size: 18px;
+ font-weight: normal;
+ color: #7e7363;
+ padding: 12px 0 7px 0;
+ clear: both;
}
h2 a {
- text-decoration:none;
- color: #7e7363;
+ text-decoration: none;
+ color: #7e7363;
}
h3 {
- font-size: 1.3em;
- margin-top: 2em;
- color: #1D3256;
+ font-size: 1.3em;
+ margin-top: 2em;
+ color: #1D3256;
}
.home p {
- padding-bottom: 1em;
- margin-right: 1em;
- margin-left:1em;
+ padding-bottom: 1em;
+ margin-right: 1em;
+ margin-left: 1em;
}
-.page{
- background: url(../../../themes/default/images/page_border_grad.png) no-repeat 0 20px;
- min-height:10px;
-
-}
+.page {
+ background: url(../../../themes/default/images/page_border_grad.png) no-repeat 0 20px;
+ min-height: 10px;
-.pageWrap{
- padding:15px 15px 0 15px;
- min-height:10px;
- position:relative;
- background: url(../../../themes/default/images/page_border_grad.png) no-repeat right 20px;
-
}
+.pageWrap {
+ padding: 15px 15px 0 15px;
+ min-height: 10px;
+ position: relative;
+ background: url(../../../themes/default/images/page_border_grad.png) no-repeat right 20px;
-
-.nav_sep{
- height:39px;
- margin:-15px -15px 18px -15px;
- border-radius: 0 4px 0 0;
- -moz-border-radius: 0 4px 0 0;
- -webkit-border-radius: 0 4px 0 0;
- border:1px solid #dddddd;
}
+.nav_sep {
+ height: 39px;
+ margin: -15px -15px 18px -15px;
+ border-radius: 0 4px 0 0;
+ -moz-border-radius: 0 4px 0 0;
+ -webkit-border-radius: 0 4px 0 0;
+ border: 1px solid #dddddd;
+}
/* Content */
#content.home {
- padding-top:5px;
- font-size:14px;
+ padding-top: 5px;
+ font-size: 14px;
}
-
/* 2 columns reports */
#leftcolumn {
- float: left;
- width: 50%;
+ float: left;
+ width: 50%;
}
+
#rightcolumn {
- float: right;
- width: 45%;
+ float: right;
+ width: 45%;
}
+
/* not in widget */
.widget #leftcolumn, .widget #rightcolumn {
- float:left;
- padding: 0 10px;
- width:auto;
+ float: left;
+ padding: 0 10px;
+ width: auto;
}
/* Calendar*/
div.ui-datepicker {
- font-size: 62.5%;
+ font-size: 62.5%;
}
.ui-datepicker-current-period a, .ui-datepicker-current-period a:link, .ui-datepicker-current-period a:visited {
- border: 1px solid #2E85FF;
- color: #2E85FF;
+ border: 1px solid #2E85FF;
+ color: #2E85FF;
}
+
#otherPeriods a {
- text-decoration: none;
+ text-decoration: none;
}
#otherPeriods a:hover {
- text-decoration: underline;
+ text-decoration: underline;
}
#currentPeriod {
- border-bottom: 1px dotted #520202;
+ border-bottom: 1px dotted #520202;
}
.hoverPeriod {
- cursor: pointer;
- font-weight: bold;
- border-bottom: 1px solid #520202;
+ cursor: pointer;
+ font-weight: bold;
+ border-bottom: 1px solid #520202;
}
#calendarRangeTo {
- float:left;
+ float: left;
margin-left: 20px;
}
#calendarRangeFrom {
- float:left;
+ float: left;
}
#inputCalendarFrom, #inputCalendarTo {
- margin-left: 10px;
+ margin-left: 10px;
width: 90px;
}
#calendarRangeApply {
- display:none;
- margin-top:10px;
- margin-left:10px;
+ display: none;
+ margin-top: 10px;
+ margin-left: 10px;
}
#invalidDateRange {
- display:none;
+ display: none;
}
div .sparkline {
- float:left;
- clear:both;
- padding-bottom: 1px;
- margin-top:10px;
- border-bottom:1px solid white;
+ float: left;
+ clear: both;
+ padding-bottom: 1px;
+ margin-top: 10px;
+ border-bottom: 1px solid white;
}
.sparkline img {
- vertical-align: middle;
- padding-right: 10px;
- margin-top: 0;
+ vertical-align: middle;
+ padding-right: 10px;
+ margin-top: 0;
}
div.pk-emptyGraph {
@@ -152,85 +151,85 @@ div.pk-emptyGraph {
font-style: italic;
}
-
/**
* Popover
* @see popover.js
*/
#Piwik_Popover {
- font-family: Arial, Helvetica, sans-serif;
+ font-family: Arial, Helvetica, sans-serif;
}
.Piwik_Popover_Loading_Name {
- padding: 50px 0 65px 0;
- font-size: 16px;
- line-height: 20px;
- font-weight: normal;
- text-align: center;
- background: url(../../../themes/default/images/loading-blue.gif) no-repeat center 20px;
+ padding: 50px 0 65px 0;
+ font-size: 16px;
+ line-height: 20px;
+ font-weight: normal;
+ text-align: center;
+ background: url(../../../themes/default/images/loading-blue.gif) no-repeat center 20px;
}
.Piwik_Popover_Loading_NameWithSubject {
- padding-bottom: 30px;
+ padding-bottom: 30px;
}
.Piwik_Popover_Loading_Subject {
- padding: 0 70px 55px 70px;
- color: #7e7363;
- text-align: center;
- font-size: 14px;
+ padding: 0 70px 55px 70px;
+ color: #7e7363;
+ text-align: center;
+ font-size: 14px;
}
.Piwik_Popover_Error {
- padding: 50px 20px 65px 20px;
- text-align: center;
+ padding: 50px 20px 65px 20px;
+ text-align: center;
}
.Piwik_Popover_Error_Title {
- color: #E87500;
- font-weight: bold;
- font-size: 16px;
+ color: #E87500;
+ font-weight: bold;
+ font-size: 16px;
}
.Piwik_Popover_Error_Title span {
- color: #222;
- font-weight: normal;
- font-size: 16px;
+ color: #222;
+ font-weight: normal;
+ font-size: 16px;
}
.Piwik_Popover_Error_Message {
- color: #7e7363;
- padding: 20px 0 0 0;
- font-size: 14px;
+ color: #7e7363;
+ padding: 20px 0 0 0;
+ font-size: 14px;
}
a.Piwik_Popover_Error_Back {
- display: block;
- margin: 20px 0 0 0;
- color: #1D3256;
- font-size: 14px;
+ display: block;
+ margin: 20px 0 0 0;
+ color: #1D3256;
+ font-size: 14px;
}
#alert.ui-confirm input {
display: block;
- margin: 10px auto 5px!important;
+ margin: 10px auto 5px !important;
}
#updateCheckLinkContainer {
- float:right;
- cursor:pointer;
+ float: right;
+ cursor: pointer;
}
#updateCheckLinkContainer>* {
- vertical-align:bottom;
+ vertical-align: bottom;
}
#header_message #checkForUpdates {
- font-size:.8em;
- line-height:16px;
- display:inline-block;
+ font-size: .8em;
+ line-height: 16px;
+ display: inline-block;
}
+
#header_message #checkForUpdates:hover {
- text-decoration:none;
+ text-decoration: none;
}
diff --git a/plugins/CoreHome/templates/tooltip.js b/plugins/CoreHome/templates/tooltip.js
index fbeba46df3..cf1343887c 100644
--- a/plugins/CoreHome/templates/tooltip.js
+++ b/plugins/CoreHome/templates/tooltip.js
@@ -5,114 +5,114 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-var Piwik_Tooltip = (function() {
-
- var domElement = false;
- var visible = false;
- var addedClass = false;
- var topOffset = 15;
-
- var mouseX, mouseY;
-
- /** Position the tooltip next to the mouse */
- var position = function() {
- var tipWidth = domElement.outerWidth();
- var maxX = $('body').innerWidth() - tipWidth - 25;
- if (mouseX < maxX) {
- // tooltip right of mouse
- domElement.css({
- top: (mouseY - topOffset) + "px",
- left: (mouseX + 15) + "px"
- });
- }
- else {
- // tooltip left of mouse
- domElement.css({
- top: (mouseY - topOffset) + "px",
- left: (mouseX - 15 - tipWidth) + "px"
- });
- }
- };
-
- /** Create and initialize the tooltip */
- var initialize = function() {
- if (domElement !== false) {
- return;
- }
-
- domElement = $(document.createElement('div'));
- domElement.addClass('piwik-tooltip');
- $('body').prepend(domElement);
- domElement.hide();
-
- $(document).mousemove(function(e) {
- mouseX = e.pageX;
- mouseY = e.pageY;
- if (visible) {
- position();
- }
- });
- };
-
- $(document).ready(function() {
- initialize();
- });
-
- return {
-
- /** Show the tooltip with HTML content. */
- show: function(html, addClass, maxWidth) {
- initialize();
-
- if (visible && addedClass != addClass) {
- domElement.removeClass(addedClass);
- } else {
- visible = true;
- position();
- domElement.show();
- }
-
- if (addClass && addedClass != addClass) {
- addedClass = addClass;
- domElement.addClass(addClass);
- }
-
- domElement.css({width: 'auto'});
- domElement.html(html);
- if (domElement.outerWidth() > maxWidth) {
- domElement.css({width: maxWidth + 'px'});
- }
-
- if (domElement.outerHeight() < 25) {
- topOffset = 5;
- } else {
- topOffset = 15;
- }
-
- position();
- },
-
- /** Show the tooltip with title/text content. */
- showWithTitle: function(title, text, addClass) {
- var html = '<span class="tip-title">' + title + '</span><br />' + text;
- this.show(html, addClass);
- },
-
- /** Hide the tooltip */
- hide: function() {
- if (domElement !== false) {
- domElement.hide();
- }
-
- if (addedClass) {
- domElement.removeClass(addedClass);
- addedClass = false;
- }
-
- visible = false;
- }
-
- };
+var Piwik_Tooltip = (function () {
+
+ var domElement = false;
+ var visible = false;
+ var addedClass = false;
+ var topOffset = 15;
+
+ var mouseX, mouseY;
+
+ /** Position the tooltip next to the mouse */
+ var position = function () {
+ var tipWidth = domElement.outerWidth();
+ var maxX = $('body').innerWidth() - tipWidth - 25;
+ if (mouseX < maxX) {
+ // tooltip right of mouse
+ domElement.css({
+ top: (mouseY - topOffset) + "px",
+ left: (mouseX + 15) + "px"
+ });
+ }
+ else {
+ // tooltip left of mouse
+ domElement.css({
+ top: (mouseY - topOffset) + "px",
+ left: (mouseX - 15 - tipWidth) + "px"
+ });
+ }
+ };
+
+ /** Create and initialize the tooltip */
+ var initialize = function () {
+ if (domElement !== false) {
+ return;
+ }
+
+ domElement = $(document.createElement('div'));
+ domElement.addClass('piwik-tooltip');
+ $('body').prepend(domElement);
+ domElement.hide();
+
+ $(document).mousemove(function (e) {
+ mouseX = e.pageX;
+ mouseY = e.pageY;
+ if (visible) {
+ position();
+ }
+ });
+ };
+
+ $(document).ready(function () {
+ initialize();
+ });
+
+ return {
+
+ /** Show the tooltip with HTML content. */
+ show: function (html, addClass, maxWidth) {
+ initialize();
+
+ if (visible && addedClass != addClass) {
+ domElement.removeClass(addedClass);
+ } else {
+ visible = true;
+ position();
+ domElement.show();
+ }
+
+ if (addClass && addedClass != addClass) {
+ addedClass = addClass;
+ domElement.addClass(addClass);
+ }
+
+ domElement.css({width: 'auto'});
+ domElement.html(html);
+ if (domElement.outerWidth() > maxWidth) {
+ domElement.css({width: maxWidth + 'px'});
+ }
+
+ if (domElement.outerHeight() < 25) {
+ topOffset = 5;
+ } else {
+ topOffset = 15;
+ }
+
+ position();
+ },
+
+ /** Show the tooltip with title/text content. */
+ showWithTitle: function (title, text, addClass) {
+ var html = '<span class="tip-title">' + title + '</span><br />' + text;
+ this.show(html, addClass);
+ },
+
+ /** Hide the tooltip */
+ hide: function () {
+ if (domElement !== false) {
+ domElement.hide();
+ }
+
+ if (addedClass) {
+ domElement.removeClass(addedClass);
+ addedClass = false;
+ }
+
+ visible = false;
+ }
+
+ };
})();
diff --git a/plugins/CoreHome/templates/top_bar.tpl b/plugins/CoreHome/templates/top_bar.tpl
index 2b04de21d5..56a8c8937e 100644
--- a/plugins/CoreHome/templates/top_bar.tpl
+++ b/plugins/CoreHome/templates/top_bar.tpl
@@ -1,13 +1,13 @@
<div id="topBars">
- {include file="CoreHome/templates/top_bar_top_menu.tpl"}
- {include file="CoreHome/templates/top_bar_hello_menu.tpl"}
+ {include file="CoreHome/templates/top_bar_top_menu.tpl"}
+ {include file="CoreHome/templates/top_bar_hello_menu.tpl"}
</div>
{if $showSitesSelection}
-<div class="top_bar_sites_selector">
- <label>{'General_Website'|translate}</label>
- {include file="CoreHome/templates/sites_selection.tpl"}
-</div>
+ <div class="top_bar_sites_selector">
+ <label>{'General_Website'|translate}</label>
+ {include file="CoreHome/templates/sites_selection.tpl"}
+ </div>
{/if}
diff --git a/plugins/CoreHome/templates/top_bar_hello_menu.tpl b/plugins/CoreHome/templates/top_bar_hello_menu.tpl
index 4a3fa0dc8f..9a5dc26cbc 100644
--- a/plugins/CoreHome/templates/top_bar_hello_menu.tpl
+++ b/plugins/CoreHome/templates/top_bar_hello_menu.tpl
@@ -1,7 +1,7 @@
-
<div id="topRightBar">
-{capture assign=helloAlias}{if !empty($userAlias)}{$userAlias}{else}{$userLogin}{/if}{/capture}
- <span class="topBarElem">{'General_HelloUser'|translate:"<strong>$helloAlias</strong>"}</span>
-{if $userLogin != 'anonymous'}| <span class="topBarElem"><a href='index.php?module=CoreAdminHome'>{'General_Settings'|translate}</a></span>{/if}
- | <span class="topBarElem">{if $userLogin == 'anonymous'}<a href='index.php?module={$loginModule}'>{'Login_LogIn'|translate}</a>{else}<a href='index.php?module={$loginModule}&amp;action=logout'>{'Login_Logout'|translate}</a>{/if}</span>
+ {capture assign=helloAlias}{if !empty($userAlias)}{$userAlias}{else}{$userLogin}{/if}{/capture}
+ <span class="topBarElem">{'General_HelloUser'|translate:"<strong>$helloAlias</strong>"}</span>
+ {if $userLogin != 'anonymous'}| <span class="topBarElem"><a href='index.php?module=CoreAdminHome'>{'General_Settings'|translate}</a></span>{/if}
+ | <span class="topBarElem">{if $userLogin == 'anonymous'}<a href='index.php?module={$loginModule}'>{'Login_LogIn'|translate}</a>{else}<a
+ href='index.php?module={$loginModule}&amp;action=logout'>{'Login_Logout'|translate}</a>{/if}</span>
</div>
diff --git a/plugins/CoreHome/templates/top_bar_top_menu.tpl b/plugins/CoreHome/templates/top_bar_top_menu.tpl
index 669db27933..844549f8df 100644
--- a/plugins/CoreHome/templates/top_bar_top_menu.tpl
+++ b/plugins/CoreHome/templates/top_bar_top_menu.tpl
@@ -1,13 +1,16 @@
<div id="topLeftBar">
-{foreach from=$topMenu key=label item=menu name=topMenu}
+ {foreach from=$topMenu key=label item=menu name=topMenu}
- {if isset($menu._html)}
- {$menu._html}
- {elseif $menu._url.module == $currentModule && (empty($menu._url.action) || $menu._url.action == $currentAction)}
- <span class="topBarElem"><b>{$label|translate}</b></span> |
- {else}
- <span class="topBarElem" {if isset($menu._tooltip)}title="{$menu._tooltip}"{/if}><a id="topmenu-{$menu._url.module|strtolower}" href="index.php{$menu._url|@urlRewriteWithParameters}">{$label|translate}</a></span> |
- {/if}
+ {if isset($menu._html)}
+ {$menu._html}
+ {elseif $menu._url.module == $currentModule && (empty($menu._url.action) || $menu._url.action == $currentAction)}
+ <span class="topBarElem"><b>{$label|translate}</b></span>
+ |
+ {else}
+ <span class="topBarElem" {if isset($menu._tooltip)}title="{$menu._tooltip}"{/if}><a id="topmenu-{$menu._url.module|strtolower}"
+ href="index.php{$menu._url|@urlRewriteWithParameters}">{$label|translate}</a></span>
+ |
+ {/if}
-{/foreach}
+ {/foreach}
</div> \ No newline at end of file
diff --git a/plugins/CoreHome/templates/top_screen.tpl b/plugins/CoreHome/templates/top_screen.tpl
index e38c5aaf35..771da8bc47 100644
--- a/plugins/CoreHome/templates/top_screen.tpl
+++ b/plugins/CoreHome/templates/top_screen.tpl
@@ -1,4 +1,4 @@
<div id="header">
-{include file="CoreHome/templates/logo.tpl"}
-{include file="CoreHome/templates/js_disabled_notice.tpl"}
+ {include file="CoreHome/templates/logo.tpl"}
+ {include file="CoreHome/templates/js_disabled_notice.tpl"}
</div>
diff --git a/plugins/CoreHome/templates/warning_invalid_host.tpl b/plugins/CoreHome/templates/warning_invalid_host.tpl
index d9a77cde70..0a33082ed6 100644
--- a/plugins/CoreHome/templates/warning_invalid_host.tpl
+++ b/plugins/CoreHome/templates/warning_invalid_host.tpl
@@ -1,9 +1,8 @@
-
{* untrusted host warning *}
{if isset($isValidHost) && isset($invalidHostMessage) && !$isValidHost}
-<div class="ajaxSuccess" style='clear:both;width:800px'>
- <a style="float:right" href="http://piwik.org/faq/troubleshooting/#faq_171" target="_blank"><img src="themes/default/images/help_grey.png" /></a>
- <strong>{'General_Warning'|translate}:&nbsp;</strong>{$invalidHostMessage}
-</div>
+ <div class="ajaxSuccess" style='clear:both;width:800px'>
+ <a style="float:right" href="http://piwik.org/faq/troubleshooting/#faq_171" target="_blank"><img src="themes/default/images/help_grey.png"/></a>
+ <strong>{'General_Warning'|translate}:&nbsp;</strong>{$invalidHostMessage}
+ </div>
{/if}
diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php
index 5960b4dd41..9896125435 100644
--- a/plugins/CorePluginsAdmin/Controller.php
+++ b/plugins/CorePluginsAdmin/Controller.php
@@ -15,78 +15,71 @@
*/
class Piwik_CorePluginsAdmin_Controller extends Piwik_Controller_Admin
{
- function index()
- {
- Piwik::checkUserIsSuperUser();
+ function index()
+ {
+ Piwik::checkUserIsSuperUser();
- $plugins = array();
+ $plugins = array();
- $listPlugins = array_merge(
- Piwik_PluginsManager::getInstance()->readPluginsDirectory(),
- Piwik_Config::getInstance()->Plugins['Plugins']
- );
- $listPlugins = array_unique($listPlugins);
- foreach($listPlugins as $pluginName)
- {
- Piwik_PluginsManager::getInstance()->loadPlugin($pluginName);
- $plugins[$pluginName] = array(
- 'activated' => Piwik_PluginsManager::getInstance()->isPluginActivated($pluginName),
- 'alwaysActivated' => Piwik_PluginsManager::getInstance()->isPluginAlwaysActivated($pluginName),
- );
- }
- Piwik_PluginsManager::getInstance()->loadPluginTranslations();
+ $listPlugins = array_merge(
+ Piwik_PluginsManager::getInstance()->readPluginsDirectory(),
+ Piwik_Config::getInstance()->Plugins['Plugins']
+ );
+ $listPlugins = array_unique($listPlugins);
+ foreach ($listPlugins as $pluginName) {
+ Piwik_PluginsManager::getInstance()->loadPlugin($pluginName);
+ $plugins[$pluginName] = array(
+ 'activated' => Piwik_PluginsManager::getInstance()->isPluginActivated($pluginName),
+ 'alwaysActivated' => Piwik_PluginsManager::getInstance()->isPluginAlwaysActivated($pluginName),
+ );
+ }
+ Piwik_PluginsManager::getInstance()->loadPluginTranslations();
- $loadedPlugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins();
- foreach($loadedPlugins as $oPlugin)
- {
- $pluginName = $oPlugin->getPluginName();
- $plugins[$pluginName]['info'] = $oPlugin->getInformation();
- }
-
- foreach($plugins as $pluginName => &$plugin)
- {
- if (!isset($plugin['info']))
- {
- $plugin['info'] = array(
- 'description' => '<strong><em>'.Piwik_Translate('CorePluginsAdmin_PluginCannotBeFound')
- .'</strong></em>',
- 'version' => Piwik_Translate('General_Unknown')
- );
- }
- }
+ $loadedPlugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins();
+ foreach ($loadedPlugins as $oPlugin) {
+ $pluginName = $oPlugin->getPluginName();
+ $plugins[$pluginName]['info'] = $oPlugin->getInformation();
+ }
- $view = Piwik_View::factory('manage');
- $view->pluginsName = $plugins;
- $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
- if(!Piwik_Config::getInstance()->isFileWritable())
- {
- $view->configFileNotWritable = true;
- }
- echo $view->render();
- }
+ foreach ($plugins as $pluginName => &$plugin) {
+ if (!isset($plugin['info'])) {
+ $plugin['info'] = array(
+ 'description' => '<strong><em>' . Piwik_Translate('CorePluginsAdmin_PluginCannotBeFound')
+ . '</strong></em>',
+ 'version' => Piwik_Translate('General_Unknown')
+ );
+ }
+ }
- public function deactivate($redirectAfter = true)
- {
- Piwik::checkUserIsSuperUser();
- $this->checkTokenInUrl();
- $pluginName = Piwik_Common::getRequestVar('pluginName', null, 'string');
- Piwik_PluginsManager::getInstance()->deactivatePlugin($pluginName);
- if($redirectAfter)
- {
- Piwik_Url::redirectToReferer();
- }
- }
+ $view = Piwik_View::factory('manage');
+ $view->pluginsName = $plugins;
+ $this->setBasicVariablesView($view);
+ $view->menu = Piwik_GetAdminMenu();
+ if (!Piwik_Config::getInstance()->isFileWritable()) {
+ $view->configFileNotWritable = true;
+ }
+ echo $view->render();
+ }
- public function activate($redirectAfter = true)
- {
- Piwik::checkUserIsSuperUser();
- $this->checkTokenInUrl();
- $pluginName = Piwik_Common::getRequestVar('pluginName', null, 'string');
- Piwik_PluginsManager::getInstance()->activatePlugin($pluginName);
- if($redirectAfter)
- {
- Piwik_Url::redirectToReferer();
- }
- }
+ public function deactivate($redirectAfter = true)
+ {
+ Piwik::checkUserIsSuperUser();
+ $this->checkTokenInUrl();
+ $pluginName = Piwik_Common::getRequestVar('pluginName', null, 'string');
+ Piwik_PluginsManager::getInstance()->deactivatePlugin($pluginName);
+ if ($redirectAfter) {
+ Piwik_Url::redirectToReferer();
+ }
+ }
+
+ public function activate($redirectAfter = true)
+ {
+ Piwik::checkUserIsSuperUser();
+ $this->checkTokenInUrl();
+ $pluginName = Piwik_Common::getRequestVar('pluginName', null, 'string');
+ Piwik_PluginsManager::getInstance()->activatePlugin($pluginName);
+ if ($redirectAfter) {
+ Piwik_Url::redirectToReferer();
+ }
+ }
}
diff --git a/plugins/CorePluginsAdmin/CorePluginsAdmin.php b/plugins/CorePluginsAdmin/CorePluginsAdmin.php
index 1630e59bf6..89dd9bb2e6 100644
--- a/plugins/CorePluginsAdmin/CorePluginsAdmin.php
+++ b/plugins/CorePluginsAdmin/CorePluginsAdmin.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_CorePluginsAdmin
*/
@@ -15,27 +15,27 @@
*/
class Piwik_CorePluginsAdmin extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('CorePluginsAdmin_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- function getListHooksRegistered()
- {
- return array('AdminMenu.add' => 'addMenu');
- }
-
- function addMenu()
- {
- Piwik_AddAdminSubMenu('CorePluginsAdmin_MenuPlugins', null, "", Piwik::isUserIsSuperUser(), $order = 15);
- Piwik_AddAdminSubMenu('CorePluginsAdmin_MenuPlugins', 'CorePluginsAdmin_MenuPluginsInstalled',
- array('module' => 'CorePluginsAdmin', 'action' => 'index'),
- Piwik::isUserIsSuperUser(),
- $order = 1);
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('CorePluginsAdmin_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ function getListHooksRegistered()
+ {
+ return array('AdminMenu.add' => 'addMenu');
+ }
+
+ function addMenu()
+ {
+ Piwik_AddAdminSubMenu('CorePluginsAdmin_MenuPlugins', null, "", Piwik::isUserIsSuperUser(), $order = 15);
+ Piwik_AddAdminSubMenu('CorePluginsAdmin_MenuPlugins', 'CorePluginsAdmin_MenuPluginsInstalled',
+ array('module' => 'CorePluginsAdmin', 'action' => 'index'),
+ Piwik::isUserIsSuperUser(),
+ $order = 1);
+ }
}
diff --git a/plugins/CorePluginsAdmin/templates/manage.tpl b/plugins/CorePluginsAdmin/templates/manage.tpl
index a381bac550..40d742ea78 100644
--- a/plugins/CorePluginsAdmin/templates/manage.tpl
+++ b/plugins/CorePluginsAdmin/templates/manage.tpl
@@ -2,54 +2,63 @@
<div style="max-width:980px;">
-<h2>{'CorePluginsAdmin_PluginsManagement'|translate}</h2>
-<p>{'CorePluginsAdmin_MainDescription'|translate}</p>
-<div class='entityContainer'>
-<table class="dataTable entityTable">
- <thead>
- <tr>
- <th>{'CorePluginsAdmin_Plugin'|translate}</th>
- <th class="num">{'CorePluginsAdmin_Version'|translate}</th>
- <th>{'General_Description'|translate}</th>
- <th class="status">{'CorePluginsAdmin_Status'|translate}</th>
- <th class="action-links">{'CorePluginsAdmin_Action'|translate}</th>
- </tr>
- </thead>
- <tbody id="plugins">
- {foreach from=$pluginsName key=name item=plugin}
- {if isset($plugin.alwaysActivated) && !$plugin.alwaysActivated}
- <tr {if $plugin.activated}class="highlighted"{/if}>
- <td class="name">
- {if isset($plugin.info.homepage)}<a title="{'CorePluginsAdmin_PluginHomepage'|translate}" href="{$plugin.info.homepage}" target="_blank">{/if}
- {$name}
- {if isset($plugin.info.homepage)}</a>{/if}
- </td>
- <td class="vers">{$plugin.info.version}</td>
- <td class="desc">
- {$plugin.info.description|nl2br}
- {if isset($plugin.info.license)}
- &nbsp;({if isset($plugin.info.license_homepage)}<a title="{'CorePluginsAdmin_LicenseHomepage'|translate}" target="_blank" href="{$plugin.info.license_homepage}">{/if}{$plugin.info.license}{if isset($plugin.info.license_homepage)}</a>){/if}
- {/if}
- {if isset($plugin.info.author)}
- &nbsp;<cite>By
- {if isset($plugin.info.author_homepage)}<a title="{'CorePluginsAdmin_AuthorHomepage'|translate}" href="{$plugin.info.author_homepage}" target="_blank">{/if}{$plugin.info.author}{if isset($plugin.info.author_homepage)}</a>{/if}.</cite>
- {/if}
- </td>
- <td class="status">
- {if $plugin.activated}{'CorePluginsAdmin_Active'|translate}
- {else}{'CorePluginsAdmin_Inactive'|translate}{/if}
- </td>
-
- <td class="togl action-links">
- {if $plugin.activated}<a href='index.php?module=CorePluginsAdmin&action=deactivate&pluginName={$name}&token_auth={$token_auth}'>{'CorePluginsAdmin_Deactivate'|translate}</a>
- {else}<a href='index.php?module=CorePluginsAdmin&action=activate&pluginName={$name}&token_auth={$token_auth}'>{'CorePluginsAdmin_Activate'|translate}</a>{/if}
- </td>
- </tr>
- {/if}
-{/foreach}
-</tbody>
-</table>
-</div>
+ <h2>{'CorePluginsAdmin_PluginsManagement'|translate}</h2>
+
+ <p>{'CorePluginsAdmin_MainDescription'|translate}</p>
+
+ <div class='entityContainer'>
+ <table class="dataTable entityTable">
+ <thead>
+ <tr>
+ <th>{'CorePluginsAdmin_Plugin'|translate}</th>
+ <th class="num">{'CorePluginsAdmin_Version'|translate}</th>
+ <th>{'General_Description'|translate}</th>
+ <th class="status">{'CorePluginsAdmin_Status'|translate}</th>
+ <th class="action-links">{'CorePluginsAdmin_Action'|translate}</th>
+ </tr>
+ </thead>
+ <tbody id="plugins">
+ {foreach from=$pluginsName key=name item=plugin}
+ {if isset($plugin.alwaysActivated) && !$plugin.alwaysActivated}
+ <tr {if $plugin.activated}class="highlighted"{/if}>
+ <td class="name">
+ {if isset($plugin.info.homepage)}<a title="{'CorePluginsAdmin_PluginHomepage'|translate}" href="{$plugin.info.homepage}"
+ target="_blank">{/if}
+ {$name}
+ {if isset($plugin.info.homepage)}</a>{/if}
+ </td>
+ <td class="vers">{$plugin.info.version}</td>
+ <td class="desc">
+ {$plugin.info.description|nl2br}
+ {if isset($plugin.info.license)}
+ &nbsp;({if isset($plugin.info.license_homepage)}<a title="{'CorePluginsAdmin_LicenseHomepage'|translate}" target="_blank" href="{$plugin.info.license_homepage}">{/if}{$plugin.info.license}{if isset($plugin.info.license_homepage)}</a>){/if}
+ {/if}
+ {if isset($plugin.info.author)}
+ &nbsp;
+ <cite>By
+ {if isset($plugin.info.author_homepage)}<a title="{'CorePluginsAdmin_AuthorHomepage'|translate}"
+ href="{$plugin.info.author_homepage}"
+ target="_blank">{/if}{$plugin.info.author}{if isset($plugin.info.author_homepage)}</a>{/if}
+ .</cite>
+ {/if}
+ </td>
+ <td class="status">
+ {if $plugin.activated}{'CorePluginsAdmin_Active'|translate}
+ {else}{'CorePluginsAdmin_Inactive'|translate}{/if}
+ </td>
+
+ <td class="togl action-links">
+ {if $plugin.activated}<a
+ href='index.php?module=CorePluginsAdmin&action=deactivate&pluginName={$name}&token_auth={$token_auth}'>{'CorePluginsAdmin_Deactivate'|translate}</a>
+ {else}<a
+ href='index.php?module=CorePluginsAdmin&action=activate&pluginName={$name}&token_auth={$token_auth}'>{'CorePluginsAdmin_Activate'|translate}</a>{/if}
+ </td>
+ </tr>
+ {/if}
+ {/foreach}
+ </tbody>
+ </table>
+ </div>
</div>
{include file="CoreAdminHome/templates/footer.tpl"}
diff --git a/plugins/CoreUpdater/Controller.php b/plugins/CoreUpdater/Controller.php
index b8830a8711..8e20f9c82a 100644
--- a/plugins/CoreUpdater/Controller.php
+++ b/plugins/CoreUpdater/Controller.php
@@ -15,368 +15,336 @@
*/
class Piwik_CoreUpdater_Controller extends Piwik_Controller
{
- const CONFIG_FILE_BACKUP = '/config/global.ini.auto-backup-before-update.php';
- const PATH_TO_EXTRACT_LATEST_VERSION = '/tmp/latest/';
-
- private $coreError = false;
- private $warningMessages = array();
- private $errorMessages = array();
- private $deactivatedPlugins = array();
-
- static protected function getLatestZipUrl($newVersion)
- {
- if(@Piwik_Config::getInstance()->Debug['allow_upgrades_to_beta'])
- {
- return 'http://builds.piwik.org/piwik-'.$newVersion.'.zip';
- }
- return Piwik_Config::getInstance()->General['latest_version_url'];
- }
- public function newVersionAvailable()
- {
- Piwik::checkUserIsSuperUser();
-
- $newVersion = $this->checkNewVersionIsAvailableOrDie();
-
- $view = Piwik_View::factory('update_new_version_available');
- $view->piwik_version = Piwik_Version::VERSION;
- $view->piwik_new_version = $newVersion;
- $view->piwik_latest_version_url = self::getLatestZipUrl($newVersion);
- $view->can_auto_update = Piwik::canAutoUpdate();
- $view->makeWritableCommands = Piwik::getAutoUpdateMakeWritableMessage();
- echo $view->render();
- }
-
- public function oneClickUpdate()
- {
- Piwik::checkUserIsSuperUser();
- $this->newVersion = $this->checkNewVersionIsAvailableOrDie();
-
- Piwik::setMaxExecutionTime(0);
-
- $url = self::getLatestZipUrl($this->newVersion);
- $steps = array(
- array('oneClick_Download', Piwik_Translate('CoreUpdater_DownloadingUpdateFromX', $url)),
- array('oneClick_Unpack', Piwik_Translate('CoreUpdater_UnpackingTheUpdate')),
- array('oneClick_Verify', Piwik_Translate('CoreUpdater_VerifyingUnpackedFiles')),
- array('oneClick_CreateConfigFileBackup', Piwik_Translate('CoreUpdater_CreatingBackupOfConfigurationFile', self::CONFIG_FILE_BACKUP)),
- array('oneClick_Copy', Piwik_Translate('CoreUpdater_InstallingTheLatestVersion')),
- array('oneClick_Finished', Piwik_Translate('CoreUpdater_PiwikUpdatedSuccessfully')),
- );
-
- $errorMessage = false;
- $messages = array();
- foreach($steps as $step) {
- try {
- $method = $step[0];
- $message = $step[1];
- $this->$method();
- $messages[] = $message;
- } catch(Exception $e) {
- $errorMessage = $e->getMessage();
- break;
- }
- }
-
- // this is a magic template to trigger the Piwik_View_Update
- $view = Piwik_View::factory(Piwik_View::COREUPDATER_ONE_CLICK_DONE);
- $view->coreError = $errorMessage;
- $view->feedbackMessages = $messages;
- echo $view->render();
- }
-
- public function oneClickResults()
- {
- Piwik_API_Request::reloadAuthUsingTokenAuth($_POST);
- Piwik::checkUserIsSuperUser();
-
- $view = Piwik_View::factory('update_one_click_results');
- $view->coreError = Piwik_Common::getRequestVar('error', '', 'string', $_POST);
- $view->feedbackMessages = safe_unserialize(Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('messages', '', 'string', $_POST)));
- echo $view->render();
- }
-
- private function checkNewVersionIsAvailableOrDie()
- {
- $newVersion = Piwik_UpdateCheck::isNewestVersionAvailable();
- if(!$newVersion)
- {
- throw new Exception(Piwik_TranslateException('CoreUpdater_ExceptionAlreadyLatestVersion', Piwik_Version::VERSION));
- }
- return $newVersion;
- }
-
- private function oneClick_Download()
- {
- $this->pathPiwikZip = PIWIK_USER_PATH . self::PATH_TO_EXTRACT_LATEST_VERSION . 'latest.zip';
- Piwik::checkDirectoriesWritableOrDie( array(self::PATH_TO_EXTRACT_LATEST_VERSION) );
-
- // we catch exceptions in the caller (i.e., oneClickUpdate)
- $url = self::getLatestZipUrl($this->newVersion) . '?cb=' . $this->newVersion;
- $fetched = Piwik_Http::fetchRemoteFile($url, $this->pathPiwikZip);
- }
-
- private function oneClick_Unpack()
- {
- $pathExtracted = PIWIK_USER_PATH . self::PATH_TO_EXTRACT_LATEST_VERSION;
- $this->pathRootExtractedPiwik = $pathExtracted . 'piwik';
-
- if(file_exists($this->pathRootExtractedPiwik))
- {
- Piwik::unlinkRecursive($this->pathRootExtractedPiwik, true);
- }
-
- $archive = Piwik_Unzip::factory('PclZip', $this->pathPiwikZip);
-
- if ( 0 == ($archive_files = $archive->extract($pathExtracted) ) )
- {
- throw new Exception(Piwik_TranslateException('CoreUpdater_ExceptionArchiveIncompatible', $archive->errorInfo()));
- }
-
- if ( 0 == count($archive_files) )
- {
- throw new Exception(Piwik_TranslateException('CoreUpdater_ExceptionArchiveEmpty'));
- }
- unlink($this->pathPiwikZip);
- }
-
- private function oneClick_Verify()
- {
- $someExpectedFiles = array(
- '/config/global.ini.php',
- '/index.php',
- '/core/Piwik.php',
- '/piwik.php',
- '/plugins/API/API.php'
- );
- foreach($someExpectedFiles as $file)
- {
- if(!is_file($this->pathRootExtractedPiwik . $file))
- {
- throw new Exception(Piwik_TranslateException('CoreUpdater_ExceptionArchiveIncomplete', $file));
- }
- }
- }
-
- private function oneClick_CreateConfigFileBackup()
- {
- $configFileBefore = PIWIK_USER_PATH . '/config/global.ini.php';
- $configFileAfter = PIWIK_USER_PATH . self::CONFIG_FILE_BACKUP;
- Piwik::copy($configFileBefore, $configFileAfter);
- }
-
- private function oneClick_Copy()
- {
- /*
- * Make sure the execute bit is set for this shell script
- */
- if(!Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled())
- {
- @chmod($this->pathRootExtractedPiwik . '/misc/cron/archive.sh', 0755);
- }
-
- /*
- * Copy all files to PIWIK_INCLUDE_PATH.
- * These files are accessed through the dispatcher.
- */
- Piwik::copyRecursive($this->pathRootExtractedPiwik, PIWIK_INCLUDE_PATH);
-
- /*
- * These files are visible in the web root and are generally
- * served directly by the web server. May be shared.
- */
- if(PIWIK_INCLUDE_PATH !== PIWIK_DOCUMENT_ROOT)
- {
- /*
- * Copy PHP files that expect to be in the document root
- */
- $specialCases = array(
- '/index.php',
- '/piwik.php',
- '/js/index.php',
- );
-
- foreach($specialCases as $file)
- {
- Piwik::copy($this->pathRootExtractedPiwik . $file, PIWIK_DOCUMENT_ROOT . $file);
- }
-
- /*
- * Copy the non-PHP files (e.g., images, css, javascript)
- */
- Piwik::copyRecursive($this->pathRootExtractedPiwik, PIWIK_DOCUMENT_ROOT, true);
- }
-
- /*
- * Config files may be user (account) specific
- */
- if(PIWIK_INCLUDE_PATH !== PIWIK_USER_PATH)
- {
- Piwik::copyRecursive($this->pathRootExtractedPiwik . '/config', PIWIK_USER_PATH . '/config');
- }
-
- Piwik::unlinkRecursive($this->pathRootExtractedPiwik, true);
-
- if(function_exists('apc_clear_cache'))
- {
- apc_clear_cache(); // clear the system (aka 'opcode') cache
- }
- }
-
- private function oneClick_Finished()
- {
- }
-
- public function index()
- {
- $language = Piwik_Common::getRequestVar('language', '');
- if(!empty($language))
- {
- Piwik_LanguagesManager::setLanguageForSession($language);
- }
- $this->runUpdaterAndExit();
- }
-
- protected function runUpdaterAndExit()
- {
- $updater = new Piwik_Updater();
+ const CONFIG_FILE_BACKUP = '/config/global.ini.auto-backup-before-update.php';
+ const PATH_TO_EXTRACT_LATEST_VERSION = '/tmp/latest/';
+
+ private $coreError = false;
+ private $warningMessages = array();
+ private $errorMessages = array();
+ private $deactivatedPlugins = array();
+
+ static protected function getLatestZipUrl($newVersion)
+ {
+ if (@Piwik_Config::getInstance()->Debug['allow_upgrades_to_beta']) {
+ return 'http://builds.piwik.org/piwik-' . $newVersion . '.zip';
+ }
+ return Piwik_Config::getInstance()->General['latest_version_url'];
+ }
+
+ public function newVersionAvailable()
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $newVersion = $this->checkNewVersionIsAvailableOrDie();
+
+ $view = Piwik_View::factory('update_new_version_available');
+ $view->piwik_version = Piwik_Version::VERSION;
+ $view->piwik_new_version = $newVersion;
+ $view->piwik_latest_version_url = self::getLatestZipUrl($newVersion);
+ $view->can_auto_update = Piwik::canAutoUpdate();
+ $view->makeWritableCommands = Piwik::getAutoUpdateMakeWritableMessage();
+ echo $view->render();
+ }
+
+ public function oneClickUpdate()
+ {
+ Piwik::checkUserIsSuperUser();
+ $this->newVersion = $this->checkNewVersionIsAvailableOrDie();
+
+ Piwik::setMaxExecutionTime(0);
+
+ $url = self::getLatestZipUrl($this->newVersion);
+ $steps = array(
+ array('oneClick_Download', Piwik_Translate('CoreUpdater_DownloadingUpdateFromX', $url)),
+ array('oneClick_Unpack', Piwik_Translate('CoreUpdater_UnpackingTheUpdate')),
+ array('oneClick_Verify', Piwik_Translate('CoreUpdater_VerifyingUnpackedFiles')),
+ array('oneClick_CreateConfigFileBackup', Piwik_Translate('CoreUpdater_CreatingBackupOfConfigurationFile', self::CONFIG_FILE_BACKUP)),
+ array('oneClick_Copy', Piwik_Translate('CoreUpdater_InstallingTheLatestVersion')),
+ array('oneClick_Finished', Piwik_Translate('CoreUpdater_PiwikUpdatedSuccessfully')),
+ );
+
+ $errorMessage = false;
+ $messages = array();
+ foreach ($steps as $step) {
+ try {
+ $method = $step[0];
+ $message = $step[1];
+ $this->$method();
+ $messages[] = $message;
+ } catch (Exception $e) {
+ $errorMessage = $e->getMessage();
+ break;
+ }
+ }
+
+ // this is a magic template to trigger the Piwik_View_Update
+ $view = Piwik_View::factory(Piwik_View::COREUPDATER_ONE_CLICK_DONE);
+ $view->coreError = $errorMessage;
+ $view->feedbackMessages = $messages;
+ echo $view->render();
+ }
+
+ public function oneClickResults()
+ {
+ Piwik_API_Request::reloadAuthUsingTokenAuth($_POST);
+ Piwik::checkUserIsSuperUser();
+
+ $view = Piwik_View::factory('update_one_click_results');
+ $view->coreError = Piwik_Common::getRequestVar('error', '', 'string', $_POST);
+ $view->feedbackMessages = safe_unserialize(Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('messages', '', 'string', $_POST)));
+ echo $view->render();
+ }
+
+ private function checkNewVersionIsAvailableOrDie()
+ {
+ $newVersion = Piwik_UpdateCheck::isNewestVersionAvailable();
+ if (!$newVersion) {
+ throw new Exception(Piwik_TranslateException('CoreUpdater_ExceptionAlreadyLatestVersion', Piwik_Version::VERSION));
+ }
+ return $newVersion;
+ }
+
+ private function oneClick_Download()
+ {
+ $this->pathPiwikZip = PIWIK_USER_PATH . self::PATH_TO_EXTRACT_LATEST_VERSION . 'latest.zip';
+ Piwik::checkDirectoriesWritableOrDie(array(self::PATH_TO_EXTRACT_LATEST_VERSION));
+
+ // we catch exceptions in the caller (i.e., oneClickUpdate)
+ $url = self::getLatestZipUrl($this->newVersion) . '?cb=' . $this->newVersion;
+ $fetched = Piwik_Http::fetchRemoteFile($url, $this->pathPiwikZip);
+ }
+
+ private function oneClick_Unpack()
+ {
+ $pathExtracted = PIWIK_USER_PATH . self::PATH_TO_EXTRACT_LATEST_VERSION;
+ $this->pathRootExtractedPiwik = $pathExtracted . 'piwik';
+
+ if (file_exists($this->pathRootExtractedPiwik)) {
+ Piwik::unlinkRecursive($this->pathRootExtractedPiwik, true);
+ }
+
+ $archive = Piwik_Unzip::factory('PclZip', $this->pathPiwikZip);
+
+ if (0 == ($archive_files = $archive->extract($pathExtracted))) {
+ throw new Exception(Piwik_TranslateException('CoreUpdater_ExceptionArchiveIncompatible', $archive->errorInfo()));
+ }
+
+ if (0 == count($archive_files)) {
+ throw new Exception(Piwik_TranslateException('CoreUpdater_ExceptionArchiveEmpty'));
+ }
+ unlink($this->pathPiwikZip);
+ }
+
+ private function oneClick_Verify()
+ {
+ $someExpectedFiles = array(
+ '/config/global.ini.php',
+ '/index.php',
+ '/core/Piwik.php',
+ '/piwik.php',
+ '/plugins/API/API.php'
+ );
+ foreach ($someExpectedFiles as $file) {
+ if (!is_file($this->pathRootExtractedPiwik . $file)) {
+ throw new Exception(Piwik_TranslateException('CoreUpdater_ExceptionArchiveIncomplete', $file));
+ }
+ }
+ }
+
+ private function oneClick_CreateConfigFileBackup()
+ {
+ $configFileBefore = PIWIK_USER_PATH . '/config/global.ini.php';
+ $configFileAfter = PIWIK_USER_PATH . self::CONFIG_FILE_BACKUP;
+ Piwik::copy($configFileBefore, $configFileAfter);
+ }
+
+ private function oneClick_Copy()
+ {
+ /*
+ * Make sure the execute bit is set for this shell script
+ */
+ if (!Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled()) {
+ @chmod($this->pathRootExtractedPiwik . '/misc/cron/archive.sh', 0755);
+ }
+
+ /*
+ * Copy all files to PIWIK_INCLUDE_PATH.
+ * These files are accessed through the dispatcher.
+ */
+ Piwik::copyRecursive($this->pathRootExtractedPiwik, PIWIK_INCLUDE_PATH);
+
+ /*
+ * These files are visible in the web root and are generally
+ * served directly by the web server. May be shared.
+ */
+ if (PIWIK_INCLUDE_PATH !== PIWIK_DOCUMENT_ROOT) {
+ /*
+ * Copy PHP files that expect to be in the document root
+ */
+ $specialCases = array(
+ '/index.php',
+ '/piwik.php',
+ '/js/index.php',
+ );
+
+ foreach ($specialCases as $file) {
+ Piwik::copy($this->pathRootExtractedPiwik . $file, PIWIK_DOCUMENT_ROOT . $file);
+ }
+
+ /*
+ * Copy the non-PHP files (e.g., images, css, javascript)
+ */
+ Piwik::copyRecursive($this->pathRootExtractedPiwik, PIWIK_DOCUMENT_ROOT, true);
+ }
+
+ /*
+ * Config files may be user (account) specific
+ */
+ if (PIWIK_INCLUDE_PATH !== PIWIK_USER_PATH) {
+ Piwik::copyRecursive($this->pathRootExtractedPiwik . '/config', PIWIK_USER_PATH . '/config');
+ }
+
+ Piwik::unlinkRecursive($this->pathRootExtractedPiwik, true);
+
+ if (function_exists('apc_clear_cache')) {
+ apc_clear_cache(); // clear the system (aka 'opcode') cache
+ }
+ }
+
+ private function oneClick_Finished()
+ {
+ }
+
+ public function index()
+ {
+ $language = Piwik_Common::getRequestVar('language', '');
+ if (!empty($language)) {
+ Piwik_LanguagesManager::setLanguageForSession($language);
+ }
+ $this->runUpdaterAndExit();
+ }
+
+ protected function runUpdaterAndExit()
+ {
+ $updater = new Piwik_Updater();
$componentsWithUpdateFile = Piwik_CoreUpdater::getComponentUpdates($updater);
- if(empty($componentsWithUpdateFile))
- {
- Piwik::redirectToModule('CoreHome');
- }
-
- Piwik::setMaxExecutionTime(0);
-
- $sqlQueries = $updater->getSqlQueriesToExecute();
- if(Piwik_Common::isPhpCliMode())
- {
- $view = Piwik_View::factory('update_welcome');
- $this->doWelcomeUpdates($view, $componentsWithUpdateFile);
- echo $view->render();
-
- if(!$this->coreError
- && Piwik::getModule() == 'CoreUpdater')
- {
- $view = Piwik_View::factory('update_database_done');
- $this->doExecuteUpdates($view, $updater, $componentsWithUpdateFile);
- echo $view->render();
- }
- }
- else if(Piwik_Common::getRequestVar('updateCorePlugins', 0, 'integer') == 1)
- {
- $this->warningMessages = array();
- $view = Piwik_View::factory('update_database_done');
- $this->doExecuteUpdates($view, $updater, $componentsWithUpdateFile);
-
- if(count($sqlQueries)== 1 && !$this->coreError)
- {
- Piwik::redirectToModule('CoreHome');
- }
-
- echo $view->render();
- }
- else
- {
- $view = Piwik_View::factory('update_welcome');
- $view->queries = $sqlQueries;
- $view->isMajor = $updater->hasMajorDbUpdate();
- $this->doWelcomeUpdates($view, $componentsWithUpdateFile);
- echo $view->render();
- }
- exit;
- }
-
- private function doWelcomeUpdates($view, $componentsWithUpdateFile)
- {
- $view->new_piwik_version = Piwik_Version::VERSION;
- $view->commandUpgradePiwik = "<br /><code>php ".Piwik_Common::getPathToPiwikRoot()."/index.php -- \"module=CoreUpdater\" </code>";
- $pluginNamesToUpdate = array();
- $coreToUpdate = false;
-
- // handle case of existing database with no tables
- if(!Piwik::isInstalled())
- {
- $this->errorMessages[] = Piwik_Translate('CoreUpdater_EmptyDatabaseError', Piwik_Config::getInstance()->database['dbname']);
- $this->coreError = true;
- $currentVersion = 'N/A';
- }
- else
- {
- $this->errorMessages = array();
- try {
- $currentVersion = Piwik_GetOption('version_core');
- } catch( Exception $e) {
- $currentVersion = '<= 0.2.9';
- }
-
- foreach($componentsWithUpdateFile as $name => $filenames)
- {
- if($name == 'core')
- {
- $coreToUpdate = true;
- }
- else
- {
- $pluginNamesToUpdate[] = $name;
- }
- }
- }
-
- // check file integrity
- $integrityInfo = Piwik::getFileIntegrityInformation();
- if(isset($integrityInfo[1]))
- {
- if($integrityInfo[0] == false)
- {
- $this->warningMessages[] = '<b>'.Piwik_Translate('General_FileIntegrityWarningExplanation').'</b>';
- }
- $this->warningMessages = array_merge($this->warningMessages, array_slice($integrityInfo, 1));
- }
- Piwik::deleteAllCacheOnUpdate();
-
- $view->coreError = $this->coreError;
- $view->warningMessages = $this->warningMessages;
- $view->errorMessages = $this->errorMessages;
- $view->current_piwik_version = $currentVersion;
- $view->pluginNamesToUpdate = $pluginNamesToUpdate;
- $view->coreToUpdate = $coreToUpdate;
- }
-
- private function doExecuteUpdates($view, $updater, $componentsWithUpdateFile)
- {
- $this->loadAndExecuteUpdateFiles($updater, $componentsWithUpdateFile);
-
- Piwik::deleteAllCacheOnUpdate();
-
- $view->coreError = $this->coreError;
- $view->warningMessages = $this->warningMessages;
- $view->errorMessages = $this->errorMessages;
- $view->deactivatedPlugins = $this->deactivatedPlugins;
- }
-
- private function loadAndExecuteUpdateFiles($updater, $componentsWithUpdateFile)
- {
- // if error in any core update, show message + help message + EXIT
- // if errors in any plugins updates, show them on screen, disable plugins that errored + CONTINUE
- // if warning in any core update or in any plugins update, show message + CONTINUE
- // if no error or warning, success message + CONTINUE
- foreach($componentsWithUpdateFile as $name => $filenames)
- {
- try {
- $this->warningMessages = array_merge($this->warningMessages, $updater->update($name));
- } catch (Piwik_Updater_UpdateErrorException $e) {
- $this->errorMessages[] = $e->getMessage();
- if($name == 'core')
- {
- $this->coreError = true;
- break;
- }
- else
- {
- Piwik_PluginsManager::getInstance()->deactivatePlugin($name);
- $this->deactivatedPlugins[] = $name;
- }
- }
- }
- }
+ if (empty($componentsWithUpdateFile)) {
+ Piwik::redirectToModule('CoreHome');
+ }
+
+ Piwik::setMaxExecutionTime(0);
+
+ $sqlQueries = $updater->getSqlQueriesToExecute();
+ if (Piwik_Common::isPhpCliMode()) {
+ $view = Piwik_View::factory('update_welcome');
+ $this->doWelcomeUpdates($view, $componentsWithUpdateFile);
+ echo $view->render();
+
+ if (!$this->coreError
+ && Piwik::getModule() == 'CoreUpdater'
+ ) {
+ $view = Piwik_View::factory('update_database_done');
+ $this->doExecuteUpdates($view, $updater, $componentsWithUpdateFile);
+ echo $view->render();
+ }
+ } else if (Piwik_Common::getRequestVar('updateCorePlugins', 0, 'integer') == 1) {
+ $this->warningMessages = array();
+ $view = Piwik_View::factory('update_database_done');
+ $this->doExecuteUpdates($view, $updater, $componentsWithUpdateFile);
+
+ if (count($sqlQueries) == 1 && !$this->coreError) {
+ Piwik::redirectToModule('CoreHome');
+ }
+
+ echo $view->render();
+ } else {
+ $view = Piwik_View::factory('update_welcome');
+ $view->queries = $sqlQueries;
+ $view->isMajor = $updater->hasMajorDbUpdate();
+ $this->doWelcomeUpdates($view, $componentsWithUpdateFile);
+ echo $view->render();
+ }
+ exit;
+ }
+
+ private function doWelcomeUpdates($view, $componentsWithUpdateFile)
+ {
+ $view->new_piwik_version = Piwik_Version::VERSION;
+ $view->commandUpgradePiwik = "<br /><code>php " . Piwik_Common::getPathToPiwikRoot() . "/index.php -- \"module=CoreUpdater\" </code>";
+ $pluginNamesToUpdate = array();
+ $coreToUpdate = false;
+
+ // handle case of existing database with no tables
+ if (!Piwik::isInstalled()) {
+ $this->errorMessages[] = Piwik_Translate('CoreUpdater_EmptyDatabaseError', Piwik_Config::getInstance()->database['dbname']);
+ $this->coreError = true;
+ $currentVersion = 'N/A';
+ } else {
+ $this->errorMessages = array();
+ try {
+ $currentVersion = Piwik_GetOption('version_core');
+ } catch (Exception $e) {
+ $currentVersion = '<= 0.2.9';
+ }
+
+ foreach ($componentsWithUpdateFile as $name => $filenames) {
+ if ($name == 'core') {
+ $coreToUpdate = true;
+ } else {
+ $pluginNamesToUpdate[] = $name;
+ }
+ }
+ }
+
+ // check file integrity
+ $integrityInfo = Piwik::getFileIntegrityInformation();
+ if (isset($integrityInfo[1])) {
+ if ($integrityInfo[0] == false) {
+ $this->warningMessages[] = '<b>' . Piwik_Translate('General_FileIntegrityWarningExplanation') . '</b>';
+ }
+ $this->warningMessages = array_merge($this->warningMessages, array_slice($integrityInfo, 1));
+ }
+ Piwik::deleteAllCacheOnUpdate();
+
+ $view->coreError = $this->coreError;
+ $view->warningMessages = $this->warningMessages;
+ $view->errorMessages = $this->errorMessages;
+ $view->current_piwik_version = $currentVersion;
+ $view->pluginNamesToUpdate = $pluginNamesToUpdate;
+ $view->coreToUpdate = $coreToUpdate;
+ }
+
+ private function doExecuteUpdates($view, $updater, $componentsWithUpdateFile)
+ {
+ $this->loadAndExecuteUpdateFiles($updater, $componentsWithUpdateFile);
+
+ Piwik::deleteAllCacheOnUpdate();
+
+ $view->coreError = $this->coreError;
+ $view->warningMessages = $this->warningMessages;
+ $view->errorMessages = $this->errorMessages;
+ $view->deactivatedPlugins = $this->deactivatedPlugins;
+ }
+
+ private function loadAndExecuteUpdateFiles($updater, $componentsWithUpdateFile)
+ {
+ // if error in any core update, show message + help message + EXIT
+ // if errors in any plugins updates, show them on screen, disable plugins that errored + CONTINUE
+ // if warning in any core update or in any plugins update, show message + CONTINUE
+ // if no error or warning, success message + CONTINUE
+ foreach ($componentsWithUpdateFile as $name => $filenames) {
+ try {
+ $this->warningMessages = array_merge($this->warningMessages, $updater->update($name));
+ } catch (Piwik_Updater_UpdateErrorException $e) {
+ $this->errorMessages[] = $e->getMessage();
+ if ($name == 'core') {
+ $this->coreError = true;
+ break;
+ } else {
+ Piwik_PluginsManager::getInstance()->deactivatePlugin($name);
+ $this->deactivatedPlugins[] = $name;
+ }
+ }
+ }
+ }
}
diff --git a/plugins/CoreUpdater/CoreUpdater.php b/plugins/CoreUpdater/CoreUpdater.php
index 60168b83e7..30a28f76a6 100644
--- a/plugins/CoreUpdater/CoreUpdater.php
+++ b/plugins/CoreUpdater/CoreUpdater.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_CoreUpdater
*/
@@ -15,75 +15,69 @@
*/
class Piwik_CoreUpdater extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('CoreUpdater_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('CoreUpdater_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'FrontController.dispatchCoreAndPluginUpdatesScreen' => 'dispatch',
+ 'FrontController.checkForUpdates' => 'updateCheck',
+ );
+ return $hooks;
+ }
+
+ public static function getComponentUpdates($updater)
+ {
+ $updater->addComponentToCheck('core', Piwik_Version::VERSION);
+ $plugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins();
+ foreach ($plugins as $pluginName => $plugin) {
+ $updater->addComponentToCheck($pluginName, $plugin->getVersion());
+ }
+
+ $componentsWithUpdateFile = $updater->getComponentsWithUpdateFile();
+ if (count($componentsWithUpdateFile) == 0 && !$updater->hasNewVersion('core')) {
+ return null;
+ }
- function getListHooksRegistered()
- {
- $hooks = array(
- 'FrontController.dispatchCoreAndPluginUpdatesScreen' => 'dispatch',
- 'FrontController.checkForUpdates' => 'updateCheck',
- );
- return $hooks;
- }
+ return $componentsWithUpdateFile;
+ }
- public static function getComponentUpdates($updater)
- {
- $updater->addComponentToCheck('core', Piwik_Version::VERSION);
- $plugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins();
- foreach($plugins as $pluginName => $plugin)
- {
- $updater->addComponentToCheck($pluginName, $plugin->getVersion());
- }
-
- $componentsWithUpdateFile = $updater->getComponentsWithUpdateFile();
- if(count($componentsWithUpdateFile) == 0 && !$updater->hasNewVersion('core'))
- {
- return null;
- }
+ function dispatch()
+ {
+ $module = Piwik_Common::getRequestVar('module', '', 'string');
+ $action = Piwik_Common::getRequestVar('action', '', 'string');
- return $componentsWithUpdateFile;
- }
-
- function dispatch()
- {
- $module = Piwik_Common::getRequestVar('module', '', 'string');
- $action = Piwik_Common::getRequestVar('action', '', 'string');
-
- $updater = new Piwik_Updater();
- $updater->addComponentToCheck('core', Piwik_Version::VERSION);
- $updates = $updater->getComponentsWithNewVersion();
- if(!empty($updates))
- {
- Piwik::deleteAllCacheOnUpdate();
- }
- if(self::getComponentUpdates($updater) !== null
- && $module != 'CoreUpdater'
- // Proxy module is used to redirect users to piwik.org, should still work when Piwik must be updated
- && $module != 'Proxy'
- && !($module == 'LanguagesManager'
- && $action == 'saveLanguage'))
- {
- if(Piwik_FrontController::shouldRethrowException())
- {
- throw new Exception("Piwik and/or some plugins have been upgraded to a new version. Please run the update process first. See documentation: http://piwik.org/docs/update/");
- }
- else
- {
- Piwik::redirectToModule('CoreUpdater');
- }
- }
- }
+ $updater = new Piwik_Updater();
+ $updater->addComponentToCheck('core', Piwik_Version::VERSION);
+ $updates = $updater->getComponentsWithNewVersion();
+ if (!empty($updates)) {
+ Piwik::deleteAllCacheOnUpdate();
+ }
+ if (self::getComponentUpdates($updater) !== null
+ && $module != 'CoreUpdater'
+ // Proxy module is used to redirect users to piwik.org, should still work when Piwik must be updated
+ && $module != 'Proxy'
+ && !($module == 'LanguagesManager'
+ && $action == 'saveLanguage')
+ ) {
+ if (Piwik_FrontController::shouldRethrowException()) {
+ throw new Exception("Piwik and/or some plugins have been upgraded to a new version. Please run the update process first. See documentation: http://piwik.org/docs/update/");
+ } else {
+ Piwik::redirectToModule('CoreUpdater');
+ }
+ }
+ }
- function updateCheck()
- {
- Piwik_UpdateCheck::check();
- }
+ function updateCheck()
+ {
+ Piwik_UpdateCheck::check();
+ }
}
diff --git a/plugins/CoreUpdater/templates/cli_update_database_done.tpl b/plugins/CoreUpdater/templates/cli_update_database_done.tpl
index 19b5554b3b..099960be4d 100644
--- a/plugins/CoreUpdater/templates/cli_update_database_done.tpl
+++ b/plugins/CoreUpdater/templates/cli_update_database_done.tpl
@@ -1,64 +1,64 @@
{textformat}
-{assign var='helpMessage' value='CoreUpdater_HelpMessageContent'|translate:'[':']':"\n\n* "|unescape}
+ {assign var='helpMessage' value='CoreUpdater_HelpMessageContent'|translate:'[':']':"\n\n* "|unescape}
-{if $coreError}
- [X] {'CoreUpdater_CriticalErrorDuringTheUpgradeProcess'|translate|unescape}
+ {if $coreError}
+ [X] {'CoreUpdater_CriticalErrorDuringTheUpgradeProcess'|translate|unescape}
- {foreach from=$errorMessages item=message}
- * {$message}
+ {foreach from=$errorMessages item=message}
+ * {$message}
- {/foreach}
+ {/foreach}
- {'CoreUpdater_HelpMessageIntroductionWhenError'|translate|unescape}
+ {'CoreUpdater_HelpMessageIntroductionWhenError'|translate|unescape}
- * {$helpMessage}
+ * {$helpMessage}
- {'CoreUpdater_ErrorDIYHelp'|translate}
+ {'CoreUpdater_ErrorDIYHelp'|translate}
- * {'CoreUpdater_ErrorDIYHelp_1'|translate}
+ * {'CoreUpdater_ErrorDIYHelp_1'|translate}
- * {'CoreUpdater_ErrorDIYHelp_2'|translate}
+ * {'CoreUpdater_ErrorDIYHelp_2'|translate}
- * {'CoreUpdater_ErrorDIYHelp_3'|translate}
+ * {'CoreUpdater_ErrorDIYHelp_3'|translate}
- * {'CoreUpdater_ErrorDIYHelp_4'|translate}
+ * {'CoreUpdater_ErrorDIYHelp_4'|translate}
- * {'CoreUpdater_ErrorDIYHelp_5'|translate}
+ * {'CoreUpdater_ErrorDIYHelp_5'|translate}
-{else}
- {if count($warningMessages) > 0}
- [!] {'CoreUpdater_WarningMessages'|translate|unescape}
+ {else}
+ {if count($warningMessages) > 0}
+ [!] {'CoreUpdater_WarningMessages'|translate|unescape}
- {foreach from=$warningMessages item=message}
- * {$message}
+ {foreach from=$warningMessages item=message}
+ * {$message}
- {/foreach}
- {/if}
-
- {if count($errorMessages) > 0}
- [X] {'CoreUpdater_ErrorDuringPluginsUpdates'|translate|unescape}
+ {/foreach}
+ {/if}
- {foreach from=$errorMessages item=message}
- * {$message}
+ {if count($errorMessages) > 0}
+ [X] {'CoreUpdater_ErrorDuringPluginsUpdates'|translate|unescape}
- {/foreach}
-
- {if isset($deactivatedPlugins) && count($deactivatedPlugins) > 0}
- {assign var=listOfDeactivatedPlugins value=$deactivatedPlugins|@implode:', '}
- [!] {'CoreUpdater_WeAutomaticallyDeactivatedTheFollowingPlugins'|translate:$listOfDeactivatedPlugins|unescape}
+ {foreach from=$errorMessages item=message}
+ * {$message}
- {/if}
- {/if}
- {if count($errorMessages) > 0 || count($warningMessages) > 0}
- {'CoreUpdater_HelpMessageIntroductionWhenWarning'|translate|unescape}
+ {/foreach}
- * {$helpMessage}
- {else}
- {'CoreUpdater_PiwikHasBeenSuccessfullyUpgraded'|translate|unescape}
+ {if isset($deactivatedPlugins) && count($deactivatedPlugins) > 0}
+ {assign var=listOfDeactivatedPlugins value=$deactivatedPlugins|@implode:', '}
+ [!] {'CoreUpdater_WeAutomaticallyDeactivatedTheFollowingPlugins'|translate:$listOfDeactivatedPlugins|unescape}
- {/if}
-{/if}
+ {/if}
+ {/if}
+ {if count($errorMessages) > 0 || count($warningMessages) > 0}
+ {'CoreUpdater_HelpMessageIntroductionWhenWarning'|translate|unescape}
+
+ * {$helpMessage}
+ {else}
+ {'CoreUpdater_PiwikHasBeenSuccessfullyUpgraded'|translate|unescape}
+
+ {/if}
+ {/if}
{/textformat}
diff --git a/plugins/CoreUpdater/templates/cli_update_welcome.tpl b/plugins/CoreUpdater/templates/cli_update_welcome.tpl
index 1e4afc0da2..b3802e024a 100644
--- a/plugins/CoreUpdater/templates/cli_update_welcome.tpl
+++ b/plugins/CoreUpdater/templates/cli_update_welcome.tpl
@@ -1,38 +1,38 @@
{assign var='helpMessage' value='CoreUpdater_HelpMessageContent'|translate:'[':']':"\n\n* "|unescape}
{textformat}
-*** {'CoreUpdater_UpdateTitle'|translate|unescape} ***
+ *** {'CoreUpdater_UpdateTitle'|translate|unescape} ***
-{if $coreError}
- [X] {'CoreUpdater_CriticalErrorDuringTheUpgradeProcess'|translate|unescape}
+ {if $coreError}
+ [X] {'CoreUpdater_CriticalErrorDuringTheUpgradeProcess'|translate|unescape}
- {foreach from=$errorMessages item=message}
- * {$message}
+ {foreach from=$errorMessages item=message}
+ * {$message}
- {/foreach}
+ {/foreach}
- {'CoreUpdater_HelpMessageIntroductionWhenError'|translate|unescape}
+ {'CoreUpdater_HelpMessageIntroductionWhenError'|translate|unescape}
- * {$helpMessage}
+ * {$helpMessage}
-{else}
- {if $coreToUpdate || count($pluginNamesToUpdate) > 0}
- {'CoreUpdater_DatabaseUpgradeRequired'|translate|unescape}
+ {else}
+ {if $coreToUpdate || count($pluginNamesToUpdate) > 0}
+ {'CoreUpdater_DatabaseUpgradeRequired'|translate|unescape}
- {'CoreUpdater_YourDatabaseIsOutOfDate'|translate|unescape}
+ {'CoreUpdater_YourDatabaseIsOutOfDate'|translate|unescape}
- {if $coreToUpdate}
- {'CoreUpdater_PiwikWillBeUpgradedFromVersionXToVersionY'|translate:$current_piwik_version:$new_piwik_version|unescape}
+ {if $coreToUpdate}
+ {'CoreUpdater_PiwikWillBeUpgradedFromVersionXToVersionY'|translate:$current_piwik_version:$new_piwik_version|unescape}
- {/if}
- {if count($pluginNamesToUpdate) > 0}
- {assign var=listOfPlugins value=$pluginNamesToUpdate|@implode:', '}
- {'CoreUpdater_TheFollowingPluginsWillBeUpgradedX'|translate:$listOfPlugins|unescape}
+ {/if}
+ {if count($pluginNamesToUpdate) > 0}
+ {assign var=listOfPlugins value=$pluginNamesToUpdate|@implode:', '}
+ {'CoreUpdater_TheFollowingPluginsWillBeUpgradedX'|translate:$listOfPlugins|unescape}
- {/if}
- {'CoreUpdater_TheUpgradeProcessMayTakeAWhilePleaseBePatient'|translate|unescape}
+ {/if}
+ {'CoreUpdater_TheUpgradeProcessMayTakeAWhilePleaseBePatient'|translate|unescape}
- {/if}
-{/if}
+ {/if}
+ {/if}
{/textformat}
diff --git a/plugins/CoreUpdater/templates/header.tpl b/plugins/CoreUpdater/templates/header.tpl
index 65670a7728..e0023e6389 100644
--- a/plugins/CoreUpdater/templates/header.tpl
+++ b/plugins/CoreUpdater/templates/header.tpl
@@ -1,42 +1,44 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
- <title>Piwik &rsaquo; {'CoreUpdater_UpdateTitle'|translate}</title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico" />
+ <title>Piwik &rsaquo; {'CoreUpdater_UpdateTitle'|translate}</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico"/>
- <link rel="stylesheet" type="text/css" href="themes/default/simple_structure.css" />
- <link rel="stylesheet" type="text/css" href="libs/jquery/themes/base/jquery-ui.css" />
- <link rel="stylesheet" type="text/css" href="themes/default/styles.css" />
- <link rel="stylesheet" type="text/css" href="plugins/CoreHome/templates/donate.css"></link>
- <link rel="stylesheet" type="text/css" href="plugins/CoreHome/templates/jquery.ui.autocomplete.css"></link>
-{literal}
-<style type="text/css">
-* {
- margin: 0;
- padding: 0;
-}
-.topBarElem {
- font-family:arial,sans-serif !important;
- font-size:13px;
- line-height:1.33;
-}
+ <link rel="stylesheet" type="text/css" href="themes/default/simple_structure.css"/>
+ <link rel="stylesheet" type="text/css" href="libs/jquery/themes/base/jquery-ui.css"/>
+ <link rel="stylesheet" type="text/css" href="themes/default/styles.css"/>
+ <link rel="stylesheet" type="text/css" href="plugins/CoreHome/templates/donate.css"></link>
+ <link rel="stylesheet" type="text/css" href="plugins/CoreHome/templates/jquery.ui.autocomplete.css"></link>
+ {literal}
+ <style type="text/css">
+ * {
+ margin: 0;
+ padding: 0;
+ }
-#donate-form-container {
- margin: 0 0 2em 2em;
-}
-</style>
-{/literal}
+ .topBarElem {
+ font-family: arial, sans-serif !important;
+ font-size: 13px;
+ line-height: 1.33;
+ }
- <script type="text/javascript" src="libs/jquery/jquery.js"></script>
- <script type="text/javascript" src="libs/jquery/jquery-ui.js"></script>
- <script type="text/javascript" src="plugins/CoreHome/templates/donate.js"></script>
-{if 'General_LayoutDirection'|translate =='rtl'}
-<link rel="stylesheet" type="text/css" href="themes/default/rtl.css" />
-{/if}
-{loadJavascriptTranslations plugins='CoreHome'}
+ #donate-form-container {
+ margin: 0 0 2em 2em;
+ }
+ </style>
+ {/literal}
+
+ <script type="text/javascript" src="libs/jquery/jquery.js"></script>
+ <script type="text/javascript" src="libs/jquery/jquery-ui.js"></script>
+ <script type="text/javascript" src="plugins/CoreHome/templates/donate.js"></script>
+ {if 'General_LayoutDirection'|translate =='rtl'}
+ <link rel="stylesheet" type="text/css" href="themes/default/rtl.css"/>
+ {/if}
+ {loadJavascriptTranslations plugins='CoreHome'}
</head>
<body id="simple">
<div id="contentsimple">
- <div id="title"><img title='Piwik' alt="Piwik" src='themes/default/images/logo-header.png' style='margin-left:10px' /><span id="subh1"> # {'General_OpenSourceWebAnalytics'|translate}</span></div>
+ <div id="title"><img title='Piwik' alt="Piwik" src='themes/default/images/logo-header.png' style='margin-left:10px'/><span
+ id="subh1"> # {'General_OpenSourceWebAnalytics'|translate}</span></div>
diff --git a/plugins/CoreUpdater/templates/update_database_done.tpl b/plugins/CoreUpdater/templates/update_database_done.tpl
index 649321c57f..0ab5d72517 100644
--- a/plugins/CoreUpdater/templates/update_database_done.tpl
+++ b/plugins/CoreUpdater/templates/update_database_done.tpl
@@ -2,64 +2,75 @@
{assign var='helpMessage' value='CoreUpdater_HelpMessageContent'|translate:'<a target="_blank" href="?module=Proxy&action=redirect&url=http://piwik.org/faq/">':'</a>':'</li><li>'}
{if $coreError}
- <br /><br />
- <div class="error">
- <img src="themes/default/images/error_medium.png" /> {'CoreUpdater_CriticalErrorDuringTheUpgradeProcess'|translate}
- {foreach from=$errorMessages item=message}
- <pre>{$message}</pre><br />
- {/foreach}
- </div>
- <br />
- <p>{'CoreUpdater_HelpMessageIntroductionWhenError'|translate}
- <ul><li>{$helpMessage}</li></ul></p>
-
- <p>{'CoreUpdater_ErrorDIYHelp'|translate}
- <ul><li>{'CoreUpdater_ErrorDIYHelp_1'|translate}</li>
- <li>{'CoreUpdater_ErrorDIYHelp_2'|translate}</li>
- <li>{'CoreUpdater_ErrorDIYHelp_3'|translate}</li>
- <li>{'CoreUpdater_ErrorDIYHelp_4'|translate}</li>
- <li>{'CoreUpdater_ErrorDIYHelp_5'|translate}</li></ul></p>
+ <br/>
+ <br/>
+ <div class="error">
+ <img src="themes/default/images/error_medium.png"/> {'CoreUpdater_CriticalErrorDuringTheUpgradeProcess'|translate}
+ {foreach from=$errorMessages item=message}
+ <pre>{$message}</pre>
+ <br/>
+ {/foreach}
+ </div>
+ <br/>
+ <p>{'CoreUpdater_HelpMessageIntroductionWhenError'|translate}
+ <ul>
+ <li>{$helpMessage}</li>
+ </ul>
+ </p>
+ <p>{'CoreUpdater_ErrorDIYHelp'|translate}
+ <ul>
+ <li>{'CoreUpdater_ErrorDIYHelp_1'|translate}</li>
+ <li>{'CoreUpdater_ErrorDIYHelp_2'|translate}</li>
+ <li>{'CoreUpdater_ErrorDIYHelp_3'|translate}</li>
+ <li>{'CoreUpdater_ErrorDIYHelp_4'|translate}</li>
+ <li>{'CoreUpdater_ErrorDIYHelp_5'|translate}</li>
+ </ul>
+ </p>
{else}
-
- {if count($warningMessages) > 0}
- <div class="warning">
- <p><img src="themes/default/images/warning_medium.png" /> {'CoreUpdater_WarningMessages'|translate}</p>
- {foreach from=$warningMessages item=message}
- <pre>{$message}</pre><br />
- {/foreach}
- </div>
- {/if}
-
- {if count($errorMessages) > 0}
- <div class="warning">
- <p><img src="themes/default/images/error_medium.png" /> {'CoreUpdater_ErrorDuringPluginsUpdates'|translate}</p>
- {foreach from=$errorMessages item=message}
- <pre>{$message}</pre><br />
- {/foreach}
-
- {if isset($deactivatedPlugins) && count($deactivatedPlugins) > 0}
- {assign var=listOfDeactivatedPlugins value=$deactivatedPlugins|@implode:', '}
- <p style="color:red"><img src="themes/default/images/error_medium.png" /> {'CoreUpdater_WeAutomaticallyDeactivatedTheFollowingPlugins'|translate:$listOfDeactivatedPlugins}</p>
- {/if}
- </div>
- {/if}
-
- {if count($errorMessages) > 0 || count($warningMessages) > 0}
- <br />
- <p>{'CoreUpdater_HelpMessageIntroductionWhenWarning'|translate}
- <ul><li>{$helpMessage}</li></ul>
- </p>
- {else}
- <p class="success">{'CoreUpdater_PiwikHasBeenSuccessfullyUpgraded'|translate}</p>
-
- <div id="donate-form-container">
- {include file="CoreHome/templates/donate.tpl"}
- </div>
- {/if}
- <form action="index.php">
- <input type="submit" class="submit" value="{'CoreUpdater_ContinueToPiwik'|translate}" />
- </form>
+ {if count($warningMessages) > 0}
+ <div class="warning">
+ <p><img src="themes/default/images/warning_medium.png"/> {'CoreUpdater_WarningMessages'|translate}</p>
+ {foreach from=$warningMessages item=message}
+ <pre>{$message}</pre>
+ <br/>
+ {/foreach}
+ </div>
+ {/if}
+
+ {if count($errorMessages) > 0}
+ <div class="warning">
+ <p><img src="themes/default/images/error_medium.png"/> {'CoreUpdater_ErrorDuringPluginsUpdates'|translate}</p>
+ {foreach from=$errorMessages item=message}
+ <pre>{$message}</pre>
+ <br/>
+ {/foreach}
+
+ {if isset($deactivatedPlugins) && count($deactivatedPlugins) > 0}
+ {assign var=listOfDeactivatedPlugins value=$deactivatedPlugins|@implode:', '}
+ <p style="color:red"><img
+ src="themes/default/images/error_medium.png"/> {'CoreUpdater_WeAutomaticallyDeactivatedTheFollowingPlugins'|translate:$listOfDeactivatedPlugins}
+ </p>
+ {/if}
+ </div>
+ {/if}
+
+ {if count($errorMessages) > 0 || count($warningMessages) > 0}
+ <br/>
+ <p>{'CoreUpdater_HelpMessageIntroductionWhenWarning'|translate}
+ <ul>
+ <li>{$helpMessage}</li>
+ </ul>
+ </p>
+ {else}
+ <p class="success">{'CoreUpdater_PiwikHasBeenSuccessfullyUpgraded'|translate}</p>
+ <div id="donate-form-container">
+ {include file="CoreHome/templates/donate.tpl"}
+ </div>
+ {/if}
+ <form action="index.php">
+ <input type="submit" class="submit" value="{'CoreUpdater_ContinueToPiwik'|translate}"/>
+ </form>
{/if}
{include file="CoreUpdater/templates/footer.tpl"}
diff --git a/plugins/CoreUpdater/templates/update_new_version_available.tpl b/plugins/CoreUpdater/templates/update_new_version_available.tpl
index d8be6033a6..15541aa48d 100644
--- a/plugins/CoreUpdater/templates/update_new_version_available.tpl
+++ b/plugins/CoreUpdater/templates/update_new_version_available.tpl
@@ -4,25 +4,26 @@
<p><b>{'CoreUpdater_ThereIsNewVersionAvailableForUpdate'|translate}</b></p>
{if $can_auto_update}
- <p>{'CoreUpdater_YouCanUpgradeAutomaticallyOrDownloadPackage'|translate:$piwik_new_version}</p>
+ <p>{'CoreUpdater_YouCanUpgradeAutomaticallyOrDownloadPackage'|translate:$piwik_new_version}</p>
{else}
- <p>{'Installation_SystemCheckAutoUpdateHelp'|translate}</p>
- <p>{'CoreUpdater_YouMustDownloadPackageOrFixPermissions'|translate:$piwik_new_version}
- {$makeWritableCommands}
- </p>
+ <p>{'Installation_SystemCheckAutoUpdateHelp'|translate}</p>
+ <p>{'CoreUpdater_YouMustDownloadPackageOrFixPermissions'|translate:$piwik_new_version}
+ {$makeWritableCommands}
+ </p>
{/if}
{if $can_auto_update}
- <form action="index.php">
- <input type="hidden" name="module" value="CoreUpdater" />
- <input type="hidden" name="action" value="oneClickUpdate" />
- <input type="submit" class="submit" value="{'CoreUpdater_UpdateAutomatically'|translate}" />
+<form action="index.php">
+ <input type="hidden" name="module" value="CoreUpdater"/>
+ <input type="hidden" name="action" value="oneClickUpdate"/>
+ <input type="submit" class="submit" value="{'CoreUpdater_UpdateAutomatically'|translate}"/>
+ {/if}
+ <a style="margin-left:50px" class="submit button"
+ href="{$piwik_latest_version_url}?cb={$piwik_new_version}">{'CoreUpdater_DownloadX'|translate:$piwik_new_version}</a><br/>
+ {if $can_auto_update}
+</form>
{/if}
- <a style="margin-left:50px" class="submit button" href="{$piwik_latest_version_url}?cb={$piwik_new_version}">{'CoreUpdater_DownloadX'|translate:$piwik_new_version}</a><br />
-{if $can_auto_update}
- </form>
-{/if}
-<br />
+<br/>
<a href='index.php'>&laquo; {'General_BackToPiwik'|translate}</a>
{include file="CoreUpdater/templates/footer.tpl"}
diff --git a/plugins/CoreUpdater/templates/update_one_click_results.tpl b/plugins/CoreUpdater/templates/update_one_click_results.tpl
index db463db066..7ba1bf1627 100644
--- a/plugins/CoreUpdater/templates/update_one_click_results.tpl
+++ b/plugins/CoreUpdater/templates/update_one_click_results.tpl
@@ -2,18 +2,23 @@
<br/>
{foreach from=$feedbackMessages item=message}
- <p>{$message|escape:'html'}</p>
+ <p>{$message|escape:'html'}</p>
{/foreach}
{if $coreError}
- <br /><br />
- <div class="error"><img src="themes/default/images/error_medium.png" /> {$coreError|escape:'html'}</div>
- <br /><br />
- <div class="warning"><img src="themes/default/images/warning_medium.png" /> {'CoreUpdater_UpdateHasBeenCancelledExplanation'|translate:"<br /><br />":"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/update/'>":"</a>"}</div>
- <br /><br />
+ <br/>
+ <br/>
+ <div class="error"><img src="themes/default/images/error_medium.png"/> {$coreError|escape:'html'}</div>
+ <br/>
+ <br/>
+ <div class="warning"><img
+ src="themes/default/images/warning_medium.png"/> {'CoreUpdater_UpdateHasBeenCancelledExplanation'|translate:"<br /><br />":"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/update/'>":"</a>"}
+ </div>
+ <br/>
+ <br/>
{/if}
<form action="index.php">
-<input type="submit" class="submit" value="{'CoreUpdater_ContinueToPiwik'|translate}" />
+ <input type="submit" class="submit" value="{'CoreUpdater_ContinueToPiwik'|translate}"/>
</form>
{include file="CoreUpdater/templates/footer.tpl"}
diff --git a/plugins/CoreUpdater/templates/update_welcome.tpl b/plugins/CoreUpdater/templates/update_welcome.tpl
index 434b8ad99c..aeaa73c627 100644
--- a/plugins/CoreUpdater/templates/update_welcome.tpl
+++ b/plugins/CoreUpdater/templates/update_welcome.tpl
@@ -3,116 +3,124 @@
{assign var='helpMessage' value='CoreUpdater_HelpMessageContent'|translate:'<a target="_blank" href="?module=Proxy&action=redirect&url=http://piwik.org/faq/">':'</a>':'</li><li>'}
{if $coreError}
- <br /><br />
- <div class="error">
- <img src="themes/default/images/error_medium.png" /> {'CoreUpdater_CriticalErrorDuringTheUpgradeProcess'|translate}
- {foreach from=$errorMessages item=message}
- <pre>{$message}</pre>
- {/foreach}
- </div>
- <br />
- <p>{'CoreUpdater_HelpMessageIntroductionWhenError'|translate}
- <ul><li>{$helpMessage}</li></ul></p>
+ <br/>
+ <br/>
+ <div class="error">
+ <img src="themes/default/images/error_medium.png"/> {'CoreUpdater_CriticalErrorDuringTheUpgradeProcess'|translate}
+ {foreach from=$errorMessages item=message}
+ <pre>{$message}</pre>
+ {/foreach}
+ </div>
+ <br/>
+ <p>{'CoreUpdater_HelpMessageIntroductionWhenError'|translate}
+ <ul>
+ <li>{$helpMessage}</li>
+ </ul>
+ </p>
{else}
- {if $coreToUpdate || count($pluginNamesToUpdate) > 0}
- <p style='font-size:110%;padding-top:1em;'><b id='titleUpdate'>{'CoreUpdater_DatabaseUpgradeRequired'|translate}</b></p>
- <p>{'CoreUpdater_YourDatabaseIsOutOfDate'|translate}</p>
+ {if $coreToUpdate || count($pluginNamesToUpdate) > 0}
+ <p style='font-size:110%;padding-top:1em;'><b id='titleUpdate'>{'CoreUpdater_DatabaseUpgradeRequired'|translate}</b></p>
+ <p>{'CoreUpdater_YourDatabaseIsOutOfDate'|translate}</p>
+ {if $coreToUpdate}
+ <p>{'CoreUpdater_PiwikWillBeUpgradedFromVersionXToVersionY'|translate:$current_piwik_version:$new_piwik_version}</p>
+ {/if}
- {if $coreToUpdate}
- <p>{'CoreUpdater_PiwikWillBeUpgradedFromVersionXToVersionY'|translate:$current_piwik_version:$new_piwik_version}</p>
- {/if}
+ {if count($pluginNamesToUpdate) > 0}
+ {assign var=listOfPlugins value=$pluginNamesToUpdate|@implode:', '}
+ <p>{'CoreUpdater_TheFollowingPluginsWillBeUpgradedX'|translate:$listOfPlugins}</p>
+ {/if}
+ <h3 id='titleUpdate'>{'CoreUpdater_NoteForLargePiwikInstances'|translate}</h3>
+ {if $isMajor}
+ <p class="warning normalFontSize">
+ {'CoreUpdater_MajorUpdateWarning1'|translate}<br/>
+ {'CoreUpdater_MajorUpdateWarning2'|translate}
+ </p>
+ {/if}
+ <ul>
+ <li>{'CoreUpdater_TheUpgradeProcessMayFailExecuteCommand'|translate:$commandUpgradePiwik}</li>
+ <li>It is also recommended for high traffic Piwik servers to <a target='_blank'
+ href='?module=Proxy&action=redirect&url={"http://piwik.org/faq/how-to/#faq_111"|escape:"url"}'>momentarily
+ disable visitor Tracking and put the Piwik User Interface in maintenance mode</a>.
+ </li>
+ <li>{'CoreUpdater_YouCouldManuallyExecuteSqlQueries'|translate}<br/>
+ <a href='#' id='showSql' style='margin-left:20px'>› {'CoreUpdater_ClickHereToViewSqlQueries'|translate}</a>
- {if count($pluginNamesToUpdate) > 0}
- {assign var=listOfPlugins value=$pluginNamesToUpdate|@implode:', '}
- <p>{'CoreUpdater_TheFollowingPluginsWillBeUpgradedX'|translate:$listOfPlugins}</p>
- {/if}
+ <div id='sqlQueries' style='display:none'>
+ <br/>
+ <code>
+ # {'CoreUpdater_NoteItIsExpectedThatQueriesFail'|translate}<br/><br/>
+ {foreach from=$queries item=query}&nbsp;&nbsp;&nbsp;{$query}
+ <br/>
+ {/foreach}
+ </code>
+ </div>
+ </li>
+ </ul>
+ <br/>
+ <br/>
+ <h4 id='titleUpdate'>{'CoreUpdater_ReadyToGo'|translate}</h4>
+ <p>{'CoreUpdater_TheUpgradeProcessMayTakeAWhilePleaseBePatient'|translate}</p>
+ {/if}
- <h3 id='titleUpdate'>{'CoreUpdater_NoteForLargePiwikInstances'|translate}</h3>
- {if $isMajor}
- <p class="warning normalFontSize">
- {'CoreUpdater_MajorUpdateWarning1'|translate}<br />
- {'CoreUpdater_MajorUpdateWarning2'|translate}
- </p>
- {/if}
- <ul>
- <li>{'CoreUpdater_TheUpgradeProcessMayFailExecuteCommand'|translate:$commandUpgradePiwik}</li>
- <li>It is also recommended for high traffic Piwik servers to <a target='_blank' href='?module=Proxy&action=redirect&url={"http://piwik.org/faq/how-to/#faq_111"|escape:"url"}'>momentarily disable visitor Tracking and put the Piwik User Interface in maintenance mode</a>.</li>
- <li>{'CoreUpdater_YouCouldManuallyExecuteSqlQueries'|translate}<br />
- <a href='#' id='showSql' style='margin-left:20px'>› {'CoreUpdater_ClickHereToViewSqlQueries'|translate}</a>
- <div id='sqlQueries' style='display:none'>
- <br />
- <code>
- # {'CoreUpdater_NoteItIsExpectedThatQueriesFail'|translate}<br /><br />
- {foreach from=$queries item=query}&nbsp;&nbsp;&nbsp;{$query}<br />
- {/foreach}
- </code>
- </div>
- </li>
- </ul>
- <br /><br />
- <h4 id='titleUpdate'>{'CoreUpdater_ReadyToGo'|translate}</h4>
- <p>{'CoreUpdater_TheUpgradeProcessMayTakeAWhilePleaseBePatient'|translate}</p>
- {/if}
+ {if count($warningMessages) > 0}
+ <p><i>{$warningMessages[0]}</i>
+ {if count($warningMessages) > 1}
+ <button id="more-results" class="ui-button ui-state-default ui-corner-all">{'General_Details'|translate}</button>
+ {/if}
+ </p>
+ {/if}
- {if count($warningMessages) > 0}
- <p><i>{$warningMessages[0]}</i>
- {if count($warningMessages) > 1}
- <button id="more-results" class="ui-button ui-state-default ui-corner-all">{'General_Details'|translate}</button>
- {/if}
- </p>
- {/if}
-
- {if $coreToUpdate || count($pluginNamesToUpdate) > 0}
- <br />
- <form action="index.php" id="upgradeCorePluginsForm">
- <input type="hidden" name="updateCorePlugins" value="1" />
- {if count($queries) == 1}
- <input type="submit" class="submit" value="{'CoreUpdater_ContinueToPiwik'|translate}" />
- {else}
- <input type="submit" class="submit" value="{'CoreUpdater_UpgradePiwik'|translate}" />
- {/if}
- </form>
- {else}
- {if count($warningMessages) == 0}
- <p class="success">{'CoreUpdater_PiwikHasBeenSuccessfullyUpgraded'|translate}</p>
- {/if}
-
- <br />
- <form action="index.php">
- <input type="submit" class="submit" value="{'CoreUpdater_ContinueToPiwik'|translate}" />
- </form>
- {/if}
+ {if $coreToUpdate || count($pluginNamesToUpdate) > 0}
+ <br/>
+ <form action="index.php" id="upgradeCorePluginsForm">
+ <input type="hidden" name="updateCorePlugins" value="1"/>
+ {if count($queries) == 1}
+ <input type="submit" class="submit" value="{'CoreUpdater_ContinueToPiwik'|translate}"/>
+ {else}
+ <input type="submit" class="submit" value="{'CoreUpdater_UpgradePiwik'|translate}"/>
+ {/if}
+ </form>
+ {else}
+ {if count($warningMessages) == 0}
+ <p class="success">{'CoreUpdater_PiwikHasBeenSuccessfullyUpgraded'|translate}</p>
+ {/if}
+ <br/>
+ <form action="index.php">
+ <input type="submit" class="submit" value="{'CoreUpdater_ContinueToPiwik'|translate}"/>
+ </form>
+ {/if}
{/if}
{include file="Installation/templates/integrityDetails.tpl"}
{literal}
-<style type="text/css">
-code {
- background-color:#F0F7FF;
- border: 1px dashed #00008B;
- border-left: 5px solid;
- direction:ltr;
- display:block;
- margin:2px 2px 20px;
- padding:4px;
- text-align:left;
-}
-li {
- margin-top:10px;
- margin-left:30px;
-}
-</style>
-<script type="text/javascript">
-$(document).ready(function() {
- $('#showSql').click( function () {
- $('#sqlQueries').toggle();
- });
- $('#upgradeCorePluginsForm').submit(function(){
- $('input[type=submit]', this).prop('disabled', 'disabled');
- });
-});
-</script>
+ <style type="text/css">
+ code {
+ background-color: #F0F7FF;
+ border: 1px dashed #00008B;
+ border-left: 5px solid;
+ direction: ltr;
+ display: block;
+ margin: 2px 2px 20px;
+ padding: 4px;
+ text-align: left;
+ }
+
+ li {
+ margin-top: 10px;
+ margin-left: 30px;
+ }
+ </style>
+ <script type="text/javascript">
+ $(document).ready(function () {
+ $('#showSql').click(function () {
+ $('#sqlQueries').toggle();
+ });
+ $('#upgradeCorePluginsForm').submit(function () {
+ $('input[type=submit]', this).prop('disabled', 'disabled');
+ });
+ });
+ </script>
{/literal}
{include file="CoreUpdater/templates/footer.tpl"}
diff --git a/plugins/CustomVariables/API.php b/plugins/CustomVariables/API.php
index 1a340e4964..3a47c837c5 100644
--- a/plugins/CustomVariables/API.php
+++ b/plugins/CustomVariables/API.php
@@ -1,111 +1,105 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_CustomVariables
*/
/**
* The Custom Variables API lets you access reports for your <a href='http://piwik.org/docs/custom-variables/' target='_blank'>Custom Variables</a> names and values.
- *
+ *
* @package Piwik_CustomVariables
*/
-class Piwik_CustomVariables_API
+class Piwik_CustomVariables_API
{
- static private $instance = null;
-
- /**
- * @return Piwik_CustomVariables_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
+ static private $instance = null;
/**
- * @param int $idSite
- * @param string $period
+ * @return Piwik_CustomVariables_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * @param int $idSite
+ * @param string $period
* @param Piwik_Date $date
- * @param string $segment
- * @param bool $expanded
- * @param int $idSubtable
+ * @param string $segment
+ * @param bool $expanded
+ * @param int $idSubtable
*
* @return Piwik_DataTable|Piwik_DataTable_Array
*/
- protected function getDataTable($idSite, $period, $date, $segment, $expanded, $idSubtable)
- {
- $dataTable = Piwik_Archive::getDataTableFromArchive('CustomVariables_valueByName', $idSite, $period, $date, $segment, $expanded, $idSubtable);
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
- $dataTable->queueFilter('ReplaceColumnNames');
- return $dataTable;
- }
+ protected function getDataTable($idSite, $period, $date, $segment, $expanded, $idSubtable)
+ {
+ $dataTable = Piwik_Archive::getDataTableFromArchive('CustomVariables_valueByName', $idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
+ $dataTable->queueFilter('ReplaceColumnNames');
+ return $dataTable;
+ }
/**
- * @param int $idSite
- * @param string $period
- * @param Piwik_Date $date
- * @param string|bool $segment
- * @param bool $expanded
- * @param bool $_leavePiwikCoreVariables
+ * @param int $idSite
+ * @param string $period
+ * @param Piwik_Date $date
+ * @param string|bool $segment
+ * @param bool $expanded
+ * @param bool $_leavePiwikCoreVariables
*
* @return Piwik_DataTable|Piwik_DataTable_Array
*/
- public function getCustomVariables($idSite, $period, $date, $segment = false, $expanded = false, $_leavePiwikCoreVariables = false)
- {
- $dataTable = $this->getDataTable($idSite, $period, $date, $segment, $expanded, $idSubtable = null);
+ public function getCustomVariables($idSite, $period, $date, $segment = false, $expanded = false, $_leavePiwikCoreVariables = false)
+ {
+ $dataTable = $this->getDataTable($idSite, $period, $date, $segment, $expanded, $idSubtable = null);
- if($dataTable instanceof Piwik_DataTable
- && !$_leavePiwikCoreVariables)
- {
- $mapping = array('_pks', '_pkn', '_pkc', '_pkp', Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT, Piwik_Tracker_Action::CVAR_KEY_SEARCH_CATEGORY );
- foreach($mapping as $name)
- {
- $row = $dataTable->getRowFromLabel($name);
- if($row)
- {
- $dataTable->deleteRow($dataTable->getRowIdFromLabel($name));
- }
- }
- }
- return $dataTable;
- }
+ if ($dataTable instanceof Piwik_DataTable
+ && !$_leavePiwikCoreVariables
+ ) {
+ $mapping = array('_pks', '_pkn', '_pkc', '_pkp', Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT, Piwik_Tracker_Action::CVAR_KEY_SEARCH_CATEGORY);
+ foreach ($mapping as $name) {
+ $row = $dataTable->getRowFromLabel($name);
+ if ($row) {
+ $dataTable->deleteRow($dataTable->getRowIdFromLabel($name));
+ }
+ }
+ }
+ return $dataTable;
+ }
/**
- * @param int $idSite
- * @param string $period
- * @param Piwik_Date $date
- * @param int $idSubtable
- * @param string|bool $segment
- * @param bool $_leavePriceViewedColumn
+ * @param int $idSite
+ * @param string $period
+ * @param Piwik_Date $date
+ * @param int $idSubtable
+ * @param string|bool $segment
+ * @param bool $_leavePriceViewedColumn
*
* @return Piwik_DataTable|Piwik_DataTable_Array
*/
- public function getCustomVariablesValuesFromNameId($idSite, $period, $date, $idSubtable, $segment = false, $_leavePriceViewedColumn = false)
- {
- $dataTable = $this->getDataTable($idSite, $period, $date, $segment, $expanded = false, $idSubtable);
+ public function getCustomVariablesValuesFromNameId($idSite, $period, $date, $idSubtable, $segment = false, $_leavePriceViewedColumn = false)
+ {
+ $dataTable = $this->getDataTable($idSite, $period, $date, $segment, $expanded = false, $idSubtable);
- if(!$_leavePriceViewedColumn)
- {
- $dataTable->deleteColumn('price_viewed');
- }
- else
- {
- // Hack Ecommerce product price tracking to display correctly
- $dataTable->renameColumn('price_viewed', 'price');
- }
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', create_function('$label', '
+ if (!$_leavePriceViewedColumn) {
+ $dataTable->deleteColumn('price_viewed');
+ } else {
+ // Hack Ecommerce product price tracking to display correctly
+ $dataTable->renameColumn('price_viewed', 'price');
+ }
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', create_function('$label', '
return $label == Piwik_CustomVariables::LABEL_CUSTOM_VALUE_NOT_DEFINED
- ? "'. Piwik_Translate( 'General_NotDefined', Piwik_Translate('CustomVariables_ColumnCustomVariableValue')) .'"
+ ? "' . Piwik_Translate('General_NotDefined', Piwik_Translate('CustomVariables_ColumnCustomVariableValue')) . '"
: $label;')));
- return $dataTable;
- }
+ return $dataTable;
+ }
}
diff --git a/plugins/CustomVariables/Controller.php b/plugins/CustomVariables/Controller.php
index eca9bc2b62..ea00259d3e 100644
--- a/plugins/CustomVariables/Controller.php
+++ b/plugins/CustomVariables/Controller.php
@@ -15,44 +15,44 @@
*/
class Piwik_CustomVariables_Controller extends Piwik_Controller
{
-
- function index($fetch = false)
- {
- return Piwik_View::singleReport(
- Piwik_Translate('CustomVariables_CustomVariables'),
- $this->getCustomVariables(true), $fetch);
- }
-
- function getCustomVariables($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__, "CustomVariables.getCustomVariables", "getCustomVariablesValuesFromNameId" );
-
- $this->setPeriodVariablesView($view);
- $view->enableShowGoals();
-
- $view->setColumnsToDisplay( array('label','nb_visits', 'nb_actions') );
- $view->setColumnTranslation('label', Piwik_Translate('CustomVariables_ColumnCustomVariableName'));
- $view->setSortedColumn( 'nb_visits' );
- $view->setLimit( 10 );
- $view->setFooterMessage( Piwik_Translate('CustomVariables_TrackingHelp', array('<a target="_blank" href="http://piwik.org/docs/custom-variables/">', '</a>')) );
- $this->setMetricsVariablesView($view);
- return $this->renderView($view, $fetch);
- }
-
- function getCustomVariablesValuesFromNameId( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__, 'CustomVariables.getCustomVariablesValuesFromNameId' );
-
- $view->disableSearchBox();
- $view->enableShowGoals();
- $view->disableExcludeLowPopulation();
- $view->setColumnsToDisplay( array('label','nb_visits', 'nb_actions') );
- $view->setColumnTranslation('label', Piwik_Translate('CustomVariables_ColumnCustomVariableValue'));
-
- return $this->renderView($view, $fetch);
- }
-
+
+ function index($fetch = false)
+ {
+ return Piwik_View::singleReport(
+ Piwik_Translate('CustomVariables_CustomVariables'),
+ $this->getCustomVariables(true), $fetch);
+ }
+
+ function getCustomVariables($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, "CustomVariables.getCustomVariables", "getCustomVariablesValuesFromNameId");
+
+ $this->setPeriodVariablesView($view);
+ $view->enableShowGoals();
+
+ $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_actions'));
+ $view->setColumnTranslation('label', Piwik_Translate('CustomVariables_ColumnCustomVariableName'));
+ $view->setSortedColumn('nb_visits');
+ $view->setLimit(10);
+ $view->setFooterMessage(Piwik_Translate('CustomVariables_TrackingHelp', array('<a target="_blank" href="http://piwik.org/docs/custom-variables/">', '</a>')));
+ $this->setMetricsVariablesView($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ function getCustomVariablesValuesFromNameId($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'CustomVariables.getCustomVariablesValuesFromNameId');
+
+ $view->disableSearchBox();
+ $view->enableShowGoals();
+ $view->disableExcludeLowPopulation();
+ $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_actions'));
+ $view->setColumnTranslation('label', Piwik_Translate('CustomVariables_ColumnCustomVariableValue'));
+
+ return $this->renderView($view, $fetch);
+ }
+
}
diff --git a/plugins/CustomVariables/CustomVariables.php b/plugins/CustomVariables/CustomVariables.php
index 3bf3d184c1..d76adab5ea 100644
--- a/plugins/CustomVariables/CustomVariables.php
+++ b/plugins/CustomVariables/CustomVariables.php
@@ -14,324 +14,315 @@
*/
class Piwik_CustomVariables extends Piwik_Plugin
{
- public $archiveProcessing;
- protected $columnToSortByBeforeTruncation;
- protected $maximumRowsInDataTableLevelZero;
- protected $maximumRowsInSubDataTable;
-
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('CustomVariables_PluginDescription')
- . ' <br/>Required to use <a href="http://piwik.org/docs/ecommerce-analytics/">Ecommerce Analytics</a> feature!',
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
-
- return $info;
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenus',
- 'Goals.getReportsWithGoalMetrics' => 'getReportsWithGoalMetrics',
- 'API.getReportMetadata' => 'getReportMetadata',
- 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
- );
- return $hooks;
- }
-
- function addWidgets()
- {
- Piwik_AddWidget( 'General_Visitors', 'CustomVariables_CustomVariables', 'CustomVariables', 'getCustomVariables');
- }
-
- function addMenus()
- {
- Piwik_AddMenu('General_Visitors', 'CustomVariables_CustomVariables', array('module' => 'CustomVariables', 'action' => 'index'), $display = true, $order = 50);
- }
-
- /**
- * Returns metadata for available reports
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $reports = &$notification->getNotificationObject();
-
- $documentation = Piwik_Translate('CustomVariables_CustomVariablesReportDocumentation',
- array('<br />', '<a href="http://piwik.org/docs/custom-variables/" target="_blank">', '</a>'));
-
- $reports[] = array( 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('CustomVariables_CustomVariables'),
- 'module' => 'CustomVariables',
- 'action' => 'getCustomVariables',
- 'actionToLoadSubTables' => 'getCustomVariablesValuesFromNameId',
- 'dimension' => Piwik_Translate('CustomVariables_ColumnCustomVariableName'),
- 'documentation' => $documentation,
- 'order' => 10 );
-
- $reports[] = array( 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('CustomVariables_CustomVariables'),
- 'module' => 'CustomVariables',
- 'action' => 'getCustomVariablesValuesFromNameId',
- 'dimension' => Piwik_Translate('CustomVariables_ColumnCustomVariableValue'),
- 'documentation' => $documentation,
- 'isSubtableReport' => true,
- 'order' => 15 );
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getSegmentsMetadata($notification)
- {
- $segments =& $notification->getNotificationObject();
- for($i=1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++)
- {
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'CustomVariables_CustomVariables',
- 'name' => Piwik_Translate('CustomVariables_ColumnCustomVariableName').' '.$i
- .' ('.Piwik_Translate('CustomVariables_ScopeVisit').')',
- 'segment' => 'customVariableName'.$i,
- 'sqlSegment' => 'log_visit.custom_var_k'.$i,
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'CustomVariables_CustomVariables',
- 'name' => Piwik_Translate('CustomVariables_ColumnCustomVariableValue').' '.$i
- .' ('.Piwik_Translate('CustomVariables_ScopeVisit').')',
- 'segment' => 'customVariableValue'.$i,
- 'sqlSegment' => 'log_visit.custom_var_v'.$i,
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'CustomVariables_CustomVariables',
- 'name' => Piwik_Translate('CustomVariables_ColumnCustomVariableName').' '.$i
- .' ('.Piwik_Translate('CustomVariables_ScopePage').')',
- 'segment' => 'customVariablePageName'.$i,
- 'sqlSegment' => 'log_link_visit_action.custom_var_k'.$i,
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'CustomVariables_CustomVariables',
- 'name' => Piwik_Translate('CustomVariables_ColumnCustomVariableValue').' '.$i
- .' ('.Piwik_Translate('CustomVariables_ScopePage').')',
- 'segment' => 'customVariablePageValue'.$i,
- 'sqlSegment' => 'log_link_visit_action.custom_var_v'.$i,
- );
- }
- }
-
- /**
- * Adds Goal dimensions, so that the dimensions are displayed in the UI Goal Overview page
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportsWithGoalMetrics( $notification )
- {
- $dimensions =& $notification->getNotificationObject();
- $dimensions = array_merge($dimensions, array(
- array( 'category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('CustomVariables_CustomVariables'),
- 'module' => 'CustomVariables',
- 'action' => 'getCustomVariables',
- ),
- ));
- }
-
- function __construct()
- {
- $this->maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_referers'];
- $this->maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_referers'];
- }
-
- protected $interestByCustomVariables = array();
- protected $interestByCustomVariablesAndValue = array();
-
- /**
- * Hooks on daily archive to trigger various log processing
- *
- * @param Piwik_Event_Notification $notification notification object
- * @return void
- */
- public function archiveDay( $notification )
- {
- $this->interestByCustomVariables = $this->interestByCustomVariablesAndValue = array();
-
- /**
- * @var Piwik_ArchiveProcessing_Day
- */
- $this->archiveProcessing = $notification->getNotificationObject();
-
- if(!$this->archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveDayAggregate($this->archiveProcessing);
- $this->archiveDayRecordInDatabase($this->archiveProcessing);
- destroy($this->interestByCustomVariables);
- destroy($this->interestByCustomVariablesAndValue);
- }
-
- const LABEL_CUSTOM_VALUE_NOT_DEFINED = "Value not defined";
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- * @return void
- */
- protected function archiveDayAggregate(Piwik_ArchiveProcessing_Day $archiveProcessing)
- {
- for($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++ )
- {
- $keyField = "custom_var_k".$i;
- $valueField = "custom_var_v".$i;
- $dimensions = array($keyField, $valueField);
- $where = "%s.$keyField != ''";
-
- // Custom Vars names and values metrics for visits
- $query = $archiveProcessing->queryVisitsByDimension($dimensions, $where);
-
- while($row = $query->fetch() )
- {
- // Handle case custom var value is empty
- $row[$valueField] = $this->cleanCustomVarValue($row[$valueField]);
-
- // Aggregate
- if(!isset($this->interestByCustomVariables[$row[$keyField]])) $this->interestByCustomVariables[$row[$keyField]]= $archiveProcessing->getNewInterestRow();
- if(!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]] = $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats( $row, $this->interestByCustomVariables[$row[$keyField]]);
- $archiveProcessing->updateInterestStats( $row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]]);
- }
-
- // Custom Vars names and values metrics for page views
- $query = $archiveProcessing->queryActionsByDimension($dimensions, $where);
- $onlyMetricsAvailableInActionsTable = true;
- while($row = $query->fetch() )
- {
- // Handle case custom var value is empty
- $row[$valueField] = $this->cleanCustomVarValue($row[$valueField]);
-
- $label = $row[$valueField];
-
- // Remove price tracked if it's zero or we if we are not currently tracking an ecommerce var
- if(!in_array($row[$keyField], array('_pks', '_pkn', '_pkc')))
- {
- unset($row[Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED]);
- }
-
- // when custom variable value is a JSON array of categories
- // possibly JSON value
- $mustInsertCustomVariableValue = true;
- if($row[$keyField] == '_pkc'
- && $label[0] == '[' && $label[1] == '"')
- {
- // In case categories were truncated, try closing the array
- if(substr($label, -2) != '"]') {
- $label .= '"]';
- }
- $decoded = @Piwik_Common::json_decode($label);
- if(is_array($decoded))
- {
- $count = 0;
- foreach($decoded as $category)
- {
- if(empty($category)
- || $count >= Piwik_Tracker_GoalManager::MAXIMUM_PRODUCT_CATEGORIES) {
- continue;
- }
- if(!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$category])) {
- $this->interestByCustomVariablesAndValue[$row[$keyField]][$category] = $archiveProcessing->getNewInterestRow($onlyMetricsAvailableInActionsTable);
- }
- $archiveProcessing->updateInterestStats( $row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$category], $onlyMetricsAvailableInActionsTable);
- $mustInsertCustomVariableValue = false;
- $count++;
- }
- }
- } // end multi categories hack
-
- if($mustInsertCustomVariableValue)
- {
- if(!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]] = $archiveProcessing->getNewInterestRow($onlyMetricsAvailableInActionsTable);
- $archiveProcessing->updateInterestStats( $row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]], $onlyMetricsAvailableInActionsTable);
- }
-
- // Do not report on Price viewed for the Custom Variable names
- unset($row[Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED]);
-
- // When tracking Custom Variables with scope=page we do not add up visits numbers
- // as it is incorrect to sum visits this way
- // for scope=visit this is allowed, since there is supposed to be one custom var value per custom variable name for a given visit
- $doNotSumVisits = true;
-
- if(!isset($this->interestByCustomVariables[$row[$keyField]])) $this->interestByCustomVariables[$row[$keyField]]= $archiveProcessing->getNewInterestRow($onlyMetricsAvailableInActionsTable, $doNotSumVisits);
- $archiveProcessing->updateInterestStats( $row, $this->interestByCustomVariables[$row[$keyField]], $onlyMetricsAvailableInActionsTable, $doNotSumVisits);
- }
-
- // Custom Vars names and values metrics for Goals
- $query = $archiveProcessing->queryConversionsByDimension($dimensions, $where);
-
- if($query !== false)
- {
- while($row = $query->fetch() )
- {
- // Handle case custom var value is empty
- $row[$valueField] = $this->cleanCustomVarValue($row[$valueField]);
-
- if(!isset($this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- if(!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
-
- $archiveProcessing->updateGoalStats( $row, $this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- $archiveProcessing->updateGoalStats( $row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- }
- }
- }
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByCustomVariables);
- $archiveProcessing->enrichConversionsByLabelArrayHasTwoLevels($this->interestByCustomVariablesAndValue);
- }
-
- protected function cleanCustomVarValue($value)
- {
- if(strlen($value))
- {
- return $value;
- }
- return self::LABEL_CUSTOM_VALUE_NOT_DEFINED;
- }
-
- /**
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @return void
- */
- protected function archiveDayRecordInDatabase($archiveProcessing)
- {
- $recordName = 'CustomVariables_valueByName';
- $table = $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByCustomVariablesAndValue, $this->interestByCustomVariables);
-
- $blob = $table->getSerialized(
- $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable,
- $columnToSort = Piwik_Archive::INDEX_NB_VISITS);
- $archiveProcessing->insertBlobRecord($recordName, $blob);
- destroy($table);
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archivePeriod( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = 'CustomVariables_valueByName';
- $nameToCount = $archiveProcessing->archiveDataTable(
- $dataTableToSum, null, $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable,
- $columnToSort = Piwik_Archive::INDEX_NB_VISITS);
- }
+ public $archiveProcessing;
+ protected $columnToSortByBeforeTruncation;
+ protected $maximumRowsInDataTableLevelZero;
+ protected $maximumRowsInSubDataTable;
+
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('CustomVariables_PluginDescription')
+ . ' <br/>Required to use <a href="http://piwik.org/docs/ecommerce-analytics/">Ecommerce Analytics</a> feature!',
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenus',
+ 'Goals.getReportsWithGoalMetrics' => 'getReportsWithGoalMetrics',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ );
+ return $hooks;
+ }
+
+ function addWidgets()
+ {
+ Piwik_AddWidget('General_Visitors', 'CustomVariables_CustomVariables', 'CustomVariables', 'getCustomVariables');
+ }
+
+ function addMenus()
+ {
+ Piwik_AddMenu('General_Visitors', 'CustomVariables_CustomVariables', array('module' => 'CustomVariables', 'action' => 'index'), $display = true, $order = 50);
+ }
+
+ /**
+ * Returns metadata for available reports
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $reports = & $notification->getNotificationObject();
+
+ $documentation = Piwik_Translate('CustomVariables_CustomVariablesReportDocumentation',
+ array('<br />', '<a href="http://piwik.org/docs/custom-variables/" target="_blank">', '</a>'));
+
+ $reports[] = array('category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('CustomVariables_CustomVariables'),
+ 'module' => 'CustomVariables',
+ 'action' => 'getCustomVariables',
+ 'actionToLoadSubTables' => 'getCustomVariablesValuesFromNameId',
+ 'dimension' => Piwik_Translate('CustomVariables_ColumnCustomVariableName'),
+ 'documentation' => $documentation,
+ 'order' => 10);
+
+ $reports[] = array('category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('CustomVariables_CustomVariables'),
+ 'module' => 'CustomVariables',
+ 'action' => 'getCustomVariablesValuesFromNameId',
+ 'dimension' => Piwik_Translate('CustomVariables_ColumnCustomVariableValue'),
+ 'documentation' => $documentation,
+ 'isSubtableReport' => true,
+ 'order' => 15);
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ $segments =& $notification->getNotificationObject();
+ for ($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++) {
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'CustomVariables_CustomVariables',
+ 'name' => Piwik_Translate('CustomVariables_ColumnCustomVariableName') . ' ' . $i
+ . ' (' . Piwik_Translate('CustomVariables_ScopeVisit') . ')',
+ 'segment' => 'customVariableName' . $i,
+ 'sqlSegment' => 'log_visit.custom_var_k' . $i,
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'CustomVariables_CustomVariables',
+ 'name' => Piwik_Translate('CustomVariables_ColumnCustomVariableValue') . ' ' . $i
+ . ' (' . Piwik_Translate('CustomVariables_ScopeVisit') . ')',
+ 'segment' => 'customVariableValue' . $i,
+ 'sqlSegment' => 'log_visit.custom_var_v' . $i,
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'CustomVariables_CustomVariables',
+ 'name' => Piwik_Translate('CustomVariables_ColumnCustomVariableName') . ' ' . $i
+ . ' (' . Piwik_Translate('CustomVariables_ScopePage') . ')',
+ 'segment' => 'customVariablePageName' . $i,
+ 'sqlSegment' => 'log_link_visit_action.custom_var_k' . $i,
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'CustomVariables_CustomVariables',
+ 'name' => Piwik_Translate('CustomVariables_ColumnCustomVariableValue') . ' ' . $i
+ . ' (' . Piwik_Translate('CustomVariables_ScopePage') . ')',
+ 'segment' => 'customVariablePageValue' . $i,
+ 'sqlSegment' => 'log_link_visit_action.custom_var_v' . $i,
+ );
+ }
+ }
+
+ /**
+ * Adds Goal dimensions, so that the dimensions are displayed in the UI Goal Overview page
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportsWithGoalMetrics($notification)
+ {
+ $dimensions =& $notification->getNotificationObject();
+ $dimensions = array_merge($dimensions, array(
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('CustomVariables_CustomVariables'),
+ 'module' => 'CustomVariables',
+ 'action' => 'getCustomVariables',
+ ),
+ ));
+ }
+
+ function __construct()
+ {
+ $this->maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_referers'];
+ $this->maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_referers'];
+ }
+
+ protected $interestByCustomVariables = array();
+ protected $interestByCustomVariablesAndValue = array();
+
+ /**
+ * Hooks on daily archive to trigger various log processing
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ * @return void
+ */
+ public function archiveDay($notification)
+ {
+ $this->interestByCustomVariables = $this->interestByCustomVariablesAndValue = array();
+
+ /**
+ * @var Piwik_ArchiveProcessing_Day
+ */
+ $this->archiveProcessing = $notification->getNotificationObject();
+
+ if (!$this->archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $this->archiveDayAggregate($this->archiveProcessing);
+ $this->archiveDayRecordInDatabase($this->archiveProcessing);
+ destroy($this->interestByCustomVariables);
+ destroy($this->interestByCustomVariablesAndValue);
+ }
+
+ const LABEL_CUSTOM_VALUE_NOT_DEFINED = "Value not defined";
+
+ /**
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ * @return void
+ */
+ protected function archiveDayAggregate(Piwik_ArchiveProcessing_Day $archiveProcessing)
+ {
+ for ($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++) {
+ $keyField = "custom_var_k" . $i;
+ $valueField = "custom_var_v" . $i;
+ $dimensions = array($keyField, $valueField);
+ $where = "%s.$keyField != ''";
+
+ // Custom Vars names and values metrics for visits
+ $query = $archiveProcessing->queryVisitsByDimension($dimensions, $where);
+
+ while ($row = $query->fetch()) {
+ // Handle case custom var value is empty
+ $row[$valueField] = $this->cleanCustomVarValue($row[$valueField]);
+
+ // Aggregate
+ if (!isset($this->interestByCustomVariables[$row[$keyField]])) $this->interestByCustomVariables[$row[$keyField]] = $archiveProcessing->getNewInterestRow();
+ if (!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]] = $archiveProcessing->getNewInterestRow();
+ $archiveProcessing->updateInterestStats($row, $this->interestByCustomVariables[$row[$keyField]]);
+ $archiveProcessing->updateInterestStats($row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]]);
+ }
+
+ // Custom Vars names and values metrics for page views
+ $query = $archiveProcessing->queryActionsByDimension($dimensions, $where);
+ $onlyMetricsAvailableInActionsTable = true;
+ while ($row = $query->fetch()) {
+ // Handle case custom var value is empty
+ $row[$valueField] = $this->cleanCustomVarValue($row[$valueField]);
+
+ $label = $row[$valueField];
+
+ // Remove price tracked if it's zero or we if we are not currently tracking an ecommerce var
+ if (!in_array($row[$keyField], array('_pks', '_pkn', '_pkc'))) {
+ unset($row[Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED]);
+ }
+
+ // when custom variable value is a JSON array of categories
+ // possibly JSON value
+ $mustInsertCustomVariableValue = true;
+ if ($row[$keyField] == '_pkc'
+ && $label[0] == '[' && $label[1] == '"'
+ ) {
+ // In case categories were truncated, try closing the array
+ if (substr($label, -2) != '"]') {
+ $label .= '"]';
+ }
+ $decoded = @Piwik_Common::json_decode($label);
+ if (is_array($decoded)) {
+ $count = 0;
+ foreach ($decoded as $category) {
+ if (empty($category)
+ || $count >= Piwik_Tracker_GoalManager::MAXIMUM_PRODUCT_CATEGORIES
+ ) {
+ continue;
+ }
+ if (!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$category])) {
+ $this->interestByCustomVariablesAndValue[$row[$keyField]][$category] = $archiveProcessing->getNewInterestRow($onlyMetricsAvailableInActionsTable);
+ }
+ $archiveProcessing->updateInterestStats($row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$category], $onlyMetricsAvailableInActionsTable);
+ $mustInsertCustomVariableValue = false;
+ $count++;
+ }
+ }
+ } // end multi categories hack
+
+ if ($mustInsertCustomVariableValue) {
+ if (!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]] = $archiveProcessing->getNewInterestRow($onlyMetricsAvailableInActionsTable);
+ $archiveProcessing->updateInterestStats($row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]], $onlyMetricsAvailableInActionsTable);
+ }
+
+ // Do not report on Price viewed for the Custom Variable names
+ unset($row[Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED]);
+
+ // When tracking Custom Variables with scope=page we do not add up visits numbers
+ // as it is incorrect to sum visits this way
+ // for scope=visit this is allowed, since there is supposed to be one custom var value per custom variable name for a given visit
+ $doNotSumVisits = true;
+
+ if (!isset($this->interestByCustomVariables[$row[$keyField]])) $this->interestByCustomVariables[$row[$keyField]] = $archiveProcessing->getNewInterestRow($onlyMetricsAvailableInActionsTable, $doNotSumVisits);
+ $archiveProcessing->updateInterestStats($row, $this->interestByCustomVariables[$row[$keyField]], $onlyMetricsAvailableInActionsTable, $doNotSumVisits);
+ }
+
+ // Custom Vars names and values metrics for Goals
+ $query = $archiveProcessing->queryConversionsByDimension($dimensions, $where);
+
+ if ($query !== false) {
+ while ($row = $query->fetch()) {
+ // Handle case custom var value is empty
+ $row[$valueField] = $this->cleanCustomVarValue($row[$valueField]);
+
+ if (!isset($this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+ if (!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+
+ $archiveProcessing->updateGoalStats($row, $this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
+ $archiveProcessing->updateGoalStats($row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
+ }
+ }
+ }
+ $archiveProcessing->enrichConversionsByLabelArray($this->interestByCustomVariables);
+ $archiveProcessing->enrichConversionsByLabelArrayHasTwoLevels($this->interestByCustomVariablesAndValue);
+ }
+
+ protected function cleanCustomVarValue($value)
+ {
+ if (strlen($value)) {
+ return $value;
+ }
+ return self::LABEL_CUSTOM_VALUE_NOT_DEFINED;
+ }
+
+ /**
+ * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @return void
+ */
+ protected function archiveDayRecordInDatabase($archiveProcessing)
+ {
+ $recordName = 'CustomVariables_valueByName';
+ $table = $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByCustomVariablesAndValue, $this->interestByCustomVariables);
+
+ $blob = $table->getSerialized(
+ $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable,
+ $columnToSort = Piwik_Archive::INDEX_NB_VISITS);
+ $archiveProcessing->insertBlobRecord($recordName, $blob);
+ destroy($table);
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ function archivePeriod($notification)
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $dataTableToSum = 'CustomVariables_valueByName';
+ $nameToCount = $archiveProcessing->archiveDataTable(
+ $dataTableToSum, null, $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable,
+ $columnToSort = Piwik_Archive::INDEX_NB_VISITS);
+ }
}
diff --git a/plugins/DBStats/API.php b/plugins/DBStats/API.php
index b72d0a2e49..fec0e0fe71 100644
--- a/plugins/DBStats/API.php
+++ b/plugins/DBStats/API.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_DBStats
*/
@@ -16,304 +16,292 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/DBStats/MySQLMetadataProvider.php';
/**
* DBStats API is used to request the overall status of the Mysql tables in use by Piwik.
- *
+ *
* @package Piwik_DBStats
*/
class Piwik_DBStats_API
{
- /** Singleton instance of this class. */
- static private $instance = null;
-
- /**
- * Gets or creates the DBStats API singleton.
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * The MySQLMetadataProvider instance that fetches table/db status information.
- */
- private $metadataProvider;
-
- /**
- * Constructor.
- */
- public function __construct()
- {
- $this->metadataProvider = new Piwik_DBStats_MySQLMetadataProvider();
- }
-
- /**
- * Forces the next table status request to issue a query by reseting the table status cache.
- */
- public function resetTableStatuses()
- {
- Piwik::checkUserIsSuperUser();
- self::getInstance()->metadataProvider = new Piwik_DBStats_MySQLMetadataProvider();
- }
-
- /**
- * Gets some general information about this Piwik installation, including the count of
- * websites tracked, the count of users and the total space used by the database.
- *
- * @return array Contains the website count, user count and total space used by the database.
- */
- public function getGeneralInformation()
- {
- Piwik::checkUserIsSuperUser();
- // calculate total size
- $totalSpaceUsed = 0;
- foreach ($this->metadataProvider->getAllTablesStatus() as $status)
- {
- $totalSpaceUsed += $status['Data_length'] + $status['Index_length'];
- }
-
- $siteTableStatus = $this->metadataProvider->getTableStatus('site');
- $userTableStatus = $this->metadataProvider->getTableStatus('user');
-
- $siteCount = $siteTableStatus['Rows'];
- $userCount = $userTableStatus['Rows'];
-
- return array($siteCount, $userCount, $totalSpaceUsed);
- }
-
- /**
- * Gets general database info that is not specific to any table.
- *
- * @return array See http://dev.mysql.com/doc/refman/5.1/en/show-status.html .
- */
- public function getDBStatus()
- {
- Piwik::checkUserIsSuperUser();
- return $this->metadataProvider->getDBStatus();
- }
-
- /**
- * Returns a datatable summarizing how data is distributed among Piwik tables.
- *
- * This function will group tracker tables, numeric archive tables, blob archive tables
- * and other tables together so only four rows are shown.
- *
- * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
- */
- public function getDatabaseUsageSummary()
- {
- Piwik::checkUserIsSuperUser();
-
- $emptyRow = array('data_size' => 0, 'index_size' => 0, 'row_count' => 0);
- $rows = array(
- 'tracker_data' => $emptyRow,
- 'metric_data' => $emptyRow,
- 'report_data' => $emptyRow,
- 'other_data' => $emptyRow
- );
-
- foreach ($this->metadataProvider->getAllTablesStatus() as $status)
- {
- if ($this->isNumericArchiveTable($status['Name']))
- {
- $rowToAddTo = &$rows['metric_data'];
- }
- else if ($this->isBlobArchiveTable($status['Name']))
- {
- $rowToAddTo = &$rows['report_data'];
- }
- else if ($this->isTrackerTable($status['Name']))
- {
- $rowToAddTo = &$rows['tracker_data'];
- }
- else
- {
- $rowToAddTo = &$rows['other_data'];
- }
-
- $rowToAddTo['data_size'] += $status['Data_length'];
- $rowToAddTo['index_size'] += $status['Index_length'];
- $rowToAddTo['row_count'] += $status['Rows'];
- }
-
- $result = new Piwik_DataTable();
- $result->addRowsFromArrayWithIndexLabel($rows);
- return $result;
- }
-
- /**
- * Returns a datatable describing how much space is taken up by each log table.
- *
- * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
- */
- public function getTrackerDataSummary()
- {
- Piwik::checkUserIsSuperUser();
- return $this->getTablesSummary($this->metadataProvider->getAllLogTableStatus());
- }
-
- /**
- * Returns a datatable describing how much space is taken up by each numeric
- * archive table.
- *
- * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
- */
- public function getMetricDataSummary()
- {
- Piwik::checkUserIsSuperUser();
- return $this->getTablesSummary($this->metadataProvider->getAllNumericArchiveStatus());
- }
-
- /**
- * Returns a datatable describing how much space is taken up by each numeric
- * archive table, grouped by year.
- *
- * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
- */
- public function getMetricDataSummaryByYear()
- {
- Piwik::checkUserIsSuperUser();
-
- $dataTable = $this->getMetricDataSummary();
-
- $getTableYear = array('Piwik_DBStats_API', 'getArchiveTableYear');
- $dataTable->filter('GroupBy', array('label', $getTableYear));
-
- return $dataTable;
- }
-
- /**
- * Returns a datatable describing how much space is taken up by each blob
- * archive table.
- *
- * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
- */
- public function getReportDataSummary()
- {
- Piwik::checkUserIsSuperUser();
- return $this->getTablesSummary($this->metadataProvider->getAllBlobArchiveStatus());
- }
-
- /**
- * Returns a datatable describing how much space is taken up by each blob
- * archive table, grouped by year.
- *
- * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
- */
- public function getReportDataSummaryByYear()
- {
- Piwik::checkUserIsSuperUser();
-
- $dataTable = $this->getReportDataSummary();
-
- $getTableYear = array('Piwik_DBStats_API', 'getArchiveTableYear');
- $dataTable->filter('GroupBy', array('label', $getTableYear));
-
- return $dataTable;
- }
-
- /**
- * Returns a datatable describing how much space is taken up by 'admin' tables.
- *
- * An 'admin' table is a table that is not central to analytics functionality.
- * So any table that isn't an archive table or a log table is an 'admin' table.
- *
- * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
- */
- public function getAdminDataSummary()
- {
- Piwik::checkUserIsSuperUser();
- return $this->getTablesSummary($this->metadataProvider->getAllAdminTableStatus());
- }
-
- /**
- * Returns a datatable describing how much total space is taken up by each
- * individual report type.
- *
- * Goal reports and reports of the format .*_[0-9]+ are grouped together.
- *
- * @param bool $forceCache false to use the cached result, true to run the queries again and
- * cache the result.
- * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
- */
- public function getIndividualReportsSummary( $forceCache = false )
- {
- Piwik::checkUserIsSuperUser();
- return $this->metadataProvider->getRowCountsAndSizeByBlobName($forceCache);
- }
-
- /**
- * Returns a datatable describing how much total space is taken up by each
- * individual metric type.
- *
- * Goal metrics, metrics of the format .*_[0-9]+ and 'done...' metrics are grouped together.
- *
- * @param bool $forceCache false to use the cached result, true to run the queries again and
- * cache the result.
- * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
- */
- public function getIndividualMetricsSummary( $forceCache = false )
- {
- Piwik::checkUserIsSuperUser();
- return $this->metadataProvider->getRowCountsAndSizeByMetricName($forceCache);
- }
-
- /**
- * Returns a datatable representation of a set of table statuses.
- *
- * @param array $statuses The table statuses to summarize.
- * @return Piwik_DataTable
- */
- private function getTablesSummary( $statuses )
- {
- $dataTable = new Piwik_DataTable();
- foreach ($statuses as $status)
- {
- $dataTable->addRowFromSimpleArray(array(
- 'label' => $status['Name'],
- 'data_size' => $status['Data_length'],
- 'index_size' => $status['Index_length'],
- 'row_count' => $status['Rows']
- ));
- }
- return $dataTable;
- }
-
- /** Returns true if $name is the name of a numeric archive table, false if otherwise. */
- private function isNumericArchiveTable( $name )
- {
- return strpos($name, Piwik_Common::prefixTable('archive_numeric_')) === 0;
- }
-
- /** Returns true if $name is the name of a blob archive table, false if otherwise. */
- private function isBlobArchiveTable( $name )
- {
- return strpos($name, Piwik_Common::prefixTable('archive_blob_')) === 0;
- }
-
- /** Returns true if $name is the name of a log table, false if otherwise. */
- private function isTrackerTable( $name )
- {
- return strpos($name, Piwik_Common::prefixTable('log_')) === 0;
- }
-
- /**
- * Gets the year of an archive table from its name.
- *
- * @param string $tableName
- * @param string The year.
- *
- * @ignore
- */
- public static function getArchiveTableYear( $tableName )
- {
- if (preg_match("/archive_(?:numeric|blob)_([0-9]+)_/", $tableName, $matches) === 0)
- {
- return '';
- }
-
- return $matches[1];
- }
+ /** Singleton instance of this class. */
+ static private $instance = null;
+
+ /**
+ * Gets or creates the DBStats API singleton.
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * The MySQLMetadataProvider instance that fetches table/db status information.
+ */
+ private $metadataProvider;
+
+ /**
+ * Constructor.
+ */
+ public function __construct()
+ {
+ $this->metadataProvider = new Piwik_DBStats_MySQLMetadataProvider();
+ }
+
+ /**
+ * Forces the next table status request to issue a query by reseting the table status cache.
+ */
+ public function resetTableStatuses()
+ {
+ Piwik::checkUserIsSuperUser();
+ self::getInstance()->metadataProvider = new Piwik_DBStats_MySQLMetadataProvider();
+ }
+
+ /**
+ * Gets some general information about this Piwik installation, including the count of
+ * websites tracked, the count of users and the total space used by the database.
+ *
+ * @return array Contains the website count, user count and total space used by the database.
+ */
+ public function getGeneralInformation()
+ {
+ Piwik::checkUserIsSuperUser();
+ // calculate total size
+ $totalSpaceUsed = 0;
+ foreach ($this->metadataProvider->getAllTablesStatus() as $status) {
+ $totalSpaceUsed += $status['Data_length'] + $status['Index_length'];
+ }
+
+ $siteTableStatus = $this->metadataProvider->getTableStatus('site');
+ $userTableStatus = $this->metadataProvider->getTableStatus('user');
+
+ $siteCount = $siteTableStatus['Rows'];
+ $userCount = $userTableStatus['Rows'];
+
+ return array($siteCount, $userCount, $totalSpaceUsed);
+ }
+
+ /**
+ * Gets general database info that is not specific to any table.
+ *
+ * @return array See http://dev.mysql.com/doc/refman/5.1/en/show-status.html .
+ */
+ public function getDBStatus()
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->metadataProvider->getDBStatus();
+ }
+
+ /**
+ * Returns a datatable summarizing how data is distributed among Piwik tables.
+ *
+ * This function will group tracker tables, numeric archive tables, blob archive tables
+ * and other tables together so only four rows are shown.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getDatabaseUsageSummary()
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $emptyRow = array('data_size' => 0, 'index_size' => 0, 'row_count' => 0);
+ $rows = array(
+ 'tracker_data' => $emptyRow,
+ 'metric_data' => $emptyRow,
+ 'report_data' => $emptyRow,
+ 'other_data' => $emptyRow
+ );
+
+ foreach ($this->metadataProvider->getAllTablesStatus() as $status) {
+ if ($this->isNumericArchiveTable($status['Name'])) {
+ $rowToAddTo = & $rows['metric_data'];
+ } else if ($this->isBlobArchiveTable($status['Name'])) {
+ $rowToAddTo = & $rows['report_data'];
+ } else if ($this->isTrackerTable($status['Name'])) {
+ $rowToAddTo = & $rows['tracker_data'];
+ } else {
+ $rowToAddTo = & $rows['other_data'];
+ }
+
+ $rowToAddTo['data_size'] += $status['Data_length'];
+ $rowToAddTo['index_size'] += $status['Index_length'];
+ $rowToAddTo['row_count'] += $status['Rows'];
+ }
+
+ $result = new Piwik_DataTable();
+ $result->addRowsFromArrayWithIndexLabel($rows);
+ return $result;
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by each log table.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getTrackerDataSummary()
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->getTablesSummary($this->metadataProvider->getAllLogTableStatus());
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by each numeric
+ * archive table.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getMetricDataSummary()
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->getTablesSummary($this->metadataProvider->getAllNumericArchiveStatus());
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by each numeric
+ * archive table, grouped by year.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getMetricDataSummaryByYear()
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $dataTable = $this->getMetricDataSummary();
+
+ $getTableYear = array('Piwik_DBStats_API', 'getArchiveTableYear');
+ $dataTable->filter('GroupBy', array('label', $getTableYear));
+
+ return $dataTable;
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by each blob
+ * archive table.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getReportDataSummary()
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->getTablesSummary($this->metadataProvider->getAllBlobArchiveStatus());
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by each blob
+ * archive table, grouped by year.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getReportDataSummaryByYear()
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $dataTable = $this->getReportDataSummary();
+
+ $getTableYear = array('Piwik_DBStats_API', 'getArchiveTableYear');
+ $dataTable->filter('GroupBy', array('label', $getTableYear));
+
+ return $dataTable;
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by 'admin' tables.
+ *
+ * An 'admin' table is a table that is not central to analytics functionality.
+ * So any table that isn't an archive table or a log table is an 'admin' table.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getAdminDataSummary()
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->getTablesSummary($this->metadataProvider->getAllAdminTableStatus());
+ }
+
+ /**
+ * Returns a datatable describing how much total space is taken up by each
+ * individual report type.
+ *
+ * Goal reports and reports of the format .*_[0-9]+ are grouped together.
+ *
+ * @param bool $forceCache false to use the cached result, true to run the queries again and
+ * cache the result.
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getIndividualReportsSummary($forceCache = false)
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->metadataProvider->getRowCountsAndSizeByBlobName($forceCache);
+ }
+
+ /**
+ * Returns a datatable describing how much total space is taken up by each
+ * individual metric type.
+ *
+ * Goal metrics, metrics of the format .*_[0-9]+ and 'done...' metrics are grouped together.
+ *
+ * @param bool $forceCache false to use the cached result, true to run the queries again and
+ * cache the result.
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getIndividualMetricsSummary($forceCache = false)
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->metadataProvider->getRowCountsAndSizeByMetricName($forceCache);
+ }
+
+ /**
+ * Returns a datatable representation of a set of table statuses.
+ *
+ * @param array $statuses The table statuses to summarize.
+ * @return Piwik_DataTable
+ */
+ private function getTablesSummary($statuses)
+ {
+ $dataTable = new Piwik_DataTable();
+ foreach ($statuses as $status) {
+ $dataTable->addRowFromSimpleArray(array(
+ 'label' => $status['Name'],
+ 'data_size' => $status['Data_length'],
+ 'index_size' => $status['Index_length'],
+ 'row_count' => $status['Rows']
+ ));
+ }
+ return $dataTable;
+ }
+
+ /** Returns true if $name is the name of a numeric archive table, false if otherwise. */
+ private function isNumericArchiveTable($name)
+ {
+ return strpos($name, Piwik_Common::prefixTable('archive_numeric_')) === 0;
+ }
+
+ /** Returns true if $name is the name of a blob archive table, false if otherwise. */
+ private function isBlobArchiveTable($name)
+ {
+ return strpos($name, Piwik_Common::prefixTable('archive_blob_')) === 0;
+ }
+
+ /** Returns true if $name is the name of a log table, false if otherwise. */
+ private function isTrackerTable($name)
+ {
+ return strpos($name, Piwik_Common::prefixTable('log_')) === 0;
+ }
+
+ /**
+ * Gets the year of an archive table from its name.
+ *
+ * @param string $tableName
+ * @param string The year.
+ *
+ * @ignore
+ */
+ public static function getArchiveTableYear($tableName)
+ {
+ if (preg_match("/archive_(?:numeric|blob)_([0-9]+)_/", $tableName, $matches) === 0) {
+ return '';
+ }
+
+ return $matches[1];
+ }
}
diff --git a/plugins/DBStats/Controller.php b/plugins/DBStats/Controller.php
index 06bae7232b..ab0de6a292 100644
--- a/plugins/DBStats/Controller.php
+++ b/plugins/DBStats/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_DBStats
*/
@@ -15,366 +15,355 @@
*/
class Piwik_DBStats_Controller extends Piwik_Controller_Admin
{
- /**
- * Returns the index for this plugin. Shows every other report defined by this plugin,
- * except the '...ByYear' reports. These can be loaded as related reports.
- *
- * Also, the 'getIndividual...Summary' reports are loaded by AJAX, as they can take
- * a significant amount of time to load on setups w/ lots of websites.
- */
- public function index()
- {
- Piwik::checkUserIsSuperUser();
- $view = Piwik_View::factory('index');
- $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
-
- $view->databaseUsageSummary = $this->getDatabaseUsageSummary(true);
- $view->trackerDataSummary = $this->getTrackerDataSummary(true);
- $view->metricDataSummary = $this->getMetricDataSummary(true);
- $view->reportDataSummary = $this->getReportDataSummary(true);
- $view->adminDataSummary = $this->getAdminDataSummary(true);
-
- list($siteCount, $userCount, $totalSpaceUsed) = Piwik_DBStats_API::getInstance()->getGeneralInformation();
- $view->siteCount = Piwik::getPrettyNumber($siteCount);
- $view->userCount = Piwik::getPrettyNumber($userCount);
- $view->totalSpaceUsed = Piwik::getPrettySizeFromBytes($totalSpaceUsed);
-
- echo $view->render();
- }
-
- /**
- * Shows a datatable that displays how much space the tracker tables, numeric
- * archive tables, report tables and other tables take up in the MySQL database.
- *
- * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
- * it is echoed.
- */
- public function getDatabaseUsageSummary( $fetch = false )
- {
- Piwik::checkUserIsSuperUser();
-
- $view = $this->getDataTableView(__FUNCTION__, $viewType = 'graphPie', $orderDir = 'desc',
- $addPercentColumn = true);
- $view->disableOffsetInformationAndPaginationControls();
-
- if ($view instanceof Piwik_ViewDataTable_GenerateGraphHTML)
- {
- $view->showAllTicks();
- }
-
- // translate the labels themselves
- $translateSummaryLabel = array($this, 'translateSummarylabel');
- $view->queueFilter('ColumnCallbackReplace', array(array('label'), $translateSummaryLabel),
- $runBeforeGenericFilters = true);
-
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Shows a datatable that displays the amount of space each individual log table
- * takes up in the MySQL database.
- *
- * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
- * it is echoed.
- */
- public function getTrackerDataSummary( $fetch = false )
- {
- Piwik::checkUserIsSuperUser();
-
- $view = $this->getDataTableView(__FUNCTION__);
- $view->disableOffsetInformationAndPaginationControls();
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Shows a datatable that displays the amount of space each numeric archive table
- * takes up in the MySQL database.
- *
- * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
- * it is echoed.
- */
- public function getMetricDataSummary( $fetch = false )
- {
- Piwik::checkUserIsSuperUser();
- $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc');
- $view->addRelatedReports(Piwik_Translate('DBStats_MetricTables'), array(
- 'DBStats.getMetricDataSummaryByYear' => Piwik_Translate('DBStats_MetricDataByYear')
- ));
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Shows a datatable that displays the amount of space each numeric archive table
- * takes up in the MySQL database, for each year of numeric data.
- *
- * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
- * it is echoed.
- */
- public function getMetricDataSummaryByYear( $fetch = false )
- {
- Piwik::checkUserIsSuperUser();
- $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc',
- $addPercentColumn = false, $labelKey = 'CoreHome_PeriodYear');
- $view->addRelatedReports(Piwik_Translate('DBStats_MetricDataByYear'), array(
- 'DBStats.getMetricDataSummary' => Piwik_Translate('DBStats_MetricTables')
- ));
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Shows a datatable that displays the amount of space each blob archive table
- * takes up in the MySQL database.
- *
- * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
- * it is echoed.
- */
- public function getReportDataSummary( $fetch = false )
- {
- Piwik::checkUserIsSuperUser();
- $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc');
- $view->addRelatedReports(Piwik_Translate('DBStats_ReportTables'), array(
- 'DBStats.getReportDataSummaryByYear' => Piwik_Translate('DBStats_ReportDataByYear')
- ));
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Shows a datatable that displays the amount of space each blob archive table
- * takes up in the MySQL database, for each year of blob data.
- *
- * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
- * it is echoed.
- */
- public function getReportDataSummaryByYear( $fetch = false )
- {
- Piwik::checkUserIsSuperUser();
- $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc',
- $addPercentColumn = false, $labelKey = 'CoreHome_PeriodYear');
- $view->addRelatedReports(Piwik_Translate('DBStats_ReportDataByYear'), array(
- 'DBStats.getReportDataSummary' => Piwik_Translate('DBStats_ReportTables')
- ));
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Shows a datatable that displays how many occurances there are of each individual
- * report type stored in the MySQL database.
- *
- * Goal reports and reports of the format: .*_[0-9]+ are grouped together.
- *
- * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
- * it is echoed.
- */
- public function getIndividualReportsSummary( $fetch = false )
- {
- Piwik::checkUserIsSuperUser();
- $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'asc',
- $addPercentColumn = false, $labelKey = 'General_Report',
- $sizeColumns = array('estimated_size'));
-
- // this report table has some extra columns that shouldn't be shown
- if ($view instanceof Piwik_ViewDataTable_HtmlTable)
- {
- $view->setColumnsToDisplay(array('label', 'row_count', 'estimated_size'));
- }
-
- $this->setIndividualSummaryFooterMessage($view);
-
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Shows a datatable that displays how many occurances there are of each individual
- * metric type stored in the MySQL database.
- *
- * Goal metrics, metrics of the format .*_[0-9]+ and 'done...' metrics are grouped together.
- *
- * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
- * it is echoed.
- */
- public function getIndividualMetricsSummary( $fetch = false )
- {
- Piwik::checkUserIsSuperUser();
- $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'asc',
- $addPercentColumn = false, $labelKey = 'General_Metric',
- $sizeColumns = array('estimated_size'));
-
- $this->setIndividualSummaryFooterMessage($view);
-
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Shows a datatable that displays the amount of space each 'admin' table takes
- * up in the MySQL database.
- *
- * An 'admin' table is a table that is not central to analytics functionality.
- * So any table that isn't an archive table or a log table is an 'admin' table.
- *
- * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
- * it is echoed.
- */
- public function getAdminDataSummary( $fetch = false )
- {
- Piwik::checkUserIsSuperUser();
- $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table');
- $view->disableOffsetInformationAndPaginationControls();
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Utility function that creates and prepares a ViewDataTable for this plugin.
- */
- private function getDataTableView( $function, $viewType = 'table', $orderDir = 'asc', $addPercentColumn = false,
- $labelKey = 'DBStats_Table', $sizeColumns = array('data_size', 'index_size'),
- $limit = 25 )
- {
- $columnTranslations = array(
- 'label' => Piwik_Translate($labelKey),
- 'year' => Piwik_Translate('CoreHome_PeriodYear'),
- 'data_size' => Piwik_Translate('DBStats_DataSize'),
- 'index_size' => Piwik_Translate('DBStats_IndexSize'),
- 'total_size' => Piwik_Translate('DBStats_TotalSize'),
- 'row_count' => Piwik_Translate('DBStats_RowCount'),
- 'percent_total' => '%&nbsp;'.Piwik_Translate('DBStats_DBSize'),
- 'estimated_size' => Piwik_Translate('DBStats_EstimatedSize')
- );
-
- $view = Piwik_ViewDataTable::factory($viewType);
- $view->init($this->pluginName, $function, "DBStats.$function");
- $view->setSortedColumn('label', $orderDir);
- $view->setLimit($limit);
- $view->setHighlightSummaryRow(true);
- $view->disableSearchBox();
- $view->disableExcludeLowPopulation();
- $view->disableTagCloud();
- $view->disableShowAllColumns();
- $view->alwaysShowSummaryRow();
-
- // translate columns
- foreach ($columnTranslations as $columnName => $translation)
- {
- $view->setColumnTranslation($columnName, $translation);
- }
-
- // add total_size column (if necessary columns are present)
- if (in_array('data_size', $sizeColumns) && in_array('index_size', $sizeColumns))
- {
- $getTotalTableSize = array($this, 'getTotalTableSize');
- $view->queueFilter('ColumnCallbackAddColumn',
- array(array('data_size', 'index_size'), 'total_size', $getTotalTableSize),
- $runBeforeGenericFilters = true);
-
- $sizeColumns[] = 'total_size';
- }
-
- $runPrettySizeFilterBeforeGeneric = false;
- $fixedMemoryUnit = false;
- if ($view instanceof Piwik_ViewDataTable_HtmlTable) // if displaying a table
- {
- $view->disableRowEvolution();
-
- // add summary row only if displaying a table
- $view->queueFilter('AddSummaryRow', array(0, Piwik_Translate('General_Total'), 'label', false),
- $runBeforeGenericFilters = true);
-
- // add other filters
- if ($addPercentColumn && in_array('total_size', $sizeColumns))
- {
- $view->queueFilter('ColumnCallbackAddColumnPercentage',
- array('percent_total', 'total_size', 'total_size', $quotientPrecision = 0, $shouldSkipRows = false,
- $getDivisorFromSummaryRow = true),
- $runBeforeGenericFilters = true);
- $view->setSortedColumn('percent_total', $orderDir);
- }
- }
- else if ($view instanceof Piwik_ViewDataTable_GenerateGraphData) // if displaying a graph
- {
- if (in_array('total_size', $sizeColumns))
- {
- $view->setColumnsToDisplay(array('label', 'total_size'));
-
- // when displaying a graph, we force sizes to be shown as the same unit so axis labels
- // will be readable. NOTE: The unit should depend on the smallest value of the data table,
- // however there's no way to know this information, short of creating a custom filter. For
- // now, just assume KB.
- $fixedMemoryUnit = 'K';
- $view->setAxisYUnit(' K');
-
- $view->setSortedColumn('total_size', 'desc');
-
- $runPrettySizeFilterBeforeGeneric = true;
- }
- else
- {
- $view->setColumnsToDisplay(array('label', 'row_count'));
- $view->setAxisYUnit(' '.Piwik_Translate('General_Rows'));
-
- $view->setSortedColumn('row_count', 'desc');
- }
- }
-
- $getPrettySize = array('Piwik', 'getPrettySizeFromBytes');
- $params = $fixedMemoryUnit === false ? array() : array($fixedMemoryUnit);
- $view->queueFilter(
- 'ColumnCallbackReplace', array($sizeColumns, $getPrettySize, $params), $runPrettySizeFilterBeforeGeneric);
-
- // jqPlot will display &nbsp; as, well, '&nbsp;', so don't replace the spaces when rendering as a graph
- if (!($view instanceof Piwik_ViewDataTable_GenerateGraphData))
- {
- $replaceSpaces = array($this, 'replaceColumnSpaces');
- $view->queueFilter('ColumnCallbackReplace', array($sizeColumns, $replaceSpaces));
- }
-
- $getPrettyNumber = array('Piwik', 'getPrettyNumber');
- $view->queueFilter('ColumnCallbackReplace', array(array('row_count'), $getPrettyNumber));
-
- return $view;
- }
-
- /**
- * Replaces spaces w/ &nbsp; for correct HTML output.
- */
- public function replaceColumnSpaces( $value )
- {
- return str_replace(' ', '&nbsp;', $value);
- }
-
- /**
- * Row callback function that calculates a tables total size.
- */
- public function getTotalTableSize( $dataSize, $indexSize )
- {
- return $dataSize + $indexSize;
- }
-
- /**
- * Column callback used to translate the column values in the database usage summary table.
- */
- public function translateSummarylabel( $value )
- {
- static $valueToTranslationStr = array(
- 'tracker_data' => 'DBStats_TrackerTables',
- 'report_data' => 'DBStats_ReportTables',
- 'metric_data' => 'DBStats_MetricTables',
- 'other_data' => 'DBStats_OtherTables'
- );
-
- return isset($valueToTranslationStr[$value])
- ? Piwik_Translate($valueToTranslationStr[$value])
- : $value;
- }
-
- /**
- * Sets the footer message for the Individual...Summary reports.
- */
- private function setIndividualSummaryFooterMessage( $view )
- {
- $lastGenerated = Piwik_DBStats::getDateOfLastCachingRun();
- if ($lastGenerated !== false)
- {
- $view->setFooterMessage(Piwik_Translate('Mobile_LastUpdated', $lastGenerated));
- }
- }
+ /**
+ * Returns the index for this plugin. Shows every other report defined by this plugin,
+ * except the '...ByYear' reports. These can be loaded as related reports.
+ *
+ * Also, the 'getIndividual...Summary' reports are loaded by AJAX, as they can take
+ * a significant amount of time to load on setups w/ lots of websites.
+ */
+ public function index()
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = Piwik_View::factory('index');
+ $this->setBasicVariablesView($view);
+ $view->menu = Piwik_GetAdminMenu();
+
+ $view->databaseUsageSummary = $this->getDatabaseUsageSummary(true);
+ $view->trackerDataSummary = $this->getTrackerDataSummary(true);
+ $view->metricDataSummary = $this->getMetricDataSummary(true);
+ $view->reportDataSummary = $this->getReportDataSummary(true);
+ $view->adminDataSummary = $this->getAdminDataSummary(true);
+
+ list($siteCount, $userCount, $totalSpaceUsed) = Piwik_DBStats_API::getInstance()->getGeneralInformation();
+ $view->siteCount = Piwik::getPrettyNumber($siteCount);
+ $view->userCount = Piwik::getPrettyNumber($userCount);
+ $view->totalSpaceUsed = Piwik::getPrettySizeFromBytes($totalSpaceUsed);
+
+ echo $view->render();
+ }
+
+ /**
+ * Shows a datatable that displays how much space the tracker tables, numeric
+ * archive tables, report tables and other tables take up in the MySQL database.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getDatabaseUsageSummary($fetch = false)
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'graphPie', $orderDir = 'desc',
+ $addPercentColumn = true);
+ $view->disableOffsetInformationAndPaginationControls();
+
+ if ($view instanceof Piwik_ViewDataTable_GenerateGraphHTML) {
+ $view->showAllTicks();
+ }
+
+ // translate the labels themselves
+ $translateSummaryLabel = array($this, 'translateSummarylabel');
+ $view->queueFilter('ColumnCallbackReplace', array(array('label'), $translateSummaryLabel),
+ $runBeforeGenericFilters = true);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each individual log table
+ * takes up in the MySQL database.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getTrackerDataSummary($fetch = false)
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $view = $this->getDataTableView(__FUNCTION__);
+ $view->disableOffsetInformationAndPaginationControls();
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each numeric archive table
+ * takes up in the MySQL database.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getMetricDataSummary($fetch = false)
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc');
+ $view->addRelatedReports(Piwik_Translate('DBStats_MetricTables'), array(
+ 'DBStats.getMetricDataSummaryByYear' => Piwik_Translate('DBStats_MetricDataByYear')
+ ));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each numeric archive table
+ * takes up in the MySQL database, for each year of numeric data.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getMetricDataSummaryByYear($fetch = false)
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc',
+ $addPercentColumn = false, $labelKey = 'CoreHome_PeriodYear');
+ $view->addRelatedReports(Piwik_Translate('DBStats_MetricDataByYear'), array(
+ 'DBStats.getMetricDataSummary' => Piwik_Translate('DBStats_MetricTables')
+ ));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each blob archive table
+ * takes up in the MySQL database.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getReportDataSummary($fetch = false)
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc');
+ $view->addRelatedReports(Piwik_Translate('DBStats_ReportTables'), array(
+ 'DBStats.getReportDataSummaryByYear' => Piwik_Translate('DBStats_ReportDataByYear')
+ ));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each blob archive table
+ * takes up in the MySQL database, for each year of blob data.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getReportDataSummaryByYear($fetch = false)
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc',
+ $addPercentColumn = false, $labelKey = 'CoreHome_PeriodYear');
+ $view->addRelatedReports(Piwik_Translate('DBStats_ReportDataByYear'), array(
+ 'DBStats.getReportDataSummary' => Piwik_Translate('DBStats_ReportTables')
+ ));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays how many occurances there are of each individual
+ * report type stored in the MySQL database.
+ *
+ * Goal reports and reports of the format: .*_[0-9]+ are grouped together.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getIndividualReportsSummary($fetch = false)
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'asc',
+ $addPercentColumn = false, $labelKey = 'General_Report',
+ $sizeColumns = array('estimated_size'));
+
+ // this report table has some extra columns that shouldn't be shown
+ if ($view instanceof Piwik_ViewDataTable_HtmlTable) {
+ $view->setColumnsToDisplay(array('label', 'row_count', 'estimated_size'));
+ }
+
+ $this->setIndividualSummaryFooterMessage($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays how many occurances there are of each individual
+ * metric type stored in the MySQL database.
+ *
+ * Goal metrics, metrics of the format .*_[0-9]+ and 'done...' metrics are grouped together.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getIndividualMetricsSummary($fetch = false)
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'asc',
+ $addPercentColumn = false, $labelKey = 'General_Metric',
+ $sizeColumns = array('estimated_size'));
+
+ $this->setIndividualSummaryFooterMessage($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each 'admin' table takes
+ * up in the MySQL database.
+ *
+ * An 'admin' table is a table that is not central to analytics functionality.
+ * So any table that isn't an archive table or a log table is an 'admin' table.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getAdminDataSummary($fetch = false)
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table');
+ $view->disableOffsetInformationAndPaginationControls();
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Utility function that creates and prepares a ViewDataTable for this plugin.
+ */
+ private function getDataTableView($function, $viewType = 'table', $orderDir = 'asc', $addPercentColumn = false,
+ $labelKey = 'DBStats_Table', $sizeColumns = array('data_size', 'index_size'),
+ $limit = 25)
+ {
+ $columnTranslations = array(
+ 'label' => Piwik_Translate($labelKey),
+ 'year' => Piwik_Translate('CoreHome_PeriodYear'),
+ 'data_size' => Piwik_Translate('DBStats_DataSize'),
+ 'index_size' => Piwik_Translate('DBStats_IndexSize'),
+ 'total_size' => Piwik_Translate('DBStats_TotalSize'),
+ 'row_count' => Piwik_Translate('DBStats_RowCount'),
+ 'percent_total' => '%&nbsp;' . Piwik_Translate('DBStats_DBSize'),
+ 'estimated_size' => Piwik_Translate('DBStats_EstimatedSize')
+ );
+
+ $view = Piwik_ViewDataTable::factory($viewType);
+ $view->init($this->pluginName, $function, "DBStats.$function");
+ $view->setSortedColumn('label', $orderDir);
+ $view->setLimit($limit);
+ $view->setHighlightSummaryRow(true);
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->disableTagCloud();
+ $view->disableShowAllColumns();
+ $view->alwaysShowSummaryRow();
+
+ // translate columns
+ foreach ($columnTranslations as $columnName => $translation) {
+ $view->setColumnTranslation($columnName, $translation);
+ }
+
+ // add total_size column (if necessary columns are present)
+ if (in_array('data_size', $sizeColumns) && in_array('index_size', $sizeColumns)) {
+ $getTotalTableSize = array($this, 'getTotalTableSize');
+ $view->queueFilter('ColumnCallbackAddColumn',
+ array(array('data_size', 'index_size'), 'total_size', $getTotalTableSize),
+ $runBeforeGenericFilters = true);
+
+ $sizeColumns[] = 'total_size';
+ }
+
+ $runPrettySizeFilterBeforeGeneric = false;
+ $fixedMemoryUnit = false;
+ if ($view instanceof Piwik_ViewDataTable_HtmlTable) // if displaying a table
+ {
+ $view->disableRowEvolution();
+
+ // add summary row only if displaying a table
+ $view->queueFilter('AddSummaryRow', array(0, Piwik_Translate('General_Total'), 'label', false),
+ $runBeforeGenericFilters = true);
+
+ // add other filters
+ if ($addPercentColumn && in_array('total_size', $sizeColumns)) {
+ $view->queueFilter('ColumnCallbackAddColumnPercentage',
+ array('percent_total', 'total_size', 'total_size', $quotientPrecision = 0, $shouldSkipRows = false,
+ $getDivisorFromSummaryRow = true),
+ $runBeforeGenericFilters = true);
+ $view->setSortedColumn('percent_total', $orderDir);
+ }
+ } else if ($view instanceof Piwik_ViewDataTable_GenerateGraphData) // if displaying a graph
+ {
+ if (in_array('total_size', $sizeColumns)) {
+ $view->setColumnsToDisplay(array('label', 'total_size'));
+
+ // when displaying a graph, we force sizes to be shown as the same unit so axis labels
+ // will be readable. NOTE: The unit should depend on the smallest value of the data table,
+ // however there's no way to know this information, short of creating a custom filter. For
+ // now, just assume KB.
+ $fixedMemoryUnit = 'K';
+ $view->setAxisYUnit(' K');
+
+ $view->setSortedColumn('total_size', 'desc');
+
+ $runPrettySizeFilterBeforeGeneric = true;
+ } else {
+ $view->setColumnsToDisplay(array('label', 'row_count'));
+ $view->setAxisYUnit(' ' . Piwik_Translate('General_Rows'));
+
+ $view->setSortedColumn('row_count', 'desc');
+ }
+ }
+
+ $getPrettySize = array('Piwik', 'getPrettySizeFromBytes');
+ $params = $fixedMemoryUnit === false ? array() : array($fixedMemoryUnit);
+ $view->queueFilter(
+ 'ColumnCallbackReplace', array($sizeColumns, $getPrettySize, $params), $runPrettySizeFilterBeforeGeneric);
+
+ // jqPlot will display &nbsp; as, well, '&nbsp;', so don't replace the spaces when rendering as a graph
+ if (!($view instanceof Piwik_ViewDataTable_GenerateGraphData)) {
+ $replaceSpaces = array($this, 'replaceColumnSpaces');
+ $view->queueFilter('ColumnCallbackReplace', array($sizeColumns, $replaceSpaces));
+ }
+
+ $getPrettyNumber = array('Piwik', 'getPrettyNumber');
+ $view->queueFilter('ColumnCallbackReplace', array(array('row_count'), $getPrettyNumber));
+
+ return $view;
+ }
+
+ /**
+ * Replaces spaces w/ &nbsp; for correct HTML output.
+ */
+ public function replaceColumnSpaces($value)
+ {
+ return str_replace(' ', '&nbsp;', $value);
+ }
+
+ /**
+ * Row callback function that calculates a tables total size.
+ */
+ public function getTotalTableSize($dataSize, $indexSize)
+ {
+ return $dataSize + $indexSize;
+ }
+
+ /**
+ * Column callback used to translate the column values in the database usage summary table.
+ */
+ public function translateSummarylabel($value)
+ {
+ static $valueToTranslationStr = array(
+ 'tracker_data' => 'DBStats_TrackerTables',
+ 'report_data' => 'DBStats_ReportTables',
+ 'metric_data' => 'DBStats_MetricTables',
+ 'other_data' => 'DBStats_OtherTables'
+ );
+
+ return isset($valueToTranslationStr[$value])
+ ? Piwik_Translate($valueToTranslationStr[$value])
+ : $value;
+ }
+
+ /**
+ * Sets the footer message for the Individual...Summary reports.
+ */
+ private function setIndividualSummaryFooterMessage($view)
+ {
+ $lastGenerated = Piwik_DBStats::getDateOfLastCachingRun();
+ if ($lastGenerated !== false) {
+ $view->setFooterMessage(Piwik_Translate('Mobile_LastUpdated', $lastGenerated));
+ }
+ }
}
diff --git a/plugins/DBStats/DBStats.php b/plugins/DBStats/DBStats.php
index 01a745c3ef..479596f30a 100644
--- a/plugins/DBStats/DBStats.php
+++ b/plugins/DBStats/DBStats.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_DBStats
*/
@@ -15,70 +15,70 @@
*/
class Piwik_DBStats extends Piwik_Plugin
{
- const TIME_OF_LAST_TASK_RUN_OPTION = 'dbstats_time_of_last_cache_task_run';
-
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('DBStats_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
+ const TIME_OF_LAST_TASK_RUN_OPTION = 'dbstats_time_of_last_cache_task_run';
- function getListHooksRegistered()
- {
- return array(
- 'AdminMenu.add' => 'addMenu',
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('DBStats_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ function getListHooksRegistered()
+ {
+ return array(
+ 'AdminMenu.add' => 'addMenu',
'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
- );
- }
-
- function addMenu()
- {
- Piwik_AddAdminSubMenu('CoreAdminHome_MenuDiagnostic', 'DBStats_DatabaseUsage',
- array('module' => 'DBStats', 'action' => 'index'),
- Piwik::isUserIsSuperUser(),
- $order = 9);
- }
+ );
+ }
+
+ function addMenu()
+ {
+ Piwik_AddAdminSubMenu('CoreAdminHome_MenuDiagnostic', 'DBStats_DatabaseUsage',
+ array('module' => 'DBStats', 'action' => 'index'),
+ Piwik::isUserIsSuperUser(),
+ $order = 9);
+ }
+
+ /**
+ * Gets all scheduled tasks executed by this plugin.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getScheduledTasks($notification)
+ {
+ $tasks = & $notification->getNotificationObject();
+
+ $cacheDataByArchiveNameReportsTask = new Piwik_ScheduledTask(
+ $this,
+ 'cacheDataByArchiveNameReports',
+ null,
+ new Piwik_ScheduledTime_Weekly(),
+ Piwik_ScheduledTask::LOWEST_PRIORITY
+ );
+ $tasks[] = $cacheDataByArchiveNameReportsTask;
+ }
+
+ /**
+ * Caches the intermediate DataTables used in the getIndividualReportsSummary and
+ * getIndividualMetricsSummary reports in the option table.
+ */
+ public function cacheDataByArchiveNameReports()
+ {
+ $api = Piwik_DBStats_API::getInstance();
+ $api->getIndividualReportsSummary(true);
+ $api->getIndividualMetricsSummary(true);
- /**
- * Gets all scheduled tasks executed by this plugin.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getScheduledTasks($notification)
- {
- $tasks = &$notification->getNotificationObject();
+ $now = Piwik_Date::now()->getLocalized("%longYear%, %shortMonth% %day%");
+ Piwik_SetOption(self::TIME_OF_LAST_TASK_RUN_OPTION, $now);
+ }
- $cacheDataByArchiveNameReportsTask = new Piwik_ScheduledTask(
- $this,
- 'cacheDataByArchiveNameReports',
- null,
- new Piwik_ScheduledTime_Weekly(),
- Piwik_ScheduledTask::LOWEST_PRIORITY
- );
- $tasks[] = $cacheDataByArchiveNameReportsTask;
- }
-
- /**
- * Caches the intermediate DataTables used in the getIndividualReportsSummary and
- * getIndividualMetricsSummary reports in the option table.
- */
- public function cacheDataByArchiveNameReports()
- {
- $api = Piwik_DBStats_API::getInstance();
- $api->getIndividualReportsSummary(true);
- $api->getIndividualMetricsSummary(true);
-
- $now = Piwik_Date::now()->getLocalized("%longYear%, %shortMonth% %day%");
- Piwik_SetOption(self::TIME_OF_LAST_TASK_RUN_OPTION, $now);
- }
-
- /** Returns the date when the cacheDataByArchiveNameReports was last run. */
- public static function getDateOfLastCachingRun()
- {
- return Piwik_GetOption(self::TIME_OF_LAST_TASK_RUN_OPTION);
- }
+ /** Returns the date when the cacheDataByArchiveNameReports was last run. */
+ public static function getDateOfLastCachingRun()
+ {
+ return Piwik_GetOption(self::TIME_OF_LAST_TASK_RUN_OPTION);
+ }
}
diff --git a/plugins/DBStats/MySQLMetadataProvider.php b/plugins/DBStats/MySQLMetadataProvider.php
index ccb3ae2431..6240a24b19 100755
--- a/plugins/DBStats/MySQLMetadataProvider.php
+++ b/plugins/DBStats/MySQLMetadataProvider.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_DBStats
*/
@@ -13,374 +13,348 @@
* Utility class that provides general information about databases, including the size of
* the entire database, the size and row count of each table and the size and row count
* of each metric/report type currently stored.
- *
+ *
* This class will cache the table information it retrieves from the database. In order to
* issue a new query instead of using this cache, you must create a new instance of this type.
*/
class Piwik_DBStats_MySQLMetadataProvider
{
- /**
- * Cached MySQL table statuses. So we won't needlessly re-issue SHOW TABLE STATUS queries.
- */
- private $tableStatuses = null;
-
- /**
- * Constructor.
- */
- public function __construct()
- {
- // empty
- }
-
- /**
- * Gets general database info that is not specific to any table.
- *
- * @return array See http://dev.mysql.com/doc/refman/5.1/en/show-status.html .
- */
- public function getDBStatus()
- {
- if (function_exists('mysql_connect'))
- {
- $configDb = Piwik_Config::getInstance()->database;
- $link = mysql_connect($configDb['host'], $configDb['username'], $configDb['password']);
- $status = mysql_stat($link);
- mysql_close($link);
- $status = explode(" ", $status);
- }
- else
- {
- $fullStatus = Piwik_FetchAssoc('SHOW STATUS');
- if (empty($fullStatus))
- {
- throw new Exception('Error, SHOW STATUS failed');
- }
-
- $status = array(
- 'Uptime' => $fullStatus['Uptime']['Value'],
- 'Threads' => $fullStatus['Threads_running']['Value'],
- 'Questions' => $fullStatus['Questions']['Value'],
- 'Slow queries' => $fullStatus['Slow_queries']['Value'],
- 'Flush tables' => $fullStatus['Flush_commands']['Value'],
- 'Open tables' => $fullStatus['Open_tables']['Value'],
- 'Opens' => 'unavailable', // not available via SHOW STATUS
- 'Queries per second avg' => 'unavailable' // not available via SHOW STATUS
- );
- }
-
- return $status;
- }
-
- /**
- * Gets the MySQL table status of the requested Piwik table.
- *
- * @param string $table The name of the table. Should not be prefixed (ie, 'log_visit' is
- * correct, 'piwik_log_visit' is not).
- * @return array See http://dev.mysql.com/doc/refman/5.1/en/show-table-status.html .
- */
- public function getTableStatus( $table )
- {
- $prefixed = Piwik_Common::prefixTable($table);
-
- // if we've already gotten every table status, don't issue an uneeded query
- if (!is_null($this->tableStatuses) && isset($this->tableStatuses[$prefixed]))
- {
- return $this->tableStatuses[$prefixed];
- }
- else
- {
- return Piwik_FetchRow("SHOW TABLE STATUS LIKE ?", array($prefixed));
- }
- }
-
- /**
- * Gets the result of a SHOW TABLE STATUS query for every Piwik table in the DB.
- * Non-piwik tables are ignored.
- *
- * @param string $matchingRegex Regex used to filter out tables whose name doesn't
- * match it.
- * @return array The table information. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html
- * for specifics.
- */
- public function getAllTablesStatus( $matchingRegex = null )
- {
- if (is_null($this->tableStatuses))
- {
- $tablesPiwik = Piwik::getTablesInstalled();
-
- $this->tableStatuses = array();
- foreach(Piwik_FetchAll("SHOW TABLE STATUS") as $t)
- {
- if (in_array($t['Name'], $tablesPiwik))
- {
- $this->tableStatuses[$t['Name']] = $t;
- }
- }
- }
-
- if (is_null($matchingRegex))
- {
- return $this->tableStatuses;
- }
-
- $result = array();
- foreach ($this->tableStatuses as $status)
- {
- if (preg_match($matchingRegex, $status['Name']))
- {
- $result[] = $status;
- }
- }
- return $result;
- }
-
- /**
- * Returns table statuses for every log table.
- *
- * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
- */
- public function getAllLogTableStatus()
- {
- $regex = "/^".Piwik_Common::prefixTable('log_')."(?!profiling)/";
- return $this->getAllTablesStatus($regex);
- }
-
- /**
- * Returns table statuses for every numeric archive table.
- *
- * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
- */
- public function getAllNumericArchiveStatus()
- {
- $regex = "/^".Piwik_Common::prefixTable('archive_numeric')."_/";
- return $this->getAllTablesStatus($regex);
- }
-
- /**
- * Returns table statuses for every blob archive table.
- *
- * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
- */
- public function getAllBlobArchiveStatus()
- {
- $regex = "/^".Piwik_Common::prefixTable('archive_blob')."_/";
- return $this->getAllTablesStatus($regex);
- }
-
- /**
- * Retruns table statuses for every admin table.
- *
- * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
- */
- public function getAllAdminTableStatus()
- {
- $regex = "/^".Piwik_Common::prefixTable('')."(?!archive_|(?:log_(?!profiling)))/";
- return $this->getAllTablesStatus($regex);
- }
-
- /**
- * Returns a DataTable that lists the number of rows and the estimated amount of space
- * each blob archive type takes up in the database.
- *
- * Blob types are differentiated by name.
- *
- * @param bool $forceCache false to use the cached result, true to run the queries again and
- * cache the result.
- * @return Piwik_DataTable
- */
- public function getRowCountsAndSizeByBlobName( $forceCache = false )
- {
- $extraSelects = array("SUM(OCTET_LENGTH(value)) AS 'blob_size'", "SUM(LENGTH(name)) AS 'name_size'");
- $extraCols = array('blob_size', 'name_size');
- return $this->getRowCountsByArchiveName(
- $this->getAllBlobArchiveStatus(), 'getEstimatedBlobArchiveRowSize', $forceCache, $extraSelects,
- $extraCols);
- }
-
- /**
- * Returns a DataTable that lists the number of rows and the estimated amount of space
- * each metric archive type takes up in the database.
- *
- * Metric types are differentiated by name.
- *
- * @param bool $forceCache false to use the cached result, true to run the queries again and
- * cache the result.
- * @return Piwik_DataTable
- */
- public function getRowCountsAndSizeByMetricName( $forceCache = false )
- {
- return $this->getRowCountsByArchiveName(
- $this->getAllNumericArchiveStatus(), 'getEstimatedRowsSize', $forceCache);
- }
-
- /**
- * Utility function. Gets row count of a set of tables grouped by the 'name' column.
- * This is the implementation of the getRowCountsAndSizeBy... functions.
- */
- private function getRowCountsByArchiveName( $statuses, $getRowSizeMethod, $forceCache = false,
- $otherSelects = array(), $otherDataTableColumns = array() )
- {
- $extraCols = '';
- if (!empty($otherSelects))
- {
- $extraCols = ', '.implode(', ', $otherSelects);
- }
-
- $cols = array_merge(array('row_count'), $otherDataTableColumns);
-
- $dataTable = new Piwik_DataTable();
- foreach ($statuses as $status)
- {
- $dataTableOptionName = $this->getCachedOptionName($status['Name'], 'byArchiveName');
-
- // if option exists && !$forceCache, use the cached data, otherwise create the
- $cachedData = Piwik_GetOption($dataTableOptionName);
- if ($cachedData !== false && !$forceCache)
- {
- $table = new Piwik_DataTable();
- $table->addRowsFromSerializedArray($cachedData);
- }
- else
- {
- // otherwise, create data table & cache it
- $sql = "SELECT name as 'label', COUNT(*) as 'row_count'$extraCols FROM {$status['Name']} GROUP BY name";
-
- $table = new Piwik_DataTable();
- $table->addRowsFromSimpleArray(Piwik_FetchAll($sql));
-
- $reduceArchiveRowName = array($this, 'reduceArchiveRowName');
- $table->filter('GroupBy', array('label', $reduceArchiveRowName));
-
- $serializedTables = $table->getSerialized();
- $serializedTable = reset($serializedTables);
- Piwik_SetOption($dataTableOptionName, $serializedTable);
- }
-
- // add estimated_size column
- $getEstimatedSize = array($this, $getRowSizeMethod);
- $table->filter('ColumnCallbackAddColumn',
- array($cols, 'estimated_size', $getEstimatedSize, array($status)));
-
- $dataTable->addDataTable($table);
- destroy($table);
- }
- return $dataTable;
- }
-
- /**
- * Gets the estimated database size a count of rows takes in a table.
- */
- public function getEstimatedRowsSize( $row_count, $status )
- {
- if($status['Rows'] == 0)
- {
- return 0;
- }
- $avgRowSize = ($status['Data_length'] + $status['Index_length']) / $status['Rows'];
- return $avgRowSize * $row_count;
- }
-
- /**
- * Gets the estimated database size a count of rows in a blob_archive table. Depends on
- * the data table row to contain the size of all blobs & name strings in the row set it
- * represents.
- */
- public function getEstimatedBlobArchiveRowSize( $row_count, $blob_size, $name_size, $status )
- {
- // calculate the size of each fixed size column in a blob archive table
- static $fixedSizeColumnLength = null;
- if (is_null($fixedSizeColumnLength))
- {
- $fixedSizeColumnLength = 0;
- foreach (Piwik_FetchAll("SHOW COLUMNS FROM ".$status['Name']) as $column)
- {
- $columnType = $column['Type'];
-
- if (($paren = strpos($columnType, '(')) !== false)
- {
- $columnType = substr($columnType, 0, $paren);
- }
-
- $fixedSizeColumnLength += $this->sizeOfMySQLColumn($columnType);
- }
- }
- // calculate the average row size
- if($status['Rows'] == 0) {
- $avgRowSize = 0;
- } else {
- $avgRowSize = $status['Index_length'] / $status['Rows'] + $fixedSizeColumnLength;
- }
-
- // calculate the row set's size
- return $avgRowSize * $row_count + $blob_size + $name_size;
- }
-
- /** Returns the size in bytes of a fixed size MySQL data type. Returns 0 for unsupported data type. */
- private function sizeOfMySQLColumn( $columnType )
- {
- switch (strtolower($columnType))
- {
- case "tinyint":
- case "year":
- return 1;
- case "smallint":
- return 2;
- case "mediumint":
- case "date":
- case "time":
- return 3;
- case "int":
- case "float": // assumes precision isn't used
- case "timestamp":
- return 4;
- case "bigint":
- case "double":
- case "real":
- case "datetime":
- return 8;
- default:
- return 0;
- }
- }
-
- /**
- * Gets the option name used to cache the result of an intensive query.
- */
- private function getCachedOptionName( $tableName, $suffix )
- {
- return 'dbstats_cached_'.$tableName.'_'.$suffix;
- }
-
- /**
- * Reduces the given metric name. Used to simplify certain reports.
- *
- * Some metrics, like goal metrics, can have different string names. For goal metrics,
- * there's one name per goal ID. Grouping metrics and reports like these together
- * simplifies the tables that display them.
- *
- * This function makes goal names, 'done...' names and names of the format .*_[0-9]+
- * equivalent.
- */
- public function reduceArchiveRowName( $name )
- {
- // all 'done...' fields are considered the same
- if (strpos($name, 'done') === 0)
- {
- return 'done';
- }
-
- // check for goal id, if present (Goals_... reports should not be reduced here, just Goal_... ones)
- if (preg_match("/^Goal_(?:-?[0-9]+_)?(.*)/", $name, $matches))
- {
- $name = "Goal_*_".$matches[1];
- }
-
- // remove subtable id suffix, if present
- if (preg_match("/^(.*)_[0-9]+$/", $name, $matches))
- {
- $name = $matches[1]."_*";
- }
-
- return $name;
- }
+ /**
+ * Cached MySQL table statuses. So we won't needlessly re-issue SHOW TABLE STATUS queries.
+ */
+ private $tableStatuses = null;
+
+ /**
+ * Constructor.
+ */
+ public function __construct()
+ {
+ // empty
+ }
+
+ /**
+ * Gets general database info that is not specific to any table.
+ *
+ * @return array See http://dev.mysql.com/doc/refman/5.1/en/show-status.html .
+ */
+ public function getDBStatus()
+ {
+ if (function_exists('mysql_connect')) {
+ $configDb = Piwik_Config::getInstance()->database;
+ $link = mysql_connect($configDb['host'], $configDb['username'], $configDb['password']);
+ $status = mysql_stat($link);
+ mysql_close($link);
+ $status = explode(" ", $status);
+ } else {
+ $fullStatus = Piwik_FetchAssoc('SHOW STATUS');
+ if (empty($fullStatus)) {
+ throw new Exception('Error, SHOW STATUS failed');
+ }
+
+ $status = array(
+ 'Uptime' => $fullStatus['Uptime']['Value'],
+ 'Threads' => $fullStatus['Threads_running']['Value'],
+ 'Questions' => $fullStatus['Questions']['Value'],
+ 'Slow queries' => $fullStatus['Slow_queries']['Value'],
+ 'Flush tables' => $fullStatus['Flush_commands']['Value'],
+ 'Open tables' => $fullStatus['Open_tables']['Value'],
+ 'Opens' => 'unavailable', // not available via SHOW STATUS
+ 'Queries per second avg' => 'unavailable' // not available via SHOW STATUS
+ );
+ }
+
+ return $status;
+ }
+
+ /**
+ * Gets the MySQL table status of the requested Piwik table.
+ *
+ * @param string $table The name of the table. Should not be prefixed (ie, 'log_visit' is
+ * correct, 'piwik_log_visit' is not).
+ * @return array See http://dev.mysql.com/doc/refman/5.1/en/show-table-status.html .
+ */
+ public function getTableStatus($table)
+ {
+ $prefixed = Piwik_Common::prefixTable($table);
+
+ // if we've already gotten every table status, don't issue an uneeded query
+ if (!is_null($this->tableStatuses) && isset($this->tableStatuses[$prefixed])) {
+ return $this->tableStatuses[$prefixed];
+ } else {
+ return Piwik_FetchRow("SHOW TABLE STATUS LIKE ?", array($prefixed));
+ }
+ }
+
+ /**
+ * Gets the result of a SHOW TABLE STATUS query for every Piwik table in the DB.
+ * Non-piwik tables are ignored.
+ *
+ * @param string $matchingRegex Regex used to filter out tables whose name doesn't
+ * match it.
+ * @return array The table information. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html
+ * for specifics.
+ */
+ public function getAllTablesStatus($matchingRegex = null)
+ {
+ if (is_null($this->tableStatuses)) {
+ $tablesPiwik = Piwik::getTablesInstalled();
+
+ $this->tableStatuses = array();
+ foreach (Piwik_FetchAll("SHOW TABLE STATUS") as $t) {
+ if (in_array($t['Name'], $tablesPiwik)) {
+ $this->tableStatuses[$t['Name']] = $t;
+ }
+ }
+ }
+
+ if (is_null($matchingRegex)) {
+ return $this->tableStatuses;
+ }
+
+ $result = array();
+ foreach ($this->tableStatuses as $status) {
+ if (preg_match($matchingRegex, $status['Name'])) {
+ $result[] = $status;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Returns table statuses for every log table.
+ *
+ * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
+ */
+ public function getAllLogTableStatus()
+ {
+ $regex = "/^" . Piwik_Common::prefixTable('log_') . "(?!profiling)/";
+ return $this->getAllTablesStatus($regex);
+ }
+
+ /**
+ * Returns table statuses for every numeric archive table.
+ *
+ * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
+ */
+ public function getAllNumericArchiveStatus()
+ {
+ $regex = "/^" . Piwik_Common::prefixTable('archive_numeric') . "_/";
+ return $this->getAllTablesStatus($regex);
+ }
+
+ /**
+ * Returns table statuses for every blob archive table.
+ *
+ * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
+ */
+ public function getAllBlobArchiveStatus()
+ {
+ $regex = "/^" . Piwik_Common::prefixTable('archive_blob') . "_/";
+ return $this->getAllTablesStatus($regex);
+ }
+
+ /**
+ * Retruns table statuses for every admin table.
+ *
+ * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
+ */
+ public function getAllAdminTableStatus()
+ {
+ $regex = "/^" . Piwik_Common::prefixTable('') . "(?!archive_|(?:log_(?!profiling)))/";
+ return $this->getAllTablesStatus($regex);
+ }
+
+ /**
+ * Returns a DataTable that lists the number of rows and the estimated amount of space
+ * each blob archive type takes up in the database.
+ *
+ * Blob types are differentiated by name.
+ *
+ * @param bool $forceCache false to use the cached result, true to run the queries again and
+ * cache the result.
+ * @return Piwik_DataTable
+ */
+ public function getRowCountsAndSizeByBlobName($forceCache = false)
+ {
+ $extraSelects = array("SUM(OCTET_LENGTH(value)) AS 'blob_size'", "SUM(LENGTH(name)) AS 'name_size'");
+ $extraCols = array('blob_size', 'name_size');
+ return $this->getRowCountsByArchiveName(
+ $this->getAllBlobArchiveStatus(), 'getEstimatedBlobArchiveRowSize', $forceCache, $extraSelects,
+ $extraCols);
+ }
+
+ /**
+ * Returns a DataTable that lists the number of rows and the estimated amount of space
+ * each metric archive type takes up in the database.
+ *
+ * Metric types are differentiated by name.
+ *
+ * @param bool $forceCache false to use the cached result, true to run the queries again and
+ * cache the result.
+ * @return Piwik_DataTable
+ */
+ public function getRowCountsAndSizeByMetricName($forceCache = false)
+ {
+ return $this->getRowCountsByArchiveName(
+ $this->getAllNumericArchiveStatus(), 'getEstimatedRowsSize', $forceCache);
+ }
+
+ /**
+ * Utility function. Gets row count of a set of tables grouped by the 'name' column.
+ * This is the implementation of the getRowCountsAndSizeBy... functions.
+ */
+ private function getRowCountsByArchiveName($statuses, $getRowSizeMethod, $forceCache = false,
+ $otherSelects = array(), $otherDataTableColumns = array())
+ {
+ $extraCols = '';
+ if (!empty($otherSelects)) {
+ $extraCols = ', ' . implode(', ', $otherSelects);
+ }
+
+ $cols = array_merge(array('row_count'), $otherDataTableColumns);
+
+ $dataTable = new Piwik_DataTable();
+ foreach ($statuses as $status) {
+ $dataTableOptionName = $this->getCachedOptionName($status['Name'], 'byArchiveName');
+
+ // if option exists && !$forceCache, use the cached data, otherwise create the
+ $cachedData = Piwik_GetOption($dataTableOptionName);
+ if ($cachedData !== false && !$forceCache) {
+ $table = new Piwik_DataTable();
+ $table->addRowsFromSerializedArray($cachedData);
+ } else {
+ // otherwise, create data table & cache it
+ $sql = "SELECT name as 'label', COUNT(*) as 'row_count'$extraCols FROM {$status['Name']} GROUP BY name";
+
+ $table = new Piwik_DataTable();
+ $table->addRowsFromSimpleArray(Piwik_FetchAll($sql));
+
+ $reduceArchiveRowName = array($this, 'reduceArchiveRowName');
+ $table->filter('GroupBy', array('label', $reduceArchiveRowName));
+
+ $serializedTables = $table->getSerialized();
+ $serializedTable = reset($serializedTables);
+ Piwik_SetOption($dataTableOptionName, $serializedTable);
+ }
+
+ // add estimated_size column
+ $getEstimatedSize = array($this, $getRowSizeMethod);
+ $table->filter('ColumnCallbackAddColumn',
+ array($cols, 'estimated_size', $getEstimatedSize, array($status)));
+
+ $dataTable->addDataTable($table);
+ destroy($table);
+ }
+ return $dataTable;
+ }
+
+ /**
+ * Gets the estimated database size a count of rows takes in a table.
+ */
+ public function getEstimatedRowsSize($row_count, $status)
+ {
+ if ($status['Rows'] == 0) {
+ return 0;
+ }
+ $avgRowSize = ($status['Data_length'] + $status['Index_length']) / $status['Rows'];
+ return $avgRowSize * $row_count;
+ }
+
+ /**
+ * Gets the estimated database size a count of rows in a blob_archive table. Depends on
+ * the data table row to contain the size of all blobs & name strings in the row set it
+ * represents.
+ */
+ public function getEstimatedBlobArchiveRowSize($row_count, $blob_size, $name_size, $status)
+ {
+ // calculate the size of each fixed size column in a blob archive table
+ static $fixedSizeColumnLength = null;
+ if (is_null($fixedSizeColumnLength)) {
+ $fixedSizeColumnLength = 0;
+ foreach (Piwik_FetchAll("SHOW COLUMNS FROM " . $status['Name']) as $column) {
+ $columnType = $column['Type'];
+
+ if (($paren = strpos($columnType, '(')) !== false) {
+ $columnType = substr($columnType, 0, $paren);
+ }
+
+ $fixedSizeColumnLength += $this->sizeOfMySQLColumn($columnType);
+ }
+ }
+ // calculate the average row size
+ if ($status['Rows'] == 0) {
+ $avgRowSize = 0;
+ } else {
+ $avgRowSize = $status['Index_length'] / $status['Rows'] + $fixedSizeColumnLength;
+ }
+
+ // calculate the row set's size
+ return $avgRowSize * $row_count + $blob_size + $name_size;
+ }
+
+ /** Returns the size in bytes of a fixed size MySQL data type. Returns 0 for unsupported data type. */
+ private function sizeOfMySQLColumn($columnType)
+ {
+ switch (strtolower($columnType)) {
+ case "tinyint":
+ case "year":
+ return 1;
+ case "smallint":
+ return 2;
+ case "mediumint":
+ case "date":
+ case "time":
+ return 3;
+ case "int":
+ case "float": // assumes precision isn't used
+ case "timestamp":
+ return 4;
+ case "bigint":
+ case "double":
+ case "real":
+ case "datetime":
+ return 8;
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * Gets the option name used to cache the result of an intensive query.
+ */
+ private function getCachedOptionName($tableName, $suffix)
+ {
+ return 'dbstats_cached_' . $tableName . '_' . $suffix;
+ }
+
+ /**
+ * Reduces the given metric name. Used to simplify certain reports.
+ *
+ * Some metrics, like goal metrics, can have different string names. For goal metrics,
+ * there's one name per goal ID. Grouping metrics and reports like these together
+ * simplifies the tables that display them.
+ *
+ * This function makes goal names, 'done...' names and names of the format .*_[0-9]+
+ * equivalent.
+ */
+ public function reduceArchiveRowName($name)
+ {
+ // all 'done...' fields are considered the same
+ if (strpos($name, 'done') === 0) {
+ return 'done';
+ }
+
+ // check for goal id, if present (Goals_... reports should not be reduced here, just Goal_... ones)
+ if (preg_match("/^Goal_(?:-?[0-9]+_)?(.*)/", $name, $matches)) {
+ $name = "Goal_*_" . $matches[1];
+ }
+
+ // remove subtable id suffix, if present
+ if (preg_match("/^(.*)_[0-9]+$/", $name, $matches)) {
+ $name = $matches[1] . "_*";
+ }
+
+ return $name;
+ }
}
diff --git a/plugins/DBStats/templates/index.tpl b/plugins/DBStats/templates/index.tpl
index efe3550e89..5c85eea7d9 100755
--- a/plugins/DBStats/templates/index.tpl
+++ b/plugins/DBStats/templates/index.tpl
@@ -2,145 +2,154 @@
{loadJavascriptTranslations plugins='CoreAdminHome CoreHome'}
{literal}
-<style>
-.dbstatsTable {
- display: inline-block;
-}
-.dbstatsTable>tbody>tr>td:first-child {
- width: 550px;
-}
-.dbstatsTable h2 {
- width: 500px;
-}
-.adminTable.dbstatsTable a {
- color: black;
- text-decoration: underline;
-}
-</style>
+ <style>
+ .dbstatsTable {
+ display: inline-block;
+ }
+
+ .dbstatsTable>tbody>tr>td:first-child {
+ width: 550px;
+ }
+
+ .dbstatsTable h2 {
+ width: 500px;
+ }
+
+ .adminTable.dbstatsTable a {
+ color: black;
+ text-decoration: underline;
+ }
+ </style>
{/literal}
<a name="databaseUsageSummary"></a>
<h2>{'DBStats_DatabaseUsage'|translate}</h2>
<p>
- {'DBStats_MainDescription'|translate:$totalSpaceUsed}<br/>
- {'DBStats_LearnMore'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>Piwik Auto Archiving</a>"}<br/>
- <br/>
+ {'DBStats_MainDescription'|translate:$totalSpaceUsed}<br/>
+ {'DBStats_LearnMore'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>Piwik Auto Archiving</a>"}
+ <br/>
+ <br/>
</p>
<table class="adminTable dbstatsTable">
- <tbody>
- <tr>
- <td>{$databaseUsageSummary}</td>
- <td>
- <h3 style="margin-top:0">{'General_GeneralInformation'|translate}</h3><br/>
- <p style="font-size:1.4em;padding-left:21px;line-height:1.8em">
- <strong><em>{$userCount}</strong></em>&nbsp;{if $userCount == 1}{'UsersManager_User'|translate}{else}{'UsersManager_MenuUsers'|translate}{/if}<br/>
- <strong><em>{$siteCount}</strong></em>&nbsp;{if $siteCount == 1}{'General_Website'|translate}{else}{'Referers_Websites'|translate}{/if}
- </p><br/>
- {capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteDataSettings'|translate}{/capture}
- <h3 style="margin-top:0">{'PrivacyManager_DeleteDataSettings'|translate}</h3><br/>
- <p>
- {'PrivacyManager_DeleteDataDescription'|translate}
- <br/>
- <a href='{url module="PrivacyManager" action="privacySettings"}#deleteLogsAnchor'>
- {'PrivacyManager_ClickHereSettings'|translate:"'$clickDeleteLogSettings'"}
- </a>
- </p>
- </td>
- </tr>
- </tbody>
+ <tbody>
+ <tr>
+ <td>{$databaseUsageSummary}</td>
+ <td>
+ <h3 style="margin-top:0">{'General_GeneralInformation'|translate}</h3><br/>
+
+ <p style="font-size:1.4em;padding-left:21px;line-height:1.8em">
+ <strong><em>{$userCount}</strong></em>&nbsp;{if $userCount == 1}{'UsersManager_User'|translate}{else}{'UsersManager_MenuUsers'|translate}{/if}
+ <br/>
+ <strong><em>{$siteCount}</strong></em>&nbsp;{if $siteCount == 1}{'General_Website'|translate}{else}{'Referers_Websites'|translate}{/if}
+ </p><br/>
+ {capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteDataSettings'|translate}{/capture}
+ <h3 style="margin-top:0">{'PrivacyManager_DeleteDataSettings'|translate}</h3><br/>
+
+ <p>
+ {'PrivacyManager_DeleteDataDescription'|translate}
+ <br/>
+ <a href='{url module="PrivacyManager" action="privacySettings"}#deleteLogsAnchor'>
+ {'PrivacyManager_ClickHereSettings'|translate:"'$clickDeleteLogSettings'"}
+ </a>
+ </p>
+ </td>
+ </tr>
+ </tbody>
</table>
<br/>
<a name="trackerDataSummary"></a>
<table class="adminTable dbstatsTable">
- <tbody>
- <tr>
- <td>
- <h2>{'DBStats_TrackerTables'|translate}</h2>
- {$trackerDataSummary}
- </td>
- <td>&nbsp;</td>
- </tr>
- </tbody>
+ <tbody>
+ <tr>
+ <td>
+ <h2>{'DBStats_TrackerTables'|translate}</h2>
+ {$trackerDataSummary}
+ </td>
+ <td>&nbsp;</td>
+ </tr>
+ </tbody>
</table>
<a name="reportDataSummary"></a>
<table class="adminTable dbstatsTable">
- <tbody>
- <tr>
- <td>
- <h2>{'DBStats_ReportTables'|translate}</h2>
- {$reportDataSummary}
- </td>
- <td>
- <h2>{'General_Reports'|translate}</h2>
- <div class="ajaxLoad" action="getIndividualReportsSummary">
- <span class="loadingPiwik"><img src="themes/default/images/loading-blue.gif" />{'General_LoadingData'|translate}</span>
- </div>
- </td>
- </tr>
- </tbody>
+ <tbody>
+ <tr>
+ <td>
+ <h2>{'DBStats_ReportTables'|translate}</h2>
+ {$reportDataSummary}
+ </td>
+ <td>
+ <h2>{'General_Reports'|translate}</h2>
+
+ <div class="ajaxLoad" action="getIndividualReportsSummary">
+ <span class="loadingPiwik"><img src="themes/default/images/loading-blue.gif"/>{'General_LoadingData'|translate}</span>
+ </div>
+ </td>
+ </tr>
+ </tbody>
</table>
<a name="metricDataSummary"></a>
<table class="adminTable dbstatsTable">
- <tbody>
- <tr>
- <td>
- <h2>{'DBStats_MetricTables'|translate}</h2>
- {$metricDataSummary}
- </td>
- <td>
- <h2>{'General_Metrics'|translate}</h2>
- <div class="ajaxLoad" action="getIndividualMetricsSummary">
- <span class="loadingPiwik"><img src="themes/default/images/loading-blue.gif" />{'General_LoadingData'|translate}</span>
- </div>
- </td>
- </tr>
- </tbody>
+ <tbody>
+ <tr>
+ <td>
+ <h2>{'DBStats_MetricTables'|translate}</h2>
+ {$metricDataSummary}
+ </td>
+ <td>
+ <h2>{'General_Metrics'|translate}</h2>
+
+ <div class="ajaxLoad" action="getIndividualMetricsSummary">
+ <span class="loadingPiwik"><img src="themes/default/images/loading-blue.gif"/>{'General_LoadingData'|translate}</span>
+ </div>
+ </td>
+ </tr>
+ </tbody>
</table>
<a name="adminDataSummary"></a>
<table class="adminTable dbstatsTable">
- <tbody>
- <tr>
- <td>
- <h2>{'DBStats_OtherTables'|translate}</h2>
- {$adminDataSummary}
- </td>
- <td>&nbsp;</td>
- </tr>
- </tbody>
+ <tbody>
+ <tr>
+ <td>
+ <h2>{'DBStats_OtherTables'|translate}</h2>
+ {$adminDataSummary}
+ </td>
+ <td>&nbsp;</td>
+ </tr>
+ </tbody>
</table>
{literal}
-<script type="text/javascript">
-(function( $ ){
- $(document).ready(function() {
- $('.ajaxLoad').each(function() {
- var self = this;
- var action = $(this).attr('action');
-
- // build & execute AJAX request
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams({
- module: 'DBStats',
- action: action,
- viewDataTable: 'table'
- }, 'get');
- ajaxRequest.setCallback(
- function (data) {
- $('.loadingPiwik', self).remove();
- $(self).html(data);
- }
- );
- ajaxRequest.setFormat('html');
- ajaxRequest.send(false);
- });
- });
-})( jQuery );
-</script>
+ <script type="text/javascript">
+ (function ($) {
+ $(document).ready(function () {
+ $('.ajaxLoad').each(function () {
+ var self = this;
+ var action = $(this).attr('action');
+
+ // build & execute AJAX request
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams({
+ module: 'DBStats',
+ action: action,
+ viewDataTable: 'table'
+ }, 'get');
+ ajaxRequest.setCallback(
+ function (data) {
+ $('.loadingPiwik', self).remove();
+ $(self).html(data);
+ }
+ );
+ ajaxRequest.setFormat('html');
+ ajaxRequest.send(false);
+ });
+ });
+ })(jQuery);
+ </script>
{/literal}
{include file="CoreAdminHome/templates/footer.tpl"}
diff --git a/plugins/Dashboard/Controller.php b/plugins/Dashboard/Controller.php
index cb4e3fe1ee..be49ce1ade 100644
--- a/plugins/Dashboard/Controller.php
+++ b/plugins/Dashboard/Controller.php
@@ -23,7 +23,7 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
$view->availableWidgets = Piwik_Common::json_encode(Piwik_GetWidgetsList());
$view->availableLayouts = $this->getAvailableLayouts();
- $view->dashboardId = Piwik_Common::getRequestVar('idDashboard', 1, 'int');
+ $view->dashboardId = Piwik_Common::getRequestVar('idDashboard', 1, 'int');
$view->dashboardLayout = $this->getLayout($view->dashboardId);
return $view;
@@ -38,7 +38,7 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
public function index()
{
- $view = $this->_getDashboardView('standalone');
+ $view = $this->_getDashboardView('standalone');
$view->dashboards = array();
if (!Piwik::isUserIsAnonymous()) {
$login = Piwik::getCurrentUserLogin();
@@ -52,7 +52,7 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
{
$this->checkTokenInUrl();
- Piwik_DataTable_Renderer_Json::sendHeaderJSON();
+ Piwik_DataTable_Renderer_Json::sendHeaderJSON();
echo Piwik_Common::json_encode(Piwik_GetWidgetsList());
}
@@ -73,10 +73,10 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
public function resetLayout()
{
$this->checkTokenInUrl();
- $layout = $this->getDefaultLayout();
+ $layout = $this->getDefaultLayout();
$idDashboard = Piwik_Common::getRequestVar('idDashboard', 1, 'int');
if (Piwik::isUserIsAnonymous()) {
- $session = new Piwik_Session_Namespace("Piwik_Dashboard");
+ $session = new Piwik_Session_Namespace("Piwik_Dashboard");
$session->dashboardLayout = $layout;
$session->setExpirationSeconds(1800);
} else {
@@ -88,14 +88,14 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
* Records the layout in the DB for the given user.
*
* @param string $login
- * @param int $idDashboard
+ * @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=?',
- Piwik_Common::prefixTable('user_dashboard'));
+ $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?',
+ Piwik_Common::prefixTable('user_dashboard'));
Piwik_Query($query, $paramsBind);
}
@@ -103,14 +103,14 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
* Updates the name of a dashboard
*
* @param string $login
- * @param int $idDashboard
+ * @param int $idDashboard
* @param string $name
*/
protected function updateDashboardName($login, $idDashboard, $name)
{
$paramsBind = array($name, $login, $idDashboard);
- $query = sprintf('UPDATE %s SET name = ? WHERE login = ? AND iddashboard = ?',
- Piwik_Common::prefixTable('user_dashboard'));
+ $query = sprintf('UPDATE %s SET name = ? WHERE login = ? AND iddashboard = ?',
+ Piwik_Common::prefixTable('user_dashboard'));
Piwik_Query($query, $paramsBind);
}
@@ -119,16 +119,16 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
* Parameters must be checked BEFORE this function call
*
* @param string $login
- * @param int $idDashboard
+ * @param int $idDashboard
*
* @return bool
*/
protected function _getLayoutForUser($login, $idDashboard)
{
$paramsBind = array($login, $idDashboard);
- $query = sprintf('SELECT layout FROM %s WHERE login = ? AND iddashboard = ?',
- Piwik_Common::prefixTable('user_dashboard'));
- $return = Piwik_FetchAll($query, $paramsBind);
+ $query = sprintf('SELECT layout FROM %s WHERE login = ? AND iddashboard = ?',
+ Piwik_Common::prefixTable('user_dashboard'));
+ $return = Piwik_FetchAll($query, $paramsBind);
if (count($return) == 0) {
return false;
@@ -153,7 +153,7 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
// first layout can't be removed
if ($idDashboard != 1) {
$query = sprintf('DELETE FROM %s WHERE iddashboard = ? AND login = ?',
- Piwik_Common::prefixTable('user_dashboard'));
+ Piwik_Common::prefixTable('user_dashboard'));
Piwik_Query($query, array($idDashboard, Piwik::getCurrentUserLogin()));
}
}
@@ -165,8 +165,8 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
{
$this->checkTokenInUrl();
if (Piwik::isUserIsAnonymous()) {
- echo '[]';
- return;
+ echo '[]';
+ return;
}
$login = Piwik::getCurrentUserLogin();
@@ -185,14 +185,14 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
$this->checkTokenInUrl();
if (Piwik::isUserIsAnonymous()) {
- echo '0';
- return;
+ echo '0';
+ return;
}
- $user = Piwik::getCurrentUserLogin();
+ $user = Piwik::getCurrentUserLogin();
$nextId = $this->getNextIdDashboard($user);
- $name = urldecode(Piwik_Common::getRequestVar('name', '', 'string'));
- $type = urldecode(Piwik_Common::getRequestVar('type', 'default', 'string'));
+ $name = urldecode(Piwik_Common::getRequestVar('name', '', 'string'));
+ $type = urldecode(Piwik_Common::getRequestVar('type', 'default', 'string'));
$layout = '{}';
if ($type == 'default') {
@@ -200,50 +200,50 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
}
$query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)',
- Piwik_Common::prefixTable('user_dashboard'));
+ Piwik_Common::prefixTable('user_dashboard'));
Piwik_Query($query, array($user, $nextId, $name, $layout));
Piwik_DataTable_Renderer_Json::sendHeaderJSON();
echo Piwik_Common::json_encode($nextId);
}
- private function getNextIdDashboard($login)
- {
- $nextIdQuery = sprintf('SELECT MAX(iddashboard)+1 FROM %s WHERE login = ?',
- Piwik_Common::prefixTable('user_dashboard'));
- $nextId = Piwik_FetchOne($nextIdQuery, array($login));
+ private function getNextIdDashboard($login)
+ {
+ $nextIdQuery = sprintf('SELECT MAX(iddashboard)+1 FROM %s WHERE login = ?',
+ Piwik_Common::prefixTable('user_dashboard'));
+ $nextId = Piwik_FetchOne($nextIdQuery, array($login));
- if (empty($nextId)) {
- $nextId = 1;
- return $nextId;
- }
- return $nextId;
- }
+ if (empty($nextId)) {
+ $nextId = 1;
+ return $nextId;
+ }
+ return $nextId;
+ }
- public function copyDashboardToUser()
+ public function copyDashboardToUser()
{
$this->checkTokenInUrl();
if (!Piwik::isUserIsSuperUser()) {
- echo '0';
- return;
+ echo '0';
+ return;
}
- $login = Piwik::getCurrentUserLogin();
- $name = urldecode(Piwik_Common::getRequestVar('name', '', 'string'));
- $user = urldecode(Piwik_Common::getRequestVar('user', '', 'string'));
+ $login = Piwik::getCurrentUserLogin();
+ $name = urldecode(Piwik_Common::getRequestVar('name', '', 'string'));
+ $user = urldecode(Piwik_Common::getRequestVar('user', '', 'string'));
$idDashboard = Piwik_Common::getRequestVar('dashboardId', 0, 'int');
- $layout = $this->_getLayoutForUser($login, $idDashboard);
+ $layout = $this->_getLayoutForUser($login, $idDashboard);
- if($layout !== false) {
- $nextId = $this->getNextIdDashboard($user);
+ if ($layout !== false) {
+ $nextId = $this->getNextIdDashboard($user);
- $query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)',
- Piwik_Common::prefixTable('user_dashboard'));
- Piwik_Query($query, array($user, $nextId, $name, $layout));
+ $query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)',
+ Piwik_Common::prefixTable('user_dashboard'));
+ Piwik_Query($query, array($user, $nextId, $name, $layout));
- Piwik_DataTable_Renderer_Json::sendHeaderJSON();
- echo Piwik_Common::json_encode($nextId);
- return;
+ Piwik_DataTable_Renderer_Json::sendHeaderJSON();
+ echo Piwik_Common::json_encode($nextId);
+ return;
}
}
@@ -256,11 +256,11 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
{
$this->checkTokenInUrl();
- $layout = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('layout'));
+ $layout = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('layout'));
$idDashboard = Piwik_Common::getRequestVar('idDashboard', 1, 'int');
- $name = Piwik_Common::getRequestVar('name', '', 'string');
+ $name = Piwik_Common::getRequestVar('name', '', 'string');
if (Piwik::isUserIsAnonymous()) {
- $session = new Piwik_Session_Namespace("Piwik_Dashboard");
+ $session = new Piwik_Session_Namespace("Piwik_Dashboard");
$session->dashboardLayout = $layout;
$session->setExpirationSeconds(1800);
} else {
@@ -279,10 +279,10 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
$this->checkTokenInUrl();
if (Piwik::isUserIsSuperUser()) {
- $layout = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('layout'));
+ $layout = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('layout'));
$paramsBind = array('', '1', $layout, $layout);
- $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?',
- Piwik_Common::prefixTable('user_dashboard'));
+ $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?',
+ Piwik_Common::prefixTable('user_dashboard'));
Piwik_Query($query, $paramsBind);
}
}
@@ -344,7 +344,7 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
foreach ($row as $widgetId => $widget) {
if (isset($widget->parameters->module)) {
- $controllerName = $widget->parameters->module;
+ $controllerName = $widget->parameters->module;
$controllerAction = $widget->parameters->action;
if (!Piwik_IsWidgetDefined($controllerName, $controllerAction)) {
unset($row[$widgetId]);
@@ -363,18 +363,15 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
$defaultLayout = $this->_getLayoutForUser('', 1);
if (empty($defaultLayout)) {
- $topWidget = '';
- if (Piwik::isUserIsSuperUser())
- {
- $topWidget = '{"uniqueId":"widgetCoreHomegetDonateForm",'
- . '"parameters":{"module":"CoreHome","action":"getDonateForm"}},';
- }
- else
- {
- $topWidget = '{"uniqueId":"widgetCoreHomegetPromoVideo",'
- . '"parameters":{"module":"CoreHome","action":"getPromoVideo"}},';
- }
-
+ $topWidget = '';
+ if (Piwik::isUserIsSuperUser()) {
+ $topWidget = '{"uniqueId":"widgetCoreHomegetDonateForm",'
+ . '"parameters":{"module":"CoreHome","action":"getDonateForm"}},';
+ } else {
+ $topWidget = '{"uniqueId":"widgetCoreHomegetPromoVideo",'
+ . '"parameters":{"module":"CoreHome","action":"getPromoVideo"}},';
+ }
+
$defaultLayout = '[
[
{"uniqueId":"widgetVisitsSummarygetEvolutionGraphcolumnsArray","parameters":{"module":"VisitsSummary","action":"getEvolutionGraph","columns":"nb_visits"}},
@@ -382,7 +379,7 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
{"uniqueId":"widgetVisitorInterestgetNumberOfVisitsPerVisitDuration","parameters":{"module":"VisitorInterest","action":"getNumberOfVisitsPerVisitDuration"}}
],
[
- '.$topWidget.'
+ ' . $topWidget . '
{"uniqueId":"widgetReferersgetKeywords","parameters":{"module":"Referers","action":"getKeywords"}},
{"uniqueId":"widgetReferersgetWebsites","parameters":{"module":"Referers","action":"getWebsites"}}
],
diff --git a/plugins/Dashboard/Dashboard.php b/plugins/Dashboard/Dashboard.php
index 10afa65382..50a71bf28c 100644
--- a/plugins/Dashboard/Dashboard.php
+++ b/plugins/Dashboard/Dashboard.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Dashboard
*/
@@ -14,153 +14,146 @@
*/
class Piwik_Dashboard extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('Dashboard_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- public function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'UsersManager.deleteUser' => 'deleteDashboardLayout',
- 'Menu.add' => 'addMenus',
- 'TopMenu.add' => 'addTopMenu',
- );
- }
-
- public static function getAllDashboards($login) {
- $dashboards = Piwik_FetchAll('SELECT iddashboard, name
- FROM '.Piwik_Common::prefixTable('user_dashboard') .
- ' WHERE login = ? ORDER BY iddashboard', array($login));
- $pos = 0;
- $nameless = 1;
- foreach ($dashboards AS &$dashboard) {
- if (empty($dashboard['name'])) {
- $dashboard['name'] = Piwik_Translate('Dashboard_DashboardOf', $login);
- if($nameless > 1) {
- $dashboard['name'] .= " ($nameless)";
- }
- if(empty($dashboard['layout']))
- {
- $layout = '[]';
- }
- else
- {
- $layout = html_entity_decode($dashboard['layout']);
- $layout = str_replace("\\\"", "\"", $layout);
- }
- $dashboard['layout'] = Piwik_Common::json_decode($layout);
- $nameless++;
- }
- $dashboard['name'] = Piwik_Common::unsanitizeInputValue($dashboard['name']);
- $pos++;
- }
- return $dashboards;
- }
-
- public function addMenus()
- {
- Piwik_AddMenu('Dashboard_Dashboard', '', array('module' => 'Dashboard', 'action' => 'embeddedIndex', 'idDashboard' => 1), true, 5);
-
- if (!Piwik::isUserIsAnonymous()) {
- $login = Piwik::getCurrentUserLogin();
-
- $dashboards = self::getAllDashboards($login);
- if (count($dashboards) > 1)
- {
- $pos = 0;
- foreach ($dashboards AS $dashboard) {
- Piwik_AddMenu('Dashboard_Dashboard', $dashboard['name'], array('module' => 'Dashboard', 'action' => 'embeddedIndex', 'idDashboard' => $dashboard['iddashboard']), true, $pos);
- $pos++;
- }
- }
-
- }
- }
-
- public function addTopMenu()
- {
- $tooltip = false;
- try
- {
- $idSite = Piwik_Common::getRequestVar('idSite');
- $tooltip = Piwik_Translate('Dashboard_TopLinkTooltip', Piwik_Site::getNameFor($idSite));
- }
- catch (Exception $ex)
- {
- // if no idSite parameter, show no tooltip
- }
-
- $urlParams = array('module' => 'CoreHome', 'action' => 'index');
- Piwik_AddTopMenu('General_Dashboard', $urlParams, true, 1, $isHTML = false, $tooltip);
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
-
- $jsFiles[] = "plugins/Dashboard/templates/widgetMenu.js";
- $jsFiles[] = "libs/javascript/json2.js";
- $jsFiles[] = "plugins/Dashboard/templates/dashboardObject.js";
- $jsFiles[] = "plugins/Dashboard/templates/dashboardWidget.js";
- $jsFiles[] = "plugins/Dashboard/templates/dashboard.js";
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "plugins/CoreHome/templates/datatable.css";
- $cssFiles[] = "plugins/Dashboard/templates/dashboard.css";
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function deleteDashboardLayout($notification)
- {
- $userLogin = $notification->getNotificationObject();
- Piwik_Query('DELETE FROM ' . Piwik_Common::prefixTable('user_dashboard') . ' WHERE login = ?', array($userLogin));
- }
-
- public function install()
- {
- // we catch the exception
- try{
- $sql = "CREATE TABLE ". Piwik_Common::prefixTable('user_dashboard')." (
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('Dashboard_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'UsersManager.deleteUser' => 'deleteDashboardLayout',
+ 'Menu.add' => 'addMenus',
+ 'TopMenu.add' => 'addTopMenu',
+ );
+ }
+
+ public static function getAllDashboards($login)
+ {
+ $dashboards = Piwik_FetchAll('SELECT iddashboard, name
+ FROM ' . Piwik_Common::prefixTable('user_dashboard') .
+ ' WHERE login = ? ORDER BY iddashboard', array($login));
+ $pos = 0;
+ $nameless = 1;
+ foreach ($dashboards AS &$dashboard) {
+ if (empty($dashboard['name'])) {
+ $dashboard['name'] = Piwik_Translate('Dashboard_DashboardOf', $login);
+ if ($nameless > 1) {
+ $dashboard['name'] .= " ($nameless)";
+ }
+ if (empty($dashboard['layout'])) {
+ $layout = '[]';
+ } else {
+ $layout = html_entity_decode($dashboard['layout']);
+ $layout = str_replace("\\\"", "\"", $layout);
+ }
+ $dashboard['layout'] = Piwik_Common::json_decode($layout);
+ $nameless++;
+ }
+ $dashboard['name'] = Piwik_Common::unsanitizeInputValue($dashboard['name']);
+ $pos++;
+ }
+ return $dashboards;
+ }
+
+ public function addMenus()
+ {
+ Piwik_AddMenu('Dashboard_Dashboard', '', array('module' => 'Dashboard', 'action' => 'embeddedIndex', 'idDashboard' => 1), true, 5);
+
+ if (!Piwik::isUserIsAnonymous()) {
+ $login = Piwik::getCurrentUserLogin();
+
+ $dashboards = self::getAllDashboards($login);
+ if (count($dashboards) > 1) {
+ $pos = 0;
+ foreach ($dashboards AS $dashboard) {
+ Piwik_AddMenu('Dashboard_Dashboard', $dashboard['name'], array('module' => 'Dashboard', 'action' => 'embeddedIndex', 'idDashboard' => $dashboard['iddashboard']), true, $pos);
+ $pos++;
+ }
+ }
+
+ }
+ }
+
+ public function addTopMenu()
+ {
+ $tooltip = false;
+ try {
+ $idSite = Piwik_Common::getRequestVar('idSite');
+ $tooltip = Piwik_Translate('Dashboard_TopLinkTooltip', Piwik_Site::getNameFor($idSite));
+ } catch (Exception $ex) {
+ // if no idSite parameter, show no tooltip
+ }
+
+ $urlParams = array('module' => 'CoreHome', 'action' => 'index');
+ Piwik_AddTopMenu('General_Dashboard', $urlParams, true, 1, $isHTML = false, $tooltip);
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+
+ $jsFiles[] = "plugins/Dashboard/templates/widgetMenu.js";
+ $jsFiles[] = "libs/javascript/json2.js";
+ $jsFiles[] = "plugins/Dashboard/templates/dashboardObject.js";
+ $jsFiles[] = "plugins/Dashboard/templates/dashboardWidget.js";
+ $jsFiles[] = "plugins/Dashboard/templates/dashboard.js";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+
+ $cssFiles[] = "plugins/CoreHome/templates/datatable.css";
+ $cssFiles[] = "plugins/Dashboard/templates/dashboard.css";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function deleteDashboardLayout($notification)
+ {
+ $userLogin = $notification->getNotificationObject();
+ Piwik_Query('DELETE FROM ' . Piwik_Common::prefixTable('user_dashboard') . ' WHERE login = ?', array($userLogin));
+ }
+
+ public function install()
+ {
+ // we catch the exception
+ try {
+ $sql = "CREATE TABLE " . Piwik_Common::prefixTable('user_dashboard') . " (
login VARCHAR( 100 ) NOT NULL ,
iddashboard INT NOT NULL ,
name VARCHAR( 100 ) NULL DEFAULT NULL ,
layout TEXT NOT NULL,
PRIMARY KEY ( login , iddashboard )
- ) DEFAULT CHARSET=utf8 " ;
- Piwik_Exec($sql);
- } catch(Exception $e){
- // mysql code error 1050:table already exists
- // see bug #153 http://dev.piwik.org/trac/ticket/153
- if(!Zend_Registry::get('db')->isErrNo($e, '1050'))
- {
- throw $e;
- }
- }
- }
-
- public function uninstall()
- {
- Piwik_DropTables(Piwik_Common::prefixTable('user_dashboard'));
- }
-
+ ) DEFAULT CHARSET=utf8 ";
+ Piwik_Exec($sql);
+ } catch (Exception $e) {
+ // mysql code error 1050:table already exists
+ // see bug #153 http://dev.piwik.org/trac/ticket/153
+ if (!Zend_Registry::get('db')->isErrNo($e, '1050')) {
+ throw $e;
+ }
+ }
+ }
+
+ public function uninstall()
+ {
+ Piwik_DropTables(Piwik_Common::prefixTable('user_dashboard'));
+ }
+
}
diff --git a/plugins/Dashboard/templates/dashboard.css b/plugins/Dashboard/templates/dashboard.css
index 0fd262308d..80b2c4e23a 100644
--- a/plugins/Dashboard/templates/dashboard.css
+++ b/plugins/Dashboard/templates/dashboard.css
@@ -1,67 +1,73 @@
-
-#dashboard{
- padding:0 0 0 7px;
- margin:-4px -14px 0 -14px;
+#dashboard {
+ padding: 0 0 0 7px;
+ margin: -4px -14px 0 -14px;
}
-#dashboardWidgetsArea{
- padding:2px 0 0 0;
+#dashboardWidgetsArea {
+ padding: 2px 0 0 0;
}
.col {
- float: left;
- padding-bottom: 100px;
- min-height: 100px;
- -height: 100px;
+ float: left;
+ padding-bottom: 100px;
+ min-height: 100px;
+ -height: 100px;
}
.col.width-100 {
width: 100%;
}
+
.col.width-75 {
width: 75%;
}
+
.col.width-67 {
width: 67%;
}
+
.col.width-50 {
width: 50%;
}
+
.col.width-40 {
width: 40%;
}
+
.col.width-33 {
width: 33%;
}
+
.col.width-30 {
width: 30%;
}
+
.col.width-25 {
width: 25%;
}
.sortable {
- background: white;
+ background: white;
}
.hover {
- border: 2px dashed #E3E3E3;
+ border: 2px dashed #E3E3E3;
}
.widget {
- background:#fff;
- border: 1px solid #bbb6ad;
- margin:10px 7px;
- overflow: hidden;
- border-radius:4px;
- font-size:14px;
- border-radius:4px;
- -moz-border-radius:4px;
- -webkit-border-radius:4px;
+ background: #fff;
+ border: 1px solid #bbb6ad;
+ margin: 10px 7px;
+ overflow: hidden;
+ border-radius: 4px;
+ font-size: 14px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
}
.widgetHover {
- border: 1px solid #aba494;
+ border: 1px solid #aba494;
}
.widgetContent.hidden {
@@ -71,95 +77,97 @@
.widgetContent.loading {
opacity: 0.5;
- filter:Alpha(opacity:50);
+ filter: Alpha(opacity:50);
background: url(../../../themes/default/images/loading-blue.gif) no-repeat top right;
}
.widget h2 {
- font-size:1.2em;
- margin-left:10px;
- font-weight:bold;
+ font-size: 1.2em;
+ margin-left: 10px;
+ font-weight: bold;
}
+
.widgetTop {
- background:#b5b0a7 url(../../../themes/default/images/dashboard_h_bg.png) repeat-x 0 0;
- border-radius:4px 4px 0 0;
- -moz-border-radius:4px 4px 0 0;
- -webkit-border-radius:4px 4px 0 0;
- width: 100%;
- cursor: move;
- font-size: 10pt;
- font-weight: bold;
- padding-bottom: 4px;
+ background: #b5b0a7 url(../../../themes/default/images/dashboard_h_bg.png) repeat-x 0 0;
+ border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ -webkit-border-radius: 4px 4px 0 0;
+ width: 100%;
+ cursor: move;
+ font-size: 10pt;
+ font-weight: bold;
+ padding-bottom: 4px;
}
.widgetTopHover {
- background:#B0A798 url(../../../themes/default/images/dashboard_h_bg_hover.png) repeat-x 0 0;
+ background: #B0A798 url(../../../themes/default/images/dashboard_h_bg_hover.png) repeat-x 0 0;
}
.widgetName {
- font-size: 18px;
- padding:2px 0 0 10px;
- font-weight:normal;
- color:#fff;
- text-shadow:1px 1px 2px #7e7363;
+ font-size: 18px;
+ padding: 2px 0 0 10px;
+ font-weight: normal;
+ color: #fff;
+ text-shadow: 1px 1px 2px #7e7363;
}
.button {
- cursor: pointer;
+ cursor: pointer;
}
#close.button, #maximise.button, #minimise.button, #refresh.button {
- float: right;
- display: none;
- margin: 6px 6px 0 0;
+ float: right;
+ display: none;
+ margin: 6px 6px 0 0;
}
.ui-confirm {
- display: none;
- width:630px;
- background:#fff;
- color: #444;
- cursor: default;
- font-size: 12px!important;
- font-family:Arial,Verdana,Arial,Helvetica,sans-serif;
- border-radius:4px;
- padding:20px 10px;
- border-radius:4px;
- -moz-border-radius:4px;
- -webkit-border-radius:4px;
- min-height:0!important;
-}
-.ui-confirm h2{
+ display: none;
+ width: 630px;
+ background: #fff;
+ color: #444;
+ cursor: default;
+ font-size: 12px !important;
+ font-family: Arial, Verdana, Arial, Helvetica, sans-serif;
+ border-radius: 4px;
+ padding: 20px 10px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ min-height: 0 !important;
+}
+
+.ui-confirm h2 {
text-align: center;
- font-weight:bold;
- padding:0;
+ font-weight: bold;
+ padding: 0;
}
-.ui-dialog .ui-dialog-buttonpane{
+.ui-dialog .ui-dialog-buttonpane {
text-align: center;
border: none;
}
-.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
float: none;
}
-.ui-dialog input[type=button], .ui-dialog button{
- background: #B5B0A7 url(../../../themes/default/images/dashboard_h_bg_hover.png) repeat-x 0 0!important;
- color:#fff!important;
- border:0!important;
- font-size: 12px!important;
- padding:5px 20px!important;
- border-radius:3px;
- -moz-border-radius:3px;
- -webkit-border-radius:3px;
- cursor:pointer;
- display:inline-block;
- margin:0 8px 3px 8px!important;
+.ui-dialog input[type=button], .ui-dialog button {
+ background: #B5B0A7 url(../../../themes/default/images/dashboard_h_bg_hover.png) repeat-x 0 0 !important;
+ color: #fff !important;
+ border: 0 !important;
+ font-size: 12px !important;
+ padding: 5px 20px !important;
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ cursor: pointer;
+ display: inline-block;
+ margin: 0 8px 3px 8px !important;
}
-.ui-dialog .ui-button-text{
- padding: 0!important;
+.ui-dialog .ui-button-text {
+ padding: 0 !important;
}
.ui-widget-overlay {
@@ -169,43 +177,44 @@
}
.menu {
- display: none;
+ display: none;
}
.widgetLoading {
- cursor: wait;
- padding: 10px;
- text-align: center;
- font-size: 10pt;
+ cursor: wait;
+ padding: 10px;
+ text-align: center;
+ font-size: 10pt;
}
#closeMenuIcon {
- float: right;
- margin: 3px;
- cursor: pointer;
+ float: right;
+ margin: 3px;
+ cursor: pointer;
}
.menuClear {
- clear: both;
- height: 30px;
+ clear: both;
+ height: 30px;
}
#dashboardSettings {
- position:absolute;
- z-index: 998;
- background: #f7f7f7;
- border: 1px solid #e4e5e4;
- padding:5px 10px 6px 10px;
- border-radius:4px;
- -moz-border-radius:4px;
- -webkit-border-radius:4px;
- color:#444;
- font-size:14px;
+ position: absolute;
+ z-index: 998;
+ background: #f7f7f7;
+ border: 1px solid #e4e5e4;
+ padding: 5px 10px 6px 10px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ color: #444;
+ font-size: 14px;
cursor: pointer;
}
+
#dashboardSettings:hover {
- background:#f1f0eb;
- border-color:#a9a399;
+ background: #f1f0eb;
+ border-color: #a9a399;
}
#dashboardSettings > span {
@@ -250,20 +259,20 @@
float: left;
opacity: 0.4;
cursor: pointer;
- filter:Alpha(opacity:40);
+ filter: Alpha(opacity:40);
}
#columnPreview>div:hover, #columnPreview>div.choosen {
opacity: 1;
- filter:Alpha(opacity:100);
+ filter: Alpha(opacity:100);
}
-#columnPreview div div{
+#columnPreview div div {
height: 120px;
float: left;
}
-#columnPreview div div span{
+#columnPreview div div span {
background-color: #ddd;
width: 100%;
height: 100%;
@@ -272,31 +281,38 @@
margin: 0 1px;
}
-#columnPreview div.choosen div span, #columnPreview div:hover div span{
+#columnPreview div.choosen div span, #columnPreview div:hover div span {
border-style: solid;
}
#columnPreview .width-100 {
width: 120px;
}
+
#columnPreview .width-75 {
width: 90px;
}
+
#columnPreview .width-67 {
width: 80.4px;
}
+
#columnPreview .width-50 {
width: 60px;
}
+
#columnPreview .width-40 {
width: 48px;
}
+
#columnPreview .width-33 {
width: 40px;
}
+
#columnPreview .width-30 {
width: 36px;
}
+
#columnPreview .width-25 {
width: 30px;
}
@@ -312,17 +328,17 @@
}
#addWidget, #manageDashboard {
- cursor:default;
+ cursor: default;
}
ul.widgetpreview-widgetlist,
ul.widgetpreview-categorylist {
color: #5d5342;
- list-style:none;
- font-size:11px;
- line-height:20px;
+ list-style: none;
+ font-size: 11px;
+ line-height: 20px;
float: left;
- margin-right:20px;
+ margin-right: 20px;
}
ul.widgetpreview-categorylist {
@@ -331,16 +347,16 @@ ul.widgetpreview-categorylist {
ul.widgetpreview-categorylist li,
ul.widgetpreview-widgetlist li {
- line-height:20px;
- padding:0 25px 0 5px;
- border-radius:2px;
- -moz-border-radius:2px;
- -webkit-border-radius:2px;
+ line-height: 20px;
+ padding: 0 25px 0 5px;
+ border-radius: 2px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
}
.widgetpreview-base li.widgetpreview-choosen {
- background:#e4e2d7 url(../../../themes/default/images/arr_r.png) no-repeat right 6px;
- color:#255792;
+ background: #e4e2d7 url(../../../themes/default/images/arr_r.png) no-repeat right 6px;
+ color: #255792;
font-weight: bold;
}
@@ -372,15 +388,15 @@ div.widgetpreview-preview {
}
#dashboardSettings .submenu {
- font-weight:bold;
- color:#255792;
+ font-weight: bold;
+ color: #255792;
}
#dashboardSettings .submenu ul {
float: none;
- font-weight:normal;
- padding-top:10px;
- margin-left: 10px;
+ font-weight: normal;
+ padding-top: 10px;
+ margin-left: 10px;
color: #5D5342;
list-style: none;
font-size: 11px;
@@ -399,12 +415,12 @@ div.widgetpreview-preview {
}
#dashboardSettings .widgetpreview-widgetlist {
- width: 228px;
- font-weight:normal;
+ width: 228px;
+ font-weight: normal;
}
#dashboardSettings .widgetTop {
- cursor:pointer;
+ cursor: pointer;
}
#dashboardSettings .widgetpreview-widgetlist,
diff --git a/plugins/Dashboard/templates/dashboard.js b/plugins/Dashboard/templates/dashboard.js
index 1285834105..96ea45c28d 100644
--- a/plugins/Dashboard/templates/dashboard.js
+++ b/plugins/Dashboard/templates/dashboard.js
@@ -8,21 +8,19 @@
function initDashboard(dashboardId, dashboardLayout) {
// Standard dashboard
- if($('#periodString').length)
- {
+ if ($('#periodString').length) {
$('#periodString').after($('#dashboardSettings'));
- $('#dashboardSettings').css({left:$('#periodString')[0].offsetWidth});
+ $('#dashboardSettings').css({left: $('#periodString')[0].offsetWidth});
}
// Embed dashboard
- if(!$('#topBars').length)
- {
- $('#dashboardSettings').css({left:0});
+ if (!$('#topBars').length) {
+ $('#dashboardSettings').css({left: 0});
$('#dashboardSettings').after($('#Dashboard'));
- $('#Dashboard > ul li a').each(function(){$(this).css({width:this.offestWidth+30, paddingLeft:0, paddingRight:0});});
- $('#Dashboard_embeddedIndex_'+dashboardId).addClass('sfHover');
+ $('#Dashboard > ul li a').each(function () {$(this).css({width: this.offestWidth + 30, paddingLeft: 0, paddingRight: 0});});
+ $('#Dashboard_embeddedIndex_' + dashboardId).addClass('sfHover');
}
- $('#dashboardSettings').on('click', function(){
+ $('#dashboardSettings').on('click', function () {
$('#dashboardSettings').toggleClass('visible');
if ($('#dashboardWidgetsArea').dashboard('isDefaultDashboard')) {
$('#removeDashboardLink').hide();
@@ -32,8 +30,8 @@ function initDashboard(dashboardId, dashboardLayout) {
// fix position
$('#dashboardSettings .widgetpreview-widgetlist').css('paddingTop', $('#dashboardSettings .widgetpreview-categorylist').parent('li').position().top);
});
- $('body').on('mouseup', function(e) {
- if(!$(e.target).parents('#dashboardSettings').length && !$(e.target).is('#dashboardSettings')) {
+ $('body').on('mouseup', function (e) {
+ if (!$(e.target).parents('#dashboardSettings').length && !$(e.target).is('#dashboardSettings')) {
$('#dashboardSettings').widgetPreview('reset');
$('#dashboardSettings').removeClass('visible');
}
@@ -49,10 +47,10 @@ function initDashboard(dashboardId, dashboardLayout) {
});
$('#dashboardSettings').widgetPreview({
- isWidgetAvailable: function(widgetUniqueId) {
+ isWidgetAvailable: function (widgetUniqueId) {
return !$('#dashboardWidgetsArea [widgetId=' + widgetUniqueId + ']').length;
},
- onSelect: function(widgetUniqueId) {
+ onSelect: function (widgetUniqueId) {
var widget = widgetsHelper.getWidgetObjectFromUniqueId(widgetUniqueId);
$('#dashboardWidgetsArea').dashboard('addWidget', widget.uniqueId, 1, widget.parameters, true, false);
$('#dashboardSettings').removeClass('visible');
@@ -60,20 +58,20 @@ function initDashboard(dashboardId, dashboardLayout) {
resetOnSelect: true
});
- $('#columnPreview>div').each(function(){
+ $('#columnPreview>div').each(function () {
var width = [];
- $('div', this).each(function(){
+ $('div', this).each(function () {
width.push(this.className.replace(/width-/, ''));
});
$(this).attr('layout', width.join('-'));
});
- $('#columnPreview>div').on('click', function(){
+ $('#columnPreview>div').on('click', function () {
$('#columnPreview>div').removeClass('choosen');
$(this).addClass('choosen');
});
- $('.submenu>li').on('mouseenter', function(event){
+ $('.submenu>li').on('mouseenter', function (event) {
if (!$('.widgetpreview-categorylist', event.target).length) {
$('#dashboardSettings').widgetPreview('reset');
}
@@ -83,7 +81,7 @@ function initDashboard(dashboardId, dashboardLayout) {
function createDashboard() {
$('#createDashboardName').attr('value', '');
- piwikHelper.modalConfirm('#createDashboardConfirm', {yes: function(){
+ piwikHelper.modalConfirm('#createDashboardConfirm', {yes: function () {
var dashboardName = $('#createDashboardName').attr('value');
var type = ($('#dashboard_type_empty:checked').length > 0) ? 'empty' : 'default';
@@ -107,37 +105,37 @@ function createDashboard() {
}
function resetDashboard() {
- piwikHelper.modalConfirm('#resetDashboardConfirm', {yes: function(){ $('#dashboardWidgetsArea').dashboard('resetLayout'); }});
+ piwikHelper.modalConfirm('#resetDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('resetLayout'); }});
}
function renameDashboard() {
$('#newDashboardName').attr('value', $('#dashboardWidgetsArea').dashboard('getDashboardName'));
- piwikHelper.modalConfirm('#renameDashboardConfirm', {yes: function(){ $('#dashboardWidgetsArea').dashboard('setDashboardName', $('#newDashboardName').attr('value')); }});
+ piwikHelper.modalConfirm('#renameDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('setDashboardName', $('#newDashboardName').attr('value')); }});
}
function removeDashboard() {
$('#removeDashboardConfirm h2 span').html($('#dashboardWidgetsArea').dashboard('getDashboardName'));
- piwikHelper.modalConfirm('#removeDashboardConfirm', {yes: function(){ $('#dashboardWidgetsArea').dashboard('removeDashboard'); }});
+ piwikHelper.modalConfirm('#removeDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('removeDashboard'); }});
}
function showChangeDashboardLayoutDialog() {
$('#columnPreview>div').removeClass('choosen');
- $('#columnPreview>div[layout='+$('#dashboardWidgetsArea').dashboard('getColumnLayout')+']').addClass('choosen');
- piwikHelper.modalConfirm('#changeDashboardLayout', {yes: function(){
+ $('#columnPreview>div[layout=' + $('#dashboardWidgetsArea').dashboard('getColumnLayout') + ']').addClass('choosen');
+ piwikHelper.modalConfirm('#changeDashboardLayout', {yes: function () {
$('#dashboardWidgetsArea').dashboard('setColumnLayout', $('#changeDashboardLayout .choosen').attr('layout'));
}});
}
function showEmptyDashboardNotification() {
piwikHelper.modalConfirm('#dashboardEmptyNotification', {
- resetDashboard: function() { $('#dashboardWidgetsArea').dashboard('resetLayout'); },
- addWidget: function(){ $('#dashboardSettings').trigger('click'); }
+ resetDashboard: function () { $('#dashboardWidgetsArea').dashboard('resetLayout'); },
+ addWidget: function () { $('#dashboardSettings').trigger('click'); }
});
}
function setAsDefaultWidgets() {
piwikHelper.modalConfirm('#setAsDefaultWidgetsConfirm', {
- yes: function(){ $('#dashboardWidgetsArea').dashboard('saveLayoutAsDefaultWidgetLayout'); }
+ yes: function () { $('#dashboardWidgetsArea').dashboard('saveLayoutAsDefaultWidgetLayout'); }
});
}
@@ -145,9 +143,9 @@ function copyDashboardToUser() {
$('#copyDashboardName').attr('value', $('#dashboardWidgetsArea').dashboard('getDashboardName'));
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
- module: 'API',
- method: 'UsersManager.getUsers',
- format: 'json'
+ module: 'API',
+ method: 'UsersManager.getUsers',
+ format: 'json'
}, 'get');
ajaxRequest.setCallback(
function (availableUsers) {
@@ -155,10 +153,10 @@ function copyDashboardToUser() {
$('#copyDashboardUser').append(
$('<option></option>').val(piwik.userLogin).html(piwik.userLogin)
);
- $.each(availableUsers, function(index, user) {
+ $.each(availableUsers, function (index, user) {
if (user.login != 'anonymous' && user.login != piwik.userLogin) {
$('#copyDashboardUser').append(
- $('<option></option>').val(user.login).html(user.login + ' ('+user.alias+')')
+ $('<option></option>').val(user.login).html(user.login + ' (' + user.alias + ')')
);
}
});
@@ -167,7 +165,7 @@ function copyDashboardToUser() {
ajaxRequest.send(true);
piwikHelper.modalConfirm('#copyDashboardToUserConfirm', {
- yes: function() {
+ yes: function () {
var copyDashboardName = $('#copyDashboardName').attr('value');
var copyDashboardUser = $('#copyDashboardUser').attr('value');
diff --git a/plugins/Dashboard/templates/dashboardObject.js b/plugins/Dashboard/templates/dashboardObject.js
index 55c4e452fb..918a4be23f 100644
--- a/plugins/Dashboard/templates/dashboardObject.js
+++ b/plugins/Dashboard/templates/dashboardObject.js
@@ -4,33 +4,33 @@
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function( $ ){
+(function ($) {
/**
* Current dashboard column layout
* @type {object}
*/
- var dashboardLayout = {};
+ var dashboardLayout = {};
/**
* Id of current dashboard
* @type {int}
*/
- var dashboardId = 1;
+ var dashboardId = 1;
/**
* Name of current dashboard
* @type {string}
*/
- var dashboardName = '';
+ var dashboardName = '';
/**
* Holds a reference to the dashboard element
* @type {object}
*/
- var dashboardElement = null;
+ var dashboardElement = null;
/**
* Boolean indicating wether the layout config has been changed or not
* @type {boolean}
*/
- var dashboardChanged = false;
+ var dashboardChanged = false;
/**
* public methods of dashboard plugin
@@ -43,7 +43,7 @@
*
* @param {object} options
*/
- init: function(options) {
+ init: function (options) {
dashboardElement = this;
@@ -70,11 +70,11 @@
*
* @return void
*/
- destroy: function() {
+ destroy: function () {
$(dashboardElement).remove();
dashboardElement = null;
var widgets = $('[widgetId]');
- for (var i=0; i < widgets.length; i++) {
+ for (var i = 0; i < widgets.length; i++) {
$(widgets[i]).dashboardWidget('destroy');
}
},
@@ -84,15 +84,15 @@
*
* @param {int} dashboardIdToLoad
*/
- loadDashboard: function(dashboardIdToLoad) {
+ loadDashboard: function (dashboardIdToLoad) {
$(dashboardElement).empty();
- dashboardName = '';
+ dashboardName = '';
dashboardLayout = null;
- dashboardId = dashboardIdToLoad;
+ dashboardId = dashboardIdToLoad;
piwikHelper.showAjaxLoading();
broadcast.updateHashOnly = true;
- broadcast.propagateAjax('?idDashboard='+dashboardIdToLoad);
+ broadcast.propagateAjax('?idDashboard=' + dashboardIdToLoad);
fetchLayout(generateLayout);
buildMenu();
return this;
@@ -103,7 +103,7 @@
*
* @param {String} newLayout
*/
- setColumnLayout: function(newLayout) {
+ setColumnLayout: function (newLayout) {
adjustDashboardColumns(newLayout);
},
@@ -112,7 +112,7 @@
*
* @return {String}
*/
- getColumnLayout: function() {
+ getColumnLayout: function () {
return dashboardLayout.config.layout;
},
@@ -121,7 +121,7 @@
*
* @return {String}
*/
- getDashboardName: function() {
+ getDashboardName: function () {
return dashboardName;
},
@@ -130,7 +130,7 @@
*
* @return {int}
*/
- getDashboardId: function() {
+ getDashboardId: function () {
return dashboardId;
},
@@ -139,8 +139,8 @@
*
* @param {String} newName
*/
- setDashboardName: function(newName) {
- dashboardName = newName;
+ setDashboardName: function (newName) {
+ dashboardName = newName;
dashboardChanged = true;
saveLayout();
},
@@ -154,7 +154,7 @@
* @param {boolean} addWidgetOnTop
* @param {boolean} isHidden
*/
- addWidget: function(uniqueId, columnNumber, widgetParameters, addWidgetOnTop, isHidden) {
+ addWidget: function (uniqueId, columnNumber, widgetParameters, addWidgetOnTop, isHidden) {
addWidgetTemplate(uniqueId, columnNumber, widgetParameters, addWidgetOnTop, isHidden);
reloadWidget(uniqueId);
saveLayout();
@@ -163,12 +163,11 @@
/**
* Resets the current layout to the defaults
*/
- resetLayout: function()
- {
+ resetLayout: function () {
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
- module: 'Dashboard',
- action: 'resetLayout',
+ module: 'Dashboard',
+ action: 'resetLayout',
idDashboard: dashboardId
}, 'get');
ajaxRequest.setCallback(
@@ -184,21 +183,21 @@
/**
* Removes the current dashboard
*/
- removeDashboard: function() {
+ removeDashboard: function () {
removeDashboard();
},
/**
* Saves the current layout aus new default widget layout
*/
- saveLayoutAsDefaultWidgetLayout: function() {
+ saveLayoutAsDefaultWidgetLayout: function () {
saveLayout('saveLayoutAsDefault');
},
/**
* Returns if the current loaded dashboard is the default dashboard
*/
- isDefaultDashboard: function() {
+ isDefaultDashboard: function () {
return (dashboardId == 1);
}
};
@@ -215,16 +214,16 @@
adjustDashboardColumns(dashboardLayout.config.layout);
var dashboardContainsWidgets = false;
- for (var column=0; column < dashboardLayout.columns.length; column++) {
+ for (var column = 0; column < dashboardLayout.columns.length; column++) {
for (var i in dashboardLayout.columns[column]) {
- if (typeof dashboardLayout.columns[column][i] != 'object') {
- // Fix IE8 bug: the "i in" loop contains i="indexOf", which would yield type function.
- // If we would continue with i="indexOf", an invalid widget would be created.
- continue;
- }
+ if (typeof dashboardLayout.columns[column][i] != 'object') {
+ // Fix IE8 bug: the "i in" loop contains i="indexOf", which would yield type function.
+ // If we would continue with i="indexOf", an invalid widget would be created.
+ continue;
+ }
var widget = dashboardLayout.columns[column][i];
dashboardContainsWidgets = true;
- addWidgetTemplate(widget.uniqueId, column+1, widget.parameters, false, widget.isHidden)
+ addWidgetTemplate(widget.uniqueId, column + 1, widget.parameters, false, widget.isHidden)
}
}
@@ -241,8 +240,7 @@
*
* @param {function} callback
*/
- function fetchLayout(callback)
- {
+ function fetchLayout(callback) {
globalAjaxQueue.abort();
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
@@ -261,8 +259,7 @@
* @param {String} layout new layout in format xx-xx-xx
* @return {void}
*/
- function adjustDashboardColumns(layout)
- {
+ function adjustDashboardColumns(layout) {
var columnWidth = layout.split('-');
var columnCount = columnWidth.length;
@@ -270,7 +267,7 @@
if (currentCount < columnCount) {
$('.menuClear', dashboardElement).remove();
- for (var i=currentCount;i<columnCount;i++) {
+ for (var i = currentCount; i < columnCount; i++) {
if (dashboardLayout.columns.length < i) {
dashboardLayout.columns.push({});
}
@@ -278,16 +275,16 @@
}
$(dashboardElement).append('<div class="menuClear"> </div>');
} else if (currentCount > columnCount) {
- for (var i=columnCount;i<currentCount;i++) {
- if(dashboardLayout.columns.length >= i) {
+ for (var i = columnCount; i < currentCount; i++) {
+ if (dashboardLayout.columns.length >= i) {
dashboardLayout.columns.pop();
}
// move widgets to other columns depending on columns height
- $('[widgetId]', $('.col:last')).each(function(id, elem){
+ $('[widgetId]', $('.col:last')).each(function (id, elem) {
var cols = $('.col').slice(0, columnCount);
var smallestColumn = $(cols[0]);
var smallestColumnHeight = null;
- cols.each(function(colId, col){
+ cols.each(function (colId, col) {
if (smallestColumnHeight == null || smallestColumnHeight > $(col).height()) {
smallestColumnHeight = $(col).height();
smallestColumn = $(col);
@@ -301,21 +298,21 @@
}
}
- for (var i=0; i < columnCount; i++) {
- $('.col', dashboardElement)[i].className = 'col width-'+columnWidth[i];
+ for (var i = 0; i < columnCount; i++) {
+ $('.col', dashboardElement)[i].className = 'col width-' + columnWidth[i];
}
makeWidgetsSortable();
// if dashboard column count is changed (not on initial load)
- if(currentCount > 0 && dashboardLayout.config.layout != layout) {
- dashboardChanged = true;
+ if (currentCount > 0 && dashboardLayout.config.layout != layout) {
+ dashboardChanged = true;
dashboardLayout.config.layout = layout;
saveLayout();
}
// reload all widgets containing a graph to make them display correct
- $('.widget:has(".piwik-graph")').each(function(id, elem){
+ $('.widget:has(".piwik-graph")').each(function (id, elem) {
reloadWidget($(elem).attr('id'));
});
}
@@ -333,8 +330,8 @@
// column count was always 3, so use layout 33-33-33 as default
if ($.isArray(layout)) {
layout = {
- config: {layout: '33-33-33'},
- columns: layout
+ config: {layout: '33-33-33'},
+ columns: layout
};
}
@@ -351,7 +348,7 @@
* @param {String} uniqueId
*/
function reloadWidget(uniqueId) {
- $('[widgetId='+uniqueId+']', dashboardElement).dashboardWidget('reload');
+ $('[widgetId=' + uniqueId + ']', dashboardElement).dashboardWidget('reload');
}
/**
@@ -368,22 +365,22 @@
}
// do not try to add widget if given columnnumber is to high
- if(columnNumber > $('.col', dashboardElement).length) {
+ if (columnNumber > $('.col', dashboardElement).length) {
return;
}
- var widgetContent = '<div class="sortable" widgetId="'+uniqueId+'"></div>';
+ var widgetContent = '<div class="sortable" widgetId="' + uniqueId + '"></div>';
if (addWidgetOnTop) {
- $('.col:nth-child('+columnNumber+')', dashboardElement).prepend(widgetContent);
+ $('.col:nth-child(' + columnNumber + ')', dashboardElement).prepend(widgetContent);
} else {
- $('.col:nth-child('+columnNumber+')', dashboardElement).append(widgetContent);
+ $('.col:nth-child(' + columnNumber + ')', dashboardElement).append(widgetContent);
}
- $('[widgetId='+uniqueId+']', dashboardElement).dashboardWidget({
+ $('[widgetId=' + uniqueId + ']', dashboardElement).dashboardWidget({
uniqueId: uniqueId,
widgetParameters: widgetParameters,
- onChange: function() {
+ onChange: function () {
saveLayout();
},
isHidden: isHidden
@@ -393,8 +390,7 @@
/**
* Make all widgets on the dashboard sortable
*/
- function makeWidgetsSortable()
- {
+ function makeWidgetsSortable() {
function onStart(event, ui) {
if (!jQuery.support.noCloneEvent) {
$('object', this).hide();
@@ -406,7 +402,7 @@
$('.widgetHover', this).removeClass('widgetHover');
$('.widgetTopHover', this).removeClass('widgetTopHover');
$('.button#close, .button#maximise', this).hide();
- if($('.widget:has(".piwik-graph")', ui.item).length) {
+ if ($('.widget:has(".piwik-graph")', ui.item).length) {
reloadWidget($('.widget', ui.item).attr('id'));
}
saveLayout();
@@ -414,19 +410,19 @@
//launch 'sortable' property on every dashboard widgets
$('div.col', dashboardElement)
- .sortable('destroy')
- .sortable({
- items: 'div.sortable',
- opacity: 0.6,
- forceHelperSize: true,
- forcePlaceholderSize: true,
- placeholder: 'hover',
- handle: '.widgetTop',
- helper: 'clone',
- start: onStart,
- stop: onStop,
- connectWith: 'div.col'
- });
+ .sortable('destroy')
+ .sortable({
+ items: 'div.sortable',
+ opacity: 0.6,
+ forceHelperSize: true,
+ forcePlaceholderSize: true,
+ placeholder: 'hover',
+ handle: '.widgetTop',
+ helper: 'clone',
+ start: onStart,
+ stop: onStop,
+ connectWith: 'div.col'
+ });
}
/**
@@ -434,24 +430,24 @@
*/
function buildMenu() {
- var success = function(dashboards) {
+ var success = function (dashboards) {
var dashboardMenuList = $('#Dashboard > ul');
dashboardMenuList.empty();
if (dashboards.length > 1) {
dashboardMenuList.show();
- for (var i=0; i<dashboards.length; i++) {
- dashboardMenuList.append('<li id="Dashboard_embeddedIndex_'+dashboards[i].iddashboard+'" class="dashboardMenuItem"><a dashboardId="'+dashboards[i].iddashboard+'">'+ piwikHelper.htmlEntities( dashboards[i].name ) +'</a></li>');
- if(dashboards[i].iddashboard == dashboardId) {
+ for (var i = 0; i < dashboards.length; i++) {
+ dashboardMenuList.append('<li id="Dashboard_embeddedIndex_' + dashboards[i].iddashboard + '" class="dashboardMenuItem"><a dashboardId="' + dashboards[i].iddashboard + '">' + piwikHelper.htmlEntities(dashboards[i].name) + '</a></li>');
+ if (dashboards[i].iddashboard == dashboardId) {
dashboardName = dashboards[i].name;
}
}
- $('li a', dashboardMenuList).each(function(){$(this).css({width:$(this).width()+30, paddingLeft:0, paddingRight:0});});
- $('#Dashboard_embeddedIndex_'+dashboardId).addClass('sfHover');
+ $('li a', dashboardMenuList).each(function () {$(this).css({width: $(this).width() + 30, paddingLeft: 0, paddingRight: 0});});
+ $('#Dashboard_embeddedIndex_' + dashboardId).addClass('sfHover');
} else {
dashboardMenuList.hide();
}
- $('.dashboardMenuItem').on('click', function() {
+ $('.dashboardMenuItem').on('click', function () {
if (typeof piwikMenu != 'undefined') {
piwikMenu.activateMenu('Dashboard', 'embeddedIndex');
}
@@ -459,7 +455,7 @@
if ($(dashboardElement).length) {
$(dashboardElement).dashboard('loadDashboard', $('a', this).attr('dashboardId'));
} else {
- broadcast.propagateAjax('module=Dashboard&action=embeddedIndex&idDashboard='+$('a', this).attr('dashboardId'));
+ broadcast.propagateAjax('module=Dashboard&action=embeddedIndex&idDashboard=' + $('a', this).attr('dashboardId'));
}
$(this).addClass('sfHover');
});
@@ -483,15 +479,15 @@
var columns = [];
var columnNumber = 0;
- $('.col').each(function() {
+ $('.col').each(function () {
columns[columnNumber] = new Array;
var items = $('[widgetId]', this);
- for (var j=0; j<items.size(); j++) {
+ for (var j = 0; j < items.size(); j++) {
columns[columnNumber][j] = $(items[j]).dashboardWidget('getWidgetObject');
-
+
// Do not store segment in the dashboard layout
delete columns[columnNumber][j].parameters.segment;
-
+
}
columnNumber++;
});
@@ -517,7 +513,7 @@
}, 'post');
ajaxRequest.setCallback(
function () {
- if(dashboardChanged) {
+ if (dashboardChanged) {
dashboardChanged = false;
buildMenu();
}
@@ -555,14 +551,14 @@
/**
* Make plugin methods available
*/
- $.fn.dashboard = function( method ) {
- if ( methods[method] ) {
- return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
- } else if ( typeof method === 'object' || ! method ) {
- return methods.init.apply( this, arguments );
+ $.fn.dashboard = function (method) {
+ if (methods[method]) {
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+ } else if (typeof method === 'object' || !method) {
+ return methods.init.apply(this, arguments);
} else {
- $.error('Method ' + method + ' does not exist on jQuery.dashboard');
+ $.error('Method ' + method + ' does not exist on jQuery.dashboard');
}
}
-})( jQuery ); \ No newline at end of file
+})(jQuery); \ No newline at end of file
diff --git a/plugins/Dashboard/templates/dashboardWidget.js b/plugins/Dashboard/templates/dashboardWidget.js
index 7ac8a8f6eb..617f03d4c6 100755
--- a/plugins/Dashboard/templates/dashboardWidget.js
+++ b/plugins/Dashboard/templates/dashboardWidget.js
@@ -4,7 +4,7 @@
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function( $ ){
+(function ($) {
$.widget('piwik.dashboardWidget', {
@@ -12,17 +12,17 @@
* Boolean indicating wether the widget is currently maximised
* @type {Boolean}
*/
- isMaximised: false,
+ isMaximised: false,
/**
* Unique Id of the widget
* @type {String}
*/
- uniqueId: null,
+ uniqueId: null,
/**
* Object holding the widget parameters
* @type {Object}
*/
- widgetParameters: {},
+ widgetParameters: {},
/**
* Options available for initialization
@@ -37,23 +37,23 @@
/**
* creates a widget object
*/
- _create: function() {
+ _create: function () {
- if(!this.options.uniqueId) {
+ if (!this.options.uniqueId) {
piwikHelper.error('widgets can\'t be created without an uniqueId');
return;
} else {
this.uniqueId = this.options.uniqueId;
}
- if(this.options.widgetParameters) {
+ if (this.options.widgetParameters) {
this.widgetParameters = this.options.widgetParameters;
}
this._createDashboardWidget(this.uniqueId);
var self = this;
- this.element.on('setParameters.dashboardWidget', function(e, params) { self.setParameters(params); });
+ this.element.on('setParameters.dashboardWidget', function (e, params) { self.setParameters(params); });
this.reload(true, true);
},
@@ -62,9 +62,9 @@
* Cleanup some events and dialog
* Called automatically upon removing the widgets domNode
*/
- destroy: function() {
- if(this.isMaximised) {
- $('[widgetId='+this.uniqueId+']').dialog('destroy');
+ destroy: function () {
+ if (this.isMaximised) {
+ $('[widgetId=' + this.uniqueId + ']').dialog('destroy');
}
$('*', this.element).off('.dashboardWidget'); // unbind all events
$('.widgetContent', this.element).trigger('widget:destroy');
@@ -75,7 +75,7 @@
* Returns the data currently set for the widget
* @return {object}
*/
- getWidgetObject: function() {
+ getWidgetObject: function () {
return {
uniqueId: this.uniqueId,
parameters: this.widgetParameters,
@@ -86,13 +86,13 @@
/**
* Show the current widget in an ui.dialog
*/
- maximise: function() {
+ maximise: function () {
this.isMaximised = true;
$('.button#close, .button#maximise', this.element).hide();
- this.element.before('<div id="'+this.uniqueId+'-placeholder" class="widgetPlaceholder widget"> </div>');
- $('#'+this.uniqueId+'-placeholder').height(this.element.height());
- $('#'+this.uniqueId+'-placeholder').width(this.element.width()-16);
+ this.element.before('<div id="' + this.uniqueId + '-placeholder" class="widgetPlaceholder widget"> </div>');
+ $('#' + this.uniqueId + '-placeholder').height(this.element.height());
+ $('#' + this.uniqueId + '-placeholder').width(this.element.width() - 16);
var width = Math.floor($('body').width() * 0.7);
@@ -104,12 +104,12 @@
position: ['center', 'center'],
resizable: true,
autoOpen: true,
- close: function(event, ui) {
+ close: function (event, ui) {
self.isMaximised = false;
$('.button#minimise, .button#refresh', $(this)).hide();
$('body').off('.dashboardWidget');
$(this).dialog("destroy");
- $('#'+self.uniqueId+'-placeholder').replaceWith(this);
+ $('#' + self.uniqueId + '-placeholder').replaceWith(this);
$(this).removeAttr('style');
self.options.onChange();
$(this).find('div.piwik-graph').trigger('resizeGraph');
@@ -119,8 +119,8 @@
this.element.find('div.piwik-graph').trigger('resizeGraph');
var currentWidget = this.element;
- $('body').on('click.dashboardWidget', function(ev) {
- if(ev.target.className == "ui-widget-overlay") {
+ $('body').on('click.dashboardWidget', function (ev) {
+ if (ev.target.className == "ui-widget-overlay") {
$(currentWidget).dialog("close");
}
});
@@ -131,15 +131,15 @@
/**
* Reloads the widgets content with the currently set parameters
*/
- reload: function(hideLoading, notJQueryUI) {
+ reload: function (hideLoading, notJQueryUI) {
if (!notJQueryUI) {
piwikHelper.log('widget.reload() was called by jquery.ui, ignoring', arguments.callee.caller);
return;
}
var self = this, currentWidget = this.element;
- function onWidgetLoadedReplaceElementWithContent(loadedContent)
- {
+
+ function onWidgetLoadedReplaceElementWithContent(loadedContent) {
$('.widgetContent', currentWidget).html(loadedContent);
$('.widgetContent', currentWidget).removeClass('loading');
$('.widgetContent', currentWidget).trigger('widget:create', [self]);
@@ -147,7 +147,7 @@
// Reading segment from hash tag (standard case) or from the URL (when embedding dashboard)
var segment = broadcast.getValueFromHash('segment') || broadcast.getValueFromUrl('segment');
- if(segment.length) {
+ if (segment.length) {
this.widgetParameters.segment = segment;
}
@@ -165,7 +165,7 @@
*
* @param {object} parameters
*/
- setParameters: function(parameters) {
+ setParameters: function (parameters) {
if (!this.isMaximised && (parameters.viewDataTable == 'tableAllColumns' || parameters.viewDataTable == 'tableGoals')) {
this.maximise();
@@ -185,7 +185,7 @@
*
* @param {String} uniqueId
*/
- _createDashboardWidget: function(uniqueId) {
+ _createDashboardWidget: function (uniqueId) {
var widgetName = widgetsHelper.getWidgetNameFromUniqueId(uniqueId);
if (!widgetName) {
@@ -195,42 +195,42 @@
var emptyWidgetContent = widgetsHelper.getEmptyWidgetHtml(uniqueId, widgetName);
this.element.html(emptyWidgetContent);
- var widgetElement = $('#'+ uniqueId, this.element);
+ var widgetElement = $('#' + uniqueId, this.element);
var self = this;
widgetElement
- .on( 'mouseenter.dashboardWidget', function() {
- if(!self.isMaximised) {
+ .on('mouseenter.dashboardWidget', function () {
+ if (!self.isMaximised) {
$(this).addClass('widgetHover');
$('.widgetTop', this).addClass('widgetTopHover');
$('.button#close, .button#maximise', this).show();
- if(!$('.widgetContent', this).hasClass('hidden')) {
+ if (!$('.widgetContent', this).hasClass('hidden')) {
$('.button#minimise, .button#refresh', this).show();
}
}
})
- .on( 'mouseleave.dashboardWidget', function() {
- if(!self.isMaximised) {
+ .on('mouseleave.dashboardWidget', function () {
+ if (!self.isMaximised) {
$(this).removeClass('widgetHover');
$('.widgetTop', this).removeClass('widgetTopHover');
$('.button#close, .button#maximise, .button#minimise, .button#refresh', this).hide();
}
});
- if(this.options.isHidden) {
+ if (this.options.isHidden) {
$('.widgetContent', widgetElement).toggleClass('hidden');
}
$('.button#close', widgetElement)
- .on( 'click.dashboardWidget', function(ev){
- piwikHelper.modalConfirm('#confirm',{yes: function(){
+ .on('click.dashboardWidget', function (ev) {
+ piwikHelper.modalConfirm('#confirm', {yes: function () {
self.element.remove();
self.options.onChange();
}});
});
$('.button#maximise', widgetElement)
- .on( 'click.dashboardWidget', function(ev){
- if($('.widgetContent', $(this).parents('.widget')).hasClass('hidden')) {
+ .on('click.dashboardWidget', function (ev) {
+ if ($('.widgetContent', $(this).parents('.widget')).hasClass('hidden')) {
self.isMaximised = false;
self.options.isHidden = false;
$('.widgetContent', $(this).parents('.widget')).removeClass('hidden');
@@ -244,8 +244,8 @@
});
$('.button#minimise', widgetElement)
- .on( 'click.dashboardWidget', function(ev){
- if(!self.isMaximised) {
+ .on('click.dashboardWidget', function (ev) {
+ if (!self.isMaximised) {
$('.widgetContent', $(this).parents('.widget')).addClass('hidden');
$('.button#minimise, .button#refresh', $(this).parents('.widget')).hide();
self.options.isHidden = true;
@@ -256,7 +256,7 @@
});
$('.button#refresh', widgetElement)
- .on('click.dashboardWidget', function(ev){
+ .on('click.dashboardWidget', function (ev) {
self.reload(false, true);
});
@@ -265,4 +265,4 @@
});
-})( jQuery );
+})(jQuery);
diff --git a/plugins/Dashboard/templates/header.tpl b/plugins/Dashboard/templates/header.tpl
index 51b3738b6a..721b31e7ba 100644
--- a/plugins/Dashboard/templates/header.tpl
+++ b/plugins/Dashboard/templates/header.tpl
@@ -1,94 +1,97 @@
{* This header is for loading the dashboard in stand alone mode*}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
-<title>{'General_Dashboard'|translate} - {'CoreHome_WebAnalyticsReports'|translate}</title>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-{loadJavascriptTranslations plugins='CoreHome Dashboard'}
-<!--[if lt IE 9]>
-<script language="javascript" type="text/javascript" src="libs/jqplot/excanvas.min.js"></script>
-<![endif]-->
-{include file="CoreHome/templates/js_global_variables.tpl"}
-{include file="CoreHome/templates/js_css_includes.tpl"}
-{literal}
-<style type="text/css">
- body {
- padding-left:7px;
- }
- #dashboard {
- margin: 30px -6px 0 -12px;
- width: 100%;
- padding-top:8px;
- }
- #menuHead {
- position: absolute;
- top: 0;
- padding: 7px 0 0 2px;
- }
+ <title>{'General_Dashboard'|translate} - {'CoreHome_WebAnalyticsReports'|translate}</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ {loadJavascriptTranslations plugins='CoreHome Dashboard'}
+ <!--[if lt IE 9]>
+ <script language="javascript" type="text/javascript" src="libs/jqplot/excanvas.min.js"></script>
+ <![endif]-->
+ {include file="CoreHome/templates/js_global_variables.tpl"}
+ {include file="CoreHome/templates/js_css_includes.tpl"}
+ {literal}
+ <style type="text/css">
+ body {
+ padding-left: 7px;
+ }
- #Dashboard {
- z-index:5;
- font-size:14px;
- cursor: pointer;
- }
+ #dashboard {
+ margin: 30px -6px 0 -12px;
+ width: 100%;
+ padding-top: 8px;
+ }
- #Dashboard > ul {
- list-style: square inside none;
- background: #f7f7f7;
- border: 1px solid #e4e5e4;
- padding:5px 10px 6px 10px;
- border-radius:4px;
- -moz-border-radius:4px;
- -webkit-border-radius:4px;
- color:#444;
- height: 18px;
- }
+ #menuHead {
+ position: absolute;
+ top: 0;
+ padding: 7px 0 0 2px;
+ }
- #Dashboard:hover ul {
- background:#f1f0eb;
- border-color:#a9a399;
- }
+ #Dashboard {
+ z-index: 5;
+ font-size: 14px;
+ cursor: pointer;
+ }
- #Dashboard > ul > li {
- float: left;
- text-align: center;
- margin: 0 15px;
- }
+ #Dashboard > ul {
+ list-style: square inside none;
+ background: #f7f7f7;
+ border: 1px solid #e4e5e4;
+ padding: 5px 10px 6px 10px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ color: #444;
+ height: 18px;
+ }
- #Dashboard a {
- color: #444;
- text-decoration: none;
- font-weight: normal;
- display: inline-block;
- margin: 0 -15px;
- }
+ #Dashboard:hover ul {
+ background: #f1f0eb;
+ border-color: #a9a399;
+ }
- #Dashboard > ul > li:hover , #Dashboard > ul > li:hover a,
- #Dashboard > ul > li.sfHover, #Dashboard > ul > li.sfHover a {
- color: #e87500;
- }
+ #Dashboard > ul > li {
+ float: left;
+ text-align: center;
+ margin: 0 15px;
+ }
- #Dashboard > ul > li.sfHover, #Dashboard > ul > li.sfHover a {
- font-weight: bold;
- }
+ #Dashboard a {
+ color: #444;
+ text-decoration: none;
+ font-weight: normal;
+ display: inline-block;
+ margin: 0 -15px;
+ }
- #Dashboard, #periodString, #dashboardSettings {
- float: left;
- clear: none;
- position: relative;
- margin-left: 0;
- margin-right: 10px;
- }
- .jqplock-seriespicker-popover {
- top: 0;
- }
+ #Dashboard > ul > li:hover, #Dashboard > ul > li:hover a,
+ #Dashboard > ul > li.sfHover, #Dashboard > ul > li.sfHover a {
+ color: #e87500;
+ }
- #ajaxLoading {
- margin: 40px 0 -30px 0;
- }
+ #Dashboard > ul > li.sfHover, #Dashboard > ul > li.sfHover a {
+ font-weight: bold;
+ }
-</style>
-{/literal}
+ #Dashboard, #periodString, #dashboardSettings {
+ float: left;
+ clear: none;
+ position: relative;
+ margin-left: 0;
+ margin-right: 10px;
+ }
+
+ .jqplock-seriespicker-popover {
+ top: 0;
+ }
+
+ #ajaxLoading {
+ margin: 40px 0 -30px 0;
+ }
+
+ </style>
+ {/literal}
</head>
<body>
diff --git a/plugins/Dashboard/templates/index.tpl b/plugins/Dashboard/templates/index.tpl
index 60a675ac38..43b0fea5f2 100644
--- a/plugins/Dashboard/templates/index.tpl
+++ b/plugins/Dashboard/templates/index.tpl
@@ -2,89 +2,98 @@
{literal}
<script type="text/javascript">
-widgetsHelper.availableWidgets = {/literal}{$availableWidgets}{literal};
-$(document).ready(function() {
- initDashboard({/literal}{$dashboardId},{$dashboardLayout}{literal});
-});
+ widgetsHelper.availableWidgets = {/literal}{$availableWidgets}{literal};
+ $(document).ready(function () {
+ initDashboard({/literal}{$dashboardId}, {$dashboardLayout}{literal});
+ });
</script>
{/literal}
<div id="dashboard">
-
+
<div class="ui-confirm" id="confirm">
<h2>{'Dashboard_DeleteWidgetConfirm'|translate}</h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_Cancel'|translate}" />
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_Cancel'|translate}"/>
</div>
<div class="ui-confirm" id="setAsDefaultWidgetsConfirm">
<h2>{'Dashboard_SetAsDefaultWidgetsConfirm'|translate}</h2>
{capture assign=resetDashboard}{'Dashboard_ResetDashboard'|translate}{/capture}
<div class="popoverSubMessage">{'Dashboard_SetAsDefaultWidgetsConfirmHelp'|translate:$resetDashboard}</div>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_Cancel'|translate}" />
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_Cancel'|translate}"/>
</div>
<div class="ui-confirm" id="resetDashboardConfirm">
<h2>{'Dashboard_ResetDashboardConfirm'|translate}</h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_Cancel'|translate}" />
- </div>
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_Cancel'|translate}"/>
+ </div>
<div class="ui-confirm" id="dashboardEmptyNotification">
<h2>{'Dashboard_DashboardEmptyNotification'|translate}</h2>
- <input role="addWidget" type="button" value="{'Dashboard_AddAWidget'|translate}" />
- <input role="resetDashboard" type="button" value="{'Dashboard_ResetDashboard'|translate}" />
+ <input role="addWidget" type="button" value="{'Dashboard_AddAWidget'|translate}"/>
+ <input role="resetDashboard" type="button" value="{'Dashboard_ResetDashboard'|translate}"/>
</div>
<div class="ui-confirm" id="changeDashboardLayout">
<h2>{'Dashboard_SelectDashboardLayout'|translate}</h2>
+
<div id="columnPreview">
- {foreach from=$availableLayouts item=layout}
- <div>
- {foreach from=$layout item=column}
- <div class="width-{$column}"><span></span></div>
+ {foreach from=$availableLayouts item=layout}
+ <div>
+ {foreach from=$layout item=column}
+ <div class="width-{$column}"><span></span></div>
+ {/foreach}
+ </div>
{/foreach}
- </div>
- {/foreach}
</div>
- <input role="yes" type="button" value="{'General_Save'|translate}" />
+ <input role="yes" type="button" value="{'General_Save'|translate}"/>
</div>
<div class="ui-confirm" id="renameDashboardConfirm">
<h2>{'Dashboard_RenameDashboard'|translate}</h2>
- <div id="newDashboardNameInput"><label for="newDashboardName">{'Dashboard_DashboardName'|translate} </label><input type="input" name="newDashboardName" id="newDashboardName" value=""/></div>
- <input role="yes" type="button" value="{'General_Save'|translate}" />
- <input role="cancel" type="button" value="{'General_Cancel'|translate}" />
+
+ <div id="newDashboardNameInput"><label for="newDashboardName">{'Dashboard_DashboardName'|translate} </label><input type="input" name="newDashboardName"
+ id="newDashboardName" value=""/>
+ </div>
+ <input role="yes" type="button" value="{'General_Save'|translate}"/>
+ <input role="cancel" type="button" value="{'General_Cancel'|translate}"/>
</div>
{if $isSuperUser}
- <div class="ui-confirm" id="copyDashboardToUserConfirm">
- <h2>{'Dashboard_CopyDashboardToUser'|translate}</h2>
- <div class="inputs"><label for="copyDashboardName">{'Dashboard_DashboardName'|translate} </label><input type="input" name="copyDashboardName" id="copyDashboardName" value=""/>
- <label for="copyDashboardUser">{'General_Username'|translate} </label>
- <select name="copyDashboardUser" id="copyDashboardUser">
- </select></div>
- <input role="yes" type="button" value="{'General_Ok'|translate}" />
- <input role="cancel" type="button" value="{'General_Cancel'|translate}" />
- </div>
+ <div class="ui-confirm" id="copyDashboardToUserConfirm">
+ <h2>{'Dashboard_CopyDashboardToUser'|translate}</h2>
+
+ <div class="inputs"><label for="copyDashboardName">{'Dashboard_DashboardName'|translate} </label><input type="input" name="copyDashboardName"
+ id="copyDashboardName" value=""/>
+ <label for="copyDashboardUser">{'General_Username'|translate} </label>
+ <select name="copyDashboardUser" id="copyDashboardUser">
+ </select></div>
+ <input role="yes" type="button" value="{'General_Ok'|translate}"/>
+ <input role="cancel" type="button" value="{'General_Cancel'|translate}"/>
+ </div>
{/if}
<div class="ui-confirm" id="createDashboardConfirm">
<h2>{'Dashboard_CreateNewDashboard'|translate}</h2>
+
<div id="createDashboardNameInput">
- <label>{'Dashboard_DashboardName'|translate} <input type="input" name="newDashboardName" id="createDashboardName" value=""/></label><br />
- <label><input type="radio" checked="checked" name="type" value="default" id="dashboard_type_default">{'Dashboard_DefaultDashboard'|translate}</label><br />
+ <label>{'Dashboard_DashboardName'|translate} <input type="input" name="newDashboardName" id="createDashboardName" value=""/></label><br/>
+ <label><input type="radio" checked="checked" name="type" value="default" id="dashboard_type_default">{'Dashboard_DefaultDashboard'|translate}
+ </label><br/>
<label><input type="radio" name="type" value="empty" id="dashboard_type_empty">{'Dashboard_EmptyDashboard'|translate}</label>
</div>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_Cancel'|translate}" />
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_Cancel'|translate}"/>
</div>
<div class="ui-confirm" id="removeDashboardConfirm">
<h2>{'Dashboard_RemoveDashboardConfirm'|translate:'<span></span>'}</h2>
+
<div class="popoverSubMessage">{'Dashboard_NotUndo'|translate:$resetDashboard}</div>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_Cancel'|translate}" />
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_Cancel'|translate}"/>
</div>
<div id="dashboardSettings">
@@ -100,24 +109,24 @@ $(document).ready(function() {
<li onclick="resetDashboard();">{'Dashboard_ResetDashboard'|translate}</li>
<li onclick="showChangeDashboardLayoutDialog();">{'Dashboard_ChangeDashboardLayout'|translate}</li>
{if ($userLogin && 'anonymous' != $userLogin)}
- <li onclick="renameDashboard();">{'Dashboard_RenameDashboard'|translate}</li>
- <li onclick="removeDashboard();" id="removeDashboardLink">{'Dashboard_RemoveDashboard'|translate}</li>
- {if ($isSuperUser)}
- <li onclick="setAsDefaultWidgets();">{'Dashboard_SetAsDefaultWidgets'|translate}</li>
- <li onclick="copyDashboardToUser();">{'Dashboard_CopyDashboardToUser'|translate}</li>
- {/if}
+ <li onclick="renameDashboard();">{'Dashboard_RenameDashboard'|translate}</li>
+ <li onclick="removeDashboard();" id="removeDashboardLink">{'Dashboard_RemoveDashboard'|translate}</li>
+ {if ($isSuperUser)}
+ <li onclick="setAsDefaultWidgets();">{'Dashboard_SetAsDefaultWidgets'|translate}</li>
+ <li onclick="copyDashboardToUser();">{'Dashboard_CopyDashboardToUser'|translate}</li>
+ {/if}
{/if}
</ul>
</li>
{if ($userLogin && 'anonymous' != $userLogin)}
- <li onclick="createDashboard();">{'Dashboard_CreateNewDashboard'|translate}</li>
+ <li onclick="createDashboard();">{'Dashboard_CreateNewDashboard'|translate}</li>
{/if}
</ul>
<ul class="widgetpreview-widgetlist"></ul>
<div class="widgetpreview-preview"></div>
</div>
-
+
<div class="clear"></div>
-
+
<div id="dashboardWidgetsArea"></div>
</div>
diff --git a/plugins/Dashboard/templates/standalone.tpl b/plugins/Dashboard/templates/standalone.tpl
index 4ef3feeb9c..bdfffaf095 100644
--- a/plugins/Dashboard/templates/standalone.tpl
+++ b/plugins/Dashboard/templates/standalone.tpl
@@ -1,11 +1,14 @@
{include file="Dashboard/templates/header.tpl"}
<div id="menuHead">
{include file="CoreHome/templates/period_select.tpl"}
- <div id="Dashboard"><ul>
- {foreach from=$dashboards item=dashboard}
- <li class="dashboardMenuItem" id="Dashboard_embeddedIndex_{$dashboard.iddashboard}"><a href="javascript:$('#dashboardWidgetsArea').dashboard('loadDashboard', {$dashboard.iddashboard});">{$dashboard.name|escape}</a></li>
- {/foreach}
- </ul></div>
+ <div id="Dashboard">
+ <ul>
+ {foreach from=$dashboards item=dashboard}
+ <li class="dashboardMenuItem" id="Dashboard_embeddedIndex_{$dashboard.iddashboard}"><a
+ href="javascript:$('#dashboardWidgetsArea').dashboard('loadDashboard', {$dashboard.iddashboard});">{$dashboard.name|escape}</a></li>
+ {/foreach}
+ </ul>
+ </div>
<div class="clear"></div>
</div>
{ajaxLoadingDiv}
diff --git a/plugins/Dashboard/templates/widgetMenu.js b/plugins/Dashboard/templates/widgetMenu.js
index f8d1f4a96e..7a8ac082d1 100644
--- a/plugins/Dashboard/templates/widgetMenu.js
+++ b/plugins/Dashboard/templates/widgetMenu.js
@@ -5,8 +5,7 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function widgetsHelper()
-{
+function widgetsHelper() {
}
/**
@@ -14,9 +13,8 @@ function widgetsHelper()
*
* @return {object} object containing available widgets
*/
-widgetsHelper.getAvailableWidgets = function ()
-{
- if(!widgetsHelper.availableWidgets) {
+widgetsHelper.getAvailableWidgets = function () {
+ if (!widgetsHelper.availableWidgets) {
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
module: 'Dashboard',
@@ -29,7 +27,7 @@ widgetsHelper.getAvailableWidgets = function ()
);
ajaxRequest.send(true);
}
-
+
return widgetsHelper.availableWidgets;
};
@@ -39,18 +37,17 @@ widgetsHelper.getAvailableWidgets = function ()
* @param {string} uniqueId
* @return {object} widget object
*/
-widgetsHelper.getWidgetObjectFromUniqueId = function (uniqueId)
-{
- var widgets = widgetsHelper.getAvailableWidgets();
- for(var widgetCategory in widgets) {
- var widgetInCategory = widgets[widgetCategory];
- for(var i in widgetInCategory) {
- if(widgetInCategory[i]["uniqueId"] == uniqueId) {
- return widgetInCategory[i];
- }
- }
- }
- return false;
+widgetsHelper.getWidgetObjectFromUniqueId = function (uniqueId) {
+ var widgets = widgetsHelper.getAvailableWidgets();
+ for (var widgetCategory in widgets) {
+ var widgetInCategory = widgets[widgetCategory];
+ for (var i in widgetInCategory) {
+ if (widgetInCategory[i]["uniqueId"] == uniqueId) {
+ return widgetInCategory[i];
+ }
+ }
+ }
+ return false;
};
/**
@@ -59,13 +56,12 @@ widgetsHelper.getWidgetObjectFromUniqueId = function (uniqueId)
* @param {string} uniqueId unique id of the widget
* @return {string}
*/
-widgetsHelper.getWidgetNameFromUniqueId = function (uniqueId)
-{
- var widget = this.getWidgetObjectFromUniqueId(uniqueId);
- if(widget == false) {
- return false;
- }
- return widget["name"];
+widgetsHelper.getWidgetNameFromUniqueId = function (uniqueId) {
+ var widget = this.getWidgetObjectFromUniqueId(uniqueId);
+ if (widget == false) {
+ return false;
+ }
+ return widget["name"];
};
/**
@@ -77,29 +73,26 @@ widgetsHelper.getWidgetNameFromUniqueId = function (uniqueId)
* @return {object}
* @deprecated since 1.9.3 - will be removed in next major release. use widgetsHelper.loadWidgetAjax
*/
-widgetsHelper.getLoadWidgetAjaxRequest = function (widgetUniqueId, widgetParameters, onWidgetLoadedCallback)
-{
+widgetsHelper.getLoadWidgetAjaxRequest = function (widgetUniqueId, widgetParameters, onWidgetLoadedCallback) {
var token_auth = broadcast.getValueFromUrl('token_auth');
- if(token_auth.length && token_auth != 'anonymous')
- {
- widgetParameters['token_auth'] = token_auth;
+ if (token_auth.length && token_auth != 'anonymous') {
+ widgetParameters['token_auth'] = token_auth;
}
var disableLink = broadcast.getValueFromUrl('disableLink');
- if(disableLink.length)
- {
- widgetParameters['disableLink'] = disableLink;
+ if (disableLink.length) {
+ widgetParameters['disableLink'] = disableLink;
}
- return {
- widgetUniqueId:widgetUniqueId,
- type: 'GET',
- url: 'index.php',
- dataType: 'html',
- async: true,
- error: piwikHelper.ajaxHandleError,
- success: onWidgetLoadedCallback,
- data: piwikHelper.getQueryStringFromParameters(widgetParameters) + "&widget=1&idSite="+piwik.idSite+"&period="+piwik.period+"&date="+broadcast.getValueFromUrl('date') + "&token_auth=" + piwik.token_auth
- };
+ return {
+ widgetUniqueId: widgetUniqueId,
+ type: 'GET',
+ url: 'index.php',
+ dataType: 'html',
+ async: true,
+ error: piwikHelper.ajaxHandleError,
+ success: onWidgetLoadedCallback,
+ data: piwikHelper.getQueryStringFromParameters(widgetParameters) + "&widget=1&idSite=" + piwik.idSite + "&period=" + piwik.period + "&date=" + broadcast.getValueFromUrl('date') + "&token_auth=" + piwik.token_auth
+ };
};
/**
@@ -110,12 +103,10 @@ widgetsHelper.getLoadWidgetAjaxRequest = function (widgetUniqueId, widgetParamet
* @param {function} onWidgetLoadedCallback callback to be executed after widget is loaded
* @return {object}
*/
-widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWidgetLoadedCallback)
-{
+widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWidgetLoadedCallback) {
var disableLink = broadcast.getValueFromUrl('disableLink');
- if(disableLink.length)
- {
- widgetParameters['disableLink'] = disableLink;
+ if (disableLink.length) {
+ widgetParameters['disableLink'] = disableLink;
}
widgetParameters['widget'] = 1;
@@ -135,42 +126,41 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid
* @param {string} widgetName name of the widget
* @return {string} html for empty widget
*/
-widgetsHelper.getEmptyWidgetHtml = function (uniqueId, widgetName)
-{
- return '<div id="'+uniqueId+'" class="widget">'+
- '<div class="widgetTop">'+
- '<div class="button" id="close">'+
- '<img src="themes/default/images/close.png" title="'+_pk_translate('Dashboard_Close_js')+'" />'+
- '</div>'+
- '<div class="button" id="maximise">'+
- '<img src="themes/default/images/maximise.png" title="'+_pk_translate('Dashboard_Maximise_js')+'" />'+
- '</div>'+
- '<div class="button" id="minimise">'+
- '<img src="themes/default/images/minimise.png" title="'+_pk_translate('Dashboard_Minimise_js')+'" />'+
- '</div>'+
- '<div class="button" id="refresh">'+
- '<img src="themes/default/images/refresh.png" title="'+_pk_translate('Dashboard_Refresh_js')+'" />'+
- '</div>'+
- '<div class="widgetName">'+widgetName+'</div>'+
- '</div>'+
- '<div class="widgetContent">'+
- '<div class="widgetLoading">'+
- _pk_translate('Dashboard_LoadingWidget_js') +
- '</div>'+
- '</div>'+
- '</div>';
+widgetsHelper.getEmptyWidgetHtml = function (uniqueId, widgetName) {
+ return '<div id="' + uniqueId + '" class="widget">' +
+ '<div class="widgetTop">' +
+ '<div class="button" id="close">' +
+ '<img src="themes/default/images/close.png" title="' + _pk_translate('Dashboard_Close_js') + '" />' +
+ '</div>' +
+ '<div class="button" id="maximise">' +
+ '<img src="themes/default/images/maximise.png" title="' + _pk_translate('Dashboard_Maximise_js') + '" />' +
+ '</div>' +
+ '<div class="button" id="minimise">' +
+ '<img src="themes/default/images/minimise.png" title="' + _pk_translate('Dashboard_Minimise_js') + '" />' +
+ '</div>' +
+ '<div class="button" id="refresh">' +
+ '<img src="themes/default/images/refresh.png" title="' + _pk_translate('Dashboard_Refresh_js') + '" />' +
+ '</div>' +
+ '<div class="widgetName">' + widgetName + '</div>' +
+ '</div>' +
+ '<div class="widgetContent">' +
+ '<div class="widgetLoading">' +
+ _pk_translate('Dashboard_LoadingWidget_js') +
+ '</div>' +
+ '</div>' +
+ '</div>';
};
/**
* widgetPreview jQuery Extension
- *
+ *
* Converts an dom element to a widget preview
* Widget preview contains an categorylist, widgetlist and a preview
*/
-(function($) {
+(function ($) {
$.extend({
- widgetPreview: new function() {
-
+ widgetPreview: new function () {
+
/**
* Default settings for widgetPreview
* @type {object}
@@ -180,18 +170,18 @@ widgetsHelper.getEmptyWidgetHtml = function (uniqueId, widgetName)
* handler called after a widget preview is loaded in preview element
* @type {function}
*/
- onPreviewLoaded: function() {},
+ onPreviewLoaded: function () {},
/**
* handler called on click on element in widgetlist or widget header
* @type {function}
*/
- onSelect: function() {},
+ onSelect: function () {},
/**
* callback used to determine if a widget is available or not
* unavailable widgets aren't chooseable in widgetlist
* @type {function}
*/
- isWidgetAvailable: function(widgetUniqueId){ return true; },
+ isWidgetAvailable: function (widgetUniqueId) { return true; },
/**
* should the lists and preview be reset on widget selection?
* @type {boolean}
@@ -208,58 +198,58 @@ widgetsHelper.getEmptyWidgetHtml = function (uniqueId, widgetName)
choosenClass: 'widgetpreview-choosen',
unavailableClass: 'widgetpreview-unavailable'
};
-
+
var availableWidgets, widgetPreview, widgetAjaxRequest = null;
-
+
/**
* Returns the div to show category list in
* - if element doesn't exist it will be created and added
* - if element already exist it's content will be removed
- *
+ *
* @return {$} category list element
*/
function createWidgetCategoryList() {
-
- if(!$('.'+settings.categorylistClass, widgetPreview).length) {
- $(widgetPreview).append('<ul class="'+ settings.categorylistClass +'"></ul>');
+
+ if (!$('.' + settings.categorylistClass, widgetPreview).length) {
+ $(widgetPreview).append('<ul class="' + settings.categorylistClass + '"></ul>');
} else {
- $('.'+settings.categorylistClass, widgetPreview).empty();
+ $('.' + settings.categorylistClass, widgetPreview).empty();
}
-
+
for (var widgetCategory in availableWidgets) {
-
- $('.'+settings.categorylistClass, widgetPreview).append('<li>'+ widgetCategory +'</li>');
+
+ $('.' + settings.categorylistClass, widgetPreview).append('<li>' + widgetCategory + '</li>');
}
-
- return $('.'+settings.categorylistClass, widgetPreview);
+
+ return $('.' + settings.categorylistClass, widgetPreview);
};
-
+
/**
* Returns the div to show widget list in
* - if element doesn't exist it will be created and added
* - if element already exist it's content will be removed
- *
+ *
* @return {$} widget list element
*/
function createWidgetList() {
-
- if(!$('.'+settings.widgetlistClass, widgetPreview).length) {
- $(widgetPreview).append('<ul class="'+ settings.widgetlistClass +'"></ul>');
+
+ if (!$('.' + settings.widgetlistClass, widgetPreview).length) {
+ $(widgetPreview).append('<ul class="' + settings.widgetlistClass + '"></ul>');
} else {
- $('.'+settings.widgetlistClass+' li', widgetPreview).off('mouseover');
- $('.'+settings.widgetlistClass+' li', widgetPreview).off('click');
- $('.'+settings.widgetlistClass, widgetPreview).empty();
+ $('.' + settings.widgetlistClass + ' li', widgetPreview).off('mouseover');
+ $('.' + settings.widgetlistClass + ' li', widgetPreview).off('click');
+ $('.' + settings.widgetlistClass, widgetPreview).empty();
}
-
- if($('.'+settings.categorylistClass+' .'+settings.choosenClass, widgetPreview).length) {
- var position = $('.'+settings.categorylistClass+' .'+settings.choosenClass, widgetPreview).position().top -
- $('.'+settings.categorylistClass).position().top;
-
- $('.'+settings.widgetlistClass, widgetPreview).css('top', position);
- $('.'+settings.widgetlistClass, widgetPreview).css('marginBottom', position);
+
+ if ($('.' + settings.categorylistClass + ' .' + settings.choosenClass, widgetPreview).length) {
+ var position = $('.' + settings.categorylistClass + ' .' + settings.choosenClass, widgetPreview).position().top -
+ $('.' + settings.categorylistClass).position().top;
+
+ $('.' + settings.widgetlistClass, widgetPreview).css('top', position);
+ $('.' + settings.widgetlistClass, widgetPreview).css('marginBottom', position);
}
-
- return $('.'+settings.widgetlistClass, widgetPreview);
+
+ return $('.' + settings.widgetlistClass, widgetPreview);
};
/**
@@ -270,27 +260,27 @@ widgetsHelper.getEmptyWidgetHtml = function (uniqueId, widgetName)
*/
function showWidgetList(widgets) {
- var widgetList = createWidgetList(),
+ var widgetList = createWidgetList(),
widgetPreviewTimer;
- for (var j = 0; j < widgets.length; j++) {
- var widgetName = widgets[j]["name"];
- var widgetUniqueId = widgets[j]["uniqueId"];
+ for (var j = 0; j < widgets.length; j++) {
+ var widgetName = widgets[j]["name"];
+ var widgetUniqueId = widgets[j]["uniqueId"];
var widgetParameters = widgets[j]["parameters"];
- var widgetClass = '';
- if(!settings.isWidgetAvailable(widgetUniqueId)) {
+ var widgetClass = '';
+ if (!settings.isWidgetAvailable(widgetUniqueId)) {
widgetClass += ' ' + settings.unavailableClass;
}
- widgetList.append('<li class="'+ widgetClass +'" uniqueid="'+ widgetUniqueId +'">'+ widgetName +'</li>');
+ widgetList.append('<li class="' + widgetClass + '" uniqueid="' + widgetUniqueId + '">' + widgetName + '</li>');
}
// delay widget preview a few millisconds
- $('li:not(.'+settings.unavailableClass+')', widgetList).on('mouseenter', function(){
+ $('li:not(.' + settings.unavailableClass + ')', widgetList).on('mouseenter', function () {
var that = this,
widgetUniqueId = $(this).attr('uniqueid');
clearTimeout(widgetPreview);
- widgetPreviewTimer = setTimeout(function() {
+ widgetPreviewTimer = setTimeout(function () {
$('li', widgetList).removeClass(settings.choosenClass);
$(that).addClass(settings.choosenClass);
@@ -299,14 +289,14 @@ widgetsHelper.getEmptyWidgetHtml = function (uniqueId, widgetName)
});
// clear timeout after mouse has left
- $('li:not(.'+settings.unavailableClass+')', widgetList).on('mouseleave', function(){
+ $('li:not(.' + settings.unavailableClass + ')', widgetList).on('mouseleave', function () {
clearTimeout(widgetPreview);
});
- $('li:not(.'+settings.unavailableClass+')', widgetList).on('click', function(){
- if(!$('.widgetLoading', widgetPreview).length) {
+ $('li:not(.' + settings.unavailableClass + ')', widgetList).on('click', function () {
+ if (!$('.widgetLoading', widgetPreview).length) {
settings.onSelect($(this).attr('uniqueid'));
- if(settings.resetOnSelect) {
+ if (settings.resetOnSelect) {
resetWidgetPreview();
}
}
@@ -322,107 +312,107 @@ widgetsHelper.getEmptyWidgetHtml = function (uniqueId, widgetName)
* @return {$} preview element
*/
function createPreviewElement() {
-
- if(!$('.'+settings.widgetpreviewClass, widgetPreview).length) {
- $(widgetPreview).append('<div class="'+ settings.widgetpreviewClass +'"></div>');
+
+ if (!$('.' + settings.widgetpreviewClass, widgetPreview).length) {
+ $(widgetPreview).append('<div class="' + settings.widgetpreviewClass + '"></div>');
} else {
- $('.'+settings.widgetpreviewClass+' .widgetTop', widgetPreview).off('click');
- $('.'+settings.widgetpreviewClass, widgetPreview).empty();
+ $('.' + settings.widgetpreviewClass + ' .widgetTop', widgetPreview).off('click');
+ $('.' + settings.widgetpreviewClass, widgetPreview).empty();
}
-
- return $('.'+settings.widgetpreviewClass, widgetPreview);
+
+ return $('.' + settings.widgetpreviewClass, widgetPreview);
};
-
+
/**
* Show widget with the given uniqueId in preview
- *
+ *
* @param {string} widgetUniqueId unique id of widget to display
* @return {void}
*/
function showPreview(widgetUniqueId) {
// do not reload id widget already displayed
- if($('#'+widgetUniqueId, widgetPreview).length) return;
-
+ if ($('#' + widgetUniqueId, widgetPreview).length) return;
+
var previewElement = createPreviewElement();
-
+
var widget = widgetsHelper.getWidgetObjectFromUniqueId(widgetUniqueId);
var widgetParameters = widget['parameters'];
-
+
var emptyWidgetHtml = widgetsHelper.getEmptyWidgetHtml(
- widgetUniqueId,
- '<div title="'+_pk_translate("Dashboard_AddPreviewedWidget_js")+'">'+
- _pk_translate('Dashboard_WidgetPreview_js')+
- '</div>'
+ widgetUniqueId,
+ '<div title="' + _pk_translate("Dashboard_AddPreviewedWidget_js") + '">' +
+ _pk_translate('Dashboard_WidgetPreview_js') +
+ '</div>'
);
previewElement.html(emptyWidgetHtml);
-
+
var onWidgetLoadedCallback = function (response) {
- var widgetElement = $('#'+widgetUniqueId);
+ var widgetElement = $('#' + widgetUniqueId);
$('.widgetContent', widgetElement).html($(response));
$('.widgetContent', widgetElement).trigger('widget:create');
settings.onPreviewLoaded(widgetUniqueId, widgetElement);
- $('.'+settings.widgetpreviewClass+' .widgetTop', widgetPreview).on('click', function(){
+ $('.' + settings.widgetpreviewClass + ' .widgetTop', widgetPreview).on('click', function () {
settings.onSelect(widgetUniqueId);
- if(settings.resetOnSelect) {
+ if (settings.resetOnSelect) {
resetWidgetPreview();
}
return false;
});
};
-
+
// abort previous sent request
- if(widgetAjaxRequest) {
+ if (widgetAjaxRequest) {
widgetAjaxRequest.abort();
}
-
+
widgetAjaxRequest = widgetsHelper.loadWidgetAjax(widgetUniqueId, widgetParameters, onWidgetLoadedCallback);
};
-
+
/**
* Reset function
- *
+ *
* @return {void}
*/
function resetWidgetPreview() {
- $('.'+settings.categorylistClass+' li', widgetPreview).removeClass(settings.choosenClass);
+ $('.' + settings.categorylistClass + ' li', widgetPreview).removeClass(settings.choosenClass);
createWidgetList();
createPreviewElement();
};
-
+
/**
* Constructor
- *
+ *
* @param {object} userSettings Settings to be used
* @return {void}
*/
- this.construct = function(userSettings) {
-
- if(widgetPreview && userSettings == 'reset') {
+ this.construct = function (userSettings) {
+
+ if (widgetPreview && userSettings == 'reset') {
resetWidgetPreview();
return;
}
-
+
widgetPreview = this;
-
+
$(this).addClass('widgetpreview-base');
-
+
settings = jQuery.extend(settings, userSettings);
-
+
// set onSelect callback
- if(typeof settings.onSelect == 'function') {
+ if (typeof settings.onSelect == 'function') {
this.onSelect = settings.onSelect;
}
-
+
// set onPreviewLoaded callback
- if(typeof settings.onPreviewLoaded == 'function') {
+ if (typeof settings.onPreviewLoaded == 'function') {
this.onPreviewLoaded = settings.onPreviewLoaded;
}
-
+
availableWidgets = widgetsHelper.getAvailableWidgets();
-
+
var categoryList = createWidgetCategoryList();
-
- $('li', categoryList).on('mouseover', function(){
+
+ $('li', categoryList).on('mouseover', function () {
var category = $(this).text();
var widgets = availableWidgets[category];
$('li', categoryList).removeClass(settings.choosenClass);
@@ -433,7 +423,7 @@ widgetsHelper.getEmptyWidgetHtml = function (uniqueId, widgetName)
};
}
});
-
+
/**
* Makes widgetPreview available with $().widgetPreview()
*/
diff --git a/plugins/DoNotTrack/DoNotTrack.php b/plugins/DoNotTrack/DoNotTrack.php
index e609f5e9af..16d8cbf7ed 100644
--- a/plugins/DoNotTrack/DoNotTrack.php
+++ b/plugins/DoNotTrack/DoNotTrack.php
@@ -18,58 +18,57 @@
*/
class Piwik_DoNotTrack extends Piwik_Plugin
{
- /**
- * Return information about this plugin.
- *
- * @see Piwik_Plugin
- *
- * @return array
- */
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('DoNotTrack_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- 'translationAvailable' => false,
- 'TrackerPlugin' => true,
- );
- }
+ /**
+ * Return information about this plugin.
+ *
+ * @see Piwik_Plugin
+ *
+ * @return array
+ */
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('DoNotTrack_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ 'translationAvailable' => false,
+ 'TrackerPlugin' => true,
+ );
+ }
- public function getListHooksRegistered()
- {
- return array(
- 'Tracker.Visit.isExcluded' => 'checkHeader',
- );
- }
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'Tracker.Visit.isExcluded' => 'checkHeader',
+ );
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function checkHeader($notification)
- {
- if((isset($_SERVER['HTTP_X_DO_NOT_TRACK']) && $_SERVER['HTTP_X_DO_NOT_TRACK'] === '1')
- || (isset($_SERVER['HTTP_DNT']) && substr($_SERVER['HTTP_DNT'], 0, 1) === '1'))
- {
- $ua = Piwik_Tracker_Visit::getUserAgent($_REQUEST);
- if(strpos($ua, 'MSIE 10') !== false)
- {
- printDebug("INTERNET EXPLORER 10 Enables DNT by default, so Piwik ignores DNT for all IE10 browsers...");
- return;
- }
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function checkHeader($notification)
+ {
+ if ((isset($_SERVER['HTTP_X_DO_NOT_TRACK']) && $_SERVER['HTTP_X_DO_NOT_TRACK'] === '1')
+ || (isset($_SERVER['HTTP_DNT']) && substr($_SERVER['HTTP_DNT'], 0, 1) === '1')
+ ) {
+ $ua = Piwik_Tracker_Visit::getUserAgent($_REQUEST);
+ if (strpos($ua, 'MSIE 10') !== false) {
+ printDebug("INTERNET EXPLORER 10 Enables DNT by default, so Piwik ignores DNT for all IE10 browsers...");
+ return;
+ }
- $exclude =& $notification->getNotificationObject();
- $exclude = true;
- printDebug("DoNotTrack found.");
+ $exclude =& $notification->getNotificationObject();
+ $exclude = true;
+ printDebug("DoNotTrack found.");
- $trackingCookie = Piwik_Tracker_IgnoreCookie::getTrackingCookie();
- $trackingCookie->delete();
+ $trackingCookie = Piwik_Tracker_IgnoreCookie::getTrackingCookie();
+ $trackingCookie->delete();
- // this is an optional supplement to the site's tracking status resource at:
- // /.well-known/dnt
- // per Tracking Preference Expression (draft)
- header('Tk: 1');
- }
- }
+ // this is an optional supplement to the site's tracking status resource at:
+ // /.well-known/dnt
+ // per Tracking Preference Expression (draft)
+ header('Tk: 1');
+ }
+ }
}
diff --git a/plugins/ExampleAPI/API.php b/plugins/ExampleAPI/API.php
index 7f9e2c86e2..cbae12e22a 100644
--- a/plugins/ExampleAPI/API.php
+++ b/plugins/ExampleAPI/API.php
@@ -10,168 +10,167 @@
*/
/**
- * The ExampleAPI is useful to developers building a custom Piwik plugin.
- *
+ * The ExampleAPI is useful to developers building a custom Piwik plugin.
+ *
* Please see the <a href='http://dev.piwik.org/trac/browser/trunk/plugins/ExampleAPI/API.php#L1' target='_blank'>source code in in the file plugins/ExampleAPI/API.php</a> for more documentation.
* @package Piwik_ExampleAPI
*/
class Piwik_ExampleAPI_API
{
- /**
- * * This is an example of a basic API file. Each plugin can have one public API.
- * Each public function in this class will be available to be called via the API.
- * Protected and private members will not be callable.
- * Functions can be called internally using the PHP objects directly, or via the
- * Piwik Web APIs, using HTTP requests. For more information, check out:
- * http://piwik.org/docs/analytics-api/calling-techniques
- *
- * Parameters are passed automatically from the GET request to the API functions.
- *
- * Common API uses include:
- * - requesting stats for a given date and period, for one or several websites
- * - creating, editing, deleting entities (Goals, Websites, Users)
- * - any logic that could be useful to a larger scope than the Controller (make a setting editable for example)
- *
- * It is highly recommended that all the plugin logic is done inside API implementations, and the
- * Controller and other objects would all call the API internally using, eg.
- * Piwik_ExampleAPI_API::getInstance()->getSum(1, 2);
- *
- */
- static private $instance = null;
-
- /**
- * Singleton
- * @return Piwik_ExampleAPI_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Get Piwik version
- * @return string
- */
- public function getPiwikVersion()
- {
- Piwik::checkUserHasSomeViewAccess();
- return Piwik_Version::VERSION;
- }
-
- /**
- * Get Answer to Life
- * @return integer
- */
- public function getAnswerToLife()
- {
- return 42;
- }
-
- /**
- * Returns a custom object.
- * API format conversion will fail for this custom object.
- * If used internally, the data structure can be returned untouched by using
- * the API parameter 'format=original'
- *
- * @return Piwik_MagicObject Will return a standard Piwik error when called from the Web APIs
- */
- public function getObject()
- {
- return new Piwik_MagicObject();
- }
-
- /**
- * Sums two floats and returns the result.
- * The paramaters are set automatically from the GET request
- * when the API function is called. You can also use default values
- * as shown in this example.
- *
- * @param float $a
- * @param float $b
- * @return float
- */
- public function getSum($a = 0, $b = 0)
- {
- return (float)($a + $b);
- }
-
- /**
- * Returns null value
- *
- * @return null
- */
- public function getNull()
- {
- return null;
- }
-
- /**
- * Get array of descriptive text
- * When called from the Web API, you see that simple arrays like this one
- * are automatically converted in the various formats (xml, csv, etc.)
- *
- * @return array
- */
- public function getDescriptionArray()
- {
- return array('piwik','open source','web analytics','free', 'Strong message: Свободный Тибет');
- }
-
- /**
- * Returns a custom data table.
- * This data table will be converted to all available formats
- * when requested in the API request.
- *
- * @return Piwik_DataTable
- */
- public function getCompetitionDatatable()
- {
- $dataTable = new Piwik_DataTable();
-
- $row1 = new Piwik_DataTable_Row();
- $row1->setColumns( array('name' => 'piwik', 'license' => 'GPL'));
-
- // Rows Metadata is useful to store non stats data for example (logos, urls, etc.)
- // When printed out, they are simply merged with columns
- $row1->setMetadata('logo', 'logo.png');
- $dataTable->addRow($row1);
-
- $dataTable->addRowFromSimpleArray( array('name' => 'google analytics', 'license' => 'commercial') );
-
- return $dataTable;
- }
-
- /**
- * Get more information on the Answer to Life...
- *
- * @return string
- */
- public function getMoreInformationAnswerToLife()
- {
- return "Check http://en.wikipedia.org/wiki/The_Answer_to_Life,_the_Universe,_and_Everything";
- }
-
- /**
- * Returns a Multidimensional Array
- * Only supported in JSON
- *
- * @return array
- */
- public function getMultiArray()
- {
- $return = array(
- 'Limitation' => array(
- "Multi dimensional arrays is only supported by format=JSON",
- "Known limitation"
- ),
- 'Second Dimension' => array( true, false, 1, 0, 152, 'test', array( 42 => 'end') ),
- );
- return $return;
- }
+ /**
+ * * This is an example of a basic API file. Each plugin can have one public API.
+ * Each public function in this class will be available to be called via the API.
+ * Protected and private members will not be callable.
+ * Functions can be called internally using the PHP objects directly, or via the
+ * Piwik Web APIs, using HTTP requests. For more information, check out:
+ * http://piwik.org/docs/analytics-api/calling-techniques
+ *
+ * Parameters are passed automatically from the GET request to the API functions.
+ *
+ * Common API uses include:
+ * - requesting stats for a given date and period, for one or several websites
+ * - creating, editing, deleting entities (Goals, Websites, Users)
+ * - any logic that could be useful to a larger scope than the Controller (make a setting editable for example)
+ *
+ * It is highly recommended that all the plugin logic is done inside API implementations, and the
+ * Controller and other objects would all call the API internally using, eg.
+ * Piwik_ExampleAPI_API::getInstance()->getSum(1, 2);
+ *
+ */
+ static private $instance = null;
+
+ /**
+ * Singleton
+ * @return Piwik_ExampleAPI_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Get Piwik version
+ * @return string
+ */
+ public function getPiwikVersion()
+ {
+ Piwik::checkUserHasSomeViewAccess();
+ return Piwik_Version::VERSION;
+ }
+
+ /**
+ * Get Answer to Life
+ * @return integer
+ */
+ public function getAnswerToLife()
+ {
+ return 42;
+ }
+
+ /**
+ * Returns a custom object.
+ * API format conversion will fail for this custom object.
+ * If used internally, the data structure can be returned untouched by using
+ * the API parameter 'format=original'
+ *
+ * @return Piwik_MagicObject Will return a standard Piwik error when called from the Web APIs
+ */
+ public function getObject()
+ {
+ return new Piwik_MagicObject();
+ }
+
+ /**
+ * Sums two floats and returns the result.
+ * The paramaters are set automatically from the GET request
+ * when the API function is called. You can also use default values
+ * as shown in this example.
+ *
+ * @param float $a
+ * @param float $b
+ * @return float
+ */
+ public function getSum($a = 0, $b = 0)
+ {
+ return (float)($a + $b);
+ }
+
+ /**
+ * Returns null value
+ *
+ * @return null
+ */
+ public function getNull()
+ {
+ return null;
+ }
+
+ /**
+ * Get array of descriptive text
+ * When called from the Web API, you see that simple arrays like this one
+ * are automatically converted in the various formats (xml, csv, etc.)
+ *
+ * @return array
+ */
+ public function getDescriptionArray()
+ {
+ return array('piwik', 'open source', 'web analytics', 'free', 'Strong message: Свободный Тибет');
+ }
+
+ /**
+ * Returns a custom data table.
+ * This data table will be converted to all available formats
+ * when requested in the API request.
+ *
+ * @return Piwik_DataTable
+ */
+ public function getCompetitionDatatable()
+ {
+ $dataTable = new Piwik_DataTable();
+
+ $row1 = new Piwik_DataTable_Row();
+ $row1->setColumns(array('name' => 'piwik', 'license' => 'GPL'));
+
+ // Rows Metadata is useful to store non stats data for example (logos, urls, etc.)
+ // When printed out, they are simply merged with columns
+ $row1->setMetadata('logo', 'logo.png');
+ $dataTable->addRow($row1);
+
+ $dataTable->addRowFromSimpleArray(array('name' => 'google analytics', 'license' => 'commercial'));
+
+ return $dataTable;
+ }
+
+ /**
+ * Get more information on the Answer to Life...
+ *
+ * @return string
+ */
+ public function getMoreInformationAnswerToLife()
+ {
+ return "Check http://en.wikipedia.org/wiki/The_Answer_to_Life,_the_Universe,_and_Everything";
+ }
+
+ /**
+ * Returns a Multidimensional Array
+ * Only supported in JSON
+ *
+ * @return array
+ */
+ public function getMultiArray()
+ {
+ $return = array(
+ 'Limitation' => array(
+ "Multi dimensional arrays is only supported by format=JSON",
+ "Known limitation"
+ ),
+ 'Second Dimension' => array(true, false, 1, 0, 152, 'test', array(42 => 'end')),
+ );
+ return $return;
+ }
}
/**
@@ -181,7 +180,11 @@ class Piwik_ExampleAPI_API
*/
class Piwik_MagicObject
{
- function Incredible() { return 'Incroyable'; }
- protected $wonderful = 'magnifique';
- public $great = 'formidable';
+ function Incredible()
+ {
+ return 'Incroyable';
+ }
+
+ protected $wonderful = 'magnifique';
+ public $great = 'formidable';
}
diff --git a/plugins/ExampleAPI/ExampleAPI.php b/plugins/ExampleAPI/ExampleAPI.php
index 4e251121b1..f26630e7ee 100644
--- a/plugins/ExampleAPI/ExampleAPI.php
+++ b/plugins/ExampleAPI/ExampleAPI.php
@@ -1,35 +1,35 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_ExampleAPI
*/
-/**
+/**
* ExampleAPI plugin
*
* @package Piwik_ExampleAPI
*/
class Piwik_ExampleAPI extends Piwik_Plugin
{
- /**
- * Return information about this plugin.
- *
- * @see Piwik_Plugin
- *
- * @return array
- */
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('ExampleAPI_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => '0.1',
- );
- }
+ /**
+ * Return information about this plugin.
+ *
+ * @see Piwik_Plugin
+ *
+ * @return array
+ */
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('ExampleAPI_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => '0.1',
+ );
+ }
}
diff --git a/plugins/ExamplePlugin/Controller.php b/plugins/ExamplePlugin/Controller.php
index e3e56cae3b..c4d7c494ce 100644
--- a/plugins/ExamplePlugin/Controller.php
+++ b/plugins/ExamplePlugin/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_ExamplePlugin
*/
@@ -14,107 +14,107 @@
* @package Piwik_ExamplePlugin
*/
class Piwik_ExamplePlugin_Controller extends Piwik_Controller
-{
- /**
- * Go to /piwik/?module=ExamplePlugin&action=helloWorld to execute this method
- *
- */
- function helloWorld()
- {
- echo "<p>Hello world! <br />";
- echo "Happy coding with Piwik :)</p>";
- }
-
- /**
- * See the result on piwik/?module=ExamplePlugin&action=exampleWidget
- * or in the dashboard > Add a new widget
- *
- */
- function exampleWidget()
- {
- echo "<p>Hello world! <br /> You can output whatever you want in widgets, and put them on dashboard or everywhere on the web (in your blog, website, etc.).
+{
+ /**
+ * Go to /piwik/?module=ExamplePlugin&action=helloWorld to execute this method
+ *
+ */
+ function helloWorld()
+ {
+ echo "<p>Hello world! <br />";
+ echo "Happy coding with Piwik :)</p>";
+ }
+
+ /**
+ * See the result on piwik/?module=ExamplePlugin&action=exampleWidget
+ * or in the dashboard > Add a new widget
+ *
+ */
+ function exampleWidget()
+ {
+ echo "<p>Hello world! <br /> You can output whatever you want in widgets, and put them on dashboard or everywhere on the web (in your blog, website, etc.).
<br />Widgets can include graphs, tables, flash, text, images, etc.
<br />It's very easy to create a new plugin and widgets in Piwik. Have a look at this example file (/plugins/ExamplePlugin/ExamplePlugin.php).
<div id='happycoding'><i>Happy coding!</i></div>
<div id='jsenabled'>You can easily use Jquery in widgets</div>
<p>
<script type=\"text/javascript\">$('#happycoding').hide().fadeIn(5000);$('#jsenabled').hide().css({'color':'red'}).fadeIn(10000);</script>";
- }
-
- function photostreamMatt()
- {
- echo '<object width="400" height="400"> <param name="flashvars" value="offsite=true&lang=en-us&page_show_url=%2Fphotos%2Fmatthieu-aubry%2Fsets%2F72157602308487455%2Fshow%2F&page_show_back_url=%2Fphotos%2Fmatthieu-aubry%2Fsets%2F72157602308487455%2F&set_id=72157602308487455&jump_to="></param> <param name="movie" value="http://www.flickr.com/apps/slideshow/show.swf?v=109615"></param> <param name="allowFullScreen" value="true"></param><embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/slideshow/show.swf?v=109615" allowFullScreen="true" flashvars="offsite=true&lang=en-us&page_show_url=%2Fphotos%2Fmatthieu-aubry%2Fsets%2F72157602308487455%2Fshow%2F&page_show_back_url=%2Fphotos%2Fmatthieu-aubry%2Fsets%2F72157602308487455%2F&set_id=72157602308487455&jump_to=" width="400" height="400"></embed></object>';
- }
-
- /**
- * this widgets shows how to make a remote API request to piwik.org
- * you find the main JS code in templates/piwikDownloadCount.tpl
- */
- function piwikDownloads()
- {
- $view = Piwik_View::factory('piwikDownloads');
- $this->setGeneralVariablesView($view);
- echo $view->render();
- }
-
- /**
- * This method displays a text containing an help about "How to build plugins for Piwik".
- * This help is then used on http://piwik.org/docs/plugins/functions
- *
- */
- function index()
- {
- $out = '';
- $out .= '<i>This page aims to list the different functions you can use when programming plugins for Piwik.</i><br />';
- $out .= '<b>Be careful, the following APIs may change in the near future as Piwik is still in development.</b><br />';
-
- $out .= '<h2>General</h2>';
- $out .= '<h3>Accessible from your plugin controller</h3>';
-
- $out .= '<code>$this->date</code> = current selected <b>Piwik_Date</b> object (<a href="https://github.com/piwik/piwik/blob/master/core/Date.php">class</a>)<br />';
- $out .= '<code>$period = Piwik_Common::getRequestVar("period");</code> - Get the current selected period<br />';
- $out .= '<code>$idSite = Piwik_Common::getRequestVar("idSite");</code> - Get the selected idSite<br />';
- $out .= '<code>$site = new Piwik_Site($idSite);</code> - Build the Piwik_Site object (<a href="https://github.com/piwik/piwik/tree/master/core/Site.php">class</a>)<br />';
- $out .= '<code>$this->str_date</code> = current selected date in YYYY-MM-DD format<br />';
-
- $out .= '<h3>Misc</h3>';
- $out .= '<code>Piwik_AddMenu( $mainMenuName, $subMenuName, $url );</code> - Adds an entry to the menu in the Piwik interface (See the example in the <a href="https://github.com/piwik/piwik/blob/1.0/plugins/UserCountry/UserCountry.php#L76">UserCountry Plugin file</a>)<br />';
- $out .= '<code>Piwik_AddWidget( $widgetCategory, $widgetName, $controllerName, $controllerAction, $customParameters = array());</code> - Adds a widget that users can add in the dashboard, or export using the Widgets link at the top of the screen. See the example in the <a href="https://github.com/piwik/piwik/blob/1.0/plugins/UserCountry/UserCountry.php#L70">UserCountry Plugin file</a> or any other plugin)<br />';
- $out .= '<code>Piwik_Common::prefixTable("site")</code> = <b>' . Piwik_Common::prefixTable("site") . '</b><br />';
-
-
- $out .= '<h2>User access</h2>';
- $out .= '<code>Piwik::getCurrentUserLogin()</code> = <b>' . Piwik::getCurrentUserLogin() . '</b><br />';
- $out .= '<code>Piwik::isUserHasSomeAdminAccess()</code> = <b>' . self::boolToString(Piwik::isUserHasSomeAdminAccess()) . '</b><br />';
- $out .= '<code>Piwik::isUserHasAdminAccess( array $idSites = array(1,2) )</code> = <b>' . self::boolToString(Piwik::isUserHasAdminAccess(array(1,2) )) . '</b><br />';
- $out .= '<code>Piwik::isUserHasViewAccess( array $idSites = array(1) ) </code> = <b>' . self::boolToString(Piwik::isUserHasViewAccess(array(1))) . '</b><br />';
- $out .= '<code>Piwik::isUserIsSuperUser()</code> = <b>' . self::boolToString(Piwik::isUserIsSuperUser()) . '</b><br />';
-
- $out .= '<h2>Execute SQL queries</h2>';
- $txtQuery = "SELECT token_auth FROM ".Piwik_Common::prefixTable('user')." WHERE login = ?";
- $result = Piwik_FetchOne($txtQuery, array('anonymous'));
- $out .= '<code>Piwik_FetchOne("'.$txtQuery.'", array("anonymous"))</code> = <b>' . var_export($result,true) . '</b><br />';
- $out .= '<br />';
-
- $query = Piwik_Query($txtQuery, array('anonymous'));
- $fetched = $query->fetch();
- $token_auth = $fetched['token_auth'];
-
- $out .= '<code>$query = Piwik_Query("'.$txtQuery.'", array("anonymous"))</code><br />';
- $out .= '<code>$fetched = $query->fetch();</code><br />';
- $out .= 'At this point, we have: <code>$fetched[\'token_auth\'] == <b>'.var_export($token_auth,true) . '</b></code><br />';
-
- $out .= '<h2>Example Sites information API</h2>';
- $out .= '<code>Piwik_SitesManager_API::getInstance()->getSitesWithViewAccess()</code> = <b><pre>' .var_export(Piwik_SitesManager_API::getInstance()->getSitesWithViewAccess(),true) . '</pre></b><br />';
- $out .= '<code>Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess()</code> = <b><pre>' .var_export(Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess(),true) . '</pre></b><br />';
-
- $out .= '<h2>Example API Users information</h2>';
- $out .= 'View the list of API methods you can call on <a href="http://piwik.org/docs/analytics-api/reference">API reference</a><br />';
- $out .= 'For example you can try <code>Piwik_UsersManager_API::getInstance()->getUsersSitesFromAccess("view");</code> or <code>Piwik_UsersManager_API::getInstance()->deleteUser("userToDelete");</code><br />';
-
- $out .= '<h2>Javascript in Piwik</h2>';
- $out .= '<h3>i18n internationalization</h3>';
- $out .= 'In order to translate strings within Javascript code, you can use the javascript function _pk_translate( token );.
+ }
+
+ function photostreamMatt()
+ {
+ echo '<object width="400" height="400"> <param name="flashvars" value="offsite=true&lang=en-us&page_show_url=%2Fphotos%2Fmatthieu-aubry%2Fsets%2F72157602308487455%2Fshow%2F&page_show_back_url=%2Fphotos%2Fmatthieu-aubry%2Fsets%2F72157602308487455%2F&set_id=72157602308487455&jump_to="></param> <param name="movie" value="http://www.flickr.com/apps/slideshow/show.swf?v=109615"></param> <param name="allowFullScreen" value="true"></param><embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/slideshow/show.swf?v=109615" allowFullScreen="true" flashvars="offsite=true&lang=en-us&page_show_url=%2Fphotos%2Fmatthieu-aubry%2Fsets%2F72157602308487455%2Fshow%2F&page_show_back_url=%2Fphotos%2Fmatthieu-aubry%2Fsets%2F72157602308487455%2F&set_id=72157602308487455&jump_to=" width="400" height="400"></embed></object>';
+ }
+
+ /**
+ * this widgets shows how to make a remote API request to piwik.org
+ * you find the main JS code in templates/piwikDownloadCount.tpl
+ */
+ function piwikDownloads()
+ {
+ $view = Piwik_View::factory('piwikDownloads');
+ $this->setGeneralVariablesView($view);
+ echo $view->render();
+ }
+
+ /**
+ * This method displays a text containing an help about "How to build plugins for Piwik".
+ * This help is then used on http://piwik.org/docs/plugins/functions
+ *
+ */
+ function index()
+ {
+ $out = '';
+ $out .= '<i>This page aims to list the different functions you can use when programming plugins for Piwik.</i><br />';
+ $out .= '<b>Be careful, the following APIs may change in the near future as Piwik is still in development.</b><br />';
+
+ $out .= '<h2>General</h2>';
+ $out .= '<h3>Accessible from your plugin controller</h3>';
+
+ $out .= '<code>$this->date</code> = current selected <b>Piwik_Date</b> object (<a href="https://github.com/piwik/piwik/blob/master/core/Date.php">class</a>)<br />';
+ $out .= '<code>$period = Piwik_Common::getRequestVar("period");</code> - Get the current selected period<br />';
+ $out .= '<code>$idSite = Piwik_Common::getRequestVar("idSite");</code> - Get the selected idSite<br />';
+ $out .= '<code>$site = new Piwik_Site($idSite);</code> - Build the Piwik_Site object (<a href="https://github.com/piwik/piwik/tree/master/core/Site.php">class</a>)<br />';
+ $out .= '<code>$this->str_date</code> = current selected date in YYYY-MM-DD format<br />';
+
+ $out .= '<h3>Misc</h3>';
+ $out .= '<code>Piwik_AddMenu( $mainMenuName, $subMenuName, $url );</code> - Adds an entry to the menu in the Piwik interface (See the example in the <a href="https://github.com/piwik/piwik/blob/1.0/plugins/UserCountry/UserCountry.php#L76">UserCountry Plugin file</a>)<br />';
+ $out .= '<code>Piwik_AddWidget( $widgetCategory, $widgetName, $controllerName, $controllerAction, $customParameters = array());</code> - Adds a widget that users can add in the dashboard, or export using the Widgets link at the top of the screen. See the example in the <a href="https://github.com/piwik/piwik/blob/1.0/plugins/UserCountry/UserCountry.php#L70">UserCountry Plugin file</a> or any other plugin)<br />';
+ $out .= '<code>Piwik_Common::prefixTable("site")</code> = <b>' . Piwik_Common::prefixTable("site") . '</b><br />';
+
+
+ $out .= '<h2>User access</h2>';
+ $out .= '<code>Piwik::getCurrentUserLogin()</code> = <b>' . Piwik::getCurrentUserLogin() . '</b><br />';
+ $out .= '<code>Piwik::isUserHasSomeAdminAccess()</code> = <b>' . self::boolToString(Piwik::isUserHasSomeAdminAccess()) . '</b><br />';
+ $out .= '<code>Piwik::isUserHasAdminAccess( array $idSites = array(1,2) )</code> = <b>' . self::boolToString(Piwik::isUserHasAdminAccess(array(1, 2))) . '</b><br />';
+ $out .= '<code>Piwik::isUserHasViewAccess( array $idSites = array(1) ) </code> = <b>' . self::boolToString(Piwik::isUserHasViewAccess(array(1))) . '</b><br />';
+ $out .= '<code>Piwik::isUserIsSuperUser()</code> = <b>' . self::boolToString(Piwik::isUserIsSuperUser()) . '</b><br />';
+
+ $out .= '<h2>Execute SQL queries</h2>';
+ $txtQuery = "SELECT token_auth FROM " . Piwik_Common::prefixTable('user') . " WHERE login = ?";
+ $result = Piwik_FetchOne($txtQuery, array('anonymous'));
+ $out .= '<code>Piwik_FetchOne("' . $txtQuery . '", array("anonymous"))</code> = <b>' . var_export($result, true) . '</b><br />';
+ $out .= '<br />';
+
+ $query = Piwik_Query($txtQuery, array('anonymous'));
+ $fetched = $query->fetch();
+ $token_auth = $fetched['token_auth'];
+
+ $out .= '<code>$query = Piwik_Query("' . $txtQuery . '", array("anonymous"))</code><br />';
+ $out .= '<code>$fetched = $query->fetch();</code><br />';
+ $out .= 'At this point, we have: <code>$fetched[\'token_auth\'] == <b>' . var_export($token_auth, true) . '</b></code><br />';
+
+ $out .= '<h2>Example Sites information API</h2>';
+ $out .= '<code>Piwik_SitesManager_API::getInstance()->getSitesWithViewAccess()</code> = <b><pre>' . var_export(Piwik_SitesManager_API::getInstance()->getSitesWithViewAccess(), true) . '</pre></b><br />';
+ $out .= '<code>Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess()</code> = <b><pre>' . var_export(Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess(), true) . '</pre></b><br />';
+
+ $out .= '<h2>Example API Users information</h2>';
+ $out .= 'View the list of API methods you can call on <a href="http://piwik.org/docs/analytics-api/reference">API reference</a><br />';
+ $out .= 'For example you can try <code>Piwik_UsersManager_API::getInstance()->getUsersSitesFromAccess("view");</code> or <code>Piwik_UsersManager_API::getInstance()->deleteUser("userToDelete");</code><br />';
+
+ $out .= '<h2>Javascript in Piwik</h2>';
+ $out .= '<h3>i18n internationalization</h3>';
+ $out .= 'In order to translate strings within Javascript code, you can use the javascript function _pk_translate( token );.
<ul><li>The "token" parameter is the string unique key found in the translation file. For this token string to be available in Javascript, you must
suffix your token by "_js" in the language file. For example, you can add <code>\'Goals_AddGoal_js\' => \'Add Goal\',</code> in the lang/en.php file</li>
<li>You then need to instruct Piwik to load your Javascript translations for your plugin; by default, all translation strings are not loaded in Javascript for performance reasons. This can be done by calling a custom-made Smarty modifier before the Javascript code requiring translations, eg.
@@ -122,20 +122,20 @@ class Piwik_ExamplePlugin_Controller extends Piwik_Controller
</li><li>You can then print this string from your JS code by doing <code>_pk_translate(\'Goals_AddGoal_js\');</code>.
</li></ul>';
- $out .= '<h3>Reload a widget in the dashboard</h3>';
- $out .= 'It is sometimes useful to reload one widget in the dashboard (for example, every 20 seconds for a real time widget, or after a setting change).
+ $out .= '<h3>Reload a widget in the dashboard</h3>';
+ $out .= 'It is sometimes useful to reload one widget in the dashboard (for example, every 20 seconds for a real time widget, or after a setting change).
You can easily force your widget to reload in the dashboard by calling the helper function <code>$(this).parents(\'[widgetId]\').dashboardWidget(\'reload\');</code>.';
-
- $out .= '<h2>Smarty plugins</h2>';
- $out .= 'There are some builtin plugins for Smarty especially developped for Piwik. <br />
+
+ $out .= '<h2>Smarty plugins</h2>';
+ $out .= 'There are some builtin plugins for Smarty especially developped for Piwik. <br />
You can find them on the <a href="https://github.com/piwik/piwik/tree/master/core/SmartyPlugins">Git at /core/SmartyPlugins</a>. <br />
More documentation to come about smarty plugins.<br />';
-
- echo $out;
- }
-
- static private function boolToString($bool)
- {
- return $bool ? "true" : "false";
- }
+
+ echo $out;
+ }
+
+ static private function boolToString($bool)
+ {
+ return $bool ? "true" : "false";
+ }
}
diff --git a/plugins/ExamplePlugin/ExamplePlugin.php b/plugins/ExamplePlugin/ExamplePlugin.php
index 24eab7a811..1334436bf4 100644
--- a/plugins/ExamplePlugin/ExamplePlugin.php
+++ b/plugins/ExamplePlugin/ExamplePlugin.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_ExamplePlugin
*/
@@ -15,65 +15,65 @@
*/
class Piwik_ExamplePlugin extends Piwik_Plugin
{
- /**
- * Return information about this plugin.
- *
- * @see Piwik_Plugin
- *
- * @return array
- */
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('ExamplePlugin_PluginDescription'),
- 'homepage' => 'http://piwik.org/',
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'license' => 'GPL v3 or later',
- 'license_homepage' => 'http://www.gnu.org/licenses/gpl.html',
- 'version' => '0.1',
- 'translationAvailable' => true,
- );
- }
-
- public function getListHooksRegistered()
- {
- return array(
+ /**
+ * Return information about this plugin.
+ *
+ * @see Piwik_Plugin
+ *
+ * @return array
+ */
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('ExamplePlugin_PluginDescription'),
+ 'homepage' => 'http://piwik.org/',
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'license' => 'GPL v3 or later',
+ 'license_homepage' => 'http://www.gnu.org/licenses/gpl.html',
+ 'version' => '0.1',
+ 'translationAvailable' => true,
+ );
+ }
+
+ public function getListHooksRegistered()
+ {
+ return array(
// 'Controller.renderView' => 'addUniqueVisitorsColumnToGivenReport',
'WidgetsList.add' => 'addWidgets',
- );
- }
+ );
+ }
+
+ function activate()
+ {
+ // Executed every time plugin is Enabled
+ }
+
+ function deactivate()
+ {
+ // Executed every time plugin is disabled
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function addUniqueVisitorsColumnToGivenReport($notification)
+ {
+ $view = $notification->getNotificationInfo();
+ $view = $view['view'];
+ if ($view->getCurrentControllerName() == 'Referers'
+ && $view->getCurrentControllerAction() == 'getWebsites'
+ ) {
+ $view->addColumnToDisplay('nb_uniq_visitors');
+ }
+ }
- function activate()
- {
- // Executed every time plugin is Enabled
- }
-
- function deactivate()
- {
- // Executed every time plugin is disabled
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function addUniqueVisitorsColumnToGivenReport($notification)
- {
- $view = $notification->getNotificationInfo();
- $view = $view['view'];
- if($view->getCurrentControllerName() == 'Referers'
- && $view->getCurrentControllerAction() == 'getWebsites')
- {
- $view->addColumnToDisplay('nb_uniq_visitors');
- }
- }
-
- function addWidgets()
- {
- // we register the widgets so they appear in the "Add a new widget" window in the dashboard
- // Note that the first two parameters can be either a normal string, or an index to a translation string
- Piwik_AddWidget('ExamplePlugin_exampleWidgets', 'ExamplePlugin_exampleWidget', 'ExamplePlugin', 'exampleWidget');
- Piwik_AddWidget('ExamplePlugin_exampleWidgets', 'ExamplePlugin_photostreamMatt', 'ExamplePlugin', 'photostreamMatt');
- Piwik_AddWidget('ExamplePlugin_exampleWidgets', 'ExamplePlugin_piwikForumVisits', 'ExamplePlugin', 'piwikDownloads');
- }
+ function addWidgets()
+ {
+ // we register the widgets so they appear in the "Add a new widget" window in the dashboard
+ // Note that the first two parameters can be either a normal string, or an index to a translation string
+ Piwik_AddWidget('ExamplePlugin_exampleWidgets', 'ExamplePlugin_exampleWidget', 'ExamplePlugin', 'exampleWidget');
+ Piwik_AddWidget('ExamplePlugin_exampleWidgets', 'ExamplePlugin_photostreamMatt', 'ExamplePlugin', 'photostreamMatt');
+ Piwik_AddWidget('ExamplePlugin_exampleWidgets', 'ExamplePlugin_piwikForumVisits', 'ExamplePlugin', 'piwikDownloads');
+ }
}
diff --git a/plugins/ExamplePlugin/config/local.config.sample.php b/plugins/ExamplePlugin/config/local.config.sample.php
index 24da8c15be..fe6e62b365 100644
--- a/plugins/ExamplePlugin/config/local.config.sample.php
+++ b/plugins/ExamplePlugin/config/local.config.sample.php
@@ -1,6 +1,6 @@
<?php
return array(
- 'id' => 'Example',
- 'name' => 'ExamplePlugin',
- 'description' => 'This is an example',
+ 'id' => 'Example',
+ 'name' => 'ExamplePlugin',
+ 'description' => 'This is an example',
);
diff --git a/plugins/ExamplePlugin/lang/en.php b/plugins/ExamplePlugin/lang/en.php
index ce951d54de..253072064e 100644
--- a/plugins/ExamplePlugin/lang/en.php
+++ b/plugins/ExamplePlugin/lang/en.php
@@ -1,20 +1,20 @@
-<?php
+<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_ExamplePlugin
*/
$translations = array(
- 'ExamplePlugin_PluginDescription' => 'Example Plugin: This plugin shows how to create a very simple plugin, that exports two widgets in the Dashboard.',
- 'ExamplePlugin_exampleWidgets' => 'Example Widgets',
- 'ExamplePlugin_exampleWidget' => 'Example Widget',
- 'ExamplePlugin_photostreamMatt' => 'Matt\'s Photos',
- 'ExamplePlugin_piwikForumVisits' => 'Piwik Forums visits',
- 'ExamplePlugin_PiwikForumReceivedVisits' => 'On %s, Piwik forums received %s visits.'
-
+ 'ExamplePlugin_PluginDescription' => 'Example Plugin: This plugin shows how to create a very simple plugin, that exports two widgets in the Dashboard.',
+ 'ExamplePlugin_exampleWidgets' => 'Example Widgets',
+ 'ExamplePlugin_exampleWidget' => 'Example Widget',
+ 'ExamplePlugin_photostreamMatt' => 'Matt\'s Photos',
+ 'ExamplePlugin_piwikForumVisits' => 'Piwik Forums visits',
+ 'ExamplePlugin_PiwikForumReceivedVisits' => 'On %s, Piwik forums received %s visits.'
+
);
diff --git a/plugins/ExamplePlugin/templates/piwikDownloads.tpl b/plugins/ExamplePlugin/templates/piwikDownloads.tpl
index 910a81f213..22b10df4b3 100644
--- a/plugins/ExamplePlugin/templates/piwikDownloads.tpl
+++ b/plugins/ExamplePlugin/templates/piwikDownloads.tpl
@@ -1,20 +1,20 @@
<div style="padding:1.5em;text-align:center">
- {"ExamplePlugin_PiwikForumReceivedVisits"|translate:$prettyDate:'<b class="piwikDownloadCount_cnt" >...</b>'}
+ {"ExamplePlugin_PiwikForumReceivedVisits"|translate:$prettyDate:'<b class="piwikDownloadCount_cnt" >...</b>'}
</div>
{*
* loading piwik download stats from demo.piwik.org
*}
<script type="text/javascript">
-{literal}
- $.ajax({
- url: "http://demo.piwik.org/?module=API&method=VisitsSummary.getVisits"
- +"&idSite=7&period="+piwik.period+"&date="+broadcast.getValueFromUrl('date')
- +"&token_auth=anonymous&format=json",
- dataType: 'jsonp',
- jsonp: 'callback',
- success: function(data) {
- $('.piwikDownloadCount_cnt').html(data.value);
- }
- });
-{/literal}
+ {literal}
+ $.ajax({
+ url: "http://demo.piwik.org/?module=API&method=VisitsSummary.getVisits"
+ + "&idSite=7&period=" + piwik.period + "&date=" + broadcast.getValueFromUrl('date')
+ + "&token_auth=anonymous&format=json",
+ dataType: 'jsonp',
+ jsonp: 'callback',
+ success: function (data) {
+ $('.piwikDownloadCount_cnt').html(data.value);
+ }
+ });
+ {/literal}
</script>
diff --git a/plugins/ExampleRssWidget/Controller.php b/plugins/ExampleRssWidget/Controller.php
index c356ee7205..41e6691f9d 100644
--- a/plugins/ExampleRssWidget/Controller.php
+++ b/plugins/ExampleRssWidget/Controller.php
@@ -21,21 +21,20 @@ class Piwik_ExampleRssWidget_Controller extends Piwik_Controller
$rss = new Piwik_ExampleRssWidget_Rss('http://feeds.feedburner.com/Piwik');
$rss->showDescription(true);
echo $rss->get();
- } catch(Exception $e) {
+ } catch (Exception $e) {
$this->error($e);
}
}
public function rssChangelog()
{
- try
- {
+ try {
$rss = new Piwik_ExampleRssWidget_Rss('http://feeds.feedburner.com/PiwikReleases');
$rss->setCountPosts(1);
$rss->showDescription(false);
$rss->showContent(true);
echo $rss->get();
- } catch(Exception $e) {
+ } catch (Exception $e) {
$this->error($e);
}
}
diff --git a/plugins/ExampleRssWidget/ExampleRssWidget.php b/plugins/ExampleRssWidget/ExampleRssWidget.php
index 0c174345ee..4fac8a6f73 100644
--- a/plugins/ExampleRssWidget/ExampleRssWidget.php
+++ b/plugins/ExampleRssWidget/ExampleRssWidget.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_ExampleRssWidget
*/
@@ -15,22 +15,22 @@
*/
class Piwik_ExampleRssWidget extends Piwik_Plugin
{
- /**
- * Return information about this plugin.
- *
- * @see Piwik_Plugin
- *
- * @return array
- */
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('ExampleRssWidget_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => '0.1',
- );
- }
+ /**
+ * Return information about this plugin.
+ *
+ * @see Piwik_Plugin
+ *
+ * @return array
+ */
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('ExampleRssWidget_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => '0.1',
+ );
+ }
/**
* Returns a list of registered hooks.
@@ -38,22 +38,22 @@ class Piwik_ExampleRssWidget extends Piwik_Plugin
* @return array
*/
public function getListHooksRegistered()
- {
- return array(
+ {
+ return array(
'AssetManager.getCssFiles' => 'getCssFiles',
- 'WidgetsList.add' => 'addWidgets'
+ 'WidgetsList.add' => 'addWidgets'
);
- }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "plugins/ExampleRssWidget/templates/styles.css";
- }
+ $cssFiles[] = "plugins/ExampleRssWidget/templates/styles.css";
+ }
public function addWidgets()
{
diff --git a/plugins/ExampleRssWidget/Rss.php b/plugins/ExampleRssWidget/Rss.php
index 678001da8d..ee95a8a56d 100644
--- a/plugins/ExampleRssWidget/Rss.php
+++ b/plugins/ExampleRssWidget/Rss.php
@@ -42,8 +42,7 @@ class Piwik_ExampleRssWidget_Rss
public function get()
{
- try
- {
+ try {
$rss = Zend_Feed::import($this->url);
} catch (Zend_Feed_Exception $e) {
echo "Error while importing feed: {$e->getMessage()}\n";
@@ -53,27 +52,23 @@ class Piwik_ExampleRssWidget_Rss
$output = '<div style="padding:10px 15px;"><ul class="rss">';
$i = 0;
- foreach($rss as $post)
- {
+ foreach ($rss as $post) {
$title = $post->title();
$date = @strftime("%B %e, %Y", strtotime($post->pubDate()));
$link = $post->link();
- $output .= '<li><a class="rss-title" title="" target="_blank" href="?module=Proxy&action=redirect&url='.$link.'">'.$title.'</a>'.
- '<span class="rss-date">'.$date.'</span>';
- if($this->showDescription)
- {
- $output .= '<div class="rss-description">'.$post->description().'</div>';
+ $output .= '<li><a class="rss-title" title="" target="_blank" href="?module=Proxy&action=redirect&url=' . $link . '">' . $title . '</a>' .
+ '<span class="rss-date">' . $date . '</span>';
+ if ($this->showDescription) {
+ $output .= '<div class="rss-description">' . $post->description() . '</div>';
}
- if($this->showContent)
- {
- $output .= '<div class="rss-content">'.$post->content().'</div>';
+ if ($this->showContent) {
+ $output .= '<div class="rss-content">' . $post->content() . '</div>';
}
$output .= '</li>';
- if(++$i == $this->count)
- {
+ if (++$i == $this->count) {
break;
}
}
diff --git a/plugins/ExampleRssWidget/templates/styles.css b/plugins/ExampleRssWidget/templates/styles.css
index 185e57088b..5676571a7e 100644
--- a/plugins/ExampleRssWidget/templates/styles.css
+++ b/plugins/ExampleRssWidget/templates/styles.css
@@ -1,28 +1,33 @@
.rss ul {
- list-style:none outside none;
- padding:0;
+ list-style: none outside none;
+ padding: 0;
}
+
.rss li {
- line-height:140%;
- margin:0.5em 0 1em;
+ line-height: 140%;
+ margin: 0.5em 0 1em;
}
-.rss-title, .rss-date {
- float:left;
- font-size:14px;
- line-height:140%;
+
+.rss-title, .rss-date {
+ float: left;
+ font-size: 14px;
+ line-height: 140%;
}
-.rss-title{
- color:#2583AD;
- margin:0 0.5em 0.2em 0;
- font-weight:bold;
-}
+
+.rss-title {
+ color: #2583AD;
+ margin: 0 0.5em 0.2em 0;
+ font-weight: bold;
+}
+
.rss-date {
- color:#999999;
- margin:0;
+ color: #999999;
+ margin: 0;
}
+
.rss-content, .rss-description {
- clear:both;
- line-height:1.5em;
- font-size:11px;
- color:#333333;
+ clear: both;
+ line-height: 1.5em;
+ font-size: 11px;
+ color: #333333;
}
diff --git a/plugins/ExampleUI/API.php b/plugins/ExampleUI/API.php
index c88be1d598..91e29806be 100644
--- a/plugins/ExampleUI/API.php
+++ b/plugins/ExampleUI/API.php
@@ -1,109 +1,107 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_ExampleUI
*/
/**
* ExampleUI API is also an example API useful if you are developing a Piwik plugin.
- *
- * The functions listed in this API are returning the data used in the Controller to draw graphs and
+ *
+ * The functions listed in this API are returning the data used in the Controller to draw graphs and
* display tables. See also the ExampleAPI plugin for an introduction to Piwik APIs.
- *
+ *
* @package Piwik_ExampleUI
*/
-class Piwik_ExampleUI_API
+class Piwik_ExampleUI_API
{
- static private $instance = null;
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- public function getTemperaturesEvolution($date, $period)
- {
- $period = new Piwik_Period_Range($period, 'last30');
- $dateStart = $period->getDateStart()->toString('Y-m-d'); // eg. "2009-04-01"
- $dateEnd = $period->getDateEnd()->toString('Y-m-d'); // eg. "2009-04-30"
-
- // here you could select from your custom table in the database, eg.
- $query = "SELECT AVG(temperature)
+ static private $instance = null;
+
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ public function getTemperaturesEvolution($date, $period)
+ {
+ $period = new Piwik_Period_Range($period, 'last30');
+ $dateStart = $period->getDateStart()->toString('Y-m-d'); // eg. "2009-04-01"
+ $dateEnd = $period->getDateEnd()->toString('Y-m-d'); // eg. "2009-04-30"
+
+ // here you could select from your custom table in the database, eg.
+ $query = "SELECT AVG(temperature)
FROM server_temperatures
WHERE date > ?
AND date < ?
GROUP BY date
ORDER BY date ASC";
- //$result = Piwik_FetchAll($query, array($dateStart, $dateEnd));
- // to keep things simple, we generate the data
- foreach($period->getSubperiods() as $subPeriod)
- {
- $server1 = mt_rand(50,90);
- $server2 = mt_rand(40, 110);
- $value = array('server1' => $server1, 'server2' => $server2);
- $temperatures[$subPeriod->getLocalizedShortString()] = $value;
- }
-
- // convert this array to a DataTable object
- $dataTable = new Piwik_DataTable();
- $dataTable->addRowsFromArrayWithIndexLabel($temperatures);
- return $dataTable;
- }
-
- // we generate an array of random server temperatures
- public function getTemperatures()
- {
- $xAxis = array(
- '0h', '1h', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', '10h', '11h',
- '12h', '13h', '14h', '15h', '16h', '17h', '18h', '19h', '20h', '21h', '22h', '23h',
- );
- $temperatureValues = array_slice(range(50,90), 0, count($xAxis));
- shuffle($temperatureValues);
- $temperatures = array();
- foreach($xAxis as $i => $xAxisLabel) {
- $temperatures[$xAxisLabel] = $temperatureValues[$i];
- }
-
- // convert this array to a DataTable object
- $dataTable = new Piwik_DataTable();
- $dataTable->addRowsFromArrayWithIndexLabel($temperatures);
- return $dataTable;
- }
-
- public function getPlanetRatios()
- {
- $planetRatios = array(
- 'Mercury' => 0.382,
- 'Venus' => 0.949,
- 'Earth' => 1.00,
- 'Mars' => 0.532,
- 'Jupiter' => 11.209,
- 'Saturn' => 9.449,
- 'Uranus' => 4.007,
- 'Neptune' => 3.883,
- );
- // convert this array to a DataTable object
- $dataTable = new Piwik_DataTable();
- $dataTable->addRowsFromArrayWithIndexLabel($planetRatios);
- return $dataTable;
- }
-
- public function getPlanetRatiosWithLogos()
- {
- $planetsDataTable = $this->getPlanetRatios();
- foreach($planetsDataTable->getRows() as $row)
- {
- $row->addMetadata('logo', "plugins/ExampleUI/images/icons-planet/".strtolower($row->getColumn('label').".png"));
- $row->addMetadata('url', "http://en.wikipedia.org/wiki/".$row->getColumn('label'));
- }
- return $planetsDataTable;
- }
+ //$result = Piwik_FetchAll($query, array($dateStart, $dateEnd));
+ // to keep things simple, we generate the data
+ foreach ($period->getSubperiods() as $subPeriod) {
+ $server1 = mt_rand(50, 90);
+ $server2 = mt_rand(40, 110);
+ $value = array('server1' => $server1, 'server2' => $server2);
+ $temperatures[$subPeriod->getLocalizedShortString()] = $value;
+ }
+
+ // convert this array to a DataTable object
+ $dataTable = new Piwik_DataTable();
+ $dataTable->addRowsFromArrayWithIndexLabel($temperatures);
+ return $dataTable;
+ }
+
+ // we generate an array of random server temperatures
+ public function getTemperatures()
+ {
+ $xAxis = array(
+ '0h', '1h', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', '10h', '11h',
+ '12h', '13h', '14h', '15h', '16h', '17h', '18h', '19h', '20h', '21h', '22h', '23h',
+ );
+ $temperatureValues = array_slice(range(50, 90), 0, count($xAxis));
+ shuffle($temperatureValues);
+ $temperatures = array();
+ foreach ($xAxis as $i => $xAxisLabel) {
+ $temperatures[$xAxisLabel] = $temperatureValues[$i];
+ }
+
+ // convert this array to a DataTable object
+ $dataTable = new Piwik_DataTable();
+ $dataTable->addRowsFromArrayWithIndexLabel($temperatures);
+ return $dataTable;
+ }
+
+ public function getPlanetRatios()
+ {
+ $planetRatios = array(
+ 'Mercury' => 0.382,
+ 'Venus' => 0.949,
+ 'Earth' => 1.00,
+ 'Mars' => 0.532,
+ 'Jupiter' => 11.209,
+ 'Saturn' => 9.449,
+ 'Uranus' => 4.007,
+ 'Neptune' => 3.883,
+ );
+ // convert this array to a DataTable object
+ $dataTable = new Piwik_DataTable();
+ $dataTable->addRowsFromArrayWithIndexLabel($planetRatios);
+ return $dataTable;
+ }
+
+ public function getPlanetRatiosWithLogos()
+ {
+ $planetsDataTable = $this->getPlanetRatios();
+ foreach ($planetsDataTable->getRows() as $row) {
+ $row->addMetadata('logo', "plugins/ExampleUI/images/icons-planet/" . strtolower($row->getColumn('label') . ".png"));
+ $row->addMetadata('url', "http://en.wikipedia.org/wiki/" . $row->getColumn('label'));
+ }
+ return $planetsDataTable;
+ }
}
diff --git a/plugins/ExampleUI/Controller.php b/plugins/ExampleUI/Controller.php
index c57bbeb19e..8a997a9e92 100644
--- a/plugins/ExampleUI/Controller.php
+++ b/plugins/ExampleUI/Controller.php
@@ -14,124 +14,126 @@
*/
class Piwik_ExampleUI_Controller extends Piwik_Controller
{
- function dataTables()
- {
- $view = Piwik_ViewDataTable::factory('table');
- $view->init( $this->pluginName, __FUNCTION__, 'ExampleUI.getTemperatures' );
- $view->setColumnTranslation('value', "Temperature in °C");
- $view->setColumnTranslation('label', "Hour of day");
- $view->setSortedColumn('label', 'asc');
- $view->setGraphLimit( 24 );
- $view->setLimit( 24 );
- $view->disableExcludeLowPopulation();
- $view->disableShowAllColumns();
- $view->disableRowEvolution();
- $view->setAxisYUnit('°C'); // useful if the user requests the bar graph
- return $this->renderView($view);
- }
-
- function evolutionGraph()
- {
- echo "<h2>Evolution of server temperatures over the last few days</h2>";
- $this->echoEvolutionGraph();
- }
-
- function echoEvolutionGraph()
- {
- $view = Piwik_ViewDataTable::factory('graphEvolution');
- $view->init( $this->pluginName, __FUNCTION__, 'ExampleUI.getTemperaturesEvolution' );
- $view->setColumnTranslation('server1', "Temperature server piwik.org");
- $view->setColumnTranslation('server2', "Temperature server dev.piwik.org");
- $view->setAxisYUnit('°C'); // useful if the user requests the bar graph
- return $this->renderView($view);
- }
-
- function barGraph()
- {
- $view = Piwik_ViewDataTable::factory('graphVerticalBar');
- $view->init( $this->pluginName, __FUNCTION__, 'ExampleUI.getTemperatures' );
- $view->setColumnTranslation('value', "Temperature");
- $view->setAxisYUnit('°C');
- $view->setGraphLimit( 24 );
- $view->disableFooter();
- return $this->renderView($view);
- }
-
- function pieGraph()
- {
- $view = Piwik_ViewDataTable::factory('graphPie');
- $view->init( $this->pluginName, __FUNCTION__, 'ExampleUI.getPlanetRatios' );
- $view->setColumnsToDisplay( 'value' );
- $view->setColumnTranslation('value', "times the diameter of Earth");
- $view->setGraphLimit( 10 );
- $view->disableFooterIcons();
- return $this->renderView($view);
- }
-
- function tagClouds()
- {
- echo "<h2>Simple tag cloud</h2>";
- $this->echoSimpleTagClouds();
-
- echo "<br /><br /><h2>Advanced tag cloud: with logos and links</h2>
+ function dataTables()
+ {
+ $view = Piwik_ViewDataTable::factory('table');
+ $view->init($this->pluginName, __FUNCTION__, 'ExampleUI.getTemperatures');
+ $view->setColumnTranslation('value', "Temperature in °C");
+ $view->setColumnTranslation('label', "Hour of day");
+ $view->setSortedColumn('label', 'asc');
+ $view->setGraphLimit(24);
+ $view->setLimit(24);
+ $view->disableExcludeLowPopulation();
+ $view->disableShowAllColumns();
+ $view->disableRowEvolution();
+ $view->setAxisYUnit('°C'); // useful if the user requests the bar graph
+ return $this->renderView($view);
+ }
+
+ function evolutionGraph()
+ {
+ echo "<h2>Evolution of server temperatures over the last few days</h2>";
+ $this->echoEvolutionGraph();
+ }
+
+ function echoEvolutionGraph()
+ {
+ $view = Piwik_ViewDataTable::factory('graphEvolution');
+ $view->init($this->pluginName, __FUNCTION__, 'ExampleUI.getTemperaturesEvolution');
+ $view->setColumnTranslation('server1', "Temperature server piwik.org");
+ $view->setColumnTranslation('server2', "Temperature server dev.piwik.org");
+ $view->setAxisYUnit('°C'); // useful if the user requests the bar graph
+ return $this->renderView($view);
+ }
+
+ function barGraph()
+ {
+ $view = Piwik_ViewDataTable::factory('graphVerticalBar');
+ $view->init($this->pluginName, __FUNCTION__, 'ExampleUI.getTemperatures');
+ $view->setColumnTranslation('value', "Temperature");
+ $view->setAxisYUnit('°C');
+ $view->setGraphLimit(24);
+ $view->disableFooter();
+ return $this->renderView($view);
+ }
+
+ function pieGraph()
+ {
+ $view = Piwik_ViewDataTable::factory('graphPie');
+ $view->init($this->pluginName, __FUNCTION__, 'ExampleUI.getPlanetRatios');
+ $view->setColumnsToDisplay('value');
+ $view->setColumnTranslation('value', "times the diameter of Earth");
+ $view->setGraphLimit(10);
+ $view->disableFooterIcons();
+ return $this->renderView($view);
+ }
+
+ function tagClouds()
+ {
+ echo "<h2>Simple tag cloud</h2>";
+ $this->echoSimpleTagClouds();
+
+ echo "<br /><br /><h2>Advanced tag cloud: with logos and links</h2>
<ul style='list-style-type:disc;margin-left:50px'>
<li>The logo size is proportional to the value returned by the API</li>
<li>The logo is linked to a specific URL</li>
</ul><br /><br />";
- $this->echoAdvancedTagClouds();
- }
- function echoSimpleTagClouds()
- {
- $view = Piwik_ViewDataTable::factory('cloud');
- $view->init( $this->pluginName, __FUNCTION__, 'ExampleUI.getPlanetRatios' );
- $view->setColumnsToDisplay( array('label','value') );
- $view->setColumnTranslation('value', "times the diameter of Earth");
- $view->disableFooter();
- $this->renderView($view);
- }
- function echoAdvancedTagClouds()
- {
- $view = Piwik_ViewDataTable::factory('cloud');
- $view->init( $this->pluginName, __FUNCTION__, 'ExampleUI.getPlanetRatiosWithLogos' );
- $view->setDisplayLogoInTagCloud(true);
- $view->disableFooterExceptExportIcons();
- $view->setColumnsToDisplay( array('label','value') );
- $view->setColumnTranslation('value', "times the diameter of Earth");
- $this->renderView($view);
- }
-
- function sparklines()
- {
- require_once PIWIK_INCLUDE_PATH . '/core/SmartyPlugins/function.sparkline.php';
- $srcSparkline1 = Piwik_Url::getCurrentQueryStringWithParametersModified(array('action'=>'generateSparkline', 'server' => 'server1', 'rand'=>mt_rand()));
- $htmlSparkline1 = smarty_function_sparkline(array('src' => $srcSparkline1));
- echo "<div class='sparkline'>$htmlSparkline1 Evolution of temperature for server piwik.org</div>";
-
- $srcSparkline2 = Piwik_Url::getCurrentQueryStringWithParametersModified(array('action'=>'generateSparkline', 'server' => 'server2', 'rand'=>mt_rand()));
- $htmlSparkline2 = smarty_function_sparkline(array('src' => $srcSparkline2));
- echo "<div class='sparkline'>$htmlSparkline2 Evolution of temperature for server dev.piwik.org</div>";
- }
-
- function generateSparkline()
- {
- $serverRequested = Piwik_Common::getRequestVar('server', '');
- $view = Piwik_ViewDataTable::factory('sparkline');
- $view->init( $this->pluginName, __FUNCTION__, 'ExampleUI.getTemperaturesEvolution' );
- $view->setColumnsToDisplay($serverRequested);
- $this->renderView($view);
- }
-
- function misc()
- {
- echo "<h2>Evolution graph filtered to Google and Yahoo!</h2>";
- $this->echoDataTableSearchEnginesFiltered();
- }
-
- function echoDataTableSearchEnginesFiltered()
- {
- $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Referers.getSearchEngines');
- $view->setColumnsToDisplay( 'nb_visits' );
- $view->setSearchPattern('^(Google|Yahoo!)$', 'label');
- return $this->renderView($view);
- }
+ $this->echoAdvancedTagClouds();
+ }
+
+ function echoSimpleTagClouds()
+ {
+ $view = Piwik_ViewDataTable::factory('cloud');
+ $view->init($this->pluginName, __FUNCTION__, 'ExampleUI.getPlanetRatios');
+ $view->setColumnsToDisplay(array('label', 'value'));
+ $view->setColumnTranslation('value', "times the diameter of Earth");
+ $view->disableFooter();
+ $this->renderView($view);
+ }
+
+ function echoAdvancedTagClouds()
+ {
+ $view = Piwik_ViewDataTable::factory('cloud');
+ $view->init($this->pluginName, __FUNCTION__, 'ExampleUI.getPlanetRatiosWithLogos');
+ $view->setDisplayLogoInTagCloud(true);
+ $view->disableFooterExceptExportIcons();
+ $view->setColumnsToDisplay(array('label', 'value'));
+ $view->setColumnTranslation('value', "times the diameter of Earth");
+ $this->renderView($view);
+ }
+
+ function sparklines()
+ {
+ require_once PIWIK_INCLUDE_PATH . '/core/SmartyPlugins/function.sparkline.php';
+ $srcSparkline1 = Piwik_Url::getCurrentQueryStringWithParametersModified(array('action' => 'generateSparkline', 'server' => 'server1', 'rand' => mt_rand()));
+ $htmlSparkline1 = smarty_function_sparkline(array('src' => $srcSparkline1));
+ echo "<div class='sparkline'>$htmlSparkline1 Evolution of temperature for server piwik.org</div>";
+
+ $srcSparkline2 = Piwik_Url::getCurrentQueryStringWithParametersModified(array('action' => 'generateSparkline', 'server' => 'server2', 'rand' => mt_rand()));
+ $htmlSparkline2 = smarty_function_sparkline(array('src' => $srcSparkline2));
+ echo "<div class='sparkline'>$htmlSparkline2 Evolution of temperature for server dev.piwik.org</div>";
+ }
+
+ function generateSparkline()
+ {
+ $serverRequested = Piwik_Common::getRequestVar('server', '');
+ $view = Piwik_ViewDataTable::factory('sparkline');
+ $view->init($this->pluginName, __FUNCTION__, 'ExampleUI.getTemperaturesEvolution');
+ $view->setColumnsToDisplay($serverRequested);
+ $this->renderView($view);
+ }
+
+ function misc()
+ {
+ echo "<h2>Evolution graph filtered to Google and Yahoo!</h2>";
+ $this->echoDataTableSearchEnginesFiltered();
+ }
+
+ function echoDataTableSearchEnginesFiltered()
+ {
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Referers.getSearchEngines');
+ $view->setColumnsToDisplay('nb_visits');
+ $view->setSearchPattern('^(Google|Yahoo!)$', 'label');
+ return $this->renderView($view);
+ }
}
diff --git a/plugins/ExampleUI/ExampleUI.php b/plugins/ExampleUI/ExampleUI.php
index 0937269bb3..11be32f4bd 100644
--- a/plugins/ExampleUI/ExampleUI.php
+++ b/plugins/ExampleUI/ExampleUI.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_ExampleUI
*/
@@ -25,48 +25,47 @@
*/
class Piwik_ExampleUI extends Piwik_Plugin
{
- /**
- * Return information about this plugin.
- *
- * @see Piwik_Plugin
- *
- * @return array
- */
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('ExampleUI_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => '0.1',
- );
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'Menu.add' => 'addMenus',
- );
- return $hooks;
- }
+ /**
+ * Return information about this plugin.
+ *
+ * @see Piwik_Plugin
+ *
+ * @return array
+ */
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('ExampleUI_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => '0.1',
+ );
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'Menu.add' => 'addMenus',
+ );
+ return $hooks;
+ }
- function addMenus()
- {
- $menus = array(
- 'Data tables' => 'dataTables',
- 'Evolution graph' => 'evolutionGraph',
- 'Bar graph' => 'barGraph',
- 'Pie graph' => 'pieGraph',
- 'Tag clouds' => 'tagClouds',
- 'Sparklines' => 'sparklines',
- 'Misc' => 'misc',
- );
+ function addMenus()
+ {
+ $menus = array(
+ 'Data tables' => 'dataTables',
+ 'Evolution graph' => 'evolutionGraph',
+ 'Bar graph' => 'barGraph',
+ 'Pie graph' => 'pieGraph',
+ 'Tag clouds' => 'tagClouds',
+ 'Sparklines' => 'sparklines',
+ 'Misc' => 'misc',
+ );
- Piwik_AddMenu('UI Framework', '', array('module' => 'ExampleUI', 'action' => 'dataTables'), true, 30);
- $order = 1;
- foreach($menus as $subMenu => $action)
- {
- Piwik_AddMenu('UI Framework', $subMenu, array('module' => 'ExampleUI', 'action' => $action), true, $order++);
- }
- }
+ Piwik_AddMenu('UI Framework', '', array('module' => 'ExampleUI', 'action' => 'dataTables'), true, 30);
+ $order = 1;
+ foreach ($menus as $subMenu => $action) {
+ Piwik_AddMenu('UI Framework', $subMenu, array('module' => 'ExampleUI', 'action' => $action), true, $order++);
+ }
+ }
}
diff --git a/plugins/Feedback/Controller.php b/plugins/Feedback/Controller.php
index 9eae844333..a584caefe2 100644
--- a/plugins/Feedback/Controller.php
+++ b/plugins/Feedback/Controller.php
@@ -15,66 +15,60 @@
*/
class Piwik_Feedback_Controller extends Piwik_Controller
{
- function index()
- {
- $view = Piwik_View::factory('index');
- $view->nonce = Piwik_Nonce::getNonce('Piwik_Feedback.sendFeedback', 3600);
- echo $view->render();
- }
+ function index()
+ {
+ $view = Piwik_View::factory('index');
+ $view->nonce = Piwik_Nonce::getNonce('Piwik_Feedback.sendFeedback', 3600);
+ echo $view->render();
+ }
- /**
- * send email to Piwik team and display nice thanks
- * @throws Exception
- */
- function sendFeedback()
- {
- $email = Piwik_Common::getRequestVar('email', '', 'string');
- $body = Piwik_Common::getRequestVar('body', '', 'string');
- $category = Piwik_Common::getRequestVar('category', '', 'string');
- $nonce = Piwik_Common::getRequestVar('nonce', '', 'string');
+ /**
+ * send email to Piwik team and display nice thanks
+ * @throws Exception
+ */
+ function sendFeedback()
+ {
+ $email = Piwik_Common::getRequestVar('email', '', 'string');
+ $body = Piwik_Common::getRequestVar('body', '', 'string');
+ $category = Piwik_Common::getRequestVar('category', '', 'string');
+ $nonce = Piwik_Common::getRequestVar('nonce', '', 'string');
- $view = Piwik_View::factory('sent');
- $view->feedbackEmailAddress = Piwik_Config::getInstance()->General['feedback_email_address'];
- try
- {
- $minimumBodyLength = 40;
- if(strlen($body) < $minimumBodyLength
- // Avoid those really annoying automated security test emails
- || strpos($email, 'probe@') !== false
- || strpos($body, '&lt;probe') !== false)
- {
- throw new Exception(Piwik_TranslateException('Feedback_ExceptionBodyLength', array($minimumBodyLength)));
- }
- if(!Piwik::isValidEmailString($email))
- {
- throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidEmail'));
- }
- if(preg_match('/https?:/i', $body))
- {
- throw new Exception(Piwik_TranslateException('Feedback_ExceptionNoUrls'));
- }
- if(!Piwik_Nonce::verifyNonce('Piwik_Feedback.sendFeedback', $nonce))
- {
- throw new Exception(Piwik_TranslateException('General_ExceptionNonceMismatch'));
- }
- Piwik_Nonce::discardNonce('Piwik_Feedback.sendFeedback');
+ $view = Piwik_View::factory('sent');
+ $view->feedbackEmailAddress = Piwik_Config::getInstance()->General['feedback_email_address'];
+ try {
+ $minimumBodyLength = 40;
+ if (strlen($body) < $minimumBodyLength
+ // Avoid those really annoying automated security test emails
+ || strpos($email, 'probe@') !== false
+ || strpos($body, '&lt;probe') !== false
+ ) {
+ throw new Exception(Piwik_TranslateException('Feedback_ExceptionBodyLength', array($minimumBodyLength)));
+ }
+ if (!Piwik::isValidEmailString($email)) {
+ throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidEmail'));
+ }
+ if (preg_match('/https?:/i', $body)) {
+ throw new Exception(Piwik_TranslateException('Feedback_ExceptionNoUrls'));
+ }
+ if (!Piwik_Nonce::verifyNonce('Piwik_Feedback.sendFeedback', $nonce)) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionNonceMismatch'));
+ }
+ Piwik_Nonce::discardNonce('Piwik_Feedback.sendFeedback');
- $mail = new Piwik_Mail();
- $mail->setFrom(Piwik_Common::unsanitizeInputValue($email));
- $mail->addTo($view->feedbackEmailAddress, 'Piwik Team');
- $mail->setSubject('[ Feedback form - Piwik ] ' . $category);
- $mail->setBodyText(Piwik_Common::unsanitizeInputValue($body) . "\n"
- . 'Piwik ' . Piwik_Version::VERSION . "\n"
- . 'IP: ' . Piwik_IP::getIpFromHeader() . "\n"
- . 'URL: ' . Piwik_Url::getReferer() . "\n");
- @$mail->send();
- }
- catch(Exception $e)
- {
- $view->ErrorString = $e->getMessage();
- $view->message = $body;
- }
+ $mail = new Piwik_Mail();
+ $mail->setFrom(Piwik_Common::unsanitizeInputValue($email));
+ $mail->addTo($view->feedbackEmailAddress, 'Piwik Team');
+ $mail->setSubject('[ Feedback form - Piwik ] ' . $category);
+ $mail->setBodyText(Piwik_Common::unsanitizeInputValue($body) . "\n"
+ . 'Piwik ' . Piwik_Version::VERSION . "\n"
+ . 'IP: ' . Piwik_IP::getIpFromHeader() . "\n"
+ . 'URL: ' . Piwik_Url::getReferer() . "\n");
+ @$mail->send();
+ } catch (Exception $e) {
+ $view->ErrorString = $e->getMessage();
+ $view->message = $body;
+ }
- echo $view->render();
- }
+ echo $view->render();
+ }
}
diff --git a/plugins/Feedback/Feedback.php b/plugins/Feedback/Feedback.php
index 1e0098b6f1..d0094578ed 100644
--- a/plugins/Feedback/Feedback.php
+++ b/plugins/Feedback/Feedback.php
@@ -15,55 +15,55 @@
*/
class Piwik_Feedback extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('Feedback_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('Feedback_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
- function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'TopMenu.add' => 'addTopMenu',
- );
- }
+ function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'TopMenu.add' => 'addTopMenu',
+ );
+ }
- public function addTopMenu()
- {
- Piwik_AddTopMenu(
- 'General_GiveUsYourFeedback',
- array('module' => 'Feedback', 'action' => 'index'),
- true,
- $order = 20,
- $isHTML = false,
- $tooltip = Piwik_Translate('Feedback_TopLinkTooltip')
- );
- }
+ public function addTopMenu()
+ {
+ Piwik_AddTopMenu(
+ 'General_GiveUsYourFeedback',
+ array('module' => 'Feedback', 'action' => 'index'),
+ true,
+ $order = 20,
+ $isHTML = false,
+ $tooltip = Piwik_Translate('Feedback_TopLinkTooltip')
+ );
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "plugins/Feedback/templates/styles.css";
- }
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+
+ $cssFiles[] = "plugins/Feedback/templates/styles.css";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+
+ $jsFiles[] = "plugins/Feedback/templates/feedback.js";
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
-
- $jsFiles[] = "plugins/Feedback/templates/feedback.js";
- }
-
}
diff --git a/plugins/Feedback/templates/feedback.js b/plugins/Feedback/templates/feedback.js
index 5fcd04d58a..cbf07b4863 100644
--- a/plugins/Feedback/templates/feedback.js
+++ b/plugins/Feedback/templates/feedback.js
@@ -5,35 +5,35 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-$(function() {
- var feedback = $('a#topmenu-feedback');
- if (feedback.size()) {
- var fbDiv = $('<div id="feedback-dialog"></div>').appendTo('body');
+$(function () {
+ var feedback = $('a#topmenu-feedback');
+ if (feedback.size()) {
+ var fbDiv = $('<div id="feedback-dialog"></div>').appendTo('body');
- $('a#topmenu-feedback').click(function() {
- if(fbDiv.html() == '') {
- fbDiv.html('<div id="feedback-loading"><img alt="" src="themes/default/images/loading-blue.gif"> '+ _pk_translate('General_Loading_js') +'</div>');
- }
- if($('#feedback-loading' ,fbDiv).length) {
- $.get(feedback.attr('href'), function(data) {
- fbDiv.html(data);
- });
+ $('a#topmenu-feedback').click(function () {
+ if (fbDiv.html() == '') {
+ fbDiv.html('<div id="feedback-loading"><img alt="" src="themes/default/images/loading-blue.gif"> ' + _pk_translate('General_Loading_js') + '</div>');
+ }
+ if ($('#feedback-loading', fbDiv).length) {
+ $.get(feedback.attr('href'), function (data) {
+ fbDiv.html(data);
+ });
+
+ fbDiv.dialog({
+ title: feedback.html(),
+ modal: true,
+ width: 650,
+ position: ['center', 40],
+ resizable: false,
+ autoOpen: false
+ });
+ }
+ $('#feedback-faq').show();
+ $('#feedback-form').hide();
+ $('#feedback-sent').hide().empty();
+ fbDiv.dialog('open');
+ return false;
+ });
+ }
- fbDiv.dialog({
- title: feedback.html(),
- modal: true,
- width: 650,
- position: ['center', 40],
- resizable: false,
- autoOpen: false
- });
- }
- $('#feedback-faq').show();
- $('#feedback-form').hide();
- $('#feedback-sent').hide().empty();
- fbDiv.dialog('open');
- return false;
- });
- }
-
});
diff --git a/plugins/Feedback/templates/index.tpl b/plugins/Feedback/templates/index.tpl
index 0794ad1e2c..adff78c214 100644
--- a/plugins/Feedback/templates/index.tpl
+++ b/plugins/Feedback/templates/index.tpl
@@ -1,67 +1,74 @@
{literal}
-<script type="text/javascript">
-$(function() {
- $('#feedback-contact').click(function() {
- $('#feedback-faq').hide();
- $('#feedback-form').show();
- return false;
- });
+ <script type="text/javascript">
+ $(function () {
+ $('#feedback-contact').click(function () {
+ $('#feedback-faq').hide();
+ $('#feedback-form').show();
+ return false;
+ });
- $('#feedback-home').click(function() {
- $('#feedback-form').hide();
- $('#feedback-faq').show();
- return false;
- });
+ $('#feedback-home').click(function () {
+ $('#feedback-form').hide();
+ $('#feedback-faq').show();
+ return false;
+ });
- $('#feedback-form-submit').click(function() {
- var feedback = $('#feedback-form form');
- $('#feedback-form').hide();
- $.post(feedback.attr('action'), feedback.serialize(), function (data) {
- $('#feedback-sent').show().html(data);
- });
- return false;
- });
-});
-</script>
+ $('#feedback-form-submit').click(function () {
+ var feedback = $('#feedback-form form');
+ $('#feedback-form').hide();
+ $.post(feedback.attr('action'), feedback.serialize(), function (data) {
+ $('#feedback-sent').show().html(data);
+ });
+ return false;
+ });
+ });
+ </script>
{/literal}
- <div id="feedback-faq">
+<div id="feedback-faq">
<p><strong>{'Feedback_DoYouHaveBugReportOrFeatureRequest'|translate}</strong></p>
+
<p> &bull; {'Feedback_ViewAnswersToFAQ'|translate:"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/faq/'>":"</a>"}.</p>
<ul>
- <li>» {'Feedback_WhyAreMyVisitsNoTracked'|translate}</li>
- <li>» {'Feedback_HowToExclude'|translate}</li>
- <li>» {'Feedback_WhyWrongCountry'|translate}</li>
- <li>» {'Feedback_HowToAnonymizeIP'|translate}</li>
+ <li>» {'Feedback_WhyAreMyVisitsNoTracked'|translate}</li>
+ <li>» {'Feedback_HowToExclude'|translate}</li>
+ <li>» {'Feedback_WhyWrongCountry'|translate}</li>
+ <li>» {'Feedback_HowToAnonymizeIP'|translate}</li>
</ul>
<p> &bull; {'Feedback_VisitTheForums'|translate:"<a target='_blank' href='?module=Proxy&action=redirect&url=http://forum.piwik.org/'>":"</a>"}.</p>
- <p> &bull; {'Feedback_LearnWaysToParticipate'|translate:"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/contribute/'>":"</a>"}.</p>
- <br />
+
+ <p> &bull; {'Feedback_LearnWaysToParticipate'|translate:"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/contribute/'>":"</a>"}
+ .</p>
+ <br/>
+
<p><strong>{'Feedback_SpecialRequest'|translate}</strong></p>
- <p> &bull; <a target='_blank' href="#" id="feedback-contact">{'Feedback_ContactThePiwikTeam'|translate}</a></p>
+
+ <p> &bull; <a target='_blank' href="#" id="feedback-contact">{'Feedback_ContactThePiwikTeam'|translate}</a></p>
<br/>
+
<p><strong>{'Feedback_WantToThankConsiderDonating'|translate}</strong></p>
<br/>
{include file="CoreHome/templates/donate.tpl" msg=""}
- </div>
- <div id="feedback-form" style="display:none;">
+</div>
+<div id="feedback-form" style="display:none;">
<form method="post" action="index.php?module=Feedback&action=sendFeedback">
- <label>{'Feedback_IWantTo'|translate}</label>
+ <label>{'Feedback_IWantTo'|translate}</label>
<select name="category">
- <option value="share">{'Feedback_CategoryShareStory'|translate}</option>
- <option value="sponsor">{'Feedback_CategorySponsor'|translate}</option>
- <option value="hire">{'Feedback_CategoryHire'|translate}</option>
- <option value="security">{'Feedback_CategorySecurity'|translate}</option>
+ <option value="share">{'Feedback_CategoryShareStory'|translate}</option>
+ <option value="sponsor">{'Feedback_CategorySponsor'|translate}</option>
+ <option value="hire">{'Feedback_CategoryHire'|translate}</option>
+ <option value="security">{'Feedback_CategorySecurity'|translate}</option>
</select>
- <br />
- <label>{'Feedback_MyEmailAddress'|translate}</label>
- <input type="text" name="email" size="59" />
- <input type="hidden" name="nonce" value="{$nonce}" /><br />
- <label>{'Feedback_MyMessage'|translate}<br /><i>{'Feedback_DetailsPlease'|translate}</i></label>
- <textarea name="body" cols="57" rows="10">Please write your message in English</textarea><br />
- <label><a href="#" id="feedback-home"><img src="plugins/Feedback/images/go-previous.png" border="0" title="{'General_Previous'|translate}" alt="[{'General_Previous'|translate}]" /></a></label>
- <input id="feedback-form-submit" type="submit" class='submit' value="{'Feedback_SendFeedback'|translate}" />
+ <br/>
+ <label>{'Feedback_MyEmailAddress'|translate}</label>
+ <input type="text" name="email" size="59"/>
+ <input type="hidden" name="nonce" value="{$nonce}"/><br/>
+ <label>{'Feedback_MyMessage'|translate}<br/><i>{'Feedback_DetailsPlease'|translate}</i></label>
+ <textarea name="body" cols="57" rows="10">Please write your message in English</textarea><br/>
+ <label><a href="#" id="feedback-home"><img src="plugins/Feedback/images/go-previous.png" border="0" title="{'General_Previous'|translate}"
+ alt="[{'General_Previous'|translate}]"/></a></label>
+ <input id="feedback-form-submit" type="submit" class='submit' value="{'Feedback_SendFeedback'|translate}"/>
</form>
- </div>
- <div id="feedback-sent" style="display:none;">
- </div>
+</div>
+<div id="feedback-sent" style="display:none;">
+</div>
diff --git a/plugins/Feedback/templates/sent.tpl b/plugins/Feedback/templates/sent.tpl
index 5b4780ce5d..74e2604878 100644
--- a/plugins/Feedback/templates/sent.tpl
+++ b/plugins/Feedback/templates/sent.tpl
@@ -1,20 +1,23 @@
{literal}
-<script type="text/javascript">
- $('#feedback-retry').click(function() {
- $('#feedback-sent').hide().empty();
- $('#feedback-form').show();
- return false;
- });
-</script>
+ <script type="text/javascript">
+ $('#feedback-retry').click(function () {
+ $('#feedback-sent').hide().empty();
+ $('#feedback-form').show();
+ return false;
+ });
+ </script>
{/literal}
{if isset($ErrorString)}
- <div id="feedback-error"><strong>{'General_Error'|translate}:</strong> {$ErrorString}</div>
- <p>{'Feedback_ManuallySendEmailTo'|translate} <a href='mailto:{$feedbackEmailAddress}?subject={'[Feedback form - Piwik]'|escape:"hex"}&body={$message|stripeol|escape:"hex"}'>{$feedbackEmailAddress}</a></p>
- <textarea cols="53" rows="10" readonly="readonly">{$message}</textarea>
- <p><a href="#" id="feedback-retry"><img src="plugins/Feedback/images/go-previous.png" border="0" title="{'General_Previous'|translate}" alt="[{'General_Previous'|translate}]" /></a></p>
+ <div id="feedback-error"><strong>{'General_Error'|translate}:</strong> {$ErrorString}</div>
+ <p>{'Feedback_ManuallySendEmailTo'|translate} <a
+ href='mailto:{$feedbackEmailAddress}?subject={'[Feedback form - Piwik]'|escape:"hex"}&body={$message|stripeol|escape:"hex"}'>{$feedbackEmailAddress}</a>
+ </p>
+ <textarea cols="53" rows="10" readonly="readonly">{$message}</textarea>
+ <p><a href="#" id="feedback-retry"><img src="plugins/Feedback/images/go-previous.png" border="0" title="{'General_Previous'|translate}"
+ alt="[{'General_Previous'|translate}]"/></a></p>
{else}
- <div id="feedback-success">{'Feedback_MessageSent'|translate}</div>
- <p><strong>{'Feedback_ThankYou'|translate}</strong></p>
- <p>-- {'Feedback_ThePiwikTeam'|translate}</p>
+ <div id="feedback-success">{'Feedback_MessageSent'|translate}</div>
+ <p><strong>{'Feedback_ThankYou'|translate}</strong></p>
+ <p>-- {'Feedback_ThePiwikTeam'|translate}</p>
{/if}
diff --git a/plugins/Feedback/templates/styles.css b/plugins/Feedback/templates/styles.css
index abc187741b..258f078ca1 100644
--- a/plugins/Feedback/templates/styles.css
+++ b/plugins/Feedback/templates/styles.css
@@ -1,47 +1,68 @@
#feedback-faq, #feedback-form, #feedback-sent {
- font-family: Arial, Helvetica, sans-serif;
- color:#5e5e5c;
- font-size:14px;
- line-height:24px;
- padding:0 20px 15px 0;
+ font-family: Arial, Helvetica, sans-serif;
+ color: #5e5e5c;
+ font-size: 14px;
+ line-height: 24px;
+ padding: 0 20px 15px 0;
}
+
#feedback-faq strong, #feedback-sent strong {
- color:#5e5e5c;
+ color: #5e5e5c;
}
#feedback-faq ul, #feedback-sent ul {
- list-style:none;
- padding:0 0 0 20px;
- font-size:12px;
- line-height:18px;
+ list-style: none;
+ padding: 0 0 0 20px;
+ font-size: 12px;
+ line-height: 18px;
}
-#feedback-faq a, #feedback-sent a{
- color:#5176a0;
- text-decoration:none;
- font-weight:bold;
+#feedback-faq a, #feedback-sent a {
+ color: #5176a0;
+ text-decoration: none;
+ font-weight: bold;
}
-
#feedback-error {
- color: red;
- text-align: center;
- border: 2px solid red;
- background-color:#FFFBFB;
- margin: 10px;
- padding: 10px;
+ color: red;
+ text-align: center;
+ border: 2px solid red;
+ background-color: #FFFBFB;
+ margin: 10px;
+ padding: 10px;
}
+
#feedback-success {
- color: #38D73B;
- text-align: center;
- border: 2px solid #38D73B;
- margin: 10px;
- padding: 10px;
+ color: #38D73B;
+ text-align: center;
+ border: 2px solid #38D73B;
+ margin: 10px;
+ padding: 10px;
+}
+
+#feedback-form {
+ font-size: 12px;
+ line-height: 18px;
}
-#feedback-form { font-size:12px; line-height:18px;}
-#feedback-form label{ float:left; width:135px; padding:4px 15px 0 0; text-align:right; position:relative; }
-#feedback-form label i{ font-style:normal; color:#cbcbcb; }
-#feedback-form br{ clear:both; }
+#feedback-form label {
+ float: left;
+ width: 135px;
+ padding: 4px 15px 0 0;
+ text-align: right;
+ position: relative;
+}
+
+#feedback-form label i {
+ font-style: normal;
+ color: #cbcbcb;
+}
+
+#feedback-form br {
+ clear: both;
+}
-#feedback-home{ float:left; margin:-7px 0 0 20px; } \ No newline at end of file
+#feedback-home {
+ float: left;
+ margin: -7px 0 0 20px;
+} \ No newline at end of file
diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php
index 318705d1ab..5feba1b47e 100644
--- a/plugins/Goals/API.php
+++ b/plugins/Goals/API.php
@@ -1,575 +1,540 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Goals
*/
/**
- * Goals API lets you Manage existing goals, via "updateGoal" and "deleteGoal", create new Goals via "addGoal",
- * or list existing Goals for one or several websites via "getGoals"
- *
+ * Goals API lets you Manage existing goals, via "updateGoal" and "deleteGoal", create new Goals via "addGoal",
+ * or list existing Goals for one or several websites via "getGoals"
+ *
* If you are <a href='http://piwik.org/docs/ecommerce-analytics/' target='_blank'>tracking Ecommerce orders and products</a> on your site, the functions "getItemsSku", "getItemsName" and "getItemsCategory"
* will return the list of products purchased on your site, either grouped by Product SKU, Product Name or Product Category. For each name, SKU or category, the following
* metrics are returned: Total revenue, Total quantity, average price, average quantity, number of orders (or abandoned carts) containing this product, number of visits on the Product page,
* Conversion rate.
- *
+ *
* By default, these functions return the 'Products purchased'. These functions also accept an optional parameter &abandonedCarts=1.
- * If the parameter is set, it will instead return the metrics for products that were left in an abandoned cart therefore not purchased.
- *
+ * If the parameter is set, it will instead return the metrics for products that were left in an abandoned cart therefore not purchased.
+ *
* The API also lets you request overall Goal metrics via the method "get": Conversions, Visits with at least one conversion, Conversion rate and Revenue.
- * If you wish to request specific metrics about Ecommerce goals, you can set the parameter &idGoal=ecommerceAbandonedCart to get metrics about abandoned carts (including Lost revenue, and number of items left in the cart)
+ * If you wish to request specific metrics about Ecommerce goals, you can set the parameter &idGoal=ecommerceAbandonedCart to get metrics about abandoned carts (including Lost revenue, and number of items left in the cart)
* or &idGoal=ecommerceOrder to get metrics about Ecommerce orders (number of orders, visits with an order, subtotal, tax, shipping, discount, revenue, items ordered)
- *
+ *
* See also the documentation about <a href='http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>Tracking Goals</a> in Piwik.
- *
+ *
* @package Piwik_Goals
*/
-class Piwik_Goals_API
+class Piwik_Goals_API
{
- static private $instance = null;
- /**
- * @return Piwik_Goals_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Returns all Goals for a given website, or list of websites
- *
- * @param string|array $idSite Array or Comma separated list of website IDs to request the goals for
- * @return array Array of Goal attributes
- */
- public function getGoals( $idSite )
- {
- //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 = Piwik_Site::getIdSitesFromIdSitesString($idSite);
- if(empty($idSite))
- {
- return array();
- }
- Piwik::checkUserHasViewAccess($idSite);
- $goals = Piwik_FetchAll("SELECT *
- FROM ".Piwik_Common::prefixTable('goal')."
- WHERE idsite IN (".implode(", ", $idSite).")
+ static private $instance = null;
+
+ /**
+ * @return Piwik_Goals_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Returns all Goals for a given website, or list of websites
+ *
+ * @param string|array $idSite Array or Comma separated list of website IDs to request the goals for
+ * @return array Array of Goal attributes
+ */
+ public function getGoals($idSite)
+ {
+ //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 = Piwik_Site::getIdSitesFromIdSitesString($idSite);
+ if (empty($idSite)) {
+ return array();
+ }
+ Piwik::checkUserHasViewAccess($idSite);
+ $goals = Piwik_FetchAll("SELECT *
+ FROM " . Piwik_Common::prefixTable('goal') . "
+ WHERE idsite IN (" . implode(", ", $idSite) . ")
AND deleted = 0");
- $cleanedGoals = array();
- foreach($goals as &$goal)
- {
- if($goal['match_attribute'] == 'manually') {
- unset($goal['pattern']);
- unset($goal['pattern_type']);
- unset($goal['case_sensitive']);
- }
- $cleanedGoals[$goal['idgoal']] = $goal;
- }
- return $cleanedGoals;
- }
-
- /**
- * Creates a Goal for a given website.
- *
- * @param int $idSite
- * @param string $name
- * @param string $matchAttribute 'url', 'title', 'file', 'external_website' or 'manually'
- * @param string $pattern eg. purchase-confirmation.htm
- * @param string $patternType 'regex', 'contains', 'exact'
- * @param bool $caseSensitive
- * @param bool|float $revenue If set, default revenue to assign to conversions
- * @param bool $allowMultipleConversionsPerVisit By default, multiple conversions in the same visit will only record the first conversion.
- * If set to true, multiple conversions will all be recorded within a visit (useful for Ecommerce goals)
- * @return int ID of the new goal
- */
- public function addGoal( $idSite, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false)
- {
- Piwik::checkUserHasAdminAccess($idSite);
- $this->checkPatternIsValid($patternType, $pattern);
- $name = $this->checkName($name);
- $pattern = $this->checkPattern($pattern);
-
- // save in db
- $db = Zend_Registry::get('db');
- $idGoal = $db->fetchOne("SELECT max(idgoal) + 1
- FROM ".Piwik_Common::prefixTable('goal')."
+ $cleanedGoals = array();
+ foreach ($goals as &$goal) {
+ if ($goal['match_attribute'] == 'manually') {
+ unset($goal['pattern']);
+ unset($goal['pattern_type']);
+ unset($goal['case_sensitive']);
+ }
+ $cleanedGoals[$goal['idgoal']] = $goal;
+ }
+ return $cleanedGoals;
+ }
+
+ /**
+ * Creates a Goal for a given website.
+ *
+ * @param int $idSite
+ * @param string $name
+ * @param string $matchAttribute 'url', 'title', 'file', 'external_website' or 'manually'
+ * @param string $pattern eg. purchase-confirmation.htm
+ * @param string $patternType 'regex', 'contains', 'exact'
+ * @param bool $caseSensitive
+ * @param bool|float $revenue If set, default revenue to assign to conversions
+ * @param bool $allowMultipleConversionsPerVisit By default, multiple conversions in the same visit will only record the first conversion.
+ * If set to true, multiple conversions will all be recorded within a visit (useful for Ecommerce goals)
+ * @return int ID of the new goal
+ */
+ public function addGoal($idSite, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false)
+ {
+ Piwik::checkUserHasAdminAccess($idSite);
+ $this->checkPatternIsValid($patternType, $pattern);
+ $name = $this->checkName($name);
+ $pattern = $this->checkPattern($pattern);
+
+ // save in db
+ $db = Zend_Registry::get('db');
+ $idGoal = $db->fetchOne("SELECT max(idgoal) + 1
+ FROM " . Piwik_Common::prefixTable('goal') . "
WHERE idsite = ?", $idSite);
- if($idGoal == false)
- {
- $idGoal = 1;
- }
- $db->insert(Piwik_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,
- ));
- Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($idSite);
- return $idGoal;
- }
-
- /**
- * Updates a Goal description.
- * Will not update or re-process the conversions already recorded
- *
- * @see addGoal() for parameters description
- * @param int $idSite
- * @param int $idGoal
- * @param $name
- * @param $matchAttribute
- * @param string $pattern
- * @param string $patternType
- * @param bool $caseSensitive
- * @param bool|float $revenue
- * @param bool $allowMultipleConversionsPerVisit
- * @return void
- */
- public function updateGoal( $idSite, $idGoal, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false)
- {
- Piwik::checkUserHasAdminAccess($idSite);
- $name = $this->checkName($name);
- $pattern = $this->checkPattern($pattern);
- $this->checkPatternIsValid($patternType, $pattern);
- Zend_Registry::get('db')->update( Piwik_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'"
- );
- Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($idSite);
- }
-
- private function checkPatternIsValid($patternType, $pattern)
- {
- if($patternType == 'exact'
- && substr($pattern, 0, 4) != 'http')
- {
- throw new Exception(Piwik_TranslateException('Goals_ExceptionInvalidMatchingString', array("http:// or https://", "http://www.yourwebsite.com/newsletter/subscribed.html")));
- }
- }
-
- private function checkName($name)
- {
- return urldecode($name);
- }
-
- private function checkPattern($pattern)
- {
- return urldecode($pattern);
- }
-
- /**
- * Soft deletes a given Goal.
- * Stats data in the archives will still be recorded, but not displayed.
- *
- * @param int $idSite
- * @param int $idGoal
- * @return void
- */
- public function deleteGoal( $idSite, $idGoal )
- {
- Piwik::checkUserHasAdminAccess($idSite);
- Piwik_Query("UPDATE ".Piwik_Common::prefixTable('goal')."
+ if ($idGoal == false) {
+ $idGoal = 1;
+ }
+ $db->insert(Piwik_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,
+ ));
+ Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($idSite);
+ return $idGoal;
+ }
+
+ /**
+ * Updates a Goal description.
+ * Will not update or re-process the conversions already recorded
+ *
+ * @see addGoal() for parameters description
+ * @param int $idSite
+ * @param int $idGoal
+ * @param $name
+ * @param $matchAttribute
+ * @param string $pattern
+ * @param string $patternType
+ * @param bool $caseSensitive
+ * @param bool|float $revenue
+ * @param bool $allowMultipleConversionsPerVisit
+ * @return void
+ */
+ public function updateGoal($idSite, $idGoal, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false)
+ {
+ Piwik::checkUserHasAdminAccess($idSite);
+ $name = $this->checkName($name);
+ $pattern = $this->checkPattern($pattern);
+ $this->checkPatternIsValid($patternType, $pattern);
+ Zend_Registry::get('db')->update(Piwik_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'"
+ );
+ Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($idSite);
+ }
+
+ private function checkPatternIsValid($patternType, $pattern)
+ {
+ if ($patternType == 'exact'
+ && substr($pattern, 0, 4) != 'http'
+ ) {
+ throw new Exception(Piwik_TranslateException('Goals_ExceptionInvalidMatchingString', array("http:// or https://", "http://www.yourwebsite.com/newsletter/subscribed.html")));
+ }
+ }
+
+ private function checkName($name)
+ {
+ return urldecode($name);
+ }
+
+ private function checkPattern($pattern)
+ {
+ return urldecode($pattern);
+ }
+
+ /**
+ * Soft deletes a given Goal.
+ * Stats data in the archives will still be recorded, but not displayed.
+ *
+ * @param int $idSite
+ * @param int $idGoal
+ * @return void
+ */
+ public function deleteGoal($idSite, $idGoal)
+ {
+ Piwik::checkUserHasAdminAccess($idSite);
+ Piwik_Query("UPDATE " . Piwik_Common::prefixTable('goal') . "
SET deleted = 1
WHERE idsite = ?
AND idgoal = ?",
- array($idSite, $idGoal));
- Piwik_DeleteAllRows(Piwik_Common::prefixTable("log_conversion"), "WHERE idgoal = ?", 100000, array($idGoal));
- Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($idSite);
- }
-
- /**
- * Returns a datatable of Items SKU/name or categories and their metrics
- * If $abandonedCarts set to 1, will return items abandoned in carts. If set to 0, will return items ordered
- */
- protected function getItems($recordName, $idSite, $period, $date, $abandonedCarts )
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $recordNameFinal = $recordName;
- if($abandonedCarts)
- {
- $recordNameFinal = Piwik_Goals::getItemRecordNameAbandonedCart($recordName);
- }
- $archive = Piwik_Archive::build($idSite, $period, $date );
- $dataTable = $archive->getDataTable($recordNameFinal);
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE));
- $dataTable->queueFilter('ReplaceColumnNames');
-
- $ordersColumn = 'orders';
- if($abandonedCarts)
- {
- $ordersColumn = 'abandoned_carts';
- $dataTable->renameColumn(Piwik_Archive::INDEX_ECOMMERCE_ORDERS, $ordersColumn);
- }
-
- // Average price = sum product revenue / quantity
- $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_price', 'price', $ordersColumn, Piwik_Tracker_GoalManager::REVENUE_PRECISION));
-
- // Average quantity = sum product quantity / abandoned carts
- $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_quantity', 'quantity', $ordersColumn, $precision = 1));
- $dataTable->queueFilter('ColumnDelete', array('price'));
-
- // Enrich the datatable with Product/Categories views, and conversion rates
- $customVariables = Piwik_CustomVariables_API::getInstance()->getCustomVariables($idSite, $period, $date, $segment = false, $expanded = false, $_leavePiwikCoreVariables = true);
- $mapping = array(
- 'Goals_ItemsSku' => '_pks',
- 'Goals_ItemsName' => '_pkn',
- 'Goals_ItemsCategory' => '_pkc',
- );
- $reportToNotDefinedString = array(
- 'Goals_ItemsSku' => Piwik_Translate('General_NotDefined', Piwik_Translate('Goals_ProductSKU')), // Note: this should never happen
- 'Goals_ItemsName' => Piwik_Translate('General_NotDefined', Piwik_Translate('Goals_ProductName')),
- 'Goals_ItemsCategory' => Piwik_Translate('General_NotDefined', Piwik_Translate('Goals_ProductCategory'))
- );
- $notDefinedStringPretty = $reportToNotDefinedString[$recordName];
- $customVarNameToLookFor = $mapping[$recordName];
-
- // Handle case where date=last30&period=day
- if($customVariables instanceof Piwik_DataTable_Array)
- {
- $customVariableDatatables = $customVariables->getArray();
- $dataTables = $dataTable->getArray();
- foreach($customVariableDatatables as $key => $customVariableTableForDate)
- {
- $dataTableForDate = isset($dataTables[$key]) ? $dataTables[$key] : new Piwik_DataTable();
-
- // we do not enter the IF
- // if case idSite=1,3 AND period=day&date=datefrom,dateto,
- if(isset($customVariableTableForDate->metadata['period']))
- {
- $dateRewrite = $customVariableTableForDate->metadata['period']->getDateStart()->toString();
- $row = $customVariableTableForDate->getRowFromLabel($customVarNameToLookFor);
- if($row)
- {
- $idSubtable = $row->getIdSubDataTable();
- $this->enrichItemsDataTableWithItemsViewMetrics($dataTableForDate, $idSite, $period, $dateRewrite, $idSubtable);
- }
- $dataTable->addTable($dataTableForDate, $key);
- }
- $this->renameNotDefinedRow($dataTableForDate, $notDefinedStringPretty);
- }
- }
- elseif($customVariables instanceof Piwik_DataTable)
- {
- $row = $customVariables->getRowFromLabel($customVarNameToLookFor);
- if($row)
- {
- $idSubtable = $row->getIdSubDataTable();
- $this->enrichItemsDataTableWithItemsViewMetrics($dataTable, $idSite, $period, $date, $idSubtable);
- }
- $this->renameNotDefinedRow($dataTable, $notDefinedStringPretty);
- }
-
- // Product conversion rate = orders / visits
- $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('conversion_rate', $ordersColumn, 'nb_visits', Piwik_Tracker_GoalManager::REVENUE_PRECISION));
-
- return $dataTable;
- }
-
- protected function renameNotDefinedRow($dataTable, $notDefinedStringPretty)
- {
- if($dataTable instanceof Piwik_DataTable_Array)
- {
- foreach($dataTable->getArray() as $table)
- {
- $this->renameNotDefinedRow($table, $notDefinedStringPretty);
- }
- return;
- }
- $rowNotDefined = $dataTable->getRowFromLabel(Piwik_CustomVariables::LABEL_CUSTOM_VALUE_NOT_DEFINED);
- if($rowNotDefined)
- {
- $rowNotDefined->setColumn('label', $notDefinedStringPretty);
- }
- }
-
-
- protected function enrichItemsDataTableWithItemsViewMetrics($dataTable, $idSite, $period, $date, $idSubtable)
- {
- $ecommerceViews = Piwik_CustomVariables_API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $date, $idSubtable, $segment = false, $_leavePriceViewedColumn = true);
-
- // For Product names and SKU reports, and for Category report
- // Use the Price (tracked on page views)
- // ONLY when the price sold in conversions is not found (ie. product viewed but not sold)
- foreach($ecommerceViews->getRows() as $rowView)
- {
- // If there is not already a 'sum price' for this product
- $rowFound = $dataTable->getRowFromLabel($rowView->getColumn('label'));
- $price = $rowFound
- ? $rowFound->getColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE)
- : false;
- if(empty($price))
- {
- // If a price was tracked on the product page
- if($rowView->getColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED))
- {
- $rowView->renameColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED, 'avg_price');
- }
- }
- $rowView->deleteColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED);
- }
-
- $dataTable->addDataTable($ecommerceViews);
- }
-
- public function getItemsSku($idSite, $period, $date, $abandonedCarts = false )
- {
- return $this->getItems('Goals_ItemsSku', $idSite, $period, $date, $abandonedCarts);
- }
-
- public function getItemsName($idSite, $period, $date, $abandonedCarts = false )
- {
- return $this->getItems('Goals_ItemsName', $idSite, $period, $date, $abandonedCarts);
- }
-
- public function getItemsCategory($idSite, $period, $date, $abandonedCarts = false )
- {
- return $this->getItems('Goals_ItemsCategory', $idSite, $period, $date, $abandonedCarts);
- }
-
- /**
- * Helper function that checks for special string goal IDs and converts them to
- * their integer equivalents.
- *
- * Checks for the following values:
- * Piwik_Archive::LABEL_ECOMMERCE_ORDER
- * Piwik_Archive::LABEL_ECOMMERCE_CART
- *
- * @param string|int $idGoal The goal id as an integer or a special string.
- * @return int The numeric goal id.
- */
- protected static function convertSpecialGoalIds( $idGoal )
- {
- if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
- {
- return Piwik_Tracker_GoalManager::IDGOAL_ORDER;
- }
- else if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_CART)
- {
- return Piwik_Tracker_GoalManager::IDGOAL_CART;
- }
- else
- {
- return $idGoal;
- }
- }
-
- /**
- * Returns Goals data
- *
- * @param int $idSite
- * @param string $period
- * @param string $date
- * @param bool $segment
- * @param bool|int $idGoal
- * @param array $columns Array of metrics to fetch: nb_conversions, conversion_rate, revenue
- * @return Piwik_DataTable
- */
- public function get( $idSite, $period, $date, $segment = false, $idGoal = false, $columns = array() )
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- $columns = Piwik::getArrayFromApiParameter($columns);
-
- // Mapping string idGoal to internal ID
- $idGoal = self::convertSpecialGoalIds($idGoal);
-
- if(empty($columns))
- {
- $columns = Piwik_Goals::getGoalColumns($idGoal);
- if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
- {
- $columns[] = 'avg_order_revenue';
- }
- }
- if(in_array('avg_order_revenue', $columns)
- && $idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
- {
- $columns[] = 'nb_conversions';
- $columns[] = 'revenue';
- $columns = array_values(array_unique($columns));
- }
- $columnsToSelect = array();
- foreach($columns as &$columnName)
- {
- $columnsToSelect[] = Piwik_Goals::getRecordName($columnName, $idGoal);
- }
- $dataTable = $archive->getDataTableFromNumeric($columnsToSelect);
-
- // Rewrite column names as we expect them
- foreach($columnsToSelect as $id => $oldName)
- {
- $dataTable->renameColumn($oldName, $columns[$id]);
- }
- if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
- {
- if($dataTable instanceof Piwik_DataTable_Array)
- {
- foreach($dataTable->getArray() as $row)
- {
- $this->enrichTable($row);
- }
- }
- else
- {
- $this->enrichTable($dataTable);
- }
- }
- return $dataTable;
- }
-
- protected function enrichTable($table)
- {
- $row = $table->getFirstRow();
- if(!$row)
- {
- return;
- }
- // AVG order per visit
- if(false !== $table->getColumn('avg_order_revenue'))
- {
- $conversions = $row->getColumn('nb_conversions');
- if($conversions)
- {
- $row->setColumn('avg_order_revenue', round($row->getColumn('revenue') / $conversions, 2));
- }
- }
- }
-
- protected function getNumeric( $idSite, $period, $date, $segment, $toFetch )
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- $dataTable = $archive->getNumeric($toFetch);
- return $dataTable;
- }
-
- /**
- * @ignore
- */
- public function getConversions( $idSite, $period, $date, $segment = false, $idGoal = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, Piwik_Goals::getRecordName('nb_conversions', $idGoal));
- }
-
- /**
- * @ignore
- */
- public function getNbVisitsConverted( $idSite, $period, $date, $segment = false, $idGoal = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, Piwik_Goals::getRecordName('nb_visits_converted', $idGoal));
- }
-
- /**
- * @ignore
- */
- public function getConversionRate( $idSite, $period, $date, $segment = false, $idGoal = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, Piwik_Goals::getRecordName('conversion_rate', $idGoal));
- }
-
- /**
- * @ignore
- */
- public function getRevenue( $idSite, $period, $date, $segment = false, $idGoal = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, Piwik_Goals::getRecordName('revenue', $idGoal));
- }
-
- /**
- * Utility method that retrieve an archived DataTable for a specific site, date range,
- * segment and goal. If not goal is specified, this method will retrieve and sum the
- * data for every goal.
- *
- * @param string $recordName The archive entry name.
- * @param int|string $idSite The site(s) to select data for.
- * @param string $period The period type.
- * @param string $date The date type.
- * @param string $segment The segment.
- * @param int|bool $idGoal The id of the goal to get data for. If this is set to false,
- * data for every goal that belongs to $idSite is returned.
- */
- protected function getGoalSpecificDataTable($recordName, $idSite, $period, $date, $segment, $idGoal)
- {
- Piwik::checkUserHasViewAccess( $idSite );
-
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
-
- // check for the special goal ids
- $realGoalId = $idGoal != true ? false : self::convertSpecialGoalIds($idGoal);
-
- // get the data table
- $dataTable = $archive->getDataTable(Piwik_Goals::getRecordName($recordName, $realGoalId), $idSubtable = null);
- $dataTable->queueFilter('ReplaceColumnNames');
-
- return $dataTable;
- }
-
- /**
- * Gets a DataTable that maps ranges of days to the number of conversions that occurred
- * within those ranges, for the specified site, date range, segment and goal.
- *
- * @param int $idSite The site to select data from.
- * @param string $period The period type.
- * @param string $date The date type.
- * @param string|bool $segment The segment.
- * @param int|bool $idGoal The id of the goal to get data for. If this is set to false,
- * data for every goal that belongs to $idSite is returned.
- */
- public function getDaysToConversion($idSite, $period, $date, $segment = false, $idGoal = false)
- {
- $dataTable = $this->getGoalSpecificDataTable(
- Piwik_Goals::DAYS_UNTIL_CONV_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
-
- $dataTable->queueFilter('Sort', array('label', 'asc', true));
- $dataTable->queueFilter(
- 'BeautifyRangeLabels', array(Piwik_Translate('General_OneDay'), Piwik_Translate('General_NDays')));
-
- return $dataTable;
- }
-
- /**
- * Gets a DataTable that maps ranges of visit counts to the number of conversions that
- * occurred on those visits for the specified site, date range, segment and goal.
- *
- * @param int $idSite The site to select data from.
- * @param string $period The period type.
- * @param string $date The date type.
- * @param string|bool $segment The segment.
- * @param int|bool $idGoal The id of the goal to get data for. If this is set to false,
- * data for every goal that belongs to $idSite is returned.
- */
- public function getVisitsUntilConversion($idSite, $period, $date, $segment = false, $idGoal = false)
- {
- $dataTable = $this->getGoalSpecificDataTable(
- Piwik_Goals::VISITS_UNTIL_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
-
- $dataTable->queueFilter('Sort', array('label', 'asc', true));
- $dataTable->queueFilter(
- 'BeautifyRangeLabels', array(Piwik_Translate('General_OneVisit'), Piwik_Translate('General_NVisits')));
-
- return $dataTable;
- }
+ array($idSite, $idGoal));
+ Piwik_DeleteAllRows(Piwik_Common::prefixTable("log_conversion"), "WHERE idgoal = ?", 100000, array($idGoal));
+ Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($idSite);
+ }
+
+ /**
+ * Returns a datatable of Items SKU/name or categories and their metrics
+ * If $abandonedCarts set to 1, will return items abandoned in carts. If set to 0, will return items ordered
+ */
+ protected function getItems($recordName, $idSite, $period, $date, $abandonedCarts)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $recordNameFinal = $recordName;
+ if ($abandonedCarts) {
+ $recordNameFinal = Piwik_Goals::getItemRecordNameAbandonedCart($recordName);
+ }
+ $archive = Piwik_Archive::build($idSite, $period, $date);
+ $dataTable = $archive->getDataTable($recordNameFinal);
+ $dataTable->filter('Sort', array(Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE));
+ $dataTable->queueFilter('ReplaceColumnNames');
+
+ $ordersColumn = 'orders';
+ if ($abandonedCarts) {
+ $ordersColumn = 'abandoned_carts';
+ $dataTable->renameColumn(Piwik_Archive::INDEX_ECOMMERCE_ORDERS, $ordersColumn);
+ }
+
+ // Average price = sum product revenue / quantity
+ $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_price', 'price', $ordersColumn, Piwik_Tracker_GoalManager::REVENUE_PRECISION));
+
+ // Average quantity = sum product quantity / abandoned carts
+ $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_quantity', 'quantity', $ordersColumn, $precision = 1));
+ $dataTable->queueFilter('ColumnDelete', array('price'));
+
+ // Enrich the datatable with Product/Categories views, and conversion rates
+ $customVariables = Piwik_CustomVariables_API::getInstance()->getCustomVariables($idSite, $period, $date, $segment = false, $expanded = false, $_leavePiwikCoreVariables = true);
+ $mapping = array(
+ 'Goals_ItemsSku' => '_pks',
+ 'Goals_ItemsName' => '_pkn',
+ 'Goals_ItemsCategory' => '_pkc',
+ );
+ $reportToNotDefinedString = array(
+ 'Goals_ItemsSku' => Piwik_Translate('General_NotDefined', Piwik_Translate('Goals_ProductSKU')), // Note: this should never happen
+ 'Goals_ItemsName' => Piwik_Translate('General_NotDefined', Piwik_Translate('Goals_ProductName')),
+ 'Goals_ItemsCategory' => Piwik_Translate('General_NotDefined', Piwik_Translate('Goals_ProductCategory'))
+ );
+ $notDefinedStringPretty = $reportToNotDefinedString[$recordName];
+ $customVarNameToLookFor = $mapping[$recordName];
+
+ // Handle case where date=last30&period=day
+ if ($customVariables instanceof Piwik_DataTable_Array) {
+ $customVariableDatatables = $customVariables->getArray();
+ $dataTables = $dataTable->getArray();
+ foreach ($customVariableDatatables as $key => $customVariableTableForDate) {
+ $dataTableForDate = isset($dataTables[$key]) ? $dataTables[$key] : new Piwik_DataTable();
+
+ // we do not enter the IF
+ // if case idSite=1,3 AND period=day&date=datefrom,dateto,
+ if (isset($customVariableTableForDate->metadata['period'])) {
+ $dateRewrite = $customVariableTableForDate->metadata['period']->getDateStart()->toString();
+ $row = $customVariableTableForDate->getRowFromLabel($customVarNameToLookFor);
+ if ($row) {
+ $idSubtable = $row->getIdSubDataTable();
+ $this->enrichItemsDataTableWithItemsViewMetrics($dataTableForDate, $idSite, $period, $dateRewrite, $idSubtable);
+ }
+ $dataTable->addTable($dataTableForDate, $key);
+ }
+ $this->renameNotDefinedRow($dataTableForDate, $notDefinedStringPretty);
+ }
+ } elseif ($customVariables instanceof Piwik_DataTable) {
+ $row = $customVariables->getRowFromLabel($customVarNameToLookFor);
+ if ($row) {
+ $idSubtable = $row->getIdSubDataTable();
+ $this->enrichItemsDataTableWithItemsViewMetrics($dataTable, $idSite, $period, $date, $idSubtable);
+ }
+ $this->renameNotDefinedRow($dataTable, $notDefinedStringPretty);
+ }
+
+ // Product conversion rate = orders / visits
+ $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('conversion_rate', $ordersColumn, 'nb_visits', Piwik_Tracker_GoalManager::REVENUE_PRECISION));
+
+ return $dataTable;
+ }
+
+ protected function renameNotDefinedRow($dataTable, $notDefinedStringPretty)
+ {
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ foreach ($dataTable->getArray() as $table) {
+ $this->renameNotDefinedRow($table, $notDefinedStringPretty);
+ }
+ return;
+ }
+ $rowNotDefined = $dataTable->getRowFromLabel(Piwik_CustomVariables::LABEL_CUSTOM_VALUE_NOT_DEFINED);
+ if ($rowNotDefined) {
+ $rowNotDefined->setColumn('label', $notDefinedStringPretty);
+ }
+ }
+
+
+ protected function enrichItemsDataTableWithItemsViewMetrics($dataTable, $idSite, $period, $date, $idSubtable)
+ {
+ $ecommerceViews = Piwik_CustomVariables_API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $date, $idSubtable, $segment = false, $_leavePriceViewedColumn = true);
+
+ // For Product names and SKU reports, and for Category report
+ // Use the Price (tracked on page views)
+ // ONLY when the price sold in conversions is not found (ie. product viewed but not sold)
+ foreach ($ecommerceViews->getRows() as $rowView) {
+ // If there is not already a 'sum price' for this product
+ $rowFound = $dataTable->getRowFromLabel($rowView->getColumn('label'));
+ $price = $rowFound
+ ? $rowFound->getColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE)
+ : false;
+ if (empty($price)) {
+ // If a price was tracked on the product page
+ if ($rowView->getColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED)) {
+ $rowView->renameColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED, 'avg_price');
+ }
+ }
+ $rowView->deleteColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED);
+ }
+
+ $dataTable->addDataTable($ecommerceViews);
+ }
+
+ public function getItemsSku($idSite, $period, $date, $abandonedCarts = false)
+ {
+ return $this->getItems('Goals_ItemsSku', $idSite, $period, $date, $abandonedCarts);
+ }
+
+ public function getItemsName($idSite, $period, $date, $abandonedCarts = false)
+ {
+ return $this->getItems('Goals_ItemsName', $idSite, $period, $date, $abandonedCarts);
+ }
+
+ public function getItemsCategory($idSite, $period, $date, $abandonedCarts = false)
+ {
+ return $this->getItems('Goals_ItemsCategory', $idSite, $period, $date, $abandonedCarts);
+ }
+
+ /**
+ * Helper function that checks for special string goal IDs and converts them to
+ * their integer equivalents.
+ *
+ * Checks for the following values:
+ * Piwik_Archive::LABEL_ECOMMERCE_ORDER
+ * Piwik_Archive::LABEL_ECOMMERCE_CART
+ *
+ * @param string|int $idGoal The goal id as an integer or a special string.
+ * @return int The numeric goal id.
+ */
+ protected static function convertSpecialGoalIds($idGoal)
+ {
+ if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ return Piwik_Tracker_GoalManager::IDGOAL_ORDER;
+ } else if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_CART) {
+ return Piwik_Tracker_GoalManager::IDGOAL_CART;
+ } else {
+ return $idGoal;
+ }
+ }
+
+ /**
+ * Returns Goals data
+ *
+ * @param int $idSite
+ * @param string $period
+ * @param string $date
+ * @param bool $segment
+ * @param bool|int $idGoal
+ * @param array $columns Array of metrics to fetch: nb_conversions, conversion_rate, revenue
+ * @return Piwik_DataTable
+ */
+ public function get($idSite, $period, $date, $segment = false, $idGoal = false, $columns = array())
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ $columns = Piwik::getArrayFromApiParameter($columns);
+
+ // Mapping string idGoal to internal ID
+ $idGoal = self::convertSpecialGoalIds($idGoal);
+
+ if (empty($columns)) {
+ $columns = Piwik_Goals::getGoalColumns($idGoal);
+ if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ $columns[] = 'avg_order_revenue';
+ }
+ }
+ if (in_array('avg_order_revenue', $columns)
+ && $idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER
+ ) {
+ $columns[] = 'nb_conversions';
+ $columns[] = 'revenue';
+ $columns = array_values(array_unique($columns));
+ }
+ $columnsToSelect = array();
+ foreach ($columns as &$columnName) {
+ $columnsToSelect[] = Piwik_Goals::getRecordName($columnName, $idGoal);
+ }
+ $dataTable = $archive->getDataTableFromNumeric($columnsToSelect);
+
+ // Rewrite column names as we expect them
+ foreach ($columnsToSelect as $id => $oldName) {
+ $dataTable->renameColumn($oldName, $columns[$id]);
+ }
+ if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ foreach ($dataTable->getArray() as $row) {
+ $this->enrichTable($row);
+ }
+ } else {
+ $this->enrichTable($dataTable);
+ }
+ }
+ return $dataTable;
+ }
+
+ protected function enrichTable($table)
+ {
+ $row = $table->getFirstRow();
+ if (!$row) {
+ return;
+ }
+ // AVG order per visit
+ if (false !== $table->getColumn('avg_order_revenue')) {
+ $conversions = $row->getColumn('nb_conversions');
+ if ($conversions) {
+ $row->setColumn('avg_order_revenue', round($row->getColumn('revenue') / $conversions, 2));
+ }
+ }
+ }
+
+ protected function getNumeric($idSite, $period, $date, $segment, $toFetch)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ $dataTable = $archive->getNumeric($toFetch);
+ return $dataTable;
+ }
+
+ /**
+ * @ignore
+ */
+ public function getConversions($idSite, $period, $date, $segment = false, $idGoal = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals::getRecordName('nb_conversions', $idGoal));
+ }
+
+ /**
+ * @ignore
+ */
+ public function getNbVisitsConverted($idSite, $period, $date, $segment = false, $idGoal = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals::getRecordName('nb_visits_converted', $idGoal));
+ }
+
+ /**
+ * @ignore
+ */
+ public function getConversionRate($idSite, $period, $date, $segment = false, $idGoal = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals::getRecordName('conversion_rate', $idGoal));
+ }
+
+ /**
+ * @ignore
+ */
+ public function getRevenue($idSite, $period, $date, $segment = false, $idGoal = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals::getRecordName('revenue', $idGoal));
+ }
+
+ /**
+ * Utility method that retrieve an archived DataTable for a specific site, date range,
+ * segment and goal. If not goal is specified, this method will retrieve and sum the
+ * data for every goal.
+ *
+ * @param string $recordName The archive entry name.
+ * @param int|string $idSite The site(s) to select data for.
+ * @param string $period The period type.
+ * @param string $date The date type.
+ * @param string $segment The segment.
+ * @param int|bool $idGoal The id of the goal to get data for. If this is set to false,
+ * data for every goal that belongs to $idSite is returned.
+ */
+ protected function getGoalSpecificDataTable($recordName, $idSite, $period, $date, $segment, $idGoal)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+
+ // check for the special goal ids
+ $realGoalId = $idGoal != true ? false : self::convertSpecialGoalIds($idGoal);
+
+ // get the data table
+ $dataTable = $archive->getDataTable(Piwik_Goals::getRecordName($recordName, $realGoalId), $idSubtable = null);
+ $dataTable->queueFilter('ReplaceColumnNames');
+
+ return $dataTable;
+ }
+
+ /**
+ * Gets a DataTable that maps ranges of days to the number of conversions that occurred
+ * within those ranges, for the specified site, date range, segment and goal.
+ *
+ * @param int $idSite The site to select data from.
+ * @param string $period The period type.
+ * @param string $date The date type.
+ * @param string|bool $segment The segment.
+ * @param int|bool $idGoal The id of the goal to get data for. If this is set to false,
+ * data for every goal that belongs to $idSite is returned.
+ */
+ public function getDaysToConversion($idSite, $period, $date, $segment = false, $idGoal = false)
+ {
+ $dataTable = $this->getGoalSpecificDataTable(
+ Piwik_Goals::DAYS_UNTIL_CONV_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
+
+ $dataTable->queueFilter('Sort', array('label', 'asc', true));
+ $dataTable->queueFilter(
+ 'BeautifyRangeLabels', array(Piwik_Translate('General_OneDay'), Piwik_Translate('General_NDays')));
+
+ return $dataTable;
+ }
+
+ /**
+ * Gets a DataTable that maps ranges of visit counts to the number of conversions that
+ * occurred on those visits for the specified site, date range, segment and goal.
+ *
+ * @param int $idSite The site to select data from.
+ * @param string $period The period type.
+ * @param string $date The date type.
+ * @param string|bool $segment The segment.
+ * @param int|bool $idGoal The id of the goal to get data for. If this is set to false,
+ * data for every goal that belongs to $idSite is returned.
+ */
+ public function getVisitsUntilConversion($idSite, $period, $date, $segment = false, $idGoal = false)
+ {
+ $dataTable = $this->getGoalSpecificDataTable(
+ Piwik_Goals::VISITS_UNTIL_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
+
+ $dataTable->queueFilter('Sort', array('label', 'asc', true));
+ $dataTable->queueFilter(
+ 'BeautifyRangeLabels', array(Piwik_Translate('General_OneVisit'), Piwik_Translate('General_NVisits')));
+
+ return $dataTable;
+ }
}
diff --git a/plugins/Goals/Controller.php b/plugins/Goals/Controller.php
index 2b423bf9db..35d795a23f 100644
--- a/plugins/Goals/Controller.php
+++ b/plugins/Goals/Controller.php
@@ -15,570 +15,537 @@
*/
class Piwik_Goals_Controller extends Piwik_Controller
{
- const CONVERSION_RATE_PRECISION = 1;
-
- /**
- * Number of "Your top converting keywords/etc are" to display in the per Goal overview page
- * @var int
- */
- const COUNT_TOP_ROWS_TO_DISPLAY = 3;
-
- protected $goalColumnNameToLabel = array(
- 'avg_order_revenue' => 'General_AverageOrderValue',
- 'nb_conversions' => 'Goals_ColumnConversions',
- 'conversion_rate'=> 'General_ColumnConversionRate',
- 'revenue' => 'General_TotalRevenue',
- 'items' => 'General_PurchasedProducts',
- );
-
- private function formatConversionRate($conversionRate)
- {
- return sprintf('%.' . self::CONVERSION_RATE_PRECISION . 'f%%', $conversionRate);
- }
-
- public function __construct()
- {
- parent::__construct();
- $this->idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
- $this->goals = Piwik_Goals_API::getInstance()->getGoals($this->idSite);
- foreach($this->goals as &$goal)
- {
- $goal['name'] = Piwik_Common::sanitizeInputValue($goal['name']);
- if(isset($goal['pattern']))
- {
- $goal['pattern'] = Piwik_Common::sanitizeInputValue($goal['pattern']);
- }
- }
- }
-
- public function widgetGoalReport()
- {
- $view = $this->getGoalReportView($idGoal = Piwik_Common::getRequestVar('idGoal', null, 'string'));
- $view->displayFullReport = false;
- echo $view->render();
- }
-
- public function goalReport()
- {
- $view = $this->getGoalReportView($idGoal = Piwik_Common::getRequestVar('idGoal', null, 'string'));
- $view->displayFullReport = true;
- echo $view->render();
- }
-
- public function ecommerceReport()
- {
- if(!Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables'))
- {
- throw new Exception("Ecommerce Tracking requires that the plugin Custom Variables is enabled. Please enable the plugin CustomVariables (or ask your admin).");
- }
-
- $view = $this->getGoalReportView($idGoal = Piwik_Archive::LABEL_ECOMMERCE_ORDER);
- $view->displayFullReport = true;
- echo $view->render();
- }
- protected function getItemsView($fetch, $type, $function, $api, $abandonedCart = false)
- {
- $saveGET = $_GET;
- $label = Piwik_Translate($type);
- $abandonedCart = Piwik_Common::getRequestVar('viewDataTable', 'ecommerceOrder', 'string') == 'ecommerceAbandonedCart';
-
- // Products in Ecommerce Orders
- if($abandonedCart === false)
- {
- $view = new Piwik_ViewDataTable_HtmlTable_EcommerceOrder();
- $columns = Piwik_Goals::getProductReportColumns();
- $view->setMetricDocumentation('revenue', Piwik_Translate('Goals_ColumnRevenueDocumentation', Piwik_Translate('Goals_DocumentationRevenueGeneratedByProductSales')));
- $view->setMetricDocumentation('quantity', Piwik_Translate('Goals_ColumnQuantityDocumentation', $label));
- $view->setMetricDocumentation('orders', Piwik_Translate('Goals_ColumnOrdersDocumentation', $label));
- $view->setMetricDocumentation('avg_price', Piwik_Translate('Goals_ColumnAveragePriceDocumentation', $label));
- $view->setMetricDocumentation('avg_quantity', Piwik_Translate('Goals_ColumnAverageQuantityDocumentation', $label));
- $view->setMetricDocumentation('nb_visits', Piwik_Translate('Goals_ColumnVisitsProductDocumentation', $label));
- $view->setMetricDocumentation('conversion_rate', Piwik_Translate('Goals_ColumnConversionRateProductDocumentation', $label));
- }
- // Products in Abandoned Carts
- else
- {
- $view = new Piwik_ViewDataTable_HtmlTable_EcommerceAbandonedCart();
- $columns = Piwik_Goals::getProductReportColumns();
- $columns['abandoned_carts'] = Piwik_Translate('General_AbandonedCarts');
- $columns['revenue'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('General_ProductRevenue'));
- $columns['quantity'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('General_Quantity'));
- $columns['avg_quantity'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('General_AverageQuantity'));
- unset($columns['orders']);
- unset($columns['conversion_rate']);
- $_GET['abandonedCarts'] = 1;
- }
-
- $view->init( $this->pluginName, $function, $api );
- $view->enableShowEcommerce();
- $view->disableShowAllViewsIcons();
- $view->disableShowTable();
- $view->disableExcludeLowPopulation();
- $view->disableShowAllColumns();
- $this->setPeriodVariablesView($view);
- $view->setLimit( 10 );
-
- $view->setColumnsTranslations(array_merge(
- array('label' => $label),
- $columns
- ));
- $columnsToDisplay = array_merge(array('label'), array_keys($columns));
- $view->setColumnsToDisplay($columnsToDisplay);
- $view->setSortedColumn('revenue', 'desc');
- foreach(array('revenue', 'avg_price') as $column)
- {
- $view->queueFilter('ColumnCallbackReplace', array($column, array("Piwik", "getPrettyMoney"), array($this->idSite)));
- }
- $return = $this->renderView($view, $fetch);
- $_GET = $saveGET;
- return $return;
- }
-
- public function getItemsSku($fetch = false)
- {
- return $this->getItemsView($fetch, 'Goals_ProductSKU', __FUNCTION__, "Goals.getItemsSku");
- }
-
- public function getItemsName($fetch = false)
- {
- return $this->getItemsView($fetch, 'Goals_ProductName', __FUNCTION__, "Goals.getItemsName");
- }
-
- public function getItemsCategory($fetch = false)
- {
- return $this->getItemsView($fetch, 'Goals_ProductCategory', __FUNCTION__, "Goals.getItemsCategory");
- }
-
- public function getEcommerceLog($fetch = false)
- {
- $saveGET = $_GET;
- $_GET['filterEcommerce'] = Piwik_Common::getRequestVar('filterEcommerce', 1, 'int');
- $_GET['widget'] = 1;
- $_GET['segment'] = 'visitEcommerceStatus!=none';
- $output = Piwik_FrontController::getInstance()->dispatch('Live', 'getVisitorLog', array($fetch));
- $_GET = $saveGET;
- return $output;
- }
-
- protected function getGoalReportView($idGoal = false)
- {
- $view = Piwik_View::factory('single_goal');
- if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
- {
- $goalDefinition['name'] = Piwik_Translate('Goals_Ecommerce');
- $goalDefinition['allow_multiple'] = true;
- $ecommerce = $view->ecommerce = true;
- }
- else
- {
- if(!isset($this->goals[$idGoal]))
- {
- Piwik::redirectToModule('Goals', 'index', array('idGoal' => null));
- }
- $goalDefinition = $this->goals[$idGoal];
- }
- $this->setGeneralVariablesView($view);
- $goal = $this->getMetricsForGoal($idGoal);
- foreach($goal as $name => $value)
- {
- $view->$name = $value;
- }
- if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
- {
- $goal = $this->getMetricsForGoal(Piwik_Archive::LABEL_ECOMMERCE_CART);
- foreach($goal as $name => $value)
- {
- $name = 'cart_'.$name;
- $view->$name = $value;
- }
- }
- $view->idGoal = $idGoal;
- $view->goalName = $goalDefinition['name'];
- $view->goalAllowMultipleConversionsPerVisit = $goalDefinition['allow_multiple'];
- $view->graphEvolution = $this->getEvolutionGraph(true, array('nb_conversions'), $idGoal);
- $view->nameGraphEvolution = 'GoalsgetEvolutionGraph'.$idGoal;
- $view->topDimensions = $this->getTopDimensions($idGoal);
-
- // conversion rate for new and returning visitors
- $segment = 'visitorType==returning,visitorType==returningCustomer';
- $conversionRateReturning = Piwik_Goals_API::getInstance()->getConversionRate($this->idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), $segment, $idGoal);
- $view->conversion_rate_returning = $this->formatConversionRate($conversionRateReturning);
- $segment = 'visitorType==new';
- $conversionRateNew = Piwik_Goals_API::getInstance()->getConversionRate($this->idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), $segment, $idGoal);
- $view->conversion_rate_new = $this->formatConversionRate($conversionRateNew);
- $view->goalReportsByDimension = $this->getGoalReportsByDimensionTable(
- $view->nb_conversions, isset($ecommerce), !empty($view->cart_nb_conversions));
- return $view;
- }
-
- public function index()
- {
- $view = $this->getOverviewView();
- $view->goalsJSON = Piwik_Common::json_encode($this->goals);
- $view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
- $view->ecommerceEnabled = $this->site->isEcommerceEnabled();
- $view->displayFullReport = true;
- echo $view->render();
- }
-
- public function widgetGoalsOverview( )
- {
- $view = $this->getOverviewView();
- $view->displayFullReport = false;
- echo $view->render();
- }
-
- protected function getOverviewView()
- {
- $view = Piwik_View::factory('overview');
- $this->setGeneralVariablesView($view);
-
- $view->graphEvolution = $this->getEvolutionGraph(true, array('nb_conversions'));
- $view->nameGraphEvolution = 'GoalsgetEvolutionGraph';
-
- // sparkline for the historical data of the above values
- $view->urlSparklineConversions = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_conversions'), 'idGoal' => ''));
- $view->urlSparklineConversionRate = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate'), 'idGoal' => ''));
- $view->urlSparklineRevenue = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => ''));
-
- // Pass empty idGoal will return Goal overview
- $request = new Piwik_API_Request("method=Goals.get&format=original&idGoal=");
- $datatable = $request->process();
- $dataRow = $datatable->getFirstRow();
- $view->nb_conversions = $dataRow->getColumn('nb_conversions');
- $view->nb_visits_converted = $dataRow->getColumn('nb_visits_converted');
- $view->conversion_rate = $this->formatConversionRate($dataRow->getColumn('conversion_rate'));
- $view->revenue = $dataRow->getColumn('revenue');
-
- $goalMetrics = array();
- foreach($this->goals as $idGoal => $goal)
- {
- $goalMetrics[$idGoal] = $this->getMetricsForGoal($idGoal);
- $goalMetrics[$idGoal]['name'] = $goal['name'];
- $goalMetrics[$idGoal]['goalAllowMultipleConversionsPerVisit'] = $goal['allow_multiple'];
- }
-
- $view->goalMetrics = $goalMetrics;
- $view->goals = $this->goals;
- $view->goalReportsByDimension = $this->getGoalReportsByDimensionTable(
- $view->nb_conversions, $ecommerce = false, !empty($view->cart_nb_conversions));
- return $view;
- }
-
- public function getLastNbConversionsGraph( $fetch = false )
- {
- $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getConversions');
- return $this->renderView($view, $fetch);
- }
-
- public function getLastConversionRateGraph( $fetch = false )
- {
- $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getConversionRate');
- return $this->renderView($view, $fetch);
- }
-
- public function getLastRevenueGraph( $fetch = false )
- {
- $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getRevenue');
- return $this->renderView($view, $fetch);
- }
-
- public function addNewGoal()
- {
- $view = Piwik_View::factory('add_new_goal');
- $this->setGeneralVariablesView($view);
- $view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
- $view->onlyShowAddNewGoal = true;
- echo $view->render();
- }
-
- public function getEvolutionGraph( $fetch = false, array $columns = array(), $idGoal = false)
- {
- if(empty($columns))
- {
- $columns = Piwik_Common::getRequestVar('columns');
- $columns = Piwik::getArrayFromApiParameter($columns);
- }
-
- $columns = !is_array($columns) ? array($columns) : $columns;
-
- if(empty($idGoal))
- {
- $idGoal = Piwik_Common::getRequestVar('idGoal', false, 'string');
- }
- $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.get');
- $view->setParametersToModify(array('idGoal' => $idGoal));
-
- $nameToLabel = $this->goalColumnNameToLabel;
- if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
- {
- $nameToLabel['nb_conversions'] = 'General_EcommerceOrders';
- }
- elseif($idGoal == Piwik_Archive::LABEL_ECOMMERCE_CART)
- {
- $nameToLabel['nb_conversions'] = Piwik_Translate('General_VisitsWith', Piwik_Translate('Goals_AbandonedCart'));
- $nameToLabel['conversion_rate'] = $nameToLabel['nb_conversions'];
- $nameToLabel['revenue'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_ColumnRevenue'));
- $nameToLabel['items'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_Products'));
- }
-
- $selectableColumns = array('nb_conversions', 'conversion_rate', 'revenue');
- if ($this->site->isEcommerceEnabled())
- {
- $selectableColumns[] = 'items';
- $selectableColumns[] = 'avg_order_revenue';
- }
-
- foreach(array_merge($columns, $selectableColumns) as $columnName)
- {
- $columnTranslation = '';
- // find the right translation for this column, eg. find 'revenue' if column is Goal_1_revenue
- foreach($nameToLabel as $metric => $metricTranslation)
- {
- if(strpos($columnName, $metric) !== false)
- {
- $columnTranslation = Piwik_Translate($metricTranslation);
- break;
- }
- }
-
- if(!empty($idGoal) && isset($this->goals[$idGoal]))
- {
- $goalName = $this->goals[$idGoal]['name'];
- $columnTranslation = "$columnTranslation (".Piwik_Translate('Goals_GoalX', "$goalName").")";
- }
- $view->setColumnTranslation($columnName, $columnTranslation);
- }
- $view->setColumnsToDisplay($columns);
- $view->setSelectableColumns($selectableColumns);
-
- $langString = $idGoal ? 'Goals_SingleGoalOverviewDocumentation' : 'Goals_GoalsOverviewDocumentation';
- $view->setReportDocumentation(Piwik_Translate($langString, '<br />'));
-
- return $this->renderView($view, $fetch);
- }
-
-
- protected function getTopDimensions($idGoal)
- {
- $columnNbConversions = 'goal_'.$idGoal.'_nb_conversions';
- $columnConversionRate = 'goal_'.$idGoal.'_conversion_rate';
-
- $topDimensionsToLoad = array();
-
- if(Piwik_PluginsManager::getInstance()->isPluginActivated('UserCountry'))
- {
- $topDimensionsToLoad += array(
- 'country' => 'UserCountry.getCountry',
- );
- }
-
- $keywordNotDefinedString = '';
- if(Piwik_PluginsManager::getInstance()->isPluginActivated('Referers'))
- {
- $keywordNotDefinedString = Piwik_Referers::getKeywordNotDefinedString();
- $topDimensionsToLoad += array(
- 'keyword' => 'Referers.getKeywords',
- 'website' => 'Referers.getWebsites',
- );
- }
- $topDimensions = array();
- foreach($topDimensionsToLoad as $dimensionName => $apiMethod)
- {
- $request = new Piwik_API_Request("method=$apiMethod
+ const CONVERSION_RATE_PRECISION = 1;
+
+ /**
+ * Number of "Your top converting keywords/etc are" to display in the per Goal overview page
+ * @var int
+ */
+ const COUNT_TOP_ROWS_TO_DISPLAY = 3;
+
+ protected $goalColumnNameToLabel = array(
+ 'avg_order_revenue' => 'General_AverageOrderValue',
+ 'nb_conversions' => 'Goals_ColumnConversions',
+ 'conversion_rate' => 'General_ColumnConversionRate',
+ 'revenue' => 'General_TotalRevenue',
+ 'items' => 'General_PurchasedProducts',
+ );
+
+ private function formatConversionRate($conversionRate)
+ {
+ return sprintf('%.' . self::CONVERSION_RATE_PRECISION . 'f%%', $conversionRate);
+ }
+
+ public function __construct()
+ {
+ parent::__construct();
+ $this->idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
+ $this->goals = Piwik_Goals_API::getInstance()->getGoals($this->idSite);
+ foreach ($this->goals as &$goal) {
+ $goal['name'] = Piwik_Common::sanitizeInputValue($goal['name']);
+ if (isset($goal['pattern'])) {
+ $goal['pattern'] = Piwik_Common::sanitizeInputValue($goal['pattern']);
+ }
+ }
+ }
+
+ public function widgetGoalReport()
+ {
+ $view = $this->getGoalReportView($idGoal = Piwik_Common::getRequestVar('idGoal', null, 'string'));
+ $view->displayFullReport = false;
+ echo $view->render();
+ }
+
+ public function goalReport()
+ {
+ $view = $this->getGoalReportView($idGoal = Piwik_Common::getRequestVar('idGoal', null, 'string'));
+ $view->displayFullReport = true;
+ echo $view->render();
+ }
+
+ public function ecommerceReport()
+ {
+ if (!Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables')) {
+ throw new Exception("Ecommerce Tracking requires that the plugin Custom Variables is enabled. Please enable the plugin CustomVariables (or ask your admin).");
+ }
+
+ $view = $this->getGoalReportView($idGoal = Piwik_Archive::LABEL_ECOMMERCE_ORDER);
+ $view->displayFullReport = true;
+ echo $view->render();
+ }
+
+ protected function getItemsView($fetch, $type, $function, $api, $abandonedCart = false)
+ {
+ $saveGET = $_GET;
+ $label = Piwik_Translate($type);
+ $abandonedCart = Piwik_Common::getRequestVar('viewDataTable', 'ecommerceOrder', 'string') == 'ecommerceAbandonedCart';
+
+ // Products in Ecommerce Orders
+ if ($abandonedCart === false) {
+ $view = new Piwik_ViewDataTable_HtmlTable_EcommerceOrder();
+ $columns = Piwik_Goals::getProductReportColumns();
+ $view->setMetricDocumentation('revenue', Piwik_Translate('Goals_ColumnRevenueDocumentation', Piwik_Translate('Goals_DocumentationRevenueGeneratedByProductSales')));
+ $view->setMetricDocumentation('quantity', Piwik_Translate('Goals_ColumnQuantityDocumentation', $label));
+ $view->setMetricDocumentation('orders', Piwik_Translate('Goals_ColumnOrdersDocumentation', $label));
+ $view->setMetricDocumentation('avg_price', Piwik_Translate('Goals_ColumnAveragePriceDocumentation', $label));
+ $view->setMetricDocumentation('avg_quantity', Piwik_Translate('Goals_ColumnAverageQuantityDocumentation', $label));
+ $view->setMetricDocumentation('nb_visits', Piwik_Translate('Goals_ColumnVisitsProductDocumentation', $label));
+ $view->setMetricDocumentation('conversion_rate', Piwik_Translate('Goals_ColumnConversionRateProductDocumentation', $label));
+ } // Products in Abandoned Carts
+ else {
+ $view = new Piwik_ViewDataTable_HtmlTable_EcommerceAbandonedCart();
+ $columns = Piwik_Goals::getProductReportColumns();
+ $columns['abandoned_carts'] = Piwik_Translate('General_AbandonedCarts');
+ $columns['revenue'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('General_ProductRevenue'));
+ $columns['quantity'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('General_Quantity'));
+ $columns['avg_quantity'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('General_AverageQuantity'));
+ unset($columns['orders']);
+ unset($columns['conversion_rate']);
+ $_GET['abandonedCarts'] = 1;
+ }
+
+ $view->init($this->pluginName, $function, $api);
+ $view->enableShowEcommerce();
+ $view->disableShowAllViewsIcons();
+ $view->disableShowTable();
+ $view->disableExcludeLowPopulation();
+ $view->disableShowAllColumns();
+ $this->setPeriodVariablesView($view);
+ $view->setLimit(10);
+
+ $view->setColumnsTranslations(array_merge(
+ array('label' => $label),
+ $columns
+ ));
+ $columnsToDisplay = array_merge(array('label'), array_keys($columns));
+ $view->setColumnsToDisplay($columnsToDisplay);
+ $view->setSortedColumn('revenue', 'desc');
+ foreach (array('revenue', 'avg_price') as $column) {
+ $view->queueFilter('ColumnCallbackReplace', array($column, array("Piwik", "getPrettyMoney"), array($this->idSite)));
+ }
+ $return = $this->renderView($view, $fetch);
+ $_GET = $saveGET;
+ return $return;
+ }
+
+ public function getItemsSku($fetch = false)
+ {
+ return $this->getItemsView($fetch, 'Goals_ProductSKU', __FUNCTION__, "Goals.getItemsSku");
+ }
+
+ public function getItemsName($fetch = false)
+ {
+ return $this->getItemsView($fetch, 'Goals_ProductName', __FUNCTION__, "Goals.getItemsName");
+ }
+
+ public function getItemsCategory($fetch = false)
+ {
+ return $this->getItemsView($fetch, 'Goals_ProductCategory', __FUNCTION__, "Goals.getItemsCategory");
+ }
+
+ public function getEcommerceLog($fetch = false)
+ {
+ $saveGET = $_GET;
+ $_GET['filterEcommerce'] = Piwik_Common::getRequestVar('filterEcommerce', 1, 'int');
+ $_GET['widget'] = 1;
+ $_GET['segment'] = 'visitEcommerceStatus!=none';
+ $output = Piwik_FrontController::getInstance()->dispatch('Live', 'getVisitorLog', array($fetch));
+ $_GET = $saveGET;
+ return $output;
+ }
+
+ protected function getGoalReportView($idGoal = false)
+ {
+ $view = Piwik_View::factory('single_goal');
+ if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ $goalDefinition['name'] = Piwik_Translate('Goals_Ecommerce');
+ $goalDefinition['allow_multiple'] = true;
+ $ecommerce = $view->ecommerce = true;
+ } else {
+ if (!isset($this->goals[$idGoal])) {
+ Piwik::redirectToModule('Goals', 'index', array('idGoal' => null));
+ }
+ $goalDefinition = $this->goals[$idGoal];
+ }
+ $this->setGeneralVariablesView($view);
+ $goal = $this->getMetricsForGoal($idGoal);
+ foreach ($goal as $name => $value) {
+ $view->$name = $value;
+ }
+ if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ $goal = $this->getMetricsForGoal(Piwik_Archive::LABEL_ECOMMERCE_CART);
+ foreach ($goal as $name => $value) {
+ $name = 'cart_' . $name;
+ $view->$name = $value;
+ }
+ }
+ $view->idGoal = $idGoal;
+ $view->goalName = $goalDefinition['name'];
+ $view->goalAllowMultipleConversionsPerVisit = $goalDefinition['allow_multiple'];
+ $view->graphEvolution = $this->getEvolutionGraph(true, array('nb_conversions'), $idGoal);
+ $view->nameGraphEvolution = 'GoalsgetEvolutionGraph' . $idGoal;
+ $view->topDimensions = $this->getTopDimensions($idGoal);
+
+ // conversion rate for new and returning visitors
+ $segment = 'visitorType==returning,visitorType==returningCustomer';
+ $conversionRateReturning = Piwik_Goals_API::getInstance()->getConversionRate($this->idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), $segment, $idGoal);
+ $view->conversion_rate_returning = $this->formatConversionRate($conversionRateReturning);
+ $segment = 'visitorType==new';
+ $conversionRateNew = Piwik_Goals_API::getInstance()->getConversionRate($this->idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), $segment, $idGoal);
+ $view->conversion_rate_new = $this->formatConversionRate($conversionRateNew);
+ $view->goalReportsByDimension = $this->getGoalReportsByDimensionTable(
+ $view->nb_conversions, isset($ecommerce), !empty($view->cart_nb_conversions));
+ return $view;
+ }
+
+ public function index()
+ {
+ $view = $this->getOverviewView();
+ $view->goalsJSON = Piwik_Common::json_encode($this->goals);
+ $view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
+ $view->ecommerceEnabled = $this->site->isEcommerceEnabled();
+ $view->displayFullReport = true;
+ echo $view->render();
+ }
+
+ public function widgetGoalsOverview()
+ {
+ $view = $this->getOverviewView();
+ $view->displayFullReport = false;
+ echo $view->render();
+ }
+
+ protected function getOverviewView()
+ {
+ $view = Piwik_View::factory('overview');
+ $this->setGeneralVariablesView($view);
+
+ $view->graphEvolution = $this->getEvolutionGraph(true, array('nb_conversions'));
+ $view->nameGraphEvolution = 'GoalsgetEvolutionGraph';
+
+ // sparkline for the historical data of the above values
+ $view->urlSparklineConversions = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_conversions'), 'idGoal' => ''));
+ $view->urlSparklineConversionRate = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate'), 'idGoal' => ''));
+ $view->urlSparklineRevenue = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => ''));
+
+ // Pass empty idGoal will return Goal overview
+ $request = new Piwik_API_Request("method=Goals.get&format=original&idGoal=");
+ $datatable = $request->process();
+ $dataRow = $datatable->getFirstRow();
+ $view->nb_conversions = $dataRow->getColumn('nb_conversions');
+ $view->nb_visits_converted = $dataRow->getColumn('nb_visits_converted');
+ $view->conversion_rate = $this->formatConversionRate($dataRow->getColumn('conversion_rate'));
+ $view->revenue = $dataRow->getColumn('revenue');
+
+ $goalMetrics = array();
+ foreach ($this->goals as $idGoal => $goal) {
+ $goalMetrics[$idGoal] = $this->getMetricsForGoal($idGoal);
+ $goalMetrics[$idGoal]['name'] = $goal['name'];
+ $goalMetrics[$idGoal]['goalAllowMultipleConversionsPerVisit'] = $goal['allow_multiple'];
+ }
+
+ $view->goalMetrics = $goalMetrics;
+ $view->goals = $this->goals;
+ $view->goalReportsByDimension = $this->getGoalReportsByDimensionTable(
+ $view->nb_conversions, $ecommerce = false, !empty($view->cart_nb_conversions));
+ return $view;
+ }
+
+ public function getLastNbConversionsGraph($fetch = false)
+ {
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getConversions');
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getLastConversionRateGraph($fetch = false)
+ {
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getConversionRate');
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getLastRevenueGraph($fetch = false)
+ {
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getRevenue');
+ return $this->renderView($view, $fetch);
+ }
+
+ public function addNewGoal()
+ {
+ $view = Piwik_View::factory('add_new_goal');
+ $this->setGeneralVariablesView($view);
+ $view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
+ $view->onlyShowAddNewGoal = true;
+ echo $view->render();
+ }
+
+ public function getEvolutionGraph($fetch = false, array $columns = array(), $idGoal = false)
+ {
+ if (empty($columns)) {
+ $columns = Piwik_Common::getRequestVar('columns');
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ }
+
+ $columns = !is_array($columns) ? array($columns) : $columns;
+
+ if (empty($idGoal)) {
+ $idGoal = Piwik_Common::getRequestVar('idGoal', false, 'string');
+ }
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.get');
+ $view->setParametersToModify(array('idGoal' => $idGoal));
+
+ $nameToLabel = $this->goalColumnNameToLabel;
+ if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ $nameToLabel['nb_conversions'] = 'General_EcommerceOrders';
+ } elseif ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_CART) {
+ $nameToLabel['nb_conversions'] = Piwik_Translate('General_VisitsWith', Piwik_Translate('Goals_AbandonedCart'));
+ $nameToLabel['conversion_rate'] = $nameToLabel['nb_conversions'];
+ $nameToLabel['revenue'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_ColumnRevenue'));
+ $nameToLabel['items'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_Products'));
+ }
+
+ $selectableColumns = array('nb_conversions', 'conversion_rate', 'revenue');
+ if ($this->site->isEcommerceEnabled()) {
+ $selectableColumns[] = 'items';
+ $selectableColumns[] = 'avg_order_revenue';
+ }
+
+ foreach (array_merge($columns, $selectableColumns) as $columnName) {
+ $columnTranslation = '';
+ // find the right translation for this column, eg. find 'revenue' if column is Goal_1_revenue
+ foreach ($nameToLabel as $metric => $metricTranslation) {
+ if (strpos($columnName, $metric) !== false) {
+ $columnTranslation = Piwik_Translate($metricTranslation);
+ break;
+ }
+ }
+
+ if (!empty($idGoal) && isset($this->goals[$idGoal])) {
+ $goalName = $this->goals[$idGoal]['name'];
+ $columnTranslation = "$columnTranslation (" . Piwik_Translate('Goals_GoalX', "$goalName") . ")";
+ }
+ $view->setColumnTranslation($columnName, $columnTranslation);
+ }
+ $view->setColumnsToDisplay($columns);
+ $view->setSelectableColumns($selectableColumns);
+
+ $langString = $idGoal ? 'Goals_SingleGoalOverviewDocumentation' : 'Goals_GoalsOverviewDocumentation';
+ $view->setReportDocumentation(Piwik_Translate($langString, '<br />'));
+
+ return $this->renderView($view, $fetch);
+ }
+
+
+ protected function getTopDimensions($idGoal)
+ {
+ $columnNbConversions = 'goal_' . $idGoal . '_nb_conversions';
+ $columnConversionRate = 'goal_' . $idGoal . '_conversion_rate';
+
+ $topDimensionsToLoad = array();
+
+ if (Piwik_PluginsManager::getInstance()->isPluginActivated('UserCountry')) {
+ $topDimensionsToLoad += array(
+ 'country' => 'UserCountry.getCountry',
+ );
+ }
+
+ $keywordNotDefinedString = '';
+ if (Piwik_PluginsManager::getInstance()->isPluginActivated('Referers')) {
+ $keywordNotDefinedString = Piwik_Referers::getKeywordNotDefinedString();
+ $topDimensionsToLoad += array(
+ 'keyword' => 'Referers.getKeywords',
+ 'website' => 'Referers.getWebsites',
+ );
+ }
+ $topDimensions = array();
+ foreach ($topDimensionsToLoad as $dimensionName => $apiMethod) {
+ $request = new Piwik_API_Request("method=$apiMethod
&format=original
&filter_update_columns_when_show_all_goals=1
- &idGoal=". Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE ."
+ &idGoal=" . Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE . "
&filter_sort_order=desc
- &filter_sort_column=$columnNbConversions".
- // select a couple more in case some are not valid (ie. conversions==0 or they are "Keyword not defined")
- "&filter_limit=". ( self::COUNT_TOP_ROWS_TO_DISPLAY + 2) );
- $datatable = $request->process();
- $topDimension = array();
- $count = 0;
- foreach($datatable->getRows() as $row)
- {
- $conversions = $row->getColumn($columnNbConversions);
- if($conversions > 0
- && $count < self::COUNT_TOP_ROWS_TO_DISPLAY
-
- // Don't put the "Keyword not defined" in the best segment since it's irritating
- && !($dimensionName == 'keyword'
- && $row->getColumn('label') == $keywordNotDefinedString)
- )
- {
- $topDimension[] = array (
- 'name' => $row->getColumn('label'),
- 'nb_conversions' => $conversions,
- 'conversion_rate' => $this->formatConversionRate($row->getColumn($columnConversionRate)),
- 'metadata' => $row->getMetadata(),
- );
- $count++;
- }
- }
- $topDimensions[$dimensionName] = $topDimension;
- }
- return $topDimensions;
- }
-
- protected function getMetricsForGoal($idGoal)
- {
- $request = new Piwik_API_Request("method=Goals.get&format=original&idGoal=$idGoal");
- $datatable = $request->process();
- $dataRow = $datatable->getFirstRow();
- $nbConversions = $dataRow->getColumn('nb_conversions');
- $nbVisitsConverted = $dataRow->getColumn('nb_visits_converted');
- // Backward compatibilty before 1.3, this value was not processed
- if(empty($nbVisitsConverted))
- {
- $nbVisitsConverted = $nbConversions;
- }
- $revenue = $dataRow->getColumn('revenue');
- $return = array (
- 'id' => $idGoal,
- 'nb_conversions' => (int)$nbConversions,
- 'nb_visits_converted' => (int)$nbVisitsConverted,
- 'conversion_rate' => $this->formatConversionRate($dataRow->getColumn('conversion_rate')),
- 'revenue' => $revenue ? $revenue : 0,
- 'urlSparklineConversions' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_conversions'), 'idGoal' => $idGoal)),
- 'urlSparklineConversionRate' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate'), 'idGoal' => $idGoal)),
- 'urlSparklineRevenue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => $idGoal)),
- );
- if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
- {
- $items = $dataRow->getColumn('items');
- $aov = $dataRow->getColumn('avg_order_revenue');
- $return = array_merge($return, array(
- 'revenue_subtotal' => $dataRow->getColumn('revenue_subtotal'),
- 'revenue_tax' => $dataRow->getColumn('revenue_tax'),
- 'revenue_shipping' => $dataRow->getColumn('revenue_shipping'),
- 'revenue_discount' => $dataRow->getColumn('revenue_discount'),
-
- 'items' => $items ? $items : 0,
- 'avg_order_revenue' => $aov ? $aov : 0,
- 'urlSparklinePurchasedProducts' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('items'), 'idGoal' => $idGoal)),
- 'urlSparklineAverageOrderValue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_order_revenue'), 'idGoal' => $idGoal)),
- ));
- }
- return $return;
- }
-
- /**
- * Gets the 'visits to conversion' report using the requested view type.
- */
- public function getVisitsUntilConversion( $fetch = false )
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init($this->pluginName, __FUNCTION__, 'Goals.getVisitsUntilConversion', 'getVisitsUntilConversion');
- $view->disableSearchBox();
- $view->disableExcludeLowPopulation();
- $view->disableSubTableWhenShowGoals();
- $view->disableShowAllColumns();
- $view->setColumnsToDisplay( array('label','nb_conversions') );
- $view->setSortedColumn('label', 'asc');
- $view->setColumnTranslation('label', Piwik_Translate('Goals_VisitsUntilConv'));
- $view->setColumnTranslation('nb_conversions', Piwik_Translate('Goals_ColumnConversions'));
- $view->setLimit(count(Piwik_Goals::$visitCountRanges));
- $view->disableOffsetInformationAndPaginationControls();
- $view->disableShowAllViewsIcons();
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Gets the 'days to conversion' report using the requested view type.
- */
- public function getDaysToConversion( $fetch = false )
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init($this->pluginName, __FUNCTION__, 'Goals.getDaysToConversion', 'getDaysToConversion');
- $view->disableSearchBox();
- $view->disableExcludeLowPopulation();
- $view->disableSubTableWhenShowGoals();
- $view->disableShowAllColumns();
- $view->setColumnsToDisplay( array('label','nb_conversions') );
- $view->setSortedColumn('label', 'asc');
- $view->setColumnTranslation('label', Piwik_Translate('Goals_DaysToConv'));
- $view->setColumnTranslation('nb_conversions', Piwik_Translate('Goals_ColumnConversions'));
- $view->disableShowAllViewsIcons();
- $view->setLimit(count(Piwik_Goals::$daysToConvRanges));
- $view->disableOffsetInformationAndPaginationControls();
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Utility function that returns HTML that displays Goal information for reports. This
- * is the HTML that is at the bottom of every goals page.
- *
- * @param int $conversions The number of conversions for this goal (or all goals
- * in case of the overview).
- * @param bool $ecommerce Whether to show ecommerce reports or not.
- * @param bool $cartNbConversions Whether there are cart conversions or not for this
- * goal.
- */
- private function getGoalReportsByDimensionTable( $conversions, $ecommerce = false, $cartNbConversions = false )
- {
- $preloadAbandonedCart = $cartNbConversions !== false && $conversions == 0;
-
- $goalReportsByDimension = new Piwik_View_ReportsByDimension();
-
- // add ecommerce reports
- $ecommerceCustomParams = array();
- if ($ecommerce)
- {
- if ($preloadAbandonedCart)
- {
- $ecommerceCustomParams['viewDataTable'] = 'ecommerceAbandonedCart';
- $ecommerceCustomParams['filterEcommerce'] = 2;
- }
-
- $goalReportsByDimension->addReport(
- 'Goals_EcommerceReports', 'Goals_ProductSKU', 'Goals.getItemsSku', $ecommerceCustomParams);
- $goalReportsByDimension->addReport(
- 'Goals_EcommerceReports', 'Goals_ProductName', 'Goals.getItemsName', $ecommerceCustomParams);
- $goalReportsByDimension->addReport(
- 'Goals_EcommerceReports', 'Goals_ProductCategory', 'Goals.getItemsCategory', $ecommerceCustomParams);
- $goalReportsByDimension->addReport(
- 'Goals_EcommerceReports', 'Goals_EcommerceLog', 'Goals.getEcommerceLog', $ecommerceCustomParams);
- }
-
- if ($conversions > 0)
- {
- // for non-Goals reports, we show the goals table
- $customParams = $ecommerceCustomParams
- + array('viewDataTable' => 'tableGoals', 'documentationForGoalsPage' => '1');
-
- if (Piwik_Common::getRequestVar('idGoal', '') === '') // if no idGoal, use 0 for overview
- {
- $customParams['idGoal'] = '0'; // NOTE: Must be string! Otherwise Piwik_View_HtmlTable_Goals fails.
- }
-
- $allReports = Piwik_Goals::getReportsWithGoalMetrics();
- foreach ($allReports as $category => $reports)
- {
- $categoryText = Piwik_Translate('Goals_ViewGoalsBy', $category);
- foreach ($reports as $report)
- {
- $goalReportsByDimension->addReport(
- $categoryText, $report['name'], $report['module'].'.'.$report['action'], $customParams);
- }
- }
- }
-
- return $goalReportsByDimension->render();
- }
+ &filter_sort_column=$columnNbConversions" .
+ // select a couple more in case some are not valid (ie. conversions==0 or they are "Keyword not defined")
+ "&filter_limit=" . (self::COUNT_TOP_ROWS_TO_DISPLAY + 2));
+ $datatable = $request->process();
+ $topDimension = array();
+ $count = 0;
+ foreach ($datatable->getRows() as $row) {
+ $conversions = $row->getColumn($columnNbConversions);
+ if ($conversions > 0
+ && $count < self::COUNT_TOP_ROWS_TO_DISPLAY
+
+ // Don't put the "Keyword not defined" in the best segment since it's irritating
+ && !($dimensionName == 'keyword'
+ && $row->getColumn('label') == $keywordNotDefinedString)
+ ) {
+ $topDimension[] = array(
+ 'name' => $row->getColumn('label'),
+ 'nb_conversions' => $conversions,
+ 'conversion_rate' => $this->formatConversionRate($row->getColumn($columnConversionRate)),
+ 'metadata' => $row->getMetadata(),
+ );
+ $count++;
+ }
+ }
+ $topDimensions[$dimensionName] = $topDimension;
+ }
+ return $topDimensions;
+ }
+
+ protected function getMetricsForGoal($idGoal)
+ {
+ $request = new Piwik_API_Request("method=Goals.get&format=original&idGoal=$idGoal");
+ $datatable = $request->process();
+ $dataRow = $datatable->getFirstRow();
+ $nbConversions = $dataRow->getColumn('nb_conversions');
+ $nbVisitsConverted = $dataRow->getColumn('nb_visits_converted');
+ // Backward compatibilty before 1.3, this value was not processed
+ if (empty($nbVisitsConverted)) {
+ $nbVisitsConverted = $nbConversions;
+ }
+ $revenue = $dataRow->getColumn('revenue');
+ $return = array(
+ 'id' => $idGoal,
+ 'nb_conversions' => (int)$nbConversions,
+ 'nb_visits_converted' => (int)$nbVisitsConverted,
+ 'conversion_rate' => $this->formatConversionRate($dataRow->getColumn('conversion_rate')),
+ 'revenue' => $revenue ? $revenue : 0,
+ 'urlSparklineConversions' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_conversions'), 'idGoal' => $idGoal)),
+ 'urlSparklineConversionRate' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate'), 'idGoal' => $idGoal)),
+ 'urlSparklineRevenue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => $idGoal)),
+ );
+ if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ $items = $dataRow->getColumn('items');
+ $aov = $dataRow->getColumn('avg_order_revenue');
+ $return = array_merge($return, array(
+ 'revenue_subtotal' => $dataRow->getColumn('revenue_subtotal'),
+ 'revenue_tax' => $dataRow->getColumn('revenue_tax'),
+ 'revenue_shipping' => $dataRow->getColumn('revenue_shipping'),
+ 'revenue_discount' => $dataRow->getColumn('revenue_discount'),
+
+ 'items' => $items ? $items : 0,
+ 'avg_order_revenue' => $aov ? $aov : 0,
+ 'urlSparklinePurchasedProducts' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('items'), 'idGoal' => $idGoal)),
+ 'urlSparklineAverageOrderValue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_order_revenue'), 'idGoal' => $idGoal)),
+ ));
+ }
+ return $return;
+ }
+
+ /**
+ * Gets the 'visits to conversion' report using the requested view type.
+ */
+ public function getVisitsUntilConversion($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Goals.getVisitsUntilConversion', 'getVisitsUntilConversion');
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->disableSubTableWhenShowGoals();
+ $view->disableShowAllColumns();
+ $view->setColumnsToDisplay(array('label', 'nb_conversions'));
+ $view->setSortedColumn('label', 'asc');
+ $view->setColumnTranslation('label', Piwik_Translate('Goals_VisitsUntilConv'));
+ $view->setColumnTranslation('nb_conversions', Piwik_Translate('Goals_ColumnConversions'));
+ $view->setLimit(count(Piwik_Goals::$visitCountRanges));
+ $view->disableOffsetInformationAndPaginationControls();
+ $view->disableShowAllViewsIcons();
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Gets the 'days to conversion' report using the requested view type.
+ */
+ public function getDaysToConversion($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Goals.getDaysToConversion', 'getDaysToConversion');
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->disableSubTableWhenShowGoals();
+ $view->disableShowAllColumns();
+ $view->setColumnsToDisplay(array('label', 'nb_conversions'));
+ $view->setSortedColumn('label', 'asc');
+ $view->setColumnTranslation('label', Piwik_Translate('Goals_DaysToConv'));
+ $view->setColumnTranslation('nb_conversions', Piwik_Translate('Goals_ColumnConversions'));
+ $view->disableShowAllViewsIcons();
+ $view->setLimit(count(Piwik_Goals::$daysToConvRanges));
+ $view->disableOffsetInformationAndPaginationControls();
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Utility function that returns HTML that displays Goal information for reports. This
+ * is the HTML that is at the bottom of every goals page.
+ *
+ * @param int $conversions The number of conversions for this goal (or all goals
+ * in case of the overview).
+ * @param bool $ecommerce Whether to show ecommerce reports or not.
+ * @param bool $cartNbConversions Whether there are cart conversions or not for this
+ * goal.
+ */
+ private function getGoalReportsByDimensionTable($conversions, $ecommerce = false, $cartNbConversions = false)
+ {
+ $preloadAbandonedCart = $cartNbConversions !== false && $conversions == 0;
+
+ $goalReportsByDimension = new Piwik_View_ReportsByDimension();
+
+ // add ecommerce reports
+ $ecommerceCustomParams = array();
+ if ($ecommerce) {
+ if ($preloadAbandonedCart) {
+ $ecommerceCustomParams['viewDataTable'] = 'ecommerceAbandonedCart';
+ $ecommerceCustomParams['filterEcommerce'] = 2;
+ }
+
+ $goalReportsByDimension->addReport(
+ 'Goals_EcommerceReports', 'Goals_ProductSKU', 'Goals.getItemsSku', $ecommerceCustomParams);
+ $goalReportsByDimension->addReport(
+ 'Goals_EcommerceReports', 'Goals_ProductName', 'Goals.getItemsName', $ecommerceCustomParams);
+ $goalReportsByDimension->addReport(
+ 'Goals_EcommerceReports', 'Goals_ProductCategory', 'Goals.getItemsCategory', $ecommerceCustomParams);
+ $goalReportsByDimension->addReport(
+ 'Goals_EcommerceReports', 'Goals_EcommerceLog', 'Goals.getEcommerceLog', $ecommerceCustomParams);
+ }
+
+ if ($conversions > 0) {
+ // for non-Goals reports, we show the goals table
+ $customParams = $ecommerceCustomParams
+ + array('viewDataTable' => 'tableGoals', 'documentationForGoalsPage' => '1');
+
+ if (Piwik_Common::getRequestVar('idGoal', '') === '') // if no idGoal, use 0 for overview
+ {
+ $customParams['idGoal'] = '0'; // NOTE: Must be string! Otherwise Piwik_View_HtmlTable_Goals fails.
+ }
+
+ $allReports = Piwik_Goals::getReportsWithGoalMetrics();
+ foreach ($allReports as $category => $reports) {
+ $categoryText = Piwik_Translate('Goals_ViewGoalsBy', $category);
+ foreach ($reports as $report) {
+ $goalReportsByDimension->addReport(
+ $categoryText, $report['name'], $report['module'] . '.' . $report['action'], $customParams);
+ }
+ }
+ }
+
+ return $goalReportsByDimension->render();
+ }
}
// Used so that the template knows which datatable is being currently viewed
-class Piwik_ViewDataTable_HtmlTable_EcommerceOrder extends Piwik_ViewDataTable_HtmlTable {
- protected function getViewDataTableId()
- {
- return Piwik_Archive::LABEL_ECOMMERCE_ORDER;
- }
+class Piwik_ViewDataTable_HtmlTable_EcommerceOrder extends Piwik_ViewDataTable_HtmlTable
+{
+ protected function getViewDataTableId()
+ {
+ return Piwik_Archive::LABEL_ECOMMERCE_ORDER;
+ }
}
-class Piwik_ViewDataTable_HtmlTable_EcommerceAbandonedCart extends Piwik_ViewDataTable_HtmlTable {
- protected function getViewDataTableId()
- {
- return Piwik_Archive::LABEL_ECOMMERCE_CART;
- }
+
+class Piwik_ViewDataTable_HtmlTable_EcommerceAbandonedCart extends Piwik_ViewDataTable_HtmlTable
+{
+ protected function getViewDataTableId()
+ {
+ return Piwik_Archive::LABEL_ECOMMERCE_CART;
+ }
}
diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php
index ee4af3a197..5e0fe62728 100644
--- a/plugins/Goals/Goals.php
+++ b/plugins/Goals/Goals.php
@@ -15,897 +15,855 @@
*/
class Piwik_Goals extends Piwik_Plugin
{
- const VISITS_UNTIL_RECORD_NAME = 'visits_until_conv';
- const DAYS_UNTIL_CONV_RECORD_NAME = 'days_until_conv';
-
- /**
- * This array stores the ranges to use when displaying the 'visits to conversion'
- * report.
- */
- public static $visitCountRanges = array(
- array(1, 1),
- array(2, 2),
- array(3, 3),
- array(4, 4),
- array(5, 5),
- array(6, 6),
- array(7, 7),
- array(8, 8),
- array(9, 14),
- array(15, 25),
- array(26, 50),
- array(51, 100),
- array(100)
- );
-
- /**
- * This array stores the ranges to use when displaying the 'days to conversion'
- * report.
- */
- public static $daysToConvRanges = array(
- array(0, 0),
- array(1, 1),
- array(2, 2),
- array(3, 3),
- array(4, 4),
- array(5, 5),
- array(6, 6),
- array(7, 7),
- array(8, 14),
- array(15, 30),
- array(31, 60),
- array(61, 120),
- array(121, 364),
- array(364)
- );
+ const VISITS_UNTIL_RECORD_NAME = 'visits_until_conv';
+ const DAYS_UNTIL_CONV_RECORD_NAME = 'days_until_conv';
+
+ /**
+ * This array stores the ranges to use when displaying the 'visits to conversion'
+ * report.
+ */
+ public static $visitCountRanges = array(
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 6),
+ array(7, 7),
+ array(8, 8),
+ array(9, 14),
+ array(15, 25),
+ array(26, 50),
+ array(51, 100),
+ array(100)
+ );
+
+ /**
+ * This array stores the ranges to use when displaying the 'days to conversion'
+ * report.
+ */
+ public static $daysToConvRanges = array(
+ array(0, 0),
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 6),
+ array(7, 7),
+ array(8, 14),
+ array(15, 30),
+ array(31, 60),
+ array(61, 120),
+ array(121, 364),
+ array(364)
+ );
public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('Goals_PluginDescription') . ' '. Piwik_Translate('SitesManager_PiwikOffersEcommerceAnalytics', array('<a href="http://piwik.org/docs/ecommerce-analytics/" target="_blank">','</a>')),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- 'TrackerPlugin' => true, // this plugin must be loaded during the stats logging
- );
- return $info;
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'Common.fetchWebsiteAttributes' => 'fetchGoalsFromDb',
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'API.getReportMetadata.end' => 'getReportMetadata',
- 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenus',
- 'SitesManager.deleteSite' => 'deleteSiteGoals',
- 'Goals.getReportsWithGoalMetrics' => 'getActualReportsWithGoalMetrics',
- );
- return $hooks;
- }
-
- /**
- * Delete goals recorded for this site
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function deleteSiteGoals($notification)
- {
- $idSite = &$notification->getNotificationObject();
- Piwik_Query("DELETE FROM ".Piwik_Common::prefixTable('goal') . " WHERE idsite = ? ", array($idSite));
- }
-
- /**
- * Returns the Metadata for the Goals plugin API.
- * The API returns general Goal metrics: conv, conv rate and revenue globally
- * and for each goal.
- *
- * Also, this will update metadata of all other reports that have Goal segmentation
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $info = $notification->getNotificationInfo();
- $idSites = $info['idSites'];
- $reports = &$notification->getNotificationObject();
-
- // Processed in AddColumnsProcessedMetricsGoal
- // These metrics will also be available for some reports, for each goal
- // Example: Conversion rate for Goal 2 for the keyword 'piwik'
- $goalProcessedMetrics = array(
- 'revenue_per_visit' => Piwik_Translate('General_ColumnValuePerVisit'),
- );
-
- $goalMetrics = array(
- 'nb_conversions' => Piwik_Translate('Goals_ColumnConversions'),
- 'nb_visits_converted' => Piwik_Translate('General_ColumnVisitsWithConversions'),
- 'conversion_rate' => Piwik_Translate('General_ColumnConversionRate'),
- 'revenue' => Piwik_Translate('Goals_ColumnRevenue')
- );
-
- $conversionReportMetrics = array(
- 'nb_conversions' => Piwik_Translate('Goals_ColumnConversions')
- );
-
- // General Goal metrics: conversions, conv rate, revenue
- $goalsCategory = Piwik_Translate('Goals_Goals');
- $reports[] = array(
- 'category' => $goalsCategory,
- 'name' => Piwik_Translate('Goals_Goals'),
- 'module' => 'Goals',
- 'action' => 'get',
- 'metrics' => $goalMetrics,
- 'processedMetrics' => array(),
- 'order' => 1
- );
-
- // If only one website is selected, we add the Goal metrics
- if(count($idSites) == 1)
- {
- $idSite = reset($idSites);
- $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
-
- // Add overall visits to conversion report
- $reports[] = array(
- 'category' => $goalsCategory,
- 'name' => Piwik_Translate('Goals_VisitsUntilConv'),
- 'module' => 'Goals',
- 'action' => 'getVisitsUntilConversion',
- 'dimension' => Piwik_Translate('Goals_VisitsUntilConv'),
- 'constantRowsCount' => true,
- 'parameters' => array(),
- 'metrics' => $conversionReportMetrics,
- 'order' => 5
- );
-
- // Add overall days to conversion report
- $reports[] = array(
- 'category' => $goalsCategory,
- 'name' => Piwik_Translate('Goals_DaysToConv'),
- 'module' => 'Goals',
- 'action' => 'getDaysToConversion',
- 'dimension' => Piwik_Translate('Goals_DaysToConv'),
- 'constantRowsCount' => true,
- 'parameters' => array(),
- 'metrics' => $conversionReportMetrics,
- 'order' => 10
- );
-
- foreach($goals as $goal)
- {
- // Add the general Goal metrics: ie. total Goal conversions,
- // Goal conv rate or Goal total revenue.
- // This API call requires a custom parameter
- $reports[] = array(
- 'category' => $goalsCategory,
- 'name' => Piwik_Translate('Goals_GoalX', $goal['name']),
- 'module' => 'Goals',
- 'action' => 'get',
- 'parameters' => array('idGoal' => $goal['idgoal']),
- 'metrics' => $goalMetrics,
- 'processedMetrics' => false,
- 'order' => 50 + $goal['idgoal'] * 3
- );
-
- // Add visits to conversion report
- $reports[] = array(
- 'category' => $goalsCategory,
- 'name' => $goal['name'] . ' - ' . Piwik_Translate('Goals_VisitsUntilConv'),
- 'module' => 'Goals',
- 'action' => 'getVisitsUntilConversion',
- 'dimension' => Piwik_Translate('Goals_VisitsUntilConv'),
- 'constantRowsCount' => true,
- 'parameters' => array('idGoal' => $goal['idgoal']),
- 'metrics' => $conversionReportMetrics,
- 'order' => 51 + $goal['idgoal'] * 3
- );
-
- // Add days to conversion report
- $reports[] = array(
- 'category' => $goalsCategory,
- 'name' => $goal['name'] . ' - ' . Piwik_Translate('Goals_DaysToConv'),
- 'module' => 'Goals',
- 'action' => 'getDaysToConversion',
- 'dimension' => Piwik_Translate('Goals_DaysToConv'),
- 'constantRowsCount' => true,
- 'parameters' => array('idGoal' => $goal['idgoal']),
- 'metrics' => $conversionReportMetrics,
- 'order' => 52 + $goal['idgoal'] * 3
- );
- }
-
- $site = new Piwik_Site($idSite);
- if($site->isEcommerceEnabled())
- {
- $category = Piwik_Translate('Goals_Ecommerce');
- $ecommerceMetrics = array_merge($goalMetrics, array(
- 'revenue_subtotal' => Piwik_Translate('General_Subtotal'),
- 'revenue_tax' => Piwik_Translate('General_Tax'),
- 'revenue_shipping' => Piwik_Translate('General_Shipping'),
- 'revenue_discount' => Piwik_Translate('General_Discount'),
- 'items' => Piwik_Translate('General_PurchasedProducts'),
- 'avg_order_revenue' => Piwik_Translate('General_AverageOrderValue')
- ));
- $ecommerceMetrics['nb_conversions'] = Piwik_Translate('General_EcommerceOrders');
-
- // General Ecommerce metrics
- $reports[] = array(
- 'category' => $category,
- 'name' => Piwik_Translate('General_EcommerceOrders'),
- 'module' => 'Goals',
- 'action' => 'get',
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER),
- 'metrics' => $ecommerceMetrics,
- 'processedMetrics' => false,
- 'order' => 10
- );
- $reports[] = array(
- 'category' => $category,
- 'name' => Piwik_Translate('General_EcommerceOrders') . ' - ' . Piwik_Translate('Goals_VisitsUntilConv'),
- 'module' => 'Goals',
- 'action' => 'getVisitsUntilConversion',
- 'dimension' => Piwik_Translate('Goals_VisitsUntilConv'),
- 'constantRowsCount' => true,
- 'metrics' => $conversionReportMetrics,
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER),
- 'order' => 11
- );
- $reports[] = array(
- 'category' => $category,
- 'name' => Piwik_Translate('General_EcommerceOrders') . ' - ' . Piwik_Translate('Goals_DaysToConv'),
- 'module' => 'Goals',
- 'action' => 'getDaysToConversion',
- 'dimension' => Piwik_Translate('Goals_DaysToConv'),
- 'constantRowsCount' => true,
- 'metrics' => $conversionReportMetrics,
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER),
- 'order' => 12
- );
-
- // Abandoned cart general metrics
- $abandonedCartMetrics = $goalMetrics;
- $abandonedCartMetrics['nb_conversions'] = Piwik_Translate('General_AbandonedCarts');
- $abandonedCartMetrics['revenue'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_ColumnRevenue'));
- $abandonedCartMetrics['items'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_Products'));
- unset($abandonedCartMetrics['nb_visits_converted']);
-
- // Abandoned Cart metrics
- $reports[] = array(
- 'category' => $category,
- 'name' => Piwik_Translate('General_AbandonedCarts'),
- 'module' => 'Goals',
- 'action' => 'get',
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART),
- 'metrics' => $abandonedCartMetrics,
- 'processedMetrics' => false,
- 'order' => 15
- );
-
- $reports[] = array(
- 'category' => $category,
- 'name' => Piwik_Translate('General_AbandonedCarts') . ' - ' . Piwik_Translate('Goals_VisitsUntilConv'),
- 'module' => 'Goals',
- 'action' => 'getVisitsUntilConversion',
- 'dimension' => Piwik_Translate('Goals_VisitsUntilConv'),
- 'constantRowsCount' => true,
- 'metrics' => $conversionReportMetrics,
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART),
- 'order' => 20
- );
- $reports[] = array(
- 'category' => $category,
- 'name' => Piwik_Translate('General_AbandonedCarts') . ' - ' . Piwik_Translate('Goals_DaysToConv'),
- 'module' => 'Goals',
- 'action' => 'getDaysToConversion',
- 'dimension' => Piwik_Translate('Goals_DaysToConv'),
- 'constantRowsCount' => true,
- 'metrics' => $conversionReportMetrics,
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART),
- 'order' => 25
- );
-
- // Product reports metadata
- $productColumns = self::getProductReportColumns();
- foreach($this->ecommerceReports as $i => $ecommerceReport)
- {
- $reports[] = array(
- 'category' => $category,
- 'name' => Piwik_Translate($ecommerceReport[0]),
- 'module' => 'Goals',
- 'action' => $ecommerceReport[2],
- 'dimension' => Piwik_Translate($ecommerceReport[0]),
- 'metrics' => $productColumns,
- 'processedMetrics' => false,
- 'order' => 30 + $i
- );
- }
- }
- }
-
- unset($goalMetrics['nb_visits_converted']);
-
- /*
- * Add the metricsGoal and processedMetricsGoal entry
- * to all reports that have Goal segmentation
- */
- $reportsWithGoals = array();
- Piwik_PostEvent('Goals.getReportsWithGoalMetrics', $reportsWithGoals);
- foreach($reportsWithGoals as $reportWithGoals)
- {
- // Select this report from the API metadata array
- // and add the Goal metrics to it
- foreach($reports as &$apiReportToUpdate)
- {
- if($apiReportToUpdate['module'] == $reportWithGoals['module']
- && $apiReportToUpdate['action'] == $reportWithGoals['action'])
- {
- $apiReportToUpdate['metricsGoal'] = $goalMetrics;
- $apiReportToUpdate['processedMetricsGoal'] = $goalProcessedMetrics;
- break;
- }
- }
- }
-
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getSegmentsMetadata($notification)
- {
- $segments =& $notification->getNotificationObject();
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => 'General_VisitConvertedGoalId',
- 'segment' => 'visitConvertedGoalId',
- 'sqlSegment' => 'log_conversion.idgoal',
- 'acceptedValues' => '1, 2, 3, etc.',
+ {
+ $info = array(
+ 'description' => Piwik_Translate('Goals_PluginDescription') . ' ' . Piwik_Translate('SitesManager_PiwikOffersEcommerceAnalytics', array('<a href="http://piwik.org/docs/ecommerce-analytics/" target="_blank">', '</a>')),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ 'TrackerPlugin' => true, // this plugin must be loaded during the stats logging
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'Common.fetchWebsiteAttributes' => 'fetchGoalsFromDb',
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'API.getReportMetadata.end' => 'getReportMetadata',
+ 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenus',
+ 'SitesManager.deleteSite' => 'deleteSiteGoals',
+ 'Goals.getReportsWithGoalMetrics' => 'getActualReportsWithGoalMetrics',
+ );
+ return $hooks;
+ }
+
+ /**
+ * Delete goals recorded for this site
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function deleteSiteGoals($notification)
+ {
+ $idSite = & $notification->getNotificationObject();
+ Piwik_Query("DELETE FROM " . Piwik_Common::prefixTable('goal') . " WHERE idsite = ? ", array($idSite));
+ }
+
+ /**
+ * Returns the Metadata for the Goals plugin API.
+ * The API returns general Goal metrics: conv, conv rate and revenue globally
+ * and for each goal.
+ *
+ * Also, this will update metadata of all other reports that have Goal segmentation
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $info = $notification->getNotificationInfo();
+ $idSites = $info['idSites'];
+ $reports = & $notification->getNotificationObject();
+
+ // Processed in AddColumnsProcessedMetricsGoal
+ // These metrics will also be available for some reports, for each goal
+ // Example: Conversion rate for Goal 2 for the keyword 'piwik'
+ $goalProcessedMetrics = array(
+ 'revenue_per_visit' => Piwik_Translate('General_ColumnValuePerVisit'),
+ );
+
+ $goalMetrics = array(
+ 'nb_conversions' => Piwik_Translate('Goals_ColumnConversions'),
+ 'nb_visits_converted' => Piwik_Translate('General_ColumnVisitsWithConversions'),
+ 'conversion_rate' => Piwik_Translate('General_ColumnConversionRate'),
+ 'revenue' => Piwik_Translate('Goals_ColumnRevenue')
+ );
+
+ $conversionReportMetrics = array(
+ 'nb_conversions' => Piwik_Translate('Goals_ColumnConversions')
);
- }
-
- protected $ecommerceReports = array(
- array('Goals_ProductSKU', 'Goals', 'getItemsSku'),
- array('Goals_ProductName', 'Goals', 'getItemsName'),
- array('Goals_ProductCategory', 'Goals', 'getItemsCategory')
- );
-
- static public function getProductReportColumns()
- {
- return array(
- 'revenue' => Piwik_Translate('General_ProductRevenue'),
- 'quantity' => Piwik_Translate('General_Quantity'),
- 'orders' => Piwik_Translate('General_UniquePurchases'),
- 'avg_price' => Piwik_Translate('General_AveragePrice'),
- 'avg_quantity' => Piwik_Translate('General_AverageQuantity'),
- 'nb_visits' => Piwik_Translate('General_ColumnNbVisits'),
- 'conversion_rate' => Piwik_Translate('General_ProductConversionRate'),
- );
- }
-
- static public function getReportsWithGoalMetrics()
- {
- $dimensions = array();
- Piwik_PostEvent('Goals.getReportsWithGoalMetrics', $dimensions);
- $dimensionsByGroup = array();
- foreach($dimensions as $dimension)
- {
- $group = $dimension['category'];
- unset($dimension['category']);
- $dimensionsByGroup[$group][] = $dimension;
- }
- return $dimensionsByGroup;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
- $jsFiles[] = "plugins/Goals/templates/GoalForm.js";
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
- $cssFiles[] = "plugins/Goals/templates/goals.css";
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function fetchGoalsFromDb($notification)
- {
- $idsite = $notification->getNotificationInfo();
-
- // add the 'goal' entry in the website array
- $array =& $notification->getNotificationObject();
- $array['goals'] = Piwik_Goals_API::getInstance()->getGoals($idsite);
- }
-
- function addWidgets()
- {
- $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
-
- // Ecommerce widgets
- $site = new Piwik_Site($idSite);
- if($site->isEcommerceEnabled())
- {
- Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceOverview', 'Goals', 'widgetGoalReport', array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER));
- Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceLog', 'Goals', 'getEcommerceLog');
- foreach($this->ecommerceReports as $widget)
- {
- Piwik_AddWidget('Goals_Ecommerce', $widget[0], $widget[1], $widget[2]);
- }
- }
-
- // Goals widgets
- Piwik_AddWidget('Goals_Goals', 'Goals_GoalsOverview', 'Goals', 'widgetGoalsOverview');
- $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
- if(count($goals) > 0)
- {
- foreach($goals as $goal)
- {
- Piwik_AddWidget('Goals_Goals', Piwik_Common::sanitizeInputValue($goal['name']), 'Goals', 'widgetGoalReport', array('idGoal' => $goal['idgoal']));
- }
- }
- }
-
- protected function getGoalCategoryName($idSite)
- {
- $site = new Piwik_Site($idSite);
- return $site->isEcommerceEnabled() ? 'Goals_EcommerceAndGoalsMenu' : 'Goals_Goals';
- }
-
-
- function addMenus()
- {
- $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
- $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
- $mainGoalMenu = $this->getGoalCategoryName($idSite);
- $site = new Piwik_Site($idSite);
- if(count($goals)==0)
- {
- Piwik_AddMenu($mainGoalMenu, '', array(
- 'module' => 'Goals',
- 'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'addNewGoal'),
- 'idGoal' => ($site->isEcommerceEnabled() ? Piwik_Archive::LABEL_ECOMMERCE_ORDER : null)),
- true,
- 25);
- if($site->isEcommerceEnabled())
- {
- Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER), true, 1);
- }
- Piwik_AddMenu($mainGoalMenu, 'Goals_AddNewGoal', array('module' => 'Goals', 'action' => 'addNewGoal'));
- }
- else
- {
- Piwik_AddMenu($mainGoalMenu, '', array(
- 'module' => 'Goals',
- 'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'index'),
- 'idGoal' => ($site->isEcommerceEnabled() ? Piwik_Archive::LABEL_ECOMMERCE_ORDER : null)),
- true,
- 25);
-
- if($site->isEcommerceEnabled())
- {
- Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER), true, 1);
- }
- Piwik_AddMenu($mainGoalMenu, 'Goals_GoalsOverview', array('module' => 'Goals', 'action' => 'index'), true, 2);
- foreach($goals as $goal)
- {
- Piwik_AddMenu($mainGoalMenu, str_replace('%', '%%', Piwik_TranslationWriter::clean($goal['name'])), array('module' => 'Goals', 'action' => 'goalReport', 'idGoal' => $goal['idgoal']));
- }
- }
- }
-
- /**
- * @param string $recordName 'nb_conversions'
- * @param int|bool $idGoal idGoal to return the metrics for, or false to return overall
- * @return string Archive record name
- */
- static public function getRecordName($recordName, $idGoal = false)
- {
- $idGoalStr = '';
- if($idGoal !== false)
- {
- $idGoalStr = $idGoal . "_";
- }
- return 'Goal_' . $idGoalStr . $recordName;
- }
-
- /**
- * Hooks on Period archiving.
- * Sums up Goal conversions stats, and processes overall conversion rate
- *
- * @param Piwik_Event_Notification $notification
- * @return void
- */
- function archivePeriod($notification )
- {
- /**
- * @var Piwik_ArchiveProcessing
- */
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- /*
- * Archive Ecommerce Items
- */
- if($this->shouldArchiveEcommerceItems($archiveProcessing))
- {
- $dataTableToSum = $this->dimensions;
- foreach($this->dimensions as $recordName)
- {
- $dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
- }
- $archiveProcessing->archiveDataTable($dataTableToSum);
- }
-
- /*
- * Archive General Goal metrics
- */
- $goalIdsToSum = Piwik_Tracker_GoalManager::getGoalIds($archiveProcessing->idsite);
-
- //Ecommerce
- $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_ORDER;
- $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_CART; //bug here if idgoal=1
- // Overall goal metrics
- $goalIdsToSum[] = false;
-
- $fieldsToSum = array();
- foreach($goalIdsToSum as $goalId)
- {
- $metricsToSum = Piwik_Goals::getGoalColumns($goalId);
- unset($metricsToSum[array_search('conversion_rate', $metricsToSum)]);
- foreach($metricsToSum as $metricName)
- {
- $fieldsToSum[] = self::getRecordName($metricName, $goalId);
- }
- }
- $records = $archiveProcessing->archiveNumericValuesSum($fieldsToSum);
-
- // also recording conversion_rate for each goal
- foreach($goalIdsToSum as $goalId)
- {
- $nb_conversions = $records[self::getRecordName('nb_visits_converted', $goalId)];
- $conversion_rate = $this->getConversionRate($nb_conversions, $archiveProcessing);
- $archiveProcessing->insertNumericRecord(self::getRecordName('conversion_rate', $goalId), $conversion_rate);
-
- // sum up the visits to conversion data table & the days to conversion data table
- $archiveProcessing->archiveDataTable(array(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $goalId),
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $goalId)));
- }
-
- // sum up goal overview reports
- $archiveProcessing->archiveDataTable(array(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME),
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)));
- }
-
- static public function getGoalColumns($idGoal)
- {
- $columns = array(
- 'nb_conversions',
- 'nb_visits_converted',
- 'conversion_rate',
- 'revenue',
- );
- if($idGoal === false)
- {
- return $columns;
- }
- // Orders
- if($idGoal === Piwik_Tracker_GoalManager::IDGOAL_ORDER)
- {
- $columns = array_merge($columns, array(
- 'revenue_subtotal',
- 'revenue_tax',
- 'revenue_shipping',
- 'revenue_discount',
- ));
- }
- // Abandoned carts & orders
- if($idGoal <= Piwik_Tracker_GoalManager::IDGOAL_ORDER)
- {
- $columns[] = 'items';
- }
- return $columns;
- }
-
- /**
- * Hooks on the Daily archiving.
- * Will process Goal stats overall and for each Goal.
- * Also processes the New VS Returning visitors conversion stats.
- *
- * @param Piwik_Event_Notification $notification
- * @return void
- */
- function archiveDay( $notification )
- {
- /**
- * @var Piwik_ArchiveProcessing_Day
- */
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveGeneralGoalMetrics($archiveProcessing);
- $this->archiveEcommerceItems($archiveProcessing);
- }
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- function archiveGeneralGoalMetrics($archiveProcessing)
- {
- // extra aggregate selects for the visits to conversion report
- $visitToConvExtraCols = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visitor_count_visits', self::$visitCountRanges, 'log_conversion', 'vcv');
-
- // extra aggregate selects for the days to conversion report
- $daysToConvExtraCols = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visitor_days_since_first', self::$daysToConvRanges, 'log_conversion', 'vdsf');
-
- $query = $archiveProcessing->queryConversionsByDimension(
- array(), '', array_merge($visitToConvExtraCols, $daysToConvExtraCols));
-
- if($query === false) { return; }
-
- $goals = array();
- $visitsToConvReport = array();
- $daysToConvReport = array();
-
- // Get a standard empty goal row
- $overall = $archiveProcessing->getNewGoalRow( $idGoal = 1);
- while($row = $query->fetch() )
- {
- $idgoal = $row['idgoal'];
-
- if(!isset($goals[$idgoal]))
- {
- $goals[$idgoal] = $archiveProcessing->getNewGoalRow($idgoal);
-
- $visitsToConvReport[$idgoal] = new Piwik_DataTable();
- $daysToConvReport[$idgoal] = new Piwik_DataTable();
- }
- $archiveProcessing->updateGoalStats($row, $goals[$idgoal]);
-
- // We don't want to sum Abandoned cart metrics in the overall revenue/conversions/converted visits
- // since it is a "negative conversion"
- if($idgoal != Piwik_Tracker_GoalManager::IDGOAL_CART)
- {
- $archiveProcessing->updateGoalStats($row, $overall);
- }
-
- // map the goal + visit number of a visitor with the # of conversions that happened on that visit
- $table = $archiveProcessing->getSimpleDataTableFromRow($row, Piwik_Archive::INDEX_NB_CONVERSIONS, 'vcv');
- $visitsToConvReport[$idgoal]->addDataTable($table);
-
- // map the goal + day number of a visit with the # of conversion that happened on that day
- $table = $archiveProcessing->getSimpleDataTableFromRow($row, Piwik_Archive::INDEX_NB_CONVERSIONS, 'vdsf');
- $daysToConvReport[$idgoal]->addDataTable($table);
- }
-
- // these data tables hold reports for every goal of a site
- $visitsToConvOverview = new Piwik_DataTable();
- $daysToConvOverview = new Piwik_DataTable();
-
- // Stats by goal, for all visitors
- foreach($goals as $idgoal => $values)
- {
- foreach($values as $metricId => $value)
- {
- $metricName = Piwik_Archive::$mappingFromIdToNameGoal[$metricId];
- $recordName = self::getRecordName($metricName, $idgoal);
- $archiveProcessing->insertNumericRecord($recordName, $value);
- }
- $conversion_rate = $this->getConversionRate($values[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED], $archiveProcessing);
- $recordName = self::getRecordName('conversion_rate', $idgoal);
- $archiveProcessing->insertNumericRecord($recordName, $conversion_rate);
-
- // if the goal is not a special goal (like ecommerce) add it to the overview report
- if ($idgoal !== Piwik_Tracker_GoalManager::IDGOAL_CART &&
- $idgoal !== Piwik_Tracker_GoalManager::IDGOAL_ORDER)
- {
- $visitsToConvOverview->addDataTable($visitsToConvReport[$idgoal]);
- $daysToConvOverview->addDataTable($daysToConvReport[$idgoal]);
- }
-
- // visit count until conversion stats
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $idgoal),
- $visitsToConvReport[$idgoal]->getSerialized());
-
- // day count until conversion stats
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $idgoal),
- $daysToConvReport[$idgoal]->getSerialized());
- }
-
- // archive overview reports
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME), $visitsToConvOverview->getSerialized());
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME), $daysToConvOverview->getSerialized());
-
- // Stats for all goals
- $totalAllGoals = array(
- self::getRecordName('conversion_rate') => $this->getConversionRate($archiveProcessing->getNumberOfVisitsConverted(), $archiveProcessing),
- self::getRecordName('nb_conversions') => $overall[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS],
- self::getRecordName('nb_visits_converted') => $archiveProcessing->getNumberOfVisitsConverted(),
- self::getRecordName('revenue') => $overall[Piwik_Archive::INDEX_GOAL_REVENUE],
- );
- foreach($totalAllGoals as $recordName => $value)
- {
- $archiveProcessing->insertNumericRecord($recordName, $value);
- }
- }
-
- protected $dimensions = array(
- 'idaction_sku' => 'Goals_ItemsSku',
- 'idaction_name' => 'Goals_ItemsName',
- 'idaction_category' => 'Goals_ItemsCategory'
- );
-
- protected function shouldArchiveEcommerceItems($archiveProcessing)
- {
- // Per item doesn't support segment
- // Also, when querying Goal metrics for visitorType==returning, we wouldnt want to trigger an extra request
- // event if it did support segment
- // (if this is implented, we should have shouldProcessReportsForPlugin() support partial archiving based on which metric is requested)
- if(!$archiveProcessing->getSegment()->isEmpty())
- {
- return false;
- }
- return true;
- }
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- function archiveEcommerceItems($archiveProcessing)
- {
- if(!$this->shouldArchiveEcommerceItems($archiveProcessing))
- {
- return false;
- }
- $items = array();
-
- $dimensionsToQuery = $this->dimensions;
- $dimensionsToQuery['idaction_category2'] = 'AdditionalCategory';
- $dimensionsToQuery['idaction_category3'] = 'AdditionalCategory';
- $dimensionsToQuery['idaction_category4'] = 'AdditionalCategory';
- $dimensionsToQuery['idaction_category5'] = 'AdditionalCategory';
-
- foreach($dimensionsToQuery as $dimension => $recordName)
- {
- $query = $archiveProcessing->queryEcommerceItems($dimension);
- if($query == false) { continue; }
-
- while($row = $query->fetch())
- {
- $label = $row['label'];
- $ecommerceType = $row['ecommerceType'];
-
- if(empty($label))
- {
- // idaction==0 case:
- // If we are querying any optional category, we do not include idaction=0
- // Otherwise we over-report in the Product Categories report
- if($recordName == 'AdditionalCategory') {
- continue;
- }
- // Product Name/Category not defined"
- if(class_exists('Piwik_CustomVariables')) {
- $label = Piwik_CustomVariables::LABEL_CUSTOM_VALUE_NOT_DEFINED;
- } else {
- $label = "Value not defined";
- }
- }
- // For carts, idorder = 0. To count abandoned carts, we must count visits with an abandoned cart
- if($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART)
- {
- $row[Piwik_Archive::INDEX_ECOMMERCE_ORDERS] = $row[Piwik_Archive::INDEX_NB_VISITS];
- }
- unset($row[Piwik_Archive::INDEX_NB_VISITS]);
- unset($row['label']);
- unset($row['ecommerceType']);
-
- $columnsToRound = array(
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE,
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY,
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE,
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED,
- );
- foreach($columnsToRound as $column)
- {
- if(isset($row[$column])
- && $row[$column] == round($row[$column]))
- {
- $row[$column] = round($row[$column]);
- }
- }
- $items[$dimension][$ecommerceType][$label] = $row;
- }
- }
-
- foreach($this->dimensions as $dimension => $recordName)
- {
- foreach(array(Piwik_Tracker_GoalManager::IDGOAL_CART, Piwik_Tracker_GoalManager::IDGOAL_ORDER) as $ecommerceType)
- {
- if(!isset($items[$dimension][$ecommerceType]))
- {
- continue;
- }
- $recordNameInsert = $recordName;
- if($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART)
- {
- $recordNameInsert = self::getItemRecordNameAbandonedCart($recordName);
- }
- $table = $archiveProcessing->getDataTableFromArray($items[$dimension][$ecommerceType]);
-
- // For "category" report, we aggregate all 5 category queries into one datatable
- if($dimension == 'idaction_category')
- {
- foreach(array('idaction_category2','idaction_category3','idaction_category4','idaction_category5') as $categoryToSum)
- {
- if(!empty($items[$categoryToSum][$ecommerceType]))
- {
- $tableToSum = $archiveProcessing->getDataTableFromArray($items[$categoryToSum][$ecommerceType]);
- $table->addDataTable($tableToSum);
- }
- }
- }
- $archiveProcessing->insertBlobRecord($recordNameInsert, $table->getSerialized());
- }
- }
- }
-
- static public function getItemRecordNameAbandonedCart($recordName)
- {
- return $recordName . '_Cart';
- }
-
- function getConversionRate($count, $archiveProcessing)
- {
- $visits = $archiveProcessing->getNumberOfVisits();
- return round(100 * $count / $visits, Piwik_Tracker_GoalManager::REVENUE_PRECISION);
- }
-
- /**
- * This function executes when the 'Goals.getReportsWithGoalMetrics' event fires. It
- * adds the 'visits to conversion' report metadata to the list of goal reports so
- * this report will be displayed.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function getActualReportsWithGoalMetrics( $notification )
- {
- $dimensions =& $notification->getNotificationObject();
- $dimensions = array_merge($dimensions, array(
- array( 'category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('Goals_VisitsUntilConv'),
- 'module' => 'Goals',
- 'action' => 'getVisitsUntilConversion'
- ),
- array( 'category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('Goals_DaysToConv'),
- 'module' => 'Goals',
- 'action' => 'getDaysToConversion'
- )
- ));
- }
+
+ // General Goal metrics: conversions, conv rate, revenue
+ $goalsCategory = Piwik_Translate('Goals_Goals');
+ $reports[] = array(
+ 'category' => $goalsCategory,
+ 'name' => Piwik_Translate('Goals_Goals'),
+ 'module' => 'Goals',
+ 'action' => 'get',
+ 'metrics' => $goalMetrics,
+ 'processedMetrics' => array(),
+ 'order' => 1
+ );
+
+ // If only one website is selected, we add the Goal metrics
+ if (count($idSites) == 1) {
+ $idSite = reset($idSites);
+ $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
+
+ // Add overall visits to conversion report
+ $reports[] = array(
+ 'category' => $goalsCategory,
+ 'name' => Piwik_Translate('Goals_VisitsUntilConv'),
+ 'module' => 'Goals',
+ 'action' => 'getVisitsUntilConversion',
+ 'dimension' => Piwik_Translate('Goals_VisitsUntilConv'),
+ 'constantRowsCount' => true,
+ 'parameters' => array(),
+ 'metrics' => $conversionReportMetrics,
+ 'order' => 5
+ );
+
+ // Add overall days to conversion report
+ $reports[] = array(
+ 'category' => $goalsCategory,
+ 'name' => Piwik_Translate('Goals_DaysToConv'),
+ 'module' => 'Goals',
+ 'action' => 'getDaysToConversion',
+ 'dimension' => Piwik_Translate('Goals_DaysToConv'),
+ 'constantRowsCount' => true,
+ 'parameters' => array(),
+ 'metrics' => $conversionReportMetrics,
+ 'order' => 10
+ );
+
+ foreach ($goals as $goal) {
+ // Add the general Goal metrics: ie. total Goal conversions,
+ // Goal conv rate or Goal total revenue.
+ // This API call requires a custom parameter
+ $reports[] = array(
+ 'category' => $goalsCategory,
+ 'name' => Piwik_Translate('Goals_GoalX', $goal['name']),
+ 'module' => 'Goals',
+ 'action' => 'get',
+ 'parameters' => array('idGoal' => $goal['idgoal']),
+ 'metrics' => $goalMetrics,
+ 'processedMetrics' => false,
+ 'order' => 50 + $goal['idgoal'] * 3
+ );
+
+ // Add visits to conversion report
+ $reports[] = array(
+ 'category' => $goalsCategory,
+ 'name' => $goal['name'] . ' - ' . Piwik_Translate('Goals_VisitsUntilConv'),
+ 'module' => 'Goals',
+ 'action' => 'getVisitsUntilConversion',
+ 'dimension' => Piwik_Translate('Goals_VisitsUntilConv'),
+ 'constantRowsCount' => true,
+ 'parameters' => array('idGoal' => $goal['idgoal']),
+ 'metrics' => $conversionReportMetrics,
+ 'order' => 51 + $goal['idgoal'] * 3
+ );
+
+ // Add days to conversion report
+ $reports[] = array(
+ 'category' => $goalsCategory,
+ 'name' => $goal['name'] . ' - ' . Piwik_Translate('Goals_DaysToConv'),
+ 'module' => 'Goals',
+ 'action' => 'getDaysToConversion',
+ 'dimension' => Piwik_Translate('Goals_DaysToConv'),
+ 'constantRowsCount' => true,
+ 'parameters' => array('idGoal' => $goal['idgoal']),
+ 'metrics' => $conversionReportMetrics,
+ 'order' => 52 + $goal['idgoal'] * 3
+ );
+ }
+
+ $site = new Piwik_Site($idSite);
+ if ($site->isEcommerceEnabled()) {
+ $category = Piwik_Translate('Goals_Ecommerce');
+ $ecommerceMetrics = array_merge($goalMetrics, array(
+ 'revenue_subtotal' => Piwik_Translate('General_Subtotal'),
+ 'revenue_tax' => Piwik_Translate('General_Tax'),
+ 'revenue_shipping' => Piwik_Translate('General_Shipping'),
+ 'revenue_discount' => Piwik_Translate('General_Discount'),
+ 'items' => Piwik_Translate('General_PurchasedProducts'),
+ 'avg_order_revenue' => Piwik_Translate('General_AverageOrderValue')
+ ));
+ $ecommerceMetrics['nb_conversions'] = Piwik_Translate('General_EcommerceOrders');
+
+ // General Ecommerce metrics
+ $reports[] = array(
+ 'category' => $category,
+ 'name' => Piwik_Translate('General_EcommerceOrders'),
+ 'module' => 'Goals',
+ 'action' => 'get',
+ 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER),
+ 'metrics' => $ecommerceMetrics,
+ 'processedMetrics' => false,
+ 'order' => 10
+ );
+ $reports[] = array(
+ 'category' => $category,
+ 'name' => Piwik_Translate('General_EcommerceOrders') . ' - ' . Piwik_Translate('Goals_VisitsUntilConv'),
+ 'module' => 'Goals',
+ 'action' => 'getVisitsUntilConversion',
+ 'dimension' => Piwik_Translate('Goals_VisitsUntilConv'),
+ 'constantRowsCount' => true,
+ 'metrics' => $conversionReportMetrics,
+ 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER),
+ 'order' => 11
+ );
+ $reports[] = array(
+ 'category' => $category,
+ 'name' => Piwik_Translate('General_EcommerceOrders') . ' - ' . Piwik_Translate('Goals_DaysToConv'),
+ 'module' => 'Goals',
+ 'action' => 'getDaysToConversion',
+ 'dimension' => Piwik_Translate('Goals_DaysToConv'),
+ 'constantRowsCount' => true,
+ 'metrics' => $conversionReportMetrics,
+ 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER),
+ 'order' => 12
+ );
+
+ // Abandoned cart general metrics
+ $abandonedCartMetrics = $goalMetrics;
+ $abandonedCartMetrics['nb_conversions'] = Piwik_Translate('General_AbandonedCarts');
+ $abandonedCartMetrics['revenue'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_ColumnRevenue'));
+ $abandonedCartMetrics['items'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_Products'));
+ unset($abandonedCartMetrics['nb_visits_converted']);
+
+ // Abandoned Cart metrics
+ $reports[] = array(
+ 'category' => $category,
+ 'name' => Piwik_Translate('General_AbandonedCarts'),
+ 'module' => 'Goals',
+ 'action' => 'get',
+ 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART),
+ 'metrics' => $abandonedCartMetrics,
+ 'processedMetrics' => false,
+ 'order' => 15
+ );
+
+ $reports[] = array(
+ 'category' => $category,
+ 'name' => Piwik_Translate('General_AbandonedCarts') . ' - ' . Piwik_Translate('Goals_VisitsUntilConv'),
+ 'module' => 'Goals',
+ 'action' => 'getVisitsUntilConversion',
+ 'dimension' => Piwik_Translate('Goals_VisitsUntilConv'),
+ 'constantRowsCount' => true,
+ 'metrics' => $conversionReportMetrics,
+ 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART),
+ 'order' => 20
+ );
+ $reports[] = array(
+ 'category' => $category,
+ 'name' => Piwik_Translate('General_AbandonedCarts') . ' - ' . Piwik_Translate('Goals_DaysToConv'),
+ 'module' => 'Goals',
+ 'action' => 'getDaysToConversion',
+ 'dimension' => Piwik_Translate('Goals_DaysToConv'),
+ 'constantRowsCount' => true,
+ 'metrics' => $conversionReportMetrics,
+ 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART),
+ 'order' => 25
+ );
+
+ // Product reports metadata
+ $productColumns = self::getProductReportColumns();
+ foreach ($this->ecommerceReports as $i => $ecommerceReport) {
+ $reports[] = array(
+ 'category' => $category,
+ 'name' => Piwik_Translate($ecommerceReport[0]),
+ 'module' => 'Goals',
+ 'action' => $ecommerceReport[2],
+ 'dimension' => Piwik_Translate($ecommerceReport[0]),
+ 'metrics' => $productColumns,
+ 'processedMetrics' => false,
+ 'order' => 30 + $i
+ );
+ }
+ }
+ }
+
+ unset($goalMetrics['nb_visits_converted']);
+
+ /*
+ * Add the metricsGoal and processedMetricsGoal entry
+ * to all reports that have Goal segmentation
+ */
+ $reportsWithGoals = array();
+ Piwik_PostEvent('Goals.getReportsWithGoalMetrics', $reportsWithGoals);
+ foreach ($reportsWithGoals as $reportWithGoals) {
+ // Select this report from the API metadata array
+ // and add the Goal metrics to it
+ foreach ($reports as &$apiReportToUpdate) {
+ if ($apiReportToUpdate['module'] == $reportWithGoals['module']
+ && $apiReportToUpdate['action'] == $reportWithGoals['action']
+ ) {
+ $apiReportToUpdate['metricsGoal'] = $goalMetrics;
+ $apiReportToUpdate['processedMetricsGoal'] = $goalProcessedMetrics;
+ break;
+ }
+ }
+ }
+
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ $segments =& $notification->getNotificationObject();
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit',
+ 'name' => 'General_VisitConvertedGoalId',
+ 'segment' => 'visitConvertedGoalId',
+ 'sqlSegment' => 'log_conversion.idgoal',
+ 'acceptedValues' => '1, 2, 3, etc.',
+ );
+ }
+
+ protected $ecommerceReports = array(
+ array('Goals_ProductSKU', 'Goals', 'getItemsSku'),
+ array('Goals_ProductName', 'Goals', 'getItemsName'),
+ array('Goals_ProductCategory', 'Goals', 'getItemsCategory')
+ );
+
+ static public function getProductReportColumns()
+ {
+ return array(
+ 'revenue' => Piwik_Translate('General_ProductRevenue'),
+ 'quantity' => Piwik_Translate('General_Quantity'),
+ 'orders' => Piwik_Translate('General_UniquePurchases'),
+ 'avg_price' => Piwik_Translate('General_AveragePrice'),
+ 'avg_quantity' => Piwik_Translate('General_AverageQuantity'),
+ 'nb_visits' => Piwik_Translate('General_ColumnNbVisits'),
+ 'conversion_rate' => Piwik_Translate('General_ProductConversionRate'),
+ );
+ }
+
+ static public function getReportsWithGoalMetrics()
+ {
+ $dimensions = array();
+ Piwik_PostEvent('Goals.getReportsWithGoalMetrics', $dimensions);
+ $dimensionsByGroup = array();
+ foreach ($dimensions as $dimension) {
+ $group = $dimension['category'];
+ unset($dimension['category']);
+ $dimensionsByGroup[$group][] = $dimension;
+ }
+ return $dimensionsByGroup;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+ $jsFiles[] = "plugins/Goals/templates/GoalForm.js";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+ $cssFiles[] = "plugins/Goals/templates/goals.css";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function fetchGoalsFromDb($notification)
+ {
+ $idsite = $notification->getNotificationInfo();
+
+ // add the 'goal' entry in the website array
+ $array =& $notification->getNotificationObject();
+ $array['goals'] = Piwik_Goals_API::getInstance()->getGoals($idsite);
+ }
+
+ function addWidgets()
+ {
+ $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
+
+ // Ecommerce widgets
+ $site = new Piwik_Site($idSite);
+ if ($site->isEcommerceEnabled()) {
+ Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceOverview', 'Goals', 'widgetGoalReport', array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER));
+ Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceLog', 'Goals', 'getEcommerceLog');
+ foreach ($this->ecommerceReports as $widget) {
+ Piwik_AddWidget('Goals_Ecommerce', $widget[0], $widget[1], $widget[2]);
+ }
+ }
+
+ // Goals widgets
+ Piwik_AddWidget('Goals_Goals', 'Goals_GoalsOverview', 'Goals', 'widgetGoalsOverview');
+ $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
+ if (count($goals) > 0) {
+ foreach ($goals as $goal) {
+ Piwik_AddWidget('Goals_Goals', Piwik_Common::sanitizeInputValue($goal['name']), 'Goals', 'widgetGoalReport', array('idGoal' => $goal['idgoal']));
+ }
+ }
+ }
+
+ protected function getGoalCategoryName($idSite)
+ {
+ $site = new Piwik_Site($idSite);
+ return $site->isEcommerceEnabled() ? 'Goals_EcommerceAndGoalsMenu' : 'Goals_Goals';
+ }
+
+
+ function addMenus()
+ {
+ $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
+ $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
+ $mainGoalMenu = $this->getGoalCategoryName($idSite);
+ $site = new Piwik_Site($idSite);
+ if (count($goals) == 0) {
+ Piwik_AddMenu($mainGoalMenu, '', array(
+ 'module' => 'Goals',
+ 'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'addNewGoal'),
+ 'idGoal' => ($site->isEcommerceEnabled() ? Piwik_Archive::LABEL_ECOMMERCE_ORDER : null)),
+ true,
+ 25);
+ if ($site->isEcommerceEnabled()) {
+ Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER), true, 1);
+ }
+ Piwik_AddMenu($mainGoalMenu, 'Goals_AddNewGoal', array('module' => 'Goals', 'action' => 'addNewGoal'));
+ } else {
+ Piwik_AddMenu($mainGoalMenu, '', array(
+ 'module' => 'Goals',
+ 'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'index'),
+ 'idGoal' => ($site->isEcommerceEnabled() ? Piwik_Archive::LABEL_ECOMMERCE_ORDER : null)),
+ true,
+ 25);
+
+ if ($site->isEcommerceEnabled()) {
+ Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER), true, 1);
+ }
+ Piwik_AddMenu($mainGoalMenu, 'Goals_GoalsOverview', array('module' => 'Goals', 'action' => 'index'), true, 2);
+ foreach ($goals as $goal) {
+ Piwik_AddMenu($mainGoalMenu, str_replace('%', '%%', Piwik_TranslationWriter::clean($goal['name'])), array('module' => 'Goals', 'action' => 'goalReport', 'idGoal' => $goal['idgoal']));
+ }
+ }
+ }
+
+ /**
+ * @param string $recordName 'nb_conversions'
+ * @param int|bool $idGoal idGoal to return the metrics for, or false to return overall
+ * @return string Archive record name
+ */
+ static public function getRecordName($recordName, $idGoal = false)
+ {
+ $idGoalStr = '';
+ if ($idGoal !== false) {
+ $idGoalStr = $idGoal . "_";
+ }
+ return 'Goal_' . $idGoalStr . $recordName;
+ }
+
+ /**
+ * Hooks on Period archiving.
+ * Sums up Goal conversions stats, and processes overall conversion rate
+ *
+ * @param Piwik_Event_Notification $notification
+ * @return void
+ */
+ function archivePeriod($notification)
+ {
+ /**
+ * @var Piwik_ArchiveProcessing
+ */
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ /*
+ * Archive Ecommerce Items
+ */
+ if ($this->shouldArchiveEcommerceItems($archiveProcessing)) {
+ $dataTableToSum = $this->dimensions;
+ foreach ($this->dimensions as $recordName) {
+ $dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
+ }
+ $archiveProcessing->archiveDataTable($dataTableToSum);
+ }
+
+ /*
+ * Archive General Goal metrics
+ */
+ $goalIdsToSum = Piwik_Tracker_GoalManager::getGoalIds($archiveProcessing->idsite);
+
+ //Ecommerce
+ $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_ORDER;
+ $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_CART; //bug here if idgoal=1
+ // Overall goal metrics
+ $goalIdsToSum[] = false;
+
+ $fieldsToSum = array();
+ foreach ($goalIdsToSum as $goalId) {
+ $metricsToSum = Piwik_Goals::getGoalColumns($goalId);
+ unset($metricsToSum[array_search('conversion_rate', $metricsToSum)]);
+ foreach ($metricsToSum as $metricName) {
+ $fieldsToSum[] = self::getRecordName($metricName, $goalId);
+ }
+ }
+ $records = $archiveProcessing->archiveNumericValuesSum($fieldsToSum);
+
+ // also recording conversion_rate for each goal
+ foreach ($goalIdsToSum as $goalId) {
+ $nb_conversions = $records[self::getRecordName('nb_visits_converted', $goalId)];
+ $conversion_rate = $this->getConversionRate($nb_conversions, $archiveProcessing);
+ $archiveProcessing->insertNumericRecord(self::getRecordName('conversion_rate', $goalId), $conversion_rate);
+
+ // sum up the visits to conversion data table & the days to conversion data table
+ $archiveProcessing->archiveDataTable(array(
+ self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $goalId),
+ self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $goalId)));
+ }
+
+ // sum up goal overview reports
+ $archiveProcessing->archiveDataTable(array(
+ self::getRecordName(self::VISITS_UNTIL_RECORD_NAME),
+ self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)));
+ }
+
+ static public function getGoalColumns($idGoal)
+ {
+ $columns = array(
+ 'nb_conversions',
+ 'nb_visits_converted',
+ 'conversion_rate',
+ 'revenue',
+ );
+ if ($idGoal === false) {
+ return $columns;
+ }
+ // Orders
+ if ($idGoal === Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
+ $columns = array_merge($columns, array(
+ 'revenue_subtotal',
+ 'revenue_tax',
+ 'revenue_shipping',
+ 'revenue_discount',
+ ));
+ }
+ // Abandoned carts & orders
+ if ($idGoal <= Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
+ $columns[] = 'items';
+ }
+ return $columns;
+ }
+
+ /**
+ * Hooks on the Daily archiving.
+ * Will process Goal stats overall and for each Goal.
+ * Also processes the New VS Returning visitors conversion stats.
+ *
+ * @param Piwik_Event_Notification $notification
+ * @return void
+ */
+ function archiveDay($notification)
+ {
+ /**
+ * @var Piwik_ArchiveProcessing_Day
+ */
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $this->archiveGeneralGoalMetrics($archiveProcessing);
+ $this->archiveEcommerceItems($archiveProcessing);
+ }
+
+ /**
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ */
+ function archiveGeneralGoalMetrics($archiveProcessing)
+ {
+ // extra aggregate selects for the visits to conversion report
+ $visitToConvExtraCols = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
+ 'visitor_count_visits', self::$visitCountRanges, 'log_conversion', 'vcv');
+
+ // extra aggregate selects for the days to conversion report
+ $daysToConvExtraCols = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
+ 'visitor_days_since_first', self::$daysToConvRanges, 'log_conversion', 'vdsf');
+
+ $query = $archiveProcessing->queryConversionsByDimension(
+ array(), '', array_merge($visitToConvExtraCols, $daysToConvExtraCols));
+
+ if ($query === false) {
+ return;
+ }
+
+ $goals = array();
+ $visitsToConvReport = array();
+ $daysToConvReport = array();
+
+ // Get a standard empty goal row
+ $overall = $archiveProcessing->getNewGoalRow($idGoal = 1);
+ while ($row = $query->fetch()) {
+ $idgoal = $row['idgoal'];
+
+ if (!isset($goals[$idgoal])) {
+ $goals[$idgoal] = $archiveProcessing->getNewGoalRow($idgoal);
+
+ $visitsToConvReport[$idgoal] = new Piwik_DataTable();
+ $daysToConvReport[$idgoal] = new Piwik_DataTable();
+ }
+ $archiveProcessing->updateGoalStats($row, $goals[$idgoal]);
+
+ // We don't want to sum Abandoned cart metrics in the overall revenue/conversions/converted visits
+ // since it is a "negative conversion"
+ if ($idgoal != Piwik_Tracker_GoalManager::IDGOAL_CART) {
+ $archiveProcessing->updateGoalStats($row, $overall);
+ }
+
+ // map the goal + visit number of a visitor with the # of conversions that happened on that visit
+ $table = $archiveProcessing->getSimpleDataTableFromRow($row, Piwik_Archive::INDEX_NB_CONVERSIONS, 'vcv');
+ $visitsToConvReport[$idgoal]->addDataTable($table);
+
+ // map the goal + day number of a visit with the # of conversion that happened on that day
+ $table = $archiveProcessing->getSimpleDataTableFromRow($row, Piwik_Archive::INDEX_NB_CONVERSIONS, 'vdsf');
+ $daysToConvReport[$idgoal]->addDataTable($table);
+ }
+
+ // these data tables hold reports for every goal of a site
+ $visitsToConvOverview = new Piwik_DataTable();
+ $daysToConvOverview = new Piwik_DataTable();
+
+ // Stats by goal, for all visitors
+ foreach ($goals as $idgoal => $values) {
+ foreach ($values as $metricId => $value) {
+ $metricName = Piwik_Archive::$mappingFromIdToNameGoal[$metricId];
+ $recordName = self::getRecordName($metricName, $idgoal);
+ $archiveProcessing->insertNumericRecord($recordName, $value);
+ }
+ $conversion_rate = $this->getConversionRate($values[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED], $archiveProcessing);
+ $recordName = self::getRecordName('conversion_rate', $idgoal);
+ $archiveProcessing->insertNumericRecord($recordName, $conversion_rate);
+
+ // if the goal is not a special goal (like ecommerce) add it to the overview report
+ if ($idgoal !== Piwik_Tracker_GoalManager::IDGOAL_CART &&
+ $idgoal !== Piwik_Tracker_GoalManager::IDGOAL_ORDER
+ ) {
+ $visitsToConvOverview->addDataTable($visitsToConvReport[$idgoal]);
+ $daysToConvOverview->addDataTable($daysToConvReport[$idgoal]);
+ }
+
+ // visit count until conversion stats
+ $archiveProcessing->insertBlobRecord(
+ self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $idgoal),
+ $visitsToConvReport[$idgoal]->getSerialized());
+
+ // day count until conversion stats
+ $archiveProcessing->insertBlobRecord(
+ self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $idgoal),
+ $daysToConvReport[$idgoal]->getSerialized());
+ }
+
+ // archive overview reports
+ $archiveProcessing->insertBlobRecord(
+ self::getRecordName(self::VISITS_UNTIL_RECORD_NAME), $visitsToConvOverview->getSerialized());
+ $archiveProcessing->insertBlobRecord(
+ self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME), $daysToConvOverview->getSerialized());
+
+ // Stats for all goals
+ $totalAllGoals = array(
+ self::getRecordName('conversion_rate') => $this->getConversionRate($archiveProcessing->getNumberOfVisitsConverted(), $archiveProcessing),
+ self::getRecordName('nb_conversions') => $overall[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS],
+ self::getRecordName('nb_visits_converted') => $archiveProcessing->getNumberOfVisitsConverted(),
+ self::getRecordName('revenue') => $overall[Piwik_Archive::INDEX_GOAL_REVENUE],
+ );
+ foreach ($totalAllGoals as $recordName => $value) {
+ $archiveProcessing->insertNumericRecord($recordName, $value);
+ }
+ }
+
+ protected $dimensions = array(
+ 'idaction_sku' => 'Goals_ItemsSku',
+ 'idaction_name' => 'Goals_ItemsName',
+ 'idaction_category' => 'Goals_ItemsCategory'
+ );
+
+ protected function shouldArchiveEcommerceItems($archiveProcessing)
+ {
+ // Per item doesn't support segment
+ // Also, when querying Goal metrics for visitorType==returning, we wouldnt want to trigger an extra request
+ // event if it did support segment
+ // (if this is implented, we should have shouldProcessReportsForPlugin() support partial archiving based on which metric is requested)
+ if (!$archiveProcessing->getSegment()->isEmpty()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ */
+ function archiveEcommerceItems($archiveProcessing)
+ {
+ if (!$this->shouldArchiveEcommerceItems($archiveProcessing)) {
+ return false;
+ }
+ $items = array();
+
+ $dimensionsToQuery = $this->dimensions;
+ $dimensionsToQuery['idaction_category2'] = 'AdditionalCategory';
+ $dimensionsToQuery['idaction_category3'] = 'AdditionalCategory';
+ $dimensionsToQuery['idaction_category4'] = 'AdditionalCategory';
+ $dimensionsToQuery['idaction_category5'] = 'AdditionalCategory';
+
+ foreach ($dimensionsToQuery as $dimension => $recordName) {
+ $query = $archiveProcessing->queryEcommerceItems($dimension);
+ if ($query == false) {
+ continue;
+ }
+
+ while ($row = $query->fetch()) {
+ $label = $row['label'];
+ $ecommerceType = $row['ecommerceType'];
+
+ if (empty($label)) {
+ // idaction==0 case:
+ // If we are querying any optional category, we do not include idaction=0
+ // Otherwise we over-report in the Product Categories report
+ if ($recordName == 'AdditionalCategory') {
+ continue;
+ }
+ // Product Name/Category not defined"
+ if (class_exists('Piwik_CustomVariables')) {
+ $label = Piwik_CustomVariables::LABEL_CUSTOM_VALUE_NOT_DEFINED;
+ } else {
+ $label = "Value not defined";
+ }
+ }
+ // For carts, idorder = 0. To count abandoned carts, we must count visits with an abandoned cart
+ if ($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART) {
+ $row[Piwik_Archive::INDEX_ECOMMERCE_ORDERS] = $row[Piwik_Archive::INDEX_NB_VISITS];
+ }
+ unset($row[Piwik_Archive::INDEX_NB_VISITS]);
+ unset($row['label']);
+ unset($row['ecommerceType']);
+
+ $columnsToRound = array(
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE,
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY,
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE,
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED,
+ );
+ foreach ($columnsToRound as $column) {
+ if (isset($row[$column])
+ && $row[$column] == round($row[$column])
+ ) {
+ $row[$column] = round($row[$column]);
+ }
+ }
+ $items[$dimension][$ecommerceType][$label] = $row;
+ }
+ }
+
+ foreach ($this->dimensions as $dimension => $recordName) {
+ foreach (array(Piwik_Tracker_GoalManager::IDGOAL_CART, Piwik_Tracker_GoalManager::IDGOAL_ORDER) as $ecommerceType) {
+ if (!isset($items[$dimension][$ecommerceType])) {
+ continue;
+ }
+ $recordNameInsert = $recordName;
+ if ($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART) {
+ $recordNameInsert = self::getItemRecordNameAbandonedCart($recordName);
+ }
+ $table = $archiveProcessing->getDataTableFromArray($items[$dimension][$ecommerceType]);
+
+ // For "category" report, we aggregate all 5 category queries into one datatable
+ if ($dimension == 'idaction_category') {
+ foreach (array('idaction_category2', 'idaction_category3', 'idaction_category4', 'idaction_category5') as $categoryToSum) {
+ if (!empty($items[$categoryToSum][$ecommerceType])) {
+ $tableToSum = $archiveProcessing->getDataTableFromArray($items[$categoryToSum][$ecommerceType]);
+ $table->addDataTable($tableToSum);
+ }
+ }
+ }
+ $archiveProcessing->insertBlobRecord($recordNameInsert, $table->getSerialized());
+ }
+ }
+ }
+
+ static public function getItemRecordNameAbandonedCart($recordName)
+ {
+ return $recordName . '_Cart';
+ }
+
+ function getConversionRate($count, $archiveProcessing)
+ {
+ $visits = $archiveProcessing->getNumberOfVisits();
+ return round(100 * $count / $visits, Piwik_Tracker_GoalManager::REVENUE_PRECISION);
+ }
+
+ /**
+ * This function executes when the 'Goals.getReportsWithGoalMetrics' event fires. It
+ * adds the 'visits to conversion' report metadata to the list of goal reports so
+ * this report will be displayed.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getActualReportsWithGoalMetrics($notification)
+ {
+ $dimensions =& $notification->getNotificationObject();
+ $dimensions = array_merge($dimensions, array(
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('Goals_VisitsUntilConv'),
+ 'module' => 'Goals',
+ 'action' => 'getVisitsUntilConversion'
+ ),
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('Goals_DaysToConv'),
+ 'module' => 'Goals',
+ 'action' => 'getDaysToConversion'
+ )
+ ));
+ }
}
diff --git a/plugins/Goals/templates/GoalForm.js b/plugins/Goals/templates/GoalForm.js
index 042f8eb95f..f4574bb7c5 100644
--- a/plugins/Goals/templates/GoalForm.js
+++ b/plugins/Goals/templates/GoalForm.js
@@ -5,103 +5,96 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function showAddNewGoal()
-{
- hideForms();
- $(".entityAddContainer").show();
- showCancel();
- piwikHelper.lazyScrollTo(".entityContainer", 400);
- return false;
+function showAddNewGoal() {
+ hideForms();
+ $(".entityAddContainer").show();
+ showCancel();
+ piwikHelper.lazyScrollTo(".entityContainer", 400);
+ return false;
}
-function showEditGoals()
-{
- hideForms();
- $("#entityEditContainer").show();
- showCancel();
- piwikHelper.lazyScrollTo(".entityContainer", 400);
- return false;
+function showEditGoals() {
+ hideForms();
+ $("#entityEditContainer").show();
+ showCancel();
+ piwikHelper.lazyScrollTo(".entityContainer", 400);
+ return false;
}
-function hideForms()
-{
- $(".entityAddContainer").hide();
- $("#entityEditContainer").hide();
+function hideForms() {
+ $(".entityAddContainer").hide();
+ $("#entityEditContainer").hide();
}
-function showCancel()
-{
- $(".entityCancel").show();
- $('.entityCancelLink').click( function(){
- hideForms();
- $(".entityCancel").hide();
- });
+function showCancel() {
+ $(".entityCancel").show();
+ $('.entityCancelLink').click(function () {
+ hideForms();
+ $(".entityCancel").hide();
+ });
}
// init the goal form with existing goal value, if any
-function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, pattern, patternType, caseSensitive, revenue, allowMultiple, goalId)
-{
- $('#goal_name').val(goalName);
- if(matchAttribute == 'manually') {
- $('select[name=trigger_type] option[value=manually]').prop('selected', true);
- $('input[name=match_attribute]').prop('disabled', true);
- $('#match_attribute_section').hide();
- $('#manual_trigger_section').show();
- matchAttribute = 'url';
- } else {
- $('select[name=trigger_type] option[value=visitors]').prop('selected', true);
- }
- $('input[name=match_attribute][value='+matchAttribute+']').prop('checked', true);
- $('input[name=allow_multiple][value='+allowMultiple+']').prop('checked', true);
- $('#match_attribute_name').html(mappingMatchTypeName[matchAttribute]);
- $('#examples_pattern').html(mappingMatchTypeExamples[matchAttribute]);
- $('select[name=pattern_type] option[value='+patternType+']').prop('selected', true);
- $('input[name=pattern]').val(pattern);
- $('#case_sensitive').prop('checked', caseSensitive);
- $('input[name=revenue]').val(revenue);
- $('input[name=methodGoalAPI]').val(goalMethodAPI);
- $('#goal_submit').val(submitText);
- if(goalId != undefined) {
- $('input[name=goalIdUpdate]').val(goalId);
- }
+function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, pattern, patternType, caseSensitive, revenue, allowMultiple, goalId) {
+ $('#goal_name').val(goalName);
+ if (matchAttribute == 'manually') {
+ $('select[name=trigger_type] option[value=manually]').prop('selected', true);
+ $('input[name=match_attribute]').prop('disabled', true);
+ $('#match_attribute_section').hide();
+ $('#manual_trigger_section').show();
+ matchAttribute = 'url';
+ } else {
+ $('select[name=trigger_type] option[value=visitors]').prop('selected', true);
+ }
+ $('input[name=match_attribute][value=' + matchAttribute + ']').prop('checked', true);
+ $('input[name=allow_multiple][value=' + allowMultiple + ']').prop('checked', true);
+ $('#match_attribute_name').html(mappingMatchTypeName[matchAttribute]);
+ $('#examples_pattern').html(mappingMatchTypeExamples[matchAttribute]);
+ $('select[name=pattern_type] option[value=' + patternType + ']').prop('selected', true);
+ $('input[name=pattern]').val(pattern);
+ $('#case_sensitive').prop('checked', caseSensitive);
+ $('input[name=revenue]').val(revenue);
+ $('input[name=methodGoalAPI]').val(goalMethodAPI);
+ $('#goal_submit').val(submitText);
+ if (goalId != undefined) {
+ $('input[name=goalIdUpdate]').val(goalId);
+ }
}
-function bindGoalForm()
-{
- $('select[name=trigger_type]').click( function() {
- var triggerTypeId = $(this).val();
- if(triggerTypeId == "manually") {
- $('input[name=match_attribute]').prop('disabled', true);
- $('#match_attribute_section').hide();
- $('#manual_trigger_section').show();
- } else {
- $('input[name=match_attribute]').removeProp('disabled');
- $('#match_attribute_section').show();
- $('#manual_trigger_section').hide();
- }
- });
-
- $('input[name=match_attribute]').click( function() {
- var matchTypeId = $(this).val();
- $('#match_attribute_name').html(mappingMatchTypeName[matchTypeId]);
- $('#examples_pattern').html(mappingMatchTypeExamples[matchTypeId]);
- });
-
- $('#goal_submit').click( function() {
- // prepare ajax query to API to add goal
+function bindGoalForm() {
+ $('select[name=trigger_type]').click(function () {
+ var triggerTypeId = $(this).val();
+ if (triggerTypeId == "manually") {
+ $('input[name=match_attribute]').prop('disabled', true);
+ $('#match_attribute_section').hide();
+ $('#manual_trigger_section').show();
+ } else {
+ $('input[name=match_attribute]').removeProp('disabled');
+ $('#match_attribute_section').show();
+ $('#manual_trigger_section').hide();
+ }
+ });
+
+ $('input[name=match_attribute]').click(function () {
+ var matchTypeId = $(this).val();
+ $('#match_attribute_name').html(mappingMatchTypeName[matchTypeId]);
+ $('#examples_pattern').html(mappingMatchTypeExamples[matchTypeId]);
+ });
+
+ $('#goal_submit').click(function () {
+ // prepare ajax query to API to add goal
ajaxAddGoal();
- return false;
- });
-
- $('a[name=linkAddNewGoal]').click( function(){
- initAndShowAddGoalForm();
- piwikHelper.lazyScrollTo('#goal_name');
- } );
+ return false;
+ });
+
+ $('a[name=linkAddNewGoal]').click(function () {
+ initAndShowAddGoalForm();
+ piwikHelper.lazyScrollTo('#goal_name');
+ });
}
-function ajaxDeleteGoal(idGoal)
-{
+function ajaxDeleteGoal(idGoal) {
piwikHelper.lazyScrollTo(".entityContainer", 400);
var parameters = {};
@@ -113,18 +106,17 @@ function ajaxDeleteGoal(idGoal)
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(parameters, 'get');
ajaxRequest.setLoadingElement('#goalAjaxLoading');
- ajaxRequest.setCallback(function(){ location.reload(); });
+ ajaxRequest.setCallback(function () { location.reload(); });
ajaxRequest.send(true);
}
-function ajaxAddGoal()
-{
+function ajaxAddGoal() {
piwikHelper.lazyScrollTo(".entityContainer", 400);
var parameters = {};
- parameters.name = encodeURIComponent( $('#goal_name').val() );
+ parameters.name = encodeURIComponent($('#goal_name').val());
- if($('[name=trigger_type]').val() == 'manually') {
+ if ($('[name=trigger_type]').val() == 'manually') {
parameters.matchAttribute = 'manually';
parameters.patternType = 'regex';
parameters.pattern = '.*';
@@ -132,13 +124,13 @@ function ajaxAddGoal()
} else {
parameters.matchAttribute = $('input[name=match_attribute]:checked').val();
parameters.patternType = $('[name=pattern_type]').val();
- parameters.pattern = encodeURIComponent( $('input[name=pattern]').val() );
- parameters.caseSensitive = $('#case_sensitive').prop('checked') == true ? 1: 0;
+ parameters.pattern = encodeURIComponent($('input[name=pattern]').val());
+ parameters.caseSensitive = $('#case_sensitive').prop('checked') == true ? 1 : 0;
}
parameters.revenue = $('input[name=revenue]').val();
- parameters.allowMultipleConversionsPerVisit = $('input[name=allow_multiple]:checked').val() == true ? 1: 0;
+ parameters.allowMultipleConversionsPerVisit = $('input[name=allow_multiple]:checked').val() == true ? 1 : 0;
- parameters.idGoal = $('input[name=goalIdUpdate]').val();
+ parameters.idGoal = $('input[name=goalIdUpdate]').val();
parameters.format = 'json';
parameters.module = 'API';
parameters.method = $('input[name=methodGoalAPI]').val();
@@ -146,38 +138,36 @@ function ajaxAddGoal()
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(parameters, 'get');
ajaxRequest.setLoadingElement('#goalAjaxLoading');
- ajaxRequest.setCallback(function(){ location.reload(); });
+ ajaxRequest.setCallback(function () { location.reload(); });
ajaxRequest.send(true);
}
-function bindListGoalEdit()
-{
- $('a[name=linkEditGoal]').click( function() {
- var goalId = $(this).attr('id');
- var goal = piwik.goals[goalId];
- initGoalForm("Goals.updateGoal", _pk_translate('Goals_UpdateGoal_js'), goal.name, goal.match_attribute, goal.pattern, goal.pattern_type, (goal.case_sensitive != '0'), goal.revenue, goal.allow_multiple, goalId);
- showAddNewGoal();
- return false;
- });
-
- $('a[name=linkDeleteGoal]').click( function() {
- var goalId = $(this).attr('id');
- var goal = piwik.goals[goalId];
-
- $('#confirm h2').text(sprintf(_pk_translate('Goals_DeleteGoalConfirm_js'), '"'+goal.name+'"'));
- piwikHelper.modalConfirm('#confirm', {yes: function(){
- ajaxDeleteGoal( goalId );
- }});
- return false;
- });
-
- $('a[name=linkEditGoals]').click( function(){
- return showEditGoals();
- } );
+function bindListGoalEdit() {
+ $('a[name=linkEditGoal]').click(function () {
+ var goalId = $(this).attr('id');
+ var goal = piwik.goals[goalId];
+ initGoalForm("Goals.updateGoal", _pk_translate('Goals_UpdateGoal_js'), goal.name, goal.match_attribute, goal.pattern, goal.pattern_type, (goal.case_sensitive != '0'), goal.revenue, goal.allow_multiple, goalId);
+ showAddNewGoal();
+ return false;
+ });
+
+ $('a[name=linkDeleteGoal]').click(function () {
+ var goalId = $(this).attr('id');
+ var goal = piwik.goals[goalId];
+
+ $('#confirm h2').text(sprintf(_pk_translate('Goals_DeleteGoalConfirm_js'), '"' + goal.name + '"'));
+ piwikHelper.modalConfirm('#confirm', {yes: function () {
+ ajaxDeleteGoal(goalId);
+ }});
+ return false;
+ });
+
+ $('a[name=linkEditGoals]').click(function () {
+ return showEditGoals();
+ });
}
-function initAndShowAddGoalForm()
-{
- initGoalForm('Goals.addGoal', _pk_translate('Goals_AddGoal_js'), '', 'url', '', 'contains', caseSensitive = false, allowMultiple = '0', '0');
- return showAddNewGoal();
+function initAndShowAddGoalForm() {
+ initGoalForm('Goals.addGoal', _pk_translate('Goals_AddGoal_js'), '', 'url', '', 'contains', caseSensitive = false, allowMultiple = '0', '0');
+ return showAddNewGoal();
}
diff --git a/plugins/Goals/templates/add_edit_goal.tpl b/plugins/Goals/templates/add_edit_goal.tpl
index 373ddc7bc8..8a5c42588f 100644
--- a/plugins/Goals/templates/add_edit_goal.tpl
+++ b/plugins/Goals/templates/add_edit_goal.tpl
@@ -1,76 +1,79 @@
{if isset($onlyShowAddNewGoal)}
<h2>{'Goals_AddNewGoal'|translate}</h2>
- <p>Goal Conversion tracking is one of the most efficient ways to measure and improve your business objectives.</p>
- <p>A Goal in Piwik is your strategy, your priority, and can entail many things: "Downloaded brochure", "Registered newsletter", "Visited page services.html", etc. What do you want your users to do on your website?
- You will be able to view and analyse your performance for each Goal, and learn how to increase conversions, conversion rates and revenue per visit.</p>
+ <p>Goal Conversion tracking is one of the most efficient ways to measure and improve your business objectives.</p>
+ <p>A Goal in Piwik is your strategy, your priority, and can entail many things: "Downloaded brochure", "Registered newsletter", "Visited page
+ services.html", etc. What do you want your users to do on your website?
+ You will be able to view and analyse your performance for each Goal, and learn how to increase conversions, conversion rates and revenue per visit.</p>
<p>{'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>":"</a>"}
</p>
{else}
<div class="clear"></div>
- <h2>{'Goals_GoalsManagement'|translate}</h2>
- <div class="entityList">
- <ul class='listCircle'>
- <li><a onclick='' name='linkAddNewGoal'><u>{'Goals_CreateNewGOal'|translate}</u></a></li>
- <li><a onclick='' name='linkEditGoals'>{'Goals_ViewAndEditGoals'|translate}</a></li>
- <li>{'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>":"</a>"}</li>
+ <h2>{'Goals_GoalsManagement'|translate}</h2>
+ <div class="entityList">
+ <ul class='listCircle'>
+ <li><a onclick='' name='linkAddNewGoal'><u>{'Goals_CreateNewGOal'|translate}</u></a></li>
+ <li><a onclick='' name='linkEditGoals'>{'Goals_ViewAndEditGoals'|translate}</a></li>
+ <li>{'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>":"</a>"}</li>
- <li>{if !$ecommerceEnabled}
- {capture assign='websiteManageText'}<a href='{url module=SitesManager action=index}'>{'SitesManager_WebsitesManagement'|translate}</a>{/capture}
- {capture assign='ecommerceReportText'}<a href="http://piwik.org/docs/ecommerce-analytics/" target="_blank">{'Goals_EcommerceReports'|translate}</a>{/capture}
- {'Goals_Optional'|translate} {'Goals_Ecommerce'|translate}: {'Goals_YouCanEnableEcommerceReports'|translate:$ecommerceReportText:$websiteManageText}
- {else}
- {'SitesManager_PiwikOffersEcommerceAnalytics'|translate:'<a href="http://piwik.org/docs/ecommerce-analytics/" target="_blank">':"</a>"}
- {/if}
- </li>
- </ul>
- </div>
- <br/>
+ <li>{if !$ecommerceEnabled}
+ {capture assign='websiteManageText'}<a
+ href='{url module=SitesManager action=index}'>{'SitesManager_WebsitesManagement'|translate}</a>{/capture}
+ {capture assign='ecommerceReportText'}<a href="http://piwik.org/docs/ecommerce-analytics/"
+ target="_blank">{'Goals_EcommerceReports'|translate}</a>{/capture}
+ {'Goals_Optional'|translate} {'Goals_Ecommerce'|translate}: {'Goals_YouCanEnableEcommerceReports'|translate:$ecommerceReportText:$websiteManageText}
+ {else}
+ {'SitesManager_PiwikOffersEcommerceAnalytics'|translate:'<a href="http://piwik.org/docs/ecommerce-analytics/" target="_blank">':"</a>"}
+ {/if}
+ </li>
+ </ul>
+ </div>
+ <br/>
{/if}
{ajaxErrorDiv}
{ajaxLoadingDiv id=goalAjaxLoading}
-
+
<div class="entityContainer">
- {if !isset($onlyShowAddNewGoal)}
- {include file="Goals/templates/list_goal_edit.tpl"}
- {/if}
- {include file="Goals/templates/form_add_goal.tpl"}
- {if !isset($onlyShowAddNewGoal)}
- <div class='entityCancel' style='display:none'>
- {'General_OrCancel'|translate:"<a class='entityCancelLink'>":"</a>"}
- </div>
- {/if}
- <a id='bottom'></a>
+ {if !isset($onlyShowAddNewGoal)}
+ {include file="Goals/templates/list_goal_edit.tpl"}
+ {/if}
+ {include file="Goals/templates/form_add_goal.tpl"}
+ {if !isset($onlyShowAddNewGoal)}
+ <div class='entityCancel' style='display:none'>
+ {'General_OrCancel'|translate:"<a class='entityCancelLink'>":"</a>"}
+ </div>
+ {/if}
+ <a id='bottom'></a>
</div>
<br/><br/>
{loadJavascriptTranslations plugins='Goals'}
<script type="text/javascript">
-var mappingMatchTypeName = {ldelim}
- "url": "{'Goals_URL'|translate|escape}",
- "title": "{'Goals_PageTitle'|translate|escape}",
- "file": "{'Goals_Filename'|translate|escape}",
- "external_website": "{'Goals_ExternalWebsiteUrl'|translate|escape}"
-{rdelim};
-var mappingMatchTypeExamples = {ldelim}
- "url": "{'General_ForExampleShort'|translate} {'Goals_Contains'|translate:"'checkout/confirmation'"|escape} \
+ var mappingMatchTypeName = {ldelim}
+ "url": "{'Goals_URL'|translate|escape}",
+ "title": "{'Goals_PageTitle'|translate|escape}",
+ "file": "{'Goals_Filename'|translate|escape}",
+ "external_website": "{'Goals_ExternalWebsiteUrl'|translate|escape}"
+ {rdelim};
+ var mappingMatchTypeExamples = {ldelim}
+ "url": "{'General_ForExampleShort'|translate} {'Goals_Contains'|translate:"'checkout/confirmation'"|escape} \
<br />{'General_ForExampleShort'|translate|escape} {'Goals_IsExactly'|translate:"'http://example.com/thank-you.html'"|escape} \
- <br />{'General_ForExampleShort'|translate|escape} {'Goals_MatchesExpression'|translate:"'(.*)\\\/demo\\\/(.*)'"|escape}",
- "title": "{'General_ForExampleShort'|translate} {'Goals_Contains'|translate:"'Order confirmation'"|escape}",
- "file": "{'General_ForExampleShort'|translate|escape} {'Goals_Contains'|translate:"'files/brochure.pdf'"|escape} \
+ <br />{'General_ForExampleShort'|translate|escape} {'Goals_MatchesExpression'|translate:"'(.*)\\\/demo\\\/(.*)'"|escape}",
+ "title": "{'General_ForExampleShort'|translate} {'Goals_Contains'|translate:"'Order confirmation'"|escape}",
+ "file": "{'General_ForExampleShort'|translate|escape} {'Goals_Contains'|translate:"'files/brochure.pdf'"|escape} \
<br />{'General_ForExampleShort'|translate|escape} {'Goals_IsExactly'|translate:"'http://example.com/files/brochure.pdf'"|escape} \
- <br />{'General_ForExampleShort'|translate|escape} {'Goals_MatchesExpression'|translate:"'(.*)\\\.zip'"|escape}",
- "external_website": "{'General_ForExampleShort'|translate|escape} {'Goals_Contains'|translate:"'amazon.com'"|escape} \
+ <br />{'General_ForExampleShort'|translate|escape} {'Goals_MatchesExpression'|translate:"'(.*)\\\.zip'"|escape}",
+ "external_website": "{'General_ForExampleShort'|translate|escape} {'Goals_Contains'|translate:"'amazon.com'"|escape} \
<br />{'General_ForExampleShort'|translate|escape} {'Goals_IsExactly'|translate:"'http://mypartner.com/landing.html'"|escape} \
- <br />{'General_ForExampleShort'|translate|escape} {'Goals_MatchesExpression'|translate:"'http://www.amazon.com\\\/(.*)\\\/yourAffiliateId'"|escape}"
-{rdelim};
-bindGoalForm();
+ <br />{'General_ForExampleShort'|translate|escape} {'Goals_MatchesExpression'|translate:"'http://www.amazon.com\\\/(.*)\\\/yourAffiliateId'"|escape}"
+ {rdelim};
+ bindGoalForm();
-{if !isset($onlyShowAddNewGoal)}
-piwik.goals = {$goalsJSON};
-bindListGoalEdit();
-{else}
-initAndShowAddGoalForm();
-{/if}
+ {if !isset($onlyShowAddNewGoal)}
+ piwik.goals = {$goalsJSON};
+ bindListGoalEdit();
+ {else}
+ initAndShowAddGoalForm();
+ {/if}
</script>
diff --git a/plugins/Goals/templates/add_new_goal.tpl b/plugins/Goals/templates/add_new_goal.tpl
index fb41a3e1c3..5b7a916072 100644
--- a/plugins/Goals/templates/add_new_goal.tpl
+++ b/plugins/Goals/templates/add_new_goal.tpl
@@ -1,11 +1,10 @@
-
{if $userCanEditGoals}
- {include file="Goals/templates/add_edit_goal.tpl"}
+ {include file="Goals/templates/add_edit_goal.tpl"}
{else}
-<h2>{'Goals_CreateNewGOal'|translate}</h2>
-<p>
-{'Goals_NoGoalsNeedAccess'|translate}
-</p>
-<p>{'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>":"</a>"}
-</p>
+ <h2>{'Goals_CreateNewGOal'|translate}</h2>
+ <p>
+ {'Goals_NoGoalsNeedAccess'|translate}
+ </p>
+ <p>{'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>":"</a>"}
+ </p>
{/if}
diff --git a/plugins/Goals/templates/form_add_goal.tpl b/plugins/Goals/templates/form_add_goal.tpl
index 73a4f89f57..4c8517eb63 100644
--- a/plugins/Goals/templates/form_add_goal.tpl
+++ b/plugins/Goals/templates/form_add_goal.tpl
@@ -1,93 +1,97 @@
<div class='entityAddContainer' style="display:none;">
-<form>
-<table class="dataTable entityTable">
- <thead>
- <tr class="first">
- <th colspan="2">{'Goals_AddNewGoal'|translate}</th>
- <tr>
- </thead>
- <tbody>
- <tr>
- <td class="first">{'Goals_GoalName'|translate} </th>
- <td><input type="text" name="name" value="" size="28" id="goal_name" class="inp" /></td>
- </tr>
- <tr>
- <td style='width:260px;' class="first">{'Goals_GoalIsTriggered'|translate}
- <select name="trigger_type" class="inp">
- <option value="visitors">{'Goals_WhenVisitors'|translate}</option>
- <option value="manually">{'Goals_Manually'|translate}</option>
- </select>
- </td>
- <td>
- <input type="radio" id="match_attribute_url" value="url" name="match_attribute" />
- <label for="match_attribute_url">{'Goals_VisitUrl'|translate}</label>
- <br />
- <input type="radio" id="match_attribute_title" value="title" name="match_attribute" />
- <label for="match_attribute_title">{'Goals_VisitPageTitle'|translate}</label>
- <br />
- <input type="radio" id="match_attribute_file" value="file" name="match_attribute" />
- <label for="match_attribute_file">{'Goals_Download'|translate}</label>
- <br />
- <input type="radio" id="match_attribute_external_website" value="external_website" name="match_attribute" />
- <label for="match_attribute_external_website">{'Goals_ClickOutlink'|translate}</label>
- </td>
- </tr>
- </tbody>
- <tbody id="match_attribute_section">
- <tr>
- <td class="first">{'Goals_WhereThe'|translate} <span id="match_attribute_name"></span></td>
- <td>
- <select name="pattern_type" class="inp">
- <option value="contains">{'Goals_Contains'|translate:""}</option>
- <option value="exact">{'Goals_IsExactly'|translate:""}</option>
- <option value="regex">{'Goals_MatchesExpression'|translate:""}</option>
- </select>
-
- <input type="text" name="pattern" value="" size="16" class="inp" />
- <br />
- <div id="examples_pattern" class="entityInlineHelp"></div>
- <br />
+ <form>
+ <table class="dataTable entityTable">
+ <thead>
+ <tr class="first">
+ <th colspan="2">{'Goals_AddNewGoal'|translate}</th>
+ <tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="first">{'Goals_GoalName'|translate} </th>
+ <td><input type="text" name="name" value="" size="28" id="goal_name" class="inp"/></td>
+ </tr>
+ <tr>
+ <td style='width:260px;' class="first">{'Goals_GoalIsTriggered'|translate}
+ <select name="trigger_type" class="inp">
+ <option value="visitors">{'Goals_WhenVisitors'|translate}</option>
+ <option value="manually">{'Goals_Manually'|translate}</option>
+ </select>
+ </td>
+ <td>
+ <input type="radio" id="match_attribute_url" value="url" name="match_attribute"/>
+ <label for="match_attribute_url">{'Goals_VisitUrl'|translate}</label>
+ <br/>
+ <input type="radio" id="match_attribute_title" value="title" name="match_attribute"/>
+ <label for="match_attribute_title">{'Goals_VisitPageTitle'|translate}</label>
+ <br/>
+ <input type="radio" id="match_attribute_file" value="file" name="match_attribute"/>
+ <label for="match_attribute_file">{'Goals_Download'|translate}</label>
+ <br/>
+ <input type="radio" id="match_attribute_external_website" value="external_website" name="match_attribute"/>
+ <label for="match_attribute_external_website">{'Goals_ClickOutlink'|translate}</label>
+ </td>
+ </tr>
+ </tbody>
+ <tbody id="match_attribute_section">
+ <tr>
+ <td class="first">{'Goals_WhereThe'|translate} <span id="match_attribute_name"></span></td>
+ <td>
+ <select name="pattern_type" class="inp">
+ <option value="contains">{'Goals_Contains'|translate:""}</option>
+ <option value="exact">{'Goals_IsExactly'|translate:""}</option>
+ <option value="regex">{'Goals_MatchesExpression'|translate:""}</option>
+ </select>
+
+ <input type="text" name="pattern" value="" size="16" class="inp"/>
+ <br/>
+
+ <div id="examples_pattern" class="entityInlineHelp"></div>
+ <br/>
<span style="float:right">
- {'Goals_Optional'|translate} <input type="checkbox" id="case_sensitive" />
+ {'Goals_Optional'|translate} <input type="checkbox" id="case_sensitive"/>
<label for="case_sensitive">{'Goals_CaseSensitive'|translate}</label>
</span>
- </td>
- </tr>
- </tbody>
- <tbody id="manual_trigger_section" style="display:none">
- <tr><td colspan="2" class="first">
- {'Goals_WhereVisitedPageManuallyCallsJavascriptTrackerLearnMore'|translate:"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/javascript-tracking/%23toc-manually-trigger-a-conversion-for-a-goal'>":"</a>"}
- </td></tr>
- </tbody>
- <tbody>
- <tr>
- <td class="first"> {'Goals_AllowMultipleConversionsPerVisit'|translate} </td>
- <td>
- <input type="radio" id="allow_multiple_0" value="0" name="allow_multiple" />
- <label for="allow_multiple_0">{'Goals_DefaultGoalConvertedOncePerVisit'|translate}</label>
- <div class="entityInlineHelp">
- {'Goals_HelpOneConversionPerVisit'|translate}
- </div>
- <br/>
-
- <input type="radio" id="allow_multiple_1" value="1" name="allow_multiple" />
- <label for="allow_multiple_1">{'Goals_AllowGoalConvertedMoreThanOncePerVisit'|translate}</label>
- <br /><br />
- </tr>
- <tr>
- </tbody>
- <tbody>
- <tr>
- <td class="first">{'Goals_Optional'|translate} {'Goals_DefaultRevenue'|translate}</td>
- <td>{' <input type="text" name="revenue" size="2" value="0" class="inp" /> '|money:$idSite}
- <div class="entityInlineHelp"> {'Goals_DefaultRevenueHelp'|translate} </div>
- </td>
- </tr>
- <tr>
- </tbody>
-</table>
- <input type="hidden" name="methodGoalAPI" value="" />
- <input type="hidden" name="goalIdUpdate" value="" />
- <input type="submit" value="" name="submit" id="goal_submit" class="submit" />
-</form>
+ </td>
+ </tr>
+ </tbody>
+ <tbody id="manual_trigger_section" style="display:none">
+ <tr>
+ <td colspan="2" class="first">
+ {'Goals_WhereVisitedPageManuallyCallsJavascriptTrackerLearnMore'|translate:"<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/javascript-tracking/%23toc-manually-trigger-a-conversion-for-a-goal'>":"</a>"}
+ </td>
+ </tr>
+ </tbody>
+ <tbody>
+ <tr>
+ <td class="first"> {'Goals_AllowMultipleConversionsPerVisit'|translate} </td>
+ <td>
+ <input type="radio" id="allow_multiple_0" value="0" name="allow_multiple"/>
+ <label for="allow_multiple_0">{'Goals_DefaultGoalConvertedOncePerVisit'|translate}</label>
+
+ <div class="entityInlineHelp">
+ {'Goals_HelpOneConversionPerVisit'|translate}
+ </div>
+ <br/>
+
+ <input type="radio" id="allow_multiple_1" value="1" name="allow_multiple"/>
+ <label for="allow_multiple_1">{'Goals_AllowGoalConvertedMoreThanOncePerVisit'|translate}</label>
+ <br/><br/>
+ </tr>
+ <tr>
+ </tbody>
+ <tbody>
+ <tr>
+ <td class="first">{'Goals_Optional'|translate} {'Goals_DefaultRevenue'|translate}</td>
+ <td>{' <input type="text" name="revenue" size="2" value="0" class="inp" /> '|money:$idSite}
+ <div class="entityInlineHelp"> {'Goals_DefaultRevenueHelp'|translate} </div>
+ </td>
+ </tr>
+ <tr>
+ </tbody>
+ </table>
+ <input type="hidden" name="methodGoalAPI" value=""/>
+ <input type="hidden" name="goalIdUpdate" value=""/>
+ <input type="submit" value="" name="submit" id="goal_submit" class="submit"/>
+ </form>
</div>
diff --git a/plugins/Goals/templates/goals.css b/plugins/Goals/templates/goals.css
index b27176beac..52602215bb 100644
--- a/plugins/Goals/templates/goals.css
+++ b/plugins/Goals/templates/goals.css
@@ -1,24 +1,27 @@
-.goalTopElement {
- border-bottom:1px dotted;
-}
-.goalEntry{
- margin:0 0 20px 0;
- padding:0 0 10px 0;
- border-bottom:1px solid #7e7363;
- width:614px;
+.goalTopElement {
+ border-bottom: 1px dotted;
+}
+
+.goalEntry {
+ margin: 0 0 20px 0;
+ padding: 0 0 10px 0;
+ border-bottom: 1px solid #7e7363;
+ width: 614px;
}
/* dimension selector */
#titleGoalsByDimension {
- padding-top:30px;
+ padding-top: 30px;
}
+
ul.ulGoalTopElements {
- list-style-type:circle;
- margin-left:30px;
+ list-style-type: circle;
+ margin-left: 30px;
}
+
.ulGoalTopElements a {
- text-decoration:none;
- color:#0033CC;
- border-bottom:1px dotted #0033CC;
- line-height:2em;
+ text-decoration: none;
+ color: #0033CC;
+ border-bottom: 1px dotted #0033CC;
+ line-height: 2em;
}
diff --git a/plugins/Goals/templates/list_goal_edit.tpl b/plugins/Goals/templates/list_goal_edit.tpl
index 4580f8baf1..1a412d5f82 100644
--- a/plugins/Goals/templates/list_goal_edit.tpl
+++ b/plugins/Goals/templates/list_goal_edit.tpl
@@ -1,50 +1,54 @@
<div id='entityEditContainer' style="display:none;">
- <table class="dataTable entityTable">
- <thead>
- <tr>
- <th class="first">Id</th>
- <th>{'Goals_GoalName'|translate}</th>
- <th>{'Goals_GoalIsTriggeredWhen'|translate}</th>
- <th>{'Goals_ColumnRevenue'|translate}</th>
- <th>{'General_Edit'|translate}</th>
- <th>{'General_Delete'|translate}</th>
- </tr>
- </thead>
- {foreach from=$goals item=goal}
- <tr>
- <td class="first">{$goal.idgoal}</td>
- <td>{$goal.name}</td>
- <td><span class='matchAttribute'>{$goal.match_attribute}</span> {if isset($goal.pattern_type)}<br />{'Goals_Pattern'|translate} {$goal.pattern_type}: {$goal.pattern}</b>{/if}</td>
- <td>{if $goal.revenue==0}-{else}{$goal.revenue|money:$idSite}{/if}</td>
- <td><a href='#' name="linkEditGoal" id="{$goal.idgoal}" class="link_but"><img src='themes/default/images/ico_edit.png' border="0" /> {'General_Edit'|translate}</a></td>
- <td><a href='#' name="linkDeleteGoal" id="{$goal.idgoal}" class="link_but"><img src='themes/default/images/ico_delete.png' border="0" /> {'General_Delete'|translate}</a></td>
- </tr>
- {/foreach}
- </table>
+ <table class="dataTable entityTable">
+ <thead>
+ <tr>
+ <th class="first">Id</th>
+ <th>{'Goals_GoalName'|translate}</th>
+ <th>{'Goals_GoalIsTriggeredWhen'|translate}</th>
+ <th>{'Goals_ColumnRevenue'|translate}</th>
+ <th>{'General_Edit'|translate}</th>
+ <th>{'General_Delete'|translate}</th>
+ </tr>
+ </thead>
+ {foreach from=$goals item=goal}
+ <tr>
+ <td class="first">{$goal.idgoal}</td>
+ <td>{$goal.name}</td>
+ <td><span class='matchAttribute'>{$goal.match_attribute}</span> {if isset($goal.pattern_type)}
+ <br/>
+ {'Goals_Pattern'|translate} {$goal.pattern_type}: {$goal.pattern}</b>{/if}</td>
+ <td>{if $goal.revenue==0}-{else}{$goal.revenue|money:$idSite}{/if}</td>
+ <td><a href='#' name="linkEditGoal" id="{$goal.idgoal}" class="link_but"><img src='themes/default/images/ico_edit.png'
+ border="0"/> {'General_Edit'|translate}</a></td>
+ <td><a href='#' name="linkDeleteGoal" id="{$goal.idgoal}" class="link_but"><img src='themes/default/images/ico_delete.png'
+ border="0"/> {'General_Delete'|translate}</a></td>
+ </tr>
+ {/foreach}
+ </table>
</div>
<div class="ui-confirm" id="confirm">
<h2></h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_No'|translate}" />
-</div>
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_No'|translate}"/>
+</div>
<script type="text/javascript">
-var goalTypeToTranslation = {ldelim}
- "manually" : "{'Goals_ManuallyTriggeredUsingJavascriptFunction'|translate}",
- "file" : "{'Goals_Download'|translate}",
- "url" : "{'Goals_VisitUrl'|translate}",
- "title" : "{'Goals_VisitPageTitle'|translate}",
- "external_website" : "{'Goals_ClickOutlink'|translate}"
-{rdelim}
-{literal}
-$(document).ready( function() {
- // translation of the goal "match attribute" to human readable description
- $('.matchAttribute').each( function() {
- matchAttribute = $(this).text();
- translation = goalTypeToTranslation[matchAttribute];
- $(this).text(translation);
- });
-} );
-{/literal}
+ var goalTypeToTranslation = {ldelim}
+ "manually": "{'Goals_ManuallyTriggeredUsingJavascriptFunction'|translate}",
+ "file": "{'Goals_Download'|translate}",
+ "url": "{'Goals_VisitUrl'|translate}",
+ "title": "{'Goals_VisitPageTitle'|translate}",
+ "external_website": "{'Goals_ClickOutlink'|translate}"
+ {rdelim}
+ {literal}
+ $(document).ready(function () {
+ // translation of the goal "match attribute" to human readable description
+ $('.matchAttribute').each(function () {
+ matchAttribute = $(this).text();
+ translation = goalTypeToTranslation[matchAttribute];
+ $(this).text(translation);
+ });
+ });
+ {/literal}
</script>
diff --git a/plugins/Goals/templates/list_top_dimension.tpl b/plugins/Goals/templates/list_top_dimension.tpl
index 50e86d9b4d..4bbe28adc9 100644
--- a/plugins/Goals/templates/list_top_dimension.tpl
+++ b/plugins/Goals/templates/list_top_dimension.tpl
@@ -1,10 +1,9 @@
-
{foreach from=$topDimension item=element name=topGoalElements}
- {assign var=goal_nb_conversion value=$element.nb_conversions}
- {assign var=goal_conversion_rate value=$element.conversion_rate}
- <span class='goalTopElement' title='{'Goals_Conversions'|translate:"<b>$goal_nb_conversion</b>"},
+ {assign var=goal_nb_conversion value=$element.nb_conversions}
+ {assign var=goal_conversion_rate value=$element.conversion_rate}
+ <span class='goalTopElement' title='{'Goals_Conversions'|translate:"<b>$goal_nb_conversion</b>"},
{'Goals_ConversionRate'|translate:"<b>$goal_conversion_rate</b>"}'>
{$element.name}</span>
- {logoHtml metadata=$element.metadata alt=$element.name}
- {if $smarty.foreach.topGoalElements.iteration == $smarty.foreach.topGoalElements.total-1} and {elseif $smarty.foreach.topGoalElements.iteration < $smarty.foreach.topGoalElements.total-1}, {else}{/if}
+ {logoHtml metadata=$element.metadata alt=$element.name}
+ {if $smarty.foreach.topGoalElements.iteration == $smarty.foreach.topGoalElements.total-1} and {elseif $smarty.foreach.topGoalElements.iteration < $smarty.foreach.topGoalElements.total-1}, {else}{/if}
{/foreach} \ No newline at end of file
diff --git a/plugins/Goals/templates/overview.tpl b/plugins/Goals/templates/overview.tpl
index 200260f36b..0e971602ce 100644
--- a/plugins/Goals/templates/overview.tpl
+++ b/plugins/Goals/templates/overview.tpl
@@ -1,48 +1,47 @@
-<link rel="stylesheet" type="text/css" href="plugins/Goals/templates/goals.css" />
+<link rel="stylesheet" type="text/css" href="plugins/Goals/templates/goals.css"/>
{include file="Goals/templates/title_and_evolution_graph.tpl"}
{assign var=sum_nb_conversions value=$nb_conversions}
{foreach from=$goalMetrics item=goal}
- {assign var=nb_conversions value=$goal.nb_conversions}
- {assign var=nb_visits_converted value=$goal.nb_visits_converted}
- {assign var=conversion_rate value=$goal.conversion_rate}
- {assign var=name value=$goal.name}
+ {assign var=nb_conversions value=$goal.nb_conversions}
+ {assign var=nb_visits_converted value=$goal.nb_visits_converted}
+ {assign var=conversion_rate value=$goal.conversion_rate}
+ {assign var=name value=$goal.name}
+ <div class="goalEntry">
+ <h2>
+ <a href="javascript:broadcast.propagateAjax('module=Goals&action=goalReport&idGoal={$goal.id}')">
+ {'Goals_GoalX'|translate:"'$name'"}
+ </a>
+ </h2>
-<div class="goalEntry">
- <h2>
- <a href="javascript:broadcast.propagateAjax('module=Goals&action=goalReport&idGoal={$goal.id}')">
- {'Goals_GoalX'|translate:"'$name'"}
- </a>
- </h2>
- <div id='leftcolumn'>
- <div class="sparkline">{sparkline src=$goal.urlSparklineConversions}
- {'Goals_Conversions'|translate:"<strong>$nb_conversions</strong>"}
- {if $goal.goalAllowMultipleConversionsPerVisit}
- ({'VisitsSummary_NbVisits'|translate:"<strong>$nb_visits_converted</strong>"})
- {/if}
- </div>
- </div>
- <div id='rightcolumn'>
- <div class="sparkline">{sparkline src=$goal.urlSparklineConversionRate}
- {'Goals_ConversionRate'|translate:"<strong>$conversion_rate</strong>"}</div>
- </div>
- <br class="clear" />
-</div>
+ <div id='leftcolumn'>
+ <div class="sparkline">{sparkline src=$goal.urlSparklineConversions}
+ {'Goals_Conversions'|translate:"<strong>$nb_conversions</strong>"}
+ {if $goal.goalAllowMultipleConversionsPerVisit}
+ ({'VisitsSummary_NbVisits'|translate:"<strong>$nb_visits_converted</strong>"})
+ {/if}
+ </div>
+ </div>
+ <div id='rightcolumn'>
+ <div class="sparkline">{sparkline src=$goal.urlSparklineConversionRate}
+ {'Goals_ConversionRate'|translate:"<strong>$conversion_rate</strong>"}</div>
+ </div>
+ <br class="clear"/>
+ </div>
{/foreach}
{if $displayFullReport}
-<h2 id='titleGoalsByDimension'>
-{if isset($idGoal)}
- {'Goals_GoalConversionsBy'|translate:$goalName}
-{else}
- {'Goals_ConversionsOverviewBy'|translate}
-{/if}
-</h2>
-
-{$goalReportsByDimension}
+ <h2 id='titleGoalsByDimension'>
+ {if isset($idGoal)}
+ {'Goals_GoalConversionsBy'|translate:$goalName}
+ {else}
+ {'Goals_ConversionsOverviewBy'|translate}
+ {/if}
+ </h2>
+ {$goalReportsByDimension}
- {if $userCanEditGoals}
- {include file="Goals/templates/add_edit_goal.tpl"}
- {/if}
+ {if $userCanEditGoals}
+ {include file="Goals/templates/add_edit_goal.tpl"}
+ {/if}
{/if}
diff --git a/plugins/Goals/templates/single_goal.tpl b/plugins/Goals/templates/single_goal.tpl
index a65d0a0cdd..741a310f6c 100644
--- a/plugins/Goals/templates/single_goal.tpl
+++ b/plugins/Goals/templates/single_goal.tpl
@@ -1,44 +1,47 @@
-<link rel="stylesheet" type="text/css" href="plugins/Goals/templates/goals.css" />
+<link rel="stylesheet" type="text/css" href="plugins/Goals/templates/goals.css"/>
{include file="Goals/templates/title_and_evolution_graph.tpl"}
- <div class="clear"></div>
- {if $nb_conversions > 0}
- <h2>{'Goals_ConversionsOverview'|translate}</h2>
- <ul class="ulGoalTopElements">
-{if !isset($ecommerce)}
- {if isset($topDimensions.country)}<li>{'Goals_BestCountries'|translate} {include file='Goals/templates/list_top_dimension.tpl' topDimension=$topDimensions.country}</li>{/if}
- {if isset($topDimensions.keyword) && count($topDimensions.keyword)>0}<li>{'Goals_BestKeywords'|translate} {include file='Goals/templates/list_top_dimension.tpl' topDimension=$topDimensions.keyword}</li>{/if}
- {if isset($topDimensions.website) && count($topDimensions.website)>0}<li>{'Goals_BestReferers'|translate} {include file='Goals/templates/list_top_dimension.tpl' topDimension=$topDimensions.website}</li>{/if}
- <li>{'Goals_ReturningVisitorsConversionRateIs'|translate:"<b>$conversion_rate_returning</b>"}, {'Goals_NewVisitorsConversionRateIs'|translate:"<b>$conversion_rate_new</b>"}</li>
-{else}
- <li>{'Live_GoalRevenue'|translate}: {$revenue|money:$idSite}{if !empty($revenue_subtotal)},
- {'General_Subtotal'|translate}: {$revenue_subtotal|money:$idSite}{/if}{if !empty($revenue_tax)},
- {'General_Tax'|translate}: {$revenue_tax|money:$idSite}{/if}{if !empty($revenue_shipping)},
- {'General_Shipping'|translate}: {$revenue_shipping|money:$idSite}{/if}{if !empty($revenue_discount)},
- {'General_Discount'|translate}: {$revenue_discount|money:$idSite}{/if}
- </li>
+<div class="clear"></div>
+{if $nb_conversions > 0}
+ <h2>{'Goals_ConversionsOverview'|translate}</h2>
+ <ul class="ulGoalTopElements">
+ {if !isset($ecommerce)}
+ {if isset($topDimensions.country)}
+ <li>{'Goals_BestCountries'|translate} {include file='Goals/templates/list_top_dimension.tpl' topDimension=$topDimensions.country}</li>{/if}
+ {if isset($topDimensions.keyword) && count($topDimensions.keyword)>0}
+ <li>{'Goals_BestKeywords'|translate} {include file='Goals/templates/list_top_dimension.tpl' topDimension=$topDimensions.keyword}</li>{/if}
+ {if isset($topDimensions.website) && count($topDimensions.website)>0}
+ <li>{'Goals_BestReferers'|translate} {include file='Goals/templates/list_top_dimension.tpl' topDimension=$topDimensions.website}</li>{/if}
+ <li>{'Goals_ReturningVisitorsConversionRateIs'|translate:"<b>$conversion_rate_returning</b>"}
+ , {'Goals_NewVisitorsConversionRateIs'|translate:"<b>$conversion_rate_new</b>"}</li>
+ {else}
+ <li>{'Live_GoalRevenue'|translate}: {$revenue|money:$idSite}{if !empty($revenue_subtotal)},
+ {'General_Subtotal'|translate}: {$revenue_subtotal|money:$idSite}{/if}{if !empty($revenue_tax)},
+ {'General_Tax'|translate}: {$revenue_tax|money:$idSite}{/if}{if !empty($revenue_shipping)},
+ {'General_Shipping'|translate}: {$revenue_shipping|money:$idSite}{/if}{if !empty($revenue_discount)},
+ {'General_Discount'|translate}: {$revenue_discount|money:$idSite}{/if}
+ </li>
+ {/if}
+ </ul>
{/if}
- </ul>
- {/if}
{literal}
-<script type="text/javascript">
-$(document).ready( function() {
- $('.goalTopElement').tooltip();
-});
-</script>
+ <script type="text/javascript">
+ $(document).ready(function () {
+ $('.goalTopElement').tooltip();
+ });
+ </script>
{/literal}
{if $displayFullReport}
- {if $nb_conversions > 0 || !empty($cart_nb_conversions)}
- <h2 id='titleGoalsByDimension'>
- {if isset($idGoal)}
- {'Goals_GoalConversionsBy'|translate:$goalName}
- {else}
- {'Goals_ConversionsOverviewBy'|translate}
- {/if}
- </h2>
-
- {$goalReportsByDimension}
- {/if}
+ {if $nb_conversions > 0 || !empty($cart_nb_conversions)}
+ <h2 id='titleGoalsByDimension'>
+ {if isset($idGoal)}
+ {'Goals_GoalConversionsBy'|translate:$goalName}
+ {else}
+ {'Goals_ConversionsOverviewBy'|translate}
+ {/if}
+ </h2>
+ {$goalReportsByDimension}
+ {/if}
{/if}
diff --git a/plugins/Goals/templates/title_and_evolution_graph.tpl b/plugins/Goals/templates/title_and_evolution_graph.tpl
index 6065ef5bfc..d45d149295 100644
--- a/plugins/Goals/templates/title_and_evolution_graph.tpl
+++ b/plugins/Goals/templates/title_and_evolution_graph.tpl
@@ -1,67 +1,70 @@
<a name="evolutionGraph" graphId="{$nameGraphEvolution}"></a>
{if $displayFullReport}
- <h2>{if isset($goalName)}{'Goals_GoalX'|translate:$goalName}{else}{'Goals_GoalsOverview'|translate}{/if}</h2>
+ <h2>{if isset($goalName)}{'Goals_GoalX'|translate:$goalName}{else}{'Goals_GoalsOverview'|translate}{/if}</h2>
{/if}
{$graphEvolution}
<div id='leftcolumn' {if !$isWidget}style='width:33%'{/if}>
- <div class="sparkline">{sparkline src=$urlSparklineConversions}
- {if isset($ecommerce)} <strong>{$nb_conversions}</strong> {'General_EcommerceOrders'|translate} <img src='themes/default/images/ecommerceOrder.gif'>
- {else}{'Goals_Conversions'|translate:"<strong>$nb_conversions</strong>"}
- {/if}
- {if isset($goalAllowMultipleConversionsPerVisit) && $goalAllowMultipleConversionsPerVisit}
- ({'VisitsSummary_NbVisits'|translate:"<strong>$nb_visits_converted</strong>"})
- {/if}
- </div>
- {if $revenue != 0 || isset($ecommerce)}
- <div class="sparkline">{sparkline src=$urlSparklineRevenue}
- {assign var=revenue value=$revenue|money:$idSite}
- {if isset($ecommerce)}<strong>{$revenue}</strong> {'General_TotalRevenue'|translate}
- {else}{'Goals_OverallRevenue'|translate:"<strong>$revenue</strong>"}
- {/if}
- </div>
- {/if}
- {if isset($ecommerce)}
- <div class="sparkline">{sparkline src=$urlSparklineAverageOrderValue}
- <strong>{$avg_order_revenue|money:$idSite}</strong> {'General_AverageOrderValue'|translate}</div>
- {/if}
-
+ <div class="sparkline">{sparkline src=$urlSparklineConversions}
+ {if isset($ecommerce)}
+ <strong>{$nb_conversions}</strong>
+ {'General_EcommerceOrders'|translate}
+ <img src='themes/default/images/ecommerceOrder.gif'>
+ {else}{'Goals_Conversions'|translate:"<strong>$nb_conversions</strong>"}
+ {/if}
+ {if isset($goalAllowMultipleConversionsPerVisit) && $goalAllowMultipleConversionsPerVisit}
+ ({'VisitsSummary_NbVisits'|translate:"<strong>$nb_visits_converted</strong>"})
+ {/if}
+ </div>
+ {if $revenue != 0 || isset($ecommerce)}
+ <div class="sparkline">{sparkline src=$urlSparklineRevenue}
+ {assign var=revenue value=$revenue|money:$idSite}
+ {if isset($ecommerce)}<strong>{$revenue}</strong> {'General_TotalRevenue'|translate}
+ {else}{'Goals_OverallRevenue'|translate:"<strong>$revenue</strong>"}
+ {/if}
+ </div>
+ {/if}
+ {if isset($ecommerce)}
+ <div class="sparkline">{sparkline src=$urlSparklineAverageOrderValue}
+ <strong>{$avg_order_revenue|money:$idSite}</strong> {'General_AverageOrderValue'|translate}</div>
+ {/if}
+
</div>
<div id='leftcolumn' {if !$isWidget}style='width:33%'{/if}>
- <div class="sparkline">{sparkline src=$urlSparklineConversionRate}
- {if isset($ecommerce)}{capture assign='ecommerceOrdersText'}{'General_EcommerceOrders'|translate}{/capture}
- {'Goals_ConversionRate'|translate:"<strong>$conversion_rate</strong> $ecommerceOrdersText"}
- {else}
- {'Goals_OverallConversionRate'|translate:"<strong>$conversion_rate</strong>"}
- {/if}
- </div>
- {if isset($ecommerce)}
- <div class="sparkline">{sparkline src=$urlSparklinePurchasedProducts}
- <strong>{$items}</strong> {'General_PurchasedProducts'|translate}</div>
- {/if}
+ <div class="sparkline">{sparkline src=$urlSparklineConversionRate}
+ {if isset($ecommerce)}{capture assign='ecommerceOrdersText'}{'General_EcommerceOrders'|translate}{/capture}
+ {'Goals_ConversionRate'|translate:"<strong>$conversion_rate</strong> $ecommerceOrdersText"}
+ {else}
+ {'Goals_OverallConversionRate'|translate:"<strong>$conversion_rate</strong>"}
+ {/if}
+ </div>
+ {if isset($ecommerce)}
+ <div class="sparkline">{sparkline src=$urlSparklinePurchasedProducts}
+ <strong>{$items}</strong> {'General_PurchasedProducts'|translate}</div>
+ {/if}
</div>
{if isset($ecommerce)}
-<div id='rightcolumn' {if !$isWidget}style='width:30%'{/if}>
- <div>
- <img src='themes/default/images/ecommerceAbandonedCart.gif'> <i>{'General_AbandonedCarts'|translate}</i>
- </div>
-
- <div class="sparkline">{sparkline src=$cart_urlSparklineConversions}
- {capture assign='ecommerceAbandonedCartsText'}{'Goals_AbandonedCart'|translate}{/capture}
- <strong>{$cart_nb_conversions}</strong> {'General_VisitsWith'|translate:$ecommerceAbandonedCartsText}
- </div>
-
- <div class="sparkline">{sparkline src=$cart_urlSparklineRevenue}
- {capture assign=revenue}{$cart_revenue|money:$idSite}{/capture}
- {capture assign=revenueText}{'Live_GoalRevenue'|translate}{/capture}
- <strong>{$revenue}</strong> {'Goals_LeftInCart'|translate:$revenueText}
- </div>
-
- <div class="sparkline">{sparkline src=$cart_urlSparklineConversionRate}
- <strong>{$cart_conversion_rate}</strong> {'General_VisitsWith'|translate:$ecommerceAbandonedCartsText}
- </div>
-</div>
+ <div id='rightcolumn' {if !$isWidget}style='width:30%'{/if}>
+ <div>
+ <img src='themes/default/images/ecommerceAbandonedCart.gif'> <i>{'General_AbandonedCarts'|translate}</i>
+ </div>
+
+ <div class="sparkline">{sparkline src=$cart_urlSparklineConversions}
+ {capture assign='ecommerceAbandonedCartsText'}{'Goals_AbandonedCart'|translate}{/capture}
+ <strong>{$cart_nb_conversions}</strong> {'General_VisitsWith'|translate:$ecommerceAbandonedCartsText}
+ </div>
+
+ <div class="sparkline">{sparkline src=$cart_urlSparklineRevenue}
+ {capture assign=revenue}{$cart_revenue|money:$idSite}{/capture}
+ {capture assign=revenueText}{'Live_GoalRevenue'|translate}{/capture}
+ <strong>{$revenue}</strong> {'Goals_LeftInCart'|translate:$revenueText}
+ </div>
+
+ <div class="sparkline">{sparkline src=$cart_urlSparklineConversionRate}
+ <strong>{$cart_conversion_rate}</strong> {'General_VisitsWith'|translate:$ecommerceAbandonedCartsText}
+ </div>
+ </div>
{/if}
{include file="CoreHome/templates/sparkline_footer.tpl"}
diff --git a/plugins/ImageGraph/API.php b/plugins/ImageGraph/API.php
index d1d5bace51..c4a16297b4 100644
--- a/plugins/ImageGraph/API.php
+++ b/plugins/ImageGraph/API.php
@@ -1,7 +1,7 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
@@ -24,562 +24,497 @@
*/
class Piwik_ImageGraph_API
{
- const FILENAME_KEY = 'filename';
- const TRUNCATE_KEY = 'truncate';
- const WIDTH_KEY = 'width';
- const HEIGHT_KEY = 'height';
- const MAX_WIDTH = 2048;
- const MAX_HEIGHT = 2048;
-
- static private $DEFAULT_PARAMETERS = array(
- Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_BASIC_LINE => array(
- self::FILENAME_KEY => 'BasicLine',
- self::TRUNCATE_KEY => 6,
- self::WIDTH_KEY => 1044,
- self::HEIGHT_KEY => 290,
- ),
- Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_VERTICAL_BAR => array(
- self::FILENAME_KEY => 'BasicBar',
- self::TRUNCATE_KEY => 6,
- self::WIDTH_KEY => 1044,
- self::HEIGHT_KEY => 290,
- ),
- Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_HORIZONTAL_BAR => array(
- self::FILENAME_KEY => 'HorizontalBar',
- self::TRUNCATE_KEY => null, // horizontal bar graphs are dynamically truncated
- self::WIDTH_KEY => 800,
- self::HEIGHT_KEY => 290,
- ),
- Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_3D_PIE => array(
- self::FILENAME_KEY => '3DPie',
- self::TRUNCATE_KEY => 5,
- self::WIDTH_KEY => 1044,
- self::HEIGHT_KEY => 290,
- ),
- Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_BASIC_PIE => array(
- self::FILENAME_KEY => 'BasicPie',
- self::TRUNCATE_KEY => 5,
- self::WIDTH_KEY => 1044,
- self::HEIGHT_KEY => 290,
- ),
- );
-
- static private $DEFAULT_GRAPH_TYPE_OVERRIDE = array(
- 'UserSettings_getPlugin' => array(
- false // override if !$isMultiplePeriod
- => Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_HORIZONTAL_BAR,
- ),
- 'Referers_getRefererType' => array(
- false // override if !$isMultiplePeriod
- => Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_HORIZONTAL_BAR,
- ),
- );
-
- const GRAPH_OUTPUT_INLINE = 0;
- const GRAPH_OUTPUT_FILE = 1;
- const GRAPH_OUTPUT_PHP = 2;
-
- const DEFAULT_ORDINATE_METRIC = 'nb_visits';
- const FONT_DIR = '/plugins/ImageGraph/fonts/';
- const DEFAULT_FONT = 'tahoma.ttf';
- const UNICODE_FONT = 'unifont.ttf';
- const DEFAULT_FONT_SIZE = 9;
- const DEFAULT_LEGEND_FONT_SIZE_OFFSET = 2;
-
- // number of row evolutions to plot when no labels are specified, can be overridden using &filter_limit
- const DEFAULT_NB_ROW_EVOLUTIONS = 5;
- const MAX_NB_ROW_LABELS = 10;
-
- static private $instance = null;
-
- /**
- * @return Piwik_ImageGraph_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- $c = __CLASS__;
- self::$instance = new $c();
- }
- return self::$instance;
- }
-
- public function get($idSite, $period, $date, $apiModule, $apiAction, $graphType = false,
- $outputType = Piwik_ImageGraph_API::GRAPH_OUTPUT_INLINE, $columns = false, $labels = false, $showLegend = true,
- $width = false, $height = false, $fontSize = Piwik_ImageGraph_API::DEFAULT_FONT_SIZE, $legendFontSize = false,
- $aliasedGraph = true, $idGoal = false, $colors = false, $idSubtable = false, $legendAppendMetric = true)
- {
- Piwik::checkUserHasViewAccess($idSite);
-
- // Health check - should we also test for GD2 only?
- if(!Piwik::isGdExtensionEnabled())
- {
- throw new Exception('Error: To create graphs in Piwik, please enable GD php extension (with Freetype support) in php.ini, and restart your web server.');
- }
-
- $useUnicodeFont = array(
- 'am', 'ar', 'el', 'fa' , 'fi', 'he', 'ja', 'ka', 'ko', 'te', 'th', 'zh-cn', 'zh-tw',
- );
- $languageLoaded = Piwik_Translate::getInstance()->getLanguageLoaded();
- $font = self::getFontPath(self::DEFAULT_FONT);
- if(in_array($languageLoaded, $useUnicodeFont))
- {
- $unicodeFontPath = self::getFontPath(self::UNICODE_FONT);
- $font = file_exists($unicodeFontPath) ? $unicodeFontPath : $font;
- }
-
- // save original GET to reset after processing. Important for API-in-API-call
- $savedGET = $_GET;
-
- try
- {
- $apiParameters = array();
- if(!empty($idGoal)) {
- $apiParameters = array( 'idGoal' => $idGoal);
- }
- // Fetch the metadata for given api-action
- $metadata = Piwik_API_API::getInstance()->getMetadata(
- $idSite, $apiModule, $apiAction, $apiParameters, $languageLoaded, $period, $date,
- $hideMetricsDoc = false, $showSubtableReports = true);
- if(!$metadata)
- {
- throw new Exception('Invalid API Module and/or API Action');
- }
-
- $metadata = $metadata[0];
- $reportHasDimension = !empty($metadata['dimension']);
- $constantRowsCount = !empty($metadata['constantRowsCount']);
-
- $isMultiplePeriod = Piwik_Archive::isMultiplePeriod($date, $period);
- if(!$reportHasDimension && !$isMultiplePeriod)
- {
- throw new Exception('The graph cannot be drawn for this combination of \'date\' and \'period\' parameters.');
- }
-
- if(empty($legendFontSize))
- {
- $legendFontSize = (int)$fontSize + self::DEFAULT_LEGEND_FONT_SIZE_OFFSET;
- }
-
- if(empty($graphType))
- {
- if($isMultiplePeriod)
- {
- $graphType = Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_BASIC_LINE;
- }
- else
- {
- if($constantRowsCount)
- {
- $graphType = Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_VERTICAL_BAR;
- }
- else
- {
- $graphType = Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_HORIZONTAL_BAR;
- }
- }
-
- $reportUniqueId = $metadata['uniqueId'];
- if(isset(self::$DEFAULT_GRAPH_TYPE_OVERRIDE[$reportUniqueId][$isMultiplePeriod]))
- {
- $graphType = self::$DEFAULT_GRAPH_TYPE_OVERRIDE[$reportUniqueId][$isMultiplePeriod];
- }
- }
- else
- {
- $availableGraphTypes = Piwik_ImageGraph_StaticGraph::getAvailableStaticGraphTypes();
- if (!in_array($graphType, $availableGraphTypes))
- {
- throw new Exception(
- Piwik_TranslateException(
- 'General_ExceptionInvalidStaticGraphType',
- array($graphType, implode(', ', $availableGraphTypes))
- )
- );
- }
- }
-
- $width = (int)$width;
- $height = (int)$height;
- if(empty($width))
- {
- $width = self::$DEFAULT_PARAMETERS[$graphType][self::WIDTH_KEY];
- }
- if(empty($height))
- {
- $height = self::$DEFAULT_PARAMETERS[$graphType][self::HEIGHT_KEY];
- }
-
- // Cap width and height to a safe amount
- $width = min($width, self::MAX_WIDTH);
- $height = min($height, self::MAX_HEIGHT);
-
- $reportColumns = array_merge(
- !empty($metadata['metrics']) ? $metadata['metrics'] : array(),
- !empty($metadata['processedMetrics']) ? $metadata['processedMetrics'] : array(),
- !empty($metadata['metricsGoal']) ? $metadata['metricsGoal'] : array(),
- !empty($metadata['processedMetricsGoal']) ? $metadata['processedMetricsGoal'] : array()
- );
-
- $ordinateColumns = array();
- if(empty($columns))
- {
- $ordinateColumns[] =
- empty($reportColumns[self::DEFAULT_ORDINATE_METRIC]) ? key($metadata['metrics']) : self::DEFAULT_ORDINATE_METRIC;
- }
- else
- {
- $ordinateColumns = explode(',', $columns);
- foreach($ordinateColumns as $column)
- {
- if(empty($reportColumns[$column]))
- {
- throw new Exception(
- Piwik_Translate(
- 'ImageGraph_ColumnOrdinateMissing',
- array($column, implode(',',array_keys($reportColumns)))
- )
- );
- }
- }
- }
-
- $ordinateLabels = array();
- foreach($ordinateColumns as $column)
- {
- $ordinateLabels[$column] = $reportColumns[$column];
- }
-
- // sort and truncate filters
- $defaultFilterTruncate = self::$DEFAULT_PARAMETERS[$graphType][self::TRUNCATE_KEY];
- switch($graphType)
- {
- case Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_3D_PIE:
- case Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_BASIC_PIE:
-
- if(count($ordinateColumns) > 1)
- {
- // pChart doesn't support multiple series on pie charts
- throw new Exception("Pie charts do not currently support multiple series");
- }
-
- $_GET['filter_sort_column'] = reset($ordinateColumns);
- $this->setFilterTruncate($defaultFilterTruncate);
- break;
-
- case Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_VERTICAL_BAR:
- case Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_BASIC_LINE:
-
- if(!$isMultiplePeriod && !$constantRowsCount)
- {
- $this->setFilterTruncate($defaultFilterTruncate);
- }
- break;
- }
-
- $ordinateLogos = array();
-
- // row evolutions
- if($isMultiplePeriod && $reportHasDimension)
- {
- $plottedMetric = reset($ordinateColumns);
-
- // when no labels are specified, getRowEvolution returns the top N=filter_limit row evolutions
- // rows are sorted using filter_sort_column (see Piwik_API_DataTableGenericFilter for more info)
- if(!$labels)
- {
- $savedFilterSortColumnValue = Piwik_Common::getRequestVar('filter_sort_column', '');
- $_GET['filter_sort_column'] = $plottedMetric;
-
- $savedFilterLimitValue = Piwik_Common::getRequestVar('filter_limit', -1, 'int');
- if($savedFilterLimitValue == -1 || $savedFilterLimitValue > self::MAX_NB_ROW_LABELS)
- {
- $_GET['filter_limit'] = self::DEFAULT_NB_ROW_EVOLUTIONS;
- }
- }
-
- $processedReport = Piwik_API_API::getInstance()->getRowEvolution(
- $idSite,
- $period,
- $date,
- $apiModule,
- $apiAction,
- $labels,
- $segment = false,
- $plottedMetric,
- $languageLoaded,
- $idGoal,
- $legendAppendMetric,
- $labelUseAbsoluteUrl = false
- );
-
- //@review this test will need to be updated after evaluating the @review comment in API/API.php
- if(!$processedReport)
- {
- throw new Exception(Piwik_Translate('General_NoDataForGraph_js'));
- }
-
- // restoring generic filter parameters
- if(!$labels)
- {
- $_GET['filter_sort_column'] = $savedFilterSortColumnValue;
- if($savedFilterLimitValue != -1)
- {
- $_GET['filter_limit'] = $savedFilterLimitValue;
- }
- }
-
- // retrieve metric names & labels
- $metrics = $processedReport['metadata']['metrics'];
- $ordinateLabels = array();
-
- // getRowEvolution returned more than one label
- if(!array_key_exists($plottedMetric, $metrics))
- {
- $ordinateColumns = array();
- $i = 0;
- foreach($metrics as $metric => $info)
- {
- $ordinateColumn = $plottedMetric . '_' . $i++;
- $ordinateColumns[] = $metric;
- $ordinateLabels[$ordinateColumn] = $info['name'];
-
- if(isset($info['logo']))
- {
- $ordinateLogo = $info['logo'];
-
- // @review pChart does not support gifs in graph legends, would it be possible to convert all plugin pictures (cookie.gif, flash.gif, ..) to png files?
- if(!strstr($ordinateLogo, '.gif'))
- {
- $absoluteLogoPath = self::getAbsoluteLogoPath($ordinateLogo);
- if(file_exists($absoluteLogoPath))
- {
- $ordinateLogos[$ordinateColumn] = $absoluteLogoPath;
- }
- }
- }
- }
- }
- else
- {
- $ordinateLabels[$plottedMetric] = $processedReport['label'] . ' (' . $metrics[$plottedMetric]['name'] . ')';
- }
- }
- else
- {
- $processedReport = Piwik_API_API::getInstance()->getProcessedReport(
- $idSite,
- $period,
- $date,
- $apiModule,
- $apiAction,
- $segment = false,
- $apiParameters = false,
- $idGoal,
- $languageLoaded,
- $showTimer = true,
- $hideMetricsDoc = false,
- $idSubtable,
- $showRawMetrics = false
- );
- }
- // prepare abscissa and ordinate series
- $abscissaSeries = array();
- $abscissaLogos = array();
- $ordinateSeries = array();
- $reportData = $processedReport['reportData'];
- $hasData = false;
- $hasNonZeroValue = false;
-
- if(!$isMultiplePeriod)
- {
- $reportMetadata = $processedReport['reportMetadata']->getRows();
-
- $i = 0;
- // $reportData instanceof Piwik_DataTable
- foreach($reportData->getRows() as $row) // Piwik_DataTable_Row[]
- {
- // $row instanceof Piwik_DataTable_Row
- $rowData = $row->getColumns(); // Associative Array
- $abscissaSeries[] = Piwik_Common::unsanitizeInputValue($rowData['label']);
-
- foreach($ordinateColumns as $column)
- {
- $parsedOrdinateValue = $this->parseOrdinateValue($rowData[$column]);
- $hasData = true;
-
- if($parsedOrdinateValue != 0)
- {
- $hasNonZeroValue = true;
- }
- $ordinateSeries[$column][] = $parsedOrdinateValue;
- }
-
- if(isset($reportMetadata[$i]))
- {
- $rowMetadata = $reportMetadata[$i]->getColumns();
- if(isset($rowMetadata['logo']))
- {
- $absoluteLogoPath = self::getAbsoluteLogoPath($rowMetadata['logo']);
- if(file_exists($absoluteLogoPath))
- {
- $abscissaLogos[$i] = $absoluteLogoPath;
- }
- }
- }
- $i++;
- }
- }
- else // if the report has no dimension we have multiple reports each with only one row within the reportData
- {
- // $periodsData instanceof Piwik_DataTable_Simple[]
- $periodsData = array_values($reportData->getArray());
- $periodsCount = count($periodsData);
-
- for ($i = 0 ; $i < $periodsCount ; $i++)
- {
- // $periodsData[$i] instanceof Piwik_DataTable_Simple
- // $rows instanceof Piwik_DataTable_Row[]
- if(empty($periodsData[$i]))
- {
- continue;
- }
- $rows = $periodsData[$i]->getRows();
-
- if(array_key_exists(0, $rows))
- {
- $rowData = $rows[0]->getColumns(); // associative Array
-
- foreach($ordinateColumns as $column)
- {
- $ordinateValue = $rowData[$column];
- $parsedOrdinateValue = $this->parseOrdinateValue($ordinateValue);
-
- $hasData = true;
-
- if(!empty($parsedOrdinateValue))
- {
- $hasNonZeroValue = true;
- }
-
- $ordinateSeries[$column][] = $parsedOrdinateValue;
- }
-
- }
- else
- {
- foreach($ordinateColumns as $column)
- {
- $ordinateSeries[$column][] = 0;
- }
- }
-
- $rowId = $periodsData[$i]->metadata['period']->getLocalizedShortString();
- $abscissaSeries[] = Piwik_Common::unsanitizeInputValue($rowId);
- }
- }
-
- if(!$hasData || !$hasNonZeroValue)
- {
- throw new Exception(Piwik_Translate('General_NoDataForGraph_js'));
- }
-
- //Setup the graph
- $graph = Piwik_ImageGraph_StaticGraph::factory($graphType);
- $graph->setWidth($width);
- $graph->setHeight($height);
- $graph->setFont($font);
- $graph->setFontSize($fontSize);
- $graph->setLegendFontSize($legendFontSize);
- $graph->setOrdinateLabels($ordinateLabels);
- $graph->setShowLegend($showLegend);
- $graph->setAliasedGraph($aliasedGraph);
- $graph->setAbscissaSeries($abscissaSeries);
- $graph->setAbscissaLogos($abscissaLogos);
- $graph->setOrdinateSeries($ordinateSeries);
- $graph->setOrdinateLogos($ordinateLogos);
- $graph->setColors(!empty($colors) ? explode(',', $colors) : array());
- if($period == 'day')
- {
- $graph->setForceSkippedLabels(6);
- }
-
- // render graph
- $graph->renderGraph();
-
- } catch (Exception $e) {
-
- $graph = new Piwik_ImageGraph_StaticGraph_Exception();
- $graph->setWidth($width);
- $graph->setHeight($height);
- $graph->setFont($font);
- $graph->setFontSize($fontSize);
- $graph->setException($e);
- $graph->renderGraph();
- }
-
- // restoring get parameters
- $_GET = $savedGET;
-
- switch($outputType)
- {
- case self::GRAPH_OUTPUT_FILE:
- if($idGoal != '')
- {
- $idGoal = '_' . $idGoal;
- }
- $fileName = self::$DEFAULT_PARAMETERS[$graphType][self::FILENAME_KEY] . '_' . $apiModule . '_' . $apiAction . $idGoal . ' ' . str_replace(',', '-', $date) . ' ' . $idSite . '.png';
- $fileName = str_replace(array(' ','/'), '_', $fileName);
-
- if(!Piwik_Common::isValidFilename($fileName))
- {
- throw new Exception('Error: Image graph filename ' . $fileName . ' is not valid.');
- }
-
- return $graph->sendToDisk($fileName);
-
- case self::GRAPH_OUTPUT_PHP:
- return $graph->getRenderedImage();
-
- case self::GRAPH_OUTPUT_INLINE:
- default:
- $graph->sendToBrowser();
- exit;
- }
- }
-
- private function setFilterTruncate($default)
- {
- $_GET['filter_truncate'] = Piwik_Common::getRequestVar('filter_truncate', $default, 'int');
- }
-
- private static function parseOrdinateValue($ordinateValue)
- {
- $ordinateValue = @str_replace(',', '.', $ordinateValue);
-
- // convert hh:mm:ss formatted time values to number of seconds
- if(preg_match('/([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})/', $ordinateValue, $matches))
- {
- $hour = $matches[1];
- $min = $matches[2];
- $sec = $matches[3];
-
- $ordinateValue = ($hour * 3600) + ($min * 60) + $sec;
- }
-
- // OK, only numbers from here please (strip out currency sign)
- $ordinateValue = preg_replace('/[^0-9.]/', '', $ordinateValue);
- return $ordinateValue;
- }
-
- private static function getFontPath($font)
- {
- return PIWIK_INCLUDE_PATH . self::FONT_DIR . $font;
- }
-
- protected static function getAbsoluteLogoPath($relativeLogoPath)
- {
- return PIWIK_INCLUDE_PATH . '/' . $relativeLogoPath;
- }
+ const FILENAME_KEY = 'filename';
+ const TRUNCATE_KEY = 'truncate';
+ const WIDTH_KEY = 'width';
+ const HEIGHT_KEY = 'height';
+ const MAX_WIDTH = 2048;
+ const MAX_HEIGHT = 2048;
+
+ static private $DEFAULT_PARAMETERS = array(
+ Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_BASIC_LINE => array(
+ self::FILENAME_KEY => 'BasicLine',
+ self::TRUNCATE_KEY => 6,
+ self::WIDTH_KEY => 1044,
+ self::HEIGHT_KEY => 290,
+ ),
+ Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_VERTICAL_BAR => array(
+ self::FILENAME_KEY => 'BasicBar',
+ self::TRUNCATE_KEY => 6,
+ self::WIDTH_KEY => 1044,
+ self::HEIGHT_KEY => 290,
+ ),
+ Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_HORIZONTAL_BAR => array(
+ self::FILENAME_KEY => 'HorizontalBar',
+ self::TRUNCATE_KEY => null, // horizontal bar graphs are dynamically truncated
+ self::WIDTH_KEY => 800,
+ self::HEIGHT_KEY => 290,
+ ),
+ Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_3D_PIE => array(
+ self::FILENAME_KEY => '3DPie',
+ self::TRUNCATE_KEY => 5,
+ self::WIDTH_KEY => 1044,
+ self::HEIGHT_KEY => 290,
+ ),
+ Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_BASIC_PIE => array(
+ self::FILENAME_KEY => 'BasicPie',
+ self::TRUNCATE_KEY => 5,
+ self::WIDTH_KEY => 1044,
+ self::HEIGHT_KEY => 290,
+ ),
+ );
+
+ static private $DEFAULT_GRAPH_TYPE_OVERRIDE = array(
+ 'UserSettings_getPlugin' => array(
+ false // override if !$isMultiplePeriod
+ => Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_HORIZONTAL_BAR,
+ ),
+ 'Referers_getRefererType' => array(
+ false // override if !$isMultiplePeriod
+ => Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_HORIZONTAL_BAR,
+ ),
+ );
+
+ const GRAPH_OUTPUT_INLINE = 0;
+ const GRAPH_OUTPUT_FILE = 1;
+ const GRAPH_OUTPUT_PHP = 2;
+
+ const DEFAULT_ORDINATE_METRIC = 'nb_visits';
+ const FONT_DIR = '/plugins/ImageGraph/fonts/';
+ const DEFAULT_FONT = 'tahoma.ttf';
+ const UNICODE_FONT = 'unifont.ttf';
+ const DEFAULT_FONT_SIZE = 9;
+ const DEFAULT_LEGEND_FONT_SIZE_OFFSET = 2;
+
+ // number of row evolutions to plot when no labels are specified, can be overridden using &filter_limit
+ const DEFAULT_NB_ROW_EVOLUTIONS = 5;
+ const MAX_NB_ROW_LABELS = 10;
+
+ static private $instance = null;
+
+ /**
+ * @return Piwik_ImageGraph_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ return self::$instance;
+ }
+
+ public function get($idSite, $period, $date, $apiModule, $apiAction, $graphType = false,
+ $outputType = Piwik_ImageGraph_API::GRAPH_OUTPUT_INLINE, $columns = false, $labels = false, $showLegend = true,
+ $width = false, $height = false, $fontSize = Piwik_ImageGraph_API::DEFAULT_FONT_SIZE, $legendFontSize = false,
+ $aliasedGraph = true, $idGoal = false, $colors = false, $idSubtable = false, $legendAppendMetric = true)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+
+ // Health check - should we also test for GD2 only?
+ if (!Piwik::isGdExtensionEnabled()) {
+ throw new Exception('Error: To create graphs in Piwik, please enable GD php extension (with Freetype support) in php.ini, and restart your web server.');
+ }
+
+ $useUnicodeFont = array(
+ 'am', 'ar', 'el', 'fa', 'fi', 'he', 'ja', 'ka', 'ko', 'te', 'th', 'zh-cn', 'zh-tw',
+ );
+ $languageLoaded = Piwik_Translate::getInstance()->getLanguageLoaded();
+ $font = self::getFontPath(self::DEFAULT_FONT);
+ if (in_array($languageLoaded, $useUnicodeFont)) {
+ $unicodeFontPath = self::getFontPath(self::UNICODE_FONT);
+ $font = file_exists($unicodeFontPath) ? $unicodeFontPath : $font;
+ }
+
+ // save original GET to reset after processing. Important for API-in-API-call
+ $savedGET = $_GET;
+
+ try {
+ $apiParameters = array();
+ if (!empty($idGoal)) {
+ $apiParameters = array('idGoal' => $idGoal);
+ }
+ // Fetch the metadata for given api-action
+ $metadata = Piwik_API_API::getInstance()->getMetadata(
+ $idSite, $apiModule, $apiAction, $apiParameters, $languageLoaded, $period, $date,
+ $hideMetricsDoc = false, $showSubtableReports = true);
+ if (!$metadata) {
+ throw new Exception('Invalid API Module and/or API Action');
+ }
+
+ $metadata = $metadata[0];
+ $reportHasDimension = !empty($metadata['dimension']);
+ $constantRowsCount = !empty($metadata['constantRowsCount']);
+
+ $isMultiplePeriod = Piwik_Archive::isMultiplePeriod($date, $period);
+ if (!$reportHasDimension && !$isMultiplePeriod) {
+ throw new Exception('The graph cannot be drawn for this combination of \'date\' and \'period\' parameters.');
+ }
+
+ if (empty($legendFontSize)) {
+ $legendFontSize = (int)$fontSize + self::DEFAULT_LEGEND_FONT_SIZE_OFFSET;
+ }
+
+ if (empty($graphType)) {
+ if ($isMultiplePeriod) {
+ $graphType = Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_BASIC_LINE;
+ } else {
+ if ($constantRowsCount) {
+ $graphType = Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_VERTICAL_BAR;
+ } else {
+ $graphType = Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_HORIZONTAL_BAR;
+ }
+ }
+
+ $reportUniqueId = $metadata['uniqueId'];
+ if (isset(self::$DEFAULT_GRAPH_TYPE_OVERRIDE[$reportUniqueId][$isMultiplePeriod])) {
+ $graphType = self::$DEFAULT_GRAPH_TYPE_OVERRIDE[$reportUniqueId][$isMultiplePeriod];
+ }
+ } else {
+ $availableGraphTypes = Piwik_ImageGraph_StaticGraph::getAvailableStaticGraphTypes();
+ if (!in_array($graphType, $availableGraphTypes)) {
+ throw new Exception(
+ Piwik_TranslateException(
+ 'General_ExceptionInvalidStaticGraphType',
+ array($graphType, implode(', ', $availableGraphTypes))
+ )
+ );
+ }
+ }
+
+ $width = (int)$width;
+ $height = (int)$height;
+ if (empty($width)) {
+ $width = self::$DEFAULT_PARAMETERS[$graphType][self::WIDTH_KEY];
+ }
+ if (empty($height)) {
+ $height = self::$DEFAULT_PARAMETERS[$graphType][self::HEIGHT_KEY];
+ }
+
+ // Cap width and height to a safe amount
+ $width = min($width, self::MAX_WIDTH);
+ $height = min($height, self::MAX_HEIGHT);
+
+ $reportColumns = array_merge(
+ !empty($metadata['metrics']) ? $metadata['metrics'] : array(),
+ !empty($metadata['processedMetrics']) ? $metadata['processedMetrics'] : array(),
+ !empty($metadata['metricsGoal']) ? $metadata['metricsGoal'] : array(),
+ !empty($metadata['processedMetricsGoal']) ? $metadata['processedMetricsGoal'] : array()
+ );
+
+ $ordinateColumns = array();
+ if (empty($columns)) {
+ $ordinateColumns[] =
+ empty($reportColumns[self::DEFAULT_ORDINATE_METRIC]) ? key($metadata['metrics']) : self::DEFAULT_ORDINATE_METRIC;
+ } else {
+ $ordinateColumns = explode(',', $columns);
+ foreach ($ordinateColumns as $column) {
+ if (empty($reportColumns[$column])) {
+ throw new Exception(
+ Piwik_Translate(
+ 'ImageGraph_ColumnOrdinateMissing',
+ array($column, implode(',', array_keys($reportColumns)))
+ )
+ );
+ }
+ }
+ }
+
+ $ordinateLabels = array();
+ foreach ($ordinateColumns as $column) {
+ $ordinateLabels[$column] = $reportColumns[$column];
+ }
+
+ // sort and truncate filters
+ $defaultFilterTruncate = self::$DEFAULT_PARAMETERS[$graphType][self::TRUNCATE_KEY];
+ switch ($graphType) {
+ case Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_3D_PIE:
+ case Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_BASIC_PIE:
+
+ if (count($ordinateColumns) > 1) {
+ // pChart doesn't support multiple series on pie charts
+ throw new Exception("Pie charts do not currently support multiple series");
+ }
+
+ $_GET['filter_sort_column'] = reset($ordinateColumns);
+ $this->setFilterTruncate($defaultFilterTruncate);
+ break;
+
+ case Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_VERTICAL_BAR:
+ case Piwik_ImageGraph_StaticGraph::GRAPH_TYPE_BASIC_LINE:
+
+ if (!$isMultiplePeriod && !$constantRowsCount) {
+ $this->setFilterTruncate($defaultFilterTruncate);
+ }
+ break;
+ }
+
+ $ordinateLogos = array();
+
+ // row evolutions
+ if ($isMultiplePeriod && $reportHasDimension) {
+ $plottedMetric = reset($ordinateColumns);
+
+ // when no labels are specified, getRowEvolution returns the top N=filter_limit row evolutions
+ // rows are sorted using filter_sort_column (see Piwik_API_DataTableGenericFilter for more info)
+ if (!$labels) {
+ $savedFilterSortColumnValue = Piwik_Common::getRequestVar('filter_sort_column', '');
+ $_GET['filter_sort_column'] = $plottedMetric;
+
+ $savedFilterLimitValue = Piwik_Common::getRequestVar('filter_limit', -1, 'int');
+ if ($savedFilterLimitValue == -1 || $savedFilterLimitValue > self::MAX_NB_ROW_LABELS) {
+ $_GET['filter_limit'] = self::DEFAULT_NB_ROW_EVOLUTIONS;
+ }
+ }
+
+ $processedReport = Piwik_API_API::getInstance()->getRowEvolution(
+ $idSite,
+ $period,
+ $date,
+ $apiModule,
+ $apiAction,
+ $labels,
+ $segment = false,
+ $plottedMetric,
+ $languageLoaded,
+ $idGoal,
+ $legendAppendMetric,
+ $labelUseAbsoluteUrl = false
+ );
+
+ //@review this test will need to be updated after evaluating the @review comment in API/API.php
+ if (!$processedReport) {
+ throw new Exception(Piwik_Translate('General_NoDataForGraph_js'));
+ }
+
+ // restoring generic filter parameters
+ if (!$labels) {
+ $_GET['filter_sort_column'] = $savedFilterSortColumnValue;
+ if ($savedFilterLimitValue != -1) {
+ $_GET['filter_limit'] = $savedFilterLimitValue;
+ }
+ }
+
+ // retrieve metric names & labels
+ $metrics = $processedReport['metadata']['metrics'];
+ $ordinateLabels = array();
+
+ // getRowEvolution returned more than one label
+ if (!array_key_exists($plottedMetric, $metrics)) {
+ $ordinateColumns = array();
+ $i = 0;
+ foreach ($metrics as $metric => $info) {
+ $ordinateColumn = $plottedMetric . '_' . $i++;
+ $ordinateColumns[] = $metric;
+ $ordinateLabels[$ordinateColumn] = $info['name'];
+
+ if (isset($info['logo'])) {
+ $ordinateLogo = $info['logo'];
+
+ // @review pChart does not support gifs in graph legends, would it be possible to convert all plugin pictures (cookie.gif, flash.gif, ..) to png files?
+ if (!strstr($ordinateLogo, '.gif')) {
+ $absoluteLogoPath = self::getAbsoluteLogoPath($ordinateLogo);
+ if (file_exists($absoluteLogoPath)) {
+ $ordinateLogos[$ordinateColumn] = $absoluteLogoPath;
+ }
+ }
+ }
+ }
+ } else {
+ $ordinateLabels[$plottedMetric] = $processedReport['label'] . ' (' . $metrics[$plottedMetric]['name'] . ')';
+ }
+ } else {
+ $processedReport = Piwik_API_API::getInstance()->getProcessedReport(
+ $idSite,
+ $period,
+ $date,
+ $apiModule,
+ $apiAction,
+ $segment = false,
+ $apiParameters = false,
+ $idGoal,
+ $languageLoaded,
+ $showTimer = true,
+ $hideMetricsDoc = false,
+ $idSubtable,
+ $showRawMetrics = false
+ );
+ }
+ // prepare abscissa and ordinate series
+ $abscissaSeries = array();
+ $abscissaLogos = array();
+ $ordinateSeries = array();
+ $reportData = $processedReport['reportData'];
+ $hasData = false;
+ $hasNonZeroValue = false;
+
+ if (!$isMultiplePeriod) {
+ $reportMetadata = $processedReport['reportMetadata']->getRows();
+
+ $i = 0;
+ // $reportData instanceof Piwik_DataTable
+ foreach ($reportData->getRows() as $row) // Piwik_DataTable_Row[]
+ {
+ // $row instanceof Piwik_DataTable_Row
+ $rowData = $row->getColumns(); // Associative Array
+ $abscissaSeries[] = Piwik_Common::unsanitizeInputValue($rowData['label']);
+
+ foreach ($ordinateColumns as $column) {
+ $parsedOrdinateValue = $this->parseOrdinateValue($rowData[$column]);
+ $hasData = true;
+
+ if ($parsedOrdinateValue != 0) {
+ $hasNonZeroValue = true;
+ }
+ $ordinateSeries[$column][] = $parsedOrdinateValue;
+ }
+
+ if (isset($reportMetadata[$i])) {
+ $rowMetadata = $reportMetadata[$i]->getColumns();
+ if (isset($rowMetadata['logo'])) {
+ $absoluteLogoPath = self::getAbsoluteLogoPath($rowMetadata['logo']);
+ if (file_exists($absoluteLogoPath)) {
+ $abscissaLogos[$i] = $absoluteLogoPath;
+ }
+ }
+ }
+ $i++;
+ }
+ } else // if the report has no dimension we have multiple reports each with only one row within the reportData
+ {
+ // $periodsData instanceof Piwik_DataTable_Simple[]
+ $periodsData = array_values($reportData->getArray());
+ $periodsCount = count($periodsData);
+
+ for ($i = 0; $i < $periodsCount; $i++) {
+ // $periodsData[$i] instanceof Piwik_DataTable_Simple
+ // $rows instanceof Piwik_DataTable_Row[]
+ if (empty($periodsData[$i])) {
+ continue;
+ }
+ $rows = $periodsData[$i]->getRows();
+
+ if (array_key_exists(0, $rows)) {
+ $rowData = $rows[0]->getColumns(); // associative Array
+
+ foreach ($ordinateColumns as $column) {
+ $ordinateValue = $rowData[$column];
+ $parsedOrdinateValue = $this->parseOrdinateValue($ordinateValue);
+
+ $hasData = true;
+
+ if (!empty($parsedOrdinateValue)) {
+ $hasNonZeroValue = true;
+ }
+
+ $ordinateSeries[$column][] = $parsedOrdinateValue;
+ }
+
+ } else {
+ foreach ($ordinateColumns as $column) {
+ $ordinateSeries[$column][] = 0;
+ }
+ }
+
+ $rowId = $periodsData[$i]->metadata['period']->getLocalizedShortString();
+ $abscissaSeries[] = Piwik_Common::unsanitizeInputValue($rowId);
+ }
+ }
+
+ if (!$hasData || !$hasNonZeroValue) {
+ throw new Exception(Piwik_Translate('General_NoDataForGraph_js'));
+ }
+
+ //Setup the graph
+ $graph = Piwik_ImageGraph_StaticGraph::factory($graphType);
+ $graph->setWidth($width);
+ $graph->setHeight($height);
+ $graph->setFont($font);
+ $graph->setFontSize($fontSize);
+ $graph->setLegendFontSize($legendFontSize);
+ $graph->setOrdinateLabels($ordinateLabels);
+ $graph->setShowLegend($showLegend);
+ $graph->setAliasedGraph($aliasedGraph);
+ $graph->setAbscissaSeries($abscissaSeries);
+ $graph->setAbscissaLogos($abscissaLogos);
+ $graph->setOrdinateSeries($ordinateSeries);
+ $graph->setOrdinateLogos($ordinateLogos);
+ $graph->setColors(!empty($colors) ? explode(',', $colors) : array());
+ if ($period == 'day') {
+ $graph->setForceSkippedLabels(6);
+ }
+
+ // render graph
+ $graph->renderGraph();
+
+ } catch (Exception $e) {
+
+ $graph = new Piwik_ImageGraph_StaticGraph_Exception();
+ $graph->setWidth($width);
+ $graph->setHeight($height);
+ $graph->setFont($font);
+ $graph->setFontSize($fontSize);
+ $graph->setException($e);
+ $graph->renderGraph();
+ }
+
+ // restoring get parameters
+ $_GET = $savedGET;
+
+ switch ($outputType) {
+ case self::GRAPH_OUTPUT_FILE:
+ if ($idGoal != '') {
+ $idGoal = '_' . $idGoal;
+ }
+ $fileName = self::$DEFAULT_PARAMETERS[$graphType][self::FILENAME_KEY] . '_' . $apiModule . '_' . $apiAction . $idGoal . ' ' . str_replace(',', '-', $date) . ' ' . $idSite . '.png';
+ $fileName = str_replace(array(' ', '/'), '_', $fileName);
+
+ if (!Piwik_Common::isValidFilename($fileName)) {
+ throw new Exception('Error: Image graph filename ' . $fileName . ' is not valid.');
+ }
+
+ return $graph->sendToDisk($fileName);
+
+ case self::GRAPH_OUTPUT_PHP:
+ return $graph->getRenderedImage();
+
+ case self::GRAPH_OUTPUT_INLINE:
+ default:
+ $graph->sendToBrowser();
+ exit;
+ }
+ }
+
+ private function setFilterTruncate($default)
+ {
+ $_GET['filter_truncate'] = Piwik_Common::getRequestVar('filter_truncate', $default, 'int');
+ }
+
+ private static function parseOrdinateValue($ordinateValue)
+ {
+ $ordinateValue = @str_replace(',', '.', $ordinateValue);
+
+ // convert hh:mm:ss formatted time values to number of seconds
+ if (preg_match('/([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})/', $ordinateValue, $matches)) {
+ $hour = $matches[1];
+ $min = $matches[2];
+ $sec = $matches[3];
+
+ $ordinateValue = ($hour * 3600) + ($min * 60) + $sec;
+ }
+
+ // OK, only numbers from here please (strip out currency sign)
+ $ordinateValue = preg_replace('/[^0-9.]/', '', $ordinateValue);
+ return $ordinateValue;
+ }
+
+ private static function getFontPath($font)
+ {
+ return PIWIK_INCLUDE_PATH . self::FONT_DIR . $font;
+ }
+
+ protected static function getAbsoluteLogoPath($relativeLogoPath)
+ {
+ return PIWIK_INCLUDE_PATH . '/' . $relativeLogoPath;
+ }
}
diff --git a/plugins/ImageGraph/Controller.php b/plugins/ImageGraph/Controller.php
index 91689fa3ed..1b76c6cec7 100644
--- a/plugins/ImageGraph/Controller.php
+++ b/plugins/ImageGraph/Controller.php
@@ -1,75 +1,73 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_ImageGraph
*/
class Piwik_ImageGraph_Controller extends Piwik_Controller
{
- // Call metadata reports, and draw the default graph for each report.
- public function index()
- {
- Piwik::checkUserHasSomeAdminAccess();
- $idSite = Piwik_Common::getRequestVar('idSite', 1, 'int');
- $period = Piwik_Common::getRequestVar('period', 'day', 'string');
- $date = Piwik_Common::getRequestVar('date', 'today', 'string');
- $_GET['token_auth'] = Piwik::getCurrentUserTokenAuth();
- $reports = Piwik_API_API::getInstance()->getReportMetadata($idSite, $period, $date);
- $plot = array();
- foreach($reports as $report)
- {
- if(!empty($report['imageGraphUrl']))
- {
- $plot[] = array(
- // Title
- $report['category'] . ' › ' . $report['name'],
- //URL
- Piwik::getPiwikUrl() . $report['imageGraphUrl']
- );
- }
- }
- $view = Piwik_View::factory('index');
- $view->titleAndUrls = $plot;
- echo $view->render();
- }
-
- // Draw graphs for all sizes (DEBUG)
+ // Call metadata reports, and draw the default graph for each report.
+ public function index()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ $idSite = Piwik_Common::getRequestVar('idSite', 1, 'int');
+ $period = Piwik_Common::getRequestVar('period', 'day', 'string');
+ $date = Piwik_Common::getRequestVar('date', 'today', 'string');
+ $_GET['token_auth'] = Piwik::getCurrentUserTokenAuth();
+ $reports = Piwik_API_API::getInstance()->getReportMetadata($idSite, $period, $date);
+ $plot = array();
+ foreach ($reports as $report) {
+ if (!empty($report['imageGraphUrl'])) {
+ $plot[] = array(
+ // Title
+ $report['category'] . ' › ' . $report['name'],
+ //URL
+ Piwik::getPiwikUrl() . $report['imageGraphUrl']
+ );
+ }
+ }
+ $view = Piwik_View::factory('index');
+ $view->titleAndUrls = $plot;
+ echo $view->render();
+ }
+
+ // Draw graphs for all sizes (DEBUG)
public function testAllSizes()
- {
- Piwik::checkUserIsSuperUser();
-
- $view = Piwik_View::factory('debug_graphs_all_sizes');
- $this->setGeneralVariablesView($view);
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $view = Piwik_View::factory('debug_graphs_all_sizes');
+ $this->setGeneralVariablesView($view);
- $period = Piwik_Common::getRequestVar('period', 'day', 'string');
- $date = Piwik_Common::getRequestVar('date', 'today', 'string');
+ $period = Piwik_Common::getRequestVar('period', 'day', 'string');
+ $date = Piwik_Common::getRequestVar('date', 'today', 'string');
- $_GET['token_auth'] = Piwik::getCurrentUserTokenAuth();
- $availableReports = Piwik_API_API::getInstance()->getReportMetadata($this->idSite, $period, $date);
- $view->availableReports = $availableReports;
- $view->graphTypes = array(
- '', // default graph type
+ $_GET['token_auth'] = Piwik::getCurrentUserTokenAuth();
+ $availableReports = Piwik_API_API::getInstance()->getReportMetadata($this->idSite, $period, $date);
+ $view->availableReports = $availableReports;
+ $view->graphTypes = array(
+ '', // default graph type
// 'evolution',
// 'verticalBar',
// 'horizontalBar',
// 'pie',
// '3dPie',
- );
- $view->graphSizes = array(
- array(null, null), // default graph size
- array(Piwik_ReportRenderer::IMAGE_GRAPH_WIDTH, Piwik_ReportRenderer::IMAGE_GRAPH_HEIGHT), // PDF/HTML reports
- array(460, 150), // standard phone
- array(300, 150), // standard phone 2
- array(240, 150), // smallest mobile display
- array(800, 150), // landscape mode
- array(600, 300, $fontSize = 18, 300, 150), // iphone requires bigger font, then it will be scaled down by ios
- );
- echo $view->render();
- }
+ );
+ $view->graphSizes = array(
+ array(null, null), // default graph size
+ array(Piwik_ReportRenderer::IMAGE_GRAPH_WIDTH, Piwik_ReportRenderer::IMAGE_GRAPH_HEIGHT), // PDF/HTML reports
+ array(460, 150), // standard phone
+ array(300, 150), // standard phone 2
+ array(240, 150), // smallest mobile display
+ array(800, 150), // landscape mode
+ array(600, 300, $fontSize = 18, 300, 150), // iphone requires bigger font, then it will be scaled down by ios
+ );
+ echo $view->render();
+ }
}
diff --git a/plugins/ImageGraph/ImageGraph.php b/plugins/ImageGraph/ImageGraph.php
index 1494dc79e6..f149150d23 100644
--- a/plugins/ImageGraph/ImageGraph.php
+++ b/plugins/ImageGraph/ImageGraph.php
@@ -1,157 +1,140 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_ImageGraph
*/
class Piwik_ImageGraph extends Piwik_Plugin
{
- static private $CONSTANT_ROW_COUNT_REPORT_EXCEPTIONS = array(
- 'Referers_getRefererType',
- );
-
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('ImageGraph_PluginDescription')
- . ' Debug: <a href="'.Piwik_Url::getCurrentQueryStringWithParametersModified(
- array('module'=> 'ImageGraph', 'action' => 'index'))
- . '">All images</a>',
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION
- );
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'API.getReportMetadata.end.end' => 'getReportMetadata',
- );
- return $hooks;
- }
-
- // Number of periods to plot on an evolution graph
- const GRAPH_EVOLUTION_LAST_PERIODS = 30;
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- public function getReportMetadata($notification)
- {
- $info = $notification->getNotificationInfo();
- $reports = &$notification->getNotificationObject();
- $idSites = $info['idSites'];
-
- // If only one website is selected, we add the Graph URL
- if(count($idSites) != 1)
- {
- return;
- }
- $idSite = reset($idSites);
-
- // in case API.getReportMetadata was not called with date/period we use sane defaults
- if(empty($info['period']))
- {
- $info['period'] = 'day';
- }
- if(empty($info['date']))
- {
- $info['date'] = 'today';
- }
-
- // need two sets of period & date, one for single period graphs, one for multiple periods graphs
- if(Piwik_Archive::isMultiplePeriod($info['date'], $info['period']))
- {
- $periodForMultiplePeriodGraph = $info['period'];
- $dateForMultiplePeriodGraph = $info['date'];
-
- $periodForSinglePeriodGraph = 'range';
- $dateForSinglePeriodGraph = $info['date'];
- }
- else
- {
- $periodForSinglePeriodGraph = $info['period'];
- $dateForSinglePeriodGraph = $info['date'];
-
- $piwikSite = new Piwik_Site($idSite);
- if($periodForSinglePeriodGraph == 'range')
- {
- $periodForMultiplePeriodGraph = Piwik_Config::getInstance()->General['graphs_default_period_to_plot_when_period_range'];
- $dateForMultiplePeriodGraph = $dateForSinglePeriodGraph;
- }
- else
- {
- $periodForMultiplePeriodGraph = $periodForSinglePeriodGraph;
- $dateForMultiplePeriodGraph = Piwik_Controller::getDateRangeRelativeToEndDate(
- $periodForSinglePeriodGraph,
- 'last' . self::GRAPH_EVOLUTION_LAST_PERIODS,
- $dateForSinglePeriodGraph,
- $piwikSite
- );
- }
- }
-
- $token_auth = Piwik_Common::getRequestVar('token_auth', false);
-
- $urlPrefix = "index.php?";
- foreach($reports as &$report)
- {
- $reportModule = $report['module'];
- $reportAction = $report['action'];
- $reportUniqueId = $reportModule.'_'.$reportAction;
-
- $parameters = array();
- $parameters['module'] = 'API';
- $parameters['method'] = 'ImageGraph.get';
- $parameters['idSite'] = $idSite;
- $parameters['apiModule'] = $reportModule;
- $parameters['apiAction'] = $reportAction;
- if(!empty($token_auth))
- {
- $parameters['token_auth'] = $token_auth;
- }
-
- // Forward custom Report parameters to the graph URL
- if(!empty($report['parameters']))
- {
- $parameters = array_merge($parameters, $report['parameters']);
- }
- if(empty($report['dimension']))
- {
- $parameters['period'] = $periodForMultiplePeriodGraph;
- $parameters['date'] = $dateForMultiplePeriodGraph;
- }
- else
- {
- $parameters['period'] = $periodForSinglePeriodGraph;
- $parameters['date'] = $dateForSinglePeriodGraph;
- }
-
- // add the idSubtable if it exists
- $idSubtable = Piwik_Common::getRequestVar('idSubtable', false);
- if ($idSubtable !== false)
- {
- $parameters['idSubtable'] = $idSubtable;
- }
-
- $report['imageGraphUrl'] = $urlPrefix . Piwik_Url::getQueryStringFromParameters($parameters);
-
- // thanks to API.getRowEvolution, reports with dimensions can now be plotted using an evolution graph
- // however, most reports with a fixed set of dimension values are excluded
- // this is done so Piwik Mobile and Scheduled Reports do not display them
- if(empty($report['constantRowsCount']) || in_array($reportUniqueId,self::$CONSTANT_ROW_COUNT_REPORT_EXCEPTIONS))
- {
- $parameters['period'] = $periodForMultiplePeriodGraph;
- $parameters['date'] = $dateForMultiplePeriodGraph;
- $report['imageGraphEvolutionUrl'] = $urlPrefix . Piwik_Url::getQueryStringFromParameters($parameters);
- }
- }
- }
+ static private $CONSTANT_ROW_COUNT_REPORT_EXCEPTIONS = array(
+ 'Referers_getRefererType',
+ );
+
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('ImageGraph_PluginDescription')
+ . ' Debug: <a href="' . Piwik_Url::getCurrentQueryStringWithParametersModified(
+ array('module' => 'ImageGraph', 'action' => 'index'))
+ . '">All images</a>',
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION
+ );
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'API.getReportMetadata.end.end' => 'getReportMetadata',
+ );
+ return $hooks;
+ }
+
+ // Number of periods to plot on an evolution graph
+ const GRAPH_EVOLUTION_LAST_PERIODS = 30;
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ public function getReportMetadata($notification)
+ {
+ $info = $notification->getNotificationInfo();
+ $reports = & $notification->getNotificationObject();
+ $idSites = $info['idSites'];
+
+ // If only one website is selected, we add the Graph URL
+ if (count($idSites) != 1) {
+ return;
+ }
+ $idSite = reset($idSites);
+
+ // in case API.getReportMetadata was not called with date/period we use sane defaults
+ if (empty($info['period'])) {
+ $info['period'] = 'day';
+ }
+ if (empty($info['date'])) {
+ $info['date'] = 'today';
+ }
+
+ // need two sets of period & date, one for single period graphs, one for multiple periods graphs
+ if (Piwik_Archive::isMultiplePeriod($info['date'], $info['period'])) {
+ $periodForMultiplePeriodGraph = $info['period'];
+ $dateForMultiplePeriodGraph = $info['date'];
+
+ $periodForSinglePeriodGraph = 'range';
+ $dateForSinglePeriodGraph = $info['date'];
+ } else {
+ $periodForSinglePeriodGraph = $info['period'];
+ $dateForSinglePeriodGraph = $info['date'];
+
+ $piwikSite = new Piwik_Site($idSite);
+ if ($periodForSinglePeriodGraph == 'range') {
+ $periodForMultiplePeriodGraph = Piwik_Config::getInstance()->General['graphs_default_period_to_plot_when_period_range'];
+ $dateForMultiplePeriodGraph = $dateForSinglePeriodGraph;
+ } else {
+ $periodForMultiplePeriodGraph = $periodForSinglePeriodGraph;
+ $dateForMultiplePeriodGraph = Piwik_Controller::getDateRangeRelativeToEndDate(
+ $periodForSinglePeriodGraph,
+ 'last' . self::GRAPH_EVOLUTION_LAST_PERIODS,
+ $dateForSinglePeriodGraph,
+ $piwikSite
+ );
+ }
+ }
+
+ $token_auth = Piwik_Common::getRequestVar('token_auth', false);
+
+ $urlPrefix = "index.php?";
+ foreach ($reports as &$report) {
+ $reportModule = $report['module'];
+ $reportAction = $report['action'];
+ $reportUniqueId = $reportModule . '_' . $reportAction;
+
+ $parameters = array();
+ $parameters['module'] = 'API';
+ $parameters['method'] = 'ImageGraph.get';
+ $parameters['idSite'] = $idSite;
+ $parameters['apiModule'] = $reportModule;
+ $parameters['apiAction'] = $reportAction;
+ if (!empty($token_auth)) {
+ $parameters['token_auth'] = $token_auth;
+ }
+
+ // Forward custom Report parameters to the graph URL
+ if (!empty($report['parameters'])) {
+ $parameters = array_merge($parameters, $report['parameters']);
+ }
+ if (empty($report['dimension'])) {
+ $parameters['period'] = $periodForMultiplePeriodGraph;
+ $parameters['date'] = $dateForMultiplePeriodGraph;
+ } else {
+ $parameters['period'] = $periodForSinglePeriodGraph;
+ $parameters['date'] = $dateForSinglePeriodGraph;
+ }
+
+ // add the idSubtable if it exists
+ $idSubtable = Piwik_Common::getRequestVar('idSubtable', false);
+ if ($idSubtable !== false) {
+ $parameters['idSubtable'] = $idSubtable;
+ }
+
+ $report['imageGraphUrl'] = $urlPrefix . Piwik_Url::getQueryStringFromParameters($parameters);
+
+ // thanks to API.getRowEvolution, reports with dimensions can now be plotted using an evolution graph
+ // however, most reports with a fixed set of dimension values are excluded
+ // this is done so Piwik Mobile and Scheduled Reports do not display them
+ if (empty($report['constantRowsCount']) || in_array($reportUniqueId, self::$CONSTANT_ROW_COUNT_REPORT_EXCEPTIONS)) {
+ $parameters['period'] = $periodForMultiplePeriodGraph;
+ $parameters['date'] = $dateForMultiplePeriodGraph;
+ $report['imageGraphEvolutionUrl'] = $urlPrefix . Piwik_Url::getQueryStringFromParameters($parameters);
+ }
+ }
+ }
}
diff --git a/plugins/ImageGraph/StaticGraph.php b/plugins/ImageGraph/StaticGraph.php
index afb8993db8..2d3ee04685 100644
--- a/plugins/ImageGraph/StaticGraph.php
+++ b/plugins/ImageGraph/StaticGraph.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_ImageGraph
*/
@@ -21,311 +21,291 @@ require_once PIWIK_INCLUDE_PATH . "/libs/pChart2.1.3/class/pData.class.php";
*/
abstract class Piwik_ImageGraph_StaticGraph
{
- const GRAPH_TYPE_BASIC_LINE = "evolution";
- const GRAPH_TYPE_VERTICAL_BAR = "verticalBar";
- const GRAPH_TYPE_HORIZONTAL_BAR = "horizontalBar";
- const GRAPH_TYPE_3D_PIE = "3dPie";
- const GRAPH_TYPE_BASIC_PIE = "pie";
-
- static private $availableStaticGraphTypes = array(
- self::GRAPH_TYPE_BASIC_LINE => 'Piwik_ImageGraph_StaticGraph_Evolution',
- self::GRAPH_TYPE_VERTICAL_BAR => 'Piwik_ImageGraph_StaticGraph_VerticalBar',
- self::GRAPH_TYPE_HORIZONTAL_BAR => 'Piwik_ImageGraph_StaticGraph_HorizontalBar',
- self::GRAPH_TYPE_BASIC_PIE => 'Piwik_ImageGraph_StaticGraph_Pie',
- self::GRAPH_TYPE_3D_PIE => 'Piwik_ImageGraph_StaticGraph_3DPie',
- );
-
- const ABSCISSA_SERIE_NAME = 'ABSCISSA';
-
- private $aliasedGraph;
-
- /**
- * @var pImage
- */
- protected $pImage;
- protected $pData;
- protected $ordinateLabels;
- protected $showLegend;
- protected $abscissaSeries;
- protected $abscissaLogos;
- protected $ordinateSeries;
- protected $ordinateLogos;
- protected $colors;
- protected $font;
- protected $fontSize;
- protected $legendFontSize;
- protected $width;
- protected $height;
- protected $forceSkippedLabels = false;
-
- abstract protected function getDefaultColors();
-
- abstract public function renderGraph();
-
- /**
- * Return the StaticGraph according to the static graph type $graphType
- *
- * @throws exception If the static graph type is unknown
- * @param string $graphType
- * @return Piwik_ImageGraph_StaticGraph
- */
- public static function factory($graphType)
- {
- if (isset(self::$availableStaticGraphTypes[$graphType]))
- {
-
- $className = self::$availableStaticGraphTypes[$graphType];
- Piwik_Loader::loadClass($className);
- return new $className;
- }
- else
- {
- throw new Exception(
- Piwik_TranslateException(
- 'General_ExceptionInvalidStaticGraphType',
- array($graphType, implode(', ', self::getAvailableStaticGraphTypes()))
- )
- );
- }
- }
-
- public static function getAvailableStaticGraphTypes()
- {
- return array_keys(self::$availableStaticGraphTypes);
- }
-
- /**
- * Save rendering to disk
- *
- * @param string $filename without path
- * @return string path of file
- */
- public function sendToDisk($filename)
- {
- $filePath = self::getOutputPath($filename);
- $this->pImage->Render($filePath);
- return $filePath;
- }
-
- /**
- * @return rendered static graph
- */
- public function getRenderedImage()
- {
- return $this->pImage->Picture;
- }
-
- /**
- * Output rendering to browser
- */
- public function sendToBrowser()
- {
- $this->pImage->stroke();
- }
-
- public function setWidth($width)
- {
- $this->width = $width;
- }
-
- public function setHeight($height)
- {
- $this->height = $height;
- }
-
- public function setFontSize($fontSize)
- {
- if(!is_numeric($fontSize))
- {
- $fontSize = Piwik_ImageGraph_API::DEFAULT_FONT_SIZE;
- }
- $this->fontSize = $fontSize;
- }
-
- public function setLegendFontSize($legendFontSize)
- {
- $this->legendFontSize = $legendFontSize;
- }
-
- public function setFont($font)
- {
- $this->font = $font;
- }
-
- public function setOrdinateSeries($ordinateSeries)
- {
- $this->ordinateSeries = $ordinateSeries;
- }
-
- public function setOrdinateLogos($ordinateLogos)
- {
- $this->ordinateLogos = $ordinateLogos;
- }
-
- public function setAbscissaLogos($abscissaLogos)
- {
- $this->abscissaLogos = $abscissaLogos;
- }
-
- public function setAbscissaSeries($abscissaSeries)
- {
- $this->abscissaSeries = $abscissaSeries;
- }
-
- public function setShowLegend($showLegend)
- {
- $this->showLegend = $showLegend;
- }
-
- public function setForceSkippedLabels($forceSkippedLabels)
- {
- $this->forceSkippedLabels = $forceSkippedLabels;
- }
-
- public function setOrdinateLabels($ordinateLabels)
- {
- $this->ordinateLabels = $ordinateLabels;
- }
-
- public function setAliasedGraph($aliasedGraph)
- {
- $this->aliasedGraph = $aliasedGraph;
- }
-
- public function setColors($colors)
- {
- $i = 0;
- foreach($this->getDefaultColors() as $colorKey => $defaultColor)
- {
- if(isset($colors[$i]) && $this->hex2rgb($colors[$i]))
- {
- $hexColor = $colors[$i];
- }
- else
- {
- $hexColor = $defaultColor;
- }
-
- $this->colors[$colorKey] = $this->hex2rgb($hexColor);
- $i++;
- }
- }
-
- /**
- * Return $filename with temp directory and delete file
- *
- * @static
- * @param $filename
- * @return string path of file in temp directory
- */
- protected static function getOutputPath($filename)
- {
- $outputFilename = PIWIK_USER_PATH . '/tmp/assets/' . $filename;
- @chmod($outputFilename, 0600);
- @unlink($outputFilename);
- return $outputFilename;
- }
-
- protected function initpData()
- {
- $this->pData = new pData();
-
- foreach($this->ordinateSeries as $column => $data)
- {
- $this->pData->addPoints($data, $column);
- $this->pData->setSerieDescription($column,$this->ordinateLabels[$column]);
- if(isset($this->ordinateLogos[$column]))
- {
- $ordinateLogo = $this->ordinateLogos[$column];
- $this->pData->setSeriePicture($column, $ordinateLogo);
- }
- }
-
- $this->pData->addPoints($this->abscissaSeries, self::ABSCISSA_SERIE_NAME);
- $this->pData->setAbscissa(self::ABSCISSA_SERIE_NAME);
- }
-
- protected function initpImage()
- {
- $this->pImage = new pImage($this->width, $this->height, $this->pData);
- $this->pImage->Antialias = $this->aliasedGraph;
-
- $this->pImage->setFontProperties(
- array(
- "FontName" => $this->font,
- "FontSize" => $this->fontSize
- )
- );
- }
-
- protected function getTextWidthHeight($text, $fontSize = false)
- {
- if(!$fontSize)
- {
- $fontSize = $this->fontSize;
- }
-
- if(!$this->pImage)
- {
- $this->initpImage();
- }
-
- // could not find a way to get pixel perfect width & height info using imageftbbox
- $textInfo = $this->pImage->drawText(
- 0, 0, $text,
- array(
- 'Alpha'=>0,
- 'FontSize'=>$fontSize,
- 'FontName' => $this->font
- )
- );
-
- return array($textInfo[1]["X"] + 1, $textInfo[0]["Y"]-$textInfo[2]["Y"]);
- }
-
- protected function getMaximumTextWidthHeight($values)
- {
- if(array_values($values) === $values)
- {
- $values = array('' => $values);
- }
-
- $maxWidth = 0;
- $maxHeight = 0;
- foreach($values as $column => $data)
- {
- foreach($data as $value)
- {
- list($valueWidth, $valueHeight) = $this->getTextWidthHeight($value);
-
- if($valueWidth > $maxWidth)
- {
- $maxWidth = $valueWidth;
- }
-
- if($valueHeight > $maxHeight)
- {
- $maxHeight = $valueHeight;
- }
- }
- }
-
- return array($maxWidth, $maxHeight);
- }
-
- private static function hex2rgb($hexColor)
- {
- if(preg_match('/([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/', $hexColor, $matches))
- {
- return array(
- 'R' => hexdec($matches[1]),
- 'G' => hexdec($matches[2]),
- 'B' => hexdec($matches[3])
- );
- }
- else
- {
- return false;
- }
- }
+ const GRAPH_TYPE_BASIC_LINE = "evolution";
+ const GRAPH_TYPE_VERTICAL_BAR = "verticalBar";
+ const GRAPH_TYPE_HORIZONTAL_BAR = "horizontalBar";
+ const GRAPH_TYPE_3D_PIE = "3dPie";
+ const GRAPH_TYPE_BASIC_PIE = "pie";
+
+ static private $availableStaticGraphTypes = array(
+ self::GRAPH_TYPE_BASIC_LINE => 'Piwik_ImageGraph_StaticGraph_Evolution',
+ self::GRAPH_TYPE_VERTICAL_BAR => 'Piwik_ImageGraph_StaticGraph_VerticalBar',
+ self::GRAPH_TYPE_HORIZONTAL_BAR => 'Piwik_ImageGraph_StaticGraph_HorizontalBar',
+ self::GRAPH_TYPE_BASIC_PIE => 'Piwik_ImageGraph_StaticGraph_Pie',
+ self::GRAPH_TYPE_3D_PIE => 'Piwik_ImageGraph_StaticGraph_3DPie',
+ );
+
+ const ABSCISSA_SERIE_NAME = 'ABSCISSA';
+
+ private $aliasedGraph;
+
+ /**
+ * @var pImage
+ */
+ protected $pImage;
+ protected $pData;
+ protected $ordinateLabels;
+ protected $showLegend;
+ protected $abscissaSeries;
+ protected $abscissaLogos;
+ protected $ordinateSeries;
+ protected $ordinateLogos;
+ protected $colors;
+ protected $font;
+ protected $fontSize;
+ protected $legendFontSize;
+ protected $width;
+ protected $height;
+ protected $forceSkippedLabels = false;
+
+ abstract protected function getDefaultColors();
+
+ abstract public function renderGraph();
+
+ /**
+ * Return the StaticGraph according to the static graph type $graphType
+ *
+ * @throws exception If the static graph type is unknown
+ * @param string $graphType
+ * @return Piwik_ImageGraph_StaticGraph
+ */
+ public static function factory($graphType)
+ {
+ if (isset(self::$availableStaticGraphTypes[$graphType])) {
+
+ $className = self::$availableStaticGraphTypes[$graphType];
+ Piwik_Loader::loadClass($className);
+ return new $className;
+ } else {
+ throw new Exception(
+ Piwik_TranslateException(
+ 'General_ExceptionInvalidStaticGraphType',
+ array($graphType, implode(', ', self::getAvailableStaticGraphTypes()))
+ )
+ );
+ }
+ }
+
+ public static function getAvailableStaticGraphTypes()
+ {
+ return array_keys(self::$availableStaticGraphTypes);
+ }
+
+ /**
+ * Save rendering to disk
+ *
+ * @param string $filename without path
+ * @return string path of file
+ */
+ public function sendToDisk($filename)
+ {
+ $filePath = self::getOutputPath($filename);
+ $this->pImage->Render($filePath);
+ return $filePath;
+ }
+
+ /**
+ * @return rendered static graph
+ */
+ public function getRenderedImage()
+ {
+ return $this->pImage->Picture;
+ }
+
+ /**
+ * Output rendering to browser
+ */
+ public function sendToBrowser()
+ {
+ $this->pImage->stroke();
+ }
+
+ public function setWidth($width)
+ {
+ $this->width = $width;
+ }
+
+ public function setHeight($height)
+ {
+ $this->height = $height;
+ }
+
+ public function setFontSize($fontSize)
+ {
+ if (!is_numeric($fontSize)) {
+ $fontSize = Piwik_ImageGraph_API::DEFAULT_FONT_SIZE;
+ }
+ $this->fontSize = $fontSize;
+ }
+
+ public function setLegendFontSize($legendFontSize)
+ {
+ $this->legendFontSize = $legendFontSize;
+ }
+
+ public function setFont($font)
+ {
+ $this->font = $font;
+ }
+
+ public function setOrdinateSeries($ordinateSeries)
+ {
+ $this->ordinateSeries = $ordinateSeries;
+ }
+
+ public function setOrdinateLogos($ordinateLogos)
+ {
+ $this->ordinateLogos = $ordinateLogos;
+ }
+
+ public function setAbscissaLogos($abscissaLogos)
+ {
+ $this->abscissaLogos = $abscissaLogos;
+ }
+
+ public function setAbscissaSeries($abscissaSeries)
+ {
+ $this->abscissaSeries = $abscissaSeries;
+ }
+
+ public function setShowLegend($showLegend)
+ {
+ $this->showLegend = $showLegend;
+ }
+
+ public function setForceSkippedLabels($forceSkippedLabels)
+ {
+ $this->forceSkippedLabels = $forceSkippedLabels;
+ }
+
+ public function setOrdinateLabels($ordinateLabels)
+ {
+ $this->ordinateLabels = $ordinateLabels;
+ }
+
+ public function setAliasedGraph($aliasedGraph)
+ {
+ $this->aliasedGraph = $aliasedGraph;
+ }
+
+ public function setColors($colors)
+ {
+ $i = 0;
+ foreach ($this->getDefaultColors() as $colorKey => $defaultColor) {
+ if (isset($colors[$i]) && $this->hex2rgb($colors[$i])) {
+ $hexColor = $colors[$i];
+ } else {
+ $hexColor = $defaultColor;
+ }
+
+ $this->colors[$colorKey] = $this->hex2rgb($hexColor);
+ $i++;
+ }
+ }
+
+ /**
+ * Return $filename with temp directory and delete file
+ *
+ * @static
+ * @param $filename
+ * @return string path of file in temp directory
+ */
+ protected static function getOutputPath($filename)
+ {
+ $outputFilename = PIWIK_USER_PATH . '/tmp/assets/' . $filename;
+ @chmod($outputFilename, 0600);
+ @unlink($outputFilename);
+ return $outputFilename;
+ }
+
+ protected function initpData()
+ {
+ $this->pData = new pData();
+
+ foreach ($this->ordinateSeries as $column => $data) {
+ $this->pData->addPoints($data, $column);
+ $this->pData->setSerieDescription($column, $this->ordinateLabels[$column]);
+ if (isset($this->ordinateLogos[$column])) {
+ $ordinateLogo = $this->ordinateLogos[$column];
+ $this->pData->setSeriePicture($column, $ordinateLogo);
+ }
+ }
+
+ $this->pData->addPoints($this->abscissaSeries, self::ABSCISSA_SERIE_NAME);
+ $this->pData->setAbscissa(self::ABSCISSA_SERIE_NAME);
+ }
+
+ protected function initpImage()
+ {
+ $this->pImage = new pImage($this->width, $this->height, $this->pData);
+ $this->pImage->Antialias = $this->aliasedGraph;
+
+ $this->pImage->setFontProperties(
+ array(
+ "FontName" => $this->font,
+ "FontSize" => $this->fontSize
+ )
+ );
+ }
+
+ protected function getTextWidthHeight($text, $fontSize = false)
+ {
+ if (!$fontSize) {
+ $fontSize = $this->fontSize;
+ }
+
+ if (!$this->pImage) {
+ $this->initpImage();
+ }
+
+ // could not find a way to get pixel perfect width & height info using imageftbbox
+ $textInfo = $this->pImage->drawText(
+ 0, 0, $text,
+ array(
+ 'Alpha' => 0,
+ 'FontSize' => $fontSize,
+ 'FontName' => $this->font
+ )
+ );
+
+ return array($textInfo[1]["X"] + 1, $textInfo[0]["Y"] - $textInfo[2]["Y"]);
+ }
+
+ protected function getMaximumTextWidthHeight($values)
+ {
+ if (array_values($values) === $values) {
+ $values = array('' => $values);
+ }
+
+ $maxWidth = 0;
+ $maxHeight = 0;
+ foreach ($values as $column => $data) {
+ foreach ($data as $value) {
+ list($valueWidth, $valueHeight) = $this->getTextWidthHeight($value);
+
+ if ($valueWidth > $maxWidth) {
+ $maxWidth = $valueWidth;
+ }
+
+ if ($valueHeight > $maxHeight) {
+ $maxHeight = $valueHeight;
+ }
+ }
+ }
+
+ return array($maxWidth, $maxHeight);
+ }
+
+ private static function hex2rgb($hexColor)
+ {
+ if (preg_match('/([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/', $hexColor, $matches)) {
+ return array(
+ 'R' => hexdec($matches[1]),
+ 'G' => hexdec($matches[2]),
+ 'B' => hexdec($matches[3])
+ );
+ } else {
+ return false;
+ }
+ }
} \ No newline at end of file
diff --git a/plugins/ImageGraph/StaticGraph/3DPie.php b/plugins/ImageGraph/StaticGraph/3DPie.php
index 966800b454..62252c0059 100644
--- a/plugins/ImageGraph/StaticGraph/3DPie.php
+++ b/plugins/ImageGraph/StaticGraph/3DPie.php
@@ -15,14 +15,14 @@
*/
class Piwik_ImageGraph_StaticGraph_3DPie extends Piwik_ImageGraph_StaticGraph_PieGraph
{
- public function renderGraph()
- {
- $this->initPieGraph(true);
+ public function renderGraph()
+ {
+ $this->initPieGraph(true);
- $this->pieChart->draw3DPie(
- $this->xPosition,
- $this->yPosition,
- $this->pieConfig
- );
- }
+ $this->pieChart->draw3DPie(
+ $this->xPosition,
+ $this->yPosition,
+ $this->pieConfig
+ );
+ }
}
diff --git a/plugins/ImageGraph/StaticGraph/Evolution.php b/plugins/ImageGraph/StaticGraph/Evolution.php
index 69f87ea2f3..a4851afaa4 100644
--- a/plugins/ImageGraph/StaticGraph/Evolution.php
+++ b/plugins/ImageGraph/StaticGraph/Evolution.php
@@ -17,16 +17,16 @@
class Piwik_ImageGraph_StaticGraph_Evolution extends Piwik_ImageGraph_StaticGraph_GridGraph
{
- public function renderGraph()
- {
- $this->initGridChart(
- $displayVerticalGridLines = true,
- $bulletType = LEGEND_FAMILY_LINE,
- $horizontalGraph = false,
- $showTicks = true,
- $verticalLegend = true
- );
+ public function renderGraph()
+ {
+ $this->initGridChart(
+ $displayVerticalGridLines = true,
+ $bulletType = LEGEND_FAMILY_LINE,
+ $horizontalGraph = false,
+ $showTicks = true,
+ $verticalLegend = true
+ );
- $this->pImage->drawLineChart();
- }
+ $this->pImage->drawLineChart();
+ }
}
diff --git a/plugins/ImageGraph/StaticGraph/Exception.php b/plugins/ImageGraph/StaticGraph/Exception.php
index 9d93c84dc2..5847ee10e1 100644
--- a/plugins/ImageGraph/StaticGraph/Exception.php
+++ b/plugins/ImageGraph/StaticGraph/Exception.php
@@ -16,60 +16,58 @@
*/
class Piwik_ImageGraph_StaticGraph_Exception extends Piwik_ImageGraph_StaticGraph
{
- const MESSAGE_RIGHT_MARGIN = 5;
+ const MESSAGE_RIGHT_MARGIN = 5;
- private $exception;
+ private $exception;
- public function setException($exception)
- {
- $this->exception = $exception;
- }
+ public function setException($exception)
+ {
+ $this->exception = $exception;
+ }
- protected function getDefaultColors()
- {
- return array();
- }
+ protected function getDefaultColors()
+ {
+ return array();
+ }
- public function setWidth($width)
- {
- if(empty($width)) {
- $width = 450;
- }
- parent::setWidth($width);
- }
+ public function setWidth($width)
+ {
+ if (empty($width)) {
+ $width = 450;
+ }
+ parent::setWidth($width);
+ }
- public function setHeight($height)
- {
- if(empty($height)) {
- $height = 300;
- }
- parent::setHeight($height);
- }
+ public function setHeight($height)
+ {
+ if (empty($height)) {
+ $height = 300;
+ }
+ parent::setHeight($height);
+ }
- public function renderGraph()
- {
- $this->pData = new pData();
+ public function renderGraph()
+ {
+ $this->pData = new pData();
- $message = $this->exception->getMessage();
- list($textWidth, $textHeight) = $this->getTextWidthHeight($message);
+ $message = $this->exception->getMessage();
+ list($textWidth, $textHeight) = $this->getTextWidthHeight($message);
- if($this->width == null)
- {
- $this->width = $textWidth + self::MESSAGE_RIGHT_MARGIN;
- }
+ if ($this->width == null) {
+ $this->width = $textWidth + self::MESSAGE_RIGHT_MARGIN;
+ }
- if($this->height == null)
- {
- $this->height = $textHeight;
- }
+ if ($this->height == null) {
+ $this->height = $textHeight;
+ }
- $this->initpImage();
+ $this->initpImage();
- $this->pImage->drawText(
- 0,
- $textHeight,
- $message
- );
- }
+ $this->pImage->drawText(
+ 0,
+ $textHeight,
+ $message
+ );
+ }
}
diff --git a/plugins/ImageGraph/StaticGraph/GridGraph.php b/plugins/ImageGraph/StaticGraph/GridGraph.php
index 8e150054c5..dd70094106 100644
--- a/plugins/ImageGraph/StaticGraph/GridGraph.php
+++ b/plugins/ImageGraph/StaticGraph/GridGraph.php
@@ -16,452 +16,411 @@
*/
abstract class Piwik_ImageGraph_StaticGraph_GridGraph extends Piwik_ImageGraph_StaticGraph
{
- const GRAPHIC_COLOR_KEY = 'GRAPHIC_COLOR';
- const VALUE_COLOR_KEY = 'VALUE_COLOR';
- const GRID_COLOR_KEY = 'GRID_COLOR';
-
- const TRUNCATION_TEXT = '...';
-
- const DEFAULT_TICK_ALPHA = 20;
- const DEFAULT_SERIE_WEIGHT = 0.5;
- const LEFT_GRID_MARGIN = 4;
- const BOTTOM_GRID_MARGIN = 10;
- const TOP_GRID_MARGIN_HORIZONTAL_GRAPH = 1;
- const RIGHT_GRID_MARGIN_HORIZONTAL_GRAPH = 4;
- const OUTER_TICK_WIDTH = 5;
- const INNER_TICK_WIDTH = 0;
- const LABEL_SPACE_VERTICAL_GRAPH = 7;
-
- const HORIZONTAL_LEGEND_TOP_MARGIN = 5;
- const HORIZONTAL_LEGEND_LEFT_MARGIN = 10;
- const HORIZONTAL_LEGEND_BOTTOM_MARGIN = 10;
- const VERTICAL_LEGEND_TOP_MARGIN = 8;
- const VERTICAL_LEGEND_LEFT_MARGIN = 6;
- const VERTICAL_LEGEND_MAX_WIDTH_PCT = 0.70;
- const LEGEND_LINE_BULLET_WIDTH = 14;
- const LEGEND_BOX_BULLET_WIDTH = 5;
- const LEGEND_BULLET_RIGHT_PADDING = 5;
- const LEGEND_ITEM_HORIZONTAL_INTERSTICE = 6;
- const LEGEND_ITEM_VERTICAL_INTERSTICE_OFFSET = 4;
- const LEGEND_SHADOW_OPACITY = 25;
- const LEGEND_VERTICAL_SHADOW_PADDING = 3;
- const LEGEND_HORIZONTAL_SHADOW_PADDING = 2;
- const PCHART_HARD_CODED_VERTICAL_LEGEND_INTERSTICE = 5;
-
- protected function getDefaultColors()
- {
- return array(
- self::VALUE_COLOR_KEY => '444444',
- self::GRID_COLOR_KEY => 'CCCCCC',
- self::GRAPHIC_COLOR_KEY . '1' => '5170AE',
- self::GRAPHIC_COLOR_KEY . '2' => 'F29007',
- self::GRAPHIC_COLOR_KEY . '3' => 'CC3399',
- self::GRAPHIC_COLOR_KEY . '4' => '9933CC',
- self::GRAPHIC_COLOR_KEY . '5' => '80A033',
- self::GRAPHIC_COLOR_KEY . '6' => '246AD2'
- );
- }
-
- protected function initGridChart(
- $displayVerticalGridLines,
- $bulletType,
- $horizontalGraph,
- $showTicks,
- $verticalLegend
- )
- {
- $this->initpData();
-
- $colorIndex = 1;
- foreach($this->ordinateSeries as $column => $data)
- {
- $this->pData->setSerieWeight($column, self::DEFAULT_SERIE_WEIGHT);
- $graphicColor = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++];
- $this->pData->setPalette($column, $graphicColor);
- }
-
- $this->initpImage();
-
- // graph area coordinates
- $topLeftXValue = $this->getGridLeftMargin($horizontalGraph, $withLabel = true);
- $topLeftYValue = $this->getGridTopMargin($horizontalGraph, $verticalLegend);
- $bottomRightXValue = $this->width - $this->getGridRightMargin($horizontalGraph);
- $bottomRightYValue = $this->getGraphBottom($horizontalGraph);
-
- $this->pImage->setGraphArea(
- $topLeftXValue,
- $topLeftYValue,
- $bottomRightXValue,
- $bottomRightYValue
- );
-
- // determine how many labels need to be skipped
- $skippedLabels = 0;
- if(!$horizontalGraph)
- {
- list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries);
- $graphWidth = $bottomRightXValue - $topLeftXValue;
- $maxNumOfLabels = floor($graphWidth / ($abscissaMaxWidth + self::LABEL_SPACE_VERTICAL_GRAPH));
-
- $abscissaSeriesCount = count($this->abscissaSeries);
- if($maxNumOfLabels < $abscissaSeriesCount)
- {
- for($candidateSkippedLabels = 1 ; $candidateSkippedLabels < $abscissaSeriesCount; $candidateSkippedLabels++)
- {
- $numberOfSegments = $abscissaSeriesCount / ($candidateSkippedLabels + 1);
- $numberOfCompleteSegments = floor($numberOfSegments);
-
- $numberOfLabels = $numberOfCompleteSegments;
- if($numberOfSegments > $numberOfCompleteSegments)
- {
- $numberOfLabels++;
- }
-
- if($numberOfLabels <= $maxNumOfLabels )
- {
- $skippedLabels = $candidateSkippedLabels;
- break;
- }
- }
- }
-
- if($this->forceSkippedLabels
- && $skippedLabels
- && $skippedLabels < $this->forceSkippedLabels
- && $abscissaSeriesCount > $this->forceSkippedLabels + 1
- )
- {
- $skippedLabels = $this->forceSkippedLabels;
- }
- }
-
- $ordinateAxisLength =
- $horizontalGraph ? $bottomRightXValue - $topLeftXValue : $this->getGraphHeight($horizontalGraph, $verticalLegend);
-
- $maxOrdinateValue = 0;
- foreach($this->ordinateSeries as $column => $data)
- {
- $currentMax = $this->pData->getMax($column);
-
- if($currentMax > $maxOrdinateValue)
- {
- $maxOrdinateValue = $currentMax;
- }
- }
-
- // rounding top scale value to the next multiple of 10
- if($maxOrdinateValue > 10)
- {
- $modTen = $maxOrdinateValue % 10;
- if($modTen) $maxOrdinateValue += 10 - $modTen;
- }
-
- $gridColor = $this->colors[self::GRID_COLOR_KEY];
- $this->pImage->drawScale(
- array(
- 'Mode' => SCALE_MODE_MANUAL,
- 'GridTicks' => 0,
- 'LabelSkip' => $skippedLabels,
- 'DrawXLines' => $displayVerticalGridLines,
- 'Factors' => array(ceil($maxOrdinateValue / 2)),
- 'MinDivHeight' => $ordinateAxisLength / 2,
- 'AxisAlpha' => 0,
- 'SkippedAxisAlpha' => 0,
- 'TickAlpha' => $showTicks ? self::DEFAULT_TICK_ALPHA : 0,
- 'InnerTickWidth' => self::INNER_TICK_WIDTH,
- 'OuterTickWidth' => self::OUTER_TICK_WIDTH,
- 'GridR' => $gridColor['R'],
- 'GridG' => $gridColor['G'],
- 'GridB' => $gridColor['B'],
- 'GridAlpha' => 100,
- 'ManualScale' => array(
- 0 => array(
- 'Min' => 0,
- 'Max' => $maxOrdinateValue
- )
- ),
- 'Pos' => $horizontalGraph ? SCALE_POS_TOPBOTTOM : SCALE_POS_LEFTRIGHT,
- )
- );
-
- if($this->showLegend)
- {
- switch($bulletType)
- {
- case LEGEND_FAMILY_LINE:
- $bulletWidth = self::LEGEND_LINE_BULLET_WIDTH;
-
- // measured using a picture editing software
- $iconOffsetAboveLabelSymmetryAxis = -2;
- break;
-
- case LEGEND_FAMILY_BOX:
- $bulletWidth = self::LEGEND_BOX_BULLET_WIDTH;
-
- // measured using a picture editing software
- $iconOffsetAboveLabelSymmetryAxis = 3;
- break;
- }
-
- // pChart requires two coordinates to draw the legend $legendTopLeftXValue & $legendTopLeftYValue
- // $legendTopLeftXValue = legend's left padding
- $legendTopLeftXValue = $topLeftXValue + ($verticalLegend ? self::VERTICAL_LEGEND_LEFT_MARGIN : self::HORIZONTAL_LEGEND_LEFT_MARGIN);
-
- // $legendTopLeftYValue = y coordinate of the top edge of the legend's icons
- // Caution :
- // - pChart will silently add some value (see $paddingAddedByPChart) to $legendTopLeftYValue depending on multiple criterias
- // - pChart will not take into account the size of the text. Setting $legendTopLeftYValue = 0 will crop the legend's labels
- // The following section of code determines the value of $legendTopLeftYValue while taking into account the following paremeters :
- // - whether legend items have icons
- // - whether icons are bigger than the legend's labels
- // - how much colored shadow padding is required
- list($maxLogoWidth, $maxLogoHeight) = self::getMaxLogoSize(array_values($this->ordinateLogos));
- if($maxLogoHeight >= $this->legendFontSize)
- {
- $heightOfTextAboveBulletTop = 0;
- $paddingCreatedByLogo = $maxLogoHeight - $this->legendFontSize;
- $effectiveShadowPadding = $paddingCreatedByLogo < self::LEGEND_VERTICAL_SHADOW_PADDING * 2 ? self::LEGEND_VERTICAL_SHADOW_PADDING - ($paddingCreatedByLogo / 2) : 0;
- }
- else
- {
- if($maxLogoHeight)
- {
- // measured using a picture editing software
- $iconOffsetAboveLabelSymmetryAxis = 5;
- }
- $heightOfTextAboveBulletTop = $this->legendFontSize / 2 - $iconOffsetAboveLabelSymmetryAxis;
- $effectiveShadowPadding = self::LEGEND_VERTICAL_SHADOW_PADDING;
- }
-
- $effectiveLegendItemVerticalInterstice = $this->legendFontSize + self::LEGEND_ITEM_VERTICAL_INTERSTICE_OFFSET;
- $effectiveLegendItemHorizontalInterstice = self::LEGEND_ITEM_HORIZONTAL_INTERSTICE + self::LEGEND_HORIZONTAL_SHADOW_PADDING;
-
- $legendTopMargin = $verticalLegend ? self::VERTICAL_LEGEND_TOP_MARGIN : self::HORIZONTAL_LEGEND_TOP_MARGIN;
- $requiredPaddingAboveItemBullet = $legendTopMargin + $heightOfTextAboveBulletTop + $effectiveShadowPadding;
-
- $paddingAddedByPChart = 0;
- if($verticalLegend)
- {
- if($maxLogoHeight)
- {
- // see line 1691 of pDraw.class.php
- if($maxLogoHeight < $effectiveLegendItemVerticalInterstice)
- {
- $paddingAddedByPChart = ($effectiveLegendItemVerticalInterstice / 2) - ($maxLogoHeight / 2);
- }
- }
- else
- {
- // see line 1711 of pDraw.class.php ($Y+$IconAreaHeight/2)
- $paddingAddedByPChart = $effectiveLegendItemVerticalInterstice / 2;
- }
- }
-
- $legendTopLeftYValue = $paddingAddedByPChart < $requiredPaddingAboveItemBullet ? $requiredPaddingAboveItemBullet - $paddingAddedByPChart : 0;
-
- // add colored background to each legend item
- if(count($this->ordinateLabels) > 1)
- {
- $currentPosition = $verticalLegend ? $legendTopMargin : $legendTopLeftXValue;
- $colorIndex = 1;
- foreach($this->ordinateLabels as $metricCode => &$label)
- {
- $color = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++];
-
- $paddedBulletWidth = $bulletWidth;
- if(isset($this->ordinateLogos[$metricCode]))
- {
- $paddedBulletWidth = $maxLogoWidth;
- }
- $paddedBulletWidth += self::LEGEND_BULLET_RIGHT_PADDING;
-
- // truncate labels if required
- if($verticalLegend)
- {
- $label = $this->truncateLabel($label, ($this->width * self::VERTICAL_LEGEND_MAX_WIDTH_PCT) - $legendTopLeftXValue - $paddedBulletWidth, $this->legendFontSize);
- $this->pData->setSerieDescription($metricCode, $label);
- }
-
- $rectangleTopLeftXValue = ($verticalLegend ? $legendTopLeftXValue : $currentPosition) + $paddedBulletWidth - self::LEGEND_HORIZONTAL_SHADOW_PADDING;
- $rectangleTopLeftYValue = $verticalLegend ? $currentPosition : $legendTopMargin;
-
- list($labelWidth, $labelHeight) = $this->getTextWidthHeight($label, $this->legendFontSize);
- $legendItemWidth = $paddedBulletWidth + $labelWidth + $effectiveLegendItemHorizontalInterstice;
- $rectangleBottomRightXValue = $rectangleTopLeftXValue + $labelWidth + (self::LEGEND_HORIZONTAL_SHADOW_PADDING * 2);
-
- $legendItemHeight = max($maxLogoHeight, $this->legendFontSize) + ($effectiveShadowPadding * 2);
- $rectangleBottomRightYValue = $rectangleTopLeftYValue + $legendItemHeight;
-
- $this->pImage->drawFilledRectangle(
- $rectangleTopLeftXValue,
- $rectangleTopLeftYValue,
- $rectangleBottomRightXValue,
- $rectangleBottomRightYValue,
- array(
- 'Alpha' => self::LEGEND_SHADOW_OPACITY,
- 'R' => $color['R'],
- 'G' => $color['G'],
- 'B' => $color['B'],
- )
- );
-
- if($verticalLegend)
- {
- $currentPositionIncrement = max($maxLogoHeight, $effectiveLegendItemVerticalInterstice, $this->legendFontSize) + self::PCHART_HARD_CODED_VERTICAL_LEGEND_INTERSTICE;
- }
- else
- {
- $currentPositionIncrement = $legendItemWidth;
- }
-
- $currentPosition += $currentPositionIncrement;
- }
- }
-
- // draw legend
- $legendColor = $this->colors[self::VALUE_COLOR_KEY];
- $this->pImage->drawLegend(
- $legendTopLeftXValue,
- $legendTopLeftYValue,
- array(
- 'Style' => LEGEND_NOBORDER,
- 'FontSize' => $this->legendFontSize,
- 'BoxWidth' => $bulletWidth,
- 'XSpacing' => $effectiveLegendItemHorizontalInterstice, // not effective when vertical
- 'Mode' => $verticalLegend ? LEGEND_VERTICAL : LEGEND_HORIZONTAL,
- 'BoxHeight' => $verticalLegend ? $effectiveLegendItemVerticalInterstice : null,
- 'Family' => $bulletType,
- 'FontR' => $legendColor['R'],
- 'FontG' => $legendColor['G'],
- 'FontB' => $legendColor['B'],
- )
- );
- }
- }
-
- protected static function getMaxLogoSize($logoPaths)
- {
- $maxLogoWidth = 0;
- $maxLogoHeight = 0;
- foreach($logoPaths as $logoPath)
- {
- list($logoWidth, $logoHeight) = self::getLogoSize($logoPath);
-
- if($logoWidth > $maxLogoWidth)
- {
- $maxLogoWidth = $logoWidth;
- }
- if($logoHeight > $maxLogoHeight)
- {
- $maxLogoHeight = $logoHeight;
- }
- }
-
- return array($maxLogoWidth, $maxLogoHeight);
- }
-
- protected static function getLogoSize($logoPath)
- {
- $pathInfo = getimagesize($logoPath);
- return array($pathInfo[0], $pathInfo[1]);
- }
-
- protected function getGridLeftMargin($horizontalGraph, $withLabel)
- {
- $gridLeftMargin = self::LEFT_GRID_MARGIN + self::OUTER_TICK_WIDTH;
-
- if($withLabel)
- {
- list($maxTextWidth, $maxTextHeight) = $this->getMaximumTextWidthHeight($horizontalGraph ? $this->abscissaSeries : $this->ordinateSeries);
- $gridLeftMargin += $maxTextWidth;
- }
-
- return $gridLeftMargin;
- }
-
- protected function getGridTopMargin($horizontalGraph, $verticalLegend)
- {
- list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries);
-
- if($horizontalGraph)
- {
- $topMargin = $ordinateMaxHeight + self::TOP_GRID_MARGIN_HORIZONTAL_GRAPH + self::OUTER_TICK_WIDTH;
- }
- else
- {
- $topMargin = $ordinateMaxHeight / 2;
- }
-
- if($this->showLegend && !$verticalLegend)
- {
- $topMargin += $this->getHorizontalLegendHeight();
- }
-
- return $topMargin;
- }
-
- private function getHorizontalLegendHeight()
- {
- list($maxMetricLegendWidth, $maxMetricLegendHeight) =
- $this->getMaximumTextWidthHeight(array_values($this->ordinateLabels), $this->legendFontSize);
-
- return $maxMetricLegendHeight + self::HORIZONTAL_LEGEND_BOTTOM_MARGIN + self::HORIZONTAL_LEGEND_TOP_MARGIN;
- }
-
- protected function getGraphHeight($horizontalGraph, $verticalLegend)
- {
- return $this->getGraphBottom($horizontalGraph) - $this->getGridTopMargin($horizontalGraph, $verticalLegend);
- }
-
- private function getGridBottomMargin($horizontalGraph)
- {
- $gridBottomMargin = self::BOTTOM_GRID_MARGIN;
- if(!$horizontalGraph)
- {
- list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries);
- $gridBottomMargin += $abscissaMaxHeight;
- }
- return $gridBottomMargin;
- }
-
- protected function getGridRightMargin($horizontalGraph)
- {
- if($horizontalGraph)
- {
- // in horizontal graphs, metric values are displayed on the far right of the bar
- list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries);
- return self::RIGHT_GRID_MARGIN_HORIZONTAL_GRAPH + $ordinateMaxWidth;
- }
- else
- {
- return 0;
- }
- }
-
- protected function getGraphBottom($horizontalGraph)
- {
- return $this->height - $this->getGridBottomMargin($horizontalGraph);
- }
-
- protected function truncateLabel($label, $labelWidthLimit, $fontSize = false)
- {
- list($truncationTextWidth, $truncationTextHeight) = $this->getTextWidthHeight(self::TRUNCATION_TEXT, $fontSize);
- list($labelWidth, $labelHeight) = $this->getTextWidthHeight($label, $fontSize);
-
- if($labelWidth > $labelWidthLimit)
- {
- $averageCharWidth = $labelWidth / strlen($label);
- $charsToKeep = floor(($labelWidthLimit - $truncationTextWidth) / $averageCharWidth);
- $label = substr($label, 0, $charsToKeep) . self::TRUNCATION_TEXT;
- }
- return $label;
- }
-
- // display min & max values
- // can not currently be used because pChart's label design is not flexible enough
- // e.g: it is not possible to remove the box border & the square icon
- // it would require modifying pChart code base which we try to avoid
- // see http://dev.piwik.org/trac/ticket/3396
+ const GRAPHIC_COLOR_KEY = 'GRAPHIC_COLOR';
+ const VALUE_COLOR_KEY = 'VALUE_COLOR';
+ const GRID_COLOR_KEY = 'GRID_COLOR';
+
+ const TRUNCATION_TEXT = '...';
+
+ const DEFAULT_TICK_ALPHA = 20;
+ const DEFAULT_SERIE_WEIGHT = 0.5;
+ const LEFT_GRID_MARGIN = 4;
+ const BOTTOM_GRID_MARGIN = 10;
+ const TOP_GRID_MARGIN_HORIZONTAL_GRAPH = 1;
+ const RIGHT_GRID_MARGIN_HORIZONTAL_GRAPH = 4;
+ const OUTER_TICK_WIDTH = 5;
+ const INNER_TICK_WIDTH = 0;
+ const LABEL_SPACE_VERTICAL_GRAPH = 7;
+
+ const HORIZONTAL_LEGEND_TOP_MARGIN = 5;
+ const HORIZONTAL_LEGEND_LEFT_MARGIN = 10;
+ const HORIZONTAL_LEGEND_BOTTOM_MARGIN = 10;
+ const VERTICAL_LEGEND_TOP_MARGIN = 8;
+ const VERTICAL_LEGEND_LEFT_MARGIN = 6;
+ const VERTICAL_LEGEND_MAX_WIDTH_PCT = 0.70;
+ const LEGEND_LINE_BULLET_WIDTH = 14;
+ const LEGEND_BOX_BULLET_WIDTH = 5;
+ const LEGEND_BULLET_RIGHT_PADDING = 5;
+ const LEGEND_ITEM_HORIZONTAL_INTERSTICE = 6;
+ const LEGEND_ITEM_VERTICAL_INTERSTICE_OFFSET = 4;
+ const LEGEND_SHADOW_OPACITY = 25;
+ const LEGEND_VERTICAL_SHADOW_PADDING = 3;
+ const LEGEND_HORIZONTAL_SHADOW_PADDING = 2;
+ const PCHART_HARD_CODED_VERTICAL_LEGEND_INTERSTICE = 5;
+
+ protected function getDefaultColors()
+ {
+ return array(
+ self::VALUE_COLOR_KEY => '444444',
+ self::GRID_COLOR_KEY => 'CCCCCC',
+ self::GRAPHIC_COLOR_KEY . '1' => '5170AE',
+ self::GRAPHIC_COLOR_KEY . '2' => 'F29007',
+ self::GRAPHIC_COLOR_KEY . '3' => 'CC3399',
+ self::GRAPHIC_COLOR_KEY . '4' => '9933CC',
+ self::GRAPHIC_COLOR_KEY . '5' => '80A033',
+ self::GRAPHIC_COLOR_KEY . '6' => '246AD2'
+ );
+ }
+
+ protected function initGridChart(
+ $displayVerticalGridLines,
+ $bulletType,
+ $horizontalGraph,
+ $showTicks,
+ $verticalLegend
+ )
+ {
+ $this->initpData();
+
+ $colorIndex = 1;
+ foreach ($this->ordinateSeries as $column => $data) {
+ $this->pData->setSerieWeight($column, self::DEFAULT_SERIE_WEIGHT);
+ $graphicColor = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++];
+ $this->pData->setPalette($column, $graphicColor);
+ }
+
+ $this->initpImage();
+
+ // graph area coordinates
+ $topLeftXValue = $this->getGridLeftMargin($horizontalGraph, $withLabel = true);
+ $topLeftYValue = $this->getGridTopMargin($horizontalGraph, $verticalLegend);
+ $bottomRightXValue = $this->width - $this->getGridRightMargin($horizontalGraph);
+ $bottomRightYValue = $this->getGraphBottom($horizontalGraph);
+
+ $this->pImage->setGraphArea(
+ $topLeftXValue,
+ $topLeftYValue,
+ $bottomRightXValue,
+ $bottomRightYValue
+ );
+
+ // determine how many labels need to be skipped
+ $skippedLabels = 0;
+ if (!$horizontalGraph) {
+ list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries);
+ $graphWidth = $bottomRightXValue - $topLeftXValue;
+ $maxNumOfLabels = floor($graphWidth / ($abscissaMaxWidth + self::LABEL_SPACE_VERTICAL_GRAPH));
+
+ $abscissaSeriesCount = count($this->abscissaSeries);
+ if ($maxNumOfLabels < $abscissaSeriesCount) {
+ for ($candidateSkippedLabels = 1; $candidateSkippedLabels < $abscissaSeriesCount; $candidateSkippedLabels++) {
+ $numberOfSegments = $abscissaSeriesCount / ($candidateSkippedLabels + 1);
+ $numberOfCompleteSegments = floor($numberOfSegments);
+
+ $numberOfLabels = $numberOfCompleteSegments;
+ if ($numberOfSegments > $numberOfCompleteSegments) {
+ $numberOfLabels++;
+ }
+
+ if ($numberOfLabels <= $maxNumOfLabels) {
+ $skippedLabels = $candidateSkippedLabels;
+ break;
+ }
+ }
+ }
+
+ if ($this->forceSkippedLabels
+ && $skippedLabels
+ && $skippedLabels < $this->forceSkippedLabels
+ && $abscissaSeriesCount > $this->forceSkippedLabels + 1
+ ) {
+ $skippedLabels = $this->forceSkippedLabels;
+ }
+ }
+
+ $ordinateAxisLength =
+ $horizontalGraph ? $bottomRightXValue - $topLeftXValue : $this->getGraphHeight($horizontalGraph, $verticalLegend);
+
+ $maxOrdinateValue = 0;
+ foreach ($this->ordinateSeries as $column => $data) {
+ $currentMax = $this->pData->getMax($column);
+
+ if ($currentMax > $maxOrdinateValue) {
+ $maxOrdinateValue = $currentMax;
+ }
+ }
+
+ // rounding top scale value to the next multiple of 10
+ if ($maxOrdinateValue > 10) {
+ $modTen = $maxOrdinateValue % 10;
+ if ($modTen) $maxOrdinateValue += 10 - $modTen;
+ }
+
+ $gridColor = $this->colors[self::GRID_COLOR_KEY];
+ $this->pImage->drawScale(
+ array(
+ 'Mode' => SCALE_MODE_MANUAL,
+ 'GridTicks' => 0,
+ 'LabelSkip' => $skippedLabels,
+ 'DrawXLines' => $displayVerticalGridLines,
+ 'Factors' => array(ceil($maxOrdinateValue / 2)),
+ 'MinDivHeight' => $ordinateAxisLength / 2,
+ 'AxisAlpha' => 0,
+ 'SkippedAxisAlpha' => 0,
+ 'TickAlpha' => $showTicks ? self::DEFAULT_TICK_ALPHA : 0,
+ 'InnerTickWidth' => self::INNER_TICK_WIDTH,
+ 'OuterTickWidth' => self::OUTER_TICK_WIDTH,
+ 'GridR' => $gridColor['R'],
+ 'GridG' => $gridColor['G'],
+ 'GridB' => $gridColor['B'],
+ 'GridAlpha' => 100,
+ 'ManualScale' => array(
+ 0 => array(
+ 'Min' => 0,
+ 'Max' => $maxOrdinateValue
+ )
+ ),
+ 'Pos' => $horizontalGraph ? SCALE_POS_TOPBOTTOM : SCALE_POS_LEFTRIGHT,
+ )
+ );
+
+ if ($this->showLegend) {
+ switch ($bulletType) {
+ case LEGEND_FAMILY_LINE:
+ $bulletWidth = self::LEGEND_LINE_BULLET_WIDTH;
+
+ // measured using a picture editing software
+ $iconOffsetAboveLabelSymmetryAxis = -2;
+ break;
+
+ case LEGEND_FAMILY_BOX:
+ $bulletWidth = self::LEGEND_BOX_BULLET_WIDTH;
+
+ // measured using a picture editing software
+ $iconOffsetAboveLabelSymmetryAxis = 3;
+ break;
+ }
+
+ // pChart requires two coordinates to draw the legend $legendTopLeftXValue & $legendTopLeftYValue
+ // $legendTopLeftXValue = legend's left padding
+ $legendTopLeftXValue = $topLeftXValue + ($verticalLegend ? self::VERTICAL_LEGEND_LEFT_MARGIN : self::HORIZONTAL_LEGEND_LEFT_MARGIN);
+
+ // $legendTopLeftYValue = y coordinate of the top edge of the legend's icons
+ // Caution :
+ // - pChart will silently add some value (see $paddingAddedByPChart) to $legendTopLeftYValue depending on multiple criterias
+ // - pChart will not take into account the size of the text. Setting $legendTopLeftYValue = 0 will crop the legend's labels
+ // The following section of code determines the value of $legendTopLeftYValue while taking into account the following paremeters :
+ // - whether legend items have icons
+ // - whether icons are bigger than the legend's labels
+ // - how much colored shadow padding is required
+ list($maxLogoWidth, $maxLogoHeight) = self::getMaxLogoSize(array_values($this->ordinateLogos));
+ if ($maxLogoHeight >= $this->legendFontSize) {
+ $heightOfTextAboveBulletTop = 0;
+ $paddingCreatedByLogo = $maxLogoHeight - $this->legendFontSize;
+ $effectiveShadowPadding = $paddingCreatedByLogo < self::LEGEND_VERTICAL_SHADOW_PADDING * 2 ? self::LEGEND_VERTICAL_SHADOW_PADDING - ($paddingCreatedByLogo / 2) : 0;
+ } else {
+ if ($maxLogoHeight) {
+ // measured using a picture editing software
+ $iconOffsetAboveLabelSymmetryAxis = 5;
+ }
+ $heightOfTextAboveBulletTop = $this->legendFontSize / 2 - $iconOffsetAboveLabelSymmetryAxis;
+ $effectiveShadowPadding = self::LEGEND_VERTICAL_SHADOW_PADDING;
+ }
+
+ $effectiveLegendItemVerticalInterstice = $this->legendFontSize + self::LEGEND_ITEM_VERTICAL_INTERSTICE_OFFSET;
+ $effectiveLegendItemHorizontalInterstice = self::LEGEND_ITEM_HORIZONTAL_INTERSTICE + self::LEGEND_HORIZONTAL_SHADOW_PADDING;
+
+ $legendTopMargin = $verticalLegend ? self::VERTICAL_LEGEND_TOP_MARGIN : self::HORIZONTAL_LEGEND_TOP_MARGIN;
+ $requiredPaddingAboveItemBullet = $legendTopMargin + $heightOfTextAboveBulletTop + $effectiveShadowPadding;
+
+ $paddingAddedByPChart = 0;
+ if ($verticalLegend) {
+ if ($maxLogoHeight) {
+ // see line 1691 of pDraw.class.php
+ if ($maxLogoHeight < $effectiveLegendItemVerticalInterstice) {
+ $paddingAddedByPChart = ($effectiveLegendItemVerticalInterstice / 2) - ($maxLogoHeight / 2);
+ }
+ } else {
+ // see line 1711 of pDraw.class.php ($Y+$IconAreaHeight/2)
+ $paddingAddedByPChart = $effectiveLegendItemVerticalInterstice / 2;
+ }
+ }
+
+ $legendTopLeftYValue = $paddingAddedByPChart < $requiredPaddingAboveItemBullet ? $requiredPaddingAboveItemBullet - $paddingAddedByPChart : 0;
+
+ // add colored background to each legend item
+ if (count($this->ordinateLabels) > 1) {
+ $currentPosition = $verticalLegend ? $legendTopMargin : $legendTopLeftXValue;
+ $colorIndex = 1;
+ foreach ($this->ordinateLabels as $metricCode => &$label) {
+ $color = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++];
+
+ $paddedBulletWidth = $bulletWidth;
+ if (isset($this->ordinateLogos[$metricCode])) {
+ $paddedBulletWidth = $maxLogoWidth;
+ }
+ $paddedBulletWidth += self::LEGEND_BULLET_RIGHT_PADDING;
+
+ // truncate labels if required
+ if ($verticalLegend) {
+ $label = $this->truncateLabel($label, ($this->width * self::VERTICAL_LEGEND_MAX_WIDTH_PCT) - $legendTopLeftXValue - $paddedBulletWidth, $this->legendFontSize);
+ $this->pData->setSerieDescription($metricCode, $label);
+ }
+
+ $rectangleTopLeftXValue = ($verticalLegend ? $legendTopLeftXValue : $currentPosition) + $paddedBulletWidth - self::LEGEND_HORIZONTAL_SHADOW_PADDING;
+ $rectangleTopLeftYValue = $verticalLegend ? $currentPosition : $legendTopMargin;
+
+ list($labelWidth, $labelHeight) = $this->getTextWidthHeight($label, $this->legendFontSize);
+ $legendItemWidth = $paddedBulletWidth + $labelWidth + $effectiveLegendItemHorizontalInterstice;
+ $rectangleBottomRightXValue = $rectangleTopLeftXValue + $labelWidth + (self::LEGEND_HORIZONTAL_SHADOW_PADDING * 2);
+
+ $legendItemHeight = max($maxLogoHeight, $this->legendFontSize) + ($effectiveShadowPadding * 2);
+ $rectangleBottomRightYValue = $rectangleTopLeftYValue + $legendItemHeight;
+
+ $this->pImage->drawFilledRectangle(
+ $rectangleTopLeftXValue,
+ $rectangleTopLeftYValue,
+ $rectangleBottomRightXValue,
+ $rectangleBottomRightYValue,
+ array(
+ 'Alpha' => self::LEGEND_SHADOW_OPACITY,
+ 'R' => $color['R'],
+ 'G' => $color['G'],
+ 'B' => $color['B'],
+ )
+ );
+
+ if ($verticalLegend) {
+ $currentPositionIncrement = max($maxLogoHeight, $effectiveLegendItemVerticalInterstice, $this->legendFontSize) + self::PCHART_HARD_CODED_VERTICAL_LEGEND_INTERSTICE;
+ } else {
+ $currentPositionIncrement = $legendItemWidth;
+ }
+
+ $currentPosition += $currentPositionIncrement;
+ }
+ }
+
+ // draw legend
+ $legendColor = $this->colors[self::VALUE_COLOR_KEY];
+ $this->pImage->drawLegend(
+ $legendTopLeftXValue,
+ $legendTopLeftYValue,
+ array(
+ 'Style' => LEGEND_NOBORDER,
+ 'FontSize' => $this->legendFontSize,
+ 'BoxWidth' => $bulletWidth,
+ 'XSpacing' => $effectiveLegendItemHorizontalInterstice, // not effective when vertical
+ 'Mode' => $verticalLegend ? LEGEND_VERTICAL : LEGEND_HORIZONTAL,
+ 'BoxHeight' => $verticalLegend ? $effectiveLegendItemVerticalInterstice : null,
+ 'Family' => $bulletType,
+ 'FontR' => $legendColor['R'],
+ 'FontG' => $legendColor['G'],
+ 'FontB' => $legendColor['B'],
+ )
+ );
+ }
+ }
+
+ protected static function getMaxLogoSize($logoPaths)
+ {
+ $maxLogoWidth = 0;
+ $maxLogoHeight = 0;
+ foreach ($logoPaths as $logoPath) {
+ list($logoWidth, $logoHeight) = self::getLogoSize($logoPath);
+
+ if ($logoWidth > $maxLogoWidth) {
+ $maxLogoWidth = $logoWidth;
+ }
+ if ($logoHeight > $maxLogoHeight) {
+ $maxLogoHeight = $logoHeight;
+ }
+ }
+
+ return array($maxLogoWidth, $maxLogoHeight);
+ }
+
+ protected static function getLogoSize($logoPath)
+ {
+ $pathInfo = getimagesize($logoPath);
+ return array($pathInfo[0], $pathInfo[1]);
+ }
+
+ protected function getGridLeftMargin($horizontalGraph, $withLabel)
+ {
+ $gridLeftMargin = self::LEFT_GRID_MARGIN + self::OUTER_TICK_WIDTH;
+
+ if ($withLabel) {
+ list($maxTextWidth, $maxTextHeight) = $this->getMaximumTextWidthHeight($horizontalGraph ? $this->abscissaSeries : $this->ordinateSeries);
+ $gridLeftMargin += $maxTextWidth;
+ }
+
+ return $gridLeftMargin;
+ }
+
+ protected function getGridTopMargin($horizontalGraph, $verticalLegend)
+ {
+ list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries);
+
+ if ($horizontalGraph) {
+ $topMargin = $ordinateMaxHeight + self::TOP_GRID_MARGIN_HORIZONTAL_GRAPH + self::OUTER_TICK_WIDTH;
+ } else {
+ $topMargin = $ordinateMaxHeight / 2;
+ }
+
+ if ($this->showLegend && !$verticalLegend) {
+ $topMargin += $this->getHorizontalLegendHeight();
+ }
+
+ return $topMargin;
+ }
+
+ private function getHorizontalLegendHeight()
+ {
+ list($maxMetricLegendWidth, $maxMetricLegendHeight) =
+ $this->getMaximumTextWidthHeight(array_values($this->ordinateLabels), $this->legendFontSize);
+
+ return $maxMetricLegendHeight + self::HORIZONTAL_LEGEND_BOTTOM_MARGIN + self::HORIZONTAL_LEGEND_TOP_MARGIN;
+ }
+
+ protected function getGraphHeight($horizontalGraph, $verticalLegend)
+ {
+ return $this->getGraphBottom($horizontalGraph) - $this->getGridTopMargin($horizontalGraph, $verticalLegend);
+ }
+
+ private function getGridBottomMargin($horizontalGraph)
+ {
+ $gridBottomMargin = self::BOTTOM_GRID_MARGIN;
+ if (!$horizontalGraph) {
+ list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries);
+ $gridBottomMargin += $abscissaMaxHeight;
+ }
+ return $gridBottomMargin;
+ }
+
+ protected function getGridRightMargin($horizontalGraph)
+ {
+ if ($horizontalGraph) {
+ // in horizontal graphs, metric values are displayed on the far right of the bar
+ list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries);
+ return self::RIGHT_GRID_MARGIN_HORIZONTAL_GRAPH + $ordinateMaxWidth;
+ } else {
+ return 0;
+ }
+ }
+
+ protected function getGraphBottom($horizontalGraph)
+ {
+ return $this->height - $this->getGridBottomMargin($horizontalGraph);
+ }
+
+ protected function truncateLabel($label, $labelWidthLimit, $fontSize = false)
+ {
+ list($truncationTextWidth, $truncationTextHeight) = $this->getTextWidthHeight(self::TRUNCATION_TEXT, $fontSize);
+ list($labelWidth, $labelHeight) = $this->getTextWidthHeight($label, $fontSize);
+
+ if ($labelWidth > $labelWidthLimit) {
+ $averageCharWidth = $labelWidth / strlen($label);
+ $charsToKeep = floor(($labelWidthLimit - $truncationTextWidth) / $averageCharWidth);
+ $label = substr($label, 0, $charsToKeep) . self::TRUNCATION_TEXT;
+ }
+ return $label;
+ }
+
+ // display min & max values
+ // can not currently be used because pChart's label design is not flexible enough
+ // e.g: it is not possible to remove the box border & the square icon
+ // it would require modifying pChart code base which we try to avoid
+ // see http://dev.piwik.org/trac/ticket/3396
// protected function displayMinMaxValues()
// {
// if($displayMinMax)
diff --git a/plugins/ImageGraph/StaticGraph/HorizontalBar.php b/plugins/ImageGraph/StaticGraph/HorizontalBar.php
index e531239522..0fd8d46890 100644
--- a/plugins/ImageGraph/StaticGraph/HorizontalBar.php
+++ b/plugins/ImageGraph/StaticGraph/HorizontalBar.php
@@ -16,188 +16,170 @@
*/
class Piwik_ImageGraph_StaticGraph_HorizontalBar extends Piwik_ImageGraph_StaticGraph_GridGraph
{
- const INTERLEAVE = 0.30;
- const PADDING_CHARS = ' ';
- const LEGEND_SQUARE_WIDTH = 11;
- const MIN_SPACE_BETWEEN_HORIZONTAL_VALUES = 5;
- const LOGO_MIN_RIGHT_MARGIN = 3;
-
- public function renderGraph()
- {
- $verticalLegend = false;
-
- // determine the maximum logo width & height
- list($maxLogoWidth, $maxLogoHeight) = self::getMaxLogoSize($this->abscissaLogos);
-
- foreach($this->abscissaLogos as $logoPath)
- {
- list($logoWidth, $logoHeight) = self::getLogoSize($logoPath);
- $logoPathToHeight[$logoPath] = $logoHeight;
- }
-
- // truncate report
- $graphHeight = $this->getGraphBottom($horizontalGraph = true) - $this->getGridTopMargin($horizontalGraph = true, $verticalLegend);
-
- list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries);
- list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries);
-
- $numberOfSeries = count($this->ordinateSeries);
- $ordinateMaxHeight = $ordinateMaxHeight * $numberOfSeries;
-
- $textMaxHeight = $abscissaMaxHeight > $ordinateMaxHeight ? $abscissaMaxHeight : $ordinateMaxHeight;
-
- $minLineWidth = ($textMaxHeight > $maxLogoHeight ? $textMaxHeight : $maxLogoHeight) + (self::MIN_SPACE_BETWEEN_HORIZONTAL_VALUES * $numberOfSeries);
- $maxNumOfValues = floor($graphHeight / $minLineWidth);
- $abscissaSeriesCount = count($this->abscissaSeries);
-
- if($maxNumOfValues < $abscissaSeriesCount - 1)
- {
- $sumOfOthers = array();
- $truncatedOrdinateSeries = array();
- $truncatedAbscissaLogos = array();
- $truncatedAbscissaSeries = array();
- foreach($this->ordinateSeries as $column => $data)
- {
- $truncatedOrdinateSeries[$column] = array();
- $sumOfOthers[$column] = 0;
- }
-
- $i = 0;
- for(; $i < $maxNumOfValues; $i++)
- {
- foreach($this->ordinateSeries as $column => $data)
- {
- $truncatedOrdinateSeries[$column][] = $data[$i];
- }
-
- $truncatedAbscissaLogos[] = isset($this->abscissaLogos[$i]) ? $this->abscissaLogos[$i] : null;
- $truncatedAbscissaSeries[] = $this->abscissaSeries[$i];
- }
-
- for(; $i < $abscissaSeriesCount; $i++)
- {
- foreach($this->ordinateSeries as $column => $data)
- {
- $sumOfOthers[$column] += $data[$i];
- }
- }
-
- foreach($this->ordinateSeries as $column => $data)
- {
- $truncatedOrdinateSeries[$column][] = $sumOfOthers[$column];
- }
-
- $truncatedAbscissaSeries[] = Piwik_Translate('General_Others');
- $this->abscissaSeries = $truncatedAbscissaSeries;
- $this->ordinateSeries = $truncatedOrdinateSeries;
- $this->abscissaLogos = $truncatedAbscissaLogos;
- }
-
- // blank characters are used to pad labels so the logo can be displayed
- $paddingText = '';
- $paddingWidth = 0;
- if($maxLogoWidth > 0)
- {
- while($paddingWidth < $maxLogoWidth + self::LOGO_MIN_RIGHT_MARGIN)
- {
- $paddingText .= self::PADDING_CHARS;
- list($paddingWidth, $paddingHeight) = $this->getTextWidthHeight($paddingText);
- }
- }
-
- // determine the maximum label width according to the minimum comfortable graph size
- $gridRightMargin = $this->getGridRightMargin($horizontalGraph = true);
- $minGraphSize = ($this->width - $gridRightMargin) / 2;
-
- $metricLegendWidth = 0;
- foreach($this->ordinateLabels as $column => $label)
- {
- list($textWidth, $textHeight) = $this->getTextWidthHeight($label);
- $metricLegendWidth += $textWidth;
- }
-
- $legendWidth = $metricLegendWidth + ((self::HORIZONTAL_LEGEND_LEFT_MARGIN + self::LEGEND_SQUARE_WIDTH) * $numberOfSeries);
- if($this->showLegend)
- {
- if($legendWidth > $minGraphSize)
- {
- $minGraphSize = $legendWidth;
- }
- }
-
- $gridLeftMarginWithoutLabels = $this->getGridLeftMargin($horizontalGraph = true, $withLabel = false);
- $labelWidthLimit =
- $this->width
- - $gridLeftMarginWithoutLabels
- - $gridRightMargin
- - $paddingWidth
- - $minGraphSize;
-
- // truncate labels if needed
- foreach($this->abscissaSeries as &$label)
- {
- $label = $this->truncateLabel($label, $labelWidthLimit);
- }
-
- $gridLeftMarginBeforePadding = $this->getGridLeftMargin($horizontalGraph = true, $withLabel = true);
-
- // pad labels for logo space
- foreach($this->abscissaSeries as &$label)
- {
- $label .= $paddingText;
- }
-
- $this->initGridChart(
- $displayVerticalGridLines = false,
- $bulletType = LEGEND_FAMILY_BOX,
- $horizontalGraph = true,
- $showTicks = false,
- $verticalLegend
- );
-
- $valueColor = $this->colors[self::VALUE_COLOR_KEY];
- $this->pImage->drawBarChart(
- array(
- 'DisplayValues' => true,
- 'Interleave' => self::INTERLEAVE,
- 'DisplayR' => $valueColor['R'],
- 'DisplayG' => $valueColor['G'],
- 'DisplayB' => $valueColor['B'],
- )
- );
+ const INTERLEAVE = 0.30;
+ const PADDING_CHARS = ' ';
+ const LEGEND_SQUARE_WIDTH = 11;
+ const MIN_SPACE_BETWEEN_HORIZONTAL_VALUES = 5;
+ const LOGO_MIN_RIGHT_MARGIN = 3;
+
+ public function renderGraph()
+ {
+ $verticalLegend = false;
+
+ // determine the maximum logo width & height
+ list($maxLogoWidth, $maxLogoHeight) = self::getMaxLogoSize($this->abscissaLogos);
+
+ foreach ($this->abscissaLogos as $logoPath) {
+ list($logoWidth, $logoHeight) = self::getLogoSize($logoPath);
+ $logoPathToHeight[$logoPath] = $logoHeight;
+ }
+
+ // truncate report
+ $graphHeight = $this->getGraphBottom($horizontalGraph = true) - $this->getGridTopMargin($horizontalGraph = true, $verticalLegend);
+
+ list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries);
+ list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries);
+
+ $numberOfSeries = count($this->ordinateSeries);
+ $ordinateMaxHeight = $ordinateMaxHeight * $numberOfSeries;
+
+ $textMaxHeight = $abscissaMaxHeight > $ordinateMaxHeight ? $abscissaMaxHeight : $ordinateMaxHeight;
+
+ $minLineWidth = ($textMaxHeight > $maxLogoHeight ? $textMaxHeight : $maxLogoHeight) + (self::MIN_SPACE_BETWEEN_HORIZONTAL_VALUES * $numberOfSeries);
+ $maxNumOfValues = floor($graphHeight / $minLineWidth);
+ $abscissaSeriesCount = count($this->abscissaSeries);
+
+ if ($maxNumOfValues < $abscissaSeriesCount - 1) {
+ $sumOfOthers = array();
+ $truncatedOrdinateSeries = array();
+ $truncatedAbscissaLogos = array();
+ $truncatedAbscissaSeries = array();
+ foreach ($this->ordinateSeries as $column => $data) {
+ $truncatedOrdinateSeries[$column] = array();
+ $sumOfOthers[$column] = 0;
+ }
+
+ $i = 0;
+ for (; $i < $maxNumOfValues; $i++) {
+ foreach ($this->ordinateSeries as $column => $data) {
+ $truncatedOrdinateSeries[$column][] = $data[$i];
+ }
+
+ $truncatedAbscissaLogos[] = isset($this->abscissaLogos[$i]) ? $this->abscissaLogos[$i] : null;
+ $truncatedAbscissaSeries[] = $this->abscissaSeries[$i];
+ }
+
+ for (; $i < $abscissaSeriesCount; $i++) {
+ foreach ($this->ordinateSeries as $column => $data) {
+ $sumOfOthers[$column] += $data[$i];
+ }
+ }
+
+ foreach ($this->ordinateSeries as $column => $data) {
+ $truncatedOrdinateSeries[$column][] = $sumOfOthers[$column];
+ }
+
+ $truncatedAbscissaSeries[] = Piwik_Translate('General_Others');
+ $this->abscissaSeries = $truncatedAbscissaSeries;
+ $this->ordinateSeries = $truncatedOrdinateSeries;
+ $this->abscissaLogos = $truncatedAbscissaLogos;
+ }
+
+ // blank characters are used to pad labels so the logo can be displayed
+ $paddingText = '';
+ $paddingWidth = 0;
+ if ($maxLogoWidth > 0) {
+ while ($paddingWidth < $maxLogoWidth + self::LOGO_MIN_RIGHT_MARGIN) {
+ $paddingText .= self::PADDING_CHARS;
+ list($paddingWidth, $paddingHeight) = $this->getTextWidthHeight($paddingText);
+ }
+ }
+
+ // determine the maximum label width according to the minimum comfortable graph size
+ $gridRightMargin = $this->getGridRightMargin($horizontalGraph = true);
+ $minGraphSize = ($this->width - $gridRightMargin) / 2;
+
+ $metricLegendWidth = 0;
+ foreach ($this->ordinateLabels as $column => $label) {
+ list($textWidth, $textHeight) = $this->getTextWidthHeight($label);
+ $metricLegendWidth += $textWidth;
+ }
+
+ $legendWidth = $metricLegendWidth + ((self::HORIZONTAL_LEGEND_LEFT_MARGIN + self::LEGEND_SQUARE_WIDTH) * $numberOfSeries);
+ if ($this->showLegend) {
+ if ($legendWidth > $minGraphSize) {
+ $minGraphSize = $legendWidth;
+ }
+ }
+
+ $gridLeftMarginWithoutLabels = $this->getGridLeftMargin($horizontalGraph = true, $withLabel = false);
+ $labelWidthLimit =
+ $this->width
+ - $gridLeftMarginWithoutLabels
+ - $gridRightMargin
+ - $paddingWidth
+ - $minGraphSize;
+
+ // truncate labels if needed
+ foreach ($this->abscissaSeries as &$label) {
+ $label = $this->truncateLabel($label, $labelWidthLimit);
+ }
+
+ $gridLeftMarginBeforePadding = $this->getGridLeftMargin($horizontalGraph = true, $withLabel = true);
+
+ // pad labels for logo space
+ foreach ($this->abscissaSeries as &$label) {
+ $label .= $paddingText;
+ }
+
+ $this->initGridChart(
+ $displayVerticalGridLines = false,
+ $bulletType = LEGEND_FAMILY_BOX,
+ $horizontalGraph = true,
+ $showTicks = false,
+ $verticalLegend
+ );
+
+ $valueColor = $this->colors[self::VALUE_COLOR_KEY];
+ $this->pImage->drawBarChart(
+ array(
+ 'DisplayValues' => true,
+ 'Interleave' => self::INTERLEAVE,
+ 'DisplayR' => $valueColor['R'],
+ 'DisplayG' => $valueColor['G'],
+ 'DisplayB' => $valueColor['B'],
+ )
+ );
// // display icons
- $graphData = $this->pData->getData();
- $numberOfRows = count($this->abscissaSeries);
- $logoInterleave = $this->getGraphHeight(true, $verticalLegend) / $numberOfRows;
- for($i = 0; $i < $numberOfRows; $i++)
- {
- if(isset($this->abscissaLogos[$i]))
- {
- $logoPath = $this->abscissaLogos[$i];
-
- if(isset($logoPathToHeight[$logoPath]))
- {
- $logoHeight = $logoPathToHeight[$logoPath];
-
- $pathInfo = pathinfo($logoPath);
- $logoExtension = strtoupper($pathInfo['extension']);
- $drawingFunction = 'drawFrom' . $logoExtension;
-
- $logoYPosition =
- ($logoInterleave * $i)
- + $this->getGridTopMargin(true, $verticalLegend)
- + $graphData['Axis'][1]['Margin']
- - $logoHeight / 2
- + 1;
-
- $this->pImage->$drawingFunction(
- $gridLeftMarginBeforePadding,
- $logoYPosition,
- $logoPath
- );
- }
- }
- }
- }
+ $graphData = $this->pData->getData();
+ $numberOfRows = count($this->abscissaSeries);
+ $logoInterleave = $this->getGraphHeight(true, $verticalLegend) / $numberOfRows;
+ for ($i = 0; $i < $numberOfRows; $i++) {
+ if (isset($this->abscissaLogos[$i])) {
+ $logoPath = $this->abscissaLogos[$i];
+
+ if (isset($logoPathToHeight[$logoPath])) {
+ $logoHeight = $logoPathToHeight[$logoPath];
+
+ $pathInfo = pathinfo($logoPath);
+ $logoExtension = strtoupper($pathInfo['extension']);
+ $drawingFunction = 'drawFrom' . $logoExtension;
+
+ $logoYPosition =
+ ($logoInterleave * $i)
+ + $this->getGridTopMargin(true, $verticalLegend)
+ + $graphData['Axis'][1]['Margin']
+ - $logoHeight / 2
+ + 1;
+
+ $this->pImage->$drawingFunction(
+ $gridLeftMarginBeforePadding,
+ $logoYPosition,
+ $logoPath
+ );
+ }
+ }
+ }
+ }
}
diff --git a/plugins/ImageGraph/StaticGraph/Pie.php b/plugins/ImageGraph/StaticGraph/Pie.php
index 5c01745edd..bdfbdef9ab 100644
--- a/plugins/ImageGraph/StaticGraph/Pie.php
+++ b/plugins/ImageGraph/StaticGraph/Pie.php
@@ -15,14 +15,14 @@
*/
class Piwik_ImageGraph_StaticGraph_Pie extends Piwik_ImageGraph_StaticGraph_PieGraph
{
- public function renderGraph()
- {
- $this->initPieGraph(false);
+ public function renderGraph()
+ {
+ $this->initPieGraph(false);
- $this->pieChart->draw2DPie(
- $this->xPosition,
- $this->yPosition,
- $this->pieConfig
- );
- }
+ $this->pieChart->draw2DPie(
+ $this->xPosition,
+ $this->yPosition,
+ $this->pieConfig
+ );
+ }
}
diff --git a/plugins/ImageGraph/StaticGraph/PieGraph.php b/plugins/ImageGraph/StaticGraph/PieGraph.php
index 383f605432..d2b8df51c5 100644
--- a/plugins/ImageGraph/StaticGraph/PieGraph.php
+++ b/plugins/ImageGraph/StaticGraph/PieGraph.php
@@ -17,118 +17,107 @@ require_once PIWIK_INCLUDE_PATH . "/libs/pChart2.1.3/class/pPie.class.php";
*/
abstract class Piwik_ImageGraph_StaticGraph_PieGraph extends Piwik_ImageGraph_StaticGraph
{
- const RADIUS_MARGIN = 40;
- const PIE_RIGHT_MARGIN = 20;
- const SECTOR_GAP = 2.5;
-
- const SLICE_COLOR_KEY = "SLICE_COLOR";
-
- protected $pieChart;
- protected $xPosition;
- protected $yPosition;
- protected $pieConfig;
-
- protected function getDefaultColors()
- {
- return array(
- self::SLICE_COLOR_KEY . '1' => '3C5A69',
- self::SLICE_COLOR_KEY . '2' => '679BB5',
- self::SLICE_COLOR_KEY . '3' => '695A3C',
- self::SLICE_COLOR_KEY . '4' => 'B58E67',
- self::SLICE_COLOR_KEY . '5' => '8AA68A',
- self::SLICE_COLOR_KEY . '6' => 'A4D2A6',
- );
- }
-
- protected function initPieGraph($showLegend)
- {
- $this->truncateSmallValues();
- $this->initpData();
- $this->initpImage();
-
- if ($this->height > $this->width)
- {
- $radius = ($this->width / 2) - self::RADIUS_MARGIN;
- }
- else
- {
- $radius = ($this->height / 2) - self::RADIUS_MARGIN;
- }
-
- $this->pieChart = new pPie($this->pImage, $this->pData);
-
- $numberOfSlices = count($this->abscissaSeries);
- $numberOfAvailableColors = count($this->colors);
- for($i = 0; $i < $numberOfSlices; $i++)
- {
- $this->pieChart->setSliceColor($i, $this->colors[self::SLICE_COLOR_KEY . (($i % $numberOfAvailableColors) + 1)]);
- }
-
- // max abscissa label width is used to set the pie right margin
- list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries);
-
- $this->xPosition = $this->width - $radius - $abscissaMaxWidth - self::PIE_RIGHT_MARGIN;
- $this->yPosition = $this->height / 2;
-
- if ($showLegend)
- {
- $this->pieChart->drawPieLegend(15, 40, array("Alpha" => 20));
- }
-
- $this->pieConfig =
- array(
- 'Radius' => $radius,
- 'DrawLabels' => true,
- 'DataGapAngle' => self::SECTOR_GAP,
- 'DataGapRadius' => self::SECTOR_GAP,
- );
- }
-
- /**
- * this method logic is close to Piwik's core filter_truncate.
- * it uses a threshold to determine if an abscissa value should be drawn on the PIE
- * discarded abscissa values are summed in the 'other' abscissa value
- *
- * if this process is not perform, pChart will draw pie slices that are too small to see
- */
- private function truncateSmallValues()
- {
- $metricColumns = array_keys($this->ordinateSeries);
- $metricColumn = $metricColumns[0];
-
- $ordinateValuesSum = 0;
- foreach($this->ordinateSeries[$metricColumn] as $ordinateValue)
- {
- $ordinateValuesSum += $ordinateValue;
- }
-
- $truncatedOrdinateSeries[$metricColumn] = array();
- $truncatedAbscissaSeries = array();
- $smallValuesSum = 0;
-
- $ordinateValuesCount = count($this->ordinateSeries[$metricColumn]);
- for($i = 0; $i < $ordinateValuesCount - 1 ; $i++)
- {
- $ordinateValue = $this->ordinateSeries[$metricColumn][$i];
- if($ordinateValue / $ordinateValuesSum > 0.01)
- {
- $truncatedOrdinateSeries[$metricColumn][] = $ordinateValue;
- $truncatedAbscissaSeries[] = $this->abscissaSeries[$i];
- }
- else
- {
- $smallValuesSum += $ordinateValue;
- }
- }
-
- $smallValuesSum += $this->ordinateSeries[$metricColumn][$ordinateValuesCount - 1];
- if(($smallValuesSum / $ordinateValuesSum) > 0.01)
- {
- $truncatedOrdinateSeries[$metricColumn][] = $smallValuesSum;
- $truncatedAbscissaSeries[] = Piwik_Translate('General_Others');
- }
-
- $this->ordinateSeries = $truncatedOrdinateSeries;
- $this->abscissaSeries = $truncatedAbscissaSeries;
- }
+ const RADIUS_MARGIN = 40;
+ const PIE_RIGHT_MARGIN = 20;
+ const SECTOR_GAP = 2.5;
+
+ const SLICE_COLOR_KEY = "SLICE_COLOR";
+
+ protected $pieChart;
+ protected $xPosition;
+ protected $yPosition;
+ protected $pieConfig;
+
+ protected function getDefaultColors()
+ {
+ return array(
+ self::SLICE_COLOR_KEY . '1' => '3C5A69',
+ self::SLICE_COLOR_KEY . '2' => '679BB5',
+ self::SLICE_COLOR_KEY . '3' => '695A3C',
+ self::SLICE_COLOR_KEY . '4' => 'B58E67',
+ self::SLICE_COLOR_KEY . '5' => '8AA68A',
+ self::SLICE_COLOR_KEY . '6' => 'A4D2A6',
+ );
+ }
+
+ protected function initPieGraph($showLegend)
+ {
+ $this->truncateSmallValues();
+ $this->initpData();
+ $this->initpImage();
+
+ if ($this->height > $this->width) {
+ $radius = ($this->width / 2) - self::RADIUS_MARGIN;
+ } else {
+ $radius = ($this->height / 2) - self::RADIUS_MARGIN;
+ }
+
+ $this->pieChart = new pPie($this->pImage, $this->pData);
+
+ $numberOfSlices = count($this->abscissaSeries);
+ $numberOfAvailableColors = count($this->colors);
+ for ($i = 0; $i < $numberOfSlices; $i++) {
+ $this->pieChart->setSliceColor($i, $this->colors[self::SLICE_COLOR_KEY . (($i % $numberOfAvailableColors) + 1)]);
+ }
+
+ // max abscissa label width is used to set the pie right margin
+ list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries);
+
+ $this->xPosition = $this->width - $radius - $abscissaMaxWidth - self::PIE_RIGHT_MARGIN;
+ $this->yPosition = $this->height / 2;
+
+ if ($showLegend) {
+ $this->pieChart->drawPieLegend(15, 40, array("Alpha" => 20));
+ }
+
+ $this->pieConfig =
+ array(
+ 'Radius' => $radius,
+ 'DrawLabels' => true,
+ 'DataGapAngle' => self::SECTOR_GAP,
+ 'DataGapRadius' => self::SECTOR_GAP,
+ );
+ }
+
+ /**
+ * this method logic is close to Piwik's core filter_truncate.
+ * it uses a threshold to determine if an abscissa value should be drawn on the PIE
+ * discarded abscissa values are summed in the 'other' abscissa value
+ *
+ * if this process is not perform, pChart will draw pie slices that are too small to see
+ */
+ private function truncateSmallValues()
+ {
+ $metricColumns = array_keys($this->ordinateSeries);
+ $metricColumn = $metricColumns[0];
+
+ $ordinateValuesSum = 0;
+ foreach ($this->ordinateSeries[$metricColumn] as $ordinateValue) {
+ $ordinateValuesSum += $ordinateValue;
+ }
+
+ $truncatedOrdinateSeries[$metricColumn] = array();
+ $truncatedAbscissaSeries = array();
+ $smallValuesSum = 0;
+
+ $ordinateValuesCount = count($this->ordinateSeries[$metricColumn]);
+ for ($i = 0; $i < $ordinateValuesCount - 1; $i++) {
+ $ordinateValue = $this->ordinateSeries[$metricColumn][$i];
+ if ($ordinateValue / $ordinateValuesSum > 0.01) {
+ $truncatedOrdinateSeries[$metricColumn][] = $ordinateValue;
+ $truncatedAbscissaSeries[] = $this->abscissaSeries[$i];
+ } else {
+ $smallValuesSum += $ordinateValue;
+ }
+ }
+
+ $smallValuesSum += $this->ordinateSeries[$metricColumn][$ordinateValuesCount - 1];
+ if (($smallValuesSum / $ordinateValuesSum) > 0.01) {
+ $truncatedOrdinateSeries[$metricColumn][] = $smallValuesSum;
+ $truncatedAbscissaSeries[] = Piwik_Translate('General_Others');
+ }
+
+ $this->ordinateSeries = $truncatedOrdinateSeries;
+ $this->abscissaSeries = $truncatedAbscissaSeries;
+ }
}
diff --git a/plugins/ImageGraph/StaticGraph/VerticalBar.php b/plugins/ImageGraph/StaticGraph/VerticalBar.php
index cac1b219c6..5bf40df7f8 100644
--- a/plugins/ImageGraph/StaticGraph/VerticalBar.php
+++ b/plugins/ImageGraph/StaticGraph/VerticalBar.php
@@ -16,22 +16,22 @@
*/
class Piwik_ImageGraph_StaticGraph_VerticalBar extends Piwik_ImageGraph_StaticGraph_GridGraph
{
- const INTERLEAVE = 0.10;
+ const INTERLEAVE = 0.10;
- public function renderGraph()
- {
- $this->initGridChart(
- $displayVerticalGridLines = false,
- $bulletType = LEGEND_FAMILY_BOX,
- $horizontalGraph = false,
- $showTicks = true,
- $verticalLegend = false
- );
+ public function renderGraph()
+ {
+ $this->initGridChart(
+ $displayVerticalGridLines = false,
+ $bulletType = LEGEND_FAMILY_BOX,
+ $horizontalGraph = false,
+ $showTicks = true,
+ $verticalLegend = false
+ );
- $this->pImage->drawBarChart(
- array(
- 'Interleave' => self::INTERLEAVE,
- )
- );
- }
+ $this->pImage->drawBarChart(
+ array(
+ 'Interleave' => self::INTERLEAVE,
+ )
+ );
+ }
}
diff --git a/plugins/ImageGraph/templates/debug_graphs_all_sizes.tpl b/plugins/ImageGraph/templates/debug_graphs_all_sizes.tpl
index 67e04750f1..9214293f95 100644
--- a/plugins/ImageGraph/templates/debug_graphs_all_sizes.tpl
+++ b/plugins/ImageGraph/templates/debug_graphs_all_sizes.tpl
@@ -3,49 +3,50 @@
{include file="CoreHome/templates/header.tpl"}
<div class="top_controls_inner"></div>
<div>
- <h2>{"ImageGraph_ImageGraph"|translate} ::: {$siteName}</h2>
- <div class="top_controls_inner">
- {include file="CoreHome/templates/period_select.tpl"}
- </div>
-
- <div class="entityContainer" style="width:100%;">
- <div class="entityAddContainer">
- <table class="dataTable entityTable">
- <thead>
- </thead>
- <tbody>
- <tr class="first">
- <th style="white-space:normal;">Category</th>
- <th style="white-space:normal;">Name</th>
-
- {foreach from=$graphTypes item=type}
- <th style="white-space:normal;">{$type}</th>
- {/foreach}
- </tr>
- {foreach from=$availableReports item=report name=i}
- {if isset($report.imageGraphUrl)}
- <tr>
- <td>{$report.category|escape:"html"}</td>
- <td>{$report.name|escape:"html"}</td>
- {foreach from=$graphTypes item=type}
- <td>
- <h2>Graph {$type} for all supported sizes</h2>
- {foreach from=$graphSizes item=sizes}
- <p>{$sizes.0} x {$sizes.1} {if !empty($sizes.2)} (scaled down to {$sizes.3} x {$sizes.4}){/if}</p>
- <img
- src="{$report.imageGraphUrl}&graphType={$type}&width={$sizes.0}&height={$sizes.1}{if !empty($sizes.2)}&fontSize={$sizes.2}{/if}"
- {if !empty($sizes.3)}width={$sizes.3}{/if}
- {if !empty($sizes.4)}height={$sizes.4}{/if}
- />
- {/foreach}
- </td>
- {/foreach}
- </tr>
- {/if}
- {/foreach}
- </tbody>
- </table>
- </div>
- <a id="bottom"></a>
- </div>
+ <h2>{"ImageGraph_ImageGraph"|translate} ::: {$siteName}</h2>
+
+ <div class="top_controls_inner">
+ {include file="CoreHome/templates/period_select.tpl"}
+ </div>
+
+ <div class="entityContainer" style="width:100%;">
+ <div class="entityAddContainer">
+ <table class="dataTable entityTable">
+ <thead>
+ </thead>
+ <tbody>
+ <tr class="first">
+ <th style="white-space:normal;">Category</th>
+ <th style="white-space:normal;">Name</th>
+
+ {foreach from=$graphTypes item=type}
+ <th style="white-space:normal;">{$type}</th>
+ {/foreach}
+ </tr>
+ {foreach from=$availableReports item=report name=i}
+ {if isset($report.imageGraphUrl)}
+ <tr>
+ <td>{$report.category|escape:"html"}</td>
+ <td>{$report.name|escape:"html"}</td>
+ {foreach from=$graphTypes item=type}
+ <td>
+ <h2>Graph {$type} for all supported sizes</h2>
+ {foreach from=$graphSizes item=sizes}
+ <p>{$sizes.0} x {$sizes.1} {if !empty($sizes.2)} (scaled down to {$sizes.3} x {$sizes.4}){/if}</p>
+ <img
+ src="{$report.imageGraphUrl}&graphType={$type}&width={$sizes.0}&height={$sizes.1}{if !empty($sizes.2)}&fontSize={$sizes.2}{/if}"
+ {if !empty($sizes.3)}width={$sizes.3}{/if}
+ {if !empty($sizes.4)}height={$sizes.4}{/if}
+ />
+ {/foreach}
+ </td>
+ {/foreach}
+ </tr>
+ {/if}
+ {/foreach}
+ </tbody>
+ </table>
+ </div>
+ <a id="bottom"></a>
+ </div>
</div> \ No newline at end of file
diff --git a/plugins/ImageGraph/templates/index.tpl b/plugins/ImageGraph/templates/index.tpl
index f292e8271a..8dae3a1901 100644
--- a/plugins/ImageGraph/templates/index.tpl
+++ b/plugins/ImageGraph/templates/index.tpl
@@ -1,4 +1,4 @@
{foreach from=$titleAndUrls item=plot}
- <h2>{$plot.0|escape}</h2>
- <a href='{$plot.1}'><img border=0 src="{$plot.1}"></a>
+ <h2>{$plot.0|escape}</h2>
+ <a href='{$plot.1}'><img border=0 src="{$plot.1}"></a>
{/foreach} \ No newline at end of file
diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php
index 1cdc760c73..153991a5d3 100644
--- a/plugins/Installation/Controller.php
+++ b/plugins/Installation/Controller.php
@@ -16,381 +16,365 @@
*/
class Piwik_Installation_Controller extends Piwik_Controller_Admin
{
- // public so plugins can add/delete installation steps
- public $steps = array(
- 'welcome' => 'Installation_Welcome',
- 'systemCheck' => 'Installation_SystemCheck',
- 'databaseSetup' => 'Installation_DatabaseSetup',
- 'databaseCheck' => 'Installation_DatabaseCheck',
- 'tablesCreation' => 'Installation_Tables',
- 'generalSetup' => 'Installation_SuperUser',
- 'firstWebsiteSetup' => 'Installation_SetupWebsite',
- 'displayJavascriptCode' => 'Installation_JsTag',
- 'finished' => 'Installation_Congratulations',
- );
-
- protected $pathView = 'Installation/templates/';
-
- protected $session;
-
- public function __construct()
- {
- $this->session = new Piwik_Session_Namespace('Piwik_Installation');
- if(!isset($this->session->currentStepDone))
- {
- $this->session->currentStepDone = '';
- $this->session->skipThisStep = array();
- }
-
- Piwik_PostEvent('InstallationController.construct', $this);
- }
-
- /**
- * Get installation steps
- *
- * @return array installation steps
- */
- public function getInstallationSteps()
- {
- return $this->steps;
- }
-
- /**
- * Get default action (first installation step)
- *
- * @return string function name
- */
- function getDefaultAction()
- {
- $steps = array_keys($this->steps);
- return $steps[0];
- }
-
- /**
- * Installation Step 1: Welcome
- */
- function welcome($message = false)
- {
- // Delete merged js/css files to force regenerations based on updated activated plugin list
- Piwik::deleteAllCacheOnUpdate();
-
- $view = new Piwik_Installation_View(
- $this->pathView . 'welcome.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $view->newInstall = !file_exists(Piwik_Config::getLocalConfigPath());
- $view->errorMessage = $message;
- $this->skipThisStep( __FUNCTION__ );
- $view->showNextStep = $view->newInstall;
- $this->session->currentStepDone = __FUNCTION__;
- echo $view->render();
- }
-
- /**
- * Installation Step 2: System Check
- */
- function systemCheck()
- {
- $this->checkPreviousStepIsValid( __FUNCTION__ );
-
- $view = new Piwik_Installation_View(
- $this->pathView . 'systemCheck.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->skipThisStep( __FUNCTION__ );
-
- $view->duringInstall = true;
-
- $this->setupSystemCheckView($view);
- $this->session->general_infos = $view->infos['general_infos'];
-
- // make sure DB sessions are used if the filesystem is NFS
- if ($view->infos['is_nfs'])
- {
- $this->session->general_infos['session_save_handler'] = 'dbtable';
- }
-
- $view->showNextStep = !$view->problemWithSomeDirectories
- && $view->infos['phpVersion_ok']
- && count($view->infos['adapters'])
- && !count($view->infos['missing_extensions'])
- && !count($view->infos['missing_functions'])
- ;
- // On the system check page, if all is green, display Next link at the top
- $view->showNextStepAtTop = $view->showNextStep;
-
- $this->session->currentStepDone = __FUNCTION__;
-
- echo $view->render();
- }
-
- /**
- * Installation Step 3: Database Set-up
- * @throws Exception|Zend_Db_Adapter_Exception
- */
- function databaseSetup()
- {
- $this->checkPreviousStepIsValid( __FUNCTION__ );
-
- // case the user hits the back button
- $this->session->skipThisStep = array(
- 'firstWebsiteSetup' => false,
- 'displayJavascriptCode' => false,
- );
-
- $view = new Piwik_Installation_View(
- $this->pathView . 'databaseSetup.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->skipThisStep( __FUNCTION__ );
-
- $view->showNextStep = false;
-
- $form = new Piwik_Installation_FormDatabaseSetup();
-
- if ($form->validate())
- {
- try
- {
- $dbInfos = $form->createDatabaseObject();
- $this->session->databaseCreated = true;
-
- Piwik::checkDatabaseVersion();
- $this->session->databaseVersionOk = true;
-
- $this->session->db_infos = $dbInfos;
- $this->redirectToNextStep( __FUNCTION__ );
- }
- catch (Exception $e)
- {
- $view->errorMessage = Piwik_Common::sanitizeInputValue($e->getMessage());
- }
- }
- $view->addForm($form);
-
- echo $view->render();
- }
-
- /**
- * Installation Step 4: Database Check
- */
- function databaseCheck()
- {
- $this->checkPreviousStepIsValid( __FUNCTION__ );
- $view = new Piwik_Installation_View(
- $this->pathView . 'databaseCheck.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
-
- $error = false;
- $this->skipThisStep( __FUNCTION__ );
-
- if(isset($this->session->databaseVersionOk)
- && $this->session->databaseVersionOk === true)
- {
- $view->databaseVersionOk = true;
- }
- else
- {
- $error = true;
- }
-
- if(isset($this->session->databaseCreated)
- && $this->session->databaseCreated === true)
- {
- $dbInfos = $this->session->db_infos;
- $view->databaseName = $dbInfos['dbname'];
- $view->databaseCreated = true;
- }
- else
- {
- $error = true;
- }
-
- $this->createDbFromSessionInformation();
- $db = Zend_Registry::get('db');
-
- try {
- $db->checkClientVersion();
- } catch(Exception $e) {
- $view->clientVersionWarning = $e->getMessage();
- $error = true;
- }
-
- if(!Piwik::isDatabaseConnectionUTF8())
- {
- $dbInfos = $this->session->db_infos;
- $dbInfos['charset'] = 'utf8';
- $this->session->db_infos = $dbInfos;
- }
-
- $view->showNextStep = true;
- $this->session->currentStepDone = __FUNCTION__;
-
- if($error === false)
- {
- $this->redirectToNextStep(__FUNCTION__);
- }
- echo $view->render();
- }
-
- /**
- * Installation Step 5: Table Creation
- */
- function tablesCreation()
- {
- $this->checkPreviousStepIsValid( __FUNCTION__ );
-
- $view = new Piwik_Installation_View(
- $this->pathView . 'tablesCreation.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->skipThisStep( __FUNCTION__ );
- $this->createDbFromSessionInformation();
-
- if(Piwik_Common::getRequestVar('deleteTables', 0, 'int') == 1)
- {
- Piwik::dropTables();
- $view->existingTablesDeleted = true;
-
- // when the user decides to drop the tables then we dont skip the next steps anymore
- // workaround ZF-1743
- $tmp = $this->session->skipThisStep;
- $tmp['firstWebsiteSetup'] = false;
- $tmp['displayJavascriptCode'] = false;
- $this->session->skipThisStep = $tmp;
- }
-
- $tablesInstalled = Piwik::getTablesInstalled();
- $tablesToInstall = Piwik::getTablesNames();
- $view->tablesInstalled = '';
- if(count($tablesInstalled) > 0)
- {
- // we have existing tables
- $view->tablesInstalled = implode(', ', $tablesInstalled);
- $view->someTablesInstalled = true;
-
- $minimumCountPiwikTables = 17;
- $baseTablesInstalled = preg_grep('/archive_numeric|archive_blob/', $tablesInstalled, PREG_GREP_INVERT);
-
- Piwik::createAccessObject();
- Piwik::setUserIsSuperUser();
- if(count($baseTablesInstalled) >= $minimumCountPiwikTables &&
- count(Piwik_SitesManager_API::getInstance()->getAllSitesId()) > 0 &&
- count(Piwik_UsersManager_API::getInstance()->getUsers()) > 0)
- {
- $view->showReuseExistingTables = true;
- // when the user reuses the same tables we skip the website creation step
- // workaround ZF-1743
- $tmp = $this->session->skipThisStep;
- $tmp['firstWebsiteSetup'] = true;
- $tmp['displayJavascriptCode'] = true;
- $this->session->skipThisStep = $tmp;
- }
- }
- else
- {
- Piwik::createTables();
- Piwik::createAnonymousUser();
-
- $updater = new Piwik_Updater();
- $updater->recordComponentSuccessfullyUpdated('core', Piwik_Version::VERSION);
- $view->tablesCreated = true;
- $view->showNextStep = true;
- }
-
- $this->session->currentStepDone = __FUNCTION__;
- echo $view->render();
- }
-
- /**
- * Installation Step 6: General Set-up (superuser login/password/email and subscriptions)
- */
- function generalSetup()
- {
- $this->checkPreviousStepIsValid( __FUNCTION__ );
-
- $view = new Piwik_Installation_View(
- $this->pathView . 'generalSetup.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->skipThisStep( __FUNCTION__ );
-
- $form = new Piwik_Installation_FormGeneralSetup();
-
- if($form->validate())
- {
- $superUserInfos = array(
- 'login' => $form->getSubmitValue('login'),
- 'password' => md5( $form->getSubmitValue('password') ),
- 'email' => $form->getSubmitValue('email'),
- 'salt' => Piwik_Common::generateUniqId(),
- );
-
- $this->session->superuser_infos = $superUserInfos;
-
- $url = Piwik_Config::getInstance()->General['api_service_url'];
- $url .= '/1.0/subscribeNewsletter/';
- $params = array(
- 'email' => $form->getSubmitValue('email'),
- 'security' => $form->getSubmitValue('subscribe_newsletter_security'),
- 'community' => $form->getSubmitValue('subscribe_newsletter_community'),
- 'url' => Piwik_Url::getCurrentUrlWithoutQueryString(),
- );
- if($params['security'] == '1'
- || $params['community'] == '1')
- {
- if( !isset($params['security'])) { $params['security'] = '0'; }
- if( !isset($params['community'])) { $params['community'] = '0'; }
- $url .= '?' . http_build_query($params, '', '&');
- try {
- Piwik_Http::sendHttpRequest($url, $timeout = 2);
- } catch(Exception $e) {
- // e.g., disable_functions = fsockopen; allow_url_open = Off
- }
- }
- $this->redirectToNextStep( __FUNCTION__ );
- }
- $view->addForm($form);
-
- echo $view->render();
- }
-
- /**
- * Installation Step 7: Configure first web-site
- */
- public function firstWebsiteSetup()
- {
- $this->checkPreviousStepIsValid( __FUNCTION__ );
-
- $view = new Piwik_Installation_View(
- $this->pathView . 'firstWebsiteSetup.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->skipThisStep( __FUNCTION__ );
-
- $form = new Piwik_Installation_FormFirstWebsiteSetup();
- if( !isset($this->session->generalSetupSuccessMessage))
- {
- $view->displayGeneralSetupSuccess = true;
- $this->session->generalSetupSuccessMessage = true;
- }
-
- $this->initObjectsToCallAPI();
- if($form->validate())
- {
- $name = urlencode($form->getSubmitValue('siteName'));
- $url = urlencode($form->getSubmitValue('url'));
- $ecommerce = (int)$form->getSubmitValue('ecommerce');
-
- $request = new Piwik_API_Request("
+ // public so plugins can add/delete installation steps
+ public $steps = array(
+ 'welcome' => 'Installation_Welcome',
+ 'systemCheck' => 'Installation_SystemCheck',
+ 'databaseSetup' => 'Installation_DatabaseSetup',
+ 'databaseCheck' => 'Installation_DatabaseCheck',
+ 'tablesCreation' => 'Installation_Tables',
+ 'generalSetup' => 'Installation_SuperUser',
+ 'firstWebsiteSetup' => 'Installation_SetupWebsite',
+ 'displayJavascriptCode' => 'Installation_JsTag',
+ 'finished' => 'Installation_Congratulations',
+ );
+
+ protected $pathView = 'Installation/templates/';
+
+ protected $session;
+
+ public function __construct()
+ {
+ $this->session = new Piwik_Session_Namespace('Piwik_Installation');
+ if (!isset($this->session->currentStepDone)) {
+ $this->session->currentStepDone = '';
+ $this->session->skipThisStep = array();
+ }
+
+ Piwik_PostEvent('InstallationController.construct', $this);
+ }
+
+ /**
+ * Get installation steps
+ *
+ * @return array installation steps
+ */
+ public function getInstallationSteps()
+ {
+ return $this->steps;
+ }
+
+ /**
+ * Get default action (first installation step)
+ *
+ * @return string function name
+ */
+ function getDefaultAction()
+ {
+ $steps = array_keys($this->steps);
+ return $steps[0];
+ }
+
+ /**
+ * Installation Step 1: Welcome
+ */
+ function welcome($message = false)
+ {
+ // Delete merged js/css files to force regenerations based on updated activated plugin list
+ Piwik::deleteAllCacheOnUpdate();
+
+ $view = new Piwik_Installation_View(
+ $this->pathView . 'welcome.tpl',
+ $this->getInstallationSteps(),
+ __FUNCTION__
+ );
+ $view->newInstall = !file_exists(Piwik_Config::getLocalConfigPath());
+ $view->errorMessage = $message;
+ $this->skipThisStep(__FUNCTION__);
+ $view->showNextStep = $view->newInstall;
+ $this->session->currentStepDone = __FUNCTION__;
+ echo $view->render();
+ }
+
+ /**
+ * Installation Step 2: System Check
+ */
+ function systemCheck()
+ {
+ $this->checkPreviousStepIsValid(__FUNCTION__);
+
+ $view = new Piwik_Installation_View(
+ $this->pathView . 'systemCheck.tpl',
+ $this->getInstallationSteps(),
+ __FUNCTION__
+ );
+ $this->skipThisStep(__FUNCTION__);
+
+ $view->duringInstall = true;
+
+ $this->setupSystemCheckView($view);
+ $this->session->general_infos = $view->infos['general_infos'];
+
+ // make sure DB sessions are used if the filesystem is NFS
+ if ($view->infos['is_nfs']) {
+ $this->session->general_infos['session_save_handler'] = 'dbtable';
+ }
+
+ $view->showNextStep = !$view->problemWithSomeDirectories
+ && $view->infos['phpVersion_ok']
+ && count($view->infos['adapters'])
+ && !count($view->infos['missing_extensions'])
+ && !count($view->infos['missing_functions']);
+ // On the system check page, if all is green, display Next link at the top
+ $view->showNextStepAtTop = $view->showNextStep;
+
+ $this->session->currentStepDone = __FUNCTION__;
+
+ echo $view->render();
+ }
+
+ /**
+ * Installation Step 3: Database Set-up
+ * @throws Exception|Zend_Db_Adapter_Exception
+ */
+ function databaseSetup()
+ {
+ $this->checkPreviousStepIsValid(__FUNCTION__);
+
+ // case the user hits the back button
+ $this->session->skipThisStep = array(
+ 'firstWebsiteSetup' => false,
+ 'displayJavascriptCode' => false,
+ );
+
+ $view = new Piwik_Installation_View(
+ $this->pathView . 'databaseSetup.tpl',
+ $this->getInstallationSteps(),
+ __FUNCTION__
+ );
+ $this->skipThisStep(__FUNCTION__);
+
+ $view->showNextStep = false;
+
+ $form = new Piwik_Installation_FormDatabaseSetup();
+
+ if ($form->validate()) {
+ try {
+ $dbInfos = $form->createDatabaseObject();
+ $this->session->databaseCreated = true;
+
+ Piwik::checkDatabaseVersion();
+ $this->session->databaseVersionOk = true;
+
+ $this->session->db_infos = $dbInfos;
+ $this->redirectToNextStep(__FUNCTION__);
+ } catch (Exception $e) {
+ $view->errorMessage = Piwik_Common::sanitizeInputValue($e->getMessage());
+ }
+ }
+ $view->addForm($form);
+
+ echo $view->render();
+ }
+
+ /**
+ * Installation Step 4: Database Check
+ */
+ function databaseCheck()
+ {
+ $this->checkPreviousStepIsValid(__FUNCTION__);
+ $view = new Piwik_Installation_View(
+ $this->pathView . 'databaseCheck.tpl',
+ $this->getInstallationSteps(),
+ __FUNCTION__
+ );
+
+ $error = false;
+ $this->skipThisStep(__FUNCTION__);
+
+ if (isset($this->session->databaseVersionOk)
+ && $this->session->databaseVersionOk === true
+ ) {
+ $view->databaseVersionOk = true;
+ } else {
+ $error = true;
+ }
+
+ if (isset($this->session->databaseCreated)
+ && $this->session->databaseCreated === true
+ ) {
+ $dbInfos = $this->session->db_infos;
+ $view->databaseName = $dbInfos['dbname'];
+ $view->databaseCreated = true;
+ } else {
+ $error = true;
+ }
+
+ $this->createDbFromSessionInformation();
+ $db = Zend_Registry::get('db');
+
+ try {
+ $db->checkClientVersion();
+ } catch (Exception $e) {
+ $view->clientVersionWarning = $e->getMessage();
+ $error = true;
+ }
+
+ if (!Piwik::isDatabaseConnectionUTF8()) {
+ $dbInfos = $this->session->db_infos;
+ $dbInfos['charset'] = 'utf8';
+ $this->session->db_infos = $dbInfos;
+ }
+
+ $view->showNextStep = true;
+ $this->session->currentStepDone = __FUNCTION__;
+
+ if ($error === false) {
+ $this->redirectToNextStep(__FUNCTION__);
+ }
+ echo $view->render();
+ }
+
+ /**
+ * Installation Step 5: Table Creation
+ */
+ function tablesCreation()
+ {
+ $this->checkPreviousStepIsValid(__FUNCTION__);
+
+ $view = new Piwik_Installation_View(
+ $this->pathView . 'tablesCreation.tpl',
+ $this->getInstallationSteps(),
+ __FUNCTION__
+ );
+ $this->skipThisStep(__FUNCTION__);
+ $this->createDbFromSessionInformation();
+
+ if (Piwik_Common::getRequestVar('deleteTables', 0, 'int') == 1) {
+ Piwik::dropTables();
+ $view->existingTablesDeleted = true;
+
+ // when the user decides to drop the tables then we dont skip the next steps anymore
+ // workaround ZF-1743
+ $tmp = $this->session->skipThisStep;
+ $tmp['firstWebsiteSetup'] = false;
+ $tmp['displayJavascriptCode'] = false;
+ $this->session->skipThisStep = $tmp;
+ }
+
+ $tablesInstalled = Piwik::getTablesInstalled();
+ $tablesToInstall = Piwik::getTablesNames();
+ $view->tablesInstalled = '';
+ if (count($tablesInstalled) > 0) {
+ // we have existing tables
+ $view->tablesInstalled = implode(', ', $tablesInstalled);
+ $view->someTablesInstalled = true;
+
+ $minimumCountPiwikTables = 17;
+ $baseTablesInstalled = preg_grep('/archive_numeric|archive_blob/', $tablesInstalled, PREG_GREP_INVERT);
+
+ Piwik::createAccessObject();
+ Piwik::setUserIsSuperUser();
+ if (count($baseTablesInstalled) >= $minimumCountPiwikTables &&
+ count(Piwik_SitesManager_API::getInstance()->getAllSitesId()) > 0 &&
+ count(Piwik_UsersManager_API::getInstance()->getUsers()) > 0
+ ) {
+ $view->showReuseExistingTables = true;
+ // when the user reuses the same tables we skip the website creation step
+ // workaround ZF-1743
+ $tmp = $this->session->skipThisStep;
+ $tmp['firstWebsiteSetup'] = true;
+ $tmp['displayJavascriptCode'] = true;
+ $this->session->skipThisStep = $tmp;
+ }
+ } else {
+ Piwik::createTables();
+ Piwik::createAnonymousUser();
+
+ $updater = new Piwik_Updater();
+ $updater->recordComponentSuccessfullyUpdated('core', Piwik_Version::VERSION);
+ $view->tablesCreated = true;
+ $view->showNextStep = true;
+ }
+
+ $this->session->currentStepDone = __FUNCTION__;
+ echo $view->render();
+ }
+
+ /**
+ * Installation Step 6: General Set-up (superuser login/password/email and subscriptions)
+ */
+ function generalSetup()
+ {
+ $this->checkPreviousStepIsValid(__FUNCTION__);
+
+ $view = new Piwik_Installation_View(
+ $this->pathView . 'generalSetup.tpl',
+ $this->getInstallationSteps(),
+ __FUNCTION__
+ );
+ $this->skipThisStep(__FUNCTION__);
+
+ $form = new Piwik_Installation_FormGeneralSetup();
+
+ if ($form->validate()) {
+ $superUserInfos = array(
+ 'login' => $form->getSubmitValue('login'),
+ 'password' => md5($form->getSubmitValue('password')),
+ 'email' => $form->getSubmitValue('email'),
+ 'salt' => Piwik_Common::generateUniqId(),
+ );
+
+ $this->session->superuser_infos = $superUserInfos;
+
+ $url = Piwik_Config::getInstance()->General['api_service_url'];
+ $url .= '/1.0/subscribeNewsletter/';
+ $params = array(
+ 'email' => $form->getSubmitValue('email'),
+ 'security' => $form->getSubmitValue('subscribe_newsletter_security'),
+ 'community' => $form->getSubmitValue('subscribe_newsletter_community'),
+ 'url' => Piwik_Url::getCurrentUrlWithoutQueryString(),
+ );
+ if ($params['security'] == '1'
+ || $params['community'] == '1'
+ ) {
+ if (!isset($params['security'])) {
+ $params['security'] = '0';
+ }
+ if (!isset($params['community'])) {
+ $params['community'] = '0';
+ }
+ $url .= '?' . http_build_query($params, '', '&');
+ try {
+ Piwik_Http::sendHttpRequest($url, $timeout = 2);
+ } catch (Exception $e) {
+ // e.g., disable_functions = fsockopen; allow_url_open = Off
+ }
+ }
+ $this->redirectToNextStep(__FUNCTION__);
+ }
+ $view->addForm($form);
+
+ echo $view->render();
+ }
+
+ /**
+ * Installation Step 7: Configure first web-site
+ */
+ public function firstWebsiteSetup()
+ {
+ $this->checkPreviousStepIsValid(__FUNCTION__);
+
+ $view = new Piwik_Installation_View(
+ $this->pathView . 'firstWebsiteSetup.tpl',
+ $this->getInstallationSteps(),
+ __FUNCTION__
+ );
+ $this->skipThisStep(__FUNCTION__);
+
+ $form = new Piwik_Installation_FormFirstWebsiteSetup();
+ if (!isset($this->session->generalSetupSuccessMessage)) {
+ $view->displayGeneralSetupSuccess = true;
+ $this->session->generalSetupSuccessMessage = true;
+ }
+
+ $this->initObjectsToCallAPI();
+ if ($form->validate()) {
+ $name = urlencode($form->getSubmitValue('siteName'));
+ $url = urlencode($form->getSubmitValue('url'));
+ $ecommerce = (int)$form->getSubmitValue('ecommerce');
+
+ $request = new Piwik_API_Request("
method=SitesManager.addSite
&siteName=$name
&urls=$url
@@ -398,616 +382,573 @@ class Piwik_Installation_Controller extends Piwik_Controller_Admin
&format=original
");
- try {
- $result = $request->process();
- $this->session->site_idSite = $result;
- $this->session->site_name = $name;
- $this->session->site_url = $url;
-
- $this->redirectToNextStep( __FUNCTION__ );
- } catch(Exception $e) {
- $view->errorMessage = $e->getMessage();
- }
-
- }
- $view->addForm($form);
- echo $view->render();
- }
-
- /**
- * Installation Step 8: Display JavaScript tracking code
- */
- public function displayJavascriptCode()
- {
- $this->checkPreviousStepIsValid( __FUNCTION__ );
-
- $view = new Piwik_Installation_View(
- $this->pathView . 'displayJavascriptCode.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->skipThisStep( __FUNCTION__ );
-
- if( !isset($this->session->firstWebsiteSetupSuccessMessage))
- {
- $view->displayfirstWebsiteSetupSuccess = true;
- $this->session->firstWebsiteSetupSuccessMessage = true;
- }
- $siteName = $this->session->site_name;
- $siteName = Piwik_Common::sanitizeInputValue( urldecode($siteName) );
- $idSite = $this->session->site_idSite;
-
- // Load the Tracking code and help text from the SitesManager
- $viewTrackingHelp = new Piwik_View('SitesManager/templates/DisplayJavascriptCode.tpl');
- $viewTrackingHelp->displaySiteName = $siteName;
- $viewTrackingHelp->jsTag = Piwik::getJavascriptCode($idSite, Piwik_Url::getCurrentUrlWithoutFileName());
- $viewTrackingHelp->idSite = $idSite;
- $viewTrackingHelp->piwikUrl = Piwik_Url::getCurrentUrlWithoutFileName();
-
- // Assign the html output to a smarty variable
- $view->trackingHelp = $viewTrackingHelp->render();
- $view->displaySiteName = $siteName;
-
- $view->showNextStep = true;
-
- $this->session->currentStepDone = __FUNCTION__;
- echo $view->render();
- }
-
- /**
- * Installation Step 9: Finished!
- */
- public function finished()
- {
- $this->checkPreviousStepIsValid( __FUNCTION__ );
-
- $view = new Piwik_Installation_View(
- $this->pathView . 'finished.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->skipThisStep( __FUNCTION__ );
-
- if(!file_exists(Piwik_Config::getLocalConfigPath()))
- {
+ try {
+ $result = $request->process();
+ $this->session->site_idSite = $result;
+ $this->session->site_name = $name;
+ $this->session->site_url = $url;
+
+ $this->redirectToNextStep(__FUNCTION__);
+ } catch (Exception $e) {
+ $view->errorMessage = $e->getMessage();
+ }
+
+ }
+ $view->addForm($form);
+ echo $view->render();
+ }
+
+ /**
+ * Installation Step 8: Display JavaScript tracking code
+ */
+ public function displayJavascriptCode()
+ {
+ $this->checkPreviousStepIsValid(__FUNCTION__);
+
+ $view = new Piwik_Installation_View(
+ $this->pathView . 'displayJavascriptCode.tpl',
+ $this->getInstallationSteps(),
+ __FUNCTION__
+ );
+ $this->skipThisStep(__FUNCTION__);
+
+ if (!isset($this->session->firstWebsiteSetupSuccessMessage)) {
+ $view->displayfirstWebsiteSetupSuccess = true;
+ $this->session->firstWebsiteSetupSuccessMessage = true;
+ }
+ $siteName = $this->session->site_name;
+ $siteName = Piwik_Common::sanitizeInputValue(urldecode($siteName));
+ $idSite = $this->session->site_idSite;
+
+ // Load the Tracking code and help text from the SitesManager
+ $viewTrackingHelp = new Piwik_View('SitesManager/templates/DisplayJavascriptCode.tpl');
+ $viewTrackingHelp->displaySiteName = $siteName;
+ $viewTrackingHelp->jsTag = Piwik::getJavascriptCode($idSite, Piwik_Url::getCurrentUrlWithoutFileName());
+ $viewTrackingHelp->idSite = $idSite;
+ $viewTrackingHelp->piwikUrl = Piwik_Url::getCurrentUrlWithoutFileName();
+
+ // Assign the html output to a smarty variable
+ $view->trackingHelp = $viewTrackingHelp->render();
+ $view->displaySiteName = $siteName;
+
+ $view->showNextStep = true;
+
+ $this->session->currentStepDone = __FUNCTION__;
+ echo $view->render();
+ }
+
+ /**
+ * Installation Step 9: Finished!
+ */
+ public function finished()
+ {
+ $this->checkPreviousStepIsValid(__FUNCTION__);
+
+ $view = new Piwik_Installation_View(
+ $this->pathView . 'finished.tpl',
+ $this->getInstallationSteps(),
+ __FUNCTION__
+ );
+ $this->skipThisStep(__FUNCTION__);
+
+ if (!file_exists(Piwik_Config::getLocalConfigPath())) {
// $this->addTrustedHosts();
- $this->writeConfigFileFromSession();
- }
-
- $view->showNextStep = false;
-
- $this->session->currentStepDone = __FUNCTION__;
- echo $view->render();
-
- $this->session->unsetAll();
- }
-
- /**
- * This controller action renders an admin tab that runs the installation
- * system check, so people can see if there are any issues w/ their running
- * Piwik installation.
- *
- * This admin tab is only viewable by the super user.
- */
- public function systemCheckPage()
- {
- Piwik::checkUserIsSuperUser();
-
- $view = Piwik_View::factory('systemCheckPage');
- $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
-
- $view->duringInstall = false;
-
- $this->setupSystemCheckView($view);
-
- $infos = $view->infos;
- $infos['extra'] = self::performAdminPageOnlySystemCheck();
- $view->infos = $infos;
-
- echo $view->render();
- }
-
- /**
- * Instantiate access and log objects
- */
- protected function initObjectsToCallAPI()
- {
- // connect to the database using the DB infos currently in the session
- $this->createDbFromSessionInformation();
-
- Piwik::createAccessObject();
- Piwik::setUserIsSuperUser();
- Piwik::createLogObject();
- }
-
- /**
- * Create database connection from session-store
- */
- protected function createDbFromSessionInformation()
- {
- $dbInfos = $this->session->db_infos;
- Piwik_Config::getInstance()->database = $dbInfos;
- Piwik::createDatabaseObject($dbInfos);
- }
-
- /**
- * Write configuration file from session-store
- */
- protected function writeConfigFileFromSession()
- {
- if(!isset($this->session->superuser_infos)
- || !isset($this->session->db_infos))
- {
- return;
- }
-
- $config = Piwik_Config::getInstance();
- try {
- // expect exception since config.ini.php doesn't exist yet
- $config->init();
- } catch(Exception $e) {
- $config->superuser = $this->session->superuser_infos;
- $config->database = $this->session->db_infos;
-
- if(!empty($this->session->general_infos))
- {
- $config->General = $this->session->general_infos;
- }
-
- $config->forceSave();
- }
-
- unset($this->session->superuser_infos);
- unset($this->session->db_infos);
- unset($this->session->general_infos);
- }
-
- /**
- * Save language selection in session-store
- */
- public function saveLanguage()
- {
- $language = Piwik_Common::getRequestVar('language');
- Piwik_LanguagesManager::setLanguageForSession($language);
- Piwik_Url::redirectToReferer();
- }
-
- /**
- * The previous step is valid if it is either
- * - any step before (OK to go back)
- * - the current step (case when validating a form)
- * If step is invalid, then exit.
- *
- * @param string $currentStep Current step
- */
- protected function checkPreviousStepIsValid( $currentStep )
- {
- $error = false;
-
- if(empty($this->session->currentStepDone))
- {
- $error = true;
- }
- else if($currentStep == 'finished' && $this->session->currentStepDone == 'finished')
- {
- // ok to refresh this page or use language selector
- }
- else
- {
- if(file_exists(Piwik_Config::getLocalConfigPath()))
- {
- $error = true;
- }
-
- $steps = array_keys($this->steps);
-
- // the currentStep
- $currentStepId = array_search($currentStep, $steps);
-
- // the step before
- $previousStepId = array_search($this->session->currentStepDone, $steps);
-
- // not OK if currentStepId > previous+1
- if( $currentStepId > $previousStepId + 1 )
- {
- $error = true;
- }
- }
- if($error)
- {
- Piwik_Login_Controller::clearSession();
- $message = Piwik_Translate('Installation_ErrorInvalidState',
- array( '<br /><b>',
- '</b>',
- '<a href=\''.Piwik_Common::sanitizeInputValue(Piwik_Url::getCurrentUrlWithoutFileName()).'\'>',
- '</a>')
- );
- Piwik::exitWithErrorMessage( $message );
- }
- }
-
- /**
- * Redirect to next step
- *
- * @param string Current step
- * @return none
- */
- protected function redirectToNextStep($currentStep)
- {
- $steps = array_keys($this->steps);
- $this->session->currentStepDone = $currentStep;
- $nextStep = $steps[1 + array_search($currentStep, $steps)];
- Piwik::redirectToModule('Installation' , $nextStep);
- }
-
- /**
- * Skip this step (typically to mark the current function as completed)
- *
- * @param string function name
- */
- protected function skipThisStep( $step )
- {
- $skipThisStep = $this->session->skipThisStep;
- if(isset($skipThisStep[$step]) && $skipThisStep[$step])
- {
- $this->redirectToNextStep($step);
- }
- }
-
- /**
- * Extract host from URL
- *
- * @param string $url URL
- *
- * @return string|false
- */
- protected function extractHost($url)
- {
- $urlParts = parse_url($url);
- if (isset($urlParts['host']) && strlen($host = $urlParts['host']))
- {
- return $host;
- }
-
- return false;
- }
-
- /**
- * Add trusted hosts
- */
- protected function addTrustedHosts()
- {
- $trustedHosts = array();
-
- // extract host from the request header
- if (($host = $this->extractHost('http://'.Piwik_Url::getHost())) !== false)
- {
- $trustedHosts[] = $host;
- }
-
- // extract host from first web site
- if (($host = $this->extractHost(urldecode($this->session->site_url))) !== false)
- {
- $trustedHosts[] = $host;
- }
-
- $trustedHosts = array_unique($trustedHosts);
- if (count($trustedHosts))
- {
- $this->session->general_infos['trusted_hosts'] = $trustedHosts;
- }
- }
-
- /**
- * Get system information
- */
- public static function getSystemInformation()
- {
- global $piwik_minimumPHPVersion;
- $minimumMemoryLimit = Piwik_Config::getInstance()->General['minimum_memory_limit'];
-
- $infos = array();
-
- $infos['general_infos'] = array();
- $infos['directories'] = Piwik::checkDirectoriesWritable();
- $infos['can_auto_update'] = Piwik::canAutoUpdate();
-
- if(Piwik_Common::isIIS())
- {
- Piwik::createWebConfigFiles();
- }
- else
- {
- Piwik::createHtAccessFiles();
- }
- Piwik::createWebRootFiles();
-
- $infos['phpVersion_minimum'] = $piwik_minimumPHPVersion;
- $infos['phpVersion'] = PHP_VERSION;
- $infos['phpVersion_ok'] = version_compare( $piwik_minimumPHPVersion, $infos['phpVersion']) === -1;
-
- // critical errors
- $extensions = @get_loaded_extensions();
- $needed_extensions = array(
- 'zlib',
- 'SPL',
- 'iconv',
- 'Reflection',
- );
- $infos['needed_extensions'] = $needed_extensions;
- $infos['missing_extensions'] = array();
- foreach($needed_extensions as $needed_extension)
- {
- if(!in_array($needed_extension, $extensions))
- {
- $infos['missing_extensions'][] = $needed_extension;
- }
- }
-
- $infos['pdo_ok'] = false;
- if(in_array('PDO', $extensions))
- {
- $infos['pdo_ok'] = true;
- }
-
- $infos['adapters'] = Piwik_Db_Adapter::getAdapters();
-
- $needed_functions = array(
- 'debug_backtrace',
- 'create_function',
- 'eval',
- 'gzcompress',
- 'gzuncompress',
- 'pack',
- );
- $infos['needed_functions'] = $needed_functions;
- $infos['missing_functions'] = array();
- foreach($needed_functions as $needed_function)
- {
- if(!self::functionExists($needed_function))
- {
- $infos['missing_functions'][] = $needed_function;
- }
- }
-
- // warnings
- $desired_extensions = array(
- 'json',
- 'libxml',
- 'dom',
- 'SimpleXML',
- );
- $infos['desired_extensions'] = $desired_extensions;
- $infos['missing_desired_extensions'] = array();
- foreach($desired_extensions as $desired_extension)
- {
- if(!in_array($desired_extension, $extensions))
- {
- $infos['missing_desired_extensions'][] = $desired_extension;
- }
- }
-
- $desired_functions = array(
- 'set_time_limit',
- 'mail',
- 'parse_ini_file',
- 'glob',
- );
- $infos['desired_functions'] = $desired_functions;
- $infos['missing_desired_functions'] = array();
- foreach($desired_functions as $desired_function)
- {
- if(!self::functionExists($desired_function))
- {
- $infos['missing_desired_functions'][] = $desired_function;
- }
- }
-
- $infos['openurl'] = Piwik_Http::getTransportMethod();
-
- $infos['gd_ok'] = Piwik::isGdExtensionEnabled();
-
- $infos['hasMbstring'] = false;
- $infos['multibyte_ok'] = true;
- if(function_exists('mb_internal_encoding'))
- {
- $infos['hasMbstring'] = true;
- if (((int) ini_get('mbstring.func_overload')) != 0)
- {
- $infos['multibyte_ok'] = false;
- }
- }
-
- $serverSoftware = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
- $infos['serverVersion'] = addslashes($serverSoftware);
- $infos['serverOs'] = @php_uname();
- $infos['serverTime'] = date('H:i:s');
-
- $infos['registerGlobals_ok'] = ini_get('register_globals') == 0;
- $infos['memoryMinimum'] = $minimumMemoryLimit;
-
- $infos['memory_ok'] = true;
- $infos['memoryCurrent'] = '';
-
- $raised = Piwik::raiseMemoryLimitIfNecessary();
- if(($memoryValue = Piwik::getMemoryLimitValue()) > 0)
- {
- $infos['memoryCurrent'] = $memoryValue.'M';
- $infos['memory_ok'] = $memoryValue >= $minimumMemoryLimit;
- }
-
- $infos['isWindows'] = Piwik_Common::isWindows();
-
- $integrityInfo = Piwik::getFileIntegrityInformation();
- $infos['integrity'] = $integrityInfo[0];
-
- $infos['integrityErrorMessages'] = array();
- if(isset($integrityInfo[1]))
- {
- if($infos['integrity'] == false)
- {
- $infos['integrityErrorMessages'][] = '<b>'.Piwik_Translate('General_FileIntegrityWarningExplanation').'</b>';
- }
- $infos['integrityErrorMessages'] = array_merge($infos['integrityErrorMessages'], array_slice($integrityInfo, 1));
- }
-
- $infos['timezone'] = Piwik::isTimezoneSupportEnabled();
-
- $infos['tracker_status'] = Piwik_Common::getRequestVar('trackerStatus', 0, 'int');
-
- $infos['protocol'] = Piwik_ProxyHeaders::getProtocolInformation();
- if(!Piwik::isHttps() && $infos['protocol'] !== null)
- {
- $infos['general_infos']['assume_secure_protocol'] = '1';
- }
- if(count($headers = Piwik_ProxyHeaders::getProxyClientHeaders()) > 0)
- {
- $infos['general_infos']['proxy_client_headers'] = $headers;
- }
- if(count($headers = Piwik_ProxyHeaders::getProxyHostHeaders()) > 0)
- {
- $infos['general_infos']['proxy_host_headers'] = $headers;
- }
-
- // check if filesystem is NFS, if it is file based sessions won't work properly
- $infos['is_nfs'] = Piwik::checkIfFileSystemIsNFS();
-
- // determine whether there are any errors/warnings from the checks done above
- $infos['has_errors'] = false;
- $infos['has_warnings'] = false;
- if (in_array(0, $infos['directories']) // if a directory is not writable
- || !$infos['phpVersion_ok']
- || !empty($infos['missing_extensions'])
- || empty($infos['adapters'])
- || !empty($infos['missing_functions']))
- {
- $infos['has_errors'] = true;
- }
-
- if (!$infos['can_auto_update']
- || !empty($infos['missing_desired_extensions'])
- || !$infos['gd_ok']
- || !$infos['multibyte_ok']
- || !$infos['registerGlobals_ok']
- || !$infos['memory_ok']
- || !empty($infos['integrityErrorMessages'])
- || !$infos['timezone'] // if timezone support isn't available
- || $infos['tracker_status'] != 0
- || $infos['is_nfs'])
- {
- $infos['has_warnings'] = true;
- }
-
- return $infos;
- }
-
- /**
- * Test if function exists. Also handles case where function is disabled via Suhosin.
- *
- * @param string $functionName Function name
- * @return bool True if function exists (not disabled); False otherwise.
- */
- public static function functionExists($functionName)
- {
- // eval() is a language construct
- if($functionName == 'eval')
- {
- // does not check suhosin.executor.eval.whitelist (or blacklist)
- if(extension_loaded('suhosin'))
- {
- return @ini_get("suhosin.executor.disable_eval") != "1";
- }
- return true;
- }
-
- $exists = function_exists($functionName);
- if(extension_loaded('suhosin'))
- {
- $blacklist = @ini_get("suhosin.executor.func.blacklist");
- if(!empty($blacklist))
- {
- $blacklistFunctions = array_map('strtolower', array_map('trim', explode(',', $blacklist)));
- return $exists && !in_array($functionName, $blacklistFunctions);
- }
-
- }
- return $exists;
- }
-
- /**
- * Utility function, sets up a view that will display system check info.
- *
- * @param Piwik_View $view
- */
- private function setupSystemCheckView( $view )
- {
- $view->infos = self::getSystemInformation();
-
- $view->helpMessages = array(
- 'zlib' => 'Installation_SystemCheckZlibHelp',
- 'SPL' => 'Installation_SystemCheckSplHelp',
- 'iconv' => 'Installation_SystemCheckIconvHelp',
- 'Reflection' => 'Required extension that is built in PHP, see http://www.php.net/manual/en/book.reflection.php',
- 'json' => 'Installation_SystemCheckWarnJsonHelp',
- 'libxml' => 'Installation_SystemCheckWarnLibXmlHelp',
- 'dom' => 'Installation_SystemCheckWarnDomHelp',
- 'SimpleXML' => 'Installation_SystemCheckWarnSimpleXMLHelp',
- 'set_time_limit' => 'Installation_SystemCheckTimeLimitHelp',
- 'mail' => 'Installation_SystemCheckMailHelp',
- 'parse_ini_file' => 'Installation_SystemCheckParseIniFileHelp',
- 'glob' => 'Installation_SystemCheckGlobHelp',
- 'debug_backtrace' => 'Installation_SystemCheckDebugBacktraceHelp',
- 'create_function' => 'Installation_SystemCheckCreateFunctionHelp',
- 'eval' => 'Installation_SystemCheckEvalHelp',
- 'gzcompress' => 'Installation_SystemCheckGzcompressHelp',
- 'gzuncompress' => 'Installation_SystemCheckGzuncompressHelp',
- 'pack' => 'Installation_SystemCheckPackHelp',
- );
-
- $view->problemWithSomeDirectories = (false !== array_search(false, $view->infos['directories']));
- }
-
- /**
- * Performs extra system checks for the 'System Check' admin page. These
- * checks are not performed during Installation.
- *
- * The following checks are performed:
- * - Check for whether LOAD DATA INFILE can be used. The result of the check
- * is stored in $result['load_data_infile_available']. The error message is
- * stored in $result['load_data_infile_error'].
- *
- * @return array
- */
- public static function performAdminPageOnlySystemCheck()
- {
- $result = array();
-
- // check if LOAD DATA INFILE works
- $optionTable = Piwik_Common::prefixTable('option');
- $testOptionNames = array('test_system_check1', 'test_system_check2');
-
- $result['load_data_infile_available'] = false;
- try
- {
- $result['load_data_infile_available'] = Piwik::tableInsertBatch(
- $optionTable,
- array('option_name', 'option_value'),
- array(
- array($testOptionNames[0], '1'),
- array($testOptionNames[1], '2'),
- ),
- $throwException = true
- );
- }
- catch (Exception $ex)
- {
- $result['load_data_infile_error'] = str_replace("\n","<br/>", $ex->getMessage());
- }
-
- // delete the temporary rows that were created
- Piwik_Exec("DELETE FROM `$optionTable` WHERE option_name IN ('".implode("','", $testOptionNames)."')");
-
- return $result;
- }
+ $this->writeConfigFileFromSession();
+ }
+
+ $view->showNextStep = false;
+
+ $this->session->currentStepDone = __FUNCTION__;
+ echo $view->render();
+
+ $this->session->unsetAll();
+ }
+
+ /**
+ * This controller action renders an admin tab that runs the installation
+ * system check, so people can see if there are any issues w/ their running
+ * Piwik installation.
+ *
+ * This admin tab is only viewable by the super user.
+ */
+ public function systemCheckPage()
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $view = Piwik_View::factory('systemCheckPage');
+ $this->setBasicVariablesView($view);
+ $view->menu = Piwik_GetAdminMenu();
+
+ $view->duringInstall = false;
+
+ $this->setupSystemCheckView($view);
+
+ $infos = $view->infos;
+ $infos['extra'] = self::performAdminPageOnlySystemCheck();
+ $view->infos = $infos;
+
+ echo $view->render();
+ }
+
+ /**
+ * Instantiate access and log objects
+ */
+ protected function initObjectsToCallAPI()
+ {
+ // connect to the database using the DB infos currently in the session
+ $this->createDbFromSessionInformation();
+
+ Piwik::createAccessObject();
+ Piwik::setUserIsSuperUser();
+ Piwik::createLogObject();
+ }
+
+ /**
+ * Create database connection from session-store
+ */
+ protected function createDbFromSessionInformation()
+ {
+ $dbInfos = $this->session->db_infos;
+ Piwik_Config::getInstance()->database = $dbInfos;
+ Piwik::createDatabaseObject($dbInfos);
+ }
+
+ /**
+ * Write configuration file from session-store
+ */
+ protected function writeConfigFileFromSession()
+ {
+ if (!isset($this->session->superuser_infos)
+ || !isset($this->session->db_infos)
+ ) {
+ return;
+ }
+
+ $config = Piwik_Config::getInstance();
+ try {
+ // expect exception since config.ini.php doesn't exist yet
+ $config->init();
+ } catch (Exception $e) {
+ $config->superuser = $this->session->superuser_infos;
+ $config->database = $this->session->db_infos;
+
+ if (!empty($this->session->general_infos)) {
+ $config->General = $this->session->general_infos;
+ }
+
+ $config->forceSave();
+ }
+
+ unset($this->session->superuser_infos);
+ unset($this->session->db_infos);
+ unset($this->session->general_infos);
+ }
+
+ /**
+ * Save language selection in session-store
+ */
+ public function saveLanguage()
+ {
+ $language = Piwik_Common::getRequestVar('language');
+ Piwik_LanguagesManager::setLanguageForSession($language);
+ Piwik_Url::redirectToReferer();
+ }
+
+ /**
+ * The previous step is valid if it is either
+ * - any step before (OK to go back)
+ * - the current step (case when validating a form)
+ * If step is invalid, then exit.
+ *
+ * @param string $currentStep Current step
+ */
+ protected function checkPreviousStepIsValid($currentStep)
+ {
+ $error = false;
+
+ if (empty($this->session->currentStepDone)) {
+ $error = true;
+ } else if ($currentStep == 'finished' && $this->session->currentStepDone == 'finished') {
+ // ok to refresh this page or use language selector
+ } else {
+ if (file_exists(Piwik_Config::getLocalConfigPath())) {
+ $error = true;
+ }
+
+ $steps = array_keys($this->steps);
+
+ // the currentStep
+ $currentStepId = array_search($currentStep, $steps);
+
+ // the step before
+ $previousStepId = array_search($this->session->currentStepDone, $steps);
+
+ // not OK if currentStepId > previous+1
+ if ($currentStepId > $previousStepId + 1) {
+ $error = true;
+ }
+ }
+ if ($error) {
+ Piwik_Login_Controller::clearSession();
+ $message = Piwik_Translate('Installation_ErrorInvalidState',
+ array('<br /><b>',
+ '</b>',
+ '<a href=\'' . Piwik_Common::sanitizeInputValue(Piwik_Url::getCurrentUrlWithoutFileName()) . '\'>',
+ '</a>')
+ );
+ Piwik::exitWithErrorMessage($message);
+ }
+ }
+
+ /**
+ * Redirect to next step
+ *
+ * @param string Current step
+ * @return none
+ */
+ protected function redirectToNextStep($currentStep)
+ {
+ $steps = array_keys($this->steps);
+ $this->session->currentStepDone = $currentStep;
+ $nextStep = $steps[1 + array_search($currentStep, $steps)];
+ Piwik::redirectToModule('Installation', $nextStep);
+ }
+
+ /**
+ * Skip this step (typically to mark the current function as completed)
+ *
+ * @param string function name
+ */
+ protected function skipThisStep($step)
+ {
+ $skipThisStep = $this->session->skipThisStep;
+ if (isset($skipThisStep[$step]) && $skipThisStep[$step]) {
+ $this->redirectToNextStep($step);
+ }
+ }
+
+ /**
+ * Extract host from URL
+ *
+ * @param string $url URL
+ *
+ * @return string|false
+ */
+ protected function extractHost($url)
+ {
+ $urlParts = parse_url($url);
+ if (isset($urlParts['host']) && strlen($host = $urlParts['host'])) {
+ return $host;
+ }
+
+ return false;
+ }
+
+ /**
+ * Add trusted hosts
+ */
+ protected function addTrustedHosts()
+ {
+ $trustedHosts = array();
+
+ // extract host from the request header
+ if (($host = $this->extractHost('http://' . Piwik_Url::getHost())) !== false) {
+ $trustedHosts[] = $host;
+ }
+
+ // extract host from first web site
+ if (($host = $this->extractHost(urldecode($this->session->site_url))) !== false) {
+ $trustedHosts[] = $host;
+ }
+
+ $trustedHosts = array_unique($trustedHosts);
+ if (count($trustedHosts)) {
+ $this->session->general_infos['trusted_hosts'] = $trustedHosts;
+ }
+ }
+
+ /**
+ * Get system information
+ */
+ public static function getSystemInformation()
+ {
+ global $piwik_minimumPHPVersion;
+ $minimumMemoryLimit = Piwik_Config::getInstance()->General['minimum_memory_limit'];
+
+ $infos = array();
+
+ $infos['general_infos'] = array();
+ $infos['directories'] = Piwik::checkDirectoriesWritable();
+ $infos['can_auto_update'] = Piwik::canAutoUpdate();
+
+ if (Piwik_Common::isIIS()) {
+ Piwik::createWebConfigFiles();
+ } else {
+ Piwik::createHtAccessFiles();
+ }
+ Piwik::createWebRootFiles();
+
+ $infos['phpVersion_minimum'] = $piwik_minimumPHPVersion;
+ $infos['phpVersion'] = PHP_VERSION;
+ $infos['phpVersion_ok'] = version_compare($piwik_minimumPHPVersion, $infos['phpVersion']) === -1;
+
+ // critical errors
+ $extensions = @get_loaded_extensions();
+ $needed_extensions = array(
+ 'zlib',
+ 'SPL',
+ 'iconv',
+ 'Reflection',
+ );
+ $infos['needed_extensions'] = $needed_extensions;
+ $infos['missing_extensions'] = array();
+ foreach ($needed_extensions as $needed_extension) {
+ if (!in_array($needed_extension, $extensions)) {
+ $infos['missing_extensions'][] = $needed_extension;
+ }
+ }
+
+ $infos['pdo_ok'] = false;
+ if (in_array('PDO', $extensions)) {
+ $infos['pdo_ok'] = true;
+ }
+
+ $infos['adapters'] = Piwik_Db_Adapter::getAdapters();
+
+ $needed_functions = array(
+ 'debug_backtrace',
+ 'create_function',
+ 'eval',
+ 'gzcompress',
+ 'gzuncompress',
+ 'pack',
+ );
+ $infos['needed_functions'] = $needed_functions;
+ $infos['missing_functions'] = array();
+ foreach ($needed_functions as $needed_function) {
+ if (!self::functionExists($needed_function)) {
+ $infos['missing_functions'][] = $needed_function;
+ }
+ }
+
+ // warnings
+ $desired_extensions = array(
+ 'json',
+ 'libxml',
+ 'dom',
+ 'SimpleXML',
+ );
+ $infos['desired_extensions'] = $desired_extensions;
+ $infos['missing_desired_extensions'] = array();
+ foreach ($desired_extensions as $desired_extension) {
+ if (!in_array($desired_extension, $extensions)) {
+ $infos['missing_desired_extensions'][] = $desired_extension;
+ }
+ }
+
+ $desired_functions = array(
+ 'set_time_limit',
+ 'mail',
+ 'parse_ini_file',
+ 'glob',
+ );
+ $infos['desired_functions'] = $desired_functions;
+ $infos['missing_desired_functions'] = array();
+ foreach ($desired_functions as $desired_function) {
+ if (!self::functionExists($desired_function)) {
+ $infos['missing_desired_functions'][] = $desired_function;
+ }
+ }
+
+ $infos['openurl'] = Piwik_Http::getTransportMethod();
+
+ $infos['gd_ok'] = Piwik::isGdExtensionEnabled();
+
+ $infos['hasMbstring'] = false;
+ $infos['multibyte_ok'] = true;
+ if (function_exists('mb_internal_encoding')) {
+ $infos['hasMbstring'] = true;
+ if (((int)ini_get('mbstring.func_overload')) != 0) {
+ $infos['multibyte_ok'] = false;
+ }
+ }
+
+ $serverSoftware = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
+ $infos['serverVersion'] = addslashes($serverSoftware);
+ $infos['serverOs'] = @php_uname();
+ $infos['serverTime'] = date('H:i:s');
+
+ $infos['registerGlobals_ok'] = ini_get('register_globals') == 0;
+ $infos['memoryMinimum'] = $minimumMemoryLimit;
+
+ $infos['memory_ok'] = true;
+ $infos['memoryCurrent'] = '';
+
+ $raised = Piwik::raiseMemoryLimitIfNecessary();
+ if (($memoryValue = Piwik::getMemoryLimitValue()) > 0) {
+ $infos['memoryCurrent'] = $memoryValue . 'M';
+ $infos['memory_ok'] = $memoryValue >= $minimumMemoryLimit;
+ }
+
+ $infos['isWindows'] = Piwik_Common::isWindows();
+
+ $integrityInfo = Piwik::getFileIntegrityInformation();
+ $infos['integrity'] = $integrityInfo[0];
+
+ $infos['integrityErrorMessages'] = array();
+ if (isset($integrityInfo[1])) {
+ if ($infos['integrity'] == false) {
+ $infos['integrityErrorMessages'][] = '<b>' . Piwik_Translate('General_FileIntegrityWarningExplanation') . '</b>';
+ }
+ $infos['integrityErrorMessages'] = array_merge($infos['integrityErrorMessages'], array_slice($integrityInfo, 1));
+ }
+
+ $infos['timezone'] = Piwik::isTimezoneSupportEnabled();
+
+ $infos['tracker_status'] = Piwik_Common::getRequestVar('trackerStatus', 0, 'int');
+
+ $infos['protocol'] = Piwik_ProxyHeaders::getProtocolInformation();
+ if (!Piwik::isHttps() && $infos['protocol'] !== null) {
+ $infos['general_infos']['assume_secure_protocol'] = '1';
+ }
+ if (count($headers = Piwik_ProxyHeaders::getProxyClientHeaders()) > 0) {
+ $infos['general_infos']['proxy_client_headers'] = $headers;
+ }
+ if (count($headers = Piwik_ProxyHeaders::getProxyHostHeaders()) > 0) {
+ $infos['general_infos']['proxy_host_headers'] = $headers;
+ }
+
+ // check if filesystem is NFS, if it is file based sessions won't work properly
+ $infos['is_nfs'] = Piwik::checkIfFileSystemIsNFS();
+
+ // determine whether there are any errors/warnings from the checks done above
+ $infos['has_errors'] = false;
+ $infos['has_warnings'] = false;
+ if (in_array(0, $infos['directories']) // if a directory is not writable
+ || !$infos['phpVersion_ok']
+ || !empty($infos['missing_extensions'])
+ || empty($infos['adapters'])
+ || !empty($infos['missing_functions'])
+ ) {
+ $infos['has_errors'] = true;
+ }
+
+ if (!$infos['can_auto_update']
+ || !empty($infos['missing_desired_extensions'])
+ || !$infos['gd_ok']
+ || !$infos['multibyte_ok']
+ || !$infos['registerGlobals_ok']
+ || !$infos['memory_ok']
+ || !empty($infos['integrityErrorMessages'])
+ || !$infos['timezone'] // if timezone support isn't available
+ || $infos['tracker_status'] != 0
+ || $infos['is_nfs']
+ ) {
+ $infos['has_warnings'] = true;
+ }
+
+ return $infos;
+ }
+
+ /**
+ * Test if function exists. Also handles case where function is disabled via Suhosin.
+ *
+ * @param string $functionName Function name
+ * @return bool True if function exists (not disabled); False otherwise.
+ */
+ public static function functionExists($functionName)
+ {
+ // eval() is a language construct
+ if ($functionName == 'eval') {
+ // does not check suhosin.executor.eval.whitelist (or blacklist)
+ if (extension_loaded('suhosin')) {
+ return @ini_get("suhosin.executor.disable_eval") != "1";
+ }
+ return true;
+ }
+
+ $exists = function_exists($functionName);
+ if (extension_loaded('suhosin')) {
+ $blacklist = @ini_get("suhosin.executor.func.blacklist");
+ if (!empty($blacklist)) {
+ $blacklistFunctions = array_map('strtolower', array_map('trim', explode(',', $blacklist)));
+ return $exists && !in_array($functionName, $blacklistFunctions);
+ }
+
+ }
+ return $exists;
+ }
+
+ /**
+ * Utility function, sets up a view that will display system check info.
+ *
+ * @param Piwik_View $view
+ */
+ private function setupSystemCheckView($view)
+ {
+ $view->infos = self::getSystemInformation();
+
+ $view->helpMessages = array(
+ 'zlib' => 'Installation_SystemCheckZlibHelp',
+ 'SPL' => 'Installation_SystemCheckSplHelp',
+ 'iconv' => 'Installation_SystemCheckIconvHelp',
+ 'Reflection' => 'Required extension that is built in PHP, see http://www.php.net/manual/en/book.reflection.php',
+ 'json' => 'Installation_SystemCheckWarnJsonHelp',
+ 'libxml' => 'Installation_SystemCheckWarnLibXmlHelp',
+ 'dom' => 'Installation_SystemCheckWarnDomHelp',
+ 'SimpleXML' => 'Installation_SystemCheckWarnSimpleXMLHelp',
+ 'set_time_limit' => 'Installation_SystemCheckTimeLimitHelp',
+ 'mail' => 'Installation_SystemCheckMailHelp',
+ 'parse_ini_file' => 'Installation_SystemCheckParseIniFileHelp',
+ 'glob' => 'Installation_SystemCheckGlobHelp',
+ 'debug_backtrace' => 'Installation_SystemCheckDebugBacktraceHelp',
+ 'create_function' => 'Installation_SystemCheckCreateFunctionHelp',
+ 'eval' => 'Installation_SystemCheckEvalHelp',
+ 'gzcompress' => 'Installation_SystemCheckGzcompressHelp',
+ 'gzuncompress' => 'Installation_SystemCheckGzuncompressHelp',
+ 'pack' => 'Installation_SystemCheckPackHelp',
+ );
+
+ $view->problemWithSomeDirectories = (false !== array_search(false, $view->infos['directories']));
+ }
+
+ /**
+ * Performs extra system checks for the 'System Check' admin page. These
+ * checks are not performed during Installation.
+ *
+ * The following checks are performed:
+ * - Check for whether LOAD DATA INFILE can be used. The result of the check
+ * is stored in $result['load_data_infile_available']. The error message is
+ * stored in $result['load_data_infile_error'].
+ *
+ * @return array
+ */
+ public static function performAdminPageOnlySystemCheck()
+ {
+ $result = array();
+
+ // check if LOAD DATA INFILE works
+ $optionTable = Piwik_Common::prefixTable('option');
+ $testOptionNames = array('test_system_check1', 'test_system_check2');
+
+ $result['load_data_infile_available'] = false;
+ try {
+ $result['load_data_infile_available'] = Piwik::tableInsertBatch(
+ $optionTable,
+ array('option_name', 'option_value'),
+ array(
+ array($testOptionNames[0], '1'),
+ array($testOptionNames[1], '2'),
+ ),
+ $throwException = true
+ );
+ } catch (Exception $ex) {
+ $result['load_data_infile_error'] = str_replace("\n", "<br/>", $ex->getMessage());
+ }
+
+ // delete the temporary rows that were created
+ Piwik_Exec("DELETE FROM `$optionTable` WHERE option_name IN ('" . implode("','", $testOptionNames) . "')");
+
+ return $result;
+ }
}
diff --git a/plugins/Installation/FormDatabaseSetup.php b/plugins/Installation/FormDatabaseSetup.php
index 866025be67..809c19c20c 100644
--- a/plugins/Installation/FormDatabaseSetup.php
+++ b/plugins/Installation/FormDatabaseSetup.php
@@ -1,145 +1,138 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Installation
*/
/**
- *
+ *
* @package Piwik_Installation
*/
class Piwik_Installation_FormDatabaseSetup extends Piwik_QuickForm2
{
- function __construct( $id = 'databasesetupform', $method = 'post', $attributes = null, $trackSubmit = false)
- {
- parent::__construct($id, $method, $attributes = array('autocomplete' => 'off'), $trackSubmit);
- }
-
- function init()
- {
- HTML_QuickForm2_Factory::registerRule('checkValidFilename', 'Piwik_Installation_FormDatabaseSetup_Rule_checkValidFilename');
-
- $checkUserPrivilegesClass = 'Piwik_Installation_FormDatabaseSetup_Rule_checkUserPrivileges';
- HTML_QuickForm2_Factory::registerRule('checkUserPrivileges', $checkUserPrivilegesClass);
-
- $availableAdapters = Piwik_Db_Adapter::getAdapters();
- $adapters = array();
- foreach($availableAdapters as $adapter => $port)
- {
- $adapters[$adapter] = $adapter;
- }
-
- $this->addElement('text', 'host')
- ->setLabel(Piwik_Translate('Installation_DatabaseSetupServer'))
- ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_DatabaseSetupServer')));
-
- $user = $this->addElement('text', 'username')
- ->setLabel(Piwik_Translate('Installation_DatabaseSetupLogin'));
- $user->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_DatabaseSetupLogin')));
- $requiredPrivileges = Piwik_Installation_FormDatabaseSetup_Rule_checkUserPrivileges::getRequiredPrivilegesPretty();
- $user->addRule('checkUserPrivileges',
- Piwik_Translate('Installation_InsufficientPrivileges', $requiredPrivileges . '<br/><br/>').
- Piwik_Translate('Installation_InsufficientPrivilegesHelp'));
-
- $this->addElement('password', 'password')
- ->setLabel(Piwik_Translate('Installation_DatabaseSetupPassword'));
-
- $item = $this->addElement('text', 'dbname')
- ->setLabel(Piwik_Translate('Installation_DatabaseSetupDatabaseName'));
- $item->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_DatabaseSetupDatabaseName')));
- $item->addRule('checkValidFilename', Piwik_Translate('General_NotValid', Piwik_Translate('Installation_DatabaseSetupDatabaseName')));
-
- $this->addElement('text', 'tables_prefix')
- ->setLabel(Piwik_Translate('Installation_DatabaseSetupTablePrefix'))
- ->addRule('checkValidFilename', Piwik_Translate('General_NotValid', Piwik_Translate('Installation_DatabaseSetupTablePrefix')));
-
- $this->addElement('select', 'adapter')
- ->setLabel(Piwik_Translate('Installation_DatabaseSetupAdapter'))
- ->loadOptions($adapters)
- ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_DatabaseSetupAdapter')));
-
- $this->addElement('submit', 'submit', array('value' => Piwik_Translate('General_Next') .' »', 'class' => 'submit'));
-
- // default values
- $this->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
- 'host' => '127.0.0.1',
- 'tables_prefix' => 'piwik_',
- )));
- }
-
- /**
- * Creates database object based on form data.
- *
- * @return array The database connection info. Can be passed into Piwik::createDatabaseObject.
- */
- public function createDatabaseObject()
- {
- $dbname = $this->getSubmitValue('dbname');
- if (empty($dbname)) // disallow database object creation w/ no selected database
- {
- throw new Exception("No database name");
- }
-
- $adapter = $this->getSubmitValue('adapter');
- $port = Piwik_Db_Adapter::getDefaultPortForAdapter($adapter);
-
- $dbInfos = array(
- 'host' => $this->getSubmitValue('host'),
- 'username' => $this->getSubmitValue('username'),
- 'password' => $this->getSubmitValue('password'),
- 'dbname' => $dbname,
- 'tables_prefix' => $this->getSubmitValue('tables_prefix'),
- 'adapter' => $adapter,
- 'port' => $port,
- );
-
- if(($portIndex = strpos($dbInfos['host'], '/')) !== false)
- {
- // unix_socket=/path/sock.n
- $dbInfos['port'] = substr($dbInfos['host'], $portIndex);
- $dbInfos['host'] = '';
- }
- else if(($portIndex = strpos($dbInfos['host'], ':')) !== false)
- {
- // host:port
- $dbInfos['port'] = substr($dbInfos['host'], $portIndex + 1 );
- $dbInfos['host'] = substr($dbInfos['host'], 0, $portIndex);
- }
-
- try {
- @Piwik::createDatabaseObject($dbInfos);
- } catch (Zend_Db_Adapter_Exception $e) {
- $db = Piwik_Db_Adapter::factory($adapter, $dbInfos, $connect = false);
-
- // database not found, we try to create it
- if($db->isErrNo($e, '1049'))
- {
- $dbInfosConnectOnly = $dbInfos;
- $dbInfosConnectOnly['dbname'] = null;
- @Piwik::createDatabaseObject($dbInfosConnectOnly);
- @Piwik::createDatabase($dbInfos['dbname']);
-
- // select the newly created database
- @Piwik::createDatabaseObject($dbInfos);
- }
- else
- {
- throw $e;
- }
- }
-
- return $dbInfos;
- }
+ function __construct($id = 'databasesetupform', $method = 'post', $attributes = null, $trackSubmit = false)
+ {
+ parent::__construct($id, $method, $attributes = array('autocomplete' => 'off'), $trackSubmit);
+ }
+
+ function init()
+ {
+ HTML_QuickForm2_Factory::registerRule('checkValidFilename', 'Piwik_Installation_FormDatabaseSetup_Rule_checkValidFilename');
+
+ $checkUserPrivilegesClass = 'Piwik_Installation_FormDatabaseSetup_Rule_checkUserPrivileges';
+ HTML_QuickForm2_Factory::registerRule('checkUserPrivileges', $checkUserPrivilegesClass);
+
+ $availableAdapters = Piwik_Db_Adapter::getAdapters();
+ $adapters = array();
+ foreach ($availableAdapters as $adapter => $port) {
+ $adapters[$adapter] = $adapter;
+ }
+
+ $this->addElement('text', 'host')
+ ->setLabel(Piwik_Translate('Installation_DatabaseSetupServer'))
+ ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_DatabaseSetupServer')));
+
+ $user = $this->addElement('text', 'username')
+ ->setLabel(Piwik_Translate('Installation_DatabaseSetupLogin'));
+ $user->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_DatabaseSetupLogin')));
+ $requiredPrivileges = Piwik_Installation_FormDatabaseSetup_Rule_checkUserPrivileges::getRequiredPrivilegesPretty();
+ $user->addRule('checkUserPrivileges',
+ Piwik_Translate('Installation_InsufficientPrivileges', $requiredPrivileges . '<br/><br/>') .
+ Piwik_Translate('Installation_InsufficientPrivilegesHelp'));
+
+ $this->addElement('password', 'password')
+ ->setLabel(Piwik_Translate('Installation_DatabaseSetupPassword'));
+
+ $item = $this->addElement('text', 'dbname')
+ ->setLabel(Piwik_Translate('Installation_DatabaseSetupDatabaseName'));
+ $item->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_DatabaseSetupDatabaseName')));
+ $item->addRule('checkValidFilename', Piwik_Translate('General_NotValid', Piwik_Translate('Installation_DatabaseSetupDatabaseName')));
+
+ $this->addElement('text', 'tables_prefix')
+ ->setLabel(Piwik_Translate('Installation_DatabaseSetupTablePrefix'))
+ ->addRule('checkValidFilename', Piwik_Translate('General_NotValid', Piwik_Translate('Installation_DatabaseSetupTablePrefix')));
+
+ $this->addElement('select', 'adapter')
+ ->setLabel(Piwik_Translate('Installation_DatabaseSetupAdapter'))
+ ->loadOptions($adapters)
+ ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_DatabaseSetupAdapter')));
+
+ $this->addElement('submit', 'submit', array('value' => Piwik_Translate('General_Next') . ' »', 'class' => 'submit'));
+
+ // default values
+ $this->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
+ 'host' => '127.0.0.1',
+ 'tables_prefix' => 'piwik_',
+ )));
+ }
+
+ /**
+ * Creates database object based on form data.
+ *
+ * @return array The database connection info. Can be passed into Piwik::createDatabaseObject.
+ */
+ public function createDatabaseObject()
+ {
+ $dbname = $this->getSubmitValue('dbname');
+ if (empty($dbname)) // disallow database object creation w/ no selected database
+ {
+ throw new Exception("No database name");
+ }
+
+ $adapter = $this->getSubmitValue('adapter');
+ $port = Piwik_Db_Adapter::getDefaultPortForAdapter($adapter);
+
+ $dbInfos = array(
+ 'host' => $this->getSubmitValue('host'),
+ 'username' => $this->getSubmitValue('username'),
+ 'password' => $this->getSubmitValue('password'),
+ 'dbname' => $dbname,
+ 'tables_prefix' => $this->getSubmitValue('tables_prefix'),
+ 'adapter' => $adapter,
+ 'port' => $port,
+ );
+
+ if (($portIndex = strpos($dbInfos['host'], '/')) !== false) {
+ // unix_socket=/path/sock.n
+ $dbInfos['port'] = substr($dbInfos['host'], $portIndex);
+ $dbInfos['host'] = '';
+ } else if (($portIndex = strpos($dbInfos['host'], ':')) !== false) {
+ // host:port
+ $dbInfos['port'] = substr($dbInfos['host'], $portIndex + 1);
+ $dbInfos['host'] = substr($dbInfos['host'], 0, $portIndex);
+ }
+
+ try {
+ @Piwik::createDatabaseObject($dbInfos);
+ } catch (Zend_Db_Adapter_Exception $e) {
+ $db = Piwik_Db_Adapter::factory($adapter, $dbInfos, $connect = false);
+
+ // database not found, we try to create it
+ if ($db->isErrNo($e, '1049')) {
+ $dbInfosConnectOnly = $dbInfos;
+ $dbInfosConnectOnly['dbname'] = null;
+ @Piwik::createDatabaseObject($dbInfosConnectOnly);
+ @Piwik::createDatabase($dbInfos['dbname']);
+
+ // select the newly created database
+ @Piwik::createDatabaseObject($dbInfos);
+ } else {
+ throw $e;
+ }
+ }
+
+ return $dbInfos;
+ }
}
/**
* Validation rule that checks that the supplied DB user has enough privileges.
- *
+ *
* The following privileges are required for Piwik to run:
* - CREATE
* - ALTER
@@ -149,169 +142,148 @@ class Piwik_Installation_FormDatabaseSetup extends Piwik_QuickForm2
* - DELETE
* - DROP
* - CREATE TEMPORARY TABLES
- *
+ *
* @package Piwik_Installation
*/
class Piwik_Installation_FormDatabaseSetup_Rule_checkUserPrivileges extends HTML_QuickForm2_Rule
{
- const TEST_TABLE_NAME = 'piwik_test_table';
- const TEST_TEMP_TABLE_NAME = 'piwik_test_table_temp';
-
- /**
- * Checks that the DB user entered in the form has the necessary privileges for Piwik
- * to run.
- */
- public function validateOwner()
- {
- // try and create the database object
- try
- {
- $this->createDatabaseObject();
- }
- catch (Exception $ex)
- {
- if ($this->isAccessDenied($ex))
- {
- return false;
- }
- else
- {
- return true; // if we can't create the database object, skip this validation
- }
- }
-
- $db = Zend_Registry::get('db');
-
- try
- {
- // try to drop tables before running privilege tests
- $this->dropExtraTables($db);
- }
- catch (Exception $ex)
- {
- if ($this->isAccessDenied($ex))
- {
- return false;
- }
- else
- {
- throw $ex;
- }
- }
-
- // check each required privilege by running a query that uses it
- foreach (self::getRequiredPrivileges() as $privilegeType => $queries)
- {
- if (!is_array($queries))
- {
- $queries = array($queries);
- }
-
- foreach ($queries as $sql)
- {
- try
- {
- if( in_array($privilegeType, array('SELECT'))) {
- $db->fetchAll($sql);
- } else {
- $db->exec($sql);
- }
- }
- catch (Exception $ex)
- {
- if ($this->isAccessDenied($ex))
- {
- return false;
- }
- else
- {
- throw new Exception("Test SQL failed to execute: $sql\nError: ".$ex->getMessage());
- }
- }
- }
- }
-
- // remove extra tables that were created
- $this->dropExtraTables($db);
-
- return true;
- }
-
- /**
- * Returns an array describing the database privileges required for Piwik to run. The
- * array maps privilege names with one or more SQL queries that can be used to test
- * if the current user has the privilege.
- *
- * NOTE: LOAD DATA INFILE & LOCK TABLES privileges are not **required** so they're
- * not checked.
- *
- * @return array
- */
- public static function getRequiredPrivileges()
- {
- return array(
- 'CREATE' => 'CREATE TABLE '.self::TEST_TABLE_NAME.' (
+ const TEST_TABLE_NAME = 'piwik_test_table';
+ const TEST_TEMP_TABLE_NAME = 'piwik_test_table_temp';
+
+ /**
+ * Checks that the DB user entered in the form has the necessary privileges for Piwik
+ * to run.
+ */
+ public function validateOwner()
+ {
+ // try and create the database object
+ try {
+ $this->createDatabaseObject();
+ } catch (Exception $ex) {
+ if ($this->isAccessDenied($ex)) {
+ return false;
+ } else {
+ return true; // if we can't create the database object, skip this validation
+ }
+ }
+
+ $db = Zend_Registry::get('db');
+
+ try {
+ // try to drop tables before running privilege tests
+ $this->dropExtraTables($db);
+ } catch (Exception $ex) {
+ if ($this->isAccessDenied($ex)) {
+ return false;
+ } else {
+ throw $ex;
+ }
+ }
+
+ // check each required privilege by running a query that uses it
+ foreach (self::getRequiredPrivileges() as $privilegeType => $queries) {
+ if (!is_array($queries)) {
+ $queries = array($queries);
+ }
+
+ foreach ($queries as $sql) {
+ try {
+ if (in_array($privilegeType, array('SELECT'))) {
+ $db->fetchAll($sql);
+ } else {
+ $db->exec($sql);
+ }
+ } catch (Exception $ex) {
+ if ($this->isAccessDenied($ex)) {
+ return false;
+ } else {
+ throw new Exception("Test SQL failed to execute: $sql\nError: " . $ex->getMessage());
+ }
+ }
+ }
+ }
+
+ // remove extra tables that were created
+ $this->dropExtraTables($db);
+
+ return true;
+ }
+
+ /**
+ * Returns an array describing the database privileges required for Piwik to run. The
+ * array maps privilege names with one or more SQL queries that can be used to test
+ * if the current user has the privilege.
+ *
+ * NOTE: LOAD DATA INFILE & LOCK TABLES privileges are not **required** so they're
+ * not checked.
+ *
+ * @return array
+ */
+ public static function getRequiredPrivileges()
+ {
+ return array(
+ 'CREATE' => 'CREATE TABLE ' . self::TEST_TABLE_NAME . ' (
id INT AUTO_INCREMENT,
value INT,
PRIMARY KEY (id),
KEY index_value (value)
)',
- 'ALTER' => 'ALTER TABLE '.self::TEST_TABLE_NAME.'
+ 'ALTER' => 'ALTER TABLE ' . self::TEST_TABLE_NAME . '
ADD COLUMN other_value INT DEFAULT 0',
- 'SELECT' => 'SELECT * FROM '.self::TEST_TABLE_NAME,
- 'INSERT' => 'INSERT INTO '.self::TEST_TABLE_NAME.' (value) VALUES (123)',
- 'UPDATE' => 'UPDATE '.self::TEST_TABLE_NAME.' SET value = 456 WHERE id = 1',
- 'DELETE' => 'DELETE FROM '.self::TEST_TABLE_NAME.' WHERE id = 1',
- 'DROP' => 'DROP TABLE '.self::TEST_TABLE_NAME,
- 'CREATE TEMPORARY TABLES' => 'CREATE TEMPORARY TABLE '.self::TEST_TEMP_TABLE_NAME.' (
+ 'SELECT' => 'SELECT * FROM ' . self::TEST_TABLE_NAME,
+ 'INSERT' => 'INSERT INTO ' . self::TEST_TABLE_NAME . ' (value) VALUES (123)',
+ 'UPDATE' => 'UPDATE ' . self::TEST_TABLE_NAME . ' SET value = 456 WHERE id = 1',
+ 'DELETE' => 'DELETE FROM ' . self::TEST_TABLE_NAME . ' WHERE id = 1',
+ 'DROP' => 'DROP TABLE ' . self::TEST_TABLE_NAME,
+ 'CREATE TEMPORARY TABLES' => 'CREATE TEMPORARY TABLE ' . self::TEST_TEMP_TABLE_NAME . ' (
id INT AUTO_INCREMENT,
PRIMARY KEY (id)
)',
- );
- }
-
- /**
- * Returns a string description of the database privileges required for Piwik to run.
- *
- * @return string
- */
- public static function getRequiredPrivilegesPretty()
- {
- return implode('<br/>', array_keys(self::getRequiredPrivileges()));
- }
-
- /**
- * Checks if an exception that was thrown after running a query represents an 'access denied'
- * error.
- *
- * @param Exception $ex The exception to check.
- * @return bool
- */
- private function isAccessDenied( $ex )
- {
- //NOte: this code is duplicated in Tracker.php error handler
- return $ex->getCode() == 1044 || $ex->getCode() == 42000;
- }
-
- /**
- * Creates a database object using the connection information entered in the form.
- *
- * @return array
- */
- private function createDatabaseObject()
- {
- return $this->owner->getContainer()->createDatabaseObject();
- }
-
- /**
- * Drops the tables created by the privilege checking queries, if they exist.
- *
- * @param $db The database object to use.
- */
- private function dropExtraTables( $db )
- {
- $db->query('DROP TABLE IF EXISTS '.self::TEST_TABLE_NAME.', '.self::TEST_TEMP_TABLE_NAME);
- }
+ );
+ }
+
+ /**
+ * Returns a string description of the database privileges required for Piwik to run.
+ *
+ * @return string
+ */
+ public static function getRequiredPrivilegesPretty()
+ {
+ return implode('<br/>', array_keys(self::getRequiredPrivileges()));
+ }
+
+ /**
+ * Checks if an exception that was thrown after running a query represents an 'access denied'
+ * error.
+ *
+ * @param Exception $ex The exception to check.
+ * @return bool
+ */
+ private function isAccessDenied($ex)
+ {
+ //NOte: this code is duplicated in Tracker.php error handler
+ return $ex->getCode() == 1044 || $ex->getCode() == 42000;
+ }
+
+ /**
+ * Creates a database object using the connection information entered in the form.
+ *
+ * @return array
+ */
+ private function createDatabaseObject()
+ {
+ return $this->owner->getContainer()->createDatabaseObject();
+ }
+
+ /**
+ * Drops the tables created by the privilege checking queries, if they exist.
+ *
+ * @param $db The database object to use.
+ */
+ private function dropExtraTables($db)
+ {
+ $db->query('DROP TABLE IF EXISTS ' . self::TEST_TABLE_NAME . ', ' . self::TEST_TEMP_TABLE_NAME);
+ }
}
/**
@@ -321,9 +293,9 @@ class Piwik_Installation_FormDatabaseSetup_Rule_checkUserPrivileges extends HTML
*/
class Piwik_Installation_FormDatabaseSetup_Rule_checkValidFilename extends HTML_QuickForm2_Rule
{
- function validateOwner()
- {
- return Piwik_Common::isValidFilename($this->owner->getValue());
- }
+ function validateOwner()
+ {
+ return Piwik_Common::isValidFilename($this->owner->getValue());
+ }
}
diff --git a/plugins/Installation/FormFirstWebsiteSetup.php b/plugins/Installation/FormFirstWebsiteSetup.php
index 8629d12021..d44151f1b5 100644
--- a/plugins/Installation/FormFirstWebsiteSetup.php
+++ b/plugins/Installation/FormFirstWebsiteSetup.php
@@ -1,65 +1,65 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Installation
*/
/**
- *
+ *
* @package Piwik_Installation
*/
class Piwik_Installation_FormFirstWebsiteSetup extends Piwik_QuickForm2
{
- function __construct( $id = 'websitesetupform', $method = 'post', $attributes = null, $trackSubmit = false)
- {
- parent::__construct($id, $method, $attributes, $trackSubmit);
- }
+ function __construct($id = 'websitesetupform', $method = 'post', $attributes = null, $trackSubmit = false)
+ {
+ parent::__construct($id, $method, $attributes, $trackSubmit);
+ }
- function init()
- {
- HTML_QuickForm2_Factory::registerRule('checkTimezone', 'Piwik_Installation_FormFirstWebsiteSetup_Rule_isValidTimezone');
+ function init()
+ {
+ HTML_QuickForm2_Factory::registerRule('checkTimezone', 'Piwik_Installation_FormFirstWebsiteSetup_Rule_isValidTimezone');
- $urlExample = 'http://example.org';
- $javascriptOnClickUrlExample = "javascript:if(this.value=='$urlExample'){this.value='http://';} this.style.color='black';";
+ $urlExample = 'http://example.org';
+ $javascriptOnClickUrlExample = "javascript:if(this.value=='$urlExample'){this.value='http://';} this.style.color='black';";
- $timezones = Piwik_SitesManager_API::getInstance()->getTimezonesList();
- $timezones = array_merge(array('No timezone' => Piwik_Translate('SitesManager_SelectACity')), $timezones);
+ $timezones = Piwik_SitesManager_API::getInstance()->getTimezonesList();
+ $timezones = array_merge(array('No timezone' => Piwik_Translate('SitesManager_SelectACity')), $timezones);
- $this->addElement('text', 'siteName')
- ->setLabel(Piwik_Translate('Installation_SetupWebSiteName'))
- ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_SetupWebSiteName')));
+ $this->addElement('text', 'siteName')
+ ->setLabel(Piwik_Translate('Installation_SetupWebSiteName'))
+ ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_SetupWebSiteName')));
- $url = $this->addElement('text', 'url')
- ->setLabel(Piwik_Translate('Installation_SetupWebSiteURL'));
- $url->setAttribute('style', 'color:rgb(153, 153, 153);');
- $url->setAttribute('onfocus', $javascriptOnClickUrlExample);
- $url->setAttribute('onclick', $javascriptOnClickUrlExample);
- $url->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_SetupWebSiteURL')));
+ $url = $this->addElement('text', 'url')
+ ->setLabel(Piwik_Translate('Installation_SetupWebSiteURL'));
+ $url->setAttribute('style', 'color:rgb(153, 153, 153);');
+ $url->setAttribute('onfocus', $javascriptOnClickUrlExample);
+ $url->setAttribute('onclick', $javascriptOnClickUrlExample);
+ $url->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_SetupWebSiteURL')));
- $tz = $this->addElement('select', 'timezone')
- ->setLabel(Piwik_Translate('Installation_Timezone'))
- ->loadOptions($timezones);
- $tz->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_Timezone')));
- $tz->addRule('checkTimezone', Piwik_Translate('General_NotValid', Piwik_Translate('Installation_Timezone')));
- $tz = $this->addElement('select', 'ecommerce')
- ->setLabel(Piwik_Translate('Goals_Ecommerce'))
- ->loadOptions(array(
- 0 => Piwik_Translate('SitesManager_NotAnEcommerceSite'),
- 1 => Piwik_Translate('SitesManager_EnableEcommerce'),
- ));
+ $tz = $this->addElement('select', 'timezone')
+ ->setLabel(Piwik_Translate('Installation_Timezone'))
+ ->loadOptions($timezones);
+ $tz->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_Timezone')));
+ $tz->addRule('checkTimezone', Piwik_Translate('General_NotValid', Piwik_Translate('Installation_Timezone')));
+ $tz = $this->addElement('select', 'ecommerce')
+ ->setLabel(Piwik_Translate('Goals_Ecommerce'))
+ ->loadOptions(array(
+ 0 => Piwik_Translate('SitesManager_NotAnEcommerceSite'),
+ 1 => Piwik_Translate('SitesManager_EnableEcommerce'),
+ ));
- $this->addElement('submit', 'submit', array('value' => Piwik_Translate('General_Next').' »', 'class' => 'submit'));
+ $this->addElement('submit', 'submit', array('value' => Piwik_Translate('General_Next') . ' »', 'class' => 'submit'));
- // default values
- $this->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
- 'url' => $urlExample,
- )));
- }
+ // default values
+ $this->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
+ 'url' => $urlExample,
+ )));
+ }
}
/**
@@ -69,17 +69,16 @@ class Piwik_Installation_FormFirstWebsiteSetup extends Piwik_QuickForm2
*/
class Piwik_Installation_FormFirstWebsiteSetup_Rule_isValidTimezone extends HTML_QuickForm2_Rule
{
- function validateOwner()
- {
- try {
- $timezone = $this->owner->getValue();
- if(!empty($timezone))
- {
- Piwik_SitesManager_API::getInstance()->setDefaultTimezone($timezone);
- }
- } catch(Exception $e) {
- return false;
- }
- return true;
- }
+ function validateOwner()
+ {
+ try {
+ $timezone = $this->owner->getValue();
+ if (!empty($timezone)) {
+ Piwik_SitesManager_API::getInstance()->setDefaultTimezone($timezone);
+ }
+ } catch (Exception $e) {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/plugins/Installation/FormGeneralSetup.php b/plugins/Installation/FormGeneralSetup.php
index 8d1f23a5c1..345b027298 100644
--- a/plugins/Installation/FormGeneralSetup.php
+++ b/plugins/Installation/FormGeneralSetup.php
@@ -1,65 +1,65 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Installation
*/
/**
- *
+ *
* @package Piwik_Installation
*/
class Piwik_Installation_FormGeneralSetup extends Piwik_QuickForm2
{
- function __construct( $id = 'generalsetupform', $method = 'post', $attributes = null, $trackSubmit = false)
- {
- parent::__construct($id, $method, $attributes = array('autocomplete' => 'off'), $trackSubmit);
- }
+ function __construct($id = 'generalsetupform', $method = 'post', $attributes = null, $trackSubmit = false)
+ {
+ parent::__construct($id, $method, $attributes = array('autocomplete' => 'off'), $trackSubmit);
+ }
- function init()
- {
- HTML_QuickForm2_Factory::registerRule('checkLogin', 'Piwik_Installation_FormGeneralSetup_Rule_isValidLoginString');
- HTML_QuickForm2_Factory::registerRule('checkEmail', 'Piwik_Installation_FormGeneralSetup_Rule_isValidEmailString');
+ function init()
+ {
+ HTML_QuickForm2_Factory::registerRule('checkLogin', 'Piwik_Installation_FormGeneralSetup_Rule_isValidLoginString');
+ HTML_QuickForm2_Factory::registerRule('checkEmail', 'Piwik_Installation_FormGeneralSetup_Rule_isValidEmailString');
- $login = $this->addElement('text', 'login')
- ->setLabel(Piwik_Translate('Installation_SuperUserLogin'));
- $login->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_SuperUserLogin')));
- $login->addRule('checkLogin');
+ $login = $this->addElement('text', 'login')
+ ->setLabel(Piwik_Translate('Installation_SuperUserLogin'));
+ $login->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_SuperUserLogin')));
+ $login->addRule('checkLogin');
- $password = $this->addElement('password', 'password')
- ->setLabel(Piwik_Translate('Installation_Password'));
- $password->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_Password')));
+ $password = $this->addElement('password', 'password')
+ ->setLabel(Piwik_Translate('Installation_Password'));
+ $password->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_Password')));
- $passwordBis = $this->addElement('password', 'password_bis')
- ->setLabel(Piwik_Translate('Installation_PasswordRepeat'));
- $passwordBis->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_PasswordRepeat')));
- $passwordBis->addRule('eq', Piwik_Translate( 'Installation_PasswordDoNotMatch'), $password);
+ $passwordBis = $this->addElement('password', 'password_bis')
+ ->setLabel(Piwik_Translate('Installation_PasswordRepeat'));
+ $passwordBis->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_PasswordRepeat')));
+ $passwordBis->addRule('eq', Piwik_Translate('Installation_PasswordDoNotMatch'), $password);
- $email = $this->addElement('text', 'email')
- ->setLabel(Piwik_Translate('Installation_Email'));
- $email->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_Email')));
- $email->addRule('checkEmail', Piwik_Translate( 'UsersManager_ExceptionInvalidEmail'));
+ $email = $this->addElement('text', 'email')
+ ->setLabel(Piwik_Translate('Installation_Email'));
+ $email->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Installation_Email')));
+ $email->addRule('checkEmail', Piwik_Translate('UsersManager_ExceptionInvalidEmail'));
- $this->addElement('checkbox', 'subscribe_newsletter_security', null, array(
- 'content' => '&nbsp;&nbsp;' . Piwik_Translate('Installation_SecurityNewsletter'),
- ));
+ $this->addElement('checkbox', 'subscribe_newsletter_security', null, array(
+ 'content' => '&nbsp;&nbsp;' . Piwik_Translate('Installation_SecurityNewsletter'),
+ ));
- $this->addElement('checkbox', 'subscribe_newsletter_community', null, array(
- 'content' => '&nbsp;&nbsp;' . Piwik_Translate('Installation_CommunityNewsletter'),
- ));
+ $this->addElement('checkbox', 'subscribe_newsletter_community', null, array(
+ 'content' => '&nbsp;&nbsp;' . Piwik_Translate('Installation_CommunityNewsletter'),
+ ));
- $this->addElement('submit', 'submit', array('value' => Piwik_Translate('General_Next').' »', 'class' => 'submit'));
+ $this->addElement('submit', 'submit', array('value' => Piwik_Translate('General_Next') . ' »', 'class' => 'submit'));
- // default values
- $this->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
- 'subscribe_newsletter_community' => 1,
- 'subscribe_newsletter_security' => 1,
- )));
- }
+ // default values
+ $this->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
+ 'subscribe_newsletter_community' => 1,
+ 'subscribe_newsletter_security' => 1,
+ )));
+ }
}
/**
@@ -69,20 +69,19 @@ class Piwik_Installation_FormGeneralSetup extends Piwik_QuickForm2
*/
class Piwik_Installation_FormGeneralSetup_Rule_isValidLoginString extends HTML_QuickForm2_Rule
{
- function validateOwner()
- {
- try {
- $login = $this->owner->getValue();
- if(!empty($login))
- {
- Piwik::checkValidLoginString($login);
- }
- } catch(Exception $e) {
- $this->setMessage($e->getMessage());
- return false;
- }
- return true;
- }
+ function validateOwner()
+ {
+ try {
+ $login = $this->owner->getValue();
+ if (!empty($login)) {
+ Piwik::checkValidLoginString($login);
+ }
+ } catch (Exception $e) {
+ $this->setMessage($e->getMessage());
+ return false;
+ }
+ return true;
+ }
}
/**
@@ -92,8 +91,8 @@ class Piwik_Installation_FormGeneralSetup_Rule_isValidLoginString extends HTML_Q
*/
class Piwik_Installation_FormGeneralSetup_Rule_isValidEmailString extends HTML_QuickForm2_Rule
{
- function validateOwner()
- {
- return Piwik::isValidEmailString($this->owner->getValue());
- }
+ function validateOwner()
+ {
+ return Piwik::isValidEmailString($this->owner->getValue());
+ }
}
diff --git a/plugins/Installation/Installation.php b/plugins/Installation/Installation.php
index 793ae38210..ede139e2aa 100644
--- a/plugins/Installation/Installation.php
+++ b/plugins/Installation/Installation.php
@@ -1,105 +1,99 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Installation
*/
/**
- *
+ *
* @package Piwik_Installation
*/
class Piwik_Installation extends Piwik_Plugin
-{
- protected $installationControllerName = 'Piwik_Installation_Controller';
+{
+ protected $installationControllerName = 'Piwik_Installation_Controller';
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('Installation_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
-
- return $info;
- }
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('Installation_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
- function getListHooksRegistered()
- {
- $hooks = array(
- 'FrontController.NoConfigurationFile' => 'dispatch',
- 'FrontController.badConfigurationFile' => 'dispatch',
- 'AdminMenu.add' => 'addMenu',
- 'AssetManager.getCssFiles' => 'getCss',
- );
- return $hooks;
- }
+ return $info;
+ }
- public function setControllerToLoad( $newControllerName )
- {
- $this->installationControllerName = $newControllerName;
- }
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'FrontController.NoConfigurationFile' => 'dispatch',
+ 'FrontController.badConfigurationFile' => 'dispatch',
+ 'AdminMenu.add' => 'addMenu',
+ 'AssetManager.getCssFiles' => 'getCss',
+ );
+ return $hooks;
+ }
- protected function getInstallationController()
- {
- return new $this->installationControllerName();
- }
+ public function setControllerToLoad($newControllerName)
+ {
+ $this->installationControllerName = $newControllerName;
+ }
- /**
- * @param Piwik_Event_Notification|null $notification notification object
- */
- function dispatch($notification = null)
- {
- if($notification)
- {
- $exception = $notification->getNotificationObject();
- $message = $exception->getMessage();
- }
- else
- {
- $message = '';
- }
+ protected function getInstallationController()
+ {
+ return new $this->installationControllerName();
+ }
- Piwik_Translate::getInstance()->loadCoreTranslation();
+ /**
+ * @param Piwik_Event_Notification|null $notification notification object
+ */
+ function dispatch($notification = null)
+ {
+ if ($notification) {
+ $exception = $notification->getNotificationObject();
+ $message = $exception->getMessage();
+ } else {
+ $message = '';
+ }
- Piwik_PostEvent('Installation.startInstallation', $this);
+ Piwik_Translate::getInstance()->loadCoreTranslation();
- $step = Piwik_Common::getRequestVar('action', 'welcome', 'string');
- $controller = $this->getInstallationController();
- if(in_array($step, array_keys($controller->getInstallationSteps())) || $step == 'saveLanguage')
- {
- $controller->$step($message);
- }
- else
- {
- Piwik::exitWithErrorMessage(Piwik_Translate('Installation_NoConfigFound'));
- }
+ Piwik_PostEvent('Installation.startInstallation', $this);
- exit;
- }
+ $step = Piwik_Common::getRequestVar('action', 'welcome', 'string');
+ $controller = $this->getInstallationController();
+ if (in_array($step, array_keys($controller->getInstallationSteps())) || $step == 'saveLanguage') {
+ $controller->$step($message);
+ } else {
+ Piwik::exitWithErrorMessage(Piwik_Translate('Installation_NoConfigFound'));
+ }
+
+ exit;
+ }
- /**
- * Adds the 'System Check' admin page if the user is the super user.
- */
- public function addMenu()
- {
- Piwik_AddAdminSubMenu('CoreAdminHome_MenuDiagnostic', 'Installation_SystemCheck',
- array('module' => 'Installation', 'action' => 'systemCheckPage'),
- $addIf = Piwik::isUserIsSuperUser(),
- $order = 15);
+ /**
+ * Adds the 'System Check' admin page if the user is the super user.
+ */
+ public function addMenu()
+ {
+ Piwik_AddAdminSubMenu('CoreAdminHome_MenuDiagnostic', 'Installation_SystemCheck',
+ array('module' => 'Installation', 'action' => 'systemCheckPage'),
+ $addIf = Piwik::isUserIsSuperUser(),
+ $order = 15);
}
-
+
/**
* Adds CSS files to list of CSS files for asset manager.
*/
- public function getCss( $notification )
+ public function getCss($notification)
{
- $cssFiles = &$notification->getNotificationObject();
+ $cssFiles = & $notification->getNotificationObject();
$cssFiles[] = "plugins/Installation/templates/systemCheckPage.css";
}
diff --git a/plugins/Installation/View.php b/plugins/Installation/View.php
index c55fcb4ac2..1bd4866ab0 100644
--- a/plugins/Installation/View.php
+++ b/plugins/Installation/View.php
@@ -1,58 +1,55 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Installation
*/
/**
- *
+ *
* @package Piwik_Installation
*/
class Piwik_Installation_View extends Piwik_View
{
- protected $mainTemplate = 'Installation/templates/structure.tpl';
-
- function __construct($subtemplatePath, $installationSteps, $currentStepName)
- {
- parent::__construct($this->mainTemplate);
+ protected $mainTemplate = 'Installation/templates/structure.tpl';
- $this->subTemplateToLoad = $subtemplatePath;
- $this->steps = array_keys($installationSteps);
- $this->allStepsTitle = array_values($installationSteps);
- $this->currentStepName = $currentStepName;
- $this->showNextStep = false;
- }
-
- function render()
- {
- // prepare the all steps templates
- $this->currentStepId = array_search($this->currentStepName, $this->steps);
- $this->totalNumberOfSteps = count($this->steps);
-
- $this->percentDone = round(($this->currentStepId) * 100 / ($this->totalNumberOfSteps-1));
- $this->percentToDo = 100 - $this->percentDone;
-
- $this->nextModuleName = '';
- if(isset($this->steps[$this->currentStepId + 1]))
- {
- $this->nextModuleName = $this->steps[$this->currentStepId + 1];
- }
- $this->previousModuleName = '';
- if(isset($this->steps[$this->currentStepId - 1]))
- {
- $this->previousModuleName = $this->steps[$this->currentStepId - 1];
- }
- $this->previousPreviousModuleName = '';
- if(isset($this->steps[$this->currentStepId - 2]))
- {
- $this->previousPreviousModuleName = $this->steps[$this->currentStepId - 2];
- }
+ function __construct($subtemplatePath, $installationSteps, $currentStepName)
+ {
+ parent::__construct($this->mainTemplate);
- return parent::render();
- }
+ $this->subTemplateToLoad = $subtemplatePath;
+ $this->steps = array_keys($installationSteps);
+ $this->allStepsTitle = array_values($installationSteps);
+ $this->currentStepName = $currentStepName;
+ $this->showNextStep = false;
+ }
+
+ function render()
+ {
+ // prepare the all steps templates
+ $this->currentStepId = array_search($this->currentStepName, $this->steps);
+ $this->totalNumberOfSteps = count($this->steps);
+
+ $this->percentDone = round(($this->currentStepId) * 100 / ($this->totalNumberOfSteps - 1));
+ $this->percentToDo = 100 - $this->percentDone;
+
+ $this->nextModuleName = '';
+ if (isset($this->steps[$this->currentStepId + 1])) {
+ $this->nextModuleName = $this->steps[$this->currentStepId + 1];
+ }
+ $this->previousModuleName = '';
+ if (isset($this->steps[$this->currentStepId - 1])) {
+ $this->previousModuleName = $this->steps[$this->currentStepId - 1];
+ }
+ $this->previousPreviousModuleName = '';
+ if (isset($this->steps[$this->currentStepId - 2])) {
+ $this->previousPreviousModuleName = $this->steps[$this->currentStepId - 2];
+ }
+
+ return parent::render();
+ }
}
diff --git a/plugins/Installation/templates/allSteps.tpl b/plugins/Installation/templates/allSteps.tpl
index 5cba61e4d5..fa4bcf0063 100644
--- a/plugins/Installation/templates/allSteps.tpl
+++ b/plugins/Installation/templates/allSteps.tpl
@@ -1,11 +1,11 @@
<ul>
-{foreach from=$allStepsTitle key=stepId item=stepName}
- {if $currentStepId > $stepId}
- <li class="pastStep">{$stepName|translate}</li>
- {elseif $currentStepId == $stepId}
- <li class="actualStep">{$stepName|translate}</li>
- {else}
- <li class="futureStep">{$stepName|translate}</li>
- {/if}
-{/foreach}
+ {foreach from=$allStepsTitle key=stepId item=stepName}
+ {if $currentStepId > $stepId}
+ <li class="pastStep">{$stepName|translate}</li>
+ {elseif $currentStepId == $stepId}
+ <li class="actualStep">{$stepName|translate}</li>
+ {else}
+ <li class="futureStep">{$stepName|translate}</li>
+ {/if}
+ {/foreach}
</ul>
diff --git a/plugins/Installation/templates/databaseCheck.tpl b/plugins/Installation/templates/databaseCheck.tpl
index 07916d11b4..3f0309f1ee 100644
--- a/plugins/Installation/templates/databaseCheck.tpl
+++ b/plugins/Installation/templates/databaseCheck.tpl
@@ -6,29 +6,29 @@
<h2>{'Installation_DatabaseCheck'|translate}</h2>
<table class="infosServer">
- <tr>
- <td class="label">{'Installation_DatabaseServerVersion'|translate}</td>
- <td>{if isset($databaseVersionOk)}{$ok}{else}{$error}{/if}</td>
- </tr>
- <tr>
- <td class="label">{'Installation_DatabaseClientVersion'|translate}</td>
- <td>{if isset($clientVersionWarning)}{$warning}{else}{$ok}{/if}</td>
- </tr>
-{if isset($clientVersionWarning)}
- <tr>
- <td colspan="2">
- <small>
- <span style="color:#FF7F00">{$clientVersionWarning}</span>
- </small>
- </td>
- </tr>
-{/if}
- <tr>
- <td class="label">{'Installation_DatabaseCreation'|translate}</td>
- <td>{if isset($databaseCreated)}{$ok}{else}{$error}{/if}</td>
- </tr>
+ <tr>
+ <td class="label">{'Installation_DatabaseServerVersion'|translate}</td>
+ <td>{if isset($databaseVersionOk)}{$ok}{else}{$error}{/if}</td>
+ </tr>
+ <tr>
+ <td class="label">{'Installation_DatabaseClientVersion'|translate}</td>
+ <td>{if isset($clientVersionWarning)}{$warning}{else}{$ok}{/if}</td>
+ </tr>
+ {if isset($clientVersionWarning)}
+ <tr>
+ <td colspan="2">
+ <small>
+ <span style="color:#FF7F00">{$clientVersionWarning}</span>
+ </small>
+ </td>
+ </tr>
+ {/if}
+ <tr>
+ <td class="label">{'Installation_DatabaseCreation'|translate}</td>
+ <td>{if isset($databaseCreated)}{$ok}{else}{$error}{/if}</td>
+ </tr>
</table>
<p>
-{$link} <a href="?module=Proxy&action=redirect&url=http://piwik.org/docs/requirements/" target="_blank">{'Installation_Requirements'|translate}</a>
+ {$link} <a href="?module=Proxy&action=redirect&url=http://piwik.org/docs/requirements/" target="_blank">{'Installation_Requirements'|translate}</a>
</p>
diff --git a/plugins/Installation/templates/databaseSetup.tpl b/plugins/Installation/templates/databaseSetup.tpl
index 22cd8e0469..d5546fb28d 100644
--- a/plugins/Installation/templates/databaseSetup.tpl
+++ b/plugins/Installation/templates/databaseSetup.tpl
@@ -1,14 +1,14 @@
<h2>{'Installation_DatabaseSetup'|translate}</h2>
{if isset($errorMessage)}
- <div class="error">
- <img src="themes/default/images/error_medium.png" />
- {'Installation_DatabaseErrorConnect'|translate}:
- <br />{$errorMessage}
-
- </div>
+ <div class="error">
+ <img src="themes/default/images/error_medium.png"/>
+ {'Installation_DatabaseErrorConnect'|translate}:
+ <br/>{$errorMessage}
+
+ </div>
{/if}
{if isset($form_data)}
- {include file="default/genericForm.tpl"}
+ {include file="default/genericForm.tpl"}
{/if}
diff --git a/plugins/Installation/templates/displayJavascriptCode.tpl b/plugins/Installation/templates/displayJavascriptCode.tpl
index 1b0c00e0c5..78abfca64c 100644
--- a/plugins/Installation/templates/displayJavascriptCode.tpl
+++ b/plugins/Installation/templates/displayJavascriptCode.tpl
@@ -1,10 +1,7 @@
-
-
{if isset($displayfirstWebsiteSetupSuccess)}
-
-<span id="toFade" class="success">
+ <span id="toFade" class="success">
{'Installation_SetupWebsiteSetupSuccess'|translate:$displaySiteName}
- <img src="themes/default/images/success_medium.png" />
+ <img src="themes/default/images/success_medium.png"/>
</span>
{/if}
@@ -14,15 +11,14 @@
{'Installation_JsTagArchivingHelp1'|translate:'<a target="_blank" href="http://piwik.org/docs/setup-auto-archiving/">':'</a>'} {'General_ReadThisToLearnMore'|translate:'<a target="_blank" href="http://piwik.org/docs/optimize/">':'</a>'}
{literal}
-<style type="text/css">
-code {
- font-size:80%;
-}
-</style>
-<script>
-$(document).ready( function(){
- $('code').click( function(){ $(this).select(); });
-});
-</script>
-
+ <style type="text/css">
+ code {
+ font-size: 80%;
+ }
+ </style>
+ <script>
+ $(document).ready(function () {
+ $('code').click(function () { $(this).select(); });
+ });
+ </script>
{/literal}
diff --git a/plugins/Installation/templates/finished.tpl b/plugins/Installation/templates/finished.tpl
index e1137c510e..ebf32507f6 100644
--- a/plugins/Installation/templates/finished.tpl
+++ b/plugins/Installation/templates/finished.tpl
@@ -4,5 +4,5 @@
<p class="nextStep">
- <a class="submit" href="index.php">{'Installation_ContinueToPiwik'|translate} &raquo;</a>
+ <a class="submit" href="index.php">{'Installation_ContinueToPiwik'|translate} &raquo;</a>
</p>
diff --git a/plugins/Installation/templates/firstWebsiteSetup.tpl b/plugins/Installation/templates/firstWebsiteSetup.tpl
index 41360cdd2d..1db08d0674 100644
--- a/plugins/Installation/templates/firstWebsiteSetup.tpl
+++ b/plugins/Installation/templates/firstWebsiteSetup.tpl
@@ -1,25 +1,23 @@
-
-
{if isset($displayGeneralSetupSuccess)}
-<span id="toFade" class="success">
+ <span id="toFade" class="success">
{'Installation_SuperUserSetupSuccess'|translate}
- <img src="themes/default/images/success_medium.png" />
+ <img src="themes/default/images/success_medium.png"/>
</span>
{/if}
<h2>{'Installation_SetupWebsite'|translate}</h2>
<p>{'Installation_SiteSetup'|translate}</p>
{if isset($errorMessage)}
- <div class="error">
- <img src="themes/default/images/error_medium.png" />
- {'Installation_SetupWebsiteError'|translate}:
- <br />- {$errorMessage}
-
- </div>
+ <div class="error">
+ <img src="themes/default/images/error_medium.png"/>
+ {'Installation_SetupWebsiteError'|translate}:
+ <br/>- {$errorMessage}
+
+ </div>
{/if}
{if isset($form_data)}
- {include file="default/genericForm.tpl"}
+ {include file="default/genericForm.tpl"}
{/if}
<br/>
<p><i>{'Installation_SiteSetupFootnote'|translate}</i></p> \ No newline at end of file
diff --git a/plugins/Installation/templates/generalSetup.tpl b/plugins/Installation/templates/generalSetup.tpl
index 6fc4f246f3..29406ec657 100644
--- a/plugins/Installation/templates/generalSetup.tpl
+++ b/plugins/Installation/templates/generalSetup.tpl
@@ -1,5 +1,5 @@
<h2>{'Installation_SuperUser'|translate}</h2>
{if isset($form_data)}
- {include file="default/genericForm.tpl"}
+ {include file="default/genericForm.tpl"}
{/if}
diff --git a/plugins/Installation/templates/install.css b/plugins/Installation/templates/install.css
index 3f605feee9..ba67fc2531 100644
--- a/plugins/Installation/templates/install.css
+++ b/plugins/Installation/templates/install.css
@@ -1,195 +1,215 @@
-
div.both {
- clear: both;
+ clear: both;
}
body {
- background-color: #FFFBF9;
- text-align: center;
- font-family:Arial,Georgia,"Times New Roman",Times,serif;
- font-size:17px;
+ background-color: #FFFBF9;
+ text-align: center;
+ font-family: Arial, Georgia, "Times New Roman", Times, serif;
+ font-size: 17px;
}
+
p {
- margin-bottom:5px;
- margin-top:10px;
+ margin-bottom: 5px;
+ margin-top: 10px;
}
-#title{
- font-size:50px;
- color:#284F92;
- vertical-align: text-bottom;
+
+#title {
+ font-size: 50px;
+ color: #284F92;
+ vertical-align: text-bottom;
}
-#subtitle{
- font-size:30px;
- color: #6B320B;
- font-size: 27px;
+#subtitle {
+ font-size: 30px;
+ color: #6B320B;
+ font-size: 27px;
}
#logo {
- padding: 20px 30px 40px;
+ padding: 20px 30px 40px;
}
h2 {
- font-size:20px;
- color:#666666;
- border-bottom:1px solid #DADADA;
- padding:0 0 7px;
+ font-size: 20px;
+ color: #666666;
+ border-bottom: 1px solid #DADADA;
+ padding: 0 0 7px;
}
h3 {
- margin-top:10px;
- font-size:17px;
- color:#3F5163;
+ margin-top: 10px;
+ font-size: 17px;
+ color: #3F5163;
}
.topBarElem {
- font-family:arial,sans-serif !important;
- font-size:13px;
- line-height:1.33;
+ font-family: arial, sans-serif !important;
+ font-size: 13px;
+ line-height: 1.33;
}
#topRightBar {
- float:right;top:-60px; right:10px;position:relative;
+ float: right;
+ top: -60px;
+ right: 10px;
+ position: relative;
}
+
.error {
- color:red;
- font-size:100%;
- font-weight:bold;
- border: 2px solid red;
- width: 550px;
- padding:20px;
- margin-bottom:10px;
+ color: red;
+ font-size: 100%;
+ font-weight: bold;
+ border: 2px solid red;
+ width: 550px;
+ padding: 20px;
+ margin-bottom: 10px;
}
.error img {
- border:0;
- float:right;
- margin:10px;
+ border: 0;
+ float: right;
+ margin: 10px;
}
+
.success {
- color:#26981C;
- font-size:130%;
- font-weight:bold;
- padding:10px;
+ color: #26981C;
+ font-size: 130%;
+ font-weight: bold;
+ padding: 10px;
}
+
.warn {
- color:#D7A006;
- font-weight:bold;
+ color: #D7A006;
+ font-weight: bold;
}
+
.warning {
- margin:10px;
- color:#ff5502;
- font-size:130%;
- font-weight:bold;
- padding:10px 20px 10px 30px;
- border: 1px solid #ff5502;
+ margin: 10px;
+ color: #ff5502;
+ font-size: 130%;
+ font-weight: bold;
+ padding: 10px 20px 10px 30px;
+ border: 1px solid #ff5502;
}
.warning ul {
- list-style:disc;
+ list-style: disc;
}
.success img, .warning img {
- border:0;
- vertical-align:bottom;
+ border: 0;
+ vertical-align: bottom;
}
-
/* Cadre general */
#main {
- margin: 30px 5px 5px;
- text-align: left;
+ margin: 30px 5px 5px;
+ text-align: left;
}
#content {
- font-size: 90%;
- line-height: 1.4em;
- width: 860px;
- border: 1px solid #7A5A3F;
- text-align: $rightouleft;
- margin: auto;
- background: #FFFFFF;
- padding: 0.2em 2em 2em 2em;
- border-radius: 8px;
- -moz-border-radius: 8px;
- -webkit-border-radius: 8px;
+ font-size: 90%;
+ line-height: 1.4em;
+ width: 860px;
+ border: 1px solid #7A5A3F;
+ text-align: $ rightouleft;
+ margin: auto;
+ background: #FFFFFF;
+ padding: 0.2em 2em 2em 2em;
+ border-radius: 8px;
+ -moz-border-radius: 8px;
+ -webkit-border-radius: 8px;
}
+
/* form errors */
#adminErrors {
- color:#FF6E46;
- font-size:120%;
+ color: #FF6E46;
+ font-size: 120%;
}
+
/* listing all the steps */
#generalInstall {
- float: left;
- margin-left:20px;
- width:19%;
+ float: left;
+ margin-left: 20px;
+ width: 19%;
}
#detailInstall {
- width:75%;
- float: right;
+ width: 75%;
+ float: right;
}
#generalInstall ul {
- list-style-type: decimal;
+ list-style-type: decimal;
}
+
li.futureStep {
- color: #d3d3d3;
+ color: #d3d3d3;
}
+
li.actualStep {
- font-weight: bold;
+ font-weight: bold;
}
+
li.pastStep {
- color: #008000;
+ color: #008000;
}
p.nextStep a {
- text-decoration: none;
+ text-decoration: none;
}
td {
- border: 1px solid rgb(198, 205, 216);
- border-top-color: #FFF;
- color:#203276;
- padding:0.5em 0.5em 0.5em 0.8em;
+ border: 1px solid rgb(198, 205, 216);
+ border-top-color: #FFF;
+ color: #203276;
+ padding: 0.5em 0.5em 0.5em 0.8em;
}
.submit {
- text-align:center;
- cursor: pointer;
+ text-align: center;
+ cursor: pointer;
}
+
.submit input {
- margin-top:15px;
- background:transparent url(./themes/default/images/background-submit.png) repeat scroll 0;
- font-size:1.4em;
- border-color:#CCCCCC rgb(153, 153, 153) rgb(153, 153, 153) rgb(204, 204, 204);
- border-style:double;
- border-width:3px;
- color:#333333;
- padding:0.15em;
-}
-
-input {
- font-size:18px;
- border-color:#CCCCCC rgb(153, 153, 153) rgb(153, 153, 153) rgb(204, 204, 204);
- border-width:1px;
- color:#3A2B16;
- padding:0.15em;
+ margin-top: 15px;
+ background: transparent url(./themes/default/images/background-submit.png) repeat scroll 0;
+ font-size: 1.4em;
+ border-color: #CCCCCC rgb(153, 153, 153) rgb(153, 153, 153) rgb(204, 204, 204);
+ border-style: double;
+ border-width: 3px;
+ color: #333333;
+ padding: 0.15em;
}
+
+input {
+ font-size: 18px;
+ border-color: #CCCCCC rgb(153, 153, 153) rgb(153, 153, 153) rgb(204, 204, 204);
+ border-width: 1px;
+ color: #3A2B16;
+ padding: 0.15em;
+}
+
#systemCheckLegend {
- border:1px solid #A5A5A5;
- padding:20px;
- color:#727272;
- margin-top:30px;
+ border: 1px solid #A5A5A5;
+ padding: 20px;
+ color: #727272;
+ margin-top: 30px;
}
+
#systemCheckLegend img {
- padding-right:10px;
+ padding-right: 10px;
vertical-align: middle;
}
-.infos img , .infosServer img {
- padding-right:10px;
- vertical-align:middle;
+
+.infos img, .infosServer img {
+ padding-right: 10px;
+ vertical-align: middle;
+}
+
+.err {
+ color: red;
+ font-weight: bold;
}
-.err { color:red;font-weight:bold;}
diff --git a/plugins/Installation/templates/integrityDetails.tpl b/plugins/Installation/templates/integrityDetails.tpl
index 074f93faca..66882fa3d1 100644
--- a/plugins/Installation/templates/integrityDetails.tpl
+++ b/plugins/Installation/templates/integrityDetails.tpl
@@ -1,42 +1,45 @@
{if !isset($warningMessages)}
-{assign var=warningMessages value=$infos.integrityErrorMessages}
+ {assign var=warningMessages value=$infos.integrityErrorMessages}
{/if}
<div id="integrity-results" title="{'Installation_SystemCheckFileIntegrity'|translate}" style="display:none; font-size: 62.5%;">
- <table>
- {foreach from=$warningMessages item=msg}
- <tr><td>{$msg}</td></tr>
- {/foreach}
- </table>
+ <table>
+ {foreach from=$warningMessages item=msg}
+ <tr>
+ <td>{$msg}</td>
+ </tr>
+ {/foreach}
+ </table>
</div>
<script type="text/javascript">
-{literal}<!--
-$(function() {
- $("#integrity-results").dialog({
- modal: true,
- autoOpen: false,
- width: 600,
- buttons: {
- Ok: function() {
- $(this).dialog('close');
- }
- }
- });
-});
-$('#more-results').click(function() {
- $('#integrity-results').dialog('open');
-})
-.hover(
- function(){
- $(this).addClass("ui-state-hover");
- },
- function(){
- $(this).removeClass("ui-state-hover");
- }
-).mousedown(function(){
- $(this).addClass("ui-state-active");
-})
-.mouseup(function(){
- $(this).removeClass("ui-state-active");
-});
-//-->{/literal}
+ {literal}<!--
+ $(function () {
+ $("#integrity-results").dialog({
+ modal: true,
+ autoOpen: false,
+ width: 600,
+ buttons: {
+ Ok: function () {
+ $(this).dialog('close');
+ }
+ }
+ });
+ });
+ $('#more-results').click(function () {
+ $('#integrity-results').dialog('open');
+ })
+ .hover(
+ function () {
+ $(this).addClass("ui-state-hover");
+ },
+ function () {
+ $(this).removeClass("ui-state-hover");
+ }
+ ).mousedown(function () {
+ $(this).addClass("ui-state-active");
+ })
+ .mouseup(function () {
+ $(this).removeClass("ui-state-active");
+ });
+ //-->
+ {/literal}
</script>
diff --git a/plugins/Installation/templates/structure.tpl b/plugins/Installation/templates/structure.tpl
index 5e5fd11893..6760c60c12 100644
--- a/plugins/Installation/templates/structure.tpl
+++ b/plugins/Installation/templates/structure.tpl
@@ -1,75 +1,77 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
-<title>Piwik &rsaquo; {'Installation_Installation'|translate}</title>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Piwik &rsaquo; {'Installation_Installation'|translate}</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-<link rel="stylesheet" type="text/css" href="themes/default/common.css" />
-<link rel="stylesheet" type="text/css" href="libs/jquery/themes/base/jquery-ui.css" />
-<link rel="stylesheet" type="text/css" href="themes/default/styles.css" />
-<link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico" />
-<script type="text/javascript" src="libs/jquery/jquery.js"></script>
-<script type="text/javascript" src="libs/jquery/jquery-ui.js"></script>
+ <link rel="stylesheet" type="text/css" href="themes/default/common.css"/>
+ <link rel="stylesheet" type="text/css" href="libs/jquery/themes/base/jquery-ui.css"/>
+ <link rel="stylesheet" type="text/css" href="themes/default/styles.css"/>
+ <link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico"/>
+ <script type="text/javascript" src="libs/jquery/jquery.js"></script>
+ <script type="text/javascript" src="libs/jquery/jquery-ui.js"></script>
-{literal}
-<script type="text/javascript">
-$(document).ready( function(){
- $('#toFade').fadeOut(4000, function(){ $(this).show().css({visibility:'hidden'}); } );
- $('input:first').focus();
- $('#progressbar').progressbar({
-{/literal}
- value: {$percentDone}
-{literal}
- });
-});
-</script>
-{/literal}
+ {literal}
+ <script type="text/javascript">
+ $(document).ready(function () {
+ $('#toFade').fadeOut(4000, function () { $(this).show().css({visibility: 'hidden'}); });
+ $('input:first').focus();
+ $('#progressbar').progressbar({
+ {/literal}
+ value: {$percentDone}
+ {literal}
+ });
+ });
+ </script>
+ {/literal}
-<link rel="stylesheet" type="text/css" href="plugins/Installation/templates/install.css" />
-{if 'General_LayoutDirection'|translate =='rtl'}
-<link rel="stylesheet" type="text/css" href="themes/default/rtl.css" />
-{/if}
+ <link rel="stylesheet" type="text/css" href="plugins/Installation/templates/install.css"/>
+ {if 'General_LayoutDirection'|translate =='rtl'}
+ <link rel="stylesheet" type="text/css" href="themes/default/rtl.css"/>
+ {/if}
</head>
<body>
<div id="main">
- <div id="content">
- <div id="logo">
- <img id="title" width='160' src="themes/default/images/logo.png"/> &nbsp;&nbsp;&nbsp;<span id="subtitle"># {'General_OpenSourceWebAnalytics'|translate}</span>
- </div>
- <div style="float:right" id="topRightBar">
- <br />
- {postEvent name="template_topBar"}
- </div>
- <div class="both"></div>
+ <div id="content">
+ <div id="logo">
+ <img id="title" width='160' src="themes/default/images/logo.png"/> &nbsp;&nbsp;&nbsp;<span
+ id="subtitle"># {'General_OpenSourceWebAnalytics'|translate}</span>
+ </div>
+ <div style="float:right" id="topRightBar">
+ <br/>
+ {postEvent name="template_topBar"}
+ </div>
+ <div class="both"></div>
- <div id="generalInstall">
- {include file="Installation/templates/allSteps.tpl"}
- </div>
-
- <div id="detailInstall">
- {if isset($showNextStepAtTop) && $showNextStepAtTop}
- <p class="nextStep">
- <a class="submit" href="{url action=$nextModuleName}">{'General_Next'|translate} &raquo;</a>
- </p>
- {/if}
- {include file="$subTemplateToLoad"}
- {if $showNextStep}
- <p class="nextStep">
- <a class="submit" href="{url action=$nextModuleName}">{'General_Next'|translate} &raquo;</a>
- </p>
- {/if}
- </div>
-
- <div class="both"></div>
-
- <br />
- <br />
- <h3>{'Installation_InstallationStatus'|translate}</h3>
-
- <div id="progressbar"></div>
- {'Installation_PercentDone'|translate:$percentDone}
- </div>
+ <div id="generalInstall">
+ {include file="Installation/templates/allSteps.tpl"}
+ </div>
+
+ <div id="detailInstall">
+ {if isset($showNextStepAtTop) && $showNextStepAtTop}
+ <p class="nextStep">
+ <a class="submit" href="{url action=$nextModuleName}">{'General_Next'|translate} &raquo;</a>
+ </p>
+ {/if}
+ {include file="$subTemplateToLoad"}
+ {if $showNextStep}
+ <p class="nextStep">
+ <a class="submit" href="{url action=$nextModuleName}">{'General_Next'|translate} &raquo;</a>
+ </p>
+ {/if}
+ </div>
+
+ <div class="both"></div>
+
+ <br/>
+ <br/>
+
+ <h3>{'Installation_InstallationStatus'|translate}</h3>
+
+ <div id="progressbar"></div>
+ {'Installation_PercentDone'|translate:$percentDone}
+ </div>
</div>
</body>
</html>
diff --git a/plugins/Installation/templates/systemCheck.tpl b/plugins/Installation/templates/systemCheck.tpl
index 0f8cb0e309..50278ab34a 100644
--- a/plugins/Installation/templates/systemCheck.tpl
+++ b/plugins/Installation/templates/systemCheck.tpl
@@ -1,6 +1,6 @@
{if !$showNextStep}
-{include file="Installation/templates/systemCheck_legend.tpl"}
-<br style="clear:both">
+ {include file="Installation/templates/systemCheck_legend.tpl"}
+ <br style="clear:both">
{/if}
<h3>{'Installation_SystemCheck'|translate}</h3>
@@ -8,8 +8,10 @@
{include file="Installation/templates/systemCheckSection.tpl"}
{if !$showNextStep}
-<br/><p>
-<img src='themes/default/images/link.gif' /> &nbsp;<a href="?module=Proxy&action=redirect&url=http://piwik.org/docs/requirements/" target="_blank">{'Installation_Requirements'|translate}</a>
-</p>
-{include file="Installation/templates/systemCheck_legend.tpl"}
+ <br/>
+ <p>
+ <img src='themes/default/images/link.gif'/> &nbsp;<a href="?module=Proxy&action=redirect&url=http://piwik.org/docs/requirements/"
+ target="_blank">{'Installation_Requirements'|translate}</a>
+ </p>
+ {include file="Installation/templates/systemCheck_legend.tpl"}
{/if}
diff --git a/plugins/Installation/templates/systemCheckPage.css b/plugins/Installation/templates/systemCheckPage.css
index e9175cdabe..49444ce275 100755
--- a/plugins/Installation/templates/systemCheckPage.css
+++ b/plugins/Installation/templates/systemCheckPage.css
@@ -1,42 +1,42 @@
-#systemCheckOptional,#systemCheckRequired {
- border: 1px solid #dadada;
- width:100%;
- max-width: 900px;
+#systemCheckOptional, #systemCheckRequired {
+ border: 1px solid #dadada;
+ width: 100%;
+ max-width: 900px;
}
#systemCheckOptional {
- margin-bottom:2em;
+ margin-bottom: 2em;
}
-#systemCheckOptional td,#systemCheckRequired td {
- /*border: 1px solid #222;*/
- padding: 1em .5em 1em 2em;
- vertical-align:middle;
- font-size:1.2em;
- margin:0;
+#systemCheckOptional td, #systemCheckRequired td {
+ /*border: 1px solid #222;*/
+ padding: 1em .5em 1em 2em;
+ vertical-align: middle;
+ font-size: 1.2em;
+ margin: 0;
}
#systemCheckOptional tr:nth-child(even), #systemCheckRequired tr:nth-child(even) {
- background-color: #EFEEEC;
+ background-color: #EFEEEC;
}
#systemCheckOptional tr:nth-child(odd), #systemCheckRequired tr:nth-child(odd) {
- background-color: #F6F5F3;
+ background-color: #F6F5F3;
}
.error {
- color:red;
- font-size:100%;
- font-weight:bold;
- border: 2px solid red;
- width: 550px;
- padding:20px;
- margin-bottom:10px;
+ color: red;
+ font-size: 100%;
+ font-weight: bold;
+ border: 2px solid red;
+ width: 550px;
+ padding: 20px;
+ margin-bottom: 10px;
}
.error img {
- border:0;
- float:right;
- margin:10px;
+ border: 0;
+ float: right;
+ margin: 10px;
}
diff --git a/plugins/Installation/templates/systemCheckPage.tpl b/plugins/Installation/templates/systemCheckPage.tpl
index b70b4ef94c..133a4b0adb 100755
--- a/plugins/Installation/templates/systemCheckPage.tpl
+++ b/plugins/Installation/templates/systemCheckPage.tpl
@@ -1,18 +1,18 @@
{include file="CoreAdminHome/templates/header.tpl"}
{if $isSuperUser}
-
-<h2>{'Installation_SystemCheck'|translate}</h2>
-
-<p style="margin-left:1em">{if $infos.has_errors}
-<img src='themes/default/images/error.png' /> {'Installation_SystemCheckSummaryThereWereErrors'|translate:'<strong>':'</strong>':'<strong><em>':'</em></strong>'} {'Installation_SeeBelowForMoreInfo'|translate}
-{elseif $infos.has_warnings}
-<img src='themes/default/images/warning.png' /> {'Installation_SystemCheckSummaryThereWereWarnings'|translate} {'Installation_SeeBelowForMoreInfo'|translate}
-{else}
-<img src='themes/default/images/ok.png' /> {'Installation_SystemCheckSummaryNoProblems'|translate}
-{/if}</p>
-
-{include file="Installation/templates/systemCheckSection.tpl"}
+ <h2>{'Installation_SystemCheck'|translate}</h2>
+ <p style="margin-left:1em">{if $infos.has_errors}
+ <img src='themes/default/images/error.png'/>
+ {'Installation_SystemCheckSummaryThereWereErrors'|translate:'<strong>':'</strong>':'<strong><em>':'</em></strong>'} {'Installation_SeeBelowForMoreInfo'|translate}
+ {elseif $infos.has_warnings}
+ <img src='themes/default/images/warning.png'/>
+ {'Installation_SystemCheckSummaryThereWereWarnings'|translate} {'Installation_SeeBelowForMoreInfo'|translate}
+ {else}
+ <img src='themes/default/images/ok.png'/>
+ {'Installation_SystemCheckSummaryNoProblems'|translate}
+ {/if}</p>
+ {include file="Installation/templates/systemCheckSection.tpl"}
{/if}
diff --git a/plugins/Installation/templates/systemCheckSection.tpl b/plugins/Installation/templates/systemCheckSection.tpl
index 8788e396cd..e659684ba4 100755
--- a/plugins/Installation/templates/systemCheckSection.tpl
+++ b/plugins/Installation/templates/systemCheckSection.tpl
@@ -4,275 +4,303 @@
{assign var=link value="<img src='themes/default/images/link.gif' />"}
<table class="infosServer" id="systemCheckRequired">
- <tr>
- {capture assign="MinPHP"}{'Installation_SystemCheckPhp'|translate} &gt; {$infos.phpVersion_minimum}{/capture}
- <td class="label">{$MinPHP}</td>
+ <tr>
+ {capture assign="MinPHP"}{'Installation_SystemCheckPhp'|translate} &gt; {$infos.phpVersion_minimum}{/capture}
+ <td class="label">{$MinPHP}</td>
- <td>{if $infos.phpVersion_ok}{$ok}
- {else}{$error} <span class="err">{'General_Error'|translate}: {'General_Required'|translate:$MinPHP}</span>{/if}</td>
- </tr>
- <tr>
- <td class="label">PDO {'Installation_Extension'|translate}</td>
- <td>{if $infos.pdo_ok}{$ok}
- {else}-{/if}
- </td>
- </tr>
- {foreach from=$infos.adapters key=adapter item=port}
- <tr>
- <td class="label">{$adapter} {'Installation_Extension'|translate}</td>
- <td>{$ok}</td>
- </tr>
- {/foreach}
- {if !count($infos.adapters)}
- <tr>
- <td colspan="2" class="error">
- <small>
- {'Installation_SystemCheckDatabaseHelp'|translate}
- <p>
- {if $infos.isWindows}
- {'Installation_SystemCheckWinPdoAndMysqliHelp'|translate:"<br /><br /><code>extension=php_mysqli.dll</code><br /><code>extension=php_pdo.dll</code><br /><code>extension=php_pdo_mysql.dll</code><br />"|nl2br}
- {else}
- {'Installation_SystemCheckPdoAndMysqliHelp'|translate:"<br /><br /><code>--with-mysqli</code><br /><code>--with-pdo-mysql</code><br /><br />":"<br /><br /><code>extension=mysqli.so</code><br /><code>extension=pdo.so</code><br /><code>extension=pdo_mysql.so</code><br />"}
- {/if}
- {'Installation_RestartWebServer'|translate}
- <br />
- <br />
- {'Installation_SystemCheckPhpPdoAndMysqliSite'|translate}
- </p>
- </small>
- </td>
- </tr>
- {/if}
- </tr>
- <tr>
- <td class="label">{'Installation_SystemCheckExtensions'|translate}</td>
- <td>{foreach from=$infos.needed_extensions item=needed_extension}
- {if in_array($needed_extension, $infos.missing_extensions)}
- {$error}
- {capture assign="hasError"}1{/capture}
- {else}
- {$ok}
- {/if}
- {$needed_extension}
- <br />
- {/foreach}
- <br/>{if isset($hasError)}{'Installation_RestartWebServer'|translate}{/if}
- </td>
- </tr>
- {if count($infos.missing_extensions) gt 0}
- <tr>
- <td colspan="2" class="error">
- <small>
- {foreach from=$infos.missing_extensions item=missing_extension}
- <p>
- <i>{$helpMessages[$missing_extension]|translate}</i>
- </p>
- {/foreach}
- </small>
- </td>
- </tr>
- {/if}
- <tr>
- <td class="label">{'Installation_SystemCheckFunctions'|translate}</td>
-
- <td>{foreach from=$infos.needed_functions item=needed_function}
- {if in_array($needed_function, $infos.missing_functions)}
- {$error} <span class='err'>{$needed_function}</span>
- {capture assign="hasError"}1{/capture}
- <p>
- <i>{$helpMessages[$needed_function]|translate}</i>
- </p>
- {else}
- {$ok} {$needed_function}<br />
- {/if}
- {/foreach}
- <br/>{if isset($hasError)}{'Installation_RestartWebServer'|translate}{/if}
- </td>
- </tr>
- <tr>
- <td valign="top">
- {'Installation_SystemCheckWriteDirs'|translate}
- </td>
- <td>
- <small>
- {foreach from=$infos.directories key=dir item=bool}
- {if $bool}{$ok}{else}
- <span style="color:red">{$error}</span>{/if}
- {$dir}
- <br />
- {/foreach}
- </small>
- </td>
- </tr>
- {if $problemWithSomeDirectories}
- <tr>
- <td colspan="2" class="error">
- {'Installation_SystemCheckWriteDirsHelp'|translate}:
- {foreach from=$infos.directories key=dir item=bool}
- <ul>{if !$bool}
- <li><pre>chmod a+w {$dir}</pre></li>
- {/if}
- </ul>
- {/foreach}
- </td>
- </tr>
- {/if}
+ <td>{if $infos.phpVersion_ok}{$ok}
+ {else}{$error} <span class="err">{'General_Error'|translate}: {'General_Required'|translate:$MinPHP}</span>{/if}</td>
+ </tr>
+ <tr>
+ <td class="label">PDO {'Installation_Extension'|translate}</td>
+ <td>{if $infos.pdo_ok}{$ok}
+ {else}-{/if}
+ </td>
+ </tr>
+ {foreach from=$infos.adapters key=adapter item=port}
+ <tr>
+ <td class="label">{$adapter} {'Installation_Extension'|translate}</td>
+ <td>{$ok}</td>
+ </tr>
+ {/foreach}
+ {if !count($infos.adapters)}
+ <tr>
+ <td colspan="2" class="error">
+ <small>
+ {'Installation_SystemCheckDatabaseHelp'|translate}
+ <p>
+ {if $infos.isWindows}
+ {'Installation_SystemCheckWinPdoAndMysqliHelp'|translate:"<br /><br /><code>extension=php_mysqli.dll</code><br /><code>extension=php_pdo.dll</code><br /><code>extension=php_pdo_mysql.dll</code><br />"|nl2br}
+ {else}
+ {'Installation_SystemCheckPdoAndMysqliHelp'|translate:"<br /><br /><code>--with-mysqli</code><br /><code>--with-pdo-mysql</code><br /><br />":"<br /><br /><code>extension=mysqli.so</code><br /><code>extension=pdo.so</code><br /><code>extension=pdo_mysql.so</code><br />"}
+ {/if}
+ {'Installation_RestartWebServer'|translate}
+ <br/>
+ <br/>
+ {'Installation_SystemCheckPhpPdoAndMysqliSite'|translate}
+ </p>
+ </small>
+ </td>
+ </tr>
+ {/if}
+ </tr>
+ <tr>
+ <td class="label">{'Installation_SystemCheckExtensions'|translate}</td>
+ <td>{foreach from=$infos.needed_extensions item=needed_extension}
+ {if in_array($needed_extension, $infos.missing_extensions)}
+ {$error}
+ {capture assign="hasError"}1{/capture}
+ {else}
+ {$ok}
+ {/if}
+ {$needed_extension}
+ <br/>
+ {/foreach}
+ <br/>{if isset($hasError)}{'Installation_RestartWebServer'|translate}{/if}
+ </td>
+ </tr>
+ {if count($infos.missing_extensions) gt 0}
+ <tr>
+ <td colspan="2" class="error">
+ <small>
+ {foreach from=$infos.missing_extensions item=missing_extension}
+ <p>
+ <i>{$helpMessages[$missing_extension]|translate}</i>
+ </p>
+ {/foreach}
+ </small>
+ </td>
+ </tr>
+ {/if}
+ <tr>
+ <td class="label">{'Installation_SystemCheckFunctions'|translate}</td>
+
+ <td>{foreach from=$infos.needed_functions item=needed_function}
+ {if in_array($needed_function, $infos.missing_functions)}
+ {$error}
+ <span class='err'>{$needed_function}</span>
+ {capture assign="hasError"}1{/capture}
+ <p>
+ <i>{$helpMessages[$needed_function]|translate}</i>
+ </p>
+ {else}
+ {$ok} {$needed_function}
+ <br/>
+ {/if}
+ {/foreach}
+ <br/>{if isset($hasError)}{'Installation_RestartWebServer'|translate}{/if}
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ {'Installation_SystemCheckWriteDirs'|translate}
+ </td>
+ <td>
+ <small>
+ {foreach from=$infos.directories key=dir item=bool}
+ {if $bool}{$ok}{else}
+ <span style="color:red">{$error}</span>{/if}
+ {$dir}
+ <br/>
+ {/foreach}
+ </small>
+ </td>
+ </tr>
+ {if $problemWithSomeDirectories}
+ <tr>
+ <td colspan="2" class="error">
+ {'Installation_SystemCheckWriteDirsHelp'|translate}:
+ {foreach from=$infos.directories key=dir item=bool}
+ <ul>{if !$bool}
+ <li>
+ <pre>chmod a+w {$dir}</pre>
+ </li>
+ {/if}
+ </ul>
+ {/foreach}
+ </td>
+ </tr>
+ {/if}
</table>
-<br />
-
+<br/>
+
<h2>{'Optional'|translate}</h2>
<table class="infos" id="systemCheckOptional">
- <tr>
- <td class="label">{'Installation_SystemCheckFileIntegrity'|translate}</td>
- <td>
- {if empty($infos.integrityErrorMessages)}
- {$ok}
- {else}
- {if $infos.integrity}
- {$warning} <i>{$infos.integrityErrorMessages[0]}</i>
- {else}
- {$error} <i>{$infos.integrityErrorMessages[0]}</i>
- {/if}
- {if count($infos.integrityErrorMessages) > 1}
- <button id="more-results" class="ui-button ui-state-default ui-corner-all">{'General_Details'|translate}</button>
- {/if}
- {/if}
- </td>
- </tr>
- <tr>
- <td class="label">{'Installation_SystemCheckTracker'|translate}</td>
- <td>
- {if $infos.tracker_status == 0}
- {$ok}
- {else}
- {$warning} <span class="warn">{$infos.tracker_status}
- <br />{'Installation_SystemCheckTrackerHelp'|translate} </span>
- <br/>{'Installation_RestartWebServer'|translate}
- {/if}
- </td>
- </tr>
- <tr>
- <td class="label">{'Installation_SystemCheckMemoryLimit'|translate}</td>
- <td>
- {if $infos.memory_ok}
- {$ok} {$infos.memoryCurrent}
- {else}
- {$warning} <span class="warn">{$infos.memoryCurrent}</span>
- <br />{'Installation_SystemCheckMemoryLimitHelp'|translate}
- {'Installation_RestartWebServer'|translate}
- {/if}
- </td>
- </tr>
- <tr>
- <td class="label">{'SitesManager_Timezone'|translate}</td>
- <td>
- {if $infos.timezone}{$ok}
- {else}{$warning}
- <span class="warn">{'SitesManager_AdvancedTimezoneSupportNotFound'|translate} </span>
- <br/><a href="http://php.net/manual/en/datetime.installation.php" target="_blank">Timezone PHP documentation</a>.
- {/if}
- </td>
- </tr>
- <tr>
- <td class="label">{'Installation_SystemCheckOpenURL'|translate}</td>
- <td>
- {if $infos.openurl}{$ok} {$infos.openurl}
- {else}{$warning} <span class="warn">{'Installation_SystemCheckOpenURLHelp'|translate}</span>
- {/if}
- {if !$infos.can_auto_update}
- <br />{$warning} <span class="warn">{'Installation_SystemCheckAutoUpdateHelp'|translate}</span>{/if}
- </td>
- </tr>
- <tr>
- <td class="label">{'Installation_SystemCheckGD'|translate}</td>
- <td>
- {if $infos.gd_ok}{$ok}
- {else}{$warning} <span class="warn">{'Installation_SystemCheckGD'|translate}
- <br /> {'Installation_SystemCheckGDHelp'|translate} </span>{/if}
- </td>
- </tr>
- <tr>
- <td class="label">{'Installation_SystemCheckMbstring'|translate}</td>
- <td>
- {if $infos.hasMbstring}
- {if $infos.multibyte_ok}{$ok}
- {else}
- {$warning} <span class="warn">{'Installation_SystemCheckMbstring'|translate}
- <br/> {'Installation_SystemCheckMbstringFuncOverloadHelp'|translate}</span>
- {/if}
- {else}
- {$warning} <span class="warn">{'Installation_SystemCheckMbstringExtensionHelp'|translate}&nbsp;{'Installation_SystemCheckMbstringExtensionGeoIpHelp'|translate}</span>
- {/if}
- </td>
- </tr>
- <tr>
- <td class="label">{'Installation_SystemCheckOtherExtensions'|translate}</td>
- <td>{foreach from=$infos.desired_extensions item=desired_extension}
- {if in_array($desired_extension, $infos.missing_desired_extensions)}
- {$warning}<span class="warn">{$desired_extension}</span>
- <p>{$helpMessages[$desired_extension]|translate}</p>
- {else}
- {$ok} {$desired_extension}<br />
- {/if}
- {/foreach}
- </td>
- </tr>
- <tr>
- <td class="label">{'Installation_SystemCheckOtherFunctions'|translate}</td>
- <td>{foreach from=$infos.desired_functions item=desired_function}
- {if in_array($desired_function, $infos.missing_desired_functions)}
- {$warning} <span class="warn">{$desired_function}</span>
- <p>{$helpMessages[$desired_function]|translate}</p>
- {else}
- {$ok} {$desired_function}<br />
- {/if}
- {/foreach}
- </td>
- </tr>
- {if isset($infos.general_infos.assume_secure_protocol)}
- <tr>
- <td class="label">{'Installation_SystemCheckSecureProtocol'|translate}</td>
- <td>
- {$warning} <span class="warn">{$infos.protocol} </span><br/>
- {'Installation_SystemCheckSecureProtocolHelp'|translate}
- <br /><br />
- <code>[General]<br/>
-assume_secure_protocol = 1</code><br />
- </td>
- </tr>
- {/if}
- {if isset($infos.extra.load_data_infile_available)}
- <tr>
- <td class="label">{'Installation_DatabaseAbilities'|translate}</td>
- <td>
- {if $infos.extra.load_data_infile_available}
- {$ok} LOAD DATA INFILE<br/>
- {else}
- {$warning} <span class="warn">LOAD DATA INFILE</span><br/><br/>
- <p>{'Installation_LoadDataInfileUnavailableHelp'|translate:"LOAD DATA INFILE":"FILE"}</p>
- <p>{'Installation_LoadDataInfileRecommended'|translate}</p>
- {if isset($infos.extra.load_data_infile_error)}
- <em><strong>{'General_Error'|translate}:</strong></em> {$infos.extra.load_data_infile_error}
- {/if}
- {/if}
- </td>
- </tr>
- {/if}
- <tr>
- <td class="label">{'Installation_Filesystem'|translate}</td>
- <td>
- {if !$infos.is_nfs}
- {$ok} {'General_Ok'|translate}<br/>
- {else}
- {$warning} <span class="warn">{'Installation_NfsFilesystemWarning'|translate}</span>
- {if !empty($duringInstall)}
- <p>{'Installation_NfsFilesystemWarningSuffixInstall'|translate}</p>
- {else}
- <p>{'Installation_NfsFilesystemWarningSuffixAdmin'|translate}</p>
- {/if}
- {/if}
- </td>
- </tr>
+ <tr>
+ <td class="label">{'Installation_SystemCheckFileIntegrity'|translate}</td>
+ <td>
+ {if empty($infos.integrityErrorMessages)}
+ {$ok}
+ {else}
+ {if $infos.integrity}
+ {$warning}
+ <i>{$infos.integrityErrorMessages[0]}</i>
+ {else}
+ {$error}
+ <i>{$infos.integrityErrorMessages[0]}</i>
+ {/if}
+ {if count($infos.integrityErrorMessages) > 1}
+ <button id="more-results" class="ui-button ui-state-default ui-corner-all">{'General_Details'|translate}</button>
+ {/if}
+ {/if}
+ </td>
+ </tr>
+ <tr>
+ <td class="label">{'Installation_SystemCheckTracker'|translate}</td>
+ <td>
+ {if $infos.tracker_status == 0}
+ {$ok}
+ {else}
+ {$warning}
+ <span class="warn">{$infos.tracker_status}
+ <br/>{'Installation_SystemCheckTrackerHelp'|translate} </span>
+ <br/>
+ {'Installation_RestartWebServer'|translate}
+ {/if}
+ </td>
+ </tr>
+ <tr>
+ <td class="label">{'Installation_SystemCheckMemoryLimit'|translate}</td>
+ <td>
+ {if $infos.memory_ok}
+ {$ok} {$infos.memoryCurrent}
+ {else}
+ {$warning}
+ <span class="warn">{$infos.memoryCurrent}</span>
+ <br/>
+ {'Installation_SystemCheckMemoryLimitHelp'|translate}
+ {'Installation_RestartWebServer'|translate}
+ {/if}
+ </td>
+ </tr>
+ <tr>
+ <td class="label">{'SitesManager_Timezone'|translate}</td>
+ <td>
+ {if $infos.timezone}{$ok}
+ {else}{$warning}
+ <span class="warn">{'SitesManager_AdvancedTimezoneSupportNotFound'|translate} </span>
+ <br/>
+ <a href="http://php.net/manual/en/datetime.installation.php" target="_blank">Timezone PHP documentation</a>
+ .
+ {/if}
+ </td>
+ </tr>
+ <tr>
+ <td class="label">{'Installation_SystemCheckOpenURL'|translate}</td>
+ <td>
+ {if $infos.openurl}{$ok} {$infos.openurl}
+ {else}{$warning}
+ <span class="warn">{'Installation_SystemCheckOpenURLHelp'|translate}</span>
+ {/if}
+ {if !$infos.can_auto_update}
+ <br/>
+ {$warning} <span class="warn">{'Installation_SystemCheckAutoUpdateHelp'|translate}</span>{/if}
+ </td>
+ </tr>
+ <tr>
+ <td class="label">{'Installation_SystemCheckGD'|translate}</td>
+ <td>
+ {if $infos.gd_ok}{$ok}
+ {else}{$warning} <span class="warn">{'Installation_SystemCheckGD'|translate}
+ <br/>
+ {'Installation_SystemCheckGDHelp'|translate} </span>{/if}
+ </td>
+ </tr>
+ <tr>
+ <td class="label">{'Installation_SystemCheckMbstring'|translate}</td>
+ <td>
+ {if $infos.hasMbstring}
+ {if $infos.multibyte_ok}{$ok}
+ {else}
+ {$warning}
+ <span class="warn">{'Installation_SystemCheckMbstring'|translate}
+ <br/> {'Installation_SystemCheckMbstringFuncOverloadHelp'|translate}</span>
+ {/if}
+ {else}
+ {$warning}
+ <span class="warn">{'Installation_SystemCheckMbstringExtensionHelp'|translate}
+ &nbsp;{'Installation_SystemCheckMbstringExtensionGeoIpHelp'|translate}</span>
+ {/if}
+ </td>
+ </tr>
+ <tr>
+ <td class="label">{'Installation_SystemCheckOtherExtensions'|translate}</td>
+ <td>{foreach from=$infos.desired_extensions item=desired_extension}
+ {if in_array($desired_extension, $infos.missing_desired_extensions)}
+ {$warning}<span class="warn">{$desired_extension}</span>
+ <p>{$helpMessages[$desired_extension]|translate}</p>
+ {else}
+ {$ok} {$desired_extension}
+ <br/>
+ {/if}
+ {/foreach}
+ </td>
+ </tr>
+ <tr>
+ <td class="label">{'Installation_SystemCheckOtherFunctions'|translate}</td>
+ <td>{foreach from=$infos.desired_functions item=desired_function}
+ {if in_array($desired_function, $infos.missing_desired_functions)}
+ {$warning}
+ <span class="warn">{$desired_function}</span>
+ <p>{$helpMessages[$desired_function]|translate}</p>
+ {else}
+ {$ok} {$desired_function}
+ <br/>
+ {/if}
+ {/foreach}
+ </td>
+ </tr>
+ {if isset($infos.general_infos.assume_secure_protocol)}
+ <tr>
+ <td class="label">{'Installation_SystemCheckSecureProtocol'|translate}</td>
+ <td>
+ {$warning} <span class="warn">{$infos.protocol} </span><br/>
+ {'Installation_SystemCheckSecureProtocolHelp'|translate}
+ <br/><br/>
+ <code>[General]<br/>
+ assume_secure_protocol = 1</code><br/>
+ </td>
+ </tr>
+ {/if}
+ {if isset($infos.extra.load_data_infile_available)}
+ <tr>
+ <td class="label">{'Installation_DatabaseAbilities'|translate}</td>
+ <td>
+ {if $infos.extra.load_data_infile_available}
+ {$ok} LOAD DATA INFILE
+ <br/>
+ {else}
+ {$warning}
+ <span class="warn">LOAD DATA INFILE</span>
+ <br/>
+ <br/>
+ <p>{'Installation_LoadDataInfileUnavailableHelp'|translate:"LOAD DATA INFILE":"FILE"}</p>
+ <p>{'Installation_LoadDataInfileRecommended'|translate}</p>
+ {if isset($infos.extra.load_data_infile_error)}
+ <em><strong>{'General_Error'|translate}:</strong></em>
+ {$infos.extra.load_data_infile_error}
+ {/if}
+ {/if}
+ </td>
+ </tr>
+ {/if}
+ <tr>
+ <td class="label">{'Installation_Filesystem'|translate}</td>
+ <td>
+ {if !$infos.is_nfs}
+ {$ok} {'General_Ok'|translate}
+ <br/>
+ {else}
+ {$warning}
+ <span class="warn">{'Installation_NfsFilesystemWarning'|translate}</span>
+ {if !empty($duringInstall)}
+ <p>{'Installation_NfsFilesystemWarningSuffixInstall'|translate}</p>
+ {else}
+ <p>{'Installation_NfsFilesystemWarningSuffixAdmin'|translate}</p>
+ {/if}
+ {/if}
+ </td>
+ </tr>
</table>
{include file="Installation/templates/integrityDetails.tpl"}
diff --git a/plugins/Installation/templates/systemCheck_legend.tpl b/plugins/Installation/templates/systemCheck_legend.tpl
index d07cf00fba..0e57aa11e1 100644
--- a/plugins/Installation/templates/systemCheck_legend.tpl
+++ b/plugins/Installation/templates/systemCheck_legend.tpl
@@ -1,11 +1,15 @@
-<div id="systemCheckLegend"><small>
-<h2>{'Installation_Legend'|translate}</h2>
-<br />
-<img src='themes/default/images/warning.png' /> <span class="warn">{'General_Warning'|translate}: {'Installation_SystemCheckWarning'|translate}</span> <br />
-<img src='themes/default/images/error.png' /> <span style="color:red;font-weight:bold">{'General_Error'|translate}: {'Installation_SystemCheckError'|translate} </span><br />
-<img src='themes/default/images/ok.png' /> <span style="color:#26981C;font-weight:bold">{'General_Ok'|translate}</span><br />
-</small></div>
+<div id="systemCheckLegend">
+ <small>
+ <h2>{'Installation_Legend'|translate}</h2>
+ <br/>
+ <img src='themes/default/images/warning.png'/> <span class="warn">{'General_Warning'|translate}: {'Installation_SystemCheckWarning'|translate}</span>
+ <br/>
+ <img src='themes/default/images/error.png'/> <span style="color:red;font-weight:bold">{'General_Error'|translate}
+ : {'Installation_SystemCheckError'|translate} </span><br/>
+ <img src='themes/default/images/ok.png'/> <span style="color:#26981C;font-weight:bold">{'General_Ok'|translate}</span><br/>
+ </small>
+</div>
<p class="nextStep">
- <a href="{url}">{'General_Refresh'|translate} &raquo;</a>
+ <a href="{url}">{'General_Refresh'|translate} &raquo;</a>
</p>
diff --git a/plugins/Installation/templates/tablesCreation.tpl b/plugins/Installation/templates/tablesCreation.tpl
index 8614cb3153..fc03008d2c 100644
--- a/plugins/Installation/templates/tablesCreation.tpl
+++ b/plugins/Installation/templates/tablesCreation.tpl
@@ -1,63 +1,63 @@
<h2>{'Installation_Tables'|translate}</h2>
{if isset($someTablesInstalled)}
- <div class="warning">{'Installation_TablesWithSameNamesFound'|translate:"<span id='linkToggle'>":"</span>"}
- <img src="themes/default/images/warning_medium.png" />
- </div>
- <div id="toggle" style="display:none;color:#4F2410"><small><i>{'Installation_TablesFound'|translate}:
- <br />{$tablesInstalled} </i></small></div>
-
- {if isset($showReuseExistingTables)}
- <p>{'Installation_TablesWarningHelp'|translate}</p>
- <p class="nextStep"><a href="{url action=$nextModuleName}">{'Installation_TablesReuse'|translate} &raquo;</a></p>
- {else}
- <p class="nextStep"><a href="{url action=$previousPreviousModuleName}">&laquo; {'Installation_GoBackAndDefinePrefix'|translate}</a></p>
- {/if}
-
- <p class="nextStep"><a href="{url deleteTables=1}" id="eraseAllTables">{'Installation_TablesDelete'|translate} &raquo;</a></p>
+ <div class="warning">{'Installation_TablesWithSameNamesFound'|translate:"<span id='linkToggle'>":"</span>"}
+ <img src="themes/default/images/warning_medium.png"/>
+ </div>
+ <div id="toggle" style="display:none;color:#4F2410">
+ <small><i>{'Installation_TablesFound'|translate}:
+ <br/>{$tablesInstalled} </i></small>
+ </div>
+ {if isset($showReuseExistingTables)}
+ <p>{'Installation_TablesWarningHelp'|translate}</p>
+ <p class="nextStep"><a href="{url action=$nextModuleName}">{'Installation_TablesReuse'|translate} &raquo;</a></p>
+ {else}
+ <p class="nextStep"><a href="{url action=$previousPreviousModuleName}">&laquo; {'Installation_GoBackAndDefinePrefix'|translate}</a></p>
+ {/if}
+ <p class="nextStep"><a href="{url deleteTables=1}" id="eraseAllTables">{'Installation_TablesDelete'|translate} &raquo;</a></p>
{/if}
{if isset($existingTablesDeleted)}
- <div class="success"> {'Installation_TablesDeletedSuccess'|translate}
- <img src="themes/default/images/success_medium.png" /></div>
+ <div class="success"> {'Installation_TablesDeletedSuccess'|translate}
+ <img src="themes/default/images/success_medium.png"/></div>
{/if}
{if isset($tablesCreated)}
- <div class="success"> {'Installation_TablesCreatedSuccess'|translate}
- <img src="themes/default/images/success_medium.png" /></div>
+ <div class="success"> {'Installation_TablesCreatedSuccess'|translate}
+ <img src="themes/default/images/success_medium.png"/></div>
{/if}
{literal}
<script>
-$(document).ready( function(){
- {/literal}
- var strConfirmEraseTables = "{'Installation_ConfirmDeleteExistingTables'|translate:"[$tablesInstalled]":"<br />"} ";
- {literal}
-
- // toggle the display of the tables detected during the installation when clicking
- // on the span "linkToggle"
- $("#linkToggle")
- .css("border-bottom","thin dotted #ff5502")
-
- .hover( function() {
- $(this).css({ cursor: "pointer"});
- },
- function() {
- $(this).css({ cursor: "auto"});
- })
- .css("border-bottom","thin dotted #ff5502")
- .click(function(){
- $("#toggle").toggle();} );
-
- $("#eraseAllTables")
- .click( function(){
- if(!confirm( strConfirmEraseTables ) )
- {
- return false;
- }
- });
-
- ;
-});
+ $(document).ready(function () {
+ {/literal}
+ var strConfirmEraseTables = "{'Installation_ConfirmDeleteExistingTables'|translate:"[$tablesInstalled]":"<br />"} ";
+ {literal}
+
+ // toggle the display of the tables detected during the installation when clicking
+ // on the span "linkToggle"
+ $("#linkToggle")
+ .css("border-bottom", "thin dotted #ff5502")
+
+ .hover(function () {
+ $(this).css({ cursor: "pointer"});
+ },
+ function () {
+ $(this).css({ cursor: "auto"});
+ })
+ .css("border-bottom", "thin dotted #ff5502")
+ .click(function () {
+ $("#toggle").toggle();
+ });
+
+ $("#eraseAllTables")
+ .click(function () {
+ if (!confirm(strConfirmEraseTables)) {
+ return false;
+ }
+ });
+
+ ;
+ });
</script>
{/literal}
diff --git a/plugins/Installation/templates/welcome.tpl b/plugins/Installation/templates/welcome.tpl
index 526f04452f..b1fecd42dc 100644
--- a/plugins/Installation/templates/welcome.tpl
+++ b/plugins/Installation/templates/welcome.tpl
@@ -1,43 +1,43 @@
<h2>{'Installation_Welcome'|translate}</h2>
{if $newInstall}
-{'Installation_WelcomeHelp'|translate:$totalNumberOfSteps}
+ {'Installation_WelcomeHelp'|translate:$totalNumberOfSteps}
{else}
-<p>{'Installation_ConfigurationHelp'|translate}</p>
-<br />
-<div class="error">
-{$errorMessage}
-</div>
+ <p>{'Installation_ConfigurationHelp'|translate}</p>
+ <br/>
+ <div class="error">
+ {$errorMessage}
+ </div>
{/if}
{literal}
-<script type="text/javascript">
-<!--
-$(function() {
- // client-side test for https to handle the case where the server is behind a reverse proxy
- if (document.location.protocol === 'https:') {
- $('p.nextStep a').attr('href', $('p.nextStep a').attr('href') + '&clientProtocol=https');
- }
+ <script type="text/javascript">
+ <!--
+ $(function () {
+ // client-side test for https to handle the case where the server is behind a reverse proxy
+ if (document.location.protocol === 'https:') {
+ $('p.nextStep a').attr('href', $('p.nextStep a').attr('href') + '&clientProtocol=https');
+ }
- // client-side test for broken tracker (e.g., mod_security rule)
- $('p.nextStep').hide();
- $.ajax({
- url: 'piwik.php',
- data: 'url=http://example.com',
- complete: function() {
- $('p.nextStep').show();
- },
- error: function(req) {
- $('p.nextStep a').attr('href', $('p.nextStep a').attr('href') + '&trackerStatus=' + req.status);
- }
- });
-});
-//-->
-</script>
+ // client-side test for broken tracker (e.g., mod_security rule)
+ $('p.nextStep').hide();
+ $.ajax({
+ url: 'piwik.php',
+ data: 'url=http://example.com',
+ complete: function () {
+ $('p.nextStep').show();
+ },
+ error: function (req) {
+ $('p.nextStep a').attr('href', $('p.nextStep a').attr('href') + '&trackerStatus=' + req.status);
+ }
+ });
+ });
+ //-->
+ </script>
{/literal}
{if !$showNextStep}
-<p class="nextStep">
- <a href="{url}">{'General_Refresh'|translate} &raquo;</a>
-</p>
+ <p class="nextStep">
+ <a href="{url}">{'General_Refresh'|translate} &raquo;</a>
+ </p>
{/if}
diff --git a/plugins/LanguagesManager/API.php b/plugins/LanguagesManager/API.php
index f8e7a4effb..484065f4e1 100644
--- a/plugins/LanguagesManager/API.php
+++ b/plugins/LanguagesManager/API.php
@@ -24,173 +24,163 @@
*/
class Piwik_LanguagesManager_API
{
- static private $instance = null;
-
- /**
- * @return Piwik_LanguagesManager_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
+ static private $instance = null;
- protected $availableLanguageNames = null;
- protected $languageNames = null;
-
- /**
- * Returns true if specified language is available
- *
- * @param string $languageCode
- * @return bool true if language available; false otherwise
- */
- public function isLanguageAvailable($languageCode)
- {
- return $languageCode !== false
- && Piwik_Common::isValidFilename($languageCode)
- && in_array($languageCode, $this->getAvailableLanguages());
- }
-
- /**
- * Return array of available languages
- *
- * @return array Arry of strings, each containing its ISO language code
- */
- public function getAvailableLanguages()
- {
- if(!is_null($this->languageNames))
- {
- return $this->languageNames;
- }
- $path = PIWIK_INCLUDE_PATH . "/lang/";
- $languages = _glob($path . "*.php");
- $pathLength = strlen($path);
- $languageNames = array();
- if($languages)
- {
- foreach($languages as $language)
- {
- $languageNames[] = substr($language, $pathLength, -strlen('.php'));
- }
- }
- $this->languageNames = $languageNames;
- return $languageNames;
- }
+ /**
+ * @return Piwik_LanguagesManager_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
- /**
- * Return information on translations (code, language, % translated, etc)
- *
- * @return array Array of arrays
- */
- public function getAvailableLanguagesInfo()
- {
- require PIWIK_INCLUDE_PATH . '/lang/en.php';
- $englishTranslation = $translations;
- $filenames = $this->getAvailableLanguages();
- $languagesInfo = array();
- foreach($filenames as $filename)
- {
- require PIWIK_INCLUDE_PATH . "/lang/$filename.php";
- $translationStringsDone = array_intersect_key($englishTranslation, array_filter($translations, 'strlen'));
- $percentageComplete = count($translationStringsDone) / count($englishTranslation);
- $percentageComplete = round(100 * $percentageComplete, 0);
- $languageInfo = array( 'code' => $filename,
- 'name' => $translations['General_OriginalLanguageName'],
- 'english_name' => $translations['General_EnglishLanguageName'],
- 'translators' => $translations['General_TranslatorName'],
- 'translators_email' => $translations['General_TranslatorEmail'],
- 'percentage_complete' => $percentageComplete . '%',
- );
- $languagesInfo[] = $languageInfo;
- }
- return $languagesInfo;
- }
-
- /**
- * Return array of available languages
- *
- * @return array Arry of array, each containing its ISO language code and name of the language
- */
- public function getAvailableLanguageNames()
- {
- if(!is_null($this->availableLanguageNames))
- {
- return $this->availableLanguageNames;
- }
-
- $filenames = $this->getAvailableLanguages();
- $languagesInfo = array();
- foreach($filenames as $filename)
- {
- require PIWIK_INCLUDE_PATH . "/lang/$filename.php";
- $languagesInfo[] = array(
- 'code' => $filename,
- 'name' => $translations['General_OriginalLanguageName'],
- 'english_name' => $translations['General_EnglishLanguageName']
- );
- }
- $this->availableLanguageNames = $languagesInfo;
- return $this->availableLanguageNames;
- }
-
- /**
- * Returns translation strings by language
- *
- * @param string $languageCode ISO language code
- * @return array|false Array of arrays, each containing 'label' (translation index) and 'value' (translated string); false if language unavailable
- */
- public function getTranslationsForLanguage($languageCode)
- {
- if(!$this->isLanguageAvailable($languageCode))
- {
- return false;
- }
- require PIWIK_INCLUDE_PATH . "/lang/$languageCode.php";
- $languageInfo = array();
- foreach($translations as $key => $value)
- {
- $languageInfo[] = array('label' => $key, 'value' => $value);
- }
- return $languageInfo;
- }
+ protected $availableLanguageNames = null;
+ protected $languageNames = null;
- /**
- * Returns the language for the user
- *
- * @param string $login
- * @return string
- */
- public function getLanguageForUser( $login )
- {
- Piwik::checkUserIsSuperUserOrTheUser($login);
- Piwik::checkUserIsNotAnonymous();
- return Piwik_FetchOne('SELECT language FROM '.Piwik_Common::prefixTable('user_language') .
- ' WHERE login = ? ', array($login ));
- }
-
- /**
- * Sets the language for the user
- *
- * @param string $login
- * @param string $languageCode
- * @return bool
- */
- public function setLanguageForUser($login, $languageCode)
- {
- Piwik::checkUserIsSuperUserOrTheUser($login);
- Piwik::checkUserIsNotAnonymous();
- if(!$this->isLanguageAvailable($languageCode))
- {
- return false;
- }
- $paramsBind = array($login, $languageCode, $languageCode);
- Piwik_Query('INSERT INTO '.Piwik_Common::prefixTable('user_language') .
- ' (login, language)
- VALUES (?,?)
- ON DUPLICATE KEY UPDATE language=?',
- $paramsBind);
- }
+ /**
+ * Returns true if specified language is available
+ *
+ * @param string $languageCode
+ * @return bool true if language available; false otherwise
+ */
+ public function isLanguageAvailable($languageCode)
+ {
+ return $languageCode !== false
+ && Piwik_Common::isValidFilename($languageCode)
+ && in_array($languageCode, $this->getAvailableLanguages());
+ }
+
+ /**
+ * Return array of available languages
+ *
+ * @return array Arry of strings, each containing its ISO language code
+ */
+ public function getAvailableLanguages()
+ {
+ if (!is_null($this->languageNames)) {
+ return $this->languageNames;
+ }
+ $path = PIWIK_INCLUDE_PATH . "/lang/";
+ $languages = _glob($path . "*.php");
+ $pathLength = strlen($path);
+ $languageNames = array();
+ if ($languages) {
+ foreach ($languages as $language) {
+ $languageNames[] = substr($language, $pathLength, -strlen('.php'));
+ }
+ }
+ $this->languageNames = $languageNames;
+ return $languageNames;
+ }
+
+ /**
+ * Return information on translations (code, language, % translated, etc)
+ *
+ * @return array Array of arrays
+ */
+ public function getAvailableLanguagesInfo()
+ {
+ require PIWIK_INCLUDE_PATH . '/lang/en.php';
+ $englishTranslation = $translations;
+ $filenames = $this->getAvailableLanguages();
+ $languagesInfo = array();
+ foreach ($filenames as $filename) {
+ require PIWIK_INCLUDE_PATH . "/lang/$filename.php";
+ $translationStringsDone = array_intersect_key($englishTranslation, array_filter($translations, 'strlen'));
+ $percentageComplete = count($translationStringsDone) / count($englishTranslation);
+ $percentageComplete = round(100 * $percentageComplete, 0);
+ $languageInfo = array('code' => $filename,
+ 'name' => $translations['General_OriginalLanguageName'],
+ 'english_name' => $translations['General_EnglishLanguageName'],
+ 'translators' => $translations['General_TranslatorName'],
+ 'translators_email' => $translations['General_TranslatorEmail'],
+ 'percentage_complete' => $percentageComplete . '%',
+ );
+ $languagesInfo[] = $languageInfo;
+ }
+ return $languagesInfo;
+ }
+
+ /**
+ * Return array of available languages
+ *
+ * @return array Arry of array, each containing its ISO language code and name of the language
+ */
+ public function getAvailableLanguageNames()
+ {
+ if (!is_null($this->availableLanguageNames)) {
+ return $this->availableLanguageNames;
+ }
+
+ $filenames = $this->getAvailableLanguages();
+ $languagesInfo = array();
+ foreach ($filenames as $filename) {
+ require PIWIK_INCLUDE_PATH . "/lang/$filename.php";
+ $languagesInfo[] = array(
+ 'code' => $filename,
+ 'name' => $translations['General_OriginalLanguageName'],
+ 'english_name' => $translations['General_EnglishLanguageName']
+ );
+ }
+ $this->availableLanguageNames = $languagesInfo;
+ return $this->availableLanguageNames;
+ }
+
+ /**
+ * Returns translation strings by language
+ *
+ * @param string $languageCode ISO language code
+ * @return array|false Array of arrays, each containing 'label' (translation index) and 'value' (translated string); false if language unavailable
+ */
+ public function getTranslationsForLanguage($languageCode)
+ {
+ if (!$this->isLanguageAvailable($languageCode)) {
+ return false;
+ }
+ require PIWIK_INCLUDE_PATH . "/lang/$languageCode.php";
+ $languageInfo = array();
+ foreach ($translations as $key => $value) {
+ $languageInfo[] = array('label' => $key, 'value' => $value);
+ }
+ return $languageInfo;
+ }
+
+ /**
+ * Returns the language for the user
+ *
+ * @param string $login
+ * @return string
+ */
+ public function getLanguageForUser($login)
+ {
+ Piwik::checkUserIsSuperUserOrTheUser($login);
+ Piwik::checkUserIsNotAnonymous();
+ return Piwik_FetchOne('SELECT language FROM ' . Piwik_Common::prefixTable('user_language') .
+ ' WHERE login = ? ', array($login));
+ }
+
+ /**
+ * Sets the language for the user
+ *
+ * @param string $login
+ * @param string $languageCode
+ * @return bool
+ */
+ public function setLanguageForUser($login, $languageCode)
+ {
+ Piwik::checkUserIsSuperUserOrTheUser($login);
+ Piwik::checkUserIsNotAnonymous();
+ if (!$this->isLanguageAvailable($languageCode)) {
+ return false;
+ }
+ $paramsBind = array($login, $languageCode, $languageCode);
+ Piwik_Query('INSERT INTO ' . Piwik_Common::prefixTable('user_language') .
+ ' (login, language)
+ VALUES (?,?)
+ ON DUPLICATE KEY UPDATE language=?',
+ $paramsBind);
+ }
}
diff --git a/plugins/LanguagesManager/Controller.php b/plugins/LanguagesManager/Controller.php
index 4553143783..3162d209ef 100644
--- a/plugins/LanguagesManager/Controller.php
+++ b/plugins/LanguagesManager/Controller.php
@@ -1,13 +1,13 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_LanguagesManager
- *
+ *
*/
/**
@@ -15,26 +15,25 @@
*/
class Piwik_LanguagesManager_Controller extends Piwik_Controller
{
- /**
- * anonymous = in the session
- * authenticated user = in the session and in DB
- */
- public function saveLanguage()
- {
- $language = Piwik_Common::getRequestVar('language');
+ /**
+ * anonymous = in the session
+ * authenticated user = in the session and in DB
+ */
+ public function saveLanguage()
+ {
+ $language = Piwik_Common::getRequestVar('language');
- // Prevent CSRF only when piwik is not installed yet (During install user can change language)
- if(Piwik::isInstalled()) {
- $this->checkTokenInUrl();
- }
- Piwik_LanguagesManager::setLanguageForSession($language);
- if(Zend_Registry::isRegistered('access')) {
- $currentUser = Piwik::getCurrentUserLogin();
- if($currentUser && $currentUser !== 'anonymous')
- {
- Piwik_LanguagesManager_API::getInstance()->setLanguageForUser($currentUser, $language);
- }
- }
- Piwik_Url::redirectToReferer();
- }
+ // Prevent CSRF only when piwik is not installed yet (During install user can change language)
+ if (Piwik::isInstalled()) {
+ $this->checkTokenInUrl();
+ }
+ Piwik_LanguagesManager::setLanguageForSession($language);
+ if (Zend_Registry::isRegistered('access')) {
+ $currentUser = Piwik::getCurrentUserLogin();
+ if ($currentUser && $currentUser !== 'anonymous') {
+ Piwik_LanguagesManager_API::getInstance()->setLanguageForUser($currentUser, $language);
+ }
+ }
+ Piwik_Url::redirectToReferer();
+ }
}
diff --git a/plugins/LanguagesManager/LanguagesManager.php b/plugins/LanguagesManager/LanguagesManager.php
index d6162ecf01..29c900a8eb 100644
--- a/plugins/LanguagesManager/LanguagesManager.php
+++ b/plugins/LanguagesManager/LanguagesManager.php
@@ -1,13 +1,13 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_LanguagesManager
- *
+ *
*/
/**
@@ -16,229 +16,219 @@
*/
class Piwik_LanguagesManager extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('LanguagesManager_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- public function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'TopMenu.add' => 'showLanguagesSelector',
- 'Translate.getLanguageToLoad' => 'getLanguageToLoad',
- 'UsersManager.deleteUser' => 'deleteUserLanguage',
- 'template_topBar' => 'addLanguagesManagerToOtherTopBar',
- );
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "themes/default/styles.css";
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
-
- $jsFiles[] = "plugins/LanguagesManager/templates/languageSelector.js";
- }
-
- /**
- * Show styled language selection drop-down list
- *
- * @param string $url The form action. Default is to save language.
- */
- function showLanguagesSelector()
- {
- Piwik_AddTopMenu('LanguageSelector', $this->getLanguagesSelector(), true, $order = 30, true);
- }
-
- /**
- * Adds the languages drop-down list to topbars other than the main one rendered
- * in CoreHome/templates/top_bar.tpl. The 'other' topbars are on the Installation
- * and CoreUpdater screens.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function addLanguagesManagerToOtherTopBar( $notification )
- {
- $str =& $notification->getNotificationObject();
- // piwik object & scripts aren't loaded in 'other' topbars
- $str .= "<script type='text/javascript'>if (!window.piwik) window.piwik={};</script>";
- $str .= "<script type='text/javascript' src='plugins/LanguagesManager/templates/languageSelector.js'></script>";
- $str .= $this->getLanguagesSelector();
- }
-
- /**
- * Renders and returns the language selector HTML.
- *
- * @return string
- */
- private function getLanguagesSelector()
- {
- // don't use Piwik_View::factory() here
- $view = new Piwik_View("LanguagesManager/templates/languages.tpl");
- $view->languages = Piwik_LanguagesManager_API::getInstance()->getAvailableLanguageNames();
- $view->currentLanguageCode = self::getLanguageCodeForCurrentUser();
- $view->currentLanguageName = self::getLanguageNameForCurrentUser();
- return $view->render();
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getLanguageToLoad($notification)
- {
- $language =& $notification->getNotificationObject();
- if (empty($language))
- {
- $language = self::getLanguageCodeForCurrentUser();
- }
- if(!Piwik_LanguagesManager_API::getInstance()->isLanguageAvailable($language))
- {
- $language = Piwik_Translate::getInstance()->getLanguageDefault();
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function deleteUserLanguage($notification)
- {
- $userLogin = $notification->getNotificationObject();
- Piwik_Query('DELETE FROM ' . Piwik_Common::prefixTable('user_language') . ' WHERE login = ?', $userLogin);
- }
-
- /**
- * @throws Exception if non-recoverable error
- */
- public function install()
- {
- // we catch the exception
- try{
- $sql = "CREATE TABLE ". Piwik_Common::prefixTable('user_language')." (
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('LanguagesManager_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'TopMenu.add' => 'showLanguagesSelector',
+ 'Translate.getLanguageToLoad' => 'getLanguageToLoad',
+ 'UsersManager.deleteUser' => 'deleteUserLanguage',
+ 'template_topBar' => 'addLanguagesManagerToOtherTopBar',
+ );
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+
+ $cssFiles[] = "themes/default/styles.css";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+
+ $jsFiles[] = "plugins/LanguagesManager/templates/languageSelector.js";
+ }
+
+ /**
+ * Show styled language selection drop-down list
+ *
+ * @param string $url The form action. Default is to save language.
+ */
+ function showLanguagesSelector()
+ {
+ Piwik_AddTopMenu('LanguageSelector', $this->getLanguagesSelector(), true, $order = 30, true);
+ }
+
+ /**
+ * Adds the languages drop-down list to topbars other than the main one rendered
+ * in CoreHome/templates/top_bar.tpl. The 'other' topbars are on the Installation
+ * and CoreUpdater screens.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function addLanguagesManagerToOtherTopBar($notification)
+ {
+ $str =& $notification->getNotificationObject();
+ // piwik object & scripts aren't loaded in 'other' topbars
+ $str .= "<script type='text/javascript'>if (!window.piwik) window.piwik={};</script>";
+ $str .= "<script type='text/javascript' src='plugins/LanguagesManager/templates/languageSelector.js'></script>";
+ $str .= $this->getLanguagesSelector();
+ }
+
+ /**
+ * Renders and returns the language selector HTML.
+ *
+ * @return string
+ */
+ private function getLanguagesSelector()
+ {
+ // don't use Piwik_View::factory() here
+ $view = new Piwik_View("LanguagesManager/templates/languages.tpl");
+ $view->languages = Piwik_LanguagesManager_API::getInstance()->getAvailableLanguageNames();
+ $view->currentLanguageCode = self::getLanguageCodeForCurrentUser();
+ $view->currentLanguageName = self::getLanguageNameForCurrentUser();
+ return $view->render();
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getLanguageToLoad($notification)
+ {
+ $language =& $notification->getNotificationObject();
+ if (empty($language)) {
+ $language = self::getLanguageCodeForCurrentUser();
+ }
+ if (!Piwik_LanguagesManager_API::getInstance()->isLanguageAvailable($language)) {
+ $language = Piwik_Translate::getInstance()->getLanguageDefault();
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function deleteUserLanguage($notification)
+ {
+ $userLogin = $notification->getNotificationObject();
+ Piwik_Query('DELETE FROM ' . Piwik_Common::prefixTable('user_language') . ' WHERE login = ?', $userLogin);
+ }
+
+ /**
+ * @throws Exception if non-recoverable error
+ */
+ public function install()
+ {
+ // we catch the exception
+ try {
+ $sql = "CREATE TABLE " . Piwik_Common::prefixTable('user_language') . " (
login VARCHAR( 100 ) NOT NULL ,
language VARCHAR( 10 ) NOT NULL ,
PRIMARY KEY ( login )
- ) DEFAULT CHARSET=utf8 " ;
- Piwik_Exec($sql);
- } catch(Exception $e){
- // mysql code error 1050:table already exists
- // see bug #153 http://dev.piwik.org/trac/ticket/153
- if(!Zend_Registry::get('db')->isErrNo($e, '1050'))
- {
- throw $e;
- }
- }
- }
-
- /**
- * @throws Exception if non-recoverable error
- */
- public function uninstall()
- {
- Piwik_DropTables(Piwik_Common::prefixTable('user_language'));
- }
-
- /**
- * @return string Two letters language code, eg. "fr"
- */
- static public function getLanguageCodeForCurrentUser()
- {
- $languageCode = self::getLanguageFromPreferences();
- if(!Piwik_LanguagesManager_API::getInstance()->isLanguageAvailable($languageCode))
- {
- $languageCode = Piwik_Common::extractLanguageCodeFromBrowserLanguage(Piwik_Common::getBrowserLanguage(), Piwik_LanguagesManager_API::getInstance()->getAvailableLanguages());
- }
- if(!Piwik_LanguagesManager_API::getInstance()->isLanguageAvailable($languageCode))
- {
- $languageCode = Piwik_Translate::getInstance()->getLanguageDefault();
- }
- return $languageCode;
- }
-
- /**
- * @return string Full english language string, eg. "French"
- */
- static public function getLanguageNameForCurrentUser()
- {
- $languageCode = self::getLanguageCodeForCurrentUser();
- $languages = Piwik_LanguagesManager_API::getInstance()->getAvailableLanguageNames();
- foreach($languages as $language)
- {
- if($language['code'] === $languageCode)
- {
- return $language['name'];
- }
- }
- }
-
- /**
- * @return string|false if language preference could not be loaded
- */
- static protected function getLanguageFromPreferences()
- {
- if(($language = self::getLanguageForSession()) != null)
- {
- return $language;
- }
-
- try {
- $currentUser = Piwik::getCurrentUserLogin();
- return Piwik_LanguagesManager_API::getInstance()->getLanguageForUser($currentUser);
- } catch(Exception $e) {
- return false;
- }
- }
-
-
- /**
- * Returns the langage for the session
- *
- * @return string|null
- */
- static public function getLanguageForSession()
- {
- $cookieName = Piwik_Config::getInstance()->General['language_cookie_name'];
- $cookie = new Piwik_Cookie($cookieName);
- if($cookie->isCookieFound())
- {
- return $cookie->get('language');
- }
- return null;
- }
-
- /**
- * Set the language for the session
- *
- * @param string $languageCode ISO language code
- * @return bool
- */
- static public function setLanguageForSession($languageCode)
- {
- if(!Piwik_LanguagesManager_API::getInstance()->isLanguageAvailable($languageCode))
- {
- return false;
- }
-
- $cookieName = Piwik_Config::getInstance()->General['language_cookie_name'];
- $cookie = new Piwik_Cookie($cookieName, 0);
- $cookie->set('language', $languageCode);
- $cookie->save();
- }
+ ) DEFAULT CHARSET=utf8 ";
+ Piwik_Exec($sql);
+ } catch (Exception $e) {
+ // mysql code error 1050:table already exists
+ // see bug #153 http://dev.piwik.org/trac/ticket/153
+ if (!Zend_Registry::get('db')->isErrNo($e, '1050')) {
+ throw $e;
+ }
+ }
+ }
+
+ /**
+ * @throws Exception if non-recoverable error
+ */
+ public function uninstall()
+ {
+ Piwik_DropTables(Piwik_Common::prefixTable('user_language'));
+ }
+
+ /**
+ * @return string Two letters language code, eg. "fr"
+ */
+ static public function getLanguageCodeForCurrentUser()
+ {
+ $languageCode = self::getLanguageFromPreferences();
+ if (!Piwik_LanguagesManager_API::getInstance()->isLanguageAvailable($languageCode)) {
+ $languageCode = Piwik_Common::extractLanguageCodeFromBrowserLanguage(Piwik_Common::getBrowserLanguage(), Piwik_LanguagesManager_API::getInstance()->getAvailableLanguages());
+ }
+ if (!Piwik_LanguagesManager_API::getInstance()->isLanguageAvailable($languageCode)) {
+ $languageCode = Piwik_Translate::getInstance()->getLanguageDefault();
+ }
+ return $languageCode;
+ }
+
+ /**
+ * @return string Full english language string, eg. "French"
+ */
+ static public function getLanguageNameForCurrentUser()
+ {
+ $languageCode = self::getLanguageCodeForCurrentUser();
+ $languages = Piwik_LanguagesManager_API::getInstance()->getAvailableLanguageNames();
+ foreach ($languages as $language) {
+ if ($language['code'] === $languageCode) {
+ return $language['name'];
+ }
+ }
+ }
+
+ /**
+ * @return string|false if language preference could not be loaded
+ */
+ static protected function getLanguageFromPreferences()
+ {
+ if (($language = self::getLanguageForSession()) != null) {
+ return $language;
+ }
+
+ try {
+ $currentUser = Piwik::getCurrentUserLogin();
+ return Piwik_LanguagesManager_API::getInstance()->getLanguageForUser($currentUser);
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+
+ /**
+ * Returns the langage for the session
+ *
+ * @return string|null
+ */
+ static public function getLanguageForSession()
+ {
+ $cookieName = Piwik_Config::getInstance()->General['language_cookie_name'];
+ $cookie = new Piwik_Cookie($cookieName);
+ if ($cookie->isCookieFound()) {
+ return $cookie->get('language');
+ }
+ return null;
+ }
+
+ /**
+ * Set the language for the session
+ *
+ * @param string $languageCode ISO language code
+ * @return bool
+ */
+ static public function setLanguageForSession($languageCode)
+ {
+ if (!Piwik_LanguagesManager_API::getInstance()->isLanguageAvailable($languageCode)) {
+ return false;
+ }
+
+ $cookieName = Piwik_Config::getInstance()->General['language_cookie_name'];
+ $cookie = new Piwik_Cookie($cookieName, 0);
+ $cookie->set('language', $languageCode);
+ $cookie->save();
+ }
}
diff --git a/plugins/LanguagesManager/templates/languageSelector.js b/plugins/LanguagesManager/templates/languageSelector.js
index 4343d998cc..cebd8d54a5 100644
--- a/plugins/LanguagesManager/templates/languageSelector.js
+++ b/plugins/LanguagesManager/templates/languageSelector.js
@@ -5,22 +5,22 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-$(document).ready(function() {
- // no Language sector on the page
- if($("#languageSelection").size() == 0) return false;
-
+$(document).ready(function () {
+ // no Language sector on the page
+ if ($("#languageSelection").size() == 0) return false;
+
$("#languageSelection input").hide();
var select = $("#language").hide();
- var langSelect = $( "<a>" )
- .insertAfter( select )
- .text( select.children(':selected').text() )
- .autocomplete({
- delay: 0,
- minLength: 0,
- appendTo: '#languageSelection',
- source: function( request, response ) {
- response( select.children( "option" ).map(function() {
- var text = $( this ).text();
+ var langSelect = $("<a>")
+ .insertAfter(select)
+ .text(select.children(':selected').text())
+ .autocomplete({
+ delay: 0,
+ minLength: 0,
+ appendTo: '#languageSelection',
+ source: function (request, response) {
+ response(select.children("option").map(function () {
+ var text = $(this).text();
return {
label: text,
value: this.value,
@@ -28,39 +28,39 @@ $(document).ready(function() {
href: $(this).attr('href'),
option: this
};
- }) );
- },
- select: function( event, ui ) {
- ui.item.option.selected = true;
- if(ui.item.value) {
- langSelect.text(ui.item.label);
- $('#languageSelection form').submit();
- } else if(ui.item.href) {
- window.open(ui.item.href);
+ }));
+ },
+ select: function (event, ui) {
+ ui.item.option.selected = true;
+ if (ui.item.value) {
+ langSelect.text(ui.item.label);
+ $('#languageSelection form').submit();
+ } else if (ui.item.href) {
+ window.open(ui.item.href);
+ }
+ }
+ })
+ .click(function () {
+ // close if already visible
+ if ($(this).autocomplete("widget").is(":visible")) {
+ $(this).autocomplete("close");
+ return;
}
- }
- })
- .click(function() {
- // close if already visible
- if ( $(this).autocomplete( "widget" ).is(":visible") ) {
- $(this).autocomplete("close");
- return;
- }
- // pass empty string as value to search for, displaying all results
- $(this).autocomplete( "search", "" );
- });
+ // pass empty string as value to search for, displaying all results
+ $(this).autocomplete("search", "");
+ });
- langSelect.data( "autocomplete" )._renderItem = function( ul, item ) {
+ langSelect.data("autocomplete")._renderItem = function (ul, item) {
$(ul).attr('id', 'languageSelect');
- return $( "<li></li>" )
- .data( "item.autocomplete", item )
- .append( "<a title=\"" + item.title + "\" href=\"" + $('#languageSelection form').attr('action') + "&language=" + item.value + "\">" + item.label + "</a>" )
- .appendTo( ul );
+ return $("<li></li>")
+ .data("item.autocomplete", item)
+ .append("<a title=\"" + item.title + "\" href=\"" + $('#languageSelection form').attr('action') + "&language=" + item.value + "\">" + item.label + "</a>")
+ .appendTo(ul);
};
- $('body').on('mouseup',function(e){
- if(!$(e.target).parents('#languageSelection').length && !$(e.target).is('#languageSelection') && !$(e.target).parents('#languageSelect').length) {
+ $('body').on('mouseup', function (e) {
+ if (!$(e.target).parents('#languageSelection').length && !$(e.target).is('#languageSelection') && !$(e.target).parents('#languageSelect').length) {
langSelect.autocomplete("close");
}
});
diff --git a/plugins/LanguagesManager/templates/languages.tpl b/plugins/LanguagesManager/templates/languages.tpl
index c1c84e8ffa..9da7b2ff93 100644
--- a/plugins/LanguagesManager/templates/languages.tpl
+++ b/plugins/LanguagesManager/templates/languages.tpl
@@ -1,19 +1,21 @@
<span class="topBarElem" style="padding-right:70px">
<span id="languageSelection" style="position:absolute">
<form action="index.php?module=LanguagesManager&amp;action=saveLanguage" method="post">
- <select name="language" id="language">
- <option title="" value="" href="?module=Proxy&amp;action=redirect&amp;url=http://piwik.org/translations/">{'LanguagesManager_AboutPiwikTranslations'|translate}</option>
- {foreach from=$languages item=language}
- <option value="{$language.code}" {if $language.code == $currentLanguageCode}selected="selected"{/if} title="{$language.name} ({$language.english_name})">{$language.name}</option>
- {/foreach}
- </select>
- {* During installation token_auth is not set *}
- {if !empty($token_auth)}<input type="hidden" name="token_auth" value="{$token_auth}"/>{/if}
- <input type="submit" value="go" />
- </form>
+ <select name="language" id="language">
+ <option title="" value=""
+ href="?module=Proxy&amp;action=redirect&amp;url=http://piwik.org/translations/">{'LanguagesManager_AboutPiwikTranslations'|translate}</option>
+ {foreach from=$languages item=language}
+ <option value="{$language.code}" {if $language.code == $currentLanguageCode}selected="selected"{/if}
+ title="{$language.name} ({$language.english_name})">{$language.name}</option>
+ {/foreach}
+ </select>
+ {* During installation token_auth is not set *}
+ {if !empty($token_auth)}<input type="hidden" name="token_auth" value="{$token_auth}"/>{/if}
+ <input type="submit" value="go"/>
+ </form>
</span>
<script type="text/javascript">
- piwik.languageName = "{$currentLanguageName}";
- </script>
+ piwik.languageName = "{$currentLanguageName}";
+ </script>
</span>
diff --git a/plugins/Live/API.php b/plugins/Live/API.php
index 64fe37c444..e1b2525c1f 100644
--- a/plugins/Live/API.php
+++ b/plugins/Live/API.php
@@ -15,176 +15,173 @@
require_once PIWIK_INCLUDE_PATH . '/plugins/Live/Visitor.php';
/**
- * The Live! API lets you access complete visit level information about your visitors. Combined with the power of <a href='http://piwik.org/docs/analytics-api/segmentation/' target='_blank'>Segmentation</a>,
+ * The Live! API lets you access complete visit level information about your visitors. Combined with the power of <a href='http://piwik.org/docs/analytics-api/segmentation/' target='_blank'>Segmentation</a>,
* you will be able to request visits filtered by any criteria.
- *
- * The method "getLastVisitsDetails" will return extensive data for each visit, which includes: server time, visitId, visitorId,
- * visitorType (new or returning), number of pages, list of all pages (and events, file downloaded and outlinks clicked),
- * custom variables names and values set to this visit, number of goal conversions (and list of all Goal conversions for this visit,
- * with time of conversion, revenue, URL, etc.), but also other attributes such as: days since last visit, days since first visit,
+ *
+ * The method "getLastVisitsDetails" will return extensive data for each visit, which includes: server time, visitId, visitorId,
+ * visitorType (new or returning), number of pages, list of all pages (and events, file downloaded and outlinks clicked),
+ * custom variables names and values set to this visit, number of goal conversions (and list of all Goal conversions for this visit,
+ * with time of conversion, revenue, URL, etc.), but also other attributes such as: days since last visit, days since first visit,
* country, continent, visitor IP,
- * provider, referrer used (referrer name, keyword if it was a search engine, full URL), campaign name and keyword, operating system,
+ * provider, referrer used (referrer name, keyword if it was a search engine, full URL), campaign name and keyword, operating system,
* browser, type of screen, resolution, supported browser plugins (flash, java, silverlight, pdf, etc.), various dates & times format to make
* it easier for API users... and more!
- *
+ *
* With the parameter <a href='http://piwik.org/docs/analytics-api/segmentation/' target='_blank'>'&segment='</a> you can filter the
* returned visits by any criteria (visitor IP, visitor ID, country, keyword used, time of day, etc.).
- *
+ *
* The method "getCounters" is used to return a simple counter: visits, number of actions, number of converted visits, in the last N minutes.
- *
+ *
* See also the documentation about <a href='http://piwik.org/docs/real-time/' target='_blank'>Real time widget and visitor level reports</a> in Piwik.
* @package Piwik_Live
*/
class Piwik_Live_API
{
- static private $instance = null;
- /**
- * @return Piwik_Live_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * This will return simple counters, for a given website ID, for visits over the last N minutes
- *
- * @param int Id Site
- * @param int Number of minutes to look back at
- *
- * @return array( visits => N, actions => M, visitsConverted => P )
- */
- public function getCounters($idSite, $lastMinutes, $segment = false)
- {
- Piwik::checkUserHasViewAccess($idSite);
- $lastMinutes = (int)$lastMinutes;
-
- $select = "count(*) as visits,
+ static private $instance = null;
+
+ /**
+ * @return Piwik_Live_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * This will return simple counters, for a given website ID, for visits over the last N minutes
+ *
+ * @param int Id Site
+ * @param int Number of minutes to look back at
+ *
+ * @return array( visits => N, actions => M, visitsConverted => P )
+ */
+ public function getCounters($idSite, $lastMinutes, $segment = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $lastMinutes = (int)$lastMinutes;
+
+ $select = "count(*) as visits,
SUM(log_visit.visit_total_actions) as actions,
SUM(log_visit.visit_goal_converted) as visitsConverted,
COUNT(DISTINCT log_visit.idvisitor) as visitors";
-
- $from = "log_visit";
-
- $where = "log_visit.idsite = ?
+
+ $from = "log_visit";
+
+ $where = "log_visit.idsite = ?
AND log_visit.visit_last_action_time >= ?";
-
- $bind = array(
- $idSite,
- Piwik_Date::factory(time() - $lastMinutes * 60)->toString('Y-m-d H:i:s')
- );
-
- $segment = new Piwik_Segment($segment, $idSite);
- $query = $segment->getSelectQuery($select, $from, $where, $bind);
-
- $data = Piwik_FetchAll($query['sql'], $query['bind']);
-
- // These could be unset for some reasons, ensure they are set to 0
- empty($data[0]['actions']) ? $data[0]['actions'] = 0 : '';
- empty($data[0]['visitsConverted']) ? $data[0]['visitsConverted'] = 0 : '';
- return $data;
- }
-
- /**
- * The same functionnality can be obtained using segment=visitorId==$visitorId with getLastVisitsDetails
- *
- * @deprecated
- * @ignore
- * @param int $visitorId
- * @param int $idSite
- * @param int $filter_limit
- * @return Piwik_DataTable
- */
- public function getLastVisitsForVisitor( $visitorId, $idSite, $filter_limit = 10 )
- {
- $visitorDetails = $this->loadLastVisitorDetailsFromDatabase($idSite, $period = false, $date = false, $segment = false, $filter_limit, $filter_offset = false, $visitorId);
- $table = $this->getCleanedVisitorsFromDetails($visitorDetails, $idSite);
- return $table;
- }
-
- /**
- * Returns the last visits tracked in the specified website
- * You can define any number of filters: none, one, many or all parameters can be defined
- *
- * @param int $idSite Site ID
- * @param bool|string $period Period to restrict to when looking at the logs
- * @param bool|string $date Date to restrict to
- * @param bool|int $segment (optional) Number of visits rows to return
- * @param bool|int $filter_limit (optional) Only return X visits
- * @param bool|int $filter_offset (optional) Skip the first X visits (useful when paginating)
- * @param bool|int $minTimestamp (optional) Minimum timestamp to restrict the query to (useful when paginating or refreshing visits)
- *
- * @return Piwik_DataTable
- */
- public function getLastVisitsDetails( $idSite, $period, $date, $segment = false, $filter_limit = false, $filter_offset = false, $minTimestamp = false )
- {
- if(empty($filter_limit))
- {
- $filter_limit = 10;
- }
- Piwik::checkUserHasViewAccess($idSite);
- $visitorDetails = $this->loadLastVisitorDetailsFromDatabase($idSite, $period, $date, $segment, $filter_limit, $filter_offset, $visitorId = false, $minTimestamp);
- $dataTable = $this->getCleanedVisitorsFromDetails($visitorDetails, $idSite);
-
- return $dataTable;
- }
-
- /**
- * @deprecated
- */
-
- public function getLastVisits( $idSite, $filter_limit = 10, $minTimestamp = false )
- {
- return $this->getLastVisitsDetails($idSite, $period = false, $date = false, $segment = false, $filter_limit, $filter_offset = false, $minTimestamp );
- }
-
- /**
- * For an array of visits, query the list of pages for this visit
- * as well as make the data human readable
- * @param array $visitorDetails
- * @param int $idSite
- * @return Piwik_DataTable
- */
- private function getCleanedVisitorsFromDetails($visitorDetails, $idSite)
- {
- $actionsLimit = Piwik_Config::getInstance()->General['visitor_log_maximum_actions_per_visit'];
-
- $table = new Piwik_DataTable();
-
- $site = new Piwik_Site($idSite);
- $timezone = $site->getTimezone();
- $currencies = Piwik_SitesManager_API::getInstance()->getCurrencySymbols();
- foreach($visitorDetails as $visitorDetail)
- {
- $this->cleanVisitorDetails($visitorDetail, $idSite);
- $visitor = new Piwik_Live_Visitor($visitorDetail);
- $visitorDetailsArray = $visitor->getAllVisitorDetails();
-
- $visitorDetailsArray['siteCurrency'] = $site->getCurrency();
- $visitorDetailsArray['siteCurrencySymbol'] = @$currencies[$site->getCurrency()];
- $visitorDetailsArray['serverTimestamp'] = $visitorDetailsArray['lastActionTimestamp'];
- $dateTimeVisit = Piwik_Date::factory($visitorDetailsArray['lastActionTimestamp'], $timezone);
- $visitorDetailsArray['serverTimePretty'] = $dateTimeVisit->getLocalized('%time%');
- $visitorDetailsArray['serverDatePretty'] = $dateTimeVisit->getLocalized(Piwik_Translate('CoreHome_ShortDateFormat'));
-
- $dateTimeVisitFirstAction = Piwik_Date::factory($visitorDetailsArray['firstActionTimestamp'], $timezone);
- $visitorDetailsArray['serverDatePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized(Piwik_Translate('CoreHome_ShortDateFormat'));
- $visitorDetailsArray['serverTimePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized('%time%');
-
- $idvisit = $visitorDetailsArray['idVisit'];
-
- $sqlCustomVariables = '';
- for($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++)
- {
- $sqlCustomVariables .= ', custom_var_k' . $i . ', custom_var_v' . $i;
- }
- // The second join is a LEFT join to allow returning records that don't have a matching page title
- // eg. Downloads, Outlinks. For these, idaction_name is set to 0
- $sql = "
+
+ $bind = array(
+ $idSite,
+ Piwik_Date::factory(time() - $lastMinutes * 60)->toString('Y-m-d H:i:s')
+ );
+
+ $segment = new Piwik_Segment($segment, $idSite);
+ $query = $segment->getSelectQuery($select, $from, $where, $bind);
+
+ $data = Piwik_FetchAll($query['sql'], $query['bind']);
+
+ // These could be unset for some reasons, ensure they are set to 0
+ empty($data[0]['actions']) ? $data[0]['actions'] = 0 : '';
+ empty($data[0]['visitsConverted']) ? $data[0]['visitsConverted'] = 0 : '';
+ return $data;
+ }
+
+ /**
+ * The same functionnality can be obtained using segment=visitorId==$visitorId with getLastVisitsDetails
+ *
+ * @deprecated
+ * @ignore
+ * @param int $visitorId
+ * @param int $idSite
+ * @param int $filter_limit
+ * @return Piwik_DataTable
+ */
+ public function getLastVisitsForVisitor($visitorId, $idSite, $filter_limit = 10)
+ {
+ $visitorDetails = $this->loadLastVisitorDetailsFromDatabase($idSite, $period = false, $date = false, $segment = false, $filter_limit, $filter_offset = false, $visitorId);
+ $table = $this->getCleanedVisitorsFromDetails($visitorDetails, $idSite);
+ return $table;
+ }
+
+ /**
+ * Returns the last visits tracked in the specified website
+ * You can define any number of filters: none, one, many or all parameters can be defined
+ *
+ * @param int $idSite Site ID
+ * @param bool|string $period Period to restrict to when looking at the logs
+ * @param bool|string $date Date to restrict to
+ * @param bool|int $segment (optional) Number of visits rows to return
+ * @param bool|int $filter_limit (optional) Only return X visits
+ * @param bool|int $filter_offset (optional) Skip the first X visits (useful when paginating)
+ * @param bool|int $minTimestamp (optional) Minimum timestamp to restrict the query to (useful when paginating or refreshing visits)
+ *
+ * @return Piwik_DataTable
+ */
+ public function getLastVisitsDetails($idSite, $period, $date, $segment = false, $filter_limit = false, $filter_offset = false, $minTimestamp = false)
+ {
+ if (empty($filter_limit)) {
+ $filter_limit = 10;
+ }
+ Piwik::checkUserHasViewAccess($idSite);
+ $visitorDetails = $this->loadLastVisitorDetailsFromDatabase($idSite, $period, $date, $segment, $filter_limit, $filter_offset, $visitorId = false, $minTimestamp);
+ $dataTable = $this->getCleanedVisitorsFromDetails($visitorDetails, $idSite);
+
+ return $dataTable;
+ }
+
+ /**
+ * @deprecated
+ */
+
+ public function getLastVisits($idSite, $filter_limit = 10, $minTimestamp = false)
+ {
+ return $this->getLastVisitsDetails($idSite, $period = false, $date = false, $segment = false, $filter_limit, $filter_offset = false, $minTimestamp);
+ }
+
+ /**
+ * For an array of visits, query the list of pages for this visit
+ * as well as make the data human readable
+ * @param array $visitorDetails
+ * @param int $idSite
+ * @return Piwik_DataTable
+ */
+ private function getCleanedVisitorsFromDetails($visitorDetails, $idSite)
+ {
+ $actionsLimit = Piwik_Config::getInstance()->General['visitor_log_maximum_actions_per_visit'];
+
+ $table = new Piwik_DataTable();
+
+ $site = new Piwik_Site($idSite);
+ $timezone = $site->getTimezone();
+ $currencies = Piwik_SitesManager_API::getInstance()->getCurrencySymbols();
+ foreach ($visitorDetails as $visitorDetail) {
+ $this->cleanVisitorDetails($visitorDetail, $idSite);
+ $visitor = new Piwik_Live_Visitor($visitorDetail);
+ $visitorDetailsArray = $visitor->getAllVisitorDetails();
+
+ $visitorDetailsArray['siteCurrency'] = $site->getCurrency();
+ $visitorDetailsArray['siteCurrencySymbol'] = @$currencies[$site->getCurrency()];
+ $visitorDetailsArray['serverTimestamp'] = $visitorDetailsArray['lastActionTimestamp'];
+ $dateTimeVisit = Piwik_Date::factory($visitorDetailsArray['lastActionTimestamp'], $timezone);
+ $visitorDetailsArray['serverTimePretty'] = $dateTimeVisit->getLocalized('%time%');
+ $visitorDetailsArray['serverDatePretty'] = $dateTimeVisit->getLocalized(Piwik_Translate('CoreHome_ShortDateFormat'));
+
+ $dateTimeVisitFirstAction = Piwik_Date::factory($visitorDetailsArray['firstActionTimestamp'], $timezone);
+ $visitorDetailsArray['serverDatePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized(Piwik_Translate('CoreHome_ShortDateFormat'));
+ $visitorDetailsArray['serverTimePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized('%time%');
+
+ $idvisit = $visitorDetailsArray['idVisit'];
+
+ $sqlCustomVariables = '';
+ for ($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++) {
+ $sqlCustomVariables .= ', custom_var_k' . $i . ', custom_var_v' . $i;
+ }
+ // The second join is a LEFT join to allow returning records that don't have a matching page title
+ // eg. Downloads, Outlinks. For these, idaction_name is set to 0
+ $sql = "
SELECT
COALESCE(log_action.type,log_action_title.type) AS type,
log_action.name AS url,
@@ -195,53 +192,48 @@ class Piwik_Live_API
log_link_visit_action.server_time as serverTimePretty,
log_link_visit_action.time_spent_ref_action as timeSpentRef
$sqlCustomVariables
- FROM " .Piwik_Common::prefixTable('log_link_visit_action')." AS log_link_visit_action
- LEFT JOIN " .Piwik_Common::prefixTable('log_action')." AS log_action
+ FROM " . Piwik_Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action
+ LEFT JOIN " . Piwik_Common::prefixTable('log_action') . " AS log_action
ON log_link_visit_action.idaction_url = log_action.idaction
- LEFT JOIN " .Piwik_Common::prefixTable('log_action')." AS log_action_title
+ LEFT JOIN " . Piwik_Common::prefixTable('log_action') . " AS log_action_title
ON log_link_visit_action.idaction_name = log_action_title.idaction
WHERE log_link_visit_action.idvisit = ?
LIMIT 0, $actionsLimit
";
- $actionDetails = Piwik_FetchAll($sql, array($idvisit));
-
- foreach($actionDetails as $actionIdx => &$actionDetail)
- {
- $actionDetail =& $actionDetails[$actionIdx];
- $customVariablesPage = array();
- for($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++)
- {
- if(!empty($actionDetail['custom_var_k'.$i]))
- {
- $cvarKey = $actionDetail['custom_var_k'.$i];
- $cvarKey = $this->getCustomVariablePrettyKey($cvarKey);
- $customVariablesPage[$i] = array(
- 'customVariableName'.$i => $cvarKey,
- 'customVariableValue'.$i => $actionDetail['custom_var_v'.$i],
- );
- }
- unset($actionDetail['custom_var_k'.$i]);
- unset($actionDetail['custom_var_v'.$i]);
- }
- if(!empty($customVariablesPage))
- {
- $actionDetail['customVariables'] = $customVariablesPage;
- }
- // reconstruct url from prefix
- $actionDetail['url'] = Piwik_Tracker_Action::reconstructNormalizedUrl($actionDetail['url'], $actionDetail['url_prefix']);
- unset($actionDetail['url_prefix']);
- // set the time spent for this action (which is the timeSpentRef of the next action)
- if (isset($actionDetails[$actionIdx + 1]))
- {
- $actionDetail['timeSpent'] = $actionDetails[$actionIdx + 1]['timeSpentRef'];
- $actionDetail['timeSpentPretty'] = Piwik::getPrettyTimeFromSeconds($actionDetail['timeSpent']);
-
- }
- unset($actionDetails[$actionIdx]['timeSpentRef']); // not needed after timeSpent is added
- }
-
- // If the visitor converted a goal, we shall select all Goals
- $sql = "
+ $actionDetails = Piwik_FetchAll($sql, array($idvisit));
+
+ foreach ($actionDetails as $actionIdx => &$actionDetail) {
+ $actionDetail =& $actionDetails[$actionIdx];
+ $customVariablesPage = array();
+ for ($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++) {
+ if (!empty($actionDetail['custom_var_k' . $i])) {
+ $cvarKey = $actionDetail['custom_var_k' . $i];
+ $cvarKey = $this->getCustomVariablePrettyKey($cvarKey);
+ $customVariablesPage[$i] = array(
+ 'customVariableName' . $i => $cvarKey,
+ 'customVariableValue' . $i => $actionDetail['custom_var_v' . $i],
+ );
+ }
+ unset($actionDetail['custom_var_k' . $i]);
+ unset($actionDetail['custom_var_v' . $i]);
+ }
+ if (!empty($customVariablesPage)) {
+ $actionDetail['customVariables'] = $customVariablesPage;
+ }
+ // reconstruct url from prefix
+ $actionDetail['url'] = Piwik_Tracker_Action::reconstructNormalizedUrl($actionDetail['url'], $actionDetail['url_prefix']);
+ unset($actionDetail['url_prefix']);
+ // set the time spent for this action (which is the timeSpentRef of the next action)
+ if (isset($actionDetails[$actionIdx + 1])) {
+ $actionDetail['timeSpent'] = $actionDetails[$actionIdx + 1]['timeSpentRef'];
+ $actionDetail['timeSpentPretty'] = Piwik::getPrettyTimeFromSeconds($actionDetail['timeSpent']);
+
+ }
+ unset($actionDetails[$actionIdx]['timeSpentRef']); // not needed after timeSpent is added
+ }
+
+ // If the visitor converted a goal, we shall select all Goals
+ $sql = "
SELECT
'goal' as type,
goal.name as goalName,
@@ -249,8 +241,8 @@ class Piwik_Live_API
log_conversion.idlink_va as goalPageId,
log_conversion.server_time as serverTimePretty,
log_conversion.url as url
- FROM ".Piwik_Common::prefixTable('log_conversion')." AS log_conversion
- LEFT JOIN ".Piwik_Common::prefixTable('goal')." AS goal
+ FROM " . Piwik_Common::prefixTable('log_conversion') . " AS log_conversion
+ LEFT JOIN " . Piwik_Common::prefixTable('goal') . " AS goal
ON (goal.idsite = log_conversion.idsite
AND
goal.idgoal = log_conversion.idgoal)
@@ -259,293 +251,269 @@ class Piwik_Live_API
AND log_conversion.idgoal > 0
LIMIT 0, $actionsLimit
";
- $goalDetails = Piwik_FetchAll($sql, array($idvisit));
+ $goalDetails = Piwik_FetchAll($sql, array($idvisit));
- $sql = "SELECT
- case idgoal when ".Piwik_Tracker_GoalManager::IDGOAL_CART." then '".Piwik_Archive::LABEL_ECOMMERCE_CART."' else '".Piwik_Archive::LABEL_ECOMMERCE_ORDER."' end as type,
+ $sql = "SELECT
+ case idgoal when " . Piwik_Tracker_GoalManager::IDGOAL_CART . " then '" . Piwik_Archive::LABEL_ECOMMERCE_CART . "' else '" . Piwik_Archive::LABEL_ECOMMERCE_ORDER . "' end as type,
idorder as orderId,
- ".Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue')." as revenue,
- ".Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_subtotal')." as revenueSubTotal,
- ".Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_tax')." as revenueTax,
- ".Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_shipping')." as revenueShipping,
- ".Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_discount')." as revenueDiscount,
+ " . Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue') . " as revenue,
+ " . Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_subtotal') . " as revenueSubTotal,
+ " . Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_tax') . " as revenueTax,
+ " . Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_shipping') . " as revenueShipping,
+ " . Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_discount') . " as revenueDiscount,
items as items,
log_conversion.server_time as serverTimePretty
- FROM ".Piwik_Common::prefixTable('log_conversion')." AS log_conversion
+ FROM " . Piwik_Common::prefixTable('log_conversion') . " AS log_conversion
WHERE idvisit = ?
- AND idgoal <= ".Piwik_Tracker_GoalManager::IDGOAL_ORDER."
+ AND idgoal <= " . Piwik_Tracker_GoalManager::IDGOAL_ORDER . "
LIMIT 0, $actionsLimit";
- $ecommerceDetails = Piwik_FetchAll($sql, array($idvisit));
-
- foreach($ecommerceDetails as &$ecommerceDetail)
- {
- if($ecommerceDetail['type'] == Piwik_Archive::LABEL_ECOMMERCE_CART)
- {
- unset($ecommerceDetail['orderId']);
- unset($ecommerceDetail['revenueSubTotal']);
- unset($ecommerceDetail['revenueTax']);
- unset($ecommerceDetail['revenueShipping']);
- unset($ecommerceDetail['revenueDiscount']);
- }
-
- // 25.00 => 25
- foreach($ecommerceDetail as $column => $value)
- {
- if(strpos($column, 'revenue') !== false)
- {
- if($value == round($value))
- {
- $ecommerceDetail[$column] = round($value);
- }
- }
- }
- }
-
- // Enrich ecommerce carts/orders with the list of products
- usort($ecommerceDetails, array($this, 'sortByServerTime'));
- foreach($ecommerceDetails as $key => &$ecommerceConversion)
- {
- $sql = "SELECT
+ $ecommerceDetails = Piwik_FetchAll($sql, array($idvisit));
+
+ foreach ($ecommerceDetails as &$ecommerceDetail) {
+ if ($ecommerceDetail['type'] == Piwik_Archive::LABEL_ECOMMERCE_CART) {
+ unset($ecommerceDetail['orderId']);
+ unset($ecommerceDetail['revenueSubTotal']);
+ unset($ecommerceDetail['revenueTax']);
+ unset($ecommerceDetail['revenueShipping']);
+ unset($ecommerceDetail['revenueDiscount']);
+ }
+
+ // 25.00 => 25
+ foreach ($ecommerceDetail as $column => $value) {
+ if (strpos($column, 'revenue') !== false) {
+ if ($value == round($value)) {
+ $ecommerceDetail[$column] = round($value);
+ }
+ }
+ }
+ }
+
+ // Enrich ecommerce carts/orders with the list of products
+ usort($ecommerceDetails, array($this, 'sortByServerTime'));
+ foreach ($ecommerceDetails as $key => &$ecommerceConversion) {
+ $sql = "SELECT
log_action_sku.name as itemSKU,
log_action_name.name as itemName,
log_action_category.name as itemCategory,
- ".Piwik_ArchiveProcessing_Day::getSqlRevenue('price')." as price,
+ " . Piwik_ArchiveProcessing_Day::getSqlRevenue('price') . " as price,
quantity as quantity
- FROM ".Piwik_Common::prefixTable('log_conversion_item')."
- INNER JOIN " .Piwik_Common::prefixTable('log_action')." AS log_action_sku
+ FROM " . Piwik_Common::prefixTable('log_conversion_item') . "
+ INNER JOIN " . Piwik_Common::prefixTable('log_action') . " AS log_action_sku
ON idaction_sku = log_action_sku.idaction
- LEFT JOIN " .Piwik_Common::prefixTable('log_action')." AS log_action_name
+ LEFT JOIN " . Piwik_Common::prefixTable('log_action') . " AS log_action_name
ON idaction_name = log_action_name.idaction
- LEFT JOIN " .Piwik_Common::prefixTable('log_action')." AS log_action_category
+ LEFT JOIN " . Piwik_Common::prefixTable('log_action') . " AS log_action_category
ON idaction_category = log_action_category.idaction
WHERE idvisit = ?
AND idorder = ?
AND deleted = 0
LIMIT 0, $actionsLimit
";
- $bind = array($idvisit, isset($ecommerceConversion['orderId'])
- ? $ecommerceConversion['orderId']
- : Piwik_Tracker_GoalManager::ITEM_IDORDER_ABANDONED_CART
- );
-
- $itemsDetails = Piwik_FetchAll($sql, $bind);
- foreach($itemsDetails as &$detail)
- {
- if($detail['price'] == round($detail['price']))
- {
- $detail['price'] = round($detail['price']);
- }
- }
- $ecommerceConversion['itemDetails'] = $itemsDetails;
- }
-
- $actions = array_merge($actionDetails, $goalDetails, $ecommerceDetails);
-
- usort($actions, array($this, 'sortByServerTime'));
-
- $visitorDetailsArray['actionDetails'] = $actions;
- // Convert datetimes to the site timezone
- foreach($visitorDetailsArray['actionDetails'] as &$details)
- {
- switch($details['type'])
- {
- case 'goal':
- $details['icon'] = 'themes/default/images/goal.png';
- break;
- case Piwik_Archive::LABEL_ECOMMERCE_ORDER:
- case Piwik_Archive::LABEL_ECOMMERCE_CART:
- $details['icon'] = 'themes/default/images/'.$details['type'].'.gif';
- break;
- case Piwik_Tracker_Action_Interface::TYPE_DOWNLOAD:
- $details['type'] = 'download';
- $details['icon'] = 'themes/default/images/download.png';
- break;
- case Piwik_Tracker_Action_Interface::TYPE_OUTLINK:
- $details['type'] = 'outlink';
- $details['icon'] = 'themes/default/images/link.gif';
- break;
- case Piwik_Tracker_Action::TYPE_SITE_SEARCH:
- $details['type'] = 'search';
- $details['icon'] = 'themes/default/images/search_ico.png';
- break;
- default:
- $details['type'] = 'action';
- $details['icon'] = null;
- break;
- }
- $dateTimeVisit = Piwik_Date::factory($details['serverTimePretty'], $timezone);
- $details['serverTimePretty'] = $dateTimeVisit->getLocalized(Piwik_Translate('CoreHome_ShortDateFormat') .' %time%');
- }
- $visitorDetailsArray['goalConversions'] = count($goalDetails);
-
- $table->addRowFromArray( array(Piwik_DataTable_Row::COLUMNS => $visitorDetailsArray));
- }
- return $table;
- }
-
- private function getCustomVariablePrettyKey($key)
- {
- $rename = array(
- Piwik_Tracker_Action::CVAR_KEY_SEARCH_CATEGORY => Piwik_Translate('Actions_ColumnSearchCategory'),
- Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT => Piwik_Translate('Actions_ColumnSearchResultsCount'),
- );
- if(isset($rename[$key])) {
- return $rename[$key];
- }
- return $key;
- }
-
- private function sortByServerTime($a, $b)
- {
- $ta = strtotime($a['serverTimePretty']);
- $tb = strtotime($b['serverTimePretty']);
- return $ta < $tb
- ? -1
- : ($ta == $tb
- ? 0
- : 1 );
- }
-
- private function loadLastVisitorDetailsFromDatabase($idSite, $period = false, $date = false, $segment = false, $filter_limit = false, $filter_offset = false, $visitorId = false, $minTimestamp = false)
- {
- if(empty($filter_limit))
- {
- $filter_limit = 100;
- }
- if(empty($filter_offset))
- {
- $filter_offset = 0;
- }
-
- $where = $whereBind = array();
- $where[] = "log_visit.idsite = ? ";
- $whereBind[] = $idSite;
- $orderBy = "idsite, visit_last_action_time DESC";
- $orderByParent = "sub.visit_last_action_time DESC";
- if(!empty($visitorId))
- {
- $where[] = "log_visit.idvisitor = ? ";
- $whereBind[] = @Piwik_Common::hex2bin($visitorId);
- }
-
- if(!empty($minTimestamp))
- {
- $where[] = "log_visit.visit_last_action_time > ? ";
- $whereBind[] = date("Y-m-d H:i:s", $minTimestamp);
- }
-
- // If no other filter, only look at the last 24 hours of stats
- if(empty($visitorId)
- && empty($filter_offset)
- && empty($period)
- && empty($date))
- {
- $period = 'day';
- $date = 'yesterdaySameTime';
- }
-
- // SQL Filter with provided period
- if (!empty($period) && !empty($date))
- {
- $currentSite = new Piwik_Site($idSite);
- $currentTimezone = $currentSite->getTimezone();
-
- $dateString = $date;
- if($period == 'range')
- {
- $processedPeriod = new Piwik_Period_Range('range', $date);
- if($parsedDate = Piwik_Period_Range::parseDateRange($date))
- {
- $dateString = $parsedDate[2];
- }
- }
- else
- {
- $processedDate = Piwik_Date::factory($date);
- if($date == 'today'
- || $date == 'now'
- || $processedDate->toString() == Piwik_Date::factory('now', $currentTimezone)->toString())
- {
- $processedDate = $processedDate->subDay(1);
- }
- $processedPeriod = Piwik_Period::factory($period, $processedDate);
- }
- $dateStart = $processedPeriod->getDateStart()->setTimezone($currentTimezone);
- $where[] = "log_visit.visit_last_action_time >= ?";
- $whereBind[] = $dateStart->toString('Y-m-d H:i:s');
-
- if(!in_array($date, array('now', 'today', 'yesterdaySameTime'))
- && strpos($date, 'last') === false
- && strpos($date, 'previous') === false
- && Piwik_Date::factory($dateString)->toString('Y-m-d') != Piwik_Date::factory('now', $currentTimezone)->toString())
- {
- $dateEnd = $processedPeriod->getDateEnd()->setTimezone($currentTimezone);
- $where[] = " log_visit.visit_last_action_time <= ?";
- $dateEndString = $dateEnd->addDay(1)->toString('Y-m-d H:i:s');
- $whereBind[] = $dateEndString;
- }
- }
-
- if(count($where) > 0)
- {
- $where = join("
+ $bind = array($idvisit, isset($ecommerceConversion['orderId'])
+ ? $ecommerceConversion['orderId']
+ : Piwik_Tracker_GoalManager::ITEM_IDORDER_ABANDONED_CART
+ );
+
+ $itemsDetails = Piwik_FetchAll($sql, $bind);
+ foreach ($itemsDetails as &$detail) {
+ if ($detail['price'] == round($detail['price'])) {
+ $detail['price'] = round($detail['price']);
+ }
+ }
+ $ecommerceConversion['itemDetails'] = $itemsDetails;
+ }
+
+ $actions = array_merge($actionDetails, $goalDetails, $ecommerceDetails);
+
+ usort($actions, array($this, 'sortByServerTime'));
+
+ $visitorDetailsArray['actionDetails'] = $actions;
+ // Convert datetimes to the site timezone
+ foreach ($visitorDetailsArray['actionDetails'] as &$details) {
+ switch ($details['type']) {
+ case 'goal':
+ $details['icon'] = 'themes/default/images/goal.png';
+ break;
+ case Piwik_Archive::LABEL_ECOMMERCE_ORDER:
+ case Piwik_Archive::LABEL_ECOMMERCE_CART:
+ $details['icon'] = 'themes/default/images/' . $details['type'] . '.gif';
+ break;
+ case Piwik_Tracker_Action_Interface::TYPE_DOWNLOAD:
+ $details['type'] = 'download';
+ $details['icon'] = 'themes/default/images/download.png';
+ break;
+ case Piwik_Tracker_Action_Interface::TYPE_OUTLINK:
+ $details['type'] = 'outlink';
+ $details['icon'] = 'themes/default/images/link.gif';
+ break;
+ case Piwik_Tracker_Action::TYPE_SITE_SEARCH:
+ $details['type'] = 'search';
+ $details['icon'] = 'themes/default/images/search_ico.png';
+ break;
+ default:
+ $details['type'] = 'action';
+ $details['icon'] = null;
+ break;
+ }
+ $dateTimeVisit = Piwik_Date::factory($details['serverTimePretty'], $timezone);
+ $details['serverTimePretty'] = $dateTimeVisit->getLocalized(Piwik_Translate('CoreHome_ShortDateFormat') . ' %time%');
+ }
+ $visitorDetailsArray['goalConversions'] = count($goalDetails);
+
+ $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => $visitorDetailsArray));
+ }
+ return $table;
+ }
+
+ private function getCustomVariablePrettyKey($key)
+ {
+ $rename = array(
+ Piwik_Tracker_Action::CVAR_KEY_SEARCH_CATEGORY => Piwik_Translate('Actions_ColumnSearchCategory'),
+ Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT => Piwik_Translate('Actions_ColumnSearchResultsCount'),
+ );
+ if (isset($rename[$key])) {
+ return $rename[$key];
+ }
+ return $key;
+ }
+
+ private function sortByServerTime($a, $b)
+ {
+ $ta = strtotime($a['serverTimePretty']);
+ $tb = strtotime($b['serverTimePretty']);
+ return $ta < $tb
+ ? -1
+ : ($ta == $tb
+ ? 0
+ : 1);
+ }
+
+ private function loadLastVisitorDetailsFromDatabase($idSite, $period = false, $date = false, $segment = false, $filter_limit = false, $filter_offset = false, $visitorId = false, $minTimestamp = false)
+ {
+ if (empty($filter_limit)) {
+ $filter_limit = 100;
+ }
+ if (empty($filter_offset)) {
+ $filter_offset = 0;
+ }
+
+ $where = $whereBind = array();
+ $where[] = "log_visit.idsite = ? ";
+ $whereBind[] = $idSite;
+ $orderBy = "idsite, visit_last_action_time DESC";
+ $orderByParent = "sub.visit_last_action_time DESC";
+ if (!empty($visitorId)) {
+ $where[] = "log_visit.idvisitor = ? ";
+ $whereBind[] = @Piwik_Common::hex2bin($visitorId);
+ }
+
+ if (!empty($minTimestamp)) {
+ $where[] = "log_visit.visit_last_action_time > ? ";
+ $whereBind[] = date("Y-m-d H:i:s", $minTimestamp);
+ }
+
+ // If no other filter, only look at the last 24 hours of stats
+ if (empty($visitorId)
+ && empty($filter_offset)
+ && empty($period)
+ && empty($date)
+ ) {
+ $period = 'day';
+ $date = 'yesterdaySameTime';
+ }
+
+ // SQL Filter with provided period
+ if (!empty($period) && !empty($date)) {
+ $currentSite = new Piwik_Site($idSite);
+ $currentTimezone = $currentSite->getTimezone();
+
+ $dateString = $date;
+ if ($period == 'range') {
+ $processedPeriod = new Piwik_Period_Range('range', $date);
+ if ($parsedDate = Piwik_Period_Range::parseDateRange($date)) {
+ $dateString = $parsedDate[2];
+ }
+ } else {
+ $processedDate = Piwik_Date::factory($date);
+ if ($date == 'today'
+ || $date == 'now'
+ || $processedDate->toString() == Piwik_Date::factory('now', $currentTimezone)->toString()
+ ) {
+ $processedDate = $processedDate->subDay(1);
+ }
+ $processedPeriod = Piwik_Period::factory($period, $processedDate);
+ }
+ $dateStart = $processedPeriod->getDateStart()->setTimezone($currentTimezone);
+ $where[] = "log_visit.visit_last_action_time >= ?";
+ $whereBind[] = $dateStart->toString('Y-m-d H:i:s');
+
+ if (!in_array($date, array('now', 'today', 'yesterdaySameTime'))
+ && strpos($date, 'last') === false
+ && strpos($date, 'previous') === false
+ && Piwik_Date::factory($dateString)->toString('Y-m-d') != Piwik_Date::factory('now', $currentTimezone)->toString()
+ ) {
+ $dateEnd = $processedPeriod->getDateEnd()->setTimezone($currentTimezone);
+ $where[] = " log_visit.visit_last_action_time <= ?";
+ $dateEndString = $dateEnd->addDay(1)->toString('Y-m-d H:i:s');
+ $whereBind[] = $dateEndString;
+ }
+ }
+
+ if (count($where) > 0) {
+ $where = join("
AND ", $where);
- }
- else
- {
- $where = false;
- }
-
- $segment = new Piwik_Segment($segment, $idSite);
-
- // Subquery to use the indexes for ORDER BY
- $select = "log_visit.*";
- $from = "log_visit";
- $subQuery = $segment->getSelectQuery($select, $from, $where, $whereBind, $orderBy);
-
- $sqlLimit = $filter_limit >= 1 ? " LIMIT ".$filter_offset.", ".(int)$filter_limit : "";
-
- // Group by idvisit so that a visitor converting 2 goals only appears once
- $sql = "
+ } else {
+ $where = false;
+ }
+
+ $segment = new Piwik_Segment($segment, $idSite);
+
+ // Subquery to use the indexes for ORDER BY
+ $select = "log_visit.*";
+ $from = "log_visit";
+ $subQuery = $segment->getSelectQuery($select, $from, $where, $whereBind, $orderBy);
+
+ $sqlLimit = $filter_limit >= 1 ? " LIMIT " . $filter_offset . ", " . (int)$filter_limit : "";
+
+ // Group by idvisit so that a visitor converting 2 goals only appears once
+ $sql = "
SELECT sub.*
FROM (
- ".$subQuery['sql']."
+ " . $subQuery['sql'] . "
$sqlLimit
) AS sub
GROUP BY sub.idvisit
ORDER BY $orderByParent
";
-
- try {
- $data = Piwik_FetchAll($sql, $subQuery['bind']);
- } catch(Exception $e) {
- echo $e->getMessage();exit;
- }
-
- return $data;
- }
-
- /**
- * Removes fields that are not meant to be displayed (md5 config hash)
- * Or that the user should only access if he is super user or admin (cookie, IP)
- *
- * @return void
- */
- private function cleanVisitorDetails( &$visitorDetails, $idSite )
- {
- $toUnset = array('config_id');
- if(Piwik::isUserIsAnonymous())
- {
- $toUnset[] = 'idvisitor';
- $toUnset[] = 'location_ip';
- }
- foreach($toUnset as $keyName)
- {
- if(isset($visitorDetails[$keyName]))
- {
- unset($visitorDetails[$keyName]);
- }
- }
- }
+
+ try {
+ $data = Piwik_FetchAll($sql, $subQuery['bind']);
+ } catch (Exception $e) {
+ echo $e->getMessage();
+ exit;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Removes fields that are not meant to be displayed (md5 config hash)
+ * Or that the user should only access if he is super user or admin (cookie, IP)
+ *
+ * @return void
+ */
+ private function cleanVisitorDetails(&$visitorDetails, $idSite)
+ {
+ $toUnset = array('config_id');
+ if (Piwik::isUserIsAnonymous()) {
+ $toUnset[] = 'idvisitor';
+ $toUnset[] = 'location_ip';
+ }
+ foreach ($toUnset as $keyName) {
+ if (isset($visitorDetails[$keyName])) {
+ unset($visitorDetails[$keyName]);
+ }
+ }
+ }
}
diff --git a/plugins/Live/Controller.php b/plugins/Live/Controller.php
index 815d2701a4..40f2300678 100644
--- a/plugins/Live/Controller.php
+++ b/plugins/Live/Controller.php
@@ -14,133 +14,132 @@
*/
class Piwik_Live_Controller extends Piwik_Controller
{
- const SIMPLE_VISIT_COUNT_WIDGET_LAST_MINUTES_CONFIG_KEY = 'live_widget_visitor_count_last_minutes';
-
- function index($fetch = false)
- {
- return $this->widget($fetch);
- }
-
- public function widget($fetch = false)
- {
- $view = Piwik_View::factory('index');
- $view->idSite = $this->idSite;
- $view = $this->setCounters($view);
- $view->liveRefreshAfterMs = (int)Piwik_Config::getInstance()->General['live_widget_refresh_after_seconds'] * 1000;
- $view->visitors = $this->getLastVisitsStart($fetchPlease = true);
- $view->liveTokenAuth = Piwik::getCurrentUserTokenAuth();
- return $this->render($view, $fetch);
- }
-
- public function getSimpleLastVisitCount( $fetch = false )
- {
- $lastMinutes = Piwik_Config::getInstance()->General[self::SIMPLE_VISIT_COUNT_WIDGET_LAST_MINUTES_CONFIG_KEY];
-
- $lastNData = Piwik_API_Request::processRequest('Live.getCounters', array('lastMinutes' => $lastMinutes));
-
- $view = Piwik_View::factory('simpleLastVisitCount');
- $view->lastMinutes = $lastMinutes;
- $view->visitors = Piwik::getPrettyNumber($lastNData[0]['visitors']);
- $view->visits = Piwik::getPrettyNumber($lastNData[0]['visits']);
- $view->actions = Piwik::getPrettyNumber($lastNData[0]['actions']);
- $view->refreshAfterXSecs = Piwik_Config::getInstance()->General['live_widget_refresh_after_seconds'];
- $view->translations = array(
- 'one_visitor' => Piwik_Translate('Live_NbVisitor'),
- 'visitors' => Piwik_Translate('Live_NbVisitors'),
- 'one_visit' => Piwik_Translate('General_OneVisit'),
- 'visits' => Piwik_Translate('General_NVisits'),
- 'one_action' => Piwik_Translate('General_OneAction'),
- 'actions' => Piwik_Translate('VisitsSummary_NbActionsDescription'),
- 'one_minute' => Piwik_Translate('General_OneMinute'),
- 'minutes' => Piwik_Translate('General_NMinutes')
- );
- return $this->render($view, $fetch);
- }
-
- public function ajaxTotalVisitors($fetch = false)
- {
- $view = Piwik_View::factory('totalVisits');
- $view = $this->setCounters($view);
- $view->idSite = $this->idSite;
- return $this->render($view, $fetch);
- }
-
- private function render($view, $fetch)
- {
- $rendered = $view->render();
- if($fetch) {
- return $rendered;
- }
- echo $rendered;
- }
-
- public function getVisitorLog($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName,
- __FUNCTION__,
- 'Live.getLastVisitsDetails'
- );
- $view->disableGenericFilters();
- $view->disableSort();
- $view->setTemplate("Live/templates/visitorLog.tpl");
- $view->setSortedColumn('idVisit', 'ASC');
- $view->disableSearchBox();
- $view->setLimit(20);
- $view->disableOffsetInformation();
- $view->disableExcludeLowPopulation();
-
- // disable the tag cloud, pie charts, bar chart icons
- $view->disableShowAllViewsIcons();
- // disable the button "show more datas"
- $view->disableShowAllColumns();
- // disable the RSS feed
- $view->disableShowExportAsRssFeed();
-
- // disable all row actions
- if ($view instanceof Piwik_ViewDataTable_HtmlTable)
- {
- $view->disableRowActions();
- }
-
- $view->setReportDocumentation(Piwik_Translate('Live_VisitorLogDocumentation', array('<br />', '<br />')));
-
- // set a very high row count so that the next link in the footer of the data table is always shown
- $view->setCustomParameter('totalRows', 10000000);
-
- $view->setCustomParameter('filterEcommerce', Piwik_Common::getRequestVar('filterEcommerce', 0, 'int'));
- $view->setCustomParameter('pageUrlNotDefined', Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageURL')));
-
- return $this->renderView($view, $fetch);
- }
-
- public function getLastVisitsStart($fetch = false)
- {
- // hack, ensure we load today's visits by default
- $_GET['date'] = 'today';
- $_GET['period'] = 'day';
- $view = Piwik_View::factory('lastVisits');
- $view->idSite = $this->idSite;
-
- $api = new Piwik_API_Request("method=Live.getLastVisitsDetails&idSite=$this->idSite&filter_limit=10&format=php&serialize=0&disable_generic_filters=1");
- $visitors = $api->process();
- $view->visitors = $visitors;
-
- return $this->render($view, $fetch);
- }
-
- private function setCounters($view)
- {
- $segment = Piwik_Common::getRequestVar('segment', false, 'string');
- $last30min = Piwik_Live_API::getInstance()->getCounters($this->idSite, $lastMinutes = 30, $segment);
- $last30min = $last30min[0];
- $today = Piwik_Live_API::getInstance()->getCounters($this->idSite, $lastMinutes = 24*60, $segment);
- $today = $today[0];
- $view->visitorsCountHalfHour = $last30min['visits'];
- $view->visitorsCountToday = $today['visits'];
- $view->pisHalfhour = $last30min['actions'];
- $view->pisToday = $today['actions'];
- return $view;
- }
+ const SIMPLE_VISIT_COUNT_WIDGET_LAST_MINUTES_CONFIG_KEY = 'live_widget_visitor_count_last_minutes';
+
+ function index($fetch = false)
+ {
+ return $this->widget($fetch);
+ }
+
+ public function widget($fetch = false)
+ {
+ $view = Piwik_View::factory('index');
+ $view->idSite = $this->idSite;
+ $view = $this->setCounters($view);
+ $view->liveRefreshAfterMs = (int)Piwik_Config::getInstance()->General['live_widget_refresh_after_seconds'] * 1000;
+ $view->visitors = $this->getLastVisitsStart($fetchPlease = true);
+ $view->liveTokenAuth = Piwik::getCurrentUserTokenAuth();
+ return $this->render($view, $fetch);
+ }
+
+ public function getSimpleLastVisitCount($fetch = false)
+ {
+ $lastMinutes = Piwik_Config::getInstance()->General[self::SIMPLE_VISIT_COUNT_WIDGET_LAST_MINUTES_CONFIG_KEY];
+
+ $lastNData = Piwik_API_Request::processRequest('Live.getCounters', array('lastMinutes' => $lastMinutes));
+
+ $view = Piwik_View::factory('simpleLastVisitCount');
+ $view->lastMinutes = $lastMinutes;
+ $view->visitors = Piwik::getPrettyNumber($lastNData[0]['visitors']);
+ $view->visits = Piwik::getPrettyNumber($lastNData[0]['visits']);
+ $view->actions = Piwik::getPrettyNumber($lastNData[0]['actions']);
+ $view->refreshAfterXSecs = Piwik_Config::getInstance()->General['live_widget_refresh_after_seconds'];
+ $view->translations = array(
+ 'one_visitor' => Piwik_Translate('Live_NbVisitor'),
+ 'visitors' => Piwik_Translate('Live_NbVisitors'),
+ 'one_visit' => Piwik_Translate('General_OneVisit'),
+ 'visits' => Piwik_Translate('General_NVisits'),
+ 'one_action' => Piwik_Translate('General_OneAction'),
+ 'actions' => Piwik_Translate('VisitsSummary_NbActionsDescription'),
+ 'one_minute' => Piwik_Translate('General_OneMinute'),
+ 'minutes' => Piwik_Translate('General_NMinutes')
+ );
+ return $this->render($view, $fetch);
+ }
+
+ public function ajaxTotalVisitors($fetch = false)
+ {
+ $view = Piwik_View::factory('totalVisits');
+ $view = $this->setCounters($view);
+ $view->idSite = $this->idSite;
+ return $this->render($view, $fetch);
+ }
+
+ private function render($view, $fetch)
+ {
+ $rendered = $view->render();
+ if ($fetch) {
+ return $rendered;
+ }
+ echo $rendered;
+ }
+
+ public function getVisitorLog($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName,
+ __FUNCTION__,
+ 'Live.getLastVisitsDetails'
+ );
+ $view->disableGenericFilters();
+ $view->disableSort();
+ $view->setTemplate("Live/templates/visitorLog.tpl");
+ $view->setSortedColumn('idVisit', 'ASC');
+ $view->disableSearchBox();
+ $view->setLimit(20);
+ $view->disableOffsetInformation();
+ $view->disableExcludeLowPopulation();
+
+ // disable the tag cloud, pie charts, bar chart icons
+ $view->disableShowAllViewsIcons();
+ // disable the button "show more datas"
+ $view->disableShowAllColumns();
+ // disable the RSS feed
+ $view->disableShowExportAsRssFeed();
+
+ // disable all row actions
+ if ($view instanceof Piwik_ViewDataTable_HtmlTable) {
+ $view->disableRowActions();
+ }
+
+ $view->setReportDocumentation(Piwik_Translate('Live_VisitorLogDocumentation', array('<br />', '<br />')));
+
+ // set a very high row count so that the next link in the footer of the data table is always shown
+ $view->setCustomParameter('totalRows', 10000000);
+
+ $view->setCustomParameter('filterEcommerce', Piwik_Common::getRequestVar('filterEcommerce', 0, 'int'));
+ $view->setCustomParameter('pageUrlNotDefined', Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageURL')));
+
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getLastVisitsStart($fetch = false)
+ {
+ // hack, ensure we load today's visits by default
+ $_GET['date'] = 'today';
+ $_GET['period'] = 'day';
+ $view = Piwik_View::factory('lastVisits');
+ $view->idSite = $this->idSite;
+
+ $api = new Piwik_API_Request("method=Live.getLastVisitsDetails&idSite=$this->idSite&filter_limit=10&format=php&serialize=0&disable_generic_filters=1");
+ $visitors = $api->process();
+ $view->visitors = $visitors;
+
+ return $this->render($view, $fetch);
+ }
+
+ private function setCounters($view)
+ {
+ $segment = Piwik_Common::getRequestVar('segment', false, 'string');
+ $last30min = Piwik_Live_API::getInstance()->getCounters($this->idSite, $lastMinutes = 30, $segment);
+ $last30min = $last30min[0];
+ $today = Piwik_Live_API::getInstance()->getCounters($this->idSite, $lastMinutes = 24 * 60, $segment);
+ $today = $today[0];
+ $view->visitorsCountHalfHour = $last30min['visits'];
+ $view->visitorsCountToday = $today['visits'];
+ $view->pisHalfhour = $last30min['actions'];
+ $view->pisToday = $today['actions'];
+ return $view;
+ }
}
diff --git a/plugins/Live/Live.php b/plugins/Live/Live.php
index 4f51435854..4a505d0ad2 100644
--- a/plugins/Live/Live.php
+++ b/plugins/Live/Live.php
@@ -15,56 +15,56 @@
*/
class Piwik_Live extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('Live_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('Live_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
- function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'WidgetsList.add' => 'addWidget',
- 'Menu.add' => 'addMenu',
- );
- }
+ function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'WidgetsList.add' => 'addWidget',
+ 'Menu.add' => 'addMenu',
+ );
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "plugins/Live/templates/live.css";
- }
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
-
- $jsFiles[] = "plugins/Live/templates/scripts/live.js";
- }
+ $cssFiles[] = "plugins/Live/templates/live.css";
+ }
- function addMenu()
- {
- Piwik_AddMenu('General_Visitors', 'Live_VisitorLog', array('module' => 'Live', 'action' => 'getVisitorLog'), true, $order = 5);
- }
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
- public function addWidget()
- {
- Piwik_AddWidget('Live!', 'Live_VisitorsInRealTime', 'Live', 'widget');
- Piwik_AddWidget('Live!', 'Live_VisitorLog', 'Live', 'getVisitorLog');
- Piwik_AddWidget('Live!', 'Live_RealTimeVisitorCount', 'Live', 'getSimpleLastVisitCount');
- }
+ $jsFiles[] = "plugins/Live/templates/scripts/live.js";
+ }
+
+ function addMenu()
+ {
+ Piwik_AddMenu('General_Visitors', 'Live_VisitorLog', array('module' => 'Live', 'action' => 'getVisitorLog'), true, $order = 5);
+ }
+
+ public function addWidget()
+ {
+ Piwik_AddWidget('Live!', 'Live_VisitorsInRealTime', 'Live', 'widget');
+ Piwik_AddWidget('Live!', 'Live_VisitorLog', 'Live', 'getVisitorLog');
+ Piwik_AddWidget('Live!', 'Live_RealTimeVisitorCount', 'Live', 'getSimpleLastVisitCount');
+ }
}
diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php
index c42ff66233..249a98349d 100644
--- a/plugins/Live/Visitor.php
+++ b/plugins/Live/Visitor.php
@@ -27,518 +27,500 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/Provider/functions.php';
*/
class Piwik_Live_Visitor
{
- const DELIMITER_PLUGIN_NAME = ", ";
-
- function __construct($visitorRawData)
- {
- $this->details = $visitorRawData;
- }
-
- function getAllVisitorDetails()
- {
- return array(
- 'idSite' => $this->getIdSite(),
- 'idVisit' => $this->getIdVisit(),
- 'visitIp' => $this->getIp(),
- 'visitorId' => $this->getVisitorId(),
- 'visitorType' => $this->getVisitorReturning(),
- 'visitorTypeIcon' => $this->getVisitorReturningIcon(),
- 'visitConverted' => $this->isVisitorGoalConverted(),
- 'visitConvertedIcon' => $this->getVisitorGoalConvertedIcon(),
- 'visitEcommerceStatus' => $this->getVisitEcommerceStatus(),
- 'visitEcommerceStatusIcon' => $this->getVisitEcommerceStatusIcon(),
-
- 'searches' => $this->getNumberOfSearches(),
- 'actions' => $this->getNumberOfActions(),
- // => false are placeholders to be filled in API later
- 'actionDetails' => false,
- 'customVariables' => $this->getCustomVariables(),
- 'goalConversions' => false,
- 'siteCurrency' => false,
- 'siteCurrencySymbol' => false,
-
- // all time entries
- 'serverDate' => $this->getServerDate(),
- 'visitLocalTime' => $this->getVisitLocalTime(),
- 'firstActionTimestamp' => $this->getTimestampFirstAction(),
- 'lastActionTimestamp' => $this->getTimestampLastAction(),
- 'lastActionDateTime' => $this->getDateTimeLastAction(),
-
- // standard attributes
- 'visitDuration' => $this->getVisitLength(),
- 'visitDurationPretty' => $this->getVisitLengthPretty(),
- 'visitCount' => $this->getVisitCount(),
- 'daysSinceLastVisit' => $this->getDaysSinceLastVisit(),
- 'daysSinceFirstVisit' => $this->getDaysSinceFirstVisit(),
- 'daysSinceLastEcommerceOrder' => $this->getDaysSinceLastEcommerceOrder(),
- 'continent' => $this->getContinent(),
- 'continentCode' => $this->getContinentCode(),
- 'country' => $this->getCountryName(),
- 'countryCode' => $this->getCountryCode(),
- 'countryFlag' => $this->getCountryFlag(),
- 'region' => $this->getRegionName(),
- 'city' => $this->getCityName(),
- 'location' => $this->getPrettyLocation(),
- 'latitude' => $this->getLatitude(),
- 'longitude' => $this->getLongitude(),
- 'provider' => $this->getProvider(),
- 'providerUrl' => $this->getProviderUrl(),
-
- 'referrerType' => $this->getRefererType(),
- 'referrerTypeName' => $this->getRefererTypeName(),
- 'referrerName' => $this->getRefererName(),
- 'referrerKeyword' => $this->getKeyword(),
- 'referrerKeywordPosition' => $this->getKeywordPosition(),
- 'referrerUrl' => $this->getRefererUrl(),
- 'referrerSearchEngineUrl' => $this->getSearchEngineUrl(),
- 'referrerSearchEngineIcon' => $this->getSearchEngineIcon(),
- 'operatingSystem' => $this->getOperatingSystem(),
- 'operatingSystemShortName' => $this->getOperatingSystemShortName(),
- 'operatingSystemIcon' => $this->getOperatingSystemIcon(),
- 'browserFamily' => $this->getBrowserFamily(),
- 'browserFamilyDescription' => $this->getBrowserFamilyDescription(),
- 'browserName' => $this->getBrowser(),
- 'browserIcon' => $this->getBrowserIcon(),
- 'screenType' => $this->getScreenType(),
- 'resolution' => $this->getResolution(),
- 'screenTypeIcon' => $this->getScreenTypeIcon(),
- 'plugins' => $this->getPlugins(),
- 'pluginsIcons' => $this->getPluginIcons(),
- );
- }
-
- function getVisitorId()
- {
- if(isset($this->details['idvisitor']))
- {
- return bin2hex($this->details['idvisitor']);
- }
- return false;
- }
-
- function getVisitLocalTime()
- {
- return $this->details['visitor_localtime'];
- }
-
- function getVisitCount()
- {
- return $this->details['visitor_count_visits'];
- }
-
- function getDaysSinceLastVisit()
- {
- return $this->details['visitor_days_since_last'];
- }
-
- function getDaysSinceLastEcommerceOrder()
- {
- return $this->details['visitor_days_since_order'];
- }
- function getDaysSinceFirstVisit()
- {
- return $this->details['visitor_days_since_first'];
- }
-
- function getServerDate()
- {
- return date('Y-m-d', strtotime($this->details['visit_last_action_time']));
- }
-
- function getIp()
- {
- if(isset($this->details['location_ip']))
- {
- return Piwik_IP::N2P($this->details['location_ip']);
- }
- return false;
- }
-
- function getIdVisit()
- {
- return $this->details['idvisit'];
- }
-
- function getIdSite()
- {
- return $this->details['idsite'];
- }
-
- function getNumberOfActions()
- {
- return $this->details['visit_total_actions'];
- }
-
- function getNumberOfSearches()
- {
- return $this->details['visit_total_searches'];
- }
-
- function getVisitLength()
- {
- return $this->details['visit_total_time'];
- }
-
- function getVisitLengthPretty()
- {
- return Piwik::getPrettyTimeFromSeconds($this->details['visit_total_time']);
- }
-
- function getVisitorReturning()
- {
- $type = $this->details['visitor_returning'];
- return $type == 2
- ? 'returningCustomer'
- : ($type == 1
- ? 'returning'
- : 'new');
- }
-
- function getVisitorReturningIcon()
- {
- $type = $this->getVisitorReturning();
- if($type == 'returning'
- || $type =='returningCustomer')
- {
- return "plugins/Live/templates/images/returningVisitor.gif";
- }
- return null;
- }
-
- function getTimestampFirstAction()
- {
- return strtotime($this->details['visit_first_action_time']);
- }
-
- function getTimestampLastAction()
- {
- return strtotime($this->details['visit_last_action_time']);
- }
-
- function getCountryCode()
- {
- return $this->details['location_country'];
- }
-
- function getCountryName()
- {
- return Piwik_CountryTranslate($this->getCountryCode());
- }
-
- function getCountryFlag()
- {
- return Piwik_getFlagFromCode($this->getCountryCode());
- }
-
- function getContinent()
- {
- return Piwik_ContinentTranslate($this->getContinentCode());
- }
-
- function getContinentCode()
- {
- return Piwik_Common::getContinent($this->details['location_country']);
- }
-
- function getCityName()
- {
- if (!empty($this->details['location_city']))
- {
- return $this->details['location_city'];
- }
- return null;
- }
-
- public function getRegionName()
- {
- $region = $this->details['location_region'];
- if ($region != '' && $region != Piwik_Tracker_Visit::UNKNOWN_CODE)
- {
- return Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes(
- $this->details['location_country'], $region);
- }
- return null;
- }
-
- function getPrettyLocation()
- {
- $parts = array();
-
- $city = $this->getCityName();
- if(!empty($city)) {
- $parts[] = $city;
- }
- $region = $this->getRegionName();
- if(!empty($region)) {
- $parts[] = $region;
- }
-
- // add country & return concatenated result
- $parts[] = $this->getCountryName();
- return implode(', ', $parts);
- }
-
- function getLatitude()
- {
- if(!empty($this->details['location_latitude'])) {
- return $this->details['location_latitude'];
- }
- return null;
- }
-
- function getLongitude()
- {
- if(!empty($this->details['location_longitude'])) {
- return $this->details['location_longitude'];
- }
- return null;
- }
-
- function getCustomVariables()
- {
- $customVariables = array();
- for($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++)
- {
- if(!empty($this->details['custom_var_k'.$i]))
- {
- $customVariables[$i] = array(
- 'customVariableName'.$i => $this->details['custom_var_k'.$i],
- 'customVariableValue'.$i => $this->details['custom_var_v'.$i],
- );
- }
- }
- return $customVariables;
- }
-
- function getRefererType()
- {
- return Piwik_getRefererTypeFromShortName($this->details['referer_type']);
- }
-
- function getRefererTypeName()
- {
- return Piwik_getRefererTypeLabel($this->details['referer_type']);
- }
-
- function getKeyword()
- {
- $keyword = $this->details['referer_keyword'];
- if(Piwik_PluginsManager::getInstance()->isPluginActivated('Referers')
- && $this->getRefererType() == 'search')
- {
- $keyword = Piwik_Referers::getCleanKeyword($keyword);
- }
- return urldecode($keyword);
- }
-
- function getRefererUrl()
- {
- if($this->getRefererType() == 'search')
- {
- if(Piwik_PluginsManager::getInstance()->isPluginActivated('Referers')
- && $this->details['referer_keyword'] == Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED)
- {
- return 'http://piwik.org/faq/general/#faq_144';
- }
- // Case URL is google.XX/url.... then we rewrite to the search result page url
- elseif($this->getRefererName() == 'Google'
- && strpos($this->details['referer_url'], '/url'))
- {
- $refUrl = @parse_url($this->details['referer_url']);
- if(isset($refUrl['host']))
- {
- $url = Piwik_getSearchEngineUrlFromUrlAndKeyword('http://google.com', $this->getKeyword());
- $url = str_replace('google.com', $refUrl['host'], $url);
- return $url;
- }
- }
- }
- if(Piwik_Common::isLookLikeUrl($this->details['referer_url']))
- {
- return $this->details['referer_url'];
- }
- return null;
- }
-
- function getKeywordPosition()
- {
- if($this->getRefererType() == 'search'
- && strpos($this->getRefererName(), 'Google') !== false)
- {
- $url = @parse_url($this->details['referer_url']);
- if(empty($url['query']))
- {
- return null;
- }
- $position = Piwik_Common::getParameterFromQueryString($url['query'], 'cd');
- if(!empty($position))
- {
- return $position;
- }
- }
- return null;
- }
-
- function getRefererName()
- {
- return urldecode($this->details['referer_name']);
- }
-
- function getSearchEngineUrl()
- {
- if($this->getRefererType() == 'search'
- && !empty($this->details['referer_name']))
- {
- return Piwik_getSearchEngineUrlFromName($this->details['referer_name']);
- }
- return null;
- }
-
- function getSearchEngineIcon()
- {
- $searchEngineUrl = $this->getSearchEngineUrl();
- if( !is_null($searchEngineUrl) )
- {
- return Piwik_getSearchEngineLogoFromUrl($searchEngineUrl);
- }
- return null;
- }
-
- function getPlugins()
- {
- $plugins = array(
- 'config_pdf',
- 'config_flash',
- 'config_java',
- 'config_director',
- 'config_quicktime',
- 'config_realplayer',
- 'config_windowsmedia',
- 'config_gears',
- 'config_silverlight',
- );
- $pluginShortNames = array();
- foreach($plugins as $plugin)
- {
- if($this->details[$plugin] == 1)
- {
- $pluginShortName = substr($plugin, 7);
- $pluginShortNames[] = $pluginShortName;
- }
- }
- return implode(self::DELIMITER_PLUGIN_NAME, $pluginShortNames);
- }
-
- function getPluginIcons()
- {
- $pluginNames = $this->getPlugins();
- if( !empty($pluginNames) )
- {
- $pluginNames = explode(self::DELIMITER_PLUGIN_NAME, $pluginNames);
- $pluginIcons = array();
-
- foreach($pluginNames as $plugin) {
- $pluginIcons[] = array("pluginIcon" =>Piwik_getPluginsLogo($plugin), "pluginName" =>$plugin);
- }
- return $pluginIcons;
- }
- return null;
- }
-
- function getOperatingSystem()
- {
- return Piwik_getOSLabel($this->details['config_os']);
- }
-
- function getOperatingSystemShortName()
- {
- return Piwik_getOSShortLabel($this->details['config_os']);
- }
-
- function getOperatingSystemIcon()
- {
- return Piwik_getOSLogo($this->details['config_os']);
- }
-
- function getBrowserFamilyDescription()
- {
- return Piwik_getBrowserTypeLabel($this->getBrowserFamily());
- }
-
- function getBrowserFamily()
- {
- return Piwik_getBrowserFamily($this->details['config_browser_name']);
- }
-
- function getBrowser()
- {
- return Piwik_getBrowserLabel($this->details['config_browser_name'] . ";" . $this->details['config_browser_version']);
- }
-
- function getBrowserIcon()
- {
- return Piwik_getBrowsersLogo($this->details['config_browser_name'] . ";" . $this->details['config_browser_version']);
- }
-
- function getScreenType()
- {
- return Piwik_getScreenTypeFromResolution($this->details['config_resolution']);
- }
-
- function getResolution()
- {
- return $this->details['config_resolution'];
- }
-
- function getScreenTypeIcon()
- {
- return Piwik_getScreensLogo($this->getScreenType());
- }
-
- function getProvider()
- {
- return Piwik_getHostnameName( @$this->details['location_provider']);
- }
-
- function getProviderUrl()
- {
- return Piwik_getHostnameUrl( @$this->details['location_provider']);
- }
-
- function getDateTimeLastAction()
- {
- return date('Y-m-d H:i:s', strtotime($this->details['visit_last_action_time']));
- }
-
- function getVisitEcommerceStatusIcon()
- {
- $status = $this->getVisitEcommerceStatus();
-
- if(in_array($status, array('ordered', 'orderedThenAbandonedCart')))
- {
- return "themes/default/images/ecommerceOrder.gif";
- }
- elseif($status == 'abandonedCart')
- {
- return "themes/default/images/ecommerceAbandonedCart.gif";
- }
- return null;
- }
-
- function getVisitEcommerceStatus()
- {
- return Piwik_API_API::getVisitEcommerceStatusFromId($this->details['visit_goal_buyer']);
- }
-
- function getVisitorGoalConvertedIcon()
- {
- return $this->isVisitorGoalConverted()
- ? "themes/default/images/goal.png"
- : null;
- }
-
- function isVisitorGoalConverted()
- {
- return $this->details['visit_goal_converted'];
- }
+ const DELIMITER_PLUGIN_NAME = ", ";
+
+ function __construct($visitorRawData)
+ {
+ $this->details = $visitorRawData;
+ }
+
+ function getAllVisitorDetails()
+ {
+ return array(
+ 'idSite' => $this->getIdSite(),
+ 'idVisit' => $this->getIdVisit(),
+ 'visitIp' => $this->getIp(),
+ 'visitorId' => $this->getVisitorId(),
+ 'visitorType' => $this->getVisitorReturning(),
+ 'visitorTypeIcon' => $this->getVisitorReturningIcon(),
+ 'visitConverted' => $this->isVisitorGoalConverted(),
+ 'visitConvertedIcon' => $this->getVisitorGoalConvertedIcon(),
+ 'visitEcommerceStatus' => $this->getVisitEcommerceStatus(),
+ 'visitEcommerceStatusIcon' => $this->getVisitEcommerceStatusIcon(),
+
+ 'searches' => $this->getNumberOfSearches(),
+ 'actions' => $this->getNumberOfActions(),
+ // => false are placeholders to be filled in API later
+ 'actionDetails' => false,
+ 'customVariables' => $this->getCustomVariables(),
+ 'goalConversions' => false,
+ 'siteCurrency' => false,
+ 'siteCurrencySymbol' => false,
+
+ // all time entries
+ 'serverDate' => $this->getServerDate(),
+ 'visitLocalTime' => $this->getVisitLocalTime(),
+ 'firstActionTimestamp' => $this->getTimestampFirstAction(),
+ 'lastActionTimestamp' => $this->getTimestampLastAction(),
+ 'lastActionDateTime' => $this->getDateTimeLastAction(),
+
+ // standard attributes
+ 'visitDuration' => $this->getVisitLength(),
+ 'visitDurationPretty' => $this->getVisitLengthPretty(),
+ 'visitCount' => $this->getVisitCount(),
+ 'daysSinceLastVisit' => $this->getDaysSinceLastVisit(),
+ 'daysSinceFirstVisit' => $this->getDaysSinceFirstVisit(),
+ 'daysSinceLastEcommerceOrder' => $this->getDaysSinceLastEcommerceOrder(),
+ 'continent' => $this->getContinent(),
+ 'continentCode' => $this->getContinentCode(),
+ 'country' => $this->getCountryName(),
+ 'countryCode' => $this->getCountryCode(),
+ 'countryFlag' => $this->getCountryFlag(),
+ 'region' => $this->getRegionName(),
+ 'city' => $this->getCityName(),
+ 'location' => $this->getPrettyLocation(),
+ 'latitude' => $this->getLatitude(),
+ 'longitude' => $this->getLongitude(),
+ 'provider' => $this->getProvider(),
+ 'providerUrl' => $this->getProviderUrl(),
+
+ 'referrerType' => $this->getRefererType(),
+ 'referrerTypeName' => $this->getRefererTypeName(),
+ 'referrerName' => $this->getRefererName(),
+ 'referrerKeyword' => $this->getKeyword(),
+ 'referrerKeywordPosition' => $this->getKeywordPosition(),
+ 'referrerUrl' => $this->getRefererUrl(),
+ 'referrerSearchEngineUrl' => $this->getSearchEngineUrl(),
+ 'referrerSearchEngineIcon' => $this->getSearchEngineIcon(),
+ 'operatingSystem' => $this->getOperatingSystem(),
+ 'operatingSystemShortName' => $this->getOperatingSystemShortName(),
+ 'operatingSystemIcon' => $this->getOperatingSystemIcon(),
+ 'browserFamily' => $this->getBrowserFamily(),
+ 'browserFamilyDescription' => $this->getBrowserFamilyDescription(),
+ 'browserName' => $this->getBrowser(),
+ 'browserIcon' => $this->getBrowserIcon(),
+ 'screenType' => $this->getScreenType(),
+ 'resolution' => $this->getResolution(),
+ 'screenTypeIcon' => $this->getScreenTypeIcon(),
+ 'plugins' => $this->getPlugins(),
+ 'pluginsIcons' => $this->getPluginIcons(),
+ );
+ }
+
+ function getVisitorId()
+ {
+ if (isset($this->details['idvisitor'])) {
+ return bin2hex($this->details['idvisitor']);
+ }
+ return false;
+ }
+
+ function getVisitLocalTime()
+ {
+ return $this->details['visitor_localtime'];
+ }
+
+ function getVisitCount()
+ {
+ return $this->details['visitor_count_visits'];
+ }
+
+ function getDaysSinceLastVisit()
+ {
+ return $this->details['visitor_days_since_last'];
+ }
+
+ function getDaysSinceLastEcommerceOrder()
+ {
+ return $this->details['visitor_days_since_order'];
+ }
+
+ function getDaysSinceFirstVisit()
+ {
+ return $this->details['visitor_days_since_first'];
+ }
+
+ function getServerDate()
+ {
+ return date('Y-m-d', strtotime($this->details['visit_last_action_time']));
+ }
+
+ function getIp()
+ {
+ if (isset($this->details['location_ip'])) {
+ return Piwik_IP::N2P($this->details['location_ip']);
+ }
+ return false;
+ }
+
+ function getIdVisit()
+ {
+ return $this->details['idvisit'];
+ }
+
+ function getIdSite()
+ {
+ return $this->details['idsite'];
+ }
+
+ function getNumberOfActions()
+ {
+ return $this->details['visit_total_actions'];
+ }
+
+ function getNumberOfSearches()
+ {
+ return $this->details['visit_total_searches'];
+ }
+
+ function getVisitLength()
+ {
+ return $this->details['visit_total_time'];
+ }
+
+ function getVisitLengthPretty()
+ {
+ return Piwik::getPrettyTimeFromSeconds($this->details['visit_total_time']);
+ }
+
+ function getVisitorReturning()
+ {
+ $type = $this->details['visitor_returning'];
+ return $type == 2
+ ? 'returningCustomer'
+ : ($type == 1
+ ? 'returning'
+ : 'new');
+ }
+
+ function getVisitorReturningIcon()
+ {
+ $type = $this->getVisitorReturning();
+ if ($type == 'returning'
+ || $type == 'returningCustomer'
+ ) {
+ return "plugins/Live/templates/images/returningVisitor.gif";
+ }
+ return null;
+ }
+
+ function getTimestampFirstAction()
+ {
+ return strtotime($this->details['visit_first_action_time']);
+ }
+
+ function getTimestampLastAction()
+ {
+ return strtotime($this->details['visit_last_action_time']);
+ }
+
+ function getCountryCode()
+ {
+ return $this->details['location_country'];
+ }
+
+ function getCountryName()
+ {
+ return Piwik_CountryTranslate($this->getCountryCode());
+ }
+
+ function getCountryFlag()
+ {
+ return Piwik_getFlagFromCode($this->getCountryCode());
+ }
+
+ function getContinent()
+ {
+ return Piwik_ContinentTranslate($this->getContinentCode());
+ }
+
+ function getContinentCode()
+ {
+ return Piwik_Common::getContinent($this->details['location_country']);
+ }
+
+ function getCityName()
+ {
+ if (!empty($this->details['location_city'])) {
+ return $this->details['location_city'];
+ }
+ return null;
+ }
+
+ public function getRegionName()
+ {
+ $region = $this->details['location_region'];
+ if ($region != '' && $region != Piwik_Tracker_Visit::UNKNOWN_CODE) {
+ return Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes(
+ $this->details['location_country'], $region);
+ }
+ return null;
+ }
+
+ function getPrettyLocation()
+ {
+ $parts = array();
+
+ $city = $this->getCityName();
+ if (!empty($city)) {
+ $parts[] = $city;
+ }
+ $region = $this->getRegionName();
+ if (!empty($region)) {
+ $parts[] = $region;
+ }
+
+ // add country & return concatenated result
+ $parts[] = $this->getCountryName();
+ return implode(', ', $parts);
+ }
+
+ function getLatitude()
+ {
+ if (!empty($this->details['location_latitude'])) {
+ return $this->details['location_latitude'];
+ }
+ return null;
+ }
+
+ function getLongitude()
+ {
+ if (!empty($this->details['location_longitude'])) {
+ return $this->details['location_longitude'];
+ }
+ return null;
+ }
+
+ function getCustomVariables()
+ {
+ $customVariables = array();
+ for ($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++) {
+ if (!empty($this->details['custom_var_k' . $i])) {
+ $customVariables[$i] = array(
+ 'customVariableName' . $i => $this->details['custom_var_k' . $i],
+ 'customVariableValue' . $i => $this->details['custom_var_v' . $i],
+ );
+ }
+ }
+ return $customVariables;
+ }
+
+ function getRefererType()
+ {
+ return Piwik_getRefererTypeFromShortName($this->details['referer_type']);
+ }
+
+ function getRefererTypeName()
+ {
+ return Piwik_getRefererTypeLabel($this->details['referer_type']);
+ }
+
+ function getKeyword()
+ {
+ $keyword = $this->details['referer_keyword'];
+ if (Piwik_PluginsManager::getInstance()->isPluginActivated('Referers')
+ && $this->getRefererType() == 'search'
+ ) {
+ $keyword = Piwik_Referers::getCleanKeyword($keyword);
+ }
+ return urldecode($keyword);
+ }
+
+ function getRefererUrl()
+ {
+ if ($this->getRefererType() == 'search') {
+ if (Piwik_PluginsManager::getInstance()->isPluginActivated('Referers')
+ && $this->details['referer_keyword'] == Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED
+ ) {
+ return 'http://piwik.org/faq/general/#faq_144';
+ } // Case URL is google.XX/url.... then we rewrite to the search result page url
+ elseif ($this->getRefererName() == 'Google'
+ && strpos($this->details['referer_url'], '/url')
+ ) {
+ $refUrl = @parse_url($this->details['referer_url']);
+ if (isset($refUrl['host'])) {
+ $url = Piwik_getSearchEngineUrlFromUrlAndKeyword('http://google.com', $this->getKeyword());
+ $url = str_replace('google.com', $refUrl['host'], $url);
+ return $url;
+ }
+ }
+ }
+ if (Piwik_Common::isLookLikeUrl($this->details['referer_url'])) {
+ return $this->details['referer_url'];
+ }
+ return null;
+ }
+
+ function getKeywordPosition()
+ {
+ if ($this->getRefererType() == 'search'
+ && strpos($this->getRefererName(), 'Google') !== false
+ ) {
+ $url = @parse_url($this->details['referer_url']);
+ if (empty($url['query'])) {
+ return null;
+ }
+ $position = Piwik_Common::getParameterFromQueryString($url['query'], 'cd');
+ if (!empty($position)) {
+ return $position;
+ }
+ }
+ return null;
+ }
+
+ function getRefererName()
+ {
+ return urldecode($this->details['referer_name']);
+ }
+
+ function getSearchEngineUrl()
+ {
+ if ($this->getRefererType() == 'search'
+ && !empty($this->details['referer_name'])
+ ) {
+ return Piwik_getSearchEngineUrlFromName($this->details['referer_name']);
+ }
+ return null;
+ }
+
+ function getSearchEngineIcon()
+ {
+ $searchEngineUrl = $this->getSearchEngineUrl();
+ if (!is_null($searchEngineUrl)) {
+ return Piwik_getSearchEngineLogoFromUrl($searchEngineUrl);
+ }
+ return null;
+ }
+
+ function getPlugins()
+ {
+ $plugins = array(
+ 'config_pdf',
+ 'config_flash',
+ 'config_java',
+ 'config_director',
+ 'config_quicktime',
+ 'config_realplayer',
+ 'config_windowsmedia',
+ 'config_gears',
+ 'config_silverlight',
+ );
+ $pluginShortNames = array();
+ foreach ($plugins as $plugin) {
+ if ($this->details[$plugin] == 1) {
+ $pluginShortName = substr($plugin, 7);
+ $pluginShortNames[] = $pluginShortName;
+ }
+ }
+ return implode(self::DELIMITER_PLUGIN_NAME, $pluginShortNames);
+ }
+
+ function getPluginIcons()
+ {
+ $pluginNames = $this->getPlugins();
+ if (!empty($pluginNames)) {
+ $pluginNames = explode(self::DELIMITER_PLUGIN_NAME, $pluginNames);
+ $pluginIcons = array();
+
+ foreach ($pluginNames as $plugin) {
+ $pluginIcons[] = array("pluginIcon" => Piwik_getPluginsLogo($plugin), "pluginName" => $plugin);
+ }
+ return $pluginIcons;
+ }
+ return null;
+ }
+
+ function getOperatingSystem()
+ {
+ return Piwik_getOSLabel($this->details['config_os']);
+ }
+
+ function getOperatingSystemShortName()
+ {
+ return Piwik_getOSShortLabel($this->details['config_os']);
+ }
+
+ function getOperatingSystemIcon()
+ {
+ return Piwik_getOSLogo($this->details['config_os']);
+ }
+
+ function getBrowserFamilyDescription()
+ {
+ return Piwik_getBrowserTypeLabel($this->getBrowserFamily());
+ }
+
+ function getBrowserFamily()
+ {
+ return Piwik_getBrowserFamily($this->details['config_browser_name']);
+ }
+
+ function getBrowser()
+ {
+ return Piwik_getBrowserLabel($this->details['config_browser_name'] . ";" . $this->details['config_browser_version']);
+ }
+
+ function getBrowserIcon()
+ {
+ return Piwik_getBrowsersLogo($this->details['config_browser_name'] . ";" . $this->details['config_browser_version']);
+ }
+
+ function getScreenType()
+ {
+ return Piwik_getScreenTypeFromResolution($this->details['config_resolution']);
+ }
+
+ function getResolution()
+ {
+ return $this->details['config_resolution'];
+ }
+
+ function getScreenTypeIcon()
+ {
+ return Piwik_getScreensLogo($this->getScreenType());
+ }
+
+ function getProvider()
+ {
+ return Piwik_getHostnameName(@$this->details['location_provider']);
+ }
+
+ function getProviderUrl()
+ {
+ return Piwik_getHostnameUrl(@$this->details['location_provider']);
+ }
+
+ function getDateTimeLastAction()
+ {
+ return date('Y-m-d H:i:s', strtotime($this->details['visit_last_action_time']));
+ }
+
+ function getVisitEcommerceStatusIcon()
+ {
+ $status = $this->getVisitEcommerceStatus();
+
+ if (in_array($status, array('ordered', 'orderedThenAbandonedCart'))) {
+ return "themes/default/images/ecommerceOrder.gif";
+ } elseif ($status == 'abandonedCart') {
+ return "themes/default/images/ecommerceAbandonedCart.gif";
+ }
+ return null;
+ }
+
+ function getVisitEcommerceStatus()
+ {
+ return Piwik_API_API::getVisitEcommerceStatusFromId($this->details['visit_goal_buyer']);
+ }
+
+ function getVisitorGoalConvertedIcon()
+ {
+ return $this->isVisitorGoalConverted()
+ ? "themes/default/images/goal.png"
+ : null;
+ }
+
+ function isVisitorGoalConverted()
+ {
+ return $this->details['visit_goal_converted'];
+ }
}
diff --git a/plugins/Live/templates/index.tpl b/plugins/Live/templates/index.tpl
index d839c6bf33..13e8eb8d92 100644
--- a/plugins/Live/templates/index.tpl
+++ b/plugins/Live/templates/index.tpl
@@ -1,29 +1,29 @@
{literal}
<script type="text/javascript" charset="utf-8">
-$(document).ready(function() {
- $('#visitsLive').liveWidget({
- interval: {/literal}{$liveRefreshAfterMs}{literal},
- onUpdate: function(){
- //updates the numbers of total visits in startbox
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.setFormat('html');
- ajaxRequest.addParams({
+ $(document).ready(function () {
+ $('#visitsLive').liveWidget({
+ interval: {/literal}{$liveRefreshAfterMs}{literal},
+ onUpdate: function () {
+ //updates the numbers of total visits in startbox
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.setFormat('html');
+ ajaxRequest.addParams({
+ module: 'Live',
+ action: 'ajaxTotalVisitors'
+ }, 'GET');
+ ajaxRequest.setCallback(function (r) {
+ $("#visitsTotal").html(r);
+ });
+ ajaxRequest.send(false);
+ },
+ maxRows: 10,
+ fadeInSpeed: 600,
+ dataUrlParams: {
module: 'Live',
- action: 'ajaxTotalVisitors'
- }, 'GET');
- ajaxRequest.setCallback(function (r) {
- $("#visitsTotal").html(r);
- });
- ajaxRequest.send(false);
- },
- maxRows: 10,
- fadeInSpeed: 600,
- dataUrlParams: {
- module: 'Live',
- action: 'getLastVisitsStart'
- }
+ action: 'getLastVisitsStart'
+ }
+ });
});
-});
</script>
{/literal}
@@ -32,9 +32,11 @@ $(document).ready(function() {
{$visitors}
<div class="visitsLiveFooter">
- <a title="Pause Live!" href="javascript:void(0);" onclick="onClickPause();"><img id="pauseImage" border="0" src="plugins/Live/templates/images/pause_disabled.gif" /></a>
- <a title="Start Live!" href="javascript:void(0);" onclick="onClickPlay();"><img id="playImage" border="0" src="plugins/Live/templates/images/play.gif" /></a>
- {if !$disableLink}
- &nbsp; <a class="rightLink" href="javascript:broadcast.propagateAjax('module=Live&action=getVisitorLog')">{'Live_LinkVisitorLog'|translate}</a>
- {/if}
+ <a title="Pause Live!" href="javascript:void(0);" onclick="onClickPause();"><img id="pauseImage" border="0"
+ src="plugins/Live/templates/images/pause_disabled.gif"/></a>
+ <a title="Start Live!" href="javascript:void(0);" onclick="onClickPlay();"><img id="playImage" border="0" src="plugins/Live/templates/images/play.gif"/></a>
+ {if !$disableLink}
+ &nbsp;
+ <a class="rightLink" href="javascript:broadcast.propagateAjax('module=Live&action=getVisitorLog')">{'Live_LinkVisitorLog'|translate}</a>
+ {/if}
</div>
diff --git a/plugins/Live/templates/lastVisits.tpl b/plugins/Live/templates/lastVisits.tpl
index 5fb028978a..63a26afe6b 100644
--- a/plugins/Live/templates/lastVisits.tpl
+++ b/plugins/Live/templates/lastVisits.tpl
@@ -2,77 +2,90 @@
{assign var=maxPagesDisplayedByVisitor value=100}
<ul id='visitsLive'>
-{foreach from=$visitors item=visitor}
- <li id="{$visitor.idVisit}" class="visit">
- <div style="display:none" class="idvisit">{$visitor.idVisit}</div>
- <div title="{$visitor.actionDetails|@count} {'Live_Actions'|translate}" class="datetime">
- <span style='display:none' class='serverTimestamp'>{$visitor.serverTimestamp}</span>
- {$visitor.serverDatePretty} - {$visitor.serverTimePretty} {if $visitor.visitDuration > 0}<i>({$visitor.visitDurationPretty})</i>{/if}
- &nbsp;<img src="{$visitor.countryFlag}" title="{$visitor.location|escape:'html'}, {'Provider_ColumnProvider'|translate} {$visitor.provider}" />
- &nbsp;<img src="{$visitor.browserIcon}" title="{$visitor.browserName}, {'UserSettings_Plugins'|translate}: {$visitor.plugins}" />
- &nbsp;<img src="{$visitor.operatingSystemIcon}" title="{$visitor.operatingSystem}, {$visitor.resolution}" />
- &nbsp;
- {if $visitor.visitConverted}
- <span title="{'General_VisitConvertedNGoals'|translate:$visitor.goalConversions}" class='visitorRank'>
- <img src="{$visitor.visitConvertedIcon}" />
- <span class='hash'>#</span>{$visitor.goalConversions}
- {if $visitor.visitEcommerceStatusIcon}
- &nbsp;- <img src="{$visitor.visitEcommerceStatusIcon}" title="{$visitor.visitEcommerceStatus}"/>
- {/if}
- </span>{/if}
- {if $visitor.visitorTypeIcon}
- <a class="rightLink" href="javascript:broadcast.propagateAjax('module=Live&action=getVisitorLog&period=month&segment=visitorId=={$visitor.visitorId}')">
- &nbsp;- <img src="{$visitor.visitorTypeIcon}" title="{'General_ReturningVisitor'|translate} - {'General_ReturningVisitorAllVisits'|translate}" />
- </a>
- {/if}
- {if $visitor.visitIp}- <span title="{if !empty($visitor.visitorId)}{'General_VisitorID'|translate}: {$visitor.visitorId}{/if}">IP: {$visitor.visitIp}</span>{/if}
- </div>
- <!--<div class="settings"></div>-->
- <div class="referer">
- {if $visitor.referrerType != 'direct'}{'General_FromReferrer'|translate} {if !empty($visitor.referrerUrl)}<a href="{$visitor.referrerUrl|escape:'html'}" target="_blank">{/if}{if !empty($visitor.searchEngineIcon)}<img src="{$visitor.searchEngineIcon}" /> {/if}{$visitor.referrerName|escape:'html'}{if !empty($visitor.referrerUrl)}</a>{/if}
- {if !empty($visitor.referrerKeyword)} - "{$visitor.referrerKeyword|escape:'html'}"{/if}
- {capture assign='keyword'}{$visitor.referrerKeyword|escape:'html'}{/capture}
- {capture assign='searchName'}{$visitor.referrerName|escape:"html"}{/capture}
- {capture assign='position'}#{$visitor.referrerKeywordPosition}{/capture}
- {if !empty($visitor.referrerKeywordPosition)}<span title='{'Live_KeywordRankedOnSearchResultForThisVisitor'|translate:$keyword:$position:$searchName}' class='visitorRank'><span class='hash'>#</span>{$visitor.referrerKeywordPosition}</span>{/if}
- {else}{'Referers_DirectEntry'|translate}{/if}
- </div>
- <div id="{$visitor.idVisit}_actions" class="settings">
- <span class="pagesTitle" title="{$visitor.actionDetails|@count} {'Live_Actions'|translate}" >{'Actions_SubmenuPages'|translate}:</span>&nbsp;
- {php} $col = 0; {/php}
- {foreach from=$visitor.actionDetails item=action name=visitorPages}
- {if $smarty.foreach.visitorPages.iteration <= $maxPagesDisplayedByVisitor}
- {if $action.type == 'ecommerceOrder' || $action.type == 'ecommerceAbandonedCart'}
- <span title="
+ {foreach from=$visitors item=visitor}
+ <li id="{$visitor.idVisit}" class="visit">
+ <div style="display:none" class="idvisit">{$visitor.idVisit}</div>
+ <div title="{$visitor.actionDetails|@count} {'Live_Actions'|translate}" class="datetime">
+ <span style='display:none' class='serverTimestamp'>{$visitor.serverTimestamp}</span>
+ {$visitor.serverDatePretty} - {$visitor.serverTimePretty} {if $visitor.visitDuration > 0}<i>({$visitor.visitDurationPretty})</i>{/if}
+ &nbsp;<img src="{$visitor.countryFlag}" title="{$visitor.location|escape:'html'}, {'Provider_ColumnProvider'|translate} {$visitor.provider}"/>
+ &nbsp;<img src="{$visitor.browserIcon}" title="{$visitor.browserName}, {'UserSettings_Plugins'|translate}: {$visitor.plugins}"/>
+ &nbsp;<img src="{$visitor.operatingSystemIcon}" title="{$visitor.operatingSystem}, {$visitor.resolution}"/>
+ &nbsp;
+ {if $visitor.visitConverted}
+ <span title="{'General_VisitConvertedNGoals'|translate:$visitor.goalConversions}" class='visitorRank'>
+ <img src="{$visitor.visitConvertedIcon}"/>
+ <span class='hash'>#</span>
+ {$visitor.goalConversions}
+ {if $visitor.visitEcommerceStatusIcon}
+ &nbsp;-
+ <img src="{$visitor.visitEcommerceStatusIcon}" title="{$visitor.visitEcommerceStatus}"/>
+ {/if}
+ </span>{/if}
+ {if $visitor.visitorTypeIcon}
+ <a class="rightLink"
+ href="javascript:broadcast.propagateAjax('module=Live&action=getVisitorLog&period=month&segment=visitorId=={$visitor.visitorId}')">
+ &nbsp;- <img src="{$visitor.visitorTypeIcon}"
+ title="{'General_ReturningVisitor'|translate} - {'General_ReturningVisitorAllVisits'|translate}"/>
+ </a>
+ {/if}
+ {if $visitor.visitIp}- <span title="{if !empty($visitor.visitorId)}{'General_VisitorID'|translate}: {$visitor.visitorId}{/if}">
+ IP: {$visitor.visitIp}</span>{/if}
+ </div>
+ <!--<div class="settings"></div>-->
+ <div class="referer">
+ {if $visitor.referrerType != 'direct'}{'General_FromReferrer'|translate} {if !empty($visitor.referrerUrl)}<a href="{$visitor.referrerUrl|escape:'html'}" target="_blank">{/if}{if !empty($visitor.searchEngineIcon)}
+ <img src="{$visitor.searchEngineIcon}" /> {/if}{$visitor.referrerName|escape:'html'}{if !empty($visitor.referrerUrl)}</a>{/if}
+ {if !empty($visitor.referrerKeyword)} - "{$visitor.referrerKeyword|escape:'html'}"{/if}
+ {capture assign='keyword'}{$visitor.referrerKeyword|escape:'html'}{/capture}
+ {capture assign='searchName'}{$visitor.referrerName|escape:"html"}{/capture}
+ {capture assign='position'}#{$visitor.referrerKeywordPosition}{/capture}
+ {if !empty($visitor.referrerKeywordPosition)}<span
+ title='{'Live_KeywordRankedOnSearchResultForThisVisitor'|translate:$keyword:$position:$searchName}' class='visitorRank'>
+ <span class='hash'>#</span>
+ {$visitor.referrerKeywordPosition}</span>{/if}
+ {else}{'Referers_DirectEntry'|translate}{/if}
+ </div>
+ <div id="{$visitor.idVisit}_actions" class="settings">
+ <span class="pagesTitle" title="{$visitor.actionDetails|@count} {'Live_Actions'|translate}">{'Actions_SubmenuPages'|translate}:</span>&nbsp;
+ {php} $col = 0; {/php}
+ {foreach from=$visitor.actionDetails item=action name=visitorPages}
+ {if $smarty.foreach.visitorPages.iteration <= $maxPagesDisplayedByVisitor}
+ {if $action.type == 'ecommerceOrder' || $action.type == 'ecommerceAbandonedCart'}
+ <span title="
{if $action.type == 'ecommerceOrder'}{'Goals_EcommerceOrder'|translate}{else}{'Goals_AbandonedCart'|translate}{/if}
- {if $action.type == 'ecommerceOrder'}{'Live_GoalRevenue'|translate}: {else}{capture assign='revenueLeft'}{'Live_GoalRevenue'|translate}{/capture}{'Goals_LeftInCart'|translate:$revenueLeft}: {/if}{$action.revenue|money:$idSite}
- {$action.serverTimePretty|escape:'html'}
{if !empty($action.itemDetails)}{foreach from=$action.itemDetails item=product}
# {$product.itemSKU}{if !empty($product.itemName)}: {$product.itemName}{/if}{if !empty($product.itemCategory)} ({$product.itemCategory}){/if}, {'General_Quantity'|translate}: {$product.quantity}, {'General_Price'|translate}: {$product.price|money:$idSite}
{/foreach}{/if}">
- <img class='iconPadding' src="{$action.icon }" />
- {if $action.type == 'ecommerceOrder'}{'Live_GoalRevenue'|translate}: {$action.revenue|money:$idSite} {/if}
+ <img class='iconPadding' src="{$action.icon }"/>
+ {if $action.type == 'ecommerceOrder'}{'Live_GoalRevenue'|translate}: {$action.revenue|money:$idSite} {/if}
</span>
- {else}
- {php}$col++; if ($col>=9) { $col=0; }{/php}
- <a href="{$action.url|escape:'html'}" target="_blank">
- {if $action.type == 'action'}
- <img src="plugins/Live/templates/images/file{php} echo $col; {/php}.png" title="{if !empty($action.pageTitle)}{$action.pageTitle}{/if} - {$action.serverTimePretty|escape:'html'}{if isset($action.timeSpentPretty)} - {'General_TimeOnPage'|translate}: {$action.timeSpentPretty}{/if}" />
- {elseif $action.type == 'outlink' || $action.type == 'download'}
- <img class='iconPadding' src="{$action.icon}" title="{$action.url|escape:'html'} - {$action.serverTimePretty|escape:'html'}" />
- {elseif $action.type == 'search'}
- <img class='iconPadding' src="{$action.icon}" title="{'Actions_SubmenuSitesearch'|translate|escape:'html'}: {$action.pageTitle|escape:'html'} - {$action.serverTimePretty|escape:'html'}" />
- {else}
- <img class='iconPadding' src="{$action.icon}" title="{$action.goalName|escape:'html'} - {if $action.revenue > 0}{'Live_GoalRevenue'|translate}: {$action.revenue|money:$idSite} - {/if} {$action.serverTimePretty|escape:'html'}" />
- {/if}
- </a>
- {/if}
- {/if}
- {/foreach}
- {if $smarty.foreach.visitorPages.iteration > $maxPagesDisplayedByVisitor}
- <i>({'Live_MorePagesNotDisplayed'|translate})</i>
- {/if}
- </div>
- </li>
-{/foreach}
+ {else}
+ {php}$col++; if ($col>=9) { $col=0; }{/php}
+ <a href="{$action.url|escape:'html'}" target="_blank">
+ {if $action.type == 'action'}
+ <img src="plugins/Live/templates/images/file{php} echo $col; {/php}.png"
+ title="{if !empty($action.pageTitle)}{$action.pageTitle}{/if} - {$action.serverTimePretty|escape:'html'}{if isset($action.timeSpentPretty)} - {'General_TimeOnPage'|translate}: {$action.timeSpentPretty}{/if}"/>
+ {elseif $action.type == 'outlink' || $action.type == 'download'}
+ <img class='iconPadding' src="{$action.icon}"
+ title="{$action.url|escape:'html'} - {$action.serverTimePretty|escape:'html'}"/>
+ {elseif $action.type == 'search'}
+ <img class='iconPadding' src="{$action.icon}"
+ title="{'Actions_SubmenuSitesearch'|translate|escape:'html'}: {$action.pageTitle|escape:'html'} - {$action.serverTimePretty|escape:'html'}"/>
+ {else}
+ <img class='iconPadding' src="{$action.icon}"
+ title="{$action.goalName|escape:'html'} - {if $action.revenue > 0}{'Live_GoalRevenue'|translate}: {$action.revenue|money:$idSite} - {/if} {$action.serverTimePretty|escape:'html'}"/>
+ {/if}
+ </a>
+ {/if}
+ {/if}
+ {/foreach}
+ {if $smarty.foreach.visitorPages.iteration > $maxPagesDisplayedByVisitor}
+ <i>({'Live_MorePagesNotDisplayed'|translate})</i>
+ {/if}
+ </div>
+ </li>
+ {/foreach}
</ul>
diff --git a/plugins/Live/templates/live.css b/plugins/Live/templates/live.css
index e4fab30625..43ef43e2bd 100644
--- a/plugins/Live/templates/live.css
+++ b/plugins/Live/templates/live.css
@@ -1,99 +1,123 @@
#visitsLive {
- text-align:left;
- font-size:90%;
- color:#444;
+ text-align: left;
+ font-size: 90%;
+ color: #444;
}
+
#visitsLive .datetime, #visitsLive .country, #visitsLive .referer, #visitsLive .settings, #visitsLive .returning {
- border-bottom: 1px solid #d3d1c5;
- border-right:1px solid #d3d1c5;
- padding:5px 5px 5px 12px;
+ border-bottom: 1px solid #d3d1c5;
+ border-right: 1px solid #d3d1c5;
+ padding: 5px 5px 5px 12px;
}
+
#visitsLive .datetime {
- background:#E4E2D7;
- border-top:1px solid #d3d1c5;
- margin:0;
- text-align:left;
+ background: #E4E2D7;
+ border-top: 1px solid #d3d1c5;
+ margin: 0;
+ text-align: left;
}
+
#visitsLive .country {
- background:#FFF url(../../../plugins/CoreHome/templates/images/bullet1.gif) no-repeat scroll 0 0;
+ background: #FFF url(../../../plugins/CoreHome/templates/images/bullet1.gif) no-repeat scroll 0 0;
}
+
#visitsLive .referer {
- background:#F9FAFA none repeat scroll 0 0;
+ background: #F9FAFA none repeat scroll 0 0;
}
+
#visitsLive .referer:hover {
- background:#FFFFF7;
+ background: #FFFFF7;
}
+
#visitsLive .pagesTitle {
- display:block;
- float:left;
+ display: block;
+ float: left;
}
+
#visitsLive .settings {
- background:#FFF none repeat scroll 0 0;
+ background: #FFF none repeat scroll 0 0;
}
-#visitsLive .settings a{
- text-decoration:none;
+
+#visitsLive .settings a {
+ text-decoration: none;
}
+
#visitsLive .returning {
- background:#F9FAFA none repeat scroll 0 0;
+ background: #F9FAFA none repeat scroll 0 0;
}
+
.visitsLiveFooter img {
- vertical-align:middle;
+ vertical-align: middle;
}
+
.visitsLiveFooter {
- line-height:2.5em;
+ line-height: 2.5em;
}
+
.visitorLog table img {
- margin:0 3px 0 0;
+ margin: 0 3px 0 0;
}
-.visitsLiveFooter a.rightLink{
- float:right;
- padding-right:20px;
+
+.visitsLiveFooter a.rightLink {
+ float: right;
+ padding-right: 20px;
}
-#visitsLive .datetime a{
+
+#visitsLive .datetime a {
text-decoration: none;
}
+
table.dataTable td.highlightField {
- background-color:#FFFFCB !important;
+ background-color: #FFFFCB !important;
}
+
ol.visitorLog {
- list-style:decimal inside none;
+ list-style: decimal inside none;
}
+
ol.visitorLog li {
- margin-bottom:4px;
+ margin-bottom: 4px;
}
+
#visitsLive img {
- vertical-align:middle;
+ vertical-align: middle;
}
+
.visitorRank img {
- vertical-align:text-bottom;
+ vertical-align: text-bottom;
}
+
.iconPadding {
- margin-left:4px;
- margin-right:4px;
+ margin-left: 4px;
+ margin-right: 4px;
}
+
.visitorRank {
- margin-left:15px;
- border:1px solid #D8D8D8;
- color:#474747;
- border-radius:3px;
- -moz-border-radius:3px;
- -webkit-border-radius:3px;
+ margin-left: 15px;
+ border: 1px solid #D8D8D8;
+ color: #474747;
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
padding: 3px 5px;
}
+
#visitsLive .visitorRank {
- padding:2px;
- border:none;
- margin-left:5px;
+ padding: 2px;
+ border: none;
+ margin-left: 5px;
}
+
.hash {
color: #BBB;
font-size: 9pt;
- margin-right:2px;
+ margin-right: 2px;
}
-.repeat {
- font-weight: bold;
+
+.repeat {
+ font-weight: bold;
border: 1px solid #444;
- border-radius: 3px 3px 3px 3px;
+ border-radius: 3px 3px 3px 3px;
-moz-border-radius: 3px 3px 3px 3px;
-webkit-border-radius: 3px 3px 3px 3px;
padding: 2px;
diff --git a/plugins/Live/templates/scripts/live.js b/plugins/Live/templates/scripts/live.js
index 13d34cce2f..bad5c82ee5 100644
--- a/plugins/Live/templates/scripts/live.js
+++ b/plugins/Live/templates/scripts/live.js
@@ -9,95 +9,95 @@
* jQuery Plugin for Live visitors widget
*/
-(function($) {
+(function ($) {
$.extend({
- liveWidget: new function() {
-
+ liveWidget: new function () {
+
/**
* Default settings for widgetPreview
*/
var settings = {
- // Maximum numbers of rows to display in widget
- maxRows: 10,
- // minimal time in microseconds to wait between updates
- interval: 3000,
- // maximum time to wait between requests
- maxInterval: 300000,
- // url params to use for data request
- dataUrlParams: null,
- // callback triggered on a successfull update (content of widget changed)
- onUpdate: null,
- // speed for fade animation
- fadeInSpeed: 'slow'
+ // Maximum numbers of rows to display in widget
+ maxRows: 10,
+ // minimal time in microseconds to wait between updates
+ interval: 3000,
+ // maximum time to wait between requests
+ maxInterval: 300000,
+ // url params to use for data request
+ dataUrlParams: null,
+ // callback triggered on a successfull update (content of widget changed)
+ onUpdate: null,
+ // speed for fade animation
+ fadeInSpeed: 'slow'
};
-
+
var currentInterval, updated, updateInterval, liveWidget;
var isStarted = true;
-
+
/**
* Update the widget
*/
function update() {
-
+
// is content updated (eg new visits/views)
updated = false;
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(settings.dataUrlParams, 'GET');
ajaxRequest.setFormat('html');
- ajaxRequest.setCallback( function(r) {
+ ajaxRequest.setCallback(function (r) {
parseResponse(r);
-
+
// add default interval to last interval if not updated or reset to default if so
- if(!updated) {
+ if (!updated) {
currentInterval += settings.interval;
} else {
currentInterval = settings.interval;
- if(settings.onUpdate) settings.onUpdate();
+ if (settings.onUpdate) settings.onUpdate();
}
-
+
// check new interval doesn't reach the defined maximum
- if(settings.maxInterval < currentInterval) {
+ if (settings.maxInterval < currentInterval) {
currentInterval = settings.maxInterval;
}
-
- if(isStarted) {
+
+ if (isStarted) {
window.clearTimeout(updateInterval);
- if($(liveWidget).closest('body').length) {
+ if ($(liveWidget).closest('body').length) {
updateInterval = window.setTimeout(update, currentInterval);
}
}
});
ajaxRequest.send(false);
};
-
+
/**
* Parses the given response and updates the widget if newer content is available
*/
function parseResponse(data) {
- if(!data || !data.length) {
+ if (!data || !data.length) {
updated = false;
return;
}
-
+
var items = $('li', $(data));
- for(var i=items.length;i--;){
+ for (var i = items.length; i--;) {
parseItem(items[i]);
}
};
-
+
/**
* Parses the given item and updates or adds an entry to the list
- *
+ *
* @param item to parse
*/
function parseItem(item) {
var visitId = $(item).attr('id');
- if($('#'+visitId, liveWidget).length) {
- if($('#'+visitId, liveWidget).html() != $(item).html()) {
+ if ($('#' + visitId, liveWidget).length) {
+ if ($('#' + visitId, liveWidget).html() != $(item).html()) {
updated = true;
}
- $('#'+visitId, liveWidget).remove();
+ $('#' + visitId, liveWidget).remove();
$(liveWidget).prepend(item);
} else {
updated = true;
@@ -106,65 +106,65 @@
$(item).fadeIn(settings.fadeInSpeed);
}
// remove rows if there are more than the maximum
- $('li:gt('+(settings.maxRows-1)+')', liveWidget).remove();
+ $('li:gt(' + (settings.maxRows - 1) + ')', liveWidget).remove();
};
-
+
/**
* Constructor
- *
+ *
* @param object userSettings Settings to be used
* @return void
*/
- this.construct = function(userSettings) {
+ this.construct = function (userSettings) {
settings = jQuery.extend(settings, userSettings);
-
- if(!settings.dataUrlParams) {
+
+ if (!settings.dataUrlParams) {
console && console.error('liveWidget error: dataUrlParams needs to be defined in settings.');
return;
}
-
+
liveWidget = this;
-
+
currentInterval = settings.interval;
-
+
updateInterval = window.setTimeout(update, currentInterval);
};
-
+
/**
* Triggers an update for the widget
- *
+ *
* @return void
*/
- this.update = function() {
+ this.update = function () {
update();
};
-
+
/**
* Starts the automatic update cycle
*/
- this.start = function() {
+ this.start = function () {
isStarted = true;
currentInterval = 0;
update();
};
-
+
/**
* Stops the automatic update cycle
*/
- this.stop = function() {
+ this.stop = function () {
isStarted = false;
window.clearTimeout(updateInterval);
};
-
+
/**
* Set the interval for refresh
*/
- this.setInterval = function(interval) {
+ this.setInterval = function (interval) {
currentInterval = interval;
};
}
});
-
+
/**
* Makes liveWidget available with $().liveWidget()
*/
@@ -178,15 +178,13 @@ var pauseImage = "plugins/Live/templates/images/pause.gif";
var pauseDisabledImage = "plugins/Live/templates/images/pause_disabled.gif";
var playImage = "plugins/Live/templates/images/play.gif";
var playDisabledImage = "plugins/Live/templates/images/play_disabled.gif";
-function onClickPause()
-{
- $('#pauseImage').attr('src', pauseImage);
- $('#playImage').attr('src', playDisabledImage);
- return $.liveWidget.stop();
+function onClickPause() {
+ $('#pauseImage').attr('src', pauseImage);
+ $('#playImage').attr('src', playDisabledImage);
+ return $.liveWidget.stop();
}
-function onClickPlay()
-{
- $('#playImage').attr('src', playImage);
- $('#pauseImage').attr('src', pauseDisabledImage);
- return $.liveWidget.start();
+function onClickPlay() {
+ $('#playImage').attr('src', playImage);
+ $('#pauseImage').attr('src', pauseDisabledImage);
+ return $.liveWidget.start();
}
diff --git a/plugins/Live/templates/simpleLastVisitCount.tpl b/plugins/Live/templates/simpleLastVisitCount.tpl
index bc893a93b0..65e2ae731e 100644
--- a/plugins/Live/templates/simpleLastVisitCount.tpl
+++ b/plugins/Live/templates/simpleLastVisitCount.tpl
@@ -1,121 +1,123 @@
{literal}
<script type="text/javascript">
-$(document).ready(function() {
- var refreshWidget = function (element, refreshAfterXSecs)
- {
- // if the widget has been removed from the DOM, abort
- if ($(element).parent().length == 0)
- {
- return;
- }
-
- var lastMinutes = $(element).attr('data-last-minutes') || 3,
- translations = JSON.parse($(element).attr('data-translations'));
-
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams({
- module: 'API',
- method: 'Live.getCounters',
- format: 'json',
- lastMinutes: lastMinutes
- }, 'get');
- ajaxRequest.setFormat('json');
- ajaxRequest.setCallback(function (data) {
- data = data[0];
-
- // set text and tooltip of visitors count metric
- var visitors = data['visitors'];
- if (visitors == 1)
- {
- var visitorsCountMessage = translations['one_visitor'];
- }
- else
- {
- var visitorsCountMessage = translations['visitors'].replace('%s', visitors);
- }
- $('.simple-realtime-visitor-counter', element)
- .attr('title', visitorsCountMessage)
- .find('div').text(visitors);
-
- // set text of individual metrics spans
- var metrics = $('.simple-realtime-metric', element);
-
- var visitsText = data['visits'] == 1
- ? translations['one_visit'] : translations['visits'].replace('%s', data['visits']);
- $(metrics[0]).text(visitsText);
-
- var actionsText = data['actions'] == 1
- ? translations['one_action'] : translations['actions'].replace('%s', data['actions']);
- $(metrics[1]).text(actionsText);
-
- var lastMinutesText = lastMinutes == 1
- ? translations['one_minute'] : translations['minutes'].replace('%s', lastMinutes);
- $(metrics[2]).text(lastMinutesText);
-
- // schedule another request
- setTimeout(function() { refreshWidget(element, refreshAfterXSecs); }, refreshAfterXSecs * 1000);
- });
- ajaxRequest.send(true);
- };
-
- var initSimpleRealtimeVisitorWidget = function (refreshAfterXSecs) {
- $('.simple-realtime-visitor-widget').each(function() {
- var self = this;
- if ($(self).attr('data-inited'))
- {
- return;
- }
-
- $(self).attr('data-inited', 1);
-
- setTimeout(function() { refreshWidget(self, refreshAfterXSecs); }, refreshAfterXSecs * 1000);
- });
- };
-
- initSimpleRealtimeVisitorWidget({/literal}{$refreshAfterXSecs}{literal});
-});
+ $(document).ready(function () {
+ var refreshWidget = function (element, refreshAfterXSecs) {
+ // if the widget has been removed from the DOM, abort
+ if ($(element).parent().length == 0) {
+ return;
+ }
+
+ var lastMinutes = $(element).attr('data-last-minutes') || 3,
+ translations = JSON.parse($(element).attr('data-translations'));
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams({
+ module: 'API',
+ method: 'Live.getCounters',
+ format: 'json',
+ lastMinutes: lastMinutes
+ }, 'get');
+ ajaxRequest.setFormat('json');
+ ajaxRequest.setCallback(function (data) {
+ data = data[0];
+
+ // set text and tooltip of visitors count metric
+ var visitors = data['visitors'];
+ if (visitors == 1) {
+ var visitorsCountMessage = translations['one_visitor'];
+ }
+ else {
+ var visitorsCountMessage = translations['visitors'].replace('%s', visitors);
+ }
+ $('.simple-realtime-visitor-counter', element)
+ .attr('title', visitorsCountMessage)
+ .find('div').text(visitors);
+
+ // set text of individual metrics spans
+ var metrics = $('.simple-realtime-metric', element);
+
+ var visitsText = data['visits'] == 1
+ ? translations['one_visit'] : translations['visits'].replace('%s', data['visits']);
+ $(metrics[0]).text(visitsText);
+
+ var actionsText = data['actions'] == 1
+ ? translations['one_action'] : translations['actions'].replace('%s', data['actions']);
+ $(metrics[1]).text(actionsText);
+
+ var lastMinutesText = lastMinutes == 1
+ ? translations['one_minute'] : translations['minutes'].replace('%s', lastMinutes);
+ $(metrics[2]).text(lastMinutesText);
+
+ // schedule another request
+ setTimeout(function () { refreshWidget(element, refreshAfterXSecs); }, refreshAfterXSecs * 1000);
+ });
+ ajaxRequest.send(true);
+ };
+
+ var initSimpleRealtimeVisitorWidget = function (refreshAfterXSecs) {
+ $('.simple-realtime-visitor-widget').each(function () {
+ var self = this;
+ if ($(self).attr('data-inited')) {
+ return;
+ }
+
+ $(self).attr('data-inited', 1);
+
+ setTimeout(function () { refreshWidget(self, refreshAfterXSecs); }, refreshAfterXSecs * 1000);
+ });
+ };
+
+ initSimpleRealtimeVisitorWidget({/literal}{$refreshAfterXSecs}{literal});
+ });
</script>
-<style>
-.simple-realtime-visitor-widget {
- text-align:center;
-}
-.simple-realtime-visitor-counter {
- background-color: #F1F0EB;
-
- -moz-border-radius: 10px;
- -webkit-border-radius: 10px;
- border-radius: 10px;
- display:inline-block;
- margin: 2em 0 1em 0;
-}
-.simple-realtime-visitor-counter > div {
- font-size: 4.0em;
- padding: .25em .5em .25em .5em;
- color:#444;
-}
-.simple-realtime-metric {
- font-style:italic;
- font-weight:bold;
- color:#333;
-}
-
-.simple-realtime-elaboration {
- margin: 1em 2em 1em 2em;
- color:#666;
- display:inline-block;
-}
-</style>
+ <style>
+ .simple-realtime-visitor-widget {
+ text-align: center;
+ }
+
+ .simple-realtime-visitor-counter {
+ background-color: #F1F0EB;
+
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+ border-radius: 10px;
+ display: inline-block;
+ margin: 2em 0 1em 0;
+ }
+
+ .simple-realtime-visitor-counter > div {
+ font-size: 4.0em;
+ padding: .25em .5em .25em .5em;
+ color: #444;
+ }
+
+ .simple-realtime-metric {
+ font-style: italic;
+ font-weight: bold;
+ color: #333;
+ }
+
+ .simple-realtime-elaboration {
+ margin: 1em 2em 1em 2em;
+ color: #666;
+ display: inline-block;
+ }
+ </style>
{/literal}
<div class='simple-realtime-visitor-widget' data-last-minutes="{$lastMinutes}" data-translations="{$translations|@json_encode|escape:'html'}">
- <div class='simple-realtime-visitor-counter' title="{if $visitors eq 1}{'Live_NbVisitor'|translate}{else}{'Live_NbVisitors'|translate:$visitors}{/if}">
- <div>{$visitors}</div>
- </div>
- <br/>
- <div class='simple-realtime-elaboration'>
- {capture assign="visitsMessage"}<span class="simple-realtime-metric" data-metric="visits">{if $visits eq 1}{'General_OneVisit'|translate}{else}{'General_NVisits'|translate:$visits}{/if}</span>{/capture}
- {capture assign="actionsMessage"}<span class="simple-realtime-metric" data-metric="actions">{if $actions eq 1}{'General_OneAction'|translate}{else}{'VisitsSummary_NbActionsDescription'|translate:$actions}{/if}</span>{/capture}
- {capture assign="minutesMessage"}<span class="simple-realtime-metric" data-metric="minutes">{if $lastMinutes eq 1}{'General_OneMinute'|translate}{else}{'General_NMinutes'|translate:$lastMinutes}{/if}</span>{/capture}
-
- {'Live_SimpleRealTimeWidget_Message'|translate:$visitsMessage:$actionsMessage:$minutesMessage}
- </div>
+ <div class='simple-realtime-visitor-counter' title="{if $visitors eq 1}{'Live_NbVisitor'|translate}{else}{'Live_NbVisitors'|translate:$visitors}{/if}">
+ <div>{$visitors}</div>
+ </div>
+ <br/>
+
+ <div class='simple-realtime-elaboration'>
+ {capture assign="visitsMessage"}<span class="simple-realtime-metric"
+ data-metric="visits">{if $visits eq 1}{'General_OneVisit'|translate}{else}{'General_NVisits'|translate:$visits}{/if}</span>{/capture}
+ {capture assign="actionsMessage"}<span class="simple-realtime-metric"
+ data-metric="actions">{if $actions eq 1}{'General_OneAction'|translate}{else}{'VisitsSummary_NbActionsDescription'|translate:$actions}{/if}</span>{/capture}
+ {capture assign="minutesMessage"}<span class="simple-realtime-metric"
+ data-metric="minutes">{if $lastMinutes eq 1}{'General_OneMinute'|translate}{else}{'General_NMinutes'|translate:$lastMinutes}{/if}</span>{/capture}
+
+ {'Live_SimpleRealTimeWidget_Message'|translate:$visitsMessage:$actionsMessage:$minutesMessage}
+ </div>
</div>
diff --git a/plugins/Live/templates/totalVisits.tpl b/plugins/Live/templates/totalVisits.tpl
index 36d8558eb3..27c12873a0 100644
--- a/plugins/Live/templates/totalVisits.tpl
+++ b/plugins/Live/templates/totalVisits.tpl
@@ -1,26 +1,29 @@
<div id="visitsTotal">
- <table class="dataTable" cellspacing="0">
- <thead>
- <tr>
- <th id="label" class="sortable label" style="cursor: auto;">
- <div id="thDIV">{'General_Date'|translate}</div></th>
- <th id="label" class="sortable label" style="cursor: auto;">
- <div id="thDIV">{'General_ColumnNbVisits'|translate}</div></th>
- <th id="label" class="sortable label" style="cursor: auto;">
- <div id="thDIV">{'General_ColumnPageviews'|translate}</div></th>
- </tr>
- </thead>
- <tbody>
- <tr class="">
- <td class="columnodd">{'Live_LastHours'|translate:24}</td>
- <td class="columnodd">{$visitorsCountToday}</td>
- <td class="columnodd">{$pisToday}</td>
- </tr>
- <tr class="">
- <td class="columnodd">{'Live_LastMinutes'|translate:30}</td>
- <td class="columnodd">{$visitorsCountHalfHour}</td>
- <td class="columnodd">{$pisHalfhour}</td>
- </tr>
- </tbody>
- </table>
+ <table class="dataTable" cellspacing="0">
+ <thead>
+ <tr>
+ <th id="label" class="sortable label" style="cursor: auto;">
+ <div id="thDIV">{'General_Date'|translate}</div>
+ </th>
+ <th id="label" class="sortable label" style="cursor: auto;">
+ <div id="thDIV">{'General_ColumnNbVisits'|translate}</div>
+ </th>
+ <th id="label" class="sortable label" style="cursor: auto;">
+ <div id="thDIV">{'General_ColumnPageviews'|translate}</div>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr class="">
+ <td class="columnodd">{'Live_LastHours'|translate:24}</td>
+ <td class="columnodd">{$visitorsCountToday}</td>
+ <td class="columnodd">{$pisToday}</td>
+ </tr>
+ <tr class="">
+ <td class="columnodd">{'Live_LastMinutes'|translate:30}</td>
+ <td class="columnodd">{$visitorsCountHalfHour}</td>
+ <td class="columnodd">{$pisHalfhour}</td>
+ </tr>
+ </tbody>
+ </table>
</div>
diff --git a/plugins/Live/templates/visitorLog.tpl b/plugins/Live/templates/visitorLog.tpl
index f8b9183738..ff39c60179 100644
--- a/plugins/Live/templates/visitorLog.tpl
+++ b/plugins/Live/templates/visitorLog.tpl
@@ -1,316 +1,347 @@
<div class="visitorLog dataTable" data-report="{$properties.uniqueId}" data-params="{$javascriptVariablesToSet|@json_encode|escape:'html'}">
{if !$isWidget}
- <h2>{if $javascriptVariablesToSet.filterEcommerce}{'Goals_EcommerceLog'|translate}{else}{'Live_VisitorLog'|translate}{/if}</h2>
-
- {if !empty($reportDocumentation)}
- <div class="reportDocumentation"><p>{$reportDocumentation}</p></div>
- {/if}
+ <h2>{if $javascriptVariablesToSet.filterEcommerce}{'Goals_EcommerceLog'|translate}{else}{'Live_VisitorLog'|translate}{/if}</h2>
+ {if !empty($reportDocumentation)}
+ <div class="reportDocumentation"><p>{$reportDocumentation}</p></div>
+ {/if}
{/if}
{capture assign='displayVisitorsInOwnColumn'}{if $isWidget}0{else}1{/if}{/capture}
<a graphid="VisitsSummarygetEvolutionGraph" name="evolutionGraph"></a>
{if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
- {$arrayDataTable.message}
- {else}
- {if count($arrayDataTable) == 0}
- <a name="{$properties.uniqueId}"></a>
- <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
- {else}
- <a name="{$properties.uniqueId}"></a>
+ {$arrayDataTable.message}
+{else}
+{if count($arrayDataTable) == 0}
+ <a name="{$properties.uniqueId}"></a>
+ <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
+{else}
+ <a name="{$properties.uniqueId}"></a>
+ <table class="dataTable" cellspacing="0" width="100%" style="width:100%;">
+ <thead>
+ <tr>
+ <th style="display:none"></th>
+ <th id="label" class="sortable label" style="cursor: auto;width:12%" width="12%">
+ <div id="thDIV">{'General_Date'|translate}
+ <div>
+ </th>
+ {if $displayVisitorsInOwnColumn}
+ <th id="label" class="sortable label" style="cursor: auto;width:13%" width="13%">
+ <div id="thDIV">{'General_Visitors'|translate}
+ <div>
+ </th>
+ {/if}
+ <th id="label" class="sortable label" style="cursor: auto;width:15%" width="15%">
+ <div id="thDIV">{'Live_Referrer_URL'|translate}
+ <div>
+ </th>
+ <th id="label" class="sortable label" style="cursor: auto;width:62%" width="62%">
+ <div id="thDIV">{'General_ColumnNbActions'|translate}
+ <div>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
- <table class="dataTable" cellspacing="0" width="100%" style="width:100%;">
- <thead>
- <tr>
- <th style="display:none"></th>
- <th id="label" class="sortable label" style="cursor: auto;width:12%" width="12%">
- <div id="thDIV">{'General_Date'|translate}<div></th>
- {if $displayVisitorsInOwnColumn}
- <th id="label" class="sortable label" style="cursor: auto;width:13%" width="13%">
- <div id="thDIV">{'General_Visitors'|translate}<div></th>
- {/if}
- <th id="label" class="sortable label" style="cursor: auto;width:15%" width="15%">
- <div id="thDIV">{'Live_Referrer_URL'|translate}<div></th>
- <th id="label" class="sortable label" style="cursor: auto;width:62%" width="62%">
- <div id="thDIV">{'General_ColumnNbActions'|translate}<div></th>
- </tr>
- </thead>
- <tbody>
+ {foreach from=$arrayDataTable item=visitor}
-{foreach from=$arrayDataTable item=visitor}
+ {capture assign='visitorColumnContent'}
+ &nbsp;
+ <img src="{$visitor.columns.countryFlag}" title="{$visitor.columns.location|escape:'html'}, Provider {$visitor.columns.provider|escape:'html'}"/>
+ &nbsp;
+ <img src="{$visitor.columns.browserIcon}" title="{$visitor.columns.browserName} with plugins {$visitor.columns.plugins} enabled"/>
+ &nbsp;
+ <img src="{$visitor.columns.operatingSystemIcon}"
+ title="{$visitor.columns.operatingSystem}, {$visitor.columns.resolution} ({$visitor.columns.screenType})"/>
+ {if $visitor.columns.visitorTypeIcon}
+ {if !empty($visitor.columns.visitorId)}
+ <a class="rightLink" href="javascript:Piwik_Live_LoadVisitorPopover('{$visitor.columns.visitorId}')">
+ {/if}
+ &nbsp;-
+ <img src="{$visitor.columns.visitorTypeIcon}"
+ title="{'General_ReturningVisitor'|translate}{if !empty($visitor.columns.visitorId)} - {'General_ReturningVisitorAllVisits'|translate}{/if}"/>
+ {if !empty($visitor.columns.visitorId)}</a>{/if}
+ {/if}
+
+ {if !$displayVisitorsInOwnColumn}<br/><br/>{/if}
- {capture assign='visitorColumnContent'}
- &nbsp;<img src="{$visitor.columns.countryFlag}" title="{$visitor.columns.location|escape:'html'}, Provider {$visitor.columns.provider|escape:'html'}" />
- &nbsp;<img src="{$visitor.columns.browserIcon}" title="{$visitor.columns.browserName} with plugins {$visitor.columns.plugins} enabled" />
- &nbsp;<img src="{$visitor.columns.operatingSystemIcon}" title="{$visitor.columns.operatingSystem}, {$visitor.columns.resolution} ({$visitor.columns.screenType})" />
- {if $visitor.columns.visitorTypeIcon}
- {if !empty($visitor.columns.visitorId)}
- <a class="rightLink" href="javascript:Piwik_Live_LoadVisitorPopover('{$visitor.columns.visitorId}')">
+ &nbsp;{if $visitor.columns.visitConverted}
+ <span title="{'General_VisitConvertedNGoals'|translate:$visitor.columns.goalConversions}" class='visitorRank'
+ {if !$displayVisitorsInOwnColumn}style='margin-left:0'{/if}>
+ <img src="{$visitor.columns.visitConvertedIcon}"/>
+ <span class='hash'>#</span>
+ {$visitor.columns.goalConversions}
+ {if $visitor.columns.visitEcommerceStatusIcon}
+ &nbsp;-
+ <img src="{$visitor.columns.visitEcommerceStatusIcon}" title="{$visitor.columns.visitEcommerceStatus}"/>
{/if}
- &nbsp;- <img src="{$visitor.columns.visitorTypeIcon}" title="{'General_ReturningVisitor'|translate}{if !empty($visitor.columns.visitorId)} - {'General_ReturningVisitorAllVisits'|translate}{/if}" />
- {if !empty($visitor.columns.visitorId)}</a>{/if}
- {/if}
-
- {if !$displayVisitorsInOwnColumn} <br/> <br/> {/if}
-
- &nbsp;{if $visitor.columns.visitConverted}
- <span title="{'General_VisitConvertedNGoals'|translate:$visitor.columns.goalConversions}" class='visitorRank' {if !$displayVisitorsInOwnColumn}style='margin-left:0'{/if}>
- <img src="{$visitor.columns.visitConvertedIcon}" />
- <span class='hash'>#</span>{$visitor.columns.goalConversions}
- {if $visitor.columns.visitEcommerceStatusIcon}
- &nbsp;- <img src="{$visitor.columns.visitEcommerceStatusIcon}" title="{$visitor.columns.visitEcommerceStatus}"/>
- {/if}
- </span>{/if}
- <br/>
- {if $displayVisitorsInOwnColumn}
- {if count($visitor.columns.pluginsIcons) > 0}
- <hr/>
- {'UserSettings_Plugins'|translate}:
- {foreach from=$visitor.columns.pluginsIcons item=pluginIcon name=plugins}
- <img src="{$pluginIcon.pluginIcon}" title="{$pluginIcon.pluginName|capitalize:true}" alt="{$pluginIcon.pluginName|capitalize:true}" />
- {/foreach}
- {/if}
- {/if}
- {/capture}
-
- {capture assign='visitorRow'}
- <tr class="label{cycle values='odd,even'}">
- <td style="display:none;"></td>
- <td class="label" style="width:12%" width="12%">
- <strong title="{if $visitor.columns.visitorType=='new'}{'General_NewVisitor'|translate}{else}{'Live_VisitorsLastVisit'|translate:$visitor.columns.daysSinceLastVisit}{/if}">
- {$visitor.columns.serverDatePrettyFirstAction}
- {if $isWidget}<br/>{else}-{/if} {$visitor.columns.serverTimePrettyFirstAction}</strong>
- {if !empty($visitor.columns.visitIp)} <br/><span title="{if !empty($visitor.columns.visitorId)}{'General_VisitorID'|translate}: {$visitor.columns.visitorId}{/if}{if $visitor.columns.latitude || $visitor.columns.longitude}
+ </span>{/if}
+ <br/>
+ {if $displayVisitorsInOwnColumn}
+ {if count($visitor.columns.pluginsIcons) > 0}
+ <hr/>
+ {'UserSettings_Plugins'|translate}:
+ {foreach from=$visitor.columns.pluginsIcons item=pluginIcon name=plugins}
+ <img src="{$pluginIcon.pluginIcon}" title="{$pluginIcon.pluginName|capitalize:true}" alt="{$pluginIcon.pluginName|capitalize:true}"/>
+ {/foreach}
+ {/if}
+ {/if}
+ {/capture}
+
+ {capture assign='visitorRow'}
+ <tr class="label{cycle values='odd,even'}">
+ <td style="display:none;"></td>
+ <td class="label" style="width:12%" width="12%">
+ <strong title="{if $visitor.columns.visitorType=='new'}{'General_NewVisitor'|translate}{else}{'Live_VisitorsLastVisit'|translate:$visitor.columns.daysSinceLastVisit}{/if}">
+ {$visitor.columns.serverDatePrettyFirstAction}
+ {if $isWidget}<br/>{else}-{/if} {$visitor.columns.serverTimePrettyFirstAction}</strong>
+ {if !empty($visitor.columns.visitIp)}
+ <br/>
+ <span title="{if !empty($visitor.columns.visitorId)}{'General_VisitorID'|translate}: {$visitor.columns.visitorId}{/if}{if $visitor.columns.latitude || $visitor.columns.longitude}
+
+ GPS (lat/long): {$visitor.columns.latitude|escape:'html'},{$visitor.columns.longitude|escape:'html'}{/if}">
+ IP: {$visitor.columns.visitIp}</span>{/if}
+
+ {if (isset($visitor.columns.provider)&&$visitor.columns.provider!='IP')}
+ <br/>
+ {'Provider_ColumnProvider'|translate}:
+ <a href="{$visitor.columns.providerUrl}" target="_blank" title="{$visitor.columns.providerUrl}" style="text-decoration:underline;">
+ {$visitor.columns.provider}
+ </a>
+ {/if}
+ {if !empty($visitor.columns.customVariables)}
+ <br/>
+ {foreach from=$visitor.columns.customVariables item=customVariable key=id}
+ {capture assign=name}customVariableName{$id}{/capture}
+ {capture assign=value}customVariableValue{$id}{/capture}
+ <br/>
+ <acronym
+ title="{'CustomVariables_CustomVariables'|translate} (index {$id})">{$customVariable.$name|truncate:30:"...":true|escape:'html'}</acronym>{if strlen($customVariable.$value)>0}: {$customVariable.$value|truncate:50:"...":true|escape:'html'}{/if}
+ {/foreach}
+ {/if}
+ {if !$displayVisitorsInOwnColumn}
+ <br/>
+ {$visitorColumnContent}
+ {/if}
+ </td>
+
+ {if $displayVisitorsInOwnColumn}
+ <td class="label" style="width:13%" width="13%">
+ {$visitorColumnContent}
+ </td>
+ {/if}
- GPS (lat/long): {$visitor.columns.latitude|escape:'html'},{$visitor.columns.longitude|escape:'html'}{/if}">IP: {$visitor.columns.visitIp}</span>{/if}
-
- {if (isset($visitor.columns.provider)&&$visitor.columns.provider!='IP')}
- <br />
- {'Provider_ColumnProvider'|translate}:
- <a href="{$visitor.columns.providerUrl}" target="_blank" title="{$visitor.columns.providerUrl}" style="text-decoration:underline;">
- {$visitor.columns.provider}
- </a>
- {/if}
- {if !empty($visitor.columns.customVariables)}
- <br/>
- {foreach from=$visitor.columns.customVariables item=customVariable key=id}
- {capture assign=name}customVariableName{$id}{/capture}
- {capture assign=value}customVariableValue{$id}{/capture}
- <br/><acronym title="{'CustomVariables_CustomVariables'|translate} (index {$id})">{$customVariable.$name|truncate:30:"...":true|escape:'html'}</acronym>{if strlen($customVariable.$value)>0}: {$customVariable.$value|truncate:50:"...":true|escape:'html'}{/if}
- {/foreach}
- {/if}
- {if !$displayVisitorsInOwnColumn}
- <br/>
- {$visitorColumnContent}
- {/if}
- </td>
-
- {if $displayVisitorsInOwnColumn}
- <td class="label" style="width:13%" width="13%">
- {$visitorColumnContent}
- </td>
- {/if}
-
- <td class="column" style="width:20%" width="20%">
- <div class="referer">
- {if $visitor.columns.referrerType == 'website'}
- {'Referers_ColumnWebsite'|translate}:
- <a href="{$visitor.columns.referrerUrl|escape:'html'}" target="_blank" title="{$visitor.columns.referrerUrl|escape:'html'}" style="text-decoration:underline;">
- {$visitor.columns.referrerName|escape:'html'}
- </a>
- {/if}
- {if $visitor.columns.referrerType == 'campaign'}
- {'Referers_ColumnCampaign'|translate}
- <br />
- {$visitor.columns.referrerName|escape:'html'}
- {if !empty($visitor.columns.referrerKeyword)} - {$visitor.columns.referrerKeyword|escape:'html'}{/if}
- {/if}
- {if $visitor.columns.referrerType == 'search'}
- {if !empty($visitor.columns.searchEngineIcon)}
- <img src="{$visitor.columns.searchEngineIcon}" alt="{$visitor.columns.referrerName|escape:'html'}" />
- {/if}
- {$visitor.columns.referrerName|escape:'html'}
- {if !empty($visitor.columns.referrerKeyword)}{'Referers_Keywords'|translate}:
- <br />
- <a href="{$visitor.columns.referrerUrl|escape:'html'}" target="_blank" style="text-decoration:underline;">
- "{$visitor.columns.referrerKeyword|escape:'html'}"</a>
- {/if}
- {capture assign='keyword'}{$visitor.columns.referrerKeyword|escape:'html'}{/capture}
- {capture assign='searchName'}{$visitor.columns.referrerName|escape:"html"}{/capture}
- {capture assign='position'}#{$visitor.columns.referrerKeywordPosition}{/capture}
- {if !empty($visitor.columns.referrerKeywordPosition)}<span title='{'Live_KeywordRankedOnSearchResultForThisVisitor'|translate:$keyword:$position:$searchName}' class='visitorRank'><span class='hash'>#</span>{$visitor.columns.referrerKeywordPosition}</span>{/if}
- {/if}
- {if $visitor.columns.referrerType == 'direct'}{'Referers_DirectEntry'|translate}{/if}
- </div>
- </td>
- <td class="column {if $visitor.columns.visitConverted && !$isWidget}highlightField{/if}" style="width:55%" width="55%">
- <strong>
- {$visitor.columns.actionDetails|@count}
- {if $visitor.columns.actionDetails|@count <= 1}
- {'Live_Action'|translate}
- {else}
- {'Live_Actions'|translate}
- {/if}
- {if $visitor.columns.visitDuration > 0}- {$visitor.columns.visitDurationPretty}{/if}
- </strong>
- <br />
- <ol class='visitorLog'>
- {capture assign='visitorHasSomeEcommerceActivity'}0{/capture}
- {foreach from=$visitor.columns.actionDetails item=action}
- {capture assign='customVariablesTooltip'}{if !empty($action.customVariables)}{'CustomVariables_CustomVariables'|translate}
- {foreach from=$action.customVariables item=customVariable key=id}{capture assign=name}customVariableName{$id}{/capture}{capture assign=value}customVariableValue{$id}{/capture}
+ <td class="column" style="width:20%" width="20%">
+ <div class="referer">
+ {if $visitor.columns.referrerType == 'website'}
+ {'Referers_ColumnWebsite'|translate}:
+ <a href="{$visitor.columns.referrerUrl|escape:'html'}" target="_blank" title="{$visitor.columns.referrerUrl|escape:'html'}"
+ style="text-decoration:underline;">
+ {$visitor.columns.referrerName|escape:'html'}
+ </a>
+ {/if}
+ {if $visitor.columns.referrerType == 'campaign'}
+ {'Referers_ColumnCampaign'|translate}
+ <br/>
+ {$visitor.columns.referrerName|escape:'html'}
+ {if !empty($visitor.columns.referrerKeyword)} - {$visitor.columns.referrerKeyword|escape:'html'}{/if}
+ {/if}
+ {if $visitor.columns.referrerType == 'search'}
+ {if !empty($visitor.columns.searchEngineIcon)}
+ <img src="{$visitor.columns.searchEngineIcon}" alt="{$visitor.columns.referrerName|escape:'html'}"/>
+ {/if}
+ {$visitor.columns.referrerName|escape:'html'}
+ {if !empty($visitor.columns.referrerKeyword)}{'Referers_Keywords'|translate}:
+ <br/>
+ <a href="{$visitor.columns.referrerUrl|escape:'html'}" target="_blank" style="text-decoration:underline;">
+ "{$visitor.columns.referrerKeyword|escape:'html'}"</a>
+ {/if}
+ {capture assign='keyword'}{$visitor.columns.referrerKeyword|escape:'html'}{/capture}
+ {capture assign='searchName'}{$visitor.columns.referrerName|escape:"html"}{/capture}
+ {capture assign='position'}#{$visitor.columns.referrerKeywordPosition}{/capture}
+ {if !empty($visitor.columns.referrerKeywordPosition)}<span
+ title='{'Live_KeywordRankedOnSearchResultForThisVisitor'|translate:$keyword:$position:$searchName}' class='visitorRank'>
+ <span class='hash'>#</span>
+ {$visitor.columns.referrerKeywordPosition}</span>{/if}
+ {/if}
+ {if $visitor.columns.referrerType == 'direct'}{'Referers_DirectEntry'|translate}{/if}
+ </div>
+ </td>
+ <td class="column {if $visitor.columns.visitConverted && !$isWidget}highlightField{/if}" style="width:55%" width="55%">
+ <strong>
+ {$visitor.columns.actionDetails|@count}
+ {if $visitor.columns.actionDetails|@count <= 1}
+ {'Live_Action'|translate}
+ {else}
+ {'Live_Actions'|translate}
+ {/if}
+ {if $visitor.columns.visitDuration > 0}- {$visitor.columns.visitDurationPretty}{/if}
+ </strong>
+ <br/>
+ <ol class='visitorLog'>
+ {capture assign='visitorHasSomeEcommerceActivity'}0{/capture}
+ {foreach from=$visitor.columns.actionDetails item=action}
+ {capture assign='customVariablesTooltip'}{if !empty($action.customVariables)}{'CustomVariables_CustomVariables'|translate}
+ {foreach from=$action.customVariables item=customVariable key=id}{capture assign=name}customVariableName{$id}{/capture}{capture assign=value}customVariableValue{$id}{/capture}
-- {$customVariable.$name|escape:'html'} {if strlen($customVariable.$value) > 0} = {$customVariable.$value|escape:'html'}{/if}
- {/foreach}{/if}
- {/capture}
- {if !$javascriptVariablesToSet.filterEcommerce
- || $action.type == 'ecommerceOrder'
- || $action.type == 'ecommerceAbandonedCart'}
- <li class="{if !empty($action.goalName)}goal{else}action{/if}" title="{$action.serverTimePretty|escape:'html'}{if !empty($action.url) && strlen(trim($action.url))} - {$action.url|escape:'html'}{/if} {if strlen(trim($customVariablesTooltip))}
+ - {$customVariable.$name|escape:'html'} {if strlen($customVariable.$value) > 0} = {$customVariable.$value|escape:'html'}{/if}
+ {/foreach}{/if}
+ {/capture}
+ {if !$javascriptVariablesToSet.filterEcommerce
+ || $action.type == 'ecommerceOrder'
+ || $action.type == 'ecommerceAbandonedCart'}
+ <li class="{if !empty($action.goalName)}goal{else}action{/if}"
+ title="{$action.serverTimePretty|escape:'html'}{if !empty($action.url) && strlen(trim($action.url))} - {$action.url|escape:'html'}{/if} {if strlen(trim($customVariablesTooltip))}
{$customVariablesTooltip|trim}{/if}{if isset($action.timeSpentPretty)}
{'General_TimeOnPage'|translate}: {$action.timeSpentPretty}{/if}">
- {if $action.type == 'ecommerceOrder' || $action.type == 'ecommerceAbandonedCart'}
- {* Ecommerce Abandoned Cart / Ecommerce Order *}
-
- <img src="{$action.icon}" />
- {if $action.type == 'ecommerceOrder'}
- {capture assign='visitorHasSomeEcommerceActivity'}1{/capture}
- <strong>{'Goals_EcommerceOrder'|translate}</strong> <span style='color:#666666'>({$action.orderId})</span>
- {else}<strong>{'Goals_AbandonedCart'|translate}</strong>
-
- {* TODO: would be nice to have the icons Orders / Cart in the ecommerce log footer *}
- {if $javascriptVariablesToSet.filterEcommerce == 2}{capture assign='visitorHasSomeEcommerceActivity'}1{/capture}{/if}
-
- {/if} <br/>
- <span {if !$isWidget}style='margin-left:20px'{/if}>
+ {if $action.type == 'ecommerceOrder' || $action.type == 'ecommerceAbandonedCart'}
+ {* Ecommerce Abandoned Cart / Ecommerce Order *}
+ <img src="{$action.icon}"/>
+ {if $action.type == 'ecommerceOrder'}
+ {capture assign='visitorHasSomeEcommerceActivity'}1{/capture}
+ <strong>{'Goals_EcommerceOrder'|translate}</strong>
+ <span style='color:#666666'>({$action.orderId})</span>
+ {else}<strong>{'Goals_AbandonedCart'|translate}</strong>
+
+ {* TODO: would be nice to have the icons Orders / Cart in the ecommerce log footer *}
+ {if $javascriptVariablesToSet.filterEcommerce == 2}{capture assign='visitorHasSomeEcommerceActivity'}1{/capture}{/if}
+
+ {/if}
+ <br/>
+ <span {if !$isWidget}style='margin-left:20px'{/if}>
{if $action.type == 'ecommerceOrder'}
- <abbr title="
+ <abbr title="
{'Live_GoalRevenue'|translate}: {$action.revenue|money:$javascriptVariablesToSet.idSite}
{if !empty($action.revenueSubTotal)} - {'General_Subtotal'|translate}: {$action.revenueSubTotal|money:$javascriptVariablesToSet.idSite}{/if}
{if !empty($action.revenueTax)} - {'General_Tax'|translate}: {$action.revenueTax|money:$javascriptVariablesToSet.idSite}{/if}
{if !empty($action.revenueShipping)} - {'General_Shipping'|translate}: {$action.revenueShipping|money:$javascriptVariablesToSet.idSite}{/if}
{if !empty($action.revenueDiscount)} - {'General_Discount'|translate}: {$action.revenueDiscount|money:$javascriptVariablesToSet.idSite}{/if}
">{'Live_GoalRevenue'|translate}:
- {else}
- {capture assign='revenueLeft'}{'Live_GoalRevenue'|translate}{/capture}{'Goals_LeftInCart'|translate:$revenueLeft}:
- {/if}
- <strong>{$action.revenue|money:$javascriptVariablesToSet.idSite}</strong>{if $action.type == 'ecommerceOrder'}</abbr>{/if},
- {'General_Quantity'|translate}: {$action.items}
-
- {* Ecommerce items in Cart/Order *}
- {if !empty($action.itemDetails)}
- <ul style='list-style:square;margin-left:{if $isWidget}15{else}50{/if}px'>
- {foreach from=$action.itemDetails item=product}
- <li>{$product.itemSKU|escape}{if !empty($product.itemName)}: {$product.itemName|escape}{/if}{if !empty($product.itemCategory)} ({$product.itemCategory|escape}){/if},
- {'General_Quantity'|translate}: {$product.quantity},
- {'General_Price'|translate}: {$product.price|money:$javascriptVariablesToSet.idSite}
- </li>
- {/foreach}
- </ul>
- {/if}
+ {else}
+ {capture assign='revenueLeft'}{'Live_GoalRevenue'|translate}{/capture}{'Goals_LeftInCart'|translate:$revenueLeft}
+ :
+ {/if}
+ <strong>{$action.revenue|money:$javascriptVariablesToSet.idSite}</strong>{if $action.type == 'ecommerceOrder'}
+ </abbr>{/if},
+ {'General_Quantity'|translate}: {$action.items}
+
+ {* Ecommerce items in Cart/Order *}
+ {if !empty($action.itemDetails)}
+ <ul style='list-style:square;margin-left:{if $isWidget}15{else}50{/if}px'>
+ {foreach from=$action.itemDetails item=product}
+ <li>{$product.itemSKU|escape}{if !empty($product.itemName)}: {$product.itemName|escape}{/if}{if !empty($product.itemCategory)} ({$product.itemCategory|escape}){/if}
+ ,
+ {'General_Quantity'|translate}: {$product.quantity},
+ {'General_Price'|translate}: {$product.price|money:$javascriptVariablesToSet.idSite}
+ </li>
+ {/foreach}
+ </ul>
+ {/if}
</span>
-
- {elseif empty($action.goalName)}
- {* Page view / Download / Outlink *}
- {if !empty($action.pageTitle)}
- {if $action.type == 'search'}<img src='{$action.icon}' title='{'Actions_SubmenuSitesearch'|translate|escape:'html'}'>{/if}
- {$action.pageTitle|unescape|urldecode|escape:'html'|truncate:80:"...":true}
- {/if}
- {if !empty($action.url)}
- {if $action.type == 'action' && !empty($action.pageTitle)}<br/>{/if}
- {if $action.type == 'download'
- || $action.type == 'outlink'}
- <img src='{$action.icon}'>
- {/if}
- <a href="{$action.url|escape:'html'}" target="_blank" style="{if $action.type=='action' && !empty($action.pageTitle)}margin-left: 25px;{/if}text-decoration:underline;">{$action.url|escape:'html'|truncate:80:"...":true}</a>
- {elseif $action.type!='search'}
- <br/>
- <span style="margin-left: 25px;">{$javascriptVariablesToSet.pageUrlNotDefined}</span>
- {/if}
- {else}
- {* Goal conversion *}
- <img src="{$action.icon}" />
- <strong>{$action.goalName|escape:'html'}</strong>
- {if $action.revenue > 0}, {'Live_GoalRevenue'|translate}: <strong>{$action.revenue|money:$javascriptVariablesToSet.idSite}</strong>{/if}
- {/if}
- </li>
- {/if}
- {/foreach}
- </ol>
- </td>
- </tr>
- {/capture}
-
- {if !$javascriptVariablesToSet.filterEcommerce
- || !empty($visitorHasSomeEcommerceActivity)}
- {$visitorRow}
- {/if}
-{/foreach}
+ {elseif empty($action.goalName)}
+ {* Page view / Download / Outlink *}
+ {if !empty($action.pageTitle)}
+ {if $action.type == 'search'}<img src='{$action.icon}'
+ title='{'Actions_SubmenuSitesearch'|translate|escape:'html'}'>{/if}
+ {$action.pageTitle|unescape|urldecode|escape:'html'|truncate:80:"...":true}
+ {/if}
+ {if !empty($action.url)}
+ {if $action.type == 'action' && !empty($action.pageTitle)}<br/>{/if}
+ {if $action.type == 'download'
+ || $action.type == 'outlink'}
+ <img src='{$action.icon}'>
+ {/if}
+ <a href="{$action.url|escape:'html'}" target="_blank"
+ style="{if $action.type=='action' && !empty($action.pageTitle)}margin-left: 25px;{/if}text-decoration:underline;">{$action.url|escape:'html'|truncate:80:"...":true}</a>
+ {elseif $action.type!='search'}
+ <br/>
+ <span style="margin-left: 25px;">{$javascriptVariablesToSet.pageUrlNotDefined}</span>
+ {/if}
+ {else}
+ {* Goal conversion *}
+ <img src="{$action.icon}"/>
+ <strong>{$action.goalName|escape:'html'}</strong>
+ {if $action.revenue > 0}, {'Live_GoalRevenue'|translate}:
+ <strong>{$action.revenue|money:$javascriptVariablesToSet.idSite}</strong>{/if}
+ {/if}
+ </li>
+ {/if}
+ {/foreach}
+ </ol>
+ </td>
+ </tr>
+ {/capture}
-</tbody>
-</table>
-{/if}
+ {if !$javascriptVariablesToSet.filterEcommerce
+ || !empty($visitorHasSomeEcommerceActivity)}
+ {$visitorRow}
+ {/if}
+ {/foreach}
-{if $properties.show_footer}
- {include file="CoreHome/templates/datatable_footer.tpl"}
+ </tbody>
+ </table>
{/if}
-{include file="CoreHome/templates/datatable_js.tpl"}
-<script type="text/javascript" defer="defer">
+ {if $properties.show_footer}
+ {include file="CoreHome/templates/datatable_footer.tpl"}
+ {/if}
+
+ {include file="CoreHome/templates/datatable_js.tpl"}
+ <script type="text/javascript" defer="defer">
-var visitorLogTitle = '{'Live_VisitorLog'|translate|escape:'javascript'}';
-function Piwik_Live_LoadVisitorPopover(visitorId)
-{ldelim}
- var startingDate = piwik.minDateYear +'-01-01';
- var url = 'module=Live&action=getVisitorLog&period=range&date='+ startingDate +',today&show_footer=0&segment=visitorId'+encodeURIComponent('==')+visitorId;
- return Piwik_Popover.createPopupAndLoadUrl(url,visitorLogTitle);
-{rdelim}
+ var visitorLogTitle = '{'Live_VisitorLog'|translate|escape:'javascript'}';
+ function Piwik_Live_LoadVisitorPopover(visitorId) {ldelim}
+ var startingDate = piwik.minDateYear + '-01-01';
+ var url = 'module=Live&action=getVisitorLog&period=range&date=' + startingDate + ',today&show_footer=0&segment=visitorId' + encodeURIComponent('==') + visitorId;
+ return Piwik_Popover.createPopupAndLoadUrl(url, visitorLogTitle);
+ {rdelim
+ }
-$(document).ready(function(){ldelim}
- {literal}
- // Replace duplicated page views by a NX count instead of using too much vertical space
- $("ol.visitorLog").each(function () {
- var prevelement;
- var prevhtml;
- var counter = 0;
- $(this).find("li").each(function () {
- counter++;
- $(this).val(counter);
- var current = $(this).html();
- if (current == prevhtml) {
- var repeat = prevelement.find(".repeat")
- if (repeat.length) {
- repeat.html( (parseInt(repeat.html()) + 1) + "x" );
- } else {
- prevelement.append($("<em title='{/literal}{'Live_PageRefreshed'|translate|escape:'js'}{literal}' class='repeat'>2x</em>"));
- }
- $(this).hide();
- } else {
- prevhtml = current;
- prevelement = $(this);
- }
+ $(document).ready(function () {ldelim}
+ {literal}
+ // Replace duplicated page views by a NX count instead of using too much vertical space
+ $("ol.visitorLog").each(function () {
+ var prevelement;
+ var prevhtml;
+ var counter = 0;
+ $(this).find("li").each(function () {
+ counter++;
+ $(this).val(counter);
+ var current = $(this).html();
+ if (current == prevhtml) {
+ var repeat = prevelement.find(".repeat")
+ if (repeat.length) {
+ repeat.html((parseInt(repeat.html()) + 1) + "x");
+ } else {
+ prevelement.append($("<em title='{/literal}{'Live_PageRefreshed'|translate|escape:'js'}{literal}' class='repeat'>2x</em>"));
+ }
+ $(this).hide();
+ } else {
+ prevhtml = current;
+ prevelement = $(this);
+ }
+ });
+ });
});
- });
-});
-{/literal}
-</script>
+ {/literal}
+ </script>
{/if}
{literal}
-<style type="text/css">
-hr {
- background:none repeat scroll 0 0 transparent;
- border: 0 none #000;
- border-bottom: 1px solid #ccc;
- color:#eee;
- margin:0 2em 0.5em;
- padding:0 0 0.5em;
-}
+ <style type="text/css">
+ hr {
+ background: none repeat scroll 0 0 transparent;
+ border: 0 none #000;
+ border-bottom: 1px solid #ccc;
+ color: #eee;
+ margin: 0 2em 0.5em;
+ padding: 0 0 0.5em;
+ }
-</style>
+ </style>
{/literal}
</div>
diff --git a/plugins/Login/Auth.php b/plugins/Login/Auth.php
index c8c58e7aaf..a7a8a2e490 100644
--- a/plugins/Login/Auth.php
+++ b/plugins/Login/Auth.php
@@ -15,106 +15,101 @@
*/
class Piwik_Login_Auth implements Piwik_Auth
{
- protected $login = null;
- protected $token_auth = null;
+ protected $login = null;
+ protected $token_auth = null;
- /**
- * Authentication module's name, e.g., "Login"
- *
- * @return string
- */
- public function getName()
- {
- return 'Login';
- }
+ /**
+ * Authentication module's name, e.g., "Login"
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return 'Login';
+ }
- /**
- * Authenticates user
- *
- * @return Piwik_Auth_Result
- */
- public function authenticate()
- {
- $rootLogin = Piwik_Config::getInstance()->superuser['login'];
- $rootPassword = Piwik_Config::getInstance()->superuser['password'];
- $rootToken = Piwik_UsersManager_API::getInstance()->getTokenAuth($rootLogin, $rootPassword);
+ /**
+ * Authenticates user
+ *
+ * @return Piwik_Auth_Result
+ */
+ public function authenticate()
+ {
+ $rootLogin = Piwik_Config::getInstance()->superuser['login'];
+ $rootPassword = Piwik_Config::getInstance()->superuser['password'];
+ $rootToken = Piwik_UsersManager_API::getInstance()->getTokenAuth($rootLogin, $rootPassword);
- if(is_null($this->login))
- {
- if($this->token_auth === $rootToken)
- {
- return new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS_SUPERUSER_AUTH_CODE, $rootLogin, $this->token_auth );
- }
+ if (is_null($this->login)) {
+ if ($this->token_auth === $rootToken) {
+ return new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS_SUPERUSER_AUTH_CODE, $rootLogin, $this->token_auth);
+ }
- $login = Piwik_FetchOne(
- 'SELECT login
- FROM '.Piwik_Common::prefixTable('user').'
+ $login = Piwik_FetchOne(
+ 'SELECT login
+ FROM ' . Piwik_Common::prefixTable('user') . '
WHERE token_auth = ?',
- array($this->token_auth)
- );
- if(!empty($login))
- {
- return new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS, $login, $this->token_auth );
- }
- }
- else if(!empty($this->login))
- {
- if($this->login === $rootLogin
- && ($this->getHashTokenAuth($rootLogin, $rootToken) === $this->token_auth)
- || $rootToken === $this->token_auth)
- {
- $this->setTokenAuth($rootToken);
- return new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS_SUPERUSER_AUTH_CODE, $rootLogin, $this->token_auth );
- }
+ array($this->token_auth)
+ );
+ if (!empty($login)) {
+ return new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS, $login, $this->token_auth);
+ }
+ } else if (!empty($this->login)) {
+ if ($this->login === $rootLogin
+ && ($this->getHashTokenAuth($rootLogin, $rootToken) === $this->token_auth)
+ || $rootToken === $this->token_auth
+ ) {
+ $this->setTokenAuth($rootToken);
+ return new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS_SUPERUSER_AUTH_CODE, $rootLogin, $this->token_auth);
+ }
- $login = $this->login;
- $userToken = Piwik_FetchOne(
- 'SELECT token_auth
- FROM '.Piwik_Common::prefixTable('user').'
+ $login = $this->login;
+ $userToken = Piwik_FetchOne(
+ 'SELECT token_auth
+ FROM ' . Piwik_Common::prefixTable('user') . '
WHERE login = ?',
- array($login)
- );
- if(!empty($userToken)
- && (($this->getHashTokenAuth($login, $userToken) === $this->token_auth)
- || $userToken === $this->token_auth))
- {
- $this->setTokenAuth($userToken);
- return new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS, $login, $userToken );
- }
- }
+ array($login)
+ );
+ if (!empty($userToken)
+ && (($this->getHashTokenAuth($login, $userToken) === $this->token_auth)
+ || $userToken === $this->token_auth)
+ ) {
+ $this->setTokenAuth($userToken);
+ return new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS, $login, $userToken);
+ }
+ }
- return new Piwik_Auth_Result( Piwik_Auth_Result::FAILURE, $this->login, $this->token_auth );
- }
+ return new Piwik_Auth_Result(Piwik_Auth_Result::FAILURE, $this->login, $this->token_auth);
+ }
- /**
- * Accessor to set login name
- *
- * @param string $login user login
- */
- public function setLogin($login)
- {
- $this->login = $login;
- }
+ /**
+ * Accessor to set login name
+ *
+ * @param string $login user login
+ */
+ public function setLogin($login)
+ {
+ $this->login = $login;
+ }
- /**
- * Accessor to set authentication token
- *
- * @param string $token_auth authentication token
- */
- public function setTokenAuth($token_auth)
- {
- $this->token_auth = $token_auth;
- }
+ /**
+ * Accessor to set authentication token
+ *
+ * @param string $token_auth authentication token
+ */
+ public function setTokenAuth($token_auth)
+ {
+ $this->token_auth = $token_auth;
+ }
- /**
- * Accessor to compute the hashed authentication token
- *
- * @param string $login user login
- * @param string $token_auth authentication token
- * @return string hashed authentication token
- */
- public function getHashTokenAuth($login, $token_auth)
- {
- return md5($login . $token_auth);
- }
+ /**
+ * Accessor to compute the hashed authentication token
+ *
+ * @param string $login user login
+ * @param string $token_auth authentication token
+ * @return string hashed authentication token
+ */
+ public function getHashTokenAuth($login, $token_auth)
+ {
+ return md5($login . $token_auth);
+ }
}
diff --git a/plugins/Login/Controller.php b/plugins/Login/Controller.php
index 7031ca4307..912c5f441d 100644
--- a/plugins/Login/Controller.php
+++ b/plugins/Login/Controller.php
@@ -16,510 +16,470 @@
*/
class Piwik_Login_Controller extends Piwik_Controller
{
- /**
- * Generate hash on user info and password
- *
- * @param string $userinfo User name, email, etc
- * @param string $password
- * @return string
- */
- private function generateHash($userInfo, $password)
- {
- // mitigate rainbow table attack
- $passwordLen = strlen($password) / 2;
- $hash = Piwik_Common::hash(
- $userInfo . substr($password, 0, $passwordLen)
- . Piwik_Common::getSalt() . substr($password, $passwordLen)
- );
- return $hash;
- }
-
- /**
- * Default action
- *
- * @param none
- * @return void
- */
- function index()
- {
- $this->login();
- }
-
- /**
- * Login form
- *
- * @param string $messageNoAccess Access error message
- * @param string $currentUrl Current URL
- * @return void
- */
- function login($messageNoAccess = null, $infoMessage = false)
- {
- self::checkForceSslLogin();
-
- $form = new Piwik_Login_FormLogin();
- if($form->validate())
- {
- $nonce = $form->getSubmitValue('form_nonce');
- if(Piwik_Nonce::verifyNonce('Piwik_Login.login', $nonce))
- {
- $login = $form->getSubmitValue('form_login');
- $password = $form->getSubmitValue('form_password');
- $rememberMe = $form->getSubmitValue('form_rememberme') == '1';
- $md5Password = md5($password);
- try {
- $this->authenticateAndRedirect($login, $md5Password, $rememberMe);
- } catch(Exception $e) {
- $messageNoAccess = $e->getMessage();
- }
- }
- else
- {
- $messageNoAccess = $this->getMessageExceptionNoAccess();
- }
- }
-
- $view = Piwik_View::factory('login');
- $view->AccessErrorString = $messageNoAccess;
- $view->infoMessage = nl2br($infoMessage);
- $view->addForm( $form );
- $this->configureView($view);
- self::setHostValidationVariablesView($view);
- echo $view->render();
- }
-
- /**
- * Configure common view properties
- *
- * @param Piwik_View $view
- */
- private function configureView($view)
- {
- $this->setBasicVariablesView($view);
-
- $view->linkTitle = Piwik::getRandomTitle();
-
- $view->forceSslLogin = Piwik_Config::getInstance()->General['force_ssl_login'];
-
- // crsf token: don't trust the submitted value; generate/fetch it from session data
- $view->nonce = Piwik_Nonce::getNonce('Piwik_Login.login');
- }
-
- /**
- * Form-less login
- * @see how to use it on http://piwik.org/faq/how-to/#faq_30
- * @throws Exception
- * @return void
- */
- function logme()
- {
- self::checkForceSslLogin();
-
- $password = Piwik_Common::getRequestVar('password', null, 'string');
- if(strlen($password) != 32)
- {
- throw new Exception(Piwik_TranslateException('Login_ExceptionPasswordMD5HashExpected'));
- }
-
- $login = Piwik_Common::getRequestVar('login', null, 'string');
- if($login == Piwik_Config::getInstance()->superuser['login'])
- {
- throw new Exception(Piwik_TranslateException('Login_ExceptionInvalidSuperUserAuthenticationMethod', array("logme")));
- }
-
- $currentUrl = 'index.php';
-
- if(($idSite = Piwik_Common::getRequestVar('idSite', false, 'int')) !== false)
- {
- $currentUrl .= '?idSite='.$idSite;
- }
-
- $urlToRedirect = Piwik_Common::getRequestVar('url', $currentUrl, 'string');
- $urlToRedirect = Piwik_Common::unsanitizeInputValue($urlToRedirect);
-
- $this->authenticateAndRedirect($login, $password, false, $urlToRedirect);
- }
-
- /**
- * Authenticate user and password. Redirect if successful.
- *
- * @param string $login user name
- * @param string $md5Password md5 hash of password
- * @param bool $rememberMe Remember me?
- * @param string $urlToRedirect URL to redirect to, if successfully authenticated
- * @return string failure message if unable to authenticate
- */
- protected function authenticateAndRedirect($login, $md5Password, $rememberMe, $urlToRedirect = 'index.php')
- {
- $info = array( 'login' => $login,
- 'md5Password' => $md5Password,
- 'rememberMe' => $rememberMe,
- );
- Piwik_Nonce::discardNonce('Piwik_Login.login');
- Piwik_PostEvent('Login.initSession', $info);
- Piwik_Url::redirectToUrl($urlToRedirect);
- }
-
- protected function getMessageExceptionNoAccess()
- {
- $message = Piwik_Translate('Login_InvalidNonceOrHeadersOrReferer', array('<a href="?module=Proxy&action=redirect&url='.urlencode('http://piwik.org/faq/how-to-install/#faq_98').'" target="_blank">', '</a>'));
- // Should mention trusted_hosts or link to FAQ
- return $message;
- }
-
- /**
- * Reset password action. Stores new password as hash and sends email
- * to confirm use.
- *
- * @param none
- * @return void
- */
- function resetPassword()
- {
- self::checkForceSslLogin();
-
- $infoMessage = null;
- $formErrors = null;
-
- $form = new Piwik_Login_FormResetPassword();
- if($form->validate())
- {
- $nonce = $form->getSubmitValue('form_nonce');
- if(Piwik_Nonce::verifyNonce('Piwik_Login.login', $nonce))
- {
- $formErrors = $this->resetPasswordFirstStep($form);
- if (empty($formErrors))
- {
- $infoMessage = Piwik_Translate('Login_ConfirmationLinkSent');
- }
- }
- else
- {
- $formErrors = array($this->getMessageExceptionNoAccess());
- }
- }
- else
- {
- // if invalid, display error
- $formData = $form->getFormData();
- $formErrors = $formData['errors'];
- }
-
- $view = Piwik_View::factory('message');
- $view->infoMessage = $infoMessage;
- $view->formErrors = $formErrors;
- echo $view->render();
- }
-
- /**
- * Saves password reset info and sends confirmation email.
- *
- * @return array Error message(s) if an error occurs.
- */
- private function resetPasswordFirstStep( $form )
- {
- $loginMail = $form->getSubmitValue('form_login');
- $token = $form->getSubmitValue('form_token');
- $password = $form->getSubmitValue('form_password');
-
- // check the password
- try
- {
- Piwik_UsersManager::checkPassword($password);
- }
- catch (Exception $ex)
- {
- return array($ex->getMessage());
- }
-
- // get the user's login
- if ($loginMail === 'anonymous')
- {
- return array(Piwik_Translate('Login_InvalidUsernameEmail'));
- }
-
- $user = self::getUserInformation($loginMail);
- if ($user === null)
- {
- return array(Piwik_Translate('Login_InvalidUsernameEmail'));
- }
-
- $login = $user['login'];
-
- // if valid, store password information in options table, then...
- Piwik_Login::savePasswordResetInfo($login, $password);
-
- // ... send email with confirmation link
- try
- {
- $this->sendEmailConfirmationLink($user);
- }
- catch (Exception $ex)
- {
- // remove password reset info
- Piwik_Login::removePasswordResetInfo($login);
-
- return array($ex->getMessage().'<br/>'.Piwik_Translate('Login_ContactAdmin'));
- }
-
- return null;
- }
-
- /**
- * Sends email confirmation link for a password reset request.
- *
- * @param array $user User info for the requested password reset.
- */
- private function sendEmailConfirmationLink( $user )
- {
- $login = $user['login'];
- $email = $user['email'];
-
- // construct a password reset token from user information
- $resetToken = self::generatePasswordResetToken($user);
-
- $ip = Piwik_IP::getIpFromHeader();
- $url = Piwik_Url::getCurrentUrlWithoutQueryString()
- . "?module=Login&action=confirmResetPassword&login=".urlencode($login)
- . "&resetToken=".urlencode($resetToken);
-
- // send email with new password
- $mail = new Piwik_Mail();
- $mail->addTo($email, $login);
- $mail->setSubject(Piwik_Translate('Login_MailTopicPasswordChange'));
- $bodyText = str_replace(
- '\n',
- "\n",
- sprintf(Piwik_Translate('Login_MailPasswordChangeBody'), $login, $ip, $url)
- ) . "\n";
- $mail->setBodyText($bodyText);
-
- $fromEmailName = Piwik_Config::getInstance()->General['login_password_recovery_email_name'];
- $fromEmailAddress = Piwik_Config::getInstance()->General['login_password_recovery_email_address'];
- $mail->setFrom($fromEmailAddress, $fromEmailName);
- @$mail->send();
- }
-
- /**
- * Password reset confirmation action. Finishes the password reset process.
- * Users visit this action from a link supplied in an email.
- */
- public function confirmResetPassword()
- {
- $errorMessage = null;
-
- $login = Piwik_Common::getRequestVar('login', '');
- $resetToken = Piwik_Common::getRequestVar('resetToken', '');
-
- try
- {
- // get password reset info & user info
- $user = self::getUserInformation($login);
- if ($user === null)
- {
- throw new Exception(Piwik_Translate('Login_InvalidUsernameEmail'));
- }
-
- // check that the reset token is valid
- $resetPassword = Piwik_Login::getPasswordToResetTo($login);
- if ($resetPassword === false || !self::isValidToken($resetToken, $user))
- {
- throw new Exception(Piwik_Translate('Login_InvalidOrExpiredToken'));
- }
-
- // reset password of user
- $this->setNewUserPassword($user, $resetPassword);
- }
- catch (Exception $ex)
- {
- $errorMessage = $ex->getMessage();
- }
-
- if (is_null($errorMessage)) // if success, show login w/ success message
- {
- $this->redirectToIndex('Login', 'resetPasswordSuccess');
- }
- else
- {
- // show login page w/ error. this will keep the token in the URL
- return $this->login($errorMessage);
- }
- }
-
- /**
- * Sets the password for a user.
- *
- * @param array $user User info.
- * @param string $passwordHash The hashed password to use.
- */
- private function setNewUserPassword( $user, $passwordHash )
- {
- if (strlen($passwordHash) !== 32) // sanity check
- {
- throw new Exception(
- "setNewUserPassword called w/ incorrect password hash. Something has gone terribly wrong.");
- }
-
- if( $user['email'] == Piwik::getSuperUserEmail() )
- {
- if(!Piwik_Config::getInstance()->isFileWritable())
- {
- throw new Exception(Piwik_Translate('General_ConfigFileIsNotWritable', array("(config/config.ini.php)","<br/>")));
- }
-
- $user['password'] = $passwordHash;
- Piwik_Config::getInstance()->superuser = $user;
- Piwik_Config::getInstance()->forceSave();
- }
- else
- {
- Piwik_UsersManager_API::getInstance()->updateUser(
- $user['login'], $passwordHash, $email = false, $alias = false, $isPasswordHashed = true);
- }
- }
-
- /**
- * The action used after a password is successfully reset. Displays the login
- * screen with an extra message. A separate action is used instead of returning
- * the HTML in confirmResetPassword so the resetToken won't be in the URL.
- */
- public function resetPasswordSuccess()
- {
- return $this->login($errorMessage = null, $infoMessage = Piwik_Translate('Login_PasswordChanged'));
- }
-
- /**
- * Get user information
- *
- * @param string $loginMail user login or email address
- * @return array ("login" => '...', "email" => '...', "password" => '...') or null, if user not found
- */
- protected function getUserInformation($loginMail)
- {
- Piwik::setUserIsSuperUser();
-
- $user = null;
- if( $loginMail == Piwik::getSuperUserEmail()
- || $loginMail == Piwik_Config::getInstance()->superuser['login'] )
- {
- $user = array(
- 'login' => Piwik_Config::getInstance()->superuser['login'],
- 'email' => Piwik::getSuperUserEmail(),
- 'password' => Piwik_Config::getInstance()->superuser['password'],
- );
- }
- else if( Piwik_UsersManager_API::getInstance()->userExists($loginMail) )
- {
- $user = Piwik_UsersManager_API::getInstance()->getUser($loginMail);
- }
- else if( Piwik_UsersManager_API::getInstance()->userEmailExists($loginMail) )
- {
- $user = Piwik_UsersManager_API::getInstance()->getUserByEmail($loginMail);
- }
-
- return $user;
- }
-
- /**
- * Generate a password reset token. Expires in (roughly) 24 hours.
- *
- * @param array user information
- * @param int $timestamp Unix timestamp
- * @return string generated token
- */
- protected function generatePasswordResetToken($user, $timestamp = null)
- {
- /*
- * Piwik does not store the generated password reset token.
- * This avoids a database schema change and SQL queries to store, retrieve, and purge (expired) tokens.
- */
- if(!$timestamp)
- {
- $timestamp = time() + 24*60*60; /* +24 hrs */
- }
-
- $expiry = strftime('%Y%m%d%H', $timestamp);
- $token = $this->generateHash(
- $expiry . $user['login'] . $user['email'],
- $user['password']
- );
- return $token;
- }
-
- /**
- * Validate token.
- *
- * @param string $token
- * @param array $user user information
- * @return bool true if valid, false otherwise
- */
- protected function isValidToken($token, $user)
- {
- $now = time();
-
- // token valid for 24 hrs (give or take, due to the coarse granularity in our strftime format string)
- for($i = 0; $i <= 24; $i++)
- {
- $generatedToken = self::generatePasswordResetToken($user, $now + $i*60*60);
- if($generatedToken === $token)
- {
- return true;
- }
- }
-
- // fails if token is invalid, expired, password already changed, other user information has changed, ...
- return false;
- }
-
- /**
- * Clear session information
- *
- * @param none
- * @return void
- */
- static public function clearSession()
- {
- $authCookieName = Piwik_Config::getInstance()->General['login_cookie_name'];
- $cookie = new Piwik_Cookie($authCookieName);
- $cookie->delete();
-
- Piwik_Session::expireSessionCookie();
- }
-
- /**
- * Logout current user
- *
- * @param none
- * @return void
- */
- public function logout()
- {
- self::clearSession();
-
- $logoutUrl = @Piwik_Config::getInstance()->General['login_logout_url'];
- if(empty($logoutUrl)) {
- Piwik::redirectToModule('CoreHome');
- } else {
- Piwik_Url::redirectToUrl($logoutUrl);
- }
- }
-
- /**
- * Check force_ssl_login and redirect if connection isn't secure and not using a reverse proxy
- *
- * @param none
- * @return void
- */
- protected function checkForceSslLogin()
- {
- $forceSslLogin = Piwik_Config::getInstance()->General['force_ssl_login'];
- if($forceSslLogin
- && !Piwik::isHttps())
- {
- $url = 'https://'
- . Piwik_Url::getCurrentHost()
- . Piwik_Url::getCurrentScriptName()
- . Piwik_Url::getCurrentQueryString();
- Piwik_Url::redirectToUrl($url);
- }
- }
+ /**
+ * Generate hash on user info and password
+ *
+ * @param string $userinfo User name, email, etc
+ * @param string $password
+ * @return string
+ */
+ private function generateHash($userInfo, $password)
+ {
+ // mitigate rainbow table attack
+ $passwordLen = strlen($password) / 2;
+ $hash = Piwik_Common::hash(
+ $userInfo . substr($password, 0, $passwordLen)
+ . Piwik_Common::getSalt() . substr($password, $passwordLen)
+ );
+ return $hash;
+ }
+
+ /**
+ * Default action
+ *
+ * @param none
+ * @return void
+ */
+ function index()
+ {
+ $this->login();
+ }
+
+ /**
+ * Login form
+ *
+ * @param string $messageNoAccess Access error message
+ * @param string $currentUrl Current URL
+ * @return void
+ */
+ function login($messageNoAccess = null, $infoMessage = false)
+ {
+ self::checkForceSslLogin();
+
+ $form = new Piwik_Login_FormLogin();
+ if ($form->validate()) {
+ $nonce = $form->getSubmitValue('form_nonce');
+ if (Piwik_Nonce::verifyNonce('Piwik_Login.login', $nonce)) {
+ $login = $form->getSubmitValue('form_login');
+ $password = $form->getSubmitValue('form_password');
+ $rememberMe = $form->getSubmitValue('form_rememberme') == '1';
+ $md5Password = md5($password);
+ try {
+ $this->authenticateAndRedirect($login, $md5Password, $rememberMe);
+ } catch (Exception $e) {
+ $messageNoAccess = $e->getMessage();
+ }
+ } else {
+ $messageNoAccess = $this->getMessageExceptionNoAccess();
+ }
+ }
+
+ $view = Piwik_View::factory('login');
+ $view->AccessErrorString = $messageNoAccess;
+ $view->infoMessage = nl2br($infoMessage);
+ $view->addForm($form);
+ $this->configureView($view);
+ self::setHostValidationVariablesView($view);
+ echo $view->render();
+ }
+
+ /**
+ * Configure common view properties
+ *
+ * @param Piwik_View $view
+ */
+ private function configureView($view)
+ {
+ $this->setBasicVariablesView($view);
+
+ $view->linkTitle = Piwik::getRandomTitle();
+
+ $view->forceSslLogin = Piwik_Config::getInstance()->General['force_ssl_login'];
+
+ // crsf token: don't trust the submitted value; generate/fetch it from session data
+ $view->nonce = Piwik_Nonce::getNonce('Piwik_Login.login');
+ }
+
+ /**
+ * Form-less login
+ * @see how to use it on http://piwik.org/faq/how-to/#faq_30
+ * @throws Exception
+ * @return void
+ */
+ function logme()
+ {
+ self::checkForceSslLogin();
+
+ $password = Piwik_Common::getRequestVar('password', null, 'string');
+ if (strlen($password) != 32) {
+ throw new Exception(Piwik_TranslateException('Login_ExceptionPasswordMD5HashExpected'));
+ }
+
+ $login = Piwik_Common::getRequestVar('login', null, 'string');
+ if ($login == Piwik_Config::getInstance()->superuser['login']) {
+ throw new Exception(Piwik_TranslateException('Login_ExceptionInvalidSuperUserAuthenticationMethod', array("logme")));
+ }
+
+ $currentUrl = 'index.php';
+
+ if (($idSite = Piwik_Common::getRequestVar('idSite', false, 'int')) !== false) {
+ $currentUrl .= '?idSite=' . $idSite;
+ }
+
+ $urlToRedirect = Piwik_Common::getRequestVar('url', $currentUrl, 'string');
+ $urlToRedirect = Piwik_Common::unsanitizeInputValue($urlToRedirect);
+
+ $this->authenticateAndRedirect($login, $password, false, $urlToRedirect);
+ }
+
+ /**
+ * Authenticate user and password. Redirect if successful.
+ *
+ * @param string $login user name
+ * @param string $md5Password md5 hash of password
+ * @param bool $rememberMe Remember me?
+ * @param string $urlToRedirect URL to redirect to, if successfully authenticated
+ * @return string failure message if unable to authenticate
+ */
+ protected function authenticateAndRedirect($login, $md5Password, $rememberMe, $urlToRedirect = 'index.php')
+ {
+ $info = array('login' => $login,
+ 'md5Password' => $md5Password,
+ 'rememberMe' => $rememberMe,
+ );
+ Piwik_Nonce::discardNonce('Piwik_Login.login');
+ Piwik_PostEvent('Login.initSession', $info);
+ Piwik_Url::redirectToUrl($urlToRedirect);
+ }
+
+ protected function getMessageExceptionNoAccess()
+ {
+ $message = Piwik_Translate('Login_InvalidNonceOrHeadersOrReferer', array('<a href="?module=Proxy&action=redirect&url=' . urlencode('http://piwik.org/faq/how-to-install/#faq_98') . '" target="_blank">', '</a>'));
+ // Should mention trusted_hosts or link to FAQ
+ return $message;
+ }
+
+ /**
+ * Reset password action. Stores new password as hash and sends email
+ * to confirm use.
+ *
+ * @param none
+ * @return void
+ */
+ function resetPassword()
+ {
+ self::checkForceSslLogin();
+
+ $infoMessage = null;
+ $formErrors = null;
+
+ $form = new Piwik_Login_FormResetPassword();
+ if ($form->validate()) {
+ $nonce = $form->getSubmitValue('form_nonce');
+ if (Piwik_Nonce::verifyNonce('Piwik_Login.login', $nonce)) {
+ $formErrors = $this->resetPasswordFirstStep($form);
+ if (empty($formErrors)) {
+ $infoMessage = Piwik_Translate('Login_ConfirmationLinkSent');
+ }
+ } else {
+ $formErrors = array($this->getMessageExceptionNoAccess());
+ }
+ } else {
+ // if invalid, display error
+ $formData = $form->getFormData();
+ $formErrors = $formData['errors'];
+ }
+
+ $view = Piwik_View::factory('message');
+ $view->infoMessage = $infoMessage;
+ $view->formErrors = $formErrors;
+ echo $view->render();
+ }
+
+ /**
+ * Saves password reset info and sends confirmation email.
+ *
+ * @return array Error message(s) if an error occurs.
+ */
+ private function resetPasswordFirstStep($form)
+ {
+ $loginMail = $form->getSubmitValue('form_login');
+ $token = $form->getSubmitValue('form_token');
+ $password = $form->getSubmitValue('form_password');
+
+ // check the password
+ try {
+ Piwik_UsersManager::checkPassword($password);
+ } catch (Exception $ex) {
+ return array($ex->getMessage());
+ }
+
+ // get the user's login
+ if ($loginMail === 'anonymous') {
+ return array(Piwik_Translate('Login_InvalidUsernameEmail'));
+ }
+
+ $user = self::getUserInformation($loginMail);
+ if ($user === null) {
+ return array(Piwik_Translate('Login_InvalidUsernameEmail'));
+ }
+
+ $login = $user['login'];
+
+ // if valid, store password information in options table, then...
+ Piwik_Login::savePasswordResetInfo($login, $password);
+
+ // ... send email with confirmation link
+ try {
+ $this->sendEmailConfirmationLink($user);
+ } catch (Exception $ex) {
+ // remove password reset info
+ Piwik_Login::removePasswordResetInfo($login);
+
+ return array($ex->getMessage() . '<br/>' . Piwik_Translate('Login_ContactAdmin'));
+ }
+
+ return null;
+ }
+
+ /**
+ * Sends email confirmation link for a password reset request.
+ *
+ * @param array $user User info for the requested password reset.
+ */
+ private function sendEmailConfirmationLink($user)
+ {
+ $login = $user['login'];
+ $email = $user['email'];
+
+ // construct a password reset token from user information
+ $resetToken = self::generatePasswordResetToken($user);
+
+ $ip = Piwik_IP::getIpFromHeader();
+ $url = Piwik_Url::getCurrentUrlWithoutQueryString()
+ . "?module=Login&action=confirmResetPassword&login=" . urlencode($login)
+ . "&resetToken=" . urlencode($resetToken);
+
+ // send email with new password
+ $mail = new Piwik_Mail();
+ $mail->addTo($email, $login);
+ $mail->setSubject(Piwik_Translate('Login_MailTopicPasswordChange'));
+ $bodyText = str_replace(
+ '\n',
+ "\n",
+ sprintf(Piwik_Translate('Login_MailPasswordChangeBody'), $login, $ip, $url)
+ ) . "\n";
+ $mail->setBodyText($bodyText);
+
+ $fromEmailName = Piwik_Config::getInstance()->General['login_password_recovery_email_name'];
+ $fromEmailAddress = Piwik_Config::getInstance()->General['login_password_recovery_email_address'];
+ $mail->setFrom($fromEmailAddress, $fromEmailName);
+ @$mail->send();
+ }
+
+ /**
+ * Password reset confirmation action. Finishes the password reset process.
+ * Users visit this action from a link supplied in an email.
+ */
+ public function confirmResetPassword()
+ {
+ $errorMessage = null;
+
+ $login = Piwik_Common::getRequestVar('login', '');
+ $resetToken = Piwik_Common::getRequestVar('resetToken', '');
+
+ try {
+ // get password reset info & user info
+ $user = self::getUserInformation($login);
+ if ($user === null) {
+ throw new Exception(Piwik_Translate('Login_InvalidUsernameEmail'));
+ }
+
+ // check that the reset token is valid
+ $resetPassword = Piwik_Login::getPasswordToResetTo($login);
+ if ($resetPassword === false || !self::isValidToken($resetToken, $user)) {
+ throw new Exception(Piwik_Translate('Login_InvalidOrExpiredToken'));
+ }
+
+ // reset password of user
+ $this->setNewUserPassword($user, $resetPassword);
+ } catch (Exception $ex) {
+ $errorMessage = $ex->getMessage();
+ }
+
+ if (is_null($errorMessage)) // if success, show login w/ success message
+ {
+ $this->redirectToIndex('Login', 'resetPasswordSuccess');
+ } else {
+ // show login page w/ error. this will keep the token in the URL
+ return $this->login($errorMessage);
+ }
+ }
+
+ /**
+ * Sets the password for a user.
+ *
+ * @param array $user User info.
+ * @param string $passwordHash The hashed password to use.
+ */
+ private function setNewUserPassword($user, $passwordHash)
+ {
+ if (strlen($passwordHash) !== 32) // sanity check
+ {
+ throw new Exception(
+ "setNewUserPassword called w/ incorrect password hash. Something has gone terribly wrong.");
+ }
+
+ if ($user['email'] == Piwik::getSuperUserEmail()) {
+ if (!Piwik_Config::getInstance()->isFileWritable()) {
+ throw new Exception(Piwik_Translate('General_ConfigFileIsNotWritable', array("(config/config.ini.php)", "<br/>")));
+ }
+
+ $user['password'] = $passwordHash;
+ Piwik_Config::getInstance()->superuser = $user;
+ Piwik_Config::getInstance()->forceSave();
+ } else {
+ Piwik_UsersManager_API::getInstance()->updateUser(
+ $user['login'], $passwordHash, $email = false, $alias = false, $isPasswordHashed = true);
+ }
+ }
+
+ /**
+ * The action used after a password is successfully reset. Displays the login
+ * screen with an extra message. A separate action is used instead of returning
+ * the HTML in confirmResetPassword so the resetToken won't be in the URL.
+ */
+ public function resetPasswordSuccess()
+ {
+ return $this->login($errorMessage = null, $infoMessage = Piwik_Translate('Login_PasswordChanged'));
+ }
+
+ /**
+ * Get user information
+ *
+ * @param string $loginMail user login or email address
+ * @return array ("login" => '...', "email" => '...', "password" => '...') or null, if user not found
+ */
+ protected function getUserInformation($loginMail)
+ {
+ Piwik::setUserIsSuperUser();
+
+ $user = null;
+ if ($loginMail == Piwik::getSuperUserEmail()
+ || $loginMail == Piwik_Config::getInstance()->superuser['login']
+ ) {
+ $user = array(
+ 'login' => Piwik_Config::getInstance()->superuser['login'],
+ 'email' => Piwik::getSuperUserEmail(),
+ 'password' => Piwik_Config::getInstance()->superuser['password'],
+ );
+ } else if (Piwik_UsersManager_API::getInstance()->userExists($loginMail)) {
+ $user = Piwik_UsersManager_API::getInstance()->getUser($loginMail);
+ } else if (Piwik_UsersManager_API::getInstance()->userEmailExists($loginMail)) {
+ $user = Piwik_UsersManager_API::getInstance()->getUserByEmail($loginMail);
+ }
+
+ return $user;
+ }
+
+ /**
+ * Generate a password reset token. Expires in (roughly) 24 hours.
+ *
+ * @param array user information
+ * @param int $timestamp Unix timestamp
+ * @return string generated token
+ */
+ protected function generatePasswordResetToken($user, $timestamp = null)
+ {
+ /*
+ * Piwik does not store the generated password reset token.
+ * This avoids a database schema change and SQL queries to store, retrieve, and purge (expired) tokens.
+ */
+ if (!$timestamp) {
+ $timestamp = time() + 24 * 60 * 60; /* +24 hrs */
+ }
+
+ $expiry = strftime('%Y%m%d%H', $timestamp);
+ $token = $this->generateHash(
+ $expiry . $user['login'] . $user['email'],
+ $user['password']
+ );
+ return $token;
+ }
+
+ /**
+ * Validate token.
+ *
+ * @param string $token
+ * @param array $user user information
+ * @return bool true if valid, false otherwise
+ */
+ protected function isValidToken($token, $user)
+ {
+ $now = time();
+
+ // token valid for 24 hrs (give or take, due to the coarse granularity in our strftime format string)
+ for ($i = 0; $i <= 24; $i++) {
+ $generatedToken = self::generatePasswordResetToken($user, $now + $i * 60 * 60);
+ if ($generatedToken === $token) {
+ return true;
+ }
+ }
+
+ // fails if token is invalid, expired, password already changed, other user information has changed, ...
+ return false;
+ }
+
+ /**
+ * Clear session information
+ *
+ * @param none
+ * @return void
+ */
+ static public function clearSession()
+ {
+ $authCookieName = Piwik_Config::getInstance()->General['login_cookie_name'];
+ $cookie = new Piwik_Cookie($authCookieName);
+ $cookie->delete();
+
+ Piwik_Session::expireSessionCookie();
+ }
+
+ /**
+ * Logout current user
+ *
+ * @param none
+ * @return void
+ */
+ public function logout()
+ {
+ self::clearSession();
+
+ $logoutUrl = @Piwik_Config::getInstance()->General['login_logout_url'];
+ if (empty($logoutUrl)) {
+ Piwik::redirectToModule('CoreHome');
+ } else {
+ Piwik_Url::redirectToUrl($logoutUrl);
+ }
+ }
+
+ /**
+ * Check force_ssl_login and redirect if connection isn't secure and not using a reverse proxy
+ *
+ * @param none
+ * @return void
+ */
+ protected function checkForceSslLogin()
+ {
+ $forceSslLogin = Piwik_Config::getInstance()->General['force_ssl_login'];
+ if ($forceSslLogin
+ && !Piwik::isHttps()
+ ) {
+ $url = 'https://'
+ . Piwik_Url::getCurrentHost()
+ . Piwik_Url::getCurrentScriptName()
+ . Piwik_Url::getCurrentQueryString();
+ Piwik_Url::redirectToUrl($url);
+ }
+ }
}
diff --git a/plugins/Login/FormLogin.php b/plugins/Login/FormLogin.php
index 46c02f2f91..a90146f47e 100644
--- a/plugins/Login/FormLogin.php
+++ b/plugins/Login/FormLogin.php
@@ -15,28 +15,28 @@
*/
class Piwik_Login_FormLogin extends Piwik_QuickForm2
{
- function __construct( $id = 'login_form', $method = 'post', $attributes = null, $trackSubmit = false)
- {
- parent::__construct($id, $method, $attributes, $trackSubmit);
- }
+ function __construct($id = 'login_form', $method = 'post', $attributes = null, $trackSubmit = false)
+ {
+ parent::__construct($id, $method, $attributes, $trackSubmit);
+ }
- function init()
- {
- $this->addElement('text', 'form_login')
- ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('General_Username')));
+ function init()
+ {
+ $this->addElement('text', 'form_login')
+ ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('General_Username')));
- $this->addElement('password', 'form_password')
- ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Login_Password')));
+ $this->addElement('password', 'form_password')
+ ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Login_Password')));
- $this->addElement('hidden', 'form_nonce');
+ $this->addElement('hidden', 'form_nonce');
- $this->addElement('checkbox', 'form_rememberme');
+ $this->addElement('checkbox', 'form_rememberme');
- $this->addElement('submit', 'submit');
+ $this->addElement('submit', 'submit');
- // default values
- $this->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
- 'form_rememberme' => 0,
- )));
- }
+ // default values
+ $this->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
+ 'form_rememberme' => 0,
+ )));
+ }
}
diff --git a/plugins/Login/FormResetPassword.php b/plugins/Login/FormResetPassword.php
index 88791060d4..0411beddbb 100644
--- a/plugins/Login/FormResetPassword.php
+++ b/plugins/Login/FormResetPassword.php
@@ -15,25 +15,25 @@
*/
class Piwik_Login_FormResetPassword extends Piwik_QuickForm2
{
- function __construct( $id = 'resetpasswordform', $method = 'post', $attributes = null, $trackSubmit = false)
- {
- parent::__construct($id, $method, $attributes, $trackSubmit);
- }
+ function __construct($id = 'resetpasswordform', $method = 'post', $attributes = null, $trackSubmit = false)
+ {
+ parent::__construct($id, $method, $attributes, $trackSubmit);
+ }
- function init()
- {
- $this->addElement('text', 'form_login')
- ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('General_Username')));
+ function init()
+ {
+ $this->addElement('text', 'form_login')
+ ->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('General_Username')));
- $password = $this->addElement('password', 'form_password');
- $password->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Login_Password')));
+ $password = $this->addElement('password', 'form_password');
+ $password->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Login_Password')));
- $passwordBis = $this->addElement('password', 'form_password_bis');
- $passwordBis->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Login_PasswordRepeat')));
- $passwordBis->addRule('eq', Piwik_Translate( 'Login_PasswordsDoNotMatch'), $password);
+ $passwordBis = $this->addElement('password', 'form_password_bis');
+ $passwordBis->addRule('required', Piwik_Translate('General_Required', Piwik_Translate('Login_PasswordRepeat')));
+ $passwordBis->addRule('eq', Piwik_Translate('Login_PasswordsDoNotMatch'), $password);
- $this->addElement('hidden', 'form_nonce');
+ $this->addElement('hidden', 'form_nonce');
- $this->addElement('submit', 'submit');
- }
+ $this->addElement('submit', 'submit');
+ }
}
diff --git a/plugins/Login/Login.php b/plugins/Login/Login.php
index e56c4560e2..5b92eaca87 100644
--- a/plugins/Login/Login.php
+++ b/plugins/Login/Login.php
@@ -15,183 +15,180 @@
*/
class Piwik_Login extends Piwik_Plugin
{
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('Login_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- return $info;
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'FrontController.initAuthenticationObject' => 'initAuthenticationObject',
- 'FrontController.NoAccessException' => 'noAccess',
- 'API.Request.authenticate' => 'ApiRequestAuthenticate',
- 'Login.initSession' => 'initSession',
- );
- return $hooks;
- }
-
- /**
- * Redirects to Login form with error message.
- * Listens to FrontController.NoAccessException hook.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function noAccess( $notification )
- {
- /* @var Exception $exception */
- $exception = $notification->getNotificationObject();
- $exceptionMessage = $exception->getMessage();
-
- $controller = new Piwik_Login_Controller();
- $controller->login($exceptionMessage, '' /* $exception->getTraceAsString() */ );
- }
-
- /**
- * Set login name and autehntication token for authentication request.
- * Listens to API.Request.authenticate hook.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function ApiRequestAuthenticate($notification)
- {
- $tokenAuth = $notification->getNotificationObject();
- Zend_Registry::get('auth')->setLogin($login = null);
- Zend_Registry::get('auth')->setTokenAuth($tokenAuth);
- }
-
- /**
- * Initializes the authentication object.
- * Listens to FrontController.initAuthenticationObject hook.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function initAuthenticationObject($notification)
- {
- $auth = new Piwik_Login_Auth();
- Zend_Registry::set('auth', $auth);
-
- $allowCookieAuthentication = $notification->getNotificationInfo();
-
- $action = Piwik::getAction();
- if (Piwik::getModule() === 'API'
- && (empty($action) || $action == 'index')
- && $allowCookieAuthentication !== true)
- {
- return;
- }
-
- $authCookieName = Piwik_Config::getInstance()->General['login_cookie_name'];
- $authCookieExpiry = 0;
- $authCookiePath = Piwik_Config::getInstance()->General['login_cookie_path'];
- $authCookie = new Piwik_Cookie($authCookieName, $authCookieExpiry, $authCookiePath);
- $defaultLogin = 'anonymous';
- $defaultTokenAuth = 'anonymous';
- if($authCookie->isCookieFound())
- {
- $defaultLogin = $authCookie->get('login');
- $defaultTokenAuth = $authCookie->get('token_auth');
- }
- $auth->setLogin($defaultLogin);
- $auth->setTokenAuth($defaultTokenAuth);
- }
-
- /**
- * Authenticate user and initializes the session.
- * Listens to Login.initSession hook.
- *
- * @param Piwik_Event_Notification $notification notification object
- * @throws Exception
- */
- function initSession($notification)
- {
- $info = $notification->getNotificationObject();
- $login = $info['login'];
- $md5Password = $info['md5Password'];
- $rememberMe = $info['rememberMe'];
-
- $tokenAuth = Piwik_UsersManager_API::getInstance()->getTokenAuth($login, $md5Password);
-
- $auth = Zend_Registry::get('auth');
- $auth->setLogin($login);
- $auth->setTokenAuth($tokenAuth);
- $authResult = $auth->authenticate();
-
- $authCookieName = Piwik_Config::getInstance()->General['login_cookie_name'];
- $authCookieExpiry = $rememberMe ? time() + Piwik_Config::getInstance()->General['login_cookie_expire'] : 0;
- $authCookiePath = Piwik_Config::getInstance()->General['login_cookie_path'];
- $cookie = new Piwik_Cookie($authCookieName, $authCookieExpiry, $authCookiePath);
- if(!$authResult->isValid())
-
- {
- $cookie->delete();
- throw new Exception(Piwik_Translate('Login_LoginPasswordNotCorrect'));
- }
-
- $cookie->set('login', $login);
- $cookie->set('token_auth', $auth->getHashTokenAuth($login, $authResult->getTokenAuth()));
- $cookie->setSecure(Piwik::isHttps());
- $cookie->setHttpOnly(true);
- $cookie->save();
-
- @Piwik_Session::regenerateId();
-
- // remove password reset entry if it exists
- self::removePasswordResetInfo($login);
- }
-
- /**
- * Stores password reset info for a specific login.
- *
- * @param string $login The user login for whom a password change was requested.
- * @param string $password The new password to set.
- */
- public static function savePasswordResetInfo( $login, $password )
- {
- $optionName = self::getPasswordResetInfoOptionName($login);
- $optionData = Piwik_UsersManager::getPasswordHash($password);
-
- Piwik_SetOption($optionName, $optionData);
- }
-
- /**
- * Removes stored password reset info if it exists.
- *
- * @param string $login The user login to check for.
- */
- public static function removePasswordResetInfo( $login )
- {
- $optionName = self::getPasswordResetInfoOptionName($login);
- Piwik_Option::getInstance()->delete($optionName);
- }
-
- /**
- * Gets password hash stored in password reset info.
- *
- * @param string $login The user login to check for.
- * @return string|false The hashed password or false if no reset info exists.
- */
- public static function getPasswordToResetTo( $login )
- {
- $optionName = self::getPasswordResetInfoOptionName($login);
- return Piwik_GetOption($optionName);
- }
-
- /**
- * Gets the option name for the option that will store a user's password change
- * request.
- *
- * @param string $login The user login for whom a password change was requested.
- * @return string
- */
- public static function getPasswordResetInfoOptionName( $login )
- {
- return $login.'_reset_password_info';
- }
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('Login_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'FrontController.initAuthenticationObject' => 'initAuthenticationObject',
+ 'FrontController.NoAccessException' => 'noAccess',
+ 'API.Request.authenticate' => 'ApiRequestAuthenticate',
+ 'Login.initSession' => 'initSession',
+ );
+ return $hooks;
+ }
+
+ /**
+ * Redirects to Login form with error message.
+ * Listens to FrontController.NoAccessException hook.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function noAccess($notification)
+ {
+ /* @var Exception $exception */
+ $exception = $notification->getNotificationObject();
+ $exceptionMessage = $exception->getMessage();
+
+ $controller = new Piwik_Login_Controller();
+ $controller->login($exceptionMessage, '' /* $exception->getTraceAsString() */);
+ }
+
+ /**
+ * Set login name and autehntication token for authentication request.
+ * Listens to API.Request.authenticate hook.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function ApiRequestAuthenticate($notification)
+ {
+ $tokenAuth = $notification->getNotificationObject();
+ Zend_Registry::get('auth')->setLogin($login = null);
+ Zend_Registry::get('auth')->setTokenAuth($tokenAuth);
+ }
+
+ /**
+ * Initializes the authentication object.
+ * Listens to FrontController.initAuthenticationObject hook.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function initAuthenticationObject($notification)
+ {
+ $auth = new Piwik_Login_Auth();
+ Zend_Registry::set('auth', $auth);
+
+ $allowCookieAuthentication = $notification->getNotificationInfo();
+
+ $action = Piwik::getAction();
+ if (Piwik::getModule() === 'API'
+ && (empty($action) || $action == 'index')
+ && $allowCookieAuthentication !== true
+ ) {
+ return;
+ }
+
+ $authCookieName = Piwik_Config::getInstance()->General['login_cookie_name'];
+ $authCookieExpiry = 0;
+ $authCookiePath = Piwik_Config::getInstance()->General['login_cookie_path'];
+ $authCookie = new Piwik_Cookie($authCookieName, $authCookieExpiry, $authCookiePath);
+ $defaultLogin = 'anonymous';
+ $defaultTokenAuth = 'anonymous';
+ if ($authCookie->isCookieFound()) {
+ $defaultLogin = $authCookie->get('login');
+ $defaultTokenAuth = $authCookie->get('token_auth');
+ }
+ $auth->setLogin($defaultLogin);
+ $auth->setTokenAuth($defaultTokenAuth);
+ }
+
+ /**
+ * Authenticate user and initializes the session.
+ * Listens to Login.initSession hook.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ * @throws Exception
+ */
+ function initSession($notification)
+ {
+ $info = $notification->getNotificationObject();
+ $login = $info['login'];
+ $md5Password = $info['md5Password'];
+ $rememberMe = $info['rememberMe'];
+
+ $tokenAuth = Piwik_UsersManager_API::getInstance()->getTokenAuth($login, $md5Password);
+
+ $auth = Zend_Registry::get('auth');
+ $auth->setLogin($login);
+ $auth->setTokenAuth($tokenAuth);
+ $authResult = $auth->authenticate();
+
+ $authCookieName = Piwik_Config::getInstance()->General['login_cookie_name'];
+ $authCookieExpiry = $rememberMe ? time() + Piwik_Config::getInstance()->General['login_cookie_expire'] : 0;
+ $authCookiePath = Piwik_Config::getInstance()->General['login_cookie_path'];
+ $cookie = new Piwik_Cookie($authCookieName, $authCookieExpiry, $authCookiePath);
+ if (!$authResult->isValid()) {
+ $cookie->delete();
+ throw new Exception(Piwik_Translate('Login_LoginPasswordNotCorrect'));
+ }
+
+ $cookie->set('login', $login);
+ $cookie->set('token_auth', $auth->getHashTokenAuth($login, $authResult->getTokenAuth()));
+ $cookie->setSecure(Piwik::isHttps());
+ $cookie->setHttpOnly(true);
+ $cookie->save();
+
+ @Piwik_Session::regenerateId();
+
+ // remove password reset entry if it exists
+ self::removePasswordResetInfo($login);
+ }
+
+ /**
+ * Stores password reset info for a specific login.
+ *
+ * @param string $login The user login for whom a password change was requested.
+ * @param string $password The new password to set.
+ */
+ public static function savePasswordResetInfo($login, $password)
+ {
+ $optionName = self::getPasswordResetInfoOptionName($login);
+ $optionData = Piwik_UsersManager::getPasswordHash($password);
+
+ Piwik_SetOption($optionName, $optionData);
+ }
+
+ /**
+ * Removes stored password reset info if it exists.
+ *
+ * @param string $login The user login to check for.
+ */
+ public static function removePasswordResetInfo($login)
+ {
+ $optionName = self::getPasswordResetInfoOptionName($login);
+ Piwik_Option::getInstance()->delete($optionName);
+ }
+
+ /**
+ * Gets password hash stored in password reset info.
+ *
+ * @param string $login The user login to check for.
+ * @return string|false The hashed password or false if no reset info exists.
+ */
+ public static function getPasswordToResetTo($login)
+ {
+ $optionName = self::getPasswordResetInfoOptionName($login);
+ return Piwik_GetOption($optionName);
+ }
+
+ /**
+ * Gets the option name for the option that will store a user's password change
+ * request.
+ *
+ * @param string $login The user login for whom a password change was requested.
+ * @return string
+ */
+ public static function getPasswordResetInfoOptionName($login)
+ {
+ return $login . '_reset_password_info';
+ }
}
diff --git a/plugins/Login/templates/header.tpl b/plugins/Login/templates/header.tpl
index 6b27f66710..e4e1919633 100644
--- a/plugins/Login/templates/header.tpl
+++ b/plugins/Login/templates/header.tpl
@@ -1,60 +1,64 @@
<!DOCTYPE html>
-<!--[if lt IE 9 ]><html class="old-ie"> <![endif]-->
-<!--[if (gte IE 9)|!(IE)]><!--><html><!--<![endif]-->
+<!--[if lt IE 9 ]>
+<html class="old-ie"> <![endif]-->
+<!--[if (gte IE 9)|!(IE)]><!-->
+<html><!--<![endif]-->
<head>
- <title>{if !$isCustomLogo}Piwik &rsaquo; {/if}{'Login_LogIn'|translate}</title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico" />
- <link rel="stylesheet" type="text/css" href="plugins/Login/templates/login.css" />
- <meta name="description" content="{'General_OpenSourceWebAnalytics'|translate|escape}" />
- <!--[if lt IE 9]>
- <script src="libs/html5shiv/html5shiv.js"></script>
- <![endif]-->
- <script type="text/javascript" src="libs/jquery/jquery.js"></script>
- <script type="text/javascript" src="libs/jquery/jquery.placeholder.js"></script>
-{if isset($forceSslLogin) && $forceSslLogin}
-{literal}
- <script type="text/javascript">
- if(window.location.protocol !== 'https:') {
- var url = window.location.toString();
- url = url.replace(/^http:/, 'https:');
- window.location.replace(url);
- }
- </script>
-{/literal}
-{/if}
-{literal}
- <script type="text/javascript">
- $(function() {
- $('#form_login').focus();
- $('input').placeholder();
- });
- </script>
-{/literal}
- <script type="text/javascript" src="plugins/Login/templates/login.js"></script>
-{if 'General_LayoutDirection'|translate =='rtl'}
-<link rel="stylesheet" type="text/css" href="themes/default/rtl.css" />
-{/if}
-{include file="CoreHome/templates/iframe_buster_header.tpl"}
+ <title>{if !$isCustomLogo}Piwik &rsaquo; {/if}{'Login_LogIn'|translate}</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico"/>
+ <link rel="stylesheet" type="text/css" href="plugins/Login/templates/login.css"/>
+ <meta name="description" content="{'General_OpenSourceWebAnalytics'|translate|escape}"/>
+ <!--[if lt IE 9]>
+ <script src="libs/html5shiv/html5shiv.js"></script>
+ <![endif]-->
+ <script type="text/javascript" src="libs/jquery/jquery.js"></script>
+ <script type="text/javascript" src="libs/jquery/jquery.placeholder.js"></script>
+ {if isset($forceSslLogin) && $forceSslLogin}
+ {literal}
+ <script type="text/javascript">
+ if (window.location.protocol !== 'https:') {
+ var url = window.location.toString();
+ url = url.replace(/^http:/, 'https:');
+ window.location.replace(url);
+ }
+ </script>
+ {/literal}
+ {/if}
+ {literal}
+ <script type="text/javascript">
+ $(function () {
+ $('#form_login').focus();
+ $('input').placeholder();
+ });
+ </script>
+ {/literal}
+ <script type="text/javascript" src="plugins/Login/templates/login.js"></script>
+ {if 'General_LayoutDirection'|translate =='rtl'}
+ <link rel="stylesheet" type="text/css" href="themes/default/rtl.css"/>
+ {/if}
+ {include file="CoreHome/templates/iframe_buster_header.tpl"}
</head>
<body class="login">
{include file="CoreHome/templates/iframe_buster_body.tpl"}
- <div id="logo">
- {if !$isCustomLogo}<a href="http://piwik.org" title="{$linkTitle}">{/if}
- {if $hasSVGLogo}
- <img src='{$logoSVG}' title="{$linkTitle}" alt="Piwik" width="240" style='margin-right: 20px' class="ie-hide" />
- <!--[if lt IE 9]>
- {/if}
- <img src='{$logoLarge}' title="{$linkTitle}" alt="Piwik" width="240" style='margin-right:20px' />
- {if $hasSVGLogo}<![endif]-->{/if}
- {if $isCustomLogo}
- {capture name='poweredByPiwik'}
- <i><a href="http://piwik.org/" target="_blank">{$linkTitle}</a></i>
- {/capture}
- {/if}
- {if !$isCustomLogo}</a>
- <div class="description"><a href="http://piwik.org" title="{$linkTitle}">{$linkTitle}</a>
- <div class="arrow"> </div>
- </div>
- {/if}
- </div>
+<div id="logo">
+ {if !$isCustomLogo}<a href="http://piwik.org" title="{$linkTitle}">{/if}
+ {if $hasSVGLogo}
+ <img src='{$logoSVG}' title="{$linkTitle}" alt="Piwik" width="240" style='margin-right: 20px' class="ie-hide"/>
+ <!--[if lt IE 9]>
+ {/if}
+ <img src='{$logoLarge}' title="{$linkTitle}" alt="Piwik" width="240" style='margin-right:20px'/>
+ {if $hasSVGLogo}<![endif]-->{/if}
+ {if $isCustomLogo}
+ {capture name='poweredByPiwik'}
+ <i><a href="http://piwik.org/" target="_blank">{$linkTitle}</a></i>
+ {/capture}
+ {/if}
+ {if !$isCustomLogo}</a>
+
+ <div class="description"><a href="http://piwik.org" title="{$linkTitle}">{$linkTitle}</a>
+
+ <div class="arrow"></div>
+ </div>
+ {/if}
+</div>
diff --git a/plugins/Login/templates/login.css b/plugins/Login/templates/login.css
index d8c3f3d87f..7d62cea47a 100644
--- a/plugins/Login/templates/login.css
+++ b/plugins/Login/templates/login.css
@@ -1,227 +1,224 @@
/* shamelessly taken from wordpress 2.5 - thank you guys!!! */
* {
- margin: 0;
- padding: 0;
+ margin: 0;
+ padding: 0;
}
body {
- font: 12px "Lucida Grande", "Lucida Sans Unicode", Tahoma, Verdana, sans-serif;
+ font: 12px "Lucida Grande", "Lucida Sans Unicode", Tahoma, Verdana, sans-serif;
}
form {
- padding: 16px 16px 16px 16px;
- border-radius: 5px;
+ padding: 16px 16px 16px 16px;
+ border-radius: 5px;
}
#login form input.submit {
- font-family: "Lucida Grande", "Lucida Sans Unicode", Tahoma, Verdana, sans-serif;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- float: right;
- height: 35px;
- padding: 0 20px;
- cursor: pointer;
- font: bold 15px Arial, Helvetica;
+ font-family: "Lucida Grande", "Lucida Sans Unicode", Tahoma, Verdana, sans-serif;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ float: right;
+ height: 35px;
+ padding: 0 20px;
+ cursor: pointer;
+ font: bold 15px Arial, Helvetica;
}
#login form div {
- margin-bottom: 24px;
+ margin-bottom: 24px;
}
-.updated,.login #login_error,.login .message {
- background-color: #ffffe0;
- border-color: #e6db55;
- margin: 0 auto;
- width: 330px;
+.updated, .login #login_error, .login .message {
+ background-color: #ffffe0;
+ border-color: #e6db55;
+ margin: 0 auto;
+ width: 330px;
}
#login fieldset {
- border: 0;
+ border: 0;
}
#login fieldset.actions {
- line-height: 35px;
- width: 315px;
- margin-top: 10px;
-}
-
-#login h1
-{
- text-align: center;
- color: #666;
- margin: 0 0 30px 0;
- font: normal 26px/1 Verdana, Helvetica;
- position: relative;
-}
-
-#login
-{
- background-color: #fafafa;
- width: 360px;
- padding: 30px;
- margin: 50px auto 0 auto;
- z-index: 0;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- -webkit-box-shadow:
- 0 0 2px rgba(0, 0, 0, 0.2),
- 0 1px 1px rgba(0, 0, 0, .2);
- -moz-box-shadow:
- 0 0 2px rgba(0, 0, 0, 0.2),
- 1px 1px 0 rgba(0, 0, 0, .1);
- box-shadow:
- 0 0 2px rgba(0, 0, 0, 0.2),
- 0 1px 1px rgba(0, 0, 0, .2);
-}
-
-#login form { margin: 0 5px; position: relative; }
+ line-height: 35px;
+ width: 315px;
+ margin-top: 10px;
+}
+
+#login h1 {
+ text-align: center;
+ color: #666;
+ margin: 0 0 30px 0;
+ font: normal 26px/1 Verdana, Helvetica;
+ position: relative;
+}
+
+#login {
+ background-color: #fafafa;
+ width: 360px;
+ padding: 30px;
+ margin: 50px auto 0 auto;
+ z-index: 0;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -webkit-box-shadow: 0 0 2px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, .2);
+ -moz-box-shadow: 0 0 2px rgba(0, 0, 0, 0.2), 1px 1px 0 rgba(0, 0, 0, .1);
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, .2);
+}
+
+#login form {
+ margin: 0 5px;
+ position: relative;
+}
+
#login form input[type="text"],
#login form input[type="password"] {
- padding: 10px 15px 10px 45px;
- margin: 0 0 15px 0;
- width: 253px; /* 258 + 2 + 55 = 315 */
- border: 1px solid #ccc;
- -moz-border-radius: 5px;
- -webkit-border-radius: 5px;
- border-radius: 5px;
- -moz-box-shadow: 0 1px 1px #ccc inset, 0 1px 0 #fff;
- -webkit-box-shadow: 0 1px 1px #ccc inset, 0 1px 0 #fff;
- box-shadow: 0 1px 1px #ccc inset, 0 1px 0 #fff;
+ padding: 10px 15px 10px 45px;
+ margin: 0 0 15px 0;
+ width: 253px; /* 258 + 2 + 55 = 315 */
+ border: 1px solid #ccc;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ -moz-box-shadow: 0 1px 1px #ccc inset, 0 1px 0 #fff;
+ -webkit-box-shadow: 0 1px 1px #ccc inset, 0 1px 0 #fff;
+ box-shadow: 0 1px 1px #ccc inset, 0 1px 0 #fff;
}
#login_form_rememberme {
- vertical-align: middle;
+ vertical-align: middle;
}
-#login_error,.message {
- margin: 0 0 16px 8px;
- border: 1px solid;
- padding: 12px;
+#login_error, .message {
+ margin: 0 0 16px 8px;
+ border: 1px solid;
+ padding: 12px;
}
#nav, #piwik {
- margin: 0 0 0 8px;
- padding: 16px;
+ margin: 0 0 0 8px;
+ padding: 16px;
}
#nav {
- text-align: center;
+ text-align: center;
}
#nav a:hover {
- text-decoration: underline;
+ text-decoration: underline;
}
-#login_form_password, #reset_form_password,
+#login_form_password, #reset_form_password,
#reset_form_password_bis, #login_form_login, #reset_form_login {
- background: #fff url(../../../themes/default/images/login-sprite.png) no-repeat;
+ background: #fff url(../../../themes/default/images/login-sprite.png) no-repeat;
}
#login_form_password, #reset_form_password, #reset_form_password_bis {
- background-position: 10px -51px !important;
+ background-position: 10px -51px !important;
}
#login_form_login, #reset_form_login {
- background-position: 10px 11px !important;
+ background-position: 10px 11px !important;
}
-#login_form_password,#reset_form_password,#reset_form_password_bis,
-#login_form_login,#reset_form_login {
- font-size: 20px;
- width: 97%;
- padding: 3px;
- margin-right: 6px;
+#login_form_password, #reset_form_password, #reset_form_password_bis,
+#login_form_login, #reset_form_login {
+ font-size: 20px;
+ width: 97%;
+ padding: 3px;
+ margin-right: 6px;
}
#login #login_error {
- background-color: #ffebe8;
- border-color: #c00;
+ background-color: #ffebe8;
+ border-color: #c00;
}
#login form input.submit {
- background-color: #e5e5e5;
+ background-color: #e5e5e5;
}
#login form input.submit:hover {
- background-color: #eee;
+ background-color: #eee;
}
.login #login_error {
- background-color: #ffffe0;
- border-color: #e6db55;
+ background-color: #ffffe0;
+ border-color: #e6db55;
}
.login #nav a {
- color: #777;
+ color: #777;
}
+
.login #piwik a {
- color: #CDCDCD;
+ color: #CDCDCD;
}
body.login {
- border-top-color: #464646;
+ border-top-color: #464646;
}
#login form input {
- color: #555;
+ color: #555;
}
a {
- text-decoration: none;
+ text-decoration: none;
}
#logo {
- margin: 100px auto 0 auto;
- width: 240px;
- position: relative;
+ margin: 100px auto 0 auto;
+ width: 240px;
+ position: relative;
}
#logo .description a {
- font:16px/16px 'Patrick Hand';
- color:#666666;
- right: auto;
- text-decoration: none;
+ font: 16px/16px 'Patrick Hand';
+ color: #666666;
+ right: auto;
+ text-decoration: none;
}
#logo .description {
- position: absolute;
- left: -40px !important;
- top: -30px !important;
- -webkit-transform:rotate(-6deg);
- -moz-transform:rotate(-6deg);
- -ms-transform:rotate(-6deg);
- -o-transform:rotate(-6deg);
+ position: absolute;
+ left: -40px !important;
+ top: -30px !important;
+ -webkit-transform: rotate(-6deg);
+ -moz-transform: rotate(-6deg);
+ -ms-transform: rotate(-6deg);
+ -o-transform: rotate(-6deg);
}
#logo .description .arrow {
- background:url(../../../themes/default/images/affix-arrow.png);
- width:50px;
- height:68px;
- position:absolute;
- left:-35px;
+ background: url(../../../themes/default/images/affix-arrow.png);
+ width: 50px;
+ height: 68px;
+ position: absolute;
+ left: -35px;
}
#logo img {
- border:0;
- vertical-align: bottom;
- width: 260px;
+ border: 0;
+ vertical-align: bottom;
+ width: 260px;
}
#logo .h1 {
- font-family: Georgia, "Times New Roman", Times, serif;
- font-weight: normal;
- color: #136F8B;
- font-size: 45pt;
- text-transform: none;
+ font-family: Georgia, "Times New Roman", Times, serif;
+ font-weight: normal;
+ color: #136F8B;
+ font-size: 45pt;
+ text-transform: none;
}
.loadingPiwik {
- float: left;
- margin-left: 16px;
+ float: left;
+ margin-left: 16px;
}
/* IE < 9 will use this */
-html.old-ie .ie-hide {
- display: none;
+html.old-ie .ie-hide {
+ display: none;
}
diff --git a/plugins/Login/templates/login.js b/plugins/Login/templates/login.js
index 28171b47ab..5458942fef 100755
--- a/plugins/Login/templates/login.js
+++ b/plugins/Login/templates/login.js
@@ -4,104 +4,97 @@
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function($) {
-
-$(document).ready(function() {
- var switchForm = function(fromFormId, toFormId, message, callback) {
- var fromLoginInputId = '#'+fromFormId+'_login',
- toLoginInputId = '#'+toFormId+'_login',
- toPasswordInputId = '#'+toFormId+'_password',
- fromLoginNavId = '#'+fromFormId+'_nav',
- toLoginNavId = '#'+toFormId+'_nav';
-
- if ($(toLoginInputId).val() === '')
- {
- $(toLoginInputId).val($(fromLoginInputId).val());
- }
-
- // hide the bottom portion of the login screen & show the password reset bits
- $('#'+fromFormId+',#message_container').fadeOut(500, function() {
- // show lost password instructions
- $('#message_container').html(message);
-
- $(fromLoginNavId).hide();
- $(toLoginNavId).show();
- $('#'+toFormId+',#message_container').fadeIn(500, function() {
- // focus on login or password control based on whether a login exists
- if ($(toLoginInputId).val() === '')
- {
- $(toLoginInputId).focus();
- }
- else
- {
- $(toPasswordInputId).focus();
- }
-
- if (callback)
- {
- callback();
- }
- });
- });
- };
-
- // 'lost your password?' on click
- $('#login_form_nav').click(function(e) {
- e.preventDefault();
- switchForm('login_form', 'reset_form', $('#lost_password_instructions').html());
- return false;
- });
-
- // 'cancel' on click
- $('#reset_form_nav,#alternate_reset_nav').click(function(e) {
- e.preventDefault();
- $('#alternate_reset_nav').hide();
- switchForm('reset_form', 'login_form', '');
- return false;
- });
-
- // password reset on submit
- $('#reset_form_submit').click(function(e) {
- e.preventDefault();
-
- var ajaxDone = function(response)
- {
- $('.loadingPiwik').hide();
-
- var isSuccess = response.indexOf('id="login_error"') === -1,
- fadeOutIds = '#message_container';
- if (isSuccess)
- {
- fadeOutIds += ',#reset_form,#reset_form_nav';
- }
-
- $(fadeOutIds).fadeOut(300, function() {
- if (isSuccess)
- {
- $('#alternate_reset_nav').show();
- }
-
- $('#message_container').html(response).fadeIn(300);
- });
- };
-
- $('.loadingPiwik').show();
-
- // perform reset password request
- $.ajax({
- type: 'POST',
- url: 'index.php',
- dataType: 'html',
- async: true,
- error: function() { ajaxDone('<div id="login_error"><strong>HTTP Error</strong></div>'); },
- success: ajaxDone, // Callback when the request succeeds
- data: $('#reset_form').serialize()
- });
-
- return false;
- });
-
- $('#login_form_login').focus();
-});
+(function ($) {
+
+ $(document).ready(function () {
+ var switchForm = function (fromFormId, toFormId, message, callback) {
+ var fromLoginInputId = '#' + fromFormId + '_login',
+ toLoginInputId = '#' + toFormId + '_login',
+ toPasswordInputId = '#' + toFormId + '_password',
+ fromLoginNavId = '#' + fromFormId + '_nav',
+ toLoginNavId = '#' + toFormId + '_nav';
+
+ if ($(toLoginInputId).val() === '') {
+ $(toLoginInputId).val($(fromLoginInputId).val());
+ }
+
+ // hide the bottom portion of the login screen & show the password reset bits
+ $('#' + fromFormId + ',#message_container').fadeOut(500, function () {
+ // show lost password instructions
+ $('#message_container').html(message);
+
+ $(fromLoginNavId).hide();
+ $(toLoginNavId).show();
+ $('#' + toFormId + ',#message_container').fadeIn(500, function () {
+ // focus on login or password control based on whether a login exists
+ if ($(toLoginInputId).val() === '') {
+ $(toLoginInputId).focus();
+ }
+ else {
+ $(toPasswordInputId).focus();
+ }
+
+ if (callback) {
+ callback();
+ }
+ });
+ });
+ };
+
+ // 'lost your password?' on click
+ $('#login_form_nav').click(function (e) {
+ e.preventDefault();
+ switchForm('login_form', 'reset_form', $('#lost_password_instructions').html());
+ return false;
+ });
+
+ // 'cancel' on click
+ $('#reset_form_nav,#alternate_reset_nav').click(function (e) {
+ e.preventDefault();
+ $('#alternate_reset_nav').hide();
+ switchForm('reset_form', 'login_form', '');
+ return false;
+ });
+
+ // password reset on submit
+ $('#reset_form_submit').click(function (e) {
+ e.preventDefault();
+
+ var ajaxDone = function (response) {
+ $('.loadingPiwik').hide();
+
+ var isSuccess = response.indexOf('id="login_error"') === -1,
+ fadeOutIds = '#message_container';
+ if (isSuccess) {
+ fadeOutIds += ',#reset_form,#reset_form_nav';
+ }
+
+ $(fadeOutIds).fadeOut(300, function () {
+ if (isSuccess) {
+ $('#alternate_reset_nav').show();
+ }
+
+ $('#message_container').html(response).fadeIn(300);
+ });
+ };
+
+ $('.loadingPiwik').show();
+
+ // perform reset password request
+ $.ajax({
+ type: 'POST',
+ url: 'index.php',
+ dataType: 'html',
+ async: true,
+ error: function () { ajaxDone('<div id="login_error"><strong>HTTP Error</strong></div>'); },
+ success: ajaxDone, // Callback when the request succeeds
+ data: $('#reset_form').serialize()
+ });
+
+ return false;
+ });
+
+ $('#login_form_login').focus();
+ });
}(jQuery));
diff --git a/plugins/Login/templates/login.tpl b/plugins/Login/templates/login.tpl
index 956f8d4bc8..ad85960ec5 100644
--- a/plugins/Login/templates/login.tpl
+++ b/plugins/Login/templates/login.tpl
@@ -2,84 +2,89 @@
<section id="login">
-{* untrusted host warning *}
-{if isset($isValidHost) && isset($invalidHostMessage) && !$isValidHost}
-<div id="login_error">
- <strong>{'General_Warning'|translate}:&nbsp;</strong>{$invalidHostMessage}
+ {* untrusted host warning *}
+ {if isset($isValidHost) && isset($invalidHostMessage) && !$isValidHost}
+ <div id="login_error">
+ <strong>{'General_Warning'|translate}:&nbsp;</strong>{$invalidHostMessage}
- <br><br>{$invalidHostMessageHowToFix}
- <br/><br/><a style="float:right" href="http://piwik.org/faq/troubleshooting/#faq_171" target="_blank">{'General_Help'|translate} <img style='vertical-align: bottom' src="themes/default/images/help_grey.png" /></a><br/>
+ <br><br>{$invalidHostMessageHowToFix}
+ <br/><br/><a style="float:right" href="http://piwik.org/faq/troubleshooting/#faq_171" target="_blank">{'General_Help'|translate} <img
+ style='vertical-align: bottom' src="themes/default/images/help_grey.png"/></a><br/>
-</div>
-{else}
-<div id="message_container">
- {if $form_data.errors}
- <div id="login_error">
- {foreach from=$form_data.errors item=data}
- <strong>{'General_Error'|translate}</strong>: {$data}<br />
- {/foreach}
- </div>
- {/if}
+ </div>
+ {else}
+ <div id="message_container">
+ {if $form_data.errors}
+ <div id="login_error">
+ {foreach from=$form_data.errors item=data}
+ <strong>{'General_Error'|translate}</strong>
+ : {$data}
+ <br/>
+ {/foreach}
+ </div>
+ {/if}
- {if $AccessErrorString}
- <div id="login_error"><strong>{'General_Error'|translate}</strong>: {$AccessErrorString}<br /></div>
- {/if}
+ {if $AccessErrorString}
+ <div id="login_error"><strong>{'General_Error'|translate}</strong>: {$AccessErrorString}<br/></div>
+ {/if}
- {if $infoMessage}
- <p class="message">{$infoMessage}</p>
- {/if}
-</div>
+ {if $infoMessage}
+ <p class="message">{$infoMessage}</p>
+ {/if}
+ </div>
+ <form {$form_data.attributes}>
+ <h1>{'Login_LogIn'|translate}</h1>
+ <fieldset class="inputs">
+ <input type="text" name="form_login" id="login_form_login" class="input" value="" size="20" tabindex="10"
+ placeholder="{'General_Username'|translate}" autofocus="autofocus"/>
+ <input type="password" name="form_password" id="login_form_password" class="input" value="" size="20" tabindex="20"
+ placeholder="{'Login_Password'|translate}"/>
+ <input type="hidden" name="form_nonce" id="login_form_nonce" value="{$nonce}"/>
+ </fieldset>
-<form {$form_data.attributes}>
- <h1>{'Login_LogIn'|translate}</h1>
- <fieldset class="inputs">
- <input type="text" name="form_login" id="login_form_login" class="input" value="" size="20" tabindex="10" placeholder="{'General_Username'|translate}" autofocus="autofocus" />
- <input type="password" name="form_password" id="login_form_password" class="input" value="" size="20" tabindex="20" placeholder="{'Login_Password'|translate}" />
- <input type="hidden" name="form_nonce" id="login_form_nonce" value="{$nonce}" />
- </fieldset>
+ <fieldset class="actions">
+ <input name="form_rememberme" type="checkbox" id="login_form_rememberme" value="1" tabindex="90"
+ {if $form_data.form_rememberme.value}checked="checked" {/if}/>
+ <label for="login_form_rememberme">{'Login_RememberMe'|translate}</label>
+ <input class="submit" id='login_form_submit' type="submit" value="{'Login_LogIn'|translate}" tabindex="100"/>
+ </fieldset>
+ </form>
+ <form id="reset_form" style="display:none;">
+ <fieldset class="inputs">
+ <input type="text" name="form_login" id="reset_form_login" class="input" value="" size="20" tabindex="10"
+ placeholder="{'Login_LoginOrEmail'|translate}"/>
+ <input type="hidden" name="form_nonce" id="reset_form_nonce" value="{$nonce}"/>
- <fieldset class="actions">
- <input name="form_rememberme" type="checkbox" id="login_form_rememberme" value="1" tabindex="90" {if $form_data.form_rememberme.value}checked="checked" {/if}/>
- <label for="login_form_rememberme">{'Login_RememberMe'|translate}</label>
- <input class="submit" id='login_form_submit' type="submit" value="{'Login_LogIn'|translate}" tabindex="100" />
- </fieldset>
-</form>
+ <input type="password" name="form_password" id="reset_form_password" class="input" value="" size="20" tabindex="20"
+ placeholder="{'Login_Password'|translate}"/>
-<form id="reset_form" style="display:none;">
- <fieldset class="inputs">
- <input type="text" name="form_login" id="reset_form_login" class="input" value="" size="20" tabindex="10" placeholder="{'Login_LoginOrEmail'|translate}" />
- <input type="hidden" name="form_nonce" id="reset_form_nonce" value="{$nonce}" />
+ <input type="password" name="form_password_bis" id="reset_form_password_bis" class="input" value="" size="20" tabindex="30"
+ placeholder="{'Login_PasswordRepeat'|translate}"/>
+ </fieldset>
- <input type="password" name="form_password" id="reset_form_password" class="input" value="" size="20" tabindex="20" placeholder="{'Login_Password'|translate}" />
+ <fieldset class="actions">
+ <span class="loadingPiwik" style="display:none;"><img alt="Loading" src="themes/default/images/loading-blue.gif"/></span>
+ <input class="submit" id='reset_form_submit' type="submit" value="{'Login_ChangePassword'|translate}" tabindex="100"/>
+ </fieldset>
- <input type="password" name="form_password_bis" id="reset_form_password_bis" class="input" value="" size="20" tabindex="30" placeholder="{'Login_PasswordRepeat'|translate}" />
- </fieldset>
-
- <fieldset class="actions">
- <span class="loadingPiwik" style="display:none;"><img alt="Loading" src="themes/default/images/loading-blue.gif" /></span>
- <input class="submit" id='reset_form_submit' type="submit" value="{'Login_ChangePassword'|translate}" tabindex="100"/>
- </fieldset>
-
- <input type="hidden" name="module" value="Login"/>
- <input type="hidden" name="action" value="resetPassword"/>
-</form>
-
-<p id="nav">
-<a id="login_form_nav" href="#" title="{'Login_LostYourPassword'|translate}">{'Login_LostYourPassword'|translate}</a>
-<a id="alternate_reset_nav" href="#" style="display:none;" title="{'Login_LogIn'|translate}">{'Login_LogIn'|translate}</a>
-<a id="reset_form_nav" href="#" style="display:none;" title="{'Mobile_NavigationBack'|translate}">{'General_Cancel'|translate}</a>
-</p>
-{if isset($smarty.capture.poweredByPiwik)}
- <p id="piwik">
- {$smarty.capture.poweredByPiwik}
- </p>
-{/if}
-
-<div id="lost_password_instructions" style="display:none;">
- <p class="message">{'Login_ResetPasswordInstructions'|translate}</p>
-</div>
-{/if}
+ <input type="hidden" name="module" value="Login"/>
+ <input type="hidden" name="action" value="resetPassword"/>
+ </form>
+ <p id="nav">
+ <a id="login_form_nav" href="#" title="{'Login_LostYourPassword'|translate}">{'Login_LostYourPassword'|translate}</a>
+ <a id="alternate_reset_nav" href="#" style="display:none;" title="{'Login_LogIn'|translate}">{'Login_LogIn'|translate}</a>
+ <a id="reset_form_nav" href="#" style="display:none;" title="{'Mobile_NavigationBack'|translate}">{'General_Cancel'|translate}</a>
+ </p>
+ {if isset($smarty.capture.poweredByPiwik)}
+ <p id="piwik">
+ {$smarty.capture.poweredByPiwik}
+ </p>
+ {/if}
+ <div id="lost_password_instructions" style="display:none;">
+ <p class="message">{'Login_ResetPasswordInstructions'|translate}</p>
+ </div>
+ {/if}
</section>
</body>
</html>
diff --git a/plugins/Login/templates/message.tpl b/plugins/Login/templates/message.tpl
index 7b85147d8f..ff2e9d21e3 100755
--- a/plugins/Login/templates/message.tpl
+++ b/plugins/Login/templates/message.tpl
@@ -1,11 +1,13 @@
{if isset($infoMessage)}
-<p class="message">{$infoMessage}</p>
+ <p class="message">{$infoMessage}</p>
{/if}
{if isset($formErrors)}
-<p id="login_error">
- {foreach from=$formErrors item=data}
- <strong>{'General_Error'|translate}</strong>: {$data}<br />
- {/foreach}
-</p>
+ <p id="login_error">
+ {foreach from=$formErrors item=data}
+ <strong>{'General_Error'|translate}</strong>
+ : {$data}
+ <br/>
+ {/foreach}
+ </p>
{/if}
diff --git a/plugins/MobileMessaging/API.php b/plugins/MobileMessaging/API.php
index df212a662f..ae80979245 100644
--- a/plugins/MobileMessaging/API.php
+++ b/plugins/MobileMessaging/API.php
@@ -19,459 +19,442 @@
*/
class Piwik_MobileMessaging_API
{
- const VERIFICATION_CODE_LENGTH = 5;
- const SMS_FROM = 'Piwik';
-
- static private $instance = null;
-
- /**
- * @return Piwik_MobileMessaging_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * @return Piwik_MobileMessaging_SMSProvider
- */
- static private function getSMSProviderInstance($provider)
- {
- return Piwik_MobileMessaging_SMSProvider::factory($provider);
- }
-
- /**
- * determine if SMS API credential are available for the current user
- *
- * @return bool true if SMS API credential are available for the current user
- */
- public function areSMSAPICredentialProvided()
- {
- Piwik::checkUserHasSomeViewAccess();
-
- $credential = $this->getSMSAPICredential();
- return isset($credential[Piwik_MobileMessaging::API_KEY_OPTION]);
- }
-
- private function getSMSAPICredential()
- {
- $settings = $this->getCredentialManagerSettings();
- return array(
- Piwik_MobileMessaging::PROVIDER_OPTION =>
- isset($settings[Piwik_MobileMessaging::PROVIDER_OPTION]) ? $settings[Piwik_MobileMessaging::PROVIDER_OPTION] : null,
- Piwik_MobileMessaging::API_KEY_OPTION =>
- isset($settings[Piwik_MobileMessaging::API_KEY_OPTION]) ? $settings[Piwik_MobileMessaging::API_KEY_OPTION] : null,
- );
- }
-
- /**
- * return the SMS API Provider for the current user
- *
- * @return string SMS API Provider
- */
- public function getSMSProvider()
- {
- $this->checkCredentialManagementRights();
- $credential = $this->getSMSAPICredential();
- return $credential[Piwik_MobileMessaging::PROVIDER_OPTION];
- }
-
- /**
- * set the SMS API credential
- *
- * @param string $provider SMS API provider
- * @param string $apiKey API Key
- *
- * @return bool true if SMS API credential were validated and saved, false otherwise
- */
- public function setSMSAPICredential($provider, $apiKey)
- {
- $this->checkCredentialManagementRights();
-
- $smsProviderInstance = self::getSMSProviderInstance($provider);
- $smsProviderInstance->verifyCredential($apiKey);
-
- $settings = $this->getCredentialManagerSettings();
-
- $settings[Piwik_MobileMessaging::PROVIDER_OPTION] = $provider;
- $settings[Piwik_MobileMessaging::API_KEY_OPTION] = $apiKey;
-
- $this->setCredentialManagerSettings($settings);
-
- return true;
- }
-
- /**
- * add phone number
- *
- * @param string $phoneNumber
- *
- * @return bool true
- */
- public function addPhoneNumber($phoneNumber)
- {
- Piwik::checkUserIsNotAnonymous();
-
- $phoneNumber = self::sanitizePhoneNumber($phoneNumber);
-
- $verificationCode = "";
- for($i = 0; $i < self::VERIFICATION_CODE_LENGTH; $i++)
- {
- $verificationCode .= mt_rand(0,9);
- }
-
- $smsText = Piwik_Translate(
- 'MobileMessaging_VerificationText',
- array(
- $verificationCode,
- Piwik_Translate('UserSettings_SubmenuSettings'),
- Piwik_Translate('MobileMessaging_SettingsMenu')
- )
- );
-
- $this->sendSMS($smsText, $phoneNumber, self::SMS_FROM);
-
- $phoneNumbers = $this->retrievePhoneNumbers();
- $phoneNumbers[$phoneNumber] = $verificationCode;
- $this->savePhoneNumbers($phoneNumbers);
-
- $this->increaseCount(Piwik_MobileMessaging::PHONE_NUMBER_VALIDATION_REQUEST_COUNT_OPTION, $phoneNumber);
-
- return true;
- }
-
- /**
- * sanitize phone number
- *
- * @param string $phoneNumber
- *
- * @return string sanitized phone number
- */
- public static function sanitizePhoneNumber($phoneNumber)
- {
- return str_replace(' ', '', $phoneNumber);
- }
-
- /**
- * send a SMS
- *
- * @param string $phoneNumber
- * @return bool true
- * @ignore
- */
- public function sendSMS($content, $phoneNumber, $from)
- {
- Piwik::checkUserIsNotAnonymous();
-
- $credential = $this->getSMSAPICredential();
- $SMSProvider = self::getSMSProviderInstance($credential[Piwik_MobileMessaging::PROVIDER_OPTION]);
- $SMSProvider->sendSMS(
- $credential[Piwik_MobileMessaging::API_KEY_OPTION],
- $content,
- $phoneNumber,
- $from
- );
-
- $this->increaseCount(Piwik_MobileMessaging::SMS_SENT_COUNT_OPTION, $phoneNumber);
-
- return true;
- }
-
- /**
- * get remaining credit
- *
- * @return string remaining credit
- */
- public function getCreditLeft()
- {
- $this->checkCredentialManagementRights();
-
- $credential = $this->getSMSAPICredential();
- $SMSProvider = self::getSMSProviderInstance($credential[Piwik_MobileMessaging::PROVIDER_OPTION]);
- return $SMSProvider->getCreditLeft(
- $credential[Piwik_MobileMessaging::API_KEY_OPTION]
- );
- }
-
- /**
- * remove phone number
- *
- * @param string $phoneNumber
- *
- * @return bool true
- */
- public function removePhoneNumber($phoneNumber)
- {
- Piwik::checkUserIsNotAnonymous();
-
- $phoneNumbers = $this->retrievePhoneNumbers();
- unset($phoneNumbers[$phoneNumber]);
- $this->savePhoneNumbers($phoneNumbers);
-
- // remove phone number from reports
- $pdfReportsAPIInstance = Piwik_PDFReports_API::getInstance();
- $reports = $pdfReportsAPIInstance->getReports(
- $idSite = false,
- $period = false,
- $idReport = false,
- $ifSuperUserReturnOnlySuperUserReports = $this->getDelegatedManagement()
- );
-
- foreach($reports as $report)
- {
- if ($report['type'] == Piwik_MobileMessaging::MOBILE_TYPE)
- {
- $reportParameters = $report['parameters'];
- $reportPhoneNumbers = $reportParameters[Piwik_MobileMessaging::PHONE_NUMBERS_PARAMETER];
- $updatedPhoneNumbers = array();
- foreach($reportPhoneNumbers as $reportPhoneNumber)
- {
- if($reportPhoneNumber != $phoneNumber)
- {
- $updatedPhoneNumbers[] = $reportPhoneNumber;
- }
- }
-
- if(count($updatedPhoneNumbers) != count($reportPhoneNumbers))
- {
- $reportParameters[Piwik_MobileMessaging::PHONE_NUMBERS_PARAMETER] = $updatedPhoneNumbers;
-
- // note: reports can end up without any recipients
- $pdfReportsAPIInstance->updateReport(
- $report['idreport'],
- $report['idsite'],
- $report['description'],
- $report['period'],
- $report['type'],
- $report['format'],
- $report['reports'],
- $reportParameters
- );
- }
- }
- }
-
- return true;
- }
-
- private function retrievePhoneNumbers()
- {
- $settings = $this->getCurrentUserSettings();
-
- $phoneNumbers = array();
- if(isset($settings[Piwik_MobileMessaging::PHONE_NUMBERS_OPTION]))
- {
- $phoneNumbers = $settings[Piwik_MobileMessaging::PHONE_NUMBERS_OPTION];
- }
-
- return $phoneNumbers;
- }
-
- private function savePhoneNumbers($phoneNumbers)
- {
- $settings = $this->getCurrentUserSettings();
-
- $settings[Piwik_MobileMessaging::PHONE_NUMBERS_OPTION] = $phoneNumbers;
-
- $this->setCurrentUserSettings($settings);
- }
-
- private function increaseCount($option, $phoneNumber)
- {
- $settings = $this->getCurrentUserSettings();
-
- $counts = array();
- if(isset($settings[$option]))
- {
- $counts = $settings[$option];
- }
-
- $countToUpdate = 0;
- if(isset($counts[$phoneNumber]))
- {
- $countToUpdate = $counts[$phoneNumber];
- }
-
- $counts[$phoneNumber] = $countToUpdate + 1;
-
- $settings[$option] = $counts;
-
- $this->setCurrentUserSettings($settings);
- }
-
- /**
- * validate phone number
- *
- * @param string $phoneNumber
- * @param string $verificationCode
- *
- * @return bool true if validation code is correct, false otherwise
- */
- public function validatePhoneNumber($phoneNumber, $verificationCode)
- {
- Piwik::checkUserIsNotAnonymous();
-
- $phoneNumbers = $this->retrievePhoneNumbers();
-
- if(isset($phoneNumbers[$phoneNumber]))
- {
- if($verificationCode == $phoneNumbers[$phoneNumber]) {
-
- $phoneNumbers[$phoneNumber] = null;
- $this->savePhoneNumbers($phoneNumbers);
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * get phone number list
- *
- * @return array $phoneNumber => $isValidated
- * @ignore
- */
- public function getPhoneNumbers()
- {
- Piwik::checkUserIsNotAnonymous();
-
- $rawPhoneNumbers = $this->retrievePhoneNumbers();
-
- $phoneNumbers = array();
- foreach($rawPhoneNumbers as $phoneNumber => $verificationCode)
- {
- $phoneNumbers[$phoneNumber] = self::isActivated($verificationCode);
- }
-
- return $phoneNumbers;
- }
-
- /**
- * get activated phone number list
- *
- * @return array $phoneNumber
- * @ignore
- */
- public function getActivatedPhoneNumbers()
- {
- Piwik::checkUserIsNotAnonymous();
-
- $phoneNumbers = $this->retrievePhoneNumbers();
-
- $activatedPhoneNumbers = array();
- foreach($phoneNumbers as $phoneNumber => $verificationCode)
- {
- if(self::isActivated($verificationCode))
- {
- $activatedPhoneNumbers[] = $phoneNumber;
- }
- }
-
- return $activatedPhoneNumbers;
- }
-
- private static function isActivated($verificationCode)
- {
- return $verificationCode === null;
- }
-
- /**
- * delete the SMS API credential
- *
- * @return bool true
- */
- public function deleteSMSAPICredential()
- {
- $this->checkCredentialManagementRights();
-
- $settings = $this->getCredentialManagerSettings();
-
- $settings[Piwik_MobileMessaging::API_KEY_OPTION] = null;
-
- $this->setCredentialManagerSettings($settings);
-
- return true;
- }
-
- private function checkCredentialManagementRights()
- {
- $this->getDelegatedManagement() ? Piwik::checkUserIsNotAnonymous() : Piwik::checkUserIsSuperUser();
- }
-
- private function setUserSettings($user, $settings)
- {
- Piwik_SetOption(
- $user . Piwik_MobileMessaging::USER_SETTINGS_POSTFIX_OPTION,
- Piwik_Common::json_encode($settings)
- );
- }
-
- private function setCurrentUserSettings($settings)
- {
- $this->setUserSettings(Piwik::getCurrentUserLogin(), $settings);
- }
-
- private function setCredentialManagerSettings($settings)
- {
- $this->setUserSettings($this->getCredentialManagerLogin(), $settings);
- }
-
- private function getCredentialManagerLogin()
- {
- return $this->getDelegatedManagement() ? Piwik::getCurrentUserLogin() : Piwik::getSuperUserLogin();
- }
-
- private function getUserSettings($user)
- {
- $optionIndex = $user . Piwik_MobileMessaging::USER_SETTINGS_POSTFIX_OPTION;
- $userSettings = Piwik_GetOption($optionIndex);
-
- if(empty($userSettings))
- {
- $userSettings = array();
- }
- else
- {
- $userSettings = Piwik_Common::json_decode($userSettings, true);
- }
-
- return $userSettings;
- }
-
- private function getCredentialManagerSettings()
- {
- return $this->getUserSettings($this->getCredentialManagerLogin());
- }
-
- private function getCurrentUserSettings()
- {
- return $this->getUserSettings(Piwik::getCurrentUserLogin());
- }
-
- /**
- * Specify if normal users can manage their own SMS API credential
- *
- * @param bool $delegatedManagement false if SMS API credential only manageable by super admin, true otherwise
- */
- public function setDelegatedManagement($delegatedManagement)
- {
- Piwik::checkUserIsSuperUser();
- Piwik_SetOption(Piwik_MobileMessaging::DELEGATED_MANAGEMENT_OPTION, $delegatedManagement);
- }
-
- /**
- * Determine if normal users can manage their own SMS API credential
- *
- * @return bool false if SMS API credential only manageable by super admin, true otherwise
- */
- public function getDelegatedManagement()
- {
- Piwik::checkUserHasSomeViewAccess();
- return Piwik_GetOption(Piwik_MobileMessaging::DELEGATED_MANAGEMENT_OPTION) == 'true';
- }
+ const VERIFICATION_CODE_LENGTH = 5;
+ const SMS_FROM = 'Piwik';
+
+ static private $instance = null;
+
+ /**
+ * @return Piwik_MobileMessaging_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * @return Piwik_MobileMessaging_SMSProvider
+ */
+ static private function getSMSProviderInstance($provider)
+ {
+ return Piwik_MobileMessaging_SMSProvider::factory($provider);
+ }
+
+ /**
+ * determine if SMS API credential are available for the current user
+ *
+ * @return bool true if SMS API credential are available for the current user
+ */
+ public function areSMSAPICredentialProvided()
+ {
+ Piwik::checkUserHasSomeViewAccess();
+
+ $credential = $this->getSMSAPICredential();
+ return isset($credential[Piwik_MobileMessaging::API_KEY_OPTION]);
+ }
+
+ private function getSMSAPICredential()
+ {
+ $settings = $this->getCredentialManagerSettings();
+ return array(
+ Piwik_MobileMessaging::PROVIDER_OPTION =>
+ isset($settings[Piwik_MobileMessaging::PROVIDER_OPTION]) ? $settings[Piwik_MobileMessaging::PROVIDER_OPTION] : null,
+ Piwik_MobileMessaging::API_KEY_OPTION =>
+ isset($settings[Piwik_MobileMessaging::API_KEY_OPTION]) ? $settings[Piwik_MobileMessaging::API_KEY_OPTION] : null,
+ );
+ }
+
+ /**
+ * return the SMS API Provider for the current user
+ *
+ * @return string SMS API Provider
+ */
+ public function getSMSProvider()
+ {
+ $this->checkCredentialManagementRights();
+ $credential = $this->getSMSAPICredential();
+ return $credential[Piwik_MobileMessaging::PROVIDER_OPTION];
+ }
+
+ /**
+ * set the SMS API credential
+ *
+ * @param string $provider SMS API provider
+ * @param string $apiKey API Key
+ *
+ * @return bool true if SMS API credential were validated and saved, false otherwise
+ */
+ public function setSMSAPICredential($provider, $apiKey)
+ {
+ $this->checkCredentialManagementRights();
+
+ $smsProviderInstance = self::getSMSProviderInstance($provider);
+ $smsProviderInstance->verifyCredential($apiKey);
+
+ $settings = $this->getCredentialManagerSettings();
+
+ $settings[Piwik_MobileMessaging::PROVIDER_OPTION] = $provider;
+ $settings[Piwik_MobileMessaging::API_KEY_OPTION] = $apiKey;
+
+ $this->setCredentialManagerSettings($settings);
+
+ return true;
+ }
+
+ /**
+ * add phone number
+ *
+ * @param string $phoneNumber
+ *
+ * @return bool true
+ */
+ public function addPhoneNumber($phoneNumber)
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ $phoneNumber = self::sanitizePhoneNumber($phoneNumber);
+
+ $verificationCode = "";
+ for ($i = 0; $i < self::VERIFICATION_CODE_LENGTH; $i++) {
+ $verificationCode .= mt_rand(0, 9);
+ }
+
+ $smsText = Piwik_Translate(
+ 'MobileMessaging_VerificationText',
+ array(
+ $verificationCode,
+ Piwik_Translate('UserSettings_SubmenuSettings'),
+ Piwik_Translate('MobileMessaging_SettingsMenu')
+ )
+ );
+
+ $this->sendSMS($smsText, $phoneNumber, self::SMS_FROM);
+
+ $phoneNumbers = $this->retrievePhoneNumbers();
+ $phoneNumbers[$phoneNumber] = $verificationCode;
+ $this->savePhoneNumbers($phoneNumbers);
+
+ $this->increaseCount(Piwik_MobileMessaging::PHONE_NUMBER_VALIDATION_REQUEST_COUNT_OPTION, $phoneNumber);
+
+ return true;
+ }
+
+ /**
+ * sanitize phone number
+ *
+ * @param string $phoneNumber
+ *
+ * @return string sanitized phone number
+ */
+ public static function sanitizePhoneNumber($phoneNumber)
+ {
+ return str_replace(' ', '', $phoneNumber);
+ }
+
+ /**
+ * send a SMS
+ *
+ * @param string $phoneNumber
+ * @return bool true
+ * @ignore
+ */
+ public function sendSMS($content, $phoneNumber, $from)
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ $credential = $this->getSMSAPICredential();
+ $SMSProvider = self::getSMSProviderInstance($credential[Piwik_MobileMessaging::PROVIDER_OPTION]);
+ $SMSProvider->sendSMS(
+ $credential[Piwik_MobileMessaging::API_KEY_OPTION],
+ $content,
+ $phoneNumber,
+ $from
+ );
+
+ $this->increaseCount(Piwik_MobileMessaging::SMS_SENT_COUNT_OPTION, $phoneNumber);
+
+ return true;
+ }
+
+ /**
+ * get remaining credit
+ *
+ * @return string remaining credit
+ */
+ public function getCreditLeft()
+ {
+ $this->checkCredentialManagementRights();
+
+ $credential = $this->getSMSAPICredential();
+ $SMSProvider = self::getSMSProviderInstance($credential[Piwik_MobileMessaging::PROVIDER_OPTION]);
+ return $SMSProvider->getCreditLeft(
+ $credential[Piwik_MobileMessaging::API_KEY_OPTION]
+ );
+ }
+
+ /**
+ * remove phone number
+ *
+ * @param string $phoneNumber
+ *
+ * @return bool true
+ */
+ public function removePhoneNumber($phoneNumber)
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ $phoneNumbers = $this->retrievePhoneNumbers();
+ unset($phoneNumbers[$phoneNumber]);
+ $this->savePhoneNumbers($phoneNumbers);
+
+ // remove phone number from reports
+ $pdfReportsAPIInstance = Piwik_PDFReports_API::getInstance();
+ $reports = $pdfReportsAPIInstance->getReports(
+ $idSite = false,
+ $period = false,
+ $idReport = false,
+ $ifSuperUserReturnOnlySuperUserReports = $this->getDelegatedManagement()
+ );
+
+ foreach ($reports as $report) {
+ if ($report['type'] == Piwik_MobileMessaging::MOBILE_TYPE) {
+ $reportParameters = $report['parameters'];
+ $reportPhoneNumbers = $reportParameters[Piwik_MobileMessaging::PHONE_NUMBERS_PARAMETER];
+ $updatedPhoneNumbers = array();
+ foreach ($reportPhoneNumbers as $reportPhoneNumber) {
+ if ($reportPhoneNumber != $phoneNumber) {
+ $updatedPhoneNumbers[] = $reportPhoneNumber;
+ }
+ }
+
+ if (count($updatedPhoneNumbers) != count($reportPhoneNumbers)) {
+ $reportParameters[Piwik_MobileMessaging::PHONE_NUMBERS_PARAMETER] = $updatedPhoneNumbers;
+
+ // note: reports can end up without any recipients
+ $pdfReportsAPIInstance->updateReport(
+ $report['idreport'],
+ $report['idsite'],
+ $report['description'],
+ $report['period'],
+ $report['type'],
+ $report['format'],
+ $report['reports'],
+ $reportParameters
+ );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private function retrievePhoneNumbers()
+ {
+ $settings = $this->getCurrentUserSettings();
+
+ $phoneNumbers = array();
+ if (isset($settings[Piwik_MobileMessaging::PHONE_NUMBERS_OPTION])) {
+ $phoneNumbers = $settings[Piwik_MobileMessaging::PHONE_NUMBERS_OPTION];
+ }
+
+ return $phoneNumbers;
+ }
+
+ private function savePhoneNumbers($phoneNumbers)
+ {
+ $settings = $this->getCurrentUserSettings();
+
+ $settings[Piwik_MobileMessaging::PHONE_NUMBERS_OPTION] = $phoneNumbers;
+
+ $this->setCurrentUserSettings($settings);
+ }
+
+ private function increaseCount($option, $phoneNumber)
+ {
+ $settings = $this->getCurrentUserSettings();
+
+ $counts = array();
+ if (isset($settings[$option])) {
+ $counts = $settings[$option];
+ }
+
+ $countToUpdate = 0;
+ if (isset($counts[$phoneNumber])) {
+ $countToUpdate = $counts[$phoneNumber];
+ }
+
+ $counts[$phoneNumber] = $countToUpdate + 1;
+
+ $settings[$option] = $counts;
+
+ $this->setCurrentUserSettings($settings);
+ }
+
+ /**
+ * validate phone number
+ *
+ * @param string $phoneNumber
+ * @param string $verificationCode
+ *
+ * @return bool true if validation code is correct, false otherwise
+ */
+ public function validatePhoneNumber($phoneNumber, $verificationCode)
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ $phoneNumbers = $this->retrievePhoneNumbers();
+
+ if (isset($phoneNumbers[$phoneNumber])) {
+ if ($verificationCode == $phoneNumbers[$phoneNumber]) {
+
+ $phoneNumbers[$phoneNumber] = null;
+ $this->savePhoneNumbers($phoneNumbers);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * get phone number list
+ *
+ * @return array $phoneNumber => $isValidated
+ * @ignore
+ */
+ public function getPhoneNumbers()
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ $rawPhoneNumbers = $this->retrievePhoneNumbers();
+
+ $phoneNumbers = array();
+ foreach ($rawPhoneNumbers as $phoneNumber => $verificationCode) {
+ $phoneNumbers[$phoneNumber] = self::isActivated($verificationCode);
+ }
+
+ return $phoneNumbers;
+ }
+
+ /**
+ * get activated phone number list
+ *
+ * @return array $phoneNumber
+ * @ignore
+ */
+ public function getActivatedPhoneNumbers()
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ $phoneNumbers = $this->retrievePhoneNumbers();
+
+ $activatedPhoneNumbers = array();
+ foreach ($phoneNumbers as $phoneNumber => $verificationCode) {
+ if (self::isActivated($verificationCode)) {
+ $activatedPhoneNumbers[] = $phoneNumber;
+ }
+ }
+
+ return $activatedPhoneNumbers;
+ }
+
+ private static function isActivated($verificationCode)
+ {
+ return $verificationCode === null;
+ }
+
+ /**
+ * delete the SMS API credential
+ *
+ * @return bool true
+ */
+ public function deleteSMSAPICredential()
+ {
+ $this->checkCredentialManagementRights();
+
+ $settings = $this->getCredentialManagerSettings();
+
+ $settings[Piwik_MobileMessaging::API_KEY_OPTION] = null;
+
+ $this->setCredentialManagerSettings($settings);
+
+ return true;
+ }
+
+ private function checkCredentialManagementRights()
+ {
+ $this->getDelegatedManagement() ? Piwik::checkUserIsNotAnonymous() : Piwik::checkUserIsSuperUser();
+ }
+
+ private function setUserSettings($user, $settings)
+ {
+ Piwik_SetOption(
+ $user . Piwik_MobileMessaging::USER_SETTINGS_POSTFIX_OPTION,
+ Piwik_Common::json_encode($settings)
+ );
+ }
+
+ private function setCurrentUserSettings($settings)
+ {
+ $this->setUserSettings(Piwik::getCurrentUserLogin(), $settings);
+ }
+
+ private function setCredentialManagerSettings($settings)
+ {
+ $this->setUserSettings($this->getCredentialManagerLogin(), $settings);
+ }
+
+ private function getCredentialManagerLogin()
+ {
+ return $this->getDelegatedManagement() ? Piwik::getCurrentUserLogin() : Piwik::getSuperUserLogin();
+ }
+
+ private function getUserSettings($user)
+ {
+ $optionIndex = $user . Piwik_MobileMessaging::USER_SETTINGS_POSTFIX_OPTION;
+ $userSettings = Piwik_GetOption($optionIndex);
+
+ if (empty($userSettings)) {
+ $userSettings = array();
+ } else {
+ $userSettings = Piwik_Common::json_decode($userSettings, true);
+ }
+
+ return $userSettings;
+ }
+
+ private function getCredentialManagerSettings()
+ {
+ return $this->getUserSettings($this->getCredentialManagerLogin());
+ }
+
+ private function getCurrentUserSettings()
+ {
+ return $this->getUserSettings(Piwik::getCurrentUserLogin());
+ }
+
+ /**
+ * Specify if normal users can manage their own SMS API credential
+ *
+ * @param bool $delegatedManagement false if SMS API credential only manageable by super admin, true otherwise
+ */
+ public function setDelegatedManagement($delegatedManagement)
+ {
+ Piwik::checkUserIsSuperUser();
+ Piwik_SetOption(Piwik_MobileMessaging::DELEGATED_MANAGEMENT_OPTION, $delegatedManagement);
+ }
+
+ /**
+ * Determine if normal users can manage their own SMS API credential
+ *
+ * @return bool false if SMS API credential only manageable by super admin, true otherwise
+ */
+ public function getDelegatedManagement()
+ {
+ Piwik::checkUserHasSomeViewAccess();
+ return Piwik_GetOption(Piwik_MobileMessaging::DELEGATED_MANAGEMENT_OPTION) == 'true';
+ }
}
diff --git a/plugins/MobileMessaging/Controller.php b/plugins/MobileMessaging/Controller.php
index 11efa0817c..ee027c4505 100644
--- a/plugins/MobileMessaging/Controller.php
+++ b/plugins/MobileMessaging/Controller.php
@@ -17,60 +17,57 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/functions.php';
*/
class Piwik_MobileMessaging_Controller extends Piwik_Controller_Admin
{
- /*
- * Mobile Messaging Settings tab :
- * - set delegated management
- * - provide & validate SMS API credential
- * - add & activate phone numbers
- * - check remaining credits
- */
- function index()
- {
- Piwik::checkUserIsNotAnonymous();
+ /*
+ * Mobile Messaging Settings tab :
+ * - set delegated management
+ * - provide & validate SMS API credential
+ * - add & activate phone numbers
+ * - check remaining credits
+ */
+ function index()
+ {
+ Piwik::checkUserIsNotAnonymous();
- $view = Piwik_View::factory('Settings');
+ $view = Piwik_View::factory('Settings');
- $view->isSuperUser = Piwik::isUserIsSuperUser();
+ $view->isSuperUser = Piwik::isUserIsSuperUser();
- $mobileMessagingAPI = Piwik_MobileMessaging_API::getInstance();
- $view->delegatedManagement = $mobileMessagingAPI->getDelegatedManagement();
- $view->credentialSupplied = $mobileMessagingAPI->areSMSAPICredentialProvided();
- $view->accountManagedByCurrentUser = $view->isSuperUser || $view->delegatedManagement;
- $view->strHelpAddPhone = Piwik_Translate('MobileMessaging_Settings_PhoneNumbers_HelpAdd', array( Piwik_Translate('UserSettings_SubmenuSettings'), Piwik_Translate('MobileMessaging_SettingsMenu') ) );
- if($view->credentialSupplied && $view->accountManagedByCurrentUser)
- {
- $view->provider = $mobileMessagingAPI->getSMSProvider();
- $view->creditLeft = $mobileMessagingAPI->getCreditLeft();
- }
+ $mobileMessagingAPI = Piwik_MobileMessaging_API::getInstance();
+ $view->delegatedManagement = $mobileMessagingAPI->getDelegatedManagement();
+ $view->credentialSupplied = $mobileMessagingAPI->areSMSAPICredentialProvided();
+ $view->accountManagedByCurrentUser = $view->isSuperUser || $view->delegatedManagement;
+ $view->strHelpAddPhone = Piwik_Translate('MobileMessaging_Settings_PhoneNumbers_HelpAdd', array(Piwik_Translate('UserSettings_SubmenuSettings'), Piwik_Translate('MobileMessaging_SettingsMenu')));
+ if ($view->credentialSupplied && $view->accountManagedByCurrentUser) {
+ $view->provider = $mobileMessagingAPI->getSMSProvider();
+ $view->creditLeft = $mobileMessagingAPI->getCreditLeft();
+ }
- $view->smsProviders = Piwik_MobileMessaging_SMSProvider::$availableSMSProviders;
+ $view->smsProviders = Piwik_MobileMessaging_SMSProvider::$availableSMSProviders;
- // construct the list of countries from the lang files
- $countries = array();
- foreach(Piwik_Common::getCountriesList() as $countryCode => $continentCode)
- {
- if(isset(Piwik_MobileMessaging_CountryCallingCodes::$countryCallingCodes[$countryCode]))
- {
- $countries[$countryCode] =
- array(
- 'countryName' => Piwik_CountryTranslate($countryCode),
- 'countryCallingCode' => Piwik_MobileMessaging_CountryCallingCodes::$countryCallingCodes[$countryCode],
- );
- }
- }
- $view->countries = $countries;
+ // construct the list of countries from the lang files
+ $countries = array();
+ foreach (Piwik_Common::getCountriesList() as $countryCode => $continentCode) {
+ if (isset(Piwik_MobileMessaging_CountryCallingCodes::$countryCallingCodes[$countryCode])) {
+ $countries[$countryCode] =
+ array(
+ 'countryName' => Piwik_CountryTranslate($countryCode),
+ 'countryCallingCode' => Piwik_MobileMessaging_CountryCallingCodes::$countryCallingCodes[$countryCode],
+ );
+ }
+ }
+ $view->countries = $countries;
- $view->defaultCountry = Piwik_Common::getCountry(
- Piwik_LanguagesManager::getLanguageCodeForCurrentUser(),
- true,
- Piwik_IP::getIpFromHeader()
- );
+ $view->defaultCountry = Piwik_Common::getCountry(
+ Piwik_LanguagesManager::getLanguageCodeForCurrentUser(),
+ true,
+ Piwik_IP::getIpFromHeader()
+ );
- $view->phoneNumbers = $mobileMessagingAPI->getPhoneNumbers();
+ $view->phoneNumbers = $mobileMessagingAPI->getPhoneNumbers();
- $this->setBasicVariablesView($view);
+ $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
- echo $view->render();
- }
+ $view->menu = Piwik_GetAdminMenu();
+ echo $view->render();
+ }
}
diff --git a/plugins/MobileMessaging/CountryCallingCodes.php b/plugins/MobileMessaging/CountryCallingCodes.php
index a6c72bc86f..b1097220f8 100644
--- a/plugins/MobileMessaging/CountryCallingCodes.php
+++ b/plugins/MobileMessaging/CountryCallingCodes.php
@@ -15,257 +15,257 @@
*/
class Piwik_MobileMessaging_CountryCallingCodes
{
- // list taken from core/DataFiles/Countries.php
- public static $countryCallingCodes = array(
- 'ad' => '376',
- 'ae' => '971',
- 'af' => '93',
- 'ag' => '1268', // @wikipedia original value: 1 268
- 'ai' => '1264', // @wikipedia original value: 1 264
- 'al' => '355',
- 'am' => '374',
- 'ao' => '244',
+ // list taken from core/DataFiles/Countries.php
+ public static $countryCallingCodes = array(
+ 'ad' => '376',
+ 'ae' => '971',
+ 'af' => '93',
+ 'ag' => '1268', // @wikipedia original value: 1 268
+ 'ai' => '1264', // @wikipedia original value: 1 264
+ 'al' => '355',
+ 'am' => '374',
+ 'ao' => '244',
// 'aq' => 'MISSING CODE', // @wikipedia In Antarctica dialing is dependent on the parent country of each base
- 'ar' => '54',
- 'as' => '1684', // @wikipedia original value: 1 684
- 'at' => '43',
- 'au' => '61',
- 'aw' => '297',
- 'ax' => '358',
- 'az' => '994',
- 'ba' => '387',
- 'bb' => '1246', // @wikipedia original value: 1 246
- 'bd' => '880',
- 'be' => '32',
- 'bf' => '226',
- 'bg' => '359',
- 'bh' => '973',
- 'bi' => '257',
- 'bj' => '229',
- 'bl' => '590',
- 'bm' => '1441', // @wikipedia original value: 1 441
- 'bn' => '673',
- 'bo' => '591',
- 'bq' => '5997', // @wikipedia original value: 599 7
- 'br' => '55',
- 'bs' => '1242', // @wikipedia original value: 1 242
- 'bt' => '975',
+ 'ar' => '54',
+ 'as' => '1684', // @wikipedia original value: 1 684
+ 'at' => '43',
+ 'au' => '61',
+ 'aw' => '297',
+ 'ax' => '358',
+ 'az' => '994',
+ 'ba' => '387',
+ 'bb' => '1246', // @wikipedia original value: 1 246
+ 'bd' => '880',
+ 'be' => '32',
+ 'bf' => '226',
+ 'bg' => '359',
+ 'bh' => '973',
+ 'bi' => '257',
+ 'bj' => '229',
+ 'bl' => '590',
+ 'bm' => '1441', // @wikipedia original value: 1 441
+ 'bn' => '673',
+ 'bo' => '591',
+ 'bq' => '5997', // @wikipedia original value: 599 7
+ 'br' => '55',
+ 'bs' => '1242', // @wikipedia original value: 1 242
+ 'bt' => '975',
// 'bv' => 'MISSING CODE',
- 'bw' => '267',
- 'by' => '375',
- 'bz' => '501',
- 'ca' => '1',
- 'cc' => '61',
- 'cd' => '243',
- 'cf' => '236',
- 'cg' => '242',
- 'ch' => '41',
- 'ci' => '225',
- 'ck' => '682',
- 'cl' => '56',
- 'cm' => '237',
- 'cn' => '86',
- 'co' => '57',
- 'cr' => '506',
- 'cu' => '53',
- 'cv' => '238',
- 'cw' => '5999', // @wikipedia original value: 599 9
- 'cx' => '61',
- 'cy' => '357',
- 'cz' => '420',
- 'de' => '49',
- 'dj' => '253',
- 'dk' => '45',
- 'dm' => '1767', // @wikipedia original value: 1 767
+ 'bw' => '267',
+ 'by' => '375',
+ 'bz' => '501',
+ 'ca' => '1',
+ 'cc' => '61',
+ 'cd' => '243',
+ 'cf' => '236',
+ 'cg' => '242',
+ 'ch' => '41',
+ 'ci' => '225',
+ 'ck' => '682',
+ 'cl' => '56',
+ 'cm' => '237',
+ 'cn' => '86',
+ 'co' => '57',
+ 'cr' => '506',
+ 'cu' => '53',
+ 'cv' => '238',
+ 'cw' => '5999', // @wikipedia original value: 599 9
+ 'cx' => '61',
+ 'cy' => '357',
+ 'cz' => '420',
+ 'de' => '49',
+ 'dj' => '253',
+ 'dk' => '45',
+ 'dm' => '1767', // @wikipedia original value: 1 767
// 'do' => 'MISSING CODE', // @wikipedia original values: 1 809, 1 829, 1 849
- 'dz' => '213',
- 'ec' => '593',
- 'ee' => '372',
- 'eg' => '20',
- 'eh' => '212',
- 'er' => '291',
- 'es' => '34',
- 'et' => '251',
- 'fi' => '358',
- 'fj' => '679',
- 'fk' => '500',
- 'fm' => '691',
- 'fo' => '298',
- 'fr' => '33',
- 'ga' => '241',
- 'gb' => '44',
- 'gd' => '1473', // @wikipedia original value: 1 473
- 'ge' => '995',
- 'gf' => '594',
- 'gg' => '44',
- 'gh' => '233',
- 'gi' => '350',
- 'gl' => '299',
- 'gm' => '220',
- 'gn' => '224',
- 'gp' => '590',
- 'gq' => '240',
- 'gr' => '30',
- 'gs' => '500',
- 'gt' => '502',
- 'gu' => '1671', // @wikipedia original value: 1 671
- 'gw' => '245',
- 'gy' => '592',
- 'hk' => '852',
+ 'dz' => '213',
+ 'ec' => '593',
+ 'ee' => '372',
+ 'eg' => '20',
+ 'eh' => '212',
+ 'er' => '291',
+ 'es' => '34',
+ 'et' => '251',
+ 'fi' => '358',
+ 'fj' => '679',
+ 'fk' => '500',
+ 'fm' => '691',
+ 'fo' => '298',
+ 'fr' => '33',
+ 'ga' => '241',
+ 'gb' => '44',
+ 'gd' => '1473', // @wikipedia original value: 1 473
+ 'ge' => '995',
+ 'gf' => '594',
+ 'gg' => '44',
+ 'gh' => '233',
+ 'gi' => '350',
+ 'gl' => '299',
+ 'gm' => '220',
+ 'gn' => '224',
+ 'gp' => '590',
+ 'gq' => '240',
+ 'gr' => '30',
+ 'gs' => '500',
+ 'gt' => '502',
+ 'gu' => '1671', // @wikipedia original value: 1 671
+ 'gw' => '245',
+ 'gy' => '592',
+ 'hk' => '852',
// 'hm' => 'MISSING CODE',
- 'hn' => '504',
- 'hr' => '385',
- 'ht' => '509',
- 'hu' => '36',
- 'id' => '62',
- 'ie' => '353',
- 'il' => '972',
- 'im' => '44',
- 'in' => '91',
- 'io' => '246',
- 'iq' => '964',
- 'ir' => '98',
- 'is' => '354',
- 'it' => '39',
- 'je' => '44',
- 'jm' => '1876', // @wikipedia original value: 1 876
- 'jo' => '962',
- 'jp' => '81',
- 'ke' => '254',
- 'kg' => '996',
- 'kh' => '855',
- 'ki' => '686',
- 'km' => '269',
- 'kn' => '1869', // @wikipedia original value: 1 869
- 'kp' => '850',
- 'kr' => '82',
- 'kw' => '965',
- 'ky' => '1345', // @wikipedia original value: 1 345
+ 'hn' => '504',
+ 'hr' => '385',
+ 'ht' => '509',
+ 'hu' => '36',
+ 'id' => '62',
+ 'ie' => '353',
+ 'il' => '972',
+ 'im' => '44',
+ 'in' => '91',
+ 'io' => '246',
+ 'iq' => '964',
+ 'ir' => '98',
+ 'is' => '354',
+ 'it' => '39',
+ 'je' => '44',
+ 'jm' => '1876', // @wikipedia original value: 1 876
+ 'jo' => '962',
+ 'jp' => '81',
+ 'ke' => '254',
+ 'kg' => '996',
+ 'kh' => '855',
+ 'ki' => '686',
+ 'km' => '269',
+ 'kn' => '1869', // @wikipedia original value: 1 869
+ 'kp' => '850',
+ 'kr' => '82',
+ 'kw' => '965',
+ 'ky' => '1345', // @wikipedia original value: 1 345
// 'kz' => 'MISSING CODE', // @wikipedia original values: 7 6, 7 7
- 'la' => '856',
- 'lb' => '961',
- 'lc' => '1758', // @wikipedia original value: 1 758
- 'li' => '423',
- 'lk' => '94',
- 'lr' => '231',
- 'ls' => '266',
- 'lt' => '370',
- 'lu' => '352',
- 'lv' => '371',
- 'ly' => '218',
- 'ma' => '212',
- 'mc' => '377',
- 'md' => '373',
- 'me' => '382',
- 'mf' => '590',
- 'mg' => '261',
- 'mh' => '692',
- 'mk' => '389',
- 'ml' => '223',
- 'mm' => '95',
- 'mn' => '976',
- 'mo' => '853',
- 'mp' => '1670', // @wikipedia original value: 1 670
- 'mq' => '596',
- 'mr' => '222',
- 'ms' => '1664', // @wikipedia original value: 1 664
- 'mt' => '356',
- 'mu' => '230',
- 'mv' => '960',
- 'mw' => '265',
- 'mx' => '52',
- 'my' => '60',
- 'mz' => '258',
- 'na' => '264',
- 'nc' => '687',
- 'ne' => '227',
- 'nf' => '672',
- 'ng' => '234',
- 'ni' => '505',
- 'nl' => '31',
- 'no' => '47',
- 'np' => '977',
- 'nr' => '674',
- 'nu' => '683',
- 'nz' => '64',
- 'om' => '968',
- 'pa' => '507',
- 'pe' => '51',
- 'pf' => '689',
- 'pg' => '675',
- 'ph' => '63',
- 'pk' => '92',
- 'pl' => '48',
- 'pm' => '508',
- 'pn' => '672',
+ 'la' => '856',
+ 'lb' => '961',
+ 'lc' => '1758', // @wikipedia original value: 1 758
+ 'li' => '423',
+ 'lk' => '94',
+ 'lr' => '231',
+ 'ls' => '266',
+ 'lt' => '370',
+ 'lu' => '352',
+ 'lv' => '371',
+ 'ly' => '218',
+ 'ma' => '212',
+ 'mc' => '377',
+ 'md' => '373',
+ 'me' => '382',
+ 'mf' => '590',
+ 'mg' => '261',
+ 'mh' => '692',
+ 'mk' => '389',
+ 'ml' => '223',
+ 'mm' => '95',
+ 'mn' => '976',
+ 'mo' => '853',
+ 'mp' => '1670', // @wikipedia original value: 1 670
+ 'mq' => '596',
+ 'mr' => '222',
+ 'ms' => '1664', // @wikipedia original value: 1 664
+ 'mt' => '356',
+ 'mu' => '230',
+ 'mv' => '960',
+ 'mw' => '265',
+ 'mx' => '52',
+ 'my' => '60',
+ 'mz' => '258',
+ 'na' => '264',
+ 'nc' => '687',
+ 'ne' => '227',
+ 'nf' => '672',
+ 'ng' => '234',
+ 'ni' => '505',
+ 'nl' => '31',
+ 'no' => '47',
+ 'np' => '977',
+ 'nr' => '674',
+ 'nu' => '683',
+ 'nz' => '64',
+ 'om' => '968',
+ 'pa' => '507',
+ 'pe' => '51',
+ 'pf' => '689',
+ 'pg' => '675',
+ 'ph' => '63',
+ 'pk' => '92',
+ 'pl' => '48',
+ 'pm' => '508',
+ 'pn' => '672',
// 'pr' => 'MISSING CODE', // @wikipedia original values: 1 787, 1 939
- 'ps' => '970',
- 'pt' => '351',
- 'pw' => '680',
- 'py' => '595',
- 'qa' => '974',
- 're' => '262',
- 'ro' => '40',
- 'rs' => '381',
- 'ru' => '7',
- 'rw' => '250',
- 'sa' => '966',
- 'sb' => '677',
- 'sc' => '248',
- 'sd' => '249',
- 'se' => '46',
- 'sg' => '65',
- 'sh' => '290',
- 'si' => '386',
- 'sj' => '47',
- 'sk' => '421',
- 'sl' => '232',
- 'sm' => '378',
- 'sn' => '221',
- 'so' => '252',
- 'sr' => '597',
- 'ss' => '211',
- 'st' => '239',
- 'sv' => '503',
- 'sx' => '1721', //@wikipedia original value: 1 721
- 'sy' => '963',
- 'sz' => '268',
- 'tc' => '1649', // @wikipedia original value: 1 649
- 'td' => '235',
+ 'ps' => '970',
+ 'pt' => '351',
+ 'pw' => '680',
+ 'py' => '595',
+ 'qa' => '974',
+ 're' => '262',
+ 'ro' => '40',
+ 'rs' => '381',
+ 'ru' => '7',
+ 'rw' => '250',
+ 'sa' => '966',
+ 'sb' => '677',
+ 'sc' => '248',
+ 'sd' => '249',
+ 'se' => '46',
+ 'sg' => '65',
+ 'sh' => '290',
+ 'si' => '386',
+ 'sj' => '47',
+ 'sk' => '421',
+ 'sl' => '232',
+ 'sm' => '378',
+ 'sn' => '221',
+ 'so' => '252',
+ 'sr' => '597',
+ 'ss' => '211',
+ 'st' => '239',
+ 'sv' => '503',
+ 'sx' => '1721', //@wikipedia original value: 1 721
+ 'sy' => '963',
+ 'sz' => '268',
+ 'tc' => '1649', // @wikipedia original value: 1 649
+ 'td' => '235',
// 'tf' => 'MISSING CODE',
- 'tg' => '228',
- 'th' => '66',
+ 'tg' => '228',
+ 'th' => '66',
// 'ti' => 'MISSING CODE',
- 'tj' => '992',
- 'tk' => '690',
- 'tl' => '670',
- 'tm' => '993',
- 'tn' => '216',
- 'to' => '676',
- 'tr' => '90',
- 'tt' => '1868', // @wikipedia original value: 1 868
- 'tv' => '688',
- 'tw' => '886',
- 'tz' => '255',
- 'ua' => '380',
- 'ug' => '256',
+ 'tj' => '992',
+ 'tk' => '690',
+ 'tl' => '670',
+ 'tm' => '993',
+ 'tn' => '216',
+ 'to' => '676',
+ 'tr' => '90',
+ 'tt' => '1868', // @wikipedia original value: 1 868
+ 'tv' => '688',
+ 'tw' => '886',
+ 'tz' => '255',
+ 'ua' => '380',
+ 'ug' => '256',
// 'um' => 'MISSING CODE',
- 'us' => '1',
- 'uy' => '598',
- 'uz' => '998',
+ 'us' => '1',
+ 'uy' => '598',
+ 'uz' => '998',
// 'va' => 'MISSING CODE', // @wikipedia original values: 39 066, assigned 379
- 'vc' => '1784', // @wikipedia original value: 1 784
- 've' => '58',
- 'vg' => '1284', // @wikipedia original value: 1 284
- 'vi' => '1340', // @wikipedia original value: 1 340
- 'vn' => '84',
- 'vu' => '678',
- 'wf' => '681',
- 'ws' => '685',
- 'ye' => '967',
- 'yt' => '262',
- 'za' => '27',
- 'zm' => '260',
- 'zw' => '263'
- );
+ 'vc' => '1784', // @wikipedia original value: 1 784
+ 've' => '58',
+ 'vg' => '1284', // @wikipedia original value: 1 284
+ 'vi' => '1340', // @wikipedia original value: 1 340
+ 'vn' => '84',
+ 'vu' => '678',
+ 'wf' => '681',
+ 'ws' => '685',
+ 'ye' => '967',
+ 'yt' => '262',
+ 'za' => '27',
+ 'zm' => '260',
+ 'zw' => '263'
+ );
}
diff --git a/plugins/MobileMessaging/GSMCharset.php b/plugins/MobileMessaging/GSMCharset.php
index 22278facb2..4d58c96580 100644
--- a/plugins/MobileMessaging/GSMCharset.php
+++ b/plugins/MobileMessaging/GSMCharset.php
@@ -16,144 +16,144 @@
*/
class Piwik_MobileMessaging_GSMCharset
{
- public static $GSMCharset = array(
+ public static $GSMCharset = array(
- // Standard GSM Characters, weight = 1
- '@' => 1,
- '£' => 1,
- '$' => 1,
- '¥' => 1,
- 'è' => 1,
- 'é' => 1,
- 'ù' => 1,
- 'ì' => 1,
- 'ò' => 1,
- 'Ç' => 1,
- 'Ø' => 1,
- 'ø' => 1,
- 'Å' => 1,
- 'å' => 1,
- '∆' => 1,
- '_' => 1,
- 'Φ' => 1,
- 'Γ' => 1,
- 'Λ' => 1,
- 'Ω' => 1,
- 'Π' => 1,
- 'Ψ' => 1,
- 'Σ' => 1,
- 'Θ' => 1,
- 'Ξ' => 1,
- 'Æ' => 1,
- 'æ' => 1,
- 'ß' => 1,
- 'É' => 1,
- ' ' => 1,
- '!' => 1,
- '"' => 1,
- '#' => 1,
- '¤' => 1,
- '%' => 1,
- '&' => 1,
- '\'' => 1,
- '(' => 1,
- ')' => 1,
- '*' => 1,
- '+' => 1,
- ',' => 1,
- '-' => 1,
- '.' => 1,
- '/' => 1,
- '0' => 1,
- '1' => 1,
- '2' => 1,
- '3' => 1,
- '4' => 1,
- '5' => 1,
- '6' => 1,
- '7' => 1,
- '8' => 1,
- '9' => 1,
- ':' => 1,
- ';' => 1,
- '<' => 1,
- '=' => 1,
- '>' => 1,
- '?' => 1,
- '¡' => 1,
- 'A' => 1,
- 'B' => 1,
- 'C' => 1,
- 'D' => 1,
- 'E' => 1,
- 'F' => 1,
- 'G' => 1,
- 'H' => 1,
- 'I' => 1,
- 'J' => 1,
- 'K' => 1,
- 'L' => 1,
- 'M' => 1,
- 'N' => 1,
- 'O' => 1,
- 'P' => 1,
- 'Q' => 1,
- 'R' => 1,
- 'S' => 1,
- 'T' => 1,
- 'U' => 1,
- 'V' => 1,
- 'W' => 1,
- 'X' => 1,
- 'Y' => 1,
- 'Z' => 1,
- 'Ä' => 1,
- 'Ö' => 1,
- 'Ñ' => 1,
- 'Ü' => 1,
- '§' => 1,
- '¿' => 1,
- 'a' => 1,
- 'b' => 1,
- 'c' => 1,
- 'd' => 1,
- 'e' => 1,
- 'f' => 1,
- 'g' => 1,
- 'h' => 1,
- 'i' => 1,
- 'j' => 1,
- 'k' => 1,
- 'l' => 1,
- 'm' => 1,
- 'n' => 1,
- 'o' => 1,
- 'p' => 1,
- 'q' => 1,
- 'r' => 1,
- 's' => 1,
- 't' => 1,
- 'u' => 1,
- 'v' => 1,
- 'w' => 1,
- 'x' => 1,
- 'y' => 1,
- 'z' => 1,
- 'ä' => 1,
- 'ö' => 1,
- 'ñ' => 1,
- 'ü' => 1,
- 'à' => 1,
+ // Standard GSM Characters, weight = 1
+ '@' => 1,
+ '£' => 1,
+ '$' => 1,
+ '¥' => 1,
+ 'è' => 1,
+ 'é' => 1,
+ 'ù' => 1,
+ 'ì' => 1,
+ 'ò' => 1,
+ 'Ç' => 1,
+ 'Ø' => 1,
+ 'ø' => 1,
+ 'Å' => 1,
+ 'å' => 1,
+ '∆' => 1,
+ '_' => 1,
+ 'Φ' => 1,
+ 'Γ' => 1,
+ 'Λ' => 1,
+ 'Ω' => 1,
+ 'Π' => 1,
+ 'Ψ' => 1,
+ 'Σ' => 1,
+ 'Θ' => 1,
+ 'Ξ' => 1,
+ 'Æ' => 1,
+ 'æ' => 1,
+ 'ß' => 1,
+ 'É' => 1,
+ ' ' => 1,
+ '!' => 1,
+ '"' => 1,
+ '#' => 1,
+ '¤' => 1,
+ '%' => 1,
+ '&' => 1,
+ '\'' => 1,
+ '(' => 1,
+ ')' => 1,
+ '*' => 1,
+ '+' => 1,
+ ',' => 1,
+ '-' => 1,
+ '.' => 1,
+ '/' => 1,
+ '0' => 1,
+ '1' => 1,
+ '2' => 1,
+ '3' => 1,
+ '4' => 1,
+ '5' => 1,
+ '6' => 1,
+ '7' => 1,
+ '8' => 1,
+ '9' => 1,
+ ':' => 1,
+ ';' => 1,
+ '<' => 1,
+ '=' => 1,
+ '>' => 1,
+ '?' => 1,
+ '¡' => 1,
+ 'A' => 1,
+ 'B' => 1,
+ 'C' => 1,
+ 'D' => 1,
+ 'E' => 1,
+ 'F' => 1,
+ 'G' => 1,
+ 'H' => 1,
+ 'I' => 1,
+ 'J' => 1,
+ 'K' => 1,
+ 'L' => 1,
+ 'M' => 1,
+ 'N' => 1,
+ 'O' => 1,
+ 'P' => 1,
+ 'Q' => 1,
+ 'R' => 1,
+ 'S' => 1,
+ 'T' => 1,
+ 'U' => 1,
+ 'V' => 1,
+ 'W' => 1,
+ 'X' => 1,
+ 'Y' => 1,
+ 'Z' => 1,
+ 'Ä' => 1,
+ 'Ö' => 1,
+ 'Ñ' => 1,
+ 'Ü' => 1,
+ '§' => 1,
+ '¿' => 1,
+ 'a' => 1,
+ 'b' => 1,
+ 'c' => 1,
+ 'd' => 1,
+ 'e' => 1,
+ 'f' => 1,
+ 'g' => 1,
+ 'h' => 1,
+ 'i' => 1,
+ 'j' => 1,
+ 'k' => 1,
+ 'l' => 1,
+ 'm' => 1,
+ 'n' => 1,
+ 'o' => 1,
+ 'p' => 1,
+ 'q' => 1,
+ 'r' => 1,
+ 's' => 1,
+ 't' => 1,
+ 'u' => 1,
+ 'v' => 1,
+ 'w' => 1,
+ 'x' => 1,
+ 'y' => 1,
+ 'z' => 1,
+ 'ä' => 1,
+ 'ö' => 1,
+ 'ñ' => 1,
+ 'ü' => 1,
+ 'à' => 1,
- // Extended GSM Characters, weight = 2
- '^' => 2,
- '{' => 2,
- '}' => 2,
- '\\' => 2,
- '[' => 2,
- '~' => 2,
- ']' => 2,
- '|' => 2,
- '€' => 2,
- );
+ // Extended GSM Characters, weight = 2
+ '^' => 2,
+ '{' => 2,
+ '}' => 2,
+ '\\' => 2,
+ '[' => 2,
+ '~' => 2,
+ ']' => 2,
+ '|' => 2,
+ '€' => 2,
+ );
}
diff --git a/plugins/MobileMessaging/MobileMessaging.php b/plugins/MobileMessaging/MobileMessaging.php
index e090411562..62028ba3e4 100644
--- a/plugins/MobileMessaging/MobileMessaging.php
+++ b/plugins/MobileMessaging/MobileMessaging.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_MobileMessaging
*/
@@ -15,324 +15,301 @@
*/
class Piwik_MobileMessaging extends Piwik_Plugin
{
- const DELEGATED_MANAGEMENT_OPTION = 'MobileMessaging_DelegatedManagement';
- const PROVIDER_OPTION = 'Provider';
- const API_KEY_OPTION = 'APIKey';
- const PHONE_NUMBERS_OPTION = 'PhoneNumbers';
- const PHONE_NUMBER_VALIDATION_REQUEST_COUNT_OPTION = 'PhoneNumberValidationRequestCount';
- const SMS_SENT_COUNT_OPTION = 'SMSSentCount';
- const DELEGATED_MANAGEMENT_OPTION_DEFAULT = 'false';
- const USER_SETTINGS_POSTFIX_OPTION = '_MobileMessagingSettings';
-
- const PHONE_NUMBERS_PARAMETER = 'phoneNumbers';
-
- const MOBILE_TYPE = 'mobile';
- const SMS_FORMAT = 'sms';
-
- static private $availableParameters = array(
- self::PHONE_NUMBERS_PARAMETER => true,
- );
-
- static private $managedReportTypes = array(
- self::MOBILE_TYPE => 'plugins/MobileMessaging/images/phone.png'
- );
-
- static private $managedReportFormats = array(
- self::SMS_FORMAT => 'plugins/MobileMessaging/images/phone.png'
- );
-
- static private $availableReports = array(
- array(
- 'module' => 'MultiSites',
- 'action' => 'getAll',
- ),
- array(
- 'module' => 'MultiSites',
- 'action' => 'getOne',
- ),
- );
-
- /**
- * Return information about this plugin.
- *
- * @see Piwik_Plugin
- *
- * @return array
- */
- public function getInformation()
- {
- return array(
- 'name' => 'Mobile Messaging Plugin',
- 'description' => Piwik_Translate('MobileMessaging_PluginDescription'),
- 'homepage' => 'http://piwik.org/',
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'license' => 'GPL v3 or later',
- 'license_homepage' => 'http://www.gnu.org/licenses/gpl.html',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- function getListHooksRegistered()
- {
- return array(
- 'AdminMenu.add' => 'addMenu',
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'PDFReports.getReportParameters' => 'getReportParameters',
- 'PDFReports.validateReportParameters' => 'validateReportParameters',
- 'PDFReports.getReportMetadata' => 'getReportMetadata',
- 'PDFReports.getReportTypes' => 'getReportTypes',
- 'PDFReports.getReportFormats' => 'getReportFormats',
- 'PDFReports.getRendererInstance' => 'getRendererInstance',
- 'PDFReports.getReportRecipients' => 'getReportRecipients',
- 'PDFReports.allowMultipleReports' => 'allowMultipleReports',
- 'PDFReports.sendReport' => 'sendReport',
- 'template_reportParametersPDFReports' => 'template_reportParametersPDFReports',
- );
- }
-
- function addMenu()
- {
- Piwik_AddAdminMenu(
- 'MobileMessaging_SettingsMenu',
- array('module' => 'MobileMessaging', 'action' => 'index'),
- true
- );
- }
-
- /**
- * Get JavaScript files
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
-
- $jsFiles[] = "plugins/MobileMessaging/scripts/MobileMessagingSettings.js";
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function validateReportParameters( $notification )
- {
- if(self::manageEvent($notification))
- {
- $parameters = &$notification->getNotificationObject();
-
- // phone number validation
- $availablePhoneNumbers = Piwik_MobileMessaging_API::getInstance()->getActivatedPhoneNumbers();
-
- $phoneNumbers = $parameters[self::PHONE_NUMBERS_PARAMETER];
- foreach($phoneNumbers as $key => $phoneNumber)
- {
- //when a wrong phone number is supplied we silently discard it
- if(!in_array($phoneNumber, $availablePhoneNumbers))
- {
- unset($phoneNumbers[$key]);
- }
- }
-
- // 'unset' seems to transform the array to an associative array
- $parameters[self::PHONE_NUMBERS_PARAMETER] = array_values($phoneNumbers);
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportMetadata( $notification )
- {
- if(self::manageEvent($notification))
- {
- $availableReportMetadata = &$notification->getNotificationObject();
-
- $notificationInfo = $notification->getNotificationInfo();
- $idSite = $notificationInfo[Piwik_PDFReports_API::ID_SITE_INFO_KEY];
-
- foreach(self::$availableReports as $availableReport)
- {
- $reportMetadata = Piwik_API_API::getInstance()->getMetadata(
- $idSite,
- $availableReport['module'],
- $availableReport['action']
- );
-
- if($reportMetadata != null)
- {
- $reportMetadata = reset($reportMetadata);
- $availableReportMetadata[] = $reportMetadata;
- }
- }
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportTypes( $notification )
- {
- $reportTypes = &$notification->getNotificationObject();
- $reportTypes = array_merge($reportTypes, self::$managedReportTypes);
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportFormats( $notification )
- {
- if(self::manageEvent($notification))
- {
- $reportFormats = &$notification->getNotificationObject();
- $reportFormats = array_merge($reportFormats, self::$managedReportFormats);
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportParameters( $notification )
- {
- if(self::manageEvent($notification))
- {
- $availableParameters = &$notification->getNotificationObject();
- $availableParameters = self::$availableParameters;
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getRendererInstance( $notification )
- {
- if(self::manageEvent($notification))
- {
- $reportRenderer = &$notification->getNotificationObject();
-
- if(Piwik_PluginsManager::getInstance()->isPluginActivated('MultiSites'))
- {
- $reportRenderer = new Piwik_MobileMessaging_ReportRenderer_Sms();
- }
- else
- {
- $reportRenderer = new Piwik_MobileMessaging_ReportRenderer_Exception(
- Piwik_Translate('MobileMessaging_MultiSites_Must_Be_Activated')
- );
- }
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function allowMultipleReports( $notification )
- {
- if(self::manageEvent($notification))
- {
- $allowMultipleReports = &$notification->getNotificationObject();
- $allowMultipleReports = false;
- }
- }
-
- function getReportRecipients( $notification )
- {
- if(self::manageEvent($notification))
- {
- $recipients = &$notification->getNotificationObject();
- $notificationInfo = $notification->getNotificationInfo();
-
- $report = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY];
- $recipients = $report['parameters'][self::PHONE_NUMBERS_PARAMETER];
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function sendReport( $notification )
- {
- if(self::manageEvent($notification))
- {
- $notificationInfo = $notification->getNotificationInfo();
- $report = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY];
- $contents = $notificationInfo[Piwik_PDFReports_API::REPORT_CONTENT_KEY];
-
- $parameters = $report['parameters'];
- $phoneNumbers = $parameters[self::PHONE_NUMBERS_PARAMETER];
-
- if(in_array('MultiSites_getAll', $report['reports']))
- {
- $from = Piwik_Translate('General_Reports');
- }
- else
- {
- $from = $notificationInfo[Piwik_PDFReports_API::WEBSITE_NAME_KEY];
- }
-
- $mobileMessagingAPI = Piwik_MobileMessaging_API::getInstance();
- foreach($phoneNumbers as $phoneNumber)
- {
- $mobileMessagingAPI->sendSMS(
- $contents,
- $phoneNumber,
- $from
- );
- }
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- static public function template_reportParametersPDFReports($notification)
- {
- if(Piwik::isUserIsAnonymous())
- {
- return;
- }
-
- $out =& $notification->getNotificationObject();
- $view = Piwik_View::factory('ReportParameters');
- $view->reportType = self::MOBILE_TYPE;
- $view->phoneNumbers = Piwik_MobileMessaging_API::getInstance()->getActivatedPhoneNumbers();
- $out .= $view->render();
- }
-
- private static function manageEvent($notification)
- {
- $notificationInfo = $notification->getNotificationInfo();
- return in_array($notificationInfo[Piwik_PDFReports_API::REPORT_TYPE_INFO_KEY], array_keys(self::$managedReportTypes));
- }
-
- function install()
- {
- $delegatedManagement = Piwik_GetOption(self::DELEGATED_MANAGEMENT_OPTION);
- if (empty($delegatedManagement))
- {
- Piwik_SetOption(self::DELEGATED_MANAGEMENT_OPTION, self::DELEGATED_MANAGEMENT_OPTION_DEFAULT);
- }
- }
-
- function deactivate()
- {
- // delete all mobile reports
- $pdfReportsAPIInstance = Piwik_PDFReports_API::getInstance();
- $reports = $pdfReportsAPIInstance->getReports();
-
- foreach($reports as $report)
- {
- if ($report['type'] == Piwik_MobileMessaging::MOBILE_TYPE)
- {
- $pdfReportsAPIInstance->deleteReport($report['idreport']);
- }
- }
- }
-
- public function uninstall()
- {
- // currently the UI does not allow to delete a plugin
- // when it becomes available, all the MobileMessaging settings (API credentials, phone numbers, etc..) should be removed from the option table
- return;
- }
+ const DELEGATED_MANAGEMENT_OPTION = 'MobileMessaging_DelegatedManagement';
+ const PROVIDER_OPTION = 'Provider';
+ const API_KEY_OPTION = 'APIKey';
+ const PHONE_NUMBERS_OPTION = 'PhoneNumbers';
+ const PHONE_NUMBER_VALIDATION_REQUEST_COUNT_OPTION = 'PhoneNumberValidationRequestCount';
+ const SMS_SENT_COUNT_OPTION = 'SMSSentCount';
+ const DELEGATED_MANAGEMENT_OPTION_DEFAULT = 'false';
+ const USER_SETTINGS_POSTFIX_OPTION = '_MobileMessagingSettings';
+
+ const PHONE_NUMBERS_PARAMETER = 'phoneNumbers';
+
+ const MOBILE_TYPE = 'mobile';
+ const SMS_FORMAT = 'sms';
+
+ static private $availableParameters = array(
+ self::PHONE_NUMBERS_PARAMETER => true,
+ );
+
+ static private $managedReportTypes = array(
+ self::MOBILE_TYPE => 'plugins/MobileMessaging/images/phone.png'
+ );
+
+ static private $managedReportFormats = array(
+ self::SMS_FORMAT => 'plugins/MobileMessaging/images/phone.png'
+ );
+
+ static private $availableReports = array(
+ array(
+ 'module' => 'MultiSites',
+ 'action' => 'getAll',
+ ),
+ array(
+ 'module' => 'MultiSites',
+ 'action' => 'getOne',
+ ),
+ );
+
+ /**
+ * Return information about this plugin.
+ *
+ * @see Piwik_Plugin
+ *
+ * @return array
+ */
+ public function getInformation()
+ {
+ return array(
+ 'name' => 'Mobile Messaging Plugin',
+ 'description' => Piwik_Translate('MobileMessaging_PluginDescription'),
+ 'homepage' => 'http://piwik.org/',
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'license' => 'GPL v3 or later',
+ 'license_homepage' => 'http://www.gnu.org/licenses/gpl.html',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ function getListHooksRegistered()
+ {
+ return array(
+ 'AdminMenu.add' => 'addMenu',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'PDFReports.getReportParameters' => 'getReportParameters',
+ 'PDFReports.validateReportParameters' => 'validateReportParameters',
+ 'PDFReports.getReportMetadata' => 'getReportMetadata',
+ 'PDFReports.getReportTypes' => 'getReportTypes',
+ 'PDFReports.getReportFormats' => 'getReportFormats',
+ 'PDFReports.getRendererInstance' => 'getRendererInstance',
+ 'PDFReports.getReportRecipients' => 'getReportRecipients',
+ 'PDFReports.allowMultipleReports' => 'allowMultipleReports',
+ 'PDFReports.sendReport' => 'sendReport',
+ 'template_reportParametersPDFReports' => 'template_reportParametersPDFReports',
+ );
+ }
+
+ function addMenu()
+ {
+ Piwik_AddAdminMenu(
+ 'MobileMessaging_SettingsMenu',
+ array('module' => 'MobileMessaging', 'action' => 'index'),
+ true
+ );
+ }
+
+ /**
+ * Get JavaScript files
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+
+ $jsFiles[] = "plugins/MobileMessaging/scripts/MobileMessagingSettings.js";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function validateReportParameters($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $parameters = & $notification->getNotificationObject();
+
+ // phone number validation
+ $availablePhoneNumbers = Piwik_MobileMessaging_API::getInstance()->getActivatedPhoneNumbers();
+
+ $phoneNumbers = $parameters[self::PHONE_NUMBERS_PARAMETER];
+ foreach ($phoneNumbers as $key => $phoneNumber) {
+ //when a wrong phone number is supplied we silently discard it
+ if (!in_array($phoneNumber, $availablePhoneNumbers)) {
+ unset($phoneNumbers[$key]);
+ }
+ }
+
+ // 'unset' seems to transform the array to an associative array
+ $parameters[self::PHONE_NUMBERS_PARAMETER] = array_values($phoneNumbers);
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportMetadata($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $availableReportMetadata = & $notification->getNotificationObject();
+
+ $notificationInfo = $notification->getNotificationInfo();
+ $idSite = $notificationInfo[Piwik_PDFReports_API::ID_SITE_INFO_KEY];
+
+ foreach (self::$availableReports as $availableReport) {
+ $reportMetadata = Piwik_API_API::getInstance()->getMetadata(
+ $idSite,
+ $availableReport['module'],
+ $availableReport['action']
+ );
+
+ if ($reportMetadata != null) {
+ $reportMetadata = reset($reportMetadata);
+ $availableReportMetadata[] = $reportMetadata;
+ }
+ }
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportTypes($notification)
+ {
+ $reportTypes = & $notification->getNotificationObject();
+ $reportTypes = array_merge($reportTypes, self::$managedReportTypes);
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportFormats($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $reportFormats = & $notification->getNotificationObject();
+ $reportFormats = array_merge($reportFormats, self::$managedReportFormats);
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportParameters($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $availableParameters = & $notification->getNotificationObject();
+ $availableParameters = self::$availableParameters;
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getRendererInstance($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $reportRenderer = & $notification->getNotificationObject();
+
+ if (Piwik_PluginsManager::getInstance()->isPluginActivated('MultiSites')) {
+ $reportRenderer = new Piwik_MobileMessaging_ReportRenderer_Sms();
+ } else {
+ $reportRenderer = new Piwik_MobileMessaging_ReportRenderer_Exception(
+ Piwik_Translate('MobileMessaging_MultiSites_Must_Be_Activated')
+ );
+ }
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function allowMultipleReports($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $allowMultipleReports = & $notification->getNotificationObject();
+ $allowMultipleReports = false;
+ }
+ }
+
+ function getReportRecipients($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $recipients = & $notification->getNotificationObject();
+ $notificationInfo = $notification->getNotificationInfo();
+
+ $report = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY];
+ $recipients = $report['parameters'][self::PHONE_NUMBERS_PARAMETER];
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function sendReport($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $notificationInfo = $notification->getNotificationInfo();
+ $report = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY];
+ $contents = $notificationInfo[Piwik_PDFReports_API::REPORT_CONTENT_KEY];
+
+ $parameters = $report['parameters'];
+ $phoneNumbers = $parameters[self::PHONE_NUMBERS_PARAMETER];
+
+ if (in_array('MultiSites_getAll', $report['reports'])) {
+ $from = Piwik_Translate('General_Reports');
+ } else {
+ $from = $notificationInfo[Piwik_PDFReports_API::WEBSITE_NAME_KEY];
+ }
+
+ $mobileMessagingAPI = Piwik_MobileMessaging_API::getInstance();
+ foreach ($phoneNumbers as $phoneNumber) {
+ $mobileMessagingAPI->sendSMS(
+ $contents,
+ $phoneNumber,
+ $from
+ );
+ }
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ static public function template_reportParametersPDFReports($notification)
+ {
+ if (Piwik::isUserIsAnonymous()) {
+ return;
+ }
+
+ $out =& $notification->getNotificationObject();
+ $view = Piwik_View::factory('ReportParameters');
+ $view->reportType = self::MOBILE_TYPE;
+ $view->phoneNumbers = Piwik_MobileMessaging_API::getInstance()->getActivatedPhoneNumbers();
+ $out .= $view->render();
+ }
+
+ private static function manageEvent($notification)
+ {
+ $notificationInfo = $notification->getNotificationInfo();
+ return in_array($notificationInfo[Piwik_PDFReports_API::REPORT_TYPE_INFO_KEY], array_keys(self::$managedReportTypes));
+ }
+
+ function install()
+ {
+ $delegatedManagement = Piwik_GetOption(self::DELEGATED_MANAGEMENT_OPTION);
+ if (empty($delegatedManagement)) {
+ Piwik_SetOption(self::DELEGATED_MANAGEMENT_OPTION, self::DELEGATED_MANAGEMENT_OPTION_DEFAULT);
+ }
+ }
+
+ function deactivate()
+ {
+ // delete all mobile reports
+ $pdfReportsAPIInstance = Piwik_PDFReports_API::getInstance();
+ $reports = $pdfReportsAPIInstance->getReports();
+
+ foreach ($reports as $report) {
+ if ($report['type'] == Piwik_MobileMessaging::MOBILE_TYPE) {
+ $pdfReportsAPIInstance->deleteReport($report['idreport']);
+ }
+ }
+ }
+
+ public function uninstall()
+ {
+ // currently the UI does not allow to delete a plugin
+ // when it becomes available, all the MobileMessaging settings (API credentials, phone numbers, etc..) should be removed from the option table
+ return;
+ }
}
diff --git a/plugins/MobileMessaging/ReportRenderer/Exception.php b/plugins/MobileMessaging/ReportRenderer/Exception.php
index af2352150a..a1eac80d8f 100644
--- a/plugins/MobileMessaging/ReportRenderer/Exception.php
+++ b/plugins/MobileMessaging/ReportRenderer/Exception.php
@@ -15,57 +15,57 @@
*/
class Piwik_MobileMessaging_ReportRenderer_Exception extends Piwik_ReportRenderer
{
- private $rendering = "";
+ private $rendering = "";
- function __construct($exception)
- {
- $this->rendering = $exception;
- }
+ function __construct($exception)
+ {
+ $this->rendering = $exception;
+ }
- public function setLocale($locale)
- {
- // nothing to do
- }
+ public function setLocale($locale)
+ {
+ // nothing to do
+ }
- public function sendToDisk($filename)
- {
- return Piwik_ReportRenderer::writeFile(
- $filename,
- Piwik_MobileMessaging_ReportRenderer_Sms::SMS_FILE_EXTENSION,
- $this->rendering
- );
- }
+ public function sendToDisk($filename)
+ {
+ return Piwik_ReportRenderer::writeFile(
+ $filename,
+ Piwik_MobileMessaging_ReportRenderer_Sms::SMS_FILE_EXTENSION,
+ $this->rendering
+ );
+ }
- public function sendToBrowserDownload($filename)
- {
- Piwik_ReportRenderer::sendToBrowser(
- $filename,
- Piwik_MobileMessaging_ReportRenderer_Sms::SMS_FILE_EXTENSION,
- Piwik_MobileMessaging_ReportRenderer_Sms::SMS_CONTENT_TYPE,
- $this->rendering
- );
- }
+ public function sendToBrowserDownload($filename)
+ {
+ Piwik_ReportRenderer::sendToBrowser(
+ $filename,
+ Piwik_MobileMessaging_ReportRenderer_Sms::SMS_FILE_EXTENSION,
+ Piwik_MobileMessaging_ReportRenderer_Sms::SMS_CONTENT_TYPE,
+ $this->rendering
+ );
+ }
- public function sendToBrowserInline($filename)
- {
- Piwik_ReportRenderer::inlineToBrowser(
- Piwik_MobileMessaging_ReportRenderer_Sms::SMS_CONTENT_TYPE,
- $this->rendering
- );
- }
+ public function sendToBrowserInline($filename)
+ {
+ Piwik_ReportRenderer::inlineToBrowser(
+ Piwik_MobileMessaging_ReportRenderer_Sms::SMS_CONTENT_TYPE,
+ $this->rendering
+ );
+ }
- public function getRenderedReport()
- {
- return $this->rendering;
- }
+ public function getRenderedReport()
+ {
+ return $this->rendering;
+ }
- public function renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata)
- {
- // nothing to do
- }
+ public function renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata)
+ {
+ // nothing to do
+ }
- public function renderReport($processedReport)
- {
- // nothing to do
- }
+ public function renderReport($processedReport)
+ {
+ // nothing to do
+ }
}
diff --git a/plugins/MobileMessaging/ReportRenderer/Sms.php b/plugins/MobileMessaging/ReportRenderer/Sms.php
index 47186f3ff6..96f2999d9f 100644
--- a/plugins/MobileMessaging/ReportRenderer/Sms.php
+++ b/plugins/MobileMessaging/ReportRenderer/Sms.php
@@ -16,65 +16,64 @@
*/
class Piwik_MobileMessaging_ReportRenderer_Sms extends Piwik_ReportRenderer
{
- const FLOAT_REGEXP = '/[-+]?[0-9]*[\.,]?[0-9]+/';
- const SMS_CONTENT_TYPE = 'text/plain';
- const SMS_FILE_EXTENSION = 'sms';
-
- private $rendering = "";
-
- public function setLocale($locale)
- {
- // nothing to do
- }
-
- public function sendToDisk($filename)
- {
- return Piwik_ReportRenderer::writeFile($filename, self::SMS_FILE_EXTENSION, $this->rendering);
- }
-
- public function sendToBrowserDownload($filename)
- {
- Piwik_ReportRenderer::sendToBrowser($filename, self::SMS_FILE_EXTENSION, self::SMS_CONTENT_TYPE, $this->rendering);
- }
-
- public function sendToBrowserInline($filename)
- {
- Piwik_ReportRenderer::inlineToBrowser(self::SMS_CONTENT_TYPE, $this->rendering);
- }
-
- public function getRenderedReport()
- {
- return $this->rendering;
- }
-
- public function renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata)
- {
- // nothing to do
- }
-
- public function renderReport($processedReport)
- {
- $isGoalPluginEnabled = Piwik_Common::isGoalPluginEnabled();
- $prettyDate = $processedReport['prettyDate'];
- $reportData = $processedReport['reportData'];
-
- $evolutionMetrics = array();
- $multiSitesAPIMetrics = Piwik_MultiSites_API::getApiMetrics($enhanced = true);
- foreach($multiSitesAPIMetrics as $metricSettings)
- {
- $evolutionMetrics[] = $metricSettings[Piwik_MultiSites_API::METRIC_EVOLUTION_COL_NAME_KEY];
- }
-
- // no decimal for all metrics to shorten SMS content (keeps the monetary sign for revenue metrics)
- $reportData->filter(
- 'ColumnCallbackReplace',
- array(
- array_merge(array_keys($multiSitesAPIMetrics),$evolutionMetrics),
- create_function (
- '$value',
- '
- return preg_replace_callback (
- "'.self::FLOAT_REGEXP.'",
+ const FLOAT_REGEXP = '/[-+]?[0-9]*[\.,]?[0-9]+/';
+ const SMS_CONTENT_TYPE = 'text/plain';
+ const SMS_FILE_EXTENSION = 'sms';
+
+ private $rendering = "";
+
+ public function setLocale($locale)
+ {
+ // nothing to do
+ }
+
+ public function sendToDisk($filename)
+ {
+ return Piwik_ReportRenderer::writeFile($filename, self::SMS_FILE_EXTENSION, $this->rendering);
+ }
+
+ public function sendToBrowserDownload($filename)
+ {
+ Piwik_ReportRenderer::sendToBrowser($filename, self::SMS_FILE_EXTENSION, self::SMS_CONTENT_TYPE, $this->rendering);
+ }
+
+ public function sendToBrowserInline($filename)
+ {
+ Piwik_ReportRenderer::inlineToBrowser(self::SMS_CONTENT_TYPE, $this->rendering);
+ }
+
+ public function getRenderedReport()
+ {
+ return $this->rendering;
+ }
+
+ public function renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata)
+ {
+ // nothing to do
+ }
+
+ public function renderReport($processedReport)
+ {
+ $isGoalPluginEnabled = Piwik_Common::isGoalPluginEnabled();
+ $prettyDate = $processedReport['prettyDate'];
+ $reportData = $processedReport['reportData'];
+
+ $evolutionMetrics = array();
+ $multiSitesAPIMetrics = Piwik_MultiSites_API::getApiMetrics($enhanced = true);
+ foreach ($multiSitesAPIMetrics as $metricSettings) {
+ $evolutionMetrics[] = $metricSettings[Piwik_MultiSites_API::METRIC_EVOLUTION_COL_NAME_KEY];
+ }
+
+ // no decimal for all metrics to shorten SMS content (keeps the monetary sign for revenue metrics)
+ $reportData->filter(
+ 'ColumnCallbackReplace',
+ array(
+ array_merge(array_keys($multiSitesAPIMetrics), $evolutionMetrics),
+ create_function(
+ '$value',
+ '
+ return preg_replace_callback (
+ "' . self::FLOAT_REGEXP . '",
create_function (
\'$matches\',
\'return round($matches[0]);\'
@@ -82,47 +81,46 @@ class Piwik_MobileMessaging_ReportRenderer_Sms extends Piwik_ReportRenderer
$value
);
'
- )
- )
- );
-
- // evolution metrics formatting :
- // - remove monetary, percentage and white spaces to shorten SMS content
- // (this is also needed to be able to test $value != 0 and see if there is an evolution at all in SMSReport.tpl)
- // - adds a plus sign
- $reportData->filter(
- 'ColumnCallbackReplace',
- array(
- $evolutionMetrics,
- create_function (
- '$value',
- '
- $matched = preg_match("'.self::FLOAT_REGEXP.'", $value, $matches);
+ )
+ )
+ );
+
+ // evolution metrics formatting :
+ // - remove monetary, percentage and white spaces to shorten SMS content
+ // (this is also needed to be able to test $value != 0 and see if there is an evolution at all in SMSReport.tpl)
+ // - adds a plus sign
+ $reportData->filter(
+ 'ColumnCallbackReplace',
+ array(
+ $evolutionMetrics,
+ create_function(
+ '$value',
+ '
+ $matched = preg_match("' . self::FLOAT_REGEXP . '", $value, $matches);
return $matched ? sprintf("%+d",$matches[0]) : $value;
'
- )
- )
- );
-
- $dataRows = $reportData->getRows();
- $reportMetadata = $processedReport['reportMetadata'];
- $reportRowsMetadata = $reportMetadata->getRows();
-
- $siteHasECommerce = array();
- foreach($reportRowsMetadata as $rowMetadata)
- {
- $idSite = $rowMetadata->getColumn('idsite');
- $siteHasECommerce[$idSite] = Piwik_Site::isEcommerceEnabledFor($idSite);
- }
-
- $smarty = new Piwik_Smarty();
- $smarty->assign("isGoalPluginEnabled", $isGoalPluginEnabled);
- $smarty->assign("reportRows", $dataRows);
- $smarty->assign("reportRowsMetadata", $reportRowsMetadata);
- $smarty->assign("prettyDate", $prettyDate);
- $smarty->assign("siteHasECommerce", $siteHasECommerce);
- $smarty->assign("displaySiteName", $processedReport['metadata']['action'] == 'getAll');
-
- $this->rendering .= $smarty->fetch(PIWIK_USER_PATH . '/plugins/MobileMessaging/templates/SMSReport.tpl');
- }
+ )
+ )
+ );
+
+ $dataRows = $reportData->getRows();
+ $reportMetadata = $processedReport['reportMetadata'];
+ $reportRowsMetadata = $reportMetadata->getRows();
+
+ $siteHasECommerce = array();
+ foreach ($reportRowsMetadata as $rowMetadata) {
+ $idSite = $rowMetadata->getColumn('idsite');
+ $siteHasECommerce[$idSite] = Piwik_Site::isEcommerceEnabledFor($idSite);
+ }
+
+ $smarty = new Piwik_Smarty();
+ $smarty->assign("isGoalPluginEnabled", $isGoalPluginEnabled);
+ $smarty->assign("reportRows", $dataRows);
+ $smarty->assign("reportRowsMetadata", $reportRowsMetadata);
+ $smarty->assign("prettyDate", $prettyDate);
+ $smarty->assign("siteHasECommerce", $siteHasECommerce);
+ $smarty->assign("displaySiteName", $processedReport['metadata']['action'] == 'getAll');
+
+ $this->rendering .= $smarty->fetch(PIWIK_USER_PATH . '/plugins/MobileMessaging/templates/SMSReport.tpl');
+ }
}
diff --git a/plugins/MobileMessaging/SMSProvider.php b/plugins/MobileMessaging/SMSProvider.php
index 12bd7f0933..9f17311349 100644
--- a/plugins/MobileMessaging/SMSProvider.php
+++ b/plugins/MobileMessaging/SMSProvider.php
@@ -17,13 +17,13 @@
*/
abstract class Piwik_MobileMessaging_SMSProvider
{
- const MAX_GSM_CHARS_IN_ONE_UNIQUE_SMS = 160;
- const MAX_GSM_CHARS_IN_ONE_CONCATENATED_SMS = 153;
- const MAX_UCS2_CHARS_IN_ONE_UNIQUE_SMS = 70;
- const MAX_UCS2_CHARS_IN_ONE_CONCATENATED_SMS = 67;
+ const MAX_GSM_CHARS_IN_ONE_UNIQUE_SMS = 160;
+ const MAX_GSM_CHARS_IN_ONE_CONCATENATED_SMS = 153;
+ const MAX_UCS2_CHARS_IN_ONE_UNIQUE_SMS = 70;
+ const MAX_UCS2_CHARS_IN_ONE_CONCATENATED_SMS = 67;
- static public $availableSMSProviders = array(
- 'Clockwork' => 'You can use <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/"><img src="plugins/MobileMessaging/images/Clockwork.png"/></a> to send SMS Reports from Piwik.<br/>
+ static public $availableSMSProviders = array(
+ 'Clockwork' => 'You can use <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/"><img src="plugins/MobileMessaging/images/Clockwork.png"/></a> to send SMS Reports from Piwik.<br/>
<ul>
<li> First, <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/">get an API Key from Clockwork</a> (Signup is free!)
</li><li> Enter your Clockwork API Key on this page. </li>
@@ -35,141 +35,138 @@ abstract class Piwik_MobileMessaging_SMSProvider
</li>
</ul>
',
- );
-
- /**
- * Return the SMSProvider associated to the provider name $providerName
- *
- * @throws exception If the provider is unknown
- * @param string $providerName
- * @return Piwik_MobileMessaging_SMSProvider
- */
- static public function factory($providerName)
- {
- $className = 'Piwik_MobileMessaging_SMSProvider_' . $providerName;
-
- try {
- Piwik_Loader::loadClass($className);
- return new $className;
- } catch(Exception $e) {
- throw new Exception(
- Piwik_TranslateException(
- 'MobileMessaging_Exception_UnknownProvider',
- array($providerName, implode(', ', array_keys(self::$availableSMSProviders)))
- )
- );
- }
- }
-
- /**
- * Assert whether a given String contains UCS2 characters
- *
- * @param string $string
- * @return bool true if $string contains UCS2 characters
- */
- static public function containsUCS2Characters($string)
- {
- $GSMCharsetAsString = implode(array_keys(Piwik_MobileMessaging_GSMCharset::$GSMCharset));
-
- foreach(self::mb_str_split($string) as $char)
- {
- if(mb_strpos($GSMCharsetAsString, $char) === false) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Truncate $string and append $appendedString at the end if $string can not fit the
- * the $maximumNumberOfConcatenatedSMS.
- *
- * @param string $string String to truncate
- * @param int $maximumNumberOfConcatenatedSMS
- * @param string $appendedString
- * @return string original $string or truncated $string appended with $appendedString
- */
- static public function truncate($string, $maximumNumberOfConcatenatedSMS, $appendedString = 'MobileMessaging_SMS_Content_Too_Long')
- {
- $appendedString = Piwik_Translate($appendedString);
-
- $smsContentContainsUCS2Chars = self::containsUCS2Characters($string);
- $maxCharsAllowed = self::maxCharsAllowed($maximumNumberOfConcatenatedSMS, $smsContentContainsUCS2Chars);
- $sizeOfSMSContent = self::sizeOfSMSContent($string, $smsContentContainsUCS2Chars);
-
- if($sizeOfSMSContent <= $maxCharsAllowed) return $string;
-
- $smsContentContainsUCS2Chars = $smsContentContainsUCS2Chars || self::containsUCS2Characters($appendedString);
- $maxCharsAllowed = self::maxCharsAllowed($maximumNumberOfConcatenatedSMS, $smsContentContainsUCS2Chars);
- $sizeOfSMSContent = self::sizeOfSMSContent($string . $appendedString, $smsContentContainsUCS2Chars);
-
- $sizeToTruncate = $sizeOfSMSContent - $maxCharsAllowed;
-
- $subStrToTruncate = '';
- $subStrSize = 0;
- $reversedStringChars = array_reverse(self::mb_str_split($string));
- for($i = 0; $subStrSize < $sizeToTruncate; $i++)
- {
- $subStrToTruncate = $reversedStringChars[$i] . $subStrToTruncate;
- $subStrSize = self::sizeOfSMSContent($subStrToTruncate, $smsContentContainsUCS2Chars);
- }
-
- return preg_replace('/' . preg_quote($subStrToTruncate) . '$/', $appendedString, $string);
- }
-
- static private function mb_str_split($string)
- {
- return preg_split('//u',$string, -1, PREG_SPLIT_NO_EMPTY);
- }
-
- static private function sizeOfSMSContent($smsContent, $containsUCS2Chars)
- {
- if($containsUCS2Chars) return mb_strlen($smsContent, 'UTF-8');
-
- $sizeOfSMSContent = 0;
- foreach(self::mb_str_split($smsContent) as $char)
- {
- $sizeOfSMSContent += Piwik_MobileMessaging_GSMCharset::$GSMCharset[$char];
- }
- return $sizeOfSMSContent;
- }
-
- static private function maxCharsAllowed($maximumNumberOfConcatenatedSMS, $containsUCS2Chars)
- {
- $maxCharsInOneUniqueSMS = $containsUCS2Chars ? self::MAX_UCS2_CHARS_IN_ONE_UNIQUE_SMS : self::MAX_GSM_CHARS_IN_ONE_UNIQUE_SMS;
- $maxCharsInOneConcatenatedSMS = $containsUCS2Chars ? self::MAX_UCS2_CHARS_IN_ONE_CONCATENATED_SMS : self::MAX_GSM_CHARS_IN_ONE_CONCATENATED_SMS;
-
- $uniqueSMS = $maximumNumberOfConcatenatedSMS == 1;
-
- return $uniqueSMS ?
- $maxCharsInOneUniqueSMS :
- $maxCharsInOneConcatenatedSMS * $maximumNumberOfConcatenatedSMS;
- }
-
- /**
- * verify the SMS API credential
- *
- * @param string $apiKey API Key
- * @return bool true if SMS API credential are valid, false otherwise
- */
- abstract public function verifyCredential($apiKey);
-
- /**
- * get remaining credits
- *
- * @param string $apiKey API Key
- * @return string remaining credits
- */
- abstract public function getCreditLeft($apiKey);
-
- /**
- * send SMS
- *
- * @param string $apiKey
- * @param string $smsText
- * @param string $phoneNumber
- * @return bool true
- */
- abstract public function sendSMS($apiKey, $smsText, $phoneNumber, $from);
+ );
+
+ /**
+ * Return the SMSProvider associated to the provider name $providerName
+ *
+ * @throws exception If the provider is unknown
+ * @param string $providerName
+ * @return Piwik_MobileMessaging_SMSProvider
+ */
+ static public function factory($providerName)
+ {
+ $className = 'Piwik_MobileMessaging_SMSProvider_' . $providerName;
+
+ try {
+ Piwik_Loader::loadClass($className);
+ return new $className;
+ } catch (Exception $e) {
+ throw new Exception(
+ Piwik_TranslateException(
+ 'MobileMessaging_Exception_UnknownProvider',
+ array($providerName, implode(', ', array_keys(self::$availableSMSProviders)))
+ )
+ );
+ }
+ }
+
+ /**
+ * Assert whether a given String contains UCS2 characters
+ *
+ * @param string $string
+ * @return bool true if $string contains UCS2 characters
+ */
+ static public function containsUCS2Characters($string)
+ {
+ $GSMCharsetAsString = implode(array_keys(Piwik_MobileMessaging_GSMCharset::$GSMCharset));
+
+ foreach (self::mb_str_split($string) as $char) {
+ if (mb_strpos($GSMCharsetAsString, $char) === false) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Truncate $string and append $appendedString at the end if $string can not fit the
+ * the $maximumNumberOfConcatenatedSMS.
+ *
+ * @param string $string String to truncate
+ * @param int $maximumNumberOfConcatenatedSMS
+ * @param string $appendedString
+ * @return string original $string or truncated $string appended with $appendedString
+ */
+ static public function truncate($string, $maximumNumberOfConcatenatedSMS, $appendedString = 'MobileMessaging_SMS_Content_Too_Long')
+ {
+ $appendedString = Piwik_Translate($appendedString);
+
+ $smsContentContainsUCS2Chars = self::containsUCS2Characters($string);
+ $maxCharsAllowed = self::maxCharsAllowed($maximumNumberOfConcatenatedSMS, $smsContentContainsUCS2Chars);
+ $sizeOfSMSContent = self::sizeOfSMSContent($string, $smsContentContainsUCS2Chars);
+
+ if ($sizeOfSMSContent <= $maxCharsAllowed) return $string;
+
+ $smsContentContainsUCS2Chars = $smsContentContainsUCS2Chars || self::containsUCS2Characters($appendedString);
+ $maxCharsAllowed = self::maxCharsAllowed($maximumNumberOfConcatenatedSMS, $smsContentContainsUCS2Chars);
+ $sizeOfSMSContent = self::sizeOfSMSContent($string . $appendedString, $smsContentContainsUCS2Chars);
+
+ $sizeToTruncate = $sizeOfSMSContent - $maxCharsAllowed;
+
+ $subStrToTruncate = '';
+ $subStrSize = 0;
+ $reversedStringChars = array_reverse(self::mb_str_split($string));
+ for ($i = 0; $subStrSize < $sizeToTruncate; $i++) {
+ $subStrToTruncate = $reversedStringChars[$i] . $subStrToTruncate;
+ $subStrSize = self::sizeOfSMSContent($subStrToTruncate, $smsContentContainsUCS2Chars);
+ }
+
+ return preg_replace('/' . preg_quote($subStrToTruncate) . '$/', $appendedString, $string);
+ }
+
+ static private function mb_str_split($string)
+ {
+ return preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
+ }
+
+ static private function sizeOfSMSContent($smsContent, $containsUCS2Chars)
+ {
+ if ($containsUCS2Chars) return mb_strlen($smsContent, 'UTF-8');
+
+ $sizeOfSMSContent = 0;
+ foreach (self::mb_str_split($smsContent) as $char) {
+ $sizeOfSMSContent += Piwik_MobileMessaging_GSMCharset::$GSMCharset[$char];
+ }
+ return $sizeOfSMSContent;
+ }
+
+ static private function maxCharsAllowed($maximumNumberOfConcatenatedSMS, $containsUCS2Chars)
+ {
+ $maxCharsInOneUniqueSMS = $containsUCS2Chars ? self::MAX_UCS2_CHARS_IN_ONE_UNIQUE_SMS : self::MAX_GSM_CHARS_IN_ONE_UNIQUE_SMS;
+ $maxCharsInOneConcatenatedSMS = $containsUCS2Chars ? self::MAX_UCS2_CHARS_IN_ONE_CONCATENATED_SMS : self::MAX_GSM_CHARS_IN_ONE_CONCATENATED_SMS;
+
+ $uniqueSMS = $maximumNumberOfConcatenatedSMS == 1;
+
+ return $uniqueSMS ?
+ $maxCharsInOneUniqueSMS :
+ $maxCharsInOneConcatenatedSMS * $maximumNumberOfConcatenatedSMS;
+ }
+
+ /**
+ * verify the SMS API credential
+ *
+ * @param string $apiKey API Key
+ * @return bool true if SMS API credential are valid, false otherwise
+ */
+ abstract public function verifyCredential($apiKey);
+
+ /**
+ * get remaining credits
+ *
+ * @param string $apiKey API Key
+ * @return string remaining credits
+ */
+ abstract public function getCreditLeft($apiKey);
+
+ /**
+ * send SMS
+ *
+ * @param string $apiKey
+ * @param string $smsText
+ * @param string $phoneNumber
+ * @return bool true
+ */
+ abstract public function sendSMS($apiKey, $smsText, $phoneNumber, $from);
}
diff --git a/plugins/MobileMessaging/SMSProvider/Clockwork.php b/plugins/MobileMessaging/SMSProvider/Clockwork.php
index dfb7854fd3..b7a50dfe02 100644
--- a/plugins/MobileMessaging/SMSProvider/Clockwork.php
+++ b/plugins/MobileMessaging/SMSProvider/Clockwork.php
@@ -16,86 +16,85 @@ require_once PIWIK_INCLUDE_PATH . "/plugins/MobileMessaging/APIException.php";
*/
class Piwik_MobileMessaging_SMSProvider_Clockwork extends Piwik_MobileMessaging_SMSProvider
{
- const SOCKET_TIMEOUT = 15;
-
- const BASE_API_URL = 'https://api.mediaburst.co.uk/http';
- const CHECK_CREDIT_RESOURCE = '/credit.aspx';
- const SEND_SMS_RESOURCE = '/send.aspx';
-
- const ERROR_STRING = 'Error';
-
- const MAXIMUM_FROM_LENGTH = 11;
- const MAXIMUM_CONCATENATED_SMS = 3;
-
- public function verifyCredential($apiKey)
- {
- $this->getCreditLeft($apiKey);
-
- return true;
- }
-
- public function sendSMS($apiKey, $smsText, $phoneNumber, $from)
- {
- $from = substr($from, 0, self::MAXIMUM_FROM_LENGTH);
-
- $smsText = self::truncate($smsText, self::MAXIMUM_CONCATENATED_SMS);
-
- $additionalParameters = array(
- 'To' => str_replace('+','', $phoneNumber),
- 'Content' => $smsText,
- 'From' => $from,
- 'Long' => 1,
- 'MsgType' => self::containsUCS2Characters($smsText) ? 'UCS2' : 'TEXT',
- );
-
- $this->issueApiCall(
- $apiKey,
- self::SEND_SMS_RESOURCE,
- $additionalParameters
- );
- }
-
- private function issueApiCall($apiKey, $resource, $additionalParameters = array())
- {
- $accountParameters = array(
- 'Key' => $apiKey,
- );
-
- $parameters = array_merge($accountParameters, $additionalParameters);
-
- $url = self::BASE_API_URL
- . $resource
- . '?' . http_build_query($parameters, '', '&');
-
- $timeout = self::SOCKET_TIMEOUT;
-
- $result = Piwik_Http::sendHttpRequestBy(
- Piwik_Http::getTransportMethod(),
- $url,
- $timeout,
- $userAgent = null,
- $destinationPath = null,
- $file = null,
- $followDepth = 0,
- $acceptLanguage = false,
- $acceptInvalidSslCertificate = true
- );
-
- if(strpos($result, self::ERROR_STRING) !== false)
- {
- throw new Piwik_MobileMessaging_APIException(
- 'Clockwork API returned the following error message : ' . $result
- );
- }
-
- return $result;
- }
-
- public function getCreditLeft($apiKey)
- {
- return $this->issueApiCall(
- $apiKey,
- self::CHECK_CREDIT_RESOURCE
- );
- }
+ const SOCKET_TIMEOUT = 15;
+
+ const BASE_API_URL = 'https://api.mediaburst.co.uk/http';
+ const CHECK_CREDIT_RESOURCE = '/credit.aspx';
+ const SEND_SMS_RESOURCE = '/send.aspx';
+
+ const ERROR_STRING = 'Error';
+
+ const MAXIMUM_FROM_LENGTH = 11;
+ const MAXIMUM_CONCATENATED_SMS = 3;
+
+ public function verifyCredential($apiKey)
+ {
+ $this->getCreditLeft($apiKey);
+
+ return true;
+ }
+
+ public function sendSMS($apiKey, $smsText, $phoneNumber, $from)
+ {
+ $from = substr($from, 0, self::MAXIMUM_FROM_LENGTH);
+
+ $smsText = self::truncate($smsText, self::MAXIMUM_CONCATENATED_SMS);
+
+ $additionalParameters = array(
+ 'To' => str_replace('+', '', $phoneNumber),
+ 'Content' => $smsText,
+ 'From' => $from,
+ 'Long' => 1,
+ 'MsgType' => self::containsUCS2Characters($smsText) ? 'UCS2' : 'TEXT',
+ );
+
+ $this->issueApiCall(
+ $apiKey,
+ self::SEND_SMS_RESOURCE,
+ $additionalParameters
+ );
+ }
+
+ private function issueApiCall($apiKey, $resource, $additionalParameters = array())
+ {
+ $accountParameters = array(
+ 'Key' => $apiKey,
+ );
+
+ $parameters = array_merge($accountParameters, $additionalParameters);
+
+ $url = self::BASE_API_URL
+ . $resource
+ . '?' . http_build_query($parameters, '', '&');
+
+ $timeout = self::SOCKET_TIMEOUT;
+
+ $result = Piwik_Http::sendHttpRequestBy(
+ Piwik_Http::getTransportMethod(),
+ $url,
+ $timeout,
+ $userAgent = null,
+ $destinationPath = null,
+ $file = null,
+ $followDepth = 0,
+ $acceptLanguage = false,
+ $acceptInvalidSslCertificate = true
+ );
+
+ if (strpos($result, self::ERROR_STRING) !== false) {
+ throw new Piwik_MobileMessaging_APIException(
+ 'Clockwork API returned the following error message : ' . $result
+ );
+ }
+
+ return $result;
+ }
+
+ public function getCreditLeft($apiKey)
+ {
+ return $this->issueApiCall(
+ $apiKey,
+ self::CHECK_CREDIT_RESOURCE
+ );
+ }
}
diff --git a/plugins/MobileMessaging/SMSProvider/StubbedProvider.php b/plugins/MobileMessaging/SMSProvider/StubbedProvider.php
index 57f89e73d2..8402eefc51 100644
--- a/plugins/MobileMessaging/SMSProvider/StubbedProvider.php
+++ b/plugins/MobileMessaging/SMSProvider/StubbedProvider.php
@@ -17,18 +17,18 @@
class Piwik_MobileMessaging_SMSProvider_StubbedProvider extends Piwik_MobileMessaging_SMSProvider
{
- public function verifyCredential($apiKey)
- {
- return true;
- }
+ public function verifyCredential($apiKey)
+ {
+ return true;
+ }
- public function sendSMS($apiKey, $smsText, $phoneNumber, $from)
- {
- // nothing to do
- }
+ public function sendSMS($apiKey, $smsText, $phoneNumber, $from)
+ {
+ // nothing to do
+ }
- public function getCreditLeft($apiKey)
- {
- return 1;
- }
+ public function getCreditLeft($apiKey)
+ {
+ return 1;
+ }
} \ No newline at end of file
diff --git a/plugins/MobileMessaging/scripts/MobileMessagingSettings.js b/plugins/MobileMessaging/scripts/MobileMessagingSettings.js
index 840578a1c1..207d4c725d 100644
--- a/plugins/MobileMessaging/scripts/MobileMessagingSettings.js
+++ b/plugins/MobileMessaging/scripts/MobileMessagingSettings.js
@@ -8,103 +8,93 @@
// TODO when UI stabilized, factorize ajax boiler plate accros MobileMessagingSettings javascript functions
var MobileMessagingSettings = MobileMessagingSettings || (function () {
- /************************************************************
- * Private data
- ************************************************************/
- var
- delegatedManagementSelector = 'input[name=delegatedManagement]',
- apiAccountSubmitSelector = '#apiAccountSubmit',
- addPhoneNumberSubmitSelector = '#addPhoneNumberSubmit',
- providersSelector = '#smsProviders',
- providerDescriptionsSelector = '.providerDescription',
- apiKeySelector = '#apiKey',
- countriesSelector = '#countries',
- countryCallingCodeSelector = '#countryCallingCode',
- newPhoneNumberSelector = '#newPhoneNumber',
- suspiciousPhoneNumberSelector = '#suspiciousPhoneNumber',
- validatePhoneNumberSubmitSelector = '.validatePhoneNumberSubmit',
- formDescriptionSelector = '.form-description',
- removePhoneNumberSubmitSelector = '.removePhoneNumberSubmit',
- verificationCodeSelector = '.verificationCode',
- phoneNumberSelector = '.phoneNumber',
- deleteAcountSelector = '#deleteAccount',
- confirmDeleteAccountSelector = '#confirmDeleteAccount',
- accountFormSelector = '#accountForm',
- displayAccountFormSelector = '#displayAccountForm',
- phoneNumberActivatedSelector = '#phoneNumberActivated',
- invalidActivationCodeMsgSelector = '#invalidActivationCode',
- ajaxErrorsSelector = '#ajaxErrorMobileMessagingSettings',
- invalidVerificationCodeAjaxErrorSelector = '#invalidVerificationCodeAjaxError',
- ajaxLoadingSelector = '#ajaxLoadingMobileMessagingSettings';
-
- /************************************************************
- * Private methods
- ************************************************************/
-
- function initUIEvents() {
-
- $(delegatedManagementSelector).change(updateDelegatedManagement);
- $(apiAccountSubmitSelector).click(updateApiAccount);
- $(deleteAcountSelector).click(confirmDeleteApiAccount);
- $(displayAccountFormSelector).click(displayAccountForm);
- $(addPhoneNumberSubmitSelector).click(addPhoneNumber);
- $(newPhoneNumberSelector).keyup(updateSuspiciousPhoneNumberMessage);
- $(validatePhoneNumberSubmitSelector).click(validatePhoneNumber);
- $(removePhoneNumberSubmitSelector).click(removePhoneNumber);
- $(countryCallingCodeSelector).keyup(updateCountry);
- $(countriesSelector).change(updateCountryCallingCode);
- updateCountryCallingCode();
- $(providersSelector).change(updateProviderDescription);
- updateProviderDescription();
- }
-
- function updateCountry()
- {
- var countryCallingCode = $(countryCallingCodeSelector).val();
- if(countryCallingCode != null && countryCallingCode != '')
- {
- var countryToSelect = $(countriesSelector + ' option[value='+countryCallingCode+']');
- if(countryToSelect.size() > 0)
- {
- countryToSelect.attr('selected', 'selected');
- }
- else
- {
- $(countriesSelector + ' option:selected').removeAttr('selected');
- }
- }
- }
-
- function displayAccountForm()
- {
- $(accountFormSelector).show();
- }
-
- function validatePhoneNumber(event)
- {
- var phoneNumberContainer = $(event.target).parent();
- var verificationCodeContainer = phoneNumberContainer.find(verificationCodeSelector);
- var verificationCode = verificationCodeContainer.val();
- var phoneNumber = phoneNumberContainer.find(phoneNumberSelector).html();
-
- if(verificationCode != null && verificationCode != '')
- {
- var success =
- function(response)
- {
- $(phoneNumberActivatedSelector).hide();
- if(!response.value)
- {
- $(invalidVerificationCodeAjaxErrorSelector).html($(invalidActivationCodeMsgSelector).html()).fadeIn();
- }
- else
- {
- $(phoneNumberActivatedSelector).show();
- $(verificationCodeContainer).remove();
- $(phoneNumberContainer).find(validatePhoneNumberSubmitSelector).remove();
- $(phoneNumberContainer).find(formDescriptionSelector).remove();
- }
- };
+ /************************************************************
+ * Private data
+ ************************************************************/
+ var
+ delegatedManagementSelector = 'input[name=delegatedManagement]',
+ apiAccountSubmitSelector = '#apiAccountSubmit',
+ addPhoneNumberSubmitSelector = '#addPhoneNumberSubmit',
+ providersSelector = '#smsProviders',
+ providerDescriptionsSelector = '.providerDescription',
+ apiKeySelector = '#apiKey',
+ countriesSelector = '#countries',
+ countryCallingCodeSelector = '#countryCallingCode',
+ newPhoneNumberSelector = '#newPhoneNumber',
+ suspiciousPhoneNumberSelector = '#suspiciousPhoneNumber',
+ validatePhoneNumberSubmitSelector = '.validatePhoneNumberSubmit',
+ formDescriptionSelector = '.form-description',
+ removePhoneNumberSubmitSelector = '.removePhoneNumberSubmit',
+ verificationCodeSelector = '.verificationCode',
+ phoneNumberSelector = '.phoneNumber',
+ deleteAcountSelector = '#deleteAccount',
+ confirmDeleteAccountSelector = '#confirmDeleteAccount',
+ accountFormSelector = '#accountForm',
+ displayAccountFormSelector = '#displayAccountForm',
+ phoneNumberActivatedSelector = '#phoneNumberActivated',
+ invalidActivationCodeMsgSelector = '#invalidActivationCode',
+ ajaxErrorsSelector = '#ajaxErrorMobileMessagingSettings',
+ invalidVerificationCodeAjaxErrorSelector = '#invalidVerificationCodeAjaxError',
+ ajaxLoadingSelector = '#ajaxLoadingMobileMessagingSettings';
+
+ /************************************************************
+ * Private methods
+ ************************************************************/
+
+ function initUIEvents() {
+
+ $(delegatedManagementSelector).change(updateDelegatedManagement);
+ $(apiAccountSubmitSelector).click(updateApiAccount);
+ $(deleteAcountSelector).click(confirmDeleteApiAccount);
+ $(displayAccountFormSelector).click(displayAccountForm);
+ $(addPhoneNumberSubmitSelector).click(addPhoneNumber);
+ $(newPhoneNumberSelector).keyup(updateSuspiciousPhoneNumberMessage);
+ $(validatePhoneNumberSubmitSelector).click(validatePhoneNumber);
+ $(removePhoneNumberSubmitSelector).click(removePhoneNumber);
+ $(countryCallingCodeSelector).keyup(updateCountry);
+ $(countriesSelector).change(updateCountryCallingCode);
+ updateCountryCallingCode();
+ $(providersSelector).change(updateProviderDescription);
+ updateProviderDescription();
+ }
+
+ function updateCountry() {
+ var countryCallingCode = $(countryCallingCodeSelector).val();
+ if (countryCallingCode != null && countryCallingCode != '') {
+ var countryToSelect = $(countriesSelector + ' option[value=' + countryCallingCode + ']');
+ if (countryToSelect.size() > 0) {
+ countryToSelect.attr('selected', 'selected');
+ }
+ else {
+ $(countriesSelector + ' option:selected').removeAttr('selected');
+ }
+ }
+ }
+
+ function displayAccountForm() {
+ $(accountFormSelector).show();
+ }
+
+ function validatePhoneNumber(event) {
+ var phoneNumberContainer = $(event.target).parent();
+ var verificationCodeContainer = phoneNumberContainer.find(verificationCodeSelector);
+ var verificationCode = verificationCodeContainer.val();
+ var phoneNumber = phoneNumberContainer.find(phoneNumberSelector).html();
+
+ if (verificationCode != null && verificationCode != '') {
+ var success =
+ function (response) {
+ $(phoneNumberActivatedSelector).hide();
+ if (!response.value) {
+ $(invalidVerificationCodeAjaxErrorSelector).html($(invalidActivationCodeMsgSelector).html()).fadeIn();
+ }
+ else {
+ $(phoneNumberActivatedSelector).show();
+ $(verificationCodeContainer).remove();
+ $(phoneNumberContainer).find(validatePhoneNumberSubmitSelector).remove();
+ $(phoneNumberContainer).find(formDescriptionSelector).remove();
+ }
+ };
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
@@ -117,13 +107,12 @@ var MobileMessagingSettings = MobileMessagingSettings || (function () {
ajaxHandler.setLoadingElement(ajaxLoadingSelector);
ajaxHandler.setErrorElement(invalidVerificationCodeAjaxErrorSelector);
ajaxHandler.send(true);
- }
- }
+ }
+ }
- function removePhoneNumber(event)
- {
- var phoneNumberContainer = $(event.target).parent();
- var phoneNumber = phoneNumberContainer.find(phoneNumberSelector).html();
+ function removePhoneNumber(event) {
+ var phoneNumberContainer = $(event.target).parent();
+ var phoneNumber = phoneNumberContainer.find(phoneNumberSelector).html();
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
@@ -136,32 +125,27 @@ var MobileMessagingSettings = MobileMessagingSettings || (function () {
ajaxHandler.setLoadingElement(ajaxLoadingSelector);
ajaxHandler.setErrorElement(ajaxErrorsSelector);
ajaxHandler.send(true);
- }
-
- function updateSuspiciousPhoneNumberMessage()
- {
- var newPhoneNumber = $(newPhoneNumberSelector).val();
-
- // check if number starts with 0
- if($.trim(newPhoneNumber).lastIndexOf('0', 0) === 0)
- {
- $(suspiciousPhoneNumberSelector).show();
- }
- else
- {
- $(suspiciousPhoneNumberSelector).hide();
- }
- }
-
- function addPhoneNumber()
- {
- var newPhoneNumber = $(newPhoneNumberSelector).val();
- var countryCallingCode = $(countryCallingCodeSelector).val();
-
- var phoneNumber = '+' + countryCallingCode + newPhoneNumber;
-
- if(newPhoneNumber != null && newPhoneNumber != '')
- {
+ }
+
+ function updateSuspiciousPhoneNumberMessage() {
+ var newPhoneNumber = $(newPhoneNumberSelector).val();
+
+ // check if number starts with 0
+ if ($.trim(newPhoneNumber).lastIndexOf('0', 0) === 0) {
+ $(suspiciousPhoneNumberSelector).show();
+ }
+ else {
+ $(suspiciousPhoneNumberSelector).hide();
+ }
+ }
+
+ function addPhoneNumber() {
+ var newPhoneNumber = $(newPhoneNumberSelector).val();
+ var countryCallingCode = $(countryCallingCodeSelector).val();
+
+ var phoneNumber = '+' + countryCallingCode + newPhoneNumber;
+
+ if (newPhoneNumber != null && newPhoneNumber != '') {
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
module: 'API',
@@ -173,30 +157,27 @@ var MobileMessagingSettings = MobileMessagingSettings || (function () {
ajaxHandler.setLoadingElement(ajaxLoadingSelector);
ajaxHandler.setErrorElement(ajaxErrorsSelector);
ajaxHandler.send(true);
- }
- }
-
- function updateCountryCallingCode()
- {
- $(countryCallingCodeSelector).val($(countriesSelector + ' option:selected').val());
- }
-
- function updateProviderDescription()
- {
- $(providerDescriptionsSelector).hide();
- $('#' + $(providersSelector + ' option:selected').val() + providerDescriptionsSelector).show();
- }
-
- function updateDelegatedManagement() {
- setDelegatedManagement(getDelegatedManagement());
- }
-
- function confirmDeleteApiAccount()
- {
- piwikHelper.modalConfirm(confirmDeleteAccountSelector, {yes: deleteApiAccount});
- }
-
- function deleteApiAccount() {
+ }
+ }
+
+ function updateCountryCallingCode() {
+ $(countryCallingCodeSelector).val($(countriesSelector + ' option:selected').val());
+ }
+
+ function updateProviderDescription() {
+ $(providerDescriptionsSelector).hide();
+ $('#' + $(providersSelector + ' option:selected').val() + providerDescriptionsSelector).show();
+ }
+
+ function updateDelegatedManagement() {
+ setDelegatedManagement(getDelegatedManagement());
+ }
+
+ function confirmDeleteApiAccount() {
+ piwikHelper.modalConfirm(confirmDeleteAccountSelector, {yes: deleteApiAccount});
+ }
+
+ function deleteApiAccount() {
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
module: 'API',
@@ -207,14 +188,14 @@ var MobileMessagingSettings = MobileMessagingSettings || (function () {
ajaxHandler.setLoadingElement(ajaxLoadingSelector);
ajaxHandler.setErrorElement(ajaxErrorsSelector);
ajaxHandler.send(true);
- }
+ }
- function updateApiAccount() {
+ function updateApiAccount() {
- var provider = $(providersSelector + ' option:selected').val();
- var apiKey = $(apiKeySelector).val();
+ var provider = $(providersSelector + ' option:selected').val();
+ var apiKey = $(apiKeySelector).val();
- if(apiKey != '') {
+ if (apiKey != '') {
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
module: 'API',
@@ -226,10 +207,10 @@ var MobileMessagingSettings = MobileMessagingSettings || (function () {
ajaxHandler.setLoadingElement(ajaxLoadingSelector);
ajaxHandler.setErrorElement(ajaxErrorsSelector);
ajaxHandler.send(true);
- }
- }
+ }
+ }
- function setDelegatedManagement(delegatedManagement) {
+ function setDelegatedManagement(delegatedManagement) {
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
module: 'API',
@@ -241,28 +222,28 @@ var MobileMessagingSettings = MobileMessagingSettings || (function () {
ajaxHandler.setLoadingElement(ajaxLoadingSelector);
ajaxHandler.setErrorElement(ajaxErrorsSelector);
ajaxHandler.send(true);
- }
+ }
- function getDelegatedManagement() {
- return $(delegatedManagementSelector + ':checked').val();
- }
+ function getDelegatedManagement() {
+ return $(delegatedManagementSelector + ':checked').val();
+ }
- /************************************************************
- * Public data and methods
- ************************************************************/
+ /************************************************************
+ * Public data and methods
+ ************************************************************/
- return {
+ return {
- /*
- * Initialize UI events
- */
- initUIEvents: function () {
- initUIEvents();
- }
- };
+ /*
+ * Initialize UI events
+ */
+ initUIEvents: function () {
+ initUIEvents();
+ }
+ };
}());
-$(document).ready( function() {
- MobileMessagingSettings.initUIEvents();
+$(document).ready(function () {
+ MobileMessagingSettings.initUIEvents();
});
diff --git a/plugins/MobileMessaging/templates/ReportParameters.tpl b/plugins/MobileMessaging/templates/ReportParameters.tpl
index dceecaf490..24a6154667 100644
--- a/plugins/MobileMessaging/templates/ReportParameters.tpl
+++ b/plugins/MobileMessaging/templates/ReportParameters.tpl
@@ -1,64 +1,71 @@
<script>
- $(function() {ldelim}
- resetReportParametersFunctions ['{$reportType}'] =
- function () {ldelim}
+ $(function () {ldelim}
+ resetReportParametersFunctions ['{$reportType}'] =
+ function () {ldelim}
- var reportParameters = {ldelim}
- 'phoneNumbers' : [],
- {rdelim};
+ var reportParameters = {ldelim}
+ 'phoneNumbers': [],
+ {rdelim};
- updateReportParametersFunctions['{$reportType}'](reportParameters);
- {rdelim};
+ updateReportParametersFunctions['{$reportType}'](reportParameters);
+ {rdelim
+ };
- updateReportParametersFunctions['{$reportType}'] =
- function (reportParameters) {ldelim}
+ updateReportParametersFunctions['{$reportType}'] =
+ function (reportParameters) {ldelim}
- if(reportParameters == null) return;
+ if (reportParameters == null) return;
- $('[name=phoneNumbers]').removeProp('checked');
- $(reportParameters.phoneNumbers).each(function(index, phoneNumber) {ldelim}
- $('#\\'+phoneNumber).prop('checked','checked');
- {rdelim});
- {rdelim};
+ $('[name=phoneNumbers]').removeProp('checked');
+ $(reportParameters.phoneNumbers).each(function (index, phoneNumber) {ldelim}
+ $('#\\' + phoneNumber).prop('checked', 'checked');
+ {rdelim
+ });
+ {rdelim
+ };
- getReportParametersFunctions['{$reportType}'] =
- function () {ldelim}
+ getReportParametersFunctions['{$reportType}'] =
+ function () {ldelim}
- var parameters = Object();
+ var parameters = Object();
- var selectedPhoneNumbers =
- $.map(
- $('[name=phoneNumbers]:checked'),
- function (phoneNumber) {ldelim}
- return $(phoneNumber).attr('id');
- {rdelim}
- );
+ var selectedPhoneNumbers =
+ $.map(
+ $('[name=phoneNumbers]:checked'),
+ function (phoneNumber) {ldelim}
+ return $(phoneNumber).attr('id');
+ {rdelim
+ }
+ );
- // returning [''] when no phone numbers are selected avoids the "please provide a value for 'parameters'" error message
- parameters.phoneNumbers =
- selectedPhoneNumbers.length > 0 ? selectedPhoneNumbers : [''];
+ // returning [''] when no phone numbers are selected avoids the "please provide a value for 'parameters'" error message
+ parameters.phoneNumbers =
+ selectedPhoneNumbers.length > 0 ? selectedPhoneNumbers : [''];
- return parameters;
- {rdelim};
- {rdelim});
+ return parameters;
+ {rdelim
+ };
+ {rdelim
+ });
</script>
<tr class='{$reportType}'>
- <td class="first">
- {'MobileMessaging_MobileReport_PhoneNumbers'|translate}
- </td>
- <td>
- {if $phoneNumbers|@count eq 0}
- <div class="entityInlineHelp">
- {'MobileMessaging_MobileReport_NoPhoneNumbers'|translate}
- {else}
- {foreach from=$phoneNumbers item=phoneNumber}
- <label><input name='phoneNumbers' type='checkbox' id='{$phoneNumber}'/>{$phoneNumber}</label><br/>
- {/foreach}
- <div class="entityInlineHelp">
- {'MobileMessaging_MobileReport_AdditionalPhoneNumbers'|translate}
- {/if}
- <a href='{url module="MobileMessaging" updated=null}'>{'MobileMessaging_MobileReport_MobileMessagingSettingsLink'|translate}</a>
- </div>
- </td>
+ <td class="first">
+ {'MobileMessaging_MobileReport_PhoneNumbers'|translate}
+ </td>
+ <td>
+ {if $phoneNumbers|@count eq 0}
+ <div class="entityInlineHelp">
+ {'MobileMessaging_MobileReport_NoPhoneNumbers'|translate}
+ {else}
+ {foreach from=$phoneNumbers item=phoneNumber}
+ <label><input name='phoneNumbers' type='checkbox' id='{$phoneNumber}'/>{$phoneNumber}</label>
+ <br/>
+ {/foreach}
+ <div class="entityInlineHelp">
+ {'MobileMessaging_MobileReport_AdditionalPhoneNumbers'|translate}
+ {/if}
+ <a href='{url module="MobileMessaging" updated=null}'>{'MobileMessaging_MobileReport_MobileMessagingSettingsLink'|translate}</a>
+ </div>
+ </td>
</tr>
diff --git a/plugins/MobileMessaging/templates/SMSReport.tpl b/plugins/MobileMessaging/templates/SMSReport.tpl
index 2084ce426f..c74ea165c4 100644
--- a/plugins/MobileMessaging/templates/SMSReport.tpl
+++ b/plugins/MobileMessaging/templates/SMSReport.tpl
@@ -1,71 +1,71 @@
{strip}
- {$prettyDate}.{literal} {/literal}
-
- {if empty($reportRows)}
- {'CoreHome_ThereIsNoDataForThisReport'|translate}
- {/if}
-
- {foreach name=reportRows from=$reportRows item=row key=rowId}
-
- {assign var=rowMetrics value=$row->getColumns()}
- {assign var=rowMetadata value=$reportRowsMetadata[$rowId]->getColumns()}
-
- {if $displaySiteName}
- {$rowMetrics.label}:{literal} {/literal}
- {/if}
-
- {*visits*}
- {$rowMetrics.nb_visits} {'General_ColumnNbVisits'|translate}
- {if $rowMetrics.visits_evolution != 0}
- {literal} {/literal}({$rowMetrics.visits_evolution}%)
- {/if}
-
- {if $rowMetrics.nb_visits != 0}
-
- {*actions*}
- ,{literal} {/literal}
- {$rowMetrics.nb_actions} {'General_ColumnNbActions'|translate}
- {if $rowMetrics.actions_evolution != 0}
- {literal} {/literal}({$rowMetrics.actions_evolution}%)
- {/if}
-
- {if $isGoalPluginEnabled}
-
- {*goal metrics*}
- {if $rowMetrics.nb_conversions != 0}
-
- ,{literal} {/literal}
- {'Goals_ColumnRevenue'|translate}:{literal} {/literal}{$rowMetrics.revenue}
- {if $rowMetrics.revenue_evolution != 0}
- {literal} {/literal}({$rowMetrics.revenue_evolution}%)
- {/if}
-
- ,{literal} {/literal}
- {$rowMetrics.nb_conversions} {'Goals_GoalConversions'|translate}
- {if $rowMetrics.nb_conversions_evolution != 0}
- {literal} {/literal}({$rowMetrics.nb_conversions_evolution}%)
- {/if}
- {/if}
-
- {*eCommerce metrics*}
- {if $siteHasECommerce[$rowMetadata.idsite]}
-
- ,{literal} {/literal}
- {'General_ProductRevenue'|translate}:{literal} {/literal}{$rowMetrics.ecommerce_revenue}
- {if $rowMetrics.ecommerce_revenue_evolution != 0}
- {literal} {/literal}({$rowMetrics.ecommerce_revenue_evolution}%)
- {/if}
-
- ,{literal} {/literal}
- {$rowMetrics.orders} {'General_EcommerceOrders'|translate}
- {if $rowMetrics.orders_evolution != 0}
- {literal} {/literal}({$rowMetrics.orders_evolution}%)
- {/if}
- {/if}
- {/if}
-
- {/if}
-
- {if !$smarty.foreach.reportRows.last}.{literal} {/literal}{/if}
- {/foreach}
+ {$prettyDate}.{literal} {/literal}
+
+ {if empty($reportRows)}
+ {'CoreHome_ThereIsNoDataForThisReport'|translate}
+ {/if}
+
+ {foreach name=reportRows from=$reportRows item=row key=rowId}
+
+ {assign var=rowMetrics value=$row->getColumns()}
+ {assign var=rowMetadata value=$reportRowsMetadata[$rowId]->getColumns()}
+
+ {if $displaySiteName}
+ {$rowMetrics.label}:{literal} {/literal}
+ {/if}
+
+ {*visits*}
+ {$rowMetrics.nb_visits} {'General_ColumnNbVisits'|translate}
+ {if $rowMetrics.visits_evolution != 0}
+ {literal} {/literal}({$rowMetrics.visits_evolution}%)
+ {/if}
+
+ {if $rowMetrics.nb_visits != 0}
+
+ {*actions*}
+ ,{literal} {/literal}
+ {$rowMetrics.nb_actions} {'General_ColumnNbActions'|translate}
+ {if $rowMetrics.actions_evolution != 0}
+ {literal} {/literal}({$rowMetrics.actions_evolution}%)
+ {/if}
+
+ {if $isGoalPluginEnabled}
+
+ {*goal metrics*}
+ {if $rowMetrics.nb_conversions != 0}
+
+ ,{literal} {/literal}
+ {'Goals_ColumnRevenue'|translate}:{literal} {/literal}{$rowMetrics.revenue}
+ {if $rowMetrics.revenue_evolution != 0}
+ {literal} {/literal}({$rowMetrics.revenue_evolution}%)
+ {/if}
+
+ ,{literal} {/literal}
+ {$rowMetrics.nb_conversions} {'Goals_GoalConversions'|translate}
+ {if $rowMetrics.nb_conversions_evolution != 0}
+ {literal} {/literal}({$rowMetrics.nb_conversions_evolution}%)
+ {/if}
+ {/if}
+
+ {*eCommerce metrics*}
+ {if $siteHasECommerce[$rowMetadata.idsite]}
+
+ ,{literal} {/literal}
+ {'General_ProductRevenue'|translate}:{literal} {/literal}{$rowMetrics.ecommerce_revenue}
+ {if $rowMetrics.ecommerce_revenue_evolution != 0}
+ {literal} {/literal}({$rowMetrics.ecommerce_revenue_evolution}%)
+ {/if}
+
+ ,{literal} {/literal}
+ {$rowMetrics.orders} {'General_EcommerceOrders'|translate}
+ {if $rowMetrics.orders_evolution != 0}
+ {literal} {/literal}({$rowMetrics.orders_evolution}%)
+ {/if}
+ {/if}
+ {/if}
+
+ {/if}
+
+ {if !$smarty.foreach.reportRows.last}.{literal} {/literal}{/if}
+ {/foreach}
{/strip}
diff --git a/plugins/MobileMessaging/templates/Settings.tpl b/plugins/MobileMessaging/templates/Settings.tpl
index 77809a0186..547e637eb8 100644
--- a/plugins/MobileMessaging/templates/Settings.tpl
+++ b/plugins/MobileMessaging/templates/Settings.tpl
@@ -2,194 +2,201 @@
{loadJavascriptTranslations plugins='MobileMessaging'}
{literal}
-<style>#accountForm ul {
- list-style: circle;
- margin-left: 17px;
- line-height: 1.5em;
-}
-.providerDescription {
- border: 2px dashed #C5BDAD;
- border-radius: 16px 16px 16px 16px;
- margin-left: 24px;
- padding: 11px;
- width: 600px;
-}
-</style>
+ <style>#accountForm ul {
+ list-style: circle;
+ margin-left: 17px;
+ line-height: 1.5em;
+ }
+
+ .providerDescription {
+ border: 2px dashed #C5BDAD;
+ border-radius: 16px 16px 16px 16px;
+ margin-left: 24px;
+ padding: 11px;
+ width: 600px;
+ }
+ </style>
{/literal}
{if $accountManagedByCurrentUser}
-<h2>{'MobileMessaging_Settings_SMSAPIAccount'|translate}</h2>
- {if $credentialSupplied}
- {'MobileMessaging_Settings_CredentialProvided'|translate:$provider}
- {$creditLeft}
- <br/>
- {'MobileMessaging_Settings_UpdateOrDeleteAccount'|translate:"<a id='displayAccountForm'>":"</a>":"<a id='deleteAccount'>":"</a>"}
- {else}
- {'MobileMessaging_Settings_PleaseSignUp'|translate}
- {/if}
-
- <div id='accountForm' {if $credentialSupplied}style='display: none;'{/if}>
- <br/>
- {'MobileMessaging_Settings_SMSProvider'|translate}
- <select id='smsProviders'>
- {foreach from=$smsProviders key=smsProvider item=description}
- <option value='{$smsProvider}'>
- {$smsProvider}
- </option>
- {/foreach}
- </select>
-
- {'MobileMessaging_Settings_APIKey'|translate}
- <input size='25' id='apiKey'/>
-
- <input type='submit' value='{'General_Save'|translate}' id='apiAccountSubmit' class='submit' />
-
- {foreach from=$smsProviders key=smsProvider item=description}
- <div class='providerDescription' id='{$smsProvider}'>
- {$description}
- </div>
- {/foreach}
-
- </div>
+ <h2>{'MobileMessaging_Settings_SMSAPIAccount'|translate}</h2>
+ {if $credentialSupplied}
+ {'MobileMessaging_Settings_CredentialProvided'|translate:$provider}
+ {$creditLeft}
+ <br/>
+ {'MobileMessaging_Settings_UpdateOrDeleteAccount'|translate:"<a id='displayAccountForm'>":"</a>":"<a id='deleteAccount'>":"</a>"}
+ {else}
+ {'MobileMessaging_Settings_PleaseSignUp'|translate}
+ {/if}
+ <div id='accountForm' {if $credentialSupplied}style='display: none;'{/if}>
+ <br/>
+ {'MobileMessaging_Settings_SMSProvider'|translate}
+ <select id='smsProviders'>
+ {foreach from=$smsProviders key=smsProvider item=description}
+ <option value='{$smsProvider}'>
+ {$smsProvider}
+ </option>
+ {/foreach}
+ </select>
+
+ {'MobileMessaging_Settings_APIKey'|translate}
+ <input size='25' id='apiKey'/>
+
+ <input type='submit' value='{'General_Save'|translate}' id='apiAccountSubmit' class='submit'/>
+
+ {foreach from=$smsProviders key=smsProvider item=description}
+ <div class='providerDescription' id='{$smsProvider}'>
+ {$description}
+ </div>
+ {/foreach}
+
+ </div>
{/if}
{ajaxErrorDiv id=ajaxErrorMobileMessagingSettings}
<h2>{'MobileMessaging_Settings_PhoneNumbers'|translate}</h2>
{if !$credentialSupplied}
- {if $accountManagedByCurrentUser}
- {'MobileMessaging_Settings_CredentialNotProvided'|translate}
- {else}
- {'MobileMessaging_Settings_CredentialNotProvidedByAdmin'|translate}
- {/if}
+ {if $accountManagedByCurrentUser}
+ {'MobileMessaging_Settings_CredentialNotProvided'|translate}
+ {else}
+ {'MobileMessaging_Settings_CredentialNotProvidedByAdmin'|translate}
+ {/if}
{else}
- {'MobileMessaging_Settings_PhoneNumbers_Help'|translate}<br/><br/>
-
- <table style="width:900px;" class="adminTable">
- <tbody><tr>
- <td style="width:480px">
- <strong>{'MobileMessaging_Settings_PhoneNumbers_Add'|translate}</strong><br/><br/>
+ {'MobileMessaging_Settings_PhoneNumbers_Help'|translate}
+ <br/>
+ <br/>
+ <table style="width:900px;" class="adminTable">
+ <tbody>
+ <tr>
+ <td style="width:480px">
+ <strong>{'MobileMessaging_Settings_PhoneNumbers_Add'|translate}</strong><br/><br/>
<span id='suspiciousPhoneNumber' style='display:none;'>
{'MobileMessaging_Settings_SuspiciousPhoneNumber'|translate:'54184032'}<br/><br/>
</span>
-
- + <input id='countryCallingCode' size='4' maxlength='4'/>&nbsp;
- <input id='newPhoneNumber'/>
- <input
- type='submit'
- value='{'MobileMessaging_Settings_AddPhoneNumber'|translate}'
- id='addPhoneNumberSubmit'
- />
-
- <br/>
+
+ + <input id='countryCallingCode' size='4' maxlength='4'/>&nbsp;
+ <input id='newPhoneNumber'/>
+ <input
+ type='submit'
+ value='{'MobileMessaging_Settings_AddPhoneNumber'|translate}'
+ id='addPhoneNumberSubmit'
+ />
+
+ <br/>
<span style=' font-size: 11px;'><span class="form-description">{'MobileMessaging_Settings_CountryCode'|translate}</span>
<span class="form-description" style="margin-left:50px">{'MobileMessaging_Settings_PhoneNumber'|translate}</span></span>
- <br/><br/>
-
- {'MobileMessaging_Settings_PhoneNumbers_CountryCode_Help'|translate}
-
- <select id='countries'>
- <option value=''>&nbsp;</option> {* this is a trick to avoid selecting the first country when no default could be found *}
- {foreach from=$countries key=countryCode item=country}
- <option
- value='{$country.countryCallingCode}'
- {if $defaultCountry==$countryCode} selected='selected' {/if}
- >
- {$country.countryName|truncate:15:'...'}
- </option>
- {/foreach}
- </select>
-
- </td>
- <td style="width:220px">
- {$strHelpAddPhone|inlineHelp}
-
- </td></tr>
- <tr><td colspan="2">
-
- {if $phoneNumbers|@count gt 0}
- <br/>
- <br/>
- <strong>{'MobileMessaging_Settings_ManagePhoneNumbers'|translate}</strong><br/><br/>
- {/if}
-
- {ajaxErrorDiv id=invalidVerificationCodeAjaxError}
-
- <div id='phoneNumberActivated' class="ajaxSuccess" style="display:none;">
- {'MobileMessaging_Settings_PhoneActivated'|translate}
- </div>
-
- <div id='invalidActivationCode' style="display:none;">
- {'MobileMessaging_Settings_InvalidActivationCode'|translate}
- </div>
-
- <ul>
- {foreach from=$phoneNumbers key=phoneNumber item=validated}
- <li>
- <span class='phoneNumber'>{$phoneNumber}</span>
- {if !$validated}
- <input class='verificationCode'/>
- <input
- type='submit'
- value='{'MobileMessaging_Settings_ValidatePhoneNumber'|translate}'
- class='validatePhoneNumberSubmit'
- />
- {/if}
- <input
- type='submit'
- value='{'MobileMessaging_Settings_RemovePhoneNumber'|translate}'
- class='removePhoneNumberSubmit'
- />
- {if !$validated}
- <br/>
- <span class='form-description'>{'MobileMessaging_Settings_VerificationCodeJustSent'|translate}</span>
- {/if}
- <br/>
- <br/>
- </li>
- {/foreach}
- </ul>
-
- </td>
- </tr>
- </tbody></table>
+ <br/><br/>
+
+ {'MobileMessaging_Settings_PhoneNumbers_CountryCode_Help'|translate}
+
+ <select id='countries'>
+ <option value=''>&nbsp;</option> {* this is a trick to avoid selecting the first country when no default could be found *}
+ {foreach from=$countries key=countryCode item=country}
+ <option
+ value='{$country.countryCallingCode}'
+ {if $defaultCountry==$countryCode} selected='selected' {/if}
+ >
+ {$country.countryName|truncate:15:'...'}
+ </option>
+ {/foreach}
+ </select>
+
+ </td>
+ <td style="width:220px">
+ {$strHelpAddPhone|inlineHelp}
+
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+
+ {if $phoneNumbers|@count gt 0}
+ <br/>
+ <br/>
+ <strong>{'MobileMessaging_Settings_ManagePhoneNumbers'|translate}</strong>
+ <br/>
+ <br/>
+ {/if}
+
+ {ajaxErrorDiv id=invalidVerificationCodeAjaxError}
+
+ <div id='phoneNumberActivated' class="ajaxSuccess" style="display:none;">
+ {'MobileMessaging_Settings_PhoneActivated'|translate}
+ </div>
+
+ <div id='invalidActivationCode' style="display:none;">
+ {'MobileMessaging_Settings_InvalidActivationCode'|translate}
+ </div>
+
+ <ul>
+ {foreach from=$phoneNumbers key=phoneNumber item=validated}
+ <li>
+ <span class='phoneNumber'>{$phoneNumber}</span>
+ {if !$validated}
+ <input class='verificationCode'/>
+ <input
+ type='submit'
+ value='{'MobileMessaging_Settings_ValidatePhoneNumber'|translate}'
+ class='validatePhoneNumberSubmit'
+ />
+ {/if}
+ <input
+ type='submit'
+ value='{'MobileMessaging_Settings_RemovePhoneNumber'|translate}'
+ class='removePhoneNumberSubmit'
+ />
+ {if !$validated}
+ <br/>
+ <span class='form-description'>{'MobileMessaging_Settings_VerificationCodeJustSent'|translate}</span>
+ {/if}
+ <br/>
+ <br/>
+ </li>
+ {/foreach}
+ </ul>
+
+ </td>
+ </tr>
+ </tbody>
+ </table>
{/if}
{if $isSuperUser}
- <h2>{'MobileMessaging_Settings_SuperAdmin'|translate}</h2>
-
- <table class='adminTable' style='width:650px;'>
- <tr>
- <td style='width:400px'>{'MobileMessaging_Settings_LetUsersManageAPICredential'|translate}</td>
- <td style='width:250px'>
- <fieldset>
- <label>
- <input
- type='radio'
- value='false'
- name='delegatedManagement' {if !$delegatedManagement} checked='checked'{/if} />
- {'General_No'|translate}
- <br/>
- <span class='form-description'>({'General_Default'|translate}) {'MobileMessaging_Settings_LetUsersManageAPICredential_No_Help'|translate}</span>
- </label>
- <br/>
- <br/>
- <label>
- <input
- type='radio'
- value='true'
- name='delegatedManagement' {if $delegatedManagement} checked='checked'{/if} />
- {'General_Yes'|translate}
- <br/>
- <span class='form-description'>{'MobileMessaging_Settings_LetUsersManageAPICredential_Yes_Help'|translate}</span>
- </label>
-
- </fieldset>
- </tr>
- </table>
+ <h2>{'MobileMessaging_Settings_SuperAdmin'|translate}</h2>
+ <table class='adminTable' style='width:650px;'>
+ <tr>
+ <td style='width:400px'>{'MobileMessaging_Settings_LetUsersManageAPICredential'|translate}</td>
+ <td style='width:250px'>
+ <fieldset>
+ <label>
+ <input
+ type='radio'
+ value='false'
+ name='delegatedManagement' {if !$delegatedManagement} checked='checked'{/if} />
+ {'General_No'|translate}
+ <br/>
+ <span class='form-description'>({'General_Default'|translate}
+ ) {'MobileMessaging_Settings_LetUsersManageAPICredential_No_Help'|translate}</span>
+ </label>
+ <br/>
+ <br/>
+ <label>
+ <input
+ type='radio'
+ value='true'
+ name='delegatedManagement' {if $delegatedManagement} checked='checked'{/if} />
+ {'General_Yes'|translate}
+ <br/>
+ <span class='form-description'>{'MobileMessaging_Settings_LetUsersManageAPICredential_Yes_Help'|translate}</span>
+ </label>
+
+ </fieldset>
+ </tr>
+ </table>
{/if}
{ajaxLoadingDiv id=ajaxLoadingMobileMessagingSettings}
@@ -197,8 +204,8 @@
{include file='CoreAdminHome/templates/footer.tpl'}
<div class='ui-confirm' id='confirmDeleteAccount'>
- <h2>{'MobileMessaging_Settings_DeleteAccountConfirm'|translate}</h2>
- <input role='yes' type='button' value='{'General_Yes'|translate}' />
- <input role='no' type='button' value='{'General_No'|translate}' />
+ <h2>{'MobileMessaging_Settings_DeleteAccountConfirm'|translate}</h2>
+ <input role='yes' type='button' value='{'General_Yes'|translate}'/>
+ <input role='no' type='button' value='{'General_No'|translate}'/>
</div>
diff --git a/plugins/MultiSites/API.php b/plugins/MultiSites/API.php
index 3f4176bbc0..2277291598 100755
--- a/plugins/MultiSites/API.php
+++ b/plugins/MultiSites/API.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_MultiSites
*/
@@ -14,487 +14,447 @@
*/
class Piwik_MultiSites_API
{
- const METRIC_TRANSLATION_KEY = 'translation';
- const METRIC_EVOLUTION_COL_NAME_KEY = 'evolution_column_name';
- const METRIC_RECORD_NAME_KEY = 'record_name';
- const METRIC_IS_ECOMMERCE_KEY = 'is_ecommerce';
-
- const NB_VISITS_METRIC = 'nb_visits';
- const NB_ACTIONS_METRIC = 'nb_actions';
- const NB_PAGEVIEWS_LABEL = 'nb_pageviews';
- const NB_PAGEVIEWS_METRIC = 'Actions_nb_pageviews';
- const GOAL_REVENUE_METRIC = 'revenue';
- const GOAL_CONVERSION_METRIC = 'nb_conversions';
- const ECOMMERCE_ORDERS_METRIC = 'orders';
- const ECOMMERCE_REVENUE_METRIC = 'ecommerce_revenue';
-
- static private $baseMetrics = array(
- self::NB_VISITS_METRIC => array (
- self::METRIC_TRANSLATION_KEY => 'General_ColumnNbVisits',
- self::METRIC_EVOLUTION_COL_NAME_KEY => 'visits_evolution',
- self::METRIC_RECORD_NAME_KEY => self::NB_VISITS_METRIC,
- self::METRIC_IS_ECOMMERCE_KEY => false,
- ),
- self::NB_ACTIONS_METRIC => array (
- self::METRIC_TRANSLATION_KEY => 'General_ColumnNbActions',
- self::METRIC_EVOLUTION_COL_NAME_KEY => 'actions_evolution',
- self::METRIC_RECORD_NAME_KEY => self::NB_ACTIONS_METRIC,
- self::METRIC_IS_ECOMMERCE_KEY => false,
- ),
- self::NB_PAGEVIEWS_LABEL => array (
- self::METRIC_TRANSLATION_KEY => 'General_ColumnPageviews',
- self::METRIC_EVOLUTION_COL_NAME_KEY => 'pageviews_evolution',
- self::METRIC_RECORD_NAME_KEY => self::NB_PAGEVIEWS_METRIC,
- self::METRIC_IS_ECOMMERCE_KEY => false,
- )
- );
-
- /**
- * The singleton instance of this class.
- */
- static private $instance = null;
-
- /**
- * Returns the singleton instance of this class. The instance is created
- * if it hasn't been already.
- *
- * @return Piwik_MultiSites_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
-
- return self::$instance;
- }
-
- /**
- * Returns a report displaying the total visits, actions and revenue, as
- * well as the evolution of these values, of all existing sites over a
- * specified period of time.
- *
- * If the specified period is not a 'range', this function will calculcate
- * evolution metrics. Evolution metrics are metrics that display the
- * percent increase/decrease of another metric since the last period.
- *
- * This function will merge the result of the archive query so each
- * row in the result DataTable will correspond to the metrics of a single
- * site. If a date range is specified, the result will be a
- * DataTable_Array, but it will still be merged.
- *
- * @param string $period The period type to get data for.
- * @param string $date The date(s) to get data for.
- * @param bool|string $segment The segments to get data for.
- * @param bool|string $_restrictSitesToLogin Hack used to enforce we restrict the returned data to the specified username
- * Only used when a scheduled task is running
- * @param bool|string $enhanced When true, return additional goal & ecommerce metrics
- * @param bool|string $pattern If specified, only the website which names (or site ID) match the pattern will be returned using SitesManager.getPatternMatchSites
- * @return Piwik_DataTable
- */
- public function getAll($period, $date, $segment = false, $_restrictSitesToLogin = false, $enhanced = false, $pattern = false)
- {
- Piwik::checkUserHasSomeViewAccess();
-
- $idSites = $this->getSitesIdFromPattern($pattern);
-
- return $this->buildDataTable(
- $idSites,
- $period,
- $date,
- $segment,
- $_restrictSitesToLogin,
- $enhanced,
- $multipleWebsitesRequested = true
- );
- }
-
- /**
- * Fetches the list of sites which names match the string pattern
- *
- * @param $pattern
- * @return array|string
- */
- private function getSitesIdFromPattern($pattern)
- {
- $idSites = 'all';
- if (!empty($pattern)) {
- $sites = Piwik_API_Request::processRequest('SitesManager.getPatternMatchSites',
- array('pattern' => $pattern,
- // added because caller could overwrite these
- 'serialize' => 0,
- 'format' => 'original'));
- if (!empty($sites)) {
- $idSites = array();
- foreach ($sites as $site) {
- $idSites[] = $site['idsite'];
- }
- }
- }
- return $idSites;
- }
-
- /**
- * Same as getAll but for a unique Piwik site
- * @see Piwik_MultiSites_API::getAll()
- *
- * @param int $idSite Id of the Piwik site
- * @param string $period The period type to get data for.
- * @param string $date The date(s) to get data for.
- * @param bool|string $segment The segments to get data for.
- * @param bool|string $_restrictSitesToLogin Hack used to enforce we restrict the returned data to the specified username
- * Only used when a scheduled task is running
- * @param bool|string $enhanced When true, return additional goal & ecommerce metrics
- * @return Piwik_DataTable
- */
- public function getOne($idSite, $period, $date, $segment = false, $_restrictSitesToLogin = false, $enhanced = false)
- {
- Piwik::checkUserHasViewAccess($idSite);
- return $this->buildDataTable(
- $idSite,
- $period,
- $date,
- $segment,
- $_restrictSitesToLogin,
- $enhanced,
- $multipleWebsitesRequested = false
- );
- }
-
- private function buildDataTable($sites, $period, $date, $segment, $_restrictSitesToLogin, $enhanced, $multipleWebsitesRequested)
- {
- $allWebsitesRequested = ($sites == 'all');
- if($allWebsitesRequested)
- {
- if (Piwik::isUserIsSuperUser()
- // Hack: when this API function is called as a Scheduled Task, Super User status is enforced.
- // This means this function would return ALL websites in all cases.
- // Instead, we make sure that only the right set of data is returned
- && !Piwik_TaskScheduler::isTaskBeingExecuted())
- {
- Piwik_Site::setSites(
- Piwik_SitesManager_API::getInstance()->getAllSites()
- );
- }
- else
- {
- Piwik_Site::setSitesFromArray(
- Piwik_SitesManager_API::getInstance()->getSitesWithAtLeastViewAccess($limit = false, $_restrictSitesToLogin)
- );
- }
- }
-
- // build the archive type used to query archive data
- $archive = Piwik_Archive::build(
- $sites,
- $period,
- $date,
- $segment,
- $_restrictSitesToLogin
- );
-
- // determine what data will be displayed
- $fieldsToGet = array();
- $columnNameRewrites = array();
- $apiECommerceMetrics = array();
- $apiMetrics = Piwik_MultiSites_API::getApiMetrics($enhanced);
- foreach($apiMetrics as $metricName => $metricSettings)
- {
- $fieldsToGet[] = $metricSettings[self::METRIC_RECORD_NAME_KEY];
- $columnNameRewrites[$metricSettings[self::METRIC_RECORD_NAME_KEY]] = $metricName;
-
- if($metricSettings[self::METRIC_IS_ECOMMERCE_KEY])
- {
- $apiECommerceMetrics[$metricName] = $metricSettings;
- }
- }
-
- // get the data
- // $dataTable instanceOf Piwik_DataTable_Array
- $dataTable = $archive->getDataTableFromNumeric($fieldsToGet);
-
- // get rid of the DataTable_Array that is created by the IndexedBySite archive type
- if($dataTable instanceof Piwik_DataTable_Array
- && $multipleWebsitesRequested)
- {
- $dataTable = $dataTable->mergeChildren();
- }
- else
- {
- if(!$dataTable instanceof Piwik_DataTable_Array)
- {
- $firstDataTableRow = $dataTable->getFirstRow();
- $firstDataTableRow->setColumn('label', $sites);
- }
- }
-
- // calculate total visits/actions/revenue
- $this->setMetricsTotalsMetadata($dataTable, $apiMetrics);
-
- // if the period isn't a range & a lastN/previousN date isn't used, we get the same
- // data for the last period to show the evolution of visits/actions/revenue
- list($strLastDate, $lastPeriod) = Piwik_Period_Range::getLastDate($date, $period);
- if ($strLastDate !== false)
- {
- if ($lastPeriod !== false)
- {
- // NOTE: no easy way to set last period date metadata when range of dates is requested.
- // will be easier if DataTable_Array::metadata is removed, and metadata that is
- // put there is put directly in Piwik_DataTable::metadata.
- $dataTable->setMetadata(self::getLastPeriodMetadataName('date'), $lastPeriod);
- }
-
- $pastArchive = Piwik_Archive::build('all', $period, $strLastDate, $segment, $_restrictSitesToLogin);
- $pastData = $pastArchive->getDataTableFromNumeric($fieldsToGet);
-
- $pastData = $pastData->mergeChildren();
-
- // use past data to calculate evolution percentages
- $this->calculateEvolutionPercentages($dataTable, $pastData, $apiMetrics);
-
- $this->setPastDataMetadata($dataTable, $pastData, $apiMetrics);
- }
-
- // remove eCommerce related metrics on non eCommerce Piwik sites
- // note: this is not optimal in terms of performance: those metrics should not be retrieved in the first place
- if($enhanced)
- {
- // $dataTableRows instanceOf Piwik_DataTable_Row[]
- $dataTableRows = $dataTable->getRows();
-
- foreach($dataTableRows as $dataTableRow)
- {
- $siteId = $dataTableRow->getColumn('label');
- if(!Piwik_Site::isEcommerceEnabledFor($siteId))
- {
- foreach($apiECommerceMetrics as $metricSettings)
- {
- $dataTableRow->deleteColumn($metricSettings[self::METRIC_RECORD_NAME_KEY]);
- $dataTableRow->deleteColumn($metricSettings[self::METRIC_EVOLUTION_COL_NAME_KEY]);
- }
- }
- }
- }
-
- // move the site id to a metadata column
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'idsite'));
-
- // set the label of each row to the site name
- if($multipleWebsitesRequested)
- {
- $getNameFor = array('Piwik_Site', 'getNameFor');
- $dataTable->filter('ColumnCallbackReplace', array('label', $getNameFor));
- }
- else
- {
- $dataTable->filter('ColumnDelete', array('label'));
- }
-
- // replace record names with user friendly metric names
- $dataTable->filter('ReplaceColumnNames', array($columnNameRewrites));
-
- // Ensures data set sorted, for Metadata output
- $dataTable->filter('Sort', array(self::NB_VISITS_METRIC, 'desc', $naturalSort = false));
-
- // filter rows without visits
- // note: if only one website is queried and there are no visits, we can not remove the row otherwise Piwik_API_ResponseBuilder throws 'Call to a member function getColumns() on a non-object'
- if($multipleWebsitesRequested)
- {
- $dataTable->filter(
- 'ColumnCallbackDeleteRow',
- array(
- self::NB_VISITS_METRIC,
- create_function ( '$value', 'return $value != 0;')
- )
- );
- }
-
- return $dataTable;
- }
-
- /**
- * Performs a binary filter of two
- * DataTables in order to correctly calculate evolution metrics.
- *
- * @param Piwik_DataTable|Piwik_DataTable_Array $currentData
- * @param Piwik_DataTable|Piwik_DataTable_Array $pastData
- * @param array $fields The array of string fields to calculate evolution
- * metrics for.
- */
- private function calculateEvolutionPercentages($currentData, $pastData, $apiMetrics)
- {
- if ($currentData instanceof Piwik_DataTable_Array)
- {
- $pastArray = $pastData->getArray();
- foreach ($currentData->getArray() as $subTable)
- {
- $this->calculateEvolutionPercentages($subTable, current($pastArray), $apiMetrics);
- next($pastArray);
- }
- }
- else
- {
- foreach ($apiMetrics as $metricSettings)
- {
- $currentData->filter(
- 'CalculateEvolutionFilter',
- array(
- $pastData,
- $metricSettings[self::METRIC_EVOLUTION_COL_NAME_KEY],
- $metricSettings[self::METRIC_RECORD_NAME_KEY],
- $quotientPrecision = 1)
- );
- }
- }
- }
-
- /**
- * Sets the total visits, actions & revenue for a DataTable returned by
- * $this->buildDataTable.
- *
- * @param Piwik_DataTable $dataTable
- * @param array $apiMetrics Metrics info.
- * @return array Array of three values: total visits, total actions, total revenue
- */
- private function setMetricsTotalsMetadata( $dataTable, $apiMetrics )
- {
- if ($dataTable instanceof Piwik_DataTable_Array)
- {
- foreach ($dataTable->getArray() as $table)
- {
- $this->setMetricsTotalsMetadata($table, $apiMetrics);
- }
- }
- else
- {
- $revenueMetric = '';
- if (Piwik_Common::isGoalPluginEnabled())
- {
- $revenueMetric = Piwik_Goals::getRecordName(self::GOAL_REVENUE_METRIC);
- }
-
- $totals = array();
- foreach ($apiMetrics as $label => $metricInfo)
- {
- $totalMetadataName = self::getTotalMetadataName($label);
- $totals[$totalMetadataName] = 0;
- }
-
- foreach ($dataTable->getRows() as $row)
- {
- foreach ($apiMetrics as $label => $metricInfo)
- {
- $totalMetadataName = self::getTotalMetadataName($label);
- $totals[$totalMetadataName] += $row->getColumn($metricInfo[self::METRIC_RECORD_NAME_KEY]);
- }
- }
-
- foreach ($totals as $name => $value)
- {
- $dataTable->setMetadata($name, $value);
- }
- }
- }
-
- /**
- * Sets the total evolution metadata for a datatable returned by $this->buildDataTable
- * given data for the last period.
- *
- * @param Piwik_DataTable $dataTable
- * @param Piwik_DataTable $pastData
- * @param array $apiMetrics Metrics info.
- */
- private function setPastDataMetadata( $dataTable, $pastData, $apiMetrics )
- {
- if ($dataTable instanceof Piwik_DataTable_Array)
- {
- $pastArray = $pastData->getArray();
- foreach ($dataTable->getArray() as $subTable)
- {
- $this->setPastDataMetadata($subTable, current($pastArray), $apiMetrics);
- next($pastArray);
- }
- }
- else
- {
- // calculate total visits/actions/revenue for past data
- $this->setMetricsTotalsMetadata($pastData, $apiMetrics);
-
- foreach ($apiMetrics as $label => $metricInfo)
- {
- // get the names of metadata to set
- $totalMetadataName = self::getTotalMetadataName($label);
- $lastPeriodTotalMetadataName = self::getLastPeriodMetadataName($totalMetadataName);
- $totalEvolutionMetadataName =
- self::getTotalMetadataName($metricInfo[self::METRIC_EVOLUTION_COL_NAME_KEY]);
-
- // set last period total
- $pastTotal = $pastData->getMetadata($totalMetadataName);
- $dataTable->setMetadata($lastPeriodTotalMetadataName, $pastTotal);
-
- // calculate & set evolution
- $currentTotal = $dataTable->getMetadata($totalMetadataName);
- $evolution = Piwik_DataTable_Filter_CalculateEvolutionFilter::calculate($currentTotal, $pastTotal);
- $dataTable->setMetadata($totalEvolutionMetadataName, $evolution);
- }
- }
- }
-
- /**
- * @ignore
- */
- public static function getApiMetrics($enhanced)
- {
- $metrics = self::$baseMetrics;
- if (Piwik_Common::isGoalPluginEnabled())
- {
- // goal revenue metric
- $metrics[self::GOAL_REVENUE_METRIC] = array(
- self::METRIC_TRANSLATION_KEY => 'Goals_ColumnRevenue',
- self::METRIC_EVOLUTION_COL_NAME_KEY => self::GOAL_REVENUE_METRIC . '_evolution',
- self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_REVENUE_METRIC),
- self::METRIC_IS_ECOMMERCE_KEY => false,
- );
-
- if($enhanced)
- {
- // number of goal conversions metric
- $metrics[self::GOAL_CONVERSION_METRIC] = array(
- self::METRIC_TRANSLATION_KEY => 'Goals_ColumnConversions',
- self::METRIC_EVOLUTION_COL_NAME_KEY => self::GOAL_CONVERSION_METRIC . '_evolution',
- self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_CONVERSION_METRIC),
- self::METRIC_IS_ECOMMERCE_KEY => false,
- );
-
- // number of orders
- $metrics[self::ECOMMERCE_ORDERS_METRIC] = array(
- self::METRIC_TRANSLATION_KEY => 'General_EcommerceOrders',
- self::METRIC_EVOLUTION_COL_NAME_KEY => self::ECOMMERCE_ORDERS_METRIC . '_evolution',
- self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_CONVERSION_METRIC ,0),
- self::METRIC_IS_ECOMMERCE_KEY => true,
- );
-
- // eCommerce revenue
- $metrics[self::ECOMMERCE_REVENUE_METRIC] = array(
- self::METRIC_TRANSLATION_KEY => 'General_ProductRevenue',
- self::METRIC_EVOLUTION_COL_NAME_KEY => self::ECOMMERCE_REVENUE_METRIC . '_evolution',
- self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_REVENUE_METRIC ,0),
- self::METRIC_IS_ECOMMERCE_KEY => true,
- );
- }
- }
-
- return $metrics;
- }
-
- private static function getTotalMetadataName( $name )
- {
- return 'total_'.$name;
- }
-
- private static function getLastPeriodMetadataName( $name )
- {
- return 'last_period_'.$name;
- }
+ const METRIC_TRANSLATION_KEY = 'translation';
+ const METRIC_EVOLUTION_COL_NAME_KEY = 'evolution_column_name';
+ const METRIC_RECORD_NAME_KEY = 'record_name';
+ const METRIC_IS_ECOMMERCE_KEY = 'is_ecommerce';
+
+ const NB_VISITS_METRIC = 'nb_visits';
+ const NB_ACTIONS_METRIC = 'nb_actions';
+ const NB_PAGEVIEWS_LABEL = 'nb_pageviews';
+ const NB_PAGEVIEWS_METRIC = 'Actions_nb_pageviews';
+ const GOAL_REVENUE_METRIC = 'revenue';
+ const GOAL_CONVERSION_METRIC = 'nb_conversions';
+ const ECOMMERCE_ORDERS_METRIC = 'orders';
+ const ECOMMERCE_REVENUE_METRIC = 'ecommerce_revenue';
+
+ static private $baseMetrics = array(
+ self::NB_VISITS_METRIC => array(
+ self::METRIC_TRANSLATION_KEY => 'General_ColumnNbVisits',
+ self::METRIC_EVOLUTION_COL_NAME_KEY => 'visits_evolution',
+ self::METRIC_RECORD_NAME_KEY => self::NB_VISITS_METRIC,
+ self::METRIC_IS_ECOMMERCE_KEY => false,
+ ),
+ self::NB_ACTIONS_METRIC => array(
+ self::METRIC_TRANSLATION_KEY => 'General_ColumnNbActions',
+ self::METRIC_EVOLUTION_COL_NAME_KEY => 'actions_evolution',
+ self::METRIC_RECORD_NAME_KEY => self::NB_ACTIONS_METRIC,
+ self::METRIC_IS_ECOMMERCE_KEY => false,
+ ),
+ self::NB_PAGEVIEWS_LABEL => array(
+ self::METRIC_TRANSLATION_KEY => 'General_ColumnPageviews',
+ self::METRIC_EVOLUTION_COL_NAME_KEY => 'pageviews_evolution',
+ self::METRIC_RECORD_NAME_KEY => self::NB_PAGEVIEWS_METRIC,
+ self::METRIC_IS_ECOMMERCE_KEY => false,
+ )
+ );
+
+ /**
+ * The singleton instance of this class.
+ */
+ static private $instance = null;
+
+ /**
+ * Returns the singleton instance of this class. The instance is created
+ * if it hasn't been already.
+ *
+ * @return Piwik_MultiSites_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Returns a report displaying the total visits, actions and revenue, as
+ * well as the evolution of these values, of all existing sites over a
+ * specified period of time.
+ *
+ * If the specified period is not a 'range', this function will calculcate
+ * evolution metrics. Evolution metrics are metrics that display the
+ * percent increase/decrease of another metric since the last period.
+ *
+ * This function will merge the result of the archive query so each
+ * row in the result DataTable will correspond to the metrics of a single
+ * site. If a date range is specified, the result will be a
+ * DataTable_Array, but it will still be merged.
+ *
+ * @param string $period The period type to get data for.
+ * @param string $date The date(s) to get data for.
+ * @param bool|string $segment The segments to get data for.
+ * @param bool|string $_restrictSitesToLogin Hack used to enforce we restrict the returned data to the specified username
+ * Only used when a scheduled task is running
+ * @param bool|string $enhanced When true, return additional goal & ecommerce metrics
+ * @param bool|string $pattern If specified, only the website which names (or site ID) match the pattern will be returned using SitesManager.getPatternMatchSites
+ * @return Piwik_DataTable
+ */
+ public function getAll($period, $date, $segment = false, $_restrictSitesToLogin = false, $enhanced = false, $pattern = false)
+ {
+ Piwik::checkUserHasSomeViewAccess();
+
+ $idSites = $this->getSitesIdFromPattern($pattern);
+
+ return $this->buildDataTable(
+ $idSites,
+ $period,
+ $date,
+ $segment,
+ $_restrictSitesToLogin,
+ $enhanced,
+ $multipleWebsitesRequested = true
+ );
+ }
+
+ /**
+ * Fetches the list of sites which names match the string pattern
+ *
+ * @param $pattern
+ * @return array|string
+ */
+ private function getSitesIdFromPattern($pattern)
+ {
+ $idSites = 'all';
+ if (!empty($pattern)) {
+ $sites = Piwik_API_Request::processRequest('SitesManager.getPatternMatchSites',
+ array('pattern' => $pattern,
+ // added because caller could overwrite these
+ 'serialize' => 0,
+ 'format' => 'original'));
+ if (!empty($sites)) {
+ $idSites = array();
+ foreach ($sites as $site) {
+ $idSites[] = $site['idsite'];
+ }
+ }
+ }
+ return $idSites;
+ }
+
+ /**
+ * Same as getAll but for a unique Piwik site
+ * @see Piwik_MultiSites_API::getAll()
+ *
+ * @param int $idSite Id of the Piwik site
+ * @param string $period The period type to get data for.
+ * @param string $date The date(s) to get data for.
+ * @param bool|string $segment The segments to get data for.
+ * @param bool|string $_restrictSitesToLogin Hack used to enforce we restrict the returned data to the specified username
+ * Only used when a scheduled task is running
+ * @param bool|string $enhanced When true, return additional goal & ecommerce metrics
+ * @return Piwik_DataTable
+ */
+ public function getOne($idSite, $period, $date, $segment = false, $_restrictSitesToLogin = false, $enhanced = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ return $this->buildDataTable(
+ $idSite,
+ $period,
+ $date,
+ $segment,
+ $_restrictSitesToLogin,
+ $enhanced,
+ $multipleWebsitesRequested = false
+ );
+ }
+
+ private function buildDataTable($sites, $period, $date, $segment, $_restrictSitesToLogin, $enhanced, $multipleWebsitesRequested)
+ {
+ $allWebsitesRequested = ($sites == 'all');
+ if ($allWebsitesRequested) {
+ if (Piwik::isUserIsSuperUser()
+ // Hack: when this API function is called as a Scheduled Task, Super User status is enforced.
+ // This means this function would return ALL websites in all cases.
+ // Instead, we make sure that only the right set of data is returned
+ && !Piwik_TaskScheduler::isTaskBeingExecuted()
+ ) {
+ Piwik_Site::setSites(
+ Piwik_SitesManager_API::getInstance()->getAllSites()
+ );
+ } else {
+ Piwik_Site::setSitesFromArray(
+ Piwik_SitesManager_API::getInstance()->getSitesWithAtLeastViewAccess($limit = false, $_restrictSitesToLogin)
+ );
+ }
+ }
+
+ // build the archive type used to query archive data
+ $archive = Piwik_Archive::build(
+ $sites,
+ $period,
+ $date,
+ $segment,
+ $_restrictSitesToLogin
+ );
+
+ // determine what data will be displayed
+ $fieldsToGet = array();
+ $columnNameRewrites = array();
+ $apiECommerceMetrics = array();
+ $apiMetrics = Piwik_MultiSites_API::getApiMetrics($enhanced);
+ foreach ($apiMetrics as $metricName => $metricSettings) {
+ $fieldsToGet[] = $metricSettings[self::METRIC_RECORD_NAME_KEY];
+ $columnNameRewrites[$metricSettings[self::METRIC_RECORD_NAME_KEY]] = $metricName;
+
+ if ($metricSettings[self::METRIC_IS_ECOMMERCE_KEY]) {
+ $apiECommerceMetrics[$metricName] = $metricSettings;
+ }
+ }
+
+ // get the data
+ // $dataTable instanceOf Piwik_DataTable_Array
+ $dataTable = $archive->getDataTableFromNumeric($fieldsToGet);
+
+ // get rid of the DataTable_Array that is created by the IndexedBySite archive type
+ if ($dataTable instanceof Piwik_DataTable_Array
+ && $multipleWebsitesRequested
+ ) {
+ $dataTable = $dataTable->mergeChildren();
+ } else {
+ if (!$dataTable instanceof Piwik_DataTable_Array) {
+ $firstDataTableRow = $dataTable->getFirstRow();
+ $firstDataTableRow->setColumn('label', $sites);
+ }
+ }
+
+ // calculate total visits/actions/revenue
+ $this->setMetricsTotalsMetadata($dataTable, $apiMetrics);
+
+ // if the period isn't a range & a lastN/previousN date isn't used, we get the same
+ // data for the last period to show the evolution of visits/actions/revenue
+ list($strLastDate, $lastPeriod) = Piwik_Period_Range::getLastDate($date, $period);
+ if ($strLastDate !== false) {
+ if ($lastPeriod !== false) {
+ // NOTE: no easy way to set last period date metadata when range of dates is requested.
+ // will be easier if DataTable_Array::metadata is removed, and metadata that is
+ // put there is put directly in Piwik_DataTable::metadata.
+ $dataTable->setMetadata(self::getLastPeriodMetadataName('date'), $lastPeriod);
+ }
+
+ $pastArchive = Piwik_Archive::build('all', $period, $strLastDate, $segment, $_restrictSitesToLogin);
+ $pastData = $pastArchive->getDataTableFromNumeric($fieldsToGet);
+
+ $pastData = $pastData->mergeChildren();
+
+ // use past data to calculate evolution percentages
+ $this->calculateEvolutionPercentages($dataTable, $pastData, $apiMetrics);
+
+ $this->setPastDataMetadata($dataTable, $pastData, $apiMetrics);
+ }
+
+ // remove eCommerce related metrics on non eCommerce Piwik sites
+ // note: this is not optimal in terms of performance: those metrics should not be retrieved in the first place
+ if ($enhanced) {
+ // $dataTableRows instanceOf Piwik_DataTable_Row[]
+ $dataTableRows = $dataTable->getRows();
+
+ foreach ($dataTableRows as $dataTableRow) {
+ $siteId = $dataTableRow->getColumn('label');
+ if (!Piwik_Site::isEcommerceEnabledFor($siteId)) {
+ foreach ($apiECommerceMetrics as $metricSettings) {
+ $dataTableRow->deleteColumn($metricSettings[self::METRIC_RECORD_NAME_KEY]);
+ $dataTableRow->deleteColumn($metricSettings[self::METRIC_EVOLUTION_COL_NAME_KEY]);
+ }
+ }
+ }
+ }
+
+ // move the site id to a metadata column
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'idsite'));
+
+ // set the label of each row to the site name
+ if ($multipleWebsitesRequested) {
+ $getNameFor = array('Piwik_Site', 'getNameFor');
+ $dataTable->filter('ColumnCallbackReplace', array('label', $getNameFor));
+ } else {
+ $dataTable->filter('ColumnDelete', array('label'));
+ }
+
+ // replace record names with user friendly metric names
+ $dataTable->filter('ReplaceColumnNames', array($columnNameRewrites));
+
+ // Ensures data set sorted, for Metadata output
+ $dataTable->filter('Sort', array(self::NB_VISITS_METRIC, 'desc', $naturalSort = false));
+
+ // filter rows without visits
+ // note: if only one website is queried and there are no visits, we can not remove the row otherwise Piwik_API_ResponseBuilder throws 'Call to a member function getColumns() on a non-object'
+ if ($multipleWebsitesRequested) {
+ $dataTable->filter(
+ 'ColumnCallbackDeleteRow',
+ array(
+ self::NB_VISITS_METRIC,
+ create_function('$value', 'return $value != 0;')
+ )
+ );
+ }
+
+ return $dataTable;
+ }
+
+ /**
+ * Performs a binary filter of two
+ * DataTables in order to correctly calculate evolution metrics.
+ *
+ * @param Piwik_DataTable|Piwik_DataTable_Array $currentData
+ * @param Piwik_DataTable|Piwik_DataTable_Array $pastData
+ * @param array $fields The array of string fields to calculate evolution
+ * metrics for.
+ */
+ private function calculateEvolutionPercentages($currentData, $pastData, $apiMetrics)
+ {
+ if ($currentData instanceof Piwik_DataTable_Array) {
+ $pastArray = $pastData->getArray();
+ foreach ($currentData->getArray() as $subTable) {
+ $this->calculateEvolutionPercentages($subTable, current($pastArray), $apiMetrics);
+ next($pastArray);
+ }
+ } else {
+ foreach ($apiMetrics as $metricSettings) {
+ $currentData->filter(
+ 'CalculateEvolutionFilter',
+ array(
+ $pastData,
+ $metricSettings[self::METRIC_EVOLUTION_COL_NAME_KEY],
+ $metricSettings[self::METRIC_RECORD_NAME_KEY],
+ $quotientPrecision = 1)
+ );
+ }
+ }
+ }
+
+ /**
+ * Sets the total visits, actions & revenue for a DataTable returned by
+ * $this->buildDataTable.
+ *
+ * @param Piwik_DataTable $dataTable
+ * @param array $apiMetrics Metrics info.
+ * @return array Array of three values: total visits, total actions, total revenue
+ */
+ private function setMetricsTotalsMetadata($dataTable, $apiMetrics)
+ {
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ foreach ($dataTable->getArray() as $table) {
+ $this->setMetricsTotalsMetadata($table, $apiMetrics);
+ }
+ } else {
+ $revenueMetric = '';
+ if (Piwik_Common::isGoalPluginEnabled()) {
+ $revenueMetric = Piwik_Goals::getRecordName(self::GOAL_REVENUE_METRIC);
+ }
+
+ $totals = array();
+ foreach ($apiMetrics as $label => $metricInfo) {
+ $totalMetadataName = self::getTotalMetadataName($label);
+ $totals[$totalMetadataName] = 0;
+ }
+
+ foreach ($dataTable->getRows() as $row) {
+ foreach ($apiMetrics as $label => $metricInfo) {
+ $totalMetadataName = self::getTotalMetadataName($label);
+ $totals[$totalMetadataName] += $row->getColumn($metricInfo[self::METRIC_RECORD_NAME_KEY]);
+ }
+ }
+
+ foreach ($totals as $name => $value) {
+ $dataTable->setMetadata($name, $value);
+ }
+ }
+ }
+
+ /**
+ * Sets the total evolution metadata for a datatable returned by $this->buildDataTable
+ * given data for the last period.
+ *
+ * @param Piwik_DataTable $dataTable
+ * @param Piwik_DataTable $pastData
+ * @param array $apiMetrics Metrics info.
+ */
+ private function setPastDataMetadata($dataTable, $pastData, $apiMetrics)
+ {
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ $pastArray = $pastData->getArray();
+ foreach ($dataTable->getArray() as $subTable) {
+ $this->setPastDataMetadata($subTable, current($pastArray), $apiMetrics);
+ next($pastArray);
+ }
+ } else {
+ // calculate total visits/actions/revenue for past data
+ $this->setMetricsTotalsMetadata($pastData, $apiMetrics);
+
+ foreach ($apiMetrics as $label => $metricInfo) {
+ // get the names of metadata to set
+ $totalMetadataName = self::getTotalMetadataName($label);
+ $lastPeriodTotalMetadataName = self::getLastPeriodMetadataName($totalMetadataName);
+ $totalEvolutionMetadataName =
+ self::getTotalMetadataName($metricInfo[self::METRIC_EVOLUTION_COL_NAME_KEY]);
+
+ // set last period total
+ $pastTotal = $pastData->getMetadata($totalMetadataName);
+ $dataTable->setMetadata($lastPeriodTotalMetadataName, $pastTotal);
+
+ // calculate & set evolution
+ $currentTotal = $dataTable->getMetadata($totalMetadataName);
+ $evolution = Piwik_DataTable_Filter_CalculateEvolutionFilter::calculate($currentTotal, $pastTotal);
+ $dataTable->setMetadata($totalEvolutionMetadataName, $evolution);
+ }
+ }
+ }
+
+ /**
+ * @ignore
+ */
+ public static function getApiMetrics($enhanced)
+ {
+ $metrics = self::$baseMetrics;
+ if (Piwik_Common::isGoalPluginEnabled()) {
+ // goal revenue metric
+ $metrics[self::GOAL_REVENUE_METRIC] = array(
+ self::METRIC_TRANSLATION_KEY => 'Goals_ColumnRevenue',
+ self::METRIC_EVOLUTION_COL_NAME_KEY => self::GOAL_REVENUE_METRIC . '_evolution',
+ self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_REVENUE_METRIC),
+ self::METRIC_IS_ECOMMERCE_KEY => false,
+ );
+
+ if ($enhanced) {
+ // number of goal conversions metric
+ $metrics[self::GOAL_CONVERSION_METRIC] = array(
+ self::METRIC_TRANSLATION_KEY => 'Goals_ColumnConversions',
+ self::METRIC_EVOLUTION_COL_NAME_KEY => self::GOAL_CONVERSION_METRIC . '_evolution',
+ self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_CONVERSION_METRIC),
+ self::METRIC_IS_ECOMMERCE_KEY => false,
+ );
+
+ // number of orders
+ $metrics[self::ECOMMERCE_ORDERS_METRIC] = array(
+ self::METRIC_TRANSLATION_KEY => 'General_EcommerceOrders',
+ self::METRIC_EVOLUTION_COL_NAME_KEY => self::ECOMMERCE_ORDERS_METRIC . '_evolution',
+ self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_CONVERSION_METRIC, 0),
+ self::METRIC_IS_ECOMMERCE_KEY => true,
+ );
+
+ // eCommerce revenue
+ $metrics[self::ECOMMERCE_REVENUE_METRIC] = array(
+ self::METRIC_TRANSLATION_KEY => 'General_ProductRevenue',
+ self::METRIC_EVOLUTION_COL_NAME_KEY => self::ECOMMERCE_REVENUE_METRIC . '_evolution',
+ self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_REVENUE_METRIC, 0),
+ self::METRIC_IS_ECOMMERCE_KEY => true,
+ );
+ }
+ }
+
+ return $metrics;
+ }
+
+ private static function getTotalMetadataName($name)
+ {
+ return 'total_' . $name;
+ }
+
+ private static function getLastPeriodMetadataName($name)
+ {
+ return 'last_period_' . $name;
+ }
}
diff --git a/plugins/MultiSites/Controller.php b/plugins/MultiSites/Controller.php
index 46ae9c73d9..cb63dc5746 100644
--- a/plugins/MultiSites/Controller.php
+++ b/plugins/MultiSites/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_MultiSites
*/
@@ -15,227 +15,208 @@
*/
class Piwik_MultiSites_Controller extends Piwik_Controller
{
- protected $orderBy = 'visits';
- protected $order = 'desc';
- protected $evolutionBy = 'visits';
- protected $page = 1;
- protected $limit = 0;
- protected $period;
- protected $date;
-
- function __construct()
- {
- parent::__construct();
-
- $this->limit = Piwik_Config::getInstance()->General['all_websites_website_per_page'];
- }
-
- function index()
- {
- $this->getSitesInfo($isWidgetized = false);
- }
-
- function standalone()
- {
- $this->getSitesInfo($isWidgetized = true);
- }
-
-
- public function getSitesInfo($isWidgetized)
- {
- Piwik::checkUserHasSomeViewAccess();
- $displayRevenueColumn = Piwik_Common::isGoalPluginEnabled();
-
- $date = Piwik_Common::getRequestVar('date', 'today');
- $period = Piwik_Common::getRequestVar('period', 'day');
- $siteIds = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess();
- list($minDate, $maxDate) = $this->getMinMaxDateAcrossWebsites($siteIds);
-
- // overwrites the default Date set in the parent controller
- // Instead of the default current website's local date,
- // we set "today" or "yesterday" based on the default Piwik timezone
- $piwikDefaultTimezone = Piwik_SitesManager_API::getInstance()->getDefaultTimezone();
- if($period != 'range')
- {
- $date = $this->getDateParameterInTimezone($date, $piwikDefaultTimezone);
- $this->setDate($date);
- $date = $date->toString();
- }
- $dataTable = Piwik_MultiSites_API::getInstance()->getAll($period, $date, $segment = false);
-
-
- // put data into a form the template will understand better
- $digestableData = array();
- foreach($siteIds as $idSite)
- {
- $isEcommerceEnabled = Piwik_Site::isEcommerceEnabledFor($idSite);
-
- $digestableData[$idSite] = array(
- 'idsite' => $idSite,
- 'main_url' => Piwik_Site::getMainUrlFor($idSite),
- 'name' => Piwik_Site::getNameFor($idSite),
- 'visits' => 0,
- 'pageviews' => 0
- );
-
- if ($period != 'range')
- {
- $digestableData[$idSite]['visits_evolution'] = 0;
- $digestableData[$idSite]['pageviews_evolution'] = 0;
- }
-
- if ($displayRevenueColumn)
- {
- $revenueDefault = $isEcommerceEnabled ? 0 : "'-'";
-
- if ($period != 'range')
- {
- $digestableData[$idSite]['revenue_evolution'] = $revenueDefault;
- }
- }
- }
-
- foreach($dataTable->getRows() as $row)
- {
- $idsite = (int)$row->getMetadata('idsite');
-
- $site = &$digestableData[$idsite];
-
- $site['visits'] = (int)$row->getColumn('nb_visits');
- $site['pageviews'] = (int)$row->getColumn('nb_pageviews');
-
- if ($displayRevenueColumn)
- {
- if ($row->getColumn('revenue') !== false)
- {
- $site['revenue'] = $row->getColumn('revenue');
- }
- }
-
- if ($period != 'range')
- {
- $site['visits_evolution'] = $row->getColumn('visits_evolution');
- $site['pageviews_evolution'] = $row->getColumn('pageviews_evolution');
-
- if ($displayRevenueColumn)
- {
- $site['revenue_evolution'] = $row->getColumn('revenue_evolution');
- }
- }
- }
-
- $this->applyPrettyMoney($digestableData);
-
- $view = new Piwik_View("MultiSites/templates/index.tpl");
- $view->isWidgetized = $isWidgetized;
- $view->sitesData = array_values($digestableData);
- $view->evolutionBy = $this->evolutionBy;
- $view->period = $period;
- $view->page = $this->page;
- $view->limit = $this->limit;
- $view->orderBy = $this->orderBy;
- $view->order = $this->order;
- $view->totalVisits = $dataTable->getMetadata('total_nb_visits');
- $view->totalRevenue = $dataTable->getMetadata('total_revenue');
-
- $view->displayRevenueColumn = $displayRevenueColumn;
- $view->totalPageviews = $dataTable->getMetadata('total_nb_pageviews');
- $view->pastTotalVisits = $dataTable->getMetadata('last_period_total_nb_visits');
- $view->totalVisitsEvolution = $dataTable->getMetadata('total_visits_evolution');
- if ($view->totalVisitsEvolution > 0)
- {
- $view->totalVisitsEvolution = '+'.$view->totalVisitsEvolution;
- }
-
- if ($period != 'range')
- {
- $lastPeriod = Piwik_Period::factory($period, $dataTable->getMetadata('last_period_date'));
- $view->pastPeriodPretty = self::getCalendarPrettyDate($lastPeriod);
- }
-
- $params = $this->getGraphParamsModified();
- $view->dateSparkline = $period == 'range' ? $date : $params['date'];
-
- $view->autoRefreshTodayReport = false;
- // if the current date is today, or yesterday,
- // in case the website is set to UTC-12), or today in UTC+14, we refresh the page every 5min
- if(in_array($date, array( 'today', date('Y-m-d'),
- 'yesterday', Piwik_Date::factory('yesterday')->toString('Y-m-d'),
- Piwik_Date::factory('now', 'UTC+14')->toString('Y-m-d'))))
- {
-
- $view->autoRefreshTodayReport = Piwik_Config::getInstance()->General['multisites_refresh_after_seconds'];
- }
- $this->setGeneralVariablesView($view);
- $this->setMinDateView($minDate, $view);
- $this->setMaxDateView($maxDate, $view);
- $view->show_sparklines = Piwik_Config::getInstance()->General['show_multisites_sparklines'];
-
- echo $view->render();
- }
-
- /**
- * The Multisites reports displays the first calendar date as the earliest day available for all websites.
- * Also, today is the later "today" available across all timezones.
- * @param array $siteIds Array of IDs for each site being displayed.
- * @return array of two Piwik_Date instances. First is the min-date & the second
- * is the max date.
- */
- private function getMinMaxDateAcrossWebsites($siteIds)
- {
- $now = Piwik_Date::now();
-
- $minDate = null;
- $maxDate = $now->subDay(1)->getTimestamp();
- foreach($siteIds as $idsite)
- {
- // look for 'now' in the website's timezone
- $timezone = Piwik_Site::getTimezoneFor($idsite);
- $date = Piwik_Date::adjustForTimezone($now->getTimestamp(), $timezone);
- if($date > $maxDate)
- {
- $maxDate = $date;
- }
-
- // look for the absolute minimum date
- $creationDate = Piwik_Site::getCreationDateFor($idsite);
- $date = Piwik_Date::adjustForTimezone(strtotime($creationDate), $timezone);
- if(is_null($minDate) || $date < $minDate)
- {
- $minDate = $date;
- }
- }
-
- return array(Piwik_Date::factory($minDate), Piwik_Date::factory($maxDate));
- }
-
- protected function applyPrettyMoney(&$sites)
- {
- foreach($sites as $idsite => &$site)
- {
- $revenue = "-";
- if(!empty($site['revenue']))
- {
- $revenue = Piwik::getPrettyMoney($site['revenue'], $site['idsite'], $htmlAllowed = false);
- }
- $site['revenue'] = '"'. $revenue . '"';
- }
- }
-
- public function getEvolutionGraph( $fetch = false, $columns = false)
- {
- if(empty($columns))
- {
- $columns = Piwik_Common::getRequestVar('columns');
- }
- $api = "API.get";
-
- if($columns == 'revenue')
- {
- $api = "Goals.get";
- }
- $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, $api);
- $view->setColumnsToDisplay($columns);
- return $this->renderView($view, $fetch);
- }
+ protected $orderBy = 'visits';
+ protected $order = 'desc';
+ protected $evolutionBy = 'visits';
+ protected $page = 1;
+ protected $limit = 0;
+ protected $period;
+ protected $date;
+
+ function __construct()
+ {
+ parent::__construct();
+
+ $this->limit = Piwik_Config::getInstance()->General['all_websites_website_per_page'];
+ }
+
+ function index()
+ {
+ $this->getSitesInfo($isWidgetized = false);
+ }
+
+ function standalone()
+ {
+ $this->getSitesInfo($isWidgetized = true);
+ }
+
+
+ public function getSitesInfo($isWidgetized)
+ {
+ Piwik::checkUserHasSomeViewAccess();
+ $displayRevenueColumn = Piwik_Common::isGoalPluginEnabled();
+
+ $date = Piwik_Common::getRequestVar('date', 'today');
+ $period = Piwik_Common::getRequestVar('period', 'day');
+ $siteIds = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess();
+ list($minDate, $maxDate) = $this->getMinMaxDateAcrossWebsites($siteIds);
+
+ // overwrites the default Date set in the parent controller
+ // Instead of the default current website's local date,
+ // we set "today" or "yesterday" based on the default Piwik timezone
+ $piwikDefaultTimezone = Piwik_SitesManager_API::getInstance()->getDefaultTimezone();
+ if ($period != 'range') {
+ $date = $this->getDateParameterInTimezone($date, $piwikDefaultTimezone);
+ $this->setDate($date);
+ $date = $date->toString();
+ }
+ $dataTable = Piwik_MultiSites_API::getInstance()->getAll($period, $date, $segment = false);
+
+
+ // put data into a form the template will understand better
+ $digestableData = array();
+ foreach ($siteIds as $idSite) {
+ $isEcommerceEnabled = Piwik_Site::isEcommerceEnabledFor($idSite);
+
+ $digestableData[$idSite] = array(
+ 'idsite' => $idSite,
+ 'main_url' => Piwik_Site::getMainUrlFor($idSite),
+ 'name' => Piwik_Site::getNameFor($idSite),
+ 'visits' => 0,
+ 'pageviews' => 0
+ );
+
+ if ($period != 'range') {
+ $digestableData[$idSite]['visits_evolution'] = 0;
+ $digestableData[$idSite]['pageviews_evolution'] = 0;
+ }
+
+ if ($displayRevenueColumn) {
+ $revenueDefault = $isEcommerceEnabled ? 0 : "'-'";
+
+ if ($period != 'range') {
+ $digestableData[$idSite]['revenue_evolution'] = $revenueDefault;
+ }
+ }
+ }
+
+ foreach ($dataTable->getRows() as $row) {
+ $idsite = (int)$row->getMetadata('idsite');
+
+ $site = & $digestableData[$idsite];
+
+ $site['visits'] = (int)$row->getColumn('nb_visits');
+ $site['pageviews'] = (int)$row->getColumn('nb_pageviews');
+
+ if ($displayRevenueColumn) {
+ if ($row->getColumn('revenue') !== false) {
+ $site['revenue'] = $row->getColumn('revenue');
+ }
+ }
+
+ if ($period != 'range') {
+ $site['visits_evolution'] = $row->getColumn('visits_evolution');
+ $site['pageviews_evolution'] = $row->getColumn('pageviews_evolution');
+
+ if ($displayRevenueColumn) {
+ $site['revenue_evolution'] = $row->getColumn('revenue_evolution');
+ }
+ }
+ }
+
+ $this->applyPrettyMoney($digestableData);
+
+ $view = new Piwik_View("MultiSites/templates/index.tpl");
+ $view->isWidgetized = $isWidgetized;
+ $view->sitesData = array_values($digestableData);
+ $view->evolutionBy = $this->evolutionBy;
+ $view->period = $period;
+ $view->page = $this->page;
+ $view->limit = $this->limit;
+ $view->orderBy = $this->orderBy;
+ $view->order = $this->order;
+ $view->totalVisits = $dataTable->getMetadata('total_nb_visits');
+ $view->totalRevenue = $dataTable->getMetadata('total_revenue');
+
+ $view->displayRevenueColumn = $displayRevenueColumn;
+ $view->totalPageviews = $dataTable->getMetadata('total_nb_pageviews');
+ $view->pastTotalVisits = $dataTable->getMetadata('last_period_total_nb_visits');
+ $view->totalVisitsEvolution = $dataTable->getMetadata('total_visits_evolution');
+ if ($view->totalVisitsEvolution > 0) {
+ $view->totalVisitsEvolution = '+' . $view->totalVisitsEvolution;
+ }
+
+ if ($period != 'range') {
+ $lastPeriod = Piwik_Period::factory($period, $dataTable->getMetadata('last_period_date'));
+ $view->pastPeriodPretty = self::getCalendarPrettyDate($lastPeriod);
+ }
+
+ $params = $this->getGraphParamsModified();
+ $view->dateSparkline = $period == 'range' ? $date : $params['date'];
+
+ $view->autoRefreshTodayReport = false;
+ // if the current date is today, or yesterday,
+ // in case the website is set to UTC-12), or today in UTC+14, we refresh the page every 5min
+ if (in_array($date, array('today', date('Y-m-d'),
+ 'yesterday', Piwik_Date::factory('yesterday')->toString('Y-m-d'),
+ Piwik_Date::factory('now', 'UTC+14')->toString('Y-m-d')))
+ ) {
+
+ $view->autoRefreshTodayReport = Piwik_Config::getInstance()->General['multisites_refresh_after_seconds'];
+ }
+ $this->setGeneralVariablesView($view);
+ $this->setMinDateView($minDate, $view);
+ $this->setMaxDateView($maxDate, $view);
+ $view->show_sparklines = Piwik_Config::getInstance()->General['show_multisites_sparklines'];
+
+ echo $view->render();
+ }
+
+ /**
+ * The Multisites reports displays the first calendar date as the earliest day available for all websites.
+ * Also, today is the later "today" available across all timezones.
+ * @param array $siteIds Array of IDs for each site being displayed.
+ * @return array of two Piwik_Date instances. First is the min-date & the second
+ * is the max date.
+ */
+ private function getMinMaxDateAcrossWebsites($siteIds)
+ {
+ $now = Piwik_Date::now();
+
+ $minDate = null;
+ $maxDate = $now->subDay(1)->getTimestamp();
+ foreach ($siteIds as $idsite) {
+ // look for 'now' in the website's timezone
+ $timezone = Piwik_Site::getTimezoneFor($idsite);
+ $date = Piwik_Date::adjustForTimezone($now->getTimestamp(), $timezone);
+ if ($date > $maxDate) {
+ $maxDate = $date;
+ }
+
+ // look for the absolute minimum date
+ $creationDate = Piwik_Site::getCreationDateFor($idsite);
+ $date = Piwik_Date::adjustForTimezone(strtotime($creationDate), $timezone);
+ if (is_null($minDate) || $date < $minDate) {
+ $minDate = $date;
+ }
+ }
+
+ return array(Piwik_Date::factory($minDate), Piwik_Date::factory($maxDate));
+ }
+
+ protected function applyPrettyMoney(&$sites)
+ {
+ foreach ($sites as $idsite => &$site) {
+ $revenue = "-";
+ if (!empty($site['revenue'])) {
+ $revenue = Piwik::getPrettyMoney($site['revenue'], $site['idsite'], $htmlAllowed = false);
+ }
+ $site['revenue'] = '"' . $revenue . '"';
+ }
+ }
+
+ public function getEvolutionGraph($fetch = false, $columns = false)
+ {
+ if (empty($columns)) {
+ $columns = Piwik_Common::getRequestVar('columns');
+ }
+ $api = "API.get";
+
+ if ($columns == 'revenue') {
+ $api = "Goals.get";
+ }
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, $api);
+ $view->setColumnsToDisplay($columns);
+ return $this->renderView($view, $fetch);
+ }
}
diff --git a/plugins/MultiSites/MultiSites.php b/plugins/MultiSites/MultiSites.php
index 967300f9a4..43797132c7 100644
--- a/plugins/MultiSites/MultiSites.php
+++ b/plugins/MultiSites/MultiSites.php
@@ -15,91 +15,90 @@
*/
class Piwik_MultiSites extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('MultiSites_PluginDescription'),
- 'author' => 'ClearCode.cc',
- 'author_homepage' => "http://clearcode.cc/",
- 'version' => Piwik_Version::VERSION,
- );
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('MultiSites_PluginDescription'),
+ 'author' => 'ClearCode.cc',
+ 'author_homepage' => "http://clearcode.cc/",
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
- public function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'TopMenu.add' => 'addTopMenu',
- 'API.getReportMetadata' => 'getReportMetadata',
- );
- }
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'TopMenu.add' => 'addTopMenu',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ );
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $metadataMetrics = array();
- foreach(Piwik_MultiSites_API::getApiMetrics($enhanced = true) as $metricName => $metricSettings)
- {
- $metadataMetrics[$metricName] =
- Piwik_Translate($metricSettings[Piwik_MultiSites_API::METRIC_TRANSLATION_KEY]);
- $metadataMetrics[$metricSettings[Piwik_MultiSites_API::METRIC_EVOLUTION_COL_NAME_KEY]] =
- Piwik_Translate($metricSettings[Piwik_MultiSites_API::METRIC_TRANSLATION_KEY]) . " " . Piwik_Translate('MultiSites_Evolution');
- }
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $metadataMetrics = array();
+ foreach (Piwik_MultiSites_API::getApiMetrics($enhanced = true) as $metricName => $metricSettings) {
+ $metadataMetrics[$metricName] =
+ Piwik_Translate($metricSettings[Piwik_MultiSites_API::METRIC_TRANSLATION_KEY]);
+ $metadataMetrics[$metricSettings[Piwik_MultiSites_API::METRIC_EVOLUTION_COL_NAME_KEY]] =
+ Piwik_Translate($metricSettings[Piwik_MultiSites_API::METRIC_TRANSLATION_KEY]) . " " . Piwik_Translate('MultiSites_Evolution');
+ }
- $reports = &$notification->getNotificationObject();
+ $reports = & $notification->getNotificationObject();
- $reports[] = array(
- 'category' => Piwik_Translate('General_MultiSitesSummary'),
- 'name' => Piwik_Translate('General_AllWebsitesDashboard'),
- 'module' => 'MultiSites',
- 'action' => 'getAll',
- 'dimension' => Piwik_Translate('General_Website'), // re-using translation
- 'metrics' => $metadataMetrics,
- 'processedMetrics' => false,
- 'constantRowsCount' => false,
- 'order' => 5
- );
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_MultiSitesSummary'),
+ 'name' => Piwik_Translate('General_AllWebsitesDashboard'),
+ 'module' => 'MultiSites',
+ 'action' => 'getAll',
+ 'dimension' => Piwik_Translate('General_Website'), // re-using translation
+ 'metrics' => $metadataMetrics,
+ 'processedMetrics' => false,
+ 'constantRowsCount' => false,
+ 'order' => 5
+ );
- $reports[] = array(
- 'category' => Piwik_Translate('General_MultiSitesSummary'),
- 'name' => Piwik_Translate('General_SingleWebsitesDashboard'),
- 'module' => 'MultiSites',
- 'action' => 'getOne',
- 'dimension' => Piwik_Translate('General_Website'), // re-using translation
- 'metrics' => $metadataMetrics,
- 'processedMetrics' => false,
- 'constantRowsCount' => false,
- 'order' => 5
- );
- }
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_MultiSitesSummary'),
+ 'name' => Piwik_Translate('General_SingleWebsitesDashboard'),
+ 'module' => 'MultiSites',
+ 'action' => 'getOne',
+ 'dimension' => Piwik_Translate('General_Website'), // re-using translation
+ 'metrics' => $metadataMetrics,
+ 'processedMetrics' => false,
+ 'constantRowsCount' => false,
+ 'order' => 5
+ );
+ }
- public function addTopMenu()
- {
- $urlParams = array('module' => 'MultiSites', 'action' => 'index');
- $tooltip = Piwik_Translate('MultiSites_TopLinkTooltip');
- Piwik_AddTopMenu('General_MultiSitesSummary', $urlParams, true, 3, $isHTML = false, $tooltip);
- }
+ public function addTopMenu()
+ {
+ $urlParams = array('module' => 'MultiSites', 'action' => 'index');
+ $tooltip = Piwik_Translate('MultiSites_TopLinkTooltip');
+ Piwik_AddTopMenu('General_MultiSitesSummary', $urlParams, true, 3, $isHTML = false, $tooltip);
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
-
- $jsFiles[] = "plugins/MultiSites/templates/common.js";
- }
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "plugins/MultiSites/templates/styles.css";
- }
+ $jsFiles[] = "plugins/MultiSites/templates/common.js";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+
+ $cssFiles[] = "plugins/MultiSites/templates/styles.css";
+ }
}
diff --git a/plugins/MultiSites/templates/common.js b/plugins/MultiSites/templates/common.js
index 4ef9c82002..29439401d0 100644
--- a/plugins/MultiSites/templates/common.js
+++ b/plugins/MultiSites/templates/common.js
@@ -5,248 +5,217 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function setRowData (idsite, visits, pageviews, revenue, name, url, visitsSummaryValue, pageviewsSummaryValue, revenueSummaryValue)
-{
- this.idsite = idsite;
- this.visits = visits;
- this.revenue = revenue;
- this.name = name;
- this.url = url;
- this.pageviews = pageviews;
- this.visitsSummaryValue = parseFloat(visitsSummaryValue);
- this.pageviewsSummaryValue = parseFloat(pageviewsSummaryValue);
- this.revenueSummaryValue = parseFloat(revenueSummaryValue) || 0;
+function setRowData(idsite, visits, pageviews, revenue, name, url, visitsSummaryValue, pageviewsSummaryValue, revenueSummaryValue) {
+ this.idsite = idsite;
+ this.visits = visits;
+ this.revenue = revenue;
+ this.name = name;
+ this.url = url;
+ this.pageviews = pageviews;
+ this.visitsSummaryValue = parseFloat(visitsSummaryValue);
+ this.pageviewsSummaryValue = parseFloat(pageviewsSummaryValue);
+ this.revenueSummaryValue = parseFloat(revenueSummaryValue) || 0;
}
-function setOrderBy(self, allSites, params, mOrderBy)
-{
- if(params['mOrderBy'] == mOrderBy) {
- if(params['order'] == 'desc') {
- params['order'] = 'asc';
- } else {
- params['order'] = 'desc';
- }
- }
- params['mOrderBy'] = mOrderBy;
- prepareRows(allSites, params);
-
- $('.arrow').removeClass('multisites_desc multisites_asc');
- if($(self).attr('class') == 'evolution')
- {
- mOrderBy = 'evolution';
- }
- $('#' + mOrderBy + ' .arrow').addClass('multisites_' + params['order']);
-
- return params;
+function setOrderBy(self, allSites, params, mOrderBy) {
+ if (params['mOrderBy'] == mOrderBy) {
+ if (params['order'] == 'desc') {
+ params['order'] = 'asc';
+ } else {
+ params['order'] = 'desc';
+ }
+ }
+ params['mOrderBy'] = mOrderBy;
+ prepareRows(allSites, params);
+
+ $('.arrow').removeClass('multisites_desc multisites_asc');
+ if ($(self).attr('class') == 'evolution') {
+ mOrderBy = 'evolution';
+ }
+ $('#' + mOrderBy + ' .arrow').addClass('multisites_' + params['order']);
+
+ return params;
}
-function prepareRows(allUnsortedSites, params)
-{
- var allSites;
- $("#tb").find("tr").remove();
- $("#next").html('');
- $("#prev").html('');
- var mOrderBy = params['mOrderBy'];
-
- allSites = orderBy(allUnsortedSites, params);
-
- if(allSites.length > params['limit'])
- {
- allSites = limitBy(allSites, params);
- }
-
- displayRows(allSites, params);
-
- showPagination(allUnsortedSites, params);
- params['sitesVisible'] = allSites;
+function prepareRows(allUnsortedSites, params) {
+ var allSites;
+ $("#tb").find("tr").remove();
+ $("#next").html('');
+ $("#prev").html('');
+ var mOrderBy = params['mOrderBy'];
+
+ allSites = orderBy(allUnsortedSites, params);
+
+ if (allSites.length > params['limit']) {
+ allSites = limitBy(allSites, params);
+ }
+
+ displayRows(allSites, params);
+
+ showPagination(allUnsortedSites, params);
+ params['sitesVisible'] = allSites;
}
-function orderBy(allSites, params)
-{
- if(params['mOrderBy'] == 'names')
- {
- allSites.sort(function(a,b) {
- if (a['name'].toLowerCase() == b['name'].toLowerCase())
- {
- return 0;
- }
- return (a['name'].toLowerCase() < b['name'].toLowerCase()) ? -1 : 1;
- });
- }
- else if(params['mOrderBy'] == 'visits')
- {
- allSites.sort(function(a,b) {
- if (a['visits'] == b['visits']) {
- return 0;
- }
- return (a['visits'] < b['visits']) ? -1 : 1;
- });
- }
- else if(params['mOrderBy'] == 'pageviews')
- {
- allSites.sort(function (a,b) {
- if (a['pageviews'] == b['pageviews']) {
- return 0;
- }
- return (a['pageviews'] < b['pageviews']) ? -1 : 1;
- });
- }
- else if(params['mOrderBy'] == 'revenue')
- {
- allSites.sort(function (a,b) {
- var lhs = parseFloat(a['revenue'].replace(/[^0-9\.]+/g,"")) || 0,
- rhs = parseFloat(b['revenue'].replace(/[^0-9\.]+/g,"")) || 0;
-
- return lhs === rhs ? 0 : ((lhs < rhs) ? -1 : 1);
- });
- }
- else if(params['mOrderBy'] == 'revenueSummary')
- {
- allSites.sort(function (a,b) {
- if (a['revenueSummaryValue'] == b['revenueSummaryValue']) {
- return 0;
- }
- return (a['revenueSummaryValue'] - b['revenueSummaryValue'] <= 0.01) ? -1 : 1;
- });
- }
- else if(params['mOrderBy'] == 'pageviewsSummary')
- {
- allSites.sort(function (a,b) {
- if (a['pageviewsSummaryValue'] == b['pageviewsSummaryValue']) {
- return 0;
- }
- return (a['pageviewsSummaryValue'] - b['pageviewsSummaryValue'] <= 0.01) ? -1 : 1;
- });
- }
- else if(params['mOrderBy'] == 'visitsSummary')
- {
- allSites.sort(function (a,b) {
- if (a['visitsSummaryValue'] == b['visitsSummaryValue']) {
- return 0;
- }
- return (a['visitsSummaryValue'] - b['visitsSummaryValue'] <= 0.01) ? -1 : 1;
- });
- }
-
- if(params['order'] == 'desc')
- {
- allSites.reverse();
- }
- return allSites;
+function orderBy(allSites, params) {
+ if (params['mOrderBy'] == 'names') {
+ allSites.sort(function (a, b) {
+ if (a['name'].toLowerCase() == b['name'].toLowerCase()) {
+ return 0;
+ }
+ return (a['name'].toLowerCase() < b['name'].toLowerCase()) ? -1 : 1;
+ });
+ }
+ else if (params['mOrderBy'] == 'visits') {
+ allSites.sort(function (a, b) {
+ if (a['visits'] == b['visits']) {
+ return 0;
+ }
+ return (a['visits'] < b['visits']) ? -1 : 1;
+ });
+ }
+ else if (params['mOrderBy'] == 'pageviews') {
+ allSites.sort(function (a, b) {
+ if (a['pageviews'] == b['pageviews']) {
+ return 0;
+ }
+ return (a['pageviews'] < b['pageviews']) ? -1 : 1;
+ });
+ }
+ else if (params['mOrderBy'] == 'revenue') {
+ allSites.sort(function (a, b) {
+ var lhs = parseFloat(a['revenue'].replace(/[^0-9\.]+/g, "")) || 0,
+ rhs = parseFloat(b['revenue'].replace(/[^0-9\.]+/g, "")) || 0;
+
+ return lhs === rhs ? 0 : ((lhs < rhs) ? -1 : 1);
+ });
+ }
+ else if (params['mOrderBy'] == 'revenueSummary') {
+ allSites.sort(function (a, b) {
+ if (a['revenueSummaryValue'] == b['revenueSummaryValue']) {
+ return 0;
+ }
+ return (a['revenueSummaryValue'] - b['revenueSummaryValue'] <= 0.01) ? -1 : 1;
+ });
+ }
+ else if (params['mOrderBy'] == 'pageviewsSummary') {
+ allSites.sort(function (a, b) {
+ if (a['pageviewsSummaryValue'] == b['pageviewsSummaryValue']) {
+ return 0;
+ }
+ return (a['pageviewsSummaryValue'] - b['pageviewsSummaryValue'] <= 0.01) ? -1 : 1;
+ });
+ }
+ else if (params['mOrderBy'] == 'visitsSummary') {
+ allSites.sort(function (a, b) {
+ if (a['visitsSummaryValue'] == b['visitsSummaryValue']) {
+ return 0;
+ }
+ return (a['visitsSummaryValue'] - b['visitsSummaryValue'] <= 0.01) ? -1 : 1;
+ });
+ }
+
+ if (params['order'] == 'desc') {
+ allSites.reverse();
+ }
+ return allSites;
}
-function limitBy(allSites, params)
-{
- var begin = (params['page'] - 1) * params['limit'];
- var end = (params['page'] * params['limit']);
- return allSites.slice(begin, end);
+function limitBy(allSites, params) {
+ var begin = (params['page'] - 1) * params['limit'];
+ var end = (params['page'] * params['limit']);
+ return allSites.slice(begin, end);
}
-function switchEvolution(params)
-{
- $('.pageviews').hide();
- $('.revenue').hide();
- $('.visits').hide();
- $('.' + params['evolutionBy']).show();
- sitesVisible = params['sitesVisible'];
- for(i = 0; i < allSites.length; i++)
- {
- $('#sparkline_' + allSites[i].idsite).html(getSparklineImg(allSites[i].idsite, params['evolutionBy'], params));
- }
+function switchEvolution(params) {
+ $('.pageviews').hide();
+ $('.revenue').hide();
+ $('.visits').hide();
+ $('.' + params['evolutionBy']).show();
+ sitesVisible = params['sitesVisible'];
+ for (i = 0; i < allSites.length; i++) {
+ $('#sparkline_' + allSites[i].idsite).html(getSparklineImg(allSites[i].idsite, params['evolutionBy'], params));
+ }
}
-function displayRows(allSites, params)
-{
- for(var i = 0; i < allSites.length; i++)
- {
- var str = params['row'];
- str = str.replace(/%revenueSummary%/g, getImageForSummary(allSites[i].revenueSummaryValue));
- str = str.replace(/%pageviewsSummary%/g, getImageForSummary(allSites[i].pageviewsSummaryValue));
- str = str.replace(/%visitsSummary%/g, getImageForSummary(allSites[i].visitsSummaryValue));
- str = str.replace(/%sparkline%/g, getSparklineImg(allSites[i].idsite, params['evolutionBy'], params));
- str = str.replace(/%pageviews%/g, allSites[i].pageviews);
- str = str.replace(/%idsite%/g, allSites[i].idsite);
- str = str.replace(/%visits%/g, allSites[i].visits);
- str = str.replace(/%name%/g, allSites[i].name);
- str = str.replace(/%revenue%/g, allSites[i].revenue);
- str = str.replace(/%main_url%/g, allSites[i].url);
- str = str.replace(/%date%/g, params['date'] || params['dateSparkline']); // For period=range, dateSparkline only is set
- str = str.replace(/%period%/g, params['period']);
-
- $('#tb').append('<tr class="tables_row" id="row_'+ allSites[i].idsite+'">' + str + '</tr>');
- }
-
- $(".table_row").show();
- $('.pageviews').hide();
- $('.revenue').hide();
- $('.visits').hide();
- $('#main_indicator').hide();
- $('.' + params['evolutionBy']).show();
- $("#main_indicator").hide();
+function displayRows(allSites, params) {
+ for (var i = 0; i < allSites.length; i++) {
+ var str = params['row'];
+ str = str.replace(/%revenueSummary%/g, getImageForSummary(allSites[i].revenueSummaryValue));
+ str = str.replace(/%pageviewsSummary%/g, getImageForSummary(allSites[i].pageviewsSummaryValue));
+ str = str.replace(/%visitsSummary%/g, getImageForSummary(allSites[i].visitsSummaryValue));
+ str = str.replace(/%sparkline%/g, getSparklineImg(allSites[i].idsite, params['evolutionBy'], params));
+ str = str.replace(/%pageviews%/g, allSites[i].pageviews);
+ str = str.replace(/%idsite%/g, allSites[i].idsite);
+ str = str.replace(/%visits%/g, allSites[i].visits);
+ str = str.replace(/%name%/g, allSites[i].name);
+ str = str.replace(/%revenue%/g, allSites[i].revenue);
+ str = str.replace(/%main_url%/g, allSites[i].url);
+ str = str.replace(/%date%/g, params['date'] || params['dateSparkline']); // For period=range, dateSparkline only is set
+ str = str.replace(/%period%/g, params['period']);
+
+ $('#tb').append('<tr class="tables_row" id="row_' + allSites[i].idsite + '">' + str + '</tr>');
+ }
+
+ $(".table_row").show();
+ $('.pageviews').hide();
+ $('.revenue').hide();
+ $('.visits').hide();
+ $('#main_indicator').hide();
+ $('.' + params['evolutionBy']).show();
+ $("#main_indicator").hide();
}
-function getSparklineImg(id, column, params)
-{
- if(column != 'revenue') {
- column = 'nb_' + column;
- }
- var append = '';
- var token_auth = broadcast.getValueFromUrl('token_auth');
- if(token_auth.length) {
- append = '&token_auth=' + token_auth;
- }
- return '<img class="sparkline" alt="" src="?module=MultiSites&action=getEvolutionGraph&period=' + params['period'] + '&date=' + params['dateSparkline'] + '&evolutionBy=' + params['evolutionBy'] + '&columns=' + column + '&idSite=' + id + '&idsite=' + id + '&viewDataTable=sparkline'+ append +'" width="100" height="25" />';
+function getSparklineImg(id, column, params) {
+ if (column != 'revenue') {
+ column = 'nb_' + column;
+ }
+ var append = '';
+ var token_auth = broadcast.getValueFromUrl('token_auth');
+ if (token_auth.length) {
+ append = '&token_auth=' + token_auth;
+ }
+ return '<img class="sparkline" alt="" src="?module=MultiSites&action=getEvolutionGraph&period=' + params['period'] + '&date=' + params['dateSparkline'] + '&evolutionBy=' + params['evolutionBy'] + '&columns=' + column + '&idSite=' + id + '&idsite=' + id + '&viewDataTable=sparkline' + append + '" width="100" height="25" />';
}
-function showPagination(allSites, params)
-{
- if ((params['page'] * params['limit']) < allSites.length)
- {
- var html = '<span style="cursor:pointer;" class="pointer" onClick="changePage(allSites, params, \'next\');">' + params['next'] + ' &#187;</span>';
- $("#next").html(html);
- }
- if(params['page'] > 1)
- {
- html = '<span style="cursor:pointer;" onClick="changePage(allSites, params, \'prev\');">&#171; ' + params['prev'] + '</span>'
- $("#prev").html(html);
- }
- var start = (params['page'] - 1) * params['limit'] + 1;
- var count = allSites.length;
- var end = parseInt(start) + parseInt(params['limit']) - 1;
- if(end > count) end = count;
- html = '<span>' + (start ) + ' - ' + end + ' of ' + count + '</span>';
- $("#counter").html(html);
+function showPagination(allSites, params) {
+ if ((params['page'] * params['limit']) < allSites.length) {
+ var html = '<span style="cursor:pointer;" class="pointer" onClick="changePage(allSites, params, \'next\');">' + params['next'] + ' &#187;</span>';
+ $("#next").html(html);
+ }
+ if (params['page'] > 1) {
+ html = '<span style="cursor:pointer;" onClick="changePage(allSites, params, \'prev\');">&#171; ' + params['prev'] + '</span>'
+ $("#prev").html(html);
+ }
+ var start = (params['page'] - 1) * params['limit'] + 1;
+ var count = allSites.length;
+ var end = parseInt(start) + parseInt(params['limit']) - 1;
+ if (end > count) end = count;
+ html = '<span>' + (start ) + ' - ' + end + ' of ' + count + '</span>';
+ $("#counter").html(html);
}
-function changePage(allSites, params, kind)
-{
- if(kind == 'next')
- {
- params['page']++;
- }
- else
- {
- params['page']--;
- }
- prepareRows(allSites, params);
- return params;
+function changePage(allSites, params, kind) {
+ if (kind == 'next') {
+ params['page']++;
+ }
+ else {
+ params['page']--;
+ }
+ prepareRows(allSites, params);
+ return params;
}
-function getImageForSummary(value)
-{
- if(value > 0)
- {
- return '<img src="plugins/MultiSites/images/arrow_up.png" alt="" /> <b style="color: green;">' + value + '&nbsp;%</b>';
- }
- else if(value == 0)
- {
- return '<img src="plugins/MultiSites/images/stop.png" alt="" /> <b>' + value + '%</b>';
- }
- else
- {
- return '<img src="plugins/MultiSites/images/arrow_down.png" alt="" /> <b style="color: red;">' + value +'&nbsp;%</b>';
- }
+function getImageForSummary(value) {
+ if (value > 0) {
+ return '<img src="plugins/MultiSites/images/arrow_up.png" alt="" /> <b style="color: green;">' + value + '&nbsp;%</b>';
+ }
+ else if (value == 0) {
+ return '<img src="plugins/MultiSites/images/stop.png" alt="" /> <b>' + value + '%</b>';
+ }
+ else {
+ return '<img src="plugins/MultiSites/images/arrow_down.png" alt="" /> <b style="color: red;">' + value + '&nbsp;%</b>';
+ }
}
diff --git a/plugins/MultiSites/templates/index.tpl b/plugins/MultiSites/templates/index.tpl
index 8bb928fabb..f130594d6e 100644
--- a/plugins/MultiSites/templates/index.tpl
+++ b/plugins/MultiSites/templates/index.tpl
@@ -1,114 +1,121 @@
{assign var=showSitesSelection value=false}
{if !$isWidgetized}
-{include file="CoreHome/templates/header.tpl"}
+ {include file="CoreHome/templates/header.tpl"}
{/if}
<div id="multisites">
-<div id="main">
-{include file="MultiSites/templates/row.tpl" assign="row"}
-<script type="text/javascript">
- var allSites = new Array();
- var params = new Array();
- {foreach from=$sitesData key=i item=site}
- allSites[{$i}] = new setRowData({$site.idsite}, {$site.visits}, {$site.pageviews}, {if empty($site.revenue)}0{else}{$site.revenue}{/if}, '{$site.name|escape:"javascript"}', '{$site.main_url|escape:"javascript"}', '{if isset($site.visits_evolution)}{$site.visits_evolution|replace:",":"."}{/if}', '{if isset($site.pageviews_evolution)}{$site.pageviews_evolution|replace:",":"."}{/if}', '{if isset($site.revenue_evolution)}{$site.revenue_evolution|replace:",":"."}{/if}');
- {/foreach}
- params['period'] = '{$period}';
- params['date'] = '{$date}';
- params['evolutionBy'] = '{$evolutionBy}';
- params['mOrderBy'] = '{$orderBy}';
- params['order'] = '{$order}';
- params['limit'] = '{$limit}';
- params['page'] = 1;
- params['prev'] = "{'General_Previous'|translate|escape:"javascript"}";
- params['next'] = "{'General_Next'|translate|escape:"javascript"}";
- params['row'] = '{$row|escape:"javascript"}';
- params['dateSparkline'] = '{$dateSparkline}';
-</script>
+ <div id="main">
+ {include file="MultiSites/templates/row.tpl" assign="row"}
+ <script type="text/javascript">
+ var allSites = new Array();
+ var params = new Array();
+ {foreach from=$sitesData key=i item=site}
+ allSites[{$i}] = new setRowData({$site.idsite}, {$site.visits}, {$site.pageviews}, {if empty($site.revenue)}0{else}{$site.revenue}{/if}, '{$site.name|escape:"javascript"}', '{$site.main_url|escape:"javascript"}', '{if isset($site.visits_evolution)}{$site.visits_evolution|replace:",":"."}{/if}', '{if isset($site.pageviews_evolution)}{$site.pageviews_evolution|replace:",":"."}{/if}', '{if isset($site.revenue_evolution)}{$site.revenue_evolution|replace:",":"."}{/if}');
+ {/foreach}
+ params['period'] = '{$period}';
+ params['date'] = '{$date}';
+ params['evolutionBy'] = '{$evolutionBy}';
+ params['mOrderBy'] = '{$orderBy}';
+ params['order'] = '{$order}';
+ params['limit'] = '{$limit}';
+ params['page'] = 1;
+ params['prev'] = "{'General_Previous'|translate|escape:"javascript"}";
+ params['next'] = "{'General_Next'|translate|escape:"javascript"}";
+ params['row'] = '{$row|escape:"javascript"}';
+ params['dateSparkline'] = '{$dateSparkline}';
+ </script>
-{postEvent name="template_headerMultiSites"}
+ {postEvent name="template_headerMultiSites"}
-{if !$isWidgetized}
-<div class="top_controls_inner">
- {include file="CoreHome/templates/period_select.tpl"}
- {include file="CoreHome/templates/header_message.tpl"}
-</div>
-{/if}
+ {if !$isWidgetized}
+ <div class="top_controls_inner">
+ {include file="CoreHome/templates/period_select.tpl"}
+ {include file="CoreHome/templates/header_message.tpl"}
+ </div>
+ {/if}
-<div class="centerLargeDiv">
+ <div class="centerLargeDiv">
-<h2>{'General_AllWebsitesDashboard'|translate}
- {capture assign=nVisits}{'General_NVisits'|translate:$totalVisits}{/capture}
- {capture assign=nVisitsLast}{'General_NVisits'|translate:$pastTotalVisits}{/capture}
- <span class='smallTitle' {if $totalVisitsEvolution}title="{'General_EvolutionSummaryGeneric'|translate:$nVisits:$prettyDate:$nVisitsLast:$pastPeriodPretty:$totalVisitsEvolution}"{/if}>
+ <h2>{'General_AllWebsitesDashboard'|translate}
+ {capture assign=nVisits}{'General_NVisits'|translate:$totalVisits}{/capture}
+ {capture assign=nVisitsLast}{'General_NVisits'|translate:$pastTotalVisits}{/capture}
+ <span class='smallTitle'
+ {if $totalVisitsEvolution}title="{'General_EvolutionSummaryGeneric'|translate:$nVisits:$prettyDate:$nVisitsLast:$pastPeriodPretty:$totalVisitsEvolution}"{/if}>
{'General_TotalVisitsPageviewsRevenue'|translate:"<strong>$totalVisits</strong>":"<strong>$totalPageviews</strong>":"<strong>$totalRevenue</strong>"}
</span>
-</h2>
+ </h2>
-<table id="mt" class="dataTable" cellspacing="0">
- <thead>
- <tr>
- <th id="names" class="label" onClick="params = setOrderBy(this,allSites, params, 'names');">
- <span>{'General_Website'|translate}</span>
- <span class="arrow {if $evolutionBy=='names'}multisites_{$order}{/if}"></span>
- </th>
- <th id="visits" class="multisites-column" style="width: 100px" onClick="params = setOrderBy(this,allSites, params, 'visits');">
- <span>{'General_ColumnNbVisits'|translate}</span>
- <span class="arrow {if $evolutionBy=='visits'}multisites_{$order}{/if}"></span>
- </th>
- <th id="pageviews" class="multisites-column" style="width: 110px" onClick="params = setOrderBy(this,allSites, params, 'pageviews');">
- <span>{'General_ColumnPageviews'|translate}</span>
- <span class="arrow {if $evolutionBy=='pageviews'}multisites_{$order}{/if}"></span>
- </th>
- {if $displayRevenueColumn}
- <th id="revenue" class="multisites-column" style="width: 110px" onClick="params = setOrderBy(this,allSites, params, 'revenue');">
- <span>{'Goals_ColumnRevenue'|translate}</span>
- <span class="arrow {if $evolutionBy=='revenue'}multisites_{$order}{/if}"></span>
- </th>
- {/if}
- <th id="evolution" style=" width:350px" colspan="{if $show_sparklines}2{else}1{/if}">
- <span class="arrow "></span>
- <span class="evolution" style="cursor:pointer;" onClick="params = setOrderBy(this,allSites, params, $('#evolution_selector').val() + 'Summary');"> {'MultiSites_Evolution'|translate}</span>
- <select class="selector" id="evolution_selector" onchange="params['evolutionBy'] = $('#evolution_selector').val(); switchEvolution(params);">
- <option value="visits" {if $evolutionBy eq 'visits'} selected {/if}>{'General_ColumnNbVisits'|translate}</option>
- <option value="pageviews" {if $evolutionBy eq 'pageviews'} selected {/if}>{'General_ColumnPageviews'|translate}</option>
- {if $displayRevenueColumn}<option value="revenue" {if $evolutionBy eq 'revenue'} selected {/if}>{'Goals_ColumnRevenue'|translate}</option>{/if}
- </select>
- </th>
- </tr>
- </thead>
+ <table id="mt" class="dataTable" cellspacing="0">
+ <thead>
+ <tr>
+ <th id="names" class="label" onClick="params = setOrderBy(this,allSites, params, 'names');">
+ <span>{'General_Website'|translate}</span>
+ <span class="arrow {if $evolutionBy=='names'}multisites_{$order}{/if}"></span>
+ </th>
+ <th id="visits" class="multisites-column" style="width: 100px" onClick="params = setOrderBy(this,allSites, params, 'visits');">
+ <span>{'General_ColumnNbVisits'|translate}</span>
+ <span class="arrow {if $evolutionBy=='visits'}multisites_{$order}{/if}"></span>
+ </th>
+ <th id="pageviews" class="multisites-column" style="width: 110px" onClick="params = setOrderBy(this,allSites, params, 'pageviews');">
+ <span>{'General_ColumnPageviews'|translate}</span>
+ <span class="arrow {if $evolutionBy=='pageviews'}multisites_{$order}{/if}"></span>
+ </th>
+ {if $displayRevenueColumn}
+ <th id="revenue" class="multisites-column" style="width: 110px" onClick="params = setOrderBy(this,allSites, params, 'revenue');">
+ <span>{'Goals_ColumnRevenue'|translate}</span>
+ <span class="arrow {if $evolutionBy=='revenue'}multisites_{$order}{/if}"></span>
+ </th>
+ {/if}
+ <th id="evolution" style=" width:350px" colspan="{if $show_sparklines}2{else}1{/if}">
+ <span class="arrow "></span>
+ <span class="evolution" style="cursor:pointer;"
+ onClick="params = setOrderBy(this,allSites, params, $('#evolution_selector').val() + 'Summary');"> {'MultiSites_Evolution'|translate}</span>
+ <select class="selector" id="evolution_selector"
+ onchange="params['evolutionBy'] = $('#evolution_selector').val(); switchEvolution(params);">
+ <option value="visits" {if $evolutionBy eq 'visits'} selected {/if}>{'General_ColumnNbVisits'|translate}</option>
+ <option value="pageviews" {if $evolutionBy eq 'pageviews'} selected {/if}>{'General_ColumnPageviews'|translate}</option>
+ {if $displayRevenueColumn}
+ <option value="revenue" {if $evolutionBy eq 'revenue'} selected {/if}>{'Goals_ColumnRevenue'|translate}</option>{/if}
+ </select>
+ </th>
+ </tr>
+ </thead>
- <tbody id="tb">
- </tbody>
+ <tbody id="tb">
+ </tbody>
- <tfoot>
- {if $isSuperUser}
- <tr>
- <td colspan="8" class="clean" style="text-align: right; padding-top: 15px;padding-right:10px">
- <a href="{url}&module=SitesManager&action=index&showaddsite=1"><img src='plugins/UsersManager/images/add.png' alt="" style="margin: 0;" /> {'SitesManager_AddSite'|translate}</a>
- </td>
- </tr>
- {/if}
- <tr row_id="last" >
- <td colspan="8" class="clean" style="padding: 20px">
- <span id="prev" class="pager" style="padding-right: 20px;"></span>
+ <tfoot>
+ {if $isSuperUser}
+ <tr>
+ <td colspan="8" class="clean" style="text-align: right; padding-top: 15px;padding-right:10px">
+ <a href="{url}&module=SitesManager&action=index&showaddsite=1"><img src='plugins/UsersManager/images/add.png' alt=""
+ style="margin: 0;"/> {'SitesManager_AddSite'|translate}</a>
+ </td>
+ </tr>
+ {/if}
+ <tr row_id="last">
+ <td colspan="8" class="clean" style="padding: 20px">
+ <span id="prev" class="pager" style="padding-right: 20px;"></span>
<span class="dataTablePages">
<span id="counter">
</span>
</span>
- <span id="next" class="clean" style="padding-left: 20px;"></span>
- </td>
- </tr>
- </tfoot>
-</table>
-</div>
-<script type="text/javascript">
-prepareRows(allSites, params, '{$orderBy}');
+ <span id="next" class="clean" style="padding-left: 20px;"></span>
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+ </div>
+ <script type="text/javascript">
+ prepareRows(allSites, params, '{$orderBy}');
-{if $autoRefreshTodayReport}
-piwikHelper.refreshAfter({$autoRefreshTodayReport} *1000);
-{/if}
-</script>
-</div>
+ {if $autoRefreshTodayReport}
+ piwikHelper.refreshAfter({$autoRefreshTodayReport} * 1000
+ )
+ ;
+ {/if}
+ </script>
+ </div>
</div>
{include file="CoreHome/templates/footer.tpl"}
diff --git a/plugins/MultiSites/templates/row.tpl b/plugins/MultiSites/templates/row.tpl
index f651af317e..b49457a5f7 100644
--- a/plugins/MultiSites/templates/row.tpl
+++ b/plugins/MultiSites/templates/row.tpl
@@ -1,8 +1,8 @@
-<td class="multisites-label label" >
+<td class="multisites-label label">
<a title="View reports" href="index.php?module=CoreHome&action=index&date=%date%&period=%period%&idSite=%idsite%">%name%</a>
<span style="width: 10px; margin-left:3px">
- <a target="_blank" title="{'General_GoTo'|translate:"%main_url%"}" href="%main_url%"><img src="plugins/MultiSites/images/link.gif" /></a>
+ <a target="_blank" title="{'General_GoTo'|translate:"%main_url%"}" href="%main_url%"><img src="plugins/MultiSites/images/link.gif"/></a>
</span>
</td>
<td class="multisites-column">
@@ -12,22 +12,23 @@
%pageviews%
</td>
{if $displayRevenueColumn}
-<td class="multisites-column">
- %revenue%
-</td>
+ <td class="multisites-column">
+ %revenue%
+ </td>
{/if}
{if $period!='range'}
- <td style="width:170px">
- <div class="visits" style="display:none">%visitsSummary%</div>
- <div class="pageviews"style="display:none">%pageviewsSummary%</div>
- {if $displayRevenueColumn}
- <div class="revenue"style="display:none">%revenueSummary%</div>
- {/if}
-{/if}
-{if $show_sparklines}
-<td style="width:180px">
- <div id="sparkline_%idsite%" style="width: 100px; margin: auto">
- <a target="_blank" href="index.php?module=CoreHome&action=index&date=%date%&period=%period%&idSite=%idsite%" title="{capture assign=dashboardName}{'Dashboard_DashboardOf'|translate:'%name%'}{/capture} {'General_GoTo'|translate:$dashboardName}">%sparkline%</a>
- </div>
-</td>
+<td style="width:170px">
+ <div class="visits" style="display:none">%visitsSummary%</div>
+ <div class="pageviews" style="display:none">%pageviewsSummary%</div>
+ {if $displayRevenueColumn}
+ <div class="revenue" style="display:none">%revenueSummary%</div>
+ {/if}
+ {/if}
+ {if $show_sparklines}
+ <td style="width:180px">
+ <div id="sparkline_%idsite%" style="width: 100px; margin: auto">
+ <a target="_blank" href="index.php?module=CoreHome&action=index&date=%date%&period=%period%&idSite=%idsite%"
+ title="{capture assign=dashboardName}{'Dashboard_DashboardOf'|translate:'%name%'}{/capture} {'General_GoTo'|translate:$dashboardName}">%sparkline%</a>
+ </div>
+ </td>
{/if}
diff --git a/plugins/MultiSites/templates/styles.css b/plugins/MultiSites/templates/styles.css
index e217f0b468..796a2b091a 100644
--- a/plugins/MultiSites/templates/styles.css
+++ b/plugins/MultiSites/templates/styles.css
@@ -1,67 +1,77 @@
-#multisites {
- border:0;
- padding:0 15px;
- font-size:14px;
+#multisites {
+ border: 0;
+ padding: 0 15px;
+ font-size: 14px;
}
-#multisites .top_controls_inner {
- height:10px;
+#multisites .top_controls_inner {
+ height: 10px;
}
+
.smallTitle {
- font-size:15px;
+ font-size: 15px;
}
+
.indicator {
- background: url(../images/loading-blue.gif) no-repeat center;
- height: 20px;
- width: 60px;
- margin: auto;
- border: 0 !important;
+ background: url(../images/loading-blue.gif) no-repeat center;
+ height: 20px;
+ width: 60px;
+ margin: auto;
+ border: 0 !important;
}
+
.clean {
border: 0 !important;
}
+
#multisites th {
- cursor:pointer;
+ cursor: pointer;
}
+
#multisites td, #multisites tr, #multisites .sparkline {
text-align: center;
vertical-align: middle;
padding: 1px;
margin: 0;
}
+
#multisites td.multisites-label {
- padding-left: 15px ;
- text-align:left;
+ padding-left: 15px;
+ text-align: left;
width: 250px;
}
+
#multisites td.multisites-label a:hover {
text-decoration: underline;
}
-#multisites td.multisites-column, #multisites th.multisites-column {
- width:70px;
- white-space:nowrap;
+
+#multisites td.multisites-column, #multisites th.multisites-column {
+ width: 70px;
+ white-space: nowrap;
}
-#multisites td.multisites-column-evolution, #multisites th.multisites-column-evolution {
- width:70px;
+
+#multisites td.multisites-column-evolution, #multisites th.multisites-column-evolution {
+ width: 70px;
}
-.multisites_desc
-{
- width: 16px;
- height: 13px;
- display: inline-block;
- background-image: url(../../../themes/default/images/sortdesc.png);
+
+.multisites_desc {
+ width: 16px;
+ height: 13px;
+ display: inline-block;
+ background-image: url(../../../themes/default/images/sortdesc.png);
}
-.multisites_asc
-{
- width: 16px;
- height: 13px;
- display: inline-block;
- background-image: url(../../../themes/default/images/sortasc.png);
+
+.multisites_asc {
+ width: 16px;
+ height: 13px;
+ display: inline-block;
+ background-image: url(../../../themes/default/images/sortasc.png);
}
#mt thead {
- line-height:2.5em;
+ line-height: 2.5em;
}
+
#mt thead :first-child {
-moz-border-radius-topleft: 7px;
-webkit-border-top-left-radius: 7px;
diff --git a/plugins/Overlay/API.php b/plugins/Overlay/API.php
index 391b54082f..413406fe43 100644
--- a/plugins/Overlay/API.php
+++ b/plugins/Overlay/API.php
@@ -11,118 +11,111 @@
class Piwik_Overlay_API
{
-
- private static $instance = null;
-
- /**
- * Get Singleton instance
- * @return Piwik_Overlay_API
- */
- public static function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Get translation strings
- */
- public function getTranslations($idSite)
- {
- $this->authenticate($idSite);
-
- $translations = array(
- 'oneClick' => 'Overlay_OneClick',
- 'clicks' => 'Overlay_Clicks',
- 'clicksFromXLinks' => 'Overlay_ClicksFromXLinks',
- 'link' => 'Overlay_Link'
- );
-
- return array_map('Piwik_Translate', $translations);
- }
-
- /**
- * Get excluded query parameters for a site.
- * This information is used for client side url normalization.
- */
- public function getExcludedQueryParameters($idSite)
- {
- $this->authenticate($idSite);
-
- $sitesManager = Piwik_SitesManager_API::getInstance();
- $site = $sitesManager->getSiteFromId($idSite);
-
- try {
- return Piwik_SitesManager::getTrackerExcludedQueryParameters($site);
- } catch(Exception $e) {
- // an exception is thrown when the user has no view access.
- // do not throw the exception to the outside.
- return array();
- }
- }
-
- /**
- * Get following pages of a url.
- * This is done on the logs - not the archives!
- *
- * Note: if you use this method via the regular API, the number of results will be limited.
- * Make sure, you set filter_limit=-1 in the request.
- */
- public function getFollowingPages($url, $idSite, $period, $date, $segment = false)
- {
- $this->authenticate($idSite);
-
- $url = Piwik_Tracker_Action::excludeQueryParametersFromUrl($url, $idSite);
- // we don't unsanitize $url here. it will be done in the Transitions plugin.
-
- $resultDataTable = new Piwik_DataTable;
-
- try
- {
- $limitBeforeGrouping = Piwik_Config::getInstance()->General['overlay_following_pages_limit'];
- $transitionsReport = Piwik_Transitions_API::getInstance()->getTransitionsForAction(
- $url, $type = 'url', $idSite, $period, $date, $segment, $limitBeforeGrouping,
- $part = 'followingActions', $returnNormalizedUrls = true);
- }
- catch(Exception $e)
- {
- return $resultDataTable;
- }
-
- $reports = array('followingPages', 'outlinks', 'downloads');
- foreach ($reports as $reportName)
- {
- if (!isset($transitionsReport[$reportName]))
- {
- continue;
- }
- foreach ($transitionsReport[$reportName]->getRows() as $row)
- {
- // don't touch the row at all for performance reasons
- $resultDataTable->addRow($row);
- }
- }
-
- return $resultDataTable;
- }
-
- /** Do cookie authentication. This way, the token can remain secret. */
- private function authenticate($idSite)
- {
- $notification = null;
- Piwik_PostEvent('FrontController.initAuthenticationObject', $notification, $allowCookieAuthentication = true);
-
- $auth = Zend_Registry::get('auth');
- $success = Zend_Registry::get('access')->reloadAccess($auth);
-
- if (!$success) {
- throw new Exception('Authentication failed');
- }
-
- Piwik::checkUserHasViewAccess($idSite);
- }
+
+ private static $instance = null;
+
+ /**
+ * Get Singleton instance
+ * @return Piwik_Overlay_API
+ */
+ public static function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Get translation strings
+ */
+ public function getTranslations($idSite)
+ {
+ $this->authenticate($idSite);
+
+ $translations = array(
+ 'oneClick' => 'Overlay_OneClick',
+ 'clicks' => 'Overlay_Clicks',
+ 'clicksFromXLinks' => 'Overlay_ClicksFromXLinks',
+ 'link' => 'Overlay_Link'
+ );
+
+ return array_map('Piwik_Translate', $translations);
+ }
+
+ /**
+ * Get excluded query parameters for a site.
+ * This information is used for client side url normalization.
+ */
+ public function getExcludedQueryParameters($idSite)
+ {
+ $this->authenticate($idSite);
+
+ $sitesManager = Piwik_SitesManager_API::getInstance();
+ $site = $sitesManager->getSiteFromId($idSite);
+
+ try {
+ return Piwik_SitesManager::getTrackerExcludedQueryParameters($site);
+ } catch (Exception $e) {
+ // an exception is thrown when the user has no view access.
+ // do not throw the exception to the outside.
+ return array();
+ }
+ }
+
+ /**
+ * Get following pages of a url.
+ * This is done on the logs - not the archives!
+ *
+ * Note: if you use this method via the regular API, the number of results will be limited.
+ * Make sure, you set filter_limit=-1 in the request.
+ */
+ public function getFollowingPages($url, $idSite, $period, $date, $segment = false)
+ {
+ $this->authenticate($idSite);
+
+ $url = Piwik_Tracker_Action::excludeQueryParametersFromUrl($url, $idSite);
+ // we don't unsanitize $url here. it will be done in the Transitions plugin.
+
+ $resultDataTable = new Piwik_DataTable;
+
+ try {
+ $limitBeforeGrouping = Piwik_Config::getInstance()->General['overlay_following_pages_limit'];
+ $transitionsReport = Piwik_Transitions_API::getInstance()->getTransitionsForAction(
+ $url, $type = 'url', $idSite, $period, $date, $segment, $limitBeforeGrouping,
+ $part = 'followingActions', $returnNormalizedUrls = true);
+ } catch (Exception $e) {
+ return $resultDataTable;
+ }
+
+ $reports = array('followingPages', 'outlinks', 'downloads');
+ foreach ($reports as $reportName) {
+ if (!isset($transitionsReport[$reportName])) {
+ continue;
+ }
+ foreach ($transitionsReport[$reportName]->getRows() as $row) {
+ // don't touch the row at all for performance reasons
+ $resultDataTable->addRow($row);
+ }
+ }
+
+ return $resultDataTable;
+ }
+
+ /** Do cookie authentication. This way, the token can remain secret. */
+ private function authenticate($idSite)
+ {
+ $notification = null;
+ Piwik_PostEvent('FrontController.initAuthenticationObject', $notification, $allowCookieAuthentication = true);
+
+ $auth = Zend_Registry::get('auth');
+ $success = Zend_Registry::get('access')->reloadAccess($auth);
+
+ if (!$success) {
+ throw new Exception('Authentication failed');
+ }
+
+ Piwik::checkUserHasViewAccess($idSite);
+ }
}
diff --git a/plugins/Overlay/Controller.php b/plugins/Overlay/Controller.php
index 6b938b9443..636683fb36 100644
--- a/plugins/Overlay/Controller.php
+++ b/plugins/Overlay/Controller.php
@@ -11,129 +11,123 @@
class Piwik_Overlay_Controller extends Piwik_Controller
{
-
- /** The index of the plugin */
- public function index()
- {
- Piwik::checkUserHasViewAccess($this->idSite);
-
- $template = 'index';
- if (Piwik_Config::getInstance()->General['overlay_disable_framed_mode']) {
- $template = 'index_noframe';
- }
-
- $view = Piwik_View::factory($template);
-
- $this->setGeneralVariablesView($view);
- $view->showTopMenu = false;
- $view->showSitesSelection = false;
- $view->addToHead = '<script type="text/javascript" src="plugins/Overlay/templates/index.js"></script>'
- . '<link rel="stylesheet" type="text/css" href="plugins/Overlay/templates/index.css" />';
-
- $view->idSite = $this->idSite;
- $view->date = Piwik_Common::getRequestVar('date', 'today');
- $view->period = Piwik_Common::getRequestVar('period', 'day');
-
- $view->ssl = Piwik::isHttps();
-
- echo $view->render();
- }
-
- /** Render the area left of the iframe */
- public function renderSidebar()
- {
- $idSite = Piwik_Common::getRequestVar('idSite');
- $period = Piwik_Common::getRequestVar('period');
- $date = Piwik_Common::getRequestVar('date');
- $currentUrl = Piwik_Common::getRequestVar('currentUrl');
- $currentUrl = Piwik_Common::unsanitizeInputValue($currentUrl);
-
- $normalizedCurrentUrl = Piwik_Tracker_Action::excludeQueryParametersFromUrl($currentUrl, $idSite);
- $normalizedCurrentUrl = Piwik_Common::unsanitizeInputValue($normalizedCurrentUrl);
-
- // load the appropriate row of the page urls report using the label filter
- Piwik_Actions_ArchivingHelper::reloadConfig();
- $path = Piwik_Actions_ArchivingHelper::getActionExplodedNames($normalizedCurrentUrl, Piwik_Tracker_Action::TYPE_ACTION_URL);
- $path = array_map('urlencode', $path);
- $label = implode('>', $path);
- $request = new Piwik_API_Request(
- 'method=Actions.getPageUrls'
- .'&idSite='.urlencode($idSite)
- .'&date='.urlencode($date)
- .'&period='.urlencode($period)
- .'&label='.urlencode($label)
- .'&format=original'
- );
- $dataTable = $request->process();
-
- $data = array();
- if ($dataTable->getRowsCount() > 0)
- {
- $row = $dataTable->getFirstRow();
-
- $translations = Piwik_API_API::getDefaultMetricTranslations();
- $showMetrics = array('nb_hits', 'nb_visits', 'nb_uniq_visitors',
- 'bounce_rate', 'exit_rate', 'avg_time_on_page');
-
-
- foreach ($showMetrics as $metric)
- {
- $value = $row->getColumn($metric);
- if ($value === false)
- {
- // skip unique visitors for period != day
- continue;
- }
- if ($metric == 'avg_time_on_page')
- {
- $value = Piwik::getPrettyTimeFromSeconds($value);
- }
- $data[] = array(
- 'name' => $translations[$metric],
- 'value' => $value
- );
- }
- }
-
- // generate page url string
- foreach ($path as &$part)
- {
- $part = preg_replace(';^/;', '', urldecode($part));
- }
- $page = '/'.implode('/', $path);
- $page = preg_replace(';/index$;', '/', $page);
- if ($page == '/')
- {
- $page = '/index';
- }
-
- // render template
- $view = Piwik_View::factory('sidebar');
- $view->data = $data;
- $view->location = $page;
- $view->normalizedUrl = $normalizedCurrentUrl;
- $view->label = $label;
- $view->idSite = $idSite;
- $view->period = $period;
- $view->date = $date;
- echo $view->render();
- }
-
- /**
- * Start an Overlay session: Redirect to the tracked website. The Piwik
- * tracker will recognize this referrer and start the session.
- */
- public function startOverlaySession()
- {
- $idSite = Piwik_Common::getRequestVar('idsite', 0, 'int');
- Piwik::checkUserHasViewAccess($idSite);
-
- $sitesManager = Piwik_SitesManager_API::getInstance();
- $site = $sitesManager->getSiteFromId($idSite);
- $urls = $sitesManager->getSiteUrlsFromId($idSite);
-
- @header('Content-Type: text/html; charset=UTF-8');
- echo '
+
+ /** The index of the plugin */
+ public function index()
+ {
+ Piwik::checkUserHasViewAccess($this->idSite);
+
+ $template = 'index';
+ if (Piwik_Config::getInstance()->General['overlay_disable_framed_mode']) {
+ $template = 'index_noframe';
+ }
+
+ $view = Piwik_View::factory($template);
+
+ $this->setGeneralVariablesView($view);
+ $view->showTopMenu = false;
+ $view->showSitesSelection = false;
+ $view->addToHead = '<script type="text/javascript" src="plugins/Overlay/templates/index.js"></script>'
+ . '<link rel="stylesheet" type="text/css" href="plugins/Overlay/templates/index.css" />';
+
+ $view->idSite = $this->idSite;
+ $view->date = Piwik_Common::getRequestVar('date', 'today');
+ $view->period = Piwik_Common::getRequestVar('period', 'day');
+
+ $view->ssl = Piwik::isHttps();
+
+ echo $view->render();
+ }
+
+ /** Render the area left of the iframe */
+ public function renderSidebar()
+ {
+ $idSite = Piwik_Common::getRequestVar('idSite');
+ $period = Piwik_Common::getRequestVar('period');
+ $date = Piwik_Common::getRequestVar('date');
+ $currentUrl = Piwik_Common::getRequestVar('currentUrl');
+ $currentUrl = Piwik_Common::unsanitizeInputValue($currentUrl);
+
+ $normalizedCurrentUrl = Piwik_Tracker_Action::excludeQueryParametersFromUrl($currentUrl, $idSite);
+ $normalizedCurrentUrl = Piwik_Common::unsanitizeInputValue($normalizedCurrentUrl);
+
+ // load the appropriate row of the page urls report using the label filter
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+ $path = Piwik_Actions_ArchivingHelper::getActionExplodedNames($normalizedCurrentUrl, Piwik_Tracker_Action::TYPE_ACTION_URL);
+ $path = array_map('urlencode', $path);
+ $label = implode('>', $path);
+ $request = new Piwik_API_Request(
+ 'method=Actions.getPageUrls'
+ . '&idSite=' . urlencode($idSite)
+ . '&date=' . urlencode($date)
+ . '&period=' . urlencode($period)
+ . '&label=' . urlencode($label)
+ . '&format=original'
+ );
+ $dataTable = $request->process();
+
+ $data = array();
+ if ($dataTable->getRowsCount() > 0) {
+ $row = $dataTable->getFirstRow();
+
+ $translations = Piwik_API_API::getDefaultMetricTranslations();
+ $showMetrics = array('nb_hits', 'nb_visits', 'nb_uniq_visitors',
+ 'bounce_rate', 'exit_rate', 'avg_time_on_page');
+
+
+ foreach ($showMetrics as $metric) {
+ $value = $row->getColumn($metric);
+ if ($value === false) {
+ // skip unique visitors for period != day
+ continue;
+ }
+ if ($metric == 'avg_time_on_page') {
+ $value = Piwik::getPrettyTimeFromSeconds($value);
+ }
+ $data[] = array(
+ 'name' => $translations[$metric],
+ 'value' => $value
+ );
+ }
+ }
+
+ // generate page url string
+ foreach ($path as &$part) {
+ $part = preg_replace(';^/;', '', urldecode($part));
+ }
+ $page = '/' . implode('/', $path);
+ $page = preg_replace(';/index$;', '/', $page);
+ if ($page == '/') {
+ $page = '/index';
+ }
+
+ // render template
+ $view = Piwik_View::factory('sidebar');
+ $view->data = $data;
+ $view->location = $page;
+ $view->normalizedUrl = $normalizedCurrentUrl;
+ $view->label = $label;
+ $view->idSite = $idSite;
+ $view->period = $period;
+ $view->date = $date;
+ echo $view->render();
+ }
+
+ /**
+ * Start an Overlay session: Redirect to the tracked website. The Piwik
+ * tracker will recognize this referrer and start the session.
+ */
+ public function startOverlaySession()
+ {
+ $idSite = Piwik_Common::getRequestVar('idsite', 0, 'int');
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $sitesManager = Piwik_SitesManager_API::getInstance();
+ $site = $sitesManager->getSiteFromId($idSite);
+ $urls = $sitesManager->getSiteUrlsFromId($idSite);
+
+ @header('Content-Type: text/html; charset=UTF-8');
+ echo '
<html><head><title></title></head><body>
<script type="text/javascript">
function handleProtocol(url) {
@@ -154,7 +148,7 @@ class Piwik_Overlay_Controller extends Piwik_Controller
var urlToRedirect = window.location.hash.substr(1);
var urlToRedirectWithoutPrefix = removeUrlPrefix(urlToRedirect);
- var knownUrls = '.Piwik_Common::json_encode($urls).';
+ var knownUrls = ' . Piwik_Common::json_encode($urls) . ';
for (var i = 0; i < knownUrls.length; i++) {
var testUrl = removeUrlPrefix(knownUrls[i]);
if (urlToRedirectWithoutPrefix.substr(0, testUrl.length) == testUrl) {
@@ -180,57 +174,57 @@ class Piwik_Overlay_Controller extends Piwik_Controller
}
}
else {
- window.location.href = handleProtocol("'.$site['main_url'].'");
+ window.location.href = handleProtocol("' . $site['main_url'] . '");
};
</script>
</body></html>
';
- }
-
- /**
- * This method is called when the JS from startOverlaySession() detects that the target domain
- * is not configured for the current site.
- */
- public function showErrorWrongDomain()
- {
- $idSite = Piwik_Common::getRequestVar('idSite', 0, 'int');
- Piwik::checkUserHasViewAccess($idSite);
-
- $url = Piwik_Common::getRequestVar('url', '');
- $url = Piwik_Common::unsanitizeInputValue($url);
-
- $message = Piwik_Translate('Overlay_RedirectUrlError', array($url, "\n"));
- $message = nl2br(htmlentities($message));
-
- $view = Piwik_View::factory('error_wrong_domain');
- $view->message = $message;
-
- if (Piwik::isUserHasAdminAccess($idSite)) {
- // TODO use $idSite to link to the correct row. This is tricky because the #rowX ids don't match
- // the site ids when sites have been deleted.
- $url = 'index.php?module=SitesManager&action=index';
- $troubleshoot = htmlentities(Piwik_Translate('Overlay_RedirectUrlErrorAdmin'));
- $troubleshoot = sprintf($troubleshoot, '<a href="' . $url . '" target="_top">', '</a>');
- $view->troubleshoot = $troubleshoot;
- } else {
- $view->troubleshoot = htmlentities(Piwik_Translate('Overlay_RedirectUrlErrorUser'));
- }
-
- echo $view->render();
- }
-
- /**
- * This method is used to pass information from the iframe back to Piwik.
- * Due to the same origin policy, we can't do that directly, so we inject
- * an additional iframe in the Overlay session that calls this controller
- * method.
- * The rendered iframe is from the same origin as the Piwik window so we
- * can bypass the same origin policy and call the parent.
- */
- public function notifyParentIframe()
- {
- $view = Piwik_View::factory('notify_parent_iframe');
- echo $view->render();
- }
-
+ }
+
+ /**
+ * This method is called when the JS from startOverlaySession() detects that the target domain
+ * is not configured for the current site.
+ */
+ public function showErrorWrongDomain()
+ {
+ $idSite = Piwik_Common::getRequestVar('idSite', 0, 'int');
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $url = Piwik_Common::getRequestVar('url', '');
+ $url = Piwik_Common::unsanitizeInputValue($url);
+
+ $message = Piwik_Translate('Overlay_RedirectUrlError', array($url, "\n"));
+ $message = nl2br(htmlentities($message));
+
+ $view = Piwik_View::factory('error_wrong_domain');
+ $view->message = $message;
+
+ if (Piwik::isUserHasAdminAccess($idSite)) {
+ // TODO use $idSite to link to the correct row. This is tricky because the #rowX ids don't match
+ // the site ids when sites have been deleted.
+ $url = 'index.php?module=SitesManager&action=index';
+ $troubleshoot = htmlentities(Piwik_Translate('Overlay_RedirectUrlErrorAdmin'));
+ $troubleshoot = sprintf($troubleshoot, '<a href="' . $url . '" target="_top">', '</a>');
+ $view->troubleshoot = $troubleshoot;
+ } else {
+ $view->troubleshoot = htmlentities(Piwik_Translate('Overlay_RedirectUrlErrorUser'));
+ }
+
+ echo $view->render();
+ }
+
+ /**
+ * This method is used to pass information from the iframe back to Piwik.
+ * Due to the same origin policy, we can't do that directly, so we inject
+ * an additional iframe in the Overlay session that calls this controller
+ * method.
+ * The rendered iframe is from the same origin as the Piwik window so we
+ * can bypass the same origin policy and call the parent.
+ */
+ public function notifyParentIframe()
+ {
+ $view = Piwik_View::factory('notify_parent_iframe');
+ echo $view->render();
+ }
+
}
diff --git a/plugins/Overlay/Overlay.php b/plugins/Overlay/Overlay.php
index 7e800884f3..d7b950491e 100644
--- a/plugins/Overlay/Overlay.php
+++ b/plugins/Overlay/Overlay.php
@@ -11,28 +11,28 @@
class Piwik_Overlay extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('Overlay_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getJsFiles' => 'getJsFiles'
- );
- }
-
- public function getJsFiles($notification)
- {
- $jsFiles = &$notification->getNotificationObject();
- $jsFiles[] = 'plugins/Overlay/templates/rowaction.js';
- $jsFiles[] = 'plugins/Overlay/templates/helper.js';
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('Overlay_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getJsFiles' => 'getJsFiles'
+ );
+ }
+
+ public function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+ $jsFiles[] = 'plugins/Overlay/templates/rowaction.js';
+ $jsFiles[] = 'plugins/Overlay/templates/helper.js';
+ }
}
diff --git a/plugins/Overlay/client/client.css b/plugins/Overlay/client/client.css
index 05e2e87169..ec00ea71da 100644
--- a/plugins/Overlay/client/client.css
+++ b/plugins/Overlay/client/client.css
@@ -1,4 +1,3 @@
-
/**
* Reset styles
*/
@@ -11,84 +10,80 @@
.PIS_LinkHighlightBoxRight,
.PIS_LinkHighlightBoxLeft,
.PIS_LinkHighlightBoxText {
- margin: 0;
- padding: 0;
- border: 0;
- outline: 0;
- font-weight: normal;
- font-style: normal;
- font-size: 11px;
- font-family: Arial, Helvetica, sans-serif;
- vertical-align: baseline;
- line-height: 1.4em;
- text-indent: 0;
- text-decoration: none;
- text-transform: none;
- cursor: default;
- text-align: left;
- float: none;
- color: #333;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-weight: normal;
+ font-style: normal;
+ font-size: 11px;
+ font-family: Arial, Helvetica, sans-serif;
+ vertical-align: baseline;
+ line-height: 1.4em;
+ text-indent: 0;
+ text-decoration: none;
+ text-transform: none;
+ cursor: default;
+ text-align: left;
+ float: none;
+ color: #333;
}
-
-
/**
* Link Tags
*/
.PIS_LinkTag {
- position: absolute;
- z-index: 9999;
- width: 36px;
- height: 21px;
- text-align: left;
- background: url(./linktags_lessshadow.png) no-repeat 0 -21px;
- overflow: hidden;
+ position: absolute;
+ z-index: 9999;
+ width: 36px;
+ height: 21px;
+ text-align: left;
+ background: url(./linktags_lessshadow.png) no-repeat 0 -21px;
+ overflow: hidden;
}
.PIS_LinkTag span {
- position: absolute;
- width: 31px;
- height: 14px;
- font-size: 10px;
- text-align: center;
- font-weight: bold;
- line-height: 14px;
- margin-left: 1px;
+ position: absolute;
+ width: 31px;
+ height: 14px;
+ font-size: 10px;
+ text-align: center;
+ font-weight: bold;
+ line-height: 14px;
+ margin-left: 1px;
}
.PIS_LinkTag.PIS_Highlighted {
- z-index: 10002;
+ z-index: 10002;
}
.PIS_LinkTag.PIS_Highlighted span {
- color: #E87500;
+ color: #E87500;
}
.PIS_LinkTag.PIS_Right {
- background-position: -36px -21px;
+ background-position: -36px -21px;
}
.PIS_LinkTag.PIS_Right span,
.PIS_LinkTag.PIS_BottomRight span {
- margin-left: 5px;
+ margin-left: 5px;
}
.PIS_LinkTag.PIS_Bottom {
- background-position: 0 0;
+ background-position: 0 0;
}
.PIS_LinkTag.PIS_Bottom span,
.PIS_LinkTag.PIS_BottomRight span {
- margin-top: 4px;
+ margin-top: 4px;
}
.PIS_LinkTag.PIS_BottomRight {
- background-position: -36px 0;
+ background-position: -36px 0;
}
-
-
/**
* Link Highlights
*/
@@ -97,60 +92,58 @@
.PIS_LinkHighlightBoxRight,
.PIS_LinkHighlightBoxText,
.PIS_LinkHighlightBoxLeft {
- position: absolute;
- z-index: 10001;
- overflow: hidden;
- width: 2px;
- height: 2px;
- background: #E87500;
+ position: absolute;
+ z-index: 10001;
+ overflow: hidden;
+ width: 2px;
+ height: 2px;
+ background: #E87500;
}
.PIS_LinkHighlightBoxText {
- line-height: 20px;
- height: 20px;
- font-size: 11px;
- color: white;
- width: auto;
+ line-height: 20px;
+ height: 20px;
+ font-size: 11px;
+ color: white;
+ width: auto;
}
-
-
/**
* Status bar
*/
#PIS_StatusBar {
- padding: 10px 0;
- position: fixed;
- z-index: 10020;
- bottom: 0;
- right: 0;
- border-top: 1px solid #ccc;
- border-left: 1px solid #ccc;
- background: #fbfbfb;
- -webkit-border-top-left-radius: 6px;
- -webkit-border-top-right-radius: 0px;
- -webkit-border-bottom-right-radius: 0px;
- -webkit-border-bottom-left-radius: 0px;
- -moz-border-radius-topleft: 6px;
- -moz-border-radius-topright: 0px;
- -moz-border-radius-bottomright: 0px;
- -moz-border-radius-bottomleft: 0px;
- border-top-left-radius: 6px;
- border-top-right-radius: 0px;
- border-bottom-right-radius: 0px;
- border-bottom-left-radius: 0px;
+ padding: 10px 0;
+ position: fixed;
+ z-index: 10020;
+ bottom: 0;
+ right: 0;
+ border-top: 1px solid #ccc;
+ border-left: 1px solid #ccc;
+ background: #fbfbfb;
+ -webkit-border-top-left-radius: 6px;
+ -webkit-border-top-right-radius: 0px;
+ -webkit-border-bottom-right-radius: 0px;
+ -webkit-border-bottom-left-radius: 0px;
+ -moz-border-radius-topleft: 6px;
+ -moz-border-radius-topright: 0px;
+ -moz-border-radius-bottomright: 0px;
+ -moz-border-radius-bottomleft: 0px;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 0px;
+ border-bottom-right-radius: 0px;
+ border-bottom-left-radius: 0px;
}
#PIS_StatusBar .PIS_Item {
- text-align: right;
- padding: 3px 5px 0 0;
- margin: 0 15px 0 20px;
-
- font-weight: bold;
+ text-align: right;
+ padding: 3px 5px 0 0;
+ margin: 0 15px 0 20px;
+
+ font-weight: bold;
}
#PIS_StatusBar .PIS_Loading {
- background: url(./loading.gif) no-repeat right center;
- padding-right: 30px;
+ background: url(./loading.gif) no-repeat right center;
+ padding-right: 30px;
} \ No newline at end of file
diff --git a/plugins/Overlay/client/client.js b/plugins/Overlay/client/client.js
index 93d790f059..f791f8beb5 100644
--- a/plugins/Overlay/client/client.js
+++ b/plugins/Overlay/client/client.js
@@ -1,261 +1,261 @@
+var Piwik_Overlay_Client = (function () {
+
+ /** jQuery */
+ var $;
+
+ /** Url of the Piwik root */
+ var piwikRoot;
+
+ /** Piwik idsite */
+ var idSite;
+
+ /** The current period and date */
+ var period, date;
+
+ /** Reference to the status bar DOM element */
+ var statusBar;
+
+ /** Load the client CSS */
+ function loadCss() {
+ var css = c('link').attr({
+ rel: 'stylesheet',
+ type: 'text/css',
+ href: piwikRoot + 'plugins/Overlay/client/client.css'
+ });
+ $('head').append(css);
+ }
+
+ /**
+ * This method loads jQuery, if it is not there yet.
+ * The callback is triggered after jQuery is loaded.
+ */
+ function loadJQuery(callback) {
+ if (typeof jQuery != 'undefined') {
+ $ = jQuery;
+ callback();
+ }
+ else {
+ Piwik_Overlay_Client.loadScript('libs/jquery/jquery.js', function () {
+ $ = jQuery;
+ jQuery.noConflict();
+ callback();
+ });
+ }
+ }
+
+ /**
+ * Notify Piwik of the current iframe location.
+ * This way, we can display additional metrics on the side of the iframe.
+ */
+ function notifyPiwikOfLocation() {
+ // check whether the session has been opened in a new tab (instead of an iframe)
+ if (window != window.top) {
+ var iframe = c('iframe', false, {
+ src: piwikRoot + 'index.php?module=Overlay&action=notifyParentIframe#' + window.location.href
+ }).css({width: 0, height: 0, border: 0});
+
+ // in some cases, calling append right away doesn't work in IE8
+ $(document).ready(function () {
+ $('body').append(iframe);
+ });
+ }
+ }
+
+ /** Create a jqueryfied DOM element */
+ function c(tagName, className, attributes) {
+ var el = $(document.createElement(tagName));
+
+ if (className) {
+ if (className.substring(0, 1) == '#') {
+ var id = className.substring(1, className.length);
+ id = 'PIS_' + id;
+ el.attr('id', id);
+ }
+ else {
+ className = 'PIS_' + className;
+ el.addClass(className);
+ }
+ }
+
+ if (attributes) {
+ el.attr(attributes);
+ }
+
+ return el;
+ }
+
+ /** Special treatment for some internet explorers */
+ var ieStatusBarEventsBound = false;
+
+ function handleIEStatusBar() {
+ if (navigator.appVersion.indexOf("MSIE 7.") == -1
+ && navigator.appVersion.indexOf("MSIE 8.") == -1) {
+ // this is not IE8 or lower
+ return;
+ }
+
+ // IE7/8 can't handle position:fixed so we need to do it by hand
+ statusBar.css({
+ position: 'absolute',
+ right: 'auto',
+ bottom: 'auto',
+ left: 0,
+ top: 0
+ });
+
+ var position = function () {
+ var scrollY = document.body.parentElement.scrollTop;
+ var scrollX = document.body.parentElement.scrollLeft;
+ statusBar.css({
+ top: (scrollY + $(window).height() - statusBar.outerHeight()) + 'px',
+ left: (scrollX + $(window).width() - statusBar.outerWidth()) + 'px'
+ });
+ };
+
+ position();
+
+ statusBar.css({width: 'auto'});
+ if (statusBar.width() < 350) {
+ statusBar.width(350);
+ } else {
+ statusBar.width(statusBar.width());
+ }
+
+ if (!ieStatusBarEventsBound) {
+ ieStatusBarEventsBound = true;
+ $(window).resize(position);
+ $(window).scroll(position);
+ }
+ }
+
+ return {
+
+ /** Initialize in-site analytics */
+ initialize: function (pPiwikRoot, pIdSite, pPeriod, pDate) {
+ piwikRoot = pPiwikRoot;
+ idSite = pIdSite;
+ period = pPeriod;
+ date = pDate;
+
+ var load = this.loadScript;
+ var loading = this.loadingNotification;
+
+ loadJQuery(function () {
+ notifyPiwikOfLocation();
+ loadCss();
+
+ // translations
+ load('plugins/Overlay/client/translations.js', function () {
+ Piwik_Overlay_Translations.initialize(function () {
+ // following pages
+ var finishPages = loading('Loading following pages');
+ load('plugins/Overlay/client/followingpages.js', function () {
+ Piwik_Overlay_FollowingPages.initialize(finishPages);
+ });
+
+ });
+ });
+ });
+ },
+
+ /** Create a jqueryfied DOM element */
+ createElement: function (tagName, className, attributes) {
+ return c(tagName, className, attributes);
+ },
+
+ /** Load a script and wait for it to be loaded */
+ loadScript: function (relativePath, callback) {
+ var loaded = false;
+ var onLoad = function () {
+ if (!loaded) {
+ loaded = true;
+ callback();
+ }
+ };
+
+ var head = document.getElementsByTagName('head')[0];
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+
+ script.onreadystatechange = function () {
+ if (this.readyState == 'loaded' || this.readyState == 'complete') {
+ onLoad();
+ }
+ };
+ script.onload = onLoad;
+
+ script.src = piwikRoot + relativePath + '?v=1';
+ head.appendChild(script);
+ },
+
+ /** Piwik Overlay API Request */
+ api: function (method, callback, additionalParams) {
+ var url = piwikRoot + 'index.php?module=API&method=Overlay.' + method
+ + '&idSite=' + idSite + '&period=' + period + '&date=' + date + '&format=JSON&filter_limit=-1';
+
+ if (additionalParams) {
+ url += '&' + additionalParams;
+ }
+
+ $.getJSON(url + "&jsoncallback=?", function (data) {
+ if (typeof data.result != 'undefined' && data.result == 'error') {
+ alert('Error: ' + data.message);
+ }
+ else {
+ callback(data);
+ }
+ });
+ },
+
+ /**
+ * Initialize a notification
+ * To hide the notification use the returned callback
+ */
+ notification: function (message, addClass) {
+ if (!statusBar) {
+ statusBar = c('div', '#StatusBar').css('opacity', .8);
+ $('body').prepend(statusBar);
+ }
+
+ var item = c('div', 'Item').html(message);
+
+ if (addClass) {
+ item.addClass('PIS_' + addClass);
+ }
+
+ statusBar.show().append(item);
+
+ handleIEStatusBar();
+ window.setTimeout(handleIEStatusBar, 100);
+
+ return function () {
+ item.remove();
+ if (statusBar.children().size() == 0) {
+ statusBar.hide();
+ } else {
+ handleIEStatusBar();
+ }
+ };
+ },
+
+ /** Hide all notifications with a certain class */
+ hideNotifications: function (className) {
+ statusBar.find('.PIS_' + className).remove();
+ if (statusBar.children().size() == 0) {
+ statusBar.hide();
+ } else {
+ handleIEStatusBar();
+ }
+ },
+
+ /**
+ * Initialize a loading notification
+ * To hide the notification use the returned callback
+ */
+ loadingNotification: function (message) {
+ return Piwik_Overlay_Client.notification(message, 'Loading');
+ }
+
+ };
-var Piwik_Overlay_Client = (function() {
-
- /** jQuery */
- var $;
-
- /** Url of the Piwik root */
- var piwikRoot;
-
- /** Piwik idsite */
- var idSite;
-
- /** The current period and date */
- var period, date;
-
- /** Reference to the status bar DOM element */
- var statusBar;
-
- /** Load the client CSS */
- function loadCss() {
- var css = c('link').attr({
- rel: 'stylesheet',
- type: 'text/css',
- href: piwikRoot + 'plugins/Overlay/client/client.css'
- });
- $('head').append(css);
- }
-
- /**
- * This method loads jQuery, if it is not there yet.
- * The callback is triggered after jQuery is loaded.
- */
- function loadJQuery(callback) {
- if (typeof jQuery != 'undefined') {
- $ = jQuery;
- callback();
- }
- else {
- Piwik_Overlay_Client.loadScript('libs/jquery/jquery.js', function() {
- $ = jQuery;
- jQuery.noConflict();
- callback();
- });
- }
- }
-
- /**
- * Notify Piwik of the current iframe location.
- * This way, we can display additional metrics on the side of the iframe.
- */
- function notifyPiwikOfLocation() {
- // check whether the session has been opened in a new tab (instead of an iframe)
- if (window != window.top) {
- var iframe = c('iframe', false, {
- src: piwikRoot + 'index.php?module=Overlay&action=notifyParentIframe#' + window.location.href
- }).css({width: 0, height: 0, border: 0});
-
- // in some cases, calling append right away doesn't work in IE8
- $(document).ready(function() {
- $('body').append(iframe);
- });
- }
- }
-
- /** Create a jqueryfied DOM element */
- function c(tagName, className, attributes) {
- var el = $(document.createElement(tagName));
-
- if (className) {
- if (className.substring(0, 1) == '#') {
- var id = className.substring(1, className.length);
- id = 'PIS_' + id;
- el.attr('id', id);
- }
- else {
- className = 'PIS_' + className;
- el.addClass(className);
- }
- }
-
- if (attributes) {
- el.attr(attributes);
- }
-
- return el;
- }
-
- /** Special treatment for some internet explorers */
- var ieStatusBarEventsBound = false;
- function handleIEStatusBar() {
- if (navigator.appVersion.indexOf("MSIE 7.") == -1
- && navigator.appVersion.indexOf("MSIE 8.") == -1) {
- // this is not IE8 or lower
- return;
- }
-
- // IE7/8 can't handle position:fixed so we need to do it by hand
- statusBar.css({
- position: 'absolute',
- right: 'auto',
- bottom: 'auto',
- left: 0,
- top: 0
- });
-
- var position = function() {
- var scrollY = document.body.parentElement.scrollTop;
- var scrollX = document.body.parentElement.scrollLeft;
- statusBar.css({
- top: (scrollY + $(window).height() - statusBar.outerHeight()) + 'px',
- left: (scrollX + $(window).width() - statusBar.outerWidth()) + 'px'
- });
- };
-
- position();
-
- statusBar.css({width: 'auto'});
- if (statusBar.width() < 350) {
- statusBar.width(350);
- } else {
- statusBar.width(statusBar.width());
- }
-
- if (!ieStatusBarEventsBound) {
- ieStatusBarEventsBound = true;
- $(window).resize(position);
- $(window).scroll(position);
- }
- }
-
- return {
-
- /** Initialize in-site analytics */
- initialize: function(pPiwikRoot, pIdSite, pPeriod, pDate) {
- piwikRoot = pPiwikRoot;
- idSite = pIdSite;
- period = pPeriod;
- date = pDate;
-
- var load = this.loadScript;
- var loading = this.loadingNotification;
-
- loadJQuery(function() {
- notifyPiwikOfLocation();
- loadCss();
-
- // translations
- load('plugins/Overlay/client/translations.js', function() {
- Piwik_Overlay_Translations.initialize(function() {
- // following pages
- var finishPages = loading('Loading following pages');
- load('plugins/Overlay/client/followingpages.js', function() {
- Piwik_Overlay_FollowingPages.initialize(finishPages);
- });
-
- });
- });
- });
- },
-
- /** Create a jqueryfied DOM element */
- createElement: function(tagName, className, attributes) {
- return c(tagName, className, attributes);
- },
-
- /** Load a script and wait for it to be loaded */
- loadScript: function(relativePath, callback) {
- var loaded = false;
- var onLoad = function() {
- if (!loaded) {
- loaded = true;
- callback();
- }
- };
-
- var head = document.getElementsByTagName('head')[0];
- var script = document.createElement('script');
- script.type = 'text/javascript';
-
- script.onreadystatechange = function () {
- if (this.readyState == 'loaded' || this.readyState == 'complete') {
- onLoad();
- }
- };
- script.onload = onLoad;
-
- script.src = piwikRoot+relativePath+'?v=1';
- head.appendChild(script);
- },
-
- /** Piwik Overlay API Request */
- api: function(method, callback, additionalParams) {
- var url = piwikRoot+'index.php?module=API&method=Overlay.'+method
- +'&idSite='+idSite+'&period='+period+'&date='+date+'&format=JSON&filter_limit=-1';
-
- if (additionalParams) {
- url += '&' + additionalParams;
- }
-
- $.getJSON(url+"&jsoncallback=?", function(data) {
- if (typeof data.result != 'undefined' && data.result == 'error') {
- alert('Error: ' + data.message);
- }
- else {
- callback(data);
- }
- });
- },
-
- /**
- * Initialize a notification
- * To hide the notification use the returned callback
- */
- notification: function(message, addClass) {
- if (!statusBar) {
- statusBar = c('div', '#StatusBar').css('opacity', .8);
- $('body').prepend(statusBar);
- }
-
- var item = c('div', 'Item').html(message);
-
- if (addClass) {
- item.addClass('PIS_' + addClass);
- }
-
- statusBar.show().append(item);
-
- handleIEStatusBar();
- window.setTimeout(handleIEStatusBar, 100);
-
- return function() {
- item.remove();
- if (statusBar.children().size() == 0) {
- statusBar.hide();
- } else {
- handleIEStatusBar();
- }
- };
- },
-
- /** Hide all notifications with a certain class */
- hideNotifications: function(className) {
- statusBar.find('.PIS_' + className).remove();
- if (statusBar.children().size() == 0) {
- statusBar.hide();
- } else {
- handleIEStatusBar();
- }
- },
-
- /**
- * Initialize a loading notification
- * To hide the notification use the returned callback
- */
- loadingNotification: function(message) {
- return Piwik_Overlay_Client.notification(message, 'Loading');
- }
-
- };
-
})();
diff --git a/plugins/Overlay/client/followingpages.js b/plugins/Overlay/client/followingpages.js
index edb9c40e1e..e20252463f 100644
--- a/plugins/Overlay/client/followingpages.js
+++ b/plugins/Overlay/client/followingpages.js
@@ -1,511 +1,511 @@
-var Piwik_Overlay_FollowingPages = (function() {
-
- /** jQuery */
- var $ = jQuery;
-
- /** Info about the following pages */
- var followingPages = [];
-
- /** List of excluded get parameters */
- var excludedParams = [];
-
- /** Index of the links on the page */
- var linksOnPage = {};
-
- /** Reference to create element function */
- var c;
-
- /** Load the following pages */
- function load(callback) {
- // normalize current location
- var location = window.location.href;
- location = Piwik_Overlay_UrlNormalizer.normalize(location);
- location = (("https:" == document.location.protocol) ? 'https' : 'http') + '://' + location;
-
- var excludedParamsLoaded = false;
- var followingPagesLoaded = false;
-
- // load excluded params
- Piwik_Overlay_Client.api('getExcludedQueryParameters', function(data) {
- for (var i = 0; i < data.length; i++) {
- if (typeof data[i] == 'object') {
- data[i] = data[i][0];
- }
- }
- excludedParams = data;
-
- excludedParamsLoaded = true;
- if (followingPagesLoaded) {
- callback();
- }
- });
-
- // load following pages
- Piwik_Overlay_Client.api('getFollowingPages', function(data) {
- followingPages = data;
- processFollowingPages();
-
- followingPagesLoaded = true;
- if (excludedParamsLoaded) {
- callback();
- }
- }, 'url=' + encodeURIComponent(location));
- }
-
- /** Normalize the URLs of following pages and aggregate some stats */
- function processFollowingPages() {
- var totalClicks = 0;
- for (var i = 0; i < followingPages.length; i++) {
- var page = followingPages[i];
- // though the following pages are returned without the prefix, downloads
- // and outlinks still have it.
- page.label = Piwik_Overlay_UrlNormalizer.removeUrlPrefix(page.label);
- totalClicks += followingPages[i].referrals;
- }
- for (i = 0; i < followingPages.length; i++) {
- followingPages[i].clickRate = followingPages[i].referrals / totalClicks * 100;
- }
- }
-
- /**
- * Build an index of links on the page.
- * This function is passed to $('a').each()
- */
- var processLinkDelta = false;
-
- function processLink() {
- var a = $(this);
- a[0].piwikDiscovered = true;
-
- var href = a.attr('href');
- href = Piwik_Overlay_UrlNormalizer.normalize(href);
-
- if (href) {
- if (typeof linksOnPage[href] == 'undefined') {
- linksOnPage[href] = [a];
- }
- else {
- linksOnPage[href].push(a);
- }
- }
-
- if (href && processLinkDelta !== false) {
- if (typeof processLinkDelta[href] == 'undefined') {
- processLinkDelta[href] = [a];
- }
- else {
- processLinkDelta[href].push(a);
- }
- }
- }
-
- var repositionTimeout = false;
- var resizeTimeout = false;
-
- function build(callback) {
- // build an index of all links on the page
- $('a').each(processLink);
-
- // add tags to known following pages
- createLinkTags(linksOnPage);
-
- // position the tags
- positionLinkTags();
-
- callback();
-
- // check on a regular basis whether new links have appeared.
- // we use a timeout instead of an interval to make sure one call is done before
- // the next one is triggered
- var repositionAfterTimeout;
- repositionAfterTimeout = function() {
- repositionTimeout = window.setTimeout(function() {
- findNewLinks();
- positionLinkTags(repositionAfterTimeout);
- }, 1800);
- };
- repositionAfterTimeout();
-
- // reposition link tags on window resize
- $(window).resize(function() {
- if (repositionTimeout) {
- window.clearTimeout(repositionTimeout);
- }
- if (resizeTimeout) {
- window.clearTimeout(resizeTimeout);
- }
- resizeTimeout = window.setTimeout(function() {
- positionLinkTags();
- repositionAfterTimeout();
- }, 70);
- });
- }
-
- /** Create a batch of link tags */
- function createLinkTags(links) {
- var body = $('body');
- for (var i = 0; i < followingPages.length; i++) {
- var url = followingPages[i].label;
- if (typeof links[url] != 'undefined') {
- for (var j = 0; j < links[url].length; j++) {
- createLinkTag(links[url][j], url, followingPages[i], body);
- }
- }
- }
- }
-
- /** Create the link tag element */
- function createLinkTag(linkTag, linkUrl, data, body) {
- if (typeof linkTag[0].piwikTagElement != 'undefined' && linkTag[0].piwikTagElement !== null) {
- // this link tag already has a tag element. happens in rare cases.
- return;
- }
-
- linkTag[0].piwikTagElement = true;
-
- var rate = data.clickRate;
- if (rate < 10) {
- rate = Math.round(rate * 10) / 10;
- } else {
- rate = Math.round(rate);
- }
-
- var span = c('span').html(rate + '%');
- var tagElement = c('div', 'LinkTag').append(span).hide();
- body.prepend(tagElement);
-
- linkTag.add(tagElement).hover(function() {
- highlightLink(linkTag, linkUrl, data);
- }, function() {
- unHighlightLink(linkTag, linkUrl);
- });
-
- // attach the tag element to the link element. we can't use .data() because jquery
- // would remove it when removing the link from the dom. but we still need to find
- // the tag element to remove it as well.
- linkTag[0].piwikTagElement = tagElement;
- }
-
- /** Position the link tags next to the links */
- function positionLinkTags(callback) {
- var url, linkTag, tagElement, offset, top, left, isRight, hasOneChild, inlineChild;
- var tagWidth = 36, tagHeight = 21;
- var tagsToRemove = [];
-
- for (var i = 0; i < followingPages.length; i++) {
- url = followingPages[i].label;
- if (typeof linksOnPage[url] != 'undefined') {
- for (var j = 0; j < linksOnPage[url].length; j++) {
- linkTag = linksOnPage[url][j];
- tagElement = linkTag[0].piwikTagElement;
-
- if (linkTag.closest('html').length == 0 || !tagElement) {
- // the link has been removed from the dom
- if (tagElement) {
- tagElement.hide();
- }
- // mark for deletion. don't delete it now because we
- // are iterating of the array it's in. it will be deleted
- // below this for loop.
- tagsToRemove.push({
- index1: url,
- index2: j
- });
- continue;
- }
-
- hasOneChild = checkHasOneChild(linkTag);
- inlineChild = false;
- if (hasOneChild && linkTag.css('display') != 'block') {
- inlineChild = linkTag.children().eq(0);
- }
-
- if (getVisibility(linkTag) == 'hidden' || (
- // in case of hasOneChild: jquery always returns linkTag.is(':visible')=false
- !linkTag.is(':visible') && !(hasOneChild && inlineChild && inlineChild.is(':visible'))
- )) {
- // link is not visible
- tagElement.hide();
- continue;
- }
-
- tagElement.attr('class', 'PIS_LinkTag'); // reset class
- if (tagElement[0].piwikHighlighted) {
- tagElement.addClass('PIS_Highlighted');
- }
-
- // see comment in highlightLink()
- if (hasOneChild && linkTag.find('> img').size() == 1) {
- offset = linkTag.find('> img').offset();
- if (offset.left == 0 && offset.top == 0) {
- offset = linkTag.offset();
- }
- } else if (inlineChild !== false) {
- offset = inlineChild.offset();
- } else {
- offset = linkTag.offset();
- }
-
- top = offset.top - tagHeight + 6;
- left = offset.left - tagWidth + 10;
-
- if (isRight = (left < 2)) {
- tagElement.addClass('PIS_Right');
- left = offset.left + linkTag.outerWidth() - 10;
- }
-
- if (top < 2) {
- tagElement.addClass(isRight ? 'PIS_BottomRight' : 'PIS_Bottom');
- top = offset.top + linkTag.outerHeight() - 6;
- }
-
- tagElement.css({
- top: top + 'px',
- left: left + 'px'
- }).show();
-
- }
- }
- }
-
- // walk tagsToRemove from back to front because it contains the indexes in ascending
- // order. removing something from the front will impact the indexes that come after-
- // wards. this can be avoided by starting in the back.
- for (var k = tagsToRemove.length - 1; k >= 0; k--) {
- var tagToRemove = tagsToRemove[k];
- linkTag = linksOnPage[tagToRemove.index1][tagToRemove.index2];
- // remove the tag element from the dom
- if (linkTag && linkTag[0] && linkTag[0].piwikTagElement) {
- tagElement = linkTag[0].piwikTagElement;
- if (tagElement[0].piwikHighlighted) {
- unHighlightLink(linkTag, tagToRemove.index1);
- }
- tagElement.remove();
- linkTag[0].piwikTagElement = null;
- }
- // remove the link from the index
- linksOnPage[tagToRemove.index1].splice(tagToRemove.index2, 1);
- if (linksOnPage[tagToRemove.index1].length == 0) {
- delete linksOnPage[tagToRemove.index1];
- }
- }
-
- if (typeof callback == 'function') {
- callback();
- }
- }
-
- /** Get the visibility of an element */
- function getVisibility(el) {
- var visibility = el.css('visibility');
- if (visibility == 'inherit') {
- el = el.parent();
- if (el.size() > 0) {
- return getVisibility(el);
- }
- }
- return visibility;
- }
-
- /**
- * Find out whether a link has only one child. Using .children().size() == 1 doesn't work
- * because it doesn't take additional text nodes into account.
- */
- function checkHasOneChild(linkTag) {
- var hasOneChild = (linkTag.children().size() == 1);
- if (hasOneChild) {
- // if the element contains one tag and some text, hasOneChild is set incorrectly
- var contents = linkTag.contents();
- if (contents.size() > 1) {
- // find non-empty text nodes
- contents = contents.filter(function() {
- return this.nodeType == 3 && // text node
- $.trim(this.data).length > 0; // contains more than whitespaces
- });
- if (contents.size() > 0) {
- hasOneChild = false;
- }
- }
- }
- return hasOneChild;
- }
-
- /** Check whether new links have been added to the dom */
- function findNewLinks() {
- var newLinks = $('a').filter(function() {
- return typeof this.piwikDiscovered == 'undefined' || this.piwikDiscovered === null;
- });
-
- if (newLinks.size() == 0) {
- return;
- }
-
- processLinkDelta = {};
- newLinks.each(processLink);
- createLinkTags(processLinkDelta);
- processLinkDelta = false;
- }
-
- /** Dom elements used for drawing a box around the link */
- var highlightElements = [];
-
- /** Highlight a link on hover */
- function highlightLink(linkTag, linkUrl, data) {
- if (highlightElements.length == 0) {
- highlightElements.push(c('div', 'LinkHighlightBoxTop'));
- highlightElements.push(c('div', 'LinkHighlightBoxRight'));
- highlightElements.push(c('div', 'LinkHighlightBoxLeft'));
-
- highlightElements.push(c('div', 'LinkHighlightBoxText'));
-
- var body = $('body');
- for (var i = 0; i < highlightElements.length; i++) {
- body.prepend(highlightElements[i].css({display: 'none'}));
- }
- }
-
- var width = linkTag.outerWidth();
-
- var offset, height;
- var hasOneChild = checkHasOneChild(linkTag);
- if (hasOneChild && linkTag.find('img').size() == 1) {
- // if the <a> tag contains only an <img>, the offset and height methods don't work properly.
- // as a result, the box around the image link would be wrong. we use the image to derive
- // the offset and height instead of the link to get correct values.
- var img = linkTag.find('img');
- offset = img.offset();
- height = img.outerHeight();
- }
- if (hasOneChild && linkTag.css('display') != 'block') {
- // if the <a> tag is not displayed as block and has only one child, using the child to
- // derive the offset and dimensions is more robust.
- var child = linkTag.children().eq(0);
- offset = child.offset();
- height = child.outerHeight();
- width = child.outerWidth();
- } else {
- offset = linkTag.offset();
- height = linkTag.outerHeight();
- }
-
- highlightElements[0].width(width).css({top: offset.top - 2, left: offset.left}).show();
- highlightElements[1].height(height + 4).css({top: offset.top - 2, left: offset.left + width}).show();
- highlightElements[2].height(height + 4).css({top: offset.top - 2, left: offset.left - 2}).show();
-
- var numLinks = linksOnPage[linkUrl].length;
- var text;
- if (numLinks > 1) {
- text = Piwik_Overlay_Translations.get('clicksFromXLinks')
- .replace(/%1\$s/, data.referrals)
- .replace(/%2\$s/, numLinks);
- } else if (data.referrals == 1) {
- text = Piwik_Overlay_Translations.get('oneClick');
- } else {
- text = Piwik_Overlay_Translations.get('clicks')
- .replace(/%s/, data.referrals);
- }
-
- var padding = '&nbsp;&nbsp;';
- highlightElements[3].html(padding + text + padding).css({
- width: 'auto',
- top: offset.top + height,
- left: offset.left - 2
- }).show();
- if (highlightElements[3].width() < width + 4) {
- // we cannot use minWidth because of IE7
- highlightElements[3].width(width + 4);
- }
-
- for (var j = 0; j < numLinks; j++) {
- var tag = linksOnPage[linkUrl][j][0].piwikTagElement;
- tag.addClass('PIS_Highlighted');
- tag[0].piwikHighlighted = true;
- }
-
- // Sometimes it fails to remove the notification when the hovered element is removed.
- // To make sure we don't display more than one location at a time, we hide all before showing the new one.
- Piwik_Overlay_Client.hideNotifications('LinkLocation');
-
- // we don't use .data() because jquery would remove the callback when the link tag is removed
- linkTag[0].piwikHideNotification = Piwik_Overlay_Client.notification(
- Piwik_Overlay_Translations.get('link') + ': ' + linkUrl, 'LinkLocation');
- }
-
- /** Remove highlight from link */
- function unHighlightLink(linkTag, linkUrl) {
- for (var i = 0; i < highlightElements.length; i++) {
- highlightElements[i].hide();
- }
-
- var numLinks = linksOnPage[linkUrl].length;
- for (var j = 0; j < numLinks; j++) {
- var tag = linksOnPage[linkUrl][j][0].piwikTagElement;
- if (tag) {
- tag.removeClass('PIS_Highlighted');
- tag[0].piwikHighlighted = false;
- }
- }
-
- if ((typeof linkTag[0].piwikHideNotification) == 'function') {
- linkTag[0].piwikHideNotification();
- linkTag[0].piwikHideNotification = null;
- }
- }
-
-
- return {
-
- /**
- * The main method
- */
- initialize: function(finishCallback) {
- c = Piwik_Overlay_Client.createElement;
- Piwik_Overlay_Client.loadScript('plugins/Overlay/client/urlnormalizer.js', function() {
- Piwik_Overlay_UrlNormalizer.initialize();
- load(function() {
- Piwik_Overlay_UrlNormalizer.setExcludedParameters(excludedParams);
- build(function() {
- finishCallback();
- })
- });
- });
- },
-
- /**
- * Remove everything from the dom and terminate timeouts.
- * This can be used from the console in order to load a new implementation for debugging afterwards.
- * If you add `Piwik_Overlay_FollowingPages.remove();` to the beginning and
- * `Piwik_Overlay_FollowingPages.initialize(function(){});` to the end of this file, you can just
- * paste it into the console to inject the new implementation.
- */
- remove: function() {
- for (var i = 0; i < followingPages.length; i++) {
- var url = followingPages[i].label;
- if (typeof linksOnPage[url] != 'undefined') {
- for (var j = 0; j < linksOnPage[url].length; j++) {
- var linkTag = linksOnPage[url][j];
- var tagElement = linkTag[0].piwikTagElement;
- if (tagElement) {
- tagElement.remove();
- }
- linkTag[0].piwikTagElement = null;
-
- $(linkTag).unbind('mouseenter').unbind('mouseleave');
- }
- }
- }
- for (i = 0; i < highlightElements.length; i++) {
- highlightElements[i].remove();
- }
- if (repositionTimeout) {
- window.clearTimeout(repositionTimeout);
- }
- if (resizeTimeout) {
- window.clearTimeout(resizeTimeout);
- }
- $(window).unbind('resize');
- }
-
- };
+var Piwik_Overlay_FollowingPages = (function () {
+
+ /** jQuery */
+ var $ = jQuery;
+
+ /** Info about the following pages */
+ var followingPages = [];
+
+ /** List of excluded get parameters */
+ var excludedParams = [];
+
+ /** Index of the links on the page */
+ var linksOnPage = {};
+
+ /** Reference to create element function */
+ var c;
+
+ /** Load the following pages */
+ function load(callback) {
+ // normalize current location
+ var location = window.location.href;
+ location = Piwik_Overlay_UrlNormalizer.normalize(location);
+ location = (("https:" == document.location.protocol) ? 'https' : 'http') + '://' + location;
+
+ var excludedParamsLoaded = false;
+ var followingPagesLoaded = false;
+
+ // load excluded params
+ Piwik_Overlay_Client.api('getExcludedQueryParameters', function (data) {
+ for (var i = 0; i < data.length; i++) {
+ if (typeof data[i] == 'object') {
+ data[i] = data[i][0];
+ }
+ }
+ excludedParams = data;
+
+ excludedParamsLoaded = true;
+ if (followingPagesLoaded) {
+ callback();
+ }
+ });
+
+ // load following pages
+ Piwik_Overlay_Client.api('getFollowingPages', function (data) {
+ followingPages = data;
+ processFollowingPages();
+
+ followingPagesLoaded = true;
+ if (excludedParamsLoaded) {
+ callback();
+ }
+ }, 'url=' + encodeURIComponent(location));
+ }
+
+ /** Normalize the URLs of following pages and aggregate some stats */
+ function processFollowingPages() {
+ var totalClicks = 0;
+ for (var i = 0; i < followingPages.length; i++) {
+ var page = followingPages[i];
+ // though the following pages are returned without the prefix, downloads
+ // and outlinks still have it.
+ page.label = Piwik_Overlay_UrlNormalizer.removeUrlPrefix(page.label);
+ totalClicks += followingPages[i].referrals;
+ }
+ for (i = 0; i < followingPages.length; i++) {
+ followingPages[i].clickRate = followingPages[i].referrals / totalClicks * 100;
+ }
+ }
+
+ /**
+ * Build an index of links on the page.
+ * This function is passed to $('a').each()
+ */
+ var processLinkDelta = false;
+
+ function processLink() {
+ var a = $(this);
+ a[0].piwikDiscovered = true;
+
+ var href = a.attr('href');
+ href = Piwik_Overlay_UrlNormalizer.normalize(href);
+
+ if (href) {
+ if (typeof linksOnPage[href] == 'undefined') {
+ linksOnPage[href] = [a];
+ }
+ else {
+ linksOnPage[href].push(a);
+ }
+ }
+
+ if (href && processLinkDelta !== false) {
+ if (typeof processLinkDelta[href] == 'undefined') {
+ processLinkDelta[href] = [a];
+ }
+ else {
+ processLinkDelta[href].push(a);
+ }
+ }
+ }
+
+ var repositionTimeout = false;
+ var resizeTimeout = false;
+
+ function build(callback) {
+ // build an index of all links on the page
+ $('a').each(processLink);
+
+ // add tags to known following pages
+ createLinkTags(linksOnPage);
+
+ // position the tags
+ positionLinkTags();
+
+ callback();
+
+ // check on a regular basis whether new links have appeared.
+ // we use a timeout instead of an interval to make sure one call is done before
+ // the next one is triggered
+ var repositionAfterTimeout;
+ repositionAfterTimeout = function () {
+ repositionTimeout = window.setTimeout(function () {
+ findNewLinks();
+ positionLinkTags(repositionAfterTimeout);
+ }, 1800);
+ };
+ repositionAfterTimeout();
+
+ // reposition link tags on window resize
+ $(window).resize(function () {
+ if (repositionTimeout) {
+ window.clearTimeout(repositionTimeout);
+ }
+ if (resizeTimeout) {
+ window.clearTimeout(resizeTimeout);
+ }
+ resizeTimeout = window.setTimeout(function () {
+ positionLinkTags();
+ repositionAfterTimeout();
+ }, 70);
+ });
+ }
+
+ /** Create a batch of link tags */
+ function createLinkTags(links) {
+ var body = $('body');
+ for (var i = 0; i < followingPages.length; i++) {
+ var url = followingPages[i].label;
+ if (typeof links[url] != 'undefined') {
+ for (var j = 0; j < links[url].length; j++) {
+ createLinkTag(links[url][j], url, followingPages[i], body);
+ }
+ }
+ }
+ }
+
+ /** Create the link tag element */
+ function createLinkTag(linkTag, linkUrl, data, body) {
+ if (typeof linkTag[0].piwikTagElement != 'undefined' && linkTag[0].piwikTagElement !== null) {
+ // this link tag already has a tag element. happens in rare cases.
+ return;
+ }
+
+ linkTag[0].piwikTagElement = true;
+
+ var rate = data.clickRate;
+ if (rate < 10) {
+ rate = Math.round(rate * 10) / 10;
+ } else {
+ rate = Math.round(rate);
+ }
+
+ var span = c('span').html(rate + '%');
+ var tagElement = c('div', 'LinkTag').append(span).hide();
+ body.prepend(tagElement);
+
+ linkTag.add(tagElement).hover(function () {
+ highlightLink(linkTag, linkUrl, data);
+ }, function () {
+ unHighlightLink(linkTag, linkUrl);
+ });
+
+ // attach the tag element to the link element. we can't use .data() because jquery
+ // would remove it when removing the link from the dom. but we still need to find
+ // the tag element to remove it as well.
+ linkTag[0].piwikTagElement = tagElement;
+ }
+
+ /** Position the link tags next to the links */
+ function positionLinkTags(callback) {
+ var url, linkTag, tagElement, offset, top, left, isRight, hasOneChild, inlineChild;
+ var tagWidth = 36, tagHeight = 21;
+ var tagsToRemove = [];
+
+ for (var i = 0; i < followingPages.length; i++) {
+ url = followingPages[i].label;
+ if (typeof linksOnPage[url] != 'undefined') {
+ for (var j = 0; j < linksOnPage[url].length; j++) {
+ linkTag = linksOnPage[url][j];
+ tagElement = linkTag[0].piwikTagElement;
+
+ if (linkTag.closest('html').length == 0 || !tagElement) {
+ // the link has been removed from the dom
+ if (tagElement) {
+ tagElement.hide();
+ }
+ // mark for deletion. don't delete it now because we
+ // are iterating of the array it's in. it will be deleted
+ // below this for loop.
+ tagsToRemove.push({
+ index1: url,
+ index2: j
+ });
+ continue;
+ }
+
+ hasOneChild = checkHasOneChild(linkTag);
+ inlineChild = false;
+ if (hasOneChild && linkTag.css('display') != 'block') {
+ inlineChild = linkTag.children().eq(0);
+ }
+
+ if (getVisibility(linkTag) == 'hidden' || (
+ // in case of hasOneChild: jquery always returns linkTag.is(':visible')=false
+ !linkTag.is(':visible') && !(hasOneChild && inlineChild && inlineChild.is(':visible'))
+ )) {
+ // link is not visible
+ tagElement.hide();
+ continue;
+ }
+
+ tagElement.attr('class', 'PIS_LinkTag'); // reset class
+ if (tagElement[0].piwikHighlighted) {
+ tagElement.addClass('PIS_Highlighted');
+ }
+
+ // see comment in highlightLink()
+ if (hasOneChild && linkTag.find('> img').size() == 1) {
+ offset = linkTag.find('> img').offset();
+ if (offset.left == 0 && offset.top == 0) {
+ offset = linkTag.offset();
+ }
+ } else if (inlineChild !== false) {
+ offset = inlineChild.offset();
+ } else {
+ offset = linkTag.offset();
+ }
+
+ top = offset.top - tagHeight + 6;
+ left = offset.left - tagWidth + 10;
+
+ if (isRight = (left < 2)) {
+ tagElement.addClass('PIS_Right');
+ left = offset.left + linkTag.outerWidth() - 10;
+ }
+
+ if (top < 2) {
+ tagElement.addClass(isRight ? 'PIS_BottomRight' : 'PIS_Bottom');
+ top = offset.top + linkTag.outerHeight() - 6;
+ }
+
+ tagElement.css({
+ top: top + 'px',
+ left: left + 'px'
+ }).show();
+
+ }
+ }
+ }
+
+ // walk tagsToRemove from back to front because it contains the indexes in ascending
+ // order. removing something from the front will impact the indexes that come after-
+ // wards. this can be avoided by starting in the back.
+ for (var k = tagsToRemove.length - 1; k >= 0; k--) {
+ var tagToRemove = tagsToRemove[k];
+ linkTag = linksOnPage[tagToRemove.index1][tagToRemove.index2];
+ // remove the tag element from the dom
+ if (linkTag && linkTag[0] && linkTag[0].piwikTagElement) {
+ tagElement = linkTag[0].piwikTagElement;
+ if (tagElement[0].piwikHighlighted) {
+ unHighlightLink(linkTag, tagToRemove.index1);
+ }
+ tagElement.remove();
+ linkTag[0].piwikTagElement = null;
+ }
+ // remove the link from the index
+ linksOnPage[tagToRemove.index1].splice(tagToRemove.index2, 1);
+ if (linksOnPage[tagToRemove.index1].length == 0) {
+ delete linksOnPage[tagToRemove.index1];
+ }
+ }
+
+ if (typeof callback == 'function') {
+ callback();
+ }
+ }
+
+ /** Get the visibility of an element */
+ function getVisibility(el) {
+ var visibility = el.css('visibility');
+ if (visibility == 'inherit') {
+ el = el.parent();
+ if (el.size() > 0) {
+ return getVisibility(el);
+ }
+ }
+ return visibility;
+ }
+
+ /**
+ * Find out whether a link has only one child. Using .children().size() == 1 doesn't work
+ * because it doesn't take additional text nodes into account.
+ */
+ function checkHasOneChild(linkTag) {
+ var hasOneChild = (linkTag.children().size() == 1);
+ if (hasOneChild) {
+ // if the element contains one tag and some text, hasOneChild is set incorrectly
+ var contents = linkTag.contents();
+ if (contents.size() > 1) {
+ // find non-empty text nodes
+ contents = contents.filter(function () {
+ return this.nodeType == 3 && // text node
+ $.trim(this.data).length > 0; // contains more than whitespaces
+ });
+ if (contents.size() > 0) {
+ hasOneChild = false;
+ }
+ }
+ }
+ return hasOneChild;
+ }
+
+ /** Check whether new links have been added to the dom */
+ function findNewLinks() {
+ var newLinks = $('a').filter(function () {
+ return typeof this.piwikDiscovered == 'undefined' || this.piwikDiscovered === null;
+ });
+
+ if (newLinks.size() == 0) {
+ return;
+ }
+
+ processLinkDelta = {};
+ newLinks.each(processLink);
+ createLinkTags(processLinkDelta);
+ processLinkDelta = false;
+ }
+
+ /** Dom elements used for drawing a box around the link */
+ var highlightElements = [];
+
+ /** Highlight a link on hover */
+ function highlightLink(linkTag, linkUrl, data) {
+ if (highlightElements.length == 0) {
+ highlightElements.push(c('div', 'LinkHighlightBoxTop'));
+ highlightElements.push(c('div', 'LinkHighlightBoxRight'));
+ highlightElements.push(c('div', 'LinkHighlightBoxLeft'));
+
+ highlightElements.push(c('div', 'LinkHighlightBoxText'));
+
+ var body = $('body');
+ for (var i = 0; i < highlightElements.length; i++) {
+ body.prepend(highlightElements[i].css({display: 'none'}));
+ }
+ }
+
+ var width = linkTag.outerWidth();
+
+ var offset, height;
+ var hasOneChild = checkHasOneChild(linkTag);
+ if (hasOneChild && linkTag.find('img').size() == 1) {
+ // if the <a> tag contains only an <img>, the offset and height methods don't work properly.
+ // as a result, the box around the image link would be wrong. we use the image to derive
+ // the offset and height instead of the link to get correct values.
+ var img = linkTag.find('img');
+ offset = img.offset();
+ height = img.outerHeight();
+ }
+ if (hasOneChild && linkTag.css('display') != 'block') {
+ // if the <a> tag is not displayed as block and has only one child, using the child to
+ // derive the offset and dimensions is more robust.
+ var child = linkTag.children().eq(0);
+ offset = child.offset();
+ height = child.outerHeight();
+ width = child.outerWidth();
+ } else {
+ offset = linkTag.offset();
+ height = linkTag.outerHeight();
+ }
+
+ highlightElements[0].width(width).css({top: offset.top - 2, left: offset.left}).show();
+ highlightElements[1].height(height + 4).css({top: offset.top - 2, left: offset.left + width}).show();
+ highlightElements[2].height(height + 4).css({top: offset.top - 2, left: offset.left - 2}).show();
+
+ var numLinks = linksOnPage[linkUrl].length;
+ var text;
+ if (numLinks > 1) {
+ text = Piwik_Overlay_Translations.get('clicksFromXLinks')
+ .replace(/%1\$s/, data.referrals)
+ .replace(/%2\$s/, numLinks);
+ } else if (data.referrals == 1) {
+ text = Piwik_Overlay_Translations.get('oneClick');
+ } else {
+ text = Piwik_Overlay_Translations.get('clicks')
+ .replace(/%s/, data.referrals);
+ }
+
+ var padding = '&nbsp;&nbsp;';
+ highlightElements[3].html(padding + text + padding).css({
+ width: 'auto',
+ top: offset.top + height,
+ left: offset.left - 2
+ }).show();
+ if (highlightElements[3].width() < width + 4) {
+ // we cannot use minWidth because of IE7
+ highlightElements[3].width(width + 4);
+ }
+
+ for (var j = 0; j < numLinks; j++) {
+ var tag = linksOnPage[linkUrl][j][0].piwikTagElement;
+ tag.addClass('PIS_Highlighted');
+ tag[0].piwikHighlighted = true;
+ }
+
+ // Sometimes it fails to remove the notification when the hovered element is removed.
+ // To make sure we don't display more than one location at a time, we hide all before showing the new one.
+ Piwik_Overlay_Client.hideNotifications('LinkLocation');
+
+ // we don't use .data() because jquery would remove the callback when the link tag is removed
+ linkTag[0].piwikHideNotification = Piwik_Overlay_Client.notification(
+ Piwik_Overlay_Translations.get('link') + ': ' + linkUrl, 'LinkLocation');
+ }
+
+ /** Remove highlight from link */
+ function unHighlightLink(linkTag, linkUrl) {
+ for (var i = 0; i < highlightElements.length; i++) {
+ highlightElements[i].hide();
+ }
+
+ var numLinks = linksOnPage[linkUrl].length;
+ for (var j = 0; j < numLinks; j++) {
+ var tag = linksOnPage[linkUrl][j][0].piwikTagElement;
+ if (tag) {
+ tag.removeClass('PIS_Highlighted');
+ tag[0].piwikHighlighted = false;
+ }
+ }
+
+ if ((typeof linkTag[0].piwikHideNotification) == 'function') {
+ linkTag[0].piwikHideNotification();
+ linkTag[0].piwikHideNotification = null;
+ }
+ }
+
+
+ return {
+
+ /**
+ * The main method
+ */
+ initialize: function (finishCallback) {
+ c = Piwik_Overlay_Client.createElement;
+ Piwik_Overlay_Client.loadScript('plugins/Overlay/client/urlnormalizer.js', function () {
+ Piwik_Overlay_UrlNormalizer.initialize();
+ load(function () {
+ Piwik_Overlay_UrlNormalizer.setExcludedParameters(excludedParams);
+ build(function () {
+ finishCallback();
+ })
+ });
+ });
+ },
+
+ /**
+ * Remove everything from the dom and terminate timeouts.
+ * This can be used from the console in order to load a new implementation for debugging afterwards.
+ * If you add `Piwik_Overlay_FollowingPages.remove();` to the beginning and
+ * `Piwik_Overlay_FollowingPages.initialize(function(){});` to the end of this file, you can just
+ * paste it into the console to inject the new implementation.
+ */
+ remove: function () {
+ for (var i = 0; i < followingPages.length; i++) {
+ var url = followingPages[i].label;
+ if (typeof linksOnPage[url] != 'undefined') {
+ for (var j = 0; j < linksOnPage[url].length; j++) {
+ var linkTag = linksOnPage[url][j];
+ var tagElement = linkTag[0].piwikTagElement;
+ if (tagElement) {
+ tagElement.remove();
+ }
+ linkTag[0].piwikTagElement = null;
+
+ $(linkTag).unbind('mouseenter').unbind('mouseleave');
+ }
+ }
+ }
+ for (i = 0; i < highlightElements.length; i++) {
+ highlightElements[i].remove();
+ }
+ if (repositionTimeout) {
+ window.clearTimeout(repositionTimeout);
+ }
+ if (resizeTimeout) {
+ window.clearTimeout(resizeTimeout);
+ }
+ $(window).unbind('resize');
+ }
+
+ };
})(); \ No newline at end of file
diff --git a/plugins/Overlay/client/translations.js b/plugins/Overlay/client/translations.js
index 1f9e466e6b..9665b920a9 100644
--- a/plugins/Overlay/client/translations.js
+++ b/plugins/Overlay/client/translations.js
@@ -1,32 +1,30 @@
+var Piwik_Overlay_Translations = (function () {
+ /** Translations strings */
+ var translations = [];
+
+ return {
+
+ /**
+ * Initialize translations module.
+ * Callback is triggered when data is available.
+ */
+ initialize: function (callback) {
+ // Load translation data
+ Piwik_Overlay_Client.api('getTranslations', function (data) {
+ translations = data[0];
+ callback();
+ });
+ },
+
+ /** Get translation string */
+ get: function (identifier) {
+ if (typeof translations[identifier] == 'undefined') {
+ return identifier;
+ }
+ return translations[identifier];
+ }
+
+ };
-var Piwik_Overlay_Translations = (function() {
-
- /** Translations strings */
- var translations = [];
-
- return {
-
- /**
- * Initialize translations module.
- * Callback is triggered when data is available.
- */
- initialize: function(callback) {
- // Load translation data
- Piwik_Overlay_Client.api('getTranslations', function(data) {
- translations = data[0];
- callback();
- });
- },
-
- /** Get translation string */
- get: function(identifier) {
- if (typeof translations[identifier] == 'undefined') {
- return identifier;
- }
- return translations[identifier];
- }
-
- };
-
})();
diff --git a/plugins/Overlay/client/urlnormalizer.js b/plugins/Overlay/client/urlnormalizer.js
index 38423bab60..72db61269b 100644
--- a/plugins/Overlay/client/urlnormalizer.js
+++ b/plugins/Overlay/client/urlnormalizer.js
@@ -1,199 +1,198 @@
-
/**
* URL NORMALIZER
* This utility preprocesses both the URLs in the document and
* from the Piwik logs in order to make matching possible.
*/
-var Piwik_Overlay_UrlNormalizer = (function() {
-
- /** Base href of the current document */
- var baseHref = false;
-
- /** Url of current folder */
- var currentFolder;
-
- /** The current domain */
- var currentDomain;
-
- /** Regular expressions for parameters to be excluded when matching links on the page */
- var excludedParamsRegEx = [];
-
- /**
- * Basic normalizations for domain names
- * - remove protocol and www from absolute urls
- * - add a trailing slash to urls without a path
- *
- * Returns array
- * 0: normalized url
- * 1: true, if url was absolute (if not, no normalization was performed)
- */
- function normalizeDomain(url) {
- if (url === null) {
- return '';
- }
-
- var absolute = false;
-
- // remove protocol
- if (url.substring(0, 7) == 'http://') {
- absolute = true;
- url = url.substring(7, url.length);
- } else if (url.substring(0, 8) == 'https://') {
- absolute = true;
- url = url.substring(8, url.length);
- }
-
- if (absolute) {
- // remove www.
- url = removeWww(url);
-
- // add slash to domain names
- if (url.indexOf('/') == -1) {
- url += '/';
- }
- }
-
- return [url, absolute];
- }
-
- /** Remove www. from a domain */
- function removeWww(domain) {
- if (domain.substring(0, 4) == 'www.') {
- return domain.substring(4, domain.length);
- }
- return domain;
- }
-
- return {
-
- initialize: function() {
- this.setCurrentDomain(document.location.hostname);
- this.setCurrentUrl(window.location.href);
-
- var head = document.getElementsByTagName('head');
- if (head.length) {
- var base = head[0].getElementsByTagName('base');
- if (base.length && base[0].href) {
- this.setBaseHref(base[0].href);
- }
- }
- },
-
- /**
- * Explicitly set domain (for testing)
- */
- setCurrentDomain: function(pCurrentDomain) {
- currentDomain = removeWww(pCurrentDomain);
- },
-
- /**
- * Explicitly set current url (for testing)
- */
- setCurrentUrl: function(url) {
- var index = url.lastIndexOf('/');
- if (index != url.length - 1) {
- currentFolder = url.substring(0, index + 1);
- } else {
- currentFolder = url;
- }
- currentFolder = normalizeDomain(currentFolder)[0];
- },
-
- /**
- * Explicitly set base href (for testing)
- */
- setBaseHref: function(pBaseHref) {
- if (!pBaseHref) {
- baseHref = false;
- } else {
- baseHref = normalizeDomain(pBaseHref)[0];
- }
- },
-
- /**
- * Set the parameters to be excluded when matching links on the page
- */
- setExcludedParameters: function(pExcludedParams) {
- excludedParamsRegEx = [];
- for (var i = 0; i < pExcludedParams.length; i++) {
- var paramString = pExcludedParams[i];
- excludedParamsRegEx.push(new RegExp('&' + paramString + '=([^&#]*)', 'ig'));
- }
- },
-
- /**
- * Remove the protocol and the prefix of a URL
- */
- removeUrlPrefix: function(url) {
- return normalizeDomain(url)[0];
- },
-
- /**
- * Normalize URL
- * Can be an absolute or a relative URL
- */
- normalize: function(url) {
- if (!url) {
- return '';
- }
-
- // ignore urls starting with #
- if (url.substring(0, 1) == '#') {
- return '';
- }
-
- // basic normalizations for absolute urls
- var normalized = normalizeDomain(url);
- url = normalized[0];
-
- var absolute = normalized[1];
-
- if (!absolute) {
- /** relative url */
- if (url.substring(0, 1) == '/') {
- // relative to domain root
- url = currentDomain + url;
- } else if (baseHref) {
- // relative to base href
- url = baseHref + url;
- } else {
- // relative to current folder
- url = currentFolder + url;
- }
- }
-
- // replace multiple / with a single /
- url = url.replace(/\/\/+/g, '/');
-
- // handle ./ and ../
- var parts = url.split('/');
- var urlArr = [];
- for (var i = 0; i < parts.length; i++) {
- if (parts[i] == '.') {
- // ignore
- }
- else if (parts[i] == '..') {
- urlArr.pop();
- }
- else {
- urlArr.push(parts[i]);
- }
- }
- url = urlArr.join('/');
-
- // remove ignored parameters
- url = url.replace(/\?/, '?&');
- for (i = 0; i < excludedParamsRegEx.length; i++) {
- var regEx = excludedParamsRegEx[i];
- url = url.replace(regEx, '');
- }
- url = url.replace(/\?&/, '?');
- url = url.replace(/\?#/, '#');
- url = url.replace(/\?$/, '');
-
- return url;
- }
-
- };
-
+var Piwik_Overlay_UrlNormalizer = (function () {
+
+ /** Base href of the current document */
+ var baseHref = false;
+
+ /** Url of current folder */
+ var currentFolder;
+
+ /** The current domain */
+ var currentDomain;
+
+ /** Regular expressions for parameters to be excluded when matching links on the page */
+ var excludedParamsRegEx = [];
+
+ /**
+ * Basic normalizations for domain names
+ * - remove protocol and www from absolute urls
+ * - add a trailing slash to urls without a path
+ *
+ * Returns array
+ * 0: normalized url
+ * 1: true, if url was absolute (if not, no normalization was performed)
+ */
+ function normalizeDomain(url) {
+ if (url === null) {
+ return '';
+ }
+
+ var absolute = false;
+
+ // remove protocol
+ if (url.substring(0, 7) == 'http://') {
+ absolute = true;
+ url = url.substring(7, url.length);
+ } else if (url.substring(0, 8) == 'https://') {
+ absolute = true;
+ url = url.substring(8, url.length);
+ }
+
+ if (absolute) {
+ // remove www.
+ url = removeWww(url);
+
+ // add slash to domain names
+ if (url.indexOf('/') == -1) {
+ url += '/';
+ }
+ }
+
+ return [url, absolute];
+ }
+
+ /** Remove www. from a domain */
+ function removeWww(domain) {
+ if (domain.substring(0, 4) == 'www.') {
+ return domain.substring(4, domain.length);
+ }
+ return domain;
+ }
+
+ return {
+
+ initialize: function () {
+ this.setCurrentDomain(document.location.hostname);
+ this.setCurrentUrl(window.location.href);
+
+ var head = document.getElementsByTagName('head');
+ if (head.length) {
+ var base = head[0].getElementsByTagName('base');
+ if (base.length && base[0].href) {
+ this.setBaseHref(base[0].href);
+ }
+ }
+ },
+
+ /**
+ * Explicitly set domain (for testing)
+ */
+ setCurrentDomain: function (pCurrentDomain) {
+ currentDomain = removeWww(pCurrentDomain);
+ },
+
+ /**
+ * Explicitly set current url (for testing)
+ */
+ setCurrentUrl: function (url) {
+ var index = url.lastIndexOf('/');
+ if (index != url.length - 1) {
+ currentFolder = url.substring(0, index + 1);
+ } else {
+ currentFolder = url;
+ }
+ currentFolder = normalizeDomain(currentFolder)[0];
+ },
+
+ /**
+ * Explicitly set base href (for testing)
+ */
+ setBaseHref: function (pBaseHref) {
+ if (!pBaseHref) {
+ baseHref = false;
+ } else {
+ baseHref = normalizeDomain(pBaseHref)[0];
+ }
+ },
+
+ /**
+ * Set the parameters to be excluded when matching links on the page
+ */
+ setExcludedParameters: function (pExcludedParams) {
+ excludedParamsRegEx = [];
+ for (var i = 0; i < pExcludedParams.length; i++) {
+ var paramString = pExcludedParams[i];
+ excludedParamsRegEx.push(new RegExp('&' + paramString + '=([^&#]*)', 'ig'));
+ }
+ },
+
+ /**
+ * Remove the protocol and the prefix of a URL
+ */
+ removeUrlPrefix: function (url) {
+ return normalizeDomain(url)[0];
+ },
+
+ /**
+ * Normalize URL
+ * Can be an absolute or a relative URL
+ */
+ normalize: function (url) {
+ if (!url) {
+ return '';
+ }
+
+ // ignore urls starting with #
+ if (url.substring(0, 1) == '#') {
+ return '';
+ }
+
+ // basic normalizations for absolute urls
+ var normalized = normalizeDomain(url);
+ url = normalized[0];
+
+ var absolute = normalized[1];
+
+ if (!absolute) {
+ /** relative url */
+ if (url.substring(0, 1) == '/') {
+ // relative to domain root
+ url = currentDomain + url;
+ } else if (baseHref) {
+ // relative to base href
+ url = baseHref + url;
+ } else {
+ // relative to current folder
+ url = currentFolder + url;
+ }
+ }
+
+ // replace multiple / with a single /
+ url = url.replace(/\/\/+/g, '/');
+
+ // handle ./ and ../
+ var parts = url.split('/');
+ var urlArr = [];
+ for (var i = 0; i < parts.length; i++) {
+ if (parts[i] == '.') {
+ // ignore
+ }
+ else if (parts[i] == '..') {
+ urlArr.pop();
+ }
+ else {
+ urlArr.push(parts[i]);
+ }
+ }
+ url = urlArr.join('/');
+
+ // remove ignored parameters
+ url = url.replace(/\?/, '?&');
+ for (i = 0; i < excludedParamsRegEx.length; i++) {
+ var regEx = excludedParamsRegEx[i];
+ url = url.replace(regEx, '');
+ }
+ url = url.replace(/\?&/, '?');
+ url = url.replace(/\?#/, '#');
+ url = url.replace(/\?$/, '');
+
+ return url;
+ }
+
+ };
+
})(); \ No newline at end of file
diff --git a/plugins/Overlay/templates/error_wrong_domain.tpl b/plugins/Overlay/templates/error_wrong_domain.tpl
index 49bd01ed20..4cb400ecce 100644
--- a/plugins/Overlay/templates/error_wrong_domain.tpl
+++ b/plugins/Overlay/templates/error_wrong_domain.tpl
@@ -1,36 +1,38 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
- <title></title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <meta name="generator" content="Piwik - Open Source Web Analytics" />
- <link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico" />
-
- <style type="text/css">
- {literal}
- body {
- font-family: Arial, Helvetica, sans-serif;
- font-size: 14px;
- color: black;
- background: white;
- margin: 15px;
- padding: 0;
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <meta name="generator" content="Piwik - Open Source Web Analytics"/>
+ <link rel="shortcut icon" href="plugins/CoreHome/templates/images/favicon.ico"/>
+
+ <style type="text/css">
+ {literal}
+ body {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 14px;
+ color: black;
+ background: white;
+ margin: 15px;
+ padding: 0;
}
-
- p {
- margin: 0 0 10px 0;
- padding: 0;
+
+ p {
+ margin: 0 0 10px 0;
+ padding: 0;
}
-
- a {
- color: #255792;
+
+ a {
+ color: #255792;
}
- {/literal}
- </style>
-
+
+ {/literal}
+ </style>
+
</head>
<body>
- <p><b>{$message}</b></p>
- <p>{$troubleshoot}</p>
+<p><b>{$message}</b></p>
+
+<p>{$troubleshoot}</p>
</body>
</html> \ No newline at end of file
diff --git a/plugins/Overlay/templates/helper.js b/plugins/Overlay/templates/helper.js
index ee62cd60aa..1342bf79f0 100644
--- a/plugins/Overlay/templates/helper.js
+++ b/plugins/Overlay/templates/helper.js
@@ -6,26 +6,26 @@
*/
var Overlay_Helper = {
-
- /** Encode the iframe url to put it behind the hash in sidebar mode */
- encodeFrameUrl: function(url) {
- // url encode + replace % with $ to make sure that browsers don't break the encoding
- return encodeURIComponent(url).replace(/%/g, '$')
- },
-
- /** Decode the url after reading it from the hash */
- decodeFrameUrl: function(url) {
- // reverse encodeFrameUrl()
- return decodeURIComponent(url.replace(/\$/g, '%'));
- },
-
- /** Get the url to launch overlay */
- getOverlayLink: function(idSite, period, date, link) {
- var url = 'index.php?module=Overlay&period=' + period + '&date=' + date + '&idSite=' + idSite;
- if (link) {
- url += '#l=' + Overlay_Helper.encodeFrameUrl(link);
- }
- return url;
- }
-
+
+ /** Encode the iframe url to put it behind the hash in sidebar mode */
+ encodeFrameUrl: function (url) {
+ // url encode + replace % with $ to make sure that browsers don't break the encoding
+ return encodeURIComponent(url).replace(/%/g, '$')
+ },
+
+ /** Decode the url after reading it from the hash */
+ decodeFrameUrl: function (url) {
+ // reverse encodeFrameUrl()
+ return decodeURIComponent(url.replace(/\$/g, '%'));
+ },
+
+ /** Get the url to launch overlay */
+ getOverlayLink: function (idSite, period, date, link) {
+ var url = 'index.php?module=Overlay&period=' + period + '&date=' + date + '&idSite=' + idSite;
+ if (link) {
+ url += '#l=' + Overlay_Helper.encodeFrameUrl(link);
+ }
+ return url;
+ }
+
}; \ No newline at end of file
diff --git a/plugins/Overlay/templates/index.css b/plugins/Overlay/templates/index.css
index 154546eeeb..f48a94ec35 100644
--- a/plugins/Overlay/templates/index.css
+++ b/plugins/Overlay/templates/index.css
@@ -1,144 +1,143 @@
-
html {
- width: 100%;
- height: 100%;
- /** hide scroll bar in ie7 */
- *overflow: auto;
+ width: 100%;
+ height: 100%;
+ /** hide scroll bar in ie7 */
+ *overflow: auto;
}
body {
- width: 100%;
- height: 100%;
- overflow: hidden;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
}
body #header {
- margin-left: -12px;
+ margin-left: -12px;
}
body #logo {
- margin-top: 5px;
+ margin-top: 5px;
}
body h1 {
- font-size: 15px;
- font-weight: normal;
- margin: -3px 0 0 0;
- padding: 0 0 0 5px;
+ font-size: 15px;
+ font-weight: normal;
+ margin: -3px 0 0 0;
+ padding: 0 0 0 5px;
}
#Overlay_DateRangeSelection {
- padding: 30px 0 20px 23px;
- background: url(../../../themes/default/images/icon-calendar.gif) 2px 33px no-repeat;
- margin-left: 5px;
+ padding: 30px 0 20px 23px;
+ background: url(../../../themes/default/images/icon-calendar.gif) 2px 33px no-repeat;
+ margin-left: 5px;
}
#Overlay_Location {
- width: 200px;
- padding: 0 0 30px 0;
- margin-left: 5px;
- font-size: 12px;
+ width: 200px;
+ padding: 0 0 30px 0;
+ margin-left: 5px;
+ font-size: 12px;
}
#Overlay_Loading {
- background: url(../../../themes/default/images/loading-blue.gif) no-repeat center 10px;
- width: 190px;
- padding-top: 30px;
- margin-top: 30px;
- text-align: center;
- display: none;
+ background: url(../../../themes/default/images/loading-blue.gif) no-repeat center 10px;
+ width: 190px;
+ padding-top: 30px;
+ margin-top: 30px;
+ text-align: center;
+ display: none;
}
#Overlay_Sidebar {
- width: 200px;
- margin-left: 5px;
+ width: 200px;
+ margin-left: 5px;
}
#Overlay_Sidebar h2 {
- font-size: 15px;
- margin: 0;
- padding: 0 0 8px 23px;
- color: #255792;
+ font-size: 15px;
+ margin: 0;
+ padding: 0 0 8px 23px;
+ color: #255792;
}
a#Overlay_FullScreen,
a#Overlay_RowEvolution,
a#Overlay_Transitions {
- display: block;
- color: #255792;
- font-size: 13px;
- line-height: 15px;
- margin: 0 0 0 5px;
- padding: 8px 0 3px 25px;
- text-decoration: none;
+ display: block;
+ color: #255792;
+ font-size: 13px;
+ line-height: 15px;
+ margin: 0 0 0 5px;
+ padding: 8px 0 3px 25px;
+ text-decoration: none;
}
a#Overlay_RowEvolution {
- margin-top: 20px;
- background: url(../../../themes/default/images/row_evolution_hover.png) no-repeat 0 7px;
+ margin-top: 20px;
+ background: url(../../../themes/default/images/row_evolution_hover.png) no-repeat 0 7px;
}
a#Overlay_Transitions {
- background: url(../../Transitions/templates/transitions_icon_hover.png) no-repeat 0 6px;
+ background: url(../../Transitions/templates/transitions_icon_hover.png) no-repeat 0 6px;
}
a#Overlay_FullScreen {
- margin-top: 20px;
- background: url(../../../themes/default/images/fullscreen.png) no-repeat 3px 8px;
+ margin-top: 20px;
+ background: url(../../../themes/default/images/fullscreen.png) no-repeat 3px 8px;
}
a#Overlay_FullScreen:hover,
a#Overlay_RowEvolution:hover,
a#Overlay_Transitions:hover {
- color: #E87500;
+ color: #E87500;
}
#Overlay_Main {
- margin-left: 220px;
- position: absolute;
- top: 0;
+ margin-left: 220px;
+ position: absolute;
+ top: 0;
}
#Overlay_Iframe {
- border-left: 2px solid #ddd;
+ border-left: 2px solid #ddd;
}
.Overlay_Metric {
- font-size: 12px;
- padding-bottom: 4px;
+ font-size: 12px;
+ padding-bottom: 4px;
}
.Overlay_MetricValue {
- font-size: 14px;
- font-weight: bold;
+ font-size: 14px;
+ font-weight: bold;
}
.Overlay_NoData {
- font-size: 12px;
- color: #7E7363;
+ font-size: 12px;
+ color: #7E7363;
}
h2.Overlay_MainMetrics {
- background: url(./info.png) 0 1px no-repeat
+ background: url(./info.png) 0 1px no-repeat
}
body .piwik-tooltip.Overlay_Tooltip {
- font-size: 11px;
- padding: 3px 5px 3px 6px;
+ font-size: 11px;
+ padding: 3px 5px 3px 6px;
}
#Overlay_NoFrame {
- padding: 20px 0 40px 2px
+ padding: 20px 0 40px 2px
}
#Overlay_Error_NotLoading {
- width: 190px;
- display: none;
- margin: 20px 0 0 5px;
- font-size: 14px;
+ width: 190px;
+ display: none;
+ margin: 20px 0 0 5px;
+ font-size: 14px;
}
#Overlay_Error_NotLoading span {
- color: #E87500;
- font-weight: bold;
+ color: #E87500;
+ font-weight: bold;
} \ No newline at end of file
diff --git a/plugins/Overlay/templates/index.js b/plugins/Overlay/templates/index.js
index 62ff3bb513..6ff4ee8ed5 100644
--- a/plugins/Overlay/templates/index.js
+++ b/plugins/Overlay/templates/index.js
@@ -5,37 +5,37 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-var Piwik_Overlay = (function() {
+var Piwik_Overlay = (function () {
- var $body, $iframe, $sidebar, $main, $location, $loading, $errorNotLoading;
- var $rowEvolutionLink, $transitionsLink, $fullScreenLink;
+ var $body, $iframe, $sidebar, $main, $location, $loading, $errorNotLoading;
+ var $rowEvolutionLink, $transitionsLink, $fullScreenLink;
- var idSite, period, date;
+ var idSite, period, date;
- var errorTimeout = false;
+ var errorTimeout = false;
- var iframeSrcBase;
- var iframeDomain = '';
- var iframeCurrentPage = '';
- var iframeCurrentPageNormalized = '';
- var iframeCurrentActionLabel = '';
- var updateComesFromInsideFrame = false;
+ var iframeSrcBase;
+ var iframeDomain = '';
+ var iframeCurrentPage = '';
+ var iframeCurrentPageNormalized = '';
+ var iframeCurrentActionLabel = '';
+ var updateComesFromInsideFrame = false;
- /** Load the sidebar for a url */
- function loadSidebar(currentUrl) {
- showLoading();
+ /** Load the sidebar for a url */
+ function loadSidebar(currentUrl) {
+ showLoading();
- $location.html('&nbsp;').unbind('mouseenter').unbind('mouseleave');
+ $location.html('&nbsp;').unbind('mouseenter').unbind('mouseleave');
- iframeCurrentPage = currentUrl;
- iframeDomain = currentUrl.match(/http(s)?:\/\/(www\.)?([^\/]*)/i)[3];
+ iframeCurrentPage = currentUrl;
+ iframeDomain = currentUrl.match(/http(s)?:\/\/(www\.)?([^\/]*)/i)[3];
globalAjaxQueue.abort();
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
- module: 'Overlay',
- action: 'renderSidebar',
+ module: 'Overlay',
+ action: 'renderSidebar',
currentUrl: currentUrl
}, 'get');
ajaxRequest.setCallback(
@@ -52,9 +52,9 @@ var Piwik_Overlay = (function() {
$location.html($responseLocation.html()).show();
$responseLocation.remove();
- var $locationSpan = $location.find('span');
- $locationSpan.html(piwikHelper.addBreakpointsToUrl($locationSpan.text()));
- $locationSpan.hover(function () {
+ var $locationSpan = $location.find('span');
+ $locationSpan.html(piwikHelper.addBreakpointsToUrl($locationSpan.text()));
+ $locationSpan.hover(function () {
if (iframeDomain) {
// use addBreakpointsToUrl because it also encoded html entities
Piwik_Tooltip.show('<b>' + Piwik_Overlay_Translations.domain + ':</b> ' +
@@ -74,187 +74,187 @@ var Piwik_Overlay = (function() {
);
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
- }
-
- /** Adjust the dimensions of the iframe */
- function adjustDimensions() {
- $iframe.height($(window).height());
- $iframe.width($body.width() - $iframe.offset().left - 2); // -2 because of 2px border
- }
-
- /** Display the loading message and hide other containers */
- function showLoading() {
- $loading.show();
-
- $sidebar.hide();
- $location.hide();
-
- $fullScreenLink.hide();
- $rowEvolutionLink.hide();
- $transitionsLink.hide();
-
- $errorNotLoading.hide();
-
- // Start a timeout that shows an error when nothing is loaded
- if (errorTimeout) {
- window.clearTimeout(errorTimeout);
- }
- errorTimeout = window.setTimeout(function() {
- hideLoading();
- $errorNotLoading.show();
- }, 9000);
- }
-
- /** Hide the loading message */
- function hideLoading() {
- if (errorTimeout) {
- window.clearTimeout(errorTimeout);
- errorTimeout = false;
- }
- $loading.hide();
- $fullScreenLink.show();
- }
-
- /** $.history callback for hash change */
- function hashChangeCallback(urlHash) {
- var location = broadcast.getParamValue('l', urlHash);
- location = Overlay_Helper.decodeFrameUrl(location);
-
- if (!updateComesFromInsideFrame) {
- var iframeUrl = iframeSrcBase;
- if (location) {
- iframeUrl += '#' + location;
- }
- $iframe.attr('src', iframeUrl);
- showLoading();
- } else {
- loadSidebar(location);
- }
-
- updateComesFromInsideFrame = false;
- }
-
- return {
-
- /** This method is called when Overlay loads (from index.tpl) */
- init: function(iframeSrc, pIdSite, pPeriod, pDate) {
- iframeSrcBase = iframeSrc;
- idSite = pIdSite;
- period = pPeriod;
- date = pDate;
-
- $body = $('body');
- $iframe = $('#Overlay_Iframe');
- $sidebar = $('#Overlay_Sidebar');
- $location = $('#Overlay_Location');
- $main = $('#Overlay_Main');
- $loading = $('#Overlay_Loading');
- $errorNotLoading = $('#Overlay_Error_NotLoading');
-
- $rowEvolutionLink = $('#Overlay_RowEvolution');
- $transitionsLink = $('#Overlay_Transitions');
- $fullScreenLink = $('#Overlay_FullScreen');
-
- adjustDimensions();
-
- showLoading();
-
- // apply initial dimensions
- window.setTimeout(function() {
- adjustDimensions();
- }, 50);
-
- // handle window resize
- // we manipulate broadcast.pageload because it unbinds all resize events on window
- var originalPageload = broadcast.pageload;
- broadcast.pageload = function(hash) {
- originalPageload(hash);
- $(window).resize(function() {
- adjustDimensions();
- });
- };
- $(window).resize(function() {
- adjustDimensions();
- });
-
- // handle hash change
- broadcast.loadAjaxContent = hashChangeCallback;
- broadcast.init();
-
- if (window.location.href.split('#').length == 1) {
- // if there's no hash, broadcast won't trigger the callback - we have to do it here
- hashChangeCallback('');
- }
-
- // handle date selection
- var $select = $('select#Overlay_DateRangeSelect').change(function() {
- var parts = $(this).val().split(';');
- if (parts.length == 2) {
- period = parts[0];
- date = parts[1];
- window.location.href = Overlay_Helper.getOverlayLink(idSite, period, date, iframeCurrentPage);
- }
- });
-
- var optionMatchFound = false;
- $select.find('option').each(function() {
- if ($(this).val() == period + ';' + date) {
- $(this).attr('selected', 'selected');
- optionMatchFound = true;
- }
- });
-
- if (!optionMatchFound) {
- $select.prepend('<option selected="selected">');
- }
-
- // handle transitions link
- $transitionsLink.click(function() {
- DataTable_RowActions_Transitions.launchForUrl(iframeCurrentPageNormalized);
- return false;
- });
-
- // handle row evolution link
- $rowEvolutionLink.click(function() {
- DataTable_RowActions_RowEvolution.launch('Actions.getPageUrls', iframeCurrentActionLabel);
- return false;
- });
-
- // handle full screen link
- $fullScreenLink.click(function() {
- var href = iframeSrcBase;
- if (iframeCurrentPage) {
- href += '#' + iframeCurrentPage.replace(/#/g, '%23');
- }
- window.location.href = href;
- return false;
- });
- },
-
- /** This callback is used from within the iframe */
- setCurrentUrl: function(currentUrl) {
- showLoading();
-
- var locationParts = location.href.split('#');
- var currentLocation = '';
- if (locationParts.length > 1) {
- currentLocation = broadcast.getParamValue('l', locationParts[1]);
- }
-
- var newLocation = Overlay_Helper.encodeFrameUrl(currentUrl);
-
- if (newLocation != currentLocation) {
- updateComesFromInsideFrame = true;
- // put the current iframe url in the main url to enable refresh and deep linking.
- // use disableHistory=true to make sure that the back and forward buttons can be
- // used on the iframe (which in turn notifies the parent about the location change)
- broadcast.propagateAjax('l=' + newLocation, true);
- } else {
- // happens when the url is changed by hand or when the l parameter is there on page load
- loadSidebar(currentUrl);
- }
- }
-
- };
+ }
+
+ /** Adjust the dimensions of the iframe */
+ function adjustDimensions() {
+ $iframe.height($(window).height());
+ $iframe.width($body.width() - $iframe.offset().left - 2); // -2 because of 2px border
+ }
+
+ /** Display the loading message and hide other containers */
+ function showLoading() {
+ $loading.show();
+
+ $sidebar.hide();
+ $location.hide();
+
+ $fullScreenLink.hide();
+ $rowEvolutionLink.hide();
+ $transitionsLink.hide();
+
+ $errorNotLoading.hide();
+
+ // Start a timeout that shows an error when nothing is loaded
+ if (errorTimeout) {
+ window.clearTimeout(errorTimeout);
+ }
+ errorTimeout = window.setTimeout(function () {
+ hideLoading();
+ $errorNotLoading.show();
+ }, 9000);
+ }
+
+ /** Hide the loading message */
+ function hideLoading() {
+ if (errorTimeout) {
+ window.clearTimeout(errorTimeout);
+ errorTimeout = false;
+ }
+ $loading.hide();
+ $fullScreenLink.show();
+ }
+
+ /** $.history callback for hash change */
+ function hashChangeCallback(urlHash) {
+ var location = broadcast.getParamValue('l', urlHash);
+ location = Overlay_Helper.decodeFrameUrl(location);
+
+ if (!updateComesFromInsideFrame) {
+ var iframeUrl = iframeSrcBase;
+ if (location) {
+ iframeUrl += '#' + location;
+ }
+ $iframe.attr('src', iframeUrl);
+ showLoading();
+ } else {
+ loadSidebar(location);
+ }
+
+ updateComesFromInsideFrame = false;
+ }
+
+ return {
+
+ /** This method is called when Overlay loads (from index.tpl) */
+ init: function (iframeSrc, pIdSite, pPeriod, pDate) {
+ iframeSrcBase = iframeSrc;
+ idSite = pIdSite;
+ period = pPeriod;
+ date = pDate;
+
+ $body = $('body');
+ $iframe = $('#Overlay_Iframe');
+ $sidebar = $('#Overlay_Sidebar');
+ $location = $('#Overlay_Location');
+ $main = $('#Overlay_Main');
+ $loading = $('#Overlay_Loading');
+ $errorNotLoading = $('#Overlay_Error_NotLoading');
+
+ $rowEvolutionLink = $('#Overlay_RowEvolution');
+ $transitionsLink = $('#Overlay_Transitions');
+ $fullScreenLink = $('#Overlay_FullScreen');
+
+ adjustDimensions();
+
+ showLoading();
+
+ // apply initial dimensions
+ window.setTimeout(function () {
+ adjustDimensions();
+ }, 50);
+
+ // handle window resize
+ // we manipulate broadcast.pageload because it unbinds all resize events on window
+ var originalPageload = broadcast.pageload;
+ broadcast.pageload = function (hash) {
+ originalPageload(hash);
+ $(window).resize(function () {
+ adjustDimensions();
+ });
+ };
+ $(window).resize(function () {
+ adjustDimensions();
+ });
+
+ // handle hash change
+ broadcast.loadAjaxContent = hashChangeCallback;
+ broadcast.init();
+
+ if (window.location.href.split('#').length == 1) {
+ // if there's no hash, broadcast won't trigger the callback - we have to do it here
+ hashChangeCallback('');
+ }
+
+ // handle date selection
+ var $select = $('select#Overlay_DateRangeSelect').change(function () {
+ var parts = $(this).val().split(';');
+ if (parts.length == 2) {
+ period = parts[0];
+ date = parts[1];
+ window.location.href = Overlay_Helper.getOverlayLink(idSite, period, date, iframeCurrentPage);
+ }
+ });
+
+ var optionMatchFound = false;
+ $select.find('option').each(function () {
+ if ($(this).val() == period + ';' + date) {
+ $(this).attr('selected', 'selected');
+ optionMatchFound = true;
+ }
+ });
+
+ if (!optionMatchFound) {
+ $select.prepend('<option selected="selected">');
+ }
+
+ // handle transitions link
+ $transitionsLink.click(function () {
+ DataTable_RowActions_Transitions.launchForUrl(iframeCurrentPageNormalized);
+ return false;
+ });
+
+ // handle row evolution link
+ $rowEvolutionLink.click(function () {
+ DataTable_RowActions_RowEvolution.launch('Actions.getPageUrls', iframeCurrentActionLabel);
+ return false;
+ });
+
+ // handle full screen link
+ $fullScreenLink.click(function () {
+ var href = iframeSrcBase;
+ if (iframeCurrentPage) {
+ href += '#' + iframeCurrentPage.replace(/#/g, '%23');
+ }
+ window.location.href = href;
+ return false;
+ });
+ },
+
+ /** This callback is used from within the iframe */
+ setCurrentUrl: function (currentUrl) {
+ showLoading();
+
+ var locationParts = location.href.split('#');
+ var currentLocation = '';
+ if (locationParts.length > 1) {
+ currentLocation = broadcast.getParamValue('l', locationParts[1]);
+ }
+
+ var newLocation = Overlay_Helper.encodeFrameUrl(currentUrl);
+
+ if (newLocation != currentLocation) {
+ updateComesFromInsideFrame = true;
+ // put the current iframe url in the main url to enable refresh and deep linking.
+ // use disableHistory=true to make sure that the back and forward buttons can be
+ // used on the iframe (which in turn notifies the parent about the location change)
+ broadcast.propagateAjax('l=' + newLocation, true);
+ } else {
+ // happens when the url is changed by hand or when the l parameter is there on page load
+ loadSidebar(currentUrl);
+ }
+ }
+
+ };
})(); \ No newline at end of file
diff --git a/plugins/Overlay/templates/index.tpl b/plugins/Overlay/templates/index.tpl
index d2fe954194..e858aefa29 100644
--- a/plugins/Overlay/templates/index.tpl
+++ b/plugins/Overlay/templates/index.tpl
@@ -2,31 +2,33 @@
<h1>{'Overlay_Overlay'|translate|escape:'html'}</h1>
<div id="Overlay_DateRangeSelection">
- <select id="Overlay_DateRangeSelect" name="Overlay_DateRangeSelect">
- <option value="day;today">{'General_Today'|translate|escape:'html'}</option>
- <option value="day;yesterday">{'General_Yesterday'|translate|escape:'html'}</option>
- <option value="week;today">{'General_CurrentWeek'|translate|escape:'html'}</option>
- <option value="month;today">{'General_CurrentMonth'|translate|escape:'html'}</option>
- <option value="year;today">{'General_CurrentYear'|translate|escape:'html'}</option>
- </select>
+ <select id="Overlay_DateRangeSelect" name="Overlay_DateRangeSelect">
+ <option value="day;today">{'General_Today'|translate|escape:'html'}</option>
+ <option value="day;yesterday">{'General_Yesterday'|translate|escape:'html'}</option>
+ <option value="week;today">{'General_CurrentWeek'|translate|escape:'html'}</option>
+ <option value="month;today">{'General_CurrentMonth'|translate|escape:'html'}</option>
+ <option value="year;today">{'General_CurrentYear'|translate|escape:'html'}</option>
+ </select>
</div>
<div id="Overlay_Error_NotLoading">
- <p>
- <span>{'Overlay_ErrorNotLoading'|translate|escape:'html'}</span>
- </p>
- <p>
- {if $ssl}
- {'Overlay_ErrorNotLoadingDetailsSSL'|translate|escape:'html'}
- {else}
- {'Overlay_ErrorNotLoadingDetails'|translate|escape:'html'}
- {/if}
- </p>
- <p>
- <a href="http://piwik.org/docs/page-overlay/#toc-page-overlay-troubleshooting" target="_blank">
- {'Overlay_ErrorNotLoadingLink'|translate|escape:'html'}
- </a>
- </p>
+ <p>
+ <span>{'Overlay_ErrorNotLoading'|translate|escape:'html'}</span>
+ </p>
+
+ <p>
+ {if $ssl}
+ {'Overlay_ErrorNotLoadingDetailsSSL'|translate|escape:'html'}
+ {else}
+ {'Overlay_ErrorNotLoadingDetails'|translate|escape:'html'}
+ {/if}
+ </p>
+
+ <p>
+ <a href="http://piwik.org/docs/page-overlay/#toc-page-overlay-troubleshooting" target="_blank">
+ {'Overlay_ErrorNotLoadingLink'|translate|escape:'html'}
+ </a>
+ </p>
</div>
<div id="Overlay_Location">&nbsp;</div>
@@ -45,17 +47,17 @@
<div id="Overlay_Main">
- <iframe id="Overlay_Iframe" src="" frameborder="0"></iframe>
+ <iframe id="Overlay_Iframe" src="" frameborder="0"></iframe>
</div>
<script type="text/javascript">
- var iframeSrc = 'index.php?module=Overlay&action=startOverlaySession&idsite={$idSite}&period={$period}&date={$date}';
- Piwik_Overlay.init(iframeSrc, '{$idSite}', '{$period}', '{$date}');
-
- Piwik_Overlay_Translations = {literal}{{/literal}
- domain: "{'Overlay_Domain'|translate|escape:'html'}"
- {literal}}{/literal};
+ var iframeSrc = 'index.php?module=Overlay&action=startOverlaySession&idsite={$idSite}&period={$period}&date={$date}';
+ Piwik_Overlay.init(iframeSrc, '{$idSite}', '{$period}', '{$date}');
+
+ Piwik_Overlay_Translations = {literal}{{/literal}
+ domain: "{'Overlay_Domain'|translate|escape:'html'}"
+ {literal}}{/literal};
</script>
diff --git a/plugins/Overlay/templates/index_noframe.tpl b/plugins/Overlay/templates/index_noframe.tpl
index 0897f7cd9c..af2d08ab3d 100644
--- a/plugins/Overlay/templates/index_noframe.tpl
+++ b/plugins/Overlay/templates/index_noframe.tpl
@@ -2,23 +2,23 @@
<h1>{'Overlay_Overlay'|translate|escape:'html'}</h1>
<div id="Overlay_NoFrame">
-
- <script type="text/javascript">
- var newLocation = 'index.php?module=Overlay&action=startOverlaySession&idsite={$idSite}&period={$period}&date={$date}';
-
- {literal}
-
- var locationParts = window.location.href.split('#');
- if (locationParts.length > 1) {
- var url = broadcast.getParamValue('l', locationParts[1]);
- url = Overlay_Helper.decodeFrameUrl(url);
- newLocation += '#' + url;
+
+ <script type="text/javascript">
+ var newLocation = 'index.php?module=Overlay&action=startOverlaySession&idsite={$idSite}&period={$period}&date={$date}';
+
+ {literal}
+
+ var locationParts = window.location.href.split('#');
+ if (locationParts.length > 1) {
+ var url = broadcast.getParamValue('l', locationParts[1]);
+ url = Overlay_Helper.decodeFrameUrl(url);
+ newLocation += '#' + url;
}
-
- window.location.href = newLocation;
-
- {/literal}
- </script>
+
+ window.location.href = newLocation;
+
+ {/literal}
+ </script>
</div>
diff --git a/plugins/Overlay/templates/notify_parent_iframe.tpl b/plugins/Overlay/templates/notify_parent_iframe.tpl
index 4e4dbede4e..19327445a1 100644
--- a/plugins/Overlay/templates/notify_parent_iframe.tpl
+++ b/plugins/Overlay/templates/notify_parent_iframe.tpl
@@ -1,14 +1,14 @@
<html>
<head>
- <title></title>
+ <title></title>
</head>
<body>
- <script type="text/javascript">
- // go up two iframes to find the piwik window
- var piwikWindow = window.parent.parent;
- // notify piwik of location change
- // the location has been passed as the hash part of the url from the overlay session
- piwikWindow.Piwik_Overlay.setCurrentUrl(window.location.hash.substring(1));
- </script>
+<script type="text/javascript">
+ // go up two iframes to find the piwik window
+ var piwikWindow = window.parent.parent;
+ // notify piwik of location change
+ // the location has been passed as the hash part of the url from the overlay session
+ piwikWindow.Piwik_Overlay.setCurrentUrl(window.location.hash.substring(1));
+</script>
</body>
</html> \ No newline at end of file
diff --git a/plugins/Overlay/templates/rowaction.js b/plugins/Overlay/templates/rowaction.js
index b73cd27c57..ff525c1e38 100644
--- a/plugins/Overlay/templates/rowaction.js
+++ b/plugins/Overlay/templates/rowaction.js
@@ -10,52 +10,52 @@
*/
function DataTable_RowActions_Overlay(dataTable) {
- this.dataTable = dataTable;
+ this.dataTable = dataTable;
}
DataTable_RowActions_Overlay.prototype = new DataTable_RowAction;
-DataTable_RowActions_Overlay.prototype.onClick = function(actionA, tr, e) {
- if (!actionA.data('overlay-manipulated')) {
- actionA.data('overlay-manipulated', 1);
-
- var link = tr.find('> td:first > a').attr('href');
- link = $('<textarea>').html(link).val(); // remove html entities
-
- actionA.attr({
- target: '_blank',
- href: Overlay_Helper.getOverlayLink(this.dataTable.param.idSite, 'month', 'today', link)
- });
- }
-
- return true;
+DataTable_RowActions_Overlay.prototype.onClick = function (actionA, tr, e) {
+ if (!actionA.data('overlay-manipulated')) {
+ actionA.data('overlay-manipulated', 1);
+
+ var link = tr.find('> td:first > a').attr('href');
+ link = $('<textarea>').html(link).val(); // remove html entities
+
+ actionA.attr({
+ target: '_blank',
+ href: Overlay_Helper.getOverlayLink(this.dataTable.param.idSite, 'month', 'today', link)
+ });
+ }
+
+ return true;
};
DataTable_RowActions_Registry.register({
- name: 'Overlay',
+ name: 'Overlay',
+
+ dataTableIcon: 'plugins/Overlay/templates/overlay_icon.png',
+ dataTableIconHover: 'plugins/Overlay/templates/overlay_icon_hover.png',
- dataTableIcon: 'plugins/Overlay/templates/overlay_icon.png',
- dataTableIconHover: 'plugins/Overlay/templates/overlay_icon_hover.png',
-
- order: 30,
+ order: 30,
- dataTableIconTooltip: [
- _pk_translate('General_OverlayRowActionTooltipTitle_js'),
- _pk_translate('General_OverlayRowActionTooltip_js')
- ],
+ dataTableIconTooltip: [
+ _pk_translate('General_OverlayRowActionTooltipTitle_js'),
+ _pk_translate('General_OverlayRowActionTooltip_js')
+ ],
- createInstance: function(dataTable) {
- return new DataTable_RowActions_Overlay(dataTable);
- },
+ createInstance: function (dataTable) {
+ return new DataTable_RowActions_Overlay(dataTable);
+ },
- isAvailableOnReport: function(dataTableParams) {
- return DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action);
- },
+ isAvailableOnReport: function (dataTableParams) {
+ return DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action);
+ },
- isAvailableOnRow: function(dataTableParams, tr) {
- var transitions = DataTable_RowActions_Registry.getActionByName('Transitions');
- return transitions.isAvailableOnRow(dataTableParams, tr);
- }
+ isAvailableOnRow: function (dataTableParams, tr) {
+ var transitions = DataTable_RowActions_Registry.getActionByName('Transitions');
+ return transitions.isAvailableOnRow(dataTableParams, tr);
+ }
});
diff --git a/plugins/Overlay/templates/sidebar.tpl b/plugins/Overlay/templates/sidebar.tpl
index 2421fe9791..d0e6a040ad 100644
--- a/plugins/Overlay/templates/sidebar.tpl
+++ b/plugins/Overlay/templates/sidebar.tpl
@@ -1,23 +1,23 @@
<div> <!-- Wrapper is needed that the html can be jqueryfied -->
- <!-- This div is removed by JS and the content is put in the location div -->
- <div class="Overlay_Location">
- <b>{'Overlay_Location'|translate|escape:'html'}:</b>
+ <!-- This div is removed by JS and the content is put in the location div -->
+ <div class="Overlay_Location">
+ <b>{'Overlay_Location'|translate|escape:'html'}:</b>
<span data-normalized-url="{$normalizedUrl|escape:'html'}" data-label="{$label|escape:'html'}">
{$location|escape:'html'}
</span>
- </div>
-
- {if count($data)}
- <h2 class="Overlay_MainMetrics">{'Overlay_MainMetrics'|translate|escape:'html'}</h2>
- {foreach from=$data item=metric}
- <div class="Overlay_Metric">
- <span class="Overlay_MetricValue">{$metric.value}</span> {$metric.name|escape:'html'}
- </div>
- {/foreach}
- {else}
- <!-- note: the class Overlay_NoData is used in index.js -->
- <div class="Overlay_NoData">{'Overlay_NoData'|translate|escape:'html'}</div>
- {/if}
-
+ </div>
+
+ {if count($data)}
+ <h2 class="Overlay_MainMetrics">{'Overlay_MainMetrics'|translate|escape:'html'}</h2>
+ {foreach from=$data item=metric}
+ <div class="Overlay_Metric">
+ <span class="Overlay_MetricValue">{$metric.value}</span> {$metric.name|escape:'html'}
+ </div>
+ {/foreach}
+ {else}
+ <!-- note: the class Overlay_NoData is used in index.js -->
+ <div class="Overlay_NoData">{'Overlay_NoData'|translate|escape:'html'}</div>
+ {/if}
+
</div> \ No newline at end of file
diff --git a/plugins/PDFReports/API.php b/plugins/PDFReports/API.php
index f563120531..373b19fe0f 100644
--- a/plugins/PDFReports/API.php
+++ b/plugins/PDFReports/API.php
@@ -1,435 +1,415 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_PDFReports
*/
/**
* The PDFReports API lets you manage Scheduled Email reports, as well as generate, download or email any existing report.
- *
+ *
* "generateReport" will generate the requested report (for a specific date range, website and in the requested language).
- * "sendEmailReport" will send the report by email to the recipients specified for this report.
- *
- * You can also get the list of all existing reports via "getReports", create new reports via "addReport",
+ * "sendEmailReport" will send the report by email to the recipients specified for this report.
+ *
+ * You can also get the list of all existing reports via "getReports", create new reports via "addReport",
* or manage existing reports with "updateReport" and "deleteReport".
* See also the documentation about <a href='http://piwik.org/docs/email-reports/' target='_blank'>Scheduled Email reports</a> in Piwik.
- *
+ *
* @package Piwik_PDFReports
*/
class Piwik_PDFReports_API
{
- const VALIDATE_PARAMETERS_EVENT = 'PDFReports.validateReportParameters';
- const GET_REPORT_PARAMETERS_EVENT = 'PDFReports.getReportParameters';
- const GET_REPORT_METADATA_EVENT = 'PDFReports.getReportMetadata';
- const GET_REPORT_TYPES_EVENT = 'PDFReports.getReportTypes';
- const GET_REPORT_FORMATS_EVENT = 'PDFReports.getReportFormats';
- const GET_RENDERER_INSTANCE_EVENT = 'PDFReports.getRendererInstance';
- const PROCESS_REPORTS_EVENT = 'PDFReports.processReports';
- const GET_REPORT_RECIPIENTS_EVENT = 'PDFReports.getReportRecipients';
- const ALLOW_MULTIPLE_REPORTS_EVENT = 'PDFReports.allowMultipleReports';
- const SEND_REPORT_EVENT = 'PDFReports.sendReport';
-
- const OUTPUT_DOWNLOAD = 1;
- const OUTPUT_SAVE_ON_DISK = 2;
- const OUTPUT_INLINE = 3;
- const OUTPUT_RETURN = 4;
-
- const REPORT_TYPE_INFO_KEY = 'reportType';
- const OUTPUT_TYPE_INFO_KEY = 'outputType';
- const ID_SITE_INFO_KEY = 'idSite';
- const REPORT_KEY = 'report';
- const REPORT_CONTENT_KEY = 'contents';
- const FILENAME_KEY = 'filename';
- const PRETTY_DATE_KEY = 'prettyDate';
- const WEBSITE_NAME_KEY = 'websiteName';
- const ADDITIONAL_FILES_KEY = 'additionalFiles';
-
- const REPORT_TRUNCATE = 23;
-
- static private $instance = null;
-
- /**
- * @return Piwik_PDFReports_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Creates a new report and schedules it.
- *
- * @param int $idSite
- * @param string $description Report description
- * @param string $period Schedule frequency: day, week or month
- * @param int $hour Hour (0-23) when the report should be sent
- * @param string $reportType 'email' or any other format provided via the PDFReports.getReportTypes hook
- * @param string $reportFormat 'pdf', 'html' or any other format provided via the PDFReports.getReportFormats hook
- * @param array $reports array of reports
- * @param array $parameters array of parameters
- *
- * @return int idReport generated
- */
- public function addReport($idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters)
- {
- Piwik::checkUserIsNotAnonymous();
- Piwik::checkUserHasViewAccess($idSite);
-
- $currentUser = Piwik::getCurrentUserLogin();
- self::ensureLanguageSetForUser($currentUser);
-
- self::validateCommonReportAttributes($period, $hour, $description, $reportType, $reportFormat);
-
- // report parameters validations
- $parameters = self::validateReportParameters($reportType, $parameters);
-
- // validation of requested reports
- $reports = self::validateRequestedReports($idSite, $reportType, $reports);
-
- $db = Zend_Registry::get('db');
- $idReport = $db->fetchOne("SELECT max(idreport) + 1 FROM ".Piwik_Common::prefixTable('report'));
-
- if($idReport == false)
- {
- $idReport = 1;
- }
-
- $db->insert(Piwik_Common::prefixTable('report'),
- array(
- 'idreport' => $idReport,
- 'idsite' => $idSite,
- 'login' => $currentUser,
- 'description' => $description,
- 'period' => $period,
- 'hour' => $hour,
- 'type' => $reportType,
- 'format' => $reportFormat,
- 'parameters' => $parameters,
- 'reports' => $reports,
- 'ts_created' => Piwik_Date::now()->getDatetime(),
- 'deleted' => 0,
- ));
-
- return $idReport;
- }
-
- private static function ensureLanguageSetForUser($currentUser)
- {
- $lang = Piwik_LanguagesManager_API::getInstance()->getLanguageForUser( $currentUser );
- if(empty($lang))
- {
- Piwik_LanguagesManager_API::getInstance()->setLanguageForUser( $currentUser, Piwik_LanguagesManager::getLanguageCodeForCurrentUser() );
- }
- }
-
- /**
- * Updates an existing report.
- *
- * @see addReport()
- */
- public function updateReport( $idReport, $idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters)
- {
- Piwik::checkUserIsNotAnonymous();
- Piwik::checkUserHasViewAccess($idSite);
-
- $pdfReports = $this->getReports($idSite, $periodSearch = false, $idReport);
- $report = reset($pdfReports);
- $idReport = $report['idreport'];
-
- $currentUser = Piwik::getCurrentUserLogin();
- self::ensureLanguageSetForUser($currentUser);
-
- self::validateCommonReportAttributes($period, $hour, $description, $reportType, $reportFormat);
-
- // report parameters validations
- $parameters = self::validateReportParameters($reportType, $parameters);
-
- // validation of requested reports
- $reports = self::validateRequestedReports($idSite, $reportType, $reports);
-
- Zend_Registry::get('db')->update( Piwik_Common::prefixTable('report'),
- array(
- 'description' => $description,
- 'period' => $period,
- 'hour' => $hour,
- 'type' => $reportType,
- 'format' => $reportFormat,
- 'parameters' => $parameters,
- 'reports' => $reports,
- ),
- "idreport = '$idReport'"
- );
-
- self::$cache = array();
- }
-
- /**
- * Deletes a specific report
- *
- * @param int $idReport
- */
- public function deleteReport($idReport)
- {
- $pdfReports = $this->getReports($idSite = false, $periodSearch = false, $idReport);
- $report = reset($pdfReports);
- Piwik::checkUserIsSuperUserOrTheUser($report['login']);
-
- Zend_Registry::get('db')->update( Piwik_Common::prefixTable('report'),
- array(
- 'deleted' => 1,
- ),
- "idreport = '$idReport'"
- );
- self::$cache = array();
- }
-
- // static cache storing reports
- public static $cache = array();
-
- /**
- * Returns the list of reports matching the passed parameters
- *
- * @param int $idSite If specified, will filter reports that belong to a specific idsite
- * @param string $period If specified, will filter reports that are scheduled for this period (day,week,month)
- * @param int $idReport If specified, will filter the report that has the given idReport
- * @return array
- * @throws Exception if $idReport was specified but the report wasn't found
- */
- public function getReports($idSite = false, $period = false, $idReport = false, $ifSuperUserReturnOnlySuperUserReports = false)
- {
- Piwik::checkUserHasSomeViewAccess();
- $cacheKey = (int)$idSite .'.'. (string)$period .'.'. (int)$idReport .'.'. (int)$ifSuperUserReturnOnlySuperUserReports;
- if(isset(self::$cache[$cacheKey]))
- {
- return self::$cache[$cacheKey];
- }
-
- $sqlWhere = '';
- $bind = array();
-
- // Super user gets all reports back, other users only their own
- if(!Piwik::isUserIsSuperUser()
- || $ifSuperUserReturnOnlySuperUserReports)
- {
- $sqlWhere .= "AND login = ?";
- $bind[] = Piwik::getCurrentUserLogin();
- }
-
- if(!empty($period))
- {
- $this->validateReportPeriod($period);
- $sqlWhere .= " AND period = ? ";
- $bind[] = $period;
- }
- if(!empty($idSite))
- {
- Piwik::checkUserHasViewAccess($idSite);
- $sqlWhere .= " AND ".Piwik_Common::prefixTable('site').".idsite = ?";
- $bind[] = $idSite;
- }
- if(!empty($idReport))
- {
- $sqlWhere .= " AND idreport = ?";
- $bind[] = $idReport;
- }
-
- // Joining with the site table to work around pre-1.3 where reports could still be linked to a deleted site
- $reports = Piwik_FetchAll("SELECT *
- FROM ".Piwik_Common::prefixTable('report')."
- JOIN ".Piwik_Common::prefixTable('site')."
+ const VALIDATE_PARAMETERS_EVENT = 'PDFReports.validateReportParameters';
+ const GET_REPORT_PARAMETERS_EVENT = 'PDFReports.getReportParameters';
+ const GET_REPORT_METADATA_EVENT = 'PDFReports.getReportMetadata';
+ const GET_REPORT_TYPES_EVENT = 'PDFReports.getReportTypes';
+ const GET_REPORT_FORMATS_EVENT = 'PDFReports.getReportFormats';
+ const GET_RENDERER_INSTANCE_EVENT = 'PDFReports.getRendererInstance';
+ const PROCESS_REPORTS_EVENT = 'PDFReports.processReports';
+ const GET_REPORT_RECIPIENTS_EVENT = 'PDFReports.getReportRecipients';
+ const ALLOW_MULTIPLE_REPORTS_EVENT = 'PDFReports.allowMultipleReports';
+ const SEND_REPORT_EVENT = 'PDFReports.sendReport';
+
+ const OUTPUT_DOWNLOAD = 1;
+ const OUTPUT_SAVE_ON_DISK = 2;
+ const OUTPUT_INLINE = 3;
+ const OUTPUT_RETURN = 4;
+
+ const REPORT_TYPE_INFO_KEY = 'reportType';
+ const OUTPUT_TYPE_INFO_KEY = 'outputType';
+ const ID_SITE_INFO_KEY = 'idSite';
+ const REPORT_KEY = 'report';
+ const REPORT_CONTENT_KEY = 'contents';
+ const FILENAME_KEY = 'filename';
+ const PRETTY_DATE_KEY = 'prettyDate';
+ const WEBSITE_NAME_KEY = 'websiteName';
+ const ADDITIONAL_FILES_KEY = 'additionalFiles';
+
+ const REPORT_TRUNCATE = 23;
+
+ static private $instance = null;
+
+ /**
+ * @return Piwik_PDFReports_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Creates a new report and schedules it.
+ *
+ * @param int $idSite
+ * @param string $description Report description
+ * @param string $period Schedule frequency: day, week or month
+ * @param int $hour Hour (0-23) when the report should be sent
+ * @param string $reportType 'email' or any other format provided via the PDFReports.getReportTypes hook
+ * @param string $reportFormat 'pdf', 'html' or any other format provided via the PDFReports.getReportFormats hook
+ * @param array $reports array of reports
+ * @param array $parameters array of parameters
+ *
+ * @return int idReport generated
+ */
+ public function addReport($idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters)
+ {
+ Piwik::checkUserIsNotAnonymous();
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $currentUser = Piwik::getCurrentUserLogin();
+ self::ensureLanguageSetForUser($currentUser);
+
+ self::validateCommonReportAttributes($period, $hour, $description, $reportType, $reportFormat);
+
+ // report parameters validations
+ $parameters = self::validateReportParameters($reportType, $parameters);
+
+ // validation of requested reports
+ $reports = self::validateRequestedReports($idSite, $reportType, $reports);
+
+ $db = Zend_Registry::get('db');
+ $idReport = $db->fetchOne("SELECT max(idreport) + 1 FROM " . Piwik_Common::prefixTable('report'));
+
+ if ($idReport == false) {
+ $idReport = 1;
+ }
+
+ $db->insert(Piwik_Common::prefixTable('report'),
+ array(
+ 'idreport' => $idReport,
+ 'idsite' => $idSite,
+ 'login' => $currentUser,
+ 'description' => $description,
+ 'period' => $period,
+ 'hour' => $hour,
+ 'type' => $reportType,
+ 'format' => $reportFormat,
+ 'parameters' => $parameters,
+ 'reports' => $reports,
+ 'ts_created' => Piwik_Date::now()->getDatetime(),
+ 'deleted' => 0,
+ ));
+
+ return $idReport;
+ }
+
+ private static function ensureLanguageSetForUser($currentUser)
+ {
+ $lang = Piwik_LanguagesManager_API::getInstance()->getLanguageForUser($currentUser);
+ if (empty($lang)) {
+ Piwik_LanguagesManager_API::getInstance()->setLanguageForUser($currentUser, Piwik_LanguagesManager::getLanguageCodeForCurrentUser());
+ }
+ }
+
+ /**
+ * Updates an existing report.
+ *
+ * @see addReport()
+ */
+ public function updateReport($idReport, $idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters)
+ {
+ Piwik::checkUserIsNotAnonymous();
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $pdfReports = $this->getReports($idSite, $periodSearch = false, $idReport);
+ $report = reset($pdfReports);
+ $idReport = $report['idreport'];
+
+ $currentUser = Piwik::getCurrentUserLogin();
+ self::ensureLanguageSetForUser($currentUser);
+
+ self::validateCommonReportAttributes($period, $hour, $description, $reportType, $reportFormat);
+
+ // report parameters validations
+ $parameters = self::validateReportParameters($reportType, $parameters);
+
+ // validation of requested reports
+ $reports = self::validateRequestedReports($idSite, $reportType, $reports);
+
+ Zend_Registry::get('db')->update(Piwik_Common::prefixTable('report'),
+ array(
+ 'description' => $description,
+ 'period' => $period,
+ 'hour' => $hour,
+ 'type' => $reportType,
+ 'format' => $reportFormat,
+ 'parameters' => $parameters,
+ 'reports' => $reports,
+ ),
+ "idreport = '$idReport'"
+ );
+
+ self::$cache = array();
+ }
+
+ /**
+ * Deletes a specific report
+ *
+ * @param int $idReport
+ */
+ public function deleteReport($idReport)
+ {
+ $pdfReports = $this->getReports($idSite = false, $periodSearch = false, $idReport);
+ $report = reset($pdfReports);
+ Piwik::checkUserIsSuperUserOrTheUser($report['login']);
+
+ Zend_Registry::get('db')->update(Piwik_Common::prefixTable('report'),
+ array(
+ 'deleted' => 1,
+ ),
+ "idreport = '$idReport'"
+ );
+ self::$cache = array();
+ }
+
+ // static cache storing reports
+ public static $cache = array();
+
+ /**
+ * Returns the list of reports matching the passed parameters
+ *
+ * @param int $idSite If specified, will filter reports that belong to a specific idsite
+ * @param string $period If specified, will filter reports that are scheduled for this period (day,week,month)
+ * @param int $idReport If specified, will filter the report that has the given idReport
+ * @return array
+ * @throws Exception if $idReport was specified but the report wasn't found
+ */
+ public function getReports($idSite = false, $period = false, $idReport = false, $ifSuperUserReturnOnlySuperUserReports = false)
+ {
+ Piwik::checkUserHasSomeViewAccess();
+ $cacheKey = (int)$idSite . '.' . (string)$period . '.' . (int)$idReport . '.' . (int)$ifSuperUserReturnOnlySuperUserReports;
+ if (isset(self::$cache[$cacheKey])) {
+ return self::$cache[$cacheKey];
+ }
+
+ $sqlWhere = '';
+ $bind = array();
+
+ // Super user gets all reports back, other users only their own
+ if (!Piwik::isUserIsSuperUser()
+ || $ifSuperUserReturnOnlySuperUserReports
+ ) {
+ $sqlWhere .= "AND login = ?";
+ $bind[] = Piwik::getCurrentUserLogin();
+ }
+
+ if (!empty($period)) {
+ $this->validateReportPeriod($period);
+ $sqlWhere .= " AND period = ? ";
+ $bind[] = $period;
+ }
+ if (!empty($idSite)) {
+ Piwik::checkUserHasViewAccess($idSite);
+ $sqlWhere .= " AND " . Piwik_Common::prefixTable('site') . ".idsite = ?";
+ $bind[] = $idSite;
+ }
+ if (!empty($idReport)) {
+ $sqlWhere .= " AND idreport = ?";
+ $bind[] = $idReport;
+ }
+
+ // Joining with the site table to work around pre-1.3 where reports could still be linked to a deleted site
+ $reports = Piwik_FetchAll("SELECT *
+ FROM " . Piwik_Common::prefixTable('report') . "
+ JOIN " . Piwik_Common::prefixTable('site') . "
USING (idsite)
WHERE deleted = 0
$sqlWhere", $bind);
- // When a specific report was requested and not found, throw an error
- if($idReport !== false
- && empty($reports))
- {
- throw new Exception("Requested report couldn't be found.");
- }
-
- foreach($reports as &$report) {
- // decode report parameters
- $report['parameters'] = Piwik_Common::json_decode($report['parameters'], true);
-
- // decode report list
- $report['reports'] = Piwik_Common::json_decode($report['reports'], true);
- }
-
- // static cache
- self::$cache[$cacheKey] = $reports;
-
- return $reports;
- }
-
+ // When a specific report was requested and not found, throw an error
+ if ($idReport !== false
+ && empty($reports)
+ ) {
+ throw new Exception("Requested report couldn't be found.");
+ }
+
+ foreach ($reports as &$report) {
+ // decode report parameters
+ $report['parameters'] = Piwik_Common::json_decode($report['parameters'], true);
+
+ // decode report list
+ $report['reports'] = Piwik_Common::json_decode($report['reports'], true);
+ }
+
+ // static cache
+ self::$cache[$cacheKey] = $reports;
+
+ return $reports;
+ }
+
/**
- * Generates a report file.
- *
+ * Generates a report file.
+ *
* @param int $idReport ID of the report to generate.
* @param string $date YYYY-MM-DD
- * @param bool|false|string $language If not passed, will use default language.
- * @param bool|false|int $outputType 1 = download report, 2 = save report to disk, 3 = output report in browser, 4 = return report content to caller, defaults to download
- * @param bool|false|string $period Defaults to 'day'. If not specified, will default to the report's period set when creating the report
- * @param bool|false|string $reportFormat 'pdf', 'html' or any other format provided via the PDFReports.getReportFormats hook
- * @param bool|false|array $parameters array of parameters
- * @return array|void
- */
- public function generateReport($idReport, $date, $language = false, $outputType = false, $period = false, $reportFormat = false, $parameters = false)
- {
- Piwik::checkUserIsNotAnonymous();
-
- // load specified language
- if(empty($language))
- {
- $language = Piwik_Translate::getInstance()->getLanguageDefault();
- }
-
- Piwik_Translate::getInstance()->reloadLanguage($language);
-
- $reports = $this->getReports($idSite = false, $_period = false, $idReport);
- $report = reset($reports);
-
- $idSite = $report['idsite'];
- $reportType = $report['type'];
-
- // override report period
- if(empty($period))
- {
- $period = $report['period'];
- }
-
- // override report format
- if(!empty($reportFormat))
- {
- self::validateReportFormat($reportType, $reportFormat);
- $report['format'] = $reportFormat;
- }
- else
- {
- $reportFormat = $report['format'];
- }
-
- // override and/or validate report parameters
- $report['parameters'] = Piwik_Common::json_decode(
- self::validateReportParameters($reportType, empty($parameters) ? $report['parameters'] : $parameters),
- true
- );
-
- // available reports
- $availableReportMetadata = Piwik_API_API::getInstance()->getReportMetadata($idSite);
-
- // we need to lookup which reports metadata are registered in this report
- $reportMetadata = array();
- foreach($availableReportMetadata as $metadata)
- {
- if(in_array($metadata['uniqueId'], $report['reports']))
- {
- $reportMetadata[] = $metadata;
- }
- }
-
- // the report will be rendered with the first 23 rows and will aggregate other rows in a summary row
- // 23 rows table fits in one portrait page
- $initialFilterTruncate = Piwik_Common::getRequestVar('filter_truncate', false);
- $_GET['filter_truncate'] = self::REPORT_TRUNCATE;
-
- $prettyDate = null;
- $processedReports = array();
- foreach ($reportMetadata as $action)
- {
- $apiModule = $action['module'];
- $apiAction = $action['action'];
- $apiParameters = array();
- if(isset($action['parameters']))
- {
- $apiParameters = $action['parameters'];
- }
-
- $mustRestoreGET = false;
-
- // all Websites dashboard should not be truncated in the report
- if($apiModule == 'MultiSites')
- {
- $mustRestoreGET = $_GET;
- $_GET['enhanced'] = true;
-
- if($apiAction == 'getAll')
- {
- $_GET['filter_truncate'] = false;
-
- // when a view/admin user created a report, workaround the fact that "Super User"
- // is enforced in Scheduled tasks, and ensure Multisites.getAll only return the websites that this user can access
- $userLogin = $report['login'];
- if(!empty($userLogin)
- && $userLogin != Piwik_Config::getInstance()->superuser['login'])
- {
- $_GET['_restrictSitesToLogin'] = $userLogin;
- }
- }
- }
-
- $processedReport = Piwik_API_API::getInstance()->getProcessedReport(
- $idSite, $period, $date, $apiModule, $apiAction,
- $segment = false, $apiParameters, $idGoal = false, $language
- );
-
- // TODO add static method getPrettyDate($period, $date) in Piwik_Period
- $prettyDate = $processedReport['prettyDate'];
-
- if($mustRestoreGET)
- {
- $_GET = $mustRestoreGET;
- }
-
- $processedReports[] = $processedReport;
- }
-
- // restore filter truncate parameter value
- if($initialFilterTruncate !== false)
- {
- $_GET['filter_truncate'] = $initialFilterTruncate;
- }
-
- $notificationInfo = array(
- self::REPORT_TYPE_INFO_KEY => $reportType,
- self::OUTPUT_TYPE_INFO_KEY => $outputType,
- self::REPORT_KEY => $report,
- );
-
- // allow plugins to alter processed reports
- Piwik_PostEvent(
- self::PROCESS_REPORTS_EVENT,
- $processedReports,
- $notificationInfo
- );
-
- // retrieve report renderer instance
- $reportRenderer = null;
- Piwik_PostEvent(
- self::GET_RENDERER_INSTANCE_EVENT,
- $reportRenderer,
- $notificationInfo
- );
-
- // init report renderer
- $reportRenderer->setLocale($language);
-
- // render report
+ * @param bool|false|string $language If not passed, will use default language.
+ * @param bool|false|int $outputType 1 = download report, 2 = save report to disk, 3 = output report in browser, 4 = return report content to caller, defaults to download
+ * @param bool|false|string $period Defaults to 'day'. If not specified, will default to the report's period set when creating the report
+ * @param bool|false|string $reportFormat 'pdf', 'html' or any other format provided via the PDFReports.getReportFormats hook
+ * @param bool|false|array $parameters array of parameters
+ * @return array|void
+ */
+ public function generateReport($idReport, $date, $language = false, $outputType = false, $period = false, $reportFormat = false, $parameters = false)
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ // load specified language
+ if (empty($language)) {
+ $language = Piwik_Translate::getInstance()->getLanguageDefault();
+ }
+
+ Piwik_Translate::getInstance()->reloadLanguage($language);
+
+ $reports = $this->getReports($idSite = false, $_period = false, $idReport);
+ $report = reset($reports);
+
+ $idSite = $report['idsite'];
+ $reportType = $report['type'];
+
+ // override report period
+ if (empty($period)) {
+ $period = $report['period'];
+ }
+
+ // override report format
+ if (!empty($reportFormat)) {
+ self::validateReportFormat($reportType, $reportFormat);
+ $report['format'] = $reportFormat;
+ } else {
+ $reportFormat = $report['format'];
+ }
+
+ // override and/or validate report parameters
+ $report['parameters'] = Piwik_Common::json_decode(
+ self::validateReportParameters($reportType, empty($parameters) ? $report['parameters'] : $parameters),
+ true
+ );
+
+ // available reports
+ $availableReportMetadata = Piwik_API_API::getInstance()->getReportMetadata($idSite);
+
+ // we need to lookup which reports metadata are registered in this report
+ $reportMetadata = array();
+ foreach ($availableReportMetadata as $metadata) {
+ if (in_array($metadata['uniqueId'], $report['reports'])) {
+ $reportMetadata[] = $metadata;
+ }
+ }
+
+ // the report will be rendered with the first 23 rows and will aggregate other rows in a summary row
+ // 23 rows table fits in one portrait page
+ $initialFilterTruncate = Piwik_Common::getRequestVar('filter_truncate', false);
+ $_GET['filter_truncate'] = self::REPORT_TRUNCATE;
+
+ $prettyDate = null;
+ $processedReports = array();
+ foreach ($reportMetadata as $action) {
+ $apiModule = $action['module'];
+ $apiAction = $action['action'];
+ $apiParameters = array();
+ if (isset($action['parameters'])) {
+ $apiParameters = $action['parameters'];
+ }
+
+ $mustRestoreGET = false;
+
+ // all Websites dashboard should not be truncated in the report
+ if ($apiModule == 'MultiSites') {
+ $mustRestoreGET = $_GET;
+ $_GET['enhanced'] = true;
+
+ if ($apiAction == 'getAll') {
+ $_GET['filter_truncate'] = false;
+
+ // when a view/admin user created a report, workaround the fact that "Super User"
+ // is enforced in Scheduled tasks, and ensure Multisites.getAll only return the websites that this user can access
+ $userLogin = $report['login'];
+ if (!empty($userLogin)
+ && $userLogin != Piwik_Config::getInstance()->superuser['login']
+ ) {
+ $_GET['_restrictSitesToLogin'] = $userLogin;
+ }
+ }
+ }
+
+ $processedReport = Piwik_API_API::getInstance()->getProcessedReport(
+ $idSite, $period, $date, $apiModule, $apiAction,
+ $segment = false, $apiParameters, $idGoal = false, $language
+ );
+
+ // TODO add static method getPrettyDate($period, $date) in Piwik_Period
+ $prettyDate = $processedReport['prettyDate'];
+
+ if ($mustRestoreGET) {
+ $_GET = $mustRestoreGET;
+ }
+
+ $processedReports[] = $processedReport;
+ }
+
+ // restore filter truncate parameter value
+ if ($initialFilterTruncate !== false) {
+ $_GET['filter_truncate'] = $initialFilterTruncate;
+ }
+
+ $notificationInfo = array(
+ self::REPORT_TYPE_INFO_KEY => $reportType,
+ self::OUTPUT_TYPE_INFO_KEY => $outputType,
+ self::REPORT_KEY => $report,
+ );
+
+ // allow plugins to alter processed reports
+ Piwik_PostEvent(
+ self::PROCESS_REPORTS_EVENT,
+ $processedReports,
+ $notificationInfo
+ );
+
+ // retrieve report renderer instance
+ $reportRenderer = null;
+ Piwik_PostEvent(
+ self::GET_RENDERER_INSTANCE_EVENT,
+ $reportRenderer,
+ $notificationInfo
+ );
+
+ // init report renderer
+ $reportRenderer->setLocale($language);
+
+ // render report
$description = str_replace(array("\r", "\n"), ' ', $report['description']);
// if the only report is "All websites", we don't display the site name
$websiteName = Piwik_Translate('General_Website') . " " . Piwik_Site::getNameFor($idSite);
- if(count($report['reports']) == 1
- && $report['reports'][0] == 'MultiSites_getAll')
- {
+ if (count($report['reports']) == 1
+ && $report['reports'][0] == 'MultiSites_getAll'
+ ) {
$websiteName = Piwik_Translate('General_MultiSitesSummary');
}
$reportTitle = "$websiteName - $prettyDate - $description";
@@ -437,335 +417,315 @@ class Piwik_PDFReports_API
$reportRenderer->renderFrontPage($websiteName, $prettyDate, $description, $reportMetadata);
array_walk($processedReports, array($reportRenderer, 'renderReport'));
- switch($outputType)
- {
- case self::OUTPUT_SAVE_ON_DISK:
- $outputFilename = strtoupper($reportFormat) . ' ' . ucfirst($reportType) .' Report - ' . $idReport . '.' . $date . '.' . $idSite . '.' . $language;
- $outputFilename = $reportRenderer->sendToDisk($outputFilename);
-
- $additionalFiles = array();
- if($reportRenderer instanceof Piwik_ReportRenderer_Html)
- {
- foreach ($processedReports as &$report) {
- if($report['displayGraph'])
- {
- $additionalFile = array();
- $additionalFile['filename'] = $report['metadata']['name'].'.png';
- $additionalFile['cid'] = $report['metadata']['uniqueId'];
- $additionalFile['content'] =
- Piwik_ReportRenderer::getStaticGraph(
- $report['metadata'],
- Piwik_ReportRenderer_Html::IMAGE_GRAPH_WIDTH,
- Piwik_ReportRenderer_Html::IMAGE_GRAPH_HEIGHT,
- $report['evolutionGraph']
- );
- $additionalFile['mimeType'] = 'image/png';
- $additionalFile['encoding'] = Zend_Mime::ENCODING_BASE64;
-
- $additionalFiles[] = $additionalFile;
- }
- }
- }
-
- return array(
- $outputFilename,
- $prettyDate,
- $websiteName,
- $additionalFiles,
- );
- break;
-
- case self::OUTPUT_INLINE:
-
- $reportRenderer->sendToBrowserInline($reportTitle);
- break;
-
- case self::OUTPUT_RETURN:
-
- return $reportRenderer->getRenderedReport();
- break;
-
- default:
- case self::OUTPUT_DOWNLOAD:
- $reportRenderer->sendToBrowserDownload($reportTitle);
- break;
- }
- }
-
- public function sendReport($idReport, $period = false, $date = false)
- {
- Piwik::checkUserIsNotAnonymous();
-
- $reports = $this->getReports($idSite = false, false, $idReport);
- $report = reset($reports);
-
- if($report['period'] == 'never')
- {
- $report['period'] = 'day';
- }
-
- if(!empty($period))
- {
- $report['period'] = $period;
- }
-
- if(empty($date))
- {
- $date = Piwik_Date::now()->subPeriod(1, $report['period'])->toString();
- }
-
- $language = Piwik_LanguagesManager_API::getInstance()->getLanguageForUser($report['login']);
-
- // generate report
- list($outputFilename, $prettyDate, $websiteName, $additionalFiles) =
- $this->generateReport(
- $idReport,
- $date,
- $language,
- self::OUTPUT_SAVE_ON_DISK,
- $report['period']
- );
-
- if(!file_exists($outputFilename))
- {
- throw new Exception("The report file wasn't found in $outputFilename");
- }
-
- $filename = basename($outputFilename);
- $handle = fopen($outputFilename, "r");
- $contents = fread($handle, filesize($outputFilename));
- fclose($handle);
-
- $notificationObject = null;
- Piwik_PostEvent(
- self::SEND_REPORT_EVENT,
- $notificationObject,
- $notificationInfo = array(
- self::REPORT_TYPE_INFO_KEY => $report['type'],
- self::REPORT_KEY => $report,
- self::REPORT_CONTENT_KEY => $contents,
- self::FILENAME_KEY => $filename,
- self::PRETTY_DATE_KEY => $prettyDate,
- self::WEBSITE_NAME_KEY => $websiteName,
- self::ADDITIONAL_FILES_KEY => $additionalFiles,
- )
- );
-
- // Update flag in DB
- Zend_Registry::get('db')->update( Piwik_Common::prefixTable('report'),
- array( 'ts_last_sent' => Piwik_Date::now()->getDatetime() ),
- "idreport = " . $report['idreport']
- );
-
- // 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'])
- {
- @chmod($outputFilename, 0600);
- }
- }
-
- private static function validateReportParameters($reportType, $parameters)
- {
- // get list of valid parameters
- $availableParameters = array();
-
- $notificationInfo = array(
- self::REPORT_TYPE_INFO_KEY => $reportType
- );
-
- Piwik_PostEvent(self::GET_REPORT_PARAMETERS_EVENT, $availableParameters, $notificationInfo);
-
- // unset invalid parameters
- $availableParameterKeys = array_keys($availableParameters);
- foreach ($parameters as $key => $value)
- {
- if(!in_array($key, $availableParameterKeys))
- {
- unset($parameters[$key]);
- }
- }
-
- // test that all required parameters are provided
- foreach($availableParameters as $parameter => $mandatory)
- {
- if($mandatory && !isset($parameters[$parameter]))
- {
- throw new Exception('Missing parameter : ' . $parameter);
- }
- }
-
- // delegate report parameter validation
- Piwik_PostEvent(self::VALIDATE_PARAMETERS_EVENT, $parameters, $notificationInfo);
-
- return Piwik_Common::json_encode($parameters);
- }
-
- private static function validateAndTruncateDescription(&$description)
- {
- $description = substr($description, 0, 250);
- }
-
- private static function validateRequestedReports($idSite, $reportType, $requestedReports)
- {
- if(!self::allowMultipleReports($reportType))
- {
- //sms can only contain one report, we silently discard all but the first
- $requestedReports = array_slice($requestedReports, 0, 1);
- }
-
- // retrieve available reports
- $availableReportMetadata = self::getReportMetadata($idSite, $reportType);
-
- $availableReportIds = array();
- foreach($availableReportMetadata as $reportMetadata)
- {
- $availableReportIds[] = $reportMetadata['uniqueId'];
- }
-
- foreach($requestedReports as $report)
- {
- if(!in_array($report, $availableReportIds))
- {
- throw new Exception("Report $report is unknown or not available for report type '$reportType'.");
- }
- }
-
- return Piwik_Common::json_encode($requestedReports);
- }
-
- private static function validateCommonReportAttributes($period, $hour, &$description, $reportType, $reportFormat)
- {
- self::validateReportPeriod($period);
- self::validateReportHour($hour);
- self::validateAndTruncateDescription($description);
- self::validateReportType($reportType);
- self::validateReportFormat($reportType, $reportFormat);
- }
-
- private static function validateReportPeriod($period)
- {
- $availablePeriods = array('day', 'week', 'month', 'never');
- if(!in_array($period, $availablePeriods))
- {
- throw new Exception('Period schedule must be one of the following: ' . implode(', ', $availablePeriods));
- }
- }
-
- private static function validateReportHour($hour)
- {
- if(!is_numeric($hour) || $hour < 0 || $hour > 23)
- {
- throw new Exception('Invalid hour schedule. Should be anything from 0 to 23 inclusive.');
- }
- }
-
- private static function validateReportType($reportType)
- {
- $reportTypes = array_keys(self::getReportTypes());
-
- if(!in_array($reportType, $reportTypes))
- {
- throw new Exception(
- 'Report type \'' . $reportType . '\' not valid. Try one of the following ' . implode(', ', $reportTypes)
- );
- }
- }
-
- private static function validateReportFormat($reportType, $reportFormat)
- {
- $reportFormats = array_keys(self::getReportFormats($reportType));
-
- if(!in_array($reportFormat, $reportFormats))
- {
- throw new Exception(
- Piwik_TranslateException(
- 'General_ExceptionInvalidReportRendererFormat',
- array($reportFormat, implode(', ', $reportFormats))
- )
- );
- }
- }
-
- /**
- * @ignore
- */
- static public function getReportMetadata($idSite, $reportType)
- {
- $notificationInfo = array(
- self::REPORT_TYPE_INFO_KEY => $reportType,
- self::ID_SITE_INFO_KEY => $idSite,
- );
-
- // retrieve available reports
- $availableReportMetadata = array();
- Piwik_PostEvent(
- self::GET_REPORT_METADATA_EVENT,
- $availableReportMetadata,
- $notificationInfo
- );
-
- return $availableReportMetadata;
- }
-
- /**
- * @ignore
- */
- static public function allowMultipleReports($reportType)
- {
- $allowMultipleReports = null;
- Piwik_PostEvent(
- self::ALLOW_MULTIPLE_REPORTS_EVENT,
- $allowMultipleReports,
- $notificationInfo = array(
- self::REPORT_TYPE_INFO_KEY => $reportType,
- )
- );
- return $allowMultipleReports;
- }
-
- /**
- * @ignore
- */
- static public function getReportTypes()
- {
- $reportTypes = array();
- Piwik_PostEvent(self::GET_REPORT_TYPES_EVENT, $reportTypes);
-
- return $reportTypes;
- }
-
- /**
- * @ignore
- */
- static public function getReportFormats($reportType)
- {
- $reportFormats = array();
-
- Piwik_PostEvent(
- self::GET_REPORT_FORMATS_EVENT,
- $reportFormats,
- $notificationInfo = array(
- self::REPORT_TYPE_INFO_KEY => $reportType
- )
- );
-
- return $reportFormats;
- }
-
- /**
- * @ignore
- */
- static public function getReportRecipients($report)
- {
- $notificationInfo = array(
- self::REPORT_TYPE_INFO_KEY => $report['type'],
- self::REPORT_KEY => $report,
- );
-
- // retrieve report renderer instance
- $recipients = array();
- Piwik_PostEvent(self::GET_REPORT_RECIPIENTS_EVENT, $recipients, $notificationInfo);
-
- return $recipients;
- }
+ switch ($outputType) {
+ case self::OUTPUT_SAVE_ON_DISK:
+ $outputFilename = strtoupper($reportFormat) . ' ' . ucfirst($reportType) . ' Report - ' . $idReport . '.' . $date . '.' . $idSite . '.' . $language;
+ $outputFilename = $reportRenderer->sendToDisk($outputFilename);
+
+ $additionalFiles = array();
+ if ($reportRenderer instanceof Piwik_ReportRenderer_Html) {
+ foreach ($processedReports as &$report) {
+ if ($report['displayGraph']) {
+ $additionalFile = array();
+ $additionalFile['filename'] = $report['metadata']['name'] . '.png';
+ $additionalFile['cid'] = $report['metadata']['uniqueId'];
+ $additionalFile['content'] =
+ Piwik_ReportRenderer::getStaticGraph(
+ $report['metadata'],
+ Piwik_ReportRenderer_Html::IMAGE_GRAPH_WIDTH,
+ Piwik_ReportRenderer_Html::IMAGE_GRAPH_HEIGHT,
+ $report['evolutionGraph']
+ );
+ $additionalFile['mimeType'] = 'image/png';
+ $additionalFile['encoding'] = Zend_Mime::ENCODING_BASE64;
+
+ $additionalFiles[] = $additionalFile;
+ }
+ }
+ }
+
+ return array(
+ $outputFilename,
+ $prettyDate,
+ $websiteName,
+ $additionalFiles,
+ );
+ break;
+
+ case self::OUTPUT_INLINE:
+
+ $reportRenderer->sendToBrowserInline($reportTitle);
+ break;
+
+ case self::OUTPUT_RETURN:
+
+ return $reportRenderer->getRenderedReport();
+ break;
+
+ default:
+ case self::OUTPUT_DOWNLOAD:
+ $reportRenderer->sendToBrowserDownload($reportTitle);
+ break;
+ }
+ }
+
+ public function sendReport($idReport, $period = false, $date = false)
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ $reports = $this->getReports($idSite = false, false, $idReport);
+ $report = reset($reports);
+
+ if ($report['period'] == 'never') {
+ $report['period'] = 'day';
+ }
+
+ if (!empty($period)) {
+ $report['period'] = $period;
+ }
+
+ if (empty($date)) {
+ $date = Piwik_Date::now()->subPeriod(1, $report['period'])->toString();
+ }
+
+ $language = Piwik_LanguagesManager_API::getInstance()->getLanguageForUser($report['login']);
+
+ // generate report
+ list($outputFilename, $prettyDate, $websiteName, $additionalFiles) =
+ $this->generateReport(
+ $idReport,
+ $date,
+ $language,
+ self::OUTPUT_SAVE_ON_DISK,
+ $report['period']
+ );
+
+ if (!file_exists($outputFilename)) {
+ throw new Exception("The report file wasn't found in $outputFilename");
+ }
+
+ $filename = basename($outputFilename);
+ $handle = fopen($outputFilename, "r");
+ $contents = fread($handle, filesize($outputFilename));
+ fclose($handle);
+
+ $notificationObject = null;
+ Piwik_PostEvent(
+ self::SEND_REPORT_EVENT,
+ $notificationObject,
+ $notificationInfo = array(
+ self::REPORT_TYPE_INFO_KEY => $report['type'],
+ self::REPORT_KEY => $report,
+ self::REPORT_CONTENT_KEY => $contents,
+ self::FILENAME_KEY => $filename,
+ self::PRETTY_DATE_KEY => $prettyDate,
+ self::WEBSITE_NAME_KEY => $websiteName,
+ self::ADDITIONAL_FILES_KEY => $additionalFiles,
+ )
+ );
+
+ // Update flag in DB
+ Zend_Registry::get('db')->update(Piwik_Common::prefixTable('report'),
+ array('ts_last_sent' => Piwik_Date::now()->getDatetime()),
+ "idreport = " . $report['idreport']
+ );
+
+ // 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']) {
+ @chmod($outputFilename, 0600);
+ }
+ }
+
+ private static function validateReportParameters($reportType, $parameters)
+ {
+ // get list of valid parameters
+ $availableParameters = array();
+
+ $notificationInfo = array(
+ self::REPORT_TYPE_INFO_KEY => $reportType
+ );
+
+ Piwik_PostEvent(self::GET_REPORT_PARAMETERS_EVENT, $availableParameters, $notificationInfo);
+
+ // unset invalid parameters
+ $availableParameterKeys = array_keys($availableParameters);
+ foreach ($parameters as $key => $value) {
+ if (!in_array($key, $availableParameterKeys)) {
+ unset($parameters[$key]);
+ }
+ }
+
+ // test that all required parameters are provided
+ foreach ($availableParameters as $parameter => $mandatory) {
+ if ($mandatory && !isset($parameters[$parameter])) {
+ throw new Exception('Missing parameter : ' . $parameter);
+ }
+ }
+
+ // delegate report parameter validation
+ Piwik_PostEvent(self::VALIDATE_PARAMETERS_EVENT, $parameters, $notificationInfo);
+
+ return Piwik_Common::json_encode($parameters);
+ }
+
+ private static function validateAndTruncateDescription(&$description)
+ {
+ $description = substr($description, 0, 250);
+ }
+
+ private static function validateRequestedReports($idSite, $reportType, $requestedReports)
+ {
+ if (!self::allowMultipleReports($reportType)) {
+ //sms can only contain one report, we silently discard all but the first
+ $requestedReports = array_slice($requestedReports, 0, 1);
+ }
+
+ // retrieve available reports
+ $availableReportMetadata = self::getReportMetadata($idSite, $reportType);
+
+ $availableReportIds = array();
+ foreach ($availableReportMetadata as $reportMetadata) {
+ $availableReportIds[] = $reportMetadata['uniqueId'];
+ }
+
+ foreach ($requestedReports as $report) {
+ if (!in_array($report, $availableReportIds)) {
+ throw new Exception("Report $report is unknown or not available for report type '$reportType'.");
+ }
+ }
+
+ return Piwik_Common::json_encode($requestedReports);
+ }
+
+ private static function validateCommonReportAttributes($period, $hour, &$description, $reportType, $reportFormat)
+ {
+ self::validateReportPeriod($period);
+ self::validateReportHour($hour);
+ self::validateAndTruncateDescription($description);
+ self::validateReportType($reportType);
+ self::validateReportFormat($reportType, $reportFormat);
+ }
+
+ private static function validateReportPeriod($period)
+ {
+ $availablePeriods = array('day', 'week', 'month', 'never');
+ if (!in_array($period, $availablePeriods)) {
+ throw new Exception('Period schedule must be one of the following: ' . implode(', ', $availablePeriods));
+ }
+ }
+
+ private static function validateReportHour($hour)
+ {
+ if (!is_numeric($hour) || $hour < 0 || $hour > 23) {
+ throw new Exception('Invalid hour schedule. Should be anything from 0 to 23 inclusive.');
+ }
+ }
+
+ private static function validateReportType($reportType)
+ {
+ $reportTypes = array_keys(self::getReportTypes());
+
+ if (!in_array($reportType, $reportTypes)) {
+ throw new Exception(
+ 'Report type \'' . $reportType . '\' not valid. Try one of the following ' . implode(', ', $reportTypes)
+ );
+ }
+ }
+
+ private static function validateReportFormat($reportType, $reportFormat)
+ {
+ $reportFormats = array_keys(self::getReportFormats($reportType));
+
+ if (!in_array($reportFormat, $reportFormats)) {
+ throw new Exception(
+ Piwik_TranslateException(
+ 'General_ExceptionInvalidReportRendererFormat',
+ array($reportFormat, implode(', ', $reportFormats))
+ )
+ );
+ }
+ }
+
+ /**
+ * @ignore
+ */
+ static public function getReportMetadata($idSite, $reportType)
+ {
+ $notificationInfo = array(
+ self::REPORT_TYPE_INFO_KEY => $reportType,
+ self::ID_SITE_INFO_KEY => $idSite,
+ );
+
+ // retrieve available reports
+ $availableReportMetadata = array();
+ Piwik_PostEvent(
+ self::GET_REPORT_METADATA_EVENT,
+ $availableReportMetadata,
+ $notificationInfo
+ );
+
+ return $availableReportMetadata;
+ }
+
+ /**
+ * @ignore
+ */
+ static public function allowMultipleReports($reportType)
+ {
+ $allowMultipleReports = null;
+ Piwik_PostEvent(
+ self::ALLOW_MULTIPLE_REPORTS_EVENT,
+ $allowMultipleReports,
+ $notificationInfo = array(
+ self::REPORT_TYPE_INFO_KEY => $reportType,
+ )
+ );
+ return $allowMultipleReports;
+ }
+
+ /**
+ * @ignore
+ */
+ static public function getReportTypes()
+ {
+ $reportTypes = array();
+ Piwik_PostEvent(self::GET_REPORT_TYPES_EVENT, $reportTypes);
+
+ return $reportTypes;
+ }
+
+ /**
+ * @ignore
+ */
+ static public function getReportFormats($reportType)
+ {
+ $reportFormats = array();
+
+ Piwik_PostEvent(
+ self::GET_REPORT_FORMATS_EVENT,
+ $reportFormats,
+ $notificationInfo = array(
+ self::REPORT_TYPE_INFO_KEY => $reportType
+ )
+ );
+
+ return $reportFormats;
+ }
+
+ /**
+ * @ignore
+ */
+ static public function getReportRecipients($report)
+ {
+ $notificationInfo = array(
+ self::REPORT_TYPE_INFO_KEY => $report['type'],
+ self::REPORT_KEY => $report,
+ );
+
+ // retrieve report renderer instance
+ $recipients = array();
+ Piwik_PostEvent(self::GET_REPORT_RECIPIENTS_EVENT, $recipients, $notificationInfo);
+
+ return $recipients;
+ }
}
diff --git a/plugins/PDFReports/Controller.php b/plugins/PDFReports/Controller.php
index 3c996c2302..32e879c66d 100644
--- a/plugins/PDFReports/Controller.php
+++ b/plugins/PDFReports/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_PDFReports
*/
@@ -15,65 +15,61 @@
*/
class Piwik_PDFReports_Controller extends Piwik_Controller
{
- const DEFAULT_REPORT_TYPE = Piwik_PDFReports::EMAIL_TYPE;
+ const DEFAULT_REPORT_TYPE = Piwik_PDFReports::EMAIL_TYPE;
- public function index()
- {
- $view = Piwik_View::factory('index');
- $this->setGeneralVariablesView($view);
+ public function index()
+ {
+ $view = Piwik_View::factory('index');
+ $this->setGeneralVariablesView($view);
- $view->countWebsites = count(Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess());
+ $view->countWebsites = count(Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess());
- // get report types
- $reportTypes = Piwik_PDFReports_API::getReportTypes();
- $view->reportTypes = $reportTypes;
- $view->defaultReportType = self::DEFAULT_REPORT_TYPE;
- $view->defaultReportFormat = Piwik_PDFReports::DEFAULT_REPORT_FORMAT;
+ // get report types
+ $reportTypes = Piwik_PDFReports_API::getReportTypes();
+ $view->reportTypes = $reportTypes;
+ $view->defaultReportType = self::DEFAULT_REPORT_TYPE;
+ $view->defaultReportFormat = Piwik_PDFReports::DEFAULT_REPORT_FORMAT;
- $reportsByCategoryByType = array();
- $reportFormatsByReportType = array();
- $allowMultipleReportsByReportType = array();
- foreach($reportTypes as $reportType => $reportTypeIcon)
- {
- // get report formats
- $reportFormatsByReportType[$reportType] = Piwik_PDFReports_API::getReportFormats($reportType);
- $allowMultipleReportsByReportType[$reportType] = Piwik_PDFReports_API::allowMultipleReports($reportType);
+ $reportsByCategoryByType = array();
+ $reportFormatsByReportType = array();
+ $allowMultipleReportsByReportType = array();
+ foreach ($reportTypes as $reportType => $reportTypeIcon) {
+ // get report formats
+ $reportFormatsByReportType[$reportType] = Piwik_PDFReports_API::getReportFormats($reportType);
+ $allowMultipleReportsByReportType[$reportType] = Piwik_PDFReports_API::allowMultipleReports($reportType);
- // get report metadata
- $reportsByCategory = array();
- $availableReportMetadata = Piwik_PDFReports_API::getReportMetadata($this->idSite, $reportType);
- foreach($availableReportMetadata as $reportMetadata)
- {
- $reportsByCategory[$reportMetadata['category']][] = $reportMetadata;
- }
- $reportsByCategoryByType[$reportType] = $reportsByCategory;
- }
- $view->reportsByCategoryByReportType = $reportsByCategoryByType;
- $view->reportFormatsByReportType = $reportFormatsByReportType;
- $view->allowMultipleReportsByReportType = $allowMultipleReportsByReportType;
+ // get report metadata
+ $reportsByCategory = array();
+ $availableReportMetadata = Piwik_PDFReports_API::getReportMetadata($this->idSite, $reportType);
+ foreach ($availableReportMetadata as $reportMetadata) {
+ $reportsByCategory[$reportMetadata['category']][] = $reportMetadata;
+ }
+ $reportsByCategoryByType[$reportType] = $reportsByCategory;
+ }
+ $view->reportsByCategoryByReportType = $reportsByCategoryByType;
+ $view->reportFormatsByReportType = $reportFormatsByReportType;
+ $view->allowMultipleReportsByReportType = $allowMultipleReportsByReportType;
- $reports = array();
- $reportsById = array();
- if(!Piwik::isUserIsAnonymous())
- {
- $reports = Piwik_PDFReports_API::getInstance()->getReports($this->idSite, $period = false, $idReport = false, $ifSuperUserReturnOnlySuperUserReports = true);
- foreach($reports as &$report)
- {
- $report['recipients'] = Piwik_PDFReports_API::getReportRecipients($report);
- $reportsById[$report['idreport']] = $report;
- }
- }
- $view->reports = $reports;
- $view->reportsJSON = Piwik_Common::json_encode($reportsById);
+ $reports = array();
+ $reportsById = array();
+ if (!Piwik::isUserIsAnonymous()) {
+ $reports = Piwik_PDFReports_API::getInstance()->getReports($this->idSite, $period = false, $idReport = false, $ifSuperUserReturnOnlySuperUserReports = true);
+ foreach ($reports as &$report) {
+ $report['recipients'] = Piwik_PDFReports_API::getReportRecipients($report);
+ $reportsById[$report['idreport']] = $report;
+ }
+ }
+ $view->reports = $reports;
+ $view->reportsJSON = Piwik_Common::json_encode($reportsById);
- $view->downloadOutputType = Piwik_PDFReports_API::OUTPUT_INLINE;
+ $view->downloadOutputType = Piwik_PDFReports_API::OUTPUT_INLINE;
- $view->periods = Piwik_PDFReports::getPeriodToFrequency();
- $view->defaultPeriod = Piwik_PDFReports::DEFAULT_PERIOD;
- $view->defaultHour = Piwik_PDFReports::DEFAULT_HOUR;
+ $view->periods = Piwik_PDFReports::getPeriodToFrequency();
+ $view->defaultPeriod = Piwik_PDFReports::DEFAULT_PERIOD;
+ $view->defaultHour = Piwik_PDFReports::DEFAULT_HOUR;
- $view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
+ $view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
- echo $view->render();
- }
+ echo $view->render();
+ }
}
diff --git a/plugins/PDFReports/PDFReports.php b/plugins/PDFReports/PDFReports.php
index bf6f58596e..5f295b8366 100644
--- a/plugins/PDFReports/PDFReports.php
+++ b/plugins/PDFReports/PDFReports.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_PDFReports
*/
@@ -15,583 +15,542 @@
*/
class Piwik_PDFReports extends Piwik_Plugin
{
- const MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY = 'MobileMessaging_TopMenu';
- const PDF_REPORTS_TOP_MENU_TRANSLATION_KEY = 'PDFReports_EmailReports';
-
- const DISPLAY_FORMAT_GRAPHS_ONLY_FOR_KEY_METRICS = 1; // Display Tables Only (Graphs only for key metrics)
- const DISPLAY_FORMAT_GRAPHS_ONLY = 2; // Display Graphs Only for all reports
- const DISPLAY_FORMAT_TABLES_AND_GRAPHS = 3; // Display Tables and Graphs for all reports
- const DISPLAY_FORMAT_TABLES_ONLY = 4; // Display only tables for all reports
- const DEFAULT_DISPLAY_FORMAT = self::DISPLAY_FORMAT_GRAPHS_ONLY_FOR_KEY_METRICS;
-
- const DEFAULT_REPORT_FORMAT = Piwik_ReportRenderer::HTML_FORMAT;
- const DEFAULT_PERIOD = 'week';
- const DEFAULT_HOUR = '0';
-
- const EMAIL_ME_PARAMETER = 'emailMe';
- const EVOLUTION_GRAPH_PARAMETER = 'evolutionGraph';
- const ADDITIONAL_EMAILS_PARAMETER = 'additionalEmails';
- const DISPLAY_FORMAT_PARAMETER = 'displayFormat';
- const EMAIL_ME_PARAMETER_DEFAULT_VALUE = true;
- const EVOLUTION_GRAPH_PARAMETER_DEFAULT_VALUE = false;
-
- const EMAIL_TYPE = 'email';
-
- static private $availableParameters = array(
- self::EMAIL_ME_PARAMETER => false,
- self::EVOLUTION_GRAPH_PARAMETER => false,
- self::ADDITIONAL_EMAILS_PARAMETER => false,
- self::DISPLAY_FORMAT_PARAMETER => true,
- );
-
- static private $managedReportTypes = array(
- self::EMAIL_TYPE => 'themes/default/images/email.png'
- );
-
- static private $managedReportFormats = array(
- Piwik_ReportRenderer::HTML_FORMAT => 'themes/default/images/html_icon.png',
- Piwik_ReportRenderer::PDF_FORMAT => 'plugins/UserSettings/images/plugins/pdf.gif'
- );
-
- public function getInformation()
- {
- return array(
- 'name' => 'Email Reports Plugin',
- 'description' => Piwik_Translate('PDFReports_PluginDescriptionReports'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
- public function getListHooksRegistered()
- {
- return array(
- 'TopMenu.add' => 'addTopMenu',
- 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'PDFReports.getReportParameters' => 'getReportParameters',
- 'PDFReports.validateReportParameters' => 'validateReportParameters',
- 'PDFReports.getReportMetadata' => 'getReportMetadata',
- 'PDFReports.getReportTypes' => 'getReportTypes',
- 'PDFReports.getReportFormats' => 'getReportFormats',
- 'PDFReports.getRendererInstance' => 'getRendererInstance',
- 'PDFReports.getReportRecipients' => 'getReportRecipients',
- 'PDFReports.processReports' => 'processReports',
- 'PDFReports.allowMultipleReports' => 'allowMultipleReports',
- 'PDFReports.sendReport' => 'sendReport',
- 'template_reportParametersPDFReports' => 'template_reportParametersPDFReports',
- 'UsersManager.deleteUser' => 'deleteUserReport',
- 'SitesManager.deleteSite' => 'deleteSiteReport',
- );
- }
-
- /**
- * Delete reports for the website
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function deleteSiteReport( $notification )
- {
- $idSite = &$notification->getNotificationObject();
-
- $idReports = Piwik_PDFReports_API::getInstance()->getReports($idSite);
-
- foreach($idReports as $report)
- {
- $idReport = $report['idreport'];
- Piwik_PDFReports_API::getInstance()->deleteReport($idReport);
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
- $jsFiles[] = "plugins/PDFReports/templates/pdf.js";
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function validateReportParameters( $notification )
- {
- if(self::manageEvent($notification))
- {
- $parameters = &$notification->getNotificationObject();
-
- $reportFormat = $parameters[self::DISPLAY_FORMAT_PARAMETER];
- $availableDisplayFormats = array_keys(self::getDisplayFormats());
- if(!in_array($reportFormat, $availableDisplayFormats))
- {
- throw new Exception(
- Piwik_TranslateException(
- // General_ExceptionInvalidAggregateReportsFormat should be named General_ExceptionInvalidDisplayFormat
- 'General_ExceptionInvalidAggregateReportsFormat',
- array($reportFormat, implode(', ', $availableDisplayFormats))
- )
- );
- }
-
- // emailMe is an optional parameter
- if(!isset($parameters[self::EMAIL_ME_PARAMETER]))
- {
- $parameters[self::EMAIL_ME_PARAMETER] = self::EMAIL_ME_PARAMETER_DEFAULT_VALUE;
- }
- else
- {
- $parameters[self::EMAIL_ME_PARAMETER] = self::valueIsTrue($parameters[self::EMAIL_ME_PARAMETER]);
- }
-
- // evolutionGraph is an optional parameter
- if(!isset($parameters[self::EVOLUTION_GRAPH_PARAMETER]))
- {
- $parameters[self::EVOLUTION_GRAPH_PARAMETER] = self::EVOLUTION_GRAPH_PARAMETER_DEFAULT_VALUE;
- }
- else
- {
- $parameters[self::EVOLUTION_GRAPH_PARAMETER] = self::valueIsTrue($parameters[self::EVOLUTION_GRAPH_PARAMETER]);
- }
-
- // additionalEmails is an optional parameter
- if(isset($parameters[self::ADDITIONAL_EMAILS_PARAMETER]))
- {
- $parameters[self::ADDITIONAL_EMAILS_PARAMETER] = self::checkAdditionalEmails($parameters[self::ADDITIONAL_EMAILS_PARAMETER]);
- }
- }
- }
-
- // based on http://www.php.net/manual/en/filter.filters.validate.php -> FILTER_VALIDATE_BOOLEAN
- static private function valueIsTrue($value)
- {
- return $value == 'true' || $value == 1 || $value == '1' || $value === true;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportMetadata( $notification )
- {
- if(self::manageEvent($notification))
- {
- $reportMetadata = &$notification->getNotificationObject();
-
- $notificationInfo = $notification->getNotificationInfo();
- $idSite = $notificationInfo[Piwik_PDFReports_API::ID_SITE_INFO_KEY];
-
- $availableReportMetadata = Piwik_API_API::getInstance()->getReportMetadata($idSite);
-
- $filteredReportMetadata = array();
- foreach($availableReportMetadata as $reportMetadata)
- {
- // removing reports from the API category and MultiSites.getOne
- if(
- $reportMetadata['category'] == 'API' ||
- $reportMetadata['category'] == Piwik_Translate('General_MultiSitesSummary') && $reportMetadata['name'] == Piwik_Translate('General_SingleWebsitesDashboard')
- ) continue;
-
- $filteredReportMetadata[] = $reportMetadata;
- }
-
- $reportMetadata = $filteredReportMetadata;
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportTypes( $notification )
- {
- $reportTypes = &$notification->getNotificationObject();
- $reportTypes = array_merge($reportTypes, self::$managedReportTypes);
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportFormats( $notification )
- {
- if(self::manageEvent($notification))
- {
- $reportFormats = &$notification->getNotificationObject();
- $reportFormats = self::$managedReportFormats;
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportParameters( $notification )
- {
- if(self::manageEvent($notification))
- {
- $availableParameters = &$notification->getNotificationObject();
- $availableParameters = self::$availableParameters;
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function processReports( $notification )
- {
- if(self::manageEvent($notification))
- {
- $processedReports = &$notification->getNotificationObject();
-
- $notificationInfo = $notification->getNotificationInfo();
- $report = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY];
-
- $displayFormat = $report['parameters'][self::DISPLAY_FORMAT_PARAMETER];
- $evolutionGraph = $report['parameters'][self::EVOLUTION_GRAPH_PARAMETER];
-
- foreach ($processedReports as &$processedReport)
- {
- $metadata = $processedReport['metadata'];
-
- $isAggregateReport = !empty($metadata['dimension']);
-
- $processedReport['displayTable'] = $displayFormat != self::DISPLAY_FORMAT_GRAPHS_ONLY;
-
- $processedReport['displayGraph'] =
- ($isAggregateReport ?
- $displayFormat == self::DISPLAY_FORMAT_GRAPHS_ONLY || $displayFormat == self::DISPLAY_FORMAT_TABLES_AND_GRAPHS
- :
- $displayFormat != self::DISPLAY_FORMAT_TABLES_ONLY)
- && Piwik::isGdExtensionEnabled()
- && Piwik_PluginsManager::getInstance()->isPluginActivated('ImageGraph')
- && !empty($metadata['imageGraphUrl']);
-
- $processedReport['evolutionGraph'] = $evolutionGraph;
-
- // remove evolution metrics from MultiSites.getAll
- if($metadata['module'] == 'MultiSites')
- {
- $columns = $processedReport['columns'];
-
- foreach(Piwik_MultiSites_API::getApiMetrics($enhanced = true) as $metricSettings)
- {
- unset($columns[$metricSettings[Piwik_MultiSites_API::METRIC_EVOLUTION_COL_NAME_KEY]]);
- }
-
- $processedReport['metadata'] = $metadata;
- $processedReport['columns'] = $columns;
- }
- }
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getRendererInstance( $notification )
- {
- if(self::manageEvent($notification))
- {
- $reportRenderer = &$notification->getNotificationObject();
- $notificationInfo = $notification->getNotificationInfo();
-
- $reportFormat = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY]['format'];
- $outputType = $notificationInfo[Piwik_PDFReports_API::OUTPUT_TYPE_INFO_KEY];
-
- $reportRenderer = Piwik_ReportRenderer::factory($reportFormat);
-
- if($reportFormat == Piwik_ReportRenderer::HTML_FORMAT)
- {
- $reportRenderer->setRenderImageInline($outputType != Piwik_PDFReports_API::OUTPUT_SAVE_ON_DISK);
- }
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function allowMultipleReports( $notification )
- {
- if(self::manageEvent($notification))
- {
- $allowMultipleReports = &$notification->getNotificationObject();
- $allowMultipleReports = true;
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function sendReport( $notification )
- {
- if(self::manageEvent($notification))
- {
- $notificationInfo = $notification->getNotificationInfo();
- $report = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY];
- $websiteName = $notificationInfo[Piwik_PDFReports_API::WEBSITE_NAME_KEY];
- $prettyDate = $notificationInfo[Piwik_PDFReports_API::PRETTY_DATE_KEY];
- $contents = $notificationInfo[Piwik_PDFReports_API::REPORT_CONTENT_KEY];
- $filename = $notificationInfo[Piwik_PDFReports_API::FILENAME_KEY];
- $additionalFiles = $notificationInfo[Piwik_PDFReports_API::ADDITIONAL_FILES_KEY];
-
- $periods = self::getPeriodToFrequency();
- $message = Piwik_Translate('PDFReports_EmailHello');
- $subject = Piwik_Translate('General_Report') . ' '. $websiteName . " - ".$prettyDate;
-
- $mail = new Piwik_Mail();
- $mail->setSubject($subject);
- $fromEmailName = Piwik_Config::getInstance()->branding['use_custom_logo']
- ? Piwik_Translate('CoreHome_WebAnalyticsReports')
- : Piwik_Translate('PDFReports_PiwikReports');
- $fromEmailAddress = Piwik_Config::getInstance()->General['noreply_email_address'];
- $attachmentName = $subject;
- $mail->setFrom($fromEmailAddress, $fromEmailName);
-
- switch ($report['format'])
- {
- case 'html':
-
- // Needed when using images as attachment with cid
- $mail->setType(Zend_Mime::MULTIPART_RELATED);
- $message .= "<br/>" . Piwik_Translate('PDFReports_PleaseFindBelow', array($periods[$report['period']], $websiteName));
- $mail->setBodyHtml($message . "<br/><br/>". $contents);
- break;
-
- default:
- case 'pdf':
- $message .= "\n" . Piwik_Translate('PDFReports_PleaseFindAttachedFile', array($periods[$report['period']], $websiteName));
- $mail->setBodyText($message);
- $mail->createAttachment(
- $contents,
- 'application/pdf',
- Zend_Mime::DISPOSITION_INLINE,
- Zend_Mime::ENCODING_BASE64,
- $attachmentName.'.pdf'
- );
- break;
- }
-
- foreach($additionalFiles as $additionalFile)
- {
- $fileContent = $additionalFile['content'];
- $at = $mail->createAttachment(
- $fileContent,
- $additionalFile['mimeType'],
- Zend_Mime::DISPOSITION_INLINE,
- $additionalFile['encoding'],
- $additionalFile['filename']
- );
- $at->id = $additionalFile['cid'];
-
- unset($fileContent);
- }
-
- // Get user emails and languages
- $reportParameters = $report['parameters'];
- $emails = array();
-
- if(isset($reportParameters[self::ADDITIONAL_EMAILS_PARAMETER]))
- {
- $emails = $reportParameters[self::ADDITIONAL_EMAILS_PARAMETER];
- }
-
- if($reportParameters[self::EMAIL_ME_PARAMETER] == 1)
- {
- if(Piwik::getCurrentUserLogin() == $report['login'])
- {
- $emails[] = Piwik::getCurrentUserEmail();
- }
- elseif($report['login'] == Piwik_Config::getInstance()->superuser['login'])
- {
- $emails[] = Piwik::getSuperUserEmail();
- }
- else
- {
- try {
- $user = Piwik_UsersManager_API::getInstance()->getUser($report['login']);
- } catch(Exception $e) {
- return;
- }
- $emails[] = $user['email'];
- }
- }
-
- foreach ($emails as $email)
- {
- if(empty($email)) {
- continue;
- }
- $mail->addTo($email);
-
- try {
- $mail->send();
- } catch(Exception $e) {
-
- // If running from piwik.php with debug, we ignore the 'email not sent' error
- if(!isset($GLOBALS['PIWIK_TRACKER_DEBUG']) || !$GLOBALS['PIWIK_TRACKER_DEBUG'])
- {
- throw new Exception("An error occured while sending '$filename' ".
- " to ". implode(', ',$mail->getRecipients()).
- ". Error was '". $e->getMessage()."'");
- }
- }
- $mail->clearRecipients();
- }
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportRecipients( $notification )
- {
- if(self::manageEvent($notification))
- {
- $recipients = &$notification->getNotificationObject();
- $notificationInfo = $notification->getNotificationInfo();
-
- $report = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY];
- $parameters = $report['parameters'];
- $eMailMe = $parameters[self::EMAIL_ME_PARAMETER];
-
- if($eMailMe)
- {
- $recipients[] = Piwik::getCurrentUserEmail();
- }
-
- if(isset($parameters[self::ADDITIONAL_EMAILS_PARAMETER]))
- {
- $additionalEMails = $parameters[self::ADDITIONAL_EMAILS_PARAMETER];
- $recipients = array_merge($recipients, $additionalEMails);
- }
- $recipients = array_filter($recipients);
- }
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- static public function template_reportParametersPDFReports($notification)
- {
- $out =& $notification->getNotificationObject();
-
- $view = Piwik_View::factory('report_parameters');
- $view->currentUserEmail = Piwik::getCurrentUserEmail();
- $view->displayFormats = self::getDisplayFormats();
- $view->reportType = self::EMAIL_TYPE;
- $view->defaultDisplayFormat = self::DEFAULT_DISPLAY_FORMAT;
- $view->defaultEmailMe = self::EMAIL_ME_PARAMETER_DEFAULT_VALUE ? 'true' : 'false';
- $view->defaultEvolutionGraph = self::EVOLUTION_GRAPH_PARAMETER_DEFAULT_VALUE ? 'true' : 'false';
- $out .= $view->render();
- }
-
- private static function manageEvent($notification)
- {
- $notificationInfo = $notification->getNotificationInfo();
- return in_array(
- $notificationInfo[Piwik_PDFReports_API::REPORT_TYPE_INFO_KEY],
- array_keys(self::$managedReportTypes)
- );
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getScheduledTasks ( $notification )
- {
- $arbitraryDateInUTC = Piwik_Date::factory('2011-01-01');
- $tasks = &$notification->getNotificationObject();
- foreach(Piwik_PDFReports_API::getInstance()->getReports() as $report)
- {
- if (!$report['deleted'] && $report['period'] != Piwik_ScheduledTime::PERIOD_NEVER)
- {
- $midnightInSiteTimezone =
- date (
- 'H',
- Piwik_Date::factory(
- $arbitraryDateInUTC,
- Piwik_Site::getTimezoneFor($report['idsite'])
- )->getTimestamp()
- );
-
- $hourInUTC = (24 - $midnightInSiteTimezone + $report['hour']) % 24;
-
- $schedule = Piwik_ScheduledTime::getScheduledTimeForPeriod($report['period']);
- $schedule->setHour($hourInUTC);
- $tasks[] = new Piwik_ScheduledTask (
- Piwik_PDFReports_API::getInstance(),
- 'sendReport',
- $report['idreport'], $schedule
- );
- }
- }
- }
+ const MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY = 'MobileMessaging_TopMenu';
+ const PDF_REPORTS_TOP_MENU_TRANSLATION_KEY = 'PDFReports_EmailReports';
+
+ const DISPLAY_FORMAT_GRAPHS_ONLY_FOR_KEY_METRICS = 1; // Display Tables Only (Graphs only for key metrics)
+ const DISPLAY_FORMAT_GRAPHS_ONLY = 2; // Display Graphs Only for all reports
+ const DISPLAY_FORMAT_TABLES_AND_GRAPHS = 3; // Display Tables and Graphs for all reports
+ const DISPLAY_FORMAT_TABLES_ONLY = 4; // Display only tables for all reports
+ const DEFAULT_DISPLAY_FORMAT = self::DISPLAY_FORMAT_GRAPHS_ONLY_FOR_KEY_METRICS;
+
+ const DEFAULT_REPORT_FORMAT = Piwik_ReportRenderer::HTML_FORMAT;
+ const DEFAULT_PERIOD = 'week';
+ const DEFAULT_HOUR = '0';
+
+ const EMAIL_ME_PARAMETER = 'emailMe';
+ const EVOLUTION_GRAPH_PARAMETER = 'evolutionGraph';
+ const ADDITIONAL_EMAILS_PARAMETER = 'additionalEmails';
+ const DISPLAY_FORMAT_PARAMETER = 'displayFormat';
+ const EMAIL_ME_PARAMETER_DEFAULT_VALUE = true;
+ const EVOLUTION_GRAPH_PARAMETER_DEFAULT_VALUE = false;
+
+ const EMAIL_TYPE = 'email';
+
+ static private $availableParameters = array(
+ self::EMAIL_ME_PARAMETER => false,
+ self::EVOLUTION_GRAPH_PARAMETER => false,
+ self::ADDITIONAL_EMAILS_PARAMETER => false,
+ self::DISPLAY_FORMAT_PARAMETER => true,
+ );
+
+ static private $managedReportTypes = array(
+ self::EMAIL_TYPE => 'themes/default/images/email.png'
+ );
+
+ static private $managedReportFormats = array(
+ Piwik_ReportRenderer::HTML_FORMAT => 'themes/default/images/html_icon.png',
+ Piwik_ReportRenderer::PDF_FORMAT => 'plugins/UserSettings/images/plugins/pdf.gif'
+ );
+
+ public function getInformation()
+ {
+ return array(
+ 'name' => 'Email Reports Plugin',
+ 'description' => Piwik_Translate('PDFReports_PluginDescriptionReports'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'TopMenu.add' => 'addTopMenu',
+ 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'PDFReports.getReportParameters' => 'getReportParameters',
+ 'PDFReports.validateReportParameters' => 'validateReportParameters',
+ 'PDFReports.getReportMetadata' => 'getReportMetadata',
+ 'PDFReports.getReportTypes' => 'getReportTypes',
+ 'PDFReports.getReportFormats' => 'getReportFormats',
+ 'PDFReports.getRendererInstance' => 'getRendererInstance',
+ 'PDFReports.getReportRecipients' => 'getReportRecipients',
+ 'PDFReports.processReports' => 'processReports',
+ 'PDFReports.allowMultipleReports' => 'allowMultipleReports',
+ 'PDFReports.sendReport' => 'sendReport',
+ 'template_reportParametersPDFReports' => 'template_reportParametersPDFReports',
+ 'UsersManager.deleteUser' => 'deleteUserReport',
+ 'SitesManager.deleteSite' => 'deleteSiteReport',
+ );
+ }
+
+ /**
+ * Delete reports for the website
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function deleteSiteReport($notification)
+ {
+ $idSite = & $notification->getNotificationObject();
+
+ $idReports = Piwik_PDFReports_API::getInstance()->getReports($idSite);
+
+ foreach ($idReports as $report) {
+ $idReport = $report['idreport'];
+ Piwik_PDFReports_API::getInstance()->deleteReport($idReport);
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+ $jsFiles[] = "plugins/PDFReports/templates/pdf.js";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function validateReportParameters($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $parameters = & $notification->getNotificationObject();
+
+ $reportFormat = $parameters[self::DISPLAY_FORMAT_PARAMETER];
+ $availableDisplayFormats = array_keys(self::getDisplayFormats());
+ if (!in_array($reportFormat, $availableDisplayFormats)) {
+ throw new Exception(
+ Piwik_TranslateException(
+ // General_ExceptionInvalidAggregateReportsFormat should be named General_ExceptionInvalidDisplayFormat
+ 'General_ExceptionInvalidAggregateReportsFormat',
+ array($reportFormat, implode(', ', $availableDisplayFormats))
+ )
+ );
+ }
+
+ // emailMe is an optional parameter
+ if (!isset($parameters[self::EMAIL_ME_PARAMETER])) {
+ $parameters[self::EMAIL_ME_PARAMETER] = self::EMAIL_ME_PARAMETER_DEFAULT_VALUE;
+ } else {
+ $parameters[self::EMAIL_ME_PARAMETER] = self::valueIsTrue($parameters[self::EMAIL_ME_PARAMETER]);
+ }
+
+ // evolutionGraph is an optional parameter
+ if (!isset($parameters[self::EVOLUTION_GRAPH_PARAMETER])) {
+ $parameters[self::EVOLUTION_GRAPH_PARAMETER] = self::EVOLUTION_GRAPH_PARAMETER_DEFAULT_VALUE;
+ } else {
+ $parameters[self::EVOLUTION_GRAPH_PARAMETER] = self::valueIsTrue($parameters[self::EVOLUTION_GRAPH_PARAMETER]);
+ }
+
+ // additionalEmails is an optional parameter
+ if (isset($parameters[self::ADDITIONAL_EMAILS_PARAMETER])) {
+ $parameters[self::ADDITIONAL_EMAILS_PARAMETER] = self::checkAdditionalEmails($parameters[self::ADDITIONAL_EMAILS_PARAMETER]);
+ }
+ }
+ }
+
+ // based on http://www.php.net/manual/en/filter.filters.validate.php -> FILTER_VALIDATE_BOOLEAN
+ static private function valueIsTrue($value)
+ {
+ return $value == 'true' || $value == 1 || $value == '1' || $value === true;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportMetadata($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $reportMetadata = & $notification->getNotificationObject();
+
+ $notificationInfo = $notification->getNotificationInfo();
+ $idSite = $notificationInfo[Piwik_PDFReports_API::ID_SITE_INFO_KEY];
+
+ $availableReportMetadata = Piwik_API_API::getInstance()->getReportMetadata($idSite);
+
+ $filteredReportMetadata = array();
+ foreach ($availableReportMetadata as $reportMetadata) {
+ // removing reports from the API category and MultiSites.getOne
+ if (
+ $reportMetadata['category'] == 'API' ||
+ $reportMetadata['category'] == Piwik_Translate('General_MultiSitesSummary') && $reportMetadata['name'] == Piwik_Translate('General_SingleWebsitesDashboard')
+ ) continue;
+
+ $filteredReportMetadata[] = $reportMetadata;
+ }
+
+ $reportMetadata = $filteredReportMetadata;
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportTypes($notification)
+ {
+ $reportTypes = & $notification->getNotificationObject();
+ $reportTypes = array_merge($reportTypes, self::$managedReportTypes);
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportFormats($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $reportFormats = & $notification->getNotificationObject();
+ $reportFormats = self::$managedReportFormats;
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportParameters($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $availableParameters = & $notification->getNotificationObject();
+ $availableParameters = self::$availableParameters;
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function processReports($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $processedReports = & $notification->getNotificationObject();
+
+ $notificationInfo = $notification->getNotificationInfo();
+ $report = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY];
+
+ $displayFormat = $report['parameters'][self::DISPLAY_FORMAT_PARAMETER];
+ $evolutionGraph = $report['parameters'][self::EVOLUTION_GRAPH_PARAMETER];
+
+ foreach ($processedReports as &$processedReport) {
+ $metadata = $processedReport['metadata'];
+
+ $isAggregateReport = !empty($metadata['dimension']);
+
+ $processedReport['displayTable'] = $displayFormat != self::DISPLAY_FORMAT_GRAPHS_ONLY;
+
+ $processedReport['displayGraph'] =
+ ($isAggregateReport ?
+ $displayFormat == self::DISPLAY_FORMAT_GRAPHS_ONLY || $displayFormat == self::DISPLAY_FORMAT_TABLES_AND_GRAPHS
+ :
+ $displayFormat != self::DISPLAY_FORMAT_TABLES_ONLY)
+ && Piwik::isGdExtensionEnabled()
+ && Piwik_PluginsManager::getInstance()->isPluginActivated('ImageGraph')
+ && !empty($metadata['imageGraphUrl']);
+
+ $processedReport['evolutionGraph'] = $evolutionGraph;
+
+ // remove evolution metrics from MultiSites.getAll
+ if ($metadata['module'] == 'MultiSites') {
+ $columns = $processedReport['columns'];
+
+ foreach (Piwik_MultiSites_API::getApiMetrics($enhanced = true) as $metricSettings) {
+ unset($columns[$metricSettings[Piwik_MultiSites_API::METRIC_EVOLUTION_COL_NAME_KEY]]);
+ }
+
+ $processedReport['metadata'] = $metadata;
+ $processedReport['columns'] = $columns;
+ }
+ }
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getRendererInstance($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $reportRenderer = & $notification->getNotificationObject();
+ $notificationInfo = $notification->getNotificationInfo();
+
+ $reportFormat = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY]['format'];
+ $outputType = $notificationInfo[Piwik_PDFReports_API::OUTPUT_TYPE_INFO_KEY];
+
+ $reportRenderer = Piwik_ReportRenderer::factory($reportFormat);
+
+ if ($reportFormat == Piwik_ReportRenderer::HTML_FORMAT) {
+ $reportRenderer->setRenderImageInline($outputType != Piwik_PDFReports_API::OUTPUT_SAVE_ON_DISK);
+ }
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function allowMultipleReports($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $allowMultipleReports = & $notification->getNotificationObject();
+ $allowMultipleReports = true;
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function sendReport($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $notificationInfo = $notification->getNotificationInfo();
+ $report = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY];
+ $websiteName = $notificationInfo[Piwik_PDFReports_API::WEBSITE_NAME_KEY];
+ $prettyDate = $notificationInfo[Piwik_PDFReports_API::PRETTY_DATE_KEY];
+ $contents = $notificationInfo[Piwik_PDFReports_API::REPORT_CONTENT_KEY];
+ $filename = $notificationInfo[Piwik_PDFReports_API::FILENAME_KEY];
+ $additionalFiles = $notificationInfo[Piwik_PDFReports_API::ADDITIONAL_FILES_KEY];
+
+ $periods = self::getPeriodToFrequency();
+ $message = Piwik_Translate('PDFReports_EmailHello');
+ $subject = Piwik_Translate('General_Report') . ' ' . $websiteName . " - " . $prettyDate;
+
+ $mail = new Piwik_Mail();
+ $mail->setSubject($subject);
+ $fromEmailName = Piwik_Config::getInstance()->branding['use_custom_logo']
+ ? Piwik_Translate('CoreHome_WebAnalyticsReports')
+ : Piwik_Translate('PDFReports_PiwikReports');
+ $fromEmailAddress = Piwik_Config::getInstance()->General['noreply_email_address'];
+ $attachmentName = $subject;
+ $mail->setFrom($fromEmailAddress, $fromEmailName);
+
+ switch ($report['format']) {
+ case 'html':
+
+ // Needed when using images as attachment with cid
+ $mail->setType(Zend_Mime::MULTIPART_RELATED);
+ $message .= "<br/>" . Piwik_Translate('PDFReports_PleaseFindBelow', array($periods[$report['period']], $websiteName));
+ $mail->setBodyHtml($message . "<br/><br/>" . $contents);
+ break;
+
+ default:
+ case 'pdf':
+ $message .= "\n" . Piwik_Translate('PDFReports_PleaseFindAttachedFile', array($periods[$report['period']], $websiteName));
+ $mail->setBodyText($message);
+ $mail->createAttachment(
+ $contents,
+ 'application/pdf',
+ Zend_Mime::DISPOSITION_INLINE,
+ Zend_Mime::ENCODING_BASE64,
+ $attachmentName . '.pdf'
+ );
+ break;
+ }
+
+ foreach ($additionalFiles as $additionalFile) {
+ $fileContent = $additionalFile['content'];
+ $at = $mail->createAttachment(
+ $fileContent,
+ $additionalFile['mimeType'],
+ Zend_Mime::DISPOSITION_INLINE,
+ $additionalFile['encoding'],
+ $additionalFile['filename']
+ );
+ $at->id = $additionalFile['cid'];
+
+ unset($fileContent);
+ }
+
+ // Get user emails and languages
+ $reportParameters = $report['parameters'];
+ $emails = array();
+
+ if (isset($reportParameters[self::ADDITIONAL_EMAILS_PARAMETER])) {
+ $emails = $reportParameters[self::ADDITIONAL_EMAILS_PARAMETER];
+ }
+
+ if ($reportParameters[self::EMAIL_ME_PARAMETER] == 1) {
+ if (Piwik::getCurrentUserLogin() == $report['login']) {
+ $emails[] = Piwik::getCurrentUserEmail();
+ } elseif ($report['login'] == Piwik_Config::getInstance()->superuser['login']) {
+ $emails[] = Piwik::getSuperUserEmail();
+ } else {
+ try {
+ $user = Piwik_UsersManager_API::getInstance()->getUser($report['login']);
+ } catch (Exception $e) {
+ return;
+ }
+ $emails[] = $user['email'];
+ }
+ }
+
+ foreach ($emails as $email) {
+ if (empty($email)) {
+ continue;
+ }
+ $mail->addTo($email);
+
+ try {
+ $mail->send();
+ } catch (Exception $e) {
+
+ // If running from piwik.php with debug, we ignore the 'email not sent' error
+ if (!isset($GLOBALS['PIWIK_TRACKER_DEBUG']) || !$GLOBALS['PIWIK_TRACKER_DEBUG']) {
+ throw new Exception("An error occured while sending '$filename' " .
+ " to " . implode(', ', $mail->getRecipients()) .
+ ". Error was '" . $e->getMessage() . "'");
+ }
+ }
+ $mail->clearRecipients();
+ }
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportRecipients($notification)
+ {
+ if (self::manageEvent($notification)) {
+ $recipients = & $notification->getNotificationObject();
+ $notificationInfo = $notification->getNotificationInfo();
+
+ $report = $notificationInfo[Piwik_PDFReports_API::REPORT_KEY];
+ $parameters = $report['parameters'];
+ $eMailMe = $parameters[self::EMAIL_ME_PARAMETER];
+
+ if ($eMailMe) {
+ $recipients[] = Piwik::getCurrentUserEmail();
+ }
+
+ if (isset($parameters[self::ADDITIONAL_EMAILS_PARAMETER])) {
+ $additionalEMails = $parameters[self::ADDITIONAL_EMAILS_PARAMETER];
+ $recipients = array_merge($recipients, $additionalEMails);
+ }
+ $recipients = array_filter($recipients);
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ static public function template_reportParametersPDFReports($notification)
+ {
+ $out =& $notification->getNotificationObject();
+
+ $view = Piwik_View::factory('report_parameters');
+ $view->currentUserEmail = Piwik::getCurrentUserEmail();
+ $view->displayFormats = self::getDisplayFormats();
+ $view->reportType = self::EMAIL_TYPE;
+ $view->defaultDisplayFormat = self::DEFAULT_DISPLAY_FORMAT;
+ $view->defaultEmailMe = self::EMAIL_ME_PARAMETER_DEFAULT_VALUE ? 'true' : 'false';
+ $view->defaultEvolutionGraph = self::EVOLUTION_GRAPH_PARAMETER_DEFAULT_VALUE ? 'true' : 'false';
+ $out .= $view->render();
+ }
+
+ private static function manageEvent($notification)
+ {
+ $notificationInfo = $notification->getNotificationInfo();
+ return in_array(
+ $notificationInfo[Piwik_PDFReports_API::REPORT_TYPE_INFO_KEY],
+ array_keys(self::$managedReportTypes)
+ );
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getScheduledTasks($notification)
+ {
+ $arbitraryDateInUTC = Piwik_Date::factory('2011-01-01');
+ $tasks = & $notification->getNotificationObject();
+ foreach (Piwik_PDFReports_API::getInstance()->getReports() as $report) {
+ if (!$report['deleted'] && $report['period'] != Piwik_ScheduledTime::PERIOD_NEVER) {
+ $midnightInSiteTimezone =
+ date(
+ 'H',
+ Piwik_Date::factory(
+ $arbitraryDateInUTC,
+ Piwik_Site::getTimezoneFor($report['idsite'])
+ )->getTimestamp()
+ );
+
+ $hourInUTC = (24 - $midnightInSiteTimezone + $report['hour']) % 24;
+
+ $schedule = Piwik_ScheduledTime::getScheduledTimeForPeriod($report['period']);
+ $schedule->setHour($hourInUTC);
+ $tasks[] = new Piwik_ScheduledTask (
+ Piwik_PDFReports_API::getInstance(),
+ 'sendReport',
+ $report['idreport'], $schedule
+ );
+ }
+ }
+ }
function addTopMenu()
{
- Piwik_AddTopMenu(
- $this->getTopMenuTranslationKey(),
- array('module' => 'PDFReports', 'action' => 'index'),
- true,
- 13,
- $isHTML = false,
- $tooltip = Piwik_Translate(
- Piwik_PluginsManager::getInstance()->isPluginActivated('MobileMessaging')
- ? 'MobileMessaging_TopLinkTooltip' : 'PDFReports_TopLinkTooltip'
- )
- );
+ Piwik_AddTopMenu(
+ $this->getTopMenuTranslationKey(),
+ array('module' => 'PDFReports', 'action' => 'index'),
+ true,
+ 13,
+ $isHTML = false,
+ $tooltip = Piwik_Translate(
+ Piwik_PluginsManager::getInstance()->isPluginActivated('MobileMessaging')
+ ? 'MobileMessaging_TopLinkTooltip' : 'PDFReports_TopLinkTooltip'
+ )
+ );
}
- function getTopMenuTranslationKey()
- {
- // if MobileMessaging is not activated, display 'Email reports'
- if(!Piwik_PluginsManager::getInstance()->isPluginActivated('MobileMessaging'))
- return self::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY;
-
- if(Piwik::isUserIsAnonymous())
- {
- return self::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY;
- }
-
- $reports = Piwik_PDFReports_API::getInstance()->getReports();
- $reportCount = count($reports);
-
- // if there are no reports and the mobile account is
- // not configured, display 'Email reports'
- // configured, display 'Email & SMS reports'
- if($reportCount == 0)
- return Piwik_MobileMessaging_API::getInstance()->areSMSAPICredentialProvided() ?
- self::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY : self::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY;
-
- $anyMobileReport = false;
- foreach($reports as $report)
- {
- if($report['type'] == Piwik_MobileMessaging::MOBILE_TYPE)
- {
- $anyMobileReport = true;
- break;
- }
- }
-
- // if there is at least one sms report, display 'Email & SMS reports'
- if($anyMobileReport)
- {
- return self::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY;
- }
-
- return self::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
+ function getTopMenuTranslationKey()
+ {
+ // if MobileMessaging is not activated, display 'Email reports'
+ if (!Piwik_PluginsManager::getInstance()->isPluginActivated('MobileMessaging'))
+ return self::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY;
+
+ if (Piwik::isUserIsAnonymous()) {
+ return self::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY;
+ }
+
+ $reports = Piwik_PDFReports_API::getInstance()->getReports();
+ $reportCount = count($reports);
+
+ // if there are no reports and the mobile account is
+ // not configured, display 'Email reports'
+ // configured, display 'Email & SMS reports'
+ if ($reportCount == 0)
+ return Piwik_MobileMessaging_API::getInstance()->areSMSAPICredentialProvided() ?
+ self::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY : self::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY;
+
+ $anyMobileReport = false;
+ foreach ($reports as $report) {
+ if ($report['type'] == Piwik_MobileMessaging::MOBILE_TYPE) {
+ $anyMobileReport = true;
+ break;
+ }
+ }
+
+ // if there is at least one sms report, display 'Email & SMS reports'
+ if ($anyMobileReport) {
+ return self::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY;
+ }
+
+ return self::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
function deleteUserReport($notification)
- {
- $userLogin = $notification->getNotificationObject();
- Piwik_Query('DELETE FROM ' . Piwik_Common::prefixTable('report') . ' WHERE login = ?', $userLogin);
+ {
+ $userLogin = $notification->getNotificationObject();
+ Piwik_Query('DELETE FROM ' . Piwik_Common::prefixTable('report') . ' WHERE login = ?', $userLogin);
}
-
+
function install()
- {
- $queries[] = '
- CREATE TABLE `'.Piwik_Common::prefixTable('report').'` (
+ {
+ $queries[] = '
+ CREATE TABLE `' . Piwik_Common::prefixTable('report') . '` (
`idreport` INT(11) NOT NULL AUTO_INCREMENT,
`idsite` INTEGER(11) NOT NULL,
`login` VARCHAR(100) NOT NULL,
@@ -608,61 +567,54 @@ class Piwik_PDFReports extends Piwik_Plugin
PRIMARY KEY (`idreport`)
) DEFAULT CHARSET=utf8';
try {
- foreach($queries as $query)
- {
- Piwik_Exec($query);
- }
- }
- catch(Exception $e) {
- if(!Zend_Registry::get('db')->isErrNo($e, '1050'))
- {
- throw $e;
- }
- }
- }
-
- private static function checkAdditionalEmails($additionalEmails)
- {
- foreach($additionalEmails as &$email)
- {
- $email = trim($email);
- if(empty($email))
- {
- $email = false;
- }
- elseif(!Piwik::isValidEmailString($email))
- {
- throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidEmail') . ' ('.$email.')');
- }
- }
- $additionalEmails = array_filter($additionalEmails);
- return $additionalEmails;
- }
-
- private static function getDisplayFormats()
- {
- $displayFormats = array(
- // PDFReports_AggregateReportsFormat_TablesOnly should be named PDFReports_DisplayFormat_GraphsOnlyForKeyMetrics
- self::DISPLAY_FORMAT_GRAPHS_ONLY_FOR_KEY_METRICS => Piwik_Translate('PDFReports_AggregateReportsFormat_TablesOnly'),
- // PDFReports_AggregateReportsFormat_GraphsOnly should be named PDFReports_DisplayFormat_GraphsOnly
- self::DISPLAY_FORMAT_GRAPHS_ONLY => Piwik_Translate('PDFReports_AggregateReportsFormat_GraphsOnly'),
- // PDFReports_AggregateReportsFormat_TablesAndGraphs should be named PDFReports_DisplayFormat_TablesAndGraphs
- self::DISPLAY_FORMAT_TABLES_AND_GRAPHS => Piwik_Translate('PDFReports_AggregateReportsFormat_TablesAndGraphs'),
- self::DISPLAY_FORMAT_TABLES_ONLY => Piwik_Translate('PDFReports_DisplayFormat_TablesOnly'),
- );
- return $displayFormats;
- }
-
- /**
- * @ignore
- */
- static public function getPeriodToFrequency()
- {
- return array(
- Piwik_ScheduledTime::PERIOD_NEVER => Piwik_Translate('General_Never'),
- Piwik_ScheduledTime::PERIOD_DAY => Piwik_Translate('General_Daily'),
- Piwik_ScheduledTime::PERIOD_WEEK => Piwik_Translate('General_Weekly'),
- Piwik_ScheduledTime::PERIOD_MONTH => Piwik_Translate('General_Monthly'),
- );
- }
+ foreach ($queries as $query) {
+ Piwik_Exec($query);
+ }
+ } catch (Exception $e) {
+ if (!Zend_Registry::get('db')->isErrNo($e, '1050')) {
+ throw $e;
+ }
+ }
+ }
+
+ private static function checkAdditionalEmails($additionalEmails)
+ {
+ foreach ($additionalEmails as &$email) {
+ $email = trim($email);
+ if (empty($email)) {
+ $email = false;
+ } elseif (!Piwik::isValidEmailString($email)) {
+ throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidEmail') . ' (' . $email . ')');
+ }
+ }
+ $additionalEmails = array_filter($additionalEmails);
+ return $additionalEmails;
+ }
+
+ private static function getDisplayFormats()
+ {
+ $displayFormats = array(
+ // PDFReports_AggregateReportsFormat_TablesOnly should be named PDFReports_DisplayFormat_GraphsOnlyForKeyMetrics
+ self::DISPLAY_FORMAT_GRAPHS_ONLY_FOR_KEY_METRICS => Piwik_Translate('PDFReports_AggregateReportsFormat_TablesOnly'),
+ // PDFReports_AggregateReportsFormat_GraphsOnly should be named PDFReports_DisplayFormat_GraphsOnly
+ self::DISPLAY_FORMAT_GRAPHS_ONLY => Piwik_Translate('PDFReports_AggregateReportsFormat_GraphsOnly'),
+ // PDFReports_AggregateReportsFormat_TablesAndGraphs should be named PDFReports_DisplayFormat_TablesAndGraphs
+ self::DISPLAY_FORMAT_TABLES_AND_GRAPHS => Piwik_Translate('PDFReports_AggregateReportsFormat_TablesAndGraphs'),
+ self::DISPLAY_FORMAT_TABLES_ONLY => Piwik_Translate('PDFReports_DisplayFormat_TablesOnly'),
+ );
+ return $displayFormats;
+ }
+
+ /**
+ * @ignore
+ */
+ static public function getPeriodToFrequency()
+ {
+ return array(
+ Piwik_ScheduledTime::PERIOD_NEVER => Piwik_Translate('General_Never'),
+ Piwik_ScheduledTime::PERIOD_DAY => Piwik_Translate('General_Daily'),
+ Piwik_ScheduledTime::PERIOD_WEEK => Piwik_Translate('General_Weekly'),
+ Piwik_ScheduledTime::PERIOD_MONTH => Piwik_Translate('General_Monthly'),
+ );
+ }
}
diff --git a/plugins/PDFReports/config/tcpdf_config.php b/plugins/PDFReports/config/tcpdf_config.php
index 094fdd1985..096171d164 100644
--- a/plugins/PDFReports/config/tcpdf_config.php
+++ b/plugins/PDFReports/config/tcpdf_config.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_PDFReports
*/
@@ -15,223 +15,223 @@
* @package Piwik_PDFReports
*/
-define('K_PATH_MAIN', PIWIK_INCLUDE_PATH.'/libs/tcpdf/');
-define('K_PATH_CACHE', PIWIK_USER_PATH.'/tmp/tcpdf/');
-define('K_PATH_IMAGES', PIWIK_USER_PATH.'/tmp/tcpdf/');
+define('K_PATH_MAIN', PIWIK_INCLUDE_PATH . '/libs/tcpdf/');
+define('K_PATH_CACHE', PIWIK_USER_PATH . '/tmp/tcpdf/');
+define('K_PATH_IMAGES', PIWIK_USER_PATH . '/tmp/tcpdf/');
if (!defined('K_TCPDF_EXTERNAL_CONFIG')) {
- // DOCUMENT_ROOT fix for IIS Webserver
- if ((!isset($_SERVER['DOCUMENT_ROOT'])) OR (empty($_SERVER['DOCUMENT_ROOT']))) {
- if(isset($_SERVER['SCRIPT_FILENAME'])) {
- $_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr($_SERVER['SCRIPT_FILENAME'], 0, 0-strlen($_SERVER['PHP_SELF'])));
- } elseif(isset($_SERVER['PATH_TRANSLATED'])) {
- $_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0-strlen($_SERVER['PHP_SELF'])));
- } else {
- // define here your DOCUMENT_ROOT path if the previous fails
- $_SERVER['DOCUMENT_ROOT'] = '/var/www';
- }
- }
-
- if (!defined('K_PATH_MAIN')) {
- // Automatic calculation for the following K_PATH_MAIN constant
- $k_path_main = str_replace( '\\', '/', realpath(substr(dirname(__FILE__), 0, 0-strlen('config'))));
- if (substr($k_path_main, -1) != '/') {
- $k_path_main .= '/';
- }
-
- /**
- * Installation path (/var/www/tcpdf/).
- * By default it is automatically calculated but you can also set it as a fixed string to improve performances.
- */
- define ('K_PATH_MAIN', $k_path_main);
- }
-
- if (!defined('K_PATH_URL')) {
- // Automatic calculation for the following K_PATH_URL constant
- $k_path_url = K_PATH_MAIN; // default value for console mode
- if (isset($_SERVER['HTTP_HOST']) AND (!empty($_SERVER['HTTP_HOST']))) {
- if(isset($_SERVER['HTTPS']) AND (!empty($_SERVER['HTTPS'])) AND strtolower($_SERVER['HTTPS'])!='off') {
- $k_path_url = 'https://';
- } else {
- $k_path_url = 'http://';
- }
- $k_path_url .= $_SERVER['HTTP_HOST'];
- $k_path_url .= str_replace( '\\', '/', substr(K_PATH_MAIN, (strlen($_SERVER['DOCUMENT_ROOT']) - 1)));
- }
-
- /**
- * URL path to tcpdf installation folder (http://localhost/tcpdf/).
- * By default it is automatically calculated but you can also set it as a fixed string to improve performances.
- */
- define ('K_PATH_URL', $k_path_url);
- }
-
- /**
- * path for PDF fonts
- * use K_PATH_MAIN.'fonts/old/' for old non-UTF8 fonts
- */
- define ('K_PATH_FONTS', K_PATH_MAIN.'fonts/');
-
- /**
- * cache directory for temporary files (full path)
- */
- if (!defined('K_PATH_CACHE')) {
- define ('K_PATH_CACHE', K_PATH_MAIN.'cache/');
- }
-
- /**
- * cache directory for temporary files (url path)
- */
- define ('K_PATH_URL_CACHE', K_PATH_URL.'cache/');
-
- /**
- *images directory
- */
- if (!defined('K_PATH_IMAGES')) {
- define ('K_PATH_IMAGES', K_PATH_MAIN.'images/');
- }
-
- /**
- * blank image
- */
- define ('K_BLANK_IMAGE', K_PATH_IMAGES.'_blank.png');
-
- /**
- * page format
- */
- define ('PDF_PAGE_FORMAT', 'A4');
-
- /**
- * page orientation (P=portrait, L=landscape)
- */
- define ('PDF_PAGE_ORIENTATION', 'P');
-
- /**
- * document creator
- */
- define ('PDF_CREATOR', 'TCPDF');
-
- /**
- * document author
- */
- define ('PDF_AUTHOR', 'TCPDF');
-
- /**
- * header title
- */
- define ('PDF_HEADER_TITLE', 'TCPDF Example');
-
- /**
- * header description string
- */
- define ('PDF_HEADER_STRING', "by Nicola Asuni - Tecnick.com\nwww.tcpdf.org");
-
- /**
- * image logo
- */
- define ('PDF_HEADER_LOGO', 'tcpdf_logo.jpg');
-
- /**
- * header logo image width [mm]
- */
- define ('PDF_HEADER_LOGO_WIDTH', 30);
-
- /**
- * document unit of measure [pt=point, mm=millimeter, cm=centimeter, in=inch]
- */
- define ('PDF_UNIT', 'mm');
-
- /**
- * header margin
- */
- define ('PDF_MARGIN_HEADER', 5);
-
- /**
- * footer margin
- */
- define ('PDF_MARGIN_FOOTER', 10);
-
- /**
- * top margin
- */
- define ('PDF_MARGIN_TOP', 27);
-
- /**
- * bottom margin
- */
- define ('PDF_MARGIN_BOTTOM', 25);
-
- /**
- * left margin
- */
- define ('PDF_MARGIN_LEFT', 15);
-
- /**
- * right margin
- */
- define ('PDF_MARGIN_RIGHT', 15);
-
- /**
- * default main font name
- */
- define ('PDF_FONT_NAME_MAIN', 'helvetica');
-
- /**
- * default main font size
- */
- define ('PDF_FONT_SIZE_MAIN', 10);
-
- /**
- * default data font name
- */
- define ('PDF_FONT_NAME_DATA', 'helvetica');
-
- /**
- * default data font size
- */
- define ('PDF_FONT_SIZE_DATA', 8);
-
- /**
- * default monospaced font name
- */
- define ('PDF_FONT_MONOSPACED', 'courier');
-
- /**
- * ratio used to adjust the conversion of pixels to user units
- */
- define ('PDF_IMAGE_SCALE_RATIO', 1.25);
-
- /**
- * magnification factor for titles
- */
- define('HEAD_MAGNIFICATION', 1.1);
-
- /**
- * height of cell repect font height
- */
- define('K_CELL_HEIGHT_RATIO', 1.25);
-
- /**
- * title magnification respect main font size
- */
- define('K_TITLE_MAGNIFICATION', 1.3);
-
- /**
- * reduction factor for small font
- */
- define('K_SMALL_RATIO', 2/3);
-
- /**
- * set to true to enable the special procedure used to avoid the overlappind of symbols on Thai language
- */
- define('K_THAI_TOPCHARS', true);
-
- /**
- * if true allows to call TCPDF methods using HTML syntax
- * IMPORTANT: For security reason, disable this feature if you are printing user HTML content.
- */
- define('K_TCPDF_CALLS_IN_HTML', true);
+ // DOCUMENT_ROOT fix for IIS Webserver
+ if ((!isset($_SERVER['DOCUMENT_ROOT'])) OR (empty($_SERVER['DOCUMENT_ROOT']))) {
+ if (isset($_SERVER['SCRIPT_FILENAME'])) {
+ $_SERVER['DOCUMENT_ROOT'] = str_replace('\\', '/', substr($_SERVER['SCRIPT_FILENAME'], 0, 0 - strlen($_SERVER['PHP_SELF'])));
+ } elseif (isset($_SERVER['PATH_TRANSLATED'])) {
+ $_SERVER['DOCUMENT_ROOT'] = str_replace('\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0 - strlen($_SERVER['PHP_SELF'])));
+ } else {
+ // define here your DOCUMENT_ROOT path if the previous fails
+ $_SERVER['DOCUMENT_ROOT'] = '/var/www';
+ }
+ }
+
+ if (!defined('K_PATH_MAIN')) {
+ // Automatic calculation for the following K_PATH_MAIN constant
+ $k_path_main = str_replace('\\', '/', realpath(substr(dirname(__FILE__), 0, 0 - strlen('config'))));
+ if (substr($k_path_main, -1) != '/') {
+ $k_path_main .= '/';
+ }
+
+ /**
+ * Installation path (/var/www/tcpdf/).
+ * By default it is automatically calculated but you can also set it as a fixed string to improve performances.
+ */
+ define ('K_PATH_MAIN', $k_path_main);
+ }
+
+ if (!defined('K_PATH_URL')) {
+ // Automatic calculation for the following K_PATH_URL constant
+ $k_path_url = K_PATH_MAIN; // default value for console mode
+ if (isset($_SERVER['HTTP_HOST']) AND (!empty($_SERVER['HTTP_HOST']))) {
+ if (isset($_SERVER['HTTPS']) AND (!empty($_SERVER['HTTPS'])) AND strtolower($_SERVER['HTTPS']) != 'off') {
+ $k_path_url = 'https://';
+ } else {
+ $k_path_url = 'http://';
+ }
+ $k_path_url .= $_SERVER['HTTP_HOST'];
+ $k_path_url .= str_replace('\\', '/', substr(K_PATH_MAIN, (strlen($_SERVER['DOCUMENT_ROOT']) - 1)));
+ }
+
+ /**
+ * URL path to tcpdf installation folder (http://localhost/tcpdf/).
+ * By default it is automatically calculated but you can also set it as a fixed string to improve performances.
+ */
+ define ('K_PATH_URL', $k_path_url);
+ }
+
+ /**
+ * path for PDF fonts
+ * use K_PATH_MAIN.'fonts/old/' for old non-UTF8 fonts
+ */
+ define ('K_PATH_FONTS', K_PATH_MAIN . 'fonts/');
+
+ /**
+ * cache directory for temporary files (full path)
+ */
+ if (!defined('K_PATH_CACHE')) {
+ define ('K_PATH_CACHE', K_PATH_MAIN . 'cache/');
+ }
+
+ /**
+ * cache directory for temporary files (url path)
+ */
+ define ('K_PATH_URL_CACHE', K_PATH_URL . 'cache/');
+
+ /**
+ *images directory
+ */
+ if (!defined('K_PATH_IMAGES')) {
+ define ('K_PATH_IMAGES', K_PATH_MAIN . 'images/');
+ }
+
+ /**
+ * blank image
+ */
+ define ('K_BLANK_IMAGE', K_PATH_IMAGES . '_blank.png');
+
+ /**
+ * page format
+ */
+ define ('PDF_PAGE_FORMAT', 'A4');
+
+ /**
+ * page orientation (P=portrait, L=landscape)
+ */
+ define ('PDF_PAGE_ORIENTATION', 'P');
+
+ /**
+ * document creator
+ */
+ define ('PDF_CREATOR', 'TCPDF');
+
+ /**
+ * document author
+ */
+ define ('PDF_AUTHOR', 'TCPDF');
+
+ /**
+ * header title
+ */
+ define ('PDF_HEADER_TITLE', 'TCPDF Example');
+
+ /**
+ * header description string
+ */
+ define ('PDF_HEADER_STRING', "by Nicola Asuni - Tecnick.com\nwww.tcpdf.org");
+
+ /**
+ * image logo
+ */
+ define ('PDF_HEADER_LOGO', 'tcpdf_logo.jpg');
+
+ /**
+ * header logo image width [mm]
+ */
+ define ('PDF_HEADER_LOGO_WIDTH', 30);
+
+ /**
+ * document unit of measure [pt=point, mm=millimeter, cm=centimeter, in=inch]
+ */
+ define ('PDF_UNIT', 'mm');
+
+ /**
+ * header margin
+ */
+ define ('PDF_MARGIN_HEADER', 5);
+
+ /**
+ * footer margin
+ */
+ define ('PDF_MARGIN_FOOTER', 10);
+
+ /**
+ * top margin
+ */
+ define ('PDF_MARGIN_TOP', 27);
+
+ /**
+ * bottom margin
+ */
+ define ('PDF_MARGIN_BOTTOM', 25);
+
+ /**
+ * left margin
+ */
+ define ('PDF_MARGIN_LEFT', 15);
+
+ /**
+ * right margin
+ */
+ define ('PDF_MARGIN_RIGHT', 15);
+
+ /**
+ * default main font name
+ */
+ define ('PDF_FONT_NAME_MAIN', 'helvetica');
+
+ /**
+ * default main font size
+ */
+ define ('PDF_FONT_SIZE_MAIN', 10);
+
+ /**
+ * default data font name
+ */
+ define ('PDF_FONT_NAME_DATA', 'helvetica');
+
+ /**
+ * default data font size
+ */
+ define ('PDF_FONT_SIZE_DATA', 8);
+
+ /**
+ * default monospaced font name
+ */
+ define ('PDF_FONT_MONOSPACED', 'courier');
+
+ /**
+ * ratio used to adjust the conversion of pixels to user units
+ */
+ define ('PDF_IMAGE_SCALE_RATIO', 1.25);
+
+ /**
+ * magnification factor for titles
+ */
+ define('HEAD_MAGNIFICATION', 1.1);
+
+ /**
+ * height of cell repect font height
+ */
+ define('K_CELL_HEIGHT_RATIO', 1.25);
+
+ /**
+ * title magnification respect main font size
+ */
+ define('K_TITLE_MAGNIFICATION', 1.3);
+
+ /**
+ * reduction factor for small font
+ */
+ define('K_SMALL_RATIO', 2 / 3);
+
+ /**
+ * set to true to enable the special procedure used to avoid the overlappind of symbols on Thai language
+ */
+ define('K_THAI_TOPCHARS', true);
+
+ /**
+ * if true allows to call TCPDF methods using HTML syntax
+ * IMPORTANT: For security reason, disable this feature if you are printing user HTML content.
+ */
+ define('K_TCPDF_CALLS_IN_HTML', true);
}
// define the constant K_TCPDF_EXTERNAL_CONFIG to ignore tcpdf's default settings
diff --git a/plugins/PDFReports/templates/add.tpl b/plugins/PDFReports/templates/add.tpl
index ceccb9bcee..2149863a57 100644
--- a/plugins/PDFReports/templates/add.tpl
+++ b/plugins/PDFReports/templates/add.tpl
@@ -1,141 +1,145 @@
<div class='entityAddContainer' style='display:none'>
-<div class='entityCancel'>
- {'PDFReports_CancelAndReturnToReports'|translate:"<a class='entityCancelLink'>":"</a>"}
-</div>
-<div class='clear'></div>
-<form id='addEditReport'>
-<table class="dataTable entityTable">
- <thead>
- <tr class="first">
- <th colspan="2">{'PDFReports_CreateAndScheduleReport'|translate}</th>
- <tr>
- </thead>
- <tbody>
- <tr>
- <td class="first">{'General_Website'|translate} </td>
- <td style="width:650px">
- {$siteName}
- </td>
- </tr>
- <tr>
- <td class="first">{'General_Description'|translate} </td>
- <td>
- <textarea cols="30" rows="3" id="report_description" class="inp"></textarea>
- <div class="entityInlineHelp">
- {'PDFReports_DescriptionOnFirstPage'|translate}
- </div>
- </td>
- </tr>
- <tr>
- <td class="first">{'PDFReports_EmailSchedule'|translate}</td>
- <td>
- <select id="report_period" class="inp">
- {foreach from=$periods item=period key=periodId}
- <option value="{$periodId}">
- {$period}
- </option>
- {/foreach}
- </select>
-
- <div class="entityInlineHelp">
- {'PDFReports_WeeklyScheduleHelp'|translate}
- <br/>
- {'PDFReports_MonthlyScheduleHelp'|translate}
- <br/>
- {'PDFReports_ReportHour'|translate}
- <input type="text" style="height: 0.9em;padding-left: 8px;width: 17px;" id="report_hour" class="inp" size="1">
- {'PDFReports_OClock'|translate}
- </div>
- </td>
- </tr>
+ <div class='entityCancel'>
+ {'PDFReports_CancelAndReturnToReports'|translate:"<a class='entityCancelLink'>":"</a>"}
+ </div>
+ <div class='clear'></div>
+ <form id='addEditReport'>
+ <table class="dataTable entityTable">
+ <thead>
+ <tr class="first">
+ <th colspan="2">{'PDFReports_CreateAndScheduleReport'|translate}</th>
+ <tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="first">{'General_Website'|translate} </td>
+ <td style="width:650px">
+ {$siteName}
+ </td>
+ </tr>
+ <tr>
+ <td class="first">{'General_Description'|translate} </td>
+ <td>
+ <textarea cols="30" rows="3" id="report_description" class="inp"></textarea>
- <tr {if $reportTypes|@count eq 1}style='display:none'{/if}>
- <td class='first'>
- {'PDFReports_ReportType'|translate}
- </td>
- <td>
- <select id='report_type'>
- {foreach from=$reportTypes key=reportType item=reportTypeIcon}
- <option value="{$reportType}">{$reportType|upper}</option>
- {/foreach}
- </select>
- </td>
- </tr>
+ <div class="entityInlineHelp">
+ {'PDFReports_DescriptionOnFirstPage'|translate}
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td class="first">{'PDFReports_EmailSchedule'|translate}</td>
+ <td>
+ <select id="report_period" class="inp">
+ {foreach from=$periods item=period key=periodId}
+ <option value="{$periodId}">
+ {$period}
+ </option>
+ {/foreach}
+ </select>
- <tr>
- <td class='first'>
- {'PDFReports_ReportFormat'|translate}
- </td>
+ <div class="entityInlineHelp">
+ {'PDFReports_WeeklyScheduleHelp'|translate}
+ <br/>
+ {'PDFReports_MonthlyScheduleHelp'|translate}
+ <br/>
+ {'PDFReports_ReportHour'|translate}
+ <input type="text" style="height: 0.9em;padding-left: 8px;width: 17px;" id="report_hour" class="inp" size="1">
+ {'PDFReports_OClock'|translate}
+ </div>
+ </td>
+ </tr>
- <td>
- {foreach from=$reportFormatsByReportType key=reportType item=reportFormats}
- <select name='report_format' class='{$reportType}'>
- {foreach from=$reportFormats key=reportFormat item=reportFormatIcon}
- <option value="{$reportFormat}">{$reportFormat|upper}</option>
- {/foreach}
- </select>
- {/foreach}
- </td>
- </tr>
+ <tr {if $reportTypes|@count eq 1}style='display:none'{/if}>
+ <td class='first'>
+ {'PDFReports_ReportType'|translate}
+ </td>
+ <td>
+ <select id='report_type'>
+ {foreach from=$reportTypes key=reportType item=reportTypeIcon}
+ <option value="{$reportType}">{$reportType|upper}</option>
+ {/foreach}
+ </select>
+ </td>
+ </tr>
- {postEvent name="template_reportParametersPDFReports"}
+ <tr>
+ <td class='first'>
+ {'PDFReports_ReportFormat'|translate}
+ </td>
- <tr>
- <td class="first">{'PDFReports_ReportsIncluded'|translate}</td>
- <td>
- {foreach from=$reportsByCategoryByReportType key=reportType item=reportsByCategory}
- <div name='reportsList' class='{$reportType}'>
+ <td>
+ {foreach from=$reportFormatsByReportType key=reportType item=reportFormats}
+ <select name='report_format' class='{$reportType}'>
+ {foreach from=$reportFormats key=reportFormat item=reportFormatIcon}
+ <option value="{$reportFormat}">{$reportFormat|upper}</option>
+ {/foreach}
+ </select>
+ {/foreach}
+ </td>
+ </tr>
- {if $allowMultipleReportsByReportType[$reportType]}
- {assign var=reportInputType value='checkbox'}
- {else}
- {assign var=reportInputType value='radio'}
- {/if}
+ {postEvent name="template_reportParametersPDFReports"}
- {assign var=countCategory value=0}
+ <tr>
+ <td class="first">{'PDFReports_ReportsIncluded'|translate}</td>
+ <td>
+ {foreach from=$reportsByCategoryByReportType key=reportType item=reportsByCategory}
+ <div name='reportsList' class='{$reportType}'>
- {math
- equation="ceil (reportsByCategoryCount / 2)"
- reportsByCategoryCount=$reportsByCategory|@count
- assign=newColumnAfter
- }
+ {if $allowMultipleReportsByReportType[$reportType]}
+ {assign var=reportInputType value='checkbox'}
+ {else}
+ {assign var=reportInputType value='radio'}
+ {/if}
- <div id='leftcolumn'>
- {foreach from=$reportsByCategory item=reports key=category name=reports}
- {if $countCategory >= $newColumnAfter && $newColumnAfter != 0}
- {assign var=newColumnAfter value=0}
- </div><div id='rightcolumn'>
- {/if}
- <div class='reportCategory'>{$category}</div><ul class='listReports'>
- {foreach from=$reports item=report}
- <li>
- <input type='{$reportInputType}' id="{$reportType}{$report.uniqueId}" report-unique-id='{$report.uniqueId}' name='{$reportType}Reports'/>
- <label for="{$reportType}{$report.uniqueId}">
- {$report.name|escape:"html"}
- {if $report.uniqueId=='MultiSites_getAll'}
- <div class="entityInlineHelp">{'PDFReports_ReportIncludeNWebsites'|translate:"$countWebsites "}</div>
- {/if}
- </label>
- </li>
- {/foreach}
- {assign var=countCategory value=$countCategory+1}
- </ul>
- <br/>
- {/foreach}
- </div>
- </div>
- {/foreach}
- </td>
- </tr>
-
- </tbody>
-</table>
+ {assign var=countCategory value=0}
- <input type="hidden" id="report_idreport" value="">
- <input type="submit" id="report_submit" name="submit" class="submit"/>
+ {math
+ equation="ceil (reportsByCategoryCount / 2)"
+ reportsByCategoryCount=$reportsByCategory|@count
+ assign=newColumnAfter
+ }
-</form>
-<div class='entityCancel'>
- {'General_OrCancel'|translate:"<a class='entityCancelLink'>":"</a>"}
-</div>
+ <div id='leftcolumn'>
+ {foreach from=$reportsByCategory item=reports key=category name=reports}
+ {if $countCategory >= $newColumnAfter && $newColumnAfter != 0}
+ {assign var=newColumnAfter value=0}
+ </div>
+ <div id='rightcolumn'>
+ {/if}
+ <div class='reportCategory'>{$category}</div>
+ <ul class='listReports'>
+ {foreach from=$reports item=report}
+ <li>
+ <input type='{$reportInputType}' id="{$reportType}{$report.uniqueId}" report-unique-id='{$report.uniqueId}'
+ name='{$reportType}Reports'/>
+ <label for="{$reportType}{$report.uniqueId}">
+ {$report.name|escape:"html"}
+ {if $report.uniqueId=='MultiSites_getAll'}
+ <div class="entityInlineHelp">{'PDFReports_ReportIncludeNWebsites'|translate:"$countWebsites "}</div>
+ {/if}
+ </label>
+ </li>
+ {/foreach}
+ {assign var=countCategory value=$countCategory+1}
+ </ul>
+ <br/>
+ {/foreach}
+ </div>
+ </div>
+ {/foreach}
+ </td>
+ </tr>
+
+ </tbody>
+ </table>
+
+ <input type="hidden" id="report_idreport" value="">
+ <input type="submit" id="report_submit" name="submit" class="submit"/>
+
+ </form>
+ <div class='entityCancel'>
+ {'General_OrCancel'|translate:"<a class='entityCancelLink'>":"</a>"}
+ </div>
</div> \ No newline at end of file
diff --git a/plugins/PDFReports/templates/index.tpl b/plugins/PDFReports/templates/index.tpl
index 1479c8d6cc..e47b41e665 100644
--- a/plugins/PDFReports/templates/index.tpl
+++ b/plugins/PDFReports/templates/index.tpl
@@ -7,41 +7,41 @@
</div>
<div class="centerLargeDiv">
- <h2>{'PDFReports_ManageEmailReports'|translate}</h2>
-
- <div class="entityContainer">
- {ajaxErrorDiv}
- {ajaxLoadingDiv}
- {include file="PDFReports/templates/list.tpl"}
- {include file="PDFReports/templates/add.tpl"}
- <a id='bottom'></a>
- </div>
+ <h2>{'PDFReports_ManageEmailReports'|translate}</h2>
+
+ <div class="entityContainer">
+ {ajaxErrorDiv}
+ {ajaxLoadingDiv}
+ {include file="PDFReports/templates/list.tpl"}
+ {include file="PDFReports/templates/add.tpl"}
+ <a id='bottom'></a>
+ </div>
</div>
<div class="ui-confirm" id="confirm">
- <h2>{'PDFReports_AreYouSureDeleteReport'|translate}</h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_No'|translate}" />
-</div>
+ <h2>{'PDFReports_AreYouSureDeleteReport'|translate}</h2>
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_No'|translate}"/>
+</div>
<script type="text/javascript">
-var ReportPlugin = new Object();
-ReportPlugin.defaultPeriod = '{$defaultPeriod}';
-ReportPlugin.defaultHour = '{$defaultHour}';
-ReportPlugin.defaultReportType = '{$defaultReportType}';
-ReportPlugin.defaultReportFormat = '{$defaultReportFormat}';
-ReportPlugin.reportList = {$reportsJSON};
-ReportPlugin.createReportString = "{'PDFReports_CreateReport'|translate}";
-ReportPlugin.updateReportString = "{'PDFReports_UpdateReport'|translate}";
-{literal}
-$(document).ready( function() {
- initManagePdf();
-});
+ var ReportPlugin = new Object();
+ ReportPlugin.defaultPeriod = '{$defaultPeriod}';
+ ReportPlugin.defaultHour = '{$defaultHour}';
+ ReportPlugin.defaultReportType = '{$defaultReportType}';
+ ReportPlugin.defaultReportFormat = '{$defaultReportFormat}';
+ ReportPlugin.reportList = {$reportsJSON};
+ ReportPlugin.createReportString = "{'PDFReports_CreateReport'|translate}";
+ ReportPlugin.updateReportString = "{'PDFReports_UpdateReport'|translate}";
+ {literal}
+ $(document).ready(function () {
+ initManagePdf();
+ });
</script>
-<style type="text/css">
-.reportCategory {
- font-weight:bold;
- margin-bottom:5px;
-}
-</style>
+ <style type="text/css">
+ .reportCategory {
+ font-weight: bold;
+ margin-bottom: 5px;
+ }
+ </style>
{/literal}
diff --git a/plugins/PDFReports/templates/list.tpl b/plugins/PDFReports/templates/list.tpl
index d21ef65b92..c67c5ac024 100644
--- a/plugins/PDFReports/templates/list.tpl
+++ b/plugins/PDFReports/templates/list.tpl
@@ -1,90 +1,96 @@
<div id='entityEditContainer'>
- <table class="dataTable entityTable">
- <thead>
- <tr>
- <th class="first">{'General_Description'|translate}</th>
- <th>{'PDFReports_EmailSchedule'|translate}</th>
- <th>{'PDFReports_ReportFormat'|translate}</th>
- <th>{'PDFReports_SendReportTo'|translate}</th>
- <th>{'General_Download'|translate}</th>
- <th>{'General_Edit'|translate}</th>
- <th>{'General_Delete'|translate}</th>
- </tr>
- </thead>
-
- {if $userLogin=='anonymous'}
- <tr><td colspan='7'>
- <br/>
- {'PDFReports_MustBeLoggedIn'|translate}
- <br/>&rsaquo; <a href='index.php?module={$loginModule}'>{'Login_LogIn'|translate}</a></strong>
- <br/><br/>
- </td></tr>
- </table>
- {elseif empty($reports)}
- <tr><td colspan='7'>
- <br/>
- {'PDFReports_ThereIsNoReportToManage'|translate:$siteName}.
- <br/><br/>
- <a onclick='' id='linkAddReport'>&rsaquo; {'PDFReports_CreateAndScheduleReport'|translate}</a>
- <br/><br/>
- </td></tr>
- </table>
- {else}
- {foreach from=$reports item=report}
- <tr>
- <td class="first">{$report.description}</td>
- <td>{$periods[$report.period]}
- <!-- Last sent on {$report.ts_last_sent} -->
- </td>
- <td>
- {if !empty($report.format)}
- {$report.format|upper}
- {/if}
- </td>
- <td>
- {*report recipients*}
- {if $report.recipients|@count eq 0}
- {'PDFReports_NoRecipients'|translate}
- {else}
- {foreach name=recipients from=$report.recipients item=recipient}
- {$recipient}<br/>
- {/foreach}
- {*send now link*}
- <a href='#' idreport='{$report.idreport}' name='linkSendNow' class="link_but" style='margin-top:3px'>
- <img border=0 src='{$reportTypes[$report.type]}'/>
- {'PDFReports_SendReportNow'|translate}
- </a>
- {/if}
- </td>
- <td>
- {*download link*}
- <a href="{url module=API token_auth=$token_auth method='PDFReports.generateReport' date=$rawDate idReport=$report.idreport outputType=$downloadOutputType language=$language}"
- target="_blank" name="linkDownloadReport" id="{$report.idreport}" class="link_but">
- <img src='{$reportFormatsByReportType[$report.type][$report.format]}' border="0" />
- {'General_Download'|translate}
- </a>
- </td>
- <td>
- {*edit link*}
- <a href='#' name="linkEditReport" id="{$report.idreport}" class="link_but">
- <img src='themes/default/images/ico_edit.png' border="0" />
- {'General_Edit'|translate}
- </a>
- </td>
- <td>
- {*delete link *}
- <a href='#' name="linkDeleteReport" id="{$report.idreport}" class="link_but">
- <img src='themes/default/images/ico_delete.png' border="0" />
- {'General_Delete'|translate}
- </a>
- </td>
- </tr>
- {/foreach}
- </table>
- {if $userLogin != 'anonymous'}
- <br/>
- <a onclick='' id='linkAddReport'>&rsaquo; {'PDFReports_CreateAndScheduleReport'|translate}</a>
- <br/><br/>
- {/if}
- {/if}
+ <table class="dataTable entityTable">
+ <thead>
+ <tr>
+ <th class="first">{'General_Description'|translate}</th>
+ <th>{'PDFReports_EmailSchedule'|translate}</th>
+ <th>{'PDFReports_ReportFormat'|translate}</th>
+ <th>{'PDFReports_SendReportTo'|translate}</th>
+ <th>{'General_Download'|translate}</th>
+ <th>{'General_Edit'|translate}</th>
+ <th>{'General_Delete'|translate}</th>
+ </tr>
+ </thead>
+
+ {if $userLogin=='anonymous'}
+ <tr>
+ <td colspan='7'>
+ <br/>
+ {'PDFReports_MustBeLoggedIn'|translate}
+ <br/>&rsaquo; <a href='index.php?module={$loginModule}'>{'Login_LogIn'|translate}</a></strong>
+ <br/><br/>
+ </td>
+ </tr>
+ </table>
+ {elseif empty($reports)}
+ <tr>
+ <td colspan='7'>
+ <br/>
+ {'PDFReports_ThereIsNoReportToManage'|translate:$siteName}.
+ <br/><br/>
+ <a onclick='' id='linkAddReport'>&rsaquo; {'PDFReports_CreateAndScheduleReport'|translate}</a>
+ <br/><br/>
+ </td>
+ </tr>
+ </table>
+ {else}
+ {foreach from=$reports item=report}
+ <tr>
+ <td class="first">{$report.description}</td>
+ <td>{$periods[$report.period]}
+ <!-- Last sent on {$report.ts_last_sent} -->
+ </td>
+ <td>
+ {if !empty($report.format)}
+ {$report.format|upper}
+ {/if}
+ </td>
+ <td>
+ {*report recipients*}
+ {if $report.recipients|@count eq 0}
+ {'PDFReports_NoRecipients'|translate}
+ {else}
+ {foreach name=recipients from=$report.recipients item=recipient}
+ {$recipient}
+ <br/>
+ {/foreach}
+ {*send now link*}
+ <a href='#' idreport='{$report.idreport}' name='linkSendNow' class="link_but" style='margin-top:3px'>
+ <img border=0 src='{$reportTypes[$report.type]}'/>
+ {'PDFReports_SendReportNow'|translate}
+ </a>
+ {/if}
+ </td>
+ <td>
+ {*download link*}
+ <a href="{url module=API token_auth=$token_auth method='PDFReports.generateReport' date=$rawDate idReport=$report.idreport outputType=$downloadOutputType language=$language}"
+ target="_blank" name="linkDownloadReport" id="{$report.idreport}" class="link_but">
+ <img src='{$reportFormatsByReportType[$report.type][$report.format]}' border="0"/>
+ {'General_Download'|translate}
+ </a>
+ </td>
+ <td>
+ {*edit link*}
+ <a href='#' name="linkEditReport" id="{$report.idreport}" class="link_but">
+ <img src='themes/default/images/ico_edit.png' border="0"/>
+ {'General_Edit'|translate}
+ </a>
+ </td>
+ <td>
+ {*delete link *}
+ <a href='#' name="linkDeleteReport" id="{$report.idreport}" class="link_but">
+ <img src='themes/default/images/ico_delete.png' border="0"/>
+ {'General_Delete'|translate}
+ </a>
+ </td>
+ </tr>
+ {/foreach}
+ </table>
+ {if $userLogin != 'anonymous'}
+ <br/>
+ <a onclick='' id='linkAddReport'>&rsaquo; {'PDFReports_CreateAndScheduleReport'|translate}</a>
+ <br/>
+ <br/>
+ {/if}
+ {/if}
</div>
diff --git a/plugins/PDFReports/templates/pdf.js b/plugins/PDFReports/templates/pdf.js
index 221418f318..4115dcc3f1 100644
--- a/plugins/PDFReports/templates/pdf.js
+++ b/plugins/PDFReports/templates/pdf.js
@@ -9,92 +9,83 @@ var getReportParametersFunctions = Object();
var updateReportParametersFunctions = Object();
var resetReportParametersFunctions = Object();
-function formSetEditReport(idReport)
-{
- var report = {
- 'type' : ReportPlugin.defaultReportType,
- 'format' : ReportPlugin.defaultReportFormat,
- 'description' : '',
- 'period' : ReportPlugin.defaultPeriod,
- 'hour' : ReportPlugin.defaultHour,
- 'reports' : []
- };
-
- if(idReport > 0)
- {
- report = ReportPlugin.reportList[idReport];
- $('#report_submit').val(ReportPlugin.updateReportString);
- }
- else
- {
- $('#report_submit').val(ReportPlugin.createReportString);
- }
-
- toggleReportType(report.type);
-
- $('#report_description').html(report.description);
- $('#report_type option[value='+report.type+']').prop('selected', 'selected');
- $('#report_period option[value='+report.period+']').prop('selected', 'selected');
- $('#report_hour').val(report.hour);
- $('[name=report_format].'+report.type+' option[value='+report.format+']').prop('selected', 'selected');
-
- $('[name=reportsList] input').prop('checked', false);
-
- var key;
- for(key in report.reports)
- {
- $('.' + report.type + ' [report-unique-id=' + report.reports[key] + ']').prop('checked','checked');
- }
-
- updateReportParametersFunctions[report.type](report.parameters);
-
- $('#report_idreport').val(idReport);
+function formSetEditReport(idReport) {
+ var report = {
+ 'type': ReportPlugin.defaultReportType,
+ 'format': ReportPlugin.defaultReportFormat,
+ 'description': '',
+ 'period': ReportPlugin.defaultPeriod,
+ 'hour': ReportPlugin.defaultHour,
+ 'reports': []
+ };
+
+ if (idReport > 0) {
+ report = ReportPlugin.reportList[idReport];
+ $('#report_submit').val(ReportPlugin.updateReportString);
+ }
+ else {
+ $('#report_submit').val(ReportPlugin.createReportString);
+ }
+
+ toggleReportType(report.type);
+
+ $('#report_description').html(report.description);
+ $('#report_type option[value=' + report.type + ']').prop('selected', 'selected');
+ $('#report_period option[value=' + report.period + ']').prop('selected', 'selected');
+ $('#report_hour').val(report.hour);
+ $('[name=report_format].' + report.type + ' option[value=' + report.format + ']').prop('selected', 'selected');
+
+ $('[name=reportsList] input').prop('checked', false);
+
+ var key;
+ for (key in report.reports) {
+ $('.' + report.type + ' [report-unique-id=' + report.reports[key] + ']').prop('checked', 'checked');
+ }
+
+ updateReportParametersFunctions[report.type](report.parameters);
+
+ $('#report_idreport').val(idReport);
}
-function getReportAjaxRequest(idReport, defaultApiMethod)
-{
- var parameters = {};
- piwikHelper.lazyScrollTo(".entityContainer", 400);
- parameters.module = 'API';
- parameters.method = defaultApiMethod;
- if(idReport == 0)
- {
- parameters.method = 'PDFReports.addReport';
- }
- parameters.format = 'json';
- return parameters;
+function getReportAjaxRequest(idReport, defaultApiMethod) {
+ var parameters = {};
+ piwikHelper.lazyScrollTo(".entityContainer", 400);
+ parameters.module = 'API';
+ parameters.method = defaultApiMethod;
+ if (idReport == 0) {
+ parameters.method = 'PDFReports.addReport';
+ }
+ parameters.format = 'json';
+ return parameters;
}
-function toggleReportType(reportType)
-{
- resetReportParametersFunctions[reportType]();
- $('#report_type option').each(function(index, type) {
- $('.'+$(type).val()).hide();
- });
- $('.'+reportType).show();
+function toggleReportType(reportType) {
+ resetReportParametersFunctions[reportType]();
+ $('#report_type option').each(function (index, type) {
+ $('.' + $(type).val()).hide();
+ });
+ $('.' + reportType).show();
}
-function initManagePdf()
-{
- // Click Add/Update Submit
- $('#addEditReport').submit( function() {
- var idReport = $('#report_idreport').val();
- var apiParameters = getReportAjaxRequest(idReport, 'PDFReports.updateReport');
- apiParameters.idReport = idReport;
- apiParameters.description = $('#report_description').val();
- apiParameters.reportType = $('#report_type option:selected').val();
- apiParameters.reportFormat = $('[name=report_format].'+apiParameters.reportType+' option:selected').val();
-
- var reports = [];
- $('[name=reportsList].'+apiParameters.reportType+' input:checked').each(function() {
- reports.push($(this).attr('report-unique-id'));
- });
- if(reports.length > 0)
- {
- apiParameters.reports = reports;
- }
-
- apiParameters.parameters = getReportParametersFunctions[apiParameters.reportType]();
+function initManagePdf() {
+ // Click Add/Update Submit
+ $('#addEditReport').submit(function () {
+ var idReport = $('#report_idreport').val();
+ var apiParameters = getReportAjaxRequest(idReport, 'PDFReports.updateReport');
+ apiParameters.idReport = idReport;
+ apiParameters.description = $('#report_description').val();
+ apiParameters.reportType = $('#report_type option:selected').val();
+ apiParameters.reportFormat = $('[name=report_format].' + apiParameters.reportType + ' option:selected').val();
+
+ var reports = [];
+ $('[name=reportsList].' + apiParameters.reportType + ' input:checked').each(function () {
+ reports.push($(this).attr('report-unique-id'));
+ });
+ if (reports.length > 0) {
+ apiParameters.reports = reports;
+ }
+
+ apiParameters.parameters = getReportParametersFunctions[apiParameters.reportType]();
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams(apiParameters, 'POST');
@@ -104,27 +95,27 @@ function initManagePdf()
ajaxHandler.setLoadingElement();
ajaxHandler.send(true);
return false;
- });
-
- // Email now
- $('a[name=linkSendNow]').click(function(){
- var idReport = $(this).attr('idreport');
- var parameters = getReportAjaxRequest(idReport, 'PDFReports.sendReport');
- parameters.idReport = idReport;
+ });
+
+ // Email now
+ $('a[name=linkSendNow]').click(function () {
+ var idReport = $(this).attr('idreport');
+ var parameters = getReportAjaxRequest(idReport, 'PDFReports.sendReport');
+ parameters.idReport = idReport;
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams(parameters, 'POST');
ajaxHandler.setLoadingElement();
ajaxHandler.send(true);
});
-
- // Delete Report
- $('a[name=linkDeleteReport]').click(function(){
- var idReport = $(this).attr('id');
- function onDelete()
- {
- var parameters = getReportAjaxRequest(idReport, 'PDFReports.deleteReport');
- parameters.idReport = idReport;
+
+ // Delete Report
+ $('a[name=linkDeleteReport]').click(function () {
+ var idReport = $(this).attr('id');
+
+ function onDelete() {
+ var parameters = getReportAjaxRequest(idReport, 'PDFReports.deleteReport');
+ parameters.idReport = idReport;
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams(parameters, 'POST');
@@ -132,34 +123,35 @@ function initManagePdf()
ajaxHandler.setLoadingElement();
ajaxHandler.send(true);
}
- piwikHelper.modalConfirm( '#confirm', {yes: onDelete});
- });
-
- // Edit Report click
- $('a[name=linkEditReport]').click(function(){
- var idReport = $(this).attr('id');
- formSetEditReport( idReport );
- $('.entityAddContainer').show();
- $('#entityEditContainer').hide();
- });
-
- // Switch Report Type
- $('#report_type').change(function(){
- var reportType = $(this).val();
- toggleReportType(reportType);
- });
-
- // Add a Report click
- $('#linkAddReport').click(function(){
- $('.entityAddContainer').show();
- $('#entityEditContainer').hide();
- formSetEditReport( idReport = 0 );
- });
-
- // Cancel click
- $('.entityCancelLink').click(function(){
- $('.entityAddContainer').hide();
- $('#entityEditContainer').show();
- piwikHelper.hideAjaxError();
- }).click();
+
+ piwikHelper.modalConfirm('#confirm', {yes: onDelete});
+ });
+
+ // Edit Report click
+ $('a[name=linkEditReport]').click(function () {
+ var idReport = $(this).attr('id');
+ formSetEditReport(idReport);
+ $('.entityAddContainer').show();
+ $('#entityEditContainer').hide();
+ });
+
+ // Switch Report Type
+ $('#report_type').change(function () {
+ var reportType = $(this).val();
+ toggleReportType(reportType);
+ });
+
+ // Add a Report click
+ $('#linkAddReport').click(function () {
+ $('.entityAddContainer').show();
+ $('#entityEditContainer').hide();
+ formSetEditReport(idReport = 0);
+ });
+
+ // Cancel click
+ $('.entityCancelLink').click(function () {
+ $('.entityAddContainer').hide();
+ $('#entityEditContainer').show();
+ piwikHelper.hideAjaxError();
+ }).click();
}
diff --git a/plugins/PDFReports/templates/report_parameters.tpl b/plugins/PDFReports/templates/report_parameters.tpl
index 053a686bed..1d2cb3c04a 100644
--- a/plugins/PDFReports/templates/report_parameters.tpl
+++ b/plugins/PDFReports/templates/report_parameters.tpl
@@ -1,99 +1,104 @@
<script>
- function updateEvolutionGraphParameterVisibility ()
- {ldelim}
- var evolutionGraphParameterInput = $('.report_evolution_graph');
- var nonApplicableDisplayFormats = ['1','4'];
- $.inArray($('#display_format option:selected').val(), nonApplicableDisplayFormats) != -1 ?
- evolutionGraphParameterInput.hide() : evolutionGraphParameterInput.show();
- {rdelim}
-
- $(function() {ldelim}
-
- resetReportParametersFunctions ['{$reportType}'] =
- function () {ldelim}
-
- var reportParameters = {ldelim}
- 'displayFormat' : '{$defaultDisplayFormat}',
- 'emailMe' : {$defaultEmailMe},
- 'evolutionGraph' : {$defaultEvolutionGraph},
- 'additionalEmails' : null
- {rdelim};
-
- updateReportParametersFunctions['{$reportType}'](reportParameters);
- {rdelim};
-
- updateReportParametersFunctions['{$reportType}'] =
- function (reportParameters) {ldelim}
-
- if(reportParameters == null) return;
-
- $('#display_format option[value='+reportParameters.displayFormat+']').prop('selected', 'selected');
- updateEvolutionGraphParameterVisibility();
-
- if(reportParameters.emailMe === true)
- $('#report_email_me').prop('checked', 'checked');
- else
- $('#report_email_me').removeProp('checked');
-
- if(reportParameters.evolutionGraph === true)
- $('#report_evolution_graph').prop('checked', 'checked');
- else
- $('#report_evolution_graph').removeProp('checked');
-
- if(reportParameters.additionalEmails != null)
- $('#report_additional_emails').text(reportParameters.additionalEmails.join('\n'));
- else
- $('#report_additional_emails').html('');
- {rdelim};
-
- getReportParametersFunctions['{$reportType}'] =
- function () {ldelim}
-
- var parameters = Object();
-
- parameters.displayFormat = $('#display_format option:selected').val();
- parameters.emailMe = $('#report_email_me').prop('checked');
- parameters.evolutionGraph = $('#report_evolution_graph').prop('checked');
-
- additionalEmails = $('#report_additional_emails').val();
- parameters.additionalEmails =
- additionalEmails != '' ? additionalEmails.split('\n') : [];
-
- return parameters;
- {rdelim};
-
- $('#display_format').change(updateEvolutionGraphParameterVisibility);
-
- {rdelim});
+ function updateEvolutionGraphParameterVisibility() {ldelim}
+ var evolutionGraphParameterInput = $('.report_evolution_graph');
+ var nonApplicableDisplayFormats = ['1', '4'];
+ $.inArray($('#display_format option:selected').val(), nonApplicableDisplayFormats) != -1 ?
+ evolutionGraphParameterInput.hide() : evolutionGraphParameterInput.show();
+ {rdelim
+ }
+
+ $(function () {ldelim}
+
+ resetReportParametersFunctions ['{$reportType}'] =
+ function () {ldelim}
+
+ var reportParameters = {ldelim}
+ 'displayFormat': '{$defaultDisplayFormat}',
+ 'emailMe': {$defaultEmailMe},
+ 'evolutionGraph': {$defaultEvolutionGraph},
+ 'additionalEmails': null
+ {rdelim};
+
+ updateReportParametersFunctions['{$reportType}'](reportParameters);
+ {rdelim
+ };
+
+ updateReportParametersFunctions['{$reportType}'] =
+ function (reportParameters) {ldelim}
+
+ if (reportParameters == null) return;
+
+ $('#display_format option[value=' + reportParameters.displayFormat + ']').prop('selected', 'selected');
+ updateEvolutionGraphParameterVisibility();
+
+ if (reportParameters.emailMe === true)
+ $('#report_email_me').prop('checked', 'checked');
+ else
+ $('#report_email_me').removeProp('checked');
+
+ if (reportParameters.evolutionGraph === true)
+ $('#report_evolution_graph').prop('checked', 'checked');
+ else
+ $('#report_evolution_graph').removeProp('checked');
+
+ if (reportParameters.additionalEmails != null)
+ $('#report_additional_emails').text(reportParameters.additionalEmails.join('\n'));
+ else
+ $('#report_additional_emails').html('');
+ {rdelim
+ };
+
+ getReportParametersFunctions['{$reportType}'] =
+ function () {ldelim}
+
+ var parameters = Object();
+
+ parameters.displayFormat = $('#display_format option:selected').val();
+ parameters.emailMe = $('#report_email_me').prop('checked');
+ parameters.evolutionGraph = $('#report_evolution_graph').prop('checked');
+
+ additionalEmails = $('#report_additional_emails').val();
+ parameters.additionalEmails =
+ additionalEmails != '' ? additionalEmails.split('\n') : [];
+
+ return parameters;
+ {rdelim
+ };
+
+ $('#display_format').change(updateEvolutionGraphParameterVisibility);
+
+ {rdelim
+ });
</script>
<tr class='{$reportType}'>
- <td style='width:240px;' class="first">{'PDFReports_SendReportTo'|translate}
- </td>
- <td>
- <input type="checkbox" id="report_email_me"/>
- <label for="report_email_me">{'PDFReports_SentToMe'|translate} (<i>{$currentUserEmail}</i>) </label>
- <br/><br/>
- {'PDFReports_AlsoSendReportToTheseEmails'|translate}<br/>
- <textarea cols="30" rows="3" id="report_additional_emails" class="inp"></textarea>
- </td>
+ <td style='width:240px;' class="first">{'PDFReports_SendReportTo'|translate}
+ </td>
+ <td>
+ <input type="checkbox" id="report_email_me"/>
+ <label for="report_email_me">{'PDFReports_SentToMe'|translate} (<i>{$currentUserEmail}</i>) </label>
+ <br/><br/>
+ {'PDFReports_AlsoSendReportToTheseEmails'|translate}<br/>
+ <textarea cols="30" rows="3" id="report_additional_emails" class="inp"></textarea>
+ </td>
</tr>
<tr class='{$reportType}'>
- <td class="first">
- {*PDFReports_AggregateReportsFormat should be named PDFReports_DisplayFormat*}
- {'PDFReports_AggregateReportsFormat'|translate}
- </td>
- <td>
- <select id="display_format">
- {foreach from=$displayFormats key=formatValue item=formatLabel}
- <option {if $formatValue==1}selected{/if} value="{$formatValue}">{$formatLabel}</option>
- {/foreach}
- </select>
- <div class='report_evolution_graph'>
- <br/>
- <input type="checkbox" id="report_evolution_graph"/>
- <label for="report_evolution_graph"><i>{'PDFReports_EvolutionGraph'|translate:5}</i></label>
- </div>
- </td>
+ <td class="first">
+ {*PDFReports_AggregateReportsFormat should be named PDFReports_DisplayFormat*}
+ {'PDFReports_AggregateReportsFormat'|translate}
+ </td>
+ <td>
+ <select id="display_format">
+ {foreach from=$displayFormats key=formatValue item=formatLabel}
+ <option {if $formatValue==1}selected{/if} value="{$formatValue}">{$formatLabel}</option>
+ {/foreach}
+ </select>
+
+ <div class='report_evolution_graph'>
+ <br/>
+ <input type="checkbox" id="report_evolution_graph"/>
+ <label for="report_evolution_graph"><i>{'PDFReports_EvolutionGraph'|translate:5}</i></label>
+ </div>
+ </td>
</tr>
diff --git a/plugins/PrivacyManager/Controller.php b/plugins/PrivacyManager/Controller.php
index f2cb8f099e..bb25d40173 100644
--- a/plugins/PrivacyManager/Controller.php
+++ b/plugins/PrivacyManager/Controller.php
@@ -16,290 +16,275 @@
class Piwik_PrivacyManager_Controller extends Piwik_Controller_Admin
{
- const ANONYMIZE_IP_PLUGIN_NAME = "AnonymizeIP";
- const OPTION_LAST_DELETE_PIWIK_LOGS = "lastDelete_piwik_logs";
-
- public function saveSettings()
- {
- Piwik::checkUserIsSuperUser();
- if ($_SERVER["REQUEST_METHOD"] == "POST")
- {
- $this->checkTokenInUrl();
- switch (Piwik_Common::getRequestVar('form'))
- {
- case("formMaskLength"):
- $this->handlePluginState(Piwik_Common::getRequestVar("anonymizeIPEnable", 0));
- $trackerConfig = Piwik_Config::getInstance()->Tracker;
- $trackerConfig['ip_address_mask_length'] = Piwik_Common::getRequestVar("maskLength", 1);
- Piwik_Config::getInstance()->Tracker = $trackerConfig;
- Piwik_Config::getInstance()->forceSave();
- break;
-
- case("formDeleteSettings"):
- $settings = $this->getPurgeSettingsFromRequest();
- Piwik_PrivacyManager::savePurgeDataSettings($settings);
- break;
-
- default: //do nothing
- break;
- }
- }
-
- return $this->redirectToIndex('PrivacyManager', 'privacySettings', null, null, null, array('updated' => 1));
- }
-
- /**
- * Utility function. Gets the delete logs/reports settings from the request and uses
- * them to populate config arrays.
- *
- * @return array An array containing the data deletion settings.
- */
- private function getPurgeSettingsFromRequest()
- {
- $settings = array();
-
- // delete logs settings
- $settings['delete_logs_enable'] = Piwik_Common::getRequestVar("deleteEnable", 0);
- $settings['delete_logs_schedule_lowest_interval'] = Piwik_Common::getRequestVar("deleteLowestInterval", 7);
- $settings['delete_logs_older_than'] = ((int)Piwik_Common::getRequestVar("deleteOlderThan", 180) < 1) ?
- 1 : Piwik_Common::getRequestVar("deleteOlderThan", 180);
-
- // delete reports settings
- $settings['delete_reports_enable'] = Piwik_Common::getRequestVar("deleteReportsEnable", 0);
- $deleteReportsOlderThan = Piwik_Common::getRequestVar("deleteReportsOlderThan", 3);
- $settings['delete_reports_older_than'] = $deleteReportsOlderThan < 3 ? 3 : $deleteReportsOlderThan;
- $settings['delete_reports_keep_basic_metrics'] = Piwik_Common::getRequestVar("deleteReportsKeepBasic", 0);
- $settings['delete_reports_keep_day_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepDay", 0);
- $settings['delete_reports_keep_week_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepWeek", 0);
- $settings['delete_reports_keep_month_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepMonth", 0);
- $settings['delete_reports_keep_year_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepYear", 0);
- $settings['delete_reports_keep_range_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepRange", 0);
- $settings['delete_reports_keep_segment_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepSegments", 0);
-
- $settings['delete_logs_max_rows_per_query'] = Piwik_PrivacyManager::DEFAULT_MAX_ROWS_PER_QUERY;
-
- return $settings;
- }
-
- /**
- * Echo's an HTML chunk describing the current database size, and the estimated space
- * savings after the scheduled data purge is run.
- */
- public function getDatabaseSize()
- {
- Piwik::checkUserIsSuperUser();
- $view = Piwik_View::factory('databaseSize');
-
- $forceEstimate = Piwik_Common::getRequestVar('forceEstimate', 0);
- $view->dbStats = $this->getDeleteDBSizeEstimate($getSettingsFromQuery = true, $forceEstimate);
- $view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
-
- echo $view->render();
- }
-
- /**
- * Returns true if server side DoNotTrack support is enabled, false if otherwise.
- *
- * @return bool
- */
- public static function isDntSupported()
- {
- return Piwik_PluginsManager::getInstance()->isPluginActivated('DoNotTrack');
- }
-
- public function privacySettings()
- {
- Piwik::checkUserHasSomeAdminAccess();
- $view = Piwik_View::factory('privacySettings');
-
- if (Piwik::isUserIsSuperUser())
- {
- $view->deleteData = $this->getDeleteDataInfo();
- $view->anonymizeIP = $this->getAnonymizeIPInfo();
- $view->dntSupport = self::isDntSupported();
- $view->canDeleteLogActions = Piwik::isLockPrivilegeGranted();
- $view->dbUser = Piwik_Config::getInstance()->database['username'];
- }
- $view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
-
- if (!Piwik_Config::getInstance()->isFileWritable())
- {
- $view->configFileNotWritable = true;
- }
-
- $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
-
- echo $view->render();
- }
-
- /**
- * Executes a data purge, deleting log data and report data using the current config
- * options. Echo's the result of getDatabaseSize after purging.
- */
- public function executeDataPurge()
- {
- Piwik::checkUserIsSuperUser();
- $this->checkTokenInUrl();
-
- // if the request isn't a POST, redirect to index
- if ($_SERVER["REQUEST_METHOD"] != "POST"
- && !Piwik_Common::isPhpCliMode())
- {
- return $this->redirectToIndex('PrivacyManager', 'privacySettings');
- }
-
- $settings = Piwik_PrivacyManager::getPurgeDataSettings();
- if ($settings['delete_logs_enable'])
- {
- $logDataPurger = Piwik_PrivacyManager_LogDataPurger::make($settings);
- $logDataPurger->purgeData();
- }
- if ($settings['delete_reports_enable'])
- {
- $reportsPurger = Piwik_PrivacyManager_ReportsPurger::make(
- $settings, Piwik_PrivacyManager::getAllMetricsToKeep());
- $reportsPurger->purgeData(true);
- }
- }
-
- protected function getDeleteDBSizeEstimate( $getSettingsFromQuery = false, $forceEstimate = false )
- {
- // get the purging settings & create two purger instances
- if ($getSettingsFromQuery)
- {
- $settings = $this->getPurgeSettingsFromRequest();
- }
- else
- {
- $settings = Piwik_PrivacyManager::getPurgeDataSettings();
- }
-
- $doDatabaseSizeEstimate = Piwik_Config::getInstance()->Deletelogs['enable_auto_database_size_estimate'];
-
- // determine the DB size & purged DB size
- $metadataProvider = new Piwik_DBStats_MySQLMetadataProvider();
- $tableStatuses = $metadataProvider->getAllTablesStatus();
-
- $totalBytes = 0;
- foreach ($tableStatuses as $status)
- {
- $totalBytes += $status['Data_length'] + $status['Index_length'];
- }
-
- $result = array(
- 'currentSize' => Piwik::getPrettySizeFromBytes($totalBytes)
- );
-
- // if the db size estimate feature is enabled, get the estimate
- if ($doDatabaseSizeEstimate || $forceEstimate == 1)
- {
- // maps tables whose data will be deleted with number of rows that will be deleted
- // if a value is -1, it means the table will be dropped.
- $deletedDataSummary = Piwik_PrivacyManager::getPurgeEstimate($settings);
-
- $totalAfterPurge = $totalBytes;
- foreach ($tableStatuses as $status)
- {
- $tableName = $status['Name'];
- if (isset($deletedDataSummary[$tableName]))
- {
- $tableTotalBytes = $status['Data_length'] + $status['Index_length'];
-
- // if dropping the table
- if ($deletedDataSummary[$tableName] === Piwik_PrivacyManager_ReportsPurger::DROP_TABLE)
- {
- $totalAfterPurge -= $tableTotalBytes;
- }
- else // if just deleting rows
- {
- if($status['Rows'] > 0) {
- $totalAfterPurge -= ($tableTotalBytes / $status['Rows']) * $deletedDataSummary[$tableName];
- }
- }
- }
- }
-
- $result['sizeAfterPurge'] = Piwik::getPrettySizeFromBytes($totalAfterPurge);
- $result['spaceSaved'] = Piwik::getPrettySizeFromBytes($totalBytes - $totalAfterPurge);
- }
-
- return $result;
- }
-
- protected function getAnonymizeIPInfo()
- {
- Piwik::checkUserIsSuperUser();
- $anonymizeIP = array();
-
- Piwik_PluginsManager::getInstance()->loadPlugin(self::ANONYMIZE_IP_PLUGIN_NAME);
-
- $anonymizeIP["name"] = self::ANONYMIZE_IP_PLUGIN_NAME;
- $anonymizeIP["enabled"] = Piwik_PluginsManager::getInstance()->isPluginActivated(self::ANONYMIZE_IP_PLUGIN_NAME);
- $anonymizeIP["maskLength"] = Piwik_Config::getInstance()->Tracker['ip_address_mask_length'];
- $anonymizeIP["info"] = Piwik_PluginsManager::getInstance()->getLoadedPlugin(self::ANONYMIZE_IP_PLUGIN_NAME)->getInformation();
-
- return $anonymizeIP;
- }
-
- protected function getDeleteDataInfo()
- {
- Piwik::checkUserIsSuperUser();
- $deleteDataInfos = array();
- $taskScheduler = new Piwik_TaskScheduler();
- $deleteDataInfos["config"] = Piwik_PrivacyManager::getPurgeDataSettings();
- $deleteDataInfos["deleteTables"] =
- "<br/>".implode(", ", Piwik_PrivacyManager_LogDataPurger::getDeleteTableLogTables());
-
- $scheduleTimetable = $taskScheduler->getScheduledTimeForMethod("Piwik_PrivacyManager", "deleteLogTables");
-
- $optionTable = Piwik_GetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS);
-
- //If task was already rescheduled, read time from taskTimetable. Else, calculate next possible runtime.
- if (!empty($scheduleTimetable) && ($scheduleTimetable - time() > 0)) {
- $nextPossibleSchedule = (int)$scheduleTimetable;
- } else {
- $date = Piwik_Date::factory("today");
- $nextPossibleSchedule = $date->addDay(1)->getTimestamp();
- }
-
- //deletion schedule did not run before
- if (empty($optionTable)) {
- $deleteDataInfos["lastRun"] = false;
-
- //next run ASAP (with next schedule run)
- $date = Piwik_Date::factory("today");
- $deleteDataInfos["nextScheduleTime"] = $nextPossibleSchedule;
- } else {
- $deleteDataInfos["lastRun"] = $optionTable;
- $deleteDataInfos["lastRunPretty"] = Piwik_Date::factory((int)$optionTable)->getLocalized('%day% %shortMonth% %longYear%');
-
- //Calculate next run based on last run + interval
- $nextScheduleRun = (int)($deleteDataInfos["lastRun"] + $deleteDataInfos["config"]["delete_logs_schedule_lowest_interval"] * 24 * 60 * 60);
-
- //is the calculated next run in the past? (e.g. plugin was disabled in the meantime or something) -> run ASAP
- if (($nextScheduleRun - time()) <= 0) {
- $deleteDataInfos["nextScheduleTime"] = $nextPossibleSchedule;
- } else {
- $deleteDataInfos["nextScheduleTime"] = $nextScheduleRun;
- }
- }
-
- $deleteDataInfos["nextRunPretty"] = Piwik::getPrettyTimeFromSeconds($deleteDataInfos["nextScheduleTime"] - time());
-
- return $deleteDataInfos;
- }
-
- protected function handlePluginState($state = 0)
- {
- $pluginController = new Piwik_CorePluginsAdmin_Controller();
-
- if ($state == 1 && !Piwik_PluginsManager::getInstance()->isPluginActivated(self::ANONYMIZE_IP_PLUGIN_NAME)) {
- $pluginController->activate($redirectAfter = false);
- } elseif ($state == 0 && Piwik_PluginsManager::getInstance()->isPluginActivated(self::ANONYMIZE_IP_PLUGIN_NAME)) {
- $pluginController->deactivate($redirectAfter = false);
- } else {
- //nothing to do
- }
- }
+ const ANONYMIZE_IP_PLUGIN_NAME = "AnonymizeIP";
+ const OPTION_LAST_DELETE_PIWIK_LOGS = "lastDelete_piwik_logs";
+
+ public function saveSettings()
+ {
+ Piwik::checkUserIsSuperUser();
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ $this->checkTokenInUrl();
+ switch (Piwik_Common::getRequestVar('form')) {
+ case("formMaskLength"):
+ $this->handlePluginState(Piwik_Common::getRequestVar("anonymizeIPEnable", 0));
+ $trackerConfig = Piwik_Config::getInstance()->Tracker;
+ $trackerConfig['ip_address_mask_length'] = Piwik_Common::getRequestVar("maskLength", 1);
+ Piwik_Config::getInstance()->Tracker = $trackerConfig;
+ Piwik_Config::getInstance()->forceSave();
+ break;
+
+ case("formDeleteSettings"):
+ $settings = $this->getPurgeSettingsFromRequest();
+ Piwik_PrivacyManager::savePurgeDataSettings($settings);
+ break;
+
+ default: //do nothing
+ break;
+ }
+ }
+
+ return $this->redirectToIndex('PrivacyManager', 'privacySettings', null, null, null, array('updated' => 1));
+ }
+
+ /**
+ * Utility function. Gets the delete logs/reports settings from the request and uses
+ * them to populate config arrays.
+ *
+ * @return array An array containing the data deletion settings.
+ */
+ private function getPurgeSettingsFromRequest()
+ {
+ $settings = array();
+
+ // delete logs settings
+ $settings['delete_logs_enable'] = Piwik_Common::getRequestVar("deleteEnable", 0);
+ $settings['delete_logs_schedule_lowest_interval'] = Piwik_Common::getRequestVar("deleteLowestInterval", 7);
+ $settings['delete_logs_older_than'] = ((int)Piwik_Common::getRequestVar("deleteOlderThan", 180) < 1) ?
+ 1 : Piwik_Common::getRequestVar("deleteOlderThan", 180);
+
+ // delete reports settings
+ $settings['delete_reports_enable'] = Piwik_Common::getRequestVar("deleteReportsEnable", 0);
+ $deleteReportsOlderThan = Piwik_Common::getRequestVar("deleteReportsOlderThan", 3);
+ $settings['delete_reports_older_than'] = $deleteReportsOlderThan < 3 ? 3 : $deleteReportsOlderThan;
+ $settings['delete_reports_keep_basic_metrics'] = Piwik_Common::getRequestVar("deleteReportsKeepBasic", 0);
+ $settings['delete_reports_keep_day_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepDay", 0);
+ $settings['delete_reports_keep_week_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepWeek", 0);
+ $settings['delete_reports_keep_month_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepMonth", 0);
+ $settings['delete_reports_keep_year_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepYear", 0);
+ $settings['delete_reports_keep_range_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepRange", 0);
+ $settings['delete_reports_keep_segment_reports'] = Piwik_Common::getRequestVar("deleteReportsKeepSegments", 0);
+
+ $settings['delete_logs_max_rows_per_query'] = Piwik_PrivacyManager::DEFAULT_MAX_ROWS_PER_QUERY;
+
+ return $settings;
+ }
+
+ /**
+ * Echo's an HTML chunk describing the current database size, and the estimated space
+ * savings after the scheduled data purge is run.
+ */
+ public function getDatabaseSize()
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = Piwik_View::factory('databaseSize');
+
+ $forceEstimate = Piwik_Common::getRequestVar('forceEstimate', 0);
+ $view->dbStats = $this->getDeleteDBSizeEstimate($getSettingsFromQuery = true, $forceEstimate);
+ $view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
+
+ echo $view->render();
+ }
+
+ /**
+ * Returns true if server side DoNotTrack support is enabled, false if otherwise.
+ *
+ * @return bool
+ */
+ public static function isDntSupported()
+ {
+ return Piwik_PluginsManager::getInstance()->isPluginActivated('DoNotTrack');
+ }
+
+ public function privacySettings()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ $view = Piwik_View::factory('privacySettings');
+
+ if (Piwik::isUserIsSuperUser()) {
+ $view->deleteData = $this->getDeleteDataInfo();
+ $view->anonymizeIP = $this->getAnonymizeIPInfo();
+ $view->dntSupport = self::isDntSupported();
+ $view->canDeleteLogActions = Piwik::isLockPrivilegeGranted();
+ $view->dbUser = Piwik_Config::getInstance()->database['username'];
+ }
+ $view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
+
+ if (!Piwik_Config::getInstance()->isFileWritable()) {
+ $view->configFileNotWritable = true;
+ }
+
+ $this->setBasicVariablesView($view);
+ $view->menu = Piwik_GetAdminMenu();
+
+ echo $view->render();
+ }
+
+ /**
+ * Executes a data purge, deleting log data and report data using the current config
+ * options. Echo's the result of getDatabaseSize after purging.
+ */
+ public function executeDataPurge()
+ {
+ Piwik::checkUserIsSuperUser();
+ $this->checkTokenInUrl();
+
+ // if the request isn't a POST, redirect to index
+ if ($_SERVER["REQUEST_METHOD"] != "POST"
+ && !Piwik_Common::isPhpCliMode()
+ ) {
+ return $this->redirectToIndex('PrivacyManager', 'privacySettings');
+ }
+
+ $settings = Piwik_PrivacyManager::getPurgeDataSettings();
+ if ($settings['delete_logs_enable']) {
+ $logDataPurger = Piwik_PrivacyManager_LogDataPurger::make($settings);
+ $logDataPurger->purgeData();
+ }
+ if ($settings['delete_reports_enable']) {
+ $reportsPurger = Piwik_PrivacyManager_ReportsPurger::make(
+ $settings, Piwik_PrivacyManager::getAllMetricsToKeep());
+ $reportsPurger->purgeData(true);
+ }
+ }
+
+ protected function getDeleteDBSizeEstimate($getSettingsFromQuery = false, $forceEstimate = false)
+ {
+ // get the purging settings & create two purger instances
+ if ($getSettingsFromQuery) {
+ $settings = $this->getPurgeSettingsFromRequest();
+ } else {
+ $settings = Piwik_PrivacyManager::getPurgeDataSettings();
+ }
+
+ $doDatabaseSizeEstimate = Piwik_Config::getInstance()->Deletelogs['enable_auto_database_size_estimate'];
+
+ // determine the DB size & purged DB size
+ $metadataProvider = new Piwik_DBStats_MySQLMetadataProvider();
+ $tableStatuses = $metadataProvider->getAllTablesStatus();
+
+ $totalBytes = 0;
+ foreach ($tableStatuses as $status) {
+ $totalBytes += $status['Data_length'] + $status['Index_length'];
+ }
+
+ $result = array(
+ 'currentSize' => Piwik::getPrettySizeFromBytes($totalBytes)
+ );
+
+ // if the db size estimate feature is enabled, get the estimate
+ if ($doDatabaseSizeEstimate || $forceEstimate == 1) {
+ // maps tables whose data will be deleted with number of rows that will be deleted
+ // if a value is -1, it means the table will be dropped.
+ $deletedDataSummary = Piwik_PrivacyManager::getPurgeEstimate($settings);
+
+ $totalAfterPurge = $totalBytes;
+ foreach ($tableStatuses as $status) {
+ $tableName = $status['Name'];
+ if (isset($deletedDataSummary[$tableName])) {
+ $tableTotalBytes = $status['Data_length'] + $status['Index_length'];
+
+ // if dropping the table
+ if ($deletedDataSummary[$tableName] === Piwik_PrivacyManager_ReportsPurger::DROP_TABLE) {
+ $totalAfterPurge -= $tableTotalBytes;
+ } else // if just deleting rows
+ {
+ if ($status['Rows'] > 0) {
+ $totalAfterPurge -= ($tableTotalBytes / $status['Rows']) * $deletedDataSummary[$tableName];
+ }
+ }
+ }
+ }
+
+ $result['sizeAfterPurge'] = Piwik::getPrettySizeFromBytes($totalAfterPurge);
+ $result['spaceSaved'] = Piwik::getPrettySizeFromBytes($totalBytes - $totalAfterPurge);
+ }
+
+ return $result;
+ }
+
+ protected function getAnonymizeIPInfo()
+ {
+ Piwik::checkUserIsSuperUser();
+ $anonymizeIP = array();
+
+ Piwik_PluginsManager::getInstance()->loadPlugin(self::ANONYMIZE_IP_PLUGIN_NAME);
+
+ $anonymizeIP["name"] = self::ANONYMIZE_IP_PLUGIN_NAME;
+ $anonymizeIP["enabled"] = Piwik_PluginsManager::getInstance()->isPluginActivated(self::ANONYMIZE_IP_PLUGIN_NAME);
+ $anonymizeIP["maskLength"] = Piwik_Config::getInstance()->Tracker['ip_address_mask_length'];
+ $anonymizeIP["info"] = Piwik_PluginsManager::getInstance()->getLoadedPlugin(self::ANONYMIZE_IP_PLUGIN_NAME)->getInformation();
+
+ return $anonymizeIP;
+ }
+
+ protected function getDeleteDataInfo()
+ {
+ Piwik::checkUserIsSuperUser();
+ $deleteDataInfos = array();
+ $taskScheduler = new Piwik_TaskScheduler();
+ $deleteDataInfos["config"] = Piwik_PrivacyManager::getPurgeDataSettings();
+ $deleteDataInfos["deleteTables"] =
+ "<br/>" . implode(", ", Piwik_PrivacyManager_LogDataPurger::getDeleteTableLogTables());
+
+ $scheduleTimetable = $taskScheduler->getScheduledTimeForMethod("Piwik_PrivacyManager", "deleteLogTables");
+
+ $optionTable = Piwik_GetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS);
+
+ //If task was already rescheduled, read time from taskTimetable. Else, calculate next possible runtime.
+ if (!empty($scheduleTimetable) && ($scheduleTimetable - time() > 0)) {
+ $nextPossibleSchedule = (int)$scheduleTimetable;
+ } else {
+ $date = Piwik_Date::factory("today");
+ $nextPossibleSchedule = $date->addDay(1)->getTimestamp();
+ }
+
+ //deletion schedule did not run before
+ if (empty($optionTable)) {
+ $deleteDataInfos["lastRun"] = false;
+
+ //next run ASAP (with next schedule run)
+ $date = Piwik_Date::factory("today");
+ $deleteDataInfos["nextScheduleTime"] = $nextPossibleSchedule;
+ } else {
+ $deleteDataInfos["lastRun"] = $optionTable;
+ $deleteDataInfos["lastRunPretty"] = Piwik_Date::factory((int)$optionTable)->getLocalized('%day% %shortMonth% %longYear%');
+
+ //Calculate next run based on last run + interval
+ $nextScheduleRun = (int)($deleteDataInfos["lastRun"] + $deleteDataInfos["config"]["delete_logs_schedule_lowest_interval"] * 24 * 60 * 60);
+
+ //is the calculated next run in the past? (e.g. plugin was disabled in the meantime or something) -> run ASAP
+ if (($nextScheduleRun - time()) <= 0) {
+ $deleteDataInfos["nextScheduleTime"] = $nextPossibleSchedule;
+ } else {
+ $deleteDataInfos["nextScheduleTime"] = $nextScheduleRun;
+ }
+ }
+
+ $deleteDataInfos["nextRunPretty"] = Piwik::getPrettyTimeFromSeconds($deleteDataInfos["nextScheduleTime"] - time());
+
+ return $deleteDataInfos;
+ }
+
+ protected function handlePluginState($state = 0)
+ {
+ $pluginController = new Piwik_CorePluginsAdmin_Controller();
+
+ if ($state == 1 && !Piwik_PluginsManager::getInstance()->isPluginActivated(self::ANONYMIZE_IP_PLUGIN_NAME)) {
+ $pluginController->activate($redirectAfter = false);
+ } elseif ($state == 0 && Piwik_PluginsManager::getInstance()->isPluginActivated(self::ANONYMIZE_IP_PLUGIN_NAME)) {
+ $pluginController->deactivate($redirectAfter = false);
+ } else {
+ //nothing to do
+ }
+ }
}
diff --git a/plugins/PrivacyManager/LogDataPurger.php b/plugins/PrivacyManager/LogDataPurger.php
index da1b48591e..440f8f5e42 100755
--- a/plugins/PrivacyManager/LogDataPurger.php
+++ b/plugins/PrivacyManager/LogDataPurger.php
@@ -14,334 +14,313 @@
*/
class Piwik_PrivacyManager_LogDataPurger
{
- const TEMP_TABLE_NAME = 'tmp_log_actions_to_keep';
-
- /**
- * The max set of rows each table scan select should query at one time.
- */
- public static $selectSegmentSize = 100000;
-
- /**
- * The number of days after which log entries are considered old.
- */
- private $deleteLogsOlderThan;
-
- /**
- * The number of rows to delete per DELETE query.
- */
- private $maxRowsToDeletePerQuery;
-
- /**
- * Constructor.
- *
- * @param int $deleteLogsOlderThan The number of days after which log entires are considered old.
- * Visits and related data whose age is greater than this number
- * will be purged.
- * @param int $maxRowsToDeletePerQuery The maximum number of rows to delete in one query. Used to
- * make sure log tables aren't locked for too long.
- */
- public function __construct( $deleteLogsOlderThan, $maxRowsToDeletePerQuery )
- {
- $this->deleteLogsOlderThan = $deleteLogsOlderThan;
- $this->maxRowsToDeletePerQuery = $maxRowsToDeletePerQuery;
- }
-
- /**
- * Purges old data from the following tables:
- * - log_visit
- * - log_link_visit_action
- * - log_conversion
- * - log_conversion_item
- * - log_action
- */
- public function purgeData()
- {
- $maxIdVisit = $this->getDeleteIdVisitOffset();
-
- // break if no ID was found (nothing to delete for given period)
- if (empty($maxIdVisit))
- {
- return;
- }
-
- $logTables = self::getDeleteTableLogTables();
-
- // delete data from log tables
- $where = "WHERE idvisit <= ?";
- foreach ($logTables as $logTable)
- {
- // deleting from log_action must be handled differently, so we do it later
- if ($logTable != Piwik_Common::prefixTable('log_action'))
- {
- Piwik_DeleteAllRows($logTable, $where, $this->maxRowsToDeletePerQuery, array($maxIdVisit));
- }
- }
-
- // delete unused actions from the log_action table (but only if we can lock tables)
- if (Piwik::isLockPrivilegeGranted())
- {
- $this->purgeUnusedLogActions();
- }
- else
- {
- $logMessage = get_class($this).": LOCK TABLES privilege not granted; skipping unused actions purge";
- Piwik::log($logMessage);
- }
-
- // optimize table overhead after deletion
- Piwik_OptimizeTables($logTables);
- }
-
- /**
- * Returns an array describing what data would be purged if purging were invoked.
- *
- * This function returns an array that maps table names with the number of rows
- * that will be deleted.
- *
- * @return array
- */
- public function getPurgeEstimate()
- {
- $result = array();
-
- // deal w/ log tables that will be purged
- $maxIdVisit = $this->getDeleteIdVisitOffset();
- if (!empty($maxIdVisit))
- {
- foreach ($this->getDeleteTableLogTables() as $table)
- {
- // getting an estimate for log_action is not supported since it can take too long
- if ($table != Piwik_Common::prefixTable('log_action'))
- {
- $rowCount = $this->getLogTableDeleteCount($table, $maxIdVisit);
- if ($rowCount > 0)
- {
- $result[$table] = $rowCount;
- }
- }
- }
- }
-
- return $result;
- }
-
- /**
- * Safely delete all unused log_action rows.
- */
- private function purgeUnusedLogActions()
- {
- $this->createTempTable();
-
- // get current max ID in log tables w/ idaction references.
- $maxIds = $this->getMaxIdsInLogTables();
-
- // do large insert (inserting everything before maxIds) w/o locking tables...
- $this->insertActionsToKeep($maxIds, $deleteOlderThanMax = true);
-
- // ... then do small insert w/ locked tables to minimize the amount of time tables are locked.
- $this->lockLogTables();
- $this->insertActionsToKeep($maxIds, $deleteOlderThanMax = false);
-
- // delete before unlocking tables so there's no chance a new log row that references an
- // unused action will be inserted.
- $this->deleteUnusedActions();
- $this->unlockLogTables();
- }
-
- /**
- * get highest idVisit to delete rows from
- * @return string
- */
- private function getDeleteIdVisitOffset()
- {
- $logVisit = Piwik_Common::prefixTable("log_visit");
-
- // get max idvisit
- $maxIdVisit = Piwik_FetchOne("SELECT MAX(idvisit) FROM $logVisit");
- if (empty($maxIdVisit))
- {
- return false;
- }
-
- // select highest idvisit to delete from
- $dateStart = Piwik_Date::factory("today")->subDay($this->deleteLogsOlderThan);
- $sql = "SELECT idvisit
+ const TEMP_TABLE_NAME = 'tmp_log_actions_to_keep';
+
+ /**
+ * The max set of rows each table scan select should query at one time.
+ */
+ public static $selectSegmentSize = 100000;
+
+ /**
+ * The number of days after which log entries are considered old.
+ */
+ private $deleteLogsOlderThan;
+
+ /**
+ * The number of rows to delete per DELETE query.
+ */
+ private $maxRowsToDeletePerQuery;
+
+ /**
+ * Constructor.
+ *
+ * @param int $deleteLogsOlderThan The number of days after which log entires are considered old.
+ * Visits and related data whose age is greater than this number
+ * will be purged.
+ * @param int $maxRowsToDeletePerQuery The maximum number of rows to delete in one query. Used to
+ * make sure log tables aren't locked for too long.
+ */
+ public function __construct($deleteLogsOlderThan, $maxRowsToDeletePerQuery)
+ {
+ $this->deleteLogsOlderThan = $deleteLogsOlderThan;
+ $this->maxRowsToDeletePerQuery = $maxRowsToDeletePerQuery;
+ }
+
+ /**
+ * Purges old data from the following tables:
+ * - log_visit
+ * - log_link_visit_action
+ * - log_conversion
+ * - log_conversion_item
+ * - log_action
+ */
+ public function purgeData()
+ {
+ $maxIdVisit = $this->getDeleteIdVisitOffset();
+
+ // break if no ID was found (nothing to delete for given period)
+ if (empty($maxIdVisit)) {
+ return;
+ }
+
+ $logTables = self::getDeleteTableLogTables();
+
+ // delete data from log tables
+ $where = "WHERE idvisit <= ?";
+ foreach ($logTables as $logTable) {
+ // deleting from log_action must be handled differently, so we do it later
+ if ($logTable != Piwik_Common::prefixTable('log_action')) {
+ Piwik_DeleteAllRows($logTable, $where, $this->maxRowsToDeletePerQuery, array($maxIdVisit));
+ }
+ }
+
+ // delete unused actions from the log_action table (but only if we can lock tables)
+ if (Piwik::isLockPrivilegeGranted()) {
+ $this->purgeUnusedLogActions();
+ } else {
+ $logMessage = get_class($this) . ": LOCK TABLES privilege not granted; skipping unused actions purge";
+ Piwik::log($logMessage);
+ }
+
+ // optimize table overhead after deletion
+ Piwik_OptimizeTables($logTables);
+ }
+
+ /**
+ * Returns an array describing what data would be purged if purging were invoked.
+ *
+ * This function returns an array that maps table names with the number of rows
+ * that will be deleted.
+ *
+ * @return array
+ */
+ public function getPurgeEstimate()
+ {
+ $result = array();
+
+ // deal w/ log tables that will be purged
+ $maxIdVisit = $this->getDeleteIdVisitOffset();
+ if (!empty($maxIdVisit)) {
+ foreach ($this->getDeleteTableLogTables() as $table) {
+ // getting an estimate for log_action is not supported since it can take too long
+ if ($table != Piwik_Common::prefixTable('log_action')) {
+ $rowCount = $this->getLogTableDeleteCount($table, $maxIdVisit);
+ if ($rowCount > 0) {
+ $result[$table] = $rowCount;
+ }
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Safely delete all unused log_action rows.
+ */
+ private function purgeUnusedLogActions()
+ {
+ $this->createTempTable();
+
+ // get current max ID in log tables w/ idaction references.
+ $maxIds = $this->getMaxIdsInLogTables();
+
+ // do large insert (inserting everything before maxIds) w/o locking tables...
+ $this->insertActionsToKeep($maxIds, $deleteOlderThanMax = true);
+
+ // ... then do small insert w/ locked tables to minimize the amount of time tables are locked.
+ $this->lockLogTables();
+ $this->insertActionsToKeep($maxIds, $deleteOlderThanMax = false);
+
+ // delete before unlocking tables so there's no chance a new log row that references an
+ // unused action will be inserted.
+ $this->deleteUnusedActions();
+ $this->unlockLogTables();
+ }
+
+ /**
+ * get highest idVisit to delete rows from
+ * @return string
+ */
+ private function getDeleteIdVisitOffset()
+ {
+ $logVisit = Piwik_Common::prefixTable("log_visit");
+
+ // get max idvisit
+ $maxIdVisit = Piwik_FetchOne("SELECT MAX(idvisit) FROM $logVisit");
+ if (empty($maxIdVisit)) {
+ return false;
+ }
+
+ // select highest idvisit to delete from
+ $dateStart = Piwik_Date::factory("today")->subDay($this->deleteLogsOlderThan);
+ $sql = "SELECT idvisit
FROM $logVisit
- WHERE '".$dateStart->toString('Y-m-d H:i:s')."' > visit_last_action_time
+ WHERE '" . $dateStart->toString('Y-m-d H:i:s') . "' > visit_last_action_time
AND idvisit <= ?
AND idvisit > ?
ORDER BY idvisit DESC
LIMIT 1";
-
- return Piwik_SegmentedFetchFirst($sql, $maxIdVisit, 0, -self::$selectSegmentSize);
- }
-
- private function getLogTableDeleteCount( $table, $maxIdVisit )
- {
- $sql = "SELECT COUNT(*) FROM $table WHERE idvisit <= ?";
- return (int)Piwik_FetchOne($sql, array($maxIdVisit));
- }
-
- private function createTempTable()
- {
- $sql = "CREATE TEMPORARY TABLE ".Piwik_Common::prefixTable(self::TEMP_TABLE_NAME)." (
+
+ return Piwik_SegmentedFetchFirst($sql, $maxIdVisit, 0, -self::$selectSegmentSize);
+ }
+
+ private function getLogTableDeleteCount($table, $maxIdVisit)
+ {
+ $sql = "SELECT COUNT(*) FROM $table WHERE idvisit <= ?";
+ return (int)Piwik_FetchOne($sql, array($maxIdVisit));
+ }
+
+ private function createTempTable()
+ {
+ $sql = "CREATE TEMPORARY TABLE " . Piwik_Common::prefixTable(self::TEMP_TABLE_NAME) . " (
idaction INT(11),
PRIMARY KEY (idaction)
)";
- Piwik_Query($sql);
- }
-
- private function getMaxIdsInLogTables()
- {
- $tables = array('log_conversion', 'log_link_visit_action', 'log_visit', 'log_conversion_item');
- $idColumns = $this->getTableIdColumns();
-
- $result = array();
- foreach ($tables as $table)
- {
- $idCol = $idColumns[$table];
- $result[$table] = Piwik_FetchOne("SELECT MAX($idCol) FROM ".Piwik_Common::prefixTable($table));
- }
-
- return $result;
- }
-
- private function insertActionsToKeep( $maxIds, $olderThan = true )
- {
- $tempTableName = Piwik_Common::prefixTable(self::TEMP_TABLE_NAME);
-
- $idColumns = $this->getTableIdColumns();
- foreach ($this->getIdActionColumns() as $table => $columns)
- {
- $idCol = $idColumns[$table];
-
- foreach ($columns as $col)
- {
- $select = "SELECT $col FROM ".Piwik_Common::prefixTable($table)." WHERE $idCol >= ? AND $idCol < ?";
- $sql = "INSERT IGNORE INTO $tempTableName $select";
-
- if ($olderThan)
- {
- $start = 0;
- $finish = $maxIds[$table];
- }
- else
- {
- $start = $maxIds[$table];
- $finish = Piwik_FetchOne("SELECT MAX($idCol) FROM ".Piwik_Common::prefixTable($table));
- }
-
- Piwik_SegmentedQuery($sql, $start, $finish, self::$selectSegmentSize);
- }
- }
-
- // allow code to be executed after data is inserted. for concurrency testing purposes.
- if ($olderThan)
- {
- Piwik_PostEvent("LogDataPurger.actionsToKeepInserted.olderThan");
- }
- else
- {
- Piwik_PostEvent("LogDataPurger.actionsToKeepInserted.newerThan");
- }
- }
-
- private function lockLogTables()
- {
- Piwik_LockTables(
- $readLocks = Piwik_Common::prefixTables('log_conversion',
- 'log_link_visit_action',
- 'log_visit',
- 'log_conversion_item'),
- $writeLocks = Piwik_Common::prefixTables('log_action')
- );
- }
-
- private function unlockLogTables()
- {
- Piwik_UnlockAllTables();
- }
-
- private function deleteUnusedActions()
- {
- list($logActionTable, $tempTableName) = Piwik_Common::prefixTables("log_action", self::TEMP_TABLE_NAME);
-
- $deleteSql = "DELETE LOW_PRIORITY QUICK IGNORE $logActionTable
+ Piwik_Query($sql);
+ }
+
+ private function getMaxIdsInLogTables()
+ {
+ $tables = array('log_conversion', 'log_link_visit_action', 'log_visit', 'log_conversion_item');
+ $idColumns = $this->getTableIdColumns();
+
+ $result = array();
+ foreach ($tables as $table) {
+ $idCol = $idColumns[$table];
+ $result[$table] = Piwik_FetchOne("SELECT MAX($idCol) FROM " . Piwik_Common::prefixTable($table));
+ }
+
+ return $result;
+ }
+
+ private function insertActionsToKeep($maxIds, $olderThan = true)
+ {
+ $tempTableName = Piwik_Common::prefixTable(self::TEMP_TABLE_NAME);
+
+ $idColumns = $this->getTableIdColumns();
+ foreach ($this->getIdActionColumns() as $table => $columns) {
+ $idCol = $idColumns[$table];
+
+ foreach ($columns as $col) {
+ $select = "SELECT $col FROM " . Piwik_Common::prefixTable($table) . " WHERE $idCol >= ? AND $idCol < ?";
+ $sql = "INSERT IGNORE INTO $tempTableName $select";
+
+ if ($olderThan) {
+ $start = 0;
+ $finish = $maxIds[$table];
+ } else {
+ $start = $maxIds[$table];
+ $finish = Piwik_FetchOne("SELECT MAX($idCol) FROM " . Piwik_Common::prefixTable($table));
+ }
+
+ Piwik_SegmentedQuery($sql, $start, $finish, self::$selectSegmentSize);
+ }
+ }
+
+ // allow code to be executed after data is inserted. for concurrency testing purposes.
+ if ($olderThan) {
+ Piwik_PostEvent("LogDataPurger.actionsToKeepInserted.olderThan");
+ } else {
+ Piwik_PostEvent("LogDataPurger.actionsToKeepInserted.newerThan");
+ }
+ }
+
+ private function lockLogTables()
+ {
+ Piwik_LockTables(
+ $readLocks = Piwik_Common::prefixTables('log_conversion',
+ 'log_link_visit_action',
+ 'log_visit',
+ 'log_conversion_item'),
+ $writeLocks = Piwik_Common::prefixTables('log_action')
+ );
+ }
+
+ private function unlockLogTables()
+ {
+ Piwik_UnlockAllTables();
+ }
+
+ private function deleteUnusedActions()
+ {
+ list($logActionTable, $tempTableName) = Piwik_Common::prefixTables("log_action", self::TEMP_TABLE_NAME);
+
+ $deleteSql = "DELETE LOW_PRIORITY QUICK IGNORE $logActionTable
FROM $logActionTable
LEFT JOIN $tempTableName tmp ON tmp.idaction = $logActionTable.idaction
WHERE tmp.idaction IS NULL";
-
- Piwik_Query($deleteSql);
- }
-
- private function getIdActionColumns()
- {
- return array(
- 'log_link_visit_action' => array( 'idaction_url',
- 'idaction_url_ref',
- 'idaction_name',
- 'idaction_name_ref' ),
-
- 'log_conversion' => array( 'idaction_url' ),
-
- 'log_visit' => array( 'visit_exit_idaction_url',
- 'visit_exit_idaction_name',
- 'visit_entry_idaction_url',
- 'visit_entry_idaction_name' ),
-
- 'log_conversion_item' => array( 'idaction_sku',
- 'idaction_name',
- 'idaction_category',
- 'idaction_category2',
- 'idaction_category3',
- 'idaction_category4',
- 'idaction_category5' )
- );
- }
-
- private function getTableIdColumns()
- {
- return array(
- 'log_link_visit_action' => 'idlink_va',
- 'log_conversion' => 'idvisit',
- 'log_visit' => 'idvisit',
- 'log_conversion_item' => 'idvisit'
- );
- }
-
- // let's hardcode, since these are not dynamically created tables
- public static function getDeleteTableLogTables()
- {
- $result = Piwik_Common::prefixTables('log_conversion',
- 'log_link_visit_action',
- 'log_visit',
- 'log_conversion_item');
- if (Piwik::isLockPrivilegeGranted())
- {
- $result[] = Piwik_Common::prefixTable('log_action');
- }
- return $result;
- }
-
- /**
- * Utility function. Creates a new instance of LogDataPurger with the supplied array
- * of settings.
- *
- * $settings must contain values for the following keys:
- * - 'delete_logs_older_than': The number of days after which log entries are considered
- * old.
- * - 'delete_logs_max_rows_per_query': Max number of rows to DELETE in one query.
- *
- * @param array $settings Array of settings
- * @param bool $useRealTable
- * @return Piwik_PrivacyManager_LogDataPurger
- */
- public static function make( $settings, $useRealTable = false )
- {
- return new Piwik_PrivacyManager_LogDataPurger(
- $settings['delete_logs_older_than'],
- $settings['delete_logs_max_rows_per_query']
- );
- }
+
+ Piwik_Query($deleteSql);
+ }
+
+ private function getIdActionColumns()
+ {
+ return array(
+ 'log_link_visit_action' => array('idaction_url',
+ 'idaction_url_ref',
+ 'idaction_name',
+ 'idaction_name_ref'),
+
+ 'log_conversion' => array('idaction_url'),
+
+ 'log_visit' => array('visit_exit_idaction_url',
+ 'visit_exit_idaction_name',
+ 'visit_entry_idaction_url',
+ 'visit_entry_idaction_name'),
+
+ 'log_conversion_item' => array('idaction_sku',
+ 'idaction_name',
+ 'idaction_category',
+ 'idaction_category2',
+ 'idaction_category3',
+ 'idaction_category4',
+ 'idaction_category5')
+ );
+ }
+
+ private function getTableIdColumns()
+ {
+ return array(
+ 'log_link_visit_action' => 'idlink_va',
+ 'log_conversion' => 'idvisit',
+ 'log_visit' => 'idvisit',
+ 'log_conversion_item' => 'idvisit'
+ );
+ }
+
+ // let's hardcode, since these are not dynamically created tables
+ public static function getDeleteTableLogTables()
+ {
+ $result = Piwik_Common::prefixTables('log_conversion',
+ 'log_link_visit_action',
+ 'log_visit',
+ 'log_conversion_item');
+ if (Piwik::isLockPrivilegeGranted()) {
+ $result[] = Piwik_Common::prefixTable('log_action');
+ }
+ return $result;
+ }
+
+ /**
+ * Utility function. Creates a new instance of LogDataPurger with the supplied array
+ * of settings.
+ *
+ * $settings must contain values for the following keys:
+ * - 'delete_logs_older_than': The number of days after which log entries are considered
+ * old.
+ * - 'delete_logs_max_rows_per_query': Max number of rows to DELETE in one query.
+ *
+ * @param array $settings Array of settings
+ * @param bool $useRealTable
+ * @return Piwik_PrivacyManager_LogDataPurger
+ */
+ public static function make($settings, $useRealTable = false)
+ {
+ return new Piwik_PrivacyManager_LogDataPurger(
+ $settings['delete_logs_older_than'],
+ $settings['delete_logs_max_rows_per_query']
+ );
+ }
}
diff --git a/plugins/PrivacyManager/PrivacyManager.php b/plugins/PrivacyManager/PrivacyManager.php
index 85c7a8f5ce..840fe5cfc2 100644
--- a/plugins/PrivacyManager/PrivacyManager.php
+++ b/plugins/PrivacyManager/PrivacyManager.php
@@ -29,70 +29,70 @@ class Piwik_PrivacyManager extends Piwik_Plugin
const OPTION_LAST_DELETE_PIWIK_REPORTS = 'lastDelete_piwik_reports';
const OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL = "lastDelete_piwik_logs_initial";
const DEFAULT_MAX_ROWS_PER_QUERY = 100000;
-
+
// default config options for data purging feature
public static $defaultPurgeDataOptions = array(
- 'delete_logs_enable' => 0,
- 'delete_logs_schedule_lowest_interval' => 7,
- 'delete_logs_older_than' => 180,
- 'delete_logs_max_rows_per_query' => self::DEFAULT_MAX_ROWS_PER_QUERY,
- 'delete_reports_enable' => 0,
- 'delete_reports_older_than' => 12,
- 'delete_reports_keep_basic_metrics' => 1,
- 'delete_reports_keep_day_reports' => 0,
- 'delete_reports_keep_week_reports' => 0,
- 'delete_reports_keep_month_reports' => 1,
- 'delete_reports_keep_year_reports' => 1,
- 'delete_reports_keep_range_reports' => 0,
- 'delete_reports_keep_segment_reports' => 0,
- );
-
+ 'delete_logs_enable' => 0,
+ 'delete_logs_schedule_lowest_interval' => 7,
+ 'delete_logs_older_than' => 180,
+ 'delete_logs_max_rows_per_query' => self::DEFAULT_MAX_ROWS_PER_QUERY,
+ 'delete_reports_enable' => 0,
+ 'delete_reports_older_than' => 12,
+ 'delete_reports_keep_basic_metrics' => 1,
+ 'delete_reports_keep_day_reports' => 0,
+ 'delete_reports_keep_week_reports' => 0,
+ 'delete_reports_keep_month_reports' => 1,
+ 'delete_reports_keep_year_reports' => 1,
+ 'delete_reports_keep_range_reports' => 0,
+ 'delete_reports_keep_segment_reports' => 0,
+ );
+
public function getInformation()
{
return array(
- 'description' => Piwik_Translate('PrivacyManager_PluginDescription'),
- 'author' => 'Piwik',
+ 'description' => Piwik_Translate('PrivacyManager_PluginDescription'),
+ 'author' => 'Piwik',
'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
+ 'version' => Piwik_Version::VERSION,
);
}
public function getListHooksRegistered()
{
return array(
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'AdminMenu.add' => 'addMenu',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'AdminMenu.add' => 'addMenu',
'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
);
}
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getScheduledTasks($notification)
- {
- $tasks = &$notification->getNotificationObject();
-
- // both tasks are low priority so they will execute after most others, but not lowest, so
- // they will execute before the optimize tables task
-
- $purgeReportDataTask = new Piwik_ScheduledTask(
- $this, 'deleteReportData', null, new Piwik_ScheduledTime_Daily(), Piwik_ScheduledTask::LOW_PRIORITY
- );
- $tasks[] = $purgeReportDataTask;
-
- $purgeLogDataTask = new Piwik_ScheduledTask(
- $this, 'deleteLogData', null, new Piwik_ScheduledTime_Daily(), Piwik_ScheduledTask::LOW_PRIORITY
- );
- $tasks[] = $purgeLogDataTask;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getScheduledTasks($notification)
+ {
+ $tasks = & $notification->getNotificationObject();
+
+ // both tasks are low priority so they will execute after most others, but not lowest, so
+ // they will execute before the optimize tables task
+
+ $purgeReportDataTask = new Piwik_ScheduledTask(
+ $this, 'deleteReportData', null, new Piwik_ScheduledTime_Daily(), Piwik_ScheduledTask::LOW_PRIORITY
+ );
+ $tasks[] = $purgeReportDataTask;
+
+ $purgeLogDataTask = new Piwik_ScheduledTask(
+ $this, 'deleteLogData', null, new Piwik_ScheduledTime_Daily(), Piwik_ScheduledTask::LOW_PRIORITY
+ );
+ $tasks[] = $purgeLogDataTask;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
function getJsFiles($notification)
{
- $jsFiles = &$notification->getNotificationObject();
+ $jsFiles = & $notification->getNotificationObject();
$jsFiles[] = "plugins/PrivacyManager/templates/privacySettings.js";
}
@@ -100,131 +100,118 @@ class Piwik_PrivacyManager extends Piwik_Plugin
function addMenu()
{
Piwik_AddAdminMenu('PrivacyManager_MenuPrivacySettings',
- array('module' => 'PrivacyManager', 'action' => 'privacySettings'),
- Piwik::isUserHasSomeAdminAccess(),
- $order = 7);
+ array('module' => 'PrivacyManager', 'action' => 'privacySettings'),
+ Piwik::isUserHasSomeAdminAccess(),
+ $order = 7);
+ }
+
+ /**
+ * Returns the settings for the data purging feature.
+ *
+ * @return array
+ */
+ public static function getPurgeDataSettings()
+ {
+ $settings = array();
+
+ // load settings from ini config
+ try {
+ $oldSettings = array(
+ 'enable_auto_database_size_estimate',
+
+ // backwards compatibility: load old values in ini config if present
+ 'delete_logs_enable',
+ 'delete_logs_schedule_lowest_interval',
+ 'delete_logs_older_than',
+ );
+
+ $deleteLogsSettings = Piwik_Config::getInstance()->Deletelogs;
+ foreach ($oldSettings as $settingName) {
+ $settings[$settingName] = $deleteLogsSettings[$settingName];
+ }
+ } catch (Exception $e) {
+ // ignore
+ }
+
+ // load the settings for the data purging settings
+ foreach (self::$defaultPurgeDataOptions as $optionName => $defaultValue) {
+ $value = Piwik_GetOption($optionName);
+ if ($value !== false) {
+ $settings[$optionName] = $value;
+ } else {
+ // if the option hasn't been set/created, use the default value
+ if (!isset($settings[$optionName])) {
+ $settings[$optionName] = $defaultValue;
+ }
+
+ // option is not saved in the DB, so save it now
+ Piwik_SetOption($optionName, $settings[$optionName]);
+ }
+ }
+
+ return $settings;
+ }
+
+ /**
+ * Saves the supplied data purging settings.
+ *
+ * @param array $settings The settings to save.
+ */
+ public static function savePurgeDataSettings($settings)
+ {
+ $plugin = Piwik_PluginsManager::getInstance()->getLoadedPlugin('PrivacyManager');
+
+ foreach (self::$defaultPurgeDataOptions as $optionName => $defaultValue) {
+ if (isset($settings[$optionName])) {
+ Piwik_SetOption($optionName, $settings[$optionName]);
+ }
+ }
}
-
- /**
- * Returns the settings for the data purging feature.
- *
- * @return array
- */
- public static function getPurgeDataSettings()
- {
- $settings = array();
-
- // load settings from ini config
- try
- {
- $oldSettings = array(
- 'enable_auto_database_size_estimate',
-
- // backwards compatibility: load old values in ini config if present
- 'delete_logs_enable',
- 'delete_logs_schedule_lowest_interval',
- 'delete_logs_older_than',
- );
-
- $deleteLogsSettings = Piwik_Config::getInstance()->Deletelogs;
- foreach ($oldSettings as $settingName)
- {
- $settings[$settingName] = $deleteLogsSettings[$settingName];
- }
- }
- catch (Exception $e)
- {
- // ignore
- }
-
- // load the settings for the data purging settings
- foreach (self::$defaultPurgeDataOptions as $optionName => $defaultValue)
- {
- $value = Piwik_GetOption($optionName);
- if ($value !== false)
- {
- $settings[$optionName] = $value;
- }
- else
- {
- // if the option hasn't been set/created, use the default value
- if (!isset($settings[$optionName]))
- {
- $settings[$optionName] = $defaultValue;
- }
-
- // option is not saved in the DB, so save it now
- Piwik_SetOption($optionName, $settings[$optionName]);
- }
- }
-
- return $settings;
- }
-
- /**
- * Saves the supplied data purging settings.
- *
- * @param array $settings The settings to save.
- */
- public static function savePurgeDataSettings( $settings )
- {
- $plugin = Piwik_PluginsManager::getInstance()->getLoadedPlugin('PrivacyManager');
-
- foreach (self::$defaultPurgeDataOptions as $optionName => $defaultValue)
- {
- if (isset($settings[$optionName]))
- {
- Piwik_SetOption($optionName, $settings[$optionName]);
- }
- }
- }
-
+
/**
- * Deletes old archived data (reports & metrics).
- *
- * Archive tables are not optimized after, as that is handled by a separate scheduled task
- * in CoreAdminHome. This is a scheduled task and will only execute every N days. The number
+ * Deletes old archived data (reports & metrics).
+ *
+ * Archive tables are not optimized after, as that is handled by a separate scheduled task
+ * in CoreAdminHome. This is a scheduled task and will only execute every N days. The number
* of days is determined by the delete_logs_schedule_lowest_interval config option.
- *
+ *
* If delete_reports_enable is set to 1, old archive data is deleted. The following
* config options can tweak this behavior:
* - delete_reports_older_than: The number of months after which archive data is considered
* old. The current month is not considered when applying this
* value.
- * - delete_reports_keep_basic_metrics: If set to 1, keeps certain metric data. Right now,
+ * - delete_reports_keep_basic_metrics: If set to 1, keeps certain metric data. Right now,
* all metric data is kept.
* - delete_reports_keep_day_reports: If set to 1, keeps old daily reports.
* - delete_reports_keep_week_reports: If set to 1, keeps old weekly reports.
* - delete_reports_keep_month_reports: If set to 1, keeps old monthly reports.
* - delete_reports_keep_year_reports: If set to 1, keeps old yearly reports.
*/
- public function deleteReportData()
- {
+ public function deleteReportData()
+ {
$settings = self::getPurgeDataSettings();
-
+
// Make sure, data deletion is enabled
- if ($settings['delete_reports_enable'] == 0)
- {
+ if ($settings['delete_reports_enable'] == 0) {
return;
}
-
+
// make sure purging should run at this time (unless this is a forced purge)
- if (!$this->shouldPurgeData($settings, self::OPTION_LAST_DELETE_PIWIK_REPORTS))
- {
- return;
+ if (!$this->shouldPurgeData($settings, self::OPTION_LAST_DELETE_PIWIK_REPORTS)) {
+ return;
}
-
+
// set last run time
Piwik_SetOption(self::OPTION_LAST_DELETE_PIWIK_REPORTS, Piwik_Date::factory('today')->getTimestamp());
-
- Piwik_PrivacyManager_ReportsPurger::make($settings, self::getAllMetricsToKeep())->purgeData();
- }
-
+
+ Piwik_PrivacyManager_ReportsPurger::make($settings, self::getAllMetricsToKeep())->purgeData();
+ }
+
/**
* Deletes old log data based on the options set in the Deletelogs config
* section. This is a scheduled task and will only execute every N days. The number
* of days is determined by the delete_logs_schedule_lowest_interval config option.
- *
+ *
* If delete_logs_enable is set to 1, old data in the log_visit, log_conversion,
* log_conversion_item and log_link_visit_action tables is deleted. The following
* options can tweak this behavior:
@@ -236,19 +223,17 @@ class Piwik_PrivacyManager extends Piwik_Plugin
public function deleteLogData()
{
$settings = self::getPurgeDataSettings();
-
+
// Make sure, data deletion is enabled
- if ($settings['delete_logs_enable'] == 0)
- {
+ if ($settings['delete_logs_enable'] == 0) {
return;
}
-
+
// make sure purging should run at this time
- if (!$this->shouldPurgeData($settings, self::OPTION_LAST_DELETE_PIWIK_LOGS))
- {
- return;
+ if (!$this->shouldPurgeData($settings, self::OPTION_LAST_DELETE_PIWIK_LOGS)) {
+ return;
}
-
+
/*
* Tell the DB that log deletion has run BEFORE deletion is executed;
* If deletion / table optimization exceeds execution time, other tasks maybe prevented of being executed
@@ -256,169 +241,158 @@ class Piwik_PrivacyManager extends Piwik_Plugin
*/
$lastDeleteDate = Piwik_Date::factory("today")->getTimestamp();
Piwik_SetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS, $lastDeleteDate);
-
+
// execute the purge
Piwik_PrivacyManager_LogDataPurger::make($settings)->purgeData();
}
-
+
/**
* Returns an array describing what data would be purged if both log data & report
* purging is invoked.
- *
+ *
* The returned array maps table names with the number of rows that will be deleted.
* If the table name is mapped with -1, the table will be dropped.
- *
+ *
* @param array $settings The config options to use in the estimate. If null, the real
* options are used.
* @return array
*/
- public static function getPurgeEstimate( $settings = null )
+ public static function getPurgeEstimate($settings = null)
+ {
+ if (is_null($settings)) {
+ $settings = self::getPurgeDataSettings();
+ }
+
+ $result = array();
+
+ if ($settings['delete_logs_enable']) {
+ $logDataPurger = Piwik_PrivacyManager_LogDataPurger::make($settings);
+ $result = array_merge($result, $logDataPurger->getPurgeEstimate());
+ }
+
+ if ($settings['delete_reports_enable']) {
+ $reportsPurger = Piwik_PrivacyManager_ReportsPurger::make($settings, self::getAllMetricsToKeep());
+ $result = array_merge($result, $reportsPurger->getPurgeEstimate());
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns true if a report with the given year & month should be purged or not.
+ *
+ * If reportsOlderThan is set to null or not supplied, this function will check if
+ * a report should be purged, based on existing configuration. In this case, if
+ * delete_reports_enable is set to 0, this function will return false.
+ *
+ * @param int $reportDateYear The year of the report in question.
+ * @param int $reportDateMonth The month of the report in question.
+ * @param int|Piwik_Date $reportsOlderThan If an int, the number of months a report must be older than
+ * in order to be purged. If a date, the date a report must be
+ * older than in order to be purged.
+ * @return bool
+ */
+ public static function shouldReportBePurged($reportDateYear, $reportDateMonth, $reportsOlderThan = null)
+ {
+ // if no 'older than' value/date was supplied, use existing config
+ if (is_null($reportsOlderThan)) {
+ // if report deletion is not enabled, the report shouldn't be purged
+ $settings = self::getPurgeDataSettings();
+ if ($settings['delete_reports_enable'] == 0) {
+ return false;
+ }
+
+ $reportsOlderThan = $settings['delete_reports_older_than'];
+ }
+
+ // if a integer was supplied, assume it is the number of months a report must be older than
+ if (!($reportsOlderThan instanceof Piwik_Date)) {
+ $reportsOlderThan = Piwik_Date::factory('today')->subMonth(1 + $reportsOlderThan);
+ }
+
+ return Piwik_PrivacyManager_ReportsPurger::shouldReportBePurged(
+ $reportDateYear, $reportDateMonth, $reportsOlderThan);
+ }
+
+ /**
+ * Returns the general metrics to keep when the 'delete_reports_keep_basic_metrics'
+ * config is set to 1.
+ */
+ private static function getMetricsToKeep()
+ {
+ return array('nb_uniq_visitors', 'nb_visits', 'nb_actions', 'max_actions',
+ 'sum_visit_length', 'bounce_count', 'nb_visits_converted', 'nb_conversions',
+ 'revenue', 'quantity', 'price', 'orders');
+ }
+
+ /**
+ * Returns the goal metrics to keep when the 'delete_reports_keep_basic_metrics'
+ * config is set to 1.
+ */
+ private static function getGoalMetricsToKeep()
+ {
+ // keep all goal metrics
+ return array_values(Piwik_Archive::$mappingFromIdToNameGoal);
+ }
+
+ /**
+ * Returns the names of metrics that should be kept when purging as they appear in
+ * archive tables.
+ */
+ public static function getAllMetricsToKeep()
{
- if (is_null($settings))
- {
- $settings = self::getPurgeDataSettings();
- }
-
- $result = array();
-
- if ($settings['delete_logs_enable'])
- {
- $logDataPurger = Piwik_PrivacyManager_LogDataPurger::make($settings);
- $result = array_merge($result, $logDataPurger->getPurgeEstimate());
- }
-
- if ($settings['delete_reports_enable'])
- {
- $reportsPurger = Piwik_PrivacyManager_ReportsPurger::make($settings, self::getAllMetricsToKeep());
- $result = array_merge($result, $reportsPurger->getPurgeEstimate());
- }
-
- return $result;
+ $metricsToKeep = self::getMetricsToKeep();
+
+ // convert goal metric names to correct archive names
+ if (Piwik_Common::isGoalPluginEnabled()) {
+ $goalMetricsToKeep = self::getGoalMetricsToKeep();
+
+ $maxGoalId = self::getMaxGoalId();
+
+ // for each goal metric, there's a different name for each goal, including the overview,
+ // the order report & cart report
+ foreach ($goalMetricsToKeep as $metric) {
+ for ($i = 1; $i <= $maxGoalId; ++$i) // maxGoalId can be 0
+ {
+ $metricsToKeep[] = Piwik_Goals::getRecordName($metric, $i);
+ }
+
+ $metricsToKeep[] = Piwik_Goals::getRecordName($metric);
+ $metricsToKeep[] = Piwik_Goals::getRecordName($metric, Piwik_Tracker_GoalManager::IDGOAL_ORDER);
+ $metricsToKeep[] = Piwik_Goals::getRecordName($metric, Piwik_Tracker_GoalManager::IDGOAL_CART);
+ }
+ }
+
+ return $metricsToKeep;
}
-
- /**
- * Returns true if a report with the given year & month should be purged or not.
- *
- * If reportsOlderThan is set to null or not supplied, this function will check if
- * a report should be purged, based on existing configuration. In this case, if
- * delete_reports_enable is set to 0, this function will return false.
- *
- * @param int $reportDateYear The year of the report in question.
- * @param int $reportDateMonth The month of the report in question.
- * @param int|Piwik_Date $reportsOlderThan If an int, the number of months a report must be older than
- * in order to be purged. If a date, the date a report must be
- * older than in order to be purged.
- * @return bool
- */
- public static function shouldReportBePurged( $reportDateYear, $reportDateMonth, $reportsOlderThan = null )
- {
- // if no 'older than' value/date was supplied, use existing config
- if (is_null($reportsOlderThan))
- {
- // if report deletion is not enabled, the report shouldn't be purged
- $settings = self::getPurgeDataSettings();
- if ($settings['delete_reports_enable'] == 0)
- {
- return false;
- }
-
- $reportsOlderThan = $settings['delete_reports_older_than'];
- }
-
- // if a integer was supplied, assume it is the number of months a report must be older than
- if (!($reportsOlderThan instanceof Piwik_Date))
- {
- $reportsOlderThan = Piwik_Date::factory('today')->subMonth(1 + $reportsOlderThan);
- }
-
- return Piwik_PrivacyManager_ReportsPurger::shouldReportBePurged(
- $reportDateYear, $reportDateMonth, $reportsOlderThan);
- }
-
- /**
- * Returns the general metrics to keep when the 'delete_reports_keep_basic_metrics'
- * config is set to 1.
- */
- private static function getMetricsToKeep()
- {
- return array('nb_uniq_visitors', 'nb_visits', 'nb_actions', 'max_actions',
- 'sum_visit_length', 'bounce_count', 'nb_visits_converted', 'nb_conversions',
- 'revenue', 'quantity', 'price', 'orders');
- }
-
- /**
- * Returns the goal metrics to keep when the 'delete_reports_keep_basic_metrics'
- * config is set to 1.
- */
- private static function getGoalMetricsToKeep()
- {
- // keep all goal metrics
- return array_values(Piwik_Archive::$mappingFromIdToNameGoal);
- }
-
- /**
- * Returns the names of metrics that should be kept when purging as they appear in
- * archive tables.
- */
- public static function getAllMetricsToKeep()
- {
- $metricsToKeep = self::getMetricsToKeep();
-
- // convert goal metric names to correct archive names
- if (Piwik_Common::isGoalPluginEnabled())
- {
- $goalMetricsToKeep = self::getGoalMetricsToKeep();
-
- $maxGoalId = self::getMaxGoalId();
-
- // for each goal metric, there's a different name for each goal, including the overview,
- // the order report & cart report
- foreach ($goalMetricsToKeep as $metric)
- {
- for ($i = 1; $i <= $maxGoalId; ++$i) // maxGoalId can be 0
- {
- $metricsToKeep[] = Piwik_Goals::getRecordName($metric, $i);
- }
-
- $metricsToKeep[] = Piwik_Goals::getRecordName($metric);
- $metricsToKeep[] = Piwik_Goals::getRecordName($metric, Piwik_Tracker_GoalManager::IDGOAL_ORDER);
- $metricsToKeep[] = Piwik_Goals::getRecordName($metric, Piwik_Tracker_GoalManager::IDGOAL_CART);
- }
- }
-
- return $metricsToKeep;
- }
-
- /**
- * Returns true if one of the purge data tasks should run now, false if it shouldn't.
- */
- private function shouldPurgeData( $settings, $lastRanOption )
- {
+
+ /**
+ * Returns true if one of the purge data tasks should run now, false if it shouldn't.
+ */
+ private function shouldPurgeData($settings, $lastRanOption)
+ {
// Log deletion may not run until it is once rescheduled (initial run). This is the
// only way to guarantee the calculated next scheduled deletion time.
$initialDelete = Piwik_GetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL);
- if (empty($initialDelete))
- {
+ if (empty($initialDelete)) {
Piwik_SetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
return false;
}
-
+
// Make sure, log purging is allowed to run now
$lastDelete = Piwik_GetOption($lastRanOption);
$deleteIntervalDays = $settings['delete_logs_schedule_lowest_interval'];
$deleteIntervalSeconds = $this->getDeleteIntervalInSeconds($deleteIntervalDays);
-
- if ($lastDelete === false ||
- ($lastDelete !== false && ((int)$lastDelete + $deleteIntervalSeconds) <= time())
- )
- {
- return true;
- }
- else // not time to run data purge
- {
- return false;
- }
- }
+
+ if ($lastDelete === false ||
+ ($lastDelete !== false && ((int)$lastDelete + $deleteIntervalSeconds) <= time())
+ ) {
+ return true;
+ } else // not time to run data purge
+ {
+ return false;
+ }
+ }
function getDeleteIntervalInSeconds($deleteInterval)
{
@@ -427,7 +401,7 @@ class Piwik_PrivacyManager extends Piwik_Plugin
private static function getMaxGoalId()
{
- return Piwik_FetchOne("SELECT MAX(idgoal) FROM ".Piwik_Common::prefixTable('goal'));
+ return Piwik_FetchOne("SELECT MAX(idgoal) FROM " . Piwik_Common::prefixTable('goal'));
}
}
diff --git a/plugins/PrivacyManager/ReportsPurger.php b/plugins/PrivacyManager/ReportsPurger.php
index 88cd043b08..61c650a613 100755
--- a/plugins/PrivacyManager/ReportsPurger.php
+++ b/plugins/PrivacyManager/ReportsPurger.php
@@ -16,404 +16,367 @@ class Piwik_PrivacyManager_ReportsPurger
{
// constant used in database purging estimate to signify a table should be dropped
const DROP_TABLE = -1;
-
- /**
- * The max set of rows each table scan select should query at one time.
- */
- public static $selectSegmentSize = 100000;
-
- /**
+
+ /**
+ * The max set of rows each table scan select should query at one time.
+ */
+ public static $selectSegmentSize = 100000;
+
+ /**
* The number of months after which report/metric data is considered old.
*/
- private $deleteReportsOlderThan;
-
- /**
- * Whether to keep basic metrics or not.
- */
- private $keepBasicMetrics;
-
- /**
- * Array of period types. Reports for these periods will not be purged.
- */
- private $reportPeriodsToKeep;
-
- /**
- * Whether to keep reports for segments or not.
- */
- private $keepSegmentReports;
-
- /**
- * The maximum number of rows to delete per DELETE query.
- */
- private $maxRowsToDeletePerQuery;
-
- /**
- * List of metrics that should be kept when purging. If $keepBasicMetrics is true,
- * these metrics will be saved.
- */
- private $metricsToKeep;
-
- /**
- * Array that maps a year and month ('2012_01') with lists of archive IDs for segmented
- * archives. Used to keep segmented reports when purging.
- */
- private $segmentArchiveIds = null;
-
- /**
- * Constructor.
- *
- * @param int $deleteReportsOlderThan The number of months after which report/metric data
- * is considered old.
- * @param bool $keepBasicMetrics Whether to keep basic metrics or not.
- * @param array $reportPeriodsToKeep Array of period types. Reports for these periods will not
- * be purged.
- * @param bool $keepSegmentReports Whether to keep reports for segments or not.
- * @param array $metricsToKeep List of metrics that should be kept. if $keepBasicMetrics
- * is true, these metrics will be saved.
- * @param int $maxRowsToDeletePerQuery The maximum number of rows to delete per DELETE query.
- */
- public function __construct( $deleteReportsOlderThan, $keepBasicMetrics, $reportPeriodsToKeep,
- $keepSegmentReports, $metricsToKeep, $maxRowsToDeletePerQuery )
- {
- $this->deleteReportsOlderThan = $deleteReportsOlderThan;
- $this->keepBasicMetrics = $keepBasicMetrics;
- $this->reportPeriodsToKeep = $reportPeriodsToKeep;
- $this->keepSegmentReports = $keepSegmentReports;
- $this->metricsToKeep = $metricsToKeep;
- $this->maxRowsToDeletePerQuery = $maxRowsToDeletePerQuery;
- }
-
- /**
- * Purges old report/metric data.
- *
- * If $keepBasicMetrics is false, old numeric tables will be dropped, otherwise only
- * the metrics not in $metricsToKeep will be deleted.
- *
- * If $reportPeriodsToKeep is an empty array, old blob tables will be dropped. Otherwise,
- * specific reports will be deleted, except reports for periods in $reportPeriodsToKeep.
- *
- * @param bool $optimize If tables should be optimized after rows are deleted. Normally,
- * this is handled by a scheduled task.
- */
- public function purgeData($optimize = false)
- {
- // find archive tables to purge
- list($oldNumericTables, $oldBlobTables) = $this->getArchiveTablesToPurge();
-
- // process blob tables first, since archive status is stored in the numeric archives
- if (!empty($oldBlobTables))
- {
- // if no reports should be kept, drop tables, otherwise drop individual reports
- if (empty($this->reportPeriodsToKeep) && !$this->keepSegmentReports)
- {
- Piwik_DropTables($oldBlobTables);
- }
- else
- {
- foreach ($oldBlobTables as $table)
- {
- $where = $this->getBlobTableWhereExpr($oldNumericTables, $table);
- if (!empty($where))
- {
- $where = "WHERE $where";
- }
- Piwik_DeleteAllRows($table, $where, $this->maxRowsToDeletePerQuery);
- }
-
- if ($optimize)
- {
- Piwik_OptimizeTables($oldBlobTables);
- }
- }
- }
-
- // deal with numeric tables
- if (!empty($oldNumericTables))
- {
- // if keep_basic_metrics is set, empty all numeric tables of metrics to purge
- if ($this->keepBasicMetrics == 1 && !empty($this->metricsToKeep))
- {
- $where = "WHERE name NOT IN ('".implode("','", $this->metricsToKeep)."') AND name NOT LIKE 'done%'";
- foreach ($oldNumericTables as $table)
- {
- Piwik_DeleteAllRows($table, $where, $this->maxRowsToDeletePerQuery);
- }
-
- if ($optimize)
- {
- Piwik_OptimizeTables($oldNumericTables);
- }
- }
- else // drop numeric tables
- {
- Piwik_DropTables($oldNumericTables);
- }
- }
- }
-
- /**
- * Returns an array describing what data would be purged if purging were invoked.
- *
- * This function returns an array that maps table names with the number of rows
- * that will be deleted. If a table name is mapped with self::DROP_TABLE, the table
- * will be dropped.
- *
- * @return array
- */
- public function getPurgeEstimate()
- {
- $result = array();
-
- // get archive tables that will be purged
- list($oldNumericTables, $oldBlobTables) = $this->getArchiveTablesToPurge();
-
- // process blob tables first, since archive status is stored in the numeric archives
- if (empty($this->reportPeriodsToKeep) && !$this->keepSegmentReports)
- {
- // not keeping any reports, so drop all tables
- foreach ($oldBlobTables as $table)
- {
- $result[$table] = self::DROP_TABLE;
- }
- }
- else
- {
- // figure out which rows will be deleted
- foreach ($oldBlobTables as $table)
- {
- $rowCount = $this->getBlobTableDeleteCount($oldNumericTables, $table);
- if ($rowCount > 0)
- {
- $result[$table] = $rowCount;
- }
- }
- }
-
- // deal w/ numeric tables
- if ($this->keepBasicMetrics == 1)
- {
- // figure out which rows will be deleted
- foreach ($oldNumericTables as $table)
- {
- $rowCount = $this->getNumericTableDeleteCount($table);
- if ($rowCount > 0)
- {
- $result[$table] = $rowCount;
- }
- }
- }
- else
- {
- // not keeping any metrics, so drop the entire table
- foreach ($oldNumericTables as $table)
- {
- $result[$table] = self::DROP_TABLE;
- }
- }
-
- return $result;
- }
-
- /**
- * Utility function that finds every archive table whose reports are considered
- * old.
- *
- * @return array An array of two arrays. The first holds the numeric archive table
- * names, and the second holds the blob archive table names.
- */
- private function getArchiveTablesToPurge()
- {
- // get month for which reports as old or older than, should be deleted
- // reports whose creation date <= this month will be deleted
- // (NOTE: we ignore how far we are in the current month)
- $toRemoveDate = Piwik_Date::factory('today')->subMonth(1 + $this->deleteReportsOlderThan);
- $toRemoveYear = (int)$toRemoveDate->toString('Y');
- $toRemoveMonth = (int)$toRemoveDate->toString('m');
-
- // find all archive tables that are older than N months
- $oldNumericTables = array();
- $oldBlobTables = array();
- foreach (Piwik::getTablesInstalled() as $table)
- {
- if (preg_match("/archive_(numeric|blob)_([0-9]+)_([0-9]+)/", $table, $matches))
- {
- $type = $matches[1];
- $year = (int)$matches[2];
- $month = (int)$matches[3];
-
- if (self::shouldReportBePurged($year, $month, $toRemoveDate))
- {
- if ($type == "numeric")
- {
- $oldNumericTables[] = $table;
- }
- else
- {
- $oldBlobTables[] = $table;
- }
- }
- }
- }
-
- return array($oldNumericTables, $oldBlobTables);
- }
-
- /**
- * Returns true if a report with the given year & month should be purged or not.
- *
- * @param int $reportDateYear The year of the report in question.
- * @param int $reportDateMonth The month of the report in question.
- * @param Piwik_Date $toRemoveDate The date a report must be older than in order to be purged.
- * @return bool
- */
- public static function shouldReportBePurged( $reportDateYear, $reportDateMonth, $toRemoveDate )
- {
- $toRemoveYear = (int)$toRemoveDate->toString('Y');
- $toRemoveMonth = (int)$toRemoveDate->toString('m');
-
- return $reportDateYear < $toRemoveYear
- || ($reportDateYear == $toRemoveYear && $reportDateMonth <= $toRemoveMonth);
- }
-
- private function getNumericTableDeleteCount( $table )
- {
- $maxIdArchive = Piwik_FetchOne("SELECT MAX(idarchive) FROM $table");
-
- $sql = "SELECT COUNT(*)
+ private $deleteReportsOlderThan;
+
+ /**
+ * Whether to keep basic metrics or not.
+ */
+ private $keepBasicMetrics;
+
+ /**
+ * Array of period types. Reports for these periods will not be purged.
+ */
+ private $reportPeriodsToKeep;
+
+ /**
+ * Whether to keep reports for segments or not.
+ */
+ private $keepSegmentReports;
+
+ /**
+ * The maximum number of rows to delete per DELETE query.
+ */
+ private $maxRowsToDeletePerQuery;
+
+ /**
+ * List of metrics that should be kept when purging. If $keepBasicMetrics is true,
+ * these metrics will be saved.
+ */
+ private $metricsToKeep;
+
+ /**
+ * Array that maps a year and month ('2012_01') with lists of archive IDs for segmented
+ * archives. Used to keep segmented reports when purging.
+ */
+ private $segmentArchiveIds = null;
+
+ /**
+ * Constructor.
+ *
+ * @param int $deleteReportsOlderThan The number of months after which report/metric data
+ * is considered old.
+ * @param bool $keepBasicMetrics Whether to keep basic metrics or not.
+ * @param array $reportPeriodsToKeep Array of period types. Reports for these periods will not
+ * be purged.
+ * @param bool $keepSegmentReports Whether to keep reports for segments or not.
+ * @param array $metricsToKeep List of metrics that should be kept. if $keepBasicMetrics
+ * is true, these metrics will be saved.
+ * @param int $maxRowsToDeletePerQuery The maximum number of rows to delete per DELETE query.
+ */
+ public function __construct($deleteReportsOlderThan, $keepBasicMetrics, $reportPeriodsToKeep,
+ $keepSegmentReports, $metricsToKeep, $maxRowsToDeletePerQuery)
+ {
+ $this->deleteReportsOlderThan = $deleteReportsOlderThan;
+ $this->keepBasicMetrics = $keepBasicMetrics;
+ $this->reportPeriodsToKeep = $reportPeriodsToKeep;
+ $this->keepSegmentReports = $keepSegmentReports;
+ $this->metricsToKeep = $metricsToKeep;
+ $this->maxRowsToDeletePerQuery = $maxRowsToDeletePerQuery;
+ }
+
+ /**
+ * Purges old report/metric data.
+ *
+ * If $keepBasicMetrics is false, old numeric tables will be dropped, otherwise only
+ * the metrics not in $metricsToKeep will be deleted.
+ *
+ * If $reportPeriodsToKeep is an empty array, old blob tables will be dropped. Otherwise,
+ * specific reports will be deleted, except reports for periods in $reportPeriodsToKeep.
+ *
+ * @param bool $optimize If tables should be optimized after rows are deleted. Normally,
+ * this is handled by a scheduled task.
+ */
+ public function purgeData($optimize = false)
+ {
+ // find archive tables to purge
+ list($oldNumericTables, $oldBlobTables) = $this->getArchiveTablesToPurge();
+
+ // process blob tables first, since archive status is stored in the numeric archives
+ if (!empty($oldBlobTables)) {
+ // if no reports should be kept, drop tables, otherwise drop individual reports
+ if (empty($this->reportPeriodsToKeep) && !$this->keepSegmentReports) {
+ Piwik_DropTables($oldBlobTables);
+ } else {
+ foreach ($oldBlobTables as $table) {
+ $where = $this->getBlobTableWhereExpr($oldNumericTables, $table);
+ if (!empty($where)) {
+ $where = "WHERE $where";
+ }
+ Piwik_DeleteAllRows($table, $where, $this->maxRowsToDeletePerQuery);
+ }
+
+ if ($optimize) {
+ Piwik_OptimizeTables($oldBlobTables);
+ }
+ }
+ }
+
+ // deal with numeric tables
+ if (!empty($oldNumericTables)) {
+ // if keep_basic_metrics is set, empty all numeric tables of metrics to purge
+ if ($this->keepBasicMetrics == 1 && !empty($this->metricsToKeep)) {
+ $where = "WHERE name NOT IN ('" . implode("','", $this->metricsToKeep) . "') AND name NOT LIKE 'done%'";
+ foreach ($oldNumericTables as $table) {
+ Piwik_DeleteAllRows($table, $where, $this->maxRowsToDeletePerQuery);
+ }
+
+ if ($optimize) {
+ Piwik_OptimizeTables($oldNumericTables);
+ }
+ } else // drop numeric tables
+ {
+ Piwik_DropTables($oldNumericTables);
+ }
+ }
+ }
+
+ /**
+ * Returns an array describing what data would be purged if purging were invoked.
+ *
+ * This function returns an array that maps table names with the number of rows
+ * that will be deleted. If a table name is mapped with self::DROP_TABLE, the table
+ * will be dropped.
+ *
+ * @return array
+ */
+ public function getPurgeEstimate()
+ {
+ $result = array();
+
+ // get archive tables that will be purged
+ list($oldNumericTables, $oldBlobTables) = $this->getArchiveTablesToPurge();
+
+ // process blob tables first, since archive status is stored in the numeric archives
+ if (empty($this->reportPeriodsToKeep) && !$this->keepSegmentReports) {
+ // not keeping any reports, so drop all tables
+ foreach ($oldBlobTables as $table) {
+ $result[$table] = self::DROP_TABLE;
+ }
+ } else {
+ // figure out which rows will be deleted
+ foreach ($oldBlobTables as $table) {
+ $rowCount = $this->getBlobTableDeleteCount($oldNumericTables, $table);
+ if ($rowCount > 0) {
+ $result[$table] = $rowCount;
+ }
+ }
+ }
+
+ // deal w/ numeric tables
+ if ($this->keepBasicMetrics == 1) {
+ // figure out which rows will be deleted
+ foreach ($oldNumericTables as $table) {
+ $rowCount = $this->getNumericTableDeleteCount($table);
+ if ($rowCount > 0) {
+ $result[$table] = $rowCount;
+ }
+ }
+ } else {
+ // not keeping any metrics, so drop the entire table
+ foreach ($oldNumericTables as $table) {
+ $result[$table] = self::DROP_TABLE;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Utility function that finds every archive table whose reports are considered
+ * old.
+ *
+ * @return array An array of two arrays. The first holds the numeric archive table
+ * names, and the second holds the blob archive table names.
+ */
+ private function getArchiveTablesToPurge()
+ {
+ // get month for which reports as old or older than, should be deleted
+ // reports whose creation date <= this month will be deleted
+ // (NOTE: we ignore how far we are in the current month)
+ $toRemoveDate = Piwik_Date::factory('today')->subMonth(1 + $this->deleteReportsOlderThan);
+ $toRemoveYear = (int)$toRemoveDate->toString('Y');
+ $toRemoveMonth = (int)$toRemoveDate->toString('m');
+
+ // find all archive tables that are older than N months
+ $oldNumericTables = array();
+ $oldBlobTables = array();
+ foreach (Piwik::getTablesInstalled() as $table) {
+ if (preg_match("/archive_(numeric|blob)_([0-9]+)_([0-9]+)/", $table, $matches)) {
+ $type = $matches[1];
+ $year = (int)$matches[2];
+ $month = (int)$matches[3];
+
+ if (self::shouldReportBePurged($year, $month, $toRemoveDate)) {
+ if ($type == "numeric") {
+ $oldNumericTables[] = $table;
+ } else {
+ $oldBlobTables[] = $table;
+ }
+ }
+ }
+ }
+
+ return array($oldNumericTables, $oldBlobTables);
+ }
+
+ /**
+ * Returns true if a report with the given year & month should be purged or not.
+ *
+ * @param int $reportDateYear The year of the report in question.
+ * @param int $reportDateMonth The month of the report in question.
+ * @param Piwik_Date $toRemoveDate The date a report must be older than in order to be purged.
+ * @return bool
+ */
+ public static function shouldReportBePurged($reportDateYear, $reportDateMonth, $toRemoveDate)
+ {
+ $toRemoveYear = (int)$toRemoveDate->toString('Y');
+ $toRemoveMonth = (int)$toRemoveDate->toString('m');
+
+ return $reportDateYear < $toRemoveYear
+ || ($reportDateYear == $toRemoveYear && $reportDateMonth <= $toRemoveMonth);
+ }
+
+ private function getNumericTableDeleteCount($table)
+ {
+ $maxIdArchive = Piwik_FetchOne("SELECT MAX(idarchive) FROM $table");
+
+ $sql = "SELECT COUNT(*)
FROM $table
- WHERE name NOT IN ('".implode("','", $this->metricsToKeep)."')
+ WHERE name NOT IN ('" . implode("','", $this->metricsToKeep) . "')
AND name NOT LIKE 'done%'
AND idarchive >= ?
AND idarchive < ?";
-
- $segments = Piwik_SegmentedFetchOne($sql, 0, $maxIdArchive, self::$selectSegmentSize);
- return array_sum($segments);
- }
-
- private function getBlobTableDeleteCount( $oldNumericTables, $table )
- {
- $maxIdArchive = Piwik_FetchOne("SELECT MAX(idarchive) FROM $table");
-
- $sql = "SELECT COUNT(*)
+
+ $segments = Piwik_SegmentedFetchOne($sql, 0, $maxIdArchive, self::$selectSegmentSize);
+ return array_sum($segments);
+ }
+
+ private function getBlobTableDeleteCount($oldNumericTables, $table)
+ {
+ $maxIdArchive = Piwik_FetchOne("SELECT MAX(idarchive) FROM $table");
+
+ $sql = "SELECT COUNT(*)
FROM $table
- WHERE ".$this->getBlobTableWhereExpr($oldNumericTables, $table)."
+ WHERE " . $this->getBlobTableWhereExpr($oldNumericTables, $table) . "
AND idarchive >= ?
AND idarchive < ?";
-
- $segments = Piwik_SegmentedFetchOne($sql, 0, $maxIdArchive, self::$selectSegmentSize);
- return array_sum($segments);
- }
-
- /** Returns SQL WHERE expression used to find reports that should be purged. */
- private function getBlobTableWhereExpr( $oldNumericTables, $table )
- {
- $where = "";
- if (!empty($this->reportPeriodsToKeep)) // if keeping reports
- {
- $where = "period NOT IN (".implode(',', $this->reportPeriodsToKeep).")";
-
- // if not keeping segments make sure segments w/ kept periods are also deleted
- if (!$this->keepSegmentReports)
- {
- $this->findSegmentArchives($oldNumericTables);
- $archiveIds = $this->segmentArchiveIds[$this->getArchiveTableDate($table)];
-
- if (!empty($archiveIds))
- {
- $where .= " OR idarchive IN (".implode(',', $archiveIds).")";
- }
- }
-
- $where = "($where)";
- }
- return $where;
- }
-
- /**
- * If we're going to keep segmented reports, we need to know which archives are
- * for segments. This info is only in the numeric tables, so we must query them.
- */
- private function findSegmentArchives( $numericTables )
- {
- if (!is_null($this->segmentArchiveIds))
- {
- return;
- }
-
- foreach ($numericTables as $table)
- {
- $tableDate = $this->getArchiveTableDate($table);
-
- $maxIdArchive = Piwik_FetchOne("SELECT MAX(idarchive) FROM $table");
-
- $sql = "SELECT idarchive
+
+ $segments = Piwik_SegmentedFetchOne($sql, 0, $maxIdArchive, self::$selectSegmentSize);
+ return array_sum($segments);
+ }
+
+ /** Returns SQL WHERE expression used to find reports that should be purged. */
+ private function getBlobTableWhereExpr($oldNumericTables, $table)
+ {
+ $where = "";
+ if (!empty($this->reportPeriodsToKeep)) // if keeping reports
+ {
+ $where = "period NOT IN (" . implode(',', $this->reportPeriodsToKeep) . ")";
+
+ // if not keeping segments make sure segments w/ kept periods are also deleted
+ if (!$this->keepSegmentReports) {
+ $this->findSegmentArchives($oldNumericTables);
+ $archiveIds = $this->segmentArchiveIds[$this->getArchiveTableDate($table)];
+
+ if (!empty($archiveIds)) {
+ $where .= " OR idarchive IN (" . implode(',', $archiveIds) . ")";
+ }
+ }
+
+ $where = "($where)";
+ }
+ return $where;
+ }
+
+ /**
+ * If we're going to keep segmented reports, we need to know which archives are
+ * for segments. This info is only in the numeric tables, so we must query them.
+ */
+ private function findSegmentArchives($numericTables)
+ {
+ if (!is_null($this->segmentArchiveIds)) {
+ return;
+ }
+
+ foreach ($numericTables as $table) {
+ $tableDate = $this->getArchiveTableDate($table);
+
+ $maxIdArchive = Piwik_FetchOne("SELECT MAX(idarchive) FROM $table");
+
+ $sql = "SELECT idarchive
FROM $table
WHERE name != 'done'
AND name LIKE 'done_%.%'
AND idarchive >= ?
AND idarchive < ?";
-
- $this->segmentArchiveIds[$tableDate] = array();
- foreach (Piwik_SegmentedFetchAll($sql, 0, $maxIdArchive, self::$selectSegmentSize) as $row)
- {
- $this->segmentArchiveIds[$tableDate][] = $row['idarchive'];
- }
- }
- }
-
- private function getArchiveTableDate( $table )
- {
- preg_match("/[a-zA-Z_]+([0-9]+_[0-9]+)/", $table, $matches);
- return $matches[1];
- }
-
- /**
- * Utility function. Creates a new instance of ReportsPurger with the supplied array
- * of settings.
- *
- * $settings must contain the following keys:
- * -'delete_reports_older_than': The number of months after which reports/metrics are
- * considered old.
- * -'delete_reports_keep_basic_metrics': 1 if basic metrics should be kept, 0 if otherwise.
- * -'delete_reports_keep_day_reports': 1 if daily reports should be kept, 0 if otherwise.
- * -'delete_reports_keep_week_reports': 1 if weekly reports should be kept, 0 if otherwise.
- * -'delete_reports_keep_month_reports': 1 if monthly reports should be kept, 0 if otherwise.
- * -'delete_reports_keep_year_reports': 1 if yearly reports should be kept, 0 if otherwise.
- * -'delete_reports_keep_range_reports': 1 if range reports should be kept, 0 if otherwise.
- * -'delete_reports_keep_segment_reports': 1 if reports for segments should be kept, 0 if otherwise.
- * -'delete_logs_max_rows_per_query': Maximum number of rows to delete in one DELETE query.
- */
- public static function make( $settings, $metricsToKeep )
- {
- return new Piwik_PrivacyManager_ReportsPurger(
- $settings['delete_reports_older_than'],
- $settings['delete_reports_keep_basic_metrics'] == 1,
- self::getReportPeriodsToKeep($settings),
- $settings['delete_reports_keep_segment_reports'] == 1,
- $metricsToKeep,
- $settings['delete_logs_max_rows_per_query']
- );
- }
-
- /**
- * Utility function that returns an array period values based on the 'delete_reports_keep_*'
- * settings. The period values returned are the integer values stored in the DB.
- *
- * @param array $deleteReportSettings The settings to use.
- * @return array An array of period values that should be kept when purging old data.
- */
- private static function getReportPeriodsToKeep( $settings )
- {
- $keepReportPeriods = array();
- foreach (Piwik::$idPeriods as $strPeriod => $intPeriod)
- {
- $optionName = "delete_reports_keep_{$strPeriod}_reports";
- if ($settings[$optionName] == 1)
- {
- $keepReportPeriods[] = $intPeriod;
- }
- }
- return $keepReportPeriods;
- }
+
+ $this->segmentArchiveIds[$tableDate] = array();
+ foreach (Piwik_SegmentedFetchAll($sql, 0, $maxIdArchive, self::$selectSegmentSize) as $row) {
+ $this->segmentArchiveIds[$tableDate][] = $row['idarchive'];
+ }
+ }
+ }
+
+ private function getArchiveTableDate($table)
+ {
+ preg_match("/[a-zA-Z_]+([0-9]+_[0-9]+)/", $table, $matches);
+ return $matches[1];
+ }
+
+ /**
+ * Utility function. Creates a new instance of ReportsPurger with the supplied array
+ * of settings.
+ *
+ * $settings must contain the following keys:
+ * -'delete_reports_older_than': The number of months after which reports/metrics are
+ * considered old.
+ * -'delete_reports_keep_basic_metrics': 1 if basic metrics should be kept, 0 if otherwise.
+ * -'delete_reports_keep_day_reports': 1 if daily reports should be kept, 0 if otherwise.
+ * -'delete_reports_keep_week_reports': 1 if weekly reports should be kept, 0 if otherwise.
+ * -'delete_reports_keep_month_reports': 1 if monthly reports should be kept, 0 if otherwise.
+ * -'delete_reports_keep_year_reports': 1 if yearly reports should be kept, 0 if otherwise.
+ * -'delete_reports_keep_range_reports': 1 if range reports should be kept, 0 if otherwise.
+ * -'delete_reports_keep_segment_reports': 1 if reports for segments should be kept, 0 if otherwise.
+ * -'delete_logs_max_rows_per_query': Maximum number of rows to delete in one DELETE query.
+ */
+ public static function make($settings, $metricsToKeep)
+ {
+ return new Piwik_PrivacyManager_ReportsPurger(
+ $settings['delete_reports_older_than'],
+ $settings['delete_reports_keep_basic_metrics'] == 1,
+ self::getReportPeriodsToKeep($settings),
+ $settings['delete_reports_keep_segment_reports'] == 1,
+ $metricsToKeep,
+ $settings['delete_logs_max_rows_per_query']
+ );
+ }
+
+ /**
+ * Utility function that returns an array period values based on the 'delete_reports_keep_*'
+ * settings. The period values returned are the integer values stored in the DB.
+ *
+ * @param array $deleteReportSettings The settings to use.
+ * @return array An array of period values that should be kept when purging old data.
+ */
+ private static function getReportPeriodsToKeep($settings)
+ {
+ $keepReportPeriods = array();
+ foreach (Piwik::$idPeriods as $strPeriod => $intPeriod) {
+ $optionName = "delete_reports_keep_{$strPeriod}_reports";
+ if ($settings[$optionName] == 1) {
+ $keepReportPeriods[] = $intPeriod;
+ }
+ }
+ return $keepReportPeriods;
+ }
}
diff --git a/plugins/PrivacyManager/templates/databaseSize.tpl b/plugins/PrivacyManager/templates/databaseSize.tpl
index 2093a01e82..fba6612d71 100755
--- a/plugins/PrivacyManager/templates/databaseSize.tpl
+++ b/plugins/PrivacyManager/templates/databaseSize.tpl
@@ -1,7 +1,7 @@
<p>{'PrivacyManager_CurrentDBSize'|translate}: {$dbStats.currentSize}</p>
{if isset($dbStats.sizeAfterPurge)}
-<p>{'PrivacyManager_EstimatedDBSizeAfterPurge'|translate}: <b>{$dbStats.sizeAfterPurge}</b></p>
+ <p>{'PrivacyManager_EstimatedDBSizeAfterPurge'|translate}: <b>{$dbStats.sizeAfterPurge}</b></p>
{/if}
{if isset($dbStats.spaceSaved)}
-<p>{'PrivacyManager_EstimatedSpaceSaved'|translate}: {$dbStats.spaceSaved}</p>
+ <p>{'PrivacyManager_EstimatedSpaceSaved'|translate}: {$dbStats.spaceSaved}</p>
{/if}
diff --git a/plugins/PrivacyManager/templates/privacySettings.js b/plugins/PrivacyManager/templates/privacySettings.js
index 509ef85941..75de9b8caa 100644
--- a/plugins/PrivacyManager/templates/privacySettings.js
+++ b/plugins/PrivacyManager/templates/privacySettings.js
@@ -5,49 +5,49 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-$(document).ready(function() {
- function toggleBlock(id, value) {
- $('#' + id).toggle(value == 1);
- }
-
- function isEitherDeleteSectionEnabled() {
- return ($('input[name=deleteEnable]:checked').val() == 1)
- || ($('input[name=deleteReportsEnable]:checked').val() == 1);
- }
-
- function toggleOtherDeleteSections() {
- var showSection = isEitherDeleteSectionEnabled();
- toggleBlock('deleteDataEstimateSect', showSection);
- toggleBlock('deleteSchedulingSettings', showSection);
- }
-
- // reloads purged database size estimate
- var currentRequest;
- function reloadDbStats(forceEstimate) {
- if (currentRequest) {
- currentRequest.abort();
- }
-
- // if the section isn't visible or the manual estimate link is showing, abort
- // (unless on first load or forcing)
- var isFirstLoad = $('#deleteDataEstimate').html() == '';
- if (!isFirstLoad
- && forceEstimate !== true
- && (!isEitherDeleteSectionEnabled() || $('#getPurgeEstimateLink').length > 0))
- {
- return;
- }
-
- $('#deleteDataEstimate').hide();
-
- var data = $('#formDeleteSettings').serializeArray();
+$(document).ready(function () {
+ function toggleBlock(id, value) {
+ $('#' + id).toggle(value == 1);
+ }
+
+ function isEitherDeleteSectionEnabled() {
+ return ($('input[name=deleteEnable]:checked').val() == 1)
+ || ($('input[name=deleteReportsEnable]:checked').val() == 1);
+ }
+
+ function toggleOtherDeleteSections() {
+ var showSection = isEitherDeleteSectionEnabled();
+ toggleBlock('deleteDataEstimateSect', showSection);
+ toggleBlock('deleteSchedulingSettings', showSection);
+ }
+
+ // reloads purged database size estimate
+ var currentRequest;
+
+ function reloadDbStats(forceEstimate) {
+ if (currentRequest) {
+ currentRequest.abort();
+ }
+
+ // if the section isn't visible or the manual estimate link is showing, abort
+ // (unless on first load or forcing)
+ var isFirstLoad = $('#deleteDataEstimate').html() == '';
+ if (!isFirstLoad
+ && forceEstimate !== true
+ && (!isEitherDeleteSectionEnabled() || $('#getPurgeEstimateLink').length > 0)) {
+ return;
+ }
+
+ $('#deleteDataEstimate').hide();
+
+ var data = $('#formDeleteSettings').serializeArray();
var formData = {};
- for(var i=0; i<data.length; i++) {
+ for (var i = 0; i < data.length; i++) {
formData[data[i].name] = data[i].value;
}
- if (forceEstimate === true) {
+ if (forceEstimate === true) {
formData['forceEstimate'] = 1;
- }
+ }
currentRequest = new ajaxHelper();
currentRequest.setLoadingElement('#deleteDataEstimateSect .loadingPiwik');
@@ -67,107 +67,103 @@ $(document).ready(function() {
);
currentRequest.setFormat('html');
currentRequest.send(false);
- }
-
- // make sure certain sections only display if their corresponding features are enabled
- $('input[name=anonymizeIPEnable]').click(function() {
- toggleBlock("anonymizeIPenabled", $(this).val());
- });
-
- $('input[name=deleteEnable]').click(function() {
- toggleBlock("deleteLogSettings", $(this).val());
- toggleOtherDeleteSections();
- }).change(reloadDbStats);
-
- $('input[name=deleteReportsEnable]').click(function() {
- toggleBlock("deleteReportsSettings", $(this).val());
- toggleBlock("deleteOldReportsMoreInfo", $(this).val());
- toggleOtherDeleteSections();
- }).change(reloadDbStats);
-
- // initial toggling calls
- $(function() {
- toggleBlock("deleteLogSettings", $("input[name=deleteEnable]:checked").val());
- toggleBlock("anonymizeIPenabled", $("input[name=anonymizeIPEnable]:checked").val());
- toggleBlock("deleteReportsSettings", $("input[name=deleteReportsEnable]:checked").val());
- toggleBlock("deleteOldReportsMoreInfo", $("input[name=deleteReportsEnable]:checked").val());
- toggleOtherDeleteSections();
- });
-
- // make sure the DB size estimate is reloaded every time a delete logs/reports setting is changed
- $('#formDeleteSettings input[type=text]').each(function() {
- $(this).change(reloadDbStats);
- });
- $('#formDeleteSettings input[type=checkbox]').each(function() {
- $(this).click(reloadDbStats);
- });
-
- // make sure when the delete log/report settings are submitted, a confirmation popup is
- // displayed first
- $('#deleteLogSettingsSubmit').click(function(e) {
- var deletingLogs = $("input[name=deleteEnable]:checked").val() == 1,
- deletingReports = $("input[name=deleteReportsEnable]:checked").val() == 1,
- confirm_id;
-
- // hide all confirmation texts, then show the correct one based on what
- // type of deletion is enabled.
- $('#confirmDeleteSettings>h2').each(function() {
- $(this).hide();
- });
-
- if (deletingLogs)
- {
- confirm_id = deletingReports ? "deleteBothConfirm" : "deleteLogsConfirm";
- }
- else if (deletingReports)
- {
- confirm_id = "deleteReportsConfirm";
- }
-
- if (confirm_id)
- {
- $("#" + confirm_id).show();
- e.preventDefault();
-
- piwikHelper.modalConfirm('#confirmDeleteSettings', {
- yes: function() {
- $('#formDeleteSettings').submit();
- }
- });
- }
- else
- {
- $('#formDeleteSettings').submit();
- }
- });
-
- // execute purge now link click
- $('#purgeDataNowLink').click(function(e) {
- e.preventDefault();
-
- var link = this;
-
- // if any option has been modified, abort purging and instruct user to save first
- var modified = false;
- $('#formDeleteSettings input').each(function() {
- if (this.type === 'checkbox' || this.type === 'radio') {
- modified |= this.defaultChecked !== this.checked;
- } else {
- modified |= this.defaultValue !== this.value;
- }
- });
-
- if (modified) {
- piwikHelper.modalConfirm('#saveSettingsBeforePurge', {yes: function() {}});
- return;
- }
-
- // ask user if they really want to delete their old data
- piwikHelper.modalConfirm('#confirmPurgeNow', {
- yes: function() {
- $(link).hide();
-
- // execute a data purge
+ }
+
+ // make sure certain sections only display if their corresponding features are enabled
+ $('input[name=anonymizeIPEnable]').click(function () {
+ toggleBlock("anonymizeIPenabled", $(this).val());
+ });
+
+ $('input[name=deleteEnable]').click(function () {
+ toggleBlock("deleteLogSettings", $(this).val());
+ toggleOtherDeleteSections();
+ }).change(reloadDbStats);
+
+ $('input[name=deleteReportsEnable]').click(function () {
+ toggleBlock("deleteReportsSettings", $(this).val());
+ toggleBlock("deleteOldReportsMoreInfo", $(this).val());
+ toggleOtherDeleteSections();
+ }).change(reloadDbStats);
+
+ // initial toggling calls
+ $(function () {
+ toggleBlock("deleteLogSettings", $("input[name=deleteEnable]:checked").val());
+ toggleBlock("anonymizeIPenabled", $("input[name=anonymizeIPEnable]:checked").val());
+ toggleBlock("deleteReportsSettings", $("input[name=deleteReportsEnable]:checked").val());
+ toggleBlock("deleteOldReportsMoreInfo", $("input[name=deleteReportsEnable]:checked").val());
+ toggleOtherDeleteSections();
+ });
+
+ // make sure the DB size estimate is reloaded every time a delete logs/reports setting is changed
+ $('#formDeleteSettings input[type=text]').each(function () {
+ $(this).change(reloadDbStats);
+ });
+ $('#formDeleteSettings input[type=checkbox]').each(function () {
+ $(this).click(reloadDbStats);
+ });
+
+ // make sure when the delete log/report settings are submitted, a confirmation popup is
+ // displayed first
+ $('#deleteLogSettingsSubmit').click(function (e) {
+ var deletingLogs = $("input[name=deleteEnable]:checked").val() == 1,
+ deletingReports = $("input[name=deleteReportsEnable]:checked").val() == 1,
+ confirm_id;
+
+ // hide all confirmation texts, then show the correct one based on what
+ // type of deletion is enabled.
+ $('#confirmDeleteSettings>h2').each(function () {
+ $(this).hide();
+ });
+
+ if (deletingLogs) {
+ confirm_id = deletingReports ? "deleteBothConfirm" : "deleteLogsConfirm";
+ }
+ else if (deletingReports) {
+ confirm_id = "deleteReportsConfirm";
+ }
+
+ if (confirm_id) {
+ $("#" + confirm_id).show();
+ e.preventDefault();
+
+ piwikHelper.modalConfirm('#confirmDeleteSettings', {
+ yes: function () {
+ $('#formDeleteSettings').submit();
+ }
+ });
+ }
+ else {
+ $('#formDeleteSettings').submit();
+ }
+ });
+
+ // execute purge now link click
+ $('#purgeDataNowLink').click(function (e) {
+ e.preventDefault();
+
+ var link = this;
+
+ // if any option has been modified, abort purging and instruct user to save first
+ var modified = false;
+ $('#formDeleteSettings input').each(function () {
+ if (this.type === 'checkbox' || this.type === 'radio') {
+ modified |= this.defaultChecked !== this.checked;
+ } else {
+ modified |= this.defaultValue !== this.value;
+ }
+ });
+
+ if (modified) {
+ piwikHelper.modalConfirm('#saveSettingsBeforePurge', {yes: function () {}});
+ return;
+ }
+
+ // ask user if they really want to delete their old data
+ piwikHelper.modalConfirm('#confirmPurgeNow', {
+ yes: function () {
+ $(link).hide();
+
+ // execute a data purge
var ajaxRequest = new ajaxHelper();
ajaxRequest.setLoadingElement('#deleteSchedulingSettings .loadingPiwik');
ajaxRequest.addParams({
@@ -192,16 +188,16 @@ $(document).ready(function() {
);
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
- }
- });
- });
-
- // get estimate link click
- $('#getPurgeEstimateLink').click(function(e) {
- e.preventDefault();
- reloadDbStats(true);
- });
-
- // load initial db size estimate
- reloadDbStats();
+ }
+ });
+ });
+
+ // get estimate link click
+ $('#getPurgeEstimateLink').click(function (e) {
+ e.preventDefault();
+ reloadDbStats(true);
+ });
+
+ // load initial db size estimate
+ reloadDbStats();
});
diff --git a/plugins/PrivacyManager/templates/privacySettings.tpl b/plugins/PrivacyManager/templates/privacySettings.tpl
index 1550ffd472..f1478cb997 100644
--- a/plugins/PrivacyManager/templates/privacySettings.tpl
+++ b/plugins/PrivacyManager/templates/privacySettings.tpl
@@ -1,251 +1,266 @@
{include file="CoreAdminHome/templates/header.tpl"}
{if $isSuperUser}
+ <h2>{'PrivacyManager_TeaserHeadline'|translate}</h2>
+ <p>{'PrivacyManager_Teaser'|translate:'<a href="#anonymizeIPAnchor">':"</a>":'<a href="#deleteLogsAnchor">':"</a>":'<a href="#optOutAnchor">':"</a>"}
+ See also our official guide <b><a href='http://piwik.org/privacy/' target='_blank'>Web Analytics Privacy</a></b></p>
+ <a name="anonymizeIPAnchor"></a>
+ <h2>{'PrivacyManager_UseAnonymizeIp'|translate}</h2>
+ <form method="post" action="{url action=saveSettings form=formMaskLength token_auth=$token_auth}" id="formMaskLength" name="formMaskLength">
+ <div id='anonymizeIpSettings'>
+ <table class="adminTable" style='width:800px;'>
+ <tr>
+ <td width="250">{'PrivacyManager_UseAnonymizeIp'|translate}<br/>
+ <span class="form-description">{'PrivacyManager_AnonymizeIpDescription'|translate}</span>
+ </td>
+ <td width='500'>
+ <label><input type="radio" name="anonymizeIPEnable" value="1" {if $anonymizeIP.enabled eq '1'}
+ checked {/if}/> {'General_Yes'|translate}</label>
+ <label><input type="radio" name="anonymizeIPEnable" value="0"
+ style="margin-left:20px;" {if $anonymizeIP.enabled eq '0'} checked {/if}/> {'General_No'|translate}
+ </label>
+ <input type="hidden" name="token_auth" value="{$token_auth}"/>
+ <input type="hidden" name="pluginName" value="{$anonymizeIP.name}"/>
+ </td>
+ <td width="200">
+ {'AnonymizeIP_PluginDescription'|translate|inlineHelp}
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="anonymizeIPenabled">
+ <table class="adminTable" style='width:800px;'>
+ <tr>
+ <td width="250">{'PrivacyManager_AnonymizeIpMaskLengtDescription'|translate}</td>
+ <td width="500">
+ <label><input type="radio" name="maskLength" value="1" {if $anonymizeIP.maskLength eq '1'}
+ checked {/if}/> {'PrivacyManager_AnonymizeIpMaskLength'|translate:"1":"192.168.100.xxx"}
+ </label><br/>
+ <label><input type="radio" name="maskLength" value="2" {if $anonymizeIP.maskLength eq '2'}
+ checked {/if}/> {'PrivacyManager_AnonymizeIpMaskLength'|translate:"2":"192.168.xxx.xxx"} <span
+ class="form-description">{'General_Recommended'|translate}</span></label><br/>
+ <label><input type="radio" name="maskLength" value="3" {if $anonymizeIP.maskLength eq '3'}
+ checked {/if}/> {'PrivacyManager_AnonymizeIpMaskLength'|translate:"3":"192.xxx.xxx.xxx"}</label>
+ </td>
+ <td width="200">
+ {'PrivacyManager_GeolocationAnonymizeIpNote'|translate|inlineHelp}
+ </td>
+ </tr>
+ </table>
+ </div>
+ <input type="submit" value="{'General_Save'|translate}" id="privacySettingsSubmit" class="submit"/>
+ </form>
+ <div class="ui-confirm" id="confirmDeleteSettings">
+ <h2 id="deleteLogsConfirm">{'PrivacyManager_DeleteLogsConfirm'|translate}</h2>
-<h2>{'PrivacyManager_TeaserHeadline'|translate}</h2>
-<p>{'PrivacyManager_Teaser'|translate:'<a href="#anonymizeIPAnchor">':"</a>":'<a href="#deleteLogsAnchor">':"</a>":'<a href="#optOutAnchor">':"</a>"}
-See also our official guide <b><a href='http://piwik.org/privacy/' target='_blank'>Web Analytics Privacy</a></b></p>
+ <h2 id="deleteReportsConfirm">{'PrivacyManager_DeleteReportsConfirm'|translate}</h2>
-<a name="anonymizeIPAnchor"></a>
-<h2>{'PrivacyManager_UseAnonymizeIp'|translate}</h2>
-<form method="post" action="{url action=saveSettings form=formMaskLength token_auth=$token_auth}" id="formMaskLength" name="formMaskLength">
- <div id='anonymizeIpSettings'>
- <table class="adminTable" style='width:800px;'>
- <tr>
- <td width="250">{'PrivacyManager_UseAnonymizeIp'|translate}<br/>
- <span class="form-description">{'PrivacyManager_AnonymizeIpDescription'|translate}</span>
- </td>
- <td width='500'>
- <label><input type="radio" name="anonymizeIPEnable" value="1" {if $anonymizeIP.enabled eq '1'}
- checked {/if}/> {'General_Yes'|translate}</label>
- <label><input type="radio" name="anonymizeIPEnable" value="0"
- style="margin-left:20px;" {if $anonymizeIP.enabled eq '0'} checked {/if}/> {'General_No'|translate}
- </label>
- <input type="hidden" name="token_auth" value="{$token_auth}"/>
- <input type="hidden" name="pluginName" value="{$anonymizeIP.name}"/>
- </td>
- <td width="200">
- {'AnonymizeIP_PluginDescription'|translate|inlineHelp}
- </td>
- </tr>
- </table>
- </div>
- <div id="anonymizeIPenabled">
- <table class="adminTable" style='width:800px;'>
- <tr>
- <td width="250">{'PrivacyManager_AnonymizeIpMaskLengtDescription'|translate}</td>
- <td width="500">
- <label><input type="radio" name="maskLength" value="1" {if $anonymizeIP.maskLength eq '1'}
- checked {/if}/> {'PrivacyManager_AnonymizeIpMaskLength'|translate:"1":"192.168.100.xxx"}
- </label><br/>
- <label><input type="radio" name="maskLength" value="2" {if $anonymizeIP.maskLength eq '2'}
- checked {/if}/> {'PrivacyManager_AnonymizeIpMaskLength'|translate:"2":"192.168.xxx.xxx"} <span class="form-description">{'General_Recommended'|translate}</span></label><br/>
- <label><input type="radio" name="maskLength" value="3" {if $anonymizeIP.maskLength eq '3'}
- checked {/if}/> {'PrivacyManager_AnonymizeIpMaskLength'|translate:"3":"192.xxx.xxx.xxx"}</label>
- </td>
- <td width="200">
- {'PrivacyManager_GeolocationAnonymizeIpNote'|translate|inlineHelp}
- </td>
- </tr>
- </table>
- </div>
- <input type="submit" value="{'General_Save'|translate}" id="privacySettingsSubmit" class="submit"/>
-</form>
+ <h2 id="deleteBothConfirm">{'PrivacyManager_DeleteBothConfirm'|translate}</h2>
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_No'|translate}"/>
+ </div>
+ <div class="ui-confirm" id="saveSettingsBeforePurge">
+ <h2>{'PrivacyManager_SaveSettingsBeforePurge'|translate}</h2>
+ <input role="yes" type="button" value="{'General_Ok'|translate}"/>
+ </div>
+ <div class="ui-confirm" id="confirmPurgeNow">
+ <h2>{'PrivacyManager_PurgeNowConfirm'|translate}</h2>
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_No'|translate}"/>
+ </div>
+ <a name="deleteLogsAnchor"></a>
+ <h2>{'PrivacyManager_DeleteDataSettings'|translate}</h2>
+ <p>{'PrivacyManager_DeleteDataDescription'|translate} {'PrivacyManager_DeleteDataDescription2'|translate}</p>
+ <form method="post" action="{url action=saveSettings form=formDeleteSettings token_auth=$token_auth}" id="formDeleteSettings" name="formMaskLength">
+ <table class="adminTable" style='width:800px;'>
+ <tr id='deleteLogSettingEnabled'>
+ <td width="250">{'PrivacyManager_UseDeleteLog'|translate}<br/>
-<div class="ui-confirm" id="confirmDeleteSettings">
- <h2 id="deleteLogsConfirm">{'PrivacyManager_DeleteLogsConfirm'|translate}</h2>
- <h2 id="deleteReportsConfirm">{'PrivacyManager_DeleteReportsConfirm'|translate}</h2>
- <h2 id="deleteBothConfirm">{'PrivacyManager_DeleteBothConfirm'|translate}</h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_No'|translate}" />
-</div>
-
-<div class="ui-confirm" id="saveSettingsBeforePurge">
- <h2>{'PrivacyManager_SaveSettingsBeforePurge'|translate}</h2>
- <input role="yes" type="button" value="{'General_Ok'|translate}"/>
-</div>
-
-<div class="ui-confirm" id="confirmPurgeNow">
- <h2>{'PrivacyManager_PurgeNowConfirm'|translate}</h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_No'|translate}" />
-</div>
-
-<a name="deleteLogsAnchor"></a>
-<h2>{'PrivacyManager_DeleteDataSettings'|translate}</h2>
-<p>{'PrivacyManager_DeleteDataDescription'|translate} {'PrivacyManager_DeleteDataDescription2'|translate}</p>
-<form method="post" action="{url action=saveSettings form=formDeleteSettings token_auth=$token_auth}" id="formDeleteSettings" name="formMaskLength">
- <table class="adminTable" style='width:800px;'>
- <tr id='deleteLogSettingEnabled'>
- <td width="250">{'PrivacyManager_UseDeleteLog'|translate}<br/>
-
- </td>
- <td width='500'>
- <label><input type="radio" name="deleteEnable" value="1" {if $deleteData.config.delete_logs_enable eq '1'}
- checked {/if}/> {'General_Yes'|translate}</label>
- <label><input type="radio" name="deleteEnable" value="0"
- style="margin-left:20px;" {if $deleteData.config.delete_logs_enable eq '0'}
- checked {/if}/> {'General_No'|translate}
- </label>
+ </td>
+ <td width='500'>
+ <label><input type="radio" name="deleteEnable" value="1" {if $deleteData.config.delete_logs_enable eq '1'}
+ checked {/if}/> {'General_Yes'|translate}</label>
+ <label><input type="radio" name="deleteEnable" value="0"
+ style="margin-left:20px;" {if $deleteData.config.delete_logs_enable eq '0'}
+ checked {/if}/> {'General_No'|translate}
+ </label>
<span class="ajaxSuccess">
{'PrivacyManager_DeleteLogDescription2'|translate}
- <a href="http://piwik.org/faq/general/#faq_125" target="_blank">
- {'General_ClickHere'|translate}
- </a>
+ <a href="http://piwik.org/faq/general/#faq_125" target="_blank">
+ {'General_ClickHere'|translate}
+ </a>
</span>
- </td>
- <td width="200">
- {capture assign=deleteLogInfo}
- {'PrivacyManager_DeleteLogInfo'|translate:$deleteData.deleteTables}
- {if !$canDeleteLogActions}
- <br/><br/>{'PrivacyManager_CannotLockSoDeleteLogActions'|translate:$dbUser}
- {/if}
- {/capture}
- {$deleteLogInfo|inlineHelp}
- </td>
- </tr>
- <tr id="deleteLogSettings">
- <td width="250">&nbsp;</td>
- <td width="500">
- <label>{'PrivacyManager_DeleteLogsOlderThan'|translate}
- <input type="text" id="deleteOlderThan" value="{$deleteData.config.delete_logs_older_than}" style="width:30px;"
- name="deleteOlderThan"/>
- {'CoreHome_PeriodDays'|translate}</label><br/>
- <span class="form-description">{'PrivacyManager_LeastDaysInput'|translate:"1"}</span>
- </td>
- <td width="200">
+ </td>
+ <td width="200">
+ {capture assign=deleteLogInfo}
+ {'PrivacyManager_DeleteLogInfo'|translate:$deleteData.deleteTables}
+ {if !$canDeleteLogActions}
+ <br/>
+ <br/>
+ {'PrivacyManager_CannotLockSoDeleteLogActions'|translate:$dbUser}
+ {/if}
+ {/capture}
+ {$deleteLogInfo|inlineHelp}
+ </td>
+ </tr>
+ <tr id="deleteLogSettings">
+ <td width="250">&nbsp;</td>
+ <td width="500">
+ <label>{'PrivacyManager_DeleteLogsOlderThan'|translate}
+ <input type="text" id="deleteOlderThan" value="{$deleteData.config.delete_logs_older_than}" style="width:30px;"
+ name="deleteOlderThan"/>
+ {'CoreHome_PeriodDays'|translate}</label><br/>
+ <span class="form-description">{'PrivacyManager_LeastDaysInput'|translate:"1"}</span>
+ </td>
+ <td width="200">
- </td>
- </tr>
- <tr id='deleteReportsSettingEnabled'>
- <td width="250">{'PrivacyManager_UseDeleteReports'|translate}
- </td>
- <td width="500">
- <label><input type="radio" name="deleteReportsEnable" value="1" {if $deleteData.config.delete_reports_enable eq '1'}checked="true"{/if}/> {'General_Yes'|translate}</label>
- <label><input type="radio" name="deleteReportsEnable" value="0" {if $deleteData.config.delete_reports_enable eq '0'}checked="true"{/if} style="margin-left:20px;"/> {'General_No'|translate}
- </label>
+ </td>
+ </tr>
+ <tr id='deleteReportsSettingEnabled'>
+ <td width="250">{'PrivacyManager_UseDeleteReports'|translate}
+ </td>
+ <td width="500">
+ <label><input type="radio" name="deleteReportsEnable" value="1"
+ {if $deleteData.config.delete_reports_enable eq '1'}checked="true"{/if}/> {'General_Yes'|translate}</label>
+ <label><input type="radio" name="deleteReportsEnable" value="0" {if $deleteData.config.delete_reports_enable eq '0'}checked="true"{/if}
+ style="margin-left:20px;"/> {'General_No'|translate}
+ </label>
<span class="ajaxSuccess">
{capture assign=deleteOldLogs}{'PrivacyManager_UseDeleteLog'|translate}{/capture}
- {'PrivacyManager_DeleteReportsInfo'|translate:'<em>':'</em>'}
- <span id='deleteOldReportsMoreInfo'><br/><br/>
- {'PrivacyManager_DeleteReportsInfo2'|translate:$deleteOldLogs}<br/><br/>
- {'PrivacyManager_DeleteReportsInfo3'|translate:$deleteOldLogs}</span>
+ {'PrivacyManager_DeleteReportsInfo'|translate:'<em>':'</em>'}
+ <span id='deleteOldReportsMoreInfo'><br/><br/>
+ {'PrivacyManager_DeleteReportsInfo2'|translate:$deleteOldLogs}<br/><br/>
+ {'PrivacyManager_DeleteReportsInfo3'|translate:$deleteOldLogs}</span>
</span>
- </td>
- <td width="200">
- {'PrivacyManager_DeleteReportsDetailedInfo'|translate:'archive_numeric_*':'archive_blob_*'|inlineHelp}
- </td>
- </tr>
- <tr id='deleteReportsSettings'>
- <td width="250">&nbsp;</td>
- <td width="500">
- <label>{'PrivacyManager_DeleteReportsOlderThan'|translate}
- <input type="text" id="deleteReportsOlderThan" value="{$deleteData.config.delete_reports_older_than}" style="width:30px;"
- name="deleteReportsOlderThan"/>
- {'CoreHome_PeriodMonths'|translate}
- </label><br/>
- <span class="form-description">{'PrivacyManager_LeastMonthsInput'|translate:"3"}</span><br/><br/>
- <label><input type="checkbox" name="deleteReportsKeepBasic" value="1" {if $deleteData.config.delete_reports_keep_basic_metrics}checked="true"{/if}>{'PrivacyManager_KeepBasicMetrics'|translate}<span class="form-description">{'General_Recommended'|translate}</span></input>
- </label><br/><br/>
- {'PrivacyManager_KeepDataFor'|translate}<br/>
- <label><input type="checkbox" name="deleteReportsKeepDay" value="1" {if $deleteData.config.delete_reports_keep_day_reports}checked="true"{/if}>{'General_DailyReports'|translate}</input></label><br/>
- <label><input type="checkbox" name="deleteReportsKeepWeek" value="1" {if $deleteData.config.delete_reports_keep_week_reports}checked="true"{/if}>{'General_WeeklyReports'|translate}</input></label><br/>
- <label><input type="checkbox" name="deleteReportsKeepMonth" value="1" {if $deleteData.config.delete_reports_keep_month_reports}checked="true"{/if}>{'General_MonthlyReports'|translate}<span class="form-description">{'General_Recommended'|translate}</span></input></label><br/>
- <label><input type="checkbox" name="deleteReportsKeepYear" value="1" {if $deleteData.config.delete_reports_keep_year_reports}checked="true"{/if}>{'General_YearlyReports'|translate}<span class="form-description">{'General_Recommended'|translate}</span></input></label><br/>
- <label><input type="checkbox" name="deleteReportsKeepRange" value="1" {if $deleteData.config.delete_reports_keep_range_reports}checked="true"{/if}>{'General_RangeReports'|translate}</input></label><br/><br/>
- <label><input type="checkbox" name="deleteReportsKeepSegments" value="1" {if $deleteData.config.delete_reports_keep_segment_reports}checked="true"{/if}>{'PrivacyManager_KeepReportSegments'|translate}</input></label><br/>
- </td>
- <td width="200">
-
- </td>
- </tr>
- <tr id="deleteDataEstimateSect" {if $deleteData.config.delete_reports_enable eq '0' and $deleteData.config.delete_logs_enable eq '0'}style="display:none;"{/if}>
- <td width="250">{'PrivacyManager_ReportsDataSavedEstimate'|translate}<br/></td>
- <td width="500">
- <div id="deleteDataEstimate"></div>
- <span class='loadingPiwik' style='display:none'><img src='./themes/default/images/loading-blue.gif' /> {'General_LoadingData'|translate}</span>
- </td>
- <td width="200">
- {if $deleteData.config.enable_auto_database_size_estimate eq '0'}
- {capture assign=manualEstimate}
- <em><a id="getPurgeEstimateLink" class="ui-inline-help" href="#">{'PrivacyManager_GetPurgeEstimate'|translate}</a></em>
- {/capture}
- {$manualEstimate|inlineHelp}
- {/if}
- </td>
- </tr>
- <tr id="deleteSchedulingSettings">
- <td width="250">{'PrivacyManager_DeleteSchedulingSettings'|translate}<br/></td>
- <td width="500">
- <label>{'PrivacyManager_DeleteDataInterval'|translate}
- <select id="deleteLowestInterval" name="deleteLowestInterval">
- <option {if $deleteData.config.delete_logs_schedule_lowest_interval eq '1'} selected="selected" {/if}
- value="1"> {'CoreHome_PeriodDay'|translate}</option>
- <option {if $deleteData.config.delete_logs_schedule_lowest_interval eq '7'} selected="selected" {/if}
- value="7">{'CoreHome_PeriodWeek'|translate}</option>
- <option {if $deleteData.config.delete_logs_schedule_lowest_interval eq '30'} selected="selected" {/if}
- value="30">{'CoreHome_PeriodMonth'|translate}</option>
- </select></label><br/><br/>
- </td>
- <td width="200">
- {capture assign=purgeStats}
- {if $deleteData.lastRun}<strong>{'PrivacyManager_LastDelete'|translate}:</strong>
- {$deleteData.lastRunPretty}
- <br/><br/>{/if}
- <strong>{'PrivacyManager_NextDelete'|translate}:</strong>
- {$deleteData.nextRunPretty}
- <br/><br/><em><a id="purgeDataNowLink" href="#">{'PrivacyManager_PurgeNow'|translate}</a></em>
- <span class='loadingPiwik' style='display:none'><img src='./themes/default/images/loading-blue.gif' /> {'PrivacyManager_PurgingData'|translate}</span>
- <span id="db-purged-message" style="display: none;"><em>{'PrivacyManager_DBPurged'|translate}</em></span>
- {/capture}
- {$purgeStats|inlineHelp}
- </td>
- </tr>
- </table>
- <input type="button" value="{'General_Save'|translate}" id="deleteLogSettingsSubmit" class="submit"/>
-</form>
+ </td>
+ <td width="200">
+ {'PrivacyManager_DeleteReportsDetailedInfo'|translate:'archive_numeric_*':'archive_blob_*'|inlineHelp}
+ </td>
+ </tr>
+ <tr id='deleteReportsSettings'>
+ <td width="250">&nbsp;</td>
+ <td width="500">
+ <label>{'PrivacyManager_DeleteReportsOlderThan'|translate}
+ <input type="text" id="deleteReportsOlderThan" value="{$deleteData.config.delete_reports_older_than}" style="width:30px;"
+ name="deleteReportsOlderThan"/>
+ {'CoreHome_PeriodMonths'|translate}
+ </label><br/>
+ <span class="form-description">{'PrivacyManager_LeastMonthsInput'|translate:"3"}</span><br/><br/>
+ <label><input type="checkbox" name="deleteReportsKeepBasic" value="1"
+ {if $deleteData.config.delete_reports_keep_basic_metrics}checked="true"{/if}>{'PrivacyManager_KeepBasicMetrics'|translate}
+ <span class="form-description">{'General_Recommended'|translate}</span></input>
+ </label><br/><br/>
+ {'PrivacyManager_KeepDataFor'|translate}<br/>
+ <label><input type="checkbox" name="deleteReportsKeepDay" value="1"
+ {if $deleteData.config.delete_reports_keep_day_reports}checked="true"{/if}>{'General_DailyReports'|translate}</input></label><br/>
+ <label><input type="checkbox" name="deleteReportsKeepWeek" value="1"
+ {if $deleteData.config.delete_reports_keep_week_reports}checked="true"{/if}>{'General_WeeklyReports'|translate}</input></label><br/>
+ <label><input type="checkbox" name="deleteReportsKeepMonth" value="1"
+ {if $deleteData.config.delete_reports_keep_month_reports}checked="true"{/if}>{'General_MonthlyReports'|translate}<span
+ class="form-description">{'General_Recommended'|translate}</span></input></label><br/>
+ <label><input type="checkbox" name="deleteReportsKeepYear" value="1"
+ {if $deleteData.config.delete_reports_keep_year_reports}checked="true"{/if}>{'General_YearlyReports'|translate}<span
+ class="form-description">{'General_Recommended'|translate}</span></input></label><br/>
+ <label><input type="checkbox" name="deleteReportsKeepRange" value="1"
+ {if $deleteData.config.delete_reports_keep_range_reports}checked="true"{/if}>{'General_RangeReports'|translate}</input></label><br/><br/>
+ <label><input type="checkbox" name="deleteReportsKeepSegments" value="1"
+ {if $deleteData.config.delete_reports_keep_segment_reports}checked="true"{/if}>{'PrivacyManager_KeepReportSegments'|translate}</input></label><br/>
+ </td>
+ <td width="200">
-
-
-<a name="DNT"></a>
-<h2>{'PrivacyManager_DoNotTrack_SupportDNTPreference'|translate}</h2>
-
-<table class="adminTable" style='width:800px;'>
- <tr>
- <td width="650">
- <p>{if $dntSupport}
- {assign var=action value=deactivate}
- <b>{'PrivacyManager_DoNotTrack_Enabled'|translate}</b> <br/>{'PrivacyManager_DoNotTrack_EnabledMoreInfo'|translate}
- {else}
- {assign var=action value=activate}
- {'PrivacyManager_DoNotTrack_Disabled'|translate} {'PrivacyManager_DoNotTrack_DisabledMoreInfo'|translate}
- {/if}</p>
+ </td>
+ </tr>
+ <tr id="deleteDataEstimateSect"
+ {if $deleteData.config.delete_reports_enable eq '0' and $deleteData.config.delete_logs_enable eq '0'}style="display:none;"{/if}>
+ <td width="250">{'PrivacyManager_ReportsDataSavedEstimate'|translate}<br/></td>
+ <td width="500">
+ <div id="deleteDataEstimate"></div>
+ <span class='loadingPiwik' style='display:none'><img
+ src='./themes/default/images/loading-blue.gif'/> {'General_LoadingData'|translate}</span>
+ </td>
+ <td width="200">
+ {if $deleteData.config.enable_auto_database_size_estimate eq '0'}
+ {capture assign=manualEstimate}
+ <em><a id="getPurgeEstimateLink" class="ui-inline-help" href="#">{'PrivacyManager_GetPurgeEstimate'|translate}</a></em>
+ {/capture}
+ {$manualEstimate|inlineHelp}
+ {/if}
+ </td>
+ </tr>
+ <tr id="deleteSchedulingSettings">
+ <td width="250">{'PrivacyManager_DeleteSchedulingSettings'|translate}<br/></td>
+ <td width="500">
+ <label>{'PrivacyManager_DeleteDataInterval'|translate}
+ <select id="deleteLowestInterval" name="deleteLowestInterval">
+ <option {if $deleteData.config.delete_logs_schedule_lowest_interval eq '1'} selected="selected" {/if}
+ value="1"> {'CoreHome_PeriodDay'|translate}</option>
+ <option {if $deleteData.config.delete_logs_schedule_lowest_interval eq '7'} selected="selected" {/if}
+ value="7">{'CoreHome_PeriodWeek'|translate}</option>
+ <option {if $deleteData.config.delete_logs_schedule_lowest_interval eq '30'} selected="selected" {/if}
+ value="30">{'CoreHome_PeriodMonth'|translate}</option>
+ </select></label><br/><br/>
+ </td>
+ <td width="200">
+ {capture assign=purgeStats}
+ {if $deleteData.lastRun}<strong>{'PrivacyManager_LastDelete'|translate}:</strong>
+ {$deleteData.lastRunPretty}
+ <br/>
+ <br/>
+ {/if}
+ <strong>{'PrivacyManager_NextDelete'|translate}:</strong>
+ {$deleteData.nextRunPretty}
+ <br/>
+ <br/>
+ <em><a id="purgeDataNowLink" href="#">{'PrivacyManager_PurgeNow'|translate}</a></em>
+ <span class='loadingPiwik' style='display:none'><img
+ src='./themes/default/images/loading-blue.gif'/> {'PrivacyManager_PurgingData'|translate}</span>
+ <span id="db-purged-message" style="display: none;"><em>{'PrivacyManager_DBPurged'|translate}</em></span>
+ {/capture}
+ {$purgeStats|inlineHelp}
+ </td>
+ </tr>
+ </table>
+ <input type="button" value="{'General_Save'|translate}" id="deleteLogSettingsSubmit" class="submit"/>
+ </form>
+ <a name="DNT"></a>
+ <h2>{'PrivacyManager_DoNotTrack_SupportDNTPreference'|translate}</h2>
+ <table class="adminTable" style='width:800px;'>
+ <tr>
+ <td width="650">
+ <p>{if $dntSupport}
+ {assign var=action value=deactivate}
+ <b>{'PrivacyManager_DoNotTrack_Enabled'|translate}</b>
+ <br/>
+ {'PrivacyManager_DoNotTrack_EnabledMoreInfo'|translate}
+ {else}
+ {assign var=action value=activate}
+ {'PrivacyManager_DoNotTrack_Disabled'|translate} {'PrivacyManager_DoNotTrack_DisabledMoreInfo'|translate}
+ {/if}</p>
<span style='margin-left:20px'>
- <a href='{url module=CorePluginsAdmin token_auth=$token_auth action=$action pluginName=DoNotTrack}#DNT'>&rsaquo;
- {if $dntSupport}{'PrivacyManager_DoNotTrack_Disable'|translate} {'General_NotRecommended'|translate}
- {else}{'PrivacyManager_DoNotTrack_Enable'|translate} {'General_Recommended'|translate}{/if}
- <br />
- </a></span>
- </td>
- <td width="200">
- {'PrivacyManager_DoNotTrack_Description'|translate|inlineHelp}
- </td>
- </tr>
-</table>
-
+ <a href='{url module=CorePluginsAdmin token_auth=$token_auth action=$action pluginName=DoNotTrack}#DNT'>&rsaquo;
+ {if $dntSupport}{'PrivacyManager_DoNotTrack_Disable'|translate} {'General_NotRecommended'|translate}
+ {else}{'PrivacyManager_DoNotTrack_Enable'|translate} {'General_Recommended'|translate}{/if}
+ <br/>
+ </a></span>
+ </td>
+ <td width="200">
+ {'PrivacyManager_DoNotTrack_Description'|translate|inlineHelp}
+ </td>
+ </tr>
+ </table>
{/if}
<a name="optOutAnchor"></a>
<h2>{'CoreAdminHome_OptOutForYourVisitors'|translate}</h2>
<p>{'CoreAdminHome_OptOutExplanation'|translate}
-{capture name=optOutUrl}{$piwikUrl}index.php?module=CoreAdminHome&action=optOut&language={$language}{/capture}
-{assign var=optOutUrl value=$smarty.capture.optOutUrl}
-{capture name=iframeOptOut}
-<iframe frameborder="no" width="600px" height="200px" src="{$smarty.capture.optOutUrl}"></iframe>{/capture}
-<code>{$smarty.capture.iframeOptOut|escape:'html'}</code>
-<br/>
-{'CoreAdminHome_OptOutExplanationBis'|translate:"<a href='$optOutUrl' target='_blank'>":"</a>"}
+ {capture name=optOutUrl}{$piwikUrl}index.php?module=CoreAdminHome&action=optOut&language={$language}{/capture}
+ {assign var=optOutUrl value=$smarty.capture.optOutUrl}
+ {capture name=iframeOptOut}
+ <iframe frameborder="no" width="600px" height="200px" src="{$smarty.capture.optOutUrl}"></iframe>{/capture}
+ <code>{$smarty.capture.iframeOptOut|escape:'html'}</code>
+ <br/>
+ {'CoreAdminHome_OptOutExplanationBis'|translate:"<a href='$optOutUrl' target='_blank'>":"</a>"}
</p>
<div style='height:100px'></div>
diff --git a/plugins/Provider/API.php b/plugins/Provider/API.php
index d2fe4b3bee..8ce5a43703 100644
--- a/plugins/Provider/API.php
+++ b/plugins/Provider/API.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Provider
*/
@@ -16,33 +16,32 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/Provider/functions.php';
/**
* The Provider API lets you access reports for your visitors Internet Providers.
- *
+ *
* @package Piwik_Provider
*/
-class Piwik_Provider_API
+class Piwik_Provider_API
{
- static private $instance = null;
-
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
+ static private $instance = null;
- public function getProvider( $idSite, $period, $date, $segment = false )
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- $dataTable = $archive->getDataTable('Provider_hostnameExt');
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
- $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_getHostnameUrl'));
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getHostnameName'));
- $dataTable->queueFilter('ReplaceColumnNames');
- $dataTable->queueFilter('ReplaceSummaryRowLabel');
- return $dataTable;
- }
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ public function getProvider($idSite, $period, $date, $segment = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ $dataTable = $archive->getDataTable('Provider_hostnameExt');
+ $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
+ $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_getHostnameUrl'));
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getHostnameName'));
+ $dataTable->queueFilter('ReplaceColumnNames');
+ $dataTable->queueFilter('ReplaceSummaryRowLabel');
+ return $dataTable;
+ }
}
diff --git a/plugins/Provider/Controller.php b/plugins/Provider/Controller.php
index 76ae424841..f59b8d1b29 100644
--- a/plugins/Provider/Controller.php
+++ b/plugins/Provider/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Provider
*/
@@ -13,31 +13,30 @@
*
* @package Piwik_Provider
*/
-class Piwik_Provider_Controller extends Piwik_Controller
+class Piwik_Provider_Controller extends Piwik_Controller
{
- /**
- * Provider
- * @param bool $fetch
- * @return string|void
- */
- function getProvider($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__, "Provider.getProvider" );
-
- $this->setPeriodVariablesView($view);
- $column = 'nb_visits';
- if($view->period == 'day')
- {
- $column = 'nb_uniq_visitors';
- }
- $view->setColumnsToDisplay( array('label',$column) );
- $view->setColumnTranslation('label', Piwik_Translate('Provider_ColumnProvider'));
- $view->setSortedColumn( $column );
- $view->setLimit( 5 );
- $this->setMetricsVariablesView($view);
- return $this->renderView($view, $fetch);
- }
-
+ /**
+ * Provider
+ * @param bool $fetch
+ * @return string|void
+ */
+ function getProvider($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, "Provider.getProvider");
+
+ $this->setPeriodVariablesView($view);
+ $column = 'nb_visits';
+ if ($view->period == 'day') {
+ $column = 'nb_uniq_visitors';
+ }
+ $view->setColumnsToDisplay(array('label', $column));
+ $view->setColumnTranslation('label', Piwik_Translate('Provider_ColumnProvider'));
+ $view->setSortedColumn($column);
+ $view->setLimit(5);
+ $this->setMetricsVariablesView($view);
+ return $this->renderView($view, $fetch);
+ }
+
}
diff --git a/plugins/Provider/Provider.php b/plugins/Provider/Provider.php
index 8dba1153ae..64d18ace74 100644
--- a/plugins/Provider/Provider.php
+++ b/plugins/Provider/Provider.php
@@ -15,252 +15,239 @@
*/
class Piwik_Provider extends Piwik_Plugin
{
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('Provider_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- 'TrackerPlugin' => true, // this plugin must be loaded during the stats logging
- );
-
- return $info;
- }
-
- public function getListHooksRegistered()
- {
- $hooks = array(
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'Tracker.newVisitorInformation' => 'logProviderInfo',
- 'WidgetsList.add' => 'addWidget',
- 'Menu.add' => 'addMenu',
- 'API.getReportMetadata' => 'getReportMetadata',
- 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
- );
- return $hooks;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $reports = &$notification->getNotificationObject();
- $reports[] = array(
- 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('Provider_ColumnProvider'),
- 'module' => 'Provider',
- 'action' => 'getProvider',
- 'dimension' => Piwik_Translate('Provider_ColumnProvider'),
- 'documentation' => Piwik_Translate('Provider_ProviderReportDocumentation', '<br />'),
- 'order' => 50
- );
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getSegmentsMetadata($notification)
- {
- $segments =& $notification->getNotificationObject();
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit Location',
- 'name' => Piwik_Translate('Provider_ColumnProvider'),
- 'segment' => 'provider',
- 'acceptedValues' => 'comcast.net, proxad.net, etc.',
- 'sqlSegment' => 'log_visit.location_provider'
- );
- }
-
- function install()
- {
- // add column hostname / hostname ext in the visit table
- $query = "ALTER IGNORE TABLE `".Piwik_Common::prefixTable('log_visit')."` ADD `location_provider` VARCHAR( 100 ) NULL";
-
- // if the column already exist do not throw error. Could be installed twice...
- try {
- Piwik_Exec($query);
- }
- catch(Exception $e) {
- if(!Zend_Registry::get('db')->isErrNo($e, '1060'))
- {
- throw $e;
- }
- }
-
- }
-
- function uninstall()
- {
- // add column hostname / hostname ext in the visit table
- $query = "ALTER TABLE `".Piwik_Common::prefixTable('log_visit')."` DROP `location_provider`";
- Piwik_Exec($query);
- }
-
- function addWidget()
- {
- Piwik_AddWidget( 'General_Visitors', 'Provider_WidgetProviders', 'Provider', 'getProvider');
- }
-
- function addMenu()
- {
- Piwik_RenameMenuEntry( 'General_Visitors', 'UserCountry_SubmenuLocations',
- 'General_Visitors', 'Provider_SubmenuLocationsProvider');
- }
-
- function postLoad()
- {
- Piwik_AddAction('template_footerUserCountry', array('Piwik_Provider','footerUserCountry'));
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archivePeriod( $notification )
- {
- $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = array( 'Provider_hostnameExt' );
- $archiveProcessing->archiveDataTable($dataTableToSum, null, $maximumRowsInDataTable);
- }
-
- /**
- * Daily archive: processes the report Visits by Provider
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function archiveDay($notification)
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $recordName = 'Provider_hostnameExt';
- $labelSQL = "log_visit.location_provider";
- $interestByProvider = $archiveProcessing->getArrayInterestForLabel($labelSQL);
- $tableProvider = $archiveProcessing->getDataTableFromArray($interestByProvider);
- $columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
- $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
- $archiveProcessing->insertBlobRecord($recordName, $tableProvider->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
- destroy($tableProvider);
- }
-
- /**
- * Logs the provider in the log_visit table
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function logProviderInfo($notification)
- {
- $visitorInfo =& $notification->getNotificationObject();
-
- // if provider info has already been set, abort
- if (!empty($visitorInfo['location_provider']))
- {
- return;
- }
-
- $ip = Piwik_IP::N2P($visitorInfo['location_ip']);
-
- // In case the IP was anonymized, we should not continue since the DNS reverse lookup will fail and this will slow down tracking
- if(substr($ip, -2, 2) == '.0')
- {
- printDebug("IP Was anonymized so we skip the Provider DNS reverse lookup...");
- return;
- }
-
- $hostname = $this->getHost($ip);
- $hostnameExtension = $this->getCleanHostname($hostname);
-
- // add the provider value in the table log_visit
- $visitorInfo['location_provider'] = $hostnameExtension;
- $visitorInfo['location_provider'] = substr($visitorInfo['location_provider'], 0, 100);
-
- // improve the country using the provider extension if valid
- $hostnameDomain = substr($hostnameExtension, 1 + strrpos($hostnameExtension, '.'));
- if($hostnameDomain == 'uk')
- {
- $hostnameDomain = 'gb';
- }
- if(array_key_exists($hostnameDomain, Piwik_Common::getCountriesList()))
- {
- $visitorInfo['location_country'] = $hostnameDomain;
- }
- }
-
- /**
- * Returns the hostname extension (site.co.jp in fvae.VARG.ceaga.site.co.jp)
- * given the full hostname looked up from the IP
- *
- * @param string $hostname
- *
- * @return string
- */
- private function getCleanHostname($hostname)
- {
- $extToExclude = array(
- 'com', 'net', 'org', 'co'
- );
-
- $off = strrpos($hostname, '.');
- $ext = substr($hostname, $off);
-
- if(empty($off) || is_numeric($ext) || strlen($hostname) < 5)
- {
- return 'Ip';
- }
- else
- {
- $cleanHostname = null;
- Piwik_PostEvent('Provider.getCleanHostname', $cleanHostname, $hostname);
- if($cleanHostname !== null)
- {
- return $cleanHostname;
- }
-
- $e = explode('.', $hostname);
- $s = sizeof($e);
-
- // if extension not correct
- if(isset($e[$s-2]) && in_array($e[$s-2], $extToExclude))
- {
- return $e[$s-3].".".$e[$s-2].".".$e[$s-1];
- }
- else
- {
- return $e[$s-2].".".$e[$s-1];
- }
- }
- }
-
- /**
- * Returns the hostname given the IP address string
- *
- * @param string $ip IP Address
- * @return string hostname (or human-readable IP address)
- */
- private function getHost($ip)
- {
- return trim(strtolower(@Piwik_IP::getHostByAddr($ip)));
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- static public function footerUserCountry($notification)
- {
- $out =& $notification->getNotificationObject();
- $out = '<div>
- <h2>'.Piwik_Translate('Provider_WidgetProviders').'</h2>';
- $out .= Piwik_FrontController::getInstance()->fetchDispatch('Provider','getProvider');
- $out .= '</div>';
- }
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('Provider_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ 'TrackerPlugin' => true, // this plugin must be loaded during the stats logging
+ );
+
+ return $info;
+ }
+
+ public function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'Tracker.newVisitorInformation' => 'logProviderInfo',
+ 'WidgetsList.add' => 'addWidget',
+ 'Menu.add' => 'addMenu',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ );
+ return $hooks;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $reports = & $notification->getNotificationObject();
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('Provider_ColumnProvider'),
+ 'module' => 'Provider',
+ 'action' => 'getProvider',
+ 'dimension' => Piwik_Translate('Provider_ColumnProvider'),
+ 'documentation' => Piwik_Translate('Provider_ProviderReportDocumentation', '<br />'),
+ 'order' => 50
+ );
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ $segments =& $notification->getNotificationObject();
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit Location',
+ 'name' => Piwik_Translate('Provider_ColumnProvider'),
+ 'segment' => 'provider',
+ 'acceptedValues' => 'comcast.net, proxad.net, etc.',
+ 'sqlSegment' => 'log_visit.location_provider'
+ );
+ }
+
+ function install()
+ {
+ // add column hostname / hostname ext in the visit table
+ $query = "ALTER IGNORE TABLE `" . Piwik_Common::prefixTable('log_visit') . "` ADD `location_provider` VARCHAR( 100 ) NULL";
+
+ // if the column already exist do not throw error. Could be installed twice...
+ try {
+ Piwik_Exec($query);
+ } catch (Exception $e) {
+ if (!Zend_Registry::get('db')->isErrNo($e, '1060')) {
+ throw $e;
+ }
+ }
+
+ }
+
+ function uninstall()
+ {
+ // add column hostname / hostname ext in the visit table
+ $query = "ALTER TABLE `" . Piwik_Common::prefixTable('log_visit') . "` DROP `location_provider`";
+ Piwik_Exec($query);
+ }
+
+ function addWidget()
+ {
+ Piwik_AddWidget('General_Visitors', 'Provider_WidgetProviders', 'Provider', 'getProvider');
+ }
+
+ function addMenu()
+ {
+ Piwik_RenameMenuEntry('General_Visitors', 'UserCountry_SubmenuLocations',
+ 'General_Visitors', 'Provider_SubmenuLocationsProvider');
+ }
+
+ function postLoad()
+ {
+ Piwik_AddAction('template_footerUserCountry', array('Piwik_Provider', 'footerUserCountry'));
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ function archivePeriod($notification)
+ {
+ $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $dataTableToSum = array('Provider_hostnameExt');
+ $archiveProcessing->archiveDataTable($dataTableToSum, null, $maximumRowsInDataTable);
+ }
+
+ /**
+ * Daily archive: processes the report Visits by Provider
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function archiveDay($notification)
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $recordName = 'Provider_hostnameExt';
+ $labelSQL = "log_visit.location_provider";
+ $interestByProvider = $archiveProcessing->getArrayInterestForLabel($labelSQL);
+ $tableProvider = $archiveProcessing->getDataTableFromArray($interestByProvider);
+ $columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
+ $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
+ $archiveProcessing->insertBlobRecord($recordName, $tableProvider->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
+ destroy($tableProvider);
+ }
+
+ /**
+ * Logs the provider in the log_visit table
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function logProviderInfo($notification)
+ {
+ $visitorInfo =& $notification->getNotificationObject();
+
+ // if provider info has already been set, abort
+ if (!empty($visitorInfo['location_provider'])) {
+ return;
+ }
+
+ $ip = Piwik_IP::N2P($visitorInfo['location_ip']);
+
+ // In case the IP was anonymized, we should not continue since the DNS reverse lookup will fail and this will slow down tracking
+ if (substr($ip, -2, 2) == '.0') {
+ printDebug("IP Was anonymized so we skip the Provider DNS reverse lookup...");
+ return;
+ }
+
+ $hostname = $this->getHost($ip);
+ $hostnameExtension = $this->getCleanHostname($hostname);
+
+ // add the provider value in the table log_visit
+ $visitorInfo['location_provider'] = $hostnameExtension;
+ $visitorInfo['location_provider'] = substr($visitorInfo['location_provider'], 0, 100);
+
+ // improve the country using the provider extension if valid
+ $hostnameDomain = substr($hostnameExtension, 1 + strrpos($hostnameExtension, '.'));
+ if ($hostnameDomain == 'uk') {
+ $hostnameDomain = 'gb';
+ }
+ if (array_key_exists($hostnameDomain, Piwik_Common::getCountriesList())) {
+ $visitorInfo['location_country'] = $hostnameDomain;
+ }
+ }
+
+ /**
+ * Returns the hostname extension (site.co.jp in fvae.VARG.ceaga.site.co.jp)
+ * given the full hostname looked up from the IP
+ *
+ * @param string $hostname
+ *
+ * @return string
+ */
+ private function getCleanHostname($hostname)
+ {
+ $extToExclude = array(
+ 'com', 'net', 'org', 'co'
+ );
+
+ $off = strrpos($hostname, '.');
+ $ext = substr($hostname, $off);
+
+ if (empty($off) || is_numeric($ext) || strlen($hostname) < 5) {
+ return 'Ip';
+ } else {
+ $cleanHostname = null;
+ Piwik_PostEvent('Provider.getCleanHostname', $cleanHostname, $hostname);
+ if ($cleanHostname !== null) {
+ return $cleanHostname;
+ }
+
+ $e = explode('.', $hostname);
+ $s = sizeof($e);
+
+ // if extension not correct
+ if (isset($e[$s - 2]) && in_array($e[$s - 2], $extToExclude)) {
+ return $e[$s - 3] . "." . $e[$s - 2] . "." . $e[$s - 1];
+ } else {
+ return $e[$s - 2] . "." . $e[$s - 1];
+ }
+ }
+ }
+
+ /**
+ * Returns the hostname given the IP address string
+ *
+ * @param string $ip IP Address
+ * @return string hostname (or human-readable IP address)
+ */
+ private function getHost($ip)
+ {
+ return trim(strtolower(@Piwik_IP::getHostByAddr($ip)));
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ static public function footerUserCountry($notification)
+ {
+ $out =& $notification->getNotificationObject();
+ $out = '<div>
+ <h2>' . Piwik_Translate('Provider_WidgetProviders') . '</h2>';
+ $out .= Piwik_FrontController::getInstance()->fetchDispatch('Provider', 'getProvider');
+ $out .= '</div>';
+ }
}
diff --git a/plugins/Provider/functions.php b/plugins/Provider/functions.php
index ec1afd4518..34f09534c0 100644
--- a/plugins/Provider/functions.php
+++ b/plugins/Provider/functions.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Provider
*/
@@ -17,19 +17,16 @@
*/
function Piwik_getHostnameName($in)
{
- if(empty($in))
- {
- return Piwik_Translate('General_Unknown');
- }
- if(strtolower($in) === 'ip')
- {
- return "IP";
- }
- if(($positionDot = strpos($in, '.')) !== false)
- {
- return ucfirst(substr($in, 0, $positionDot));
- }
- return $in;
+ if (empty($in)) {
+ return Piwik_Translate('General_Unknown');
+ }
+ if (strtolower($in) === 'ip') {
+ return "IP";
+ }
+ if (($positionDot = strpos($in, '.')) !== false) {
+ return ucfirst(substr($in, 0, $positionDot));
+ }
+ return $in;
}
/**
@@ -40,24 +37,20 @@ function Piwik_getHostnameName($in)
*/
function Piwik_getHostnameUrl($in)
{
- if($in == Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- return false;
- }
- if(empty($in)
- || strtolower($in) === 'ip')
- {
- // link to "what does 'IP' mean?"
- return "http://piwik.org/faq/general/#faq_52";
- }
-
- // if the name looks like it can be used in a URL, use it in one, otherwise link to startpage
- if (preg_match("/^[-a-zA-Z0-9_.]+$/", $in))
- {
- return "http://www.".$in."/";
- }
- else
- {
- return "https://startpage.com/do/search?q=".urlencode($in);
- }
+ if ($in == Piwik_DataTable::LABEL_SUMMARY_ROW) {
+ return false;
+ }
+ if (empty($in)
+ || strtolower($in) === 'ip'
+ ) {
+ // link to "what does 'IP' mean?"
+ return "http://piwik.org/faq/general/#faq_52";
+ }
+
+ // if the name looks like it can be used in a URL, use it in one, otherwise link to startpage
+ if (preg_match("/^[-a-zA-Z0-9_.]+$/", $in)) {
+ return "http://www." . $in . "/";
+ } else {
+ return "https://startpage.com/do/search?q=" . urlencode($in);
+ }
}
diff --git a/plugins/Proxy/Controller.php b/plugins/Proxy/Controller.php
index 5681f6db0d..12e8435703 100644
--- a/plugins/Proxy/Controller.php
+++ b/plugins/Proxy/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Proxy
*/
@@ -15,167 +15,156 @@
* @package Piwik_Proxy
*/
class Piwik_Proxy_Controller extends Piwik_Controller
-{
- const TRANSPARENT_PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=';
-
- /**
- * Display the "Export Image" window.
- *
- * @deprecated 1.5.1
- *
- * @param string $imageData Base-64 encoded image data (via $_POST)
- */
- static public function exportImageWindow()
- {
- Piwik::checkUserHasSomeViewAccess();
-
- $view = Piwik_View::factory('exportImage');
- $view->imageData = 'data:image/png;base64,'. Piwik_Common::getRequestVar('imageData', self::TRANSPARENT_PNG_PIXEL, 'string', $_POST);
- echo $view->render();
- }
-
- function exportImage()
- {
- self::exportImageWindow();
- }
-
- /**
- * Output binary image from base-64 encoded data.
- *
- * @deprecated 1.5.1
- *
- * @param string $imageData Base-64 encoded image data (via $_POST)
- */
- static public function outputBinaryImage()
- {
- Piwik::checkUserHasSomeViewAccess();
-
- $rawData = Piwik_Common::getRequestVar('imageData', '', 'string', $_POST);
-
- // returns false if any illegal characters in input
- $data = base64_decode($rawData);
- if($data !== false)
- {
- // check for PNG header
- if(Piwik_Common::substr($data, 0, 8) === "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a")
- {
- header('Content-Type: image/png');
-
- // more robust validation (if available)
- if(function_exists('imagecreatefromstring'))
- {
- // validate image data
- $imgResource = @imagecreatefromstring($data);
- if($imgResource !== false)
- {
- // output image and clean-up
- imagepng($imgResource);
- imagedestroy($imgResource);
- exit;
- }
- }
- else
- {
- echo $data;
- exit;
- }
- }
- }
-
- Piwik::setHttpStatus('400 Bad Request');
- exit;
- }
-
- function outputImage()
- {
- self::outputBinaryImage();
- }
-
- /**
- * Output the merged CSS file.
- * This method is called when the asset manager is enabled.
- *
- * @see core/AssetManager.php
- */
- public function getCss()
- {
- $cssMergedFile = Piwik_AssetManager::getMergedCssFileLocation();
- Piwik::serveStaticFile($cssMergedFile, "text/css");
- }
-
- /**
- * Output the merged JavaScript file.
- * This method is called when the asset manager is enabled.
- *
- * @see core/AssetManager.php
- */
- public function getJs()
- {
- $jsMergedFile = Piwik_AssetManager::getMergedJsFileLocation();
- Piwik::serveStaticFile($jsMergedFile, "application/javascript; charset=UTF-8");
- }
-
- /**
- * Output redirection page instead of linking directly to avoid
- * exposing the referrer on the Piwik demo.
- *
- * @param string $url (via $_GET)
- */
- public function redirect()
- {
- $url = Piwik_Common::getRequestVar('url', '', 'string', $_GET);
-
- // validate referrer
- $referrer = Piwik_Url::getReferer();
- if(empty($referrer) || !Piwik_Url::isLocalUrl($referrer))
- {
- die('Invalid Referer detected - This means that your web browser is not sending the "Referer URL" which is
+{
+ const TRANSPARENT_PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=';
+
+ /**
+ * Display the "Export Image" window.
+ *
+ * @deprecated 1.5.1
+ *
+ * @param string $imageData Base-64 encoded image data (via $_POST)
+ */
+ static public function exportImageWindow()
+ {
+ Piwik::checkUserHasSomeViewAccess();
+
+ $view = Piwik_View::factory('exportImage');
+ $view->imageData = 'data:image/png;base64,' . Piwik_Common::getRequestVar('imageData', self::TRANSPARENT_PNG_PIXEL, 'string', $_POST);
+ echo $view->render();
+ }
+
+ function exportImage()
+ {
+ self::exportImageWindow();
+ }
+
+ /**
+ * Output binary image from base-64 encoded data.
+ *
+ * @deprecated 1.5.1
+ *
+ * @param string $imageData Base-64 encoded image data (via $_POST)
+ */
+ static public function outputBinaryImage()
+ {
+ Piwik::checkUserHasSomeViewAccess();
+
+ $rawData = Piwik_Common::getRequestVar('imageData', '', 'string', $_POST);
+
+ // returns false if any illegal characters in input
+ $data = base64_decode($rawData);
+ if ($data !== false) {
+ // check for PNG header
+ if (Piwik_Common::substr($data, 0, 8) === "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a") {
+ header('Content-Type: image/png');
+
+ // more robust validation (if available)
+ if (function_exists('imagecreatefromstring')) {
+ // validate image data
+ $imgResource = @imagecreatefromstring($data);
+ if ($imgResource !== false) {
+ // output image and clean-up
+ imagepng($imgResource);
+ imagedestroy($imgResource);
+ exit;
+ }
+ } else {
+ echo $data;
+ exit;
+ }
+ }
+ }
+
+ Piwik::setHttpStatus('400 Bad Request');
+ exit;
+ }
+
+ function outputImage()
+ {
+ self::outputBinaryImage();
+ }
+
+ /**
+ * Output the merged CSS file.
+ * This method is called when the asset manager is enabled.
+ *
+ * @see core/AssetManager.php
+ */
+ public function getCss()
+ {
+ $cssMergedFile = Piwik_AssetManager::getMergedCssFileLocation();
+ Piwik::serveStaticFile($cssMergedFile, "text/css");
+ }
+
+ /**
+ * Output the merged JavaScript file.
+ * This method is called when the asset manager is enabled.
+ *
+ * @see core/AssetManager.php
+ */
+ public function getJs()
+ {
+ $jsMergedFile = Piwik_AssetManager::getMergedJsFileLocation();
+ Piwik::serveStaticFile($jsMergedFile, "application/javascript; charset=UTF-8");
+ }
+
+ /**
+ * Output redirection page instead of linking directly to avoid
+ * exposing the referrer on the Piwik demo.
+ *
+ * @param string $url (via $_GET)
+ */
+ public function redirect()
+ {
+ $url = Piwik_Common::getRequestVar('url', '', 'string', $_GET);
+
+ // validate referrer
+ $referrer = Piwik_Url::getReferer();
+ if (empty($referrer) || !Piwik_Url::isLocalUrl($referrer)) {
+ die('Invalid Referer detected - This means that your web browser is not sending the "Referer URL" which is
required to proceed with the redirect. Verify your browser settings and add-ons, to check why your browser
is not sending this referer.
- <br/><br/>You can access the page at: '. $url);
- }
-
- // mask visits to *.piwik.org
- if (!self::isPiwikUrl($url))
- {
- Piwik::checkUserHasSomeViewAccess();
- }
- if(!Piwik_Common::isLookLikeUrl($url))
- {
- die('Please check the &url= parameter: it should to be a valid URL');
- }
- @header('Content-Type: text/html; charset=utf-8');
- echo '<html><head><meta http-equiv="refresh" content="0;url=' . $url . '" /></head></html>';
-
- exit;
- }
-
- /**
- * Validate URL against *.piwik.org domains
- *
- * @param string $url
- * @return bool True if valid; false otherwise
- */
- static public function isPiwikUrl($url)
- {
- // guard for IE6 meta refresh parsing weakness (OSVDB 19029)
- if(strpos($url, ';') !== false
- || strpos($url, '&#59') !== false)
- {
- return false;
- }
- if(preg_match('~^http://(qa\.|demo\.|dev\.|forum\.)?piwik.org([#?/]|$)~', $url))
- {
- return true;
- }
-
- // Allow clockworksms domain
- if(strpos($url, 'http://www.clockworksms.com/') === 0)
- {
- return true;
- }
-
- return false;
- }
+ <br/><br/>You can access the page at: ' . $url);
+ }
+
+ // mask visits to *.piwik.org
+ if (!self::isPiwikUrl($url)) {
+ Piwik::checkUserHasSomeViewAccess();
+ }
+ if (!Piwik_Common::isLookLikeUrl($url)) {
+ die('Please check the &url= parameter: it should to be a valid URL');
+ }
+ @header('Content-Type: text/html; charset=utf-8');
+ echo '<html><head><meta http-equiv="refresh" content="0;url=' . $url . '" /></head></html>';
+
+ exit;
+ }
+
+ /**
+ * Validate URL against *.piwik.org domains
+ *
+ * @param string $url
+ * @return bool True if valid; false otherwise
+ */
+ static public function isPiwikUrl($url)
+ {
+ // guard for IE6 meta refresh parsing weakness (OSVDB 19029)
+ if (strpos($url, ';') !== false
+ || strpos($url, '&#59') !== false
+ ) {
+ return false;
+ }
+ if (preg_match('~^http://(qa\.|demo\.|dev\.|forum\.)?piwik.org([#?/]|$)~', $url)) {
+ return true;
+ }
+
+ // Allow clockworksms domain
+ if (strpos($url, 'http://www.clockworksms.com/') === 0) {
+ return true;
+ }
+
+ return false;
+ }
}
diff --git a/plugins/Proxy/Proxy.php b/plugins/Proxy/Proxy.php
index 619c388644..c669215411 100644
--- a/plugins/Proxy/Proxy.php
+++ b/plugins/Proxy/Proxy.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Proxy
*/
@@ -16,21 +16,21 @@
*/
class Piwik_Proxy extends Piwik_Plugin
{
- /**
- * Return information about this plugin.
- *
- * @see Piwik_Plugin
- *
- * @return array
- */
- public function getInformation()
- {
- return array(
- 'description' => 'Proxy services',
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- 'translationAvailable' => false,
- );
- }
+ /**
+ * Return information about this plugin.
+ *
+ * @see Piwik_Plugin
+ *
+ * @return array
+ */
+ public function getInformation()
+ {
+ return array(
+ 'description' => 'Proxy services',
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ 'translationAvailable' => false,
+ );
+ }
}
diff --git a/plugins/Proxy/templates/exportImage.tpl b/plugins/Proxy/templates/exportImage.tpl
index 037b0b8fd8..451b5dedb6 100644
--- a/plugins/Proxy/templates/exportImage.tpl
+++ b/plugins/Proxy/templates/exportImage.tpl
@@ -1,12 +1,13 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" />
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" />
<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>{'General_ExportAsImage_js'|translate}</title>
- </head>
- <body>
- <img title="Piwik Graph" src="{$imageData}" /><br /><br />
- <p>{'General_SaveImageOnYourComputer_js'|translate}</p>
- </body>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <title>{'General_ExportAsImage_js'|translate}</title>
+</head>
+<body>
+<img title="Piwik Graph" src="{$imageData}"/><br/><br/>
+
+<p>{'General_SaveImageOnYourComputer_js'|translate}</p>
+</body>
</html>
diff --git a/plugins/Referers/API.php b/plugins/Referers/API.php
index a74f14290c..ade4026c5b 100644
--- a/plugins/Referers/API.php
+++ b/plugins/Referers/API.php
@@ -11,510 +11,477 @@
/**
* The Referrers API lets you access reports about Websites, Search engines, Keywords, Campaigns used to access your website.
- *
- * For example, "getKeywords" returns all search engine keywords (with <a href='http://piwik.org/docs/analytics-api/reference/#toc-metric-definitions' target='_blank'>general analytics metrics</a> for each keyword), "getWebsites" returns referrer websites (along with the full Referrer URL if the parameter &expanded=1 is set).
+ *
+ * For example, "getKeywords" returns all search engine keywords (with <a href='http://piwik.org/docs/analytics-api/reference/#toc-metric-definitions' target='_blank'>general analytics metrics</a> for each keyword), "getWebsites" returns referrer websites (along with the full Referrer URL if the parameter &expanded=1 is set).
* "getRefererType" returns the Referrer overview report. "getCampaigns" returns the list of all campaigns (and all campaign keywords if the parameter &expanded=1 is set).
- *
- * The methods "getKeywordsForPageUrl" and "getKeywordsForPageTitle" are used to output the top keywords used to find a page.
+ *
+ * The methods "getKeywordsForPageUrl" and "getKeywordsForPageTitle" are used to output the top keywords used to find a page.
* Check out the widget <a href='http://demo.piwik.org/index.php?module=Widgetize&action=iframe&moduleToWidgetize=Referers&actionToWidgetize=getKeywordsForPage&idSite=7&period=day&date=2011-02-15&disableLink=1' target='_blank'>"Top keywords used to find this page"</a> that you can easily re-use on your website.
* @package Piwik_Referers
*/
-class Piwik_Referers_API
+class Piwik_Referers_API
{
- static private $instance = null;
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * @return Piwik_DataTable
- */
- protected function getDataTable($name, $idSite, $period, $date, $segment, $expanded = false, $idSubtable = null)
- {
- $dataTable = Piwik_Archive::getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded, $idSubtable);
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
- $dataTable->queueFilter('ReplaceColumnNames');
- return $dataTable;
- }
-
- /**
- * Returns a report describing visit information for each possible referrer type. The
- * result is a datatable whose subtables are the reports for each parent row's referrer type.
- *
- * The subtable reports are: 'getKeywords' (for search engine referrer type), 'getWebsites',
- * and 'getCampaigns'.
- *
- * @param string $idSite The site ID.
- * @param string $period The period to get data for, either 'day', 'week', 'month', 'year',
- * or 'range'.
- * @param string $date The date of the period.
- * @param string $segment The segment to use.
- * @param int $typeReferer (deprecated) If you want to get data only for a specific referrer
- * type, supply a type for this parameter.
- * @param int $idSubtable For this report this value is a referrer type ID and not an actual
- * subtable ID. The result when using this parameter will be the
- * specific report for the given referrer type.
- * @param bool $expanded Whether to get report w/ subtables loaded or not.
- * @return Piwik_DataTable
- */
- public function getRefererType($idSite, $period, $date, $segment = false, $typeReferer = false,
- $idSubtable = false, $expanded = false)
- {
- // if idSubtable is supplied, interpret idSubtable as referrer type and return correct report
- if ($idSubtable !== false)
- {
- $result = false;
- switch ($idSubtable)
- {
- case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
- $result = $this->getKeywords($idSite, $period, $date, $segment);
- break;
- case Piwik_Common::REFERER_TYPE_WEBSITE:
- $result = $this->getWebsites($idSite, $period, $date, $segment);
- break;
- case Piwik_Common::REFERER_TYPE_CAMPAIGN:
- $result = $this->getCampaigns($idSite, $period, $date, $segment);
- break;
- default: // invalid idSubtable, return whole report
- break;
- }
-
- if ($result)
- {
- return $this->removeSubtableIds($result); // this report won't return subtables of individual reports
- }
- }
-
- // get visits by referrer type
- $dataTable = $this->getDataTable('Referers_type', $idSite, $period, $date, $segment);
-
- if ($typeReferer !== false) // filter for a specific referrer type
- {
- $dataTable->filter('Pattern', array('label', $typeReferer));
- }
-
- // set subtable IDs for each row to the label (which holds the int referrer type)
- // NOTE: not yet possible to do this w/ DataTable_Array instances
- if (!($dataTable instanceof Piwik_DataTable_Array))
- {
- $this->setGetReferrerTypeSubtables($dataTable, $idSite, $period, $date, $segment, $expanded);
- }
-
- // set referrer type column to readable value
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getRefererTypeLabel'));
-
- return $dataTable;
- }
-
- /**
- * Returns a report that shows
- */
- public function getAll( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getRefererType($idSite, $period, $date, $segment, $typeReferer = false,
- $idSubtable = false, $expanded = true);
-
- if ($dataTable instanceof Piwik_DataTable_Array)
- {
- throw new Exception("Referrers.getAll with multiple sites or dates is not supported (yet).");
- }
-
- $dataTable = $dataTable->mergeSubtables($labelColumn = 'referrer_type', $useMetadataColumn = true);
-
- // presentation filters
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc'));
- $dataTable->queueFilter('ReplaceColumnNames');
- $dataTable->queueFilter('ReplaceSummaryRowLabel');
-
- return $dataTable;
- }
-
- public function getKeywords($idSite, $period, $date, $segment = false, $expanded = false)
- {
- $dataTable = $this->getDataTable('Referers_searchEngineByKeyword', $idSite, $period, $date, $segment, $expanded);
- $dataTable = $this->handleKeywordNotDefined($dataTable);
- return $dataTable;
- }
-
- protected function handleKeywordNotDefined($dataTable)
- {
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', array('Piwik_Referers', 'getCleanKeyword')));
- return $dataTable;
- }
-
- public function getKeywordsForPageUrl($idSite, $period, $date, $url)
- {
- // Fetch the Top keywords for this page
- $segment = 'entryPageUrl=='.$url;
- $table = $this->getKeywords($idSite, $period, $date, $segment);
- $this->filterOutKeywordNotDefined($table);
- return $this->getLabelsFromTable($table);
-
- }
-
- public function getKeywordsForPageTitle($idSite, $period, $date, $title)
- {
- $segment = 'entryPageTitle=='.$title;
- $table = $this->getKeywords($idSite, $period, $date, $segment);
- $this->filterOutKeywordNotDefined($table);
- return $this->getLabelsFromTable($table);
- }
-
- /**
- * @param Piwik_Datatable $table
- */
- private function filterOutKeywordNotDefined($table)
- {
- if($table instanceof Piwik_Datatable)
- {
- $row = $table->getRowIdFromLabel('');
- if($row)
- {
- $table->deleteRow($row);
- }
- }
- }
-
- protected function getLabelsFromTable($table)
- {
- $request = $_GET;
- $request['serialize'] = 0;
-
- // Apply generic filters
- $response = new Piwik_API_ResponseBuilder($format = 'original', $request);
- $table = $response->getResponse($table);
-
- // If period=lastX we only keep the first resultset as we want to return a plain list
- if($table instanceof Piwik_DataTable_Array)
- {
- $tables = $table->getArray();
- $table = current($tables);
- }
- // Keep the response simple, only include keywords
- $keywords = $table->getColumn('label');
- return $keywords;
- }
-
- public function getSearchEnginesFromKeywordId($idSite, $period, $date, $idSubtable, $segment = false)
- {
- $dataTable = $this->getDataTable('Referers_searchEngineByKeyword',$idSite, $period, $date, $segment, $expanded = false, $idSubtable);
- $dataTable->queueFilter('ColumnCallbackAddMetadata', array( 'label', 'url', 'Piwik_getSearchEngineUrlFromName') );
- $dataTable->queueFilter('MetadataCallbackAddMetadata', array( 'url', 'logo', 'Piwik_getSearchEngineLogoFromUrl') );
-
- // get the keyword and create the URL to the search result page
- $keywords = $this->getKeywords($idSite, $period, $date, $segment);
- $subTable = $keywords->getRowFromIdSubDataTable($idSubtable);
- if($subTable)
- {
- $keyword = $subTable->getColumn('label');
- $dataTable->queueFilter('MetadataCallbackReplace', array( 'url', 'Piwik_getSearchEngineUrlFromUrlAndKeyword', array($keyword)) );
- }
- return $dataTable;
- }
-
- public function getSearchEngines($idSite, $period, $date, $segment = false, $expanded = false)
- {
- $dataTable = $this->getDataTable('Referers_keywordBySearchEngine',$idSite, $period, $date, $segment, $expanded);
- $dataTable->queueFilter('ColumnCallbackAddMetadata', array( 'label', 'url', 'Piwik_getSearchEngineUrlFromName') );
- $dataTable->queueFilter('MetadataCallbackAddMetadata', array( 'url', 'logo', 'Piwik_getSearchEngineLogoFromUrl') );
- return $dataTable;
- }
-
- public function getKeywordsFromSearchEngineId($idSite, $period, $date, $idSubtable, $segment = false)
- {
- $dataTable = $this->getDataTable('Referers_keywordBySearchEngine',$idSite, $period, $date, $segment, $expanded = false, $idSubtable);
-
- // get the search engine and create the URL to the search result page
- $searchEngines = $this->getSearchEngines($idSite, $period, $date, $segment);
- $searchEngines->applyQueuedFilters();
- $subTable = $searchEngines->getRowFromIdSubDataTable($idSubtable);
- if($subTable)
- {
- $searchEngineUrl = $subTable->getMetadata('url');
- $dataTable->queueFilter('ColumnCallbackAddMetadata', array( 'label', 'url', 'Piwik_getSearchEngineUrlFromKeywordAndUrl', array($searchEngineUrl)));
- }
- $dataTable = $this->handleKeywordNotDefined($dataTable);
- return $dataTable;
- }
-
- public function getCampaigns($idSite, $period, $date, $segment = false, $expanded = false)
- {
- $dataTable = $this->getDataTable('Referers_keywordByCampaign',$idSite, $period, $date, $segment, $expanded);
- return $dataTable;
- }
-
- public function getKeywordsFromCampaignId($idSite, $period, $date, $idSubtable, $segment = false)
- {
- $dataTable = $this->getDataTable('Referers_keywordByCampaign',$idSite, $period, $date, $segment, $expanded = false, $idSubtable);
- return $dataTable;
- }
-
- public function getWebsites($idSite, $period, $date, $segment = false, $expanded = false)
- {
- $dataTable = $this->getDataTable('Referers_urlByWebsite',$idSite, $period, $date, $segment, $expanded);
- return $dataTable;
- }
-
- public function getUrlsFromWebsiteId($idSite, $period, $date, $idSubtable, $segment = false)
- {
- $dataTable = $this->getDataTable('Referers_urlByWebsite',$idSite, $period, $date, $segment, $expanded = false, $idSubtable);
- // the htmlspecialchars_decode call is for BC for before 1.1
- // as the Referer URL was previously encoded in the log tables, but is now recorded raw
- $dataTable->queueFilter('ColumnCallbackAddMetadata', array( 'label', 'url', create_function('$label', 'return htmlspecialchars_decode($label);')) );
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getPathFromUrl'));
- return $dataTable;
- }
-
- /**
- * Returns report comparing the number of visits (and other info) for social network referrers.
- * This is a view of the getWebsites report.
- *
- * @param string $idSite
- * @param string $period
- * @param string $date
- * @param string|bool $segment
- * @param bool $expanded
- * @return Piwik_DataTable
- */
- public function getSocials($idSite, $period, $date, $segment = false, $expanded = false)
- {
- require PIWIK_INCLUDE_PATH.'/core/DataFiles/Socials.php';
-
- $dataTable = $this->getDataTable('Referers_urlByWebsite', $idSite, $period, $date, $segment, $expanded);
-
- $dataTable->filter('ColumnCallbackDeleteRow', array('label', 'Piwik_Referrers_isSocialUrl'));
-
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_Referrers_cleanSocialUrl'));
- $dataTable->filter('GroupBy', array('label', 'Piwik_Referrers_getSocialNetworkFromDomain'));
-
- $this->setSocialIdSubtables($dataTable);
- $this->removeSubtableMetadata($dataTable);
-
- $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', 'Piwik_getSocialsLogoFromUrl'));
-
- return $dataTable;
- }
-
- /**
- * Returns report containing individual referrer URLs for a specific social networking
- * site.
- *
- * @param string $idSite
- * @param string $period
- * @param string $date
- * @param string|false $segment
- * @param int|false $idSubtable This ID does not reference a real DataTable record. Instead, it
- * is the array index of an item in the /core/DataFiles/Socials.php file.
- * The urls are filtered by the social network at this index.
- * If false, no filtering is done and every social URL is returned.
- * @return Piwik_DataTable
- */
- public function getUrlsForSocial( $idSite, $period, $date, $segment = false, $idSubtable = false )
- {
- require PIWIK_INCLUDE_PATH.'/core/DataFiles/Socials.php';
-
- $dataTable = $this->getDataTable(
- 'Referers_urlByWebsite', $idSite, $period, $date, $segment, $expanded = true);
-
- // get the social network domain referred to by $idSubtable
- $social = false;
- if ($idSubtable !== false)
- {
- --$idSubtable;
-
- reset($GLOBALS['Piwik_socialUrl']);
- for ($i = 0; $i != (int)$idSubtable; ++$i)
- {
- next($GLOBALS['Piwik_socialUrl']);
- }
-
- $social = current($GLOBALS['Piwik_socialUrl']);
- }
-
- // filter out everything but social network indicated by $idSubtable
- $dataTable->filter('ColumnCallbackDeleteRow', array('label', 'Piwik_Referrers_isSocialUrl', array($social)));
-
- // merge the datatable's subtables which contain the individual URLs
- $dataTable = $dataTable->mergeSubtables();
-
- // make url labels clickable
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url'));
-
- // prettify the DataTable
- $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_Referrers_removeUrlProtocol'));
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
- $dataTable->queueFilter('ReplaceColumnNames');
-
- return $dataTable;
- }
-
- public function getNumberOfDistinctSearchEngines($idSite, $period, $date, $segment = false)
- {
- return $this->getNumeric('Referers_distinctSearchEngines', $idSite, $period, $date, $segment);
- }
-
- public function getNumberOfDistinctKeywords($idSite, $period, $date, $segment = false)
- {
- return $this->getNumeric('Referers_distinctKeywords', $idSite, $period, $date, $segment);
- }
-
- public function getNumberOfDistinctCampaigns($idSite, $period, $date, $segment = false)
- {
- return $this->getNumeric('Referers_distinctCampaigns', $idSite, $period, $date, $segment);
- }
-
- public function getNumberOfDistinctWebsites($idSite, $period, $date, $segment = false)
- {
- return $this->getNumeric('Referers_distinctWebsites', $idSite, $period, $date, $segment);
- }
-
- public function getNumberOfDistinctWebsitesUrls($idSite, $period, $date, $segment = false)
- {
- return $this->getNumeric('Referers_distinctWebsitesUrls', $idSite, $period, $date, $segment);
- }
-
- private function getNumeric($name, $idSite, $period, $date, $segment)
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- return $archive->getDataTableFromNumeric($name);
- }
-
- /**
- * Removes idsubdatatable_in_db metadata from a DataTable. Used by Social tables since
- * they use fake subtable IDs.
- *
- * @param Piwik_DataTable $dataTable
- */
- private function removeSubtableMetadata( $dataTable )
- {
- if ($dataTable instanceof Piwik_DataTable_Array)
- {
- foreach ($dataTable->getArray() as $childTable)
- {
- $this->removeSubtableMetadata($childTable);
- }
- }
- else
- {
- foreach ($dataTable->getRows() as $row)
- {
- $row->deleteMetadata('idsubdatatable_in_db');
- }
- }
- }
-
- /**
- * Sets the subtable IDs for the DataTable returned by getSocial.
- *
- * The IDs are int indexes into the array in /core/DataFiles/Socials.php.
- *
- * @param Piwik_DataTable $dataTable
- */
- private function setSocialIdSubtables( $dataTable )
- {
- if ($dataTable instanceof Piwik_DataTable_Array)
- {
- foreach ($dataTable->getArray() as $childTable)
- {
- $this->setSocialIdSubtables($childTable);
- }
- }
- else
- {
- foreach ($dataTable->getRows() as $row)
- {
- $socialName = $row->getColumn('label');
-
- $i = 1; // start at one because idSubtable=0 is equivalent to idSubtable=false
- foreach ($GLOBALS['Piwik_socialUrl'] as $domain => $name)
- {
- if ($name == $socialName)
- {
- $row->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $i;
- break;
- }
-
- ++$i;
- }
- }
- }
- }
-
- /**
- * Utility function that removes the subtable IDs for the subtables of the
- * getRefererType report. This avoids infinite recursion in said report (ie,
- * the grandchildren of the report will be the original report, and it will
- * recurse when trying to get a flat report).
- *
- * @param Piwik_DataTable $table
- * @return Piwik_DataTable Returns $table for convenience.
- */
- private function removeSubtableIds( $table )
- {
- if ($table instanceof Piwik_DataTable_Array)
- {
- foreach ($table->getArray() as $childTable)
- {
- $this->removeSubtableIds($childTable);
- }
- }
- else
- {
- foreach ($table->getRows() as $row)
- {
- $row->removeSubtable();
- }
- }
-
- return $table;
- }
-
- /**
- * Utility function that sets the subtables for the getRefererType report.
- *
- * If we're not getting an expanded datatable, the subtable ID is set to each parent
- * row's referrer type (stored in the label for the getRefererType report).
- *
- * If we are getting an expanded datatable, the datatable for the row's referrer
- * type is loaded and attached to the appropriate row in the getRefererType report.
- *
- * @param Piwik_DataTable $dataTable
- * @param string $idSite
- * @param string $period
- * @param string $date
- * @param string $segment
- * @param bool $expanded
- */
- private function setGetReferrerTypeSubtables( $dataTable, $idSite, $period, $date, $segment, $expanded )
- {
- foreach ($dataTable->getRows() as $row)
- {
- $typeReferrer = $row->getColumn('label');
- if ($typeReferrer != Piwik_Common::REFERER_TYPE_DIRECT_ENTRY)
- {
- if (!$expanded) // if we don't want the expanded datatable, then don't do any extra queries
- {
- $row->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $typeReferrer;
- }
- else // otherwise, we have to get the othe datatables
- {
- $subtable = $this->getRefererType($idSite, $period, $date, $segment, $type = false,
- $idSubtable = $typeReferrer);
-
- if ($expanded)
- {
- $subtable->applyQueuedFilters();
- }
-
- $row->setSubtable($subtable);
- }
- }
- }
- }
+ static private $instance = null;
+
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * @return Piwik_DataTable
+ */
+ protected function getDataTable($name, $idSite, $period, $date, $segment, $expanded = false, $idSubtable = null)
+ {
+ $dataTable = Piwik_Archive::getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
+ $dataTable->queueFilter('ReplaceColumnNames');
+ return $dataTable;
+ }
+
+ /**
+ * Returns a report describing visit information for each possible referrer type. The
+ * result is a datatable whose subtables are the reports for each parent row's referrer type.
+ *
+ * The subtable reports are: 'getKeywords' (for search engine referrer type), 'getWebsites',
+ * and 'getCampaigns'.
+ *
+ * @param string $idSite The site ID.
+ * @param string $period The period to get data for, either 'day', 'week', 'month', 'year',
+ * or 'range'.
+ * @param string $date The date of the period.
+ * @param string $segment The segment to use.
+ * @param int $typeReferer (deprecated) If you want to get data only for a specific referrer
+ * type, supply a type for this parameter.
+ * @param int $idSubtable For this report this value is a referrer type ID and not an actual
+ * subtable ID. The result when using this parameter will be the
+ * specific report for the given referrer type.
+ * @param bool $expanded Whether to get report w/ subtables loaded or not.
+ * @return Piwik_DataTable
+ */
+ public function getRefererType($idSite, $period, $date, $segment = false, $typeReferer = false,
+ $idSubtable = false, $expanded = false)
+ {
+ // if idSubtable is supplied, interpret idSubtable as referrer type and return correct report
+ if ($idSubtable !== false) {
+ $result = false;
+ switch ($idSubtable) {
+ case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
+ $result = $this->getKeywords($idSite, $period, $date, $segment);
+ break;
+ case Piwik_Common::REFERER_TYPE_WEBSITE:
+ $result = $this->getWebsites($idSite, $period, $date, $segment);
+ break;
+ case Piwik_Common::REFERER_TYPE_CAMPAIGN:
+ $result = $this->getCampaigns($idSite, $period, $date, $segment);
+ break;
+ default: // invalid idSubtable, return whole report
+ break;
+ }
+
+ if ($result) {
+ return $this->removeSubtableIds($result); // this report won't return subtables of individual reports
+ }
+ }
+
+ // get visits by referrer type
+ $dataTable = $this->getDataTable('Referers_type', $idSite, $period, $date, $segment);
+
+ if ($typeReferer !== false) // filter for a specific referrer type
+ {
+ $dataTable->filter('Pattern', array('label', $typeReferer));
+ }
+
+ // set subtable IDs for each row to the label (which holds the int referrer type)
+ // NOTE: not yet possible to do this w/ DataTable_Array instances
+ if (!($dataTable instanceof Piwik_DataTable_Array)) {
+ $this->setGetReferrerTypeSubtables($dataTable, $idSite, $period, $date, $segment, $expanded);
+ }
+
+ // set referrer type column to readable value
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getRefererTypeLabel'));
+
+ return $dataTable;
+ }
+
+ /**
+ * Returns a report that shows
+ */
+ public function getAll($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getRefererType($idSite, $period, $date, $segment, $typeReferer = false,
+ $idSubtable = false, $expanded = true);
+
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ throw new Exception("Referrers.getAll with multiple sites or dates is not supported (yet).");
+ }
+
+ $dataTable = $dataTable->mergeSubtables($labelColumn = 'referrer_type', $useMetadataColumn = true);
+
+ // presentation filters
+ $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc'));
+ $dataTable->queueFilter('ReplaceColumnNames');
+ $dataTable->queueFilter('ReplaceSummaryRowLabel');
+
+ return $dataTable;
+ }
+
+ public function getKeywords($idSite, $period, $date, $segment = false, $expanded = false)
+ {
+ $dataTable = $this->getDataTable('Referers_searchEngineByKeyword', $idSite, $period, $date, $segment, $expanded);
+ $dataTable = $this->handleKeywordNotDefined($dataTable);
+ return $dataTable;
+ }
+
+ protected function handleKeywordNotDefined($dataTable)
+ {
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', array('Piwik_Referers', 'getCleanKeyword')));
+ return $dataTable;
+ }
+
+ public function getKeywordsForPageUrl($idSite, $period, $date, $url)
+ {
+ // Fetch the Top keywords for this page
+ $segment = 'entryPageUrl==' . $url;
+ $table = $this->getKeywords($idSite, $period, $date, $segment);
+ $this->filterOutKeywordNotDefined($table);
+ return $this->getLabelsFromTable($table);
+
+ }
+
+ public function getKeywordsForPageTitle($idSite, $period, $date, $title)
+ {
+ $segment = 'entryPageTitle==' . $title;
+ $table = $this->getKeywords($idSite, $period, $date, $segment);
+ $this->filterOutKeywordNotDefined($table);
+ return $this->getLabelsFromTable($table);
+ }
+
+ /**
+ * @param Piwik_Datatable $table
+ */
+ private function filterOutKeywordNotDefined($table)
+ {
+ if ($table instanceof Piwik_Datatable) {
+ $row = $table->getRowIdFromLabel('');
+ if ($row) {
+ $table->deleteRow($row);
+ }
+ }
+ }
+
+ protected function getLabelsFromTable($table)
+ {
+ $request = $_GET;
+ $request['serialize'] = 0;
+
+ // Apply generic filters
+ $response = new Piwik_API_ResponseBuilder($format = 'original', $request);
+ $table = $response->getResponse($table);
+
+ // If period=lastX we only keep the first resultset as we want to return a plain list
+ if ($table instanceof Piwik_DataTable_Array) {
+ $tables = $table->getArray();
+ $table = current($tables);
+ }
+ // Keep the response simple, only include keywords
+ $keywords = $table->getColumn('label');
+ return $keywords;
+ }
+
+ public function getSearchEnginesFromKeywordId($idSite, $period, $date, $idSubtable, $segment = false)
+ {
+ $dataTable = $this->getDataTable('Referers_searchEngineByKeyword', $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
+ $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_getSearchEngineUrlFromName'));
+ $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', 'Piwik_getSearchEngineLogoFromUrl'));
+
+ // get the keyword and create the URL to the search result page
+ $keywords = $this->getKeywords($idSite, $period, $date, $segment);
+ $subTable = $keywords->getRowFromIdSubDataTable($idSubtable);
+ if ($subTable) {
+ $keyword = $subTable->getColumn('label');
+ $dataTable->queueFilter('MetadataCallbackReplace', array('url', 'Piwik_getSearchEngineUrlFromUrlAndKeyword', array($keyword)));
+ }
+ return $dataTable;
+ }
+
+ public function getSearchEngines($idSite, $period, $date, $segment = false, $expanded = false)
+ {
+ $dataTable = $this->getDataTable('Referers_keywordBySearchEngine', $idSite, $period, $date, $segment, $expanded);
+ $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_getSearchEngineUrlFromName'));
+ $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', 'Piwik_getSearchEngineLogoFromUrl'));
+ return $dataTable;
+ }
+
+ public function getKeywordsFromSearchEngineId($idSite, $period, $date, $idSubtable, $segment = false)
+ {
+ $dataTable = $this->getDataTable('Referers_keywordBySearchEngine', $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
+
+ // get the search engine and create the URL to the search result page
+ $searchEngines = $this->getSearchEngines($idSite, $period, $date, $segment);
+ $searchEngines->applyQueuedFilters();
+ $subTable = $searchEngines->getRowFromIdSubDataTable($idSubtable);
+ if ($subTable) {
+ $searchEngineUrl = $subTable->getMetadata('url');
+ $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_getSearchEngineUrlFromKeywordAndUrl', array($searchEngineUrl)));
+ }
+ $dataTable = $this->handleKeywordNotDefined($dataTable);
+ return $dataTable;
+ }
+
+ public function getCampaigns($idSite, $period, $date, $segment = false, $expanded = false)
+ {
+ $dataTable = $this->getDataTable('Referers_keywordByCampaign', $idSite, $period, $date, $segment, $expanded);
+ return $dataTable;
+ }
+
+ public function getKeywordsFromCampaignId($idSite, $period, $date, $idSubtable, $segment = false)
+ {
+ $dataTable = $this->getDataTable('Referers_keywordByCampaign', $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
+ return $dataTable;
+ }
+
+ public function getWebsites($idSite, $period, $date, $segment = false, $expanded = false)
+ {
+ $dataTable = $this->getDataTable('Referers_urlByWebsite', $idSite, $period, $date, $segment, $expanded);
+ return $dataTable;
+ }
+
+ public function getUrlsFromWebsiteId($idSite, $period, $date, $idSubtable, $segment = false)
+ {
+ $dataTable = $this->getDataTable('Referers_urlByWebsite', $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
+ // the htmlspecialchars_decode call is for BC for before 1.1
+ // as the Referer URL was previously encoded in the log tables, but is now recorded raw
+ $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', create_function('$label', 'return htmlspecialchars_decode($label);')));
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getPathFromUrl'));
+ return $dataTable;
+ }
+
+ /**
+ * Returns report comparing the number of visits (and other info) for social network referrers.
+ * This is a view of the getWebsites report.
+ *
+ * @param string $idSite
+ * @param string $period
+ * @param string $date
+ * @param string|bool $segment
+ * @param bool $expanded
+ * @return Piwik_DataTable
+ */
+ public function getSocials($idSite, $period, $date, $segment = false, $expanded = false)
+ {
+ require PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php';
+
+ $dataTable = $this->getDataTable('Referers_urlByWebsite', $idSite, $period, $date, $segment, $expanded);
+
+ $dataTable->filter('ColumnCallbackDeleteRow', array('label', 'Piwik_Referrers_isSocialUrl'));
+
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_Referrers_cleanSocialUrl'));
+ $dataTable->filter('GroupBy', array('label', 'Piwik_Referrers_getSocialNetworkFromDomain'));
+
+ $this->setSocialIdSubtables($dataTable);
+ $this->removeSubtableMetadata($dataTable);
+
+ $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', 'Piwik_getSocialsLogoFromUrl'));
+
+ return $dataTable;
+ }
+
+ /**
+ * Returns report containing individual referrer URLs for a specific social networking
+ * site.
+ *
+ * @param string $idSite
+ * @param string $period
+ * @param string $date
+ * @param string|false $segment
+ * @param int|false $idSubtable This ID does not reference a real DataTable record. Instead, it
+ * is the array index of an item in the /core/DataFiles/Socials.php file.
+ * The urls are filtered by the social network at this index.
+ * If false, no filtering is done and every social URL is returned.
+ * @return Piwik_DataTable
+ */
+ public function getUrlsForSocial($idSite, $period, $date, $segment = false, $idSubtable = false)
+ {
+ require PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php';
+
+ $dataTable = $this->getDataTable(
+ 'Referers_urlByWebsite', $idSite, $period, $date, $segment, $expanded = true);
+
+ // get the social network domain referred to by $idSubtable
+ $social = false;
+ if ($idSubtable !== false) {
+ --$idSubtable;
+
+ reset($GLOBALS['Piwik_socialUrl']);
+ for ($i = 0; $i != (int)$idSubtable; ++$i) {
+ next($GLOBALS['Piwik_socialUrl']);
+ }
+
+ $social = current($GLOBALS['Piwik_socialUrl']);
+ }
+
+ // filter out everything but social network indicated by $idSubtable
+ $dataTable->filter('ColumnCallbackDeleteRow', array('label', 'Piwik_Referrers_isSocialUrl', array($social)));
+
+ // merge the datatable's subtables which contain the individual URLs
+ $dataTable = $dataTable->mergeSubtables();
+
+ // make url labels clickable
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url'));
+
+ // prettify the DataTable
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_Referrers_removeUrlProtocol'));
+ $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
+ $dataTable->queueFilter('ReplaceColumnNames');
+
+ return $dataTable;
+ }
+
+ public function getNumberOfDistinctSearchEngines($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric('Referers_distinctSearchEngines', $idSite, $period, $date, $segment);
+ }
+
+ public function getNumberOfDistinctKeywords($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric('Referers_distinctKeywords', $idSite, $period, $date, $segment);
+ }
+
+ public function getNumberOfDistinctCampaigns($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric('Referers_distinctCampaigns', $idSite, $period, $date, $segment);
+ }
+
+ public function getNumberOfDistinctWebsites($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric('Referers_distinctWebsites', $idSite, $period, $date, $segment);
+ }
+
+ public function getNumberOfDistinctWebsitesUrls($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric('Referers_distinctWebsitesUrls', $idSite, $period, $date, $segment);
+ }
+
+ private function getNumeric($name, $idSite, $period, $date, $segment)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ return $archive->getDataTableFromNumeric($name);
+ }
+
+ /**
+ * Removes idsubdatatable_in_db metadata from a DataTable. Used by Social tables since
+ * they use fake subtable IDs.
+ *
+ * @param Piwik_DataTable $dataTable
+ */
+ private function removeSubtableMetadata($dataTable)
+ {
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ foreach ($dataTable->getArray() as $childTable) {
+ $this->removeSubtableMetadata($childTable);
+ }
+ } else {
+ foreach ($dataTable->getRows() as $row) {
+ $row->deleteMetadata('idsubdatatable_in_db');
+ }
+ }
+ }
+
+ /**
+ * Sets the subtable IDs for the DataTable returned by getSocial.
+ *
+ * The IDs are int indexes into the array in /core/DataFiles/Socials.php.
+ *
+ * @param Piwik_DataTable $dataTable
+ */
+ private function setSocialIdSubtables($dataTable)
+ {
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ foreach ($dataTable->getArray() as $childTable) {
+ $this->setSocialIdSubtables($childTable);
+ }
+ } else {
+ foreach ($dataTable->getRows() as $row) {
+ $socialName = $row->getColumn('label');
+
+ $i = 1; // start at one because idSubtable=0 is equivalent to idSubtable=false
+ foreach ($GLOBALS['Piwik_socialUrl'] as $domain => $name) {
+ if ($name == $socialName) {
+ $row->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $i;
+ break;
+ }
+
+ ++$i;
+ }
+ }
+ }
+ }
+
+ /**
+ * Utility function that removes the subtable IDs for the subtables of the
+ * getRefererType report. This avoids infinite recursion in said report (ie,
+ * the grandchildren of the report will be the original report, and it will
+ * recurse when trying to get a flat report).
+ *
+ * @param Piwik_DataTable $table
+ * @return Piwik_DataTable Returns $table for convenience.
+ */
+ private function removeSubtableIds($table)
+ {
+ if ($table instanceof Piwik_DataTable_Array) {
+ foreach ($table->getArray() as $childTable) {
+ $this->removeSubtableIds($childTable);
+ }
+ } else {
+ foreach ($table->getRows() as $row) {
+ $row->removeSubtable();
+ }
+ }
+
+ return $table;
+ }
+
+ /**
+ * Utility function that sets the subtables for the getRefererType report.
+ *
+ * If we're not getting an expanded datatable, the subtable ID is set to each parent
+ * row's referrer type (stored in the label for the getRefererType report).
+ *
+ * If we are getting an expanded datatable, the datatable for the row's referrer
+ * type is loaded and attached to the appropriate row in the getRefererType report.
+ *
+ * @param Piwik_DataTable $dataTable
+ * @param string $idSite
+ * @param string $period
+ * @param string $date
+ * @param string $segment
+ * @param bool $expanded
+ */
+ private function setGetReferrerTypeSubtables($dataTable, $idSite, $period, $date, $segment, $expanded)
+ {
+ foreach ($dataTable->getRows() as $row) {
+ $typeReferrer = $row->getColumn('label');
+ if ($typeReferrer != Piwik_Common::REFERER_TYPE_DIRECT_ENTRY) {
+ if (!$expanded) // if we don't want the expanded datatable, then don't do any extra queries
+ {
+ $row->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $typeReferrer;
+ } else // otherwise, we have to get the othe datatables
+ {
+ $subtable = $this->getRefererType($idSite, $period, $date, $segment, $type = false,
+ $idSubtable = $typeReferrer);
+
+ if ($expanded) {
+ $subtable->applyQueuedFilters();
+ }
+
+ $row->setSubtable($subtable);
+ }
+ }
+ }
+ }
}
diff --git a/plugins/Referers/Controller.php b/plugins/Referers/Controller.php
index fbbf3c14e4..bc3cc00131 100644
--- a/plugins/Referers/Controller.php
+++ b/plugins/Referers/Controller.php
@@ -15,579 +15,561 @@
*/
class Piwik_Referers_Controller extends Piwik_Controller
{
- function index()
- {
- $view = Piwik_View::factory('index');
-
- $view->graphEvolutionReferers = $this->getEvolutionGraph(true, Piwik_Common::REFERER_TYPE_DIRECT_ENTRY, array('nb_visits'));
- $view->nameGraphEvolutionReferers = 'ReferersgetEvolutionGraph';
-
- // building the referers summary report
- $view->dataTableRefererType = $this->getRefererType(true);
-
- $nameValues = $this->getReferersVisitorsByType();
-
- $totalVisits = array_sum($nameValues);
- foreach($nameValues as $name => $value)
- {
- $view->$name = $value;
-
- // calculate percent of total, if there were any visits
- if ($value != 0
- && $totalVisits != 0)
- {
- $percentName = $name.'Percent';
- $view->$percentName = round(($value / $totalVisits) * 100, 0);
- }
- }
-
- // set distinct metrics
- $distinctMetrics = $this->getDistinctReferrersMetrics();
- foreach ($distinctMetrics as $name => $value)
- {
- $view->$name = $value;
- }
-
- // calculate evolution for visit metrics & distinct metrics
- list($lastPeriodDate, $ignore) = Piwik_Period_Range::getLastDate();
- if ($lastPeriodDate !== false)
- {
- $date = Piwik_Common::getRequestVar('date');
- $period = Piwik_Common::getRequestVar('period');
-
- $prettyDate = self::getPrettyDate($date, $period);
- $prettyLastPeriodDate = self::getPrettyDate($lastPeriodDate, $period);
-
- // visit metrics
- $previousValues = $this->getReferersVisitorsByType($lastPeriodDate);
- $this->addEvolutionPropertiesToView($view, $prettyDate, $nameValues, $prettyLastPeriodDate, $previousValues);
-
- // distinct metrics
- $previousValues = $this->getDistinctReferrersMetrics($lastPeriodDate);
- $this->addEvolutionPropertiesToView($view, $prettyDate, $distinctMetrics, $prettyLastPeriodDate, $previousValues);
- }
-
- // sparkline for the historical data of the above values
- $view->urlSparklineSearchEngines = $this->getReferrerUrlSparkline(Piwik_Common::REFERER_TYPE_SEARCH_ENGINE);
- $view->urlSparklineDirectEntry = $this->getReferrerUrlSparkline(Piwik_Common::REFERER_TYPE_DIRECT_ENTRY);
- $view->urlSparklineWebsites = $this->getReferrerUrlSparkline(Piwik_Common::REFERER_TYPE_WEBSITE);
- $view->urlSparklineCampaigns = $this->getReferrerUrlSparkline(Piwik_Common::REFERER_TYPE_CAMPAIGN);
-
- // sparklines for the evolution of the distinct keywords count/websites count/ etc
- $view->urlSparklineDistinctSearchEngines = $this->getUrlSparkline('getLastDistinctSearchEnginesGraph');
- $view->urlSparklineDistinctKeywords = $this->getUrlSparkline('getLastDistinctKeywordsGraph');
- $view->urlSparklineDistinctWebsites = $this->getUrlSparkline('getLastDistinctWebsitesGraph');
- $view->urlSparklineDistinctCampaigns = $this->getUrlSparkline('getLastDistinctCampaignsGraph');
-
- $view->totalVisits = $totalVisits;
- $view->referrersReportsByDimension = $this->getReferrersReportsByDimensionView($totalVisits);
-
- echo $view->render();
- }
-
- /**
- * Returns HTML for the Referrers Overview page that categorizes Referrer reports
- * & allows the user to switch between them.
- *
- * @param int $visits The number of visits for this period & site. If <= 0, the
- * reports are not shown, since they will have no data.
- * @return string The report viewer HTML.
- */
- private function getReferrersReportsByDimensionView( $visits )
- {
- $result = '';
-
- // only display the reports by dimension view if there are visits
- if ($visits > 0)
- {
- $referrersReportsByDimension = new Piwik_View_ReportsByDimension();
-
- $referrersReportsByDimension->addReport(
- 'Referers_ViewAllReferrers', 'Referers_WidgetGetAll', 'Referers.getAll');
-
- $byTypeCategory = Piwik_Translate('Referers_ViewReferrersBy', Piwik_Translate('Live_GoalType'));
- $referrersReportsByDimension->addReport(
- $byTypeCategory, 'Referers_WidgetKeywords', 'Referers.getKeywords');
- $referrersReportsByDimension->addReport($byTypeCategory, 'SitesManager_Sites', 'Referers.getWebsites');
- $referrersReportsByDimension->addReport($byTypeCategory, 'Referers_Campaigns', 'Referers.getCampaigns');
-
- $bySourceCategory = Piwik_Translate('Referers_ViewReferrersBy', Piwik_Translate('General_Source'));
- $referrersReportsByDimension->addReport($bySourceCategory, 'Referers_Socials', 'Referers.getSocials');
- $referrersReportsByDimension->addReport(
- $bySourceCategory, 'Referers_SearchEngines', 'Referers.getSearchEngines');
-
- $result = $referrersReportsByDimension->render();
- }
-
- return $result;
- }
+ function index()
+ {
+ $view = Piwik_View::factory('index');
- function getSearchEnginesAndKeywords()
- {
- $view = Piwik_View::factory('searchEngines_Keywords');
- $view->searchEngines = $this->getSearchEngines(true) ;
- $view->keywords = $this->getKeywords(true);
- echo $view->render();
- }
-
- function getRefererType( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory('tableAllColumns');
- $view->init( $this->pluginName,
- __FUNCTION__,
- 'Referers.getRefererType',
- 'getRefererType'
- );
- $view->disableSearchBox();
- $view->disableOffsetInformationAndPaginationControls();
- $view->disableExcludeLowPopulation();
- $view->disableSubTableWhenShowGoals();
- $view->enableShowGoals();
- $view->setLimit(10);
- $view->setColumnsToDisplay( array('label', 'nb_visits') );
-
- $idSubtable = Piwik_Common::getRequestVar('idSubtable', false);
- $labelColumnTitle = Piwik_Translate('Referers_ColumnRefererType');
- if ($idSubtable !== false)
- {
- switch ($idSubtable)
- {
- case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
- $labelColumnTitle = Piwik_Translate('Referers_ColumnSearchEngine');
- break;
- case Piwik_Common::REFERER_TYPE_WEBSITE:
- $labelColumnTitle = Piwik_Translate('Referers_ColumnWebsite');
- break;
- case Piwik_Common::REFERER_TYPE_CAMPAIGN:
- $labelColumnTitle = Piwik_Translate('Referers_ColumnCampaign');
- break;
- default:
- break;
- }
- }
- $view->setColumnTranslation('label', $labelColumnTitle);
-
- $this->setMetricsVariablesView($view);
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Returns or echo's a report that shows all search keyword, website and campaign
- * referrer information in one report.
- *
- * @param bool $fetch True if the report HTML should be returned. If false, the
- * report is echo'd and nothing is returned.
- * @return string The report HTML or nothing if $fetch is set to false.
- */
- public function getAll( $fetch = false )
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init($this->pluginName, __FUNCTION__, 'Referers.getAll');
- $view->disableExcludeLowPopulation();
- $view->setColumnTranslation('label', Piwik_Translate('Referers_Referrer'));
- $view->setColumnsToDisplay(array('label', 'nb_visits'));
- $view->enableShowGoals();
- $view->setLimit(20);
- $view->setCustomParameter('disable_row_actions', '1');
-
- $setGetAllHtmlPrefix = array($this, 'setGetAllHtmlPrefix');
- $view->queueFilter(
- 'MetadataCallbackAddMetadata', array('referrer_type', 'html_label_prefix', $setGetAllHtmlPrefix));
-
- $view->setMetricsVariablesView($view);
-
- return $this->renderView($view, $fetch);
- }
-
- /**
- * DataTable filter callback that returns the HTML prefix for a label in the
- * 'getAll' report based on the row's referrer type.
- *
- * @param int $referrerType The referrer type.
- * @return string
- */
- public function setGetAllHtmlPrefix( $referrerType )
- {
- // get singular label for referrer type
- $indexTranslation = '';
- switch($referrerType)
- {
- case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
- $indexTranslation = 'Referers_DirectEntry';
- break;
- case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
- $indexTranslation = 'Referers_ColumnKeyword';
- break;
- case Piwik_Common::REFERER_TYPE_WEBSITE:
- $indexTranslation = 'Referers_ColumnWebsite';
- break;
- case Piwik_Common::REFERER_TYPE_CAMPAIGN:
- $indexTranslation = 'Referers_ColumnCampaign';
- break;
- default:
- // case of newsletter, partners, before Piwik 0.2.25
- $indexTranslation = 'General_Others';
- break;
- }
-
- $label = strtolower(Piwik_Translate($indexTranslation));
-
- // return html that displays it as grey & italic
- return '<span style="color:#999"><em>('.$label.')</em></span>';
- }
+ $view->graphEvolutionReferers = $this->getEvolutionGraph(true, Piwik_Common::REFERER_TYPE_DIRECT_ENTRY, array('nb_visits'));
+ $view->nameGraphEvolutionReferers = 'ReferersgetEvolutionGraph';
- function getKeywords( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__,
- 'Referers.getKeywords',
- 'getSearchEnginesFromKeywordId'
- );
- $view->disableExcludeLowPopulation();
- $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnKeyword'));
- $view->enableShowGoals();
- $view->setLimit(25);
- $view->disableSubTableWhenShowGoals();
-
- $this->setMetricsVariablesView($view);
-
- return $this->renderView($view, $fetch);
- }
-
- function getSearchEnginesFromKeywordId( $fetch = false )
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__,
- 'Referers.getSearchEnginesFromKeywordId'
- );
- $view->disableSearchBox();
- $view->disableExcludeLowPopulation();
- $view->setColumnsToDisplay( array('label','nb_visits') );
- $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnSearchEngine'));
- return $this->renderView($view, $fetch);
- }
-
-
- function getSearchEngines( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__,
- 'Referers.getSearchEngines',
- 'getKeywordsFromSearchEngineId'
- );
- $view->disableSearchBox();
- $view->disableExcludeLowPopulation();
- $view->enableShowGoals();
- $view->setLimit(25);
- $view->disableSubTableWhenShowGoals();
- $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnSearchEngine'));
-
- $this->setMetricsVariablesView($view);
-
- return $this->renderView($view, $fetch);
- }
+ // building the referers summary report
+ $view->dataTableRefererType = $this->getRefererType(true);
- function getKeywordsFromSearchEngineId( $fetch = false )
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__,
- 'Referers.getKeywordsFromSearchEngineId'
- );
- $view->disableSearchBox();
- $view->disableExcludeLowPopulation();
- $view->setColumnsToDisplay( array('label','nb_visits') );
- $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnKeyword'));
- return $this->renderView($view, $fetch);
- }
-
- function indexWebsites($fetch = false)
- {
- $view = Piwik_View::factory('Websites_SocialNetworks');
- $view->websites = $this->getWebsites(true) ;
- $view->socials = $this->getSocials(true);
- if ($fetch)
- {
- return $view->render();
- }
- else
- {
- echo $view->render();
- }
- }
-
- function getWebsites( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__,
- 'Referers.getWebsites',
- 'getUrlsFromWebsiteId'
- );
- $view->disableExcludeLowPopulation();
- $view->enableShowGoals();
- $view->setLimit(25);
- $view->disableSubTableWhenShowGoals();
- $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnWebsite'));
-
- $this->setMetricsVariablesView($view);
-
- return $this->renderView($view, $fetch);
- }
-
- function getSocials( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory('graphPie');
- $view->init($this->pluginName, __FUNCTION__, 'Referers.getSocials', 'getUrlsForSocial');
- $view->disableExcludeLowPopulation();
- $view->setLimit(10);
- $view->enableShowGoals();
- $view->disableSubTableWhenShowGoals();
- $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnSocial'));
-
- if(empty($_REQUEST['widget'])) {
- $view->setFooterMessage(Piwik_Translate('Referers_SocialFooterMessage'));
- }
-
- $this->setMetricsVariablesView($view);
-
- return $this->renderView($view, $fetch);
- }
-
- function getUrlsForSocial( $fetch = false )
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init($this->pluginName, __FUNCTION__, 'Referers.getUrlsForSocial');
- $view->disableExcludeLowPopulation();
- $view->setLimit(10);
- $view->enableShowGoals();
- $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnWebsitePage'));
-
- $this->setMetricsVariablesView($view);
-
- return $this->renderView($view, $fetch);
- }
-
- function indexCampaigns($fetch = false)
- {
- return Piwik_View::singleReport(
- Piwik_Translate('Referers_Campaigns'),
- $this->getCampaigns(true), $fetch);
- }
-
- function getCampaigns( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__,
- 'Referers.getCampaigns',
- 'getKeywordsFromCampaignId'
- );
- $view->disableExcludeLowPopulation();
- $view->enableShowGoals();
- $view->setLimit(25);
- $view->setColumnsToDisplay( array('label','nb_visits') );
- $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnCampaign'));
-
- $help = Piwik_Translate('Referers_CampaignFooterHelp', array( '<a target="_blank" href="http://piwik.org/docs/tracking-campaigns/">',
- '</a> - <a target="_blank" href="http://piwik.org/docs/tracking-campaigns/url-builder/">',
- '</a>'
- ));
- $view->setFooterMessage( $help );
- $this->setMetricsVariablesView($view);
- return $this->renderView($view, $fetch);
- }
-
- function getKeywordsFromCampaignId( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__,
- 'Referers.getKeywordsFromCampaignId'
- );
-
- $view->disableSearchBox();
- $view->disableExcludeLowPopulation();
- $view->setColumnsToDisplay( array('label','nb_visits') );
- $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnKeyword'));
-
- return $this->renderView($view, $fetch);
- }
-
- function getUrlsFromWebsiteId( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__,
- 'Referers.getUrlsFromWebsiteId'
- );
- $view->disableSearchBox();
- $view->disableExcludeLowPopulation();
- $view->setColumnsToDisplay( array('label','nb_visits') );
- $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnWebsitePage'));
- $view->setTooltipMetadataName('url');
- return $this->renderView($view, $fetch);
- }
-
- protected function getReferersVisitorsByType( $date = false )
- {
- if ($date === false)
- {
- $date = Piwik_Common::getRequestVar('date', false);
- }
-
- // we disable the queued filters because here we want to get the visits coming from search engines
- // if the filters were applied we would have to look up for a label looking like "Search Engines"
- // which is not good when we have translations
- $dataTableReferersType = Piwik_API_Request::processRequest(
- "Referers.getRefererType", array('disable_queued_filters' => '1', 'date' => $date));
-
- $nameToColumnId = array(
- 'visitorsFromSearchEngines' => Piwik_Common::REFERER_TYPE_SEARCH_ENGINE,
- 'visitorsFromDirectEntry' => Piwik_Common::REFERER_TYPE_DIRECT_ENTRY,
- 'visitorsFromWebsites' => Piwik_Common::REFERER_TYPE_WEBSITE,
- 'visitorsFromCampaigns' => Piwik_Common::REFERER_TYPE_CAMPAIGN,
- );
- $return = array();
- foreach($nameToColumnId as $nameVar => $columnId)
- {
- $value = 0;
- $row = $dataTableReferersType->getRowFromLabel($columnId);
- if($row !== false)
- {
- $value = $row->getColumn(Piwik_Archive::INDEX_NB_VISITS);
- }
- $return[$nameVar] = $value;
- }
- return $return;
- }
+ $nameValues = $this->getReferersVisitorsByType();
- protected $referrerTypeToLabel = array(
- Piwik_Common::REFERER_TYPE_DIRECT_ENTRY => 'Referers_DirectEntry',
- Piwik_Common::REFERER_TYPE_SEARCH_ENGINE => 'Referers_SearchEngines',
- Piwik_Common::REFERER_TYPE_WEBSITE => 'Referers_Websites',
- Piwik_Common::REFERER_TYPE_CAMPAIGN => 'Referers_Campaigns',
- );
-
- public function getEvolutionGraph( $fetch = false, $typeReferer = false, array $columns = array())
- {
- $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Referers.getRefererType');
-
- $view->addTotalRow();
-
- // configure displayed columns
- if(empty($columns))
- {
- $columns = Piwik_Common::getRequestVar('columns');
- $columns = Piwik::getArrayFromApiParameter($columns);
- }
- $columns = !is_array($columns) ? array($columns) : $columns;
- $view->setColumnsToDisplay($columns);
-
- // configure selectable columns
- if (Piwik_Common::getRequestVar('period', false) == 'day') {
- $selectable = array('nb_visits', 'nb_uniq_visitors', 'nb_actions');
- } else {
- $selectable = array('nb_visits', 'nb_actions');
- }
- $view->setSelectableColumns($selectable);
-
- // configure displayed rows
- $visibleRows = Piwik_Common::getRequestVar('rows', false);
- if ($visibleRows !== false)
- {
- // this happens when the row picker has been used
- $visibleRows = Piwik::getArrayFromApiParameter($visibleRows);
-
- // typeReferer is redundant if rows are defined, so make sure it's not used
- $view->setCustomParameter('typeReferer', false);
- }
- else
- {
- // use $typeReferer as default
- if($typeReferer === false)
- {
- $typeReferer = Piwik_Common::getRequestVar('typeReferer', false);
- }
- $label = self::getTranslatedReferrerTypeLabel($typeReferer);
- $total = Piwik_Translate('General_Total');
- $visibleRows = array($label, $total);
- $view->setParametersToModify(array('rows' => $label.','.$total));
- }
- $view->addRowPicker($visibleRows);
-
- $view->setReportDocumentation(Piwik_Translate('Referers_EvolutionDocumentation').'<br />'
- .Piwik_Translate('General_BrokenDownReportDocumentation').'<br />'
- .Piwik_Translate('Referers_EvolutionDocumentationMoreInfo', '&quot;'.Piwik_Translate('Referers_DetailsByRefererType').'&quot;'));
-
- return $this->renderView($view, $fetch);
- }
-
- function getLastDistinctSearchEnginesGraph( $fetch = false )
- {
- $view = $this->getLastUnitGraph($this->pluginName,__FUNCTION__, "Referers.getNumberOfDistinctSearchEngines");
- $view->setColumnTranslation('Referers_distinctSearchEngines', ucfirst(Piwik_Translate('Referers_DistinctSearchEngines')));
- $view->setColumnsToDisplay(array('Referers_distinctSearchEngines'));
- return $this->renderView($view, $fetch);
- }
- function getLastDistinctKeywordsGraph( $fetch = false )
- {
- $view = $this->getLastUnitGraph($this->pluginName,__FUNCTION__, "Referers.getNumberOfDistinctKeywords");
- $view->setColumnTranslation('Referers_distinctKeywords', ucfirst(Piwik_Translate('Referers_DistinctKeywords')));
- $view->setColumnsToDisplay(array('Referers_distinctKeywords'));
- return $this->renderView($view, $fetch);
- }
- function getLastDistinctWebsitesGraph( $fetch = false )
- {
- $view = $this->getLastUnitGraph($this->pluginName,__FUNCTION__, "Referers.getNumberOfDistinctWebsites");
- $view->setColumnTranslation('Referers_distinctWebsites', ucfirst(Piwik_Translate('Referers_DistinctWebsites')));
- $view->setColumnsToDisplay(array('Referers_distinctWebsites'));
- return $this->renderView($view, $fetch);
- }
- function getLastDistinctCampaignsGraph( $fetch = false )
- {
- $view = $this->getLastUnitGraph($this->pluginName,__FUNCTION__, "Referers.getNumberOfDistinctCampaigns");
- $view->setColumnTranslation('Referers_distinctCampaigns', ucfirst(Piwik_Translate('Referers_DistinctCampaigns')));
- $view->setColumnsToDisplay(array('Referers_distinctCampaigns'));
- return $this->renderView($view, $fetch);
- }
+ $totalVisits = array_sum($nameValues);
+ foreach ($nameValues as $name => $value) {
+ $view->$name = $value;
- function getKeywordsForPage()
- {
- Piwik::checkUserHasViewAccess($this->idSite);
-
- $requestUrl = '&date=previous1'
- .'&period=week'
- .'&idSite='.$this->idSite
- ;
-
- $topPageUrlRequest = $requestUrl
- .'&method=Actions.getPageUrls'
- .'&filter_limit=50'
- .'&format=original';
- $request = new Piwik_API_Request($topPageUrlRequest);
- $request = $request->process();
- $tables = $request->getArray();
-
- $topPageUrl = false;
- $first = key($tables);
- if(!empty($first))
- {
- $topPageUrls = $tables[$first];
- $topPageUrls = $topPageUrls->getRowsMetadata('url');
- $tmpTopPageUrls = array_values($topPageUrls);
- $topPageUrl = current($tmpTopPageUrls);
- }
- if(empty($topPageUrl))
- {
- $topPageUrl = $this->site->getMainUrl();
- }
- $url = $topPageUrl;
-
- // HTML
- $api = Piwik_Url::getCurrentUrlWithoutFileName()
- .'?module=API&method=Referers.getKeywordsForPageUrl'
- .'&format=php'
- .'&filter_limit=10'
- .'&token_auth='.Piwik::getCurrentUserTokenAuth();
-
- $api .= $requestUrl;
- $code = '
+ // calculate percent of total, if there were any visits
+ if ($value != 0
+ && $totalVisits != 0
+ ) {
+ $percentName = $name . 'Percent';
+ $view->$percentName = round(($value / $totalVisits) * 100, 0);
+ }
+ }
+
+ // set distinct metrics
+ $distinctMetrics = $this->getDistinctReferrersMetrics();
+ foreach ($distinctMetrics as $name => $value) {
+ $view->$name = $value;
+ }
+
+ // calculate evolution for visit metrics & distinct metrics
+ list($lastPeriodDate, $ignore) = Piwik_Period_Range::getLastDate();
+ if ($lastPeriodDate !== false) {
+ $date = Piwik_Common::getRequestVar('date');
+ $period = Piwik_Common::getRequestVar('period');
+
+ $prettyDate = self::getPrettyDate($date, $period);
+ $prettyLastPeriodDate = self::getPrettyDate($lastPeriodDate, $period);
+
+ // visit metrics
+ $previousValues = $this->getReferersVisitorsByType($lastPeriodDate);
+ $this->addEvolutionPropertiesToView($view, $prettyDate, $nameValues, $prettyLastPeriodDate, $previousValues);
+
+ // distinct metrics
+ $previousValues = $this->getDistinctReferrersMetrics($lastPeriodDate);
+ $this->addEvolutionPropertiesToView($view, $prettyDate, $distinctMetrics, $prettyLastPeriodDate, $previousValues);
+ }
+
+ // sparkline for the historical data of the above values
+ $view->urlSparklineSearchEngines = $this->getReferrerUrlSparkline(Piwik_Common::REFERER_TYPE_SEARCH_ENGINE);
+ $view->urlSparklineDirectEntry = $this->getReferrerUrlSparkline(Piwik_Common::REFERER_TYPE_DIRECT_ENTRY);
+ $view->urlSparklineWebsites = $this->getReferrerUrlSparkline(Piwik_Common::REFERER_TYPE_WEBSITE);
+ $view->urlSparklineCampaigns = $this->getReferrerUrlSparkline(Piwik_Common::REFERER_TYPE_CAMPAIGN);
+
+ // sparklines for the evolution of the distinct keywords count/websites count/ etc
+ $view->urlSparklineDistinctSearchEngines = $this->getUrlSparkline('getLastDistinctSearchEnginesGraph');
+ $view->urlSparklineDistinctKeywords = $this->getUrlSparkline('getLastDistinctKeywordsGraph');
+ $view->urlSparklineDistinctWebsites = $this->getUrlSparkline('getLastDistinctWebsitesGraph');
+ $view->urlSparklineDistinctCampaigns = $this->getUrlSparkline('getLastDistinctCampaignsGraph');
+
+ $view->totalVisits = $totalVisits;
+ $view->referrersReportsByDimension = $this->getReferrersReportsByDimensionView($totalVisits);
+
+ echo $view->render();
+ }
+
+ /**
+ * Returns HTML for the Referrers Overview page that categorizes Referrer reports
+ * & allows the user to switch between them.
+ *
+ * @param int $visits The number of visits for this period & site. If <= 0, the
+ * reports are not shown, since they will have no data.
+ * @return string The report viewer HTML.
+ */
+ private function getReferrersReportsByDimensionView($visits)
+ {
+ $result = '';
+
+ // only display the reports by dimension view if there are visits
+ if ($visits > 0) {
+ $referrersReportsByDimension = new Piwik_View_ReportsByDimension();
+
+ $referrersReportsByDimension->addReport(
+ 'Referers_ViewAllReferrers', 'Referers_WidgetGetAll', 'Referers.getAll');
+
+ $byTypeCategory = Piwik_Translate('Referers_ViewReferrersBy', Piwik_Translate('Live_GoalType'));
+ $referrersReportsByDimension->addReport(
+ $byTypeCategory, 'Referers_WidgetKeywords', 'Referers.getKeywords');
+ $referrersReportsByDimension->addReport($byTypeCategory, 'SitesManager_Sites', 'Referers.getWebsites');
+ $referrersReportsByDimension->addReport($byTypeCategory, 'Referers_Campaigns', 'Referers.getCampaigns');
+
+ $bySourceCategory = Piwik_Translate('Referers_ViewReferrersBy', Piwik_Translate('General_Source'));
+ $referrersReportsByDimension->addReport($bySourceCategory, 'Referers_Socials', 'Referers.getSocials');
+ $referrersReportsByDimension->addReport(
+ $bySourceCategory, 'Referers_SearchEngines', 'Referers.getSearchEngines');
+
+ $result = $referrersReportsByDimension->render();
+ }
+
+ return $result;
+ }
+
+ function getSearchEnginesAndKeywords()
+ {
+ $view = Piwik_View::factory('searchEngines_Keywords');
+ $view->searchEngines = $this->getSearchEngines(true);
+ $view->keywords = $this->getKeywords(true);
+ echo $view->render();
+ }
+
+ function getRefererType($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory('tableAllColumns');
+ $view->init($this->pluginName,
+ __FUNCTION__,
+ 'Referers.getRefererType',
+ 'getRefererType'
+ );
+ $view->disableSearchBox();
+ $view->disableOffsetInformationAndPaginationControls();
+ $view->disableExcludeLowPopulation();
+ $view->disableSubTableWhenShowGoals();
+ $view->enableShowGoals();
+ $view->setLimit(10);
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
+
+ $idSubtable = Piwik_Common::getRequestVar('idSubtable', false);
+ $labelColumnTitle = Piwik_Translate('Referers_ColumnRefererType');
+ if ($idSubtable !== false) {
+ switch ($idSubtable) {
+ case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
+ $labelColumnTitle = Piwik_Translate('Referers_ColumnSearchEngine');
+ break;
+ case Piwik_Common::REFERER_TYPE_WEBSITE:
+ $labelColumnTitle = Piwik_Translate('Referers_ColumnWebsite');
+ break;
+ case Piwik_Common::REFERER_TYPE_CAMPAIGN:
+ $labelColumnTitle = Piwik_Translate('Referers_ColumnCampaign');
+ break;
+ default:
+ break;
+ }
+ }
+ $view->setColumnTranslation('label', $labelColumnTitle);
+
+ $this->setMetricsVariablesView($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Returns or echo's a report that shows all search keyword, website and campaign
+ * referrer information in one report.
+ *
+ * @param bool $fetch True if the report HTML should be returned. If false, the
+ * report is echo'd and nothing is returned.
+ * @return string The report HTML or nothing if $fetch is set to false.
+ */
+ public function getAll($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Referers.getAll');
+ $view->disableExcludeLowPopulation();
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_Referrer'));
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
+ $view->enableShowGoals();
+ $view->setLimit(20);
+ $view->setCustomParameter('disable_row_actions', '1');
+
+ $setGetAllHtmlPrefix = array($this, 'setGetAllHtmlPrefix');
+ $view->queueFilter(
+ 'MetadataCallbackAddMetadata', array('referrer_type', 'html_label_prefix', $setGetAllHtmlPrefix));
+
+ $view->setMetricsVariablesView($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * DataTable filter callback that returns the HTML prefix for a label in the
+ * 'getAll' report based on the row's referrer type.
+ *
+ * @param int $referrerType The referrer type.
+ * @return string
+ */
+ public function setGetAllHtmlPrefix($referrerType)
+ {
+ // get singular label for referrer type
+ $indexTranslation = '';
+ switch ($referrerType) {
+ case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
+ $indexTranslation = 'Referers_DirectEntry';
+ break;
+ case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
+ $indexTranslation = 'Referers_ColumnKeyword';
+ break;
+ case Piwik_Common::REFERER_TYPE_WEBSITE:
+ $indexTranslation = 'Referers_ColumnWebsite';
+ break;
+ case Piwik_Common::REFERER_TYPE_CAMPAIGN:
+ $indexTranslation = 'Referers_ColumnCampaign';
+ break;
+ default:
+ // case of newsletter, partners, before Piwik 0.2.25
+ $indexTranslation = 'General_Others';
+ break;
+ }
+
+ $label = strtolower(Piwik_Translate($indexTranslation));
+
+ // return html that displays it as grey & italic
+ return '<span style="color:#999"><em>(' . $label . ')</em></span>';
+ }
+
+ function getKeywords($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__,
+ 'Referers.getKeywords',
+ 'getSearchEnginesFromKeywordId'
+ );
+ $view->disableExcludeLowPopulation();
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnKeyword'));
+ $view->enableShowGoals();
+ $view->setLimit(25);
+ $view->disableSubTableWhenShowGoals();
+
+ $this->setMetricsVariablesView($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ function getSearchEnginesFromKeywordId($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__,
+ 'Referers.getSearchEnginesFromKeywordId'
+ );
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnSearchEngine'));
+ return $this->renderView($view, $fetch);
+ }
+
+
+ function getSearchEngines($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__,
+ 'Referers.getSearchEngines',
+ 'getKeywordsFromSearchEngineId'
+ );
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->enableShowGoals();
+ $view->setLimit(25);
+ $view->disableSubTableWhenShowGoals();
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnSearchEngine'));
+
+ $this->setMetricsVariablesView($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ function getKeywordsFromSearchEngineId($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__,
+ 'Referers.getKeywordsFromSearchEngineId'
+ );
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnKeyword'));
+ return $this->renderView($view, $fetch);
+ }
+
+ function indexWebsites($fetch = false)
+ {
+ $view = Piwik_View::factory('Websites_SocialNetworks');
+ $view->websites = $this->getWebsites(true);
+ $view->socials = $this->getSocials(true);
+ if ($fetch) {
+ return $view->render();
+ } else {
+ echo $view->render();
+ }
+ }
+
+ function getWebsites($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__,
+ 'Referers.getWebsites',
+ 'getUrlsFromWebsiteId'
+ );
+ $view->disableExcludeLowPopulation();
+ $view->enableShowGoals();
+ $view->setLimit(25);
+ $view->disableSubTableWhenShowGoals();
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnWebsite'));
+
+ $this->setMetricsVariablesView($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ function getSocials($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory('graphPie');
+ $view->init($this->pluginName, __FUNCTION__, 'Referers.getSocials', 'getUrlsForSocial');
+ $view->disableExcludeLowPopulation();
+ $view->setLimit(10);
+ $view->enableShowGoals();
+ $view->disableSubTableWhenShowGoals();
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnSocial'));
+
+ if (empty($_REQUEST['widget'])) {
+ $view->setFooterMessage(Piwik_Translate('Referers_SocialFooterMessage'));
+ }
+
+ $this->setMetricsVariablesView($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ function getUrlsForSocial($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'Referers.getUrlsForSocial');
+ $view->disableExcludeLowPopulation();
+ $view->setLimit(10);
+ $view->enableShowGoals();
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnWebsitePage'));
+
+ $this->setMetricsVariablesView($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ function indexCampaigns($fetch = false)
+ {
+ return Piwik_View::singleReport(
+ Piwik_Translate('Referers_Campaigns'),
+ $this->getCampaigns(true), $fetch);
+ }
+
+ function getCampaigns($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__,
+ 'Referers.getCampaigns',
+ 'getKeywordsFromCampaignId'
+ );
+ $view->disableExcludeLowPopulation();
+ $view->enableShowGoals();
+ $view->setLimit(25);
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnCampaign'));
+
+ $help = Piwik_Translate('Referers_CampaignFooterHelp', array('<a target="_blank" href="http://piwik.org/docs/tracking-campaigns/">',
+ '</a> - <a target="_blank" href="http://piwik.org/docs/tracking-campaigns/url-builder/">',
+ '</a>'
+ ));
+ $view->setFooterMessage($help);
+ $this->setMetricsVariablesView($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ function getKeywordsFromCampaignId($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__,
+ 'Referers.getKeywordsFromCampaignId'
+ );
+
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnKeyword'));
+
+ return $this->renderView($view, $fetch);
+ }
+
+ function getUrlsFromWebsiteId($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__,
+ 'Referers.getUrlsFromWebsiteId'
+ );
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
+ $view->setColumnTranslation('label', Piwik_Translate('Referers_ColumnWebsitePage'));
+ $view->setTooltipMetadataName('url');
+ return $this->renderView($view, $fetch);
+ }
+
+ protected function getReferersVisitorsByType($date = false)
+ {
+ if ($date === false) {
+ $date = Piwik_Common::getRequestVar('date', false);
+ }
+
+ // we disable the queued filters because here we want to get the visits coming from search engines
+ // if the filters were applied we would have to look up for a label looking like "Search Engines"
+ // which is not good when we have translations
+ $dataTableReferersType = Piwik_API_Request::processRequest(
+ "Referers.getRefererType", array('disable_queued_filters' => '1', 'date' => $date));
+
+ $nameToColumnId = array(
+ 'visitorsFromSearchEngines' => Piwik_Common::REFERER_TYPE_SEARCH_ENGINE,
+ 'visitorsFromDirectEntry' => Piwik_Common::REFERER_TYPE_DIRECT_ENTRY,
+ 'visitorsFromWebsites' => Piwik_Common::REFERER_TYPE_WEBSITE,
+ 'visitorsFromCampaigns' => Piwik_Common::REFERER_TYPE_CAMPAIGN,
+ );
+ $return = array();
+ foreach ($nameToColumnId as $nameVar => $columnId) {
+ $value = 0;
+ $row = $dataTableReferersType->getRowFromLabel($columnId);
+ if ($row !== false) {
+ $value = $row->getColumn(Piwik_Archive::INDEX_NB_VISITS);
+ }
+ $return[$nameVar] = $value;
+ }
+ return $return;
+ }
+
+ protected $referrerTypeToLabel = array(
+ Piwik_Common::REFERER_TYPE_DIRECT_ENTRY => 'Referers_DirectEntry',
+ Piwik_Common::REFERER_TYPE_SEARCH_ENGINE => 'Referers_SearchEngines',
+ Piwik_Common::REFERER_TYPE_WEBSITE => 'Referers_Websites',
+ Piwik_Common::REFERER_TYPE_CAMPAIGN => 'Referers_Campaigns',
+ );
+
+ public function getEvolutionGraph($fetch = false, $typeReferer = false, array $columns = array())
+ {
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Referers.getRefererType');
+
+ $view->addTotalRow();
+
+ // configure displayed columns
+ if (empty($columns)) {
+ $columns = Piwik_Common::getRequestVar('columns');
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ }
+ $columns = !is_array($columns) ? array($columns) : $columns;
+ $view->setColumnsToDisplay($columns);
+
+ // configure selectable columns
+ if (Piwik_Common::getRequestVar('period', false) == 'day') {
+ $selectable = array('nb_visits', 'nb_uniq_visitors', 'nb_actions');
+ } else {
+ $selectable = array('nb_visits', 'nb_actions');
+ }
+ $view->setSelectableColumns($selectable);
+
+ // configure displayed rows
+ $visibleRows = Piwik_Common::getRequestVar('rows', false);
+ if ($visibleRows !== false) {
+ // this happens when the row picker has been used
+ $visibleRows = Piwik::getArrayFromApiParameter($visibleRows);
+
+ // typeReferer is redundant if rows are defined, so make sure it's not used
+ $view->setCustomParameter('typeReferer', false);
+ } else {
+ // use $typeReferer as default
+ if ($typeReferer === false) {
+ $typeReferer = Piwik_Common::getRequestVar('typeReferer', false);
+ }
+ $label = self::getTranslatedReferrerTypeLabel($typeReferer);
+ $total = Piwik_Translate('General_Total');
+ $visibleRows = array($label, $total);
+ $view->setParametersToModify(array('rows' => $label . ',' . $total));
+ }
+ $view->addRowPicker($visibleRows);
+
+ $view->setReportDocumentation(Piwik_Translate('Referers_EvolutionDocumentation') . '<br />'
+ . Piwik_Translate('General_BrokenDownReportDocumentation') . '<br />'
+ . Piwik_Translate('Referers_EvolutionDocumentationMoreInfo', '&quot;' . Piwik_Translate('Referers_DetailsByRefererType') . '&quot;'));
+
+ return $this->renderView($view, $fetch);
+ }
+
+ function getLastDistinctSearchEnginesGraph($fetch = false)
+ {
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, "Referers.getNumberOfDistinctSearchEngines");
+ $view->setColumnTranslation('Referers_distinctSearchEngines', ucfirst(Piwik_Translate('Referers_DistinctSearchEngines')));
+ $view->setColumnsToDisplay(array('Referers_distinctSearchEngines'));
+ return $this->renderView($view, $fetch);
+ }
+
+ function getLastDistinctKeywordsGraph($fetch = false)
+ {
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, "Referers.getNumberOfDistinctKeywords");
+ $view->setColumnTranslation('Referers_distinctKeywords', ucfirst(Piwik_Translate('Referers_DistinctKeywords')));
+ $view->setColumnsToDisplay(array('Referers_distinctKeywords'));
+ return $this->renderView($view, $fetch);
+ }
+
+ function getLastDistinctWebsitesGraph($fetch = false)
+ {
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, "Referers.getNumberOfDistinctWebsites");
+ $view->setColumnTranslation('Referers_distinctWebsites', ucfirst(Piwik_Translate('Referers_DistinctWebsites')));
+ $view->setColumnsToDisplay(array('Referers_distinctWebsites'));
+ return $this->renderView($view, $fetch);
+ }
+
+ function getLastDistinctCampaignsGraph($fetch = false)
+ {
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, "Referers.getNumberOfDistinctCampaigns");
+ $view->setColumnTranslation('Referers_distinctCampaigns', ucfirst(Piwik_Translate('Referers_DistinctCampaigns')));
+ $view->setColumnsToDisplay(array('Referers_distinctCampaigns'));
+ return $this->renderView($view, $fetch);
+ }
+
+ function getKeywordsForPage()
+ {
+ Piwik::checkUserHasViewAccess($this->idSite);
+
+ $requestUrl = '&date=previous1'
+ . '&period=week'
+ . '&idSite=' . $this->idSite;
+
+ $topPageUrlRequest = $requestUrl
+ . '&method=Actions.getPageUrls'
+ . '&filter_limit=50'
+ . '&format=original';
+ $request = new Piwik_API_Request($topPageUrlRequest);
+ $request = $request->process();
+ $tables = $request->getArray();
+
+ $topPageUrl = false;
+ $first = key($tables);
+ if (!empty($first)) {
+ $topPageUrls = $tables[$first];
+ $topPageUrls = $topPageUrls->getRowsMetadata('url');
+ $tmpTopPageUrls = array_values($topPageUrls);
+ $topPageUrl = current($tmpTopPageUrls);
+ }
+ if (empty($topPageUrl)) {
+ $topPageUrl = $this->site->getMainUrl();
+ }
+ $url = $topPageUrl;
+
+ // HTML
+ $api = Piwik_Url::getCurrentUrlWithoutFileName()
+ . '?module=API&method=Referers.getKeywordsForPageUrl'
+ . '&format=php'
+ . '&filter_limit=10'
+ . '&token_auth=' . Piwik::getCurrentUserTokenAuth();
+
+ $api .= $requestUrl;
+ $code = '
// This function will call the API to get best keyword for current URL.
// Then it writes the list of best keywords in a HTML list
function DisplayTopKeywords($url = "")
@@ -596,7 +578,7 @@ function DisplayTopKeywords($url = "")
@ini_set("default_socket_timeout", $timeout = 1);
// Get the Keywords data
$url = empty($url) ? "http://". $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] : $url;
- $api = "'.$api.'&url=" . urlencode($url);
+ $api = "' . $api . '&url=" . urlencode($url);
$keywords = @unserialize(file_get_contents($api));
if($keywords === false || isset($keywords["result"])) {
// DEBUG ONLY: uncomment for troubleshooting an empty output (the URL output reveals the token_auth)
@@ -616,131 +598,132 @@ function DisplayTopKeywords($url = "")
}
';
- $jsonRequest = str_replace('format=php', 'format=json', $api);
- echo "<p>This widget is designed to work in your website directly.
+ $jsonRequest = str_replace('format=php', 'format=json', $api);
+ echo "<p>This widget is designed to work in your website directly.
This widget makes it easy to use Piwik to <i>automatically display the list of Top Keywords</i>, for each of your website Page URLs.</p>
<p>
<b>Example API URL</b> - For example if you would like to get the top 10 keywords, used last week, to land on the page <a target='_blank' href='$topPageUrl'>$topPageUrl</a>,
- in format JSON: you would dynamically fetch the data using <a target='_blank' href='$jsonRequest&url=".urlencode($topPageUrl)."'>this API request URL</a>. Make sure you encode the 'url' parameter in the URL.</p>
+ in format JSON: you would dynamically fetch the data using <a target='_blank' href='$jsonRequest&url=" . urlencode($topPageUrl) . "'>this API request URL</a>. Make sure you encode the 'url' parameter in the URL.</p>
<p><b>PHP Function ready to use!</b> - If you use PHP on your website, we have prepared a small code snippet that you can copy paste in your Website PHP files. You can then simply call the function <code>DisplayTopKeywords();</code> anywhere in your template, at the bottom of the content or in your blog sidebar.
If you run this code in your page $topPageUrl, it would output the following:";
-
- echo "<div style='width:400px;margin-left:20px;padding:10px;border:1px solid black;'>";
- function DisplayTopKeywords($url = "", $api)
- {
- // Do not spend more than 1 second fetching the data
- @ini_set("default_socket_timeout", $timeout = 1);
- // Get the Keywords data
- $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"])) {
- // 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;
- }
-
- // Display the list in HTML
- $url = htmlspecialchars($url, ENT_QUOTES);
- $output = "<h2>Top Keywords for <a href=\'$url\'>$url</a></h2><ul>";
- foreach($keywords as $keyword) {
- $output .= "<li>". $keyword[0]. "</li>";
- }
- if(empty($keywords)) { $output .= "Nothing yet..."; }
- $output .= "</ul>";
- echo $output;
- }
- DisplayTopKeywords($topPageUrl, $api);
-
- echo "</div><br/>
+
+ echo "<div style='width:400px;margin-left:20px;padding:10px;border:1px solid black;'>";
+ function DisplayTopKeywords($url = "", $api)
+ {
+ // Do not spend more than 1 second fetching the data
+ @ini_set("default_socket_timeout", $timeout = 1);
+ // Get the Keywords data
+ $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"])) {
+ // 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;
+ }
+
+ // Display the list in HTML
+ $url = htmlspecialchars($url, ENT_QUOTES);
+ $output = "<h2>Top Keywords for <a href=\'$url\'>$url</a></h2><ul>";
+ foreach ($keywords as $keyword) {
+ $output .= "<li>" . $keyword[0] . "</li>";
+ }
+ if (empty($keywords)) {
+ $output .= "Nothing yet...";
+ }
+ $output .= "</ul>";
+ echo $output;
+ }
+
+ DisplayTopKeywords($topPageUrl, $api);
+
+ echo "</div><br/>
<p>Here is the PHP function that you can paste in your pages:</P>
<textarea cols=60 rows=8>&lt;?php\n" . htmlspecialchars($code) . "\n DisplayTopKeywords();</textarea>
";
-
- echo "
+
+ echo "
<p><b>Notes</b>: You can for example edit the code to to make the Top search keywords link to your Website search result pages.
<br/>On medium to large traffic websites, we recommend to cache this data, as to minimize the performance impact of calling the Piwik API on each page view.
</p>
";
-
- }
-
- /**
- * Returns the i18n-ized label for a referrer type.
- *
- * @param int $typeReferrer The referrer type. Referrer types are defined in Piwik_Common class.
- * @return string The i18n-ized label.
- */
- public static function getTranslatedReferrerTypeLabel( $typeReferrer )
- {
- $label = Piwik_getRefererTypeLabel($typeReferrer);
- return Piwik_Translate($label);
- }
-
- /**
- * Returns the URL for the sparkline of visits with a specific referrer type.
- *
- * @param int $typeReferrer The referrer type. Referrer types are defined in Piwik_Common class.
- * @return string The URL that can be used to get a sparkline image.
- */
- private function getReferrerUrlSparkline( $referrerType )
- {
- $totalRow = Piwik_Translate('General_Total');
- return $this->getUrlSparkline(
- 'getEvolutionGraph',
- array('columns' => array('nb_visits'),
- 'rows' => array(self::getTranslatedReferrerTypeLabel($referrerType), $totalRow),
- 'typeReferer' => $referrerType)
- );
- }
-
- /**
- * Returns an array containing the number of distinct referrers for each
- * referrer type.
- *
- * @param string|false $date The date to use when getting metrics. If false, the
- * date query param is used.
- * @return array The metrics.
- */
- private function getDistinctReferrersMetrics( $date = false )
- {
- $propertyToAccessorMapping = array(
- 'numberDistinctSearchEngines' => 'getNumberOfDistinctSearchEngines',
- 'numberDistinctKeywords' => 'getNumberOfDistinctKeywords',
- 'numberDistinctWebsites' => 'getNumberOfDistinctWebsites',
- 'numberDistinctWebsitesUrls' => 'getNumberOfDistinctWebsitesUrls',
- 'numberDistinctCampaigns' => 'getNumberOfDistinctCampaigns',
- );
-
- $result = array();
- foreach ($propertyToAccessorMapping as $property => $method)
- {
- $result[$property] = $this->getNumericValue('Referers.'.$method, $date);
- }
- return $result;
- }
-
- /**
- * Utility method that calculates evolution values for a set of current & past values
- * and sets properties on a Piwik_View w/ HTML that displays the evolution percents.
- *
- * @param Piwik_View $view The view to set properties on.
- * @param string $date The date of the current values.
- * @param array $currentValues Array mapping view property names w/ present values.
- * @param string $lastPeriodDate The date of the period in the past.
- * @param array $previousValues Array mapping view property names w/ past values. Keys
- * in this array should be the same as keys in $currentValues.
- * @param bool $isVisits Whether the values are counting visits or something else.
- */
- private function addEvolutionPropertiesToView( $view, $date, $currentValues, $lastPeriodDate, $previousValues)
- {
- foreach ($previousValues as $name => $pastValue)
- {
- $currentValue = $currentValues[$name];
- $evolutionName = $name.'Evolution';
-
- $view->$evolutionName = $this->getEvolutionHtml($date, $currentValue, $lastPeriodDate, $pastValue);
- }
- }
+
+ }
+
+ /**
+ * Returns the i18n-ized label for a referrer type.
+ *
+ * @param int $typeReferrer The referrer type. Referrer types are defined in Piwik_Common class.
+ * @return string The i18n-ized label.
+ */
+ public static function getTranslatedReferrerTypeLabel($typeReferrer)
+ {
+ $label = Piwik_getRefererTypeLabel($typeReferrer);
+ return Piwik_Translate($label);
+ }
+
+ /**
+ * Returns the URL for the sparkline of visits with a specific referrer type.
+ *
+ * @param int $typeReferrer The referrer type. Referrer types are defined in Piwik_Common class.
+ * @return string The URL that can be used to get a sparkline image.
+ */
+ private function getReferrerUrlSparkline($referrerType)
+ {
+ $totalRow = Piwik_Translate('General_Total');
+ return $this->getUrlSparkline(
+ 'getEvolutionGraph',
+ array('columns' => array('nb_visits'),
+ 'rows' => array(self::getTranslatedReferrerTypeLabel($referrerType), $totalRow),
+ 'typeReferer' => $referrerType)
+ );
+ }
+
+ /**
+ * Returns an array containing the number of distinct referrers for each
+ * referrer type.
+ *
+ * @param string|false $date The date to use when getting metrics. If false, the
+ * date query param is used.
+ * @return array The metrics.
+ */
+ private function getDistinctReferrersMetrics($date = false)
+ {
+ $propertyToAccessorMapping = array(
+ 'numberDistinctSearchEngines' => 'getNumberOfDistinctSearchEngines',
+ 'numberDistinctKeywords' => 'getNumberOfDistinctKeywords',
+ 'numberDistinctWebsites' => 'getNumberOfDistinctWebsites',
+ 'numberDistinctWebsitesUrls' => 'getNumberOfDistinctWebsitesUrls',
+ 'numberDistinctCampaigns' => 'getNumberOfDistinctCampaigns',
+ );
+
+ $result = array();
+ foreach ($propertyToAccessorMapping as $property => $method) {
+ $result[$property] = $this->getNumericValue('Referers.' . $method, $date);
+ }
+ return $result;
+ }
+
+ /**
+ * Utility method that calculates evolution values for a set of current & past values
+ * and sets properties on a Piwik_View w/ HTML that displays the evolution percents.
+ *
+ * @param Piwik_View $view The view to set properties on.
+ * @param string $date The date of the current values.
+ * @param array $currentValues Array mapping view property names w/ present values.
+ * @param string $lastPeriodDate The date of the period in the past.
+ * @param array $previousValues Array mapping view property names w/ past values. Keys
+ * in this array should be the same as keys in $currentValues.
+ * @param bool $isVisits Whether the values are counting visits or something else.
+ */
+ private function addEvolutionPropertiesToView($view, $date, $currentValues, $lastPeriodDate, $previousValues)
+ {
+ foreach ($previousValues as $name => $pastValue) {
+ $currentValue = $currentValues[$name];
+ $evolutionName = $name . 'Evolution';
+
+ $view->$evolutionName = $this->getEvolutionHtml($date, $currentValue, $lastPeriodDate, $pastValue);
+ }
+ }
}
diff --git a/plugins/Referers/Referers.php b/plugins/Referers/Referers.php
index 88497f8a47..3e40f2d9a9 100644
--- a/plugins/Referers/Referers.php
+++ b/plugins/Referers/Referers.php
@@ -19,591 +19,570 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/Referers/functions.php';
*/
class Piwik_Referers extends Piwik_Plugin
{
- public $archiveProcessing;
- protected $columnToSortByBeforeTruncation;
- protected $maximumRowsInDataTableLevelZero;
- protected $maximumRowsInSubDataTable;
-
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('Referers_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
-
- return $info;
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenus',
- 'Goals.getReportsWithGoalMetrics' => 'getReportsWithGoalMetrics',
- 'API.getReportMetadata' => 'getReportMetadata',
- 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
- );
- return $hooks;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $reports = &$notification->getNotificationObject();
- $reports = array_merge($reports, array(
- array(
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Type'),
- 'module' => 'Referers',
- 'action' => 'getRefererType',
- 'dimension' => Piwik_Translate('Referers_ColumnRefererType'),
- 'constantRowsCount' => true,
- 'documentation' => Piwik_Translate('Referers_TypeReportDocumentation').'<br />'
- .'<b>'.Piwik_Translate('Referers_DirectEntry').':</b> '.Piwik_Translate('Referers_DirectEntryDocumentation').'<br />'
- .'<b>'.Piwik_Translate('Referers_SearchEngines').':</b> '.Piwik_Translate('Referers_SearchEnginesDocumentation',
- array('<br />', '&quot;'.Piwik_Translate('Referers_SubmenuSearchEngines').'&quot;')).'<br />'
- .'<b>'.Piwik_Translate('Referers_Websites').':</b> '.Piwik_Translate('Referers_WebsitesDocumentation',
- array('<br />', '&quot;'.Piwik_Translate('Referers_SubmenuWebsites').'&quot;')).'<br />'
- .'<b>'.Piwik_Translate('Referers_Campaigns').':</b> '.Piwik_Translate('Referers_CampaignsDocumentation',
- array('<br />', '&quot;'.Piwik_Translate('Referers_SubmenuCampaigns').'&quot;')),
- 'order' => 1,
- ),
- array(
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_WidgetGetAll'),
- 'module' => 'Referers',
- 'action' => 'getAll',
- 'dimension' => Piwik_Translate('Referers_Referrer'),
- 'documentation' => Piwik_Translate('Referers_AllReferersReportDocumentation', '<br />'),
- 'order' => 2,
- ),
- array(
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Keywords'),
- 'module' => 'Referers',
- 'action' => 'getKeywords',
- 'actionToLoadSubTables' => 'getSearchEnginesFromKeywordId',
- 'dimension' => Piwik_Translate('Referers_ColumnKeyword'),
- 'documentation' => Piwik_Translate('Referers_KeywordsReportDocumentation', '<br />'),
- 'order' => 3,
- ),
- array( // subtable report
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Keywords'),
- 'module' => 'Referers',
- 'action' => 'getSearchEnginesFromKeywordId',
- 'dimension' => Piwik_Translate('Referers_ColumnSearchEngine'),
- 'documentation' => Piwik_Translate('Referers_KeywordsReportDocumentation', '<br />'),
- 'isSubtableReport' => true,
- 'order' => 4
- ),
-
- array(
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Websites'),
- 'module' => 'Referers',
- 'action' => 'getWebsites',
- 'dimension' => Piwik_Translate('Referers_ColumnWebsite'),
- 'documentation' => Piwik_Translate('Referers_WebsitesReportDocumentation', '<br />'),
- 'actionToLoadSubTables' => 'getUrlsFromWebsiteId',
- 'order' => 5
- ),
- array( // subtable report
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Websites'),
- 'module' => 'Referers',
- 'action' => 'getUrlsFromWebsiteId',
- 'dimension' => Piwik_Translate('Referers_ColumnWebsitePage'),
- 'documentation' => Piwik_Translate('Referers_WebsitesReportDocumentation', '<br />'),
- 'isSubtableReport' => true,
- 'order' => 6,
- ),
-
- array(
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_SearchEngines'),
- 'module' => 'Referers',
- 'action' => 'getSearchEngines',
- 'dimension' => Piwik_Translate('Referers_ColumnSearchEngine'),
- 'documentation' => Piwik_Translate('Referers_SearchEnginesReportDocumentation', '<br />'),
- 'actionToLoadSubTables' => 'getKeywordsFromSearchEngineId',
- 'order' => 7,
- ),
- array( // subtable report
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_SearchEngines'),
- 'module' => 'Referers',
- 'action' => 'getKeywordsFromSearchEngineId',
- 'dimension' => Piwik_Translate('Referers_ColumnKeyword'),
- 'documentation' => Piwik_Translate('Referers_SearchEnginesReportDocumentation', '<br />'),
- 'isSubtableReport' => true,
- 'order' => 8,
- ),
-
- array(
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Campaigns'),
- 'module' => 'Referers',
- 'action' => 'getCampaigns',
- 'dimension' => Piwik_Translate('Referers_ColumnCampaign'),
- 'documentation' => Piwik_Translate('Referers_CampaignsReportDocumentation',
- array('<br />', '<a href="http://piwik.org/docs/tracking-campaigns/" target="_blank">', '</a>')),
- 'actionToLoadSubTables' => 'getKeywordsFromCampaignId',
- 'order' => 9,
- ),
- array( // subtable report
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Campaigns'),
- 'module' => 'Referers',
- 'action' => 'getKeywordsFromCampaignId',
- 'dimension' => Piwik_Translate('Referers_ColumnKeyword'),
- 'documentation' => Piwik_Translate('Referers_CampaignsReportDocumentation',
- array('<br />', '<a href="http://piwik.org/docs/tracking-campaigns/" target="_blank">', '</a>')),
- 'isSubtableReport' => true,
- 'order' => 10,
- ),
- array(
- 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Socials'),
- 'module' => 'Referers',
- 'action' => 'getSocials',
- 'actionToLoadSubTables' => 'getUrlsForSocial',
- 'dimension' => Piwik_Translate('Referers_ColumnSocial'),
- 'documentation' => Piwik_Translate('Referers_WebsitesReportDocumentation', '<br />'),
- 'order' => 11,
- ),
- ));
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getSegmentsMetadata($notification)
- {
- $segments =& $notification->getNotificationObject();
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Referers_Referers',
- 'name' => 'Referers_ColumnRefererType',
- 'segment' => 'referrerType',
- 'acceptedValues' => 'direct, search, website, campaign',
- 'sqlSegment' => 'log_visit.referer_type',
- 'sqlFilter' => 'Piwik_getRefererTypeFromShortName',
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Referers_Referers',
- 'name' => 'Referers_ColumnKeyword',
- 'segment' => 'referrerKeyword',
- 'acceptedValues' => 'Encoded%20Keyword, keyword',
- 'sqlSegment' => 'log_visit.referer_keyword',
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Referers_Referers',
- 'name' => 'Referers_RefererName',
- 'segment' => 'referrerName',
- 'acceptedValues' => 'twitter.com, www.facebook.com, Bing, Google, Yahoo, CampaignName',
- 'sqlSegment' => 'log_visit.referer_name',
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Referers_Referers',
- 'name' => 'Live_Referrer_URL',
- 'acceptedValues' => 'http%3A%2F%2Fwww.example.org%2Freferer-page.htm',
- 'segment' => 'referrerUrl',
- 'sqlSegment' => 'log_visit.referer_url',
- );
- }
-
- /**
- * Adds Referer widgets
- */
- function addWidgets()
- {
- Piwik_AddWidget( 'Referers_Referers', 'Referers_WidgetKeywords', 'Referers', 'getKeywords');
- Piwik_AddWidget( 'Referers_Referers', 'Referers_WidgetExternalWebsites', 'Referers', 'getWebsites');
- Piwik_AddWidget( 'Referers_Referers', 'Referers_WidgetSocials', 'Referers', 'getSocials');
- Piwik_AddWidget( 'Referers_Referers', 'Referers_WidgetSearchEngines', 'Referers', 'getSearchEngines');
- Piwik_AddWidget( 'Referers_Referers', 'Referers_WidgetCampaigns', 'Referers', 'getCampaigns');
- Piwik_AddWidget( 'Referers_Referers', 'Referers_WidgetOverview', 'Referers', 'getRefererType');
- Piwik_AddWidget( 'Referers_Referers', 'Referers_WidgetGetAll', 'Referers', 'getAll');
- if(Piwik_Archive::isSegmentationEnabled())
- {
- Piwik_AddWidget( 'SEO', 'Referers_WidgetTopKeywordsForPages', 'Referers', 'getKeywordsForPage');
- }
- }
-
- /**
- * Adds Web Analytics menus
- */
- function addMenus()
- {
- Piwik_AddMenu('Referers_Referers', '', array('module' => 'Referers', 'action' => 'index'), true, 20);
- Piwik_AddMenu('Referers_Referers', 'Referers_SubmenuOverview', array('module' => 'Referers', 'action' => 'index'), true, 1);
- Piwik_AddMenu('Referers_Referers', 'Referers_SubmenuSearchEngines', array('module' => 'Referers', 'action' => 'getSearchEnginesAndKeywords'), true, 2);
- Piwik_AddMenu('Referers_Referers', 'Referers_SubmenuWebsites', array('module' => 'Referers', 'action' => 'indexWebsites'), true, 3);
- Piwik_AddMenu('Referers_Referers', 'Referers_SubmenuCampaigns', array('module' => 'Referers', 'action' => 'indexCampaigns'), true, 4);
- }
-
- /**
- * Adds Goal dimensions, so that the dimensions are displayed in the UI Goal Overview page
- *
- * @param Piwik_Event_Notification $notification notification object
- * @return void
- */
- function getReportsWithGoalMetrics( $notification )
- {
- $dimensions =& $notification->getNotificationObject();
- $dimensions = array_merge($dimensions, array(
- array( 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Keywords'),
- 'module' => 'Referers',
- 'action' => 'getKeywords',
- ),
- array( 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_SearchEngines'),
- 'module' => 'Referers',
- 'action' => 'getSearchEngines',
- ),
- array( 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Websites'),
- 'module' => 'Referers',
- 'action' => 'getWebsites',
- ),
- array( 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Campaigns'),
- 'module' => 'Referers',
- 'action' => 'getCampaigns',
- ),
- array( 'category' => Piwik_Translate('Referers_Referers'),
- 'name' => Piwik_Translate('Referers_Type'),
- 'module' => 'Referers',
- 'action' => 'getRefererType',
- ),
- ));
- }
-
- function __construct()
- {
- $this->columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
- $this->maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_referers'];
- $this->maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_referers'];
- }
-
- /**
- * Period archiving: sums up daily stats and sums report tables,
- * making sure that tables are still truncated.
- *
- * @param Piwik_Event_Notification $notification notification object
- * @return void
- */
- function archivePeriod( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = array(
- 'Referers_type',
- 'Referers_keywordBySearchEngine',
- 'Referers_searchEngineByKeyword',
- 'Referers_keywordByCampaign',
- 'Referers_urlByWebsite',
- );
- $nameToCount = $archiveProcessing->archiveDataTable($dataTableToSum, null, $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation);
-
- $mappingFromArchiveName = array(
- 'Referers_distinctSearchEngines' =>
- array( 'typeCountToUse' => 'level0',
- 'nameTableToUse' => 'Referers_keywordBySearchEngine',
- ),
- 'Referers_distinctKeywords' =>
- array( 'typeCountToUse' => 'level0',
- 'nameTableToUse' => 'Referers_searchEngineByKeyword',
- ),
- 'Referers_distinctCampaigns' =>
- array( 'typeCountToUse' => 'level0',
- 'nameTableToUse' => 'Referers_keywordByCampaign',
- ),
- 'Referers_distinctWebsites' =>
- array( 'typeCountToUse' => 'level0',
- 'nameTableToUse' => 'Referers_urlByWebsite',
- ),
- 'Referers_distinctWebsitesUrls' =>
- array( 'typeCountToUse' => 'recursive',
- 'nameTableToUse' => 'Referers_urlByWebsite',
- ),
- );
-
- foreach($mappingFromArchiveName as $name => $infoMapping)
- {
- $typeCountToUse = $infoMapping['typeCountToUse'];
- $nameTableToUse = $infoMapping['nameTableToUse'];
-
- if($typeCountToUse == 'recursive')
- {
-
- $countValue = $nameToCount[$nameTableToUse]['recursive']
- - $nameToCount[$nameTableToUse]['level0'];
- }
- else
- {
- $countValue = $nameToCount[$nameTableToUse]['level0'];
- }
- $archiveProcessing->insertNumericRecord($name, $countValue);
- }
- }
-
- const LABEL_KEYWORD_NOT_DEFINED = "";
-
- static public function getKeywordNotDefinedString()
- {
- return Piwik_Translate( 'General_NotDefined', Piwik_Translate('Referers_ColumnKeyword'));
- }
- static public function getCleanKeyword($label)
- {
- return $label == Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED
- ? self::getKeywordNotDefinedString()
- : $label;
- }
-
- /**
- * Hooks on daily archive to trigger various log processing
- *
- * @param Piwik_Event_Notification $notification notification object
- * @return void
- */
- public function archiveDay( $notification )
- {
- /**
- * @var Piwik_ArchiveProcessing_Day
- */
- $this->archiveProcessing = $notification->getNotificationObject();
- if(!$this->archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveDayAggregateVisits($this->archiveProcessing);
- $this->archiveDayAggregateGoals($this->archiveProcessing);
- Piwik_PostEvent('Referers.archiveDay', $this);
- $this->archiveDayRecordInDatabase($this->archiveProcessing);
- $this->cleanup();
- }
-
- protected function cleanup()
- {
- destroy($this->interestBySearchEngine);
- destroy($this->interestByKeyword);
- destroy($this->interestBySearchEngineAndKeyword);
- destroy($this->interestByKeywordAndSearchEngine);
- destroy($this->interestByWebsite);
- destroy($this->interestByWebsiteAndUrl);
- destroy($this->interestByCampaignAndKeyword);
- destroy($this->interestByCampaign);
- destroy($this->interestByType);
- destroy($this->distinctUrls);
- }
-
- /**
- * Daily archive: processes all Referers reports, eg. Visits by Keyword,
- * Visits by websites, etc.
- *
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @throws Exception
- * @return void
- */
- protected function archiveDayAggregateVisits(Piwik_ArchiveProcessing_Day $archiveProcessing)
- {
- $dimension = array("referer_type", "referer_name", "referer_keyword", "referer_url");
- $query = $archiveProcessing->queryVisitsByDimension($dimension);
-
- $this->interestBySearchEngine =
- $this->interestByKeyword =
- $this->interestBySearchEngineAndKeyword =
- $this->interestByKeywordAndSearchEngine =
- $this->interestByWebsite =
- $this->interestByWebsiteAndUrl =
- $this->interestByCampaignAndKeyword =
- $this->interestByCampaign =
- $this->interestByType =
- $this->distinctUrls = array();
- while($row = $query->fetch() )
- {
- if(empty($row['referer_type']))
- {
- $row['referer_type'] = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
- }
- else
- {
- switch($row['referer_type'])
- {
- case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
- if(empty($row['referer_keyword']))
- {
- $row['referer_keyword'] = self::LABEL_KEYWORD_NOT_DEFINED;
- }
- if(!isset($this->interestBySearchEngine[$row['referer_name']])) $this->interestBySearchEngine[$row['referer_name']]= $archiveProcessing->getNewInterestRow();
- if(!isset($this->interestByKeyword[$row['referer_keyword']])) $this->interestByKeyword[$row['referer_keyword']]= $archiveProcessing->getNewInterestRow();
- if(!isset($this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']])) $this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']]= $archiveProcessing->getNewInterestRow();
- if(!isset($this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']])) $this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']]= $archiveProcessing->getNewInterestRow();
-
- $archiveProcessing->updateInterestStats( $row, $this->interestBySearchEngine[$row['referer_name']]);
- $archiveProcessing->updateInterestStats( $row, $this->interestByKeyword[$row['referer_keyword']]);
- $archiveProcessing->updateInterestStats( $row, $this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']]);
- $archiveProcessing->updateInterestStats( $row, $this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']]);
- break;
-
- case Piwik_Common::REFERER_TYPE_WEBSITE:
-
- if(!isset($this->interestByWebsite[$row['referer_name']])) $this->interestByWebsite[$row['referer_name']]= $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats( $row, $this->interestByWebsite[$row['referer_name']]);
-
- if(!isset($this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']])) $this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']]= $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats( $row, $this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']]);
-
- if(!isset($this->distinctUrls[$row['referer_url']]))
- {
- $this->distinctUrls[$row['referer_url']] = true;
- }
- break;
-
- case Piwik_Common::REFERER_TYPE_CAMPAIGN:
- if(!empty($row['referer_keyword']))
- {
- if(!isset($this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']])) $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']]= $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats( $row, $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']]);
- }
- if(!isset($this->interestByCampaign[$row['referer_name']])) $this->interestByCampaign[$row['referer_name']]= $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats( $row, $this->interestByCampaign[$row['referer_name']]);
- break;
-
- case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
- // direct entry are aggregated below in $this->interestByType array
- break;
-
- default:
- throw new Exception("Non expected referer_type = " . $row['referer_type']);
- break;
- }
- }
- if(!isset($this->interestByType[$row['referer_type']] )) $this->interestByType[$row['referer_type']] = $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats($row, $this->interestByType[$row['referer_type']]);
- }
- }
-
- /**
- * Daily Goal archiving: processes reports of Goal conversions by Keyword,
- * Goal conversions by Referer Websites, etc.
- *
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @return void
- */
- protected function archiveDayAggregateGoals($archiveProcessing)
- {
- $query = $archiveProcessing->queryConversionsByDimension(array("referer_type","referer_name","referer_keyword"));
-
- if($query === false) return;
- while($row = $query->fetch() )
- {
- if(empty($row['referer_type']))
- {
- $row['referer_type'] = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
- }
- else
- {
- switch($row['referer_type'])
- {
- case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
- if(empty($row['referer_keyword']))
- {
- $row['referer_keyword'] = self::LABEL_KEYWORD_NOT_DEFINED;
- }
- if(!isset($this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- if(!isset($this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
-
- $archiveProcessing->updateGoalStats( $row, $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- $archiveProcessing->updateGoalStats( $row, $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- break;
-
- case Piwik_Common::REFERER_TYPE_WEBSITE:
- if(!isset($this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- $archiveProcessing->updateGoalStats( $row, $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- break;
-
- case Piwik_Common::REFERER_TYPE_CAMPAIGN:
- if(!empty($row['referer_keyword']))
- {
- if(!isset($this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- $archiveProcessing->updateGoalStats( $row, $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- }
- if(!isset($this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- $archiveProcessing->updateGoalStats( $row, $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- break;
-
- case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
- // Direct entry, no sub dimension
- break;
-
- default:
- // The referer type is user submitted for goal conversions, we ignore any malformed value
- // Continue to the next while iteration
- continue 2;
- break;
- }
- }
- if(!isset($this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] )) $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- $archiveProcessing->updateGoalStats($row, $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- }
-
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByType);
- $archiveProcessing->enrichConversionsByLabelArray($this->interestBySearchEngine);
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByKeyword);
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByWebsite);
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByCampaign);
- $archiveProcessing->enrichConversionsByLabelArrayHasTwoLevels($this->interestByCampaignAndKeyword);
- }
-
- /**
- * Records the daily stats (numeric or datatable blob) into the archive tables.
- *
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @return void
- */
- protected function archiveDayRecordInDatabase($archiveProcessing)
- {
- $numericRecords = array(
- 'Referers_distinctSearchEngines' => count($this->interestBySearchEngineAndKeyword),
- 'Referers_distinctKeywords' => count($this->interestByKeywordAndSearchEngine),
- 'Referers_distinctCampaigns' => count($this->interestByCampaign),
- 'Referers_distinctWebsites' => count($this->interestByWebsite),
- 'Referers_distinctWebsitesUrls' => count($this->distinctUrls),
- );
-
- foreach($numericRecords as $name => $value)
- {
- $archiveProcessing->insertNumericRecord($name, $value);
- }
-
- $dataTable = $archiveProcessing->getDataTableSerialized($this->interestByType);
- $archiveProcessing->insertBlobRecord('Referers_type', $dataTable);
- destroy($dataTable);
-
- $blobRecords = array(
- 'Referers_keywordBySearchEngine' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestBySearchEngineAndKeyword, $this->interestBySearchEngine),
- 'Referers_searchEngineByKeyword' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByKeywordAndSearchEngine, $this->interestByKeyword),
- 'Referers_keywordByCampaign' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByCampaignAndKeyword, $this->interestByCampaign),
- 'Referers_urlByWebsite' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByWebsiteAndUrl, $this->interestByWebsite),
- );
- foreach($blobRecords as $recordName => $table )
- {
- $blob = $table->getSerialized($this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation);
- $archiveProcessing->insertBlobRecord($recordName, $blob);
- destroy($table);
- }
- }
+ public $archiveProcessing;
+ protected $columnToSortByBeforeTruncation;
+ protected $maximumRowsInDataTableLevelZero;
+ protected $maximumRowsInSubDataTable;
+
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('Referers_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenus',
+ 'Goals.getReportsWithGoalMetrics' => 'getReportsWithGoalMetrics',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ );
+ return $hooks;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $reports = & $notification->getNotificationObject();
+ $reports = array_merge($reports, array(
+ array(
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Type'),
+ 'module' => 'Referers',
+ 'action' => 'getRefererType',
+ 'dimension' => Piwik_Translate('Referers_ColumnRefererType'),
+ 'constantRowsCount' => true,
+ 'documentation' => Piwik_Translate('Referers_TypeReportDocumentation') . '<br />'
+ . '<b>' . Piwik_Translate('Referers_DirectEntry') . ':</b> ' . Piwik_Translate('Referers_DirectEntryDocumentation') . '<br />'
+ . '<b>' . Piwik_Translate('Referers_SearchEngines') . ':</b> ' . Piwik_Translate('Referers_SearchEnginesDocumentation',
+ array('<br />', '&quot;' . Piwik_Translate('Referers_SubmenuSearchEngines') . '&quot;')) . '<br />'
+ . '<b>' . Piwik_Translate('Referers_Websites') . ':</b> ' . Piwik_Translate('Referers_WebsitesDocumentation',
+ array('<br />', '&quot;' . Piwik_Translate('Referers_SubmenuWebsites') . '&quot;')) . '<br />'
+ . '<b>' . Piwik_Translate('Referers_Campaigns') . ':</b> ' . Piwik_Translate('Referers_CampaignsDocumentation',
+ array('<br />', '&quot;' . Piwik_Translate('Referers_SubmenuCampaigns') . '&quot;')),
+ 'order' => 1,
+ ),
+ array(
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_WidgetGetAll'),
+ 'module' => 'Referers',
+ 'action' => 'getAll',
+ 'dimension' => Piwik_Translate('Referers_Referrer'),
+ 'documentation' => Piwik_Translate('Referers_AllReferersReportDocumentation', '<br />'),
+ 'order' => 2,
+ ),
+ array(
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Keywords'),
+ 'module' => 'Referers',
+ 'action' => 'getKeywords',
+ 'actionToLoadSubTables' => 'getSearchEnginesFromKeywordId',
+ 'dimension' => Piwik_Translate('Referers_ColumnKeyword'),
+ 'documentation' => Piwik_Translate('Referers_KeywordsReportDocumentation', '<br />'),
+ 'order' => 3,
+ ),
+ array( // subtable report
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Keywords'),
+ 'module' => 'Referers',
+ 'action' => 'getSearchEnginesFromKeywordId',
+ 'dimension' => Piwik_Translate('Referers_ColumnSearchEngine'),
+ 'documentation' => Piwik_Translate('Referers_KeywordsReportDocumentation', '<br />'),
+ 'isSubtableReport' => true,
+ 'order' => 4
+ ),
+
+ array(
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Websites'),
+ 'module' => 'Referers',
+ 'action' => 'getWebsites',
+ 'dimension' => Piwik_Translate('Referers_ColumnWebsite'),
+ 'documentation' => Piwik_Translate('Referers_WebsitesReportDocumentation', '<br />'),
+ 'actionToLoadSubTables' => 'getUrlsFromWebsiteId',
+ 'order' => 5
+ ),
+ array( // subtable report
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Websites'),
+ 'module' => 'Referers',
+ 'action' => 'getUrlsFromWebsiteId',
+ 'dimension' => Piwik_Translate('Referers_ColumnWebsitePage'),
+ 'documentation' => Piwik_Translate('Referers_WebsitesReportDocumentation', '<br />'),
+ 'isSubtableReport' => true,
+ 'order' => 6,
+ ),
+
+ array(
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_SearchEngines'),
+ 'module' => 'Referers',
+ 'action' => 'getSearchEngines',
+ 'dimension' => Piwik_Translate('Referers_ColumnSearchEngine'),
+ 'documentation' => Piwik_Translate('Referers_SearchEnginesReportDocumentation', '<br />'),
+ 'actionToLoadSubTables' => 'getKeywordsFromSearchEngineId',
+ 'order' => 7,
+ ),
+ array( // subtable report
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_SearchEngines'),
+ 'module' => 'Referers',
+ 'action' => 'getKeywordsFromSearchEngineId',
+ 'dimension' => Piwik_Translate('Referers_ColumnKeyword'),
+ 'documentation' => Piwik_Translate('Referers_SearchEnginesReportDocumentation', '<br />'),
+ 'isSubtableReport' => true,
+ 'order' => 8,
+ ),
+
+ array(
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Campaigns'),
+ 'module' => 'Referers',
+ 'action' => 'getCampaigns',
+ 'dimension' => Piwik_Translate('Referers_ColumnCampaign'),
+ 'documentation' => Piwik_Translate('Referers_CampaignsReportDocumentation',
+ array('<br />', '<a href="http://piwik.org/docs/tracking-campaigns/" target="_blank">', '</a>')),
+ 'actionToLoadSubTables' => 'getKeywordsFromCampaignId',
+ 'order' => 9,
+ ),
+ array( // subtable report
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Campaigns'),
+ 'module' => 'Referers',
+ 'action' => 'getKeywordsFromCampaignId',
+ 'dimension' => Piwik_Translate('Referers_ColumnKeyword'),
+ 'documentation' => Piwik_Translate('Referers_CampaignsReportDocumentation',
+ array('<br />', '<a href="http://piwik.org/docs/tracking-campaigns/" target="_blank">', '</a>')),
+ 'isSubtableReport' => true,
+ 'order' => 10,
+ ),
+ array(
+ 'category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Socials'),
+ 'module' => 'Referers',
+ 'action' => 'getSocials',
+ 'actionToLoadSubTables' => 'getUrlsForSocial',
+ 'dimension' => Piwik_Translate('Referers_ColumnSocial'),
+ 'documentation' => Piwik_Translate('Referers_WebsitesReportDocumentation', '<br />'),
+ 'order' => 11,
+ ),
+ ));
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ $segments =& $notification->getNotificationObject();
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Referers_Referers',
+ 'name' => 'Referers_ColumnRefererType',
+ 'segment' => 'referrerType',
+ 'acceptedValues' => 'direct, search, website, campaign',
+ 'sqlSegment' => 'log_visit.referer_type',
+ 'sqlFilter' => 'Piwik_getRefererTypeFromShortName',
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Referers_Referers',
+ 'name' => 'Referers_ColumnKeyword',
+ 'segment' => 'referrerKeyword',
+ 'acceptedValues' => 'Encoded%20Keyword, keyword',
+ 'sqlSegment' => 'log_visit.referer_keyword',
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Referers_Referers',
+ 'name' => 'Referers_RefererName',
+ 'segment' => 'referrerName',
+ 'acceptedValues' => 'twitter.com, www.facebook.com, Bing, Google, Yahoo, CampaignName',
+ 'sqlSegment' => 'log_visit.referer_name',
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Referers_Referers',
+ 'name' => 'Live_Referrer_URL',
+ 'acceptedValues' => 'http%3A%2F%2Fwww.example.org%2Freferer-page.htm',
+ 'segment' => 'referrerUrl',
+ 'sqlSegment' => 'log_visit.referer_url',
+ );
+ }
+
+ /**
+ * Adds Referer widgets
+ */
+ function addWidgets()
+ {
+ Piwik_AddWidget('Referers_Referers', 'Referers_WidgetKeywords', 'Referers', 'getKeywords');
+ Piwik_AddWidget('Referers_Referers', 'Referers_WidgetExternalWebsites', 'Referers', 'getWebsites');
+ Piwik_AddWidget('Referers_Referers', 'Referers_WidgetSocials', 'Referers', 'getSocials');
+ Piwik_AddWidget('Referers_Referers', 'Referers_WidgetSearchEngines', 'Referers', 'getSearchEngines');
+ Piwik_AddWidget('Referers_Referers', 'Referers_WidgetCampaigns', 'Referers', 'getCampaigns');
+ Piwik_AddWidget('Referers_Referers', 'Referers_WidgetOverview', 'Referers', 'getRefererType');
+ Piwik_AddWidget('Referers_Referers', 'Referers_WidgetGetAll', 'Referers', 'getAll');
+ if (Piwik_Archive::isSegmentationEnabled()) {
+ Piwik_AddWidget('SEO', 'Referers_WidgetTopKeywordsForPages', 'Referers', 'getKeywordsForPage');
+ }
+ }
+
+ /**
+ * Adds Web Analytics menus
+ */
+ function addMenus()
+ {
+ Piwik_AddMenu('Referers_Referers', '', array('module' => 'Referers', 'action' => 'index'), true, 20);
+ Piwik_AddMenu('Referers_Referers', 'Referers_SubmenuOverview', array('module' => 'Referers', 'action' => 'index'), true, 1);
+ Piwik_AddMenu('Referers_Referers', 'Referers_SubmenuSearchEngines', array('module' => 'Referers', 'action' => 'getSearchEnginesAndKeywords'), true, 2);
+ Piwik_AddMenu('Referers_Referers', 'Referers_SubmenuWebsites', array('module' => 'Referers', 'action' => 'indexWebsites'), true, 3);
+ Piwik_AddMenu('Referers_Referers', 'Referers_SubmenuCampaigns', array('module' => 'Referers', 'action' => 'indexCampaigns'), true, 4);
+ }
+
+ /**
+ * Adds Goal dimensions, so that the dimensions are displayed in the UI Goal Overview page
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ * @return void
+ */
+ function getReportsWithGoalMetrics($notification)
+ {
+ $dimensions =& $notification->getNotificationObject();
+ $dimensions = array_merge($dimensions, array(
+ array('category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Keywords'),
+ 'module' => 'Referers',
+ 'action' => 'getKeywords',
+ ),
+ array('category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_SearchEngines'),
+ 'module' => 'Referers',
+ 'action' => 'getSearchEngines',
+ ),
+ array('category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Websites'),
+ 'module' => 'Referers',
+ 'action' => 'getWebsites',
+ ),
+ array('category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Campaigns'),
+ 'module' => 'Referers',
+ 'action' => 'getCampaigns',
+ ),
+ array('category' => Piwik_Translate('Referers_Referers'),
+ 'name' => Piwik_Translate('Referers_Type'),
+ 'module' => 'Referers',
+ 'action' => 'getRefererType',
+ ),
+ ));
+ }
+
+ function __construct()
+ {
+ $this->columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
+ $this->maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_referers'];
+ $this->maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_referers'];
+ }
+
+ /**
+ * Period archiving: sums up daily stats and sums report tables,
+ * making sure that tables are still truncated.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ * @return void
+ */
+ function archivePeriod($notification)
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $dataTableToSum = array(
+ 'Referers_type',
+ 'Referers_keywordBySearchEngine',
+ 'Referers_searchEngineByKeyword',
+ 'Referers_keywordByCampaign',
+ 'Referers_urlByWebsite',
+ );
+ $nameToCount = $archiveProcessing->archiveDataTable($dataTableToSum, null, $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation);
+
+ $mappingFromArchiveName = array(
+ 'Referers_distinctSearchEngines' =>
+ array('typeCountToUse' => 'level0',
+ 'nameTableToUse' => 'Referers_keywordBySearchEngine',
+ ),
+ 'Referers_distinctKeywords' =>
+ array('typeCountToUse' => 'level0',
+ 'nameTableToUse' => 'Referers_searchEngineByKeyword',
+ ),
+ 'Referers_distinctCampaigns' =>
+ array('typeCountToUse' => 'level0',
+ 'nameTableToUse' => 'Referers_keywordByCampaign',
+ ),
+ 'Referers_distinctWebsites' =>
+ array('typeCountToUse' => 'level0',
+ 'nameTableToUse' => 'Referers_urlByWebsite',
+ ),
+ 'Referers_distinctWebsitesUrls' =>
+ array('typeCountToUse' => 'recursive',
+ 'nameTableToUse' => 'Referers_urlByWebsite',
+ ),
+ );
+
+ foreach ($mappingFromArchiveName as $name => $infoMapping) {
+ $typeCountToUse = $infoMapping['typeCountToUse'];
+ $nameTableToUse = $infoMapping['nameTableToUse'];
+
+ if ($typeCountToUse == 'recursive') {
+
+ $countValue = $nameToCount[$nameTableToUse]['recursive']
+ - $nameToCount[$nameTableToUse]['level0'];
+ } else {
+ $countValue = $nameToCount[$nameTableToUse]['level0'];
+ }
+ $archiveProcessing->insertNumericRecord($name, $countValue);
+ }
+ }
+
+ const LABEL_KEYWORD_NOT_DEFINED = "";
+
+ static public function getKeywordNotDefinedString()
+ {
+ return Piwik_Translate('General_NotDefined', Piwik_Translate('Referers_ColumnKeyword'));
+ }
+
+ static public function getCleanKeyword($label)
+ {
+ return $label == Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED
+ ? self::getKeywordNotDefinedString()
+ : $label;
+ }
+
+ /**
+ * Hooks on daily archive to trigger various log processing
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ * @return void
+ */
+ public function archiveDay($notification)
+ {
+ /**
+ * @var Piwik_ArchiveProcessing_Day
+ */
+ $this->archiveProcessing = $notification->getNotificationObject();
+ if (!$this->archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $this->archiveDayAggregateVisits($this->archiveProcessing);
+ $this->archiveDayAggregateGoals($this->archiveProcessing);
+ Piwik_PostEvent('Referers.archiveDay', $this);
+ $this->archiveDayRecordInDatabase($this->archiveProcessing);
+ $this->cleanup();
+ }
+
+ protected function cleanup()
+ {
+ destroy($this->interestBySearchEngine);
+ destroy($this->interestByKeyword);
+ destroy($this->interestBySearchEngineAndKeyword);
+ destroy($this->interestByKeywordAndSearchEngine);
+ destroy($this->interestByWebsite);
+ destroy($this->interestByWebsiteAndUrl);
+ destroy($this->interestByCampaignAndKeyword);
+ destroy($this->interestByCampaign);
+ destroy($this->interestByType);
+ destroy($this->distinctUrls);
+ }
+
+ /**
+ * Daily archive: processes all Referers reports, eg. Visits by Keyword,
+ * Visits by websites, etc.
+ *
+ * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @throws Exception
+ * @return void
+ */
+ protected function archiveDayAggregateVisits(Piwik_ArchiveProcessing_Day $archiveProcessing)
+ {
+ $dimension = array("referer_type", "referer_name", "referer_keyword", "referer_url");
+ $query = $archiveProcessing->queryVisitsByDimension($dimension);
+
+ $this->interestBySearchEngine =
+ $this->interestByKeyword =
+ $this->interestBySearchEngineAndKeyword =
+ $this->interestByKeywordAndSearchEngine =
+ $this->interestByWebsite =
+ $this->interestByWebsiteAndUrl =
+ $this->interestByCampaignAndKeyword =
+ $this->interestByCampaign =
+ $this->interestByType =
+ $this->distinctUrls = array();
+ while ($row = $query->fetch()) {
+ if (empty($row['referer_type'])) {
+ $row['referer_type'] = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
+ } else {
+ switch ($row['referer_type']) {
+ case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
+ if (empty($row['referer_keyword'])) {
+ $row['referer_keyword'] = self::LABEL_KEYWORD_NOT_DEFINED;
+ }
+ if (!isset($this->interestBySearchEngine[$row['referer_name']])) $this->interestBySearchEngine[$row['referer_name']] = $archiveProcessing->getNewInterestRow();
+ if (!isset($this->interestByKeyword[$row['referer_keyword']])) $this->interestByKeyword[$row['referer_keyword']] = $archiveProcessing->getNewInterestRow();
+ if (!isset($this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']])) $this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']] = $archiveProcessing->getNewInterestRow();
+ if (!isset($this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']])) $this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']] = $archiveProcessing->getNewInterestRow();
+
+ $archiveProcessing->updateInterestStats($row, $this->interestBySearchEngine[$row['referer_name']]);
+ $archiveProcessing->updateInterestStats($row, $this->interestByKeyword[$row['referer_keyword']]);
+ $archiveProcessing->updateInterestStats($row, $this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']]);
+ $archiveProcessing->updateInterestStats($row, $this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']]);
+ break;
+
+ case Piwik_Common::REFERER_TYPE_WEBSITE:
+
+ if (!isset($this->interestByWebsite[$row['referer_name']])) $this->interestByWebsite[$row['referer_name']] = $archiveProcessing->getNewInterestRow();
+ $archiveProcessing->updateInterestStats($row, $this->interestByWebsite[$row['referer_name']]);
+
+ if (!isset($this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']])) $this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']] = $archiveProcessing->getNewInterestRow();
+ $archiveProcessing->updateInterestStats($row, $this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']]);
+
+ if (!isset($this->distinctUrls[$row['referer_url']])) {
+ $this->distinctUrls[$row['referer_url']] = true;
+ }
+ break;
+
+ case Piwik_Common::REFERER_TYPE_CAMPAIGN:
+ if (!empty($row['referer_keyword'])) {
+ if (!isset($this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']])) $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']] = $archiveProcessing->getNewInterestRow();
+ $archiveProcessing->updateInterestStats($row, $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']]);
+ }
+ if (!isset($this->interestByCampaign[$row['referer_name']])) $this->interestByCampaign[$row['referer_name']] = $archiveProcessing->getNewInterestRow();
+ $archiveProcessing->updateInterestStats($row, $this->interestByCampaign[$row['referer_name']]);
+ break;
+
+ case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
+ // direct entry are aggregated below in $this->interestByType array
+ break;
+
+ default:
+ throw new Exception("Non expected referer_type = " . $row['referer_type']);
+ break;
+ }
+ }
+ if (!isset($this->interestByType[$row['referer_type']])) $this->interestByType[$row['referer_type']] = $archiveProcessing->getNewInterestRow();
+ $archiveProcessing->updateInterestStats($row, $this->interestByType[$row['referer_type']]);
+ }
+ }
+
+ /**
+ * Daily Goal archiving: processes reports of Goal conversions by Keyword,
+ * Goal conversions by Referer Websites, etc.
+ *
+ * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @return void
+ */
+ protected function archiveDayAggregateGoals($archiveProcessing)
+ {
+ $query = $archiveProcessing->queryConversionsByDimension(array("referer_type", "referer_name", "referer_keyword"));
+
+ if ($query === false) return;
+ while ($row = $query->fetch()) {
+ if (empty($row['referer_type'])) {
+ $row['referer_type'] = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
+ } else {
+ switch ($row['referer_type']) {
+ case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
+ if (empty($row['referer_keyword'])) {
+ $row['referer_keyword'] = self::LABEL_KEYWORD_NOT_DEFINED;
+ }
+ if (!isset($this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+ if (!isset($this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+
+ $archiveProcessing->updateGoalStats($row, $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
+ $archiveProcessing->updateGoalStats($row, $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
+ break;
+
+ case Piwik_Common::REFERER_TYPE_WEBSITE:
+ if (!isset($this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+ $archiveProcessing->updateGoalStats($row, $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
+ break;
+
+ case Piwik_Common::REFERER_TYPE_CAMPAIGN:
+ if (!empty($row['referer_keyword'])) {
+ if (!isset($this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+ $archiveProcessing->updateGoalStats($row, $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
+ }
+ if (!isset($this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+ $archiveProcessing->updateGoalStats($row, $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
+ break;
+
+ case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
+ // Direct entry, no sub dimension
+ break;
+
+ default:
+ // The referer type is user submitted for goal conversions, we ignore any malformed value
+ // Continue to the next while iteration
+ continue 2;
+ break;
+ }
+ }
+ if (!isset($this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+ $archiveProcessing->updateGoalStats($row, $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
+ }
+
+ $archiveProcessing->enrichConversionsByLabelArray($this->interestByType);
+ $archiveProcessing->enrichConversionsByLabelArray($this->interestBySearchEngine);
+ $archiveProcessing->enrichConversionsByLabelArray($this->interestByKeyword);
+ $archiveProcessing->enrichConversionsByLabelArray($this->interestByWebsite);
+ $archiveProcessing->enrichConversionsByLabelArray($this->interestByCampaign);
+ $archiveProcessing->enrichConversionsByLabelArrayHasTwoLevels($this->interestByCampaignAndKeyword);
+ }
+
+ /**
+ * Records the daily stats (numeric or datatable blob) into the archive tables.
+ *
+ * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @return void
+ */
+ protected function archiveDayRecordInDatabase($archiveProcessing)
+ {
+ $numericRecords = array(
+ 'Referers_distinctSearchEngines' => count($this->interestBySearchEngineAndKeyword),
+ 'Referers_distinctKeywords' => count($this->interestByKeywordAndSearchEngine),
+ 'Referers_distinctCampaigns' => count($this->interestByCampaign),
+ 'Referers_distinctWebsites' => count($this->interestByWebsite),
+ 'Referers_distinctWebsitesUrls' => count($this->distinctUrls),
+ );
+
+ foreach ($numericRecords as $name => $value) {
+ $archiveProcessing->insertNumericRecord($name, $value);
+ }
+
+ $dataTable = $archiveProcessing->getDataTableSerialized($this->interestByType);
+ $archiveProcessing->insertBlobRecord('Referers_type', $dataTable);
+ destroy($dataTable);
+
+ $blobRecords = array(
+ 'Referers_keywordBySearchEngine' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestBySearchEngineAndKeyword, $this->interestBySearchEngine),
+ 'Referers_searchEngineByKeyword' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByKeywordAndSearchEngine, $this->interestByKeyword),
+ 'Referers_keywordByCampaign' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByCampaignAndKeyword, $this->interestByCampaign),
+ 'Referers_urlByWebsite' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByWebsiteAndUrl, $this->interestByWebsite),
+ );
+ foreach ($blobRecords as $recordName => $table) {
+ $blob = $table->getSerialized($this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation);
+ $archiveProcessing->insertBlobRecord($recordName, $blob);
+ destroy($table);
+ }
+ }
}
diff --git a/plugins/Referers/functions.php b/plugins/Referers/functions.php
index afa9fe4c3f..28faab9885 100644
--- a/plugins/Referers/functions.php
+++ b/plugins/Referers/functions.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Referers
*/
@@ -17,67 +17,63 @@
*/
function Piwik_getPathFromUrl($url)
{
- $path = Piwik_Common::getPathAndQueryFromUrl($url);
- if(empty($path))
- {
- return 'index';
- }
- return $path;
+ $path = Piwik_Common::getPathAndQueryFromUrl($url);
+ if (empty($path)) {
+ return 'index';
+ }
+ return $path;
}
/**
* Returns the last parts of the domain of a URL.
- *
+ *
* @param string $url e.g. http://www.facebook.com/?sdlfk=lksdfj
* @return string|false e.g. facebook.com
*/
-function Piwik_Referrers_cleanSocialUrl( $url )
+function Piwik_Referrers_cleanSocialUrl($url)
{
- $segment = '[^.:\/]+';
- preg_match('/(?:https?:\/\/)?(?:'.$segment.'\.)?('.$segment.'(?:\.'.$segment.')+)/', $url, $matches);
- return isset($matches[1]) ? $matches[1] : false;
+ $segment = '[^.:\/]+';
+ preg_match('/(?:https?:\/\/)?(?:' . $segment . '\.)?(' . $segment . '(?:\.' . $segment . ')+)/', $url, $matches);
+ return isset($matches[1]) ? $matches[1] : false;
}
/**
* Get's social network name from URL.
- *
+ *
* @param string $url
* @return string
*/
-function Piwik_Referrers_getSocialNetworkFromDomain( $url )
+function Piwik_Referrers_getSocialNetworkFromDomain($url)
{
- $domain = Piwik_Referrers_cleanSocialUrl($url);
-
- if (isset($GLOBALS['Piwik_socialUrl'][$domain]))
- {
- return $GLOBALS['Piwik_socialUrl'][$domain];
- }
- else
- {
- return Piwik_Translate('General_Unknown');
- }
+ $domain = Piwik_Referrers_cleanSocialUrl($url);
+
+ if (isset($GLOBALS['Piwik_socialUrl'][$domain])) {
+ return $GLOBALS['Piwik_socialUrl'][$domain];
+ } else {
+ return Piwik_Translate('General_Unknown');
+ }
}
/**
* Returns true if a URL belongs to a social network, false if otherwise.
- *
+ *
* @param string $url The URL to check.
* @param string|false $socialName The social network's name to check for, or false to check
* for any.
* @return bool
*/
-function Piwik_Referrers_isSocialUrl( $url, $socialName = false )
+function Piwik_Referrers_isSocialUrl($url, $socialName = false)
{
- $domain = Piwik_Referrers_cleanSocialUrl($url);
-
- if (isset($GLOBALS['Piwik_socialUrl'][$domain])
- && ($socialName === false
- || $GLOBALS['Piwik_socialUrl'][$domain] == $socialName))
- {
- return true;
- }
-
- return false;
+ $domain = Piwik_Referrers_cleanSocialUrl($url);
+
+ if (isset($GLOBALS['Piwik_socialUrl'][$domain])
+ && ($socialName === false
+ || $GLOBALS['Piwik_socialUrl'][$domain] == $socialName)
+ ) {
+ return true;
+ }
+
+ return false;
}
/* Return social network logo path by URL
@@ -88,26 +84,21 @@ function Piwik_Referrers_isSocialUrl( $url, $socialName = false )
*/
function Piwik_getSocialsLogoFromUrl($domain)
{
- if (isset($GLOBALS['Piwik_socialUrl'][$domain]))
- {
- // image names are by first domain in list, so make sure we use the first if $domain isn't it
- $firstDomain = $domain;
- foreach ($GLOBALS['Piwik_socialUrl'] as $domainKey => $name)
- {
- if ($name == $GLOBALS['Piwik_socialUrl'][$domain])
- {
- $firstDomain = $domainKey;
- break;
- }
- }
-
- $pathWithCode = 'plugins/Referers/images/socials/'.$firstDomain.'.png';
- return $pathWithCode;
- }
- else
- {
- return 'plugins/Referers/images/socials/xx.png';
- }
+ if (isset($GLOBALS['Piwik_socialUrl'][$domain])) {
+ // image names are by first domain in list, so make sure we use the first if $domain isn't it
+ $firstDomain = $domain;
+ foreach ($GLOBALS['Piwik_socialUrl'] as $domainKey => $name) {
+ if ($name == $GLOBALS['Piwik_socialUrl'][$domain]) {
+ $firstDomain = $domainKey;
+ break;
+ }
+ }
+
+ $pathWithCode = 'plugins/Referers/images/socials/' . $firstDomain . '.png';
+ return $pathWithCode;
+ } else {
+ return 'plugins/Referers/images/socials/xx.png';
+ }
}
/**
@@ -120,16 +111,13 @@ function Piwik_getSocialsLogoFromUrl($domain)
*/
function Piwik_getSearchEngineUrlFromName($name)
{
- $searchEngineNames = Piwik_Common::getSearchEngineNames();
- if(isset($searchEngineNames[$name]))
- {
- $url = 'http://'.$searchEngineNames[$name];
- }
- else
- {
- $url = 'URL unknown!';
- }
- return $url;
+ $searchEngineNames = Piwik_Common::getSearchEngineNames();
+ if (isset($searchEngineNames[$name])) {
+ $url = 'http://' . $searchEngineNames[$name];
+ } else {
+ $url = 'URL unknown!';
+ }
+ return $url;
}
/**
@@ -140,12 +128,11 @@ function Piwik_getSearchEngineUrlFromName($name)
*/
function Piwik_getSearchEngineHostFromUrl($url)
{
- $url = substr($url, strpos($url,'//') + 2);
- if(($p = strpos($url, '/')) !== false)
- {
- $url = substr($url, 0, $p);
- }
- return $url;
+ $url = substr($url, strpos($url, '//') + 2);
+ if (($p = strpos($url, '/')) !== false) {
+ $url = substr($url, 0, $p);
+ }
+ return $url;
}
/**
@@ -157,14 +144,13 @@ function Piwik_getSearchEngineHostFromUrl($url)
*/
function Piwik_getSearchEngineLogoFromUrl($url)
{
- $pathInPiwik = 'plugins/Referers/images/searchEngines/%s.png';
- $pathWithCode = sprintf($pathInPiwik, Piwik_getSearchEngineHostFromUrl($url));
- $absolutePath = PIWIK_INCLUDE_PATH . '/' . $pathWithCode;
- if(file_exists($absolutePath))
- {
- return $pathWithCode;
- }
- return sprintf($pathInPiwik, 'xx');
+ $pathInPiwik = 'plugins/Referers/images/searchEngines/%s.png';
+ $pathWithCode = sprintf($pathInPiwik, Piwik_getSearchEngineHostFromUrl($url));
+ $absolutePath = PIWIK_INCLUDE_PATH . '/' . $pathWithCode;
+ if (file_exists($absolutePath)) {
+ return $pathWithCode;
+ }
+ return sprintf($pathInPiwik, 'xx');
}
/**
@@ -175,8 +161,8 @@ function Piwik_getSearchEngineLogoFromUrl($url)
*/
function Piwik_getSearchEngineHostPathFromUrl($url)
{
- $url = substr($url, strpos($url,'//') + 2);
- return $url;
+ $url = substr($url, strpos($url, '//') + 2);
+ return $url;
}
/**
@@ -190,20 +176,18 @@ function Piwik_getSearchEngineHostPathFromUrl($url)
*/
function Piwik_getSearchEngineUrlFromUrlAndKeyword($url, $keyword)
{
- if($keyword === Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED)
- {
- return 'http://piwik.org/faq/general/#faq_144';
- }
- $searchEngineUrls = Piwik_Common::getSearchEngineUrls();
- $keyword = urlencode($keyword);
- $keyword = str_replace(urlencode('+'), urlencode(' '), $keyword);
- $path = @$searchEngineUrls[Piwik_getSearchEngineHostPathFromUrl($url)][2];
- if(empty($path))
- {
- return false;
- }
- $path = str_replace("{k}", $keyword, $path);
- return $url . (substr($url, -1) != '/' ? '/' : '') . $path;
+ if ($keyword === Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED) {
+ return 'http://piwik.org/faq/general/#faq_144';
+ }
+ $searchEngineUrls = Piwik_Common::getSearchEngineUrls();
+ $keyword = urlencode($keyword);
+ $keyword = str_replace(urlencode('+'), urlencode(' '), $keyword);
+ $path = @$searchEngineUrls[Piwik_getSearchEngineHostPathFromUrl($url)][2];
+ if (empty($path)) {
+ return false;
+ }
+ $path = str_replace("{k}", $keyword, $path);
+ return $url . (substr($url, -1) != '/' ? '/' : '') . $path;
}
/**
@@ -217,7 +201,7 @@ function Piwik_getSearchEngineUrlFromUrlAndKeyword($url, $keyword)
*/
function Piwik_getSearchEngineUrlFromKeywordAndUrl($keyword, $url)
{
- return Piwik_getSearchEngineUrlFromUrlAndKeyword($url, $keyword);
+ return Piwik_getSearchEngineUrlFromUrlAndKeyword($url, $keyword);
}
/**
@@ -228,27 +212,26 @@ function Piwik_getSearchEngineUrlFromKeywordAndUrl($keyword, $url)
*/
function Piwik_getRefererTypeLabel($label)
{
- $indexTranslation = '';
- switch($label)
- {
- case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
- $indexTranslation = 'Referers_DirectEntry';
- break;
- case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
- $indexTranslation = 'Referers_SearchEngines';
- break;
- case Piwik_Common::REFERER_TYPE_WEBSITE:
- $indexTranslation = 'Referers_Websites';
- break;
- case Piwik_Common::REFERER_TYPE_CAMPAIGN:
- $indexTranslation = 'Referers_Campaigns';
- break;
- default:
- // case of newsletter, partners, before Piwik 0.2.25
- $indexTranslation = 'General_Others';
- break;
- }
- return Piwik_Translate($indexTranslation);
+ $indexTranslation = '';
+ switch ($label) {
+ case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
+ $indexTranslation = 'Referers_DirectEntry';
+ break;
+ case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
+ $indexTranslation = 'Referers_SearchEngines';
+ break;
+ case Piwik_Common::REFERER_TYPE_WEBSITE:
+ $indexTranslation = 'Referers_Websites';
+ break;
+ case Piwik_Common::REFERER_TYPE_CAMPAIGN:
+ $indexTranslation = 'Referers_Campaigns';
+ break;
+ default:
+ // case of newsletter, partners, before Piwik 0.2.25
+ $indexTranslation = 'General_Others';
+ break;
+ }
+ return Piwik_Translate($indexTranslation);
}
/**
@@ -259,34 +242,31 @@ function Piwik_getRefererTypeLabel($label)
*/
function Piwik_getRefererTypeFromShortName($name)
{
- $map = array(
- Piwik_Common::REFERER_TYPE_SEARCH_ENGINE => 'search',
- Piwik_Common::REFERER_TYPE_WEBSITE => 'website',
- Piwik_Common::REFERER_TYPE_DIRECT_ENTRY => 'direct',
- Piwik_Common::REFERER_TYPE_CAMPAIGN => 'campaign',
- );
- if(isset($map[$name]))
- {
- return $map[$name];
- }
- if($found = array_search($name, $map))
- {
- return $found;
- }
- throw new Exception("Referrer type '$name' is not valid.");
+ $map = array(
+ Piwik_Common::REFERER_TYPE_SEARCH_ENGINE => 'search',
+ Piwik_Common::REFERER_TYPE_WEBSITE => 'website',
+ Piwik_Common::REFERER_TYPE_DIRECT_ENTRY => 'direct',
+ Piwik_Common::REFERER_TYPE_CAMPAIGN => 'campaign',
+ );
+ if (isset($map[$name])) {
+ return $map[$name];
+ }
+ if ($found = array_search($name, $map)) {
+ return $found;
+ }
+ throw new Exception("Referrer type '$name' is not valid.");
}
/**
* Returns a URL w/o the protocol type.
- *
+ *
* @param string $url
* @return string
*/
-function Piwik_Referrers_removeUrlProtocol( $url )
+function Piwik_Referrers_removeUrlProtocol($url)
{
- if (preg_match('/^[a-zA-Z_-]+:\/\//', $url, $matches))
- {
- return substr($url, strlen($matches[0]));
- }
- return $url;
+ if (preg_match('/^[a-zA-Z_-]+:\/\//', $url, $matches)) {
+ return substr($url, strlen($matches[0]));
+ }
+ return $url;
}
diff --git a/plugins/Referers/templates/Websites_SocialNetworks.tpl b/plugins/Referers/templates/Websites_SocialNetworks.tpl
index 6e964fff26..496bdb085c 100755
--- a/plugins/Referers/templates/Websites_SocialNetworks.tpl
+++ b/plugins/Referers/templates/Websites_SocialNetworks.tpl
@@ -1,9 +1,9 @@
<div id='leftcolumn'>
- <h2>{'Referers_Websites'|translate}</h2>
- {$websites}
+ <h2>{'Referers_Websites'|translate}</h2>
+ {$websites}
</div>
<div id='rightcolumn'>
- <h2>{'Referers_Socials'|translate}</h2>
- {$socials}
+ <h2>{'Referers_Socials'|translate}</h2>
+ {$socials}
</div>
diff --git a/plugins/Referers/templates/index.tpl b/plugins/Referers/templates/index.tpl
index 521209b0ea..a3ac028c86 100644
--- a/plugins/Referers/templates/index.tpl
+++ b/plugins/Referers/templates/index.tpl
@@ -2,73 +2,87 @@
<h2>{'Referers_Evolution'|translate}</h2>
{$graphEvolutionReferers}
-<br />
+<br/>
<div id='leftcolumn' style="position:relative">
- <h2>{'Referers_Type'|translate}</h2>
- <div id='leftcolumn'>
- <div class="sparkline">{sparkline src=$urlSparklineDirectEntry}
- {'Referers_TypeDirectEntries'|translate:"<strong>$visitorsFromDirectEntry</strong>"}{if !empty($visitorsFromDirectEntryPercent)}, <strong>{$visitorsFromDirectEntryPercent}%</strong> of visits{/if}{if !empty($visitorsFromDirectEntryEvolution)} {$visitorsFromDirectEntryEvolution}{/if}
- </div>
- <div class="sparkline">{sparkline src=$urlSparklineSearchEngines}
- {'Referers_TypeSearchEngines'|translate:"<strong>$visitorsFromSearchEngines</strong>"}{if !empty($visitorsFromSearchEnginesPercent)}, <strong>{$visitorsFromSearchEnginesPercent}%</strong> of visits{/if}{if !empty($visitorsFromSearchEnginesEvolution)} {$visitorsFromSearchEnginesEvolution}{/if}
- </div>
- </div>
- <div id='rightcolumn'>
- <div class="sparkline">{sparkline src=$urlSparklineWebsites}
- {'Referers_TypeWebsites'|translate:"<strong>$visitorsFromWebsites</strong>"}{if !empty($visitorsFromWebsitesPercent)}, <strong>{$visitorsFromWebsitesPercent}%</strong> of visits{/if}{if !empty($visitorsFromWebsitesEvolution)} {$visitorsFromWebsitesEvolution}{/if}
- </div>
- <div class="sparkline">{sparkline src=$urlSparklineCampaigns}
- {'Referers_TypeCampaigns'|translate:"<strong>$visitorsFromCampaigns</strong>"}{if !empty($visitorsFromCampaignsPercent)}, <strong>{$visitorsFromCampaignsPercent}%</strong> of visits{/if}{if !empty($visitorsFromCampaignsEvolution)} {$visitorsFromCampaignsEvolution}{/if}
- </div>
- </div>
-
- <div style="clear:both" />
-
- <div style="float:left">
- <br/>
- <h2>{'General_MoreDetails'|translate}&nbsp;<a href="#" class="section-toggler-link" data-section-id="distinctReferrersByType">({'General_Show_js'|translate})</a></h2>
- </div>
+ <h2>{'Referers_Type'|translate}</h2>
- <div id="distinctReferrersByType" style="display:none;float:left">
- <table cellpadding="15">
- <tr><td width="50%">
- <div class="sparkline">{sparkline src=$urlSparklineDistinctSearchEngines}
- <strong>{$numberDistinctSearchEngines}</strong> {'Referers_DistinctSearchEngines'|translate}{if !empty($numberDistinctSearchEnginesEvolution)} {$numberDistinctSearchEnginesEvolution}{/if}
- </div>
- <div class="sparkline">{sparkline src=$urlSparklineDistinctKeywords}
- <strong>{$numberDistinctKeywords}</strong> {'Referers_DistinctKeywords'|translate}{if !empty($numberDistinctKeywordsEvolution)} {$numberDistinctKeywordsEvolution}{/if}
- </div>
- </td>
- <td width="50%">
- <div class="sparkline">{sparkline src=$urlSparklineDistinctWebsites}
- <strong>{$numberDistinctWebsites}</strong> {'Referers_DistinctWebsites'|translate} {'Referers_UsingNDistinctUrls'|translate:"<strong>$numberDistinctWebsitesUrls</strong>"}{if !empty($numberDistinctWebsitesEvolution)} {$numberDistinctWebsitesEvolution}{/if}
- </div>
- <div class="sparkline">{sparkline src=$urlSparklineDistinctCampaigns}
- <strong>{$numberDistinctCampaigns}</strong> {'Referers_DistinctCampaigns'|translate}{if !empty($numberDistinctCampaignsEvolution)} {$numberDistinctCampaignsEvolution}{/if}
- </div>
- </td></tr>
- </table>
- <br/>
- </div>
-
- <p style="clear:both"/>
- <div style="float:left">{'General_View'|translate}
- <a href="javascript:broadcast.propagateAjax('module=Referers&action=getSearchEnginesAndKeywords')">{'Referers_SubmenuSearchEngines'|translate}</a>,
- <a href="javascript:broadcast.propagateAjax('module=Referers&action=indexWebsites')">{'Referers_SubmenuWebsites'|translate}</a>,
- <a href="javascript:broadcast.propagateAjax('module=Referers&action=indexCampaigns')">{'Referers_SubmenuCampaigns'|translate}</a>.
- </div>
+ <div id='leftcolumn'>
+ <div class="sparkline">{sparkline src=$urlSparklineDirectEntry}
+ {'Referers_TypeDirectEntries'|translate:"<strong>$visitorsFromDirectEntry</strong>"}{if !empty($visitorsFromDirectEntryPercent)},
+ <strong>{$visitorsFromDirectEntryPercent}%</strong>
+ of visits{/if}{if !empty($visitorsFromDirectEntryEvolution)} {$visitorsFromDirectEntryEvolution}{/if}
+ </div>
+ <div class="sparkline">{sparkline src=$urlSparklineSearchEngines}
+ {'Referers_TypeSearchEngines'|translate:"<strong>$visitorsFromSearchEngines</strong>"}{if !empty($visitorsFromSearchEnginesPercent)},
+ <strong>{$visitorsFromSearchEnginesPercent}%</strong>
+ of visits{/if}{if !empty($visitorsFromSearchEnginesEvolution)} {$visitorsFromSearchEnginesEvolution}{/if}
+ </div>
+ </div>
+ <div id='rightcolumn'>
+ <div class="sparkline">{sparkline src=$urlSparklineWebsites}
+ {'Referers_TypeWebsites'|translate:"<strong>$visitorsFromWebsites</strong>"}{if !empty($visitorsFromWebsitesPercent)},
+ <strong>{$visitorsFromWebsitesPercent}%</strong>
+ of visits{/if}{if !empty($visitorsFromWebsitesEvolution)} {$visitorsFromWebsitesEvolution}{/if}
+ </div>
+ <div class="sparkline">{sparkline src=$urlSparklineCampaigns}
+ {'Referers_TypeCampaigns'|translate:"<strong>$visitorsFromCampaigns</strong>"}{if !empty($visitorsFromCampaignsPercent)},
+ <strong>{$visitorsFromCampaignsPercent}%</strong>
+ of visits{/if}{if !empty($visitorsFromCampaignsEvolution)} {$visitorsFromCampaignsEvolution}{/if}
+ </div>
+ </div>
+
+ <div style="clear:both"/>
+
+ <div style="float:left">
+ <br/>
+
+ <h2>{'General_MoreDetails'|translate}&nbsp;<a href="#" class="section-toggler-link"
+ data-section-id="distinctReferrersByType">({'General_Show_js'|translate})</a></h2>
+ </div>
+
+ <div id="distinctReferrersByType" style="display:none;float:left">
+ <table cellpadding="15">
+ <tr>
+ <td width="50%">
+ <div class="sparkline">{sparkline src=$urlSparklineDistinctSearchEngines}
+ <strong>{$numberDistinctSearchEngines}</strong> {'Referers_DistinctSearchEngines'|translate}{if !empty($numberDistinctSearchEnginesEvolution)} {$numberDistinctSearchEnginesEvolution}{/if}
+ </div>
+ <div class="sparkline">{sparkline src=$urlSparklineDistinctKeywords}
+ <strong>{$numberDistinctKeywords}</strong> {'Referers_DistinctKeywords'|translate}{if !empty($numberDistinctKeywordsEvolution)} {$numberDistinctKeywordsEvolution}{/if}
+ </div>
+ </td>
+ <td width="50%">
+ <div class="sparkline">{sparkline src=$urlSparklineDistinctWebsites}
+ <strong>{$numberDistinctWebsites}</strong> {'Referers_DistinctWebsites'|translate} {'Referers_UsingNDistinctUrls'|translate:"<strong>$numberDistinctWebsitesUrls</strong>"}{if !empty($numberDistinctWebsitesEvolution)} {$numberDistinctWebsitesEvolution}{/if}
+ </div>
+ <div class="sparkline">{sparkline src=$urlSparklineDistinctCampaigns}
+ <strong>{$numberDistinctCampaigns}</strong> {'Referers_DistinctCampaigns'|translate}{if !empty($numberDistinctCampaignsEvolution)} {$numberDistinctCampaignsEvolution}{/if}
+ </div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+ </div>
+
+ <p style="clear:both"/>
+
+ <div style="float:left">{'General_View'|translate}
+ <a href="javascript:broadcast.propagateAjax('module=Referers&action=getSearchEnginesAndKeywords')">{'Referers_SubmenuSearchEngines'|translate}</a>,
+ <a href="javascript:broadcast.propagateAjax('module=Referers&action=indexWebsites')">{'Referers_SubmenuWebsites'|translate}</a>,
+ <a href="javascript:broadcast.propagateAjax('module=Referers&action=indexCampaigns')">{'Referers_SubmenuCampaigns'|translate}</a>.
+ </div>
</div>
<div id='rightcolumn'>
- <h2>{'Referers_DetailsByRefererType'|translate}</h2>
- {$dataTableRefererType}
+ <h2>{'Referers_DetailsByRefererType'|translate}</h2>
+ {$dataTableRefererType}
</div>
<div style="clear:both;"></div>
{if $totalVisits > 0}
-<h2>{'Referers_ReferrersOverview'|translate}</h2>
-{$referrersReportsByDimension}
+ <h2>{'Referers_ReferrersOverview'|translate}</h2>
+ {$referrersReportsByDimension}
{/if}
{include file="CoreHome/templates/sparkline_footer.tpl"}
diff --git a/plugins/Referers/templates/searchEngines_Keywords.tpl b/plugins/Referers/templates/searchEngines_Keywords.tpl
index ededce4848..4d685f681c 100644
--- a/plugins/Referers/templates/searchEngines_Keywords.tpl
+++ b/plugins/Referers/templates/searchEngines_Keywords.tpl
@@ -1,9 +1,9 @@
<div id='leftcolumn'>
- <h2>{'Referers_Keywords'|translate}</h2>
- {$keywords}
+ <h2>{'Referers_Keywords'|translate}</h2>
+ {$keywords}
</div>
<div id='rightcolumn'>
- <h2>{'Referers_SearchEngines'|translate}</h2>
- {$searchEngines}
+ <h2>{'Referers_SearchEngines'|translate}</h2>
+ {$searchEngines}
</div>
diff --git a/plugins/SEO/API.php b/plugins/SEO/API.php
index 62613f3de5..26ac00c5bd 100644
--- a/plugins/SEO/API.php
+++ b/plugins/SEO/API.php
@@ -17,92 +17,91 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/Referers/functions.php';
/**
* The SEO API lets you access a list of SEO metrics for the specified URL: Google Pagerank, Goolge/Bing indexed pages
* Alexa Rank, age of the Domain name and count of DMOZ entries.
- *
+ *
* @package Piwik_SEO
*/
-class Piwik_SEO_API
+class Piwik_SEO_API
{
- static private $instance = null;
- /**
- * @return Piwik_SEO_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Returns SEO statistics for a URL.
- *
- * @param string $url URL to request SEO stats for
- * @return Piwik_DataTable
- */
- public function getRank( $url )
- {
- Piwik::checkUserHasSomeViewAccess();
- $rank = new Piwik_SEO_RankChecker($url);
-
- $linkToMajestic = Piwik_SEO_MajesticClient::getLinkForUrl($url);
-
- $data = array(
- 'Google PageRank' => array(
- 'rank' => $rank->getPageRank(),
- 'logo' => Piwik_getSearchEngineLogoFromUrl('http://google.com'),
- 'id' => 'pagerank'
- ),
+ static private $instance = null;
+
+ /**
+ * @return Piwik_SEO_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Returns SEO statistics for a URL.
+ *
+ * @param string $url URL to request SEO stats for
+ * @return Piwik_DataTable
+ */
+ public function getRank($url)
+ {
+ Piwik::checkUserHasSomeViewAccess();
+ $rank = new Piwik_SEO_RankChecker($url);
+
+ $linkToMajestic = Piwik_SEO_MajesticClient::getLinkForUrl($url);
+
+ $data = array(
+ 'Google PageRank' => array(
+ 'rank' => $rank->getPageRank(),
+ 'logo' => Piwik_getSearchEngineLogoFromUrl('http://google.com'),
+ 'id' => 'pagerank'
+ ),
Piwik_Translate('SEO_Google_IndexedPages') => array(
'rank' => $rank->getIndexedPagesGoogle(),
'logo' => Piwik_getSearchEngineLogoFromUrl('http://google.com'),
- 'id' => 'google-index',
+ 'id' => 'google-index',
),
- Piwik_Translate('SEO_Bing_IndexedPages') => array(
+ Piwik_Translate('SEO_Bing_IndexedPages') => array(
'rank' => $rank->getIndexedPagesBing(),
'logo' => Piwik_getSearchEngineLogoFromUrl('http://bing.com'),
- 'id' => 'bing-index',
- ),
- Piwik_Translate('SEO_AlexaRank') => array(
- 'rank' => $rank->getAlexaRank(),
- 'logo' => Piwik_getSearchEngineLogoFromUrl('http://alexa.com'),
- 'id' => 'alexa',
- ),
- Piwik_Translate('SEO_DomainAge') => array(
- 'rank' => $rank->getAge(),
- 'logo' => 'plugins/SEO/images/whois.png',
- 'id' => 'domain-age',
- ),
- Piwik_Translate('SEO_ExternalBacklinks') => array(
- 'rank' => $rank->getExternalBacklinkCount(),
- 'logo' => 'plugins/SEO/images/majesticseo.png',
- 'logo_link' => $linkToMajestic,
- 'logo_tooltip' => Piwik_Translate('SEO_ViewBacklinksOnMajesticSEO'),
- 'id' => 'external-backlinks',
- ),
- Piwik_Translate('SEO_ReferrerDomains') => array(
- 'rank' => $rank->getReferrerDomainCount(),
- 'logo' => 'plugins/SEO/images/majesticseo.png',
- 'logo_link' => $linkToMajestic,
- 'logo_tooltip' => Piwik_Translate('SEO_ViewBacklinksOnMajesticSEO'),
- 'id' => 'referrer-domains',
- ),
- );
+ 'id' => 'bing-index',
+ ),
+ Piwik_Translate('SEO_AlexaRank') => array(
+ 'rank' => $rank->getAlexaRank(),
+ 'logo' => Piwik_getSearchEngineLogoFromUrl('http://alexa.com'),
+ 'id' => 'alexa',
+ ),
+ Piwik_Translate('SEO_DomainAge') => array(
+ 'rank' => $rank->getAge(),
+ 'logo' => 'plugins/SEO/images/whois.png',
+ 'id' => 'domain-age',
+ ),
+ Piwik_Translate('SEO_ExternalBacklinks') => array(
+ 'rank' => $rank->getExternalBacklinkCount(),
+ 'logo' => 'plugins/SEO/images/majesticseo.png',
+ 'logo_link' => $linkToMajestic,
+ 'logo_tooltip' => Piwik_Translate('SEO_ViewBacklinksOnMajesticSEO'),
+ 'id' => 'external-backlinks',
+ ),
+ Piwik_Translate('SEO_ReferrerDomains') => array(
+ 'rank' => $rank->getReferrerDomainCount(),
+ 'logo' => 'plugins/SEO/images/majesticseo.png',
+ 'logo_link' => $linkToMajestic,
+ 'logo_tooltip' => Piwik_Translate('SEO_ViewBacklinksOnMajesticSEO'),
+ 'id' => 'referrer-domains',
+ ),
+ );
- // Add DMOZ only if > 0 entries found
- $dmozRank = array(
- 'rank' => $rank->getDmoz(),
- 'logo' => Piwik_getSearchEngineLogoFromUrl('http://dmoz.org'),
- 'id' => 'dmoz',
- );
- if($dmozRank['rank'] > 0)
- {
- $data[Piwik_Translate('SEO_Dmoz')] = $dmozRank;
- }
+ // Add DMOZ only if > 0 entries found
+ $dmozRank = array(
+ 'rank' => $rank->getDmoz(),
+ 'logo' => Piwik_getSearchEngineLogoFromUrl('http://dmoz.org'),
+ 'id' => 'dmoz',
+ );
+ if ($dmozRank['rank'] > 0) {
+ $data[Piwik_Translate('SEO_Dmoz')] = $dmozRank;
+ }
- $dataTable = new Piwik_DataTable();
- $dataTable->addRowsFromArrayWithIndexLabel($data);
- return $dataTable;
- }
+ $dataTable = new Piwik_DataTable();
+ $dataTable->addRowsFromArrayWithIndexLabel($data);
+ return $dataTable;
+ }
}
diff --git a/plugins/SEO/Controller.php b/plugins/SEO/Controller.php
index 53a749e2d3..f0e75f8200 100644
--- a/plugins/SEO/Controller.php
+++ b/plugins/SEO/Controller.php
@@ -1,43 +1,42 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_SEO
*/
-
+
/**
* @package Piwik_SEO
*/
class Piwik_SEO_Controller extends Piwik_Controller
-{
- function getRank()
- {
- $idSite = Piwik_Common::getRequestVar('idSite');
- $site = new Piwik_Site($idSite);
+{
+ function getRank()
+ {
+ $idSite = Piwik_Common::getRequestVar('idSite');
+ $site = new Piwik_Site($idSite);
+
+ $url = urldecode(Piwik_Common::getRequestVar('url', '', 'string'));
+
+ if (!empty($url) && strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0) {
+ $url = 'http://' . $url;
+ }
+
+ if (empty($url) || !Piwik_Common::isLookLikeUrl($url)) {
+ $url = $site->getMainUrl();
+ }
+
+ $dataTable = Piwik_SEO_API::getInstance()->getRank($url);
- $url = urldecode(Piwik_Common::getRequestVar('url', '', 'string'));
-
- if(!empty($url) && strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0) {
- $url = 'http://'.$url;
- }
-
- if(empty($url) || !Piwik_Common::isLookLikeUrl($url))
- {
- $url = $site->getMainUrl();
- }
+ $view = Piwik_View::factory('index');
+ $view->urlToRank = Piwik_SEO_RankChecker::extractDomainFromUrl($url);
- $dataTable = Piwik_SEO_API::getInstance()->getRank($url);
-
- $view = Piwik_View::factory('index');
- $view->urlToRank = Piwik_SEO_RankChecker::extractDomainFromUrl($url);
-
- $renderer = Piwik_DataTable_Renderer::factory('php');
- $renderer->setSerialize(false);
- $view->ranks = $renderer->render($dataTable);
- echo $view->render();
- }
+ $renderer = Piwik_DataTable_Renderer::factory('php');
+ $renderer->setSerialize(false);
+ $view->ranks = $renderer->render($dataTable);
+ echo $view->render();
+ }
}
diff --git a/plugins/SEO/MajesticClient.php b/plugins/SEO/MajesticClient.php
index 61ab4d2d29..f6678be192 100644
--- a/plugins/SEO/MajesticClient.php
+++ b/plugins/SEO/MajesticClient.php
@@ -11,85 +11,85 @@
/**
* Client for Majestic SEO's HTTP API.
- *
+ *
* Hides the HTTP request sending logic.
*/
class Piwik_SEO_MajesticClient
{
- const API_BASE = 'http://simpleapi.majesticseo.com/sapi/';
- const API_KEY = 'ETHPYY'; // please only use this key within Piwik
-
- /**
- * Returns a URL that can be used to view all SEO data for a particular website.
- *
- * @param string $targetSiteUrl The URL of the website for whom SEO stats should be
- * accessible for.
- * @return string
- */
- public static function getLinkForUrl( $targetSiteUrl )
- {
- $domain = @parse_url($targetSiteUrl, PHP_URL_HOST);
- return "http://www.majesticseo.com/reports/site-explorer/summary/$domain?IndexDataSource=F";
- }
-
- /**
- * Returns backlink statistics including the count of backlinks and count of
- * referrer domains (domains with backlinks).
- *
- * This method issues an HTTP request and waits for it to return.
- *
- * @param string $siteDomain The domain of the website to get stats for.
- * @param int $timeout The number of seconds to wait before aborting
- * the HTTP request.
- * @return array An array containing the backlink count and referrer
- * domain count:
- * array(
- * 'backlink_count' => X,
- * 'referrer_domains_count' => Y
- * )
- * If either stat is false, either the API returned an
- * error, or the IP was blocked for this request.
- */
- public function getBacklinkStats( $siteDomain, $timeout = 300 )
- {
- $apiUrl = $this->getApiUrl($method = 'GetBacklinkStats', $args = array(
- 'items' => '1',
- 'item0' => $siteDomain
- ));
- $apiResponse = Piwik_Http::sendHttpRequest($apiUrl, $timeout);
-
- $result = array(
- 'backlink_count' => false,
- 'referrer_domains_count' => false
- );
-
- $apiResponse = Piwik_Common::json_decode($apiResponse, $assoc = true);
- if (!empty($apiResponse)
- && !empty($apiResponse['Data']))
- {
- $siteSeoStats = reset($apiResponse['Data']);
-
- if (isset($siteSeoStats['ExtBackLinks'])
- && $siteSeoStats['ExtBackLinks'] !== -1)
- {
- $result['backlink_count'] = $siteSeoStats['ExtBackLinks'];
- }
-
- if (isset($siteSeoStats['RefDomains'])
- && $siteSeoStats['RefDomains'] !== -1)
- {
- $result['referrer_domains_count'] = $siteSeoStats['RefDomains'];
- }
- }
-
- return $result;
- }
-
- private function getApiUrl( $method, $args = array() )
- {
- $args['sak'] = self::API_KEY;
-
- $queryString = http_build_query($args);
- return self::API_BASE.$method.'?'.$queryString;
- }
+ const API_BASE = 'http://simpleapi.majesticseo.com/sapi/';
+ const API_KEY = 'ETHPYY'; // please only use this key within Piwik
+
+ /**
+ * Returns a URL that can be used to view all SEO data for a particular website.
+ *
+ * @param string $targetSiteUrl The URL of the website for whom SEO stats should be
+ * accessible for.
+ * @return string
+ */
+ public static function getLinkForUrl($targetSiteUrl)
+ {
+ $domain = @parse_url($targetSiteUrl, PHP_URL_HOST);
+ return "http://www.majesticseo.com/reports/site-explorer/summary/$domain?IndexDataSource=F";
+ }
+
+ /**
+ * Returns backlink statistics including the count of backlinks and count of
+ * referrer domains (domains with backlinks).
+ *
+ * This method issues an HTTP request and waits for it to return.
+ *
+ * @param string $siteDomain The domain of the website to get stats for.
+ * @param int $timeout The number of seconds to wait before aborting
+ * the HTTP request.
+ * @return array An array containing the backlink count and referrer
+ * domain count:
+ * array(
+ * 'backlink_count' => X,
+ * 'referrer_domains_count' => Y
+ * )
+ * If either stat is false, either the API returned an
+ * error, or the IP was blocked for this request.
+ */
+ public function getBacklinkStats($siteDomain, $timeout = 300)
+ {
+ $apiUrl = $this->getApiUrl($method = 'GetBacklinkStats', $args = array(
+ 'items' => '1',
+ 'item0' => $siteDomain
+ ));
+ $apiResponse = Piwik_Http::sendHttpRequest($apiUrl, $timeout);
+
+ $result = array(
+ 'backlink_count' => false,
+ 'referrer_domains_count' => false
+ );
+
+ $apiResponse = Piwik_Common::json_decode($apiResponse, $assoc = true);
+ if (!empty($apiResponse)
+ && !empty($apiResponse['Data'])
+ ) {
+ $siteSeoStats = reset($apiResponse['Data']);
+
+ if (isset($siteSeoStats['ExtBackLinks'])
+ && $siteSeoStats['ExtBackLinks'] !== -1
+ ) {
+ $result['backlink_count'] = $siteSeoStats['ExtBackLinks'];
+ }
+
+ if (isset($siteSeoStats['RefDomains'])
+ && $siteSeoStats['RefDomains'] !== -1
+ ) {
+ $result['referrer_domains_count'] = $siteSeoStats['RefDomains'];
+ }
+ }
+
+ return $result;
+ }
+
+ private function getApiUrl($method, $args = array())
+ {
+ $args['sak'] = self::API_KEY;
+
+ $queryString = http_build_query($args);
+ return self::API_BASE . $method . '?' . $queryString;
+ }
}
diff --git a/plugins/SEO/RankChecker.php b/plugins/SEO/RankChecker.php
index da9fa0cf60..57512c4e8f 100644
--- a/plugins/SEO/RankChecker.php
+++ b/plugins/SEO/RankChecker.php
@@ -20,46 +20,46 @@
*/
class Piwik_SEO_RankChecker
{
- private $url;
- private $majesticInfo = null;
-
- public function __construct($url)
- {
- $this->url = self::extractDomainFromUrl($url);
- }
-
- /**
- * Extract domain from URL as the web services generally
- * expect only a domain name (i.e., no protocol, port, path, query, etc).
- *
- * @param string $url
- * @return string
- */
- static public function extractDomainFromUrl($url)
- {
- return preg_replace(
- array(
- '~^https?\://~si', // strip protocol
- '~[/:#?;%&].*~', // strip port, path, query, anchor, etc
- '~\.$~', // trailing period
- ),
- '', $url);
- }
-
- /**
- * Web service proxy that retrieves the content at the specified URL
- *
- * @param string $url
- * @return string
- */
- private function getPage($url)
- {
- try {
- return str_replace('&nbsp;', ' ', Piwik_Http::sendHttpRequest($url, $timeout = 10, @$_SERVER['HTTP_USER_AGENT']));
- } catch(Exception $e) {
- return '';
- }
- }
+ private $url;
+ private $majesticInfo = null;
+
+ public function __construct($url)
+ {
+ $this->url = self::extractDomainFromUrl($url);
+ }
+
+ /**
+ * Extract domain from URL as the web services generally
+ * expect only a domain name (i.e., no protocol, port, path, query, etc).
+ *
+ * @param string $url
+ * @return string
+ */
+ static public function extractDomainFromUrl($url)
+ {
+ return preg_replace(
+ array(
+ '~^https?\://~si', // strip protocol
+ '~[/:#?;%&].*~', // strip port, path, query, anchor, etc
+ '~\.$~', // trailing period
+ ),
+ '', $url);
+ }
+
+ /**
+ * Web service proxy that retrieves the content at the specified URL
+ *
+ * @param string $url
+ * @return string
+ */
+ private function getPage($url)
+ {
+ try {
+ return str_replace('&nbsp;', ' ', Piwik_Http::sendHttpRequest($url, $timeout = 10, @$_SERVER['HTTP_USER_AGENT']));
+ } catch (Exception $e) {
+ return '';
+ }
+ }
/**
* Returns the google page rank for the current url
@@ -67,16 +67,16 @@ class Piwik_SEO_RankChecker
* @return int
*/
public function getPageRank()
- {
- $chwrite = $this->CheckHash($this->HashURL($this->url));
+ {
+ $chwrite = $this->CheckHash($this->HashURL($this->url));
- $url="http://toolbarqueries.google.com/tbr?client=navclient-auto&ch=".$chwrite."&features=Rank&q=info:".$this->url."&num=100&filter=0";
- $data = $this->getPage($url);
- preg_match('#Rank_[0-9]:[0-9]:([0-9]+){1,}#si', $data, $p);
- $value = isset($p[1]) ? $p[1] : 0;
+ $url = "http://toolbarqueries.google.com/tbr?client=navclient-auto&ch=" . $chwrite . "&features=Rank&q=info:" . $this->url . "&num=100&filter=0";
+ $data = $this->getPage($url);
+ preg_match('#Rank_[0-9]:[0-9]:([0-9]+){1,}#si', $data, $p);
+ $value = isset($p[1]) ? $p[1] : 0;
- return $value;
- }
+ return $value;
+ }
/**
* Returns the alexa traffic rank for the current url
@@ -84,26 +84,26 @@ class Piwik_SEO_RankChecker
* @return int
*/
public function getAlexaRank()
- {
+ {
$xml = @simplexml_load_string($this->getPage('http://data.alexa.com/data?cli=10&url=' . urlencode($this->url)));
- return $xml ? $xml->SD->POPULARITY['TEXT'] : '';
- }
+ return $xml ? $xml->SD->POPULARITY['TEXT'] : '';
+ }
/**
* Returns the number of Dmoz.org entries for the current url
*
* @return int
*/
- public function getDmoz()
- {
+ public function getDmoz()
+ {
$url = 'http://www.dmoz.org/search?q=' . urlencode($this->url);
- $data = $this->getPage($url);
+ $data = $this->getPage($url);
preg_match('#Open Directory Sites[^\(]+\([0-9]-[0-9]+ of ([0-9]+)\)#', $data, $p);
if (!empty($p[1])) {
- return (int) $p[1];
+ return (int)$p[1];
}
return 0;
- }
+ }
/**
* Returns the number of pages google holds in it's index for the current url
@@ -111,14 +111,14 @@ class Piwik_SEO_RankChecker
* @return int
*/
public function getIndexedPagesGoogle()
- {
+ {
$url = 'http://www.google.com/search?hl=en&q=site%3A' . urlencode($this->url);
- $data = $this->getPage($url);
+ $data = $this->getPage($url);
if (preg_match('#about ([0-9\,]+) results#i', $data, $p)) {
- return (int) str_replace(',', '', $p[1]);
- }
+ return (int)str_replace(',', '', $p[1]);
+ }
return 0;
- }
+ }
/**
* Returns the number of pages bing holds in it's index for the current url
@@ -126,14 +126,14 @@ class Piwik_SEO_RankChecker
* @return int
*/
public function getIndexedPagesBing()
- {
+ {
$url = 'http://www.bing.com/search?mkt=en-US&q=site%3A' . urlencode($this->url);
- $data = $this->getPage($url);
+ $data = $this->getPage($url);
if (preg_match('#([0-9\,]+) results#i', $data, $p)) {
- return (int) str_replace(',', '', $p[1]);
- }
+ return (int)str_replace(',', '', $p[1]);
+ }
return 0;
- }
+ }
/**
* Returns the domain age for the current url
@@ -148,19 +148,19 @@ class Piwik_SEO_RankChecker
$ages = array();
- if($ageArchiveOrg > 0) {
+ if ($ageArchiveOrg > 0) {
$ages[] = $ageArchiveOrg;
}
- if($ageWhoIs > 0) {
+ if ($ageWhoIs > 0) {
$ages[] = $ageWhoIs;
}
- if($ageWhoisCom > 0) {
+ if ($ageWhoisCom > 0) {
$ages[] = $ageWhoisCom;
}
- if(count($ages) > 1) {
+ if (count($ages) > 1) {
$maxAge = min($ages);
} else {
$maxAge = array_shift($ages);
@@ -171,27 +171,27 @@ class Piwik_SEO_RankChecker
}
return false;
}
-
+
/**
* Returns the number backlinks that link to the current site.
- *
+ *
* @return int
*/
public function getExternalBacklinkCount()
{
- $majesticInfo = $this->getMajesticInfo();
- return $majesticInfo['backlink_count'];
+ $majesticInfo = $this->getMajesticInfo();
+ return $majesticInfo['backlink_count'];
}
-
+
/**
* Returns the number of referrer domains that link to the current site.
- *
+ *
* @return int
*/
public function getReferrerDomainCount()
{
- $majesticInfo = $this->getMajesticInfo();
- return $majesticInfo['referrer_domains_count'];
+ $majesticInfo = $this->getMajesticInfo();
+ return $majesticInfo['referrer_domains_count'];
}
/**
@@ -201,7 +201,7 @@ class Piwik_SEO_RankChecker
*/
protected function _getAgeArchiveOrg()
{
- $url = str_replace('www.', '', $this->url);
+ $url = str_replace('www.', '', $this->url);
$data = @$this->getPage('http://wayback.archive.org/web/*/' . urlencode($url));
preg_match('#<a href=\"([^>]*)' . preg_quote($url) . '/\">([^<]*)<\/a>#', $data, $p);
if (!empty($p[2])) {
@@ -221,8 +221,8 @@ class Piwik_SEO_RankChecker
*/
protected function _getAgeWhoIs()
{
- $url = preg_replace('/^www\./', '', $this->url);
- $url = 'http://www.who.is/whois/' . urlencode($url);
+ $url = preg_replace('/^www\./', '', $this->url);
+ $url = 'http://www.who.is/whois/' . urlencode($url);
$data = $this->getPage($url);
preg_match('#(?:Creation Date|Created On|Registered on)\.*:\s*([ \ta-z0-9\/\-:\.]+)#si', $data, $p);
if (!empty($p[1])) {
@@ -242,8 +242,8 @@ class Piwik_SEO_RankChecker
*/
protected function _getAgeWhoisCom()
{
- $url = preg_replace('/^www\./', '', $this->url);
- $url = 'http://www.whois.com/whois/' . urlencode($url);
+ $url = preg_replace('/^www\./', '', $this->url);
+ $url = 'http://www.whois.com/whois/' . urlencode($url);
$data = $this->getPage($url);
preg_match('#(?:Creation Date|Created On):\s*([ \ta-z0-9\/\-:\.]+)#si', $data, $p);
if (!empty($p[1])) {
@@ -256,114 +256,107 @@ class Piwik_SEO_RankChecker
return 0;
}
- /**
- * Convert numeric string to int
- *
- * @see getPageRank()
- *
- * @param string $Str
- * @param int $Check
- * @param int $Magic
- * @return int
- */
- private function StrToNum($Str, $Check, $Magic)
- {
- $Int32Unit = 4294967296; // 2^32
-
- $length = strlen($Str);
- for($i = 0; $i < $length; $i++)
- {
- $Check *= $Magic;
- // If the float is beyond the boundaries of integer (usually +/- 2.15e+9 = 2^31),
- // the result of converting to integer is undefined
- // refer to http://www.php.net/manual/en/language.types.integer.php
- if($Check >= $Int32Unit)
- {
- $Check = ($Check - $Int32Unit * (int) ($Check / $Int32Unit));
- //if the check less than -2^31
- $Check = ($Check < -2147483648) ? ($Check + $Int32Unit) : $Check;
- }
- $Check += ord($Str{$i});
- }
- return $Check;
- }
-
- /**
- * Generate a hash for a url
- *
- * @see getPageRank()
- *
- * @param string $String
- * @return int
- */
- private function HashURL($String)
- {
- $Check1 = $this->StrToNum($String, 0x1505, 0x21);
- $Check2 = $this->StrToNum($String, 0, 0x1003F);
-
- $Check1 >>= 2;
- $Check1 = (($Check1 >> 4) & 0x3FFFFC0 ) | ($Check1 & 0x3F);
- $Check1 = (($Check1 >> 4) & 0x3FFC00 ) | ($Check1 & 0x3FF);
- $Check1 = (($Check1 >> 4) & 0x3C000 ) | ($Check1 & 0x3FFF);
-
- $T1 = (((($Check1 & 0x3C0) << 4) | ($Check1 & 0x3C)) <<2 ) | ($Check2 & 0xF0F );
- $T2 = (((($Check1 & 0xFFFFC000) << 4) | ($Check1 & 0x3C00)) << 0xA) | ($Check2 & 0xF0F0000 );
-
- return ($T1 | $T2);
- }
-
- /**
- * Generate a checksum for the hash string
- *
- * @see getPageRank()
- *
- * @param int $Hashnum
- * @return string
- */
- private function CheckHash($Hashnum)
- {
- $CheckByte = 0;
- $Flag = 0;
-
- $HashStr = sprintf('%u', $Hashnum) ;
- $length = strlen($HashStr);
-
- for($i = $length - 1; $i >= 0; $i --)
- {
- $Re = $HashStr{$i};
- if(1 === ($Flag % 2)) {
- $Re += $Re;
- $Re = (int)($Re / 10) + ($Re % 10);
- }
- $CheckByte += $Re;
- $Flag ++;
- }
-
- $CheckByte %= 10;
- if(0 !== $CheckByte)
- {
- $CheckByte = 10 - $CheckByte;
- if(1 === ($Flag % 2) )
- {
- if(1 === ($CheckByte % 2))
- {
- $CheckByte += 9;
- }
- $CheckByte >>= 1;
- }
- }
-
- return '7'.$CheckByte.$HashStr;
- }
-
- private function getMajesticInfo()
- {
- if ($this->majesticInfo === null)
- {
- $client = new Piwik_SEO_MajesticClient();
- $this->majesticInfo = $client->getBacklinkStats($this->url);
- }
-
- return $this->majesticInfo;
- }
+ /**
+ * Convert numeric string to int
+ *
+ * @see getPageRank()
+ *
+ * @param string $Str
+ * @param int $Check
+ * @param int $Magic
+ * @return int
+ */
+ private function StrToNum($Str, $Check, $Magic)
+ {
+ $Int32Unit = 4294967296; // 2^32
+
+ $length = strlen($Str);
+ for ($i = 0; $i < $length; $i++) {
+ $Check *= $Magic;
+ // If the float is beyond the boundaries of integer (usually +/- 2.15e+9 = 2^31),
+ // the result of converting to integer is undefined
+ // refer to http://www.php.net/manual/en/language.types.integer.php
+ if ($Check >= $Int32Unit) {
+ $Check = ($Check - $Int32Unit * (int)($Check / $Int32Unit));
+ //if the check less than -2^31
+ $Check = ($Check < -2147483648) ? ($Check + $Int32Unit) : $Check;
+ }
+ $Check += ord($Str{$i});
+ }
+ return $Check;
+ }
+
+ /**
+ * Generate a hash for a url
+ *
+ * @see getPageRank()
+ *
+ * @param string $String
+ * @return int
+ */
+ private function HashURL($String)
+ {
+ $Check1 = $this->StrToNum($String, 0x1505, 0x21);
+ $Check2 = $this->StrToNum($String, 0, 0x1003F);
+
+ $Check1 >>= 2;
+ $Check1 = (($Check1 >> 4) & 0x3FFFFC0) | ($Check1 & 0x3F);
+ $Check1 = (($Check1 >> 4) & 0x3FFC00) | ($Check1 & 0x3FF);
+ $Check1 = (($Check1 >> 4) & 0x3C000) | ($Check1 & 0x3FFF);
+
+ $T1 = (((($Check1 & 0x3C0) << 4) | ($Check1 & 0x3C)) << 2) | ($Check2 & 0xF0F);
+ $T2 = (((($Check1 & 0xFFFFC000) << 4) | ($Check1 & 0x3C00)) << 0xA) | ($Check2 & 0xF0F0000);
+
+ return ($T1 | $T2);
+ }
+
+ /**
+ * Generate a checksum for the hash string
+ *
+ * @see getPageRank()
+ *
+ * @param int $Hashnum
+ * @return string
+ */
+ private function CheckHash($Hashnum)
+ {
+ $CheckByte = 0;
+ $Flag = 0;
+
+ $HashStr = sprintf('%u', $Hashnum);
+ $length = strlen($HashStr);
+
+ for ($i = $length - 1; $i >= 0; $i--) {
+ $Re = $HashStr{$i};
+ if (1 === ($Flag % 2)) {
+ $Re += $Re;
+ $Re = (int)($Re / 10) + ($Re % 10);
+ }
+ $CheckByte += $Re;
+ $Flag++;
+ }
+
+ $CheckByte %= 10;
+ if (0 !== $CheckByte) {
+ $CheckByte = 10 - $CheckByte;
+ if (1 === ($Flag % 2)) {
+ if (1 === ($CheckByte % 2)) {
+ $CheckByte += 9;
+ }
+ $CheckByte >>= 1;
+ }
+ }
+
+ return '7' . $CheckByte . $HashStr;
+ }
+
+ private function getMajesticInfo()
+ {
+ if ($this->majesticInfo === null) {
+ $client = new Piwik_SEO_MajesticClient();
+ $this->majesticInfo = $client->getBacklinkStats($this->url);
+ }
+
+ return $this->majesticInfo;
+ }
}
diff --git a/plugins/SEO/SEO.php b/plugins/SEO/SEO.php
index a7e5474506..addc8ae69e 100644
--- a/plugins/SEO/SEO.php
+++ b/plugins/SEO/SEO.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_SEO
*/
@@ -14,24 +14,24 @@
*/
class Piwik_SEO extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => 'This Plugin extracts and displays SEO metrics: Alexa web ranking, Google Pagerank, number of Indexed pages and backlinks of the currently selected website.',
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- function getListHooksRegistered()
- {
- $hooks = array( 'WidgetsList.add' => 'addWidgets' );
- return $hooks;
- }
-
- function addWidgets()
- {
- Piwik_AddWidget('SEO', 'SEO_SeoRankings', 'SEO', 'getRank');
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => 'This Plugin extracts and displays SEO metrics: Alexa web ranking, Google Pagerank, number of Indexed pages and backlinks of the currently selected website.',
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array('WidgetsList.add' => 'addWidgets');
+ return $hooks;
+ }
+
+ function addWidgets()
+ {
+ Piwik_AddWidget('SEO', 'SEO_SeoRankings', 'SEO', 'getRank');
+ }
}
diff --git a/plugins/SEO/templates/index.tpl b/plugins/SEO/templates/index.tpl
index fe1fe903d3..721ca46ee5 100644
--- a/plugins/SEO/templates/index.tpl
+++ b/plugins/SEO/templates/index.tpl
@@ -1,42 +1,46 @@
<div id='SeoRanks'>
- <script type="text/javascript" src="plugins/SEO/templates/rank.js"></script>
-
- <form method="post" style="padding: 8px;" >
- <div align="left" class="mediumtext">
- {'Installation_SetupWebSiteURL'|translate|ucfirst}
- <input type="text" id="seoUrl" size="15" value="{$urlToRank|escape:'html'}" class="textbox" />
+ <script type="text/javascript" src="plugins/SEO/templates/rank.js"></script>
+
+ <form method="post" style="padding: 8px;">
+ <div align="left" class="mediumtext">
+ {'Installation_SetupWebSiteURL'|translate|ucfirst}
+ <input type="text" id="seoUrl" size="15" value="{$urlToRank|escape:'html'}" class="textbox"/>
<span style="padding-left:2px;">
- <input type="submit" id="rankbutton" value="{'SEO_Rank'|translate}" />
+ <input type="submit" id="rankbutton" value="{'SEO_Rank'|translate}"/>
</span>
- </div>
-
- {ajaxLoadingDiv id=ajaxLoadingSEO}
+ </div>
+
+ {ajaxLoadingDiv id=ajaxLoadingSEO}
+
+ <div id="rankStats" align="left" style='margin-top:10px'>
+ {if empty($ranks)}
+ {'General_Error'|translate}
+ {else}
+ {capture name=cleanUrl}
+ <a href='{$urlToRank|escape:'html'}' target='_blank'>{$urlToRank|escape:'html'}</a>
+ {/capture}
+ {'SEO_SEORankingsFor'|translate:$smarty.capture.cleanUrl}
+ <table cellspacing='2' style='margin:auto;line-height:1.5em;padding-top:10px'>
+ {foreach from=$ranks item=rank}
+ <tr>
+ <td>{if !empty($rank.logo_link)}<a href="{$rank.logo_link}" target="_blank"
+ {if !empty($rank.logo_tooltip)}title="{$rank.logo_tooltip}"{/if}>{/if}<img
+ style='vertical-align:middle;margin-right:6px;' src='{$rank.logo}' border='0'
+ alt="{$rank.label}">{if !empty($rank.logo_link)}</a>{/if} {$rank.label}
+ </td>
+ <td>
+ <div style='margin-left:15px'>
+ {if isset($rank.rank)}{$rank.rank}{else}-{/if}
+ {if $rank.id=='pagerank'} /10
+ {elseif $rank.id=='google-index' || $rank.id=='bing-index'} {'SEO_Pages'|translate}
+ {/if}
+ </div>
+ </td>
+ </tr>
+ {/foreach}
- <div id="rankStats" align="left" style='margin-top:10px'>
- {if empty($ranks)}
- {'General_Error'|translate}
- {else}
-{capture name=cleanUrl}
-<a href='{$urlToRank|escape:'html'}' target='_blank'>{$urlToRank|escape:'html'}</a>
-{/capture}
- {'SEO_SEORankingsFor'|translate:$smarty.capture.cleanUrl}
- <table cellspacing='2' style='margin:auto;line-height:1.5em;padding-top:10px'>
- {foreach from=$ranks item=rank}
- <tr>
- <td>{if !empty($rank.logo_link)}<a href="{$rank.logo_link}" target="_blank" {if !empty($rank.logo_tooltip)}title="{$rank.logo_tooltip}"{/if}>{/if}<img style='vertical-align:middle;margin-right:6px;' src='{$rank.logo}' border='0' alt="{$rank.label}">{if !empty($rank.logo_link)}</a>{/if} {$rank.label}
- </td><td>
- <div style='margin-left:15px'>
- {if isset($rank.rank)}{$rank.rank}{else}-{/if}
- {if $rank.id=='pagerank'} /10
- {elseif $rank.id=='google-index' || $rank.id=='bing-index'} {'SEO_Pages'|translate}
- {/if}
- </div>
- </td>
- </tr>
- {/foreach}
-
- </table>
- {/if}
- </div>
- </form>
+ </table>
+ {/if}
+ </div>
+ </form>
</div>
diff --git a/plugins/SEO/templates/rank.js b/plugins/SEO/templates/rank.js
index d7a3802fc9..1a9dc07d0c 100644
--- a/plugins/SEO/templates/rank.js
+++ b/plugins/SEO/templates/rank.js
@@ -12,7 +12,7 @@ $(document).ready(function () {
ajaxRequest.addParams({
module: 'SEO',
action: 'getRank',
- url: encodeURIComponent($('#seoUrl').val())
+ url: encodeURIComponent($('#seoUrl').val())
}, 'get');
ajaxRequest.setCallback(
function (response) {
diff --git a/plugins/SecurityInfo/Controller.php b/plugins/SecurityInfo/Controller.php
index 94a4d6d02c..1c8ff217cd 100644
--- a/plugins/SecurityInfo/Controller.php
+++ b/plugins/SecurityInfo/Controller.php
@@ -1,7 +1,7 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
@@ -14,30 +14,30 @@
*/
class Piwik_SecurityInfo_Controller extends Piwik_Controller_Admin
{
- function index()
- {
- Piwik::checkUserIsSuperUser();
+ function index()
+ {
+ Piwik::checkUserIsSuperUser();
- require_once(dirname(__FILE__) . '/PhpSecInfo/PhpSecInfo.php');
+ require_once(dirname(__FILE__) . '/PhpSecInfo/PhpSecInfo.php');
- // instantiate the class
- $psi = new PhpSecInfo();
+ // instantiate the class
+ $psi = new PhpSecInfo();
- // load and run all tests
- $psi->loadAndRun();
+ // load and run all tests
+ $psi->loadAndRun();
- // grab the results as a multidimensional array
- $results = $psi->getResultsAsArray();
+ // grab the results as a multidimensional array
+ $results = $psi->getResultsAsArray();
- // suppress results
- unset($results['test_results']['Core']['memory_limit']);
- unset($results['test_results']['Core']['post_max_size']);
- unset($results['test_results']['Core']['upload_max_filesize']);
+ // suppress results
+ unset($results['test_results']['Core']['memory_limit']);
+ unset($results['test_results']['Core']['post_max_size']);
+ unset($results['test_results']['Core']['upload_max_filesize']);
- $view = Piwik_View::factory('index');
- $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
- $view->results = $results;
- echo $view->render();
- }
+ $view = Piwik_View::factory('index');
+ $this->setBasicVariablesView($view);
+ $view->menu = Piwik_GetAdminMenu();
+ $view->results = $results;
+ echo $view->render();
+ }
}
diff --git a/plugins/SecurityInfo/PhpSecInfo/PhpSecInfo.php b/plugins/SecurityInfo/PhpSecInfo/PhpSecInfo.php
index 0e9bbc7768..acdba6b959 100644
--- a/plugins/SecurityInfo/PhpSecInfo/PhpSecInfo.php
+++ b/plugins/SecurityInfo/PhpSecInfo/PhpSecInfo.php
@@ -95,497 +95,505 @@ define('PHPSECINFO_BASE_DIR', dirname(__FILE__));
class PhpSecInfo
{
- /**
- * An array of tests to run
- *
- * @var array PhpSecInfo_Test
- */
- var $tests_to_run = array();
-
-
- /**
- * An array of results. Each result is an associative array:
- * <code>
- * $result['result'] = PHPSECINFO_TEST_RESULT_NOTICE;
- * $result['message'] = "a string describing the test results and what they mean";
- * </code>
- *
- * @var array
- */
- var $test_results = array();
-
-
- /**
- * An array of tests that were not run
- *
- * <code>
- * $result['result'] = PHPSECINFO_TEST_RESULT_NOTRUN;
- * $result['message'] = "a string explaining why the test was not run";
- * </code>
- *
- * @var array
- */
- var $tests_not_run = array();
-
-
- /**
- * The language code used. Defaults to PHPSECINFO_LANG_DEFAULT, which
- * is 'en'
- *
- * @var string
- * @see PHPSECINFO_LANG_DEFAULT
- */
- var $language = PHPSECINFO_LANG_DEFAULT;
-
-
- /**
- * An array of integers recording the number of test results in each category. Categories can include
- * some or all of the PHPSECINFO_TEST_* constants. Constants are the keys, # of results are the values.
- *
- * @var array
- */
- var $result_counts = array();
-
-
- /**
- * The number of tests that have been run
- *
- * @var integer
- */
- var $num_tests_run = 0;
-
-
- /**
- * The base directory for phpsecinfo. Set within the constructor. Paths are resolved from this.
- * @var string
- */
- var $_base_dir;
-
-
- /**
- * The directory PHPSecInfo will look for views. It defaults to the value
- * in PHPSECINFO_VIEW_DIR_DEFAULT, but can be changed with the setViewDirectory()
- * method.
- *
- * @var string
- */
- var $_view_directory;
-
-
- /**
- * The output format, used to load the proper view
- *
- * @var string
- **/
- var $_format;
-
- /**
- * Constructor
- *
- * @return PhpSecInfo
- */
- function PhpSecInfo($opts = null) {
-
- $this->_base_dir = dirname(__FILE__);
-
- if ($opts) {
- if (isset($opts['view_directory'])) {
- $this->setViewDirectory($opts['view_directory']);
- } else {
- $this->setViewDirectory(dirname(__FILE__).DIRECTORY_SEPARATOR . PHPSECINFO_VIEW_DIR_DEFAULT);
- }
-
- if (isset($opts['format'])) {
- $this->setFormat($opts['format']);
- } else {
- if (!strcasecmp(PHP_SAPI, 'cli')) {
- $this->setFormat('Cli');
- } else {
- $this->setFormat(PHPSECINFO_FORMAT_DEFAULT);
- }
- }
-
- } else { /* Use defaults */
- $this->setViewDirectory(dirname(__FILE__).DIRECTORY_SEPARATOR . PHPSECINFO_VIEW_DIR_DEFAULT);
- if (!strcasecmp(PHP_SAPI, 'cli')) {
- $this->setFormat('Cli');
- } else {
- $this->setFormat(PHPSECINFO_FORMAT_DEFAULT);
- }
- }
- }
-
-
- /**
- * recurses through the Test subdir and includes classes in each test group subdir,
- * then builds an array of classnames for the tests that will be run
- *
- */
- function loadTests() {
-
- $test_root = dir(dirname(__FILE__).DIRECTORY_SEPARATOR.'Test');
-
- //echo "<pre>"; echo print_r($test_root, true); echo "</pre>";
-
- while (false !== ($entry = $test_root->read())) {
- if ( is_dir($test_root->path.DIRECTORY_SEPARATOR.$entry) && !preg_match('~^(\.|_vti)(.*)$~', $entry) ) {
- $test_dirs[] = $entry;
- }
- }
- //echo "<pre>"; echo print_r($test_dirs, true); echo "</pre>";
-
- // include_once all files in each test dir
- foreach ($test_dirs as $test_dir) {
- $this_dir = dir($test_root->path.DIRECTORY_SEPARATOR.$test_dir);
-
- while (false !== ($entry = $this_dir->read())) {
- if (!is_dir($this_dir->path.DIRECTORY_SEPARATOR.$entry)) {
- include_once $this_dir->path.DIRECTORY_SEPARATOR.$entry;
- $classNames[] = "PhpSecInfo_Test_".$test_dir."_".basename($entry, '.php');
- }
- }
-
- }
-
- // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here
- $this->tests_to_run = $classNames;
- }
-
-
- /**
- * This runs the tests in the tests_to_run array and
- * places returned data in the following arrays/scalars:
- * - $this->test_results
- * - $this->result_counts
- * - $this->num_tests_run
- * - $this->tests_not_run;
- *
- */
- function runTests() {
- // initialize a bunch of arrays
- $this->test_results = array();
- $this->result_counts = array();
- $this->result_counts[PHPSECINFO_TEST_RESULT_NOTRUN] = 0;
- $this->num_tests_run = 0;
-
- foreach ($this->tests_to_run as $testClass) {
-
- /**
- * @var $test PhpSecInfo_Test
- */
- $test = new $testClass();
-
- if ($test->isTestable()) {
- $test->test();
- $rs = array( 'result' => $test->getResult(),
- 'message' => $test->getMessage(),
- 'value_current' => $test->getCurrentTestValue(),
- 'value_recommended' => $test->getRecommendedTestValue(),
- 'moreinfo_url' => $test->getMoreInfoURL(),
- );
- $this->test_results[$test->getTestGroup()][$test->getTestName()] = $rs;
-
- // initialize if not yet set
- if (!isset ($this->result_counts[$rs['result']]) ) {
- $this->result_counts[$rs['result']] = 0;
- }
-
- $this->result_counts[$rs['result']]++;
- $this->num_tests_run++;
- } else {
- $rs = array( 'result' => $test->getResult(),
- 'message' => $test->getMessage(),
- 'value_current' => NULL,
- 'value_recommended' => NULL,
- 'moreinfo_url' => $test->getMoreInfoURL(),
- );
- $this->result_counts[PHPSECINFO_TEST_RESULT_NOTRUN]++;
- $this->tests_not_run[$test->getTestGroup()."::".$test->getTestName()] = $rs;
- }
- }
- }
-
-
- /**
- * This is the main output method. The look and feel mimics phpinfo()
- *
- */
- function renderOutput($page_title="Security Information About PHP") {
- /**
- * We need to use PhpSecInfo_Test::getBooleanIniValue() below
- * @see PhpSecInfo_Test::getBooleanIniValue()
- */
- if (!class_exists('PhpSecInfo_Test')) {
- include( dirname(__FILE__).DIRECTORY_SEPARATOR.'Test'.DIRECTORY_SEPARATOR.'Test.php');
- }
- $this->loadView($this->_format);
- }
-
-
- /**
- * This is a helper method that makes it easy to output tables of test results
- * for a given test group
- *
- * @param string $group_name
- * @param array $group_results
- */
- function _outputRenderTable($group_name, $group_results) {
-
- // exit out if $group_results was empty or not an array. This sorta seems a little hacky...
- if (!is_array($group_results) || sizeof($group_results) < 1) {
- return false;
- }
-
- ksort($group_results);
-
- $this->loadView($this->_format.'/Result', array('group_name'=>$group_name, 'group_results'=>$group_results));
-
- return true;
- }
-
-
-
- /**
- * This outputs a table containing a summary of the test results (counts and % in each result type)
- *
- * @see PHPSecInfo::_outputRenderTable()
- * @see PHPSecInfo::_outputGetResultTypeFromCode()
- */
- function _outputRenderStatsTable() {
-
- foreach($this->result_counts as $code=>$val) {
- if ($code != PHPSECINFO_TEST_RESULT_NOTRUN) {
- $percentage = round($val/$this->num_tests_run * 100,2);
- $result_type = $this->_outputGetResultTypeFromCode($code);
- $stats[$result_type] = array( 'count' => $val,
- 'result' => $code,
- 'message' => "$val out of {$this->num_tests_run} ($percentage%)");
- }
- }
-
- $this->_outputRenderTable('Test Results Summary', $stats);
-
- }
-
-
-
- /**
- * This outputs a table containing a summary or test that were not executed, and the reasons why they were skipped
- *
- * @see PHPSecInfo::_outputRenderTable()
- */
- function _outputRenderNotRunTable() {
-
- $this->_outputRenderTable('Tests Not Run', $this->tests_not_run);
-
- }
-
-
-
-
- /**
- * This is a helper function that returns a CSS class corresponding to
- * the result code the test returned. This allows us to color-code
- * results
- *
- * @param integer $code
- * @return string
- */
- function _outputGetCssClassFromResult($code) {
-
- switch ($code) {
- case PHPSECINFO_TEST_RESULT_OK:
- return 'value-ok';
- break;
-
- case PHPSECINFO_TEST_RESULT_NOTICE:
- return 'value-notice';
- break;
-
- case PHPSECINFO_TEST_RESULT_WARN:
- return 'value-warn';
- break;
-
- case PHPSECINFO_TEST_RESULT_NOTRUN:
- return 'value-notrun';
- break;
-
- case PHPSECINFO_TEST_RESULT_ERROR:
- return 'value-error';
- break;
-
- default:
- return 'value-notrun';
- break;
- }
-
- }
-
-
-
- /**
- * This is a helper function that returns a label string corresponding to
- * the result code the test returned. This is mainly used for the Test
- * Results Summary table.
- *
- * @see PHPSecInfo::_outputRenderStatsTable()
- * @param integer $code
- * @return string
- */
- function _outputGetResultTypeFromCode($code) {
-
- switch ($code) {
- case PHPSECINFO_TEST_RESULT_OK:
- return 'Pass';
- break;
-
- case PHPSECINFO_TEST_RESULT_NOTICE:
- return 'Notice';
- break;
-
- case PHPSECINFO_TEST_RESULT_WARN:
- return 'Warning';
- break;
-
- case PHPSECINFO_TEST_RESULT_NOTRUN:
- return 'Not Run';
- break;
-
- case PHPSECINFO_TEST_RESULT_ERROR:
- return 'Error';
- break;
-
- default:
- return 'Invalid Result Code';
- break;
- }
-
- }
-
-
- /**
- * Loads and runs all the tests
- *
- * As loading, then running, is a pretty common process, this saves a extra method call
- *
- * @since 0.1.1
- *
- */
- function loadAndRun() {
- $this->loadTests();
- $this->runTests();
- }
-
-
- /**
- * returns an associative array of test data. Four keys are set:
- * - test_results (array)
- * - tests_not_run (array)
- * - result_counts (array)
- * - num_tests_run (integer)
- *
- * note that this must be called after tests are loaded and run
- *
- * @since 0.1.1
- * @return array
- */
- function getResultsAsArray() {
- $results = array();
-
- $results['test_results'] = $this->test_results;
- $results['tests_not_run'] = $this->tests_not_run;
- $results['result_counts'] = $this->result_counts;
- $results['num_tests_run'] = $this->num_tests_run;
-
- return $results;
- }
-
-
-
- /**
- * returns the standard output as a string instead of echoing it to the browser
- *
- * note that this must be called after tests are loaded and run
- *
- * @since 0.1.1
- *
- * @return string
- */
- function getOutput() {
- ob_start();
- $this->renderOutput();
- $output = ob_get_clean();
- return $output;
- }
-
-
- /**
- * A very, very simple "view" system
- *
- */
- function loadView($view_name, $data=null) {
- if ($data != null) {
- extract($data);
- }
-
- $view_file = $this->getViewDirectory().$view_name.".php";
-
- if ( file_exists($view_file) && is_readable($view_file) ) {
- ob_start();
- include $view_file;
- echo ob_get_clean();
- } else {
- user_error("The view '{$view_file}' either does not exist or is not readable", E_USER_WARNING);
- }
-
-
- }
-
-
- /**
- * Returns the current view directory
- *
- * @return string
- */
- function getViewDirectory() {
- return $this->_view_directory;
- }
-
-
- /**
- * Sets the directory that PHPSecInfo will look in for views
- *
- * @param string $newdir
- */
- function setViewDirectory($newdir) {
- $this->_view_directory = $newdir;
- }
-
-
-
-
- function getFormat() {
- return $this->_format;
- }
-
-
- function setFormat($format) {
- $this->_format = $format;
- }
+ /**
+ * An array of tests to run
+ *
+ * @var array PhpSecInfo_Test
+ */
+ var $tests_to_run = array();
+
+
+ /**
+ * An array of results. Each result is an associative array:
+ * <code>
+ * $result['result'] = PHPSECINFO_TEST_RESULT_NOTICE;
+ * $result['message'] = "a string describing the test results and what they mean";
+ * </code>
+ *
+ * @var array
+ */
+ var $test_results = array();
+
+
+ /**
+ * An array of tests that were not run
+ *
+ * <code>
+ * $result['result'] = PHPSECINFO_TEST_RESULT_NOTRUN;
+ * $result['message'] = "a string explaining why the test was not run";
+ * </code>
+ *
+ * @var array
+ */
+ var $tests_not_run = array();
+
+
+ /**
+ * The language code used. Defaults to PHPSECINFO_LANG_DEFAULT, which
+ * is 'en'
+ *
+ * @var string
+ * @see PHPSECINFO_LANG_DEFAULT
+ */
+ var $language = PHPSECINFO_LANG_DEFAULT;
+
+
+ /**
+ * An array of integers recording the number of test results in each category. Categories can include
+ * some or all of the PHPSECINFO_TEST_* constants. Constants are the keys, # of results are the values.
+ *
+ * @var array
+ */
+ var $result_counts = array();
+
+
+ /**
+ * The number of tests that have been run
+ *
+ * @var integer
+ */
+ var $num_tests_run = 0;
+
+
+ /**
+ * The base directory for phpsecinfo. Set within the constructor. Paths are resolved from this.
+ * @var string
+ */
+ var $_base_dir;
+
+
+ /**
+ * The directory PHPSecInfo will look for views. It defaults to the value
+ * in PHPSECINFO_VIEW_DIR_DEFAULT, but can be changed with the setViewDirectory()
+ * method.
+ *
+ * @var string
+ */
+ var $_view_directory;
+
+
+ /**
+ * The output format, used to load the proper view
+ *
+ * @var string
+ **/
+ var $_format;
+
+ /**
+ * Constructor
+ *
+ * @return PhpSecInfo
+ */
+ function PhpSecInfo($opts = null)
+ {
+
+ $this->_base_dir = dirname(__FILE__);
+
+ if ($opts) {
+ if (isset($opts['view_directory'])) {
+ $this->setViewDirectory($opts['view_directory']);
+ } else {
+ $this->setViewDirectory(dirname(__FILE__) . DIRECTORY_SEPARATOR . PHPSECINFO_VIEW_DIR_DEFAULT);
+ }
+
+ if (isset($opts['format'])) {
+ $this->setFormat($opts['format']);
+ } else {
+ if (!strcasecmp(PHP_SAPI, 'cli')) {
+ $this->setFormat('Cli');
+ } else {
+ $this->setFormat(PHPSECINFO_FORMAT_DEFAULT);
+ }
+ }
+
+ } else { /* Use defaults */
+ $this->setViewDirectory(dirname(__FILE__) . DIRECTORY_SEPARATOR . PHPSECINFO_VIEW_DIR_DEFAULT);
+ if (!strcasecmp(PHP_SAPI, 'cli')) {
+ $this->setFormat('Cli');
+ } else {
+ $this->setFormat(PHPSECINFO_FORMAT_DEFAULT);
+ }
+ }
+ }
+
+
+ /**
+ * recurses through the Test subdir and includes classes in each test group subdir,
+ * then builds an array of classnames for the tests that will be run
+ *
+ */
+ function loadTests()
+ {
+
+ $test_root = dir(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Test');
+
+ //echo "<pre>"; echo print_r($test_root, true); echo "</pre>";
+
+ while (false !== ($entry = $test_root->read())) {
+ if (is_dir($test_root->path . DIRECTORY_SEPARATOR . $entry) && !preg_match('~^(\.|_vti)(.*)$~', $entry)) {
+ $test_dirs[] = $entry;
+ }
+ }
+ //echo "<pre>"; echo print_r($test_dirs, true); echo "</pre>";
+
+ // include_once all files in each test dir
+ foreach ($test_dirs as $test_dir) {
+ $this_dir = dir($test_root->path . DIRECTORY_SEPARATOR . $test_dir);
+
+ while (false !== ($entry = $this_dir->read())) {
+ if (!is_dir($this_dir->path . DIRECTORY_SEPARATOR . $entry)) {
+ include_once $this_dir->path . DIRECTORY_SEPARATOR . $entry;
+ $classNames[] = "PhpSecInfo_Test_" . $test_dir . "_" . basename($entry, '.php');
+ }
+ }
+
+ }
+
+ // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here
+ $this->tests_to_run = $classNames;
+ }
+
+
+ /**
+ * This runs the tests in the tests_to_run array and
+ * places returned data in the following arrays/scalars:
+ * - $this->test_results
+ * - $this->result_counts
+ * - $this->num_tests_run
+ * - $this->tests_not_run;
+ *
+ */
+ function runTests()
+ {
+ // initialize a bunch of arrays
+ $this->test_results = array();
+ $this->result_counts = array();
+ $this->result_counts[PHPSECINFO_TEST_RESULT_NOTRUN] = 0;
+ $this->num_tests_run = 0;
+
+ foreach ($this->tests_to_run as $testClass) {
+
+ /**
+ * @var $test PhpSecInfo_Test
+ */
+ $test = new $testClass();
+
+ if ($test->isTestable()) {
+ $test->test();
+ $rs = array('result' => $test->getResult(),
+ 'message' => $test->getMessage(),
+ 'value_current' => $test->getCurrentTestValue(),
+ 'value_recommended' => $test->getRecommendedTestValue(),
+ 'moreinfo_url' => $test->getMoreInfoURL(),
+ );
+ $this->test_results[$test->getTestGroup()][$test->getTestName()] = $rs;
+
+ // initialize if not yet set
+ if (!isset ($this->result_counts[$rs['result']])) {
+ $this->result_counts[$rs['result']] = 0;
+ }
+
+ $this->result_counts[$rs['result']]++;
+ $this->num_tests_run++;
+ } else {
+ $rs = array('result' => $test->getResult(),
+ 'message' => $test->getMessage(),
+ 'value_current' => NULL,
+ 'value_recommended' => NULL,
+ 'moreinfo_url' => $test->getMoreInfoURL(),
+ );
+ $this->result_counts[PHPSECINFO_TEST_RESULT_NOTRUN]++;
+ $this->tests_not_run[$test->getTestGroup() . "::" . $test->getTestName()] = $rs;
+ }
+ }
+ }
+
+
+ /**
+ * This is the main output method. The look and feel mimics phpinfo()
+ *
+ */
+ function renderOutput($page_title = "Security Information About PHP")
+ {
+ /**
+ * We need to use PhpSecInfo_Test::getBooleanIniValue() below
+ * @see PhpSecInfo_Test::getBooleanIniValue()
+ */
+ if (!class_exists('PhpSecInfo_Test')) {
+ include(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Test' . DIRECTORY_SEPARATOR . 'Test.php');
+ }
+ $this->loadView($this->_format);
+ }
+
+
+ /**
+ * This is a helper method that makes it easy to output tables of test results
+ * for a given test group
+ *
+ * @param string $group_name
+ * @param array $group_results
+ */
+ function _outputRenderTable($group_name, $group_results)
+ {
+
+ // exit out if $group_results was empty or not an array. This sorta seems a little hacky...
+ if (!is_array($group_results) || sizeof($group_results) < 1) {
+ return false;
+ }
+
+ ksort($group_results);
+
+ $this->loadView($this->_format . '/Result', array('group_name' => $group_name, 'group_results' => $group_results));
+
+ return true;
+ }
+
+
+ /**
+ * This outputs a table containing a summary of the test results (counts and % in each result type)
+ *
+ * @see PHPSecInfo::_outputRenderTable()
+ * @see PHPSecInfo::_outputGetResultTypeFromCode()
+ */
+ function _outputRenderStatsTable()
+ {
+
+ foreach ($this->result_counts as $code => $val) {
+ if ($code != PHPSECINFO_TEST_RESULT_NOTRUN) {
+ $percentage = round($val / $this->num_tests_run * 100, 2);
+ $result_type = $this->_outputGetResultTypeFromCode($code);
+ $stats[$result_type] = array('count' => $val,
+ 'result' => $code,
+ 'message' => "$val out of {$this->num_tests_run} ($percentage%)");
+ }
+ }
+
+ $this->_outputRenderTable('Test Results Summary', $stats);
+
+ }
+
+
+ /**
+ * This outputs a table containing a summary or test that were not executed, and the reasons why they were skipped
+ *
+ * @see PHPSecInfo::_outputRenderTable()
+ */
+ function _outputRenderNotRunTable()
+ {
+
+ $this->_outputRenderTable('Tests Not Run', $this->tests_not_run);
+
+ }
+
+
+ /**
+ * This is a helper function that returns a CSS class corresponding to
+ * the result code the test returned. This allows us to color-code
+ * results
+ *
+ * @param integer $code
+ * @return string
+ */
+ function _outputGetCssClassFromResult($code)
+ {
+
+ switch ($code) {
+ case PHPSECINFO_TEST_RESULT_OK:
+ return 'value-ok';
+ break;
+
+ case PHPSECINFO_TEST_RESULT_NOTICE:
+ return 'value-notice';
+ break;
+
+ case PHPSECINFO_TEST_RESULT_WARN:
+ return 'value-warn';
+ break;
+
+ case PHPSECINFO_TEST_RESULT_NOTRUN:
+ return 'value-notrun';
+ break;
+
+ case PHPSECINFO_TEST_RESULT_ERROR:
+ return 'value-error';
+ break;
+
+ default:
+ return 'value-notrun';
+ break;
+ }
+
+ }
+
+
+ /**
+ * This is a helper function that returns a label string corresponding to
+ * the result code the test returned. This is mainly used for the Test
+ * Results Summary table.
+ *
+ * @see PHPSecInfo::_outputRenderStatsTable()
+ * @param integer $code
+ * @return string
+ */
+ function _outputGetResultTypeFromCode($code)
+ {
+
+ switch ($code) {
+ case PHPSECINFO_TEST_RESULT_OK:
+ return 'Pass';
+ break;
+
+ case PHPSECINFO_TEST_RESULT_NOTICE:
+ return 'Notice';
+ break;
+
+ case PHPSECINFO_TEST_RESULT_WARN:
+ return 'Warning';
+ break;
+
+ case PHPSECINFO_TEST_RESULT_NOTRUN:
+ return 'Not Run';
+ break;
+
+ case PHPSECINFO_TEST_RESULT_ERROR:
+ return 'Error';
+ break;
+
+ default:
+ return 'Invalid Result Code';
+ break;
+ }
+
+ }
+
+
+ /**
+ * Loads and runs all the tests
+ *
+ * As loading, then running, is a pretty common process, this saves a extra method call
+ *
+ * @since 0.1.1
+ *
+ */
+ function loadAndRun()
+ {
+ $this->loadTests();
+ $this->runTests();
+ }
+
+
+ /**
+ * returns an associative array of test data. Four keys are set:
+ * - test_results (array)
+ * - tests_not_run (array)
+ * - result_counts (array)
+ * - num_tests_run (integer)
+ *
+ * note that this must be called after tests are loaded and run
+ *
+ * @since 0.1.1
+ * @return array
+ */
+ function getResultsAsArray()
+ {
+ $results = array();
+
+ $results['test_results'] = $this->test_results;
+ $results['tests_not_run'] = $this->tests_not_run;
+ $results['result_counts'] = $this->result_counts;
+ $results['num_tests_run'] = $this->num_tests_run;
+
+ return $results;
+ }
+
+
+ /**
+ * returns the standard output as a string instead of echoing it to the browser
+ *
+ * note that this must be called after tests are loaded and run
+ *
+ * @since 0.1.1
+ *
+ * @return string
+ */
+ function getOutput()
+ {
+ ob_start();
+ $this->renderOutput();
+ $output = ob_get_clean();
+ return $output;
+ }
+
+
+ /**
+ * A very, very simple "view" system
+ *
+ */
+ function loadView($view_name, $data = null)
+ {
+ if ($data != null) {
+ extract($data);
+ }
+
+ $view_file = $this->getViewDirectory() . $view_name . ".php";
+
+ if (file_exists($view_file) && is_readable($view_file)) {
+ ob_start();
+ include $view_file;
+ echo ob_get_clean();
+ } else {
+ user_error("The view '{$view_file}' either does not exist or is not readable", E_USER_WARNING);
+ }
+
+
+ }
+
+
+ /**
+ * Returns the current view directory
+ *
+ * @return string
+ */
+ function getViewDirectory()
+ {
+ return $this->_view_directory;
+ }
+
+
+ /**
+ * Sets the directory that PHPSecInfo will look in for views
+ *
+ * @param string $newdir
+ */
+ function setViewDirectory($newdir)
+ {
+ $this->_view_directory = $newdir;
+ }
+
+
+ function getFormat()
+ {
+ return $this->_format;
+ }
+
+
+ function setFormat($format)
+ {
+ $this->_format = $format;
+ }
}
-
-
/**
* A globally-available function that runs the tests and creates the result page
*
*/
-function phpsecinfo() {
- // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here
- $psi = new PhpSecInfo();
- $psi->loadAndRun();
- $psi->renderOutput();
+function phpsecinfo()
+{
+ // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here
+ $psi = new PhpSecInfo();
+ $psi->loadAndRun();
+ $psi->renderOutput();
}
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Application/php.php b/plugins/SecurityInfo/PhpSecInfo/Test/Application/php.php
index 919d73a2cb..3199cb3167 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Application/php.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Application/php.php
@@ -9,7 +9,7 @@
/**
* require the PhpSecInfo_Test_Application class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Application.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Application.php');
/**
* Test class for PHP application
@@ -21,49 +21,52 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Application.php');
*/
class PhpSecInfo_Test_Application_Php extends PhpSecInfo_Test_Application
{
- var $test_name = "PHP";
+ var $test_name = "PHP";
- var $recommended_value = null;
+ var $recommended_value = null;
- function _retrieveCurrentValue() {
- $this->current_value = PHP_VERSION;
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = PHP_VERSION;
- $url = 'http://php.net/releases/?serialize=1&version=5';
- $timeout = Piwik_UpdateCheck::SOCKET_TIMEOUT;
- try {
- $latestVersion = Piwik_Http::sendHttpRequest($url, $timeout);
- $versionInfo = safe_unserialize($latestVersion);
- $this->recommended_value = $versionInfo['version'];
- } catch(Exception $e) {
- $this->recommended_value = '';
- }
- }
+ $url = 'http://php.net/releases/?serialize=1&version=5';
+ $timeout = Piwik_UpdateCheck::SOCKET_TIMEOUT;
+ try {
+ $latestVersion = Piwik_Http::sendHttpRequest($url, $timeout);
+ $versionInfo = safe_unserialize($latestVersion);
+ $this->recommended_value = $versionInfo['version'];
+ } catch (Exception $e) {
+ $this->recommended_value = '';
+ }
+ }
- function _execTest() {
- if (version_compare($this->current_value, '5.2.1') < 0) {
- return PHPSECINFO_TEST_RESULT_WARN;
- }
+ function _execTest()
+ {
+ if (version_compare($this->current_value, '5.2.1') < 0) {
+ return PHPSECINFO_TEST_RESULT_WARN;
+ }
- if (empty($this->recommended_value)) {
- return PHPSECINFO_TEST_RESULT_ERROR;
- }
+ if (empty($this->recommended_value)) {
+ return PHPSECINFO_TEST_RESULT_ERROR;
+ }
- if (version_compare($this->current_value, $this->recommended_value) >= 0 ) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
+ if (version_compare($this->current_value, $this->recommended_value) >= 0) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
- function _setMessages() {
- parent::_setMessages();
+ function _setMessages()
+ {
+ parent::_setMessages();
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "You are running PHP ".$this->current_value.
- ($this->current_value == $this->recommended_value
- ? " (the latest version)."
- : ". The latest version is ".$this->recommended_value."."));
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "You are running PHP ".$this->current_value.". The latest version of PHP is ".$this->recommended_value.".");
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', "You are running PHP ".$this->current_value." which is really old. We recommend running the latest (stable) version of PHP which includes numerous bug fixes and security fixes.");
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_ERROR, 'en', "Unable to determine the latest version of PHP available.");
- }
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "You are running PHP " . $this->current_value .
+ ($this->current_value == $this->recommended_value
+ ? " (the latest version)."
+ : ". The latest version is " . $this->recommended_value . "."));
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "You are running PHP " . $this->current_value . ". The latest version of PHP is " . $this->recommended_value . ".");
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', "You are running PHP " . $this->current_value . " which is really old. We recommend running the latest (stable) version of PHP which includes numerous bug fixes and security fixes.");
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_ERROR, 'en', "Unable to determine the latest version of PHP available.");
+ }
}
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Application/piwik.php b/plugins/SecurityInfo/PhpSecInfo/Test/Application/piwik.php
index 5a495a352c..16e7fbf42f 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Application/piwik.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Application/piwik.php
@@ -9,7 +9,7 @@
/**
* require the PhpSecInfo_Test_Application class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Application.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Application.php');
/**
* Test class for Piwik application
@@ -21,38 +21,41 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Application.php');
*/
class PhpSecInfo_Test_Application_Piwik extends PhpSecInfo_Test_Application
{
- var $test_name = "Piwik";
+ var $test_name = "Piwik";
- var $recommended_value = null;
+ var $recommended_value = null;
- function _retrieveCurrentValue() {
- $this->current_value = Piwik_Version::VERSION;
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = Piwik_Version::VERSION;
- $this->recommended_value = Piwik_GetOption(Piwik_UpdateCheck::LATEST_VERSION);
- }
+ $this->recommended_value = Piwik_GetOption(Piwik_UpdateCheck::LATEST_VERSION);
+ }
- function _execTest() {
- if (version_compare($this->current_value, '0.5') < 0) {
- return PHPSECINFO_TEST_RESULT_WARN;
- }
+ function _execTest()
+ {
+ if (version_compare($this->current_value, '0.5') < 0) {
+ return PHPSECINFO_TEST_RESULT_WARN;
+ }
- if (empty($this->recommended_value)) {
- return PHPSECINFO_TEST_RESULT_ERROR;
- }
+ if (empty($this->recommended_value)) {
+ return PHPSECINFO_TEST_RESULT_ERROR;
+ }
- if (version_compare($this->current_value, $this->recommended_value) >= 0) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
+ if (version_compare($this->current_value, $this->recommended_value) >= 0) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
- function _setMessages() {
- parent::_setMessages();
+ function _setMessages()
+ {
+ parent::_setMessages();
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "You are running Piwik ".$this->current_value." (the latest version).");
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "You are running Piwik ".$this->current_value.". The latest version of Piwik is ".$this->recommended_value.".");
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', "You are running Piwik ".$this->current_value." which is no longer supported by the Piwik developers. We recommend running the latest (stable) version of Piwik which includes numerous enhancements, bug fixes, and security fixes.");
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_ERROR, 'en', "Unable to determine the latest version of Piwik available.");
- }
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "You are running Piwik " . $this->current_value . " (the latest version).");
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "You are running Piwik " . $this->current_value . ". The latest version of Piwik is " . $this->recommended_value . ".");
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', "You are running Piwik " . $this->current_value . " which is no longer supported by the Piwik developers. We recommend running the latest (stable) version of Piwik which includes numerous enhancements, bug fixes, and security fixes.");
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_ERROR, 'en', "Unable to determine the latest version of Piwik available.");
+ }
}
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/CGI/force_redirect.php b/plugins/SecurityInfo/PhpSecInfo/Test/CGI/force_redirect.php
index d84d709cc4..cdb0ca2c4f 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/CGI/force_redirect.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/CGI/force_redirect.php
@@ -9,7 +9,7 @@
/**
* require the PhpSecInfo_Test_Cgi class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Cgi.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Cgi.php');
/**
* Test class for cgi force_redirect
@@ -20,82 +20,81 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Cgi.php');
class PhpSecInfo_Test_Cgi_Force_Redirect extends PhpSecInfo_Test_Cgi
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "force_redirect";
-
- /**
- * The recommended setting value
- *
- * @var mixed
- */
- var $recommended_value = TRUE;
-
-
-
- function _retrieveCurrentValue() {
- $this->current_value = $this->getBooleanIniValue('cgi.force_redirect');
- }
-
-
-
- private function skipTest() {
- if (strpos(PHP_SAPI, 'cgi') === false) {
- return PHP_SAPI . ' SAPI for php';
- }
-
- // these web servers require cgi.force_redirect = 0
- $webServers = array('Microsoft-IIS', 'OmniHTTPd', 'Xitami');
- if (isset($_SERVER['SERVER_SOFTWARE'])) {
- foreach ($webServers as $webServer) {
- if (strpos($_SERVER['SERVER_SOFTWARE'], $webServer) === 0) {
- return $_SERVER['SERVER_SOFTWARE'];
- }
- }
- }
-
- return false;
- }
-
-
-
- /**
- * Checks to see if cgi.force_redirect is enabled
- *
- */
- function _execTest() {
- if ($this->current_value == $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
-
- if ($this->skipTest())
- {
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
-
- return PHPSECINFO_TEST_RESULT_WARN;
- }
-
-
-
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "force_redirect is enabled, which is the recommended setting");
- $ini = ini_get_all();
- if (isset($ini['cgi.force_redirect'])) {
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "force_redirect is disabled. In most cases, this is a security vulnerability, but it appears this is not needed because you are running " . $this->skipTest());
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', "force_redirect is disabled. In most cases, this is a <strong>serious</strong> security vulnerability. Unless you are absolutely sure this is not needed, enable this setting");
- } else {
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "force_redirect is disabled because php was not compiled with --enable-force-cgi-redirect. In most cases, this is a security vulnerability, but it appears this is not needed because you are running " . $this->skipTest());
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', "force_redirect is disabled because php was not compiled with --enable-force-cgi-redirect. In most cases, this is a <strong>serious</strong> security vulnerability. Unless you are absolutely sure this is not needed, recompile php with --enable-force-cgi-redirect and enable cgi.force_redirect");
- }
- }
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "force_redirect";
+
+ /**
+ * The recommended setting value
+ *
+ * @var mixed
+ */
+ var $recommended_value = TRUE;
+
+
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->getBooleanIniValue('cgi.force_redirect');
+ }
+
+
+ private function skipTest()
+ {
+ if (strpos(PHP_SAPI, 'cgi') === false) {
+ return PHP_SAPI . ' SAPI for php';
+ }
+
+ // these web servers require cgi.force_redirect = 0
+ $webServers = array('Microsoft-IIS', 'OmniHTTPd', 'Xitami');
+ if (isset($_SERVER['SERVER_SOFTWARE'])) {
+ foreach ($webServers as $webServer) {
+ if (strpos($_SERVER['SERVER_SOFTWARE'], $webServer) === 0) {
+ return $_SERVER['SERVER_SOFTWARE'];
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Checks to see if cgi.force_redirect is enabled
+ *
+ */
+ function _execTest()
+ {
+ if ($this->current_value == $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ if ($this->skipTest()) {
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
+
+ return PHPSECINFO_TEST_RESULT_WARN;
+ }
+
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "force_redirect is enabled, which is the recommended setting");
+ $ini = ini_get_all();
+ if (isset($ini['cgi.force_redirect'])) {
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "force_redirect is disabled. In most cases, this is a security vulnerability, but it appears this is not needed because you are running " . $this->skipTest());
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', "force_redirect is disabled. In most cases, this is a <strong>serious</strong> security vulnerability. Unless you are absolutely sure this is not needed, enable this setting");
+ } else {
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "force_redirect is disabled because php was not compiled with --enable-force-cgi-redirect. In most cases, this is a security vulnerability, but it appears this is not needed because you are running " . $this->skipTest());
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', "force_redirect is disabled because php was not compiled with --enable-force-cgi-redirect. In most cases, this is a <strong>serious</strong> security vulnerability. Unless you are absolutely sure this is not needed, recompile php with --enable-force-cgi-redirect and enable cgi.force_redirect");
+ }
+ }
}
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/allow_url_fopen.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/allow_url_fopen.php
index 799bb88110..b6df400148 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/allow_url_fopen.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/allow_url_fopen.php
@@ -10,7 +10,7 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* Test Class for allow_url_fopen
@@ -21,59 +21,61 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
*/
class PhpSecInfo_Test_Core_Allow_Url_Fopen extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "allow_url_fopen";
-
- /**
- * The recommended setting value
- *
- * @var mixed
- */
- var $recommended_value = FALSE;
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "allow_url_fopen";
+ /**
+ * The recommended setting value
+ *
+ * @var mixed
+ */
+ var $recommended_value = FALSE;
-
- function _retrieveCurrentValue() {
- $this->current_value = $this->getBooleanIniValue('allow_url_fopen');
- }
-
-
- /**
- * Checks to see if allow_url_fopen is enabled
- *
- */
- function _execTest() {
- if ( version_compare(PHP_VERSION, '5.2', '<') ) { /* this is much more severe if we're running < 5.2 */
- if ($this->current_value == $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
- return PHPSECINFO_TEST_RESULT_WARN;
- } else { /* In 5.2, we'll consider allow_url_fopen "safe" */
- $this->recommended_value = TRUE;
- return PHPSECINFO_TEST_RESULT_OK;
- }
- }
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->getBooleanIniValue('allow_url_fopen');
+ }
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
- if ( version_compare(PHP_VERSION, '5.2', '<') ) { /* this is much more severe if we're running < 5.2 */
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'allow_url_fopen is disabled, which is the recommended setting');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'allow_url_fopen is enabled. This could be a serious security risk. You should disable allow_url_fopen and consider using the <a href="http://php.net/manual/en/ref.curl.php" target="_blank">PHP cURL functions</a> instead.');
-
- } else {
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'You are running PHP 5.2 or greater, which makes allow_url_fopen significantly safer. Make sure allow_url_include is <em>disabled</em>, though');
- }
- }
+ /**
+ * Checks to see if allow_url_fopen is enabled
+ *
+ */
+ function _execTest()
+ {
+ if (version_compare(PHP_VERSION, '5.2', '<')) { /* this is much more severe if we're running < 5.2 */
+ if ($this->current_value == $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ return PHPSECINFO_TEST_RESULT_WARN;
+ } else { /* In 5.2, we'll consider allow_url_fopen "safe" */
+ $this->recommended_value = TRUE;
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+ }
+
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+ if (version_compare(PHP_VERSION, '5.2', '<')) { /* this is much more severe if we're running < 5.2 */
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'allow_url_fopen is disabled, which is the recommended setting');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'allow_url_fopen is enabled. This could be a serious security risk. You should disable allow_url_fopen and consider using the <a href="http://php.net/manual/en/ref.curl.php" target="_blank">PHP cURL functions</a> instead.');
+
+ } else {
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'You are running PHP 5.2 or greater, which makes allow_url_fopen significantly safer. Make sure allow_url_include is <em>disabled</em>, though');
+ }
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/allow_url_include.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/allow_url_include.php
index c9bea4d74d..d80b04753c 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/allow_url_include.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/allow_url_include.php
@@ -10,7 +10,7 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* Test Class for allow_url_include
@@ -21,57 +21,60 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
*/
class PhpSecInfo_Test_Core_Allow_Url_Include extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "allow_url_include";
-
-
- var $recommended_value = FALSE;
-
-
- function _retrieveCurrentValue() {
- $this->current_value = $this->getBooleanIniValue('allow_url_include');
- }
-
-
- /**
- * Checks to see if allow_url_fopen is enabled
- *
- */
- function _execTest() {
- if ($this->current_value == $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
-
- return PHPSECINFO_TEST_RESULT_WARN;
- }
-
-
- /**
- * allow_url_include is only available since PHP 5.2
- *
- * @return boolean
- */
- function isTestable() {
- return version_compare(PHP_VERSION, '5.2', '>=');
- }
-
-
-
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'You are running a version of PHP older than 5.2, and allow_url_include is not available');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'allow_url_include is disabled, which is the recommended setting');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'allow_url_include is enabled. This could be a serious security risk. You should disable allow_url_include and consider using the <a href="http://php.net/manual/en/ref.curl.php" target="_blank">PHP cURL functions</a> instead.');
- }
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "allow_url_include";
+
+
+ var $recommended_value = FALSE;
+
+
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->getBooleanIniValue('allow_url_include');
+ }
+
+
+ /**
+ * Checks to see if allow_url_fopen is enabled
+ *
+ */
+ function _execTest()
+ {
+ if ($this->current_value == $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ return PHPSECINFO_TEST_RESULT_WARN;
+ }
+
+
+ /**
+ * allow_url_include is only available since PHP 5.2
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+ return version_compare(PHP_VERSION, '5.2', '>=');
+ }
+
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'You are running a version of PHP older than 5.2, and allow_url_include is not available');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'allow_url_include is disabled, which is the recommended setting');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'allow_url_include is enabled. This could be a serious security risk. You should disable allow_url_include and consider using the <a href="http://php.net/manual/en/ref.curl.php" target="_blank">PHP cURL functions</a> instead.');
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/display_errors.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/display_errors.php
index e0e93d6748..3908b289a3 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/display_errors.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/display_errors.php
@@ -1,7 +1,7 @@
<?php
/**
* Test class for display_errors
- *
+ *
* @package PhpSecInfo
* @author Ed Finkler <coj@funkatron.com>
*/
@@ -10,53 +10,56 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* Test class for display_errors
- *
+ *
* @package PhpSecInfo
*/
class PhpSecInfo_Test_Core_Display_Errors extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "display_errors";
-
- var $recommended_value = FALSE;
-
- function _retrieveCurrentValue() {
- $this->current_value = $this->getBooleanIniValue('display_errors');
- }
-
-
- /**
- * Checks to see if display_errors is enabled
- *
- */
- function _execTest() {
- if ($this->current_value == $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
-
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
-
-
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'display_errors is disabled, which is the recommended setting');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'display_errors is enabled. This is not recommended on "production" servers, as it could reveal sensitive information. You should consider disabling this feature');
- }
-
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "display_errors";
+
+ var $recommended_value = FALSE;
+
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->getBooleanIniValue('display_errors');
+ }
+
+
+ /**
+ * Checks to see if display_errors is enabled
+ *
+ */
+ function _execTest()
+ {
+ if ($this->current_value == $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
+
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'display_errors is disabled, which is the recommended setting');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'display_errors is enabled. This is not recommended on "production" servers, as it could reveal sensitive information. You should consider disabling this feature');
+ }
+
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/expose_php.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/expose_php.php
index 3f26648193..da1666eeff 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/expose_php.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/expose_php.php
@@ -1,7 +1,7 @@
<?php
/**
* Test class for expose_php
- *
+ *
* @package PhpSecInfo
* @author Ed Finkler <coj@funkatron.com>
*/
@@ -10,55 +10,58 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* Test class for expose_php
- *
+ *
* @package PhpSecInfo
*/
class PhpSecInfo_Test_Core_Expose_Php extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "expose_php";
-
- var $recommended_value = FALSE;
-
- function _retrieveCurrentValue() {
- $this->current_value = $this->returnBytes(ini_get('expose_php'));
- }
-
- /**
- * Checks to see if expose_php is enabled
- *
- */
- function _execTest() {
-
- if ($this->current_value == $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
-
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
-
-
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'expose_php is disabled, which is the recommended setting');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'expose_php is enabled. This adds
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "expose_php";
+
+ var $recommended_value = FALSE;
+
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->returnBytes(ini_get('expose_php'));
+ }
+
+ /**
+ * Checks to see if expose_php is enabled
+ *
+ */
+ function _execTest()
+ {
+
+ if ($this->current_value == $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
+
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'expose_php is disabled, which is the recommended setting');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'expose_php is enabled. This adds
the PHP "signature" to the web server header, including the PHP version number. This
could attract attackers looking for vulnerable versions of PHP');
- }
-
+ }
+
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/file_uploads.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/file_uploads.php
index 6a3c822aae..fc4f17e07c 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/file_uploads.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/file_uploads.php
@@ -1,7 +1,7 @@
<?php
/**
* Test Class for file_uploads
- *
+ *
* @package PhpSecInfo
* @author Ed Finkler <coj@funkatron.com>
*/
@@ -10,54 +10,56 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* Test Class for file_uploads
- *
+ *
* @package PhpSecInfo
*/
class PhpSecInfo_Test_Core_File_Uploads extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "file_uploads";
-
- var $recommended_value = FALSE;
-
- function _retrieveCurrentValue() {
- $this->current_value = $this->returnBytes(ini_get('file_uploads'));
- }
-
- /**
- * Checks to see if expose_php is enabled
- *
- */
- function _execTest() {
-
- if ($this->current_value == $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
-
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
-
-
-
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'file_uploads are disabled. Unless you\'re sure you need them, this is the recommended setting');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'file_uploads are enabled. If you do not require file upload capability, consider disabling them.');
- }
-
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "file_uploads";
+
+ var $recommended_value = FALSE;
+
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->returnBytes(ini_get('file_uploads'));
+ }
+
+ /**
+ * Checks to see if expose_php is enabled
+ *
+ */
+ function _execTest()
+ {
+
+ if ($this->current_value == $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
+
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'file_uploads are disabled. Unless you\'re sure you need them, this is the recommended setting');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'file_uploads are enabled. If you do not require file upload capability, consider disabling them.');
+ }
+
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/gid.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/gid.php
index 88a6f3c6c1..89265da2c1 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/gid.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/gid.php
@@ -10,7 +10,7 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
@@ -28,32 +28,34 @@ define ('PHPSECINFO_MIN_SAFE_GID', 100);
class PhpSecInfo_Test_Core_Gid extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "group_id";
-
- var $recommended_value = PHPSECINFO_MIN_SAFE_GID;
-
-
- /**
- * This test only works under Unix OSes
- *
- * @return boolean
- */
- function isTestable() {
- if ($this->osIsWindows()) {
- return false;
- } elseif ($this->getUnixId() === false) {
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'Functions required to retrieve group ID not available');
- return false;
- }
- return true;
- }
-
- function _retrieveCurrentValue() {
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "group_id";
+
+ var $recommended_value = PHPSECINFO_MIN_SAFE_GID;
+
+
+ /**
+ * This test only works under Unix OSes
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+ if ($this->osIsWindows()) {
+ return false;
+ } elseif ($this->getUnixId() === false) {
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'Functions required to retrieve group ID not available');
+ return false;
+ }
+ return true;
+ }
+
+ function _retrieveCurrentValue()
+ {
$id = $this->getUnixId();
if (is_array($id)) {
$lowest_gid = key($id['groups']);
@@ -61,33 +63,35 @@ class PhpSecInfo_Test_Core_Gid extends PhpSecInfo_Test_Core
} else {
$this->current_value = false;
}
- }
-
- /**
- * Checks the GID of the PHP process to make sure it is above PHPSECINFO_MIN_SAFE_GID
- *
- * @see PHPSECINFO_MIN_SAFE_GID
- */
- function _execTest() {
- if ($this->current_value >= $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
-
- return PHPSECINFO_TEST_RESULT_WARN;
- }
-
-
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'PHP is executing as what is probably a non-privileged group');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'PHP may be executing as a "privileged" group, which could be a serious security vulnerability.');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'This test will not run on Windows OSes');
- }
+ }
+
+ /**
+ * Checks the GID of the PHP process to make sure it is above PHPSECINFO_MIN_SAFE_GID
+ *
+ * @see PHPSECINFO_MIN_SAFE_GID
+ */
+ function _execTest()
+ {
+ if ($this->current_value >= $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ return PHPSECINFO_TEST_RESULT_WARN;
+ }
+
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'PHP is executing as what is probably a non-privileged group');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'PHP may be executing as a "privileged" group, which could be a serious security vulnerability.');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'This test will not run on Windows OSes');
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/magic_quotes_gpc.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/magic_quotes_gpc.php
index 89a0ff6f0d..2a69954f0e 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/magic_quotes_gpc.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/magic_quotes_gpc.php
@@ -7,11 +7,10 @@
*/
-
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* Test Class for magic_quotes_gpc
@@ -20,61 +19,65 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
*/
class PhpSecInfo_Test_Core_Magic_Quotes_GPC extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "magic_quotes_gpc";
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "magic_quotes_gpc";
- var $recommended_value = FALSE;
+ var $recommended_value = FALSE;
- function _retrieveCurrentValue() {
- $this->current_value = $this->getBooleanIniValue('magic_quotes_gpc');
- }
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->getBooleanIniValue('magic_quotes_gpc');
+ }
- /**
- * magic_quotes_gpc has been removed since PHP 6.0
- *
- * @return boolean
- */
- function isTestable() {
- return version_compare(PHP_VERSION, '6', '<') ;
- }
+ /**
+ * magic_quotes_gpc has been removed since PHP 6.0
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+ return version_compare(PHP_VERSION, '6', '<');
+ }
- /**
- * Checks to see if allow_url_fopen is enabled
- *
- */
- function _execTest() {
- if ($this->current_value == $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
+ /**
+ * Checks to see if allow_url_fopen is enabled
+ *
+ */
+ function _execTest()
+ {
+ if ($this->current_value == $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'You are running PHP 6 or later and magic_quotes_gpc has been removed');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'magic_quotes_gpc is disabled, which is the recommended setting');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'magic_quotes_gpc is enabled. This
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'You are running PHP 6 or later and magic_quotes_gpc has been removed');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'magic_quotes_gpc is disabled, which is the recommended setting');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'magic_quotes_gpc is enabled. This
feature is inconsistent in blocking attacks, and can in some cases cause data loss with
uploaded files. You should <i>not</i> rely on magic_quotes_gpc to block attacks. It is
recommended that magic_quotes_gpc be disabled, and input filtering be handled by your PHP
scripts');
- }
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/memory_limit.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/memory_limit.php
index 82a33d8db9..cd76ecd3a8 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/memory_limit.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/memory_limit.php
@@ -12,13 +12,13 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* The max recommended size for the memory_limit setting, in bytes
*
*/
-define ('PHPSECINFO_MEMORY_LIMIT', 8*1024*1024);
+define ('PHPSECINFO_MEMORY_LIMIT', 8 * 1024 * 1024);
/**
* Test Class for memory_limit setting
@@ -29,60 +29,63 @@ class PhpSecInfo_Test_Core_Memory_Limit extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "memory_limit";
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "memory_limit";
- var $recommended_value = PHPSECINFO_MEMORY_LIMIT;
+ var $recommended_value = PHPSECINFO_MEMORY_LIMIT;
- function _retrieveCurrentValue() {
- $this->current_value = $this->returnBytes(ini_get('memory_limit'));
- }
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->returnBytes(ini_get('memory_limit'));
+ }
- /**
- * Check to see if the memory_limit setting is enabled.
- *
- * Test conditions and results:
- * OK: memory_limit enabled and set to a value of 8MB or less.
- * NOTICE: memory_limit enabled and set to a value greater than 8MB.
- * WARNING: memory_limit disabled (compile time option).
- *
- * @return integer
- */
- function _execTest() {
- if (!$this->current_value) {
- return PHPSECINFO_TEST_RESULT_WARN;
- } else if ($this->returnBytes($this->current_value) <= PHPSECINFO_MEMORY_LIMIT) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
+ /**
+ * Check to see if the memory_limit setting is enabled.
+ *
+ * Test conditions and results:
+ * OK: memory_limit enabled and set to a value of 8MB or less.
+ * NOTICE: memory_limit enabled and set to a value greater than 8MB.
+ * WARNING: memory_limit disabled (compile time option).
+ *
+ * @return integer
+ */
+ function _execTest()
+ {
+ if (!$this->current_value) {
+ return PHPSECINFO_TEST_RESULT_WARN;
+ } else if ($this->returnBytes($this->current_value) <= PHPSECINFO_MEMORY_LIMIT) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
- /**
- * Set the messages specific to this test
- *
- * @access public
- * @return null
- */
- function _setMessages() {
- parent::_setMessages();
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'memory_limit is enabled, and appears to be set
+ /**
+ * Set the messages specific to this test
+ *
+ * @access public
+ * @return null
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'memory_limit is enabled, and appears to be set
to a realistic value.');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'memory_limit is set to a very high value. Are
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'memory_limit is set to a very high value. Are
you sure your apps require this much memory? If not, lower the limit, as certain attacks or poor
programming practices can lead to exhaustion of server resources. It is recommended that you set this
to a realistic value (8M for example) from which it can be expanded as required.');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'memory_limit does not appear to be enabled. This
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'memory_limit does not appear to be enabled. This
leaves the server vulnerable to attacks that attempt to exhaust resources and creates an environment
where poor programming practices can propagate unchecked. This must be enabled at compile time by
including the parameter "--enable-memory-limit" in the configure line. Once enabled "memory_limit" may
be set in php.ini to define the maximum amount of memory a script is allowed to allocate.');
- }
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/open_basedir.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/open_basedir.php
index d5b0d282f6..4363dbdda4 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/open_basedir.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/open_basedir.php
@@ -1,7 +1,7 @@
<?php
/**
* Test Class for open_basedir
- *
+ *
* @package PhpSecInfo
* @author Ed Finkler <coj@funkatron.com>
*/
@@ -10,60 +10,63 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* Test Class for open_basedir
- *
+ *
* @package PhpSecInfo
*/
class PhpSecInfo_Test_Core_Open_Basedir extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "open_basedir";
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "open_basedir";
- var $recommended_value = TRUE;
+ var $recommended_value = TRUE;
-
- function _retrieveCurrentValue() {
- $this->current_value = $this->getBooleanIniValue('open_basedir');
- }
-
-
- /**
- * Checks to see if allow_url_fopen is enabled
- *
- */
- function _execTest() {
- if ($this->current_value == $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
-
-
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'open_basedir is enabled, which is the
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->getBooleanIniValue('open_basedir');
+ }
+
+
+ /**
+ * Checks to see if allow_url_fopen is enabled
+ *
+ */
+ function _execTest()
+ {
+ if ($this->current_value == $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
+
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'open_basedir is enabled, which is the
recommended setting. Keep in mind that other web applications not written in PHP will not
be restricted by this setting.');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'open_basedir is disabled. When
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'open_basedir is disabled. When
this is enabled, only files that are in the
given directory/directories and their subdirectories can be read by PHP scripts.
You should consider turning this on. Keep in mind that other web applications not
written in PHP will not be restricted by this setting.');
- }
-
+ }
+
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/post_max_size.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/post_max_size.php
index 09bd2474ec..c2d9b633be 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/post_max_size.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/post_max_size.php
@@ -10,13 +10,13 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* The max recommended size for the post_max_size setting, in bytes
*
*/
-define ('PHPSECINFO_POST_MAXLIMIT', 1024*256);
+define ('PHPSECINFO_POST_MAXLIMIT', 1024 * 256);
/**
* Test Class for post_max_size
@@ -26,46 +26,50 @@ define ('PHPSECINFO_POST_MAXLIMIT', 1024*256);
class PhpSecInfo_Test_Core_Post_Max_Size extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "post_max_size";
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "post_max_size";
- var $recommended_value = PHPSECINFO_POST_MAXLIMIT;
+ var $recommended_value = PHPSECINFO_POST_MAXLIMIT;
- function _retrieveCurrentValue() {
- $this->current_value = $this->returnBytes(ini_get('post_max_size'));
- }
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->returnBytes(ini_get('post_max_size'));
+ }
- /**
- * Check to see if the post_max_size setting is enabled.
- */
- function _execTest() {
+ /**
+ * Check to see if the post_max_size setting is enabled.
+ */
+ function _execTest()
+ {
- if ($this->current_value
- && $this->current_value <= $this->recommended_value
- && $post_max_size != -1) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
+ if ($this->current_value
+ && $this->current_value <= $this->recommended_value
+ && $post_max_size != -1
+ ) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'post_max_size is enabled, and appears to
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'post_max_size is enabled, and appears to
be a relatively low value');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'post_max_size is not enabled, or is set to
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'post_max_size is not enabled, or is set to
a high value. Allowing a large value may open up your server to denial-of-service attacks');
- }
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/register_globals.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/register_globals.php
index 1caa7e8e30..522813e94b 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/register_globals.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/register_globals.php
@@ -10,7 +10,7 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
@@ -21,57 +21,60 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
class PhpSecInfo_Test_Core_Register_Globals extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "register_globals";
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "register_globals";
- var $recommended_value = FALSE;
+ var $recommended_value = FALSE;
- function _retrieveCurrentValue() {
- $this->current_value = $this->getBooleanIniValue('register_globals');
- }
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->getBooleanIniValue('register_globals');
+ }
- /**
- * register_globals has been removed since PHP 6.0
- *
- * @return boolean
- */
- function isTestable() {
- return version_compare(PHP_VERSION, '6', '<') ;
- }
+ /**
+ * register_globals has been removed since PHP 6.0
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+ return version_compare(PHP_VERSION, '6', '<');
+ }
+ /**
+ * Checks to see if allow_url_fopen is enabled
+ *
+ */
+ function _execTest()
+ {
+ if ($this->current_value == $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
- /**
- * Checks to see if allow_url_fopen is enabled
- *
- */
- function _execTest() {
- if ($this->current_value == $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
+ return PHPSECINFO_TEST_RESULT_WARN;
+ }
- return PHPSECINFO_TEST_RESULT_WARN;
- }
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'You are running PHP 6 or later and register_globals has been removed');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'register_globals is disabled, which is the recommended setting');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'register_globals is enabled. This could be a serious security risk. You should disable register_globals immediately');
- }
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'You are running PHP 6 or later and register_globals has been removed');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'register_globals is disabled, which is the recommended setting');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'register_globals is enabled. This could be a serious security risk. You should disable register_globals immediately');
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/uid.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/uid.php
index cbc3ae13ec..a07f268836 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/uid.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/uid.php
@@ -10,7 +10,7 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
@@ -28,66 +28,70 @@ define ('PHPSECINFO_MIN_SAFE_UID', 100);
class PhpSecInfo_Test_Core_Uid extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "user_id";
-
- var $recommended_value = PHPSECINFO_MIN_SAFE_UID;
-
- /**
- * This test only works under Unix OSes
- *
- * @return boolean
- */
- function isTestable() {
- if ($this->osIsWindows()) {
- return false;
- } elseif ($this->getUnixId() === false) {
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'Functions required to retrieve user ID not available');
- return false;
- }
- return true;
- }
-
-
- function _retrieveCurrentValue() {
- $id = $this->getUnixId();
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "user_id";
+
+ var $recommended_value = PHPSECINFO_MIN_SAFE_UID;
+
+ /**
+ * This test only works under Unix OSes
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+ if ($this->osIsWindows()) {
+ return false;
+ } elseif ($this->getUnixId() === false) {
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'Functions required to retrieve user ID not available');
+ return false;
+ }
+ return true;
+ }
+
+
+ function _retrieveCurrentValue()
+ {
+ $id = $this->getUnixId();
if (is_array($id)) {
$this->current_value = $id['uid'];
} else {
$this->current_value = false;
}
- }
-
- /**
- * Checks the GID of the PHP process to make sure it is above PHPSECINFO_MIN_SAFE_UID
- *
- * @see PHPSECINFO_MIN_SAFE_UID
- */
- function _execTest() {
- if ($this->current_value >= $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
-
- return PHPSECINFO_TEST_RESULT_WARN;
- }
-
-
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'PHP is executing as what is probably a non-privileged user');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'PHP may be executing as a "privileged" user, which could be a serious security vulnerability.');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'This test will not run on Windows OSes');
- }
+ }
+
+ /**
+ * Checks the GID of the PHP process to make sure it is above PHPSECINFO_MIN_SAFE_UID
+ *
+ * @see PHPSECINFO_MIN_SAFE_UID
+ */
+ function _execTest()
+ {
+ if ($this->current_value >= $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ return PHPSECINFO_TEST_RESULT_WARN;
+ }
+
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'PHP is executing as what is probably a non-privileged user');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'PHP may be executing as a "privileged" user, which could be a serious security vulnerability.');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'This test will not run on Windows OSes');
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/upload_max_filesize.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/upload_max_filesize.php
index bc0b66b42b..07502a4153 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/upload_max_filesize.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/upload_max_filesize.php
@@ -9,13 +9,13 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* The max recommended size for the upload_max_filesize setting, in bytes
*
*/
-define ('PHPSECINFO_UPLOAD_MAXLIMIT', 1024*256);
+define ('PHPSECINFO_UPLOAD_MAXLIMIT', 1024 * 256);
/**
@@ -27,44 +27,48 @@ class PhpSecInfo_Test_Core_Upload_Max_Filesize extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "upload_max_filesize";
-
- var $recommended_value = PHPSECINFO_UPLOAD_MAXLIMIT;
-
- function _retrieveCurrentValue() {
- $this->current_value = $this->returnBytes(ini_get('upload_max_filesize'));
- }
-
- /**
- * Check to see if the post_max_size setting is enabled.
- */
- function _execTest() {
-
- if ($this->current_value
- && $this->current_value <= $this->recommended_value
- && $post_max_size != -1) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
-
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
-
-
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'upload_max_filesize is enabled, and appears to be a relatively low value.');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'upload_max_filesize is not enabled, or is set to a high value. Are you sure your apps require uploading files of this size? If not, lower the limit, as large file uploads can impact server performance');
- }
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "upload_max_filesize";
+
+ var $recommended_value = PHPSECINFO_UPLOAD_MAXLIMIT;
+
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->returnBytes(ini_get('upload_max_filesize'));
+ }
+
+ /**
+ * Check to see if the post_max_size setting is enabled.
+ */
+ function _execTest()
+ {
+
+ if ($this->current_value
+ && $this->current_value <= $this->recommended_value
+ && $post_max_size != -1
+ ) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
+
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'upload_max_filesize is enabled, and appears to be a relatively low value.');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'upload_max_filesize is not enabled, or is set to a high value. Are you sure your apps require uploading files of this size? If not, lower the limit, as large file uploads can impact server performance');
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Core/upload_tmp_dir.php b/plugins/SecurityInfo/PhpSecInfo/Test/Core/upload_tmp_dir.php
index e0b173dbea..4a83e0f7cd 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Core/upload_tmp_dir.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Core/upload_tmp_dir.php
@@ -9,7 +9,7 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Core.php');
/**
* Test Class for upload_tmp_dir
@@ -19,82 +19,87 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Core.php');
class PhpSecInfo_Test_Core_Upload_Tmp_Dir extends PhpSecInfo_Test_Core
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "upload_tmp_dir";
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "upload_tmp_dir";
- var $recommended_value = "A non-world readable/writable directory";
+ var $recommended_value = "A non-world readable/writable directory";
- function _retrieveCurrentValue() {
- $this->current_value = ini_get('upload_tmp_dir');
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = ini_get('upload_tmp_dir');
- if( empty($this->current_value) ) {
- if (function_exists("sys_get_temp_dir")) {
- $this->current_value = sys_get_temp_dir();
- } else {
- $this->current_value = $this->sys_get_temp_dir();
- }
- }
- }
+ if (empty($this->current_value)) {
+ if (function_exists("sys_get_temp_dir")) {
+ $this->current_value = sys_get_temp_dir();
+ } else {
+ $this->current_value = $this->sys_get_temp_dir();
+ }
+ }
+ }
- /**
- * We are disabling this function on Windows OSes right now until
- * we can be certain of the proper way to check world-readability
- *
- * @return unknown
- */
- function isTestable() {
- if ($this->osIsWindows()) {
- return FALSE;
- } else {
- return TRUE;
- }
- }
+ /**
+ * We are disabling this function on Windows OSes right now until
+ * we can be certain of the proper way to check world-readability
+ *
+ * @return unknown
+ */
+ function isTestable()
+ {
+ if ($this->osIsWindows()) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+ }
- /**
- * Check if upload_tmp_dir matches PHPSECINFO_TEST_COMMON_TMPDIR, or is word-writable
- *
- * This is still unix-specific, and it's possible that for now
- * this test should be disabled under Windows builds.
- *
- * @see PHPSECINFO_TEST_COMMON_TMPDIR
- */
- function _execTest() {
+ /**
+ * Check if upload_tmp_dir matches PHPSECINFO_TEST_COMMON_TMPDIR, or is word-writable
+ *
+ * This is still unix-specific, and it's possible that for now
+ * this test should be disabled under Windows builds.
+ *
+ * @see PHPSECINFO_TEST_COMMON_TMPDIR
+ */
+ function _execTest()
+ {
- $perms = @fileperms($this->current_value);
- if ($perms === false) {
- return PHPSECINFO_TEST_RESULT_WARN;
- } else if ($this->current_value
- && !preg_match("|".PHPSECINFO_TEST_COMMON_TMPDIR."/?|", $this->current_value)
- && ! ($perms & 0x0004)
- && ! ($perms & 0x0002) ) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
+ $perms = @fileperms($this->current_value);
+ if ($perms === false) {
+ return PHPSECINFO_TEST_RESULT_WARN;
+ } else if ($this->current_value
+ && !preg_match("|" . PHPSECINFO_TEST_COMMON_TMPDIR . "/?|", $this->current_value)
+ && !($perms & 0x0004)
+ && !($perms & 0x0002)
+ ) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
- // rewrite current_value to display perms
- $this->current_value .= " (".substr(sprintf('%o', $perms), -4).")";
+ // rewrite current_value to display perms
+ $this->current_value .= " (" . substr(sprintf('%o', $perms), -4) . ")";
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'Test not run -- currently disabled on Windows OSes');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'upload_tmp_dir is enabled, which is the
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'Test not run -- currently disabled on Windows OSes');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'upload_tmp_dir is enabled, which is the
recommended setting. Make sure your upload_tmp_dir path is not world-readable');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'unable to retrieve file permissions on upload_tmp_dir');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'upload_tmp_dir is disabled, or is set to a
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'unable to retrieve file permissions on upload_tmp_dir');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'upload_tmp_dir is disabled, or is set to a
common world-writable directory. This typically allows other users on this server
to access temporary copies of files uploaded via your PHP scripts. You should set
upload_tmp_dir to a non-world-readable directory');
- }
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Curl/file_support.php b/plugins/SecurityInfo/PhpSecInfo/Test/Curl/file_support.php
index 816d747259..b02428f62f 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Curl/file_support.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Curl/file_support.php
@@ -9,7 +9,7 @@
/**
* require the PhpSecInfo_Test_Curl class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Curl.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Curl.php');
/**
* Test class for CURL file_support
@@ -24,52 +24,54 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Curl.php');
class PhpSecInfo_Test_Curl_File_Support extends PhpSecInfo_Test_Curl
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "file_support";
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "file_support";
- var $recommended_value = '5.1.6+ or 4.4.4+';
+ var $recommended_value = '5.1.6+ or 4.4.4+';
- function _retrieveCurrentValue() {
- $this->current_value = PHP_VERSION;
- }
-
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = PHP_VERSION;
+ }
- /**
- * Checks to see if libcurl's "file://" support is enabled by examining the "protocols" array
- * in the info returned from curl_version()
- * @return integer
- *
- */
- function _execTest() {
- $curlinfo = curl_version();
+ /**
+ * Checks to see if libcurl's "file://" support is enabled by examining the "protocols" array
+ * in the info returned from curl_version()
+ * @return integer
+ *
+ */
+ function _execTest()
+ {
- if ( version_compare($this->current_value, '5.1.6', '>=') ||
- (version_compare($this->current_value, '4.4.4', '>=')) && ( version_compare($this->current_value, '5', '<') )
- ) {
- return PHPSECINFO_TEST_RESULT_OK;
- } else {
- return PHPSECINFO_TEST_RESULT_WARN;
- }
+ $curlinfo = curl_version();
- }
+ if (version_compare($this->current_value, '5.1.6', '>=') ||
+ (version_compare($this->current_value, '4.4.4', '>=')) && (version_compare($this->current_value, '5', '<'))
+ ) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ } else {
+ return PHPSECINFO_TEST_RESULT_WARN;
+ }
+ }
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "You are running PHP 4.4.4 or higher, or PHP 5.1.6 or higher. These versions fix the security hole present in the cURL functions that allow it to bypass safe_mode and open_basedir restrictions.");
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', "A security hole present in your version of PHP allows the cURL functions to bypass safe_mode and open_basedir restrictions. You should upgrade to the latest version of PHP.");
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "You are running PHP 4.4.4 or higher, or PHP 5.1.6 or higher. These versions fix the security hole present in the cURL functions that allow it to bypass safe_mode and open_basedir restrictions.");
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', "A security hole present in your version of PHP allows the cURL functions to bypass safe_mode and open_basedir restrictions. You should upgrade to the latest version of PHP.");
- }
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Session/save_path.php b/plugins/SecurityInfo/PhpSecInfo/Test/Session/save_path.php
index 910e5c7fd2..f04d087802 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Session/save_path.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Session/save_path.php
@@ -9,7 +9,7 @@
/**
* require the PhpSecInfo_Test_Core class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Session.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Session.php');
/**
* Test class for session save_path
@@ -19,87 +19,92 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Session.php');
class PhpSecInfo_Test_Session_Save_Path extends PhpSecInfo_Test_Session
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "save_path";
-
- var $recommended_value = "A non-world readable/writable directory";
-
- function _retrieveCurrentValue() {
- $this->current_value = ini_get('session.save_path');
-
- if( empty($this->current_value) ) {
- if (function_exists("sys_get_temp_dir")) {
- $this->current_value = sys_get_temp_dir();
- } else {
- $this->current_value = $this->sys_get_temp_dir();
- }
- }
-
- if( preg_match('/^[0-9]+;(.+)/', $this->current_value, $matches) ) {
- $this->current_value = $matches[1];
- }
- }
-
-
- /**
- * We are disabling this function on Windows OSes right now until
- * we can be certain of the proper way to check world-readability
- *
- * @return unknown
- */
- function isTestable() {
- if ($this->osIsWindows()) {
- return FALSE;
- } else {
- return TRUE;
- }
- }
-
-
- /**
- * Check if session.save_path matches PHPSECINFO_TEST_COMMON_TMPDIR, or is word-writable
- *
- * This is still unix-specific, and it's possible that for now
- * this test should be disabled under Windows builds.
- *
- * @see PHPSECINFO_TEST_COMMON_TMPDIR
- */
- function _execTest() {
-
- $perms = @fileperms($this->current_value);
- if ($perms === false) {
- return PHPSECINFO_TEST_RESULT_WARN;
- } else if ($this->current_value
- && !preg_match("|".PHPSECINFO_TEST_COMMON_TMPDIR."/?|", $this->current_value)
- && ! ($perms & 0x0004)
- && ! ($perms & 0x0002) ) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
-
- // rewrite current_value to display perms
- $this->current_value .= " (".substr(sprintf('%o', $perms), -4).")";
-
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
-
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'Test not run -- currently disabled on Windows OSes');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'save_path is enabled, which is the
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "save_path";
+
+ var $recommended_value = "A non-world readable/writable directory";
+
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = ini_get('session.save_path');
+
+ if (empty($this->current_value)) {
+ if (function_exists("sys_get_temp_dir")) {
+ $this->current_value = sys_get_temp_dir();
+ } else {
+ $this->current_value = $this->sys_get_temp_dir();
+ }
+ }
+
+ if (preg_match('/^[0-9]+;(.+)/', $this->current_value, $matches)) {
+ $this->current_value = $matches[1];
+ }
+ }
+
+
+ /**
+ * We are disabling this function on Windows OSes right now until
+ * we can be certain of the proper way to check world-readability
+ *
+ * @return unknown
+ */
+ function isTestable()
+ {
+ if ($this->osIsWindows()) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+ }
+
+
+ /**
+ * Check if session.save_path matches PHPSECINFO_TEST_COMMON_TMPDIR, or is word-writable
+ *
+ * This is still unix-specific, and it's possible that for now
+ * this test should be disabled under Windows builds.
+ *
+ * @see PHPSECINFO_TEST_COMMON_TMPDIR
+ */
+ function _execTest()
+ {
+
+ $perms = @fileperms($this->current_value);
+ if ($perms === false) {
+ return PHPSECINFO_TEST_RESULT_WARN;
+ } else if ($this->current_value
+ && !preg_match("|" . PHPSECINFO_TEST_COMMON_TMPDIR . "/?|", $this->current_value)
+ && !($perms & 0x0004)
+ && !($perms & 0x0002)
+ ) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
+
+ // rewrite current_value to display perms
+ $this->current_value .= " (" . substr(sprintf('%o', $perms), -4) . ")";
+
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
+
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'Test not run -- currently disabled on Windows OSes');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'save_path is enabled, which is the
recommended setting. Make sure your save_path path is not world-readable');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'unable to retrieve file permissions on save_path');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'save_path is disabled, or is set to a
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'unable to retrieve file permissions on save_path');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'save_path is disabled, or is set to a
common world-writable directory. This typically allows other users on this server
to access session files. You should set save_path to a non-world-readable directory');
- }
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Session/use_trans_sid.php b/plugins/SecurityInfo/PhpSecInfo/Test/Session/use_trans_sid.php
index f7f3f2e4a9..c3a61e5d59 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Session/use_trans_sid.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Session/use_trans_sid.php
@@ -10,7 +10,7 @@
/**
* require the PhpSecInfo_Test_Session class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Session.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Session.php');
/**
* Test class for session use_trans_sid
@@ -21,46 +21,49 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Session.php');
class PhpSecInfo_Test_Session_Use_Trans_Sid extends PhpSecInfo_Test_Session
{
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = "use_trans_sid";
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = "use_trans_sid";
- var $recommended_value = FALSE;
+ var $recommended_value = FALSE;
- function _retrieveCurrentValue() {
- $this->current_value = $this->getBooleanIniValue('session.use_trans_sid');
- }
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = $this->getBooleanIniValue('session.use_trans_sid');
+ }
- /**
- * Checks to see if allow_url_fopen is enabled
- *
- */
- function _execTest() {
- if ($this->current_value == $this->recommended_value) {
- return PHPSECINFO_TEST_RESULT_OK;
- }
+ /**
+ * Checks to see if allow_url_fopen is enabled
+ *
+ */
+ function _execTest()
+ {
+ if ($this->current_value == $this->recommended_value) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ }
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
- /**
- * Set the messages specific to this test
- *
- */
- function _setMessages() {
- parent::_setMessages();
+ /**
+ * Set the messages specific to this test
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'use_trans_sid is disabled, which is the recommended setting');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'use_trans_sid is enabled. This makes session hijacking easier. Consider disabling this feature');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'use_trans_sid is disabled, which is the recommended setting');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'use_trans_sid is enabled. This makes session hijacking easier. Consider disabling this feature');
- }
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Suhosin/extension.php b/plugins/SecurityInfo/PhpSecInfo/Test/Suhosin/extension.php
index 50e4d05359..f6a0d340d2 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Suhosin/extension.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Suhosin/extension.php
@@ -9,7 +9,7 @@
/**
* require the PhpSecInfo_Test_Suhosin class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Suhosin.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Suhosin.php');
/**
* Test class for Suhosin extension
@@ -21,26 +21,29 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Suhosin.php');
*/
class PhpSecInfo_Test_Suhosin_Extension extends PhpSecInfo_Test_Suhosin
{
- var $test_name = "Suhosin extension";
+ var $test_name = "Suhosin extension";
- var $recommended_value = true;
+ var $recommended_value = true;
- function _retrieveCurrentValue() {
- $this->current_value = extension_loaded('suhosin');
- }
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = extension_loaded('suhosin');
+ }
- function _execTest() {
- if ( $this->current_value === true ) {
- return PHPSECINFO_TEST_RESULT_OK;
- } else {
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
- }
+ function _execTest()
+ {
+ if ($this->current_value === true) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ } else {
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
+ }
- function _setMessages() {
- parent::_setMessages();
+ function _setMessages()
+ {
+ parent::_setMessages();
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "You are running PHP with the Suhosin extension loaded. This extension provides high-level runtime protections, and additional filtering and logging features.");
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "You are not running PHP with the Suhosin extension loaded. We recommend both the patch and extension for low- and high-level protections including transparent cookie encryption and remote inclusion vulnerabilities.");
- }
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "You are running PHP with the Suhosin extension loaded. This extension provides high-level runtime protections, and additional filtering and logging features.");
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "You are not running PHP with the Suhosin extension loaded. We recommend both the patch and extension for low- and high-level protections including transparent cookie encryption and remote inclusion vulnerabilities.");
+ }
}
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Suhosin/patch.php b/plugins/SecurityInfo/PhpSecInfo/Test/Suhosin/patch.php
index 6e982edae2..ec475281bd 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Suhosin/patch.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Suhosin/patch.php
@@ -9,7 +9,7 @@
/**
* require the PhpSecInfo_Test_Suhosin class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Suhosin.php');
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test_Suhosin.php');
/**
* Test class for Suhosin
@@ -21,35 +21,38 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test_Suhosin.php');
*/
class PhpSecInfo_Test_Suhosin_Patch extends PhpSecInfo_Test_Suhosin
{
- var $test_name = "Suhosin patch";
-
- var $recommended_value = true;
-
- function _retrieveCurrentValue() {
- if (preg_match('/Suhosin/', $_SERVER['SERVER_SOFTWARE'])) {
- $this->current_value = true;
- } else {
- $this->current_value = false;
-
- $constants = get_defined_constants();
- if(isset($constants['SUHOSIN_PATCH']) && $constants['SUHOSIN_PATCH'] == 1) {
- $this->current_value = true;
- }
- }
- }
-
- function _execTest() {
- if ( $this->current_value === true ) {
- return PHPSECINFO_TEST_RESULT_OK;
- } else {
- return PHPSECINFO_TEST_RESULT_NOTICE;
- }
- }
-
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "You are running PHP with the Suhosin patch applied against the PHP core. This patch implements various low-level protections against (for example) buffer overflows and format string vulnerabilities.");
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "You are not running PHP with the Suhosin patch applied. We recommend both the patch and extension for low- and high-level protections against (for example) buffer overflows and format string vulnerabilities.");
- }
+ var $test_name = "Suhosin patch";
+
+ var $recommended_value = true;
+
+ function _retrieveCurrentValue()
+ {
+ if (preg_match('/Suhosin/', $_SERVER['SERVER_SOFTWARE'])) {
+ $this->current_value = true;
+ } else {
+ $this->current_value = false;
+
+ $constants = get_defined_constants();
+ if (isset($constants['SUHOSIN_PATCH']) && $constants['SUHOSIN_PATCH'] == 1) {
+ $this->current_value = true;
+ }
+ }
+ }
+
+ function _execTest()
+ {
+ if ($this->current_value === true) {
+ return PHPSECINFO_TEST_RESULT_OK;
+ } else {
+ return PHPSECINFO_TEST_RESULT_NOTICE;
+ }
+ }
+
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', "You are running PHP with the Suhosin patch applied against the PHP core. This patch implements various low-level protections against (for example) buffer overflows and format string vulnerabilities.");
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', "You are not running PHP with the Suhosin patch applied. We recommend both the patch and extension for low- and high-level protections against (for example) buffer overflows and format string vulnerabilities.");
+ }
}
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Test.php b/plugins/SecurityInfo/PhpSecInfo/Test/Test.php
index b4902a718c..978309879d 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Test.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Test.php
@@ -9,8 +9,7 @@
/**
* require the main PhpSecInfo class
*/
-require_once(PHPSECINFO_BASE_DIR.'/PhpSecInfo.php');
-
+require_once(PHPSECINFO_BASE_DIR . '/PhpSecInfo.php');
define ('PHPSECINFO_TEST_RESULT_OK', -1);
@@ -36,540 +35,554 @@ define ('PHPSECINFO_TEST_MOREINFO_BASEURL', 'http://phpsec.org/projects/phpsecin
class PhpSecInfo_Test
{
- /**
- * This value is used to group test results together.
- *
- * For example, all tests related to the mysql lib should be grouped under "mysql."
- *
- * @var string
- */
- var $test_group = 'misc';
-
-
- /**
- * This should be a <b>unique</b>, human-readable identifier for this test
- *
- * @var string
- */
- var $test_name = 'misc_test';
-
-
- /**
- * This is the recommended value the test will be looking for
- *
- * @var mixed
- */
- var $recommended_value = "bar";
-
-
- /**
- * The result returned from the test
- *
- * @var integer
- */
- var $_result = PHPSECINFO_TEST_RESULT_NOTRUN;
-
-
- /**
- * The message corresponding to the result of the test
- *
- * @var string
- */
- var $_message;
-
-
- /**
- * the language code. Should be a pointer to the setting in the PhpSecInfo object
- *
- * @var string
- */
- var $_language = PHPSECINFO_LANG_DEFAULT;
-
- /**
- * Enter description here...
- *
- * @var mixed
- */
- var $current_value;
-
- /**
- * This is a hash of messages that correspond to various test result levels.
- *
- * There are five messages, each corresponding to one of the result constants
- * (PHPSECINFO_TEST_RESULT_OK, PHPSECINFO_TEST_RESULT_NOTICE, PHPSECINFO_TEST_RESULT_WARN,
- * PHPSECINFO_TEST_RESULT_ERROR, PHPSECINFO_TEST_RESULT_NOTRUN)
- *
- *
- * @var array array
- */
- var $_messages = array();
-
-
-
-
- /**
- * Constructor for Test skeleton class
- *
- * @return PhpSecInfo_Test
- */
- function PhpSecInfo_Test() {
- //$this->_setTestValues();
-
- $this->_retrieveCurrentValue();
- //$this->setRecommendedValue();
-
- $this->_setMessages();
- }
-
-
- /**
- * Determines whether or not it's appropriate to run this test (for example, if
- * this test is for a particular library, it shouldn't be run if the lib isn't
- * loaded).
- *
- * This is a terrible name, but I couldn't think of a better one atm.
- *
- * @return boolean
- */
- function isTestable() {
-
- return true;
- }
-
-
- /**
- * The "meat" of the test. This is where the real test code goes. You should override this when extending
- *
- * @return integer
- */
- function _execTest() {
-
- return PHPSECINFO_TEST_RESULT_NOTRUN;
- }
-
-
- /**
- * This function loads up result messages into the $this->_messages array.
- *
- * Using this method rather than setting $this->_messages directly allows result
- * messages to be inherited. This is broken out into a separate function rather
- * than the constructor for ease of extension purposes (php4 is whack, man).
- *
- */
- function _setMessages() {
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'This setting should be safe');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'This could potentially be a security issue');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'This setting may be a serious security problem');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_ERROR, 'en', 'There was an error running this test');
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'This test cannot be run');
- }
-
-
- /**
- * Placeholder - extend for tests
- *
- */
- function _retrieveCurrentValue() {
- $this->current_value = "foo";
- }
-
-
-
- /**
- * This is the wrapper that executes the test and sets the result code and message
- */
- function test() {
- $result = $this->_execTest();
- $this->_setResult($result);
-
- }
-
-
-
- /**
- * Retrieves the result
- *
- * @return integer
- */
- function getResult() {
- return $this->_result;
- }
-
-
-
-
- /**
- * Retrieves the message for the current result
- *
- * @return string
- */
- function getMessage() {
- if (!isset($this->_message) || empty($this->_message)) {
- $this->_setMessage($this->_result, $this->_language);
- }
-
- return $this->_message;
- }
-
-
-
- /**
- * Sets the message for a given result code and language
- *
- * <code>
- * $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'This test cannot be run');
- * </code>
- *
- * @param integer $result_code
- * @param string $language_code
- * @param string $message
- *
- */
- function setMessageForResult($result_code, $language_code, $message) {
-
- if ( !isset($this->_messages[$result_code]) ) {
- $this->_messages[$result_code] = array();
- }
-
- if ( !is_array($this->_messages[$result_code]) ) {
- $this->_messages[$result_code] = array();
- }
-
- $this->_messages[$result_code][$language_code] = $message;
-
- }
-
-
-
-
- /**
- * returns the current value. This function should be used to access the
- * value for display. All values are cast as strings
- *
- * @return string
- */
- function getCurrentTestValue() {
- return $this->getStringValue($this->current_value);
- }
-
- /**
- * returns the recommended value. This function should be used to access the
- * value for display. All values are cast as strings
- *
- * @return string
- */
- function getRecommendedTestValue() {
- return $this->getStringValue($this->recommended_value);
- }
-
-
- /**
- * Sets the result code
- *
- * @param integer $result_code
- */
- function _setResult($result_code) {
- $this->_result = $result_code;
- }
-
-
- /**
- * Sets the $this->_message variable based on the passed result and language codes
- *
- * @param integer $result_code
- * @param string $language_code
- */
- function _setMessage($result_code, $language_code) {
- $messages = $this->_messages[$result_code];
- $message = $messages[$language_code];
- $this->_message = $message;
- }
-
-
- /**
- * Returns a link to a page with detailed information about the test
- *
- * URL is formatted as PHPSECINFO_TEST_MOREINFO_BASEURL + testName
- *
- * @see PHPSECINFO_TEST_MOREINFO_BASEURL
- *
- * @return string|boolean
- */
- function getMoreInfoURL() {
- if ($tn = $this->getTestName()) {
- return PHPSECINFO_TEST_MOREINFO_BASEURL.strtolower("{$tn}.html");
- } else {
- return false;
- }
- }
-
-
-
-
- /**
- * This retrieves the name of this test.
- *
- * If a name has not been set, this returns a formatted version of the class name.
- *
- * @return string
- */
- function getTestName() {
- if (isset($this->test_name) && !empty($this->test_name)) {
- return $this->test_name;
- } else {
- return ucwords(
- str_replace('_', ' ',
- get_class($this)
- )
- );
- }
-
- }
-
-
- /**
- * sets the test name
- *
- * @param string $test_name
- */
- function setTestName($test_name) {
- $this->test_name = $test_name;
- }
-
-
- /**
- * Returns the test group this test belongs to
- *
- * @return string
- */
- function getTestGroup() {
- return $this->test_group;
- }
-
-
- /**
- * sets the test group
- *
- * @param string $test_group
- */
- function setTestGroup($test_group) {
- $this->test_group = $test_group;
- }
-
-
- /**
- * This function takes the shorthand notation used in memory limit settings for PHP
- * and returns the byte value. Totally stolen from http://us3.php.net/manual/en/function.ini-get.php
- *
- * <code>
- * echo 'post_max_size in bytes = ' . $this->return_bytes(ini_get('post_max_size'));
- * </code>
- *
- * @link http://php.net/manual/en/function.ini-get.php
- * @param string $val
- * @return integer
- */
- function returnBytes($val) {
- $val = trim($val);
-
- if ( (int)$val === 0 ) {
- return 0;
- }
-
- $last = strtolower($val{strlen($val)-1});
- switch($last) {
- // The 'G' modifier is available since PHP 5.1.0
- case 'g':
- $val *= 1024;
- case 'm':
- $val *= 1024;
- case 'k':
- $val *= 1024;
- }
-
- return $val;
- }
-
-
- /**
- * This just does the usual PHP string casting, except for
- * the boolean FALSE value, where the string "0" is returned
- * instead of an empty string
- *
- * @param mixed $val
- * @return string
- */
- function getStringValue($val) {
- if ($val === FALSE) {
- return "0";
- } else {
- return (string)$val;
- }
- }
-
-
- /**
- * This method converts the several possible return values from
- * allegedly "boolean" ini settings to proper booleans
- *
- * Properly converted input values are: 'off', 'on', 'false', 'true', '', '0', '1'
- * (the last two might not be neccessary, but I'd rather be safe)
- *
- * If the ini_value doesn't match any of those, the value is returned as-is.
- *
- * @param string $ini_key the ini_key you need the value of
- * @return boolean|mixed
- */
- function getBooleanIniValue($ini_key) {
-
- $ini_val = ini_get($ini_key);
-
- switch ( strtolower($ini_val) ) {
-
- case 'off':
- return false;
- break;
- case 'on':
- return true;
- break;
- case 'false':
- return false;
- break;
- case 'true':
- return true;
- break;
- case '0':
- return false;
- break;
- case '1':
- return true;
- break;
- case '':
- return false;
- break;
- default:
- return $ini_val;
-
- }
-
- }
-
- /**
- * sys_get_temp_dir provides some temp dir detection capability
- * that is lacking in versions of PHP that do not have the
- * sys_get_temp_dir() function
- *
- * @return string|NULL
- */
- function sys_get_temp_dir() {
- // Try to get from environment variable
- $vars = array('TMP', 'TMPDIR', 'TEMP');
- foreach($vars as $var) {
- $tmp = getenv($var);
- if ( !empty($tmp) ) {
- return realpath( $tmp );
- }
- }
- return NULL;
- }
-
-
- /**
- * A quick function to determine whether we're running on Windows.
- * Uses the PHP_OS constant.
- *
- * @return boolean
- */
- function osIsWindows() {
- if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
- return true;
- } else {
- return false;
- }
- }
-
-
- /**
- * Returns an array of data returned from the UNIX 'id' command
- *
- * includes uid, username, gid, groupname, and groups (if "exec"
- * is enabled). Groups is an array of all the groups the user
- * belongs to. Keys are the group ids, values are the group names.
- *
- * returns FALSE if no suitable function is available to retrieve
- * the data
- *
- * @return array|boolean
- */
- function getUnixId() {
-
- if ($this->osIsWindows()) {
- return false;
- }
-
- $success = false;
-
-
- if (function_exists("exec") && !PhpSecInfo_Test::getBooleanIniValue('safe_mode')) {
- $id_raw = exec('id');
- // uid=1000(coj) gid=1000(coj) groups=1000(coj),1001(admin)
- preg_match( "|uid=(\d+)\((\S+)\)\s+gid=(\d+)\((\S+)\)\s+groups=(.+)|i",
- $id_raw,
- $matches);
-
- if (!$matches) {
- /**
- * for some reason the output from 'id' wasn't as we expected.
- * return false so the test doesn't run.
- */
- $success = false;
- } else {
- $id_data = array( 'uid'=>$matches[1],
- 'username'=>$matches[2],
- 'gid'=>$matches[3],
- 'group'=>$matches[4] );
-
- $groups = array();
- if ($matches[5]) {
- $gs = $matches[5];
- $gs = explode(',', $gs);
- foreach ($gs as $groupstr) {
- if (preg_match("/(\d+)\(([^\)]+)\)/", $groupstr, $subs)) {
- $groups[$subs[1]] = $subs[2];
- } else {
- $groups[$groupstr] = '';
- }
- }
- ksort($groups);
- }
- $id_data['groups'] = $groups;
- $success = true;
- }
-
- }
-
- if (!$success && function_exists("posix_getpwuid") && function_exists("posix_geteuid")
- && function_exists('posix_getgrgid') && function_exists('posix_getgroups') ) {
- $data = posix_getpwuid( posix_getuid() );
- $id_data['uid'] = $data['uid'];
- $id_data['username'] = $data['name'];
- $id_data['gid'] = $data['gid'];
- //$group_data = posix_getgrgid( posix_getegid() );
- //$id_data['group'] = $group_data['name'];
- $id_data['groups'] = array();
- $groups = posix_getgroups();
- foreach ( $groups as $gid ) {
- //$group_data = posix_getgrgid(posix_getgid());
- $id_data['groups'][$gid] = '<unknown>';
- }
- $success = true;
- }
-
- if ($success) {
- return $id_data;
- } else {
- return false;
- }
- }
+ /**
+ * This value is used to group test results together.
+ *
+ * For example, all tests related to the mysql lib should be grouped under "mysql."
+ *
+ * @var string
+ */
+ var $test_group = 'misc';
+
+
+ /**
+ * This should be a <b>unique</b>, human-readable identifier for this test
+ *
+ * @var string
+ */
+ var $test_name = 'misc_test';
+
+
+ /**
+ * This is the recommended value the test will be looking for
+ *
+ * @var mixed
+ */
+ var $recommended_value = "bar";
+
+
+ /**
+ * The result returned from the test
+ *
+ * @var integer
+ */
+ var $_result = PHPSECINFO_TEST_RESULT_NOTRUN;
+
+
+ /**
+ * The message corresponding to the result of the test
+ *
+ * @var string
+ */
+ var $_message;
+
+
+ /**
+ * the language code. Should be a pointer to the setting in the PhpSecInfo object
+ *
+ * @var string
+ */
+ var $_language = PHPSECINFO_LANG_DEFAULT;
+
+ /**
+ * Enter description here...
+ *
+ * @var mixed
+ */
+ var $current_value;
+
+ /**
+ * This is a hash of messages that correspond to various test result levels.
+ *
+ * There are five messages, each corresponding to one of the result constants
+ * (PHPSECINFO_TEST_RESULT_OK, PHPSECINFO_TEST_RESULT_NOTICE, PHPSECINFO_TEST_RESULT_WARN,
+ * PHPSECINFO_TEST_RESULT_ERROR, PHPSECINFO_TEST_RESULT_NOTRUN)
+ *
+ *
+ * @var array array
+ */
+ var $_messages = array();
+
+
+ /**
+ * Constructor for Test skeleton class
+ *
+ * @return PhpSecInfo_Test
+ */
+ function PhpSecInfo_Test()
+ {
+ //$this->_setTestValues();
+
+ $this->_retrieveCurrentValue();
+ //$this->setRecommendedValue();
+
+ $this->_setMessages();
+ }
+
+
+ /**
+ * Determines whether or not it's appropriate to run this test (for example, if
+ * this test is for a particular library, it shouldn't be run if the lib isn't
+ * loaded).
+ *
+ * This is a terrible name, but I couldn't think of a better one atm.
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+
+ return true;
+ }
+
+
+ /**
+ * The "meat" of the test. This is where the real test code goes. You should override this when extending
+ *
+ * @return integer
+ */
+ function _execTest()
+ {
+
+ return PHPSECINFO_TEST_RESULT_NOTRUN;
+ }
+
+
+ /**
+ * This function loads up result messages into the $this->_messages array.
+ *
+ * Using this method rather than setting $this->_messages directly allows result
+ * messages to be inherited. This is broken out into a separate function rather
+ * than the constructor for ease of extension purposes (php4 is whack, man).
+ *
+ */
+ function _setMessages()
+ {
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_OK, 'en', 'This setting should be safe');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTICE, 'en', 'This could potentially be a security issue');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_WARN, 'en', 'This setting may be a serious security problem');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_ERROR, 'en', 'There was an error running this test');
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'This test cannot be run');
+ }
+
+
+ /**
+ * Placeholder - extend for tests
+ *
+ */
+ function _retrieveCurrentValue()
+ {
+ $this->current_value = "foo";
+ }
+
+
+ /**
+ * This is the wrapper that executes the test and sets the result code and message
+ */
+ function test()
+ {
+ $result = $this->_execTest();
+ $this->_setResult($result);
+
+ }
+
+
+ /**
+ * Retrieves the result
+ *
+ * @return integer
+ */
+ function getResult()
+ {
+ return $this->_result;
+ }
+
+
+ /**
+ * Retrieves the message for the current result
+ *
+ * @return string
+ */
+ function getMessage()
+ {
+ if (!isset($this->_message) || empty($this->_message)) {
+ $this->_setMessage($this->_result, $this->_language);
+ }
+
+ return $this->_message;
+ }
+
+
+ /**
+ * Sets the message for a given result code and language
+ *
+ * <code>
+ * $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', 'This test cannot be run');
+ * </code>
+ *
+ * @param integer $result_code
+ * @param string $language_code
+ * @param string $message
+ *
+ */
+ function setMessageForResult($result_code, $language_code, $message)
+ {
+
+ if (!isset($this->_messages[$result_code])) {
+ $this->_messages[$result_code] = array();
+ }
+
+ if (!is_array($this->_messages[$result_code])) {
+ $this->_messages[$result_code] = array();
+ }
+
+ $this->_messages[$result_code][$language_code] = $message;
+
+ }
+
+
+ /**
+ * returns the current value. This function should be used to access the
+ * value for display. All values are cast as strings
+ *
+ * @return string
+ */
+ function getCurrentTestValue()
+ {
+ return $this->getStringValue($this->current_value);
+ }
+
+ /**
+ * returns the recommended value. This function should be used to access the
+ * value for display. All values are cast as strings
+ *
+ * @return string
+ */
+ function getRecommendedTestValue()
+ {
+ return $this->getStringValue($this->recommended_value);
+ }
+
+
+ /**
+ * Sets the result code
+ *
+ * @param integer $result_code
+ */
+ function _setResult($result_code)
+ {
+ $this->_result = $result_code;
+ }
+
+
+ /**
+ * Sets the $this->_message variable based on the passed result and language codes
+ *
+ * @param integer $result_code
+ * @param string $language_code
+ */
+ function _setMessage($result_code, $language_code)
+ {
+ $messages = $this->_messages[$result_code];
+ $message = $messages[$language_code];
+ $this->_message = $message;
+ }
+
+
+ /**
+ * Returns a link to a page with detailed information about the test
+ *
+ * URL is formatted as PHPSECINFO_TEST_MOREINFO_BASEURL + testName
+ *
+ * @see PHPSECINFO_TEST_MOREINFO_BASEURL
+ *
+ * @return string|boolean
+ */
+ function getMoreInfoURL()
+ {
+ if ($tn = $this->getTestName()) {
+ return PHPSECINFO_TEST_MOREINFO_BASEURL . strtolower("{$tn}.html");
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * This retrieves the name of this test.
+ *
+ * If a name has not been set, this returns a formatted version of the class name.
+ *
+ * @return string
+ */
+ function getTestName()
+ {
+ if (isset($this->test_name) && !empty($this->test_name)) {
+ return $this->test_name;
+ } else {
+ return ucwords(
+ str_replace('_', ' ',
+ get_class($this)
+ )
+ );
+ }
+
+ }
+
+
+ /**
+ * sets the test name
+ *
+ * @param string $test_name
+ */
+ function setTestName($test_name)
+ {
+ $this->test_name = $test_name;
+ }
+
+
+ /**
+ * Returns the test group this test belongs to
+ *
+ * @return string
+ */
+ function getTestGroup()
+ {
+ return $this->test_group;
+ }
+
+
+ /**
+ * sets the test group
+ *
+ * @param string $test_group
+ */
+ function setTestGroup($test_group)
+ {
+ $this->test_group = $test_group;
+ }
+
+
+ /**
+ * This function takes the shorthand notation used in memory limit settings for PHP
+ * and returns the byte value. Totally stolen from http://us3.php.net/manual/en/function.ini-get.php
+ *
+ * <code>
+ * echo 'post_max_size in bytes = ' . $this->return_bytes(ini_get('post_max_size'));
+ * </code>
+ *
+ * @link http://php.net/manual/en/function.ini-get.php
+ * @param string $val
+ * @return integer
+ */
+ function returnBytes($val)
+ {
+ $val = trim($val);
+
+ if ((int)$val === 0) {
+ return 0;
+ }
+
+ $last = strtolower($val{strlen($val) - 1});
+ switch ($last) {
+ // The 'G' modifier is available since PHP 5.1.0
+ case 'g':
+ $val *= 1024;
+ case 'm':
+ $val *= 1024;
+ case 'k':
+ $val *= 1024;
+ }
+
+ return $val;
+ }
+
+
+ /**
+ * This just does the usual PHP string casting, except for
+ * the boolean FALSE value, where the string "0" is returned
+ * instead of an empty string
+ *
+ * @param mixed $val
+ * @return string
+ */
+ function getStringValue($val)
+ {
+ if ($val === FALSE) {
+ return "0";
+ } else {
+ return (string)$val;
+ }
+ }
+
+
+ /**
+ * This method converts the several possible return values from
+ * allegedly "boolean" ini settings to proper booleans
+ *
+ * Properly converted input values are: 'off', 'on', 'false', 'true', '', '0', '1'
+ * (the last two might not be neccessary, but I'd rather be safe)
+ *
+ * If the ini_value doesn't match any of those, the value is returned as-is.
+ *
+ * @param string $ini_key the ini_key you need the value of
+ * @return boolean|mixed
+ */
+ function getBooleanIniValue($ini_key)
+ {
+
+ $ini_val = ini_get($ini_key);
+
+ switch (strtolower($ini_val)) {
+
+ case 'off':
+ return false;
+ break;
+ case 'on':
+ return true;
+ break;
+ case 'false':
+ return false;
+ break;
+ case 'true':
+ return true;
+ break;
+ case '0':
+ return false;
+ break;
+ case '1':
+ return true;
+ break;
+ case '':
+ return false;
+ break;
+ default:
+ return $ini_val;
+
+ }
+
+ }
+
+ /**
+ * sys_get_temp_dir provides some temp dir detection capability
+ * that is lacking in versions of PHP that do not have the
+ * sys_get_temp_dir() function
+ *
+ * @return string|NULL
+ */
+ function sys_get_temp_dir()
+ {
+ // Try to get from environment variable
+ $vars = array('TMP', 'TMPDIR', 'TEMP');
+ foreach ($vars as $var) {
+ $tmp = getenv($var);
+ if (!empty($tmp)) {
+ return realpath($tmp);
+ }
+ }
+ return NULL;
+ }
+
+
+ /**
+ * A quick function to determine whether we're running on Windows.
+ * Uses the PHP_OS constant.
+ *
+ * @return boolean
+ */
+ function osIsWindows()
+ {
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Returns an array of data returned from the UNIX 'id' command
+ *
+ * includes uid, username, gid, groupname, and groups (if "exec"
+ * is enabled). Groups is an array of all the groups the user
+ * belongs to. Keys are the group ids, values are the group names.
+ *
+ * returns FALSE if no suitable function is available to retrieve
+ * the data
+ *
+ * @return array|boolean
+ */
+ function getUnixId()
+ {
+
+ if ($this->osIsWindows()) {
+ return false;
+ }
+
+ $success = false;
+
+
+ if (function_exists("exec") && !PhpSecInfo_Test::getBooleanIniValue('safe_mode')) {
+ $id_raw = exec('id');
+ // uid=1000(coj) gid=1000(coj) groups=1000(coj),1001(admin)
+ preg_match("|uid=(\d+)\((\S+)\)\s+gid=(\d+)\((\S+)\)\s+groups=(.+)|i",
+ $id_raw,
+ $matches);
+
+ if (!$matches) {
+ /**
+ * for some reason the output from 'id' wasn't as we expected.
+ * return false so the test doesn't run.
+ */
+ $success = false;
+ } else {
+ $id_data = array('uid' => $matches[1],
+ 'username' => $matches[2],
+ 'gid' => $matches[3],
+ 'group' => $matches[4]);
+
+ $groups = array();
+ if ($matches[5]) {
+ $gs = $matches[5];
+ $gs = explode(',', $gs);
+ foreach ($gs as $groupstr) {
+ if (preg_match("/(\d+)\(([^\)]+)\)/", $groupstr, $subs)) {
+ $groups[$subs[1]] = $subs[2];
+ } else {
+ $groups[$groupstr] = '';
+ }
+ }
+ ksort($groups);
+ }
+ $id_data['groups'] = $groups;
+ $success = true;
+ }
+
+ }
+
+ if (!$success && function_exists("posix_getpwuid") && function_exists("posix_geteuid")
+ && function_exists('posix_getgrgid') && function_exists('posix_getgroups')
+ ) {
+ $data = posix_getpwuid(posix_getuid());
+ $id_data['uid'] = $data['uid'];
+ $id_data['username'] = $data['name'];
+ $id_data['gid'] = $data['gid'];
+ //$group_data = posix_getgrgid( posix_getegid() );
+ //$id_data['group'] = $group_data['name'];
+ $id_data['groups'] = array();
+ $groups = posix_getgroups();
+ foreach ($groups as $gid) {
+ //$group_data = posix_getgrgid(posix_getgid());
+ $id_data['groups'][$gid] = '<unknown>';
+ }
+ $success = true;
+ }
+
+ if ($success) {
+ return $id_data;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Application.php b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Application.php
index af7a2f9926..af97310aad 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Application.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Application.php
@@ -1,7 +1,7 @@
<?php
/**
* Skeleton Test class file for Application group
- *
+ *
* @package PhpSecInfo
* @author Anthon Pang
*/
@@ -9,8 +9,7 @@
/**
* require the main PhpSecInfo class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
-
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test.php');
/**
@@ -19,36 +18,38 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
*/
class PhpSecInfo_Test_Application extends PhpSecInfo_Test
{
-
- /**
- * This value is used to group test results together.
- *
- * For example, all tests related to the mysql lib should be grouped under "mysql."
- *
- * @var string
- */
- var $test_group = 'Application';
-
-
- /**
- * "Application" tests should pretty much be always testable, so the default is just to return true
- *
- * @return boolean
- */
- function isTestable() {
- return Piwik_Http::getTransportMethod() !== null;
- }
-
- function getMoreInfoURL() {
- $urls = array(
- 'Piwik' => 'http://piwik.org/changelog',
- 'PHP' => 'http://php.net/',
- );
-
- if ($tn = $this->getTestName()) {
- return $urls[$tn];
- } else {
- return false;
- }
- }
+
+ /**
+ * This value is used to group test results together.
+ *
+ * For example, all tests related to the mysql lib should be grouped under "mysql."
+ *
+ * @var string
+ */
+ var $test_group = 'Application';
+
+
+ /**
+ * "Application" tests should pretty much be always testable, so the default is just to return true
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+ return Piwik_Http::getTransportMethod() !== null;
+ }
+
+ function getMoreInfoURL()
+ {
+ $urls = array(
+ 'Piwik' => 'http://piwik.org/changelog',
+ 'PHP' => 'http://php.net/',
+ );
+
+ if ($tn = $this->getTestName()) {
+ return $urls[$tn];
+ } else {
+ return false;
+ }
+ }
}
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Cgi.php b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Cgi.php
index 1a4156a3f4..5384bfbf50 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Cgi.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Cgi.php
@@ -9,8 +9,7 @@
/**
* require the main PhpSecInfo class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
-
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test.php');
/**
@@ -20,42 +19,43 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
class PhpSecInfo_Test_Cgi extends PhpSecInfo_Test
{
- /**
- * This value is used to group test results together.
- *
- * For example, all tests related to the mysql lib should be grouped under "mysql."
- *
- * @var string
- */
- var $test_group = 'CGI';
-
-
-
- /**
- * "CGI" tests should only be run if we're running as a CGI. The best way I could think of
- * to test this was to preg against the php_sapi_name() return value.
- *
- * @return boolean
- */
- function isTestable() {
- /*if ( preg_match('/^cgi.*$/', PHP_SAPI) ) {
- return true;
- } else {
- return false;
- }*/
- return !strncmp(PHP_SAPI, 'cgi', 3);
- }
-
-
- /**
- * Set the messages for CGI tests
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', "You don't seem to be using the CGI SAPI");
-
- }
+ /**
+ * This value is used to group test results together.
+ *
+ * For example, all tests related to the mysql lib should be grouped under "mysql."
+ *
+ * @var string
+ */
+ var $test_group = 'CGI';
+
+
+ /**
+ * "CGI" tests should only be run if we're running as a CGI. The best way I could think of
+ * to test this was to preg against the php_sapi_name() return value.
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+ /*if ( preg_match('/^cgi.*$/', PHP_SAPI) ) {
+ return true;
+ } else {
+ return false;
+ }*/
+ return !strncmp(PHP_SAPI, 'cgi', 3);
+ }
+
+
+ /**
+ * Set the messages for CGI tests
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', "You don't seem to be using the CGI SAPI");
+
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Core.php b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Core.php
index 58230794d2..4778394f6f 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Core.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Core.php
@@ -1,7 +1,7 @@
<?php
/**
* Skeleton Test class file for Core group
- *
+ *
* @package PhpSecInfo
* @author Ed Finkler <coj@funkatron.com>
*/
@@ -9,8 +9,7 @@
/**
* require the main PhpSecInfo class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
-
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test.php');
/**
@@ -19,26 +18,27 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
*/
class PhpSecInfo_Test_Core extends PhpSecInfo_Test
{
-
- /**
- * This value is used to group test results together.
- *
- * For example, all tests related to the mysql lib should be grouped under "mysql."
- *
- * @var string
- */
- var $test_group = 'Core';
-
-
- /**
- * "Core" tests should pretty much be always testable, so the default is just to return true
- *
- * @return boolean
- */
- function isTestable() {
-
- return true;
- }
-
-
+
+ /**
+ * This value is used to group test results together.
+ *
+ * For example, all tests related to the mysql lib should be grouped under "mysql."
+ *
+ * @var string
+ */
+ var $test_group = 'Core';
+
+
+ /**
+ * "Core" tests should pretty much be always testable, so the default is just to return true
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+
+ return true;
+ }
+
+
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Curl.php b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Curl.php
index a1e7d57a6f..78deeaea6c 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Curl.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Curl.php
@@ -9,8 +9,7 @@
/**
* require the main PhpSecInfo class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
-
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test.php');
/**
@@ -20,43 +19,44 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
class PhpSecInfo_Test_Curl extends PhpSecInfo_Test
{
- /**
- * This value is used to group test results together.
- *
- * For example, all tests related to the mysql lib should be grouped under "mysql."
- *
- * @var string
- */
- var $test_group = 'Curl';
-
-
-
- /**
- * "Curl" tests should only be run if the curl extension is installed. We can check
- * for this by seeing if the function curl_init() is defined
- *
- * @return boolean
- */
- function isTestable() {
-/* if ( function_exists('curl_init') ) {
- return true;
- } else {
- return false;
- }
-*/
- return extension_loaded('curl');
- }
-
-
- /**
- * Set the messages for Curl tests
- *
- */
- function _setMessages() {
- parent::_setMessages();
-
- $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', "CURL support is not enabled in your PHP install");
-
- }
+ /**
+ * This value is used to group test results together.
+ *
+ * For example, all tests related to the mysql lib should be grouped under "mysql."
+ *
+ * @var string
+ */
+ var $test_group = 'Curl';
+
+
+ /**
+ * "Curl" tests should only be run if the curl extension is installed. We can check
+ * for this by seeing if the function curl_init() is defined
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+ /* if ( function_exists('curl_init') ) {
+ return true;
+ } else {
+ return false;
+ }
+ */
+ return extension_loaded('curl');
+ }
+
+
+ /**
+ * Set the messages for Curl tests
+ *
+ */
+ function _setMessages()
+ {
+ parent::_setMessages();
+
+ $this->setMessageForResult(PHPSECINFO_TEST_RESULT_NOTRUN, 'en', "CURL support is not enabled in your PHP install");
+
+ }
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Session.php b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Session.php
index 5270fa62c4..32185eee89 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Session.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Session.php
@@ -1,7 +1,7 @@
<?php
/**
* Skeleton Test class file for Session group
- *
+ *
* @package PhpSecInfo
* @author Ed Finkler <coj@funkatron.com>
*/
@@ -10,8 +10,7 @@
/**
* require the main PhpSecInfo class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
-
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test.php');
/**
@@ -20,27 +19,28 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
*/
class PhpSecInfo_Test_Session extends PhpSecInfo_Test
{
-
- /**
- * This value is used to group test results together.
- *
- * For example, all tests related to the mysql lib should be grouped under "mysql."
- *
- * @var string
- */
- var $test_group = 'Session';
-
-
- /**
- * "Session" tests should pretty much be always testable, so the default is
- * just to return true
- *
- * @return boolean
- */
- function isTestable() {
-
- return true;
- }
-
-
+
+ /**
+ * This value is used to group test results together.
+ *
+ * For example, all tests related to the mysql lib should be grouped under "mysql."
+ *
+ * @var string
+ */
+ var $test_group = 'Session';
+
+
+ /**
+ * "Session" tests should pretty much be always testable, so the default is
+ * just to return true
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+
+ return true;
+ }
+
+
} \ No newline at end of file
diff --git a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Suhosin.php b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Suhosin.php
index 1f8883eeba..7228fde5e0 100644
--- a/plugins/SecurityInfo/PhpSecInfo/Test/Test_Suhosin.php
+++ b/plugins/SecurityInfo/PhpSecInfo/Test/Test_Suhosin.php
@@ -1,7 +1,7 @@
<?php
/**
* Skeleton Test class file for Suhosin group
- *
+ *
* @package PhpSecInfo
* @author Anthon Pang
*/
@@ -9,8 +9,7 @@
/**
* require the main PhpSecInfo class
*/
-require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
-
+require_once(PHPSECINFO_BASE_DIR . '/Test/Test.php');
/**
@@ -19,34 +18,36 @@ require_once(PHPSECINFO_BASE_DIR.'/Test/Test.php');
*/
class PhpSecInfo_Test_Suhosin extends PhpSecInfo_Test
{
-
- /**
- * This value is used to group test results together.
- *
- * For example, all tests related to the mysql lib should be grouped under "mysql."
- *
- * @var string
- */
- var $test_group = 'Suhosin';
-
-
- /**
- * "Suhosin" tests should pretty much be always testable, so the default is just to return true
- *
- * @return boolean
- */
- function isTestable() {
- if (version_compare(PHP_VERSION, '5.3.9') >= 0) {
- return false;
- }
- return true;
- }
- function getMoreInfoURL() {
- if ($tn = $this->getTestName()) {
- return 'http://www.hardened-php.net/suhosin/index.html';
- } else {
- return false;
- }
- }
+ /**
+ * This value is used to group test results together.
+ *
+ * For example, all tests related to the mysql lib should be grouped under "mysql."
+ *
+ * @var string
+ */
+ var $test_group = 'Suhosin';
+
+
+ /**
+ * "Suhosin" tests should pretty much be always testable, so the default is just to return true
+ *
+ * @return boolean
+ */
+ function isTestable()
+ {
+ if (version_compare(PHP_VERSION, '5.3.9') >= 0) {
+ return false;
+ }
+ return true;
+ }
+
+ function getMoreInfoURL()
+ {
+ if ($tn = $this->getTestName()) {
+ return 'http://www.hardened-php.net/suhosin/index.html';
+ } else {
+ return false;
+ }
+ }
}
diff --git a/plugins/SecurityInfo/SecurityInfo.php b/plugins/SecurityInfo/SecurityInfo.php
index c42ad9aeee..487d8a92cd 100644
--- a/plugins/SecurityInfo/SecurityInfo.php
+++ b/plugins/SecurityInfo/SecurityInfo.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_SecurityInfo
*/
@@ -14,30 +14,30 @@
* @package Piwik_SecurityInfo
*/
class Piwik_SecurityInfo extends Piwik_Plugin
-{
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('SecurityInfo_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- return $info;
- }
-
- function getListHooksRegistered()
- {
- return array(
- 'AdminMenu.add' => 'addMenu',
- );
- }
-
- function addMenu()
- {
- Piwik_AddAdminSubMenu('CoreAdminHome_MenuDiagnostic', 'SecurityInfo_Security',
- array('module' => 'SecurityInfo', 'action' => 'index'),
- Piwik::isUserIsSuperUser(),
- $order = 10);
- }
+{
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('SecurityInfo_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ return array(
+ 'AdminMenu.add' => 'addMenu',
+ );
+ }
+
+ function addMenu()
+ {
+ Piwik_AddAdminSubMenu('CoreAdminHome_MenuDiagnostic', 'SecurityInfo_Security',
+ array('module' => 'SecurityInfo', 'action' => 'index'),
+ Piwik::isUserIsSuperUser(),
+ $order = 10);
+ }
}
diff --git a/plugins/SecurityInfo/templates/index.tpl b/plugins/SecurityInfo/templates/index.tpl
index ac9f8c9254..f40b86f1b7 100644
--- a/plugins/SecurityInfo/templates/index.tpl
+++ b/plugins/SecurityInfo/templates/index.tpl
@@ -3,27 +3,28 @@
<h2>{'SecurityInfo_SecurityInformation'|translate}</h2>
<p>{'SecurityInfo_PluginDescription'|translate}</p>
-<p>Learn more: read our guide <a target='_blank' href='http://piwik.org/security/how-to-secure-piwik/'>Hardening Piwik: How to make Piwik and your web server more secure?</a></p>
+<p>Learn more: read our guide <a target='_blank' href='http://piwik.org/security/how-to-secure-piwik/'>Hardening Piwik: How to make Piwik and your web server
+ more secure?</a></p>
<div style="max-width:980px;">
-{foreach from=$results.test_results key=i item=section}
-<h2>{$i}</h2>
-<table class="dataTable entityTable">
- <thead>
- <tr>
- <th>{'SecurityInfo_Test'|translate}</th>
- <th>{'SecurityInfo_Result'|translate}</th>
- </tr>
- </thead>
- <tbody>
- {foreach from=$section key=j item=test}
- <tr>
- <td>{$j}</td>
- <td style="{if $test.result==-1}background-color:green;color:white;{elseif $test.result==-2}background-color:yellow;color:black;{else if $test.result=--4}background-color:red;color:white;{/if}">{$test.message}</td>
- </tr>
- {/foreach}
- </tbody>
-</table>
-{/foreach}
+ {foreach from=$results.test_results key=i item=section}
+ <h2>{$i}</h2>
+ <table class="dataTable entityTable">
+ <thead>
+ <tr>
+ <th>{'SecurityInfo_Test'|translate}</th>
+ <th>{'SecurityInfo_Result'|translate}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {foreach from=$section key=j item=test}
+ <tr>
+ <td>{$j}</td>
+ <td style="{if $test.result==-1}background-color:green;color:white;{elseif $test.result==-2}background-color:yellow;color:black;{else if $test.result=--4}background-color:red;color:white;{/if}">{$test.message}</td>
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ {/foreach}
</div>
{include file="CoreAdminHome/templates/footer.tpl"} \ No newline at end of file
diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php
index 3b5e5e9ec0..a87a35551a 100644
--- a/plugins/SitesManager/API.php
+++ b/plugins/SitesManager/API.php
@@ -1,1411 +1,1344 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_SitesManager
*/
/**
- * The SitesManager API gives you full control on Websites in Piwik (create, update and delete), and many methods to retrieve websites based on various attributes.
- *
+ * The SitesManager API gives you full control on Websites in Piwik (create, update and delete), and many methods to retrieve websites based on various attributes.
+ *
* This API lets you create websites via "addSite", update existing websites via "updateSite" and delete websites via "deleteSite".
* When creating websites, it can be useful to access internal codes used by Piwik for currencies via "getCurrencyList", or timezones via "getTimezonesList".
- *
+ *
* There are also many ways to request a list of websites: from the website ID via "getSiteFromId" or the site URL via "getSitesIdFromSiteUrl".
* Often, the most useful technique is to list all websites that are known to a current user, based on the token_auth, via
* "getSitesWithAdminAccess", "getSitesWithViewAccess" or "getSitesWithAtLeastViewAccess" (which returns both).
- *
+ *
* Some methods will affect all websites globally: "setGlobalExcludedIps" will set the list of IPs to be excluded on all websites,
* "setGlobalExcludedQueryParameters" will set the list of URL parameters to remove from URLs for all websites.
- * The existing values can be fetched via "getExcludedIpsGlobal" and "getExcludedQueryParametersGlobal".
+ * The existing values can be fetched via "getExcludedIpsGlobal" and "getExcludedQueryParametersGlobal".
* See also the documentation about <a href='http://piwik.org/docs/manage-websites/' target='_blank'>Managing Websites</a> in Piwik.
* @package Piwik_SitesManager
*/
-class Piwik_SitesManager_API
+class Piwik_SitesManager_API
{
- static private $instance = null;
- const DEFAULT_SEARCH_KEYWORD_PARAMETERS = 'q,query,s,search,searchword,k,keyword';
-
- /**
- * @return Piwik_SitesManager_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- const OPTION_EXCLUDED_IPS_GLOBAL = 'SitesManager_ExcludedIpsGlobal';
- const OPTION_DEFAULT_TIMEZONE = 'SitesManager_DefaultTimezone';
- const OPTION_DEFAULT_CURRENCY = 'SitesManager_DefaultCurrency';
- const OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL = 'SitesManager_ExcludedQueryParameters';
- const OPTION_SEARCH_KEYWORD_QUERY_PARAMETERS_GLOBAL = 'SitesManager_SearchKeywordParameters';
- const OPTION_SEARCH_CATEGORY_QUERY_PARAMETERS_GLOBAL = 'SitesManager_SearchCategoryParameters';
- const OPTION_EXCLUDED_USER_AGENTS_GLOBAL = 'SitesManager_ExcludedUserAgentsGlobal';
- const OPTION_SITE_SPECIFIC_USER_AGENT_EXCLUDE_ENABLE = 'SitesManager_EnableSiteSpecificUserAgentExclude';
- const OPTION_KEEP_URL_FRAGMENTS_GLOBAL = 'SitesManager_KeepURLFragmentsGlobal';
-
- /**
- * Returns the javascript tag for the given idSite.
- * This tag must be included on every page to be tracked by Piwik
- *
- * @param int $idSite
- * @param string $customTitle Custom title given to the pageview
- * @return string The Javascript tag ready to be included on the HTML pages
- */
- public function getJavascriptTag( $idSite, $piwikUrl = '')
- {
- Piwik::checkUserHasViewAccess($idSite);
-
- if(empty($piwikUrl))
- {
- $piwikUrl = Piwik_Url::getCurrentUrlWithoutFileName();
- }
- $piwikUrl = Piwik_Common::sanitizeInputValues($piwikUrl);
-
- $htmlEncoded = Piwik::getJavascriptCode($idSite, $piwikUrl);
- $htmlEncoded = str_replace(array('<br>','<br />','<br/>'), '', $htmlEncoded);
- return $htmlEncoded;
- }
-
- /**
- * Returns all websites belonging to the specified group
- * @param string $group Group name
- */
- public function getSitesFromGroup($group)
- {
- Piwik::checkUserIsSuperUser();
- $group = trim($group);
-
- $sites = Zend_Registry::get('db')->fetchAll("SELECT *
- FROM ".Piwik_Common::prefixTable("site")."
+ static private $instance = null;
+ const DEFAULT_SEARCH_KEYWORD_PARAMETERS = 'q,query,s,search,searchword,k,keyword';
+
+ /**
+ * @return Piwik_SitesManager_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ const OPTION_EXCLUDED_IPS_GLOBAL = 'SitesManager_ExcludedIpsGlobal';
+ const OPTION_DEFAULT_TIMEZONE = 'SitesManager_DefaultTimezone';
+ const OPTION_DEFAULT_CURRENCY = 'SitesManager_DefaultCurrency';
+ const OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL = 'SitesManager_ExcludedQueryParameters';
+ const OPTION_SEARCH_KEYWORD_QUERY_PARAMETERS_GLOBAL = 'SitesManager_SearchKeywordParameters';
+ const OPTION_SEARCH_CATEGORY_QUERY_PARAMETERS_GLOBAL = 'SitesManager_SearchCategoryParameters';
+ const OPTION_EXCLUDED_USER_AGENTS_GLOBAL = 'SitesManager_ExcludedUserAgentsGlobal';
+ const OPTION_SITE_SPECIFIC_USER_AGENT_EXCLUDE_ENABLE = 'SitesManager_EnableSiteSpecificUserAgentExclude';
+ const OPTION_KEEP_URL_FRAGMENTS_GLOBAL = 'SitesManager_KeepURLFragmentsGlobal';
+
+ /**
+ * Returns the javascript tag for the given idSite.
+ * This tag must be included on every page to be tracked by Piwik
+ *
+ * @param int $idSite
+ * @param string $customTitle Custom title given to the pageview
+ * @return string The Javascript tag ready to be included on the HTML pages
+ */
+ public function getJavascriptTag($idSite, $piwikUrl = '')
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+
+ if (empty($piwikUrl)) {
+ $piwikUrl = Piwik_Url::getCurrentUrlWithoutFileName();
+ }
+ $piwikUrl = Piwik_Common::sanitizeInputValues($piwikUrl);
+
+ $htmlEncoded = Piwik::getJavascriptCode($idSite, $piwikUrl);
+ $htmlEncoded = str_replace(array('<br>', '<br />', '<br/>'), '', $htmlEncoded);
+ return $htmlEncoded;
+ }
+
+ /**
+ * Returns all websites belonging to the specified group
+ * @param string $group Group name
+ */
+ public function getSitesFromGroup($group)
+ {
+ Piwik::checkUserIsSuperUser();
+ $group = trim($group);
+
+ $sites = Zend_Registry::get('db')->fetchAll("SELECT *
+ FROM " . Piwik_Common::prefixTable("site") . "
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()
- {
- Piwik::checkUserIsSuperUser();
- $groups = Zend_Registry::get('db')->fetchAll("SELECT DISTINCT `group` FROM ".Piwik_Common::prefixTable("site"));
- $cleanedGroups = array();
- foreach($groups as $group)
- {
- $cleanedGroups[] = $group['group'];
- }
- $cleanedGroups = array_map('trim', $cleanedGroups);
- return $cleanedGroups;
- }
-
- /**
- * Returns the website information : name, main_url
- *
- * @exception if the site ID doesn't exist or the user doesn't have access to it
- * @return array
- */
- public function getSiteFromId( $idSite )
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $site = Zend_Registry::get('db')->fetchRow("SELECT *
- FROM ".Piwik_Common::prefixTable("site")."
+ 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()
+ {
+ Piwik::checkUserIsSuperUser();
+ $groups = Zend_Registry::get('db')->fetchAll("SELECT DISTINCT `group` FROM " . Piwik_Common::prefixTable("site"));
+ $cleanedGroups = array();
+ foreach ($groups as $group) {
+ $cleanedGroups[] = $group['group'];
+ }
+ $cleanedGroups = array_map('trim', $cleanedGroups);
+ return $cleanedGroups;
+ }
+
+ /**
+ * Returns the website information : name, main_url
+ *
+ * @exception if the site ID doesn't exist or the user doesn't have access to it
+ * @return array
+ */
+ public function getSiteFromId($idSite)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $site = Zend_Registry::get('db')->fetchRow("SELECT *
+ FROM " . Piwik_Common::prefixTable("site") . "
WHERE idsite = ?", $idSite);
- return $site;
- }
-
- /**
- * Returns the list of alias URLs registered for the given idSite.
- * The website ID must be valid when calling this method!
- *
- * @return array list of alias URLs
- */
- private function getAliasSiteUrlsFromId( $idsite )
- {
- $db = Zend_Registry::get('db');
- $result = $db->fetchAll("SELECT url
- FROM ".Piwik_Common::prefixTable("site_url"). "
+ return $site;
+ }
+
+ /**
+ * Returns the list of alias URLs registered for the given idSite.
+ * The website ID must be valid when calling this method!
+ *
+ * @return array list of alias URLs
+ */
+ private function getAliasSiteUrlsFromId($idsite)
+ {
+ $db = Zend_Registry::get('db');
+ $result = $db->fetchAll("SELECT url
+ FROM " . Piwik_Common::prefixTable("site_url") . "
WHERE idsite = ?", $idsite);
- $urls = array();
- foreach($result as $url)
- {
- $urls[] = $url['url'];
- }
- return $urls;
- }
-
- /**
- * Returns the list of all URLs registered for the given idSite (main_url + alias URLs).
- *
- * @exception if the website ID doesn't exist or the user doesn't have access to it
- * @return array list of URLs
- */
- public function getSiteUrlsFromId( $idSite )
- {
- Piwik::checkUserHasViewAccess($idSite);
- $site = new Piwik_Site($idSite);
- $urls = $this->getAliasSiteUrlsFromId($idSite);
- return array_merge(array($site->getMainUrl()), $urls);
- }
-
- /**
- * Returns the list of all the website IDs registered.
- * Caller must check access.
- *
- * @return array The list of website IDs
- */
- private function getSitesId()
- {
- $result = Piwik_FetchAll("SELECT idsite FROM ".Piwik_Common::prefixTable('site'));
- $idSites = array();
- foreach($result as $idSite)
- {
- $idSites[] = $idSite['idsite'];
- }
- return $idSites;
- }
-
- /**
- * Returns all websites, requires Super User access
- *
- * @return array The list of websites, indexed by idsite
- */
- public function getAllSites()
- {
- Piwik::checkUserIsSuperUser();
- $sites = Zend_Registry::get('db')->fetchAll("SELECT * FROM ".Piwik_Common::prefixTable("site"));
- $return = array();
- foreach($sites as $site)
- {
- $return[$site['idsite']] = $site;
- }
- return $return;
- }
-
- /**
- * Returns the list of all the website IDs registered.
- * Requires super user access.
- *
- * @return array The list of website IDs
- */
- public function getAllSitesId()
- {
- Piwik::checkUserIsSuperUser();
- return Piwik_SitesManager_API::getInstance()->getSitesId();
- }
-
- /**
- * Returns the list of the website IDs that received some visits since the specified timestamp.
- * Requires super user access.
- *
- * @return array The list of website IDs
- */
- public function getSitesIdWithVisits($timestamp = false)
- {
- Piwik::checkUserIsSuperUser();
-
- if(empty($timestamp)) $timestamp = time();
-
+ $urls = array();
+ foreach ($result as $url) {
+ $urls[] = $url['url'];
+ }
+ return $urls;
+ }
+
+ /**
+ * Returns the list of all URLs registered for the given idSite (main_url + alias URLs).
+ *
+ * @exception if the website ID doesn't exist or the user doesn't have access to it
+ * @return array list of URLs
+ */
+ public function getSiteUrlsFromId($idSite)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $site = new Piwik_Site($idSite);
+ $urls = $this->getAliasSiteUrlsFromId($idSite);
+ return array_merge(array($site->getMainUrl()), $urls);
+ }
+
+ /**
+ * Returns the list of all the website IDs registered.
+ * Caller must check access.
+ *
+ * @return array The list of website IDs
+ */
+ private function getSitesId()
+ {
+ $result = Piwik_FetchAll("SELECT idsite FROM " . Piwik_Common::prefixTable('site'));
+ $idSites = array();
+ foreach ($result as $idSite) {
+ $idSites[] = $idSite['idsite'];
+ }
+ return $idSites;
+ }
+
+ /**
+ * Returns all websites, requires Super User access
+ *
+ * @return array The list of websites, indexed by idsite
+ */
+ public function getAllSites()
+ {
+ Piwik::checkUserIsSuperUser();
+ $sites = Zend_Registry::get('db')->fetchAll("SELECT * FROM " . Piwik_Common::prefixTable("site"));
+ $return = array();
+ foreach ($sites as $site) {
+ $return[$site['idsite']] = $site;
+ }
+ return $return;
+ }
+
+ /**
+ * Returns the list of all the website IDs registered.
+ * Requires super user access.
+ *
+ * @return array The list of website IDs
+ */
+ public function getAllSitesId()
+ {
+ Piwik::checkUserIsSuperUser();
+ return Piwik_SitesManager_API::getInstance()->getSitesId();
+ }
+
+ /**
+ * Returns the list of the website IDs that received some visits since the specified timestamp.
+ * Requires super user access.
+ *
+ * @return array The list of website IDs
+ */
+ public function getSitesIdWithVisits($timestamp = false)
+ {
+ Piwik::checkUserIsSuperUser();
+
+ if (empty($timestamp)) $timestamp = time();
+
$time = Piwik_Date::factory((int)$timestamp)->getDatetime();
- $result = Piwik_FetchAll("
+ $result = Piwik_FetchAll("
SELECT
idsite
FROM
- ".Piwik_Common::prefixTable('site')." s
+ " . Piwik_Common::prefixTable('site') . " s
WHERE EXISTS (
SELECT 1
- FROM ".Piwik_Common::prefixTable('log_visit'). " v
+ FROM " . Piwik_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 = Piwik_Date::now()->addHour(1)->getDatetime()));
- $idSites = array();
- foreach($result as $idSite)
- {
- $idSites[] = $idSite['idsite'];
- }
- return $idSites;
- }
-
-
- /**
- * Returns the list of websites with the 'admin' access for the current user.
- * For the superUser it returns all the websites in the database.
- *
- * @return array for each site, an array of information (idsite, name, main_url, etc.)
- */
- public function getSitesWithAdminAccess()
- {
- $sitesId = $this->getSitesIdWithAdminAccess();
- return $this->getSitesFromIds($sitesId);
- }
-
- /**
- * Returns the list of websites with the 'view' access for the current user.
- * For the superUser it doesn't return any result because the superUser has admin access on all the websites (use getSitesWithAtLeastViewAccess() instead).
- *
- * @return array for each site, an array of information (idsite, name, main_url, etc.)
- */
- public function getSitesWithViewAccess()
- {
- $sitesId = $this->getSitesIdWithViewAccess();
- return $this->getSitesFromIds($sitesId);
- }
-
- /**
- * Returns the list of websites with the 'view' or 'admin' access for the current user.
- * For the superUser it returns all the websites in the database.
- *
- * @param int $limit Specify max number of sites to return
- * @param bool $_restrictSitesToLogin Hack necessary when runnning scheduled tasks, where "Super User" is forced, but sometimes not desired, see #3017
- * @return array array for each site, an array of information (idsite, name, main_url, etc.)
- */
- public function getSitesWithAtLeastViewAccess($limit = false, $_restrictSitesToLogin = false)
- {
- $sitesId = $this->getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin);
- return $this->getSitesFromIds($sitesId, $limit);
- }
-
- /**
- * Returns the list of websites ID with the 'admin' access for the current user.
- * For the superUser it returns all the websites in the database.
- *
- * @return array list of websites ID
- */
- public function getSitesIdWithAdminAccess()
- {
- $sitesId = Zend_Registry::get('access')->getSitesIdWithAdminAccess();
- return $sitesId;
- }
-
- /**
- * Returns the list of websites ID with the 'view' access for the current user.
- * For the superUser it doesn't return any result because the superUser has admin access on all the websites (use getSitesIdWithAtLeastViewAccess() instead).
- *
- * @return array list of websites ID
- */
- public function getSitesIdWithViewAccess()
- {
- return Zend_Registry::get('access')->getSitesIdWithViewAccess();
- }
-
- /**
- * Returns the list of websites ID with the 'view' or 'admin' access for the current user.
- * For the superUser it returns all the websites in the database.
- *
- * @return array list of websites ID
- */
- public function getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin = false)
- {
- if(!empty($_restrictSitesToLogin)
- // Very important here to make sure we only proceed when in a scheduled task
- // Otherwise anyone could get all websites for a given user
- && Piwik_TaskScheduler::isTaskBeingExecuted())
- {
- $accessRaw = Piwik_Access::getRawSitesWithSomeViewAccess($_restrictSitesToLogin);
- $sitesId = array();
- foreach($accessRaw as $access)
- {
- $sitesId[] = $access['idsite'];
- }
- return $sitesId;
- }
- else
- {
- return Zend_Registry::get('access')->getSitesIdWithAtLeastViewAccess();
- }
- }
-
- /**
- * Returns the list of websites from the ID array in parameters.
- * The user access is not checked in this method so the ID have to be accessible by the user!
- *
- * @param array list of website ID
- */
- private function getSitesFromIds( $idSites, $limit = false )
- {
- if(count($idSites) === 0)
- {
- return array();
- }
-
- if($limit)
- {
- $limit = "LIMIT " . (int)$limit;
- }
-
- $db = Zend_Registry::get('db');
- $sites = $db->fetchAll("SELECT *
- FROM ".Piwik_Common::prefixTable("site")."
- WHERE idsite IN (".implode(", ", $idSites).")
+ $idSites = array();
+ foreach ($result as $idSite) {
+ $idSites[] = $idSite['idsite'];
+ }
+ return $idSites;
+ }
+
+
+ /**
+ * Returns the list of websites with the 'admin' access for the current user.
+ * For the superUser it returns all the websites in the database.
+ *
+ * @return array for each site, an array of information (idsite, name, main_url, etc.)
+ */
+ public function getSitesWithAdminAccess()
+ {
+ $sitesId = $this->getSitesIdWithAdminAccess();
+ return $this->getSitesFromIds($sitesId);
+ }
+
+ /**
+ * Returns the list of websites with the 'view' access for the current user.
+ * For the superUser it doesn't return any result because the superUser has admin access on all the websites (use getSitesWithAtLeastViewAccess() instead).
+ *
+ * @return array for each site, an array of information (idsite, name, main_url, etc.)
+ */
+ public function getSitesWithViewAccess()
+ {
+ $sitesId = $this->getSitesIdWithViewAccess();
+ return $this->getSitesFromIds($sitesId);
+ }
+
+ /**
+ * Returns the list of websites with the 'view' or 'admin' access for the current user.
+ * For the superUser it returns all the websites in the database.
+ *
+ * @param int $limit Specify max number of sites to return
+ * @param bool $_restrictSitesToLogin Hack necessary when runnning scheduled tasks, where "Super User" is forced, but sometimes not desired, see #3017
+ * @return array array for each site, an array of information (idsite, name, main_url, etc.)
+ */
+ public function getSitesWithAtLeastViewAccess($limit = false, $_restrictSitesToLogin = false)
+ {
+ $sitesId = $this->getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin);
+ return $this->getSitesFromIds($sitesId, $limit);
+ }
+
+ /**
+ * Returns the list of websites ID with the 'admin' access for the current user.
+ * For the superUser it returns all the websites in the database.
+ *
+ * @return array list of websites ID
+ */
+ public function getSitesIdWithAdminAccess()
+ {
+ $sitesId = Zend_Registry::get('access')->getSitesIdWithAdminAccess();
+ return $sitesId;
+ }
+
+ /**
+ * Returns the list of websites ID with the 'view' access for the current user.
+ * For the superUser it doesn't return any result because the superUser has admin access on all the websites (use getSitesIdWithAtLeastViewAccess() instead).
+ *
+ * @return array list of websites ID
+ */
+ public function getSitesIdWithViewAccess()
+ {
+ return Zend_Registry::get('access')->getSitesIdWithViewAccess();
+ }
+
+ /**
+ * Returns the list of websites ID with the 'view' or 'admin' access for the current user.
+ * For the superUser it returns all the websites in the database.
+ *
+ * @return array list of websites ID
+ */
+ public function getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin = false)
+ {
+ if (!empty($_restrictSitesToLogin)
+ // Very important here to make sure we only proceed when in a scheduled task
+ // Otherwise anyone could get all websites for a given user
+ && Piwik_TaskScheduler::isTaskBeingExecuted()
+ ) {
+ $accessRaw = Piwik_Access::getRawSitesWithSomeViewAccess($_restrictSitesToLogin);
+ $sitesId = array();
+ foreach ($accessRaw as $access) {
+ $sitesId[] = $access['idsite'];
+ }
+ return $sitesId;
+ } else {
+ return Zend_Registry::get('access')->getSitesIdWithAtLeastViewAccess();
+ }
+ }
+
+ /**
+ * Returns the list of websites from the ID array in parameters.
+ * The user access is not checked in this method so the ID have to be accessible by the user!
+ *
+ * @param array list of website ID
+ */
+ private function getSitesFromIds($idSites, $limit = false)
+ {
+ if (count($idSites) === 0) {
+ return array();
+ }
+
+ if ($limit) {
+ $limit = "LIMIT " . (int)$limit;
+ }
+
+ $db = Zend_Registry::get('db');
+ $sites = $db->fetchAll("SELECT *
+ FROM " . Piwik_Common::prefixTable("site") . "
+ WHERE idsite IN (" . implode(", ", $idSites) . ")
ORDER BY idsite ASC $limit");
- return $sites;
- }
-
- protected function getNormalizedUrls($url)
- {
- if(strpos($url, 'www.') !== false)
- {
- $urlBis = str_replace('www.', '', $url);
- }
- else
- {
- $urlBis = str_replace('://', '://www.', $url);
- }
- return array($url, $urlBis);
- }
-
- /**
- * Returns the list of websites ID associated with a URL.
- *
- * @param string $url
- * @return array list of websites ID
- */
- public function getSitesIdFromSiteUrl( $url )
- {
- $url = $this->removeTrailingSlash($url);
- list($url, $urlBis) = $this->getNormalizedUrls($url);
- if(Piwik::isUserIsSuperUser())
- {
- $ids = Zend_Registry::get('db')->fetchAll(
- 'SELECT idsite
- FROM ' . Piwik_Common::prefixTable('site') . '
+ return $sites;
+ }
+
+ protected function getNormalizedUrls($url)
+ {
+ if (strpos($url, 'www.') !== false) {
+ $urlBis = str_replace('www.', '', $url);
+ } else {
+ $urlBis = str_replace('://', '://www.', $url);
+ }
+ return array($url, $urlBis);
+ }
+
+ /**
+ * Returns the list of websites ID associated with a URL.
+ *
+ * @param string $url
+ * @return array list of websites ID
+ */
+ public function getSitesIdFromSiteUrl($url)
+ {
+ $url = $this->removeTrailingSlash($url);
+ list($url, $urlBis) = $this->getNormalizedUrls($url);
+ if (Piwik::isUserIsSuperUser()) {
+ $ids = Zend_Registry::get('db')->fetchAll(
+ 'SELECT idsite
+ FROM ' . Piwik_Common::prefixTable('site') . '
WHERE (main_url = ? OR main_url = ?) ' .
- 'UNION
- SELECT idsite
- FROM ' . Piwik_Common::prefixTable('site_url') . '
+ 'UNION
+ SELECT idsite
+ FROM ' . Piwik_Common::prefixTable('site_url') . '
WHERE (url = ? OR url = ?) ', array($url, $urlBis, $url, $urlBis));
- }
- else
- {
- $login = Piwik::getCurrentUserLogin();
- $ids = Zend_Registry::get('db')->fetchAll(
- 'SELECT idsite
- FROM ' . Piwik_Common::prefixTable('site') . '
+ } else {
+ $login = Piwik::getCurrentUserLogin();
+ $ids = Zend_Registry::get('db')->fetchAll(
+ 'SELECT idsite
+ FROM ' . Piwik_Common::prefixTable('site') . '
WHERE (main_url = ? OR main_url = ?)' .
- 'AND idsite IN (' . Piwik_Access::getSqlAccessSite('idsite') . ') ' .
- 'UNION
- SELECT idsite
- FROM ' . Piwik_Common::prefixTable('site_url') . '
+ 'AND idsite IN (' . Piwik_Access::getSqlAccessSite('idsite') . ') ' .
+ 'UNION
+ SELECT idsite
+ FROM ' . Piwik_Common::prefixTable('site_url') . '
WHERE (url = ? OR url = ?)' .
- 'AND idsite IN (' . Piwik_Access::getSqlAccessSite('idsite') . ')',
- array($url, $urlBis, $login, $url, $urlBis, $login));
- }
-
- return $ids;
- }
-
- /**
- * Returns all websites with a timezone matching one the specified timezones
- *
- * @param array $timezones
- * @ignore
- */
- public function getSitesIdFromTimezones( $timezones )
- {
- Piwik::checkUserIsSuperUser();
- $timezones = Piwik::getArrayFromApiParameter($timezones);
- $timezones = array_unique($timezones);
- $ids = Zend_Registry::get('db')->fetchAll(
- 'SELECT idsite
- FROM ' . Piwik_Common::prefixTable('site') . '
- WHERE timezone IN ('.Piwik_Common::getSqlStringFieldsArray($timezones).')
- ORDER BY idsite ASC',
- $timezones);
- $return = array();
- foreach($ids as $id)
- {
- $return[] = $id['idsite'];
- }
- return $return;
- }
-
- /**
- * Add a website.
- * Requires Super User access.
- *
- * The website is defined by a name and an array of URLs.
- * @param string Site name
- * @param array|string The URLs array must contain at least one URL called the 'main_url' ;
- * if several URLs are provided in the array, they will be recorded
- * as Alias URLs for this website.
- * @param int Is Ecommerce Reporting enabled for this website?
- * @param int $sitesearch Whether site search is enabled, 0 or 1
- * @param string $searchKeywordParameters Comma separated list of search keyword parameter names
- * @param string $searchCategoryParameters Comma separated list of search category parameter names
- * @param string Comma separated list of IPs to exclude from the reports (allows wildcards)
- * @param string Timezone string, eg. 'Europe/London'
- * @param string Currency, eg. 'EUR'
- * @param string Website group identifier
- * @param string Date at which the statistics for this website will start. Defaults to today's date in YYYY-MM-DD format
- * @param int $keepURLFragments If 1, URL fragments will be kept when tracking. If 2, they
- * will be removed. If 0, the default global behavior will be used.
- * @see getKeepURLFragmentsGlobal.
- *
- * @return int the website ID created
- */
- public function addSite( $siteName,
- $urls,
- $ecommerce = null,
- $siteSearch = null,
- $searchKeywordParameters = null,
- $searchCategoryParameters = null,
- $excludedIps = null,
- $excludedQueryParameters = null,
- $timezone = null,
- $currency = null,
- $group = null,
- $startDate = null,
- $excludedUserAgents = null,
- $keepURLFragments = 0 )
- {
- Piwik::checkUserIsSuperUser();
-
- $this->checkName($siteName);
- $urls = $this->cleanParameterUrls($urls);
- $this->checkUrls($urls);
- $this->checkAtLeastOneUrl($urls);
- $siteSearch = $this->checkSiteSearch($siteSearch);
- list($searchKeywordParameters, $searchCategoryParameters ) = $this->checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters);
-
- $keepURLFragments = (int)$keepURLFragments;
- self::checkKeepURLFragmentsValue($keepURLFragments);
-
- $timezone = trim($timezone);
- if(empty($timezone))
- {
- $timezone = $this->getDefaultTimezone();
- }
- $this->checkValidTimezone($timezone);
-
- if(empty($currency))
- {
- $currency = $this->getDefaultCurrency();
- }
- $this->checkValidCurrency($currency);
-
- $db = Zend_Registry::get('db');
-
- $url = $urls[0];
- $urls = array_slice($urls, 1);
-
- $bind = array( 'name' => $siteName,
- 'main_url' => $url,
-
- );
-
- $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps);
- $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['sitesearch'] = $siteSearch;
- $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters;
- $bind['sitesearch_category_parameters'] = $searchCategoryParameters;
- $bind['ts_created'] = !is_null($startDate)
- ? Piwik_Date::factory($startDate)->getDatetime()
- : Piwik_Date::now()->getDatetime();
-
- if(!empty($group)
- && Piwik::isUserIsSuperUser())
- {
- $bind['group'] = trim($group);
- }
- else
- {
- $bind['group'] = "";
- }
-
- $db->insert(Piwik_Common::prefixTable("site"), $bind);
-
- $idSite = $db->lastInsertId();
-
- $this->insertSiteUrls($idSite, $urls);
-
- // we reload the access list which doesn't yet take in consideration this new website
- Zend_Registry::get('access')->reloadAccess();
- $this->postUpdateWebsite($idSite);
-
- Piwik_PostEvent('SitesManager.addSite', $idSite);
-
- return (int)$idSite;
- }
-
- private function postUpdateWebsite($idSite)
- {
- Piwik_Site::clearCache();
- Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($idSite);
- }
-
- /**
- * Delete a website from the database, given its Id.
- *
- * Requires Super User access.
- *
- * @param int $idSite
- * @throws Exception
- */
- public function deleteSite( $idSite )
- {
- Piwik::checkUserIsSuperUser();
-
- $idSites = Piwik_SitesManager_API::getInstance()->getSitesId();
- if(!in_array($idSite, $idSites))
- {
- throw new Exception("website id = $idSite not found");
- }
- $nbSites = count($idSites);
- if($nbSites == 1)
- {
- throw new Exception(Piwik_TranslateException("SitesManager_ExceptionDeleteSite"));
- }
-
- $db = Zend_Registry::get('db');
-
- $db->query("DELETE FROM ".Piwik_Common::prefixTable("site")."
+ 'AND idsite IN (' . Piwik_Access::getSqlAccessSite('idsite') . ')',
+ array($url, $urlBis, $login, $url, $urlBis, $login));
+ }
+
+ return $ids;
+ }
+
+ /**
+ * Returns all websites with a timezone matching one the specified timezones
+ *
+ * @param array $timezones
+ * @ignore
+ */
+ public function getSitesIdFromTimezones($timezones)
+ {
+ Piwik::checkUserIsSuperUser();
+ $timezones = Piwik::getArrayFromApiParameter($timezones);
+ $timezones = array_unique($timezones);
+ $ids = Zend_Registry::get('db')->fetchAll(
+ 'SELECT idsite
+ FROM ' . Piwik_Common::prefixTable('site') . '
+ WHERE timezone IN (' . Piwik_Common::getSqlStringFieldsArray($timezones) . ')
+ ORDER BY idsite ASC',
+ $timezones);
+ $return = array();
+ foreach ($ids as $id) {
+ $return[] = $id['idsite'];
+ }
+ return $return;
+ }
+
+ /**
+ * Add a website.
+ * Requires Super User access.
+ *
+ * The website is defined by a name and an array of URLs.
+ * @param string Site name
+ * @param array|string The URLs array must contain at least one URL called the 'main_url' ;
+ * if several URLs are provided in the array, they will be recorded
+ * as Alias URLs for this website.
+ * @param int Is Ecommerce Reporting enabled for this website?
+ * @param int $sitesearch Whether site search is enabled, 0 or 1
+ * @param string $searchKeywordParameters Comma separated list of search keyword parameter names
+ * @param string $searchCategoryParameters Comma separated list of search category parameter names
+ * @param string Comma separated list of IPs to exclude from the reports (allows wildcards)
+ * @param string Timezone string, eg. 'Europe/London'
+ * @param string Currency, eg. 'EUR'
+ * @param string Website group identifier
+ * @param string Date at which the statistics for this website will start. Defaults to today's date in YYYY-MM-DD format
+ * @param int $keepURLFragments If 1, URL fragments will be kept when tracking. If 2, they
+ * will be removed. If 0, the default global behavior will be used.
+ * @see getKeepURLFragmentsGlobal.
+ *
+ * @return int the website ID created
+ */
+ public function addSite($siteName,
+ $urls,
+ $ecommerce = null,
+ $siteSearch = null,
+ $searchKeywordParameters = null,
+ $searchCategoryParameters = null,
+ $excludedIps = null,
+ $excludedQueryParameters = null,
+ $timezone = null,
+ $currency = null,
+ $group = null,
+ $startDate = null,
+ $excludedUserAgents = null,
+ $keepURLFragments = 0)
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $this->checkName($siteName);
+ $urls = $this->cleanParameterUrls($urls);
+ $this->checkUrls($urls);
+ $this->checkAtLeastOneUrl($urls);
+ $siteSearch = $this->checkSiteSearch($siteSearch);
+ list($searchKeywordParameters, $searchCategoryParameters) = $this->checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters);
+
+ $keepURLFragments = (int)$keepURLFragments;
+ self::checkKeepURLFragmentsValue($keepURLFragments);
+
+ $timezone = trim($timezone);
+ if (empty($timezone)) {
+ $timezone = $this->getDefaultTimezone();
+ }
+ $this->checkValidTimezone($timezone);
+
+ if (empty($currency)) {
+ $currency = $this->getDefaultCurrency();
+ }
+ $this->checkValidCurrency($currency);
+
+ $db = Zend_Registry::get('db');
+
+ $url = $urls[0];
+ $urls = array_slice($urls, 1);
+
+ $bind = array('name' => $siteName,
+ 'main_url' => $url,
+
+ );
+
+ $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps);
+ $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['sitesearch'] = $siteSearch;
+ $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters;
+ $bind['sitesearch_category_parameters'] = $searchCategoryParameters;
+ $bind['ts_created'] = !is_null($startDate)
+ ? Piwik_Date::factory($startDate)->getDatetime()
+ : Piwik_Date::now()->getDatetime();
+
+ if (!empty($group)
+ && Piwik::isUserIsSuperUser()
+ ) {
+ $bind['group'] = trim($group);
+ } else {
+ $bind['group'] = "";
+ }
+
+ $db->insert(Piwik_Common::prefixTable("site"), $bind);
+
+ $idSite = $db->lastInsertId();
+
+ $this->insertSiteUrls($idSite, $urls);
+
+ // we reload the access list which doesn't yet take in consideration this new website
+ Zend_Registry::get('access')->reloadAccess();
+ $this->postUpdateWebsite($idSite);
+
+ Piwik_PostEvent('SitesManager.addSite', $idSite);
+
+ return (int)$idSite;
+ }
+
+ private function postUpdateWebsite($idSite)
+ {
+ Piwik_Site::clearCache();
+ Piwik_Tracker_Cache::regenerateCacheWebsiteAttributes($idSite);
+ }
+
+ /**
+ * Delete a website from the database, given its Id.
+ *
+ * Requires Super User access.
+ *
+ * @param int $idSite
+ * @throws Exception
+ */
+ public function deleteSite($idSite)
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $idSites = Piwik_SitesManager_API::getInstance()->getSitesId();
+ if (!in_array($idSite, $idSites)) {
+ throw new Exception("website id = $idSite not found");
+ }
+ $nbSites = count($idSites);
+ if ($nbSites == 1) {
+ throw new Exception(Piwik_TranslateException("SitesManager_ExceptionDeleteSite"));
+ }
+
+ $db = Zend_Registry::get('db');
+
+ $db->query("DELETE FROM " . Piwik_Common::prefixTable("site") . "
WHERE idsite = ?", $idSite);
-
- $db->query("DELETE FROM ".Piwik_Common::prefixTable("site_url")."
+
+ $db->query("DELETE FROM " . Piwik_Common::prefixTable("site_url") . "
WHERE idsite = ?", $idSite);
-
- $db->query("DELETE FROM ".Piwik_Common::prefixTable("access")."
+
+ $db->query("DELETE FROM " . Piwik_Common::prefixTable("access") . "
WHERE idsite = ?", $idSite);
- // we do not delete logs here on purpose (you can run these queries on the log_ tables to delete all data)
- Piwik_Tracker_Cache::deleteCacheWebsiteAttributes($idSite);
-
- Piwik_PostEvent('SitesManager.deleteSite', $idSite);
- }
-
-
- /**
- * Checks that the array has at least one element
- *
- * @param array $urls
- * @throws Exception
- */
- private function checkAtLeastOneUrl( $urls )
- {
- if(!is_array($urls)
- || count($urls) == 0)
- {
- throw new Exception(Piwik_TranslateException("SitesManager_ExceptionNoUrl"));
- }
- }
-
- private function checkValidTimezone($timezone)
- {
- $timezones = $this->getTimezonesList();
- foreach(array_values($timezones) as $cities)
- {
- foreach($cities as $timezoneId => $city)
- {
- if($timezoneId == $timezone)
- {
- return true;
- }
- }
- }
- throw new Exception(Piwik_TranslateException('SitesManager_ExceptionInvalidTimezone', array($timezone)));
- }
-
- private function checkValidCurrency($currency)
- {
- if(!in_array($currency, array_keys($this->getCurrencyList())))
- {
- throw new Exception(Piwik_TranslateException('SitesManager_ExceptionInvalidCurrency', array($currency, "USD, EUR, etc.")));
- }
- }
-
- /**
- * Checks that the submitted IPs (comma separated list) are valid
- * Returns the cleaned up IPs
- *
- * @param string $excludedIps Comma separated list of IP addresses
- * @throws Exception
- * @return array of IPs
- */
- private function checkAndReturnExcludedIps($excludedIps)
- {
- 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_TranslateException('SitesManager_ExceptionInvalidIPFormat', array($ip, "1.2.3.4, 1.2.3.*, or 1.2.3.4/5")));
- }
- }
- $ips = implode(',', $ips);
- return $ips;
- }
- /**
- * Add a list of alias Urls to the given idSite
- *
- * If some URLs given in parameter are already recorded as alias URLs for this website,
- * they won't be duplicated. The 'main_url' of the website won't be affected by this method.
- *
- * @return int the number of inserted URLs
- */
- public function addSiteAliasUrls( $idSite, $urls)
- {
- Piwik::checkUserHasAdminAccess( $idSite );
-
- $urls = $this->cleanParameterUrls($urls);
- $this->checkUrls($urls);
-
- $urlsInit = $this->getSiteUrlsFromId($idSite);
- $toInsert = array_diff($urls, $urlsInit);
- $this->insertSiteUrls($idSite, $toInsert);
- $this->postUpdateWebsite($idSite);
-
- return count($toInsert);
- }
-
- /**
- * Get the start and end IP addresses for an IP address range
- *
- * @param string $ipRange IP address range in presentation format
- * @return array|false Array( low, high ) IP addresses in presentation format; or false if error
- */
- public function getIpsForRange($ipRange)
- {
- $range = Piwik_IP::getIpsForRange($ipRange);
- if($range === false)
- {
- return false;
- }
-
- return array( Piwik_IP::N2P($range[0]), Piwik_IP::N2P($range[1]) );
- }
-
- /**
- * Sets IPs to be excluded from all websites. IPs can contain wildcards.
- * Will also apply to websites created in the future.
- *
- * @param string Comma separated list of IPs to exclude from being tracked (allows wildcards)
- * @return bool
- */
- public function setGlobalExcludedIps($excludedIps)
- {
- Piwik::checkUserIsSuperUser();
- $excludedIps = $this->checkAndReturnExcludedIps($excludedIps);
- Piwik_SetOption(self::OPTION_EXCLUDED_IPS_GLOBAL, $excludedIps);
- Piwik_Tracker_Cache::deleteTrackerCache();
- return true;
- }
-
- /**
- * Sets Site Search keyword/category parameter names, to be used on websites which have not specified these values
- * Expects Comma separated list of query params names
- *
- * @param string
- * @param string
- * @return bool
- */
- public function setGlobalSearchParameters($searchKeywordParameters, $searchCategoryParameters)
- {
- Piwik::checkUserIsSuperUser();
- Piwik_SetOption(self::OPTION_SEARCH_KEYWORD_QUERY_PARAMETERS_GLOBAL, $searchKeywordParameters);
- Piwik_SetOption(self::OPTION_SEARCH_CATEGORY_QUERY_PARAMETERS_GLOBAL, $searchCategoryParameters);
- Piwik_Tracker_Cache::deleteTrackerCache();
- return true;
- }
-
- /**
- * @return string Comma separated list of URL parameters
- */
- public function getSearchKeywordParametersGlobal()
- {
- Piwik::checkUserHasSomeAdminAccess();
- $names = Piwik_GetOption(self::OPTION_SEARCH_KEYWORD_QUERY_PARAMETERS_GLOBAL);
- if($names === false) {
- $names = self::DEFAULT_SEARCH_KEYWORD_PARAMETERS;
- }
- if(empty($names)) {
- $names = '';
- }
- return $names;
- }
-
- /**
- * @return string Comma separated list of URL parameters
- */
- public function getSearchCategoryParametersGlobal()
- {
- Piwik::checkUserHasSomeAdminAccess();
- return Piwik_GetOption(self::OPTION_SEARCH_CATEGORY_QUERY_PARAMETERS_GLOBAL);
- }
-
- /**
- * Returns the list of URL query parameters that are excluded from all websites
- *
- * @return string Comma separated list of URL parameters
- */
- public function getExcludedQueryParametersGlobal()
- {
- Piwik::checkUserHasSomeViewAccess();
- return Piwik_GetOption(self::OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL);
- }
-
- /**
- * Returns the list of user agent substrings to look for when excluding visits for
- * all websites. If a visitor's user agent string contains one of these substrings,
- * their visits will not be included.
- *
- * @return string Comma separated list of strings.
- */
- public function getExcludedUserAgentsGlobal()
- {
- Piwik::checkUserHasSomeAdminAccess();
- return Piwik_GetOption(self::OPTION_EXCLUDED_USER_AGENTS_GLOBAL);
- }
-
- /**
- * Sets list of user agent substrings to look for when excluding visits. For more info,
- * @see getExcludedUserAgentsGlobal.
- *
- * @param string $excludedUserAgents Comma separated list of strings. Each element is trimmed,
- * and empty strings are removed.
- */
- public function setGlobalExcludedUserAgents( $excludedUserAgents )
- {
- Piwik::checkUserIsSuperUser();
-
- // update option
- $excludedUserAgents = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents);
- Piwik_SetOption(self::OPTION_EXCLUDED_USER_AGENTS_GLOBAL, $excludedUserAgents);
-
- // make sure tracker cache will reflect change
- Piwik_Tracker_Cache::deleteTrackerCache();
- }
-
- /**
- * Returns true if site-specific user agent exclusion has been enabled. If it hasn't,
- * only the global user agent substrings (see @setGlobalExcludedUserAgents) will be used.
- *
- * @return bool
- */
- public function isSiteSpecificUserAgentExcludeEnabled()
- {
- Piwik::checkUserHasSomeAdminAccess();
- return (bool)Piwik_GetOption(self::OPTION_SITE_SPECIFIC_USER_AGENT_EXCLUDE_ENABLE);
- }
-
- /**
- * Sets whether it should be allowed to exclude different user agents for different
- * websites.
- *
- * @param bool $enabled
- */
- public function setSiteSpecificUserAgentExcludeEnabled( $enabled )
- {
- Piwik::checkUserIsSuperUser();
-
- // update option
- Piwik_SetOption(self::OPTION_SITE_SPECIFIC_USER_AGENT_EXCLUDE_ENABLE, $enabled);
-
- // make sure tracker cache will reflect change
- Piwik_Tracker_Cache::deleteTrackerCache();
- }
-
- /**
- * Returns true if the default behavior is to keep URL fragments when tracking,
- * false if otherwise.
- *
- * @return bool
- */
- public function getKeepURLFragmentsGlobal()
- {
- Piwik::checkUserHasSomeViewAccess();
- return (bool)Piwik_GetOption(self::OPTION_KEEP_URL_FRAGMENTS_GLOBAL);
- }
-
- /**
- * Sets whether the default behavior should be to keep URL fragments when
- * tracking or not.
- *
- * @param $enabled bool If true, the default behavior will be to keep URL
- * fragments when tracking. If false, the default
- * behavior will be to remove them.
- */
- public function setKeepURLFragmentsGlobal( $enabled )
- {
- Piwik::checkUserIsSuperUser();
-
- // update option
- Piwik_SetOption(self::OPTION_KEEP_URL_FRAGMENTS_GLOBAL, $enabled);
-
- // make sure tracker cache will reflect change
- Piwik_Tracker_Cache::deleteTrackerCache();
- }
-
- /**
- * Sets list of URL query parameters to be excluded on all websites.
- * Will also apply to websites created in the future.
- *
- * @param string Comma separated list of URL query parameters to exclude from URLs
- * @return bool
- */
- public function setGlobalExcludedQueryParameters($excludedQueryParameters)
- {
- Piwik::checkUserIsSuperUser();
- $excludedQueryParameters = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters);
- Piwik_SetOption(self::OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL, $excludedQueryParameters);
- Piwik_Tracker_Cache::deleteTrackerCache();
- return true;
- }
-
- /**
- * Returns the list of IPs that are excluded from all websites
- *
- * @return string Comma separated list of IPs
- */
- public function getExcludedIpsGlobal()
- {
- Piwik::checkUserHasSomeAdminAccess();
- return Piwik_GetOption(self::OPTION_EXCLUDED_IPS_GLOBAL);
- }
-
- /**
- * Returns the default currency that will be set when creating a website through the API.
- *
- * @return string Currency ID eg. 'USD'
- */
- public function getDefaultCurrency()
- {
- Piwik::checkUserHasSomeAdminAccess();
- $defaultCurrency = Piwik_GetOption(self::OPTION_DEFAULT_CURRENCY);
- if($defaultCurrency)
- {
- return $defaultCurrency;
- }
- return 'USD';
- }
-
- /**
- * Sets the default currency that will be used when creating websites
- *
- * @param string $defaultCurrency Currency code, eg. 'USD'
- * @return bool
- */
- public function setDefaultCurrency($defaultCurrency)
- {
- Piwik::checkUserIsSuperUser();
- $this->checkValidCurrency($defaultCurrency);
- Piwik_SetOption(self::OPTION_DEFAULT_CURRENCY, $defaultCurrency);
- return true;
- }
-
- /**
- * Returns the default timezone that will be set when creating a website through the API.
- * Via the UI, if the default timezone is not UTC, it will be pre-selected in the drop down
- *
- * @return string Timezone eg. UTC+7 or Europe/Paris
- */
- public function getDefaultTimezone()
- {
- $defaultTimezone = Piwik_GetOption(self::OPTION_DEFAULT_TIMEZONE);
- if($defaultTimezone)
- {
- return $defaultTimezone;
- }
- return 'UTC';
- }
-
- /**
- * Sets the default timezone that will be used when creating websites
- *
- * @param string $defaultTimezone Timezone string eg. Europe/Paris or UTC+8
- * @return bool
- */
- public function setDefaultTimezone($defaultTimezone)
- {
- Piwik::checkUserIsSuperUser();
- $this->checkValidTimezone($defaultTimezone);
- Piwik_SetOption(self::OPTION_DEFAULT_TIMEZONE, $defaultTimezone);
- return true;
- }
-
- /**
- * Update an existing website.
- * If only one URL is specified then only the main url will be updated.
- * If several URLs are specified, both the main URL and the alias URLs will be updated.
- *
- * @param int $idSite website ID defining the website to edit
- * @param string $siteName website name
- * @param string|array $urls the website URLs
- * @param int $ecommerce Whether Ecommerce is enabled, 0 or 1
- * @param int $sitesearch Whether site search is enabled, 0 or 1
- * @param string $searchKeywordParameters Comma separated list of search keyword parameter names
- * @param string $searchCategoryParameters Comma separated list of search category parameter names
- * @param string $excludedIps Comma separated list of IPs to exclude from being tracked (allows wildcards)
- * @param null $excludedQueryParameters
- * @param string $timezone Timezone
- * @param string $currency Currency code
- * @param string $group Group name where this website belongs
- * @param string $startDate Date at which the statistics for this website will start. Defaults to today's date in YYYY-MM-DD format
- * @param int|null $keepURLFragments If 1, URL fragments will be kept when tracking. If 2, they
- * will be removed. If 0, the default global behavior will be used.
- * @see getKeepURLFragmentsGlobal. If null, the existing value will
- * not be modified.
- *
- * @throws Exception
- * @return bool true on success
- */
- public function updateSite( $idSite,
- $siteName,
- $urls = null,
- $ecommerce = null,
- $siteSearch = null,
- $searchKeywordParameters = null,
- $searchCategoryParameters = null,
- $excludedIps = null,
- $excludedQueryParameters = null,
- $timezone = null,
- $currency = null,
- $group = null,
- $startDate = null,
- $excludedUserAgents = null,
- $keepURLFragments = null)
- {
- Piwik::checkUserHasAdminAccess($idSite);
-
- $idSites = Piwik_SitesManager_API::getInstance()->getSitesId();
- if(!in_array($idSite, $idSites))
- {
- throw new Exception("website id = $idSite not found");
- }
-
- $this->checkName($siteName);
-
- // Build the SQL UPDATE based on specified updates to perform
- $bind = array();
- if(!is_null($urls))
- {
- $urls = $this->cleanParameterUrls($urls);
- $this->checkUrls($urls);
- $this->checkAtLeastOneUrl($urls);
- $url = $urls[0];
- $bind['main_url'] = $url;
- }
-
- if(!is_null($currency))
- {
- $currency = trim($currency);
- $this->checkValidCurrency($currency);
- $bind['currency'] = $currency;
- }
- if(!is_null($timezone))
- {
- $timezone = trim($timezone);
- $this->checkValidTimezone($timezone);
- $bind['timezone'] = $timezone;
- }
- if(!is_null($group)
- && Piwik::isUserIsSuperUser())
- {
- $bind['group'] = trim($group);
- }
- if(!is_null($ecommerce))
- {
- $bind['ecommerce'] = (int)(bool)$ecommerce;
- }
- if(!is_null($startDate))
- {
- $bind['ts_created'] = Piwik_Date::factory($startDate)->getDatetime();
- }
- $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps);
- $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters);
- $bind['excluded_user_agents'] = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents);
-
- if (!is_null($keepURLFragments))
- {
- $keepURLFragments = (int)$keepURLFragments;
- self::checkKeepURLFragmentsValue($keepURLFragments);
-
- $bind['keep_url_fragment'] = $keepURLFragments;
- }
-
- $bind['sitesearch'] = $this->checkSiteSearch($siteSearch);
- list($searchKeywordParameters, $searchCategoryParameters ) = $this->checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters);
- $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters;
- $bind['sitesearch_category_parameters'] = $searchCategoryParameters;
-
- $bind['name'] = $siteName;
- $db = Zend_Registry::get('db');
- $db->update(Piwik_Common::prefixTable("site"),
- $bind,
- "idsite = $idSite"
- );
-
- // we now update the main + alias URLs
- $this->deleteSiteAliasUrls($idSite);
- if(count($urls) > 1)
- {
- $this->addSiteAliasUrls($idSite, array_slice($urls,1));
- }
- $this->postUpdateWebsite($idSite);
-
- Piwik_PostEvent('SitesManager.updateSite', $idSite);
- }
-
- private function checkAndReturnCommaSeparatedStringList($parameters)
- {
- $parameters = trim($parameters);
- if(empty($parameters))
- {
- return '';
- }
-
- $parameters = explode(',', $parameters);
- $parameters = array_map('trim', $parameters);
- $parameters = array_filter($parameters, 'strlen');
- $parameters = array_unique($parameters);
- return implode(',', $parameters);
- }
-
- /**
- * Returns the list of supported currencies
- * @see getCurrencySymbols()
- * @return array ( currencyId => currencyName)
- */
- public function getCurrencyList()
- {
- $currencies = Piwik::getCurrencyList();
- return array_map(create_function('$a', 'return $a[1]." (".$a[0].")";'), $currencies);
- }
-
- /**
- * Returns the list of currency symbols
- * @see getCurrencyList()
- * @return array( currencyId => currencySymbol )
- */
- public function getCurrencySymbols()
- {
- $currencies = Piwik::getCurrencyList();
- return array_map(create_function('$a', 'return $a[0];'), $currencies);
- }
-
- /**
- * Returns the list of timezones supported.
- * Used for addSite and updateSite
- *
- * @TODO NOT COMPATIBLE WITH API RESPONSE AUTO BUILDER
- *
- * @return array of timezone strings
- */
- public function getTimezonesList()
- {
- if(!Piwik::isTimezoneSupportEnabled())
- {
- return array('UTC' => $this->getTimezonesListUTCOffsets());
- }
-
- $continents = array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific');
- $timezones = timezone_identifiers_list();
-
- $return = array();
- foreach($timezones as $timezone)
- {
- // filter out timezones not recognized by strtotime()
- // @see http://bugs.php.net/46111
- $testDate = '2008-09-18 13:00:00 ' . $timezone;
- if(!strtotime($testDate))
- {
- continue;
- }
-
- $timezoneExploded = explode('/', $timezone);
- $continent = $timezoneExploded[0];
-
- // only display timezones that are grouped by continent
- if(!in_array($continent, $continents))
- {
- continue;
- }
- $city = $timezoneExploded[1];
- if(!empty($timezoneExploded[2]))
- {
- $city .= ' - '.$timezoneExploded[2];
- }
- $city = str_replace('_', ' ', $city);
- $return[$continent][$timezone] = $city;
- }
-
- foreach($continents as $continent)
- {
- if(!empty($return[$continent]))
- {
- ksort($return[$continent]);
- }
- }
-
- $return['UTC'] = $this->getTimezonesListUTCOffsets();
- return $return;
- }
-
- private function getTimezonesListUTCOffsets()
- {
- // manually add the UTC offsets
- $GmtOffsets = array (-12, -11.5, -11, -10.5, -10, -9.5, -9, -8.5, -8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5,
- 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 5.75, 6, 6.5, 7, 7.5, 8, 8.5, 8.75, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.75, 13, 13.75, 14);
-
- $return = array();
- foreach($GmtOffsets as $offset)
- {
- if($offset > 0)
- {
- $offset = '+'.$offset;
- }
- elseif($offset == 0)
- {
- $offset = '';
- }
- $offset = 'UTC' . $offset;
- $offsetName = str_replace(array('.25','.5','.75'), array(':15',':30',':45'), $offset);
- $return[$offset] = $offsetName;
- }
- return $return;
- }
-
- /**
- * Returns the list of unique timezones from all configured sites.
- *
- * @return array ( string )
- */
- public function getUniqueSiteTimezones()
- {
- Piwik::checkUserIsSuperUser();
- $results = Piwik_FetchAll("SELECT distinct timezone FROM ".Piwik_Common::prefixTable('site'));
- $timezones = array();
- foreach($results as $result)
- {
- $timezones[] = $result['timezone'];
- }
- return $timezones;
- }
-
- /**
- * Insert the list of alias URLs for the website.
- * The URLs must not exist already for this website!
- */
- private function insertSiteUrls($idSite, $urls)
- {
- if(count($urls) != 0)
- {
- $db = Zend_Registry::get('db');
- foreach($urls as $url)
- {
- $db->insert(Piwik_Common::prefixTable("site_url"), array(
- 'idsite' => $idSite,
- 'url' => $url
- )
- );
- }
- }
- }
-
- /**
- * Delete all the alias URLs for the given idSite.
- */
- private function deleteSiteAliasUrls($idsite)
- {
- $db = Zend_Registry::get('db');
- $db->query("DELETE FROM ".Piwik_Common::prefixTable("site_url") ."
+ // we do not delete logs here on purpose (you can run these queries on the log_ tables to delete all data)
+ Piwik_Tracker_Cache::deleteCacheWebsiteAttributes($idSite);
+
+ Piwik_PostEvent('SitesManager.deleteSite', $idSite);
+ }
+
+
+ /**
+ * Checks that the array has at least one element
+ *
+ * @param array $urls
+ * @throws Exception
+ */
+ private function checkAtLeastOneUrl($urls)
+ {
+ if (!is_array($urls)
+ || count($urls) == 0
+ ) {
+ throw new Exception(Piwik_TranslateException("SitesManager_ExceptionNoUrl"));
+ }
+ }
+
+ private function checkValidTimezone($timezone)
+ {
+ $timezones = $this->getTimezonesList();
+ foreach (array_values($timezones) as $cities) {
+ foreach ($cities as $timezoneId => $city) {
+ if ($timezoneId == $timezone) {
+ return true;
+ }
+ }
+ }
+ throw new Exception(Piwik_TranslateException('SitesManager_ExceptionInvalidTimezone', array($timezone)));
+ }
+
+ private function checkValidCurrency($currency)
+ {
+ if (!in_array($currency, array_keys($this->getCurrencyList()))) {
+ throw new Exception(Piwik_TranslateException('SitesManager_ExceptionInvalidCurrency', array($currency, "USD, EUR, etc.")));
+ }
+ }
+
+ /**
+ * Checks that the submitted IPs (comma separated list) are valid
+ * Returns the cleaned up IPs
+ *
+ * @param string $excludedIps Comma separated list of IP addresses
+ * @throws Exception
+ * @return array of IPs
+ */
+ private function checkAndReturnExcludedIps($excludedIps)
+ {
+ 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_TranslateException('SitesManager_ExceptionInvalidIPFormat', array($ip, "1.2.3.4, 1.2.3.*, or 1.2.3.4/5")));
+ }
+ }
+ $ips = implode(',', $ips);
+ return $ips;
+ }
+
+ /**
+ * Add a list of alias Urls to the given idSite
+ *
+ * If some URLs given in parameter are already recorded as alias URLs for this website,
+ * they won't be duplicated. The 'main_url' of the website won't be affected by this method.
+ *
+ * @return int the number of inserted URLs
+ */
+ public function addSiteAliasUrls($idSite, $urls)
+ {
+ Piwik::checkUserHasAdminAccess($idSite);
+
+ $urls = $this->cleanParameterUrls($urls);
+ $this->checkUrls($urls);
+
+ $urlsInit = $this->getSiteUrlsFromId($idSite);
+ $toInsert = array_diff($urls, $urlsInit);
+ $this->insertSiteUrls($idSite, $toInsert);
+ $this->postUpdateWebsite($idSite);
+
+ return count($toInsert);
+ }
+
+ /**
+ * Get the start and end IP addresses for an IP address range
+ *
+ * @param string $ipRange IP address range in presentation format
+ * @return array|false Array( low, high ) IP addresses in presentation format; or false if error
+ */
+ public function getIpsForRange($ipRange)
+ {
+ $range = Piwik_IP::getIpsForRange($ipRange);
+ if ($range === false) {
+ return false;
+ }
+
+ return array(Piwik_IP::N2P($range[0]), Piwik_IP::N2P($range[1]));
+ }
+
+ /**
+ * Sets IPs to be excluded from all websites. IPs can contain wildcards.
+ * Will also apply to websites created in the future.
+ *
+ * @param string Comma separated list of IPs to exclude from being tracked (allows wildcards)
+ * @return bool
+ */
+ public function setGlobalExcludedIps($excludedIps)
+ {
+ Piwik::checkUserIsSuperUser();
+ $excludedIps = $this->checkAndReturnExcludedIps($excludedIps);
+ Piwik_SetOption(self::OPTION_EXCLUDED_IPS_GLOBAL, $excludedIps);
+ Piwik_Tracker_Cache::deleteTrackerCache();
+ return true;
+ }
+
+ /**
+ * Sets Site Search keyword/category parameter names, to be used on websites which have not specified these values
+ * Expects Comma separated list of query params names
+ *
+ * @param string
+ * @param string
+ * @return bool
+ */
+ public function setGlobalSearchParameters($searchKeywordParameters, $searchCategoryParameters)
+ {
+ Piwik::checkUserIsSuperUser();
+ Piwik_SetOption(self::OPTION_SEARCH_KEYWORD_QUERY_PARAMETERS_GLOBAL, $searchKeywordParameters);
+ Piwik_SetOption(self::OPTION_SEARCH_CATEGORY_QUERY_PARAMETERS_GLOBAL, $searchCategoryParameters);
+ Piwik_Tracker_Cache::deleteTrackerCache();
+ return true;
+ }
+
+ /**
+ * @return string Comma separated list of URL parameters
+ */
+ public function getSearchKeywordParametersGlobal()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ $names = Piwik_GetOption(self::OPTION_SEARCH_KEYWORD_QUERY_PARAMETERS_GLOBAL);
+ if ($names === false) {
+ $names = self::DEFAULT_SEARCH_KEYWORD_PARAMETERS;
+ }
+ if (empty($names)) {
+ $names = '';
+ }
+ return $names;
+ }
+
+ /**
+ * @return string Comma separated list of URL parameters
+ */
+ public function getSearchCategoryParametersGlobal()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ return Piwik_GetOption(self::OPTION_SEARCH_CATEGORY_QUERY_PARAMETERS_GLOBAL);
+ }
+
+ /**
+ * Returns the list of URL query parameters that are excluded from all websites
+ *
+ * @return string Comma separated list of URL parameters
+ */
+ public function getExcludedQueryParametersGlobal()
+ {
+ Piwik::checkUserHasSomeViewAccess();
+ return Piwik_GetOption(self::OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL);
+ }
+
+ /**
+ * Returns the list of user agent substrings to look for when excluding visits for
+ * all websites. If a visitor's user agent string contains one of these substrings,
+ * their visits will not be included.
+ *
+ * @return string Comma separated list of strings.
+ */
+ public function getExcludedUserAgentsGlobal()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ return Piwik_GetOption(self::OPTION_EXCLUDED_USER_AGENTS_GLOBAL);
+ }
+
+ /**
+ * Sets list of user agent substrings to look for when excluding visits. For more info,
+ * @see getExcludedUserAgentsGlobal.
+ *
+ * @param string $excludedUserAgents Comma separated list of strings. Each element is trimmed,
+ * and empty strings are removed.
+ */
+ public function setGlobalExcludedUserAgents($excludedUserAgents)
+ {
+ Piwik::checkUserIsSuperUser();
+
+ // update option
+ $excludedUserAgents = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents);
+ Piwik_SetOption(self::OPTION_EXCLUDED_USER_AGENTS_GLOBAL, $excludedUserAgents);
+
+ // make sure tracker cache will reflect change
+ Piwik_Tracker_Cache::deleteTrackerCache();
+ }
+
+ /**
+ * Returns true if site-specific user agent exclusion has been enabled. If it hasn't,
+ * only the global user agent substrings (see @setGlobalExcludedUserAgents) will be used.
+ *
+ * @return bool
+ */
+ public function isSiteSpecificUserAgentExcludeEnabled()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ return (bool)Piwik_GetOption(self::OPTION_SITE_SPECIFIC_USER_AGENT_EXCLUDE_ENABLE);
+ }
+
+ /**
+ * Sets whether it should be allowed to exclude different user agents for different
+ * websites.
+ *
+ * @param bool $enabled
+ */
+ public function setSiteSpecificUserAgentExcludeEnabled($enabled)
+ {
+ Piwik::checkUserIsSuperUser();
+
+ // update option
+ Piwik_SetOption(self::OPTION_SITE_SPECIFIC_USER_AGENT_EXCLUDE_ENABLE, $enabled);
+
+ // make sure tracker cache will reflect change
+ Piwik_Tracker_Cache::deleteTrackerCache();
+ }
+
+ /**
+ * Returns true if the default behavior is to keep URL fragments when tracking,
+ * false if otherwise.
+ *
+ * @return bool
+ */
+ public function getKeepURLFragmentsGlobal()
+ {
+ Piwik::checkUserHasSomeViewAccess();
+ return (bool)Piwik_GetOption(self::OPTION_KEEP_URL_FRAGMENTS_GLOBAL);
+ }
+
+ /**
+ * Sets whether the default behavior should be to keep URL fragments when
+ * tracking or not.
+ *
+ * @param $enabled bool If true, the default behavior will be to keep URL
+ * fragments when tracking. If false, the default
+ * behavior will be to remove them.
+ */
+ public function setKeepURLFragmentsGlobal($enabled)
+ {
+ Piwik::checkUserIsSuperUser();
+
+ // update option
+ Piwik_SetOption(self::OPTION_KEEP_URL_FRAGMENTS_GLOBAL, $enabled);
+
+ // make sure tracker cache will reflect change
+ Piwik_Tracker_Cache::deleteTrackerCache();
+ }
+
+ /**
+ * Sets list of URL query parameters to be excluded on all websites.
+ * Will also apply to websites created in the future.
+ *
+ * @param string Comma separated list of URL query parameters to exclude from URLs
+ * @return bool
+ */
+ public function setGlobalExcludedQueryParameters($excludedQueryParameters)
+ {
+ Piwik::checkUserIsSuperUser();
+ $excludedQueryParameters = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters);
+ Piwik_SetOption(self::OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL, $excludedQueryParameters);
+ Piwik_Tracker_Cache::deleteTrackerCache();
+ return true;
+ }
+
+ /**
+ * Returns the list of IPs that are excluded from all websites
+ *
+ * @return string Comma separated list of IPs
+ */
+ public function getExcludedIpsGlobal()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ return Piwik_GetOption(self::OPTION_EXCLUDED_IPS_GLOBAL);
+ }
+
+ /**
+ * Returns the default currency that will be set when creating a website through the API.
+ *
+ * @return string Currency ID eg. 'USD'
+ */
+ public function getDefaultCurrency()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+ $defaultCurrency = Piwik_GetOption(self::OPTION_DEFAULT_CURRENCY);
+ if ($defaultCurrency) {
+ return $defaultCurrency;
+ }
+ return 'USD';
+ }
+
+ /**
+ * Sets the default currency that will be used when creating websites
+ *
+ * @param string $defaultCurrency Currency code, eg. 'USD'
+ * @return bool
+ */
+ public function setDefaultCurrency($defaultCurrency)
+ {
+ Piwik::checkUserIsSuperUser();
+ $this->checkValidCurrency($defaultCurrency);
+ Piwik_SetOption(self::OPTION_DEFAULT_CURRENCY, $defaultCurrency);
+ return true;
+ }
+
+ /**
+ * Returns the default timezone that will be set when creating a website through the API.
+ * Via the UI, if the default timezone is not UTC, it will be pre-selected in the drop down
+ *
+ * @return string Timezone eg. UTC+7 or Europe/Paris
+ */
+ public function getDefaultTimezone()
+ {
+ $defaultTimezone = Piwik_GetOption(self::OPTION_DEFAULT_TIMEZONE);
+ if ($defaultTimezone) {
+ return $defaultTimezone;
+ }
+ return 'UTC';
+ }
+
+ /**
+ * Sets the default timezone that will be used when creating websites
+ *
+ * @param string $defaultTimezone Timezone string eg. Europe/Paris or UTC+8
+ * @return bool
+ */
+ public function setDefaultTimezone($defaultTimezone)
+ {
+ Piwik::checkUserIsSuperUser();
+ $this->checkValidTimezone($defaultTimezone);
+ Piwik_SetOption(self::OPTION_DEFAULT_TIMEZONE, $defaultTimezone);
+ return true;
+ }
+
+ /**
+ * Update an existing website.
+ * If only one URL is specified then only the main url will be updated.
+ * If several URLs are specified, both the main URL and the alias URLs will be updated.
+ *
+ * @param int $idSite website ID defining the website to edit
+ * @param string $siteName website name
+ * @param string|array $urls the website URLs
+ * @param int $ecommerce Whether Ecommerce is enabled, 0 or 1
+ * @param int $sitesearch Whether site search is enabled, 0 or 1
+ * @param string $searchKeywordParameters Comma separated list of search keyword parameter names
+ * @param string $searchCategoryParameters Comma separated list of search category parameter names
+ * @param string $excludedIps Comma separated list of IPs to exclude from being tracked (allows wildcards)
+ * @param null $excludedQueryParameters
+ * @param string $timezone Timezone
+ * @param string $currency Currency code
+ * @param string $group Group name where this website belongs
+ * @param string $startDate Date at which the statistics for this website will start. Defaults to today's date in YYYY-MM-DD format
+ * @param int|null $keepURLFragments If 1, URL fragments will be kept when tracking. If 2, they
+ * will be removed. If 0, the default global behavior will be used.
+ * @see getKeepURLFragmentsGlobal. If null, the existing value will
+ * not be modified.
+ *
+ * @throws Exception
+ * @return bool true on success
+ */
+ public function updateSite($idSite,
+ $siteName,
+ $urls = null,
+ $ecommerce = null,
+ $siteSearch = null,
+ $searchKeywordParameters = null,
+ $searchCategoryParameters = null,
+ $excludedIps = null,
+ $excludedQueryParameters = null,
+ $timezone = null,
+ $currency = null,
+ $group = null,
+ $startDate = null,
+ $excludedUserAgents = null,
+ $keepURLFragments = null)
+ {
+ Piwik::checkUserHasAdminAccess($idSite);
+
+ $idSites = Piwik_SitesManager_API::getInstance()->getSitesId();
+ if (!in_array($idSite, $idSites)) {
+ throw new Exception("website id = $idSite not found");
+ }
+
+ $this->checkName($siteName);
+
+ // Build the SQL UPDATE based on specified updates to perform
+ $bind = array();
+ if (!is_null($urls)) {
+ $urls = $this->cleanParameterUrls($urls);
+ $this->checkUrls($urls);
+ $this->checkAtLeastOneUrl($urls);
+ $url = $urls[0];
+ $bind['main_url'] = $url;
+ }
+
+ if (!is_null($currency)) {
+ $currency = trim($currency);
+ $this->checkValidCurrency($currency);
+ $bind['currency'] = $currency;
+ }
+ if (!is_null($timezone)) {
+ $timezone = trim($timezone);
+ $this->checkValidTimezone($timezone);
+ $bind['timezone'] = $timezone;
+ }
+ if (!is_null($group)
+ && Piwik::isUserIsSuperUser()
+ ) {
+ $bind['group'] = trim($group);
+ }
+ if (!is_null($ecommerce)) {
+ $bind['ecommerce'] = (int)(bool)$ecommerce;
+ }
+ if (!is_null($startDate)) {
+ $bind['ts_created'] = Piwik_Date::factory($startDate)->getDatetime();
+ }
+ $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps);
+ $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters);
+ $bind['excluded_user_agents'] = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents);
+
+ if (!is_null($keepURLFragments)) {
+ $keepURLFragments = (int)$keepURLFragments;
+ self::checkKeepURLFragmentsValue($keepURLFragments);
+
+ $bind['keep_url_fragment'] = $keepURLFragments;
+ }
+
+ $bind['sitesearch'] = $this->checkSiteSearch($siteSearch);
+ list($searchKeywordParameters, $searchCategoryParameters) = $this->checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters);
+ $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters;
+ $bind['sitesearch_category_parameters'] = $searchCategoryParameters;
+
+ $bind['name'] = $siteName;
+ $db = Zend_Registry::get('db');
+ $db->update(Piwik_Common::prefixTable("site"),
+ $bind,
+ "idsite = $idSite"
+ );
+
+ // we now update the main + alias URLs
+ $this->deleteSiteAliasUrls($idSite);
+ if (count($urls) > 1) {
+ $this->addSiteAliasUrls($idSite, array_slice($urls, 1));
+ }
+ $this->postUpdateWebsite($idSite);
+
+ Piwik_PostEvent('SitesManager.updateSite', $idSite);
+ }
+
+ private function checkAndReturnCommaSeparatedStringList($parameters)
+ {
+ $parameters = trim($parameters);
+ if (empty($parameters)) {
+ return '';
+ }
+
+ $parameters = explode(',', $parameters);
+ $parameters = array_map('trim', $parameters);
+ $parameters = array_filter($parameters, 'strlen');
+ $parameters = array_unique($parameters);
+ return implode(',', $parameters);
+ }
+
+ /**
+ * Returns the list of supported currencies
+ * @see getCurrencySymbols()
+ * @return array ( currencyId => currencyName)
+ */
+ public function getCurrencyList()
+ {
+ $currencies = Piwik::getCurrencyList();
+ return array_map(create_function('$a', 'return $a[1]." (".$a[0].")";'), $currencies);
+ }
+
+ /**
+ * Returns the list of currency symbols
+ * @see getCurrencyList()
+ * @return array( currencyId => currencySymbol )
+ */
+ public function getCurrencySymbols()
+ {
+ $currencies = Piwik::getCurrencyList();
+ return array_map(create_function('$a', 'return $a[0];'), $currencies);
+ }
+
+ /**
+ * Returns the list of timezones supported.
+ * Used for addSite and updateSite
+ *
+ * @TODO NOT COMPATIBLE WITH API RESPONSE AUTO BUILDER
+ *
+ * @return array of timezone strings
+ */
+ public function getTimezonesList()
+ {
+ if (!Piwik::isTimezoneSupportEnabled()) {
+ return array('UTC' => $this->getTimezonesListUTCOffsets());
+ }
+
+ $continents = array('Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific');
+ $timezones = timezone_identifiers_list();
+
+ $return = array();
+ foreach ($timezones as $timezone) {
+ // filter out timezones not recognized by strtotime()
+ // @see http://bugs.php.net/46111
+ $testDate = '2008-09-18 13:00:00 ' . $timezone;
+ if (!strtotime($testDate)) {
+ continue;
+ }
+
+ $timezoneExploded = explode('/', $timezone);
+ $continent = $timezoneExploded[0];
+
+ // only display timezones that are grouped by continent
+ if (!in_array($continent, $continents)) {
+ continue;
+ }
+ $city = $timezoneExploded[1];
+ if (!empty($timezoneExploded[2])) {
+ $city .= ' - ' . $timezoneExploded[2];
+ }
+ $city = str_replace('_', ' ', $city);
+ $return[$continent][$timezone] = $city;
+ }
+
+ foreach ($continents as $continent) {
+ if (!empty($return[$continent])) {
+ ksort($return[$continent]);
+ }
+ }
+
+ $return['UTC'] = $this->getTimezonesListUTCOffsets();
+ return $return;
+ }
+
+ private function getTimezonesListUTCOffsets()
+ {
+ // manually add the UTC offsets
+ $GmtOffsets = array(-12, -11.5, -11, -10.5, -10, -9.5, -9, -8.5, -8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5,
+ 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 5.75, 6, 6.5, 7, 7.5, 8, 8.5, 8.75, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.75, 13, 13.75, 14);
+
+ $return = array();
+ foreach ($GmtOffsets as $offset) {
+ if ($offset > 0) {
+ $offset = '+' . $offset;
+ } elseif ($offset == 0) {
+ $offset = '';
+ }
+ $offset = 'UTC' . $offset;
+ $offsetName = str_replace(array('.25', '.5', '.75'), array(':15', ':30', ':45'), $offset);
+ $return[$offset] = $offsetName;
+ }
+ return $return;
+ }
+
+ /**
+ * Returns the list of unique timezones from all configured sites.
+ *
+ * @return array ( string )
+ */
+ public function getUniqueSiteTimezones()
+ {
+ Piwik::checkUserIsSuperUser();
+ $results = Piwik_FetchAll("SELECT distinct timezone FROM " . Piwik_Common::prefixTable('site'));
+ $timezones = array();
+ foreach ($results as $result) {
+ $timezones[] = $result['timezone'];
+ }
+ return $timezones;
+ }
+
+ /**
+ * Insert the list of alias URLs for the website.
+ * The URLs must not exist already for this website!
+ */
+ private function insertSiteUrls($idSite, $urls)
+ {
+ if (count($urls) != 0) {
+ $db = Zend_Registry::get('db');
+ foreach ($urls as $url) {
+ $db->insert(Piwik_Common::prefixTable("site_url"), array(
+ 'idsite' => $idSite,
+ 'url' => $url
+ )
+ );
+ }
+ }
+ }
+
+ /**
+ * Delete all the alias URLs for the given idSite.
+ */
+ private function deleteSiteAliasUrls($idsite)
+ {
+ $db = Zend_Registry::get('db');
+ $db->query("DELETE FROM " . Piwik_Common::prefixTable("site_url") . "
WHERE idsite = ?", $idsite);
- }
-
- /**
- * Remove the final slash in the URLs if found
- *
- * @return string the URL without the trailing slash
- */
- private function removeTrailingSlash($url)
- {
- // if there is a final slash, we take the URL without this slash (expected URL format)
- if(strlen($url) > 5
- && $url[strlen($url)-1] == '/')
- {
- $url = substr($url,0,strlen($url)-1);
- }
- return $url;
- }
-
- /**
- * Tests if the URL is a valid URL
- *
- * @return bool
- */
- private function isValidUrl( $url )
- {
- return Piwik_Common::isLookLikeUrl($url);
- }
-
- /**
- * Tests if the IP is a valid IP, allowing wildcards, except in the first octet.
- * Wildcards can only be used from right to left, ie. 1.1.*.* is allowed, but 1.1.*.1 is not.
- *
- * @param string $ip IP address
- * @return bool
- */
- private function isValidIp( $ip )
- {
- return Piwik_IP::getIpsForRange($ip) !== false;
- }
-
- /**
- * Check that the website name has a correct format.
- *
- * @param $siteName
- * @throws Exception
- */
- private function checkName($siteName)
- {
- if(empty($siteName))
- {
- throw new Exception(Piwik_TranslateException("SitesManager_ExceptionEmptyName"));
- }
- }
-
-
- private function checkSiteSearch($siteSearch)
- {
- if($siteSearch === null) {
- return "1";
- }
- return $siteSearch == 1 ? "1" : "0";
- }
-
- private function checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters)
- {
- $searchKeywordParameters = trim($searchKeywordParameters);
- $searchCategoryParameters = trim($searchCategoryParameters);
- if(empty($searchKeywordParameters)) {
- $searchKeywordParameters = '';
- }
- if(empty($searchCategoryParameters)) {
- $searchCategoryParameters = '';
- }
-
- return array($searchKeywordParameters, $searchCategoryParameters);
- }
-
- /**
- * Check that the array of URLs are valid URLs
- *
- * @param array $urls
- * @throws Exception if any of the urls is not valid
- */
- private function checkUrls($urls)
- {
- foreach($urls as $url)
- {
- if(!$this->isValidUrl($url))
- {
- throw new Exception(sprintf(Piwik_TranslateException("SitesManager_ExceptionInvalidUrl"),$url));
- }
- }
- }
-
- /**
- * Clean the parameter URLs:
- * - if the parameter is a string make it an array
- * - remove the trailing slashes if found
- *
- * @param string|array urls
- * @return array the array of cleaned URLs
- */
- private function cleanParameterUrls( $urls )
- {
- if(!is_array($urls))
- {
- $urls = array($urls);
- }
- $urls = array_filter($urls);
-
- $urls = array_map('urldecode', $urls);
- foreach($urls as &$url)
- {
- $url = $this->removeTrailingSlash($url);
- if(strpos($url, 'http') !== 0)
- {
- $url = 'http://'.$url;
- }
- $url = Piwik_Common::sanitizeInputValue($url);
- }
- $urls = array_unique($urls);
- return $urls;
- }
-
- public function getPatternMatchSites($pattern)
- {
- $ids = $this->getSitesIdWithAtLeastViewAccess();
- if(empty($ids))
- {
- return array();
- }
-
- $ids_str = '';
- foreach($ids as $id_num => $id_val)
- {
- $ids_str .= $id_val.' , ';
- }
- $ids_str .= $id_val;
-
- $db = Zend_Registry::get('db');
- $bind = array('%'.$pattern.'%', 'http%'.$pattern.'%');
-
- // Also match the idsite
- $where = '';
- if(is_numeric($pattern))
- {
- $bind[] = $pattern;
- $where = 'OR s.idsite = ?';
- }
- $sites = $db->fetchAll("SELECT idsite, name, main_url
- FROM ".Piwik_Common::prefixTable('site')." s
+ }
+
+ /**
+ * Remove the final slash in the URLs if found
+ *
+ * @return string the URL without the trailing slash
+ */
+ private function removeTrailingSlash($url)
+ {
+ // if there is a final slash, we take the URL without this slash (expected URL format)
+ if (strlen($url) > 5
+ && $url[strlen($url) - 1] == '/'
+ ) {
+ $url = substr($url, 0, strlen($url) - 1);
+ }
+ return $url;
+ }
+
+ /**
+ * Tests if the URL is a valid URL
+ *
+ * @return bool
+ */
+ private function isValidUrl($url)
+ {
+ return Piwik_Common::isLookLikeUrl($url);
+ }
+
+ /**
+ * Tests if the IP is a valid IP, allowing wildcards, except in the first octet.
+ * Wildcards can only be used from right to left, ie. 1.1.*.* is allowed, but 1.1.*.1 is not.
+ *
+ * @param string $ip IP address
+ * @return bool
+ */
+ private function isValidIp($ip)
+ {
+ return Piwik_IP::getIpsForRange($ip) !== false;
+ }
+
+ /**
+ * Check that the website name has a correct format.
+ *
+ * @param $siteName
+ * @throws Exception
+ */
+ private function checkName($siteName)
+ {
+ if (empty($siteName)) {
+ throw new Exception(Piwik_TranslateException("SitesManager_ExceptionEmptyName"));
+ }
+ }
+
+
+ private function checkSiteSearch($siteSearch)
+ {
+ if ($siteSearch === null) {
+ return "1";
+ }
+ return $siteSearch == 1 ? "1" : "0";
+ }
+
+ private function checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters)
+ {
+ $searchKeywordParameters = trim($searchKeywordParameters);
+ $searchCategoryParameters = trim($searchCategoryParameters);
+ if (empty($searchKeywordParameters)) {
+ $searchKeywordParameters = '';
+ }
+ if (empty($searchCategoryParameters)) {
+ $searchCategoryParameters = '';
+ }
+
+ return array($searchKeywordParameters, $searchCategoryParameters);
+ }
+
+ /**
+ * Check that the array of URLs are valid URLs
+ *
+ * @param array $urls
+ * @throws Exception if any of the urls is not valid
+ */
+ private function checkUrls($urls)
+ {
+ foreach ($urls as $url) {
+ if (!$this->isValidUrl($url)) {
+ throw new Exception(sprintf(Piwik_TranslateException("SitesManager_ExceptionInvalidUrl"), $url));
+ }
+ }
+ }
+
+ /**
+ * Clean the parameter URLs:
+ * - if the parameter is a string make it an array
+ * - remove the trailing slashes if found
+ *
+ * @param string|array urls
+ * @return array the array of cleaned URLs
+ */
+ private function cleanParameterUrls($urls)
+ {
+ if (!is_array($urls)) {
+ $urls = array($urls);
+ }
+ $urls = array_filter($urls);
+
+ $urls = array_map('urldecode', $urls);
+ foreach ($urls as &$url) {
+ $url = $this->removeTrailingSlash($url);
+ if (strpos($url, 'http') !== 0) {
+ $url = 'http://' . $url;
+ }
+ $url = Piwik_Common::sanitizeInputValue($url);
+ }
+ $urls = array_unique($urls);
+ return $urls;
+ }
+
+ public function getPatternMatchSites($pattern)
+ {
+ $ids = $this->getSitesIdWithAtLeastViewAccess();
+ if (empty($ids)) {
+ return array();
+ }
+
+ $ids_str = '';
+ foreach ($ids as $id_num => $id_val) {
+ $ids_str .= $id_val . ' , ';
+ }
+ $ids_str .= $id_val;
+
+ $db = Zend_Registry::get('db');
+ $bind = array('%' . $pattern . '%', 'http%' . $pattern . '%');
+
+ // Also match the idsite
+ $where = '';
+ if (is_numeric($pattern)) {
+ $bind[] = $pattern;
+ $where = 'OR s.idsite = ?';
+ }
+ $sites = $db->fetchAll("SELECT idsite, name, main_url
+ FROM " . Piwik_Common::prefixTable('site') . " s
WHERE ( s.name like ?
OR s.main_url like ?
$where )
AND idsite in ($ids_str)
- LIMIT ".Piwik::getWebsitesCountToDisplay(),
- $bind) ;
- return $sites;
- }
-
- /**
- * Utility function that throws if a value is not valid for the 'keep_url_fragment'
- * column of the piwik_site table.
- *
- * @param int $keepURLFragments
- * @throws Exception
- */
- private static function checkKeepURLFragmentsValue( $keepURLFragments )
- {
- // make sure value is between 0 & 2
- if (!in_array($keepURLFragments, array(0,1,2)))
- {
- throw new Exception("Error in SitesManager.updateSite: keepURLFragments must be between 0 & 2" +
- " (actual value: $keepURLFragments).");
- }
- }
+ LIMIT " . Piwik::getWebsitesCountToDisplay(),
+ $bind);
+ return $sites;
+ }
+
+ /**
+ * Utility function that throws if a value is not valid for the 'keep_url_fragment'
+ * column of the piwik_site table.
+ *
+ * @param int $keepURLFragments
+ * @throws Exception
+ */
+ private static function checkKeepURLFragmentsValue($keepURLFragments)
+ {
+ // make sure value is between 0 & 2
+ if (!in_array($keepURLFragments, array(0, 1, 2))) {
+ throw new Exception("Error in SitesManager.updateSite: keepURLFragments must be between 0 & 2" +
+ " (actual value: $keepURLFragments).");
+ }
+ }
}
diff --git a/plugins/SitesManager/Controller.php b/plugins/SitesManager/Controller.php
index 6b230e8193..c99ed03705 100644
--- a/plugins/SitesManager/Controller.php
+++ b/plugins/SitesManager/Controller.php
@@ -15,189 +15,178 @@
*/
class Piwik_SitesManager_Controller extends Piwik_Controller_Admin
{
- /*
- * Main view showing listing of websites and settings
- */
- function index()
- {
- $view = Piwik_View::factory('SitesManager');
-
- if (Piwik::isUserIsSuperUser())
- {
- $sites = Piwik_SitesManager_API::getInstance()->getAllSites();
- Piwik_Site::setSites($sites);
- $sites = array_values($sites);
- }
- else
- {
- $sites = Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess();
- Piwik_Site::setSitesFromArray($sites);
- }
-
- foreach($sites as &$site)
- {
- $site['alias_urls'] = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($site['idsite']);
- $site['excluded_ips'] = str_replace(',','<br/>', $site['excluded_ips']);
- $site['excluded_parameters'] = str_replace(',','<br/>', $site['excluded_parameters']);
- $site['excluded_user_agents'] = str_replace(',', '<br/>', $site['excluded_user_agents']);
- }
- $view->adminSites = $sites;
- $view->adminSitesCount = count($sites);
-
- $timezones = Piwik_SitesManager_API::getInstance()->getTimezonesList();
- $view->timezoneSupported = Piwik::isTimezoneSupportEnabled();
- $view->timezones = Piwik_Common::json_encode($timezones);
- $view->defaultTimezone = Piwik_SitesManager_API::getInstance()->getDefaultTimezone();
-
- $view->currencies = Piwik_Common::json_encode(Piwik_SitesManager_API::getInstance()->getCurrencyList());
- $view->defaultCurrency = Piwik_SitesManager_API::getInstance()->getDefaultCurrency();
-
- $view->utcTime = Piwik_Date::now()->getDatetime();
- $excludedIpsGlobal = Piwik_SitesManager_API::getInstance()->getExcludedIpsGlobal();
- $view->globalExcludedIps = str_replace(',',"\n", $excludedIpsGlobal);
- $excludedQueryParametersGlobal = Piwik_SitesManager_API::getInstance()->getExcludedQueryParametersGlobal();
- $view->globalExcludedQueryParameters = str_replace(',',"\n", $excludedQueryParametersGlobal);
-
- $globalExcludedUserAgents = Piwik_SitesManager_API::getInstance()->getExcludedUserAgentsGlobal();
- $view->globalExcludedUserAgents = str_replace(',', "\n", $globalExcludedUserAgents);
-
- $view->globalSearchKeywordParameters = Piwik_SitesManager_API::getInstance()->getSearchKeywordParametersGlobal();
- $view->globalSearchCategoryParameters = Piwik_SitesManager_API::getInstance()->getSearchCategoryParametersGlobal();
- $view->isSearchCategoryTrackingEnabled = Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables');
- $view->allowSiteSpecificUserAgentExclude =
- Piwik_SitesManager_API::getInstance()->isSiteSpecificUserAgentExcludeEnabled();
-
- $view->globalKeepURLFragments = Piwik_SitesManager_API::getInstance()->getKeepURLFragmentsGlobal();
-
- $view->currentIpAddress = Piwik_IP::getIpFromHeader();
-
- $view->showAddSite = (boolean) Piwik_Common::getRequestVar('showaddsite', false);
-
- $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
- echo $view->render();
- }
-
- /*
- * Records Global settings when user submit changes
- */
- function setGlobalSettings()
- {
- $response = new Piwik_API_ResponseBuilder(Piwik_Common::getRequestVar('format'));
-
- try {
- $this->checkTokenInUrl();
- $timezone = Piwik_Common::getRequestVar('timezone', false);
- $excludedIps = Piwik_Common::getRequestVar('excludedIps', false);
- $excludedQueryParameters = Piwik_Common::getRequestVar('excludedQueryParameters', false);
- $excludedUserAgents = Piwik_Common::getRequestVar('excludedUserAgents', false);
- $currency = Piwik_Common::getRequestVar('currency', false);
- $searchKeywordParameters = Piwik_Common::getRequestVar('searchKeywordParameters', $default = "");
- $searchCategoryParameters = Piwik_Common::getRequestVar('searchCategoryParameters', $default = "");
- $enableSiteUserAgentExclude = Piwik_Common::getRequestVar('enableSiteUserAgentExclude', $default = 0);
- $keepURLFragments = Piwik_Common::getRequestVar('keepURLFragments', $default = 0);
-
- $api = Piwik_SitesManager_API::getInstance();
- $api->setDefaultTimezone($timezone);
- $api->setDefaultCurrency($currency);
- $api->setGlobalExcludedQueryParameters($excludedQueryParameters);
- $api->setGlobalExcludedIps($excludedIps);
- $api->setGlobalExcludedUserAgents($excludedUserAgents);
- $api->setGlobalSearchParameters($searchKeywordParameters, $searchCategoryParameters);
- $api->setSiteSpecificUserAgentExcludeEnabled($enableSiteUserAgentExclude == 1);
- $api->setKeepURLFragmentsGlobal($keepURLFragments);
-
- $toReturn = $response->getResponse();
- } catch(Exception $e ) {
- $toReturn = $response->getResponseException( $e );
- }
- echo $toReturn;
- }
-
- /**
- * Displays the admin UI page showing all tracking tags
- * @return unknown_type
- */
- function displayJavascriptCode()
- {
- $idSite = Piwik_Common::getRequestVar('idSite');
- Piwik::checkUserHasViewAccess($idSite);
- $jsTag = Piwik::getJavascriptCode($idSite, Piwik_Url::getCurrentUrlWithoutFileName());
- $view = Piwik_View::factory('Tracking');
- $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
- $view->idSite = $idSite;
- $site = new Piwik_Site($idSite);
- $view->displaySiteName = $site->getName();
- $view->jsTag = $jsTag;
- echo $view->render();
- }
-
- /*
- * User will download a file called PiwikTracker.php that is the content of the actual script
- */
- function downloadPiwikTracker()
- {
- $path = PIWIK_INCLUDE_PATH . '/libs/PiwikTracker/';
- $filename = 'PiwikTracker.php';
- header('Content-type: text/php');
- header('Content-Disposition: attachment; filename="'.$filename.'"');
- echo file_get_contents( $path . $filename);
- }
-
- /**
- * Used to generate the doc at http://piwik.org/docs/tracking-api/
- */
- function displayAlternativeTagsHelp()
- {
- $view = Piwik_View::factory('DisplayAlternativeTags');
- $view->idSite = Piwik_Common::getRequestVar('idSite');
- $url = Piwik_Common::getRequestVar('piwikUrl', '', 'string');
- if(empty($url)
- || !Piwik_Common::isLookLikeUrl($url) )
- {
- $url = $view->piwikUrl;
- }
- $view->piwikUrlRequest = $url;
- $view->calledExternally = true;
- echo $view->render();
- }
-
- function getSitesForAutocompleter()
- {
- $pattern = Piwik_Common::getRequestVar('term');
- $sites = Piwik_SitesManager_API::getInstance()->getPatternMatchSites($pattern);
- $pattern = str_replace('%', '', $pattern);
- if(!count($sites))
- {
- $results[] = array('label' => Piwik_Translate('SitesManager_NotFound')."&nbsp;<span class='autocompleteMatched'>$pattern</span>.", 'id' => '#');
- }
- else
- {
- if(strpos($pattern, '/') !== false
- && strpos($pattern, '\\/') === false)
- {
- $pattern = str_replace('/', '\\/', $pattern);
- }
- foreach($sites as $s)
- {
- $hl_name = $s['name'];
- if(strlen($pattern) > 0)
- {
- @preg_match_all("/$pattern+/i", $hl_name, $matches);
- if (is_array($matches[0]) && count($matches[0]) >= 1)
- {
- foreach ($matches[0] as $match)
- {
- $hl_name = str_replace($match, '<span class="autocompleteMatched">'.$match.'</span>', $s['name']);
- }
- }
- }
- $results[] = array('label' => $hl_name, 'id' => $s['idsite'], 'name' => $s['name'] );
- }
- }
-
- Piwik_DataTable_Renderer_Json::sendHeaderJSON();
- print Piwik_Common::json_encode($results);
- }
+ /*
+ * Main view showing listing of websites and settings
+ */
+ function index()
+ {
+ $view = Piwik_View::factory('SitesManager');
+
+ if (Piwik::isUserIsSuperUser()) {
+ $sites = Piwik_SitesManager_API::getInstance()->getAllSites();
+ Piwik_Site::setSites($sites);
+ $sites = array_values($sites);
+ } else {
+ $sites = Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess();
+ Piwik_Site::setSitesFromArray($sites);
+ }
+
+ foreach ($sites as &$site) {
+ $site['alias_urls'] = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($site['idsite']);
+ $site['excluded_ips'] = str_replace(',', '<br/>', $site['excluded_ips']);
+ $site['excluded_parameters'] = str_replace(',', '<br/>', $site['excluded_parameters']);
+ $site['excluded_user_agents'] = str_replace(',', '<br/>', $site['excluded_user_agents']);
+ }
+ $view->adminSites = $sites;
+ $view->adminSitesCount = count($sites);
+
+ $timezones = Piwik_SitesManager_API::getInstance()->getTimezonesList();
+ $view->timezoneSupported = Piwik::isTimezoneSupportEnabled();
+ $view->timezones = Piwik_Common::json_encode($timezones);
+ $view->defaultTimezone = Piwik_SitesManager_API::getInstance()->getDefaultTimezone();
+
+ $view->currencies = Piwik_Common::json_encode(Piwik_SitesManager_API::getInstance()->getCurrencyList());
+ $view->defaultCurrency = Piwik_SitesManager_API::getInstance()->getDefaultCurrency();
+
+ $view->utcTime = Piwik_Date::now()->getDatetime();
+ $excludedIpsGlobal = Piwik_SitesManager_API::getInstance()->getExcludedIpsGlobal();
+ $view->globalExcludedIps = str_replace(',', "\n", $excludedIpsGlobal);
+ $excludedQueryParametersGlobal = Piwik_SitesManager_API::getInstance()->getExcludedQueryParametersGlobal();
+ $view->globalExcludedQueryParameters = str_replace(',', "\n", $excludedQueryParametersGlobal);
+
+ $globalExcludedUserAgents = Piwik_SitesManager_API::getInstance()->getExcludedUserAgentsGlobal();
+ $view->globalExcludedUserAgents = str_replace(',', "\n", $globalExcludedUserAgents);
+
+ $view->globalSearchKeywordParameters = Piwik_SitesManager_API::getInstance()->getSearchKeywordParametersGlobal();
+ $view->globalSearchCategoryParameters = Piwik_SitesManager_API::getInstance()->getSearchCategoryParametersGlobal();
+ $view->isSearchCategoryTrackingEnabled = Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables');
+ $view->allowSiteSpecificUserAgentExclude =
+ Piwik_SitesManager_API::getInstance()->isSiteSpecificUserAgentExcludeEnabled();
+
+ $view->globalKeepURLFragments = Piwik_SitesManager_API::getInstance()->getKeepURLFragmentsGlobal();
+
+ $view->currentIpAddress = Piwik_IP::getIpFromHeader();
+
+ $view->showAddSite = (boolean)Piwik_Common::getRequestVar('showaddsite', false);
+
+ $this->setBasicVariablesView($view);
+ $view->menu = Piwik_GetAdminMenu();
+ echo $view->render();
+ }
+
+ /*
+ * Records Global settings when user submit changes
+ */
+ function setGlobalSettings()
+ {
+ $response = new Piwik_API_ResponseBuilder(Piwik_Common::getRequestVar('format'));
+
+ try {
+ $this->checkTokenInUrl();
+ $timezone = Piwik_Common::getRequestVar('timezone', false);
+ $excludedIps = Piwik_Common::getRequestVar('excludedIps', false);
+ $excludedQueryParameters = Piwik_Common::getRequestVar('excludedQueryParameters', false);
+ $excludedUserAgents = Piwik_Common::getRequestVar('excludedUserAgents', false);
+ $currency = Piwik_Common::getRequestVar('currency', false);
+ $searchKeywordParameters = Piwik_Common::getRequestVar('searchKeywordParameters', $default = "");
+ $searchCategoryParameters = Piwik_Common::getRequestVar('searchCategoryParameters', $default = "");
+ $enableSiteUserAgentExclude = Piwik_Common::getRequestVar('enableSiteUserAgentExclude', $default = 0);
+ $keepURLFragments = Piwik_Common::getRequestVar('keepURLFragments', $default = 0);
+
+ $api = Piwik_SitesManager_API::getInstance();
+ $api->setDefaultTimezone($timezone);
+ $api->setDefaultCurrency($currency);
+ $api->setGlobalExcludedQueryParameters($excludedQueryParameters);
+ $api->setGlobalExcludedIps($excludedIps);
+ $api->setGlobalExcludedUserAgents($excludedUserAgents);
+ $api->setGlobalSearchParameters($searchKeywordParameters, $searchCategoryParameters);
+ $api->setSiteSpecificUserAgentExcludeEnabled($enableSiteUserAgentExclude == 1);
+ $api->setKeepURLFragmentsGlobal($keepURLFragments);
+
+ $toReturn = $response->getResponse();
+ } catch (Exception $e) {
+ $toReturn = $response->getResponseException($e);
+ }
+ echo $toReturn;
+ }
+
+ /**
+ * Displays the admin UI page showing all tracking tags
+ * @return unknown_type
+ */
+ function displayJavascriptCode()
+ {
+ $idSite = Piwik_Common::getRequestVar('idSite');
+ Piwik::checkUserHasViewAccess($idSite);
+ $jsTag = Piwik::getJavascriptCode($idSite, Piwik_Url::getCurrentUrlWithoutFileName());
+ $view = Piwik_View::factory('Tracking');
+ $this->setBasicVariablesView($view);
+ $view->menu = Piwik_GetAdminMenu();
+ $view->idSite = $idSite;
+ $site = new Piwik_Site($idSite);
+ $view->displaySiteName = $site->getName();
+ $view->jsTag = $jsTag;
+ echo $view->render();
+ }
+
+ /*
+ * User will download a file called PiwikTracker.php that is the content of the actual script
+ */
+ function downloadPiwikTracker()
+ {
+ $path = PIWIK_INCLUDE_PATH . '/libs/PiwikTracker/';
+ $filename = 'PiwikTracker.php';
+ header('Content-type: text/php');
+ header('Content-Disposition: attachment; filename="' . $filename . '"');
+ echo file_get_contents($path . $filename);
+ }
+
+ /**
+ * Used to generate the doc at http://piwik.org/docs/tracking-api/
+ */
+ function displayAlternativeTagsHelp()
+ {
+ $view = Piwik_View::factory('DisplayAlternativeTags');
+ $view->idSite = Piwik_Common::getRequestVar('idSite');
+ $url = Piwik_Common::getRequestVar('piwikUrl', '', 'string');
+ if (empty($url)
+ || !Piwik_Common::isLookLikeUrl($url)
+ ) {
+ $url = $view->piwikUrl;
+ }
+ $view->piwikUrlRequest = $url;
+ $view->calledExternally = true;
+ echo $view->render();
+ }
+
+ function getSitesForAutocompleter()
+ {
+ $pattern = Piwik_Common::getRequestVar('term');
+ $sites = Piwik_SitesManager_API::getInstance()->getPatternMatchSites($pattern);
+ $pattern = str_replace('%', '', $pattern);
+ if (!count($sites)) {
+ $results[] = array('label' => Piwik_Translate('SitesManager_NotFound') . "&nbsp;<span class='autocompleteMatched'>$pattern</span>.", 'id' => '#');
+ } else {
+ if (strpos($pattern, '/') !== false
+ && strpos($pattern, '\\/') === false
+ ) {
+ $pattern = str_replace('/', '\\/', $pattern);
+ }
+ foreach ($sites as $s) {
+ $hl_name = $s['name'];
+ if (strlen($pattern) > 0) {
+ @preg_match_all("/$pattern+/i", $hl_name, $matches);
+ if (is_array($matches[0]) && count($matches[0]) >= 1) {
+ foreach ($matches[0] as $match) {
+ $hl_name = str_replace($match, '<span class="autocompleteMatched">' . $match . '</span>', $s['name']);
+ }
+ }
+ }
+ $results[] = array('label' => $hl_name, 'id' => $s['idsite'], 'name' => $s['name']);
+ }
+ }
+
+ Piwik_DataTable_Renderer_Json::sendHeaderJSON();
+ print Piwik_Common::json_encode($results);
+ }
}
diff --git a/plugins/SitesManager/SitesManager.php b/plugins/SitesManager/SitesManager.php
index 6f596f9f87..b258a29b8f 100644
--- a/plugins/SitesManager/SitesManager.php
+++ b/plugins/SitesManager/SitesManager.php
@@ -15,212 +15,204 @@
*/
class Piwik_SitesManager extends Piwik_Plugin
{
- const KEEP_URL_FRAGMENT_USE_DEFAULT = 0;
- const KEEP_URL_FRAGMENT_YES = 1;
- const KEEP_URL_FRAGMENT_NO = 2;
-
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('SitesManager_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- return $info;
- }
-
- function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'AdminMenu.add' => 'addMenu',
- 'Common.fetchWebsiteAttributes' => 'recordWebsiteDataInCache',
- );
- }
-
- function addMenu()
- {
- Piwik_AddAdminSubMenu('CoreAdminHome_MenuManage', 'SitesManager_MenuSites',
- array('module' => 'SitesManager', 'action' => 'index'),
- Piwik::isUserHasSomeAdminAccess(),
- $order = 1);
- }
-
- /**
- * Get CSS files
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "themes/default/styles.css";
- }
-
- /**
- * Get JavaScript files
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
-
- $jsFiles[] = "plugins/SitesManager/templates/SitesManager.js";
- }
-
- /**
- * Hooks when a website tracker cache is flushed (website updated, cache deleted, or empty cache)
- * Will record in the tracker config file all data needed for this website in Tracker.
- *
- * @param Piwik_Event_Notification $notification notification object
- * @return void
- */
- function recordWebsiteDataInCache($notification)
- {
- $idSite = (int)$notification->getNotificationInfo();
- // add the 'hosts' entry in the website array
- $array =& $notification->getNotificationObject();
- $array['hosts'] = $this->getTrackerHosts($idSite);
-
- $website = Piwik_SitesManager_API::getInstance()->getSiteFromId($idSite);
- $array['excluded_ips'] = $this->getTrackerExcludedIps($website);
- $array['excluded_parameters'] = self::getTrackerExcludedQueryParameters($website);
- $array['excluded_user_agents'] = self::getExcludedUserAgents($website);
- $array['keep_url_fragment'] = self::shouldKeepURLFragmentsFor($website);
- $array['sitesearch'] = $website['sitesearch'];
- $array['sitesearch_keyword_parameters'] = $this->getTrackerSearchKeywordParameters($website);
- $array['sitesearch_category_parameters'] = $this->getTrackerSearchCategoryParameters($website);
- }
-
- /**
- * Returns whether we should keep URL fragments for a specific site.
- *
- * @param array $site DB data for the site.
- * @return bool
- */
- private static function shouldKeepURLFragmentsFor( $site )
- {
- if ($site['keep_url_fragment'] == self::KEEP_URL_FRAGMENT_YES)
- {
- return true;
- }
- else if ($site['keep_url_fragment'] == self::KEEP_URL_FRAGMENT_NO)
- {
- return false;
- }
-
- return Piwik_SitesManager_API::getInstance()->getKeepURLFragmentsGlobal();
- }
-
- private function getTrackerSearchKeywordParameters($website)
- {
- $searchParameters = $website['sitesearch_keyword_parameters'];
- if(empty($searchParameters)) {
- $searchParameters = Piwik_SitesManager_API::getInstance()->getSearchKeywordParametersGlobal();
- }
- return explode(",", $searchParameters);
- }
-
- private function getTrackerSearchCategoryParameters($website)
- {
- $searchParameters = $website['sitesearch_category_parameters'];
- if(empty($searchParameters)) {
- $searchParameters = Piwik_SitesManager_API::getInstance()->getSearchCategoryParametersGlobal();
- }
- return explode(",", $searchParameters);
- }
-
- /**
- * Returns the array of excluded IPs to save in the config file
- *
- * @return array
- */
- private function getTrackerExcludedIps($website)
- {
- $excludedIps = $website['excluded_ips'];
- $globalExcludedIps = Piwik_SitesManager_API::getInstance()->getExcludedIpsGlobal();
-
- $excludedIps .= ',' . $globalExcludedIps;
-
- $ipRanges = array();
- foreach(explode(',', $excludedIps) as $ip)
- {
- $ipRange = Piwik_SitesManager_API::getInstance()->getIpsForRange($ip);
- if($ipRange !== false)
- {
- $ipRanges[] = $ipRange;
- }
- }
- return $ipRanges;
- }
-
- /**
- * Returns the array of excluded user agent substrings for a site. Filters out
- * any garbage data & trims each entry.
- *
- * @param array $website The full set of information for a site.
- * @return array
- */
- private static function getExcludedUserAgents( $website )
- {
- $excludedUserAgents = Piwik_SitesManager_API::getInstance()->getExcludedUserAgentsGlobal();
- if (Piwik_SitesManager_API::getInstance()->isSiteSpecificUserAgentExcludeEnabled())
- {
- $excludedUserAgents .= ','.$website['excluded_user_agents'];
- }
- return self::filterBlankFromCommaSepList($excludedUserAgents);
- }
-
- /**
- * Returns the array of URL query parameters to exclude from URLs
- *
- * @return array
- */
- public static function getTrackerExcludedQueryParameters($website)
- {
- $excludedQueryParameters = $website['excluded_parameters'];
- $globalExcludedQueryParameters = Piwik_SitesManager_API::getInstance()->getExcludedQueryParametersGlobal();
-
- $excludedQueryParameters .= ',' . $globalExcludedQueryParameters;
- return self::filterBlankFromCommaSepList($excludedQueryParameters);
- }
-
- /**
- * Trims each element of a comma-separated list of strings, removes empty elements and
- * returns the result (as an array).
- *
- * @param string $parameters The unfiltered list.
- * @return array The filtered list of strings as an array.
- */
- static private function filterBlankFromCommaSepList( $parameters )
- {
- $parameters = explode(',', $parameters);
- $parameters = array_filter($parameters, 'strlen');
- $parameters = array_unique($parameters);
- return $parameters;
- }
-
- /**
- * Returns the hosts alias URLs
- * @param int $idSite
- * @return array
- */
- private function getTrackerHosts($idSite)
- {
- $urls = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idSite);
- $hosts = array();
- foreach($urls as $url)
- {
- $url = parse_url($url);
- if(isset($url['host']))
- {
- $hosts[] = $url['host'];
- }
- }
- return $hosts;
- }
+ const KEEP_URL_FRAGMENT_USE_DEFAULT = 0;
+ const KEEP_URL_FRAGMENT_YES = 1;
+ const KEEP_URL_FRAGMENT_NO = 2;
+
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('SitesManager_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'AdminMenu.add' => 'addMenu',
+ 'Common.fetchWebsiteAttributes' => 'recordWebsiteDataInCache',
+ );
+ }
+
+ function addMenu()
+ {
+ Piwik_AddAdminSubMenu('CoreAdminHome_MenuManage', 'SitesManager_MenuSites',
+ array('module' => 'SitesManager', 'action' => 'index'),
+ Piwik::isUserHasSomeAdminAccess(),
+ $order = 1);
+ }
+
+ /**
+ * Get CSS files
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+
+ $cssFiles[] = "themes/default/styles.css";
+ }
+
+ /**
+ * Get JavaScript files
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+
+ $jsFiles[] = "plugins/SitesManager/templates/SitesManager.js";
+ }
+
+ /**
+ * Hooks when a website tracker cache is flushed (website updated, cache deleted, or empty cache)
+ * Will record in the tracker config file all data needed for this website in Tracker.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ * @return void
+ */
+ function recordWebsiteDataInCache($notification)
+ {
+ $idSite = (int)$notification->getNotificationInfo();
+ // add the 'hosts' entry in the website array
+ $array =& $notification->getNotificationObject();
+ $array['hosts'] = $this->getTrackerHosts($idSite);
+
+ $website = Piwik_SitesManager_API::getInstance()->getSiteFromId($idSite);
+ $array['excluded_ips'] = $this->getTrackerExcludedIps($website);
+ $array['excluded_parameters'] = self::getTrackerExcludedQueryParameters($website);
+ $array['excluded_user_agents'] = self::getExcludedUserAgents($website);
+ $array['keep_url_fragment'] = self::shouldKeepURLFragmentsFor($website);
+ $array['sitesearch'] = $website['sitesearch'];
+ $array['sitesearch_keyword_parameters'] = $this->getTrackerSearchKeywordParameters($website);
+ $array['sitesearch_category_parameters'] = $this->getTrackerSearchCategoryParameters($website);
+ }
+
+ /**
+ * Returns whether we should keep URL fragments for a specific site.
+ *
+ * @param array $site DB data for the site.
+ * @return bool
+ */
+ private static function shouldKeepURLFragmentsFor($site)
+ {
+ if ($site['keep_url_fragment'] == self::KEEP_URL_FRAGMENT_YES) {
+ return true;
+ } else if ($site['keep_url_fragment'] == self::KEEP_URL_FRAGMENT_NO) {
+ return false;
+ }
+
+ return Piwik_SitesManager_API::getInstance()->getKeepURLFragmentsGlobal();
+ }
+
+ private function getTrackerSearchKeywordParameters($website)
+ {
+ $searchParameters = $website['sitesearch_keyword_parameters'];
+ if (empty($searchParameters)) {
+ $searchParameters = Piwik_SitesManager_API::getInstance()->getSearchKeywordParametersGlobal();
+ }
+ return explode(",", $searchParameters);
+ }
+
+ private function getTrackerSearchCategoryParameters($website)
+ {
+ $searchParameters = $website['sitesearch_category_parameters'];
+ if (empty($searchParameters)) {
+ $searchParameters = Piwik_SitesManager_API::getInstance()->getSearchCategoryParametersGlobal();
+ }
+ return explode(",", $searchParameters);
+ }
+
+ /**
+ * Returns the array of excluded IPs to save in the config file
+ *
+ * @return array
+ */
+ private function getTrackerExcludedIps($website)
+ {
+ $excludedIps = $website['excluded_ips'];
+ $globalExcludedIps = Piwik_SitesManager_API::getInstance()->getExcludedIpsGlobal();
+
+ $excludedIps .= ',' . $globalExcludedIps;
+
+ $ipRanges = array();
+ foreach (explode(',', $excludedIps) as $ip) {
+ $ipRange = Piwik_SitesManager_API::getInstance()->getIpsForRange($ip);
+ if ($ipRange !== false) {
+ $ipRanges[] = $ipRange;
+ }
+ }
+ return $ipRanges;
+ }
+
+ /**
+ * Returns the array of excluded user agent substrings for a site. Filters out
+ * any garbage data & trims each entry.
+ *
+ * @param array $website The full set of information for a site.
+ * @return array
+ */
+ private static function getExcludedUserAgents($website)
+ {
+ $excludedUserAgents = Piwik_SitesManager_API::getInstance()->getExcludedUserAgentsGlobal();
+ if (Piwik_SitesManager_API::getInstance()->isSiteSpecificUserAgentExcludeEnabled()) {
+ $excludedUserAgents .= ',' . $website['excluded_user_agents'];
+ }
+ return self::filterBlankFromCommaSepList($excludedUserAgents);
+ }
+
+ /**
+ * Returns the array of URL query parameters to exclude from URLs
+ *
+ * @return array
+ */
+ public static function getTrackerExcludedQueryParameters($website)
+ {
+ $excludedQueryParameters = $website['excluded_parameters'];
+ $globalExcludedQueryParameters = Piwik_SitesManager_API::getInstance()->getExcludedQueryParametersGlobal();
+
+ $excludedQueryParameters .= ',' . $globalExcludedQueryParameters;
+ return self::filterBlankFromCommaSepList($excludedQueryParameters);
+ }
+
+ /**
+ * Trims each element of a comma-separated list of strings, removes empty elements and
+ * returns the result (as an array).
+ *
+ * @param string $parameters The unfiltered list.
+ * @return array The filtered list of strings as an array.
+ */
+ static private function filterBlankFromCommaSepList($parameters)
+ {
+ $parameters = explode(',', $parameters);
+ $parameters = array_filter($parameters, 'strlen');
+ $parameters = array_unique($parameters);
+ return $parameters;
+ }
+
+ /**
+ * Returns the hosts alias URLs
+ * @param int $idSite
+ * @return array
+ */
+ private function getTrackerHosts($idSite)
+ {
+ $urls = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idSite);
+ $hosts = array();
+ foreach ($urls as $url) {
+ $url = parse_url($url);
+ if (isset($url['host'])) {
+ $hosts[] = $url['host'];
+ }
+ }
+ return $hosts;
+ }
}
diff --git a/plugins/SitesManager/templates/DisplayAlternativeTags.tpl b/plugins/SitesManager/templates/DisplayAlternativeTags.tpl
index 25328f351f..3dee0c15c4 100644
--- a/plugins/SitesManager/templates/DisplayAlternativeTags.tpl
+++ b/plugins/SitesManager/templates/DisplayAlternativeTags.tpl
@@ -1,101 +1,124 @@
<h3>Image Tracker code</h3>
-The Image Tracker code can be used when Javascript is not allowed.
-<br/><div class='toggleHelp' id='imageTracker' style='display:none'><a name='image'>› Display Image Tracker code </a></div>
+The Image Tracker code can be used when Javascript is not allowed.
+<br/>
+<div class='toggleHelp' id='imageTracker' style='display:none'><a name='image'>› Display Image Tracker code </a></div>
<div class='imageTracker'>
-<p>Some websites like MySpace or eBay will not allow users to add Javascript to their profile but accept HTML. In this case, you can still track visits with Piwik using the Image Tracker.
-<br/>
-<b>Note</b>: the code doesn't use Javascript so <b>Piwik will not be able to track some user information</b>
- such as search keywords, referrers, screen resolutions, browser plugins and page titles.
-</p>
-<code>
-&lt;!-- Piwik Image Tracker --&gt;<br/>
-&lt;img src="{if isset($piwikUrlRequest)}{$piwikUrlRequest}{else}{$piwikUrl}{/if}piwik.php?idsite={$idSite}&amp;amp;rec=1" style="border:0" alt="" /&gt;<br/>
-&lt;!-- End Piwik --&gt;<br/>
-</code>
-<br/>
-The following parameters can also be passed to the image URL:
-<ul>
- <li><i>rec</i> - (required) The parameter &rec=1 is required to force the request to be recorded</li>
- <li><i>idsite</i> - (required) Defines the Website ID being tracked</li>
- <li><i>action_name</i> - Defines the custom Page Title for this page view</li>
- <li><i>urlref</i> - The Referrer URL: must be set to the referrer URL used before landing on the page containing the Image tracker. For example, in PHP this value is accessible via <pre>$_SERVER['HTTP_REFERER']</pre></li>
- <li><i>idgoal</i> - The request will trigger the given Goal</li>
- <li><i>revenue</i> - Used with idgoal, defines the custom revenue for this conversion</li>
- <li><i>and more!</i> - There are many more parameters you can set beyond the main ones above. See the <a href='http://piwik.org/docs/tracking-api/reference/'>Tracking API documentation page</a>.</li>
-</ul>
+ <p>Some websites like MySpace or eBay will not allow users to add Javascript to their profile but accept HTML. In this case, you can still track visits with
+ Piwik using the Image Tracker.
+ <br/>
+ <b>Note</b>: the code doesn't use Javascript so <b>Piwik will not be able to track some user information</b>
+ such as search keywords, referrers, screen resolutions, browser plugins and page titles.
+ </p>
+ <code>
+ &lt;!-- Piwik Image Tracker --&gt;<br/>
+ &lt;img src="{if isset($piwikUrlRequest)}{$piwikUrlRequest}{else}{$piwikUrl}{/if}piwik.php?idsite={$idSite}&amp;amp;rec=1" style="border:0" alt="" /&gt;<br/>
+ &lt;!-- End Piwik --&gt;<br/>
+ </code>
+ <br/>
+ The following parameters can also be passed to the image URL:
+ <ul>
+ <li><i>rec</i> - (required) The parameter &rec=1 is required to force the request to be recorded</li>
+ <li><i>idsite</i> - (required) Defines the Website ID being tracked</li>
+ <li><i>action_name</i> - Defines the custom Page Title for this page view</li>
+ <li><i>urlref</i> - The Referrer URL: must be set to the referrer URL used before landing on the page containing the Image tracker. For example, in PHP
+ this value is accessible via
+ <pre>$_SERVER['HTTP_REFERER']</pre>
+ </li>
+ <li><i>idgoal</i> - The request will trigger the given Goal</li>
+ <li><i>revenue</i> - Used with idgoal, defines the custom revenue for this conversion</li>
+ <li><i>and more!</i> - There are many more parameters you can set beyond the main ones above. See the <a
+ href='http://piwik.org/docs/tracking-api/reference/'>Tracking API documentation page</a>.
+ </li>
+ </ul>
</div>
<h3>Piwik Tracking API (Advanced users)</h3>
-It is also possible to call the Piwik Tracking API using your favorite programming language.
-<br/><div class='toggleHelp' id='trackingAPI' style='display:none'><a name='image'>› Display Piwik Tracking API documentation </a></div>
+It is also possible to call the Piwik Tracking API using your favorite programming language.
+<br/>
+<div class='toggleHelp' id='trackingAPI' style='display:none'><a name='image'>› Display Piwik Tracking API documentation </a></div>
<div class='trackingAPI'>
-<p>
-The Piwik Tracking API allows to trigger visits (page views and Goal conversions) from any environment (Desktop App, iPhone or Android app, Mobile website, etc.).
-</p>
+ <p>
+ The Piwik Tracking API allows to trigger visits (page views and Goal conversions) from any environment (Desktop App, iPhone or Android app, Mobile
+ website, etc.).
+ </p>
-<p>We currently provide a <b>PHP client</b> to call the API from your PHP projects.
-If you would like to contribute a version of the client in another programming language (Python, Java, Ruby, Perl, etc.) please <a target='_blank' href='http://dev.piwik.org/'>create a ticket</a> in our developer area (please attach the client code to the ticket).
-</p><p>Follow these instructions to get started with the Tracking API:
-<ul style='list-style-type:decimal;'>
-<li><a href='{if isset($piwikUrlRequest)}{$piwikUrlRequest}{else}{$piwikUrl}{/if}{url action=downloadPiwikTracker}' target='_blank'>Click here to download the file PiwikTracker.php</a>
-</li><li>Upload the PiwikTracker.php file in the same path as your project files
-</li><li>Copy the following code, then paste it onto every page you want to track.
-<code>
-&lt;?php <br/>
-// -- Piwik Tracking API init -- <br/>
-require_once "/path/to/PiwikTracker.php";<br/>
-PiwikTracker::$URL = '{if isset($piwikUrlRequest)}{$piwikUrlRequest}{else}{$piwikUrl}{/if}';<br/>
- ?&gt;
-</code>
-</li><li>Choose a Tracking method, then paste the code onto every page you want to track.
+ <p>We currently provide a <b>PHP client</b> to call the API from your PHP projects.
+ If you would like to contribute a version of the client in another programming language (Python, Java, Ruby, Perl, etc.) please <a target='_blank'
+ href='http://dev.piwik.org/'>create
+ a ticket</a> in our developer area (please attach the client code to the ticket).
+ </p>
-<ul>
-<li><b>Method 1: Advanced Image Tracker</b>
-<br/>
-<p>The client is used to generate the tracking URL that is wrapped inside a HTML &lt;img src=''&gt; code.
-<br/>Paste this code before the &lt;/body&gt; code in your pages.
-<code>
-&lt;?php <br/>
-// Example 1: Tracks a pageview for Website id = {$idSite}<br/>
-echo '&lt;img src="'. str_replace("&amp;","&amp;amp;", Piwik_getUrlTrackPageView( $idSite = {$idSite}, $customTitle = 'This title will appear in the report Actions > Page titles')) . '" alt="" /&gt;';<br/>
-// Example 2: Triggers a Goal conversion for Website id = {$idSite} and Goal id = 2<br/>
-// $customRevenue is optional and is set to the amount generated by the current transaction (in online shops for example)<br/>
-echo '&lt;img src="'. str_replace("&amp;","&amp;amp;", Piwik_getUrlTrackGoal( $idSite = {$idSite}, $idGoal = 2, $customRevenue = 39)) . '" alt="" /&gt;';<br/>
- ?&gt;
-</code>
-<br/>
-The Advanced Image Tracker method is similar to using the standard Javascript Tracking code. However, some user settings are not detected (resolution, local time, plugins and cookie support).
-</p>
-
- </li>
- <li><b>Method 2: HTTP Request</b>
- <br/>
-<p>You can also query the Piwik Tracker API remotely via HTTP.
-This is useful for environment where you can't execute HTML nor Javascript.
-<br/>Paste this code anywhere in your code where you wish to track a user interaction.
-
-<code>
-&lt;?php <br/>
-$piwikTracker = new PiwikTracker( $idSite = {$idSite} );<br/>
-// You can manually set the visitor details (resolution, time, plugins, etc.) <br/>
-// See all other ->set* functions available in the PiwikTracker.php file<br/>
-$piwikTracker->setResolution(1600, 1400);<br/><br/>
-// Sends Tracker request via http<br/>
-$piwikTracker->doTrackPageView('Document title of current page view');<br/><br/>
-// You can also track Goal conversions<br/>
-$piwikTracker->doTrackGoal($idGoal = 1, $revenue = 42);<br/>
- ?&gt;
-</code>
-</p>
-</li></ul>
-</li>
-</ul>
-</p>
-{if !isset($calledExternally) || !$calledExternally}
- <p>
- Read more about the Piwik Tracking API <a href='http://piwik.org/docs/tracking-api/' target='_blank'>in the documentation</a>
- </p>
-{/if}
+ <p>Follow these instructions to get started with the Tracking API:
+ <ul style='list-style-type:decimal;'>
+ <li><a href='{if isset($piwikUrlRequest)}{$piwikUrlRequest}{else}{$piwikUrl}{/if}{url action=downloadPiwikTracker}' target='_blank'>Click here to
+ download the file PiwikTracker.php</a>
+ </li>
+ <li>Upload the PiwikTracker.php file in the same path as your project files
+ </li>
+ <li>Copy the following code, then paste it onto every page you want to track.
+ <code>
+ &lt;?php <br/>
+ // -- Piwik Tracking API init -- <br/>
+ require_once "/path/to/PiwikTracker.php";<br/>
+ PiwikTracker::$URL = '{if isset($piwikUrlRequest)}{$piwikUrlRequest}{else}{$piwikUrl}{/if}';<br/>
+ ?&gt;
+ </code>
+ </li>
+ <li>Choose a Tracking method, then paste the code onto every page you want to track.
+
+ <ul>
+ <li><b>Method 1: Advanced Image Tracker</b>
+ <br/>
+
+ <p>The client is used to generate the tracking URL that is wrapped inside a HTML &lt;img src=''&gt; code.
+ <br/>Paste this code before the &lt;/body&gt; code in your pages.
+ <code>
+ &lt;?php <br/>
+ // Example 1: Tracks a pageview for Website id = {$idSite}<br/>
+ echo '&lt;img src="'. str_replace("&amp;","&amp;amp;", Piwik_getUrlTrackPageView( $idSite = {$idSite}, $customTitle = 'This title
+ will appear in the report Actions > Page titles')) . '" alt="" /&gt;';<br/>
+ // Example 2: Triggers a Goal conversion for Website id = {$idSite} and Goal id = 2<br/>
+ // $customRevenue is optional and is set to the amount generated by the current transaction (in online shops for example)<br/>
+ echo '&lt;img src="'. str_replace("&amp;","&amp;amp;", Piwik_getUrlTrackGoal( $idSite = {$idSite}, $idGoal = 2, $customRevenue =
+ 39)) . '" alt="" /&gt;';<br/>
+ ?&gt;
+ </code>
+ <br/>
+ The Advanced Image Tracker method is similar to using the standard Javascript Tracking code. However, some user settings are not
+ detected (resolution, local time, plugins and cookie support).
+ </p>
+
+ </li>
+ <li><b>Method 2: HTTP Request</b>
+ <br/>
+
+ <p>You can also query the Piwik Tracker API remotely via HTTP.
+ This is useful for environment where you can't execute HTML nor Javascript.
+ <br/>Paste this code anywhere in your code where you wish to track a user interaction.
+
+ <code>
+ &lt;?php <br/>
+ $piwikTracker = new PiwikTracker( $idSite = {$idSite} );<br/>
+ // You can manually set the visitor details (resolution, time, plugins, etc.) <br/>
+ // See all other ->set* functions available in the PiwikTracker.php file<br/>
+ $piwikTracker->setResolution(1600, 1400);<br/><br/>
+ // Sends Tracker request via http<br/>
+ $piwikTracker->doTrackPageView('Document title of current page view');<br/><br/>
+ // You can also track Goal conversions<br/>
+ $piwikTracker->doTrackGoal($idGoal = 1, $revenue = 42);<br/>
+ ?&gt;
+ </code>
+ </p>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </p>
+ {if !isset($calledExternally) || !$calledExternally}
+ <p>
+ Read more about the Piwik Tracking API <a href='http://piwik.org/docs/tracking-api/' target='_blank'>in the documentation</a>
+ </p>
+ {/if}
</div>
diff --git a/plugins/SitesManager/templates/DisplayJavascriptCode.tpl b/plugins/SitesManager/templates/DisplayJavascriptCode.tpl
index 51bf27e2fd..b34e38fec0 100644
--- a/plugins/SitesManager/templates/DisplayJavascriptCode.tpl
+++ b/plugins/SitesManager/templates/DisplayJavascriptCode.tpl
@@ -1,66 +1,69 @@
-
{literal}
-<style type="text/css">
-.trackingHelp ul {
- padding-left:40px;
- list-style-type:square;
-}
-.trackingHelp ul li {
- margin-bottom:10px;
-}
-.trackingHelp h2 {
- margin-top:20px;
-}
-p {
- text-align:justify;
-}
-</style>
+ <style type="text/css">
+ .trackingHelp ul {
+ padding-left: 40px;
+ list-style-type: square;
+ }
+
+ .trackingHelp ul li {
+ margin-bottom: 10px;
+ }
+
+ .trackingHelp h2 {
+ margin-top: 20px;
+ }
+
+ p {
+ text-align: justify;
+ }
+ </style>
{/literal}
<h2>{'SitesManager_TrackingTags'|translate:$displaySiteName}</h2>
<div class='trackingHelp'>
-{'Installation_JSTracking_Intro'|translate}
-<br/><br/>
-{'CoreAdminHome_JSTrackingIntro3'|translate:'<a href="http://piwik.org/integrate/" target="_blank">':'</a>'}
+ {'Installation_JSTracking_Intro'|translate}
+ <br/><br/>
+ {'CoreAdminHome_JSTrackingIntro3'|translate:'<a href="http://piwik.org/integrate/" target="_blank">':'</a>'}
-<h3>{'SitesManager_JsTrackingTag'|translate}</h3>
-<p>{'CoreAdminHome_JSTracking_CodeNote'|translate:"&lt;/body&gt;"}</p>
+ <h3>{'SitesManager_JsTrackingTag'|translate}</h3>
-<pre class="code-pre"><code>{$jsTag}</code></pre>
+ <p>{'CoreAdminHome_JSTracking_CodeNote'|translate:"&lt;/body&gt;"}</p>
-<br />
-{'CoreAdminHome_JSTrackingIntro5'|translate:'<a target="_blank" href="http://piwik.org/docs/javascript-tracking/">':'</a>'}
-<br/><br/>
-{'Installation_JSTracking_EndNote'|translate:'<em>':'</em>'}
+ <pre class="code-pre"><code>{$jsTag}</code></pre>
+
+ <br/>
+ {'CoreAdminHome_JSTrackingIntro5'|translate:'<a target="_blank" href="http://piwik.org/docs/javascript-tracking/">':'</a>'}
+ <br/><br/>
+ {'Installation_JSTracking_EndNote'|translate:'<em>':'</em>'}
</div>
{literal}
-<script type="text/javascript">
-$(document).ready(function () {
- // when code element is clicked, select the text
- $('code').click(function () {
- // credit where credit is due:
- // http://stackoverflow.com/questions/1173194/select-all-div-text-with-single-mouse-click
- var range;
- if (document.body.createTextRange) // MSIE
- {
- range = document.body.createTextRange();
- range.moveToElementText(this);
- range.select();
- }
- else if (window.getSelection) // others
- {
- range = document.createRange();
- range.selectNodeContents(this);
-
- var selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
- }
- });
-
- $('code').click();
-});
-</script>
+ <script type="text/javascript">
+ $(document).ready(function () {
+ // when code element is clicked, select the text
+ $('code').click(function () {
+ // credit where credit is due:
+ // http://stackoverflow.com/questions/1173194/select-all-div-text-with-single-mouse-click
+ var range;
+ if (document.body.createTextRange) // MSIE
+ {
+ range = document.body.createTextRange();
+ range.moveToElementText(this);
+ range.select();
+ }
+ else if (window.getSelection) // others
+ {
+ range = document.createRange();
+ range.selectNodeContents(this);
+
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+ });
+
+ $('code').click();
+ });
+ </script>
{/literal}
diff --git a/plugins/SitesManager/templates/SitesManager.js b/plugins/SitesManager/templates/SitesManager.js
index 70a42bf7db..24d6afa849 100644
--- a/plugins/SitesManager/templates/SitesManager.js
+++ b/plugins/SitesManager/templates/SitesManager.js
@@ -6,17 +6,16 @@
*/
// NOTE: if you cannot find the definition of a variable here, look in SitesManager.tpl
-function SitesManager ( _timezones, _currencies, _defaultTimezone, _defaultCurrency ) {
+function SitesManager(_timezones, _currencies, _defaultTimezone, _defaultCurrency) {
- var timezones = _timezones;
- var currencies = _currencies;
- var defaultTimezone = _defaultTimezone;
- var defaultCurrency = _defaultCurrency;
- var siteBeingEdited = false;
- var siteBeingEditedName = '';
+ var timezones = _timezones;
+ var currencies = _currencies;
+ var defaultTimezone = _defaultTimezone;
+ var defaultCurrency = _defaultCurrency;
+ var siteBeingEdited = false;
+ var siteBeingEditedName = '';
- function sendDeleteSiteAJAX( idSite )
- {
+ function sendDeleteSiteAJAX(idSite) {
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
idSite: idSite,
@@ -27,27 +26,26 @@ function SitesManager ( _timezones, _currencies, _defaultTimezone, _defaultCurre
ajaxHandler.redirectOnSuccess();
ajaxHandler.setLoadingElement();
ajaxHandler.send(true);
- }
-
- function sendAddSiteAJAX( row )
- {
- var siteName = $(row).find('input#name').val();
- var urls = $(row).find('textarea#urls').val();
- urls = urls.trim().split("\n");
- var excludedIps = $(row).find('textarea#excludedIps').val();
- excludedIps = piwikHelper.getApiFormatTextarea(excludedIps);
- var timezone = $(row).find('#timezones option:selected').val();
- var currency = $(row).find('#currencies option:selected').val();
- var excludedQueryParameters = $(row).find('textarea#excludedQueryParameters').val();
- excludedQueryParameters = piwikHelper.getApiFormatTextarea(excludedQueryParameters);
- var excludedUserAgents = $(row).find('textarea#excludedUserAgents').val();
- excludedUserAgents = piwikHelper.getApiFormatTextarea(excludedUserAgents);
- var keepURLFragments = $('#keepURLFragmentSelect', row).val();
- var ecommerce = $(row).find('#ecommerce option:selected').val();
+ }
+
+ function sendAddSiteAJAX(row) {
+ var siteName = $(row).find('input#name').val();
+ var urls = $(row).find('textarea#urls').val();
+ urls = urls.trim().split("\n");
+ var excludedIps = $(row).find('textarea#excludedIps').val();
+ excludedIps = piwikHelper.getApiFormatTextarea(excludedIps);
+ var timezone = $(row).find('#timezones option:selected').val();
+ var currency = $(row).find('#currencies option:selected').val();
+ var excludedQueryParameters = $(row).find('textarea#excludedQueryParameters').val();
+ excludedQueryParameters = piwikHelper.getApiFormatTextarea(excludedQueryParameters);
+ var excludedUserAgents = $(row).find('textarea#excludedUserAgents').val();
+ excludedUserAgents = piwikHelper.getApiFormatTextarea(excludedUserAgents);
+ var keepURLFragments = $('#keepURLFragmentSelect', row).val();
+ var ecommerce = $(row).find('#ecommerce option:selected').val();
var sitesearch = $(row).find('#sitesearch option:selected').val();
var searchKeywordParameters = $('input#searchKeywordParameters').val();
var searchCategoryParameters = $('input#searchCategoryParameters').val();
-
+
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
module: 'API',
@@ -72,22 +70,21 @@ function SitesManager ( _timezones, _currencies, _defaultTimezone, _defaultCurre
ajaxHandler.setLoadingElement();
ajaxHandler.send(true);
}
-
- function sendUpdateSiteAJAX( row )
- {
- var siteName = $(row).find('input#siteName').val();
- var idSite = $(row).children('#idSite').html();
- var urls = $(row).find('textarea#urls').val();
- urls = urls.trim().split("\n");
- var excludedIps = $(row).find('textarea#excludedIps').val();
- excludedIps = piwikHelper.getApiFormatTextarea(excludedIps);
- var excludedQueryParameters = $(row).find('textarea#excludedQueryParameters').val();
- excludedQueryParameters = piwikHelper.getApiFormatTextarea(excludedQueryParameters);
- var excludedUserAgents = $(row).find('textarea#excludedUserAgents').val();
- excludedUserAgents = piwikHelper.getApiFormatTextarea(excludedUserAgents);
- var keepURLFragments = $('#keepURLFragmentSelect', row).val();
- var timezone = $(row).find('#timezones option:selected').val();
- var currency = $(row).find('#currencies option:selected').val();
+
+ function sendUpdateSiteAJAX(row) {
+ var siteName = $(row).find('input#siteName').val();
+ var idSite = $(row).children('#idSite').html();
+ var urls = $(row).find('textarea#urls').val();
+ urls = urls.trim().split("\n");
+ var excludedIps = $(row).find('textarea#excludedIps').val();
+ excludedIps = piwikHelper.getApiFormatTextarea(excludedIps);
+ var excludedQueryParameters = $(row).find('textarea#excludedQueryParameters').val();
+ excludedQueryParameters = piwikHelper.getApiFormatTextarea(excludedQueryParameters);
+ var excludedUserAgents = $(row).find('textarea#excludedUserAgents').val();
+ excludedUserAgents = piwikHelper.getApiFormatTextarea(excludedUserAgents);
+ var keepURLFragments = $('#keepURLFragmentSelect', row).val();
+ var timezone = $(row).find('#timezones option:selected').val();
+ var currency = $(row).find('#currencies option:selected').val();
var ecommerce = $(row).find('#ecommerce option:selected').val();
var sitesearch = $(row).find('#sitesearch option:selected').val();
var searchKeywordParameters = $('input#searchKeywordParameters').val();
@@ -117,19 +114,18 @@ function SitesManager ( _timezones, _currencies, _defaultTimezone, _defaultCurre
ajaxHandler.redirectOnSuccess();
ajaxHandler.setLoadingElement();
ajaxHandler.send(true);
- }
-
- function sendGlobalSettingsAJAX()
- {
- var timezone = $('#defaultTimezone option:selected').val();
- var currency = $('#defaultCurrency option:selected').val();
- var excludedIps = $('textarea#globalExcludedIps').val();
- excludedIps = piwikHelper.getApiFormatTextarea(excludedIps);
- var excludedQueryParameters = $('textarea#globalExcludedQueryParameters').val();
- excludedQueryParameters = piwikHelper.getApiFormatTextarea(excludedQueryParameters);
- var globalExcludedUserAgents = $('textarea#globalExcludedUserAgents').val();
- globalExcludedUserAgents = piwikHelper.getApiFormatTextarea(globalExcludedUserAgents);
- var globalKeepURLFragments = $('#globalKeepURLFragments').is(':checked') ? 1 : 0;
+ }
+
+ function sendGlobalSettingsAJAX() {
+ var timezone = $('#defaultTimezone option:selected').val();
+ var currency = $('#defaultCurrency option:selected').val();
+ var excludedIps = $('textarea#globalExcludedIps').val();
+ excludedIps = piwikHelper.getApiFormatTextarea(excludedIps);
+ var excludedQueryParameters = $('textarea#globalExcludedQueryParameters').val();
+ excludedQueryParameters = piwikHelper.getApiFormatTextarea(excludedQueryParameters);
+ var globalExcludedUserAgents = $('textarea#globalExcludedUserAgents').val();
+ globalExcludedUserAgents = piwikHelper.getApiFormatTextarea(globalExcludedUserAgents);
+ var globalKeepURLFragments = $('#globalKeepURLFragments').is(':checked') ? 1 : 0;
var searchKeywordParameters = $('input#globalSearchKeywordParameters').val();
var searchCategoryParameters = $('input#globalSearchCategoryParameters').val();
var enableSiteUserAgentExclude = $('input#enableSiteUserAgentExclude').is(':checked') ? 1 : 0;
@@ -155,172 +151,162 @@ function SitesManager ( _timezones, _currencies, _defaultTimezone, _defaultCurre
ajaxHandler.setLoadingElement('#ajaxLoadingGlobalSettings');
ajaxHandler.setErrorElement('#ajaxErrorGlobalSettings');
ajaxHandler.send(true);
- }
-
- this.init = function () {
- $('.addRowSite').click( function() {
- piwikHelper.hideAjaxError();
- $('.addRowSite').toggle();
-
- var numberOfRows = $('table#editSites')[0].rows.length;
- var newRowId = 'rowNew' + numberOfRows;
- var submitButtonHtml = '<input type="submit" class="addsite submit" value="' + _pk_translate('General_Save_js') +'" />';
- $(' <tr id="'+newRowId+'">\
+ }
+
+ this.init = function () {
+ $('.addRowSite').click(function () {
+ piwikHelper.hideAjaxError();
+ $('.addRowSite').toggle();
+
+ var numberOfRows = $('table#editSites')[0].rows.length;
+ var newRowId = 'rowNew' + numberOfRows;
+ var submitButtonHtml = '<input type="submit" class="addsite submit" value="' + _pk_translate('General_Save_js') + '" />';
+ $(' <tr id="' + newRowId + '">\
<td>&nbsp;</td>\
- <td><input id="name" value="Name" size="15" /><br/><br/><br/>'+submitButtonHtml+'</td>\
- <td><textarea cols="25" rows="3" id="urls">http://siteUrl.com/\nhttp://siteUrl2.com/</textarea><br />'+aliasUrlsHelp+keepURLFragmentSelectHTML+'</td>\
- <td><textarea cols="20" rows="4" id="excludedIps"></textarea><br />'+excludedIpHelp+'</td>\
- <td><textarea cols="20" rows="4" id="excludedQueryParameters"></textarea><br />'+excludedQueryParametersHelp+'</td>\
- <td><textarea cols="20" rows="4" id="excludedUserAgents"></textarea><br />'+excludedUserAgentsHelp+'</td>\
- <td>'+getSitesearchSelector(false)+'</td>\
- <td>'+getTimezoneSelector(defaultTimezone)+'<br />' + timezoneHelp + '</td>\
- <td>'+getCurrencySelector(defaultCurrency)+'<br />' + currencyHelp + '</td>\
- <td>'+getEcommerceSelector(0) + '<br />' + ecommerceHelp+ '</td>\
- <td>'+submitButtonHtml+'</td>\
- <td><span class="cancel link_but">'+sprintf(_pk_translate('General_OrCancel_js'),"","")+'</span></td>\
+ <td><input id="name" value="Name" size="15" /><br/><br/><br/>' + submitButtonHtml + '</td>\
+ <td><textarea cols="25" rows="3" id="urls">http://siteUrl.com/\nhttp://siteUrl2.com/</textarea><br />' + aliasUrlsHelp + keepURLFragmentSelectHTML + '</td>\
+ <td><textarea cols="20" rows="4" id="excludedIps"></textarea><br />' + excludedIpHelp + '</td>\
+ <td><textarea cols="20" rows="4" id="excludedQueryParameters"></textarea><br />' + excludedQueryParametersHelp + '</td>\
+ <td><textarea cols="20" rows="4" id="excludedUserAgents"></textarea><br />' + excludedUserAgentsHelp + '</td>\
+ <td>' + getSitesearchSelector(false) + '</td>\
+ <td>' + getTimezoneSelector(defaultTimezone) + '<br />' + timezoneHelp + '</td>\
+ <td>' + getCurrencySelector(defaultCurrency) + '<br />' + currencyHelp + '</td>\
+ <td>' + getEcommerceSelector(0) + '<br />' + ecommerceHelp + '</td>\
+ <td>' + submitButtonHtml + '</td>\
+ <td><span class="cancel link_but">' + sprintf(_pk_translate('General_OrCancel_js'), "", "") + '</span></td>\
</tr>')
- .appendTo('#editSites')
- ;
-
- piwikHelper.lazyScrollTo('#'+newRowId);
-
- $('.addsite').click( function(){
- sendAddSiteAJAX($('tr#'+newRowId));
- });
-
- $('.cancel').click(function() {
- piwikHelper.hideAjaxError();
- $(this).parents('tr').remove();
- $('.addRowSite').toggle();
- });
- return false;
- } );
-
- // when click on deleteuser, the we ask for confirmation and then delete the user
- $('.deleteSite').click( function() {
- piwikHelper.hideAjaxError();
- var idRow = $(this).attr('id');
- var nameToDelete = $(this).parent().parent().find('input#siteName').val() || $(this).parent().parent().find('td#siteName').html();
- var idsiteToDelete = $(this).parent().parent().find('#idSite').html();
-
- $('#confirm h2').text(sprintf(_pk_translate('SitesManager_DeleteConfirm_js'),'"'+nameToDelete+'" (idSite = '+idsiteToDelete+')'));
- piwikHelper.modalConfirm('#confirm', {yes: function(){
- sendDeleteSiteAJAX( idsiteToDelete );
- }});
- }
- );
-
- var alreadyEdited = new Array;
- $('.editSite')
- .click( function() {
- piwikHelper.hideAjaxError();
- var idRow = $(this).attr('id');
- if(alreadyEdited[idRow]==1) return;
- if(siteBeingEdited)
- {
- $('#alert h2').text(sprintf(_pk_translate('SitesManager_OnlyOneSiteAtTime_js'), '"'+$("<div/>").html(siteBeingEditedName).text()+'"'));
- piwikHelper.modalConfirm('#alert', {});
- return;
- }
- siteBeingEdited = true;
-
- alreadyEdited[idRow] = 1;
- $('tr#'+idRow+' .editableSite').each(
- // make the fields editable
- // change the EDIT button to VALID button
- function (i,n) {
- var contentBefore = $(n).html();
-
- var idName = $(n).attr('id');
- if(idName == 'siteName')
- {
- siteBeingEditedName = contentBefore;
- var contentAfter = '<input id="'+idName+'" value="'+piwikHelper.htmlEntities(contentBefore)+'" size="15" />';
-
- var inputSave = $('<br/><input style="margin-top:50px" type="submit" class="submit" value="'+_pk_translate('General_Save_js')+'" />')
- .click( function(){ submitUpdateSite($(this).parent()); });
- var spanCancel = $('<div><br/>'+sprintf(_pk_translate('General_OrCancel_js'),"","")+'</div>')
- .click( function(){ piwikHelper.refreshAfter(0); } );
- $(n)
- .html(contentAfter)
- .keypress( submitSiteOnEnter )
- .append(inputSave)
- .append(spanCancel);
- }
- else if(idName == 'urls')
- {
- var keepURLFragmentsForSite = $(this).closest('tr').attr('data-keep-url-fragments');
-
- var contentAfter = '<textarea cols="25" rows="3" id="urls">'+contentBefore.replace(/<br *\/? *>/gi,"\n")+'</textarea>';
- contentAfter += '<br />'+aliasUrlsHelp+keepURLFragmentSelectHTML;
- $(n).html(contentAfter).find('select').val(keepURLFragmentsForSite);
- }
- else if(idName == 'excludedIps')
- {
- var contentAfter = '<textarea cols="20" rows="4" id="excludedIps">'+contentBefore.replace(/<br *\/? *>/gi,"\n")+'</textarea>';
- contentAfter += '<br />'+excludedIpHelp;
- $(n).html(contentAfter);
- }
- else if(idName == 'excludedQueryParameters')
- {
- var contentAfter = '<textarea cols="20" rows="4" id="excludedQueryParameters">'+contentBefore.replace(/<br *\/? *>/gi,"\n")+'</textarea>';
- contentAfter += '<br />'+excludedQueryParametersHelp;
- $(n).html(contentAfter);
- }
- else if (idName == 'excludedUserAgents')
- {
- var contentAfter = '<textarea cols="20" rows="4" id="excludedUserAgents">' +
- contentBefore.replace(/<br *\/? *>/gi,"\n")+'</textarea><br />'+excludedUserAgentsHelp;
- $(n).html(contentAfter);
- }
- else if(idName == 'timezone')
- {
- var contentAfter = getTimezoneSelector(contentBefore);
- contentAfter += '<br />' + timezoneHelp;
- $(n).html(contentAfter);
- }
- else if(idName == 'currency')
- {
- var contentAfter = getCurrencySelector(contentBefore);
- contentAfter += '<br />' + currencyHelp;
- $(n).html(contentAfter);
- }
- else if(idName == 'ecommerce')
- {
- ecommerceActive = contentBefore.indexOf("ecommerceActive") > 0 ? 1 : 0;
- contentAfter = getEcommerceSelector(ecommerceActive) + '<br />' + ecommerceHelp;
- $(n).html(contentAfter);
- }
- else if(idName == 'sitesearch') {
- contentAfter = getSitesearchSelector(contentBefore);
- $(n).html(contentAfter);
- onClickSiteSearchUseDefault();
+ .appendTo('#editSites')
+ ;
+
+ piwikHelper.lazyScrollTo('#' + newRowId);
+
+ $('.addsite').click(function () {
+ sendAddSiteAJAX($('tr#' + newRowId));
+ });
+
+ $('.cancel').click(function () {
+ piwikHelper.hideAjaxError();
+ $(this).parents('tr').remove();
+ $('.addRowSite').toggle();
+ });
+ return false;
+ });
+
+ // when click on deleteuser, the we ask for confirmation and then delete the user
+ $('.deleteSite').click(function () {
+ piwikHelper.hideAjaxError();
+ var idRow = $(this).attr('id');
+ var nameToDelete = $(this).parent().parent().find('input#siteName').val() || $(this).parent().parent().find('td#siteName').html();
+ var idsiteToDelete = $(this).parent().parent().find('#idSite').html();
+
+ $('#confirm h2').text(sprintf(_pk_translate('SitesManager_DeleteConfirm_js'), '"' + nameToDelete + '" (idSite = ' + idsiteToDelete + ')'));
+ piwikHelper.modalConfirm('#confirm', {yes: function () {
+ sendDeleteSiteAJAX(idsiteToDelete);
+ }});
+ }
+ );
+
+ var alreadyEdited = new Array;
+ $('.editSite')
+ .click(function () {
+ piwikHelper.hideAjaxError();
+ var idRow = $(this).attr('id');
+ if (alreadyEdited[idRow] == 1) return;
+ if (siteBeingEdited) {
+ $('#alert h2').text(sprintf(_pk_translate('SitesManager_OnlyOneSiteAtTime_js'), '"' + $("<div/>").html(siteBeingEditedName).text() + '"'));
+ piwikHelper.modalConfirm('#alert', {});
+ return;
+ }
+ siteBeingEdited = true;
+
+ alreadyEdited[idRow] = 1;
+ $('tr#' + idRow + ' .editableSite').each(
+ // make the fields editable
+ // change the EDIT button to VALID button
+ function (i, n) {
+ var contentBefore = $(n).html();
+
+ var idName = $(n).attr('id');
+ if (idName == 'siteName') {
+ siteBeingEditedName = contentBefore;
+ var contentAfter = '<input id="' + idName + '" value="' + piwikHelper.htmlEntities(contentBefore) + '" size="15" />';
+
+ var inputSave = $('<br/><input style="margin-top:50px" type="submit" class="submit" value="' + _pk_translate('General_Save_js') + '" />')
+ .click(function () { submitUpdateSite($(this).parent()); });
+ var spanCancel = $('<div><br/>' + sprintf(_pk_translate('General_OrCancel_js'), "", "") + '</div>')
+ .click(function () { piwikHelper.refreshAfter(0); });
+ $(n)
+ .html(contentAfter)
+ .keypress(submitSiteOnEnter)
+ .append(inputSave)
+ .append(spanCancel);
+ }
+ else if (idName == 'urls') {
+ var keepURLFragmentsForSite = $(this).closest('tr').attr('data-keep-url-fragments');
+
+ var contentAfter = '<textarea cols="25" rows="3" id="urls">' + contentBefore.replace(/<br *\/? *>/gi, "\n") + '</textarea>';
+ contentAfter += '<br />' + aliasUrlsHelp + keepURLFragmentSelectHTML;
+ $(n).html(contentAfter).find('select').val(keepURLFragmentsForSite);
+ }
+ else if (idName == 'excludedIps') {
+ var contentAfter = '<textarea cols="20" rows="4" id="excludedIps">' + contentBefore.replace(/<br *\/? *>/gi, "\n") + '</textarea>';
+ contentAfter += '<br />' + excludedIpHelp;
+ $(n).html(contentAfter);
+ }
+ else if (idName == 'excludedQueryParameters') {
+ var contentAfter = '<textarea cols="20" rows="4" id="excludedQueryParameters">' + contentBefore.replace(/<br *\/? *>/gi, "\n") + '</textarea>';
+ contentAfter += '<br />' + excludedQueryParametersHelp;
+ $(n).html(contentAfter);
+ }
+ else if (idName == 'excludedUserAgents') {
+ var contentAfter = '<textarea cols="20" rows="4" id="excludedUserAgents">' +
+ contentBefore.replace(/<br *\/? *>/gi, "\n") + '</textarea><br />' + excludedUserAgentsHelp;
+ $(n).html(contentAfter);
+ }
+ else if (idName == 'timezone') {
+ var contentAfter = getTimezoneSelector(contentBefore);
+ contentAfter += '<br />' + timezoneHelp;
+ $(n).html(contentAfter);
+ }
+ else if (idName == 'currency') {
+ var contentAfter = getCurrencySelector(contentBefore);
+ contentAfter += '<br />' + currencyHelp;
+ $(n).html(contentAfter);
+ }
+ else if (idName == 'ecommerce') {
+ ecommerceActive = contentBefore.indexOf("ecommerceActive") > 0 ? 1 : 0;
+ contentAfter = getEcommerceSelector(ecommerceActive) + '<br />' + ecommerceHelp;
+ $(n).html(contentAfter);
+ }
+ else if (idName == 'sitesearch') {
+ contentAfter = getSitesearchSelector(contentBefore);
+ $(n).html(contentAfter);
+ onClickSiteSearchUseDefault();
+ }
}
- }
- );
- $(this)
- .toggle()
- .parent()
- .prepend( $('<input type="submit" class="updateSite submit" value="' + _pk_translate('General_Save_js') + '" />')
- .click( function(){ sendUpdateSiteAJAX( $('tr#'+idRow) ); } )
- );
- });
-
- $('#globalSettingsSubmit').click( function() {
- sendGlobalSettingsAJAX();
- });
-
- $('#defaultTimezone').html( getTimezoneSelector(defaultTimezone));
- $('#defaultCurrency').html( getCurrencySelector(defaultCurrency));
-
- $('td.editableSite').click( function(){ $(this).parent().find('.editSite').click(); } );
- }
-
- function getSitesearchSelector(contentBefore)
- {
+ );
+ $(this)
+ .toggle()
+ .parent()
+ .prepend($('<input type="submit" class="updateSite submit" value="' + _pk_translate('General_Save_js') + '" />')
+ .click(function () { sendUpdateSiteAJAX($('tr#' + idRow)); })
+ );
+ });
+
+ $('#globalSettingsSubmit').click(function () {
+ sendGlobalSettingsAJAX();
+ });
+
+ $('#defaultTimezone').html(getTimezoneSelector(defaultTimezone));
+ $('#defaultCurrency').html(getCurrencySelector(defaultCurrency));
+
+ $('td.editableSite').click(function () { $(this).parent().find('.editSite').click(); });
+ }
+
+ function getSitesearchSelector(contentBefore) {
var globalKeywordParameters = $('input#globalSearchKeywordParameters').val().trim();
var globalCategoryParameters = $('input#globalSearchCategoryParameters').val().trim();
- if(contentBefore) {
+ if (contentBefore) {
enabled = contentBefore.indexOf("sitesearchActive") > 0 ? 1 : 0;
spanSearch = $(contentBefore).filter('.sskp');
var searchKeywordParameters = spanSearch.attr('sitesearch_keyword_parameters').trim();
@@ -341,103 +327,96 @@ function SitesManager ( _timezones, _currencies, _defaultTimezone, _defaultCurre
html += '</select>';
html += '<span style="font-size: 11px;"><br/>';
- if(searchGlobalHasValues)
- {
+ if (searchGlobalHasValues) {
checkedStr = checked ? ' checked ' : '';
- html += '<label><span id="sitesearchUseDefault"'+ (!enabled ? ' style="display:none" ' : '') +'><input type="checkbox" '+checkedStr+' id="sitesearchUseDefaultCheck" onclick="return onClickSiteSearchUseDefault();"> '+sitesearchUseDefault+' </span>';
+ html += '<label><span id="sitesearchUseDefault"' + (!enabled ? ' style="display:none" ' : '') + '><input type="checkbox" ' + checkedStr + ' id="sitesearchUseDefaultCheck" onclick="return onClickSiteSearchUseDefault();"> ' + sitesearchUseDefault + ' </span>';
html += '</label>';
html += '<div ' + ((checked && enabled) ? '' : 'style="display-none"') + ' class="searchDisplayParams form-description">'
+ searchKeywordLabel + ' (' + strDefault + ') ' + ': '
+ globalKeywordParameters
- + (globalCategoryParameters.length ? ', '+ searchCategoryLabel + ': ' + globalCategoryParameters: '')
+ + (globalCategoryParameters.length ? ', ' + searchCategoryLabel + ': ' + globalCategoryParameters : '')
+ '</div>';
}
- html += '<div id="sitesearchIntro">'+sitesearchIntro+'</div>';
+ html += '<div id="sitesearchIntro">' + sitesearchIntro + '</div>';
html += '<div id="searchSiteParameters">';
- html += '<br/><label><div style="margin-bottom:3px">'+searchKeywordLabel+'</div><input type="text" size="22" id="searchKeywordParameters" value="'+searchKeywordParameters+'" style="margin-bottom: -10px;font-size:9pt;font-family:monospace"></input>'+searchKeywordHelp+'</label>';
+ html += '<br/><label><div style="margin-bottom:3px">' + searchKeywordLabel + '</div><input type="text" size="22" id="searchKeywordParameters" value="' + searchKeywordParameters + '" style="margin-bottom: -10px;font-size:9pt;font-family:monospace"></input>' + searchKeywordHelp + '</label>';
// if custom var plugin is disabled, category tracking not supported
- if(globalCategoryParameters!='globalSearchCategoryParametersIsDisabled') {
- html += '<br/><label><div style="margin-bottom:3px">'+searchCategoryLabel+'</div><input type="text" size="22" id="searchCategoryParameters" value="'+searchCategoryParameters+'" style="margin-bottom: -10px;font-size:9pt;font-family:monospace"></input>'+searchCategoryHelp+'</label>';
+ if (globalCategoryParameters != 'globalSearchCategoryParametersIsDisabled') {
+ html += '<br/><label><div style="margin-bottom:3px">' + searchCategoryLabel + '</div><input type="text" size="22" id="searchCategoryParameters" value="' + searchCategoryParameters + '" style="margin-bottom: -10px;font-size:9pt;font-family:monospace"></input>' + searchCategoryHelp + '</label>';
}
html += '</div></span>';
return html;
}
- function getEcommerceSelector(enabled)
- {
- var html = '<select id="ecommerce">';
- selected = ' selected="selected" ';
- html += '<option ' + (enabled ? '' : selected) + ' value="0">' + ecommerceDisabled + '</option>';
- html += '<option ' + (enabled ? selected : '') + ' value="1">' + ecommerceEnabled + '</option>';
- html += '</select>';
- return html;
- }
-
- function getTimezoneSelector(selectedTimezone)
- {
- var html = '<select id="timezones">';
- for(var continent in timezones) {
- html += '<optgroup label="' + continent + '">';
- for(var timezoneId in timezones[continent]) {
- var selected = '';
- if(timezoneId == selectedTimezone) {
- selected = ' selected="selected" ';
- }
- html += '<option ' + selected + ' value="'+ timezoneId + '">' + timezones[continent][timezoneId] + '</option>';
- }
- html += "</optgroup>\n";
- }
- html += '</select>';
- return html;
- }
-
-
- function getCurrencySelector(selectedCurrency)
- {
- var html = '<select id="currencies">';
- for(var currency in currencies) {
- var selected = '';
- if(currency == selectedCurrency) {
- selected = ' selected="selected" ';
- }
- html += '<option ' + selected + ' value="'+ currency + '">' + currencies[currency] + '</option>';
- }
- html += '</select>';
- return html;
- }
-
- function submitSiteOnEnter(e)
- {
- var key=e.keyCode || e.which;
- if (key==13)
- {
- submitUpdateSite(this);
- $(this).find('.addsite').click();
- }
- }
- function submitUpdateSite(self)
- {
- $(self).parent().find('.updateSite').click();
- }
+ function getEcommerceSelector(enabled) {
+ var html = '<select id="ecommerce">';
+ selected = ' selected="selected" ';
+ html += '<option ' + (enabled ? '' : selected) + ' value="0">' + ecommerceDisabled + '</option>';
+ html += '<option ' + (enabled ? selected : '') + ' value="1">' + ecommerceEnabled + '</option>';
+ html += '</select>';
+ return html;
+ }
+
+ function getTimezoneSelector(selectedTimezone) {
+ var html = '<select id="timezones">';
+ for (var continent in timezones) {
+ html += '<optgroup label="' + continent + '">';
+ for (var timezoneId in timezones[continent]) {
+ var selected = '';
+ if (timezoneId == selectedTimezone) {
+ selected = ' selected="selected" ';
+ }
+ html += '<option ' + selected + ' value="' + timezoneId + '">' + timezones[continent][timezoneId] + '</option>';
+ }
+ html += "</optgroup>\n";
+ }
+ html += '</select>';
+ return html;
+ }
+
+
+ function getCurrencySelector(selectedCurrency) {
+ var html = '<select id="currencies">';
+ for (var currency in currencies) {
+ var selected = '';
+ if (currency == selectedCurrency) {
+ selected = ' selected="selected" ';
+ }
+ html += '<option ' + selected + ' value="' + currency + '">' + currencies[currency] + '</option>';
+ }
+ html += '</select>';
+ return html;
+ }
+
+ function submitSiteOnEnter(e) {
+ var key = e.keyCode || e.which;
+ if (key == 13) {
+ submitUpdateSite(this);
+ $(this).find('.addsite').click();
+ }
+ }
+
+ function submitUpdateSite(self) {
+ $(self).parent().find('.updateSite').click();
+ }
}
-function onClickSiteSearchUseDefault()
-{
+function onClickSiteSearchUseDefault() {
// Site Search enabled
- if($('select#sitesearch').val() == "1") {
+ if ($('select#sitesearch').val() == "1") {
$('#sitesearchUseDefault').show();
// Use default is checked
- if($('#sitesearchUseDefaultCheck').is(':checked')) {
+ if ($('#sitesearchUseDefaultCheck').is(':checked')) {
$('#searchSiteParameters').hide();
$('#sitesearchIntro').show();
$('#searchKeywordParameters,#searchCategoryParameters').val('');
$('.searchDisplayParams').show();
- // Use default is unchecked
+ // Use default is unchecked
} else {
$('#sitesearchIntro').hide();
diff --git a/plugins/SitesManager/templates/SitesManager.tpl b/plugins/SitesManager/templates/SitesManager.tpl
index 775e82f816..f720fc2f21 100644
--- a/plugins/SitesManager/templates/SitesManager.tpl
+++ b/plugins/SitesManager/templates/SitesManager.tpl
@@ -2,322 +2,410 @@
{loadJavascriptTranslations plugins='SitesManager'}
<script type="text/javascript">
-{capture assign=excludedIpHelpPlain}{'SitesManager_HelpExcludedIps'|translate:"1.2.3.*":"1.2.*.*"}<br /><br /> {'SitesManager_YourCurrentIpAddressIs'|translate:"<i>$currentIpAddress</i>"}{/capture}
-{assign var=excludedIpHelp value=$excludedIpHelpPlain|inlineHelp}
-var excludedIpHelp = '{$excludedIpHelp|escape:javascript}';
-var aliasUrlsHelp = '{'SitesManager_AliasUrlHelp'|translate|inlineHelp|escape:javascript}';
-{capture assign=defaultTimezoneHelpPlain}
- {if $timezoneSupported}
- {'SitesManager_ChooseCityInSameTimezoneAsYou'|translate}
- {else}
- {'SitesManager_AdvancedTimezoneSupportNotFound'|translate}
- {/if} <br /><br />{'SitesManager_UTCTimeIs'|translate:$utcTime}
-{/capture}
+ {capture assign=excludedIpHelpPlain}{'SitesManager_HelpExcludedIps'|translate:"1.2.3.*":"1.2.*.*"}<
+ br / > <br/>
+ {'SitesManager_YourCurrentIpAddressIs'|translate:"<i>$currentIpAddress</i>"}{/capture}
+ {assign var=excludedIpHelp value=$excludedIpHelpPlain|inlineHelp}
+ var excludedIpHelp = '{$excludedIpHelp|escape:javascript}';
+ var aliasUrlsHelp = '{'SitesManager_AliasUrlHelp'|translate|inlineHelp|escape:javascript}';
+ {capture assign=defaultTimezoneHelpPlain}
+ {if $timezoneSupported}
+ {'SitesManager_ChooseCityInSameTimezoneAsYou'|translate}
+ {else}
+ {'SitesManager_AdvancedTimezoneSupportNotFound'|translate}
+ {/if} <
+ br / > <br/>{'SitesManager_UTCTimeIs'|translate:$utcTime}
+ {/capture}
-{capture assign=timezoneHelpPlain}
- {$defaultTimezoneHelpPlain}
- <br /><br />{'SitesManager_ChangingYourTimezoneWillOnlyAffectDataForward'|translate}
-{/capture}
+ {capture assign=timezoneHelpPlain}
+ {$defaultTimezoneHelpPlain}
+ <br/> <br/>{'SitesManager_ChangingYourTimezoneWillOnlyAffectDataForward'|translate}
+ {/capture}
-{capture assign=currencyHelpPlain}
- {'SitesManager_CurrencySymbolWillBeUsedForGoals'|translate|inlineHelp}
-{/capture}
+ {capture assign=currencyHelpPlain}
+ {'SitesManager_CurrencySymbolWillBeUsedForGoals'|translate|inlineHelp}
+ {/capture}
-{capture assign=ecommerceHelpPlain}
- {'SitesManager_EcommerceHelp'|translate}
- <br />
- {'SitesManager_PiwikOffersEcommerceAnalytics'|translate:"<a href='http://piwik.org/docs/ecommerce-analytics/' target='_blank'>":"</a>"}
-{/capture}
+ {capture assign=ecommerceHelpPlain}
+ {'SitesManager_EcommerceHelp'|translate}
+ <br/>
+ {'SitesManager_PiwikOffersEcommerceAnalytics'|translate:"<a href='http://piwik.org/docs/ecommerce-analytics/' target='_blank'>":"</a>"}
+ {/capture}
-{capture assign=excludedQueryParametersHelp}
- {'SitesManager_ListOfQueryParametersToExclude'|translate}
- <br /><br />
- {'SitesManager_PiwikWillAutomaticallyExcludeCommonSessionParameters'|translate:"phpsessid, sessionid, ..."}
-{/capture}
-{assign var=excludedQueryParametersHelp value=$excludedQueryParametersHelp|inlineHelp}
+ {capture assign=excludedQueryParametersHelp}
+ {'SitesManager_ListOfQueryParametersToExclude'|translate}
+ <br/> <br/>
+ {'SitesManager_PiwikWillAutomaticallyExcludeCommonSessionParameters'|translate:"phpsessid, sessionid, ..."}
+ {/capture}
+ {assign var=excludedQueryParametersHelp value=$excludedQueryParametersHelp|inlineHelp}
-{capture assign=excludedUserAgentsHelp}
- {'SitesManager_GlobalExcludedUserAgentHelp1'|translate}
- <br/><br/>
- {'SitesManager_GlobalListExcludedUserAgents_Desc'|translate} {'SitesManager_GlobalExcludedUserAgentHelp2'|translate}
-{/capture}
-{assign var=excludedUserAgentsHelp value=$excludedUserAgentsHelp|inlineHelp}
+ {capture assign=excludedUserAgentsHelp}
+ {'SitesManager_GlobalExcludedUserAgentHelp1'|translate}
+ <br/> <br/>
+ {'SitesManager_GlobalListExcludedUserAgents_Desc'|translate} {'SitesManager_GlobalExcludedUserAgentHelp2'|translate}
+ {/capture}
+ {assign var=excludedUserAgentsHelp value=$excludedUserAgentsHelp|inlineHelp}
-{capture assign=keepURLFragmentSelectHTML}
- <h4 style="display:inline-block;">{'SitesManager_KeepURLFragmentsLong'|translate}</h4>
+ {capture assign=keepURLFragmentSelectHTML}
+ < h4
+ style = "display:inline-block;" >{'SitesManager_KeepURLFragmentsLong'|translate} < /h4>
- <select id="keepURLFragmentSelect">
- <option value="0">{if $globalKeepURLFragments}{'General_Yes'|translate}{else}{'General_No'|translate}{/if} ({'General_Default'|translate})</option>
- <option value="1">{'General_Yes'|translate}</option>
- <option value="2">{'General_No'|translate}</option>
- </select>
-{/capture}
-var excludedQueryParametersHelp = '{$excludedQueryParametersHelp|escape:javascript}';
-var excludedUserAgentsHelp = '{$excludedUserAgentsHelp|escape:javascript}';
-var timezoneHelp = '{$timezoneHelpPlain|inlineHelp|escape:javascript}';
-var currencyHelp = '{$currencyHelpPlain|escape:javascript}';
-var ecommerceHelp = '{$ecommerceHelpPlain|inlineHelp|escape:javascript}';
-var ecommerceEnabled = '{'SitesManager_EnableEcommerce'|translate|escape:javascript}';
-var ecommerceDisabled = '{'SitesManager_NotAnEcommerceSite'|translate|escape:javascript}';
-{assign var=defaultTimezoneHelp value=$defaultTimezoneHelpPlain|inlineHelp}
-{assign var=searchKeywordHelp value='SitesManager_SearchKeywordParametersDesc'|translate|inlineHelp}
-{capture assign=searchCategoryHelpText}{'Goals_Optional'|translate} {'SitesManager_SearchCategoryParametersDesc'|translate}{/capture}
-{assign var=searchCategoryHelp value=$searchCategoryHelpText|inlineHelp}
-var sitesearchEnabled = '{'SitesManager_EnableSiteSearch'|translate|escape:javascript}';
-var sitesearchDisabled = '{'SitesManager_DisableSiteSearch'|translate|escape:javascript}';
-var searchKeywordHelp = '{$searchKeywordHelp|escape:javascript}';
-var searchCategoryHelp = '{$searchCategoryHelp|escape:javascript}';
-var sitesearchDesc = '{'SitesManager_TrackingSiteSearch'|translate|escape:javascript}';
-var keepURLFragmentSelectHTML = '{$keepURLFragmentSelectHTML|escape:javascript}';
-
-var sitesManager = new SitesManager ( {$timezones}, {$currencies}, '{$defaultTimezone}', '{$defaultCurrency}');
-{assign var=searchKeywordLabel value='SitesManager_SearchKeywordLabel'|translate}
-{assign var=searchCategoryLabel value='SitesManager_SearchCategoryLabel'|translate}
-var searchKeywordLabel = '{$searchKeywordLabel|escape:javascript}';
-var searchCategoryLabel = '{$searchCategoryLabel|escape:javascript}';
-{assign var=sitesearchIntro value='SitesManager_SiteSearchUse'|translate}
-var sitesearchIntro = '{$sitesearchIntro|inlineHelp|escape:javascript}';
-var sitesearchUseDefault = '{if $isSuperUser}{'SitesManager_SearchUseDefault'|translate:'<a href="#globalSiteSearch">':'</a>'|escape:'javascript'}{else}{'SitesManager_SearchUseDefault'|translate:'':''|escape:'javascript'}{/if}';
-var strDefault = '{'General_Default'|translate:escape:'javascript'}';
-{literal}
-$(document).ready( function() {
- sitesManager.init();
-});
+ < select
+ id = "keepURLFragmentSelect" >
+ < option
+ value = "0" > {if $globalKeepURLFragments}{'General_Yes'|translate}{else}{'General_No'|translate}{/if} ({'General_Default'|translate}) < /option>
+ < option
+ value = "1" >{'General_Yes'|translate} < /option>
+ < option
+ value = "2" >{'General_No'|translate} < /option>
+ < /select>
+ {/capture}
+ var excludedQueryParametersHelp = '{$excludedQueryParametersHelp|escape:javascript}';
+ var excludedUserAgentsHelp = '{$excludedUserAgentsHelp|escape:javascript}';
+ var timezoneHelp = '{$timezoneHelpPlain|inlineHelp|escape:javascript}';
+ var currencyHelp = '{$currencyHelpPlain|escape:javascript}';
+ var ecommerceHelp = '{$ecommerceHelpPlain|inlineHelp|escape:javascript}';
+ var ecommerceEnabled = '{'SitesManager_EnableEcommerce'|translate|escape:javascript}';
+ var ecommerceDisabled = '{'SitesManager_NotAnEcommerceSite'|translate|escape:javascript}';
+ {assign var=defaultTimezoneHelp value=$defaultTimezoneHelpPlain|inlineHelp}
+ {assign var=searchKeywordHelp value='SitesManager_SearchKeywordParametersDesc'|translate|inlineHelp}
+ {capture assign=searchCategoryHelpText}{'Goals_Optional'|translate} {'SitesManager_SearchCategoryParametersDesc'|translate}{/capture}
+ {assign var=searchCategoryHelp value=$searchCategoryHelpText|inlineHelp}
+ var sitesearchEnabled = '{'SitesManager_EnableSiteSearch'|translate|escape:javascript}';
+ var sitesearchDisabled = '{'SitesManager_DisableSiteSearch'|translate|escape:javascript}';
+ var searchKeywordHelp = '{$searchKeywordHelp|escape:javascript}';
+ var searchCategoryHelp = '{$searchCategoryHelp|escape:javascript}';
+ var sitesearchDesc = '{'SitesManager_TrackingSiteSearch'|translate|escape:javascript}';
+ var keepURLFragmentSelectHTML = '{$keepURLFragmentSelectHTML|escape:javascript}';
+
+ var sitesManager = new SitesManager({$timezones}, {$currencies}, '{$defaultTimezone}', '{$defaultCurrency}');
+ {assign var=searchKeywordLabel value='SitesManager_SearchKeywordLabel'|translate}
+ {assign var=searchCategoryLabel value='SitesManager_SearchCategoryLabel'|translate}
+ var searchKeywordLabel = '{$searchKeywordLabel|escape:javascript}';
+ var searchCategoryLabel = '{$searchCategoryLabel|escape:javascript}';
+ {assign var=sitesearchIntro value='SitesManager_SiteSearchUse'|translate}
+ var sitesearchIntro = '{$sitesearchIntro|inlineHelp|escape:javascript}';
+ var sitesearchUseDefault = '{if $isSuperUser}{'SitesManager_SearchUseDefault'|translate:'<a href="#globalSiteSearch">':'</a>'|escape:'javascript'}{else}{'SitesManager_SearchUseDefault'|translate:'':''|escape:'javascript'}{/if}';
+ var strDefault = '{'General_Default'|translate:escape:'javascript'}';
+ {literal}
+ $(document).ready(function () {
+ sitesManager.init();
+ });
</script>
-<style type="text/css">
-.entityTable tr td {
- vertical-align: top;
- padding-top:7px;
-}
-
-.addRowSite:hover, .editableSite:hover, .addsite:hover, .cancel:hover, .deleteSite:hover, .editSite:hover, .updateSite:hover{
- cursor: pointer;
-}
-.addRowSite a {
- text-decoration: none;
-}
-.addRowSite {
- padding:1em;
- font-weight:bold;
-}
-#editSites {
- vertical-align: top;
-}
-option, select {
- font-size:11px;
-}
-textarea {
-font-size:9pt;
-}
-.admin thead th {
-vertical-align:middle;
-}
-.ecommerceInactive,.sitesearchInactive {
- color: #666666;
-}
-#searchSiteParameters {
- display:none;
-}
-#editSites h4 {
- font-size:.8em;
- margin:1em 0 1em 0;
- font-weight:bold;
-}
-</style>
+ <style type="text/css">
+ .entityTable tr td {
+ vertical-align: top;
+ padding-top: 7px;
+ }
+
+ .addRowSite:hover, .editableSite:hover, .addsite:hover, .cancel:hover, .deleteSite:hover, .editSite:hover, .updateSite:hover {
+ cursor: pointer;
+ }
+
+ .addRowSite a {
+ text-decoration: none;
+ }
+
+ .addRowSite {
+ padding: 1em;
+ font-weight: bold;
+ }
+
+ #editSites {
+ vertical-align: top;
+ }
+
+ option, select {
+ font-size: 11px;
+ }
+
+ textarea {
+ font-size: 9pt;
+ }
+
+ .admin thead th {
+ vertical-align: middle;
+ }
+
+ .ecommerceInactive, .sitesearchInactive {
+ color: #666666;
+ }
+
+ #searchSiteParameters {
+ display: none;
+ }
+
+ #editSites h4 {
+ font-size: .8em;
+ margin: 1em 0 1em 0;
+ font-weight: bold;
+ }
+ </style>
{/literal}
<h2>{'SitesManager_WebsitesManagement'|translate}</h2>
<p>{'SitesManager_MainDescription'|translate}
-{'SitesManager_YouCurrentlyHaveAccessToNWebsites'|translate:"<b>$adminSitesCount</b>"}
-{if $isSuperUser}
-<br />{'SitesManager_SuperUserCan'|translate:"<a href='#globalSettings'>":"</a>"}
-{/if}
+ {'SitesManager_YouCurrentlyHaveAccessToNWebsites'|translate:"<b>$adminSitesCount</b>"}
+ {if $isSuperUser}
+ <br/>
+ {'SitesManager_SuperUserCan'|translate:"<a href='#globalSettings'>":"</a>"}
+ {/if}
</p>
{ajaxErrorDiv}
{ajaxLoadingDiv}
{capture assign=createNewWebsite}
- <div class="addRowSite"><img src='plugins/UsersManager/images/add.png' alt="" /> {'SitesManager_AddSite'|translate}</div>
+ <div class="addRowSite"><img src='plugins/UsersManager/images/add.png' alt=""/> {'SitesManager_AddSite'|translate}</div>
{/capture}
{if $adminSites|@count == 0}
- {'SitesManager_NoWebsites'|translate}
+ {'SitesManager_NoWebsites'|translate}
{else}
<div class="ui-confirm" id="confirm">
<h2></h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_No'|translate}" />
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_No'|translate}"/>
+ </div>
+ <div class="entityContainer">
+ {if $isSuperUser}
+ {$createNewWebsite}
+ {/if}
+ <table class="entityTable dataTable" id="editSites">
+ <thead>
+ <tr>
+ <th>{'General_Id'|translate}</th>
+ <th>{'General_Name'|translate}</th>
+ <th>{'SitesManager_Urls'|translate}</th>
+ <th>{'SitesManager_ExcludedIps'|translate}</th>
+ <th>{'SitesManager_ExcludedParameters'|translate|replace:" ":"<br />"}</th>
+ <th id='exclude-user-agent-header'
+ {if !$allowSiteSpecificUserAgentExclude}style="display:none"{/if}>{'SitesManager_ExcludedUserAgents'|translate}</th>
+ <th>{'Actions_SubmenuSitesearch'|translate}</th>
+ <th>{'SitesManager_Timezone'|translate}</th>
+ <th>{'SitesManager_Currency'|translate}</th>
+ <th>{'Goals_Ecommerce'|translate}</th>
+ <th></th>
+ <th></th>
+ <th> {'SitesManager_JsTrackingTag'|translate} </th>
+ </tr>
+ </thead>
+ <tbody>
+ {foreach from=$adminSites key=i item=site}
+ <tr id="row{$site.idsite}" data-keep-url-fragments="{$site.keep_url_fragment}">
+ <td id="idSite">{$site.idsite}</td>
+ <td id="siteName" class="editableSite">{$site.name}</td>
+ <td id="urls" class="editableSite">{foreach from=$site.alias_urls item=url}{$url|replace:"http://":""}<br/>{/foreach}</td>
+ <td id="excludedIps" class="editableSite">{foreach from=$site.excluded_ips item=ip}{$ip}<br/>{/foreach}</td>
+ <td id="excludedQueryParameters" class="editableSite">{foreach from=$site.excluded_parameters item=parameter}{$parameter}<br/>{/foreach}
+ </td>
+ <td id="excludedUserAgents" class="editableSite"
+ {if !$allowSiteSpecificUserAgentExclude}style="display:none"{/if}>{foreach from=$site.excluded_user_agents item=ua}{$ua}<br/>{/foreach}
+ </td>
+ <td id="sitesearch" class="editableSite">{if $site.sitesearch}<span class='sitesearchActive'>{'General_Yes'|translate}</span>{else}<span
+ class='sitesearchInactive'>-</span>{/if}<span class='sskp'
+ sitesearch_keyword_parameters="{$site.sitesearch_keyword_parameters|escape:'html'}"
+ sitesearch_category_parameters="{$site.sitesearch_category_parameters|escape:'html'}"
+ id="sitesearch_parameters"></span></td>
+ <td id="timezone" class="editableSite">{$site.timezone}</td>
+ <td id="currency" class="editableSite">{$site.currency}</td>
+ <td id="ecommerce" class="editableSite">{if $site.ecommerce}<span class='ecommerceActive'>{'General_Yes'|translate}</span>{else}
+ <span class='ecommerceInactive'>-</span>
+ {/if}</td>
+ <td><span id="row{$site.idsite}" class='editSite link_but'><img src='themes/default/images/ico_edit.png' title="{'General_Edit'|translate}"
+ border="0"/> {'General_Edit'|translate}</span></td>
+ <td><span id="row{$site.idsite}" class="deleteSite link_but"><img src='themes/default/images/ico_delete.png'
+ title="{'General_Delete'|translate}"
+ border="0"/> {'General_Delete'|translate}</span></td>
+ <td>
+ <a href='{url module=CoreAdminHome action=trackingCodeGenerator idSite=$site.idsite updated=false}'>{'SitesManager_ShowTrackingTag'|translate}</a>
+ </td>
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ {if $isSuperUser}
+ {$createNewWebsite}
+ {/if}
</div>
-
- <div class="entityContainer">
- {if $isSuperUser}
- {$createNewWebsite}
- {/if}
- <table class="entityTable dataTable" id="editSites">
- <thead>
- <tr>
- <th>{'General_Id'|translate}</th>
- <th>{'General_Name'|translate}</th>
- <th>{'SitesManager_Urls'|translate}</th>
- <th>{'SitesManager_ExcludedIps'|translate}</th>
- <th>{'SitesManager_ExcludedParameters'|translate|replace:" ":"<br />"}</th>
- <th id='exclude-user-agent-header' {if !$allowSiteSpecificUserAgentExclude}style="display:none"{/if}>{'SitesManager_ExcludedUserAgents'|translate}</th>
- <th>{'Actions_SubmenuSitesearch'|translate}</th>
- <th>{'SitesManager_Timezone'|translate}</th>
- <th>{'SitesManager_Currency'|translate}</th>
- <th>{'Goals_Ecommerce'|translate}</th>
- <th> </th>
- <th> </th>
- <th> {'SitesManager_JsTrackingTag'|translate} </th>
- </tr>
- </thead>
- <tbody>
- {foreach from=$adminSites key=i item=site}
- <tr id="row{$site.idsite}" data-keep-url-fragments="{$site.keep_url_fragment}">
- <td id="idSite">{$site.idsite}</td>
- <td id="siteName" class="editableSite">{$site.name}</td>
- <td id="urls" class="editableSite">{foreach from=$site.alias_urls item=url}{$url|replace:"http://":""}<br />{/foreach}</td>
- <td id="excludedIps" class="editableSite">{foreach from=$site.excluded_ips item=ip}{$ip}<br />{/foreach}</td>
- <td id="excludedQueryParameters" class="editableSite">{foreach from=$site.excluded_parameters item=parameter}{$parameter}<br />{/foreach}</td>
- <td id="excludedUserAgents" class="editableSite" {if !$allowSiteSpecificUserAgentExclude}style="display:none"{/if}>{foreach from=$site.excluded_user_agents item=ua}{$ua}<br />{/foreach}</td>
- <td id="sitesearch" class="editableSite">{if $site.sitesearch}<span class='sitesearchActive'>{'General_Yes'|translate}</span>{else}<span class='sitesearchInactive'>-</span>{/if}<span class='sskp' sitesearch_keyword_parameters="{$site.sitesearch_keyword_parameters|escape:'html'}" sitesearch_category_parameters="{$site.sitesearch_category_parameters|escape:'html'}" id="sitesearch_parameters"></span></td>
- <td id="timezone" class="editableSite">{$site.timezone}</td>
- <td id="currency" class="editableSite">{$site.currency}</td>
- <td id="ecommerce" class="editableSite">{if $site.ecommerce}<span class='ecommerceActive'>{'General_Yes'|translate}</span>{else}<span class='ecommerceInactive'>-</span>{/if}</td>
- <td><span id="row{$site.idsite}" class='editSite link_but'><img src='themes/default/images/ico_edit.png' title="{'General_Edit'|translate}" border="0"/> {'General_Edit'|translate}</span></td>
- <td><span id="row{$site.idsite}" class="deleteSite link_but"><img src='themes/default/images/ico_delete.png' title="{'General_Delete'|translate}" border="0" /> {'General_Delete'|translate}</span></td>
- <td><a href='{url module=CoreAdminHome action=trackingCodeGenerator idSite=$site.idsite updated=false}'>{'SitesManager_ShowTrackingTag'|translate}</a></td>
- </tr>
- {/foreach}
- </tbody>
- </table>
- {if $isSuperUser}
- {$createNewWebsite}
- {/if}
- </div>
{/if}
{* Admin users use these values for Site Search column, when editing websites *}
{if !$isSuperUser}
-<input type="hidden" size="15" id="globalSearchKeywordParameters" value="{$globalSearchKeywordParameters|escape:'html'}"></input>
-<input type="hidden" size="15" id="globalSearchCategoryParameters" value="{$globalSearchCategoryParameters|escape:'html'}"></input>
+ <input type="hidden" size="15" id="globalSearchKeywordParameters" value="{$globalSearchKeywordParameters|escape:'html'}"></input>
+ <input type="hidden" size="15" id="globalSearchCategoryParameters" value="{$globalSearchCategoryParameters|escape:'html'}"></input>
{/if}
{if $isSuperUser}
-<br />
- <a name='globalSettings'></a>
- <h2>{'SitesManager_GlobalWebsitesSettings'|translate}</h2>
- <br />
- <table style='width:600px' class="adminTable" >
-
- <tr><td colspan="2">
- <b>{'SitesManager_GlobalListExcludedIps'|translate}</b>
- <p>{'SitesManager_ListOfIpsToBeExcludedOnAllWebsites'|translate} </p>
- </td></tr>
- <tr><td>
- <textarea cols="30" rows="3" id="globalExcludedIps">{$globalExcludedIps}
-</textarea>
- </td><td>
- <label for="globalExcludedIps">{$excludedIpHelp}</label>
- </td></tr>
-
- <tr><td colspan="2">
- <b>{'SitesManager_GlobalListExcludedQueryParameters'|translate}</b>
- <p>{'SitesManager_ListOfQueryParametersToBeExcludedOnAllWebsites'|translate} </p>
- </td></tr>
-
- <tr><td>
- <textarea cols="30" rows="3" id="globalExcludedQueryParameters">{$globalExcludedQueryParameters}
-</textarea>
- </td><td><label for="globalExcludedQueryParameters">{$excludedQueryParametersHelp}</label>
- </td></tr>
-
- {* global excluded user agents *}
- <tr><td colspan="2">
- <b>{'SitesManager_GlobalListExcludedUserAgents'|translate}</b>
- <p>{'SitesManager_GlobalListExcludedUserAgents_Desc'|translate}</p>
- </td></tr>
-
- <tr><td>
- <textarea cols="30" rows="3" id="globalExcludedUserAgents">{$globalExcludedUserAgents}</textarea>
- </td><td><label for="globalExcludedUserAgents">{$excludedUserAgentsHelp}</label>
- </td></tr>
-
- <tr><td>
- <input type="checkbox" id="enableSiteUserAgentExclude" name="enableSiteUserAgentExclude" {if $allowSiteSpecificUserAgentExclude}checked="checked"{/if}/><label for="enableSiteUserAgentExclude">{'SitesManager_EnableSiteSpecificUserAgentExclude'|translate}</label>
- <span id='enableSiteUserAgentExclude-loading' class='loadingPiwik' style='display:none'><img src='./themes/default/images/loading-blue.gif' /></span>
- </td><td>{'SitesManager_EnableSiteSpecificUserAgentExclude_Help'|translate:'<a href="#editSites">':'</a>'|inlineHelp}
- </td></tr>
-
- {* global keep URL fragments *}
- <tr><td colspan="2">
- <strong>{'SitesManager_KeepURLFragments'|translate}</strong>
- <p>{'SitesManager_KeepURLFragmentsHelp'|translate:"<em>#</em>":"<em>example.org/index.html#first_section</em>":"<em>example.org/index.html</em>"}
- </p>
- <input type="checkbox" id="globalKeepURLFragments" name="globalKeepURLFragments" {if $globalKeepURLFragments}checked="checked"{/if}/>
- <label for="globalKeepURLFragments">{'SitesManager_KeepURLFragmentsLong'|translate}</label>
- <p>{'SitesManager_KeepURLFragmentsHelp2'|translate}</p>
- </td></tr>
-
- {* global site search *}
- <tr><td colspan="2">
- <a name='globalSiteSearch'></a><b>{'SitesManager_TrackingSiteSearch'|translate}</b>
- <p>{$sitesearchIntro}</p>
- <span class="form-description" style='font-size:8pt'>{'SitesManager_SearchParametersNote'|translate} {'SitesManager_SearchParametersNote2'|translate}</span>
- </td></tr>
- <tr><td colspan="2">
- <label>{$searchKeywordLabel} &nbsp;<input type="text" size="15" id="globalSearchKeywordParameters" value="{$globalSearchKeywordParameters|escape:'html'}"></input>
- <div style='width: 200px;float:right;'>{$searchKeywordHelp}</div></label>
- </td></tr>
-
- <tr><td colspan="2">
- {if !$isSearchCategoryTrackingEnabled}
- <input value='globalSearchCategoryParametersIsDisabled' id="globalSearchCategoryParameters" type='hidden'></input>
- <span class='form-description'>Note: you could also track your Internal Search Engine Categories, but the plugin Custom Variables is required. Please enable the plugin CustomVariables (or ask your Piwik admin).</span>
- {else}
- {'Goals_Optional'|translate} {'SitesManager_SearchCategoryDesc'|translate} <br/>
- </td></tr>
- <tr><td colspan="2">
- <label>{$searchCategoryLabel} &nbsp;<input type="text" size="15" id="globalSearchCategoryParameters" value="{$globalSearchCategoryParameters|escape:'html'}"></input>
- <div style='width: 200px;float:right;'>{$searchCategoryHelp}</div></label>
- {/if}
- </td></tr>
-
- <tr><td colspan="2">
- <b>{'SitesManager_DefaultTimezoneForNewWebsites'|translate}</b>
- <p>{'SitesManager_SelectDefaultTimezone'|translate} </p>
- </td></tr>
- <tr><td>
- <div id='defaultTimezone'></div>
- </td><td>
- {$defaultTimezoneHelp}
- </td></tr>
-
- <tr><td colspan="2">
- <b>{'SitesManager_DefaultCurrencyForNewWebsites'|translate}</b>
- <p>{'SitesManager_SelectDefaultCurrency'|translate} </p>
- </td></tr>
- <tr><td>
- <div id='defaultCurrency'></div>
- </td><td>
- {$currencyHelpPlain}
- </td></tr>
- </table>
- <span style='margin-left:20px'>
- <input type="submit" class="submit" id='globalSettingsSubmit' value="{'General_Save'|translate}" />
+ <br/>
+ <a name='globalSettings'></a>
+ <h2>{'SitesManager_GlobalWebsitesSettings'|translate}</h2>
+ <br/>
+ <table style='width:600px' class="adminTable">
+
+ <tr>
+ <td colspan="2">
+ <b>{'SitesManager_GlobalListExcludedIps'|translate}</b>
+
+ <p>{'SitesManager_ListOfIpsToBeExcludedOnAllWebsites'|translate} </p>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <textarea cols="30" rows="3" id="globalExcludedIps">{$globalExcludedIps}
+ </textarea>
+ </td>
+ <td>
+ <label for="globalExcludedIps">{$excludedIpHelp}</label>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <b>{'SitesManager_GlobalListExcludedQueryParameters'|translate}</b>
+
+ <p>{'SitesManager_ListOfQueryParametersToBeExcludedOnAllWebsites'|translate} </p>
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ <textarea cols="30" rows="3" id="globalExcludedQueryParameters">{$globalExcludedQueryParameters}
+ </textarea>
+ </td>
+ <td><label for="globalExcludedQueryParameters">{$excludedQueryParametersHelp}</label>
+ </td>
+ </tr>
+
+ {* global excluded user agents *}
+ <tr>
+ <td colspan="2">
+ <b>{'SitesManager_GlobalListExcludedUserAgents'|translate}</b>
+
+ <p>{'SitesManager_GlobalListExcludedUserAgents_Desc'|translate}</p>
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ <textarea cols="30" rows="3" id="globalExcludedUserAgents">{$globalExcludedUserAgents}</textarea>
+ </td>
+ <td><label for="globalExcludedUserAgents">{$excludedUserAgentsHelp}</label>
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ <input type="checkbox" id="enableSiteUserAgentExclude" name="enableSiteUserAgentExclude"
+ {if $allowSiteSpecificUserAgentExclude}checked="checked"{/if}/><label
+ for="enableSiteUserAgentExclude">{'SitesManager_EnableSiteSpecificUserAgentExclude'|translate}</label>
+ <span id='enableSiteUserAgentExclude-loading' class='loadingPiwik' style='display:none'><img
+ src='./themes/default/images/loading-blue.gif'/></span>
+ </td>
+ <td>{'SitesManager_EnableSiteSpecificUserAgentExclude_Help'|translate:'<a href="#editSites">':'</a>'|inlineHelp}
+ </td>
+ </tr>
+
+ {* global keep URL fragments *}
+ <tr>
+ <td colspan="2">
+ <strong>{'SitesManager_KeepURLFragments'|translate}</strong>
+
+ <p>{'SitesManager_KeepURLFragmentsHelp'|translate:"<em>#</em>":"<em>example.org/index.html#first_section</em>":"<em>example.org/index.html</em>"}
+ </p>
+ <input type="checkbox" id="globalKeepURLFragments" name="globalKeepURLFragments" {if $globalKeepURLFragments}checked="checked"{/if}/>
+ <label for="globalKeepURLFragments">{'SitesManager_KeepURLFragmentsLong'|translate}</label>
+
+ <p>{'SitesManager_KeepURLFragmentsHelp2'|translate}</p>
+ </td>
+ </tr>
+
+ {* global site search *}
+ <tr>
+ <td colspan="2">
+ <a name='globalSiteSearch'></a><b>{'SitesManager_TrackingSiteSearch'|translate}</b>
+
+ <p>{$sitesearchIntro}</p>
+ <span class="form-description"
+ style='font-size:8pt'>{'SitesManager_SearchParametersNote'|translate} {'SitesManager_SearchParametersNote2'|translate}</span>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <label>{$searchKeywordLabel} &nbsp;<input type="text" size="15" id="globalSearchKeywordParameters"
+ value="{$globalSearchKeywordParameters|escape:'html'}"></input>
+
+ <div style='width: 200px;float:right;'>{$searchKeywordHelp}</div>
+ </label>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ {if !$isSearchCategoryTrackingEnabled}
+ <input value='globalSearchCategoryParametersIsDisabled' id="globalSearchCategoryParameters" type='hidden'></input>
+ <span class='form-description'>Note: you could also track your Internal Search Engine Categories, but the plugin Custom Variables is required. Please enable the plugin CustomVariables (or ask your Piwik admin).</span>
+ {else}
+ {'Goals_Optional'|translate} {'SitesManager_SearchCategoryDesc'|translate} <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <label>{$searchCategoryLabel} &nbsp;<input type="text" size="15" id="globalSearchCategoryParameters"
+ value="{$globalSearchCategoryParameters|escape:'html'}"></input>
+
+ <div style='width: 200px;float:right;'>{$searchCategoryHelp}</div>
+ </label>
+ {/if}
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <b>{'SitesManager_DefaultTimezoneForNewWebsites'|translate}</b>
+
+ <p>{'SitesManager_SelectDefaultTimezone'|translate} </p>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <div id='defaultTimezone'></div>
+ </td>
+ <td>
+ {$defaultTimezoneHelp}
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2">
+ <b>{'SitesManager_DefaultCurrencyForNewWebsites'|translate}</b>
+
+ <p>{'SitesManager_SelectDefaultCurrency'|translate} </p>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <div id='defaultCurrency'></div>
+ </td>
+ <td>
+ {$currencyHelpPlain}
+ </td>
+ </tr>
+ </table>
+ <span style='margin-left:20px'>
+ <input type="submit" class="submit" id='globalSettingsSubmit' value="{'General_Save'|translate}"/>
</span>
- {ajaxErrorDiv id=ajaxErrorGlobalSettings}
- {ajaxLoadingDiv id=ajaxLoadingGlobalSettings}
+ {ajaxErrorDiv id=ajaxErrorGlobalSettings}
+ {ajaxLoadingDiv id=ajaxLoadingGlobalSettings}
{/if}
{if $showAddSite}
-<script type="text/javascript">{literal}
-$(document).ready(function(){
- $('.addRowSite:first').trigger('click');
-});
-{/literal}</script>
+ <script type="text/javascript">{literal}
+ $(document).ready(function () {
+ $('.addRowSite:first').trigger('click');
+ });
+ {/literal}</script>
{/if}
-<br /><br /><br /><br />
+<br/><br/><br/><br/>
{include file="CoreAdminHome/templates/footer.tpl"}
diff --git a/plugins/Transitions/API.php b/plugins/Transitions/API.php
index ca178a02a2..67e4735dbd 100644
--- a/plugins/Transitions/API.php
+++ b/plugins/Transitions/API.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Transitions
*/
@@ -14,301 +14,285 @@
*/
class Piwik_Transitions_API
{
-
- static private $instance = null;
-
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- public function getTransitionsForPageTitle($pageTitle, $idSite, $period, $date, $segment = false, $limitBeforeGrouping = false)
- {
- return $this->getTransitionsForAction($pageTitle, 'title', $idSite, $period, $date, $segment, $limitBeforeGrouping);
- }
-
- public function getTransitionsForPageUrl($pageUrl, $idSite, $period, $date, $segment = false, $limitBeforeGrouping = false)
- {
- return $this->getTransitionsForAction($pageUrl, 'url', $idSite, $period, $date, $segment, $limitBeforeGrouping);
- }
-
- /**
- * General method to get transitions for an action
- *
- * @param $actionName
- * @param $actionType "url"|"title"
- * @param $idSite
- * @param $period
- * @param $date
- * @param bool $segment
- * @param bool $limitBeforeGrouping
- * @param string $parts
- * @param bool $returnNormalizedUrls
- * @return array
- * @throws Exception
- */
- public function getTransitionsForAction($actionName, $actionType, $idSite, $period, $date,
- $segment = false, $limitBeforeGrouping = false, $parts = 'all', $returnNormalizedUrls = false)
- {
- Piwik::checkUserHasViewAccess($idSite);
-
- // get idaction of the requested action
- $idaction = $this->deriveIdAction($actionName, $actionType);
- if ($idaction < 0)
- {
- throw new Exception('NoDataForAction');
- }
-
- // prepare archive processing that can be used by the archiving code
- $archiveProcessing = new Piwik_ArchiveProcessing_Day();
- $archiveProcessing->setSite(new Piwik_Site($idSite));
- $archiveProcessing->setPeriod(Piwik_Period::advancedFactory($period, $date));
- $archiveProcessing->setSegment(new Piwik_Segment($segment, $idSite));
- $archiveProcessing->initForLiveUsage();
-
- // prepare the report
- $report = array(
- 'date' => Piwik_Period_Day::advancedFactory($period, $date)->getLocalizedShortString()
- );
-
- // add data to the report
- $transitionsArchiving = new Piwik_Transitions;
- if ($returnNormalizedUrls)
- {
- $transitionsArchiving->returnNormalizedUrls();
- }
-
- $partsArray = explode(',', $parts);
-
- if ($parts == 'all' || in_array('internalReferrers', $partsArray))
- {
- $this->addInternalReferrers($transitionsArchiving, $archiveProcessing, $report, $idaction,
- $actionType, $limitBeforeGrouping);
- }
- if ($parts == 'all' || in_array('followingActions', $partsArray))
- {
- $includeLoops = $parts != 'all' && !in_array('internalReferrers', $partsArray);
- $this->addFollowingActions($transitionsArchiving, $archiveProcessing, $report, $idaction,
- $actionType, $limitBeforeGrouping, $includeLoops);
- }
- if ($parts == 'all' || in_array('externalReferrers', $partsArray))
- {
- $this->addExternalReferrers($transitionsArchiving, $archiveProcessing, $report, $idaction,
- $actionType, $limitBeforeGrouping);
- }
-
- // derive the number of exits from the other metrics
- if ($parts == 'all') {
- $report['pageMetrics']['exits'] = $report['pageMetrics']['pageviews']
- - $transitionsArchiving->getTotalTransitionsToFollowingActions()
- - $report['pageMetrics']['loops'];
- }
-
- // replace column names in the data tables
- $reportNames = array(
- 'previousPages' => true,
- 'previousSiteSearches' => false,
- 'followingPages' => true,
- 'followingSiteSearches' => false,
- 'outlinks' => true,
- 'downloads' => true
- );
- foreach ($reportNames as $reportName => $replaceLabel)
- {
- if (isset($report[$reportName]))
- {
- $columnNames = array(Piwik_Archive::INDEX_NB_ACTIONS => 'referrals');
- if ($replaceLabel)
- {
- $columnNames[Piwik_Archive::INDEX_NB_ACTIONS] = 'referrals';
- }
- $report[$reportName]->filter('ReplaceColumnNames', array($columnNames));
- }
- }
-
- return $report;
- }
-
- /**
- * Derive the action ID from the request action name and type.
- */
- private function deriveIdAction($actionName, $actionType)
- {
- $actionsPlugin = new Piwik_Actions;
- switch ($actionType)
- {
- case 'url':
- $originalActionName = $actionName;
- $actionName = Piwik_Common::unsanitizeInputValue($actionName);
- $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_url');
-
- if ($id < 0)
- {
- // an example where this is needed is urls containing < or >
- $actionName = $originalActionName;
- $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_url');
- }
-
- return $id;
-
- case 'title':
- $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_name');
-
- if ($id < 0)
- {
- $unkown = Piwik_Actions_ArchivingHelper::getUnknownActionName(
- Piwik_Tracker_Action::TYPE_ACTION_NAME);
-
- if (trim($actionName) == trim($unkown))
- {
- $id = $actionsPlugin->getIdActionFromSegment('', 'idaction_name');
- }
- }
-
- return $id;
-
- default:
- throw new Exception('Unknown action type');
- }
- }
-
- /**
- * Add the internal referrers to the report:
- * previous pages and previous site searches
- *
- * @param Piwik_Transitions $transitionsArchiving
- * @param $archiveProcessing
- * @param $report
- * @param $idaction
- * @param string $actionType
- * @param $limitBeforeGrouping
- * @throws Exception
- */
- private function addInternalReferrers($transitionsArchiving, $archiveProcessing, &$report,
- $idaction, $actionType, $limitBeforeGrouping) {
-
- $data = $transitionsArchiving->queryInternalReferrers(
- $idaction, $actionType, $archiveProcessing, $limitBeforeGrouping);
-
- if ($data['pageviews'] == 0)
- {
- throw new Exception('NoDataForAction');
- }
-
- $report['previousPages'] = &$data['previousPages'];
- $report['previousSiteSearches'] = &$data['previousSiteSearches'];
- $report['pageMetrics']['loops'] = $data['loops'];
- $report['pageMetrics']['pageviews'] = $data['pageviews'];
- }
-
- /**
- * Add the following actions to the report:
- * following pages, downloads, outlinks
- *
- * @param Piwik_Transitions $transitionsArchiving
- * @param $archiveProcessing
- * @param $report
- * @param $idaction
- * @param string $actionType
- * @param $limitBeforeGrouping
- * @param boolean $includeLoops
- */
- private function addFollowingActions($transitionsArchiving, $archiveProcessing, &$report,
- $idaction, $actionType, $limitBeforeGrouping, $includeLoops=false) {
-
- $data = $transitionsArchiving->queryFollowingActions(
- $idaction, $actionType, $archiveProcessing, $limitBeforeGrouping, $includeLoops);
-
- foreach ($data as $tableName => $table)
- {
- $report[$tableName] = $table;
- }
- }
-
- /**
- * Add the external referrers to the report:
- * direct entries, websites, campaigns, search engines
- *
- * @param Piwik_Transitions $transitionsArchiving
- * @param $archiveProcessing
- * @param $report
- * @param $idaction
- * @param string $actionType
- * @param $limitBeforeGrouping
- */
- private function addExternalReferrers($transitionsArchiving, $archiveProcessing, &$report,
- $idaction, $actionType, $limitBeforeGrouping) {
-
- $data = $transitionsArchiving->queryExternalReferrers(
- $idaction, $actionType, $archiveProcessing, $limitBeforeGrouping);
-
- $report['pageMetrics']['entries'] = 0;
- $report['referrers'] = array();
- foreach ($data->getRows() as $row)
- {
- $referrerId = $row->getColumn('label');
- $visits = $row->getColumn(Piwik_Archive::INDEX_NB_VISITS);
- if ($visits)
- {
- // load details (i.e. subtables)
- $details = array();
- if ($idSubTable = $row->getIdSubDataTable())
- {
- $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
- foreach ($subTable->getRows() as $subRow)
- {
- $details[] = array(
- 'label' => $subRow->getColumn('label'),
- 'referrals' => $subRow->getColumn(Piwik_Archive::INDEX_NB_VISITS)
- );
- }
- }
- $report['referrers'][] = array(
- 'label' => $this->getReferrerLabel($referrerId),
- 'shortName' => Piwik_getRefererTypeFromShortName($referrerId),
- 'visits' => $visits,
- 'details' => $details
- );
- $report['pageMetrics']['entries'] += $visits;
- }
- }
-
- // if there's no data for referrers, Piwik_API_ResponseBuilder::handleMultiDimensionalArray
- // does not detect the multi dimensional array and the data is rendered differently, which
- // causes an exception.
- if (count($report['referrers']) == 0)
- {
- $report['referrers'][] = array(
- 'label' => $this->getReferrerLabel(Piwik_Common::REFERER_TYPE_DIRECT_ENTRY),
- 'shortName' => Piwik_getRefererTypeLabel(Piwik_Common::REFERER_TYPE_DIRECT_ENTRY),
- 'visits' => 0
- );
- }
- }
-
- private function getReferrerLabel($referrerId) {
- switch ($referrerId)
- {
- case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
- return Piwik_Transitions_Controller::getTranslation('directEntries');
- case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
- return Piwik_Transitions_Controller::getTranslation('fromSearchEngines');
- case Piwik_Common::REFERER_TYPE_WEBSITE:
- return Piwik_Transitions_Controller::getTranslation('fromWebsites');
- case Piwik_Common::REFERER_TYPE_CAMPAIGN:
- return Piwik_Transitions_Controller::getTranslation('fromCampaigns');
- default:
- return Piwik_Translate('General_Others');
- }
- }
-
- public function getTranslations() {
- $controller = new Piwik_Transitions_Controller();
- return $controller->getTranslations();
- }
-
+
+ static private $instance = null;
+
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ public function getTransitionsForPageTitle($pageTitle, $idSite, $period, $date, $segment = false, $limitBeforeGrouping = false)
+ {
+ return $this->getTransitionsForAction($pageTitle, 'title', $idSite, $period, $date, $segment, $limitBeforeGrouping);
+ }
+
+ public function getTransitionsForPageUrl($pageUrl, $idSite, $period, $date, $segment = false, $limitBeforeGrouping = false)
+ {
+ return $this->getTransitionsForAction($pageUrl, 'url', $idSite, $period, $date, $segment, $limitBeforeGrouping);
+ }
+
+ /**
+ * General method to get transitions for an action
+ *
+ * @param $actionName
+ * @param $actionType "url"|"title"
+ * @param $idSite
+ * @param $period
+ * @param $date
+ * @param bool $segment
+ * @param bool $limitBeforeGrouping
+ * @param string $parts
+ * @param bool $returnNormalizedUrls
+ * @return array
+ * @throws Exception
+ */
+ public function getTransitionsForAction($actionName, $actionType, $idSite, $period, $date,
+ $segment = false, $limitBeforeGrouping = false, $parts = 'all', $returnNormalizedUrls = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+
+ // get idaction of the requested action
+ $idaction = $this->deriveIdAction($actionName, $actionType);
+ if ($idaction < 0) {
+ throw new Exception('NoDataForAction');
+ }
+
+ // prepare archive processing that can be used by the archiving code
+ $archiveProcessing = new Piwik_ArchiveProcessing_Day();
+ $archiveProcessing->setSite(new Piwik_Site($idSite));
+ $archiveProcessing->setPeriod(Piwik_Period::advancedFactory($period, $date));
+ $archiveProcessing->setSegment(new Piwik_Segment($segment, $idSite));
+ $archiveProcessing->initForLiveUsage();
+
+ // prepare the report
+ $report = array(
+ 'date' => Piwik_Period_Day::advancedFactory($period, $date)->getLocalizedShortString()
+ );
+
+ // add data to the report
+ $transitionsArchiving = new Piwik_Transitions;
+ if ($returnNormalizedUrls) {
+ $transitionsArchiving->returnNormalizedUrls();
+ }
+
+ $partsArray = explode(',', $parts);
+
+ if ($parts == 'all' || in_array('internalReferrers', $partsArray)) {
+ $this->addInternalReferrers($transitionsArchiving, $archiveProcessing, $report, $idaction,
+ $actionType, $limitBeforeGrouping);
+ }
+ if ($parts == 'all' || in_array('followingActions', $partsArray)) {
+ $includeLoops = $parts != 'all' && !in_array('internalReferrers', $partsArray);
+ $this->addFollowingActions($transitionsArchiving, $archiveProcessing, $report, $idaction,
+ $actionType, $limitBeforeGrouping, $includeLoops);
+ }
+ if ($parts == 'all' || in_array('externalReferrers', $partsArray)) {
+ $this->addExternalReferrers($transitionsArchiving, $archiveProcessing, $report, $idaction,
+ $actionType, $limitBeforeGrouping);
+ }
+
+ // derive the number of exits from the other metrics
+ if ($parts == 'all') {
+ $report['pageMetrics']['exits'] = $report['pageMetrics']['pageviews']
+ - $transitionsArchiving->getTotalTransitionsToFollowingActions()
+ - $report['pageMetrics']['loops'];
+ }
+
+ // replace column names in the data tables
+ $reportNames = array(
+ 'previousPages' => true,
+ 'previousSiteSearches' => false,
+ 'followingPages' => true,
+ 'followingSiteSearches' => false,
+ 'outlinks' => true,
+ 'downloads' => true
+ );
+ foreach ($reportNames as $reportName => $replaceLabel) {
+ if (isset($report[$reportName])) {
+ $columnNames = array(Piwik_Archive::INDEX_NB_ACTIONS => 'referrals');
+ if ($replaceLabel) {
+ $columnNames[Piwik_Archive::INDEX_NB_ACTIONS] = 'referrals';
+ }
+ $report[$reportName]->filter('ReplaceColumnNames', array($columnNames));
+ }
+ }
+
+ return $report;
+ }
+
+ /**
+ * Derive the action ID from the request action name and type.
+ */
+ private function deriveIdAction($actionName, $actionType)
+ {
+ $actionsPlugin = new Piwik_Actions;
+ switch ($actionType) {
+ case 'url':
+ $originalActionName = $actionName;
+ $actionName = Piwik_Common::unsanitizeInputValue($actionName);
+ $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_url');
+
+ if ($id < 0) {
+ // an example where this is needed is urls containing < or >
+ $actionName = $originalActionName;
+ $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_url');
+ }
+
+ return $id;
+
+ case 'title':
+ $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_name');
+
+ if ($id < 0) {
+ $unkown = Piwik_Actions_ArchivingHelper::getUnknownActionName(
+ Piwik_Tracker_Action::TYPE_ACTION_NAME);
+
+ if (trim($actionName) == trim($unkown)) {
+ $id = $actionsPlugin->getIdActionFromSegment('', 'idaction_name');
+ }
+ }
+
+ return $id;
+
+ default:
+ throw new Exception('Unknown action type');
+ }
+ }
+
+ /**
+ * Add the internal referrers to the report:
+ * previous pages and previous site searches
+ *
+ * @param Piwik_Transitions $transitionsArchiving
+ * @param $archiveProcessing
+ * @param $report
+ * @param $idaction
+ * @param string $actionType
+ * @param $limitBeforeGrouping
+ * @throws Exception
+ */
+ private function addInternalReferrers($transitionsArchiving, $archiveProcessing, &$report,
+ $idaction, $actionType, $limitBeforeGrouping)
+ {
+
+ $data = $transitionsArchiving->queryInternalReferrers(
+ $idaction, $actionType, $archiveProcessing, $limitBeforeGrouping);
+
+ if ($data['pageviews'] == 0) {
+ throw new Exception('NoDataForAction');
+ }
+
+ $report['previousPages'] = & $data['previousPages'];
+ $report['previousSiteSearches'] = & $data['previousSiteSearches'];
+ $report['pageMetrics']['loops'] = $data['loops'];
+ $report['pageMetrics']['pageviews'] = $data['pageviews'];
+ }
+
+ /**
+ * Add the following actions to the report:
+ * following pages, downloads, outlinks
+ *
+ * @param Piwik_Transitions $transitionsArchiving
+ * @param $archiveProcessing
+ * @param $report
+ * @param $idaction
+ * @param string $actionType
+ * @param $limitBeforeGrouping
+ * @param boolean $includeLoops
+ */
+ private function addFollowingActions($transitionsArchiving, $archiveProcessing, &$report,
+ $idaction, $actionType, $limitBeforeGrouping, $includeLoops = false)
+ {
+
+ $data = $transitionsArchiving->queryFollowingActions(
+ $idaction, $actionType, $archiveProcessing, $limitBeforeGrouping, $includeLoops);
+
+ foreach ($data as $tableName => $table) {
+ $report[$tableName] = $table;
+ }
+ }
+
+ /**
+ * Add the external referrers to the report:
+ * direct entries, websites, campaigns, search engines
+ *
+ * @param Piwik_Transitions $transitionsArchiving
+ * @param $archiveProcessing
+ * @param $report
+ * @param $idaction
+ * @param string $actionType
+ * @param $limitBeforeGrouping
+ */
+ private function addExternalReferrers($transitionsArchiving, $archiveProcessing, &$report,
+ $idaction, $actionType, $limitBeforeGrouping)
+ {
+
+ $data = $transitionsArchiving->queryExternalReferrers(
+ $idaction, $actionType, $archiveProcessing, $limitBeforeGrouping);
+
+ $report['pageMetrics']['entries'] = 0;
+ $report['referrers'] = array();
+ foreach ($data->getRows() as $row) {
+ $referrerId = $row->getColumn('label');
+ $visits = $row->getColumn(Piwik_Archive::INDEX_NB_VISITS);
+ if ($visits) {
+ // load details (i.e. subtables)
+ $details = array();
+ if ($idSubTable = $row->getIdSubDataTable()) {
+ $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
+ foreach ($subTable->getRows() as $subRow) {
+ $details[] = array(
+ 'label' => $subRow->getColumn('label'),
+ 'referrals' => $subRow->getColumn(Piwik_Archive::INDEX_NB_VISITS)
+ );
+ }
+ }
+ $report['referrers'][] = array(
+ 'label' => $this->getReferrerLabel($referrerId),
+ 'shortName' => Piwik_getRefererTypeFromShortName($referrerId),
+ 'visits' => $visits,
+ 'details' => $details
+ );
+ $report['pageMetrics']['entries'] += $visits;
+ }
+ }
+
+ // if there's no data for referrers, Piwik_API_ResponseBuilder::handleMultiDimensionalArray
+ // does not detect the multi dimensional array and the data is rendered differently, which
+ // causes an exception.
+ if (count($report['referrers']) == 0) {
+ $report['referrers'][] = array(
+ 'label' => $this->getReferrerLabel(Piwik_Common::REFERER_TYPE_DIRECT_ENTRY),
+ 'shortName' => Piwik_getRefererTypeLabel(Piwik_Common::REFERER_TYPE_DIRECT_ENTRY),
+ 'visits' => 0
+ );
+ }
+ }
+
+ private function getReferrerLabel($referrerId)
+ {
+ switch ($referrerId) {
+ case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
+ return Piwik_Transitions_Controller::getTranslation('directEntries');
+ case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
+ return Piwik_Transitions_Controller::getTranslation('fromSearchEngines');
+ case Piwik_Common::REFERER_TYPE_WEBSITE:
+ return Piwik_Transitions_Controller::getTranslation('fromWebsites');
+ case Piwik_Common::REFERER_TYPE_CAMPAIGN:
+ return Piwik_Transitions_Controller::getTranslation('fromCampaigns');
+ default:
+ return Piwik_Translate('General_Others');
+ }
+ }
+
+ public function getTranslations()
+ {
+ $controller = new Piwik_Transitions_Controller();
+ return $controller->getTranslations();
+ }
+
} \ No newline at end of file
diff --git a/plugins/Transitions/Controller.php b/plugins/Transitions/Controller.php
index 86e093c523..5ef2dc539e 100644
--- a/plugins/Transitions/Controller.php
+++ b/plugins/Transitions/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Transitions
*/
@@ -14,78 +14,78 @@
*/
class Piwik_Transitions_Controller extends Piwik_Controller
{
-
- /**
- * Since the metric translations are taken from different plugins,
- * it makes the rest of the code easier to read and maintain when we
- * use this indirection to map between the metrics and the actual
- * translation keys.
- */
- private static $metricTranslations = array(
- 'pageviewsInline' => 'Transitions_PageviewsInline',
- 'loopsInline' => 'Transitions_LoopsInline',
- 'fromPreviousPages' => 'Transitions_FromPreviousPages',
- 'fromPreviousPagesInline' => 'Transitions_FromPreviousPagesInline',
- 'fromPreviousSiteSearches' => 'Transitions_FromPreviousSiteSearches',
- 'fromPreviousSiteSearchesInline' => 'Transitions_FromPreviousSiteSearchesInline',
- 'fromSearchEngines' => 'Transitions_FromSearchEngines',
- 'fromSearchEnginesInline' => 'Transitions_FromSearchEnginesInline',
- 'fromWebsites' => 'Transitions_FromWebsites',
- 'fromWebsitesInline' => 'Transitions_FromWebsitesInline',
- 'fromCampaigns' => 'Transitions_FromCampaigns',
- 'fromCampaignsInline' => 'Transitions_FromCampaignsInline',
- 'directEntries' => 'Transitions_DirectEntries',
- 'directEntriesInline' => 'Referers_TypeDirectEntries',
- 'toFollowingPages' => 'Transitions_ToFollowingPages',
- 'toFollowingPagesInline' => 'Transitions_ToFollowingPagesInline',
- 'toFollowingSiteSearches' => 'Transitions_ToFollowingSiteSearches',
- 'toFollowingSiteSearchesInline' => 'Transitions_ToFollowingSiteSearchesInline',
- 'downloads' => 'Actions_ColumnDownloads',
- 'downloadsInline' => 'VisitsSummary_NbDownloadsDescription',
- 'outlinks' => 'Actions_ColumnOutlinks',
- 'outlinksInline' => 'VisitsSummary_NbOutlinksDescription',
- 'exits' => 'General_ColumnExits',
- 'exitsInline' => 'Transitions_ExitsInline',
- 'bouncesInline' => 'Transitions_BouncesInline'
- );
- /**
- * Translations that are added to JS
- * (object Piwik_Transitions_Translations)
- */
- private static $jsTranslations = array(
- 'XOfY' => 'Transitions_XOutOfYVisits',
- 'XOfAllPageviews' => 'Transitions_XOfAllPageviews',
- 'NoDataForAction' => 'Transitions_NoDataForAction',
- 'NoDataForActionDetails' => 'Transitions_NoDataForActionDetails',
- 'NoDataForActionBack' => 'Transitions_ErrorBack',
- 'ShareOfAllPageviews' => 'Transitions_ShareOfAllPageviews',
- 'DateRange' => 'General_DateRange'
- );
-
- public static function getTranslation($key)
- {
- return Piwik_Translate(self::$metricTranslations[$key]);
- }
-
- /**
- * The main method of the plugin.
- * It is triggered from the Transitions data table action.
- */
- public function renderPopover()
- {
- $view = Piwik_View::factory('transitions');
- $view->translations = $this->getTranslations();
- echo $view->render();
- }
-
- public function getTranslations()
- {
- $translations = self::$metricTranslations + self::$jsTranslations;
- foreach ($translations as &$message) {
- $message = Piwik_Translate($message);
- }
- return $translations;
- }
-
+ /**
+ * Since the metric translations are taken from different plugins,
+ * it makes the rest of the code easier to read and maintain when we
+ * use this indirection to map between the metrics and the actual
+ * translation keys.
+ */
+ private static $metricTranslations = array(
+ 'pageviewsInline' => 'Transitions_PageviewsInline',
+ 'loopsInline' => 'Transitions_LoopsInline',
+ 'fromPreviousPages' => 'Transitions_FromPreviousPages',
+ 'fromPreviousPagesInline' => 'Transitions_FromPreviousPagesInline',
+ 'fromPreviousSiteSearches' => 'Transitions_FromPreviousSiteSearches',
+ 'fromPreviousSiteSearchesInline' => 'Transitions_FromPreviousSiteSearchesInline',
+ 'fromSearchEngines' => 'Transitions_FromSearchEngines',
+ 'fromSearchEnginesInline' => 'Transitions_FromSearchEnginesInline',
+ 'fromWebsites' => 'Transitions_FromWebsites',
+ 'fromWebsitesInline' => 'Transitions_FromWebsitesInline',
+ 'fromCampaigns' => 'Transitions_FromCampaigns',
+ 'fromCampaignsInline' => 'Transitions_FromCampaignsInline',
+ 'directEntries' => 'Transitions_DirectEntries',
+ 'directEntriesInline' => 'Referers_TypeDirectEntries',
+ 'toFollowingPages' => 'Transitions_ToFollowingPages',
+ 'toFollowingPagesInline' => 'Transitions_ToFollowingPagesInline',
+ 'toFollowingSiteSearches' => 'Transitions_ToFollowingSiteSearches',
+ 'toFollowingSiteSearchesInline' => 'Transitions_ToFollowingSiteSearchesInline',
+ 'downloads' => 'Actions_ColumnDownloads',
+ 'downloadsInline' => 'VisitsSummary_NbDownloadsDescription',
+ 'outlinks' => 'Actions_ColumnOutlinks',
+ 'outlinksInline' => 'VisitsSummary_NbOutlinksDescription',
+ 'exits' => 'General_ColumnExits',
+ 'exitsInline' => 'Transitions_ExitsInline',
+ 'bouncesInline' => 'Transitions_BouncesInline'
+ );
+
+ /**
+ * Translations that are added to JS
+ * (object Piwik_Transitions_Translations)
+ */
+ private static $jsTranslations = array(
+ 'XOfY' => 'Transitions_XOutOfYVisits',
+ 'XOfAllPageviews' => 'Transitions_XOfAllPageviews',
+ 'NoDataForAction' => 'Transitions_NoDataForAction',
+ 'NoDataForActionDetails' => 'Transitions_NoDataForActionDetails',
+ 'NoDataForActionBack' => 'Transitions_ErrorBack',
+ 'ShareOfAllPageviews' => 'Transitions_ShareOfAllPageviews',
+ 'DateRange' => 'General_DateRange'
+ );
+
+ public static function getTranslation($key)
+ {
+ return Piwik_Translate(self::$metricTranslations[$key]);
+ }
+
+ /**
+ * The main method of the plugin.
+ * It is triggered from the Transitions data table action.
+ */
+ public function renderPopover()
+ {
+ $view = Piwik_View::factory('transitions');
+ $view->translations = $this->getTranslations();
+ echo $view->render();
+ }
+
+ public function getTranslations()
+ {
+ $translations = self::$metricTranslations + self::$jsTranslations;
+ foreach ($translations as &$message) {
+ $message = Piwik_Translate($message);
+ }
+ return $translations;
+ }
+
}
diff --git a/plugins/Transitions/Transitions.php b/plugins/Transitions/Transitions.php
index 18b45bf61b..4adaf3f997 100644
--- a/plugins/Transitions/Transitions.php
+++ b/plugins/Transitions/Transitions.php
@@ -8,321 +8,299 @@
* @category Piwik_Plugins
* @package Piwik_Transitions
*/
-
+
/**
* @package Piwik_Transitions
*/
class Piwik_Transitions extends Piwik_Plugin
{
-
- private $limitBeforeGrouping = 5;
- private $totalTransitionsToFollowingActions = 0;
-
- private $returnNormalizedUrls = false;
-
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('Transitions_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'AssetManager.getJsFiles' => 'getJsFiles'
- );
- }
-
- public function getCssFiles($notification)
- {
- $cssFiles = &$notification->getNotificationObject();
- $cssFiles[] = 'plugins/Transitions/templates/transitions.css';
- }
-
- public function getJsFiles($notification)
- {
- $jsFiles = &$notification->getNotificationObject();
- $jsFiles[] = 'plugins/Transitions/templates/transitions.js';
- }
-
- /**
- * After calling this method, the query*()-Methods will return urls in their
- * normalized form (without the prefix reconstructed)
- */
- public function returnNormalizedUrls()
- {
- $this->returnNormalizedUrls = true;
- }
-
- /**
- * Get information about external referrers (i.e. search engines, websites & campaigns)
- *
- * @param $idaction
- * @param $actionType
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- * @param $limitBeforeGrouping
- * @return Piwik_DataTable
- */
- public function queryExternalReferrers($idaction, $actionType, $archiveProcessing,
- $limitBeforeGrouping = false)
- {
- $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
-
- // we generate a single column that contains the interesting data for each referrer.
- // the reason we cannot group by referer_* becomes clear when we look at search engine keywords.
- // referer_url contains the url from the search engine, referer_keyword the keyword we want to
- // group by. when we group by both, we don't get a single column for the keyword but instead
- // one column per keyword + search engine url. this way, we could not get the top keywords using
- // the ranking query.
- $dimension = 'referrer_data';
- $rankingQuery->addLabelColumn('referrer_data');
- $select = '
+
+ private $limitBeforeGrouping = 5;
+ private $totalTransitionsToFollowingActions = 0;
+
+ private $returnNormalizedUrls = false;
+
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('Transitions_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'AssetManager.getJsFiles' => 'getJsFiles'
+ );
+ }
+
+ public function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+ $cssFiles[] = 'plugins/Transitions/templates/transitions.css';
+ }
+
+ public function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+ $jsFiles[] = 'plugins/Transitions/templates/transitions.js';
+ }
+
+ /**
+ * After calling this method, the query*()-Methods will return urls in their
+ * normalized form (without the prefix reconstructed)
+ */
+ public function returnNormalizedUrls()
+ {
+ $this->returnNormalizedUrls = true;
+ }
+
+ /**
+ * Get information about external referrers (i.e. search engines, websites & campaigns)
+ *
+ * @param $idaction
+ * @param $actionType
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ * @param $limitBeforeGrouping
+ * @return Piwik_DataTable
+ */
+ public function queryExternalReferrers($idaction, $actionType, $archiveProcessing,
+ $limitBeforeGrouping = false)
+ {
+ $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
+
+ // we generate a single column that contains the interesting data for each referrer.
+ // the reason we cannot group by referer_* becomes clear when we look at search engine keywords.
+ // referer_url contains the url from the search engine, referer_keyword the keyword we want to
+ // group by. when we group by both, we don't get a single column for the keyword but instead
+ // one column per keyword + search engine url. this way, we could not get the top keywords using
+ // the ranking query.
+ $dimension = 'referrer_data';
+ $rankingQuery->addLabelColumn('referrer_data');
+ $select = '
CASE referer_type
- WHEN '.Piwik_Common::REFERER_TYPE_DIRECT_ENTRY.' THEN \'\'
- WHEN '.Piwik_Common::REFERER_TYPE_SEARCH_ENGINE.' THEN referer_keyword
- WHEN '.Piwik_Common::REFERER_TYPE_WEBSITE.' THEN referer_url
- WHEN '.Piwik_Common::REFERER_TYPE_CAMPAIGN.' THEN CONCAT(referer_name, \' \', referer_keyword)
+ WHEN ' . Piwik_Common::REFERER_TYPE_DIRECT_ENTRY . ' THEN \'\'
+ WHEN ' . Piwik_Common::REFERER_TYPE_SEARCH_ENGINE . ' THEN referer_keyword
+ WHEN ' . Piwik_Common::REFERER_TYPE_WEBSITE . ' THEN referer_url
+ WHEN ' . Piwik_Common::REFERER_TYPE_CAMPAIGN . ' THEN CONCAT(referer_name, \' \', referer_keyword)
END AS referrer_data,
referer_type';
-
- // get one limited group per referrer type
- $rankingQuery->partitionResultIntoMultipleGroups('referer_type', array(
- Piwik_Common::REFERER_TYPE_DIRECT_ENTRY,
- Piwik_Common::REFERER_TYPE_SEARCH_ENGINE,
- Piwik_Common::REFERER_TYPE_WEBSITE,
- Piwik_Common::REFERER_TYPE_CAMPAIGN
- ));
-
- $orderBy = '`'.Piwik_Archive::INDEX_NB_VISITS.'` DESC';
-
- $type = $this->getColumnTypeSuffix($actionType);
- $where = 'visit_entry_idaction_'.$type.' = '.intval($idaction);
-
- $metrics = array(Piwik_Archive::INDEX_NB_VISITS);
- $data = $archiveProcessing->queryVisitsByDimension($dimension, $where, $metrics, $orderBy,
- $rankingQuery, $select, $selectGeneratesLabelColumn = true);
-
- $referrerData = array();
- $referrerSubData = array();
-
- foreach ($data as $referrerType => &$subData)
- {
- $referrerData[$referrerType] = array(Piwik_Archive::INDEX_NB_VISITS => 0);
- if ($referrerType != Piwik_Common::REFERER_TYPE_DIRECT_ENTRY)
- {
- $referrerSubData[$referrerType] = array();
- }
-
- foreach ($subData as &$row)
- {
- if ($referrerType == Piwik_Common::REFERER_TYPE_SEARCH_ENGINE && empty($row['referrer_data']))
- {
- $row['referrer_data'] = Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED;
- }
-
- $referrerData[$referrerType][Piwik_Archive::INDEX_NB_VISITS] += $row[Piwik_Archive::INDEX_NB_VISITS];
-
- $label = $row['referrer_data'];
- if ($label)
- {
- $referrerSubData[$referrerType][$label] = array(
- Piwik_Archive::INDEX_NB_VISITS => $row[Piwik_Archive::INDEX_NB_VISITS]
- );
- }
- }
- }
-
- return $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($referrerSubData, $referrerData);
- }
-
- /**
- * Get information about internal referrers (previous pages & loops, i.e. page refreshes)
- *
- * @param $idaction
- * @param $actionType
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- * @param $limitBeforeGrouping
- * @return array(previousPages:Piwik_DataTable, loops:integer)
- */
- public function queryInternalReferrers($idaction, $actionType, $archiveProcessing,
- $limitBeforeGrouping = false)
- {
- $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
- $rankingQuery->addLabelColumn(array('name', 'url_prefix'));
- $rankingQuery->setColumnToMarkExcludedRows('is_self');
- $rankingQuery->partitionResultIntoMultipleGroups('action_partition', array(0, 1, 2));
-
- $type = $this->getColumnTypeSuffix($actionType);
- $mainActionType = Piwik_Tracker_Action::TYPE_ACTION_URL;
- $dimension = 'idaction_url_ref';
- $isTitle = $actionType == 'title';
- if ($isTitle)
- {
- $mainActionType = Piwik_Tracker_Action::TYPE_ACTION_NAME;
- $dimension = 'idaction_name_ref';
- }
-
- $addSelect = '
+
+ // get one limited group per referrer type
+ $rankingQuery->partitionResultIntoMultipleGroups('referer_type', array(
+ Piwik_Common::REFERER_TYPE_DIRECT_ENTRY,
+ Piwik_Common::REFERER_TYPE_SEARCH_ENGINE,
+ Piwik_Common::REFERER_TYPE_WEBSITE,
+ Piwik_Common::REFERER_TYPE_CAMPAIGN
+ ));
+
+ $orderBy = '`' . Piwik_Archive::INDEX_NB_VISITS . '` DESC';
+
+ $type = $this->getColumnTypeSuffix($actionType);
+ $where = 'visit_entry_idaction_' . $type . ' = ' . intval($idaction);
+
+ $metrics = array(Piwik_Archive::INDEX_NB_VISITS);
+ $data = $archiveProcessing->queryVisitsByDimension($dimension, $where, $metrics, $orderBy,
+ $rankingQuery, $select, $selectGeneratesLabelColumn = true);
+
+ $referrerData = array();
+ $referrerSubData = array();
+
+ foreach ($data as $referrerType => &$subData) {
+ $referrerData[$referrerType] = array(Piwik_Archive::INDEX_NB_VISITS => 0);
+ if ($referrerType != Piwik_Common::REFERER_TYPE_DIRECT_ENTRY) {
+ $referrerSubData[$referrerType] = array();
+ }
+
+ foreach ($subData as &$row) {
+ if ($referrerType == Piwik_Common::REFERER_TYPE_SEARCH_ENGINE && empty($row['referrer_data'])) {
+ $row['referrer_data'] = Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED;
+ }
+
+ $referrerData[$referrerType][Piwik_Archive::INDEX_NB_VISITS] += $row[Piwik_Archive::INDEX_NB_VISITS];
+
+ $label = $row['referrer_data'];
+ if ($label) {
+ $referrerSubData[$referrerType][$label] = array(
+ Piwik_Archive::INDEX_NB_VISITS => $row[Piwik_Archive::INDEX_NB_VISITS]
+ );
+ }
+ }
+ }
+
+ return $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($referrerSubData, $referrerData);
+ }
+
+ /**
+ * Get information about internal referrers (previous pages & loops, i.e. page refreshes)
+ *
+ * @param $idaction
+ * @param $actionType
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ * @param $limitBeforeGrouping
+ * @return array(previousPages:Piwik_DataTable, loops:integer)
+ */
+ public function queryInternalReferrers($idaction, $actionType, $archiveProcessing,
+ $limitBeforeGrouping = false)
+ {
+ $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
+ $rankingQuery->addLabelColumn(array('name', 'url_prefix'));
+ $rankingQuery->setColumnToMarkExcludedRows('is_self');
+ $rankingQuery->partitionResultIntoMultipleGroups('action_partition', array(0, 1, 2));
+
+ $type = $this->getColumnTypeSuffix($actionType);
+ $mainActionType = Piwik_Tracker_Action::TYPE_ACTION_URL;
+ $dimension = 'idaction_url_ref';
+ $isTitle = $actionType == 'title';
+ if ($isTitle) {
+ $mainActionType = Piwik_Tracker_Action::TYPE_ACTION_NAME;
+ $dimension = 'idaction_name_ref';
+ }
+
+ $addSelect = '
log_action.name, log_action.url_prefix,
- CASE WHEN log_link_visit_action.idaction_'.$type.'_ref = '.intval($idaction).' THEN 1 ELSE 0 END AS is_self,
+ CASE WHEN log_link_visit_action.idaction_' . $type . '_ref = ' . intval($idaction) . ' THEN 1 ELSE 0 END AS is_self,
CASE
- WHEN log_action.type = '.$mainActionType.' THEN 1
- WHEN log_action.type = '.Piwik_Tracker_Action::TYPE_SITE_SEARCH.' THEN 2
+ WHEN log_action.type = ' . $mainActionType . ' THEN 1
+ WHEN log_action.type = ' . Piwik_Tracker_Action::TYPE_SITE_SEARCH . ' THEN 2
ELSE 0
END AS action_partition';
-
- $where = '
- log_link_visit_action.idaction_'.$type.' = '.intval($idaction);
-
- if ($dimension == 'idaction_url_ref')
- {
- // site search referrers are logged with url_ref=NULL
- // when we find one, we have to join on name_ref
- $dimension = 'IF( idaction_url_ref IS NULL, idaction_name_ref, idaction_url_ref )';
- $joinLogActionOn = $dimension;
- }
- else
- {
- $joinLogActionOn = $dimension;
- $dimension = array($dimension);
- }
-
- $orderBy = '`'.Piwik_Archive::INDEX_NB_ACTIONS.'` DESC';
-
- $metrics = array(Piwik_Archive::INDEX_NB_ACTIONS);
- $data = $archiveProcessing->queryActionsByDimension($dimension, $where, $metrics, $orderBy,
- $rankingQuery, $joinLogActionOn, $addSelect);
-
- $loops = 0;
- $nbPageviews = 0;
- $previousPagesDataTable = new Piwik_DataTable;
- if (isset($data['result'][1]))
- {
- foreach ($data['result'][1] as &$page)
- {
- $nbActions = intval($page[Piwik_Archive::INDEX_NB_ACTIONS]);
- $previousPagesDataTable->addRow(new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => array(
- 'label' => $this->getPageLabel($page, $isTitle),
- Piwik_Archive::INDEX_NB_ACTIONS => $nbActions
- )
- )));
- $nbPageviews += $nbActions;
- }
- }
-
- $previousSearchesDataTable = new Piwik_DataTable;
- if (isset($data['result'][2]))
- {
- foreach ($data['result'][2] as &$search)
- {
- $nbActions = intval($search[Piwik_Archive::INDEX_NB_ACTIONS]);
- $previousSearchesDataTable->addRow(new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => array(
- 'label' => $search['name'],
- Piwik_Archive::INDEX_NB_ACTIONS => $nbActions
- )
- )));
- $nbPageviews += $nbActions;
- }
- }
-
- if (isset($data['result'][0]))
- {
- foreach ($data['result'][0] as &$referrer)
- {
- $nbPageviews += intval($referrer[Piwik_Archive::INDEX_NB_ACTIONS]);
- }
- }
-
- if (count($data['excludedFromLimit']))
- {
- $loops += intval($data['excludedFromLimit'][0][Piwik_Archive::INDEX_NB_ACTIONS]);
- $nbPageviews += $loops;
- }
-
- return array(
- 'pageviews' => $nbPageviews,
- 'previousPages' => $previousPagesDataTable,
- 'previousSiteSearches' => $previousSearchesDataTable,
- 'loops' => $loops
- );
- }
-
- private function getPageLabel(&$pageRecord, $isTitle)
- {
- if ($isTitle)
- {
- $label = $pageRecord['name'];
- if (empty($label))
- {
- $label = Piwik_Actions_ArchivingHelper::getUnknownActionName(
- Piwik_Tracker_Action::TYPE_ACTION_NAME);
- }
- return $label;
- }
- else if ($this->returnNormalizedUrls)
- {
- return $pageRecord['name'];
- }
- else
- {
- return Piwik_Tracker_Action::reconstructNormalizedUrl(
- $pageRecord['name'], $pageRecord['url_prefix']);
- }
- }
-
- /**
- * Get information about the following actions (following pages, site searches, outlinks, downloads)
- *
- * @param $idaction
- * @param $actionType
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- * @param $limitBeforeGrouping
- * @param $includeLoops
- * @return array(followingPages:Piwik_DataTable, outlinks:Piwik_DataTable, downloads:Piwik_DataTable)
- */
- public function queryFollowingActions($idaction, $actionType, Piwik_ArchiveProcessing_Day $archiveProcessing,
- $limitBeforeGrouping = false, $includeLoops = false)
- {
- $types = array();
-
- $isTitle = ($actionType == 'title');
- if (!$isTitle) {
- // specific setup for page urls
- $types[Piwik_Tracker_Action::TYPE_ACTION_URL] = 'followingPages';
- $dimension = 'IF( idaction_url IS NULL, idaction_name, idaction_url )';
- // site search referrers are logged with url=NULL
- // when we find one, we have to join on name
- $joinLogActionColumn = $dimension;
- $addSelect = 'log_action.name, log_action.url_prefix, log_action.type';
- } else {
- // specific setup for page titles:
- $types[Piwik_Tracker_Action::TYPE_ACTION_NAME] = 'followingPages';
- // join log_action on name and url and pick depending on url type
- // the table joined on url is log_action1
- $joinLogActionColumn = array('idaction_url', 'idaction_name');
- $dimension = '
+
+ $where = '
+ log_link_visit_action.idaction_' . $type . ' = ' . intval($idaction);
+
+ if ($dimension == 'idaction_url_ref') {
+ // site search referrers are logged with url_ref=NULL
+ // when we find one, we have to join on name_ref
+ $dimension = 'IF( idaction_url_ref IS NULL, idaction_name_ref, idaction_url_ref )';
+ $joinLogActionOn = $dimension;
+ } else {
+ $joinLogActionOn = $dimension;
+ $dimension = array($dimension);
+ }
+
+ $orderBy = '`' . Piwik_Archive::INDEX_NB_ACTIONS . '` DESC';
+
+ $metrics = array(Piwik_Archive::INDEX_NB_ACTIONS);
+ $data = $archiveProcessing->queryActionsByDimension($dimension, $where, $metrics, $orderBy,
+ $rankingQuery, $joinLogActionOn, $addSelect);
+
+ $loops = 0;
+ $nbPageviews = 0;
+ $previousPagesDataTable = new Piwik_DataTable;
+ if (isset($data['result'][1])) {
+ foreach ($data['result'][1] as &$page) {
+ $nbActions = intval($page[Piwik_Archive::INDEX_NB_ACTIONS]);
+ $previousPagesDataTable->addRow(new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => array(
+ 'label' => $this->getPageLabel($page, $isTitle),
+ Piwik_Archive::INDEX_NB_ACTIONS => $nbActions
+ )
+ )));
+ $nbPageviews += $nbActions;
+ }
+ }
+
+ $previousSearchesDataTable = new Piwik_DataTable;
+ if (isset($data['result'][2])) {
+ foreach ($data['result'][2] as &$search) {
+ $nbActions = intval($search[Piwik_Archive::INDEX_NB_ACTIONS]);
+ $previousSearchesDataTable->addRow(new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => array(
+ 'label' => $search['name'],
+ Piwik_Archive::INDEX_NB_ACTIONS => $nbActions
+ )
+ )));
+ $nbPageviews += $nbActions;
+ }
+ }
+
+ if (isset($data['result'][0])) {
+ foreach ($data['result'][0] as &$referrer) {
+ $nbPageviews += intval($referrer[Piwik_Archive::INDEX_NB_ACTIONS]);
+ }
+ }
+
+ if (count($data['excludedFromLimit'])) {
+ $loops += intval($data['excludedFromLimit'][0][Piwik_Archive::INDEX_NB_ACTIONS]);
+ $nbPageviews += $loops;
+ }
+
+ return array(
+ 'pageviews' => $nbPageviews,
+ 'previousPages' => $previousPagesDataTable,
+ 'previousSiteSearches' => $previousSearchesDataTable,
+ 'loops' => $loops
+ );
+ }
+
+ private function getPageLabel(&$pageRecord, $isTitle)
+ {
+ if ($isTitle) {
+ $label = $pageRecord['name'];
+ if (empty($label)) {
+ $label = Piwik_Actions_ArchivingHelper::getUnknownActionName(
+ Piwik_Tracker_Action::TYPE_ACTION_NAME);
+ }
+ return $label;
+ } else if ($this->returnNormalizedUrls) {
+ return $pageRecord['name'];
+ } else {
+ return Piwik_Tracker_Action::reconstructNormalizedUrl(
+ $pageRecord['name'], $pageRecord['url_prefix']);
+ }
+ }
+
+ /**
+ * Get information about the following actions (following pages, site searches, outlinks, downloads)
+ *
+ * @param $idaction
+ * @param $actionType
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ * @param $limitBeforeGrouping
+ * @param $includeLoops
+ * @return array(followingPages:Piwik_DataTable, outlinks:Piwik_DataTable, downloads:Piwik_DataTable)
+ */
+ public function queryFollowingActions($idaction, $actionType, Piwik_ArchiveProcessing_Day $archiveProcessing,
+ $limitBeforeGrouping = false, $includeLoops = false)
+ {
+ $types = array();
+
+ $isTitle = ($actionType == 'title');
+ if (!$isTitle) {
+ // specific setup for page urls
+ $types[Piwik_Tracker_Action::TYPE_ACTION_URL] = 'followingPages';
+ $dimension = 'IF( idaction_url IS NULL, idaction_name, idaction_url )';
+ // site search referrers are logged with url=NULL
+ // when we find one, we have to join on name
+ $joinLogActionColumn = $dimension;
+ $addSelect = 'log_action.name, log_action.url_prefix, log_action.type';
+ } else {
+ // specific setup for page titles:
+ $types[Piwik_Tracker_Action::TYPE_ACTION_NAME] = 'followingPages';
+ // join log_action on name and url and pick depending on url type
+ // the table joined on url is log_action1
+ $joinLogActionColumn = array('idaction_url', 'idaction_name');
+ $dimension = '
CASE
' /* following site search */ . '
WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.idaction
' /* following page view: use page title */ . '
- WHEN log_action1.type = '.Piwik_Tracker_Action::TYPE_ACTION_URL.' THEN log_action2.idaction
+ WHEN log_action1.type = ' . Piwik_Tracker_Action::TYPE_ACTION_URL . ' THEN log_action2.idaction
' /* following download or outlink: use url */ . '
ELSE log_action1.idaction
END
';
- $addSelect = '
+ $addSelect = '
CASE
' /* following site search */ . '
WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.name
' /* following page view: use page title */ . '
- WHEN log_action1.type = '.Piwik_Tracker_Action::TYPE_ACTION_URL.' THEN log_action2.name
+ WHEN log_action1.type = ' . Piwik_Tracker_Action::TYPE_ACTION_URL . ' THEN log_action2.name
' /* following download or outlink: use url */ . '
ELSE log_action1.name
END AS name,
@@ -330,76 +308,73 @@ class Piwik_Transitions extends Piwik_Plugin
' /* following site search */ . '
WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.type
' /* following page view: use page title */ . '
- WHEN log_action1.type = '.Piwik_Tracker_Action::TYPE_ACTION_URL.' THEN log_action2.type
+ WHEN log_action1.type = ' . Piwik_Tracker_Action::TYPE_ACTION_URL . ' THEN log_action2.type
' /* following download or outlink: use url */ . '
ELSE log_action1.type
END AS type,
NULL AS url_prefix
';
- }
-
- // these types are available for both titles and urls
- $types[Piwik_Tracker_Action::TYPE_SITE_SEARCH] = 'followingSiteSearches';
- $types[Piwik_Tracker_Action::TYPE_OUTLINK] = 'outlinks';
- $types[Piwik_Tracker_Action::TYPE_DOWNLOAD] = 'downloads';
-
- $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
- $rankingQuery->addLabelColumn(array('name', 'url_prefix'));
- $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($types));
-
- $type = $this->getColumnTypeSuffix($actionType);
- $where = 'log_link_visit_action.idaction_'.$type.'_ref = '.intval($idaction);
- if (!$includeLoops) {
- $where .= ' AND (log_link_visit_action.idaction_'.$type.' IS NULL OR '
- . 'log_link_visit_action.idaction_'.$type.' != '.intval($idaction).')';
- }
-
- $orderBy = '`'.Piwik_Archive::INDEX_NB_ACTIONS.'` DESC';
-
- $metrics = array(Piwik_Archive::INDEX_NB_ACTIONS);
- $data = $archiveProcessing->queryActionsByDimension($dimension, $where, $metrics, $orderBy,
- $rankingQuery, $joinLogActionColumn, $addSelect);
-
- $this->totalTransitionsToFollowingActions = 0;
- $dataTables = array();
- foreach ($types as $type => $recordName)
- {
- $dataTable = new Piwik_DataTable;
- if (isset($data[$type]))
- {
- foreach ($data[$type] as &$record)
- {
- $actions = intval($record[Piwik_Archive::INDEX_NB_ACTIONS]);
- $dataTable->addRow(new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => array(
- 'label' => $this->getPageLabel($record, $isTitle),
- Piwik_Archive::INDEX_NB_ACTIONS => $actions
- )
- )));
- $this->totalTransitionsToFollowingActions += $actions;
- }
- }
- $dataTables[$recordName] = $dataTable;
- }
-
- return $dataTables;
- }
-
- /**
- * Get the sum of all transitions to following actions (pages, outlinks, downloads).
- * Only works if queryFollowingActions() has been used directly before.
- */
- public function getTotalTransitionsToFollowingActions()
- {
- return $this->totalTransitionsToFollowingActions;
- }
-
- private function getColumnTypeSuffix($actionType)
- {
- if ($actionType == 'title') {
- return 'name';
- }
- return 'url';
- }
-
+ }
+
+ // these types are available for both titles and urls
+ $types[Piwik_Tracker_Action::TYPE_SITE_SEARCH] = 'followingSiteSearches';
+ $types[Piwik_Tracker_Action::TYPE_OUTLINK] = 'outlinks';
+ $types[Piwik_Tracker_Action::TYPE_DOWNLOAD] = 'downloads';
+
+ $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
+ $rankingQuery->addLabelColumn(array('name', 'url_prefix'));
+ $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($types));
+
+ $type = $this->getColumnTypeSuffix($actionType);
+ $where = 'log_link_visit_action.idaction_' . $type . '_ref = ' . intval($idaction);
+ if (!$includeLoops) {
+ $where .= ' AND (log_link_visit_action.idaction_' . $type . ' IS NULL OR '
+ . 'log_link_visit_action.idaction_' . $type . ' != ' . intval($idaction) . ')';
+ }
+
+ $orderBy = '`' . Piwik_Archive::INDEX_NB_ACTIONS . '` DESC';
+
+ $metrics = array(Piwik_Archive::INDEX_NB_ACTIONS);
+ $data = $archiveProcessing->queryActionsByDimension($dimension, $where, $metrics, $orderBy,
+ $rankingQuery, $joinLogActionColumn, $addSelect);
+
+ $this->totalTransitionsToFollowingActions = 0;
+ $dataTables = array();
+ foreach ($types as $type => $recordName) {
+ $dataTable = new Piwik_DataTable;
+ if (isset($data[$type])) {
+ foreach ($data[$type] as &$record) {
+ $actions = intval($record[Piwik_Archive::INDEX_NB_ACTIONS]);
+ $dataTable->addRow(new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => array(
+ 'label' => $this->getPageLabel($record, $isTitle),
+ Piwik_Archive::INDEX_NB_ACTIONS => $actions
+ )
+ )));
+ $this->totalTransitionsToFollowingActions += $actions;
+ }
+ }
+ $dataTables[$recordName] = $dataTable;
+ }
+
+ return $dataTables;
+ }
+
+ /**
+ * Get the sum of all transitions to following actions (pages, outlinks, downloads).
+ * Only works if queryFollowingActions() has been used directly before.
+ */
+ public function getTotalTransitionsToFollowingActions()
+ {
+ return $this->totalTransitionsToFollowingActions;
+ }
+
+ private function getColumnTypeSuffix($actionType)
+ {
+ if ($actionType == 'title') {
+ return 'name';
+ }
+ return 'url';
+ }
+
} \ No newline at end of file
diff --git a/plugins/Transitions/templates/transitions.css b/plugins/Transitions/templates/transitions.css
index 75ecacb825..b0e3b0278a 100644
--- a/plugins/Transitions/templates/transitions.css
+++ b/plugins/Transitions/templates/transitions.css
@@ -1,197 +1,198 @@
-
#Transitions_Container {
- position: relative;
- z-index: 1500;
- height: 550px;
- text-align: left;
- margin-left: 7px;
+ position: relative;
+ z-index: 1500;
+ height: 550px;
+ text-align: left;
+ margin-left: 7px;
}
.Transitions_Canvas_Container {
- position: absolute;
+ position: absolute;
}
#Transitions_Canvas_Background_Left {
- z-index: 1501;
+ z-index: 1501;
}
#Transitions_Canvas_Background_Right {
- z-index: 1502;
+ z-index: 1502;
}
#Transitions_Canvas_Left {
- z-index: 1503;
+ z-index: 1503;
}
#Transitions_Canvas_Right {
- z-index: 1504;
+ z-index: 1504;
}
#Transitions_Canvas_Loops {
- z-index: 1505;
+ z-index: 1505;
}
.Transitions_Text {
- color: black;
- font-size: 11px;
- line-height: 14px;
- position: absolute;
- background: rgba(0,0,0,0); /* without this, IE9 triggers hover only on the text, not the box */
- z-index: 1506;
- font-family: Arial, Helvetica, sans-serif;
- word-wrap: break-word;
- text-align: left;
- cursor: default;
+ color: black;
+ font-size: 11px;
+ line-height: 14px;
+ position: absolute;
+ background: rgba(0, 0, 0, 0); /* without this, IE9 triggers hover only on the text, not the box */
+ z-index: 1506;
+ font-family: Arial, Helvetica, sans-serif;
+ word-wrap: break-word;
+ text-align: left;
+ cursor: default;
}
#Transitions_CenterBox {
- margin: 27px 0 0 345px;
- width: 208px;
- height: 373px;
- background: #f7f7f7;
- border: 1px solid #a9a399;
- border-radius:10px;
- -moz-border-radius:10px;
- -webkit-border-radius:10px;
- -webkit-box-shadow: 0px 0px 9px 0px #999;
- -moz-box-shadow: 0px 0px 9px 0px #999;
- box-shadow: 0px 0px 9px 0px #999;
- z-index: 1507;
+ margin: 27px 0 0 345px;
+ width: 208px;
+ height: 373px;
+ background: #f7f7f7;
+ border: 1px solid #a9a399;
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+ -webkit-box-shadow: 0px 0px 9px 0px #999;
+ -moz-box-shadow: 0px 0px 9px 0px #999;
+ box-shadow: 0px 0px 9px 0px #999;
+ z-index: 1507;
}
#Transitions_CenterBox h2 {
- font-size: 12px;
- line-height: 16px;
- padding: 10px;
- border-bottom: 1px dotted #a9a399;
- font-weight: bold;
- overflow: hidden;
- color: #255792;
+ font-size: 12px;
+ line-height: 16px;
+ padding: 10px;
+ border-bottom: 1px dotted #a9a399;
+ font-weight: bold;
+ overflow: hidden;
+ color: #255792;
}
.Transitions_Pageviews {
- text-align: center;
+ text-align: center;
}
.Transitions_OutgoingTraffic {
- text-align: right;
+ text-align: right;
}
.Transitions_CenterBoxMetrics {
- padding: 15px 10px 0 10px;
- display: none;
- font-size: 12px;
+ padding: 15px 10px 0 10px;
+ display: none;
+ font-size: 12px;
}
.Transitions_CenterBoxMetrics table td {
- padding: 0 0 5px 0;
+ padding: 0 0 5px 0;
}
.Transitions_CenterBoxMetrics table td.Transitions_Percentage {
- padding-right: 6px;
- font-weight: bold;
+ padding-right: 6px;
+ font-weight: bold;
}
#Transitions_CenterBox h3 {
- font-weight: bold;
- font-size: 12px;
- margin: 15px 0 7px 0;
- padding: 0;
- color: #7E7363;
+ font-weight: bold;
+ font-size: 12px;
+ margin: 15px 0 7px 0;
+ padding: 0;
+ color: #7E7363;
}
#Transitions_Loops {
- margin: 445px 0 0 346px;
- width: 208px;
- text-align: center;
- line-height: 25px;
- font-size: 12px;
- display: none;
- z-index: 1506;
- cursor: default;
+ margin: 445px 0 0 346px;
+ width: 208px;
+ text-align: center;
+ line-height: 25px;
+ font-size: 12px;
+ display: none;
+ z-index: 1506;
+ cursor: default;
}
.Transitions_CenterBoxMetrics p {
- margin: 0 0 3px 0;
- padding: 0;
- cursor: default;
- font-size: 12px;
- line-height: 15px;
+ margin: 0 0 3px 0;
+ padding: 0;
+ cursor: default;
+ font-size: 12px;
+ line-height: 15px;
}
.Transitions_CenterBoxMetrics p.Transitions_Margin {
- margin-bottom: 11px;
+ margin-bottom: 11px;
}
.Transitions_CenterBoxMetrics .Transitions_Highlighted {
- color: #E87500;
+ color: #E87500;
}
span.Transitions_Metric {
- font-weight: bold;
- cursor: default;
+ font-weight: bold;
+ cursor: default;
}
.Transitions_Value0 {
- color: #666;
+ color: #666;
}
.Transitions_TitleOfOpenGroup {
- font-size: 12px;
- color: #E87500;
- font-weight: bold;
+ font-size: 12px;
+ color: #E87500;
+ font-weight: bold;
}
.Transitions_BoxTextLeft,
.Transitions_BoxTextRight {
- width: 165px;
- height: 42px;
- overflow: hidden;
+ width: 165px;
+ height: 42px;
+ overflow: hidden;
}
.Transitions_BoxTextRight {
- text-align: right;
+ text-align: right;
}
.Transitions_BoxTextLeft.Transitions_HasBackground,
.Transitions_BoxTextRight.Transitions_HasBackground {
- background-repeat: no-repeat;
- height: 18px;
+ background-repeat: no-repeat;
+ height: 18px;
}
.Transitions_BoxTextLeft.Transitions_HasBackground {
- background-position: 0 1px;
- width: 175px;
+ background-position: 0 1px;
+ width: 175px;
}
+
.Transitions_BoxTextLeft.Transitions_HasBackground span {
- display: block;
- padding-left: 16px;
+ display: block;
+ padding-left: 16px;
}
.Transitions_BoxTextRight.Transitions_HasBackground {
- background-position: right 1px;
+ background-position: right 1px;
}
+
.Transitions_BoxTextRight.Transitions_HasBackground span {
- display: block;
- padding-right: 17px;
+ display: block;
+ padding-right: 17px;
}
.Transitions_CurveTextLeft,
.Transitions_CurveTextRight {
- color: #255792;
- font-weight: bold;
- width: 34px;
- text-align: center;
- cursor: default;
+ color: #255792;
+ font-weight: bold;
+ width: 34px;
+ text-align: center;
+ cursor: default;
}
body .piwik-tooltip.Transitions_Tooltip_Small {
- font-size: 11px;
- padding: 3px 5px 3px 6px;
- background: white;
+ font-size: 11px;
+ padding: 3px 5px 3px 6px;
+ background: white;
}
.Transitions_SingleLine {
- font-size: 12px;
- height: 21px;
+ font-size: 12px;
+ height: 21px;
} \ No newline at end of file
diff --git a/plugins/Transitions/templates/transitions.js b/plugins/Transitions/templates/transitions.js
index 3f2d63db99..dc54e80d54 100644
--- a/plugins/Transitions/templates/transitions.js
+++ b/plugins/Transitions/templates/transitions.js
@@ -11,106 +11,106 @@
//
function DataTable_RowActions_Transitions(dataTable) {
- this.dataTable = dataTable;
- this.transitions = null;
+ this.dataTable = dataTable;
+ this.transitions = null;
}
DataTable_RowActions_Transitions.prototype = new DataTable_RowAction;
/** Static helper method to launch transitions from anywhere */
-DataTable_RowActions_Transitions.launchForUrl = function(url) {
- broadcast.propagateNewPopoverParameter('RowAction', 'Transitions:url:' + url);
+DataTable_RowActions_Transitions.launchForUrl = function (url) {
+ broadcast.propagateNewPopoverParameter('RowAction', 'Transitions:url:' + url);
};
-DataTable_RowActions_Transitions.isPageUrlReport = function(module, action) {
- return module == 'Actions' &&
- (action == 'getPageUrls' || action == 'getEntryPageUrls' || action == 'getExitPageUrls' || action == 'getPageUrlsFollowingSiteSearch');
+DataTable_RowActions_Transitions.isPageUrlReport = function (module, action) {
+ return module == 'Actions' &&
+ (action == 'getPageUrls' || action == 'getEntryPageUrls' || action == 'getExitPageUrls' || action == 'getPageUrlsFollowingSiteSearch');
};
-DataTable_RowActions_Transitions.isPageTitleReport = function(module, action) {
- return module == 'Actions' && (action == 'getPageTitles' || action == 'getPageTitlesFollowingSiteSearch');
+DataTable_RowActions_Transitions.isPageTitleReport = function (module, action) {
+ return module == 'Actions' && (action == 'getPageTitles' || action == 'getPageTitlesFollowingSiteSearch');
}
-DataTable_RowActions_Transitions.prototype.trigger = function(tr, e, subTableLabel) {
- var link = tr.find('> td:first > a').attr('href');
- link = $('<textarea>').html(link).val(); // remove html entities
-
- var module = this.dataTable.param.module;
- var action = this.dataTable.param.action;
- if (DataTable_RowActions_Transitions.isPageUrlReport(module, action)) {
- this.openPopover('url:' + link);
- } else if (DataTable_RowActions_Transitions.isPageTitleReport(module, action)) {
- DataTable_RowAction.prototype.trigger.apply(this, [tr, e, subTableLabel]);
- } else {
- alert('Transitions can\'t be used on this report.');
- }
+DataTable_RowActions_Transitions.prototype.trigger = function (tr, e, subTableLabel) {
+ var link = tr.find('> td:first > a').attr('href');
+ link = $('<textarea>').html(link).val(); // remove html entities
+
+ var module = this.dataTable.param.module;
+ var action = this.dataTable.param.action;
+ if (DataTable_RowActions_Transitions.isPageUrlReport(module, action)) {
+ this.openPopover('url:' + link);
+ } else if (DataTable_RowActions_Transitions.isPageTitleReport(module, action)) {
+ DataTable_RowAction.prototype.trigger.apply(this, [tr, e, subTableLabel]);
+ } else {
+ alert('Transitions can\'t be used on this report.');
+ }
};
-DataTable_RowAction.prototype.performAction = function(label, tr, e) {
- var separator = ' > '; // Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL
- var labelParts = label.split(separator);
- for (var i = 0; i < labelParts.length; i++) {
- labelParts[i] = $.trim(decodeURIComponent(labelParts[i]));
- }
- label = labelParts.join(piwik.config.action_url_category_delimiter);
- this.openPopover('title:' + label);
+DataTable_RowAction.prototype.performAction = function (label, tr, e) {
+ var separator = ' > '; // Piwik_API_DataTableManipulator_LabelFilter::SEPARATOR_RECURSIVE_LABEL
+ var labelParts = label.split(separator);
+ for (var i = 0; i < labelParts.length; i++) {
+ labelParts[i] = $.trim(decodeURIComponent(labelParts[i]));
+ }
+ label = labelParts.join(piwik.config.action_url_category_delimiter);
+ this.openPopover('title:' + label);
};
-DataTable_RowActions_Transitions.prototype.doOpenPopover = function(link) {
- var parts = link.split(':');
- if (parts.length < 2) {
- return;
- }
-
- var actionType = parts[0];
- parts.shift();
- var actionName = parts.join(':');
-
- if (this.transitions === null) {
- this.transitions = new Piwik_Transitions(actionType, actionName, this);
- } else {
- this.transitions.reset(actionType, actionName);
- }
- this.transitions.showPopover();
+DataTable_RowActions_Transitions.prototype.doOpenPopover = function (link) {
+ var parts = link.split(':');
+ if (parts.length < 2) {
+ return;
+ }
+
+ var actionType = parts[0];
+ parts.shift();
+ var actionName = parts.join(':');
+
+ if (this.transitions === null) {
+ this.transitions = new Piwik_Transitions(actionType, actionName, this);
+ } else {
+ this.transitions.reset(actionType, actionName);
+ }
+ this.transitions.showPopover();
};
DataTable_RowActions_Registry.register({
- name: 'Transitions',
-
- dataTableIcon: 'plugins/Transitions/templates/transitions_icon.png',
- dataTableIconHover: 'plugins/Transitions/templates/transitions_icon_hover.png',
-
- order: 20,
-
- dataTableIconTooltip: [
- _pk_translate('General_TransitionsRowActionTooltipTitle_js'),
- _pk_translate('General_TransitionsRowActionTooltip_js')
- ],
-
- createInstance: function(dataTable) {
- return new DataTable_RowActions_Transitions(dataTable);
- },
-
- isAvailableOnReport: function(dataTableParams) {
- return (
- DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action) ||
- DataTable_RowActions_Transitions.isPageTitleReport(dataTableParams.module, dataTableParams.action)
- );
- },
-
- isAvailableOnRow: function(dataTableParams, tr) {
- if (tr.attr('id')) {
- // not available on groups (i.e. folders)
- return false;
- }
- if (DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action)
- && !tr.find('> td:first span.label').parent().is('a')) {
- // not on page url without link (i.e. "Page URL not defined")
- return false;
- }
- return true;
- }
+ name: 'Transitions',
+
+ dataTableIcon: 'plugins/Transitions/templates/transitions_icon.png',
+ dataTableIconHover: 'plugins/Transitions/templates/transitions_icon_hover.png',
+
+ order: 20,
+
+ dataTableIconTooltip: [
+ _pk_translate('General_TransitionsRowActionTooltipTitle_js'),
+ _pk_translate('General_TransitionsRowActionTooltip_js')
+ ],
+
+ createInstance: function (dataTable) {
+ return new DataTable_RowActions_Transitions(dataTable);
+ },
+
+ isAvailableOnReport: function (dataTableParams) {
+ return (
+ DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action) ||
+ DataTable_RowActions_Transitions.isPageTitleReport(dataTableParams.module, dataTableParams.action)
+ );
+ },
+
+ isAvailableOnRow: function (dataTableParams, tr) {
+ if (tr.attr('id')) {
+ // not available on groups (i.e. folders)
+ return false;
+ }
+ if (DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action)
+ && !tr.find('> td:first span.label').parent().is('a')) {
+ // not on page url without link (i.e. "Page URL not defined")
+ return false;
+ }
+ return true;
+ }
});
@@ -120,587 +120,587 @@ DataTable_RowActions_Registry.register({
//
function Piwik_Transitions(actionType, actionName, rowAction) {
- this.reset(actionType, actionName);
- this.rowAction = rowAction;
+ this.reset(actionType, actionName);
+ this.rowAction = rowAction;
- this.ajax = new Piwik_Transitions_Ajax();
- this.model = new Piwik_Transitions_Model(this.ajax);
+ this.ajax = new Piwik_Transitions_Ajax();
+ this.model = new Piwik_Transitions_Model(this.ajax);
- this.leftGroups = ['previousPages', 'previousSiteSearches', 'searchEngines', 'websites', 'campaigns'];
- this.rightGroups = ['followingPages', 'followingSiteSearches', 'downloads', 'outlinks'];
+ this.leftGroups = ['previousPages', 'previousSiteSearches', 'searchEngines', 'websites', 'campaigns'];
+ this.rightGroups = ['followingPages', 'followingSiteSearches', 'downloads', 'outlinks'];
}
-Piwik_Transitions.prototype.reset = function(actionType, actionName) {
- this.actionType = actionType;
- this.actionName = actionName;
+Piwik_Transitions.prototype.reset = function (actionType, actionName) {
+ this.actionType = actionType;
+ this.actionName = actionName;
- this.popover = null;
- this.canvas = null;
- this.centerBox = null;
+ this.popover = null;
+ this.canvas = null;
+ this.centerBox = null;
- this.leftOpenGroup = 'previousPages';
- this.rightOpenGroup = 'followingPages';
+ this.leftOpenGroup = 'previousPages';
+ this.rightOpenGroup = 'followingPages';
- this.highlightedGroup = false;
- this.highlightedGroupSide = false;
- this.highlightedGroupCenterEl = false;
+ this.highlightedGroup = false;
+ this.highlightedGroupSide = false;
+ this.highlightedGroupCenterEl = false;
};
/** Open the popover */
-Piwik_Transitions.prototype.showPopover = function() {
- var self = this;
-
- this.popover = Piwik_Popover.showLoading('Transitions', self.actionName, 550);
- Piwik_Popover.addHelpButton('http://piwik.org/docs/transitions');
-
- var bothLoaded = function() {
- Piwik_Popover.setContent(Piwik_Transitions.popoverHtml);
-
- self.preparePopover();
- self.model.htmlLoaded();
-
- if (self.model.searchEnginesNbTransitions > 0 && self.model.websitesNbTransitions > 0
- + self.model.campaignsNbTransitions > 0) {
- self.canvas.narrowMode();
- }
-
- self.render();
- self.canvas.truncateVisibleBoxTexts();
- };
-
- // load the popover HTML (only done once)
- var callbackForHtml = false;
- if (typeof Piwik_Transitions.popoverHtml == 'undefined') {
- this.ajax.callTransitionsController('renderPopover', function(html) {
- Piwik_Transitions.popoverHtml = html;
- if (callbackForHtml !== false) {
- callbackForHtml();
- }
- });
- }
-
- // load the data
- self.model.loadData(self.actionType, self.actionName, function() {
- if (typeof Piwik_Transitions.popoverHtml == 'undefined') {
- // html not there yet
- callbackForHtml = bothLoaded;
- } else {
- // html already loaded
- bothLoaded();
- }
- });
+Piwik_Transitions.prototype.showPopover = function () {
+ var self = this;
+
+ this.popover = Piwik_Popover.showLoading('Transitions', self.actionName, 550);
+ Piwik_Popover.addHelpButton('http://piwik.org/docs/transitions');
+
+ var bothLoaded = function () {
+ Piwik_Popover.setContent(Piwik_Transitions.popoverHtml);
+
+ self.preparePopover();
+ self.model.htmlLoaded();
+
+ if (self.model.searchEnginesNbTransitions > 0 && self.model.websitesNbTransitions > 0
+ + self.model.campaignsNbTransitions > 0) {
+ self.canvas.narrowMode();
+ }
+
+ self.render();
+ self.canvas.truncateVisibleBoxTexts();
+ };
+
+ // load the popover HTML (only done once)
+ var callbackForHtml = false;
+ if (typeof Piwik_Transitions.popoverHtml == 'undefined') {
+ this.ajax.callTransitionsController('renderPopover', function (html) {
+ Piwik_Transitions.popoverHtml = html;
+ if (callbackForHtml !== false) {
+ callbackForHtml();
+ }
+ });
+ }
+
+ // load the data
+ self.model.loadData(self.actionType, self.actionName, function () {
+ if (typeof Piwik_Transitions.popoverHtml == 'undefined') {
+ // html not there yet
+ callbackForHtml = bothLoaded;
+ } else {
+ // html already loaded
+ bothLoaded();
+ }
+ });
};
/** Prepare the popover with the basic DOM to add data later. */
-Piwik_Transitions.prototype.preparePopover = function() {
- var self = this;
-
- var width = 900;
- var height = 550;
-
- var canvasBgLeft = self.prepareCanvas('Background_Left', width, height);
- var canvasBgRight = self.prepareCanvas('Background_Right', width, height);
- var canvasLeft = self.prepareCanvas('Left', width, height);
- var canvasRight = self.prepareCanvas('Right', width, height);
- var canvasLoops = self.prepareCanvas('Loops', width, height);
-
- self.canvas = new Piwik_Transitions_Canvas(canvasBgLeft, canvasBgRight, canvasLeft, canvasRight,
- canvasLoops, width, height);
-
- self.centerBox = self.popover.find('#Transitions_CenterBox');
-
- var title = self.actionName;
- if (self.actionType == 'url') {
- title = Piwik_Transitions_Util.shortenUrl(title, true);
- }
- title = self.centerBox.find('h2')
- .addClass('Transitions_ApplyTextAndTruncate')
- .data('text', title)
- .data('maxLines', 3);
-
- if (self.actionType == 'url') {
- title.click(function() {
- self.openExternalUrl(self.actionName);
- }).css('cursor', 'pointer');
- }
-
- title.add(self.popover.find('p.Transitions_Pageviews')).hover(function() {
- var totalNbPageviews = self.model.getTotalNbPageviews();
- if (totalNbPageviews > 0) {
- var share = Math.round(self.model.pageviews / totalNbPageviews * 1000) / 10;
-
- var text = Piwik_Transitions_Translations.ShareOfAllPageviews;
- text = text.replace(/%s/, self.model.pageviews).replace(/%s/, share + '%');
- text += '<br /><i>' + Piwik_Transitions_Translations.DateRange + ' ' + self.model.date + '</i>';
-
- var title = '<span class="tip-title">' + piwikHelper.addBreakpointsToUrl(self.actionName) +
- '</span><br />';
-
- Piwik_Tooltip.show(title + text, 'Transitions_Tooltip_Small', 300);
- }
- }, function() {
- Piwik_Tooltip.hide();
- });
+Piwik_Transitions.prototype.preparePopover = function () {
+ var self = this;
+
+ var width = 900;
+ var height = 550;
+
+ var canvasBgLeft = self.prepareCanvas('Background_Left', width, height);
+ var canvasBgRight = self.prepareCanvas('Background_Right', width, height);
+ var canvasLeft = self.prepareCanvas('Left', width, height);
+ var canvasRight = self.prepareCanvas('Right', width, height);
+ var canvasLoops = self.prepareCanvas('Loops', width, height);
+
+ self.canvas = new Piwik_Transitions_Canvas(canvasBgLeft, canvasBgRight, canvasLeft, canvasRight,
+ canvasLoops, width, height);
+
+ self.centerBox = self.popover.find('#Transitions_CenterBox');
+
+ var title = self.actionName;
+ if (self.actionType == 'url') {
+ title = Piwik_Transitions_Util.shortenUrl(title, true);
+ }
+ title = self.centerBox.find('h2')
+ .addClass('Transitions_ApplyTextAndTruncate')
+ .data('text', title)
+ .data('maxLines', 3);
+
+ if (self.actionType == 'url') {
+ title.click(function () {
+ self.openExternalUrl(self.actionName);
+ }).css('cursor', 'pointer');
+ }
+
+ title.add(self.popover.find('p.Transitions_Pageviews')).hover(function () {
+ var totalNbPageviews = self.model.getTotalNbPageviews();
+ if (totalNbPageviews > 0) {
+ var share = Math.round(self.model.pageviews / totalNbPageviews * 1000) / 10;
+
+ var text = Piwik_Transitions_Translations.ShareOfAllPageviews;
+ text = text.replace(/%s/, self.model.pageviews).replace(/%s/, share + '%');
+ text += '<br /><i>' + Piwik_Transitions_Translations.DateRange + ' ' + self.model.date + '</i>';
+
+ var title = '<span class="tip-title">' + piwikHelper.addBreakpointsToUrl(self.actionName) +
+ '</span><br />';
+
+ Piwik_Tooltip.show(title + text, 'Transitions_Tooltip_Small', 300);
+ }
+ }, function () {
+ Piwik_Tooltip.hide();
+ });
};
-Piwik_Transitions.prototype.prepareCanvas = function(canvasId, width, height) {
- canvasId = 'Transitions_Canvas_' + canvasId;
- var div = $('#' + canvasId).width(width).height(height);
- var canvas;
-
- if (typeof Piwik_Transitions.canvasCache == 'undefined'
- || typeof window.G_vmlCanvasManager != "undefined") {
- // no recycling for excanvas because they are disposed properly anyway and
- // recycling doesn't work this way in IE8
- Piwik_Transitions.canvasCache = {};
- }
-
- if (typeof Piwik_Transitions.canvasCache[canvasId] == 'undefined') {
- Piwik_Transitions.canvasCache[canvasId] = document.createElement('canvas');
- canvas = Piwik_Transitions.canvasCache[canvasId];
- canvas.width = width;
- canvas.height = height;
- } else {
- canvas = Piwik_Transitions.canvasCache[canvasId];
- canvas.getContext('2d').clearRect(0, 0, width, height);
- }
-
- div.append(canvas);
- return canvas;
+Piwik_Transitions.prototype.prepareCanvas = function (canvasId, width, height) {
+ canvasId = 'Transitions_Canvas_' + canvasId;
+ var div = $('#' + canvasId).width(width).height(height);
+ var canvas;
+
+ if (typeof Piwik_Transitions.canvasCache == 'undefined'
+ || typeof window.G_vmlCanvasManager != "undefined") {
+ // no recycling for excanvas because they are disposed properly anyway and
+ // recycling doesn't work this way in IE8
+ Piwik_Transitions.canvasCache = {};
+ }
+
+ if (typeof Piwik_Transitions.canvasCache[canvasId] == 'undefined') {
+ Piwik_Transitions.canvasCache[canvasId] = document.createElement('canvas');
+ canvas = Piwik_Transitions.canvasCache[canvasId];
+ canvas.width = width;
+ canvas.height = height;
+ } else {
+ canvas = Piwik_Transitions.canvasCache[canvasId];
+ canvas.getContext('2d').clearRect(0, 0, width, height);
+ }
+
+ div.append(canvas);
+ return canvas;
};
/** Render the popover content */
-Piwik_Transitions.prototype.render = function() {
- this.renderCenterBox();
+Piwik_Transitions.prototype.render = function () {
+ this.renderCenterBox();
- this.renderLeftSide();
- this.renderRightSide();
+ this.renderLeftSide();
+ this.renderRightSide();
- this.renderLoops();
+ this.renderLoops();
};
/** Render left side: referrer groups & direct entries */
-Piwik_Transitions.prototype.renderLeftSide = function(onlyBg) {
- this.renderGroups(this.leftGroups, this.leftOpenGroup, 'left', onlyBg);
- this.renderEntries(onlyBg);
+Piwik_Transitions.prototype.renderLeftSide = function (onlyBg) {
+ this.renderGroups(this.leftGroups, this.leftOpenGroup, 'left', onlyBg);
+ this.renderEntries(onlyBg);
- this.reRenderIfNeededToCenter('left', onlyBg);
+ this.reRenderIfNeededToCenter('left', onlyBg);
};
/** Render right side: following pages & exits */
-Piwik_Transitions.prototype.renderRightSide = function(onlyBg) {
- this.renderGroups(this.rightGroups, this.rightOpenGroup, 'right', onlyBg);
- this.renderExits(onlyBg);
+Piwik_Transitions.prototype.renderRightSide = function (onlyBg) {
+ this.renderGroups(this.rightGroups, this.rightOpenGroup, 'right', onlyBg);
+ this.renderExits(onlyBg);
- this.reRenderIfNeededToCenter('right', onlyBg);
+ this.reRenderIfNeededToCenter('right', onlyBg);
};
/** Helper method to render open and closed groups for both sides */
-Piwik_Transitions.prototype.renderGroups = function(groups, openGroup, side, onlyBg) {
- for (var i = 0; i < groups.length; i++) {
- var groupName = groups[i];
- if (groupName == openGroup) {
- if (i != 0) {
- var spacing = this.canvas.isNarrowMode() ? 7 : 13;
- this.canvas.addBoxSpacing(spacing, side);
- }
- this.renderOpenGroup(groupName, side, onlyBg);
- } else {
- this.renderClosedGroup(groupName, side, onlyBg);
- }
- }
-
- this.canvas.addBoxSpacing(13, side);
+Piwik_Transitions.prototype.renderGroups = function (groups, openGroup, side, onlyBg) {
+ for (var i = 0; i < groups.length; i++) {
+ var groupName = groups[i];
+ if (groupName == openGroup) {
+ if (i != 0) {
+ var spacing = this.canvas.isNarrowMode() ? 7 : 13;
+ this.canvas.addBoxSpacing(spacing, side);
+ }
+ this.renderOpenGroup(groupName, side, onlyBg);
+ } else {
+ this.renderClosedGroup(groupName, side, onlyBg);
+ }
+ }
+
+ this.canvas.addBoxSpacing(13, side);
};
/**
* If one side doesn't have much information, it doesn't look good to start from y=0.
* In this case, add some spacing on top and redraw.
*/
-Piwik_Transitions.prototype.reRenderIfNeededToCenter = function(side, onlyBg) {
- var height = (side == 'left' ? this.canvas.leftBoxPositionY : this.canvas.rightBoxPositionY) - 20;
- if (height < 460 && !this.reRendering) {
- var yOffset = (460 - height) / 2;
- this.canvas.clearSide(side, onlyBg);
- this.canvas.addBoxSpacing(yOffset, side);
- this.reRendering = true;
- side == 'left' ? this.renderLeftSide(onlyBg) : this.renderRightSide(onlyBg);
- this.reRendering = false;
- }
+Piwik_Transitions.prototype.reRenderIfNeededToCenter = function (side, onlyBg) {
+ var height = (side == 'left' ? this.canvas.leftBoxPositionY : this.canvas.rightBoxPositionY) - 20;
+ if (height < 460 && !this.reRendering) {
+ var yOffset = (460 - height) / 2;
+ this.canvas.clearSide(side, onlyBg);
+ this.canvas.addBoxSpacing(yOffset, side);
+ this.reRendering = true;
+ side == 'left' ? this.renderLeftSide(onlyBg) : this.renderRightSide(onlyBg);
+ this.reRendering = false;
+ }
};
/** Render the center box with the main metrics */
-Piwik_Transitions.prototype.renderCenterBox = function() {
- var box = this.centerBox;
-
- Piwik_Transitions_Util.replacePlaceholderInHtml(
- box.find('.Transitions_Pageviews'), this.model.pageviews);
-
- var self = this;
- var showMetric = function(cssClass, modelProperty, highlightCurveOnSide, groupCanBeExpanded) {
- var el = box.find('.Transitions_' + cssClass);
- Piwik_Transitions_Util.replacePlaceholderInHtml(el, self.model[modelProperty]);
-
- if (self.model[modelProperty] == 0) {
- el.addClass('Transitions_Value0');
- } else {
- self.addTooltipShowingPercentageOfAllPageviews(el, modelProperty);
- var groupName = cssClass.charAt(0).toLowerCase() + cssClass.substr(1);
- el.hover(function() {
- self.highlightGroup(groupName, highlightCurveOnSide);
- }, function() {
- self.unHighlightGroup(groupName, highlightCurveOnSide);
- });
- if (groupCanBeExpanded) {
- el.click(function() {
- self.openGroup(highlightCurveOnSide, groupName);
- }).css('cursor', 'pointer');
- }
- }
- };
-
- showMetric('DirectEntries', 'directEntries', 'left', false);
- showMetric('PreviousSiteSearches', 'previousSiteSearchesNbTransitions', 'left', true);
- showMetric('PreviousPages', 'previousPagesNbTransitions', 'left', true);
- showMetric('SearchEngines', 'searchEnginesNbTransitions', 'left', true);
- showMetric('Websites', 'websitesNbTransitions', 'left', true);
- showMetric('Campaigns', 'campaignsNbTransitions', 'left', true);
-
- showMetric('FollowingPages', 'followingPagesNbTransitions', 'right', true);
- showMetric('FollowingSiteSearches', 'followingSiteSearchesNbTransitions', 'right', true);
- showMetric('Outlinks', 'outlinksNbTransitions', 'right', true);
- showMetric('Downloads', 'downloadsNbTransitions', 'right', true);
- showMetric('Exits', 'exits', 'right', false);
-
- box.find('.Transitions_CenterBoxMetrics').show();
+Piwik_Transitions.prototype.renderCenterBox = function () {
+ var box = this.centerBox;
+
+ Piwik_Transitions_Util.replacePlaceholderInHtml(
+ box.find('.Transitions_Pageviews'), this.model.pageviews);
+
+ var self = this;
+ var showMetric = function (cssClass, modelProperty, highlightCurveOnSide, groupCanBeExpanded) {
+ var el = box.find('.Transitions_' + cssClass);
+ Piwik_Transitions_Util.replacePlaceholderInHtml(el, self.model[modelProperty]);
+
+ if (self.model[modelProperty] == 0) {
+ el.addClass('Transitions_Value0');
+ } else {
+ self.addTooltipShowingPercentageOfAllPageviews(el, modelProperty);
+ var groupName = cssClass.charAt(0).toLowerCase() + cssClass.substr(1);
+ el.hover(function () {
+ self.highlightGroup(groupName, highlightCurveOnSide);
+ }, function () {
+ self.unHighlightGroup(groupName, highlightCurveOnSide);
+ });
+ if (groupCanBeExpanded) {
+ el.click(function () {
+ self.openGroup(highlightCurveOnSide, groupName);
+ }).css('cursor', 'pointer');
+ }
+ }
+ };
+
+ showMetric('DirectEntries', 'directEntries', 'left', false);
+ showMetric('PreviousSiteSearches', 'previousSiteSearchesNbTransitions', 'left', true);
+ showMetric('PreviousPages', 'previousPagesNbTransitions', 'left', true);
+ showMetric('SearchEngines', 'searchEnginesNbTransitions', 'left', true);
+ showMetric('Websites', 'websitesNbTransitions', 'left', true);
+ showMetric('Campaigns', 'campaignsNbTransitions', 'left', true);
+
+ showMetric('FollowingPages', 'followingPagesNbTransitions', 'right', true);
+ showMetric('FollowingSiteSearches', 'followingSiteSearchesNbTransitions', 'right', true);
+ showMetric('Outlinks', 'outlinksNbTransitions', 'right', true);
+ showMetric('Downloads', 'downloadsNbTransitions', 'right', true);
+ showMetric('Exits', 'exits', 'right', false);
+
+ box.find('.Transitions_CenterBoxMetrics').show();
};
-Piwik_Transitions.prototype.addTooltipShowingPercentageOfAllPageviews = function(element, metric) {
- var self = this;
- element.hover(function() {
- var tip = Piwik_Transitions_Translations.XOfAllPageviews;
- var percentage = self.model.getPercentage(metric, true);
- tip = tip.replace(/%s/, '<b>' + percentage + '</b>');
- Piwik_Tooltip.show(tip, 'Transitions_Tooltip_Small');
- }, function() {
- Piwik_Tooltip.hide();
- });
+Piwik_Transitions.prototype.addTooltipShowingPercentageOfAllPageviews = function (element, metric) {
+ var self = this;
+ element.hover(function () {
+ var tip = Piwik_Transitions_Translations.XOfAllPageviews;
+ var percentage = self.model.getPercentage(metric, true);
+ tip = tip.replace(/%s/, '<b>' + percentage + '</b>');
+ Piwik_Tooltip.show(tip, 'Transitions_Tooltip_Small');
+ }, function () {
+ Piwik_Tooltip.hide();
+ });
};
/** Render the loops (i.e. page reloads) */
-Piwik_Transitions.prototype.renderLoops = function() {
- if (this.model.loops == 0) {
- return;
- }
+Piwik_Transitions.prototype.renderLoops = function () {
+ if (this.model.loops == 0) {
+ return;
+ }
- var loops = this.popover.find('#Transitions_Loops').show();
- Piwik_Transitions_Util.replacePlaceholderInHtml(loops, this.model.loops);
+ var loops = this.popover.find('#Transitions_Loops').show();
+ Piwik_Transitions_Util.replacePlaceholderInHtml(loops, this.model.loops);
- this.addTooltipShowingPercentageOfAllPageviews(loops, 'loops');
+ this.addTooltipShowingPercentageOfAllPageviews(loops, 'loops');
- this.canvas.renderLoops(this.model.getPercentage('loops'));
+ this.canvas.renderLoops(this.model.getPercentage('loops'));
};
-Piwik_Transitions.prototype.renderEntries = function(onlyBg) {
- if (this.model.directEntries > 0) {
- var self = this;
- var gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#BACFE8', 'left');
- if (this.highlightedGroup == 'directEntries') {
- gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#FAD293', 'left');
- }
- this.canvas.renderBox({
- side: 'left',
- onlyBg: onlyBg,
- share: this.model.getPercentage('directEntries'),
- gradient: gradient,
- boxText: Piwik_Transitions_Translations.directEntries,
- boxTextNumLines: 1,
- boxTextCssClass: 'SingleLine',
- smallBox: true,
- onMouseOver: function() {
- self.highlightGroup('directEntries', 'left');
- },
- onMouseOut: function() {
- self.unHighlightGroup('directEntries', 'left');
- }
- });
- this.canvas.addBoxSpacing(20, 'left');
- }
+Piwik_Transitions.prototype.renderEntries = function (onlyBg) {
+ if (this.model.directEntries > 0) {
+ var self = this;
+ var gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#BACFE8', 'left');
+ if (this.highlightedGroup == 'directEntries') {
+ gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#FAD293', 'left');
+ }
+ this.canvas.renderBox({
+ side: 'left',
+ onlyBg: onlyBg,
+ share: this.model.getPercentage('directEntries'),
+ gradient: gradient,
+ boxText: Piwik_Transitions_Translations.directEntries,
+ boxTextNumLines: 1,
+ boxTextCssClass: 'SingleLine',
+ smallBox: true,
+ onMouseOver: function () {
+ self.highlightGroup('directEntries', 'left');
+ },
+ onMouseOut: function () {
+ self.unHighlightGroup('directEntries', 'left');
+ }
+ });
+ this.canvas.addBoxSpacing(20, 'left');
+ }
};
-Piwik_Transitions.prototype.renderExits = function(onlyBg) {
- if (this.model.exits > 0) {
- var self = this;
- var gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#BACFE8', 'right');
- if (this.highlightedGroup == 'exits') {
- gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#FAD293', 'right');
- }
- this.canvas.renderBox({
- side: 'right',
- onlyBg: onlyBg,
- share: this.model.getPercentage('exits'),
- gradient: gradient,
- boxText: Piwik_Transitions_Translations.exits,
- boxTextNumLines: 1,
- boxTextCssClass: 'SingleLine',
- smallBox: true,
- onMouseOver: function() {
- self.highlightGroup('exits', 'right');
- },
- onMouseOut: function() {
- self.unHighlightGroup('exits', 'right');
- }
- });
- this.canvas.addBoxSpacing(20, 'right');
- }
+Piwik_Transitions.prototype.renderExits = function (onlyBg) {
+ if (this.model.exits > 0) {
+ var self = this;
+ var gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#BACFE8', 'right');
+ if (this.highlightedGroup == 'exits') {
+ gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#FAD293', 'right');
+ }
+ this.canvas.renderBox({
+ side: 'right',
+ onlyBg: onlyBg,
+ share: this.model.getPercentage('exits'),
+ gradient: gradient,
+ boxText: Piwik_Transitions_Translations.exits,
+ boxTextNumLines: 1,
+ boxTextCssClass: 'SingleLine',
+ smallBox: true,
+ onMouseOver: function () {
+ self.highlightGroup('exits', 'right');
+ },
+ onMouseOut: function () {
+ self.unHighlightGroup('exits', 'right');
+ }
+ });
+ this.canvas.addBoxSpacing(20, 'right');
+ }
};
/** Render the open group with the detailed data */
-Piwik_Transitions.prototype.renderOpenGroup = function(groupName, side, onlyBg) {
- var self = this;
-
- // get data from the model
- var nbTransitionsVarName = groupName + 'NbTransitions';
- var nbTransitions = self.model[nbTransitionsVarName];
- if (nbTransitions == 0) {
- return;
- }
-
- var totalShare = this.model.getPercentage(nbTransitionsVarName);
- var details = self.model.getDetailsForGroup(groupName);
-
- // prepare gradients
- var gradientItems = this.canvas.createHorizontalGradient('#E3DFD1', '#E8E4D5', side);
- var gradientOthers = this.canvas.createHorizontalGradient('#F5F3EB', '#E8E4D5', side);
- var gradientBackground = this.canvas.createHorizontalGradient('#FFFFFF', '#BACFE8', side);
- if (groupName == this.highlightedGroup) {
- gradientBackground = this.canvas.createHorizontalGradient('#FFFFFF', '#FAD293', side);
- }
-
- // remember current offsets to reset them later for drawing the background
- var boxPositionBefore, curvePositionBefore;
- if (side == 'left') {
- boxPositionBefore = this.canvas.leftBoxPositionY;
- curvePositionBefore = this.canvas.leftCurvePositionY;
- } else {
- boxPositionBefore = this.canvas.rightBoxPositionY;
- curvePositionBefore = this.canvas.rightCurvePositionY;
- }
-
- // headline of the open group
- var titleX, titleClass;
- if (side == 'left') {
- titleX = this.canvas.leftBoxBeginX + 10;
- titleClass = 'BoxTextLeft';
- } else {
- titleX = this.canvas.rightBoxBeginX - 1;
- titleClass = 'BoxTextRight';
- }
- if (!onlyBg) {
- var groupTitle = self.model.getGroupTitle(groupName);
- var titleEl = this.canvas.renderText(groupTitle, titleX, boxPositionBefore + 11,
- [titleClass, 'TitleOfOpenGroup']);
- titleEl.hover(function() {
- self.highlightGroup(groupName, side);
- }, function() {
- self.unHighlightGroup(groupName, side);
- });
- }
- this.canvas.addBoxSpacing(34, side);
-
- // draw detail boxes
- for (var i = 0; i < details.length; i++) {
- var data = details[i];
- var label = (typeof data.url != 'undefined' ? data.url : data.label);
- label = (typeof label != 'undefined' && label !== null ? label : '');
- var isOthers = (label == 'Others');
- var onClick = false;
- if (!isOthers && (groupName == 'previousPages' || groupName == 'followingPages')) {
- onClick = (function(url) {
- return function() {
- self.reloadPopover(url);
- };
- })(label);
- } else if (!isOthers && (groupName == 'outlinks' || groupName == 'websites' || groupName == 'downloads')) {
- onClick = (function(url) {
- return function() {
- self.openExternalUrl(url);
- };
- })(label);
- }
-
- var tooltip = Piwik_Transitions_Translations.XOfY;
- tooltip = '<b>' + tooltip.replace(/%s/, data.referrals + '</b>').replace(/%s/, nbTransitions);
- tooltip = this.model.getShareInGroupTooltip(tooltip, groupName);
-
- var fullLabel = label;
- var shortened = false;
- if ((this.actionType == 'url' && (groupName == 'previousPages' || groupName == 'followingPages'))
- || groupName == 'downloads') {
- // remove http + www + domain for internal URLs
- label = Piwik_Transitions_Util.shortenUrl(label, true);
- shortened = true;
- } else if (groupName == 'outlinks' || groupName == 'websites') {
- // remove http + www for external URLs
- label = Piwik_Transitions_Util.shortenUrl(label);
- shortened = true;
- }
-
- this.canvas.renderBox({
- side: side,
- onlyBg: onlyBg,
- share: data.percentage / 100 * totalShare,
- gradient: isOthers ? gradientOthers : gradientItems,
- boxText: label,
- boxTextTooltip: isOthers || !shortened ? false : fullLabel,
- boxTextNumLines: 3,
- curveText: data.percentage + '%',
- curveTextTooltip: tooltip,
- onClick: onClick
- });
- }
-
- // draw background
- var boxPositionAfter, curvePositionAfter;
- if (side == 'left') {
- boxPositionAfter = this.canvas.leftBoxPositionY;
- curvePositionAfter = this.canvas.leftCurvePositionY;
- this.canvas.leftBoxPositionY = boxPositionBefore;
- this.canvas.leftCurvePositionY = curvePositionBefore;
- } else {
- boxPositionAfter = this.canvas.rightBoxPositionY;
- curvePositionAfter = this.canvas.rightCurvePositionY;
- this.canvas.rightBoxPositionY = boxPositionBefore;
- this.canvas.rightCurvePositionY = curvePositionBefore;
- }
-
- this.canvas.renderBox({
- side: side,
- boxHeight: boxPositionAfter - boxPositionBefore - this.canvas.boxSpacing - 2,
- curveHeight: curvePositionAfter - curvePositionBefore - this.canvas.curveSpacing,
- gradient: gradientBackground,
- bgCanvas: true
- });
-
- var spacing = this.canvas.isNarrowMode() ? 8 : 15;
- this.canvas.addBoxSpacing(spacing, side);
+Piwik_Transitions.prototype.renderOpenGroup = function (groupName, side, onlyBg) {
+ var self = this;
+
+ // get data from the model
+ var nbTransitionsVarName = groupName + 'NbTransitions';
+ var nbTransitions = self.model[nbTransitionsVarName];
+ if (nbTransitions == 0) {
+ return;
+ }
+
+ var totalShare = this.model.getPercentage(nbTransitionsVarName);
+ var details = self.model.getDetailsForGroup(groupName);
+
+ // prepare gradients
+ var gradientItems = this.canvas.createHorizontalGradient('#E3DFD1', '#E8E4D5', side);
+ var gradientOthers = this.canvas.createHorizontalGradient('#F5F3EB', '#E8E4D5', side);
+ var gradientBackground = this.canvas.createHorizontalGradient('#FFFFFF', '#BACFE8', side);
+ if (groupName == this.highlightedGroup) {
+ gradientBackground = this.canvas.createHorizontalGradient('#FFFFFF', '#FAD293', side);
+ }
+
+ // remember current offsets to reset them later for drawing the background
+ var boxPositionBefore, curvePositionBefore;
+ if (side == 'left') {
+ boxPositionBefore = this.canvas.leftBoxPositionY;
+ curvePositionBefore = this.canvas.leftCurvePositionY;
+ } else {
+ boxPositionBefore = this.canvas.rightBoxPositionY;
+ curvePositionBefore = this.canvas.rightCurvePositionY;
+ }
+
+ // headline of the open group
+ var titleX, titleClass;
+ if (side == 'left') {
+ titleX = this.canvas.leftBoxBeginX + 10;
+ titleClass = 'BoxTextLeft';
+ } else {
+ titleX = this.canvas.rightBoxBeginX - 1;
+ titleClass = 'BoxTextRight';
+ }
+ if (!onlyBg) {
+ var groupTitle = self.model.getGroupTitle(groupName);
+ var titleEl = this.canvas.renderText(groupTitle, titleX, boxPositionBefore + 11,
+ [titleClass, 'TitleOfOpenGroup']);
+ titleEl.hover(function () {
+ self.highlightGroup(groupName, side);
+ }, function () {
+ self.unHighlightGroup(groupName, side);
+ });
+ }
+ this.canvas.addBoxSpacing(34, side);
+
+ // draw detail boxes
+ for (var i = 0; i < details.length; i++) {
+ var data = details[i];
+ var label = (typeof data.url != 'undefined' ? data.url : data.label);
+ label = (typeof label != 'undefined' && label !== null ? label : '');
+ var isOthers = (label == 'Others');
+ var onClick = false;
+ if (!isOthers && (groupName == 'previousPages' || groupName == 'followingPages')) {
+ onClick = (function (url) {
+ return function () {
+ self.reloadPopover(url);
+ };
+ })(label);
+ } else if (!isOthers && (groupName == 'outlinks' || groupName == 'websites' || groupName == 'downloads')) {
+ onClick = (function (url) {
+ return function () {
+ self.openExternalUrl(url);
+ };
+ })(label);
+ }
+
+ var tooltip = Piwik_Transitions_Translations.XOfY;
+ tooltip = '<b>' + tooltip.replace(/%s/, data.referrals + '</b>').replace(/%s/, nbTransitions);
+ tooltip = this.model.getShareInGroupTooltip(tooltip, groupName);
+
+ var fullLabel = label;
+ var shortened = false;
+ if ((this.actionType == 'url' && (groupName == 'previousPages' || groupName == 'followingPages'))
+ || groupName == 'downloads') {
+ // remove http + www + domain for internal URLs
+ label = Piwik_Transitions_Util.shortenUrl(label, true);
+ shortened = true;
+ } else if (groupName == 'outlinks' || groupName == 'websites') {
+ // remove http + www for external URLs
+ label = Piwik_Transitions_Util.shortenUrl(label);
+ shortened = true;
+ }
+
+ this.canvas.renderBox({
+ side: side,
+ onlyBg: onlyBg,
+ share: data.percentage / 100 * totalShare,
+ gradient: isOthers ? gradientOthers : gradientItems,
+ boxText: label,
+ boxTextTooltip: isOthers || !shortened ? false : fullLabel,
+ boxTextNumLines: 3,
+ curveText: data.percentage + '%',
+ curveTextTooltip: tooltip,
+ onClick: onClick
+ });
+ }
+
+ // draw background
+ var boxPositionAfter, curvePositionAfter;
+ if (side == 'left') {
+ boxPositionAfter = this.canvas.leftBoxPositionY;
+ curvePositionAfter = this.canvas.leftCurvePositionY;
+ this.canvas.leftBoxPositionY = boxPositionBefore;
+ this.canvas.leftCurvePositionY = curvePositionBefore;
+ } else {
+ boxPositionAfter = this.canvas.rightBoxPositionY;
+ curvePositionAfter = this.canvas.rightCurvePositionY;
+ this.canvas.rightBoxPositionY = boxPositionBefore;
+ this.canvas.rightCurvePositionY = curvePositionBefore;
+ }
+
+ this.canvas.renderBox({
+ side: side,
+ boxHeight: boxPositionAfter - boxPositionBefore - this.canvas.boxSpacing - 2,
+ curveHeight: curvePositionAfter - curvePositionBefore - this.canvas.curveSpacing,
+ gradient: gradientBackground,
+ bgCanvas: true
+ });
+
+ var spacing = this.canvas.isNarrowMode() ? 8 : 15;
+ this.canvas.addBoxSpacing(spacing, side);
};
/** Render a closed group without detailed data, only one box for the sum */
-Piwik_Transitions.prototype.renderClosedGroup = function(groupName, side, onlyBg) {
- var self = this;
- var gradient = this.canvas.createHorizontalGradient('#DDE4ED', '#9BBADE', side);
- if (groupName == this.highlightedGroup) {
- gradient = this.canvas.createHorizontalGradient('#FAE2C0', '#FAD293', side);
- }
-
- var nbTransitionsVarName = groupName + 'NbTransitions';
-
- if (self.model[nbTransitionsVarName] == 0) {
- return;
- }
-
- self.canvas.renderBox({
- side: side,
- onlyBg: onlyBg,
- share: self.model.getPercentage(nbTransitionsVarName),
- gradient: gradient,
- boxText: self.model.getGroupTitle(groupName),
- boxTextNumLines: 1,
- boxTextCssClass: 'SingleLine',
- boxIcon: 'themes/default/images/plus_blue.png',
- smallBox: true,
- onClick: function() {
- self.unHighlightGroup(groupName, side);
- self.openGroup(side, groupName);
- },
- onMouseOver: function() {
- self.highlightGroup(groupName, side);
- },
- onMouseOut: function() {
- self.unHighlightGroup(groupName, side);
- }
- });
+Piwik_Transitions.prototype.renderClosedGroup = function (groupName, side, onlyBg) {
+ var self = this;
+ var gradient = this.canvas.createHorizontalGradient('#DDE4ED', '#9BBADE', side);
+ if (groupName == this.highlightedGroup) {
+ gradient = this.canvas.createHorizontalGradient('#FAE2C0', '#FAD293', side);
+ }
+
+ var nbTransitionsVarName = groupName + 'NbTransitions';
+
+ if (self.model[nbTransitionsVarName] == 0) {
+ return;
+ }
+
+ self.canvas.renderBox({
+ side: side,
+ onlyBg: onlyBg,
+ share: self.model.getPercentage(nbTransitionsVarName),
+ gradient: gradient,
+ boxText: self.model.getGroupTitle(groupName),
+ boxTextNumLines: 1,
+ boxTextCssClass: 'SingleLine',
+ boxIcon: 'themes/default/images/plus_blue.png',
+ smallBox: true,
+ onClick: function () {
+ self.unHighlightGroup(groupName, side);
+ self.openGroup(side, groupName);
+ },
+ onMouseOver: function () {
+ self.highlightGroup(groupName, side);
+ },
+ onMouseOut: function () {
+ self.unHighlightGroup(groupName, side);
+ }
+ });
};
/** Reload the entire popover for a different URL */
-Piwik_Transitions.prototype.reloadPopover = function(url) {
- if (this.rowAction) {
- this.rowAction.openPopover(this.actionType + ':' + url);
- } else {
- this.reset(this.actionType, url);
- this.showPopover();
- }
+Piwik_Transitions.prototype.reloadPopover = function (url) {
+ if (this.rowAction) {
+ this.rowAction.openPopover(this.actionType + ':' + url);
+ } else {
+ this.reset(this.actionType, url);
+ this.showPopover();
+ }
};
/** Redraw the left or right sides with a different group opened */
-Piwik_Transitions.prototype.openGroup = function(side, groupName) {
+Piwik_Transitions.prototype.openGroup = function (side, groupName) {
- this.canvas.clearSide(side);
+ this.canvas.clearSide(side);
- if (side == 'left') {
- this.leftOpenGroup = groupName;
- this.renderLeftSide();
- } else {
- this.rightOpenGroup = groupName;
- this.renderRightSide();
- }
+ if (side == 'left') {
+ this.leftOpenGroup = groupName;
+ this.renderLeftSide();
+ } else {
+ this.rightOpenGroup = groupName;
+ this.renderRightSide();
+ }
- this.renderLoops();
+ this.renderLoops();
- this.canvas.truncateVisibleBoxTexts();
+ this.canvas.truncateVisibleBoxTexts();
};
/** Highlight a group: change curve color and highlight metric in the center box */
-Piwik_Transitions.prototype.highlightGroup = function(groupName, side) {
- if (this.highlightedGroup == groupName) {
- return;
- }
- if (this.highlightedGroup !== false) {
- this.unHighlightGroup(this.highlightedGroup, this.highlightedGroupSide);
- }
-
- this.highlightedGroup = groupName;
- this.highlightedGroupSide = side;
-
- var cssClass = 'Transitions_' + groupName.charAt(0).toUpperCase() + groupName.substr(1);
- this.highlightedGroupCenterEl = this.canvas.container.find('.' + cssClass);
- this.highlightedGroupCenterEl.addClass('Transitions_Highlighted');
-
- this.canvas.clearSide(side, true);
- if (side == 'left') {
- this.renderLeftSide(true);
- } else {
- this.renderRightSide(true);
- }
- this.renderLoops();
+Piwik_Transitions.prototype.highlightGroup = function (groupName, side) {
+ if (this.highlightedGroup == groupName) {
+ return;
+ }
+ if (this.highlightedGroup !== false) {
+ this.unHighlightGroup(this.highlightedGroup, this.highlightedGroupSide);
+ }
+
+ this.highlightedGroup = groupName;
+ this.highlightedGroupSide = side;
+
+ var cssClass = 'Transitions_' + groupName.charAt(0).toUpperCase() + groupName.substr(1);
+ this.highlightedGroupCenterEl = this.canvas.container.find('.' + cssClass);
+ this.highlightedGroupCenterEl.addClass('Transitions_Highlighted');
+
+ this.canvas.clearSide(side, true);
+ if (side == 'left') {
+ this.renderLeftSide(true);
+ } else {
+ this.renderRightSide(true);
+ }
+ this.renderLoops();
};
/** Remove highlight after using highlightGroup() */
-Piwik_Transitions.prototype.unHighlightGroup = function(groupName, side) {
- if (this.highlightedGroup === false) {
- return;
- }
-
- this.highlightedGroupCenterEl.removeClass('Transitions_Highlighted');
-
- this.highlightedGroup = false;
- this.highlightedGroupSide = false;
- this.highlightedGroupCenterEl = false;
-
- this.canvas.clearSide(side, true);
- if (side == 'left') {
- this.renderLeftSide(true);
- } else {
- this.renderRightSide(true);
- }
- this.renderLoops();
+Piwik_Transitions.prototype.unHighlightGroup = function (groupName, side) {
+ if (this.highlightedGroup === false) {
+ return;
+ }
+
+ this.highlightedGroupCenterEl.removeClass('Transitions_Highlighted');
+
+ this.highlightedGroup = false;
+ this.highlightedGroupSide = false;
+ this.highlightedGroupCenterEl = false;
+
+ this.canvas.clearSide(side, true);
+ if (side == 'left') {
+ this.renderLeftSide(true);
+ } else {
+ this.renderRightSide(true);
+ }
+ this.renderLoops();
};
/** Open a link in a new tab */
-Piwik_Transitions.prototype.openExternalUrl = function(url) {
- url = piwik.piwik_url + '?module=Proxy&action=redirect&url=' + encodeURIComponent(url);
- window.open(url, '_newtab');
+Piwik_Transitions.prototype.openExternalUrl = function (url) {
+ url = piwik.piwik_url + '?module=Proxy&action=redirect&url=' + encodeURIComponent(url);
+ window.open(url, '_newtab');
};
@@ -710,151 +710,151 @@ Piwik_Transitions.prototype.openExternalUrl = function(url) {
function Piwik_Transitions_Canvas(canvasBgLeft, canvasBgRight, canvasLeft, canvasRight, canvasLoops, width, height) {
- if (typeof window.G_vmlCanvasManager != "undefined") {
- window.G_vmlCanvasManager.initElement(canvasBgLeft);
- window.G_vmlCanvasManager.initElement(canvasBgRight);
- window.G_vmlCanvasManager.initElement(canvasLeft);
- window.G_vmlCanvasManager.initElement(canvasRight);
- window.G_vmlCanvasManager.initElement(canvasLoops);
- }
-
- if (!canvasBgLeft.getContext) {
- alert('Your browser is not supported.');
- return;
- }
-
- /** DOM element that contains the canvas */
- this.container = $(canvasBgLeft).parent().parent();
-
- /** Drawing context of the canvases */
- this.contextBgLeft = canvasBgLeft.getContext('2d');
- this.contextBgRight = canvasBgRight.getContext('2d');
- this.contextLeft = canvasLeft.getContext('2d');
- this.contextRight = canvasRight.getContext('2d');
- this.contextLoops = canvasLoops.getContext('2d');
-
- /** Width of the entire canvas */
- this.width = width;
- /** Height of the entire canvas */
- this.height = height;
-
- /** Current Y positions */
- this.leftBoxPositionY = this.originalBoxPositionY = 0;
- this.leftCurvePositionY = this.originalCurvePositionY = 110;
- this.rightBoxPositionY = this.originalBoxPositionY;
- this.rightCurvePositionY = this.originalCurvePositionY;
-
- /** Width of the rectangular box */
- this.boxWidth = 175;
- /** Height of the rectangular box */
- this.boxHeight = 53;
- /** Height of a smaller rectangular box */
- this.smallBoxHeight = 30;
- /** Width of the curve that connects the boxes to the center */
- this.curveWidth = 170;
- /** Line-height of the text */
- this.lineHeight = 14;
- /** Spacing between rectangular boxes */
- this.boxSpacing = 7;
- /** Spacing between the curves where they connect to the center */
- this.curveSpacing = 1.5;
-
- /** The total net height (without curve spacing) of the curves as they connect to the center */
- this.totalHeightOfConnections = 205;
-
- /** X positions of the left box - begin means left, end means right */
- this.leftBoxBeginX = 0;
- this.leftCurveBeginX = this.leftBoxBeginX + this.boxWidth;
- this.leftCurveEndX = this.leftCurveBeginX + this.curveWidth;
-
- /** X positions of the right box - begin means left, end means right */
- this.rightBoxEndX = this.width;
- this.rightBoxBeginX = this.rightCurveEndX = this.rightBoxEndX - this.boxWidth;
- this.rightCurveBeginX = this.rightCurveEndX - this.curveWidth;
+ if (typeof window.G_vmlCanvasManager != "undefined") {
+ window.G_vmlCanvasManager.initElement(canvasBgLeft);
+ window.G_vmlCanvasManager.initElement(canvasBgRight);
+ window.G_vmlCanvasManager.initElement(canvasLeft);
+ window.G_vmlCanvasManager.initElement(canvasRight);
+ window.G_vmlCanvasManager.initElement(canvasLoops);
+ }
+
+ if (!canvasBgLeft.getContext) {
+ alert('Your browser is not supported.');
+ return;
+ }
+
+ /** DOM element that contains the canvas */
+ this.container = $(canvasBgLeft).parent().parent();
+
+ /** Drawing context of the canvases */
+ this.contextBgLeft = canvasBgLeft.getContext('2d');
+ this.contextBgRight = canvasBgRight.getContext('2d');
+ this.contextLeft = canvasLeft.getContext('2d');
+ this.contextRight = canvasRight.getContext('2d');
+ this.contextLoops = canvasLoops.getContext('2d');
+
+ /** Width of the entire canvas */
+ this.width = width;
+ /** Height of the entire canvas */
+ this.height = height;
+
+ /** Current Y positions */
+ this.leftBoxPositionY = this.originalBoxPositionY = 0;
+ this.leftCurvePositionY = this.originalCurvePositionY = 110;
+ this.rightBoxPositionY = this.originalBoxPositionY;
+ this.rightCurvePositionY = this.originalCurvePositionY;
+
+ /** Width of the rectangular box */
+ this.boxWidth = 175;
+ /** Height of the rectangular box */
+ this.boxHeight = 53;
+ /** Height of a smaller rectangular box */
+ this.smallBoxHeight = 30;
+ /** Width of the curve that connects the boxes to the center */
+ this.curveWidth = 170;
+ /** Line-height of the text */
+ this.lineHeight = 14;
+ /** Spacing between rectangular boxes */
+ this.boxSpacing = 7;
+ /** Spacing between the curves where they connect to the center */
+ this.curveSpacing = 1.5;
+
+ /** The total net height (without curve spacing) of the curves as they connect to the center */
+ this.totalHeightOfConnections = 205;
+
+ /** X positions of the left box - begin means left, end means right */
+ this.leftBoxBeginX = 0;
+ this.leftCurveBeginX = this.leftBoxBeginX + this.boxWidth;
+ this.leftCurveEndX = this.leftCurveBeginX + this.curveWidth;
+
+ /** X positions of the right box - begin means left, end means right */
+ this.rightBoxEndX = this.width;
+ this.rightBoxBeginX = this.rightCurveEndX = this.rightBoxEndX - this.boxWidth;
+ this.rightCurveBeginX = this.rightCurveEndX - this.curveWidth;
}
/**
* Activate narrow mode: draw groups a bit more compact in order to save space
* for more than 3 referrer groups.
*/
-Piwik_Transitions_Canvas.prototype.narrowMode = function() {
- this.smallBoxHeight = 26;
- this.boxSpacing = 4;
- this.narrowMode = true;
+Piwik_Transitions_Canvas.prototype.narrowMode = function () {
+ this.smallBoxHeight = 26;
+ this.boxSpacing = 4;
+ this.narrowMode = true;
};
-Piwik_Transitions_Canvas.prototype.isNarrowMode = function() {
- return typeof this.narrowMode != 'undefined';
+Piwik_Transitions_Canvas.prototype.isNarrowMode = function () {
+ return typeof this.narrowMode != 'undefined';
};
/**
* Helper to create horizontal gradients
* @param position left|right
*/
-Piwik_Transitions_Canvas.prototype.createHorizontalGradient = function(lightColor, darkColor, position) {
- var fromX, toX, fromColor, toColor;
-
- if (position == 'left') {
- // gradient is used to fill a box on the left
- fromX = this.leftBoxBeginX + 50;
- toX = this.leftCurveEndX - 20;
- fromColor = lightColor;
- toColor = darkColor;
- } else {
- // gradient is used to fill a box on the right
- fromX = this.rightCurveBeginX + 20;
- toX = this.rightBoxEndX - 50;
- fromColor = darkColor;
- toColor = lightColor;
- }
-
- var gradient = this.contextBgLeft.createLinearGradient(fromX, 0, toX, 0);
- gradient.addColorStop(0, fromColor);
- gradient.addColorStop(1, toColor);
-
- return gradient;
+Piwik_Transitions_Canvas.prototype.createHorizontalGradient = function (lightColor, darkColor, position) {
+ var fromX, toX, fromColor, toColor;
+
+ if (position == 'left') {
+ // gradient is used to fill a box on the left
+ fromX = this.leftBoxBeginX + 50;
+ toX = this.leftCurveEndX - 20;
+ fromColor = lightColor;
+ toColor = darkColor;
+ } else {
+ // gradient is used to fill a box on the right
+ fromX = this.rightCurveBeginX + 20;
+ toX = this.rightBoxEndX - 50;
+ fromColor = darkColor;
+ toColor = lightColor;
+ }
+
+ var gradient = this.contextBgLeft.createLinearGradient(fromX, 0, toX, 0);
+ gradient.addColorStop(0, fromColor);
+ gradient.addColorStop(1, toColor);
+
+ return gradient;
};
/** Render text using a div inside the container */
-Piwik_Transitions_Canvas.prototype.renderText = function(text, x, y, cssClass, onClick, icon, maxLines) {
- var div = this.addDomElement('div', 'Text');
- div.css({
- left: x + 'px',
- top: y + 'px'
- });
- if (icon) {
- div.addClass('Transitions_HasBackground');
- div.css({backgroundImage: 'url(' + icon + ')'});
- }
- if (cssClass) {
- if (typeof cssClass == 'object') {
- for (var i = 0; i < cssClass.length; i++) {
- div.addClass('Transitions_' + cssClass[i]);
- }
- } else {
- div.addClass('Transitions_' + cssClass);
- }
- }
- if (onClick) {
- div.css('cursor', 'pointer').hover(function() {
- $(this).addClass('Transitions_Hover');
- },function() {
- $(this).removeClass('Transitions_Hover');
- }).click(onClick);
- }
- if (maxLines) {
- div.addClass('Transitions_ApplyTextAndTruncate').data('text', text);
- } else {
- div.html(text);
- }
- return div;
+Piwik_Transitions_Canvas.prototype.renderText = function (text, x, y, cssClass, onClick, icon, maxLines) {
+ var div = this.addDomElement('div', 'Text');
+ div.css({
+ left: x + 'px',
+ top: y + 'px'
+ });
+ if (icon) {
+ div.addClass('Transitions_HasBackground');
+ div.css({backgroundImage: 'url(' + icon + ')'});
+ }
+ if (cssClass) {
+ if (typeof cssClass == 'object') {
+ for (var i = 0; i < cssClass.length; i++) {
+ div.addClass('Transitions_' + cssClass[i]);
+ }
+ } else {
+ div.addClass('Transitions_' + cssClass);
+ }
+ }
+ if (onClick) {
+ div.css('cursor', 'pointer').hover(function () {
+ $(this).addClass('Transitions_Hover');
+ },function () {
+ $(this).removeClass('Transitions_Hover');
+ }).click(onClick);
+ }
+ if (maxLines) {
+ div.addClass('Transitions_ApplyTextAndTruncate').data('text', text);
+ } else {
+ div.html(text);
+ }
+ return div;
};
/** Add a DOM element inside the container (as a sibling of the canvas) */
-Piwik_Transitions_Canvas.prototype.addDomElement = function(tagName, cssClass) {
- var el = $(document.createElement('div')).addClass('Transitions_' + cssClass);
- this.container.append(el);
- return el;
+Piwik_Transitions_Canvas.prototype.addDomElement = function (tagName, cssClass) {
+ var el = $(document.createElement('div')).addClass('Transitions_' + cssClass);
+ this.container.append(el);
+ return el;
};
/**
@@ -862,36 +862,36 @@ Piwik_Transitions_Canvas.prototype.addDomElement = function(tagName, cssClass) {
* This method looks for the class Transitions_ApplyTextAndTruncate.
* It then looks up data-text and truncates it until it fits.
*/
-Piwik_Transitions_Canvas.prototype.truncateVisibleBoxTexts = function() {
- this.container.find('.Transitions_ApplyTextAndTruncate').each(function() {
- var container = $(this).html('<span>');
- var span = container.find('span');
-
- var text = container.data('text');
- span.html(piwikHelper.addBreakpointsToUrl(text));
-
- var divHeight = container.innerHeight();
- if (container.data('maxLines')) {
- divHeight = container.data('maxLines') * (parseInt(container.css('lineHeight'), 10) + .2);
- }
-
- var leftPart = false;
- var rightPart = false;
-
- while (divHeight < span.outerHeight()) {
- if (leftPart === false) {
- var middle = Math.round(text.length / 2);
- leftPart = text.substring(0, middle);
- rightPart = text.substring(middle, text.length);
- }
- leftPart = leftPart.substring(0, leftPart.length - 2);
- rightPart = rightPart.substring(2, rightPart.length);
- text = leftPart + '...' + rightPart;
- span.html(piwikHelper.addBreakpointsToUrl(text));
- }
-
- span.removeClass('Transitions_Truncate');
- });
+Piwik_Transitions_Canvas.prototype.truncateVisibleBoxTexts = function () {
+ this.container.find('.Transitions_ApplyTextAndTruncate').each(function () {
+ var container = $(this).html('<span>');
+ var span = container.find('span');
+
+ var text = container.data('text');
+ span.html(piwikHelper.addBreakpointsToUrl(text));
+
+ var divHeight = container.innerHeight();
+ if (container.data('maxLines')) {
+ divHeight = container.data('maxLines') * (parseInt(container.css('lineHeight'), 10) + .2);
+ }
+
+ var leftPart = false;
+ var rightPart = false;
+
+ while (divHeight < span.outerHeight()) {
+ if (leftPart === false) {
+ var middle = Math.round(text.length / 2);
+ leftPart = text.substring(0, middle);
+ rightPart = text.substring(middle, text.length);
+ }
+ leftPart = leftPart.substring(0, leftPart.length - 2);
+ rightPart = rightPart.substring(2, rightPart.length);
+ text = leftPart + '...' + rightPart;
+ span.html(piwikHelper.addBreakpointsToUrl(text));
+ }
+
+ span.removeClass('Transitions_Truncate');
+ });
};
/**
@@ -920,268 +920,268 @@ Piwik_Transitions_Canvas.prototype.truncateVisibleBoxTexts = function() {
* boxHeight: fix box height in px
* bgCanvas: true to draw on background canvas
*/
-Piwik_Transitions_Canvas.prototype.renderBox = function(params) {
- var curveHeight = params.curveHeight ? params.curveHeight :
- Math.round(this.totalHeightOfConnections * params.share);
- curveHeight = Math.max(curveHeight, 1);
-
- var boxHeight = this.boxHeight;
- if (params.smallBox) {
- boxHeight = this.smallBoxHeight;
- }
- if (params.boxHeight) {
- boxHeight = params.boxHeight;
- }
-
- var context;
- if (params.bgCanvas) {
- context = params.side == 'left' ? this.contextBgLeft : this.contextBgRight;
- } else {
- context = params.side == 'left' ? this.contextLeft : this.contextRight;
- }
-
- // background
- context.fillStyle = params.gradient;
- context.beginPath();
- if (params.side == 'left') {
- this.renderLeftBoxBg(context, boxHeight, curveHeight);
- } else {
- this.renderRightBoxBg(context, boxHeight, curveHeight);
- }
- if (typeof context.endPath == 'function') {
- context.endPath();
- }
-
- // text inside the box
- if (params.boxText && !params.onlyBg) {
- var onClick = typeof params.onClick == 'function' ? params.onClick : false;
- var boxTextLeft, boxTextTop, el;
- if (params.side == 'left') {
- boxTextLeft = this.leftBoxBeginX + 10;
- boxTextTop = this.leftBoxPositionY + boxHeight / 2 - params.boxTextNumLines * this.lineHeight / 2;
- el = this.renderText(params.boxText, boxTextLeft, boxTextTop, 'BoxTextLeft', onClick, params.boxIcon,
- params.boxTextNumLines);
- } else {
- boxTextLeft = this.rightBoxBeginX;
- boxTextTop = this.rightBoxPositionY + boxHeight / 2 - params.boxTextNumLines * this.lineHeight / 2;
- el = this.renderText(params.boxText, boxTextLeft, boxTextTop, 'BoxTextRight', onClick, params.boxIcon,
- params.boxTextNumLines);
- }
- if (params.boxTextCssClass) {
- el.addClass('Transitions_' + params.boxTextCssClass);
- }
- // tooltip
- if (params.boxTextTooltip) {
- el.hover(function() {
- var tip = piwikHelper.addBreakpointsToUrl(params.boxTextTooltip);
- Piwik_Tooltip.show(tip, 'Transitions_Tooltip_Small', 300);
- }, function() {
- Piwik_Tooltip.hide();
- });
- if (onClick) {
- el.click(function() {
- Piwik_Tooltip.hide();
- });
- }
- }
- if (typeof params.onMouseOver == 'function') {
- el.mouseenter(params.onMouseOver);
- }
- if (typeof params.onMouseOut == 'function') {
- el.mouseleave(params.onMouseOut);
- }
- }
-
- // text at the beginning of the curve
- if (params.curveText && !params.onlyBg) {
- var curveTextLeft, curveTextTop;
- if (params.side == 'left') {
- curveTextLeft = this.leftBoxBeginX + this.boxWidth + 3;
- curveTextTop = this.leftBoxPositionY + boxHeight / 2 - this.lineHeight / 2;
- } else {
- curveTextLeft = this.rightBoxBeginX - 37;
- curveTextTop = this.rightBoxPositionY + boxHeight / 2 - this.lineHeight / 2;
- }
- var textDiv = this.renderText(params.curveText, curveTextLeft, curveTextTop,
- params.side == 'left' ? 'CurveTextLeft' : 'CurveTextRight');
- // tooltip
- if (params.curveTextTooltip) {
- textDiv.hover(function() {
- Piwik_Tooltip.show(params.curveTextTooltip, 'Transitions_Tooltip_Small');
- }, function() {
- Piwik_Tooltip.hide();
- });
- }
- }
-
- if (params.side == 'left') {
- this.leftBoxPositionY += boxHeight + this.boxSpacing;
- this.leftCurvePositionY += curveHeight + this.curveSpacing;
- } else {
- this.rightBoxPositionY += boxHeight + this.boxSpacing;
- this.rightCurvePositionY += curveHeight + this.curveSpacing;
- }
+Piwik_Transitions_Canvas.prototype.renderBox = function (params) {
+ var curveHeight = params.curveHeight ? params.curveHeight :
+ Math.round(this.totalHeightOfConnections * params.share);
+ curveHeight = Math.max(curveHeight, 1);
+
+ var boxHeight = this.boxHeight;
+ if (params.smallBox) {
+ boxHeight = this.smallBoxHeight;
+ }
+ if (params.boxHeight) {
+ boxHeight = params.boxHeight;
+ }
+
+ var context;
+ if (params.bgCanvas) {
+ context = params.side == 'left' ? this.contextBgLeft : this.contextBgRight;
+ } else {
+ context = params.side == 'left' ? this.contextLeft : this.contextRight;
+ }
+
+ // background
+ context.fillStyle = params.gradient;
+ context.beginPath();
+ if (params.side == 'left') {
+ this.renderLeftBoxBg(context, boxHeight, curveHeight);
+ } else {
+ this.renderRightBoxBg(context, boxHeight, curveHeight);
+ }
+ if (typeof context.endPath == 'function') {
+ context.endPath();
+ }
+
+ // text inside the box
+ if (params.boxText && !params.onlyBg) {
+ var onClick = typeof params.onClick == 'function' ? params.onClick : false;
+ var boxTextLeft, boxTextTop, el;
+ if (params.side == 'left') {
+ boxTextLeft = this.leftBoxBeginX + 10;
+ boxTextTop = this.leftBoxPositionY + boxHeight / 2 - params.boxTextNumLines * this.lineHeight / 2;
+ el = this.renderText(params.boxText, boxTextLeft, boxTextTop, 'BoxTextLeft', onClick, params.boxIcon,
+ params.boxTextNumLines);
+ } else {
+ boxTextLeft = this.rightBoxBeginX;
+ boxTextTop = this.rightBoxPositionY + boxHeight / 2 - params.boxTextNumLines * this.lineHeight / 2;
+ el = this.renderText(params.boxText, boxTextLeft, boxTextTop, 'BoxTextRight', onClick, params.boxIcon,
+ params.boxTextNumLines);
+ }
+ if (params.boxTextCssClass) {
+ el.addClass('Transitions_' + params.boxTextCssClass);
+ }
+ // tooltip
+ if (params.boxTextTooltip) {
+ el.hover(function () {
+ var tip = piwikHelper.addBreakpointsToUrl(params.boxTextTooltip);
+ Piwik_Tooltip.show(tip, 'Transitions_Tooltip_Small', 300);
+ }, function () {
+ Piwik_Tooltip.hide();
+ });
+ if (onClick) {
+ el.click(function () {
+ Piwik_Tooltip.hide();
+ });
+ }
+ }
+ if (typeof params.onMouseOver == 'function') {
+ el.mouseenter(params.onMouseOver);
+ }
+ if (typeof params.onMouseOut == 'function') {
+ el.mouseleave(params.onMouseOut);
+ }
+ }
+
+ // text at the beginning of the curve
+ if (params.curveText && !params.onlyBg) {
+ var curveTextLeft, curveTextTop;
+ if (params.side == 'left') {
+ curveTextLeft = this.leftBoxBeginX + this.boxWidth + 3;
+ curveTextTop = this.leftBoxPositionY + boxHeight / 2 - this.lineHeight / 2;
+ } else {
+ curveTextLeft = this.rightBoxBeginX - 37;
+ curveTextTop = this.rightBoxPositionY + boxHeight / 2 - this.lineHeight / 2;
+ }
+ var textDiv = this.renderText(params.curveText, curveTextLeft, curveTextTop,
+ params.side == 'left' ? 'CurveTextLeft' : 'CurveTextRight');
+ // tooltip
+ if (params.curveTextTooltip) {
+ textDiv.hover(function () {
+ Piwik_Tooltip.show(params.curveTextTooltip, 'Transitions_Tooltip_Small');
+ }, function () {
+ Piwik_Tooltip.hide();
+ });
+ }
+ }
+
+ if (params.side == 'left') {
+ this.leftBoxPositionY += boxHeight + this.boxSpacing;
+ this.leftCurvePositionY += curveHeight + this.curveSpacing;
+ } else {
+ this.rightBoxPositionY += boxHeight + this.boxSpacing;
+ this.rightCurvePositionY += curveHeight + this.curveSpacing;
+ }
};
-Piwik_Transitions_Canvas.prototype.renderLeftBoxBg = function(context, boxHeight, curveHeight) {
- // derive coordinates for ths curve
- var leftUpper = {x: this.leftCurveBeginX, y: this.leftBoxPositionY};
- var leftLower = {x: this.leftCurveBeginX, y: this.leftBoxPositionY + boxHeight};
- var rightUpper = {x: this.leftCurveEndX, y: this.leftCurvePositionY};
- var rightLower = {x: this.leftCurveEndX, y: this.leftCurvePositionY + curveHeight};
-
- // derive control points for bezier curve
- var center = (this.leftCurveBeginX + this.leftCurveEndX) / 2;
- var cp1Upper = {x: center, y: leftUpper.y};
- var cp2Upper = {x: center, y: rightUpper.y};
- var cp1Lower = {x: center, y: rightLower.y};
- var cp2Lower = {x: center, y: leftLower.y};
-
- // the flow
- context.moveTo(leftUpper.x, leftUpper.y);
- context.bezierCurveTo(cp1Upper.x, cp1Upper.y, cp2Upper.x, cp2Upper.y, rightUpper.x, rightUpper.y);
- context.lineTo(rightLower.x, rightLower.y);
- context.bezierCurveTo(cp1Lower.x, cp1Lower.y, cp2Lower.x, cp2Lower.y, leftLower.x, leftLower.y);
-
- // the box
- context.lineTo(leftLower.x - this.boxWidth + 2, leftLower.y);
- context.lineTo(leftLower.x - this.boxWidth, leftUpper.y);
- context.lineTo(leftUpper.x, leftUpper.y);
- context.fill();
+Piwik_Transitions_Canvas.prototype.renderLeftBoxBg = function (context, boxHeight, curveHeight) {
+ // derive coordinates for ths curve
+ var leftUpper = {x: this.leftCurveBeginX, y: this.leftBoxPositionY};
+ var leftLower = {x: this.leftCurveBeginX, y: this.leftBoxPositionY + boxHeight};
+ var rightUpper = {x: this.leftCurveEndX, y: this.leftCurvePositionY};
+ var rightLower = {x: this.leftCurveEndX, y: this.leftCurvePositionY + curveHeight};
+
+ // derive control points for bezier curve
+ var center = (this.leftCurveBeginX + this.leftCurveEndX) / 2;
+ var cp1Upper = {x: center, y: leftUpper.y};
+ var cp2Upper = {x: center, y: rightUpper.y};
+ var cp1Lower = {x: center, y: rightLower.y};
+ var cp2Lower = {x: center, y: leftLower.y};
+
+ // the flow
+ context.moveTo(leftUpper.x, leftUpper.y);
+ context.bezierCurveTo(cp1Upper.x, cp1Upper.y, cp2Upper.x, cp2Upper.y, rightUpper.x, rightUpper.y);
+ context.lineTo(rightLower.x, rightLower.y);
+ context.bezierCurveTo(cp1Lower.x, cp1Lower.y, cp2Lower.x, cp2Lower.y, leftLower.x, leftLower.y);
+
+ // the box
+ context.lineTo(leftLower.x - this.boxWidth + 2, leftLower.y);
+ context.lineTo(leftLower.x - this.boxWidth, leftUpper.y);
+ context.lineTo(leftUpper.x, leftUpper.y);
+ context.fill();
};
-Piwik_Transitions_Canvas.prototype.renderRightBoxBg = function(context, boxHeight, curveHeight) {
- // derive coordinates for curve
- var leftUpper = {x: this.rightCurveBeginX, y: this.rightCurvePositionY};
- var leftLower = {x: this.rightCurveBeginX, y: this.rightCurvePositionY + curveHeight};
- var rightUpper = {x: this.rightCurveEndX, y: this.rightBoxPositionY};
- var rightLower = {x: this.rightCurveEndX, y: this.rightBoxPositionY + boxHeight};
-
- // derive control points for bezier curve
- var center = (this.rightCurveBeginX + this.rightCurveEndX) / 2;
- var cp1Upper = {x: center, y: leftUpper.y};
- var cp2Upper = {x: center, y: rightUpper.y};
- var cp1Lower = {x: center, y: rightLower.y};
- var cp2Lower = {x: center, y: leftLower.y};
-
- // the flow part 1
- context.moveTo(leftUpper.x, leftUpper.y);
- context.bezierCurveTo(cp1Upper.x, cp1Upper.y, cp2Upper.x, cp2Upper.y, rightUpper.x, rightUpper.y);
-
- // the box
- context.lineTo(rightUpper.x + this.boxWidth, rightUpper.y);
- context.lineTo(rightLower.x + this.boxWidth - 2, rightLower.y);
- context.lineTo(rightLower.x, rightLower.y);
-
- // the flow part 2
- context.bezierCurveTo(cp1Lower.x, cp1Lower.y, cp2Lower.x, cp2Lower.y, leftLower.x, leftLower.y);
- context.lineTo(leftUpper.x, leftUpper.y);
- context.fill();
+Piwik_Transitions_Canvas.prototype.renderRightBoxBg = function (context, boxHeight, curveHeight) {
+ // derive coordinates for curve
+ var leftUpper = {x: this.rightCurveBeginX, y: this.rightCurvePositionY};
+ var leftLower = {x: this.rightCurveBeginX, y: this.rightCurvePositionY + curveHeight};
+ var rightUpper = {x: this.rightCurveEndX, y: this.rightBoxPositionY};
+ var rightLower = {x: this.rightCurveEndX, y: this.rightBoxPositionY + boxHeight};
+
+ // derive control points for bezier curve
+ var center = (this.rightCurveBeginX + this.rightCurveEndX) / 2;
+ var cp1Upper = {x: center, y: leftUpper.y};
+ var cp2Upper = {x: center, y: rightUpper.y};
+ var cp1Lower = {x: center, y: rightLower.y};
+ var cp2Lower = {x: center, y: leftLower.y};
+
+ // the flow part 1
+ context.moveTo(leftUpper.x, leftUpper.y);
+ context.bezierCurveTo(cp1Upper.x, cp1Upper.y, cp2Upper.x, cp2Upper.y, rightUpper.x, rightUpper.y);
+
+ // the box
+ context.lineTo(rightUpper.x + this.boxWidth, rightUpper.y);
+ context.lineTo(rightLower.x + this.boxWidth - 2, rightLower.y);
+ context.lineTo(rightLower.x, rightLower.y);
+
+ // the flow part 2
+ context.bezierCurveTo(cp1Lower.x, cp1Lower.y, cp2Lower.x, cp2Lower.y, leftLower.x, leftLower.y);
+ context.lineTo(leftUpper.x, leftUpper.y);
+ context.fill();
};
/** Add spacing after the current box */
-Piwik_Transitions_Canvas.prototype.addBoxSpacing = function(spacing, side) {
- if (side == 'left') {
- this.leftBoxPositionY += spacing;
- } else {
- this.rightBoxPositionY += spacing;
- }
+Piwik_Transitions_Canvas.prototype.addBoxSpacing = function (spacing, side) {
+ if (side == 'left') {
+ this.leftBoxPositionY += spacing;
+ } else {
+ this.rightBoxPositionY += spacing;
+ }
};
-Piwik_Transitions_Canvas.prototype.renderLoops = function(share) {
- var curveHeight = Math.round(this.totalHeightOfConnections * share);
- curveHeight = Math.max(curveHeight, 1);
+Piwik_Transitions_Canvas.prototype.renderLoops = function (share) {
+ var curveHeight = Math.round(this.totalHeightOfConnections * share);
+ curveHeight = Math.max(curveHeight, 1);
- // create gradient
- var gradient = this.contextLoops.createLinearGradient(this.leftCurveEndX - 50, 0, this.rightCurveBeginX + 50, 0);
- var light = '#F5F3EB';
- var dark = '#E8E4D5';
- gradient.addColorStop(0, dark);
- gradient.addColorStop(.5, light);
- gradient.addColorStop(1, dark);
+ // create gradient
+ var gradient = this.contextLoops.createLinearGradient(this.leftCurveEndX - 50, 0, this.rightCurveBeginX + 50, 0);
+ var light = '#F5F3EB';
+ var dark = '#E8E4D5';
+ gradient.addColorStop(0, dark);
+ gradient.addColorStop(.5, light);
+ gradient.addColorStop(1, dark);
- this.contextLoops.fillStyle = gradient;
+ this.contextLoops.fillStyle = gradient;
- this.contextLoops.beginPath();
+ this.contextLoops.beginPath();
- // curve from the upper left connection to the center box to the lower left connection to the text box
- var point1 = {x: this.leftCurveEndX, y: this.leftCurvePositionY};
- var point2 = {x: this.leftCurveEndX, y: 470};
+ // curve from the upper left connection to the center box to the lower left connection to the text box
+ var point1 = {x: this.leftCurveEndX, y: this.leftCurvePositionY};
+ var point2 = {x: this.leftCurveEndX, y: 470};
- var cpLeftX = (this.leftCurveBeginX + this.leftCurveEndX) / 2 + 30;
- var cp1 = {x: cpLeftX, y: point1.y};
- var cp2 = {x: cpLeftX, y: point2.y};
+ var cpLeftX = (this.leftCurveBeginX + this.leftCurveEndX) / 2 + 30;
+ var cp1 = {x: cpLeftX, y: point1.y};
+ var cp2 = {x: cpLeftX, y: point2.y};
- this.contextLoops.moveTo(point1.x, point1.y);
- this.contextLoops.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, point2.x, point2.y);
+ this.contextLoops.moveTo(point1.x, point1.y);
+ this.contextLoops.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, point2.x, point2.y);
- // lower line of text box
- var point3 = {x: this.rightCurveBeginX, y: point2.y};
- this.contextLoops.lineTo(point3.x, point3.y);
+ // lower line of text box
+ var point3 = {x: this.rightCurveBeginX, y: point2.y};
+ this.contextLoops.lineTo(point3.x, point3.y);
- // curve to upper right connection to the center box
- var point4 = {x: this.rightCurveBeginX, y: this.rightCurvePositionY};
- var cpRightX = (this.rightCurveBeginX + this.rightCurveEndX) / 2 - 30;
- var cp3 = {x: cpRightX, y: point3.y};
- var cp4 = {x: cpRightX, y: point4.y};
- this.contextLoops.bezierCurveTo(cp3.x, cp3.y, cp4.x, cp4.y, point4.x, point4.y);
+ // curve to upper right connection to the center box
+ var point4 = {x: this.rightCurveBeginX, y: this.rightCurvePositionY};
+ var cpRightX = (this.rightCurveBeginX + this.rightCurveEndX) / 2 - 30;
+ var cp3 = {x: cpRightX, y: point3.y};
+ var cp4 = {x: cpRightX, y: point4.y};
+ this.contextLoops.bezierCurveTo(cp3.x, cp3.y, cp4.x, cp4.y, point4.x, point4.y);
- // line to lower right connection to the center box
- var point5 = {x: point4.x, y: point4.y + curveHeight};
- this.contextLoops.lineTo(point5.x, point5.y);
+ // line to lower right connection to the center box
+ var point5 = {x: point4.x, y: point4.y + curveHeight};
+ this.contextLoops.lineTo(point5.x, point5.y);
- // curve to upper right connection to the text box
- var point6 = {x: point5.x, y: point2.y - 25};
- cpRightX -= 30;
- var cp5 = {x: cpRightX, y: point5.y};
- var cp6 = {x: cpRightX, y: point6.y};
- this.contextLoops.bezierCurveTo(cp5.x, cp5.y, cp6.x, cp6.y, point6.x, point6.y);
+ // curve to upper right connection to the text box
+ var point6 = {x: point5.x, y: point2.y - 25};
+ cpRightX -= 30;
+ var cp5 = {x: cpRightX, y: point5.y};
+ var cp6 = {x: cpRightX, y: point6.y};
+ this.contextLoops.bezierCurveTo(cp5.x, cp5.y, cp6.x, cp6.y, point6.x, point6.y);
- // upper line of the text box
- var point7 = {x: point1.x, y: point6.y};
- this.contextLoops.lineTo(point7.x, point7.y);
+ // upper line of the text box
+ var point7 = {x: point1.x, y: point6.y};
+ this.contextLoops.lineTo(point7.x, point7.y);
- // line to lower left connection to the center box
- var point8 = {x: point1.x, y: point1.y + curveHeight};
- cpLeftX += 30;
- var cp7 = {x: cpLeftX, y: point7.y};
- var cp8 = {x: cpLeftX, y: point8.y};
- this.contextLoops.bezierCurveTo(cp7.x, cp7.y, cp8.x, cp8.y, point8.x, point8.y);
+ // line to lower left connection to the center box
+ var point8 = {x: point1.x, y: point1.y + curveHeight};
+ cpLeftX += 30;
+ var cp7 = {x: cpLeftX, y: point7.y};
+ var cp8 = {x: cpLeftX, y: point8.y};
+ this.contextLoops.bezierCurveTo(cp7.x, cp7.y, cp8.x, cp8.y, point8.x, point8.y);
- this.contextLoops.fill();
+ this.contextLoops.fill();
- if (typeof this.contextLoops.endPath == 'function') {
- this.contextLoops.endPath();
- }
+ if (typeof this.contextLoops.endPath == 'function') {
+ this.contextLoops.endPath();
+ }
};
/** Clear one side for redrawing */
-Piwik_Transitions_Canvas.prototype.clearSide = function(side, onlyBg) {
- if (side == 'left') {
- this.contextBgLeft.clearRect(0, 0, this.width, this.height);
- this.contextLeft.clearRect(0, 0, this.width, this.height);
- } else {
- this.contextBgRight.clearRect(0, 0, this.width, this.height);
- this.contextRight.clearRect(0, 0, this.width, this.height);
- }
- this.contextLoops.clearRect(0, 0, this.width, this.height);
-
- if (side == 'left') {
- if (!onlyBg) {
- this.container.find('.Transitions_BoxTextLeft').remove();
- this.container.find('.Transitions_CurveTextLeft').remove();
- }
- this.leftBoxPositionY = this.originalBoxPositionY;
- this.leftCurvePositionY = this.originalCurvePositionY;
- } else {
- if (!onlyBg) {
- this.container.find('.Transitions_BoxTextRight').remove();
- this.container.find('.Transitions_CurveTextRight').remove();
- }
- this.rightBoxPositionY = this.originalBoxPositionY;
- this.rightCurvePositionY = this.originalCurvePositionY;
- }
+Piwik_Transitions_Canvas.prototype.clearSide = function (side, onlyBg) {
+ if (side == 'left') {
+ this.contextBgLeft.clearRect(0, 0, this.width, this.height);
+ this.contextLeft.clearRect(0, 0, this.width, this.height);
+ } else {
+ this.contextBgRight.clearRect(0, 0, this.width, this.height);
+ this.contextRight.clearRect(0, 0, this.width, this.height);
+ }
+ this.contextLoops.clearRect(0, 0, this.width, this.height);
+
+ if (side == 'left') {
+ if (!onlyBg) {
+ this.container.find('.Transitions_BoxTextLeft').remove();
+ this.container.find('.Transitions_CurveTextLeft').remove();
+ }
+ this.leftBoxPositionY = this.originalBoxPositionY;
+ this.leftCurvePositionY = this.originalCurvePositionY;
+ } else {
+ if (!onlyBg) {
+ this.container.find('.Transitions_BoxTextRight').remove();
+ this.container.find('.Transitions_CurveTextRight').remove();
+ }
+ this.rightBoxPositionY = this.originalBoxPositionY;
+ this.rightCurvePositionY = this.originalCurvePositionY;
+ }
};
@@ -1190,185 +1190,185 @@ Piwik_Transitions_Canvas.prototype.clearSide = function(side, onlyBg) {
// --------------------------------------
function Piwik_Transitions_Model(ajax) {
- this.ajax = ajax;
+ this.ajax = ajax;
- this.groupTitles = {};
+ this.groupTitles = {};
}
-Piwik_Transitions_Model.prototype.htmlLoaded = function() {
- this.groupTitles.previousPages = Piwik_Transitions_Translations.fromPreviousPages;
- this.groupTitles.previousSiteSearches = Piwik_Transitions_Translations.fromPreviousSiteSearches;
- this.groupTitles.followingPages = Piwik_Transitions_Translations.toFollowingPages;
- this.groupTitles.followingSiteSearches = Piwik_Transitions_Translations.toFollowingSiteSearches;
- this.groupTitles.outlinks = Piwik_Transitions_Translations.outlinks;
- this.groupTitles.downloads = Piwik_Transitions_Translations.downloads;
-
- this.shareInGroupTexts = {
- previousPages: Piwik_Transitions_Translations.fromPreviousPagesInline,
- previousSiteSearches: Piwik_Transitions_Translations.fromPreviousSiteSearchesInline,
- followingPages: Piwik_Transitions_Translations.toFollowingPagesInline,
- followingSiteSearches: Piwik_Transitions_Translations.toFollowingSiteSearchesInline,
- searchEngines: Piwik_Transitions_Translations.fromSearchEnginesInline,
- websites: Piwik_Transitions_Translations.fromWebsitesInline,
- campaigns: Piwik_Transitions_Translations.fromCampaignsInline,
- outlinks: Piwik_Transitions_Translations.outlinksInline,
- downloads: Piwik_Transitions_Translations.downloadsInline
- };
+Piwik_Transitions_Model.prototype.htmlLoaded = function () {
+ this.groupTitles.previousPages = Piwik_Transitions_Translations.fromPreviousPages;
+ this.groupTitles.previousSiteSearches = Piwik_Transitions_Translations.fromPreviousSiteSearches;
+ this.groupTitles.followingPages = Piwik_Transitions_Translations.toFollowingPages;
+ this.groupTitles.followingSiteSearches = Piwik_Transitions_Translations.toFollowingSiteSearches;
+ this.groupTitles.outlinks = Piwik_Transitions_Translations.outlinks;
+ this.groupTitles.downloads = Piwik_Transitions_Translations.downloads;
+
+ this.shareInGroupTexts = {
+ previousPages: Piwik_Transitions_Translations.fromPreviousPagesInline,
+ previousSiteSearches: Piwik_Transitions_Translations.fromPreviousSiteSearchesInline,
+ followingPages: Piwik_Transitions_Translations.toFollowingPagesInline,
+ followingSiteSearches: Piwik_Transitions_Translations.toFollowingSiteSearchesInline,
+ searchEngines: Piwik_Transitions_Translations.fromSearchEnginesInline,
+ websites: Piwik_Transitions_Translations.fromWebsitesInline,
+ campaigns: Piwik_Transitions_Translations.fromCampaignsInline,
+ outlinks: Piwik_Transitions_Translations.outlinksInline,
+ downloads: Piwik_Transitions_Translations.downloadsInline
+ };
};
-Piwik_Transitions_Model.prototype.loadData = function(actionType, actionName, callback) {
- var self = this;
-
- this.pageviews = 0;
- this.exits = 0;
- this.loops = 0;
-
- this.directEntries = 0;
-
- this.searchEnginesNbTransitions = 0;
- this.searchEngines = [];
-
- this.websitesNbTransitions = 0;
- this.websites = [];
-
- this.campaignsNbTransitions = 0;
- this.campaigns = [];
-
- this.previousPagesNbTransitions = 0;
- this.previousPages = [];
-
- this.followingPagesNbTransitions = 0;
- this.followingPages = [];
-
- this.downloadsNbTransitions = 0;
- this.downloads = [];
-
- this.outlinksNbTransitions = 0;
- this.outlinks = [];
-
- this.previousSiteSearchesNbTransitions = 0;
- this.previousSiteSearches = [];
-
- this.followingSiteSearchesNbTransitions = 0;
- this.followingSiteSearches = [];
-
- this.date = '';
-
- this.ajax.callApi('Transitions.getTransitionsForAction', {
- actionType: actionType,
- actionName: actionName,
- expanded: 1
- },
- function(report) {
- self.date = report.date;
-
- // load page metrics
- self.pageviews = report.pageMetrics.pageviews;
- self.loops = report.pageMetrics.loops;
- self.exits = report.pageMetrics.exits;
-
- // load referrers: split direct entries and others
- for (var i = 0; i < report.referrers.length; i++) {
- var referrer = report.referrers[i];
- if (referrer.shortName == 'direct') {
- self.directEntries = referrer.visits;
- } else if (referrer.shortName == 'search') {
- self.searchEnginesNbTransitions = referrer.visits;
- self.searchEngines = referrer.details;
- self.groupTitles.searchEngines = referrer.label;
- } else if (referrer.shortName == 'website') {
- self.websitesNbTransitions = referrer.visits;
- self.websites = referrer.details;
- self.groupTitles.websites = referrer.label;
- } else if (referrer.shortName == 'campaign') {
- self.campaignsNbTransitions = referrer.visits;
- self.campaigns = referrer.details;
- self.groupTitles.campaigns = referrer.label;
- }
- }
-
- self.loadAndSumReport(report, 'previousPages');
- self.loadAndSumReport(report, 'previousSiteSearches');
- self.loadAndSumReport(report, 'followingPages');
- self.loadAndSumReport(report, 'followingSiteSearches');
- self.loadAndSumReport(report, 'downloads');
- self.loadAndSumReport(report, 'outlinks');
-
- if (typeof Piwik_Transitions_Model.totalNbPageviews == 'undefined') {
- Piwik_Transitions_Model.totalNbPageviews = false;
- self.ajax.loadTotalNbPageviews(function(nbPageviews) {
- Piwik_Transitions_Model.totalNbPageviews = nbPageviews;
- });
- }
-
- callback();
- });
+Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, callback) {
+ var self = this;
+
+ this.pageviews = 0;
+ this.exits = 0;
+ this.loops = 0;
+
+ this.directEntries = 0;
+
+ this.searchEnginesNbTransitions = 0;
+ this.searchEngines = [];
+
+ this.websitesNbTransitions = 0;
+ this.websites = [];
+
+ this.campaignsNbTransitions = 0;
+ this.campaigns = [];
+
+ this.previousPagesNbTransitions = 0;
+ this.previousPages = [];
+
+ this.followingPagesNbTransitions = 0;
+ this.followingPages = [];
+
+ this.downloadsNbTransitions = 0;
+ this.downloads = [];
+
+ this.outlinksNbTransitions = 0;
+ this.outlinks = [];
+
+ this.previousSiteSearchesNbTransitions = 0;
+ this.previousSiteSearches = [];
+
+ this.followingSiteSearchesNbTransitions = 0;
+ this.followingSiteSearches = [];
+
+ this.date = '';
+
+ this.ajax.callApi('Transitions.getTransitionsForAction', {
+ actionType: actionType,
+ actionName: actionName,
+ expanded: 1
+ },
+ function (report) {
+ self.date = report.date;
+
+ // load page metrics
+ self.pageviews = report.pageMetrics.pageviews;
+ self.loops = report.pageMetrics.loops;
+ self.exits = report.pageMetrics.exits;
+
+ // load referrers: split direct entries and others
+ for (var i = 0; i < report.referrers.length; i++) {
+ var referrer = report.referrers[i];
+ if (referrer.shortName == 'direct') {
+ self.directEntries = referrer.visits;
+ } else if (referrer.shortName == 'search') {
+ self.searchEnginesNbTransitions = referrer.visits;
+ self.searchEngines = referrer.details;
+ self.groupTitles.searchEngines = referrer.label;
+ } else if (referrer.shortName == 'website') {
+ self.websitesNbTransitions = referrer.visits;
+ self.websites = referrer.details;
+ self.groupTitles.websites = referrer.label;
+ } else if (referrer.shortName == 'campaign') {
+ self.campaignsNbTransitions = referrer.visits;
+ self.campaigns = referrer.details;
+ self.groupTitles.campaigns = referrer.label;
+ }
+ }
+
+ self.loadAndSumReport(report, 'previousPages');
+ self.loadAndSumReport(report, 'previousSiteSearches');
+ self.loadAndSumReport(report, 'followingPages');
+ self.loadAndSumReport(report, 'followingSiteSearches');
+ self.loadAndSumReport(report, 'downloads');
+ self.loadAndSumReport(report, 'outlinks');
+
+ if (typeof Piwik_Transitions_Model.totalNbPageviews == 'undefined') {
+ Piwik_Transitions_Model.totalNbPageviews = false;
+ self.ajax.loadTotalNbPageviews(function (nbPageviews) {
+ Piwik_Transitions_Model.totalNbPageviews = nbPageviews;
+ });
+ }
+
+ callback();
+ });
};
-Piwik_Transitions_Model.prototype.loadAndSumReport = function(apiData, reportName) {
- var data = this[reportName] = apiData[reportName];
- var sumVarName = reportName + 'NbTransitions';
+Piwik_Transitions_Model.prototype.loadAndSumReport = function (apiData, reportName) {
+ var data = this[reportName] = apiData[reportName];
+ var sumVarName = reportName + 'NbTransitions';
- this[sumVarName] = 0;
- for (var i = 0; i < data.length; i++) {
- this[sumVarName] += data[i].referrals;
- }
+ this[sumVarName] = 0;
+ for (var i = 0; i < data.length; i++) {
+ this[sumVarName] += data[i].referrals;
+ }
};
-Piwik_Transitions_Model.prototype.getTotalNbPageviews = function() {
- if (typeof Piwik_Transitions_Model.totalNbPageviews == 'undefined') {
- return false;
- }
- return Piwik_Transitions_Model.totalNbPageviews;
+Piwik_Transitions_Model.prototype.getTotalNbPageviews = function () {
+ if (typeof Piwik_Transitions_Model.totalNbPageviews == 'undefined') {
+ return false;
+ }
+ return Piwik_Transitions_Model.totalNbPageviews;
};
-Piwik_Transitions_Model.prototype.getGroupTitle = function(groupName) {
- if (typeof this.groupTitles[groupName] != 'undefined') {
- return this.groupTitles[groupName];
- }
- return groupName;
+Piwik_Transitions_Model.prototype.getGroupTitle = function (groupName) {
+ if (typeof this.groupTitles[groupName] != 'undefined') {
+ return this.groupTitles[groupName];
+ }
+ return groupName;
};
-Piwik_Transitions_Model.prototype.getShareInGroupTooltip = function(share, groupName) {
- var tip = this.shareInGroupTexts[groupName];
- return tip.replace(/%s/, share);
+Piwik_Transitions_Model.prototype.getShareInGroupTooltip = function (share, groupName) {
+ var tip = this.shareInGroupTexts[groupName];
+ return tip.replace(/%s/, share);
};
-Piwik_Transitions_Model.prototype.getDetailsForGroup = function(groupName) {
- return this.addPercentagesToData(this[groupName]);
+Piwik_Transitions_Model.prototype.getDetailsForGroup = function (groupName) {
+ return this.addPercentagesToData(this[groupName]);
};
-Piwik_Transitions_Model.prototype.getPercentage = function(metric, formatted) {
- var percentage = (this.pageviews == 0 ? 0 : this[metric] / this.pageviews);
+Piwik_Transitions_Model.prototype.getPercentage = function (metric, formatted) {
+ var percentage = (this.pageviews == 0 ? 0 : this[metric] / this.pageviews);
- if (formatted) {
- percentage = this.roundPercentage(percentage);
- percentage += '%';
- }
+ if (formatted) {
+ percentage = this.roundPercentage(percentage);
+ percentage += '%';
+ }
- return percentage;
+ return percentage;
};
-Piwik_Transitions_Model.prototype.addPercentagesToData = function(data) {
- var total = 0;
+Piwik_Transitions_Model.prototype.addPercentagesToData = function (data) {
+ var total = 0;
- for (var i = 0; i < data.length; i++) {
- total += parseInt(data[i].referrals, 10);
- }
+ for (var i = 0; i < data.length; i++) {
+ total += parseInt(data[i].referrals, 10);
+ }
- for (i = 0; i < data.length; i++) {
- data[i].percentage = this.roundPercentage(data[i].referrals / total);
- }
+ for (i = 0; i < data.length; i++) {
+ data[i].percentage = this.roundPercentage(data[i].referrals / total);
+ }
- return data;
+ return data;
};
-Piwik_Transitions_Model.prototype.roundPercentage = function(value) {
- if (value < .1) {
- return Math.round(value * 1000) / 10.0;
- } else {
- return Math.round(value * 100);
- }
+Piwik_Transitions_Model.prototype.roundPercentage = function (value) {
+ if (value < .1) {
+ return Math.round(value * 1000) / 10.0;
+ } else {
+ return Math.round(value * 100);
+ }
};
@@ -1379,16 +1379,16 @@ Piwik_Transitions_Model.prototype.roundPercentage = function(value) {
function Piwik_Transitions_Ajax() {
}
-Piwik_Transitions_Ajax.prototype.loadTotalNbPageviews = function(callback) {
- this.callApi('Actions.get', {
- columns: 'nb_pageviews'
- }, function(response) {
- var value = typeof response.value != 'undefined' ? response.value : false;
- callback(value);
- });
+Piwik_Transitions_Ajax.prototype.loadTotalNbPageviews = function (callback) {
+ this.callApi('Actions.get', {
+ columns: 'nb_pageviews'
+ }, function (response) {
+ var value = typeof response.value != 'undefined' ? response.value : false;
+ callback(value);
+ });
};
-Piwik_Transitions_Ajax.prototype.callTransitionsController = function(action, callback) {
+Piwik_Transitions_Ajax.prototype.callTransitionsController = function (action, callback) {
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
module: 'Transitions',
@@ -1399,16 +1399,16 @@ Piwik_Transitions_Ajax.prototype.callTransitionsController = function(action, ca
ajaxRequest.send(false);
};
-Piwik_Transitions_Ajax.prototype.callApi = function(method, params, callback) {
- var self = this;
+Piwik_Transitions_Ajax.prototype.callApi = function (method, params, callback) {
+ var self = this;
- params.format = 'JSON';
- params.module = 'API';
+ params.format = 'JSON';
+ params.module = 'API';
params.method = method;
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(params, 'get');
- ajaxRequest.useCallbackInCaseOfError();
+ ajaxRequest.useCallbackInCaseOfError();
ajaxRequest.setCallback(
function (result) {
if (typeof result.result != 'undefined' && result.result == 'error') {
@@ -1463,55 +1463,55 @@ Piwik_Transitions_Ajax.prototype.callApi = function(method, params, callback) {
Piwik_Transitions_Util = {
- /**
- * Removes protocol, www and trailing slashes from a URL.
- * If removeDomain is set, the domain is removed as well.
- */
- shortenUrl: function(url, removeDomain) {
- if (url == 'Others') {
- return url;
- }
-
- var urlBackup = url;
- url = url.replace(/http(s)?:\/\/(www\.)?/, '');
-
- if (urlBackup == url) {
- return url;
- }
-
- if (removeDomain) {
- url = url.replace(/[^\/]*/, '');
- if (url == '/') {
- url = urlBackup;
- }
- }
-
- url = url.replace(/\/$/, '');
-
- return url;
- },
-
- /**
- * Replaces a %s placeholder in the HTML.
- * The special feature is that it can be called multiple times, replacing the already
- * replaced placeholder again. It creates a span that can be assigned a class using the
- * spanClass parameter. The default class is 'Transitions_Metric'.
- */
- replacePlaceholderInHtml: function(container, value, spanClass) {
- var span = container.find('span');
- if (span.size() == 0) {
- var html = container.html().replace(/%s/, '<span></span>');
- span = container.html(html).find('span');
- if (!spanClass) {
- spanClass = 'Transitions_Metric';
- }
- span.addClass(spanClass);
- }
- if ($.browser.msie && parseFloat($.browser.version) < 8) {
- // ie7 fix
- value += '&nbsp;';
- }
- span.html(value);
- }
+ /**
+ * Removes protocol, www and trailing slashes from a URL.
+ * If removeDomain is set, the domain is removed as well.
+ */
+ shortenUrl: function (url, removeDomain) {
+ if (url == 'Others') {
+ return url;
+ }
+
+ var urlBackup = url;
+ url = url.replace(/http(s)?:\/\/(www\.)?/, '');
+
+ if (urlBackup == url) {
+ return url;
+ }
+
+ if (removeDomain) {
+ url = url.replace(/[^\/]*/, '');
+ if (url == '/') {
+ url = urlBackup;
+ }
+ }
+
+ url = url.replace(/\/$/, '');
+
+ return url;
+ },
+
+ /**
+ * Replaces a %s placeholder in the HTML.
+ * The special feature is that it can be called multiple times, replacing the already
+ * replaced placeholder again. It creates a span that can be assigned a class using the
+ * spanClass parameter. The default class is 'Transitions_Metric'.
+ */
+ replacePlaceholderInHtml: function (container, value, spanClass) {
+ var span = container.find('span');
+ if (span.size() == 0) {
+ var html = container.html().replace(/%s/, '<span></span>');
+ span = container.html(html).find('span');
+ if (!spanClass) {
+ spanClass = 'Transitions_Metric';
+ }
+ span.addClass(spanClass);
+ }
+ if ($.browser.msie && parseFloat($.browser.version) < 8) {
+ // ie7 fix
+ value += '&nbsp;';
+ }
+ span.html(value);
+ }
};
diff --git a/plugins/Transitions/templates/transitions.tpl b/plugins/Transitions/templates/transitions.tpl
index 0006c2e372..9ec71b8fe3 100644
--- a/plugins/Transitions/templates/transitions.tpl
+++ b/plugins/Transitions/templates/transitions.tpl
@@ -1,45 +1,56 @@
-
<div id="Transitions_Container">
- <div id="Transitions_CenterBox" class="Transitions_Text">
- <h2></h2>
- <div class="Transitions_CenterBoxMetrics">
- <p class="Transitions_Pageviews Transitions_Margin">{$translations.pageviewsInline|translate}</p>
-
- <div class="Transitions_IncomingTraffic">
- <h3>{'Transitions_IncomingTraffic'|translate}</h3>
- <p class="Transitions_PreviousPages">{$translations.fromPreviousPagesInline|translate}</p>
- <p class="Transitions_PreviousSiteSearches">{$translations.fromPreviousSiteSearchesInline|translate}</p>
- <p class="Transitions_SearchEngines">{$translations.fromSearchEnginesInline|translate}</p>
- <p class="Transitions_Websites">{$translations.fromWebsitesInline|translate}</p>
- <p class="Transitions_Campaigns">{$translations.fromCampaignsInline|translate}</p>
- <p class="Transitions_DirectEntries">{$translations.directEntriesInline|translate}</p>
- </div>
-
- <div class="Transitions_OutgoingTraffic">
- <h3>{'Transitions_OutgoingTraffic'|translate}</h3>
- <p class="Transitions_FollowingPages">{$translations.toFollowingPagesInline|translate}</p>
- <p class="Transitions_FollowingSiteSearches">{$translations.toFollowingSiteSearchesInline|translate}</p>
- <p class="Transitions_Downloads">{$translations.downloadsInline|translate}</p>
- <p class="Transitions_Outlinks">{$translations.outlinksInline|translate}</p>
- <p class="Transitions_Exits">{$translations.exitsInline|translate}</p>
- </div>
- </div>
- </div>
- <div id="Transitions_Loops" class="Transitions_Text">
- {$translations.loopsInline|translate}
- </div>
- <div id="Transitions_Canvas_Background_Left" class="Transitions_Canvas_Container"></div>
- <div id="Transitions_Canvas_Background_Right" class="Transitions_Canvas_Container"></div>
- <div id="Transitions_Canvas_Left" class="Transitions_Canvas_Container"></div>
- <div id="Transitions_Canvas_Right" class="Transitions_Canvas_Container"></div>
- <div id="Transitions_Canvas_Loops" class="Transitions_Canvas_Container"></div>
+ <div id="Transitions_CenterBox" class="Transitions_Text">
+ <h2></h2>
+
+ <div class="Transitions_CenterBoxMetrics">
+ <p class="Transitions_Pageviews Transitions_Margin">{$translations.pageviewsInline|translate}</p>
+
+ <div class="Transitions_IncomingTraffic">
+ <h3>{'Transitions_IncomingTraffic'|translate}</h3>
+
+ <p class="Transitions_PreviousPages">{$translations.fromPreviousPagesInline|translate}</p>
+
+ <p class="Transitions_PreviousSiteSearches">{$translations.fromPreviousSiteSearchesInline|translate}</p>
+
+ <p class="Transitions_SearchEngines">{$translations.fromSearchEnginesInline|translate}</p>
+
+ <p class="Transitions_Websites">{$translations.fromWebsitesInline|translate}</p>
+
+ <p class="Transitions_Campaigns">{$translations.fromCampaignsInline|translate}</p>
+
+ <p class="Transitions_DirectEntries">{$translations.directEntriesInline|translate}</p>
+ </div>
+
+ <div class="Transitions_OutgoingTraffic">
+ <h3>{'Transitions_OutgoingTraffic'|translate}</h3>
+
+ <p class="Transitions_FollowingPages">{$translations.toFollowingPagesInline|translate}</p>
+
+ <p class="Transitions_FollowingSiteSearches">{$translations.toFollowingSiteSearchesInline|translate}</p>
+
+ <p class="Transitions_Downloads">{$translations.downloadsInline|translate}</p>
+
+ <p class="Transitions_Outlinks">{$translations.outlinksInline|translate}</p>
+
+ <p class="Transitions_Exits">{$translations.exitsInline|translate}</p>
+ </div>
+ </div>
+ </div>
+ <div id="Transitions_Loops" class="Transitions_Text">
+ {$translations.loopsInline|translate}
+ </div>
+ <div id="Transitions_Canvas_Background_Left" class="Transitions_Canvas_Container"></div>
+ <div id="Transitions_Canvas_Background_Right" class="Transitions_Canvas_Container"></div>
+ <div id="Transitions_Canvas_Left" class="Transitions_Canvas_Container"></div>
+ <div id="Transitions_Canvas_Right" class="Transitions_Canvas_Container"></div>
+ <div id="Transitions_Canvas_Loops" class="Transitions_Canvas_Container"></div>
</div>
<script type="text/javascript">
- var Piwik_Transitions_Translations = {literal}{{/literal}
- {foreach from=$translations key=internalKey item=translation}
- "{$internalKey}": "{$translation|escape:'html'}",
- {/foreach}
- "": ""
- {literal}}{/literal};
+ var Piwik_Transitions_Translations = {literal}{{/literal}
+ {foreach from=$translations key=internalKey item=translation}
+ "{$internalKey}": "{$translation|escape:'html'}",
+ {/foreach}
+ "": ""
+ {literal}}{/literal};
</script> \ No newline at end of file
diff --git a/plugins/UserCountry/API.php b/plugins/UserCountry/API.php
index fad2b1a4ca..d8cdb266b6 100644
--- a/plugins/UserCountry/API.php
+++ b/plugins/UserCountry/API.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserCountry
*/
@@ -18,195 +18,192 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/functions.php';
* The UserCountry API lets you access reports about your visitors' Countries and Continents.
* @package Piwik_UserCountry
*/
-class Piwik_UserCountry_API
+class Piwik_UserCountry_API
{
- static private $instance = null;
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- public function getCountry( $idSite, $period, $date, $segment = false )
- {
- $recordName = Piwik_UserCountry::VISITS_BY_COUNTRY_RECORD_NAME;
- $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
-
- // apply filter on the whole datatable in order the inline search to work (searches
- // are done on "beautiful" label)
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'code'));
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getFlagFromCode'));
- $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_CountryTranslate'));
- $dataTable->queueFilter('AddConstantMetadata', array('logoWidth', 16));
- $dataTable->queueFilter('AddConstantMetadata', array('logoHeight', 11));
-
- return $dataTable;
- }
-
- public function getContinent( $idSite, $period, $date, $segment = false )
- {
- $recordName = Piwik_UserCountry::VISITS_BY_COUNTRY_RECORD_NAME;
- $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
-
- $getContinent = array('Piwik_Common', 'getContinent');
- $dataTable->filter('GroupBy', array('label', $getContinent));
-
- $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_ContinentTranslate'));
- $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'code'));
-
- return $dataTable;
- }
-
- /**
- * Returns visit information for every region with at least one visit.
- *
- * @param int|string $idSite
- * @param string $period
- * @param string $date
- * @param string|bool $segment
- * @return Piwik_DataTable
- */
- public function getRegion( $idSite, $period, $date, $segment = false )
- {
- $recordName = Piwik_UserCountry::VISITS_BY_REGION_RECORD_NAME;
- $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
-
- $separator = Piwik_UserCountry::LOCATION_SEPARATOR;
- $unk = Piwik_Tracker_Visit::UNKNOWN_CODE;
-
- // split the label and put the elements into the 'region' and 'country' metadata fields
- $dataTable->filter('ColumnCallbackAddMetadata',
- array('label', 'region', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 0, $unk)));
- $dataTable->filter('ColumnCallbackAddMetadata',
- array('label', 'country', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 1, $unk)));
-
- // add country name metadata
- $dataTable->filter('MetadataCallbackAddMetadata',
- array('country', 'country_name', 'Piwik_CountryTranslate', $applyToSummaryRow = false));
-
- // get the region name of each row and put it into the 'region_name' metadata
- $dataTable->filter('ColumnCallbackAddMetadata',
- array('label', 'region_name', 'Piwik_UserCountry_getRegionName', $params = null,
- $applyToSummaryRow = false));
-
- // add the country flag as a url to the 'logo' metadata field
- $dataTable->filter('MetadataCallbackAddMetadata', array('country', 'logo', 'Piwik_getFlagFromCode'));
-
- // prettify the region label
- $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_UserCountry_getPrettyRegionName'));
-
- $dataTable->queueFilter('ReplaceSummaryRowLabel');
-
- return $dataTable;
- }
-
- /**
- * Returns visit information for every city with at least one visit.
- *
- * @param int|string $idSite
- * @param string $period
- * @param string $date
- * @param string|bool $segment
- * @return Piwik_DataTable
- */
- public function getCity( $idSite, $period, $date, $segment = false )
- {
- $recordName = Piwik_UserCountry::VISITS_BY_CITY_RECORD_NAME;
- $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
-
- $separator = Piwik_UserCountry::LOCATION_SEPARATOR;
- $unk = Piwik_Tracker_Visit::UNKNOWN_CODE;
-
- // split the label and put the elements into the 'city_name', 'region', 'country',
- // 'lat' & 'long' metadata fields
- $strUnknown = Piwik_Translate('General_Unknown');
- $dataTable->filter('ColumnCallbackAddMetadata',
- array('label', 'city_name', 'Piwik_UserCountry_getElementFromStringArray',
- array($separator, 0, $strUnknown)));
- $dataTable->filter('MetadataCallbackAddMetadata',
- array('city_name', 'city', create_function('$city',' if ($city == "'.$strUnknown.'") { return "xx"; } else { return false; } ')));
- $dataTable->filter('ColumnCallbackAddMetadata',
- array('label', 'region', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 1, $unk)));
- $dataTable->filter('ColumnCallbackAddMetadata',
- array('label', 'country', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 2, $unk)));
-
- // backwards compatibility: for reports that have lat|long in label
- $dataTable->filter('ColumnCallbackAddMetadata',
- array('label', 'lat', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 3, false)));
- $dataTable->filter('ColumnCallbackAddMetadata',
- array('label', 'long', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 4, false)));
-
- // add country name & region name metadata
- $dataTable->filter('MetadataCallbackAddMetadata',
- array('country', 'country_name', 'Piwik_CountryTranslate', $applyToSummaryRow = false));
-
- $getRegionName = array('Piwik_UserCountry_LocationProvider_GeoIp', 'getRegionNameFromCodes');
- $dataTable->filter('MetadataCallbackAddMetadata', array(
- array('country', 'region'), 'region_name', $getRegionName, $applyToSummaryRow = false));
-
- // add the country flag as a url to the 'logo' metadata field
- $dataTable->filter('MetadataCallbackAddMetadata', array('country', 'logo', 'Piwik_getFlagFromCode'));
-
- // prettify the label
- $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_UserCountry_getPrettyCityName'));
-
- $dataTable->queueFilter('ReplaceSummaryRowLabel');
-
- return $dataTable;
- }
-
- /**
- * Uses a location provider to find/guess the location of an IP address.
- *
- * See Piwik_UserCountry_LocationProvider::getLocation to see the details
- * of the result of this function.
- *
- * @param string $ip The IP address.
- * @param string|false $provider The ID of the provider to use or false to use the
- * currently configured one.
- */
- public function getLocationFromIP( $ip, $provider = false )
- {
- Piwik::checkUserHasSomeViewAccess();
-
- if ($provider === false)
- {
- $provider = Piwik_UserCountry_LocationProvider::getCurrentProviderId();
- }
-
- $oProvider = Piwik_UserCountry_LocationProvider::getProviderById($provider);
- if ($oProvider === false)
- {
- throw new Exception("Cannot find the '$provider' provider. It is either an invalid provider "
- . "ID or the ID of a provider that is not working.");
- }
-
- $location = $oProvider->getLocation(array('ip' => $ip));
- if (empty($location))
- {
- throw new Exception("Could not geolocate '$ip'!");
- }
- $location['ip'] = $ip;
- return $location;
- }
-
- protected function getDataTable($name, $idSite, $period, $date, $segment)
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- $dataTable = $archive->getDataTable($name);
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
- $dataTable->queueFilter('ReplaceColumnNames');
- return $dataTable;
- }
-
- public function getNumberOfDistinctCountries($idSite, $period, $date, $segment = false)
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- return $archive->getDataTableFromNumeric('UserCountry_distinctCountries');
- }
+ static private $instance = null;
+
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ public function getCountry($idSite, $period, $date, $segment = false)
+ {
+ $recordName = Piwik_UserCountry::VISITS_BY_COUNTRY_RECORD_NAME;
+ $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
+
+ // apply filter on the whole datatable in order the inline search to work (searches
+ // are done on "beautiful" label)
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'code'));
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getFlagFromCode'));
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_CountryTranslate'));
+ $dataTable->queueFilter('AddConstantMetadata', array('logoWidth', 16));
+ $dataTable->queueFilter('AddConstantMetadata', array('logoHeight', 11));
+
+ return $dataTable;
+ }
+
+ public function getContinent($idSite, $period, $date, $segment = false)
+ {
+ $recordName = Piwik_UserCountry::VISITS_BY_COUNTRY_RECORD_NAME;
+ $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
+
+ $getContinent = array('Piwik_Common', 'getContinent');
+ $dataTable->filter('GroupBy', array('label', $getContinent));
+
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_ContinentTranslate'));
+ $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'code'));
+
+ return $dataTable;
+ }
+
+ /**
+ * Returns visit information for every region with at least one visit.
+ *
+ * @param int|string $idSite
+ * @param string $period
+ * @param string $date
+ * @param string|bool $segment
+ * @return Piwik_DataTable
+ */
+ public function getRegion($idSite, $period, $date, $segment = false)
+ {
+ $recordName = Piwik_UserCountry::VISITS_BY_REGION_RECORD_NAME;
+ $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
+
+ $separator = Piwik_UserCountry::LOCATION_SEPARATOR;
+ $unk = Piwik_Tracker_Visit::UNKNOWN_CODE;
+
+ // split the label and put the elements into the 'region' and 'country' metadata fields
+ $dataTable->filter('ColumnCallbackAddMetadata',
+ array('label', 'region', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 0, $unk)));
+ $dataTable->filter('ColumnCallbackAddMetadata',
+ array('label', 'country', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 1, $unk)));
+
+ // add country name metadata
+ $dataTable->filter('MetadataCallbackAddMetadata',
+ array('country', 'country_name', 'Piwik_CountryTranslate', $applyToSummaryRow = false));
+
+ // get the region name of each row and put it into the 'region_name' metadata
+ $dataTable->filter('ColumnCallbackAddMetadata',
+ array('label', 'region_name', 'Piwik_UserCountry_getRegionName', $params = null,
+ $applyToSummaryRow = false));
+
+ // add the country flag as a url to the 'logo' metadata field
+ $dataTable->filter('MetadataCallbackAddMetadata', array('country', 'logo', 'Piwik_getFlagFromCode'));
+
+ // prettify the region label
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_UserCountry_getPrettyRegionName'));
+
+ $dataTable->queueFilter('ReplaceSummaryRowLabel');
+
+ return $dataTable;
+ }
+
+ /**
+ * Returns visit information for every city with at least one visit.
+ *
+ * @param int|string $idSite
+ * @param string $period
+ * @param string $date
+ * @param string|bool $segment
+ * @return Piwik_DataTable
+ */
+ public function getCity($idSite, $period, $date, $segment = false)
+ {
+ $recordName = Piwik_UserCountry::VISITS_BY_CITY_RECORD_NAME;
+ $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
+
+ $separator = Piwik_UserCountry::LOCATION_SEPARATOR;
+ $unk = Piwik_Tracker_Visit::UNKNOWN_CODE;
+
+ // split the label and put the elements into the 'city_name', 'region', 'country',
+ // 'lat' & 'long' metadata fields
+ $strUnknown = Piwik_Translate('General_Unknown');
+ $dataTable->filter('ColumnCallbackAddMetadata',
+ array('label', 'city_name', 'Piwik_UserCountry_getElementFromStringArray',
+ array($separator, 0, $strUnknown)));
+ $dataTable->filter('MetadataCallbackAddMetadata',
+ array('city_name', 'city', create_function('$city', ' if ($city == "' . $strUnknown . '") { return "xx"; } else { return false; } ')));
+ $dataTable->filter('ColumnCallbackAddMetadata',
+ array('label', 'region', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 1, $unk)));
+ $dataTable->filter('ColumnCallbackAddMetadata',
+ array('label', 'country', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 2, $unk)));
+
+ // backwards compatibility: for reports that have lat|long in label
+ $dataTable->filter('ColumnCallbackAddMetadata',
+ array('label', 'lat', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 3, false)));
+ $dataTable->filter('ColumnCallbackAddMetadata',
+ array('label', 'long', 'Piwik_UserCountry_getElementFromStringArray', array($separator, 4, false)));
+
+ // add country name & region name metadata
+ $dataTable->filter('MetadataCallbackAddMetadata',
+ array('country', 'country_name', 'Piwik_CountryTranslate', $applyToSummaryRow = false));
+
+ $getRegionName = array('Piwik_UserCountry_LocationProvider_GeoIp', 'getRegionNameFromCodes');
+ $dataTable->filter('MetadataCallbackAddMetadata', array(
+ array('country', 'region'), 'region_name', $getRegionName, $applyToSummaryRow = false));
+
+ // add the country flag as a url to the 'logo' metadata field
+ $dataTable->filter('MetadataCallbackAddMetadata', array('country', 'logo', 'Piwik_getFlagFromCode'));
+
+ // prettify the label
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_UserCountry_getPrettyCityName'));
+
+ $dataTable->queueFilter('ReplaceSummaryRowLabel');
+
+ return $dataTable;
+ }
+
+ /**
+ * Uses a location provider to find/guess the location of an IP address.
+ *
+ * See Piwik_UserCountry_LocationProvider::getLocation to see the details
+ * of the result of this function.
+ *
+ * @param string $ip The IP address.
+ * @param string|false $provider The ID of the provider to use or false to use the
+ * currently configured one.
+ */
+ public function getLocationFromIP($ip, $provider = false)
+ {
+ Piwik::checkUserHasSomeViewAccess();
+
+ if ($provider === false) {
+ $provider = Piwik_UserCountry_LocationProvider::getCurrentProviderId();
+ }
+
+ $oProvider = Piwik_UserCountry_LocationProvider::getProviderById($provider);
+ if ($oProvider === false) {
+ throw new Exception("Cannot find the '$provider' provider. It is either an invalid provider "
+ . "ID or the ID of a provider that is not working.");
+ }
+
+ $location = $oProvider->getLocation(array('ip' => $ip));
+ if (empty($location)) {
+ throw new Exception("Could not geolocate '$ip'!");
+ }
+ $location['ip'] = $ip;
+ return $location;
+ }
+
+ protected function getDataTable($name, $idSite, $period, $date, $segment)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ $dataTable = $archive->getDataTable($name);
+ $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
+ $dataTable->queueFilter('ReplaceColumnNames');
+ return $dataTable;
+ }
+
+ public function getNumberOfDistinctCountries($idSite, $period, $date, $segment = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ return $archive->getDataTableFromNumeric('UserCountry_distinctCountries');
+ }
}
diff --git a/plugins/UserCountry/Controller.php b/plugins/UserCountry/Controller.php
index 01d7066fe5..4e305d27c7 100644
--- a/plugins/UserCountry/Controller.php
+++ b/plugins/UserCountry/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserCountry
*/
@@ -15,494 +15,467 @@
*/
class Piwik_UserCountry_Controller extends Piwik_Controller_Admin
{
- function index()
- {
- $view = Piwik_View::factory('index');
-
- $view->urlSparklineCountries = $this->getUrlSparkline('getLastDistinctCountriesGraph');
- $view->numberDistinctCountries = $this->getNumberOfDistinctCountries(true);
-
- $view->dataTableCountry = $this->getCountry(true);
- $view->dataTableContinent = $this->getContinent(true);
- $view->dataTableRegion = $this->getRegion(true);
- $view->dataTableCity = $this->getCity(true);
-
- echo $view->render();
- }
-
- function adminIndex()
- {
- Piwik::checkUserIsSuperUser();
- $view = Piwik_View::factory('adminIndex');
-
- $allProviderInfo = Piwik_UserCountry_LocationProvider::getAllProviderInfo(
- $newline = '<br/>', $includeExtra = true);
- $view->locationProviders = $allProviderInfo;
- $view->currentProviderId = Piwik_UserCountry_LocationProvider::getCurrentProviderId();
- $view->thisIP = Piwik_IP::getIpFromHeader();
- $geoIPDatabasesInstalled = Piwik_UserCountry_LocationProvider_GeoIp::isDatabaseInstalled();
- $view->geoIPDatabasesInstalled = $geoIPDatabasesInstalled;
-
- // check if there is a working provider (that isn't the default one)
- $isThereWorkingProvider = false;
- foreach ($allProviderInfo as $id => $provider)
- {
- if ($id != Piwik_UserCountry_LocationProvider_Default::ID
- && $provider['status'] == Piwik_UserCountry_LocationProvider::INSTALLED)
- {
- $isThereWorkingProvider = true;
- break;
- }
- }
- $view->isThereWorkingProvider = $isThereWorkingProvider;
-
- // if using either the Apache or PECL module, they are working and there are no databases
- // in misc, then the databases are located outside of Piwik, so we cannot update them
- $view->showGeoIPUpdateSection = true;
- $currentProviderId = Piwik_UserCountry_LocationProvider::getCurrentProviderId();
- if (!$geoIPDatabasesInstalled
- && ($currentProviderId == Piwik_UserCountry_LocationProvider_GeoIp_ServerBased::ID
- || $currentProviderId == Piwik_UserCountry_LocationProvider_GeoIp_Pecl::ID)
- && $allProviderInfo[$currentProviderId]['status'] == Piwik_UserCountry_LocationProvider::INSTALLED)
- {
- $view->showGeoIPUpdateSection = false;
- }
-
- $this->setUpdaterManageVars($view);
- $this->setBasicVariablesView($view);
- Piwik_Controller_Admin::setBasicVariablesAdminView($view);
- $view->menu = Piwik_GetAdminMenu();
-
- echo $view->render();
- }
-
- /**
- * Starts or continues download of GeoLiteCity.dat.
- *
- * To avoid a server/PHP timeout & to show progress of the download to the user, we
- * use the HTTP Range header to download one chunk of the file at a time. After each
- * chunk, it is the browser's responsibility to call the method again to continue the download.
- *
- * Input:
- * 'continue' query param - if set to 1, will assume we are currently downloading & use
- * Range: HTTP header to get another chunk of the file.
- *
- * Output (in JSON):
- * 'current_size' - Current size of the partially downloaded file on disk.
- * 'expected_file_size' - The expected finished file size as returned by the HTTP server.
- * 'next_screen' - When the download finishes, this is the next screen that should be shown.
- * 'error' - When an error occurs, the message is returned in this property.
- */
- public function downloadFreeGeoIPDB()
- {
- Piwik::checkUserIsSuperUser();
- if ($_SERVER["REQUEST_METHOD"] == "POST")
- {
- $this->checkTokenInUrl();
- Piwik_DataTable_Renderer_Json::sendHeaderJSON();
- $outputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase('GeoIPCity.dat').'.gz';
- try
- {
- $result = Piwik_Http::downloadChunk(
- $url = Piwik_UserCountry_LocationProvider_GeoIp::GEO_LITE_URL,
- $outputPath,
- $continue = Piwik_Common::getRequestVar('continue', true, 'int')
- );
-
- // if the file is done
- if ($result['current_size'] >= $result['expected_file_size'])
- {
- Piwik_UserCountry_GeoIPAutoUpdater::unzipDownloadedFile($outputPath, $unlink = true);
-
- // setup the auto updater
- Piwik_UserCountry_GeoIPAutoUpdater::setUpdaterOptions(array(
- 'loc_db' => Piwik_UserCountry_LocationProvider_GeoIp::GEO_LITE_URL,
- 'period' => Piwik_UserCountry_GeoIPAutoUpdater::SCHEDULE_PERIOD_MONTHLY,
- ));
-
- // make sure to echo out the geoip updater management screen
- $result['next_screen'] = $this->getGeoIpUpdaterManageScreen();
- }
-
- echo Piwik_Common::json_encode($result);
- }
- catch (Exception $ex)
- {
- echo Piwik_Common::json_encode(array('error' => $ex->getMessage()));
- }
- }
- }
-
- /**
- * Renders and returns the HTML that manages the GeoIP auto-updater.
- *
- * @return string
- */
- private function getGeoIpUpdaterManageScreen()
- {
- $view = Piwik_View::factory('updaterSetup');
- $view->geoIPDatabasesInstalled = true;
- $this->setUpdaterManageVars($view);
- return $view->render();
- }
-
- /**
- * Sets some variables needed by the updaterSetup.tpl template.
- *
- * @param Piwik_View $view
- */
- private function setUpdaterManageVars( $view )
- {
- $urls = Piwik_UserCountry_GeoIPAutoUpdater::getConfiguredUrls();
-
- $view->geoIPLocUrl = $urls['loc'];
- $view->geoIPIspUrl = $urls['isp'];
- $view->geoIPOrgUrl = $urls['org'];
- $view->geoIPUpdatePeriod = Piwik_UserCountry_GeoIPAutoUpdater::getSchedulePeriod();
-
- $view->geoLiteUrl = Piwik_UserCountry_LocationProvider_GeoIp::GEO_LITE_URL;
-
- $lastRunTime = Piwik_UserCountry_GeoIPAutoUpdater::getLastRunTime();
- if ($lastRunTime !== false)
- {
- $view->lastTimeUpdaterRun = '<strong><em>'.$lastRunTime->toString().'</em></strong>';
- }
- }
-
- /**
- * Sets the URLs used to download new versions of the installed GeoIP databases.
- *
- * Input (query params):
- * 'loc_db' - URL for a GeoIP location database.
- * 'isp_db' - URL for a GeoIP ISP database (optional).
- * 'org_db' - URL for a GeoIP Org database (optional).
- * 'period' - 'weekly' or 'monthly'. Determines how often update is run.
- *
- * Output (json):
- * 'error' - if an error occurs its message is set as the resulting JSON object's
- * 'error' property.
- */
- public function updateGeoIPLinks()
- {
- Piwik::checkUserIsSuperUser();
- if ($_SERVER["REQUEST_METHOD"] == "POST")
- {
- Piwik_DataTable_Renderer_Json::sendHeaderJSON();
- try
- {
- $this->checkTokenInUrl();
-
- Piwik_UserCountry_GeoIPAutoUpdater::setUpdaterOptionsFromUrl();
-
- // if there is a updater URL for a database, but its missing from the misc dir, tell
- // the browser so it can download it next
- $info = $this->getNextMissingDbUrlInfo();
- if ($info !== false)
- {
- echo Piwik_Common::json_encode($info);
- return;
- }
- }
- catch (Exception $ex)
- {
- echo Piwik_Common::json_encode(array('error' => $ex->getMessage()));
- }
- }
- }
-
- /**
- * Starts or continues a download for a missing GeoIP database. A database is missing if
- * it has an update URL configured, but the actual database is not available in the misc
- * directory.
- *
- * Input:
- * 'url' - The URL to download the database from.
- * 'continue' - 1 if we're continuing a download, 0 if we're starting one.
- *
- * Output:
- * 'error' - If an error occurs this describes the error.
- * 'to_download' - The URL of a missing database that should be downloaded next (if any).
- * 'to_download_label' - The label to use w/ the progress bar that describes what we're
- * downloading.
- * 'current_size' - Size of the current file on disk.
- * 'expected_file_size' - Size of the completely downloaded file.
- */
- public function downloadMissingGeoIpDb()
- {
- Piwik::checkUserIsSuperUser();
- if ($_SERVER["REQUEST_METHOD"] == "POST")
- {
- try
- {
- $this->checkTokenInUrl();
-
- Piwik_DataTable_Renderer_Json::sendHeaderJSON();
-
- // based on the database type (provided by the 'key' query param) determine the
- // url & output file name
- $key = Piwik_Common::getRequestVar('key', null, 'string');
- $url = Piwik_UserCountry_GeoIPAutoUpdater::getConfiguredUrl($key);
-
- $ext = Piwik_UserCountry_GeoIPAutoUpdater::getGeoIPUrlExtension($url);
- $filename = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$key][0].'.'.$ext;
-
- if (substr($filename, 0, 15) == 'GeoLiteCity.dat')
- {
- $filename = 'GeoIPCity.dat'.substr($filename, 15);
- }
- $outputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($filename);
-
- // download part of the file
- $result = Piwik_Http::downloadChunk(
- $url, $outputPath, Piwik_Common::getRequestVar('continue', true, 'int'));
-
- // if the file is done
- if ($result['current_size'] >= $result['expected_file_size'])
- {
- Piwik_UserCountry_GeoIPAutoUpdater::unzipDownloadedFile($outputPath, $unlink = true);
-
- $info = $this->getNextMissingDbUrlInfo();
- if ($info !== false)
- {
- echo Piwik_Common::json_encode($info);
- return;
- }
- }
-
- echo Piwik_Common::json_encode($result);
- }
- catch (Exception $ex)
- {
- echo Piwik_Common::json_encode(array('error' => $ex->getMessage()));
- }
- }
- }
-
- /**
- * Sets the current LocationProvider type.
- *
- * Input:
- * Requires the 'id' query parameter to be set to the desired LocationProvider's ID.
- *
- * Output:
- * Nothing.
- */
- public function setCurrentLocationProvider()
- {
- Piwik::checkUserIsSuperUser();
- if ($_SERVER["REQUEST_METHOD"] == "POST")
- {
- $this->checkTokenInUrl();
-
- $providerId = Piwik_Common::getRequestVar('id');
- $provider = Piwik_UserCountry_LocationProvider::setCurrentProvider($providerId);
- if ($provider === false)
- {
- throw new Exception("Invalid provider ID: '$providerId'.");
- }
- }
- }
-
- /**
- * Echo's a pretty formatted location using a specific LocationProvider.
- *
- * Input:
- * The 'id' query parameter must be set to the ID of the LocationProvider to use.
- *
- * Output:
- * The pretty formatted location that was obtained. Will be HTML.
- */
- public function getLocationUsingProvider()
- {
- $providerId = Piwik_Common::getRequestVar('id');
- $provider = $provider = Piwik_UserCountry_LocationProvider::getProviderById($providerId);
- if ($provider === false)
- {
- throw new Exception("Invalid provider ID: '$providerId'.");
- }
-
- $location = $provider->getLocation(array('ip' => Piwik_IP::getIpFromHeader(),
- 'lang' => Piwik_Common::getBrowserLanguage(),
- 'disable_fallbacks' => true));
- $location = Piwik_UserCountry_LocationProvider::prettyFormatLocation(
- $location, $newline = '<br/>', $includeExtra = true);
-
- echo $location;
- }
-
- function getCountry( $fetch = false)
- {
- $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getCountry");
- $view->setLimit( 5 );
- $view->setColumnTranslation('label', Piwik_Translate('UserCountry_Country'));
- $view->setReportDocumentation(Piwik_Translate('UserCountry_getCountryDocumentation'));
- return $this->renderView($view, $fetch);
- }
-
- function getContinent( $fetch = false)
- {
- $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getContinent", 'table');
- $view->disableSearchBox();
- $view->disableOffsetInformationAndPaginationControls();
- $view->setColumnTranslation('label', Piwik_Translate('UserCountry_Continent'));
- $view->setReportDocumentation(Piwik_Translate('UserCountry_getContinentDocumentation'));
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Echo's or returns an HTML view of the visits by region report.
- *
- * @param bool $fetch If true, returns the HTML as a string, otherwise it is echo'd.
- * @return string
- */
- public function getRegion( $fetch = false )
- {
- $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getRegion");
- $view->setLimit(5);
- $view->setColumnTranslation('label', Piwik_Translate('UserCountry_Region'));
- $view->setReportDocumentation(Piwik_Translate('UserCountry_getRegionDocumentation').'<br/>'
- . $this->getGeoIPReportDocSuffix());
- $this->checkIfNoDataForGeoIpReport($view);
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Echo's or returns an HTML view of the visits by city report.
- *
- * @param bool $fetch If true, returns the HTML as a string, otherwise it is echo'd.
- * @return string
- */
- public function getCity( $fetch = false )
- {
- $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getCity");
- $view->setLimit(5);
- $view->setColumnTranslation('label', Piwik_Translate('UserCountry_City'));
- $view->setReportDocumentation(Piwik_Translate('UserCountry_getCityDocumentation').'<br/>'
- . $this->getGeoIPReportDocSuffix());
- $this->checkIfNoDataForGeoIpReport($view);
- return $this->renderView($view, $fetch);
- }
-
- private function getGeoIPReportDocSuffix()
- {
- return Piwik_Translate('UserCountry_GeoIPDocumentationSuffix', array(
- '<a target="_blank" href="http://www.maxmind.com/?rId=piwik">',
- '</a>',
- '<a target="_blank" href="http://www.maxmind.com/en/city_accuracy?rId=piwik">',
- '</a>'
- ));
- }
-
- protected function getStandardDataTableUserCountry( $currentControllerAction,
- $APItoCall,
- $defaultDatatableType = null )
- {
- $view = Piwik_ViewDataTable::factory( $defaultDatatableType );
- $view->init( $this->pluginName, $currentControllerAction, $APItoCall );
- $view->disableExcludeLowPopulation();
-
- $this->setPeriodVariablesView($view);
- $this->setMetricsVariablesView($view);
-
- $view->enableShowGoals();
-
- return $view;
- }
-
- function getNumberOfDistinctCountries( $fetch = false)
- {
- return $this->getNumericValue('UserCountry.getNumberOfDistinctCountries');
- }
-
- function getLastDistinctCountriesGraph( $fetch = false )
- {
- $view = $this->getLastUnitGraph('UserCountry',__FUNCTION__, "UserCountry.getNumberOfDistinctCountries");
- $view->setColumnsToDisplay('UserCountry_distinctCountries');
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Checks if a datatable for a view is empty and if so, displays a message in the footer
- * telling users to configure GeoIP.
- */
- private function checkIfNoDataForGeoIpReport( $view )
- {
- // only display on HTML tables since the datatable for HTML graphs aren't accessible
- if (!($view instanceof Piwik_ViewDataTable_HtmlTable))
- {
- return;
- }
-
- // if there's only one row whose label is 'Unknown', display a message saying there's no data
- $view->main();
- $dataTable = $view->getDataTable();
- if ($dataTable->getRowsCount() == 1
- && $dataTable->getFirstRow()->getColumn('label') == Piwik_Translate('General_Unknown'))
- {
- $footerMessage = Piwik_Translate('UserCountry_NoDataForGeoIPReport1');
-
- // if GeoIP is working, don't display this part of the message
- if (!$this->isGeoIPWorking())
- {
- $params = array('module' => 'UserCountry', 'action' => 'adminIndex');
- $footerMessage .= ' '.Piwik_Translate('UserCountry_NoDataForGeoIPReport2', array(
- '<a target="_blank" href="'.Piwik_Url::getCurrentQueryStringWithParametersModified($params).'">',
- '</a>',
- '<a target="_blank" href="http://dev.maxmind.com/geoip/geolite?rId=piwik">',
- '</a>'
- ));
- }
- else
- {
- $footerMessage .= ' '.Piwik_Translate('UserCountry_ToGeolocateOldVisits', array(
- '<a target="_blank" href="http://piwik.org/faq/how-to/#faq_167">',
- '</a>'
- ));
- }
-
- // HACK! Can't use setFooterMessage because the view gets built in the main function,
- // so instead we set the property by hand.
- $realView = $view->getView();
- $properties = $realView->properties;
- $properties['show_footer_message'] = $footerMessage;
- $realView->properties = $properties;
- }
- }
-
- /**
- * Gets information for the first missing GeoIP database (if any).
- *
- * @return bool
- */
- private function getNextMissingDbUrlInfo()
- {
- $missingDbs = Piwik_UserCountry_GeoIPAutoUpdater::getMissingDatabases();
- if (!empty($missingDbs))
- {
- $missingDbKey = $missingDbs[0];
- $missingDbName = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$missingDbKey][0];
- $url = Piwik_UserCountry_GeoIPAutoUpdater::getConfiguredUrl($missingDbKey);
-
- $link = '<a href="'.$url.'">'.$missingDbName.'</a>';
-
- return array(
- 'to_download' => $missingDbKey,
- 'to_download_label' => Piwik_Translate('UserCountry_DownloadingDb', $link).'...',
- );
- }
- return false;
- }
-
- /**
- * Returns true if a GeoIP provider is installed & working, false if otherwise.
- *
- * @return bool
- */
- private function isGeoIPWorking()
- {
- $provider = Piwik_UserCountry_LocationProvider::getCurrentProvider();
- return $provider instanceof Piwik_UserCountry_LocationProvider_GeoIp
- && $provider->isAvailable() === true
- && $provider->isWorking() === true;
- }
+ function index()
+ {
+ $view = Piwik_View::factory('index');
+
+ $view->urlSparklineCountries = $this->getUrlSparkline('getLastDistinctCountriesGraph');
+ $view->numberDistinctCountries = $this->getNumberOfDistinctCountries(true);
+
+ $view->dataTableCountry = $this->getCountry(true);
+ $view->dataTableContinent = $this->getContinent(true);
+ $view->dataTableRegion = $this->getRegion(true);
+ $view->dataTableCity = $this->getCity(true);
+
+ echo $view->render();
+ }
+
+ function adminIndex()
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = Piwik_View::factory('adminIndex');
+
+ $allProviderInfo = Piwik_UserCountry_LocationProvider::getAllProviderInfo(
+ $newline = '<br/>', $includeExtra = true);
+ $view->locationProviders = $allProviderInfo;
+ $view->currentProviderId = Piwik_UserCountry_LocationProvider::getCurrentProviderId();
+ $view->thisIP = Piwik_IP::getIpFromHeader();
+ $geoIPDatabasesInstalled = Piwik_UserCountry_LocationProvider_GeoIp::isDatabaseInstalled();
+ $view->geoIPDatabasesInstalled = $geoIPDatabasesInstalled;
+
+ // check if there is a working provider (that isn't the default one)
+ $isThereWorkingProvider = false;
+ foreach ($allProviderInfo as $id => $provider) {
+ if ($id != Piwik_UserCountry_LocationProvider_Default::ID
+ && $provider['status'] == Piwik_UserCountry_LocationProvider::INSTALLED
+ ) {
+ $isThereWorkingProvider = true;
+ break;
+ }
+ }
+ $view->isThereWorkingProvider = $isThereWorkingProvider;
+
+ // if using either the Apache or PECL module, they are working and there are no databases
+ // in misc, then the databases are located outside of Piwik, so we cannot update them
+ $view->showGeoIPUpdateSection = true;
+ $currentProviderId = Piwik_UserCountry_LocationProvider::getCurrentProviderId();
+ if (!$geoIPDatabasesInstalled
+ && ($currentProviderId == Piwik_UserCountry_LocationProvider_GeoIp_ServerBased::ID
+ || $currentProviderId == Piwik_UserCountry_LocationProvider_GeoIp_Pecl::ID)
+ && $allProviderInfo[$currentProviderId]['status'] == Piwik_UserCountry_LocationProvider::INSTALLED
+ ) {
+ $view->showGeoIPUpdateSection = false;
+ }
+
+ $this->setUpdaterManageVars($view);
+ $this->setBasicVariablesView($view);
+ Piwik_Controller_Admin::setBasicVariablesAdminView($view);
+ $view->menu = Piwik_GetAdminMenu();
+
+ echo $view->render();
+ }
+
+ /**
+ * Starts or continues download of GeoLiteCity.dat.
+ *
+ * To avoid a server/PHP timeout & to show progress of the download to the user, we
+ * use the HTTP Range header to download one chunk of the file at a time. After each
+ * chunk, it is the browser's responsibility to call the method again to continue the download.
+ *
+ * Input:
+ * 'continue' query param - if set to 1, will assume we are currently downloading & use
+ * Range: HTTP header to get another chunk of the file.
+ *
+ * Output (in JSON):
+ * 'current_size' - Current size of the partially downloaded file on disk.
+ * 'expected_file_size' - The expected finished file size as returned by the HTTP server.
+ * 'next_screen' - When the download finishes, this is the next screen that should be shown.
+ * 'error' - When an error occurs, the message is returned in this property.
+ */
+ public function downloadFreeGeoIPDB()
+ {
+ Piwik::checkUserIsSuperUser();
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ $this->checkTokenInUrl();
+ Piwik_DataTable_Renderer_Json::sendHeaderJSON();
+ $outputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase('GeoIPCity.dat') . '.gz';
+ try {
+ $result = Piwik_Http::downloadChunk(
+ $url = Piwik_UserCountry_LocationProvider_GeoIp::GEO_LITE_URL,
+ $outputPath,
+ $continue = Piwik_Common::getRequestVar('continue', true, 'int')
+ );
+
+ // if the file is done
+ if ($result['current_size'] >= $result['expected_file_size']) {
+ Piwik_UserCountry_GeoIPAutoUpdater::unzipDownloadedFile($outputPath, $unlink = true);
+
+ // setup the auto updater
+ Piwik_UserCountry_GeoIPAutoUpdater::setUpdaterOptions(array(
+ 'loc_db' => Piwik_UserCountry_LocationProvider_GeoIp::GEO_LITE_URL,
+ 'period' => Piwik_UserCountry_GeoIPAutoUpdater::SCHEDULE_PERIOD_MONTHLY,
+ ));
+
+ // make sure to echo out the geoip updater management screen
+ $result['next_screen'] = $this->getGeoIpUpdaterManageScreen();
+ }
+
+ echo Piwik_Common::json_encode($result);
+ } catch (Exception $ex) {
+ echo Piwik_Common::json_encode(array('error' => $ex->getMessage()));
+ }
+ }
+ }
+
+ /**
+ * Renders and returns the HTML that manages the GeoIP auto-updater.
+ *
+ * @return string
+ */
+ private function getGeoIpUpdaterManageScreen()
+ {
+ $view = Piwik_View::factory('updaterSetup');
+ $view->geoIPDatabasesInstalled = true;
+ $this->setUpdaterManageVars($view);
+ return $view->render();
+ }
+
+ /**
+ * Sets some variables needed by the updaterSetup.tpl template.
+ *
+ * @param Piwik_View $view
+ */
+ private function setUpdaterManageVars($view)
+ {
+ $urls = Piwik_UserCountry_GeoIPAutoUpdater::getConfiguredUrls();
+
+ $view->geoIPLocUrl = $urls['loc'];
+ $view->geoIPIspUrl = $urls['isp'];
+ $view->geoIPOrgUrl = $urls['org'];
+ $view->geoIPUpdatePeriod = Piwik_UserCountry_GeoIPAutoUpdater::getSchedulePeriod();
+
+ $view->geoLiteUrl = Piwik_UserCountry_LocationProvider_GeoIp::GEO_LITE_URL;
+
+ $lastRunTime = Piwik_UserCountry_GeoIPAutoUpdater::getLastRunTime();
+ if ($lastRunTime !== false) {
+ $view->lastTimeUpdaterRun = '<strong><em>' . $lastRunTime->toString() . '</em></strong>';
+ }
+ }
+
+ /**
+ * Sets the URLs used to download new versions of the installed GeoIP databases.
+ *
+ * Input (query params):
+ * 'loc_db' - URL for a GeoIP location database.
+ * 'isp_db' - URL for a GeoIP ISP database (optional).
+ * 'org_db' - URL for a GeoIP Org database (optional).
+ * 'period' - 'weekly' or 'monthly'. Determines how often update is run.
+ *
+ * Output (json):
+ * 'error' - if an error occurs its message is set as the resulting JSON object's
+ * 'error' property.
+ */
+ public function updateGeoIPLinks()
+ {
+ Piwik::checkUserIsSuperUser();
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ Piwik_DataTable_Renderer_Json::sendHeaderJSON();
+ try {
+ $this->checkTokenInUrl();
+
+ Piwik_UserCountry_GeoIPAutoUpdater::setUpdaterOptionsFromUrl();
+
+ // if there is a updater URL for a database, but its missing from the misc dir, tell
+ // the browser so it can download it next
+ $info = $this->getNextMissingDbUrlInfo();
+ if ($info !== false) {
+ echo Piwik_Common::json_encode($info);
+ return;
+ }
+ } catch (Exception $ex) {
+ echo Piwik_Common::json_encode(array('error' => $ex->getMessage()));
+ }
+ }
+ }
+
+ /**
+ * Starts or continues a download for a missing GeoIP database. A database is missing if
+ * it has an update URL configured, but the actual database is not available in the misc
+ * directory.
+ *
+ * Input:
+ * 'url' - The URL to download the database from.
+ * 'continue' - 1 if we're continuing a download, 0 if we're starting one.
+ *
+ * Output:
+ * 'error' - If an error occurs this describes the error.
+ * 'to_download' - The URL of a missing database that should be downloaded next (if any).
+ * 'to_download_label' - The label to use w/ the progress bar that describes what we're
+ * downloading.
+ * 'current_size' - Size of the current file on disk.
+ * 'expected_file_size' - Size of the completely downloaded file.
+ */
+ public function downloadMissingGeoIpDb()
+ {
+ Piwik::checkUserIsSuperUser();
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ try {
+ $this->checkTokenInUrl();
+
+ Piwik_DataTable_Renderer_Json::sendHeaderJSON();
+
+ // based on the database type (provided by the 'key' query param) determine the
+ // url & output file name
+ $key = Piwik_Common::getRequestVar('key', null, 'string');
+ $url = Piwik_UserCountry_GeoIPAutoUpdater::getConfiguredUrl($key);
+
+ $ext = Piwik_UserCountry_GeoIPAutoUpdater::getGeoIPUrlExtension($url);
+ $filename = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$key][0] . '.' . $ext;
+
+ if (substr($filename, 0, 15) == 'GeoLiteCity.dat') {
+ $filename = 'GeoIPCity.dat' . substr($filename, 15);
+ }
+ $outputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($filename);
+
+ // download part of the file
+ $result = Piwik_Http::downloadChunk(
+ $url, $outputPath, Piwik_Common::getRequestVar('continue', true, 'int'));
+
+ // if the file is done
+ if ($result['current_size'] >= $result['expected_file_size']) {
+ Piwik_UserCountry_GeoIPAutoUpdater::unzipDownloadedFile($outputPath, $unlink = true);
+
+ $info = $this->getNextMissingDbUrlInfo();
+ if ($info !== false) {
+ echo Piwik_Common::json_encode($info);
+ return;
+ }
+ }
+
+ echo Piwik_Common::json_encode($result);
+ } catch (Exception $ex) {
+ echo Piwik_Common::json_encode(array('error' => $ex->getMessage()));
+ }
+ }
+ }
+
+ /**
+ * Sets the current LocationProvider type.
+ *
+ * Input:
+ * Requires the 'id' query parameter to be set to the desired LocationProvider's ID.
+ *
+ * Output:
+ * Nothing.
+ */
+ public function setCurrentLocationProvider()
+ {
+ Piwik::checkUserIsSuperUser();
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ $this->checkTokenInUrl();
+
+ $providerId = Piwik_Common::getRequestVar('id');
+ $provider = Piwik_UserCountry_LocationProvider::setCurrentProvider($providerId);
+ if ($provider === false) {
+ throw new Exception("Invalid provider ID: '$providerId'.");
+ }
+ }
+ }
+
+ /**
+ * Echo's a pretty formatted location using a specific LocationProvider.
+ *
+ * Input:
+ * The 'id' query parameter must be set to the ID of the LocationProvider to use.
+ *
+ * Output:
+ * The pretty formatted location that was obtained. Will be HTML.
+ */
+ public function getLocationUsingProvider()
+ {
+ $providerId = Piwik_Common::getRequestVar('id');
+ $provider = $provider = Piwik_UserCountry_LocationProvider::getProviderById($providerId);
+ if ($provider === false) {
+ throw new Exception("Invalid provider ID: '$providerId'.");
+ }
+
+ $location = $provider->getLocation(array('ip' => Piwik_IP::getIpFromHeader(),
+ 'lang' => Piwik_Common::getBrowserLanguage(),
+ 'disable_fallbacks' => true));
+ $location = Piwik_UserCountry_LocationProvider::prettyFormatLocation(
+ $location, $newline = '<br/>', $includeExtra = true);
+
+ echo $location;
+ }
+
+ function getCountry($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getCountry");
+ $view->setLimit(5);
+ $view->setColumnTranslation('label', Piwik_Translate('UserCountry_Country'));
+ $view->setReportDocumentation(Piwik_Translate('UserCountry_getCountryDocumentation'));
+ return $this->renderView($view, $fetch);
+ }
+
+ function getContinent($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getContinent", 'table');
+ $view->disableSearchBox();
+ $view->disableOffsetInformationAndPaginationControls();
+ $view->setColumnTranslation('label', Piwik_Translate('UserCountry_Continent'));
+ $view->setReportDocumentation(Piwik_Translate('UserCountry_getContinentDocumentation'));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Echo's or returns an HTML view of the visits by region report.
+ *
+ * @param bool $fetch If true, returns the HTML as a string, otherwise it is echo'd.
+ * @return string
+ */
+ public function getRegion($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getRegion");
+ $view->setLimit(5);
+ $view->setColumnTranslation('label', Piwik_Translate('UserCountry_Region'));
+ $view->setReportDocumentation(Piwik_Translate('UserCountry_getRegionDocumentation') . '<br/>'
+ . $this->getGeoIPReportDocSuffix());
+ $this->checkIfNoDataForGeoIpReport($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Echo's or returns an HTML view of the visits by city report.
+ *
+ * @param bool $fetch If true, returns the HTML as a string, otherwise it is echo'd.
+ * @return string
+ */
+ public function getCity($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getCity");
+ $view->setLimit(5);
+ $view->setColumnTranslation('label', Piwik_Translate('UserCountry_City'));
+ $view->setReportDocumentation(Piwik_Translate('UserCountry_getCityDocumentation') . '<br/>'
+ . $this->getGeoIPReportDocSuffix());
+ $this->checkIfNoDataForGeoIpReport($view);
+ return $this->renderView($view, $fetch);
+ }
+
+ private function getGeoIPReportDocSuffix()
+ {
+ return Piwik_Translate('UserCountry_GeoIPDocumentationSuffix', array(
+ '<a target="_blank" href="http://www.maxmind.com/?rId=piwik">',
+ '</a>',
+ '<a target="_blank" href="http://www.maxmind.com/en/city_accuracy?rId=piwik">',
+ '</a>'
+ ));
+ }
+
+ protected function getStandardDataTableUserCountry($currentControllerAction,
+ $APItoCall,
+ $defaultDatatableType = null)
+ {
+ $view = Piwik_ViewDataTable::factory($defaultDatatableType);
+ $view->init($this->pluginName, $currentControllerAction, $APItoCall);
+ $view->disableExcludeLowPopulation();
+
+ $this->setPeriodVariablesView($view);
+ $this->setMetricsVariablesView($view);
+
+ $view->enableShowGoals();
+
+ return $view;
+ }
+
+ function getNumberOfDistinctCountries($fetch = false)
+ {
+ return $this->getNumericValue('UserCountry.getNumberOfDistinctCountries');
+ }
+
+ function getLastDistinctCountriesGraph($fetch = false)
+ {
+ $view = $this->getLastUnitGraph('UserCountry', __FUNCTION__, "UserCountry.getNumberOfDistinctCountries");
+ $view->setColumnsToDisplay('UserCountry_distinctCountries');
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Checks if a datatable for a view is empty and if so, displays a message in the footer
+ * telling users to configure GeoIP.
+ */
+ private function checkIfNoDataForGeoIpReport($view)
+ {
+ // only display on HTML tables since the datatable for HTML graphs aren't accessible
+ if (!($view instanceof Piwik_ViewDataTable_HtmlTable)) {
+ return;
+ }
+
+ // if there's only one row whose label is 'Unknown', display a message saying there's no data
+ $view->main();
+ $dataTable = $view->getDataTable();
+ if ($dataTable->getRowsCount() == 1
+ && $dataTable->getFirstRow()->getColumn('label') == Piwik_Translate('General_Unknown')
+ ) {
+ $footerMessage = Piwik_Translate('UserCountry_NoDataForGeoIPReport1');
+
+ // if GeoIP is working, don't display this part of the message
+ if (!$this->isGeoIPWorking()) {
+ $params = array('module' => 'UserCountry', 'action' => 'adminIndex');
+ $footerMessage .= ' ' . Piwik_Translate('UserCountry_NoDataForGeoIPReport2', array(
+ '<a target="_blank" href="' . Piwik_Url::getCurrentQueryStringWithParametersModified($params) . '">',
+ '</a>',
+ '<a target="_blank" href="http://dev.maxmind.com/geoip/geolite?rId=piwik">',
+ '</a>'
+ ));
+ } else {
+ $footerMessage .= ' ' . Piwik_Translate('UserCountry_ToGeolocateOldVisits', array(
+ '<a target="_blank" href="http://piwik.org/faq/how-to/#faq_167">',
+ '</a>'
+ ));
+ }
+
+ // HACK! Can't use setFooterMessage because the view gets built in the main function,
+ // so instead we set the property by hand.
+ $realView = $view->getView();
+ $properties = $realView->properties;
+ $properties['show_footer_message'] = $footerMessage;
+ $realView->properties = $properties;
+ }
+ }
+
+ /**
+ * Gets information for the first missing GeoIP database (if any).
+ *
+ * @return bool
+ */
+ private function getNextMissingDbUrlInfo()
+ {
+ $missingDbs = Piwik_UserCountry_GeoIPAutoUpdater::getMissingDatabases();
+ if (!empty($missingDbs)) {
+ $missingDbKey = $missingDbs[0];
+ $missingDbName = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$missingDbKey][0];
+ $url = Piwik_UserCountry_GeoIPAutoUpdater::getConfiguredUrl($missingDbKey);
+
+ $link = '<a href="' . $url . '">' . $missingDbName . '</a>';
+
+ return array(
+ 'to_download' => $missingDbKey,
+ 'to_download_label' => Piwik_Translate('UserCountry_DownloadingDb', $link) . '...',
+ );
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if a GeoIP provider is installed & working, false if otherwise.
+ *
+ * @return bool
+ */
+ private function isGeoIPWorking()
+ {
+ $provider = Piwik_UserCountry_LocationProvider::getCurrentProvider();
+ return $provider instanceof Piwik_UserCountry_LocationProvider_GeoIp
+ && $provider->isAvailable() === true
+ && $provider->isWorking() === true;
+ }
}
diff --git a/plugins/UserCountry/GeoIPAutoUpdater.php b/plugins/UserCountry/GeoIPAutoUpdater.php
index 1f29d8a1bb..a9aab90c63 100755
--- a/plugins/UserCountry/GeoIPAutoUpdater.php
+++ b/plugins/UserCountry/GeoIPAutoUpdater.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserCountry
*/
@@ -15,588 +15,540 @@
*/
class Piwik_UserCountry_GeoIPAutoUpdater
{
- const SCHEDULE_PERIOD_MONTHLY = 'month';
- const SCHEDULE_PERIOD_WEEKLY = 'week';
-
- const SCHEDULE_PERIOD_OPTION_NAME = 'geoip.updater_period';
- const LOC_URL_OPTION_NAME = 'geoip.loc_db_url';
- const ISP_URL_OPTION_NAME = 'geoip.isp_db_url';
- const ORG_URL_OPTION_NAME = 'geoip.org_db_url';
-
- const LAST_RUN_TIME_OPTION_NAME = 'geoip.updater_last_run_time';
-
- private static $urlOptions = array(
- 'loc' => self::LOC_URL_OPTION_NAME,
- 'isp' => self::ISP_URL_OPTION_NAME,
- 'org' => self::ORG_URL_OPTION_NAME,
- );
-
- /**
- * PHP Error caught through a custom error handler while trying to use a downloaded
- * GeoIP database. See catchGeoIPError for more info.
- *
- * @var array
- */
- private static $unzipPhpError = null;
-
- /**
- * Attempts to download new location, ISP & organization GeoIP databases and
- * replace the existing ones w/ them.
- */
- public function update()
- {
- try
- {
- Piwik_SetOption(self::LAST_RUN_TIME_OPTION_NAME, Piwik_Date::factory('today')->getTimestamp());
-
- $locUrl = Piwik_GetOption(self::LOC_URL_OPTION_NAME);
- if (!empty($locUrl))
- {
- $this->downloadFile('loc', $locUrl);
- }
-
- $ispUrl = Piwik_GetOption(self::ISP_URL_OPTION_NAME);
- if (!empty($ispUrl))
- {
- $this->downloadFile('isp', $ispUrl);
- }
-
- $orgUrl = Piwik_GetOption(self::ORG_URL_OPTION_NAME);
- if (!empty($orgUrl))
- {
- $this->downloadFile('org', $orgUrl);
- }
- }
- catch (Exception $ex)
- {
- // message will already be prefixed w/ 'Piwik_UserCountry_GeoIPAutoUpdater: '
- Piwik::log($ex->getMessage());
- $this->performRedundantDbChecks();
- throw $ex;
- }
-
- $this->performRedundantDbChecks();
- }
-
- /**
- * Downloads a GeoIP database archive, extracts the .dat file and overwrites the existing
- * old database.
- *
- * If something happens that causes the download to fail, no exception is thrown, but
- * an error is logged.
- *
- * @param string $url URL to the database to download. The type of database is determined
- * from this URL.
- */
- private function downloadFile( $dbType, $url )
- {
- $ext = Piwik_UserCountry_GeoIPAutoUpdater::getGeoIPUrlExtension($url);
- $zippedFilename = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$dbType][0].'.'.$ext;
-
- $zippedOutputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($zippedFilename);
-
- // download zipped file to misc dir
- try
- {
- $success = Piwik_Http::sendHttpRequest($url, $timeout = 3600, $userAgent = null, $zippedOutputPath);
- }
- catch (Exception $ex)
- {
- throw new Exception("Piwik_UserCountry_GeoIPAutoUpdater: failed to download '$url' to "
- . "'$zippedOutputPath': " . $ex->getMessage());
- }
-
- if ($success !== true)
- {
- throw new Exception("Piwik_UserCountry_GeoIPAutoUpdater: failed to download '$url' to "
- . "'$zippedOutputPath'! (Unknown error)");
- }
-
- Piwik::log(sprintf("Piwik_UserCountry_GeoIPAutoUpdater: successfully downloaded '%s'", $url));
-
- try
- {
- self::unzipDownloadedFile($zippedOutputPath, $unlink = true);
- }
- catch (Exception $ex)
- {
- throw new Exception("Piwik_UserCountry_GeoIPAutoUpdater: failed to unzip '$zippedOutputPath' after "
- . "downloading " . "'$url': ".$ex->getMessage());
- }
-
- Piwik::log(sprintf("Piwik_UserCountry_GeoIPAutoUpdater: successfully updated GeoIP database '%s'", $url));
- }
-
- /**
- * Unzips a downloaded GeoIP database. Only unzips .gz & .tar.gz files.
- *
- * @param string $path Path to zipped file.
- * @param bool $unlink Whether to unlink archive or not.
- */
- public static function unzipDownloadedFile( $path, $unlink = false )
- {
- $parts = explode('.', basename($path));
- $filenameStart = $parts[0];
-
- $dbFilename = $filenameStart.'.dat';
- $tempFilename = $filenameStart.'.dat.new';
- $outputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($tempFilename);
-
- // extract file
- if (substr($path, -7, 7) == '.tar.gz')
- {
- // find the .dat file in the tar archive
- $unzip = Piwik_Unzip::factory('tar.gz', $path);
- $content = $unzip->listContent();
-
- if (empty($content))
- {
- throw new Exception(Piwik_Translate('UserCountry_CannotListContent',
- array("'$path'", $unzip->errorInfo())));
- }
-
- $datFile = null;
- foreach ($content as $info)
- {
- $archivedPath = $info['filename'];
- if (basename($archivedPath) === $dbFilename)
- {
- $datFile = $archivedPath;
- }
- }
-
- if ($datFile === null)
- {
- throw new Exception(Piwik_Translate('UserCountry_CannotFindGeoIPDatabaseInArchive',
- array($dbFilename, "'$path'")));
- }
-
- // extract JUST the .dat file
- $unzipped = $unzip->extractInString($datFile);
-
- if (empty($unzipped))
- {
- throw new Exception(Piwik_Translate('UserCountry_CannotUnzipDatFile',
- array("'$path'", $unzip->errorInfo())));
- }
-
- // write unzipped to file
- $fd = fopen($outputPath, 'wb');
- fwrite($fd, $unzipped);
- fclose($fd);
- }
- else if (substr($path, -3, 3) == '.gz')
- {
- $unzip = Piwik_Unzip::factory('gz', $path);
- $success = $unzip->extract($outputPath);
-
- if ($success !== true)
- {
- throw new Exception(Piwik_Translate('UserCountry_CannotUnzipDatFile',
- array("'$path'", $unzip->errorInfo())));
- }
- }
- else
- {
- $ext = end(explode(basename($path), '.', 2));
- throw new Exception(Piwik_Translate('UserCountry_UnsupportedArchiveType', "'$ext'"));
- }
-
- try
- {
- // test that the new archive is a valid GeoIP database
- $dbType = Piwik_UserCountry_LocationProvider_GeoIp::getGeoIPDatabaseTypeFromFilename($dbFilename);
- if ($dbType === false) // sanity check
- {
- throw new Exception("Unexpected GeoIP archive file name '$path'.");
- }
-
- $customDbNames = array(
- 'loc' => array(),
- 'isp' => array(),
- 'org' => array()
- );
- $customDbNames[$dbType] = array($tempFilename);
-
- $phpProvider = new Piwik_UserCountry_LocationProvider_GeoIp_Php($customDbNames);
-
- $location = self::getTestLocationCatchPhpErrors($phpProvider);
-
- if (empty($location)
- || self::$unzipPhpError !== null)
- {
- if (self::$unzipPhpError !== null)
- {
- list($errno, $errstr, $errfile, $errline) = self::$unzipPhpError;
- Piwik::log("Piwik_UserCountry_GeoIPAutoUpdater: Encountered PHP error when testing newly downloaded".
- " GeoIP database: $errno: $errstr on line $errline of $errfile.");
- }
-
- throw new Exception(Piwik_Translate('UserCountry_ThisUrlIsNotAValidGeoIPDB'));
- }
-
- // delete the existing GeoIP database (if any) and rename the downloaded file
- $oldDbFile = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($dbFilename);
- if (file_exists($oldDbFile))
- {
- unlink($oldDbFile);
- }
-
- $tempFile = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($tempFilename);
- rename($existing = $tempFile, $newName = $oldDbFile);
-
- // delete original archive
- if ($unlink)
- {
- unlink($path);
- }
- }
- catch (Exception $ex)
- {
- // remove downloaded files
- if (file_exists($outputPath))
- {
- unlink($outputPath);
- }
- unlink($path);
-
- throw $ex;
- }
- }
-
- /**
- * Creates a ScheduledTask instance based on set option values.
- *
- * @return Piwik_ScheduledTask
- */
- public static function makeScheduledTask()
- {
- $instance = new Piwik_UserCountry_GeoIPAutoUpdater();
-
- $schedulePeriodStr = self::getSchedulePeriod();
-
- // created the scheduledtime instance, also, since GeoIP updates are done on tuesdays,
- // get new DBs on Wednesday
- switch ($schedulePeriodStr)
- {
- case self::SCHEDULE_PERIOD_WEEKLY:
- $schedulePeriod = new Piwik_ScheduledTime_Weekly();
- $schedulePeriod->setDay(3);
- break;
- case self::SCHEDULE_PERIOD_MONTHLY:
- default:
- $schedulePeriod = new Piwik_ScheduledTime_Monthly();
- $schedulePeriod->setDayOfWeek(3, 0);
- break;
- }
-
- return new Piwik_ScheduledTask($instance, 'update', null, $schedulePeriod, Piwik_ScheduledTask::LOWEST_PRIORITY);
- }
-
- /**
- * Sets the options used by this class based on query parameter values.
- *
- * See setUpdaterOptions for query params used.
- */
- public static function setUpdaterOptionsFromUrl()
- {
- self::setUpdaterOptions(array(
- 'loc' => Piwik_Common::getRequestVar('loc_db', false, 'string'),
- 'isp' => Piwik_Common::getRequestVar('isp_db', false, 'string'),
- 'org' => Piwik_Common::getRequestVar('org_db', false, 'string'),
- 'period' => Piwik_Common::getRequestVar('period', false, 'string'),
- ));
- }
-
- /**
- * Sets the options used by this class based on the elements in $options.
- *
- * The following elements of $options are used:
- * 'loc' - URL for location database.
- * 'isp' - URL for ISP database.
- * 'org' - URL for Organization database.
- * 'period' - 'weekly' or 'monthly'. When to run the updates.
- *
- * @param array $options
- */
- public static function setUpdaterOptions( $options )
- {
- // set url options
- foreach (self::$urlOptions as $optionKey => $optionName)
- {
- if (!isset($options[$optionKey]))
- {
- continue;
- }
-
- Piwik_SetOption($optionName, $url = $options[$optionKey]);
- }
-
- // set period option
- if (!empty($options['period']))
- {
- $period = $options['period'];
- if ($period != self::SCHEDULE_PERIOD_MONTHLY
- && $period != self::SCHEDULE_PERIOD_WEEKLY)
- {
- throw new Exception(Piwik_Translate(
- 'UserCountry_InvalidGeoIPUpdatePeriod',
- array("'$period'", "'".self::SCHEDULE_PERIOD_MONTHLY."', '".self::SCHEDULE_PERIOD_WEEKLY."'")
- ));
- }
-
- Piwik_SetOption(self::SCHEDULE_PERIOD_OPTION_NAME, $period);
- }
- }
-
- /**
- * Returns true if the auto-updater is setup to update at least one type of
- * database. False if otherwise.
- *
- * @return bool
- */
- public static function isUpdaterSetup()
- {
- if (Piwik_GetOption(self::LOC_URL_OPTION_NAME) !== false
- || Piwik_GetOption(self::ISP_URL_OPTION_NAME) !== false
- || Piwik_GetOption(self::ORG_URL_OPTION_NAME) !== false)
- {
- return true;
- }
-
- return false;
- }
-
- /**
- * Retrieves the URLs used to update various GeoIP database files.
- *
- * @return array
- */
- public static function getConfiguredUrls()
- {
- $result = array();
- foreach (self::$urlOptions as $key => $optionName)
- {
- $result[$key] = Piwik_GetOption($optionName);
- }
- return $result;
- }
-
- /**
- * Returns the confiured URL (if any) for a type of database.
- *
- * @param string $key 'loc', 'isp' or 'org'
- * @return string|false
- */
- public static function getConfiguredUrl( $key )
- {
- if(empty(self::$urlOptions[$key])) {
- throw new Exception("Invalid key $key");
- }
- $url = Piwik_GetOption(self::$urlOptions[$key]);
- return $url;
- }
-
- /**
- * Performs a GeoIP database update.
- */
- public static function performUpdate()
- {
- $instance = new Piwik_UserCountry_GeoIPAutoUpdater();
- $instance->update();
- }
-
- /**
- * Returns the configured update period, either 'week' or 'month'. Defaults to
- * 'month'.
- *
- * @return string
- */
- public static function getSchedulePeriod()
- {
- $period = Piwik_GetOption(self::SCHEDULE_PERIOD_OPTION_NAME);
- if ($period === false)
- {
- $period = self::SCHEDULE_PERIOD_MONTHLY;
- }
- return $period;
- }
-
- /**
- * Returns an array of strings for GeoIP databases that have update URLs configured, but
- * are not present in the misc directory. Each string is a key describing the type of
- * database (ie, 'loc', 'isp' or 'org').
- *
- * @return array
- */
- public static function getMissingDatabases()
- {
- $result = array();
- foreach (self::getConfiguredUrls() as $key => $url)
- {
- if (!empty($url))
- {
- // if a database of the type does not exist, but there's a url to update, then
- // a database is missing
- $path = Piwik_UserCountry_LocationProvider_GeoIp::getPathToGeoIpDatabase(
- Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$key]);
- if ($path === false)
- {
- $result[] = $key;
- }
- }
- }
- return $result;
- }
-
- /**
- * Returns the extension of a URL used to update a GeoIP database, if it can be found.
- */
- public static function getGeoIPUrlExtension( $url )
- {
- // check for &suffix= query param that is special to MaxMind URLs
- if (preg_match('/suffix=([^&]+)/', $url, $matches))
- {
- return $matches[1];
- }
-
- // use basename of url
- $filenameParts = explode('.', basename($url), 2);
- if (count($filenameParts) > 1)
- {
- return end($filenameParts);
- }
- else
- {
- return reset($filenameParts);
- }
- }
-
- /**
- * Tests a location provider using a test IP address and catches PHP errors
- * (ie, notices) if they occur. PHP error information is held in self::$unzipPhpError.
- *
- * @param Piwik_UserCountry_LocationProvider $provider The provider to test.
- * @return array|false $location The result of geolocation. False if no location
- * can be found.
- */
- private static function getTestLocationCatchPhpErrors( $provider )
- {
- // note: in most cases where this will fail, the error will usually be a PHP fatal error/notice.
- // in order to delete the files in such a case (which can be caused by a man-in-the-middle attack)
- // we need to catch them, so we set a new error handler.
- self::$unzipPhpError = null;
- set_error_handler(array('Piwik_UserCountry_GeoIPAutoUpdater', 'catchGeoIPError'));
-
- $location = $provider->getLocation(array('ip' => Piwik_UserCountry_LocationProvider_GeoIp::TEST_IP));
-
- restore_error_handler();
-
- return $location;
- }
-
- /**
- * Utility function that checks if geolocation works with each installed database,
- * and if one or more doesn't, they are renamed to make sure tracking will work.
- * This is a safety measure used to make sure tracking isn't affected if strange
- * update errors occur.
- *
- * Databases are renamed to ${original}.broken .
- *
- * Note: method is protected for testability.
- */
- protected function performRedundantDbChecks()
- {
- $databaseTypes = array_keys(Piwik_UserCountry_LocationProvider_GeoIp::$dbNames);
-
- foreach ($databaseTypes as $type)
- {
- $customNames = array(
- 'loc' => array(),
- 'isp' => array(),
- 'org' => array()
- );
- $customNames[$type] = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$type];
-
- // create provider that only uses the DB type we're testing
- $provider = new Piwik_UserCountry_LocationProvider_GeoIp_Php($customNames);
-
- // test the provider. on error, we rename the broken DB.
- self::getTestLocationCatchPhpErrors($provider);
- if (self::$unzipPhpError !== null)
- {
- list($errno, $errstr, $errfile, $errline) = self::$unzipPhpError;
- Piwik::log("Piwik_UserCountry_GeoIPAutoUpdater: Encountered PHP error when performing redundant ".
- "tests on GeoIP $type database: $errno: $errstr on line $errline of $errfile.");
-
- // get the current filename for the DB and an available new one to rename it to
- list($oldPath, $newPath) = $this->getOldAndNewPathsForBrokenDb($customNames[$type]);
-
- // rename the DB so tracking will not fail
- if ($oldPath !== false
- && $newPath !== false)
- {
- if (file_exists($newPath))
- {
- unlink($newPath);
- }
-
- rename($oldPath, $newPath);
- }
- }
- }
- }
-
- /**
- * Returns the path to a GeoIP database and a path to rename it to if it's broken.
- *
- * @param array $possibleDbNames The possible names of the database.
- * @return array Array with two elements, the path to the existing database, and
- * the path to rename it to if it is broken. The second will end
- * with something like .broken .
- */
- private function getOldAndNewPathsForBrokenDb( $possibleDbNames )
- {
- $pathToDb = Piwik_UserCountry_LocationProvider_GeoIp::getPathToGeoIpDatabase($possibleDbNames);
- $newPath = false;
-
- if ($pathToDb !== false)
- {
- $newPath = $pathToDb.".broken";
- }
-
- return array($pathToDb, $newPath);
- }
-
- /**
- * Custom PHP error handler used to catch any PHP errors that occur when
- * testing a downloaded GeoIP file.
- *
- * If we download a file that is supposed to be a GeoIP database, we need to make
- * sure it is one. This is done simply by attempting to use it. If this fails, it
- * will most of the time fail as a PHP error, which we catch w/ this function
- * after it is passed to set_error_handler.
- *
- * The PHP error is stored in self::$unzipPhpError.
- *
- * @param int $errno
- * @param string $errstr
- * @param string $errfile
- * @param int $errline
- */
- public static function catchGeoIPError( $errno, $errstr, $errfile, $errline )
- {
- self::$unzipPhpError = array($errno, $errstr, $errfile, $errline);
- }
-
- /**
- * Returns the time the auto updater was last run.
- *
- * @return Piwik_Date|false
- */
- public static function getLastRunTime()
- {
- $timestamp = Piwik_GetOption(self::LAST_RUN_TIME_OPTION_NAME);
- return $timestamp === false ? false : Piwik_Date::factory((int)$timestamp);
- }
+ const SCHEDULE_PERIOD_MONTHLY = 'month';
+ const SCHEDULE_PERIOD_WEEKLY = 'week';
+
+ const SCHEDULE_PERIOD_OPTION_NAME = 'geoip.updater_period';
+ const LOC_URL_OPTION_NAME = 'geoip.loc_db_url';
+ const ISP_URL_OPTION_NAME = 'geoip.isp_db_url';
+ const ORG_URL_OPTION_NAME = 'geoip.org_db_url';
+
+ const LAST_RUN_TIME_OPTION_NAME = 'geoip.updater_last_run_time';
+
+ private static $urlOptions = array(
+ 'loc' => self::LOC_URL_OPTION_NAME,
+ 'isp' => self::ISP_URL_OPTION_NAME,
+ 'org' => self::ORG_URL_OPTION_NAME,
+ );
+
+ /**
+ * PHP Error caught through a custom error handler while trying to use a downloaded
+ * GeoIP database. See catchGeoIPError for more info.
+ *
+ * @var array
+ */
+ private static $unzipPhpError = null;
+
+ /**
+ * Attempts to download new location, ISP & organization GeoIP databases and
+ * replace the existing ones w/ them.
+ */
+ public function update()
+ {
+ try {
+ Piwik_SetOption(self::LAST_RUN_TIME_OPTION_NAME, Piwik_Date::factory('today')->getTimestamp());
+
+ $locUrl = Piwik_GetOption(self::LOC_URL_OPTION_NAME);
+ if (!empty($locUrl)) {
+ $this->downloadFile('loc', $locUrl);
+ }
+
+ $ispUrl = Piwik_GetOption(self::ISP_URL_OPTION_NAME);
+ if (!empty($ispUrl)) {
+ $this->downloadFile('isp', $ispUrl);
+ }
+
+ $orgUrl = Piwik_GetOption(self::ORG_URL_OPTION_NAME);
+ if (!empty($orgUrl)) {
+ $this->downloadFile('org', $orgUrl);
+ }
+ } catch (Exception $ex) {
+ // message will already be prefixed w/ 'Piwik_UserCountry_GeoIPAutoUpdater: '
+ Piwik::log($ex->getMessage());
+ $this->performRedundantDbChecks();
+ throw $ex;
+ }
+
+ $this->performRedundantDbChecks();
+ }
+
+ /**
+ * Downloads a GeoIP database archive, extracts the .dat file and overwrites the existing
+ * old database.
+ *
+ * If something happens that causes the download to fail, no exception is thrown, but
+ * an error is logged.
+ *
+ * @param string $url URL to the database to download. The type of database is determined
+ * from this URL.
+ */
+ private function downloadFile($dbType, $url)
+ {
+ $ext = Piwik_UserCountry_GeoIPAutoUpdater::getGeoIPUrlExtension($url);
+ $zippedFilename = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$dbType][0] . '.' . $ext;
+
+ $zippedOutputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($zippedFilename);
+
+ // download zipped file to misc dir
+ try {
+ $success = Piwik_Http::sendHttpRequest($url, $timeout = 3600, $userAgent = null, $zippedOutputPath);
+ } catch (Exception $ex) {
+ throw new Exception("Piwik_UserCountry_GeoIPAutoUpdater: failed to download '$url' to "
+ . "'$zippedOutputPath': " . $ex->getMessage());
+ }
+
+ if ($success !== true) {
+ throw new Exception("Piwik_UserCountry_GeoIPAutoUpdater: failed to download '$url' to "
+ . "'$zippedOutputPath'! (Unknown error)");
+ }
+
+ Piwik::log(sprintf("Piwik_UserCountry_GeoIPAutoUpdater: successfully downloaded '%s'", $url));
+
+ try {
+ self::unzipDownloadedFile($zippedOutputPath, $unlink = true);
+ } catch (Exception $ex) {
+ throw new Exception("Piwik_UserCountry_GeoIPAutoUpdater: failed to unzip '$zippedOutputPath' after "
+ . "downloading " . "'$url': " . $ex->getMessage());
+ }
+
+ Piwik::log(sprintf("Piwik_UserCountry_GeoIPAutoUpdater: successfully updated GeoIP database '%s'", $url));
+ }
+
+ /**
+ * Unzips a downloaded GeoIP database. Only unzips .gz & .tar.gz files.
+ *
+ * @param string $path Path to zipped file.
+ * @param bool $unlink Whether to unlink archive or not.
+ */
+ public static function unzipDownloadedFile($path, $unlink = false)
+ {
+ $parts = explode('.', basename($path));
+ $filenameStart = $parts[0];
+
+ $dbFilename = $filenameStart . '.dat';
+ $tempFilename = $filenameStart . '.dat.new';
+ $outputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($tempFilename);
+
+ // extract file
+ if (substr($path, -7, 7) == '.tar.gz') {
+ // find the .dat file in the tar archive
+ $unzip = Piwik_Unzip::factory('tar.gz', $path);
+ $content = $unzip->listContent();
+
+ if (empty($content)) {
+ throw new Exception(Piwik_Translate('UserCountry_CannotListContent',
+ array("'$path'", $unzip->errorInfo())));
+ }
+
+ $datFile = null;
+ foreach ($content as $info) {
+ $archivedPath = $info['filename'];
+ if (basename($archivedPath) === $dbFilename) {
+ $datFile = $archivedPath;
+ }
+ }
+
+ if ($datFile === null) {
+ throw new Exception(Piwik_Translate('UserCountry_CannotFindGeoIPDatabaseInArchive',
+ array($dbFilename, "'$path'")));
+ }
+
+ // extract JUST the .dat file
+ $unzipped = $unzip->extractInString($datFile);
+
+ if (empty($unzipped)) {
+ throw new Exception(Piwik_Translate('UserCountry_CannotUnzipDatFile',
+ array("'$path'", $unzip->errorInfo())));
+ }
+
+ // write unzipped to file
+ $fd = fopen($outputPath, 'wb');
+ fwrite($fd, $unzipped);
+ fclose($fd);
+ } else if (substr($path, -3, 3) == '.gz') {
+ $unzip = Piwik_Unzip::factory('gz', $path);
+ $success = $unzip->extract($outputPath);
+
+ if ($success !== true) {
+ throw new Exception(Piwik_Translate('UserCountry_CannotUnzipDatFile',
+ array("'$path'", $unzip->errorInfo())));
+ }
+ } else {
+ $ext = end(explode(basename($path), '.', 2));
+ throw new Exception(Piwik_Translate('UserCountry_UnsupportedArchiveType', "'$ext'"));
+ }
+
+ try {
+ // test that the new archive is a valid GeoIP database
+ $dbType = Piwik_UserCountry_LocationProvider_GeoIp::getGeoIPDatabaseTypeFromFilename($dbFilename);
+ if ($dbType === false) // sanity check
+ {
+ throw new Exception("Unexpected GeoIP archive file name '$path'.");
+ }
+
+ $customDbNames = array(
+ 'loc' => array(),
+ 'isp' => array(),
+ 'org' => array()
+ );
+ $customDbNames[$dbType] = array($tempFilename);
+
+ $phpProvider = new Piwik_UserCountry_LocationProvider_GeoIp_Php($customDbNames);
+
+ $location = self::getTestLocationCatchPhpErrors($phpProvider);
+
+ if (empty($location)
+ || self::$unzipPhpError !== null
+ ) {
+ if (self::$unzipPhpError !== null) {
+ list($errno, $errstr, $errfile, $errline) = self::$unzipPhpError;
+ Piwik::log("Piwik_UserCountry_GeoIPAutoUpdater: Encountered PHP error when testing newly downloaded" .
+ " GeoIP database: $errno: $errstr on line $errline of $errfile.");
+ }
+
+ throw new Exception(Piwik_Translate('UserCountry_ThisUrlIsNotAValidGeoIPDB'));
+ }
+
+ // delete the existing GeoIP database (if any) and rename the downloaded file
+ $oldDbFile = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($dbFilename);
+ if (file_exists($oldDbFile)) {
+ unlink($oldDbFile);
+ }
+
+ $tempFile = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($tempFilename);
+ rename($existing = $tempFile, $newName = $oldDbFile);
+
+ // delete original archive
+ if ($unlink) {
+ unlink($path);
+ }
+ } catch (Exception $ex) {
+ // remove downloaded files
+ if (file_exists($outputPath)) {
+ unlink($outputPath);
+ }
+ unlink($path);
+
+ throw $ex;
+ }
+ }
+
+ /**
+ * Creates a ScheduledTask instance based on set option values.
+ *
+ * @return Piwik_ScheduledTask
+ */
+ public static function makeScheduledTask()
+ {
+ $instance = new Piwik_UserCountry_GeoIPAutoUpdater();
+
+ $schedulePeriodStr = self::getSchedulePeriod();
+
+ // created the scheduledtime instance, also, since GeoIP updates are done on tuesdays,
+ // get new DBs on Wednesday
+ switch ($schedulePeriodStr) {
+ case self::SCHEDULE_PERIOD_WEEKLY:
+ $schedulePeriod = new Piwik_ScheduledTime_Weekly();
+ $schedulePeriod->setDay(3);
+ break;
+ case self::SCHEDULE_PERIOD_MONTHLY:
+ default:
+ $schedulePeriod = new Piwik_ScheduledTime_Monthly();
+ $schedulePeriod->setDayOfWeek(3, 0);
+ break;
+ }
+
+ return new Piwik_ScheduledTask($instance, 'update', null, $schedulePeriod, Piwik_ScheduledTask::LOWEST_PRIORITY);
+ }
+
+ /**
+ * Sets the options used by this class based on query parameter values.
+ *
+ * See setUpdaterOptions for query params used.
+ */
+ public static function setUpdaterOptionsFromUrl()
+ {
+ self::setUpdaterOptions(array(
+ 'loc' => Piwik_Common::getRequestVar('loc_db', false, 'string'),
+ 'isp' => Piwik_Common::getRequestVar('isp_db', false, 'string'),
+ 'org' => Piwik_Common::getRequestVar('org_db', false, 'string'),
+ 'period' => Piwik_Common::getRequestVar('period', false, 'string'),
+ ));
+ }
+
+ /**
+ * Sets the options used by this class based on the elements in $options.
+ *
+ * The following elements of $options are used:
+ * 'loc' - URL for location database.
+ * 'isp' - URL for ISP database.
+ * 'org' - URL for Organization database.
+ * 'period' - 'weekly' or 'monthly'. When to run the updates.
+ *
+ * @param array $options
+ */
+ public static function setUpdaterOptions($options)
+ {
+ // set url options
+ foreach (self::$urlOptions as $optionKey => $optionName) {
+ if (!isset($options[$optionKey])) {
+ continue;
+ }
+
+ Piwik_SetOption($optionName, $url = $options[$optionKey]);
+ }
+
+ // set period option
+ if (!empty($options['period'])) {
+ $period = $options['period'];
+ if ($period != self::SCHEDULE_PERIOD_MONTHLY
+ && $period != self::SCHEDULE_PERIOD_WEEKLY
+ ) {
+ throw new Exception(Piwik_Translate(
+ 'UserCountry_InvalidGeoIPUpdatePeriod',
+ array("'$period'", "'" . self::SCHEDULE_PERIOD_MONTHLY . "', '" . self::SCHEDULE_PERIOD_WEEKLY . "'")
+ ));
+ }
+
+ Piwik_SetOption(self::SCHEDULE_PERIOD_OPTION_NAME, $period);
+ }
+ }
+
+ /**
+ * Returns true if the auto-updater is setup to update at least one type of
+ * database. False if otherwise.
+ *
+ * @return bool
+ */
+ public static function isUpdaterSetup()
+ {
+ if (Piwik_GetOption(self::LOC_URL_OPTION_NAME) !== false
+ || Piwik_GetOption(self::ISP_URL_OPTION_NAME) !== false
+ || Piwik_GetOption(self::ORG_URL_OPTION_NAME) !== false
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieves the URLs used to update various GeoIP database files.
+ *
+ * @return array
+ */
+ public static function getConfiguredUrls()
+ {
+ $result = array();
+ foreach (self::$urlOptions as $key => $optionName) {
+ $result[$key] = Piwik_GetOption($optionName);
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the confiured URL (if any) for a type of database.
+ *
+ * @param string $key 'loc', 'isp' or 'org'
+ * @return string|false
+ */
+ public static function getConfiguredUrl($key)
+ {
+ if (empty(self::$urlOptions[$key])) {
+ throw new Exception("Invalid key $key");
+ }
+ $url = Piwik_GetOption(self::$urlOptions[$key]);
+ return $url;
+ }
+
+ /**
+ * Performs a GeoIP database update.
+ */
+ public static function performUpdate()
+ {
+ $instance = new Piwik_UserCountry_GeoIPAutoUpdater();
+ $instance->update();
+ }
+
+ /**
+ * Returns the configured update period, either 'week' or 'month'. Defaults to
+ * 'month'.
+ *
+ * @return string
+ */
+ public static function getSchedulePeriod()
+ {
+ $period = Piwik_GetOption(self::SCHEDULE_PERIOD_OPTION_NAME);
+ if ($period === false) {
+ $period = self::SCHEDULE_PERIOD_MONTHLY;
+ }
+ return $period;
+ }
+
+ /**
+ * Returns an array of strings for GeoIP databases that have update URLs configured, but
+ * are not present in the misc directory. Each string is a key describing the type of
+ * database (ie, 'loc', 'isp' or 'org').
+ *
+ * @return array
+ */
+ public static function getMissingDatabases()
+ {
+ $result = array();
+ foreach (self::getConfiguredUrls() as $key => $url) {
+ if (!empty($url)) {
+ // if a database of the type does not exist, but there's a url to update, then
+ // a database is missing
+ $path = Piwik_UserCountry_LocationProvider_GeoIp::getPathToGeoIpDatabase(
+ Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$key]);
+ if ($path === false) {
+ $result[] = $key;
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the extension of a URL used to update a GeoIP database, if it can be found.
+ */
+ public static function getGeoIPUrlExtension($url)
+ {
+ // check for &suffix= query param that is special to MaxMind URLs
+ if (preg_match('/suffix=([^&]+)/', $url, $matches)) {
+ return $matches[1];
+ }
+
+ // use basename of url
+ $filenameParts = explode('.', basename($url), 2);
+ if (count($filenameParts) > 1) {
+ return end($filenameParts);
+ } else {
+ return reset($filenameParts);
+ }
+ }
+
+ /**
+ * Tests a location provider using a test IP address and catches PHP errors
+ * (ie, notices) if they occur. PHP error information is held in self::$unzipPhpError.
+ *
+ * @param Piwik_UserCountry_LocationProvider $provider The provider to test.
+ * @return array|false $location The result of geolocation. False if no location
+ * can be found.
+ */
+ private static function getTestLocationCatchPhpErrors($provider)
+ {
+ // note: in most cases where this will fail, the error will usually be a PHP fatal error/notice.
+ // in order to delete the files in such a case (which can be caused by a man-in-the-middle attack)
+ // we need to catch them, so we set a new error handler.
+ self::$unzipPhpError = null;
+ set_error_handler(array('Piwik_UserCountry_GeoIPAutoUpdater', 'catchGeoIPError'));
+
+ $location = $provider->getLocation(array('ip' => Piwik_UserCountry_LocationProvider_GeoIp::TEST_IP));
+
+ restore_error_handler();
+
+ return $location;
+ }
+
+ /**
+ * Utility function that checks if geolocation works with each installed database,
+ * and if one or more doesn't, they are renamed to make sure tracking will work.
+ * This is a safety measure used to make sure tracking isn't affected if strange
+ * update errors occur.
+ *
+ * Databases are renamed to ${original}.broken .
+ *
+ * Note: method is protected for testability.
+ */
+ protected function performRedundantDbChecks()
+ {
+ $databaseTypes = array_keys(Piwik_UserCountry_LocationProvider_GeoIp::$dbNames);
+
+ foreach ($databaseTypes as $type) {
+ $customNames = array(
+ 'loc' => array(),
+ 'isp' => array(),
+ 'org' => array()
+ );
+ $customNames[$type] = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$type];
+
+ // create provider that only uses the DB type we're testing
+ $provider = new Piwik_UserCountry_LocationProvider_GeoIp_Php($customNames);
+
+ // test the provider. on error, we rename the broken DB.
+ self::getTestLocationCatchPhpErrors($provider);
+ if (self::$unzipPhpError !== null) {
+ list($errno, $errstr, $errfile, $errline) = self::$unzipPhpError;
+ Piwik::log("Piwik_UserCountry_GeoIPAutoUpdater: Encountered PHP error when performing redundant " .
+ "tests on GeoIP $type database: $errno: $errstr on line $errline of $errfile.");
+
+ // get the current filename for the DB and an available new one to rename it to
+ list($oldPath, $newPath) = $this->getOldAndNewPathsForBrokenDb($customNames[$type]);
+
+ // rename the DB so tracking will not fail
+ if ($oldPath !== false
+ && $newPath !== false
+ ) {
+ if (file_exists($newPath)) {
+ unlink($newPath);
+ }
+
+ rename($oldPath, $newPath);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the path to a GeoIP database and a path to rename it to if it's broken.
+ *
+ * @param array $possibleDbNames The possible names of the database.
+ * @return array Array with two elements, the path to the existing database, and
+ * the path to rename it to if it is broken. The second will end
+ * with something like .broken .
+ */
+ private function getOldAndNewPathsForBrokenDb($possibleDbNames)
+ {
+ $pathToDb = Piwik_UserCountry_LocationProvider_GeoIp::getPathToGeoIpDatabase($possibleDbNames);
+ $newPath = false;
+
+ if ($pathToDb !== false) {
+ $newPath = $pathToDb . ".broken";
+ }
+
+ return array($pathToDb, $newPath);
+ }
+
+ /**
+ * Custom PHP error handler used to catch any PHP errors that occur when
+ * testing a downloaded GeoIP file.
+ *
+ * If we download a file that is supposed to be a GeoIP database, we need to make
+ * sure it is one. This is done simply by attempting to use it. If this fails, it
+ * will most of the time fail as a PHP error, which we catch w/ this function
+ * after it is passed to set_error_handler.
+ *
+ * The PHP error is stored in self::$unzipPhpError.
+ *
+ * @param int $errno
+ * @param string $errstr
+ * @param string $errfile
+ * @param int $errline
+ */
+ public static function catchGeoIPError($errno, $errstr, $errfile, $errline)
+ {
+ self::$unzipPhpError = array($errno, $errstr, $errfile, $errline);
+ }
+
+ /**
+ * Returns the time the auto updater was last run.
+ *
+ * @return Piwik_Date|false
+ */
+ public static function getLastRunTime()
+ {
+ $timestamp = Piwik_GetOption(self::LAST_RUN_TIME_OPTION_NAME);
+ return $timestamp === false ? false : Piwik_Date::factory((int)$timestamp);
+ }
}
diff --git a/plugins/UserCountry/LocationProvider.php b/plugins/UserCountry/LocationProvider.php
index 3f6b8821ed..34dfac6dfc 100755
--- a/plugins/UserCountry/LocationProvider.php
+++ b/plugins/UserCountry/LocationProvider.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserCountry
*/
@@ -21,470 +21,431 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/LocationProvider/GeoIp.p
/**
* The base class of all LocationProviders.
- *
+ *
* LocationProviders attempt to determine a visitor's location using other
* visitor info. All LocationProviders require a visitor's IP address, some
* require more, such as the browser language.
*/
abstract class Piwik_UserCountry_LocationProvider
{
- const NOT_INSTALLED = 0;
- const INSTALLED = 1;
- const BROKEN = 2;
-
- const CURRENT_PROVIDER_OPTION_NAME = 'usercountry.location_provider';
-
- const GEOGRAPHIC_COORD_PRECISION = 3;
-
- const CONTINENT_CODE_KEY = 'continent_code';
- const CONTINENT_NAME_KEY = 'continent_name';
- const COUNTRY_CODE_KEY = 'country_code';
- const COUNTRY_NAME_KEY = 'country_name';
- const REGION_CODE_KEY = 'region_code';
- const REGION_NAME_KEY = 'region_name';
- const CITY_NAME_KEY = 'city_name';
- const AREA_CODE_KEY = 'area_code';
- const LATITUDE_KEY = 'lat';
- const LONGITUDE_KEY = 'long';
- const POSTAL_CODE_KEY = 'postal_code';
- const ISP_KEY = 'isp';
- const ORG_KEY = 'org';
-
- /**
- * An array of all provider instances. Access it through static methods.
- *
- * @var array
- */
- public static $providers = null;
-
- /**
- * Returns location information based on visitor information.
- *
- * The result of this function will be an array. The array can store some or all of
- * the following information:
- *
- * - Continent Code: The code of the visitor's continent.
- * (array key is self::CONTINENT_CODE_KEY)
- * - Continent Name: The name of the visitor's continent.
- * (array key is self::CONTINENT_NAME_KEY)
- * - Country Code: The code of the visitor's country.
- * (array key is self::COUNTRY_CODE_KEY)
- * - Country Name: The name of the visitor's country.
- * (array key is self::COUNTRY_NAME_KEY)
- * - Region Code: The code of the visitor's region.
- * (array key is self::REGION_CODE_KEY)
- * - Region Name: The name of the visitor's region.
- * (array key is self::REGION_NAME_KEY)
- * - City Name: The name of the visitor's city.
- * (array key is self::CITY_NAME_KEY)
- * - Area Code: The visitor's area code.
- * (array key is self::AREA_CODE_KEY)
- * - Latitude: The visitor's latitude.
- * (array key is self::LATITUDE_KEY)
- * - Longitude: The visitor's longitude.
- * (array key is self::LONGITUDE_KEY)
- * - Postal Code: The visitor's postal code.
- * (array key is self::POSTAL_CODE_KEY)
- * - ISP: The visitor's ISP.
- * (array key is self::ISP_KEY)
- * - Org: The company/organization of the visitor's IP.
- * (array key is self::ORG_KEY)
- *
- * All LocationProviders will attempt to return the country of the visitor.
- *
- * @param array $info What this must contain depends on the specific provider
- * implementation. All providers require an 'ip' key mapped
- * to the visitor's IP address.
- * @return array|false
- */
- abstract public function getLocation( $info );
-
- /**
- * Returns true if this provider is available for use, false if otherwise.
- *
- * @return bool
- */
- abstract public function isAvailable();
-
- /**
- * Returns true if this provider is working, false if otherwise.
- *
- * @return bool
- */
- abstract public function isWorking();
-
- /**
- * Returns an array mapping location result keys w/ bool values indicating whether
- * that information is supported by this provider. If it is not supported, that means
- * this provider either cannot get this information, or is not configured to get it.
- *
- * @return array eg. array(self::CONTINENT_CODE_KEY => true,
- * self::CONTINENT_NAME_KEY => true,
- * self::ORG_KEY => false)
- * The result is not guaranteed to have keys for every type of location
- * info.
- */
- abstract public function getSupportedLocationInfo();
-
- /**
- * Returns every available provider instance.
- *
- * @return array
- */
- public static function getAllProviders()
- {
- if (is_null(self::$providers))
- {
- self::$providers = array();
- foreach (get_declared_classes() as $klass)
- {
- if (is_subclass_of($klass, 'Piwik_UserCountry_LocationProvider'))
- {
- $klassInfo = new ReflectionClass($klass);
- if ($klassInfo->isAbstract())
- {
- continue;
- }
-
- self::$providers[] = new $klass;
- }
- }
- }
-
- return self::$providers;
- }
-
- /**
- * Returns all provider instances that are 'available'. An 'available' provider
- * is one that is available for use. They may not necessarily be working.
- *
- * @return array
- */
- public static function getAvailableProviders()
- {
- $result = array();
- foreach (self::getAllProviders() as $provider)
- {
- if ($provider->isAvailable())
- {
- $result[] = $provider;
- }
- }
- return $result;
- }
-
- /**
- * Returns an array mapping provider IDs w/ information about the provider,
- * for each location provider.
- *
- * The following information is provided for each provider:
- * 'id' - The provider's unique string ID.
- * 'title' - The provider's title.
- * 'description' - A description of how the location provider works.
- * 'status' - Either self::NOT_INSTALLED, self::INSTALLED or self::BROKEN.
- * 'statusMessage' - If the status is self::BROKEN, then the message describes why.
- * 'location' - A pretty formatted location of the current IP address
- * (Piwik_IP::getIpFromHeader()).
- *
- * An example result:
- * array(
- * 'geoip_php' => array('id' => 'geoip_php',
- * 'title' => '...',
- * 'desc' => '...',
- * 'status' => Piwik_UserCountry_LocationProvider_GeoIp::BROKEN,
- * 'statusMessage' => '...',
- * 'location' => '...')
- * 'geoip_serverbased' => array(...)
- * )
- *
- * @param string $newline What to separate lines with in the pretty locations.
- * @param bool $includeExtra Whether to include ISP/Org info in formatted location.
- * @return array
- */
- public static function getAllProviderInfo( $newline = "\n", $includeExtra = false )
- {
- $allInfo = array();
- foreach (self::getAllProviders() as $provider)
- {
- $info = $provider->getInfo();
-
- $status = self::INSTALLED;
- $location = false;
- $statusMessage = false;
-
- $availableOrMessage = $provider->isAvailable();
- if ($availableOrMessage !== true)
- {
- $status = self::NOT_INSTALLED;
- if (is_string($availableOrMessage))
- {
- $statusMessage = $availableOrMessage;
- }
- }
- else
- {
- $workingOrError = $provider->isWorking();
- if ($workingOrError === true) // if the implementation is configured correctly, get the location
- {
- $locInfo = array('ip' => Piwik_IP::getIpFromHeader(),
- 'lang' => Piwik_Common::getBrowserLanguage(),
- 'disable_fallbacks' => true);
-
- $location = $provider->getLocation($locInfo);
- $location = self::prettyFormatLocation($location, $newline, $includeExtra);
- }
- else // otherwise set an error message describing why
- {
- $status = self::BROKEN;
- $statusMessage = $workingOrError;
- }
- }
-
- $info['status'] = $status;
- $info['statusMessage'] = $statusMessage;
- $info['location'] = $location;
-
- $allInfo[$info['order']] = $info;
- }
-
- ksort($allInfo);
-
- $result = array();
- foreach ($allInfo as $info)
- {
- $result[$info['id']] = $info;
- }
- return $result;
- }
-
- /**
- * Returns the ID of the currently used location provider.
- *
- * The used provider is stored in the 'usercountry.location_provider' option.
- *
- * This function should not be called by the Tracker.
- *
- * @return string
- */
- public static function getCurrentProviderId()
- {
- $optionValue = Piwik_GetOption(self::CURRENT_PROVIDER_OPTION_NAME);
- return $optionValue === false ? Piwik_UserCountry_LocationProvider_Default::ID : $optionValue;
- }
-
- /**
- * Returns the provider instance of the current location provider.
- *
- * This function should not be called by the Tracker.
- *
- * @return Piwik_UserCountry_LocationProvider
- */
- public static function getCurrentProvider()
- {
- return self::getProviderById(self::getCurrentProviderId());
- }
-
- /**
- * Sets the provider to use when tracking.
- *
- * @param string $providerId The ID of the provider to use.
- * @return Piwik_UserCountry_LocationProvider The new current provider.
- * @throws Exception If the provider ID is invalid.
- */
- public static function setCurrentProvider( $providerId )
- {
- $provider = self::getProviderById($providerId);
- if ($provider === false)
- {
- throw new Exception(
- "Invalid provider ID '$providerId'. The provider either does not exist or is not available");
- }
- Piwik_SetOption(self::CURRENT_PROVIDER_OPTION_NAME, $providerId);
- Piwik_Tracker_Cache::clearCacheGeneral();
- return $provider;
- }
-
- /**
- * Returns a provider instance by ID or false if the ID is invalid or unavailable.
- *
- * @param string $providerId
- * @return Piwik_UserCountry_LocationProvider|false
- */
- public static function getProviderById( $providerId )
- {
- foreach (self::getAvailableProviders() as $provider)
- {
- $info = $provider->getInfo();
- if ($info['id'] == $providerId)
- {
- return $provider;
- }
- }
- return false;
- }
-
- /**
- * Tries to fill in any missing information in a location result.
- *
- * This method will try to set the continent code, continent name and country code
- * using other information.
- *
- * Note: This function must always be called by location providers in getLocation.
- *
- * @param array $location The location information to modify.
- */
- public function completeLocationResult( &$location )
- {
- // fill in continent code if country code is present
- if (empty($location[self::CONTINENT_CODE_KEY])
- && !empty($location[self::COUNTRY_CODE_KEY]))
- {
- $countryCode = strtolower($location[self::COUNTRY_CODE_KEY]);
- $location[self::CONTINENT_CODE_KEY] = Piwik_Common::getContinent($countryCode);
- }
-
- // fill in continent name if continent code is present
- if (empty($location[self::CONTINENT_NAME_KEY])
- && !empty($location[self::CONTINENT_CODE_KEY]))
- {
- $continentCode = strtolower($location[self::CONTINENT_CODE_KEY]);
- $location[self::CONTINENT_NAME_KEY] = Piwik_Translate('UserCountry_continent_'.$continentCode);
- }
-
- // fill in country name if country code is present
- if (empty($location[self::COUNTRY_NAME_KEY])
- && !empty($location[self::COUNTRY_CODE_KEY]))
- {
- $countryCode = strtolower($location[self::COUNTRY_CODE_KEY]);
- $location[self::COUNTRY_NAME_KEY] = Piwik_Translate('UserCountry_country_'.$countryCode);
- }
-
- // deal w/ improper latitude/longitude & round proper values
- if (!empty($location[self::LATITUDE_KEY]))
- {
- if (is_numeric($location[self::LATITUDE_KEY]))
- {
- $location[self::LATITUDE_KEY] = round($location[self::LATITUDE_KEY], self::GEOGRAPHIC_COORD_PRECISION);
- }
- else
- {
- unset($location[self::LATITUDE_KEY]);
- }
- }
-
- if (!empty($location[self::LONGITUDE_KEY]))
- {
- if (is_numeric($location[self::LONGITUDE_KEY]))
- {
- $location[self::LONGITUDE_KEY] = round($location[self::LONGITUDE_KEY], self::GEOGRAPHIC_COORD_PRECISION);
- }
- else
- {
- unset($location[self::LONGITUDE_KEY]);
- }
- }
- }
-
- /**
- * Returns a prettified location result.
- *
- * @param array|false $locationInfo
- * @param string $newline The line separator (ie, \n or <br/>).
- * @param bool $includeExtra Whether to include ISP/Organization info.
- * @return string
- */
- public static function prettyFormatLocation( $locationInfo, $newline = "\n", $includeExtra = false )
- {
- if ($locationInfo === false)
- {
- return Piwik_Translate('General_Unknown');
- }
-
- // add latitude/longitude line
- $lines = array();
- if (!empty($locationInfo[self::LATITUDE_KEY])
- && !empty($locationInfo[self::LONGITUDE_KEY]))
- {
- $lines[] = '('.$locationInfo[self::LATITUDE_KEY].', '.$locationInfo[self::LONGITUDE_KEY].')';
- }
-
- // add city/state line
- $cityState = array();
- if (!empty($locationInfo[self::CITY_NAME_KEY]))
- {
- $cityState[] = $locationInfo[self::CITY_NAME_KEY];
- }
-
- if (!empty($locationInfo[self::REGION_CODE_KEY]))
- {
- $cityState[] = $locationInfo[self::REGION_CODE_KEY];
- }
- else if (!empty($locationInfo[self::REGION_NAME_KEY]))
- {
- $cityState[] = $locationInfo[self::REGION_NAME_KEY];
- }
-
- if (!empty($cityState))
- {
- $lines[] = implode(', ', $cityState);
- }
-
- // add postal code line
- if (!empty($locationInfo[self::POSTAL_CODE_KEY]))
- {
- $lines[] = $locationInfo[self::POSTAL_CODE_KEY];
- }
-
- // add country line
- if (!empty($locationInfo[self::COUNTRY_NAME_KEY]))
- {
- $lines[] = $locationInfo[self::COUNTRY_NAME_KEY];
- }
- else if (!empty($locationInfo[self::COUNTRY_CODE_KEY]))
- {
- $lines[] = $locationInfo[self::COUNTRY_CODE_KEY];
- }
-
- // add extra information (ISP/Organization)
- if ($includeExtra)
- {
- $lines[] = '';
-
- $unknown = Piwik_Translate('General_Unknown');
-
- $org = !empty($locationInfo[self::ORG_KEY]) ? $locationInfo[self::ORG_KEY] : $unknown;
- $lines[] = "Org: $org";
-
- $isp = !empty($locationInfo[self::ISP_KEY]) ? $locationInfo[self::ISP_KEY] : $unknown;
- $lines[] = "ISP: $isp";
- }
-
- return implode($newline, $lines);
- }
-
- /**
- * Returns an IP address from an array that was passed into getLocation. This
- * will return an IPv4 address or false if the address is IPv6 (IPv6 is not
- * supported yet).
- *
- * @param array $ip Must have 'ip' key.
- * @return string|bool
- */
- protected function getIpFromInfo( $info )
- {
- $ip = $info['ip'];
- if (Piwik_IP::isMappedIPv4($ip))
- {
- return Piwik_IP::getIPv4FromMappedIPv6($ip);
- }
- else if (Piwik_IP::isIPv6($ip)) // IPv6 is not supported (yet)
- {
- return false;
- }
- else
- {
- return $ip;
- }
- }
+ const NOT_INSTALLED = 0;
+ const INSTALLED = 1;
+ const BROKEN = 2;
+
+ const CURRENT_PROVIDER_OPTION_NAME = 'usercountry.location_provider';
+
+ const GEOGRAPHIC_COORD_PRECISION = 3;
+
+ const CONTINENT_CODE_KEY = 'continent_code';
+ const CONTINENT_NAME_KEY = 'continent_name';
+ const COUNTRY_CODE_KEY = 'country_code';
+ const COUNTRY_NAME_KEY = 'country_name';
+ const REGION_CODE_KEY = 'region_code';
+ const REGION_NAME_KEY = 'region_name';
+ const CITY_NAME_KEY = 'city_name';
+ const AREA_CODE_KEY = 'area_code';
+ const LATITUDE_KEY = 'lat';
+ const LONGITUDE_KEY = 'long';
+ const POSTAL_CODE_KEY = 'postal_code';
+ const ISP_KEY = 'isp';
+ const ORG_KEY = 'org';
+
+ /**
+ * An array of all provider instances. Access it through static methods.
+ *
+ * @var array
+ */
+ public static $providers = null;
+
+ /**
+ * Returns location information based on visitor information.
+ *
+ * The result of this function will be an array. The array can store some or all of
+ * the following information:
+ *
+ * - Continent Code: The code of the visitor's continent.
+ * (array key is self::CONTINENT_CODE_KEY)
+ * - Continent Name: The name of the visitor's continent.
+ * (array key is self::CONTINENT_NAME_KEY)
+ * - Country Code: The code of the visitor's country.
+ * (array key is self::COUNTRY_CODE_KEY)
+ * - Country Name: The name of the visitor's country.
+ * (array key is self::COUNTRY_NAME_KEY)
+ * - Region Code: The code of the visitor's region.
+ * (array key is self::REGION_CODE_KEY)
+ * - Region Name: The name of the visitor's region.
+ * (array key is self::REGION_NAME_KEY)
+ * - City Name: The name of the visitor's city.
+ * (array key is self::CITY_NAME_KEY)
+ * - Area Code: The visitor's area code.
+ * (array key is self::AREA_CODE_KEY)
+ * - Latitude: The visitor's latitude.
+ * (array key is self::LATITUDE_KEY)
+ * - Longitude: The visitor's longitude.
+ * (array key is self::LONGITUDE_KEY)
+ * - Postal Code: The visitor's postal code.
+ * (array key is self::POSTAL_CODE_KEY)
+ * - ISP: The visitor's ISP.
+ * (array key is self::ISP_KEY)
+ * - Org: The company/organization of the visitor's IP.
+ * (array key is self::ORG_KEY)
+ *
+ * All LocationProviders will attempt to return the country of the visitor.
+ *
+ * @param array $info What this must contain depends on the specific provider
+ * implementation. All providers require an 'ip' key mapped
+ * to the visitor's IP address.
+ * @return array|false
+ */
+ abstract public function getLocation($info);
+
+ /**
+ * Returns true if this provider is available for use, false if otherwise.
+ *
+ * @return bool
+ */
+ abstract public function isAvailable();
+
+ /**
+ * Returns true if this provider is working, false if otherwise.
+ *
+ * @return bool
+ */
+ abstract public function isWorking();
+
+ /**
+ * Returns an array mapping location result keys w/ bool values indicating whether
+ * that information is supported by this provider. If it is not supported, that means
+ * this provider either cannot get this information, or is not configured to get it.
+ *
+ * @return array eg. array(self::CONTINENT_CODE_KEY => true,
+ * self::CONTINENT_NAME_KEY => true,
+ * self::ORG_KEY => false)
+ * The result is not guaranteed to have keys for every type of location
+ * info.
+ */
+ abstract public function getSupportedLocationInfo();
+
+ /**
+ * Returns every available provider instance.
+ *
+ * @return array
+ */
+ public static function getAllProviders()
+ {
+ if (is_null(self::$providers)) {
+ self::$providers = array();
+ foreach (get_declared_classes() as $klass) {
+ if (is_subclass_of($klass, 'Piwik_UserCountry_LocationProvider')) {
+ $klassInfo = new ReflectionClass($klass);
+ if ($klassInfo->isAbstract()) {
+ continue;
+ }
+
+ self::$providers[] = new $klass;
+ }
+ }
+ }
+
+ return self::$providers;
+ }
+
+ /**
+ * Returns all provider instances that are 'available'. An 'available' provider
+ * is one that is available for use. They may not necessarily be working.
+ *
+ * @return array
+ */
+ public static function getAvailableProviders()
+ {
+ $result = array();
+ foreach (self::getAllProviders() as $provider) {
+ if ($provider->isAvailable()) {
+ $result[] = $provider;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Returns an array mapping provider IDs w/ information about the provider,
+ * for each location provider.
+ *
+ * The following information is provided for each provider:
+ * 'id' - The provider's unique string ID.
+ * 'title' - The provider's title.
+ * 'description' - A description of how the location provider works.
+ * 'status' - Either self::NOT_INSTALLED, self::INSTALLED or self::BROKEN.
+ * 'statusMessage' - If the status is self::BROKEN, then the message describes why.
+ * 'location' - A pretty formatted location of the current IP address
+ * (Piwik_IP::getIpFromHeader()).
+ *
+ * An example result:
+ * array(
+ * 'geoip_php' => array('id' => 'geoip_php',
+ * 'title' => '...',
+ * 'desc' => '...',
+ * 'status' => Piwik_UserCountry_LocationProvider_GeoIp::BROKEN,
+ * 'statusMessage' => '...',
+ * 'location' => '...')
+ * 'geoip_serverbased' => array(...)
+ * )
+ *
+ * @param string $newline What to separate lines with in the pretty locations.
+ * @param bool $includeExtra Whether to include ISP/Org info in formatted location.
+ * @return array
+ */
+ public static function getAllProviderInfo($newline = "\n", $includeExtra = false)
+ {
+ $allInfo = array();
+ foreach (self::getAllProviders() as $provider) {
+ $info = $provider->getInfo();
+
+ $status = self::INSTALLED;
+ $location = false;
+ $statusMessage = false;
+
+ $availableOrMessage = $provider->isAvailable();
+ if ($availableOrMessage !== true) {
+ $status = self::NOT_INSTALLED;
+ if (is_string($availableOrMessage)) {
+ $statusMessage = $availableOrMessage;
+ }
+ } else {
+ $workingOrError = $provider->isWorking();
+ if ($workingOrError === true) // if the implementation is configured correctly, get the location
+ {
+ $locInfo = array('ip' => Piwik_IP::getIpFromHeader(),
+ 'lang' => Piwik_Common::getBrowserLanguage(),
+ 'disable_fallbacks' => true);
+
+ $location = $provider->getLocation($locInfo);
+ $location = self::prettyFormatLocation($location, $newline, $includeExtra);
+ } else // otherwise set an error message describing why
+ {
+ $status = self::BROKEN;
+ $statusMessage = $workingOrError;
+ }
+ }
+
+ $info['status'] = $status;
+ $info['statusMessage'] = $statusMessage;
+ $info['location'] = $location;
+
+ $allInfo[$info['order']] = $info;
+ }
+
+ ksort($allInfo);
+
+ $result = array();
+ foreach ($allInfo as $info) {
+ $result[$info['id']] = $info;
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the ID of the currently used location provider.
+ *
+ * The used provider is stored in the 'usercountry.location_provider' option.
+ *
+ * This function should not be called by the Tracker.
+ *
+ * @return string
+ */
+ public static function getCurrentProviderId()
+ {
+ $optionValue = Piwik_GetOption(self::CURRENT_PROVIDER_OPTION_NAME);
+ return $optionValue === false ? Piwik_UserCountry_LocationProvider_Default::ID : $optionValue;
+ }
+
+ /**
+ * Returns the provider instance of the current location provider.
+ *
+ * This function should not be called by the Tracker.
+ *
+ * @return Piwik_UserCountry_LocationProvider
+ */
+ public static function getCurrentProvider()
+ {
+ return self::getProviderById(self::getCurrentProviderId());
+ }
+
+ /**
+ * Sets the provider to use when tracking.
+ *
+ * @param string $providerId The ID of the provider to use.
+ * @return Piwik_UserCountry_LocationProvider The new current provider.
+ * @throws Exception If the provider ID is invalid.
+ */
+ public static function setCurrentProvider($providerId)
+ {
+ $provider = self::getProviderById($providerId);
+ if ($provider === false) {
+ throw new Exception(
+ "Invalid provider ID '$providerId'. The provider either does not exist or is not available");
+ }
+ Piwik_SetOption(self::CURRENT_PROVIDER_OPTION_NAME, $providerId);
+ Piwik_Tracker_Cache::clearCacheGeneral();
+ return $provider;
+ }
+
+ /**
+ * Returns a provider instance by ID or false if the ID is invalid or unavailable.
+ *
+ * @param string $providerId
+ * @return Piwik_UserCountry_LocationProvider|false
+ */
+ public static function getProviderById($providerId)
+ {
+ foreach (self::getAvailableProviders() as $provider) {
+ $info = $provider->getInfo();
+ if ($info['id'] == $providerId) {
+ return $provider;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tries to fill in any missing information in a location result.
+ *
+ * This method will try to set the continent code, continent name and country code
+ * using other information.
+ *
+ * Note: This function must always be called by location providers in getLocation.
+ *
+ * @param array $location The location information to modify.
+ */
+ public function completeLocationResult(&$location)
+ {
+ // fill in continent code if country code is present
+ if (empty($location[self::CONTINENT_CODE_KEY])
+ && !empty($location[self::COUNTRY_CODE_KEY])
+ ) {
+ $countryCode = strtolower($location[self::COUNTRY_CODE_KEY]);
+ $location[self::CONTINENT_CODE_KEY] = Piwik_Common::getContinent($countryCode);
+ }
+
+ // fill in continent name if continent code is present
+ if (empty($location[self::CONTINENT_NAME_KEY])
+ && !empty($location[self::CONTINENT_CODE_KEY])
+ ) {
+ $continentCode = strtolower($location[self::CONTINENT_CODE_KEY]);
+ $location[self::CONTINENT_NAME_KEY] = Piwik_Translate('UserCountry_continent_' . $continentCode);
+ }
+
+ // fill in country name if country code is present
+ if (empty($location[self::COUNTRY_NAME_KEY])
+ && !empty($location[self::COUNTRY_CODE_KEY])
+ ) {
+ $countryCode = strtolower($location[self::COUNTRY_CODE_KEY]);
+ $location[self::COUNTRY_NAME_KEY] = Piwik_Translate('UserCountry_country_' . $countryCode);
+ }
+
+ // deal w/ improper latitude/longitude & round proper values
+ if (!empty($location[self::LATITUDE_KEY])) {
+ if (is_numeric($location[self::LATITUDE_KEY])) {
+ $location[self::LATITUDE_KEY] = round($location[self::LATITUDE_KEY], self::GEOGRAPHIC_COORD_PRECISION);
+ } else {
+ unset($location[self::LATITUDE_KEY]);
+ }
+ }
+
+ if (!empty($location[self::LONGITUDE_KEY])) {
+ if (is_numeric($location[self::LONGITUDE_KEY])) {
+ $location[self::LONGITUDE_KEY] = round($location[self::LONGITUDE_KEY], self::GEOGRAPHIC_COORD_PRECISION);
+ } else {
+ unset($location[self::LONGITUDE_KEY]);
+ }
+ }
+ }
+
+ /**
+ * Returns a prettified location result.
+ *
+ * @param array|false $locationInfo
+ * @param string $newline The line separator (ie, \n or <br/>).
+ * @param bool $includeExtra Whether to include ISP/Organization info.
+ * @return string
+ */
+ public static function prettyFormatLocation($locationInfo, $newline = "\n", $includeExtra = false)
+ {
+ if ($locationInfo === false) {
+ return Piwik_Translate('General_Unknown');
+ }
+
+ // add latitude/longitude line
+ $lines = array();
+ if (!empty($locationInfo[self::LATITUDE_KEY])
+ && !empty($locationInfo[self::LONGITUDE_KEY])
+ ) {
+ $lines[] = '(' . $locationInfo[self::LATITUDE_KEY] . ', ' . $locationInfo[self::LONGITUDE_KEY] . ')';
+ }
+
+ // add city/state line
+ $cityState = array();
+ if (!empty($locationInfo[self::CITY_NAME_KEY])) {
+ $cityState[] = $locationInfo[self::CITY_NAME_KEY];
+ }
+
+ if (!empty($locationInfo[self::REGION_CODE_KEY])) {
+ $cityState[] = $locationInfo[self::REGION_CODE_KEY];
+ } else if (!empty($locationInfo[self::REGION_NAME_KEY])) {
+ $cityState[] = $locationInfo[self::REGION_NAME_KEY];
+ }
+
+ if (!empty($cityState)) {
+ $lines[] = implode(', ', $cityState);
+ }
+
+ // add postal code line
+ if (!empty($locationInfo[self::POSTAL_CODE_KEY])) {
+ $lines[] = $locationInfo[self::POSTAL_CODE_KEY];
+ }
+
+ // add country line
+ if (!empty($locationInfo[self::COUNTRY_NAME_KEY])) {
+ $lines[] = $locationInfo[self::COUNTRY_NAME_KEY];
+ } else if (!empty($locationInfo[self::COUNTRY_CODE_KEY])) {
+ $lines[] = $locationInfo[self::COUNTRY_CODE_KEY];
+ }
+
+ // add extra information (ISP/Organization)
+ if ($includeExtra) {
+ $lines[] = '';
+
+ $unknown = Piwik_Translate('General_Unknown');
+
+ $org = !empty($locationInfo[self::ORG_KEY]) ? $locationInfo[self::ORG_KEY] : $unknown;
+ $lines[] = "Org: $org";
+
+ $isp = !empty($locationInfo[self::ISP_KEY]) ? $locationInfo[self::ISP_KEY] : $unknown;
+ $lines[] = "ISP: $isp";
+ }
+
+ return implode($newline, $lines);
+ }
+
+ /**
+ * Returns an IP address from an array that was passed into getLocation. This
+ * will return an IPv4 address or false if the address is IPv6 (IPv6 is not
+ * supported yet).
+ *
+ * @param array $ip Must have 'ip' key.
+ * @return string|bool
+ */
+ protected function getIpFromInfo($info)
+ {
+ $ip = $info['ip'];
+ if (Piwik_IP::isMappedIPv4($ip)) {
+ return Piwik_IP::getIPv4FromMappedIPv6($ip);
+ } else if (Piwik_IP::isIPv6($ip)) // IPv6 is not supported (yet)
+ {
+ return false;
+ } else {
+ return $ip;
+ }
+ }
}
diff --git a/plugins/UserCountry/LocationProvider/Default.php b/plugins/UserCountry/LocationProvider/Default.php
index a24c3a7cd9..f19fc47641 100755
--- a/plugins/UserCountry/LocationProvider/Default.php
+++ b/plugins/UserCountry/LocationProvider/Default.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserCountry
*/
@@ -12,99 +12,99 @@
/**
* The default LocationProvider, this LocationProvider guesses a visitor's country
* using the language they use. This provider is not very accurate.
- *
+ *
* @package Piwik_UserCountry
*/
class Piwik_UserCountry_LocationProvider_Default extends Piwik_UserCountry_LocationProvider
{
- const ID = 'default';
- const TITLE = 'General_Default';
-
- /**
- * Guesses a visitor's location using a visitor's browser language.
- *
- * @param array $info Contains 'ip' & 'lang' keys.
- * @return array Contains the guessed country code mapped to LocationProvider::COUNTRY_CODE_KEY.
- */
- public function getLocation( $info )
- {
- $enableLanguageToCountryGuess = Piwik_Config::getInstance()->Tracker['enable_language_to_country_guess'];
+ const ID = 'default';
+ const TITLE = 'General_Default';
- if(empty($info['lang'])) {
- $info['lang'] = Piwik_Common::getBrowserLanguage();
- }
- $country = Piwik_Common::getCountry($info['lang'], $enableLanguageToCountryGuess, $info['ip']);
-
- $location = array(parent::COUNTRY_CODE_KEY => $country);
- $this->completeLocationResult($location);
-
- return $location;
- }
-
- /**
- * Returns whether this location provider is available.
- *
- * This implementation is always available.
- *
- * @return true
- */
- public function isAvailable()
- {
- return true;
- }
-
- /**
- * Returns whether this location provider is working correctly.
- *
- * This implementation is always working correctly.
- *
- * @return true
- */
- public function isWorking()
- {
- return true;
- }
-
- /**
- * Returns an array describing the types of location information this provider will
- * return.
- *
- * This provider supports the following types of location info:
- * - continent code
- * - continent name
- * - country code
- * - country name
- *
- * @return array
- */
- public function getSupportedLocationInfo()
- {
- return array(self::CONTINENT_CODE_KEY => true,
- self::CONTINENT_NAME_KEY => true,
- self::COUNTRY_CODE_KEY => true,
- self::COUNTRY_NAME_KEY => true);
- }
-
- /**
- * Returns information about this location provider. Contains an id, title & description:
- *
- * array(
- * 'id' => 'default',
- * 'title' => '...',
- * 'description' => '...'
- * );
- *
- * @return array
- */
- public function getInfo()
- {
- $desc = Piwik_Translate('UserCountry_DefaultLocationProviderDesc1') . ' '
- . Piwik_Translate('UserCountry_DefaultLocationProviderDesc2',
- array('<strong>', '<em>', '</em>', '</strong>'))
- . '<p><em><a href="http://piwik.org/faq/how-to/#faq_163" target="_blank">'
- . Piwik_Translate('UserCountry_HowToInstallGeoIPDatabases')
- . '</em></a></p>';
- return array('id' => self::ID, 'title' => self::TITLE, 'description' => $desc, 'order' => 1);
- }
+ /**
+ * Guesses a visitor's location using a visitor's browser language.
+ *
+ * @param array $info Contains 'ip' & 'lang' keys.
+ * @return array Contains the guessed country code mapped to LocationProvider::COUNTRY_CODE_KEY.
+ */
+ public function getLocation($info)
+ {
+ $enableLanguageToCountryGuess = Piwik_Config::getInstance()->Tracker['enable_language_to_country_guess'];
+
+ if (empty($info['lang'])) {
+ $info['lang'] = Piwik_Common::getBrowserLanguage();
+ }
+ $country = Piwik_Common::getCountry($info['lang'], $enableLanguageToCountryGuess, $info['ip']);
+
+ $location = array(parent::COUNTRY_CODE_KEY => $country);
+ $this->completeLocationResult($location);
+
+ return $location;
+ }
+
+ /**
+ * Returns whether this location provider is available.
+ *
+ * This implementation is always available.
+ *
+ * @return true
+ */
+ public function isAvailable()
+ {
+ return true;
+ }
+
+ /**
+ * Returns whether this location provider is working correctly.
+ *
+ * This implementation is always working correctly.
+ *
+ * @return true
+ */
+ public function isWorking()
+ {
+ return true;
+ }
+
+ /**
+ * Returns an array describing the types of location information this provider will
+ * return.
+ *
+ * This provider supports the following types of location info:
+ * - continent code
+ * - continent name
+ * - country code
+ * - country name
+ *
+ * @return array
+ */
+ public function getSupportedLocationInfo()
+ {
+ return array(self::CONTINENT_CODE_KEY => true,
+ self::CONTINENT_NAME_KEY => true,
+ self::COUNTRY_CODE_KEY => true,
+ self::COUNTRY_NAME_KEY => true);
+ }
+
+ /**
+ * Returns information about this location provider. Contains an id, title & description:
+ *
+ * array(
+ * 'id' => 'default',
+ * 'title' => '...',
+ * 'description' => '...'
+ * );
+ *
+ * @return array
+ */
+ public function getInfo()
+ {
+ $desc = Piwik_Translate('UserCountry_DefaultLocationProviderDesc1') . ' '
+ . Piwik_Translate('UserCountry_DefaultLocationProviderDesc2',
+ array('<strong>', '<em>', '</em>', '</strong>'))
+ . '<p><em><a href="http://piwik.org/faq/how-to/#faq_163" target="_blank">'
+ . Piwik_Translate('UserCountry_HowToInstallGeoIPDatabases')
+ . '</em></a></p>';
+ return array('id' => self::ID, 'title' => self::TITLE, 'description' => $desc, 'order' => 1);
+ }
}
diff --git a/plugins/UserCountry/LocationProvider/GeoIp.php b/plugins/UserCountry/LocationProvider/GeoIp.php
index 4af024ae96..f7a9029797 100755
--- a/plugins/UserCountry/LocationProvider/GeoIp.php
+++ b/plugins/UserCountry/LocationProvider/GeoIp.php
@@ -1,274 +1,257 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserCountry
*/
/**
* Base type for all GeoIP LocationProviders.
- *
+ *
* @package Piwik_UserCountry
*/
abstract class Piwik_UserCountry_LocationProvider_GeoIp extends Piwik_UserCountry_LocationProvider
{
- /* For testing, use: 'http://piwik-team.s3.amazonaws.com/GeoLiteCity.dat.gz' */
- const GEO_LITE_URL = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz';
- const TEST_IP = '194.57.91.215';
-
- public static $geoIPDatabaseDir = 'misc';
-
- /**
- * Stores possible database file names categorized by the type of information
- * GeoIP databases hold.
- *
- * @var array
- */
- public static $dbNames = array(
- 'loc' => array('GeoIPCity.dat', 'GeoLiteCity.dat', 'GeoIP.dat'),
- 'isp' => array('GeoIPISP.dat'),
- 'org' => array('GeoIPOrg.dat'),
- );
-
- /**
- * Cached region name array. Data is from geoipregionvars.php.
- *
- * @var array
- */
- private static $regionNames = null;
-
- /**
- * Attempts to fill in some missing information in a GeoIP location.
- *
- * This method will call LocationProvider::completeLocationResult and then
- * try to set the region name of the location if the country code & region
- * code are set.
- *
- * @param array $location The location information to modify.
- */
- public function completeLocationResult( &$location )
- {
- $this->fixupLocation($location);
- parent::completeLocationResult($location);
-
- // set region name if region code is set
- if (empty($location[self::REGION_NAME_KEY])
- && !empty($location[self::REGION_CODE_KEY])
- && !empty($location[self::COUNTRY_CODE_KEY]))
- {
- $countryCode = $location[self::COUNTRY_CODE_KEY];
- $regionCode = (string)$location[self::REGION_CODE_KEY];
- $location[self::REGION_NAME_KEY] = self::getRegionNameFromCodes($countryCode, $regionCode);
- }
- }
-
- /**
- * Fix up data to work with our SVG maps which include 'Tib' boundaries
- */
- protected function fixupLocation( &$location )
- {
- if(!empty($location[self::REGION_CODE_KEY])
- && $location[self::REGION_CODE_KEY] == '14'
- && !empty($location[self::COUNTRY_CODE_KEY])
- && strtoupper($location[self::COUNTRY_CODE_KEY]) == 'CN')
- {
- $location[self::COUNTRY_CODE_KEY] = 'ti';
- $location[self::REGION_CODE_KEY] = '1';
- }
- }
-
- /**
- * Returns true if this provider has been setup correctly, the error message if
- * otherwise.
- *
- * @return bool|string
- */
- public function isWorking()
- {
- // test with an example IP to make sure the provider is working
- // NOTE: At the moment only country, region & city info is tested.
- try
- {
- $supportedInfo = $this->getSupportedLocationInfo();
-
- list($testIp, $expectedResult) = self::getTestIpAndResult();
-
- // get location using test IP
- $location = $this->getLocation(array('ip' => $testIp));
-
- // check that result is the same as expected
- $isResultCorrect = true;
- foreach ($expectedResult as $key => $value)
- {
- // if this provider is not configured to support this information type, skip it
- if (empty($supportedInfo[$key]))
- {
- continue;
- }
-
- if (empty($location[$key])
- || $location[$key] != $value)
- {
- $isResultCorrect = false;
- }
- }
-
- if (!$isResultCorrect)
- {
- $unknown = Piwik_Translate('General_Unknown');
-
- $location = "'"
- . (empty($location[self::CITY_NAME_KEY]) ? $unknown : $location[self::CITY_NAME_KEY])
- . ", "
- . (empty($location[self::REGION_CODE_KEY]) ? $unknown : $location[self::REGION_CODE_KEY])
- . ", "
- . (empty($location[self::COUNTRY_CODE_KEY]) ? $unknown : $location[self::COUNTRY_CODE_KEY])
- . "'"
- ;
-
- $expectedLocation = "'".$expectedResult[self::CITY_NAME_KEY].", "
- . $expectedResult[self::REGION_CODE_KEY].", "
- . $expectedResult[self::COUNTRY_CODE_KEY]."'";
-
- $bind = array($testIp, $location, $expectedLocation);
- return Piwik_Translate('UserCountry_TestIPLocatorFailed', $bind);
- }
-
- return true;
- }
- catch (Exception $ex)
- {
- return $ex->getMessage();
- }
- }
-
- /**
- * Returns a region name for a country code + region code.
- *
- * @param string $countryCode
- * @param string $regionCode
- * @return string The region name or 'Unknown' (translated).
- */
- public static function getRegionNameFromCodes( $countryCode, $regionCode )
- {
- $regionNames = self::getRegionNames();
-
- $countryCode = strtoupper($countryCode);
- $regionCode = strtoupper($regionCode);
-
- if (isset($regionNames[$countryCode][$regionCode]))
- {
- return $regionNames[$countryCode][$regionCode];
- }
- else
- {
- return Piwik_Translate('General_Unknown');
- }
- }
-
- /**
- * Returns an array of region names mapped by country code & region code.
- *
- * @return array
- */
- public static function getRegionNames()
- {
- if (is_null(self::$regionNames))
- {
- require_once PIWIK_INCLUDE_PATH . '/libs/MaxMindGeoIP/geoipregionvars.php';
- self::$regionNames = $GEOIP_REGION_NAME;
- }
-
- return self::$regionNames;
- }
-
- /**
- * Returns the path of an existing GeoIP database or false if none can be found.
- *
- * @param array $possibleFileNames The list of possible file names for the GeoIP database.
- * @return string|false
- */
- public static function getPathToGeoIpDatabase( $possibleFileNames )
- {
- foreach ($possibleFileNames as $filename)
- {
- $path = self::getPathForGeoIpDatabase($filename);
- if (file_exists($path))
- {
- return $path;
- }
- }
- return false;
- }
-
- /**
- * Returns full path for a GeoIP database managed by Piwik.
- *
- * @param string $filename Name of the .dat file.
- * @return string
- */
- public static function getPathForGeoIpDatabase( $filename )
- {
- return PIWIK_INCLUDE_PATH.'/'.self::$geoIPDatabaseDir.'/'.$filename;
- }
-
- /**
- * Returns test IP used by isWorking and expected result.
- *
- * @return array eg. array('1.2.3.4', array(self::COUNTRY_CODE_KEY => ...))
- */
- private static function getTestIpAndResult()
- {
- static $result = null;
- if (is_null($result))
- {
- // TODO: what happens when IP changes? should we get this information from piwik.org?
- $expected = array(self::COUNTRY_CODE_KEY => 'FR',
- self::REGION_CODE_KEY => 'A6',
- self::CITY_NAME_KEY => 'Besançon');
- $result = array(self::TEST_IP, $expected);
- }
- return $result;
- }
-
- /**
- * Returns true if there is a GeoIP database in the 'misc' directory.
- *
- * @return bool
- */
- public static function isDatabaseInstalled()
- {
- return self::getPathToGeoIpDatabase(self::$dbNames['loc'])
- || self::getPathToGeoIpDatabase(self::$dbNames['isp'])
- || self::getPathToGeoIpDatabase(self::$dbNames['org']);
- }
-
- /**
- * Returns the type of GeoIP database ('loc', 'isp' or 'org') based on the
- * filename (eg, 'GeoLiteCity.dat', 'GeoIPISP.dat', etc).
- *
- * @param string $filename
- * @return string|false 'loc', 'isp', 'org', or false if cannot find a database
- * type.
- */
- public static function getGeoIPDatabaseTypeFromFilename( $filename )
- {
- foreach (self::$dbNames as $key => $names)
- {
- foreach ($names as $name)
- {
- if ($name === $filename)
- {
- return $key;
- }
- }
- }
- return false;
- }
+ /* For testing, use: 'http://piwik-team.s3.amazonaws.com/GeoLiteCity.dat.gz' */
+ const GEO_LITE_URL = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz';
+ const TEST_IP = '194.57.91.215';
+
+ public static $geoIPDatabaseDir = 'misc';
+
+ /**
+ * Stores possible database file names categorized by the type of information
+ * GeoIP databases hold.
+ *
+ * @var array
+ */
+ public static $dbNames = array(
+ 'loc' => array('GeoIPCity.dat', 'GeoLiteCity.dat', 'GeoIP.dat'),
+ 'isp' => array('GeoIPISP.dat'),
+ 'org' => array('GeoIPOrg.dat'),
+ );
+
+ /**
+ * Cached region name array. Data is from geoipregionvars.php.
+ *
+ * @var array
+ */
+ private static $regionNames = null;
+
+ /**
+ * Attempts to fill in some missing information in a GeoIP location.
+ *
+ * This method will call LocationProvider::completeLocationResult and then
+ * try to set the region name of the location if the country code & region
+ * code are set.
+ *
+ * @param array $location The location information to modify.
+ */
+ public function completeLocationResult(&$location)
+ {
+ $this->fixupLocation($location);
+ parent::completeLocationResult($location);
+
+ // set region name if region code is set
+ if (empty($location[self::REGION_NAME_KEY])
+ && !empty($location[self::REGION_CODE_KEY])
+ && !empty($location[self::COUNTRY_CODE_KEY])
+ ) {
+ $countryCode = $location[self::COUNTRY_CODE_KEY];
+ $regionCode = (string)$location[self::REGION_CODE_KEY];
+ $location[self::REGION_NAME_KEY] = self::getRegionNameFromCodes($countryCode, $regionCode);
+ }
+ }
+
+ /**
+ * Fix up data to work with our SVG maps which include 'Tib' boundaries
+ */
+ protected function fixupLocation(&$location)
+ {
+ if (!empty($location[self::REGION_CODE_KEY])
+ && $location[self::REGION_CODE_KEY] == '14'
+ && !empty($location[self::COUNTRY_CODE_KEY])
+ && strtoupper($location[self::COUNTRY_CODE_KEY]) == 'CN'
+ ) {
+ $location[self::COUNTRY_CODE_KEY] = 'ti';
+ $location[self::REGION_CODE_KEY] = '1';
+ }
+ }
+
+ /**
+ * Returns true if this provider has been setup correctly, the error message if
+ * otherwise.
+ *
+ * @return bool|string
+ */
+ public function isWorking()
+ {
+ // test with an example IP to make sure the provider is working
+ // NOTE: At the moment only country, region & city info is tested.
+ try {
+ $supportedInfo = $this->getSupportedLocationInfo();
+
+ list($testIp, $expectedResult) = self::getTestIpAndResult();
+
+ // get location using test IP
+ $location = $this->getLocation(array('ip' => $testIp));
+
+ // check that result is the same as expected
+ $isResultCorrect = true;
+ foreach ($expectedResult as $key => $value) {
+ // if this provider is not configured to support this information type, skip it
+ if (empty($supportedInfo[$key])) {
+ continue;
+ }
+
+ if (empty($location[$key])
+ || $location[$key] != $value
+ ) {
+ $isResultCorrect = false;
+ }
+ }
+
+ if (!$isResultCorrect) {
+ $unknown = Piwik_Translate('General_Unknown');
+
+ $location = "'"
+ . (empty($location[self::CITY_NAME_KEY]) ? $unknown : $location[self::CITY_NAME_KEY])
+ . ", "
+ . (empty($location[self::REGION_CODE_KEY]) ? $unknown : $location[self::REGION_CODE_KEY])
+ . ", "
+ . (empty($location[self::COUNTRY_CODE_KEY]) ? $unknown : $location[self::COUNTRY_CODE_KEY])
+ . "'";
+
+ $expectedLocation = "'" . $expectedResult[self::CITY_NAME_KEY] . ", "
+ . $expectedResult[self::REGION_CODE_KEY] . ", "
+ . $expectedResult[self::COUNTRY_CODE_KEY] . "'";
+
+ $bind = array($testIp, $location, $expectedLocation);
+ return Piwik_Translate('UserCountry_TestIPLocatorFailed', $bind);
+ }
+
+ return true;
+ } catch (Exception $ex) {
+ return $ex->getMessage();
+ }
+ }
+
+ /**
+ * Returns a region name for a country code + region code.
+ *
+ * @param string $countryCode
+ * @param string $regionCode
+ * @return string The region name or 'Unknown' (translated).
+ */
+ public static function getRegionNameFromCodes($countryCode, $regionCode)
+ {
+ $regionNames = self::getRegionNames();
+
+ $countryCode = strtoupper($countryCode);
+ $regionCode = strtoupper($regionCode);
+
+ if (isset($regionNames[$countryCode][$regionCode])) {
+ return $regionNames[$countryCode][$regionCode];
+ } else {
+ return Piwik_Translate('General_Unknown');
+ }
+ }
+
+ /**
+ * Returns an array of region names mapped by country code & region code.
+ *
+ * @return array
+ */
+ public static function getRegionNames()
+ {
+ if (is_null(self::$regionNames)) {
+ require_once PIWIK_INCLUDE_PATH . '/libs/MaxMindGeoIP/geoipregionvars.php';
+ self::$regionNames = $GEOIP_REGION_NAME;
+ }
+
+ return self::$regionNames;
+ }
+
+ /**
+ * Returns the path of an existing GeoIP database or false if none can be found.
+ *
+ * @param array $possibleFileNames The list of possible file names for the GeoIP database.
+ * @return string|false
+ */
+ public static function getPathToGeoIpDatabase($possibleFileNames)
+ {
+ foreach ($possibleFileNames as $filename) {
+ $path = self::getPathForGeoIpDatabase($filename);
+ if (file_exists($path)) {
+ return $path;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns full path for a GeoIP database managed by Piwik.
+ *
+ * @param string $filename Name of the .dat file.
+ * @return string
+ */
+ public static function getPathForGeoIpDatabase($filename)
+ {
+ return PIWIK_INCLUDE_PATH . '/' . self::$geoIPDatabaseDir . '/' . $filename;
+ }
+
+ /**
+ * Returns test IP used by isWorking and expected result.
+ *
+ * @return array eg. array('1.2.3.4', array(self::COUNTRY_CODE_KEY => ...))
+ */
+ private static function getTestIpAndResult()
+ {
+ static $result = null;
+ if (is_null($result)) {
+ // TODO: what happens when IP changes? should we get this information from piwik.org?
+ $expected = array(self::COUNTRY_CODE_KEY => 'FR',
+ self::REGION_CODE_KEY => 'A6',
+ self::CITY_NAME_KEY => 'Besançon');
+ $result = array(self::TEST_IP, $expected);
+ }
+ return $result;
+ }
+
+ /**
+ * Returns true if there is a GeoIP database in the 'misc' directory.
+ *
+ * @return bool
+ */
+ public static function isDatabaseInstalled()
+ {
+ return self::getPathToGeoIpDatabase(self::$dbNames['loc'])
+ || self::getPathToGeoIpDatabase(self::$dbNames['isp'])
+ || self::getPathToGeoIpDatabase(self::$dbNames['org']);
+ }
+
+ /**
+ * Returns the type of GeoIP database ('loc', 'isp' or 'org') based on the
+ * filename (eg, 'GeoLiteCity.dat', 'GeoIPISP.dat', etc).
+ *
+ * @param string $filename
+ * @return string|false 'loc', 'isp', 'org', or false if cannot find a database
+ * type.
+ */
+ public static function getGeoIPDatabaseTypeFromFilename($filename)
+ {
+ foreach (self::$dbNames as $key => $names) {
+ foreach ($names as $name) {
+ if ($name === $filename) {
+ return $key;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/plugins/UserCountry/LocationProvider/GeoIp/Pecl.php b/plugins/UserCountry/LocationProvider/GeoIp/Pecl.php
index b484cbf6b5..9ffc4ffa0a 100755
--- a/plugins/UserCountry/LocationProvider/GeoIp/Pecl.php
+++ b/plugins/UserCountry/LocationProvider/GeoIp/Pecl.php
@@ -1,353 +1,324 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserCountry
*/
/**
* A LocationProvider that uses the PECL implementation of GeoIP.
- *
+ *
* FIXME: For some reason, if the PECL module is loaded & an organization DB is available, the PHP
* module won't return organization info. If the PECL module is not loaded, organization info is returned.
- *
+ *
* @package Piwik_UserCountry
*/
class Piwik_UserCountry_LocationProvider_GeoIp_Pecl extends Piwik_UserCountry_LocationProvider_GeoIp
{
- const ID = 'geoip_pecl';
- const TITLE = 'GeoIP (PECL)';
-
- /**
- * Uses the GeoIP PECL module to get a visitor's location based on their IP address.
- *
- * This function will return different results based on the data available. If a city
- * database can be detected by the PECL module, it may return the country code,
- * region code, city name, area code, latitude, longitude and postal code of the visitor.
- *
- * Alternatively, if only the country database can be detected, only the country code
- * will be returned.
- *
- * The GeoIP PECL module will detect the following filenames:
- * - GeoIP.dat
- * - GeoIPCity.dat
- * - GeoIPISP.dat
- * - GeoIPOrg.dat
- *
- * Note how GeoLiteCity.dat, the name for the GeoLite city database, is not detected
- * by the PECL module.
- *
- * @param array $info Must have an 'ip' field.
- * @return array
- */
- public function getLocation( $info )
- {
- $ip = $this->getIpFromInfo($info);
-
- $result = array();
-
- // get location data
- if (self::isCityDatabaseAvailable())
- {
- // Must hide errors because missing IPV6:
- $location = @geoip_record_by_name($ip);
- if (!empty($location))
- {
- $result[self::COUNTRY_CODE_KEY] = $location['country_code'];
- $result[self::REGION_CODE_KEY] = $location['region'];
- $result[self::CITY_NAME_KEY] = utf8_encode($location['city']);
- $result[self::AREA_CODE_KEY] = $location['area_code'];
- $result[self::LATITUDE_KEY] = $location['latitude'];
- $result[self::LONGITUDE_KEY] = $location['longitude'];
- $result[self::POSTAL_CODE_KEY] = $location['postal_code'];
- }
- }
- else if (self::isRegionDatabaseAvailable())
- {
- $location = @geoip_region_by_name($ip);
- if (!empty($location))
- {
- $result[self::REGION_CODE_KEY] = $location['region'];
- $result[self::COUNTRY_CODE_KEY] = $location['country_code'];
- }
- }
- else
- {
- $result[self::COUNTRY_CODE_KEY] = @geoip_country_code_by_name($ip);
- }
-
- // get organization data if the org database is available
- if (self::isOrgDatabaseAvailable())
- {
- $org = @geoip_org_by_name($ip);
- if ($org !== false)
- {
- $result[self::ORG_KEY] = utf8_encode($org);
- }
- }
-
- // get isp data if the isp database is available
- if (self::isISPDatabaseAvailable())
- {
- $isp = @geoip_isp_by_name($ip);
- if ($ip !== false)
- {
- $result[self::ISP_KEY] = utf8_encode($isp);
- }
- }
-
- if (empty($result))
- {
- return false;
- }
-
- $this->completeLocationResult($result);
- return $result;
- }
-
- /**
- * Returns true if the PECL module is installed and loaded, false if otherwise.
- *
- * @return bool
- */
- public function isAvailable()
- {
- return function_exists('geoip_db_avail');
- }
-
- /**
- * Returns true if the PECL module that is installed can be successfully used
- * to get the location of an IP address.
- *
- * @return bool
- */
- public function isWorking()
- {
- // if no no location database is available, this implementation is not setup correctly
- if (!self::isLocationDatabaseAvailable())
- {
- $dbDir = dirname(geoip_db_filename(GEOIP_COUNTRY_EDITION)).'/';
- $quotedDir = "'$dbDir'";
-
- // check if the directory the PECL module is looking for exists
- if (!is_dir($dbDir))
- {
- return Piwik_Translate('UserCountry_PeclGeoIPNoDBDir', array($quotedDir, "'geoip.custom_directory'"));
- }
-
- // check if the user named the city database GeoLiteCity.dat
- if (file_exists($dbDir.'GeoLiteCity.dat'))
- {
- return Piwik_Translate('UserCountry_PeclGeoLiteError',
- array($quotedDir, "'GeoLiteCity.dat'", "'GeoIPCity.dat'"));
- }
-
- return Piwik_Translate('UserCountry_CannotFindPeclGeoIPDb',
- array($quotedDir, "'GeoIP.dat'", "'GeoIPCity.dat'"));
- }
-
- return parent::isWorking();
- }
-
- /**
- * Returns an array describing the types of location information this provider will
- * return.
- *
- * The location info this provider supports depends on what GeoIP databases it can
- * find.
- *
- * This provider will always support country & continent information.
- *
- * If a region database is found, then region code & name information will be
- * supported.
- *
- * If a city database is found, then region code, region name, city name,
- * area code, latitude, longitude & postal code are all supported.
- *
- * If an organization database is found, organization information is
- * supported.
- *
- * If an ISP database is found, ISP information is supported.
- *
- * @return array
- */
- public function getSupportedLocationInfo()
- {
- $result = array();
-
- // country & continent info always available
- $result[self::CONTINENT_CODE_KEY] = true;
- $result[self::CONTINENT_NAME_KEY] = true;
- $result[self::COUNTRY_CODE_KEY] = true;
- $result[self::COUNTRY_NAME_KEY] = true;
-
- if (self::isCityDatabaseAvailable())
- {
- $result[self::REGION_CODE_KEY] = true;
- $result[self::REGION_NAME_KEY] = true;
- $result[self::CITY_NAME_KEY] = true;
- $result[self::AREA_CODE_KEY] = true;
- $result[self::LATITUDE_KEY] = true;
- $result[self::LONGITUDE_KEY] = true;
- $result[self::POSTAL_CODE_KEY] = true;
- }
- else if (self::isRegionDatabaseAvailable())
- {
- $result[self::REGION_CODE_KEY] = true;
- $result[self::REGION_NAME_KEY] = true;
- }
-
- // check if organization info is available
- if (self::isOrgDatabaseAvailable())
- {
- $result[self::ORG_KEY] = true;
- }
-
- // check if ISP info is available
- if (self::isISPDatabaseAvailable())
- {
- $result[self::ISP_KEY] = true;
- }
-
- return $result;
- }
-
- /**
- * Returns information about this location provider. Contains an id, title & description:
- *
- * array(
- * 'id' => 'geoip_pecl',
- * 'title' => '...',
- * 'description' => '...'
- * );
- *
- * @return array
- */
- public function getInfo()
- {
- $desc = Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_Pecl1') . '<br/><br/>'
- . Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_Pecl2');
- $installDocs = '<em>'
- . '<a target="_blank" href="http://piwik.org/faq/how-to/#faq_164">'
- . Piwik_Translate('UserCountry_HowToInstallGeoIpPecl')
- . '</a>'
- . '</em>';
-
- $extraMessage = false;
- if ($this->isAvailable())
- {
- $peclDir = ini_get('geoip.custom_directory');
- if ($peclDir === false)
- {
- $extraMessage = Piwik_Translate('UserCountry_GeoIPPeclCustomDirNotSet', "'geoip.custom_directory'");
- }
- else
- {
- $extraMessage = 'The \'geoip.custom_directory\' PHP ini option is set to \''.$peclDir.'\'.';
- }
-
- $availableDatabaseTypes = array();
- if (self::isCityDatabaseAvailable())
- {
- $availableDatabaseTypes[] = Piwik_Translate('UserCountry_City');
- }
- if (self::isRegionDatabaseAvailable())
- {
- $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Region');
- }
- if (self::isCountryDatabaseAvailable())
- {
- $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Country');
- }
- if (self::isISPDatabaseAvailable())
- {
- $availableDatabaseTypes[] = 'ISP';
- }
- if (self::isOrgDatabaseAvailable())
- {
- $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Organization');
- }
-
- $extraMessage .= '<br/><br/>'.Piwik_Translate('UserCountry_GeoIPImplHasAccessTo').':&nbsp;<strong><em>'
- . implode(', ', $availableDatabaseTypes).'</em></strong>.';
-
- $extraMessage = '<strong><em>'.Piwik_Translate('General_Note').':&nbsp;</em></strong>'.$extraMessage;
- }
-
- return array('id' => self::ID,
- 'title' => self::TITLE,
- 'description' => $desc,
- 'install_docs' => $installDocs,
- 'extra_message' => $extraMessage,
- 'order' => 3);
- }
-
- /**
- * Returns true if the PECL module can detect a location database (either a country,
- * region or city will do).
- *
- * @return bool
- */
- public static function isLocationDatabaseAvailable()
- {
- return self::isCityDatabaseAvailable()
- || self::isRegionDatabaseAvailable()
- || self::isCountryDatabaseAvailable();
- }
-
- /**
- * Returns true if the PECL module can detect a city database.
- *
- * @return bool
- */
- public static function isCityDatabaseAvailable()
- {
- return geoip_db_avail(GEOIP_CITY_EDITION_REV0)
- || geoip_db_avail(GEOIP_CITY_EDITION_REV1);
- }
-
- /**
- * Returns true if the PECL module can detect a region database.
- *
- * @return bool
- */
- public static function isRegionDatabaseAvailable()
- {
- return geoip_db_avail(GEOIP_REGION_EDITION_REV0)
- || geoip_db_avail(GEOIP_REGION_EDITION_REV1);
- }
-
- /**
- * Returns true if the PECL module can detect a country database.
- *
- * @return bool
- */
- public static function isCountryDatabaseAvailable()
- {
- return geoip_db_avail(GEOIP_COUNTRY_EDITION);
- }
-
- /**
- * Returns true if the PECL module can detect an organization database.
- *
- * @return bool
- */
- public static function isOrgDatabaseAvailable()
- {
- return geoip_db_avail(GEOIP_ORG_EDITION);
- }
-
- /**
- * Returns true if the PECL module can detect an ISP database.
- *
- * @return bool
- */
- public static function isISPDatabaseAvailable()
- {
- return geoip_db_avail(GEOIP_ISP_EDITION);
- }
+ const ID = 'geoip_pecl';
+ const TITLE = 'GeoIP (PECL)';
+
+ /**
+ * Uses the GeoIP PECL module to get a visitor's location based on their IP address.
+ *
+ * This function will return different results based on the data available. If a city
+ * database can be detected by the PECL module, it may return the country code,
+ * region code, city name, area code, latitude, longitude and postal code of the visitor.
+ *
+ * Alternatively, if only the country database can be detected, only the country code
+ * will be returned.
+ *
+ * The GeoIP PECL module will detect the following filenames:
+ * - GeoIP.dat
+ * - GeoIPCity.dat
+ * - GeoIPISP.dat
+ * - GeoIPOrg.dat
+ *
+ * Note how GeoLiteCity.dat, the name for the GeoLite city database, is not detected
+ * by the PECL module.
+ *
+ * @param array $info Must have an 'ip' field.
+ * @return array
+ */
+ public function getLocation($info)
+ {
+ $ip = $this->getIpFromInfo($info);
+
+ $result = array();
+
+ // get location data
+ if (self::isCityDatabaseAvailable()) {
+ // Must hide errors because missing IPV6:
+ $location = @geoip_record_by_name($ip);
+ if (!empty($location)) {
+ $result[self::COUNTRY_CODE_KEY] = $location['country_code'];
+ $result[self::REGION_CODE_KEY] = $location['region'];
+ $result[self::CITY_NAME_KEY] = utf8_encode($location['city']);
+ $result[self::AREA_CODE_KEY] = $location['area_code'];
+ $result[self::LATITUDE_KEY] = $location['latitude'];
+ $result[self::LONGITUDE_KEY] = $location['longitude'];
+ $result[self::POSTAL_CODE_KEY] = $location['postal_code'];
+ }
+ } else if (self::isRegionDatabaseAvailable()) {
+ $location = @geoip_region_by_name($ip);
+ if (!empty($location)) {
+ $result[self::REGION_CODE_KEY] = $location['region'];
+ $result[self::COUNTRY_CODE_KEY] = $location['country_code'];
+ }
+ } else {
+ $result[self::COUNTRY_CODE_KEY] = @geoip_country_code_by_name($ip);
+ }
+
+ // get organization data if the org database is available
+ if (self::isOrgDatabaseAvailable()) {
+ $org = @geoip_org_by_name($ip);
+ if ($org !== false) {
+ $result[self::ORG_KEY] = utf8_encode($org);
+ }
+ }
+
+ // get isp data if the isp database is available
+ if (self::isISPDatabaseAvailable()) {
+ $isp = @geoip_isp_by_name($ip);
+ if ($ip !== false) {
+ $result[self::ISP_KEY] = utf8_encode($isp);
+ }
+ }
+
+ if (empty($result)) {
+ return false;
+ }
+
+ $this->completeLocationResult($result);
+ return $result;
+ }
+
+ /**
+ * Returns true if the PECL module is installed and loaded, false if otherwise.
+ *
+ * @return bool
+ */
+ public function isAvailable()
+ {
+ return function_exists('geoip_db_avail');
+ }
+
+ /**
+ * Returns true if the PECL module that is installed can be successfully used
+ * to get the location of an IP address.
+ *
+ * @return bool
+ */
+ public function isWorking()
+ {
+ // if no no location database is available, this implementation is not setup correctly
+ if (!self::isLocationDatabaseAvailable()) {
+ $dbDir = dirname(geoip_db_filename(GEOIP_COUNTRY_EDITION)) . '/';
+ $quotedDir = "'$dbDir'";
+
+ // check if the directory the PECL module is looking for exists
+ if (!is_dir($dbDir)) {
+ return Piwik_Translate('UserCountry_PeclGeoIPNoDBDir', array($quotedDir, "'geoip.custom_directory'"));
+ }
+
+ // check if the user named the city database GeoLiteCity.dat
+ if (file_exists($dbDir . 'GeoLiteCity.dat')) {
+ return Piwik_Translate('UserCountry_PeclGeoLiteError',
+ array($quotedDir, "'GeoLiteCity.dat'", "'GeoIPCity.dat'"));
+ }
+
+ return Piwik_Translate('UserCountry_CannotFindPeclGeoIPDb',
+ array($quotedDir, "'GeoIP.dat'", "'GeoIPCity.dat'"));
+ }
+
+ return parent::isWorking();
+ }
+
+ /**
+ * Returns an array describing the types of location information this provider will
+ * return.
+ *
+ * The location info this provider supports depends on what GeoIP databases it can
+ * find.
+ *
+ * This provider will always support country & continent information.
+ *
+ * If a region database is found, then region code & name information will be
+ * supported.
+ *
+ * If a city database is found, then region code, region name, city name,
+ * area code, latitude, longitude & postal code are all supported.
+ *
+ * If an organization database is found, organization information is
+ * supported.
+ *
+ * If an ISP database is found, ISP information is supported.
+ *
+ * @return array
+ */
+ public function getSupportedLocationInfo()
+ {
+ $result = array();
+
+ // country & continent info always available
+ $result[self::CONTINENT_CODE_KEY] = true;
+ $result[self::CONTINENT_NAME_KEY] = true;
+ $result[self::COUNTRY_CODE_KEY] = true;
+ $result[self::COUNTRY_NAME_KEY] = true;
+
+ if (self::isCityDatabaseAvailable()) {
+ $result[self::REGION_CODE_KEY] = true;
+ $result[self::REGION_NAME_KEY] = true;
+ $result[self::CITY_NAME_KEY] = true;
+ $result[self::AREA_CODE_KEY] = true;
+ $result[self::LATITUDE_KEY] = true;
+ $result[self::LONGITUDE_KEY] = true;
+ $result[self::POSTAL_CODE_KEY] = true;
+ } else if (self::isRegionDatabaseAvailable()) {
+ $result[self::REGION_CODE_KEY] = true;
+ $result[self::REGION_NAME_KEY] = true;
+ }
+
+ // check if organization info is available
+ if (self::isOrgDatabaseAvailable()) {
+ $result[self::ORG_KEY] = true;
+ }
+
+ // check if ISP info is available
+ if (self::isISPDatabaseAvailable()) {
+ $result[self::ISP_KEY] = true;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns information about this location provider. Contains an id, title & description:
+ *
+ * array(
+ * 'id' => 'geoip_pecl',
+ * 'title' => '...',
+ * 'description' => '...'
+ * );
+ *
+ * @return array
+ */
+ public function getInfo()
+ {
+ $desc = Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_Pecl1') . '<br/><br/>'
+ . Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_Pecl2');
+ $installDocs = '<em>'
+ . '<a target="_blank" href="http://piwik.org/faq/how-to/#faq_164">'
+ . Piwik_Translate('UserCountry_HowToInstallGeoIpPecl')
+ . '</a>'
+ . '</em>';
+
+ $extraMessage = false;
+ if ($this->isAvailable()) {
+ $peclDir = ini_get('geoip.custom_directory');
+ if ($peclDir === false) {
+ $extraMessage = Piwik_Translate('UserCountry_GeoIPPeclCustomDirNotSet', "'geoip.custom_directory'");
+ } else {
+ $extraMessage = 'The \'geoip.custom_directory\' PHP ini option is set to \'' . $peclDir . '\'.';
+ }
+
+ $availableDatabaseTypes = array();
+ if (self::isCityDatabaseAvailable()) {
+ $availableDatabaseTypes[] = Piwik_Translate('UserCountry_City');
+ }
+ if (self::isRegionDatabaseAvailable()) {
+ $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Region');
+ }
+ if (self::isCountryDatabaseAvailable()) {
+ $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Country');
+ }
+ if (self::isISPDatabaseAvailable()) {
+ $availableDatabaseTypes[] = 'ISP';
+ }
+ if (self::isOrgDatabaseAvailable()) {
+ $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Organization');
+ }
+
+ $extraMessage .= '<br/><br/>' . Piwik_Translate('UserCountry_GeoIPImplHasAccessTo') . ':&nbsp;<strong><em>'
+ . implode(', ', $availableDatabaseTypes) . '</em></strong>.';
+
+ $extraMessage = '<strong><em>' . Piwik_Translate('General_Note') . ':&nbsp;</em></strong>' . $extraMessage;
+ }
+
+ return array('id' => self::ID,
+ 'title' => self::TITLE,
+ 'description' => $desc,
+ 'install_docs' => $installDocs,
+ 'extra_message' => $extraMessage,
+ 'order' => 3);
+ }
+
+ /**
+ * Returns true if the PECL module can detect a location database (either a country,
+ * region or city will do).
+ *
+ * @return bool
+ */
+ public static function isLocationDatabaseAvailable()
+ {
+ return self::isCityDatabaseAvailable()
+ || self::isRegionDatabaseAvailable()
+ || self::isCountryDatabaseAvailable();
+ }
+
+ /**
+ * Returns true if the PECL module can detect a city database.
+ *
+ * @return bool
+ */
+ public static function isCityDatabaseAvailable()
+ {
+ return geoip_db_avail(GEOIP_CITY_EDITION_REV0)
+ || geoip_db_avail(GEOIP_CITY_EDITION_REV1);
+ }
+
+ /**
+ * Returns true if the PECL module can detect a region database.
+ *
+ * @return bool
+ */
+ public static function isRegionDatabaseAvailable()
+ {
+ return geoip_db_avail(GEOIP_REGION_EDITION_REV0)
+ || geoip_db_avail(GEOIP_REGION_EDITION_REV1);
+ }
+
+ /**
+ * Returns true if the PECL module can detect a country database.
+ *
+ * @return bool
+ */
+ public static function isCountryDatabaseAvailable()
+ {
+ return geoip_db_avail(GEOIP_COUNTRY_EDITION);
+ }
+
+ /**
+ * Returns true if the PECL module can detect an organization database.
+ *
+ * @return bool
+ */
+ public static function isOrgDatabaseAvailable()
+ {
+ return geoip_db_avail(GEOIP_ORG_EDITION);
+ }
+
+ /**
+ * Returns true if the PECL module can detect an ISP database.
+ *
+ * @return bool
+ */
+ public static function isISPDatabaseAvailable()
+ {
+ return geoip_db_avail(GEOIP_ISP_EDITION);
+ }
}
diff --git a/plugins/UserCountry/LocationProvider/GeoIp/Php.php b/plugins/UserCountry/LocationProvider/GeoIp/Php.php
index c2443822fd..7e60ac520a 100755
--- a/plugins/UserCountry/LocationProvider/GeoIp/Php.php
+++ b/plugins/UserCountry/LocationProvider/GeoIp/Php.php
@@ -1,355 +1,330 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserCountry
*/
/**
* A LocationProvider that uses the PHP implementation of GeoIP.
- *
+ *
* @package Piwik_UserCountry
*/
class Piwik_UserCountry_LocationProvider_GeoIp_Php extends Piwik_UserCountry_LocationProvider_GeoIp
{
- const ID = 'geoip_php';
- const TITLE = 'GeoIP (Php)';
-
- /**
- * The GeoIP database instances used. This array will contain at most three
- * of them: one for location info, one for ISP info and another for organization
- * info.
- *
- * Each instance is mapped w/ one of the following keys: 'loc', 'isp', 'org'
- *
- * @var array of GeoIP instances
- */
- private $geoIpCache = array();
-
- /**
- * Possible filenames for each type of GeoIP database. When looking for a database
- * file in the 'misc' subdirectory, files with these names will be looked for.
- *
- * This variable is an array mapping either the 'loc', 'isp' or 'org' strings with
- * an array of filenames.
- *
- * By default, this will be set to Piwik_UserCountry_LocationProvider_GeoIp_Php::$dbNames.
- *
- * @var array
- */
- private $customDbNames;
-
- /**
- * Constructor.
- *
- * @param array|false $customDbNames The possible filenames for each type of GeoIP database.
- * eg array(
- * 'loc' => array('GeoLiteCity.dat'),
- * 'isp' => array('GeoIP.dat', 'GeoIPISP.dat')
- * 'org' => array('GeoIPOrg.dat')
- * )
- * If a key is missing (or the parameter not supplied), then the
- * default database names are used.
- */
- public function __construct( $customDbNames = false )
- {
- $this->customDbNames = parent::$dbNames;
- if ($customDbNames !== false)
- {
- foreach ($this->customDbNames as $key => $names)
- {
- if (isset($customDbNames[$key]))
- {
- $this->customDbNames[$key] = $customDbNames[$key];
- }
- }
- }
- }
-
- /**
- * Closes all open geoip instances.
- */
- public function __destruct()
- {
- foreach ($this->geoIpCache as $instance)
- {
- geoip_close($instance);
- }
- }
-
- /**
- * Uses a GeoIP database to get a visitor's location based on their IP address.
- *
- * This function will return different results based on the data used. If a city
- * database is used, it may return the country code, region code, city name, area
- * code, latitude, longitude and postal code of the visitor.
- *
- * Alternatively, if used with a country database, only the country code will be
- * returned.
- *
- * @param array $info Must have an 'ip' field.
- * @return array
- */
- public function getLocation( $info )
- {
- $ip = $this->getIpFromInfo($info);
-
- $result = array();
-
- $locationGeoIp = $this->getGeoIpInstance($key = 'loc');
- if ($locationGeoIp)
- {
- switch ($locationGeoIp->databaseType)
- {
- case GEOIP_CITY_EDITION_REV0: // city database type
- case GEOIP_CITY_EDITION_REV1:
- case GEOIP_CITYCOMBINED_EDITION:
- $location = geoip_record_by_addr($locationGeoIp, $ip);
- if (!empty($location))
- {
- $result[self::COUNTRY_CODE_KEY] = $location->country_code;
- $result[self::REGION_CODE_KEY] = $location->region;
- $result[self::CITY_NAME_KEY] = utf8_encode($location->city);
- $result[self::AREA_CODE_KEY] = $location->area_code;
- $result[self::LATITUDE_KEY] = $location->latitude;
- $result[self::LONGITUDE_KEY] = $location->longitude;
- $result[self::POSTAL_CODE_KEY] = $location->postal_code;
- }
- break;
- case GEOIP_REGION_EDITION_REV0: // region database type
- case GEOIP_REGION_EDITION_REV1:
- $location = geoip_region_by_addr($locationGeoIp, $ip);
- if (!empty($location))
- {
- $result[self::COUNTRY_CODE_KEY] = $location[0];
- $result[self::REGION_CODE_KEY] = $location[1];
- }
- break;
- case GEOIP_COUNTRY_EDITION: // country database type
- $result[self::COUNTRY_CODE_KEY] = geoip_country_code_by_addr($locationGeoIp, $ip);
- break;
- default: // unknown database type, log warning and fallback to country edition
- Piwik::log(sprintf("Found unrecognized database type: %s", $locationGeoIp->databaseType));
-
- $result[self::COUNTRY_CODE_KEY] = geoip_country_code_by_addr($locationGeoIp, $ip);
- break;
- }
- }
-
- // NOTE: ISP & ORG require commercial dbs to test. this code has been tested manually,
- // but not by integration tests.
- $ispGeoIp = $this->getGeoIpInstance($key = 'isp');
- if ($ispGeoIp)
- {
- $isp = geoip_org_by_addr($ispGeoIp, $ip);
- if (!empty($isp))
- {
- $result[self::ISP_KEY] = utf8_encode($isp);
- }
- }
-
- $orgGeoIp = $this->getGeoIpInstance($key = 'org');
- if ($orgGeoIp)
- {
- $org = geoip_org_by_addr($orgGeoIp, $ip);
- if (!empty($org))
- {
- $result[self::ORG_KEY] = utf8_encode($org);
- }
- }
-
- if (empty($result))
- {
- return false;
- }
-
- $this->completeLocationResult($result);
- return $result;
- }
-
- /**
- * Returns true if this location provider is available. Piwik ships w/ the MaxMind
- * PHP library, so this provider is available if a location GeoIP database can be found.
- *
- * @return bool
- */
- public function isAvailable()
- {
- $path = self::getPathToGeoIpDatabase($this->customDbNames['loc']);
- return $path !== false;
- }
-
- /**
- * Returns true if this provider has been setup correctly, the error message if
- * otherwise.
- *
- * @return bool|string
- */
- public function isWorking()
- {
- if (!function_exists('mb_internal_encoding'))
- {
- return Piwik_Translate('UserCountry_GeoIPCannotFindMbstringExtension',
- array('mb_internal_encoding', 'mbstring'));
- }
-
- return parent::isWorking();
- }
-
- /**
- * Returns an array describing the types of location information this provider will
- * return.
- *
- * The location info this provider supports depends on what GeoIP databases it can
- * find.
- *
- * This provider will always support country & continent information.
- *
- * If a region database is found, then region code & name information will be
- * supported.
- *
- * If a city database is found, then region code, region name, city name,
- * area code, latitude, longitude & postal code are all supported.
- *
- * If an organization database is found, organization information is
- * supported.
- *
- * If an ISP database is found, ISP information is supported.
- *
- * @return array
- */
- public function getSupportedLocationInfo()
- {
- $result = array();
-
- // country & continent info always available
- $result[self::CONTINENT_CODE_KEY] = true;
- $result[self::CONTINENT_NAME_KEY] = true;
- $result[self::COUNTRY_CODE_KEY] = true;
- $result[self::COUNTRY_NAME_KEY] = true;
-
- $locationGeoIp = $this->getGeoIpInstance($key = 'loc');
- if ($locationGeoIp)
- {
- switch ($locationGeoIp->databaseType)
- {
- case GEOIP_CITY_EDITION_REV0: // city database type
- case GEOIP_CITY_EDITION_REV1:
- case GEOIP_CITYCOMBINED_EDITION:
- $result[self::REGION_CODE_KEY] = true;
- $result[self::REGION_NAME_KEY] = true;
- $result[self::CITY_NAME_KEY] = true;
- $result[self::AREA_CODE_KEY] = true;
- $result[self::LATITUDE_KEY] = true;
- $result[self::LONGITUDE_KEY] = true;
- $result[self::POSTAL_CODE_KEY] = true;
- break;
- case GEOIP_REGION_EDITION_REV0: // region database type
- case GEOIP_REGION_EDITION_REV1:
- $result[self::REGION_CODE_KEY] = true;
- $result[self::REGION_NAME_KEY] = true;
- break;
- default: // country or unknown database type
- break;
- }
- }
-
- // check if isp info is available
- if ($this->getGeoIpInstance($key = 'isp'))
- {
- $result[self::ISP_KEY] = true;
- }
-
- // check of org info is available
- if ($this->getGeoIpInstance($key = 'org'))
- {
- $result[self::ORG_KEY] = true;
- }
-
- return $result;
- }
-
- /**
- * Returns information about this location provider. Contains an id, title & description:
- *
- * array(
- * 'id' => 'geoip_php',
- * 'title' => '...',
- * 'description' => '...'
- * );
- *
- * @return array
- */
- public function getInfo()
- {
- $desc = Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_Php1') . '<br/><br/>'
- . Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_Php2',
- array('<strong><em>', '</em></strong>', '<strong><em>', '</em></strong>'));
- $installDocs = '<em><a target="_blank" href="http://piwik.org/faq/how-to/#faq_163">'
- . Piwik_Translate('UserCountry_HowToInstallGeoIPDatabases')
- . '</em></a>';
-
- $availableDatabaseTypes = array();
- if (self::getPathToGeoIpDatabase(array('GeoIPCity.dat', 'GeoLiteCity.dat')) !== false)
- {
- $availableDatabaseTypes[] = Piwik_Translate('UserCountry_City');
- }
- if (self::getPathToGeoIpDatabase(array('GeoIPRegion.dat')) !== false)
- {
- $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Region');
- }
- if (self::getPathToGeoIpDatabase(array('GeoIPCountry.dat')) !== false)
- {
- $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Country');
- }
- if (self::getPathToGeoIpDatabase(array('GeoIPISP.dat')) !== false)
- {
- $availableDatabaseTypes[] = 'ISP';
- }
- if (self::getPathToGeoIpDatabase(array('GeoIPOrg.dat')) !== false)
- {
- $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Organization');
- }
-
- $extraMessage = '<strong><em>'.Piwik_Translate('General_Note').'</em></strong>:&nbsp;'
- . Piwik_Translate('UserCountry_GeoIPImplHasAccessTo').':&nbsp;<strong><em>'
- . implode(', ', $availableDatabaseTypes).'</em></strong>.';
-
- return array('id' => self::ID,
- 'title' => self::TITLE,
- 'description' => $desc,
- 'install_docs' => $installDocs,
- 'extra_message' => $extraMessage,
- 'order' => 2);
- }
-
- /**
- * Returns a GeoIP instance. Creates it if necessary.
- *
- * @param string $key 'loc', 'isp' or 'org'. Determines the type of GeoIP database
- * to load.
- * @return object|false
- */
- private function getGeoIpInstance( $key )
- {
- if (empty($this->geoIpCache[$key]))
- {
- // make sure region names are loaded & saved first
- parent::getRegionNames();
- require_once PIWIK_INCLUDE_PATH . '/libs/MaxMindGeoIP/geoipcity.inc';
-
- $pathToDb = self::getPathToGeoIpDatabase($this->customDbNames[$key]);
- if ($pathToDb !== false)
- {
- $this->geoIpCache[$key] = geoip_open($pathToDb, GEOIP_STANDARD); // TODO support shared memory
- }
- }
-
- return empty($this->geoIpCache[$key]) ? false : $this->geoIpCache[$key];
- }
+ const ID = 'geoip_php';
+ const TITLE = 'GeoIP (Php)';
+
+ /**
+ * The GeoIP database instances used. This array will contain at most three
+ * of them: one for location info, one for ISP info and another for organization
+ * info.
+ *
+ * Each instance is mapped w/ one of the following keys: 'loc', 'isp', 'org'
+ *
+ * @var array of GeoIP instances
+ */
+ private $geoIpCache = array();
+
+ /**
+ * Possible filenames for each type of GeoIP database. When looking for a database
+ * file in the 'misc' subdirectory, files with these names will be looked for.
+ *
+ * This variable is an array mapping either the 'loc', 'isp' or 'org' strings with
+ * an array of filenames.
+ *
+ * By default, this will be set to Piwik_UserCountry_LocationProvider_GeoIp_Php::$dbNames.
+ *
+ * @var array
+ */
+ private $customDbNames;
+
+ /**
+ * Constructor.
+ *
+ * @param array|false $customDbNames The possible filenames for each type of GeoIP database.
+ * eg array(
+ * 'loc' => array('GeoLiteCity.dat'),
+ * 'isp' => array('GeoIP.dat', 'GeoIPISP.dat')
+ * 'org' => array('GeoIPOrg.dat')
+ * )
+ * If a key is missing (or the parameter not supplied), then the
+ * default database names are used.
+ */
+ public function __construct($customDbNames = false)
+ {
+ $this->customDbNames = parent::$dbNames;
+ if ($customDbNames !== false) {
+ foreach ($this->customDbNames as $key => $names) {
+ if (isset($customDbNames[$key])) {
+ $this->customDbNames[$key] = $customDbNames[$key];
+ }
+ }
+ }
+ }
+
+ /**
+ * Closes all open geoip instances.
+ */
+ public function __destruct()
+ {
+ foreach ($this->geoIpCache as $instance) {
+ geoip_close($instance);
+ }
+ }
+
+ /**
+ * Uses a GeoIP database to get a visitor's location based on their IP address.
+ *
+ * This function will return different results based on the data used. If a city
+ * database is used, it may return the country code, region code, city name, area
+ * code, latitude, longitude and postal code of the visitor.
+ *
+ * Alternatively, if used with a country database, only the country code will be
+ * returned.
+ *
+ * @param array $info Must have an 'ip' field.
+ * @return array
+ */
+ public function getLocation($info)
+ {
+ $ip = $this->getIpFromInfo($info);
+
+ $result = array();
+
+ $locationGeoIp = $this->getGeoIpInstance($key = 'loc');
+ if ($locationGeoIp) {
+ switch ($locationGeoIp->databaseType) {
+ case GEOIP_CITY_EDITION_REV0: // city database type
+ case GEOIP_CITY_EDITION_REV1:
+ case GEOIP_CITYCOMBINED_EDITION:
+ $location = geoip_record_by_addr($locationGeoIp, $ip);
+ if (!empty($location)) {
+ $result[self::COUNTRY_CODE_KEY] = $location->country_code;
+ $result[self::REGION_CODE_KEY] = $location->region;
+ $result[self::CITY_NAME_KEY] = utf8_encode($location->city);
+ $result[self::AREA_CODE_KEY] = $location->area_code;
+ $result[self::LATITUDE_KEY] = $location->latitude;
+ $result[self::LONGITUDE_KEY] = $location->longitude;
+ $result[self::POSTAL_CODE_KEY] = $location->postal_code;
+ }
+ break;
+ case GEOIP_REGION_EDITION_REV0: // region database type
+ case GEOIP_REGION_EDITION_REV1:
+ $location = geoip_region_by_addr($locationGeoIp, $ip);
+ if (!empty($location)) {
+ $result[self::COUNTRY_CODE_KEY] = $location[0];
+ $result[self::REGION_CODE_KEY] = $location[1];
+ }
+ break;
+ case GEOIP_COUNTRY_EDITION: // country database type
+ $result[self::COUNTRY_CODE_KEY] = geoip_country_code_by_addr($locationGeoIp, $ip);
+ break;
+ default: // unknown database type, log warning and fallback to country edition
+ Piwik::log(sprintf("Found unrecognized database type: %s", $locationGeoIp->databaseType));
+
+ $result[self::COUNTRY_CODE_KEY] = geoip_country_code_by_addr($locationGeoIp, $ip);
+ break;
+ }
+ }
+
+ // NOTE: ISP & ORG require commercial dbs to test. this code has been tested manually,
+ // but not by integration tests.
+ $ispGeoIp = $this->getGeoIpInstance($key = 'isp');
+ if ($ispGeoIp) {
+ $isp = geoip_org_by_addr($ispGeoIp, $ip);
+ if (!empty($isp)) {
+ $result[self::ISP_KEY] = utf8_encode($isp);
+ }
+ }
+
+ $orgGeoIp = $this->getGeoIpInstance($key = 'org');
+ if ($orgGeoIp) {
+ $org = geoip_org_by_addr($orgGeoIp, $ip);
+ if (!empty($org)) {
+ $result[self::ORG_KEY] = utf8_encode($org);
+ }
+ }
+
+ if (empty($result)) {
+ return false;
+ }
+
+ $this->completeLocationResult($result);
+ return $result;
+ }
+
+ /**
+ * Returns true if this location provider is available. Piwik ships w/ the MaxMind
+ * PHP library, so this provider is available if a location GeoIP database can be found.
+ *
+ * @return bool
+ */
+ public function isAvailable()
+ {
+ $path = self::getPathToGeoIpDatabase($this->customDbNames['loc']);
+ return $path !== false;
+ }
+
+ /**
+ * Returns true if this provider has been setup correctly, the error message if
+ * otherwise.
+ *
+ * @return bool|string
+ */
+ public function isWorking()
+ {
+ if (!function_exists('mb_internal_encoding')) {
+ return Piwik_Translate('UserCountry_GeoIPCannotFindMbstringExtension',
+ array('mb_internal_encoding', 'mbstring'));
+ }
+
+ return parent::isWorking();
+ }
+
+ /**
+ * Returns an array describing the types of location information this provider will
+ * return.
+ *
+ * The location info this provider supports depends on what GeoIP databases it can
+ * find.
+ *
+ * This provider will always support country & continent information.
+ *
+ * If a region database is found, then region code & name information will be
+ * supported.
+ *
+ * If a city database is found, then region code, region name, city name,
+ * area code, latitude, longitude & postal code are all supported.
+ *
+ * If an organization database is found, organization information is
+ * supported.
+ *
+ * If an ISP database is found, ISP information is supported.
+ *
+ * @return array
+ */
+ public function getSupportedLocationInfo()
+ {
+ $result = array();
+
+ // country & continent info always available
+ $result[self::CONTINENT_CODE_KEY] = true;
+ $result[self::CONTINENT_NAME_KEY] = true;
+ $result[self::COUNTRY_CODE_KEY] = true;
+ $result[self::COUNTRY_NAME_KEY] = true;
+
+ $locationGeoIp = $this->getGeoIpInstance($key = 'loc');
+ if ($locationGeoIp) {
+ switch ($locationGeoIp->databaseType) {
+ case GEOIP_CITY_EDITION_REV0: // city database type
+ case GEOIP_CITY_EDITION_REV1:
+ case GEOIP_CITYCOMBINED_EDITION:
+ $result[self::REGION_CODE_KEY] = true;
+ $result[self::REGION_NAME_KEY] = true;
+ $result[self::CITY_NAME_KEY] = true;
+ $result[self::AREA_CODE_KEY] = true;
+ $result[self::LATITUDE_KEY] = true;
+ $result[self::LONGITUDE_KEY] = true;
+ $result[self::POSTAL_CODE_KEY] = true;
+ break;
+ case GEOIP_REGION_EDITION_REV0: // region database type
+ case GEOIP_REGION_EDITION_REV1:
+ $result[self::REGION_CODE_KEY] = true;
+ $result[self::REGION_NAME_KEY] = true;
+ break;
+ default: // country or unknown database type
+ break;
+ }
+ }
+
+ // check if isp info is available
+ if ($this->getGeoIpInstance($key = 'isp')) {
+ $result[self::ISP_KEY] = true;
+ }
+
+ // check of org info is available
+ if ($this->getGeoIpInstance($key = 'org')) {
+ $result[self::ORG_KEY] = true;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns information about this location provider. Contains an id, title & description:
+ *
+ * array(
+ * 'id' => 'geoip_php',
+ * 'title' => '...',
+ * 'description' => '...'
+ * );
+ *
+ * @return array
+ */
+ public function getInfo()
+ {
+ $desc = Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_Php1') . '<br/><br/>'
+ . Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_Php2',
+ array('<strong><em>', '</em></strong>', '<strong><em>', '</em></strong>'));
+ $installDocs = '<em><a target="_blank" href="http://piwik.org/faq/how-to/#faq_163">'
+ . Piwik_Translate('UserCountry_HowToInstallGeoIPDatabases')
+ . '</em></a>';
+
+ $availableDatabaseTypes = array();
+ if (self::getPathToGeoIpDatabase(array('GeoIPCity.dat', 'GeoLiteCity.dat')) !== false) {
+ $availableDatabaseTypes[] = Piwik_Translate('UserCountry_City');
+ }
+ if (self::getPathToGeoIpDatabase(array('GeoIPRegion.dat')) !== false) {
+ $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Region');
+ }
+ if (self::getPathToGeoIpDatabase(array('GeoIPCountry.dat')) !== false) {
+ $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Country');
+ }
+ if (self::getPathToGeoIpDatabase(array('GeoIPISP.dat')) !== false) {
+ $availableDatabaseTypes[] = 'ISP';
+ }
+ if (self::getPathToGeoIpDatabase(array('GeoIPOrg.dat')) !== false) {
+ $availableDatabaseTypes[] = Piwik_Translate('UserCountry_Organization');
+ }
+
+ $extraMessage = '<strong><em>' . Piwik_Translate('General_Note') . '</em></strong>:&nbsp;'
+ . Piwik_Translate('UserCountry_GeoIPImplHasAccessTo') . ':&nbsp;<strong><em>'
+ . implode(', ', $availableDatabaseTypes) . '</em></strong>.';
+
+ return array('id' => self::ID,
+ 'title' => self::TITLE,
+ 'description' => $desc,
+ 'install_docs' => $installDocs,
+ 'extra_message' => $extraMessage,
+ 'order' => 2);
+ }
+
+ /**
+ * Returns a GeoIP instance. Creates it if necessary.
+ *
+ * @param string $key 'loc', 'isp' or 'org'. Determines the type of GeoIP database
+ * to load.
+ * @return object|false
+ */
+ private function getGeoIpInstance($key)
+ {
+ if (empty($this->geoIpCache[$key])) {
+ // make sure region names are loaded & saved first
+ parent::getRegionNames();
+ require_once PIWIK_INCLUDE_PATH . '/libs/MaxMindGeoIP/geoipcity.inc';
+
+ $pathToDb = self::getPathToGeoIpDatabase($this->customDbNames[$key]);
+ if ($pathToDb !== false) {
+ $this->geoIpCache[$key] = geoip_open($pathToDb, GEOIP_STANDARD); // TODO support shared memory
+ }
+ }
+
+ return empty($this->geoIpCache[$key]) ? false : $this->geoIpCache[$key];
+ }
}
diff --git a/plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php b/plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php
index 7be7324744..cdafeefa6a 100755
--- a/plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php
+++ b/plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php
@@ -1,306 +1,277 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserCountry
*/
/**
* A LocationProvider that uses an GeoIP module installed in an HTTP Server.
- *
+ *
* To make this provider available, make sure the GEOIP_ADDR server
* variable is set.
- *
+ *
* @package Piwik_UserCountry
*/
class Piwik_UserCountry_LocationProvider_GeoIp_ServerBased extends Piwik_UserCountry_LocationProvider_GeoIp
{
- const ID = 'geoip_serverbased';
- const TITLE = 'GeoIP (%s)';
- const TEST_SERVER_VAR = 'GEOIP_ADDR';
- const TEST_SERVER_VAR_ALT = 'GEOIP_COUNTRY_CODE';
-
- private static $geoIpServerVars = array(
- parent::COUNTRY_CODE_KEY => 'GEOIP_COUNTRY_CODE',
- parent::COUNTRY_NAME_KEY => 'GEOIP_COUNTRY_NAME',
- parent::REGION_CODE_KEY => 'GEOIP_REGION',
- parent::REGION_NAME_KEY => 'GEOIP_REGION_NAME',
- parent::AREA_CODE_KEY => 'GEOIP_AREA_CODE',
- parent::LATITUDE_KEY => 'GEOIP_LATITUDE',
- parent::LONGITUDE_KEY => 'GEOIP_LONGITUDE',
- parent::POSTAL_CODE_KEY => 'GEOIP_POSTAL_CODE',
- );
-
- private static $geoIpUtfServerVars = array(
- parent::CITY_NAME_KEY => 'GEOIP_CITY',
- parent::ISP_KEY => 'GEOIP_ISP',
- parent::ORG_KEY => 'GEOIP_ORGANIZATION',
- );
-
- /**
- * Uses a GeoIP database to get a visitor's location based on their IP address.
- *
- * This function will return different results based on the data used and based
- * on how the GeoIP module is configured.
- *
- * If a region database is used, it may return the country code, region code,
- * city name, area code, latitude, longitude and postal code of the visitor.
- *
- * Alternatively, only the country code may be returned for another database.
- *
- * If your HTTP server is not configured to include all GeoIP information, some
- * information will not be available to Piwik.
- *
- * @param array $info Must have an 'ip' field.
- * @return array
- */
- public function getLocation( $info )
- {
- $ip = $this->getIpFromInfo($info);
-
- // geoip modules that are built into servers can't use a forced IP. in this case we try
- // to fallback to another version.
- $myIP = Piwik_IP::getIpFromHeader();
- if (!self::isSameOrAnonymizedIp($ip, $myIP)
- && (!isset($info['disable_fallbacks'])
- || !$info['disable_fallbacks']))
- {
- printDebug("The request is for IP address: ".$info['ip'] . " but your IP is: $myIP. GeoIP Server Module (apache/nginx) does not support this use case... ");
- $fallbacks = array(
- Piwik_UserCountry_LocationProvider_GeoIp_Pecl::ID,
- Piwik_UserCountry_LocationProvider_GeoIp_Php::ID
- );
- foreach ($fallbacks as $fallbackProviderId)
- {
- $otherProvider = Piwik_UserCountry_LocationProvider::getProviderById($fallbackProviderId);
- if ($otherProvider)
- {
- printDebug("Used $fallbackProviderId to detect this visitor IP");
- return $otherProvider->getLocation($info);
- }
- }
- printDebug("FAILED to lookup the geo location of this IP address, as no fallback location providers is configured. We recommend to configure Geolocation PECL module to fix this error.");
-
- return false;
- }
-
- $result = array();
- foreach (self::$geoIpServerVars as $resultKey => $geoipVarName)
- {
- if (!empty($_SERVER[$geoipVarName]))
- {
- $result[$resultKey] = $_SERVER[$geoipVarName];
- }
- }
- foreach (self::$geoIpUtfServerVars as $resultKey => $geoipVarName)
- {
- if (!empty($_SERVER[$geoipVarName]))
- {
- $result[$resultKey] = utf8_encode($_SERVER[$geoipVarName]);
- }
- }
- $this->completeLocationResult($result);
- return $result;
- }
-
- /**
- * Returns an array describing the types of location information this provider will
- * return.
- *
- * There's no way to tell exactly what database the HTTP server is using, so we just
- * assume country and continent information is available. This can make diagnostics
- * a bit more difficult, unfortunately.
- *
- * @return array
- */
- public function getSupportedLocationInfo()
- {
- $result = array();
-
- // assume country info is always available. it's an error if it's not.
- $result[self::COUNTRY_CODE_KEY] = true;
- $result[self::COUNTRY_NAME_KEY] = true;
- $result[self::CONTINENT_CODE_KEY] = true;
- $result[self::CONTINENT_NAME_KEY] = true;
-
- return $result;
- }
-
- /**
- * Checks if an HTTP server module has been installed. It checks by looking for
- * the GEOIP_ADDR server variable.
- *
- * There's a special check for the Apache module, but we can't check specifically
- * for anything else.
- *
- * @return bool|string
- */
- public function isAvailable()
- {
- // check if apache module is installed
- if (function_exists('apache_get_modules'))
- {
- foreach (apache_get_modules() as $name)
- {
- if (strpos($name, 'geoip') !== false)
- {
- return true;
- }
- }
- }
-
- $available = !empty($_SERVER[self::TEST_SERVER_VAR])
- || !empty($_SERVER[self::TEST_SERVER_VAR_ALT]);
-
- if ($available)
- {
- return true;
- }
- else // if not available return message w/ extra info
- {
- if (!function_exists('apache_get_modules'))
- {
- return Piwik_Translate('General_Note').':&nbsp;'.Piwik_Translate('UserCountry_AssumingNonApache');
- }
-
- $message = "<strong><em>".Piwik_Translate('General_Note').':&nbsp;'
- . Piwik_Translate('UserCountry_FoundApacheModules')
- . "</em></strong>:<br/><br/>\n<ul style=\"list-style:disc;margin-left:24px\">\n";
- foreach (apache_get_modules() as $name)
- {
- $message .= "<li>$name</li>\n";
- }
- $message .= "</ul>";
- return $message;
- }
- }
-
- /**
- * Returns true if the GEOIP_ADDR server variable is defined.
- *
- * @return bool
- */
- public function isWorking()
- {
- if (empty($_SERVER[self::TEST_SERVER_VAR])
- && empty($_SERVER[self::TEST_SERVER_VAR_ALT]))
- {
- return Piwik_Translate("UserCountry_CannotFindGeoIPServerVar", self::TEST_SERVER_VAR.' $_SERVER');
- }
-
- return true; // can't check for another IP
- }
-
- /**
- * Returns information about this location provider. Contains an id, title & description:
- *
- * array(
- * 'id' => 'geoip_serverbased',
- * 'title' => '...',
- * 'description' => '...'
- * );
- *
- * @return array
- */
- public function getInfo()
- {
- if (function_exists('apache_note'))
- {
- $serverDesc = 'Apache';
- }
- else
- {
- $serverDesc = Piwik_Translate('UserCountry_HttpServerModule');
- }
-
- $title = sprintf(self::TITLE, $serverDesc);
- $desc = Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_ServerBased1', array('<strong>', '</strong>'))
- . '<br/><br/>'
- . '<em>'.Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_ServerBasedAnonWarn').'</em>'
- . '<br/><br/>'
- . Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_ServerBased2',
- array('<strong><em>', '</em></strong>', '<strong><em>', '</em></strong>'));
- $installDocs =
- '<em><a target="_blank" href="http://piwik.org/faq/how-to/#faq_165">'
- . Piwik_Translate('UserCountry_HowToInstallApacheModule')
- . '</a></em><br/><em>'
- . '<a target="_blank" href="http://piwik.org/faq/how-to/#faq_166">'
- . Piwik_Translate('UserCountry_HowToInstallNginxModule')
- . '</a></em>';
-
- $geoipServerVars = array();
- foreach ($_SERVER as $key => $value)
- {
- if (strpos($key, 'GEOIP') === 0)
- {
- $geoipServerVars[] = $key;
- }
- }
-
- if (empty($geoipServerVars))
- {
- $extraMessage = '<strong><em>'.Piwik_Translate('UserCountry_GeoIPNoServerVars', '$_SERVER').'</em></strong>';
- }
- else
- {
- $extraMessage = '<strong><em>'.Piwik_Translate('UserCountry_GeoIPServerVarsFound', '$_SERVER')
- .":</em></strong><br/><br/>\n<ul style=\"list-style:disc;margin-left:24px\">\n";
- foreach ($geoipServerVars as $key)
- {
- $extraMessage .= '<li>'.$key."</li>\n";
- }
- $extraMessage .= '</ul>';
- }
-
- return array('id' => self::ID,
- 'title' => $title,
- 'description' => $desc,
- 'order' => 4,
- 'install_docs' => $installDocs,
- 'extra_message' => $extraMessage);
- }
-
- /**
- * Checks if two IP addresses are the same or if the first is the anonymized
- * version of the other.
- *
- * @param string $ip
- * @param string $currentIp This IP should not be anonymized.
- * @return bool
- */
- public static function isSameOrAnonymizedIp( $ip, $currentIp )
- {
- $ip = array_reverse(explode('.', $ip));
- $currentIp = array_reverse(explode('.', $currentIp));
-
- if (count($ip) != count($currentIp))
- {
- return false;
- }
-
- foreach ($ip as $i => $byte)
- {
- if ($byte == 0)
- {
- $currentIp[$i] = 0;
- }
- else
- {
- break;
- }
- }
-
- foreach ($ip as $i => $byte)
- {
- if ($byte != $currentIp[$i])
- {
- return false;
- }
- }
- return true;
- }
+ const ID = 'geoip_serverbased';
+ const TITLE = 'GeoIP (%s)';
+ const TEST_SERVER_VAR = 'GEOIP_ADDR';
+ const TEST_SERVER_VAR_ALT = 'GEOIP_COUNTRY_CODE';
+
+ private static $geoIpServerVars = array(
+ parent::COUNTRY_CODE_KEY => 'GEOIP_COUNTRY_CODE',
+ parent::COUNTRY_NAME_KEY => 'GEOIP_COUNTRY_NAME',
+ parent::REGION_CODE_KEY => 'GEOIP_REGION',
+ parent::REGION_NAME_KEY => 'GEOIP_REGION_NAME',
+ parent::AREA_CODE_KEY => 'GEOIP_AREA_CODE',
+ parent::LATITUDE_KEY => 'GEOIP_LATITUDE',
+ parent::LONGITUDE_KEY => 'GEOIP_LONGITUDE',
+ parent::POSTAL_CODE_KEY => 'GEOIP_POSTAL_CODE',
+ );
+
+ private static $geoIpUtfServerVars = array(
+ parent::CITY_NAME_KEY => 'GEOIP_CITY',
+ parent::ISP_KEY => 'GEOIP_ISP',
+ parent::ORG_KEY => 'GEOIP_ORGANIZATION',
+ );
+
+ /**
+ * Uses a GeoIP database to get a visitor's location based on their IP address.
+ *
+ * This function will return different results based on the data used and based
+ * on how the GeoIP module is configured.
+ *
+ * If a region database is used, it may return the country code, region code,
+ * city name, area code, latitude, longitude and postal code of the visitor.
+ *
+ * Alternatively, only the country code may be returned for another database.
+ *
+ * If your HTTP server is not configured to include all GeoIP information, some
+ * information will not be available to Piwik.
+ *
+ * @param array $info Must have an 'ip' field.
+ * @return array
+ */
+ public function getLocation($info)
+ {
+ $ip = $this->getIpFromInfo($info);
+
+ // geoip modules that are built into servers can't use a forced IP. in this case we try
+ // to fallback to another version.
+ $myIP = Piwik_IP::getIpFromHeader();
+ if (!self::isSameOrAnonymizedIp($ip, $myIP)
+ && (!isset($info['disable_fallbacks'])
+ || !$info['disable_fallbacks'])
+ ) {
+ printDebug("The request is for IP address: " . $info['ip'] . " but your IP is: $myIP. GeoIP Server Module (apache/nginx) does not support this use case... ");
+ $fallbacks = array(
+ Piwik_UserCountry_LocationProvider_GeoIp_Pecl::ID,
+ Piwik_UserCountry_LocationProvider_GeoIp_Php::ID
+ );
+ foreach ($fallbacks as $fallbackProviderId) {
+ $otherProvider = Piwik_UserCountry_LocationProvider::getProviderById($fallbackProviderId);
+ if ($otherProvider) {
+ printDebug("Used $fallbackProviderId to detect this visitor IP");
+ return $otherProvider->getLocation($info);
+ }
+ }
+ printDebug("FAILED to lookup the geo location of this IP address, as no fallback location providers is configured. We recommend to configure Geolocation PECL module to fix this error.");
+
+ return false;
+ }
+
+ $result = array();
+ foreach (self::$geoIpServerVars as $resultKey => $geoipVarName) {
+ if (!empty($_SERVER[$geoipVarName])) {
+ $result[$resultKey] = $_SERVER[$geoipVarName];
+ }
+ }
+ foreach (self::$geoIpUtfServerVars as $resultKey => $geoipVarName) {
+ if (!empty($_SERVER[$geoipVarName])) {
+ $result[$resultKey] = utf8_encode($_SERVER[$geoipVarName]);
+ }
+ }
+ $this->completeLocationResult($result);
+ return $result;
+ }
+
+ /**
+ * Returns an array describing the types of location information this provider will
+ * return.
+ *
+ * There's no way to tell exactly what database the HTTP server is using, so we just
+ * assume country and continent information is available. This can make diagnostics
+ * a bit more difficult, unfortunately.
+ *
+ * @return array
+ */
+ public function getSupportedLocationInfo()
+ {
+ $result = array();
+
+ // assume country info is always available. it's an error if it's not.
+ $result[self::COUNTRY_CODE_KEY] = true;
+ $result[self::COUNTRY_NAME_KEY] = true;
+ $result[self::CONTINENT_CODE_KEY] = true;
+ $result[self::CONTINENT_NAME_KEY] = true;
+
+ return $result;
+ }
+
+ /**
+ * Checks if an HTTP server module has been installed. It checks by looking for
+ * the GEOIP_ADDR server variable.
+ *
+ * There's a special check for the Apache module, but we can't check specifically
+ * for anything else.
+ *
+ * @return bool|string
+ */
+ public function isAvailable()
+ {
+ // check if apache module is installed
+ if (function_exists('apache_get_modules')) {
+ foreach (apache_get_modules() as $name) {
+ if (strpos($name, 'geoip') !== false) {
+ return true;
+ }
+ }
+ }
+
+ $available = !empty($_SERVER[self::TEST_SERVER_VAR])
+ || !empty($_SERVER[self::TEST_SERVER_VAR_ALT]);
+
+ if ($available) {
+ return true;
+ } else // if not available return message w/ extra info
+ {
+ if (!function_exists('apache_get_modules')) {
+ return Piwik_Translate('General_Note') . ':&nbsp;' . Piwik_Translate('UserCountry_AssumingNonApache');
+ }
+
+ $message = "<strong><em>" . Piwik_Translate('General_Note') . ':&nbsp;'
+ . Piwik_Translate('UserCountry_FoundApacheModules')
+ . "</em></strong>:<br/><br/>\n<ul style=\"list-style:disc;margin-left:24px\">\n";
+ foreach (apache_get_modules() as $name) {
+ $message .= "<li>$name</li>\n";
+ }
+ $message .= "</ul>";
+ return $message;
+ }
+ }
+
+ /**
+ * Returns true if the GEOIP_ADDR server variable is defined.
+ *
+ * @return bool
+ */
+ public function isWorking()
+ {
+ if (empty($_SERVER[self::TEST_SERVER_VAR])
+ && empty($_SERVER[self::TEST_SERVER_VAR_ALT])
+ ) {
+ return Piwik_Translate("UserCountry_CannotFindGeoIPServerVar", self::TEST_SERVER_VAR . ' $_SERVER');
+ }
+
+ return true; // can't check for another IP
+ }
+
+ /**
+ * Returns information about this location provider. Contains an id, title & description:
+ *
+ * array(
+ * 'id' => 'geoip_serverbased',
+ * 'title' => '...',
+ * 'description' => '...'
+ * );
+ *
+ * @return array
+ */
+ public function getInfo()
+ {
+ if (function_exists('apache_note')) {
+ $serverDesc = 'Apache';
+ } else {
+ $serverDesc = Piwik_Translate('UserCountry_HttpServerModule');
+ }
+
+ $title = sprintf(self::TITLE, $serverDesc);
+ $desc = Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_ServerBased1', array('<strong>', '</strong>'))
+ . '<br/><br/>'
+ . '<em>' . Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_ServerBasedAnonWarn') . '</em>'
+ . '<br/><br/>'
+ . Piwik_Translate('UserCountry_GeoIpLocationProviderDesc_ServerBased2',
+ array('<strong><em>', '</em></strong>', '<strong><em>', '</em></strong>'));
+ $installDocs =
+ '<em><a target="_blank" href="http://piwik.org/faq/how-to/#faq_165">'
+ . Piwik_Translate('UserCountry_HowToInstallApacheModule')
+ . '</a></em><br/><em>'
+ . '<a target="_blank" href="http://piwik.org/faq/how-to/#faq_166">'
+ . Piwik_Translate('UserCountry_HowToInstallNginxModule')
+ . '</a></em>';
+
+ $geoipServerVars = array();
+ foreach ($_SERVER as $key => $value) {
+ if (strpos($key, 'GEOIP') === 0) {
+ $geoipServerVars[] = $key;
+ }
+ }
+
+ if (empty($geoipServerVars)) {
+ $extraMessage = '<strong><em>' . Piwik_Translate('UserCountry_GeoIPNoServerVars', '$_SERVER') . '</em></strong>';
+ } else {
+ $extraMessage = '<strong><em>' . Piwik_Translate('UserCountry_GeoIPServerVarsFound', '$_SERVER')
+ . ":</em></strong><br/><br/>\n<ul style=\"list-style:disc;margin-left:24px\">\n";
+ foreach ($geoipServerVars as $key) {
+ $extraMessage .= '<li>' . $key . "</li>\n";
+ }
+ $extraMessage .= '</ul>';
+ }
+
+ return array('id' => self::ID,
+ 'title' => $title,
+ 'description' => $desc,
+ 'order' => 4,
+ 'install_docs' => $installDocs,
+ 'extra_message' => $extraMessage);
+ }
+
+ /**
+ * Checks if two IP addresses are the same or if the first is the anonymized
+ * version of the other.
+ *
+ * @param string $ip
+ * @param string $currentIp This IP should not be anonymized.
+ * @return bool
+ */
+ public static function isSameOrAnonymizedIp($ip, $currentIp)
+ {
+ $ip = array_reverse(explode('.', $ip));
+ $currentIp = array_reverse(explode('.', $currentIp));
+
+ if (count($ip) != count($currentIp)) {
+ return false;
+ }
+
+ foreach ($ip as $i => $byte) {
+ if ($byte == 0) {
+ $currentIp[$i] = 0;
+ } else {
+ break;
+ }
+ }
+
+ foreach ($ip as $i => $byte) {
+ if ($byte != $currentIp[$i]) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/plugins/UserCountry/UserCountry.php b/plugins/UserCountry/UserCountry.php
index 31bda64852..20d2935248 100644
--- a/plugins/UserCountry/UserCountry.php
+++ b/plugins/UserCountry/UserCountry.php
@@ -20,525 +20,505 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/GeoIPAutoUpdater.php';
*/
class Piwik_UserCountry extends Piwik_Plugin
{
- const VISITS_BY_COUNTRY_RECORD_NAME = 'UserCountry_country';
- const VISITS_BY_REGION_RECORD_NAME = 'UserCountry_region';
- const VISITS_BY_CITY_RECORD_NAME = 'UserCountry_city';
-
- const DISTINCT_COUNTRIES_METRIC = 'UserCountry_distinctCountries';
-
- // separate region, city & country info in stored report labels
- const LOCATION_SEPARATOR = '|';
-
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('UserCountry_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- 'TrackerPlugin' => true,
- );
- return $info;
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenu',
- 'AdminMenu.add' => 'addAdminMenu',
- 'Goals.getReportsWithGoalMetrics' => 'getReportsWithGoalMetrics',
- 'API.getReportMetadata' => 'getReportMetadata',
- 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'Tracker.getVisitorLocation' => 'getVisitorLocation',
- 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
- );
- return $hooks;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getScheduledTasks($notification)
- {
- $tasks = &$notification->getNotificationObject();
-
- // add the auto updater task
- $tasks[] = Piwik_UserCountry_GeoIPAutoUpdater::makeScheduledTask();
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "plugins/UserCountry/templates/styles.css";
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getJsFiles( $notification )
+ const VISITS_BY_COUNTRY_RECORD_NAME = 'UserCountry_country';
+ const VISITS_BY_REGION_RECORD_NAME = 'UserCountry_region';
+ const VISITS_BY_CITY_RECORD_NAME = 'UserCountry_city';
+
+ const DISTINCT_COUNTRIES_METRIC = 'UserCountry_distinctCountries';
+
+ // separate region, city & country info in stored report labels
+ const LOCATION_SEPARATOR = '|';
+
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('UserCountry_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ 'TrackerPlugin' => true,
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenu',
+ 'AdminMenu.add' => 'addAdminMenu',
+ 'Goals.getReportsWithGoalMetrics' => 'getReportsWithGoalMetrics',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'Tracker.getVisitorLocation' => 'getVisitorLocation',
+ 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
+ );
+ return $hooks;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getScheduledTasks($notification)
+ {
+ $tasks = & $notification->getNotificationObject();
+
+ // add the auto updater task
+ $tasks[] = Piwik_UserCountry_GeoIPAutoUpdater::makeScheduledTask();
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
{
- $jsFiles = &$notification->getNotificationObject();
+ $cssFiles = & $notification->getNotificationObject();
+
+ $cssFiles[] = "plugins/UserCountry/templates/styles.css";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
$jsFiles[] = "plugins/UserCountry/templates/admin.js";
}
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getVisitorLocation( $notification )
- {
- require_once PIWIK_INCLUDE_PATH . "/plugins/UserCountry/LocationProvider.php";
- $location = &$notification->getNotificationObject();
- $visitorInfo = $notification->getNotificationInfo();
-
- $id = Piwik_Common::getCurrentLocationProviderId();
- $provider = Piwik_UserCountry_LocationProvider::getProviderById($id);
- if ($provider === false)
- {
- $id = Piwik_UserCountry_LocationProvider_Default::ID;
- $provider = Piwik_UserCountry_LocationProvider::getProviderById($id);
- printDebug("GEO: no current location provider sent, falling back to default '$id' one.");
- }
-
- $location = $provider->getLocation($visitorInfo);
-
- // if we can't find a location, use default provider
- if ($location === false)
- {
- $defaultId = Piwik_UserCountry_LocationProvider_Default::ID;
- $provider = Piwik_UserCountry_LocationProvider::getProviderById($defaultId);
- $location = $provider->getLocation($visitorInfo);
- printDebug("GEO: couldn't find a location with Geo Module '$id', using Default '$defaultId' provider as fallback...");
- $id = $defaultId;
- }
- printDebug("GEO: Found IP location (provider '". $id . "'): ". var_export($location, true));
- }
-
- function addWidgets()
- {
- $widgetContinentLabel = Piwik_Translate('UserCountry_WidgetLocation')
- . ' ('.Piwik_Translate('UserCountry_Continent').')';
- $widgetCountryLabel = Piwik_Translate('UserCountry_WidgetLocation')
- . ' ('.Piwik_Translate('UserCountry_Country').')';
- $widgetRegionLabel = Piwik_Translate('UserCountry_WidgetLocation')
- . ' ('.Piwik_Translate('UserCountry_Region').')';
- $widgetCityLabel = Piwik_Translate('UserCountry_WidgetLocation')
- . ' ('.Piwik_Translate('UserCountry_City').')';
-
- Piwik_AddWidget( 'General_Visitors', $widgetContinentLabel, 'UserCountry', 'getContinent');
- Piwik_AddWidget( 'General_Visitors', $widgetCountryLabel, 'UserCountry', 'getCountry');
- Piwik_AddWidget( 'General_Visitors', $widgetRegionLabel, 'UserCountry', 'getRegion');
- Piwik_AddWidget( 'General_Visitors', $widgetCityLabel, 'UserCountry', 'getCity');
- }
-
- function addMenu()
- {
- Piwik_AddMenu('General_Visitors', 'UserCountry_SubmenuLocations', array('module' => 'UserCountry', 'action' => 'index'));
- }
-
- /**
- * Event handler. Adds menu items to the Admin menu.
- */
- function addAdminMenu()
- {
- Piwik_AddAdminSubMenu('General_Settings', 'UserCountry_Geolocation',
- array('module' => 'UserCountry', 'action' => 'adminIndex'),
- Piwik::isUserIsSuperUser(),
- $order = 8);
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getSegmentsMetadata($notification)
- {
- $segments =& $notification->getNotificationObject();
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit Location',
- 'name' => Piwik_Translate('UserCountry_Country'),
- 'segment' => 'country',
- 'sqlSegment' => 'log_visit.location_country',
- 'acceptedValues' => 'de, us, fr, in, es, etc.',
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit Location',
- 'name' => Piwik_Translate('UserCountry_Continent'),
- 'segment' => 'continent',
- 'sqlSegment' => 'log_visit.location_country',
- 'acceptedValues' => 'eur, asi, amc, amn, ams, afr, ant, oce',
- 'sqlFilter' => array('Piwik_UserCountry', 'getCountriesForContinent'),
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit Location',
- 'name' => Piwik_Translate('UserCountry_Region'),
- 'segment' => 'region',
- 'sqlSegment' => 'log_visit.location_region',
- 'acceptedValues' => '01 02, OR, P8, etc.<br/>eg. region=A1;country=fr',
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit Location',
- 'name' => Piwik_Translate('UserCountry_City'),
- 'segment' => 'city',
- 'sqlSegment' => 'log_visit.location_city',
- 'acceptedValues' => 'Sydney, Sao Paolo, Rome, etc.',
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit Location',
- 'name' => Piwik_Translate('UserCountry_Latitude'),
- 'segment' => 'lat',
- 'sqlSegment' => 'log_visit.location_latitude',
- 'acceptedValues' => '-33.578, 40.830, etc.<br/>You can select visitors within a lat/long range using &segment=lat&gt;X;lat&lt;Y;long&gt;M;long&lt;N.',
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit Location',
- 'name' => Piwik_Translate('UserCountry_Longitude'),
- 'segment' => 'long',
- 'sqlSegment' => 'log_visit.location_longitude',
- 'acceptedValues' => '-70.664, 14.326, etc.',
- );
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $metrics = array(
- 'nb_visits' => Piwik_Translate('General_ColumnNbVisits'),
- 'nb_uniq_visitors' => Piwik_Translate('General_ColumnNbUniqVisitors'),
- 'nb_actions' => Piwik_Translate('General_ColumnNbActions'),
- );
-
- $reports = &$notification->getNotificationObject();
-
- $reports[] = array(
- 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('UserCountry_Country'),
- 'module' => 'UserCountry',
- 'action' => 'getCountry',
- 'dimension' => Piwik_Translate('UserCountry_Country'),
- 'metrics' => $metrics,
- 'order' => 5,
- );
-
- $reports[] = array(
- 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('UserCountry_Continent'),
- 'module' => 'UserCountry',
- 'action' => 'getContinent',
- 'dimension' => Piwik_Translate('UserCountry_Continent'),
- 'metrics' => $metrics,
- 'order' => 6,
- );
-
- $reports[] = array(
- 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('UserCountry_Region'),
- 'module' => 'UserCountry',
- 'action' => 'getRegion',
- 'dimension' => Piwik_Translate('UserCountry_Region'),
- 'metrics' => $metrics,
- 'order' => 7,
- );
-
- $reports[] = array(
- 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('UserCountry_City'),
- 'module' => 'UserCountry',
- 'action' => 'getCity',
- 'dimension' => Piwik_Translate('UserCountry_City'),
- 'metrics' => $metrics,
- 'order' => 8,
- );
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportsWithGoalMetrics( $notification )
- {
- $dimensions =& $notification->getNotificationObject();
- $dimensions = array_merge($dimensions, array(
- array( 'category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('UserCountry_Country'),
- 'module' => 'UserCountry',
- 'action' => 'getCountry',
- ),
- array( 'category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('UserCountry_Continent'),
- 'module' => 'UserCountry',
- 'action' => 'getContinent',
- ),
- array('category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('UserCountry_Region'),
- 'module' => 'UserCountry',
- 'action' => 'getRegion'),
- array('category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('UserCountry_City'),
- 'module' => 'UserCountry',
- 'action' => 'getCity'),
- ));
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archivePeriod( $notification )
- {
- /**
- * @param Piwik_ArchiveProcessing_Period $archiveProcessing
- */
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = array(
- self::VISITS_BY_COUNTRY_RECORD_NAME,
- self::VISITS_BY_REGION_RECORD_NAME,
- self::VISITS_BY_CITY_RECORD_NAME,
- );
-
- $nameToCount = $archiveProcessing->archiveDataTable($dataTableToSum);
- $archiveProcessing->insertNumericRecord(self::DISTINCT_COUNTRIES_METRIC,
- $nameToCount[self::VISITS_BY_COUNTRY_RECORD_NAME]['level0']);
- }
-
- private $interestTables = null;
- private $latLongForCities = null;
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archiveDay($notification)
- {
- /**
- * @var Piwik_ArchiveProcessing
- */
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->interestTables = array('location_country' => array(),
- 'location_region' => array(),
- 'location_city' => array());
- $this->latLongForCities = array();
-
- $this->archiveDayAggregateVisits($archiveProcessing);
- $this->archiveDayAggregateGoals($archiveProcessing);
- $this->archiveDayRecordInDatabase($archiveProcessing);
-
- unset($this->interestTables);
- unset($this->latLongForCities);
- }
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- protected function archiveDayAggregateVisits($archiveProcessing)
- {
- $dimensions = array_keys($this->interestTables);
- $query = $archiveProcessing->queryVisitsByDimension(
- $dimensions,
- $where = '',
- $metrics = false,
- $orderBy = false,
- $rankingQuery = null,
- $addSelect = 'MAX(log_visit.location_latitude) as location_latitude,
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getVisitorLocation($notification)
+ {
+ require_once PIWIK_INCLUDE_PATH . "/plugins/UserCountry/LocationProvider.php";
+ $location = & $notification->getNotificationObject();
+ $visitorInfo = $notification->getNotificationInfo();
+
+ $id = Piwik_Common::getCurrentLocationProviderId();
+ $provider = Piwik_UserCountry_LocationProvider::getProviderById($id);
+ if ($provider === false) {
+ $id = Piwik_UserCountry_LocationProvider_Default::ID;
+ $provider = Piwik_UserCountry_LocationProvider::getProviderById($id);
+ printDebug("GEO: no current location provider sent, falling back to default '$id' one.");
+ }
+
+ $location = $provider->getLocation($visitorInfo);
+
+ // if we can't find a location, use default provider
+ if ($location === false) {
+ $defaultId = Piwik_UserCountry_LocationProvider_Default::ID;
+ $provider = Piwik_UserCountry_LocationProvider::getProviderById($defaultId);
+ $location = $provider->getLocation($visitorInfo);
+ printDebug("GEO: couldn't find a location with Geo Module '$id', using Default '$defaultId' provider as fallback...");
+ $id = $defaultId;
+ }
+ printDebug("GEO: Found IP location (provider '" . $id . "'): " . var_export($location, true));
+ }
+
+ function addWidgets()
+ {
+ $widgetContinentLabel = Piwik_Translate('UserCountry_WidgetLocation')
+ . ' (' . Piwik_Translate('UserCountry_Continent') . ')';
+ $widgetCountryLabel = Piwik_Translate('UserCountry_WidgetLocation')
+ . ' (' . Piwik_Translate('UserCountry_Country') . ')';
+ $widgetRegionLabel = Piwik_Translate('UserCountry_WidgetLocation')
+ . ' (' . Piwik_Translate('UserCountry_Region') . ')';
+ $widgetCityLabel = Piwik_Translate('UserCountry_WidgetLocation')
+ . ' (' . Piwik_Translate('UserCountry_City') . ')';
+
+ Piwik_AddWidget('General_Visitors', $widgetContinentLabel, 'UserCountry', 'getContinent');
+ Piwik_AddWidget('General_Visitors', $widgetCountryLabel, 'UserCountry', 'getCountry');
+ Piwik_AddWidget('General_Visitors', $widgetRegionLabel, 'UserCountry', 'getRegion');
+ Piwik_AddWidget('General_Visitors', $widgetCityLabel, 'UserCountry', 'getCity');
+ }
+
+ function addMenu()
+ {
+ Piwik_AddMenu('General_Visitors', 'UserCountry_SubmenuLocations', array('module' => 'UserCountry', 'action' => 'index'));
+ }
+
+ /**
+ * Event handler. Adds menu items to the Admin menu.
+ */
+ function addAdminMenu()
+ {
+ Piwik_AddAdminSubMenu('General_Settings', 'UserCountry_Geolocation',
+ array('module' => 'UserCountry', 'action' => 'adminIndex'),
+ Piwik::isUserIsSuperUser(),
+ $order = 8);
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ $segments =& $notification->getNotificationObject();
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit Location',
+ 'name' => Piwik_Translate('UserCountry_Country'),
+ 'segment' => 'country',
+ 'sqlSegment' => 'log_visit.location_country',
+ 'acceptedValues' => 'de, us, fr, in, es, etc.',
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit Location',
+ 'name' => Piwik_Translate('UserCountry_Continent'),
+ 'segment' => 'continent',
+ 'sqlSegment' => 'log_visit.location_country',
+ 'acceptedValues' => 'eur, asi, amc, amn, ams, afr, ant, oce',
+ 'sqlFilter' => array('Piwik_UserCountry', 'getCountriesForContinent'),
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit Location',
+ 'name' => Piwik_Translate('UserCountry_Region'),
+ 'segment' => 'region',
+ 'sqlSegment' => 'log_visit.location_region',
+ 'acceptedValues' => '01 02, OR, P8, etc.<br/>eg. region=A1;country=fr',
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit Location',
+ 'name' => Piwik_Translate('UserCountry_City'),
+ 'segment' => 'city',
+ 'sqlSegment' => 'log_visit.location_city',
+ 'acceptedValues' => 'Sydney, Sao Paolo, Rome, etc.',
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit Location',
+ 'name' => Piwik_Translate('UserCountry_Latitude'),
+ 'segment' => 'lat',
+ 'sqlSegment' => 'log_visit.location_latitude',
+ 'acceptedValues' => '-33.578, 40.830, etc.<br/>You can select visitors within a lat/long range using &segment=lat&gt;X;lat&lt;Y;long&gt;M;long&lt;N.',
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit Location',
+ 'name' => Piwik_Translate('UserCountry_Longitude'),
+ 'segment' => 'long',
+ 'sqlSegment' => 'log_visit.location_longitude',
+ 'acceptedValues' => '-70.664, 14.326, etc.',
+ );
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $metrics = array(
+ 'nb_visits' => Piwik_Translate('General_ColumnNbVisits'),
+ 'nb_uniq_visitors' => Piwik_Translate('General_ColumnNbUniqVisitors'),
+ 'nb_actions' => Piwik_Translate('General_ColumnNbActions'),
+ );
+
+ $reports = & $notification->getNotificationObject();
+
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('UserCountry_Country'),
+ 'module' => 'UserCountry',
+ 'action' => 'getCountry',
+ 'dimension' => Piwik_Translate('UserCountry_Country'),
+ 'metrics' => $metrics,
+ 'order' => 5,
+ );
+
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('UserCountry_Continent'),
+ 'module' => 'UserCountry',
+ 'action' => 'getContinent',
+ 'dimension' => Piwik_Translate('UserCountry_Continent'),
+ 'metrics' => $metrics,
+ 'order' => 6,
+ );
+
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('UserCountry_Region'),
+ 'module' => 'UserCountry',
+ 'action' => 'getRegion',
+ 'dimension' => Piwik_Translate('UserCountry_Region'),
+ 'metrics' => $metrics,
+ 'order' => 7,
+ );
+
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('UserCountry_City'),
+ 'module' => 'UserCountry',
+ 'action' => 'getCity',
+ 'dimension' => Piwik_Translate('UserCountry_City'),
+ 'metrics' => $metrics,
+ 'order' => 8,
+ );
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportsWithGoalMetrics($notification)
+ {
+ $dimensions =& $notification->getNotificationObject();
+ $dimensions = array_merge($dimensions, array(
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('UserCountry_Country'),
+ 'module' => 'UserCountry',
+ 'action' => 'getCountry',
+ ),
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('UserCountry_Continent'),
+ 'module' => 'UserCountry',
+ 'action' => 'getContinent',
+ ),
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('UserCountry_Region'),
+ 'module' => 'UserCountry',
+ 'action' => 'getRegion'),
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('UserCountry_City'),
+ 'module' => 'UserCountry',
+ 'action' => 'getCity'),
+ ));
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ function archivePeriod($notification)
+ {
+ /**
+ * @param Piwik_ArchiveProcessing_Period $archiveProcessing
+ */
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $dataTableToSum = array(
+ self::VISITS_BY_COUNTRY_RECORD_NAME,
+ self::VISITS_BY_REGION_RECORD_NAME,
+ self::VISITS_BY_CITY_RECORD_NAME,
+ );
+
+ $nameToCount = $archiveProcessing->archiveDataTable($dataTableToSum);
+ $archiveProcessing->insertNumericRecord(self::DISTINCT_COUNTRIES_METRIC,
+ $nameToCount[self::VISITS_BY_COUNTRY_RECORD_NAME]['level0']);
+ }
+
+ private $interestTables = null;
+ private $latLongForCities = null;
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ function archiveDay($notification)
+ {
+ /**
+ * @var Piwik_ArchiveProcessing
+ */
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $this->interestTables = array('location_country' => array(),
+ 'location_region' => array(),
+ 'location_city' => array());
+ $this->latLongForCities = array();
+
+ $this->archiveDayAggregateVisits($archiveProcessing);
+ $this->archiveDayAggregateGoals($archiveProcessing);
+ $this->archiveDayRecordInDatabase($archiveProcessing);
+
+ unset($this->interestTables);
+ unset($this->latLongForCities);
+ }
+
+ /**
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ */
+ protected function archiveDayAggregateVisits($archiveProcessing)
+ {
+ $dimensions = array_keys($this->interestTables);
+ $query = $archiveProcessing->queryVisitsByDimension(
+ $dimensions,
+ $where = '',
+ $metrics = false,
+ $orderBy = false,
+ $rankingQuery = null,
+ $addSelect = 'MAX(log_visit.location_latitude) as location_latitude,
MAX(log_visit.location_longitude) as location_longitude'
- );
-
- if ($query === false)
- {
- return;
- }
-
- while ($row = $query->fetch())
- {
- // get latitude/longitude if there's a city
- $lat = $long = false;
- if (!empty($row['location_city']))
- {
- if (!empty($row['location_latitude']))
- {
- $lat = $row['location_latitude'];
- }
- if (!empty($row['location_longitude']))
- {
- $long = $row['location_longitude'];
- }
- }
-
- // make sure regions & cities w/ the same name don't get merged
- $this->setLongCityRegionId($row);
-
- // store latitude/longitude, if we should
- if ($lat !== false && $long !== false)
- {
- $this->latLongForCities[$row['location_city']] = array($lat, $long);
- }
-
- // add the stats to each dimension's table
- foreach ($this->interestTables as $dimension => &$table)
- {
- $label = (string)$row[$dimension];
-
- if (!isset($table[$label]))
- {
- $table[$label] = $archiveProcessing->getNewInterestRow();
- }
- $archiveProcessing->updateInterestStats($row, $table[$label]);
- }
- }
- }
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- protected function archiveDayAggregateGoals($archiveProcessing)
- {
- $dimensions = array_keys($this->interestTables);
- $query = $archiveProcessing->queryConversionsByDimension($dimensions);
-
- if ($query === false)
- {
- return;
- }
-
- while ($row = $query->fetch())
- {
- // make sure regions & cities w/ the same name don't get merged
- $this->setLongCityRegionId($row);
-
- $idGoal = $row['idgoal'];
- foreach ($this->interestTables as $dimension => &$table)
- {
- $label = (string)$row[$dimension];
-
- if (!isset($table[$label][Piwik_Archive::INDEX_GOALS][$idGoal]))
- {
- $table[$label][Piwik_Archive::INDEX_GOALS][$idGoal] = $archiveProcessing->getNewGoalRow($idGoal);
- }
-
- $archiveProcessing->updateGoalStats($row, $table[$label][Piwik_Archive::INDEX_GOALS][$idGoal]);
- }
- }
-
- foreach ($this->interestTables as &$table)
- {
- $archiveProcessing->enrichConversionsByLabelArray($table);
- }
- }
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- protected function archiveDayRecordInDatabase($archiveProcessing)
- {
- $maximumRows = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
-
- $tableCountry = Piwik_ArchiveProcessing_Day::getDataTableFromArray($this->interestTables['location_country']);
- $archiveProcessing->insertBlobRecord(self::VISITS_BY_COUNTRY_RECORD_NAME, $tableCountry->getSerialized());
- $archiveProcessing->insertNumericRecord(self::DISTINCT_COUNTRIES_METRIC, $tableCountry->getRowsCount());
- destroy($tableCountry);
-
- $tableRegion = Piwik_ArchiveProcessing_Day::getDataTableFromArray($this->interestTables['location_region']);
- $serialized = $tableRegion->getSerialized($maximumRows, $maximumRows, Piwik_Archive::INDEX_NB_VISITS);
- $archiveProcessing->insertBlobRecord(self::VISITS_BY_REGION_RECORD_NAME, $serialized);
- destroy($tableRegion);
-
- $tableCity = Piwik_ArchiveProcessing_Day::getDataTableFromArray($this->interestTables['location_city']);
- $this->setLatitudeLongitude($tableCity);
- $serialized = $tableCity->getSerialized($maximumRows, $maximumRows, Piwik_Archive::INDEX_NB_VISITS);
- $archiveProcessing->insertBlobRecord(self::VISITS_BY_CITY_RECORD_NAME, $serialized);
- destroy($tableCity);
- }
-
- /**
- * Makes sure the region and city of a query row are unique.
- *
- * @param array $row
- */
- private function setLongCityRegionId( &$row )
- {
- static $locationColumns = array('location_region', 'location_country', 'location_city');
-
- // to be on the safe side, remove the location separator from the region/city/country we
- // get from the query
- foreach ($locationColumns as $column)
- {
- $row[$column] = str_replace(self::LOCATION_SEPARATOR, '', $row[$column]);
- }
-
- if (!empty($row['location_region'])) // do not differentiate between unknown regions
- {
- $row['location_region'] = $row['location_region'].self::LOCATION_SEPARATOR.$row['location_country'];
- }
-
- if (!empty($row['location_city'])) // do not differentiate between unknown cities
- {
- $row['location_city'] = $row['location_city'].self::LOCATION_SEPARATOR.$row['location_region'];
- }
- }
-
- /**
- * Returns a list of country codes for a given continent code.
- *
- * @param string $continent The continent code.
- * @return array
- */
- public static function getCountriesForContinent( $continent )
- {
- $continent = strtolower($continent);
-
- $result = array();
- foreach (Piwik_Common::getCountriesList() as $countryCode => $continentCode)
- {
- if ($continent == $continentCode)
- {
- $result[] = $countryCode;
- }
- }
- return array('SQL' => "'".implode("', '", $result)."', ?",
- 'bind' => '-'); // HACK: SegmentExpression requires a $bind, even if there's nothing to bind
- }
-
- /**
- * Utility method, appends latitude/longitude pairs to city table labels, if that data
- * exists for the city.
- */
- private function setLatitudeLongitude( $tableCity )
- {
- foreach ($tableCity->getRows() as $row)
- {
- $label = $row->getColumn('label');
- if (isset($this->latLongForCities[$label]))
- {
- // get lat/long for city
- list($lat, $long) = $this->latLongForCities[$label];
- $lat = round($lat, Piwik_UserCountry_LocationProvider::GEOGRAPHIC_COORD_PRECISION);
- $long = round($long, Piwik_UserCountry_LocationProvider::GEOGRAPHIC_COORD_PRECISION);
-
- // set latitude + longitude metadata
- $row->setMetadata('lat', $lat);
- $row->setMetadata('long', $long);
- }
- }
- }
+ );
+
+ if ($query === false) {
+ return;
+ }
+
+ while ($row = $query->fetch()) {
+ // get latitude/longitude if there's a city
+ $lat = $long = false;
+ if (!empty($row['location_city'])) {
+ if (!empty($row['location_latitude'])) {
+ $lat = $row['location_latitude'];
+ }
+ if (!empty($row['location_longitude'])) {
+ $long = $row['location_longitude'];
+ }
+ }
+
+ // make sure regions & cities w/ the same name don't get merged
+ $this->setLongCityRegionId($row);
+
+ // store latitude/longitude, if we should
+ if ($lat !== false && $long !== false) {
+ $this->latLongForCities[$row['location_city']] = array($lat, $long);
+ }
+
+ // add the stats to each dimension's table
+ foreach ($this->interestTables as $dimension => &$table) {
+ $label = (string)$row[$dimension];
+
+ if (!isset($table[$label])) {
+ $table[$label] = $archiveProcessing->getNewInterestRow();
+ }
+ $archiveProcessing->updateInterestStats($row, $table[$label]);
+ }
+ }
+ }
+
+ /**
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ */
+ protected function archiveDayAggregateGoals($archiveProcessing)
+ {
+ $dimensions = array_keys($this->interestTables);
+ $query = $archiveProcessing->queryConversionsByDimension($dimensions);
+
+ if ($query === false) {
+ return;
+ }
+
+ while ($row = $query->fetch()) {
+ // make sure regions & cities w/ the same name don't get merged
+ $this->setLongCityRegionId($row);
+
+ $idGoal = $row['idgoal'];
+ foreach ($this->interestTables as $dimension => &$table) {
+ $label = (string)$row[$dimension];
+
+ if (!isset($table[$label][Piwik_Archive::INDEX_GOALS][$idGoal])) {
+ $table[$label][Piwik_Archive::INDEX_GOALS][$idGoal] = $archiveProcessing->getNewGoalRow($idGoal);
+ }
+
+ $archiveProcessing->updateGoalStats($row, $table[$label][Piwik_Archive::INDEX_GOALS][$idGoal]);
+ }
+ }
+
+ foreach ($this->interestTables as &$table) {
+ $archiveProcessing->enrichConversionsByLabelArray($table);
+ }
+ }
+
+ /**
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ */
+ protected function archiveDayRecordInDatabase($archiveProcessing)
+ {
+ $maximumRows = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
+
+ $tableCountry = Piwik_ArchiveProcessing_Day::getDataTableFromArray($this->interestTables['location_country']);
+ $archiveProcessing->insertBlobRecord(self::VISITS_BY_COUNTRY_RECORD_NAME, $tableCountry->getSerialized());
+ $archiveProcessing->insertNumericRecord(self::DISTINCT_COUNTRIES_METRIC, $tableCountry->getRowsCount());
+ destroy($tableCountry);
+
+ $tableRegion = Piwik_ArchiveProcessing_Day::getDataTableFromArray($this->interestTables['location_region']);
+ $serialized = $tableRegion->getSerialized($maximumRows, $maximumRows, Piwik_Archive::INDEX_NB_VISITS);
+ $archiveProcessing->insertBlobRecord(self::VISITS_BY_REGION_RECORD_NAME, $serialized);
+ destroy($tableRegion);
+
+ $tableCity = Piwik_ArchiveProcessing_Day::getDataTableFromArray($this->interestTables['location_city']);
+ $this->setLatitudeLongitude($tableCity);
+ $serialized = $tableCity->getSerialized($maximumRows, $maximumRows, Piwik_Archive::INDEX_NB_VISITS);
+ $archiveProcessing->insertBlobRecord(self::VISITS_BY_CITY_RECORD_NAME, $serialized);
+ destroy($tableCity);
+ }
+
+ /**
+ * Makes sure the region and city of a query row are unique.
+ *
+ * @param array $row
+ */
+ private function setLongCityRegionId(&$row)
+ {
+ static $locationColumns = array('location_region', 'location_country', 'location_city');
+
+ // to be on the safe side, remove the location separator from the region/city/country we
+ // get from the query
+ foreach ($locationColumns as $column) {
+ $row[$column] = str_replace(self::LOCATION_SEPARATOR, '', $row[$column]);
+ }
+
+ if (!empty($row['location_region'])) // do not differentiate between unknown regions
+ {
+ $row['location_region'] = $row['location_region'] . self::LOCATION_SEPARATOR . $row['location_country'];
+ }
+
+ if (!empty($row['location_city'])) // do not differentiate between unknown cities
+ {
+ $row['location_city'] = $row['location_city'] . self::LOCATION_SEPARATOR . $row['location_region'];
+ }
+ }
+
+ /**
+ * Returns a list of country codes for a given continent code.
+ *
+ * @param string $continent The continent code.
+ * @return array
+ */
+ public static function getCountriesForContinent($continent)
+ {
+ $continent = strtolower($continent);
+
+ $result = array();
+ foreach (Piwik_Common::getCountriesList() as $countryCode => $continentCode) {
+ if ($continent == $continentCode) {
+ $result[] = $countryCode;
+ }
+ }
+ return array('SQL' => "'" . implode("', '", $result) . "', ?",
+ 'bind' => '-'); // HACK: SegmentExpression requires a $bind, even if there's nothing to bind
+ }
+
+ /**
+ * Utility method, appends latitude/longitude pairs to city table labels, if that data
+ * exists for the city.
+ */
+ private function setLatitudeLongitude($tableCity)
+ {
+ foreach ($tableCity->getRows() as $row) {
+ $label = $row->getColumn('label');
+ if (isset($this->latLongForCities[$label])) {
+ // get lat/long for city
+ list($lat, $long) = $this->latLongForCities[$label];
+ $lat = round($lat, Piwik_UserCountry_LocationProvider::GEOGRAPHIC_COORD_PRECISION);
+ $long = round($long, Piwik_UserCountry_LocationProvider::GEOGRAPHIC_COORD_PRECISION);
+
+ // set latitude + longitude metadata
+ $row->setMetadata('lat', $lat);
+ $row->setMetadata('long', $long);
+ }
+ }
+ }
}
diff --git a/plugins/UserCountry/functions.php b/plugins/UserCountry/functions.php
index a7e78dec52..ab1ff7e26a 100644
--- a/plugins/UserCountry/functions.php
+++ b/plugins/UserCountry/functions.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserCountry
*/
@@ -17,14 +17,13 @@
*/
function Piwik_getFlagFromCode($code)
{
- $pathInPiwik = 'plugins/UserCountry/flags/%s.png';
- $pathWithCode = sprintf($pathInPiwik, $code);
- $absolutePath = PIWIK_INCLUDE_PATH . '/' . $pathWithCode;
- if(file_exists($absolutePath))
- {
- return $pathWithCode;
- }
- return sprintf($pathInPiwik, Piwik_Tracker_Visit::UNKNOWN_CODE);
+ $pathInPiwik = 'plugins/UserCountry/flags/%s.png';
+ $pathWithCode = sprintf($pathInPiwik, $code);
+ $absolutePath = PIWIK_INCLUDE_PATH . '/' . $pathWithCode;
+ if (file_exists($absolutePath)) {
+ return $pathWithCode;
+ }
+ return sprintf($pathInPiwik, Piwik_Tracker_Visit::UNKNOWN_CODE);
}
/**
@@ -35,11 +34,10 @@ function Piwik_getFlagFromCode($code)
*/
function Piwik_ContinentTranslate($label)
{
- if($label == 'unk' || $label == '')
- {
- return Piwik_Translate('General_Unknown');
- }
- return Piwik_Translate('UserCountry_continent_'. $label);
+ if ($label == 'unk' || $label == '') {
+ return Piwik_Translate('General_Unknown');
+ }
+ return Piwik_Translate('UserCountry_continent_' . $label);
}
/**
@@ -50,16 +48,15 @@ function Piwik_ContinentTranslate($label)
*/
function Piwik_CountryTranslate($label)
{
- if($label == Piwik_Tracker_Visit::UNKNOWN_CODE || $label == '')
- {
- return Piwik_Translate('General_Unknown');
- }
- return Piwik_Translate('UserCountry_country_'. $label);
+ if ($label == Piwik_Tracker_Visit::UNKNOWN_CODE || $label == '') {
+ return Piwik_Translate('General_Unknown');
+ }
+ return Piwik_Translate('UserCountry_country_' . $label);
}
/**
* Splits a label by a certain separator and returns the N-th element.
- *
+ *
* @param string $label
* @param string $separator eg. ',' or '|'
* @param int $index The element index to extract.
@@ -68,111 +65,100 @@ function Piwik_CountryTranslate($label)
* @return string|false Returns false if $label == DataTable::LABEL_SUMMARY_ROW, otherwise
* explode($separator, $label)[$index].
*/
-function Piwik_UserCountry_getElementFromStringArray( $label, $separator, $index, $emptyValue = false )
+function Piwik_UserCountry_getElementFromStringArray($label, $separator, $index, $emptyValue = false)
{
- if ($label == Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- return false; // so no metadata/column is added
- }
-
- $segments = explode($separator, $label);
- return empty($segments[$index]) ? $emptyValue : $segments[$index];
+ if ($label == Piwik_DataTable::LABEL_SUMMARY_ROW) {
+ return false; // so no metadata/column is added
+ }
+
+ $segments = explode($separator, $label);
+ return empty($segments[$index]) ? $emptyValue : $segments[$index];
}
/**
* Returns the region name using the label of a Visits by Region report.
- *
+ *
* @param string $label A label containing a region code followed by '|' and a country code, eg,
* 'P3|GB'.
* @return string|false The region name or false if $label == Piwik_DataTable::LABEL_SUMMARY_ROW.
*/
-function Piwik_UserCountry_getRegionName( $label )
+function Piwik_UserCountry_getRegionName($label)
{
- if ($label == Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- return false; // so no metadata/column is added
- }
-
- if ($label == '')
- {
- return Piwik_Translate('General_Unknown');
- }
-
- list($regionCode, $countryCode) = explode(Piwik_UserCountry::LOCATION_SEPARATOR, $label);
- return Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes($countryCode, $regionCode);
+ if ($label == Piwik_DataTable::LABEL_SUMMARY_ROW) {
+ return false; // so no metadata/column is added
+ }
+
+ if ($label == '') {
+ return Piwik_Translate('General_Unknown');
+ }
+
+ list($regionCode, $countryCode) = explode(Piwik_UserCountry::LOCATION_SEPARATOR, $label);
+ return Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes($countryCode, $regionCode);
}
/**
* Returns the name of a region + the name of the region's country using the label of
* a Visits by Region report.
- *
+ *
* @param string $label A label containing a region code followed by '|' and a country code, eg,
* 'P3|GB'.
* @return string|false eg. 'Ile de France, France' or false if $label == Piwik_DataTable::LABEL_SUMMARY_ROW.
*/
-function Piwik_UserCountry_getPrettyRegionName( $label )
+function Piwik_UserCountry_getPrettyRegionName($label)
{
- if ($label == Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- return $label;
- }
-
- if ($label == '')
- {
- return Piwik_Translate('General_Unknown');
- }
-
- list($regionCode, $countryCode) = explode(Piwik_UserCountry::LOCATION_SEPARATOR, $label);
-
- $result = Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes($countryCode, $regionCode);
- if ($countryCode != Piwik_Tracker_Visit::UNKNOWN_CODE && $countryCode != '')
- {
- $result .= ', '.Piwik_CountryTranslate($countryCode);
- }
- return $result;
+ if ($label == Piwik_DataTable::LABEL_SUMMARY_ROW) {
+ return $label;
+ }
+
+ if ($label == '') {
+ return Piwik_Translate('General_Unknown');
+ }
+
+ list($regionCode, $countryCode) = explode(Piwik_UserCountry::LOCATION_SEPARATOR, $label);
+
+ $result = Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes($countryCode, $regionCode);
+ if ($countryCode != Piwik_Tracker_Visit::UNKNOWN_CODE && $countryCode != '') {
+ $result .= ', ' . Piwik_CountryTranslate($countryCode);
+ }
+ return $result;
}
/**
* Returns the name of a city + the name of its region + the name of its country using
* the label of a Visits by City report.
- *
+ *
* @param string $label A label containing a city name, region code + country code,
* separated by two '|' chars: 'Paris|A8|FR'
* @return string|false eg. 'Paris, Ile de France, France' or false if $label ==
* Piwik_DataTable::LABEL_SUMMARY_ROW.
*/
-function Piwik_UserCountry_getPrettyCityName( $label )
+function Piwik_UserCountry_getPrettyCityName($label)
{
- if ($label == Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- return $label;
- }
-
- if ($label == '')
- {
- return Piwik_Translate('General_Unknown');
- }
-
- // get city name, region code & country code
- $parts = explode(Piwik_UserCountry::LOCATION_SEPARATOR, $label);
- $cityName = $parts[0];
- $regionCode = $parts[1];
- $countryCode = $parts[2];
-
- if ($cityName == Piwik_Tracker_Visit::UNKNOWN_CODE || $cityName == '')
- {
- $cityName = Piwik_Translate('General_Unknown');
- }
-
- $result = $cityName;
- if ($countryCode != Piwik_Tracker_Visit::UNKNOWN_CODE && $countryCode != '')
- {
- if ($regionCode != '' && $regionCode != Piwik_Tracker_Visit::UNKNOWN_CODE)
- {
- $regionName = Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes($countryCode, $regionCode);
- $result .= ', '.$regionName;
- }
- $result .= ', '.Piwik_CountryTranslate($countryCode);
- }
- return $result;
+ if ($label == Piwik_DataTable::LABEL_SUMMARY_ROW) {
+ return $label;
+ }
+
+ if ($label == '') {
+ return Piwik_Translate('General_Unknown');
+ }
+
+ // get city name, region code & country code
+ $parts = explode(Piwik_UserCountry::LOCATION_SEPARATOR, $label);
+ $cityName = $parts[0];
+ $regionCode = $parts[1];
+ $countryCode = $parts[2];
+
+ if ($cityName == Piwik_Tracker_Visit::UNKNOWN_CODE || $cityName == '') {
+ $cityName = Piwik_Translate('General_Unknown');
+ }
+
+ $result = $cityName;
+ if ($countryCode != Piwik_Tracker_Visit::UNKNOWN_CODE && $countryCode != '') {
+ if ($regionCode != '' && $regionCode != Piwik_Tracker_Visit::UNKNOWN_CODE) {
+ $regionName = Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes($countryCode, $regionCode);
+ $result .= ', ' . $regionName;
+ }
+ $result .= ', ' . Piwik_CountryTranslate($countryCode);
+ }
+ return $result;
}
diff --git a/plugins/UserCountry/templates/admin.js b/plugins/UserCountry/templates/admin.js
index 46e7931add..1bb3d22d76 100755
--- a/plugins/UserCountry/templates/admin.js
+++ b/plugins/UserCountry/templates/admin.js
@@ -5,52 +5,52 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-$(document).ready(function() {
- $('#geoip-download-progress,#geoip-updater-progressbar').progressbar({value: 1});
-
- // handle switch current location provider
- $('.location-provider').change(function() {
- if (!$(this).is(':checked')) return; // only handle radio buttons that get checked
-
- var parent = $(this).parent(),
- loading = $('.loadingPiwik', parent),
- ajaxSuccess = $('.ajaxSuccess', parent);
+$(document).ready(function () {
+ $('#geoip-download-progress,#geoip-updater-progressbar').progressbar({value: 1});
+
+ // handle switch current location provider
+ $('.location-provider').change(function () {
+ if (!$(this).is(':checked')) return; // only handle radio buttons that get checked
+
+ var parent = $(this).parent(),
+ loading = $('.loadingPiwik', parent),
+ ajaxSuccess = $('.ajaxSuccess', parent);
var ajaxRequest = new ajaxHelper();
ajaxRequest.setLoadingElement(loading);
ajaxRequest.addParams({
module: 'UserCountry',
action: 'setCurrentLocationProvider',
- id: $(this).val()
+ id: $(this).val()
}, 'get');
ajaxRequest.setCallback(
function () {
- ajaxSuccess.fadeIn(1000, function() {
- setTimeout(function() {
+ ajaxSuccess.fadeIn(1000, function () {
+ setTimeout(function () {
ajaxSuccess.fadeOut(1000);
}, 2000);
});
}
);
ajaxRequest.send(false);
- });
-
- // handle 'refresh location' link click
- $('.refresh-loc').click(function(e) {
- e.preventDefault();
-
- var cell = $(this).parent().parent(),
- loading = $('.loadingPiwik', cell),
- location = $('.location', cell);
-
- location.css('visibility', 'hidden');
+ });
+
+ // handle 'refresh location' link click
+ $('.refresh-loc').click(function (e) {
+ e.preventDefault();
+
+ var cell = $(this).parent().parent(),
+ loading = $('.loadingPiwik', cell),
+ location = $('.location', cell);
+
+ location.css('visibility', 'hidden');
var ajaxRequest = new ajaxHelper();
ajaxRequest.setLoadingElement(loading);
ajaxRequest.addParams({
module: 'UserCountry',
action: 'getLocationUsingProvider',
- id: $(this).attr('data-impl-id')
+ id: $(this).attr('data-impl-id')
}, 'get');
ajaxRequest.setCallback(
function (response) {
@@ -60,146 +60,134 @@ $(document).ready(function() {
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
- return false;
- });
-
- // geoip database wizard
- var downloadNextChunk = function(action, thisId, progressBarId, cont, extraData, callback)
- {
- var data = {
- module: 'UserCountry',
- action: action,
- token_auth: piwik.token_auth,
- 'continue': cont ? 1 : 0
- };
- for (var k in extraData)
- {
- data[k] = extraData[k];
- }
-
+ return false;
+ });
+
+ // geoip database wizard
+ var downloadNextChunk = function (action, thisId, progressBarId, cont, extraData, callback) {
+ var data = {
+ module: 'UserCountry',
+ action: action,
+ token_auth: piwik.token_auth,
+ 'continue': cont ? 1 : 0
+ };
+ for (var k in extraData) {
+ data[k] = extraData[k];
+ }
+
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(data, 'post');
- ajaxRequest.setCallback(function(response) {
- if (!response || response.error)
- {
- callback(response);
- }
- else
- {
- // update progress bar
- var newProgressVal = Math.ceil((response.current_size / response.expected_file_size) * 100);
- newProgressVal = Math.min(newProgressVal, 100);
- $('#'+progressBarId).progressbar('option', 'value', newProgressVal);
-
- // if incomplete, download next chunk, otherwise, show updater manager
- if (newProgressVal < 100)
- {
- downloadNextChunk(action, thisId, progressBarId, true, extraData, callback);
- }
- else
- {
- callback(response);
- }
- }
- });
- ajaxRequest.setErrorCallback(function() {
- callback({error: _pk_translate('UserCountry_FatalErrorDuringDownload_js')});
- });
+ ajaxRequest.setCallback(function (response) {
+ if (!response || response.error) {
+ callback(response);
+ }
+ else {
+ // update progress bar
+ var newProgressVal = Math.ceil((response.current_size / response.expected_file_size) * 100);
+ newProgressVal = Math.min(newProgressVal, 100);
+ $('#' + progressBarId).progressbar('option', 'value', newProgressVal);
+
+ // if incomplete, download next chunk, otherwise, show updater manager
+ if (newProgressVal < 100) {
+ downloadNextChunk(action, thisId, progressBarId, true, extraData, callback);
+ }
+ else {
+ callback(response);
+ }
+ }
+ });
+ ajaxRequest.setErrorCallback(function () {
+ callback({error: _pk_translate('UserCountry_FatalErrorDuringDownload_js')});
+ });
ajaxRequest.send(false);
- };
-
- $('#start-download-free-geoip').click(function() {
- $('#geoipdb-screen1').hide("slide", {direction: "left"}, 800, function() {
- $('#geoipdb-screen2-download').fadeIn(1000);
-
- // start download of free dbs
- downloadNextChunk(
- 'downloadFreeGeoIPDB',
- 'geoipdb-screen2-download',
- 'geoip-download-progress',
- false,
- {},
- function(response) {
- if (response.error)
- {
- // on error, show error & stop downloading
- $('#'+thisId).fadeOut(1000, function() {
- $('#manage-geoip-dbs').html(response.error);
- });
- }
- else
- {
- $('#geoipdb-screen2-download').fadeOut(1000, function() {
- $('#manage-geoip-dbs').html(response.next_screen);
- });
- }
- }
- );
- });
- });
-
- $('body').on('click', '#start-automatic-update-geoip', function() {
- $('#geoipdb-screen1').hide("slide", {direction: "left"}, 800, function () {
- $('#geoip-db-mangement').text(_pk_translate('UserCountry_SetupAutomaticUpdatesOfGeoIP_js'));
- $('#geoipdb-update-info').fadeIn(1000);
- });
- });
-
- $('body').on('click', '#update-geoip-links', function() {
- $('#geoipdb-update-info-error').hide();
-
- var currentDownloading = null,
- updateGeoIPSuccess = function(response)
- {
- if (response && response.error)
- {
- $('#geoip-progressbar-container').hide();
- $('#geoipdb-update-info-error').html(response.error).show();
- }
- else if (response && response.to_download)
- {
- var continuing = currentDownloading == response.to_download;
- currentDownloading = response.to_download;
-
- // show progress bar w/ message
- $('#geoip-updater-progressbar').progressbar('option', 'value', 1);
- $('#geoip-updater-progressbar-label').html(response.to_download_label);
- $('#geoip-progressbar-container').show();
-
- // start/continue download
- downloadNextChunk(
- 'downloadMissingGeoIpDb', 'geoipdb-update-info', 'geoip-updater-progressbar',
- continuing, {key: response.to_download}, updateGeoIPSuccess);
- }
- else
- {
- $('#geoipdb-update-info-error').hide();
- $('#geoip-updater-progressbar-label').html('');
- $('#geoip-progressbar-container').hide();
-
- // fade in/out Done message
- $('#done-updating-updater').fadeIn(1000, function() {
- setTimeout(function() {
- $('#done-updating-updater').fadeOut(1000);
- }, 3000);
- });
- }
- };
-
- // setup the auto-updater
+ };
+
+ $('#start-download-free-geoip').click(function () {
+ $('#geoipdb-screen1').hide("slide", {direction: "left"}, 800, function () {
+ $('#geoipdb-screen2-download').fadeIn(1000);
+
+ // start download of free dbs
+ downloadNextChunk(
+ 'downloadFreeGeoIPDB',
+ 'geoipdb-screen2-download',
+ 'geoip-download-progress',
+ false,
+ {},
+ function (response) {
+ if (response.error) {
+ // on error, show error & stop downloading
+ $('#' + thisId).fadeOut(1000, function () {
+ $('#manage-geoip-dbs').html(response.error);
+ });
+ }
+ else {
+ $('#geoipdb-screen2-download').fadeOut(1000, function () {
+ $('#manage-geoip-dbs').html(response.next_screen);
+ });
+ }
+ }
+ );
+ });
+ });
+
+ $('body').on('click', '#start-automatic-update-geoip', function () {
+ $('#geoipdb-screen1').hide("slide", {direction: "left"}, 800, function () {
+ $('#geoip-db-mangement').text(_pk_translate('UserCountry_SetupAutomaticUpdatesOfGeoIP_js'));
+ $('#geoipdb-update-info').fadeIn(1000);
+ });
+ });
+
+ $('body').on('click', '#update-geoip-links', function () {
+ $('#geoipdb-update-info-error').hide();
+
+ var currentDownloading = null,
+ updateGeoIPSuccess = function (response) {
+ if (response && response.error) {
+ $('#geoip-progressbar-container').hide();
+ $('#geoipdb-update-info-error').html(response.error).show();
+ }
+ else if (response && response.to_download) {
+ var continuing = currentDownloading == response.to_download;
+ currentDownloading = response.to_download;
+
+ // show progress bar w/ message
+ $('#geoip-updater-progressbar').progressbar('option', 'value', 1);
+ $('#geoip-updater-progressbar-label').html(response.to_download_label);
+ $('#geoip-progressbar-container').show();
+
+ // start/continue download
+ downloadNextChunk(
+ 'downloadMissingGeoIpDb', 'geoipdb-update-info', 'geoip-updater-progressbar',
+ continuing, {key: response.to_download}, updateGeoIPSuccess);
+ }
+ else {
+ $('#geoipdb-update-info-error').hide();
+ $('#geoip-updater-progressbar-label').html('');
+ $('#geoip-progressbar-container').hide();
+
+ // fade in/out Done message
+ $('#done-updating-updater').fadeIn(1000, function () {
+ setTimeout(function () {
+ $('#done-updating-updater').fadeOut(1000);
+ }, 3000);
+ });
+ }
+ };
+
+ // setup the auto-updater
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
- period: $('#geoip-update-period-cell>input:checked').val()
+ period: $('#geoip-update-period-cell>input:checked').val()
}, 'get');
ajaxRequest.addParams({
module: 'UserCountry',
action: 'updateGeoIPLinks',
- token_auth: piwik.token_auth,
- loc_db: $('#geoip-location-db').val(),
- isp_db: $('#geoip-isp-db').val(),
- org_db: $('#geoip-org-db').val()
+ token_auth: piwik.token_auth,
+ loc_db: $('#geoip-location-db').val(),
+ isp_db: $('#geoip-isp-db').val(),
+ org_db: $('#geoip-org-db').val()
}, 'post');
ajaxRequest.setCallback(updateGeoIPSuccess);
ajaxRequest.send(false);
- });
+ });
});
diff --git a/plugins/UserCountry/templates/adminIndex.tpl b/plugins/UserCountry/templates/adminIndex.tpl
index 51676f3fba..2a40f652c0 100755
--- a/plugins/UserCountry/templates/adminIndex.tpl
+++ b/plugins/UserCountry/templates/adminIndex.tpl
@@ -5,116 +5,119 @@
<div style="width:900px">
-<p>{'UserCountry_GeolocationPageDesc'|translate}</p>
+ <p>{'UserCountry_GeolocationPageDesc'|translate}</p>
-{if !$isThereWorkingProvider}
-<h3 style="margin-top:0">{'UserCountry_HowToSetupGeoIP'|translate}</h3>
-<p>{'UserCountry_HowToSetupGeoIPIntro'|translate}</p>
+ {if !$isThereWorkingProvider}
+ <h3 style="margin-top:0">{'UserCountry_HowToSetupGeoIP'|translate}</h3>
+ <p>{'UserCountry_HowToSetupGeoIPIntro'|translate}</p>
+ <ul style="list-style:disc;margin-left:2em">
+ <li>{'UserCountry_HowToSetupGeoIP_Step1'|translate:'<a href="http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz">':'</a>':'<a target="_blank" href="http://www.maxmind.com/?rId=piwik">':'</a>'}</li>
+ <li>{'UserCountry_HowToSetupGeoIP_Step2'|translate:"'GeoLiteCity.dat'":'<strong>':'</strong>'}</li>
+ <li>{'UserCountry_HowToSetupGeoIP_Step3'|translate:'<strong>':'</strong>':'<span style="color:green"><strong>':'</strong></span>'}</li>
+ <li>{'UserCountry_HowToSetupGeoIP_Step4'|translate}</li>
+ </ul>
+ <p>&nbsp;</p>
+ {/if}
-<ul style="list-style:disc;margin-left:2em">
- <li>{'UserCountry_HowToSetupGeoIP_Step1'|translate:'<a href="http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz">':'</a>':'<a target="_blank" href="http://www.maxmind.com/?rId=piwik">':'</a>'}</li>
- <li>{'UserCountry_HowToSetupGeoIP_Step2'|translate:"'GeoLiteCity.dat'":'<strong>':'</strong>'}</li>
- <li>{'UserCountry_HowToSetupGeoIP_Step3'|translate:'<strong>':'</strong>':'<span style="color:green"><strong>':'</strong></span>'}</li>
- <li>{'UserCountry_HowToSetupGeoIP_Step4'|translate}</li>
-</ul>
+ <table class="adminTable locationProviderTable">
+ <tr>
+ <th>{'UserCountry_LocationProvider'|translate}</th>
+ <th>{'General_Description'|translate}</th>
+ <th>{'General_InfoFor'|translate:$thisIP}</th>
+ </tr>
+ {foreach from=$locationProviders key=id item=provider}
+ <tr>
+ <td width="140">
+ <p>
+ <input class="location-provider" name="location-provider" value="{$id}" type="radio" {if $currentProviderId eq $id}checked="checked"{/if}
+ id="provider_input_{$id}" {if $provider.status neq 1}disabled="disabled"{/if}/>
+ <label for="provider_input_{$id}">{$provider.title|translate}</label><br/>
+ <span class='loadingPiwik' style='display:none'><img src='./themes/default/images/loading-blue.gif'/></span>
+ <span class="ajaxSuccess" style='display:none'>{'General_Done'|translate}</span>
+ </p>
-<p>&nbsp;</p>
-{/if}
-
-<table class="adminTable locationProviderTable">
- <tr>
- <th>{'UserCountry_LocationProvider'|translate}</th>
- <th>{'General_Description'|translate}</th>
- <th>{'General_InfoFor'|translate:$thisIP}</th>
- </tr>
- {foreach from=$locationProviders key=id item=provider}
- <tr>
- <td width="140">
- <p>
- <input class="location-provider" name="location-provider" value="{$id}" type="radio" {if $currentProviderId eq $id}checked="checked"{/if} id="provider_input_{$id}" {if $provider.status neq 1}disabled="disabled"{/if}/>
- <label for="provider_input_{$id}">{$provider.title|translate}</label><br/>
- <span class='loadingPiwik' style='display:none'><img src='./themes/default/images/loading-blue.gif' /></span>
- <span class="ajaxSuccess" style='display:none'>{'General_Done'|translate}</span>
- </p>
- <p class="loc-provider-status">
- <strong><em>
- {if $provider.status eq 0}
- <span class="is-not-installed">{'General_NotInstalled'|translate}</span>
- {elseif $provider.status eq 1}
- <span class="is-installed">{'General_Installed'|translate}</span>
- {elseif $provider.status eq 2}
- <span class="is-broken">{'General_Broken'|translate}</span>
- {/if}
- </em></strong>
- </p>
- </td>
- <td>
- <p>{$provider.description|translate}</p>
- {if $provider.status neq 1 && isset($provider.install_docs)}
- <p>{$provider.install_docs}</p>
- {/if}
- </td>
- <td width="164">
- {if $provider.status eq 1}
- {capture assign=currentLocation}
- {if $thisIP neq '127.0.0.1'}
- {'UserCountry_CurrentLocationIntro'|translate}:
- <div style="text-align:left;">
- <br/>
- <span class='loadingPiwik' style='display:none;position:absolute'><img src='./themes/default/images/loading-blue.gif' /> {'General_Loading_js'|translate}</span>
- <span class='location'><strong><em>{$provider.location}</em></strong></span>
- </div>
- <div style="text-align:right;">
- <a href="#" class="refresh-loc" data-impl-id="{$id}"><em>{'Dashboard_Refresh_js'|translate}</em></a>
- </div>
- {else}
- {'UserCountry_CannotLocalizeLocalIP'|translate:$thisIP}
- {/if}
- {/capture}
- {$currentLocation|inlineHelp}
- {/if}
- {if isset($provider.statusMessage) && $provider.statusMessage}
- {capture assign=brokenReason}
- {if $provider.status eq 2}<strong><em>{'General_Error'|translate}:</strong></em> {/if}{$provider.statusMessage}
- {/capture}
- {$brokenReason|inlineHelp}
- {/if}
- {if isset($provider.extra_message) && $provider.extra_message}
- {capture assign=extraMessage}
- {$provider.extra_message}
- {/capture}
- <br/>
- {$extraMessage|inlineHelp}
- {/if}
- </td>
- {/foreach}
-</table>
+ <p class="loc-provider-status">
+ <strong><em>
+ {if $provider.status eq 0}
+ <span class="is-not-installed">{'General_NotInstalled'|translate}</span>
+ {elseif $provider.status eq 1}
+ <span class="is-installed">{'General_Installed'|translate}</span>
+ {elseif $provider.status eq 2}
+ <span class="is-broken">{'General_Broken'|translate}</span>
+ {/if}
+ </em></strong>
+ </p>
+ </td>
+ <td>
+ <p>{$provider.description|translate}</p>
+ {if $provider.status neq 1 && isset($provider.install_docs)}
+ <p>{$provider.install_docs}</p>
+ {/if}
+ </td>
+ <td width="164">
+ {if $provider.status eq 1}
+ {capture assign=currentLocation}
+ {if $thisIP neq '127.0.0.1'}
+ {'UserCountry_CurrentLocationIntro'|translate}:
+ <div style="text-align:left;">
+ <br/>
+ <span class='loadingPiwik' style='display:none;position:absolute'><img
+ src='./themes/default/images/loading-blue.gif'/> {'General_Loading_js'|translate}</span>
+ <span class='location'><strong><em>{$provider.location}</em></strong></span>
+ </div>
+ <div style="text-align:right;">
+ <a href="#" class="refresh-loc" data-impl-id="{$id}"><em>{'Dashboard_Refresh_js'|translate}</em></a>
+ </div>
+ {else}
+ {'UserCountry_CannotLocalizeLocalIP'|translate:$thisIP}
+ {/if}
+ {/capture}
+ {$currentLocation|inlineHelp}
+ {/if}
+ {if isset($provider.statusMessage) && $provider.statusMessage}
+ {capture assign=brokenReason}
+ {if $provider.status eq 2}<strong><em>{'General_Error'|translate}:</strong></em> {/if}{$provider.statusMessage}
+ {/capture}
+ {$brokenReason|inlineHelp}
+ {/if}
+ {if isset($provider.extra_message) && $provider.extra_message}
+ {capture assign=extraMessage}
+ {$provider.extra_message}
+ {/capture}
+ <br/>
+ {$extraMessage|inlineHelp}
+ {/if}
+ </td>
+ {/foreach}
+ </table>
</div>
{if !$geoIPDatabasesInstalled}
-<h2 id="geoip-db-mangement">{'UserCountry_GeoIPDatabases'|translate}</h2>
+ <h2 id="geoip-db-mangement">{'UserCountry_GeoIPDatabases'|translate}</h2>
{else}
-<h2 id="geoip-db-mangement">{'UserCountry_SetupAutomaticUpdatesOfGeoIP_js'|translate}</h2>
+ <h2 id="geoip-db-mangement">{'UserCountry_SetupAutomaticUpdatesOfGeoIP_js'|translate}</h2>
{/if}
{if $showGeoIPUpdateSection}
-<div id="manage-geoip-dbs" style="width:900px" class="adminTable">
+ <div id="manage-geoip-dbs" style="width:900px" class="adminTable">
-{if !$geoIPDatabasesInstalled}
-<div id="geoipdb-screen1">
- <p>{'UserCountry_PiwikNotManagingGeoIPDBs'|translate}</p>
- <div class="geoipdb-column-1">
- <p>{'UserCountry_IWantToDownloadFreeGeoIP'|translate}</p>
- <input type="button" class="submit" value="{'General_GetStarted'|translate}..." id="start-download-free-geoip"/>
- </div>
- <div class="geoipdb-column-2">
- <p>{'UserCountry_IPurchasedGeoIPDBs'|translate:'<a href="http://www.maxmind.com/en/geolocation_landing?rId=piwik">':'</a>'}</p>
- <input type="button" class="submit" value="{'General_GetStarted'|translate}..." id="start-automatic-update-geoip"/>
- </div>
-</div>
-<div id="geoipdb-screen2-download" style="display:none">
- <p class='loadingPiwik'><img src='./themes/default/images/loading-blue.gif' />{'UserCountry_DownloadingDb'|translate:"<a href=\"$geoLiteUrl\">GeoLiteCity.dat</a>"}...</p>
+ {if !$geoIPDatabasesInstalled}
+ <div id="geoipdb-screen1">
+ <p>{'UserCountry_PiwikNotManagingGeoIPDBs'|translate}</p>
+
+ <div class="geoipdb-column-1">
+ <p>{'UserCountry_IWantToDownloadFreeGeoIP'|translate}</p>
+ <input type="button" class="submit" value="{'General_GetStarted'|translate}..." id="start-download-free-geoip"/>
+ </div>
+ <div class="geoipdb-column-2">
+ <p>{'UserCountry_IPurchasedGeoIPDBs'|translate:'<a href="http://www.maxmind.com/en/geolocation_landing?rId=piwik">':'</a>'}</p>
+ <input type="button" class="submit" value="{'General_GetStarted'|translate}..." id="start-automatic-update-geoip"/>
+ </div>
+ </div>
+ <div id="geoipdb-screen2-download" style="display:none">
+ <p class='loadingPiwik'><img src='./themes/default/images/loading-blue.gif'/>
+ {'UserCountry_DownloadingDb'|translate:"<a href=\"$geoLiteUrl\">GeoLiteCity.dat</a>"}...</p>
<div id="geoip-download-progress"></div>
</div>
{/if}
diff --git a/plugins/UserCountry/templates/index.tpl b/plugins/UserCountry/templates/index.tpl
index ab29fa57ef..5de5bdaf5e 100644
--- a/plugins/UserCountry/templates/index.tpl
+++ b/plugins/UserCountry/templates/index.tpl
@@ -1,29 +1,28 @@
-
<div id="leftcolumn">
-{postEvent name="template_leftColumnUserCountry"}
+ {postEvent name="template_leftColumnUserCountry"}
- <h2>{'UserCountry_Continent'|translate}</h2>
- {$dataTableContinent}
+ <h2>{'UserCountry_Continent'|translate}</h2>
+ {$dataTableContinent}
- <div class="sparkline">
- {sparkline src=$urlSparklineCountries}
- {'UserCountry_DistinctCountries'|translate:"<strong>$numberDistinctCountries</strong>"}
- </div>
+ <div class="sparkline">
+ {sparkline src=$urlSparklineCountries}
+ {'UserCountry_DistinctCountries'|translate:"<strong>$numberDistinctCountries</strong>"}
+ </div>
-{postEvent name="template_footerUserCountry"}
+ {postEvent name="template_footerUserCountry"}
</div>
<div id="rightcolumn">
- <h2>{'UserCountry_Country'|translate}</h2>
- {$dataTableCountry}
+ <h2>{'UserCountry_Country'|translate}</h2>
+ {$dataTableCountry}
- <h2>{'UserCountry_Region'|translate}</h2>
- {$dataTableRegion}
+ <h2>{'UserCountry_Region'|translate}</h2>
+ {$dataTableRegion}
- <h2>{'UserCountry_City'|translate}</h2>
- {$dataTableCity}
+ <h2>{'UserCountry_City'|translate}</h2>
+ {$dataTableCity}
</div>
diff --git a/plugins/UserCountry/templates/styles.css b/plugins/UserCountry/templates/styles.css
index 57cf159777..50c26af3a3 100755
--- a/plugins/UserCountry/templates/styles.css
+++ b/plugins/UserCountry/templates/styles.css
@@ -1,72 +1,75 @@
.locationProviderTable label {
- font-size: 1.2em;
+ font-size: 1.2em;
}
input.location-provider {
- cursor:pointer;
+ cursor: pointer;
}
span.is-installed {
- color:green;
+ color: green;
}
span.is-broken {
- color:red;
+ color: red;
}
.loc-provider-status {
- margin-left:.5em;
+ margin-left: .5em;
}
#manage-geoip-dbs {
- height: 20em;
+ height: 20em;
}
-#geoipdb-screen1, #geoipdb-screen2-download,#geoipdb-update-info {
- width:900px;
+#geoipdb-screen1, #geoipdb-screen2-download, #geoipdb-update-info {
+ width: 900px;
}
-#geoipdb-update-info tr input[type="text"],#geoipdb-screen2-update tr input[type="text"] {
- width: 90%;
+#geoipdb-update-info tr input[type="text"], #geoipdb-screen2-update tr input[type="text"] {
+ width: 90%;
}
#geoipdb-screen1>div {
- display: inline-block;
- vertical-align:top;
+ display: inline-block;
+ vertical-align: top;
}
#geoipdb-screen1>div>p {
- font-size: 2em;
- height: 4em;
+ font-size: 2em;
+ height: 4em;
}
-.geoipdb-column-1,.geoipdb-column-2 {
- width:396px;
+.geoipdb-column-1, .geoipdb-column-2 {
+ width: 396px;
}
+
.geoipdb-column-1 {
- margin-right: 50px;
+ margin-right: 50px;
}
+
.geoipdb-column-2 {
- border-left: solid #999 1px;
- padding-left: 50px;
+ border-left: solid #999 1px;
+ padding-left: 50px;
}
+
.geoipdb-column-1>p {
- padding-left: 20px;
+ padding-left: 20px;
}
.error {
- font-weight:bold;
- color:red;
- padding:4px 8px 4px 8px;
+ font-weight: bold;
+ color: red;
+ padding: 4px 8px 4px 8px;
}
#geoip-updater-progressbar-label {
- float: left;
- margin: -24px 24px;
+ float: left;
+ margin: -24px 24px;
}
-#geoip-progressbar-container,#geoipdb-update-info-error {
- margin: 22px 24px;
- display:inline-block;
+#geoip-progressbar-container, #geoipdb-update-info-error {
+ margin: 22px 24px;
+ display: inline-block;
}
diff --git a/plugins/UserCountry/templates/updaterSetup.tpl b/plugins/UserCountry/templates/updaterSetup.tpl
index 7960526b2e..7f29616de5 100755
--- a/plugins/UserCountry/templates/updaterSetup.tpl
+++ b/plugins/UserCountry/templates/updaterSetup.tpl
@@ -1,6 +1,7 @@
<div id="geoipdb-update-info" {if !$geoIPDatabasesInstalled}style="display:none"{/if}>
- <p>{'UserCountry_GeoIPUpdaterInstructions'|translate:'<a href="http://www.maxmind.com/en/download_files?rId=piwik" _target="blank">':'</a>':'<a href="http://www.maxmind.com/?rId=piwik">':'</a>'}<br/><br/>
- {'UserCountry_GeoLiteCityLink'|translate:"<a href=\"$geoLiteUrl\">":$geoLiteUrl:'</a>'}
+ <p>{'UserCountry_GeoIPUpdaterInstructions'|translate:'<a href="http://www.maxmind.com/en/download_files?rId=piwik" _target="blank">':'</a>':'<a href="http://www.maxmind.com/?rId=piwik">':'</a>'}
+ <br/><br/>
+{'UserCountry_GeoLiteCityLink'|translate:"<a href=\"$geoLiteUrl\">":$geoLiteUrl:'</a>'}
{if $geoIPDatabasesInstalled}
<br/><br/>{'UserCountry_GeoIPUpdaterIntro'|translate}:
{/if}
diff --git a/plugins/UserCountryMap/Controller.php b/plugins/UserCountryMap/Controller.php
index 0a18dc2027..b0ca11de21 100644
--- a/plugins/UserCountryMap/Controller.php
+++ b/plugins/UserCountryMap/Controller.php
@@ -16,202 +16,200 @@
class Piwik_UserCountryMap_Controller extends Piwik_Controller
{
- // By default plot up to the last 30 days of visitors on the map, for low traffic sites
- const REAL_TIME_WINDOW = 'last30';
-
- public function visitorMap()
- {
- $this->checkUserCountryPluginEnabled();
-
- $idSite = Piwik_Common::getRequestVar('idSite', 1, 'int');
- Piwik::checkUserHasViewAccess($idSite);
-
- $period = Piwik_Common::getRequestVar('period');
- $date = Piwik_Common::getRequestVar('date');
- $token_auth = Piwik::getCurrentUserTokenAuth();
-
- $view = Piwik_View::factory('visitor-map');
-
- // request visits summary
- $request = new Piwik_API_Request(
- 'method=VisitsSummary.get&format=PHP'
- . '&idSite=' . $idSite
- . '&period=' . $period
- . '&date=' . $date
- . '&token_auth=' . $token_auth
- . '&filter_limit=-1'
- );
- $config = array();
- $config['visitsSummary'] = unserialize($request->process());
- $config['countryDataUrl'] = $this->_report('UserCountry', 'getCountry',
- $idSite, $period, $date, $token_auth);
- $config['regionDataUrl'] = $this->_report('UserCountry', 'getRegion',
- $idSite, $period, $date, $token_auth, true);
- $config['cityDataUrl'] = $this->_report('UserCountry', 'getCity',
- $idSite, $period, $date, $token_auth, true);
- $config['countrySummaryUrl'] = $this->getApiRequestUrl('VisitsSummary', 'get',
- $idSite, $period, $date, $token_auth, true);
- $view->defaultMetric = 'nb_visits';
-
- // some translations
- $view->localeJSON = Piwik_Common::json_encode(array(
- 'nb_visits' => Piwik_Translate('VisitsSummary_NbVisits'),
- 'one_visit' => Piwik_Translate('General_OneVisit'),
- 'no_visit' => Piwik_Translate('UserCountryMap_NoVisit'),
- 'nb_actions' => Piwik_Translate('VisitsSummary_NbActionsDescription'),
- 'nb_actions_per_visit' => Piwik_Translate('VisitsSummary_NbActionsPerVisit'),
- 'bounce_rate' => Piwik_Translate('VisitsSummary_NbVisitsBounced'),
- 'avg_time_on_site' => Piwik_Translate('VisitsSummary_AverageVisitDuration'),
- 'and_n_others' => Piwik_Translate('UserCountryMap_AndNOthers'),
- 'no_data' => Piwik_Translate('CoreHome_ThereIsNoDataForThisReport')
- ));
-
- // template for ajax requests
- $view->reqParamsJSON = Piwik_Common::json_encode(array(
- 'period' => $period,
- 'idSite' => $idSite,
- 'date' => $date,
- 'token_auth' => $token_auth,
- 'format' => 'json',
- 'segment' => Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', '')),
- 'showRawMetrics' => 1,
- 'enable_filter_excludelowpop' => 1,
- 'filter_excludelowpop_value' => -1
- ));
- $view->metrics = $config['metrics'] = $this->getMetrics($idSite, $period, $date, $token_auth);
- $config['svgBasePath'] = 'plugins/UserCountryMap/svg/';
- $config['mapCssPath'] = 'plugins/UserCountryMap/css/map.css';
- $view->config = Piwik_Common::json_encode($config);
- $view->noData = empty($config['visitsSummary']['nb_visits']);
-
- echo $view->render();
- }
-
- /**
- * Used to build the report Visitor > Real time map
- */
- public function realtimeWorldMap()
- {
- return $this->realtimeMap($standalone = true);
- }
-
- /**
- * @param bool $standalone When set to true, the Top controls will be hidden to provide better full screen view
- */
- public function realtimeMap($standalone = false)
- {
- $this->checkUserCountryPluginEnabled();
-
- $idSite = Piwik_Common::getRequestVar('idSite', 1, 'int');
- Piwik::checkUserHasViewAccess($idSite);
-
- $token_auth = Piwik::getCurrentUserTokenAuth();
- $view = Piwik_View::factory('realtime-map');
-
- $view->mapIsStandaloneNotWidget = $standalone;
-
- $view->metrics = $this->getMetrics($idSite, 'range', self::REAL_TIME_WINDOW, $token_auth);
- $view->defaultMetric = 'nb_visits';
- $view->liveRefreshAfterMs = (int)Piwik_Config::getInstance()->General['live_widget_refresh_after_seconds'] * 1000;
-
- $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
- $site = new Piwik_Site($idSite);
- $view->hasGoals = !empty($goals) || $site->isEcommerceEnabled() ? 'true' : 'false';
-
- // maximum number of visits to be displayed in the map
- $view->maxVisits = Piwik_Common::getRequestVar('format_limit', 100, 'int');
-
- // some translations
- $view->localeJSON = json_encode(array(
- 'nb_actions' => Piwik_Translate('VisitsSummary_NbActionsDescription'),
- 'local_time' => Piwik_Translate('VisitTime_ColumnLocalTime'),
- 'from' => Piwik_Translate('General_FromReferrer'),
- 'seconds' => Piwik_Translate('UserCountryMap_Seconds'),
- 'seconds_ago' => Piwik_Translate('UserCountryMap_SecondsAgo'),
- 'minutes' => Piwik_Translate('UserCountryMap_Minutes'),
- 'minutes_ago' => Piwik_Translate('UserCountryMap_MinutesAgo'),
- 'hours' => Piwik_Translate('UserCountryMap_Hours'),
- 'hours_ago' => Piwik_Translate('UserCountryMap_HoursAgo'),
- 'days_ago' => Piwik_Translate('UserCountryMap_DaysAgo'),
- 'actions' => Piwik_Translate('VisitsSummary_NbPageviewsDescription'),
- 'searches' => Piwik_Translate('UserCountryMap_Searches'),
- 'goal_conversions' => Piwik_Translate('UserCountryMap_GoalConversions'),
- ));
-
- $view->reqParamsJSON = json_encode(array(
- 'period' => 'range',
- 'idSite' => $idSite,
- 'date' => self::REAL_TIME_WINDOW,
- 'token_auth' => $token_auth,
- 'format' => 'json',
- 'segment' => Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', '')),
- 'showRawMetrics' => 1
- ));
-
- echo $view->render();
- }
-
- private function checkUserCountryPluginEnabled()
- {
- if (!Piwik_PluginsManager::getInstance()->isPluginActivated('UserCountry')) {
- throw new Exception(Piwik_Translate('General_Required', 'Plugin UserCountry'));
- }
- }
-
- private function getMetrics($idSite, $period, $date, $token_auth)
- {
- $request = new Piwik_API_Request(
- 'method=API.getMetadata&format=PHP'
- . '&apiModule=UserCountry&apiAction=getCountry'
- . '&idSite=' . $idSite
- . '&period=' . $period
- . '&date=' . $date
- . '&token_auth=' . $token_auth
- . '&filter_limit=-1'
- );
- $metaData = $request->process();
-
- $metrics = array();
- foreach ($metaData[0]['metrics'] as $id => $val)
- {
- if (Piwik_Common::getRequestVar('period') == 'day' || $id != 'nb_uniq_visitors') {
- $metrics[] = array($id, $val);
- }
- }
- foreach ($metaData[0]['processedMetrics'] as $id => $val)
- {
- $metrics[] = array($id, $val);
- }
- return $metrics;
- }
-
- private function getApiRequestUrl($module, $action, $idSite, $period, $date, $token_auth, $filter_by_country = false)
- {
- // use processed reports
- $url = "?module=" . $module
- . "&method=".$module.".".$action."&format=JSON"
- . "&idSite=" . $idSite
- . "&period=" . $period
- . "&date=" . $date
- . "&token_auth=" . $token_auth
- . "&segment=" . Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', ''))
- . "&enable_filter_excludelowpop=1"
- . "&showRawMetrics=1";
-
- if ($filter_by_country) {
- $url .= "&filter_column=country"
- . "&filter_sort_column=nb_visits"
- . "&filter_limit=-1"
- . "&filter_pattern=";
- } else {
- $url .= "&filter_limit=-1";
- }
- return $url;
- }
-
- private function _report($module, $action, $idSite, $period, $date, $token_auth, $filter_by_country = false)
- {
- return $this->getApiRequestUrl('API', 'getProcessedReport&apiModule='.$module.'&apiAction='.$action, $idSite, $period, $date, $token_auth, $filter_by_country);
- }
+ // By default plot up to the last 30 days of visitors on the map, for low traffic sites
+ const REAL_TIME_WINDOW = 'last30';
+
+ public function visitorMap()
+ {
+ $this->checkUserCountryPluginEnabled();
+
+ $idSite = Piwik_Common::getRequestVar('idSite', 1, 'int');
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $period = Piwik_Common::getRequestVar('period');
+ $date = Piwik_Common::getRequestVar('date');
+ $token_auth = Piwik::getCurrentUserTokenAuth();
+
+ $view = Piwik_View::factory('visitor-map');
+
+ // request visits summary
+ $request = new Piwik_API_Request(
+ 'method=VisitsSummary.get&format=PHP'
+ . '&idSite=' . $idSite
+ . '&period=' . $period
+ . '&date=' . $date
+ . '&token_auth=' . $token_auth
+ . '&filter_limit=-1'
+ );
+ $config = array();
+ $config['visitsSummary'] = unserialize($request->process());
+ $config['countryDataUrl'] = $this->_report('UserCountry', 'getCountry',
+ $idSite, $period, $date, $token_auth);
+ $config['regionDataUrl'] = $this->_report('UserCountry', 'getRegion',
+ $idSite, $period, $date, $token_auth, true);
+ $config['cityDataUrl'] = $this->_report('UserCountry', 'getCity',
+ $idSite, $period, $date, $token_auth, true);
+ $config['countrySummaryUrl'] = $this->getApiRequestUrl('VisitsSummary', 'get',
+ $idSite, $period, $date, $token_auth, true);
+ $view->defaultMetric = 'nb_visits';
+
+ // some translations
+ $view->localeJSON = Piwik_Common::json_encode(array(
+ 'nb_visits' => Piwik_Translate('VisitsSummary_NbVisits'),
+ 'one_visit' => Piwik_Translate('General_OneVisit'),
+ 'no_visit' => Piwik_Translate('UserCountryMap_NoVisit'),
+ 'nb_actions' => Piwik_Translate('VisitsSummary_NbActionsDescription'),
+ 'nb_actions_per_visit' => Piwik_Translate('VisitsSummary_NbActionsPerVisit'),
+ 'bounce_rate' => Piwik_Translate('VisitsSummary_NbVisitsBounced'),
+ 'avg_time_on_site' => Piwik_Translate('VisitsSummary_AverageVisitDuration'),
+ 'and_n_others' => Piwik_Translate('UserCountryMap_AndNOthers'),
+ 'no_data' => Piwik_Translate('CoreHome_ThereIsNoDataForThisReport')
+ ));
+
+ // template for ajax requests
+ $view->reqParamsJSON = Piwik_Common::json_encode(array(
+ 'period' => $period,
+ 'idSite' => $idSite,
+ 'date' => $date,
+ 'token_auth' => $token_auth,
+ 'format' => 'json',
+ 'segment' => Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', '')),
+ 'showRawMetrics' => 1,
+ 'enable_filter_excludelowpop' => 1,
+ 'filter_excludelowpop_value' => -1
+ ));
+ $view->metrics = $config['metrics'] = $this->getMetrics($idSite, $period, $date, $token_auth);
+ $config['svgBasePath'] = 'plugins/UserCountryMap/svg/';
+ $config['mapCssPath'] = 'plugins/UserCountryMap/css/map.css';
+ $view->config = Piwik_Common::json_encode($config);
+ $view->noData = empty($config['visitsSummary']['nb_visits']);
+
+ echo $view->render();
+ }
+
+ /**
+ * Used to build the report Visitor > Real time map
+ */
+ public function realtimeWorldMap()
+ {
+ return $this->realtimeMap($standalone = true);
+ }
+
+ /**
+ * @param bool $standalone When set to true, the Top controls will be hidden to provide better full screen view
+ */
+ public function realtimeMap($standalone = false)
+ {
+ $this->checkUserCountryPluginEnabled();
+
+ $idSite = Piwik_Common::getRequestVar('idSite', 1, 'int');
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $token_auth = Piwik::getCurrentUserTokenAuth();
+ $view = Piwik_View::factory('realtime-map');
+
+ $view->mapIsStandaloneNotWidget = $standalone;
+
+ $view->metrics = $this->getMetrics($idSite, 'range', self::REAL_TIME_WINDOW, $token_auth);
+ $view->defaultMetric = 'nb_visits';
+ $view->liveRefreshAfterMs = (int)Piwik_Config::getInstance()->General['live_widget_refresh_after_seconds'] * 1000;
+
+ $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
+ $site = new Piwik_Site($idSite);
+ $view->hasGoals = !empty($goals) || $site->isEcommerceEnabled() ? 'true' : 'false';
+
+ // maximum number of visits to be displayed in the map
+ $view->maxVisits = Piwik_Common::getRequestVar('format_limit', 100, 'int');
+
+ // some translations
+ $view->localeJSON = json_encode(array(
+ 'nb_actions' => Piwik_Translate('VisitsSummary_NbActionsDescription'),
+ 'local_time' => Piwik_Translate('VisitTime_ColumnLocalTime'),
+ 'from' => Piwik_Translate('General_FromReferrer'),
+ 'seconds' => Piwik_Translate('UserCountryMap_Seconds'),
+ 'seconds_ago' => Piwik_Translate('UserCountryMap_SecondsAgo'),
+ 'minutes' => Piwik_Translate('UserCountryMap_Minutes'),
+ 'minutes_ago' => Piwik_Translate('UserCountryMap_MinutesAgo'),
+ 'hours' => Piwik_Translate('UserCountryMap_Hours'),
+ 'hours_ago' => Piwik_Translate('UserCountryMap_HoursAgo'),
+ 'days_ago' => Piwik_Translate('UserCountryMap_DaysAgo'),
+ 'actions' => Piwik_Translate('VisitsSummary_NbPageviewsDescription'),
+ 'searches' => Piwik_Translate('UserCountryMap_Searches'),
+ 'goal_conversions' => Piwik_Translate('UserCountryMap_GoalConversions'),
+ ));
+
+ $view->reqParamsJSON = json_encode(array(
+ 'period' => 'range',
+ 'idSite' => $idSite,
+ 'date' => self::REAL_TIME_WINDOW,
+ 'token_auth' => $token_auth,
+ 'format' => 'json',
+ 'segment' => Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', '')),
+ 'showRawMetrics' => 1
+ ));
+
+ echo $view->render();
+ }
+
+ private function checkUserCountryPluginEnabled()
+ {
+ if (!Piwik_PluginsManager::getInstance()->isPluginActivated('UserCountry')) {
+ throw new Exception(Piwik_Translate('General_Required', 'Plugin UserCountry'));
+ }
+ }
+
+ private function getMetrics($idSite, $period, $date, $token_auth)
+ {
+ $request = new Piwik_API_Request(
+ 'method=API.getMetadata&format=PHP'
+ . '&apiModule=UserCountry&apiAction=getCountry'
+ . '&idSite=' . $idSite
+ . '&period=' . $period
+ . '&date=' . $date
+ . '&token_auth=' . $token_auth
+ . '&filter_limit=-1'
+ );
+ $metaData = $request->process();
+
+ $metrics = array();
+ foreach ($metaData[0]['metrics'] as $id => $val) {
+ if (Piwik_Common::getRequestVar('period') == 'day' || $id != 'nb_uniq_visitors') {
+ $metrics[] = array($id, $val);
+ }
+ }
+ foreach ($metaData[0]['processedMetrics'] as $id => $val) {
+ $metrics[] = array($id, $val);
+ }
+ return $metrics;
+ }
+
+ private function getApiRequestUrl($module, $action, $idSite, $period, $date, $token_auth, $filter_by_country = false)
+ {
+ // use processed reports
+ $url = "?module=" . $module
+ . "&method=" . $module . "." . $action . "&format=JSON"
+ . "&idSite=" . $idSite
+ . "&period=" . $period
+ . "&date=" . $date
+ . "&token_auth=" . $token_auth
+ . "&segment=" . Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', ''))
+ . "&enable_filter_excludelowpop=1"
+ . "&showRawMetrics=1";
+
+ if ($filter_by_country) {
+ $url .= "&filter_column=country"
+ . "&filter_sort_column=nb_visits"
+ . "&filter_limit=-1"
+ . "&filter_pattern=";
+ } else {
+ $url .= "&filter_limit=-1";
+ }
+ return $url;
+ }
+
+ private function _report($module, $action, $idSite, $period, $date, $token_auth, $filter_by_country = false)
+ {
+ return $this->getApiRequestUrl('API', 'getProcessedReport&apiModule=' . $module . '&apiAction=' . $action, $idSite, $period, $date, $token_auth, $filter_by_country);
+ }
}
diff --git a/plugins/UserCountryMap/UserCountryMap.php b/plugins/UserCountryMap/UserCountryMap.php
index c50d520e2c..9b5fb337ee 100644
--- a/plugins/UserCountryMap/UserCountryMap.php
+++ b/plugins/UserCountryMap/UserCountryMap.php
@@ -18,11 +18,11 @@ class Piwik_UserCountryMap extends Piwik_Plugin
public function getInformation()
{
return array(
- 'name' => 'User Country Map',
- 'description' => 'This plugin provides the widgets Visitor Map and Real-time Map. Note: Requires the UserCountry plugin enabled.',
- 'author' => 'Piwik',
+ 'name' => 'User Country Map',
+ 'description' => 'This plugin provides the widgets Visitor Map and Real-time Map. Note: Requires the UserCountry plugin enabled.',
+ 'author' => 'Piwik',
'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION
+ 'version' => Piwik_Version::VERSION
);
}
@@ -31,37 +31,37 @@ class Piwik_UserCountryMap extends Piwik_Plugin
Piwik_AddWidget('General_Visitors', Piwik_Translate('UserCountryMap_VisitorMap'), 'UserCountryMap', 'visitorMap');
Piwik_AddWidget('Live!', Piwik_Translate('UserCountryMap_RealTimeMap'), 'UserCountryMap', 'realtimeMap');
- Piwik_AddAction('template_leftColumnUserCountry', array('Piwik_UserCountryMap', 'insertMapInLocationReport'));
+ Piwik_AddAction('template_leftColumnUserCountry', array('Piwik_UserCountryMap', 'insertMapInLocationReport'));
}
- static public function insertMapInLocationReport($notification)
- {
- $out =& $notification->getNotificationObject();
- $out = '<h2>'.Piwik_Translate('UserCountryMap_VisitorMap').'</h2>';
- $out .= Piwik_FrontController::getInstance()->fetchDispatch('UserCountryMap','visitorMap');
- }
+ static public function insertMapInLocationReport($notification)
+ {
+ $out =& $notification->getNotificationObject();
+ $out = '<h2>' . Piwik_Translate('UserCountryMap_VisitorMap') . '</h2>';
+ $out .= Piwik_FrontController::getInstance()->fetchDispatch('UserCountryMap', 'visitorMap');
+ }
- public function getListHooksRegistered()
+ public function getListHooksRegistered()
{
$hooks = array(
- 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
'AssetManager.getCssFiles' => 'getCssFiles',
- 'Menu.add' => 'addMenu',
+ 'Menu.add' => 'addMenu',
);
return $hooks;
}
- function addMenu()
- {
- Piwik_AddMenu('General_Visitors', 'UserCountryMap_RealTimeMap', array('module' => 'UserCountryMap', 'action' => 'realtimeWorldMap'), true, $order = 70);
- }
+ function addMenu()
+ {
+ Piwik_AddMenu('General_Visitors', 'UserCountryMap_RealTimeMap', array('module' => 'UserCountryMap', 'action' => 'realtimeWorldMap'), true, $order = 70);
+ }
- /**
+ /**
* @param Piwik_Event_Notification $notification notification object
*/
public function getJsFiles($notification)
{
- $jsFiles = &$notification->getNotificationObject();
+ $jsFiles = & $notification->getNotificationObject();
$jsFiles[] = "plugins/UserCountryMap/js/vendor/raphael-min.js";
$jsFiles[] = "plugins/UserCountryMap/js/vendor/jquery.qtip.min.js";
$jsFiles[] = "plugins/UserCountryMap/js/vendor/kartograph.min.js";
@@ -72,7 +72,7 @@ class Piwik_UserCountryMap extends Piwik_Plugin
public function getCssFiles($notification)
{
- $cssFiles = &$notification->getNotificationObject();
+ $cssFiles = & $notification->getNotificationObject();
$cssFiles[] = "plugins/UserCountryMap/css/qtip.css";
$cssFiles[] = "plugins/UserCountryMap/css/visitor-map.css";
$cssFiles[] = "plugins/UserCountryMap/css/realtime-map.css";
diff --git a/plugins/UserCountryMap/css/map.css b/plugins/UserCountryMap/css/map.css
index 7cc4a480aa..400e28384d 100644
--- a/plugins/UserCountryMap/css/map.css
+++ b/plugins/UserCountryMap/css/map.css
@@ -34,20 +34,19 @@
stroke: #fff;
fill: none;
}
+
.UserCountryMap_map svg .regionBG-fill {
stroke: #555;
stroke-width: 0.2px;
fill: #F6F5F3;
}
-
.UserCountryMap_map svg .regionBG-3 {
stroke: #ccc;
fill: #F2F1ED;
stroke-width: 1px;
}
-
.UserCountryMap_map svg .countryBG {
stroke: #fff;
fill: #fff;
@@ -67,7 +66,7 @@
.UserCountryMap_map svg .countryLabelBg {
font-weight: bold;
font-size: 10px;
- font-family: Arial,Verdana,Helvetica,sans-serif;
+ font-family: Arial, Verdana, Helvetica, sans-serif;
fill: #F6F5F3;
stroke: #F6F5F3;
stroke-width: 3px;
@@ -78,7 +77,7 @@
.UserCountryMap_map svg .countryLabel {
font-weight: bold;
font-size: 10px;
- font-family: Arial,Verdana,Helvetica,sans-serif;
+ font-family: Arial, Verdana, Helvetica, sans-serif;
fill: #808888;
}
diff --git a/plugins/UserCountryMap/css/qtip.css b/plugins/UserCountryMap/css/qtip.css
index 6b69dcb0c0..05057adc22 100644
--- a/plugins/UserCountryMap/css/qtip.css
+++ b/plugins/UserCountryMap/css/qtip.css
@@ -13,136 +13,140 @@
*/
/* Core qTip styles */
-.ui-tooltip, .qtip{
- position: absolute;
- left: -28000px;
- top: -28000px;
- display: none;
-
- max-width: 280px;
- min-width: 50px;
-
- font-size: 12px;
- line-height: 14px;
-
- z-index: 15000;
-}
-
- /* Fluid class for determining actual width in IE */
- .ui-tooltip-fluid{
- display: block;
- visibility: hidden;
- position: static !important;
- float: left !important;
- }
-
- .ui-tooltip-content{
- position: relative;
- padding: 5px 9px;
- overflow: hidden;
-
- border-width: 1px;
- border-style: solid;
-
- text-align: left;
- word-wrap: break-word;
- overflow: hidden;
- color: #444;
- }
-
- .ui-tooltip-titlebar{
- position: relative;
- min-height: 14px;
- padding: 5px 35px 0px 10px;
- overflow: hidden;
-
- border-width: 1px 1px 0;
- border-style: solid;
-
- font-weight: bold;
-
- }
-
- .ui-tooltip-titlebar + .ui-tooltip-content{ border-top-width: 0px !important; }
-
- /*! Default close button class */
- .ui-tooltip-titlebar .ui-state-default{
- position: absolute;
- right: 4px;
- top: 50%;
- margin-top: -9px;
-
- cursor: pointer;
- outline: medium none;
-
- border-width: 1px;
- border-style: solid;
- }
-
- * html .ui-tooltip-titlebar .ui-state-default{ top: 16px; } /* IE fix */
-
- .ui-tooltip-titlebar .ui-icon,
- .ui-tooltip-icon .ui-icon{
- display: block;
- text-indent: -1000em;
- }
-
- .ui-tooltip-icon, .ui-tooltip-icon .ui-icon{
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- }
-
- .ui-tooltip-icon .ui-icon{
- width: 18px;
- height: 14px;
-
- text-align: center;
-
- color: inherit;
- background: transparent none no-repeat -100em -100em;
- }
+.ui-tooltip, .qtip {
+ position: absolute;
+ left: -28000px;
+ top: -28000px;
+ display: none;
+ max-width: 280px;
+ min-width: 50px;
+
+ font-size: 12px;
+ line-height: 14px;
+
+ z-index: 15000;
+}
+
+/* Fluid class for determining actual width in IE */
+.ui-tooltip-fluid {
+ display: block;
+ visibility: hidden;
+ position: static !important;
+ float: left !important;
+}
+
+.ui-tooltip-content {
+ position: relative;
+ padding: 5px 9px;
+ overflow: hidden;
+
+ border-width: 1px;
+ border-style: solid;
+
+ text-align: left;
+ word-wrap: break-word;
+ overflow: hidden;
+ color: #444;
+}
+
+.ui-tooltip-titlebar {
+ position: relative;
+ min-height: 14px;
+ padding: 5px 35px 0px 10px;
+ overflow: hidden;
+
+ border-width: 1px 1px 0;
+ border-style: solid;
+
+ font-weight: bold;
+
+}
+
+.ui-tooltip-titlebar + .ui-tooltip-content {
+ border-top-width: 0px !important;
+}
+
+/*! Default close button class */
+.ui-tooltip-titlebar .ui-state-default {
+ position: absolute;
+ right: 4px;
+ top: 50%;
+ margin-top: -9px;
+
+ cursor: pointer;
+ outline: medium none;
+
+ border-width: 1px;
+ border-style: solid;
+}
+
+* html .ui-tooltip-titlebar .ui-state-default {
+ top: 16px;
+}
+
+/* IE fix */
+
+.ui-tooltip-titlebar .ui-icon,
+.ui-tooltip-icon .ui-icon {
+ display: block;
+ text-indent: -1000em;
+}
+
+.ui-tooltip-icon, .ui-tooltip-icon .ui-icon {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+}
+
+.ui-tooltip-icon .ui-icon {
+ width: 18px;
+ height: 14px;
+
+ text-align: center;
+
+ color: inherit;
+ background: transparent none no-repeat -100em -100em;
+}
/* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */
-.ui-tooltip-focus{
+.ui-tooltip-focus {
}
/* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */
-.ui-tooltip-hover{
-
-}
+.ui-tooltip-hover {
+}
/*! Default tooltip style */
.ui-tooltip-default {
-
+
}
.ui-tooltip-default .ui-tooltip-titlebar,
-.ui-tooltip-default .ui-tooltip-content{
- border-color: #E7E8E7;
- background-color: #F7F7F7;
- box-shadow: 1px 1px 3px rgba(0,0,0,.5);
+.ui-tooltip-default .ui-tooltip-content {
+ border-color: #E7E8E7;
+ background-color: #F7F7F7;
+ box-shadow: 1px 1px 3px rgba(0, 0, 0, .5);
}
-.ui-tooltip-default .ui-tooltip-titlebar .ui-state-hover{
- border-color: #E7E8E7;
- color: #111;
+.ui-tooltip-default .ui-tooltip-titlebar .ui-state-hover {
+ border-color: #E7E8E7;
+ color: #111;
}
.ui-tooltip-titlebar {
- border-bottom: 0;
+ border-bottom: 0;
}
.ui-tooltip-content {
- border-radius: 5px;
+ border-radius: 5px;
}
.ui-tooltip-content h3 {
- font-weight: bold;
- color: #7E7363;
- font-size: 13px;
- margin: 0 0 5px;
+ font-weight: bold;
+ color: #7E7363;
+ font-size: 13px;
+ margin: 0 0 5px;
} \ No newline at end of file
diff --git a/plugins/UserCountryMap/css/realtime-map.css b/plugins/UserCountryMap/css/realtime-map.css
index ba54ce63aa..c9d3aa47b0 100644
--- a/plugins/UserCountryMap/css/realtime-map.css
+++ b/plugins/UserCountryMap/css/realtime-map.css
@@ -2,13 +2,15 @@
.dataTableFooterIcons .inactiveIcon:hover {
background-color: #F2F1ED;
}
+
.dataTableFooterIcons .inactiveIcon {
cursor: default;
}
+
.dataTableFooterIcons .inactiveIcon img {
opacity: 0.3;
-moz-opacity: 0.3;
- filter:alpha(opacity=3);
+ filter: alpha(opacity=3);
}
#RealTimeMap-black {
@@ -22,40 +24,39 @@
}
#RealTimeMap .loadingPiwik {
- position: absolute!important;
- top: 42%!important;
- right: 10px!important;
- left: 10px!important;
- z-index: 10002!important;
+ position: absolute !important;
+ top: 42% !important;
+ right: 10px !important;
+ left: 10px !important;
+ z-index: 10002 !important;
display: block;
color: #000;
- vertical-align: middle!important;
+ vertical-align: middle !important;
text-align: center;
text-shadow: 0 0 5px #fff;
}
-
.tableIcon.inactiveIcon {
color: #99a;
}
.RealTimeMap-overlay,
.RealTimeMap-tooltip {
- display:block;
+ display: block;
position: absolute;
- z-index:1000;
+ z-index: 1000;
}
.RealTimeMap-overlay .content,
.RealTimeMap-tooltip .content {
- padding:5px;
- border-radius:3px;
- background:rgba(255,255,255,0.9);
+ padding: 5px;
+ border-radius: 3px;
+ background: rgba(255, 255, 255, 0.9);
}
.RealTimeMap-title {
top: 5px;
- left:5px;
+ left: 5px;
}
.RealTimeMap-legend {
@@ -63,12 +64,14 @@
font-size: 9px;
bottom: 40px;
}
+
.RealTimeMap-info {
left: 5px;
font-size: 11px;
bottom: 60px;
max-width: 42%;
}
+
.RealTimeMap-info-btn {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAA3NCSVQICAjb4U/gAAAAOVBMVEX///8AAAAAAABXV1dSUlKsrKzExMTd3d3V1dXp6end3d3p6enz8/P7+/v39/f///+vqZ6oopWUjH2LPulWAAAAE3RSTlMAESIzM2Z3mZmqqrvd7u7/////UUgTXgAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAYdEVYdENyZWF0aW9uIFRpbWUAMDMuMDEuMjAxM8rVeD8AAABnSURBVBiVhY/LFoAgCEQZ0p4W6f9/bIJ4slV3oTIeBoaICGADIAO8ibEwWn2IcwVovev7znqmCYRon9kEWUFvg3IysXyIXSil3fOvELupC9XUx7pQx/piDV1sVFLwMNF80sw97hj/AXRPCjtYdmhtAAAAAElFTkSuQmCC);
width: 16px;
@@ -77,18 +80,20 @@
left: 5px;
bottom: 40px;
position: absolute;
- z-index:1000;
+ z-index: 1000;
opacity: 0.9;
}
+
.realTimeMap_overlay {
- position:absolute;
- left:10px;
- bottom:6px;
+ position: absolute;
+ left: 10px;
+ bottom: 6px;
font-size: 12px;
- z-index:10;
- text-shadow:1px 1px 1px #FFFFFF, -1px 1px 1px #FFFFFF,1px -1px 1px #FFFFFF, -1px -1px 1px #FFFFFF,1px 1px 1px #FFFFFF, -1px 1px 1px #FFFFFF,1px -1px 1px #FFFFFF, -1px -1px 1px #FFFFFF;
+ z-index: 10;
+ text-shadow: 1px 1px 1px #FFFFFF, -1px 1px 1px #FFFFFF, 1px -1px 1px #FFFFFF, -1px -1px 1px #FFFFFF, 1px 1px 1px #FFFFFF, -1px 1px 1px #FFFFFF, 1px -1px 1px #FFFFFF, -1px -1px 1px #FFFFFF;
}
+
.realTimeMap_datetime {
bottom: 24px;
color: #887;
diff --git a/plugins/UserCountryMap/css/visitor-map.css b/plugins/UserCountryMap/css/visitor-map.css
index abc5360654..cc3340d895 100644
--- a/plugins/UserCountryMap/css/visitor-map.css
+++ b/plugins/UserCountryMap/css/visitor-map.css
@@ -9,20 +9,19 @@
}
.UserCountryMap .loadingPiwik {
- position: absolute!important;
- top: 42%!important;
- right: 10px!important;
- left: 10px!important;
- z-index: 999!important;
+ position: absolute !important;
+ top: 42% !important;
+ right: 10px !important;
+ left: 10px !important;
+ z-index: 999 !important;
display: block;
font-size: 12px;
color: #000;
- vertical-align: middle!important;
+ vertical-align: middle !important;
text-align: center;
text-shadow: 0 0 5px #fff;
}
-
.tableIcon.inactiveIcon {
color: #99a;
}
@@ -37,21 +36,21 @@
.UserCountryMap-overlay,
.UserCountryMap-tooltip {
- display:block;
+ display: block;
position: absolute;
- z-index:20;
+ z-index: 20;
}
.UserCountryMap-overlay .content,
.UserCountryMap-tooltip .content {
- padding:5px;
- border-radius:3px;
- background:rgba(255,255,255,0.9);
+ padding: 5px;
+ border-radius: 3px;
+ background: rgba(255, 255, 255, 0.9);
}
.UserCountryMap-title {
top: 5px;
- left:5px;
+ left: 5px;
}
.UserCountryMap-legend {
@@ -59,12 +58,14 @@
font-size: 9px;
bottom: 40px;
}
+
.UserCountryMap-info {
left: 5px;
font-size: 11px;
bottom: 60px;
max-width: 42%;
}
+
.UserCountryMap-info-btn {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAA3NCSVQICAjb4U/gAAAAOVBMVEX///8AAAAAAABXV1dSUlKsrKzExMTd3d3V1dXp6end3d3p6enz8/P7+/v39/f///+vqZ6oopWUjH2LPulWAAAAE3RSTlMAESIzM2Z3mZmqqrvd7u7/////UUgTXgAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAYdEVYdENyZWF0aW9uIFRpbWUAMDMuMDEuMjAxM8rVeD8AAABnSURBVBiVhY/LFoAgCEQZ0p4W6f9/bIJ4slV3oTIeBoaICGADIAO8ibEwWn2IcwVovev7znqmCYRon9kEWUFvg3IysXyIXSil3fOvELupC9XUx7pQx/piDV1sVFLwMNF80sw97hj/AXRPCjtYdmhtAAAAAElFTkSuQmCC);
width: 16px;
@@ -81,13 +82,15 @@
.dataTableFooterIcons .inactiveIcon:hover {
background-color: #F2F1ED;
}
+
.dataTableFooterIcons .inactiveIcon {
cursor: default;
}
+
.dataTableFooterIcons .inactiveIcon img {
opacity: 0.3;
-moz-opacity: 0.3;
- filter:alpha(opacity=3);
+ filter: alpha(opacity=3);
}
.mapWidgetStatus {
diff --git a/plugins/UserCountryMap/js/realtime-map.js b/plugins/UserCountryMap/js/realtime-map.js
index ef0f20c4d6..431b749089 100644
--- a/plugins/UserCountryMap/js/realtime-map.js
+++ b/plugins/UserCountryMap/js/realtime-map.js
@@ -1,4 +1,3 @@
-
/*!
* Piwik - Web Analytics
*
@@ -9,9 +8,9 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function() {
+(function () {
- var RealtimeMap = window.UserCountryMap.RealtimeMap = function(config, theWidget) {
+ var RealtimeMap = window.UserCountryMap.RealtimeMap = function (config, theWidget) {
this.config = config;
this.theWidget = theWidget || false;
this.run();
@@ -19,7 +18,7 @@
$.extend(RealtimeMap.prototype, {
- run: function() {
+ run: function () {
var debug = 0;
var self = this,
@@ -72,10 +71,10 @@
module: 'API',
method: 'Live.getLastVisitsDetails',
filter_limit: maxVisits,
- showColumns: ['latitude','longitude','actions','lastActionTimestamp',
- 'visitLocalTime','city','country','referrerType','referrerName',
- 'referrerTypeName','browserIcon','operatingSystemIcon',
- 'countryFlag','idVisit','actionDetails','continentCode',
+ showColumns: ['latitude', 'longitude', 'actions', 'lastActionTimestamp',
+ 'visitLocalTime', 'city', 'country', 'referrerType', 'referrerName',
+ 'referrerTypeName', 'browserIcon', 'operatingSystemIcon',
+ 'countryFlag', 'idVisit', 'actionDetails', 'continentCode',
'actions', 'searches', 'goalConversions'].join(','),
minTimestamp: firstRun ? -1 : lastTimestamp
});
@@ -101,7 +100,7 @@
*/
function _updateMap(svgUrl, callback) {
if (svgUrl === undefined) return;
- map.loadMap(config.svgBasePath + svgUrl, function() {
+ map.loadMap(config.svgBasePath + svgUrl, function () {
map.clear();
self.resize();
callback();
@@ -131,11 +130,11 @@
}
function relativeTime(ds) {
- var val = function(val) { return '<b>'+Math.round(val)+'</b>'; };
+ var val = function (val) { return '<b>' + Math.round(val) + '</b>'; };
return (ds < 90 ? _.seconds_ago.replace('%s', val(ds))
- : ds < 5400 ? _.minutes_ago.replace('%s', val(ds/60))
- : ds < 129600 ? _.hours_ago.replace('%s', val(ds/3600))
- : _.days_ago.replace('%s', val(ds/86400)));
+ : ds < 5400 ? _.minutes_ago.replace('%s', val(ds / 60))
+ : ds < 129600 ? _.hours_ago.replace('%s', val(ds / 3600))
+ : _.days_ago.replace('%s', val(ds / 86400)));
}
/*
@@ -145,32 +144,32 @@
function visitTooltip(r) {
var ds = new Date().getTime() / 1000 - r.lastActionTimestamp,
ad = r.actionDetails,
- ico = function(src) { return '<img src="'+src+'" alt="" class="icon" />&nbsp;'; };
- return '<h3>'+(r.city ? r.city+' / ' : '')+r.country+'</h3>'+
+ ico = function (src) { return '<img src="' + src + '" alt="" class="icon" />&nbsp;'; };
+ return '<h3>' + (r.city ? r.city + ' / ' : '') + r.country + '</h3>' +
// icons
- ico(r.countryFlag)+ico(r.browserIcon)+ico(r.operatingSystemIcon)+'<br/>'+
+ ico(r.countryFlag) + ico(r.browserIcon) + ico(r.operatingSystemIcon) + '<br/>' +
// last action
- (ad && ad.length && ad[ad.length-1].pageTitle ? '<em>' + ad[ad.length-1].pageTitle+'</em><br/>' : '')+
+ (ad && ad.length && ad[ad.length - 1].pageTitle ? '<em>' + ad[ad.length - 1].pageTitle + '</em><br/>' : '') +
// time of visit
- '<div class="rel-time" data-actiontime="'+r.lastActionTimestamp+'">'+relativeTime(ds)+'</div>'+
+ '<div class="rel-time" data-actiontime="' + r.lastActionTimestamp + '">' + relativeTime(ds) + '</div>' +
// either from or direct
(r.referrerType == "direct" ? r.referrerTypeName :
- _.from + ': '+r.referrerName) + '<br />' +
+ _.from + ': ' + r.referrerName) + '<br />' +
// local time
- '<small>'+_.local_time+': '+r.visitLocalTime+'</small><br />' +
+ '<small>' + _.local_time + ': ' + r.visitLocalTime + '</small><br />' +
// goals, if available
- (self.config.siteHasGoals && r.goalConversions ? '<small>'+_.goal_conversions.replace('%s', '<b>'+r.goalConversions+'</b>') +
- (r.searches > 0 ? ', ' + _.searches.replace('%s', r.searches) : '') + '</small><br />' : '')+
+ (self.config.siteHasGoals && r.goalConversions ? '<small>' + _.goal_conversions.replace('%s', '<b>' + r.goalConversions + '</b>') +
+ (r.searches > 0 ? ', ' + _.searches.replace('%s', r.searches) : '') + '</small><br />' : '') +
// actions and searches
- '<small>'+_.actions.replace('%s', '<b>'+r.actions+'</b>') +
- (r.searches > 0 ? ', ' + _.searches.replace('%s', '<b>'+r.searches+'</b>') : '') + '</small>';
+ '<small>' + _.actions.replace('%s', '<b>' + r.actions + '</b>') +
+ (r.searches > 0 ? ', ' + _.searches.replace('%s', '<b>' + r.searches + '</b>') : '') + '</small>';
}
/*
* the radius of the symbol depends on the lastActionTimestamp
*/
function visitRadius(r) {
- return Math.pow(age(r),4) * (self.maxRad - self.minRad) + self.minRad;
+ return Math.pow(age(r), 4) * (self.maxRad - self.minRad) + self.minRad;
}
/*
@@ -192,7 +191,7 @@
else col = chroma.hsl(
42 * age(r), // hue
Math.sqrt(age(r)), // saturation
- (engaged ? 0.65 : 0.5) - (1-age(r))* 0.45 // lightness
+ (engaged ? 0.65 : 0.5) - (1 - age(r)) * 0.45 // lightness
);
return col;
}
@@ -203,8 +202,8 @@
function visitSymbolAttrs(r) {
return {
fill: visitColor(r).hex(),
- 'fill-opacity': Math.pow(age(r),2) * 0.8 + 0.2,
- 'stroke-opacity': Math.pow(age(r),1.7) * 0.8 + 0.2,
+ 'fill-opacity': Math.pow(age(r), 2) * 0.8 + 0.2,
+ 'stroke-opacity': Math.pow(age(r), 1.7) * 0.8 + 0.2,
stroke: '#fff',
'stroke-width': 1 * age(r),
r: visitRadius(r)
@@ -216,7 +215,7 @@
* that corresponds to a visit on the map
*/
function highlightVisit(r) {
- $('#visitsLive li#'+r.idVisit + ' .datetime')
+ $('#visitsLive li#' + r.idVisit + ' .datetime')
.css('background', '#E4CD74');
}
@@ -225,7 +224,7 @@
* the visit marker on the map
*/
function unhighlightVisit(r) {
- $('#visitsLive li#'+r.idVisit + ' .datetime')
+ $('#visitsLive li#' + r.idVisit + ' .datetime')
.css({ background: '#E4E2D7' });
}
@@ -238,8 +237,8 @@
var c = map.paper.circle().attr(s.path.attrs);
c.insertBefore(s.path);
c.attr({ fill: false });
- c.animate({ r: c.attrs.r*3, 'stroke-width': 7, opacity: 0 }, 2500,
- 'linear', function() { c.remove(); });
+ c.animate({ r: c.attrs.r * 3, 'stroke-width': 7, opacity: 0 }, 2500,
+ 'linear', function () { c.remove(); });
// ..and pop the bubble itself
var col = s.path.attrs.fill,
rad = s.path.attrs.r;
@@ -276,23 +275,23 @@
data: [],
type: Kartograph.Bubble,
/*title: function(d) {
- return visitRadius(d) > 15 && d.actions > 1 ? d.actions : '';
- },
- labelattrs: {
- fill: '#fff',
- 'font-weight': 'bold',
- 'font-size': 11,
- stroke: false,
- cursor: 'pointer'
- },*/
- sortBy: function(r) { return r.lastActionTimestamp; },
+ return visitRadius(d) > 15 && d.actions > 1 ? d.actions : '';
+ },
+ labelattrs: {
+ fill: '#fff',
+ 'font-weight': 'bold',
+ 'font-size': 11,
+ stroke: false,
+ cursor: 'pointer'
+ },*/
+ sortBy: function (r) { return r.lastActionTimestamp; },
radius: visitRadius,
- location: function(r) { return [r.longitude, r.latitude]; },
+ location: function (r) { return [r.longitude, r.latitude]; },
attrs: visitSymbolAttrs,
tooltip: visitTooltip,
mouseenter: highlightVisit,
mouseleave: unhighlightVisit,
- click: function(r, s, evt) {
+ click: function (r, s, evt) {
evt.stopPropagation();
var cont = UserCountryMap.cont2cont[s.data.continentCode];
if (cont && cont != currentMap) {
@@ -307,7 +306,7 @@
if (report.length) {
// filter results without location
- report = $.grep(report, function(r) {
+ report = $.grep(report, function (r) {
return r.latitude !== null;
});
}
@@ -322,26 +321,26 @@
$('.realTimeMap_overlay .no_data').hide();
lastVisits = [].concat(report).concat(lastVisits).slice(0, maxVisits);
- oldest = lastVisits[lastVisits.length-1].lastActionTimestamp;
+ oldest = lastVisits[lastVisits.length - 1].lastActionTimestamp;
// let's try a different strategy
// remove symbols that are too old
//console.info('before', $('circle').length, visitSymbols.symbols.length);
var _removed = 0;
- visitSymbols.remove(function(r) {
+ visitSymbols.remove(function (r) {
if (r.lastActionTimestamp < oldest) _removed++;
return r.lastActionTimestamp < oldest;
});
// update symbols that remain
visitSymbols.update({
- radius: function(d) { return visitSymbolAttrs(d).r; },
+ radius: function (d) { return visitSymbolAttrs(d).r; },
attrs: visitSymbolAttrs
}, true);
// add new symbols
var newSymbols = [];
- $.each(report, function(i, r) {
+ $.each(report, function (i, r) {
newSymbols.push(visitSymbols.add(r));
});
@@ -350,11 +349,11 @@
//console.info('rendered', visitSymbols.symbols.length, $('circle').length);
- $.each(newSymbols, function(i, s) {
- if (i>10) return false;
+ $.each(newSymbols, function (i, s) {
+ if (i > 10) return false;
//if (s.data.lastActionTimestamp > lastTimestamp) {
s.path.hide(); // hide new symbol at first
- var t = setTimeout(function() { animateSymbol(s); },
+ var t = setTimeout(function () { animateSymbol(s); },
1000 * (s.data.lastActionTimestamp - now) + config.liveRefreshAfterMs);
symbolFadeInTimer.push(t);
//}
@@ -396,7 +395,7 @@
stroke: colorTheme[currentTheme].bg,
'stroke-width': 0.2
},
- click: function(d, p, evt) {
+ click: function (d, p, evt) {
evt.stopPropagation();
if (currentMap != 'world') { // zoom out if zoomed in
updateMap('world');
@@ -404,7 +403,7 @@
updateMap(UserCountryMap.ISO3toCONT[d.iso]);
}
},
- title: function(d) {
+ title: function (d) {
// return the country name for educational purpose
return d.name;
}
@@ -427,7 +426,7 @@
*/
function updateMap(_map) {
clearTimeout(nextReqTimer);
- $.each(symbolFadeInTimer, function(i, t) {
+ $.each(symbolFadeInTimer, function (i, t) {
clearTimeout(t);
});
symbolFadeInTimer = [];
@@ -442,12 +441,12 @@
updateMap(location.hash && (location.hash == '#world' || location.hash.match(/^#[A-Z][A-Z]$/)) ? location.hash.substr(1) : 'world'); // TODO: restore last state
// clicking on map background zooms out
- $('#RealTimeMap_map').off('click').click(function() {
+ $('#RealTimeMap_map').off('click').click(function () {
if (currentMap != 'world') updateMap('world');
});
// secret gimmick shortcuts
- $(window).keydown(function(evt) {
+ $(window).keydown(function (evt) {
// shift+alt+C changes color mode
if (evt.shiftKey && evt.altKey && evt.keyCode == 67) {
colorMode = ({
@@ -464,8 +463,8 @@
$('.widget').css({ 'border-width': 1 });
}
map.getLayer('countries')
- .style('fill', colorTheme[currentTheme].fill )
- .style('stroke', colorTheme[currentTheme].bg );
+ .style('fill', colorTheme[currentTheme].fill)
+ .style('stroke', colorTheme[currentTheme].bg);
storeSettings();
}
@@ -489,14 +488,15 @@
function getTimeInSiteTimezone() {
}
+
// setup automatic tooltip updates
- setInterval(function() {
- $('.qtip .rel-time').each(function(i, el) {
+ setInterval(function () {
+ $('.qtip .rel-time').each(function (i, el) {
el = $(el);
var ds = new Date().getTime() / 1000 - el.data('actiontime');
el.html(relativeTime(ds));
});
- var d = new Date(), datetime = d.toTimeString().substr(0,8);
+ var d = new Date(), datetime = d.toTimeString().substr(0, 8);
$('.realTimeMap_datetime').html(datetime);
}, 1000);
},
@@ -504,17 +504,17 @@
/*
* resizes the map to widget dimensions
*/
- resize: function() {
+ resize: function () {
var ratio, w, h, map = this.map;
ratio = map.viewAB.width / map.viewAB.height;
w = map.container.width();
- h = Math.min(w / ratio, $(window).height()-30);
+ h = Math.min(w / ratio, $(window).height() - 30);
var radScale = Math.pow((h * ratio * h) / 130000, 0.3);
this.maxRad = 10 * radScale;
this.minRad = 4 * radScale;
- map.container.height(h-2);
+ map.container.height(h - 2);
map.resize(w, h);
if (map.symbolGroups && map.symbolGroups.length > 0) {
map.symbolGroups[0].update();
@@ -524,7 +524,7 @@
else $('.tableIcon span').show();
},
- destroy: function() {
+ destroy: function () {
this.map.clear();
$(this.map.container).html('');
}
diff --git a/plugins/UserCountryMap/js/vendor/chroma.min.js b/plugins/UserCountryMap/js/vendor/chroma.min.js
index 6e8ed35d5b..971c4da88c 100644
--- a/plugins/UserCountryMap/js/vendor/chroma.min.js
+++ b/plugins/UserCountryMap/js/vendor/chroma.min.js
@@ -1,33 +1,649 @@
/**
- chroma.js - color manipulation in javascript
-
- Copyright (c) 2011-2013, Gregor Aisch
- 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.
-
- * The name Gregor Aisch may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- 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 GREGOR AISCH 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.
-
- @source: https://github.com/gka/chroma.js
-*/
-(function(){var Color,ColorScale,K,PITHIRD,TWOPI,X,Y,Z,brewer,chroma,colors,cos,hex2rgb,hsi2rgb,hsl2rgb,hsv2rgb,lab2lch,lab2rgb,lab_xyz,lch2lab,lch2rgb,limit,rgb2hex,rgb2hsi,rgb2hsl,rgb2hsv,rgb2lab,rgb2lch,rgb_xyz,root,type,unpack,xyz_lab,xyz_rgb,_ref,_ref1,_ref2,_ref3,_ref4,_ref5;root=typeof exports!=="undefined"&&exports!==null?exports:this;chroma=(_ref=root.chroma)!=null?_ref:root.chroma={};if(typeof module!=="undefined"&&module!==null){module.exports=chroma}Color=function(){function Color(x,y,z,m){var me,_ref1;me=this;if(!(x!=null)&&!(y!=null)&&!(z!=null)&&!(m!=null)){x=[255,0,255]}if(type(x)==="array"&&x.length===3){if(m==null){m=y}_ref1=x,x=_ref1[0],y=_ref1[1],z=_ref1[2]}if(type(x)==="string"){m="hex"}else{if(m==null){m="rgb"}}if(m==="rgb"){me._rgb=[x,y,z]}else if(m==="hsl"){me._rgb=hsl2rgb(x,y,z)}else if(m==="hsv"){me._rgb=hsv2rgb(x,y,z)}else if(m==="hex"){me._rgb=hex2rgb(x)}else if(m==="lab"){me._rgb=lab2rgb(x,y,z)}else if(m==="lch"){me._rgb=lch2rgb(x,y,z)}else if(m==="hsi"){me._rgb=hsi2rgb(x,y,z)}}Color.prototype.rgb=function(){return this._rgb};Color.prototype.hex=function(){return rgb2hex(this._rgb)};Color.prototype.toString=function(){return this.hex()};Color.prototype.hsl=function(){return rgb2hsl(this._rgb)};Color.prototype.hsv=function(){return rgb2hsv(this._rgb)};Color.prototype.lab=function(){return rgb2lab(this._rgb)};Color.prototype.lch=function(){return rgb2lch(this._rgb)};Color.prototype.hsi=function(){return rgb2hsi(this._rgb)};Color.prototype.interpolate=function(f,col,m){var dh,hue,hue0,hue1,lbv,lbv0,lbv1,me,sat,sat0,sat1,xyz0,xyz1;me=this;if(m==null){m="rgb"}if(type(col)==="string"){col=new Color(col)}if(m==="hsl"||m==="hsv"||m==="lch"||m==="hsi"){if(m==="hsl"){xyz0=me.hsl();xyz1=col.hsl()}else if(m==="hsv"){xyz0=me.hsv();xyz1=col.hsv()}else if(m==="hsi"){xyz0=me.hsi();xyz1=col.hsi()}else if(m==="lch"){xyz0=me.lch();xyz1=col.lch()}if(m.substr(0,1)==="h"){hue0=xyz0[0],sat0=xyz0[1],lbv0=xyz0[2];hue1=xyz1[0],sat1=xyz1[1],lbv1=xyz1[2]}else{lbv0=xyz0[0],sat0=xyz0[1],hue0=xyz0[2];lbv1=xyz1[0],sat1=xyz1[1],hue1=xyz1[2]}if(!isNaN(hue0)&&!isNaN(hue1)){if(hue1>hue0&&hue1-hue0>180){dh=hue1-(hue0+360)}else if(hue1<hue0&&hue0-hue1>180){dh=hue1+360-hue0}else{dh=hue1-hue0}hue=hue0+f*dh}else if(!isNaN(hue0)){hue=hue0;if(lbv1===1||lbv1===0){sat=sat0}}else if(!isNaN(hue1)){hue=hue1;if(lbv0===1||lbv0===0){sat=sat1}}else{hue=void 0}if(sat==null){sat=sat0+f*(sat1-sat0)}lbv=lbv0+f*(lbv1-lbv0);if(m.substr(0,1)==="h"){return new Color(hue,sat,lbv,m)}else{return new Color(lbv,sat,hue,m)}}else if(m==="rgb"){xyz0=me._rgb;xyz1=col._rgb;return new Color(xyz0[0]+f*(xyz1[0]-xyz0[0]),xyz0[1]+f*(xyz1[1]-xyz0[1]),xyz0[2]+f*(xyz1[2]-xyz0[2]),m)}else if(m==="lab"){xyz0=me.lab();xyz1=col.lab();return new Color(xyz0[0]+f*(xyz1[0]-xyz0[0]),xyz0[1]+f*(xyz1[1]-xyz0[1]),xyz0[2]+f*(xyz1[2]-xyz0[2]),m)}else{throw"color mode "+m+" is not supported"}};Color.prototype.darken=function(amount){var lch,me;if(amount==null){amount=.2}me=this;lch=me.lch();lch[2]-=amount;return chroma.lch(lch)};return Color}();hex2rgb=function(hex){var b,g,r,u;if(!hex.match(/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)){if(chroma.colors!=null&&chroma.colors[hex]){hex=chroma.colors[hex]}else{throw"unknown color format: "+hex}}if(hex.length===4||hex.length===7){hex=hex.substr(1)}if(hex.length===3){hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]}u=parseInt(hex,16);r=u>>16;g=u>>8&255;b=u&255;return[r,g,b]};rgb2hex=function(){var b,g,r,str,u,_ref1;_ref1=unpack(arguments),r=_ref1[0],g=_ref1[1],b=_ref1[2];u=r<<16|g<<8|b;str="000000"+u.toString(16).toUpperCase();return"#"+str.substr(str.length-6)};hsv2rgb=function(){var b,f,g,h,i,p,q,r,s,t,v,_ref1,_ref2,_ref3,_ref4,_ref5,_ref6,_ref7;_ref1=unpack(arguments),h=_ref1[0],s=_ref1[1],v=_ref1[2];v*=255;if(s===0){r=g=b=v}else{if(h===360){h=0}if(h>360){h-=360}if(h<0){h+=360}h/=60;i=Math.floor(h);f=h-i;p=v*(1-s);q=v*(1-s*f);t=v*(1-s*(1-f));switch(i){case 0:_ref2=[v,t,p],r=_ref2[0],g=_ref2[1],b=_ref2[2];break;case 1:_ref3=[q,v,p],r=_ref3[0],g=_ref3[1],b=_ref3[2];break;case 2:_ref4=[p,v,t],r=_ref4[0],g=_ref4[1],b=_ref4[2];break;case 3:_ref5=[p,q,v],r=_ref5[0],g=_ref5[1],b=_ref5[2];break;case 4:_ref6=[t,p,v],r=_ref6[0],g=_ref6[1],b=_ref6[2];break;case 5:_ref7=[v,p,q],r=_ref7[0],g=_ref7[1],b=_ref7[2]}}r=Math.round(r);g=Math.round(g);b=Math.round(b);return[r,g,b]};rgb2hsv=function(){var b,delta,g,h,max,min,r,s,v,_ref1;_ref1=unpack(arguments),r=_ref1[0],g=_ref1[1],b=_ref1[2];min=Math.min(r,g,b);max=Math.max(r,g,b);delta=max-min;v=max/255;if(max===0){h=void 0;s=0}else{s=delta/max;if(r===max){h=(g-b)/delta}if(g===max){h=2+(b-r)/delta}if(b===max){h=4+(r-g)/delta}h*=60;if(h<0){h+=360}}return[h,s,v]};hsl2rgb=function(){var b,c,g,h,i,l,r,s,t1,t2,t3,_i,_ref1,_ref2;_ref1=unpack(arguments),h=_ref1[0],s=_ref1[1],l=_ref1[2];if(s===0){r=g=b=l*255}else{t3=[0,0,0];c=[0,0,0];t2=l<.5?l*(1+s):l+s-l*s;t1=2*l-t2;h/=360;t3[0]=h+1/3;t3[1]=h;t3[2]=h-1/3;for(i=_i=0;_i<=2;i=++_i){if(t3[i]<0){t3[i]+=1}if(t3[i]>1){t3[i]-=1}if(6*t3[i]<1){c[i]=t1+(t2-t1)*6*t3[i]}else if(2*t3[i]<1){c[i]=t2}else if(3*t3[i]<2){c[i]=t1+(t2-t1)*(2/3-t3[i])*6}else{c[i]=t1}}_ref2=[Math.round(c[0]*255),Math.round(c[1]*255),Math.round(c[2]*255)],r=_ref2[0],g=_ref2[1],b=_ref2[2]}return[r,g,b]};rgb2hsl=function(r,g,b){var h,l,max,min,s,_ref1;if(r!==void 0&&r.length===3){_ref1=r,r=_ref1[0],g=_ref1[1],b=_ref1[2]}r/=255;g/=255;b/=255;min=Math.min(r,g,b);max=Math.max(r,g,b);l=(max+min)/2;if(max===min){s=0;h=void 0}else{s=l<.5?(max-min)/(max+min):(max-min)/(2-max-min)}if(r===max){h=(g-b)/(max-min)}else if(g===max){h=2+(b-r)/(max-min)}else if(b===max){h=4+(r-g)/(max-min)}h*=60;if(h<0){h+=360}return[h,s,l]};K=18;X=.95047;Y=1;Z=1.08883;lab2rgb=function(l,a,b){var g,r,x,y,z,_ref1,_ref2;if(l!==void 0&&l.length===3){_ref1=l,l=_ref1[0],a=_ref1[1],b=_ref1[2]}if(l!==void 0&&l.length===3){_ref2=l,l=_ref2[0],a=_ref2[1],b=_ref2[2]}y=(l+16)/116;x=y+a/500;z=y-b/200;x=lab_xyz(x)*X;y=lab_xyz(y)*Y;z=lab_xyz(z)*Z;r=xyz_rgb(3.2404542*x-1.5371385*y-.4985314*z);g=xyz_rgb(-.969266*x+1.8760108*y+.041556*z);b=xyz_rgb(.0556434*x-.2040259*y+1.0572252*z);return[limit(r,0,255),limit(g,0,255),limit(b,0,255)]};rgb2lab=function(){var b,g,r,x,y,z,_ref1;_ref1=unpack(arguments),r=_ref1[0],g=_ref1[1],b=_ref1[2];r=rgb_xyz(r);g=rgb_xyz(g);b=rgb_xyz(b);x=xyz_lab((.4124564*r+.3575761*g+.1804375*b)/X);y=xyz_lab((.2126729*r+.7151522*g+.072175*b)/Y);z=xyz_lab((.0193339*r+.119192*g+.9503041*b)/Z);return[116*y-16,500*(x-y),200*(y-z)]};lch2lab=function(){var c,h,l,_ref1;_ref1=unpack(arguments),l=_ref1[0],c=_ref1[1],h=_ref1[2];h=h*Math.PI/180;return[l,Math.cos(h)*c,Math.sin(h)*c]};lch2rgb=function(l,c,h){var L,a,b,g,r,_ref1,_ref2;_ref1=lch2lab(l,c,h),L=_ref1[0],a=_ref1[1],b=_ref1[2];_ref2=lab2rgb(L,a,b),r=_ref2[0],g=_ref2[1],b=_ref2[2];return[limit(r,0,255),limit(g,0,255),limit(b,0,255)]};lab_xyz=function(x){if(x>.206893034){return x*x*x}else{return(x-4/29)/7.787037}};xyz_lab=function(x){if(x>.008856){return Math.pow(x,1/3)}else{return 7.787037*x+4/29}};xyz_rgb=function(r){return Math.round(255*(r<=.00304?12.92*r:1.055*Math.pow(r,1/2.4)-.055))};rgb_xyz=function(r){if((r/=255)<=.04045){return r/12.92}else{return Math.pow((r+.055)/1.055,2.4)}};lab2lch=function(){var a,b,c,h,l,_ref1;_ref1=unpack(arguments),l=_ref1[0],a=_ref1[1],b=_ref1[2];c=Math.sqrt(a*a+b*b);h=Math.atan2(b,a)/Math.PI*180;return[l,c,h]};rgb2lch=function(){var a,b,g,l,r,_ref1,_ref2;_ref1=unpack(arguments),r=_ref1[0],g=_ref1[1],b=_ref1[2];_ref2=rgb2lab(r,g,b),l=_ref2[0],a=_ref2[1],b=_ref2[2];return lab2lch(l,a,b)};rgb2hsi=function(){var TWOPI,b,g,h,i,min,r,s,_ref1;_ref1=unpack(arguments),r=_ref1[0],g=_ref1[1],b=_ref1[2];TWOPI=Math.PI*2;r/=255;g/=255;b/=255;min=Math.min(r,g,b);i=(r+g+b)/3;s=1-min/i;if(s===0){h=0}else{h=(r-g+(r-b))/2;h/=Math.sqrt((r-g)*(r-g)+(r-b)*(g-b));h=Math.acos(h);if(b>g){h=TWOPI-h}h/=TWOPI}return[h*360,s,i]};hsi2rgb=function(h,s,i){var b,g,r,_ref1;_ref1=unpack(arguments),h=_ref1[0],s=_ref1[1],i=_ref1[2];h/=360;if(h<1/3){b=(1-s)/3;r=(1+s*cos(TWOPI*h)/cos(PITHIRD-TWOPI*h))/3;g=1-(b+r)}else if(h<2/3){h-=1/3;r=(1-s)/3;g=(1+s*cos(TWOPI*h)/cos(PITHIRD-TWOPI*h))/3;b=1-(r+g)}else{h-=2/3;g=(1-s)/3;b=(1+s*cos(TWOPI*h)/cos(PITHIRD-TWOPI*h))/3;r=1-(g+b)}r=limit(i*r*3);g=limit(i*g*3);b=limit(i*b*3);return[r*255,g*255,b*255]};chroma.Color=Color;chroma.color=function(x,y,z,m){return new Color(x,y,z,m)};chroma.hsl=function(h,s,l){return new Color(h,s,l,"hsl")};chroma.hsv=function(h,s,v){return new Color(h,s,v,"hsv")};chroma.rgb=function(r,g,b){return new Color(r,g,b,"rgb")};chroma.hex=function(x){return new Color(x)};chroma.lab=function(l,a,b){return new Color(l,a,b,"lab")};chroma.lch=function(l,c,h){return new Color(l,c,h,"lch")};chroma.hsi=function(h,s,i){return new Color(h,s,i,"hsi")};chroma.interpolate=function(a,b,f,m){if(!(a!=null)||!(b!=null)){return"#000"}if(type(a)==="string"){a=new Color(a)}if(type(b)==="string"){b=new Color(b)}return a.interpolate(f,b,m)};root=typeof exports!=="undefined"&&exports!==null?exports:this;chroma=(_ref1=root.chroma)!=null?_ref1:root.chroma={};Color=chroma.Color;ColorScale=function(){function ColorScale(opts){var me,_ref2,_ref3;if(opts==null){opts={}}me=this;me.range(opts.colors,opts.positions);me._mode=(_ref2=opts.mode)!=null?_ref2:"rgb";me._nacol=chroma.hex((_ref3=opts.nacol)!=null?_ref3:chroma.hex("#ccc"));me.domain([0,1]);me}ColorScale.prototype.range=function(colors,positions){var c,col,me,_i,_j,_ref2,_ref3,_ref4;me=this;if(!(colors!=null)){colors=["#ddd","#222"]}if(colors!=null&&type(colors)==="string"&&((_ref2=chroma.brewer)!=null?_ref2[colors]:void 0)!=null){colors=chroma.brewer[colors].slice(0)}for(c=_i=0,_ref3=colors.length-1;0<=_ref3?_i<=_ref3:_i>=_ref3;c=0<=_ref3?++_i:--_i){col=colors[c];if(type(col)==="string"){colors[c]=new Color(col)}}me._colors=colors;if(positions!=null){me._pos=positions}else{me._pos=[];for(c=_j=0,_ref4=colors.length-1;0<=_ref4?_j<=_ref4:_j>=_ref4;c=0<=_ref4?++_j:--_j){me._pos.push(c/(colors.length-1))}}return me};ColorScale.prototype.domain=function(domain){var me;if(domain==null){domain=[]}me=this;me._domain=domain;me._min=domain[0];me._max=domain[domain.length-1];if(domain.length===2){me._numClasses=0}else{me._numClasses=domain.length-1}return me};ColorScale.prototype.get=function(value){var c,f,f0,me;me=this;if(isNaN(value)){return me._nacol}if(me._domain.length>2){c=me.getClass(value);f=c/(me._numClasses-1)}else{f=f0=(value-me._min)/(me._max-me._min);f=Math.min(1,Math.max(0,f))}return me.fColor(f)};ColorScale.prototype.fColor=function(f){var col,cols,i,me,p,_i,_ref2;me=this;cols=me._colors;for(i=_i=0,_ref2=me._pos.length-1;0<=_ref2?_i<=_ref2:_i>=_ref2;i=0<=_ref2?++_i:--_i){p=me._pos[i];if(f<=p){col=cols[i];break}if(f>=p&&i===me._pos.length-1){col=cols[i];break}if(f>p&&f<me._pos[i+1]){f=(f-p)/(me._pos[i+1]-p);col=chroma.interpolate(cols[i],cols[i+1],f,me._mode);break}}return col};ColorScale.prototype.classifyValue=function(value){var domain,i,maxc,me,minc,n,val;me=this;domain=me._domain;val=value;if(domain.length>2){n=domain.length-1;i=me.getClass(value);val=domain[i]+(domain[i+1]-domain[i])*.5;minc=domain[0];maxc=domain[n-1];val=me._min+(val-minc)/(maxc-minc)*(me._max-me._min)}return val};ColorScale.prototype.getClass=function(value){var domain,i,n,self;self=this;domain=self._domain;if(domain!=null){n=domain.length-1;i=0;while(i<n&&value>=domain[i]){i++}return i-1}return 0};ColorScale.prototype.validValue=function(value){return!isNaN(value)};return ColorScale}();chroma.ColorScale=ColorScale;chroma.scale=function(colors,positions){var colscale,f,out;colscale=new chroma.ColorScale;colscale.range(colors,positions);out=false;f=function(v){var c;c=colscale.get(v);if(out&&c[out]){return c[out]()}else{return c}};f.domain=function(domain,classes,mode){var d;if(mode==null){mode="e"}if(classes!=null){d=chroma.analyze(domain);if(classes===0){domain=[d.min,d.max]}else{domain=chroma.limits(d,mode,classes)}}colscale.domain(domain);return f};f.mode=function(_m){colscale._mode=_m;return f};f.range=function(_colors,_pos){colscale.range(_colors,_pos);return f};f.out=function(_o){out=_o;return f};return f};if((_ref2=chroma.scales)==null){chroma.scales={}}chroma.scales.cool=function(){return chroma.scale([chroma.hsl(180,1,.9),chroma.hsl(250,.7,.4)])};chroma.scales.hot=function(){return chroma.scale(["#000","#f00","#ff0","#fff"],[0,.25,.75,1]).mode("rgb")};chroma.analyze=function(data,key,filter){var add,k,r,val,visit,_i,_len;r={min:Number.MAX_VALUE,max:Number.MAX_VALUE*-1,sum:0,values:[],count:0};if(!(filter!=null)){filter=function(){return true}}add=function(val){if(val!=null&&!isNaN(val)){r.values.push(val);r.sum+=val;if(val<r.min){r.min=val}if(val>r.max){r.max=val}r.count+=1}};visit=function(val,k){if(filter(val,k)){if(key!=null&&type(key)==="function"){return add(key(val))}else if(key!=null&&type(key)==="str"||type(key)==="number"){return add(val[key])}else{return add(val)}}};if(type(data)==="array"){for(_i=0,_len=data.length;_i<_len;_i++){val=data[_i];visit(val)}}else{for(k in data){val=data[k];visit(val,k)}}r.domain=[r.min,r.max];r.limits=function(mode,num){return chroma.limits(r,mode,num)};return r};chroma.limits=function(data,mode,num){var assignments,best,centroids,cluster,clusterSizes,dist,i,j,kClusters,limits,max,min,mindist,n,nb_iters,newCentroids,p,pb,pr,repeat,sum,tmpKMeansBreaks,value,values,_i,_j,_k,_l,_m,_n,_o,_p,_q,_r,_ref10,_ref11,_ref12,_ref13,_ref14,_ref15,_ref16,_ref3,_ref4,_ref5,_ref6,_ref7,_ref8,_ref9,_s,_t,_u,_v;if(mode==null){mode="equal"}if(num==null){num=7}if(!(data.values!=null)){data=chroma.analyze(data)}min=data.min;max=data.max;sum=data.sum;values=data.values.sort(function(a,b){return a-b});limits=[];if(mode.substr(0,1)==="c"){limits.push(min);limits.push(max)}if(mode.substr(0,1)==="e"){limits.push(min);for(i=_i=1,_ref3=num-1;1<=_ref3?_i<=_ref3:_i>=_ref3;i=1<=_ref3?++_i:--_i){limits.push(min+i/num*(max-min))}limits.push(max)}else if(mode.substr(0,1)==="q"){limits.push(min);for(i=_j=1,_ref4=num-1;1<=_ref4?_j<=_ref4:_j>=_ref4;i=1<=_ref4?++_j:--_j){p=values.length*i/num;pb=Math.floor(p);if(pb===p){limits.push(values[pb])}else{pr=p-pb;limits.push(values[pb]*pr+values[pb+1]*(1-pr))}}limits.push(max)}else if(mode.substr(0,1)==="k"){n=values.length;assignments=new Array(n);clusterSizes=new Array(num);repeat=true;nb_iters=0;centroids=null;centroids=[];centroids.push(min);for(i=_k=1,_ref5=num-1;1<=_ref5?_k<=_ref5:_k>=_ref5;i=1<=_ref5?++_k:--_k){centroids.push(min+i/num*(max-min))}centroids.push(max);while(repeat){for(j=_l=0,_ref6=num-1;0<=_ref6?_l<=_ref6:_l>=_ref6;j=0<=_ref6?++_l:--_l){clusterSizes[j]=0}for(i=_m=0,_ref7=n-1;0<=_ref7?_m<=_ref7:_m>=_ref7;i=0<=_ref7?++_m:--_m){value=values[i];mindist=Number.MAX_VALUE;for(j=_n=0,_ref8=num-1;0<=_ref8?_n<=_ref8:_n>=_ref8;j=0<=_ref8?++_n:--_n){dist=Math.abs(centroids[j]-value);if(dist<mindist){mindist=dist;best=j}}clusterSizes[best]++;assignments[i]=best}newCentroids=new Array(num);for(j=_o=0,_ref9=num-1;0<=_ref9?_o<=_ref9:_o>=_ref9;j=0<=_ref9?++_o:--_o){newCentroids[j]=null}for(i=_p=0,_ref10=n-1;0<=_ref10?_p<=_ref10:_p>=_ref10;i=0<=_ref10?++_p:--_p){cluster=assignments[i];if(newCentroids[cluster]===null){newCentroids[cluster]=values[i]}else{newCentroids[cluster]+=values[i]}}for(j=_q=0,_ref11=num-1;0<=_ref11?_q<=_ref11:_q>=_ref11;j=0<=_ref11?++_q:--_q){newCentroids[j]*=1/clusterSizes[j]}repeat=false;for(j=_r=0,_ref12=num-1;0<=_ref12?_r<=_ref12:_r>=_ref12;j=0<=_ref12?++_r:--_r){if(newCentroids[j]!==centroids[i]){repeat=true;break}}centroids=newCentroids;nb_iters++;if(nb_iters>200){repeat=false}}kClusters={};for(j=_s=0,_ref13=num-1;0<=_ref13?_s<=_ref13:_s>=_ref13;j=0<=_ref13?++_s:--_s){kClusters[j]=[]}for(i=_t=0,_ref14=n-1;0<=_ref14?_t<=_ref14:_t>=_ref14;i=0<=_ref14?++_t:--_t){cluster=assignments[i];kClusters[cluster].push(values[i])}tmpKMeansBreaks=[];for(j=_u=0,_ref15=num-1;0<=_ref15?_u<=_ref15:_u>=_ref15;j=0<=_ref15?++_u:--_u){tmpKMeansBreaks.push(kClusters[j][0]);tmpKMeansBreaks.push(kClusters[j][kClusters[j].length-1])}tmpKMeansBreaks=tmpKMeansBreaks.sort(function(a,b){return a-b});limits.push(tmpKMeansBreaks[0]);for(i=_v=1,_ref16=tmpKMeansBreaks.length-1;_v<=_ref16;i=_v+=2){if(!isNaN(tmpKMeansBreaks[i])){limits.push(tmpKMeansBreaks[i])}}}return limits};root=typeof exports!=="undefined"&&exports!==null?exports:this;type=function(){var classToType,name,_i,_len,_ref3;classToType={};_ref3="Boolean Number String Function Array Date RegExp Undefined Null".split(" ");for(_i=0,_len=_ref3.length;_i<_len;_i++){name=_ref3[_i];classToType["[object "+name+"]"]=name.toLowerCase()}return function(obj){var strType;strType=Object.prototype.toString.call(obj);return classToType[strType]||"object"}}();if((_ref3=root.type)==null){root.type=type}Array.max=function(array){return Math.max.apply(Math,array)};Array.min=function(array){return Math.min.apply(Math,array)};limit=function(x,min,max){if(min==null){min=0}if(max==null){max=1}if(x<min){x=min}if(x>max){x=max}return x};unpack=function(args){if(args.length===3){return args}else{return args[0]}};TWOPI=Math.PI*2;PITHIRD=Math.PI/3;cos=Math.cos;root=typeof exports!=="undefined"&&exports!==null?exports:this;chroma=(_ref4=root.chroma)!=null?_ref4:root.chroma={};chroma.brewer=brewer={OrRd:["#fff7ec","#fee8c8","#fdd49e","#fdbb84","#fc8d59","#ef6548","#d7301f","#b30000","#7f0000"],PuBu:["#fff7fb","#ece7f2","#d0d1e6","#a6bddb","#74a9cf","#3690c0","#0570b0","#045a8d","#023858"],BuPu:["#f7fcfd","#e0ecf4","#bfd3e6","#9ebcda","#8c96c6","#8c6bb1","#88419d","#810f7c","#4d004b"],Oranges:["#fff5eb","#fee6ce","#fdd0a2","#fdae6b","#fd8d3c","#f16913","#d94801","#a63603","#7f2704"],BuGn:["#f7fcfd","#e5f5f9","#ccece6","#99d8c9","#66c2a4","#41ae76","#238b45","#006d2c","#00441b"],YlOrBr:["#ffffe5","#fff7bc","#fee391","#fec44f","#fe9929","#ec7014","#cc4c02","#993404","#662506"],YlGn:["#ffffe5","#f7fcb9","#d9f0a3","#addd8e","#78c679","#41ab5d","#238443","#006837","#004529"],Reds:["#fff5f0","#fee0d2","#fcbba1","#fc9272","#fb6a4a","#ef3b2c","#cb181d","#a50f15","#67000d"],RdPu:["#fff7f3","#fde0dd","#fcc5c0","#fa9fb5","#f768a1","#dd3497","#ae017e","#7a0177","#49006a"],Greens:["#f7fcf5","#e5f5e0","#c7e9c0","#a1d99b","#74c476","#41ab5d","#238b45","#006d2c","#00441b"],YlGnBu:["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"],Purples:["#fcfbfd","#efedf5","#dadaeb","#bcbddc","#9e9ac8","#807dba","#6a51a3","#54278f","#3f007d"],GnBu:["#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#0868ac","#084081"],Greys:["#ffffff","#f0f0f0","#d9d9d9","#bdbdbd","#969696","#737373","#525252","#252525","#000000"],YlOrRd:["#ffffcc","#ffeda0","#fed976","#feb24c","#fd8d3c","#fc4e2a","#e31a1c","#bd0026","#800026"],PuRd:["#f7f4f9","#e7e1ef","#d4b9da","#c994c7","#df65b0","#e7298a","#ce1256","#980043","#67001f"],Blues:["#f7fbff","#deebf7","#c6dbef","#9ecae1","#6baed6","#4292c6","#2171b5","#08519c","#08306b"],PuBuGn:["#fff7fb","#ece2f0","#d0d1e6","#a6bddb","#67a9cf","#3690c0","#02818a","#016c59","#014636"],Spectral:["#9e0142","#d53e4f","#f46d43","#fdae61","#fee08b","#ffffbf","#e6f598","#abdda4","#66c2a5","#3288bd","#5e4fa2"],RdYlGn:["#a50026","#d73027","#f46d43","#fdae61","#fee08b","#ffffbf","#d9ef8b","#a6d96a","#66bd63","#1a9850","#006837"],RdBu:["#67001f","#b2182b","#d6604d","#f4a582","#fddbc7","#f7f7f7","#d1e5f0","#92c5de","#4393c3","#2166ac","#053061"],PiYG:["#8e0152","#c51b7d","#de77ae","#f1b6da","#fde0ef","#f7f7f7","#e6f5d0","#b8e186","#7fbc41","#4d9221","#276419"],PRGn:["#40004b","#762a83","#9970ab","#c2a5cf","#e7d4e8","#f7f7f7","#d9f0d3","#a6dba0","#5aae61","#1b7837","#00441b"],RdYlBu:["#a50026","#d73027","#f46d43","#fdae61","#fee090","#ffffbf","#e0f3f8","#abd9e9","#74add1","#4575b4","#313695"],BrBG:["#543005","#8c510a","#bf812d","#dfc27d","#f6e8c3","#f5f5f5","#c7eae5","#80cdc1","#35978f","#01665e","#003c30"],RdGy:["#67001f","#b2182b","#d6604d","#f4a582","#fddbc7","#ffffff","#e0e0e0","#bababa","#878787","#4d4d4d","#1a1a1a"],PuOr:["#7f3b08","#b35806","#e08214","#fdb863","#fee0b6","#f7f7f7","#d8daeb","#b2abd2","#8073ac","#542788","#2d004b"],Set2:["#66c2a5","#fc8d62","#8da0cb","#e78ac3","#a6d854","#ffd92f","#e5c494","#b3b3b3"],Accent:["#7fc97f","#beaed4","#fdc086","#ffff99","#386cb0","#f0027f","#bf5b17","#666666"],Set1:["#e41a1c","#377eb8","#4daf4a","#984ea3","#ff7f00","#ffff33","#a65628","#f781bf","#999999"],Set3:["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5","#d9d9d9","#bc80bd","#ccebc5","#ffed6f"],Dark2:["#1b9e77","#d95f02","#7570b3","#e7298a","#66a61e","#e6ab02","#a6761d","#666666"],Paired:["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a","#ffff99","#b15928"],Pastel2:["#b3e2cd","#fdcdac","#cbd5e8","#f4cae4","#e6f5c9","#fff2ae","#f1e2cc","#cccccc"],Pastel1:["#fbb4ae","#b3cde3","#ccebc5","#decbe4","#fed9a6","#ffffcc","#e5d8bd","#fddaec","#f2f2f2"]};root=typeof exports!=="undefined"&&exports!==null?exports:this;chroma=(_ref5=root.chroma)!=null?_ref5:root.chroma={};chroma.colors=colors={indigo:"#4b0082",gold:"#ffd700",hotpink:"#ff69b4",firebrick:"#b22222",indianred:"#cd5c5c",yellow:"#ffff00",mistyrose:"#ffe4e1",darkolivegreen:"#556b2f",olive:"#808000",darkseagreen:"#8fbc8f",pink:"#ffc0cb",tomato:"#ff6347",lightcoral:"#f08080",orangered:"#ff4500",navajowhite:"#ffdead",lime:"#00ff00",palegreen:"#98fb98",darkslategrey:"#2f4f4f",greenyellow:"#adff2f",burlywood:"#deb887",seashell:"#fff5ee",mediumspringgreen:"#00fa9a",fuchsia:"#ff00ff",papayawhip:"#ffefd5",blanchedalmond:"#ffebcd",chartreuse:"#7fff00",dimgray:"#696969",black:"#000000",peachpuff:"#ffdab9",springgreen:"#00ff7f",aquamarine:"#7fffd4",white:"#ffffff",orange:"#ffa500",lightsalmon:"#ffa07a",darkslategray:"#2f4f4f",brown:"#a52a2a",ivory:"#fffff0",dodgerblue:"#1e90ff",peru:"#cd853f",lawngreen:"#7cfc00",chocolate:"#d2691e",crimson:"#dc143c",forestgreen:"#228b22",darkgrey:"#a9a9a9",lightseagreen:"#20b2aa",cyan:"#00ffff",mintcream:"#f5fffa",silver:"#c0c0c0",antiquewhite:"#faebd7",mediumorchid:"#ba55d3",skyblue:"#87ceeb",gray:"#808080",darkturquoise:"#00ced1",goldenrod:"#daa520",darkgreen:"#006400",floralwhite:"#fffaf0",darkviolet:"#9400d3",darkgray:"#a9a9a9",moccasin:"#ffe4b5",saddlebrown:"#8b4513",grey:"#808080",darkslateblue:"#483d8b",lightskyblue:"#87cefa",lightpink:"#ffb6c1",mediumvioletred:"#c71585",slategrey:"#708090",red:"#ff0000",deeppink:"#ff1493",limegreen:"#32cd32",darkmagenta:"#8b008b",palegoldenrod:"#eee8aa",plum:"#dda0dd",turquoise:"#40e0d0",lightgrey:"#d3d3d3",lightgoldenrodyellow:"#fafad2",darkgoldenrod:"#b8860b",lavender:"#e6e6fa",maroon:"#800000",yellowgreen:"#9acd32",sandybrown:"#f4a460",thistle:"#d8bfd8",violet:"#ee82ee",navy:"#000080",magenta:"#ff00ff",dimgrey:"#696969",tan:"#d2b48c",rosybrown:"#bc8f8f",olivedrab:"#6b8e23",blue:"#0000ff",lightblue:"#add8e6",ghostwhite:"#f8f8ff",honeydew:"#f0fff0",cornflowerblue:"#6495ed",slateblue:"#6a5acd",linen:"#faf0e6",darkblue:"#00008b",powderblue:"#b0e0e6",seagreen:"#2e8b57",darkkhaki:"#bdb76b",snow:"#fffafa",sienna:"#a0522d",mediumblue:"#0000cd",royalblue:"#4169e1",lightcyan:"#e0ffff",green:"#008000",mediumpurple:"#9370db",midnightblue:"#191970",cornsilk:"#fff8dc",paleturquoise:"#afeeee",bisque:"#ffe4c4",slategray:"#708090",darkcyan:"#008b8b",khaki:"#f0e68c",wheat:"#f5deb3",teal:"#008080",darkorchid:"#9932cc",deepskyblue:"#00bfff",salmon:"#fa8072",darkred:"#8b0000",steelblue:"#4682b4",palevioletred:"#db7093",lightslategray:"#778899",aliceblue:"#f0f8ff",lightslategrey:"#778899",lightgreen:"#90ee90",orchid:"#da70d6",gainsboro:"#dcdcdc",mediumseagreen:"#3cb371",lightgray:"#d3d3d3",mediumturquoise:"#48d1cc",lemonchiffon:"#fffacd",cadetblue:"#5f9ea0",lightyellow:"#ffffe0",lavenderblush:"#fff0f5",coral:"#ff7f50",purple:"#800080",aqua:"#00ffff",whitesmoke:"#f5f5f5",mediumslateblue:"#7b68ee",darkorange:"#ff8c00",mediumaquamarine:"#66cdaa",darksalmon:"#e9967a",beige:"#f5f5dc",blueviolet:"#8a2be2",azure:"#f0ffff",lightsteelblue:"#b0c4de",oldlace:"#fdf5e6"}}).call(this); \ No newline at end of file
+ chroma.js - color manipulation in javascript
+
+ Copyright (c) 2011-2013, Gregor Aisch
+ 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.
+
+ * The name Gregor Aisch may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ 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 GREGOR AISCH 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.
+
+ @source: https://github.com/gka/chroma.js
+ */
+(function () {
+ var Color, ColorScale, K, PITHIRD, TWOPI, X, Y, Z, brewer, chroma, colors, cos, hex2rgb, hsi2rgb, hsl2rgb, hsv2rgb, lab2lch, lab2rgb, lab_xyz, lch2lab, lch2rgb, limit, rgb2hex, rgb2hsi, rgb2hsl, rgb2hsv, rgb2lab, rgb2lch, rgb_xyz, root, type, unpack, xyz_lab, xyz_rgb, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
+ root = typeof exports !== "undefined" && exports !== null ? exports : this;
+ chroma = (_ref = root.chroma) != null ? _ref : root.chroma = {};
+ if (typeof module !== "undefined" && module !== null) {module.exports = chroma}
+ Color = function () {
+ function Color(x, y, z, m) {
+ var me, _ref1;
+ me = this;
+ if (!(x != null) && !(y != null) && !(z != null) && !(m != null)) {x = [255, 0, 255]}
+ if (type(x) === "array" && x.length === 3) {
+ if (m == null) {m = y}
+ _ref1 = x, x = _ref1[0], y = _ref1[1], z = _ref1[2]
+ }
+ if (type(x) === "string") {m = "hex"} else {if (m == null) {m = "rgb"}}
+ if (m === "rgb") {me._rgb = [x, y, z]} else if (m === "hsl") {me._rgb = hsl2rgb(x, y, z)} else if (m === "hsv") {me._rgb = hsv2rgb(x, y, z)} else if (m === "hex") {me._rgb = hex2rgb(x)} else if (m === "lab") {me._rgb = lab2rgb(x, y, z)} else if (m === "lch") {me._rgb = lch2rgb(x, y, z)} else if (m === "hsi") {me._rgb = hsi2rgb(x, y, z)}
+ }
+
+ Color.prototype.rgb = function () {return this._rgb};
+ Color.prototype.hex = function () {return rgb2hex(this._rgb)};
+ Color.prototype.toString = function () {return this.hex()};
+ Color.prototype.hsl = function () {return rgb2hsl(this._rgb)};
+ Color.prototype.hsv = function () {return rgb2hsv(this._rgb)};
+ Color.prototype.lab = function () {return rgb2lab(this._rgb)};
+ Color.prototype.lch = function () {return rgb2lch(this._rgb)};
+ Color.prototype.hsi = function () {return rgb2hsi(this._rgb)};
+ Color.prototype.interpolate = function (f, col, m) {
+ var dh, hue, hue0, hue1, lbv, lbv0, lbv1, me, sat, sat0, sat1, xyz0, xyz1;
+ me = this;
+ if (m == null) {m = "rgb"}
+ if (type(col) === "string") {col = new Color(col)}
+ if (m === "hsl" || m === "hsv" || m === "lch" || m === "hsi") {
+ if (m === "hsl") {
+ xyz0 = me.hsl();
+ xyz1 = col.hsl()
+ } else if (m === "hsv") {
+ xyz0 = me.hsv();
+ xyz1 = col.hsv()
+ } else if (m === "hsi") {
+ xyz0 = me.hsi();
+ xyz1 = col.hsi()
+ } else if (m === "lch") {
+ xyz0 = me.lch();
+ xyz1 = col.lch()
+ }
+ if (m.substr(0, 1) === "h") {
+ hue0 = xyz0[0], sat0 = xyz0[1], lbv0 = xyz0[2];
+ hue1 = xyz1[0], sat1 = xyz1[1], lbv1 = xyz1[2]
+ } else {
+ lbv0 = xyz0[0], sat0 = xyz0[1], hue0 = xyz0[2];
+ lbv1 = xyz1[0], sat1 = xyz1[1], hue1 = xyz1[2]
+ }
+ if (!isNaN(hue0) && !isNaN(hue1)) {
+ if (hue1 > hue0 && hue1 - hue0 > 180) {dh = hue1 - (hue0 + 360)} else if (hue1 < hue0 && hue0 - hue1 > 180) {dh = hue1 + 360 - hue0} else {dh = hue1 - hue0}
+ hue = hue0 + f * dh
+ } else if (!isNaN(hue0)) {
+ hue = hue0;
+ if (lbv1 === 1 || lbv1 === 0) {sat = sat0}
+ } else if (!isNaN(hue1)) {
+ hue = hue1;
+ if (lbv0 === 1 || lbv0 === 0) {sat = sat1}
+ } else {hue = void 0}
+ if (sat == null) {sat = sat0 + f * (sat1 - sat0)}
+ lbv = lbv0 + f * (lbv1 - lbv0);
+ if (m.substr(0, 1) === "h") {return new Color(hue, sat, lbv, m)} else {return new Color(lbv, sat, hue, m)}
+ } else if (m === "rgb") {
+ xyz0 = me._rgb;
+ xyz1 = col._rgb;
+ return new Color(xyz0[0] + f * (xyz1[0] - xyz0[0]), xyz0[1] + f * (xyz1[1] - xyz0[1]), xyz0[2] + f * (xyz1[2] - xyz0[2]), m)
+ } else if (m === "lab") {
+ xyz0 = me.lab();
+ xyz1 = col.lab();
+ return new Color(xyz0[0] + f * (xyz1[0] - xyz0[0]), xyz0[1] + f * (xyz1[1] - xyz0[1]), xyz0[2] + f * (xyz1[2] - xyz0[2]), m)
+ } else {throw"color mode " + m + " is not supported"}
+ };
+ Color.prototype.darken = function (amount) {
+ var lch, me;
+ if (amount == null) {amount = .2}
+ me = this;
+ lch = me.lch();
+ lch[2] -= amount;
+ return chroma.lch(lch)
+ };
+ return Color
+ }();
+ hex2rgb = function (hex) {
+ var b, g, r, u;
+ if (!hex.match(/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)) {if (chroma.colors != null && chroma.colors[hex]) {hex = chroma.colors[hex]} else {throw"unknown color format: " + hex}}
+ if (hex.length === 4 || hex.length === 7) {hex = hex.substr(1)}
+ if (hex.length === 3) {hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]}
+ u = parseInt(hex, 16);
+ r = u >> 16;
+ g = u >> 8 & 255;
+ b = u & 255;
+ return[r, g, b]
+ };
+ rgb2hex = function () {
+ var b, g, r, str, u, _ref1;
+ _ref1 = unpack(arguments), r = _ref1[0], g = _ref1[1], b = _ref1[2];
+ u = r << 16 | g << 8 | b;
+ str = "000000" + u.toString(16).toUpperCase();
+ return"#" + str.substr(str.length - 6)
+ };
+ hsv2rgb = function () {
+ var b, f, g, h, i, p, q, r, s, t, v, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7;
+ _ref1 = unpack(arguments), h = _ref1[0], s = _ref1[1], v = _ref1[2];
+ v *= 255;
+ if (s === 0) {r = g = b = v} else {
+ if (h === 360) {h = 0}
+ if (h > 360) {h -= 360}
+ if (h < 0) {h += 360}
+ h /= 60;
+ i = Math.floor(h);
+ f = h - i;
+ p = v * (1 - s);
+ q = v * (1 - s * f);
+ t = v * (1 - s * (1 - f));
+ switch (i) {
+ case 0:
+ _ref2 = [v, t, p], r = _ref2[0], g = _ref2[1], b = _ref2[2];
+ break;
+ case 1:
+ _ref3 = [q, v, p], r = _ref3[0], g = _ref3[1], b = _ref3[2];
+ break;
+ case 2:
+ _ref4 = [p, v, t], r = _ref4[0], g = _ref4[1], b = _ref4[2];
+ break;
+ case 3:
+ _ref5 = [p, q, v], r = _ref5[0], g = _ref5[1], b = _ref5[2];
+ break;
+ case 4:
+ _ref6 = [t, p, v], r = _ref6[0], g = _ref6[1], b = _ref6[2];
+ break;
+ case 5:
+ _ref7 = [v, p, q], r = _ref7[0], g = _ref7[1], b = _ref7[2]
+ }
+ }
+ r = Math.round(r);
+ g = Math.round(g);
+ b = Math.round(b);
+ return[r, g, b]
+ };
+ rgb2hsv = function () {
+ var b, delta, g, h, max, min, r, s, v, _ref1;
+ _ref1 = unpack(arguments), r = _ref1[0], g = _ref1[1], b = _ref1[2];
+ min = Math.min(r, g, b);
+ max = Math.max(r, g, b);
+ delta = max - min;
+ v = max / 255;
+ if (max === 0) {
+ h = void 0;
+ s = 0
+ } else {
+ s = delta / max;
+ if (r === max) {h = (g - b) / delta}
+ if (g === max) {h = 2 + (b - r) / delta}
+ if (b === max) {h = 4 + (r - g) / delta}
+ h *= 60;
+ if (h < 0) {h += 360}
+ }
+ return[h, s, v]
+ };
+ hsl2rgb = function () {
+ var b, c, g, h, i, l, r, s, t1, t2, t3, _i, _ref1, _ref2;
+ _ref1 = unpack(arguments), h = _ref1[0], s = _ref1[1], l = _ref1[2];
+ if (s === 0) {r = g = b = l * 255} else {
+ t3 = [0, 0, 0];
+ c = [0, 0, 0];
+ t2 = l < .5 ? l * (1 + s) : l + s - l * s;
+ t1 = 2 * l - t2;
+ h /= 360;
+ t3[0] = h + 1 / 3;
+ t3[1] = h;
+ t3[2] = h - 1 / 3;
+ for (i = _i = 0; _i <= 2; i = ++_i) {
+ if (t3[i] < 0) {t3[i] += 1}
+ if (t3[i] > 1) {t3[i] -= 1}
+ if (6 * t3[i] < 1) {c[i] = t1 + (t2 - t1) * 6 * t3[i]} else if (2 * t3[i] < 1) {c[i] = t2} else if (3 * t3[i] < 2) {c[i] = t1 + (t2 - t1) * (2 / 3 - t3[i]) * 6} else {c[i] = t1}
+ }
+ _ref2 = [Math.round(c[0] * 255), Math.round(c[1] * 255), Math.round(c[2] * 255)], r = _ref2[0], g = _ref2[1], b = _ref2[2]
+ }
+ return[r, g, b]
+ };
+ rgb2hsl = function (r, g, b) {
+ var h, l, max, min, s, _ref1;
+ if (r !== void 0 && r.length === 3) {_ref1 = r, r = _ref1[0], g = _ref1[1], b = _ref1[2]}
+ r /= 255;
+ g /= 255;
+ b /= 255;
+ min = Math.min(r, g, b);
+ max = Math.max(r, g, b);
+ l = (max + min) / 2;
+ if (max === min) {
+ s = 0;
+ h = void 0
+ } else {s = l < .5 ? (max - min) / (max + min) : (max - min) / (2 - max - min)}
+ if (r === max) {h = (g - b) / (max - min)} else if (g === max) {h = 2 + (b - r) / (max - min)} else if (b === max) {h = 4 + (r - g) / (max - min)}
+ h *= 60;
+ if (h < 0) {h += 360}
+ return[h, s, l]
+ };
+ K = 18;
+ X = .95047;
+ Y = 1;
+ Z = 1.08883;
+ lab2rgb = function (l, a, b) {
+ var g, r, x, y, z, _ref1, _ref2;
+ if (l !== void 0 && l.length === 3) {_ref1 = l, l = _ref1[0], a = _ref1[1], b = _ref1[2]}
+ if (l !== void 0 && l.length === 3) {_ref2 = l, l = _ref2[0], a = _ref2[1], b = _ref2[2]}
+ y = (l + 16) / 116;
+ x = y + a / 500;
+ z = y - b / 200;
+ x = lab_xyz(x) * X;
+ y = lab_xyz(y) * Y;
+ z = lab_xyz(z) * Z;
+ r = xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z);
+ g = xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z);
+ b = xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z);
+ return[limit(r, 0, 255), limit(g, 0, 255), limit(b, 0, 255)]
+ };
+ rgb2lab = function () {
+ var b, g, r, x, y, z, _ref1;
+ _ref1 = unpack(arguments), r = _ref1[0], g = _ref1[1], b = _ref1[2];
+ r = rgb_xyz(r);
+ g = rgb_xyz(g);
+ b = rgb_xyz(b);
+ x = xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / X);
+ y = xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / Y);
+ z = xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / Z);
+ return[116 * y - 16, 500 * (x - y), 200 * (y - z)]
+ };
+ lch2lab = function () {
+ var c, h, l, _ref1;
+ _ref1 = unpack(arguments), l = _ref1[0], c = _ref1[1], h = _ref1[2];
+ h = h * Math.PI / 180;
+ return[l, Math.cos(h) * c, Math.sin(h) * c]
+ };
+ lch2rgb = function (l, c, h) {
+ var L, a, b, g, r, _ref1, _ref2;
+ _ref1 = lch2lab(l, c, h), L = _ref1[0], a = _ref1[1], b = _ref1[2];
+ _ref2 = lab2rgb(L, a, b), r = _ref2[0], g = _ref2[1], b = _ref2[2];
+ return[limit(r, 0, 255), limit(g, 0, 255), limit(b, 0, 255)]
+ };
+ lab_xyz = function (x) {if (x > .206893034) {return x * x * x} else {return(x - 4 / 29) / 7.787037}};
+ xyz_lab = function (x) {if (x > .008856) {return Math.pow(x, 1 / 3)} else {return 7.787037 * x + 4 / 29}};
+ xyz_rgb = function (r) {return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055))};
+ rgb_xyz = function (r) {if ((r /= 255) <= .04045) {return r / 12.92} else {return Math.pow((r + .055) / 1.055, 2.4)}};
+ lab2lch = function () {
+ var a, b, c, h, l, _ref1;
+ _ref1 = unpack(arguments), l = _ref1[0], a = _ref1[1], b = _ref1[2];
+ c = Math.sqrt(a * a + b * b);
+ h = Math.atan2(b, a) / Math.PI * 180;
+ return[l, c, h]
+ };
+ rgb2lch = function () {
+ var a, b, g, l, r, _ref1, _ref2;
+ _ref1 = unpack(arguments), r = _ref1[0], g = _ref1[1], b = _ref1[2];
+ _ref2 = rgb2lab(r, g, b), l = _ref2[0], a = _ref2[1], b = _ref2[2];
+ return lab2lch(l, a, b)
+ };
+ rgb2hsi = function () {
+ var TWOPI, b, g, h, i, min, r, s, _ref1;
+ _ref1 = unpack(arguments), r = _ref1[0], g = _ref1[1], b = _ref1[2];
+ TWOPI = Math.PI * 2;
+ r /= 255;
+ g /= 255;
+ b /= 255;
+ min = Math.min(r, g, b);
+ i = (r + g + b) / 3;
+ s = 1 - min / i;
+ if (s === 0) {h = 0} else {
+ h = (r - g + (r - b)) / 2;
+ h /= Math.sqrt((r - g) * (r - g) + (r - b) * (g - b));
+ h = Math.acos(h);
+ if (b > g) {h = TWOPI - h}
+ h /= TWOPI
+ }
+ return[h * 360, s, i]
+ };
+ hsi2rgb = function (h, s, i) {
+ var b, g, r, _ref1;
+ _ref1 = unpack(arguments), h = _ref1[0], s = _ref1[1], i = _ref1[2];
+ h /= 360;
+ if (h < 1 / 3) {
+ b = (1 - s) / 3;
+ r = (1 + s * cos(TWOPI * h) / cos(PITHIRD - TWOPI * h)) / 3;
+ g = 1 - (b + r)
+ } else if (h < 2 / 3) {
+ h -= 1 / 3;
+ r = (1 - s) / 3;
+ g = (1 + s * cos(TWOPI * h) / cos(PITHIRD - TWOPI * h)) / 3;
+ b = 1 - (r + g)
+ } else {
+ h -= 2 / 3;
+ g = (1 - s) / 3;
+ b = (1 + s * cos(TWOPI * h) / cos(PITHIRD - TWOPI * h)) / 3;
+ r = 1 - (g + b)
+ }
+ r = limit(i * r * 3);
+ g = limit(i * g * 3);
+ b = limit(i * b * 3);
+ return[r * 255, g * 255, b * 255]
+ };
+ chroma.Color = Color;
+ chroma.color = function (x, y, z, m) {return new Color(x, y, z, m)};
+ chroma.hsl = function (h, s, l) {return new Color(h, s, l, "hsl")};
+ chroma.hsv = function (h, s, v) {return new Color(h, s, v, "hsv")};
+ chroma.rgb = function (r, g, b) {return new Color(r, g, b, "rgb")};
+ chroma.hex = function (x) {return new Color(x)};
+ chroma.lab = function (l, a, b) {return new Color(l, a, b, "lab")};
+ chroma.lch = function (l, c, h) {return new Color(l, c, h, "lch")};
+ chroma.hsi = function (h, s, i) {return new Color(h, s, i, "hsi")};
+ chroma.interpolate = function (a, b, f, m) {
+ if (!(a != null) || !(b != null)) {return"#000"}
+ if (type(a) === "string") {a = new Color(a)}
+ if (type(b) === "string") {b = new Color(b)}
+ return a.interpolate(f, b, m)
+ };
+ root = typeof exports !== "undefined" && exports !== null ? exports : this;
+ chroma = (_ref1 = root.chroma) != null ? _ref1 : root.chroma = {};
+ Color = chroma.Color;
+ ColorScale = function () {
+ function ColorScale(opts) {
+ var me, _ref2, _ref3;
+ if (opts == null) {opts = {}}
+ me = this;
+ me.range(opts.colors, opts.positions);
+ me._mode = (_ref2 = opts.mode) != null ? _ref2 : "rgb";
+ me._nacol = chroma.hex((_ref3 = opts.nacol) != null ? _ref3 : chroma.hex("#ccc"));
+ me.domain([0, 1]);
+ me
+ }
+
+ ColorScale.prototype.range = function (colors, positions) {
+ var c, col, me, _i, _j, _ref2, _ref3, _ref4;
+ me = this;
+ if (!(colors != null)) {colors = ["#ddd", "#222"]}
+ if (colors != null && type(colors) === "string" && ((_ref2 = chroma.brewer) != null ? _ref2[colors] : void 0) != null) {colors = chroma.brewer[colors].slice(0)}
+ for (c = _i = 0, _ref3 = colors.length - 1; 0 <= _ref3 ? _i <= _ref3 : _i >= _ref3; c = 0 <= _ref3 ? ++_i : --_i) {
+ col = colors[c];
+ if (type(col) === "string") {colors[c] = new Color(col)}
+ }
+ me._colors = colors;
+ if (positions != null) {me._pos = positions} else {
+ me._pos = [];
+ for (c = _j = 0, _ref4 = colors.length - 1; 0 <= _ref4 ? _j <= _ref4 : _j >= _ref4; c = 0 <= _ref4 ? ++_j : --_j) {me._pos.push(c / (colors.length - 1))}
+ }
+ return me
+ };
+ ColorScale.prototype.domain = function (domain) {
+ var me;
+ if (domain == null) {domain = []}
+ me = this;
+ me._domain = domain;
+ me._min = domain[0];
+ me._max = domain[domain.length - 1];
+ if (domain.length === 2) {me._numClasses = 0} else {me._numClasses = domain.length - 1}
+ return me
+ };
+ ColorScale.prototype.get = function (value) {
+ var c, f, f0, me;
+ me = this;
+ if (isNaN(value)) {return me._nacol}
+ if (me._domain.length > 2) {
+ c = me.getClass(value);
+ f = c / (me._numClasses - 1)
+ } else {
+ f = f0 = (value - me._min) / (me._max - me._min);
+ f = Math.min(1, Math.max(0, f))
+ }
+ return me.fColor(f)
+ };
+ ColorScale.prototype.fColor = function (f) {
+ var col, cols, i, me, p, _i, _ref2;
+ me = this;
+ cols = me._colors;
+ for (i = _i = 0, _ref2 = me._pos.length - 1; 0 <= _ref2 ? _i <= _ref2 : _i >= _ref2; i = 0 <= _ref2 ? ++_i : --_i) {
+ p = me._pos[i];
+ if (f <= p) {
+ col = cols[i];
+ break
+ }
+ if (f >= p && i === me._pos.length - 1) {
+ col = cols[i];
+ break
+ }
+ if (f > p && f < me._pos[i + 1]) {
+ f = (f - p) / (me._pos[i + 1] - p);
+ col = chroma.interpolate(cols[i], cols[i + 1], f, me._mode);
+ break
+ }
+ }
+ return col
+ };
+ ColorScale.prototype.classifyValue = function (value) {
+ var domain, i, maxc, me, minc, n, val;
+ me = this;
+ domain = me._domain;
+ val = value;
+ if (domain.length > 2) {
+ n = domain.length - 1;
+ i = me.getClass(value);
+ val = domain[i] + (domain[i + 1] - domain[i]) * .5;
+ minc = domain[0];
+ maxc = domain[n - 1];
+ val = me._min + (val - minc) / (maxc - minc) * (me._max - me._min)
+ }
+ return val
+ };
+ ColorScale.prototype.getClass = function (value) {
+ var domain, i, n, self;
+ self = this;
+ domain = self._domain;
+ if (domain != null) {
+ n = domain.length - 1;
+ i = 0;
+ while (i < n && value >= domain[i]) {i++}
+ return i - 1
+ }
+ return 0
+ };
+ ColorScale.prototype.validValue = function (value) {return!isNaN(value)};
+ return ColorScale
+ }();
+ chroma.ColorScale = ColorScale;
+ chroma.scale = function (colors, positions) {
+ var colscale, f, out;
+ colscale = new chroma.ColorScale;
+ colscale.range(colors, positions);
+ out = false;
+ f = function (v) {
+ var c;
+ c = colscale.get(v);
+ if (out && c[out]) {return c[out]()} else {return c}
+ };
+ f.domain = function (domain, classes, mode) {
+ var d;
+ if (mode == null) {mode = "e"}
+ if (classes != null) {
+ d = chroma.analyze(domain);
+ if (classes === 0) {domain = [d.min, d.max]} else {domain = chroma.limits(d, mode, classes)}
+ }
+ colscale.domain(domain);
+ return f
+ };
+ f.mode = function (_m) {
+ colscale._mode = _m;
+ return f
+ };
+ f.range = function (_colors, _pos) {
+ colscale.range(_colors, _pos);
+ return f
+ };
+ f.out = function (_o) {
+ out = _o;
+ return f
+ };
+ return f
+ };
+ if ((_ref2 = chroma.scales) == null) {chroma.scales = {}}
+ chroma.scales.cool = function () {return chroma.scale([chroma.hsl(180, 1, .9), chroma.hsl(250, .7, .4)])};
+ chroma.scales.hot = function () {return chroma.scale(["#000", "#f00", "#ff0", "#fff"], [0, .25, .75, 1]).mode("rgb")};
+ chroma.analyze = function (data, key, filter) {
+ var add, k, r, val, visit, _i, _len;
+ r = {min: Number.MAX_VALUE, max: Number.MAX_VALUE * -1, sum: 0, values: [], count: 0};
+ if (!(filter != null)) {filter = function () {return true}}
+ add = function (val) {
+ if (val != null && !isNaN(val)) {
+ r.values.push(val);
+ r.sum += val;
+ if (val < r.min) {r.min = val}
+ if (val > r.max) {r.max = val}
+ r.count += 1
+ }
+ };
+ visit = function (val, k) {if (filter(val, k)) {if (key != null && type(key) === "function") {return add(key(val))} else if (key != null && type(key) === "str" || type(key) === "number") {return add(val[key])} else {return add(val)}}};
+ if (type(data) === "array") {
+ for (_i = 0, _len = data.length; _i < _len; _i++) {
+ val = data[_i];
+ visit(val)
+ }
+ } else {
+ for (k in data) {
+ val = data[k];
+ visit(val, k)
+ }
+ }
+ r.domain = [r.min, r.max];
+ r.limits = function (mode, num) {return chroma.limits(r, mode, num)};
+ return r
+ };
+ chroma.limits = function (data, mode, num) {
+ var assignments, best, centroids, cluster, clusterSizes, dist, i, j, kClusters, limits, max, min, mindist, n, nb_iters, newCentroids, p, pb, pr, repeat, sum, tmpKMeansBreaks, value, values, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _s, _t, _u, _v;
+ if (mode == null) {mode = "equal"}
+ if (num == null) {num = 7}
+ if (!(data.values != null)) {data = chroma.analyze(data)}
+ min = data.min;
+ max = data.max;
+ sum = data.sum;
+ values = data.values.sort(function (a, b) {return a - b});
+ limits = [];
+ if (mode.substr(0, 1) === "c") {
+ limits.push(min);
+ limits.push(max)
+ }
+ if (mode.substr(0, 1) === "e") {
+ limits.push(min);
+ for (i = _i = 1, _ref3 = num - 1; 1 <= _ref3 ? _i <= _ref3 : _i >= _ref3; i = 1 <= _ref3 ? ++_i : --_i) {limits.push(min + i / num * (max - min))}
+ limits.push(max)
+ } else if (mode.substr(0, 1) === "q") {
+ limits.push(min);
+ for (i = _j = 1, _ref4 = num - 1; 1 <= _ref4 ? _j <= _ref4 : _j >= _ref4; i = 1 <= _ref4 ? ++_j : --_j) {
+ p = values.length * i / num;
+ pb = Math.floor(p);
+ if (pb === p) {limits.push(values[pb])} else {
+ pr = p - pb;
+ limits.push(values[pb] * pr + values[pb + 1] * (1 - pr))
+ }
+ }
+ limits.push(max)
+ } else if (mode.substr(0, 1) === "k") {
+ n = values.length;
+ assignments = new Array(n);
+ clusterSizes = new Array(num);
+ repeat = true;
+ nb_iters = 0;
+ centroids = null;
+ centroids = [];
+ centroids.push(min);
+ for (i = _k = 1, _ref5 = num - 1; 1 <= _ref5 ? _k <= _ref5 : _k >= _ref5; i = 1 <= _ref5 ? ++_k : --_k) {centroids.push(min + i / num * (max - min))}
+ centroids.push(max);
+ while (repeat) {
+ for (j = _l = 0, _ref6 = num - 1; 0 <= _ref6 ? _l <= _ref6 : _l >= _ref6; j = 0 <= _ref6 ? ++_l : --_l) {clusterSizes[j] = 0}
+ for (i = _m = 0, _ref7 = n - 1; 0 <= _ref7 ? _m <= _ref7 : _m >= _ref7; i = 0 <= _ref7 ? ++_m : --_m) {
+ value = values[i];
+ mindist = Number.MAX_VALUE;
+ for (j = _n = 0, _ref8 = num - 1; 0 <= _ref8 ? _n <= _ref8 : _n >= _ref8; j = 0 <= _ref8 ? ++_n : --_n) {
+ dist = Math.abs(centroids[j] - value);
+ if (dist < mindist) {
+ mindist = dist;
+ best = j
+ }
+ }
+ clusterSizes[best]++;
+ assignments[i] = best
+ }
+ newCentroids = new Array(num);
+ for (j = _o = 0, _ref9 = num - 1; 0 <= _ref9 ? _o <= _ref9 : _o >= _ref9; j = 0 <= _ref9 ? ++_o : --_o) {newCentroids[j] = null}
+ for (i = _p = 0, _ref10 = n - 1; 0 <= _ref10 ? _p <= _ref10 : _p >= _ref10; i = 0 <= _ref10 ? ++_p : --_p) {
+ cluster = assignments[i];
+ if (newCentroids[cluster] === null) {newCentroids[cluster] = values[i]} else {newCentroids[cluster] += values[i]}
+ }
+ for (j = _q = 0, _ref11 = num - 1; 0 <= _ref11 ? _q <= _ref11 : _q >= _ref11; j = 0 <= _ref11 ? ++_q : --_q) {newCentroids[j] *= 1 / clusterSizes[j]}
+ repeat = false;
+ for (j = _r = 0, _ref12 = num - 1; 0 <= _ref12 ? _r <= _ref12 : _r >= _ref12; j = 0 <= _ref12 ? ++_r : --_r) {
+ if (newCentroids[j] !== centroids[i]) {
+ repeat = true;
+ break
+ }
+ }
+ centroids = newCentroids;
+ nb_iters++;
+ if (nb_iters > 200) {repeat = false}
+ }
+ kClusters = {};
+ for (j = _s = 0, _ref13 = num - 1; 0 <= _ref13 ? _s <= _ref13 : _s >= _ref13; j = 0 <= _ref13 ? ++_s : --_s) {kClusters[j] = []}
+ for (i = _t = 0, _ref14 = n - 1; 0 <= _ref14 ? _t <= _ref14 : _t >= _ref14; i = 0 <= _ref14 ? ++_t : --_t) {
+ cluster = assignments[i];
+ kClusters[cluster].push(values[i])
+ }
+ tmpKMeansBreaks = [];
+ for (j = _u = 0, _ref15 = num - 1; 0 <= _ref15 ? _u <= _ref15 : _u >= _ref15; j = 0 <= _ref15 ? ++_u : --_u) {
+ tmpKMeansBreaks.push(kClusters[j][0]);
+ tmpKMeansBreaks.push(kClusters[j][kClusters[j].length - 1])
+ }
+ tmpKMeansBreaks = tmpKMeansBreaks.sort(function (a, b) {return a - b});
+ limits.push(tmpKMeansBreaks[0]);
+ for (i = _v = 1, _ref16 = tmpKMeansBreaks.length - 1; _v <= _ref16; i = _v += 2) {if (!isNaN(tmpKMeansBreaks[i])) {limits.push(tmpKMeansBreaks[i])}}
+ }
+ return limits
+ };
+ root = typeof exports !== "undefined" && exports !== null ? exports : this;
+ type = function () {
+ var classToType, name, _i, _len, _ref3;
+ classToType = {};
+ _ref3 = "Boolean Number String Function Array Date RegExp Undefined Null".split(" ");
+ for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
+ name = _ref3[_i];
+ classToType["[object " + name + "]"] = name.toLowerCase()
+ }
+ return function (obj) {
+ var strType;
+ strType = Object.prototype.toString.call(obj);
+ return classToType[strType] || "object"
+ }
+ }();
+ if ((_ref3 = root.type) == null) {root.type = type}
+ Array.max = function (array) {return Math.max.apply(Math, array)};
+ Array.min = function (array) {return Math.min.apply(Math, array)};
+ limit = function (x, min, max) {
+ if (min == null) {min = 0}
+ if (max == null) {max = 1}
+ if (x < min) {x = min}
+ if (x > max) {x = max}
+ return x
+ };
+ unpack = function (args) {if (args.length === 3) {return args} else {return args[0]}};
+ TWOPI = Math.PI * 2;
+ PITHIRD = Math.PI / 3;
+ cos = Math.cos;
+ root = typeof exports !== "undefined" && exports !== null ? exports : this;
+ chroma = (_ref4 = root.chroma) != null ? _ref4 : root.chroma = {};
+ chroma.brewer = brewer = {OrRd: ["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"], PuBu: ["#fff7fb", "#ece7f2", "#d0d1e6", "#a6bddb", "#74a9cf", "#3690c0", "#0570b0", "#045a8d", "#023858"], BuPu: ["#f7fcfd", "#e0ecf4", "#bfd3e6", "#9ebcda", "#8c96c6", "#8c6bb1", "#88419d", "#810f7c", "#4d004b"], Oranges: ["#fff5eb", "#fee6ce", "#fdd0a2", "#fdae6b", "#fd8d3c", "#f16913", "#d94801", "#a63603", "#7f2704"], BuGn: ["#f7fcfd", "#e5f5f9", "#ccece6", "#99d8c9", "#66c2a4", "#41ae76", "#238b45", "#006d2c", "#00441b"], YlOrBr: ["#ffffe5", "#fff7bc", "#fee391", "#fec44f", "#fe9929", "#ec7014", "#cc4c02", "#993404", "#662506"], YlGn: ["#ffffe5", "#f7fcb9", "#d9f0a3", "#addd8e", "#78c679", "#41ab5d", "#238443", "#006837", "#004529"], Reds: ["#fff5f0", "#fee0d2", "#fcbba1", "#fc9272", "#fb6a4a", "#ef3b2c", "#cb181d", "#a50f15", "#67000d"], RdPu: ["#fff7f3", "#fde0dd", "#fcc5c0", "#fa9fb5", "#f768a1", "#dd3497", "#ae017e", "#7a0177", "#49006a"], Greens: ["#f7fcf5", "#e5f5e0", "#c7e9c0", "#a1d99b", "#74c476", "#41ab5d", "#238b45", "#006d2c", "#00441b"], YlGnBu: ["#ffffd9", "#edf8b1", "#c7e9b4", "#7fcdbb", "#41b6c4", "#1d91c0", "#225ea8", "#253494", "#081d58"], Purples: ["#fcfbfd", "#efedf5", "#dadaeb", "#bcbddc", "#9e9ac8", "#807dba", "#6a51a3", "#54278f", "#3f007d"], GnBu: ["#f7fcf0", "#e0f3db", "#ccebc5", "#a8ddb5", "#7bccc4", "#4eb3d3", "#2b8cbe", "#0868ac", "#084081"], Greys: ["#ffffff", "#f0f0f0", "#d9d9d9", "#bdbdbd", "#969696", "#737373", "#525252", "#252525", "#000000"], YlOrRd: ["#ffffcc", "#ffeda0", "#fed976", "#feb24c", "#fd8d3c", "#fc4e2a", "#e31a1c", "#bd0026", "#800026"], PuRd: ["#f7f4f9", "#e7e1ef", "#d4b9da", "#c994c7", "#df65b0", "#e7298a", "#ce1256", "#980043", "#67001f"], Blues: ["#f7fbff", "#deebf7", "#c6dbef", "#9ecae1", "#6baed6", "#4292c6", "#2171b5", "#08519c", "#08306b"], PuBuGn: ["#fff7fb", "#ece2f0", "#d0d1e6", "#a6bddb", "#67a9cf", "#3690c0", "#02818a", "#016c59", "#014636"], Spectral: ["#9e0142", "#d53e4f", "#f46d43", "#fdae61", "#fee08b", "#ffffbf", "#e6f598", "#abdda4", "#66c2a5", "#3288bd", "#5e4fa2"], RdYlGn: ["#a50026", "#d73027", "#f46d43", "#fdae61", "#fee08b", "#ffffbf", "#d9ef8b", "#a6d96a", "#66bd63", "#1a9850", "#006837"], RdBu: ["#67001f", "#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#f7f7f7", "#d1e5f0", "#92c5de", "#4393c3", "#2166ac", "#053061"], PiYG: ["#8e0152", "#c51b7d", "#de77ae", "#f1b6da", "#fde0ef", "#f7f7f7", "#e6f5d0", "#b8e186", "#7fbc41", "#4d9221", "#276419"], PRGn: ["#40004b", "#762a83", "#9970ab", "#c2a5cf", "#e7d4e8", "#f7f7f7", "#d9f0d3", "#a6dba0", "#5aae61", "#1b7837", "#00441b"], RdYlBu: ["#a50026", "#d73027", "#f46d43", "#fdae61", "#fee090", "#ffffbf", "#e0f3f8", "#abd9e9", "#74add1", "#4575b4", "#313695"], BrBG: ["#543005", "#8c510a", "#bf812d", "#dfc27d", "#f6e8c3", "#f5f5f5", "#c7eae5", "#80cdc1", "#35978f", "#01665e", "#003c30"], RdGy: ["#67001f", "#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#ffffff", "#e0e0e0", "#bababa", "#878787", "#4d4d4d", "#1a1a1a"], PuOr: ["#7f3b08", "#b35806", "#e08214", "#fdb863", "#fee0b6", "#f7f7f7", "#d8daeb", "#b2abd2", "#8073ac", "#542788", "#2d004b"], Set2: ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f", "#e5c494", "#b3b3b3"], Accent: ["#7fc97f", "#beaed4", "#fdc086", "#ffff99", "#386cb0", "#f0027f", "#bf5b17", "#666666"], Set1: ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", "#a65628", "#f781bf", "#999999"], Set3: ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", "#fdb462", "#b3de69", "#fccde5", "#d9d9d9", "#bc80bd", "#ccebc5", "#ffed6f"], Dark2: ["#1b9e77", "#d95f02", "#7570b3", "#e7298a", "#66a61e", "#e6ab02", "#a6761d", "#666666"], Paired: ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", "#e31a1c", "#fdbf6f", "#ff7f00", "#cab2d6", "#6a3d9a", "#ffff99", "#b15928"], Pastel2: ["#b3e2cd", "#fdcdac", "#cbd5e8", "#f4cae4", "#e6f5c9", "#fff2ae", "#f1e2cc", "#cccccc"], Pastel1: ["#fbb4ae", "#b3cde3", "#ccebc5", "#decbe4", "#fed9a6", "#ffffcc", "#e5d8bd", "#fddaec", "#f2f2f2"]};
+ root = typeof exports !== "undefined" && exports !== null ? exports : this;
+ chroma = (_ref5 = root.chroma) != null ? _ref5 : root.chroma = {};
+ chroma.colors = colors = {indigo: "#4b0082", gold: "#ffd700", hotpink: "#ff69b4", firebrick: "#b22222", indianred: "#cd5c5c", yellow: "#ffff00", mistyrose: "#ffe4e1", darkolivegreen: "#556b2f", olive: "#808000", darkseagreen: "#8fbc8f", pink: "#ffc0cb", tomato: "#ff6347", lightcoral: "#f08080", orangered: "#ff4500", navajowhite: "#ffdead", lime: "#00ff00", palegreen: "#98fb98", darkslategrey: "#2f4f4f", greenyellow: "#adff2f", burlywood: "#deb887", seashell: "#fff5ee", mediumspringgreen: "#00fa9a", fuchsia: "#ff00ff", papayawhip: "#ffefd5", blanchedalmond: "#ffebcd", chartreuse: "#7fff00", dimgray: "#696969", black: "#000000", peachpuff: "#ffdab9", springgreen: "#00ff7f", aquamarine: "#7fffd4", white: "#ffffff", orange: "#ffa500", lightsalmon: "#ffa07a", darkslategray: "#2f4f4f", brown: "#a52a2a", ivory: "#fffff0", dodgerblue: "#1e90ff", peru: "#cd853f", lawngreen: "#7cfc00", chocolate: "#d2691e", crimson: "#dc143c", forestgreen: "#228b22", darkgrey: "#a9a9a9", lightseagreen: "#20b2aa", cyan: "#00ffff", mintcream: "#f5fffa", silver: "#c0c0c0", antiquewhite: "#faebd7", mediumorchid: "#ba55d3", skyblue: "#87ceeb", gray: "#808080", darkturquoise: "#00ced1", goldenrod: "#daa520", darkgreen: "#006400", floralwhite: "#fffaf0", darkviolet: "#9400d3", darkgray: "#a9a9a9", moccasin: "#ffe4b5", saddlebrown: "#8b4513", grey: "#808080", darkslateblue: "#483d8b", lightskyblue: "#87cefa", lightpink: "#ffb6c1", mediumvioletred: "#c71585", slategrey: "#708090", red: "#ff0000", deeppink: "#ff1493", limegreen: "#32cd32", darkmagenta: "#8b008b", palegoldenrod: "#eee8aa", plum: "#dda0dd", turquoise: "#40e0d0", lightgrey: "#d3d3d3", lightgoldenrodyellow: "#fafad2", darkgoldenrod: "#b8860b", lavender: "#e6e6fa", maroon: "#800000", yellowgreen: "#9acd32", sandybrown: "#f4a460", thistle: "#d8bfd8", violet: "#ee82ee", navy: "#000080", magenta: "#ff00ff", dimgrey: "#696969", tan: "#d2b48c", rosybrown: "#bc8f8f", olivedrab: "#6b8e23", blue: "#0000ff", lightblue: "#add8e6", ghostwhite: "#f8f8ff", honeydew: "#f0fff0", cornflowerblue: "#6495ed", slateblue: "#6a5acd", linen: "#faf0e6", darkblue: "#00008b", powderblue: "#b0e0e6", seagreen: "#2e8b57", darkkhaki: "#bdb76b", snow: "#fffafa", sienna: "#a0522d", mediumblue: "#0000cd", royalblue: "#4169e1", lightcyan: "#e0ffff", green: "#008000", mediumpurple: "#9370db", midnightblue: "#191970", cornsilk: "#fff8dc", paleturquoise: "#afeeee", bisque: "#ffe4c4", slategray: "#708090", darkcyan: "#008b8b", khaki: "#f0e68c", wheat: "#f5deb3", teal: "#008080", darkorchid: "#9932cc", deepskyblue: "#00bfff", salmon: "#fa8072", darkred: "#8b0000", steelblue: "#4682b4", palevioletred: "#db7093", lightslategray: "#778899", aliceblue: "#f0f8ff", lightslategrey: "#778899", lightgreen: "#90ee90", orchid: "#da70d6", gainsboro: "#dcdcdc", mediumseagreen: "#3cb371", lightgray: "#d3d3d3", mediumturquoise: "#48d1cc", lemonchiffon: "#fffacd", cadetblue: "#5f9ea0", lightyellow: "#ffffe0", lavenderblush: "#fff0f5", coral: "#ff7f50", purple: "#800080", aqua: "#00ffff", whitesmoke: "#f5f5f5", mediumslateblue: "#7b68ee", darkorange: "#ff8c00", mediumaquamarine: "#66cdaa", darksalmon: "#e9967a", beige: "#f5f5dc", blueviolet: "#8a2be2", azure: "#f0ffff", lightsteelblue: "#b0c4de", oldlace: "#fdf5e6"}
+}).call(this); \ No newline at end of file
diff --git a/plugins/UserCountryMap/js/vendor/jquery.qtip.min.js b/plugins/UserCountryMap/js/vendor/jquery.qtip.min.js
index 5e251bc39f..1f757df953 100644
--- a/plugins/UserCountryMap/js/vendor/jquery.qtip.min.js
+++ b/plugins/UserCountryMap/js/vendor/jquery.qtip.min.js
@@ -1,14 +1,352 @@
/*
-* qTip2 - Pretty powerful tooltips
-* http://craigsworks.com/projects/qtip2/
-*
-* Version: nightly
-* Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com
-*
-* Dual licensed under MIT or GPLv2 licenses
-* http://en.wikipedia.org/wiki/MIT_License
-* http://en.wikipedia.org/wiki/GNU_General_Public_License
-*
-* Date: Mon Nov 21 13:18:18.0000000000 2011
-*//*jslint browser: true, onevar: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true *//*global window: false, jQuery: false, console: false */
-(function(a,b,c){function z(b,c){var i,j,k,l,m,n=a(this),o=a(document.body),p=this===document?o:n,q=n.metadata?n.metadata(c.metadata):f,r=c.metadata.type==="html5"&&q?q[c.metadata.name]:f,s=n.data(c.metadata.name||"qtipopts");try{s=typeof s==="string"?(new Function("return "+s))():s}catch(t){w("Unable to parse HTML5 attribute data: "+s)}l=a.extend(d,{},g.defaults,c,typeof s==="object"?x(s):f,x(r||q)),j=l.position,l.id=b;if("boolean"===typeof l.content.text){k=n.attr(l.content.attr);if(l.content.attr!==e&&k)l.content.text=k;else{w("Unable to locate content for tooltip! Aborting render of tooltip on element: ",n);return e}}j.container===e&&(j.container=o),j.target===e&&(j.target=p),l.show.target===e&&(l.show.target=p),l.show.solo===d&&(l.show.solo=o),l.hide.target===e&&(l.hide.target=p),l.position.viewport===d&&(l.position.viewport=j.container),j.at=new h.Corner(j.at),j.my=new h.Corner(j.my);if(a.data(this,"qtip"))if(l.overwrite)n.qtip("destroy");else if(l.overwrite===e)return e;l.suppress&&(m=a.attr(this,"title"))&&a(this).removeAttr("title").attr(u,m),i=new y(n,l,b,!!k),a.data(this,"qtip",i),n.bind("remove.qtip-"+b,function(){i.destroy()});return i}function y(s,t,w,y){function R(){var c=[t.show.target[0],t.hide.target[0],z.rendered&&G.tooltip[0],t.position.container[0],t.position.viewport[0],b,document];z.rendered?a([]).pushStack(a.grep(c,function(a){return typeof a==="object"})).unbind(F):t.show.target.unbind(F+"-create")}function Q(){function p(a){E.is(":visible")&&z.reposition(a)}function o(a){if(E.hasClass(m))return e;clearTimeout(z.timers.inactive),z.timers.inactive=setTimeout(function(){z.hide(a)},t.hide.inactive)}function l(b){if(E.hasClass(m)||C||D)return e;var d=a(b.relatedTarget||b.target),g=d.closest(n)[0]===E[0],h=d[0]===f.show[0];clearTimeout(z.timers.show),clearTimeout(z.timers.hide);if(c.target==="mouse"&&g||t.hide.fixed&&(/mouse(out|leave|move)/.test(b.type)&&(g||h)))try{b.preventDefault(),b.stopImmediatePropagation()}catch(i){}else t.hide.delay>0?z.timers.hide=setTimeout(function(){z.hide(b)},t.hide.delay):z.hide(b)}function k(a){if(E.hasClass(m))return e;clearTimeout(z.timers.show),clearTimeout(z.timers.hide);var b=function(){z.toggle(d,a)};t.show.delay>0?z.timers.show=setTimeout(b,t.show.delay):b()}var c=t.position,f={show:t.show.target,hide:t.hide.target,viewport:a(c.viewport),document:a(document),body:a(document.body),window:a(b)},h={show:a.trim(""+t.show.event).split(" "),hide:a.trim(""+t.hide.event).split(" ")},j=a.browser.msie&&parseInt(a.browser.version,10)===6;E.bind("mouseenter"+F+" mouseleave"+F,function(a){var b=a.type==="mouseenter";b&&z.focus(a),E.toggleClass(q,b)}),t.hide.fixed&&(f.hide=f.hide.add(E),E.bind("mouseover"+F,function(){E.hasClass(m)||clearTimeout(z.timers.hide)})),/mouse(out|leave)/i.test(t.hide.event)?t.hide.leave==="window"&&f.window.bind("mouseout"+F+" blur"+F,function(a){/select|option/.test(a.target)&&!a.relatedTarget&&z.hide(a)}):/mouse(over|enter)/i.test(t.show.event)&&f.hide.bind("mouseleave"+F,function(a){clearTimeout(z.timers.show)}),(""+t.hide.event).indexOf("unfocus")>-1&&f.body.bind("mousedown"+F,function(b){var c=a(b.target),d=!E.hasClass(m)&&E.is(":visible");c[0]!==E[0]&&c.parents(n).length===0&&c.add(s).length>1&&!c.attr("disabled")&&z.hide(b)}),"number"===typeof t.hide.inactive&&(f.show.bind("qtip-"+w+"-inactive",o),a.each(g.inactiveEvents,function(a,b){f.hide.add(G.tooltip).bind(b+F+"-inactive",o)})),a.each(h.hide,function(b,c){var d=a.inArray(c,h.show),e=a(f.hide);d>-1&&e.add(f.show).length===e.length||c==="unfocus"?(f.show.bind(c+F,function(a){E.is(":visible")?l(a):k(a)}),delete h.show[d]):f.hide.bind(c+F,l)}),a.each(h.show,function(a,b){f.show.bind(b+F,k)}),"number"===typeof t.hide.distance&&f.show.add(E).bind("mousemove"+F,function(a){var b=H.origin||{},c=t.hide.distance,d=Math.abs;(d(a.pageX-b.pageX)>=c||d(a.pageY-b.pageY)>=c)&&z.hide(a)}),c.target==="mouse"&&(f.show.bind("mousemove"+F,function(a){i={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),c.adjust.mouse&&(t.hide.event&&E.bind("mouseleave"+F,function(a){(a.relatedTarget||a.target)!==f.show[0]&&z.hide(a)}),f.document.bind("mousemove"+F,function(a){!E.hasClass(m)&&E.is(":visible")&&z.reposition(a||i)}))),(c.adjust.resize||f.viewport.length)&&(a.event.special.resize?f.viewport:f.window).bind("resize"+F,p),(f.viewport.length||j&&E.css("position")==="fixed")&&f.viewport.bind("scroll"+F,p)}function P(b,d){function g(b){function i(c){c&&(delete h[c.src],clearTimeout(z.timers.img[c.src]),a(c).unbind(F)),a.isEmptyObject(h)&&(z.redraw(),d!==e&&z.reposition(H.event),b())}var g,h={};if((g=f.find("img:not([height]):not([width])")).length===0)return i();g.each(function(b,d){if(h[d.src]===c){var e=0,f=3;(function g(){if(d.height||d.width||e>f)return i(d);e+=1,z.timers.img[d.src]=setTimeout(g,700)})(),a(d).bind("error"+F+" load"+F,function(){i(this)}),h[d.src]=d}})}var f=G.content;if(!z.rendered||!b)return e;a.isFunction(b)&&(b=b.call(s,H.event,z)||""),b.jquery&&b.length>0?f.empty().append(b.css({display:"block"})):f.html(b),z.rendered<0?E.queue("fx",g):(D=0,g(a.noop));return z}function O(b,c){var d=G.title;if(!z.rendered||!b)return e;a.isFunction(b)&&(b=b.call(s,H.event,z));if(b===e)return K(e);b.jquery&&b.length>0?d.empty().append(b.css({display:"block"})):d.html(b),z.redraw(),c!==e&&z.rendered&&E.is(":visible")&&z.reposition(H.event)}function N(a){var b=G.button,c=G.title;if(!z.rendered)return e;a?(c||M(),L()):b.remove()}function M(){var b=B+"-title";G.titlebar&&K(),G.titlebar=a("<div />",{"class":k+"-titlebar "+(t.style.widget?"ui-widget-header":"")}).append(G.title=a("<div />",{id:b,"class":k+"-title","aria-atomic":d})).insertBefore(G.content).delegate(".ui-tooltip-close","mousedown keydown mouseup keyup mouseout",function(b){a(this).toggleClass("ui-state-active ui-state-focus",b.type.substr(-4)==="down")}).delegate(".ui-tooltip-close","mouseover mouseout",function(b){a(this).toggleClass("ui-state-hover",b.type==="mouseover")}),t.content.title.button?L():z.rendered&&z.redraw()}function L(){var b=t.content.title.button,c=typeof b==="string",d=c?b:"Close tooltip";G.button&&G.button.remove(),b.jquery?G.button=b:G.button=a("<a />",{"class":"ui-state-default ui-tooltip-close "+(t.style.widget?"":k+"-icon"),title:d,"aria-label":d}).prepend(a("<span />",{"class":"ui-icon ui-icon-close",html:"&times;"})),G.button.appendTo(G.titlebar).attr("role","button").click(function(a){E.hasClass(m)||z.hide(a);return e}),z.redraw()}function K(a){G.title&&(G.titlebar.remove(),G.titlebar=G.title=G.button=f,a!==e&&z.reposition())}function J(){var a=t.style.widget;E.toggleClass(l,a).toggleClass(o,!a),G.content.toggleClass(l+"-content",a),G.titlebar&&G.titlebar.toggleClass(l+"-header",a),G.button&&G.button.toggleClass(k+"-icon",!a)}function I(a){var b=0,c,d=t,e=a.split(".");while(d=d[e[b++]])b<e.length&&(c=d);return[c||t,e.pop()]}var z=this,A=document.body,B=k+"-"+w,C=0,D=0,E=a(),F=".qtip-"+w,G,H;z.id=w,z.rendered=e,z.elements=G={target:s},z.timers={img:{}},z.options=t,z.checks={},z.plugins={},z.cache=H={event:{},target:a(),disabled:e,attr:y},z.checks.builtin={"^id$":function(b,c,f){var h=f===d?g.nextid:f,i=k+"-"+h;h!==e&&h.length>0&&!a("#"+i).length&&(E[0].id=i,G.content[0].id=i+"-content",G.title[0].id=i+"-title")},"^content.text$":function(a,b,c){P(c)},"^content.title.text$":function(a,b,c){if(!c)return K();!G.title&&c&&M(),O(c)},"^content.title.button$":function(a,b,c){N(c)},"^position.(my|at)$":function(a,b,c){"string"===typeof c&&(a[b]=new h.Corner(c))},"^position.container$":function(a,b,c){z.rendered&&E.appendTo(c)},"^show.ready$":function(){z.rendered?z.toggle(d):z.render(1)},"^style.classes$":function(a,b,c){E.attr("class",k+" qtip ui-helper-reset "+c)},"^style.widget|content.title":J,"^events.(render|show|move|hide|focus|blur)$":function(b,c,d){E[(a.isFunction(d)?"":"un")+"bind"]("tooltip"+c,d)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){var a=t.position;E.attr("tracking",a.target==="mouse"&&a.adjust.mouse),R(),Q()}},a.extend(z,{render:function(b){if(z.rendered)return z;var c=t.content.text,f=t.content.title.text,g=t.position,i=a.Event("tooltiprender");a.attr(s[0],"aria-describedby",B),E=G.tooltip=a("<div/>",{id:B,"class":k+" qtip ui-helper-reset "+o+" "+t.style.classes+" "+k+"-pos-"+t.position.my.abbrev(),width:t.style.width||"",height:t.style.height||"",tracking:g.target==="mouse"&&g.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":e,"aria-describedby":B+"-content","aria-hidden":d}).toggleClass(m,H.disabled).data("qtip",z).appendTo(t.position.container).append(G.content=a("<div />",{"class":k+"-content",id:B+"-content","aria-atomic":d})),z.rendered=-1,C=D=1,f&&(M(),a.isFunction(f)||O(f,e)),a.isFunction(c)||P(c,e),z.rendered=d,J(),a.each(t.events,function(b,c){a.isFunction(c)&&E.bind(b==="toggle"?"tooltipshow tooltiphide":"tooltip"+b,c)}),a.each(h,function(){this.initialize==="render"&&this(z)}),Q(),E.queue("fx",function(a){i.originalEvent=H.event,E.trigger(i,[z]),C=D=0,z.redraw(),(t.show.ready||b)&&z.toggle(d,H.event),a()});return z},get:function(a){var b,c;switch(a.toLowerCase()){case"dimensions":b={height:E.outerHeight(),width:E.outerWidth()};break;case"offset":b=h.offset(E,t.position.container);break;default:c=I(a.toLowerCase()),b=c[0][c[1]],b=b.precedance?b.string():b}return b},set:function(b,c){function m(a,b){var c,d,e;for(c in k)for(d in k[c])if(e=(new RegExp(d,"i")).exec(a))b.push(e),k[c][d].apply(z,b)}var g=/^position\.(my|at|adjust|target|container)|style|content|show\.ready/i,h=/^content\.(title|attr)|style/i,i=e,j=e,k=z.checks,l;"string"===typeof b?(l=b,b={},b[l]=c):b=a.extend(d,{},b),a.each(b,function(c,d){var e=I(c.toLowerCase()),f;f=e[0][e[1]],e[0][e[1]]="object"===typeof d&&d.nodeType?a(d):d,b[c]=[e[0],e[1],d,f],i=g.test(c)||i,j=h.test(c)||j}),x(t),C=D=1,a.each(b,m),C=D=0,E.is(":visible")&&z.rendered&&(i&&z.reposition(t.position.target==="mouse"?f:H.event),j&&z.redraw());return z},toggle:function(b,c){function q(){b?(a.browser.msie&&E[0].style.removeAttribute("filter"),E.css("overflow",""),"string"===typeof h.autofocus&&a(h.autofocus,E).focus(),p=a.Event("tooltipvisible"),p.originalEvent=c?H.event:f,E.trigger(p,[z]),h.target.trigger("qtip-"+w+"-inactive")):E.css({display:"",visibility:"",opacity:"",left:"",top:""})}if(!z.rendered)return b?z.render(1):z;var g=b?"show":"hide",h=t[g],j=E.is(":visible"),k=!c||t[g].target.length<2||H.target[0]===c.target,l=t.position,m=t.content,o,p;(typeof b).search("boolean|number")&&(b=!j);if(!E.is(":animated")&&j===b&&k)return z;if(c){if(/over|enter/.test(c.type)&&/out|leave/.test(H.event.type)&&c.target===t.show.target[0]&&E.has(c.relatedTarget).length)return z;H.event=a.extend({},c)}p=a.Event("tooltip"+g),p.originalEvent=c?H.event:f,E.trigger(p,[z,90]);if(p.isDefaultPrevented())return z;a.attr(E[0],"aria-hidden",!b),b?(H.origin=a.extend({},i),z.focus(c),a.isFunction(m.text)&&P(m.text,e),a.isFunction(m.title.text)&&O(m.title.text,e),!v&&l.target==="mouse"&&l.adjust.mouse&&(a(document).bind("mousemove.qtip",function(a){i={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),v=d),z.reposition(c),(p.solo=!!h.solo)&&a(n,h.solo).not(E).qtip("hide",p)):(clearTimeout(z.timers.show),delete H.origin,v&&!a(n+'[tracking="true"]:visible',h.solo).not(E).length&&(a(document).unbind("mousemove.qtip"),v=e),z.blur(c)),k&&E.stop(0,1),h.effect===e?(E[g](),q.call(E)):a.isFunction(h.effect)?(h.effect.call(E,z),E.queue("fx",function(a){q(),a()})):E.fadeTo(90,b?1:0,q),b&&h.target.trigger("qtip-"+w+"-inactive");return z},show:function(a){return z.toggle(d,a)},hide:function(a){return z.toggle(e,a)},focus:function(b){if(!z.rendered)return z;var c=a(n),d=parseInt(E[0].style.zIndex,10),e=g.zindex+c.length,f=a.extend({},b),h,i;E.hasClass(p)||(i=a.Event("tooltipfocus"),i.originalEvent=f,E.trigger(i,[z,e]),i.isDefaultPrevented()||(d!==e&&(c.each(function(){this.style.zIndex>d&&(this.style.zIndex=this.style.zIndex-1)}),c.filter("."+p).qtip("blur",f)),E.addClass(p)[0].style.zIndex=e));return z},blur:function(b){var c=a.extend({},b),d;E.removeClass(p),d=a.Event("tooltipblur"),d.originalEvent=c,E.trigger(d,[z]);return z},reposition:function(c,d){if(!z.rendered||C)return z;C=1;var f=t.position.target,g=t.position,j=g.my,l=g.at,m=g.adjust,n=m.method.split(" "),o=E.outerWidth(),p=E.outerHeight(),q=0,r=0,s=a.Event("tooltipmove"),u=E.css("position")==="fixed",v=g.viewport,w={left:0,top:0},x=g.container,y=e,B=z.plugins.tip,D={horizontal:n[0],vertical:n[1]=n[1]||n[0],enabled:v.jquery&&f[0]!==b&&f[0]!==A&&m.method!=="none",left:function(a){var b=D.horizontal==="shift",c=-x.offset.left+v.offset.left+v.scrollLeft,d=j.x==="left"?o:j.x==="right"?-o:-o/2,e=l.x==="left"?q:l.x==="right"?-q:-q/2,f=B&&B.size?B.size.width||0:0,g=B&&B.corner&&B.corner.precedance==="x"&&!b?f:0,h=c-a+g,i=a+o-v.width-c+g,k=d-(j.precedance==="x"||j.x===j.y?e:0),n=j.x==="center";b?(g=B&&B.corner&&B.corner.precedance==="y"?f:0,k=(j.x==="left"?1:-1)*d-g,w.left+=h>0?h:i>0?-i:0,w.left=Math.max(-x.offset.left+v.offset.left+(g&&B.corner.x==="center"?B.offset:0),a-k,Math.min(Math.max(-x.offset.left+v.offset.left+v.width,a+k),w.left))):(h>0&&(j.x!=="left"||i>0)?w.left-=k:i>0&&(j.x!=="right"||h>0)&&(w.left-=n?-k:k),w.left!==a&&n&&(w.left-=m.x),w.left<c&&-w.left>i&&(w.left=a));return w.left-a},top:function(a){var b=D.vertical==="shift",c=-x.offset.top+v.offset.top+v.scrollTop,d=j.y==="top"?p:j.y==="bottom"?-p:-p/2,e=l.y==="top"?r:l.y==="bottom"?-r:-r/2,f=B&&B.size?B.size.height||0:0,g=B&&B.corner&&B.corner.precedance==="y"&&!b?f:0,h=c-a+g,i=a+p-v.height-c+g,k=d-(j.precedance==="y"||j.x===j.y?e:0),n=j.y==="center";b?(g=B&&B.corner&&B.corner.precedance==="x"?f:0,k=(j.y==="top"?1:-1)*d-g,w.top+=h>0?h:i>0?-i:0,w.top=Math.max(-x.offset.top+v.offset.top+(g&&B.corner.x==="center"?B.offset:0),a-k,Math.min(Math.max(-x.offset.top+v.offset.top+v.height,a+k),w.top))):(h>0&&(j.y!=="top"||i>0)?w.top-=k:i>0&&(j.y!=="bottom"||h>0)&&(w.top-=n?-k:k),w.top!==a&&n&&(w.top-=m.y),w.top<0&&-w.top>i&&(w.top=a));return w.top-a}},F;if(a.isArray(f)&&f.length===2)l={x:"left",y:"top"},w={left:f[0],top:f[1]};else if(f==="mouse"&&(c&&c.pageX||H.event.pageX))l={x:"left",y:"top"},c=(c&&(c.type==="resize"||c.type==="scroll")?H.event:c&&c.pageX&&c.type==="mousemove"?c:i&&i.pageX&&(m.mouse||!c||!c.pageX)?{pageX:i.pageX,pageY:i.pageY}:!m.mouse&&H.origin&&H.origin.pageX?H.origin:c)||c||H.event||i||{},w={top:c.pageY,left:c.pageX};else{f==="event"?c&&c.target&&c.type!=="scroll"&&c.type!=="resize"?f=H.target=a(c.target):f=H.target:H.target=a(f),f=a(f).eq(0);if(f.length===0)return z;f[0]===document||f[0]===b?(q=h.iOS?b.innerWidth:f.width(),r=h.iOS?b.innerHeight:f.height(),f[0]===b&&(w={top:u||h.iOS?(v||f).scrollTop():0,left:u||h.iOS?(v||f).scrollLeft():0})):f.is("area")&&h.imagemap?w=h.imagemap(f,l,D.enabled?n:e):f[0].namespaceURI==="http://www.w3.org/2000/svg"&&h.svg?w=h.svg(f,l):(q=f.outerWidth(),r=f.outerHeight(),w=h.offset(f,x)),w.offset&&(q=w.width,r=w.height,y=w.flipoffset,w=w.offset);if(h.iOS<4.1&&h.iOS>3.1||h.iOS==4.3||!h.iOS&&u)F=a(b),w.left-=F.scrollLeft(),w.top-=F.scrollTop();w.left+=l.x==="right"?q:l.x==="center"?q/2:0,w.top+=l.y==="bottom"?r:l.y==="center"?r/2:0}w.left+=m.x+(j.x==="right"?-o:j.x==="center"?-o/2:0),w.top+=m.y+(j.y==="bottom"?-p:j.y==="center"?-p/2:0),D.enabled?(v={elem:v,height:v[(v[0]===b?"h":"outerH")+"eight"](),width:v[(v[0]===b?"w":"outerW")+"idth"](),scrollLeft:u?0:v.scrollLeft(),scrollTop:u?0:v.scrollTop(),offset:v.offset()||{left:0,top:0}},x={elem:x,scrollLeft:x.scrollLeft(),scrollTop:x.scrollTop(),offset:x.offset()||{left:0,top:0}},w.adjusted={left:D.horizontal!=="none"?D.left(w.left):0,top:D.vertical!=="none"?D.top(w.top):0},w.adjusted.left+w.adjusted.top&&E.attr("class",E[0].className.replace(/ui-tooltip-pos-\w+/i,k+"-pos-"+j.abbrev())),y&&w.adjusted.left&&(w.left+=y.left),y&&w.adjusted.top&&(w.top+=y.top)):w.adjusted={left:0,top:0},s.originalEvent=a.extend({},c),E.trigger(s,[z,w,v.elem||v]);if(s.isDefaultPrevented())return z;delete w.adjusted,d===e||isNaN(w.left)||isNaN(w.top)||f==="mouse"||!a.isFunction(g.effect)?E.css(w):a.isFunction(g.effect)&&(g.effect.call(E,z,a.extend({},w)),E.queue(function(b){a(this).css({opacity:"",height:""}),a.browser.msie&&this.style.removeAttribute("filter"),b()})),C=0;return z},redraw:function(){if(z.rendered<1||D)return z;var a=t.position.container,b,c,d,e;D=1,t.style.height&&E.css("height",t.style.height),t.style.width?E.css("width",t.style.width):(E.css("width","").addClass(r),c=E.width()+1,d=E.css("max-width")||"",e=E.css("min-width")||"",b=(d+e).indexOf("%")>-1?a.width()/100:0,d=(d.indexOf("%")>-1?b:1)*parseInt(d,10)||c,e=(e.indexOf("%")>-1?b:1)*parseInt(e,10)||0,c=d+e?Math.min(Math.max(c,e),d):c,E.css("width",Math.round(c)).removeClass(r)),D=0;return z},disable:function(b){"boolean"!==typeof b&&(b=!E.hasClass(m)&&!H.disabled),z.rendered?(E.toggleClass(m,b),a.attr(E[0],"aria-disabled",b)):H.disabled=!!b;return z},enable:function(){return z.disable(e)},destroy:function(){var b=s[0],c=a.attr(b,u),d=s.data("qtip");z.rendered&&(E.remove(),a.each(z.plugins,function(){this.destroy&&this.destroy()})),clearTimeout(z.timers.show),clearTimeout(z.timers.hide),R();if(!d||z===d)a.removeData(b,"qtip"),t.suppress&&c&&(a.attr(b,"title",c),s.removeAttr(u)),s.removeAttr("aria-describedby");s.unbind(".qtip-"+w),delete j[z.id];return s}})}function x(b){var c;if(!b||"object"!==typeof b)return e;if(b.metadata===f||"object"!==typeof b.metadata)b.metadata={type:b.metadata};if("content"in b){if(b.content===f||"object"!==typeof b.content||b.content.jquery)b.content={text:b.content};c=b.content.text||e,!a.isFunction(c)&&(!c&&!c.attr||c.length<1||"object"===typeof c&&!c.jquery)&&(b.content.text=e);if("title"in b.content){if(b.content.title===f||"object"!==typeof b.content.title)b.content.title={text:b.content.title};c=b.content.title.text||e,!a.isFunction(c)&&(!c&&!c.attr||c.length<1||"object"===typeof c&&!c.jquery)&&(b.content.title.text=e)}}if("position"in b)if(b.position===f||"object"!==typeof b.position)b.position={my:b.position,at:b.position};if("show"in b)if(b.show===f||"object"!==typeof b.show)b.show.jquery?b.show={target:b.show}:b.show={event:b.show};if("hide"in b)if(b.hide===f||"object"!==typeof b.hide)b.hide.jquery?b.hide={target:b.hide}:b.hide={event:b.hide};if("style"in b)if(b.style===f||"object"!==typeof b.style)b.style={classes:b.style};a.each(h,function(){this.sanitize&&this.sanitize(b)});return b}function w(){w.history=w.history||[],w.history.push(arguments);if("object"===typeof console){var a=console[console.warn?"warn":"log"],b=Array.prototype.slice.call(arguments),c;typeof arguments[0]==="string"&&(b[0]="qTip2: "+b[0]),c=a.apply?a.apply(console,b):a(b)}}"use strict";var d=!0,e=!1,f=null,g,h,i,j={},k="ui-tooltip",l="ui-widget",m="ui-state-disabled",n="div.qtip."+k,o=k+"-default",p=k+"-focus",q=k+"-hover",r=k+"-fluid",s="-31000px",t="_replacedByqTip",u="oldtitle",v;g=a.fn.qtip=function(b,h,i){var j=(""+b).toLowerCase(),k=f,l=a.makeArray(arguments).slice(1),m=l[l.length-1],n=this[0]?a.data(this[0],"qtip"):f;if(!arguments.length&&n||j==="api")return n;if("string"===typeof b){this.each(function(){var b=a.data(this,"qtip");if(!b)return d;m&&m.timeStamp&&(b.cache.event=m);if(j!=="option"&&j!=="options"||!h)b[j]&&b[j].apply(b[j],l);else if(a.isPlainObject(h)||i!==c)b.set(h,i);else{k=b.get(h);return e}});return k!==f?k:this}if("object"===typeof b||!arguments.length){n=x(a.extend(d,{},b));return g.bind.call(this,n,m)}},g.bind=function(b,f){return this.each(function(k){function r(b){function d(){p.render(typeof b==="object"||l.show.ready),m.show.add(m.hide).unbind(o)}if(p.cache.disabled)return e;p.cache.event=a.extend({},b),p.cache.target=b?a(b.target):[c],l.show.delay>0?(clearTimeout(p.timers.show),p.timers.show=setTimeout(d,l.show.delay),n.show!==n.hide&&m.hide.bind(n.hide,function(){clearTimeout(p.timers.show)})):d()}var l,m,n,o,p,q;q=a.isArray(b.id)?b.id[k]:b.id,q=!q||q===e||q.length<1||j[q]?g.nextid++:j[q]=q,o=".qtip-"+q+"-create",p=z.call(this,q,b);if(p===e)return d;l=p.options,a.each(h,function(){this.initialize==="initialize"&&this(p)}),m={show:l.show.target,hide:l.hide.target},n={show:a.trim(""+l.show.event).replace(/ /g,o+" ")+o,hide:a.trim(""+l.hide.event).replace(/ /g,o+" ")+o},/mouse(over|enter)/i.test(n.show)&&!/mouse(out|leave)/i.test(n.hide)&&(n.hide+=" mouseleave"+o),m.show.bind("mousemove"+o,function(a){i={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),m.show.bind(n.show,r),(l.show.ready||l.prerender)&&r(f)})},h=g.plugins={Corner:function(a){a=(""+a).replace(/([A-Z])/," $1").replace(/middle/gi,"center").toLowerCase(),this.x=(a.match(/left|right/i)||a.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(a.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase();var b=a.charAt(0);this.precedance=b==="t"||b==="b"?"y":"x",this.string=function(){return this.precedance==="y"?this.y+this.x:this.x+this.y},this.abbrev=function(){var a=this.x.substr(0,1),b=this.y.substr(0,1);return a===b?a:a==="c"||a!=="c"&&b!=="c"?b+a:a+b},this.clone=function(){return{x:this.x,y:this.y,precedance:this.precedance,string:this.string,abbrev:this.abbrev,clone:this.clone}}},offset:function(a,b){function i(a,b){c.left+=b*a.scrollLeft(),c.top+=b*a.scrollTop()}var c=a.offset(),d=b,e=0,f=document.body,g,h;if(d){do{d.css("position")!=="static"&&(g=d[0]===f?{left:parseInt(d.css("left"),10)||0,top:parseInt(d.css("top"),10)||0}:d.position(),c.left-=g.left+(parseInt(d.css("borderLeftWidth"),10)||0)+(parseInt(d.css("marginLeft"),10)||0),c.top-=g.top+(parseInt(d.css("borderTopWidth"),10)||0),h=d.css("overflow"),(h==="scroll"||h==="auto")&&++e);if(d[0]===f)break}while(d=d.offsetParent());b[0]!==f&&e&&i(b,1)}return c},iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,3})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_","."))||e,fn:{attr:function(b,c){if(this.length){var d=this[0],e="title",f=a.data(d,"qtip");if(b===e&&f&&"object"===typeof f&&f.options.suppress){if(arguments.length<2)return a.attr(d,u);f&&f.options.content.attr===e&&f.cache.attr&&f.set("content.text",c);return this.attr(u,c)}}return a.fn["attr"+t].apply(this,arguments)},clone:function(b){var c=a([]),d="title",e=a.fn["clone"+t].apply(this,arguments);b||e.filter("["+u+"]").attr("title",function(){return a.attr(this,u)}).removeAttr(u);return e},remove:a.ui?f:function(b,c){a.ui||a(this).each(function(){c||(!b||a.filter(b,[this]).length)&&a("*",this).add(this).each(function(){a(this).triggerHandler("remove")})})}}},a.each(h.fn,function(b,c){if(!c||a.fn[b+t])return d;var e=a.fn[b+t]=a.fn[b];a.fn[b]=function(){return c.apply(this,arguments)||e.apply(this,arguments)}}),g.version="nightly",g.nextid=0,g.inactiveEvents="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),g.zindex=15e3,g.defaults={prerender:e,id:e,overwrite:d,suppress:d,content:{text:d,attr:"title",title:{text:e,button:e}},position:{my:"top left",at:"bottom right",target:e,container:e,viewport:e,adjust:{x:0,y:0,mouse:d,resize:d,method:"flip flip"},effect:function(b,c,d){a(this).animate(c,{duration:200,queue:e})}},show:{target:e,event:"mouseenter",effect:d,delay:90,solo:e,ready:e,autofocus:e},hide:{target:e,event:"mouseleave",effect:d,delay:0,fixed:e,inactive:e,leave:"window",distance:e},style:{classes:"",widget:e,width:e,height:e},events:{render:f,move:f,show:f,hide:f,toggle:f,visible:f,focus:f,blur:f}},h.svg=function(b,c){var d=a(document),e=b[0],f={width:0,height:0,offset:{top:1e10,left:1e10}},g,h,i,j,k;if(e.getBBox&&e.parentNode){g=e.getBBox(),h=e.getScreenCTM(),i=e.farthestViewportElement||e;if(!i.createSVGPoint)return f;j=i.createSVGPoint(),j.x=g.x,j.y=g.y,k=j.matrixTransform(h),f.offset.left=k.x,f.offset.top=k.y,j.x+=g.width,j.y+=g.height,k=j.matrixTransform(h),f.width=k.x-f.offset.left,f.height=k.y-f.offset.top,f.offset.left+=d.scrollLeft(),f.offset.top+=d.scrollTop()}return f}})(jQuery,window);
+ * qTip2 - Pretty powerful tooltips
+ * http://craigsworks.com/projects/qtip2/
+ *
+ * Version: nightly
+ * Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com
+ *
+ * Dual licensed under MIT or GPLv2 licenses
+ * http://en.wikipedia.org/wiki/MIT_License
+ * http://en.wikipedia.org/wiki/GNU_General_Public_License
+ *
+ * Date: Mon Nov 21 13:18:18.0000000000 2011
+ *//*jslint browser: true, onevar: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true *//*global window: false, jQuery: false, console: false */
+(function (a, b, c) {
+ function z(b, c) {
+ var i, j, k, l, m, n = a(this), o = a(document.body), p = this === document ? o : n, q = n.metadata ? n.metadata(c.metadata) : f, r = c.metadata.type === "html5" && q ? q[c.metadata.name] : f, s = n.data(c.metadata.name || "qtipopts");
+ try {s = typeof s === "string" ? (new Function("return " + s))() : s} catch (t) {w("Unable to parse HTML5 attribute data: " + s)}
+ l = a.extend(d, {}, g.defaults, c, typeof s === "object" ? x(s) : f, x(r || q)), j = l.position, l.id = b;
+ if ("boolean" === typeof l.content.text) {
+ k = n.attr(l.content.attr);
+ if (l.content.attr !== e && k)l.content.text = k; else {
+ w("Unable to locate content for tooltip! Aborting render of tooltip on element: ", n);
+ return e
+ }
+ }
+ j.container === e && (j.container = o), j.target === e && (j.target = p), l.show.target === e && (l.show.target = p), l.show.solo === d && (l.show.solo = o), l.hide.target === e && (l.hide.target = p), l.position.viewport === d && (l.position.viewport = j.container), j.at = new h.Corner(j.at), j.my = new h.Corner(j.my);
+ if (a.data(this, "qtip"))if (l.overwrite)n.qtip("destroy"); else if (l.overwrite === e)return e;
+ l.suppress && (m = a.attr(this, "title")) && a(this).removeAttr("title").attr(u, m), i = new y(n, l, b, !!k), a.data(this, "qtip", i), n.bind("remove.qtip-" + b, function () {i.destroy()});
+ return i
+ }
+
+ function y(s, t, w, y) {
+ function R() {
+ var c = [t.show.target[0], t.hide.target[0], z.rendered && G.tooltip[0], t.position.container[0], t.position.viewport[0], b, document];
+ z.rendered ? a([]).pushStack(a.grep(c, function (a) {return typeof a === "object"})).unbind(F) : t.show.target.unbind(F + "-create")
+ }
+
+ function Q() {
+ function p(a) {E.is(":visible") && z.reposition(a)}
+
+ function o(a) {
+ if (E.hasClass(m))return e;
+ clearTimeout(z.timers.inactive), z.timers.inactive = setTimeout(function () {z.hide(a)}, t.hide.inactive)
+ }
+
+ function l(b) {
+ if (E.hasClass(m) || C || D)return e;
+ var d = a(b.relatedTarget || b.target), g = d.closest(n)[0] === E[0], h = d[0] === f.show[0];
+ clearTimeout(z.timers.show), clearTimeout(z.timers.hide);
+ if (c.target === "mouse" && g || t.hide.fixed && (/mouse(out|leave|move)/.test(b.type) && (g || h)))try {b.preventDefault(), b.stopImmediatePropagation()} catch (i) {} else t.hide.delay > 0 ? z.timers.hide = setTimeout(function () {z.hide(b)}, t.hide.delay) : z.hide(b)
+ }
+
+ function k(a) {
+ if (E.hasClass(m))return e;
+ clearTimeout(z.timers.show), clearTimeout(z.timers.hide);
+ var b = function () {z.toggle(d, a)};
+ t.show.delay > 0 ? z.timers.show = setTimeout(b, t.show.delay) : b()
+ }
+
+ var c = t.position, f = {show: t.show.target, hide: t.hide.target, viewport: a(c.viewport), document: a(document), body: a(document.body), window: a(b)}, h = {show: a.trim("" + t.show.event).split(" "), hide: a.trim("" + t.hide.event).split(" ")}, j = a.browser.msie && parseInt(a.browser.version, 10) === 6;
+ E.bind("mouseenter" + F + " mouseleave" + F, function (a) {
+ var b = a.type === "mouseenter";
+ b && z.focus(a), E.toggleClass(q, b)
+ }), t.hide.fixed && (f.hide = f.hide.add(E), E.bind("mouseover" + F, function () {E.hasClass(m) || clearTimeout(z.timers.hide)})), /mouse(out|leave)/i.test(t.hide.event) ? t.hide.leave === "window" && f.window.bind("mouseout" + F + " blur" + F, function (a) {/select|option/.test(a.target) && !a.relatedTarget && z.hide(a)}) : /mouse(over|enter)/i.test(t.show.event) && f.hide.bind("mouseleave" + F, function (a) {clearTimeout(z.timers.show)}), ("" + t.hide.event).indexOf("unfocus") > -1 && f.body.bind("mousedown" + F, function (b) {
+ var c = a(b.target), d = !E.hasClass(m) && E.is(":visible");
+ c[0] !== E[0] && c.parents(n).length === 0 && c.add(s).length > 1 && !c.attr("disabled") && z.hide(b)
+ }), "number" === typeof t.hide.inactive && (f.show.bind("qtip-" + w + "-inactive", o), a.each(g.inactiveEvents, function (a, b) {f.hide.add(G.tooltip).bind(b + F + "-inactive", o)})), a.each(h.hide, function (b, c) {
+ var d = a.inArray(c, h.show), e = a(f.hide);
+ d > -1 && e.add(f.show).length === e.length || c === "unfocus" ? (f.show.bind(c + F, function (a) {E.is(":visible") ? l(a) : k(a)}), delete h.show[d]) : f.hide.bind(c + F, l)
+ }), a.each(h.show, function (a, b) {f.show.bind(b + F, k)}), "number" === typeof t.hide.distance && f.show.add(E).bind("mousemove" + F, function (a) {
+ var b = H.origin || {}, c = t.hide.distance, d = Math.abs;
+ (d(a.pageX - b.pageX) >= c || d(a.pageY - b.pageY) >= c) && z.hide(a)
+ }), c.target === "mouse" && (f.show.bind("mousemove" + F, function (a) {i = {pageX: a.pageX, pageY: a.pageY, type: "mousemove"}}), c.adjust.mouse && (t.hide.event && E.bind("mouseleave" + F, function (a) {(a.relatedTarget || a.target) !== f.show[0] && z.hide(a)}), f.document.bind("mousemove" + F, function (a) {!E.hasClass(m) && E.is(":visible") && z.reposition(a || i)}))), (c.adjust.resize || f.viewport.length) && (a.event.special.resize ? f.viewport : f.window).bind("resize" + F, p), (f.viewport.length || j && E.css("position") === "fixed") && f.viewport.bind("scroll" + F, p)
+ }
+
+ function P(b, d) {
+ function g(b) {
+ function i(c) {c && (delete h[c.src], clearTimeout(z.timers.img[c.src]), a(c).unbind(F)), a.isEmptyObject(h) && (z.redraw(), d !== e && z.reposition(H.event), b())}
+
+ var g, h = {};
+ if ((g = f.find("img:not([height]):not([width])")).length === 0)return i();
+ g.each(function (b, d) {
+ if (h[d.src] === c) {
+ var e = 0, f = 3;
+ (function g() {
+ if (d.height || d.width || e > f)return i(d);
+ e += 1, z.timers.img[d.src] = setTimeout(g, 700)
+ })(), a(d).bind("error" + F + " load" + F, function () {i(this)}), h[d.src] = d
+ }
+ })
+ }
+
+ var f = G.content;
+ if (!z.rendered || !b)return e;
+ a.isFunction(b) && (b = b.call(s, H.event, z) || ""), b.jquery && b.length > 0 ? f.empty().append(b.css({display: "block"})) : f.html(b), z.rendered < 0 ? E.queue("fx", g) : (D = 0, g(a.noop));
+ return z
+ }
+
+ function O(b, c) {
+ var d = G.title;
+ if (!z.rendered || !b)return e;
+ a.isFunction(b) && (b = b.call(s, H.event, z));
+ if (b === e)return K(e);
+ b.jquery && b.length > 0 ? d.empty().append(b.css({display: "block"})) : d.html(b), z.redraw(), c !== e && z.rendered && E.is(":visible") && z.reposition(H.event)
+ }
+
+ function N(a) {
+ var b = G.button, c = G.title;
+ if (!z.rendered)return e;
+ a ? (c || M(), L()) : b.remove()
+ }
+
+ function M() {
+ var b = B + "-title";
+ G.titlebar && K(), G.titlebar = a("<div />", {"class": k + "-titlebar " + (t.style.widget ? "ui-widget-header" : "")}).append(G.title = a("<div />", {id: b, "class": k + "-title", "aria-atomic": d})).insertBefore(G.content).delegate(".ui-tooltip-close", "mousedown keydown mouseup keyup mouseout",function (b) {a(this).toggleClass("ui-state-active ui-state-focus", b.type.substr(-4) === "down")}).delegate(".ui-tooltip-close", "mouseover mouseout", function (b) {a(this).toggleClass("ui-state-hover", b.type === "mouseover")}), t.content.title.button ? L() : z.rendered && z.redraw()
+ }
+
+ function L() {
+ var b = t.content.title.button, c = typeof b === "string", d = c ? b : "Close tooltip";
+ G.button && G.button.remove(), b.jquery ? G.button = b : G.button = a("<a />", {"class": "ui-state-default ui-tooltip-close " + (t.style.widget ? "" : k + "-icon"), title: d, "aria-label": d}).prepend(a("<span />", {"class": "ui-icon ui-icon-close", html: "&times;"})), G.button.appendTo(G.titlebar).attr("role", "button").click(function (a) {
+ E.hasClass(m) || z.hide(a);
+ return e
+ }), z.redraw()
+ }
+
+ function K(a) {G.title && (G.titlebar.remove(), G.titlebar = G.title = G.button = f, a !== e && z.reposition())}
+
+ function J() {
+ var a = t.style.widget;
+ E.toggleClass(l, a).toggleClass(o, !a), G.content.toggleClass(l + "-content", a), G.titlebar && G.titlebar.toggleClass(l + "-header", a), G.button && G.button.toggleClass(k + "-icon", !a)
+ }
+
+ function I(a) {
+ var b = 0, c, d = t, e = a.split(".");
+ while (d = d[e[b++]])b < e.length && (c = d);
+ return[c || t, e.pop()]
+ }
+
+ var z = this, A = document.body, B = k + "-" + w, C = 0, D = 0, E = a(), F = ".qtip-" + w, G, H;
+ z.id = w, z.rendered = e, z.elements = G = {target: s}, z.timers = {img: {}}, z.options = t, z.checks = {}, z.plugins = {}, z.cache = H = {event: {}, target: a(), disabled: e, attr: y}, z.checks.builtin = {"^id$": function (b, c, f) {
+ var h = f === d ? g.nextid : f, i = k + "-" + h;
+ h !== e && h.length > 0 && !a("#" + i).length && (E[0].id = i, G.content[0].id = i + "-content", G.title[0].id = i + "-title")
+ }, "^content.text$": function (a, b, c) {P(c)}, "^content.title.text$": function (a, b, c) {
+ if (!c)return K();
+ !G.title && c && M(), O(c)
+ }, "^content.title.button$": function (a, b, c) {N(c)}, "^position.(my|at)$": function (a, b, c) {"string" === typeof c && (a[b] = new h.Corner(c))}, "^position.container$": function (a, b, c) {z.rendered && E.appendTo(c)}, "^show.ready$": function () {z.rendered ? z.toggle(d) : z.render(1)}, "^style.classes$": function (a, b, c) {E.attr("class", k + " qtip ui-helper-reset " + c)}, "^style.widget|content.title": J, "^events.(render|show|move|hide|focus|blur)$": function (b, c, d) {E[(a.isFunction(d) ? "" : "un") + "bind"]("tooltip" + c, d)}, "^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)": function () {
+ var a = t.position;
+ E.attr("tracking", a.target === "mouse" && a.adjust.mouse), R(), Q()
+ }}, a.extend(z, {render: function (b) {
+ if (z.rendered)return z;
+ var c = t.content.text, f = t.content.title.text, g = t.position, i = a.Event("tooltiprender");
+ a.attr(s[0], "aria-describedby", B), E = G.tooltip = a("<div/>", {id: B, "class": k + " qtip ui-helper-reset " + o + " " + t.style.classes + " " + k + "-pos-" + t.position.my.abbrev(), width: t.style.width || "", height: t.style.height || "", tracking: g.target === "mouse" && g.adjust.mouse, role: "alert", "aria-live": "polite", "aria-atomic": e, "aria-describedby": B + "-content", "aria-hidden": d}).toggleClass(m, H.disabled).data("qtip", z).appendTo(t.position.container).append(G.content = a("<div />", {"class": k + "-content", id: B + "-content", "aria-atomic": d})), z.rendered = -1, C = D = 1, f && (M(), a.isFunction(f) || O(f, e)), a.isFunction(c) || P(c, e), z.rendered = d, J(), a.each(t.events, function (b, c) {a.isFunction(c) && E.bind(b === "toggle" ? "tooltipshow tooltiphide" : "tooltip" + b, c)}), a.each(h, function () {this.initialize === "render" && this(z)}), Q(), E.queue("fx", function (a) {i.originalEvent = H.event, E.trigger(i, [z]), C = D = 0, z.redraw(), (t.show.ready || b) && z.toggle(d, H.event), a()});
+ return z
+ }, get: function (a) {
+ var b, c;
+ switch (a.toLowerCase()) {
+ case"dimensions":
+ b = {height: E.outerHeight(), width: E.outerWidth()};
+ break;
+ case"offset":
+ b = h.offset(E, t.position.container);
+ break;
+ default:
+ c = I(a.toLowerCase()), b = c[0][c[1]], b = b.precedance ? b.string() : b
+ }
+ return b
+ }, set: function (b, c) {
+ function m(a, b) {
+ var c, d, e;
+ for (c in k)for (d in k[c])if (e = (new RegExp(d, "i")).exec(a))b.push(e), k[c][d].apply(z, b)
+ }
+
+ var g = /^position\.(my|at|adjust|target|container)|style|content|show\.ready/i, h = /^content\.(title|attr)|style/i, i = e, j = e, k = z.checks, l;
+ "string" === typeof b ? (l = b, b = {}, b[l] = c) : b = a.extend(d, {}, b), a.each(b, function (c, d) {
+ var e = I(c.toLowerCase()), f;
+ f = e[0][e[1]], e[0][e[1]] = "object" === typeof d && d.nodeType ? a(d) : d, b[c] = [e[0], e[1], d, f], i = g.test(c) || i, j = h.test(c) || j
+ }), x(t), C = D = 1, a.each(b, m), C = D = 0, E.is(":visible") && z.rendered && (i && z.reposition(t.position.target === "mouse" ? f : H.event), j && z.redraw());
+ return z
+ }, toggle: function (b, c) {
+ function q() {b ? (a.browser.msie && E[0].style.removeAttribute("filter"), E.css("overflow", ""), "string" === typeof h.autofocus && a(h.autofocus, E).focus(), p = a.Event("tooltipvisible"), p.originalEvent = c ? H.event : f, E.trigger(p, [z]), h.target.trigger("qtip-" + w + "-inactive")) : E.css({display: "", visibility: "", opacity: "", left: "", top: ""})}
+
+ if (!z.rendered)return b ? z.render(1) : z;
+ var g = b ? "show" : "hide", h = t[g], j = E.is(":visible"), k = !c || t[g].target.length < 2 || H.target[0] === c.target, l = t.position, m = t.content, o, p;
+ (typeof b).search("boolean|number") && (b = !j);
+ if (!E.is(":animated") && j === b && k)return z;
+ if (c) {
+ if (/over|enter/.test(c.type) && /out|leave/.test(H.event.type) && c.target === t.show.target[0] && E.has(c.relatedTarget).length)return z;
+ H.event = a.extend({}, c)
+ }
+ p = a.Event("tooltip" + g), p.originalEvent = c ? H.event : f, E.trigger(p, [z, 90]);
+ if (p.isDefaultPrevented())return z;
+ a.attr(E[0], "aria-hidden", !b), b ? (H.origin = a.extend({}, i), z.focus(c), a.isFunction(m.text) && P(m.text, e), a.isFunction(m.title.text) && O(m.title.text, e), !v && l.target === "mouse" && l.adjust.mouse && (a(document).bind("mousemove.qtip", function (a) {i = {pageX: a.pageX, pageY: a.pageY, type: "mousemove"}}), v = d), z.reposition(c), (p.solo = !!h.solo) && a(n, h.solo).not(E).qtip("hide", p)) : (clearTimeout(z.timers.show), delete H.origin, v && !a(n + '[tracking="true"]:visible', h.solo).not(E).length && (a(document).unbind("mousemove.qtip"), v = e), z.blur(c)), k && E.stop(0, 1), h.effect === e ? (E[g](), q.call(E)) : a.isFunction(h.effect) ? (h.effect.call(E, z), E.queue("fx", function (a) {q(), a()})) : E.fadeTo(90, b ? 1 : 0, q), b && h.target.trigger("qtip-" + w + "-inactive");
+ return z
+ }, show: function (a) {return z.toggle(d, a)}, hide: function (a) {return z.toggle(e, a)}, focus: function (b) {
+ if (!z.rendered)return z;
+ var c = a(n), d = parseInt(E[0].style.zIndex, 10), e = g.zindex + c.length, f = a.extend({}, b), h, i;
+ E.hasClass(p) || (i = a.Event("tooltipfocus"), i.originalEvent = f, E.trigger(i, [z, e]), i.isDefaultPrevented() || (d !== e && (c.each(function () {this.style.zIndex > d && (this.style.zIndex = this.style.zIndex - 1)}), c.filter("." + p).qtip("blur", f)), E.addClass(p)[0].style.zIndex = e));
+ return z
+ }, blur: function (b) {
+ var c = a.extend({}, b), d;
+ E.removeClass(p), d = a.Event("tooltipblur"), d.originalEvent = c, E.trigger(d, [z]);
+ return z
+ }, reposition: function (c, d) {
+ if (!z.rendered || C)return z;
+ C = 1;
+ var f = t.position.target, g = t.position, j = g.my, l = g.at, m = g.adjust, n = m.method.split(" "), o = E.outerWidth(), p = E.outerHeight(), q = 0, r = 0, s = a.Event("tooltipmove"), u = E.css("position") === "fixed", v = g.viewport, w = {left: 0, top: 0}, x = g.container, y = e, B = z.plugins.tip, D = {horizontal: n[0], vertical: n[1] = n[1] || n[0], enabled: v.jquery && f[0] !== b && f[0] !== A && m.method !== "none", left: function (a) {
+ var b = D.horizontal === "shift", c = -x.offset.left + v.offset.left + v.scrollLeft, d = j.x === "left" ? o : j.x === "right" ? -o : -o / 2, e = l.x === "left" ? q : l.x === "right" ? -q : -q / 2, f = B && B.size ? B.size.width || 0 : 0, g = B && B.corner && B.corner.precedance === "x" && !b ? f : 0, h = c - a + g, i = a + o - v.width - c + g, k = d - (j.precedance === "x" || j.x === j.y ? e : 0), n = j.x === "center";
+ b ? (g = B && B.corner && B.corner.precedance === "y" ? f : 0, k = (j.x === "left" ? 1 : -1) * d - g, w.left += h > 0 ? h : i > 0 ? -i : 0, w.left = Math.max(-x.offset.left + v.offset.left + (g && B.corner.x === "center" ? B.offset : 0), a - k, Math.min(Math.max(-x.offset.left + v.offset.left + v.width, a + k), w.left))) : (h > 0 && (j.x !== "left" || i > 0) ? w.left -= k : i > 0 && (j.x !== "right" || h > 0) && (w.left -= n ? -k : k), w.left !== a && n && (w.left -= m.x), w.left < c && -w.left > i && (w.left = a));
+ return w.left - a
+ }, top: function (a) {
+ var b = D.vertical === "shift", c = -x.offset.top + v.offset.top + v.scrollTop, d = j.y === "top" ? p : j.y === "bottom" ? -p : -p / 2, e = l.y === "top" ? r : l.y === "bottom" ? -r : -r / 2, f = B && B.size ? B.size.height || 0 : 0, g = B && B.corner && B.corner.precedance === "y" && !b ? f : 0, h = c - a + g, i = a + p - v.height - c + g, k = d - (j.precedance === "y" || j.x === j.y ? e : 0), n = j.y === "center";
+ b ? (g = B && B.corner && B.corner.precedance === "x" ? f : 0, k = (j.y === "top" ? 1 : -1) * d - g, w.top += h > 0 ? h : i > 0 ? -i : 0, w.top = Math.max(-x.offset.top + v.offset.top + (g && B.corner.x === "center" ? B.offset : 0), a - k, Math.min(Math.max(-x.offset.top + v.offset.top + v.height, a + k), w.top))) : (h > 0 && (j.y !== "top" || i > 0) ? w.top -= k : i > 0 && (j.y !== "bottom" || h > 0) && (w.top -= n ? -k : k), w.top !== a && n && (w.top -= m.y), w.top < 0 && -w.top > i && (w.top = a));
+ return w.top - a
+ }}, F;
+ if (a.isArray(f) && f.length === 2)l = {x: "left", y: "top"}, w = {left: f[0], top: f[1]}; else if (f === "mouse" && (c && c.pageX || H.event.pageX))l = {x: "left", y: "top"}, c = (c && (c.type === "resize" || c.type === "scroll") ? H.event : c && c.pageX && c.type === "mousemove" ? c : i && i.pageX && (m.mouse || !c || !c.pageX) ? {pageX: i.pageX, pageY: i.pageY} : !m.mouse && H.origin && H.origin.pageX ? H.origin : c) || c || H.event || i || {}, w = {top: c.pageY, left: c.pageX}; else {
+ f === "event" ? c && c.target && c.type !== "scroll" && c.type !== "resize" ? f = H.target = a(c.target) : f = H.target : H.target = a(f), f = a(f).eq(0);
+ if (f.length === 0)return z;
+ f[0] === document || f[0] === b ? (q = h.iOS ? b.innerWidth : f.width(), r = h.iOS ? b.innerHeight : f.height(), f[0] === b && (w = {top: u || h.iOS ? (v || f).scrollTop() : 0, left: u || h.iOS ? (v || f).scrollLeft() : 0})) : f.is("area") && h.imagemap ? w = h.imagemap(f, l, D.enabled ? n : e) : f[0].namespaceURI === "http://www.w3.org/2000/svg" && h.svg ? w = h.svg(f, l) : (q = f.outerWidth(), r = f.outerHeight(), w = h.offset(f, x)), w.offset && (q = w.width, r = w.height, y = w.flipoffset, w = w.offset);
+ if (h.iOS < 4.1 && h.iOS > 3.1 || h.iOS == 4.3 || !h.iOS && u)F = a(b), w.left -= F.scrollLeft(), w.top -= F.scrollTop();
+ w.left += l.x === "right" ? q : l.x === "center" ? q / 2 : 0, w.top += l.y === "bottom" ? r : l.y === "center" ? r / 2 : 0
+ }
+ w.left += m.x + (j.x === "right" ? -o : j.x === "center" ? -o / 2 : 0), w.top += m.y + (j.y === "bottom" ? -p : j.y === "center" ? -p / 2 : 0), D.enabled ? (v = {elem: v, height: v[(v[0] === b ? "h" : "outerH") + "eight"](), width: v[(v[0] === b ? "w" : "outerW") + "idth"](), scrollLeft: u ? 0 : v.scrollLeft(), scrollTop: u ? 0 : v.scrollTop(), offset: v.offset() || {left: 0, top: 0}}, x = {elem: x, scrollLeft: x.scrollLeft(), scrollTop: x.scrollTop(), offset: x.offset() || {left: 0, top: 0}}, w.adjusted = {left: D.horizontal !== "none" ? D.left(w.left) : 0, top: D.vertical !== "none" ? D.top(w.top) : 0}, w.adjusted.left + w.adjusted.top && E.attr("class", E[0].className.replace(/ui-tooltip-pos-\w+/i, k + "-pos-" + j.abbrev())), y && w.adjusted.left && (w.left += y.left), y && w.adjusted.top && (w.top += y.top)) : w.adjusted = {left: 0, top: 0}, s.originalEvent = a.extend({}, c), E.trigger(s, [z, w, v.elem || v]);
+ if (s.isDefaultPrevented())return z;
+ delete w.adjusted, d === e || isNaN(w.left) || isNaN(w.top) || f === "mouse" || !a.isFunction(g.effect) ? E.css(w) : a.isFunction(g.effect) && (g.effect.call(E, z, a.extend({}, w)), E.queue(function (b) {a(this).css({opacity: "", height: ""}), a.browser.msie && this.style.removeAttribute("filter"), b()})), C = 0;
+ return z
+ }, redraw: function () {
+ if (z.rendered < 1 || D)return z;
+ var a = t.position.container, b, c, d, e;
+ D = 1, t.style.height && E.css("height", t.style.height), t.style.width ? E.css("width", t.style.width) : (E.css("width", "").addClass(r), c = E.width() + 1, d = E.css("max-width") || "", e = E.css("min-width") || "", b = (d + e).indexOf("%") > -1 ? a.width() / 100 : 0, d = (d.indexOf("%") > -1 ? b : 1) * parseInt(d, 10) || c, e = (e.indexOf("%") > -1 ? b : 1) * parseInt(e, 10) || 0, c = d + e ? Math.min(Math.max(c, e), d) : c, E.css("width", Math.round(c)).removeClass(r)), D = 0;
+ return z
+ }, disable: function (b) {
+ "boolean" !== typeof b && (b = !E.hasClass(m) && !H.disabled), z.rendered ? (E.toggleClass(m, b), a.attr(E[0], "aria-disabled", b)) : H.disabled = !!b;
+ return z
+ }, enable: function () {return z.disable(e)}, destroy: function () {
+ var b = s[0], c = a.attr(b, u), d = s.data("qtip");
+ z.rendered && (E.remove(), a.each(z.plugins, function () {this.destroy && this.destroy()})), clearTimeout(z.timers.show), clearTimeout(z.timers.hide), R();
+ if (!d || z === d)a.removeData(b, "qtip"), t.suppress && c && (a.attr(b, "title", c), s.removeAttr(u)), s.removeAttr("aria-describedby");
+ s.unbind(".qtip-" + w), delete j[z.id];
+ return s
+ }})
+ }
+
+ function x(b) {
+ var c;
+ if (!b || "object" !== typeof b)return e;
+ if (b.metadata === f || "object" !== typeof b.metadata)b.metadata = {type: b.metadata};
+ if ("content"in b) {
+ if (b.content === f || "object" !== typeof b.content || b.content.jquery)b.content = {text: b.content};
+ c = b.content.text || e, !a.isFunction(c) && (!c && !c.attr || c.length < 1 || "object" === typeof c && !c.jquery) && (b.content.text = e);
+ if ("title"in b.content) {
+ if (b.content.title === f || "object" !== typeof b.content.title)b.content.title = {text: b.content.title};
+ c = b.content.title.text || e, !a.isFunction(c) && (!c && !c.attr || c.length < 1 || "object" === typeof c && !c.jquery) && (b.content.title.text = e)
+ }
+ }
+ if ("position"in b)if (b.position === f || "object" !== typeof b.position)b.position = {my: b.position, at: b.position};
+ if ("show"in b)if (b.show === f || "object" !== typeof b.show)b.show.jquery ? b.show = {target: b.show} : b.show = {event: b.show};
+ if ("hide"in b)if (b.hide === f || "object" !== typeof b.hide)b.hide.jquery ? b.hide = {target: b.hide} : b.hide = {event: b.hide};
+ if ("style"in b)if (b.style === f || "object" !== typeof b.style)b.style = {classes: b.style};
+ a.each(h, function () {this.sanitize && this.sanitize(b)});
+ return b
+ }
+
+ function w() {
+ w.history = w.history || [], w.history.push(arguments);
+ if ("object" === typeof console) {
+ var a = console[console.warn ? "warn" : "log"], b = Array.prototype.slice.call(arguments), c;
+ typeof arguments[0] === "string" && (b[0] = "qTip2: " + b[0]), c = a.apply ? a.apply(console, b) : a(b)
+ }
+ }
+
+ "use strict";
+ var d = !0, e = !1, f = null, g, h, i, j = {}, k = "ui-tooltip", l = "ui-widget", m = "ui-state-disabled", n = "div.qtip." + k, o = k + "-default", p = k + "-focus", q = k + "-hover", r = k + "-fluid", s = "-31000px", t = "_replacedByqTip", u = "oldtitle", v;
+ g = a.fn.qtip = function (b, h, i) {
+ var j = ("" + b).toLowerCase(), k = f, l = a.makeArray(arguments).slice(1), m = l[l.length - 1], n = this[0] ? a.data(this[0], "qtip") : f;
+ if (!arguments.length && n || j === "api")return n;
+ if ("string" === typeof b) {
+ this.each(function () {
+ var b = a.data(this, "qtip");
+ if (!b)return d;
+ m && m.timeStamp && (b.cache.event = m);
+ if (j !== "option" && j !== "options" || !h)b[j] && b[j].apply(b[j], l); else if (a.isPlainObject(h) || i !== c)b.set(h, i); else {
+ k = b.get(h);
+ return e
+ }
+ });
+ return k !== f ? k : this
+ }
+ if ("object" === typeof b || !arguments.length) {
+ n = x(a.extend(d, {}, b));
+ return g.bind.call(this, n, m)
+ }
+ }, g.bind = function (b, f) {
+ return this.each(function (k) {
+ function r(b) {
+ function d() {p.render(typeof b === "object" || l.show.ready), m.show.add(m.hide).unbind(o)}
+
+ if (p.cache.disabled)return e;
+ p.cache.event = a.extend({}, b), p.cache.target = b ? a(b.target) : [c], l.show.delay > 0 ? (clearTimeout(p.timers.show), p.timers.show = setTimeout(d, l.show.delay), n.show !== n.hide && m.hide.bind(n.hide, function () {clearTimeout(p.timers.show)})) : d()
+ }
+
+ var l, m, n, o, p, q;
+ q = a.isArray(b.id) ? b.id[k] : b.id, q = !q || q === e || q.length < 1 || j[q] ? g.nextid++ : j[q] = q, o = ".qtip-" + q + "-create", p = z.call(this, q, b);
+ if (p === e)return d;
+ l = p.options, a.each(h, function () {this.initialize === "initialize" && this(p)}), m = {show: l.show.target, hide: l.hide.target}, n = {show: a.trim("" + l.show.event).replace(/ /g, o + " ") + o, hide: a.trim("" + l.hide.event).replace(/ /g, o + " ") + o}, /mouse(over|enter)/i.test(n.show) && !/mouse(out|leave)/i.test(n.hide) && (n.hide += " mouseleave" + o), m.show.bind("mousemove" + o, function (a) {i = {pageX: a.pageX, pageY: a.pageY, type: "mousemove"}}), m.show.bind(n.show, r), (l.show.ready || l.prerender) && r(f)
+ })
+ }, h = g.plugins = {Corner: function (a) {
+ a = ("" + a).replace(/([A-Z])/, " $1").replace(/middle/gi, "center").toLowerCase(), this.x = (a.match(/left|right/i) || a.match(/center/) || ["inherit"])[0].toLowerCase(), this.y = (a.match(/top|bottom|center/i) || ["inherit"])[0].toLowerCase();
+ var b = a.charAt(0);
+ this.precedance = b === "t" || b === "b" ? "y" : "x", this.string = function () {return this.precedance === "y" ? this.y + this.x : this.x + this.y}, this.abbrev = function () {
+ var a = this.x.substr(0, 1), b = this.y.substr(0, 1);
+ return a === b ? a : a === "c" || a !== "c" && b !== "c" ? b + a : a + b
+ }, this.clone = function () {return{x: this.x, y: this.y, precedance: this.precedance, string: this.string, abbrev: this.abbrev, clone: this.clone}}
+ }, offset: function (a, b) {
+ function i(a, b) {c.left += b * a.scrollLeft(), c.top += b * a.scrollTop()}
+
+ var c = a.offset(), d = b, e = 0, f = document.body, g, h;
+ if (d) {
+ do {
+ d.css("position") !== "static" && (g = d[0] === f ? {left: parseInt(d.css("left"), 10) || 0, top: parseInt(d.css("top"), 10) || 0} : d.position(), c.left -= g.left + (parseInt(d.css("borderLeftWidth"), 10) || 0) + (parseInt(d.css("marginLeft"), 10) || 0), c.top -= g.top + (parseInt(d.css("borderTopWidth"), 10) || 0), h = d.css("overflow"), (h === "scroll" || h === "auto") && ++e);
+ if (d[0] === f)break
+ } while (d = d.offsetParent());
+ b[0] !== f && e && i(b, 1)
+ }
+ return c
+ }, iOS: parseFloat(("" + (/CPU.*OS ([0-9_]{1,3})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0, ""])[1]).replace("undefined", "3_2").replace("_", ".")) || e, fn: {attr: function (b, c) {
+ if (this.length) {
+ var d = this[0], e = "title", f = a.data(d, "qtip");
+ if (b === e && f && "object" === typeof f && f.options.suppress) {
+ if (arguments.length < 2)return a.attr(d, u);
+ f && f.options.content.attr === e && f.cache.attr && f.set("content.text", c);
+ return this.attr(u, c)
+ }
+ }
+ return a.fn["attr" + t].apply(this, arguments)
+ }, clone: function (b) {
+ var c = a([]), d = "title", e = a.fn["clone" + t].apply(this, arguments);
+ b || e.filter("[" + u + "]").attr("title",function () {return a.attr(this, u)}).removeAttr(u);
+ return e
+ }, remove: a.ui ? f : function (b, c) {a.ui || a(this).each(function () {c || (!b || a.filter(b, [this]).length) && a("*", this).add(this).each(function () {a(this).triggerHandler("remove")})})}}}, a.each(h.fn, function (b, c) {
+ if (!c || a.fn[b + t])return d;
+ var e = a.fn[b + t] = a.fn[b];
+ a.fn[b] = function () {return c.apply(this, arguments) || e.apply(this, arguments)}
+ }), g.version = "nightly", g.nextid = 0, g.inactiveEvents = "click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "), g.zindex = 15e3, g.defaults = {prerender: e, id: e, overwrite: d, suppress: d, content: {text: d, attr: "title", title: {text: e, button: e}}, position: {my: "top left", at: "bottom right", target: e, container: e, viewport: e, adjust: {x: 0, y: 0, mouse: d, resize: d, method: "flip flip"}, effect: function (b, c, d) {a(this).animate(c, {duration: 200, queue: e})}}, show: {target: e, event: "mouseenter", effect: d, delay: 90, solo: e, ready: e, autofocus: e}, hide: {target: e, event: "mouseleave", effect: d, delay: 0, fixed: e, inactive: e, leave: "window", distance: e}, style: {classes: "", widget: e, width: e, height: e}, events: {render: f, move: f, show: f, hide: f, toggle: f, visible: f, focus: f, blur: f}}, h.svg = function (b, c) {
+ var d = a(document), e = b[0], f = {width: 0, height: 0, offset: {top: 1e10, left: 1e10}}, g, h, i, j, k;
+ if (e.getBBox && e.parentNode) {
+ g = e.getBBox(), h = e.getScreenCTM(), i = e.farthestViewportElement || e;
+ if (!i.createSVGPoint)return f;
+ j = i.createSVGPoint(), j.x = g.x, j.y = g.y, k = j.matrixTransform(h), f.offset.left = k.x, f.offset.top = k.y, j.x += g.width, j.y += g.height, k = j.matrixTransform(h), f.width = k.x - f.offset.left, f.height = k.y - f.offset.top, f.offset.left += d.scrollLeft(), f.offset.top += d.scrollTop()
+ }
+ return f
+ }
+})(jQuery, window);
diff --git a/plugins/UserCountryMap/js/vendor/kartograph.js b/plugins/UserCountryMap/js/vendor/kartograph.js
index 6cb754dfb6..e50442fdc1 100644
--- a/plugins/UserCountryMap/js/vendor/kartograph.js
+++ b/plugins/UserCountryMap/js/vendor/kartograph.js
@@ -15,6003 +15,6015 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
-*/
+ */
+
+
+(function () {
+ var $, Aitoff, Azimuthal, BBox, Balthasart, Behrmann, BlurFilter, Bubble, CEA, CantersModifiedSinusoidalI, Circle, CohenSutherland, Conic, Cylindrical, EckertIV, EquidistantAzimuthal, Equirectangular, Filter, GallPeters, GlowFilter, GoodeHomolosine, Hatano, HoboDyer, HtmlLabel, Icon, Kartograph, LAEA, LCC, LabeledBubble, LatLon, Line, LinearScale, LogScale, LonLat, Loximuthal, MapLayer, MapLayerPath, Mercator, Mollweide, NaturalEarth, Nicolosi, Orthographic, PanAndZoomControl, Path, PieChart, Proj, PseudoConic, PseudoCylindrical, QuantileScale, REbraces, REcomment_string, REfull, REmunged, Robinson, Satellite, Scale, Sinusoidal, SqrtScale, StackedBarChart, Stereographic, SvgLabel, Symbol, SymbolGroup, View, WagnerIV, WagnerV, Winkel3, drawPieChart, filter, kartograph, log, map_layer_path_uid, munge, munged, parsedeclarations, resolve, restore, root, uid, warn, __point_in_polygon, __proj, __type, _base, _base1, _ref, _ref1, _ref2, _ref3, _ref4, _ref5,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function (child, parent) {
+ for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
+ function ctor() { this.constructor = child; }
+
+ ctor.prototype = parent.prototype;
+ child.prototype = new ctor();
+ child.__super__ = parent.prototype;
+ return child;
+ },
+ __bind = function (fn, me) { return function () { return fn.apply(me, arguments); }; },
+ __indexOf = [].indexOf || function (item) {
+ for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; }
+ return -1;
+ };
+ root = typeof exports !== "undefined" && exports !== null ? exports : this;
-(function() {
- var $, Aitoff, Azimuthal, BBox, Balthasart, Behrmann, BlurFilter, Bubble, CEA, CantersModifiedSinusoidalI, Circle, CohenSutherland, Conic, Cylindrical, EckertIV, EquidistantAzimuthal, Equirectangular, Filter, GallPeters, GlowFilter, GoodeHomolosine, Hatano, HoboDyer, HtmlLabel, Icon, Kartograph, LAEA, LCC, LabeledBubble, LatLon, Line, LinearScale, LogScale, LonLat, Loximuthal, MapLayer, MapLayerPath, Mercator, Mollweide, NaturalEarth, Nicolosi, Orthographic, PanAndZoomControl, Path, PieChart, Proj, PseudoConic, PseudoCylindrical, QuantileScale, REbraces, REcomment_string, REfull, REmunged, Robinson, Satellite, Scale, Sinusoidal, SqrtScale, StackedBarChart, Stereographic, SvgLabel, Symbol, SymbolGroup, View, WagnerIV, WagnerV, Winkel3, drawPieChart, filter, kartograph, log, map_layer_path_uid, munge, munged, parsedeclarations, resolve, restore, root, uid, warn, __point_in_polygon, __proj, __type, _base, _base1, _ref, _ref1, _ref2, _ref3, _ref4, _ref5,
- __hasProp = {}.hasOwnProperty,
- __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
- __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+ kartograph = root.$K = window.Kartograph = (_ref = root.Kartograph) != null ? _ref : root.Kartograph = {};
- root = typeof exports !== "undefined" && exports !== null ? exports : this;
+ kartograph.version = "0.5.2";
- kartograph = root.$K = window.Kartograph = (_ref = root.Kartograph) != null ? _ref : root.Kartograph = {};
+ $ = root.jQuery;
- kartograph.version = "0.5.2";
+ kartograph.__verbose = false;
- $ = root.jQuery;
+ warn = function (s) {
+ try {
+ return console.warn.apply(console, arguments);
+ } catch (e) {
+ try {
+ return opera.postError.apply(opera, arguments);
+ } catch (e) {
+ return alert(Array.prototype.join.call(arguments, ' '));
+ }
+ }
+ };
- kartograph.__verbose = false;
+ log = function (s) {
+ if (kartograph.__verbose) {
+ try {
+ return console.debug.apply(console, arguments);
+ } catch (e) {
+ try {
+ return opera.postError.apply(opera, arguments);
+ } catch (e) {
+ return alert(Array.prototype.join.call(arguments, ' '));
+ }
+ }
+ }
+ };
- warn = function(s) {
- try {
- return console.warn.apply(console, arguments);
- } catch (e) {
- try {
- return opera.postError.apply(opera, arguments);
- } catch (e) {
- return alert(Array.prototype.join.call(arguments, ' '));
- }
+ if ((_ref1 = (_base = String.prototype).trim) == null) {
+ _base.trim = function () {
+ return this.replace(/^\s+|\s+$/g, "");
+ };
}
- };
- log = function(s) {
- if (kartograph.__verbose) {
- try {
- return console.debug.apply(console, arguments);
- } catch (e) {
- try {
- return opera.postError.apply(opera, arguments);
- } catch (e) {
- return alert(Array.prototype.join.call(arguments, ' '));
+ if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function (searchElement /*, fromIndex */) {
+ "use strict";
+ if (this == null) {
+ throw new TypeError();
+ }
+ var t = Object(this);
+ var len = t.length >>> 0;
+ if (len === 0) {
+ return -1;
+ }
+ var n = 0;
+ if (arguments.length > 0) {
+ n = Number(arguments[1]);
+ if (n != n) { // shortcut for verifying if it's NaN
+ n = 0;
+ } else if (n != 0 && n != Infinity && n != -Infinity) {
+ n = (n > 0 || -1) * Math.floor(Math.abs(n));
+ }
+ }
+ if (n >= len) {
+ return -1;
+ }
+ var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
+ for (; k < len; k++) {
+ if (k in t && t[k] === searchElement) {
+ return k;
+ }
+ }
+ return -1;
}
- }
}
- };
+ ;
- if ((_ref1 = (_base = String.prototype).trim) == null) {
- _base.trim = function() {
- return this.replace(/^\s+|\s+$/g, "");
- };
- }
- if (!Array.prototype.indexOf) {
- Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
- "use strict";
- if (this == null) {
- throw new TypeError();
- }
- var t = Object(this);
- var len = t.length >>> 0;
- if (len === 0) {
- return -1;
+ __type = (function () {
+ /*
+ for browser-safe type checking+
+ ported from jQuery's $.type
+ */
+
+ var classToType, name, _i, _len, _ref2;
+ classToType = {};
+ _ref2 = "Boolean Number String Function Array Date RegExp Undefined Null".split(" ");
+ for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
+ name = _ref2[_i];
+ classToType["[object " + name + "]"] = name.toLowerCase();
}
- var n = 0;
- if (arguments.length > 0) {
- n = Number(arguments[1]);
- if (n != n) { // shortcut for verifying if it's NaN
- n = 0;
- } else if (n != 0 && n != Infinity && n != -Infinity) {
- n = (n > 0 || -1) * Math.floor(Math.abs(n));
+ return function (obj) {
+ var strType;
+ strType = Object.prototype.toString.call(obj);
+ return classToType[strType] || "object";
+ };
+ })();
+
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ BBox = (function () {
+ /*
+ 2D bounding box
+ */
+
+ function BBox(left, top, width, height) {
+ var s;
+ if (left == null) {
+ left = 0;
}
- }
- if (n >= len) {
- return -1;
- }
- var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
- for (; k < len; k++) {
- if (k in t && t[k] === searchElement) {
- return k;
+ if (top == null) {
+ top = 0;
+ }
+ if (width == null) {
+ width = null;
}
+ if (height == null) {
+ height = null;
+ }
+ s = this;
+ if (width === null) {
+ s.xmin = Number.MAX_VALUE;
+ s.xmax = Number.MAX_VALUE * -1;
+ } else {
+ s.xmin = s.left = left;
+ s.xmax = s.right = left + width;
+ s.width = width;
+ }
+ if (height === null) {
+ s.ymin = Number.MAX_VALUE;
+ s.ymax = Number.MAX_VALUE * -1;
+ } else {
+ s.ymin = s.top = top;
+ s.ymax = s.bottom = height + top;
+ s.height = height;
+ }
+ return;
}
- return -1;
- }
-};
+ BBox.prototype.update = function (x, y) {
+ var s;
+ if (!(y != null)) {
+ y = x[1];
+ x = x[0];
+ }
+ s = this;
+ s.xmin = Math.min(s.xmin, x);
+ s.ymin = Math.min(s.ymin, y);
+ s.xmax = Math.max(s.xmax, x);
+ s.ymax = Math.max(s.ymax, y);
+ s.left = s.xmin;
+ s.top = s.ymin;
+ s.right = s.xmax;
+ s.bottom = s.ymax;
+ s.width = s.xmax - s.xmin;
+ s.height = s.ymax - s.ymin;
+ return this;
+ };
+
+ BBox.prototype.intersects = function (bbox) {
+ return bbox.left < s.right && bbox.right > s.left && bbox.top < s.bottom && bbox.bottom > s.top;
+ };
- __type = (function() {
- /*
- for browser-safe type checking+
- ported from jQuery's $.type
- */
-
- var classToType, name, _i, _len, _ref2;
- classToType = {};
- _ref2 = "Boolean Number String Function Array Date RegExp Undefined Null".split(" ");
- for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
- name = _ref2[_i];
- classToType["[object " + name + "]"] = name.toLowerCase();
- }
- return function(obj) {
- var strType;
- strType = Object.prototype.toString.call(obj);
- return classToType[strType] || "object";
+ BBox.prototype.inside = function (x, y) {
+ var s;
+ s = this;
+ return x >= s.left && x <= s.right && y >= s.top && y <= s.bottom;
+ };
+
+ BBox.prototype.join = function (bbox) {
+ var s;
+ s = this;
+ s.update(bbox.left, bbox.top);
+ s.update(bbox.right, bbox.bottom);
+ return this;
+ };
+
+ return BBox;
+
+ })();
+
+ BBox.fromXML = function (xml) {
+ var h, w, x, y;
+ x = Number(xml.getAttribute('x'));
+ y = Number(xml.getAttribute('y'));
+ w = Number(xml.getAttribute('w'));
+ h = Number(xml.getAttribute('h'));
+ return new kartograph.BBox(x, y, w, h);
};
- })();
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- BBox = (function() {
+
+ kartograph.BBox = BBox;
+
/*
- 2D bounding box
- */
-
- function BBox(left, top, width, height) {
- var s;
- if (left == null) {
- left = 0;
- }
- if (top == null) {
- top = 0;
- }
- if (width == null) {
- width = null;
- }
- if (height == null) {
- height = null;
- }
- s = this;
- if (width === null) {
- s.xmin = Number.MAX_VALUE;
- s.xmax = Number.MAX_VALUE * -1;
- } else {
- s.xmin = s.left = left;
- s.xmax = s.right = left + width;
- s.width = width;
- }
- if (height === null) {
- s.ymin = Number.MAX_VALUE;
- s.ymax = Number.MAX_VALUE * -1;
- } else {
- s.ymin = s.top = top;
- s.ymax = s.bottom = height + top;
- s.height = height;
- }
- return;
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ if ((_ref2 = kartograph.geom) == null) {
+ kartograph.geom = {};
}
- BBox.prototype.update = function(x, y) {
- var s;
- if (!(y != null)) {
- y = x[1];
- x = x[0];
- }
- s = this;
- s.xmin = Math.min(s.xmin, x);
- s.ymin = Math.min(s.ymin, y);
- s.xmax = Math.max(s.xmax, x);
- s.ymax = Math.max(s.ymax, y);
- s.left = s.xmin;
- s.top = s.ymin;
- s.right = s.xmax;
- s.bottom = s.ymax;
- s.width = s.xmax - s.xmin;
- s.height = s.ymax - s.ymin;
- return this;
- };
+ if ((_ref3 = (_base1 = kartograph.geom).clipping) == null) {
+ _base1.clipping = {};
+ }
- BBox.prototype.intersects = function(bbox) {
- return bbox.left < s.right && bbox.right > s.left && bbox.top < s.bottom && bbox.bottom > s.top;
- };
+ CohenSutherland = (function () {
+ var BOTTOM, INSIDE, LEFT, RIGHT, TOP;
- BBox.prototype.inside = function(x, y) {
- var s;
- s = this;
- return x >= s.left && x <= s.right && y >= s.top && y <= s.bottom;
- };
+ function CohenSutherland() {}
- BBox.prototype.join = function(bbox) {
- var s;
- s = this;
- s.update(bbox.left, bbox.top);
- s.update(bbox.right, bbox.bottom);
- return this;
- };
+ INSIDE = 0;
- return BBox;
-
- })();
-
- BBox.fromXML = function(xml) {
- var h, w, x, y;
- x = Number(xml.getAttribute('x'));
- y = Number(xml.getAttribute('y'));
- w = Number(xml.getAttribute('w'));
- h = Number(xml.getAttribute('h'));
- return new kartograph.BBox(x, y, w, h);
- };
-
- kartograph.BBox = BBox;
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- if ((_ref2 = kartograph.geom) == null) {
- kartograph.geom = {};
- }
-
- if ((_ref3 = (_base1 = kartograph.geom).clipping) == null) {
- _base1.clipping = {};
- }
-
- CohenSutherland = (function() {
- var BOTTOM, INSIDE, LEFT, RIGHT, TOP;
-
- function CohenSutherland() {}
-
- INSIDE = 0;
-
- LEFT = 1;
-
- RIGHT = 2;
-
- BOTTOM = 4;
-
- TOP = 8;
-
- CohenSutherland.prototype.compute_out_code = function(bbox, x, y) {
- var code, self;
- self = this;
- code = self.INSIDE;
- if (x < bbox.left) {
- code |= self.LEFT;
- } else if (x > bbox.right) {
- code |= self.RIGHT;
- }
- if (y < bbox.top) {
- code |= self.TOP;
- } else if (y > bbox.bottom) {
- code |= self.BOTTOM;
- }
- return code;
- };
+ LEFT = 1;
- CohenSutherland.prototype.clip = function(bbox, x0, y0, x1, y1) {
- var accept, code0, code1, cout, self, x, y;
- self = this;
- code0 = self.compute_out_code(bbox, x0, y0);
- code1 = self.compute_out_code(bbox, x1, y1);
- accept = False;
- while (True) {
- if (!(code0 | code1)) {
- accept = True;
- break;
- } else if (code0 & code1) {
- break;
- } else {
- cout = code === 0 ? code1 : code0;
- if (cout & self.TOP) {
- x = x0 + (x1 - x0) * (bbox.top - y0) / (y1 - y0);
- y = bbox.top;
- } else if (cout & self.BOTTOM) {
- x = x0 + (x1 - x0) * (bbox.bottom - y0) / (y1 - y0);
- y = bbox.bottom;
- } else if (cout & self.RIGHT) {
- y = y0 + (y1 - y0) * (bbox.right - x0) / (x1 - x0);
- x = bbox.right;
- } else if (cout & self.LEFT) {
- y = y0 + (y1 - y0) * (bbox.left - x0) / (x1 - x0);
- x = bbox.left;
- }
- if (cout === code0) {
- x0 = x;
- y0 = y;
+ RIGHT = 2;
+
+ BOTTOM = 4;
+
+ TOP = 8;
+
+ CohenSutherland.prototype.compute_out_code = function (bbox, x, y) {
+ var code, self;
+ self = this;
+ code = self.INSIDE;
+ if (x < bbox.left) {
+ code |= self.LEFT;
+ } else if (x > bbox.right) {
+ code |= self.RIGHT;
+ }
+ if (y < bbox.top) {
+ code |= self.TOP;
+ } else if (y > bbox.bottom) {
+ code |= self.BOTTOM;
+ }
+ return code;
+ };
+
+ CohenSutherland.prototype.clip = function (bbox, x0, y0, x1, y1) {
+ var accept, code0, code1, cout, self, x, y;
+ self = this;
code0 = self.compute_out_code(bbox, x0, y0);
- } else {
- x1 = x;
- y1 = y;
code1 = self.compute_out_code(bbox, x1, y1);
- }
- }
- }
- if (accept) {
- return [x0, y0, x1, y1];
- } else {
- return null;
- }
- };
+ accept = False;
+ while (True) {
+ if (!(code0 | code1)) {
+ accept = True;
+ break;
+ } else if (code0 & code1) {
+ break;
+ } else {
+ cout = code === 0 ? code1 : code0;
+ if (cout & self.TOP) {
+ x = x0 + (x1 - x0) * (bbox.top - y0) / (y1 - y0);
+ y = bbox.top;
+ } else if (cout & self.BOTTOM) {
+ x = x0 + (x1 - x0) * (bbox.bottom - y0) / (y1 - y0);
+ y = bbox.bottom;
+ } else if (cout & self.RIGHT) {
+ y = y0 + (y1 - y0) * (bbox.right - x0) / (x1 - x0);
+ x = bbox.right;
+ } else if (cout & self.LEFT) {
+ y = y0 + (y1 - y0) * (bbox.left - x0) / (x1 - x0);
+ x = bbox.left;
+ }
+ if (cout === code0) {
+ x0 = x;
+ y0 = y;
+ code0 = self.compute_out_code(bbox, x0, y0);
+ } else {
+ x1 = x;
+ y1 = y;
+ code1 = self.compute_out_code(bbox, x1, y1);
+ }
+ }
+ }
+ if (accept) {
+ return [x0, y0, x1, y1];
+ } else {
+ return null;
+ }
+ };
- return CohenSutherland;
-
- })();
-
- kartograph.geom.clipping.CohenSutherland = CohenSutherland;
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- Kartograph = (function() {
-
- function Kartograph(container, width, height) {
- var cnt, me;
- me = this;
- me.container = cnt = $(container);
- if (width == null) {
- width = cnt.width();
- }
- if (height == null) {
- height = cnt.height();
- }
- if (height === 0) {
- height = 'auto';
- }
- me.size = {
- h: height,
- w: width
- };
- me.markers = [];
- me.pathById = {};
- me.container.addClass('kartograph');
- }
+ return CohenSutherland;
- Kartograph.prototype.createSVGLayer = function(id) {
- var about, cnt, lid, me, paper, svg, vp, _ref4;
- me = this;
- if ((_ref4 = me._layerCnt) == null) {
- me._layerCnt = 0;
- }
- lid = me._layerCnt++;
- vp = me.viewport;
- cnt = me.container;
- paper = Raphael(cnt[0], vp.width, vp.height);
- svg = $(paper.canvas);
- svg.css({
- position: 'absolute',
- top: '0px',
- left: '0px',
- 'z-index': lid + 5
- });
- if (cnt.css('position') === 'static') {
- cnt.css({
- position: 'relative',
- height: vp.height + 'px'
- });
- }
- svg.addClass(id);
- about = $('desc', paper.canvas).text();
- $('desc', paper.canvas).text(about.replace('with ', 'with kartograph ' + kartograph.version + ' and '));
- return paper;
- };
+ })();
- Kartograph.prototype.createHTMLLayer = function(id) {
- var cnt, div, lid, me, vp, _ref4;
- me = this;
- vp = me.viewport;
- cnt = me.container;
- if ((_ref4 = me._layerCnt) == null) {
- me._layerCnt = 0;
- }
- lid = me._layerCnt++;
- div = $('<div class="layer ' + id + '" />');
- div.css({
- position: 'absolute',
- top: '0px',
- left: '0px',
- width: vp.width + 'px',
- height: vp.height + 'px',
- 'z-index': lid + 5
- });
- cnt.append(div);
- return div;
- };
+ kartograph.geom.clipping.CohenSutherland = CohenSutherland;
- Kartograph.prototype.loadMap = function(mapurl, callback, opts) {
- var def, me, _base2, _ref4;
- me = this;
- def = $.Deferred();
- me.clear();
- me.opts = opts != null ? opts : {};
- if ((_ref4 = (_base2 = me.opts).zoom) == null) {
- _base2.zoom = 1;
- }
- me.mapLoadCallback = callback;
- me._loadMapDeferred = def;
- me._lastMapUrl = mapurl;
- if (me.cacheMaps && (kartograph.__mapCache[mapurl] != null)) {
- me._mapLoaded(kartograph.__mapCache[mapurl]);
- } else {
- $.ajax({
- url: mapurl,
- dataType: "text",
- success: me._mapLoaded,
- context: me,
- error: function(a, b, c) {
- return warn(a, b, c);
- }
- });
- }
- return def.promise();
- };
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
- Kartograph.prototype.setMap = function(svg, opts) {
- var me, _base2, _ref4;
- me = this;
- me.opts = opts != null ? opts : {};
- if ((_ref4 = (_base2 = me.opts).zoom) == null) {
- _base2.zoom = 1;
- }
- me._lastMapUrl = 'string';
- me._mapLoaded(svg);
- };
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- Kartograph.prototype._mapLoaded = function(xml) {
- var $view, AB, h, halign, me, padding, ratio, valign, vp, w, zoom, _ref4, _ref5, _ref6, _ref7, _ref8;
- me = this;
- if (me.cacheMaps) {
- if ((_ref4 = kartograph.__mapCache) == null) {
- kartograph.__mapCache = {};
- }
- kartograph.__mapCache[me._lastMapUrl] = xml;
- }
- try {
- xml = $(xml);
- } catch (err) {
- warn('something went horribly wrong while parsing svg');
- me._loadMapDeferred.reject('could not parse svg');
- return;
- }
- me.svgSrc = xml;
- $view = $('view', xml);
- log('got svg src', me.svgSrc);
- if (!(me.paper != null)) {
- w = me.size.w;
- h = me.size.h;
- if (h === 'auto') {
- ratio = $view.attr('w') / $view.attr('h');
- h = w / ratio;
- }
- me.viewport = new BBox(0, 0, w, h);
- }
- vp = me.viewport;
- log('got viewport', me.viewport);
- me.viewAB = AB = kartograph.View.fromXML($view[0]);
- log('got first view', me.viewAB);
- padding = (_ref5 = me.opts.padding) != null ? _ref5 : 0;
- halign = (_ref6 = me.opts.halign) != null ? _ref6 : 'center';
- valign = (_ref7 = me.opts.valign) != null ? _ref7 : 'center';
- log('got alignment', halign, valign);
- zoom = (_ref8 = me.opts.zoom) != null ? _ref8 : 1;
- me.viewBC = new kartograph.View(me.viewAB.asBBox(), vp.width * zoom, vp.height * zoom, padding, halign, valign);
- log('got second view', me.viewBC);
- me.proj = kartograph.Proj.fromXML($('proj', $view)[0]);
- log('got projection', me.proj);
- if (me.mapLoadCallback != null) {
- me.mapLoadCallback(me);
- }
- if (me._loadMapDeferred != null) {
- me._loadMapDeferred.resolve(me);
- }
- };
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- Kartograph.prototype.addLayer = function(id, opts) {
- var $paths, checkEvents, evt, layer, layer_id, me, path_id, prop, src_id, svgLayer, svg_path, titles, val, _i, _j, _len, _len1, _ref4, _ref5, _ref6;
- if (opts == null) {
- opts = {};
- }
- /*
- add new layer
- */
-
- me = this;
- if ((_ref4 = me.layerIds) == null) {
- me.layerIds = [];
- }
- if ((_ref5 = me.layers) == null) {
- me.layers = {};
- }
- if (!(me.paper != null)) {
- me.paper = me.createSVGLayer();
- }
- src_id = id;
- if (__type(opts) === 'object') {
- layer_id = opts.name;
- path_id = opts.key;
- titles = opts.title;
- } else {
- opts = {};
- }
- if (layer_id == null) {
- layer_id = src_id;
- }
- svgLayer = $('#' + src_id, me.svgSrc);
- if (svgLayer.length === 0) {
- return;
- }
- layer = new MapLayer(layer_id, path_id, me, opts.filter);
- $paths = $('*', svgLayer[0]);
- for (_i = 0, _len = $paths.length; _i < _len; _i++) {
- svg_path = $paths[_i];
- layer.addPath(svg_path, titles);
- }
- if (layer.paths.length > 0) {
- me.layers[layer_id] = layer;
- me.layerIds.push(layer_id);
- }
- checkEvents = ['click', 'mouseenter', 'mouseleave', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'];
- for (_j = 0, _len1 = checkEvents.length; _j < _len1; _j++) {
- evt = checkEvents[_j];
- if (__type(opts[evt]) === 'function') {
- layer.on(evt, opts[evt]);
- }
- }
- if (opts.styles != null) {
- _ref6 = opts.styles;
- for (prop in _ref6) {
- val = _ref6[prop];
- layer.style(prop, val);
- }
- }
- if (opts.tooltips != null) {
- layer.tooltips(opts.tooltips);
- }
- return me;
- };
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- Kartograph.prototype.getLayer = function(layer_id) {
- /* returns a map layer
- */
-
- var me;
- me = this;
- if (!(me.layers[layer_id] != null)) {
- warn('could not find layer ' + layer_id);
- return null;
- }
- return me.layers[layer_id];
- };
- Kartograph.prototype.getLayerPath = function(layer_id, path_id) {
- var layer, me;
- me = this;
- layer = me.getLayer(layer_id);
- if (layer != null) {
- if (__type(path_id) === 'object') {
- return layer.getPaths(path_id)[0];
- } else {
- return layer.getPath(path_id);
+ Kartograph = (function () {
+
+ function Kartograph(container, width, height) {
+ var cnt, me;
+ me = this;
+ me.container = cnt = $(container);
+ if (width == null) {
+ width = cnt.width();
+ }
+ if (height == null) {
+ height = cnt.height();
+ }
+ if (height === 0) {
+ height = 'auto';
+ }
+ me.size = {
+ h: height,
+ w: width
+ };
+ me.markers = [];
+ me.pathById = {};
+ me.container.addClass('kartograph');
}
- }
- return null;
- };
- Kartograph.prototype.onLayerEvent = function(event, callback, layerId) {
- var me;
- me = this;
- me.getLayer(layerId).on(event, callback);
- return me;
- };
+ Kartograph.prototype.createSVGLayer = function (id) {
+ var about, cnt, lid, me, paper, svg, vp, _ref4;
+ me = this;
+ if ((_ref4 = me._layerCnt) == null) {
+ me._layerCnt = 0;
+ }
+ lid = me._layerCnt++;
+ vp = me.viewport;
+ cnt = me.container;
+ paper = Raphael(cnt[0], vp.width, vp.height);
+ svg = $(paper.canvas);
+ svg.css({
+ position: 'absolute',
+ top: '0px',
+ left: '0px',
+ 'z-index': lid + 5
+ });
+ if (cnt.css('position') === 'static') {
+ cnt.css({
+ position: 'relative',
+ height: vp.height + 'px'
+ });
+ }
+ svg.addClass(id);
+ about = $('desc', paper.canvas).text();
+ $('desc', paper.canvas).text(about.replace('with ', 'with kartograph ' + kartograph.version + ' and '));
+ return paper;
+ };
- Kartograph.prototype.addMarker = function(marker) {
- var me, xy;
- me = this;
- me.markers.push(marker);
- xy = me.viewBC.project(me.viewAB.project(me.proj.project(marker.lonlat.lon, marker.lonlat.lat)));
- return marker.render(xy[0], xy[1], me.container, me.paper);
- };
+ Kartograph.prototype.createHTMLLayer = function (id) {
+ var cnt, div, lid, me, vp, _ref4;
+ me = this;
+ vp = me.viewport;
+ cnt = me.container;
+ if ((_ref4 = me._layerCnt) == null) {
+ me._layerCnt = 0;
+ }
+ lid = me._layerCnt++;
+ div = $('<div class="layer ' + id + '" />');
+ div.css({
+ position: 'absolute',
+ top: '0px',
+ left: '0px',
+ width: vp.width + 'px',
+ height: vp.height + 'px',
+ 'z-index': lid + 5
+ });
+ cnt.append(div);
+ return div;
+ };
- Kartograph.prototype.clearMarkers = function() {
- var marker, me, _i, _len, _ref4;
- me = this;
- _ref4 = me.markers;
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- marker = _ref4[_i];
- marker.clear();
- }
- return me.markers = [];
- };
+ Kartograph.prototype.loadMap = function (mapurl, callback, opts) {
+ var def, me, _base2, _ref4;
+ me = this;
+ def = $.Deferred();
+ me.clear();
+ me.opts = opts != null ? opts : {};
+ if ((_ref4 = (_base2 = me.opts).zoom) == null) {
+ _base2.zoom = 1;
+ }
+ me.mapLoadCallback = callback;
+ me._loadMapDeferred = def;
+ me._lastMapUrl = mapurl;
+ if (me.cacheMaps && (kartograph.__mapCache[mapurl] != null)) {
+ me._mapLoaded(kartograph.__mapCache[mapurl]);
+ } else {
+ $.ajax({
+ url: mapurl,
+ dataType: "text",
+ success: me._mapLoaded,
+ context: me,
+ error: function (a, b, c) {
+ return warn(a, b, c);
+ }
+ });
+ }
+ return def.promise();
+ };
- Kartograph.prototype.fadeIn = function(opts) {
- var dur, duration, id, layer_id, me, path, paths, _ref4, _ref5, _ref6, _results;
- if (opts == null) {
- opts = {};
- }
- me = this;
- layer_id = (_ref4 = opts.layer) != null ? _ref4 : me.layerIds[me.layerIds.length - 1];
- duration = (_ref5 = opts.duration) != null ? _ref5 : 500;
- _ref6 = me.layers[layer_id].pathsById;
- _results = [];
- for (id in _ref6) {
- paths = _ref6[id];
- _results.push((function() {
- var _i, _len, _results1;
- _results1 = [];
- for (_i = 0, _len = paths.length; _i < _len; _i++) {
- path = paths[_i];
- if (__type(duration) === "function") {
- dur = duration(path.data);
+ Kartograph.prototype.setMap = function (svg, opts) {
+ var me, _base2, _ref4;
+ me = this;
+ me.opts = opts != null ? opts : {};
+ if ((_ref4 = (_base2 = me.opts).zoom) == null) {
+ _base2.zoom = 1;
+ }
+ me._lastMapUrl = 'string';
+ me._mapLoaded(svg);
+ };
+
+ Kartograph.prototype._mapLoaded = function (xml) {
+ var $view, AB, h, halign, me, padding, ratio, valign, vp, w, zoom, _ref4, _ref5, _ref6, _ref7, _ref8;
+ me = this;
+ if (me.cacheMaps) {
+ if ((_ref4 = kartograph.__mapCache) == null) {
+ kartograph.__mapCache = {};
+ }
+ kartograph.__mapCache[me._lastMapUrl] = xml;
+ }
+ try {
+ xml = $(xml);
+ } catch (err) {
+ warn('something went horribly wrong while parsing svg');
+ me._loadMapDeferred.reject('could not parse svg');
+ return;
+ }
+ me.svgSrc = xml;
+ $view = $('view', xml);
+ log('got svg src', me.svgSrc);
+ if (!(me.paper != null)) {
+ w = me.size.w;
+ h = me.size.h;
+ if (h === 'auto') {
+ ratio = $view.attr('w') / $view.attr('h');
+ h = w / ratio;
+ }
+ me.viewport = new BBox(0, 0, w, h);
+ }
+ vp = me.viewport;
+ log('got viewport', me.viewport);
+ me.viewAB = AB = kartograph.View.fromXML($view[0]);
+ log('got first view', me.viewAB);
+ padding = (_ref5 = me.opts.padding) != null ? _ref5 : 0;
+ halign = (_ref6 = me.opts.halign) != null ? _ref6 : 'center';
+ valign = (_ref7 = me.opts.valign) != null ? _ref7 : 'center';
+ log('got alignment', halign, valign);
+ zoom = (_ref8 = me.opts.zoom) != null ? _ref8 : 1;
+ me.viewBC = new kartograph.View(me.viewAB.asBBox(), vp.width * zoom, vp.height * zoom, padding, halign, valign);
+ log('got second view', me.viewBC);
+ me.proj = kartograph.Proj.fromXML($('proj', $view)[0]);
+ log('got projection', me.proj);
+ if (me.mapLoadCallback != null) {
+ me.mapLoadCallback(me);
+ }
+ if (me._loadMapDeferred != null) {
+ me._loadMapDeferred.resolve(me);
+ }
+ };
+
+ Kartograph.prototype.addLayer = function (id, opts) {
+ var $paths, checkEvents, evt, layer, layer_id, me, path_id, prop, src_id, svgLayer, svg_path, titles, val, _i, _j, _len, _len1, _ref4, _ref5, _ref6;
+ if (opts == null) {
+ opts = {};
+ }
+ /*
+ add new layer
+ */
+
+ me = this;
+ if ((_ref4 = me.layerIds) == null) {
+ me.layerIds = [];
+ }
+ if ((_ref5 = me.layers) == null) {
+ me.layers = {};
+ }
+ if (!(me.paper != null)) {
+ me.paper = me.createSVGLayer();
+ }
+ src_id = id;
+ if (__type(opts) === 'object') {
+ layer_id = opts.name;
+ path_id = opts.key;
+ titles = opts.title;
} else {
- dur = duration;
- }
- path.svgPath.attr('opacity', 0);
- _results1.push(path.svgPath.animate({
- opacity: 1
- }, dur));
- }
- return _results1;
- })());
- }
- return _results;
- };
+ opts = {};
+ }
+ if (layer_id == null) {
+ layer_id = src_id;
+ }
+ svgLayer = $('#' + src_id, me.svgSrc);
+ if (svgLayer.length === 0) {
+ return;
+ }
+ layer = new MapLayer(layer_id, path_id, me, opts.filter);
+ $paths = $('*', svgLayer[0]);
+ for (_i = 0, _len = $paths.length; _i < _len; _i++) {
+ svg_path = $paths[_i];
+ layer.addPath(svg_path, titles);
+ }
+ if (layer.paths.length > 0) {
+ me.layers[layer_id] = layer;
+ me.layerIds.push(layer_id);
+ }
+ checkEvents = ['click', 'mouseenter', 'mouseleave', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'];
+ for (_j = 0, _len1 = checkEvents.length; _j < _len1; _j++) {
+ evt = checkEvents[_j];
+ if (__type(opts[evt]) === 'function') {
+ layer.on(evt, opts[evt]);
+ }
+ }
+ if (opts.styles != null) {
+ _ref6 = opts.styles;
+ for (prop in _ref6) {
+ val = _ref6[prop];
+ layer.style(prop, val);
+ }
+ }
+ if (opts.tooltips != null) {
+ layer.tooltips(opts.tooltips);
+ }
+ return me;
+ };
- /*
- end of public API
- */
-
-
- Kartograph.prototype.loadCoastline = function() {
- var me;
- me = this;
- return $.ajax({
- url: 'coastline.json',
- success: me.renderCoastline,
- context: me
- });
- };
+ Kartograph.prototype.getLayer = function (layer_id) {
+ /* returns a map layer
+ */
- Kartograph.prototype.resize = function(w, h) {
- /*
- forces redraw of every layer
- */
-
- var cnt, halign, id, layer, me, padding, sg, valign, vp, zoom, _i, _len, _ref4, _ref5, _ref6, _ref7, _ref8;
- me = this;
- cnt = me.container;
- if (w == null) {
- w = cnt.width();
- }
- if (h == null) {
- h = cnt.height();
- }
- me.viewport = vp = new kartograph.BBox(0, 0, w, h);
- if (me.paper != null) {
- me.paper.setSize(vp.width, vp.height);
- }
- vp = me.viewport;
- padding = (_ref4 = me.opts.padding) != null ? _ref4 : 0;
- halign = (_ref5 = me.opts.halign) != null ? _ref5 : 'center';
- valign = (_ref6 = me.opts.valign) != null ? _ref6 : 'center';
- zoom = me.opts.zoom;
- me.viewBC = new kartograph.View(me.viewAB.asBBox(), vp.width * zoom, vp.height * zoom, padding, halign, valign);
- _ref7 = me.layers;
- for (id in _ref7) {
- layer = _ref7[id];
- layer.setView(me.viewBC);
- }
- if (me.symbolGroups != null) {
- _ref8 = me.symbolGroups;
- for (_i = 0, _len = _ref8.length; _i < _len; _i++) {
- sg = _ref8[_i];
- sg.onResize();
- }
- }
- };
+ var me;
+ me = this;
+ if (!(me.layers[layer_id] != null)) {
+ warn('could not find layer ' + layer_id);
+ return null;
+ }
+ return me.layers[layer_id];
+ };
- Kartograph.prototype.lonlat2xy = function(lonlat) {
- var a, me;
- me = this;
- if (lonlat.length === 2) {
- lonlat = new kartograph.LonLat(lonlat[0], lonlat[1]);
- }
- if (lonlat.length === 3) {
- lonlat = new kartograph.LonLat(lonlat[0], lonlat[1], lonlat[2]);
- }
- a = me.proj.project(lonlat.lon, lonlat.lat, lonlat.alt);
- return me.viewBC.project(me.viewAB.project(a));
- };
+ Kartograph.prototype.getLayerPath = function (layer_id, path_id) {
+ var layer, me;
+ me = this;
+ layer = me.getLayer(layer_id);
+ if (layer != null) {
+ if (__type(path_id) === 'object') {
+ return layer.getPaths(path_id)[0];
+ } else {
+ return layer.getPath(path_id);
+ }
+ }
+ return null;
+ };
- Kartograph.prototype.showZoomControls = function() {
- var me;
- me = this;
- me.zc = new PanAndZoomControl(me);
- return me;
- };
+ Kartograph.prototype.onLayerEvent = function (event, callback, layerId) {
+ var me;
+ me = this;
+ me.getLayer(layerId).on(event, callback);
+ return me;
+ };
- Kartograph.prototype.addSymbolGroup = function(symbolgroup) {
- var me, _ref4;
- me = this;
- if ((_ref4 = me.symbolGroups) == null) {
- me.symbolGroups = [];
- }
- return me.symbolGroups.push(symbolgroup);
- };
+ Kartograph.prototype.addMarker = function (marker) {
+ var me, xy;
+ me = this;
+ me.markers.push(marker);
+ xy = me.viewBC.project(me.viewAB.project(me.proj.project(marker.lonlat.lon, marker.lonlat.lat)));
+ return marker.render(xy[0], xy[1], me.container, me.paper);
+ };
- Kartograph.prototype.removeSymbols = function(index) {
- var me, sg, _i, _len, _ref4, _results;
- me = this;
- if (index != null) {
- return me.symbolGroups[index].remove();
- } else {
- _ref4 = me.symbolGroups;
- _results = [];
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- sg = _ref4[_i];
- _results.push(sg.remove());
- }
- return _results;
- }
- };
+ Kartograph.prototype.clearMarkers = function () {
+ var marker, me, _i, _len, _ref4;
+ me = this;
+ _ref4 = me.markers;
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ marker = _ref4[_i];
+ marker.clear();
+ }
+ return me.markers = [];
+ };
- Kartograph.prototype.clear = function() {
- var id, me, sg, _i, _len, _ref4;
- me = this;
- if (me.layers != null) {
- for (id in me.layers) {
- me.layers[id].remove();
- }
- me.layers = {};
- me.layerIds = [];
- }
- if (me.symbolGroups != null) {
- _ref4 = me.symbolGroups;
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- sg = _ref4[_i];
- sg.remove();
- }
- me.symbolGroups = [];
- }
- if (me.paper != null) {
- $(me.paper.canvas).remove();
- return me.paper = void 0;
- }
- };
+ Kartograph.prototype.fadeIn = function (opts) {
+ var dur, duration, id, layer_id, me, path, paths, _ref4, _ref5, _ref6, _results;
+ if (opts == null) {
+ opts = {};
+ }
+ me = this;
+ layer_id = (_ref4 = opts.layer) != null ? _ref4 : me.layerIds[me.layerIds.length - 1];
+ duration = (_ref5 = opts.duration) != null ? _ref5 : 500;
+ _ref6 = me.layers[layer_id].pathsById;
+ _results = [];
+ for (id in _ref6) {
+ paths = _ref6[id];
+ _results.push((function () {
+ var _i, _len, _results1;
+ _results1 = [];
+ for (_i = 0, _len = paths.length; _i < _len; _i++) {
+ path = paths[_i];
+ if (__type(duration) === "function") {
+ dur = duration(path.data);
+ } else {
+ dur = duration;
+ }
+ path.svgPath.attr('opacity', 0);
+ _results1.push(path.svgPath.animate({
+ opacity: 1
+ }, dur));
+ }
+ return _results1;
+ })());
+ }
+ return _results;
+ };
- Kartograph.prototype.loadCSS = function(url, callback) {
- /*
- loads a stylesheet
- */
-
- var me;
- me = this;
- if ($.browser.msie) {
- return $.ajax({
- url: url,
- dataType: 'text',
- success: function(resp) {
- me.styles = kartograph.parsecss(resp);
- return callback();
- },
- error: function(a, b, c) {
- return warn('error while loading ' + url, a, b, c);
- }
- });
- } else {
- $('body').append('<link rel="stylesheet" href="' + url + '" />');
- return callback();
- }
- };
+ /*
+ end of public API
+ */
- Kartograph.prototype.applyCSS = function(el, className) {
- /*
- applies pre-loaded css styles to
- raphael elements
- */
- var classes, k, me, p, props, sel, selectors, _i, _j, _len, _len1, _ref4, _ref5, _ref6, _ref7;
- me = this;
- if (!(me.styles != null)) {
- return el;
- }
- if ((_ref4 = me._pathTypes) == null) {
- me._pathTypes = ["path", "circle", "rectangle", "ellipse"];
- }
- if ((_ref5 = me._regardStyles) == null) {
- me._regardStyles = ["fill", "stroke", "fill-opacity", "stroke-width", "stroke-opacity"];
- }
- for (sel in me.styles) {
- p = sel;
- _ref6 = p.split(',');
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- selectors = _ref6[_i];
- p = selectors.split(' ');
- p = p[p.length - 1];
- p = p.split(':');
- if (p.length > 1) {
- continue;
- }
- p = p[0].split('.');
- classes = p.slice(1);
- if (classes.length > 0 && classes.indexOf(className) < 0) {
- continue;
- }
- p = p[0];
- if (me._pathTypes.indexOf(p) >= 0 && p !== el.type) {
- continue;
- }
- props = me.styles[sel];
- _ref7 = me._regardStyles;
- for (_j = 0, _len1 = _ref7.length; _j < _len1; _j++) {
- k = _ref7[_j];
- if (props[k] != null) {
- el.attr(k, props[k]);
- }
- }
- }
- }
- return el;
- };
+ Kartograph.prototype.loadCoastline = function () {
+ var me;
+ me = this;
+ return $.ajax({
+ url: 'coastline.json',
+ success: me.renderCoastline,
+ context: me
+ });
+ };
- Kartograph.prototype.style = function(layer, prop, value, duration, delay) {
- var me;
- me = this;
- layer = me.getLayer(layer);
- if (layer != null) {
- return layer.style(prop, value, duration, delay);
- }
- };
+ Kartograph.prototype.resize = function (w, h) {
+ /*
+ forces redraw of every layer
+ */
- return Kartograph;
+ var cnt, halign, id, layer, me, padding, sg, valign, vp, zoom, _i, _len, _ref4, _ref5, _ref6, _ref7, _ref8;
+ me = this;
+ cnt = me.container;
+ if (w == null) {
+ w = cnt.width();
+ }
+ if (h == null) {
+ h = cnt.height();
+ }
+ me.viewport = vp = new kartograph.BBox(0, 0, w, h);
+ if (me.paper != null) {
+ me.paper.setSize(vp.width, vp.height);
+ }
+ vp = me.viewport;
+ padding = (_ref4 = me.opts.padding) != null ? _ref4 : 0;
+ halign = (_ref5 = me.opts.halign) != null ? _ref5 : 'center';
+ valign = (_ref6 = me.opts.valign) != null ? _ref6 : 'center';
+ zoom = me.opts.zoom;
+ me.viewBC = new kartograph.View(me.viewAB.asBBox(), vp.width * zoom, vp.height * zoom, padding, halign, valign);
+ _ref7 = me.layers;
+ for (id in _ref7) {
+ layer = _ref7[id];
+ layer.setView(me.viewBC);
+ }
+ if (me.symbolGroups != null) {
+ _ref8 = me.symbolGroups;
+ for (_i = 0, _len = _ref8.length; _i < _len; _i++) {
+ sg = _ref8[_i];
+ sg.onResize();
+ }
+ }
+ };
- })();
+ Kartograph.prototype.lonlat2xy = function (lonlat) {
+ var a, me;
+ me = this;
+ if (lonlat.length === 2) {
+ lonlat = new kartograph.LonLat(lonlat[0], lonlat[1]);
+ }
+ if (lonlat.length === 3) {
+ lonlat = new kartograph.LonLat(lonlat[0], lonlat[1], lonlat[2]);
+ }
+ a = me.proj.project(lonlat.lon, lonlat.lat, lonlat.alt);
+ return me.viewBC.project(me.viewAB.project(a));
+ };
- kartograph.Kartograph = Kartograph;
+ Kartograph.prototype.showZoomControls = function () {
+ var me;
+ me = this;
+ me.zc = new PanAndZoomControl(me);
+ return me;
+ };
- kartograph.map = function(container, width, height) {
- /* short-hand constructor
- */
- return new Kartograph(container, width, height);
- };
+ Kartograph.prototype.addSymbolGroup = function (symbolgroup) {
+ var me, _ref4;
+ me = this;
+ if ((_ref4 = me.symbolGroups) == null) {
+ me.symbolGroups = [];
+ }
+ return me.symbolGroups.push(symbolgroup);
+ };
- kartograph.__mapCache = {};
+ Kartograph.prototype.removeSymbols = function (index) {
+ var me, sg, _i, _len, _ref4, _results;
+ me = this;
+ if (index != null) {
+ return me.symbolGroups[index].remove();
+ } else {
+ _ref4 = me.symbolGroups;
+ _results = [];
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ sg = _ref4[_i];
+ _results.push(sg.remove());
+ }
+ return _results;
+ }
+ };
+
+ Kartograph.prototype.clear = function () {
+ var id, me, sg, _i, _len, _ref4;
+ me = this;
+ if (me.layers != null) {
+ for (id in me.layers) {
+ me.layers[id].remove();
+ }
+ me.layers = {};
+ me.layerIds = [];
+ }
+ if (me.symbolGroups != null) {
+ _ref4 = me.symbolGroups;
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ sg = _ref4[_i];
+ sg.remove();
+ }
+ me.symbolGroups = [];
+ }
+ if (me.paper != null) {
+ $(me.paper.canvas).remove();
+ return me.paper = void 0;
+ }
+ };
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
+ Kartograph.prototype.loadCSS = function (url, callback) {
+ /*
+ loads a stylesheet
+ */
+
+ var me;
+ me = this;
+ if ($.browser.msie) {
+ return $.ajax({
+ url: url,
+ dataType: 'text',
+ success: function (resp) {
+ me.styles = kartograph.parsecss(resp);
+ return callback();
+ },
+ error: function (a, b, c) {
+ return warn('error while loading ' + url, a, b, c);
+ }
+ });
+ } else {
+ $('body').append('<link rel="stylesheet" href="' + url + '" />');
+ return callback();
+ }
+ };
+ Kartograph.prototype.applyCSS = function (el, className) {
+ /*
+ applies pre-loaded css styles to
+ raphael elements
+ */
- LonLat = (function() {
- /*
- represents a Point
- */
-
- function LonLat(lon, lat, alt) {
- if (alt == null) {
- alt = 0;
- }
- this.lon = Number(lon);
- this.lat = Number(lat);
- this.alt = Number(alt);
- }
+ var classes, k, me, p, props, sel, selectors, _i, _j, _len, _len1, _ref4, _ref5, _ref6, _ref7;
+ me = this;
+ if (!(me.styles != null)) {
+ return el;
+ }
+ if ((_ref4 = me._pathTypes) == null) {
+ me._pathTypes = ["path", "circle", "rectangle", "ellipse"];
+ }
+ if ((_ref5 = me._regardStyles) == null) {
+ me._regardStyles = ["fill", "stroke", "fill-opacity", "stroke-width", "stroke-opacity"];
+ }
+ for (sel in me.styles) {
+ p = sel;
+ _ref6 = p.split(',');
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ selectors = _ref6[_i];
+ p = selectors.split(' ');
+ p = p[p.length - 1];
+ p = p.split(':');
+ if (p.length > 1) {
+ continue;
+ }
+ p = p[0].split('.');
+ classes = p.slice(1);
+ if (classes.length > 0 && classes.indexOf(className) < 0) {
+ continue;
+ }
+ p = p[0];
+ if (me._pathTypes.indexOf(p) >= 0 && p !== el.type) {
+ continue;
+ }
+ props = me.styles[sel];
+ _ref7 = me._regardStyles;
+ for (_j = 0, _len1 = _ref7.length; _j < _len1; _j++) {
+ k = _ref7[_j];
+ if (props[k] != null) {
+ el.attr(k, props[k]);
+ }
+ }
+ }
+ }
+ return el;
+ };
+
+ Kartograph.prototype.style = function (layer, prop, value, duration, delay) {
+ var me;
+ me = this;
+ layer = me.getLayer(layer);
+ if (layer != null) {
+ return layer.style(prop, value, duration, delay);
+ }
+ };
- LonLat.prototype.distance = function(ll) {
- var R, a, c, dLat, dLon, deg2rad, lat1, lat2, me;
- me = this;
- R = 6371;
- deg2rad = Math.PI / 180;
- dLat = (ll.lat - me.lat) * deg2rad;
- dLon = (ll.lon - me.lon) * deg2rad;
- lat1 = me.lat * deg2rad;
- lat2 = ll.lat * deg2rad;
- a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
- c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
- return R * c;
+ return Kartograph;
+
+ })();
+
+ kartograph.Kartograph = Kartograph;
+
+ kartograph.map = function (container, width, height) {
+ /* short-hand constructor
+ */
+ return new Kartograph(container, width, height);
};
- return LonLat;
+ kartograph.__mapCache = {};
- })();
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
- LatLon = (function(_super) {
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- __extends(LatLon, _super);
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- function LatLon(lat, lon, alt) {
- if (alt == null) {
- alt = 0;
- }
- LatLon.__super__.constructor.call(this, lon, lat, alt);
- }
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- return LatLon;
-
- })(LonLat);
-
- kartograph.LonLat = LonLat;
-
- kartograph.LatLon = LatLon;
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- MapLayer = (function() {
-
- function MapLayer(layer_id, path_id, map, filter) {
- var me;
- me = this;
- me.id = layer_id;
- me.path_id = path_id;
- me.paper = map.paper;
- me.view = map.viewBC;
- me.map = map;
- me.filter = filter;
- }
- MapLayer.prototype.addPath = function(svg_path, titles) {
- var layerPath, me, _base2, _name, _ref4, _ref5, _ref6;
- me = this;
- if ((_ref4 = me.paths) == null) {
- me.paths = [];
- }
- layerPath = new MapLayerPath(svg_path, me.id, me.map, titles);
- if (__type(me.filter) === 'function') {
- if (me.filter(layerPath.data) === false) {
- layerPath.remove();
- return;
- }
- }
- me.paths.push(layerPath);
- if (me.path_id != null) {
- if ((_ref5 = me.pathsById) == null) {
- me.pathsById = {};
- }
- if ((_ref6 = (_base2 = me.pathsById)[_name = layerPath.data[me.path_id]]) == null) {
- _base2[_name] = [];
+ LonLat = (function () {
+ /*
+ represents a Point
+ */
+
+ function LonLat(lon, lat, alt) {
+ if (alt == null) {
+ alt = 0;
+ }
+ this.lon = Number(lon);
+ this.lat = Number(lat);
+ this.alt = Number(alt);
}
- return me.pathsById[layerPath.data[me.path_id]].push(layerPath);
- }
- };
- MapLayer.prototype.hasPath = function(id) {
- var me;
- me = this;
- return (me.pathsById != null) && (me.pathsById[id] != null);
- };
+ LonLat.prototype.distance = function (ll) {
+ var R, a, c, dLat, dLon, deg2rad, lat1, lat2, me;
+ me = this;
+ R = 6371;
+ deg2rad = Math.PI / 180;
+ dLat = (ll.lat - me.lat) * deg2rad;
+ dLon = (ll.lon - me.lon) * deg2rad;
+ lat1 = me.lat * deg2rad;
+ lat2 = ll.lat * deg2rad;
+ a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ return R * c;
+ };
- MapLayer.prototype.getPathsData = function() {
- /* returns a list of all shape data dictionaries
- */
-
- var me, path, pd, _i, _len, _ref4;
- me = this;
- pd = [];
- _ref4 = me.paths;
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- path = _ref4[_i];
- pd.push(path.data);
- }
- return pd;
- };
+ return LonLat;
- MapLayer.prototype.getPath = function(id) {
- var me;
- me = this;
- if (me.hasPath(id)) {
- return me.pathsById[id][0];
- }
- return null;
- };
+ })();
- MapLayer.prototype.getPaths = function(query) {
- var key, match, matches, me, path, _i, _len, _ref4;
- me = this;
- matches = [];
- if (__type(query) === 'object') {
- _ref4 = me.paths;
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- path = _ref4[_i];
- match = true;
- for (key in query) {
- match = match && path.data[key] === query[key];
- }
- if (match) {
- matches.push(path);
- }
+ LatLon = (function (_super) {
+
+ __extends(LatLon, _super);
+
+ function LatLon(lat, lon, alt) {
+ if (alt == null) {
+ alt = 0;
+ }
+ LatLon.__super__.constructor.call(this, lon, lat, alt);
}
- }
- return matches;
- };
- MapLayer.prototype.setView = function(view) {
- /*
- # after resizing of the map, each layer gets a new view
- */
-
- var me, path, _i, _len, _ref4;
- me = this;
- _ref4 = me.paths;
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- path = _ref4[_i];
- path.setView(view);
- }
- return me;
- };
+ return LatLon;
- MapLayer.prototype.remove = function() {
- /*
- removes every path
- */
-
- var me, path, _i, _len, _ref4, _results;
- me = this;
- _ref4 = me.paths;
- _results = [];
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- path = _ref4[_i];
- _results.push(path.remove());
- }
- return _results;
- };
+ })(LonLat);
- MapLayer.prototype.style = function(prop, value, duration, delay) {
- var anim, at, dly, dur, key, me, path, val, _i, _len, _ref4;
- me = this;
- if (__type(prop) === "object") {
- for (key in prop) {
- val = prop[key];
- me.style(key, val);
- }
- return me;
- }
- if (duration == null) {
- duration = 0;
- }
- if (delay == null) {
- delay = 0;
- }
- _ref4 = me.paths;
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- path = _ref4[_i];
- val = resolve(value, path.data);
- dur = resolve(duration, path.data);
- dly = resolve(delay, path.data);
- if (dur > 0) {
- at = {};
- at[prop] = val;
- anim = Raphael.animation(at, dur * 1000);
- path.svgPath.animate(anim.delay(dly * 1000));
- } else {
- path.svgPath.attr(prop, val);
+ kartograph.LonLat = LonLat;
+
+ kartograph.LatLon = LatLon;
+
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ MapLayer = (function () {
+
+ function MapLayer(layer_id, path_id, map, filter) {
+ var me;
+ me = this;
+ me.id = layer_id;
+ me.path_id = path_id;
+ me.paper = map.paper;
+ me.view = map.viewBC;
+ me.map = map;
+ me.filter = filter;
}
- }
- return me;
- };
- MapLayer.prototype.on = function(event, callback) {
- var EventContext, ctx, me, path, _i, _len, _ref4;
- me = this;
- EventContext = (function() {
+ MapLayer.prototype.addPath = function (svg_path, titles) {
+ var layerPath, me, _base2, _name, _ref4, _ref5, _ref6;
+ me = this;
+ if ((_ref4 = me.paths) == null) {
+ me.paths = [];
+ }
+ layerPath = new MapLayerPath(svg_path, me.id, me.map, titles);
+ if (__type(me.filter) === 'function') {
+ if (me.filter(layerPath.data) === false) {
+ layerPath.remove();
+ return;
+ }
+ }
+ me.paths.push(layerPath);
+ if (me.path_id != null) {
+ if ((_ref5 = me.pathsById) == null) {
+ me.pathsById = {};
+ }
+ if ((_ref6 = (_base2 = me.pathsById)[_name = layerPath.data[me.path_id]]) == null) {
+ _base2[_name] = [];
+ }
+ return me.pathsById[layerPath.data[me.path_id]].push(layerPath);
+ }
+ };
- function EventContext(type, cb, layer) {
- this.type = type;
- this.cb = cb;
- this.layer = layer;
- this.handle = __bind(this.handle, this);
+ MapLayer.prototype.hasPath = function (id) {
+ var me;
+ me = this;
+ return (me.pathsById != null) && (me.pathsById[id] != null);
+ };
- }
+ MapLayer.prototype.getPathsData = function () {
+ /* returns a list of all shape data dictionaries
+ */
+
+ var me, path, pd, _i, _len, _ref4;
+ me = this;
+ pd = [];
+ _ref4 = me.paths;
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ path = _ref4[_i];
+ pd.push(path.data);
+ }
+ return pd;
+ };
- EventContext.prototype.handle = function(e) {
- var path;
- me = this;
- path = me.layer.map.pathById[e.target.getAttribute('id')];
- return me.cb(path.data, path.svgPath, e);
+ MapLayer.prototype.getPath = function (id) {
+ var me;
+ me = this;
+ if (me.hasPath(id)) {
+ return me.pathsById[id][0];
+ }
+ return null;
};
- return EventContext;
+ MapLayer.prototype.getPaths = function (query) {
+ var key, match, matches, me, path, _i, _len, _ref4;
+ me = this;
+ matches = [];
+ if (__type(query) === 'object') {
+ _ref4 = me.paths;
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ path = _ref4[_i];
+ match = true;
+ for (key in query) {
+ match = match && path.data[key] === query[key];
+ }
+ if (match) {
+ matches.push(path);
+ }
+ }
+ }
+ return matches;
+ };
- })();
- ctx = new EventContext(event, callback, me);
- _ref4 = me.paths;
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- path = _ref4[_i];
- $(path.svgPath.node).bind(event, ctx.handle);
- }
- return me;
- };
+ MapLayer.prototype.setView = function (view) {
+ /*
+ # after resizing of the map, each layer gets a new view
+ */
+
+ var me, path, _i, _len, _ref4;
+ me = this;
+ _ref4 = me.paths;
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ path = _ref4[_i];
+ path.setView(view);
+ }
+ return me;
+ };
- MapLayer.prototype.tooltips = function(content, delay) {
- var me, path, setTooltip, tt, _i, _len, _ref4;
- me = this;
- setTooltip = function(path, tt) {
- var cfg;
- cfg = {
- position: {
- target: 'mouse',
- viewport: $(window),
- adjust: {
- x: 7,
- y: 7
- }
- },
- show: {
- delay: delay != null ? delay : 20
- },
- events: {
- show: function(evt, api) {
- return $('.qtip').filter(function() {
- return this !== api.elements.tooltip.get(0);
- }).hide();
- }
- },
- content: {}
- };
- if (tt != null) {
- if (typeof tt === "string") {
- cfg.content.text = tt;
- } else if ($.isArray(tt)) {
- cfg.content.title = tt[0];
- cfg.content.text = tt[1];
- }
- } else {
- cfg.content.text = 'n/a';
- }
- return $(path.svgPath.node).qtip(cfg);
- };
- _ref4 = me.paths;
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- path = _ref4[_i];
- tt = resolve(content, path.data);
- setTooltip(path, tt);
- }
- return me;
- };
+ MapLayer.prototype.remove = function () {
+ /*
+ removes every path
+ */
+
+ var me, path, _i, _len, _ref4, _results;
+ me = this;
+ _ref4 = me.paths;
+ _results = [];
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ path = _ref4[_i];
+ _results.push(path.remove());
+ }
+ return _results;
+ };
- MapLayer.prototype.sort = function(sortBy) {
- var lp, me, path, _i, _len, _ref4;
- me = this;
- me.paths.sort(function(a, b) {
- var av, bv, _ref4;
- av = sortBy(a.data);
- bv = sortBy(b.data);
- if (av === bv) {
- return 0;
- }
- return (_ref4 = av > bv) != null ? _ref4 : {
- 1: -1
- };
- });
- lp = false;
- _ref4 = me.paths;
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- path = _ref4[_i];
- if (lp) {
- path.svgPath.insertAfter(lp.svgPath);
- }
- lp = path;
- }
- return me;
- };
+ MapLayer.prototype.style = function (prop, value, duration, delay) {
+ var anim, at, dly, dur, key, me, path, val, _i, _len, _ref4;
+ me = this;
+ if (__type(prop) === "object") {
+ for (key in prop) {
+ val = prop[key];
+ me.style(key, val);
+ }
+ return me;
+ }
+ if (duration == null) {
+ duration = 0;
+ }
+ if (delay == null) {
+ delay = 0;
+ }
+ _ref4 = me.paths;
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ path = _ref4[_i];
+ val = resolve(value, path.data);
+ dur = resolve(duration, path.data);
+ dly = resolve(delay, path.data);
+ if (dur > 0) {
+ at = {};
+ at[prop] = val;
+ anim = Raphael.animation(at, dur * 1000);
+ path.svgPath.animate(anim.delay(dly * 1000));
+ } else {
+ path.svgPath.attr(prop, val);
+ }
+ }
+ return me;
+ };
- return MapLayer;
+ MapLayer.prototype.on = function (event, callback) {
+ var EventContext, ctx, me, path, _i, _len, _ref4;
+ me = this;
+ EventContext = (function () {
- })();
+ function EventContext(type, cb, layer) {
+ this.type = type;
+ this.cb = cb;
+ this.layer = layer;
+ this.handle = __bind(this.handle, this);
- resolve = function(prop, data) {
- if (__type(prop) === 'function') {
- return prop(data);
- }
- return prop;
- };
-
- map_layer_path_uid = 0;
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- MapLayerPath = (function() {
-
- function MapLayerPath(svg_path, layer_id, map, titles) {
- var attr, data, i, me, paper, path, title, uid, v, view, vn, _i, _ref4;
- me = this;
- paper = map.paper;
- view = map.viewBC;
- me.path = path = kartograph.geom.Path.fromSVG(svg_path);
- me.vpath = view.projectPath(path);
- me.svgPath = me.vpath.toSVG(paper);
- if (!(map.styles != null)) {
- me.svgPath.node.setAttribute('class', layer_id);
- } else {
- map.applyCSS(me.svgPath, layer_id);
- }
- uid = 'path_' + map_layer_path_uid++;
- me.svgPath.node.setAttribute('id', uid);
- map.pathById[uid] = me;
- data = {};
- for (i = _i = 0, _ref4 = svg_path.attributes.length - 1; 0 <= _ref4 ? _i <= _ref4 : _i >= _ref4; i = 0 <= _ref4 ? ++_i : --_i) {
- attr = svg_path.attributes[i];
- if (attr.name.substr(0, 5) === "data-") {
- v = attr.value;
- vn = Number(v);
- if (v.trim() !== "" && vn === v && !isNaN(vn)) {
- v = vn;
- }
- data[attr.name.substr(5)] = v;
- }
- }
- me.data = data;
- if (__type(titles) === 'string') {
- title = titles;
- } else if (__type(titles) === 'function') {
- title = titles(data);
- }
- if (title != null) {
- me.svgPath.attr('title', title);
- }
- }
+ }
- MapLayerPath.prototype.setView = function(view) {
- var me, path, path_str;
- me = this;
- path = view.projectPath(me.path);
- me.vpath = path;
- if (me.path.type === "path") {
- path_str = path.svgString();
- return me.svgPath.attr({
- path: path_str
- });
- } else if (me.path.type === "circle") {
- return me.svgPath.attr({
- cx: path.x,
- cy: path.y,
- r: path.r
- });
- }
- };
+ EventContext.prototype.handle = function (e) {
+ var path;
+ me = this;
+ path = me.layer.map.pathById[e.target.getAttribute('id')];
+ return me.cb(path.data, path.svgPath, e);
+ };
+
+ return EventContext;
+
+ })();
+ ctx = new EventContext(event, callback, me);
+ _ref4 = me.paths;
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ path = _ref4[_i];
+ $(path.svgPath.node).bind(event, ctx.handle);
+ }
+ return me;
+ };
+
+ MapLayer.prototype.tooltips = function (content, delay) {
+ var me, path, setTooltip, tt, _i, _len, _ref4;
+ me = this;
+ setTooltip = function (path, tt) {
+ var cfg;
+ cfg = {
+ position: {
+ target: 'mouse',
+ viewport: $(window),
+ adjust: {
+ x: 7,
+ y: 7
+ }
+ },
+ show: {
+ delay: delay != null ? delay : 20
+ },
+ events: {
+ show: function (evt, api) {
+ return $('.qtip').filter(function () {
+ return this !== api.elements.tooltip.get(0);
+ }).hide();
+ }
+ },
+ content: {}
+ };
+ if (tt != null) {
+ if (typeof tt === "string") {
+ cfg.content.text = tt;
+ } else if ($.isArray(tt)) {
+ cfg.content.title = tt[0];
+ cfg.content.text = tt[1];
+ }
+ } else {
+ cfg.content.text = 'n/a';
+ }
+ return $(path.svgPath.node).qtip(cfg);
+ };
+ _ref4 = me.paths;
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ path = _ref4[_i];
+ tt = resolve(content, path.data);
+ setTooltip(path, tt);
+ }
+ return me;
+ };
- MapLayerPath.prototype.remove = function() {
- var me;
- me = this;
- return me.svgPath.remove();
+ MapLayer.prototype.sort = function (sortBy) {
+ var lp, me, path, _i, _len, _ref4;
+ me = this;
+ me.paths.sort(function (a, b) {
+ var av, bv, _ref4;
+ av = sortBy(a.data);
+ bv = sortBy(b.data);
+ if (av === bv) {
+ return 0;
+ }
+ return (_ref4 = av > bv) != null ? _ref4 : {
+ 1: -1
+ };
+ });
+ lp = false;
+ _ref4 = me.paths;
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ path = _ref4[_i];
+ if (lp) {
+ path.svgPath.insertAfter(lp.svgPath);
+ }
+ lp = path;
+ }
+ return me;
+ };
+
+ return MapLayer;
+
+ })();
+
+ resolve = function (prop, data) {
+ if (__type(prop) === 'function') {
+ return prop(data);
+ }
+ return prop;
};
- return MapLayerPath;
-
- })();
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- /*
- This is a reduced version of Danial Wachsstocks jQuery based CSS parser
- Everything is removed but the core css-to-object parsing
-
- jQuery based CSS parser
- documentation: http://youngisrael-stl.org/wordpress/2009/01/16/jquery-css-parser/
- Version: 1.3
- Copyright (c) 2011 Daniel Wachsstock
- MIT license:
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use,
- copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following
- conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
- kartograph.parsecss = function(str, callback) {
- var css, k, props, ret, v, _i, _len, _ref4;
- ret = {};
- str = munge(str);
- _ref4 = str.split('`b%');
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- css = _ref4[_i];
- css = css.split('%b`');
- if (css.length < 2) {
- continue;
- }
- css[0] = restore(css[0]);
- props = parsedeclarations(css[1]);
- if (ret[css[0]] != null) {
- for (k in props) {
- v = props[k];
- ret[css[0]][k] = v;
+ map_layer_path_uid = 0;
+
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ MapLayerPath = (function () {
+
+ function MapLayerPath(svg_path, layer_id, map, titles) {
+ var attr, data, i, me, paper, path, title, uid, v, view, vn, _i, _ref4;
+ me = this;
+ paper = map.paper;
+ view = map.viewBC;
+ me.path = path = kartograph.geom.Path.fromSVG(svg_path);
+ me.vpath = view.projectPath(path);
+ me.svgPath = me.vpath.toSVG(paper);
+ if (!(map.styles != null)) {
+ me.svgPath.node.setAttribute('class', layer_id);
+ } else {
+ map.applyCSS(me.svgPath, layer_id);
+ }
+ uid = 'path_' + map_layer_path_uid++;
+ me.svgPath.node.setAttribute('id', uid);
+ map.pathById[uid] = me;
+ data = {};
+ for (i = _i = 0, _ref4 = svg_path.attributes.length - 1; 0 <= _ref4 ? _i <= _ref4 : _i >= _ref4; i = 0 <= _ref4 ? ++_i : --_i) {
+ attr = svg_path.attributes[i];
+ if (attr.name.substr(0, 5) === "data-") {
+ v = attr.value;
+ vn = Number(v);
+ if (v.trim() !== "" && vn === v && !isNaN(vn)) {
+ v = vn;
+ }
+ data[attr.name.substr(5)] = v;
+ }
+ }
+ me.data = data;
+ if (__type(titles) === 'string') {
+ title = titles;
+ } else if (__type(titles) === 'function') {
+ title = titles(data);
+ }
+ if (title != null) {
+ me.svgPath.attr('title', title);
+ }
}
- } else {
- ret[css[0]] = props;
- }
- }
- if (__type(callback) === 'function') {
- callback(ret);
- } else {
- return ret;
- }
- };
-
- munged = {};
-
- parsedeclarations = function(index) {
- var decl, parsed, str, _i, _len, _ref4;
- str = munged[index].replace(/^{|}$/g, '');
- str = munge(str);
- parsed = {};
- _ref4 = str.split(';');
- for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
- decl = _ref4[_i];
- decl = decl.split(':');
- if (decl.length < 2) {
- continue;
- }
- parsed[restore(decl[0])] = restore(decl.slice(1).join(':'));
- }
- return parsed;
- };
-
- REbraces = /{[^{}]*}/;
-
- REfull = /\[[^\[\]]*\]|{[^{}]*}|\([^()]*\)|function(\s+\w+)?(\s*%b`\d+`b%){2}/;
-
- REcomment_string = /(?:\/\*(?:[^\*]|\*[^\/])*\*\/)|(\\.|"(?:[^\\\"]|\\.|\\\n)*"|'(?:[^\\\']|\\.|\\\n)*')/g;
-
- REmunged = /%\w`(\d+)`\w%/;
-
- uid = 0;
-
- munge = function(str, full) {
- var RE, match, replacement;
- str = str.replace(REcomment_string, function(s, string) {
- var replacement;
- if (!string) {
- return '';
- }
- replacement = '%s`' + (++uid) + '`s%';
- munged[uid] = string.replace(/^\\/, '');
- return replacement;
- });
- RE = full ? REfull : REbraces;
- while (match = RE.exec(str)) {
- replacement = '%b`' + (++uid) + '`b%';
- munged[uid] = match[0];
- str = str.replace(RE, replacement);
- }
- return str;
- };
- restore = function(str) {
- var match;
- if (!(str != null)) {
- return str;
- }
- while (match = REmunged.exec(str)) {
- str = str.replace(REmunged, munged[match[1]]);
- }
- return str.trim();
- };
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- if ((_ref4 = kartograph.geom) == null) {
- kartograph.geom = {};
- }
-
- Path = (function() {
+ MapLayerPath.prototype.setView = function (view) {
+ var me, path, path_str;
+ me = this;
+ path = view.projectPath(me.path);
+ me.vpath = path;
+ if (me.path.type === "path") {
+ path_str = path.svgString();
+ return me.svgPath.attr({
+ path: path_str
+ });
+ } else if (me.path.type === "circle") {
+ return me.svgPath.attr({
+ cx: path.x,
+ cy: path.y,
+ r: path.r
+ });
+ }
+ };
+
+ MapLayerPath.prototype.remove = function () {
+ var me;
+ me = this;
+ return me.svgPath.remove();
+ };
+
+ return MapLayerPath;
+
+ })();
+
/*
- represents complex polygons (aka multi-polygons)
- */
-
- function Path(type, contours, closed) {
- var self;
- if (closed == null) {
- closed = true;
- }
- self = this;
- self.type = type;
- self.contours = contours;
- self.closed = closed;
- }
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
- Path.prototype.clipToBBox = function(bbox) {
- throw "path clipping is not implemented yet";
- };
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- Path.prototype.toSVG = function(paper) {
- /* translates this path to a SVG path string
- */
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- var str;
- str = this.svgString();
- return paper.path(str);
- };
- Path.prototype.svgString = function() {
- var contour, fst, glue, me, str, x, y, _i, _j, _len, _len1, _ref5, _ref6;
- me = this;
- str = "";
- glue = me.closed ? "Z M" : "M";
- _ref5 = me.contours;
- for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
- contour = _ref5[_i];
- fst = true;
- str += str === "" ? "M" : glue;
- for (_j = 0, _len1 = contour.length; _j < _len1; _j++) {
- _ref6 = contour[_j], x = _ref6[0], y = _ref6[1];
- if (!fst) {
- str += "L";
- }
- str += x + ',' + y;
- fst = false;
+ /*
+ This is a reduced version of Danial Wachsstocks jQuery based CSS parser
+ Everything is removed but the core css-to-object parsing
+
+ jQuery based CSS parser
+ documentation: http://youngisrael-stl.org/wordpress/2009/01/16/jquery-css-parser/
+ Version: 1.3
+ Copyright (c) 2011 Daniel Wachsstock
+ MIT license:
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+ kartograph.parsecss = function (str, callback) {
+ var css, k, props, ret, v, _i, _len, _ref4;
+ ret = {};
+ str = munge(str);
+ _ref4 = str.split('`b%');
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ css = _ref4[_i];
+ css = css.split('%b`');
+ if (css.length < 2) {
+ continue;
+ }
+ css[0] = restore(css[0]);
+ props = parsedeclarations(css[1]);
+ if (ret[css[0]] != null) {
+ for (k in props) {
+ v = props[k];
+ ret[css[0]][k] = v;
+ }
+ } else {
+ ret[css[0]] = props;
+ }
+ }
+ if (__type(callback) === 'function') {
+ callback(ret);
+ } else {
+ return ret;
}
- }
- if (me.closed) {
- str += "Z";
- }
- return str;
};
- Path.prototype.area = function() {
- var area, cnt, i, me, _i, _j, _len, _ref5, _ref6;
- me = this;
- if (me.areas != null) {
- return me._area;
- }
- me.areas = [];
- me._area = 0;
- _ref5 = me.contours;
- for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
- cnt = _ref5[_i];
- area = 0;
- for (i = _j = 0, _ref6 = cnt.length - 2; 0 <= _ref6 ? _j <= _ref6 : _j >= _ref6; i = 0 <= _ref6 ? ++_j : --_j) {
- area += cnt[i][0] * cnt[i + 1][1] - cnt[i + 1][0] * cnt[i][1];
+ munged = {};
+
+ parsedeclarations = function (index) {
+ var decl, parsed, str, _i, _len, _ref4;
+ str = munged[index].replace(/^{|}$/g, '');
+ str = munge(str);
+ parsed = {};
+ _ref4 = str.split(';');
+ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
+ decl = _ref4[_i];
+ decl = decl.split(':');
+ if (decl.length < 2) {
+ continue;
+ }
+ parsed[restore(decl[0])] = restore(decl.slice(1).join(':'));
}
- area *= .5;
- area = area;
- me.areas.push(area);
- me._area += area;
- }
- return me._area;
+ return parsed;
};
- Path.prototype.centroid = function() {
- var S, a, area, cnt, cnt_orig, cx, cy, diff, dx, dy, i, j, k, l, len, me, p0, p1, s, sp, total_len, w, x, x_, y, y_, _i, _j, _k, _l, _lengths, _m, _ref5, _ref6, _ref7, _ref8, _ref9;
- me = this;
- if (me._centroid != null) {
- return me._centroid;
- }
- area = me.area();
- cx = cy = 0;
- for (i = _i = 0, _ref5 = me.contours.length - 1; 0 <= _ref5 ? _i <= _ref5 : _i >= _ref5; i = 0 <= _ref5 ? ++_i : --_i) {
- cnt_orig = me.contours[i];
- cnt = [];
- l = cnt_orig.length;
- for (j = _j = 0, _ref6 = l - 1; 0 <= _ref6 ? _j <= _ref6 : _j >= _ref6; j = 0 <= _ref6 ? ++_j : --_j) {
- p0 = cnt_orig[j];
- p1 = cnt_orig[(j + 1) % l];
- diff = 0;
- cnt.push(p0);
- if (p0[0] === p1[0]) {
- diff = Math.abs(p0[1] - p1[1]);
- }
- if (p0[1] === p1[1]) {
- diff = Math.abs(p0[0] - p1[0]);
- }
- if (diff > 10) {
- S = Math.floor(diff * 2);
- for (s = _k = 1, _ref7 = S - 1; 1 <= _ref7 ? _k <= _ref7 : _k >= _ref7; s = 1 <= _ref7 ? ++_k : --_k) {
- sp = [p0[0] + s / S * (p1[0] - p0[0]), p0[1] + s / S * (p1[1] - p0[1])];
- cnt.push(sp);
- }
- }
- }
- a = me.areas[i];
- x = y = x_ = y_ = 0;
- l = cnt.length;
- _lengths = [];
- total_len = 0;
- for (j = _l = 0, _ref8 = l - 1; 0 <= _ref8 ? _l <= _ref8 : _l >= _ref8; j = 0 <= _ref8 ? ++_l : --_l) {
- p0 = cnt[j];
- p1 = cnt[(j + 1) % l];
- dx = p1[0] - p0[0];
- dy = p1[1] - p0[1];
- len = Math.sqrt(dx * dx + dy * dy);
- _lengths.push(len);
- total_len += len;
- }
- for (j = _m = 0, _ref9 = l - 1; 0 <= _ref9 ? _m <= _ref9 : _m >= _ref9; j = 0 <= _ref9 ? ++_m : --_m) {
- p0 = cnt[j];
- w = _lengths[j] / total_len;
- x += w * p0[0];
- y += w * p0[1];
+ REbraces = /{[^{}]*}/;
+
+ REfull = /\[[^\[\]]*\]|{[^{}]*}|\([^()]*\)|function(\s+\w+)?(\s*%b`\d+`b%){2}/;
+
+ REcomment_string = /(?:\/\*(?:[^\*]|\*[^\/])*\*\/)|(\\.|"(?:[^\\\"]|\\.|\\\n)*"|'(?:[^\\\']|\\.|\\\n)*')/g;
+
+ REmunged = /%\w`(\d+)`\w%/;
+
+ uid = 0;
+
+ munge = function (str, full) {
+ var RE, match, replacement;
+ str = str.replace(REcomment_string, function (s, string) {
+ var replacement;
+ if (!string) {
+ return '';
+ }
+ replacement = '%s`' + (++uid) + '`s%';
+ munged[uid] = string.replace(/^\\/, '');
+ return replacement;
+ });
+ RE = full ? REfull : REbraces;
+ while (match = RE.exec(str)) {
+ replacement = '%b`' + (++uid) + '`b%';
+ munged[uid] = match[0];
+ str = str.replace(RE, replacement);
}
- k = a / area;
- cx += x * k;
- cy += y * k;
- }
- me._centroid = [cx, cy];
- return me._centroid;
+ return str;
};
- Path.prototype.isInside = function(x, y) {
- var bbox, cnt, i, me, _i, _ref5;
- me = this;
- bbox = me._bbox;
- if (x < bbox[0] || x > bbox[2] || y < bbox[1] || y > bbox[3]) {
- return false;
- }
- for (i = _i = 0, _ref5 = me.contours.length - 1; 0 <= _ref5 ? _i <= _ref5 : _i >= _ref5; i = 0 <= _ref5 ? ++_i : --_i) {
- cnt = me.contours[i];
- if (__point_in_polygon(cnt, [x, y])) {
- return true;
+ restore = function (str) {
+ var match;
+ if (!(str != null)) {
+ return str;
}
- }
- return false;
+ while (match = REmunged.exec(str)) {
+ str = str.replace(REmunged, munged[match[1]]);
+ }
+ return str.trim();
};
- return Path;
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
- })();
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- kartograph.geom.Path = Path;
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- Circle = (function(_super) {
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- __extends(Circle, _super);
- function Circle(x, y, r) {
- this.x = x;
- this.y = y;
- this.r = r;
- Circle.__super__.constructor.call(this, 'circle', null, true);
+ if ((_ref4 = kartograph.geom) == null) {
+ kartograph.geom = {};
}
- Circle.prototype.toSVG = function(paper) {
- var me;
- me = this;
- return paper.circle(me.x, me.y, me.r);
- };
+ Path = (function () {
+ /*
+ represents complex polygons (aka multi-polygons)
+ */
- Circle.prototype.centroid = function() {
- var me;
- me = this;
- return [me.x, me.y];
- };
+ function Path(type, contours, closed) {
+ var self;
+ if (closed == null) {
+ closed = true;
+ }
+ self = this;
+ self.type = type;
+ self.contours = contours;
+ self.closed = closed;
+ }
- Circle.prototype.area = function() {
- var me;
- me = this;
- return Math.PI * me.r * m.r;
- };
+ Path.prototype.clipToBBox = function (bbox) {
+ throw "path clipping is not implemented yet";
+ };
- return Circle;
+ Path.prototype.toSVG = function (paper) {
+ /* translates this path to a SVG path string
+ */
- })(Path);
+ var str;
+ str = this.svgString();
+ return paper.path(str);
+ };
- kartograph.geom.Circle = Circle;
+ Path.prototype.svgString = function () {
+ var contour, fst, glue, me, str, x, y, _i, _j, _len, _len1, _ref5, _ref6;
+ me = this;
+ str = "";
+ glue = me.closed ? "Z M" : "M";
+ _ref5 = me.contours;
+ for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
+ contour = _ref5[_i];
+ fst = true;
+ str += str === "" ? "M" : glue;
+ for (_j = 0, _len1 = contour.length; _j < _len1; _j++) {
+ _ref6 = contour[_j], x = _ref6[0], y = _ref6[1];
+ if (!fst) {
+ str += "L";
+ }
+ str += x + ',' + y;
+ fst = false;
+ }
+ }
+ if (me.closed) {
+ str += "Z";
+ }
+ return str;
+ };
- Path.fromSVG = function(path) {
- /*
- loads a path from a SVG path string
- */
-
- var closed, cmd, contour, contours, cx, cy, path_data, path_str, r, res, sep, type, _i, _len;
- contours = [];
- type = path.nodeName;
- res = null;
- if (type === "path") {
- path_str = path.getAttribute('d').trim();
- path_data = Raphael.parsePathString(path_str);
- closed = path_data[path_data.length - 1] === "Z";
- sep = closed ? "Z M" : "M";
- contour = [];
- for (_i = 0, _len = path_data.length; _i < _len; _i++) {
- cmd = path_data[_i];
- if (cmd.length === 0) {
- continue;
+ Path.prototype.area = function () {
+ var area, cnt, i, me, _i, _j, _len, _ref5, _ref6;
+ me = this;
+ if (me.areas != null) {
+ return me._area;
+ }
+ me.areas = [];
+ me._area = 0;
+ _ref5 = me.contours;
+ for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
+ cnt = _ref5[_i];
+ area = 0;
+ for (i = _j = 0, _ref6 = cnt.length - 2; 0 <= _ref6 ? _j <= _ref6 : _j >= _ref6; i = 0 <= _ref6 ? ++_j : --_j) {
+ area += cnt[i][0] * cnt[i + 1][1] - cnt[i + 1][0] * cnt[i][1];
+ }
+ area *= .5;
+ area = area;
+ me.areas.push(area);
+ me._area += area;
+ }
+ return me._area;
+ };
+
+ Path.prototype.centroid = function () {
+ var S, a, area, cnt, cnt_orig, cx, cy, diff, dx, dy, i, j, k, l, len, me, p0, p1, s, sp, total_len, w, x, x_, y, y_, _i, _j, _k, _l, _lengths, _m, _ref5, _ref6, _ref7, _ref8, _ref9;
+ me = this;
+ if (me._centroid != null) {
+ return me._centroid;
+ }
+ area = me.area();
+ cx = cy = 0;
+ for (i = _i = 0, _ref5 = me.contours.length - 1; 0 <= _ref5 ? _i <= _ref5 : _i >= _ref5; i = 0 <= _ref5 ? ++_i : --_i) {
+ cnt_orig = me.contours[i];
+ cnt = [];
+ l = cnt_orig.length;
+ for (j = _j = 0, _ref6 = l - 1; 0 <= _ref6 ? _j <= _ref6 : _j >= _ref6; j = 0 <= _ref6 ? ++_j : --_j) {
+ p0 = cnt_orig[j];
+ p1 = cnt_orig[(j + 1) % l];
+ diff = 0;
+ cnt.push(p0);
+ if (p0[0] === p1[0]) {
+ diff = Math.abs(p0[1] - p1[1]);
+ }
+ if (p0[1] === p1[1]) {
+ diff = Math.abs(p0[0] - p1[0]);
+ }
+ if (diff > 10) {
+ S = Math.floor(diff * 2);
+ for (s = _k = 1, _ref7 = S - 1; 1 <= _ref7 ? _k <= _ref7 : _k >= _ref7; s = 1 <= _ref7 ? ++_k : --_k) {
+ sp = [p0[0] + s / S * (p1[0] - p0[0]), p0[1] + s / S * (p1[1] - p0[1])];
+ cnt.push(sp);
+ }
+ }
+ }
+ a = me.areas[i];
+ x = y = x_ = y_ = 0;
+ l = cnt.length;
+ _lengths = [];
+ total_len = 0;
+ for (j = _l = 0, _ref8 = l - 1; 0 <= _ref8 ? _l <= _ref8 : _l >= _ref8; j = 0 <= _ref8 ? ++_l : --_l) {
+ p0 = cnt[j];
+ p1 = cnt[(j + 1) % l];
+ dx = p1[0] - p0[0];
+ dy = p1[1] - p0[1];
+ len = Math.sqrt(dx * dx + dy * dy);
+ _lengths.push(len);
+ total_len += len;
+ }
+ for (j = _m = 0, _ref9 = l - 1; 0 <= _ref9 ? _m <= _ref9 : _m >= _ref9; j = 0 <= _ref9 ? ++_m : --_m) {
+ p0 = cnt[j];
+ w = _lengths[j] / total_len;
+ x += w * p0[0];
+ y += w * p0[1];
+ }
+ k = a / area;
+ cx += x * k;
+ cy += y * k;
+ }
+ me._centroid = [cx, cy];
+ return me._centroid;
+ };
+
+ Path.prototype.isInside = function (x, y) {
+ var bbox, cnt, i, me, _i, _ref5;
+ me = this;
+ bbox = me._bbox;
+ if (x < bbox[0] || x > bbox[2] || y < bbox[1] || y > bbox[3]) {
+ return false;
+ }
+ for (i = _i = 0, _ref5 = me.contours.length - 1; 0 <= _ref5 ? _i <= _ref5 : _i >= _ref5; i = 0 <= _ref5 ? ++_i : --_i) {
+ cnt = me.contours[i];
+ if (__point_in_polygon(cnt, [x, y])) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ return Path;
+
+ })();
+
+ kartograph.geom.Path = Path;
+
+ Circle = (function (_super) {
+
+ __extends(Circle, _super);
+
+ function Circle(x, y, r) {
+ this.x = x;
+ this.y = y;
+ this.r = r;
+ Circle.__super__.constructor.call(this, 'circle', null, true);
}
- if (cmd[0] === "M") {
- if (contour.length > 2) {
- contours.push(contour);
- contour = [];
- }
- contour.push([cmd[1], cmd[2]]);
- } else if (cmd[0] === "L") {
- contour.push([cmd[1], cmd[2]]);
- } else if (cmd[0] === "Z") {
- if (contour.length > 2) {
- contours.push(contour);
+
+ Circle.prototype.toSVG = function (paper) {
+ var me;
+ me = this;
+ return paper.circle(me.x, me.y, me.r);
+ };
+
+ Circle.prototype.centroid = function () {
+ var me;
+ me = this;
+ return [me.x, me.y];
+ };
+
+ Circle.prototype.area = function () {
+ var me;
+ me = this;
+ return Math.PI * me.r * m.r;
+ };
+
+ return Circle;
+
+ })(Path);
+
+ kartograph.geom.Circle = Circle;
+
+ Path.fromSVG = function (path) {
+ /*
+ loads a path from a SVG path string
+ */
+
+ var closed, cmd, contour, contours, cx, cy, path_data, path_str, r, res, sep, type, _i, _len;
+ contours = [];
+ type = path.nodeName;
+ res = null;
+ if (type === "path") {
+ path_str = path.getAttribute('d').trim();
+ path_data = Raphael.parsePathString(path_str);
+ closed = path_data[path_data.length - 1] === "Z";
+ sep = closed ? "Z M" : "M";
contour = [];
- }
+ for (_i = 0, _len = path_data.length; _i < _len; _i++) {
+ cmd = path_data[_i];
+ if (cmd.length === 0) {
+ continue;
+ }
+ if (cmd[0] === "M") {
+ if (contour.length > 2) {
+ contours.push(contour);
+ contour = [];
+ }
+ contour.push([cmd[1], cmd[2]]);
+ } else if (cmd[0] === "L") {
+ contour.push([cmd[1], cmd[2]]);
+ } else if (cmd[0] === "Z") {
+ if (contour.length > 2) {
+ contours.push(contour);
+ contour = [];
+ }
+ }
+ }
+ if (contour.length > 2) {
+ contours.push(contour);
+ contour = [];
+ }
+ res = new kartograph.geom.Path(type, contours, closed);
+ } else if (type === "circle") {
+ cx = path.getAttribute("cx");
+ cy = path.getAttribute("cy");
+ r = path.getAttribute("r");
+ res = new kartograph.geom.Circle(cx, cy, r);
}
- }
- if (contour.length > 2) {
- contours.push(contour);
- contour = [];
- }
- res = new kartograph.geom.Path(type, contours, closed);
- } else if (type === "circle") {
- cx = path.getAttribute("cx");
- cy = path.getAttribute("cy");
- r = path.getAttribute("r");
- res = new kartograph.geom.Circle(cx, cy, r);
- }
- return res;
- };
+ return res;
+ };
- Line = (function() {
- /*
- represents simple lines
- */
+ Line = (function () {
+ /*
+ represents simple lines
+ */
- function Line(points) {
- this.points = points;
- }
+ function Line(points) {
+ this.points = points;
+ }
- Line.prototype.clipToBBox = function(bbox) {
- var clip, i, last_in, lines, p0x, p0y, p1x, p1y, pts, self, x0, x1, y0, y1, _i, _ref5, _ref6, _ref7, _ref8;
- self = this;
- clip = new kartograph.geom.clipping.CohenSutherland().clip;
- pts = [];
- lines = [];
- last_in = false;
- for (i = _i = 0, _ref5 = self.points.length - 2; 0 <= _ref5 ? _i <= _ref5 : _i >= _ref5; i = 0 <= _ref5 ? ++_i : --_i) {
- _ref6 = self.points[i], p0x = _ref6[0], p0y = _ref6[1];
- _ref7 = self.points[i + 1], p1x = _ref7[0], p1y = _ref7[1];
- try {
- _ref8 = clip(bbox, p0x, p0y, p1x, p1y), x0 = _ref8[0], y0 = _ref8[1], x1 = _ref8[2], y1 = _ref8[3];
- last_in = true;
- pts.push([x0, y0]);
- if (p1x !== x1 || p1y !== y0 || i === len(self.points) - 2) {
- pts.push([x1, y1]);
- }
- } catch (err) {
- if (last_in && pts.length > 1) {
- lines.push(new Line(pts));
+ Line.prototype.clipToBBox = function (bbox) {
+ var clip, i, last_in, lines, p0x, p0y, p1x, p1y, pts, self, x0, x1, y0, y1, _i, _ref5, _ref6, _ref7, _ref8;
+ self = this;
+ clip = new kartograph.geom.clipping.CohenSutherland().clip;
+ pts = [];
+ lines = [];
+ last_in = false;
+ for (i = _i = 0, _ref5 = self.points.length - 2; 0 <= _ref5 ? _i <= _ref5 : _i >= _ref5; i = 0 <= _ref5 ? ++_i : --_i) {
+ _ref6 = self.points[i], p0x = _ref6[0], p0y = _ref6[1];
+ _ref7 = self.points[i + 1], p1x = _ref7[0], p1y = _ref7[1];
+ try {
+ _ref8 = clip(bbox, p0x, p0y, p1x, p1y), x0 = _ref8[0], y0 = _ref8[1], x1 = _ref8[2], y1 = _ref8[3];
+ last_in = true;
+ pts.push([x0, y0]);
+ if (p1x !== x1 || p1y !== y0 || i === len(self.points) - 2) {
+ pts.push([x1, y1]);
+ }
+ } catch (err) {
+ if (last_in && pts.length > 1) {
+ lines.push(new Line(pts));
+ pts = [];
+ }
+ last_in = false;
+ }
+ }
+ if (pts.length > 1) {
+ lines.push(new Line(pts));
+ }
+ return lines;
+ };
+
+ Line.prototype.toSVG = function () {
+ var pts, self, x, y, _i, _len, _ref5, _ref6;
+ self = this;
pts = [];
- }
- last_in = false;
+ _ref5 = self.points;
+ for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
+ _ref6 = _ref5[_i], x = _ref6[0], y = _ref6[1];
+ pts.push(x + ',' + y);
+ }
+ return 'M' + pts.join('L');
+ };
+
+ return Line;
+
+ })();
+
+ kartograph.geom.Line = Line;
+
+ __point_in_polygon = function (polygon, p) {
+ var angle, atan2, dtheta, i, n, pi, theta1, theta2, twopi, x1, x2, y1, y2, _i, _ref5;
+ pi = Math.PI;
+ atan2 = Math.atan2;
+ twopi = pi * 2;
+ n = polygon.length;
+ angle = 0;
+ for (i = _i = 0, _ref5 = n - 1; 0 <= _ref5 ? _i <= _ref5 : _i >= _ref5; i = 0 <= _ref5 ? ++_i : --_i) {
+ x1 = polygon[i][0] - p[0];
+ y1 = polygon[i][1] - p[1];
+ x2 = polygon[(i + 1) % n][0] - p[0];
+ y2 = polygon[(i + 1) % n][1] - p[1];
+ theta1 = atan2(y1, x1);
+ theta2 = atan2(y2, x2);
+ dtheta = theta2 - theta1;
+ while (dtheta > pi) {
+ dtheta -= twopi;
+ }
+ while (dtheta < -pi) {
+ dtheta += twopi;
+ }
+ angle += dtheta;
}
- }
- if (pts.length > 1) {
- lines.push(new Line(pts));
- }
- return lines;
+ return Math.abs(angle) >= pi;
};
- Line.prototype.toSVG = function() {
- var pts, self, x, y, _i, _len, _ref5, _ref6;
- self = this;
- pts = [];
- _ref5 = self.points;
- for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
- _ref6 = _ref5[_i], x = _ref6[0], y = _ref6[1];
- pts.push(x + ',' + y);
- }
- return 'M' + pts.join('L');
- };
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
- return Line;
-
- })();
-
- kartograph.geom.Line = Line;
-
- __point_in_polygon = function(polygon, p) {
- var angle, atan2, dtheta, i, n, pi, theta1, theta2, twopi, x1, x2, y1, y2, _i, _ref5;
- pi = Math.PI;
- atan2 = Math.atan2;
- twopi = pi * 2;
- n = polygon.length;
- angle = 0;
- for (i = _i = 0, _ref5 = n - 1; 0 <= _ref5 ? _i <= _ref5 : _i >= _ref5; i = 0 <= _ref5 ? ++_i : --_i) {
- x1 = polygon[i][0] - p[0];
- y1 = polygon[i][1] - p[1];
- x2 = polygon[(i + 1) % n][0] - p[0];
- y2 = polygon[(i + 1) % n][1] - p[1];
- theta1 = atan2(y1, x1);
- theta2 = atan2(y2, x2);
- dtheta = theta2 - theta1;
- while (dtheta > pi) {
- dtheta -= twopi;
- }
- while (dtheta < -pi) {
- dtheta += twopi;
- }
- angle += dtheta;
- }
- return Math.abs(angle) >= pi;
- };
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- __proj = kartograph.proj = {};
-
- Function.prototype.bind = function(scope) {
- var _function;
- _function = this;
- return function() {
- return _function.apply(scope, arguments);
- };
- };
-
- Proj = (function() {
-
- Proj.parameters = [];
-
- Proj.title = "Projection";
-
- function Proj(opts) {
- var me, _ref5, _ref6;
- me = this;
- me.lon0 = (_ref5 = opts.lon0) != null ? _ref5 : 0;
- me.lat0 = (_ref6 = opts.lat0) != null ? _ref6 : 0;
- me.PI = Math.PI;
- me.HALFPI = me.PI * .5;
- me.QUARTERPI = me.PI * .25;
- me.RAD = me.PI / 180;
- me.DEG = 180 / me.PI;
- me.lam0 = me.rad(this.lon0);
- me.phi0 = me.rad(this.lat0);
- me.minLat = -90;
- me.maxLat = 90;
- }
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- Proj.prototype.rad = function(a) {
- return a * this.RAD;
- };
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- Proj.prototype.deg = function(a) {
- return a * this.DEG;
- };
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- Proj.prototype.plot = function(polygon, truncate) {
- var ignore, lat, lon, points, vis, x, y, _i, _len, _ref5, _ref6;
- if (truncate == null) {
- truncate = true;
- }
- points = [];
- ignore = true;
- for (_i = 0, _len = polygon.length; _i < _len; _i++) {
- _ref5 = polygon[_i], lon = _ref5[0], lat = _ref5[1];
- vis = this._visible(lon, lat);
- if (vis) {
- ignore = false;
- }
- _ref6 = this.project(lon, lat), x = _ref6[0], y = _ref6[1];
- if (!vis && truncate) {
- points.push(this._truncate(x, y));
- } else {
- points.push([x, y]);
- }
- }
- if (ignore) {
- return null;
- } else {
- return [points];
- }
- };
- Proj.prototype.sea = function() {
- var l0, lat, lon, o, p, s, _i, _j, _k, _l, _ref5, _ref6, _ref7, _ref8;
- s = this;
- p = s.project.bind(this);
- o = [];
- l0 = s.lon0;
- s.lon0 = 0;
- for (lon = _i = -180; _i <= 180; lon = ++_i) {
- o.push(p(lon, s.maxLat));
- }
- for (lat = _j = _ref5 = s.maxLat, _ref6 = s.minLat; _ref5 <= _ref6 ? _j <= _ref6 : _j >= _ref6; lat = _ref5 <= _ref6 ? ++_j : --_j) {
- o.push(p(180, lat));
- }
- for (lon = _k = 180; _k >= -180; lon = --_k) {
- o.push(p(lon, s.minLat));
- }
- for (lat = _l = _ref7 = s.minLat, _ref8 = s.maxLat; _ref7 <= _ref8 ? _l <= _ref8 : _l >= _ref8; lat = _ref7 <= _ref8 ? ++_l : --_l) {
- o.push(p(-180, lat));
- }
- s.lon0 = l0;
- return o;
- };
+ __proj = kartograph.proj = {};
- Proj.prototype.world_bbox = function() {
- var bbox, p, s, sea, _i, _len;
- p = this.project.bind(this);
- sea = this.sea();
- bbox = new kartograph.BBox();
- for (_i = 0, _len = sea.length; _i < _len; _i++) {
- s = sea[_i];
- bbox.update(s[0], s[1]);
- }
- return bbox;
+ Function.prototype.bind = function (scope) {
+ var _function;
+ _function = this;
+ return function () {
+ return _function.apply(scope, arguments);
+ };
};
- Proj.prototype.toString = function() {
- var me;
- me = this;
- return '[Proj: ' + me.name + ']';
- };
+ Proj = (function () {
- return Proj;
+ Proj.parameters = [];
- })();
+ Proj.title = "Projection";
- Proj.fromXML = function(xml) {
- /*
- reconstructs a projection from xml description
- */
-
- var attr, i, id, opts, proj, _i, _ref5;
- id = xml.getAttribute('id');
- opts = {};
- for (i = _i = 0, _ref5 = xml.attributes.length - 1; 0 <= _ref5 ? _i <= _ref5 : _i >= _ref5; i = 0 <= _ref5 ? ++_i : --_i) {
- attr = xml.attributes[i];
- if (attr.name !== "id") {
- opts[attr.name] = attr.value;
- }
- }
- proj = new kartograph.proj[id](opts);
- proj.name = id;
- return proj;
- };
+ function Proj(opts) {
+ var me, _ref5, _ref6;
+ me = this;
+ me.lon0 = (_ref5 = opts.lon0) != null ? _ref5 : 0;
+ me.lat0 = (_ref6 = opts.lat0) != null ? _ref6 : 0;
+ me.PI = Math.PI;
+ me.HALFPI = me.PI * .5;
+ me.QUARTERPI = me.PI * .25;
+ me.RAD = me.PI / 180;
+ me.DEG = 180 / me.PI;
+ me.lam0 = me.rad(this.lon0);
+ me.phi0 = me.rad(this.lat0);
+ me.minLat = -90;
+ me.maxLat = 90;
+ }
- kartograph.Proj = Proj;
+ Proj.prototype.rad = function (a) {
+ return a * this.RAD;
+ };
- Cylindrical = (function(_super) {
+ Proj.prototype.deg = function (a) {
+ return a * this.DEG;
+ };
- __extends(Cylindrical, _super);
+ Proj.prototype.plot = function (polygon, truncate) {
+ var ignore, lat, lon, points, vis, x, y, _i, _len, _ref5, _ref6;
+ if (truncate == null) {
+ truncate = true;
+ }
+ points = [];
+ ignore = true;
+ for (_i = 0, _len = polygon.length; _i < _len; _i++) {
+ _ref5 = polygon[_i], lon = _ref5[0], lat = _ref5[1];
+ vis = this._visible(lon, lat);
+ if (vis) {
+ ignore = false;
+ }
+ _ref6 = this.project(lon, lat), x = _ref6[0], y = _ref6[1];
+ if (!vis && truncate) {
+ points.push(this._truncate(x, y));
+ } else {
+ points.push([x, y]);
+ }
+ }
+ if (ignore) {
+ return null;
+ } else {
+ return [points];
+ }
+ };
- /*
- Base class for cylindrical projections
- */
+ Proj.prototype.sea = function () {
+ var l0, lat, lon, o, p, s, _i, _j, _k, _l, _ref5, _ref6, _ref7, _ref8;
+ s = this;
+ p = s.project.bind(this);
+ o = [];
+ l0 = s.lon0;
+ s.lon0 = 0;
+ for (lon = _i = -180; _i <= 180; lon = ++_i) {
+ o.push(p(lon, s.maxLat));
+ }
+ for (lat = _j = _ref5 = s.maxLat, _ref6 = s.minLat; _ref5 <= _ref6 ? _j <= _ref6 : _j >= _ref6; lat = _ref5 <= _ref6 ? ++_j : --_j) {
+ o.push(p(180, lat));
+ }
+ for (lon = _k = 180; _k >= -180; lon = --_k) {
+ o.push(p(lon, s.minLat));
+ }
+ for (lat = _l = _ref7 = s.minLat, _ref8 = s.maxLat; _ref7 <= _ref8 ? _l <= _ref8 : _l >= _ref8; lat = _ref7 <= _ref8 ? ++_l : --_l) {
+ o.push(p(-180, lat));
+ }
+ s.lon0 = l0;
+ return o;
+ };
+ Proj.prototype.world_bbox = function () {
+ var bbox, p, s, sea, _i, _len;
+ p = this.project.bind(this);
+ sea = this.sea();
+ bbox = new kartograph.BBox();
+ for (_i = 0, _len = sea.length; _i < _len; _i++) {
+ s = sea[_i];
+ bbox.update(s[0], s[1]);
+ }
+ return bbox;
+ };
- Cylindrical.parameters = ['lon0', 'flip'];
+ Proj.prototype.toString = function () {
+ var me;
+ me = this;
+ return '[Proj: ' + me.name + ']';
+ };
- Cylindrical.title = "Cylindrical Projection";
+ return Proj;
- function Cylindrical(opts) {
- var me, _ref5, _ref6;
- if (opts == null) {
- opts = {};
- }
- me = this;
- me.flip = Number((_ref5 = opts.flip) != null ? _ref5 : 0);
- if (me.flip === 1) {
- opts.lon0 = (_ref6 = -opts.lon0) != null ? _ref6 : 0;
- }
- Cylindrical.__super__.constructor.call(this, opts);
- }
+ })();
- Cylindrical.prototype._visible = function(lon, lat) {
- return true;
- };
+ Proj.fromXML = function (xml) {
+ /*
+ reconstructs a projection from xml description
+ */
- Cylindrical.prototype.clon = function(lon) {
- lon -= this.lon0;
- if (lon < -180) {
- lon += 360;
- } else if (lon > 180) {
- lon -= 360;
- }
- return lon;
+ var attr, i, id, opts, proj, _i, _ref5;
+ id = xml.getAttribute('id');
+ opts = {};
+ for (i = _i = 0, _ref5 = xml.attributes.length - 1; 0 <= _ref5 ? _i <= _ref5 : _i >= _ref5; i = 0 <= _ref5 ? ++_i : --_i) {
+ attr = xml.attributes[i];
+ if (attr.name !== "id") {
+ opts[attr.name] = attr.value;
+ }
+ }
+ proj = new kartograph.proj[id](opts);
+ proj.name = id;
+ return proj;
};
- Cylindrical.prototype.ll = function(lon, lat) {
- if (this.flip === 1) {
- return [-lon, -lat];
- } else {
- return [lon, lat];
- }
- };
+ kartograph.Proj = Proj;
- return Cylindrical;
+ Cylindrical = (function (_super) {
- })(Proj);
+ __extends(Cylindrical, _super);
- Equirectangular = (function(_super) {
+ /*
+ Base class for cylindrical projections
+ */
- __extends(Equirectangular, _super);
- /*
- Equirectangular Projection aka Lonlat aka Plate Carree
- */
+ Cylindrical.parameters = ['lon0', 'flip'];
+ Cylindrical.title = "Cylindrical Projection";
- function Equirectangular() {
- return Equirectangular.__super__.constructor.apply(this, arguments);
- }
+ function Cylindrical(opts) {
+ var me, _ref5, _ref6;
+ if (opts == null) {
+ opts = {};
+ }
+ me = this;
+ me.flip = Number((_ref5 = opts.flip) != null ? _ref5 : 0);
+ if (me.flip === 1) {
+ opts.lon0 = (_ref6 = -opts.lon0) != null ? _ref6 : 0;
+ }
+ Cylindrical.__super__.constructor.call(this, opts);
+ }
- Equirectangular.title = "Equirectangular Projection";
+ Cylindrical.prototype._visible = function (lon, lat) {
+ return true;
+ };
- Equirectangular.prototype.project = function(lon, lat) {
- var _ref5;
- _ref5 = this.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lon = this.clon(lon);
- return [lon * Math.cos(this.phi0) * 1000, lat * -1 * 1000];
- };
+ Cylindrical.prototype.clon = function (lon) {
+ lon -= this.lon0;
+ if (lon < -180) {
+ lon += 360;
+ } else if (lon > 180) {
+ lon -= 360;
+ }
+ return lon;
+ };
- return Equirectangular;
+ Cylindrical.prototype.ll = function (lon, lat) {
+ if (this.flip === 1) {
+ return [-lon, -lat];
+ } else {
+ return [lon, lat];
+ }
+ };
- })(Cylindrical);
+ return Cylindrical;
- __proj['lonlat'] = Equirectangular;
+ })(Proj);
- CEA = (function(_super) {
+ Equirectangular = (function (_super) {
- __extends(CEA, _super);
+ __extends(Equirectangular, _super);
- CEA.parameters = ['lon0', 'lat1', 'flip'];
+ /*
+ Equirectangular Projection aka Lonlat aka Plate Carree
+ */
- CEA.title = "Cylindrical Equal Area";
- function CEA(opts) {
- var _ref5;
- CEA.__super__.constructor.call(this, opts);
- this.lat1 = (_ref5 = opts.lat1) != null ? _ref5 : 0;
- this.phi1 = this.rad(this.lat1);
- }
+ function Equirectangular() {
+ return Equirectangular.__super__.constructor.apply(this, arguments);
+ }
- /*
- Cylindrical Equal Area Projection
- */
-
-
- CEA.prototype.project = function(lon, lat) {
- var lam, phi, x, y, _ref5;
- _ref5 = this.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lam = this.rad(this.clon(lon));
- phi = this.rad(lat * -1);
- x = lam * Math.cos(this.phi1);
- y = Math.sin(phi) / Math.cos(this.phi1);
- return [x * 1000, y * 1000];
- };
+ Equirectangular.title = "Equirectangular Projection";
- return CEA;
+ Equirectangular.prototype.project = function (lon, lat) {
+ var _ref5;
+ _ref5 = this.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lon = this.clon(lon);
+ return [lon * Math.cos(this.phi0) * 1000, lat * -1 * 1000];
+ };
- })(Cylindrical);
+ return Equirectangular;
- __proj['cea'] = CEA;
+ })(Cylindrical);
- GallPeters = (function(_super) {
+ __proj['lonlat'] = Equirectangular;
- __extends(GallPeters, _super);
+ CEA = (function (_super) {
- /*
- Gall-Peters Projection
- */
+ __extends(CEA, _super);
+ CEA.parameters = ['lon0', 'lat1', 'flip'];
- GallPeters.title = "Gall-Peters Projection";
+ CEA.title = "Cylindrical Equal Area";
- GallPeters.parameters = ['lon0', 'flip'];
+ function CEA(opts) {
+ var _ref5;
+ CEA.__super__.constructor.call(this, opts);
+ this.lat1 = (_ref5 = opts.lat1) != null ? _ref5 : 0;
+ this.phi1 = this.rad(this.lat1);
+ }
- function GallPeters(opts) {
- opts.lat1 = 45;
- GallPeters.__super__.constructor.call(this, opts);
- }
+ /*
+ Cylindrical Equal Area Projection
+ */
- return GallPeters;
- })(CEA);
+ CEA.prototype.project = function (lon, lat) {
+ var lam, phi, x, y, _ref5;
+ _ref5 = this.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lam = this.rad(this.clon(lon));
+ phi = this.rad(lat * -1);
+ x = lam * Math.cos(this.phi1);
+ y = Math.sin(phi) / Math.cos(this.phi1);
+ return [x * 1000, y * 1000];
+ };
- __proj['gallpeters'] = GallPeters;
+ return CEA;
- HoboDyer = (function(_super) {
+ })(Cylindrical);
- __extends(HoboDyer, _super);
+ __proj['cea'] = CEA;
- /*
- Hobo-Dyer Projection
- */
+ GallPeters = (function (_super) {
+ __extends(GallPeters, _super);
- HoboDyer.title = "Hobo-Dyer Projection";
+ /*
+ Gall-Peters Projection
+ */
- HoboDyer.parameters = ['lon0', 'flip'];
- function HoboDyer(opts) {
- opts.lat1 = 37.7;
- HoboDyer.__super__.constructor.call(this, opts);
- }
+ GallPeters.title = "Gall-Peters Projection";
- return HoboDyer;
+ GallPeters.parameters = ['lon0', 'flip'];
- })(CEA);
+ function GallPeters(opts) {
+ opts.lat1 = 45;
+ GallPeters.__super__.constructor.call(this, opts);
+ }
- __proj['hobodyer'] = HoboDyer;
+ return GallPeters;
- Behrmann = (function(_super) {
+ })(CEA);
- __extends(Behrmann, _super);
+ __proj['gallpeters'] = GallPeters;
- /*
- Behrmann Projection
- */
+ HoboDyer = (function (_super) {
+ __extends(HoboDyer, _super);
- Behrmann.title = "Behrmann Projection";
+ /*
+ Hobo-Dyer Projection
+ */
- Behrmann.parameters = ['lon0', 'flip'];
- function Behrmann(opts) {
- opts.lat1 = 30;
- Behrmann.__super__.constructor.call(this, opts);
- }
+ HoboDyer.title = "Hobo-Dyer Projection";
- return Behrmann;
+ HoboDyer.parameters = ['lon0', 'flip'];
- })(CEA);
+ function HoboDyer(opts) {
+ opts.lat1 = 37.7;
+ HoboDyer.__super__.constructor.call(this, opts);
+ }
- __proj['behrmann'] = Behrmann;
+ return HoboDyer;
- Balthasart = (function(_super) {
+ })(CEA);
- __extends(Balthasart, _super);
+ __proj['hobodyer'] = HoboDyer;
- /*
- Balthasart Projection
- */
+ Behrmann = (function (_super) {
+ __extends(Behrmann, _super);
- Balthasart.title = "Balthasart Projection";
+ /*
+ Behrmann Projection
+ */
- Balthasart.parameters = ['lon0', 'flip'];
- function Balthasart(opts) {
- opts.lat1 = 50;
- Balthasart.__super__.constructor.call(this, opts);
- }
+ Behrmann.title = "Behrmann Projection";
- return Balthasart;
+ Behrmann.parameters = ['lon0', 'flip'];
- })(CEA);
+ function Behrmann(opts) {
+ opts.lat1 = 30;
+ Behrmann.__super__.constructor.call(this, opts);
+ }
- __proj['balthasart'] = Balthasart;
+ return Behrmann;
- Mercator = (function(_super) {
+ })(CEA);
- __extends(Mercator, _super);
+ __proj['behrmann'] = Behrmann;
- /*
- # you're not really into maps..
- */
+ Balthasart = (function (_super) {
+ __extends(Balthasart, _super);
- Mercator.title = "Mercator Projection";
+ /*
+ Balthasart Projection
+ */
- function Mercator(opts) {
- Mercator.__super__.constructor.call(this, opts);
- this.minLat = -85;
- this.maxLat = 85;
- }
- Mercator.prototype.project = function(lon, lat) {
- var lam, math, phi, s, x, y, _ref5;
- s = this;
- _ref5 = s.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- math = Math;
- lam = s.rad(s.clon(lon));
- phi = s.rad(lat * -1);
- x = lam * 1000;
- y = math.log((1 + math.sin(phi)) / math.cos(phi)) * 1000;
- return [x, y];
- };
+ Balthasart.title = "Balthasart Projection";
- return Mercator;
+ Balthasart.parameters = ['lon0', 'flip'];
- })(Cylindrical);
+ function Balthasart(opts) {
+ opts.lat1 = 50;
+ Balthasart.__super__.constructor.call(this, opts);
+ }
- __proj['mercator'] = Mercator;
+ return Balthasart;
- PseudoCylindrical = (function(_super) {
+ })(CEA);
- __extends(PseudoCylindrical, _super);
+ __proj['balthasart'] = Balthasart;
- /*
- Base class for pseudo cylindrical projections
- */
+ Mercator = (function (_super) {
+ __extends(Mercator, _super);
- function PseudoCylindrical() {
- return PseudoCylindrical.__super__.constructor.apply(this, arguments);
- }
+ /*
+ # you're not really into maps..
+ */
- PseudoCylindrical.title = "Pseudo-Cylindrical Projection";
- return PseudoCylindrical;
+ Mercator.title = "Mercator Projection";
- })(Cylindrical);
+ function Mercator(opts) {
+ Mercator.__super__.constructor.call(this, opts);
+ this.minLat = -85;
+ this.maxLat = 85;
+ }
- NaturalEarth = (function(_super) {
+ Mercator.prototype.project = function (lon, lat) {
+ var lam, math, phi, s, x, y, _ref5;
+ s = this;
+ _ref5 = s.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ math = Math;
+ lam = s.rad(s.clon(lon));
+ phi = s.rad(lat * -1);
+ x = lam * 1000;
+ y = math.log((1 + math.sin(phi)) / math.cos(phi)) * 1000;
+ return [x, y];
+ };
- __extends(NaturalEarth, _super);
+ return Mercator;
- /*
- Natural Earth Projection
- see here http://www.shadedrelief.com/NE_proj/
- */
-
-
- NaturalEarth.title = "Natural Earth Projection";
-
- function NaturalEarth(opts) {
- var s;
- NaturalEarth.__super__.constructor.call(this, opts);
- s = this;
- s.A0 = 0.8707;
- s.A1 = -0.131979;
- s.A2 = -0.013791;
- s.A3 = 0.003971;
- s.A4 = -0.001529;
- s.B0 = 1.007226;
- s.B1 = 0.015085;
- s.B2 = -0.044475;
- s.B3 = 0.028874;
- s.B4 = -0.005916;
- s.C0 = s.B0;
- s.C1 = 3 * s.B1;
- s.C2 = 7 * s.B2;
- s.C3 = 9 * s.B3;
- s.C4 = 11 * s.B4;
- s.EPS = 1e-11;
- s.MAX_Y = 0.8707 * 0.52 * Math.PI;
- return;
- }
+ })(Cylindrical);
- NaturalEarth.prototype.project = function(lon, lat) {
- var lplam, lpphi, phi2, phi4, s, x, y, _ref5;
- s = this;
- _ref5 = s.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lplam = s.rad(s.clon(lon));
- lpphi = s.rad(lat * -1);
- phi2 = lpphi * lpphi;
- phi4 = phi2 * phi2;
- x = lplam * (s.A0 + phi2 * (s.A1 + phi2 * (s.A2 + phi4 * phi2 * (s.A3 + phi2 * s.A4)))) * 180 + 500;
- y = lpphi * (s.B0 + phi2 * (s.B1 + phi4 * (s.B2 + s.B3 * phi2 + s.B4 * phi4))) * 180 + 270;
- return [x, y];
- };
+ __proj['mercator'] = Mercator;
- return NaturalEarth;
+ PseudoCylindrical = (function (_super) {
- })(PseudoCylindrical);
+ __extends(PseudoCylindrical, _super);
- __proj['naturalearth'] = NaturalEarth;
+ /*
+ Base class for pseudo cylindrical projections
+ */
- Robinson = (function(_super) {
- __extends(Robinson, _super);
+ function PseudoCylindrical() {
+ return PseudoCylindrical.__super__.constructor.apply(this, arguments);
+ }
- /*
- Robinson Projection
- */
-
-
- Robinson.title = "Robinson Projection";
-
- function Robinson(opts) {
- var s;
- Robinson.__super__.constructor.call(this, opts);
- s = this;
- s.X = [1, -5.67239e-12, -7.15511e-05, 3.11028e-06, 0.9986, -0.000482241, -2.4897e-05, -1.33094e-06, 0.9954, -0.000831031, -4.4861e-05, -9.86588e-07, 0.99, -0.00135363, -5.96598e-05, 3.67749e-06, 0.9822, -0.00167442, -4.4975e-06, -5.72394e-06, 0.973, -0.00214869, -9.03565e-05, 1.88767e-08, 0.96, -0.00305084, -9.00732e-05, 1.64869e-06, 0.9427, -0.00382792, -6.53428e-05, -2.61493e-06, 0.9216, -0.00467747, -0.000104566, 4.8122e-06, 0.8962, -0.00536222, -3.23834e-05, -5.43445e-06, 0.8679, -0.00609364, -0.0001139, 3.32521e-06, 0.835, -0.00698325, -6.40219e-05, 9.34582e-07, 0.7986, -0.00755337, -5.00038e-05, 9.35532e-07, 0.7597, -0.00798325, -3.59716e-05, -2.27604e-06, 0.7186, -0.00851366, -7.0112e-05, -8.63072e-06, 0.6732, -0.00986209, -0.000199572, 1.91978e-05, 0.6213, -0.010418, 8.83948e-05, 6.24031e-06, 0.5722, -0.00906601, 0.000181999, 6.24033e-06, 0.5322, 0, 0, 0];
- s.Y = [0, 0.0124, 3.72529e-10, 1.15484e-09, 0.062, 0.0124001, 1.76951e-08, -5.92321e-09, 0.124, 0.0123998, -7.09668e-08, 2.25753e-08, 0.186, 0.0124008, 2.66917e-07, -8.44523e-08, 0.248, 0.0123971, -9.99682e-07, 3.15569e-07, 0.31, 0.0124108, 3.73349e-06, -1.1779e-06, 0.372, 0.0123598, -1.3935e-05, 4.39588e-06, 0.434, 0.0125501, 5.20034e-05, -1.00051e-05, 0.4968, 0.0123198, -9.80735e-05, 9.22397e-06, 0.5571, 0.0120308, 4.02857e-05, -5.2901e-06, 0.6176, 0.0120369, -3.90662e-05, 7.36117e-07, 0.6769, 0.0117015, -2.80246e-05, -8.54283e-07, 0.7346, 0.0113572, -4.08389e-05, -5.18524e-07, 0.7903, 0.0109099, -4.86169e-05, -1.0718e-06, 0.8435, 0.0103433, -6.46934e-05, 5.36384e-09, 0.8936, 0.00969679, -6.46129e-05, -8.54894e-06, 0.9394, 0.00840949, -0.000192847, -4.21023e-06, 0.9761, 0.00616525, -0.000256001, -4.21021e-06, 1, 0, 0, 0];
- s.NODES = 18;
- s.FXC = 0.8487;
- s.FYC = 1.3523;
- s.C1 = 11.45915590261646417544;
- s.RC1 = 0.08726646259971647884;
- s.ONEEPS = 1.000001;
- s.EPS = 1e-8;
- return;
- }
+ PseudoCylindrical.title = "Pseudo-Cylindrical Projection";
+
+ return PseudoCylindrical;
+
+ })(Cylindrical);
+
+ NaturalEarth = (function (_super) {
+
+ __extends(NaturalEarth, _super);
+
+ /*
+ Natural Earth Projection
+ see here http://www.shadedrelief.com/NE_proj/
+ */
+
+
+ NaturalEarth.title = "Natural Earth Projection";
+
+ function NaturalEarth(opts) {
+ var s;
+ NaturalEarth.__super__.constructor.call(this, opts);
+ s = this;
+ s.A0 = 0.8707;
+ s.A1 = -0.131979;
+ s.A2 = -0.013791;
+ s.A3 = 0.003971;
+ s.A4 = -0.001529;
+ s.B0 = 1.007226;
+ s.B1 = 0.015085;
+ s.B2 = -0.044475;
+ s.B3 = 0.028874;
+ s.B4 = -0.005916;
+ s.C0 = s.B0;
+ s.C1 = 3 * s.B1;
+ s.C2 = 7 * s.B2;
+ s.C3 = 9 * s.B3;
+ s.C4 = 11 * s.B4;
+ s.EPS = 1e-11;
+ s.MAX_Y = 0.8707 * 0.52 * Math.PI;
+ return;
+ }
- Robinson.prototype._poly = function(arr, offs, z) {
- return arr[offs] + z * (arr[offs + 1] + z * (arr[offs + 2] + z * arr[offs + 3]));
- };
+ NaturalEarth.prototype.project = function (lon, lat) {
+ var lplam, lpphi, phi2, phi4, s, x, y, _ref5;
+ s = this;
+ _ref5 = s.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lplam = s.rad(s.clon(lon));
+ lpphi = s.rad(lat * -1);
+ phi2 = lpphi * lpphi;
+ phi4 = phi2 * phi2;
+ x = lplam * (s.A0 + phi2 * (s.A1 + phi2 * (s.A2 + phi4 * phi2 * (s.A3 + phi2 * s.A4)))) * 180 + 500;
+ y = lpphi * (s.B0 + phi2 * (s.B1 + phi4 * (s.B2 + s.B3 * phi2 + s.B4 * phi4))) * 180 + 270;
+ return [x, y];
+ };
- Robinson.prototype.project = function(lon, lat) {
- var i, lplam, lpphi, phi, s, x, y, _ref5;
- s = this;
- _ref5 = s.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lon = s.clon(lon);
- lplam = s.rad(lon);
- lpphi = s.rad(lat * -1);
- phi = Math.abs(lpphi);
- i = Math.floor(phi * s.C1);
- if (i >= s.NODES) {
- i = s.NODES - 1;
- }
- phi = s.deg(phi - s.RC1 * i);
- i *= 4;
- x = 1000 * s._poly(s.X, i, phi) * s.FXC * lplam;
- y = 1000 * s._poly(s.Y, i, phi) * s.FYC;
- if (lpphi < 0.0) {
- y = -y;
- }
- return [x, y];
- };
+ return NaturalEarth;
- return Robinson;
+ })(PseudoCylindrical);
- })(PseudoCylindrical);
+ __proj['naturalearth'] = NaturalEarth;
- __proj['robinson'] = Robinson;
+ Robinson = (function (_super) {
- EckertIV = (function(_super) {
+ __extends(Robinson, _super);
- __extends(EckertIV, _super);
+ /*
+ Robinson Projection
+ */
- /*
- Eckert IV Projection
- */
-
-
- EckertIV.title = "Eckert IV Projection";
-
- function EckertIV(opts) {
- var me;
- EckertIV.__super__.constructor.call(this, opts);
- me = this;
- me.C_x = .42223820031577120149;
- me.C_y = 1.32650042817700232218;
- me.RC_y = .75386330736002178205;
- me.C_p = 3.57079632679489661922;
- me.RC_p = .28004957675577868795;
- me.EPS = 1e-7;
- me.NITER = 6;
- }
- EckertIV.prototype.project = function(lon, lat) {
- var V, c, i, lplam, lpphi, me, p, s, x, y, _ref5;
- me = this;
- _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lplam = me.rad(me.clon(lon));
- lpphi = me.rad(lat * -1);
- p = me.C_p * Math.sin(lpphi);
- V = lpphi * lpphi;
- lpphi *= 0.895168 + V * (0.0218849 + V * 0.00826809);
- i = me.NITER;
- while (i > 0) {
- c = Math.cos(lpphi);
- s = Math.sin(lpphi);
- V = (lpphi + s * (c + 2) - p) / (1 + c * (c + 2) - s * s);
- lpphi -= V;
- if (Math.abs(V) < me.EPS) {
- break;
+ Robinson.title = "Robinson Projection";
+
+ function Robinson(opts) {
+ var s;
+ Robinson.__super__.constructor.call(this, opts);
+ s = this;
+ s.X = [1, -5.67239e-12, -7.15511e-05, 3.11028e-06, 0.9986, -0.000482241, -2.4897e-05, -1.33094e-06, 0.9954, -0.000831031, -4.4861e-05, -9.86588e-07, 0.99, -0.00135363, -5.96598e-05, 3.67749e-06, 0.9822, -0.00167442, -4.4975e-06, -5.72394e-06, 0.973, -0.00214869, -9.03565e-05, 1.88767e-08, 0.96, -0.00305084, -9.00732e-05, 1.64869e-06, 0.9427, -0.00382792, -6.53428e-05, -2.61493e-06, 0.9216, -0.00467747, -0.000104566, 4.8122e-06, 0.8962, -0.00536222, -3.23834e-05, -5.43445e-06, 0.8679, -0.00609364, -0.0001139, 3.32521e-06, 0.835, -0.00698325, -6.40219e-05, 9.34582e-07, 0.7986, -0.00755337, -5.00038e-05, 9.35532e-07, 0.7597, -0.00798325, -3.59716e-05, -2.27604e-06, 0.7186, -0.00851366, -7.0112e-05, -8.63072e-06, 0.6732, -0.00986209, -0.000199572, 1.91978e-05, 0.6213, -0.010418, 8.83948e-05, 6.24031e-06, 0.5722, -0.00906601, 0.000181999, 6.24033e-06, 0.5322, 0, 0, 0];
+ s.Y = [0, 0.0124, 3.72529e-10, 1.15484e-09, 0.062, 0.0124001, 1.76951e-08, -5.92321e-09, 0.124, 0.0123998, -7.09668e-08, 2.25753e-08, 0.186, 0.0124008, 2.66917e-07, -8.44523e-08, 0.248, 0.0123971, -9.99682e-07, 3.15569e-07, 0.31, 0.0124108, 3.73349e-06, -1.1779e-06, 0.372, 0.0123598, -1.3935e-05, 4.39588e-06, 0.434, 0.0125501, 5.20034e-05, -1.00051e-05, 0.4968, 0.0123198, -9.80735e-05, 9.22397e-06, 0.5571, 0.0120308, 4.02857e-05, -5.2901e-06, 0.6176, 0.0120369, -3.90662e-05, 7.36117e-07, 0.6769, 0.0117015, -2.80246e-05, -8.54283e-07, 0.7346, 0.0113572, -4.08389e-05, -5.18524e-07, 0.7903, 0.0109099, -4.86169e-05, -1.0718e-06, 0.8435, 0.0103433, -6.46934e-05, 5.36384e-09, 0.8936, 0.00969679, -6.46129e-05, -8.54894e-06, 0.9394, 0.00840949, -0.000192847, -4.21023e-06, 0.9761, 0.00616525, -0.000256001, -4.21021e-06, 1, 0, 0, 0];
+ s.NODES = 18;
+ s.FXC = 0.8487;
+ s.FYC = 1.3523;
+ s.C1 = 11.45915590261646417544;
+ s.RC1 = 0.08726646259971647884;
+ s.ONEEPS = 1.000001;
+ s.EPS = 1e-8;
+ return;
}
- i -= 1;
- }
- if (i === 0) {
- x = me.C_x * lplam;
- y = lpphi < 0 ? -me.C_y : me.C_y;
- } else {
- x = me.C_x * lplam * (1 + Math.cos(lpphi));
- y = me.C_y * Math.sin(lpphi);
- }
- return [x, y];
- };
- return EckertIV;
+ Robinson.prototype._poly = function (arr, offs, z) {
+ return arr[offs] + z * (arr[offs + 1] + z * (arr[offs + 2] + z * arr[offs + 3]));
+ };
- })(PseudoCylindrical);
+ Robinson.prototype.project = function (lon, lat) {
+ var i, lplam, lpphi, phi, s, x, y, _ref5;
+ s = this;
+ _ref5 = s.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lon = s.clon(lon);
+ lplam = s.rad(lon);
+ lpphi = s.rad(lat * -1);
+ phi = Math.abs(lpphi);
+ i = Math.floor(phi * s.C1);
+ if (i >= s.NODES) {
+ i = s.NODES - 1;
+ }
+ phi = s.deg(phi - s.RC1 * i);
+ i *= 4;
+ x = 1000 * s._poly(s.X, i, phi) * s.FXC * lplam;
+ y = 1000 * s._poly(s.Y, i, phi) * s.FYC;
+ if (lpphi < 0.0) {
+ y = -y;
+ }
+ return [x, y];
+ };
- __proj['eckert4'] = EckertIV;
+ return Robinson;
- Sinusoidal = (function(_super) {
+ })(PseudoCylindrical);
- __extends(Sinusoidal, _super);
+ __proj['robinson'] = Robinson;
- /*
- Sinusoidal Projection
- */
+ EckertIV = (function (_super) {
+ __extends(EckertIV, _super);
- function Sinusoidal() {
- return Sinusoidal.__super__.constructor.apply(this, arguments);
- }
+ /*
+ Eckert IV Projection
+ */
- Sinusoidal.title = "Sinusoidal Projection";
-
- Sinusoidal.prototype.project = function(lon, lat) {
- var lam, me, phi, x, y, _ref5;
- me = this;
- _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lam = me.rad(me.clon(lon));
- phi = me.rad(lat * -1);
- x = 1032 * lam * Math.cos(phi);
- y = 1032 * phi;
- return [x, y];
- };
- return Sinusoidal;
+ EckertIV.title = "Eckert IV Projection";
- })(PseudoCylindrical);
+ function EckertIV(opts) {
+ var me;
+ EckertIV.__super__.constructor.call(this, opts);
+ me = this;
+ me.C_x = .42223820031577120149;
+ me.C_y = 1.32650042817700232218;
+ me.RC_y = .75386330736002178205;
+ me.C_p = 3.57079632679489661922;
+ me.RC_p = .28004957675577868795;
+ me.EPS = 1e-7;
+ me.NITER = 6;
+ }
- __proj['sinusoidal'] = Sinusoidal;
+ EckertIV.prototype.project = function (lon, lat) {
+ var V, c, i, lplam, lpphi, me, p, s, x, y, _ref5;
+ me = this;
+ _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lplam = me.rad(me.clon(lon));
+ lpphi = me.rad(lat * -1);
+ p = me.C_p * Math.sin(lpphi);
+ V = lpphi * lpphi;
+ lpphi *= 0.895168 + V * (0.0218849 + V * 0.00826809);
+ i = me.NITER;
+ while (i > 0) {
+ c = Math.cos(lpphi);
+ s = Math.sin(lpphi);
+ V = (lpphi + s * (c + 2) - p) / (1 + c * (c + 2) - s * s);
+ lpphi -= V;
+ if (Math.abs(V) < me.EPS) {
+ break;
+ }
+ i -= 1;
+ }
+ if (i === 0) {
+ x = me.C_x * lplam;
+ y = lpphi < 0 ? -me.C_y : me.C_y;
+ } else {
+ x = me.C_x * lplam * (1 + Math.cos(lpphi));
+ y = me.C_y * Math.sin(lpphi);
+ }
+ return [x, y];
+ };
- Mollweide = (function(_super) {
+ return EckertIV;
- __extends(Mollweide, _super);
+ })(PseudoCylindrical);
- /*
- Mollweide Projection
- */
-
-
- Mollweide.title = "Mollweide Projection";
-
- function Mollweide(opts, p, cx, cy, cp) {
- var me, p2, r, sp;
- if (p == null) {
- p = 1.5707963267948966;
- }
- if (cx == null) {
- cx = null;
- }
- if (cy == null) {
- cy = null;
- }
- if (cp == null) {
- cp = null;
- }
- Mollweide.__super__.constructor.call(this, opts);
- me = this;
- me.MAX_ITER = 10;
- me.TOLERANCE = 1e-7;
- if (p != null) {
- p2 = p + p;
- sp = Math.sin(p);
- r = Math.sqrt(Math.PI * 2.0 * sp / (p2 + Math.sin(p2)));
- me.cx = 2 * r / Math.PI;
- me.cy = r / sp;
- me.cp = p2 + Math.sin(p2);
- } else if ((cx != null) && (cy != null) && (typeof cz !== "undefined" && cz !== null)) {
- me.cx = cx;
- me.cy = cy;
- me.cp = cp;
- } else {
- warn('kartograph.proj.Mollweide: either p or cx,cy,cp must be defined');
- }
- }
+ __proj['eckert4'] = EckertIV;
- Mollweide.prototype.project = function(lon, lat) {
- var abs, i, k, lam, math, me, phi, v, x, y, _ref5;
- me = this;
- _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- math = Math;
- abs = math.abs;
- lam = me.rad(me.clon(lon));
- phi = me.rad(lat);
- k = me.cp * math.sin(phi);
- i = me.MAX_ITER;
- while (i !== 0) {
- v = (phi + math.sin(phi) - k) / (1 + math.cos(phi));
- phi -= v;
- if (abs(v) < me.TOLERANCE) {
- break;
+ Sinusoidal = (function (_super) {
+
+ __extends(Sinusoidal, _super);
+
+ /*
+ Sinusoidal Projection
+ */
+
+
+ function Sinusoidal() {
+ return Sinusoidal.__super__.constructor.apply(this, arguments);
}
- i -= 1;
- }
- if (i === 0) {
- phi = phi >= 0 ? me.HALFPI : -me.HALFPI;
- } else {
- phi *= 0.5;
- }
- x = 1000 * me.cx * lam * math.cos(phi);
- y = 1000 * me.cy * math.sin(phi);
- return [x, y * -1];
- };
- return Mollweide;
+ Sinusoidal.title = "Sinusoidal Projection";
+
+ Sinusoidal.prototype.project = function (lon, lat) {
+ var lam, me, phi, x, y, _ref5;
+ me = this;
+ _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lam = me.rad(me.clon(lon));
+ phi = me.rad(lat * -1);
+ x = 1032 * lam * Math.cos(phi);
+ y = 1032 * phi;
+ return [x, y];
+ };
- })(PseudoCylindrical);
+ return Sinusoidal;
- __proj['mollweide'] = Mollweide;
+ })(PseudoCylindrical);
- WagnerIV = (function(_super) {
+ __proj['sinusoidal'] = Sinusoidal;
- __extends(WagnerIV, _super);
+ Mollweide = (function (_super) {
- /*
- Wagner IV Projection
- */
+ __extends(Mollweide, _super);
+ /*
+ Mollweide Projection
+ */
- WagnerIV.title = "Wagner IV Projection";
- function WagnerIV(opts) {
- WagnerIV.__super__.constructor.call(this, opts, 1.0471975511965976);
- }
+ Mollweide.title = "Mollweide Projection";
- return WagnerIV;
+ function Mollweide(opts, p, cx, cy, cp) {
+ var me, p2, r, sp;
+ if (p == null) {
+ p = 1.5707963267948966;
+ }
+ if (cx == null) {
+ cx = null;
+ }
+ if (cy == null) {
+ cy = null;
+ }
+ if (cp == null) {
+ cp = null;
+ }
+ Mollweide.__super__.constructor.call(this, opts);
+ me = this;
+ me.MAX_ITER = 10;
+ me.TOLERANCE = 1e-7;
+ if (p != null) {
+ p2 = p + p;
+ sp = Math.sin(p);
+ r = Math.sqrt(Math.PI * 2.0 * sp / (p2 + Math.sin(p2)));
+ me.cx = 2 * r / Math.PI;
+ me.cy = r / sp;
+ me.cp = p2 + Math.sin(p2);
+ } else if ((cx != null) && (cy != null) && (typeof cz !== "undefined" && cz !== null)) {
+ me.cx = cx;
+ me.cy = cy;
+ me.cp = cp;
+ } else {
+ warn('kartograph.proj.Mollweide: either p or cx,cy,cp must be defined');
+ }
+ }
- })(Mollweide);
+ Mollweide.prototype.project = function (lon, lat) {
+ var abs, i, k, lam, math, me, phi, v, x, y, _ref5;
+ me = this;
+ _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ math = Math;
+ abs = math.abs;
+ lam = me.rad(me.clon(lon));
+ phi = me.rad(lat);
+ k = me.cp * math.sin(phi);
+ i = me.MAX_ITER;
+ while (i !== 0) {
+ v = (phi + math.sin(phi) - k) / (1 + math.cos(phi));
+ phi -= v;
+ if (abs(v) < me.TOLERANCE) {
+ break;
+ }
+ i -= 1;
+ }
+ if (i === 0) {
+ phi = phi >= 0 ? me.HALFPI : -me.HALFPI;
+ } else {
+ phi *= 0.5;
+ }
+ x = 1000 * me.cx * lam * math.cos(phi);
+ y = 1000 * me.cy * math.sin(phi);
+ return [x, y * -1];
+ };
- __proj['wagner4'] = WagnerIV;
+ return Mollweide;
- WagnerV = (function(_super) {
+ })(PseudoCylindrical);
- __extends(WagnerV, _super);
+ __proj['mollweide'] = Mollweide;
- /*
- Wagner V Projection
- */
+ WagnerIV = (function (_super) {
+ __extends(WagnerIV, _super);
- WagnerV.title = "Wagner V Projection";
+ /*
+ Wagner IV Projection
+ */
- function WagnerV(opts) {
- WagnerV.__super__.constructor.call(this, opts, null, 0.90977, 1.65014, 3.00896);
- }
- return WagnerV;
+ WagnerIV.title = "Wagner IV Projection";
- })(Mollweide);
+ function WagnerIV(opts) {
+ WagnerIV.__super__.constructor.call(this, opts, 1.0471975511965976);
+ }
- __proj['wagner5'] = WagnerV;
+ return WagnerIV;
- Loximuthal = (function(_super) {
- var maxLat, minLat;
+ })(Mollweide);
- __extends(Loximuthal, _super);
+ __proj['wagner4'] = WagnerIV;
- function Loximuthal() {
- return Loximuthal.__super__.constructor.apply(this, arguments);
- }
+ WagnerV = (function (_super) {
- minLat = -89;
-
- maxLat = 89;
-
- Loximuthal.parameters = ['lon0', 'lat0', 'flip'];
-
- Loximuthal.title = "Loximuthal Projection (equidistant)";
-
- Loximuthal.prototype.project = function(lon, lat) {
- var lam, math, me, phi, x, y, _ref5;
- me = this;
- _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- math = Math;
- lam = me.rad(me.clon(lon));
- phi = me.rad(lat);
- if (phi === me.phi0) {
- x = lam * math.cos(me.phi0);
- } else {
- x = lam * (phi - me.phi0) / (math.log(math.tan(me.QUARTERPI + phi * 0.5)) - math.log(math.tan(me.QUARTERPI + me.phi0 * 0.5)));
- }
- x *= 1000;
- y = 1000 * (phi - me.phi0);
- return [x, y * -1];
- };
+ __extends(WagnerV, _super);
- return Loximuthal;
+ /*
+ Wagner V Projection
+ */
- })(PseudoCylindrical);
- __proj['loximuthal'] = Loximuthal;
+ WagnerV.title = "Wagner V Projection";
- CantersModifiedSinusoidalI = (function(_super) {
- var C1, C3, C3x3, C5, C5x5;
+ function WagnerV(opts) {
+ WagnerV.__super__.constructor.call(this, opts, null, 0.90977, 1.65014, 3.00896);
+ }
- __extends(CantersModifiedSinusoidalI, _super);
+ return WagnerV;
- /*
- Canters, F. (2002) Small-scale Map projection Design. p. 218-219.
- Modified Sinusoidal, equal-area.
-
- implementation borrowed from
- http://cartography.oregonstate.edu/temp/AdaptiveProjection/src/projections/Canters1.js
- */
+ })(Mollweide);
+ __proj['wagner5'] = WagnerV;
- function CantersModifiedSinusoidalI() {
- return CantersModifiedSinusoidalI.__super__.constructor.apply(this, arguments);
- }
+ Loximuthal = (function (_super) {
+ var maxLat, minLat;
- CantersModifiedSinusoidalI.title = "Canters Modified Sinusoidal I";
+ __extends(Loximuthal, _super);
- CantersModifiedSinusoidalI.parameters = ['lon0'];
+ function Loximuthal() {
+ return Loximuthal.__super__.constructor.apply(this, arguments);
+ }
- C1 = 1.1966;
+ minLat = -89;
- C3 = -0.1290;
+ maxLat = 89;
- C3x3 = 3 * C3;
+ Loximuthal.parameters = ['lon0', 'lat0', 'flip'];
- C5 = -0.0076;
+ Loximuthal.title = "Loximuthal Projection (equidistant)";
- C5x5 = 5 * C5;
+ Loximuthal.prototype.project = function (lon, lat) {
+ var lam, math, me, phi, x, y, _ref5;
+ me = this;
+ _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ math = Math;
+ lam = me.rad(me.clon(lon));
+ phi = me.rad(lat);
+ if (phi === me.phi0) {
+ x = lam * math.cos(me.phi0);
+ } else {
+ x = lam * (phi - me.phi0) / (math.log(math.tan(me.QUARTERPI + phi * 0.5)) - math.log(math.tan(me.QUARTERPI + me.phi0 * 0.5)));
+ }
+ x *= 1000;
+ y = 1000 * (phi - me.phi0);
+ return [x, y * -1];
+ };
- CantersModifiedSinusoidalI.prototype.project = function(lon, lat) {
- var me, x, y, y2, y4, _ref5;
- me = this;
- _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lon = me.rad(me.clon(lon));
- lat = me.rad(lat);
- y2 = lat * lat;
- y4 = y2 * y2;
- x = 1000 * lon * Math.cos(lat) / (C1 + C3x3 * y2 + C5x5 * y4);
- y = 1000 * lat * (C1 + C3 * y2 + C5 * y4);
- return [x, y * -1];
- };
+ return Loximuthal;
- return CantersModifiedSinusoidalI;
+ })(PseudoCylindrical);
- })(PseudoCylindrical);
+ __proj['loximuthal'] = Loximuthal;
- __proj['canters1'] = CantersModifiedSinusoidalI;
+ CantersModifiedSinusoidalI = (function (_super) {
+ var C1, C3, C3x3, C5, C5x5;
- Hatano = (function(_super) {
- var CN, CS, EPS, FXC, FYCN, FYCS, NITER, ONETOL, RCN, RCS, RXC, RYCN, RYCS;
+ __extends(CantersModifiedSinusoidalI, _super);
- __extends(Hatano, _super);
+ /*
+ Canters, F. (2002) Small-scale Map projection Design. p. 218-219.
+ Modified Sinusoidal, equal-area.
- Hatano.title = "Hatano Projection";
+ implementation borrowed from
+ http://cartography.oregonstate.edu/temp/AdaptiveProjection/src/projections/Canters1.js
+ */
- NITER = 20;
- EPS = 1e-7;
+ function CantersModifiedSinusoidalI() {
+ return CantersModifiedSinusoidalI.__super__.constructor.apply(this, arguments);
+ }
- ONETOL = 1.000001;
+ CantersModifiedSinusoidalI.title = "Canters Modified Sinusoidal I";
- CN = 2.67595;
+ CantersModifiedSinusoidalI.parameters = ['lon0'];
- CS = 2.43763;
+ C1 = 1.1966;
- RCN = 0.37369906014686373063;
+ C3 = -0.1290;
- RCS = 0.41023453108141924738;
+ C3x3 = 3 * C3;
- FYCN = 1.75859;
+ C5 = -0.0076;
- FYCS = 1.93052;
+ C5x5 = 5 * C5;
- RYCN = 0.56863737426006061674;
+ CantersModifiedSinusoidalI.prototype.project = function (lon, lat) {
+ var me, x, y, y2, y4, _ref5;
+ me = this;
+ _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lon = me.rad(me.clon(lon));
+ lat = me.rad(lat);
+ y2 = lat * lat;
+ y4 = y2 * y2;
+ x = 1000 * lon * Math.cos(lat) / (C1 + C3x3 * y2 + C5x5 * y4);
+ y = 1000 * lat * (C1 + C3 * y2 + C5 * y4);
+ return [x, y * -1];
+ };
- RYCS = 0.51799515156538134803;
+ return CantersModifiedSinusoidalI;
- FXC = 0.85;
+ })(PseudoCylindrical);
- RXC = 1.17647058823529411764;
+ __proj['canters1'] = CantersModifiedSinusoidalI;
- function Hatano(opts) {
- Hatano.__super__.constructor.call(this, opts);
- }
+ Hatano = (function (_super) {
+ var CN, CS, EPS, FXC, FYCN, FYCS, NITER, ONETOL, RCN, RCS, RXC, RYCN, RYCS;
- Hatano.prototype.project = function(lon, lat) {
- var c, i, lam, me, phi, th1, x, y, _i, _ref5;
- me = this;
- _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lam = me.rad(me.clon(lon));
- phi = me.rad(lat);
- c = Math.sin(phi) * (phi < 0.0 ? CS : CN);
- for (i = _i = NITER; _i >= 1; i = _i += -1) {
- th1 = (phi + Math.sin(phi) - c) / (1.0 + Math.cos(phi));
- phi -= th1;
- if (Math.abs(th1) < EPS) {
- break;
- }
- }
- x = 1000 * FXC * lam * Math.cos(phi *= 0.5);
- y = 1000 * Math.sin(phi) * (phi < 0.0 ? FYCS : FYCN);
- return [x, y * -1];
- };
+ __extends(Hatano, _super);
- return Hatano;
+ Hatano.title = "Hatano Projection";
- })(PseudoCylindrical);
+ NITER = 20;
- __proj['hatano'] = Hatano;
+ EPS = 1e-7;
- GoodeHomolosine = (function(_super) {
+ ONETOL = 1.000001;
- __extends(GoodeHomolosine, _super);
+ CN = 2.67595;
- GoodeHomolosine.title = "Goode Homolosine Projection";
+ CS = 2.43763;
- GoodeHomolosine.parameters = ['lon0'];
+ RCN = 0.37369906014686373063;
- function GoodeHomolosine(opts) {
- var me;
- GoodeHomolosine.__super__.constructor.call(this, opts);
- me = this;
- me.lat1 = 41.737;
- me.p1 = new Mollweide();
- me.p0 = new Sinusoidal();
- }
+ RCS = 0.41023453108141924738;
- GoodeHomolosine.prototype.project = function(lon, lat) {
- var me, _ref5;
- me = this;
- _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lon = me.clon(lon);
- if (Math.abs(lat) > me.lat1) {
- return me.p1.project(lon, lat);
- } else {
- return me.p0.project(lon, lat);
- }
- };
+ FYCN = 1.75859;
- return GoodeHomolosine;
+ FYCS = 1.93052;
- })(PseudoCylindrical);
+ RYCN = 0.56863737426006061674;
- __proj['goodehomolosine'] = GoodeHomolosine;
+ RYCS = 0.51799515156538134803;
- Nicolosi = (function(_super) {
- var EPS;
+ FXC = 0.85;
- __extends(Nicolosi, _super);
+ RXC = 1.17647058823529411764;
- Nicolosi.title = "Nicolosi Globular Projection";
+ function Hatano(opts) {
+ Hatano.__super__.constructor.call(this, opts);
+ }
- Nicolosi.parameters = ['lon0'];
+ Hatano.prototype.project = function (lon, lat) {
+ var c, i, lam, me, phi, th1, x, y, _i, _ref5;
+ me = this;
+ _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lam = me.rad(me.clon(lon));
+ phi = me.rad(lat);
+ c = Math.sin(phi) * (phi < 0.0 ? CS : CN);
+ for (i = _i = NITER; _i >= 1; i = _i += -1) {
+ th1 = (phi + Math.sin(phi) - c) / (1.0 + Math.cos(phi));
+ phi -= th1;
+ if (Math.abs(th1) < EPS) {
+ break;
+ }
+ }
+ x = 1000 * FXC * lam * Math.cos(phi *= 0.5);
+ y = 1000 * Math.sin(phi) * (phi < 0.0 ? FYCS : FYCN);
+ return [x, y * -1];
+ };
- EPS = 1e-10;
+ return Hatano;
- function Nicolosi(opts) {
- Nicolosi.__super__.constructor.call(this, opts);
- this.r = this.HALFPI * 100;
- }
+ })(PseudoCylindrical);
- Nicolosi.prototype._visible = function(lon, lat) {
- var me;
- me = this;
- lon = me.clon(lon);
- return lon > -90 && lon < 90;
- };
+ __proj['hatano'] = Hatano;
- Nicolosi.prototype.project = function(lon, lat) {
- var c, d, lam, m, me, n, phi, r2, sp, tb, x, y, _ref5;
- me = this;
- _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lam = me.rad(me.clon(lon));
- phi = me.rad(lat);
- if (Math.abs(lam) < EPS) {
- x = 0;
- y = phi;
- } else if (Math.abs(phi) < EPS) {
- x = lam;
- y = 0;
- } else if (Math.abs(Math.abs(lam) - me.HALFPI) < EPS) {
- x = lam * Math.cos(phi);
- y = me.HALFPI * Math.sin(phi);
- } else if (Math.abs(Math.abs(phi) - me.HALFPI) < EPS) {
- x = 0;
- y = phi;
- } else {
- tb = me.HALFPI / lam - lam / me.HALFPI;
- c = phi / me.HALFPI;
- sp = Math.sin(phi);
- d = (1 - c * c) / (sp - c);
- r2 = tb / d;
- r2 *= r2;
- m = (tb * sp / d - 0.5 * tb) / (1.0 + r2);
- n = (sp / r2 + 0.5 * d) / (1.0 + 1.0 / r2);
- x = Math.cos(phi);
- x = Math.sqrt(m * m + x * x / (1.0 + r2));
- x = me.HALFPI * (m + (lam < 0 ? -x : x));
- y = Math.sqrt(n * n - (sp * sp / r2 + d * sp - 1.0) / (1.0 + 1.0 / r2));
- y = me.HALFPI * (n + (phi < 0 ? y : -y));
- }
- return [x * 100, y * -100];
- };
+ GoodeHomolosine = (function (_super) {
- Nicolosi.prototype.sea = function() {
- var math, out, phi, r, _i;
- out = [];
- r = this.r;
- math = Math;
- for (phi = _i = 0; _i <= 360; phi = ++_i) {
- out.push([math.cos(this.rad(phi)) * r, math.sin(this.rad(phi)) * r]);
- }
- return out;
- };
+ __extends(GoodeHomolosine, _super);
- Nicolosi.prototype.world_bbox = function() {
- var r;
- r = this.r;
- return new kartograph.BBox(-r, -r, r * 2, r * 2);
- };
+ GoodeHomolosine.title = "Goode Homolosine Projection";
- return Nicolosi;
+ GoodeHomolosine.parameters = ['lon0'];
- })(PseudoCylindrical);
+ function GoodeHomolosine(opts) {
+ var me;
+ GoodeHomolosine.__super__.constructor.call(this, opts);
+ me = this;
+ me.lat1 = 41.737;
+ me.p1 = new Mollweide();
+ me.p0 = new Sinusoidal();
+ }
- __proj['nicolosi'] = Nicolosi;
+ GoodeHomolosine.prototype.project = function (lon, lat) {
+ var me, _ref5;
+ me = this;
+ _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lon = me.clon(lon);
+ if (Math.abs(lat) > me.lat1) {
+ return me.p1.project(lon, lat);
+ } else {
+ return me.p0.project(lon, lat);
+ }
+ };
- Azimuthal = (function(_super) {
+ return GoodeHomolosine;
- __extends(Azimuthal, _super);
+ })(PseudoCylindrical);
- /*
- Base class for azimuthal projections
- */
+ __proj['goodehomolosine'] = GoodeHomolosine;
+ Nicolosi = (function (_super) {
+ var EPS;
- Azimuthal.parameters = ['lon0', 'lat0'];
+ __extends(Nicolosi, _super);
- Azimuthal.title = "Azimuthal Projection";
+ Nicolosi.title = "Nicolosi Globular Projection";
- function Azimuthal(opts, rad) {
- var me;
- if (rad == null) {
- rad = 1000;
- }
- Azimuthal.__super__.constructor.call(this, opts);
- me = this;
- me.r = rad;
- me.elevation0 = me.to_elevation(me.lat0);
- me.azimuth0 = me.to_azimuth(me.lon0);
- }
+ Nicolosi.parameters = ['lon0'];
- Azimuthal.prototype.to_elevation = function(lat) {
- var me;
- me = this;
- return ((lat + 90) / 180) * me.PI - me.HALFPI;
- };
+ EPS = 1e-10;
- Azimuthal.prototype.to_azimuth = function(lon) {
- var me;
- me = this;
- return ((lon + 180) / 360) * me.PI * 2 - me.PI;
- };
+ function Nicolosi(opts) {
+ Nicolosi.__super__.constructor.call(this, opts);
+ this.r = this.HALFPI * 100;
+ }
- Azimuthal.prototype._visible = function(lon, lat) {
- var azimuth, cosc, elevation, math, me;
- me = this;
- math = Math;
- elevation = me.to_elevation(lat);
- azimuth = me.to_azimuth(lon);
- cosc = math.sin(elevation) * math.sin(me.elevation0) + math.cos(me.elevation0) * math.cos(elevation) * math.cos(azimuth - me.azimuth0);
- return cosc >= 0.0;
- };
+ Nicolosi.prototype._visible = function (lon, lat) {
+ var me;
+ me = this;
+ lon = me.clon(lon);
+ return lon > -90 && lon < 90;
+ };
- Azimuthal.prototype._truncate = function(x, y) {
- var math, r, theta, x1, y1;
- math = Math;
- r = this.r;
- theta = math.atan2(y - r, x - r);
- x1 = r + r * math.cos(theta);
- y1 = r + r * math.sin(theta);
- return [x1, y1];
- };
+ Nicolosi.prototype.project = function (lon, lat) {
+ var c, d, lam, m, me, n, phi, r2, sp, tb, x, y, _ref5;
+ me = this;
+ _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lam = me.rad(me.clon(lon));
+ phi = me.rad(lat);
+ if (Math.abs(lam) < EPS) {
+ x = 0;
+ y = phi;
+ } else if (Math.abs(phi) < EPS) {
+ x = lam;
+ y = 0;
+ } else if (Math.abs(Math.abs(lam) - me.HALFPI) < EPS) {
+ x = lam * Math.cos(phi);
+ y = me.HALFPI * Math.sin(phi);
+ } else if (Math.abs(Math.abs(phi) - me.HALFPI) < EPS) {
+ x = 0;
+ y = phi;
+ } else {
+ tb = me.HALFPI / lam - lam / me.HALFPI;
+ c = phi / me.HALFPI;
+ sp = Math.sin(phi);
+ d = (1 - c * c) / (sp - c);
+ r2 = tb / d;
+ r2 *= r2;
+ m = (tb * sp / d - 0.5 * tb) / (1.0 + r2);
+ n = (sp / r2 + 0.5 * d) / (1.0 + 1.0 / r2);
+ x = Math.cos(phi);
+ x = Math.sqrt(m * m + x * x / (1.0 + r2));
+ x = me.HALFPI * (m + (lam < 0 ? -x : x));
+ y = Math.sqrt(n * n - (sp * sp / r2 + d * sp - 1.0) / (1.0 + 1.0 / r2));
+ y = me.HALFPI * (n + (phi < 0 ? y : -y));
+ }
+ return [x * 100, y * -100];
+ };
- Azimuthal.prototype.sea = function() {
- var math, out, phi, r, _i;
- out = [];
- r = this.r;
- math = Math;
- for (phi = _i = 0; _i <= 360; phi = ++_i) {
- out.push([r + math.cos(this.rad(phi)) * r, r + math.sin(this.rad(phi)) * r]);
- }
- return out;
- };
+ Nicolosi.prototype.sea = function () {
+ var math, out, phi, r, _i;
+ out = [];
+ r = this.r;
+ math = Math;
+ for (phi = _i = 0; _i <= 360; phi = ++_i) {
+ out.push([math.cos(this.rad(phi)) * r, math.sin(this.rad(phi)) * r]);
+ }
+ return out;
+ };
- Azimuthal.prototype.world_bbox = function() {
- var r;
- r = this.r;
- return new kartograph.BBox(0, 0, r * 2, r * 2);
- };
+ Nicolosi.prototype.world_bbox = function () {
+ var r;
+ r = this.r;
+ return new kartograph.BBox(-r, -r, r * 2, r * 2);
+ };
- return Azimuthal;
+ return Nicolosi;
- })(Proj);
+ })(PseudoCylindrical);
- Orthographic = (function(_super) {
+ __proj['nicolosi'] = Nicolosi;
- __extends(Orthographic, _super);
+ Azimuthal = (function (_super) {
- /*
- Orthographic Azimuthal Projection
-
- implementation taken from http://www.mccarroll.net/snippets/svgworld/
- */
+ __extends(Azimuthal, _super);
+ /*
+ Base class for azimuthal projections
+ */
- function Orthographic() {
- return Orthographic.__super__.constructor.apply(this, arguments);
- }
- Orthographic.title = "Orthographic Projection";
-
- Orthographic.prototype.project = function(lon, lat) {
- var azimuth, elevation, math, me, x, xo, y, yo;
- me = this;
- math = Math;
- elevation = me.to_elevation(lat);
- azimuth = me.to_azimuth(lon);
- xo = me.r * math.cos(elevation) * math.sin(azimuth - me.azimuth0);
- yo = -me.r * (math.cos(me.elevation0) * math.sin(elevation) - math.sin(me.elevation0) * math.cos(elevation) * math.cos(azimuth - me.azimuth0));
- x = me.r + xo;
- y = me.r + yo;
- return [x, y];
- };
+ Azimuthal.parameters = ['lon0', 'lat0'];
- return Orthographic;
+ Azimuthal.title = "Azimuthal Projection";
- })(Azimuthal);
+ function Azimuthal(opts, rad) {
+ var me;
+ if (rad == null) {
+ rad = 1000;
+ }
+ Azimuthal.__super__.constructor.call(this, opts);
+ me = this;
+ me.r = rad;
+ me.elevation0 = me.to_elevation(me.lat0);
+ me.azimuth0 = me.to_azimuth(me.lon0);
+ }
- __proj['ortho'] = Orthographic;
+ Azimuthal.prototype.to_elevation = function (lat) {
+ var me;
+ me = this;
+ return ((lat + 90) / 180) * me.PI - me.HALFPI;
+ };
- LAEA = (function(_super) {
+ Azimuthal.prototype.to_azimuth = function (lon) {
+ var me;
+ me = this;
+ return ((lon + 180) / 360) * me.PI * 2 - me.PI;
+ };
- __extends(LAEA, _super);
+ Azimuthal.prototype._visible = function (lon, lat) {
+ var azimuth, cosc, elevation, math, me;
+ me = this;
+ math = Math;
+ elevation = me.to_elevation(lat);
+ azimuth = me.to_azimuth(lon);
+ cosc = math.sin(elevation) * math.sin(me.elevation0) + math.cos(me.elevation0) * math.cos(elevation) * math.cos(azimuth - me.azimuth0);
+ return cosc >= 0.0;
+ };
- /*
- Lambert Azimuthal Equal-Area Projection
-
- implementation taken from
- Snyder, Map projections - A working manual
- */
+ Azimuthal.prototype._truncate = function (x, y) {
+ var math, r, theta, x1, y1;
+ math = Math;
+ r = this.r;
+ theta = math.atan2(y - r, x - r);
+ x1 = r + r * math.cos(theta);
+ y1 = r + r * math.sin(theta);
+ return [x1, y1];
+ };
+ Azimuthal.prototype.sea = function () {
+ var math, out, phi, r, _i;
+ out = [];
+ r = this.r;
+ math = Math;
+ for (phi = _i = 0; _i <= 360; phi = ++_i) {
+ out.push([r + math.cos(this.rad(phi)) * r, r + math.sin(this.rad(phi)) * r]);
+ }
+ return out;
+ };
- LAEA.title = "Lambert Azimuthal Equal-Area Projection";
+ Azimuthal.prototype.world_bbox = function () {
+ var r;
+ r = this.r;
+ return new kartograph.BBox(0, 0, r * 2, r * 2);
+ };
- function LAEA(opts) {
- LAEA.__super__.constructor.call(this, opts);
- this.scale = Math.sqrt(2) * 0.5;
- }
+ return Azimuthal;
- LAEA.prototype.project = function(lon, lat) {
- var cos, k, lam, math, phi, sin, x, xo, y, yo;
- phi = this.rad(lat);
- lam = this.rad(lon);
- math = Math;
- sin = math.sin;
- cos = math.cos;
- if (false && math.abs(lon - this.lon0) === 180) {
- xo = this.r * 2;
- yo = 0;
- } else {
- k = math.pow(2 / (1 + sin(this.phi0) * sin(phi) + cos(this.phi0) * cos(phi) * cos(lam - this.lam0)), .5);
- k *= this.scale;
- xo = this.r * k * cos(phi) * sin(lam - this.lam0);
- yo = -this.r * k * (cos(this.phi0) * sin(phi) - sin(this.phi0) * cos(phi) * cos(lam - this.lam0));
- }
- x = this.r + xo;
- y = this.r + yo;
- return [x, y];
- };
+ })(Proj);
- return LAEA;
+ Orthographic = (function (_super) {
- })(Azimuthal);
+ __extends(Orthographic, _super);
- __proj['laea'] = LAEA;
+ /*
+ Orthographic Azimuthal Projection
- Stereographic = (function(_super) {
+ implementation taken from http://www.mccarroll.net/snippets/svgworld/
+ */
- __extends(Stereographic, _super);
- /*
- Stereographic projection
-
- implementation taken from
- Snyder, Map projections - A working manual
- */
+ function Orthographic() {
+ return Orthographic.__super__.constructor.apply(this, arguments);
+ }
+ Orthographic.title = "Orthographic Projection";
+
+ Orthographic.prototype.project = function (lon, lat) {
+ var azimuth, elevation, math, me, x, xo, y, yo;
+ me = this;
+ math = Math;
+ elevation = me.to_elevation(lat);
+ azimuth = me.to_azimuth(lon);
+ xo = me.r * math.cos(elevation) * math.sin(azimuth - me.azimuth0);
+ yo = -me.r * (math.cos(me.elevation0) * math.sin(elevation) - math.sin(me.elevation0) * math.cos(elevation) * math.cos(azimuth - me.azimuth0));
+ x = me.r + xo;
+ y = me.r + yo;
+ return [x, y];
+ };
- function Stereographic() {
- return Stereographic.__super__.constructor.apply(this, arguments);
- }
+ return Orthographic;
- Stereographic.title = "Stereographic Projection";
-
- Stereographic.prototype.project = function(lon, lat) {
- var cos, k, k0, lam, math, phi, sin, x, xo, y, yo;
- phi = this.rad(lat);
- lam = this.rad(lon);
- math = Math;
- sin = math.sin;
- cos = math.cos;
- k0 = 0.5;
- k = 2 * k0 / (1 + sin(this.phi0) * sin(phi) + cos(this.phi0) * cos(phi) * cos(lam - this.lam0));
- xo = this.r * k * cos(phi) * sin(lam - this.lam0);
- yo = -this.r * k * (cos(this.phi0) * sin(phi) - sin(this.phi0) * cos(phi) * cos(lam - this.lam0));
- x = this.r + xo;
- y = this.r + yo;
- return [x, y];
- };
+ })(Azimuthal);
- return Stereographic;
+ __proj['ortho'] = Orthographic;
- })(Azimuthal);
+ LAEA = (function (_super) {
- __proj['stereo'] = Stereographic;
+ __extends(LAEA, _super);
- Satellite = (function(_super) {
+ /*
+ Lambert Azimuthal Equal-Area Projection
- __extends(Satellite, _super);
+ implementation taken from
+ Snyder, Map projections - A working manual
+ */
- /*
- General perspective projection, aka Satellite projection
-
- implementation taken from
- Snyder, Map projections - A working manual
-
- up .. angle the camera is turned away from north (clockwise)
- tilt .. angle the camera is tilted
- */
-
-
- Satellite.parameters = ['lon0', 'lat0', 'tilt', 'dist', 'up'];
-
- Satellite.title = "Satellite Projection";
-
- function Satellite(opts) {
- var lat, lon, xmax, xmin, xy, _i, _j, _ref5, _ref6, _ref7;
- Satellite.__super__.constructor.call(this, {
- lon0: 0,
- lat0: 0
- });
- this.dist = (_ref5 = opts.dist) != null ? _ref5 : 3;
- this.up = this.rad((_ref6 = opts.up) != null ? _ref6 : 0);
- this.tilt = this.rad((_ref7 = opts.tilt) != null ? _ref7 : 0);
- this.scale = 1;
- xmin = Number.MAX_VALUE;
- xmax = Number.MAX_VALUE * -1;
- for (lat = _i = 0; _i <= 179; lat = ++_i) {
- for (lon = _j = 0; _j <= 360; lon = ++_j) {
- xy = this.project(lon - 180, lat - 90);
- xmin = Math.min(xy[0], xmin);
- xmax = Math.max(xy[0], xmax);
- }
- }
- this.scale = (this.r * 2) / (xmax - xmin);
- Satellite.__super__.constructor.call(this, opts);
- return;
- }
- Satellite.prototype.project = function(lon, lat, alt) {
- var A, H, cos, cos_c, cos_tilt, cos_up, k, lam, math, phi, r, ra, sin, sin_tilt, sin_up, x, xo, xt, y, yo, yt;
- if (alt == null) {
- alt = 0;
- }
- phi = this.rad(lat);
- lam = this.rad(lon);
- math = Math;
- sin = math.sin;
- cos = math.cos;
- r = this.r;
- ra = r * (alt + 6371) / 3671;
- cos_c = sin(this.phi0) * sin(phi) + cos(this.phi0) * cos(phi) * cos(lam - this.lam0);
- k = (this.dist - 1) / (this.dist - cos_c);
- k = (this.dist - 1) / (this.dist - cos_c);
- k *= this.scale;
- xo = ra * k * cos(phi) * sin(lam - this.lam0);
- yo = -ra * k * (cos(this.phi0) * sin(phi) - sin(this.phi0) * cos(phi) * cos(lam - this.lam0));
- cos_up = cos(this.up);
- sin_up = sin(this.up);
- cos_tilt = cos(this.tilt);
- sin_tilt = sin(this.tilt);
- H = ra * (this.dist - 1);
- A = ((yo * cos_up + xo * sin_up) * sin(this.tilt / H)) + cos_tilt;
- xt = (xo * cos_up - yo * sin_up) * cos(this.tilt / A);
- yt = (yo * cos_up + xo * sin_up) / A;
- x = r + xt;
- y = r + yt;
- return [x, y];
- };
+ LAEA.title = "Lambert Azimuthal Equal-Area Projection";
- Satellite.prototype._visible = function(lon, lat) {
- var azimuth, cosc, elevation, math;
- elevation = this.to_elevation(lat);
- azimuth = this.to_azimuth(lon);
- math = Math;
- cosc = math.sin(elevation) * math.sin(this.elevation0) + math.cos(this.elevation0) * math.cos(elevation) * math.cos(azimuth - this.azimuth0);
- return cosc >= (1.0 / this.dist);
- };
+ function LAEA(opts) {
+ LAEA.__super__.constructor.call(this, opts);
+ this.scale = Math.sqrt(2) * 0.5;
+ }
- Satellite.prototype.sea = function() {
- var math, out, phi, r, _i;
- out = [];
- r = this.r;
- math = Math;
- for (phi = _i = 0; _i <= 360; phi = ++_i) {
- out.push([r + math.cos(this.rad(phi)) * r, r + math.sin(this.rad(phi)) * r]);
- }
- return out;
- };
+ LAEA.prototype.project = function (lon, lat) {
+ var cos, k, lam, math, phi, sin, x, xo, y, yo;
+ phi = this.rad(lat);
+ lam = this.rad(lon);
+ math = Math;
+ sin = math.sin;
+ cos = math.cos;
+ if (false && math.abs(lon - this.lon0) === 180) {
+ xo = this.r * 2;
+ yo = 0;
+ } else {
+ k = math.pow(2 / (1 + sin(this.phi0) * sin(phi) + cos(this.phi0) * cos(phi) * cos(lam - this.lam0)), .5);
+ k *= this.scale;
+ xo = this.r * k * cos(phi) * sin(lam - this.lam0);
+ yo = -this.r * k * (cos(this.phi0) * sin(phi) - sin(this.phi0) * cos(phi) * cos(lam - this.lam0));
+ }
+ x = this.r + xo;
+ y = this.r + yo;
+ return [x, y];
+ };
- return Satellite;
+ return LAEA;
- })(Azimuthal);
+ })(Azimuthal);
- __proj['satellite'] = Satellite;
+ __proj['laea'] = LAEA;
- EquidistantAzimuthal = (function(_super) {
+ Stereographic = (function (_super) {
- __extends(EquidistantAzimuthal, _super);
+ __extends(Stereographic, _super);
- /*
- Equidistant projection
-
- implementation taken from
- Snyder, Map projections - A working manual
- */
+ /*
+ Stereographic projection
+ implementation taken from
+ Snyder, Map projections - A working manual
+ */
- function EquidistantAzimuthal() {
- return EquidistantAzimuthal.__super__.constructor.apply(this, arguments);
- }
- EquidistantAzimuthal.title = "Equidistant Azimuthal Projection";
-
- EquidistantAzimuthal.prototype.project = function(lon, lat) {
- var c, cos, cos_c, k, lam, math, me, phi, sin, x, xo, y, yo;
- me = this;
- phi = me.rad(lat);
- lam = me.rad(lon);
- math = Math;
- sin = math.sin;
- cos = math.cos;
- cos_c = sin(this.phi0) * sin(phi) + cos(this.phi0) * cos(phi) * cos(lam - this.lam0);
- c = math.acos(cos_c);
- k = 0.325 * c / sin(c);
- xo = this.r * k * cos(phi) * sin(lam - this.lam0);
- yo = -this.r * k * (cos(this.phi0) * sin(phi) - sin(this.phi0) * cos(phi) * cos(lam - this.lam0));
- x = this.r + xo;
- y = this.r + yo;
- return [x, y];
- };
+ function Stereographic() {
+ return Stereographic.__super__.constructor.apply(this, arguments);
+ }
- EquidistantAzimuthal.prototype._visible = function(lon, lat) {
- return true;
- };
+ Stereographic.title = "Stereographic Projection";
+
+ Stereographic.prototype.project = function (lon, lat) {
+ var cos, k, k0, lam, math, phi, sin, x, xo, y, yo;
+ phi = this.rad(lat);
+ lam = this.rad(lon);
+ math = Math;
+ sin = math.sin;
+ cos = math.cos;
+ k0 = 0.5;
+ k = 2 * k0 / (1 + sin(this.phi0) * sin(phi) + cos(this.phi0) * cos(phi) * cos(lam - this.lam0));
+ xo = this.r * k * cos(phi) * sin(lam - this.lam0);
+ yo = -this.r * k * (cos(this.phi0) * sin(phi) - sin(this.phi0) * cos(phi) * cos(lam - this.lam0));
+ x = this.r + xo;
+ y = this.r + yo;
+ return [x, y];
+ };
- return EquidistantAzimuthal;
+ return Stereographic;
- })(Azimuthal);
+ })(Azimuthal);
- __proj['equi'] = EquidistantAzimuthal;
+ __proj['stereo'] = Stereographic;
- Aitoff = (function(_super) {
- var COSPHI1;
+ Satellite = (function (_super) {
- __extends(Aitoff, _super);
+ __extends(Satellite, _super);
- /*
- Aitoff projection
-
- implementation taken from
- Snyder, Map projections - A working manual
- */
+ /*
+ General perspective projection, aka Satellite projection
+ implementation taken from
+ Snyder, Map projections - A working manual
- Aitoff.title = "Aitoff Projection";
+ up .. angle the camera is turned away from north (clockwise)
+ tilt .. angle the camera is tilted
+ */
- Aitoff.parameters = ['lon0'];
- COSPHI1 = 0.636619772367581343;
+ Satellite.parameters = ['lon0', 'lat0', 'tilt', 'dist', 'up'];
- function Aitoff(opts) {
- var me;
- me = this;
- opts.lat0 = 0;
- Aitoff.__super__.constructor.call(this, opts);
- me.lam0 = 0;
- }
+ Satellite.title = "Satellite Projection";
- Aitoff.prototype.project = function(lon, lat) {
- var c, d, lam, me, phi, x, y, _ref5;
- me = this;
- _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
- lon = me.clon(lon);
- lam = me.rad(lon);
- phi = me.rad(lat);
- c = 0.5 * lam;
- d = Math.acos(Math.cos(phi) * Math.cos(c));
- if (d !== 0) {
- y = 1.0 / Math.sin(d);
- x = 2.0 * d * Math.cos(phi) * Math.sin(c) * y;
- y *= d * Math.sin(phi);
- } else {
- x = y = 0;
- }
- if (me.winkel) {
- x = (x + lam * COSPHI1) * 0.5;
- y = (y + phi) * 0.5;
- }
- return [x * 1000, y * -1000];
- };
+ function Satellite(opts) {
+ var lat, lon, xmax, xmin, xy, _i, _j, _ref5, _ref6, _ref7;
+ Satellite.__super__.constructor.call(this, {
+ lon0: 0,
+ lat0: 0
+ });
+ this.dist = (_ref5 = opts.dist) != null ? _ref5 : 3;
+ this.up = this.rad((_ref6 = opts.up) != null ? _ref6 : 0);
+ this.tilt = this.rad((_ref7 = opts.tilt) != null ? _ref7 : 0);
+ this.scale = 1;
+ xmin = Number.MAX_VALUE;
+ xmax = Number.MAX_VALUE * -1;
+ for (lat = _i = 0; _i <= 179; lat = ++_i) {
+ for (lon = _j = 0; _j <= 360; lon = ++_j) {
+ xy = this.project(lon - 180, lat - 90);
+ xmin = Math.min(xy[0], xmin);
+ xmax = Math.max(xy[0], xmax);
+ }
+ }
+ this.scale = (this.r * 2) / (xmax - xmin);
+ Satellite.__super__.constructor.call(this, opts);
+ return;
+ }
- Aitoff.prototype._visible = function(lon, lat) {
- return true;
- };
+ Satellite.prototype.project = function (lon, lat, alt) {
+ var A, H, cos, cos_c, cos_tilt, cos_up, k, lam, math, phi, r, ra, sin, sin_tilt, sin_up, x, xo, xt, y, yo, yt;
+ if (alt == null) {
+ alt = 0;
+ }
+ phi = this.rad(lat);
+ lam = this.rad(lon);
+ math = Math;
+ sin = math.sin;
+ cos = math.cos;
+ r = this.r;
+ ra = r * (alt + 6371) / 3671;
+ cos_c = sin(this.phi0) * sin(phi) + cos(this.phi0) * cos(phi) * cos(lam - this.lam0);
+ k = (this.dist - 1) / (this.dist - cos_c);
+ k = (this.dist - 1) / (this.dist - cos_c);
+ k *= this.scale;
+ xo = ra * k * cos(phi) * sin(lam - this.lam0);
+ yo = -ra * k * (cos(this.phi0) * sin(phi) - sin(this.phi0) * cos(phi) * cos(lam - this.lam0));
+ cos_up = cos(this.up);
+ sin_up = sin(this.up);
+ cos_tilt = cos(this.tilt);
+ sin_tilt = sin(this.tilt);
+ H = ra * (this.dist - 1);
+ A = ((yo * cos_up + xo * sin_up) * sin(this.tilt / H)) + cos_tilt;
+ xt = (xo * cos_up - yo * sin_up) * cos(this.tilt / A);
+ yt = (yo * cos_up + xo * sin_up) / A;
+ x = r + xt;
+ y = r + yt;
+ return [x, y];
+ };
- return Aitoff;
+ Satellite.prototype._visible = function (lon, lat) {
+ var azimuth, cosc, elevation, math;
+ elevation = this.to_elevation(lat);
+ azimuth = this.to_azimuth(lon);
+ math = Math;
+ cosc = math.sin(elevation) * math.sin(this.elevation0) + math.cos(this.elevation0) * math.cos(elevation) * math.cos(azimuth - this.azimuth0);
+ return cosc >= (1.0 / this.dist);
+ };
- })(PseudoCylindrical);
+ Satellite.prototype.sea = function () {
+ var math, out, phi, r, _i;
+ out = [];
+ r = this.r;
+ math = Math;
+ for (phi = _i = 0; _i <= 360; phi = ++_i) {
+ out.push([r + math.cos(this.rad(phi)) * r, r + math.sin(this.rad(phi)) * r]);
+ }
+ return out;
+ };
- __proj['aitoff'] = Aitoff;
+ return Satellite;
- Winkel3 = (function(_super) {
+ })(Azimuthal);
- __extends(Winkel3, _super);
+ __proj['satellite'] = Satellite;
- Winkel3.title = "Winkel Tripel Projection";
+ EquidistantAzimuthal = (function (_super) {
- function Winkel3(opts) {
- Winkel3.__super__.constructor.call(this, opts);
- this.winkel = true;
- }
+ __extends(EquidistantAzimuthal, _super);
- return Winkel3;
+ /*
+ Equidistant projection
- })(Aitoff);
+ implementation taken from
+ Snyder, Map projections - A working manual
+ */
- __proj['winkel3'] = Winkel3;
- Conic = (function(_super) {
+ function EquidistantAzimuthal() {
+ return EquidistantAzimuthal.__super__.constructor.apply(this, arguments);
+ }
- __extends(Conic, _super);
+ EquidistantAzimuthal.title = "Equidistant Azimuthal Projection";
+
+ EquidistantAzimuthal.prototype.project = function (lon, lat) {
+ var c, cos, cos_c, k, lam, math, me, phi, sin, x, xo, y, yo;
+ me = this;
+ phi = me.rad(lat);
+ lam = me.rad(lon);
+ math = Math;
+ sin = math.sin;
+ cos = math.cos;
+ cos_c = sin(this.phi0) * sin(phi) + cos(this.phi0) * cos(phi) * cos(lam - this.lam0);
+ c = math.acos(cos_c);
+ k = 0.325 * c / sin(c);
+ xo = this.r * k * cos(phi) * sin(lam - this.lam0);
+ yo = -this.r * k * (cos(this.phi0) * sin(phi) - sin(this.phi0) * cos(phi) * cos(lam - this.lam0));
+ x = this.r + xo;
+ y = this.r + yo;
+ return [x, y];
+ };
- Conic.title = "Conic Projection";
+ EquidistantAzimuthal.prototype._visible = function (lon, lat) {
+ return true;
+ };
- Conic.parameters = ['lon0', 'lat0', 'lat1', 'lat2'];
+ return EquidistantAzimuthal;
- function Conic(opts) {
- var self, _ref5, _ref6;
- self = this;
- Conic.__super__.constructor.call(this, opts);
- self.lat1 = (_ref5 = opts.lat1) != null ? _ref5 : 30;
- self.phi1 = self.rad(self.lat1);
- self.lat2 = (_ref6 = opts.lat2) != null ? _ref6 : 50;
- self.phi2 = self.rad(self.lat2);
- }
+ })(Azimuthal);
- Conic.prototype._visible = function(lon, lat) {
- var self;
- self = this;
- return lat > self.minLat && lat < self.maxLat;
- };
+ __proj['equi'] = EquidistantAzimuthal;
- Conic.prototype._truncate = function(x, y) {
- return [x, y];
- };
+ Aitoff = (function (_super) {
+ var COSPHI1;
- Conic.prototype.clon = function(lon) {
- lon -= this.lon0;
- if (lon < -180) {
- lon += 360;
- } else if (lon > 180) {
- lon -= 360;
- }
- return lon;
- };
+ __extends(Aitoff, _super);
- return Conic;
+ /*
+ Aitoff projection
- })(Proj);
+ implementation taken from
+ Snyder, Map projections - A working manual
+ */
- LCC = (function(_super) {
- __extends(LCC, _super);
+ Aitoff.title = "Aitoff Projection";
- /*
- Lambert Conformal Conic Projection (spherical)
- */
-
-
- LCC.title = "Lambert Conformal Conic Projection";
-
- function LCC(opts) {
- var abs, c, cos, cosphi, m, n, pow, secant, self, sin, sinphi, tan, _ref5;
- self = this;
- LCC.__super__.constructor.call(this, opts);
- m = Math;
- _ref5 = [m.sin, m.cos, m.abs, m.log, m.tan, m.pow], sin = _ref5[0], cos = _ref5[1], abs = _ref5[2], log = _ref5[3], tan = _ref5[4], pow = _ref5[5];
- self.n = n = sinphi = sin(self.phi1);
- cosphi = cos(self.phi1);
- secant = abs(self.phi1 - self.phi2) >= 1e-10;
- if (secant) {
- n = log(cosphi / cos(self.phi2)) / log(tan(self.QUARTERPI + 0.5 * self.phi2) / tan(self.QUARTERPI + 0.5 * self.phi1));
- }
- self.c = c = cosphi * pow(tan(self.QUARTERPI + .5 * self.phi1), n) / n;
- if (abs(abs(self.phi0) - self.HALFPI) < 1e-10) {
- self.rho0 = 0.0;
- } else {
- self.rho0 = c * pow(tan(self.QUARTERPI + .5 * self.phi0), -n);
- }
- self.minLat = -60;
- self.maxLat = 85;
- }
+ Aitoff.parameters = ['lon0'];
- LCC.prototype.project = function(lon, lat) {
- var abs, cos, lam, lam_, m, n, phi, pow, rho, self, sin, tan, x, y, _ref5;
- self = this;
- phi = self.rad(lat);
- lam = self.rad(self.clon(lon));
- m = Math;
- _ref5 = [m.sin, m.cos, m.abs, m.log, m.tan, m.pow], sin = _ref5[0], cos = _ref5[1], abs = _ref5[2], log = _ref5[3], tan = _ref5[4], pow = _ref5[5];
- n = self.n;
- if (abs(abs(phi) - self.HALFPI) < 1e-10) {
- rho = 0.0;
- } else {
- rho = self.c * pow(tan(self.QUARTERPI + 0.5 * phi), -n);
- }
- lam_ = lam * n;
- x = 1000 * rho * sin(lam_);
- y = 1000 * (self.rho0 - rho * cos(lam_));
- return [x, y * -1];
- };
+ COSPHI1 = 0.636619772367581343;
- return LCC;
+ function Aitoff(opts) {
+ var me;
+ me = this;
+ opts.lat0 = 0;
+ Aitoff.__super__.constructor.call(this, opts);
+ me.lam0 = 0;
+ }
- })(Conic);
+ Aitoff.prototype.project = function (lon, lat) {
+ var c, d, lam, me, phi, x, y, _ref5;
+ me = this;
+ _ref5 = me.ll(lon, lat), lon = _ref5[0], lat = _ref5[1];
+ lon = me.clon(lon);
+ lam = me.rad(lon);
+ phi = me.rad(lat);
+ c = 0.5 * lam;
+ d = Math.acos(Math.cos(phi) * Math.cos(c));
+ if (d !== 0) {
+ y = 1.0 / Math.sin(d);
+ x = 2.0 * d * Math.cos(phi) * Math.sin(c) * y;
+ y *= d * Math.sin(phi);
+ } else {
+ x = y = 0;
+ }
+ if (me.winkel) {
+ x = (x + lam * COSPHI1) * 0.5;
+ y = (y + phi) * 0.5;
+ }
+ return [x * 1000, y * -1000];
+ };
- __proj['lcc'] = LCC;
+ Aitoff.prototype._visible = function (lon, lat) {
+ return true;
+ };
- PseudoConic = (function(_super) {
+ return Aitoff;
- __extends(PseudoConic, _super);
+ })(PseudoCylindrical);
- function PseudoConic() {
- return PseudoConic.__super__.constructor.apply(this, arguments);
- }
+ __proj['aitoff'] = Aitoff;
- return PseudoConic;
-
- })(Conic);
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more detailme.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- View = (function() {
- /*
- 2D coordinate transfomation
- */
-
- function View(bbox, width, height, padding, halign, valign) {
- var me;
- me = this;
- me.bbox = bbox;
- me.width = width;
- me.padding = padding != null ? padding : 0;
- me.halign = halign != null ? halign : 'center';
- me.valign = valign != null ? valign : 'center';
- me.height = height;
- me.scale = Math.min((width - padding * 2) / bbox.width, (height - padding * 2) / bbox.height);
- }
+ Winkel3 = (function (_super) {
- View.prototype.project = function(x, y) {
- var bbox, h, me, s, w, xf, yf;
- if (!(y != null)) {
- y = x[1];
- x = x[0];
- }
- me = this;
- s = me.scale;
- bbox = me.bbox;
- h = me.height;
- w = me.width;
- xf = me.halign === "center" ? (w - bbox.width * s) * 0.5 : me.halign === "left" ? me.padding * s : w - (bbox.width - me.padding) * s;
- yf = me.valign === "center" ? (h - bbox.height * s) * 0.5 : me.valign === "top" ? me.padding * s : 0;
- x = (x - bbox.left) * s + xf;
- y = (y - bbox.top) * s + yf;
- return [x, y];
- };
+ __extends(Winkel3, _super);
- View.prototype.projectPath = function(path) {
- var bbox, cont, contours, me, new_path, pcont, r, x, y, _i, _j, _len, _len1, _ref5, _ref6, _ref7, _ref8;
- me = this;
- if (path.type === "path") {
- contours = [];
- bbox = [99999, 99999, -99999, -99999];
- _ref5 = path.contours;
- for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
- pcont = _ref5[_i];
- cont = [];
- for (_j = 0, _len1 = pcont.length; _j < _len1; _j++) {
- _ref6 = pcont[_j], x = _ref6[0], y = _ref6[1];
- _ref7 = me.project(x, y), x = _ref7[0], y = _ref7[1];
- cont.push([x, y]);
- bbox[0] = Math.min(bbox[0], x);
- bbox[1] = Math.min(bbox[1], y);
- bbox[2] = Math.max(bbox[2], x);
- bbox[3] = Math.max(bbox[3], y);
- }
- contours.push(cont);
+ Winkel3.title = "Winkel Tripel Projection";
+
+ function Winkel3(opts) {
+ Winkel3.__super__.constructor.call(this, opts);
+ this.winkel = true;
}
- new_path = new kartograph.geom.Path(path.type, contours, path.closed);
- new_path._bbox = bbox;
- return new_path;
- } else if (path.type === "circle") {
- _ref8 = me.project(path.x, path.y), x = _ref8[0], y = _ref8[1];
- r = path.r * me.scale;
- return new kartograph.geom.Circle(x, y, r);
- }
- };
- View.prototype.asBBox = function() {
- var me;
- me = this;
- return new kartograph.BBox(0, 0, me.width, me.height);
- };
+ return Winkel3;
- return View;
+ })(Aitoff);
- })();
+ __proj['winkel3'] = Winkel3;
- View.fromXML = function(xml) {
- /*
- constructs a view from XML
- */
-
- var bbox, bbox_xml, h, pad, w;
- w = Number(xml.getAttribute('w'));
- h = Number(xml.getAttribute('h'));
- pad = Number(xml.getAttribute('padding'));
- bbox_xml = xml.getElementsByTagName('bbox')[0];
- bbox = BBox.fromXML(bbox_xml);
- return new kartograph.View(bbox, w, h, pad);
- };
-
- kartograph.View = View;
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- kartograph.Kartograph.prototype.dotgrid = function(opts) {
- var anim, data, data_col, data_key, delay, dly, dotgrid, dotstyle, ds, dur, f, g, gridsize, id, layer, layer_id, me, path, pathData, paths, pd, row, size, sizes, x, y, _i, _j, _k, _l, _len, _len1, _len2, _len3, _m, _n, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref5, _ref6, _ref7, _ref8, _ref9;
- me = this;
- layer_id = (_ref5 = opts.layer) != null ? _ref5 : me.layerIds[me.layerIds.length - 1];
- if (!me.layers.hasOwnProperty(layer_id)) {
- warn('dotgrid error: layer "' + layer_id + '" not found');
- return;
- }
- layer = me.layers[layer_id];
- data = opts.data;
- data_col = opts.value;
- data_key = opts.key;
- pathData = {};
- if ((data_key != null) && __type(data) === "array") {
- for (_i = 0, _len = data.length; _i < _len; _i++) {
- row = data[_i];
- id = row[data_key];
- pathData[String(id)] = row;
- }
- } else {
- for (id in data) {
- row = data[id];
- pathData[String(id)] = row;
- }
- }
- dotstyle = (_ref6 = opts.style) != null ? _ref6 : {
- fill: 'black',
- stroke: 'none'
- };
- sizes = opts.size;
- gridsize = (_ref7 = opts.gridsize) != null ? _ref7 : 15;
- dotgrid = (_ref8 = layer.dotgrid) != null ? _ref8 : layer.dotgrid = {
- gridsize: gridsize,
- grid: []
- };
- if (dotgrid.gridsize !== gridsize) {
- _ref9 = dotgrid.grid;
- for (_j = 0, _len1 = _ref9.length; _j < _len1; _j++) {
- g = _ref9[_j];
- if (g.shape != null) {
- g.shape.remove();
- g.shape = null;
+ Conic = (function (_super) {
+
+ __extends(Conic, _super);
+
+ Conic.title = "Conic Projection";
+
+ Conic.parameters = ['lon0', 'lat0', 'lat1', 'lat2'];
+
+ function Conic(opts) {
+ var self, _ref5, _ref6;
+ self = this;
+ Conic.__super__.constructor.call(this, opts);
+ self.lat1 = (_ref5 = opts.lat1) != null ? _ref5 : 30;
+ self.phi1 = self.rad(self.lat1);
+ self.lat2 = (_ref6 = opts.lat2) != null ? _ref6 : 50;
+ self.phi2 = self.rad(self.lat2);
}
- }
- }
- if (gridsize > 0) {
- if (dotgrid.grid.length === 0) {
- for (x = _k = 0, _ref10 = me.viewport.width; 0 <= _ref10 ? _k <= _ref10 : _k >= _ref10; x = _k += gridsize) {
- for (y = _l = 0, _ref11 = me.viewport.height; 0 <= _ref11 ? _l <= _ref11 : _l >= _ref11; y = _l += gridsize) {
- g = {
- x: x + (Math.random() - 0.5) * gridsize * 0.2,
- y: y + (Math.random() - 0.5) * gridsize * 0.2,
- pathid: false
- };
- f = false;
- _ref12 = layer.pathsById;
- for (id in _ref12) {
- paths = _ref12[id];
- for (_m = 0, _len2 = paths.length; _m < _len2; _m++) {
- path = paths[_m];
- if (path.vpath.isInside(g.x, g.y)) {
- f = true;
- pd = (_ref13 = pathData[id]) != null ? _ref13 : null;
- size = sizes(pd);
- g.pathid = id;
- g.shape = layer.paper.circle(g.x, g.y, 1);
- break;
- }
- }
- if (f) {
- break;
- }
+
+ Conic.prototype._visible = function (lon, lat) {
+ var self;
+ self = this;
+ return lat > self.minLat && lat < self.maxLat;
+ };
+
+ Conic.prototype._truncate = function (x, y) {
+ return [x, y];
+ };
+
+ Conic.prototype.clon = function (lon) {
+ lon -= this.lon0;
+ if (lon < -180) {
+ lon += 360;
+ } else if (lon > 180) {
+ lon -= 360;
}
- dotgrid.grid.push(g);
- }
- }
- }
- _ref14 = dotgrid.grid;
- for (_n = 0, _len3 = _ref14.length; _n < _len3; _n++) {
- g = _ref14[_n];
- if (g.pathid) {
- pd = (_ref15 = pathData[g.pathid]) != null ? _ref15 : null;
- size = sizes(pd);
- dur = (_ref16 = opts.duration) != null ? _ref16 : 0;
- delay = (_ref17 = opts.delay) != null ? _ref17 : 0;
- if (__type(delay) === "function") {
- dly = delay(pd);
- } else {
- dly = delay;
- }
- if (dur > 0 && Raphael.svg) {
- anim = Raphael.animation({
- r: size * 0.5
- }, dur);
- g.shape.animate(anim.delay(dly));
- } else {
- g.shape.attr({
- r: size * 0.5
- });
- }
- if (__type(dotstyle) === "function") {
- ds = dotstyle(pd);
- } else {
- ds = dotstyle;
- }
- g.shape.attr(ds);
- }
- }
- }
- };
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- filter = (_ref5 = kartograph.filter) != null ? _ref5 : kartograph.filter = {};
-
- filter.__knownFilter = {};
-
- filter.__patternFills = 0;
-
- MapLayer.prototype.SVG = function(el, attr) {
- var key, val;
- if (typeof el === "string") {
- el = window.document.createElementNS("http://www.w3.org/2000/svg", el);
- }
- if (attr) {
- for (key in attr) {
- val = attr[key];
- el.setAttribute(key, val);
- }
- }
- return el;
- };
+ return lon;
+ };
- kartograph.Kartograph.prototype.addFilter = function(id, type, params) {
- var doc, fltr, me;
- if (params == null) {
- params = {};
- }
- me = this;
- doc = window.document;
- if (kartograph.filter[type] != null) {
- fltr = new kartograph.filter[type](params).getFilter(id);
- } else {
- throw 'unknown filter type ' + type;
- }
- return me.paper.defs.appendChild(fltr);
- };
-
- MapLayer.prototype.applyFilter = function(filter_id) {
- var me;
- me = this;
- return $('.' + me.id, me.paper.canvas).attr({
- filter: 'url(#' + filter_id + ')'
- });
- };
-
- MapLayer.prototype.applyTexture = function(url, filt, defCol) {
- var lp, me, _i, _len, _ref6, _results;
- if (filt == null) {
- filt = false;
- }
- if (defCol == null) {
- defCol = '#000';
- }
- me = this;
- filter.__patternFills += 1;
- _ref6 = me.paths;
- _results = [];
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- lp = _ref6[_i];
- if (!filt || filt(lp.data)) {
- _results.push(lp.svgPath.attr({
- fill: 'url(' + url + ')'
- }));
- } else {
- _results.push(lp.svgPath.attr('fill', defCol));
- }
- }
- return _results;
- };
+ return Conic;
- Filter = (function() {
- /* base class for all svg filter
- */
+ })(Proj);
- function Filter(params) {
- this.params = params != null ? params : {};
- }
+ LCC = (function (_super) {
- Filter.prototype.getFilter = function(id) {
- var fltr, me;
- me = this;
- fltr = me.SVG('filter', {
- id: id
- });
- me.buildFilter(fltr);
- return fltr;
- };
+ __extends(LCC, _super);
+
+ /*
+ Lambert Conformal Conic Projection (spherical)
+ */
- Filter.prototype._getFilter = function() {
- throw "not implemented";
- };
- Filter.prototype.SVG = function(el, attr) {
- var key, val;
- if (typeof el === "string") {
- el = window.document.createElementNS("http://www.w3.org/2000/svg", el);
- }
- if (attr) {
- for (key in attr) {
- val = attr[key];
- el.setAttribute(key, val);
+ LCC.title = "Lambert Conformal Conic Projection";
+
+ function LCC(opts) {
+ var abs, c, cos, cosphi, m, n, pow, secant, self, sin, sinphi, tan, _ref5;
+ self = this;
+ LCC.__super__.constructor.call(this, opts);
+ m = Math;
+ _ref5 = [m.sin, m.cos, m.abs, m.log, m.tan, m.pow], sin = _ref5[0], cos = _ref5[1], abs = _ref5[2], log = _ref5[3], tan = _ref5[4], pow = _ref5[5];
+ self.n = n = sinphi = sin(self.phi1);
+ cosphi = cos(self.phi1);
+ secant = abs(self.phi1 - self.phi2) >= 1e-10;
+ if (secant) {
+ n = log(cosphi / cos(self.phi2)) / log(tan(self.QUARTERPI + 0.5 * self.phi2) / tan(self.QUARTERPI + 0.5 * self.phi1));
+ }
+ self.c = c = cosphi * pow(tan(self.QUARTERPI + .5 * self.phi1), n) / n;
+ if (abs(abs(self.phi0) - self.HALFPI) < 1e-10) {
+ self.rho0 = 0.0;
+ } else {
+ self.rho0 = c * pow(tan(self.QUARTERPI + .5 * self.phi0), -n);
+ }
+ self.minLat = -60;
+ self.maxLat = 85;
}
- }
- return el;
- };
- return Filter;
+ LCC.prototype.project = function (lon, lat) {
+ var abs, cos, lam, lam_, m, n, phi, pow, rho, self, sin, tan, x, y, _ref5;
+ self = this;
+ phi = self.rad(lat);
+ lam = self.rad(self.clon(lon));
+ m = Math;
+ _ref5 = [m.sin, m.cos, m.abs, m.log, m.tan, m.pow], sin = _ref5[0], cos = _ref5[1], abs = _ref5[2], log = _ref5[3], tan = _ref5[4], pow = _ref5[5];
+ n = self.n;
+ if (abs(abs(phi) - self.HALFPI) < 1e-10) {
+ rho = 0.0;
+ } else {
+ rho = self.c * pow(tan(self.QUARTERPI + 0.5 * phi), -n);
+ }
+ lam_ = lam * n;
+ x = 1000 * rho * sin(lam_);
+ y = 1000 * (self.rho0 - rho * cos(lam_));
+ return [x, y * -1];
+ };
- })();
+ return LCC;
- BlurFilter = (function(_super) {
+ })(Conic);
- __extends(BlurFilter, _super);
+ __proj['lcc'] = LCC;
- function BlurFilter() {
- return BlurFilter.__super__.constructor.apply(this, arguments);
- }
+ PseudoConic = (function (_super) {
+
+ __extends(PseudoConic, _super);
- /* simple gaussian blur filter
- */
+ function PseudoConic() {
+ return PseudoConic.__super__.constructor.apply(this, arguments);
+ }
+ return PseudoConic;
- BlurFilter.prototype.buildFilter = function(fltr) {
- var SVG, blur, me;
- me = this;
- SVG = me.SVG;
- blur = SVG('feGaussianBlur', {
- stdDeviation: me.params.size || 4,
- result: 'blur'
- });
- return fltr.appendChild(blur);
- };
+ })(Conic);
+
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more detailme.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ View = (function () {
+ /*
+ 2D coordinate transfomation
+ */
+
+ function View(bbox, width, height, padding, halign, valign) {
+ var me;
+ me = this;
+ me.bbox = bbox;
+ me.width = width;
+ me.padding = padding != null ? padding : 0;
+ me.halign = halign != null ? halign : 'center';
+ me.valign = valign != null ? valign : 'center';
+ me.height = height;
+ me.scale = Math.min((width - padding * 2) / bbox.width, (height - padding * 2) / bbox.height);
+ }
- return BlurFilter;
+ View.prototype.project = function (x, y) {
+ var bbox, h, me, s, w, xf, yf;
+ if (!(y != null)) {
+ y = x[1];
+ x = x[0];
+ }
+ me = this;
+ s = me.scale;
+ bbox = me.bbox;
+ h = me.height;
+ w = me.width;
+ xf = me.halign === "center" ? (w - bbox.width * s) * 0.5 : me.halign === "left" ? me.padding * s : w - (bbox.width - me.padding) * s;
+ yf = me.valign === "center" ? (h - bbox.height * s) * 0.5 : me.valign === "top" ? me.padding * s : 0;
+ x = (x - bbox.left) * s + xf;
+ y = (y - bbox.top) * s + yf;
+ return [x, y];
+ };
- })(Filter);
+ View.prototype.projectPath = function (path) {
+ var bbox, cont, contours, me, new_path, pcont, r, x, y, _i, _j, _len, _len1, _ref5, _ref6, _ref7, _ref8;
+ me = this;
+ if (path.type === "path") {
+ contours = [];
+ bbox = [99999, 99999, -99999, -99999];
+ _ref5 = path.contours;
+ for (_i = 0, _len = _ref5.length; _i < _len; _i++) {
+ pcont = _ref5[_i];
+ cont = [];
+ for (_j = 0, _len1 = pcont.length; _j < _len1; _j++) {
+ _ref6 = pcont[_j], x = _ref6[0], y = _ref6[1];
+ _ref7 = me.project(x, y), x = _ref7[0], y = _ref7[1];
+ cont.push([x, y]);
+ bbox[0] = Math.min(bbox[0], x);
+ bbox[1] = Math.min(bbox[1], y);
+ bbox[2] = Math.max(bbox[2], x);
+ bbox[3] = Math.max(bbox[3], y);
+ }
+ contours.push(cont);
+ }
+ new_path = new kartograph.geom.Path(path.type, contours, path.closed);
+ new_path._bbox = bbox;
+ return new_path;
+ } else if (path.type === "circle") {
+ _ref8 = me.project(path.x, path.y), x = _ref8[0], y = _ref8[1];
+ r = path.r * me.scale;
+ return new kartograph.geom.Circle(x, y, r);
+ }
+ };
- filter.blur = BlurFilter;
+ View.prototype.asBBox = function () {
+ var me;
+ me = this;
+ return new kartograph.BBox(0, 0, me.width, me.height);
+ };
- GlowFilter = (function(_super) {
+ return View;
- __extends(GlowFilter, _super);
+ })();
- function GlowFilter() {
- return GlowFilter.__super__.constructor.apply(this, arguments);
- }
+ View.fromXML = function (xml) {
+ /*
+ constructs a view from XML
+ */
- /* combined class for outer and inner glow filter
- */
-
-
- GlowFilter.prototype.buildFilter = function(fltr) {
- var alpha, blur, color, inner, knockout, me, rgb, strength, _ref10, _ref11, _ref6, _ref7, _ref8, _ref9;
- me = this;
- blur = (_ref6 = me.params.blur) != null ? _ref6 : 4;
- strength = (_ref7 = me.params.strength) != null ? _ref7 : 1;
- color = (_ref8 = me.params.color) != null ? _ref8 : '#D1BEB0';
- if (typeof color === 'string') {
- color = chroma.hex(color);
- }
- rgb = color.rgb;
- inner = (_ref9 = me.params.inner) != null ? _ref9 : false;
- knockout = (_ref10 = me.params.knockout) != null ? _ref10 : false;
- alpha = (_ref11 = me.params.alpha) != null ? _ref11 : 1;
- if (inner) {
- me.innerGlow(fltr, blur, strength, rgb, alpha, knockout);
- } else {
- me.outerGlow(fltr, blur, strength, rgb, alpha, knockout);
- }
+ var bbox, bbox_xml, h, pad, w;
+ w = Number(xml.getAttribute('w'));
+ h = Number(xml.getAttribute('h'));
+ pad = Number(xml.getAttribute('padding'));
+ bbox_xml = xml.getElementsByTagName('bbox')[0];
+ bbox = BBox.fromXML(bbox_xml);
+ return new kartograph.View(bbox, w, h, pad);
};
- GlowFilter.prototype.outerGlow = function(fltr, _blur, _strength, rgb, alpha, knockout) {
- var SVG, blur, comp, mat, me, merge, morph;
- me = this;
- SVG = me.SVG;
- mat = SVG('feColorMatrix', {
- "in": 'SourceGraphic',
- type: 'matrix',
- values: '0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0',
- result: 'mask'
- });
- fltr.appendChild(mat);
- if (_strength > 0) {
- morph = SVG('feMorphology', {
- "in": 'mask',
- radius: _strength,
- operator: 'dilate',
- result: 'mask'
- });
- fltr.appendChild(morph);
- }
- mat = SVG('feColorMatrix', {
- "in": 'mask',
- type: 'matrix',
- values: '0 0 0 0 ' + (rgb[0] / 255) + ' 0 0 0 0 ' + (rgb[1] / 255) + ' 0 0 0 0 ' + (rgb[2] / 255) + ' 0 0 0 1 0',
- result: 'r0'
- });
- fltr.appendChild(mat);
- blur = SVG('feGaussianBlur', {
- "in": 'r0',
- stdDeviation: _blur,
- result: 'r1'
- });
- fltr.appendChild(blur);
- comp = SVG('feComposite', {
- operator: 'out',
- "in": 'r1',
- in2: 'mask',
- result: 'comp'
- });
- fltr.appendChild(comp);
- merge = SVG('feMerge');
- if (!knockout) {
- merge.appendChild(SVG('feMergeNode', {
- 'in': 'SourceGraphic'
- }));
- }
- merge.appendChild(SVG('feMergeNode', {
- 'in': 'r1'
- }));
- return fltr.appendChild(merge);
- };
+ kartograph.View = View;
- GlowFilter.prototype.innerGlow = function(fltr, _blur, _strength, rgb, alpha, knockout) {
- var SVG, blur, comp, mat, me, merge, morph;
- me = this;
- SVG = me.SVG;
- log('innerglow');
- mat = SVG('feColorMatrix', {
- "in": 'SourceGraphic',
- type: 'matrix',
- values: '0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 0',
- result: 'mask'
- });
- fltr.appendChild(mat);
- morph = SVG('feMorphology', {
- "in": 'mask',
- radius: _strength,
- operator: 'erode',
- result: 'r1'
- });
- fltr.appendChild(morph);
- blur = SVG('feGaussianBlur', {
- "in": 'r1',
- stdDeviation: _blur,
- result: 'r2'
- });
- fltr.appendChild(blur);
- mat = SVG('feColorMatrix', {
- type: 'matrix',
- "in": 'r2',
- values: '1 0 0 0 ' + (rgb[0] / 255) + ' 0 1 0 0 ' + (rgb[1] / 255) + ' 0 0 1 0 ' + (rgb[2] / 255) + ' 0 0 0 -1 1',
- result: 'r3'
- });
- fltr.appendChild(mat);
- comp = SVG('feComposite', {
- operator: 'in',
- "in": 'r3',
- in2: 'mask',
- result: 'comp'
- });
- fltr.appendChild(comp);
- merge = SVG('feMerge');
- if (!knockout) {
- merge.appendChild(SVG('feMergeNode', {
- 'in': 'SourceGraphic'
- }));
- }
- merge.appendChild(SVG('feMergeNode', {
- 'in': 'comp'
- }));
- return fltr.appendChild(merge);
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ kartograph.Kartograph.prototype.dotgrid = function (opts) {
+ var anim, data, data_col, data_key, delay, dly, dotgrid, dotstyle, ds, dur, f, g, gridsize, id, layer, layer_id, me, path, pathData, paths, pd, row, size, sizes, x, y, _i, _j, _k, _l, _len, _len1, _len2, _len3, _m, _n, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref5, _ref6, _ref7, _ref8, _ref9;
+ me = this;
+ layer_id = (_ref5 = opts.layer) != null ? _ref5 : me.layerIds[me.layerIds.length - 1];
+ if (!me.layers.hasOwnProperty(layer_id)) {
+ warn('dotgrid error: layer "' + layer_id + '" not found');
+ return;
+ }
+ layer = me.layers[layer_id];
+ data = opts.data;
+ data_col = opts.value;
+ data_key = opts.key;
+ pathData = {};
+ if ((data_key != null) && __type(data) === "array") {
+ for (_i = 0, _len = data.length; _i < _len; _i++) {
+ row = data[_i];
+ id = row[data_key];
+ pathData[String(id)] = row;
+ }
+ } else {
+ for (id in data) {
+ row = data[id];
+ pathData[String(id)] = row;
+ }
+ }
+ dotstyle = (_ref6 = opts.style) != null ? _ref6 : {
+ fill: 'black',
+ stroke: 'none'
+ };
+ sizes = opts.size;
+ gridsize = (_ref7 = opts.gridsize) != null ? _ref7 : 15;
+ dotgrid = (_ref8 = layer.dotgrid) != null ? _ref8 : layer.dotgrid = {
+ gridsize: gridsize,
+ grid: []
+ };
+ if (dotgrid.gridsize !== gridsize) {
+ _ref9 = dotgrid.grid;
+ for (_j = 0, _len1 = _ref9.length; _j < _len1; _j++) {
+ g = _ref9[_j];
+ if (g.shape != null) {
+ g.shape.remove();
+ g.shape = null;
+ }
+ }
+ }
+ if (gridsize > 0) {
+ if (dotgrid.grid.length === 0) {
+ for (x = _k = 0, _ref10 = me.viewport.width; 0 <= _ref10 ? _k <= _ref10 : _k >= _ref10; x = _k += gridsize) {
+ for (y = _l = 0, _ref11 = me.viewport.height; 0 <= _ref11 ? _l <= _ref11 : _l >= _ref11; y = _l += gridsize) {
+ g = {
+ x: x + (Math.random() - 0.5) * gridsize * 0.2,
+ y: y + (Math.random() - 0.5) * gridsize * 0.2,
+ pathid: false
+ };
+ f = false;
+ _ref12 = layer.pathsById;
+ for (id in _ref12) {
+ paths = _ref12[id];
+ for (_m = 0, _len2 = paths.length; _m < _len2; _m++) {
+ path = paths[_m];
+ if (path.vpath.isInside(g.x, g.y)) {
+ f = true;
+ pd = (_ref13 = pathData[id]) != null ? _ref13 : null;
+ size = sizes(pd);
+ g.pathid = id;
+ g.shape = layer.paper.circle(g.x, g.y, 1);
+ break;
+ }
+ }
+ if (f) {
+ break;
+ }
+ }
+ dotgrid.grid.push(g);
+ }
+ }
+ }
+ _ref14 = dotgrid.grid;
+ for (_n = 0, _len3 = _ref14.length; _n < _len3; _n++) {
+ g = _ref14[_n];
+ if (g.pathid) {
+ pd = (_ref15 = pathData[g.pathid]) != null ? _ref15 : null;
+ size = sizes(pd);
+ dur = (_ref16 = opts.duration) != null ? _ref16 : 0;
+ delay = (_ref17 = opts.delay) != null ? _ref17 : 0;
+ if (__type(delay) === "function") {
+ dly = delay(pd);
+ } else {
+ dly = delay;
+ }
+ if (dur > 0 && Raphael.svg) {
+ anim = Raphael.animation({
+ r: size * 0.5
+ }, dur);
+ g.shape.animate(anim.delay(dly));
+ } else {
+ g.shape.attr({
+ r: size * 0.5
+ });
+ }
+ if (__type(dotstyle) === "function") {
+ ds = dotstyle(pd);
+ } else {
+ ds = dotstyle;
+ }
+ g.shape.attr(ds);
+ }
+ }
+ }
};
- return GlowFilter;
-
- })(Filter);
-
- filter.glow = GlowFilter;
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- kartograph.Kartograph.prototype.addGeoPath = function(points, cmds, className) {
- var me, path, path_str;
- if (cmds == null) {
- cmds = [];
- }
- if (className == null) {
- className = '';
- }
- /* converts a set of
- */
-
- me = this;
- path_str = me.getGeoPathStr(points, cmds);
- path = me.paper.path(path_str);
- if (className !== '') {
- path.node.setAttribute('class', className);
- }
- return path;
- };
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
- kartograph.Kartograph.prototype.getGeoPathStr = function(points, cmds) {
- var cmd, i, me, path_str, pt, xy, _ref6;
- if (cmds == null) {
- cmds = [];
- }
- /* converts a set of
- */
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- me = this;
- if (type(cmds) === 'string') {
- cmds = cmds.split("");
- }
- if (cmds.length === 0) {
- cmds.push('M');
- }
- path_str = '';
- for (i in points) {
- pt = points[i];
- cmd = (_ref6 = cmds[i]) != null ? _ref6 : 'L';
- xy = me.lonlat2xy(pt);
- if (isNaN(xy[0]) || isNaN(xy[1])) {
- continue;
- }
- path_str += cmd + xy[0] + ',' + xy[1];
- }
- return path_str;
- };
-
- kartograph.Kartograph.prototype.addGeoPolygon = function(points, className) {
- /* converts a set of
- */
-
- var cmds, i, me;
- me = this;
- cmds = ['M'];
- for (i in points) {
- cmds.push('L');
- }
- cmds.push('Z');
- return me.addGeoPath(points, cmds, className);
- };
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- PanAndZoomControl = (function() {
-
- function PanAndZoomControl(map) {
- this.zoomOut = __bind(this.zoomOut, this);
-
- this.zoomIn = __bind(this.zoomIn, this);
-
- var c, div, mdown, me, mup, zc, zcm, zcp;
- me = this;
- me.map = map;
- c = map.container;
- div = function(className, childNodes) {
- var child, d, _i, _len;
- if (childNodes == null) {
- childNodes = [];
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ filter = (_ref5 = kartograph.filter) != null ? _ref5 : kartograph.filter = {};
+
+ filter.__knownFilter = {};
+
+ filter.__patternFills = 0;
+
+ MapLayer.prototype.SVG = function (el, attr) {
+ var key, val;
+ if (typeof el === "string") {
+ el = window.document.createElementNS("http://www.w3.org/2000/svg", el);
}
- d = $('<div class="' + className + '" />');
- for (_i = 0, _len = childNodes.length; _i < _len; _i++) {
- child = childNodes[_i];
- d.append(child);
+ if (attr) {
+ for (key in attr) {
+ val = attr[key];
+ el.setAttribute(key, val);
+ }
}
- return d;
- };
- mdown = function(evt) {
- return $(evt.target).addClass('md');
- };
- mup = function(evt) {
- return $(evt.target).removeClass('md');
- };
- zcp = div('plus');
- zcp.mousedown(mdown);
- zcp.mouseup(mup);
- zcp.click(me.zoomIn);
- zcm = div('minus');
- zcm.mousedown(mdown);
- zcm.mouseup(mup);
- zcm.click(me.zoomOut);
- zc = div('zoom-control', [zcp, zcm]);
- c.append(zc);
- }
+ return el;
+ };
- PanAndZoomControl.prototype.zoomIn = function(evt) {
- var me;
- me = this;
- me.map.opts.zoom += 1;
- return me.map.resize();
+ kartograph.Kartograph.prototype.addFilter = function (id, type, params) {
+ var doc, fltr, me;
+ if (params == null) {
+ params = {};
+ }
+ me = this;
+ doc = window.document;
+ if (kartograph.filter[type] != null) {
+ fltr = new kartograph.filter[type](params).getFilter(id);
+ } else {
+ throw 'unknown filter type ' + type;
+ }
+ return me.paper.defs.appendChild(fltr);
};
- PanAndZoomControl.prototype.zoomOut = function(evt) {
- var me;
- me = this;
- me.map.opts.zoom -= 1;
- if (me.map.opts.zoom < 1) {
- me.map.opts.zoom = 1;
- }
- return me.map.resize();
+ MapLayer.prototype.applyFilter = function (filter_id) {
+ var me;
+ me = this;
+ return $('.' + me.id, me.paper.canvas).attr({
+ filter: 'url(#' + filter_id + ')'
+ });
};
- return PanAndZoomControl;
-
- })();
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- Scale = (function() {
- /* scales map values to [0..1]
- */
-
- function Scale(domain, prop, filter) {
- var i, me, val, values,
- _this = this;
- if (domain == null) {
- domain = [0, 1];
- }
- if (prop == null) {
- prop = null;
- }
- if (filter == null) {
- filter = null;
- }
- this.rangedScale = __bind(this.rangedScale, this);
-
- this.scale = __bind(this.scale, this);
-
- me = this;
- values = [];
- for (i in domain) {
- if (__type(filter) === "function") {
- if (filter(domain[i]) === false) {
- continue;
- }
+ MapLayer.prototype.applyTexture = function (url, filt, defCol) {
+ var lp, me, _i, _len, _ref6, _results;
+ if (filt == null) {
+ filt = false;
}
- if (prop != null) {
- if (__type(prop) === "function") {
- val = prop(domain[i]);
- } else {
- val = domain[i][prop];
- }
- } else {
- val = domain[i];
+ if (defCol == null) {
+ defCol = '#000';
}
- if (!isNaN(val)) {
- values.push(val);
+ me = this;
+ filter.__patternFills += 1;
+ _ref6 = me.paths;
+ _results = [];
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ lp = _ref6[_i];
+ if (!filt || filt(lp.data)) {
+ _results.push(lp.svgPath.attr({
+ fill: 'url(' + url + ')'
+ }));
+ } else {
+ _results.push(lp.svgPath.attr('fill', defCol));
+ }
}
- }
- values = values.sort(function(a, b) {
- return a - b;
- });
- me.values = values;
- me._range = [0, 1];
- me.rangedScale.range = function(_r) {
- me._range = _r;
- return me.rangedScale;
- };
- }
-
- Scale.prototype.scale = function(x) {
- return x;
+ return _results;
};
- Scale.prototype.rangedScale = function(x) {
- var me, r;
- me = this;
- x = me.scale(x);
- r = me._range;
- return x * (r[1] - r[0]) + r[0];
- };
+ Filter = (function () {
+ /* base class for all svg filter
+ */
- return Scale;
+ function Filter(params) {
+ this.params = params != null ? params : {};
+ }
- })();
+ Filter.prototype.getFilter = function (id) {
+ var fltr, me;
+ me = this;
+ fltr = me.SVG('filter', {
+ id: id
+ });
+ me.buildFilter(fltr);
+ return fltr;
+ };
- LinearScale = (function(_super) {
+ Filter.prototype._getFilter = function () {
+ throw "not implemented";
+ };
- __extends(LinearScale, _super);
+ Filter.prototype.SVG = function (el, attr) {
+ var key, val;
+ if (typeof el === "string") {
+ el = window.document.createElementNS("http://www.w3.org/2000/svg", el);
+ }
+ if (attr) {
+ for (key in attr) {
+ val = attr[key];
+ el.setAttribute(key, val);
+ }
+ }
+ return el;
+ };
- function LinearScale() {
- this.scale = __bind(this.scale, this);
- return LinearScale.__super__.constructor.apply(this, arguments);
- }
+ return Filter;
- /* liniear scale
- */
+ })();
+ BlurFilter = (function (_super) {
- LinearScale.prototype.scale = function(x) {
- var me, vals;
- me = this;
- vals = me.values;
- return (x - vals[0]) / (vals[vals.length - 1] - vals[0]);
- };
+ __extends(BlurFilter, _super);
- return LinearScale;
+ function BlurFilter() {
+ return BlurFilter.__super__.constructor.apply(this, arguments);
+ }
- })(Scale);
+ /* simple gaussian blur filter
+ */
- LogScale = (function(_super) {
- __extends(LogScale, _super);
+ BlurFilter.prototype.buildFilter = function (fltr) {
+ var SVG, blur, me;
+ me = this;
+ SVG = me.SVG;
+ blur = SVG('feGaussianBlur', {
+ stdDeviation: me.params.size || 4,
+ result: 'blur'
+ });
+ return fltr.appendChild(blur);
+ };
- function LogScale() {
- this.scale = __bind(this.scale, this);
- return LogScale.__super__.constructor.apply(this, arguments);
- }
+ return BlurFilter;
- /* logatithmic scale
- */
+ })(Filter);
+ filter.blur = BlurFilter;
- LogScale.prototype.scale = function(x) {
- var me, vals;
- me = this;
- vals = me.values;
- log = Math.log;
- return (log(x) - log(vals[0])) / (log(vals[vals.length - 1]) - log(vals[0]));
- };
+ GlowFilter = (function (_super) {
- return LogScale;
+ __extends(GlowFilter, _super);
- })(Scale);
+ function GlowFilter() {
+ return GlowFilter.__super__.constructor.apply(this, arguments);
+ }
- SqrtScale = (function(_super) {
+ /* combined class for outer and inner glow filter
+ */
- __extends(SqrtScale, _super);
- function SqrtScale() {
- this.scale = __bind(this.scale, this);
- return SqrtScale.__super__.constructor.apply(this, arguments);
- }
+ GlowFilter.prototype.buildFilter = function (fltr) {
+ var alpha, blur, color, inner, knockout, me, rgb, strength, _ref10, _ref11, _ref6, _ref7, _ref8, _ref9;
+ me = this;
+ blur = (_ref6 = me.params.blur) != null ? _ref6 : 4;
+ strength = (_ref7 = me.params.strength) != null ? _ref7 : 1;
+ color = (_ref8 = me.params.color) != null ? _ref8 : '#D1BEB0';
+ if (typeof color === 'string') {
+ color = chroma.hex(color);
+ }
+ rgb = color.rgb;
+ inner = (_ref9 = me.params.inner) != null ? _ref9 : false;
+ knockout = (_ref10 = me.params.knockout) != null ? _ref10 : false;
+ alpha = (_ref11 = me.params.alpha) != null ? _ref11 : 1;
+ if (inner) {
+ me.innerGlow(fltr, blur, strength, rgb, alpha, knockout);
+ } else {
+ me.outerGlow(fltr, blur, strength, rgb, alpha, knockout);
+ }
+ };
- /* square root scale
- */
+ GlowFilter.prototype.outerGlow = function (fltr, _blur, _strength, rgb, alpha, knockout) {
+ var SVG, blur, comp, mat, me, merge, morph;
+ me = this;
+ SVG = me.SVG;
+ mat = SVG('feColorMatrix', {
+ "in": 'SourceGraphic',
+ type: 'matrix',
+ values: '0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0',
+ result: 'mask'
+ });
+ fltr.appendChild(mat);
+ if (_strength > 0) {
+ morph = SVG('feMorphology', {
+ "in": 'mask',
+ radius: _strength,
+ operator: 'dilate',
+ result: 'mask'
+ });
+ fltr.appendChild(morph);
+ }
+ mat = SVG('feColorMatrix', {
+ "in": 'mask',
+ type: 'matrix',
+ values: '0 0 0 0 ' + (rgb[0] / 255) + ' 0 0 0 0 ' + (rgb[1] / 255) + ' 0 0 0 0 ' + (rgb[2] / 255) + ' 0 0 0 1 0',
+ result: 'r0'
+ });
+ fltr.appendChild(mat);
+ blur = SVG('feGaussianBlur', {
+ "in": 'r0',
+ stdDeviation: _blur,
+ result: 'r1'
+ });
+ fltr.appendChild(blur);
+ comp = SVG('feComposite', {
+ operator: 'out',
+ "in": 'r1',
+ in2: 'mask',
+ result: 'comp'
+ });
+ fltr.appendChild(comp);
+ merge = SVG('feMerge');
+ if (!knockout) {
+ merge.appendChild(SVG('feMergeNode', {
+ 'in': 'SourceGraphic'
+ }));
+ }
+ merge.appendChild(SVG('feMergeNode', {
+ 'in': 'r1'
+ }));
+ return fltr.appendChild(merge);
+ };
+ GlowFilter.prototype.innerGlow = function (fltr, _blur, _strength, rgb, alpha, knockout) {
+ var SVG, blur, comp, mat, me, merge, morph;
+ me = this;
+ SVG = me.SVG;
+ log('innerglow');
+ mat = SVG('feColorMatrix', {
+ "in": 'SourceGraphic',
+ type: 'matrix',
+ values: '0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 0',
+ result: 'mask'
+ });
+ fltr.appendChild(mat);
+ morph = SVG('feMorphology', {
+ "in": 'mask',
+ radius: _strength,
+ operator: 'erode',
+ result: 'r1'
+ });
+ fltr.appendChild(morph);
+ blur = SVG('feGaussianBlur', {
+ "in": 'r1',
+ stdDeviation: _blur,
+ result: 'r2'
+ });
+ fltr.appendChild(blur);
+ mat = SVG('feColorMatrix', {
+ type: 'matrix',
+ "in": 'r2',
+ values: '1 0 0 0 ' + (rgb[0] / 255) + ' 0 1 0 0 ' + (rgb[1] / 255) + ' 0 0 1 0 ' + (rgb[2] / 255) + ' 0 0 0 -1 1',
+ result: 'r3'
+ });
+ fltr.appendChild(mat);
+ comp = SVG('feComposite', {
+ operator: 'in',
+ "in": 'r3',
+ in2: 'mask',
+ result: 'comp'
+ });
+ fltr.appendChild(comp);
+ merge = SVG('feMerge');
+ if (!knockout) {
+ merge.appendChild(SVG('feMergeNode', {
+ 'in': 'SourceGraphic'
+ }));
+ }
+ merge.appendChild(SVG('feMergeNode', {
+ 'in': 'comp'
+ }));
+ return fltr.appendChild(merge);
+ };
- SqrtScale.prototype.scale = function(x) {
- var me, vals;
- me = this;
- vals = me.values;
- return Math.sqrt((x - vals[0]) / (vals[vals.length - 1] - vals[0]));
- };
+ return GlowFilter;
- return SqrtScale;
+ })(Filter);
- })(Scale);
+ filter.glow = GlowFilter;
- QuantileScale = (function(_super) {
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
- __extends(QuantileScale, _super);
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- function QuantileScale() {
- this.scale = __bind(this.scale, this);
- return QuantileScale.__super__.constructor.apply(this, arguments);
- }
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- /* quantiles scale
- */
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- QuantileScale.prototype.scale = function(x) {
- var i, k, me, nv, v, vals;
- me = this;
- vals = me.values;
- k = vals.length - 1;
- for (i in vals) {
- v = vals[Number(i)];
- nv = vals[Number(i) + 1];
- if (x === v) {
- return i / k;
+ kartograph.Kartograph.prototype.addGeoPath = function (points, cmds, className) {
+ var me, path, path_str;
+ if (cmds == null) {
+ cmds = [];
+ }
+ if (className == null) {
+ className = '';
}
- if (i < k && x > v && x < nv) {
- return i / k + (x - v) / (nv - v);
+ /* converts a set of
+ */
+
+ me = this;
+ path_str = me.getGeoPathStr(points, cmds);
+ path = me.paper.path(path_str);
+ if (className !== '') {
+ path.node.setAttribute('class', className);
}
- }
+ return path;
};
- return QuantileScale;
-
- })(Scale);
-
- kartograph.scale = {};
-
- kartograph.scale.identity = function(s) {
- return new Scale(domain, prop, filter).rangedScale;
- };
-
- kartograph.scale.linear = function(domain, prop, filter) {
- return new LinearScale(domain, prop, filter).rangedScale;
- };
-
- kartograph.scale.log = function(domain, prop, filter) {
- return new LogScale(domain, prop, filter).rangedScale;
- };
-
- kartograph.scale.sqrt = function(domain, prop, filter) {
- return new SqrtScale(domain, prop, filter).rangedScale;
- };
-
- kartograph.scale.quantile = function(domain, prop, filter) {
- return new QuantileScale(domain, prop, filter).rangedScale;
- };
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- Symbol = (function() {
- /* base class for all symbols
- */
-
- var me;
-
- me = null;
-
- function Symbol(opts) {
- me = this;
- me.location = opts.location;
- me.data = opts.data;
- me.map = opts.map;
- me.layers = opts.layers;
- me.key = opts.key;
- me.x = opts.x;
- me.y = opts.y;
- }
+ kartograph.Kartograph.prototype.getGeoPathStr = function (points, cmds) {
+ var cmd, i, me, path_str, pt, xy, _ref6;
+ if (cmds == null) {
+ cmds = [];
+ }
+ /* converts a set of
+ */
- Symbol.prototype.init = function() {
- return me;
+ me = this;
+ if (type(cmds) === 'string') {
+ cmds = cmds.split("");
+ }
+ if (cmds.length === 0) {
+ cmds.push('M');
+ }
+ path_str = '';
+ for (i in points) {
+ pt = points[i];
+ cmd = (_ref6 = cmds[i]) != null ? _ref6 : 'L';
+ xy = me.lonlat2xy(pt);
+ if (isNaN(xy[0]) || isNaN(xy[1])) {
+ continue;
+ }
+ path_str += cmd + xy[0] + ',' + xy[1];
+ }
+ return path_str;
};
- Symbol.prototype.overlaps = function(symbol) {
- return false;
- };
+ kartograph.Kartograph.prototype.addGeoPolygon = function (points, className) {
+ /* converts a set of
+ */
- Symbol.prototype.update = function(opts) {
- /* once the data has changed
- */
- return me;
+ var cmds, i, me;
+ me = this;
+ cmds = ['M'];
+ for (i in points) {
+ cmds.push('L');
+ }
+ cmds.push('Z');
+ return me.addGeoPath(points, cmds, className);
};
- Symbol.prototype.nodes = function() {
- return [];
- };
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
- Symbol.prototype.clear = function() {
- return me;
- };
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- return Symbol;
-
- })();
-
- kartograph.Symbol = Symbol;
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- SymbolGroup = (function() {
- /* symbol groups
-
- Usage:
- new $K.SymbolGroup(options);
- map.addSymbols(options)
- */
-
- var me;
-
- me = null;
-
- function SymbolGroup(opts) {
- this._initTooltips = __bind(this._initTooltips, this);
-
- this._noverlap = __bind(this._noverlap, this);
-
- this._kMeans = __bind(this._kMeans, this);
-
- var SymbolType, d, i, id, l, layer, nid, optional, p, required, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref6, _ref7;
- me = this;
- required = ['data', 'location', 'type', 'map'];
- optional = ['filter', 'tooltip', 'click', 'delay', 'sortBy', 'clustering', 'aggregate', 'clusteringOpts', 'mouseenter', 'mouseleave'];
- for (_i = 0, _len = required.length; _i < _len; _i++) {
- p = required[_i];
- if (opts[p] != null) {
- me[p] = opts[p];
- } else {
- throw "SymbolGroup: missing argument '" + p + "'";
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ PanAndZoomControl = (function () {
+
+ function PanAndZoomControl(map) {
+ this.zoomOut = __bind(this.zoomOut, this);
+
+ this.zoomIn = __bind(this.zoomIn, this);
+
+ var c, div, mdown, me, mup, zc, zcm, zcp;
+ me = this;
+ me.map = map;
+ c = map.container;
+ div = function (className, childNodes) {
+ var child, d, _i, _len;
+ if (childNodes == null) {
+ childNodes = [];
+ }
+ d = $('<div class="' + className + '" />');
+ for (_i = 0, _len = childNodes.length; _i < _len; _i++) {
+ child = childNodes[_i];
+ d.append(child);
+ }
+ return d;
+ };
+ mdown = function (evt) {
+ return $(evt.target).addClass('md');
+ };
+ mup = function (evt) {
+ return $(evt.target).removeClass('md');
+ };
+ zcp = div('plus');
+ zcp.mousedown(mdown);
+ zcp.mouseup(mup);
+ zcp.click(me.zoomIn);
+ zcm = div('minus');
+ zcm.mousedown(mdown);
+ zcm.mouseup(mup);
+ zcm.click(me.zoomOut);
+ zc = div('zoom-control', [zcp, zcm]);
+ c.append(zc);
}
- }
- for (_j = 0, _len1 = optional.length; _j < _len1; _j++) {
- p = optional[_j];
- if (opts[p] != null) {
- me[p] = opts[p];
+
+ PanAndZoomControl.prototype.zoomIn = function (evt) {
+ var me;
+ me = this;
+ me.map.opts.zoom += 1;
+ return me.map.resize();
+ };
+
+ PanAndZoomControl.prototype.zoomOut = function (evt) {
+ var me;
+ me = this;
+ me.map.opts.zoom -= 1;
+ if (me.map.opts.zoom < 1) {
+ me.map.opts.zoom = 1;
+ }
+ return me.map.resize();
+ };
+
+ return PanAndZoomControl;
+
+ })();
+
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011 Gregor Aisch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ Scale = (function () {
+ /* scales map values to [0..1]
+ */
+
+ function Scale(domain, prop, filter) {
+ var i, me, val, values,
+ _this = this;
+ if (domain == null) {
+ domain = [0, 1];
+ }
+ if (prop == null) {
+ prop = null;
+ }
+ if (filter == null) {
+ filter = null;
+ }
+ this.rangedScale = __bind(this.rangedScale, this);
+
+ this.scale = __bind(this.scale, this);
+
+ me = this;
+ values = [];
+ for (i in domain) {
+ if (__type(filter) === "function") {
+ if (filter(domain[i]) === false) {
+ continue;
+ }
+ }
+ if (prop != null) {
+ if (__type(prop) === "function") {
+ val = prop(domain[i]);
+ } else {
+ val = domain[i][prop];
+ }
+ } else {
+ val = domain[i];
+ }
+ if (!isNaN(val)) {
+ values.push(val);
+ }
+ }
+ values = values.sort(function (a, b) {
+ return a - b;
+ });
+ me.values = values;
+ me._range = [0, 1];
+ me.rangedScale.range = function (_r) {
+ me._range = _r;
+ return me.rangedScale;
+ };
}
- }
- SymbolType = me.type;
- if (!(SymbolType != null)) {
- warn('could not resolve symbol type', me.type);
- return;
- }
- _ref6 = SymbolType.props;
- for (_k = 0, _len2 = _ref6.length; _k < _len2; _k++) {
- p = _ref6[_k];
- if (opts[p] != null) {
- me[p] = opts[p];
+
+ Scale.prototype.scale = function (x) {
+ return x;
+ };
+
+ Scale.prototype.rangedScale = function (x) {
+ var me, r;
+ me = this;
+ x = me.scale(x);
+ r = me._range;
+ return x * (r[1] - r[0]) + r[0];
+ };
+
+ return Scale;
+
+ })();
+
+ LinearScale = (function (_super) {
+
+ __extends(LinearScale, _super);
+
+ function LinearScale() {
+ this.scale = __bind(this.scale, this);
+ return LinearScale.__super__.constructor.apply(this, arguments);
}
- }
- me.layers = {
- mapcanvas: me.map.paper
- };
- _ref7 = SymbolType.layers;
- for (_l = 0, _len3 = _ref7.length; _l < _len3; _l++) {
- l = _ref7[_l];
- nid = SymbolGroup._layerid++;
- id = 'sl_' + nid;
- if (l.type === 'svg') {
- layer = me.map.createSVGLayer(id);
- } else if (l.type === 'html') {
- layer = me.map.createHTMLLayer(id);
+
+ /* liniear scale
+ */
+
+
+ LinearScale.prototype.scale = function (x) {
+ var me, vals;
+ me = this;
+ vals = me.values;
+ return (x - vals[0]) / (vals[vals.length - 1] - vals[0]);
+ };
+
+ return LinearScale;
+
+ })(Scale);
+
+ LogScale = (function (_super) {
+
+ __extends(LogScale, _super);
+
+ function LogScale() {
+ this.scale = __bind(this.scale, this);
+ return LogScale.__super__.constructor.apply(this, arguments);
}
- me.layers[l.id] = layer;
- }
- me.symbols = [];
- for (i in me.data) {
- d = me.data[i];
- if (__type(me.filter) === "function") {
- if (me.filter(d, i)) {
- me.add(d, i);
- }
- } else {
- me.add(d, i);
+
+ /* logatithmic scale
+ */
+
+
+ LogScale.prototype.scale = function (x) {
+ var me, vals;
+ me = this;
+ vals = me.values;
+ log = Math.log;
+ return (log(x) - log(vals[0])) / (log(vals[vals.length - 1]) - log(vals[0]));
+ };
+
+ return LogScale;
+
+ })(Scale);
+
+ SqrtScale = (function (_super) {
+
+ __extends(SqrtScale, _super);
+
+ function SqrtScale() {
+ this.scale = __bind(this.scale, this);
+ return SqrtScale.__super__.constructor.apply(this, arguments);
}
- }
- me.layout();
- me.render();
- me.map.addSymbolGroup(me);
- }
- SymbolGroup.prototype.add = function(data, key) {
- /* adds a new symbol to this group
- */
-
- var SymbolType, ll, p, sprops, symbol, _i, _len, _ref6;
- me = this;
- SymbolType = me.type;
- ll = me._evaluate(me.location, data, key);
- if (__type(ll) === 'array') {
- ll = new kartograph.LonLat(ll[0], ll[1]);
- }
- sprops = {
- layers: me.layers,
- location: ll,
- data: data,
- key: key != null ? key : me.symbols.length,
- map: me.map
- };
- _ref6 = SymbolType.props;
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- p = _ref6[_i];
- if (me[p] != null) {
- sprops[p] = me._evaluate(me[p], data, key);
+ /* square root scale
+ */
+
+
+ SqrtScale.prototype.scale = function (x) {
+ var me, vals;
+ me = this;
+ vals = me.values;
+ return Math.sqrt((x - vals[0]) / (vals[vals.length - 1] - vals[0]));
+ };
+
+ return SqrtScale;
+
+ })(Scale);
+
+ QuantileScale = (function (_super) {
+
+ __extends(QuantileScale, _super);
+
+ function QuantileScale() {
+ this.scale = __bind(this.scale, this);
+ return QuantileScale.__super__.constructor.apply(this, arguments);
}
- }
- symbol = new SymbolType(sprops);
- me.symbols.push(symbol);
- return symbol;
+
+ /* quantiles scale
+ */
+
+
+ QuantileScale.prototype.scale = function (x) {
+ var i, k, me, nv, v, vals;
+ me = this;
+ vals = me.values;
+ k = vals.length - 1;
+ for (i in vals) {
+ v = vals[Number(i)];
+ nv = vals[Number(i) + 1];
+ if (x === v) {
+ return i / k;
+ }
+ if (i < k && x > v && x < nv) {
+ return i / k + (x - v) / (nv - v);
+ }
+ }
+ };
+
+ return QuantileScale;
+
+ })(Scale);
+
+ kartograph.scale = {};
+
+ kartograph.scale.identity = function (s) {
+ return new Scale(domain, prop, filter).rangedScale;
};
- SymbolGroup.prototype.layout = function() {
- var layer_id, ll, path, path_id, s, xy, _i, _len, _ref6, _ref7;
- _ref6 = me.symbols;
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- s = _ref6[_i];
- ll = s.location;
- if (__type(ll) === 'string') {
- _ref7 = ll.split('.'), layer_id = _ref7[0], path_id = _ref7[1];
- path = me.map.getLayerPath(layer_id, path_id);
- if (path != null) {
- xy = me.map.viewBC.project(path.path.centroid());
- } else {
- warn('could not find layer path ' + layer_id + '.' + path_id);
- continue;
- }
- } else {
- xy = me.map.lonlat2xy(ll);
- }
- s.x = xy[0];
- s.y = xy[1];
- }
- if (me.clustering === 'k-means') {
- me._kMeans();
- } else if (me.clustering === 'noverlap') {
- me._noverlap();
- }
- return me;
+ kartograph.scale.linear = function (domain, prop, filter) {
+ return new LinearScale(domain, prop, filter).rangedScale;
};
- SymbolGroup.prototype.render = function() {
- var node, s, sortBy, sortDir, _i, _j, _len, _len1, _ref6, _ref7, _ref8;
- me = this;
- if (me.sortBy) {
- sortDir = 'asc';
- if (__type(me.sortBy) === "string") {
- me.sortBy = me.sortBy.split(' ', 2);
- sortBy = me.sortBy[0];
- sortDir = (_ref6 = me.sortBy[1]) != null ? _ref6 : 'asc';
- }
- me.symbols = me.symbols.sort(function(a, b) {
- var m, va, vb;
- if (__type(me.sortBy) === "function") {
- va = me.sortBy(a.data, a);
- vb = me.sortBy(b.data, b);
- } else {
- va = a[sortBy];
- vb = b[sortBy];
- }
- if (va === vb) {
- return 0;
- }
- m = sortDir === 'asc' ? 1 : -1;
- if (va > vb) {
- return 1 * m;
- } else {
- return -1 * m;
- }
- });
- }
- _ref7 = me.symbols;
- for (_i = 0, _len = _ref7.length; _i < _len; _i++) {
- s = _ref7[_i];
- s.render();
- _ref8 = s.nodes();
- for (_j = 0, _len1 = _ref8.length; _j < _len1; _j++) {
- node = _ref8[_j];
- node.symbol = s;
- }
- }
- if (__type(me.tooltip) === "function") {
- me._initTooltips();
- }
- $.each(['click', 'mouseenter', 'mouseleave'], function(i, evt) {
- var _k, _len2, _ref9, _results;
- if (__type(me[evt]) === "function") {
- _ref9 = me.symbols;
- _results = [];
- for (_k = 0, _len2 = _ref9.length; _k < _len2; _k++) {
- s = _ref9[_k];
- _results.push((function() {
- var _l, _len3, _ref10, _results1,
- _this = this;
- _ref10 = s.nodes();
- _results1 = [];
- for (_l = 0, _len3 = _ref10.length; _l < _len3; _l++) {
- node = _ref10[_l];
- _results1.push($(node)[evt](function(e) {
- var tgt;
- tgt = e.target;
- while (!tgt.symbol) {
- tgt = $(tgt).parent().get(0);
- }
- e.stopPropagation();
- return me[evt](tgt.symbol.data, tgt.symbol, e);
- }));
- }
- return _results1;
- }).call(this));
- }
- return _results;
- }
- });
- return me;
+ kartograph.scale.log = function (domain, prop, filter) {
+ return new LogScale(domain, prop, filter).rangedScale;
};
- SymbolGroup.prototype.tooltips = function(cb) {
- me = this;
- me.tooltips = cb;
- me._initTooltips();
- return me;
+ kartograph.scale.sqrt = function (domain, prop, filter) {
+ return new SqrtScale(domain, prop, filter).rangedScale;
};
- SymbolGroup.prototype.remove = function(filter) {
- var id, kept, layer, s, _i, _len, _ref6, _ref7, _results;
- me = this;
- kept = [];
- _ref6 = me.symbols;
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- s = _ref6[_i];
- if ((filter != null) && !filter(s.data)) {
- kept.push(s);
- continue;
- }
- try {
- s.clear();
- } catch (error) {
- warn('error: symbolgroup.remove');
- }
- }
- if (!(filter != null)) {
- _ref7 = me.layers;
- _results = [];
- for (id in _ref7) {
- layer = _ref7[id];
- if (id !== "mapcanvas") {
- _results.push(layer.remove());
- } else {
- _results.push(void 0);
- }
- }
- return _results;
- } else {
- return me.symbols = kept;
- }
+ kartograph.scale.quantile = function (domain, prop, filter) {
+ return new QuantileScale(domain, prop, filter).rangedScale;
};
- SymbolGroup.prototype._evaluate = function(prop, data, key) {
- /* evaluates a property function or returns a static value
- */
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- var val;
- if (__type(prop) === 'function') {
- return val = prop(data, key);
- } else {
- return val = prop;
- }
- };
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- SymbolGroup.prototype._kMeans = function() {
- /*
- layouts symbols in this group, eventually adds new 'grouped' symbols
- map.addSymbols({
- layout: "k-means",
- aggregate: function(data) {
- // compresses a list of data objects into a single one
- // typically you want to calculate the mean position, sum value or something here
- }
- })
- */
-
- var SymbolType, cluster, d, i, mean, means, out, p, s, size, sprops, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref6, _ref7, _ref8, _ref9;
- me = this;
- if ((_ref6 = me.osymbols) == null) {
- me.osymbols = me.symbols;
- }
- SymbolType = me.type;
- if (me.clusteringOpts != null) {
- size = me.clusteringOpts.size;
- }
- if (size == null) {
- size = 64;
- }
- cluster = kmeans().iterations(16).size(size);
- _ref7 = me.osymbols;
- for (_i = 0, _len = _ref7.length; _i < _len; _i++) {
- s = _ref7[_i];
- cluster.add({
- x: s.x,
- y: s.y
- });
- }
- means = cluster.means();
- out = [];
- for (_j = 0, _len1 = means.length; _j < _len1; _j++) {
- mean = means[_j];
- if (mean.size === 0) {
- continue;
- }
- d = [];
- _ref8 = mean.indices;
- for (_k = 0, _len2 = _ref8.length; _k < _len2; _k++) {
- i = _ref8[_k];
- d.push(me.osymbols[i].data);
- }
- d = me.aggregate(d);
- sprops = {
- layers: me.layers,
- location: false,
- data: d,
- map: me.map
- };
- _ref9 = SymbolType.props;
- for (_l = 0, _len3 = _ref9.length; _l < _len3; _l++) {
- p = _ref9[_l];
- if (me[p] != null) {
- sprops[p] = me._evaluate(me[p], d);
- }
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ Symbol = (function () {
+ /* base class for all symbols
+ */
+
+ var me;
+
+ me = null;
+
+ function Symbol(opts) {
+ me = this;
+ me.location = opts.location;
+ me.data = opts.data;
+ me.map = opts.map;
+ me.layers = opts.layers;
+ me.key = opts.key;
+ me.x = opts.x;
+ me.y = opts.y;
}
- s = new SymbolType(sprops);
- s.x = mean.x;
- s.y = mean.y;
- out.push(s);
- }
- return me.symbols = out;
- };
- SymbolGroup.prototype._noverlap = function() {
- var SymbolType, b0, b1, d, dx, dy, i, intersects, iterations, l, l0, l1, maxRatio, out, p, q, r, r0, r1, rad0, rad1, s, s0, s1, sprops, symbols, t0, t1, tolerance, w, x, y, _i, _j, _k, _l, _len, _len1, _len2, _m, _n, _ref10, _ref11, _ref6, _ref7, _ref8, _ref9;
- me = this;
- if ((_ref6 = me.osymbols) == null) {
- me.osymbols = me.symbols;
- }
- iterations = 3;
- SymbolType = me.type;
- if (__indexOf.call(SymbolType.props, 'radius') < 0) {
- warn('noverlap layout only available for symbols with property "radius"');
- return;
- }
- symbols = me.osymbols.slice();
- if (me.clusteringOpts != null) {
- tolerance = me.clusteringOpts.tolerance;
- maxRatio = me.clusteringOpts.maxRatio;
- }
- if (tolerance == null) {
- tolerance = 0.05;
- }
- if (maxRatio == null) {
- maxRatio = 0.8;
- }
- for (i = _i = 0, _ref7 = iterations - 1; 0 <= _ref7 ? _i <= _ref7 : _i >= _ref7; i = 0 <= _ref7 ? ++_i : --_i) {
- symbols.sort(function(a, b) {
- return b.radius - a.radius;
- });
- l = symbols.length;
- out = [];
- for (p = _j = 0, _ref8 = l - 3; 0 <= _ref8 ? _j <= _ref8 : _j >= _ref8; p = 0 <= _ref8 ? ++_j : --_j) {
- s0 = symbols[p];
- if (!s0) {
- continue;
- }
- rad0 = s0.radius * (1 - tolerance);
- l0 = s0.x - rad0;
- r0 = s0.x + rad0;
- t0 = s0.y - rad0;
- b0 = s0.y + rad0;
- intersects = [];
- for (q = _k = _ref9 = p + 1, _ref10 = l - 2; _ref9 <= _ref10 ? _k <= _ref10 : _k >= _ref10; q = _ref9 <= _ref10 ? ++_k : --_k) {
- s1 = symbols[q];
- if (!s1) {
- continue;
- }
- rad1 = s1.radius;
- l1 = s1.x - rad1;
- r1 = s1.x + rad1;
- t1 = s1.y - rad1;
- b1 = s1.y + rad1;
- if (rad1 / s0.radius < maxRatio) {
- if (!(r0 < l1 || r1 < l0) && !(b0 < t1 || b1 < t0)) {
- dx = s1.x - s0.x;
- dy = s1.y - s0.y;
- if (dx * dx + dy * dy < (rad0 + rad1) * (rad0 + rad1)) {
- intersects.push(q);
+ Symbol.prototype.init = function () {
+ return me;
+ };
+
+ Symbol.prototype.overlaps = function (symbol) {
+ return false;
+ };
+
+ Symbol.prototype.update = function (opts) {
+ /* once the data has changed
+ */
+ return me;
+ };
+
+ Symbol.prototype.nodes = function () {
+ return [];
+ };
+
+ Symbol.prototype.clear = function () {
+ return me;
+ };
+
+ return Symbol;
+
+ })();
+
+ kartograph.Symbol = Symbol;
+
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ SymbolGroup = (function () {
+ /* symbol groups
+
+ Usage:
+ new $K.SymbolGroup(options);
+ map.addSymbols(options)
+ */
+
+ var me;
+
+ me = null;
+
+ function SymbolGroup(opts) {
+ this._initTooltips = __bind(this._initTooltips, this);
+
+ this._noverlap = __bind(this._noverlap, this);
+
+ this._kMeans = __bind(this._kMeans, this);
+
+ var SymbolType, d, i, id, l, layer, nid, optional, p, required, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref6, _ref7;
+ me = this;
+ required = ['data', 'location', 'type', 'map'];
+ optional = ['filter', 'tooltip', 'click', 'delay', 'sortBy', 'clustering', 'aggregate', 'clusteringOpts', 'mouseenter', 'mouseleave'];
+ for (_i = 0, _len = required.length; _i < _len; _i++) {
+ p = required[_i];
+ if (opts[p] != null) {
+ me[p] = opts[p];
+ } else {
+ throw "SymbolGroup: missing argument '" + p + "'";
}
- }
- }
- }
- if (intersects.length > 0) {
- d = [s0.data];
- r = s0.radius * s0.radius;
- for (_l = 0, _len = intersects.length; _l < _len; _l++) {
- i = intersects[_l];
- d.push(symbols[i].data);
- r += symbols[i].radius * symbols[i].radius;
- }
- d = me.aggregate(d);
- sprops = {
- layers: me.layers,
- location: false,
- data: d,
- map: me.map
+ }
+ for (_j = 0, _len1 = optional.length; _j < _len1; _j++) {
+ p = optional[_j];
+ if (opts[p] != null) {
+ me[p] = opts[p];
+ }
+ }
+ SymbolType = me.type;
+ if (!(SymbolType != null)) {
+ warn('could not resolve symbol type', me.type);
+ return;
+ }
+ _ref6 = SymbolType.props;
+ for (_k = 0, _len2 = _ref6.length; _k < _len2; _k++) {
+ p = _ref6[_k];
+ if (opts[p] != null) {
+ me[p] = opts[p];
+ }
+ }
+ me.layers = {
+ mapcanvas: me.map.paper
};
- _ref11 = SymbolType.props;
- for (_m = 0, _len1 = _ref11.length; _m < _len1; _m++) {
- p = _ref11[_m];
- if (me[p] != null) {
- sprops[p] = me._evaluate(me[p], d);
- }
- }
- s = new SymbolType(sprops);
- w = s0.radius * s0.radius / r;
- x = s0.x * w;
- y = s0.y * w;
- for (_n = 0, _len2 = intersects.length; _n < _len2; _n++) {
- i = intersects[_n];
- s1 = symbols[i];
- w = s1.radius * s1.radius / r;
- x += s1.x * w;
- y += s1.y * w;
- symbols[i] = void 0;
- }
- s.x = x;
- s.y = y;
- symbols[p] = void 0;
- out.push(s);
- } else {
- out.push(s0);
- }
+ _ref7 = SymbolType.layers;
+ for (_l = 0, _len3 = _ref7.length; _l < _len3; _l++) {
+ l = _ref7[_l];
+ nid = SymbolGroup._layerid++;
+ id = 'sl_' + nid;
+ if (l.type === 'svg') {
+ layer = me.map.createSVGLayer(id);
+ } else if (l.type === 'html') {
+ layer = me.map.createHTMLLayer(id);
+ }
+ me.layers[l.id] = layer;
+ }
+ me.symbols = [];
+ for (i in me.data) {
+ d = me.data[i];
+ if (__type(me.filter) === "function") {
+ if (me.filter(d, i)) {
+ me.add(d, i);
+ }
+ } else {
+ me.add(d, i);
+ }
+ }
+ me.layout();
+ me.render();
+ me.map.addSymbolGroup(me);
}
- symbols = out;
- }
- return me.symbols = symbols;
- };
- SymbolGroup.prototype._initTooltips = function() {
- var cfg, node, s, tooltips, tt, _i, _j, _len, _len1, _ref6, _ref7;
- me = this;
- tooltips = me.tooltip;
- _ref6 = me.symbols;
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- s = _ref6[_i];
- cfg = {
- position: {
- target: 'mouse',
- viewport: $(window),
- adjust: {
- x: 7,
- y: 7
- }
- },
- show: {
- delay: 20
- },
- content: {},
- events: {
- show: function(evt, api) {
- return $('.qtip').filter(function() {
- return this !== api.elements.tooltip.get(0);
- }).hide();
- }
- }
- };
- tt = tooltips(s.data, s.key);
- if (__type(tt) === "string") {
- cfg.content.text = tt;
- } else if (__type(tt) === "array") {
- cfg.content.title = tt[0];
- cfg.content.text = tt[1];
- }
- _ref7 = s.nodes();
- for (_j = 0, _len1 = _ref7.length; _j < _len1; _j++) {
- node = _ref7[_j];
- $(node).qtip(cfg);
- }
- }
- };
+ SymbolGroup.prototype.add = function (data, key) {
+ /* adds a new symbol to this group
+ */
- SymbolGroup.prototype.onResize = function() {
- var s, _i, _len, _ref6;
- me = this;
- me.layout();
- _ref6 = me.symbols;
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- s = _ref6[_i];
- s.update();
- }
- };
+ var SymbolType, ll, p, sprops, symbol, _i, _len, _ref6;
+ me = this;
+ SymbolType = me.type;
+ ll = me._evaluate(me.location, data, key);
+ if (__type(ll) === 'array') {
+ ll = new kartograph.LonLat(ll[0], ll[1]);
+ }
+ sprops = {
+ layers: me.layers,
+ location: ll,
+ data: data,
+ key: key != null ? key : me.symbols.length,
+ map: me.map
+ };
+ _ref6 = SymbolType.props;
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ p = _ref6[_i];
+ if (me[p] != null) {
+ sprops[p] = me._evaluate(me[p], data, key);
+ }
+ }
+ symbol = new SymbolType(sprops);
+ me.symbols.push(symbol);
+ return symbol;
+ };
- SymbolGroup.prototype.update = function(opts, duration, easing) {
- var p, s, _i, _j, _len, _len1, _ref6, _ref7;
- me = this;
- if (!(opts != null)) {
- opts = {};
- }
- _ref6 = me.symbols;
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- s = _ref6[_i];
- _ref7 = me.type.props;
- for (_j = 0, _len1 = _ref7.length; _j < _len1; _j++) {
- p = _ref7[_j];
- if (opts[p] != null) {
- s[p] = me._evaluate(opts[p], s.data);
- } else if (me[p] != null) {
- s[p] = me._evaluate(me[p], s.data);
- }
- }
- s.update(duration, easing);
- }
- return me;
- };
+ SymbolGroup.prototype.layout = function () {
+ var layer_id, ll, path, path_id, s, xy, _i, _len, _ref6, _ref7;
+ _ref6 = me.symbols;
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ s = _ref6[_i];
+ ll = s.location;
+ if (__type(ll) === 'string') {
+ _ref7 = ll.split('.'), layer_id = _ref7[0], path_id = _ref7[1];
+ path = me.map.getLayerPath(layer_id, path_id);
+ if (path != null) {
+ xy = me.map.viewBC.project(path.path.centroid());
+ } else {
+ warn('could not find layer path ' + layer_id + '.' + path_id);
+ continue;
+ }
+ } else {
+ xy = me.map.lonlat2xy(ll);
+ }
+ s.x = xy[0];
+ s.y = xy[1];
+ }
+ if (me.clustering === 'k-means') {
+ me._kMeans();
+ } else if (me.clustering === 'noverlap') {
+ me._noverlap();
+ }
+ return me;
+ };
- return SymbolGroup;
+ SymbolGroup.prototype.render = function () {
+ var node, s, sortBy, sortDir, _i, _j, _len, _len1, _ref6, _ref7, _ref8;
+ me = this;
+ if (me.sortBy) {
+ sortDir = 'asc';
+ if (__type(me.sortBy) === "string") {
+ me.sortBy = me.sortBy.split(' ', 2);
+ sortBy = me.sortBy[0];
+ sortDir = (_ref6 = me.sortBy[1]) != null ? _ref6 : 'asc';
+ }
+ me.symbols = me.symbols.sort(function (a, b) {
+ var m, va, vb;
+ if (__type(me.sortBy) === "function") {
+ va = me.sortBy(a.data, a);
+ vb = me.sortBy(b.data, b);
+ } else {
+ va = a[sortBy];
+ vb = b[sortBy];
+ }
+ if (va === vb) {
+ return 0;
+ }
+ m = sortDir === 'asc' ? 1 : -1;
+ if (va > vb) {
+ return 1 * m;
+ } else {
+ return -1 * m;
+ }
+ });
+ }
+ _ref7 = me.symbols;
+ for (_i = 0, _len = _ref7.length; _i < _len; _i++) {
+ s = _ref7[_i];
+ s.render();
+ _ref8 = s.nodes();
+ for (_j = 0, _len1 = _ref8.length; _j < _len1; _j++) {
+ node = _ref8[_j];
+ node.symbol = s;
+ }
+ }
+ if (__type(me.tooltip) === "function") {
+ me._initTooltips();
+ }
+ $.each(['click', 'mouseenter', 'mouseleave'], function (i, evt) {
+ var _k, _len2, _ref9, _results;
+ if (__type(me[evt]) === "function") {
+ _ref9 = me.symbols;
+ _results = [];
+ for (_k = 0, _len2 = _ref9.length; _k < _len2; _k++) {
+ s = _ref9[_k];
+ _results.push((function () {
+ var _l, _len3, _ref10, _results1,
+ _this = this;
+ _ref10 = s.nodes();
+ _results1 = [];
+ for (_l = 0, _len3 = _ref10.length; _l < _len3; _l++) {
+ node = _ref10[_l];
+ _results1.push($(node)[evt](function (e) {
+ var tgt;
+ tgt = e.target;
+ while (!tgt.symbol) {
+ tgt = $(tgt).parent().get(0);
+ }
+ e.stopPropagation();
+ return me[evt](tgt.symbol.data, tgt.symbol, e);
+ }));
+ }
+ return _results1;
+ }).call(this));
+ }
+ return _results;
+ }
+ });
+ return me;
+ };
- })();
+ SymbolGroup.prototype.tooltips = function (cb) {
+ me = this;
+ me.tooltips = cb;
+ me._initTooltips();
+ return me;
+ };
- SymbolGroup._layerid = 0;
+ SymbolGroup.prototype.remove = function (filter) {
+ var id, kept, layer, s, _i, _len, _ref6, _ref7, _results;
+ me = this;
+ kept = [];
+ _ref6 = me.symbols;
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ s = _ref6[_i];
+ if ((filter != null) && !filter(s.data)) {
+ kept.push(s);
+ continue;
+ }
+ try {
+ s.clear();
+ } catch (error) {
+ warn('error: symbolgroup.remove');
+ }
+ }
+ if (!(filter != null)) {
+ _ref7 = me.layers;
+ _results = [];
+ for (id in _ref7) {
+ layer = _ref7[id];
+ if (id !== "mapcanvas") {
+ _results.push(layer.remove());
+ } else {
+ _results.push(void 0);
+ }
+ }
+ return _results;
+ } else {
+ return me.symbols = kept;
+ }
+ };
- kartograph.SymbolGroup = SymbolGroup;
+ SymbolGroup.prototype._evaluate = function (prop, data, key) {
+ /* evaluates a property function or returns a static value
+ */
- kartograph.Kartograph.prototype.addSymbols = function(opts) {
- opts.map = this;
- return new SymbolGroup(opts);
- };
+ var val;
+ if (__type(prop) === 'function') {
+ return val = prop(data, key);
+ } else {
+ return val = prop;
+ }
+ };
-
-/*
- Copyright (c) 2010, SimpleGeo and Stamen Design
- 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.
- * Neither the name of SimpleGeo nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- 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 SIMPLEGEO 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.
-*/
+ SymbolGroup.prototype._kMeans = function () {
+ /*
+ layouts symbols in this group, eventually adds new 'grouped' symbols
+ map.addSymbols({
+ layout: "k-means",
+ aggregate: function(data) {
+ // compresses a list of data objects into a single one
+ // typically you want to calculate the mean position, sum value or something here
+ }
+ })
+ */
+
+ var SymbolType, cluster, d, i, mean, means, out, p, s, size, sprops, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref6, _ref7, _ref8, _ref9;
+ me = this;
+ if ((_ref6 = me.osymbols) == null) {
+ me.osymbols = me.symbols;
+ }
+ SymbolType = me.type;
+ if (me.clusteringOpts != null) {
+ size = me.clusteringOpts.size;
+ }
+ if (size == null) {
+ size = 64;
+ }
+ cluster = kmeans().iterations(16).size(size);
+ _ref7 = me.osymbols;
+ for (_i = 0, _len = _ref7.length; _i < _len; _i++) {
+ s = _ref7[_i];
+ cluster.add({
+ x: s.x,
+ y: s.y
+ });
+ }
+ means = cluster.means();
+ out = [];
+ for (_j = 0, _len1 = means.length; _j < _len1; _j++) {
+ mean = means[_j];
+ if (mean.size === 0) {
+ continue;
+ }
+ d = [];
+ _ref8 = mean.indices;
+ for (_k = 0, _len2 = _ref8.length; _k < _len2; _k++) {
+ i = _ref8[_k];
+ d.push(me.osymbols[i].data);
+ }
+ d = me.aggregate(d);
+ sprops = {
+ layers: me.layers,
+ location: false,
+ data: d,
+ map: me.map
+ };
+ _ref9 = SymbolType.props;
+ for (_l = 0, _len3 = _ref9.length; _l < _len3; _l++) {
+ p = _ref9[_l];
+ if (me[p] != null) {
+ sprops[p] = me._evaluate(me[p], d);
+ }
+ }
+ s = new SymbolType(sprops);
+ s.x = mean.x;
+ s.y = mean.y;
+ out.push(s);
+ }
+ return me.symbols = out;
+ };
-// k-means clustering
-function kmeans() {
- var kmeans = {},
- points = [],
- iterations = 1,
- size = 1;
-
- kmeans.size = function(x) {
- if (!arguments.length) return size;
- size = x;
- return kmeans;
- };
-
- kmeans.iterations = function(x) {
- if (!arguments.length) return iterations;
- iterations = x;
- return kmeans;
- };
-
- kmeans.add = function(x) {
- points.push(x);
- return kmeans;
- };
-
- kmeans.means = function() {
- var means = [],
- seen = {},
- n = Math.min(size, points.length);
-
- // Initialize k random (unique!) means.
- for (var i = 0, m = 2 * n; i < m; i++) {
- var p = points[~~(Math.random() * points.length)], id = p.x + "/" + p.y;
- if (!(id in seen)) {
- seen[id] = 1;
- if (means.push({x: p.x, y: p.y}) >= n) break;
- }
- }
- n = means.length;
-
- // For each iteration, create a kd-tree of the current means.
- for (var j = 0; j < iterations; j++) {
- var kd = kdtree().points(means);
-
- // Clear the state.
- for (var i = 0; i < n; i++) {
- var mean = means[i];
- mean.sumX = 0;
- mean.sumY = 0;
- mean.size = 0;
- mean.points = [];
- mean.indices = [];
- }
-
- // Find the mean closest to each point.
- for (var i = 0; i < points.length; i++) {
- var point = points[i], mean = kd.find(point);
- mean.sumX += point.x;
- mean.sumY += point.y;
- mean.size++;
- mean.points.push(point);
- mean.indices.push(i);
- }
-
- // Compute the new means.
- for (var i = 0; i < n; i++) {
- var mean = means[i];
- if (!mean.size) continue; // overlapping mean
- mean.x = mean.sumX / mean.size;
- mean.y = mean.sumY / mean.size;
- }
- }
+ SymbolGroup.prototype._noverlap = function () {
+ var SymbolType, b0, b1, d, dx, dy, i, intersects, iterations, l, l0, l1, maxRatio, out, p, q, r, r0, r1, rad0, rad1, s, s0, s1, sprops, symbols, t0, t1, tolerance, w, x, y, _i, _j, _k, _l, _len, _len1, _len2, _m, _n, _ref10, _ref11, _ref6, _ref7, _ref8, _ref9;
+ me = this;
+ if ((_ref6 = me.osymbols) == null) {
+ me.osymbols = me.symbols;
+ }
+ iterations = 3;
+ SymbolType = me.type;
+ if (__indexOf.call(SymbolType.props, 'radius') < 0) {
+ warn('noverlap layout only available for symbols with property "radius"');
+ return;
+ }
+ symbols = me.osymbols.slice();
+ if (me.clusteringOpts != null) {
+ tolerance = me.clusteringOpts.tolerance;
+ maxRatio = me.clusteringOpts.maxRatio;
+ }
+ if (tolerance == null) {
+ tolerance = 0.05;
+ }
+ if (maxRatio == null) {
+ maxRatio = 0.8;
+ }
+ for (i = _i = 0, _ref7 = iterations - 1; 0 <= _ref7 ? _i <= _ref7 : _i >= _ref7; i = 0 <= _ref7 ? ++_i : --_i) {
+ symbols.sort(function (a, b) {
+ return b.radius - a.radius;
+ });
+ l = symbols.length;
+ out = [];
+ for (p = _j = 0, _ref8 = l - 3; 0 <= _ref8 ? _j <= _ref8 : _j >= _ref8; p = 0 <= _ref8 ? ++_j : --_j) {
+ s0 = symbols[p];
+ if (!s0) {
+ continue;
+ }
+ rad0 = s0.radius * (1 - tolerance);
+ l0 = s0.x - rad0;
+ r0 = s0.x + rad0;
+ t0 = s0.y - rad0;
+ b0 = s0.y + rad0;
+ intersects = [];
+ for (q = _k = _ref9 = p + 1, _ref10 = l - 2; _ref9 <= _ref10 ? _k <= _ref10 : _k >= _ref10; q = _ref9 <= _ref10 ? ++_k : --_k) {
+ s1 = symbols[q];
+ if (!s1) {
+ continue;
+ }
+ rad1 = s1.radius;
+ l1 = s1.x - rad1;
+ r1 = s1.x + rad1;
+ t1 = s1.y - rad1;
+ b1 = s1.y + rad1;
+ if (rad1 / s0.radius < maxRatio) {
+ if (!(r0 < l1 || r1 < l0) && !(b0 < t1 || b1 < t0)) {
+ dx = s1.x - s0.x;
+ dy = s1.y - s0.y;
+ if (dx * dx + dy * dy < (rad0 + rad1) * (rad0 + rad1)) {
+ intersects.push(q);
+ }
+ }
+ }
+ }
+ if (intersects.length > 0) {
+ d = [s0.data];
+ r = s0.radius * s0.radius;
+ for (_l = 0, _len = intersects.length; _l < _len; _l++) {
+ i = intersects[_l];
+ d.push(symbols[i].data);
+ r += symbols[i].radius * symbols[i].radius;
+ }
+ d = me.aggregate(d);
+ sprops = {
+ layers: me.layers,
+ location: false,
+ data: d,
+ map: me.map
+ };
+ _ref11 = SymbolType.props;
+ for (_m = 0, _len1 = _ref11.length; _m < _len1; _m++) {
+ p = _ref11[_m];
+ if (me[p] != null) {
+ sprops[p] = me._evaluate(me[p], d);
+ }
+ }
+ s = new SymbolType(sprops);
+ w = s0.radius * s0.radius / r;
+ x = s0.x * w;
+ y = s0.y * w;
+ for (_n = 0, _len2 = intersects.length; _n < _len2; _n++) {
+ i = intersects[_n];
+ s1 = symbols[i];
+ w = s1.radius * s1.radius / r;
+ x += s1.x * w;
+ y += s1.y * w;
+ symbols[i] = void 0;
+ }
+ s.x = x;
+ s.y = y;
+ symbols[p] = void 0;
+ out.push(s);
+ } else {
+ out.push(s0);
+ }
+ }
+ symbols = out;
+ }
+ return me.symbols = symbols;
+ };
+
+ SymbolGroup.prototype._initTooltips = function () {
+ var cfg, node, s, tooltips, tt, _i, _j, _len, _len1, _ref6, _ref7;
+ me = this;
+ tooltips = me.tooltip;
+ _ref6 = me.symbols;
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ s = _ref6[_i];
+ cfg = {
+ position: {
+ target: 'mouse',
+ viewport: $(window),
+ adjust: {
+ x: 7,
+ y: 7
+ }
+ },
+ show: {
+ delay: 20
+ },
+ content: {},
+ events: {
+ show: function (evt, api) {
+ return $('.qtip').filter(function () {
+ return this !== api.elements.tooltip.get(0);
+ }).hide();
+ }
+ }
+ };
+ tt = tooltips(s.data, s.key);
+ if (__type(tt) === "string") {
+ cfg.content.text = tt;
+ } else if (__type(tt) === "array") {
+ cfg.content.title = tt[0];
+ cfg.content.text = tt[1];
+ }
+ _ref7 = s.nodes();
+ for (_j = 0, _len1 = _ref7.length; _j < _len1; _j++) {
+ node = _ref7[_j];
+ $(node).qtip(cfg);
+ }
+ }
+ };
- return means;
- };
+ SymbolGroup.prototype.onResize = function () {
+ var s, _i, _len, _ref6;
+ me = this;
+ me.layout();
+ _ref6 = me.symbols;
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ s = _ref6[_i];
+ s.update();
+ }
+ };
- return kmeans;
-}
+ SymbolGroup.prototype.update = function (opts, duration, easing) {
+ var p, s, _i, _j, _len, _len1, _ref6, _ref7;
+ me = this;
+ if (!(opts != null)) {
+ opts = {};
+ }
+ _ref6 = me.symbols;
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ s = _ref6[_i];
+ _ref7 = me.type.props;
+ for (_j = 0, _len1 = _ref7.length; _j < _len1; _j++) {
+ p = _ref7[_j];
+ if (opts[p] != null) {
+ s[p] = me._evaluate(opts[p], s.data);
+ } else if (me[p] != null) {
+ s[p] = me._evaluate(me[p], s.data);
+ }
+ }
+ s.update(duration, easing);
+ }
+ return me;
+ };
-// kd-tree
-function kdtree() {
- var kdtree = {},
- axes = ["x", "y"],
- root,
- points = [];
-
- kdtree.axes = function(x) {
- if (!arguments.length) return axes;
- axes = x;
- return kdtree;
- };
-
- kdtree.points = function(x) {
- if (!arguments.length) return points;
- points = x;
- root = null;
- return kdtree;
- };
-
- kdtree.find = function(x) {
- return find(kdtree.root(), x, root).point;
- };
-
- kdtree.root = function(x) {
- return root || (root = node(points, 0));
- };
-
- function node(points, depth) {
- if (!points.length) return;
- var axis = axes[depth % axes.length], median = points.length >> 1;
- points.sort(order(axis)); // could use random sample to speed up here
- return {
- axis: axis,
- point: points[median],
- left: node(points.slice(0, median), depth + 1),
- right: node(points.slice(median + 1), depth + 1)
- };
- }
+ return SymbolGroup;
- function distance(a, b) {
- var sum = 0;
- for (var i = 0; i < axes.length; i++) {
- var axis = axes[i], d = a[axis] - b[axis];
- sum += d * d;
- }
- return sum;
- }
-
- function order(axis) {
- return function(a, b) {
- a = a[axis];
- b = b[axis];
- return a < b ? -1 : a > b ? 1 : 0;
+ })();
+
+ SymbolGroup._layerid = 0;
+
+ kartograph.SymbolGroup = SymbolGroup;
+
+ kartograph.Kartograph.prototype.addSymbols = function (opts) {
+ opts.map = this;
+ return new SymbolGroup(opts);
};
- }
-
- function find(node, point, best) {
- if (distance(node.point, point) < distance(best.point, point)) best = node;
- if (node.left) best = find(node.left, point, best);
- if (node.right) {
- var d = node.point[node.axis] - point[node.axis];
- if (d * d < distance(best.point, point)) best = find(node.right, point, best);
- }
- return best;
- }
- return kdtree;
-}
-;
+ /*
+ Copyright (c) 2010, SimpleGeo and Stamen Design
+ 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.
+ * Neither the name of SimpleGeo nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ 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 SIMPLEGEO 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.
+ */
+
+// k-means clustering
+ function kmeans() {
+ var kmeans = {},
+ points = [],
+ iterations = 1,
+ size = 1;
+
+ kmeans.size = function (x) {
+ if (!arguments.length) return size;
+ size = x;
+ return kmeans;
+ };
+
+ kmeans.iterations = function (x) {
+ if (!arguments.length) return iterations;
+ iterations = x;
+ return kmeans;
+ };
- kartograph.dorlingLayout = function(symbolgroup, iterations) {
- var A, B, apply, d, ds, dx, dy, f, i, j, nodes, r, rd, rs, _i;
- if (iterations == null) {
- iterations = 40;
+ kmeans.add = function (x) {
+ points.push(x);
+ return kmeans;
+ };
+
+ kmeans.means = function () {
+ var means = [],
+ seen = {},
+ n = Math.min(size, points.length);
+
+ // Initialize k random (unique!) means.
+ for (var i = 0, m = 2 * n; i < m; i++) {
+ var p = points[~~(Math.random() * points.length)], id = p.x + "/" + p.y;
+ if (!(id in seen)) {
+ seen[id] = 1;
+ if (means.push({x: p.x, y: p.y}) >= n) break;
+ }
+ }
+ n = means.length;
+
+ // For each iteration, create a kd-tree of the current means.
+ for (var j = 0; j < iterations; j++) {
+ var kd = kdtree().points(means);
+
+ // Clear the state.
+ for (var i = 0; i < n; i++) {
+ var mean = means[i];
+ mean.sumX = 0;
+ mean.sumY = 0;
+ mean.size = 0;
+ mean.points = [];
+ mean.indices = [];
+ }
+
+ // Find the mean closest to each point.
+ for (var i = 0; i < points.length; i++) {
+ var point = points[i], mean = kd.find(point);
+ mean.sumX += point.x;
+ mean.sumY += point.y;
+ mean.size++;
+ mean.points.push(point);
+ mean.indices.push(i);
+ }
+
+ // Compute the new means.
+ for (var i = 0; i < n; i++) {
+ var mean = means[i];
+ if (!mean.size) continue; // overlapping mean
+ mean.x = mean.sumX / mean.size;
+ mean.y = mean.sumY / mean.size;
+ }
+ }
+
+ return means;
+ };
+
+ return kmeans;
}
- nodes = [];
- $.each(symbolgroup.symbols, function(i, s) {
- return nodes.push({
- i: i,
- x: s.path.attrs.cx,
- y: s.path.attrs.cy,
- r: s.path.attrs.r
- });
- });
- nodes.sort(function(a, b) {
- return b.r - a.r;
- });
- apply = function() {
- var n, _i, _len;
- for (_i = 0, _len = nodes.length; _i < _len; _i++) {
- n = nodes[_i];
- symbolgroup.symbols[n.i].path.attr({
- cx: n.x,
- cy: n.y
- });
- }
- };
- for (r = _i = 1; 1 <= iterations ? _i <= iterations : _i >= iterations; r = 1 <= iterations ? ++_i : --_i) {
- for (i in nodes) {
- for (j in nodes) {
- if (j > i) {
- A = nodes[i];
- B = nodes[j];
- if (A.x + A.r < B.x - B.r || A.x - A.r > B.x + B.r) {
- continue;
- }
- if (A.y + A.r < B.y - B.r || A.y - A.r > B.y + B.r) {
- continue;
- }
- dx = A.x - B.x;
- dy = A.y - B.y;
- ds = dx * dx + dy * dy;
- rd = A.r + B.r;
- rs = rd * rd;
- if (ds < rs) {
- d = Math.sqrt(ds);
- f = 10 / d;
- A.x += dx * f * (1 - (A.r / rd));
- A.y += dy * f * (1 - (A.r / rd));
- B.x -= dx * f * (1 - (B.r / rd));
- B.y -= dy * f * (1 - (B.r / rd));
- }
- }
+
+// kd-tree
+ function kdtree() {
+ var kdtree = {},
+ axes = ["x", "y"],
+ root,
+ points = [];
+
+ kdtree.axes = function (x) {
+ if (!arguments.length) return axes;
+ axes = x;
+ return kdtree;
+ };
+
+ kdtree.points = function (x) {
+ if (!arguments.length) return points;
+ points = x;
+ root = null;
+ return kdtree;
+ };
+
+ kdtree.find = function (x) {
+ return find(kdtree.root(), x, root).point;
+ };
+
+ kdtree.root = function (x) {
+ return root || (root = node(points, 0));
+ };
+
+ function node(points, depth) {
+ if (!points.length) return;
+ var axis = axes[depth % axes.length], median = points.length >> 1;
+ points.sort(order(axis)); // could use random sample to speed up here
+ return {
+ axis: axis,
+ point: points[median],
+ left: node(points.slice(0, median), depth + 1),
+ right: node(points.slice(median + 1), depth + 1)
+ };
}
- }
- }
- return apply();
- };
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
- Bubble = (function(_super) {
-
- __extends(Bubble, _super);
-
- function Bubble(opts) {
- this.nodes = __bind(this.nodes, this);
-
- this.clear = __bind(this.clear, this);
-
- this.update = __bind(this.update, this);
-
- this.render = __bind(this.render, this);
-
- this.overlaps = __bind(this.overlaps, this);
-
- var me, _ref6, _ref7;
- me = this;
- Bubble.__super__.constructor.call(this, opts);
- me.radius = (_ref6 = opts.radius) != null ? _ref6 : 4;
- me.style = opts.style;
- me.attrs = opts.attrs;
- me.title = opts.title;
- me["class"] = (_ref7 = opts["class"]) != null ? _ref7 : 'bubble';
- }
- Bubble.prototype.overlaps = function(bubble) {
- var dx, dy, me, r1, r2, x1, x2, y1, y2, _ref6, _ref7;
- me = this;
- _ref6 = [me.x, me.y, me.radius], x1 = _ref6[0], y1 = _ref6[1], r1 = _ref6[2];
- _ref7 = [bubble.x, bubble.y, bubble.radius], x2 = _ref7[0], y2 = _ref7[1], r2 = _ref7[2];
- if (x1 - r1 > x2 + r2 || x1 + r1 < x2 - r2 || y1 - r1 > y2 + r2 || y1 + r1 < y2 - r2) {
- return false;
- }
- dx = x1 - x2;
- dy = y1 - y2;
- if (dx * dx + dy * dy > (r1 + r2) * (r1 + r2)) {
- return false;
- }
- return true;
- };
+ function distance(a, b) {
+ var sum = 0;
+ for (var i = 0; i < axes.length; i++) {
+ var axis = axes[i], d = a[axis] - b[axis];
+ sum += d * d;
+ }
+ return sum;
+ }
- Bubble.prototype.render = function(layers) {
- var me;
- me = this;
- if (!(me.path != null)) {
- me.path = me.layers.mapcanvas.circle(me.x, me.y, me.radius);
- }
- me.update();
- me.map.applyCSS(me.path);
- return me;
- };
+ function order(axis) {
+ return function (a, b) {
+ a = a[axis];
+ b = b[axis];
+ return a < b ? -1 : a > b ? 1 : 0;
+ };
+ }
+
+ function find(node, point, best) {
+ if (distance(node.point, point) < distance(best.point, point)) best = node;
+ if (node.left) best = find(node.left, point, best);
+ if (node.right) {
+ var d = node.point[node.axis] - point[node.axis];
+ if (d * d < distance(best.point, point)) best = find(node.right, point, best);
+ }
+ return best;
+ }
+
+ return kdtree;
+ }
+ ;
- Bubble.prototype.update = function(duration, easing) {
- var attrs, me, path;
- if (duration == null) {
- duration = false;
- }
- if (easing == null) {
- easing = 'expo-out';
- }
- me = this;
- path = me.path;
- attrs = {
- cx: me.x,
- cy: me.y,
- r: me.radius
- };
- if (me.attrs != null) {
- attrs = $.extend(attrs, me.attrs);
- }
- if (!duration) {
- path.attr(attrs);
- } else {
- path.animate(attrs, duration, easing);
- }
- if (path.node != null) {
- if (me.style != null) {
- path.node.setAttribute('style', me.style);
+
+ kartograph.dorlingLayout = function (symbolgroup, iterations) {
+ var A, B, apply, d, ds, dx, dy, f, i, j, nodes, r, rd, rs, _i;
+ if (iterations == null) {
+ iterations = 40;
}
- if (me["class"] != null) {
- path.node.setAttribute('class', me["class"]);
+ nodes = [];
+ $.each(symbolgroup.symbols, function (i, s) {
+ return nodes.push({
+ i: i,
+ x: s.path.attrs.cx,
+ y: s.path.attrs.cy,
+ r: s.path.attrs.r
+ });
+ });
+ nodes.sort(function (a, b) {
+ return b.r - a.r;
+ });
+ apply = function () {
+ var n, _i, _len;
+ for (_i = 0, _len = nodes.length; _i < _len; _i++) {
+ n = nodes[_i];
+ symbolgroup.symbols[n.i].path.attr({
+ cx: n.x,
+ cy: n.y
+ });
+ }
+ };
+ for (r = _i = 1; 1 <= iterations ? _i <= iterations : _i >= iterations; r = 1 <= iterations ? ++_i : --_i) {
+ for (i in nodes) {
+ for (j in nodes) {
+ if (j > i) {
+ A = nodes[i];
+ B = nodes[j];
+ if (A.x + A.r < B.x - B.r || A.x - A.r > B.x + B.r) {
+ continue;
+ }
+ if (A.y + A.r < B.y - B.r || A.y - A.r > B.y + B.r) {
+ continue;
+ }
+ dx = A.x - B.x;
+ dy = A.y - B.y;
+ ds = dx * dx + dy * dy;
+ rd = A.r + B.r;
+ rs = rd * rd;
+ if (ds < rs) {
+ d = Math.sqrt(ds);
+ f = 10 / d;
+ A.x += dx * f * (1 - (A.r / rd));
+ A.y += dy * f * (1 - (A.r / rd));
+ B.x -= dx * f * (1 - (B.r / rd));
+ B.y -= dy * f * (1 - (B.r / rd));
+ }
+ }
+ }
+ }
}
- }
- if (me.title != null) {
- path.attr('title', me.title);
- }
- return me;
+ return apply();
};
- Bubble.prototype.clear = function() {
- var me;
- me = this;
- me.path.remove();
- return me;
- };
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
- Bubble.prototype.nodes = function() {
- var me;
- me = this;
- return [me.path.node];
- };
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- return Bubble;
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- })(Symbol);
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- Bubble.props = ['radius', 'style', 'class', 'title', 'attrs'];
- Bubble.layers = [];
+ Bubble = (function (_super) {
- kartograph.Bubble = Bubble;
+ __extends(Bubble, _super);
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
+ function Bubble(opts) {
+ this.nodes = __bind(this.nodes, this);
+ this.clear = __bind(this.clear, this);
- Icon = (function(_super) {
+ this.update = __bind(this.update, this);
- __extends(Icon, _super);
+ this.render = __bind(this.render, this);
- function Icon(opts) {
- var me, _ref10, _ref6, _ref7, _ref8, _ref9;
- me = this;
- Icon.__super__.constructor.call(this, opts);
- me.icon = (_ref6 = opts.icon) != null ? _ref6 : '';
- me.offset = (_ref7 = opts.offset) != null ? _ref7 : [0, 0];
- me.iconsize = (_ref8 = opts.iconsize) != null ? _ref8 : [10, 10];
- me["class"] = (_ref9 = opts["class"]) != null ? _ref9 : '';
- me.title = (_ref10 = opts.title) != null ? _ref10 : '';
- }
+ this.overlaps = __bind(this.overlaps, this);
- Icon.prototype.render = function(layers) {
- var cont, me;
- me = this;
- cont = me.map.container;
- me.img = $('<img />');
- me.img.attr({
- src: me.icon,
- title: me.title,
- alt: me.title,
- width: me.iconsize[0],
- height: me.iconsize[1]
- });
- me.img.addClass(me["class"]);
- me.img.css({
- position: 'absolute',
- 'z-index': 1000,
- cursor: 'pointer'
- });
- me.img[0].symbol = me;
- cont.append(me.img);
- return me.update();
- };
+ var me, _ref6, _ref7;
+ me = this;
+ Bubble.__super__.constructor.call(this, opts);
+ me.radius = (_ref6 = opts.radius) != null ? _ref6 : 4;
+ me.style = opts.style;
+ me.attrs = opts.attrs;
+ me.title = opts.title;
+ me["class"] = (_ref7 = opts["class"]) != null ? _ref7 : 'bubble';
+ }
- Icon.prototype.update = function() {
- var me;
- me = this;
- return me.img.css({
- left: (me.x + me.offset[0]) + 'px',
- top: (me.y + me.offset[1]) + 'px'
- });
- };
+ Bubble.prototype.overlaps = function (bubble) {
+ var dx, dy, me, r1, r2, x1, x2, y1, y2, _ref6, _ref7;
+ me = this;
+ _ref6 = [me.x, me.y, me.radius], x1 = _ref6[0], y1 = _ref6[1], r1 = _ref6[2];
+ _ref7 = [bubble.x, bubble.y, bubble.radius], x2 = _ref7[0], y2 = _ref7[1], r2 = _ref7[2];
+ if (x1 - r1 > x2 + r2 || x1 + r1 < x2 - r2 || y1 - r1 > y2 + r2 || y1 + r1 < y2 - r2) {
+ return false;
+ }
+ dx = x1 - x2;
+ dy = y1 - y2;
+ if (dx * dx + dy * dy > (r1 + r2) * (r1 + r2)) {
+ return false;
+ }
+ return true;
+ };
- Icon.prototype.clear = function() {
- var me;
- me = this;
- me.img.remove();
- return me;
- };
+ Bubble.prototype.render = function (layers) {
+ var me;
+ me = this;
+ if (!(me.path != null)) {
+ me.path = me.layers.mapcanvas.circle(me.x, me.y, me.radius);
+ }
+ me.update();
+ me.map.applyCSS(me.path);
+ return me;
+ };
- Icon.prototype.nodes = function() {
- var me;
- me = this;
- return [me.img];
- };
+ Bubble.prototype.update = function (duration, easing) {
+ var attrs, me, path;
+ if (duration == null) {
+ duration = false;
+ }
+ if (easing == null) {
+ easing = 'expo-out';
+ }
+ me = this;
+ path = me.path;
+ attrs = {
+ cx: me.x,
+ cy: me.y,
+ r: me.radius
+ };
+ if (me.attrs != null) {
+ attrs = $.extend(attrs, me.attrs);
+ }
+ if (!duration) {
+ path.attr(attrs);
+ } else {
+ path.animate(attrs, duration, easing);
+ }
+ if (path.node != null) {
+ if (me.style != null) {
+ path.node.setAttribute('style', me.style);
+ }
+ if (me["class"] != null) {
+ path.node.setAttribute('class', me["class"]);
+ }
+ }
+ if (me.title != null) {
+ path.attr('title', me.title);
+ }
+ return me;
+ };
- return Icon;
+ Bubble.prototype.clear = function () {
+ var me;
+ me = this;
+ me.path.remove();
+ return me;
+ };
- })(kartograph.Symbol);
+ Bubble.prototype.nodes = function () {
+ var me;
+ me = this;
+ return [me.path.node];
+ };
- Icon.props = ['icon', 'offset', 'class', 'title', 'iconsize'];
+ return Bubble;
- Icon.layers = [];
+ })(Symbol);
- kartograph.Icon = Icon;
+ Bubble.props = ['radius', 'style', 'class', 'title', 'attrs'];
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
+ Bubble.layers = [];
+ kartograph.Bubble = Bubble;
- SvgLabel = (function(_super) {
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
- __extends(SvgLabel, _super);
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- function SvgLabel(opts) {
- var me, _ref6, _ref7, _ref8, _ref9;
- me = this;
- SvgLabel.__super__.constructor.call(this, opts);
- me.text = (_ref6 = opts.text) != null ? _ref6 : '';
- me.style = (_ref7 = opts.style) != null ? _ref7 : '';
- me["class"] = (_ref8 = opts["class"]) != null ? _ref8 : '';
- me.offset = (_ref9 = opts.offset) != null ? _ref9 : [0, 0];
- }
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- SvgLabel.prototype.render = function(layers) {
- var lbl, me;
- me = this;
- me.lbl = lbl = me.layers.mapcanvas.text(me.x, me.y, me.text);
- me.update();
- return me;
- };
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- SvgLabel.prototype.update = function() {
- var me;
- me = this;
- me.lbl.attr({
- x: me.x + me.offset[0],
- y: me.y + me.offset[1]
- });
- me.lbl.node.setAttribute('style', me.style);
- return me.lbl.node.setAttribute('class', me["class"]);
- };
- SvgLabel.prototype.clear = function() {
- var me;
- me = this;
- me.lbl.remove();
- return me;
- };
+ Icon = (function (_super) {
- SvgLabel.prototype.nodes = function() {
- var me;
- me = this;
- return [me.lbl.node];
- };
+ __extends(Icon, _super);
- return SvgLabel;
+ function Icon(opts) {
+ var me, _ref10, _ref6, _ref7, _ref8, _ref9;
+ me = this;
+ Icon.__super__.constructor.call(this, opts);
+ me.icon = (_ref6 = opts.icon) != null ? _ref6 : '';
+ me.offset = (_ref7 = opts.offset) != null ? _ref7 : [0, 0];
+ me.iconsize = (_ref8 = opts.iconsize) != null ? _ref8 : [10, 10];
+ me["class"] = (_ref9 = opts["class"]) != null ? _ref9 : '';
+ me.title = (_ref10 = opts.title) != null ? _ref10 : '';
+ }
- })(kartograph.Symbol);
+ Icon.prototype.render = function (layers) {
+ var cont, me;
+ me = this;
+ cont = me.map.container;
+ me.img = $('<img />');
+ me.img.attr({
+ src: me.icon,
+ title: me.title,
+ alt: me.title,
+ width: me.iconsize[0],
+ height: me.iconsize[1]
+ });
+ me.img.addClass(me["class"]);
+ me.img.css({
+ position: 'absolute',
+ 'z-index': 1000,
+ cursor: 'pointer'
+ });
+ me.img[0].symbol = me;
+ cont.append(me.img);
+ return me.update();
+ };
- SvgLabel.props = ['text', 'style', 'class', 'offset'];
+ Icon.prototype.update = function () {
+ var me;
+ me = this;
+ return me.img.css({
+ left: (me.x + me.offset[0]) + 'px',
+ top: (me.y + me.offset[1]) + 'px'
+ });
+ };
- SvgLabel.layers = [];
+ Icon.prototype.clear = function () {
+ var me;
+ me = this;
+ me.img.remove();
+ return me;
+ };
- kartograph.Label = SvgLabel;
+ Icon.prototype.nodes = function () {
+ var me;
+ me = this;
+ return [me.img];
+ };
- HtmlLabel = (function(_super) {
+ return Icon;
- __extends(HtmlLabel, _super);
+ })(kartograph.Symbol);
- function HtmlLabel(opts) {
- var me, _ref6, _ref7, _ref8;
- me = this;
- HtmlLabel.__super__.constructor.call(this, opts);
- me.text = (_ref6 = opts.text) != null ? _ref6 : '';
- me.style = (_ref7 = opts.style) != null ? _ref7 : '';
- me["class"] = (_ref8 = opts["class"]) != null ? _ref8 : '';
- }
+ Icon.props = ['icon', 'offset', 'class', 'title', 'iconsize'];
- HtmlLabel.prototype.render = function(layers) {
- var l, lbl, me;
- me = this;
- l = $('<div>' + me.text + '</div>');
- l.css({
- width: '50px',
- position: 'absolute',
- left: '-25px',
- 'text-align': 'center'
- });
- me.lbl = lbl = $('<div class="label" />');
- lbl.append(l);
- me.layers.lbl.append(lbl);
- l.css({
- height: l.height() + 'px',
- top: (l.height() * -.4) + 'px'
- });
- me.update();
- return me;
- };
+ Icon.layers = [];
- HtmlLabel.prototype.update = function() {
- var me;
- me = this;
- return me.lbl.css({
- position: 'absolute',
- left: me.x + 'px',
- top: me.y + 'px'
- });
- };
+ kartograph.Icon = Icon;
- HtmlLabel.prototype.clear = function() {
- var me;
- me = this;
- me.lbl.remove();
- return me;
- };
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
- HtmlLabel.prototype.nodes = function() {
- var me;
- me = this;
- return [me.lbl[0]];
- };
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- return HtmlLabel;
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- })(kartograph.Symbol);
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- HtmlLabel.props = ['text', 'style', 'class'];
- HtmlLabel.layers = [
- {
- id: 'lbl',
- type: 'html'
- }
- ];
+ SvgLabel = (function (_super) {
- kartograph.HtmlLabel = HtmlLabel;
+ __extends(SvgLabel, _super);
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
+ function SvgLabel(opts) {
+ var me, _ref6, _ref7, _ref8, _ref9;
+ me = this;
+ SvgLabel.__super__.constructor.call(this, opts);
+ me.text = (_ref6 = opts.text) != null ? _ref6 : '';
+ me.style = (_ref7 = opts.style) != null ? _ref7 : '';
+ me["class"] = (_ref8 = opts["class"]) != null ? _ref8 : '';
+ me.offset = (_ref9 = opts.offset) != null ? _ref9 : [0, 0];
+ }
+ SvgLabel.prototype.render = function (layers) {
+ var lbl, me;
+ me = this;
+ me.lbl = lbl = me.layers.mapcanvas.text(me.x, me.y, me.text);
+ me.update();
+ return me;
+ };
- LabeledBubble = (function(_super) {
+ SvgLabel.prototype.update = function () {
+ var me;
+ me = this;
+ me.lbl.attr({
+ x: me.x + me.offset[0],
+ y: me.y + me.offset[1]
+ });
+ me.lbl.node.setAttribute('style', me.style);
+ return me.lbl.node.setAttribute('class', me["class"]);
+ };
- __extends(LabeledBubble, _super);
+ SvgLabel.prototype.clear = function () {
+ var me;
+ me = this;
+ me.lbl.remove();
+ return me;
+ };
- function LabeledBubble(opts) {
- this.nodes = __bind(this.nodes, this);
+ SvgLabel.prototype.nodes = function () {
+ var me;
+ me = this;
+ return [me.lbl.node];
+ };
- this.clear = __bind(this.clear, this);
+ return SvgLabel;
- this.update = __bind(this.update, this);
+ })(kartograph.Symbol);
- this.render = __bind(this.render, this);
+ SvgLabel.props = ['text', 'style', 'class', 'offset'];
- var me, _ref6, _ref7;
- me = this;
- LabeledBubble.__super__.constructor.call(this, opts);
- me.labelattrs = (_ref6 = opts.labelattrs) != null ? _ref6 : {};
- me.buffer = opts.buffer;
- me.center = (_ref7 = opts.center) != null ? _ref7 : true;
- }
+ SvgLabel.layers = [];
- LabeledBubble.prototype.render = function(layers) {
- var me;
- me = this;
- if ((me.title != null) && String(me.title).trim() !== '') {
- if (me.buffer) {
- me.bufferlabel = me.layers.mapcanvas.text(me.x, me.y, me.title);
- }
- me.label = me.layers.mapcanvas.text(me.x, me.y, me.title);
- }
- LabeledBubble.__super__.render.call(this, layers);
- return me;
- };
+ kartograph.Label = SvgLabel;
+
+ HtmlLabel = (function (_super) {
- LabeledBubble.prototype.update = function(duration, easing) {
- var attrs, me, vp, x, y;
- if (duration == null) {
- duration = false;
- }
- if (easing == null) {
- easing = 'expo-out';
- }
- me = this;
- LabeledBubble.__super__.update.call(this, duration, easing);
- if (me.label != null) {
- vp = me.map.viewport;
- attrs = $.extend({}, me.labelattrs);
- x = me.x;
- y = me.y;
- if (me.center) {
- y -= 0;
- } else if (x > vp.width * 0.5) {
- attrs['text-anchor'] = 'end';
- x -= me.radius + 5;
- } else if (x < vp.width * 0.5) {
- attrs['text-anchor'] = 'start';
- x += me.radius + 5;
+ __extends(HtmlLabel, _super);
+
+ function HtmlLabel(opts) {
+ var me, _ref6, _ref7, _ref8;
+ me = this;
+ HtmlLabel.__super__.constructor.call(this, opts);
+ me.text = (_ref6 = opts.text) != null ? _ref6 : '';
+ me.style = (_ref7 = opts.style) != null ? _ref7 : '';
+ me["class"] = (_ref8 = opts["class"]) != null ? _ref8 : '';
}
- attrs['x'] = x;
- attrs['y'] = y;
- if (me.buffer) {
- me.bufferlabel.attr(attrs);
- me.bufferlabel.attr({
- stroke: '#fff',
- fill: '#fff',
- 'stroke-linejoin': 'round',
- 'stroke-linecap': 'round',
- 'stroke-width': 6
- });
+
+ HtmlLabel.prototype.render = function (layers) {
+ var l, lbl, me;
+ me = this;
+ l = $('<div>' + me.text + '</div>');
+ l.css({
+ width: '50px',
+ position: 'absolute',
+ left: '-25px',
+ 'text-align': 'center'
+ });
+ me.lbl = lbl = $('<div class="label" />');
+ lbl.append(l);
+ me.layers.lbl.append(lbl);
+ l.css({
+ height: l.height() + 'px',
+ top: (l.height() * -.4) + 'px'
+ });
+ me.update();
+ return me;
+ };
+
+ HtmlLabel.prototype.update = function () {
+ var me;
+ me = this;
+ return me.lbl.css({
+ position: 'absolute',
+ left: me.x + 'px',
+ top: me.y + 'px'
+ });
+ };
+
+ HtmlLabel.prototype.clear = function () {
+ var me;
+ me = this;
+ me.lbl.remove();
+ return me;
+ };
+
+ HtmlLabel.prototype.nodes = function () {
+ var me;
+ me = this;
+ return [me.lbl[0]];
+ };
+
+ return HtmlLabel;
+
+ })(kartograph.Symbol);
+
+ HtmlLabel.props = ['text', 'style', 'class'];
+
+ HtmlLabel.layers = [
+ {
+ id: 'lbl',
+ type: 'html'
}
- me.label.attr(attrs);
- me.label.toFront();
- }
- return me;
- };
+ ];
- LabeledBubble.prototype.clear = function() {
- var me;
- me = this;
- return LabeledBubble.__super__.clear.apply(this, arguments);
- };
+ kartograph.HtmlLabel = HtmlLabel;
- LabeledBubble.prototype.nodes = function() {
- var me, nodes;
- me = this;
- nodes = LabeledBubble.__super__.nodes.apply(this, arguments);
- if (me.label) {
- nodes.push(me.label.node);
- }
- if (me.bufferlabel) {
- nodes.push(me.bufferlabel.node);
- }
- return nodes;
- };
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- return LabeledBubble;
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- })(Bubble);
- LabeledBubble.props = ['radius', 'style', 'class', 'title', 'labelattrs', 'buffer', 'center', 'attrs'];
+ LabeledBubble = (function (_super) {
- LabeledBubble.layers = [];
+ __extends(LabeledBubble, _super);
- kartograph.LabeledBubble = LabeledBubble;
+ function LabeledBubble(opts) {
+ this.nodes = __bind(this.nodes, this);
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
+ this.clear = __bind(this.clear, this);
+ this.update = __bind(this.update, this);
+
+ this.render = __bind(this.render, this);
+
+ var me, _ref6, _ref7;
+ me = this;
+ LabeledBubble.__super__.constructor.call(this, opts);
+ me.labelattrs = (_ref6 = opts.labelattrs) != null ? _ref6 : {};
+ me.buffer = opts.buffer;
+ me.center = (_ref7 = opts.center) != null ? _ref7 : true;
+ }
+
+ LabeledBubble.prototype.render = function (layers) {
+ var me;
+ me = this;
+ if ((me.title != null) && String(me.title).trim() !== '') {
+ if (me.buffer) {
+ me.bufferlabel = me.layers.mapcanvas.text(me.x, me.y, me.title);
+ }
+ me.label = me.layers.mapcanvas.text(me.x, me.y, me.title);
+ }
+ LabeledBubble.__super__.render.call(this, layers);
+ return me;
+ };
- PieChart = (function(_super) {
- var me;
+ LabeledBubble.prototype.update = function (duration, easing) {
+ var attrs, me, vp, x, y;
+ if (duration == null) {
+ duration = false;
+ }
+ if (easing == null) {
+ easing = 'expo-out';
+ }
+ me = this;
+ LabeledBubble.__super__.update.call(this, duration, easing);
+ if (me.label != null) {
+ vp = me.map.viewport;
+ attrs = $.extend({}, me.labelattrs);
+ x = me.x;
+ y = me.y;
+ if (me.center) {
+ y -= 0;
+ } else if (x > vp.width * 0.5) {
+ attrs['text-anchor'] = 'end';
+ x -= me.radius + 5;
+ } else if (x < vp.width * 0.5) {
+ attrs['text-anchor'] = 'start';
+ x += me.radius + 5;
+ }
+ attrs['x'] = x;
+ attrs['y'] = y;
+ if (me.buffer) {
+ me.bufferlabel.attr(attrs);
+ me.bufferlabel.attr({
+ stroke: '#fff',
+ fill: '#fff',
+ 'stroke-linejoin': 'round',
+ 'stroke-linecap': 'round',
+ 'stroke-width': 6
+ });
+ }
+ me.label.attr(attrs);
+ me.label.toFront();
+ }
+ return me;
+ };
- __extends(PieChart, _super);
+ LabeledBubble.prototype.clear = function () {
+ var me;
+ me = this;
+ return LabeledBubble.__super__.clear.apply(this, arguments);
+ };
+
+ LabeledBubble.prototype.nodes = function () {
+ var me, nodes;
+ me = this;
+ nodes = LabeledBubble.__super__.nodes.apply(this, arguments);
+ if (me.label) {
+ nodes.push(me.label.node);
+ }
+ if (me.bufferlabel) {
+ nodes.push(me.bufferlabel.node);
+ }
+ return nodes;
+ };
+
+ return LabeledBubble;
+
+ })(Bubble);
+
+ LabeledBubble.props = ['radius', 'style', 'class', 'title', 'labelattrs', 'buffer', 'center', 'attrs'];
+
+ LabeledBubble.layers = [];
+
+ kartograph.LabeledBubble = LabeledBubble;
/*
- usage:
- new SymbolMap({
- map: map,
- radius: 10
- data: [25,75],
- colors: ['red', 'blue'],
- titles: ['red pie', 'blue pie']
- })
- */
-
-
- me = null;
-
- function PieChart(opts) {
- var _base2, _ref10, _ref11, _ref12, _ref13, _ref14, _ref6, _ref7, _ref8, _ref9;
- me = this;
- PieChart.__super__.constructor.call(this, opts);
- me.radius = (_ref6 = opts.radius) != null ? _ref6 : 4;
- me.styles = (_ref7 = opts.styles) != null ? _ref7 : '';
- me.colors = (_ref8 = opts.colors) != null ? _ref8 : ['#3cc', '#c3c', '#33c', '#cc3'];
- me.titles = (_ref9 = opts.titles) != null ? _ref9 : ['', '', '', '', ''];
- me.values = (_ref10 = opts.values) != null ? _ref10 : [];
- me.border = (_ref11 = opts.border) != null ? _ref11 : false;
- me.borderWidth = (_ref12 = opts.borderWidth) != null ? _ref12 : 2;
- me["class"] = (_ref13 = opts["class"]) != null ? _ref13 : 'piechart';
- if ((_ref14 = (_base2 = Raphael.fn).pieChart) == null) {
- _base2.pieChart = drawPieChart;
- }
- }
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ PieChart = (function (_super) {
+ var me;
+
+ __extends(PieChart, _super);
+
+ /*
+ usage:
+ new SymbolMap({
+ map: map,
+ radius: 10
+ data: [25,75],
+ colors: ['red', 'blue'],
+ titles: ['red pie', 'blue pie']
+ })
+ */
+
+
+ me = null;
+
+ function PieChart(opts) {
+ var _base2, _ref10, _ref11, _ref12, _ref13, _ref14, _ref6, _ref7, _ref8, _ref9;
+ me = this;
+ PieChart.__super__.constructor.call(this, opts);
+ me.radius = (_ref6 = opts.radius) != null ? _ref6 : 4;
+ me.styles = (_ref7 = opts.styles) != null ? _ref7 : '';
+ me.colors = (_ref8 = opts.colors) != null ? _ref8 : ['#3cc', '#c3c', '#33c', '#cc3'];
+ me.titles = (_ref9 = opts.titles) != null ? _ref9 : ['', '', '', '', ''];
+ me.values = (_ref10 = opts.values) != null ? _ref10 : [];
+ me.border = (_ref11 = opts.border) != null ? _ref11 : false;
+ me.borderWidth = (_ref12 = opts.borderWidth) != null ? _ref12 : 2;
+ me["class"] = (_ref13 = opts["class"]) != null ? _ref13 : 'piechart';
+ if ((_ref14 = (_base2 = Raphael.fn).pieChart) == null) {
+ _base2.pieChart = drawPieChart;
+ }
+ }
- PieChart.prototype.overlaps = function(bubble) {
- var dx, dy, r1, r2, x1, x2, y1, y2, _ref6, _ref7;
- _ref6 = [me.x, me.y, me.radius], x1 = _ref6[0], y1 = _ref6[1], r1 = _ref6[2];
- _ref7 = [bubble.x, bubble.y, bubble.radius], x2 = _ref7[0], y2 = _ref7[1], r2 = _ref7[2];
- if (x1 - r1 > x2 + r2 || x1 + r1 < x2 - r2 || y1 - r1 > y2 + r2 || y1 + r1 < y2 - r2) {
- return false;
- }
- dx = x1 - x2;
- dy = y1 - y2;
- if (dx * dx + dy * dy > (r1 + r2) * (r1 + r2)) {
- return false;
- }
- return true;
- };
+ PieChart.prototype.overlaps = function (bubble) {
+ var dx, dy, r1, r2, x1, x2, y1, y2, _ref6, _ref7;
+ _ref6 = [me.x, me.y, me.radius], x1 = _ref6[0], y1 = _ref6[1], r1 = _ref6[2];
+ _ref7 = [bubble.x, bubble.y, bubble.radius], x2 = _ref7[0], y2 = _ref7[1], r2 = _ref7[2];
+ if (x1 - r1 > x2 + r2 || x1 + r1 < x2 - r2 || y1 - r1 > y2 + r2 || y1 + r1 < y2 - r2) {
+ return false;
+ }
+ dx = x1 - x2;
+ dy = y1 - y2;
+ if (dx * dx + dy * dy > (r1 + r2) * (r1 + r2)) {
+ return false;
+ }
+ return true;
+ };
- PieChart.prototype.render = function(layers) {
- var bg;
- me = this;
- if (me.border != null) {
- bg = me.layers.mapcanvas.circle(me.x, me.y, me.radius + me.borderWidth).attr({
- stroke: 'none',
- fill: me.border
- });
- }
- me.chart = me.layers.mapcanvas.pieChart(me.x, me.y, me.radius, me.values, me.titles, me.colors, "none");
- me.chart.push(bg);
- return me;
- };
+ PieChart.prototype.render = function (layers) {
+ var bg;
+ me = this;
+ if (me.border != null) {
+ bg = me.layers.mapcanvas.circle(me.x, me.y, me.radius + me.borderWidth).attr({
+ stroke: 'none',
+ fill: me.border
+ });
+ }
+ me.chart = me.layers.mapcanvas.pieChart(me.x, me.y, me.radius, me.values, me.titles, me.colors, "none");
+ me.chart.push(bg);
+ return me;
+ };
- PieChart.prototype.update = function(opts) {
- var path;
- return;
- me.path.attr({
- x: me.x,
- y: me.y,
- r: me.radius
- });
- path = me.path;
- path.node.setAttribute('style', me.styles[0]);
- path.node.setAttribute('class', me["class"]);
- if (me.title != null) {
- path.attr('title', me.titles[0]);
- }
- return me;
- };
+ PieChart.prototype.update = function (opts) {
+ var path;
+ return;
+ me.path.attr({
+ x: me.x,
+ y: me.y,
+ r: me.radius
+ });
+ path = me.path;
+ path.node.setAttribute('style', me.styles[0]);
+ path.node.setAttribute('class', me["class"]);
+ if (me.title != null) {
+ path.attr('title', me.titles[0]);
+ }
+ return me;
+ };
- PieChart.prototype.clear = function() {
- var p, _i, _len, _ref6;
- me = this;
- _ref6 = me.chart;
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- p = _ref6[_i];
- p.remove();
- }
- return me;
- };
+ PieChart.prototype.clear = function () {
+ var p, _i, _len, _ref6;
+ me = this;
+ _ref6 = me.chart;
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ p = _ref6[_i];
+ p.remove();
+ }
+ return me;
+ };
- PieChart.prototype.nodes = function() {
- var el, _i, _len, _ref6, _results;
- _ref6 = me.chart;
- _results = [];
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- el = _ref6[_i];
- _results.push(el.node);
- }
- return _results;
- };
+ PieChart.prototype.nodes = function () {
+ var el, _i, _len, _ref6, _results;
+ _ref6 = me.chart;
+ _results = [];
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ el = _ref6[_i];
+ _results.push(el.node);
+ }
+ return _results;
+ };
- return PieChart;
+ return PieChart;
- })(Symbol);
+ })(Symbol);
- PieChart.props = ['radius', 'values', 'styles', 'class', 'titles', 'colors', 'border', 'borderWidth'];
+ PieChart.props = ['radius', 'values', 'styles', 'class', 'titles', 'colors', 'border', 'borderWidth'];
- PieChart.layers = [];
+ PieChart.layers = [];
- kartograph.PieChart = PieChart;
+ kartograph.PieChart = PieChart;
- /*
- pie chart extension for RaphaelJS
- */
+ /*
+ pie chart extension for RaphaelJS
+ */
- drawPieChart = function(cx, cy, r, values, labels, colors, stroke) {
- var angle, chart, i, paper, process, rad, sector, total, v, _i, _len;
- if (isNaN(cx) || isNaN(cy) || isNaN(r)) {
- return [];
- }
- paper = this;
- rad = Math.PI / 180;
- chart = paper.set();
- sector = function(cx, cy, r, startAngle, endAngle, params) {
- var x1, x2, y1, y2;
- x1 = cx + r * Math.cos(-startAngle * rad);
- x2 = cx + r * Math.cos(-endAngle * rad);
- y1 = cy + r * Math.sin(-startAngle * rad);
- y2 = cy + r * Math.sin(-endAngle * rad);
- return paper.path(["M", cx, cy, "L", x1, y1, "A", r, r, 0, +(endAngle - startAngle > 180), 0, x2, y2, "z"]).attr(params);
- };
- angle = -270;
- total = 0;
- process = function(j) {
- var angleplus, color, delta, ms, p, popangle, value;
- value = values[j];
- angleplus = 360 * value / total;
- popangle = angle + (angleplus * 0.5);
- color = colors[j];
- ms = 500;
- delta = 30;
- p = sector(cx, cy, r, angle, angle + angleplus, {
- fill: color,
- stroke: stroke,
- 'stroke-width': 1
- });
- p.mouseover(function() {
- p.stop().animate({
- transform: "s1.1 1.1 " + cx + " " + cy
- }, ms, "elastic");
- });
- p.mouseout(function() {
- p.stop().animate({
- transform: ""
- }, ms, "elastic");
- });
- angle += angleplus;
- chart.push(p);
- };
- for (_i = 0, _len = values.length; _i < _len; _i++) {
- v = values[_i];
- total += v;
- }
- for (i in values) {
- process(i);
- }
- return chart;
- };
-
- /*
- kartograph - a svg mapping library
- Copyright (C) 2011,2012 Gregor Aisch
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-
-
-drawStackedBars = function (cx, cy, w, h, values, labels, colors, stroke) {
- var paper = this,
- chart = this.set();
- function bar(x, y, w, h, params) {
- return paper.rect(x,y,w,h).attr(params);
- }
- var yo = 0,
- total = 0,
+ drawPieChart = function (cx, cy, r, values, labels, colors, stroke) {
+ var angle, chart, i, paper, process, rad, sector, total, v, _i, _len;
+ if (isNaN(cx) || isNaN(cy) || isNaN(r)) {
+ return [];
+ }
+ paper = this;
+ rad = Math.PI / 180;
+ chart = paper.set();
+ sector = function (cx, cy, r, startAngle, endAngle, params) {
+ var x1, x2, y1, y2;
+ x1 = cx + r * Math.cos(-startAngle * rad);
+ x2 = cx + r * Math.cos(-endAngle * rad);
+ y1 = cy + r * Math.sin(-startAngle * rad);
+ y2 = cy + r * Math.sin(-endAngle * rad);
+ return paper.path(["M", cx, cy, "L", x1, y1, "A", r, r, 0, +(endAngle - startAngle > 180), 0, x2, y2, "z"]).attr(params);
+ };
+ angle = -270;
+ total = 0;
process = function (j) {
- var value = values[j],
- bh = h * value / total,
- x = cx - w*0.5,
- y = cy + h*0.5 - yo,
- bw = w,
- color = colors[j],
- ms = 500,
- delta = 30,
- p = bar(x, y-bh, bw, bh, {fill: color, stroke: stroke, "stroke-width": 1});
-
- yo += bh;
-
+ var angleplus, color, delta, ms, p, popangle, value;
+ value = values[j];
+ angleplus = 360 * value / total;
+ popangle = angle + (angleplus * 0.5);
+ color = colors[j];
+ ms = 500;
+ delta = 30;
+ p = sector(cx, cy, r, angle, angle + angleplus, {
+ fill: color,
+ stroke: stroke,
+ 'stroke-width': 1
+ });
p.mouseover(function () {
- p.stop().animate({transform: "s1.1 1.1 " + cx + " " + cy}, ms, "elastic");
- }).mouseout(function () {
- p.stop().animate({transform: ""}, ms, "elastic");
-
+ p.stop().animate({
+ transform: "s1.1 1.1 " + cx + " " + cy
+ }, ms, "elastic");
+ });
+ p.mouseout(function () {
+ p.stop().animate({
+ transform: ""
+ }, ms, "elastic");
});
+ angle += angleplus;
chart.push(p);
};
- for (var i = 0, ii = values.length; i < ii; i++) {
- total += values[i];
- }
- for (i = 0; i < ii; i++) {
- process(i);
- }
- return chart;
-};
+ for (_i = 0, _len = values.length; _i < _len; _i++) {
+ v = values[_i];
+ total += v;
+ }
+ for (i in values) {
+ process(i);
+ }
+ return chart;
+ };
-;
+ /*
+ kartograph - a svg mapping library
+ Copyright (C) 2011,2012 Gregor Aisch
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- StackedBarChart = (function(_super) {
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
- __extends(StackedBarChart, _super);
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
- /*
- usage:
- new SymbolMap({
- map: map,
- radius: 10
- data: [25,75],
- colors: ['red', 'blue'],
- titles: ['red pie', 'blue pie']
- })
- */
-
-
- function StackedBarChart(opts) {
- var me, _base2, _ref10, _ref11, _ref12, _ref13, _ref6, _ref7, _ref8, _ref9;
- me = this;
- StackedBarChart.__super__.constructor.call(this, opts);
- me.styles = (_ref6 = opts.styles) != null ? _ref6 : '';
- me.colors = (_ref7 = opts.colors) != null ? _ref7 : [];
- me.titles = (_ref8 = opts.titles) != null ? _ref8 : ['', '', '', '', ''];
- me.values = (_ref9 = opts.values) != null ? _ref9 : [];
- me.width = (_ref10 = opts.width) != null ? _ref10 : 17;
- me.height = (_ref11 = opts.height) != null ? _ref11 : 30;
- me["class"] = (_ref12 = opts["class"]) != null ? _ref12 : 'barchart';
- if ((_ref13 = (_base2 = Raphael.fn).drawStackedBarChart) == null) {
- _base2.drawStackedBarChart = drawStackedBars;
- }
- }
- StackedBarChart.prototype.overlaps = function(bubble) {
- var dx, dy, me, r1, r2, x1, x2, y1, y2, _ref6, _ref7;
- me = this;
- _ref6 = [me.x, me.y, me.radius], x1 = _ref6[0], y1 = _ref6[1], r1 = _ref6[2];
- _ref7 = [bubble.x, bubble.y, bubble.radius], x2 = _ref7[0], y2 = _ref7[1], r2 = _ref7[2];
- if (x1 - r1 > x2 + r2 || x1 + r1 < x2 - r2 || y1 - r1 > y2 + r2 || y1 + r1 < y2 - r2) {
- return false;
- }
- dx = x1 - x2;
- dy = y1 - y2;
- if (dx * dx + dy * dy > (r1 + r2) * (r1 + r2)) {
- return false;
- }
- return true;
- };
+ drawStackedBars = function (cx, cy, w, h, values, labels, colors, stroke) {
+ var paper = this,
+ chart = this.set();
- StackedBarChart.prototype.render = function(layers) {
- var bg, h, me, w, x, y;
- me = this;
- w = me.width;
- h = me.height;
- x = me.x;
- y = me.y;
- bg = me.layers.mapcanvas.rect(x - w * 0.5 - 2, y - h * 0.5 - 2, w + 4, h + 4).attr({
- stroke: 'none',
- fill: '#fff'
- });
- me.chart = me.layers.mapcanvas.drawStackedBarChart(me.x, me.y, me.width, me.height, me.values, me.titles, me.colors, "none");
- me.chart.push(bg);
- return me;
- };
+ function bar(x, y, w, h, params) {
+ return paper.rect(x, y, w, h).attr(params);
+ }
- StackedBarChart.prototype.update = function() {
- var me, path;
- me = this;
- return;
- me.path.attr({
- x: me.x,
- y: me.y,
- r: me.radius
- });
- path = me.path;
- path.node.setAttribute('style', me.styles[0]);
- path.node.setAttribute('class', me["class"]);
- if (me.title != null) {
- path.attr('title', me.titles[0]);
- }
- return me;
+ var yo = 0,
+ total = 0,
+ process = function (j) {
+ var value = values[j],
+ bh = h * value / total,
+ x = cx - w * 0.5,
+ y = cy + h * 0.5 - yo,
+ bw = w,
+ color = colors[j],
+ ms = 500,
+ delta = 30,
+ p = bar(x, y - bh, bw, bh, {fill: color, stroke: stroke, "stroke-width": 1});
+
+ yo += bh;
+
+ p.mouseover(function () {
+ p.stop().animate({transform: "s1.1 1.1 " + cx + " " + cy}, ms, "elastic");
+ }).mouseout(function () {
+ p.stop().animate({transform: ""}, ms, "elastic");
+
+ });
+ chart.push(p);
+ };
+ for (var i = 0, ii = values.length; i < ii; i++) {
+ total += values[i];
+ }
+ for (i = 0; i < ii; i++) {
+ process(i);
+ }
+ return chart;
};
- StackedBarChart.prototype.clear = function() {
- var me, p, _i, _len, _ref6;
- me = this;
- _ref6 = me.chart;
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- p = _ref6[_i];
- p.remove();
- }
- me.chart = [];
- return me;
- };
+ ;
- StackedBarChart.prototype.nodes = function() {
- var el, me, _i, _len, _ref6, _results;
- me = this;
- _ref6 = me.chart;
- _results = [];
- for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
- el = _ref6[_i];
- _results.push(el.node);
- }
- return _results;
- };
- return StackedBarChart;
+ StackedBarChart = (function (_super) {
+
+ __extends(StackedBarChart, _super);
+
+ /*
+ usage:
+ new SymbolMap({
+ map: map,
+ radius: 10
+ data: [25,75],
+ colors: ['red', 'blue'],
+ titles: ['red pie', 'blue pie']
+ })
+ */
+
+
+ function StackedBarChart(opts) {
+ var me, _base2, _ref10, _ref11, _ref12, _ref13, _ref6, _ref7, _ref8, _ref9;
+ me = this;
+ StackedBarChart.__super__.constructor.call(this, opts);
+ me.styles = (_ref6 = opts.styles) != null ? _ref6 : '';
+ me.colors = (_ref7 = opts.colors) != null ? _ref7 : [];
+ me.titles = (_ref8 = opts.titles) != null ? _ref8 : ['', '', '', '', ''];
+ me.values = (_ref9 = opts.values) != null ? _ref9 : [];
+ me.width = (_ref10 = opts.width) != null ? _ref10 : 17;
+ me.height = (_ref11 = opts.height) != null ? _ref11 : 30;
+ me["class"] = (_ref12 = opts["class"]) != null ? _ref12 : 'barchart';
+ if ((_ref13 = (_base2 = Raphael.fn).drawStackedBarChart) == null) {
+ _base2.drawStackedBarChart = drawStackedBars;
+ }
+ }
+
+ StackedBarChart.prototype.overlaps = function (bubble) {
+ var dx, dy, me, r1, r2, x1, x2, y1, y2, _ref6, _ref7;
+ me = this;
+ _ref6 = [me.x, me.y, me.radius], x1 = _ref6[0], y1 = _ref6[1], r1 = _ref6[2];
+ _ref7 = [bubble.x, bubble.y, bubble.radius], x2 = _ref7[0], y2 = _ref7[1], r2 = _ref7[2];
+ if (x1 - r1 > x2 + r2 || x1 + r1 < x2 - r2 || y1 - r1 > y2 + r2 || y1 + r1 < y2 - r2) {
+ return false;
+ }
+ dx = x1 - x2;
+ dy = y1 - y2;
+ if (dx * dx + dy * dy > (r1 + r2) * (r1 + r2)) {
+ return false;
+ }
+ return true;
+ };
+
+ StackedBarChart.prototype.render = function (layers) {
+ var bg, h, me, w, x, y;
+ me = this;
+ w = me.width;
+ h = me.height;
+ x = me.x;
+ y = me.y;
+ bg = me.layers.mapcanvas.rect(x - w * 0.5 - 2, y - h * 0.5 - 2, w + 4, h + 4).attr({
+ stroke: 'none',
+ fill: '#fff'
+ });
+ me.chart = me.layers.mapcanvas.drawStackedBarChart(me.x, me.y, me.width, me.height, me.values, me.titles, me.colors, "none");
+ me.chart.push(bg);
+ return me;
+ };
+
+ StackedBarChart.prototype.update = function () {
+ var me, path;
+ me = this;
+ return;
+ me.path.attr({
+ x: me.x,
+ y: me.y,
+ r: me.radius
+ });
+ path = me.path;
+ path.node.setAttribute('style', me.styles[0]);
+ path.node.setAttribute('class', me["class"]);
+ if (me.title != null) {
+ path.attr('title', me.titles[0]);
+ }
+ return me;
+ };
+
+ StackedBarChart.prototype.clear = function () {
+ var me, p, _i, _len, _ref6;
+ me = this;
+ _ref6 = me.chart;
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ p = _ref6[_i];
+ p.remove();
+ }
+ me.chart = [];
+ return me;
+ };
+
+ StackedBarChart.prototype.nodes = function () {
+ var el, me, _i, _len, _ref6, _results;
+ me = this;
+ _ref6 = me.chart;
+ _results = [];
+ for (_i = 0, _len = _ref6.length; _i < _len; _i++) {
+ el = _ref6[_i];
+ _results.push(el.node);
+ }
+ return _results;
+ };
+
+ return StackedBarChart;
- })(kartograph.Symbol);
+ })(kartograph.Symbol);
- StackedBarChart.props = ['values', 'styles', 'class', 'titles', 'colors', 'width', 'height'];
+ StackedBarChart.props = ['values', 'styles', 'class', 'titles', 'colors', 'width', 'height'];
- StackedBarChart.layers = [];
+ StackedBarChart.layers = [];
- kartograph.StackedBarChart = StackedBarChart;
+ kartograph.StackedBarChart = StackedBarChart;
}).call(this);
diff --git a/plugins/UserCountryMap/js/vendor/kartograph.min.js b/plugins/UserCountryMap/js/vendor/kartograph.min.js
index 2a6e8ac636..1f3a863c4a 100644
--- a/plugins/UserCountryMap/js/vendor/kartograph.min.js
+++ b/plugins/UserCountryMap/js/vendor/kartograph.min.js
@@ -15,7 +15,1673 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
-*/
-(function(){function bS(){function h(a,b,c){f(a.point,b)<f(c.point,b)&&(c=a),a.left&&(c=h(a.left,b,c));if(a.right){var d=a.point[a.axis]-b[a.axis];d*d<f(c.point,b)&&(c=h(a.right,b,c))}return c}function g(a){return function(b,c){b=b[a],c=c[a];return b<c?-1:b>c?1:0}}function f(a,c){var d=0;for(var e=0;e<b.length;e++){var f=b[e],g=a[f]-c[f];d+=g*g}return d}function e(a,c){if(!!a.length){var d=b[c%b.length],f=a.length>>1;a.sort(g(d));return{axis:d,point:a[f],left:e(a.slice(0,f),c+1),right:e(a.slice(f+1),c+1)}}}var a={},b=["x","y"],c,d=[];a.axes=function(c){if(!arguments.length)return b;b=c;return a},a.points=function(b){if(!arguments.length)return d;d=b,c=null;return a},a.find=function(b){return h(a.root(),b,c).point},a.root=function(a){return c||(c=e(d,0))};return a}function bR(){var a={},b=[],c=1,d=1;a.size=function(b){if(!arguments.length)return d;d=b;return a},a.iterations=function(b){if(!arguments.length)return c;c=b;return a},a.add=function(c){b.push(c);return a},a.means=function(){var a=[],e={},f=Math.min(d,b.length);for(var g=0,h=2*f;g<h;g++){var i=b[~~(Math.random()*b.length)],j=i.x+"/"+i.y;if(!(j in e)){e[j]=1;if(a.push({x:i.x,y:i.y})>=f)break}}f=a.length;for(var k=0;k<c;k++){var l=bS().points(a);for(var g=0;g<f;g++){var m=a[g];m.sumX=0,m.sumY=0,m.size=0,m.points=[],m.indices=[]}for(var g=0;g<b.length;g++){var n=b[g],m=l.find(n);m.sumX+=n.x,m.sumY+=n.y,m.size++,m.points.push(n),m.indices.push(g)}for(var g=0;g<f;g++){var m=a[g];if(!m.size)continue;m.x=m.sumX/m.size,m.y=m.sumY/m.size}}return a};return a}var a,b,c,d,e,f,g,h,i,j,k,l,n,o,p,q,r,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,_,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,bu,bv,bw,bx,by,bz,bA,bB,bC,bD,bE,bF,bG,bH,bI,bJ,bK,bL,bM,bN={}.hasOwnProperty,bO=function(a,b){function d(){this.constructor=a}for(var c in b)bN.call(b,c)&&(a[c]=b[c]);d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype;return a},bP=function(a,b){return function(){return a.apply(b,arguments)}},bQ=[].indexOf||function(a){for(var b=0,c=this.length;b<c;b++)if(b in this&&this[b]===a)return b;return-1};bz=typeof exports!="undefined"&&exports!==null?exports:this,br=bz.$K=window.Kartograph=(bH=bz.Kartograph)!=null?bH:bz.Kartograph={},br.version="0.5.2",a=bz.jQuery,br.__verbose=!1,bB=function(a){try{return console.warn.apply(console,arguments)}catch(b){try{return opera.postError.apply(opera,arguments)}catch(b){return alert(Array.prototype.join.call(arguments," "))}}},bs=function(a){if(br.__verbose)try{return console.debug.apply(console,arguments)}catch(b){try{return opera.postError.apply(opera,arguments)}catch(b){return alert(Array.prototype.join.call(arguments," "))}}},(bI=(bF=String.prototype).trim)==null&&(bF.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){"use strict";if(this==null)throw new TypeError;var b=Object(this),c=b.length>>>0;if(c===0)return-1;var d=0;arguments.length>0&&(d=Number(arguments[1]),d!=d?d=0:d!=0&&d!=Infinity&&d!=-Infinity&&(d=(d>0||-1)*Math.floor(Math.abs(d))));if(d>=c)return-1;var e=d>=0?d:Math.max(c-Math.abs(d),0);for(;e<c;e++)if(e in b&&b[e]===a)return e;return-1}),bE=function(){var a,b,c,d,e;a={},e="Boolean Number String Function Array Date RegExp Undefined Null".split(" ");for(c=0,d=e.length;c<d;c++)b=e[c],a["[object "+b+"]"]=b.toLowerCase();return function(b){var c;c=Object.prototype.toString.call(b);return a[c]||"object"}}(),d=function(){function a(a,b,c,d){var e;a==null&&(a=0),b==null&&(b=0),c==null&&(c=null),d==null&&(d=null),e=this,c===null?(e.xmin=Number.MAX_VALUE,e.xmax=Number.MAX_VALUE*-1):(e.xmin=e.left=a,e.xmax=e.right=a+c,e.width=c),d===null?(e.ymin=Number.MAX_VALUE,e.ymax=Number.MAX_VALUE*-1):(e.ymin=e.top=b,e.ymax=e.bottom=d+b,e.height=d);return}a.prototype.update=function(a,b){var c;b==null&&(b=a[1],a=a[0]),c=this,c.xmin=Math.min(c.xmin,a),c.ymin=Math.min(c.ymin,b),c.xmax=Math.max(c.xmax,a),c.ymax=Math.max(c.ymax,b),c.left=c.xmin,c.top=c.ymin,c.right=c.xmax,c.bottom=c.ymax,c.width=c.xmax-c.xmin,c.height=c.ymax-c.ymin;return this},a.prototype.intersects=function(a){return a.left<s.right&&a.right>s.left&&a.top<s.bottom&&a.bottom>s.top},a.prototype.inside=function(a,b){var c;c=this;return a>=c.left&&a<=c.right&&b>=c.top&&b<=c.bottom},a.prototype.join=function(a){var b;b=this,b.update(a.left,a.top),b.update(a.right,a.bottom);return this};return a}(),d.fromXML=function(a){var b,c,d,e;d=Number(a.getAttribute("x")),e=Number(a.getAttribute("y")),c=Number(a.getAttribute("w")),b=Number(a.getAttribute("h"));return new br.BBox(d,e,c,b)},br.BBox=d,(bJ=br.geom)==null&&(br.geom={}),(bK=(bG=br.geom).clipping)==null&&(bG.clipping={}),l=function(){function f(){}var a,b,c,d,e;b=0,c=1,d=2,a=4,e=8,f.prototype.compute_out_code=function(a,b,c){var d,e;e=this,d=e.INSIDE,b<a.left?d|=e.LEFT:b>a.right&&(d|=e.RIGHT),c<a.top?d|=e.TOP:c>a.bottom&&(d|=e.BOTTOM);return d},f.prototype.clip=function(a,b,c,d,e){var f,g,h,i,j,k,l;j=this,g=j.compute_out_code(a,b,c),h=j.compute_out_code(a,d,e),f=False;while(True){if(!(g|h)){f=True;break}if(g&h)break;i=code===0?h:g,i&j.TOP?(k=b+(d-b)*(a.top-c)/(e-c),l=a.top):i&j.BOTTOM?(k=b+(d-b)*(a.bottom-c)/(e-c),l=a.bottom):i&j.RIGHT?(l=c+(e-c)*(a.right-b)/(d-b),k=a.right):i&j.LEFT&&(l=c+(e-c)*(a.left-b)/(d-b),k=a.left),i===g?(b=k,c=l,g=j.compute_out_code(a,b,c)):(d=k,e=l,h=j.compute_out_code(a,d,e))}return f?[b,c,d,e]:null};return f}(),br.geom.clipping.CohenSutherland=l,B=function(){function b(b,c,d){var e,f;f=this,f.container=e=a(b),c==null&&(c=e.width()),d==null&&(d=e.height()),d===0&&(d="auto"),f.size={h:d,w:c},f.markers=[],f.pathById={},f.container.addClass("kartograph")}b.prototype.createSVGLayer=function(b){var c,d,e,f,g,h,i,j;f=this,(j=f._layerCnt)==null&&(f._layerCnt=0),e=f._layerCnt++,i=f.viewport,d=f.container,g=Raphael(d[0],i.width,i.height),h=a(g.canvas),h.css({position:"absolute",top:"0px",left:"0px","z-index":e+5}),d.css("position")==="static"&&d.css({position:"relative",height:i.height+"px"}),h.addClass(b),c=a("desc",g.canvas).text(),a("desc",g.canvas).text(c.replace("with ","with kartograph "+br.version+" and "));return g},b.prototype.createHTMLLayer=function(b){var c,d,e,f,g,h;f=this,g=f.viewport,c=f.container,(h=f._layerCnt)==null&&(f._layerCnt=0),e=f._layerCnt++,d=a('<div class="layer '+b+'" />'),d.css({position:"absolute",top:"0px",left:"0px",width:g.width+"px",height:g.height+"px","z-index":e+5}),c.append(d);return d},b.prototype.loadMap=function(b,c,d){var e,f,g,h;f=this,e=a.Deferred(),f.clear(),f.opts=d!=null?d:{},(h=(g=f.opts).zoom)==null&&(g.zoom=1),f.mapLoadCallback=c,f._loadMapDeferred=e,f._lastMapUrl=b,f.cacheMaps&&br.__mapCache[b]!=null?f._mapLoaded(br.__mapCache[b]):a.ajax({url:b,dataType:"text",success:f._mapLoaded,context:f,error:function(a,b,c){return bB(a,b,c)}});return e.promise()},b.prototype.setMap=function(a,b){var c,d,e;c=this,c.opts=b!=null?b:{},(e=(d=c.opts).zoom)==null&&(d.zoom=1),c._lastMapUrl="string",c._mapLoaded(a)},b.prototype._mapLoaded=function(b){var c,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;h=this,h.cacheMaps&&((o=br.__mapCache)==null&&(br.__mapCache={}),br.__mapCache[h._lastMapUrl]=b);try{b=a(b)}catch(t){bB("something went horribly wrong while parsing svg"),h._loadMapDeferred.reject("could not parse svg");return}h.svgSrc=b,c=a("view",b),bs("got svg src",h.svgSrc),h.paper==null&&(m=h.size.w,f=h.size.h,f==="auto"&&(j=c.attr("w")/c.attr("h"),f=m/j),h.viewport=new d(0,0,m,f)),l=h.viewport,bs("got viewport",h.viewport),h.viewAB=e=br.View.fromXML(c[0]),bs("got first view",h.viewAB),i=(p=h.opts.padding)!=null?p:0,g=(q=h.opts.halign)!=null?q:"center",k=(r=h.opts.valign)!=null?r:"center",bs("got alignment",g,k),n=(s=h.opts.zoom)!=null?s:1,h.viewBC=new br.View(h.viewAB.asBBox(),l.width*n,l.height*n,i,g,k),bs("got second view",h.viewBC),h.proj=br.Proj.fromXML(a("proj",c)[0]),bs("got projection",h.proj),h.mapLoadCallback!=null&&h.mapLoadCallback(h),h._loadMapDeferred!=null&&h._loadMapDeferred.resolve(h)},b.prototype.addLayer=function(b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w;c==null&&(c={}),i=this,(u=i.layerIds)==null&&(i.layerIds=[]),(v=i.layers)==null&&(i.layers={}),i.paper==null&&(i.paper=i.createSVGLayer()),l=b,bE(c)==="object"?(h=c.name,j=c.key,o=c.title):c={},h==null&&(h=l),m=a("#"+l,i.svgSrc);if(m.length!==0){g=new L(h,j,i,c.filter),d=a("*",m[0]);for(q=0,s=d.length;q<s;q++)n=d[q],g.addPath(n,o);g.paths.length>0&&(i.layers[h]=g,i.layerIds.push(h)),e=["click","mouseenter","mouseleave","dblclick","mousedown","mouseup","mouseover","mouseout"];for(r=0,t=e.length;r<t;r++)f=e[r],bE(c[f])==="function"&&g.on(f,c[f]);if(c.styles!=null){w=c.styles;for(k in w)p=w[k],g.style(k,p)}c.tooltips!=null&&g.tooltips(c.tooltips);return i}},b.prototype.getLayer=function(a){var b;b=this;if(b.layers[a]==null){bB("could not find layer "+a);return null}return b.layers[a]},b.prototype.getLayerPath=function(a,b){var c,d;d=this,c=d.getLayer(a);if(c!=null)return bE(b)==="object"?c.getPaths(b)[0]:c.getPath(b);return null},b.prototype.onLayerEvent=function(a,b,c){var d;d=this,d.getLayer(c).on(a,b);return d},b.prototype.addMarker=function(a){var b,c;b=this,b.markers.push(a),c=b.viewBC.project(b.viewAB.project(b.proj.project(a.lonlat.lon,a.lonlat.lat)));return a.render(c[0],c[1],b.container,b.paper)},b.prototype.clearMarkers=function(){var a,b,c,d,e;b=this,e=b.markers;for(c=0,d=e.length;c<d;c++)a=e[c],a.clear();return b.markers=[]},b.prototype.fadeIn=function(a){var b,c,d,e,f,g,h,i,j,k,l;a==null&&(a={}),f=this,e=(i=a.layer)!=null?i:f.layerIds[f.layerIds.length-1],c=(j=a.duration)!=null?j:500,k=f.layers[e].pathsById,l=[];for(d in k)h=k[d],l.push(function(){var a,d,e;e=[];for(a=0,d=h.length;a<d;a++)g=h[a],bE(c)==="function"?b=c(g.data):b=c,g.svgPath.attr("opacity",0),e.push(g.svgPath.animate({opacity:1},b));return e}());return l},b.prototype.loadCoastline=function(){var b;b=this;return a.ajax({url:"coastline.json",success:b.renderCoastline,context:b})},b.prototype.resize=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;g=this,c=g.container,a==null&&(a=c.width()),b==null&&(b=c.height()),g.viewport=k=new br.BBox(0,0,a,b),g.paper!=null&&g.paper.setSize(k.width,k.height),k=g.viewport,h=(o=g.opts.padding)!=null?o:0,d=(p=g.opts.halign)!=null?p:"center",j=(q=g.opts.valign)!=null?q:"center",l=g.opts.zoom,g.viewBC=new br.View(g.viewAB.asBBox(),k.width*l,k.height*l,h,d,j),r=g.layers;for(e in r)f=r[e],f.setView(g.viewBC);if(g.symbolGroups!=null){s=g.symbolGroups;for(m=0,n=s.length;m<n;m++)i=s[m],i.onResize()}},b.prototype.lonlat2xy=function(a){var b,c;c=this,a.length===2&&(a=new br.LonLat(a[0],a[1])),a.length===3&&(a=new br.LonLat(a[0],a[1],a[2])),b=c.proj.project(a.lon,a.lat,a.alt);return c.viewBC.project(c.viewAB.project(b))},b.prototype.showZoomControls=function(){var a;a=this,a.zc=new S(a);return a},b.prototype.addSymbolGroup=function(a){var b,c;b=this,(c=b.symbolGroups)==null&&(b.symbolGroups=[]);return b.symbolGroups.push(a)},b.prototype.removeSymbols=function(a){var b,c,d,e,f,g;b=this;if(a!=null)return b.symbolGroups[a].remove();f=b.symbolGroups,g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(c.remove());return g},b.prototype.clear=function(){var b,c,d,e,f,g;c=this;if(c.layers!=null){for(b in c.layers)c.layers[b].remove();c.layers={},c.layerIds=[]}if(c.symbolGroups!=null){g=c.symbolGroups;for(e=0,f=g.length;e<f;e++)d=g[e],d.remove();c.symbolGroups=[]}if(c.paper!=null){a(c.paper.canvas).remove();return c.paper=void 0}},b.prototype.loadCSS=function(b,c){var d;d=this;if(a.browser.msie)return a.ajax({url:b,dataType:"text",success:function(a){d.styles=br.parsecss(a);return c()},error:function(a,c,d){return bB("error while loading "+b,a,c,d)}});a("body").append('<link rel="stylesheet" href="'+b+'" />');return c()},b.prototype.applyCSS=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q;e=this;if(e.styles==null)return a;(n=e._pathTypes)==null&&(e._pathTypes=["path","circle","rectangle","ellipse"]),(o=e._regardStyles)==null&&(e._regardStyles=["fill","stroke","fill-opacity","stroke-width","stroke-opacity"]);for(h in e.styles){f=h,p=f.split(",");for(j=0,l=p.length;j<l;j++){i=p[j],f=i.split(" "),f=f[f.length-1],f=f.split(":");if(f.length>1)continue;f=f[0].split("."),c=f.slice(1);if(c.length>0&&c.indexOf(b)<0)continue;f=f[0];if(e._pathTypes.indexOf(f)>=0&&f!==a.type)continue;g=e.styles[h],q=e._regardStyles;for(k=0,m=q.length;k<m;k++)d=q[k],g[d]!=null&&a.attr(d,g[d])}}return a},b.prototype.style=function(a,b,c,d,e){var f;f=this,a=f.getLayer(a);if(a!=null)return a.style(b,c,d,e)};return b}(),br.Kartograph=B,br.map=function(a,b,c){return new B(a,b,c)},br.__mapCache={},J=function(){function a(a,b,c){c==null&&(c=0),this.lon=Number(a),this.lat=Number(b),this.alt=Number(c)}a.prototype.distance=function(a){var b,c,d,e,f,g,h,i,j;j=this,b=6371,g=Math.PI/180,e=(a.lat-j.lat)*g,f=(a.lon-j.lon)*g,h=j.lat*g,i=a.lat*g,c=Math.sin(e/2)*Math.sin(e/2)+Math.sin(f/2)*Math.sin(f/2)*Math.cos(h)*Math.cos(i),d=2*Math.atan2(Math.sqrt(c),Math.sqrt(1-c));return b*d};return a}(),F=function(a){function b(a,c,d){d==null&&(d=0),b.__super__.constructor.call(this,c,a,d)}bO(b,a);return b}(J),br.LonLat=J,br.LatLon=F,L=function(){function b(a,b,c,d){var e;e=this,e.id=a,e.path_id=b,e.paper=c.paper,e.view=c.viewBC,e.map=c,e.filter=d}b.prototype.addPath=function(a,b){var c,d,e,f,g,h,i;d=this,(g=d.paths)==null&&(d.paths=[]),c=new M(a,d.id,d.map,b);if(bE(d.filter)==="function"&&d.filter(c.data)===!1)c.remove();else{d.paths.push(c);if(d.path_id!=null){(h=d.pathsById)==null&&(d.pathsById={}),(i=(e=d.pathsById)[f=c.data[d.path_id]])==null&&(e[f]=[]);return d.pathsById[c.data[d.path_id]].push(c)}}},b.prototype.hasPath=function(a){var b;b=this;return b.pathsById!=null&&b.pathsById[a]!=null},b.prototype.getPathsData=function(){var a,b,c,d,e,f;a=this,c=[],f=a.paths;for(d=0,e=f.length;d<e;d++)b=f[d],c.push(b.data);return c},b.prototype.getPath=function(a){var b;b=this;return b.hasPath(a)?b.pathsById[a][0]:null},b.prototype.getPaths=function(a){var b,c,d,e,f,g,h,i;e=this,d=[];if(bE(a)==="object"){i=e.paths;for(g=0,h=i.length;g<h;g++){f=i[g],c=!0;for(b in a)c=c&&f.data[b]===a[b];c&&d.push(f)}}return d},b.prototype.setView=function(a){var b,c,d,e,f;b=this,f=b.paths;for(d=0,e=f.length;d<e;d++)c=f[d],c.setView(a);return b},b.prototype.remove=function(){var a,b,c,d,e,f;a=this,e=a.paths,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(b.remove());return f},b.prototype.style=function(a,b,c,d){var e,f,g,h,i,j,k,l,m,n,o;j=this;if(bE(a)==="object"){for(i in a)l=a[i],j.style(i,l);return j}c==null&&(c=0),d==null&&(d=0),o=j.paths;for(m=0,n=o.length;m<n;m++)k=o[m],l=bx(b,k.data),h=bx(c,k.data),g=bx(d,k.data),h>0?(f={},f[a]=l,e=Raphael.animation(f,h*1e3),k.svgPath.animate(e.delay(g*1e3))):k.svgPath.attr(a,l);return j},b.prototype.on=function(b,c){var d,e,f,g,h,i,j;f=this,d=function(){function a(a,b,c){this.type=a,this.cb=b,this.layer=c,this.handle=bP(this.handle,this)}a.prototype.handle=function(a){var b;f=this,b=f.layer.map.pathById[a.target.getAttribute("id")];return f.cb(b.data,b.svgPath,a)};return a}(),e=new d(b,c,f),j=f.paths;for(h=0,i=j.length;h<i;h++)g=j[h],a(g.svgPath.node).bind(b,e.handle);return f},b.prototype.tooltips=function(b,c){var d,e,f,g,h,i,j;d=this,f=function(b,d){var e;e={position:{target:"mouse",viewport:a(window),adjust:{x:7,y:7}},show:{delay:c!=null?c:20},events:{show:function(b,c){return a(".qtip").filter(function(){return this!==c.elements.tooltip.get(0)}).hide()}},content:{}},d!=null?typeof d=="string"?e.content.text=d:a.isArray(d)&&(e.content.title=d[0],e.content.text=d[1]):e.content.text="n/a";return a(b.svgPath.node).qtip(e)},j=d.paths;for(h=0,i=j.length;h<i;h++)e=j[h],g=bx(b,e.data),f(e,g);return d},b.prototype.sort=function(a){var b,c,d,e,f,g;c=this,c.paths.sort(function(b,c){var d,e,f;d=a(b.data),e=a(c.data);return d===e?0:(f=d>e)!=null?f:{1:-1}}),b=!1,g=c.paths;for(e=0,f=g.length;e<f;e++)d=g[e],b&&d.svgPath.insertAfter(b.svgPath),b=d;return c};return b}(),bx=function(a,b){return bE(a)==="function"?a(b):a},bt=0,M=function(){function a(a,b,c,d){var e,f,g,h,i,j,k,l,m,n,o,p,q;h=this,i=c.paper,n=c.viewBC,h.path=j=br.geom.Path.fromSVG(a),h.vpath=n.projectPath(j),h.svgPath=h.vpath.toSVG(i),c.styles==null?h.svgPath.node.setAttribute("class",b):c.applyCSS(h.svgPath,b),l="path_"+bt++,h.svgPath.node.setAttribute("id",l),c.pathById[l]=h,f={};for(g=p=0,q=a.attributes.length-1;0<=q?p<=q:p>=q;g=0<=q?++p:--p)e=a.attributes[g],e.name.substr(0,5)==="data-"&&(m=e.value,o=Number(m),m.trim()!==""&&o===m&&!isNaN(o)&&(m=o),f[e.name.substr(5)]=m);h.data=f,bE(d)==="string"?k=d:bE(d)==="function"&&(k=d(f)),k!=null&&h.svgPath.attr("title",k)}a.prototype.setView=function(a){var b,c,d;b=this,c=a.projectPath(b.path),b.vpath=c;if(b.path.type==="path"){d=c.svgString();return b.svgPath.attr({path:d})}if(b.path.type==="circle")return b.svgPath.attr({cx:c.x,cy:c.y,r:c.r})},a.prototype.remove=function(){var a;a=this;return a.svgPath.remove()};return a}(),br.parsecss=function(a,b){var c,d,e,f,g,h,i,j;f={},a=bu(a),j=a.split("`b%");for(h=0,i=j.length;h<i;h++){c=j[h],c=c.split("%b`");if(c.length<2)continue;c[0]=by(c[0]),e=bw(c[1]);if(f[c[0]]!=null)for(d in e)g=e[d],f[c[0]][d]=g;else f[c[0]]=e}if(bE(b)==="function")b(f);else return f},bv={},bw=function(a){var b,c,d,e,f,g;d=bv[a].replace(/^{|}$/g,""),d=bu(d),c={},g=d.split(";");for(e=0,f=g.length;e<f;e++){b=g[e],b=b.split(":");if(b.length<2)continue;c[by(b[0])]=by(b.slice(1).join(":"))}return c},Z=/{[^{}]*}/,_=/\[[^\[\]]*\]|{[^{}]*}|\([^()]*\)|function(\s+\w+)?(\s*%b`\d+`b%){2}/,$=/(?:\/\*(?:[^\*]|\*[^\/])*\*\/)|(\\.|"(?:[^\\\"]|\\.|\\\n)*"|'(?:[^\\\']|\\.|\\\n)*')/g,ba=/%\w`(\d+)`\w%/,bA=0,bu=function(a,b){var c,d,e;a=a.replace($,function(a,b){var c;if(!b)return"";c="%s`"+ ++bA+"`s%",bv[bA]=b.replace(/^\\/,"");return c}),c=b?_:Z;while(d=c.exec(a))e="%b`"+ ++bA+"`b%",bv[bA]=d[0],a=a.replace(c,e);return a},by=function(a){var b;if(a==null)return a;while(b=ba.exec(a))a=a.replace(ba,bv[b[1]]);return a.trim()},(bL=br.geom)==null&&(br.geom={}),T=function(){function a(a,b,c){var d;c==null&&(c=!0),d=this,d.type=a,d.contours=b,d.closed=c}a.prototype.clipToBBox=function(a){throw"path clipping is not implemented yet"},a.prototype.toSVG=function(a){var b;b=this.svgString();return a.path(b)},a.prototype.svgString=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m;d=this,e="",c=d.closed?"Z M":"M",l=d.contours;for(h=0,j=l.length;h<j;h++){a=l[h],b=!0,e+=e===""?"M":c;for(i=0,k=a.length;i<k;i++)m=a[i],f=m[0],g=m[1],b||(e+="L"),e+=f+","+g,b=!1}d.closed&&(e+="Z");return e},a.prototype.area=function(){var a,b,c,d,e,f,g,h,i;d=this;if(d.areas!=null)return d._area;d.areas=[],d._area=0,h=d.contours;for(e=0,g=h.length;e<g;e++){b=h[e],a=0;for(c=f=0,i=b.length-2;0<=i?f<=i:f>=i;c=0<=i?++f:--f)a+=b[c][0]*b[c+1][1]-b[c+1][0]*b[c][1];a*=.5,a=a,d.areas.push(a),d._area+=a}return d._area},a.prototype.centroid=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K;p=this;if(p._centroid!=null)return p._centroid;c=p.area(),f=g=0;for(k=A=0,G=p.contours.length-1;0<=G?A<=G:A>=G;k=0<=G?++A:--A){e=p.contours[k],d=[],n=e.length;for(l=B=0,H=n-1;0<=H?B<=H:B>=H;l=0<=H?++B:--B){q=e[l],r=e[(l+1)%n],h=0,d.push(q),q[0]===r[0]&&(h=Math.abs(q[1]-r[1])),q[1]===r[1]&&(h=Math.abs(q[0]-r[0]));if(h>10){a=Math.floor(h*2);for(s=C=1,I=a-1;1<=I?C<=I:C>=I;s=1<=I?++C:--C)t=[q[0]+s/a*(r[0]-q[0]),q[1]+s/a*(r[1]-q[1])],d.push(t)}}b=p.areas[k],w=y=x=z=0,n=d.length,E=[],u=0;for(l=D=0,J=n-1;0<=J?D<=J:D>=J;l=0<=J?++D:--D)q=d[l],r=d[(l+1)%n],i=r[0]-q[0],j=r[1]-q[1],o=Math.sqrt(i*i+j*j),E.push(o),u+=o;for(l=F=0,K=n-1;0<=K?F<=K:F>=K;l=0<=K?++F:--F)q=d[l],v=E[l]/u,w+=v*q[0],y+=v*q[1];m=b/c,f+=w*m,g+=y*m}p._centroid=[f,g];return p._centroid},a.prototype.isInside=function(a,b){var c,d,e,f,g,h;f=this,c=f._bbox;if(a<c[0]||a>c[2]||b<c[1]||b>c[3])return!1;for(e=g=0,h=f.contours.length-1;0<=h?g<=h:g>=h;e=0<=h?++g:--g){d=f.contours[e];if(bC(d,[a,b]))return!0}return!1};return a}(),br.geom.Path=T,k=function(a){function b(a,c,d){this.x=a,this.y=c,this.r=d,b.__super__.constructor.call(this,"circle",null,!0)}bO(b,a),b.prototype.toSVG=function(a){var b;b=this;return a.circle(b.x,b.y,b.r)},b.prototype.centroid=function(){var a;a=this;return[a.x,a.y]},b.prototype.area=function(){var a;a=this;return Math.PI*a.r*m.r};return b}(T),br.geom.Circle=k,T.fromSVG=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o;e=[],m=a.nodeName,k=null;if(m==="path"){i=a.getAttribute("d").trim(),h=Raphael.parsePathString(i),b=h[h.length-1]==="Z",l=b?"Z M":"M",d=[];for(n=0,o=h.length;n<o;n++){c=h[n];if(c.length===0)continue;c[0]==="M"?(d.length>2&&(e.push(d),d=[]),d.push([c[1],c[2]])):c[0]==="L"?d.push([c[1],c[2]]):c[0]==="Z"&&d.length>2&&(e.push(d),d=[])}d.length>2&&(e.push(d),d=[]),k=new br.geom.Path(m,e,b)}else m==="circle"&&(f=a.getAttribute("cx"),g=a.getAttribute("cy"),j=a.getAttribute("r"),k=new br.geom.Circle(f,g,j));return k},G=function(){function a(a){this.points=a}a.prototype.clipToBBox=function(b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;l=this,c=(new br.geom.clipping.CohenSutherland).clip,k=[],f=[],e=!1;for(d=q=0,r=l.points.length-2;0<=r?q<=r:q>=r;d=0<=r?++q:--q){s=l.points[d],g=s[0],h=s[1],t=l.points[d+1],i=t[0],j=t[1];try{u=c(b,g,h,i,j),m=u[0],o=u[1],n=u[2],p=u[3],e=!0,k.push([m,o]),(i!==n||j!==o||d===len(l.points)-2)&&k.push([n,p])}catch(v){e&&k.length>1&&(f.push(new a(k)),k=[]),e=!1}}k.length>1&&f.push(new a(k));return f},a.prototype.toSVG=function(){var a,b,c,d,e,f,g,h;b=this,a=[],g=b.points;for(e=0,f=g.length;e<f;e++)h=g[e],c=h[0],d=h[1],a.push(c+","+d);return"M"+a.join("L")};return a}(),br.geom.Line=G,bC=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q;h=Math.PI,d=Math.atan2,k=h*2,g=a.length,c=0;for(f=p=0,q=g-1;0<=q?p<=q:p>=q;f=0<=q?++p:--p){l=a[f][0]-b[0],n=a[f][1]-b[1],m=a[(f+1)%g][0]-b[0],o=a[(f+1)%g][1]-b[1],i=d(n,l),j=d(o,m),e=j-i;while(e>h)e-=k;while(e<-h)e+=k;c+=e}return Math.abs(c)>=h},bD=br.proj={},Function.prototype.bind=function(a){var b;b=this;return function(){return b.apply(a,arguments)}},V=function(){function a(a){var b,c,d;b=this,b.lon0=(c=a.lon0)!=null?c:0,b.lat0=(d=a.lat0)!=null?d:0,b.PI=Math.PI,b.HALFPI=b.PI*.5,b.QUARTERPI=b.PI*.25,b.RAD=b.PI/180,b.DEG=180/b.PI,b.lam0=b.rad(this.lon0),b.phi0=b.rad(this.lat0),b.minLat=-90,b.maxLat=90}a.parameters=[],a.title="Projection",a.prototype.rad=function(a){return a*this.RAD},a.prototype.deg=function(a){return a*this.DEG},a.prototype.plot=function(a,b){var c,d,e,f,g,h,i,j,k,l,m;b==null&&(b=!0),f=[],c=!0;for(j=0,k=a.length;j<k;j++)l=a[j],e=l[0],d=l[1],g=this._visible(e,d),g&&(c=!1),m=this.project(e,d),h=m[0],i=m[1],!g&&b?f.push(this._truncate(h,i)):f.push([h,i]);return c?null:[f]},a.prototype.sea=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n;f=this,e=f.project.bind(this),d=[],a=f.lon0,f.lon0=0;for(c=g=-180;g<=180;c=++g)d.push(e(c,f.maxLat));for(b=h=k=f.maxLat,l=f.minLat;k<=l?h<=l:h>=l;b=k<=l?++h:--h)d.push(e(180,b));for(c=i=180;i>=-180;c=--i)d.push(e(c,f.minLat));for(b=j=m=f.minLat,n=f.maxLat;m<=n?j<=n:j>=n;b=m<=n?++j:--j)d.push(e(-180,b));f.lon0=a;return d},a.prototype.world_bbox=function(){var a,b,c,d,e,f;b=this.project.bind(this),d=this.sea(),a=new br.BBox;for(e=0,f=d.length;e<f;e++)c=d[e],a.update(c[0],c[1]);return a},a.prototype.toString=function(){var a;a=this;return"[Proj: "+a.name+"]"};return a}(),V.fromXML=function(a){var b,c,d,e,f,g,h;d=a.getAttribute("id"),e={};for(c=g=0,h=a.attributes.length-1;0<=h?g<=h:g>=h;c=0<=h?++g:--g)b=a.attributes[c],b.name!=="id"&&(e[b.name]=b.value);f=new br.proj[d](e),f.name=d;return f},br.Proj=V,o=function(a){function b(a){var c,d,e;a==null&&(a={}),c=this,c.flip=Number((d=a.flip)!=null?d:0),c.flip===1&&(a.lon0=(e=-a.lon0)!=null?e:0),b.__super__.constructor.call(this,a)}bO(b,a),b.parameters=["lon0","flip"],b.title="Cylindrical Projection",b.prototype._visible=function(a,b){return!0},b.prototype.clon=function(a){a-=this.lon0,a<-180?a+=360:a>180&&(a-=360);return a},b.prototype.ll=function(a,b){return this.flip===1?[-a,-b]:[a,b]};return b}(V),r=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.title="Equirectangular Projection",b.prototype.project=function(a,b){var c;c=this.ll(a,b),a=c[0],b=c[1],a=this.clon(a);return[a*Math.cos(this.phi0)*1e3,b*-1*1e3]};return b}(o),bD.lonlat=r,i=function(a){function b(a){var c;b.__super__.constructor.call(this,a),this.lat1=(c=a.lat1)!=null?c:0,this.phi1=this.rad(this.lat1)}bO(b,a),b.parameters=["lon0","lat1","flip"],b.title="Cylindrical Equal Area",b.prototype.project=function(a,b){var c,d,e,f,g;g=this.ll(a,b),a=g[0],b=g[1],c=this.rad(this.clon(a)),d=this.rad(b*-1),e=c*Math.cos(this.phi1),f=Math.sin(d)/Math.cos(this.phi1);return[e*1e3,f*1e3]};return b}(o),bD.cea=i,u=function(a){function b(a){a.lat1=45,b.__super__.constructor.call(this,a)}bO(b,a),b.title="Gall-Peters Projection",b.parameters=["lon0","flip"];return b}(i),bD.gallpeters=u,y=function(a){function b(a){a.lat1=37.7,b.__super__.constructor.call(this,a)}bO(b,a),b.title="Hobo-Dyer Projection",b.parameters=["lon0","flip"];return b}(i),bD.hobodyer=y,f=function(a){function b(a){a.lat1=30,b.__super__.constructor.call(this,a)}bO(b,a),b.title="Behrmann Projection",b.parameters=["lon0","flip"];return b}(i),bD.behrmann=f,e=function(a){function b(a){a.lat1=50,b.__super__.constructor.call(this,a)}bO(b,a),b.title="Balthasart Projection",b.parameters=["lon0","flip"];return b}(i),bD.balthasart=e,N=function(a){function b(a){b.__super__.constructor.call(this,a),this.minLat=-85,this.maxLat=85}bO(b,a),b.title="Mercator Projection",b.prototype.project=function(a,b){var c,d,e,f,g,h,i;f=this,i=f.ll(a,b),a=i[0],b=i[1],d=Math,c=f.rad(f.clon(a)),e=f.rad(b*-1),g=c*1e3,h=d.log((1+d.sin(e))/d.cos(e))*1e3;return[g,h]};return b}(o),bD.mercator=N,X=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.title="Pseudo-Cylindrical Projection";return b}(o),P=function(a){function b(a){var c;b.__super__.constructor.call(this,a),c=this,c.A0=.8707,c.A1=-0.131979,c.A2=-0.013791,c.A3=.003971,c.A4=-0.001529,c.B0=1.007226,c.B1=.015085,c.B2=-0.044475,c.B3=.028874,c.B4=-0.005916,c.C0=c.B0,c.C1=3*c.B1,c.C2=7*c.B2,c.C3=9*c.B3,c.C4=11*c.B4,c.EPS=1e-11,c.MAX_Y=.8707*.52*Math.PI;return}bO(b,a),b.title="Natural Earth Projection",b.prototype.project=function(a,b){var c,d,e,f,g,h,i,j;g=this,j=g.ll(a,b),a=j[0],b=j[1],c=g.rad(g.clon(a)),d=g.rad(b*-1),e=d*d,f=e*e,h=c*(g.A0+e*(g.A1+e*(g.A2+f*e*(g.A3+e*g.A4))))*180+500,i=d*(g.B0+e*(g.B1+f*(g.B2+g.B3*e+g.B4*f)))*180+270;return[h,i]};return b}(X),bD.naturalearth=P,bb=function(a){function b(a){var c;b.__super__.constructor.call(this,a),c=this,c.X=[1,-5.67239e-12,-0.0000715511,311028e-11,.9986,-0.000482241,-0.000024897,-0.00000133094,.9954,-0.000831031,-0.000044861,-9.86588e-7,.99,-0.00135363,-0.0000596598,367749e-11,.9822,-0.00167442,-0.0000044975,-0.00000572394,.973,-0.00214869,-0.0000903565,1.88767e-8,.96,-0.00305084,-0.0000900732,164869e-11,.9427,-0.00382792,-0.0000653428,-0.00000261493,.9216,-0.00467747,-0.000104566,48122e-10,.8962,-0.00536222,-0.0000323834,-0.00000543445,.8679,-0.00609364,-0.0001139,332521e-11,.835,-0.00698325,-0.0000640219,9.34582e-7,.7986,-0.00755337,-0.0000500038,9.35532e-7,.7597,-0.00798325,-0.0000359716,-0.00000227604,.7186,-0.00851366,-0.000070112,-0.00000863072,.6732,-0.00986209,-0.000199572,191978e-10,.6213,-0.010418,883948e-10,624031e-11,.5722,-0.00906601,181999e-9,624033e-11,.5322,0,0,0],c.Y=[0,.0124,3.72529e-10,1.15484e-9,.062,.0124001,1.76951e-8,-5.92321e-9,.124,.0123998,-7.09668e-8,2.25753e-8,.186,.0124008,2.66917e-7,-8.44523e-8,.248,.0123971,-9.99682e-7,3.15569e-7,.31,.0124108,373349e-11,-0.0000011779,.372,.0123598,-0.000013935,439588e-11,.434,.0125501,520034e-10,-0.0000100051,.4968,.0123198,-0.0000980735,922397e-11,.5571,.0120308,402857e-10,-0.0000052901,.6176,.0120369,-0.0000390662,7.36117e-7,.6769,.0117015,-0.0000280246,-8.54283e-7,.7346,.0113572,-0.0000408389,-5.18524e-7,.7903,.0109099,-0.0000486169,-0.0000010718,.8435,.0103433,-0.0000646934,5.36384e-9,.8936,.00969679,-0.0000646129,-0.00000854894,.9394,.00840949,-0.000192847,-0.00000421023,.9761,.00616525,-0.000256001,-0.00000421021,1,0,0,0],c.NODES=18,c.FXC=.8487,c.FYC=1.3523,c.C1=11.459155902616464,c.RC1=.08726646259971647,c.ONEEPS=1.000001,c.EPS=1e-8;return}bO(b,a),b.title="Robinson Projection",b.prototype._poly=function(a,b,c){return a[b]+c*(a[b+1]+c*(a[b+2]+c*a[b+3]))},b.prototype.project=function(a,b){var c,d,e,f,g,h,i,j;g=this,j=g.ll(a,b),a=j[0],b=j[1],a=g.clon(a),d=g.rad(a),e=g.rad(b*-1),f=Math.abs(e),c=Math.floor(f*g.C1),c>=g.NODES&&(c=g.NODES-1),f=g.deg(f-g.RC1*c),c*=4,h=1e3*g._poly(g.X,c,f)*g.FXC*d,i=1e3*g._poly(g.Y,c,f)*g.FYC,e<0&&(i=-i);return[h,i]};return b}(X),bD.robinson=bb,p=function(a){function b(a){var c;b.__super__.constructor.call(this,a),c=this,c.C_x=.4222382003157712,c.C_y=1.3265004281770023,c.RC_y=.7538633073600218,c.C_p=3.5707963267948966,c.RC_p=.2800495767557787,c.EPS=1e-7,c.NITER=6}bO(b,a),b.title="Eckert IV Projection",b.prototype.project=function(a,b){var c,d,e,f,g,h,i,j,k,l,m;h=this,m=h.ll(a,b),a=m[0],b=m[1],f=h.rad(h.clon(a)),g=h.rad(b*-1),i=h.C_p*Math.sin(g),c=g*g,g*=.895168+c*(.0218849+c*.00826809),e=h.NITER;while(e>0){d=Math.cos(g),j=Math.sin(g),c=(g+j*(d+2)-i)/(1+d*(d+2)-j*j),g-=c;if(Math.abs(c)<h.EPS)break;e-=1}e===0?(k=h.C_x*f,l=g<0?-h.C_y:h.C_y):(k=h.C_x*f*(1+Math.cos(g)),l=h.C_y*Math.sin(g));return[k,l]};return b}(X),bD.eckert4=p,be=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.title="Sinusoidal Projection",b.prototype.project=function(a,b){var c,d,e,f,g,h;d=this,h=d.ll(a,b),a=h[0],b=h[1],c=d.rad(d.clon(a)),e=d.rad(b*-1),f=1032*c*Math.cos(e),g=1032*e;return[f,g]};return b}(X),bD.sinusoidal=be,O=function(a){function b(a,c,d,e,f){var g,h,i,j;c==null&&(c=1.5707963267948966),d==null&&(d=null),e==null&&(e=null),f==null&&(f=null),b.__super__.constructor.call(this,a),g=this,g.MAX_ITER=10,g.TOLERANCE=1e-7,c!=null?(h=c+c,j=Math.sin(c),i=Math.sqrt(Math.PI*2*j/(h+Math.sin(h))),g.cx=2*i/Math.PI,g.cy=i/j,g.cp=h+Math.sin(h)):d!=null&&e!=null&&typeof cz!="undefined"&&cz!==null?(g.cx=d,g.cy=e,g.cp=f):bB("kartograph.proj.Mollweide: either p or cx,cy,cp must be defined")}bO(b,a),b.title="Mollweide Projection",b.prototype.project=function(a,b){var c,d,e,f,g,h,i,j,k,l,m;h=this,m=h.ll(a,b),a=m[0],b=m[1],g=Math,c=g.abs,f=h.rad(h.clon(a)),i=h.rad(b),e=h.cp*g.sin(i),d=h.MAX_ITER;while(d!==0){j=(i+g.sin(i)-e)/(1+g.cos(i)),i-=j;if(c(j)<h.TOLERANCE)break;d-=1}d===0?i=i>=0?h.HALFPI:-h.HALFPI:i*=.5,k=1e3*h.cx*f*g.cos(i),l=1e3*h.cy*g.sin(i);return[k,l*-1]};return b}(X),bD.mollweide=O,bm=function(a){function b(a){b.__super__.constructor.call(this,a,1.0471975511965976)}bO(b,a),b.title="Wagner IV Projection";return b}(O),bD.wagner4=bm,bn=function(a){function b(a){b.__super__.constructor.call(this,a,null,.90977,1.65014,3.00896)}bO(b,a),b.title="Wagner V Projection";return b}(O),bD.wagner5=bn,K=function(a){function d(){return d.__super__.constructor.apply(this,arguments)}var b,c;bO(d,a),c=-89,b=89,d.parameters=["lon0","lat0","flip"],d.title="Loximuthal Projection (equidistant)",d.prototype.project=function(a,b){var c,d,e,f,g,h,i;e=this,i=e.ll(a,b),a=i[0],b=i[1],d=Math,c=e.rad(e.clon(a)),f=e.rad(b),f===e.phi0?g=c*d.cos(e.phi0):g=c*(f-e.phi0)/(d.log(d.tan(e.QUARTERPI+f*.5))-d.log(d.tan(e.QUARTERPI+e.phi0*.5))),g*=1e3,h=1e3*(f-e.phi0);return[g,h*-1]};return d}(X),bD.loximuthal=K,j=function(a){function g(){return g.__super__.constructor.apply(this,arguments)}var b,c,d,e,f;bO(g,a),g.title="Canters Modified Sinusoidal I",g.parameters=["lon0"],b=1.1966,c=-0.129,d=3*c,e=-0.0076,f=5*e,g.prototype.project=function(a,g){var h,i,j,k,l,m;h=this,m=h.ll(a,g),a=m[0],g=m[1],a=h.rad(h.clon(a)),g=h.rad(g),k=g*g,l=k*k,i=1e3*a*Math.cos(g)/(b+d*k+f*l),j=1e3*g*(b+c*k+e*l);return[i,j*-1]};return g}(X),bD.canters1=j,x=function(a){function o(a){o.__super__.constructor.call(this,a)}var b,c,d,e,f,g,h,i,j,k,l,m,n;bO(o,a),o.title="Hatano Projection",h=20,d=1e-7,i=1.000001,b=2.67595,c=2.43763,j=.3736990601468637,k=.4102345310814193,f=1.75859,g=1.93052,m=.5686373742600607,n=.5179951515653813,e=.85,l=1.1764705882352942,o.prototype.project=function(a,i){var j,k,l,m,n,
-o,p,q,r,s;m=this,s=m.ll(a,i),a=s[0],i=s[1],l=m.rad(m.clon(a)),n=m.rad(i),j=Math.sin(n)*(n<0?c:b);for(k=r=h;r>=1;k=r+=-1){o=(n+Math.sin(n)-j)/(1+Math.cos(n)),n-=o;if(Math.abs(o)<d)break}p=1e3*e*l*Math.cos(n*=.5),q=1e3*Math.sin(n)*(n<0?g:f);return[p,q*-1]};return o}(X),bD.hatano=x,w=function(a){function b(a){var c;b.__super__.constructor.call(this,a),c=this,c.lat1=41.737,c.p1=new O,c.p0=new be}bO(b,a),b.title="Goode Homolosine Projection",b.parameters=["lon0"],b.prototype.project=function(a,b){var c,d;c=this,d=c.ll(a,b),a=d[0],b=d[1],a=c.clon(a);return Math.abs(b)>c.lat1?c.p1.project(a,b):c.p0.project(a,b)};return b}(X),bD.goodehomolosine=w,Q=function(a){function c(a){c.__super__.constructor.call(this,a),this.r=this.HALFPI*100}var b;bO(c,a),c.title="Nicolosi Globular Projection",c.parameters=["lon0"],b=1e-10,c.prototype._visible=function(a,b){var c;c=this,a=c.clon(a);return a>-90&&a<90},c.prototype.project=function(a,c){var d,e,f,g,h,i,j,k,l,m,n,o,p;h=this,p=h.ll(a,c),a=p[0],c=p[1],f=h.rad(h.clon(a)),j=h.rad(c),Math.abs(f)<b?(n=0,o=j):Math.abs(j)<b?(n=f,o=0):Math.abs(Math.abs(f)-h.HALFPI)<b?(n=f*Math.cos(j),o=h.HALFPI*Math.sin(j)):Math.abs(Math.abs(j)-h.HALFPI)<b?(n=0,o=j):(m=h.HALFPI/f-f/h.HALFPI,d=j/h.HALFPI,l=Math.sin(j),e=(1-d*d)/(l-d),k=m/e,k*=k,g=(m*l/e-.5*m)/(1+k),i=(l/k+.5*e)/(1+1/k),n=Math.cos(j),n=Math.sqrt(g*g+n*n/(1+k)),n=h.HALFPI*(g+(f<0?-n:n)),o=Math.sqrt(i*i-(l*l/k+e*l-1)/(1+1/k)),o=h.HALFPI*(i+(j<0?o:-o)));return[n*100,o*-100]},c.prototype.sea=function(){var a,b,c,d,e;b=[],d=this.r,a=Math;for(c=e=0;e<=360;c=++e)b.push([a.cos(this.rad(c))*d,a.sin(this.rad(c))*d]);return b},c.prototype.world_bbox=function(){var a;a=this.r;return new br.BBox(-a,-a,a*2,a*2)};return c}(X),bD.nicolosi=Q,c=function(a){function b(a,c){var d;c==null&&(c=1e3),b.__super__.constructor.call(this,a),d=this,d.r=c,d.elevation0=d.to_elevation(d.lat0),d.azimuth0=d.to_azimuth(d.lon0)}bO(b,a),b.parameters=["lon0","lat0"],b.title="Azimuthal Projection",b.prototype.to_elevation=function(a){var b;b=this;return(a+90)/180*b.PI-b.HALFPI},b.prototype.to_azimuth=function(a){var b;b=this;return(a+180)/360*b.PI*2-b.PI},b.prototype._visible=function(a,b){var c,d,e,f,g;g=this,f=Math,e=g.to_elevation(b),c=g.to_azimuth(a),d=f.sin(e)*f.sin(g.elevation0)+f.cos(g.elevation0)*f.cos(e)*f.cos(c-g.azimuth0);return d>=0},b.prototype._truncate=function(a,b){var c,d,e,f,g;c=Math,d=this.r,e=c.atan2(b-d,a-d),f=d+d*c.cos(e),g=d+d*c.sin(e);return[f,g]},b.prototype.sea=function(){var a,b,c,d,e;b=[],d=this.r,a=Math;for(c=e=0;e<=360;c=++e)b.push([d+a.cos(this.rad(c))*d,d+a.sin(this.rad(c))*d]);return b},b.prototype.world_bbox=function(){var a;a=this.r;return new br.BBox(0,0,a*2,a*2)};return b}(V),R=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.title="Orthographic Projection",b.prototype.project=function(a,b){var c,d,e,f,g,h,i,j;f=this,e=Math,d=f.to_elevation(b),c=f.to_azimuth(a),h=f.r*e.cos(d)*e.sin(c-f.azimuth0),j=-f.r*(e.cos(f.elevation0)*e.sin(d)-e.sin(f.elevation0)*e.cos(d)*e.cos(c-f.azimuth0)),g=f.r+h,i=f.r+j;return[g,i]};return b}(c),bD.ortho=R,C=function(a){function b(a){b.__super__.constructor.call(this,a),this.scale=Math.sqrt(2)*.5}bO(b,a),b.title="Lambert Azimuthal Equal-Area Projection",b.prototype.project=function(a,b){var c,d,e,f,g,h,i,j,k,l;g=this.rad(b),e=this.rad(a),f=Math,h=f.sin,c=f.cos,d=f.pow(2/(1+h(this.phi0)*h(g)+c(this.phi0)*c(g)*c(e-this.lam0)),.5),d*=this.scale,j=this.r*d*c(g)*h(e-this.lam0),l=-this.r*d*(c(this.phi0)*h(g)-h(this.phi0)*c(g)*c(e-this.lam0)),i=this.r+j,k=this.r+l;return[i,k]};return b}(c),bD.laea=C,bh=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.title="Stereographic Projection",b.prototype.project=function(a,b){var c,d,e,f,g,h,i,j,k,l,m;h=this.rad(b),f=this.rad(a),g=Math,i=g.sin,c=g.cos,e=.5,d=2*e/(1+i(this.phi0)*i(h)+c(this.phi0)*c(h)*c(f-this.lam0)),k=this.r*d*c(h)*i(f-this.lam0),m=-this.r*d*(c(this.phi0)*i(h)-i(this.phi0)*c(h)*c(f-this.lam0)),j=this.r+k,l=this.r+m;return[j,l]};return b}(c),bD.stereo=bh,bc=function(a){function b(a){var c,d,e,f,g,h,i,j,k,l;b.__super__.constructor.call(this,{lon0:0,lat0:0}),this.dist=(j=a.dist)!=null?j:3,this.up=this.rad((k=a.up)!=null?k:0),this.tilt=this.rad((l=a.tilt)!=null?l:0),this.scale=1,f=Number.MAX_VALUE,e=Number.MAX_VALUE*-1;for(c=h=0;h<=179;c=++h)for(d=i=0;i<=360;d=++i)g=this.project(d-180,c-90),f=Math.min(g[0],f),e=Math.max(g[0],e);this.scale=this.r*2/(e-f),b.__super__.constructor.call(this,a);return}bO(b,a),b.parameters=["lon0","lat0","tilt","dist","up"],b.title="Satellite Projection",b.prototype.project=function(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x;c==null&&(c=0),m=this.rad(b),k=this.rad(a),l=Math,p=l.sin,f=l.cos,n=this.r,o=n*(c+6371)/3671,g=p(this.phi0)*p(m)+f(this.phi0)*f(m)*f(k-this.lam0),j=(this.dist-1)/(this.dist-g),j=(this.dist-1)/(this.dist-g),j*=this.scale,t=o*j*f(m)*p(k-this.lam0),w=-o*j*(f(this.phi0)*p(m)-p(this.phi0)*f(m)*f(k-this.lam0)),i=f(this.up),r=p(this.up),h=f(this.tilt),q=p(this.tilt),e=o*(this.dist-1),d=(w*i+t*r)*p(this.tilt/e)+h,u=(t*i-w*r)*f(this.tilt/d),x=(w*i+t*r)/d,s=n+u,v=n+x;return[s,v]},b.prototype._visible=function(a,b){var c,d,e,f;e=this.to_elevation(b),c=this.to_azimuth(a),f=Math,d=f.sin(e)*f.sin(this.elevation0)+f.cos(this.elevation0)*f.cos(e)*f.cos(c-this.azimuth0);return d>=1/this.dist},b.prototype.sea=function(){var a,b,c,d,e;b=[],d=this.r,a=Math;for(c=e=0;e<=360;c=++e)b.push([d+a.cos(this.rad(c))*d,d+a.sin(this.rad(c))*d]);return b};return b}(c),bD.satellite=bc,q=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.title="Equidistant Azimuthal Projection",b.prototype.project=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o;i=this,j=i.rad(b),g=i.rad(a),h=Math,k=h.sin,d=h.cos,e=k(this.phi0)*k(j)+d(this.phi0)*d(j)*d(g-this.lam0),c=h.acos(e),f=.325*c/k(c),m=this.r*f*d(j)*k(g-this.lam0),o=-this.r*f*(d(this.phi0)*k(j)-k(this.phi0)*d(j)*d(g-this.lam0)),l=this.r+m,n=this.r+o;return[l,n]},b.prototype._visible=function(a,b){return!0};return b}(c),bD.equi=q,b=function(a){function c(a){var b;b=this,a.lat0=0,c.__super__.constructor.call(this,a),b.lam0=0}var b;bO(c,a),c.title="Aitoff Projection",c.parameters=["lon0"],b=.6366197723675814,c.prototype.project=function(a,c){var d,e,f,g,h,i,j,k;g=this,k=g.ll(a,c),a=k[0],c=k[1],a=g.clon(a),f=g.rad(a),h=g.rad(c),d=.5*f,e=Math.acos(Math.cos(h)*Math.cos(d)),e!==0?(j=1/Math.sin(e),i=2*e*Math.cos(h)*Math.sin(d)*j,j*=e*Math.sin(h)):i=j=0,g.winkel&&(i=(i+f*b)*.5,j=(j+h)*.5);return[i*1e3,j*-1e3]},c.prototype._visible=function(a,b){return!0};return c}(X),bD.aitoff=b,bo=function(a){function b(a){b.__super__.constructor.call(this,a),this.winkel=!0}bO(b,a),b.title="Winkel Tripel Projection";return b}(b),bD.winkel3=bo,n=function(a){function b(a){var c,d,e;c=this,b.__super__.constructor.call(this,a),c.lat1=(d=a.lat1)!=null?d:30,c.phi1=c.rad(c.lat1),c.lat2=(e=a.lat2)!=null?e:50,c.phi2=c.rad(c.lat2)}bO(b,a),b.title="Conic Projection",b.parameters=["lon0","lat0","lat1","lat2"],b.prototype._visible=function(a,b){var c;c=this;return b>c.minLat&&b<c.maxLat},b.prototype._truncate=function(a,b){return[a,b]},b.prototype.clon=function(a){a-=this.lon0,a<-180?a+=360:a>180&&(a-=360);return a};return b}(V),D=function(a){function b(a){var c,d,e,f,g,h,i,j,k,l,m,n,o;k=this,b.__super__.constructor.call(this,a),g=Math,o=[g.sin,g.cos,g.abs,g.log,g.tan,g.pow],l=o[0],e=o[1],c=o[2],bs=o[3],n=o[4],i=o[5],k.n=h=m=l(k.phi1),f=e(k.phi1),j=c(k.phi1-k.phi2)>=1e-10,j&&(h=bs(f/e(k.phi2))/bs(n(k.QUARTERPI+.5*k.phi2)/n(k.QUARTERPI+.5*k.phi1))),k.c=d=f*i(n(k.QUARTERPI+.5*k.phi1),h)/h,c(c(k.phi0)-k.HALFPI)<1e-10?k.rho0=0:k.rho0=d*i(n(k.QUARTERPI+.5*k.phi0),-h),k.minLat=-60,k.maxLat=85}bO(b,a),b.title="Lambert Conformal Conic Projection",b.prototype.project=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q;l=this,i=l.rad(b),e=l.rad(l.clon(a)),g=Math,q=[g.sin,g.cos,g.abs,g.log,g.tan,g.pow],m=q[0],d=q[1],c=q[2],bs=q[3],n=q[4],j=q[5],h=l.n,c(c(i)-l.HALFPI)<1e-10?k=0:k=l.c*j(n(l.QUARTERPI+.5*i),-h),f=e*h,o=1e3*k*m(f),p=1e3*(l.rho0-k*d(f));return[o,p*-1]};return b}(n),bD.lcc=D,W=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}bO(b,a);return b}(n),bl=function(){function a(a,b,c,d,e,f){var g;g=this,g.bbox=a,g.width=b,g.padding=d!=null?d:0,g.halign=e!=null?e:"center",g.valign=f!=null?f:"center",g.height=c,g.scale=Math.min((b-d*2)/a.width,(c-d*2)/a.height)}a.prototype.project=function(a,b){var c,d,e,f,g,h,i;b==null&&(b=a[1],a=a[0]),e=this,f=e.scale,c=e.bbox,d=e.height,g=e.width,h=e.halign==="center"?(g-c.width*f)*.5:e.halign==="left"?e.padding*f:g-(c.width-e.padding)*f,i=e.valign==="center"?(d-c.height*f)*.5:e.valign==="top"?e.padding*f:0,a=(a-c.left)*f+h,b=(b-c.top)*f+i;return[a,b]},a.prototype.projectPath=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;e=this;if(a.type==="path"){d=[],b=[99999,99999,-99999,-99999],o=a.contours;for(k=0,m=o.length;k<m;k++){g=o[k],c=[];for(l=0,n=g.length;l<n;l++)p=g[l],i=p[0],j=p[1],q=e.project(i,j),i=q[0],j=q[1],c.push([i,j]),b[0]=Math.min(b[0],i),b[1]=Math.min(b[1],j),b[2]=Math.max(b[2],i),b[3]=Math.max(b[3],j);d.push(c)}f=new br.geom.Path(a.type,d,a.closed),f._bbox=b;return f}if(a.type==="circle"){r=e.project(a.x,a.y),i=r[0],j=r[1],h=a.r*e.scale;return new br.geom.Circle(i,j,h)}},a.prototype.asBBox=function(){var a;a=this;return new br.BBox(0,0,a.width,a.height)};return a}(),bl.fromXML=function(a){var b,c,e,f,g;g=Number(a.getAttribute("w")),e=Number(a.getAttribute("h")),f=Number(a.getAttribute("padding")),c=a.getElementsByTagName("bbox")[0],b=d.fromXML(c);return new br.View(b,g,e,f)},br.View=bl,br.Kartograph.prototype.dotgrid=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X;r=this,q=(T=a.layer)!=null?T:r.layerIds[r.layerIds.length-1];if(!r.layers.hasOwnProperty(q))bB('dotgrid error: layer "'+q+'" not found');else{p=r.layers[q],c=a.data,d=a.value,e=a.key,t={};if(e!=null&&bE(c)==="array")for(B=0,F=c.length;B<F;B++)w=c[B],o=w[e],t[String(o)]=w;else for(o in c)w=c[o],t[String(o)]=w;i=(U=a.style)!=null?U:{fill:"black",stroke:"none"},y=a.size,n=(V=a.gridsize)!=null?V:15,h=(W=p.dotgrid)!=null?W:p.dotgrid={gridsize:n,grid:[]};if(h.gridsize!==n){X=h.grid;for(C=0,G=X.length;C<G;C++)m=X[C],m.shape!=null&&(m.shape.remove(),m.shape=null)}if(n>0){if(h.grid.length===0)for(z=D=0,L=r.viewport.width;0<=L?D<=L:D>=L;z=D+=n)for(A=E=0,M=r.viewport.height;0<=M?E<=M:E>=M;A=E+=n){m={x:z+(Math.random()-.5)*n*.2,y:A+(Math.random()-.5)*n*.2,pathid:!1},l=!1,N=p.pathsById;for(o in N){u=N[o];for(J=0,H=u.length;J<H;J++){s=u[J];if(s.vpath.isInside(m.x,m.y)){l=!0,v=(O=t[o])!=null?O:null,x=y(v),m.pathid=o,m.shape=p.paper.circle(m.x,m.y,1);break}}if(l)break}h.grid.push(m)}P=h.grid;for(K=0,I=P.length;K<I;K++)m=P[K],m.pathid&&(v=(Q=t[m.pathid])!=null?Q:null,x=y(v),k=(R=a.duration)!=null?R:0,f=(S=a.delay)!=null?S:0,bE(f)==="function"?g=f(v):g=f,k>0&&Raphael.svg?(b=Raphael.animation({r:x*.5},k),m.shape.animate(b.delay(g))):m.shape.attr({r:x*.5}),bE(i)==="function"?j=i(v):j=i,m.shape.attr(j))}}},bq=(bM=br.filter)!=null?bM:br.filter={},bq.__knownFilter={},bq.__patternFills=0,L.prototype.SVG=function(a,b){var c,d;typeof a=="string"&&(a=window.document.createElementNS("http://www.w3.org/2000/svg",a));if(b)for(c in b)d=b[c],a.setAttribute(c,d);return a},br.Kartograph.prototype.addFilter=function(a,b,c){var d,e,f;c==null&&(c={}),f=this,d=window.document;if(br.filter[b]!=null)e=(new br.filter[b](c)).getFilter(a);else throw"unknown filter type "+b;return f.paper.defs.appendChild(e)},L.prototype.applyFilter=function(b){var c;c=this;return a("."+c.id,c.paper.canvas).attr({filter:"url(#"+b+")"})},L.prototype.applyTexture=function(a,b,c){var d,e,f,g,h,i;b==null&&(b=!1),c==null&&(c="#000"),e=this,bq.__patternFills+=1,h=e.paths,i=[];for(f=0,g=h.length;f<g;f++)d=h[f],!b||b(d.data)?i.push(d.svgPath.attr({fill:"url("+a+")"})):i.push(d.svgPath.attr("fill",c));return i},t=function(){function a(a){this.params=a!=null?a:{}}a.prototype.getFilter=function(a){var b,c;c=this,b=c.SVG("filter",{id:a}),c.buildFilter(b);return b},a.prototype._getFilter=function(){throw"not implemented"},a.prototype.SVG=function(a,b){var c,d;typeof a=="string"&&(a=window.document.createElementNS("http://www.w3.org/2000/svg",a));if(b)for(c in b)d=b[c],a.setAttribute(c,d);return a};return a}(),g=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.prototype.buildFilter=function(a){var b,c,d;d=this,b=d.SVG,c=b("feGaussianBlur",{stdDeviation:d.params.size||4,result:"blur"});return a.appendChild(c)};return b}(t),bq.blur=g,v=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.prototype.buildFilter=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o;g=this,c=(l=g.params.blur)!=null?l:4,i=(m=g.params.strength)!=null?m:1,d=(n=g.params.color)!=null?n:"#D1BEB0",typeof d=="string"&&(d=chroma.hex(d)),h=d.rgb,e=(o=g.params.inner)!=null?o:!1,f=(j=g.params.knockout)!=null?j:!1,b=(k=g.params.alpha)!=null?k:1,e?g.innerGlow(a,c,i,h,b,f):g.outerGlow(a,c,i,h,b,f)},b.prototype.outerGlow=function(a,b,c,d,e,f){var g,h,i,j,k,l,m;k=this,g=k.SVG,j=g("feColorMatrix",{"in":"SourceGraphic",type:"matrix",values:"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0",result:"mask"}),a.appendChild(j),c>0&&(m=g("feMorphology",{"in":"mask",radius:c,operator:"dilate",result:"mask"}),a.appendChild(m)),j=g("feColorMatrix",{"in":"mask",type:"matrix",values:"0 0 0 0 "+d[0]/255+" 0 0 0 0 "+d[1]/255+" 0 0 0 0 "+d[2]/255+" 0 0 0 1 0",result:"r0"}),a.appendChild(j),h=g("feGaussianBlur",{"in":"r0",stdDeviation:b,result:"r1"}),a.appendChild(h),i=g("feComposite",{operator:"out","in":"r1",in2:"mask",result:"comp"}),a.appendChild(i),l=g("feMerge"),f||l.appendChild(g("feMergeNode",{"in":"SourceGraphic"})),l.appendChild(g("feMergeNode",{"in":"r1"}));return a.appendChild(l)},b.prototype.innerGlow=function(a,b,c,d,e,f){var g,h,i,j,k,l,m;k=this,g=k.SVG,bs("innerglow"),j=g("feColorMatrix",{"in":"SourceGraphic",type:"matrix",values:"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 0",result:"mask"}),a.appendChild(j),m=g("feMorphology",{"in":"mask",radius:c,operator:"erode",result:"r1"}),a.appendChild(m),h=g("feGaussianBlur",{"in":"r1",stdDeviation:b,result:"r2"}),a.appendChild(h),j=g("feColorMatrix",{type:"matrix","in":"r2",values:"1 0 0 0 "+d[0]/255+" 0 1 0 0 "+d[1]/255+" 0 0 1 0 "+d[2]/255+" 0 0 0 -1 1",result:"r3"}),a.appendChild(j),i=g("feComposite",{operator:"in","in":"r3",in2:"mask",result:"comp"}),a.appendChild(i),l=g("feMerge"),f||l.appendChild(g("feMergeNode",{"in":"SourceGraphic"})),l.appendChild(g("feMergeNode",{"in":"comp"}));return a.appendChild(l)};return b}(t),bq.glow=v,br.Kartograph.prototype.addGeoPath=function(a,b,c){var d,e,f;b==null&&(b=[]),c==null&&(c=""),d=this,f=d.getGeoPathStr(a,b),e=d.paper.path(f),c!==""&&e.node.setAttribute("class",c);return e},br.Kartograph.prototype.getGeoPathStr=function(a,b){var c,d,e,f,g,h,i;b==null&&(b=[]),e=this,type(b)==="string"&&(b=b.split("")),b.length===0&&b.push("M"),f="";for(d in a){g=a[d],c=(i=b[d])!=null?i:"L",h=e.lonlat2xy(g);if(isNaN(h[0])||isNaN(h[1]))continue;f+=c+h[0]+","+h[1]}return f},br.Kartograph.prototype.addGeoPolygon=function(a,b){var c,d,e;e=this,c=["M"];for(d in a)c.push("L");c.push("Z");return e.addGeoPath(a,c,b)},S=function(){function b(b){this.zoomOut=bP(this.zoomOut,this),this.zoomIn=bP(this.zoomIn,this);var c,d,e,f,g,h,i,j;f=this,f.map=b,c=b.container,d=function(b,c){var d,e,f,g;c==null&&(c=[]),e=a('<div class="'+b+'" />');for(f=0,g=c.length;f<g;f++)d=c[f],e.append(d);return e},e=function(b){return a(b.target).addClass("md")},g=function(b){return a(b.target).removeClass("md")},j=d("plus"),j.mousedown(e),j.mouseup(g),j.click(f.zoomIn),i=d("minus"),i.mousedown(e),i.mouseup(g),i.click(f.zoomOut),h=d("zoom-control",[j,i]),c.append(h)}b.prototype.zoomIn=function(a){var b;b=this,b.map.opts.zoom+=1;return b.map.resize()},b.prototype.zoomOut=function(a){var b;b=this,b.map.opts.zoom-=1,b.map.opts.zoom<1&&(b.map.opts.zoom=1);return b.map.resize()};return b}(),bd=function(){function a(a,b,c){var d,e,f,g,h=this;a==null&&(a=[0,1]),b==null&&(b=null),c==null&&(c=null),this.rangedScale=bP(this.rangedScale,this),this.scale=bP(this.scale,this),e=this,g=[];for(d in a){if(bE(c)==="function"&&c(a[d])===!1)continue;b!=null?bE(b)==="function"?f=b(a[d]):f=a[d][b]:f=a[d],isNaN(f)||g.push(f)}g=g.sort(function(a,b){return a-b}),e.values=g,e._range=[0,1],e.rangedScale.range=function(a){e._range=a;return e.rangedScale}}a.prototype.scale=function(a){return a},a.prototype.rangedScale=function(a){var b,c;b=this,a=b.scale(a),c=b._range;return a*(c[1]-c[0])+c[0]};return a}(),H=function(a){function b(){this.scale=bP(this.scale,this);return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.prototype.scale=function(a){var b,c;b=this,c=b.values;return(a-c[0])/(c[c.length-1]-c[0])};return b}(bd),I=function(a){function b(){this.scale=bP(this.scale,this);return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.prototype.scale=function(a){var b,c;b=this,c=b.values,bs=Math.log;return(bs(a)-bs(c[0]))/(bs(c[c.length-1])-bs(c[0]))};return b}(bd),bf=function(a){function b(){this.scale=bP(this.scale,this);return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.prototype.scale=function(a){var b,c;b=this,c=b.values;return Math.sqrt((a-c[0])/(c[c.length-1]-c[0]))};return b}(bd),Y=function(a){function b(){this.scale=bP(this.scale,this);return b.__super__.constructor.apply(this,arguments)}bO(b,a),b.prototype.scale=function(a){var b,c,d,e,f,g;d=this,g=d.values,c=g.length-1;for(b in g){f=g[Number(b)],e=g[Number(b)+1];if(a===f)return b/c;if(b<c&&a>f&&a<e)return b/c+(a-f)/(e-f)}};return b}(bd),br.scale={},br.scale.identity=function(a){return(new bd(domain,prop,bq)).rangedScale},br.scale.linear=function(a,b,c){return(new H(a,b,c)).rangedScale},br.scale.log=function(a,b,c){return(new I(a,b,c)).rangedScale},br.scale.sqrt=function(a,b,c){return(new bf(a,b,c)).rangedScale},br.scale.quantile=function(a,b,c){return(new Y(a,b,c)).rangedScale},bj=function(){function b(b){a=this,a.location=b.location,a.data=b.data,a.map=b.map,a.layers=b.layers,a.key=b.key,a.x=b.x,a.y=b.y}var a;a=null,b.prototype.init=function(){return a},b.prototype.overlaps=function(a){return!1},b.prototype.update=function(b){return a},b.prototype.nodes=function(){return[]},b.prototype.clear=function(){return a};return b}(),br.Symbol=bj,bk=function(){function c(a){this._initTooltips=bP(this._initTooltips,this),this._noverlap=bP(this._noverlap,this),this._kMeans=bP(this._kMeans,this);var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w;b=this,m=["data","location","type","map"],k=["filter","tooltip","click","delay","sortBy","clustering","aggregate","clusteringOpts","mouseenter","mouseleave"];for(n=0,r=m.length;n<r;n++){l=m[n];if(a[l]!=null)b[l]=a[l];else throw"SymbolGroup: missing argument '"+l+"'"}for(o=0,s=k.length;o<s;o++)l=k[o],a[l]!=null&&(b[l]=a[l]);d=b.type;if(d==null)bB("could not resolve symbol type",b.type);else{v=d.props;for(p=0,t=v.length;p<t;p++)l=v[p],a[l]!=null&&(b[l]=a[l]);b.layers={mapcanvas:b.map.paper},w=d.layers;for(q=0,u=w.length;q<u;q++)h=w[q],j=c._layerid++,g="sl_"+j,h.type==="svg"?i=b.map.createSVGLayer(g):h.type==="html"&&(i=b.map.createHTMLLayer(g)),b.layers[h.id]=i;b.symbols=[];for(f in b.data)e=b.data[f],bE(b.filter)==="function"?b.filter(e,f)&&b.add(e,f):b.add(e,f);b.layout(),b.render(),b.map.addSymbolGroup(b)}}var b;b=null,c.prototype.add=function(a,c){var d,e,f,g,h,i,j,k;b=this,d=b.type,e=b._evaluate(b.location,a,c),bE(e)==="array"&&(e=new br.LonLat(e[0],e[1])),g={layers:b.layers,location:e,data:a,key:c!=null?c:b.symbols.length,map:b.map},k=d.props;for(i=0,j=k.length;i<j;i++)f=k[i],b[f]!=null&&(g[f]=b._evaluate(b[f],a,c));h=new d(g),b.symbols.push(h);return h},c.prototype.layout=function(){var a,c,d,e,f,g,h,i,j,k;j=b.symbols;for(h=0,i=j.length;h<i;h++){f=j[h],c=f.location;if(bE(c)==="string"){k=c.split("."),a=k[0],e=k[1],d=b.map.getLayerPath(a,e);if(d!=null)g=b.map.viewBC.project(d.path.centroid());else{bB("could not find layer path "+a+"."+e);continue}}else g=b.map.lonlat2xy(c);f.x=g[0],f.y=g[1]}b.clustering==="k-means"?b._kMeans():b.clustering==="noverlap"&&b._noverlap();return b},c.prototype.render=function(){var c,d,e,f,g,h,i,j,k,l,m;b=this,b.sortBy&&(f="asc",bE(b.sortBy)==="string"&&(b.sortBy=b.sortBy.split(" ",2),e=b.sortBy[0],f=(k=b.sortBy[1])!=null?k:"asc"),b.symbols=b.symbols.sort(function(a,c){var d,g,h;bE(b.sortBy)==="function"?(g=b.sortBy(a.data,a),h=b.sortBy(c.data,c)):(g=a[e],h=c[e]);if(g===h)return 0;d=f==="asc"?1:-1;return g>h?1*d:-1*d})),l=b.symbols;for(g=0,i=l.length;g<i;g++){d=l[g],d.render(),m=d.nodes();for(h=0,j=m.length;h<j;h++)c=m[h],c.symbol=d}bE(b.tooltip)==="function"&&b._initTooltips(),a.each(["click","mouseenter","mouseleave"],function(e,f){var g,h,i,j;if(bE(b[f])==="function"){i=b.symbols,j=[];for(g=0,h=i.length;g<h;g++)d=i[g],j.push(function(){var e,g,h,i,j=this;h=d.nodes(),i=[];for(e=0,g=h.length;e<g;e++)c=h[e],i.push(a(c)[f](function(c){var d;d=c.target;while(!d.symbol)d=a(d).parent().get(0);c.stopPropagation();return b[f](d.symbol.data,d.symbol,c)}));return i}.call(this));return j}});return b},c.prototype.tooltips=function(a){b=this,b.tooltips=a,b._initTooltips();return b},c.prototype.remove=function(a){var c,d,e,f,g,h,i,j,k;b=this,d=[],i=b.symbols;for(g=0,h=i.length;g<h;g++){f=i[g];if(a!=null&&!a(f.data)){d.push(f);continue}try{f.clear()}catch(l){bB("error: symbolgroup.remove")}}if(a==null){j=b.layers,k=[];for(c in j)e=j[c],c!=="mapcanvas"?k.push(e.remove()):k.push(void 0);return k}return b.symbols=d},c.prototype._evaluate=function(a,b,c){var d;return bE(a)==="function"?d=a(b,c):d=a},c.prototype._kMeans=function(){var a,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x;b=this,(u=b.osymbols)==null&&(b.osymbols=b.symbols),a=b.type,b.clusteringOpts!=null&&(k=b.clusteringOpts.size),k==null&&(k=64),c=bR().iterations(16).size(k),v=b.osymbols;for(m=0,q=v.length;m<q;m++)j=v[m],c.add({x:j.x,y:j.y});g=c.means(),h=[];for(n=0,r=g.length;n<r;n++){f=g[n];if(f.size===0)continue;d=[],w=f.indices;for(o=0,s=w.length;o<s;o++)e=w[o],d.push(b.osymbols[e].data);d=b.aggregate(d),l={layers:b.layers,location:!1,data:d,map:b.map},x=a.props;for(p=0,t=x.length;p<t;p++)i=x[p],b[i]!=null&&(l[i]=b._evaluate(b[i],d));j=new a(l),j.x=f.x,j.y=f.y,h.push(j)}return b.symbols=h},c.prototype._noverlap=function(){var a,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V;b=this,(S=b.osymbols)==null&&(b.osymbols=b.symbols),j=3,a=b.type;if(bQ.call(a.props,"radius")<0)bB('noverlap layout only available for symbols with property "radius"');else{A=b.osymbols.slice(),b.clusteringOpts!=null&&(D=b.clusteringOpts.tolerance,n=b.clusteringOpts.maxRatio),D==null&&(D=.05),n==null&&(n=.8);for(h=H=0,T=j-1;0<=T?H<=T:H>=T;h=0<=T?++H:--H){A.sort(function(a,b){return b.radius-a.radius}),k=A.length,o=[];for(p=I=0,U=k-3;0<=U?I<=U:I>=U;p=0<=U?++I:--I){x=A[p];if(!x)continue;u=x.radius*(1-D),l=x.x-u,s=x.x+u,B=x.y-u,c=x.y+u,i=[];for(q=J=V=p+1,Q=k-2;V<=Q?J<=Q:J>=Q;q=V<=Q?++J:--J){y=A[q];if(!y)continue;v=y.radius,m=y.x-v,t=y.x+v,C=y.y-v,d=y.y+v,v/x.radius<n&&!(s<m||t<l)&&!(c<C||d<B)&&(f=y.x-x.x,g=y.y-x.y,f*f+g*g<(u+v)*(u+v)&&i.push(q))}if(i.length>0){e=[x.data],r=x.radius*x.radius;for(K=0,L=i.length;K<L;K++)h=i[K],e.push(A[h].data),r+=A[h].radius*A[h].radius;e=b.aggregate(e),z={layers:b.layers,location:!1,data:e,map:b.map},R=a.props;for(O=0,M=R.length;O<M;O++)p=R[O],b[p]!=null&&(z[p]=b._evaluate(b[p],e));w=new a(z),E=x.radius*x.radius/r,F=x.x*E,G=x.y*E;for(P=0,N=i.length;P<N;P++)h=i[P],y=A[h],E=y.radius*y.radius/r,F+=y.x*E,G+=y.y*E,A[h]=void 0;w.x=F,w.y=G,A[p]=void 0,o.push(w)}else o.push(x)}A=o}return b.symbols=A}},c.prototype._initTooltips=function(){var c,d,e,f,g,h,i,j,k,l,m;b=this,f=b.tooltip,l=b.symbols;for(h=0,j=l.length;h<j;h++){e=l[h],c={position:{target:"mouse",viewport:a(window),adjust:{x:7,y:7}},show:{delay:20},content:{},events:{show:function(b,c){return a(".qtip").filter(function(){return this!==c.elements.tooltip.get(0)}).hide()}}},g=f(e.data,e.key),bE(g)==="string"?c.content.text=g:bE(g)==="array"&&(c.content.title=g[0],c.content.text=g[1]),m=e.nodes();for(i=0,k=m.length;i<k;i++)d=m[i],a(d).qtip(c)}},c.prototype.onResize=function(){var a,c,d,e;b=this,b.layout(),e=b.symbols;for(c=0,d=e.length;c<d;c++)a=e[c],a.update()},c.prototype.update=function(a,c,d){var e,f,g,h,i,j,k,l;b=this,a==null&&(a={}),k=b.symbols;for(g=0,i=k.length;g<i;g++){f=k[g],l=b.type.props;for(h=0,j=l.length;h<j;h++)e=l[h],a[e]!=null?f[e]=b._evaluate(a[e],f.data):b[e]!=null&&(f[e]=b._evaluate(b[e],f.data));f.update(c,d)}return b};return c}(),bk._layerid=0,br.SymbolGroup=bk,br.Kartograph.prototype.addSymbols=function(a){a.map=this;return new bk(a)},br.dorlingLayout=function(b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;c==null&&(c=40),n=[],a.each(b.symbols,function(a,b){return n.push({i:a,x:b.path.attrs.cx,y:b.path.attrs.cy,r:b.path.attrs.r})}),n.sort(function(a,b){return b.r-a.r}),f=function(){var a,c,d;for(c=0,d=n.length;c<d;c++)a=n[c],b.symbols[a.i].path.attr({cx:a.x,cy:a.y})};for(o=r=1;1<=c?r<=c:r>=c;o=1<=c?++r:--r)for(l in n)for(m in n)if(m>l){d=n[l],e=n[m];if(d.x+d.r<e.x-e.r||d.x-d.r>e.x+e.r)continue;if(d.y+d.r<e.y-e.r||d.y-d.r>e.y+e.r)continue;i=d.x-e.x,j=d.y-e.y,h=i*i+j*j,p=d.r+e.r,q=p*p,h<q&&(g=Math.sqrt(h),k=10/g,d.x+=i*k*(1-d.r/p),d.y+=j*k*(1-d.r/p),e.x-=i*k*(1-e.r/p),e.y-=j*k*(1-e.r/p))}return f()},h=function(b){function c(a){this.nodes=bP(this.nodes,this),this.clear=bP(this.clear,this),this.update=bP(this.update,this),this.render=bP(this.render,this),this.overlaps=bP(this.overlaps,this);var b,d,e;b=this,c.__super__.constructor.call(this,a),b.radius=(d=a.radius)!=null?d:4,b.style=a.style,b.attrs=a.attrs,b.title=a.title,b["class"]=(e=a["class"])!=null?e:"bubble"}bO(c,b),c.prototype.overlaps=function(a){var b,c,d,e,f,g,h,i,j,k,l;d=this,k=[d.x,d.y,d.radius],g=k[0],i=k[1],e=k[2],l=[a.x,a.y,a.radius],h=l[0],j=l[1],f=l[2];if(g-e>h+f||g+e<h-f||i-e>j+f||i+e<j-f)return!1;b=g-h,c=i-j;return b*b+c*c>(e+f)*(e+f)?!1:!0},c.prototype.render=function(a){var b;b=this,b.path==null&&(b.path=b.layers.mapcanvas.circle(b.x,b.y,b.radius)),b.update(),b.map.applyCSS(b.path);return b},c.prototype.update=function(b,c){var d,e,f;b==null&&(b=!1),c==null&&(c="expo-out"),e=this,f=e.path,d={cx:e.x,cy:e.y,r:e.radius},e.attrs!=null&&(d=a.extend(d,e.attrs)),b?f.animate(d,b,c):f.attr(d),f.node!=null&&(e.style!=null&&f.node.setAttribute("style",e.style),e["class"]!=null&&f.node.setAttribute("class",e["class"])),e.title!=null&&f.attr("title",e.title);return e},c.prototype.clear=function(){var a;a=this,a.path.remove();return a},c.prototype.nodes=function(){var a;a=this;return[a.path.node]};return c}(bj),h.props=["radius","style","class","title","attrs"],h.layers=[],br.Bubble=h,A=function(b){function c(a){var b,d,e,f,g,h;b=this,c.__super__.constructor.call(this,a),b.icon=(e=a.icon)!=null?e:"",b.offset=(f=a.offset)!=null?f:[0,0],b.iconsize=(g=a.iconsize)!=null?g:[10,10],b["class"]=(h=a["class"])!=null?h:"",b.title=(d=a.title)!=null?d:""}bO(c,b),c.prototype.render=function(b){var c,d;d=this,c=d.map.container,d.img=a("<img />"),d.img.attr({src:d.icon,title:d.title,alt:d.title,width:d.iconsize[0],height:d.iconsize[1]}),d.img.addClass(d["class"]),d.img.css({position:"absolute","z-index":1e3,cursor:"pointer"}),d.img[0].symbol=d,c.append(d.img);return d.update()},c.prototype.update=function(){var a;a=this;return a.img.css({left:a.x+a.offset[0]+"px",top:a.y+a.offset[1]+"px"})},c.prototype.clear=function(){var a;a=this,a.img.remove();return a},c.prototype.nodes=function(){var a;a=this;return[a.img]};return c}(br.Symbol),A.props=["icon","offset","class","title","iconsize"],A.layers=[],br.Icon=A,bi=function(a){function b(a){var c,d,e,f,g;c=this,b.__super__.constructor.call(this,a),c.text=(d=a.text)!=null?d:"",c.style=(e=a.style)!=null?e:"",c["class"]=(f=a["class"])!=null?f:"",c.offset=(g=a.offset)!=null?g:[0,0]}bO(b,a),b.prototype.render=function(a){var b,c;c=this,c.lbl=b=c.layers.mapcanvas.text(c.x,c.y,c.text),c.update();return c},b.prototype.update=function(){var a;a=this,a.lbl.attr({x:a.x+a.offset[0],y:a.y+a.offset[1]}),a.lbl.node.setAttribute("style",a.style);return a.lbl.node.setAttribute("class",a["class"])},b.prototype.clear=function(){var a;a=this,a.lbl.remove();return a},b.prototype.nodes=function(){var a;a=this;return[a.lbl.node]};return b}(br.Symbol),bi.props=["text","style","class","offset"],bi.layers=[],br.Label=bi,z=function(b){function c(a){var b,d,e,f;b=this,c.__super__.constructor.call(this,a),b.text=(d=a.text)!=null?d:"",b.style=(e=a.style)!=null?e:"",b["class"]=(f=a["class"])!=null?f:""}bO(c,b),c.prototype.render=function(b){var c,d,e;e=this,c=a("<div>"+e.text+"</div>"),c.css({width:"50px",position:"absolute",left:"-25px","text-align":"center"}),e.lbl=d=a('<div class="label" />'),d.append(c),e.layers.lbl.append(d),c.css({height:c.height()+"px",top:c.height()*-0.4+"px"}),e.update();return e},c.prototype.update=function(){var a;a=this;return a.lbl.css({position:"absolute",left:a.x+"px",top:a.y+"px"})},c.prototype.clear=function(){var a;a=this,a.lbl.remove();return a},c.prototype.nodes=function(){var a;a=this;return[a.lbl[0]]};return c}(br.Symbol),z.props=["text","style","class"],z.layers=[{id:"lbl",type:"html"}],br.HtmlLabel=z,E=function(b){function c(a){this.nodes=bP(this.nodes,this),this.clear=bP(this.clear,this),this.update=bP(this.update,this),this.render=bP(this.render,this);var b,d,e;b=this,c.__super__.constructor.call(this,a),b.labelattrs=(d=a.labelattrs)!=null?d:{},b.buffer=a.buffer,b.center=(e=a.center)!=null?e:!0}bO(c,b),c.prototype.render=function(a){var b;b=this,b.title!=null&&String(b.title).trim()!==""&&(b.buffer&&(b.bufferlabel=b.layers.mapcanvas.text(b.x,b.y,b.title)),b.label=b.layers.mapcanvas.text(b.x,b.y,b.title)),c.__super__.render.call(this,a);return b},c.prototype.update=function(b,d){var e,f,g,h,i;b==null&&(b=!1),d==null&&(d="expo-out"),f=this,c.__super__.update.call(this,b,d),f.label!=null&&(g=f.map.viewport,e=a.extend({},f.labelattrs),h=f.x,i=f.y,f.center?i-=0:h>g.width*.5?(e["text-anchor"]="end",h-=f.radius+5):h<g.width*.5&&(e["text-anchor"]="start",h+=f.radius+5),e.x=h,e.y=i,f.buffer&&(f.bufferlabel.attr(e),f.bufferlabel.attr({stroke:"#fff",fill:"#fff","stroke-linejoin":"round","stroke-linecap":"round","stroke-width":6})),f.label.attr(e),f.label.toFront());return f},c.prototype.clear=function(){var a;a=this;return c.__super__.clear.apply(this,arguments)},c.prototype.nodes=function(){var a,b;a=this,b=c.__super__.nodes.apply(this,arguments),a.label&&b.push(a.label.node),a.bufferlabel&&b.push(a.bufferlabel.node);return b};return c}(h),E.props=["radius","style","class","title","labelattrs","buffer","center","attrs"],E.layers=[],br.LabeledBubble=E,U=function(a){function c(a){var d,e,f,g,h,i,j,k,l,m;b=this,c.__super__.constructor.call(this,a),b.radius=(j=a.radius)!=null?j:4,b.styles=(k=a.styles)!=null?k:"",b.colors=(l=a.colors)!=null?l:["#3cc","#c3c","#33c","#cc3"],b.titles=(m=a.titles)!=null?m:["","","","",""],b.values=(e=a.values)!=null?e:[],b.border=(f=a.border)!=null?f:!1,b.borderWidth=(g=a.borderWidth)!=null?g:2,b["class"]=(h=a["class"])!=null?h:"piechart",(i=(d=Raphael.fn).pieChart)==null&&(d.pieChart=bp)}var b;bO(c,a),b=null,c.prototype.overlaps=function(a){var c,d,e,f,g,h,i,j,k,l;k=[b.x,b.y,b.radius],g=k[0],i=k[1],e=k[2],l=[a.x,a.y,a.radius],h=l[0],j=l[1],f=l[2];if(g-e>h+f||g+e<h-f||i-e>j+f||i+e<j-f)return!1;c=g-h,d=i-j;return c*c+d*d>(e+f)*(e+f)?!1:!0},c.prototype.render=function(a){var c;b=this,b.border!=null&&(c=b.layers.mapcanvas.circle(b.x,b.y,b.radius+b.borderWidth).attr({stroke:"none",fill:b.border})),b.chart=b.layers.mapcanvas.pieChart(b.x,b.y,b.radius,b.values,b.titles,b.colors,"none"),b.chart.push(c);return b},c.prototype.update=function(a){var c;return},c.prototype.clear=function(){var a,c,d,e;b=this,e=b.chart;for(c=0,d=e.length;c<d;c++)a=e[c],a.remove();return b},c.prototype.nodes=function(){var a,c,d,e,f;e=b.chart,f=[];for(c=0,d=e.length;c<d;c++)a=e[c],f.push(a.node);return f};return c}(bj),U.props=["radius","values","styles","class","titles","colors","border","borderWidth"],U.layers=[],br.PieChart=U,bp=function(a,b,c,d,e,f,g){var h,i,j,k,l,m,n,o,p,q,r;if(isNaN(a)||isNaN(b)||isNaN(c))return[];k=this,m=Math.PI/180,i=k.set(),n=function(a,b,c,d,e,f){var g,h,i,j;g=a+c*Math.cos(-d*m),h=a+c*Math.cos(-e*m),i=b+c*Math.sin(-d*m),j=b+c*Math.sin(-e*m);return k.path(["M",a,b,"L",g,i,"A",c,c,0,+(e-d>180),0,h,j,"z"]).attr(f)},h=-270,o=0,l=function(e){var j,k,l,m,p,q,r;r=d[e],j=360*r/o,q=h+j*.5,k=f[e],m=500,l=30,p=n(a,b,c,h,h+j,{fill:k,stroke:g,"stroke-width":1}),p.mouseover(function(){p.stop().animate({transform:"s1.1 1.1 "+a+" "+b},m,"elastic")}),p.mouseout(function(){p.stop().animate({transform:""},m,"elastic")}),h+=j,i.push(p
-)};for(q=0,r=d.length;q<r;q++)p=d[q],o+=p;for(j in d)l(j);return i},drawStackedBars=function(a,b,c,d,e,f,g,h){function k(a,b,c,d,e){return i.rect(a,b,c,d).attr(e)}var i=this,j=this.set(),l=0,m=0,n=function(f){var i=e[f],n=d*i/m,o=a-c*.5,p=b+d*.5-l,q=c,r=g[f],s=500,t=30,u=k(o,p-n,q,n,{fill:r,stroke:h,"stroke-width":1});l+=n,u.mouseover(function(){u.stop().animate({transform:"s1.1 1.1 "+a+" "+b},s,"elastic")}).mouseout(function(){u.stop().animate({transform:""},s,"elastic")}),j.push(u)};for(var o=0,p=e.length;o<p;o++)m+=e[o];for(o=0;o<p;o++)n(o);return j},bg=function(a){function b(a){var c,d,e,f,g,h,i,j,k,l;c=this,b.__super__.constructor.call(this,a),c.styles=(i=a.styles)!=null?i:"",c.colors=(j=a.colors)!=null?j:[],c.titles=(k=a.titles)!=null?k:["","","","",""],c.values=(l=a.values)!=null?l:[],c.width=(e=a.width)!=null?e:17,c.height=(f=a.height)!=null?f:30,c["class"]=(g=a["class"])!=null?g:"barchart",(h=(d=Raphael.fn).drawStackedBarChart)==null&&(d.drawStackedBarChart=drawStackedBars)}bO(b,a),b.prototype.overlaps=function(a){var b,c,d,e,f,g,h,i,j,k,l;d=this,k=[d.x,d.y,d.radius],g=k[0],i=k[1],e=k[2],l=[a.x,a.y,a.radius],h=l[0],j=l[1],f=l[2];if(g-e>h+f||g+e<h-f||i-e>j+f||i+e<j-f)return!1;b=g-h,c=i-j;return b*b+c*c>(e+f)*(e+f)?!1:!0},b.prototype.render=function(a){var b,c,d,e,f,g;d=this,e=d.width,c=d.height,f=d.x,g=d.y,b=d.layers.mapcanvas.rect(f-e*.5-2,g-c*.5-2,e+4,c+4).attr({stroke:"none",fill:"#fff"}),d.chart=d.layers.mapcanvas.drawStackedBarChart(d.x,d.y,d.width,d.height,d.values,d.titles,d.colors,"none"),d.chart.push(b);return d},b.prototype.update=function(){var a,b;a=this;return},b.prototype.clear=function(){var a,b,c,d,e;a=this,e=a.chart;for(c=0,d=e.length;c<d;c++)b=e[c],b.remove();a.chart=[];return a},b.prototype.nodes=function(){var a,b,c,d,e,f;b=this,e=b.chart,f=[];for(c=0,d=e.length;c<d;c++)a=e[c],f.push(a.node);return f};return b}(br.Symbol),bg.props=["values","styles","class","titles","colors","width","height"],bg.layers=[],br.StackedBarChart=bg}).call(this);
+ */
+(function () {
+ function bS() {
+ function h(a, b, c) {
+ f(a.point, b) < f(c.point, b) && (c = a), a.left && (c = h(a.left, b, c));
+ if (a.right) {
+ var d = a.point[a.axis] - b[a.axis];
+ d * d < f(c.point, b) && (c = h(a.right, b, c))
+ }
+ return c
+ }
+
+ function g(a) {
+ return function (b, c) {
+ b = b[a], c = c[a];
+ return b < c ? -1 : b > c ? 1 : 0
+ }
+ }
+
+ function f(a, c) {
+ var d = 0;
+ for (var e = 0; e < b.length; e++) {
+ var f = b[e], g = a[f] - c[f];
+ d += g * g
+ }
+ return d
+ }
+
+ function e(a, c) {
+ if (!!a.length) {
+ var d = b[c % b.length], f = a.length >> 1;
+ a.sort(g(d));
+ return{axis: d, point: a[f], left: e(a.slice(0, f), c + 1), right: e(a.slice(f + 1), c + 1)}
+ }
+ }
+
+ var a = {}, b = ["x", "y"], c, d = [];
+ a.axes = function (c) {
+ if (!arguments.length)return b;
+ b = c;
+ return a
+ }, a.points = function (b) {
+ if (!arguments.length)return d;
+ d = b, c = null;
+ return a
+ }, a.find = function (b) {return h(a.root(), b, c).point}, a.root = function (a) {return c || (c = e(d, 0))};
+ return a
+ }
+
+ function bR() {
+ var a = {}, b = [], c = 1, d = 1;
+ a.size = function (b) {
+ if (!arguments.length)return d;
+ d = b;
+ return a
+ }, a.iterations = function (b) {
+ if (!arguments.length)return c;
+ c = b;
+ return a
+ }, a.add = function (c) {
+ b.push(c);
+ return a
+ }, a.means = function () {
+ var a = [], e = {}, f = Math.min(d, b.length);
+ for (var g = 0, h = 2 * f; g < h; g++) {
+ var i = b[~~(Math.random() * b.length)], j = i.x + "/" + i.y;
+ if (!(j in e)) {
+ e[j] = 1;
+ if (a.push({x: i.x, y: i.y}) >= f)break
+ }
+ }
+ f = a.length;
+ for (var k = 0; k < c; k++) {
+ var l = bS().points(a);
+ for (var g = 0; g < f; g++) {
+ var m = a[g];
+ m.sumX = 0, m.sumY = 0, m.size = 0, m.points = [], m.indices = []
+ }
+ for (var g = 0; g < b.length; g++) {
+ var n = b[g], m = l.find(n);
+ m.sumX += n.x, m.sumY += n.y, m.size++, m.points.push(n), m.indices.push(g)
+ }
+ for (var g = 0; g < f; g++) {
+ var m = a[g];
+ if (!m.size)continue;
+ m.x = m.sumX / m.size, m.y = m.sumY / m.size
+ }
+ }
+ return a
+ };
+ return a
+ }
+
+ var a, b, c, d, e, f, g, h, i, j, k, l, n, o, p, q, r, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, $, _, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm, bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz, bA, bB, bC, bD, bE, bF, bG, bH, bI, bJ, bK, bL, bM, bN = {}.hasOwnProperty, bO = function (a, b) {
+ function d() {this.constructor = a}
+
+ for (var c in b)bN.call(b, c) && (a[c] = b[c]);
+ d.prototype = b.prototype, a.prototype = new d, a.__super__ = b.prototype;
+ return a
+ }, bP = function (a, b) {return function () {return a.apply(b, arguments)}}, bQ = [].indexOf || function (a) {
+ for (var b = 0, c = this.length; b < c; b++)if (b in this && this[b] === a)return b;
+ return-1
+ };
+ bz = typeof exports != "undefined" && exports !== null ? exports : this, br = bz.$K = window.Kartograph = (bH = bz.Kartograph) != null ? bH : bz.Kartograph = {}, br.version = "0.5.2", a = bz.jQuery, br.__verbose = !1, bB = function (a) {try {return console.warn.apply(console, arguments)} catch (b) {try {return opera.postError.apply(opera, arguments)} catch (b) {return alert(Array.prototype.join.call(arguments, " "))}}}, bs = function (a) {if (br.__verbose)try {return console.debug.apply(console, arguments)} catch (b) {try {return opera.postError.apply(opera, arguments)} catch (b) {return alert(Array.prototype.join.call(arguments, " "))}}}, (bI = (bF = String.prototype).trim) == null && (bF.trim = function () {return this.replace(/^\s+|\s+$/g, "")}), Array.prototype.indexOf || (Array.prototype.indexOf = function (a) {
+ "use strict";
+ if (this == null)throw new TypeError;
+ var b = Object(this), c = b.length >>> 0;
+ if (c === 0)return-1;
+ var d = 0;
+ arguments.length > 0 && (d = Number(arguments[1]), d != d ? d = 0 : d != 0 && d != Infinity && d != -Infinity && (d = (d > 0 || -1) * Math.floor(Math.abs(d))));
+ if (d >= c)return-1;
+ var e = d >= 0 ? d : Math.max(c - Math.abs(d), 0);
+ for (; e < c; e++)if (e in b && b[e] === a)return e;
+ return-1
+ }), bE = function () {
+ var a, b, c, d, e;
+ a = {}, e = "Boolean Number String Function Array Date RegExp Undefined Null".split(" ");
+ for (c = 0, d = e.length; c < d; c++)b = e[c], a["[object " + b + "]"] = b.toLowerCase();
+ return function (b) {
+ var c;
+ c = Object.prototype.toString.call(b);
+ return a[c] || "object"
+ }
+ }(), d = function () {
+ function a(a, b, c, d) {
+ var e;
+ a == null && (a = 0), b == null && (b = 0), c == null && (c = null), d == null && (d = null), e = this, c === null ? (e.xmin = Number.MAX_VALUE, e.xmax = Number.MAX_VALUE * -1) : (e.xmin = e.left = a, e.xmax = e.right = a + c, e.width = c), d === null ? (e.ymin = Number.MAX_VALUE, e.ymax = Number.MAX_VALUE * -1) : (e.ymin = e.top = b, e.ymax = e.bottom = d + b, e.height = d);
+ return
+ }
+
+ a.prototype.update = function (a, b) {
+ var c;
+ b == null && (b = a[1], a = a[0]), c = this, c.xmin = Math.min(c.xmin, a), c.ymin = Math.min(c.ymin, b), c.xmax = Math.max(c.xmax, a), c.ymax = Math.max(c.ymax, b), c.left = c.xmin, c.top = c.ymin, c.right = c.xmax, c.bottom = c.ymax, c.width = c.xmax - c.xmin, c.height = c.ymax - c.ymin;
+ return this
+ }, a.prototype.intersects = function (a) {return a.left < s.right && a.right > s.left && a.top < s.bottom && a.bottom > s.top}, a.prototype.inside = function (a, b) {
+ var c;
+ c = this;
+ return a >= c.left && a <= c.right && b >= c.top && b <= c.bottom
+ }, a.prototype.join = function (a) {
+ var b;
+ b = this, b.update(a.left, a.top), b.update(a.right, a.bottom);
+ return this
+ };
+ return a
+ }(), d.fromXML = function (a) {
+ var b, c, d, e;
+ d = Number(a.getAttribute("x")), e = Number(a.getAttribute("y")), c = Number(a.getAttribute("w")), b = Number(a.getAttribute("h"));
+ return new br.BBox(d, e, c, b)
+ }, br.BBox = d, (bJ = br.geom) == null && (br.geom = {}), (bK = (bG = br.geom).clipping) == null && (bG.clipping = {}), l = function () {
+ function f() {}
+
+ var a, b, c, d, e;
+ b = 0, c = 1, d = 2, a = 4, e = 8, f.prototype.compute_out_code = function (a, b, c) {
+ var d, e;
+ e = this, d = e.INSIDE, b < a.left ? d |= e.LEFT : b > a.right && (d |= e.RIGHT), c < a.top ? d |= e.TOP : c > a.bottom && (d |= e.BOTTOM);
+ return d
+ }, f.prototype.clip = function (a, b, c, d, e) {
+ var f, g, h, i, j, k, l;
+ j = this, g = j.compute_out_code(a, b, c), h = j.compute_out_code(a, d, e), f = False;
+ while (True) {
+ if (!(g | h)) {
+ f = True;
+ break
+ }
+ if (g & h)break;
+ i = code === 0 ? h : g, i & j.TOP ? (k = b + (d - b) * (a.top - c) / (e - c), l = a.top) : i & j.BOTTOM ? (k = b + (d - b) * (a.bottom - c) / (e - c), l = a.bottom) : i & j.RIGHT ? (l = c + (e - c) * (a.right - b) / (d - b), k = a.right) : i & j.LEFT && (l = c + (e - c) * (a.left - b) / (d - b), k = a.left), i === g ? (b = k, c = l, g = j.compute_out_code(a, b, c)) : (d = k, e = l, h = j.compute_out_code(a, d, e))
+ }
+ return f ? [b, c, d, e] : null
+ };
+ return f
+ }(), br.geom.clipping.CohenSutherland = l, B = function () {
+ function b(b, c, d) {
+ var e, f;
+ f = this, f.container = e = a(b), c == null && (c = e.width()), d == null && (d = e.height()), d === 0 && (d = "auto"), f.size = {h: d, w: c}, f.markers = [], f.pathById = {}, f.container.addClass("kartograph")
+ }
+
+ b.prototype.createSVGLayer = function (b) {
+ var c, d, e, f, g, h, i, j;
+ f = this, (j = f._layerCnt) == null && (f._layerCnt = 0), e = f._layerCnt++, i = f.viewport, d = f.container, g = Raphael(d[0], i.width, i.height), h = a(g.canvas), h.css({position: "absolute", top: "0px", left: "0px", "z-index": e + 5}), d.css("position") === "static" && d.css({position: "relative", height: i.height + "px"}), h.addClass(b), c = a("desc", g.canvas).text(), a("desc", g.canvas).text(c.replace("with ", "with kartograph " + br.version + " and "));
+ return g
+ }, b.prototype.createHTMLLayer = function (b) {
+ var c, d, e, f, g, h;
+ f = this, g = f.viewport, c = f.container, (h = f._layerCnt) == null && (f._layerCnt = 0), e = f._layerCnt++, d = a('<div class="layer ' + b + '" />'), d.css({position: "absolute", top: "0px", left: "0px", width: g.width + "px", height: g.height + "px", "z-index": e + 5}), c.append(d);
+ return d
+ }, b.prototype.loadMap = function (b, c, d) {
+ var e, f, g, h;
+ f = this, e = a.Deferred(), f.clear(), f.opts = d != null ? d : {}, (h = (g = f.opts).zoom) == null && (g.zoom = 1), f.mapLoadCallback = c, f._loadMapDeferred = e, f._lastMapUrl = b, f.cacheMaps && br.__mapCache[b] != null ? f._mapLoaded(br.__mapCache[b]) : a.ajax({url: b, dataType: "text", success: f._mapLoaded, context: f, error: function (a, b, c) {return bB(a, b, c)}});
+ return e.promise()
+ }, b.prototype.setMap = function (a, b) {
+ var c, d, e;
+ c = this, c.opts = b != null ? b : {}, (e = (d = c.opts).zoom) == null && (d.zoom = 1), c._lastMapUrl = "string", c._mapLoaded(a)
+ }, b.prototype._mapLoaded = function (b) {
+ var c, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s;
+ h = this, h.cacheMaps && ((o = br.__mapCache) == null && (br.__mapCache = {}), br.__mapCache[h._lastMapUrl] = b);
+ try {b = a(b)} catch (t) {
+ bB("something went horribly wrong while parsing svg"), h._loadMapDeferred.reject("could not parse svg");
+ return
+ }
+ h.svgSrc = b, c = a("view", b), bs("got svg src", h.svgSrc), h.paper == null && (m = h.size.w, f = h.size.h, f === "auto" && (j = c.attr("w") / c.attr("h"), f = m / j), h.viewport = new d(0, 0, m, f)), l = h.viewport, bs("got viewport", h.viewport), h.viewAB = e = br.View.fromXML(c[0]), bs("got first view", h.viewAB), i = (p = h.opts.padding) != null ? p : 0, g = (q = h.opts.halign) != null ? q : "center", k = (r = h.opts.valign) != null ? r : "center", bs("got alignment", g, k), n = (s = h.opts.zoom) != null ? s : 1, h.viewBC = new br.View(h.viewAB.asBBox(), l.width * n, l.height * n, i, g, k), bs("got second view", h.viewBC), h.proj = br.Proj.fromXML(a("proj", c)[0]), bs("got projection", h.proj), h.mapLoadCallback != null && h.mapLoadCallback(h), h._loadMapDeferred != null && h._loadMapDeferred.resolve(h)
+ }, b.prototype.addLayer = function (b, c) {
+ var d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w;
+ c == null && (c = {}), i = this, (u = i.layerIds) == null && (i.layerIds = []), (v = i.layers) == null && (i.layers = {}), i.paper == null && (i.paper = i.createSVGLayer()), l = b, bE(c) === "object" ? (h = c.name, j = c.key, o = c.title) : c = {}, h == null && (h = l), m = a("#" + l, i.svgSrc);
+ if (m.length !== 0) {
+ g = new L(h, j, i, c.filter), d = a("*", m[0]);
+ for (q = 0, s = d.length; q < s; q++)n = d[q], g.addPath(n, o);
+ g.paths.length > 0 && (i.layers[h] = g, i.layerIds.push(h)), e = ["click", "mouseenter", "mouseleave", "dblclick", "mousedown", "mouseup", "mouseover", "mouseout"];
+ for (r = 0, t = e.length; r < t; r++)f = e[r], bE(c[f]) === "function" && g.on(f, c[f]);
+ if (c.styles != null) {
+ w = c.styles;
+ for (k in w)p = w[k], g.style(k, p)
+ }
+ c.tooltips != null && g.tooltips(c.tooltips);
+ return i
+ }
+ }, b.prototype.getLayer = function (a) {
+ var b;
+ b = this;
+ if (b.layers[a] == null) {
+ bB("could not find layer " + a);
+ return null
+ }
+ return b.layers[a]
+ }, b.prototype.getLayerPath = function (a, b) {
+ var c, d;
+ d = this, c = d.getLayer(a);
+ if (c != null)return bE(b) === "object" ? c.getPaths(b)[0] : c.getPath(b);
+ return null
+ }, b.prototype.onLayerEvent = function (a, b, c) {
+ var d;
+ d = this, d.getLayer(c).on(a, b);
+ return d
+ }, b.prototype.addMarker = function (a) {
+ var b, c;
+ b = this, b.markers.push(a), c = b.viewBC.project(b.viewAB.project(b.proj.project(a.lonlat.lon, a.lonlat.lat)));
+ return a.render(c[0], c[1], b.container, b.paper)
+ }, b.prototype.clearMarkers = function () {
+ var a, b, c, d, e;
+ b = this, e = b.markers;
+ for (c = 0, d = e.length; c < d; c++)a = e[c], a.clear();
+ return b.markers = []
+ }, b.prototype.fadeIn = function (a) {
+ var b, c, d, e, f, g, h, i, j, k, l;
+ a == null && (a = {}), f = this, e = (i = a.layer) != null ? i : f.layerIds[f.layerIds.length - 1], c = (j = a.duration) != null ? j : 500, k = f.layers[e].pathsById, l = [];
+ for (d in k)h = k[d], l.push(function () {
+ var a, d, e;
+ e = [];
+ for (a = 0, d = h.length; a < d; a++)g = h[a], bE(c) === "function" ? b = c(g.data) : b = c, g.svgPath.attr("opacity", 0), e.push(g.svgPath.animate({opacity: 1}, b));
+ return e
+ }());
+ return l
+ }, b.prototype.loadCoastline = function () {
+ var b;
+ b = this;
+ return a.ajax({url: "coastline.json", success: b.renderCoastline, context: b})
+ }, b.prototype.resize = function (a, b) {
+ var c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s;
+ g = this, c = g.container, a == null && (a = c.width()), b == null && (b = c.height()), g.viewport = k = new br.BBox(0, 0, a, b), g.paper != null && g.paper.setSize(k.width, k.height), k = g.viewport, h = (o = g.opts.padding) != null ? o : 0, d = (p = g.opts.halign) != null ? p : "center", j = (q = g.opts.valign) != null ? q : "center", l = g.opts.zoom, g.viewBC = new br.View(g.viewAB.asBBox(), k.width * l, k.height * l, h, d, j), r = g.layers;
+ for (e in r)f = r[e], f.setView(g.viewBC);
+ if (g.symbolGroups != null) {
+ s = g.symbolGroups;
+ for (m = 0, n = s.length; m < n; m++)i = s[m], i.onResize()
+ }
+ }, b.prototype.lonlat2xy = function (a) {
+ var b, c;
+ c = this, a.length === 2 && (a = new br.LonLat(a[0], a[1])), a.length === 3 && (a = new br.LonLat(a[0], a[1], a[2])), b = c.proj.project(a.lon, a.lat, a.alt);
+ return c.viewBC.project(c.viewAB.project(b))
+ }, b.prototype.showZoomControls = function () {
+ var a;
+ a = this, a.zc = new S(a);
+ return a
+ }, b.prototype.addSymbolGroup = function (a) {
+ var b, c;
+ b = this, (c = b.symbolGroups) == null && (b.symbolGroups = []);
+ return b.symbolGroups.push(a)
+ }, b.prototype.removeSymbols = function (a) {
+ var b, c, d, e, f, g;
+ b = this;
+ if (a != null)return b.symbolGroups[a].remove();
+ f = b.symbolGroups, g = [];
+ for (d = 0, e = f.length; d < e; d++)c = f[d], g.push(c.remove());
+ return g
+ }, b.prototype.clear = function () {
+ var b, c, d, e, f, g;
+ c = this;
+ if (c.layers != null) {
+ for (b in c.layers)c.layers[b].remove();
+ c.layers = {}, c.layerIds = []
+ }
+ if (c.symbolGroups != null) {
+ g = c.symbolGroups;
+ for (e = 0, f = g.length; e < f; e++)d = g[e], d.remove();
+ c.symbolGroups = []
+ }
+ if (c.paper != null) {
+ a(c.paper.canvas).remove();
+ return c.paper = void 0
+ }
+ }, b.prototype.loadCSS = function (b, c) {
+ var d;
+ d = this;
+ if (a.browser.msie)return a.ajax({url: b, dataType: "text", success: function (a) {
+ d.styles = br.parsecss(a);
+ return c()
+ }, error: function (a, c, d) {return bB("error while loading " + b, a, c, d)}});
+ a("body").append('<link rel="stylesheet" href="' + b + '" />');
+ return c()
+ }, b.prototype.applyCSS = function (a, b) {
+ var c, d, e, f, g, h, i, j, k, l, m, n, o, p, q;
+ e = this;
+ if (e.styles == null)return a;
+ (n = e._pathTypes) == null && (e._pathTypes = ["path", "circle", "rectangle", "ellipse"]), (o = e._regardStyles) == null && (e._regardStyles = ["fill", "stroke", "fill-opacity", "stroke-width", "stroke-opacity"]);
+ for (h in e.styles) {
+ f = h, p = f.split(",");
+ for (j = 0, l = p.length; j < l; j++) {
+ i = p[j], f = i.split(" "), f = f[f.length - 1], f = f.split(":");
+ if (f.length > 1)continue;
+ f = f[0].split("."), c = f.slice(1);
+ if (c.length > 0 && c.indexOf(b) < 0)continue;
+ f = f[0];
+ if (e._pathTypes.indexOf(f) >= 0 && f !== a.type)continue;
+ g = e.styles[h], q = e._regardStyles;
+ for (k = 0, m = q.length; k < m; k++)d = q[k], g[d] != null && a.attr(d, g[d])
+ }
+ }
+ return a
+ }, b.prototype.style = function (a, b, c, d, e) {
+ var f;
+ f = this, a = f.getLayer(a);
+ if (a != null)return a.style(b, c, d, e)
+ };
+ return b
+ }(), br.Kartograph = B, br.map = function (a, b, c) {return new B(a, b, c)}, br.__mapCache = {}, J = function () {
+ function a(a, b, c) {c == null && (c = 0), this.lon = Number(a), this.lat = Number(b), this.alt = Number(c)}
+
+ a.prototype.distance = function (a) {
+ var b, c, d, e, f, g, h, i, j;
+ j = this, b = 6371, g = Math.PI / 180, e = (a.lat - j.lat) * g, f = (a.lon - j.lon) * g, h = j.lat * g, i = a.lat * g, c = Math.sin(e / 2) * Math.sin(e / 2) + Math.sin(f / 2) * Math.sin(f / 2) * Math.cos(h) * Math.cos(i), d = 2 * Math.atan2(Math.sqrt(c), Math.sqrt(1 - c));
+ return b * d
+ };
+ return a
+ }(), F = function (a) {
+ function b(a, c, d) {d == null && (d = 0), b.__super__.constructor.call(this, c, a, d)}
+
+ bO(b, a);
+ return b
+ }(J), br.LonLat = J, br.LatLon = F, L = function () {
+ function b(a, b, c, d) {
+ var e;
+ e = this, e.id = a, e.path_id = b, e.paper = c.paper, e.view = c.viewBC, e.map = c, e.filter = d
+ }
+
+ b.prototype.addPath = function (a, b) {
+ var c, d, e, f, g, h, i;
+ d = this, (g = d.paths) == null && (d.paths = []), c = new M(a, d.id, d.map, b);
+ if (bE(d.filter) === "function" && d.filter(c.data) === !1)c.remove(); else {
+ d.paths.push(c);
+ if (d.path_id != null) {
+ (h = d.pathsById) == null && (d.pathsById = {}), (i = (e = d.pathsById)[f = c.data[d.path_id]]) == null && (e[f] = []);
+ return d.pathsById[c.data[d.path_id]].push(c)
+ }
+ }
+ }, b.prototype.hasPath = function (a) {
+ var b;
+ b = this;
+ return b.pathsById != null && b.pathsById[a] != null
+ }, b.prototype.getPathsData = function () {
+ var a, b, c, d, e, f;
+ a = this, c = [], f = a.paths;
+ for (d = 0, e = f.length; d < e; d++)b = f[d], c.push(b.data);
+ return c
+ }, b.prototype.getPath = function (a) {
+ var b;
+ b = this;
+ return b.hasPath(a) ? b.pathsById[a][0] : null
+ }, b.prototype.getPaths = function (a) {
+ var b, c, d, e, f, g, h, i;
+ e = this, d = [];
+ if (bE(a) === "object") {
+ i = e.paths;
+ for (g = 0, h = i.length; g < h; g++) {
+ f = i[g], c = !0;
+ for (b in a)c = c && f.data[b] === a[b];
+ c && d.push(f)
+ }
+ }
+ return d
+ }, b.prototype.setView = function (a) {
+ var b, c, d, e, f;
+ b = this, f = b.paths;
+ for (d = 0, e = f.length; d < e; d++)c = f[d], c.setView(a);
+ return b
+ }, b.prototype.remove = function () {
+ var a, b, c, d, e, f;
+ a = this, e = a.paths, f = [];
+ for (c = 0, d = e.length; c < d; c++)b = e[c], f.push(b.remove());
+ return f
+ }, b.prototype.style = function (a, b, c, d) {
+ var e, f, g, h, i, j, k, l, m, n, o;
+ j = this;
+ if (bE(a) === "object") {
+ for (i in a)l = a[i], j.style(i, l);
+ return j
+ }
+ c == null && (c = 0), d == null && (d = 0), o = j.paths;
+ for (m = 0, n = o.length; m < n; m++)k = o[m], l = bx(b, k.data), h = bx(c, k.data), g = bx(d, k.data), h > 0 ? (f = {}, f[a] = l, e = Raphael.animation(f, h * 1e3), k.svgPath.animate(e.delay(g * 1e3))) : k.svgPath.attr(a, l);
+ return j
+ }, b.prototype.on = function (b, c) {
+ var d, e, f, g, h, i, j;
+ f = this, d = function () {
+ function a(a, b, c) {this.type = a, this.cb = b, this.layer = c, this.handle = bP(this.handle, this)}
+
+ a.prototype.handle = function (a) {
+ var b;
+ f = this, b = f.layer.map.pathById[a.target.getAttribute("id")];
+ return f.cb(b.data, b.svgPath, a)
+ };
+ return a
+ }(), e = new d(b, c, f), j = f.paths;
+ for (h = 0, i = j.length; h < i; h++)g = j[h], a(g.svgPath.node).bind(b, e.handle);
+ return f
+ }, b.prototype.tooltips = function (b, c) {
+ var d, e, f, g, h, i, j;
+ d = this, f = function (b, d) {
+ var e;
+ e = {position: {target: "mouse", viewport: a(window), adjust: {x: 7, y: 7}}, show: {delay: c != null ? c : 20}, events: {show: function (b, c) {return a(".qtip").filter(function () {return this !== c.elements.tooltip.get(0)}).hide()}}, content: {}}, d != null ? typeof d == "string" ? e.content.text = d : a.isArray(d) && (e.content.title = d[0], e.content.text = d[1]) : e.content.text = "n/a";
+ return a(b.svgPath.node).qtip(e)
+ }, j = d.paths;
+ for (h = 0, i = j.length; h < i; h++)e = j[h], g = bx(b, e.data), f(e, g);
+ return d
+ }, b.prototype.sort = function (a) {
+ var b, c, d, e, f, g;
+ c = this, c.paths.sort(function (b, c) {
+ var d, e, f;
+ d = a(b.data), e = a(c.data);
+ return d === e ? 0 : (f = d > e) != null ? f : {1: -1}
+ }), b = !1, g = c.paths;
+ for (e = 0, f = g.length; e < f; e++)d = g[e], b && d.svgPath.insertAfter(b.svgPath), b = d;
+ return c
+ };
+ return b
+ }(), bx = function (a, b) {return bE(a) === "function" ? a(b) : a}, bt = 0, M = function () {
+ function a(a, b, c, d) {
+ var e, f, g, h, i, j, k, l, m, n, o, p, q;
+ h = this, i = c.paper, n = c.viewBC, h.path = j = br.geom.Path.fromSVG(a), h.vpath = n.projectPath(j), h.svgPath = h.vpath.toSVG(i), c.styles == null ? h.svgPath.node.setAttribute("class", b) : c.applyCSS(h.svgPath, b), l = "path_" + bt++, h.svgPath.node.setAttribute("id", l), c.pathById[l] = h, f = {};
+ for (g = p = 0, q = a.attributes.length - 1; 0 <= q ? p <= q : p >= q; g = 0 <= q ? ++p : --p)e = a.attributes[g], e.name.substr(0, 5) === "data-" && (m = e.value, o = Number(m), m.trim() !== "" && o === m && !isNaN(o) && (m = o), f[e.name.substr(5)] = m);
+ h.data = f, bE(d) === "string" ? k = d : bE(d) === "function" && (k = d(f)), k != null && h.svgPath.attr("title", k)
+ }
+
+ a.prototype.setView = function (a) {
+ var b, c, d;
+ b = this, c = a.projectPath(b.path), b.vpath = c;
+ if (b.path.type === "path") {
+ d = c.svgString();
+ return b.svgPath.attr({path: d})
+ }
+ if (b.path.type === "circle")return b.svgPath.attr({cx: c.x, cy: c.y, r: c.r})
+ }, a.prototype.remove = function () {
+ var a;
+ a = this;
+ return a.svgPath.remove()
+ };
+ return a
+ }(), br.parsecss = function (a, b) {
+ var c, d, e, f, g, h, i, j;
+ f = {}, a = bu(a), j = a.split("`b%");
+ for (h = 0, i = j.length; h < i; h++) {
+ c = j[h], c = c.split("%b`");
+ if (c.length < 2)continue;
+ c[0] = by(c[0]), e = bw(c[1]);
+ if (f[c[0]] != null)for (d in e)g = e[d], f[c[0]][d] = g; else f[c[0]] = e
+ }
+ if (bE(b) === "function")b(f); else return f
+ }, bv = {}, bw = function (a) {
+ var b, c, d, e, f, g;
+ d = bv[a].replace(/^{|}$/g, ""), d = bu(d), c = {}, g = d.split(";");
+ for (e = 0, f = g.length; e < f; e++) {
+ b = g[e], b = b.split(":");
+ if (b.length < 2)continue;
+ c[by(b[0])] = by(b.slice(1).join(":"))
+ }
+ return c
+ }, Z = /{[^{}]*}/, _ = /\[[^\[\]]*\]|{[^{}]*}|\([^()]*\)|function(\s+\w+)?(\s*%b`\d+`b%){2}/, $ = /(?:\/\*(?:[^\*]|\*[^\/])*\*\/)|(\\.|"(?:[^\\\"]|\\.|\\\n)*"|'(?:[^\\\']|\\.|\\\n)*')/g, ba = /%\w`(\d+)`\w%/, bA = 0, bu = function (a, b) {
+ var c, d, e;
+ a = a.replace($, function (a, b) {
+ var c;
+ if (!b)return"";
+ c = "%s`" + ++bA + "`s%", bv[bA] = b.replace(/^\\/, "");
+ return c
+ }), c = b ? _ : Z;
+ while (d = c.exec(a))e = "%b`" + ++bA + "`b%", bv[bA] = d[0], a = a.replace(c, e);
+ return a
+ }, by = function (a) {
+ var b;
+ if (a == null)return a;
+ while (b = ba.exec(a))a = a.replace(ba, bv[b[1]]);
+ return a.trim()
+ }, (bL = br.geom) == null && (br.geom = {}), T = function () {
+ function a(a, b, c) {
+ var d;
+ c == null && (c = !0), d = this, d.type = a, d.contours = b, d.closed = c
+ }
+
+ a.prototype.clipToBBox = function (a) {throw"path clipping is not implemented yet"}, a.prototype.toSVG = function (a) {
+ var b;
+ b = this.svgString();
+ return a.path(b)
+ }, a.prototype.svgString = function () {
+ var a, b, c, d, e, f, g, h, i, j, k, l, m;
+ d = this, e = "", c = d.closed ? "Z M" : "M", l = d.contours;
+ for (h = 0, j = l.length; h < j; h++) {
+ a = l[h], b = !0, e += e === "" ? "M" : c;
+ for (i = 0, k = a.length; i < k; i++)m = a[i], f = m[0], g = m[1], b || (e += "L"), e += f + "," + g, b = !1
+ }
+ d.closed && (e += "Z");
+ return e
+ }, a.prototype.area = function () {
+ var a, b, c, d, e, f, g, h, i;
+ d = this;
+ if (d.areas != null)return d._area;
+ d.areas = [], d._area = 0, h = d.contours;
+ for (e = 0, g = h.length; e < g; e++) {
+ b = h[e], a = 0;
+ for (c = f = 0, i = b.length - 2; 0 <= i ? f <= i : f >= i; c = 0 <= i ? ++f : --f)a += b[c][0] * b[c + 1][1] - b[c + 1][0] * b[c][1];
+ a *= .5, a = a, d.areas.push(a), d._area += a
+ }
+ return d._area
+ }, a.prototype.centroid = function () {
+ var a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K;
+ p = this;
+ if (p._centroid != null)return p._centroid;
+ c = p.area(), f = g = 0;
+ for (k = A = 0, G = p.contours.length - 1; 0 <= G ? A <= G : A >= G; k = 0 <= G ? ++A : --A) {
+ e = p.contours[k], d = [], n = e.length;
+ for (l = B = 0, H = n - 1; 0 <= H ? B <= H : B >= H; l = 0 <= H ? ++B : --B) {
+ q = e[l], r = e[(l + 1) % n], h = 0, d.push(q), q[0] === r[0] && (h = Math.abs(q[1] - r[1])), q[1] === r[1] && (h = Math.abs(q[0] - r[0]));
+ if (h > 10) {
+ a = Math.floor(h * 2);
+ for (s = C = 1, I = a - 1; 1 <= I ? C <= I : C >= I; s = 1 <= I ? ++C : --C)t = [q[0] + s / a * (r[0] - q[0]), q[1] + s / a * (r[1] - q[1])], d.push(t)
+ }
+ }
+ b = p.areas[k], w = y = x = z = 0, n = d.length, E = [], u = 0;
+ for (l = D = 0, J = n - 1; 0 <= J ? D <= J : D >= J; l = 0 <= J ? ++D : --D)q = d[l], r = d[(l + 1) % n], i = r[0] - q[0], j = r[1] - q[1], o = Math.sqrt(i * i + j * j), E.push(o), u += o;
+ for (l = F = 0, K = n - 1; 0 <= K ? F <= K : F >= K; l = 0 <= K ? ++F : --F)q = d[l], v = E[l] / u, w += v * q[0], y += v * q[1];
+ m = b / c, f += w * m, g += y * m
+ }
+ p._centroid = [f, g];
+ return p._centroid
+ }, a.prototype.isInside = function (a, b) {
+ var c, d, e, f, g, h;
+ f = this, c = f._bbox;
+ if (a < c[0] || a > c[2] || b < c[1] || b > c[3])return!1;
+ for (e = g = 0, h = f.contours.length - 1; 0 <= h ? g <= h : g >= h; e = 0 <= h ? ++g : --g) {
+ d = f.contours[e];
+ if (bC(d, [a, b]))return!0
+ }
+ return!1
+ };
+ return a
+ }(), br.geom.Path = T, k = function (a) {
+ function b(a, c, d) {this.x = a, this.y = c, this.r = d, b.__super__.constructor.call(this, "circle", null, !0)}
+
+ bO(b, a), b.prototype.toSVG = function (a) {
+ var b;
+ b = this;
+ return a.circle(b.x, b.y, b.r)
+ }, b.prototype.centroid = function () {
+ var a;
+ a = this;
+ return[a.x, a.y]
+ }, b.prototype.area = function () {
+ var a;
+ a = this;
+ return Math.PI * a.r * m.r
+ };
+ return b
+ }(T), br.geom.Circle = k, T.fromSVG = function (a) {
+ var b, c, d, e, f, g, h, i, j, k, l, m, n, o;
+ e = [], m = a.nodeName, k = null;
+ if (m === "path") {
+ i = a.getAttribute("d").trim(), h = Raphael.parsePathString(i), b = h[h.length - 1] === "Z", l = b ? "Z M" : "M", d = [];
+ for (n = 0, o = h.length; n < o; n++) {
+ c = h[n];
+ if (c.length === 0)continue;
+ c[0] === "M" ? (d.length > 2 && (e.push(d), d = []), d.push([c[1], c[2]])) : c[0] === "L" ? d.push([c[1], c[2]]) : c[0] === "Z" && d.length > 2 && (e.push(d), d = [])
+ }
+ d.length > 2 && (e.push(d), d = []), k = new br.geom.Path(m, e, b)
+ } else m === "circle" && (f = a.getAttribute("cx"), g = a.getAttribute("cy"), j = a.getAttribute("r"), k = new br.geom.Circle(f, g, j));
+ return k
+ }, G = function () {
+ function a(a) {this.points = a}
+
+ a.prototype.clipToBBox = function (b) {
+ var c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u;
+ l = this, c = (new br.geom.clipping.CohenSutherland).clip, k = [], f = [], e = !1;
+ for (d = q = 0, r = l.points.length - 2; 0 <= r ? q <= r : q >= r; d = 0 <= r ? ++q : --q) {
+ s = l.points[d], g = s[0], h = s[1], t = l.points[d + 1], i = t[0], j = t[1];
+ try {u = c(b, g, h, i, j), m = u[0], o = u[1], n = u[2], p = u[3], e = !0, k.push([m, o]), (i !== n || j !== o || d === len(l.points) - 2) && k.push([n, p])} catch (v) {e && k.length > 1 && (f.push(new a(k)), k = []), e = !1}
+ }
+ k.length > 1 && f.push(new a(k));
+ return f
+ }, a.prototype.toSVG = function () {
+ var a, b, c, d, e, f, g, h;
+ b = this, a = [], g = b.points;
+ for (e = 0, f = g.length; e < f; e++)h = g[e], c = h[0], d = h[1], a.push(c + "," + d);
+ return"M" + a.join("L")
+ };
+ return a
+ }(), br.geom.Line = G, bC = function (a, b) {
+ var c, d, e, f, g, h, i, j, k, l, m, n, o, p, q;
+ h = Math.PI, d = Math.atan2, k = h * 2, g = a.length, c = 0;
+ for (f = p = 0, q = g - 1; 0 <= q ? p <= q : p >= q; f = 0 <= q ? ++p : --p) {
+ l = a[f][0] - b[0], n = a[f][1] - b[1], m = a[(f + 1) % g][0] - b[0], o = a[(f + 1) % g][1] - b[1], i = d(n, l), j = d(o, m), e = j - i;
+ while (e > h)e -= k;
+ while (e < -h)e += k;
+ c += e
+ }
+ return Math.abs(c) >= h
+ }, bD = br.proj = {}, Function.prototype.bind = function (a) {
+ var b;
+ b = this;
+ return function () {return b.apply(a, arguments)}
+ }, V = function () {
+ function a(a) {
+ var b, c, d;
+ b = this, b.lon0 = (c = a.lon0) != null ? c : 0, b.lat0 = (d = a.lat0) != null ? d : 0, b.PI = Math.PI, b.HALFPI = b.PI * .5, b.QUARTERPI = b.PI * .25, b.RAD = b.PI / 180, b.DEG = 180 / b.PI, b.lam0 = b.rad(this.lon0), b.phi0 = b.rad(this.lat0), b.minLat = -90, b.maxLat = 90
+ }
+
+ a.parameters = [], a.title = "Projection", a.prototype.rad = function (a) {return a * this.RAD}, a.prototype.deg = function (a) {return a * this.DEG}, a.prototype.plot = function (a, b) {
+ var c, d, e, f, g, h, i, j, k, l, m;
+ b == null && (b = !0), f = [], c = !0;
+ for (j = 0, k = a.length; j < k; j++)l = a[j], e = l[0], d = l[1], g = this._visible(e, d), g && (c = !1), m = this.project(e, d), h = m[0], i = m[1], !g && b ? f.push(this._truncate(h, i)) : f.push([h, i]);
+ return c ? null : [f]
+ }, a.prototype.sea = function () {
+ var a, b, c, d, e, f, g, h, i, j, k, l, m, n;
+ f = this, e = f.project.bind(this), d = [], a = f.lon0, f.lon0 = 0;
+ for (c = g = -180; g <= 180; c = ++g)d.push(e(c, f.maxLat));
+ for (b = h = k = f.maxLat, l = f.minLat; k <= l ? h <= l : h >= l; b = k <= l ? ++h : --h)d.push(e(180, b));
+ for (c = i = 180; i >= -180; c = --i)d.push(e(c, f.minLat));
+ for (b = j = m = f.minLat, n = f.maxLat; m <= n ? j <= n : j >= n; b = m <= n ? ++j : --j)d.push(e(-180, b));
+ f.lon0 = a;
+ return d
+ }, a.prototype.world_bbox = function () {
+ var a, b, c, d, e, f;
+ b = this.project.bind(this), d = this.sea(), a = new br.BBox;
+ for (e = 0, f = d.length; e < f; e++)c = d[e], a.update(c[0], c[1]);
+ return a
+ }, a.prototype.toString = function () {
+ var a;
+ a = this;
+ return"[Proj: " + a.name + "]"
+ };
+ return a
+ }(), V.fromXML = function (a) {
+ var b, c, d, e, f, g, h;
+ d = a.getAttribute("id"), e = {};
+ for (c = g = 0, h = a.attributes.length - 1; 0 <= h ? g <= h : g >= h; c = 0 <= h ? ++g : --g)b = a.attributes[c], b.name !== "id" && (e[b.name] = b.value);
+ f = new br.proj[d](e), f.name = d;
+ return f
+ }, br.Proj = V, o = function (a) {
+ function b(a) {
+ var c, d, e;
+ a == null && (a = {}), c = this, c.flip = Number((d = a.flip) != null ? d : 0), c.flip === 1 && (a.lon0 = (e = -a.lon0) != null ? e : 0), b.__super__.constructor.call(this, a)
+ }
+
+ bO(b, a), b.parameters = ["lon0", "flip"], b.title = "Cylindrical Projection", b.prototype._visible = function (a, b) {return!0}, b.prototype.clon = function (a) {
+ a -= this.lon0, a < -180 ? a += 360 : a > 180 && (a -= 360);
+ return a
+ }, b.prototype.ll = function (a, b) {return this.flip === 1 ? [-a, -b] : [a, b]};
+ return b
+ }(V), r = function (a) {
+ function b() {return b.__super__.constructor.apply(this, arguments)}
+
+ bO(b, a), b.title = "Equirectangular Projection", b.prototype.project = function (a, b) {
+ var c;
+ c = this.ll(a, b), a = c[0], b = c[1], a = this.clon(a);
+ return[a * Math.cos(this.phi0) * 1e3, b * -1 * 1e3]
+ };
+ return b
+ }(o), bD.lonlat = r, i = function (a) {
+ function b(a) {
+ var c;
+ b.__super__.constructor.call(this, a), this.lat1 = (c = a.lat1) != null ? c : 0, this.phi1 = this.rad(this.lat1)
+ }
+
+ bO(b, a), b.parameters = ["lon0", "lat1", "flip"], b.title = "Cylindrical Equal Area", b.prototype.project = function (a, b) {
+ var c, d, e, f, g;
+ g = this.ll(a, b), a = g[0], b = g[1], c = this.rad(this.clon(a)), d = this.rad(b * -1), e = c * Math.cos(this.phi1), f = Math.sin(d) / Math.cos(this.phi1);
+ return[e * 1e3, f * 1e3]
+ };
+ return b
+ }(o), bD.cea = i, u = function (a) {
+ function b(a) {a.lat1 = 45, b.__super__.constructor.call(this, a)}
+
+ bO(b, a), b.title = "Gall-Peters Projection", b.parameters = ["lon0", "flip"];
+ return b
+ }(i), bD.gallpeters = u, y = function (a) {
+ function b(a) {a.lat1 = 37.7, b.__super__.constructor.call(this, a)}
+
+ bO(b, a), b.title = "Hobo-Dyer Projection", b.parameters = ["lon0", "flip"];
+ return b
+ }(i), bD.hobodyer = y, f = function (a) {
+ function b(a) {a.lat1 = 30, b.__super__.constructor.call(this, a)}
+
+ bO(b, a), b.title = "Behrmann Projection", b.parameters = ["lon0", "flip"];
+ return b
+ }(i), bD.behrmann = f, e = function (a) {
+ function b(a) {a.lat1 = 50, b.__super__.constructor.call(this, a)}
+
+ bO(b, a), b.title = "Balthasart Projection", b.parameters = ["lon0", "flip"];
+ return b
+ }(i), bD.balthasart = e, N = function (a) {
+ function b(a) {b.__super__.constructor.call(this, a), this.minLat = -85, this.maxLat = 85}
+
+ bO(b, a), b.title = "Mercator Projection", b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i;
+ f = this, i = f.ll(a, b), a = i[0], b = i[1], d = Math, c = f.rad(f.clon(a)), e = f.rad(b * -1), g = c * 1e3, h = d.log((1 + d.sin(e)) / d.cos(e)) * 1e3;
+ return[g, h]
+ };
+ return b
+ }(o), bD.mercator = N, X = function (a) {
+ function b() {return b.__super__.constructor.apply(this, arguments)}
+
+ bO(b, a), b.title = "Pseudo-Cylindrical Projection";
+ return b
+ }(o), P = function (a) {
+ function b(a) {
+ var c;
+ b.__super__.constructor.call(this, a), c = this, c.A0 = .8707, c.A1 = -0.131979, c.A2 = -0.013791, c.A3 = .003971, c.A4 = -0.001529, c.B0 = 1.007226, c.B1 = .015085, c.B2 = -0.044475, c.B3 = .028874, c.B4 = -0.005916, c.C0 = c.B0, c.C1 = 3 * c.B1, c.C2 = 7 * c.B2, c.C3 = 9 * c.B3, c.C4 = 11 * c.B4, c.EPS = 1e-11, c.MAX_Y = .8707 * .52 * Math.PI;
+ return
+ }
+
+ bO(b, a), b.title = "Natural Earth Projection", b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i, j;
+ g = this, j = g.ll(a, b), a = j[0], b = j[1], c = g.rad(g.clon(a)), d = g.rad(b * -1), e = d * d, f = e * e, h = c * (g.A0 + e * (g.A1 + e * (g.A2 + f * e * (g.A3 + e * g.A4)))) * 180 + 500, i = d * (g.B0 + e * (g.B1 + f * (g.B2 + g.B3 * e + g.B4 * f))) * 180 + 270;
+ return[h, i]
+ };
+ return b
+ }(X), bD.naturalearth = P, bb = function (a) {
+ function b(a) {
+ var c;
+ b.__super__.constructor.call(this, a), c = this, c.X = [1, -5.67239e-12, -0.0000715511, 311028e-11, .9986, -0.000482241, -0.000024897, -0.00000133094, .9954, -0.000831031, -0.000044861, -9.86588e-7, .99, -0.00135363, -0.0000596598, 367749e-11, .9822, -0.00167442, -0.0000044975, -0.00000572394, .973, -0.00214869, -0.0000903565, 1.88767e-8, .96, -0.00305084, -0.0000900732, 164869e-11, .9427, -0.00382792, -0.0000653428, -0.00000261493, .9216, -0.00467747, -0.000104566, 48122e-10, .8962, -0.00536222, -0.0000323834, -0.00000543445, .8679, -0.00609364, -0.0001139, 332521e-11, .835, -0.00698325, -0.0000640219, 9.34582e-7, .7986, -0.00755337, -0.0000500038, 9.35532e-7, .7597, -0.00798325, -0.0000359716, -0.00000227604, .7186, -0.00851366, -0.000070112, -0.00000863072, .6732, -0.00986209, -0.000199572, 191978e-10, .6213, -0.010418, 883948e-10, 624031e-11, .5722, -0.00906601, 181999e-9, 624033e-11, .5322, 0, 0, 0], c.Y = [0, .0124, 3.72529e-10, 1.15484e-9, .062, .0124001, 1.76951e-8, -5.92321e-9, .124, .0123998, -7.09668e-8, 2.25753e-8, .186, .0124008, 2.66917e-7, -8.44523e-8, .248, .0123971, -9.99682e-7, 3.15569e-7, .31, .0124108, 373349e-11, -0.0000011779, .372, .0123598, -0.000013935, 439588e-11, .434, .0125501, 520034e-10, -0.0000100051, .4968, .0123198, -0.0000980735, 922397e-11, .5571, .0120308, 402857e-10, -0.0000052901, .6176, .0120369, -0.0000390662, 7.36117e-7, .6769, .0117015, -0.0000280246, -8.54283e-7, .7346, .0113572, -0.0000408389, -5.18524e-7, .7903, .0109099, -0.0000486169, -0.0000010718, .8435, .0103433, -0.0000646934, 5.36384e-9, .8936, .00969679, -0.0000646129, -0.00000854894, .9394, .00840949, -0.000192847, -0.00000421023, .9761, .00616525, -0.000256001, -0.00000421021, 1, 0, 0, 0], c.NODES = 18, c.FXC = .8487, c.FYC = 1.3523, c.C1 = 11.459155902616464, c.RC1 = .08726646259971647, c.ONEEPS = 1.000001, c.EPS = 1e-8;
+ return
+ }
+
+ bO(b, a), b.title = "Robinson Projection", b.prototype._poly = function (a, b, c) {return a[b] + c * (a[b + 1] + c * (a[b + 2] + c * a[b + 3]))}, b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i, j;
+ g = this, j = g.ll(a, b), a = j[0], b = j[1], a = g.clon(a), d = g.rad(a), e = g.rad(b * -1), f = Math.abs(e), c = Math.floor(f * g.C1), c >= g.NODES && (c = g.NODES - 1), f = g.deg(f - g.RC1 * c), c *= 4, h = 1e3 * g._poly(g.X, c, f) * g.FXC * d, i = 1e3 * g._poly(g.Y, c, f) * g.FYC, e < 0 && (i = -i);
+ return[h, i]
+ };
+ return b
+ }(X), bD.robinson = bb, p = function (a) {
+ function b(a) {
+ var c;
+ b.__super__.constructor.call(this, a), c = this, c.C_x = .4222382003157712, c.C_y = 1.3265004281770023, c.RC_y = .7538633073600218, c.C_p = 3.5707963267948966, c.RC_p = .2800495767557787, c.EPS = 1e-7, c.NITER = 6
+ }
+
+ bO(b, a), b.title = "Eckert IV Projection", b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i, j, k, l, m;
+ h = this, m = h.ll(a, b), a = m[0], b = m[1], f = h.rad(h.clon(a)), g = h.rad(b * -1), i = h.C_p * Math.sin(g), c = g * g, g *= .895168 + c * (.0218849 + c * .00826809), e = h.NITER;
+ while (e > 0) {
+ d = Math.cos(g), j = Math.sin(g), c = (g + j * (d + 2) - i) / (1 + d * (d + 2) - j * j), g -= c;
+ if (Math.abs(c) < h.EPS)break;
+ e -= 1
+ }
+ e === 0 ? (k = h.C_x * f, l = g < 0 ? -h.C_y : h.C_y) : (k = h.C_x * f * (1 + Math.cos(g)), l = h.C_y * Math.sin(g));
+ return[k, l]
+ };
+ return b
+ }(X), bD.eckert4 = p, be = function (a) {
+ function b() {return b.__super__.constructor.apply(this, arguments)}
+
+ bO(b, a), b.title = "Sinusoidal Projection", b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h;
+ d = this, h = d.ll(a, b), a = h[0], b = h[1], c = d.rad(d.clon(a)), e = d.rad(b * -1), f = 1032 * c * Math.cos(e), g = 1032 * e;
+ return[f, g]
+ };
+ return b
+ }(X), bD.sinusoidal = be, O = function (a) {
+ function b(a, c, d, e, f) {
+ var g, h, i, j;
+ c == null && (c = 1.5707963267948966), d == null && (d = null), e == null && (e = null), f == null && (f = null), b.__super__.constructor.call(this, a), g = this, g.MAX_ITER = 10, g.TOLERANCE = 1e-7, c != null ? (h = c + c, j = Math.sin(c), i = Math.sqrt(Math.PI * 2 * j / (h + Math.sin(h))), g.cx = 2 * i / Math.PI, g.cy = i / j, g.cp = h + Math.sin(h)) : d != null && e != null && typeof cz != "undefined" && cz !== null ? (g.cx = d, g.cy = e, g.cp = f) : bB("kartograph.proj.Mollweide: either p or cx,cy,cp must be defined")
+ }
+
+ bO(b, a), b.title = "Mollweide Projection", b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i, j, k, l, m;
+ h = this, m = h.ll(a, b), a = m[0], b = m[1], g = Math, c = g.abs, f = h.rad(h.clon(a)), i = h.rad(b), e = h.cp * g.sin(i), d = h.MAX_ITER;
+ while (d !== 0) {
+ j = (i + g.sin(i) - e) / (1 + g.cos(i)), i -= j;
+ if (c(j) < h.TOLERANCE)break;
+ d -= 1
+ }
+ d === 0 ? i = i >= 0 ? h.HALFPI : -h.HALFPI : i *= .5, k = 1e3 * h.cx * f * g.cos(i), l = 1e3 * h.cy * g.sin(i);
+ return[k, l * -1]
+ };
+ return b
+ }(X), bD.mollweide = O, bm = function (a) {
+ function b(a) {b.__super__.constructor.call(this, a, 1.0471975511965976)}
+
+ bO(b, a), b.title = "Wagner IV Projection";
+ return b
+ }(O), bD.wagner4 = bm, bn = function (a) {
+ function b(a) {b.__super__.constructor.call(this, a, null, .90977, 1.65014, 3.00896)}
+
+ bO(b, a), b.title = "Wagner V Projection";
+ return b
+ }(O), bD.wagner5 = bn, K = function (a) {
+ function d() {return d.__super__.constructor.apply(this, arguments)}
+
+ var b, c;
+ bO(d, a), c = -89, b = 89, d.parameters = ["lon0", "lat0", "flip"], d.title = "Loximuthal Projection (equidistant)", d.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i;
+ e = this, i = e.ll(a, b), a = i[0], b = i[1], d = Math, c = e.rad(e.clon(a)), f = e.rad(b), f === e.phi0 ? g = c * d.cos(e.phi0) : g = c * (f - e.phi0) / (d.log(d.tan(e.QUARTERPI + f * .5)) - d.log(d.tan(e.QUARTERPI + e.phi0 * .5))), g *= 1e3, h = 1e3 * (f - e.phi0);
+ return[g, h * -1]
+ };
+ return d
+ }(X), bD.loximuthal = K, j = function (a) {
+ function g() {return g.__super__.constructor.apply(this, arguments)}
+
+ var b, c, d, e, f;
+ bO(g, a), g.title = "Canters Modified Sinusoidal I", g.parameters = ["lon0"], b = 1.1966, c = -0.129, d = 3 * c, e = -0.0076, f = 5 * e, g.prototype.project = function (a, g) {
+ var h, i, j, k, l, m;
+ h = this, m = h.ll(a, g), a = m[0], g = m[1], a = h.rad(h.clon(a)), g = h.rad(g), k = g * g, l = k * k, i = 1e3 * a * Math.cos(g) / (b + d * k + f * l), j = 1e3 * g * (b + c * k + e * l);
+ return[i, j * -1]
+ };
+ return g
+ }(X), bD.canters1 = j, x = function (a) {
+ function o(a) {o.__super__.constructor.call(this, a)}
+
+ var b, c, d, e, f, g, h, i, j, k, l, m, n;
+ bO(o, a), o.title = "Hatano Projection", h = 20, d = 1e-7, i = 1.000001, b = 2.67595, c = 2.43763, j = .3736990601468637, k = .4102345310814193, f = 1.75859, g = 1.93052, m = .5686373742600607, n = .5179951515653813, e = .85, l = 1.1764705882352942, o.prototype.project = function (a, i) {
+ var j, k, l, m, n,
+ o, p, q, r, s;
+ m = this, s = m.ll(a, i), a = s[0], i = s[1], l = m.rad(m.clon(a)), n = m.rad(i), j = Math.sin(n) * (n < 0 ? c : b);
+ for (k = r = h; r >= 1; k = r += -1) {
+ o = (n + Math.sin(n) - j) / (1 + Math.cos(n)), n -= o;
+ if (Math.abs(o) < d)break
+ }
+ p = 1e3 * e * l * Math.cos(n *= .5), q = 1e3 * Math.sin(n) * (n < 0 ? g : f);
+ return[p, q * -1]
+ };
+ return o
+ }(X), bD.hatano = x, w = function (a) {
+ function b(a) {
+ var c;
+ b.__super__.constructor.call(this, a), c = this, c.lat1 = 41.737, c.p1 = new O, c.p0 = new be
+ }
+
+ bO(b, a), b.title = "Goode Homolosine Projection", b.parameters = ["lon0"], b.prototype.project = function (a, b) {
+ var c, d;
+ c = this, d = c.ll(a, b), a = d[0], b = d[1], a = c.clon(a);
+ return Math.abs(b) > c.lat1 ? c.p1.project(a, b) : c.p0.project(a, b)
+ };
+ return b
+ }(X), bD.goodehomolosine = w, Q = function (a) {
+ function c(a) {c.__super__.constructor.call(this, a), this.r = this.HALFPI * 100}
+
+ var b;
+ bO(c, a), c.title = "Nicolosi Globular Projection", c.parameters = ["lon0"], b = 1e-10, c.prototype._visible = function (a, b) {
+ var c;
+ c = this, a = c.clon(a);
+ return a > -90 && a < 90
+ }, c.prototype.project = function (a, c) {
+ var d, e, f, g, h, i, j, k, l, m, n, o, p;
+ h = this, p = h.ll(a, c), a = p[0], c = p[1], f = h.rad(h.clon(a)), j = h.rad(c), Math.abs(f) < b ? (n = 0, o = j) : Math.abs(j) < b ? (n = f, o = 0) : Math.abs(Math.abs(f) - h.HALFPI) < b ? (n = f * Math.cos(j), o = h.HALFPI * Math.sin(j)) : Math.abs(Math.abs(j) - h.HALFPI) < b ? (n = 0, o = j) : (m = h.HALFPI / f - f / h.HALFPI, d = j / h.HALFPI, l = Math.sin(j), e = (1 - d * d) / (l - d), k = m / e, k *= k, g = (m * l / e - .5 * m) / (1 + k), i = (l / k + .5 * e) / (1 + 1 / k), n = Math.cos(j), n = Math.sqrt(g * g + n * n / (1 + k)), n = h.HALFPI * (g + (f < 0 ? -n : n)), o = Math.sqrt(i * i - (l * l / k + e * l - 1) / (1 + 1 / k)), o = h.HALFPI * (i + (j < 0 ? o : -o)));
+ return[n * 100, o * -100]
+ }, c.prototype.sea = function () {
+ var a, b, c, d, e;
+ b = [], d = this.r, a = Math;
+ for (c = e = 0; e <= 360; c = ++e)b.push([a.cos(this.rad(c)) * d, a.sin(this.rad(c)) * d]);
+ return b
+ }, c.prototype.world_bbox = function () {
+ var a;
+ a = this.r;
+ return new br.BBox(-a, -a, a * 2, a * 2)
+ };
+ return c
+ }(X), bD.nicolosi = Q, c = function (a) {
+ function b(a, c) {
+ var d;
+ c == null && (c = 1e3), b.__super__.constructor.call(this, a), d = this, d.r = c, d.elevation0 = d.to_elevation(d.lat0), d.azimuth0 = d.to_azimuth(d.lon0)
+ }
+
+ bO(b, a), b.parameters = ["lon0", "lat0"], b.title = "Azimuthal Projection", b.prototype.to_elevation = function (a) {
+ var b;
+ b = this;
+ return(a + 90) / 180 * b.PI - b.HALFPI
+ }, b.prototype.to_azimuth = function (a) {
+ var b;
+ b = this;
+ return(a + 180) / 360 * b.PI * 2 - b.PI
+ }, b.prototype._visible = function (a, b) {
+ var c, d, e, f, g;
+ g = this, f = Math, e = g.to_elevation(b), c = g.to_azimuth(a), d = f.sin(e) * f.sin(g.elevation0) + f.cos(g.elevation0) * f.cos(e) * f.cos(c - g.azimuth0);
+ return d >= 0
+ }, b.prototype._truncate = function (a, b) {
+ var c, d, e, f, g;
+ c = Math, d = this.r, e = c.atan2(b - d, a - d), f = d + d * c.cos(e), g = d + d * c.sin(e);
+ return[f, g]
+ }, b.prototype.sea = function () {
+ var a, b, c, d, e;
+ b = [], d = this.r, a = Math;
+ for (c = e = 0; e <= 360; c = ++e)b.push([d + a.cos(this.rad(c)) * d, d + a.sin(this.rad(c)) * d]);
+ return b
+ }, b.prototype.world_bbox = function () {
+ var a;
+ a = this.r;
+ return new br.BBox(0, 0, a * 2, a * 2)
+ };
+ return b
+ }(V), R = function (a) {
+ function b() {return b.__super__.constructor.apply(this, arguments)}
+
+ bO(b, a), b.title = "Orthographic Projection", b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i, j;
+ f = this, e = Math, d = f.to_elevation(b), c = f.to_azimuth(a), h = f.r * e.cos(d) * e.sin(c - f.azimuth0), j = -f.r * (e.cos(f.elevation0) * e.sin(d) - e.sin(f.elevation0) * e.cos(d) * e.cos(c - f.azimuth0)), g = f.r + h, i = f.r + j;
+ return[g, i]
+ };
+ return b
+ }(c), bD.ortho = R, C = function (a) {
+ function b(a) {b.__super__.constructor.call(this, a), this.scale = Math.sqrt(2) * .5}
+
+ bO(b, a), b.title = "Lambert Azimuthal Equal-Area Projection", b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i, j, k, l;
+ g = this.rad(b), e = this.rad(a), f = Math, h = f.sin, c = f.cos, d = f.pow(2 / (1 + h(this.phi0) * h(g) + c(this.phi0) * c(g) * c(e - this.lam0)), .5), d *= this.scale, j = this.r * d * c(g) * h(e - this.lam0), l = -this.r * d * (c(this.phi0) * h(g) - h(this.phi0) * c(g) * c(e - this.lam0)), i = this.r + j, k = this.r + l;
+ return[i, k]
+ };
+ return b
+ }(c), bD.laea = C, bh = function (a) {
+ function b() {return b.__super__.constructor.apply(this, arguments)}
+
+ bO(b, a), b.title = "Stereographic Projection", b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i, j, k, l, m;
+ h = this.rad(b), f = this.rad(a), g = Math, i = g.sin, c = g.cos, e = .5, d = 2 * e / (1 + i(this.phi0) * i(h) + c(this.phi0) * c(h) * c(f - this.lam0)), k = this.r * d * c(h) * i(f - this.lam0), m = -this.r * d * (c(this.phi0) * i(h) - i(this.phi0) * c(h) * c(f - this.lam0)), j = this.r + k, l = this.r + m;
+ return[j, l]
+ };
+ return b
+ }(c), bD.stereo = bh, bc = function (a) {
+ function b(a) {
+ var c, d, e, f, g, h, i, j, k, l;
+ b.__super__.constructor.call(this, {lon0: 0, lat0: 0}), this.dist = (j = a.dist) != null ? j : 3, this.up = this.rad((k = a.up) != null ? k : 0), this.tilt = this.rad((l = a.tilt) != null ? l : 0), this.scale = 1, f = Number.MAX_VALUE, e = Number.MAX_VALUE * -1;
+ for (c = h = 0; h <= 179; c = ++h)for (d = i = 0; i <= 360; d = ++i)g = this.project(d - 180, c - 90), f = Math.min(g[0], f), e = Math.max(g[0], e);
+ this.scale = this.r * 2 / (e - f), b.__super__.constructor.call(this, a);
+ return
+ }
+
+ bO(b, a), b.parameters = ["lon0", "lat0", "tilt", "dist", "up"], b.title = "Satellite Projection", b.prototype.project = function (a, b, c) {
+ var d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x;
+ c == null && (c = 0), m = this.rad(b), k = this.rad(a), l = Math, p = l.sin, f = l.cos, n = this.r, o = n * (c + 6371) / 3671, g = p(this.phi0) * p(m) + f(this.phi0) * f(m) * f(k - this.lam0), j = (this.dist - 1) / (this.dist - g), j = (this.dist - 1) / (this.dist - g), j *= this.scale, t = o * j * f(m) * p(k - this.lam0), w = -o * j * (f(this.phi0) * p(m) - p(this.phi0) * f(m) * f(k - this.lam0)), i = f(this.up), r = p(this.up), h = f(this.tilt), q = p(this.tilt), e = o * (this.dist - 1), d = (w * i + t * r) * p(this.tilt / e) + h, u = (t * i - w * r) * f(this.tilt / d), x = (w * i + t * r) / d, s = n + u, v = n + x;
+ return[s, v]
+ }, b.prototype._visible = function (a, b) {
+ var c, d, e, f;
+ e = this.to_elevation(b), c = this.to_azimuth(a), f = Math, d = f.sin(e) * f.sin(this.elevation0) + f.cos(this.elevation0) * f.cos(e) * f.cos(c - this.azimuth0);
+ return d >= 1 / this.dist
+ }, b.prototype.sea = function () {
+ var a, b, c, d, e;
+ b = [], d = this.r, a = Math;
+ for (c = e = 0; e <= 360; c = ++e)b.push([d + a.cos(this.rad(c)) * d, d + a.sin(this.rad(c)) * d]);
+ return b
+ };
+ return b
+ }(c), bD.satellite = bc, q = function (a) {
+ function b() {return b.__super__.constructor.apply(this, arguments)}
+
+ bO(b, a), b.title = "Equidistant Azimuthal Projection", b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i, j, k, l, m, n, o;
+ i = this, j = i.rad(b), g = i.rad(a), h = Math, k = h.sin, d = h.cos, e = k(this.phi0) * k(j) + d(this.phi0) * d(j) * d(g - this.lam0), c = h.acos(e), f = .325 * c / k(c), m = this.r * f * d(j) * k(g - this.lam0), o = -this.r * f * (d(this.phi0) * k(j) - k(this.phi0) * d(j) * d(g - this.lam0)), l = this.r + m, n = this.r + o;
+ return[l, n]
+ }, b.prototype._visible = function (a, b) {return!0};
+ return b
+ }(c), bD.equi = q, b = function (a) {
+ function c(a) {
+ var b;
+ b = this, a.lat0 = 0, c.__super__.constructor.call(this, a), b.lam0 = 0
+ }
+
+ var b;
+ bO(c, a), c.title = "Aitoff Projection", c.parameters = ["lon0"], b = .6366197723675814, c.prototype.project = function (a, c) {
+ var d, e, f, g, h, i, j, k;
+ g = this, k = g.ll(a, c), a = k[0], c = k[1], a = g.clon(a), f = g.rad(a), h = g.rad(c), d = .5 * f, e = Math.acos(Math.cos(h) * Math.cos(d)), e !== 0 ? (j = 1 / Math.sin(e), i = 2 * e * Math.cos(h) * Math.sin(d) * j, j *= e * Math.sin(h)) : i = j = 0, g.winkel && (i = (i + f * b) * .5, j = (j + h) * .5);
+ return[i * 1e3, j * -1e3]
+ }, c.prototype._visible = function (a, b) {return!0};
+ return c
+ }(X), bD.aitoff = b, bo = function (a) {
+ function b(a) {b.__super__.constructor.call(this, a), this.winkel = !0}
+
+ bO(b, a), b.title = "Winkel Tripel Projection";
+ return b
+ }(b), bD.winkel3 = bo, n = function (a) {
+ function b(a) {
+ var c, d, e;
+ c = this, b.__super__.constructor.call(this, a), c.lat1 = (d = a.lat1) != null ? d : 30, c.phi1 = c.rad(c.lat1), c.lat2 = (e = a.lat2) != null ? e : 50, c.phi2 = c.rad(c.lat2)
+ }
+
+ bO(b, a), b.title = "Conic Projection", b.parameters = ["lon0", "lat0", "lat1", "lat2"], b.prototype._visible = function (a, b) {
+ var c;
+ c = this;
+ return b > c.minLat && b < c.maxLat
+ }, b.prototype._truncate = function (a, b) {return[a, b]}, b.prototype.clon = function (a) {
+ a -= this.lon0, a < -180 ? a += 360 : a > 180 && (a -= 360);
+ return a
+ };
+ return b
+ }(V), D = function (a) {
+ function b(a) {
+ var c, d, e, f, g, h, i, j, k, l, m, n, o;
+ k = this, b.__super__.constructor.call(this, a), g = Math, o = [g.sin, g.cos, g.abs, g.log, g.tan, g.pow], l = o[0], e = o[1], c = o[2], bs = o[3], n = o[4], i = o[5], k.n = h = m = l(k.phi1), f = e(k.phi1), j = c(k.phi1 - k.phi2) >= 1e-10, j && (h = bs(f / e(k.phi2)) / bs(n(k.QUARTERPI + .5 * k.phi2) / n(k.QUARTERPI + .5 * k.phi1))), k.c = d = f * i(n(k.QUARTERPI + .5 * k.phi1), h) / h, c(c(k.phi0) - k.HALFPI) < 1e-10 ? k.rho0 = 0 : k.rho0 = d * i(n(k.QUARTERPI + .5 * k.phi0), -h), k.minLat = -60, k.maxLat = 85
+ }
+
+ bO(b, a), b.title = "Lambert Conformal Conic Projection", b.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i, j, k, l, m, n, o, p, q;
+ l = this, i = l.rad(b), e = l.rad(l.clon(a)), g = Math, q = [g.sin, g.cos, g.abs, g.log, g.tan, g.pow], m = q[0], d = q[1], c = q[2], bs = q[3], n = q[4], j = q[5], h = l.n, c(c(i) - l.HALFPI) < 1e-10 ? k = 0 : k = l.c * j(n(l.QUARTERPI + .5 * i), -h), f = e * h, o = 1e3 * k * m(f), p = 1e3 * (l.rho0 - k * d(f));
+ return[o, p * -1]
+ };
+ return b
+ }(n), bD.lcc = D, W = function (a) {
+ function b() {return b.__super__.constructor.apply(this, arguments)}
+
+ bO(b, a);
+ return b
+ }(n), bl = function () {
+ function a(a, b, c, d, e, f) {
+ var g;
+ g = this, g.bbox = a, g.width = b, g.padding = d != null ? d : 0, g.halign = e != null ? e : "center", g.valign = f != null ? f : "center", g.height = c, g.scale = Math.min((b - d * 2) / a.width, (c - d * 2) / a.height)
+ }
+
+ a.prototype.project = function (a, b) {
+ var c, d, e, f, g, h, i;
+ b == null && (b = a[1], a = a[0]), e = this, f = e.scale, c = e.bbox, d = e.height, g = e.width, h = e.halign === "center" ? (g - c.width * f) * .5 : e.halign === "left" ? e.padding * f : g - (c.width - e.padding) * f, i = e.valign === "center" ? (d - c.height * f) * .5 : e.valign === "top" ? e.padding * f : 0, a = (a - c.left) * f + h, b = (b - c.top) * f + i;
+ return[a, b]
+ }, a.prototype.projectPath = function (a) {
+ var b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r;
+ e = this;
+ if (a.type === "path") {
+ d = [], b = [99999, 99999, -99999, -99999], o = a.contours;
+ for (k = 0, m = o.length; k < m; k++) {
+ g = o[k], c = [];
+ for (l = 0, n = g.length; l < n; l++)p = g[l], i = p[0], j = p[1], q = e.project(i, j), i = q[0], j = q[1], c.push([i, j]), b[0] = Math.min(b[0], i), b[1] = Math.min(b[1], j), b[2] = Math.max(b[2], i), b[3] = Math.max(b[3], j);
+ d.push(c)
+ }
+ f = new br.geom.Path(a.type, d, a.closed), f._bbox = b;
+ return f
+ }
+ if (a.type === "circle") {
+ r = e.project(a.x, a.y), i = r[0], j = r[1], h = a.r * e.scale;
+ return new br.geom.Circle(i, j, h)
+ }
+ }, a.prototype.asBBox = function () {
+ var a;
+ a = this;
+ return new br.BBox(0, 0, a.width, a.height)
+ };
+ return a
+ }(), bl.fromXML = function (a) {
+ var b, c, e, f, g;
+ g = Number(a.getAttribute("w")), e = Number(a.getAttribute("h")), f = Number(a.getAttribute("padding")), c = a.getElementsByTagName("bbox")[0], b = d.fromXML(c);
+ return new br.View(b, g, e, f)
+ }, br.View = bl, br.Kartograph.prototype.dotgrid = function (a) {
+ var b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X;
+ r = this, q = (T = a.layer) != null ? T : r.layerIds[r.layerIds.length - 1];
+ if (!r.layers.hasOwnProperty(q))bB('dotgrid error: layer "' + q + '" not found'); else {
+ p = r.layers[q], c = a.data, d = a.value, e = a.key, t = {};
+ if (e != null && bE(c) === "array")for (B = 0, F = c.length; B < F; B++)w = c[B], o = w[e], t[String(o)] = w; else for (o in c)w = c[o], t[String(o)] = w;
+ i = (U = a.style) != null ? U : {fill: "black", stroke: "none"}, y = a.size, n = (V = a.gridsize) != null ? V : 15, h = (W = p.dotgrid) != null ? W : p.dotgrid = {gridsize: n, grid: []};
+ if (h.gridsize !== n) {
+ X = h.grid;
+ for (C = 0, G = X.length; C < G; C++)m = X[C], m.shape != null && (m.shape.remove(), m.shape = null)
+ }
+ if (n > 0) {
+ if (h.grid.length === 0)for (z = D = 0, L = r.viewport.width; 0 <= L ? D <= L : D >= L; z = D += n)for (A = E = 0, M = r.viewport.height; 0 <= M ? E <= M : E >= M; A = E += n) {
+ m = {x: z + (Math.random() - .5) * n * .2, y: A + (Math.random() - .5) * n * .2, pathid: !1}, l = !1, N = p.pathsById;
+ for (o in N) {
+ u = N[o];
+ for (J = 0, H = u.length; J < H; J++) {
+ s = u[J];
+ if (s.vpath.isInside(m.x, m.y)) {
+ l = !0, v = (O = t[o]) != null ? O : null, x = y(v), m.pathid = o, m.shape = p.paper.circle(m.x, m.y, 1);
+ break
+ }
+ }
+ if (l)break
+ }
+ h.grid.push(m)
+ }
+ P = h.grid;
+ for (K = 0, I = P.length; K < I; K++)m = P[K], m.pathid && (v = (Q = t[m.pathid]) != null ? Q : null, x = y(v), k = (R = a.duration) != null ? R : 0, f = (S = a.delay) != null ? S : 0, bE(f) === "function" ? g = f(v) : g = f, k > 0 && Raphael.svg ? (b = Raphael.animation({r: x * .5}, k), m.shape.animate(b.delay(g))) : m.shape.attr({r: x * .5}), bE(i) === "function" ? j = i(v) : j = i, m.shape.attr(j))
+ }
+ }
+ }, bq = (bM = br.filter) != null ? bM : br.filter = {}, bq.__knownFilter = {}, bq.__patternFills = 0, L.prototype.SVG = function (a, b) {
+ var c, d;
+ typeof a == "string" && (a = window.document.createElementNS("http://www.w3.org/2000/svg", a));
+ if (b)for (c in b)d = b[c], a.setAttribute(c, d);
+ return a
+ }, br.Kartograph.prototype.addFilter = function (a, b, c) {
+ var d, e, f;
+ c == null && (c = {}), f = this, d = window.document;
+ if (br.filter[b] != null)e = (new br.filter[b](c)).getFilter(a); else throw"unknown filter type " + b;
+ return f.paper.defs.appendChild(e)
+ }, L.prototype.applyFilter = function (b) {
+ var c;
+ c = this;
+ return a("." + c.id, c.paper.canvas).attr({filter: "url(#" + b + ")"})
+ }, L.prototype.applyTexture = function (a, b, c) {
+ var d, e, f, g, h, i;
+ b == null && (b = !1), c == null && (c = "#000"), e = this, bq.__patternFills += 1, h = e.paths, i = [];
+ for (f = 0, g = h.length; f < g; f++)d = h[f], !b || b(d.data) ? i.push(d.svgPath.attr({fill: "url(" + a + ")"})) : i.push(d.svgPath.attr("fill", c));
+ return i
+ }, t = function () {
+ function a(a) {this.params = a != null ? a : {}}
+
+ a.prototype.getFilter = function (a) {
+ var b, c;
+ c = this, b = c.SVG("filter", {id: a}), c.buildFilter(b);
+ return b
+ }, a.prototype._getFilter = function () {throw"not implemented"}, a.prototype.SVG = function (a, b) {
+ var c, d;
+ typeof a == "string" && (a = window.document.createElementNS("http://www.w3.org/2000/svg", a));
+ if (b)for (c in b)d = b[c], a.setAttribute(c, d);
+ return a
+ };
+ return a
+ }(), g = function (a) {
+ function b() {return b.__super__.constructor.apply(this, arguments)}
+
+ bO(b, a), b.prototype.buildFilter = function (a) {
+ var b, c, d;
+ d = this, b = d.SVG, c = b("feGaussianBlur", {stdDeviation: d.params.size || 4, result: "blur"});
+ return a.appendChild(c)
+ };
+ return b
+ }(t), bq.blur = g, v = function (a) {
+ function b() {return b.__super__.constructor.apply(this, arguments)}
+
+ bO(b, a), b.prototype.buildFilter = function (a) {
+ var b, c, d, e, f, g, h, i, j, k, l, m, n, o;
+ g = this, c = (l = g.params.blur) != null ? l : 4, i = (m = g.params.strength) != null ? m : 1, d = (n = g.params.color) != null ? n : "#D1BEB0", typeof d == "string" && (d = chroma.hex(d)), h = d.rgb, e = (o = g.params.inner) != null ? o : !1, f = (j = g.params.knockout) != null ? j : !1, b = (k = g.params.alpha) != null ? k : 1, e ? g.innerGlow(a, c, i, h, b, f) : g.outerGlow(a, c, i, h, b, f)
+ }, b.prototype.outerGlow = function (a, b, c, d, e, f) {
+ var g, h, i, j, k, l, m;
+ k = this, g = k.SVG, j = g("feColorMatrix", {"in": "SourceGraphic", type: "matrix", values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0", result: "mask"}), a.appendChild(j), c > 0 && (m = g("feMorphology", {"in": "mask", radius: c, operator: "dilate", result: "mask"}), a.appendChild(m)), j = g("feColorMatrix", {"in": "mask", type: "matrix", values: "0 0 0 0 " + d[0] / 255 + " 0 0 0 0 " + d[1] / 255 + " 0 0 0 0 " + d[2] / 255 + " 0 0 0 1 0", result: "r0"}), a.appendChild(j), h = g("feGaussianBlur", {"in": "r0", stdDeviation: b, result: "r1"}), a.appendChild(h), i = g("feComposite", {operator: "out", "in": "r1", in2: "mask", result: "comp"}), a.appendChild(i), l = g("feMerge"), f || l.appendChild(g("feMergeNode", {"in": "SourceGraphic"})), l.appendChild(g("feMergeNode", {"in": "r1"}));
+ return a.appendChild(l)
+ }, b.prototype.innerGlow = function (a, b, c, d, e, f) {
+ var g, h, i, j, k, l, m;
+ k = this, g = k.SVG, bs("innerglow"), j = g("feColorMatrix", {"in": "SourceGraphic", type: "matrix", values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 0", result: "mask"}), a.appendChild(j), m = g("feMorphology", {"in": "mask", radius: c, operator: "erode", result: "r1"}), a.appendChild(m), h = g("feGaussianBlur", {"in": "r1", stdDeviation: b, result: "r2"}), a.appendChild(h), j = g("feColorMatrix", {type: "matrix", "in": "r2", values: "1 0 0 0 " + d[0] / 255 + " 0 1 0 0 " + d[1] / 255 + " 0 0 1 0 " + d[2] / 255 + " 0 0 0 -1 1", result: "r3"}), a.appendChild(j), i = g("feComposite", {operator: "in", "in": "r3", in2: "mask", result: "comp"}), a.appendChild(i), l = g("feMerge"), f || l.appendChild(g("feMergeNode", {"in": "SourceGraphic"})), l.appendChild(g("feMergeNode", {"in": "comp"}));
+ return a.appendChild(l)
+ };
+ return b
+ }(t), bq.glow = v, br.Kartograph.prototype.addGeoPath = function (a, b, c) {
+ var d, e, f;
+ b == null && (b = []), c == null && (c = ""), d = this, f = d.getGeoPathStr(a, b), e = d.paper.path(f), c !== "" && e.node.setAttribute("class", c);
+ return e
+ }, br.Kartograph.prototype.getGeoPathStr = function (a, b) {
+ var c, d, e, f, g, h, i;
+ b == null && (b = []), e = this, type(b) === "string" && (b = b.split("")), b.length === 0 && b.push("M"), f = "";
+ for (d in a) {
+ g = a[d], c = (i = b[d]) != null ? i : "L", h = e.lonlat2xy(g);
+ if (isNaN(h[0]) || isNaN(h[1]))continue;
+ f += c + h[0] + "," + h[1]
+ }
+ return f
+ }, br.Kartograph.prototype.addGeoPolygon = function (a, b) {
+ var c, d, e;
+ e = this, c = ["M"];
+ for (d in a)c.push("L");
+ c.push("Z");
+ return e.addGeoPath(a, c, b)
+ }, S = function () {
+ function b(b) {
+ this.zoomOut = bP(this.zoomOut, this), this.zoomIn = bP(this.zoomIn, this);
+ var c, d, e, f, g, h, i, j;
+ f = this, f.map = b, c = b.container, d = function (b, c) {
+ var d, e, f, g;
+ c == null && (c = []), e = a('<div class="' + b + '" />');
+ for (f = 0, g = c.length; f < g; f++)d = c[f], e.append(d);
+ return e
+ }, e = function (b) {return a(b.target).addClass("md")}, g = function (b) {return a(b.target).removeClass("md")}, j = d("plus"), j.mousedown(e), j.mouseup(g), j.click(f.zoomIn), i = d("minus"), i.mousedown(e), i.mouseup(g), i.click(f.zoomOut), h = d("zoom-control", [j, i]), c.append(h)
+ }
+
+ b.prototype.zoomIn = function (a) {
+ var b;
+ b = this, b.map.opts.zoom += 1;
+ return b.map.resize()
+ }, b.prototype.zoomOut = function (a) {
+ var b;
+ b = this, b.map.opts.zoom -= 1, b.map.opts.zoom < 1 && (b.map.opts.zoom = 1);
+ return b.map.resize()
+ };
+ return b
+ }(), bd = function () {
+ function a(a, b, c) {
+ var d, e, f, g, h = this;
+ a == null && (a = [0, 1]), b == null && (b = null), c == null && (c = null), this.rangedScale = bP(this.rangedScale, this), this.scale = bP(this.scale, this), e = this, g = [];
+ for (d in a) {
+ if (bE(c) === "function" && c(a[d]) === !1)continue;
+ b != null ? bE(b) === "function" ? f = b(a[d]) : f = a[d][b] : f = a[d], isNaN(f) || g.push(f)
+ }
+ g = g.sort(function (a, b) {return a - b}), e.values = g, e._range = [0, 1], e.rangedScale.range = function (a) {
+ e._range = a;
+ return e.rangedScale
+ }
+ }
+
+ a.prototype.scale = function (a) {return a}, a.prototype.rangedScale = function (a) {
+ var b, c;
+ b = this, a = b.scale(a), c = b._range;
+ return a * (c[1] - c[0]) + c[0]
+ };
+ return a
+ }(), H = function (a) {
+ function b() {
+ this.scale = bP(this.scale, this);
+ return b.__super__.constructor.apply(this, arguments)
+ }
+
+ bO(b, a), b.prototype.scale = function (a) {
+ var b, c;
+ b = this, c = b.values;
+ return(a - c[0]) / (c[c.length - 1] - c[0])
+ };
+ return b
+ }(bd), I = function (a) {
+ function b() {
+ this.scale = bP(this.scale, this);
+ return b.__super__.constructor.apply(this, arguments)
+ }
+
+ bO(b, a), b.prototype.scale = function (a) {
+ var b, c;
+ b = this, c = b.values, bs = Math.log;
+ return(bs(a) - bs(c[0])) / (bs(c[c.length - 1]) - bs(c[0]))
+ };
+ return b
+ }(bd), bf = function (a) {
+ function b() {
+ this.scale = bP(this.scale, this);
+ return b.__super__.constructor.apply(this, arguments)
+ }
+
+ bO(b, a), b.prototype.scale = function (a) {
+ var b, c;
+ b = this, c = b.values;
+ return Math.sqrt((a - c[0]) / (c[c.length - 1] - c[0]))
+ };
+ return b
+ }(bd), Y = function (a) {
+ function b() {
+ this.scale = bP(this.scale, this);
+ return b.__super__.constructor.apply(this, arguments)
+ }
+
+ bO(b, a), b.prototype.scale = function (a) {
+ var b, c, d, e, f, g;
+ d = this, g = d.values, c = g.length - 1;
+ for (b in g) {
+ f = g[Number(b)], e = g[Number(b) + 1];
+ if (a === f)return b / c;
+ if (b < c && a > f && a < e)return b / c + (a - f) / (e - f)
+ }
+ };
+ return b
+ }(bd), br.scale = {}, br.scale.identity = function (a) {return(new bd(domain, prop, bq)).rangedScale}, br.scale.linear = function (a, b, c) {return(new H(a, b, c)).rangedScale}, br.scale.log = function (a, b, c) {return(new I(a, b, c)).rangedScale}, br.scale.sqrt = function (a, b, c) {return(new bf(a, b, c)).rangedScale}, br.scale.quantile = function (a, b, c) {return(new Y(a, b, c)).rangedScale}, bj = function () {
+ function b(b) {a = this, a.location = b.location, a.data = b.data, a.map = b.map, a.layers = b.layers, a.key = b.key, a.x = b.x, a.y = b.y}
+
+ var a;
+ a = null, b.prototype.init = function () {return a}, b.prototype.overlaps = function (a) {return!1}, b.prototype.update = function (b) {return a}, b.prototype.nodes = function () {return[]}, b.prototype.clear = function () {return a};
+ return b
+ }(), br.Symbol = bj, bk = function () {
+ function c(a) {
+ this._initTooltips = bP(this._initTooltips, this), this._noverlap = bP(this._noverlap, this), this._kMeans = bP(this._kMeans, this);
+ var d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w;
+ b = this, m = ["data", "location", "type", "map"], k = ["filter", "tooltip", "click", "delay", "sortBy", "clustering", "aggregate", "clusteringOpts", "mouseenter", "mouseleave"];
+ for (n = 0, r = m.length; n < r; n++) {
+ l = m[n];
+ if (a[l] != null)b[l] = a[l]; else throw"SymbolGroup: missing argument '" + l + "'"
+ }
+ for (o = 0, s = k.length; o < s; o++)l = k[o], a[l] != null && (b[l] = a[l]);
+ d = b.type;
+ if (d == null)bB("could not resolve symbol type", b.type); else {
+ v = d.props;
+ for (p = 0, t = v.length; p < t; p++)l = v[p], a[l] != null && (b[l] = a[l]);
+ b.layers = {mapcanvas: b.map.paper}, w = d.layers;
+ for (q = 0, u = w.length; q < u; q++)h = w[q], j = c._layerid++, g = "sl_" + j, h.type === "svg" ? i = b.map.createSVGLayer(g) : h.type === "html" && (i = b.map.createHTMLLayer(g)), b.layers[h.id] = i;
+ b.symbols = [];
+ for (f in b.data)e = b.data[f], bE(b.filter) === "function" ? b.filter(e, f) && b.add(e, f) : b.add(e, f);
+ b.layout(), b.render(), b.map.addSymbolGroup(b)
+ }
+ }
+
+ var b;
+ b = null, c.prototype.add = function (a, c) {
+ var d, e, f, g, h, i, j, k;
+ b = this, d = b.type, e = b._evaluate(b.location, a, c), bE(e) === "array" && (e = new br.LonLat(e[0], e[1])), g = {layers: b.layers, location: e, data: a, key: c != null ? c : b.symbols.length, map: b.map}, k = d.props;
+ for (i = 0, j = k.length; i < j; i++)f = k[i], b[f] != null && (g[f] = b._evaluate(b[f], a, c));
+ h = new d(g), b.symbols.push(h);
+ return h
+ }, c.prototype.layout = function () {
+ var a, c, d, e, f, g, h, i, j, k;
+ j = b.symbols;
+ for (h = 0, i = j.length; h < i; h++) {
+ f = j[h], c = f.location;
+ if (bE(c) === "string") {
+ k = c.split("."), a = k[0], e = k[1], d = b.map.getLayerPath(a, e);
+ if (d != null)g = b.map.viewBC.project(d.path.centroid()); else {
+ bB("could not find layer path " + a + "." + e);
+ continue
+ }
+ } else g = b.map.lonlat2xy(c);
+ f.x = g[0], f.y = g[1]
+ }
+ b.clustering === "k-means" ? b._kMeans() : b.clustering === "noverlap" && b._noverlap();
+ return b
+ }, c.prototype.render = function () {
+ var c, d, e, f, g, h, i, j, k, l, m;
+ b = this, b.sortBy && (f = "asc", bE(b.sortBy) === "string" && (b.sortBy = b.sortBy.split(" ", 2), e = b.sortBy[0], f = (k = b.sortBy[1]) != null ? k : "asc"), b.symbols = b.symbols.sort(function (a, c) {
+ var d, g, h;
+ bE(b.sortBy) === "function" ? (g = b.sortBy(a.data, a), h = b.sortBy(c.data, c)) : (g = a[e], h = c[e]);
+ if (g === h)return 0;
+ d = f === "asc" ? 1 : -1;
+ return g > h ? 1 * d : -1 * d
+ })), l = b.symbols;
+ for (g = 0, i = l.length; g < i; g++) {
+ d = l[g], d.render(), m = d.nodes();
+ for (h = 0, j = m.length; h < j; h++)c = m[h], c.symbol = d
+ }
+ bE(b.tooltip) === "function" && b._initTooltips(), a.each(["click", "mouseenter", "mouseleave"], function (e, f) {
+ var g, h, i, j;
+ if (bE(b[f]) === "function") {
+ i = b.symbols, j = [];
+ for (g = 0, h = i.length; g < h; g++)d = i[g], j.push(function () {
+ var e, g, h, i, j = this;
+ h = d.nodes(), i = [];
+ for (e = 0, g = h.length; e < g; e++)c = h[e], i.push(a(c)[f](function (c) {
+ var d;
+ d = c.target;
+ while (!d.symbol)d = a(d).parent().get(0);
+ c.stopPropagation();
+ return b[f](d.symbol.data, d.symbol, c)
+ }));
+ return i
+ }.call(this));
+ return j
+ }
+ });
+ return b
+ }, c.prototype.tooltips = function (a) {
+ b = this, b.tooltips = a, b._initTooltips();
+ return b
+ }, c.prototype.remove = function (a) {
+ var c, d, e, f, g, h, i, j, k;
+ b = this, d = [], i = b.symbols;
+ for (g = 0, h = i.length; g < h; g++) {
+ f = i[g];
+ if (a != null && !a(f.data)) {
+ d.push(f);
+ continue
+ }
+ try {f.clear()} catch (l) {bB("error: symbolgroup.remove")}
+ }
+ if (a == null) {
+ j = b.layers, k = [];
+ for (c in j)e = j[c], c !== "mapcanvas" ? k.push(e.remove()) : k.push(void 0);
+ return k
+ }
+ return b.symbols = d
+ }, c.prototype._evaluate = function (a, b, c) {
+ var d;
+ return bE(a) === "function" ? d = a(b, c) : d = a
+ }, c.prototype._kMeans = function () {
+ var a, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x;
+ b = this, (u = b.osymbols) == null && (b.osymbols = b.symbols), a = b.type, b.clusteringOpts != null && (k = b.clusteringOpts.size), k == null && (k = 64), c = bR().iterations(16).size(k), v = b.osymbols;
+ for (m = 0, q = v.length; m < q; m++)j = v[m], c.add({x: j.x, y: j.y});
+ g = c.means(), h = [];
+ for (n = 0, r = g.length; n < r; n++) {
+ f = g[n];
+ if (f.size === 0)continue;
+ d = [], w = f.indices;
+ for (o = 0, s = w.length; o < s; o++)e = w[o], d.push(b.osymbols[e].data);
+ d = b.aggregate(d), l = {layers: b.layers, location: !1, data: d, map: b.map}, x = a.props;
+ for (p = 0, t = x.length; p < t; p++)i = x[p], b[i] != null && (l[i] = b._evaluate(b[i], d));
+ j = new a(l), j.x = f.x, j.y = f.y, h.push(j)
+ }
+ return b.symbols = h
+ }, c.prototype._noverlap = function () {
+ var a, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V;
+ b = this, (S = b.osymbols) == null && (b.osymbols = b.symbols), j = 3, a = b.type;
+ if (bQ.call(a.props, "radius") < 0)bB('noverlap layout only available for symbols with property "radius"'); else {
+ A = b.osymbols.slice(), b.clusteringOpts != null && (D = b.clusteringOpts.tolerance, n = b.clusteringOpts.maxRatio), D == null && (D = .05), n == null && (n = .8);
+ for (h = H = 0, T = j - 1; 0 <= T ? H <= T : H >= T; h = 0 <= T ? ++H : --H) {
+ A.sort(function (a, b) {return b.radius - a.radius}), k = A.length, o = [];
+ for (p = I = 0, U = k - 3; 0 <= U ? I <= U : I >= U; p = 0 <= U ? ++I : --I) {
+ x = A[p];
+ if (!x)continue;
+ u = x.radius * (1 - D), l = x.x - u, s = x.x + u, B = x.y - u, c = x.y + u, i = [];
+ for (q = J = V = p + 1, Q = k - 2; V <= Q ? J <= Q : J >= Q; q = V <= Q ? ++J : --J) {
+ y = A[q];
+ if (!y)continue;
+ v = y.radius, m = y.x - v, t = y.x + v, C = y.y - v, d = y.y + v, v / x.radius < n && !(s < m || t < l) && !(c < C || d < B) && (f = y.x - x.x, g = y.y - x.y, f * f + g * g < (u + v) * (u + v) && i.push(q))
+ }
+ if (i.length > 0) {
+ e = [x.data], r = x.radius * x.radius;
+ for (K = 0, L = i.length; K < L; K++)h = i[K], e.push(A[h].data), r += A[h].radius * A[h].radius;
+ e = b.aggregate(e), z = {layers: b.layers, location: !1, data: e, map: b.map}, R = a.props;
+ for (O = 0, M = R.length; O < M; O++)p = R[O], b[p] != null && (z[p] = b._evaluate(b[p], e));
+ w = new a(z), E = x.radius * x.radius / r, F = x.x * E, G = x.y * E;
+ for (P = 0, N = i.length; P < N; P++)h = i[P], y = A[h], E = y.radius * y.radius / r, F += y.x * E, G += y.y * E, A[h] = void 0;
+ w.x = F, w.y = G, A[p] = void 0, o.push(w)
+ } else o.push(x)
+ }
+ A = o
+ }
+ return b.symbols = A
+ }
+ }, c.prototype._initTooltips = function () {
+ var c, d, e, f, g, h, i, j, k, l, m;
+ b = this, f = b.tooltip, l = b.symbols;
+ for (h = 0, j = l.length; h < j; h++) {
+ e = l[h], c = {position: {target: "mouse", viewport: a(window), adjust: {x: 7, y: 7}}, show: {delay: 20}, content: {}, events: {show: function (b, c) {return a(".qtip").filter(function () {return this !== c.elements.tooltip.get(0)}).hide()}}}, g = f(e.data, e.key), bE(g) === "string" ? c.content.text = g : bE(g) === "array" && (c.content.title = g[0], c.content.text = g[1]), m = e.nodes();
+ for (i = 0, k = m.length; i < k; i++)d = m[i], a(d).qtip(c)
+ }
+ }, c.prototype.onResize = function () {
+ var a, c, d, e;
+ b = this, b.layout(), e = b.symbols;
+ for (c = 0, d = e.length; c < d; c++)a = e[c], a.update()
+ }, c.prototype.update = function (a, c, d) {
+ var e, f, g, h, i, j, k, l;
+ b = this, a == null && (a = {}), k = b.symbols;
+ for (g = 0, i = k.length; g < i; g++) {
+ f = k[g], l = b.type.props;
+ for (h = 0, j = l.length; h < j; h++)e = l[h], a[e] != null ? f[e] = b._evaluate(a[e], f.data) : b[e] != null && (f[e] = b._evaluate(b[e], f.data));
+ f.update(c, d)
+ }
+ return b
+ };
+ return c
+ }(), bk._layerid = 0, br.SymbolGroup = bk, br.Kartograph.prototype.addSymbols = function (a) {
+ a.map = this;
+ return new bk(a)
+ }, br.dorlingLayout = function (b, c) {
+ var d, e, f, g, h, i, j, k, l, m, n, o, p, q, r;
+ c == null && (c = 40), n = [], a.each(b.symbols, function (a, b) {return n.push({i: a, x: b.path.attrs.cx, y: b.path.attrs.cy, r: b.path.attrs.r})}), n.sort(function (a, b) {return b.r - a.r}), f = function () {
+ var a, c, d;
+ for (c = 0, d = n.length; c < d; c++)a = n[c], b.symbols[a.i].path.attr({cx: a.x, cy: a.y})
+ };
+ for (o = r = 1; 1 <= c ? r <= c : r >= c; o = 1 <= c ? ++r : --r)for (l in n)for (m in n)if (m > l) {
+ d = n[l], e = n[m];
+ if (d.x + d.r < e.x - e.r || d.x - d.r > e.x + e.r)continue;
+ if (d.y + d.r < e.y - e.r || d.y - d.r > e.y + e.r)continue;
+ i = d.x - e.x, j = d.y - e.y, h = i * i + j * j, p = d.r + e.r, q = p * p, h < q && (g = Math.sqrt(h), k = 10 / g, d.x += i * k * (1 - d.r / p), d.y += j * k * (1 - d.r / p), e.x -= i * k * (1 - e.r / p), e.y -= j * k * (1 - e.r / p))
+ }
+ return f()
+ }, h = function (b) {
+ function c(a) {
+ this.nodes = bP(this.nodes, this), this.clear = bP(this.clear, this), this.update = bP(this.update, this), this.render = bP(this.render, this), this.overlaps = bP(this.overlaps, this);
+ var b, d, e;
+ b = this, c.__super__.constructor.call(this, a), b.radius = (d = a.radius) != null ? d : 4, b.style = a.style, b.attrs = a.attrs, b.title = a.title, b["class"] = (e = a["class"]) != null ? e : "bubble"
+ }
+
+ bO(c, b), c.prototype.overlaps = function (a) {
+ var b, c, d, e, f, g, h, i, j, k, l;
+ d = this, k = [d.x, d.y, d.radius], g = k[0], i = k[1], e = k[2], l = [a.x, a.y, a.radius], h = l[0], j = l[1], f = l[2];
+ if (g - e > h + f || g + e < h - f || i - e > j + f || i + e < j - f)return!1;
+ b = g - h, c = i - j;
+ return b * b + c * c > (e + f) * (e + f) ? !1 : !0
+ }, c.prototype.render = function (a) {
+ var b;
+ b = this, b.path == null && (b.path = b.layers.mapcanvas.circle(b.x, b.y, b.radius)), b.update(), b.map.applyCSS(b.path);
+ return b
+ }, c.prototype.update = function (b, c) {
+ var d, e, f;
+ b == null && (b = !1), c == null && (c = "expo-out"), e = this, f = e.path, d = {cx: e.x, cy: e.y, r: e.radius}, e.attrs != null && (d = a.extend(d, e.attrs)), b ? f.animate(d, b, c) : f.attr(d), f.node != null && (e.style != null && f.node.setAttribute("style", e.style), e["class"] != null && f.node.setAttribute("class", e["class"])), e.title != null && f.attr("title", e.title);
+ return e
+ }, c.prototype.clear = function () {
+ var a;
+ a = this, a.path.remove();
+ return a
+ }, c.prototype.nodes = function () {
+ var a;
+ a = this;
+ return[a.path.node]
+ };
+ return c
+ }(bj), h.props = ["radius", "style", "class", "title", "attrs"], h.layers = [], br.Bubble = h, A = function (b) {
+ function c(a) {
+ var b, d, e, f, g, h;
+ b = this, c.__super__.constructor.call(this, a), b.icon = (e = a.icon) != null ? e : "", b.offset = (f = a.offset) != null ? f : [0, 0], b.iconsize = (g = a.iconsize) != null ? g : [10, 10], b["class"] = (h = a["class"]) != null ? h : "", b.title = (d = a.title) != null ? d : ""
+ }
+
+ bO(c, b), c.prototype.render = function (b) {
+ var c, d;
+ d = this, c = d.map.container, d.img = a("<img />"), d.img.attr({src: d.icon, title: d.title, alt: d.title, width: d.iconsize[0], height: d.iconsize[1]}), d.img.addClass(d["class"]), d.img.css({position: "absolute", "z-index": 1e3, cursor: "pointer"}), d.img[0].symbol = d, c.append(d.img);
+ return d.update()
+ }, c.prototype.update = function () {
+ var a;
+ a = this;
+ return a.img.css({left: a.x + a.offset[0] + "px", top: a.y + a.offset[1] + "px"})
+ }, c.prototype.clear = function () {
+ var a;
+ a = this, a.img.remove();
+ return a
+ }, c.prototype.nodes = function () {
+ var a;
+ a = this;
+ return[a.img]
+ };
+ return c
+ }(br.Symbol), A.props = ["icon", "offset", "class", "title", "iconsize"], A.layers = [], br.Icon = A, bi = function (a) {
+ function b(a) {
+ var c, d, e, f, g;
+ c = this, b.__super__.constructor.call(this, a), c.text = (d = a.text) != null ? d : "", c.style = (e = a.style) != null ? e : "", c["class"] = (f = a["class"]) != null ? f : "", c.offset = (g = a.offset) != null ? g : [0, 0]
+ }
+
+ bO(b, a), b.prototype.render = function (a) {
+ var b, c;
+ c = this, c.lbl = b = c.layers.mapcanvas.text(c.x, c.y, c.text), c.update();
+ return c
+ }, b.prototype.update = function () {
+ var a;
+ a = this, a.lbl.attr({x: a.x + a.offset[0], y: a.y + a.offset[1]}), a.lbl.node.setAttribute("style", a.style);
+ return a.lbl.node.setAttribute("class", a["class"])
+ }, b.prototype.clear = function () {
+ var a;
+ a = this, a.lbl.remove();
+ return a
+ }, b.prototype.nodes = function () {
+ var a;
+ a = this;
+ return[a.lbl.node]
+ };
+ return b
+ }(br.Symbol), bi.props = ["text", "style", "class", "offset"], bi.layers = [], br.Label = bi, z = function (b) {
+ function c(a) {
+ var b, d, e, f;
+ b = this, c.__super__.constructor.call(this, a), b.text = (d = a.text) != null ? d : "", b.style = (e = a.style) != null ? e : "", b["class"] = (f = a["class"]) != null ? f : ""
+ }
+
+ bO(c, b), c.prototype.render = function (b) {
+ var c, d, e;
+ e = this, c = a("<div>" + e.text + "</div>"), c.css({width: "50px", position: "absolute", left: "-25px", "text-align": "center"}), e.lbl = d = a('<div class="label" />'), d.append(c), e.layers.lbl.append(d), c.css({height: c.height() + "px", top: c.height() * -0.4 + "px"}), e.update();
+ return e
+ }, c.prototype.update = function () {
+ var a;
+ a = this;
+ return a.lbl.css({position: "absolute", left: a.x + "px", top: a.y + "px"})
+ }, c.prototype.clear = function () {
+ var a;
+ a = this, a.lbl.remove();
+ return a
+ }, c.prototype.nodes = function () {
+ var a;
+ a = this;
+ return[a.lbl[0]]
+ };
+ return c
+ }(br.Symbol), z.props = ["text", "style", "class"], z.layers = [
+ {id: "lbl", type: "html"}
+ ], br.HtmlLabel = z, E = function (b) {
+ function c(a) {
+ this.nodes = bP(this.nodes, this), this.clear = bP(this.clear, this), this.update = bP(this.update, this), this.render = bP(this.render, this);
+ var b, d, e;
+ b = this, c.__super__.constructor.call(this, a), b.labelattrs = (d = a.labelattrs) != null ? d : {}, b.buffer = a.buffer, b.center = (e = a.center) != null ? e : !0
+ }
+
+ bO(c, b), c.prototype.render = function (a) {
+ var b;
+ b = this, b.title != null && String(b.title).trim() !== "" && (b.buffer && (b.bufferlabel = b.layers.mapcanvas.text(b.x, b.y, b.title)), b.label = b.layers.mapcanvas.text(b.x, b.y, b.title)), c.__super__.render.call(this, a);
+ return b
+ }, c.prototype.update = function (b, d) {
+ var e, f, g, h, i;
+ b == null && (b = !1), d == null && (d = "expo-out"), f = this, c.__super__.update.call(this, b, d), f.label != null && (g = f.map.viewport, e = a.extend({}, f.labelattrs), h = f.x, i = f.y, f.center ? i -= 0 : h > g.width * .5 ? (e["text-anchor"] = "end", h -= f.radius + 5) : h < g.width * .5 && (e["text-anchor"] = "start", h += f.radius + 5), e.x = h, e.y = i, f.buffer && (f.bufferlabel.attr(e), f.bufferlabel.attr({stroke: "#fff", fill: "#fff", "stroke-linejoin": "round", "stroke-linecap": "round", "stroke-width": 6})), f.label.attr(e), f.label.toFront());
+ return f
+ }, c.prototype.clear = function () {
+ var a;
+ a = this;
+ return c.__super__.clear.apply(this, arguments)
+ }, c.prototype.nodes = function () {
+ var a, b;
+ a = this, b = c.__super__.nodes.apply(this, arguments), a.label && b.push(a.label.node), a.bufferlabel && b.push(a.bufferlabel.node);
+ return b
+ };
+ return c
+ }(h), E.props = ["radius", "style", "class", "title", "labelattrs", "buffer", "center", "attrs"], E.layers = [], br.LabeledBubble = E, U = function (a) {
+ function c(a) {
+ var d, e, f, g, h, i, j, k, l, m;
+ b = this, c.__super__.constructor.call(this, a), b.radius = (j = a.radius) != null ? j : 4, b.styles = (k = a.styles) != null ? k : "", b.colors = (l = a.colors) != null ? l : ["#3cc", "#c3c", "#33c", "#cc3"], b.titles = (m = a.titles) != null ? m : ["", "", "", "", ""], b.values = (e = a.values) != null ? e : [], b.border = (f = a.border) != null ? f : !1, b.borderWidth = (g = a.borderWidth) != null ? g : 2, b["class"] = (h = a["class"]) != null ? h : "piechart", (i = (d = Raphael.fn).pieChart) == null && (d.pieChart = bp)
+ }
+
+ var b;
+ bO(c, a), b = null, c.prototype.overlaps = function (a) {
+ var c, d, e, f, g, h, i, j, k, l;
+ k = [b.x, b.y, b.radius], g = k[0], i = k[1], e = k[2], l = [a.x, a.y, a.radius], h = l[0], j = l[1], f = l[2];
+ if (g - e > h + f || g + e < h - f || i - e > j + f || i + e < j - f)return!1;
+ c = g - h, d = i - j;
+ return c * c + d * d > (e + f) * (e + f) ? !1 : !0
+ }, c.prototype.render = function (a) {
+ var c;
+ b = this, b.border != null && (c = b.layers.mapcanvas.circle(b.x, b.y, b.radius + b.borderWidth).attr({stroke: "none", fill: b.border})), b.chart = b.layers.mapcanvas.pieChart(b.x, b.y, b.radius, b.values, b.titles, b.colors, "none"), b.chart.push(c);
+ return b
+ }, c.prototype.update = function (a) {
+ var c;
+ return
+ }, c.prototype.clear = function () {
+ var a, c, d, e;
+ b = this, e = b.chart;
+ for (c = 0, d = e.length; c < d; c++)a = e[c], a.remove();
+ return b
+ }, c.prototype.nodes = function () {
+ var a, c, d, e, f;
+ e = b.chart, f = [];
+ for (c = 0, d = e.length; c < d; c++)a = e[c], f.push(a.node);
+ return f
+ };
+ return c
+ }(bj), U.props = ["radius", "values", "styles", "class", "titles", "colors", "border", "borderWidth"], U.layers = [], br.PieChart = U, bp = function (a, b, c, d, e, f, g) {
+ var h, i, j, k, l, m, n, o, p, q, r;
+ if (isNaN(a) || isNaN(b) || isNaN(c))return[];
+ k = this, m = Math.PI / 180, i = k.set(), n = function (a, b, c, d, e, f) {
+ var g, h, i, j;
+ g = a + c * Math.cos(-d * m), h = a + c * Math.cos(-e * m), i = b + c * Math.sin(-d * m), j = b + c * Math.sin(-e * m);
+ return k.path(["M", a, b, "L", g, i, "A", c, c, 0, +(e - d > 180), 0, h, j, "z"]).attr(f)
+ }, h = -270, o = 0, l = function (e) {
+ var j, k, l, m, p, q, r;
+ r = d[e], j = 360 * r / o, q = h + j * .5, k = f[e], m = 500, l = 30, p = n(a, b, c, h, h + j, {fill: k, stroke: g, "stroke-width": 1}), p.mouseover(function () {p.stop().animate({transform: "s1.1 1.1 " + a + " " + b}, m, "elastic")}), p.mouseout(function () {p.stop().animate({transform: ""}, m, "elastic")}), h += j, i.push(p
+ )
+ };
+ for (q = 0, r = d.length; q < r; q++)p = d[q], o += p;
+ for (j in d)l(j);
+ return i
+ }, drawStackedBars = function (a, b, c, d, e, f, g, h) {
+ function k(a, b, c, d, e) {return i.rect(a, b, c, d).attr(e)}
+
+ var i = this, j = this.set(), l = 0, m = 0, n = function (f) {
+ var i = e[f], n = d * i / m, o = a - c * .5, p = b + d * .5 - l, q = c, r = g[f], s = 500, t = 30, u = k(o, p - n, q, n, {fill: r, stroke: h, "stroke-width": 1});
+ l += n, u.mouseover(function () {u.stop().animate({transform: "s1.1 1.1 " + a + " " + b}, s, "elastic")}).mouseout(function () {u.stop().animate({transform: ""}, s, "elastic")}), j.push(u)
+ };
+ for (var o = 0, p = e.length; o < p; o++)m += e[o];
+ for (o = 0; o < p; o++)n(o);
+ return j
+ }, bg = function (a) {
+ function b(a) {
+ var c, d, e, f, g, h, i, j, k, l;
+ c = this, b.__super__.constructor.call(this, a), c.styles = (i = a.styles) != null ? i : "", c.colors = (j = a.colors) != null ? j : [], c.titles = (k = a.titles) != null ? k : ["", "", "", "", ""], c.values = (l = a.values) != null ? l : [], c.width = (e = a.width) != null ? e : 17, c.height = (f = a.height) != null ? f : 30, c["class"] = (g = a["class"]) != null ? g : "barchart", (h = (d = Raphael.fn).drawStackedBarChart) == null && (d.drawStackedBarChart = drawStackedBars)
+ }
+
+ bO(b, a), b.prototype.overlaps = function (a) {
+ var b, c, d, e, f, g, h, i, j, k, l;
+ d = this, k = [d.x, d.y, d.radius], g = k[0], i = k[1], e = k[2], l = [a.x, a.y, a.radius], h = l[0], j = l[1], f = l[2];
+ if (g - e > h + f || g + e < h - f || i - e > j + f || i + e < j - f)return!1;
+ b = g - h, c = i - j;
+ return b * b + c * c > (e + f) * (e + f) ? !1 : !0
+ }, b.prototype.render = function (a) {
+ var b, c, d, e, f, g;
+ d = this, e = d.width, c = d.height, f = d.x, g = d.y, b = d.layers.mapcanvas.rect(f - e * .5 - 2, g - c * .5 - 2, e + 4, c + 4).attr({stroke: "none", fill: "#fff"}), d.chart = d.layers.mapcanvas.drawStackedBarChart(d.x, d.y, d.width, d.height, d.values, d.titles, d.colors, "none"), d.chart.push(b);
+ return d
+ }, b.prototype.update = function () {
+ var a, b;
+ a = this;
+ return
+ }, b.prototype.clear = function () {
+ var a, b, c, d, e;
+ a = this, e = a.chart;
+ for (c = 0, d = e.length; c < d; c++)b = e[c], b.remove();
+ a.chart = [];
+ return a
+ }, b.prototype.nodes = function () {
+ var a, b, c, d, e, f;
+ b = this, e = b.chart, f = [];
+ for (c = 0, d = e.length; c < d; c++)a = e[c], f.push(a.node);
+ return f
+ };
+ return b
+ }(br.Symbol), bg.props = ["values", "styles", "class", "titles", "colors", "width", "height"], bg.layers = [], br.StackedBarChart = bg
+}).call(this);
diff --git a/plugins/UserCountryMap/js/vendor/kmeans.js b/plugins/UserCountryMap/js/vendor/kmeans.js
index b6204a3522..f4a0d9ca4c 100644
--- a/plugins/UserCountryMap/js/vendor/kmeans.js
+++ b/plugins/UserCountryMap/js/vendor/kmeans.js
@@ -1,145 +1,145 @@
// k-means clustering
function kmeans() {
- var kmeans = {},
- points = [],
- iterations = 1,
- size = 1;
-
- kmeans.size = function(x) {
- if (!arguments.length) return size;
- size = x;
- return kmeans;
- };
+ var kmeans = {},
+ points = [],
+ iterations = 1,
+ size = 1;
+
+ kmeans.size = function (x) {
+ if (!arguments.length) return size;
+ size = x;
+ return kmeans;
+ };
- kmeans.iterations = function(x) {
- if (!arguments.length) return iterations;
- iterations = x;
- return kmeans;
- };
+ kmeans.iterations = function (x) {
+ if (!arguments.length) return iterations;
+ iterations = x;
+ return kmeans;
+ };
- kmeans.add = function(x) {
- points.push(x);
- return kmeans;
- };
-
- kmeans.means = function() {
- var means = [],
- seen = {},
- n = Math.min(size, points.length);
-
- // Initialize k random (unique!) means.
- for (var i = 0, m = 2 * n; i < m; i++) {
- var p = points[~~(Math.random() * points.length)], id = p.x + "/" + p.y;
- if (!(id in seen)) {
- seen[id] = 1;
- if (means.push({x: p.x, y: p.y}) >= n) break;
- }
- }
- n = means.length;
-
- // For each iteration, create a kd-tree of the current means.
- for (var j = 0; j < iterations; j++) {
- var kd = kdtree().points(means);
-
- // Clear the state.
- for (var i = 0; i < n; i++) {
- var mean = means[i];
- mean.sumX = 0;
- mean.sumY = 0;
- mean.size = 0;
- mean.points = [];
- }
-
- // Find the mean closest to each point.
- for (var i = 0; i < points.length; i++) {
- var point = points[i], mean = kd.find(point);
- mean.sumX += point.x;
- mean.sumY += point.y;
- mean.size++;
- mean.points.push(point);
- }
-
- // Compute the new means.
- for (var i = 0; i < n; i++) {
- var mean = means[i];
- if (!mean.size) continue; // overlapping mean
- mean.x = mean.sumX / mean.size;
- mean.y = mean.sumY / mean.size;
- }
- }
+ kmeans.add = function (x) {
+ points.push(x);
+ return kmeans;
+ };
- return means;
- };
+ kmeans.means = function () {
+ var means = [],
+ seen = {},
+ n = Math.min(size, points.length);
+
+ // Initialize k random (unique!) means.
+ for (var i = 0, m = 2 * n; i < m; i++) {
+ var p = points[~~(Math.random() * points.length)], id = p.x + "/" + p.y;
+ if (!(id in seen)) {
+ seen[id] = 1;
+ if (means.push({x: p.x, y: p.y}) >= n) break;
+ }
+ }
+ n = means.length;
+
+ // For each iteration, create a kd-tree of the current means.
+ for (var j = 0; j < iterations; j++) {
+ var kd = kdtree().points(means);
+
+ // Clear the state.
+ for (var i = 0; i < n; i++) {
+ var mean = means[i];
+ mean.sumX = 0;
+ mean.sumY = 0;
+ mean.size = 0;
+ mean.points = [];
+ }
+
+ // Find the mean closest to each point.
+ for (var i = 0; i < points.length; i++) {
+ var point = points[i], mean = kd.find(point);
+ mean.sumX += point.x;
+ mean.sumY += point.y;
+ mean.size++;
+ mean.points.push(point);
+ }
+
+ // Compute the new means.
+ for (var i = 0; i < n; i++) {
+ var mean = means[i];
+ if (!mean.size) continue; // overlapping mean
+ mean.x = mean.sumX / mean.size;
+ mean.y = mean.sumY / mean.size;
+ }
+ }
+
+ return means;
+ };
- return kmeans;
+ return kmeans;
}
// kd-tree
function kdtree() {
- var kdtree = {},
- axes = ["x", "y"],
- root,
- points = [];
-
- kdtree.axes = function(x) {
- if (!arguments.length) return axes;
- axes = x;
- return kdtree;
- };
+ var kdtree = {},
+ axes = ["x", "y"],
+ root,
+ points = [];
+
+ kdtree.axes = function (x) {
+ if (!arguments.length) return axes;
+ axes = x;
+ return kdtree;
+ };
- kdtree.points = function(x) {
- if (!arguments.length) return points;
- points = x;
- root = null;
- return kdtree;
- };
-
- kdtree.find = function(x) {
- return find(kdtree.root(), x, root).point;
- };
-
- kdtree.root = function(x) {
- return root || (root = node(points, 0));
- };
-
- function node(points, depth) {
- if (!points.length) return;
- var axis = axes[depth % axes.length], median = points.length >> 1;
- points.sort(order(axis)); // could use random sample to speed up here
- return {
- axis: axis,
- point: points[median],
- left: node(points.slice(0, median), depth + 1),
- right: node(points.slice(median + 1), depth + 1)
+ kdtree.points = function (x) {
+ if (!arguments.length) return points;
+ points = x;
+ root = null;
+ return kdtree;
};
- }
- function distance(a, b) {
- var sum = 0;
- for (var i = 0; i < axes.length; i++) {
- var axis = axes[i], d = a[axis] - b[axis];
- sum += d * d;
- }
- return sum;
- }
-
- function order(axis) {
- return function(a, b) {
- a = a[axis];
- b = b[axis];
- return a < b ? -1 : a > b ? 1 : 0;
+ kdtree.find = function (x) {
+ return find(kdtree.root(), x, root).point;
+ };
+
+ kdtree.root = function (x) {
+ return root || (root = node(points, 0));
};
- }
-
- function find(node, point, best) {
- if (distance(node.point, point) < distance(best.point, point)) best = node;
- if (node.left) best = find(node.left, point, best);
- if (node.right) {
- var d = node.point[node.axis] - point[node.axis];
- if (d * d < distance(best.point, point)) best = find(node.right, point, best);
+
+ function node(points, depth) {
+ if (!points.length) return;
+ var axis = axes[depth % axes.length], median = points.length >> 1;
+ points.sort(order(axis)); // could use random sample to speed up here
+ return {
+ axis: axis,
+ point: points[median],
+ left: node(points.slice(0, median), depth + 1),
+ right: node(points.slice(median + 1), depth + 1)
+ };
+ }
+
+ function distance(a, b) {
+ var sum = 0;
+ for (var i = 0; i < axes.length; i++) {
+ var axis = axes[i], d = a[axis] - b[axis];
+ sum += d * d;
+ }
+ return sum;
+ }
+
+ function order(axis) {
+ return function (a, b) {
+ a = a[axis];
+ b = b[axis];
+ return a < b ? -1 : a > b ? 1 : 0;
+ };
}
- return best;
- }
- return kdtree;
+ function find(node, point, best) {
+ if (distance(node.point, point) < distance(best.point, point)) best = node;
+ if (node.left) best = find(node.left, point, best);
+ if (node.right) {
+ var d = node.point[node.axis] - point[node.axis];
+ if (d * d < distance(best.point, point)) best = find(node.right, point, best);
+ }
+ return best;
+ }
+
+ return kdtree;
}
diff --git a/plugins/UserCountryMap/js/vendor/raphael-min.js b/plugins/UserCountryMap/js/vendor/raphael-min.js
index eeb59152af..118d7f93f2 100644
--- a/plugins/UserCountryMap/js/vendor/raphael-min.js
+++ b/plugins/UserCountryMap/js/vendor/raphael-min.js
@@ -7,4 +7,2217 @@
// │ Licensed under the MIT (http://raphaeljs.com/license.html) license.│ \\
// └────────────────────────────────────────────────────────────────────┘ \\
-(function(a){var b="0.3.4",c="hasOwnProperty",d=/[\.\/]/,e="*",f=function(){},g=function(a,b){return a-b},h,i,j={n:{}},k=function(a,b){var c=j,d=i,e=Array.prototype.slice.call(arguments,2),f=k.listeners(a),l=0,m=!1,n,o=[],p={},q=[],r=h,s=[];h=a,i=0;for(var t=0,u=f.length;t<u;t++)"zIndex"in f[t]&&(o.push(f[t].zIndex),f[t].zIndex<0&&(p[f[t].zIndex]=f[t]));o.sort(g);while(o[l]<0){n=p[o[l++]],q.push(n.apply(b,e));if(i){i=d;return q}}for(t=0;t<u;t++){n=f[t];if("zIndex"in n)if(n.zIndex==o[l]){q.push(n.apply(b,e));if(i)break;do{l++,n=p[o[l]],n&&q.push(n.apply(b,e));if(i)break}while(n)}else p[n.zIndex]=n;else{q.push(n.apply(b,e));if(i)break}}i=d,h=r;return q.length?q:null};k.listeners=function(a){var b=a.split(d),c=j,f,g,h,i,k,l,m,n,o=[c],p=[];for(i=0,k=b.length;i<k;i++){n=[];for(l=0,m=o.length;l<m;l++){c=o[l].n,g=[c[b[i]],c[e]],h=2;while(h--)f=g[h],f&&(n.push(f),p=p.concat(f.f||[]))}o=n}return p},k.on=function(a,b){var c=a.split(d),e=j;for(var g=0,h=c.length;g<h;g++)e=e.n,!e[c[g]]&&(e[c[g]]={n:{}}),e=e[c[g]];e.f=e.f||[];for(g=0,h=e.f.length;g<h;g++)if(e.f[g]==b)return f;e.f.push(b);return function(a){+a==+a&&(b.zIndex=+a)}},k.stop=function(){i=1},k.nt=function(a){if(a)return(new RegExp("(?:\\.|\\/|^)"+a+"(?:\\.|\\/|$)")).test(h);return h},k.off=k.unbind=function(a,b){var f=a.split(d),g,h,i,k,l,m,n,o=[j];for(k=0,l=f.length;k<l;k++)for(m=0;m<o.length;m+=i.length-2){i=[m,1],g=o[m].n;if(f[k]!=e)g[f[k]]&&i.push(g[f[k]]);else for(h in g)g[c](h)&&i.push(g[h]);o.splice.apply(o,i)}for(k=0,l=o.length;k<l;k++){g=o[k];while(g.n){if(b){if(g.f){for(m=0,n=g.f.length;m<n;m++)if(g.f[m]==b){g.f.splice(m,1);break}!g.f.length&&delete g.f}for(h in g.n)if(g.n[c](h)&&g.n[h].f){var p=g.n[h].f;for(m=0,n=p.length;m<n;m++)if(p[m]==b){p.splice(m,1);break}!p.length&&delete g.n[h].f}}else{delete g.f;for(h in g.n)g.n[c](h)&&g.n[h].f&&delete g.n[h].f}g=g.n}}},k.once=function(a,b){var c=function(){var d=b.apply(this,arguments);k.unbind(a,c);return d};return k.on(a,c)},k.version=b,k.toString=function(){return"You are running Eve "+b},typeof module!="undefined"&&module.exports?module.exports=k:typeof define!="undefined"?define("eve",[],function(){return k}):a.eve=k})(this),function(){function cF(a){for(var b=0;b<cy.length;b++)cy[b].el.paper==a&&cy.splice(b--,1)}function cE(b,d,e,f,h,i){e=Q(e);var j,k,l,m=[],o,p,q,t=b.ms,u={},v={},w={};if(f)for(y=0,z=cy.length;y<z;y++){var x=cy[y];if(x.el.id==d.id&&x.anim==b){x.percent!=e?(cy.splice(y,1),l=1):k=x,d.attr(x.totalOrigin);break}}else f=+v;for(var y=0,z=b.percents.length;y<z;y++){if(b.percents[y]==e||b.percents[y]>f*b.top){e=b.percents[y],p=b.percents[y-1]||0,t=t/b.top*(e-p),o=b.percents[y+1],j=b.anim[e];break}f&&d.attr(b.anim[b.percents[y]])}if(!!j){if(!k){for(var A in j)if(j[g](A))if(U[g](A)||d.paper.customAttributes[g](A)){u[A]=d.attr(A),u[A]==null&&(u[A]=T[A]),v[A]=j[A];switch(U[A]){case C:w[A]=(v[A]-u[A])/t;break;case"colour":u[A]=a.getRGB(u[A]);var B=a.getRGB(v[A]);w[A]={r:(B.r-u[A].r)/t,g:(B.g-u[A].g)/t,b:(B.b-u[A].b)/t};break;case"path":var D=bR(u[A],v[A]),E=D[1];u[A]=D[0],w[A]=[];for(y=0,z=u[A].length;y<z;y++){w[A][y]=[0];for(var F=1,G=u[A][y].length;F<G;F++)w[A][y][F]=(E[y][F]-u[A][y][F])/t}break;case"transform":var H=d._,I=ca(H[A],v[A]);if(I){u[A]=I.from,v[A]=I.to,w[A]=[],w[A].real=!0;for(y=0,z=u[A].length;y<z;y++){w[A][y]=[u[A][y][0]];for(F=1,G=u[A][y].length;F<G;F++)w[A][y][F]=(v[A][y][F]-u[A][y][F])/t}}else{var J=d.matrix||new cb,K={_:{transform:H.transform},getBBox:function(){return d.getBBox(1)}};u[A]=[J.a,J.b,J.c,J.d,J.e,J.f],b$(K,v[A]),v[A]=K._.transform,w[A]=[(K.matrix.a-J.a)/t,(K.matrix.b-J.b)/t,(K.matrix.c-J.c)/t,(K.matrix.d-J.d)/t,(K.matrix.e-J.e)/t,(K.matrix.f-J.f)/t]}break;case"csv":var L=r(j[A])[s](c),M=r(u[A])[s](c);if(A=="clip-rect"){u[A]=M,w[A]=[],y=M.length;while(y--)w[A][y]=(L[y]-u[A][y])/t}v[A]=L;break;default:L=[][n](j[A]),M=[][n](u[A]),w[A]=[],y=d.paper.customAttributes[A].length;while(y--)w[A][y]=((L[y]||0)-(M[y]||0))/t}}var O=j.easing,P=a.easing_formulas[O];if(!P){P=r(O).match(N);if(P&&P.length==5){var R=P;P=function(a){return cC(a,+R[1],+R[2],+R[3],+R[4],t)}}else P=bf}q=j.start||b.start||+(new Date),x={anim:b,percent:e,timestamp:q,start:q+(b.del||0),status:0,initstatus:f||0,stop:!1,ms:t,easing:P,from:u,diff:w,to:v,el:d,callback:j.callback,prev:p,next:o,repeat:i||b.times,origin:d.attr(),totalOrigin:h},cy.push(x);if(f&&!k&&!l){x.stop=!0,x.start=new Date-t*f;if(cy.length==1)return cA()}l&&(x.start=new Date-x.ms*f),cy.length==1&&cz(cA)}else k.initstatus=f,k.start=new Date-k.ms*f;eve("raphael.anim.start."+d.id,d,b)}}function cD(a,b){var c=[],d={};this.ms=b,this.times=1;if(a){for(var e in a)a[g](e)&&(d[Q(e)]=a[e],c.push(Q(e)));c.sort(bd)}this.anim=d,this.top=c[c.length-1],this.percents=c}function cC(a,b,c,d,e,f){function o(a,b){var c,d,e,f,j,k;for(e=a,k=0;k<8;k++){f=m(e)-a;if(z(f)<b)return e;j=(3*i*e+2*h)*e+g;if(z(j)<1e-6)break;e=e-f/j}c=0,d=1,e=a;if(e<c)return c;if(e>d)return d;while(c<d){f=m(e);if(z(f-a)<b)return e;a>f?c=e:d=e,e=(d-c)/2+c}return e}function n(a,b){var c=o(a,b);return((l*c+k)*c+j)*c}function m(a){return((i*a+h)*a+g)*a}var g=3*b,h=3*(d-b)-g,i=1-g-h,j=3*c,k=3*(e-c)-j,l=1-j-k;return n(a,1/(200*f))}function cq(){return this.x+q+this.y+q+this.width+" × "+this.height}function cp(){return this.x+q+this.y}function cb(a,b,c,d,e,f){a!=null?(this.a=+a,this.b=+b,this.c=+c,this.d=+d,this.e=+e,this.f=+f):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0)}function bH(b,c,d){b=a._path2curve(b),c=a._path2curve(c);var e,f,g,h,i,j,k,l,m,n,o=d?0:[];for(var p=0,q=b.length;p<q;p++){var r=b[p];if(r[0]=="M")e=i=r[1],f=j=r[2];else{r[0]=="C"?(m=[e,f].concat(r.slice(1)),e=m[6],f=m[7]):(m=[e,f,e,f,i,j,i,j],e=i,f=j);for(var s=0,t=c.length;s<t;s++){var u=c[s];if(u[0]=="M")g=k=u[1],h=l=u[2];else{u[0]=="C"?(n=[g,h].concat(u.slice(1)),g=n[6],h=n[7]):(n=[g,h,g,h,k,l,k,l],g=k,h=l);var v=bG(m,n,d);if(d)o+=v;else{for(var w=0,x=v.length;w<x;w++)v[w].segment1=p,v[w].segment2=s,v[w].bez1=m,v[w].bez2=n;o=o.concat(v)}}}}}return o}function bG(b,c,d){var e=a.bezierBBox(b),f=a.bezierBBox(c);if(!a.isBBoxIntersect(e,f))return d?0:[];var g=bB.apply(0,b),h=bB.apply(0,c),i=~~(g/5),j=~~(h/5),k=[],l=[],m={},n=d?0:[];for(var o=0;o<i+1;o++){var p=a.findDotsAtSegment.apply(a,b.concat(o/i));k.push({x:p.x,y:p.y,t:o/i})}for(o=0;o<j+1;o++)p=a.findDotsAtSegment.apply(a,c.concat(o/j)),l.push({x:p.x,y:p.y,t:o/j});for(o=0;o<i;o++)for(var q=0;q<j;q++){var r=k[o],s=k[o+1],t=l[q],u=l[q+1],v=z(s.x-r.x)<.001?"y":"x",w=z(u.x-t.x)<.001?"y":"x",x=bD(r.x,r.y,s.x,s.y,t.x,t.y,u.x,u.y);if(x){if(m[x.x.toFixed(4)]==x.y.toFixed(4))continue;m[x.x.toFixed(4)]=x.y.toFixed(4);var y=r.t+z((x[v]-r[v])/(s[v]-r[v]))*(s.t-r.t),A=t.t+z((x[w]-t[w])/(u[w]-t[w]))*(u.t-t.t);y>=0&&y<=1&&A>=0&&A<=1&&(d?n++:n.push({x:x.x,y:x.y,t1:y,t2:A}))}}return n}function bF(a,b){return bG(a,b,1)}function bE(a,b){return bG(a,b)}function bD(a,b,c,d,e,f,g,h){if(!(x(a,c)<y(e,g)||y(a,c)>x(e,g)||x(b,d)<y(f,h)||y(b,d)>x(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(!k)return;var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(n<+y(a,c).toFixed(2)||n>+x(a,c).toFixed(2)||n<+y(e,g).toFixed(2)||n>+x(e,g).toFixed(2)||o<+y(b,d).toFixed(2)||o>+x(b,d).toFixed(2)||o<+y(f,h).toFixed(2)||o>+x(f,h).toFixed(2))return;return{x:l,y:m}}}function bC(a,b,c,d,e,f,g,h,i){if(!(i<0||bB(a,b,c,d,e,f,g,h)<i)){var j=1,k=j/2,l=j-k,m,n=.01;m=bB(a,b,c,d,e,f,g,h,l);while(z(m-i)>n)k/=2,l+=(m<i?1:-1)*k,m=bB(a,b,c,d,e,f,g,h,l);return l}}function bB(a,b,c,d,e,f,g,h,i){i==null&&(i=1),i=i>1?1:i<0?0:i;var j=i/2,k=12,l=[-0.1252,.1252,-0.3678,.3678,-0.5873,.5873,-0.7699,.7699,-0.9041,.9041,-0.9816,.9816],m=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],n=0;for(var o=0;o<k;o++){var p=j*l[o]+j,q=bA(p,a,c,e,g),r=bA(p,b,d,f,h),s=q*q+r*r;n+=m[o]*w.sqrt(s)}return j*n}function bA(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}function by(a,b){var c=[];for(var d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}function bx(){return this.hex}function bv(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("␀"),h=d.cache=d.cache||{},i=d.count=d.count||[];if(h[g](f)){bu(i,f);return c?c(h[f]):h[f]}i.length>=1e3&&delete h[i.shift()],i.push(f),h[f]=a[m](b,e);return c?c(h[f]):h[f]}return d}function bu(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return a.push(a.splice(c,1)[0])}function bm(a){if(Object(a)!==a)return a;var b=new a.constructor;for(var c in a)a[g](c)&&(b[c]=bm(a[c]));return b}function a(c){if(a.is(c,"function"))return b?c():eve.on("raphael.DOMload",c);if(a.is(c,E))return a._engine.create[m](a,c.splice(0,3+a.is(c[0],C))).add(c);var d=Array.prototype.slice.call(arguments,0);if(a.is(d[d.length-1],"function")){var e=d.pop();return b?e.call(a._engine.create[m](a,d)):eve.on("raphael.DOMload",function(){e.call(a._engine.create[m](a,d))})}return a._engine.create[m](a,arguments)}a.version="2.1.0",a.eve=eve;var b,c=/[, ]+/,d={circle:1,rect:1,path:1,ellipse:1,text:1,image:1},e=/\{(\d+)\}/g,f="prototype",g="hasOwnProperty",h={doc:document,win:window},i={was:Object.prototype[g].call(h.win,"Raphael"),is:h.win.Raphael},j=function(){this.ca=this.customAttributes={}},k,l="appendChild",m="apply",n="concat",o="createTouch"in h.doc,p="",q=" ",r=String,s="split",t="click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[s](q),u={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},v=r.prototype.toLowerCase,w=Math,x=w.max,y=w.min,z=w.abs,A=w.pow,B=w.PI,C="number",D="string",E="array",F="toString",G="fill",H=Object.prototype.toString,I={},J="push",K=a._ISURL=/^url\(['"]?([^\)]+?)['"]?\)$/i,L=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,M={NaN:1,Infinity:1,"-Infinity":1},N=/^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,O=w.round,P="setAttribute",Q=parseFloat,R=parseInt,S=r.prototype.toUpperCase,T=a._availableAttrs={"arrow-end":"none","arrow-start":"none",blur:0,"clip-rect":"0 0 1e9 1e9",cursor:"default",cx:0,cy:0,fill:"#fff","fill-opacity":1,font:'10px "Arial"',"font-family":'"Arial"',"font-size":"10","font-style":"normal","font-weight":400,gradient:0,height:0,href:"http://raphaeljs.com/","letter-spacing":0,opacity:1,path:"M0,0",r:0,rx:0,ry:0,src:"",stroke:"#000","stroke-dasharray":"","stroke-linecap":"butt","stroke-linejoin":"butt","stroke-miterlimit":0,"stroke-opacity":1,"stroke-width":1,target:"_blank","text-anchor":"middle",title:"Raphael",transform:"",width:0,x:0,y:0},U=a._availableAnimAttrs={blur:C,"clip-rect":"csv",cx:C,cy:C,fill:"colour","fill-opacity":C,"font-size":C,height:C,opacity:C,path:"path",r:C,rx:C,ry:C,stroke:"colour","stroke-opacity":C,"stroke-width":C,transform:"transform",width:C,x:C,y:C},V=/[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]/g,W=/[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/,X={hs:1,rg:1},Y=/,?([achlmqrstvxz]),?/gi,Z=/([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,$=/([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,_=/(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/ig,ba=a._radial_gradient=/^r(?:\(([^,]+?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*([^\)]+?)\))?/,bb={},bc=function(a,b){return a.key-b.key},bd=function(a,b){return Q(a)-Q(b)},be=function(){},bf=function(a){return a},bg=a._rectPath=function(a,b,c,d,e){if(e)return[["M",a+e,b],["l",c-e*2,0],["a",e,e,0,0,1,e,e],["l",0,d-e*2],["a",e,e,0,0,1,-e,e],["l",e*2-c,0],["a",e,e,0,0,1,-e,-e],["l",0,e*2-d],["a",e,e,0,0,1,e,-e],["z"]];return[["M",a,b],["l",c,0],["l",0,d],["l",-c,0],["z"]]},bh=function(a,b,c,d){d==null&&(d=c);return[["M",a,b],["m",0,-d],["a",c,d,0,1,1,0,2*d],["a",c,d,0,1,1,0,-2*d],["z"]]},bi=a._getPath={path:function(a){return a.attr("path")},circle:function(a){var b=a.attrs;return bh(b.cx,b.cy,b.r)},ellipse:function(a){var b=a.attrs;return bh(b.cx,b.cy,b.rx,b.ry)},rect:function(a){var b=a.attrs;return bg(b.x,b.y,b.width,b.height,b.r)},image:function(a){var b=a.attrs;return bg(b.x,b.y,b.width,b.height)},text:function(a){var b=a._getBBox();return bg(b.x,b.y,b.width,b.height)}},bj=a.mapPath=function(a,b){if(!b)return a;var c,d,e,f,g,h,i;a=bR(a);for(e=0,g=a.length;e<g;e++){i=a[e];for(f=1,h=i.length;f<h;f+=2)c=b.x(i[f],i[f+1]),d=b.y(i[f],i[f+1]),i[f]=c,i[f+1]=d}return a};a._g=h,a.type=h.win.SVGAngle||h.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")?"SVG":"VML";if(a.type=="VML"){var bk=h.doc.createElement("div"),bl;bk.innerHTML='<v:shape adj="1"/>',bl=bk.firstChild,bl.style.behavior="url(#default#VML)";if(!bl||typeof bl.adj!="object")return a.type=p;bk=null}a.svg=!(a.vml=a.type=="VML"),a._Paper=j,a.fn=k=j.prototype=a.prototype,a._id=0,a._oid=0,a.is=function(a,b){b=v.call(b);if(b=="finite")return!M[g](+a);if(b=="array")return a instanceof Array;return b=="null"&&a===null||b==typeof a&&a!==null||b=="object"&&a===Object(a)||b=="array"&&Array.isArray&&Array.isArray(a)||H.call(a).slice(8,-1).toLowerCase()==b},a.angle=function(b,c,d,e,f,g){if(f==null){var h=b-d,i=c-e;if(!h&&!i)return 0;return(180+w.atan2(-i,-h)*180/B+360)%360}return a.angle(b,c,f,g)-a.angle(d,e,f,g)},a.rad=function(a){return a%360*B/180},a.deg=function(a){return a*180/B%360},a.snapTo=function(b,c,d){d=a.is(d,"finite")?d:10;if(a.is(b,E)){var e=b.length;while(e--)if(z(b[e]-c)<=d)return b[e]}else{b=+b;var f=c%b;if(f<d)return c-f;if(f>b-d)return c-f+b}return c};var bn=a.createUUID=function(a,b){return function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(a,b).toUpperCase()}}(/[xy]/g,function(a){var b=w.random()*16|0,c=a=="x"?b:b&3|8;return c.toString(16)});a.setWindow=function(b){eve("raphael.setWindow",a,h.win,b),h.win=b,h.doc=h.win.document,a._engine.initWin&&a._engine.initWin(h.win)};var bo=function(b){if(a.vml){var c=/^\s+|\s+$/g,d;try{var e=new ActiveXObject("htmlfile");e.write("<body>"),e.close(),d=e.body}catch(f){d=createPopup().document.body}var g=d.createTextRange();bo=bv(function(a){try{d.style.color=r(a).replace(c,p);var b=g.queryCommandValue("ForeColor");b=(b&255)<<16|b&65280|(b&16711680)>>>16;return"#"+("000000"+b.toString(16)).slice(-6)}catch(e){return"none"}})}else{var i=h.doc.createElement("i");i.title="Raphaël Colour Picker",i.style.display="none",h.doc.body.appendChild(i),bo=bv(function(a){i.style.color=a;return h.doc.defaultView.getComputedStyle(i,p).getPropertyValue("color")})}return bo(b)},bp=function(){return"hsb("+[this.h,this.s,this.b]+")"},bq=function(){return"hsl("+[this.h,this.s,this.l]+")"},br=function(){return this.hex},bs=function(b,c,d){c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b&&(d=b.b,c=b.g,b=b.r);if(c==null&&a.is(b,D)){var e=a.getRGB(b);b=e.r,c=e.g,d=e.b}if(b>1||c>1||d>1)b/=255,c/=255,d/=255;return[b,c,d]},bt=function(b,c,d,e){b*=255,c*=255,d*=255;var f={r:b,g:c,b:d,hex:a.rgb(b,c,d),toString:br};a.is(e,"finite")&&(f.opacity=e);return f};a.color=function(b){var c;a.is(b,"object")&&"h"in b&&"s"in b&&"b"in b?(c=a.hsb2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):a.is(b,"object")&&"h"in b&&"s"in b&&"l"in b?(c=a.hsl2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):(a.is(b,"string")&&(b=a.getRGB(b)),a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b?(c=a.rgb2hsl(b),b.h=c.h,b.s=c.s,b.l=c.l,c=a.rgb2hsb(b),b.v=c.b):(b={hex:"none"},b.r=b.g=b.b=b.h=b.s=b.v=b.l=-1)),b.toString=br;return b},a.hsb2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,a=a.h,d=a.o),a*=360;var e,f,g,h,i;a=a%360/60,i=c*b,h=i*(1-z(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bt(e,f,g,d)},a.hsl2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h);if(a>1||b>1||c>1)a/=360,b/=100,c/=100;a*=360;var e,f,g,h,i;a=a%360/60,i=2*b*(c<.5?c:1-c),h=i*(1-z(a%2-1)),e=f=g=c-i/2,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bt(e,f,g,d)},a.rgb2hsb=function(a,b,c){c=bs(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;f=x(a,b,c),g=f-y(a,b,c),d=g==0?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=g==0?0:g/f;return{h:d,s:e,b:f,toString:bp}},a.rgb2hsl=function(a,b,c){c=bs(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;g=x(a,b,c),h=y(a,b,c),i=g-h,d=i==0?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=i==0?0:f<.5?i/(2*f):i/(2-2*f);return{h:d,s:e,l:f,toString:bq}},a._path2string=function(){return this.join(",").replace(Y,"$1")};var bw=a._preload=function(a,b){var c=h.doc.createElement("img");c.style.cssText="position:absolute;left:-9999em;top:-9999em",c.onload=function(){b.call(this),this.onload=null,h.doc.body.removeChild(this)},c.onerror=function(){h.doc.body.removeChild(this)},h.doc.body.appendChild(c),c.src=a};a.getRGB=bv(function(b){if(!b||!!((b=r(b)).indexOf("-")+1))return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bx};if(b=="none")return{r:-1,g:-1,b:-1,hex:"none",toString:bx};!X[g](b.toLowerCase().substring(0,2))&&b.charAt()!="#"&&(b=bo(b));var c,d,e,f,h,i,j,k=b.match(L);if(k){k[2]&&(f=R(k[2].substring(5),16),e=R(k[2].substring(3,5),16),d=R(k[2].substring(1,3),16)),k[3]&&(f=R((i=k[3].charAt(3))+i,16),e=R((i=k[3].charAt(2))+i,16),d=R((i=k[3].charAt(1))+i,16)),k[4]&&(j=k[4][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),k[1].toLowerCase().slice(0,4)=="rgba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100));if(k[5]){j=k[5][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsb2rgb(d,e,f,h)}if(k[6]){j=k[6][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsla"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsl2rgb(d,e,f,h)}k={r:d,g:e,b:f,toString:bx},k.hex="#"+(16777216|f|e<<8|d<<16).toString(16).slice(1),a.is(h,"finite")&&(k.opacity=h);return k}return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bx}},a),a.hsb=bv(function(b,c,d){return a.hsb2rgb(b,c,d).hex}),a.hsl=bv(function(b,c,d){return a.hsl2rgb(b,c,d).hex}),a.rgb=bv(function(a,b,c){return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)}),a.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||.75},c=this.hsb2rgb(b.h,b.s,b.b);b.h+=.075,b.h>1&&(b.h=0,b.s-=.2,b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b}));return c.hex},a.getColor.reset=function(){delete this.start},a.parsePathString=function(b){if(!b)return null;var c=bz(b);if(c.arr)return bJ(c.arr);var d={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0},e=[];a.is(b,E)&&a.is(b[0],E)&&(e=bJ(b)),e.length||r(b).replace(Z,function(a,b,c){var f=[],g=b.toLowerCase();c.replace(_,function(a,b){b&&f.push(+b)}),g=="m"&&f.length>2&&(e.push([b][n](f.splice(0,2))),g="l",b=b=="m"?"l":"L");if(g=="r")e.push([b][n](f));else while(f.length>=d[g]){e.push([b][n](f.splice(0,d[g])));if(!d[g])break}}),e.toString=a._path2string,c.arr=bJ(e);return e},a.parseTransformString=bv(function(b){if(!b)return null;var c={r:3,s:4,t:2,m:6},d=[];a.is(b,E)&&a.is(b[0],E)&&(d=bJ(b)),d.length||r(b).replace($,function(a,b,c){var e=[],f=v.call(b);c.replace(_,function(a,b){b&&e.push(+b)}),d.push([b][n](e))}),d.toString=a._path2string;return d});var bz=function(a){var b=bz.ps=bz.ps||{};b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)b[g](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])});return b[a]};a.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=A(j,3),l=A(j,2),m=i*i,n=m*i,o=k*a+l*3*i*c+j*3*i*i*e+n*g,p=k*b+l*3*i*d+j*3*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,x=j*e+i*g,y=j*f+i*h,z=90-w.atan2(q-s,r-t)*180/B;(q>s||r<t)&&(z+=180);return{x:o,y:p,m:{x:q,y:r},n:{x:s,y:t},start:{x:u,y:v},end:{x:x,y:y},alpha:z}},a.bezierBBox=function(b,c,d,e,f,g,h,i){a.is(b,"array")||(b=[b,c,d,e,f,g,h,i]);var j=bQ.apply(null,b);return{x:j.min.x,y:j.min.y,x2:j.max.x,y2:j.max.y,width:j.max.x-j.min.x,height:j.max.y-j.min.y}},a.isPointInsideBBox=function(a,b,c){return b>=a.x&&b<=a.x2&&c>=a.y&&c<=a.y2},a.isBBoxIntersect=function(b,c){var d=a.isPointInsideBBox;return d(c,b.x,b.y)||d(c,b.x2,b.y)||d(c,b.x,b.y2)||d(c,b.x2,b.y2)||d(b,c.x,c.y)||d(b,c.x2,c.y)||d(b,c.x,c.y2)||d(b,c.x2,c.y2)||(b.x<c.x2&&b.x>c.x||c.x<b.x2&&c.x>b.x)&&(b.y<c.y2&&b.y>c.y||c.y<b.y2&&c.y>b.y)},a.pathIntersection=function(a,b){return bH(a,b)},a.pathIntersectionNumber=function(a,b){return bH(a,b,1)},a.isPointInsidePath=function(b,c,d){var e=a.pathBBox(b);return a.isPointInsideBBox(e,c,d)&&bH(b,[["M",c,d],["H",e.x2+10]],1)%2==1},a._removedFactory=function(a){return function(){eve("raphael.log",null,"Raphaël: you are calling to method “"+a+"” of removed object",a)}};var bI=a.pathBBox=function(a){var b=bz(a);if(b.bbox)return b.bbox;if(!a)return{x:0,y:0,width:0,height:0,x2:0,y2:0};a=bR(a);var c=0,d=0,e=[],f=[],g;for(var h=0,i=a.length;h<i;h++){g=a[h];if(g[0]=="M")c=g[1],d=g[2],e.push(c),f.push(d);else{var j=bQ(c,d,g[1],g[2],g[3],g[4],g[5],g[6]);e=e[n](j.min.x,j.max.x),f=f[n](j.min.y,j.max.y),c=g[5],d=g[6]}}var k=y[m](0,e),l=y[m](0,f),o=x[m](0,e),p=x[m](0,f),q={x:k,y:l,x2:o,y2:p,width:o-k,height:p-l};b.bbox=bm(q);return q},bJ=function(b){var c=bm(b);c.toString=a._path2string;return c},bK=a._pathToRelative=function(b){var c=bz(b);if(c.rel)return bJ(c.rel);if(!a.is(b,E)||!a.is(b&&b[0],E))b=a.parsePathString(b);var d=[],e=0,f=0,g=0,h=0,i=0;b[0][0]=="M"&&(e=b[0][1],f=b[0][2],g=e,h=f,i++,d.push(["M",e,f]));for(var j=i,k=b.length;j<k;j++){var l=d[j]=[],m=b[j];if(m[0]!=v.call(m[0])){l[0]=v.call(m[0]);switch(l[0]){case"a":l[1]=m[1],l[2]=m[2],l[3]=m[3],l[4]=m[4],l[5]=m[5],l[6]=+(m[6]-e).toFixed(3),l[7]=+(m[7]-f).toFixed(3);break;case"v":l[1]=+(m[1]-f).toFixed(3);break;case"m":g=m[1],h=m[2];default:for(var n=1,o=m.length;n<o;n++)l[n]=+(m[n]-(n%2?e:f)).toFixed(3)}}else{l=d[j]=[],m[0]=="m"&&(g=m[1]+e,h=m[2]+f);for(var p=0,q=m.length;p<q;p++)d[j][p]=m[p]}var r=d[j].length;switch(d[j][0]){case"z":e=g,f=h;break;case"h":e+=+d[j][r-1];break;case"v":f+=+d[j][r-1];break;default:e+=+d[j][r-2],f+=+d[j][r-1]}}d.toString=a._path2string,c.rel=bJ(d);return d},bL=a._pathToAbsolute=function(b){var c=bz(b);if(c.abs)return bJ(c.abs);if(!a.is(b,E)||!a.is(b&&b[0],E))b=a.parsePathString(b);if(!b||!b.length)return[["M",0,0]];var d=[],e=0,f=0,g=0,h=0,i=0;b[0][0]=="M"&&(e=+b[0][1],f=+b[0][2],g=e,h=f,i++,d[0]=["M",e,f]);var j=b.length==3&&b[0][0]=="M"&&b[1][0].toUpperCase()=="R"&&b[2][0].toUpperCase()=="Z";for(var k,l,m=i,o=b.length;m<o;m++){d.push(k=[]),l=b[m];if(l[0]!=S.call(l[0])){k[0]=S.call(l[0]);switch(k[0]){case"A":k[1]=l[1],k[2]=l[2],k[3]=l[3],k[4]=l[4],k[5]=l[5],k[6]=+(l[6]+e),k[7]=+(l[7]+f);break;case"V":k[1]=+l[1]+f;break;case"H":k[1]=+l[1]+e;break;case"R":var p=[e,f][n](l.slice(1));for(var q=2,r=p.length;q<r;q++)p[q]=+p[q]+e,p[++q]=+p[q]+f;d.pop(),d=d[n](by(p,j));break;case"M":g=+l[1]+e,h=+l[2]+f;default:for(q=1,r=l.length;q<r;q++)k[q]=+l[q]+(q%2?e:f)}}else if(l[0]=="R")p=[e,f][n](l.slice(1)),d.pop(),d=d[n](by(p,j)),k=["R"][n](l.slice(-2));else for(var s=0,t=l.length;s<t;s++)k[s]=l[s];switch(k[0]){case"Z":e=g,f=h;break;case"H":e=k[1];break;case"V":f=k[1];break;case"M":g=k[k.length-2],h=k[k.length-1];default:e=k[k.length-2],f=k[k.length-1]}}d.toString=a._path2string,c.abs=bJ(d);return d},bM=function(a,b,c,d){return[a,b,c,d,c,d]},bN=function(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]},bO=function(a,b,c,d,e,f,g,h,i,j){var k=B*120/180,l=B/180*(+e||0),m=[],o,p=bv(function(a,b,c){var d=a*w.cos(c)-b*w.sin(c),e=a*w.sin(c)+b*w.cos(c);return{x:d,y:e}});if(!j){o=p(a,b,-l),a=o.x,b=o.y,o=p(h,i,-l),h=o.x,i=o.y;var q=w.cos(B/180*e),r=w.sin(B/180*e),t=(a-h)/2,u=(b-i)/2,v=t*t/(c*c)+u*u/(d*d);v>1&&(v=w.sqrt(v),c=v*c,d=v*d);var x=c*c,y=d*d,A=(f==g?-1:1)*w.sqrt(z((x*y-x*u*u-y*t*t)/(x*u*u+y*t*t))),C=A*c*u/d+(a+h)/2,D=A*-d*t/c+(b+i)/2,E=w.asin(((b-D)/d).toFixed(9)),F=w.asin(((i-D)/d).toFixed(9));E=a<C?B-E:E,F=h<C?B-F:F,E<0&&(E=B*2+E),F<0&&(F=B*2+F),g&&E>F&&(E=E-B*2),!g&&F>E&&(F=F-B*2)}else E=j[0],F=j[1],C=j[2],D=j[3];var G=F-E;if(z(G)>k){var H=F,I=h,J=i;F=E+k*(g&&F>E?1:-1),h=C+c*w.cos(F),i=D+d*w.sin(F),m=bO(h,i,c,d,e,0,g,I,J,[F,H,C,D])}G=F-E;var K=w.cos(E),L=w.sin(E),M=w.cos(F),N=w.sin(F),O=w.tan(G/4),P=4/3*c*O,Q=4/3*d*O,R=[a,b],S=[a+P*L,b-Q*K],T=[h+P*N,i-Q*M],U=[h,i];S[0]=2*R[0]-S[0],S[1]=2*R[1]-S[1];if(j)return[S,T,U][n](m);m=[S,T,U][n](m).join()[s](",");var V=[];for(var W=0,X=m.length;W<X;W++)V[W]=W%2?p(m[W-1],m[W],l).y:p(m[W],m[W+1],l).x;return V},bP=function(a,b,c,d,e,f,g,h,i){var j=1-i;return{x:A(j,3)*a+A(j,2)*3*i*c+j*3*i*i*e+A(i,3)*g,y:A(j,3)*b+A(j,2)*3*i*d+j*3*i*i*f+A(i,3)*h}},bQ=bv(function(a,b,c,d,e,f,g,h){var i=e-2*c+a-(g-2*e+c),j=2*(c-a)-2*(e-c),k=a-c,l=(-j+w.sqrt(j*j-4*i*k))/2/i,n=(-j-w.sqrt(j*j-4*i*k))/2/i,o=[b,h],p=[a,g],q;z(l)>"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bP(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bP(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y)),i=f-2*d+b-(h-2*f+d),j=2*(d-b)-2*(f-d),k=b-d,l=(-j+w.sqrt(j*j-4*i*k))/2/i,n=(-j-w.sqrt(j*j-4*i*k))/2/i,z(l)>"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bP(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bP(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y));return{min:{x:y[m](0,p),y:y[m](0,o)},max:{x:x[m](0,p),y:x[m](0,o)}}}),bR=a._path2curve=bv(function(a,b){var c=!b&&bz(a);if(!b&&c.curve)return bJ(c.curve);var d=bL(a),e=b&&bL(b),f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},h=function(a,b){var c,d;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null);switch(a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"][n](bO[m](0,[b.x,b.y][n](a.slice(1))));break;case"S":c=b.x+(b.x-(b.bx||b.x)),d=b.y+(b.y-(b.by||b.y)),a=["C",c,d][n](a.slice(1));break;case"T":b.qx=b.x+(b.x-(b.qx||b.x)),b.qy=b.y+(b.y-(b.qy||b.y)),a=["C"][n](bN(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"][n](bN(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][n](bM(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][n](bM(b.x,b.y,a[1],b.y));break;case"V":a=["C"][n](bM(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][n](bM(b.x,b.y,b.X,b.Y))}return a},i=function(a,b){if(a[b].length>7){a[b].shift();var c=a[b];while(c.length)a.splice(b++,0,["C"][n](c.splice(0,6)));a.splice(b,1),l=x(d.length,e&&e.length||0)}},j=function(a,b,c,f,g){a&&b&&a[g][0]=="M"&&b[g][0]!="M"&&(b.splice(g,0,["M",f.x,f.y]),c.bx=0,c.by=0,c.x=a[g][1],c.y=a[g][2],l=x(d.length,e&&e.length||0))};for(var k=0,l=x(d.length,e&&e.length||0);k<l;k++){d[k]=h(d[k],f),i(d,k),e&&(e[k]=h(e[k],g)),e&&i(e,k),j(d,e,f,g,k),j(e,d,g,f,k);var o=d[k],p=e&&e[k],q=o.length,r=e&&p.length;f.x=o[q-2],f.y=o[q-1],f.bx=Q(o[q-4])||f.x,f.by=Q(o[q-3])||f.y,g.bx=e&&(Q(p[r-4])||g.x),g.by=e&&(Q(p[r-3])||g.y),g.x=e&&p[r-2],g.y=e&&p[r-1]}e||(c.curve=bJ(d));return e?[d,e]:d},null,bJ),bS=a._parseDots=bv(function(b){var c=[];for(var d=0,e=b.length;d<e;d++){var f={},g=b[d].match(/^([^:]*):?([\d\.]*)/);f.color=a.getRGB(g[1]);if(f.color.error)return null;f.color=f.color.hex,g[2]&&(f.offset=g[2]+"%"),c.push(f)}for(d=1,e=c.length-1;d<e;d++)if(!c[d].offset){var h=Q(c[d-1].offset||0),i=0;for(var j=d+1;j<e;j++)if(c[j].offset){i=c[j].offset;break}i||(i=100,j=e),i=Q(i);var k=(i-h)/(j-d+1);for(;d<j;d++)h+=k,c[d].offset=h+"%"}return c}),bT=a._tear=function(a,b){a==b.top&&(b.top=a.prev),a==b.bottom&&(b.bottom=a.next),a.next&&(a.next.prev=a.prev),a.prev&&(a.prev.next=a.next)},bU=a._tofront=function(a,b){b.top!==a&&(bT(a,b),a.next=null,a.prev=b.top,b.top.next=a,b.top=a)},bV=a._toback=function(a,b){b.bottom!==a&&(bT(a,b),a.next=b.bottom,a.prev=null,b.bottom.prev=a,b.bottom=a)},bW=a._insertafter=function(a,b,c){bT(a,c),b==c.top&&(c.top=a),b.next&&(b.next.prev=a),a.next=b.next,a.prev=b,b.next=a},bX=a._insertbefore=function(a,b,c){bT(a,c),b==c.bottom&&(c.bottom=a),b.prev&&(b.prev.next=a),a.prev=b.prev,b.prev=a,a.next=b},bY=a.toMatrix=function(a,b){var c=bI(a),d={_:{transform:p},getBBox:function(){return c}};b$(d,b);return d.matrix},bZ=a.transformPath=function(a,b){return bj(a,bY(a,b))},b$=a._extractTransform=function(b,c){if(c==null)return b._.transform;c=r(c).replace(/\.{3}|\u2026/g,b._.transform||p);var d=a.parseTransformString(c),e=0,f=0,g=0,h=1,i=1,j=b._,k=new cb;j.transform=d||[];if(d)for(var l=0,m=d.length;l<m;l++){var n=d[l],o=n.length,q=r(n[0]).toLowerCase(),s=n[0]!=q,t=s?k.invert():0,u,v,w,x,y;q=="t"&&o==3?s?(u=t.x(0,0),v=t.y(0,0),w=t.x(n[1],n[2]),x=t.y(n[1],n[2]),k.translate(w-u,x-v)):k.translate(n[1],n[2]):q=="r"?o==2?(y=y||b.getBBox(1),k.rotate(n[1],y.x+y.width/2,y.y+y.height/2),e+=n[1]):o==4&&(s?(w=t.x(n[2],n[3]),x=t.y(n[2],n[3]),k.rotate(n[1],w,x)):k.rotate(n[1],n[2],n[3]),e+=n[1]):q=="s"?o==2||o==3?(y=y||b.getBBox(1),k.scale(n[1],n[o-1],y.x+y.width/2,y.y+y.height/2),h*=n[1],i*=n[o-1]):o==5&&(s?(w=t.x(n[3],n[4]),x=t.y(n[3],n[4]),k.scale(n[1],n[2],w,x)):k.scale(n[1],n[2],n[3],n[4]),h*=n[1],i*=n[2]):q=="m"&&o==7&&k.add(n[1],n[2],n[3],n[4],n[5],n[6]),j.dirtyT=1,b.matrix=k}b.matrix=k,j.sx=h,j.sy=i,j.deg=e,j.dx=f=k.e,j.dy=g=k.f,h==1&&i==1&&!e&&j.bbox?(j.bbox.x+=+f,j.bbox.y+=+g):j.dirtyT=1},b_=function(a){var b=a[0];switch(b.toLowerCase()){case"t":return[b,0,0];case"m":return[b,1,0,0,1,0,0];case"r":return a.length==4?[b,0,a[2],a[3]]:[b,0];case"s":return a.length==5?[b,1,1,a[3],a[4]]:a.length==3?[b,1,1]:[b,1]}},ca=a._equaliseTransform=function(b,c){c=r(c).replace(/\.{3}|\u2026/g,b),b=a.parseTransformString(b)||[],c=a.parseTransformString(c)||[];var d=x(b.length,c.length),e=[],f=[],g=0,h,i,j,k;for(;g<d;g++){j=b[g]||b_(c[g]),k=c[g]||b_(j);if(j[0]!=k[0]||j[0].toLowerCase()=="r"&&(j[2]!=k[2]||j[3]!=k[3])||j[0].toLowerCase()=="s"&&(j[3]!=k[3]||j[4]!=k[4]))return;e[g]=[],f[g]=[];for(h=0,i=x(j.length,k.length);h<i;h++)h in j&&(e[g][h]=j[h]),h in k&&(f[g][h]=k[h])}return{from:e,to:f}};a._getContainer=function(b,c,d,e){var f;f=e==null&&!a.is(b,"object")?h.doc.getElementById(b):b;if(f!=null){if(f.tagName)return c==null?{container:f,width:f.style.pixelWidth||f.offsetWidth,height:f.style.pixelHeight||f.offsetHeight}:{container:f,width:c,height:d};return{container:1,x:b,y:c,width:d,height:e}}},a.pathToRelative=bK,a._engine={},a.path2curve=bR,a.matrix=function(a,b,c,d,e,f){return new cb(a,b,c,d,e,f)},function(b){function d(a){var b=w.sqrt(c(a));a[0]&&(a[0]/=b),a[1]&&(a[1]/=b)}function c(a){return a[0]*a[0]+a[1]*a[1]}b.add=function(a,b,c,d,e,f){var g=[[],[],[]],h=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]],i=[[a,c,e],[b,d,f],[0,0,1]],j,k,l,m;a&&a instanceof cb&&(i=[[a.a,a.c,a.e],[a.b,a.d,a.f],[0,0,1]]);for(j=0;j<3;j++)for(k=0;k<3;k++){m=0;for(l=0;l<3;l++)m+=h[j][l]*i[l][k];g[j][k]=m}this.a=g[0][0],this.b=g[1][0],this.c=g[0][1],this.d=g[1][1],this.e=g[0][2],this.f=g[1][2]},b.invert=function(){var a=this,b=a.a*a.d-a.b*a.c;return new cb(a.d/b,-a.b/b,-a.c/b,a.a/b,(a.c*a.f-a.d*a.e)/b,(a.b*a.e-a.a*a.f)/b)},b.clone=function(){return new cb(this.a,this.b,this.c,this.d,this.e,this.f)},b.translate=function(a,b){this.add(1,0,0,1,a,b)},b.scale=function(a,b,c,d){b==null&&(b=a),(c||d)&&this.add(1,0,0,1,c,d),this.add(a,0,0,b,0,0),(c||d)&&this.add(1,0,0,1,-c,-d)},b.rotate=function(b,c,d){b=a.rad(b),c=c||0,d=d||0;var e=+w.cos(b).toFixed(9),f=+w.sin(b).toFixed(9);this.add(e,f,-f,e,c,d),this.add(1,0,0,1,-c,-d)},b.x=function(a,b){return a*this.a+b*this.c+this.e},b.y=function(a,b){return a*this.b+b*this.d+this.f},b.get=function(a){return+this[r.fromCharCode(97+a)].toFixed(4)},b.toString=function(){return a.svg?"matrix("+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+")":[this.get(0),this.get(2),this.get(1),this.get(3),0,0].join()},b.toFilter=function(){return"progid:DXImageTransform.Microsoft.Matrix(M11="+this.get(0)+", M12="+this.get(2)+", M21="+this.get(1)+", M22="+this.get(3)+", Dx="+this.get(4)+", Dy="+this.get(5)+", sizingmethod='auto expand')"},b.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]},b.split=function(){var b={};b.dx=this.e,b.dy=this.f;var e=[[this.a,this.c],[this.b,this.d]];b.scalex=w.sqrt(c(e[0])),d(e[0]),b.shear=e[0][0]*e[1][0]+e[0][1]*e[1][1],e[1]=[e[1][0]-e[0][0]*b.shear,e[1][1]-e[0][1]*b.shear],b.scaley=w.sqrt(c(e[1])),d(e[1]),b.shear/=b.scaley;var f=-e[0][1],g=e[1][1];g<0?(b.rotate=a.deg(w.acos(g)),f<0&&(b.rotate=360-b.rotate)):b.rotate=a.deg(w.asin(f)),b.isSimple=!+b.shear.toFixed(9)&&(b.scalex.toFixed(9)==b.scaley.toFixed(9)||!b.rotate),b.isSuperSimple=!+b.shear.toFixed(9)&&b.scalex.toFixed(9)==b.scaley.toFixed(9)&&!b.rotate,b.noRotation=!+b.shear.toFixed(9)&&!b.rotate;return b},b.toTransformString=function(a){var b=a||this[s]();if(b.isSimple){b.scalex=+b.scalex.toFixed(4),b.scaley=+b.scaley.toFixed(4),b.rotate=+b.rotate.toFixed(4);return(b.dx||b.dy?"t"+[b.dx,b.dy]:p)+(b.scalex!=1||b.scaley!=1?"s"+[b.scalex,b.scaley,0,0]:p)+(b.rotate?"r"+[b.rotate,0,0]:p)}return"m"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)]}}(cb.prototype);var cc=navigator.userAgent.match(/Version\/(.*?)\s/)||navigator.userAgent.match(/Chrome\/(\d+)/);navigator.vendor=="Apple Computer, Inc."&&(cc&&cc[1]<4||navigator.platform.slice(0,2)=="iP")||navigator.vendor=="Google Inc."&&cc&&cc[1]<8?k.safari=function(){var a=this.rect(-99,-99,this.width+99,this.height+99).attr({stroke:"none"});setTimeout(function(){a.remove()})}:k.safari=be;var cd=function(){this.returnValue=!1},ce=function(){return this.originalEvent.preventDefault()},cf=function(){this.cancelBubble=!0},cg=function(){return this.originalEvent.stopPropagation()},ch=function(){if(h.doc.addEventListener)return function(a,b,c,d){var e=o&&u[b]?u[b]:b,f=function(e){var f=h.doc.documentElement.scrollTop||h.doc.body.scrollTop,i=h.doc.documentElement.scrollLeft||h.doc.body.scrollLeft,j=e.clientX+i,k=e.clientY+f;if(o&&u[g](b))for(var l=0,m=e.targetTouches&&e.targetTouches.length;l<m;l++)if(e.targetTouches[l].target==a){var n=e;e=e.targetTouches[l],e.originalEvent=n,e.preventDefault=ce,e.stopPropagation=cg;break}return c.call(d,e,j,k)};a.addEventListener(e,f,!1);return function(){a.removeEventListener(e,f,!1);return!0}};if(h.doc.attachEvent)return function(a,b,c,d){var e=function(a){a=a||h.win.event;var b=h.doc.documentElement.scrollTop||h.doc.body.scrollTop,e=h.doc.documentElement.scrollLeft||h.doc.body.scrollLeft,f=a.clientX+e,g=a.clientY+b;a.preventDefault=a.preventDefault||cd,a.stopPropagation=a.stopPropagation||cf;return c.call(d,a,f,g)};a.attachEvent("on"+b,e);var f=function(){a.detachEvent("on"+b,e);return!0};return f}}(),ci=[],cj=function(a){var b=a.clientX,c=a.clientY,d=h.doc.documentElement.scrollTop||h.doc.body.scrollTop,e=h.doc.documentElement.scrollLeft||h.doc.body.scrollLeft,f,g=ci.length;while(g--){f=ci[g];if(o){var i=a.touches.length,j;while(i--){j=a.touches[i];if(j.identifier==f.el._drag.id){b=j.clientX,c=j.clientY,(a.originalEvent?a.originalEvent:a).preventDefault();break}}}else a.preventDefault();var k=f.el.node,l,m=k.nextSibling,n=k.parentNode,p=k.style.display;h.win.opera&&n.removeChild(k),k.style.display="none",l=f.el.paper.getElementByPoint(b,c),k.style.display=p,h.win.opera&&(m?n.insertBefore(k,m):n.appendChild(k)),l&&eve("raphael.drag.over."+f.el.id,f.el,l),b+=e,c+=d,eve("raphael.drag.move."+f.el.id,f.move_scope||f.el,b-f.el._drag.x,c-f.el._drag.y,b,c,a)}},ck=function(b){a.unmousemove(cj).unmouseup(ck);var c=ci.length,d;while(c--)d=ci[c],d.el._drag={},eve("raphael.drag.end."+d.el.id,d.end_scope||d.start_scope||d.move_scope||d.el,b);ci=[]},cl=a.el={};for(var cm=t.length;cm--;)(function(b){a[b]=cl[b]=function(c,d){a.is(c,"function")&&(this.events=this.events||[],this.events.push({name:b,f:c,unbind:ch(this.shape||this.node||h.doc,b,c,d||this)}));return this},a["un"+b]=cl["un"+b]=function(a){var c=this.events||[],d=c.length;while(d--)if(c[d].name==b&&c[d].f==a){c[d].unbind(),c.splice(d,1),!c.length&&delete this.events;return this}return this}})(t[cm]);cl.data=function(b,c){var d=bb[this.id]=bb[this.id]||{};if(arguments.length==1){if(a.is(b,"object")){for(var e in b)b[g](e)&&this.data(e,b[e]);return this}eve("raphael.data.get."+this.id,this,d[b],b);return d[b]}d[b]=c,eve("raphael.data.set."+this.id,this,c,b);return this},cl.removeData=function(a){a==null?bb[this.id]={}:bb[this.id]&&delete bb[this.id][a];return this},cl.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)},cl.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var cn=[];cl.drag=function(b,c,d,e,f,g){function i(i){(i.originalEvent||i).preventDefault();var j=h.doc.documentElement.scrollTop||h.doc.body.scrollTop,k=h.doc.documentElement.scrollLeft||h.doc.body.scrollLeft;this._drag.x=i.clientX+k,this._drag.y=i.clientY+j,this._drag.id=i.identifier,!ci.length&&a.mousemove(cj).mouseup(ck),ci.push({el:this,move_scope:e,start_scope:f,end_scope:g}),c&&eve.on("raphael.drag.start."+this.id,c),b&&eve.on("raphael.drag.move."+this.id,b),d&&eve.on("raphael.drag.end."+this.id,d),eve("raphael.drag.start."+this.id,f||e||this,i.clientX+k,i.clientY+j,i)}this._drag={},cn.push({el:this,start:i}),this.mousedown(i);return this},cl.onDragOver=function(a){a?eve.on("raphael.drag.over."+this.id,a):eve.unbind("raphael.drag.over."+this.id)},cl.undrag=function(){var b=cn.length;while(b--)cn[b].el==this&&(this.unmousedown(cn[b].start),cn.splice(b,1),eve.unbind("raphael.drag.*."+this.id));!cn.length&&a.unmousemove(cj).unmouseup(ck)},k.circle=function(b,c,d){var e=a._engine.circle(this,b||0,c||0,d||0);this.__set__&&this.__set__.push(e);return e},k.rect=function(b,c,d,e,f){var g=a._engine.rect(this,b||0,c||0,d||0,e||0,f||0);this.__set__&&this.__set__.push(g);return g},k.ellipse=function(b,c,d,e){var f=a._engine.ellipse(this,b||0,c||0,d||0,e||0);this.__set__&&this.__set__.push(f);return f},k.path=function(b){b&&!a.is(b,D)&&!a.is(b[0],E)&&(b+=p);var c=a._engine.path(a.format[m](a,arguments),this);this.__set__&&this.__set__.push(c);return c},k.image=function(b,c,d,e,f){var g=a._engine.image(this,b||"about:blank",c||0,d||0,e||0,f||0);this.__set__&&this.__set__.push(g);return g},k.text=function(b,c,d){var e=a._engine.text(this,b||0,c||0,r(d));this.__set__&&this.__set__.push(e);return e},k.set=function(b){!a.is(b,"array")&&(b=Array.prototype.splice.call(arguments,0,arguments.length));var c=new cG(b);this.__set__&&this.__set__.push(c);return c},k.setStart=function(a){this.__set__=a||this.set()},k.setFinish=function(a){var b=this.__set__;delete this.__set__;return b},k.setSize=function(b,c){return a._engine.setSize.call(this,b,c)},k.setViewBox=function(b,c,d,e,f){return a._engine.setViewBox.call(this,b,c,d,e,f)},k.top=k.bottom=null,k.raphael=a;var co=function(a){var b=a.getBoundingClientRect(),c=a.ownerDocument,d=c.body,e=c.documentElement,f=e.clientTop||d.clientTop||0,g=e.clientLeft||d.clientLeft||0,i=b.top+(h.win.pageYOffset||e.scrollTop||d.scrollTop)-f,j=b.left+(h.win.pageXOffset||e.scrollLeft||d.scrollLeft)-g;return{y:i,x:j}};k.getElementByPoint=function(a,b){var c=this,d=c.canvas,e=h.doc.elementFromPoint(a,b);if(h.win.opera&&e.tagName=="svg"){var f=co(d),g=d.createSVGRect();g.x=a-f.x,g.y=b-f.y,g.width=g.height=1;var i=d.getIntersectionList(g,null);i.length&&(e=i[i.length-1])}if(!e)return null;while(e.parentNode&&e!=d.parentNode&&!e.raphael)e=e.parentNode;e==c.canvas.parentNode&&(e=d),e=e&&e.raphael?c.getById(e.raphaelid):null;return e},k.getById=function(a){var b=this.bottom;while(b){if(b.id==a)return b;b=b.next}return null},k.forEach=function(a,b){var c=this.bottom;while(c){if(a.call(b,c)===!1)return this;c=c.next}return this},k.getElementsByPoint=function(a,b){var c=this.set();this.forEach(function(d){d.isPointInside(a,b)&&c.push(d)});return c},cl.isPointInside=function(b,c){var d=this.realPath=this.realPath||bi[this.type](this);return a.isPointInsidePath(d,b,c)},cl.getBBox=function(a){if(this.removed)return{};var b=this._;if(a){if(b.dirty||!b.bboxwt)this.realPath=bi[this.type](this),b.bboxwt=bI(this.realPath),b.bboxwt.toString=cq,b.dirty=0;return b.bboxwt}if(b.dirty||b.dirtyT||!b.bbox){if(b.dirty||!this.realPath)b.bboxwt=0,this.realPath=bi[this.type](this);b.bbox=bI(bj(this.realPath,this.matrix)),b.bbox.toString=cq,b.dirty=b.dirtyT=0}return b.bbox},cl.clone=function(){if(this.removed)return null;var a=this.paper[this.type]().attr(this.attr());this.__set__&&this.__set__.push(a);return a},cl.glow=function(a){if(this.type=="text")return null;a=a||{};var b={width:(a.width||10)+(+this.attr("stroke-width")||1),fill:a.fill||!1,opacity:a.opacity||.5,offsetx:a.offsetx||0,offsety:a.offsety||0,color:a.color||"#000"},c=b.width/2,d=this.paper,e=d.set(),f=this.realPath||bi[this.type](this);f=this.matrix?bj(f,this.matrix):f;for(var g=1;g<c+1;g++)e.push(d.path(f).attr({stroke:b.color,fill:b.fill?b.color:"none","stroke-linejoin":"round","stroke-linecap":"round","stroke-width":+(b.width/c*g).toFixed(3),opacity:+(b.opacity/c).toFixed(3)}));return e.insertBefore(this).translate(b.offsetx,b.offsety)};var cr={},cs=function(b,c,d,e,f,g,h,i,j){return j==null?bB(b,c,d,e,f,g,h,i):a.findDotsAtSegment(b,c,d,e,f,g,h,i,bC(b,c,d,e,f,g,h,i,j))},ct=function(b,c){return function(d,e,f){d=bR(d);var g,h,i,j,k="",l={},m,n=0;for(var o=0,p=d.length;o<p;o++){i=d[o];if(i[0]=="M")g=+i[1],h=+i[2];else{j=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6]);if(n+j>e){if(c&&!l.start){m=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),k+=["C"+m.start.x,m.start.y,m.m.x,m.m.y,m.x,m.y];if(f)return k;l.start=k,k=["M"+m.x,m.y+"C"+m.n.x,m.n.y,m.end.x,m.end.y,i[5],i[6]].join(),n+=j,g=+i[5],h=+i[6];continue}if(!b&&!c){m=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);return{x:m.x,y:m.y,alpha:m.alpha}}}n+=j,g=+i[5],h=+i[6]}k+=i.shift()+i}l.end=k,m=b?n:c?l:a.findDotsAtSegment(g,h,i[0],i[1],i[2],i[3],i[4],i[5],1),m.alpha&&(m={x:m.x,y:m.y,alpha:m.alpha});return m}},cu=ct(1),cv=ct(),cw=ct(0,1);a.getTotalLength=cu,a.getPointAtLength=cv,a.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return cw(a,b).end;var d=cw(a,c,1);return b?cw(d,b).end:d},cl.getTotalLength=function(){if(this.type=="path"){if(this.node.getTotalLength)return this.node.getTotalLength();return cu(this.attrs.path)}},cl.getPointAtLength=function(a){if(this.type=="path")return cv(this.attrs.path,a)},cl.getSubpath=function(b,c){if(this.type=="path")return a.getSubpath(this.attrs.path,b,c)};var cx=a.easing_formulas={linear:function(a){return a},"<":function(a){return A(a,1.7)},">":function(a){return A(a,.48)},"<>":function(a){var b=.48-a/1.04,c=w.sqrt(.1734+b*b),d=c-b,e=A(z(d),1/3)*(d<0?-1:1),f=-c-b,g=A(z(f),1/3)*(f<0?-1:1),h=e+g+.5;return(1-h)*3*h*h+h*h*h},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a=a-1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a==!!a)return a;return A(2,-10*a)*w.sin((a-.075)*2*B/.3)+1},bounce:function(a){var b=7.5625,c=2.75,d;a<1/c?d=b*a*a:a<2/c?(a-=1.5/c,d=b*a*a+.75):a<2.5/c?(a-=2.25/c,d=b*a*a+.9375):(a-=2.625/c,d=b*a*a+.984375);return d}};cx.easeIn=cx["ease-in"]=cx["<"],cx.easeOut=cx["ease-out"]=cx[">"],cx.easeInOut=cx["ease-in-out"]=cx["<>"],cx["back-in"]=cx.backIn,cx["back-out"]=cx.backOut;var cy=[],cz=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){setTimeout(a,16)},cA=function(){var b=+(new Date),c=0;for(;c<cy.length;c++){var d=cy[c];if(d.el.removed||d.paused)continue;var e=b-d.start,f=d.ms,h=d.easing,i=d.from,j=d.diff,k=d.to,l=d.t,m=d.el,o={},p,r={},s;d.initstatus?(e=(d.initstatus*d.anim.top-d.prev)/(d.percent-d.prev)*f,d.status=d.initstatus,delete d.initstatus,d.stop&&cy.splice(c--,1)):d.status=(d.prev+(d.percent-d.prev)*(e/f))/d.anim.top;if(e<0)continue;if(e<f){var t=h(e/f);for(var u in i)if(i[g](u)){switch(U[u]){case C:p=+i[u]+t*f*j[u];break;case"colour":p="rgb("+[cB(O(i[u].r+t*f*j[u].r)),cB(O(i[u].g+t*f*j[u].g)),cB(O(i[u].b+t*f*j[u].b))].join(",")+")";break;case"path":p=[];for(var v=0,w=i[u].length;v<w;v++){p[v]=[i[u][v][0]];for(var x=1,y=i[u][v].length;x<y;x++)p[v][x]=+i[u][v][x]+t*f*j[u][v][x];p[v]=p[v].join(q)}p=p.join(q);break;case"transform":if(j[u].real){p=[];for(v=0,w=i[u].length;v<w;v++){p[v]=[i[u][v][0]];for(x=1,y=i[u][v].length;x<y;x++)p[v][x]=i[u][v][x]+t*f*j[u][v][x]}}else{var z=function(a){return+i[u][a]+t*f*j[u][a]};p=[["m",z(0),z(1),z(2),z(3),z(4),z(5)]]}break;case"csv":if(u=="clip-rect"){p=[],v=4;while(v--)p[v]=+i[u][v]+t*f*j[u][v]}break;default:var A=[][n](i[u]);p=[],v=m.paper.customAttributes[u].length;while(v--)p[v]=+A[v]+t*f*j[u][v]}o[u]=p}m.attr(o),function(a,b,c){setTimeout(function(){eve("raphael.anim.frame."+a,b,c)})}(m.id,m,d.anim)}else{(function(b,c,d){setTimeout(function(){eve("raphael.anim.frame."+c.id,c,d),eve("raphael.anim.finish."+c.id,c,d),a.is(b,"function")&&b.call(c)})})(d.callback,m,d.anim),m.attr(k),cy.splice(c--,1);if(d.repeat>1&&!d.next){for(s in k)k[g](s)&&(r[s]=d.totalOrigin[s]);d.el.attr(r),cE(d.anim,d.el,d.anim.percents[0],null,d.totalOrigin,d.repeat-1)}d.next&&!d.stop&&cE(d.anim,d.el,d.next,null,d.totalOrigin,d.repeat)}}a.svg&&m&&m.paper&&m.paper.safari(),cy.length&&cz(cA)},cB=function(a){return a>255?255:a<0?0:a};cl.animateWith=function(b,c,d,e,f,g){var h=this;if(h.removed){g&&g.call(h);return h}var i=d instanceof cD?d:a.animation(d,e,f,g),j,k;cE(i,h,i.percents[0],null,h.attr());for(var l=0,m=cy.length;l<m;l++)if(cy[l].anim==c&&cy[l].el==b){cy[m-1].start=cy[l].start;break}return h},cl.onAnimation=function(a){a?eve.on("raphael.anim.frame."+this.id,a):eve.unbind("raphael.anim.frame."+this.id);return this},cD.prototype.delay=function(a){var b=new cD(this.anim,this.ms);b.times=this.times,b.del=+a||0;return b},cD.prototype.repeat=function(a){var b=new cD(this.anim,this.ms);b.del=this.del,b.times=w.floor(x(a,0))||1;return b},a.animation=function(b,c,d,e){if(b instanceof cD)return b;if(a.is(d,"function")||!d)e=e||d||null,d=null;b=Object(b),c=+c||0;var f={},h,i;for(i in b)b[g](i)&&Q(i)!=i&&Q(i)+"%"!=i&&(h=!0,f[i]=b[i]);if(!h)return new cD(b,c);d&&(f.easing=d),e&&(f.callback=e);return new cD({100:f},c)},cl.animate=function(b,c,d,e){var f=this;if(f.removed){e&&e.call(f);return f}var g=b instanceof cD?b:a.animation(b,c,d,e);cE(g,f,g.percents[0],null,f.attr());return f},cl.setTime=function(a,b){a&&b!=null&&this.status(a,y(b,a.ms)/a.ms);return this},cl.status=function(a,b){var c=[],d=0,e,f;if(b!=null){cE(a,this,-1,y(b,1));return this}e=cy.length;for(;d<e;d++){f=cy[d];if(f.el.id==this.id&&(!a||f.anim==a)){if(a)return f.status;c.push({anim:f.anim,status:f.status})}}if(a)return 0;return c},cl.pause=function(a){for(var b=0;b<cy.length;b++)cy[b].el.id==this.id&&(!a||cy[b].anim==a)&&eve("raphael.anim.pause."+this.id,this,cy[b].anim)!==!1&&(cy[b].paused=!0);return this},cl.resume=function(a){for(var b=0;b<cy.length;b++)if(cy[b].el.id==this.id&&(!a||cy[b].anim==a)){var c=cy[b];eve("raphael.anim.resume."+this.id,this,c.anim)!==!1&&(delete c.paused,this.status(c.anim,c.status))}return this},cl.stop=function(a){for(var b=0;b<cy.length;b++)cy[b].el.id==this.id&&(!a||cy[b].anim==a)&&eve("raphael.anim.stop."+this.id,this,cy[b].anim)!==!1&&cy.splice(b--,1);return this},eve.on("raphael.remove",cF),eve.on("raphael.clear",cF),cl.toString=function(){return"Raphaël’s object"};var cG=function(a){this.items=[],this.length=0,this.type="set";if(a)for(var b=0,c=a.length;b<c;b++)a[b]&&(a[b].constructor==cl.constructor||a[b].constructor==cG)&&(this[this.items.length]=this.items[this.items.length]=a[b],this.length++)},cH=cG.prototype;cH.push=function(){var a,b;for(var c=0,d=arguments.length;c<d;c++)a=arguments[c],a&&(a.constructor==cl.constructor||a.constructor==cG)&&(b=this.items.length,this[b]=this.items[b]=a,this.length++);return this},cH.pop=function(){this.length&&delete this[this.length--];return this.items.pop()},cH.forEach=function(a,b){for(var c=0,d=this.items.length;c<d;c++)if(a.call(b,this.items[c],c)===!1)return this;return this};for(var cI in cl)cl[g](cI)&&(cH[cI]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a][m](c,b)})}}(cI));cH.attr=function(b,c){if(b&&a.is(b,E)&&a.is(b[0],"object"))for(var d=0,e=b.length;d<e;d++)this.items[d].attr(b[d]);else for(var f=0,g=this.items.length;f<g;f++)this.items[f].attr(b,c);return this},cH.clear=function(){while(this.length)this.pop()},cH.splice=function(a,b,c){a=a<0?x(this.length+a,0):a,b=x(0,y(this.length-a,b));var d=[],e=[],f=[],g;for(g=2;g<arguments.length;g++)f.push(arguments[g]);for(g=0;g<b;g++)e.push(this[a+g]);for(;g<this.length-a;g++)d.push(this[a+g]);var h=f.length;for(g=0;g<h+d.length;g++)this.items[a+g]=this[a+g]=g<h?f[g]:d[g-h];g=this.items.length=this.length-=b-h;while(this[g])delete this[g++];return new cG(e)},cH.exclude=function(a){for(var b=0,c=this.length;b<c;b++)if(this[b]==a){this.splice(b,1);return!0}},cH.animate=function(b,c,d,e){(a.is(d,"function")||!d)&&(e=d||null);var f=this.items.length,g=f,h,i=this,j;if(!f)return this;e&&(j=function(){!--f&&e.call(i)}),d=a.is(d,D)?d:j;var k=a.animation(b,c,d,j);h=this.items[--g].animate(k);while(g--)this.items[g]&&!this.items[g].removed&&this.items[g].animateWith(h,k,k);return this},cH.insertAfter=function(a){var b=this.items.length;while(b--)this.items[b].insertAfter(a);return this},cH.getBBox=function(){var a=[],b=[],c=[],d=[];for(var e=this.items.length;e--;)if(!this.items[e].removed){var f=this.items[e].getBBox();a.push(f.x),b.push(f.y),c.push(f.x+f.width),d.push(f.y+f.height)}a=y[m](0,a),b=y[m](0,b),c=x[m](0,c),d=x[m](0,d);return{x:a,y:b,x2:c,y2:d,width:c-a,height:d-b}},cH.clone=function(a){a=new cG;for(var b=0,c=this.items.length;b<c;b++)a.push(this.items[b].clone());return a},cH.toString=function(){return"Raphaël‘s set"},a.registerFont=function(a){if(!a.face)return a;this.fonts=this.fonts||{};var b={w:a.w,face:{},glyphs:{}},c=a.face["font-family"];for(var d in a.face)a.face[g](d)&&(b.face[d]=a.face[d]);this.fonts[c]?this.fonts[c].push(b):this.fonts[c]=[b];if(!a.svg){b.face["units-per-em"]=R(a.face["units-per-em"],10);for(var e in a.glyphs)if(a.glyphs[g](e)){var f=a.glyphs[e];b.glyphs[e]={w:f.w,k:{},d:f.d&&"M"+f.d.replace(/[mlcxtrv]/g,function(a){return{l:"L",c:"C",x:"z",t:"m",r:"l",v:"c"}[a]||"M"})+"z"};if(f.k)for(var h in f.k)f[g](h)&&(b.glyphs[e].k[h]=f.k[h])}}return a},k.getFont=function(b,c,d,e){e=e||"normal",d=d||"normal",c=+c||{normal:400,bold:700,lighter:300,bolder:800}[c]||400;if(!!a.fonts){var f=a.fonts[b];if(!f){var h=new RegExp("(^|\\s)"+b.replace(/[^\w\d\s+!~.:_-]/g,p)+"(\\s|$)","i");for(var i in a.fonts)if(a.fonts[g](i)&&h.test(i)){f=a.fonts[i];break}}var j;if(f)for(var k=0,l=f.length;k<l;k++){j=f[k];if(j.face["font-weight"]==c&&(j.face["font-style"]==d||!j.face["font-style"])&&j.face["font-stretch"]==e)break}return j}},k.print=function(b,d,e,f,g,h,i){h=h||"middle",i=x(y(i||0,1),-1);var j=r(e)[s](p),k=0,l=0,m=p,n;a.is(f,e)&&(f=this.getFont(f));if(f){n=(g||16)/f.face["units-per-em"];var o=f.face.bbox[s](c),q=+o[0],t=o[3]-o[1],u=0,v=+o[1]+(h=="baseline"?t+ +f.face.descent:t/2);for(var w=0,z=j.length;w<z;w++){if(j[w]=="\n")k=0,B=0,l=0,u+=t;else{var A=l&&f.glyphs[j[w-1]]||{},B=f.glyphs[j[w]];k+=l?(A.w||f.w)+(A.k&&A.k[j[w]]||0)+f.w*i:0,l=1}B&&B.d&&(m+=a.transformPath(B.d,["t",k*n,u*n,"s",n,n,q,v,"t",(b-q)/n,(d-v)/n]))}}return this.path(m).attr({fill:"#000",stroke:"none"})},k.add=function(b){if(a.is(b,"array")){var c=this.set(),e=0,f=b.length,h;for(;e<f;e++)h=b[e]||{},d[g](h.type)&&c.push(this[h.type]().attr(h))}return c},a.format=function(b,c){var d=a.is(c,E)?[0][n](c):arguments;b&&a.is(b,D)&&d.length-1&&(b=b.replace(e,function(a,b){return d[++b]==null?p:d[b]}));return b||p},a.fullfill=function(){var a=/\{([^\}]+)\}/g,b=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g,c=function(a,c,d){var e=d;c.replace(b,function(a,b,c,d,f){b=b||d,e&&(b in e&&(e=e[b]),typeof e=="function"&&f&&(e=e()))}),e=(e==null||e==d?a:e)+"";return e};return function(b,d){return String(b).replace(a,function(a,b){return c(a,b,d)})}}(),a.ninja=function(){i.was?h.win.Raphael=i.is:delete Raphael;return a},a.st=cH,function(b,c,d){function e(){/in/.test(b.readyState)?setTimeout(e,9):a.eve("raphael.DOMload")}b.readyState==null&&b.addEventListener&&(b.addEventListener(c,d=function(){b.removeEventListener(c,d,!1),b.readyState="complete"},!1),b.readyState="loading"),e()}(document,"DOMContentLoaded"),i.was?h.win.Raphael=a:Raphael=a,eve.on("raphael.DOMload",function(){b=!0})}(),window.Raphael.svg&&function(a){var b="hasOwnProperty",c=String,d=parseFloat,e=parseInt,f=Math,g=f.max,h=f.abs,i=f.pow,j=/[, ]+/,k=a.eve,l="",m=" ",n="http://www.w3.org/1999/xlink",o={block:"M5,0 0,2.5 5,5z",classic:"M5,0 0,2.5 5,5 3.5,3 3.5,2z",diamond:"M2.5,0 5,2.5 2.5,5 0,2.5z",open:"M6,1 1,3.5 6,6",oval:"M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"},p={};a.toString=function(){return"Your browser supports SVG.\nYou are running Raphaël "+this.version};var q=function(d,e){if(e){typeof d=="string"&&(d=q(d));for(var f in e)e[b](f)&&(f.substring(0,6)=="xlink:"?d.setAttributeNS(n,f.substring(6),c(e[f])):d.setAttribute(f,c(e[f])))}else d=a._g.doc.createElementNS("http://www.w3.org/2000/svg",d),d.style&&(d.style.webkitTapHighlightColor="rgba(0,0,0,0)");return d},r=function(b,e){var j="linear",k=b.id+e,m=.5,n=.5,o=b.node,p=b.paper,r=o.style,s=a._g.doc.getElementById(k);if(!s){e=c(e).replace(a._radial_gradient,function(a,b,c){j="radial";if(b&&c){m=d(b),n=d(c);var e=(n>.5)*2-1;i(m-.5,2)+i(n-.5,2)>.25&&(n=f.sqrt(.25-i(m-.5,2))*e+.5)&&n!=.5&&(n=n.toFixed(5)-1e-5*e)}return l}),e=e.split(/\s*\-\s*/);if(j=="linear"){var t=e.shift();t=-d(t);if(isNaN(t))return null;var u=[0,0,f.cos(a.rad(t)),f.sin(a.rad(t))],v=1/(g(h(u[2]),h(u[3]))||1);u[2]*=v,u[3]*=v,u[2]<0&&(u[0]=-u[2],u[2]=0),u[3]<0&&(u[1]=-u[3],u[3]=0)}var w=a._parseDots(e);if(!w)return null;k=k.replace(/[\(\)\s,\xb0#]/g,"_"),b.gradient&&k!=b.gradient.id&&(p.defs.removeChild(b.gradient),delete b.gradient);if(!b.gradient){s=q(j+"Gradient",{id:k}),b.gradient=s,q(s,j=="radial"?{fx:m,fy:n}:{x1:u[0],y1:u[1],x2:u[2],y2:u[3],gradientTransform:b.matrix.invert()}),p.defs.appendChild(s);for(var x=0,y=w.length;x<y;x++)s.appendChild(q("stop",{offset:w[x].offset?w[x].offset:x?"100%":"0%","stop-color":w[x].color||"#fff"}))}}q(o,{fill:"url(#"+k+")",opacity:1,"fill-opacity":1}),r.fill=l,r.opacity=1,r.fillOpacity=1;return 1},s=function(a){var b=a.getBBox(1);q(a.pattern,{patternTransform:a.matrix.invert()+" translate("+b.x+","+b.y+")"})},t=function(d,e,f){if(d.type=="path"){var g=c(e).toLowerCase().split("-"),h=d.paper,i=f?"end":"start",j=d.node,k=d.attrs,m=k["stroke-width"],n=g.length,r="classic",s,t,u,v,w,x=3,y=3,z=5;while(n--)switch(g[n]){case"block":case"classic":case"oval":case"diamond":case"open":case"none":r=g[n];break;case"wide":y=5;break;case"narrow":y=2;break;case"long":x=5;break;case"short":x=2}r=="open"?(x+=2,y+=2,z+=2,u=1,v=f?4:1,w={fill:"none",stroke:k.stroke}):(v=u=x/2,w={fill:k.stroke,stroke:"none"}),d._.arrows?f?(d._.arrows.endPath&&p[d._.arrows.endPath]--,d._.arrows.endMarker&&p[d._.arrows.endMarker]--):(d._.arrows.startPath&&p[d._.arrows.startPath]--,d._.arrows.startMarker&&p[d._.arrows.startMarker]--):d._.arrows={};if(r!="none"){var A="raphael-marker-"+r,B="raphael-marker-"+i+r+x+y;a._g.doc.getElementById(A)?p[A]++:(h.defs.appendChild(q(q("path"),{"stroke-linecap":"round",d:o[r],id:A})),p[A]=1);var C=a._g.doc.getElementById(B),D;C?(p[B]++,D=C.getElementsByTagName("use")[0]):(C=q(q("marker"),{id:B,markerHeight:y,markerWidth:x,orient:"auto",refX:v,refY:y/2}),D=q(q("use"),{"xlink:href":"#"+A,transform:(f?"rotate(180 "+x/2+" "+y/2+") ":l)+"scale("+x/z+","+y/z+")","stroke-width":(1/((x/z+y/z)/2)).toFixed(4)}),C.appendChild(D),h.defs.appendChild(C),p[B]=1),q(D,w);var F=u*(r!="diamond"&&r!="oval");f?(s=d._.arrows.startdx*m||0,t=a.getTotalLength(k.path)-F*m):(s=F*m,t=a.getTotalLength(k.path)-(d._.arrows.enddx*m||0)),w={},w["marker-"+i]="url(#"+B+")";if(t||s)w.d=Raphael.getSubpath(k.path,s,t);q(j,w),d._.arrows[i+"Path"]=A,d._.arrows[i+"Marker"]=B,d._.arrows[i+"dx"]=F,d._.arrows[i+"Type"]=r,d._.arrows[i+"String"]=e}else f?(s=d._.arrows.startdx*m||0,t=a.getTotalLength(k.path)-s):(s=0,t=a.getTotalLength(k.path)-(d._.arrows.enddx*m||0)),d._.arrows[i+"Path"]&&q(j,{d:Raphael.getSubpath(k.path,s,t)}),delete d._.arrows[i+"Path"],delete d._.arrows[i+"Marker"],delete d._.arrows[i+"dx"],delete d._.arrows[i+"Type"],delete d._.arrows[i+"String"];for(w in p)if(p[b](w)&&!p[w]){var G=a._g.doc.getElementById(w);G&&G.parentNode.removeChild(G)}}},u={"":[0],none:[0],"-":[3,1],".":[1,1],"-.":[3,1,1,1],"-..":[3,1,1,1,1,1],". ":[1,3],"- ":[4,3],"--":[8,3],"- .":[4,3,1,3],"--.":[8,3,1,3],"--..":[8,3,1,3,1,3]},v=function(a,b,d){b=u[c(b).toLowerCase()];if(b){var e=a.attrs["stroke-width"]||"1",f={round:e,square:e,butt:0}[a.attrs["stroke-linecap"]||d["stroke-linecap"]]||0,g=[],h=b.length;while(h--)g[h]=b[h]*e+(h%2?1:-1)*f;q(a.node,{"stroke-dasharray":g.join(",")})}},w=function(d,f){var i=d.node,k=d.attrs,m=i.style.visibility;i.style.visibility="hidden";for(var o in f)if(f[b](o)){if(!a._availableAttrs[b](o))continue;var p=f[o];k[o]=p;switch(o){case"blur":d.blur(p);break;case"href":case"title":case"target":var u=i.parentNode;if(u.tagName.toLowerCase()!="a"){var w=q("a");u.insertBefore(w,i),w.appendChild(i),u=w}o=="target"?u.setAttributeNS(n,"show",p=="blank"?"new":p):u.setAttributeNS(n,o,p);break;case"cursor":i.style.cursor=p;break;case"transform":d.transform(p);break;case"arrow-start":t(d,p);break;case"arrow-end":t(d,p,1);break;case"clip-rect":var x=c(p).split(j);if(x.length==4){d.clip&&d.clip.parentNode.parentNode.removeChild(d.clip.parentNode);var z=q("clipPath"),A=q("rect");z.id=a.createUUID(),q(A,{x:x[0],y:x[1],width:x[2],height:x[3]}),z.appendChild(A),d.paper.defs.appendChild(z),q(i,{"clip-path":"url(#"+z.id+")"}),d.clip=A}if(!p){var B=i.getAttribute("clip-path");if(B){var C=a._g.doc.getElementById(B.replace(/(^url\(#|\)$)/g,l));C&&C.parentNode.removeChild(C),q(i,{"clip-path":l}),delete d.clip}}break;case"path":d.type=="path"&&(q(i,{d:p?k.path=a._pathToAbsolute(p):"M0,0"}),d._.dirty=1,d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1)));break;case"width":i.setAttribute(o,p),d._.dirty=1;if(k.fx)o="x",p=k.x;else break;case"x":k.fx&&(p=-k.x-(k.width||0));case"rx":if(o=="rx"&&d.type=="rect")break;case"cx":i.setAttribute(o,p),d.pattern&&s(d),d._.dirty=1;break;case"height":i.setAttribute(o,p),d._.dirty=1;if(k.fy)o="y",p=k.y;else break;case"y":k.fy&&(p=-k.y-(k.height||0));case"ry":if(o=="ry"&&d.type=="rect")break;case"cy":i.setAttribute(o,p),d.pattern&&s(d),d._.dirty=1;break;case"r":d.type=="rect"?q(i,{rx:p,ry:p}):i.setAttribute(o,p),d._.dirty=1;break;case"src":d.type=="image"&&i.setAttributeNS(n,"href",p);break;case"stroke-width":if(d._.sx!=1||d._.sy!=1)p/=g(h(d._.sx),h(d._.sy))||1;d.paper._vbSize&&(p*=d.paper._vbSize),i.setAttribute(o,p),k["stroke-dasharray"]&&v(d,k["stroke-dasharray"],f),d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"stroke-dasharray":v(d,p,f);break;case"fill":var D=c(p).match(a._ISURL);if(D){z=q("pattern");var F=q("image");z.id=a.createUUID(),q(z,{x:0,y:0,patternUnits:"userSpaceOnUse",height:1,width:1}),q(F,{x:0,y:0,"xlink:href":D[1]}),z.appendChild(F),function(b){a._preload(D[1],function(){var a=this.offsetWidth,c=this.offsetHeight;q(b,{width:a,height:c}),q(F,{width:a,height:c}),d.paper.safari()})}(z),d.paper.defs.appendChild(z),q(i,{fill:"url(#"+z.id+")"}),d.pattern=z,d.pattern&&s(d);break}var G=a.getRGB(p);if(!G.error)delete f.gradient,delete k.gradient,!a.is(k.opacity,"undefined")&&a.is(f.opacity,"undefined")&&q(i,{opacity:k.opacity}),!a.is(k["fill-opacity"],"undefined")&&a.is(f["fill-opacity"],"undefined")&&q(i,{"fill-opacity":k["fill-opacity"]});else if((d.type=="circle"||d.type=="ellipse"||c(p).charAt()!="r")&&r(d,p)){if("opacity"in k||"fill-opacity"in k){var H=a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l));if(H){var I=H.getElementsByTagName("stop");q(I[I.length-1],{"stop-opacity":("opacity"in k?k.opacity:1)*("fill-opacity"in k?k["fill-opacity"]:1)})}}k.gradient=p,k.fill="none";break}G[b]("opacity")&&q(i,{"fill-opacity":G.opacity>1?G.opacity/100:G.opacity});case"stroke":G=a.getRGB(p),i.setAttribute(o,G.hex),o=="stroke"&&G[b]("opacity")&&q(i,{"stroke-opacity":G.opacity>1?G.opacity/100:G.opacity}),o=="stroke"&&d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"gradient":(d.type=="circle"||d.type=="ellipse"||c(p).charAt()!="r")&&r(d,p);break;case"opacity":k.gradient&&!k[b]("stroke-opacity")&&q(i,{"stroke-opacity":p>1?p/100:p});case"fill-opacity":if(k.gradient){H=a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l)),H&&(I=H.getElementsByTagName("stop"),q(I[I.length-1],{"stop-opacity":p}));break};default:o=="font-size"&&(p=e(p,10)+"px");var J=o.replace(/(\-.)/g,function(a){return a.substring(1).toUpperCase()});i.style[J]=p,d._.dirty=1,i.setAttribute(o,p)}}y(d,f),i.style.visibility=m},x=1.2,y=function(d,f){if(d.type=="text"&&!!(f[b]("text")||f[b]("font")||f[b]("font-size")||f[b]("x")||f[b]("y"))){var g=d.attrs,h=d.node,i=h.firstChild?e(a._g.doc.defaultView.getComputedStyle(h.firstChild,l).getPropertyValue("font-size"),10):10;if(f[b]("text")){g.text=f.text;while(h.firstChild)h.removeChild(h.firstChild);var j=c(f.text).split("\n"),k=[],m;for(var n=0,o=j.length;n<o;n++)m=q("tspan"),n&&q(m,{dy:i*x,x:g.x}),m.appendChild(a._g.doc.createTextNode(j[n])),h.appendChild(m),k[n]=m}else{k=h.getElementsByTagName("tspan");for(n=0,o=k.length;n<o;n++)n?q(k[n],{dy:i*x,x:g.x}):q(k[0],{dy:0})}q(h,{x:g.x,y:g.y}),d._.dirty=1;var p=d._getBBox(),r=g.y-(p.y+p.height/2);r&&a.is(r,"finite")&&q(k[0],{dy:r})}},z=function(b,c){var d=0,e=0;this[0]=this.node=b,b.raphael=!0,this.id=a._oid++,b.raphaelid=this.id,this.matrix=a.matrix(),this.realPath=null,this.paper=c,this.attrs=this.attrs||{},this._={transform:[],sx:1,sy:1,deg:0,dx:0,dy:0,dirty:1},!c.bottom&&(c.bottom=this),this.prev=c.top,c.top&&(c.top.next=this),c.top=this,this.next=null},A=a.el;z.prototype=A,A.constructor=z,a._engine.path=function(a,b){var c=q("path");b.canvas&&b.canvas.appendChild(c);var d=new z(c,b);d.type="path",w(d,{fill:"none",stroke:"#000",path:a});return d},A.rotate=function(a,b,e){if(this.removed)return this;a=c(a).split(j),a.length-1&&(b=d(a[1]),e=d(a[2])),a=d(a[0]),e==null&&(b=e);if(b==null||e==null){var f=this.getBBox(1);b=f.x+f.width/2,e=f.y+f.height/2}this.transform(this._.transform.concat([["r",a,b,e]]));return this},A.scale=function(a,b,e,f){if(this.removed)return this;a=c(a).split(j),a.length-1&&(b=d(a[1]),e=d(a[2]),f=d(a[3])),a=d(a[0]),b==null&&(b=a),f==null&&(e=f);if(e==null||f==null)var g=this.getBBox(1);e=e==null?g.x+g.width/2:e,f=f==null?g.y+g.height/2:f,this.transform(this._.transform.concat([["s",a,b,e,f]]));return this},A.translate=function(a,b){if(this.removed)return this;a=c(a).split(j),a.length-1&&(b=d(a[1])),a=d(a[0])||0,b=+b||0,this.transform(this._.transform.concat([["t",a,b]]));return this},A.transform=function(c){var d=this._;if(c==null)return d.transform;a._extractTransform(this,c),this.clip&&q(this.clip,{transform:this.matrix.invert()}),this.pattern&&s(this),this.node&&q(this.node,{transform:this.matrix});if(d.sx!=1||d.sy!=1){var e=this.attrs[b]("stroke-width")?this.attrs["stroke-width"]:1;this.attr({"stroke-width":e})}return this},A.hide=function(){!this.removed&&this.paper.safari(this.node.style.display="none");return this},A.show=function(){!this.removed&&this.paper.safari(this.node.style.display="");return this},A.remove=function(){if(!this.removed&&!!this.node.parentNode){var b=this.paper;b.__set__&&b.__set__.exclude(this),k.unbind("raphael.*.*."+this.id),this.gradient&&b.defs.removeChild(this.gradient),a._tear(this,b),this.node.parentNode.tagName.toLowerCase()=="a"?this.node.parentNode.parentNode.removeChild(this.node.parentNode):this.node.parentNode.removeChild(this.node);for(var c in this)this[c]=typeof this[c]=="function"?a._removedFactory(c):null;this.removed=!0}},A._getBBox=function(){if(this.node.style.display=="none"){this.show();var a=!0}var b={};try{b=this.node.getBBox()}catch(c){}finally{b=b||{}}a&&this.hide();return b},A.attr=function(c,d){if(this.removed)return this;if(c==null){var e={};for(var f in this.attrs)this.attrs[b](f)&&(e[f]=this.attrs[f]);e.gradient&&e.fill=="none"&&(e.fill=e.gradient)&&delete e.gradient,e.transform=this._.transform;return e}if(d==null&&a.is(c,"string")){if(c=="fill"&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;if(c=="transform")return this._.transform;var g=c.split(j),h={};for(var i=0,l=g.length;i<l;i++)c=g[i],c in this.attrs?h[c]=this.attrs[c]:a.is(this.paper.customAttributes[c],"function")?h[c]=this.paper.customAttributes[c].def:h[c]=a._availableAttrs[c];return l-1?h:h[g[0]]}if(d==null&&a.is(c,"array")){h={};for(i=0,l=c.length;i<l;i++)h[c[i]]=this.attr(c[i]);return h}if(d!=null){var m={};m[c]=d}else c!=null&&a.is(c,"object")&&(m=c);for(var n in m)k("raphael.attr."+n+"."+this.id,this,m[n]);for(n in this.paper.customAttributes)if(this.paper.customAttributes[b](n)&&m[b](n)&&a.is(this.paper.customAttributes[n],"function")){var o=this.paper.customAttributes[n].apply(this,[].concat(m[n]));this.attrs[n]=m[n];for(var p in o)o[b](p)&&(m[p]=o[p])}w(this,m);return this},A.toFront=function(){if(this.removed)return this;this.node.parentNode.tagName.toLowerCase()=="a"?this.node.parentNode.parentNode.appendChild(this.node.parentNode):this.node.parentNode.appendChild(this.node);var b=this.paper;b.top!=this&&a._tofront(this,b);return this},A.toBack=function(){if(this.removed)return this;var b=this.node.parentNode;b.tagName.toLowerCase()=="a"?b.parentNode.insertBefore(this.node.parentNode,this.node.parentNode.parentNode.firstChild):b.firstChild!=this.node&&b.insertBefore(this.node,this.node.parentNode.firstChild),a._toback(this,this.paper);var c=this.paper;return this},A.insertAfter=function(b){if(this.removed)return this;var c=b.node||b[b.length-1].node;c.nextSibling?c.parentNode.insertBefore(this.node,c.nextSibling):c.parentNode.appendChild(this.node),a._insertafter(this,b,this.paper);return this},A.insertBefore=function(b){if(this.removed)return this;var c=b.node||b[0].node;c.parentNode.insertBefore(this.node,c),a._insertbefore(this,b,this.paper);return this},A.blur=function(b){var c=this;if(+b!==0){var d=q("filter"),e=q("feGaussianBlur");c.attrs.blur=b,d.id=a.createUUID(),q(e,{stdDeviation:+b||1.5}),d.appendChild(e),c.paper.defs.appendChild(d),c._blur=d,q(c.node,{filter:"url(#"+d.id+")"})}else c._blur&&(c._blur.parentNode.removeChild(c._blur),delete c._blur,delete c.attrs.blur),c.node.removeAttribute("filter")},a._engine.circle=function(a,b,c,d){var e=q("circle");a.canvas&&a.canvas.appendChild(e);var f=new z(e,a);f.attrs={cx:b,cy:c,r:d,fill:"none",stroke:"#000"},f.type="circle",q(e,f.attrs);return f},a._engine.rect=function(a,b,c,d,e,f){var g=q("rect");a.canvas&&a.canvas.appendChild(g);var h=new z(g,a);h.attrs={x:b,y:c,width:d,height:e,r:f||0,rx:f||0,ry:f||0,fill:"none",stroke:"#000"},h.type="rect",q(g,h.attrs);return h},a._engine.ellipse=function(a,b,c,d,e){var f=q("ellipse");a.canvas&&a.canvas.appendChild(f);var g=new z(f,a);g.attrs={cx:b,cy:c,rx:d,ry:e,fill:"none",stroke:"#000"},g.type="ellipse",q(f,g.attrs);return g},a._engine.image=function(a,b,c,d,e,f){var g=q("image");q(g,{x:c,y:d,width:e,height:f,preserveAspectRatio:"none"}),g.setAttributeNS(n,"href",b),a.canvas&&a.canvas.appendChild(g);var h=new z(g,a);h.attrs={x:c,y:d,width:e,height:f,src:b},h.type="image";return h},a._engine.text=function(b,c,d,e){var f=q("text");b.canvas&&b.canvas.appendChild(f);var g=new z(f,b);g.attrs={x:c,y:d,"text-anchor":"middle",text:e,font:a._availableAttrs.font,stroke:"none",fill:"#000"},g.type="text",w(g,g.attrs);return g},a._engine.setSize=function(a,b){this.width=a||this.width,this.height=b||this.height,this.canvas.setAttribute("width",this.width),this.canvas.setAttribute("height",this.height),this._viewBox&&this.setViewBox.apply(this,this._viewBox);return this},a._engine.create=function(){var b=a._getContainer.apply(0,arguments),c=b&&b.container,d=b.x,e=b.y,f=b.width,g=b.height;if(!c)throw new Error("SVG container not found.");var h=q("svg"),i="overflow:hidden;",j;d=d||0,e=e||0,f=f||512,g=g||342,q(h,{height:g,version:1.1,width:f,xmlns:"http://www.w3.org/2000/svg"}),c==1?(h.style.cssText=i+"position:absolute;left:"+d+"px;top:"+e+"px",a._g.doc.body.appendChild(h),j=1):(h.style.cssText=i+"position:relative",c.firstChild?c.insertBefore(h,c.firstChild):c.appendChild(h)),c=new a._Paper,c.width=f,c.height=g,c.canvas=h,c.clear(),c._left=c._top=0,j&&(c.renderfix=function(){}),c.renderfix();return c},a._engine.setViewBox=function(a,b,c,d,e){k("raphael.setViewBox",this,this._viewBox,[a,b,c,d,e]);var f=g(c/this.width,d/this.height),h=this.top,i=e?"meet":"xMinYMin",j,l;a==null?(this._vbSize&&(f=1),delete this._vbSize,j="0 0 "+this.width+m+this.height):(this._vbSize=f,j=a+m+b+m+c+m+d),q(this.canvas,{viewBox:j,preserveAspectRatio:i});while(f&&h)l="stroke-width"in h.attrs?h.attrs["stroke-width"]:1,h.attr({"stroke-width":l}),h._.dirty=1,h._.dirtyT=1,h=h.prev;this._viewBox=[a,b,c,d,!!e];return this},a.prototype.renderfix=function(){var a=this.canvas,b=a.style,c;try{c=a.getScreenCTM()||a.createSVGMatrix()}catch(d){c=a.createSVGMatrix()}var e=-c.e%1,f=-c.f%1;if(e||f)e&&(this._left=(this._left+e)%1,b.left=this._left+"px"),f&&(this._top=(this._top+f)%1,b.top=this._top+"px")},a.prototype.clear=function(){a.eve("raphael.clear",this);var b=this.canvas;while(b.firstChild)b.removeChild(b.firstChild);this.bottom=this.top=null,(this.desc=q("desc")).appendChild(a._g.doc.createTextNode("Created with Raphaël "+a.version)),b.appendChild(this.desc),b.appendChild(this.defs=q("defs"))},a.prototype.remove=function(){k("raphael.remove",this),this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var b in this)this[b]=typeof this[b]=="function"?a._removedFactory(b):null};var B=a.st;for(var C in A)A[b](C)&&!B[b](C)&&(B[C]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}(C))}(window.Raphael),window.Raphael.vml&&function(a){var b="hasOwnProperty",c=String,d=parseFloat,e=Math,f=e.round,g=e.max,h=e.min,i=e.abs,j="fill",k=/[, ]+/,l=a.eve,m=" progid:DXImageTransform.Microsoft",n=" ",o="",p={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},q=/([clmz]),?([^clmz]*)/gi,r=/ progid:\S+Blur\([^\)]+\)/g,s=/-?[^,\s-]+/g,t="position:absolute;left:0;top:0;width:1px;height:1px",u=21600,v={path:1,rect:1,image:1},w={circle:1,ellipse:1},x=function(b){var d=/[ahqstv]/ig,e=a._pathToAbsolute;c(b).match(d)&&(e=a._path2curve),d=/[clmz]/g;if(e==a._pathToAbsolute&&!c(b).match(d)){var g=c(b).replace(q,function(a,b,c){var d=[],e=b.toLowerCase()=="m",g=p[b];c.replace(s,function(a){e&&d.length==2&&(g+=d+p[b=="m"?"l":"L"],d=[]),d.push(f(a*u))});return g+d});return g}var h=e(b),i,j;g=[];for(var k=0,l=h.length;k<l;k++){i=h[k],j=h[k][0].toLowerCase(),j=="z"&&(j="x");for(var m=1,r=i.length;m<r;m++)j+=f(i[m]*u)+(m!=r-1?",":o);g.push(j)}return g.join(n)},y=function(b,c,d){var e=a.matrix();e.rotate(-b,.5,.5);return{dx:e.x(c,d),dy:e.y(c,d)}},z=function(a,b,c,d,e,f){var g=a._,h=a.matrix,k=g.fillpos,l=a.node,m=l.style,o=1,p="",q,r=u/b,s=u/c;m.visibility="hidden";if(!!b&&!!c){l.coordsize=i(r)+n+i(s),m.rotation=f*(b*c<0?-1:1);if(f){var t=y(f,d,e);d=t.dx,e=t.dy}b<0&&(p+="x"),c<0&&(p+=" y")&&(o=-1),m.flip=p,l.coordorigin=d*-r+n+e*-s;if(k||g.fillsize){var v=l.getElementsByTagName(j);v=v&&v[0],l.removeChild(v),k&&(t=y(f,h.x(k[0],k[1]),h.y(k[0],k[1])),v.position=t.dx*o+n+t.dy*o),g.fillsize&&(v.size=g.fillsize[0]*i(b)+n+g.fillsize[1]*i(c)),l.appendChild(v)}m.visibility="visible"}};a.toString=function(){return"Your browser doesn’t support SVG. Falling down to VML.\nYou are running Raphaël "+this.version};var A=function(a,b,d){var e=c(b).toLowerCase().split("-"),f=d?"end":"start",g=e.length,h="classic",i="medium",j="medium";while(g--)switch(e[g]){case"block":case"classic":case"oval":case"diamond":case"open":case"none":h=e[g];break;case"wide":case"narrow":j=e[g];break;case"long":case"short":i=e[g]}var k=a.node.getElementsByTagName("stroke")[0];k[f+"arrow"]=h,k[f+"arrowlength"]=i,k[f+"arrowwidth"]=j},B=function(e,i){e.attrs=e.attrs||{};var l=e.node,m=e.attrs,p=l.style,q,r=v[e.type]&&(i.x!=m.x||i.y!=m.y||i.width!=m.width||i.height!=m.height||i.cx!=m.cx||i.cy!=m.cy||i.rx!=m.rx||i.ry!=m.ry||i.r!=m.r),s=w[e.type]&&(m.cx!=i.cx||m.cy!=i.cy||m.r!=i.r||m.rx!=i.rx||m.ry!=i.ry),t=e;for(var y in i)i[b](y)&&(m[y]=i[y]);r&&(m.path=a._getPath[e.type](e),e._.dirty=1),i.href&&(l.href=i.href),i.title&&(l.title=i.title),i.target&&(l.target=i.target),i.cursor&&(p.cursor=i.cursor),"blur"in i&&e.blur(i.blur);if(i.path&&e.type=="path"||r)l.path=x(~c(m.path).toLowerCase().indexOf("r")?a._pathToAbsolute(m.path):m.path),e.type=="image"&&(e._.fillpos=[m.x,m.y],e._.fillsize=[m.width,m.height],z(e,1,1,0,0,0));"transform"in i&&e.transform(i.transform);if(s){var B=+m.cx,D=+m.cy,E=+m.rx||+m.r||0,G=+m.ry||+m.r||0;l.path=a.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x",f((B-E)*u),f((D-G)*u),f((B+E)*u),f((D+G)*u),f(B*u))}if("clip-rect"in i){var H=c(i["clip-rect"]).split(k);if(H.length==4){H[2]=+H[2]+ +H[0],H[3]=+H[3]+ +H[1];var I=l.clipRect||a._g.doc.createElement("div"),J=I.style;J.clip=a.format("rect({1}px {2}px {3}px {0}px)",H),l.clipRect||(J.position="absolute",J.top=0,J.left=0,J.width=e.paper.width+"px",J.height=e.paper.height+"px",l.parentNode.insertBefore(I,l),I.appendChild(l),l.clipRect=I)}i["clip-rect"]||l.clipRect&&(l.clipRect.style.clip="auto")}if(e.textpath){var K=e.textpath.style;i.font&&(K.font=i.font),i["font-family"]&&(K.fontFamily='"'+i["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g,o)+'"'),i["font-size"]&&(K.fontSize=i["font-size"]),i["font-weight"]&&(K.fontWeight=i["font-weight"]),i["font-style"]&&(K.fontStyle=i["font-style"])}"arrow-start"in i&&A(t,i["arrow-start"]),"arrow-end"in i&&A(t,i["arrow-end"],1);if(i.opacity!=null||i["stroke-width"]!=null||i.fill!=null||i.src!=null||i.stroke!=null||i["stroke-width"]!=null||i["stroke-opacity"]!=null||i["fill-opacity"]!=null||i["stroke-dasharray"]!=null||i["stroke-miterlimit"]!=null||i["stroke-linejoin"]!=null||i["stroke-linecap"]!=null){var L=l.getElementsByTagName(j),M=!1;L=L&&L[0],!L&&(M=L=F(j)),e.type=="image"&&i.src&&(L.src=i.src),i.fill&&(L.on=!0);if(L.on==null||i.fill=="none"||i.fill===null)L.on=!1;if(L.on&&i.fill){var N=c(i.fill).match(a._ISURL);if(N){L.parentNode==l&&l.removeChild(L),L.rotate=!0,L.src=N[1],L.type="tile";var O=e.getBBox(1);L.position=O.x+n+O.y,e._.fillpos=[O.x,O.y],a._preload(N[1],function(){e._.fillsize=[this.offsetWidth,this.offsetHeight]})}else L.color=a.getRGB(i.fill).hex,L.src=o,L.type="solid",a.getRGB(i.fill).error&&(t.type in{circle:1,ellipse:1}||c(i.fill).charAt()!="r")&&C(t,i.fill,L)&&(m.fill="none",m.gradient=i.fill,L.rotate=!1)}if("fill-opacity"in i||"opacity"in i){var P=((+m["fill-opacity"]+1||2)-1)*((+m.opacity+1||2)-1)*((+a.getRGB(i.fill).o+1||2)-1);P=h(g(P,0),1),L.opacity=P,L.src&&(L.color="none")}l.appendChild(L);var Q=l.getElementsByTagName("stroke")&&l.getElementsByTagName("stroke")[0],T=!1;!Q&&(T=Q=F("stroke"));if(i.stroke&&i.stroke!="none"||i["stroke-width"]||i["stroke-opacity"]!=null||i["stroke-dasharray"]||i["stroke-miterlimit"]||i["stroke-linejoin"]||i["stroke-linecap"])Q.on=!0;(i.stroke=="none"||i.stroke===null||Q.on==null||i.stroke==0||i["stroke-width"]==0)&&(Q.on=!1);var U=a.getRGB(i.stroke);Q.on&&i.stroke&&(Q.color=U.hex),P=((+m["stroke-opacity"]+1||2)-1)*((+m.opacity+1||2)-1)*((+U.o+1||2)-1);var V=(d(i["stroke-width"])||1)*.75;P=h(g(P,0),1),i["stroke-width"]==null&&(V=m["stroke-width"]),i["stroke-width"]&&(Q.weight=V),V&&V<1&&(P*=V)&&(Q.weight=1),Q.opacity=P,i["stroke-linejoin"]&&(Q.joinstyle=i["stroke-linejoin"]||"miter"),Q.miterlimit=i["stroke-miterlimit"]||8,i["stroke-linecap"]&&(Q.endcap=i["stroke-linecap"]=="butt"?"flat":i["stroke-linecap"]=="square"?"square":"round");if(i["stroke-dasharray"]){var W={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};Q.dashstyle=W[b](i["stroke-dasharray"])?W[i["stroke-dasharray"]]:o}T&&l.appendChild(Q)}if(t.type=="text"){t.paper.canvas.style.display=o;var X=t.paper.span,Y=100,Z=m.font&&m.font.match(/\d+(?:\.\d*)?(?=px)/);p=X.style,m.font&&(p.font=m.font),m["font-family"]&&(p.fontFamily=m["font-family"]),m["font-weight"]&&(p.fontWeight=m["font-weight"]),m["font-style"]&&(p.fontStyle=m["font-style"]),Z=d(m["font-size"]||Z&&Z[0])||10,p.fontSize=Z*Y+"px",t.textpath.string&&(X.innerHTML=c(t.textpath.string).replace(/</g,"&#60;").replace(/&/g,"&#38;").replace(/\n/g,"<br>"));var $=X.getBoundingClientRect();t.W=m.w=($.right-$.left)/Y,t.H=m.h=($.bottom-$.top)/Y,t.X=m.x,t.Y=m.y+t.H/2,("x"in i||"y"in i)&&(t.path.v=a.format("m{0},{1}l{2},{1}",f(m.x*u),f(m.y*u),f(m.x*u)+1));var _=["x","y","text","font","font-family","font-weight","font-style","font-size"];for(var ba=0,bb=_.length;ba<bb;ba++)if(_[ba]in i){t._.dirty=1;break}switch(m["text-anchor"]){case"start":t.textpath.style["v-text-align"]="left",t.bbx=t.W/2;break;case"end":t.textpath.style["v-text-align"]="right",t.bbx=-t.W/2;break;default:t.textpath.style["v-text-align"]="center",t.bbx=0}t.textpath.style["v-text-kern"]=!0}},C=function(b,f,g){b.attrs=b.attrs||{};var h=b.attrs,i=Math.pow,j,k,l="linear",m=".5 .5";b.attrs.gradient=f,f=c(f).replace(a._radial_gradient,function(a,b,c){l="radial",b&&c&&(b=d(b),c=d(c),i(b-.5,2)+i(c-.5,2)>.25&&(c=e.sqrt(.25-i(b-.5,2))*((c>.5)*2-1)+.5),m=b+n+c);return o}),f=f.split(/\s*\-\s*/);if(l=="linear"){var p=f.shift();p=-d(p);if(isNaN(p))return null}var q=a._parseDots(f);if(!q)return null;b=b.shape||b.node;if(q.length){b.removeChild(g),g.on=!0,g.method="none",g.color=q[0].color,g.color2=q[q.length-1].color;var r=[];for(var s=0,t=q.length;s<t;s++)q[s].offset&&r.push(q[s].offset+n+q[s].color);g.colors=r.length?r.join():"0% "+g.color,l=="radial"?(g.type="gradientTitle",g.focus="100%",g.focussize="0 0",g.focusposition=m,g.angle=0):(g.type="gradient",g.angle=(270-p)%360),b.appendChild(g)}return 1},D=function(b,c){this[0]=this.node=b,b.raphael=!0,this.id=a._oid++,b.raphaelid=this.id,this.X=0,this.Y=0,this.attrs={},this.paper=c,this.matrix=a.matrix(),this._={transform:[],sx:1,sy:1,dx:0,dy:0,deg:0,dirty:1,dirtyT:1},!c.bottom&&(c.bottom=this),this.prev=c.top,c.top&&(c.top.next=this),c.top=this,this.next=null},E=a.el;D.prototype=E,E.constructor=D,E.transform=function(b){if(b==null)return this._.transform;var d=this.paper._viewBoxShift,e=d?"s"+[d.scale,d.scale]+"-1-1t"+[d.dx,d.dy]:o,f;d&&(f=b=c(b).replace(/\.{3}|\u2026/g,this._.transform||o)),a._extractTransform(this,e+b);var g=this.matrix.clone(),h=this.skew,i=this.node,j,k=~c(this.attrs.fill).indexOf("-"),l=!c(this.attrs.fill).indexOf("url(");g.translate(-0.5,-0.5);if(l||k||this.type=="image"){h.matrix="1 0 0 1",h.offset="0 0",j=g.split();if(k&&j.noRotation||!j.isSimple){i.style.filter=g.toFilter();var m=this.getBBox(),p=this.getBBox(1),q=m.x-p.x,r=m.y-p.y;i.coordorigin=q*-u+n+r*-u,z(this,1,1,q,r,0)}else i.style.filter=o,z(this,j.scalex,j.scaley,j.dx,j.dy,j.rotate)}else i.style.filter=o,h.matrix=c(g),h.offset=g.offset();f&&(this._.transform=f);return this},E.rotate=function(a,b,e){if(this.removed)return this;if(a!=null){a=c(a).split(k),a.length-1&&(b=d(a[1]),e=d(a[2])),a=d(a[0]),e==null&&(b=e);if(b==null||e==null){var f=this.getBBox(1);b=f.x+f.width/2,e=f.y+f.height/2}this._.dirtyT=1,this.transform(this._.transform.concat([["r",a,b,e]]));return this}},E.translate=function(a,b){if(this.removed)return this;a=c(a).split(k),a.length-1&&(b=d(a[1])),a=d(a[0])||0,b=+b||0,this._.bbox&&(this._.bbox.x+=a,this._.bbox.y+=b),this.transform(this._.transform.concat([["t",a,b]]));return this},E.scale=function(a,b,e,f){if(this.removed)return this;a=c(a).split(k),a.length-1&&(b=d(a[1]),e=d(a[2]),f=d(a[3]),isNaN(e)&&(e=null),isNaN(f)&&(f=null)),a=d(a[0]),b==null&&(b=a),f==null&&(e=f);if(e==null||f==null)var g=this.getBBox(1);e=e==null?g.x+g.width/2:e,f=f==null?g.y+g.height/2:f,this.transform(this._.transform.concat([["s",a,b,e,f]])),this._.dirtyT=1;return this},E.hide=function(){!this.removed&&(this.node.style.display="none");return this},E.show=function(){!this.removed&&(this.node.style.display=o);return this},E._getBBox=function(){if(this.removed)return{};return{x:this.X+(this.bbx||0)-this.W/2,y:this.Y-this.H,width:this.W,height:this.H}},E.remove=function(){if(!this.removed&&!!this.node.parentNode){this.paper.__set__&&this.paper.__set__.exclude(this),a.eve.unbind("raphael.*.*."+this.id),a._tear(this,this.paper),this.node.parentNode.removeChild(this.node),this.shape&&this.shape.parentNode.removeChild(this.shape);for(var b in this)this[b]=typeof this[b]=="function"?a._removedFactory(b):null;this.removed=!0}},E.attr=function(c,d){if(this.removed)return this;if(c==null){var e={};for(var f in this.attrs)this.attrs[b](f)&&(e[f]=this.attrs[f]);e.gradient&&e.fill=="none"&&(e.fill=e.gradient)&&delete e.gradient,e.transform=this._.transform;return e}if(d==null&&a.is(c,"string")){if(c==j&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;var g=c.split(k),h={};for(var i=0,m=g.length;i<m;i++)c=g[i],c in this.attrs?h[c]=this.attrs[c]:a.is(this.paper.customAttributes[c],"function")?h[c]=this.paper.customAttributes[c].def:h[c]=a._availableAttrs[c];return m-1?h:h[g[0]]}if(this.attrs&&d==null&&a.is(c,"array")){h={};for(i=0,m=c.length;i<m;i++)h[c[i]]=this.attr(c[i]);return h}var n;d!=null&&(n={},n[c]=d),d==null&&a.is(c,"object")&&(n=c);for(var o in n)l("raphael.attr."+o+"."+this.id,this,n[o]);if(n){for(o in this.paper.customAttributes)if(this.paper.customAttributes[b](o)&&n[b](o)&&a.is(this.paper.customAttributes[o],"function")){var p=this.paper.customAttributes[o].apply(this,[].concat(n[o]));this.attrs[o]=n[o];for(var q in p)p[b](q)&&(n[q]=p[q])}n.text&&this.type=="text"&&(this.textpath.string=n.text),B(this,n)}return this},E.toFront=function(){!this.removed&&this.node.parentNode.appendChild(this.node),this.paper&&this.paper.top!=this&&a._tofront(this,this.paper);return this},E.toBack=function(){if(this.removed)return this;this.node.parentNode.firstChild!=this.node&&(this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild),a._toback(this,this.paper));return this},E.insertAfter=function(b){if(this.removed)return this;b.constructor==a.st.constructor&&(b=b[b.length-1]),b.node.nextSibling?b.node.parentNode.insertBefore(this.node,b.node.nextSibling):b.node.parentNode.appendChild(this.node),a._insertafter(this,b,this.paper);return this},E.insertBefore=function(b){if(this.removed)return this;b.constructor==a.st.constructor&&(b=b[0]),b.node.parentNode.insertBefore(this.node,b.node),a._insertbefore(this,b,this.paper);return this},E.blur=function(b){var c=this.node.runtimeStyle,d=c.filter;d=d.replace(r,o),+b!==0?(this.attrs.blur=b,c.filter=d+n+m+".Blur(pixelradius="+(+b||1.5)+")",c.margin=a.format("-{0}px 0 0 -{0}px",f(+b||1.5))):(c.filter=d,c.margin=0,delete this.attrs.blur)},a._engine.path=function(a,b){var c=F("shape");c.style.cssText=t,c.coordsize=u+n+u,c.coordorigin=b.coordorigin;var d=new D(c,b),e={fill:"none",stroke:"#000"};a&&(e.path=a),d.type="path",d.path=[],d.Path=o,B(d,e),b.canvas.appendChild(c);var f=F("skew");f.on=!0,c.appendChild(f),d.skew=f,d.transform(o);return d},a._engine.rect=function(b,c,d,e,f,g){var h=a._rectPath(c,d,e,f,g),i=b.path(h),j=i.attrs;i.X=j.x=c,i.Y=j.y=d,i.W=j.width=e,i.H=j.height=f,j.r=g,j.path=h,i.type="rect";return i},a._engine.ellipse=function(a,b,c,d,e){var f=a.path(),g=f.attrs;f.X=b-d,f.Y=c-e,f.W=d*2,f.H=e*2,f.type="ellipse",B(f,{cx:b,cy:c,rx:d,ry:e});return f},a._engine.circle=function(a,b,c,d){var e=a.path(),f=e.attrs;e.X=b-d,e.Y=c-d,e.W=e.H=d*2,e.type="circle",B(e,{cx:b,cy:c,r:d});return e},a._engine.image=function(b,c,d,e,f,g){var h=a._rectPath(d,e,f,g),i=b.path(h).attr({stroke:"none"}),k=i.attrs,l=i.node,m=l.getElementsByTagName(j)[0];k.src=c,i.X=k.x=d,i.Y=k.y=e,i.W=k.width=f,i.H=k.height=g,k.path=h,i.type="image",m.parentNode==l&&l.removeChild(m),m.rotate=!0,m.src=c,m.type="tile",i._.fillpos=[d,e],i._.fillsize=[f,g],l.appendChild(m),z(i,1,1,0,0,0);return i},a._engine.text=function(b,d,e,g){var h=F("shape"),i=F("path"),j=F("textpath");d=d||0,e=e||0,g=g||"",i.v=a.format("m{0},{1}l{2},{1}",f(d*u),f(e*u),f(d*u)+1),i.textpathok=!0,j.string=c(g),j.on=!0,h.style.cssText=t,h.coordsize=u+n+u,h.coordorigin="0 0";var k=new D(h,b),l={fill:"#000",stroke:"none",font:a._availableAttrs.font,text:g};k.shape=h,k.path=i,k.textpath=j,k.type="text",k.attrs.text=c(g),k.attrs.x=d,k.attrs.y=e,k.attrs.w=1,k.attrs.h=1,B(k,l),h.appendChild(j),h.appendChild(i),b.canvas.appendChild(h);var m=F("skew");m.on=!0,h.appendChild(m),k.skew=m,k.transform(o);return k},a._engine.setSize=function(b,c){var d=this.canvas.style;this.width=b,this.height=c,b==+b&&(b+="px"),c==+c&&(c+="px"),d.width=b,d.height=c,d.clip="rect(0 "+b+" "+c+" 0)",this._viewBox&&a._engine.setViewBox.apply(this,this._viewBox);return this},a._engine.setViewBox=function(b,c,d,e,f){a.eve("raphael.setViewBox",this,this._viewBox,[b,c,d,e,f]);var h=this.width,i=this.height,j=1/g(d/h,e/i),k,l;f&&(k=i/e,l=h/d,d*k<h&&(b-=(h-d*k)/2/k),e*l<i&&(c-=(i-e*l)/2/l)),this._viewBox=[b,c,d,e,!!f],this._viewBoxShift={dx:-b,dy:-c,scale:j},this.forEach(function(a){a.transform("...")});return this};var F;a._engine.initWin=function(a){var b=a.document;b.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)");try{!b.namespaces.rvml&&b.namespaces.add("rvml","urn:schemas-microsoft-com:vml"),F=function(a){return b.createElement("<rvml:"+a+' class="rvml">')}}catch(c){F=function(a){return b.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}},a._engine.initWin(a._g.win),a._engine.create=function(){var b=a._getContainer.apply(0,arguments),c=b.container,d=b.height,e,f=b.width,g=b.x,h=b.y;if(!c)throw new Error("VML container not found.");var i=new a._Paper,j=i.canvas=a._g.doc.createElement("div"),k=j.style;g=g||0,h=h||0,f=f||512,d=d||342,i.width=f,i.height=d,f==+f&&(f+="px"),d==+d&&(d+="px"),i.coordsize=u*1e3+n+u*1e3,i.coordorigin="0 0",i.span=a._g.doc.createElement("span"),i.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;",j.appendChild(i.span),k.cssText=a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",f,d),c==1?(a._g.doc.body.appendChild(j),k.left=g+"px",k.top=h+"px",k.position="absolute"):c.firstChild?c.insertBefore(j,c.firstChild):c.appendChild(j),i.renderfix=function(){};return i},a.prototype.clear=function(){a.eve("raphael.clear",this),this.canvas.innerHTML=o,this.span=a._g.doc.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas.appendChild(this.span),this.bottom=this.top=null},a.prototype.remove=function(){a.eve("raphael.remove",this),this.canvas.parentNode.removeChild(this.canvas);for(var b in this)this[b]=typeof this[b]=="function"?a._removedFactory(b):null;return!0};var G=a.st;for(var H in E)E[b](H)&&!G[b](H)&&(G[H]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}(H))}(window.Raphael); \ No newline at end of file
+(function (a) {
+ var b = "0.3.4", c = "hasOwnProperty", d = /[\.\/]/, e = "*", f = function () {}, g = function (a, b) {return a - b}, h, i, j = {n: {}}, k = function (a, b) {
+ var c = j, d = i, e = Array.prototype.slice.call(arguments, 2), f = k.listeners(a), l = 0, m = !1, n, o = [], p = {}, q = [], r = h, s = [];
+ h = a, i = 0;
+ for (var t = 0, u = f.length; t < u; t++)"zIndex"in f[t] && (o.push(f[t].zIndex), f[t].zIndex < 0 && (p[f[t].zIndex] = f[t]));
+ o.sort(g);
+ while (o[l] < 0) {
+ n = p[o[l++]], q.push(n.apply(b, e));
+ if (i) {
+ i = d;
+ return q
+ }
+ }
+ for (t = 0; t < u; t++) {
+ n = f[t];
+ if ("zIndex"in n)if (n.zIndex == o[l]) {
+ q.push(n.apply(b, e));
+ if (i)break;
+ do {
+ l++, n = p[o[l]], n && q.push(n.apply(b, e));
+ if (i)break
+ } while (n)
+ } else p[n.zIndex] = n; else {
+ q.push(n.apply(b, e));
+ if (i)break
+ }
+ }
+ i = d, h = r;
+ return q.length ? q : null
+ };
+ k.listeners = function (a) {
+ var b = a.split(d), c = j, f, g, h, i, k, l, m, n, o = [c], p = [];
+ for (i = 0, k = b.length; i < k; i++) {
+ n = [];
+ for (l = 0, m = o.length; l < m; l++) {
+ c = o[l].n, g = [c[b[i]], c[e]], h = 2;
+ while (h--)f = g[h], f && (n.push(f), p = p.concat(f.f || []))
+ }
+ o = n
+ }
+ return p
+ }, k.on = function (a, b) {
+ var c = a.split(d), e = j;
+ for (var g = 0, h = c.length; g < h; g++)e = e.n, !e[c[g]] && (e[c[g]] = {n: {}}), e = e[c[g]];
+ e.f = e.f || [];
+ for (g = 0, h = e.f.length; g < h; g++)if (e.f[g] == b)return f;
+ e.f.push(b);
+ return function (a) {+a == +a && (b.zIndex = +a)}
+ }, k.stop = function () {i = 1}, k.nt = function (a) {
+ if (a)return(new RegExp("(?:\\.|\\/|^)" + a + "(?:\\.|\\/|$)")).test(h);
+ return h
+ }, k.off = k.unbind = function (a, b) {
+ var f = a.split(d), g, h, i, k, l, m, n, o = [j];
+ for (k = 0, l = f.length; k < l; k++)for (m = 0; m < o.length; m += i.length - 2) {
+ i = [m, 1], g = o[m].n;
+ if (f[k] != e)g[f[k]] && i.push(g[f[k]]); else for (h in g)g[c](h) && i.push(g[h]);
+ o.splice.apply(o, i)
+ }
+ for (k = 0, l = o.length; k < l; k++) {
+ g = o[k];
+ while (g.n) {
+ if (b) {
+ if (g.f) {
+ for (m = 0, n = g.f.length; m < n; m++)if (g.f[m] == b) {
+ g.f.splice(m, 1);
+ break
+ }
+ !g.f.length && delete g.f
+ }
+ for (h in g.n)if (g.n[c](h) && g.n[h].f) {
+ var p = g.n[h].f;
+ for (m = 0, n = p.length; m < n; m++)if (p[m] == b) {
+ p.splice(m, 1);
+ break
+ }
+ !p.length && delete g.n[h].f
+ }
+ } else {
+ delete g.f;
+ for (h in g.n)g.n[c](h) && g.n[h].f && delete g.n[h].f
+ }
+ g = g.n
+ }
+ }
+ }, k.once = function (a, b) {
+ var c = function () {
+ var d = b.apply(this, arguments);
+ k.unbind(a, c);
+ return d
+ };
+ return k.on(a, c)
+ }, k.version = b, k.toString = function () {return"You are running Eve " + b}, typeof module != "undefined" && module.exports ? module.exports = k : typeof define != "undefined" ? define("eve", [], function () {return k}) : a.eve = k
+})(this), function () {
+ function cF(a) {for (var b = 0; b < cy.length; b++)cy[b].el.paper == a && cy.splice(b--, 1)}
+
+ function cE(b, d, e, f, h, i) {
+ e = Q(e);
+ var j, k, l, m = [], o, p, q, t = b.ms, u = {}, v = {}, w = {};
+ if (f)for (y = 0, z = cy.length; y < z; y++) {
+ var x = cy[y];
+ if (x.el.id == d.id && x.anim == b) {
+ x.percent != e ? (cy.splice(y, 1), l = 1) : k = x, d.attr(x.totalOrigin);
+ break
+ }
+ } else f = +v;
+ for (var y = 0, z = b.percents.length; y < z; y++) {
+ if (b.percents[y] == e || b.percents[y] > f * b.top) {
+ e = b.percents[y], p = b.percents[y - 1] || 0, t = t / b.top * (e - p), o = b.percents[y + 1], j = b.anim[e];
+ break
+ }
+ f && d.attr(b.anim[b.percents[y]])
+ }
+ if (!!j) {
+ if (!k) {
+ for (var A in j)if (j[g](A))if (U[g](A) || d.paper.customAttributes[g](A)) {
+ u[A] = d.attr(A), u[A] == null && (u[A] = T[A]), v[A] = j[A];
+ switch (U[A]) {
+ case C:
+ w[A] = (v[A] - u[A]) / t;
+ break;
+ case"colour":
+ u[A] = a.getRGB(u[A]);
+ var B = a.getRGB(v[A]);
+ w[A] = {r: (B.r - u[A].r) / t, g: (B.g - u[A].g) / t, b: (B.b - u[A].b) / t};
+ break;
+ case"path":
+ var D = bR(u[A], v[A]), E = D[1];
+ u[A] = D[0], w[A] = [];
+ for (y = 0, z = u[A].length; y < z; y++) {
+ w[A][y] = [0];
+ for (var F = 1, G = u[A][y].length; F < G; F++)w[A][y][F] = (E[y][F] - u[A][y][F]) / t
+ }
+ break;
+ case"transform":
+ var H = d._, I = ca(H[A], v[A]);
+ if (I) {
+ u[A] = I.from, v[A] = I.to, w[A] = [], w[A].real = !0;
+ for (y = 0, z = u[A].length; y < z; y++) {
+ w[A][y] = [u[A][y][0]];
+ for (F = 1, G = u[A][y].length; F < G; F++)w[A][y][F] = (v[A][y][F] - u[A][y][F]) / t
+ }
+ } else {
+ var J = d.matrix || new cb, K = {_: {transform: H.transform}, getBBox: function () {return d.getBBox(1)}};
+ u[A] = [J.a, J.b, J.c, J.d, J.e, J.f], b$(K, v[A]), v[A] = K._.transform, w[A] = [(K.matrix.a - J.a) / t, (K.matrix.b - J.b) / t, (K.matrix.c - J.c) / t, (K.matrix.d - J.d) / t, (K.matrix.e - J.e) / t, (K.matrix.f - J.f) / t]
+ }
+ break;
+ case"csv":
+ var L = r(j[A])[s](c), M = r(u[A])[s](c);
+ if (A == "clip-rect") {
+ u[A] = M, w[A] = [], y = M.length;
+ while (y--)w[A][y] = (L[y] - u[A][y]) / t
+ }
+ v[A] = L;
+ break;
+ default:
+ L = [][n](j[A]), M = [][n](u[A]), w[A] = [], y = d.paper.customAttributes[A].length;
+ while (y--)w[A][y] = ((L[y] || 0) - (M[y] || 0)) / t
+ }
+ }
+ var O = j.easing, P = a.easing_formulas[O];
+ if (!P) {
+ P = r(O).match(N);
+ if (P && P.length == 5) {
+ var R = P;
+ P = function (a) {return cC(a, +R[1], +R[2], +R[3], +R[4], t)}
+ } else P = bf
+ }
+ q = j.start || b.start || +(new Date), x = {anim: b, percent: e, timestamp: q, start: q + (b.del || 0), status: 0, initstatus: f || 0, stop: !1, ms: t, easing: P, from: u, diff: w, to: v, el: d, callback: j.callback, prev: p, next: o, repeat: i || b.times, origin: d.attr(), totalOrigin: h}, cy.push(x);
+ if (f && !k && !l) {
+ x.stop = !0, x.start = new Date - t * f;
+ if (cy.length == 1)return cA()
+ }
+ l && (x.start = new Date - x.ms * f), cy.length == 1 && cz(cA)
+ } else k.initstatus = f, k.start = new Date - k.ms * f;
+ eve("raphael.anim.start." + d.id, d, b)
+ }
+ }
+
+ function cD(a, b) {
+ var c = [], d = {};
+ this.ms = b, this.times = 1;
+ if (a) {
+ for (var e in a)a[g](e) && (d[Q(e)] = a[e], c.push(Q(e)));
+ c.sort(bd)
+ }
+ this.anim = d, this.top = c[c.length - 1], this.percents = c
+ }
+
+ function cC(a, b, c, d, e, f) {
+ function o(a, b) {
+ var c, d, e, f, j, k;
+ for (e = a, k = 0; k < 8; k++) {
+ f = m(e) - a;
+ if (z(f) < b)return e;
+ j = (3 * i * e + 2 * h) * e + g;
+ if (z(j) < 1e-6)break;
+ e = e - f / j
+ }
+ c = 0, d = 1, e = a;
+ if (e < c)return c;
+ if (e > d)return d;
+ while (c < d) {
+ f = m(e);
+ if (z(f - a) < b)return e;
+ a > f ? c = e : d = e, e = (d - c) / 2 + c
+ }
+ return e
+ }
+
+ function n(a, b) {
+ var c = o(a, b);
+ return((l * c + k) * c + j) * c
+ }
+
+ function m(a) {return((i * a + h) * a + g) * a}
+
+ var g = 3 * b, h = 3 * (d - b) - g, i = 1 - g - h, j = 3 * c, k = 3 * (e - c) - j, l = 1 - j - k;
+ return n(a, 1 / (200 * f))
+ }
+
+ function cq() {return this.x + q + this.y + q + this.width + " × " + this.height}
+
+ function cp() {return this.x + q + this.y}
+
+ function cb(a, b, c, d, e, f) {a != null ? (this.a = +a, this.b = +b, this.c = +c, this.d = +d, this.e = +e, this.f = +f) : (this.a = 1, this.b = 0, this.c = 0, this.d = 1, this.e = 0, this.f = 0)}
+
+ function bH(b, c, d) {
+ b = a._path2curve(b), c = a._path2curve(c);
+ var e, f, g, h, i, j, k, l, m, n, o = d ? 0 : [];
+ for (var p = 0, q = b.length; p < q; p++) {
+ var r = b[p];
+ if (r[0] == "M")e = i = r[1], f = j = r[2]; else {
+ r[0] == "C" ? (m = [e, f].concat(r.slice(1)), e = m[6], f = m[7]) : (m = [e, f, e, f, i, j, i, j], e = i, f = j);
+ for (var s = 0, t = c.length; s < t; s++) {
+ var u = c[s];
+ if (u[0] == "M")g = k = u[1], h = l = u[2]; else {
+ u[0] == "C" ? (n = [g, h].concat(u.slice(1)), g = n[6], h = n[7]) : (n = [g, h, g, h, k, l, k, l], g = k, h = l);
+ var v = bG(m, n, d);
+ if (d)o += v; else {
+ for (var w = 0, x = v.length; w < x; w++)v[w].segment1 = p, v[w].segment2 = s, v[w].bez1 = m, v[w].bez2 = n;
+ o = o.concat(v)
+ }
+ }
+ }
+ }
+ }
+ return o
+ }
+
+ function bG(b, c, d) {
+ var e = a.bezierBBox(b), f = a.bezierBBox(c);
+ if (!a.isBBoxIntersect(e, f))return d ? 0 : [];
+ var g = bB.apply(0, b), h = bB.apply(0, c), i = ~~(g / 5), j = ~~(h / 5), k = [], l = [], m = {}, n = d ? 0 : [];
+ for (var o = 0; o < i + 1; o++) {
+ var p = a.findDotsAtSegment.apply(a, b.concat(o / i));
+ k.push({x: p.x, y: p.y, t: o / i})
+ }
+ for (o = 0; o < j + 1; o++)p = a.findDotsAtSegment.apply(a, c.concat(o / j)), l.push({x: p.x, y: p.y, t: o / j});
+ for (o = 0; o < i; o++)for (var q = 0; q < j; q++) {
+ var r = k[o], s = k[o + 1], t = l[q], u = l[q + 1], v = z(s.x - r.x) < .001 ? "y" : "x", w = z(u.x - t.x) < .001 ? "y" : "x", x = bD(r.x, r.y, s.x, s.y, t.x, t.y, u.x, u.y);
+ if (x) {
+ if (m[x.x.toFixed(4)] == x.y.toFixed(4))continue;
+ m[x.x.toFixed(4)] = x.y.toFixed(4);
+ var y = r.t + z((x[v] - r[v]) / (s[v] - r[v])) * (s.t - r.t), A = t.t + z((x[w] - t[w]) / (u[w] - t[w])) * (u.t - t.t);
+ y >= 0 && y <= 1 && A >= 0 && A <= 1 && (d ? n++ : n.push({x: x.x, y: x.y, t1: y, t2: A}))
+ }
+ }
+ return n
+ }
+
+ function bF(a, b) {return bG(a, b, 1)}
+
+ function bE(a, b) {return bG(a, b)}
+
+ function bD(a, b, c, d, e, f, g, h) {
+ if (!(x(a, c) < y(e, g) || y(a, c) > x(e, g) || x(b, d) < y(f, h) || y(b, d) > x(f, h))) {
+ var i = (a * d - b * c) * (e - g) - (a - c) * (e * h - f * g), j = (a * d - b * c) * (f - h) - (b - d) * (e * h - f * g), k = (a - c) * (f - h) - (b - d) * (e - g);
+ if (!k)return;
+ var l = i / k, m = j / k, n = +l.toFixed(2), o = +m.toFixed(2);
+ if (n < +y(a, c).toFixed(2) || n > +x(a, c).toFixed(2) || n < +y(e, g).toFixed(2) || n > +x(e, g).toFixed(2) || o < +y(b, d).toFixed(2) || o > +x(b, d).toFixed(2) || o < +y(f, h).toFixed(2) || o > +x(f, h).toFixed(2))return;
+ return{x: l, y: m}
+ }
+ }
+
+ function bC(a, b, c, d, e, f, g, h, i) {
+ if (!(i < 0 || bB(a, b, c, d, e, f, g, h) < i)) {
+ var j = 1, k = j / 2, l = j - k, m, n = .01;
+ m = bB(a, b, c, d, e, f, g, h, l);
+ while (z(m - i) > n)k /= 2, l += (m < i ? 1 : -1) * k, m = bB(a, b, c, d, e, f, g, h, l);
+ return l
+ }
+ }
+
+ function bB(a, b, c, d, e, f, g, h, i) {
+ i == null && (i = 1), i = i > 1 ? 1 : i < 0 ? 0 : i;
+ var j = i / 2, k = 12, l = [-0.1252, .1252, -0.3678, .3678, -0.5873, .5873, -0.7699, .7699, -0.9041, .9041, -0.9816, .9816], m = [.2491, .2491, .2335, .2335, .2032, .2032, .1601, .1601, .1069, .1069, .0472, .0472], n = 0;
+ for (var o = 0; o < k; o++) {
+ var p = j * l[o] + j, q = bA(p, a, c, e, g), r = bA(p, b, d, f, h), s = q * q + r * r;
+ n += m[o] * w.sqrt(s)
+ }
+ return j * n
+ }
+
+ function bA(a, b, c, d, e) {
+ var f = -3 * b + 9 * c - 9 * d + 3 * e, g = a * f + 6 * b - 12 * c + 6 * d;
+ return a * g - 3 * b + 3 * c
+ }
+
+ function by(a, b) {
+ var c = [];
+ for (var d = 0, e = a.length; e - 2 * !b > d; d += 2) {
+ var f = [
+ {x: +a[d - 2], y: +a[d - 1]},
+ {x: +a[d], y: +a[d + 1]},
+ {x: +a[d + 2], y: +a[d + 3]},
+ {x: +a[d + 4], y: +a[d + 5]}
+ ];
+ b ? d ? e - 4 == d ? f[3] = {x: +a[0], y: +a[1]} : e - 2 == d && (f[2] = {x: +a[0], y: +a[1]}, f[3] = {x: +a[2], y: +a[3]}) : f[0] = {x: +a[e - 2], y: +a[e - 1]} : e - 4 == d ? f[3] = f[2] : d || (f[0] = {x: +a[d], y: +a[d + 1]}), c.push(["C", (-f[0].x + 6 * f[1].x + f[2].x) / 6, (-f[0].y + 6 * f[1].y + f[2].y) / 6, (f[1].x + 6 * f[2].x - f[3].x) / 6, (f[1].y + 6 * f[2].y - f[3].y) / 6, f[2].x, f[2].y])
+ }
+ return c
+ }
+
+ function bx() {return this.hex}
+
+ function bv(a, b, c) {
+ function d() {
+ var e = Array.prototype.slice.call(arguments, 0), f = e.join("␀"), h = d.cache = d.cache || {}, i = d.count = d.count || [];
+ if (h[g](f)) {
+ bu(i, f);
+ return c ? c(h[f]) : h[f]
+ }
+ i.length >= 1e3 && delete h[i.shift()], i.push(f), h[f] = a[m](b, e);
+ return c ? c(h[f]) : h[f]
+ }
+
+ return d
+ }
+
+ function bu(a, b) {for (var c = 0, d = a.length; c < d; c++)if (a[c] === b)return a.push(a.splice(c, 1)[0])}
+
+ function bm(a) {
+ if (Object(a) !== a)return a;
+ var b = new a.constructor;
+ for (var c in a)a[g](c) && (b[c] = bm(a[c]));
+ return b
+ }
+
+ function a(c) {
+ if (a.is(c, "function"))return b ? c() : eve.on("raphael.DOMload", c);
+ if (a.is(c, E))return a._engine.create[m](a, c.splice(0, 3 + a.is(c[0], C))).add(c);
+ var d = Array.prototype.slice.call(arguments, 0);
+ if (a.is(d[d.length - 1], "function")) {
+ var e = d.pop();
+ return b ? e.call(a._engine.create[m](a, d)) : eve.on("raphael.DOMload", function () {e.call(a._engine.create[m](a, d))})
+ }
+ return a._engine.create[m](a, arguments)
+ }
+
+ a.version = "2.1.0", a.eve = eve;
+ var b, c = /[, ]+/, d = {circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1}, e = /\{(\d+)\}/g, f = "prototype", g = "hasOwnProperty", h = {doc: document, win: window}, i = {was: Object.prototype[g].call(h.win, "Raphael"), is: h.win.Raphael}, j = function () {this.ca = this.customAttributes = {}}, k, l = "appendChild", m = "apply", n = "concat", o = "createTouch"in h.doc, p = "", q = " ", r = String, s = "split", t = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[s](q), u = {mousedown: "touchstart", mousemove: "touchmove", mouseup: "touchend"}, v = r.prototype.toLowerCase, w = Math, x = w.max, y = w.min, z = w.abs, A = w.pow, B = w.PI, C = "number", D = "string", E = "array", F = "toString", G = "fill", H = Object.prototype.toString, I = {}, J = "push", K = a._ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i, L = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i, M = {NaN: 1, Infinity: 1, "-Infinity": 1}, N = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/, O = w.round, P = "setAttribute", Q = parseFloat, R = parseInt, S = r.prototype.toUpperCase, T = a._availableAttrs = {"arrow-end": "none", "arrow-start": "none", blur: 0, "clip-rect": "0 0 1e9 1e9", cursor: "default", cx: 0, cy: 0, fill: "#fff", "fill-opacity": 1, font: '10px "Arial"', "font-family": '"Arial"', "font-size": "10", "font-style": "normal", "font-weight": 400, gradient: 0, height: 0, href: "http://raphaeljs.com/", "letter-spacing": 0, opacity: 1, path: "M0,0", r: 0, rx: 0, ry: 0, src: "", stroke: "#000", "stroke-dasharray": "", "stroke-linecap": "butt", "stroke-linejoin": "butt", "stroke-miterlimit": 0, "stroke-opacity": 1, "stroke-width": 1, target: "_blank", "text-anchor": "middle", title: "Raphael", transform: "", width: 0, x: 0, y: 0}, U = a._availableAnimAttrs = {blur: C, "clip-rect": "csv", cx: C, cy: C, fill: "colour", "fill-opacity": C, "font-size": C, height: C, opacity: C, path: "path", r: C, rx: C, ry: C, stroke: "colour", "stroke-opacity": C, "stroke-width": C, transform: "transform", width: C, x: C, y: C}, V = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]/g, W = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/, X = {hs: 1, rg: 1}, Y = /,?([achlmqrstvxz]),?/gi, Z = /([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig, $ = /([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig, _ = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/ig, ba = a._radial_gradient = /^r(?:\(([^,]+?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*([^\)]+?)\))?/, bb = {}, bc = function (a, b) {return a.key - b.key}, bd = function (a, b) {return Q(a) - Q(b)}, be = function () {}, bf = function (a) {return a}, bg = a._rectPath = function (a, b, c, d, e) {
+ if (e)return[
+ ["M", a + e, b],
+ ["l", c - e * 2, 0],
+ ["a", e, e, 0, 0, 1, e, e],
+ ["l", 0, d - e * 2],
+ ["a", e, e, 0, 0, 1, -e, e],
+ ["l", e * 2 - c, 0],
+ ["a", e, e, 0, 0, 1, -e, -e],
+ ["l", 0, e * 2 - d],
+ ["a", e, e, 0, 0, 1, e, -e],
+ ["z"]
+ ];
+ return[
+ ["M", a, b],
+ ["l", c, 0],
+ ["l", 0, d],
+ ["l", -c, 0],
+ ["z"]
+ ]
+ }, bh = function (a, b, c, d) {
+ d == null && (d = c);
+ return[
+ ["M", a, b],
+ ["m", 0, -d],
+ ["a", c, d, 0, 1, 1, 0, 2 * d],
+ ["a", c, d, 0, 1, 1, 0, -2 * d],
+ ["z"]
+ ]
+ }, bi = a._getPath = {path: function (a) {return a.attr("path")}, circle: function (a) {
+ var b = a.attrs;
+ return bh(b.cx, b.cy, b.r)
+ }, ellipse: function (a) {
+ var b = a.attrs;
+ return bh(b.cx, b.cy, b.rx, b.ry)
+ }, rect: function (a) {
+ var b = a.attrs;
+ return bg(b.x, b.y, b.width, b.height, b.r)
+ }, image: function (a) {
+ var b = a.attrs;
+ return bg(b.x, b.y, b.width, b.height)
+ }, text: function (a) {
+ var b = a._getBBox();
+ return bg(b.x, b.y, b.width, b.height)
+ }}, bj = a.mapPath = function (a, b) {
+ if (!b)return a;
+ var c, d, e, f, g, h, i;
+ a = bR(a);
+ for (e = 0, g = a.length; e < g; e++) {
+ i = a[e];
+ for (f = 1, h = i.length; f < h; f += 2)c = b.x(i[f], i[f + 1]), d = b.y(i[f], i[f + 1]), i[f] = c, i[f + 1] = d
+ }
+ return a
+ };
+ a._g = h, a.type = h.win.SVGAngle || h.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML";
+ if (a.type == "VML") {
+ var bk = h.doc.createElement("div"), bl;
+ bk.innerHTML = '<v:shape adj="1"/>', bl = bk.firstChild, bl.style.behavior = "url(#default#VML)";
+ if (!bl || typeof bl.adj != "object")return a.type = p;
+ bk = null
+ }
+ a.svg = !(a.vml = a.type == "VML"), a._Paper = j, a.fn = k = j.prototype = a.prototype, a._id = 0, a._oid = 0, a.is = function (a, b) {
+ b = v.call(b);
+ if (b == "finite")return!M[g](+a);
+ if (b == "array")return a instanceof Array;
+ return b == "null" && a === null || b == typeof a && a !== null || b == "object" && a === Object(a) || b == "array" && Array.isArray && Array.isArray(a) || H.call(a).slice(8, -1).toLowerCase() == b
+ }, a.angle = function (b, c, d, e, f, g) {
+ if (f == null) {
+ var h = b - d, i = c - e;
+ if (!h && !i)return 0;
+ return(180 + w.atan2(-i, -h) * 180 / B + 360) % 360
+ }
+ return a.angle(b, c, f, g) - a.angle(d, e, f, g)
+ }, a.rad = function (a) {return a % 360 * B / 180}, a.deg = function (a) {return a * 180 / B % 360}, a.snapTo = function (b, c, d) {
+ d = a.is(d, "finite") ? d : 10;
+ if (a.is(b, E)) {
+ var e = b.length;
+ while (e--)if (z(b[e] - c) <= d)return b[e]
+ } else {
+ b = +b;
+ var f = c % b;
+ if (f < d)return c - f;
+ if (f > b - d)return c - f + b
+ }
+ return c
+ };
+ var bn = a.createUUID = function (a, b) {return function () {return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(a, b).toUpperCase()}}(/[xy]/g, function (a) {
+ var b = w.random() * 16 | 0, c = a == "x" ? b : b & 3 | 8;
+ return c.toString(16)
+ });
+ a.setWindow = function (b) {eve("raphael.setWindow", a, h.win, b), h.win = b, h.doc = h.win.document, a._engine.initWin && a._engine.initWin(h.win)};
+ var bo = function (b) {
+ if (a.vml) {
+ var c = /^\s+|\s+$/g, d;
+ try {
+ var e = new ActiveXObject("htmlfile");
+ e.write("<body>"), e.close(), d = e.body
+ } catch (f) {d = createPopup().document.body}
+ var g = d.createTextRange();
+ bo = bv(function (a) {
+ try {
+ d.style.color = r(a).replace(c, p);
+ var b = g.queryCommandValue("ForeColor");
+ b = (b & 255) << 16 | b & 65280 | (b & 16711680) >>> 16;
+ return"#" + ("000000" + b.toString(16)).slice(-6)
+ } catch (e) {return"none"}
+ })
+ } else {
+ var i = h.doc.createElement("i");
+ i.title = "Raphaël Colour Picker", i.style.display = "none", h.doc.body.appendChild(i), bo = bv(function (a) {
+ i.style.color = a;
+ return h.doc.defaultView.getComputedStyle(i, p).getPropertyValue("color")
+ })
+ }
+ return bo(b)
+ }, bp = function () {return"hsb(" + [this.h, this.s, this.b] + ")"}, bq = function () {return"hsl(" + [this.h, this.s, this.l] + ")"}, br = function () {return this.hex}, bs = function (b, c, d) {
+ c == null && a.is(b, "object") && "r"in b && "g"in b && "b"in b && (d = b.b, c = b.g, b = b.r);
+ if (c == null && a.is(b, D)) {
+ var e = a.getRGB(b);
+ b = e.r, c = e.g, d = e.b
+ }
+ if (b > 1 || c > 1 || d > 1)b /= 255, c /= 255, d /= 255;
+ return[b, c, d]
+ }, bt = function (b, c, d, e) {
+ b *= 255, c *= 255, d *= 255;
+ var f = {r: b, g: c, b: d, hex: a.rgb(b, c, d), toString: br};
+ a.is(e, "finite") && (f.opacity = e);
+ return f
+ };
+ a.color = function (b) {
+ var c;
+ a.is(b, "object") && "h"in b && "s"in b && "b"in b ? (c = a.hsb2rgb(b), b.r = c.r, b.g = c.g, b.b = c.b, b.hex = c.hex) : a.is(b, "object") && "h"in b && "s"in b && "l"in b ? (c = a.hsl2rgb(b), b.r = c.r, b.g = c.g, b.b = c.b, b.hex = c.hex) : (a.is(b, "string") && (b = a.getRGB(b)), a.is(b, "object") && "r"in b && "g"in b && "b"in b ? (c = a.rgb2hsl(b), b.h = c.h, b.s = c.s, b.l = c.l, c = a.rgb2hsb(b), b.v = c.b) : (b = {hex: "none"}, b.r = b.g = b.b = b.h = b.s = b.v = b.l = -1)), b.toString = br;
+ return b
+ }, a.hsb2rgb = function (a, b, c, d) {
+ this.is(a, "object") && "h"in a && "s"in a && "b"in a && (c = a.b, b = a.s, a = a.h, d = a.o), a *= 360;
+ var e, f, g, h, i;
+ a = a % 360 / 60, i = c * b, h = i * (1 - z(a % 2 - 1)), e = f = g = c - i, a = ~~a, e += [i, h, 0, 0, h, i][a], f += [h, i, i, h, 0, 0][a], g += [0, 0, h, i, i, h][a];
+ return bt(e, f, g, d)
+ }, a.hsl2rgb = function (a, b, c, d) {
+ this.is(a, "object") && "h"in a && "s"in a && "l"in a && (c = a.l, b = a.s, a = a.h);
+ if (a > 1 || b > 1 || c > 1)a /= 360, b /= 100, c /= 100;
+ a *= 360;
+ var e, f, g, h, i;
+ a = a % 360 / 60, i = 2 * b * (c < .5 ? c : 1 - c), h = i * (1 - z(a % 2 - 1)), e = f = g = c - i / 2, a = ~~a, e += [i, h, 0, 0, h, i][a], f += [h, i, i, h, 0, 0][a], g += [0, 0, h, i, i, h][a];
+ return bt(e, f, g, d)
+ }, a.rgb2hsb = function (a, b, c) {
+ c = bs(a, b, c), a = c[0], b = c[1], c = c[2];
+ var d, e, f, g;
+ f = x(a, b, c), g = f - y(a, b, c), d = g == 0 ? null : f == a ? (b - c) / g : f == b ? (c - a) / g + 2 : (a - b) / g + 4, d = (d + 360) % 6 * 60 / 360, e = g == 0 ? 0 : g / f;
+ return{h: d, s: e, b: f, toString: bp}
+ }, a.rgb2hsl = function (a, b, c) {
+ c = bs(a, b, c), a = c[0], b = c[1], c = c[2];
+ var d, e, f, g, h, i;
+ g = x(a, b, c), h = y(a, b, c), i = g - h, d = i == 0 ? null : g == a ? (b - c) / i : g == b ? (c - a) / i + 2 : (a - b) / i + 4, d = (d + 360) % 6 * 60 / 360, f = (g + h) / 2, e = i == 0 ? 0 : f < .5 ? i / (2 * f) : i / (2 - 2 * f);
+ return{h: d, s: e, l: f, toString: bq}
+ }, a._path2string = function () {return this.join(",").replace(Y, "$1")};
+ var bw = a._preload = function (a, b) {
+ var c = h.doc.createElement("img");
+ c.style.cssText = "position:absolute;left:-9999em;top:-9999em", c.onload = function () {b.call(this), this.onload = null, h.doc.body.removeChild(this)}, c.onerror = function () {h.doc.body.removeChild(this)}, h.doc.body.appendChild(c), c.src = a
+ };
+ a.getRGB = bv(function (b) {
+ if (!b || !!((b = r(b)).indexOf("-") + 1))return{r: -1, g: -1, b: -1, hex: "none", error: 1, toString: bx};
+ if (b == "none")return{r: -1, g: -1, b: -1, hex: "none", toString: bx};
+ !X[g](b.toLowerCase().substring(0, 2)) && b.charAt() != "#" && (b = bo(b));
+ var c, d, e, f, h, i, j, k = b.match(L);
+ if (k) {
+ k[2] && (f = R(k[2].substring(5), 16), e = R(k[2].substring(3, 5), 16), d = R(k[2].substring(1, 3), 16)), k[3] && (f = R((i = k[3].charAt(3)) + i, 16), e = R((i = k[3].charAt(2)) + i, 16), d = R((i = k[3].charAt(1)) + i, 16)), k[4] && (j = k[4][s](W), d = Q(j[0]), j[0].slice(-1) == "%" && (d *= 2.55), e = Q(j[1]), j[1].slice(-1) == "%" && (e *= 2.55), f = Q(j[2]), j[2].slice(-1) == "%" && (f *= 2.55), k[1].toLowerCase().slice(0, 4) == "rgba" && (h = Q(j[3])), j[3] && j[3].slice(-1) == "%" && (h /= 100));
+ if (k[5]) {
+ j = k[5][s](W), d = Q(j[0]), j[0].slice(-1) == "%" && (d *= 2.55), e = Q(j[1]), j[1].slice(-1) == "%" && (e *= 2.55), f = Q(j[2]), j[2].slice(-1) == "%" && (f *= 2.55), (j[0].slice(-3) == "deg" || j[0].slice(-1) == "°") && (d /= 360), k[1].toLowerCase().slice(0, 4) == "hsba" && (h = Q(j[3])), j[3] && j[3].slice(-1) == "%" && (h /= 100);
+ return a.hsb2rgb(d, e, f, h)
+ }
+ if (k[6]) {
+ j = k[6][s](W), d = Q(j[0]), j[0].slice(-1) == "%" && (d *= 2.55), e = Q(j[1]), j[1].slice(-1) == "%" && (e *= 2.55), f = Q(j[2]), j[2].slice(-1) == "%" && (f *= 2.55), (j[0].slice(-3) == "deg" || j[0].slice(-1) == "°") && (d /= 360), k[1].toLowerCase().slice(0, 4) == "hsla" && (h = Q(j[3])), j[3] && j[3].slice(-1) == "%" && (h /= 100);
+ return a.hsl2rgb(d, e, f, h)
+ }
+ k = {r: d, g: e, b: f, toString: bx}, k.hex = "#" + (16777216 | f | e << 8 | d << 16).toString(16).slice(1), a.is(h, "finite") && (k.opacity = h);
+ return k
+ }
+ return{r: -1, g: -1, b: -1, hex: "none", error: 1, toString: bx}
+ }, a), a.hsb = bv(function (b, c, d) {return a.hsb2rgb(b, c, d).hex}), a.hsl = bv(function (b, c, d) {return a.hsl2rgb(b, c, d).hex}), a.rgb = bv(function (a, b, c) {return"#" + (16777216 | c | b << 8 | a << 16).toString(16).slice(1)}), a.getColor = function (a) {
+ var b = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: a || .75}, c = this.hsb2rgb(b.h, b.s, b.b);
+ b.h += .075, b.h > 1 && (b.h = 0, b.s -= .2, b.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: b.b}));
+ return c.hex
+ }, a.getColor.reset = function () {delete this.start}, a.parsePathString = function (b) {
+ if (!b)return null;
+ var c = bz(b);
+ if (c.arr)return bJ(c.arr);
+ var d = {a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0}, e = [];
+ a.is(b, E) && a.is(b[0], E) && (e = bJ(b)), e.length || r(b).replace(Z, function (a, b, c) {
+ var f = [], g = b.toLowerCase();
+ c.replace(_, function (a, b) {b && f.push(+b)}), g == "m" && f.length > 2 && (e.push([b][n](f.splice(0, 2))), g = "l", b = b == "m" ? "l" : "L");
+ if (g == "r")e.push([b][n](f)); else while (f.length >= d[g]) {
+ e.push([b][n](f.splice(0, d[g])));
+ if (!d[g])break
+ }
+ }), e.toString = a._path2string, c.arr = bJ(e);
+ return e
+ }, a.parseTransformString = bv(function (b) {
+ if (!b)return null;
+ var c = {r: 3, s: 4, t: 2, m: 6}, d = [];
+ a.is(b, E) && a.is(b[0], E) && (d = bJ(b)), d.length || r(b).replace($, function (a, b, c) {
+ var e = [], f = v.call(b);
+ c.replace(_, function (a, b) {b && e.push(+b)}), d.push([b][n](e))
+ }), d.toString = a._path2string;
+ return d
+ });
+ var bz = function (a) {
+ var b = bz.ps = bz.ps || {};
+ b[a] ? b[a].sleep = 100 : b[a] = {sleep: 100}, setTimeout(function () {for (var c in b)b[g](c) && c != a && (b[c].sleep--, !b[c].sleep && delete b[c])});
+ return b[a]
+ };
+ a.findDotsAtSegment = function (a, b, c, d, e, f, g, h, i) {
+ var j = 1 - i, k = A(j, 3), l = A(j, 2), m = i * i, n = m * i, o = k * a + l * 3 * i * c + j * 3 * i * i * e + n * g, p = k * b + l * 3 * i * d + j * 3 * i * i * f + n * h, q = a + 2 * i * (c - a) + m * (e - 2 * c + a), r = b + 2 * i * (d - b) + m * (f - 2 * d + b), s = c + 2 * i * (e - c) + m * (g - 2 * e + c), t = d + 2 * i * (f - d) + m * (h - 2 * f + d), u = j * a + i * c, v = j * b + i * d, x = j * e + i * g, y = j * f + i * h, z = 90 - w.atan2(q - s, r - t) * 180 / B;
+ (q > s || r < t) && (z += 180);
+ return{x: o, y: p, m: {x: q, y: r}, n: {x: s, y: t}, start: {x: u, y: v}, end: {x: x, y: y}, alpha: z}
+ }, a.bezierBBox = function (b, c, d, e, f, g, h, i) {
+ a.is(b, "array") || (b = [b, c, d, e, f, g, h, i]);
+ var j = bQ.apply(null, b);
+ return{x: j.min.x, y: j.min.y, x2: j.max.x, y2: j.max.y, width: j.max.x - j.min.x, height: j.max.y - j.min.y}
+ }, a.isPointInsideBBox = function (a, b, c) {return b >= a.x && b <= a.x2 && c >= a.y && c <= a.y2}, a.isBBoxIntersect = function (b, c) {
+ var d = a.isPointInsideBBox;
+ return d(c, b.x, b.y) || d(c, b.x2, b.y) || d(c, b.x, b.y2) || d(c, b.x2, b.y2) || d(b, c.x, c.y) || d(b, c.x2, c.y) || d(b, c.x, c.y2) || d(b, c.x2, c.y2) || (b.x < c.x2 && b.x > c.x || c.x < b.x2 && c.x > b.x) && (b.y < c.y2 && b.y > c.y || c.y < b.y2 && c.y > b.y)
+ }, a.pathIntersection = function (a, b) {return bH(a, b)}, a.pathIntersectionNumber = function (a, b) {return bH(a, b, 1)}, a.isPointInsidePath = function (b, c, d) {
+ var e = a.pathBBox(b);
+ return a.isPointInsideBBox(e, c, d) && bH(b, [
+ ["M", c, d],
+ ["H", e.x2 + 10]
+ ], 1) % 2 == 1
+ }, a._removedFactory = function (a) {return function () {eve("raphael.log", null, "Raphaël: you are calling to method “" + a + "” of removed object", a)}};
+ var bI = a.pathBBox = function (a) {
+ var b = bz(a);
+ if (b.bbox)return b.bbox;
+ if (!a)return{x: 0, y: 0, width: 0, height: 0, x2: 0, y2: 0};
+ a = bR(a);
+ var c = 0, d = 0, e = [], f = [], g;
+ for (var h = 0, i = a.length; h < i; h++) {
+ g = a[h];
+ if (g[0] == "M")c = g[1], d = g[2], e.push(c), f.push(d); else {
+ var j = bQ(c, d, g[1], g[2], g[3], g[4], g[5], g[6]);
+ e = e[n](j.min.x, j.max.x), f = f[n](j.min.y, j.max.y), c = g[5], d = g[6]
+ }
+ }
+ var k = y[m](0, e), l = y[m](0, f), o = x[m](0, e), p = x[m](0, f), q = {x: k, y: l, x2: o, y2: p, width: o - k, height: p - l};
+ b.bbox = bm(q);
+ return q
+ }, bJ = function (b) {
+ var c = bm(b);
+ c.toString = a._path2string;
+ return c
+ }, bK = a._pathToRelative = function (b) {
+ var c = bz(b);
+ if (c.rel)return bJ(c.rel);
+ if (!a.is(b, E) || !a.is(b && b[0], E))b = a.parsePathString(b);
+ var d = [], e = 0, f = 0, g = 0, h = 0, i = 0;
+ b[0][0] == "M" && (e = b[0][1], f = b[0][2], g = e, h = f, i++, d.push(["M", e, f]));
+ for (var j = i, k = b.length; j < k; j++) {
+ var l = d[j] = [], m = b[j];
+ if (m[0] != v.call(m[0])) {
+ l[0] = v.call(m[0]);
+ switch (l[0]) {
+ case"a":
+ l[1] = m[1], l[2] = m[2], l[3] = m[3], l[4] = m[4], l[5] = m[5], l[6] = +(m[6] - e).toFixed(3), l[7] = +(m[7] - f).toFixed(3);
+ break;
+ case"v":
+ l[1] = +(m[1] - f).toFixed(3);
+ break;
+ case"m":
+ g = m[1], h = m[2];
+ default:
+ for (var n = 1, o = m.length; n < o; n++)l[n] = +(m[n] - (n % 2 ? e : f)).toFixed(3)
+ }
+ } else {
+ l = d[j] = [], m[0] == "m" && (g = m[1] + e, h = m[2] + f);
+ for (var p = 0, q = m.length; p < q; p++)d[j][p] = m[p]
+ }
+ var r = d[j].length;
+ switch (d[j][0]) {
+ case"z":
+ e = g, f = h;
+ break;
+ case"h":
+ e += +d[j][r - 1];
+ break;
+ case"v":
+ f += +d[j][r - 1];
+ break;
+ default:
+ e += +d[j][r - 2], f += +d[j][r - 1]
+ }
+ }
+ d.toString = a._path2string, c.rel = bJ(d);
+ return d
+ }, bL = a._pathToAbsolute = function (b) {
+ var c = bz(b);
+ if (c.abs)return bJ(c.abs);
+ if (!a.is(b, E) || !a.is(b && b[0], E))b = a.parsePathString(b);
+ if (!b || !b.length)return[
+ ["M", 0, 0]
+ ];
+ var d = [], e = 0, f = 0, g = 0, h = 0, i = 0;
+ b[0][0] == "M" && (e = +b[0][1], f = +b[0][2], g = e, h = f, i++, d[0] = ["M", e, f]);
+ var j = b.length == 3 && b[0][0] == "M" && b[1][0].toUpperCase() == "R" && b[2][0].toUpperCase() == "Z";
+ for (var k, l, m = i, o = b.length; m < o; m++) {
+ d.push(k = []), l = b[m];
+ if (l[0] != S.call(l[0])) {
+ k[0] = S.call(l[0]);
+ switch (k[0]) {
+ case"A":
+ k[1] = l[1], k[2] = l[2], k[3] = l[3], k[4] = l[4], k[5] = l[5], k[6] = +(l[6] + e), k[7] = +(l[7] + f);
+ break;
+ case"V":
+ k[1] = +l[1] + f;
+ break;
+ case"H":
+ k[1] = +l[1] + e;
+ break;
+ case"R":
+ var p = [e, f][n](l.slice(1));
+ for (var q = 2, r = p.length; q < r; q++)p[q] = +p[q] + e, p[++q] = +p[q] + f;
+ d.pop(), d = d[n](by(p, j));
+ break;
+ case"M":
+ g = +l[1] + e, h = +l[2] + f;
+ default:
+ for (q = 1, r = l.length; q < r; q++)k[q] = +l[q] + (q % 2 ? e : f)
+ }
+ } else if (l[0] == "R")p = [e, f][n](l.slice(1)), d.pop(), d = d[n](by(p, j)), k = ["R"][n](l.slice(-2)); else for (var s = 0, t = l.length; s < t; s++)k[s] = l[s];
+ switch (k[0]) {
+ case"Z":
+ e = g, f = h;
+ break;
+ case"H":
+ e = k[1];
+ break;
+ case"V":
+ f = k[1];
+ break;
+ case"M":
+ g = k[k.length - 2], h = k[k.length - 1];
+ default:
+ e = k[k.length - 2], f = k[k.length - 1]
+ }
+ }
+ d.toString = a._path2string, c.abs = bJ(d);
+ return d
+ }, bM = function (a, b, c, d) {return[a, b, c, d, c, d]}, bN = function (a, b, c, d, e, f) {
+ var g = 1 / 3, h = 2 / 3;
+ return[g * a + h * c, g * b + h * d, g * e + h * c, g * f + h * d, e, f]
+ }, bO = function (a, b, c, d, e, f, g, h, i, j) {
+ var k = B * 120 / 180, l = B / 180 * (+e || 0), m = [], o, p = bv(function (a, b, c) {
+ var d = a * w.cos(c) - b * w.sin(c), e = a * w.sin(c) + b * w.cos(c);
+ return{x: d, y: e}
+ });
+ if (!j) {
+ o = p(a, b, -l), a = o.x, b = o.y, o = p(h, i, -l), h = o.x, i = o.y;
+ var q = w.cos(B / 180 * e), r = w.sin(B / 180 * e), t = (a - h) / 2, u = (b - i) / 2, v = t * t / (c * c) + u * u / (d * d);
+ v > 1 && (v = w.sqrt(v), c = v * c, d = v * d);
+ var x = c * c, y = d * d, A = (f == g ? -1 : 1) * w.sqrt(z((x * y - x * u * u - y * t * t) / (x * u * u + y * t * t))), C = A * c * u / d + (a + h) / 2, D = A * -d * t / c + (b + i) / 2, E = w.asin(((b - D) / d).toFixed(9)), F = w.asin(((i - D) / d).toFixed(9));
+ E = a < C ? B - E : E, F = h < C ? B - F : F, E < 0 && (E = B * 2 + E), F < 0 && (F = B * 2 + F), g && E > F && (E = E - B * 2), !g && F > E && (F = F - B * 2)
+ } else E = j[0], F = j[1], C = j[2], D = j[3];
+ var G = F - E;
+ if (z(G) > k) {
+ var H = F, I = h, J = i;
+ F = E + k * (g && F > E ? 1 : -1), h = C + c * w.cos(F), i = D + d * w.sin(F), m = bO(h, i, c, d, e, 0, g, I, J, [F, H, C, D])
+ }
+ G = F - E;
+ var K = w.cos(E), L = w.sin(E), M = w.cos(F), N = w.sin(F), O = w.tan(G / 4), P = 4 / 3 * c * O, Q = 4 / 3 * d * O, R = [a, b], S = [a + P * L, b - Q * K], T = [h + P * N, i - Q * M], U = [h, i];
+ S[0] = 2 * R[0] - S[0], S[1] = 2 * R[1] - S[1];
+ if (j)return[S, T, U][n](m);
+ m = [S, T, U][n](m).join()[s](",");
+ var V = [];
+ for (var W = 0, X = m.length; W < X; W++)V[W] = W % 2 ? p(m[W - 1], m[W], l).y : p(m[W], m[W + 1], l).x;
+ return V
+ }, bP = function (a, b, c, d, e, f, g, h, i) {
+ var j = 1 - i;
+ return{x: A(j, 3) * a + A(j, 2) * 3 * i * c + j * 3 * i * i * e + A(i, 3) * g, y: A(j, 3) * b + A(j, 2) * 3 * i * d + j * 3 * i * i * f + A(i, 3) * h}
+ }, bQ = bv(function (a, b, c, d, e, f, g, h) {
+ var i = e - 2 * c + a - (g - 2 * e + c), j = 2 * (c - a) - 2 * (e - c), k = a - c, l = (-j + w.sqrt(j * j - 4 * i * k)) / 2 / i, n = (-j - w.sqrt(j * j - 4 * i * k)) / 2 / i, o = [b, h], p = [a, g], q;
+ z(l) > "1e12" && (l = .5), z(n) > "1e12" && (n = .5), l > 0 && l < 1 && (q = bP(a, b, c, d, e, f, g, h, l), p.push(q.x), o.push(q.y)), n > 0 && n < 1 && (q = bP(a, b, c, d, e, f, g, h, n), p.push(q.x), o.push(q.y)), i = f - 2 * d + b - (h - 2 * f + d), j = 2 * (d - b) - 2 * (f - d), k = b - d, l = (-j + w.sqrt(j * j - 4 * i * k)) / 2 / i, n = (-j - w.sqrt(j * j - 4 * i * k)) / 2 / i, z(l) > "1e12" && (l = .5), z(n) > "1e12" && (n = .5), l > 0 && l < 1 && (q = bP(a, b, c, d, e, f, g, h, l), p.push(q.x), o.push(q.y)), n > 0 && n < 1 && (q = bP(a, b, c, d, e, f, g, h, n), p.push(q.x), o.push(q.y));
+ return{min: {x: y[m](0, p), y: y[m](0, o)}, max: {x: x[m](0, p), y: x[m](0, o)}}
+ }), bR = a._path2curve = bv(function (a, b) {
+ var c = !b && bz(a);
+ if (!b && c.curve)return bJ(c.curve);
+ var d = bL(a), e = b && bL(b), f = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, g = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, h = function (a, b) {
+ var c, d;
+ if (!a)return["C", b.x, b.y, b.x, b.y, b.x, b.y];
+ !(a[0]in{T: 1, Q: 1}) && (b.qx = b.qy = null);
+ switch (a[0]) {
+ case"M":
+ b.X = a[1], b.Y = a[2];
+ break;
+ case"A":
+ a = ["C"][n](bO[m](0, [b.x, b.y][n](a.slice(1))));
+ break;
+ case"S":
+ c = b.x + (b.x - (b.bx || b.x)), d = b.y + (b.y - (b.by || b.y)), a = ["C", c, d][n](a.slice(1));
+ break;
+ case"T":
+ b.qx = b.x + (b.x - (b.qx || b.x)), b.qy = b.y + (b.y - (b.qy || b.y)), a = ["C"][n](bN(b.x, b.y, b.qx, b.qy, a[1], a[2]));
+ break;
+ case"Q":
+ b.qx = a[1], b.qy = a[2], a = ["C"][n](bN(b.x, b.y, a[1], a[2], a[3], a[4]));
+ break;
+ case"L":
+ a = ["C"][n](bM(b.x, b.y, a[1], a[2]));
+ break;
+ case"H":
+ a = ["C"][n](bM(b.x, b.y, a[1], b.y));
+ break;
+ case"V":
+ a = ["C"][n](bM(b.x, b.y, b.x, a[1]));
+ break;
+ case"Z":
+ a = ["C"][n](bM(b.x, b.y, b.X, b.Y))
+ }
+ return a
+ }, i = function (a, b) {
+ if (a[b].length > 7) {
+ a[b].shift();
+ var c = a[b];
+ while (c.length)a.splice(b++, 0, ["C"][n](c.splice(0, 6)));
+ a.splice(b, 1), l = x(d.length, e && e.length || 0)
+ }
+ }, j = function (a, b, c, f, g) {a && b && a[g][0] == "M" && b[g][0] != "M" && (b.splice(g, 0, ["M", f.x, f.y]), c.bx = 0, c.by = 0, c.x = a[g][1], c.y = a[g][2], l = x(d.length, e && e.length || 0))};
+ for (var k = 0, l = x(d.length, e && e.length || 0); k < l; k++) {
+ d[k] = h(d[k], f), i(d, k), e && (e[k] = h(e[k], g)), e && i(e, k), j(d, e, f, g, k), j(e, d, g, f, k);
+ var o = d[k], p = e && e[k], q = o.length, r = e && p.length;
+ f.x = o[q - 2], f.y = o[q - 1], f.bx = Q(o[q - 4]) || f.x, f.by = Q(o[q - 3]) || f.y, g.bx = e && (Q(p[r - 4]) || g.x), g.by = e && (Q(p[r - 3]) || g.y), g.x = e && p[r - 2], g.y = e && p[r - 1]
+ }
+ e || (c.curve = bJ(d));
+ return e ? [d, e] : d
+ }, null, bJ), bS = a._parseDots = bv(function (b) {
+ var c = [];
+ for (var d = 0, e = b.length; d < e; d++) {
+ var f = {}, g = b[d].match(/^([^:]*):?([\d\.]*)/);
+ f.color = a.getRGB(g[1]);
+ if (f.color.error)return null;
+ f.color = f.color.hex, g[2] && (f.offset = g[2] + "%"), c.push(f)
+ }
+ for (d = 1, e = c.length - 1; d < e; d++)if (!c[d].offset) {
+ var h = Q(c[d - 1].offset || 0), i = 0;
+ for (var j = d + 1; j < e; j++)if (c[j].offset) {
+ i = c[j].offset;
+ break
+ }
+ i || (i = 100, j = e), i = Q(i);
+ var k = (i - h) / (j - d + 1);
+ for (; d < j; d++)h += k, c[d].offset = h + "%"
+ }
+ return c
+ }), bT = a._tear = function (a, b) {a == b.top && (b.top = a.prev), a == b.bottom && (b.bottom = a.next), a.next && (a.next.prev = a.prev), a.prev && (a.prev.next = a.next)}, bU = a._tofront = function (a, b) {b.top !== a && (bT(a, b), a.next = null, a.prev = b.top, b.top.next = a, b.top = a)}, bV = a._toback = function (a, b) {b.bottom !== a && (bT(a, b), a.next = b.bottom, a.prev = null, b.bottom.prev = a, b.bottom = a)}, bW = a._insertafter = function (a, b, c) {bT(a, c), b == c.top && (c.top = a), b.next && (b.next.prev = a), a.next = b.next, a.prev = b, b.next = a}, bX = a._insertbefore = function (a, b, c) {bT(a, c), b == c.bottom && (c.bottom = a), b.prev && (b.prev.next = a), a.prev = b.prev, b.prev = a, a.next = b}, bY = a.toMatrix = function (a, b) {
+ var c = bI(a), d = {_: {transform: p}, getBBox: function () {return c}};
+ b$(d, b);
+ return d.matrix
+ }, bZ = a.transformPath = function (a, b) {return bj(a, bY(a, b))}, b$ = a._extractTransform = function (b, c) {
+ if (c == null)return b._.transform;
+ c = r(c).replace(/\.{3}|\u2026/g, b._.transform || p);
+ var d = a.parseTransformString(c), e = 0, f = 0, g = 0, h = 1, i = 1, j = b._, k = new cb;
+ j.transform = d || [];
+ if (d)for (var l = 0, m = d.length; l < m; l++) {
+ var n = d[l], o = n.length, q = r(n[0]).toLowerCase(), s = n[0] != q, t = s ? k.invert() : 0, u, v, w, x, y;
+ q == "t" && o == 3 ? s ? (u = t.x(0, 0), v = t.y(0, 0), w = t.x(n[1], n[2]), x = t.y(n[1], n[2]), k.translate(w - u, x - v)) : k.translate(n[1], n[2]) : q == "r" ? o == 2 ? (y = y || b.getBBox(1), k.rotate(n[1], y.x + y.width / 2, y.y + y.height / 2), e += n[1]) : o == 4 && (s ? (w = t.x(n[2], n[3]), x = t.y(n[2], n[3]), k.rotate(n[1], w, x)) : k.rotate(n[1], n[2], n[3]), e += n[1]) : q == "s" ? o == 2 || o == 3 ? (y = y || b.getBBox(1), k.scale(n[1], n[o - 1], y.x + y.width / 2, y.y + y.height / 2), h *= n[1], i *= n[o - 1]) : o == 5 && (s ? (w = t.x(n[3], n[4]), x = t.y(n[3], n[4]), k.scale(n[1], n[2], w, x)) : k.scale(n[1], n[2], n[3], n[4]), h *= n[1], i *= n[2]) : q == "m" && o == 7 && k.add(n[1], n[2], n[3], n[4], n[5], n[6]), j.dirtyT = 1, b.matrix = k
+ }
+ b.matrix = k, j.sx = h, j.sy = i, j.deg = e, j.dx = f = k.e, j.dy = g = k.f, h == 1 && i == 1 && !e && j.bbox ? (j.bbox.x += +f, j.bbox.y += +g) : j.dirtyT = 1
+ }, b_ = function (a) {
+ var b = a[0];
+ switch (b.toLowerCase()) {
+ case"t":
+ return[b, 0, 0];
+ case"m":
+ return[b, 1, 0, 0, 1, 0, 0];
+ case"r":
+ return a.length == 4 ? [b, 0, a[2], a[3]] : [b, 0];
+ case"s":
+ return a.length == 5 ? [b, 1, 1, a[3], a[4]] : a.length == 3 ? [b, 1, 1] : [b, 1]
+ }
+ }, ca = a._equaliseTransform = function (b, c) {
+ c = r(c).replace(/\.{3}|\u2026/g, b), b = a.parseTransformString(b) || [], c = a.parseTransformString(c) || [];
+ var d = x(b.length, c.length), e = [], f = [], g = 0, h, i, j, k;
+ for (; g < d; g++) {
+ j = b[g] || b_(c[g]), k = c[g] || b_(j);
+ if (j[0] != k[0] || j[0].toLowerCase() == "r" && (j[2] != k[2] || j[3] != k[3]) || j[0].toLowerCase() == "s" && (j[3] != k[3] || j[4] != k[4]))return;
+ e[g] = [], f[g] = [];
+ for (h = 0, i = x(j.length, k.length); h < i; h++)h in j && (e[g][h] = j[h]), h in k && (f[g][h] = k[h])
+ }
+ return{from: e, to: f}
+ };
+ a._getContainer = function (b, c, d, e) {
+ var f;
+ f = e == null && !a.is(b, "object") ? h.doc.getElementById(b) : b;
+ if (f != null) {
+ if (f.tagName)return c == null ? {container: f, width: f.style.pixelWidth || f.offsetWidth, height: f.style.pixelHeight || f.offsetHeight} : {container: f, width: c, height: d};
+ return{container: 1, x: b, y: c, width: d, height: e}
+ }
+ }, a.pathToRelative = bK, a._engine = {}, a.path2curve = bR, a.matrix = function (a, b, c, d, e, f) {return new cb(a, b, c, d, e, f)}, function (b) {
+ function d(a) {
+ var b = w.sqrt(c(a));
+ a[0] && (a[0] /= b), a[1] && (a[1] /= b)
+ }
+
+ function c(a) {return a[0] * a[0] + a[1] * a[1]}
+
+ b.add = function (a, b, c, d, e, f) {
+ var g = [
+ [],
+ [],
+ []
+ ], h = [
+ [this.a, this.c, this.e],
+ [this.b, this.d, this.f],
+ [0, 0, 1]
+ ], i = [
+ [a, c, e],
+ [b, d, f],
+ [0, 0, 1]
+ ], j, k, l, m;
+ a && a instanceof cb && (i = [
+ [a.a, a.c, a.e],
+ [a.b, a.d, a.f],
+ [0, 0, 1]
+ ]);
+ for (j = 0; j < 3; j++)for (k = 0; k < 3; k++) {
+ m = 0;
+ for (l = 0; l < 3; l++)m += h[j][l] * i[l][k];
+ g[j][k] = m
+ }
+ this.a = g[0][0], this.b = g[1][0], this.c = g[0][1], this.d = g[1][1], this.e = g[0][2], this.f = g[1][2]
+ }, b.invert = function () {
+ var a = this, b = a.a * a.d - a.b * a.c;
+ return new cb(a.d / b, -a.b / b, -a.c / b, a.a / b, (a.c * a.f - a.d * a.e) / b, (a.b * a.e - a.a * a.f) / b)
+ }, b.clone = function () {return new cb(this.a, this.b, this.c, this.d, this.e, this.f)}, b.translate = function (a, b) {this.add(1, 0, 0, 1, a, b)}, b.scale = function (a, b, c, d) {b == null && (b = a), (c || d) && this.add(1, 0, 0, 1, c, d), this.add(a, 0, 0, b, 0, 0), (c || d) && this.add(1, 0, 0, 1, -c, -d)}, b.rotate = function (b, c, d) {
+ b = a.rad(b), c = c || 0, d = d || 0;
+ var e = +w.cos(b).toFixed(9), f = +w.sin(b).toFixed(9);
+ this.add(e, f, -f, e, c, d), this.add(1, 0, 0, 1, -c, -d)
+ }, b.x = function (a, b) {return a * this.a + b * this.c + this.e}, b.y = function (a, b) {return a * this.b + b * this.d + this.f}, b.get = function (a) {return+this[r.fromCharCode(97 + a)].toFixed(4)}, b.toString = function () {return a.svg ? "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" : [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join()}, b.toFilter = function () {return"progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) + ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) + ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')"}, b.offset = function () {return[this.e.toFixed(4), this.f.toFixed(4)]}, b.split = function () {
+ var b = {};
+ b.dx = this.e, b.dy = this.f;
+ var e = [
+ [this.a, this.c],
+ [this.b, this.d]
+ ];
+ b.scalex = w.sqrt(c(e[0])), d(e[0]), b.shear = e[0][0] * e[1][0] + e[0][1] * e[1][1], e[1] = [e[1][0] - e[0][0] * b.shear, e[1][1] - e[0][1] * b.shear], b.scaley = w.sqrt(c(e[1])), d(e[1]), b.shear /= b.scaley;
+ var f = -e[0][1], g = e[1][1];
+ g < 0 ? (b.rotate = a.deg(w.acos(g)), f < 0 && (b.rotate = 360 - b.rotate)) : b.rotate = a.deg(w.asin(f)), b.isSimple = !+b.shear.toFixed(9) && (b.scalex.toFixed(9) == b.scaley.toFixed(9) || !b.rotate), b.isSuperSimple = !+b.shear.toFixed(9) && b.scalex.toFixed(9) == b.scaley.toFixed(9) && !b.rotate, b.noRotation = !+b.shear.toFixed(9) && !b.rotate;
+ return b
+ }, b.toTransformString = function (a) {
+ var b = a || this[s]();
+ if (b.isSimple) {
+ b.scalex = +b.scalex.toFixed(4), b.scaley = +b.scaley.toFixed(4), b.rotate = +b.rotate.toFixed(4);
+ return(b.dx || b.dy ? "t" + [b.dx, b.dy] : p) + (b.scalex != 1 || b.scaley != 1 ? "s" + [b.scalex, b.scaley, 0, 0] : p) + (b.rotate ? "r" + [b.rotate, 0, 0] : p)
+ }
+ return"m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)]
+ }
+ }(cb.prototype);
+ var cc = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/);
+ navigator.vendor == "Apple Computer, Inc." && (cc && cc[1] < 4 || navigator.platform.slice(0, 2) == "iP") || navigator.vendor == "Google Inc." && cc && cc[1] < 8 ? k.safari = function () {
+ var a = this.rect(-99, -99, this.width + 99, this.height + 99).attr({stroke: "none"});
+ setTimeout(function () {a.remove()})
+ } : k.safari = be;
+ var cd = function () {this.returnValue = !1}, ce = function () {return this.originalEvent.preventDefault()}, cf = function () {this.cancelBubble = !0}, cg = function () {return this.originalEvent.stopPropagation()}, ch = function () {
+ if (h.doc.addEventListener)return function (a, b, c, d) {
+ var e = o && u[b] ? u[b] : b, f = function (e) {
+ var f = h.doc.documentElement.scrollTop || h.doc.body.scrollTop, i = h.doc.documentElement.scrollLeft || h.doc.body.scrollLeft, j = e.clientX + i, k = e.clientY + f;
+ if (o && u[g](b))for (var l = 0, m = e.targetTouches && e.targetTouches.length; l < m; l++)if (e.targetTouches[l].target == a) {
+ var n = e;
+ e = e.targetTouches[l], e.originalEvent = n, e.preventDefault = ce, e.stopPropagation = cg;
+ break
+ }
+ return c.call(d, e, j, k)
+ };
+ a.addEventListener(e, f, !1);
+ return function () {
+ a.removeEventListener(e, f, !1);
+ return!0
+ }
+ };
+ if (h.doc.attachEvent)return function (a, b, c, d) {
+ var e = function (a) {
+ a = a || h.win.event;
+ var b = h.doc.documentElement.scrollTop || h.doc.body.scrollTop, e = h.doc.documentElement.scrollLeft || h.doc.body.scrollLeft, f = a.clientX + e, g = a.clientY + b;
+ a.preventDefault = a.preventDefault || cd, a.stopPropagation = a.stopPropagation || cf;
+ return c.call(d, a, f, g)
+ };
+ a.attachEvent("on" + b, e);
+ var f = function () {
+ a.detachEvent("on" + b, e);
+ return!0
+ };
+ return f
+ }
+ }(), ci = [], cj = function (a) {
+ var b = a.clientX, c = a.clientY, d = h.doc.documentElement.scrollTop || h.doc.body.scrollTop, e = h.doc.documentElement.scrollLeft || h.doc.body.scrollLeft, f, g = ci.length;
+ while (g--) {
+ f = ci[g];
+ if (o) {
+ var i = a.touches.length, j;
+ while (i--) {
+ j = a.touches[i];
+ if (j.identifier == f.el._drag.id) {
+ b = j.clientX, c = j.clientY, (a.originalEvent ? a.originalEvent : a).preventDefault();
+ break
+ }
+ }
+ } else a.preventDefault();
+ var k = f.el.node, l, m = k.nextSibling, n = k.parentNode, p = k.style.display;
+ h.win.opera && n.removeChild(k), k.style.display = "none", l = f.el.paper.getElementByPoint(b, c), k.style.display = p, h.win.opera && (m ? n.insertBefore(k, m) : n.appendChild(k)), l && eve("raphael.drag.over." + f.el.id, f.el, l), b += e, c += d, eve("raphael.drag.move." + f.el.id, f.move_scope || f.el, b - f.el._drag.x, c - f.el._drag.y, b, c, a)
+ }
+ }, ck = function (b) {
+ a.unmousemove(cj).unmouseup(ck);
+ var c = ci.length, d;
+ while (c--)d = ci[c], d.el._drag = {}, eve("raphael.drag.end." + d.el.id, d.end_scope || d.start_scope || d.move_scope || d.el, b);
+ ci = []
+ }, cl = a.el = {};
+ for (var cm = t.length; cm--;)(function (b) {
+ a[b] = cl[b] = function (c, d) {
+ a.is(c, "function") && (this.events = this.events || [], this.events.push({name: b, f: c, unbind: ch(this.shape || this.node || h.doc, b, c, d || this)}));
+ return this
+ }, a["un" + b] = cl["un" + b] = function (a) {
+ var c = this.events || [], d = c.length;
+ while (d--)if (c[d].name == b && c[d].f == a) {
+ c[d].unbind(), c.splice(d, 1), !c.length && delete this.events;
+ return this
+ }
+ return this
+ }
+ })(t[cm]);
+ cl.data = function (b, c) {
+ var d = bb[this.id] = bb[this.id] || {};
+ if (arguments.length == 1) {
+ if (a.is(b, "object")) {
+ for (var e in b)b[g](e) && this.data(e, b[e]);
+ return this
+ }
+ eve("raphael.data.get." + this.id, this, d[b], b);
+ return d[b]
+ }
+ d[b] = c, eve("raphael.data.set." + this.id, this, c, b);
+ return this
+ }, cl.removeData = function (a) {
+ a == null ? bb[this.id] = {} : bb[this.id] && delete bb[this.id][a];
+ return this
+ }, cl.hover = function (a, b, c, d) {return this.mouseover(a, c).mouseout(b, d || c)}, cl.unhover = function (a, b) {return this.unmouseover(a).unmouseout(b)};
+ var cn = [];
+ cl.drag = function (b, c, d, e, f, g) {
+ function i(i) {
+ (i.originalEvent || i).preventDefault();
+ var j = h.doc.documentElement.scrollTop || h.doc.body.scrollTop, k = h.doc.documentElement.scrollLeft || h.doc.body.scrollLeft;
+ this._drag.x = i.clientX + k, this._drag.y = i.clientY + j, this._drag.id = i.identifier, !ci.length && a.mousemove(cj).mouseup(ck), ci.push({el: this, move_scope: e, start_scope: f, end_scope: g}), c && eve.on("raphael.drag.start." + this.id, c), b && eve.on("raphael.drag.move." + this.id, b), d && eve.on("raphael.drag.end." + this.id, d), eve("raphael.drag.start." + this.id, f || e || this, i.clientX + k, i.clientY + j, i)
+ }
+
+ this._drag = {}, cn.push({el: this, start: i}), this.mousedown(i);
+ return this
+ }, cl.onDragOver = function (a) {a ? eve.on("raphael.drag.over." + this.id, a) : eve.unbind("raphael.drag.over." + this.id)}, cl.undrag = function () {
+ var b = cn.length;
+ while (b--)cn[b].el == this && (this.unmousedown(cn[b].start), cn.splice(b, 1), eve.unbind("raphael.drag.*." + this.id));
+ !cn.length && a.unmousemove(cj).unmouseup(ck)
+ }, k.circle = function (b, c, d) {
+ var e = a._engine.circle(this, b || 0, c || 0, d || 0);
+ this.__set__ && this.__set__.push(e);
+ return e
+ }, k.rect = function (b, c, d, e, f) {
+ var g = a._engine.rect(this, b || 0, c || 0, d || 0, e || 0, f || 0);
+ this.__set__ && this.__set__.push(g);
+ return g
+ }, k.ellipse = function (b, c, d, e) {
+ var f = a._engine.ellipse(this, b || 0, c || 0, d || 0, e || 0);
+ this.__set__ && this.__set__.push(f);
+ return f
+ }, k.path = function (b) {
+ b && !a.is(b, D) && !a.is(b[0], E) && (b += p);
+ var c = a._engine.path(a.format[m](a, arguments), this);
+ this.__set__ && this.__set__.push(c);
+ return c
+ }, k.image = function (b, c, d, e, f) {
+ var g = a._engine.image(this, b || "about:blank", c || 0, d || 0, e || 0, f || 0);
+ this.__set__ && this.__set__.push(g);
+ return g
+ }, k.text = function (b, c, d) {
+ var e = a._engine.text(this, b || 0, c || 0, r(d));
+ this.__set__ && this.__set__.push(e);
+ return e
+ }, k.set = function (b) {
+ !a.is(b, "array") && (b = Array.prototype.splice.call(arguments, 0, arguments.length));
+ var c = new cG(b);
+ this.__set__ && this.__set__.push(c);
+ return c
+ }, k.setStart = function (a) {this.__set__ = a || this.set()}, k.setFinish = function (a) {
+ var b = this.__set__;
+ delete this.__set__;
+ return b
+ }, k.setSize = function (b, c) {return a._engine.setSize.call(this, b, c)}, k.setViewBox = function (b, c, d, e, f) {return a._engine.setViewBox.call(this, b, c, d, e, f)}, k.top = k.bottom = null, k.raphael = a;
+ var co = function (a) {
+ var b = a.getBoundingClientRect(), c = a.ownerDocument, d = c.body, e = c.documentElement, f = e.clientTop || d.clientTop || 0, g = e.clientLeft || d.clientLeft || 0, i = b.top + (h.win.pageYOffset || e.scrollTop || d.scrollTop) - f, j = b.left + (h.win.pageXOffset || e.scrollLeft || d.scrollLeft) - g;
+ return{y: i, x: j}
+ };
+ k.getElementByPoint = function (a, b) {
+ var c = this, d = c.canvas, e = h.doc.elementFromPoint(a, b);
+ if (h.win.opera && e.tagName == "svg") {
+ var f = co(d), g = d.createSVGRect();
+ g.x = a - f.x, g.y = b - f.y, g.width = g.height = 1;
+ var i = d.getIntersectionList(g, null);
+ i.length && (e = i[i.length - 1])
+ }
+ if (!e)return null;
+ while (e.parentNode && e != d.parentNode && !e.raphael)e = e.parentNode;
+ e == c.canvas.parentNode && (e = d), e = e && e.raphael ? c.getById(e.raphaelid) : null;
+ return e
+ }, k.getById = function (a) {
+ var b = this.bottom;
+ while (b) {
+ if (b.id == a)return b;
+ b = b.next
+ }
+ return null
+ }, k.forEach = function (a, b) {
+ var c = this.bottom;
+ while (c) {
+ if (a.call(b, c) === !1)return this;
+ c = c.next
+ }
+ return this
+ }, k.getElementsByPoint = function (a, b) {
+ var c = this.set();
+ this.forEach(function (d) {d.isPointInside(a, b) && c.push(d)});
+ return c
+ }, cl.isPointInside = function (b, c) {
+ var d = this.realPath = this.realPath || bi[this.type](this);
+ return a.isPointInsidePath(d, b, c)
+ }, cl.getBBox = function (a) {
+ if (this.removed)return{};
+ var b = this._;
+ if (a) {
+ if (b.dirty || !b.bboxwt)this.realPath = bi[this.type](this), b.bboxwt = bI(this.realPath), b.bboxwt.toString = cq, b.dirty = 0;
+ return b.bboxwt
+ }
+ if (b.dirty || b.dirtyT || !b.bbox) {
+ if (b.dirty || !this.realPath)b.bboxwt = 0, this.realPath = bi[this.type](this);
+ b.bbox = bI(bj(this.realPath, this.matrix)), b.bbox.toString = cq, b.dirty = b.dirtyT = 0
+ }
+ return b.bbox
+ }, cl.clone = function () {
+ if (this.removed)return null;
+ var a = this.paper[this.type]().attr(this.attr());
+ this.__set__ && this.__set__.push(a);
+ return a
+ }, cl.glow = function (a) {
+ if (this.type == "text")return null;
+ a = a || {};
+ var b = {width: (a.width || 10) + (+this.attr("stroke-width") || 1), fill: a.fill || !1, opacity: a.opacity || .5, offsetx: a.offsetx || 0, offsety: a.offsety || 0, color: a.color || "#000"}, c = b.width / 2, d = this.paper, e = d.set(), f = this.realPath || bi[this.type](this);
+ f = this.matrix ? bj(f, this.matrix) : f;
+ for (var g = 1; g < c + 1; g++)e.push(d.path(f).attr({stroke: b.color, fill: b.fill ? b.color : "none", "stroke-linejoin": "round", "stroke-linecap": "round", "stroke-width": +(b.width / c * g).toFixed(3), opacity: +(b.opacity / c).toFixed(3)}));
+ return e.insertBefore(this).translate(b.offsetx, b.offsety)
+ };
+ var cr = {}, cs = function (b, c, d, e, f, g, h, i, j) {return j == null ? bB(b, c, d, e, f, g, h, i) : a.findDotsAtSegment(b, c, d, e, f, g, h, i, bC(b, c, d, e, f, g, h, i, j))}, ct = function (b, c) {
+ return function (d, e, f) {
+ d = bR(d);
+ var g, h, i, j, k = "", l = {}, m, n = 0;
+ for (var o = 0, p = d.length; o < p; o++) {
+ i = d[o];
+ if (i[0] == "M")g = +i[1], h = +i[2]; else {
+ j = cs(g, h, i[1], i[2], i[3], i[4], i[5], i[6]);
+ if (n + j > e) {
+ if (c && !l.start) {
+ m = cs(g, h, i[1], i[2], i[3], i[4], i[5], i[6], e - n), k += ["C" + m.start.x, m.start.y, m.m.x, m.m.y, m.x, m.y];
+ if (f)return k;
+ l.start = k, k = ["M" + m.x, m.y + "C" + m.n.x, m.n.y, m.end.x, m.end.y, i[5], i[6]].join(), n += j, g = +i[5], h = +i[6];
+ continue
+ }
+ if (!b && !c) {
+ m = cs(g, h, i[1], i[2], i[3], i[4], i[5], i[6], e - n);
+ return{x: m.x, y: m.y, alpha: m.alpha}
+ }
+ }
+ n += j, g = +i[5], h = +i[6]
+ }
+ k += i.shift() + i
+ }
+ l.end = k, m = b ? n : c ? l : a.findDotsAtSegment(g, h, i[0], i[1], i[2], i[3], i[4], i[5], 1), m.alpha && (m = {x: m.x, y: m.y, alpha: m.alpha});
+ return m
+ }
+ }, cu = ct(1), cv = ct(), cw = ct(0, 1);
+ a.getTotalLength = cu, a.getPointAtLength = cv, a.getSubpath = function (a, b, c) {
+ if (this.getTotalLength(a) - c < 1e-6)return cw(a, b).end;
+ var d = cw(a, c, 1);
+ return b ? cw(d, b).end : d
+ }, cl.getTotalLength = function () {
+ if (this.type == "path") {
+ if (this.node.getTotalLength)return this.node.getTotalLength();
+ return cu(this.attrs.path)
+ }
+ }, cl.getPointAtLength = function (a) {if (this.type == "path")return cv(this.attrs.path, a)}, cl.getSubpath = function (b, c) {if (this.type == "path")return a.getSubpath(this.attrs.path, b, c)};
+ var cx = a.easing_formulas = {linear: function (a) {return a}, "<": function (a) {return A(a, 1.7)}, ">": function (a) {return A(a, .48)}, "<>": function (a) {
+ var b = .48 - a / 1.04, c = w.sqrt(.1734 + b * b), d = c - b, e = A(z(d), 1 / 3) * (d < 0 ? -1 : 1), f = -c - b, g = A(z(f), 1 / 3) * (f < 0 ? -1 : 1), h = e + g + .5;
+ return(1 - h) * 3 * h * h + h * h * h
+ }, backIn: function (a) {
+ var b = 1.70158;
+ return a * a * ((b + 1) * a - b)
+ }, backOut: function (a) {
+ a = a - 1;
+ var b = 1.70158;
+ return a * a * ((b + 1) * a + b) + 1
+ }, elastic: function (a) {
+ if (a == !!a)return a;
+ return A(2, -10 * a) * w.sin((a - .075) * 2 * B / .3) + 1
+ }, bounce: function (a) {
+ var b = 7.5625, c = 2.75, d;
+ a < 1 / c ? d = b * a * a : a < 2 / c ? (a -= 1.5 / c, d = b * a * a + .75) : a < 2.5 / c ? (a -= 2.25 / c, d = b * a * a + .9375) : (a -= 2.625 / c, d = b * a * a + .984375);
+ return d
+ }};
+ cx.easeIn = cx["ease-in"] = cx["<"], cx.easeOut = cx["ease-out"] = cx[">"], cx.easeInOut = cx["ease-in-out"] = cx["<>"], cx["back-in"] = cx.backIn, cx["back-out"] = cx.backOut;
+ var cy = [], cz = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (a) {setTimeout(a, 16)}, cA = function () {
+ var b = +(new Date), c = 0;
+ for (; c < cy.length; c++) {
+ var d = cy[c];
+ if (d.el.removed || d.paused)continue;
+ var e = b - d.start, f = d.ms, h = d.easing, i = d.from, j = d.diff, k = d.to, l = d.t, m = d.el, o = {}, p, r = {}, s;
+ d.initstatus ? (e = (d.initstatus * d.anim.top - d.prev) / (d.percent - d.prev) * f, d.status = d.initstatus, delete d.initstatus, d.stop && cy.splice(c--, 1)) : d.status = (d.prev + (d.percent - d.prev) * (e / f)) / d.anim.top;
+ if (e < 0)continue;
+ if (e < f) {
+ var t = h(e / f);
+ for (var u in i)if (i[g](u)) {
+ switch (U[u]) {
+ case C:
+ p = +i[u] + t * f * j[u];
+ break;
+ case"colour":
+ p = "rgb(" + [cB(O(i[u].r + t * f * j[u].r)), cB(O(i[u].g + t * f * j[u].g)), cB(O(i[u].b + t * f * j[u].b))].join(",") + ")";
+ break;
+ case"path":
+ p = [];
+ for (var v = 0, w = i[u].length; v < w; v++) {
+ p[v] = [i[u][v][0]];
+ for (var x = 1, y = i[u][v].length; x < y; x++)p[v][x] = +i[u][v][x] + t * f * j[u][v][x];
+ p[v] = p[v].join(q)
+ }
+ p = p.join(q);
+ break;
+ case"transform":
+ if (j[u].real) {
+ p = [];
+ for (v = 0, w = i[u].length; v < w; v++) {
+ p[v] = [i[u][v][0]];
+ for (x = 1, y = i[u][v].length; x < y; x++)p[v][x] = i[u][v][x] + t * f * j[u][v][x]
+ }
+ } else {
+ var z = function (a) {return+i[u][a] + t * f * j[u][a]};
+ p = [
+ ["m", z(0), z(1), z(2), z(3), z(4), z(5)]
+ ]
+ }
+ break;
+ case"csv":
+ if (u == "clip-rect") {
+ p = [], v = 4;
+ while (v--)p[v] = +i[u][v] + t * f * j[u][v]
+ }
+ break;
+ default:
+ var A = [][n](i[u]);
+ p = [], v = m.paper.customAttributes[u].length;
+ while (v--)p[v] = +A[v] + t * f * j[u][v]
+ }
+ o[u] = p
+ }
+ m.attr(o), function (a, b, c) {setTimeout(function () {eve("raphael.anim.frame." + a, b, c)})}(m.id, m, d.anim)
+ } else {
+ (function (b, c, d) {setTimeout(function () {eve("raphael.anim.frame." + c.id, c, d), eve("raphael.anim.finish." + c.id, c, d), a.is(b, "function") && b.call(c)})})(d.callback, m, d.anim), m.attr(k), cy.splice(c--, 1);
+ if (d.repeat > 1 && !d.next) {
+ for (s in k)k[g](s) && (r[s] = d.totalOrigin[s]);
+ d.el.attr(r), cE(d.anim, d.el, d.anim.percents[0], null, d.totalOrigin, d.repeat - 1)
+ }
+ d.next && !d.stop && cE(d.anim, d.el, d.next, null, d.totalOrigin, d.repeat)
+ }
+ }
+ a.svg && m && m.paper && m.paper.safari(), cy.length && cz(cA)
+ }, cB = function (a) {return a > 255 ? 255 : a < 0 ? 0 : a};
+ cl.animateWith = function (b, c, d, e, f, g) {
+ var h = this;
+ if (h.removed) {
+ g && g.call(h);
+ return h
+ }
+ var i = d instanceof cD ? d : a.animation(d, e, f, g), j, k;
+ cE(i, h, i.percents[0], null, h.attr());
+ for (var l = 0, m = cy.length; l < m; l++)if (cy[l].anim == c && cy[l].el == b) {
+ cy[m - 1].start = cy[l].start;
+ break
+ }
+ return h
+ }, cl.onAnimation = function (a) {
+ a ? eve.on("raphael.anim.frame." + this.id, a) : eve.unbind("raphael.anim.frame." + this.id);
+ return this
+ }, cD.prototype.delay = function (a) {
+ var b = new cD(this.anim, this.ms);
+ b.times = this.times, b.del = +a || 0;
+ return b
+ }, cD.prototype.repeat = function (a) {
+ var b = new cD(this.anim, this.ms);
+ b.del = this.del, b.times = w.floor(x(a, 0)) || 1;
+ return b
+ }, a.animation = function (b, c, d, e) {
+ if (b instanceof cD)return b;
+ if (a.is(d, "function") || !d)e = e || d || null, d = null;
+ b = Object(b), c = +c || 0;
+ var f = {}, h, i;
+ for (i in b)b[g](i) && Q(i) != i && Q(i) + "%" != i && (h = !0, f[i] = b[i]);
+ if (!h)return new cD(b, c);
+ d && (f.easing = d), e && (f.callback = e);
+ return new cD({100: f}, c)
+ }, cl.animate = function (b, c, d, e) {
+ var f = this;
+ if (f.removed) {
+ e && e.call(f);
+ return f
+ }
+ var g = b instanceof cD ? b : a.animation(b, c, d, e);
+ cE(g, f, g.percents[0], null, f.attr());
+ return f
+ }, cl.setTime = function (a, b) {
+ a && b != null && this.status(a, y(b, a.ms) / a.ms);
+ return this
+ }, cl.status = function (a, b) {
+ var c = [], d = 0, e, f;
+ if (b != null) {
+ cE(a, this, -1, y(b, 1));
+ return this
+ }
+ e = cy.length;
+ for (; d < e; d++) {
+ f = cy[d];
+ if (f.el.id == this.id && (!a || f.anim == a)) {
+ if (a)return f.status;
+ c.push({anim: f.anim, status: f.status})
+ }
+ }
+ if (a)return 0;
+ return c
+ }, cl.pause = function (a) {
+ for (var b = 0; b < cy.length; b++)cy[b].el.id == this.id && (!a || cy[b].anim == a) && eve("raphael.anim.pause." + this.id, this, cy[b].anim) !== !1 && (cy[b].paused = !0);
+ return this
+ }, cl.resume = function (a) {
+ for (var b = 0; b < cy.length; b++)if (cy[b].el.id == this.id && (!a || cy[b].anim == a)) {
+ var c = cy[b];
+ eve("raphael.anim.resume." + this.id, this, c.anim) !== !1 && (delete c.paused, this.status(c.anim, c.status))
+ }
+ return this
+ }, cl.stop = function (a) {
+ for (var b = 0; b < cy.length; b++)cy[b].el.id == this.id && (!a || cy[b].anim == a) && eve("raphael.anim.stop." + this.id, this, cy[b].anim) !== !1 && cy.splice(b--, 1);
+ return this
+ }, eve.on("raphael.remove", cF), eve.on("raphael.clear", cF), cl.toString = function () {return"Raphaël’s object"};
+ var cG = function (a) {
+ this.items = [], this.length = 0, this.type = "set";
+ if (a)for (var b = 0, c = a.length; b < c; b++)a[b] && (a[b].constructor == cl.constructor || a[b].constructor == cG) && (this[this.items.length] = this.items[this.items.length] = a[b], this.length++)
+ }, cH = cG.prototype;
+ cH.push = function () {
+ var a, b;
+ for (var c = 0, d = arguments.length; c < d; c++)a = arguments[c], a && (a.constructor == cl.constructor || a.constructor == cG) && (b = this.items.length, this[b] = this.items[b] = a, this.length++);
+ return this
+ }, cH.pop = function () {
+ this.length && delete this[this.length--];
+ return this.items.pop()
+ }, cH.forEach = function (a, b) {
+ for (var c = 0, d = this.items.length; c < d; c++)if (a.call(b, this.items[c], c) === !1)return this;
+ return this
+ };
+ for (var cI in cl)cl[g](cI) && (cH[cI] = function (a) {
+ return function () {
+ var b = arguments;
+ return this.forEach(function (c) {c[a][m](c, b)})
+ }
+ }(cI));
+ cH.attr = function (b, c) {
+ if (b && a.is(b, E) && a.is(b[0], "object"))for (var d = 0, e = b.length; d < e; d++)this.items[d].attr(b[d]); else for (var f = 0, g = this.items.length; f < g; f++)this.items[f].attr(b, c);
+ return this
+ }, cH.clear = function () {while (this.length)this.pop()}, cH.splice = function (a, b, c) {
+ a = a < 0 ? x(this.length + a, 0) : a, b = x(0, y(this.length - a, b));
+ var d = [], e = [], f = [], g;
+ for (g = 2; g < arguments.length; g++)f.push(arguments[g]);
+ for (g = 0; g < b; g++)e.push(this[a + g]);
+ for (; g < this.length - a; g++)d.push(this[a + g]);
+ var h = f.length;
+ for (g = 0; g < h + d.length; g++)this.items[a + g] = this[a + g] = g < h ? f[g] : d[g - h];
+ g = this.items.length = this.length -= b - h;
+ while (this[g])delete this[g++];
+ return new cG(e)
+ }, cH.exclude = function (a) {
+ for (var b = 0, c = this.length; b < c; b++)if (this[b] == a) {
+ this.splice(b, 1);
+ return!0
+ }
+ }, cH.animate = function (b, c, d, e) {
+ (a.is(d, "function") || !d) && (e = d || null);
+ var f = this.items.length, g = f, h, i = this, j;
+ if (!f)return this;
+ e && (j = function () {!--f && e.call(i)}), d = a.is(d, D) ? d : j;
+ var k = a.animation(b, c, d, j);
+ h = this.items[--g].animate(k);
+ while (g--)this.items[g] && !this.items[g].removed && this.items[g].animateWith(h, k, k);
+ return this
+ }, cH.insertAfter = function (a) {
+ var b = this.items.length;
+ while (b--)this.items[b].insertAfter(a);
+ return this
+ }, cH.getBBox = function () {
+ var a = [], b = [], c = [], d = [];
+ for (var e = this.items.length; e--;)if (!this.items[e].removed) {
+ var f = this.items[e].getBBox();
+ a.push(f.x), b.push(f.y), c.push(f.x + f.width), d.push(f.y + f.height)
+ }
+ a = y[m](0, a), b = y[m](0, b), c = x[m](0, c), d = x[m](0, d);
+ return{x: a, y: b, x2: c, y2: d, width: c - a, height: d - b}
+ }, cH.clone = function (a) {
+ a = new cG;
+ for (var b = 0, c = this.items.length; b < c; b++)a.push(this.items[b].clone());
+ return a
+ }, cH.toString = function () {return"Raphaël‘s set"}, a.registerFont = function (a) {
+ if (!a.face)return a;
+ this.fonts = this.fonts || {};
+ var b = {w: a.w, face: {}, glyphs: {}}, c = a.face["font-family"];
+ for (var d in a.face)a.face[g](d) && (b.face[d] = a.face[d]);
+ this.fonts[c] ? this.fonts[c].push(b) : this.fonts[c] = [b];
+ if (!a.svg) {
+ b.face["units-per-em"] = R(a.face["units-per-em"], 10);
+ for (var e in a.glyphs)if (a.glyphs[g](e)) {
+ var f = a.glyphs[e];
+ b.glyphs[e] = {w: f.w, k: {}, d: f.d && "M" + f.d.replace(/[mlcxtrv]/g, function (a) {return{l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[a] || "M"}) + "z"};
+ if (f.k)for (var h in f.k)f[g](h) && (b.glyphs[e].k[h] = f.k[h])
+ }
+ }
+ return a
+ }, k.getFont = function (b, c, d, e) {
+ e = e || "normal", d = d || "normal", c = +c || {normal: 400, bold: 700, lighter: 300, bolder: 800}[c] || 400;
+ if (!!a.fonts) {
+ var f = a.fonts[b];
+ if (!f) {
+ var h = new RegExp("(^|\\s)" + b.replace(/[^\w\d\s+!~.:_-]/g, p) + "(\\s|$)", "i");
+ for (var i in a.fonts)if (a.fonts[g](i) && h.test(i)) {
+ f = a.fonts[i];
+ break
+ }
+ }
+ var j;
+ if (f)for (var k = 0, l = f.length; k < l; k++) {
+ j = f[k];
+ if (j.face["font-weight"] == c && (j.face["font-style"] == d || !j.face["font-style"]) && j.face["font-stretch"] == e)break
+ }
+ return j
+ }
+ }, k.print = function (b, d, e, f, g, h, i) {
+ h = h || "middle", i = x(y(i || 0, 1), -1);
+ var j = r(e)[s](p), k = 0, l = 0, m = p, n;
+ a.is(f, e) && (f = this.getFont(f));
+ if (f) {
+ n = (g || 16) / f.face["units-per-em"];
+ var o = f.face.bbox[s](c), q = +o[0], t = o[3] - o[1], u = 0, v = +o[1] + (h == "baseline" ? t + +f.face.descent : t / 2);
+ for (var w = 0, z = j.length; w < z; w++) {
+ if (j[w] == "\n")k = 0, B = 0, l = 0, u += t; else {
+ var A = l && f.glyphs[j[w - 1]] || {}, B = f.glyphs[j[w]];
+ k += l ? (A.w || f.w) + (A.k && A.k[j[w]] || 0) + f.w * i : 0, l = 1
+ }
+ B && B.d && (m += a.transformPath(B.d, ["t", k * n, u * n, "s", n, n, q, v, "t", (b - q) / n, (d - v) / n]))
+ }
+ }
+ return this.path(m).attr({fill: "#000", stroke: "none"})
+ }, k.add = function (b) {
+ if (a.is(b, "array")) {
+ var c = this.set(), e = 0, f = b.length, h;
+ for (; e < f; e++)h = b[e] || {}, d[g](h.type) && c.push(this[h.type]().attr(h))
+ }
+ return c
+ }, a.format = function (b, c) {
+ var d = a.is(c, E) ? [0][n](c) : arguments;
+ b && a.is(b, D) && d.length - 1 && (b = b.replace(e, function (a, b) {return d[++b] == null ? p : d[b]}));
+ return b || p
+ }, a.fullfill = function () {
+ var a = /\{([^\}]+)\}/g, b = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, c = function (a, c, d) {
+ var e = d;
+ c.replace(b, function (a, b, c, d, f) {b = b || d, e && (b in e && (e = e[b]), typeof e == "function" && f && (e = e()))}), e = (e == null || e == d ? a : e) + "";
+ return e
+ };
+ return function (b, d) {return String(b).replace(a, function (a, b) {return c(a, b, d)})}
+ }(), a.ninja = function () {
+ i.was ? h.win.Raphael = i.is : delete Raphael;
+ return a
+ }, a.st = cH, function (b, c, d) {
+ function e() {/in/.test(b.readyState) ? setTimeout(e, 9) : a.eve("raphael.DOMload")}
+
+ b.readyState == null && b.addEventListener && (b.addEventListener(c, d = function () {b.removeEventListener(c, d, !1), b.readyState = "complete"}, !1), b.readyState = "loading"), e()
+ }(document, "DOMContentLoaded"), i.was ? h.win.Raphael = a : Raphael = a, eve.on("raphael.DOMload", function () {b = !0})
+}(), window.Raphael.svg && function (a) {
+ var b = "hasOwnProperty", c = String, d = parseFloat, e = parseInt, f = Math, g = f.max, h = f.abs, i = f.pow, j = /[, ]+/, k = a.eve, l = "", m = " ", n = "http://www.w3.org/1999/xlink", o = {block: "M5,0 0,2.5 5,5z", classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z", diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z", open: "M6,1 1,3.5 6,6", oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"}, p = {};
+ a.toString = function () {return"Your browser supports SVG.\nYou are running Raphaël " + this.version};
+ var q = function (d, e) {
+ if (e) {
+ typeof d == "string" && (d = q(d));
+ for (var f in e)e[b](f) && (f.substring(0, 6) == "xlink:" ? d.setAttributeNS(n, f.substring(6), c(e[f])) : d.setAttribute(f, c(e[f])))
+ } else d = a._g.doc.createElementNS("http://www.w3.org/2000/svg", d), d.style && (d.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
+ return d
+ }, r = function (b, e) {
+ var j = "linear", k = b.id + e, m = .5, n = .5, o = b.node, p = b.paper, r = o.style, s = a._g.doc.getElementById(k);
+ if (!s) {
+ e = c(e).replace(a._radial_gradient, function (a, b, c) {
+ j = "radial";
+ if (b && c) {
+ m = d(b), n = d(c);
+ var e = (n > .5) * 2 - 1;
+ i(m - .5, 2) + i(n - .5, 2) > .25 && (n = f.sqrt(.25 - i(m - .5, 2)) * e + .5) && n != .5 && (n = n.toFixed(5) - 1e-5 * e)
+ }
+ return l
+ }), e = e.split(/\s*\-\s*/);
+ if (j == "linear") {
+ var t = e.shift();
+ t = -d(t);
+ if (isNaN(t))return null;
+ var u = [0, 0, f.cos(a.rad(t)), f.sin(a.rad(t))], v = 1 / (g(h(u[2]), h(u[3])) || 1);
+ u[2] *= v, u[3] *= v, u[2] < 0 && (u[0] = -u[2], u[2] = 0), u[3] < 0 && (u[1] = -u[3], u[3] = 0)
+ }
+ var w = a._parseDots(e);
+ if (!w)return null;
+ k = k.replace(/[\(\)\s,\xb0#]/g, "_"), b.gradient && k != b.gradient.id && (p.defs.removeChild(b.gradient), delete b.gradient);
+ if (!b.gradient) {
+ s = q(j + "Gradient", {id: k}), b.gradient = s, q(s, j == "radial" ? {fx: m, fy: n} : {x1: u[0], y1: u[1], x2: u[2], y2: u[3], gradientTransform: b.matrix.invert()}), p.defs.appendChild(s);
+ for (var x = 0, y = w.length; x < y; x++)s.appendChild(q("stop", {offset: w[x].offset ? w[x].offset : x ? "100%" : "0%", "stop-color": w[x].color || "#fff"}))
+ }
+ }
+ q(o, {fill: "url(#" + k + ")", opacity: 1, "fill-opacity": 1}), r.fill = l, r.opacity = 1, r.fillOpacity = 1;
+ return 1
+ }, s = function (a) {
+ var b = a.getBBox(1);
+ q(a.pattern, {patternTransform: a.matrix.invert() + " translate(" + b.x + "," + b.y + ")"})
+ }, t = function (d, e, f) {
+ if (d.type == "path") {
+ var g = c(e).toLowerCase().split("-"), h = d.paper, i = f ? "end" : "start", j = d.node, k = d.attrs, m = k["stroke-width"], n = g.length, r = "classic", s, t, u, v, w, x = 3, y = 3, z = 5;
+ while (n--)switch (g[n]) {
+ case"block":
+ case"classic":
+ case"oval":
+ case"diamond":
+ case"open":
+ case"none":
+ r = g[n];
+ break;
+ case"wide":
+ y = 5;
+ break;
+ case"narrow":
+ y = 2;
+ break;
+ case"long":
+ x = 5;
+ break;
+ case"short":
+ x = 2
+ }
+ r == "open" ? (x += 2, y += 2, z += 2, u = 1, v = f ? 4 : 1, w = {fill: "none", stroke: k.stroke}) : (v = u = x / 2, w = {fill: k.stroke, stroke: "none"}), d._.arrows ? f ? (d._.arrows.endPath && p[d._.arrows.endPath]--, d._.arrows.endMarker && p[d._.arrows.endMarker]--) : (d._.arrows.startPath && p[d._.arrows.startPath]--, d._.arrows.startMarker && p[d._.arrows.startMarker]--) : d._.arrows = {};
+ if (r != "none") {
+ var A = "raphael-marker-" + r, B = "raphael-marker-" + i + r + x + y;
+ a._g.doc.getElementById(A) ? p[A]++ : (h.defs.appendChild(q(q("path"), {"stroke-linecap": "round", d: o[r], id: A})), p[A] = 1);
+ var C = a._g.doc.getElementById(B), D;
+ C ? (p[B]++, D = C.getElementsByTagName("use")[0]) : (C = q(q("marker"), {id: B, markerHeight: y, markerWidth: x, orient: "auto", refX: v, refY: y / 2}), D = q(q("use"), {"xlink:href": "#" + A, transform: (f ? "rotate(180 " + x / 2 + " " + y / 2 + ") " : l) + "scale(" + x / z + "," + y / z + ")", "stroke-width": (1 / ((x / z + y / z) / 2)).toFixed(4)}), C.appendChild(D), h.defs.appendChild(C), p[B] = 1), q(D, w);
+ var F = u * (r != "diamond" && r != "oval");
+ f ? (s = d._.arrows.startdx * m || 0, t = a.getTotalLength(k.path) - F * m) : (s = F * m, t = a.getTotalLength(k.path) - (d._.arrows.enddx * m || 0)), w = {}, w["marker-" + i] = "url(#" + B + ")";
+ if (t || s)w.d = Raphael.getSubpath(k.path, s, t);
+ q(j, w), d._.arrows[i + "Path"] = A, d._.arrows[i + "Marker"] = B, d._.arrows[i + "dx"] = F, d._.arrows[i + "Type"] = r, d._.arrows[i + "String"] = e
+ } else f ? (s = d._.arrows.startdx * m || 0, t = a.getTotalLength(k.path) - s) : (s = 0, t = a.getTotalLength(k.path) - (d._.arrows.enddx * m || 0)), d._.arrows[i + "Path"] && q(j, {d: Raphael.getSubpath(k.path, s, t)}), delete d._.arrows[i + "Path"], delete d._.arrows[i + "Marker"], delete d._.arrows[i + "dx"], delete d._.arrows[i + "Type"], delete d._.arrows[i + "String"];
+ for (w in p)if (p[b](w) && !p[w]) {
+ var G = a._g.doc.getElementById(w);
+ G && G.parentNode.removeChild(G)
+ }
+ }
+ }, u = {"": [0], none: [0], "-": [3, 1], ".": [1, 1], "-.": [3, 1, 1, 1], "-..": [3, 1, 1, 1, 1, 1], ". ": [1, 3], "- ": [4, 3], "--": [8, 3], "- .": [4, 3, 1, 3], "--.": [8, 3, 1, 3], "--..": [8, 3, 1, 3, 1, 3]}, v = function (a, b, d) {
+ b = u[c(b).toLowerCase()];
+ if (b) {
+ var e = a.attrs["stroke-width"] || "1", f = {round: e, square: e, butt: 0}[a.attrs["stroke-linecap"] || d["stroke-linecap"]] || 0, g = [], h = b.length;
+ while (h--)g[h] = b[h] * e + (h % 2 ? 1 : -1) * f;
+ q(a.node, {"stroke-dasharray": g.join(",")})
+ }
+ }, w = function (d, f) {
+ var i = d.node, k = d.attrs, m = i.style.visibility;
+ i.style.visibility = "hidden";
+ for (var o in f)if (f[b](o)) {
+ if (!a._availableAttrs[b](o))continue;
+ var p = f[o];
+ k[o] = p;
+ switch (o) {
+ case"blur":
+ d.blur(p);
+ break;
+ case"href":
+ case"title":
+ case"target":
+ var u = i.parentNode;
+ if (u.tagName.toLowerCase() != "a") {
+ var w = q("a");
+ u.insertBefore(w, i), w.appendChild(i), u = w
+ }
+ o == "target" ? u.setAttributeNS(n, "show", p == "blank" ? "new" : p) : u.setAttributeNS(n, o, p);
+ break;
+ case"cursor":
+ i.style.cursor = p;
+ break;
+ case"transform":
+ d.transform(p);
+ break;
+ case"arrow-start":
+ t(d, p);
+ break;
+ case"arrow-end":
+ t(d, p, 1);
+ break;
+ case"clip-rect":
+ var x = c(p).split(j);
+ if (x.length == 4) {
+ d.clip && d.clip.parentNode.parentNode.removeChild(d.clip.parentNode);
+ var z = q("clipPath"), A = q("rect");
+ z.id = a.createUUID(), q(A, {x: x[0], y: x[1], width: x[2], height: x[3]}), z.appendChild(A), d.paper.defs.appendChild(z), q(i, {"clip-path": "url(#" + z.id + ")"}), d.clip = A
+ }
+ if (!p) {
+ var B = i.getAttribute("clip-path");
+ if (B) {
+ var C = a._g.doc.getElementById(B.replace(/(^url\(#|\)$)/g, l));
+ C && C.parentNode.removeChild(C), q(i, {"clip-path": l}), delete d.clip
+ }
+ }
+ break;
+ case"path":
+ d.type == "path" && (q(i, {d: p ? k.path = a._pathToAbsolute(p) : "M0,0"}), d._.dirty = 1, d._.arrows && ("startString"in d._.arrows && t(d, d._.arrows.startString), "endString"in d._.arrows && t(d, d._.arrows.endString, 1)));
+ break;
+ case"width":
+ i.setAttribute(o, p), d._.dirty = 1;
+ if (k.fx)o = "x", p = k.x; else break;
+ case"x":
+ k.fx && (p = -k.x - (k.width || 0));
+ case"rx":
+ if (o == "rx" && d.type == "rect")break;
+ case"cx":
+ i.setAttribute(o, p), d.pattern && s(d), d._.dirty = 1;
+ break;
+ case"height":
+ i.setAttribute(o, p), d._.dirty = 1;
+ if (k.fy)o = "y", p = k.y; else break;
+ case"y":
+ k.fy && (p = -k.y - (k.height || 0));
+ case"ry":
+ if (o == "ry" && d.type == "rect")break;
+ case"cy":
+ i.setAttribute(o, p), d.pattern && s(d), d._.dirty = 1;
+ break;
+ case"r":
+ d.type == "rect" ? q(i, {rx: p, ry: p}) : i.setAttribute(o, p), d._.dirty = 1;
+ break;
+ case"src":
+ d.type == "image" && i.setAttributeNS(n, "href", p);
+ break;
+ case"stroke-width":
+ if (d._.sx != 1 || d._.sy != 1)p /= g(h(d._.sx), h(d._.sy)) || 1;
+ d.paper._vbSize && (p *= d.paper._vbSize), i.setAttribute(o, p), k["stroke-dasharray"] && v(d, k["stroke-dasharray"], f), d._.arrows && ("startString"in d._.arrows && t(d, d._.arrows.startString), "endString"in d._.arrows && t(d, d._.arrows.endString, 1));
+ break;
+ case"stroke-dasharray":
+ v(d, p, f);
+ break;
+ case"fill":
+ var D = c(p).match(a._ISURL);
+ if (D) {
+ z = q("pattern");
+ var F = q("image");
+ z.id = a.createUUID(), q(z, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1}), q(F, {x: 0, y: 0, "xlink:href": D[1]}), z.appendChild(F), function (b) {
+ a._preload(D[1], function () {
+ var a = this.offsetWidth, c = this.offsetHeight;
+ q(b, {width: a, height: c}), q(F, {width: a, height: c}), d.paper.safari()
+ })
+ }(z), d.paper.defs.appendChild(z), q(i, {fill: "url(#" + z.id + ")"}), d.pattern = z, d.pattern && s(d);
+ break
+ }
+ var G = a.getRGB(p);
+ if (!G.error)delete f.gradient, delete k.gradient, !a.is(k.opacity, "undefined") && a.is(f.opacity, "undefined") && q(i, {opacity: k.opacity}), !a.is(k["fill-opacity"], "undefined") && a.is(f["fill-opacity"], "undefined") && q(i, {"fill-opacity": k["fill-opacity"]}); else if ((d.type == "circle" || d.type == "ellipse" || c(p).charAt() != "r") && r(d, p)) {
+ if ("opacity"in k || "fill-opacity"in k) {
+ var H = a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g, l));
+ if (H) {
+ var I = H.getElementsByTagName("stop");
+ q(I[I.length - 1], {"stop-opacity": ("opacity"in k ? k.opacity : 1) * ("fill-opacity"in k ? k["fill-opacity"] : 1)})
+ }
+ }
+ k.gradient = p, k.fill = "none";
+ break
+ }
+ G[b]("opacity") && q(i, {"fill-opacity": G.opacity > 1 ? G.opacity / 100 : G.opacity});
+ case"stroke":
+ G = a.getRGB(p), i.setAttribute(o, G.hex), o == "stroke" && G[b]("opacity") && q(i, {"stroke-opacity": G.opacity > 1 ? G.opacity / 100 : G.opacity}), o == "stroke" && d._.arrows && ("startString"in d._.arrows && t(d, d._.arrows.startString), "endString"in d._.arrows && t(d, d._.arrows.endString, 1));
+ break;
+ case"gradient":
+ (d.type == "circle" || d.type == "ellipse" || c(p).charAt() != "r") && r(d, p);
+ break;
+ case"opacity":
+ k.gradient && !k[b]("stroke-opacity") && q(i, {"stroke-opacity": p > 1 ? p / 100 : p});
+ case"fill-opacity":
+ if (k.gradient) {
+ H = a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g, l)), H && (I = H.getElementsByTagName("stop"), q(I[I.length - 1], {"stop-opacity": p}));
+ break
+ }
+ ;
+ default:
+ o == "font-size" && (p = e(p, 10) + "px");
+ var J = o.replace(/(\-.)/g, function (a) {return a.substring(1).toUpperCase()});
+ i.style[J] = p, d._.dirty = 1, i.setAttribute(o, p)
+ }
+ }
+ y(d, f), i.style.visibility = m
+ }, x = 1.2, y = function (d, f) {
+ if (d.type == "text" && !!(f[b]("text") || f[b]("font") || f[b]("font-size") || f[b]("x") || f[b]("y"))) {
+ var g = d.attrs, h = d.node, i = h.firstChild ? e(a._g.doc.defaultView.getComputedStyle(h.firstChild, l).getPropertyValue("font-size"), 10) : 10;
+ if (f[b]("text")) {
+ g.text = f.text;
+ while (h.firstChild)h.removeChild(h.firstChild);
+ var j = c(f.text).split("\n"), k = [], m;
+ for (var n = 0, o = j.length; n < o; n++)m = q("tspan"), n && q(m, {dy: i * x, x: g.x}), m.appendChild(a._g.doc.createTextNode(j[n])), h.appendChild(m), k[n] = m
+ } else {
+ k = h.getElementsByTagName("tspan");
+ for (n = 0, o = k.length; n < o; n++)n ? q(k[n], {dy: i * x, x: g.x}) : q(k[0], {dy: 0})
+ }
+ q(h, {x: g.x, y: g.y}), d._.dirty = 1;
+ var p = d._getBBox(), r = g.y - (p.y + p.height / 2);
+ r && a.is(r, "finite") && q(k[0], {dy: r})
+ }
+ }, z = function (b, c) {
+ var d = 0, e = 0;
+ this[0] = this.node = b, b.raphael = !0, this.id = a._oid++, b.raphaelid = this.id, this.matrix = a.matrix(), this.realPath = null, this.paper = c, this.attrs = this.attrs || {}, this._ = {transform: [], sx: 1, sy: 1, deg: 0, dx: 0, dy: 0, dirty: 1}, !c.bottom && (c.bottom = this), this.prev = c.top, c.top && (c.top.next = this), c.top = this, this.next = null
+ }, A = a.el;
+ z.prototype = A, A.constructor = z, a._engine.path = function (a, b) {
+ var c = q("path");
+ b.canvas && b.canvas.appendChild(c);
+ var d = new z(c, b);
+ d.type = "path", w(d, {fill: "none", stroke: "#000", path: a});
+ return d
+ }, A.rotate = function (a, b, e) {
+ if (this.removed)return this;
+ a = c(a).split(j), a.length - 1 && (b = d(a[1]), e = d(a[2])), a = d(a[0]), e == null && (b = e);
+ if (b == null || e == null) {
+ var f = this.getBBox(1);
+ b = f.x + f.width / 2, e = f.y + f.height / 2
+ }
+ this.transform(this._.transform.concat([
+ ["r", a, b, e]
+ ]));
+ return this
+ }, A.scale = function (a, b, e, f) {
+ if (this.removed)return this;
+ a = c(a).split(j), a.length - 1 && (b = d(a[1]), e = d(a[2]), f = d(a[3])), a = d(a[0]), b == null && (b = a), f == null && (e = f);
+ if (e == null || f == null)var g = this.getBBox(1);
+ e = e == null ? g.x + g.width / 2 : e, f = f == null ? g.y + g.height / 2 : f, this.transform(this._.transform.concat([
+ ["s", a, b, e, f]
+ ]));
+ return this
+ }, A.translate = function (a, b) {
+ if (this.removed)return this;
+ a = c(a).split(j), a.length - 1 && (b = d(a[1])), a = d(a[0]) || 0, b = +b || 0, this.transform(this._.transform.concat([
+ ["t", a, b]
+ ]));
+ return this
+ }, A.transform = function (c) {
+ var d = this._;
+ if (c == null)return d.transform;
+ a._extractTransform(this, c), this.clip && q(this.clip, {transform: this.matrix.invert()}), this.pattern && s(this), this.node && q(this.node, {transform: this.matrix});
+ if (d.sx != 1 || d.sy != 1) {
+ var e = this.attrs[b]("stroke-width") ? this.attrs["stroke-width"] : 1;
+ this.attr({"stroke-width": e})
+ }
+ return this
+ }, A.hide = function () {
+ !this.removed && this.paper.safari(this.node.style.display = "none");
+ return this
+ }, A.show = function () {
+ !this.removed && this.paper.safari(this.node.style.display = "");
+ return this
+ }, A.remove = function () {
+ if (!this.removed && !!this.node.parentNode) {
+ var b = this.paper;
+ b.__set__ && b.__set__.exclude(this), k.unbind("raphael.*.*." + this.id), this.gradient && b.defs.removeChild(this.gradient), a._tear(this, b), this.node.parentNode.tagName.toLowerCase() == "a" ? this.node.parentNode.parentNode.removeChild(this.node.parentNode) : this.node.parentNode.removeChild(this.node);
+ for (var c in this)this[c] = typeof this[c] == "function" ? a._removedFactory(c) : null;
+ this.removed = !0
+ }
+ }, A._getBBox = function () {
+ if (this.node.style.display == "none") {
+ this.show();
+ var a = !0
+ }
+ var b = {};
+ try {b = this.node.getBBox()} catch (c) {} finally {b = b || {}}
+ a && this.hide();
+ return b
+ }, A.attr = function (c, d) {
+ if (this.removed)return this;
+ if (c == null) {
+ var e = {};
+ for (var f in this.attrs)this.attrs[b](f) && (e[f] = this.attrs[f]);
+ e.gradient && e.fill == "none" && (e.fill = e.gradient) && delete e.gradient, e.transform = this._.transform;
+ return e
+ }
+ if (d == null && a.is(c, "string")) {
+ if (c == "fill" && this.attrs.fill == "none" && this.attrs.gradient)return this.attrs.gradient;
+ if (c == "transform")return this._.transform;
+ var g = c.split(j), h = {};
+ for (var i = 0, l = g.length; i < l; i++)c = g[i], c in this.attrs ? h[c] = this.attrs[c] : a.is(this.paper.customAttributes[c], "function") ? h[c] = this.paper.customAttributes[c].def : h[c] = a._availableAttrs[c];
+ return l - 1 ? h : h[g[0]]
+ }
+ if (d == null && a.is(c, "array")) {
+ h = {};
+ for (i = 0, l = c.length; i < l; i++)h[c[i]] = this.attr(c[i]);
+ return h
+ }
+ if (d != null) {
+ var m = {};
+ m[c] = d
+ } else c != null && a.is(c, "object") && (m = c);
+ for (var n in m)k("raphael.attr." + n + "." + this.id, this, m[n]);
+ for (n in this.paper.customAttributes)if (this.paper.customAttributes[b](n) && m[b](n) && a.is(this.paper.customAttributes[n], "function")) {
+ var o = this.paper.customAttributes[n].apply(this, [].concat(m[n]));
+ this.attrs[n] = m[n];
+ for (var p in o)o[b](p) && (m[p] = o[p])
+ }
+ w(this, m);
+ return this
+ }, A.toFront = function () {
+ if (this.removed)return this;
+ this.node.parentNode.tagName.toLowerCase() == "a" ? this.node.parentNode.parentNode.appendChild(this.node.parentNode) : this.node.parentNode.appendChild(this.node);
+ var b = this.paper;
+ b.top != this && a._tofront(this, b);
+ return this
+ }, A.toBack = function () {
+ if (this.removed)return this;
+ var b = this.node.parentNode;
+ b.tagName.toLowerCase() == "a" ? b.parentNode.insertBefore(this.node.parentNode, this.node.parentNode.parentNode.firstChild) : b.firstChild != this.node && b.insertBefore(this.node, this.node.parentNode.firstChild), a._toback(this, this.paper);
+ var c = this.paper;
+ return this
+ }, A.insertAfter = function (b) {
+ if (this.removed)return this;
+ var c = b.node || b[b.length - 1].node;
+ c.nextSibling ? c.parentNode.insertBefore(this.node, c.nextSibling) : c.parentNode.appendChild(this.node), a._insertafter(this, b, this.paper);
+ return this
+ }, A.insertBefore = function (b) {
+ if (this.removed)return this;
+ var c = b.node || b[0].node;
+ c.parentNode.insertBefore(this.node, c), a._insertbefore(this, b, this.paper);
+ return this
+ }, A.blur = function (b) {
+ var c = this;
+ if (+b !== 0) {
+ var d = q("filter"), e = q("feGaussianBlur");
+ c.attrs.blur = b, d.id = a.createUUID(), q(e, {stdDeviation: +b || 1.5}), d.appendChild(e), c.paper.defs.appendChild(d), c._blur = d, q(c.node, {filter: "url(#" + d.id + ")"})
+ } else c._blur && (c._blur.parentNode.removeChild(c._blur), delete c._blur, delete c.attrs.blur), c.node.removeAttribute("filter")
+ }, a._engine.circle = function (a, b, c, d) {
+ var e = q("circle");
+ a.canvas && a.canvas.appendChild(e);
+ var f = new z(e, a);
+ f.attrs = {cx: b, cy: c, r: d, fill: "none", stroke: "#000"}, f.type = "circle", q(e, f.attrs);
+ return f
+ }, a._engine.rect = function (a, b, c, d, e, f) {
+ var g = q("rect");
+ a.canvas && a.canvas.appendChild(g);
+ var h = new z(g, a);
+ h.attrs = {x: b, y: c, width: d, height: e, r: f || 0, rx: f || 0, ry: f || 0, fill: "none", stroke: "#000"}, h.type = "rect", q(g, h.attrs);
+ return h
+ }, a._engine.ellipse = function (a, b, c, d, e) {
+ var f = q("ellipse");
+ a.canvas && a.canvas.appendChild(f);
+ var g = new z(f, a);
+ g.attrs = {cx: b, cy: c, rx: d, ry: e, fill: "none", stroke: "#000"}, g.type = "ellipse", q(f, g.attrs);
+ return g
+ }, a._engine.image = function (a, b, c, d, e, f) {
+ var g = q("image");
+ q(g, {x: c, y: d, width: e, height: f, preserveAspectRatio: "none"}), g.setAttributeNS(n, "href", b), a.canvas && a.canvas.appendChild(g);
+ var h = new z(g, a);
+ h.attrs = {x: c, y: d, width: e, height: f, src: b}, h.type = "image";
+ return h
+ }, a._engine.text = function (b, c, d, e) {
+ var f = q("text");
+ b.canvas && b.canvas.appendChild(f);
+ var g = new z(f, b);
+ g.attrs = {x: c, y: d, "text-anchor": "middle", text: e, font: a._availableAttrs.font, stroke: "none", fill: "#000"}, g.type = "text", w(g, g.attrs);
+ return g
+ }, a._engine.setSize = function (a, b) {
+ this.width = a || this.width, this.height = b || this.height, this.canvas.setAttribute("width", this.width), this.canvas.setAttribute("height", this.height), this._viewBox && this.setViewBox.apply(this, this._viewBox);
+ return this
+ }, a._engine.create = function () {
+ var b = a._getContainer.apply(0, arguments), c = b && b.container, d = b.x, e = b.y, f = b.width, g = b.height;
+ if (!c)throw new Error("SVG container not found.");
+ var h = q("svg"), i = "overflow:hidden;", j;
+ d = d || 0, e = e || 0, f = f || 512, g = g || 342, q(h, {height: g, version: 1.1, width: f, xmlns: "http://www.w3.org/2000/svg"}), c == 1 ? (h.style.cssText = i + "position:absolute;left:" + d + "px;top:" + e + "px", a._g.doc.body.appendChild(h), j = 1) : (h.style.cssText = i + "position:relative", c.firstChild ? c.insertBefore(h, c.firstChild) : c.appendChild(h)), c = new a._Paper, c.width = f, c.height = g, c.canvas = h, c.clear(), c._left = c._top = 0, j && (c.renderfix = function () {}), c.renderfix();
+ return c
+ }, a._engine.setViewBox = function (a, b, c, d, e) {
+ k("raphael.setViewBox", this, this._viewBox, [a, b, c, d, e]);
+ var f = g(c / this.width, d / this.height), h = this.top, i = e ? "meet" : "xMinYMin", j, l;
+ a == null ? (this._vbSize && (f = 1), delete this._vbSize, j = "0 0 " + this.width + m + this.height) : (this._vbSize = f, j = a + m + b + m + c + m + d), q(this.canvas, {viewBox: j, preserveAspectRatio: i});
+ while (f && h)l = "stroke-width"in h.attrs ? h.attrs["stroke-width"] : 1, h.attr({"stroke-width": l}), h._.dirty = 1, h._.dirtyT = 1, h = h.prev;
+ this._viewBox = [a, b, c, d, !!e];
+ return this
+ }, a.prototype.renderfix = function () {
+ var a = this.canvas, b = a.style, c;
+ try {c = a.getScreenCTM() || a.createSVGMatrix()} catch (d) {c = a.createSVGMatrix()}
+ var e = -c.e % 1, f = -c.f % 1;
+ if (e || f)e && (this._left = (this._left + e) % 1, b.left = this._left + "px"), f && (this._top = (this._top + f) % 1, b.top = this._top + "px")
+ }, a.prototype.clear = function () {
+ a.eve("raphael.clear", this);
+ var b = this.canvas;
+ while (b.firstChild)b.removeChild(b.firstChild);
+ this.bottom = this.top = null, (this.desc = q("desc")).appendChild(a._g.doc.createTextNode("Created with Raphaël " + a.version)), b.appendChild(this.desc), b.appendChild(this.defs = q("defs"))
+ }, a.prototype.remove = function () {
+ k("raphael.remove", this), this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
+ for (var b in this)this[b] = typeof this[b] == "function" ? a._removedFactory(b) : null
+ };
+ var B = a.st;
+ for (var C in A)A[b](C) && !B[b](C) && (B[C] = function (a) {
+ return function () {
+ var b = arguments;
+ return this.forEach(function (c) {c[a].apply(c, b)})
+ }
+ }(C))
+}(window.Raphael), window.Raphael.vml && function (a) {
+ var b = "hasOwnProperty", c = String, d = parseFloat, e = Math, f = e.round, g = e.max, h = e.min, i = e.abs, j = "fill", k = /[, ]+/, l = a.eve, m = " progid:DXImageTransform.Microsoft", n = " ", o = "", p = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"}, q = /([clmz]),?([^clmz]*)/gi, r = / progid:\S+Blur\([^\)]+\)/g, s = /-?[^,\s-]+/g, t = "position:absolute;left:0;top:0;width:1px;height:1px", u = 21600, v = {path: 1, rect: 1, image: 1}, w = {circle: 1, ellipse: 1}, x = function (b) {
+ var d = /[ahqstv]/ig, e = a._pathToAbsolute;
+ c(b).match(d) && (e = a._path2curve), d = /[clmz]/g;
+ if (e == a._pathToAbsolute && !c(b).match(d)) {
+ var g = c(b).replace(q, function (a, b, c) {
+ var d = [], e = b.toLowerCase() == "m", g = p[b];
+ c.replace(s, function (a) {e && d.length == 2 && (g += d + p[b == "m" ? "l" : "L"], d = []), d.push(f(a * u))});
+ return g + d
+ });
+ return g
+ }
+ var h = e(b), i, j;
+ g = [];
+ for (var k = 0, l = h.length; k < l; k++) {
+ i = h[k], j = h[k][0].toLowerCase(), j == "z" && (j = "x");
+ for (var m = 1, r = i.length; m < r; m++)j += f(i[m] * u) + (m != r - 1 ? "," : o);
+ g.push(j)
+ }
+ return g.join(n)
+ }, y = function (b, c, d) {
+ var e = a.matrix();
+ e.rotate(-b, .5, .5);
+ return{dx: e.x(c, d), dy: e.y(c, d)}
+ }, z = function (a, b, c, d, e, f) {
+ var g = a._, h = a.matrix, k = g.fillpos, l = a.node, m = l.style, o = 1, p = "", q, r = u / b, s = u / c;
+ m.visibility = "hidden";
+ if (!!b && !!c) {
+ l.coordsize = i(r) + n + i(s), m.rotation = f * (b * c < 0 ? -1 : 1);
+ if (f) {
+ var t = y(f, d, e);
+ d = t.dx, e = t.dy
+ }
+ b < 0 && (p += "x"), c < 0 && (p += " y") && (o = -1), m.flip = p, l.coordorigin = d * -r + n + e * -s;
+ if (k || g.fillsize) {
+ var v = l.getElementsByTagName(j);
+ v = v && v[0], l.removeChild(v), k && (t = y(f, h.x(k[0], k[1]), h.y(k[0], k[1])), v.position = t.dx * o + n + t.dy * o), g.fillsize && (v.size = g.fillsize[0] * i(b) + n + g.fillsize[1] * i(c)), l.appendChild(v)
+ }
+ m.visibility = "visible"
+ }
+ };
+ a.toString = function () {return"Your browser doesn’t support SVG. Falling down to VML.\nYou are running Raphaël " + this.version};
+ var A = function (a, b, d) {
+ var e = c(b).toLowerCase().split("-"), f = d ? "end" : "start", g = e.length, h = "classic", i = "medium", j = "medium";
+ while (g--)switch (e[g]) {
+ case"block":
+ case"classic":
+ case"oval":
+ case"diamond":
+ case"open":
+ case"none":
+ h = e[g];
+ break;
+ case"wide":
+ case"narrow":
+ j = e[g];
+ break;
+ case"long":
+ case"short":
+ i = e[g]
+ }
+ var k = a.node.getElementsByTagName("stroke")[0];
+ k[f + "arrow"] = h, k[f + "arrowlength"] = i, k[f + "arrowwidth"] = j
+ }, B = function (e, i) {
+ e.attrs = e.attrs || {};
+ var l = e.node, m = e.attrs, p = l.style, q, r = v[e.type] && (i.x != m.x || i.y != m.y || i.width != m.width || i.height != m.height || i.cx != m.cx || i.cy != m.cy || i.rx != m.rx || i.ry != m.ry || i.r != m.r), s = w[e.type] && (m.cx != i.cx || m.cy != i.cy || m.r != i.r || m.rx != i.rx || m.ry != i.ry), t = e;
+ for (var y in i)i[b](y) && (m[y] = i[y]);
+ r && (m.path = a._getPath[e.type](e), e._.dirty = 1), i.href && (l.href = i.href), i.title && (l.title = i.title), i.target && (l.target = i.target), i.cursor && (p.cursor = i.cursor), "blur"in i && e.blur(i.blur);
+ if (i.path && e.type == "path" || r)l.path = x(~c(m.path).toLowerCase().indexOf("r") ? a._pathToAbsolute(m.path) : m.path), e.type == "image" && (e._.fillpos = [m.x, m.y], e._.fillsize = [m.width, m.height], z(e, 1, 1, 0, 0, 0));
+ "transform"in i && e.transform(i.transform);
+ if (s) {
+ var B = +m.cx, D = +m.cy, E = +m.rx || +m.r || 0, G = +m.ry || +m.r || 0;
+ l.path = a.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", f((B - E) * u), f((D - G) * u), f((B + E) * u), f((D + G) * u), f(B * u))
+ }
+ if ("clip-rect"in i) {
+ var H = c(i["clip-rect"]).split(k);
+ if (H.length == 4) {
+ H[2] = +H[2] + +H[0], H[3] = +H[3] + +H[1];
+ var I = l.clipRect || a._g.doc.createElement("div"), J = I.style;
+ J.clip = a.format("rect({1}px {2}px {3}px {0}px)", H), l.clipRect || (J.position = "absolute", J.top = 0, J.left = 0, J.width = e.paper.width + "px", J.height = e.paper.height + "px", l.parentNode.insertBefore(I, l), I.appendChild(l), l.clipRect = I)
+ }
+ i["clip-rect"] || l.clipRect && (l.clipRect.style.clip = "auto")
+ }
+ if (e.textpath) {
+ var K = e.textpath.style;
+ i.font && (K.font = i.font), i["font-family"] && (K.fontFamily = '"' + i["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, o) + '"'), i["font-size"] && (K.fontSize = i["font-size"]), i["font-weight"] && (K.fontWeight = i["font-weight"]), i["font-style"] && (K.fontStyle = i["font-style"])
+ }
+ "arrow-start"in i && A(t, i["arrow-start"]), "arrow-end"in i && A(t, i["arrow-end"], 1);
+ if (i.opacity != null || i["stroke-width"] != null || i.fill != null || i.src != null || i.stroke != null || i["stroke-width"] != null || i["stroke-opacity"] != null || i["fill-opacity"] != null || i["stroke-dasharray"] != null || i["stroke-miterlimit"] != null || i["stroke-linejoin"] != null || i["stroke-linecap"] != null) {
+ var L = l.getElementsByTagName(j), M = !1;
+ L = L && L[0], !L && (M = L = F(j)), e.type == "image" && i.src && (L.src = i.src), i.fill && (L.on = !0);
+ if (L.on == null || i.fill == "none" || i.fill === null)L.on = !1;
+ if (L.on && i.fill) {
+ var N = c(i.fill).match(a._ISURL);
+ if (N) {
+ L.parentNode == l && l.removeChild(L), L.rotate = !0, L.src = N[1], L.type = "tile";
+ var O = e.getBBox(1);
+ L.position = O.x + n + O.y, e._.fillpos = [O.x, O.y], a._preload(N[1], function () {e._.fillsize = [this.offsetWidth, this.offsetHeight]})
+ } else L.color = a.getRGB(i.fill).hex, L.src = o, L.type = "solid", a.getRGB(i.fill).error && (t.type in{circle: 1, ellipse: 1} || c(i.fill).charAt() != "r") && C(t, i.fill, L) && (m.fill = "none", m.gradient = i.fill, L.rotate = !1)
+ }
+ if ("fill-opacity"in i || "opacity"in i) {
+ var P = ((+m["fill-opacity"] + 1 || 2) - 1) * ((+m.opacity + 1 || 2) - 1) * ((+a.getRGB(i.fill).o + 1 || 2) - 1);
+ P = h(g(P, 0), 1), L.opacity = P, L.src && (L.color = "none")
+ }
+ l.appendChild(L);
+ var Q = l.getElementsByTagName("stroke") && l.getElementsByTagName("stroke")[0], T = !1;
+ !Q && (T = Q = F("stroke"));
+ if (i.stroke && i.stroke != "none" || i["stroke-width"] || i["stroke-opacity"] != null || i["stroke-dasharray"] || i["stroke-miterlimit"] || i["stroke-linejoin"] || i["stroke-linecap"])Q.on = !0;
+ (i.stroke == "none" || i.stroke === null || Q.on == null || i.stroke == 0 || i["stroke-width"] == 0) && (Q.on = !1);
+ var U = a.getRGB(i.stroke);
+ Q.on && i.stroke && (Q.color = U.hex), P = ((+m["stroke-opacity"] + 1 || 2) - 1) * ((+m.opacity + 1 || 2) - 1) * ((+U.o + 1 || 2) - 1);
+ var V = (d(i["stroke-width"]) || 1) * .75;
+ P = h(g(P, 0), 1), i["stroke-width"] == null && (V = m["stroke-width"]), i["stroke-width"] && (Q.weight = V), V && V < 1 && (P *= V) && (Q.weight = 1), Q.opacity = P, i["stroke-linejoin"] && (Q.joinstyle = i["stroke-linejoin"] || "miter"), Q.miterlimit = i["stroke-miterlimit"] || 8, i["stroke-linecap"] && (Q.endcap = i["stroke-linecap"] == "butt" ? "flat" : i["stroke-linecap"] == "square" ? "square" : "round");
+ if (i["stroke-dasharray"]) {
+ var W = {"-": "shortdash", ".": "shortdot", "-.": "shortdashdot", "-..": "shortdashdotdot", ". ": "dot", "- ": "dash", "--": "longdash", "- .": "dashdot", "--.": "longdashdot", "--..": "longdashdotdot"};
+ Q.dashstyle = W[b](i["stroke-dasharray"]) ? W[i["stroke-dasharray"]] : o
+ }
+ T && l.appendChild(Q)
+ }
+ if (t.type == "text") {
+ t.paper.canvas.style.display = o;
+ var X = t.paper.span, Y = 100, Z = m.font && m.font.match(/\d+(?:\.\d*)?(?=px)/);
+ p = X.style, m.font && (p.font = m.font), m["font-family"] && (p.fontFamily = m["font-family"]), m["font-weight"] && (p.fontWeight = m["font-weight"]), m["font-style"] && (p.fontStyle = m["font-style"]), Z = d(m["font-size"] || Z && Z[0]) || 10, p.fontSize = Z * Y + "px", t.textpath.string && (X.innerHTML = c(t.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>"));
+ var $ = X.getBoundingClientRect();
+ t.W = m.w = ($.right - $.left) / Y, t.H = m.h = ($.bottom - $.top) / Y, t.X = m.x, t.Y = m.y + t.H / 2, ("x"in i || "y"in i) && (t.path.v = a.format("m{0},{1}l{2},{1}", f(m.x * u), f(m.y * u), f(m.x * u) + 1));
+ var _ = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"];
+ for (var ba = 0, bb = _.length; ba < bb; ba++)if (_[ba]in i) {
+ t._.dirty = 1;
+ break
+ }
+ switch (m["text-anchor"]) {
+ case"start":
+ t.textpath.style["v-text-align"] = "left", t.bbx = t.W / 2;
+ break;
+ case"end":
+ t.textpath.style["v-text-align"] = "right", t.bbx = -t.W / 2;
+ break;
+ default:
+ t.textpath.style["v-text-align"] = "center", t.bbx = 0
+ }
+ t.textpath.style["v-text-kern"] = !0
+ }
+ }, C = function (b, f, g) {
+ b.attrs = b.attrs || {};
+ var h = b.attrs, i = Math.pow, j, k, l = "linear", m = ".5 .5";
+ b.attrs.gradient = f, f = c(f).replace(a._radial_gradient, function (a, b, c) {
+ l = "radial", b && c && (b = d(b), c = d(c), i(b - .5, 2) + i(c - .5, 2) > .25 && (c = e.sqrt(.25 - i(b - .5, 2)) * ((c > .5) * 2 - 1) + .5), m = b + n + c);
+ return o
+ }), f = f.split(/\s*\-\s*/);
+ if (l == "linear") {
+ var p = f.shift();
+ p = -d(p);
+ if (isNaN(p))return null
+ }
+ var q = a._parseDots(f);
+ if (!q)return null;
+ b = b.shape || b.node;
+ if (q.length) {
+ b.removeChild(g), g.on = !0, g.method = "none", g.color = q[0].color, g.color2 = q[q.length - 1].color;
+ var r = [];
+ for (var s = 0, t = q.length; s < t; s++)q[s].offset && r.push(q[s].offset + n + q[s].color);
+ g.colors = r.length ? r.join() : "0% " + g.color, l == "radial" ? (g.type = "gradientTitle", g.focus = "100%", g.focussize = "0 0", g.focusposition = m, g.angle = 0) : (g.type = "gradient", g.angle = (270 - p) % 360), b.appendChild(g)
+ }
+ return 1
+ }, D = function (b, c) {this[0] = this.node = b, b.raphael = !0, this.id = a._oid++, b.raphaelid = this.id, this.X = 0, this.Y = 0, this.attrs = {}, this.paper = c, this.matrix = a.matrix(), this._ = {transform: [], sx: 1, sy: 1, dx: 0, dy: 0, deg: 0, dirty: 1, dirtyT: 1}, !c.bottom && (c.bottom = this), this.prev = c.top, c.top && (c.top.next = this), c.top = this, this.next = null}, E = a.el;
+ D.prototype = E, E.constructor = D, E.transform = function (b) {
+ if (b == null)return this._.transform;
+ var d = this.paper._viewBoxShift, e = d ? "s" + [d.scale, d.scale] + "-1-1t" + [d.dx, d.dy] : o, f;
+ d && (f = b = c(b).replace(/\.{3}|\u2026/g, this._.transform || o)), a._extractTransform(this, e + b);
+ var g = this.matrix.clone(), h = this.skew, i = this.node, j, k = ~c(this.attrs.fill).indexOf("-"), l = !c(this.attrs.fill).indexOf("url(");
+ g.translate(-0.5, -0.5);
+ if (l || k || this.type == "image") {
+ h.matrix = "1 0 0 1", h.offset = "0 0", j = g.split();
+ if (k && j.noRotation || !j.isSimple) {
+ i.style.filter = g.toFilter();
+ var m = this.getBBox(), p = this.getBBox(1), q = m.x - p.x, r = m.y - p.y;
+ i.coordorigin = q * -u + n + r * -u, z(this, 1, 1, q, r, 0)
+ } else i.style.filter = o, z(this, j.scalex, j.scaley, j.dx, j.dy, j.rotate)
+ } else i.style.filter = o, h.matrix = c(g), h.offset = g.offset();
+ f && (this._.transform = f);
+ return this
+ }, E.rotate = function (a, b, e) {
+ if (this.removed)return this;
+ if (a != null) {
+ a = c(a).split(k), a.length - 1 && (b = d(a[1]), e = d(a[2])), a = d(a[0]), e == null && (b = e);
+ if (b == null || e == null) {
+ var f = this.getBBox(1);
+ b = f.x + f.width / 2, e = f.y + f.height / 2
+ }
+ this._.dirtyT = 1, this.transform(this._.transform.concat([
+ ["r", a, b, e]
+ ]));
+ return this
+ }
+ }, E.translate = function (a, b) {
+ if (this.removed)return this;
+ a = c(a).split(k), a.length - 1 && (b = d(a[1])), a = d(a[0]) || 0, b = +b || 0, this._.bbox && (this._.bbox.x += a, this._.bbox.y += b), this.transform(this._.transform.concat([
+ ["t", a, b]
+ ]));
+ return this
+ }, E.scale = function (a, b, e, f) {
+ if (this.removed)return this;
+ a = c(a).split(k), a.length - 1 && (b = d(a[1]), e = d(a[2]), f = d(a[3]), isNaN(e) && (e = null), isNaN(f) && (f = null)), a = d(a[0]), b == null && (b = a), f == null && (e = f);
+ if (e == null || f == null)var g = this.getBBox(1);
+ e = e == null ? g.x + g.width / 2 : e, f = f == null ? g.y + g.height / 2 : f, this.transform(this._.transform.concat([
+ ["s", a, b, e, f]
+ ])), this._.dirtyT = 1;
+ return this
+ }, E.hide = function () {
+ !this.removed && (this.node.style.display = "none");
+ return this
+ }, E.show = function () {
+ !this.removed && (this.node.style.display = o);
+ return this
+ }, E._getBBox = function () {
+ if (this.removed)return{};
+ return{x: this.X + (this.bbx || 0) - this.W / 2, y: this.Y - this.H, width: this.W, height: this.H}
+ }, E.remove = function () {
+ if (!this.removed && !!this.node.parentNode) {
+ this.paper.__set__ && this.paper.__set__.exclude(this), a.eve.unbind("raphael.*.*." + this.id), a._tear(this, this.paper), this.node.parentNode.removeChild(this.node), this.shape && this.shape.parentNode.removeChild(this.shape);
+ for (var b in this)this[b] = typeof this[b] == "function" ? a._removedFactory(b) : null;
+ this.removed = !0
+ }
+ }, E.attr = function (c, d) {
+ if (this.removed)return this;
+ if (c == null) {
+ var e = {};
+ for (var f in this.attrs)this.attrs[b](f) && (e[f] = this.attrs[f]);
+ e.gradient && e.fill == "none" && (e.fill = e.gradient) && delete e.gradient, e.transform = this._.transform;
+ return e
+ }
+ if (d == null && a.is(c, "string")) {
+ if (c == j && this.attrs.fill == "none" && this.attrs.gradient)return this.attrs.gradient;
+ var g = c.split(k), h = {};
+ for (var i = 0, m = g.length; i < m; i++)c = g[i], c in this.attrs ? h[c] = this.attrs[c] : a.is(this.paper.customAttributes[c], "function") ? h[c] = this.paper.customAttributes[c].def : h[c] = a._availableAttrs[c];
+ return m - 1 ? h : h[g[0]]
+ }
+ if (this.attrs && d == null && a.is(c, "array")) {
+ h = {};
+ for (i = 0, m = c.length; i < m; i++)h[c[i]] = this.attr(c[i]);
+ return h
+ }
+ var n;
+ d != null && (n = {}, n[c] = d), d == null && a.is(c, "object") && (n = c);
+ for (var o in n)l("raphael.attr." + o + "." + this.id, this, n[o]);
+ if (n) {
+ for (o in this.paper.customAttributes)if (this.paper.customAttributes[b](o) && n[b](o) && a.is(this.paper.customAttributes[o], "function")) {
+ var p = this.paper.customAttributes[o].apply(this, [].concat(n[o]));
+ this.attrs[o] = n[o];
+ for (var q in p)p[b](q) && (n[q] = p[q])
+ }
+ n.text && this.type == "text" && (this.textpath.string = n.text), B(this, n)
+ }
+ return this
+ }, E.toFront = function () {
+ !this.removed && this.node.parentNode.appendChild(this.node), this.paper && this.paper.top != this && a._tofront(this, this.paper);
+ return this
+ }, E.toBack = function () {
+ if (this.removed)return this;
+ this.node.parentNode.firstChild != this.node && (this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild), a._toback(this, this.paper));
+ return this
+ }, E.insertAfter = function (b) {
+ if (this.removed)return this;
+ b.constructor == a.st.constructor && (b = b[b.length - 1]), b.node.nextSibling ? b.node.parentNode.insertBefore(this.node, b.node.nextSibling) : b.node.parentNode.appendChild(this.node), a._insertafter(this, b, this.paper);
+ return this
+ }, E.insertBefore = function (b) {
+ if (this.removed)return this;
+ b.constructor == a.st.constructor && (b = b[0]), b.node.parentNode.insertBefore(this.node, b.node), a._insertbefore(this, b, this.paper);
+ return this
+ }, E.blur = function (b) {
+ var c = this.node.runtimeStyle, d = c.filter;
+ d = d.replace(r, o), +b !== 0 ? (this.attrs.blur = b, c.filter = d + n + m + ".Blur(pixelradius=" + (+b || 1.5) + ")", c.margin = a.format("-{0}px 0 0 -{0}px", f(+b || 1.5))) : (c.filter = d, c.margin = 0, delete this.attrs.blur)
+ }, a._engine.path = function (a, b) {
+ var c = F("shape");
+ c.style.cssText = t, c.coordsize = u + n + u, c.coordorigin = b.coordorigin;
+ var d = new D(c, b), e = {fill: "none", stroke: "#000"};
+ a && (e.path = a), d.type = "path", d.path = [], d.Path = o, B(d, e), b.canvas.appendChild(c);
+ var f = F("skew");
+ f.on = !0, c.appendChild(f), d.skew = f, d.transform(o);
+ return d
+ }, a._engine.rect = function (b, c, d, e, f, g) {
+ var h = a._rectPath(c, d, e, f, g), i = b.path(h), j = i.attrs;
+ i.X = j.x = c, i.Y = j.y = d, i.W = j.width = e, i.H = j.height = f, j.r = g, j.path = h, i.type = "rect";
+ return i
+ }, a._engine.ellipse = function (a, b, c, d, e) {
+ var f = a.path(), g = f.attrs;
+ f.X = b - d, f.Y = c - e, f.W = d * 2, f.H = e * 2, f.type = "ellipse", B(f, {cx: b, cy: c, rx: d, ry: e});
+ return f
+ }, a._engine.circle = function (a, b, c, d) {
+ var e = a.path(), f = e.attrs;
+ e.X = b - d, e.Y = c - d, e.W = e.H = d * 2, e.type = "circle", B(e, {cx: b, cy: c, r: d});
+ return e
+ }, a._engine.image = function (b, c, d, e, f, g) {
+ var h = a._rectPath(d, e, f, g), i = b.path(h).attr({stroke: "none"}), k = i.attrs, l = i.node, m = l.getElementsByTagName(j)[0];
+ k.src = c, i.X = k.x = d, i.Y = k.y = e, i.W = k.width = f, i.H = k.height = g, k.path = h, i.type = "image", m.parentNode == l && l.removeChild(m), m.rotate = !0, m.src = c, m.type = "tile", i._.fillpos = [d, e], i._.fillsize = [f, g], l.appendChild(m), z(i, 1, 1, 0, 0, 0);
+ return i
+ }, a._engine.text = function (b, d, e, g) {
+ var h = F("shape"), i = F("path"), j = F("textpath");
+ d = d || 0, e = e || 0, g = g || "", i.v = a.format("m{0},{1}l{2},{1}", f(d * u), f(e * u), f(d * u) + 1), i.textpathok = !0, j.string = c(g), j.on = !0, h.style.cssText = t, h.coordsize = u + n + u, h.coordorigin = "0 0";
+ var k = new D(h, b), l = {fill: "#000", stroke: "none", font: a._availableAttrs.font, text: g};
+ k.shape = h, k.path = i, k.textpath = j, k.type = "text", k.attrs.text = c(g), k.attrs.x = d, k.attrs.y = e, k.attrs.w = 1, k.attrs.h = 1, B(k, l), h.appendChild(j), h.appendChild(i), b.canvas.appendChild(h);
+ var m = F("skew");
+ m.on = !0, h.appendChild(m), k.skew = m, k.transform(o);
+ return k
+ }, a._engine.setSize = function (b, c) {
+ var d = this.canvas.style;
+ this.width = b, this.height = c, b == +b && (b += "px"), c == +c && (c += "px"), d.width = b, d.height = c, d.clip = "rect(0 " + b + " " + c + " 0)", this._viewBox && a._engine.setViewBox.apply(this, this._viewBox);
+ return this
+ }, a._engine.setViewBox = function (b, c, d, e, f) {
+ a.eve("raphael.setViewBox", this, this._viewBox, [b, c, d, e, f]);
+ var h = this.width, i = this.height, j = 1 / g(d / h, e / i), k, l;
+ f && (k = i / e, l = h / d, d * k < h && (b -= (h - d * k) / 2 / k), e * l < i && (c -= (i - e * l) / 2 / l)), this._viewBox = [b, c, d, e, !!f], this._viewBoxShift = {dx: -b, dy: -c, scale: j}, this.forEach(function (a) {a.transform("...")});
+ return this
+ };
+ var F;
+ a._engine.initWin = function (a) {
+ var b = a.document;
+ b.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
+ try {!b.namespaces.rvml && b.namespaces.add("rvml", "urn:schemas-microsoft-com:vml"), F = function (a) {return b.createElement("<rvml:" + a + ' class="rvml">')}} catch (c) {F = function (a) {return b.createElement("<" + a + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}
+ }, a._engine.initWin(a._g.win), a._engine.create = function () {
+ var b = a._getContainer.apply(0, arguments), c = b.container, d = b.height, e, f = b.width, g = b.x, h = b.y;
+ if (!c)throw new Error("VML container not found.");
+ var i = new a._Paper, j = i.canvas = a._g.doc.createElement("div"), k = j.style;
+ g = g || 0, h = h || 0, f = f || 512, d = d || 342, i.width = f, i.height = d, f == +f && (f += "px"), d == +d && (d += "px"), i.coordsize = u * 1e3 + n + u * 1e3, i.coordorigin = "0 0", i.span = a._g.doc.createElement("span"), i.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;", j.appendChild(i.span), k.cssText = a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", f, d), c == 1 ? (a._g.doc.body.appendChild(j), k.left = g + "px", k.top = h + "px", k.position = "absolute") : c.firstChild ? c.insertBefore(j, c.firstChild) : c.appendChild(j), i.renderfix = function () {};
+ return i
+ }, a.prototype.clear = function () {a.eve("raphael.clear", this), this.canvas.innerHTML = o, this.span = a._g.doc.createElement("span"), this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;", this.canvas.appendChild(this.span), this.bottom = this.top = null}, a.prototype.remove = function () {
+ a.eve("raphael.remove", this), this.canvas.parentNode.removeChild(this.canvas);
+ for (var b in this)this[b] = typeof this[b] == "function" ? a._removedFactory(b) : null;
+ return!0
+ };
+ var G = a.st;
+ for (var H in E)E[b](H) && !G[b](H) && (G[H] = function (a) {
+ return function () {
+ var b = arguments;
+ return this.forEach(function (c) {c[a].apply(c, b)})
+ }
+ }(H))
+}(window.Raphael); \ No newline at end of file
diff --git a/plugins/UserCountryMap/js/visitor-map.js b/plugins/UserCountryMap/js/visitor-map.js
index 1a69b5caad..190da458e3 100644
--- a/plugins/UserCountryMap/js/visitor-map.js
+++ b/plugins/UserCountryMap/js/visitor-map.js
@@ -8,14 +8,14 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-(function() {
+(function () {
// create a global namespace for UserCountryMap plugin
// this is used both by visitor map and realtime map
window.UserCountryMap = window.UserCountryMap || {};
// the main class for this widget, provides the interface for the template
- var VisitorMap = window.UserCountryMap.VisitorMap = function(config, theWidget) {
+ var VisitorMap = window.UserCountryMap.VisitorMap = function (config, theWidget) {
this.config = config;
this.theWidget = theWidget || false;
this.run();
@@ -26,7 +26,7 @@
/*
* initializes the map after widget creation
*/
- run: function() {
+ run: function () {
var self = this,
config = self.config;
@@ -76,7 +76,7 @@
function ajax(params, dataType) {
dataType = dataType || 'json';
params = $.extend({}, params);
- var token_auth = ''+params.token_auth;
+ var token_auth = '' + params.token_auth;
delete params['token_auth'];
return $.ajax({
url: 'index.php?' + $.param(params),
@@ -87,22 +87,22 @@
}
function minmax(values) {
- values = values.sort(function(a,b) { return Number(a) - Number(b); });
+ values = values.sort(function (a, b) { return Number(a) - Number(b); });
return {
min: values[0],
- max: values[values.length-1],
- median: values[Math.floor(values.length*0.5)],
- p33: values[Math.floor(values.length*0.33)],
- p66: values[Math.floor(values.length*0.66)],
- p90: values[Math.floor(values.length*0.9)]
+ max: values[values.length - 1],
+ median: values[Math.floor(values.length * 0.5)],
+ p33: values[Math.floor(values.length * 0.33)],
+ p66: values[Math.floor(values.length * 0.66)],
+ p90: values[Math.floor(values.length * 0.9)]
};
}
function formatNumber(v) {
v = Number(v);
- return v > 1000000 ? (v/1000000).toFixed(1) + 'm' :
- v > 1000 ? (v/1000).toFixed(1) + 'k' :
- v;
+ return v > 1000000 ? (v / 1000000).toFixed(1) + 'm' :
+ v > 1000 ? (v / 1000).toFixed(1) + 'k' :
+ v;
}
//
@@ -113,8 +113,8 @@
//
function formatValueForTooltips(data, metric, id) {
- var val = data[metric] % 1 === 0 || Number(data[metric]) != data[metric] ? data[metric] : data[metric].toFixed(1),
- v = _[metric].replace('%s', '<b>'+val+'</b>');
+ var val = data[metric] % 1 === 0 || Number(data[metric]) != data[metric] ? data[metric] : data[metric].toFixed(1),
+ v = _[metric].replace('%s', '<b>' + val + '</b>');
if (val == 1 && metric == 'nb_visits') v = _.one_visit;
@@ -126,17 +126,17 @@
else if (id == 'world') total = _worldTotal;
else {
total = 0;
- $.each(UserCountryMap.countriesByIso, function(iso, country) {
+ $.each(UserCountryMap.countriesByIso, function (iso, country) {
if (UserCountryMap.ISO3toCONT[iso] == id) {
total += country[metric];
}
});
}
if (total) {
- v += ' ('+formatPercentage(data[metric] / total)+')';
+ v += ' (' + formatPercentage(data[metric] / total) + ')';
}
} else if (metric == 'avg_time_on_site') {
- v += '<br/> (over '+data.nb_visits+' visits)';
+ v += '<br/> (over ' + data.nb_visits + ' visits)';
}
return v;
}
@@ -150,7 +150,7 @@
metric = $$('.userCountryMapSelectMetrics').val(),
v = formatNumber(Math.round(val)) + (metric == 'avg_time_on_site' ? first ? ' sec' : 's' : '');
d.css({ width: 17, height: 17, float: 'left', background: colscale(val) });
- l.css({ 'margin-left':20, 'line-height': '20px', 'text-align': 'right' }).html(v);
+ l.css({ 'margin-left': 20, 'line-height': '20px', 'text-align': 'right' }).html(v);
r.css({ clear: 'both', height: 19 });
r.append(d).append(l);
$('.UserCountryMap-legend .content').append(r);
@@ -158,7 +158,7 @@
var stats, values = [], id = self.lastSelected, c;
- $.each(rows, function(i, r) {
+ $.each(rows, function (i, r) {
if (!$.isFunction(filter) || filter(r)) {
var v = quantify(r, metric);
if (!isNaN(v)) values.push(v);
@@ -168,7 +168,7 @@
stats = minmax(values);
if (stats.min == stats.max) {
- colscale = function() { return chroma.hex('#CDDAEF'); };
+ colscale = function () { return chroma.hex('#CDDAEF'); };
if (choropleth) {
$('.UserCountryMap-legend .content').html('').show();
addLegendItem(stats.min, true);
@@ -184,7 +184,7 @@
if (metric == 'avg_time_on_site' || metric == 'nb_actions_per_visit' || metric == 'bounce_rate') {
if (id.length == 3) {
c = (stats.p90 - stats.min) / (stats.max - stats.min);
- colscale = chroma.scale(['#385993', '#385993','#E87500', '#E87500'], [0, c, c+0.001, 1])
+ colscale = chroma.scale(['#385993', '#385993', '#E87500', '#E87500'], [0, c, c + 0.001, 1])
.domain(chroma.limits(rows, 'c', 5, 'curMetric', filter))
.mode('hsl');
}
@@ -194,7 +194,7 @@
if (choropleth) {
$('.UserCountryMap-legend .content').html('').show();
var itemExists = {};
- $.each(chroma.limits(values, 'k', 3), function(i, v) {
+ $.each(chroma.limits(values, 'k', 3), function (i, v) {
if (itemExists[v]) return;
addLegendItem(v, i === 0);
itemExists[v] = true;
@@ -210,7 +210,7 @@
function formatPercentage(val) {
if (val < 0.001) return '< 0.1%';
- return Math.round(1000 * val)/10 + '%';
+ return Math.round(1000 * val) / 10 + '%';
}
/*
@@ -231,7 +231,7 @@
function initUserInterface() {
// react to changes of country select
- $$('.userCountryMapSelectCountry').off('change').change(function() {
+ $$('.userCountryMapSelectCountry').off('change').change(function () {
updateState($$('.userCountryMapSelectCountry').val());
});
@@ -252,13 +252,13 @@
$(window).off('resize').resize(onResizeLazy);
// enable metric changes
- $$('.userCountryMapSelectMetrics').off('change').change(function() {
+ $$('.userCountryMapSelectMetrics').off('change').change(function () {
updateState(self.lastSelected);
});
// handle city button
- (function(btn) {
- btn.off('click').click(function() {
+ (function (btn) {
+ btn.off('click').click(function () {
if (self.lastSelected.length == 3) {
if (self.mode != "city") {
self.mode = "city";
@@ -269,8 +269,8 @@
})($$('.UserCountryMap-btn-city'));
// handle region button
- (function(btn) {
- btn.off('click').click(function() {
+ (function (btn) {
+ btn.off('click').click(function () {
if (self.mode != "region") {
$$('.UserCountryMap-view-mode-buttons a').removeClass('activeIcon');
self.mode = "region";
@@ -286,11 +286,11 @@
$$('.UserCountryMap_map').append(bl);
var infobtn = $('.UserCountryMap-info-btn');
- infobtn.off('mouseenter').on('mouseenter', function(e) {
+ infobtn.off('mouseenter').on('mouseenter',function (e) {
$(infobtn.data('tooltip-target')).show();
- }).off('mouseleave').on('mouseleave', function(e) {
- $(infobtn.data('tooltip-target')).hide();
- });
+ }).off('mouseleave').on('mouseleave', function (e) {
+ $(infobtn.data('tooltip-target')).hide();
+ });
$('.UserCountryMap-tooltip').hide();
}
@@ -358,7 +358,7 @@
if (id.length == 3) {
if (UserCountryMap.countriesByIso[id]) { // we have visits in this country
flag.css({
- 'background-image': 'url('+UserCountryMap.countriesByIso[id].flag+')',
+ 'background-image': 'url(' + UserCountryMap.countriesByIso[id].flag + ')',
'background-repeat': 'no-repeat',
'background-position': '5px 5px'
});
@@ -382,16 +382,16 @@
var mapTitle = id.length == 3 ?
UserCountryMap.countriesByIso[id].name :
- $$('.userCountryMapSelectCountry option[value='+id+']').html(),
+ $$('.userCountryMapSelectCountry option[value=' + id + ']').html(),
totalVisits = 0;
// update map title
$('.map-title').html(mapTitle);
- $$('.widgetUserCountryMapvisitorMap .widgetName .map-title').html(' – '+mapTitle);
+ $$('.widgetUserCountryMapvisitorMap .widgetName .map-title').html(' – ' + mapTitle);
// update total visits for that region
if (id.length == 3) {
totalVisits = UserCountryMap.countriesByIso[id]['nb_visits'];
} else if (id.length == 2) {
- $.each(UserCountryMap.countriesByIso, function(iso, country) {
+ $.each(UserCountryMap.countriesByIso, function (iso, country) {
if (UserCountryMap.ISO3toCONT[iso] == id) {
totalVisits += country['nb_visits'];
}
@@ -404,8 +404,8 @@
$('.map-stats').html(formatValueForTooltips(UserCountryMap.countriesByIso[id], metric, 'world'));
} else {
$('.map-stats').html(
- _.nb_visits.replace('%s', '<b>'+formatNumber(totalVisits) + '</b>') +(id != 'world' ? ' ('+
- formatPercentage(totalVisits / worldTotalVisits)+')' : '')
+ _.nb_visits.replace('%s', '<b>' + formatNumber(totalVisits) + '</b>') + (id != 'world' ? ' (' +
+ formatPercentage(totalVisits / worldTotalVisits) + ')' : '')
);
}
}
@@ -422,7 +422,7 @@
// Create a chroma ColorScale for the selected metric that regards only the
// countries that are visible in the map.
- colscale = getColorScale(UserCountryMap.countryData, metric, function(r) {
+ colscale = getColorScale(UserCountryMap.countryData, metric, function (r) {
if (target.length == 2) {
return UserCountryMap.ISO3toCONT[r.iso] == target;
} else {
@@ -441,23 +441,23 @@
// Apply the color scale to the map.
map.getLayer('countries')
- .style('fill', countryFill)
- .on('mouseenter', function(d, path, evt) {
+ .style('fill', countryFill)
+ .on('mouseenter', function (d, path, evt) {
if (evt.shiftKey) { // highlight on mouseover with shift pressed
path.attr('fill', '#f4f45b');
}
})
- .on('mouseleave', function(d, path, evt) {
+ .on('mouseleave', function (d, path, evt) {
if ($.inArray(UserCountryMap.countriesByIso[d.iso].name, _rowEvolution.labels) == -1) {
path.attr('fill', countryFill(d)); // reset color
}
});
// Update the map tooltips.
- map.getLayer('countries').tooltips(function(data) {
+ map.getLayer('countries').tooltips(function (data) {
var metric = $$('.userCountryMapSelectMetrics').val(),
country = UserCountryMap.countriesByIso[data.iso];
- return '<h3>'+country.name + '</h3>'+
+ return '<h3>' + country.name + '</h3>' +
formatValueForTooltips(country, metric, target);
});
}
@@ -470,32 +470,32 @@
}
// otherwise we need to load another map svg
- _updateMap(target + '.svg', function() {
+ _updateMap(target + '.svg', function () {
// add a layer for non-selectable countries = for which no data is
// defined in the current report
map.addLayer('countries', {
name: 'context',
- filter: function(pd) {
+ filter: function (pd) {
return UserCountryMap.countriesByIso[pd.iso] === undefined;
},
- tooltips: function(pd) {
- return '<h3>'+pd.name+'</h3>' + _.no_visit;
+ tooltips: function (pd) {
+ return '<h3>' + pd.name + '</h3>' + _.no_visit;
}
});
// add a layer for selectable countries = for which we have data
// available in the current report
- map.addLayer('countries', { name: 'countryBG', filter: function(pd) {
+ map.addLayer('countries', { name: 'countryBG', filter: function (pd) {
return UserCountryMap.countriesByIso[pd.iso] !== undefined;
}});
map.addLayer('countries', {
key: 'iso',
- filter: function(pd) {
+ filter: function (pd) {
return UserCountryMap.countriesByIso[pd.iso] !== undefined;
},
- click: function(data, path, evt) {
+ click: function (data, path, evt) {
evt.stopPropagation();
if (evt.shiftKey || _rowEvolution.labels.length) {
if (evt.altKey) {
@@ -526,7 +526,7 @@
* updateMap is called by renderCountryMap() and renderWorldMap()
*/
function _updateMap(svgUrl, callback) {
- map.loadMap(config.svgBasePath + svgUrl, function() {
+ map.loadMap(config.svgBasePath + svgUrl, function () {
map.clear();
self.resize();
@@ -576,7 +576,7 @@
function aggregate(rows, groupBy) {
var groups = {};
- $.each(rows, function(i, row) {
+ $.each(rows, function (i, row) {
var g_id = groupBy ? groupBy(row) : 'X';
g_id = g_id === true ? $.isNumeric(i) && i === Number(i) ? false : i : g_id;
if (g_id) {
@@ -588,19 +588,19 @@
bounce_count: 0
};
}
- $.each(groups[g_id], function(metric) {
+ $.each(groups[g_id], function (metric) {
groups[g_id][metric] += row[metric];
});
}
});
- $.each(groups, function(g_id, group) {
+ $.each(groups, function (g_id, group) {
var apv = group.nb_actions / group.nb_visits,
ats = group.sum_visit_length / group.nb_visits,
br = (group.bounce_count * 100 / group.bounce_count);
group['nb_actions_per_visit'] = apv;
- group['avg_time_on_site'] = new Date(0,0,0,ats / 3600, ats % 3600 / 60, ats % 60).toLocaleTimeString();
- group['bounce_rate'] = (br % 1 !== 0 ? br.toFixed(1) : br)+"%";
+ group['avg_time_on_site'] = new Date(0, 0, 0, ats / 3600, ats % 3600 / 60, ats % 60).toLocaleTimeString();
+ group['bounce_rate'] = (br % 1 !== 0 ? br.toFixed(1) : br) + "%";
});
return groupBy ? groups : groups.X;
@@ -610,7 +610,7 @@
$('.unlocated-stats').html(
$('.unlocated-stats').data('tpl')
.replace('%s', unlocated)
- .replace('%p', '('+formatPercentage(unlocated/total)+')')
+ .replace('%p', '(' + formatPercentage(unlocated / total) + ')')
.replace('%c', UserCountryMap.countriesByIso[self.lastSelected].name)
);
$('.UserCountryMap-info-btn').show();
@@ -636,120 +636,120 @@
indicateLoading();
// load data from Piwik API
ajax(_reportParams('UserCountry', 'getRegion', UserCountryMap.countriesByIso[iso].iso2))
- .done(function(data) {
+ .done(function (data) {
- loadingComplete();
+ loadingComplete();
- var regionDict = {},
- totalCountryVisits = UserCountryMap.countriesByIso[iso].nb_visits,
- unlocated = totalCountryVisits;
- // self.lastReportMetricStats = {};
+ var regionDict = {},
+ totalCountryVisits = UserCountryMap.countriesByIso[iso].nb_visits,
+ unlocated = totalCountryVisits;
+ // self.lastReportMetricStats = {};
- function regionCode(region) {
- var key = UserCountryMap.keys[iso] || 'fips';
- return key.substr(0,4) == "fips" ? region[key].substr(2) : region[key]; // cut first two letters from fips code (=country code)
- }
+ function regionCode(region) {
+ var key = UserCountryMap.keys[iso] || 'fips';
+ return key.substr(0, 4) == "fips" ? region[key].substr(2) : region[key]; // cut first two letters from fips code (=country code)
+ }
- function regionExistsInMap(code) {
- var key = UserCountryMap.keys[iso] || 'fips', q = {};
- q[key] = key.substr(0,4) == 'fips' ? UserCountryMap.countriesByIso[iso].fips + code : code;
- if (map.getLayer('regions').getPaths(q).length === 0) {
- return false;
+ function regionExistsInMap(code) {
+ var key = UserCountryMap.keys[iso] || 'fips', q = {};
+ q[key] = key.substr(0, 4) == 'fips' ? UserCountryMap.countriesByIso[iso].fips + code : code;
+ if (map.getLayer('regions').getPaths(q).length === 0) {
+ return false;
+ }
+ return true;
}
- return true;
- }
- $.each(data.reportData, function(i, row) {
- regionDict[data.reportMetadata[i].region] = $.extend(row, data.reportMetadata[i], {
- curMetric: quantify(row, metric)
+ $.each(data.reportData, function (i, row) {
+ regionDict[data.reportMetadata[i].region] = $.extend(row, data.reportMetadata[i], {
+ curMetric: quantify(row, metric)
+ });
});
- });
- var metric = $$('.userCountryMapSelectMetrics').val();
+ var metric = $$('.userCountryMapSelectMetrics').val();
- if (UserCountryMap.aggregate[iso]) {
- var aggregated = aggregate(regionDict, function(row) {
- var id = row.region, res = false;
- $.each(UserCountryMap.aggregate[iso].groups, function(group, codes) {
- if ($.inArray(id, codes) > -1) {
- res = group;
- }
+ if (UserCountryMap.aggregate[iso]) {
+ var aggregated = aggregate(regionDict, function (row) {
+ var id = row.region, res = false;
+ $.each(UserCountryMap.aggregate[iso].groups, function (group, codes) {
+ if ($.inArray(id, codes) > -1) {
+ res = group;
+ }
+ });
+ return res;
});
- return res;
- });
- //if (!UserCountryMap.aggregate.partial) regionDict = {};
- $.each(aggregated, function(id, group) {
- group.curMetric = quantify(group, metric);
- regionDict[id] = group;
+ //if (!UserCountryMap.aggregate.partial) regionDict = {};
+ $.each(aggregated, function (id, group) {
+ group.curMetric = quantify(group, metric);
+ regionDict[id] = group;
+ });
+ }
+
+ $.each(regionDict, function (key, region) {
+ if (regionExistsInMap(key)) unlocated -= region.nb_visits;
});
- }
+ displayUnlocatableCount(unlocated, totalCountryVisits);
- $.each(regionDict, function(key, region) {
- if (regionExistsInMap(key)) unlocated -= region.nb_visits;
- });
- displayUnlocatableCount(unlocated, totalCountryVisits);
+ // create color scale
+ colscale = getColorScale(regionDict, 'curMetric', null, true);
- // create color scale
- colscale = getColorScale(regionDict, 'curMetric', null, true);
+ function regionFill(data) {
+ var code = regionCode(data);
+ return regionDict[code] === undefined ? '#fff' : colscale(regionDict[code].curMetric);
+ }
- function regionFill(data) {
- var code = regionCode(data);
- return regionDict[code] === undefined ? '#fff' : colscale(regionDict[code].curMetric);
- }
+ // apply colors to map
+ map.getLayer('regions')
+ .style('fill', regionFill)
+ .style('stroke',function (data) {
+ return regionDict[regionCode(data)] === undefined ? '#bbb' : '#3C6FB6';
+ }).sort(function (data) {
+ var code = regionCode(data);
+ return regionDict[code] === undefined ? -1 : regionDict[code].curMetric;
+ }).tooltips(function (data) {
+ var metric = $$('.userCountryMapSelectMetrics').val(),
+ region = regionDict[regionCode(data)];
+ if (region === undefined) {
+ return '<h3>' + data.name + '</h3><p>' + _.nb_visits.replace('%s', '<b>0</b>') + '</p>';
+ }
+ return '<h3>' + data.name + '</h3>' +
+ formatValueForTooltips(region, metric, iso);
+ }).on('click',function (d, path, evt) {
+ var region = regionDict[regionCode(d)];
+ if (region && region.label) {
+ if (evt.shiftKey) {
+ path.attr('fill', '#f4f45b');
+ addMultipleRowEvolution('getRegion', region.label);
+ } else {
+ map.getLayer('regions').style('fill', regionFill);
+ showRowEvolution('getRegion', region.label);
+ }
+ }
+ }).on('mouseenter',function (d, path, evt) {
+ var region = regionDict[regionCode(d)];
+ if (region && region.label) {
+ if (evt.shiftKey) {
+ path.attr('fill', '#f4f45b');
+ }
+ }
+ }).on('mouseleave',function (d, path, evt) {
+ var region = regionDict[regionCode(d)];
+ if (region && region.label) {
+ if ($.inArray(region.label, _rowEvolution.labels) == -1) {
+ // reset color
+ path.attr('fill', regionFill(d));
+ }
+ }
+ }).style('cursor', function (d) {
+ return regionDict[regionCode(d)] && regionDict[regionCode(d)].label ? 'pointer' : 'default';
+ });
- // apply colors to map
- map.getLayer('regions')
- .style('fill', regionFill)
- .style('stroke', function(data) {
- return regionDict[regionCode(data)] === undefined ? '#bbb' : '#3C6FB6';
- }).sort(function(data) {
- var code = regionCode(data);
- return regionDict[code] === undefined ? -1 : regionDict[code].curMetric;
- }).tooltips(function(data) {
- var metric = $$('.userCountryMapSelectMetrics').val(),
- region = regionDict[regionCode(data)];
- if (region === undefined) {
- return '<h3>'+data.name+'</h3><p>'+_.nb_visits.replace('%s', '<b>0</b>')+'</p>';
- }
- return '<h3>'+data.name+'</h3>'+
- formatValueForTooltips(region, metric, iso);
- }).on('click', function(d, path, evt) {
- var region = regionDict[regionCode(d)];
- if (region && region.label) {
- if (evt.shiftKey) {
- path.attr('fill', '#f4f45b');
- addMultipleRowEvolution('getRegion', region.label);
- } else {
- map.getLayer('regions').style('fill', regionFill);
- showRowEvolution('getRegion', region.label);
- }
- }
- }).on('mouseenter', function(d, path, evt) {
- var region = regionDict[regionCode(d)];
- if (region && region.label) {
- if (evt.shiftKey) {
- path.attr('fill', '#f4f45b');
+ // check for regions missing in the map
+ $.each(regionDict, function (code, region) {
+ if (!regionExistsInMap(code)) {
+ console.warn('possible region mismatch!', code, region.nb_visits);
}
- }
- }).on('mouseleave', function(d, path, evt) {
- var region = regionDict[regionCode(d)];
- if (region && region.label) {
- if ($.inArray(region.label, _rowEvolution.labels) == -1) {
- // reset color
- path.attr('fill', regionFill(d));
- }
- }
- }).style('cursor', function(d) {
- return regionDict[regionCode(d)] && regionDict[regionCode(d)].label ? 'pointer' : 'default';
- });
-
- // check for regions missing in the map
- $.each(regionDict, function(code, region) {
- if (!regionExistsInMap(code)) {
- console.warn('possible region mismatch!', code, region.nb_visits);
- }
+ });
});
- });
}
/*
@@ -765,174 +765,175 @@
// get visits per city from API
ajax(_reportParams('UserCountry', 'getCity', UserCountryMap.countriesByIso[iso].iso2))
- .done(function(data) {
+ .done(function (data) {
- loadingComplete();
+ loadingComplete();
- var metric = $$('.userCountryMapSelectMetrics').val(),
- colscale,
- totalCountryVisits = UserCountryMap.countriesByIso[iso].nb_visits,
- unlocated = totalCountryVisits,
- cities = [];
-
- // merge reportData and reportMetadata to cities array
- $.each(data.reportData, function(i, row) {
- unlocated -= row.nb_visits;
- cities.push($.extend(row, data.reportMetadata[i], {
- curMetric: quantify(row, metric)
- }));
- });
+ var metric = $$('.userCountryMapSelectMetrics').val(),
+ colscale,
+ totalCountryVisits = UserCountryMap.countriesByIso[iso].nb_visits,
+ unlocated = totalCountryVisits,
+ cities = [];
+
+ // merge reportData and reportMetadata to cities array
+ $.each(data.reportData, function (i, row) {
+ unlocated -= row.nb_visits;
+ cities.push($.extend(row, data.reportMetadata[i], {
+ curMetric: quantify(row, metric)
+ }));
+ });
- displayUnlocatableCount(unlocated, totalCountryVisits);
+ displayUnlocatableCount(unlocated, totalCountryVisits);
- // sort by current metric
- cities.sort(function(a, b) { return b.curMetric - a.curMetric; });
+ // sort by current metric
+ cities.sort(function (a, b) { return b.curMetric - a.curMetric; });
- colscale = getColorScale(cities, metric);
+ colscale = getColorScale(cities, metric);
- // construct scale
- var radscale = $K.scale.linear(cities.concat({ curMetric: 0 }), 'curMetric');
+ // construct scale
+ var radscale = $K.scale.linear(cities.concat({ curMetric: 0 }), 'curMetric');
- var area = map.container.width() * map.container.height(),
- sumArea = 0,
- f = {
- nb_visits: 0.002,
- nb_actions: 0.002,
- avg_time_on_site: 0.02,
- nb_actions_per_visit: 0.02,
- bounce_rate: 0.02
- },
- maxRad;
+ var area = map.container.width() * map.container.height(),
+ sumArea = 0,
+ f = {
+ nb_visits: 0.002,
+ nb_actions: 0.002,
+ avg_time_on_site: 0.02,
+ nb_actions_per_visit: 0.02,
+ bounce_rate: 0.02
+ },
+ maxRad;
- $.each(cities, function(i, city) {
- sumArea += isNaN(city.curMetric) ? 0 : Math.pow(radscale(city.curMetric), 2);
- });
- maxRad = Math.sqrt(area * f[metric] / sumArea);
-
- radscale = $K.scale.sqrt(cities.concat({ curMetric: 0 }), 'curMetric').range([2, maxRad+2]);
-
- var is_rate = metric.substr(0,3) != 'nb_' || metric == 'nb_actions_per_visit';
-
- var citySymbols = map.addSymbols({
- type: Kartograph.LabeledBubble,
- data: cities,
- clustering: 'noverlap',
- clusteringOpts: {
- size: 128,
- tolerance: 0
- },
- title: function(d) {
- return radscale(d.curMetric) > 10 ? formatNumber(d.curMetric) : '';
- },
- labelattrs: {
- fill: '#fff',
- 'font-size': 11,
- stroke: false,
- cursor: 'pointer'
- },
- filter: function(d) {
- if (isNaN(d.lat) || isNaN(d.long)) return false;
- return is_rate ? d.nb_visits > 5 && d.curMetric : d.curMetric;
- },
- aggregate: function(rows) {
- var row = aggregate(rows);
- row.city_names = [];
- row.label = rows[0].label; // keep label of biggest city for row evolution
- $.each(rows, function(i, r) {
- row.city_names = row.city_names.concat(r.city_names ? r.city_names : [r.city_name]);
- });
- row.city_name = row.city_names[0] + (row.city_names.length > 1 ? ' '+_.and_n_others.replace('%s', (row.city_names.length-1)) : '');
- row.curMetric = quantify(row, metric);
- return row;
- },
- sortBy: 'radius desc',
- location: function(city) { return [city.long, city.lat]; },
- radius: function(city) { return radscale(city.curMetric); },
- tooltip: function(city) {
- return '<h3>'+city.city_name+'</h3>'+
- formatValueForTooltips(city, metric, iso);
- },
- attrs: function(city) {
- return {
- fill: colscale(city.curMetric).hex(),
- 'fill-opacity': 0.7,
- stroke: '#fff',
+ $.each(cities, function (i, city) {
+ sumArea += isNaN(city.curMetric) ? 0 : Math.pow(radscale(city.curMetric), 2);
+ });
+ maxRad = Math.sqrt(area * f[metric] / sumArea);
+
+ radscale = $K.scale.sqrt(cities.concat({ curMetric: 0 }), 'curMetric').range([2, maxRad + 2]);
+
+ var is_rate = metric.substr(0, 3) != 'nb_' || metric == 'nb_actions_per_visit';
+
+ var citySymbols = map.addSymbols({
+ type: Kartograph.LabeledBubble,
+ data: cities,
+ clustering: 'noverlap',
+ clusteringOpts: {
+ size: 128,
+ tolerance: 0
+ },
+ title: function (d) {
+ return radscale(d.curMetric) > 10 ? formatNumber(d.curMetric) : '';
+ },
+ labelattrs: {
+ fill: '#fff',
+ 'font-size': 11,
+ stroke: false,
cursor: 'pointer'
- };
- },
- mouseenter: function(city, symbol, evt) {
- symbol.path.attr({
- 'fill-opacity': 1,
- 'stroke': '#000000',
- 'stroke-opacity': 1,
- 'stroke-width': 2
- });
- if (evt.shiftKey) {
- symbol.path.attr({ fill: '#f4f45b' });
- if (symbol.label) symbol.label.attr({ fill: '#000' });
- }
- },
- mouseleave: function(city, symbol) {
- symbol.path.attr({
- 'fill-opacity': 0.7,
- 'stroke-opacity': 1,
- 'stroke-width': 1,
- 'stroke': '#ffffff'
- });
- if ($.inArray(city.label, _rowEvolution.labels) == -1) {
- symbol.path.attr({ fill: colscale(city.curMetric) });
- if (symbol.label) symbol.label.attr({ fill: '#fff' });
- }
- },
- click: function(city, symbol, evt) {
- if (evt.shiftKey) {
- addMultipleRowEvolution('getCity', city.label);
- symbol.path.attr('fill', '#f4f45b');
- if (symbol.label) symbol.label.attr('fill', '#000');
- } else {
- showRowEvolution('getCity', city.label);
- citySymbols.update({
- attrs: function(city) {
- return { fill: colscale(city.curMetric) };
- }
+ },
+ filter: function (d) {
+ if (isNaN(d.lat) || isNaN(d.long)) return false;
+ return is_rate ? d.nb_visits > 5 && d.curMetric : d.curMetric;
+ },
+ aggregate: function (rows) {
+ var row = aggregate(rows);
+ row.city_names = [];
+ row.label = rows[0].label; // keep label of biggest city for row evolution
+ $.each(rows, function (i, r) {
+ row.city_names = row.city_names.concat(r.city_names ? r.city_names : [r.city_name]);
+ });
+ row.city_name = row.city_names[0] + (row.city_names.length > 1 ? ' ' + _.and_n_others.replace('%s', (row.city_names.length - 1)) : '');
+ row.curMetric = quantify(row, metric);
+ return row;
+ },
+ sortBy: 'radius desc',
+ location: function (city) { return [city.long, city.lat]; },
+ radius: function (city) { return radscale(city.curMetric); },
+ tooltip: function (city) {
+ return '<h3>' + city.city_name + '</h3>' +
+ formatValueForTooltips(city, metric, iso);
+ },
+ attrs: function (city) {
+ return {
+ fill: colscale(city.curMetric).hex(),
+ 'fill-opacity': 0.7,
+ stroke: '#fff',
+ cursor: 'pointer'
+ };
+ },
+ mouseenter: function (city, symbol, evt) {
+ symbol.path.attr({
+ 'fill-opacity': 1,
+ 'stroke': '#000000',
+ 'stroke-opacity': 1,
+ 'stroke-width': 2
});
+ if (evt.shiftKey) {
+ symbol.path.attr({ fill: '#f4f45b' });
+ if (symbol.label) symbol.label.attr({ fill: '#000' });
+ }
+ },
+ mouseleave: function (city, symbol) {
+ symbol.path.attr({
+ 'fill-opacity': 0.7,
+ 'stroke-opacity': 1,
+ 'stroke-width': 1,
+ 'stroke': '#ffffff'
+ });
+ if ($.inArray(city.label, _rowEvolution.labels) == -1) {
+ symbol.path.attr({ fill: colscale(city.curMetric) });
+ if (symbol.label) symbol.label.attr({ fill: '#fff' });
+ }
+ },
+ click: function (city, symbol, evt) {
+ if (evt.shiftKey) {
+ addMultipleRowEvolution('getCity', city.label);
+ symbol.path.attr('fill', '#f4f45b');
+ if (symbol.label) symbol.label.attr('fill', '#000');
+ } else {
+ showRowEvolution('getCity', city.label);
+ citySymbols.update({
+ attrs: function (city) {
+ return { fill: colscale(city.curMetric) };
+ }
+ });
+ }
}
- }
+ });
});
- });
}
- _updateMap(iso + '.svg', function() {
+ _updateMap(iso + '.svg', function () {
// add background
map.addLayer('context', {
key: 'iso',
- filter: function(pd) {
+ filter: function (pd) {
return UserCountryMap.countriesByIso[pd.iso] === undefined;
}
});
map.addLayer('context', {
key: 'iso',
name: 'context-clickable',
- filter: function(pd) {
+ filter: function (pd) {
return UserCountryMap.countriesByIso[pd.iso] !== undefined;
},
- click: function(path, p, evt) { // add click events for surrounding countries
+ click: function (path, p, evt) { // add click events for surrounding countries
evt.stopPropagation();
updateState(path.iso);
},
- tooltips: function(data) {
+ tooltips: function (data) {
if (UserCountryMap.countriesByIso[data.iso] === undefined) {
return 'no data';
}
var metric = $$('.userCountryMapSelectMetrics').val(),
country = UserCountryMap.countriesByIso[data.iso];
- return '<h3>'+country.name+'</h3>'+
+ return '<h3>' + country.name + '</h3>' +
formatValueForTooltips(country, metric, 'world');
}
});
function isThisCountry(d) { return d.iso == iso;}
+
map.addLayer("context", {
name: "regionBG",
filter: isThisCountry
@@ -947,7 +948,7 @@
styles: {
stroke: '#aaa'
},
- click: function(d, p, evt) {
+ click: function (d, p, evt) {
evt.stopPropagation();
}
});
@@ -957,19 +958,21 @@
map.getLayer('context-clickable').getPath(data.iso) &&
Math.abs(map.getLayer('context-clickable').getPath(data.iso).path.area()) > 700;
}
+
// returns either the reference to the country polygon or a custom label
// position if defined in UserCountryMap.customLabelPositions
function countryLabelPos(data) {
var CLP = UserCountryMap.customLabelPositions;
if (CLP[iso] && CLP[iso][data.iso]) return CLP[iso][data.iso];
- return 'context-clickable.'+data.iso;
+ return 'context-clickable.' + data.iso;
}
+
map.addSymbols({
data: map.getLayer('context-clickable').getPathsData(),
type: $K.Label,
filter: filtCountryLabels,
location: countryLabelPos,
- text: function(data) { return UserCountryMap.countriesByIso[data.iso].iso2; },
+ text: function (data) { return UserCountryMap.countriesByIso[data.iso].iso2; },
'class': 'countryLabelBg'
});
map.addSymbols({
@@ -977,7 +980,7 @@
type: $K.Label,
filter: filtCountryLabels,
location: countryLabelPos,
- text: function(data) { return UserCountryMap.countriesByIso[data.iso].iso2; },
+ text: function (data) { return UserCountryMap.countriesByIso[data.iso].iso2; },
'class': 'countryLabel'
});
@@ -1012,7 +1015,7 @@
if (multiple) {
_rowEvolution.labels.push(label);
- $.each(_rowEvolution.labels, function(i,l) {
+ $.each(_rowEvolution.labels, function (i, l) {
_rowEvolution.labels[i] = l.replace(/, /g, '%2C%20');
});
}
@@ -1032,86 +1035,86 @@
if (column) { requestParams.column = column; }
ajax(requestParams, 'html')
- .done(function(html) {
- Piwik_Popover.setContent(html);
-
- // use the popover title returned from the server
- var title = box.find('div.popover-title');
- if (title.size() > 0) {
- Piwik_Popover.setTitle(title.html());
- title.remove();
- }
+ .done(function (html) {
+ Piwik_Popover.setContent(html);
+
+ // use the popover title returned from the server
+ var title = box.find('div.popover-title');
+ if (title.size() > 0) {
+ Piwik_Popover.setTitle(title.html());
+ title.remove();
+ }
- box.find('.compare-container').hide();
- box.find('.rowevolution-startmulti').hide();
- box.find('.multirowevoltion-metric').off('change').change(function(e) {
- _rowEvolution.labels = oldLabels;
- showRowEvolution(method, label, box.find('.multirowevoltion-metric').val());
+ box.find('.compare-container').hide();
+ box.find('.rowevolution-startmulti').hide();
+ box.find('.multirowevoltion-metric').off('change').change(function (e) {
+ _rowEvolution.labels = oldLabels;
+ showRowEvolution(method, label, box.find('.multirowevoltion-metric').val());
+ });
});
- });
_rowEvolution.labels = [];
}
// now load the metrics for all countries
ajax(_reportParams('UserCountry', 'getCountry'))
- .done(function(report) {
- var metrics = $$('.userCountryMapSelectMetrics option');
- var countryData = [], countrySelect = $$('.userCountryMapSelectCountry'),
- countriesByIso = {};
- UserCountryMap.lastReportMetricStats = {};
- // read api result to countryData and countriesByIso
- $.each(report.reportData, function(i, data) {
- var meta = report.reportMetadata[i],
- country = {
- name: data.label,
- iso2: meta.code.toUpperCase(),
- fips: meta.code.toUpperCase(),
- iso: UserCountryMap.ISO2toISO3[meta.code.toUpperCase()],
- flag: meta.logo
- };
- if (UserCountryMap.differentFIPS[country.iso2]) {
- country.fips = UserCountryMap.differentFIPS[country.iso2];
- }
- $.each(metrics, function(i, metric) {
- metric = $(metric).attr('value');
- country[metric] = data[metric];
- });
- countryData.push(country);
- countriesByIso[country.iso] = country;
- worldTotalVisits += country['nb_visits'];
- });
- _worldTotal = worldTotalVisits;
- // sort countries by name
- countryData.sort(function(a,b) { return a.name > b.name ? 1 : -1; });
-
- // store country data globally
- UserCountryMap.countryData = countryData;
- UserCountryMap.countriesByIso = countriesByIso;
-
- map.loadCSS(config.mapCssPath, function() {
- // map stylesheets are loaded
-
- // hide loading indicator
- $$('.UserCountryMap .loadingPiwik').hide();
- $('.mapWidgetStatus').height(0);
-
- // start with default view (or saved state??)
- var params = self.widget.dashboardWidget('getWidgetObject').parameters;
- self.mode = params && params.viewMode ? params.viewMode : 'region';
- if (params && params.lastMetric) $$('.userCountryMapSelectMetrics').val(params.lastMetric);
- //alert('updateState: '+params && params.lastMap ? params.lastMap : 'world');
- updateState(params && params.lastMap ? params.lastMap : 'world');
-
- // populate country select
- $.each(countryData, function(i, country) {
- countrySelect.append('<option value="'+country.iso+'">'+country.name+'</option>');
+ .done(function (report) {
+ var metrics = $$('.userCountryMapSelectMetrics option');
+ var countryData = [], countrySelect = $$('.userCountryMapSelectCountry'),
+ countriesByIso = {};
+ UserCountryMap.lastReportMetricStats = {};
+ // read api result to countryData and countriesByIso
+ $.each(report.reportData, function (i, data) {
+ var meta = report.reportMetadata[i],
+ country = {
+ name: data.label,
+ iso2: meta.code.toUpperCase(),
+ fips: meta.code.toUpperCase(),
+ iso: UserCountryMap.ISO2toISO3[meta.code.toUpperCase()],
+ flag: meta.logo
+ };
+ if (UserCountryMap.differentFIPS[country.iso2]) {
+ country.fips = UserCountryMap.differentFIPS[country.iso2];
+ }
+ $.each(metrics, function (i, metric) {
+ metric = $(metric).attr('value');
+ country[metric] = data[metric];
+ });
+ countryData.push(country);
+ countriesByIso[country.iso] = country;
+ worldTotalVisits += country['nb_visits'];
});
+ _worldTotal = worldTotalVisits;
+ // sort countries by name
+ countryData.sort(function (a, b) { return a.name > b.name ? 1 : -1; });
+
+ // store country data globally
+ UserCountryMap.countryData = countryData;
+ UserCountryMap.countriesByIso = countriesByIso;
+
+ map.loadCSS(config.mapCssPath, function () {
+ // map stylesheets are loaded
+
+ // hide loading indicator
+ $$('.UserCountryMap .loadingPiwik').hide();
+ $('.mapWidgetStatus').height(0);
+
+ // start with default view (or saved state??)
+ var params = self.widget.dashboardWidget('getWidgetObject').parameters;
+ self.mode = params && params.viewMode ? params.viewMode : 'region';
+ if (params && params.lastMetric) $$('.userCountryMapSelectMetrics').val(params.lastMetric);
+ //alert('updateState: '+params && params.lastMap ? params.lastMap : 'world');
+ updateState(params && params.lastMap ? params.lastMap : 'world');
+
+ // populate country select
+ $.each(countryData, function (i, country) {
+ countrySelect.append('<option value="' + country.iso + '">' + country.name + '</option>');
+ });
- initUserInterface();
+ initUserInterface();
+ });
});
- });
function hideOverlay(e) {
var overlay = $('.content', $(e.target).parents('.UserCountryMap-overlay'));
@@ -1119,36 +1122,36 @@
overlay.data('locked', true);
overlay.fadeOut(200);
- $$('.UserCountryMap').mouseleave(function() {
+ $$('.UserCountryMap').mouseleave(function () {
overlay.fadeIn(200);
$$('.UserCountryMap').parent().off('mouseleave');
- setTimeout(function() {
+ setTimeout(function () {
overlay.data('locked', false);
}, 1000);
});
var offset = $$('.UserCountryMap').offset(),
dim = {
- x: overlay.offset().left - offset.left,
- y: overlay.offset().top - offset.top,
- w: overlay.width(),
- h: overlay.height()
- };
- $$('.UserCountryMap').mousemove(function(e) {
+ x: overlay.offset().left - offset.left,
+ y: overlay.offset().top - offset.top,
+ w: overlay.width(),
+ h: overlay.height()
+ };
+ $$('.UserCountryMap').mousemove(function (e) {
var mx = e.pageX - offset.left, my = e.pageY - offset.top, pad = 20,
- outside = mx < dim.x - pad || mx > dim.x + dim.w + pad || my < dim.y - pad || my > dim.y + dim.h + pad;
+ outside = mx < dim.x - pad || mx > dim.x + dim.w + pad || my < dim.y - pad || my > dim.y + dim.h + pad;
if (outside) {
$$('.UserCountryMap').parent().off('mouseleave');
- setTimeout(function() {
+ setTimeout(function () {
overlay.fadeIn(200);
- setTimeout(function() {
+ setTimeout(function () {
overlay.data('locked', false);
}, 1000);
}, 100);
}
});
/*setTimeout(function() {
- overlay.fadeIn(1000);
- }, 3000);*/
+ overlay.fadeIn(1000);
+ }, 3000);*/
}
$('.UserCountryMap-overlay').off('mouseenter').on('mouseenter', hideOverlay);
@@ -1161,7 +1164,7 @@
/*
* resizes the map
*/
- resize: function() {
+ resize: function () {
var ratio, w, h,
map = this.map,
maxHeight = $(window).height() - (this.theWidget && this.theWidget.isMaximised ? 150 : 55);
@@ -1179,7 +1182,7 @@
/*
* removes the map
*/
- destroy: function() {
+ destroy: function () {
this.map.clear();
$(this.map.container).html('');
}
@@ -1250,8 +1253,8 @@ $.extend(UserCountryMap, {
CZE: {
partial: true,
groups: {
- "82": ["82","70","23","20"],
- "88": ["88","41"]
+ "82": ["82", "70", "23", "20"],
+ "88": ["88", "41"]
}
},
BEL: {
@@ -1266,7 +1269,7 @@ $.extend(UserCountryMap, {
"19": ["19", "07"],
"18": ["18", "15"],
"20": ["20", "12"],
- "21": ["21", "11", "04"]
+ "21": ["21", "11", "04"]
}
}
},
@@ -1284,7 +1287,7 @@ $.extend(UserCountryMap, {
CZE: { DEU: [12.3, 49] },
DEU: { AUT: [13.9, 48.1] },
ESP: { PRT: [-8.5, 39.6] },
- NLD: { BEL: [4.6, 51,1], DEU: [6.9, 51.5] },
+ NLD: { BEL: [4.6, 51, 1], DEU: [6.9, 51.5] },
CHE: { FRA: [6.2, 47.2], AUT: [9.95, 47.2], ITA: [9.7, 46.0], DEU: [8.14, 47.83] },
USA: { MEX: [-102, 24], CAN: [-97, 52] },
BIH: { HRV: [15.3, 45] }
diff --git a/plugins/UserCountryMap/templates/realtime-map.tpl b/plugins/UserCountryMap/templates/realtime-map.tpl
index 231ecd5ddb..583eed74fa 100644
--- a/plugins/UserCountryMap/templates/realtime-map.tpl
+++ b/plugins/UserCountryMap/templates/realtime-map.tpl
@@ -3,7 +3,8 @@
<div id="RealTimeMap_container">
<div id="RealTimeMap_map" style="overflow:hidden"></div>
<div class="realTimeMap_overlay">
- <span class="showing_visits_of" style="display:none">{'UserCountryMap_ShowingVisits'|translate} <span class="realTimeMap_timeSpan" style="font-weight:bold"></span></span>
+ <span class="showing_visits_of" style="display:none">{'UserCountryMap_ShowingVisits'|translate} <span class="realTimeMap_timeSpan"
+ style="font-weight:bold"></span></span>
<span class="no_data" style="display:none">{'CoreHome_ThereIsNoDataForThisReport'|translate}</span>
<span class="loading_data">{'General_LoadingData'|translate}...</span>
<img src="{$piwikUrl}plugins/UserCountryMap/img/realtimemap-loading.gif" style="vertical-align:baseline;position:relative;left:-2px;">
@@ -21,26 +22,29 @@
<!-- configure some piwik vars -->
<script type="text/javascript">
-{* If the map is loaded from the menu, do a few tweaks to clean up the display *}
-{if $mapIsStandaloneNotWidget}
- function initStandaloneMap() {ldelim}
- $('.top_controls').hide();
- $('ul.nav').on('piwikSwitchPage', function(event, item) {ldelim}
- var clickedMenuIsNotMap = ($(item).text() != "{'UserCountryMap_RealTimeMap'|translate|escape:'js'}");
- if(clickedMenuIsNotMap) {ldelim}
- $('.top_controls').show();
- {rdelim}
- {rdelim});
- $('.realTimeMap_overlay').css('top', '0px');
- $('.realTimeMap_datetime').css('top', '20px');
- {rdelim}
+ {* If the map is loaded from the menu, do a few tweaks to clean up the display *}
+ {if $mapIsStandaloneNotWidget}
+ function initStandaloneMap() {ldelim}
+ $('.top_controls').hide();
+ $('ul.nav').on('piwikSwitchPage', function (event, item) {ldelim}
+ var clickedMenuIsNotMap = ($(item).text() != "{'UserCountryMap_RealTimeMap'|translate|escape:'js'}");
+ if (clickedMenuIsNotMap) {ldelim}
+ $('.top_controls').show();
+ {rdelim
+ }
+ {rdelim
+ });
+ $('.realTimeMap_overlay').css('top', '0px');
+ $('.realTimeMap_datetime').css('top', '20px');
+ {rdelim
+ }
- initStandaloneMap();
-{/if}
+ initStandaloneMap();
+ {/if}
- {literal}
+ {literal}
var config = { metrics: {} };
-{/literal}
+ {/literal}
config.svgBasePath = "{$piwikUrl}plugins/UserCountryMap/svg/";
config.liveRefreshAfterMs = {$liveRefreshAfterMs};
@@ -52,24 +56,24 @@
var realtimeMap;
-{literal}
+ {literal}
if ($('#dashboardWidgetsArea').length) {
// dashboard mode
var $widgetContent = $('#RealTimeMap').parents('.widgetContent');
- $widgetContent.on('widget:create', function(evt, widget) {
+ $widgetContent.on('widget:create',function (evt, widget) {
realtimeMap = new UserCountryMap.RealtimeMap(config, widget);
- }).on('widget:maximise', function(evt) {
- realtimeMap.resize();
- }).on('widget:minimise', function(evt) {
- realtimeMap.resize();
- }).on('widget:destroy', function(evt) {
- realtimeMap.destroy();
- });
+ }).on('widget:maximise',function (evt) {
+ realtimeMap.resize();
+ }).on('widget:minimise',function (evt) {
+ realtimeMap.resize();
+ }).on('widget:destroy', function (evt) {
+ realtimeMap.destroy();
+ });
} else {
// stand-alone mode
realtimeMap = new UserCountryMap.RealtimeMap(config);
}
-{/literal}
+ {/literal}
</script>
diff --git a/plugins/UserCountryMap/templates/visitor-map.tpl b/plugins/UserCountryMap/templates/visitor-map.tpl
index a3b4c37cf8..8e967f8213 100644
--- a/plugins/UserCountryMap/templates/visitor-map.tpl
+++ b/plugins/UserCountryMap/templates/visitor-map.tpl
@@ -4,7 +4,7 @@
<div class="UserCountryMap-overlay UserCountryMap-title">
<div class="content">
<!--<div class="map-title" style="font-weight:bold; color:#9A9386;"></div>-->
- <div class="map-stats" style="color:#565656;"><b></b> </div>
+ <div class="map-stats" style="color:#565656;"><b></b></div>
</div>
</div>
<div class="UserCountryMap-overlay UserCountryMap-legend">
@@ -12,16 +12,16 @@
</div>
</div>
<div class="UserCountryMap-tooltip UserCountryMap-info">
- <div foo="bar" class="content unlocated-stats" data-tpl="{'UserCountryMap_Unlocated'|translate}" >
+ <div foo="bar" class="content unlocated-stats" data-tpl="{'UserCountryMap_Unlocated'|translate}">
</div>
</div>
<div class="UserCountryMap-info-btn" data-tooltip-target=".UserCountryMap-tooltip"></div>
</div>
<div class="mapWidgetStatus">
{if $noData }
- <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
+ <div class="pk-emptyDataTable">{'CoreHome_ThereIsNoDataForThisReport'|translate}</div>
{else}
- <span class="loadingPiwik">
+ <span class="loadingPiwik">
<img src="{$piwikUrl}themes/default/images/loading-blue.gif"> {'General_LoadingData'|translate}...
</span>
{/if}
@@ -29,23 +29,30 @@
<div class="dataTableFeatures" style="padding-top:0px;">
<div class="dataTableFooterIcons">
<div class="dataTableFooterWrap" var="graphVerticalBar">
- <img class="UserCountryMap-activeItem dataTableFooterActiveItem" src="{$piwikUrl}themes/default/images/data_table_footer_active_item.png" style="left: 25px;">
+ <img class="UserCountryMap-activeItem dataTableFooterActiveItem" src="{$piwikUrl}themes/default/images/data_table_footer_active_item.png"
+ style="left: 25px;">
<div class="tableIconsGroup">
<span class="tableAllColumnsSwitch">
- <a class="UserCountryMap-btn-zoom tableIcon" format="table"><img src="{$piwikUrl}plugins/UserCountryMap/img/zoom-out.png" title="Zoom to world"></a>
+ <a class="UserCountryMap-btn-zoom tableIcon" format="table"><img src="{$piwikUrl}plugins/UserCountryMap/img/zoom-out.png"
+ title="Zoom to world"></a>
</span>
</div>
<div class="tableIconsGroup UserCountryMap-view-mode-buttons">
<span class="tableAllColumnsSwitch">
- <a var="tableAllColumns" class="UserCountryMap-btn-region tableIcon activeIcon" format="tableAllColumns" data-region="{'UserCountryMap_Regions'|translate}" data-country="{'UserCountryMap_Countries'|translate}"><img src="{$piwikUrl}plugins/UserCountryMap/img/regions.png" title="Show vistors per region/country"> <span style="margin:0">{'UserCountryMap_Countries'|translate}</span>&nbsp;</a>
- <a var="tableGoals" class="UserCountryMap-btn-city tableIcon inactiveIco" format="tableGoals"><img src="{$piwikUrl}plugins/UserCountryMap/img/cities.png" title="Show visitors per city"> <span style="margin:0">{'UserCountryMap_Cities'|translate}</span>&nbsp;</a>
+ <a var="tableAllColumns" class="UserCountryMap-btn-region tableIcon activeIcon" format="tableAllColumns"
+ data-region="{'UserCountryMap_Regions'|translate}" data-country="{'UserCountryMap_Countries'|translate}"><img
+ src="{$piwikUrl}plugins/UserCountryMap/img/regions.png" title="Show vistors per region/country"> <span
+ style="margin:0">{'UserCountryMap_Countries'|translate}</span>&nbsp;</a>
+ <a var="tableGoals" class="UserCountryMap-btn-city tableIcon inactiveIco" format="tableGoals"><img
+ src="{$piwikUrl}plugins/UserCountryMap/img/cities.png" title="Show visitors per city"> <span
+ style="margin:0">{'UserCountryMap_Cities'|translate}</span>&nbsp;</a>
</span>
</div>
</div>
- <select class="userCountryMapSelectMetrics" style="float:right;margin-right:0;margin-bottom:5px;max-width: 9em;font-size:10px">
+ <select class="userCountryMapSelectMetrics" style="float:right;margin-right:0;margin-bottom:5px;max-width: 9em;font-size:10px">
{foreach from=$metrics item=metric}
<option value="{$metric[0]}" {if $metric[0] == $defaultMetric}selected="selected"{/if}>{$metric[1]}</option>
{/foreach}
@@ -67,36 +74,36 @@
{if !$noData }
-<!-- configure some piwik vars -->
-<script type="text/javascript">
+ <!-- configure some piwik vars -->
+ <script type="text/javascript">
- var visitorMap,
- config = JSON.parse('{$config|escape:'javascript'}');
- config._ = JSON.parse('{$localeJSON|escape:'javascript'}');
- config.reqParams = JSON.parse('{$reqParamsJSON|escape:'javascript'}');
+ var visitorMap,
+ config = JSON.parse('{$config|escape:'javascript'}');
+ config._ = JSON.parse('{$localeJSON|escape:'javascript'}');
+ config.reqParams = JSON.parse('{$reqParamsJSON|escape:'javascript'}');
- $('.UserCountryMap').addClass('dataTable');
+ $('.UserCountryMap').addClass('dataTable');
-{literal}
- if ($('#dashboardWidgetsArea').length) {
- // dashboard mode
- var $widgetContent = $('.UserCountryMap').parents('.widgetContent');
+ {literal}
+ if ($('#dashboardWidgetsArea').length) {
+ // dashboard mode
+ var $widgetContent = $('.UserCountryMap').parents('.widgetContent');
- $widgetContent.on('widget:create', function(evt, widget) {
- visitorMap = new UserCountryMap.VisitorMap(config, widget);
- }).on('widget:maximise', function(evt) {
- visitorMap.resize();
- }).on('widget:minimise', function(evt) {
- visitorMap.resize();
- }).on('widget:destroy', function(evt) {
- visitorMap.destroy();
- });
- } else {
- // stand-alone mode
- visitorMap = new UserCountryMap.VisitorMap(config);
- }
-{/literal}
+ $widgetContent.on('widget:create',function (evt, widget) {
+ visitorMap = new UserCountryMap.VisitorMap(config, widget);
+ }).on('widget:maximise',function (evt) {
+ visitorMap.resize();
+ }).on('widget:minimise',function (evt) {
+ visitorMap.resize();
+ }).on('widget:destroy', function (evt) {
+ visitorMap.destroy();
+ });
+ } else {
+ // stand-alone mode
+ visitorMap = new UserCountryMap.VisitorMap(config);
+ }
+ {/literal}
-</script>
+ </script>
{/if}
diff --git a/plugins/UserSettings/API.php b/plugins/UserSettings/API.php
index f118dcff56..49a6c4d883 100644
--- a/plugins/UserSettings/API.php
+++ b/plugins/UserSettings/API.php
@@ -22,75 +22,74 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/UserSettings/functions.php';
*/
class Piwik_UserSettings_API
{
- static private $instance = null;
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- protected function getDataTable($name, $idSite, $period, $date, $segment)
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- $dataTable = $archive->getDataTable($name);
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
- $dataTable->queueFilter('ReplaceColumnNames');
- $dataTable->queueFilter('ReplaceSummaryRowLabel');
- return $dataTable;
- }
-
- public function getResolution( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getDataTable('UserSettings_resolution', $idSite, $period, $date, $segment);
- return $dataTable;
- }
-
- public function getConfiguration( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getDataTable('UserSettings_configuration', $idSite, $period, $date, $segment);
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getConfigurationLabel'));
- return $dataTable;
- }
-
- public function getOS( $idSite, $period, $date, $segment = false, $addShortLabel = true )
- {
- $dataTable = $this->getDataTable('UserSettings_os', $idSite, $period, $date, $segment);
- // these filters are applied directly so other API methods can use GroupBy on the result of this method
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getOSLogo'));
- if ($addShortLabel)
- {
- $dataTable->filter(
- 'ColumnCallbackAddMetadata', array( 'label', 'shortLabel', 'Piwik_getOSShortLabel') );
- }
- $dataTable->filter('ColumnCallbackReplace', array( 'label', 'Piwik_getOSLabel') );
- return $dataTable;
- }
-
- /**
- * Gets a DataTable displaying number of visits by operating system family. The operating
- * system families are listed in /libs/UserAgentParser/UserAgentParser.php.
- */
- public function getOSFamily( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getOS($idSite, $period, $date, $segment, $addShortLabel = false);
- $dataTable->filter('GroupBy', array('label', 'Piwik_UserSettings_getOSFamily'));
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_Translate'));
- return $dataTable;
- }
-
- /**
- * Gets a DataTable displaying number of visits by device type (mobile vs. desktop).
- */
- public function getMobileVsDesktop( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getOS($idSite, $period, $date, $segment, $addShortLabel = false);
- $dataTable->filter('GroupBy', array('label', 'Piwik_UserSettings_getDeviceTypeFromOS'));
-
- // make sure the datatable has a row for mobile & desktop (if it has rows)
+ static private $instance = null;
+
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ protected function getDataTable($name, $idSite, $period, $date, $segment)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ $dataTable = $archive->getDataTable($name);
+ $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
+ $dataTable->queueFilter('ReplaceColumnNames');
+ $dataTable->queueFilter('ReplaceSummaryRowLabel');
+ return $dataTable;
+ }
+
+ public function getResolution($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('UserSettings_resolution', $idSite, $period, $date, $segment);
+ return $dataTable;
+ }
+
+ public function getConfiguration($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('UserSettings_configuration', $idSite, $period, $date, $segment);
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getConfigurationLabel'));
+ return $dataTable;
+ }
+
+ public function getOS($idSite, $period, $date, $segment = false, $addShortLabel = true)
+ {
+ $dataTable = $this->getDataTable('UserSettings_os', $idSite, $period, $date, $segment);
+ // these filters are applied directly so other API methods can use GroupBy on the result of this method
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getOSLogo'));
+ if ($addShortLabel) {
+ $dataTable->filter(
+ 'ColumnCallbackAddMetadata', array('label', 'shortLabel', 'Piwik_getOSShortLabel'));
+ }
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getOSLabel'));
+ return $dataTable;
+ }
+
+ /**
+ * Gets a DataTable displaying number of visits by operating system family. The operating
+ * system families are listed in /libs/UserAgentParser/UserAgentParser.php.
+ */
+ public function getOSFamily($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getOS($idSite, $period, $date, $segment, $addShortLabel = false);
+ $dataTable->filter('GroupBy', array('label', 'Piwik_UserSettings_getOSFamily'));
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_Translate'));
+ return $dataTable;
+ }
+
+ /**
+ * Gets a DataTable displaying number of visits by device type (mobile vs. desktop).
+ */
+ public function getMobileVsDesktop($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getOS($idSite, $period, $date, $segment, $addShortLabel = false);
+ $dataTable->filter('GroupBy', array('label', 'Piwik_UserSettings_getDeviceTypeFromOS'));
+
+ // make sure the datatable has a row for mobile & desktop (if it has rows)
$dataTables = array($dataTable);
if ($dataTable instanceof Piwik_DataTable_Array) {
$dataTables = $dataTable->getArray();
@@ -98,7 +97,7 @@ class Piwik_UserSettings_API
$requiredRows = array(
'General_Desktop' => Piwik_Archive::INDEX_NB_VISITS,
- 'General_Mobile' => Piwik_Archive::INDEX_NB_VISITS
+ 'General_Mobile' => Piwik_Archive::INDEX_NB_VISITS
);
foreach ($dataTables AS $table) {
@@ -109,127 +108,126 @@ class Piwik_UserSettings_API
$row = $table->getRowFromLabel($requiredRow);
if (empty($row)) {
$table->addRowsFromSimpleArray(array(
- array('label' => $requiredRow, $key => 0)
- ));
+ array('label' => $requiredRow, $key => 0)
+ ));
}
}
}
- // set the logo metadata
- $dataTable->queueFilter('MetadataCallbackReplace',
- array('logo', 'Piwik_UserSettings_getDeviceTypeImg', null, array('label')));
-
- // translate the labels
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_Translate'));
-
- return $dataTable;
- }
-
- public function getBrowserVersion( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getDataTable('UserSettings_browser', $idSite, $period, $date, $segment);
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getBrowsersLogo'));
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'Piwik_getBrowserShortLabel'));
- $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserLabel'));
- return $dataTable;
- }
-
- /**
- * Gets a DataTable displaying number of visits by browser (ie, Firefox, Chrome, etc.).
- * The browser version is not included in this report.
- */
- public function getBrowser( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getDataTable('UserSettings_browser', $idSite, $period, $date, $segment);
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getBrowsersLogo'));
- $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserLabel'));
-
- $getBrowserFromBrowserVersion = 'Piwik_UserSettings_getBrowserFromBrowserVersion';
- $dataTable->filter('GroupBy', array('label', $getBrowserFromBrowserVersion));
-
- return $dataTable;
- }
-
- public function getBrowserType( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getDataTable('UserSettings_browserType', $idSite, $period, $date, $segment);
- $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'ucfirst'));
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserTypeLabel'));
- return $dataTable;
- }
-
- public function getWideScreen( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getDataTable('UserSettings_wideScreen', $idSite, $period, $date, $segment);
- $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getScreensLogo'));
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'ucfirst'));
- return $dataTable;
- }
-
- public function getPlugin( $idSite, $period, $date, $segment = false )
- {
- // fetch all archive data required
- $dataTable = $this->getDataTable('UserSettings_plugin', $idSite, $period, $date, $segment);
- $browserTypes = $this->getDataTable('UserSettings_browserType', $idSite, $period, $date, $segment);
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
- $visitsSums = $archive->getNumeric('nb_visits');
-
- // check whether given tables are arrays
- if($dataTable instanceof Piwik_DataTable_Array) {
- $tableArray = $dataTable->getArray();
- $browserTypesArray = $browserTypes->getArray();
- $visitSumsArray = $visitsSums->getArray();
- } else {
- $tableArray = Array($dataTable);
- $browserTypesArray = Array($browserTypes);
- $visitSumsArray = Array($visitsSums);
- }
-
- // walk through the results and calculate the percentage
- foreach($tableArray as $key => $table) {
-
- // get according browserType table
- foreach($browserTypesArray AS $k => $browsers) {
- if($k == $key) {
- $browserType = $browsers;
- }
- }
-
- // get according visitsSum
- foreach($visitSumsArray AS $k => $visits) {
- if($k == $key) {
- if(is_object($visits)) {
- $visitsSumTotal = (float)$visits->getFirstRow()->getColumn(0);
- } else {
- $visitsSumTotal = (float)$visits;
- }
- }
- }
-
- // Calculate percentage, but ignore IE users because plugin detection doesn't work on IE
- $ieVisits = 0;
-
- $ieStats = $browserType->getRowFromLabel('ie');
- if($ieStats !== false)
- {
- $ieVisits = $ieStats->getColumn(Piwik_Archive::INDEX_NB_VISITS);
- }
-
- $visitsSum = $visitsSumTotal - $ieVisits;
-
- // The filter must be applied now so that the new column can
- // be sorted by the generic filters (applied right after this loop exits)
- $table->filter('ColumnCallbackAddColumnPercentage', array('nb_visits_percentage', Piwik_Archive::INDEX_NB_VISITS, $visitsSum, 1));
- $table->filter('RangeCheck', array('nb_visits_percentage'));
- }
-
- $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getPluginsLogo'));
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'ucfirst'));
-
- return $dataTable;
- }
-
- public function getLanguage( $idSite, $period, $date, $segment = false )
+ // set the logo metadata
+ $dataTable->queueFilter('MetadataCallbackReplace',
+ array('logo', 'Piwik_UserSettings_getDeviceTypeImg', null, array('label')));
+
+ // translate the labels
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_Translate'));
+
+ return $dataTable;
+ }
+
+ public function getBrowserVersion($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('UserSettings_browser', $idSite, $period, $date, $segment);
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getBrowsersLogo'));
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'Piwik_getBrowserShortLabel'));
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserLabel'));
+ return $dataTable;
+ }
+
+ /**
+ * Gets a DataTable displaying number of visits by browser (ie, Firefox, Chrome, etc.).
+ * The browser version is not included in this report.
+ */
+ public function getBrowser($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('UserSettings_browser', $idSite, $period, $date, $segment);
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getBrowsersLogo'));
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserLabel'));
+
+ $getBrowserFromBrowserVersion = 'Piwik_UserSettings_getBrowserFromBrowserVersion';
+ $dataTable->filter('GroupBy', array('label', $getBrowserFromBrowserVersion));
+
+ return $dataTable;
+ }
+
+ public function getBrowserType($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('UserSettings_browserType', $idSite, $period, $date, $segment);
+ $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'ucfirst'));
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserTypeLabel'));
+ return $dataTable;
+ }
+
+ public function getWideScreen($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('UserSettings_wideScreen', $idSite, $period, $date, $segment);
+ $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getScreensLogo'));
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'ucfirst'));
+ return $dataTable;
+ }
+
+ public function getPlugin($idSite, $period, $date, $segment = false)
+ {
+ // fetch all archive data required
+ $dataTable = $this->getDataTable('UserSettings_plugin', $idSite, $period, $date, $segment);
+ $browserTypes = $this->getDataTable('UserSettings_browserType', $idSite, $period, $date, $segment);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ $visitsSums = $archive->getNumeric('nb_visits');
+
+ // check whether given tables are arrays
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ $tableArray = $dataTable->getArray();
+ $browserTypesArray = $browserTypes->getArray();
+ $visitSumsArray = $visitsSums->getArray();
+ } else {
+ $tableArray = Array($dataTable);
+ $browserTypesArray = Array($browserTypes);
+ $visitSumsArray = Array($visitsSums);
+ }
+
+ // walk through the results and calculate the percentage
+ foreach ($tableArray as $key => $table) {
+
+ // get according browserType table
+ foreach ($browserTypesArray AS $k => $browsers) {
+ if ($k == $key) {
+ $browserType = $browsers;
+ }
+ }
+
+ // get according visitsSum
+ foreach ($visitSumsArray AS $k => $visits) {
+ if ($k == $key) {
+ if (is_object($visits)) {
+ $visitsSumTotal = (float)$visits->getFirstRow()->getColumn(0);
+ } else {
+ $visitsSumTotal = (float)$visits;
+ }
+ }
+ }
+
+ // Calculate percentage, but ignore IE users because plugin detection doesn't work on IE
+ $ieVisits = 0;
+
+ $ieStats = $browserType->getRowFromLabel('ie');
+ if ($ieStats !== false) {
+ $ieVisits = $ieStats->getColumn(Piwik_Archive::INDEX_NB_VISITS);
+ }
+
+ $visitsSum = $visitsSumTotal - $ieVisits;
+
+ // The filter must be applied now so that the new column can
+ // be sorted by the generic filters (applied right after this loop exits)
+ $table->filter('ColumnCallbackAddColumnPercentage', array('nb_visits_percentage', Piwik_Archive::INDEX_NB_VISITS, $visitsSum, 1));
+ $table->filter('RangeCheck', array('nb_visits_percentage'));
+ }
+
+ $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getPluginsLogo'));
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'ucfirst'));
+
+ return $dataTable;
+ }
+
+ public function getLanguage($idSite, $period, $date, $segment = false)
{
$dataTable = $this->getDataTable('UserSettings_language', $idSite, $period, $date, $segment);
$dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_LanguageTranslate'));
diff --git a/plugins/UserSettings/Controller.php b/plugins/UserSettings/Controller.php
index 82c23fe870..d4e39fcc3b 100644
--- a/plugins/UserSettings/Controller.php
+++ b/plugins/UserSettings/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserSettings
*/
@@ -13,181 +13,181 @@
*
* @package Piwik_UserSettings
*/
-class Piwik_UserSettings_Controller extends Piwik_Controller
+class Piwik_UserSettings_Controller extends Piwik_Controller
{
- /** The set of related reports displayed under the 'Operating Systems' header. */
- private $osRelatedReports = null;
-
- public function __construct()
- {
- parent::__construct();
- $this->osRelatedReports = array(
- 'UserSettings.getOSFamily' => Piwik_Translate('UserSettings_OperatingSystemFamily'),
- 'UserSettings.getOS' => Piwik_Translate('UserSettings_OperatingSystems')
- );
- }
-
- function index()
- {
- $view = Piwik_View::factory('index');
-
- $view->dataTablePlugin = $this->getPlugin( true );
- $view->dataTableResolution = $this->getResolution( true );
- $view->dataTableConfiguration = $this->getConfiguration( true );
- $view->dataTableOS = $this->getOS( true );
- $view->dataTableBrowser = $this->getBrowser( true );
- $view->dataTableBrowserType = $this->getBrowserType ( true );
- $view->dataTableMobileVsDesktop = $this->getMobileVsDesktop( true );
- $view->dataTableBrowserLanguage = $this->getLanguage( true );
-
- echo $view->render();
- }
-
- function getResolution( $fetch = false)
- {
- $view = $this->getStandardDataTableUserSettings(
- __FUNCTION__,
- 'UserSettings.getResolution'
- );
- $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnResolution'));
- return $this->renderView($view, $fetch);
- }
-
- function getConfiguration( $fetch = false)
- {
- $view = $this->getStandardDataTableUserSettings(
- __FUNCTION__,
- 'UserSettings.getConfiguration'
- );
- $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnConfiguration'));
- $view->setLimit( 3 );
- return $this->renderView($view, $fetch);
- }
-
- function getOS( $fetch = false)
- {
- $view = $this->getStandardDataTableUserSettings(
- __FUNCTION__,
- 'UserSettings.getOS'
- );
- $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnOperatingSystem'));
- $view->addRelatedReports(Piwik_Translate('UserSettings_OperatingSystems'), $this->osRelatedReports);
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Returns or echos a report displaying the number of visits by operating system family.
- */
- public function getOSFamily( $fetch = false )
- {
- $view = $this->getStandardDataTableUserSettings(__FUNCTION__, 'UserSettings.getOSFamily');
- $view->setColumnTranslation('label', Piwik_Translate('UserSettings_OperatingSystemFamily'));
- $view->addRelatedReports(Piwik_Translate('UserSettings_OperatingSystemFamily'), $this->osRelatedReports);
- return $this->renderView($view, $fetch);
- }
-
- function getBrowserVersion( $fetch = false)
- {
- $view = $this->getStandardDataTableUserSettings(
- __FUNCTION__,
- 'UserSettings.getBrowserVersion'
- );
- $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnBrowserVersion'));
- $view->setGraphLimit(7);
- $view->addRelatedReports(Piwik_Translate('UserSettings_ColumnBrowserVersion'), array(
- 'UserSettings.getBrowser' => Piwik_Translate('UserSettings_Browsers')
- ));
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Returns or echos a report displaying the number of visits by browser type. The browser
- * version is not included in this report.
- */
- public function getBrowser( $fetch = false )
- {
- $view = $this->getStandardDataTableUserSettings(__FUNCTION__, 'UserSettings.getBrowser');
- $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnBrowser'));
- $view->setGraphLimit(7);
- $view->addRelatedReports(Piwik_Translate('UserSettings_Browsers'), array(
- 'UserSettings.getBrowserVersion' => Piwik_Translate('UserSettings_ColumnBrowserVersion')
- ));
- return $this->renderView($view, $fetch);
- }
-
- function getBrowserType ( $fetch = false)
- {
- $view = $this->getStandardDataTableUserSettings(
- __FUNCTION__,
- 'UserSettings.getBrowserType',
- 'graphPie'
- );
- $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnBrowserFamily'));
- $view->disableOffsetInformationAndPaginationControls();
- return $this->renderView($view, $fetch);
- }
-
- function getWideScreen( $fetch = false)
- {
- $view = $this->getStandardDataTableUserSettings(
- __FUNCTION__,
- 'UserSettings.getWideScreen'
- );
- $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnTypeOfScreen'));
- $view->disableOffsetInformationAndPaginationControls();
- $view->addRelatedReports(Piwik_Translate('UserSettings_ColumnTypeOfScreen'), array(
- 'UserSettings.getMobileVsDesktop' => Piwik_Translate('UserSettings_MobileVsDesktop')
- ));
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Returns or echos a report displaying the number of visits by device type (Mobile or Desktop).
- */
- public function getMobileVsDesktop( $fetch = false )
- {
- $view = $this->getStandardDataTableUserSettings(__FUNCTION__, 'UserSettings.getMobileVsDesktop');
- $view->setColumnTranslation('label', Piwik_Translate('UserSettings_MobileVsDesktop'));
- $view->addRelatedReports(Piwik_Translate('UserSettings_MobileVsDesktop'), array(
- 'UserSettings.getWideScreen' => Piwik_Translate('UserSettings_ColumnTypeOfScreen')
- ));
- return $this->renderView($view, $fetch);
- }
-
- function getPlugin( $fetch = false)
- {
- $view = $this->getStandardDataTableUserSettings(
- __FUNCTION__,
- 'UserSettings.getPlugin'
- );
- $view->disableShowAllViewsIcons();
- $view->disableShowAllColumns();
- $view->disableOffsetInformationAndPaginationControls();
- $view->setColumnsToDisplay( array('label','nb_visits_percentage','nb_visits') );
- $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnPlugin'));
- $view->setColumnTranslation('nb_visits_percentage', str_replace(' ', '&nbsp;', Piwik_Translate('General_ColumnPercentageVisits')));
- $view->setSortedColumn('nb_visits_percentage');
- $view->setLimit( 10 );
- $view->setFooterMessage( Piwik_Translate('UserSettings_PluginDetectionDoesNotWorkInIE'));
- return $this->renderView($view, $fetch);
- }
-
- protected function getStandardDataTableUserSettings( $currentControllerAction,
- $APItoCall,
- $defaultDatatableType = null )
- {
- $view = Piwik_ViewDataTable::factory( $defaultDatatableType);
- $view->init( $this->pluginName, $currentControllerAction, $APItoCall );
- $view->disableSearchBox();
- $view->disableExcludeLowPopulation();
- $view->setLimit( 5 );
- $view->setGraphLimit(5);
-
- $this->setPeriodVariablesView($view);
- $this->setMetricsVariablesView($view);
-
- return $view;
- }
+ /** The set of related reports displayed under the 'Operating Systems' header. */
+ private $osRelatedReports = null;
+
+ public function __construct()
+ {
+ parent::__construct();
+ $this->osRelatedReports = array(
+ 'UserSettings.getOSFamily' => Piwik_Translate('UserSettings_OperatingSystemFamily'),
+ 'UserSettings.getOS' => Piwik_Translate('UserSettings_OperatingSystems')
+ );
+ }
+
+ function index()
+ {
+ $view = Piwik_View::factory('index');
+
+ $view->dataTablePlugin = $this->getPlugin(true);
+ $view->dataTableResolution = $this->getResolution(true);
+ $view->dataTableConfiguration = $this->getConfiguration(true);
+ $view->dataTableOS = $this->getOS(true);
+ $view->dataTableBrowser = $this->getBrowser(true);
+ $view->dataTableBrowserType = $this->getBrowserType(true);
+ $view->dataTableMobileVsDesktop = $this->getMobileVsDesktop(true);
+ $view->dataTableBrowserLanguage = $this->getLanguage(true);
+
+ echo $view->render();
+ }
+
+ function getResolution($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__,
+ 'UserSettings.getResolution'
+ );
+ $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnResolution'));
+ return $this->renderView($view, $fetch);
+ }
+
+ function getConfiguration($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__,
+ 'UserSettings.getConfiguration'
+ );
+ $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnConfiguration'));
+ $view->setLimit(3);
+ return $this->renderView($view, $fetch);
+ }
+
+ function getOS($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__,
+ 'UserSettings.getOS'
+ );
+ $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnOperatingSystem'));
+ $view->addRelatedReports(Piwik_Translate('UserSettings_OperatingSystems'), $this->osRelatedReports);
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Returns or echos a report displaying the number of visits by operating system family.
+ */
+ public function getOSFamily($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(__FUNCTION__, 'UserSettings.getOSFamily');
+ $view->setColumnTranslation('label', Piwik_Translate('UserSettings_OperatingSystemFamily'));
+ $view->addRelatedReports(Piwik_Translate('UserSettings_OperatingSystemFamily'), $this->osRelatedReports);
+ return $this->renderView($view, $fetch);
+ }
+
+ function getBrowserVersion($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__,
+ 'UserSettings.getBrowserVersion'
+ );
+ $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnBrowserVersion'));
+ $view->setGraphLimit(7);
+ $view->addRelatedReports(Piwik_Translate('UserSettings_ColumnBrowserVersion'), array(
+ 'UserSettings.getBrowser' => Piwik_Translate('UserSettings_Browsers')
+ ));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Returns or echos a report displaying the number of visits by browser type. The browser
+ * version is not included in this report.
+ */
+ public function getBrowser($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(__FUNCTION__, 'UserSettings.getBrowser');
+ $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnBrowser'));
+ $view->setGraphLimit(7);
+ $view->addRelatedReports(Piwik_Translate('UserSettings_Browsers'), array(
+ 'UserSettings.getBrowserVersion' => Piwik_Translate('UserSettings_ColumnBrowserVersion')
+ ));
+ return $this->renderView($view, $fetch);
+ }
+
+ function getBrowserType($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__,
+ 'UserSettings.getBrowserType',
+ 'graphPie'
+ );
+ $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnBrowserFamily'));
+ $view->disableOffsetInformationAndPaginationControls();
+ return $this->renderView($view, $fetch);
+ }
+
+ function getWideScreen($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__,
+ 'UserSettings.getWideScreen'
+ );
+ $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnTypeOfScreen'));
+ $view->disableOffsetInformationAndPaginationControls();
+ $view->addRelatedReports(Piwik_Translate('UserSettings_ColumnTypeOfScreen'), array(
+ 'UserSettings.getMobileVsDesktop' => Piwik_Translate('UserSettings_MobileVsDesktop')
+ ));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Returns or echos a report displaying the number of visits by device type (Mobile or Desktop).
+ */
+ public function getMobileVsDesktop($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(__FUNCTION__, 'UserSettings.getMobileVsDesktop');
+ $view->setColumnTranslation('label', Piwik_Translate('UserSettings_MobileVsDesktop'));
+ $view->addRelatedReports(Piwik_Translate('UserSettings_MobileVsDesktop'), array(
+ 'UserSettings.getWideScreen' => Piwik_Translate('UserSettings_ColumnTypeOfScreen')
+ ));
+ return $this->renderView($view, $fetch);
+ }
+
+ function getPlugin($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__,
+ 'UserSettings.getPlugin'
+ );
+ $view->disableShowAllViewsIcons();
+ $view->disableShowAllColumns();
+ $view->disableOffsetInformationAndPaginationControls();
+ $view->setColumnsToDisplay(array('label', 'nb_visits_percentage', 'nb_visits'));
+ $view->setColumnTranslation('label', Piwik_Translate('UserSettings_ColumnPlugin'));
+ $view->setColumnTranslation('nb_visits_percentage', str_replace(' ', '&nbsp;', Piwik_Translate('General_ColumnPercentageVisits')));
+ $view->setSortedColumn('nb_visits_percentage');
+ $view->setLimit(10);
+ $view->setFooterMessage(Piwik_Translate('UserSettings_PluginDetectionDoesNotWorkInIE'));
+ return $this->renderView($view, $fetch);
+ }
+
+ protected function getStandardDataTableUserSettings($currentControllerAction,
+ $APItoCall,
+ $defaultDatatableType = null)
+ {
+ $view = Piwik_ViewDataTable::factory($defaultDatatableType);
+ $view->init($this->pluginName, $currentControllerAction, $APItoCall);
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->setLimit(5);
+ $view->setGraphLimit(5);
+
+ $this->setPeriodVariablesView($view);
+ $this->setMetricsVariablesView($view);
+
+ return $view;
+ }
/**
* Renders datatable for browser language
@@ -196,17 +196,17 @@ class Piwik_UserSettings_Controller extends Piwik_Controller
*
* @return string|void
*/
- public function getLanguage( $fetch = false)
+ public function getLanguage($fetch = false)
{
$view = Piwik_ViewDataTable::factory();
- $view->init( $this->pluginName, __FUNCTION__, "UserSettings.getLanguage" );
+ $view->init($this->pluginName, __FUNCTION__, "UserSettings.getLanguage");
$view->disableExcludeLowPopulation();
- $view->setColumnsToDisplay( array('label','nb_visits') );
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
$view->setColumnTranslation('label', Piwik_Translate('General_Language'));
$view->setSortedColumn('nb_visits');
$view->disableSearchBox();
- $view->setLimit( 5 );
+ $view->setLimit(5);
return $this->renderView($view, $fetch);
}
diff --git a/plugins/UserSettings/UserSettings.php b/plugins/UserSettings/UserSettings.php
index b9a2ec5b20..d46b952c56 100644
--- a/plugins/UserSettings/UserSettings.php
+++ b/plugins/UserSettings/UserSettings.php
@@ -15,417 +15,409 @@
*/
class Piwik_UserSettings extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('UserSettings_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- /*
- * Mapping between the browser family shortcode and the displayed name
- */
- static public $browserType_display = array(
- 'ie' => 'Trident (IE)',
- 'gecko' => 'Gecko (Firefox)',
- 'khtml' => 'KHTML (Konqueror)',
- 'webkit' => 'WebKit (Safari, Chrome)',
- 'opera' => 'Presto (Opera)',
- );
-
- /*
- * Defines API reports.
- * Also used to define Widgets.
- *
- * @array Category, Report Name, API Module, API action, Translated column name,
- * $segment, $sqlSegment, $acceptedValues, $sqlFilter
- */
- protected $reportMetadata = array(
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_WidgetResolutions',
- 'UserSettings',
- 'getResolution',
- 'UserSettings_ColumnResolution',
- 'resolution',
- 'log_visit.config_resolution',
- '1280x1024, 800x600, etc.',
- null,),
-
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_WidgetBrowsers',
- 'UserSettings',
- 'getBrowser',
- 'UserSettings_ColumnBrowser',
- 'browserName',
- 'log_visit.config_browser_name',
- 'FF, IE, CH, SF, OP, etc.',
- null,),
-
- // browser version
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_WidgetBrowserVersion',
- 'UserSettings',
- 'getBrowserVersion',
- 'UserSettings_ColumnBrowserVersion',
- 'browserVersion',
- 'log_visit.config_browser_version',
- '1.0, 8.0, etc.',
- null,),
-
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_WidgetBrowserFamilies',
- 'UserSettings',
- 'getBrowserType',
- 'UserSettings_ColumnBrowserFamily',
- null,
- null,
- null,
- null,),
-
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_WidgetPlugins',
- 'UserSettings',
- 'getPlugin',
- 'UserSettings_ColumnPlugin',
- null,
- null,
- null,
- null,),
-
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_WidgetWidescreen',
- 'UserSettings',
- 'getWideScreen',
- 'UserSettings_ColumnTypeOfScreen',
- null,
- null,
- null,
- null,),
-
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_WidgetOperatingSystems',
- 'UserSettings',
- 'getOS',
- 'UserSettings_ColumnOperatingSystem',
- 'operatingSystem',
- 'log_visit.config_os',
- 'WXP, WI7, MAC, LIN, AND, IPD, etc.',
- null,),
-
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_WidgetGlobalVisitors',
- 'UserSettings',
- 'getConfiguration',
- 'UserSettings_ColumnConfiguration',
- null,
- null,
- null,
- null),
-
- // operating system family
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_WidgetOperatingSystemFamily',
- 'UserSettings',
- 'getOSFamily',
- 'UserSettings_OperatingSystemFamily',
- null,
- null,
- null,
- null),
-
- // device type
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_MobileVsDesktop',
- 'UserSettings',
- 'getMobileVsDesktop',
- 'UserSettings_MobileVsDesktop',
- null,
- null,
- null,
- null),
-
- // Browser language
- array( 'UserSettings_VisitorSettings',
- 'UserSettings_BrowserLanguage',
- 'UserSettings',
- 'getLanguage',
- 'General_Language',
- null,
- null,
- null,
- null),
- );
-
- /*
- * List of hooks
- */
- function getListHooksRegistered()
- {
- $hooks = array(
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenu',
- 'API.getReportMetadata' => 'getReportMetadata',
- 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
- );
- return $hooks;
- }
-
- /*
- * Registers reports metadata
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $reports = &$notification->getNotificationObject();
-
- $i = 0;
- foreach($this->reportMetadata as $report)
- {
- list( $category, $name, $apiModule, $apiAction, $columnName ) = $report;
- if($category == false) continue;
-
- $report = array(
- 'category' => Piwik_Translate($category),
- 'name' => Piwik_Translate($name),
- 'module' => $apiModule,
- 'action' => $apiAction,
- 'dimension' => Piwik_Translate($columnName),
- 'order' => $i++
- );
-
- $translation = $name.'Documentation';
- $translated = Piwik_Translate($translation, '<br />');
- if ($translated != $translation) {
- $report['documentation'] = $translated;
- }
-
- // getPlugin returns only a subset of metrics
- if($apiAction == 'getPlugin')
- {
- $report['metrics'] = array(
- 'nb_visits',
- 'nb_visits_percentage' => Piwik_Translate('General_ColumnPercentageVisits')
- );
- // There is no processedMetrics for this report
- $report['processedMetrics'] = array();
- // Always has same number of rows, 1 per plugin
- $report['constantRowsCount'] = true;
- }
- $reports[] = $report;
- }
- }
-
- /**
- * Get segments meta data
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getSegmentsMetadata($notification)
- {
- $segments =& $notification->getNotificationObject();
- foreach($this->reportMetadata as $report)
- {
- @list( $category, $name, $apiModule, $apiAction, $columnName, $segment, $sqlSegment, $acceptedValues, $sqlFilter ) = $report;
- if(empty($segment)) continue;
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => $columnName,
- 'segment' => $segment,
- 'acceptedValues' => $acceptedValues,
- 'sqlSegment' => $sqlSegment,
- 'sqlFilter' => isset($sqlFilter) ? $sqlFilter : false,
- );
- }
- }
-
- /**
- * Adds the various User Settings widgets
- */
- function addWidgets()
- {
- // in this case, Widgets have same names as API reports
- foreach($this->reportMetadata as $report)
- {
- list( $category, $name, $controllerName, $controllerAction ) = $report;
- if($category == false) continue;
- Piwik_AddWidget( $category, $name, $controllerName, $controllerAction );
- }
- }
-
- /**
- * Adds the User Settings menu
- */
- function addMenu()
- {
- Piwik_AddMenu('General_Visitors', 'UserSettings_SubmenuSettings', array('module' => 'UserSettings', 'action' => 'index'));
- }
-
- /**
- * Daily archive of User Settings report. Processes reports for Visits by Resolution,
- * by Browser, Browser family, etc. Some reports are built from the logs, some reports
- * are superset of an existing report (eg. Browser family is built from the Browser report)
- *
- * @param Piwik_Event_Notification $notification notification object
- * @return void
- */
- function archiveDay( $notification )
- {
- require_once PIWIK_INCLUDE_PATH . '/plugins/UserSettings/functions.php';
- $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
- $columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
-
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveProcessing = $archiveProcessing;
-
- $recordName = 'UserSettings_configuration';
- $labelSQL = "CONCAT(log_visit.config_os, ';', log_visit.config_browser_name, ';', log_visit.config_resolution)";
- $interestByConfiguration = $archiveProcessing->getArrayInterestForLabel($labelSQL);
-
- $tableConfiguration = $archiveProcessing->getDataTableFromArray($interestByConfiguration);
- $archiveProcessing->insertBlobRecord($recordName, $tableConfiguration->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
- destroy($tableConfiguration);
-
- $recordName = 'UserSettings_os';
- $labelSQL = "log_visit.config_os";
- $interestByOs = $archiveProcessing->getArrayInterestForLabel($labelSQL);
- $tableOs = $archiveProcessing->getDataTableFromArray($interestByOs);
- $archiveProcessing->insertBlobRecord($recordName, $tableOs->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
- destroy($tableOs);
-
- $recordName = 'UserSettings_browser';
- $labelSQL = "CONCAT(log_visit.config_browser_name, ';', log_visit.config_browser_version)";
- $interestByBrowser = $archiveProcessing->getArrayInterestForLabel($labelSQL);
- $tableBrowser = $archiveProcessing->getDataTableFromArray($interestByBrowser);
- $archiveProcessing->insertBlobRecord($recordName, $tableBrowser->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
-
- $recordName = 'UserSettings_browserType';
- $tableBrowserType = $this->getTableBrowserByType($tableBrowser);
- $archiveProcessing->insertBlobRecord($recordName, $tableBrowserType->getSerialized());
- destroy($tableBrowser);
- destroy($tableBrowserType);
-
- $recordName = 'UserSettings_resolution';
- $labelSQL = "log_visit.config_resolution";
- $interestByResolution = $archiveProcessing->getArrayInterestForLabel($labelSQL);
- $tableResolution = $archiveProcessing->getDataTableFromArray($interestByResolution);
- $tableResolution->filter('ColumnCallbackDeleteRow', array('label', 'Piwik_UserSettings_keepStrlenGreater'));
- $archiveProcessing->insertBlobRecord($recordName, $tableResolution->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
-
- $recordName = 'UserSettings_wideScreen';
- $tableWideScreen = $this->getTableWideScreen($tableResolution);
- $archiveProcessing->insertBlobRecord($recordName, $tableWideScreen->getSerialized());
- destroy($tableResolution);
- destroy($tableWideScreen);
-
- $recordName = 'UserSettings_plugin';
- $tablePlugin = $this->getDataTablePlugin();
- $archiveProcessing->insertBlobRecord($recordName, $tablePlugin->getSerialized());
- destroy($tablePlugin);
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('UserSettings_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ /*
+ * Mapping between the browser family shortcode and the displayed name
+ */
+ static public $browserType_display = array(
+ 'ie' => 'Trident (IE)',
+ 'gecko' => 'Gecko (Firefox)',
+ 'khtml' => 'KHTML (Konqueror)',
+ 'webkit' => 'WebKit (Safari, Chrome)',
+ 'opera' => 'Presto (Opera)',
+ );
+
+ /*
+ * Defines API reports.
+ * Also used to define Widgets.
+ *
+ * @array Category, Report Name, API Module, API action, Translated column name,
+ * $segment, $sqlSegment, $acceptedValues, $sqlFilter
+ */
+ protected $reportMetadata = array(
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_WidgetResolutions',
+ 'UserSettings',
+ 'getResolution',
+ 'UserSettings_ColumnResolution',
+ 'resolution',
+ 'log_visit.config_resolution',
+ '1280x1024, 800x600, etc.',
+ null,),
+
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_WidgetBrowsers',
+ 'UserSettings',
+ 'getBrowser',
+ 'UserSettings_ColumnBrowser',
+ 'browserName',
+ 'log_visit.config_browser_name',
+ 'FF, IE, CH, SF, OP, etc.',
+ null,),
+
+ // browser version
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_WidgetBrowserVersion',
+ 'UserSettings',
+ 'getBrowserVersion',
+ 'UserSettings_ColumnBrowserVersion',
+ 'browserVersion',
+ 'log_visit.config_browser_version',
+ '1.0, 8.0, etc.',
+ null,),
+
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_WidgetBrowserFamilies',
+ 'UserSettings',
+ 'getBrowserType',
+ 'UserSettings_ColumnBrowserFamily',
+ null,
+ null,
+ null,
+ null,),
+
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_WidgetPlugins',
+ 'UserSettings',
+ 'getPlugin',
+ 'UserSettings_ColumnPlugin',
+ null,
+ null,
+ null,
+ null,),
+
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_WidgetWidescreen',
+ 'UserSettings',
+ 'getWideScreen',
+ 'UserSettings_ColumnTypeOfScreen',
+ null,
+ null,
+ null,
+ null,),
+
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_WidgetOperatingSystems',
+ 'UserSettings',
+ 'getOS',
+ 'UserSettings_ColumnOperatingSystem',
+ 'operatingSystem',
+ 'log_visit.config_os',
+ 'WXP, WI7, MAC, LIN, AND, IPD, etc.',
+ null,),
+
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_WidgetGlobalVisitors',
+ 'UserSettings',
+ 'getConfiguration',
+ 'UserSettings_ColumnConfiguration',
+ null,
+ null,
+ null,
+ null),
+
+ // operating system family
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_WidgetOperatingSystemFamily',
+ 'UserSettings',
+ 'getOSFamily',
+ 'UserSettings_OperatingSystemFamily',
+ null,
+ null,
+ null,
+ null),
+
+ // device type
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_MobileVsDesktop',
+ 'UserSettings',
+ 'getMobileVsDesktop',
+ 'UserSettings_MobileVsDesktop',
+ null,
+ null,
+ null,
+ null),
+
+ // Browser language
+ array('UserSettings_VisitorSettings',
+ 'UserSettings_BrowserLanguage',
+ 'UserSettings',
+ 'getLanguage',
+ 'General_Language',
+ null,
+ null,
+ null,
+ null),
+ );
+
+ /*
+ * List of hooks
+ */
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenu',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ );
+ return $hooks;
+ }
+
+ /*
+ * Registers reports metadata
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $reports = & $notification->getNotificationObject();
+
+ $i = 0;
+ foreach ($this->reportMetadata as $report) {
+ list($category, $name, $apiModule, $apiAction, $columnName) = $report;
+ if ($category == false) continue;
+
+ $report = array(
+ 'category' => Piwik_Translate($category),
+ 'name' => Piwik_Translate($name),
+ 'module' => $apiModule,
+ 'action' => $apiAction,
+ 'dimension' => Piwik_Translate($columnName),
+ 'order' => $i++
+ );
+
+ $translation = $name . 'Documentation';
+ $translated = Piwik_Translate($translation, '<br />');
+ if ($translated != $translation) {
+ $report['documentation'] = $translated;
+ }
+
+ // getPlugin returns only a subset of metrics
+ if ($apiAction == 'getPlugin') {
+ $report['metrics'] = array(
+ 'nb_visits',
+ 'nb_visits_percentage' => Piwik_Translate('General_ColumnPercentageVisits')
+ );
+ // There is no processedMetrics for this report
+ $report['processedMetrics'] = array();
+ // Always has same number of rows, 1 per plugin
+ $report['constantRowsCount'] = true;
+ }
+ $reports[] = $report;
+ }
+ }
+
+ /**
+ * Get segments meta data
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ $segments =& $notification->getNotificationObject();
+ foreach ($this->reportMetadata as $report) {
+ @list($category, $name, $apiModule, $apiAction, $columnName, $segment, $sqlSegment, $acceptedValues, $sqlFilter) = $report;
+ if (empty($segment)) continue;
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit',
+ 'name' => $columnName,
+ 'segment' => $segment,
+ 'acceptedValues' => $acceptedValues,
+ 'sqlSegment' => $sqlSegment,
+ 'sqlFilter' => isset($sqlFilter) ? $sqlFilter : false,
+ );
+ }
+ }
+
+ /**
+ * Adds the various User Settings widgets
+ */
+ function addWidgets()
+ {
+ // in this case, Widgets have same names as API reports
+ foreach ($this->reportMetadata as $report) {
+ list($category, $name, $controllerName, $controllerAction) = $report;
+ if ($category == false) continue;
+ Piwik_AddWidget($category, $name, $controllerName, $controllerAction);
+ }
+ }
+
+ /**
+ * Adds the User Settings menu
+ */
+ function addMenu()
+ {
+ Piwik_AddMenu('General_Visitors', 'UserSettings_SubmenuSettings', array('module' => 'UserSettings', 'action' => 'index'));
+ }
+
+ /**
+ * Daily archive of User Settings report. Processes reports for Visits by Resolution,
+ * by Browser, Browser family, etc. Some reports are built from the logs, some reports
+ * are superset of an existing report (eg. Browser family is built from the Browser report)
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ * @return void
+ */
+ function archiveDay($notification)
+ {
+ require_once PIWIK_INCLUDE_PATH . '/plugins/UserSettings/functions.php';
+ $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
+ $columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
+
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $this->archiveProcessing = $archiveProcessing;
+
+ $recordName = 'UserSettings_configuration';
+ $labelSQL = "CONCAT(log_visit.config_os, ';', log_visit.config_browser_name, ';', log_visit.config_resolution)";
+ $interestByConfiguration = $archiveProcessing->getArrayInterestForLabel($labelSQL);
+
+ $tableConfiguration = $archiveProcessing->getDataTableFromArray($interestByConfiguration);
+ $archiveProcessing->insertBlobRecord($recordName, $tableConfiguration->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
+ destroy($tableConfiguration);
+
+ $recordName = 'UserSettings_os';
+ $labelSQL = "log_visit.config_os";
+ $interestByOs = $archiveProcessing->getArrayInterestForLabel($labelSQL);
+ $tableOs = $archiveProcessing->getDataTableFromArray($interestByOs);
+ $archiveProcessing->insertBlobRecord($recordName, $tableOs->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
+ destroy($tableOs);
+
+ $recordName = 'UserSettings_browser';
+ $labelSQL = "CONCAT(log_visit.config_browser_name, ';', log_visit.config_browser_version)";
+ $interestByBrowser = $archiveProcessing->getArrayInterestForLabel($labelSQL);
+ $tableBrowser = $archiveProcessing->getDataTableFromArray($interestByBrowser);
+ $archiveProcessing->insertBlobRecord($recordName, $tableBrowser->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
+
+ $recordName = 'UserSettings_browserType';
+ $tableBrowserType = $this->getTableBrowserByType($tableBrowser);
+ $archiveProcessing->insertBlobRecord($recordName, $tableBrowserType->getSerialized());
+ destroy($tableBrowser);
+ destroy($tableBrowserType);
+
+ $recordName = 'UserSettings_resolution';
+ $labelSQL = "log_visit.config_resolution";
+ $interestByResolution = $archiveProcessing->getArrayInterestForLabel($labelSQL);
+ $tableResolution = $archiveProcessing->getDataTableFromArray($interestByResolution);
+ $tableResolution->filter('ColumnCallbackDeleteRow', array('label', 'Piwik_UserSettings_keepStrlenGreater'));
+ $archiveProcessing->insertBlobRecord($recordName, $tableResolution->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
+
+ $recordName = 'UserSettings_wideScreen';
+ $tableWideScreen = $this->getTableWideScreen($tableResolution);
+ $archiveProcessing->insertBlobRecord($recordName, $tableWideScreen->getSerialized());
+ destroy($tableResolution);
+ destroy($tableWideScreen);
+
+ $recordName = 'UserSettings_plugin';
+ $tablePlugin = $this->getDataTablePlugin();
+ $archiveProcessing->insertBlobRecord($recordName, $tablePlugin->getSerialized());
+ destroy($tablePlugin);
$recordName = 'UserSettings_language';
- $tableLanguage = $this->getDataTableLanguages();
+ $tableLanguage = $this->getDataTableLanguages();
$archiveProcessing->insertBlobRecord($recordName, $tableLanguage->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
}
- /**
- * Period archiving: simply sums up daily archives
- *
- * @param Piwik_Event_Notification $notification notification object
- * @return void
- */
- function archivePeriod( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
-
- $dataTableToSum = array(
- 'UserSettings_configuration',
- 'UserSettings_os',
- 'UserSettings_browser',
- 'UserSettings_browserType',
- 'UserSettings_resolution',
- 'UserSettings_wideScreen',
- 'UserSettings_plugin',
- 'UserSettings_language',
- );
-
- $archiveProcessing->archiveDataTable($dataTableToSum, null, $maximumRowsInDataTable);
- }
-
- /**
- * Returns the report Visits by Screen type given the Resolution table
- *
- * @param Piwik_DataTable $tableResolution
- * @return Piwik_DataTable
- */
- protected function getTableWideScreen(Piwik_DataTable $tableResolution)
- {
- $nameToRow = array();
- foreach($tableResolution->getRows() as $row)
- {
- $resolution = $row->getColumn('label');
- $name = Piwik_getScreenTypeFromResolution($resolution);
- if(!isset($nameToRow[$name]))
- {
- $nameToRow[$name] = new Piwik_DataTable_Row();
- $nameToRow[$name]->addColumn('label', $name);
- }
-
- $nameToRow[$name]->sumRow( $row );
- }
- $tableWideScreen = new Piwik_DataTable();
- $tableWideScreen->addRowsFromArray($nameToRow);
-
- return $tableWideScreen;
- }
-
- /**
- * Returns the report Visits by Browser family given the Browser report
- *
- * @param Piwik_DataTable $tableBrowser
- * @return Piwik_DataTable
- */
- protected function getTableBrowserByType(Piwik_DataTable $tableBrowser)
- {
- $nameToRow = array();
- foreach($tableBrowser->getRows() as $row)
- {
- $browserLabel = $row->getColumn('label');
- $familyNameToUse = Piwik_getBrowserFamily($browserLabel);
- if(!isset($nameToRow[$familyNameToUse]))
- {
- $nameToRow[$familyNameToUse] = new Piwik_DataTable_Row();
- $nameToRow[$familyNameToUse]->addColumn('label',$familyNameToUse);
- }
- $nameToRow[$familyNameToUse]->sumRow( $row );
- }
-
- $tableBrowserType = new Piwik_DataTable();
- $tableBrowserType->addRowsFromArray($nameToRow);
- return $tableBrowserType;
- }
-
- /**
- * Returns SQL that processes stats for Plugins
- *
- * @return Piwik_DataTable_Simple
- */
- protected function getDataTablePlugin()
- {
- $toSelect = "sum(case log_visit.config_pdf when 1 then 1 else 0 end) as pdf,
+ /**
+ * Period archiving: simply sums up daily archives
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ * @return void
+ */
+ function archivePeriod($notification)
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
+
+ $dataTableToSum = array(
+ 'UserSettings_configuration',
+ 'UserSettings_os',
+ 'UserSettings_browser',
+ 'UserSettings_browserType',
+ 'UserSettings_resolution',
+ 'UserSettings_wideScreen',
+ 'UserSettings_plugin',
+ 'UserSettings_language',
+ );
+
+ $archiveProcessing->archiveDataTable($dataTableToSum, null, $maximumRowsInDataTable);
+ }
+
+ /**
+ * Returns the report Visits by Screen type given the Resolution table
+ *
+ * @param Piwik_DataTable $tableResolution
+ * @return Piwik_DataTable
+ */
+ protected function getTableWideScreen(Piwik_DataTable $tableResolution)
+ {
+ $nameToRow = array();
+ foreach ($tableResolution->getRows() as $row) {
+ $resolution = $row->getColumn('label');
+ $name = Piwik_getScreenTypeFromResolution($resolution);
+ if (!isset($nameToRow[$name])) {
+ $nameToRow[$name] = new Piwik_DataTable_Row();
+ $nameToRow[$name]->addColumn('label', $name);
+ }
+
+ $nameToRow[$name]->sumRow($row);
+ }
+ $tableWideScreen = new Piwik_DataTable();
+ $tableWideScreen->addRowsFromArray($nameToRow);
+
+ return $tableWideScreen;
+ }
+
+ /**
+ * Returns the report Visits by Browser family given the Browser report
+ *
+ * @param Piwik_DataTable $tableBrowser
+ * @return Piwik_DataTable
+ */
+ protected function getTableBrowserByType(Piwik_DataTable $tableBrowser)
+ {
+ $nameToRow = array();
+ foreach ($tableBrowser->getRows() as $row) {
+ $browserLabel = $row->getColumn('label');
+ $familyNameToUse = Piwik_getBrowserFamily($browserLabel);
+ if (!isset($nameToRow[$familyNameToUse])) {
+ $nameToRow[$familyNameToUse] = new Piwik_DataTable_Row();
+ $nameToRow[$familyNameToUse]->addColumn('label', $familyNameToUse);
+ }
+ $nameToRow[$familyNameToUse]->sumRow($row);
+ }
+
+ $tableBrowserType = new Piwik_DataTable();
+ $tableBrowserType->addRowsFromArray($nameToRow);
+ return $tableBrowserType;
+ }
+
+ /**
+ * Returns SQL that processes stats for Plugins
+ *
+ * @return Piwik_DataTable_Simple
+ */
+ protected function getDataTablePlugin()
+ {
+ $toSelect = "sum(case log_visit.config_pdf when 1 then 1 else 0 end) as pdf,
sum(case log_visit.config_flash when 1 then 1 else 0 end) as flash,
sum(case log_visit.config_java when 1 then 1 else 0 end) as java,
sum(case log_visit.config_director when 1 then 1 else 0 end) as director,
@@ -435,38 +427,35 @@ class Piwik_UserSettings extends Piwik_Plugin
sum(case log_visit.config_gears when 1 then 1 else 0 end) as gears,
sum(case log_visit.config_silverlight when 1 then 1 else 0 end) as silverlight,
sum(case log_visit.config_cookie when 1 then 1 else 0 end) as cookie ";
- return $this->archiveProcessing->getSimpleDataTableFromSelect($toSelect, Piwik_Archive::INDEX_NB_VISITS);
- }
+ return $this->archiveProcessing->getSimpleDataTableFromSelect($toSelect, Piwik_Archive::INDEX_NB_VISITS);
+ }
- protected function getDataTableLanguages()
- {
- $labelSQL = "log_visit.location_browser_lang";
- $interestByLanguage = $this->archiveProcessing->getArrayInterestForLabel($labelSQL);
+ protected function getDataTableLanguages()
+ {
+ $labelSQL = "log_visit.location_browser_lang";
+ $interestByLanguage = $this->archiveProcessing->getArrayInterestForLabel($labelSQL);
$languageCodes = array_keys(Piwik_Common::getLanguagesList());
- foreach ($interestByLanguage as $lang => $count)
- {
- // get clean language code
+ foreach ($interestByLanguage as $lang => $count) {
+ // get clean language code
$code = Piwik_Common::extractLanguageCodeFromBrowserLanguage($lang, $languageCodes);
- if ($code != $lang)
- {
- if (!array_key_exists($code, $interestByLanguage)) {
- $interestByLanguage[$code] = array();
- }
- // Add the values to the primary language
- foreach ($count as $key => $value)
- {
- if (array_key_exists($key, $interestByLanguage[$code])) {
- $interestByLanguage[$code][$key] += $value;
- } else {
- $interestByLanguage[$code][$key] = $value;
- }
- }
- unset($interestByLanguage[$lang]);
- }
- }
- $tableLanguage = $this->archiveProcessing->getDataTableFromArray($interestByLanguage);
- return $tableLanguage;
- }
+ if ($code != $lang) {
+ if (!array_key_exists($code, $interestByLanguage)) {
+ $interestByLanguage[$code] = array();
+ }
+ // Add the values to the primary language
+ foreach ($count as $key => $value) {
+ if (array_key_exists($key, $interestByLanguage[$code])) {
+ $interestByLanguage[$code][$key] += $value;
+ } else {
+ $interestByLanguage[$code][$key] = $value;
+ }
+ }
+ unset($interestByLanguage[$lang]);
+ }
+ }
+ $tableLanguage = $this->archiveProcessing->getDataTableFromArray($interestByLanguage);
+ return $tableLanguage;
+ }
}
diff --git a/plugins/UserSettings/functions.php b/plugins/UserSettings/functions.php
index 790a61f939..6e5db05428 100644
--- a/plugins/UserSettings/functions.php
+++ b/plugins/UserSettings/functions.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UserSettings
*/
@@ -13,250 +13,224 @@
* @see libs/UserAgentParser/UserAgentParser.php
*/
require_once PIWIK_INCLUDE_PATH . '/libs/UserAgentParser/UserAgentParser.php';
-
-function Piwik_getPluginsLogo( $oldLabel )
+
+function Piwik_getPluginsLogo($oldLabel)
{
- return 'plugins/UserSettings/images/plugins/'. $oldLabel . '.gif';
+ return 'plugins/UserSettings/images/plugins/' . $oldLabel . '.gif';
}
function Piwik_getOSLabel($osId)
{
- $osName = UserAgentParser::getOperatingSystemNameFromId($osId);
- if($osName !== false)
- {
- return $osName;
- }
- if( $osId == 'UNK')
- {
- return Piwik_Translate('General_Unknown');
- }
- return $osId;
+ $osName = UserAgentParser::getOperatingSystemNameFromId($osId);
+ if ($osName !== false) {
+ return $osName;
+ }
+ if ($osId == 'UNK') {
+ return Piwik_Translate('General_Unknown');
+ }
+ return $osId;
}
function Piwik_getOSShortLabel($osId)
{
- $osShortName = UserAgentParser::getOperatingSystemShortNameFromId($osId);
- if($osShortName !== false)
- {
- return $osShortName;
- }
- if( $osId == 'UNK')
- {
- return Piwik_Translate('General_Unknown');
- }
- return $osId;
+ $osShortName = UserAgentParser::getOperatingSystemShortNameFromId($osId);
+ if ($osShortName !== false) {
+ return $osShortName;
+ }
+ if ($osId == 'UNK') {
+ return Piwik_Translate('General_Unknown');
+ }
+ return $osId;
}
function Piwik_UserSettings_getOSFamily($osLabel)
{
- $osId = UserAgentParser::getOperatingSystemIdFromName($osLabel);
- $osFamily = UserAgentParser::getOperatingSystemFamilyFromId($osId);
-
- if ($osFamily == 'unknown')
- {
- $osFamily = Piwik_Translate('General_Unknown');
- }
- else if ($osFamily == 'Gaming Console')
- {
- $osFamily = Piwik_Translate('UserSettings_GamingConsole');
- }
-
- return $osFamily;
+ $osId = UserAgentParser::getOperatingSystemIdFromName($osLabel);
+ $osFamily = UserAgentParser::getOperatingSystemFamilyFromId($osId);
+
+ if ($osFamily == 'unknown') {
+ $osFamily = Piwik_Translate('General_Unknown');
+ } else if ($osFamily == 'Gaming Console') {
+ $osFamily = Piwik_Translate('UserSettings_GamingConsole');
+ }
+
+ return $osFamily;
}
function Piwik_UserSettings_getDeviceTypeFromOS($osLabel)
{
- $osId = UserAgentParser::getOperatingSystemIdFromName($osLabel);
- $osFamily = UserAgentParser::getOperatingSystemFamilyFromId($osId);
-
- // NOTE: translations done in another filter
- switch ($osFamily)
- {
- case 'Windows':
- case 'Linux':
- case 'Mac':
- case 'Unix':
- case 'Other':
- case 'Gaming Console':
- return 'General_Desktop';
- case 'iOS':
- case 'Android':
- case 'Windows Mobile':
- case 'Other Mobile':
- case 'Mobile Gaming Console':
- return 'General_Mobile';
- default:
- return 'General_Unknown';
- }
+ $osId = UserAgentParser::getOperatingSystemIdFromName($osLabel);
+ $osFamily = UserAgentParser::getOperatingSystemFamilyFromId($osId);
+
+ // NOTE: translations done in another filter
+ switch ($osFamily) {
+ case 'Windows':
+ case 'Linux':
+ case 'Mac':
+ case 'Unix':
+ case 'Other':
+ case 'Gaming Console':
+ return 'General_Desktop';
+ case 'iOS':
+ case 'Android':
+ case 'Windows Mobile':
+ case 'Other Mobile':
+ case 'Mobile Gaming Console':
+ return 'General_Mobile';
+ default:
+ return 'General_Unknown';
+ }
}
function Piwik_getBrowserTypeLabel($oldLabel)
{
- if(isset(Piwik_UserSettings::$browserType_display[$oldLabel]))
- {
- return Piwik_UserSettings::$browserType_display[$oldLabel];
- }
- if($oldLabel == 'unknown')
- {
- return Piwik_Translate('General_Unknown');
- }
- return $oldLabel;
+ if (isset(Piwik_UserSettings::$browserType_display[$oldLabel])) {
+ return Piwik_UserSettings::$browserType_display[$oldLabel];
+ }
+ if ($oldLabel == 'unknown') {
+ return Piwik_Translate('General_Unknown');
+ }
+ return $oldLabel;
}
function Piwik_getConfigurationLabel($str)
{
- if(strpos($str, ';') === false)
- {
- return $str;
- }
- $values = explode(";", $str);
-
- $os = Piwik_getOSLabel($values[0]);
- $name = $values[1];
- $browser = UserAgentParser::getBrowserNameFromId($name);
- if($browser === false)
- {
- $browser = Piwik_Translate('General_Unknown');
- }
- $resolution = $values[2];
- return $os . " / " . $browser . " / " . $resolution;
+ if (strpos($str, ';') === false) {
+ return $str;
+ }
+ $values = explode(";", $str);
+
+ $os = Piwik_getOSLabel($values[0]);
+ $name = $values[1];
+ $browser = UserAgentParser::getBrowserNameFromId($name);
+ if ($browser === false) {
+ $browser = Piwik_Translate('General_Unknown');
+ }
+ $resolution = $values[2];
+ return $os . " / " . $browser . " / " . $resolution;
}
function Piwik_getBrowserLabel($oldLabel)
{
- $browserId = Piwik_getBrowserId($oldLabel);
- $version = Piwik_getBrowserVersion($oldLabel);
- $browserName = UserAgentParser::getBrowserNameFromId($browserId);
- if( $browserName !== false)
- {
- return $browserName . " ". $version;
- }
- if( $browserId == 'UNK')
- {
- return Piwik_Translate('General_Unknown');
- }
- return $oldLabel;
+ $browserId = Piwik_getBrowserId($oldLabel);
+ $version = Piwik_getBrowserVersion($oldLabel);
+ $browserName = UserAgentParser::getBrowserNameFromId($browserId);
+ if ($browserName !== false) {
+ return $browserName . " " . $version;
+ }
+ if ($browserId == 'UNK') {
+ return Piwik_Translate('General_Unknown');
+ }
+ return $oldLabel;
}
function Piwik_getBrowserShortLabel($oldLabel)
{
- $browserId = Piwik_getBrowserId($oldLabel);
- $version = Piwik_getBrowserVersion($oldLabel);
- $browserName = UserAgentParser::getBrowserShortNameFromId($browserId);
- if( $browserName !== false)
- {
- return $browserName . " ". $version;
- }
- if( $browserId == 'UNK')
- {
- return Piwik_Translate('General_Unknown');
- }
- return $oldLabel;
+ $browserId = Piwik_getBrowserId($oldLabel);
+ $version = Piwik_getBrowserVersion($oldLabel);
+ $browserName = UserAgentParser::getBrowserShortNameFromId($browserId);
+ if ($browserName !== false) {
+ return $browserName . " " . $version;
+ }
+ if ($browserId == 'UNK') {
+ return Piwik_Translate('General_Unknown');
+ }
+ return $oldLabel;
}
function Piwik_getBrowserId($str)
{
- return substr($str, 0, strpos($str, ';'));
+ return substr($str, 0, strpos($str, ';'));
}
function Piwik_getBrowserVersion($str)
{
- return substr($str, strpos($str, ';') + 1);
+ return substr($str, strpos($str, ';') + 1);
}
function Piwik_getBrowsersLogo($label)
{
- $id = Piwik_getBrowserId($label);
- // For aggregated row 'Others'
- if(empty($id)) {
- $id = 'UNK';
- }
- return 'plugins/UserSettings/images/browsers/'. $id . '.gif';
+ $id = Piwik_getBrowserId($label);
+ // For aggregated row 'Others'
+ if (empty($id)) {
+ $id = 'UNK';
+ }
+ return 'plugins/UserSettings/images/browsers/' . $id . '.gif';
}
function Piwik_getOSLogo($label)
{
- // For aggregated row 'Others'
- if(empty($label)) {
- $label = 'UNK';
- }
- $path = 'plugins/UserSettings/images/os/'. $label . '.gif';
- return $path;
+ // For aggregated row 'Others'
+ if (empty($label)) {
+ $label = 'UNK';
+ }
+ $path = 'plugins/UserSettings/images/os/' . $label . '.gif';
+ return $path;
}
function Piwik_getScreensLogo($label)
{
- return 'plugins/UserSettings/images/screens/' . $label . '.gif';
+ return 'plugins/UserSettings/images/screens/' . $label . '.gif';
}
-function Piwik_UserSettings_getDeviceTypeImg( $oldOSImage, $osFamilyLabel )
+function Piwik_UserSettings_getDeviceTypeImg($oldOSImage, $osFamilyLabel)
{
- switch ($osFamilyLabel)
- {
- case 'General_Desktop':
- return 'plugins/UserSettings/images/screens/normal.gif';
- case 'General_Mobile':
- return 'plugins/UserSettings/images/screens/mobile.gif';
- case 'General_Unknown':
- default:
- return 'plugins/UserSettings/images/os/UNK.gif';
- }
+ switch ($osFamilyLabel) {
+ case 'General_Desktop':
+ return 'plugins/UserSettings/images/screens/normal.gif';
+ case 'General_Mobile':
+ return 'plugins/UserSettings/images/screens/mobile.gif';
+ case 'General_Unknown':
+ default:
+ return 'plugins/UserSettings/images/os/UNK.gif';
+ }
}
function Piwik_UserSettings_keepStrlenGreater($value)
{
- return strlen($value) > 5;
+ return strlen($value) > 5;
}
function Piwik_getScreenTypeFromResolution($resolution)
{
- if($resolution === 'unknown')
- {
- return $resolution;
- }
-
- $width = intval(substr($resolution, 0, strpos($resolution, 'x')));
- $height= intval(substr($resolution, strpos($resolution, 'x') + 1));
- $ratio = Piwik::secureDiv($width, $height);
-
- if($width < 640)
- {
- $name = 'mobile';
- }
- elseif($ratio < 1.4)
- {
- $name = 'normal';
- }
- else if($ratio < 2)
- {
- $name = 'wide';
- }
- else
- {
- $name = 'dual';
- }
- return $name;
+ if ($resolution === 'unknown') {
+ return $resolution;
+ }
+
+ $width = intval(substr($resolution, 0, strpos($resolution, 'x')));
+ $height = intval(substr($resolution, strpos($resolution, 'x') + 1));
+ $ratio = Piwik::secureDiv($width, $height);
+
+ if ($width < 640) {
+ $name = 'mobile';
+ } elseif ($ratio < 1.4) {
+ $name = 'normal';
+ } else if ($ratio < 2) {
+ $name = 'wide';
+ } else {
+ $name = 'dual';
+ }
+ return $name;
}
function Piwik_getBrowserFamily($browserLabel)
{
- $familyNameToUse = UserAgentParser::getBrowserFamilyFromId(substr($browserLabel, 0, 2));
- return $familyNameToUse;
+ $familyNameToUse = UserAgentParser::getBrowserFamilyFromId(substr($browserLabel, 0, 2));
+ return $familyNameToUse;
}
/**
* Extracts the browser name from a string with the browser name and version.
*/
-function Piwik_UserSettings_getBrowserFromBrowserVersion( $browserWithVersion )
+function Piwik_UserSettings_getBrowserFromBrowserVersion($browserWithVersion)
{
- if (preg_match("/(.+) [0-9]+(?:\.[0-9]+)?$/", $browserWithVersion, $matches) === 0)
- {
- return $browserWithVersion;
- }
-
- return $matches[1];
+ if (preg_match("/(.+) [0-9]+(?:\.[0-9]+)?$/", $browserWithVersion, $matches) === 0) {
+ return $browserWithVersion;
+ }
+
+ return $matches[1];
}
/**
diff --git a/plugins/UserSettings/templates/index.tpl b/plugins/UserSettings/templates/index.tpl
index 0532de2f72..830e77e0ff 100644
--- a/plugins/UserSettings/templates/index.tpl
+++ b/plugins/UserSettings/templates/index.tpl
@@ -1,27 +1,27 @@
<div id='leftcolumn'>
- <h2>{'UserSettings_BrowserFamilies'|translate}</h2>
- {$dataTableBrowserType}
-
- <h2>{'UserSettings_Browsers'|translate}</h2>
- {$dataTableBrowser}
-
- <h2>{'UserSettings_Plugins'|translate}</h2>
- {$dataTablePlugin}
+ <h2>{'UserSettings_BrowserFamilies'|translate}</h2>
+ {$dataTableBrowserType}
+
+ <h2>{'UserSettings_Browsers'|translate}</h2>
+ {$dataTableBrowser}
+
+ <h2>{'UserSettings_Plugins'|translate}</h2>
+ {$dataTablePlugin}
</div>
<div id='rightcolumn'>
- <h2>{'UserSettings_Configurations'|translate}</h2>
- {$dataTableConfiguration}
-
- <h2>{'UserSettings_OperatingSystems'|translate}</h2>
- {$dataTableOS}
-
- <h2>{'UserSettings_Resolutions'|translate}</h2>
- {$dataTableResolution}
-
- <h2>{'UserSettings_MobileVsDesktop'|translate}</h2>
- {$dataTableMobileVsDesktop}
+ <h2>{'UserSettings_Configurations'|translate}</h2>
+ {$dataTableConfiguration}
+
+ <h2>{'UserSettings_OperatingSystems'|translate}</h2>
+ {$dataTableOS}
+
+ <h2>{'UserSettings_Resolutions'|translate}</h2>
+ {$dataTableResolution}
+
+ <h2>{'UserSettings_MobileVsDesktop'|translate}</h2>
+ {$dataTableMobileVsDesktop}
- <h2>{'UserSettings_BrowserLanguage'|translate}</h2>
- {$dataTableBrowserLanguage}
+ <h2>{'UserSettings_BrowserLanguage'|translate}</h2>
+ {$dataTableBrowserLanguage}
</div>
diff --git a/plugins/UsersManager/API.php b/plugins/UsersManager/API.php
index a5e58a703e..340fb97961 100644
--- a/plugins/UsersManager/API.php
+++ b/plugins/UsersManager/API.php
@@ -1,698 +1,661 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UsersManager
*/
/**
* The UsersManager API lets you Manage Users and their permissions to access specific websites.
- *
+ *
* You can create users via "addUser", update existing users via "updateUser" and delete users via "deleteUser".
- * There are many ways to list users based on their login "getUser" and "getUsers", their email "getUserByEmail",
+ * There are many ways to list users based on their login "getUser" and "getUsers", their email "getUserByEmail",
* or which users have permission (view or admin) to access the specified websites "getUsersWithSiteAccess".
- *
- * Existing Permissions are listed given a login via "getSitesAccessFromUser", or a website ID via "getUsersAccessFromSite",
+ *
+ * Existing Permissions are listed given a login via "getSitesAccessFromUser", or a website ID via "getUsersAccessFromSite",
* or you can list all users and websites for a given permission via "getUsersSitesFromAccess". Permissions are set and updated
* via the method "setUserAccess".
* See also the documentation about <a href='http://piwik.org/docs/manage-users/' target='_blank'>Managing Users</a> in Piwik.
* @package Piwik_UsersManager
*/
-class Piwik_UsersManager_API
+class Piwik_UsersManager_API
{
- static private $instance = null;
-
- /**
- * You can create your own Users Plugin to override this class.
- * Example of how you would overwrite the UsersManager_API with your own class:
- * Call the following in your plugin __construct() for example:
- *
- * Zend_Registry::set('UsersManager_API',Piwik_MyCustomUsersManager_API::getInstance());
- *
- * @throws Exception
- * @return Piwik_UsersManager_API
- */
- static public function getInstance()
- {
- try {
- $instance = Zend_Registry::get('UsersManager_API');
- if( !($instance instanceof Piwik_UsersManager_API) ) {
- // Exception is caught below and corrected
- throw new Exception('UsersManager_API must inherit Piwik_UsersManager_API');
- }
- self::$instance = $instance;
- }
- catch (Exception $e) {
- self::$instance = new self;
- Zend_Registry::set('UsersManager_API', self::$instance);
- }
- return self::$instance;
- }
- const PREFERENCE_DEFAULT_REPORT = 'defaultReport';
- const PREFERENCE_DEFAULT_REPORT_DATE = 'defaultReportDate';
-
- /**
- * Sets a user preference
- * @param string $userLogin
- * @param string $preferenceName
- * @param string $preferenceValue
- * @return void
- */
- public function setUserPreference($userLogin, $preferenceName, $preferenceValue)
- {
- Piwik::checkUserIsSuperUserOrTheUser($userLogin);
- Piwik_SetOption($this->getPreferenceId($userLogin, $preferenceName), $preferenceValue);
- }
-
- /**
- * Gets a user preference
- * @param string $userLogin
- * @param string $preferenceName
- * @return bool|string
- */
- public function getUserPreference($userLogin, $preferenceName)
- {
- Piwik::checkUserIsSuperUserOrTheUser($userLogin);
- return Piwik_GetOption($this->getPreferenceId($userLogin, $preferenceName));
- }
-
- private function getPreferenceId($login, $preference)
- {
- return $login . '_' . $preference;
- }
-
- /**
- * Returns the list of all the users
- *
- * @param string $userLogins Comma separated list of users to select. If not specified, will return all users
- * @return array the list of all the users
- */
- public function getUsers( $userLogins = '' )
- {
- Piwik::checkUserHasSomeAdminAccess();
-
- $where = '';
- $bind = array();
- if(!empty($userLogins))
- {
- $userLogins = explode(',', $userLogins);
- $where = 'WHERE login IN ('. Piwik_Common::getSqlStringFieldsArray($userLogins).')';
- $bind = $userLogins;
- }
- $db = Zend_Registry::get('db');
- $users = $db->fetchAll("SELECT *
- FROM ".Piwik_Common::prefixTable("user")."
+ static private $instance = null;
+
+ /**
+ * You can create your own Users Plugin to override this class.
+ * Example of how you would overwrite the UsersManager_API with your own class:
+ * Call the following in your plugin __construct() for example:
+ *
+ * Zend_Registry::set('UsersManager_API',Piwik_MyCustomUsersManager_API::getInstance());
+ *
+ * @throws Exception
+ * @return Piwik_UsersManager_API
+ */
+ static public function getInstance()
+ {
+ try {
+ $instance = Zend_Registry::get('UsersManager_API');
+ if (!($instance instanceof Piwik_UsersManager_API)) {
+ // Exception is caught below and corrected
+ throw new Exception('UsersManager_API must inherit Piwik_UsersManager_API');
+ }
+ self::$instance = $instance;
+ } catch (Exception $e) {
+ self::$instance = new self;
+ Zend_Registry::set('UsersManager_API', self::$instance);
+ }
+ return self::$instance;
+ }
+
+ const PREFERENCE_DEFAULT_REPORT = 'defaultReport';
+ const PREFERENCE_DEFAULT_REPORT_DATE = 'defaultReportDate';
+
+ /**
+ * Sets a user preference
+ * @param string $userLogin
+ * @param string $preferenceName
+ * @param string $preferenceValue
+ * @return void
+ */
+ public function setUserPreference($userLogin, $preferenceName, $preferenceValue)
+ {
+ Piwik::checkUserIsSuperUserOrTheUser($userLogin);
+ Piwik_SetOption($this->getPreferenceId($userLogin, $preferenceName), $preferenceValue);
+ }
+
+ /**
+ * Gets a user preference
+ * @param string $userLogin
+ * @param string $preferenceName
+ * @return bool|string
+ */
+ public function getUserPreference($userLogin, $preferenceName)
+ {
+ Piwik::checkUserIsSuperUserOrTheUser($userLogin);
+ return Piwik_GetOption($this->getPreferenceId($userLogin, $preferenceName));
+ }
+
+ private function getPreferenceId($login, $preference)
+ {
+ return $login . '_' . $preference;
+ }
+
+ /**
+ * Returns the list of all the users
+ *
+ * @param string $userLogins Comma separated list of users to select. If not specified, will return all users
+ * @return array the list of all the users
+ */
+ public function getUsers($userLogins = '')
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+
+ $where = '';
+ $bind = array();
+ if (!empty($userLogins)) {
+ $userLogins = explode(',', $userLogins);
+ $where = 'WHERE login IN (' . Piwik_Common::getSqlStringFieldsArray($userLogins) . ')';
+ $bind = $userLogins;
+ }
+ $db = Zend_Registry::get('db');
+ $users = $db->fetchAll("SELECT *
+ FROM " . Piwik_Common::prefixTable("user") . "
$where
ORDER BY login ASC", $bind);
- // Non Super user can only access login & alias
- if(!Piwik::isUserIsSuperUser())
- {
- foreach($users as &$user)
- {
- $user = array('login' => $user['login'], 'alias' => $user['alias'] );
- }
- }
- return $users;
- }
-
- /**
- * Returns the list of all the users login
- *
- * @return array the list of all the users login
- */
- public function getUsersLogin()
- {
- Piwik::checkUserHasSomeAdminAccess();
-
- $db = Zend_Registry::get('db');
- $users = $db->fetchAll("SELECT login
- FROM ".Piwik_Common::prefixTable("user")."
+ // Non Super user can only access login & alias
+ if (!Piwik::isUserIsSuperUser()) {
+ foreach ($users as &$user) {
+ $user = array('login' => $user['login'], 'alias' => $user['alias']);
+ }
+ }
+ return $users;
+ }
+
+ /**
+ * Returns the list of all the users login
+ *
+ * @return array the list of all the users login
+ */
+ public function getUsersLogin()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+
+ $db = Zend_Registry::get('db');
+ $users = $db->fetchAll("SELECT login
+ FROM " . Piwik_Common::prefixTable("user") . "
ORDER BY login ASC");
- $return = array();
- foreach($users as $login)
- {
- $return[] = $login['login'];
- }
- return $return;
- }
-
- /**
- * For each user, returns the list of website IDs where the user has the supplied $access level.
- * If a user doesn't have the given $access to any website IDs,
- * the user will not be in the returned array.
- *
- * @param string Access can have the following values : 'view' or 'admin'
- *
- * @return array The returned array has the format
- * array(
- * login1 => array ( idsite1,idsite2),
- * login2 => array(idsite2),
- * ...
- * )
- *
- */
- public function getUsersSitesFromAccess( $access )
- {
- Piwik::checkUserIsSuperUser();
-
- $this->checkAccessType($access);
-
- $db = Zend_Registry::get('db');
- $users = $db->fetchAll("SELECT login,idsite
- FROM ".Piwik_Common::prefixTable("access")
- ." WHERE access = ?
+ $return = array();
+ foreach ($users as $login) {
+ $return[] = $login['login'];
+ }
+ return $return;
+ }
+
+ /**
+ * For each user, returns the list of website IDs where the user has the supplied $access level.
+ * If a user doesn't have the given $access to any website IDs,
+ * the user will not be in the returned array.
+ *
+ * @param string Access can have the following values : 'view' or 'admin'
+ *
+ * @return array The returned array has the format
+ * array(
+ * login1 => array ( idsite1,idsite2),
+ * login2 => array(idsite2),
+ * ...
+ * )
+ *
+ */
+ public function getUsersSitesFromAccess($access)
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $this->checkAccessType($access);
+
+ $db = Zend_Registry::get('db');
+ $users = $db->fetchAll("SELECT login,idsite
+ FROM " . Piwik_Common::prefixTable("access")
+ . " WHERE access = ?
ORDER BY login, idsite", $access);
- $return = array();
- foreach($users as $user)
- {
- $return[$user['login']][] = $user['idsite'];
- }
- return $return;
-
- }
-
- /**
- * For each user, returns his access level for the given $idSite.
- * If a user doesn't have any access to the $idSite ('noaccess'),
- * the user will not be in the returned array.
- *
- * @param string website ID
- *
- * @return array The returned array has the format
- * array(
- * login1 => 'view',
- * login2 => 'admin',
- * login3 => 'view',
- * ...
- * )
- */
- public function getUsersAccessFromSite( $idSite )
- {
- Piwik::checkUserHasAdminAccess( $idSite );
-
- $db = Zend_Registry::get('db');
- $users = $db->fetchAll("SELECT login,access
- FROM ".Piwik_Common::prefixTable("access")
- ." WHERE idsite = ?", $idSite);
- $return = array();
- foreach($users as $user)
- {
- $return[$user['login']] = $user['access'];
- }
- return $return;
- }
-
- public function getUsersWithSiteAccess( $idSite, $access )
- {
- Piwik::checkUserHasAdminAccess( $idSite );
- $this->checkAccessType( $access );
-
- $db = Zend_Registry::get('db');
- $users = $db->fetchAll("SELECT login
- FROM ".Piwik_Common::prefixTable("access")
- ." WHERE idsite = ? AND access = ?", array($idSite, $access));
- $logins = array();
- foreach($users as $user)
- {
- $logins[] = $user['login'];
- }
- if(empty($logins))
- {
- return array();
- }
- $logins = implode(',', $logins);
- return $this->getUsers($logins);
- }
-
- /**
- * For each website ID, returns the access level of the given $userLogin.
- * If the user doesn't have any access to a website ('noaccess'),
- * this website will not be in the returned array.
- * If the user doesn't have any access, the returned array will be an empty array.
- *
- * @param string User that has to be valid
- *
- * @return array The returned array has the format
- * array(
- * idsite1 => 'view',
- * idsite2 => 'admin',
- * idsite3 => 'view',
- * ...
- * )
- */
- public function getSitesAccessFromUser( $userLogin )
- {
- Piwik::checkUserIsSuperUser();
- $this->checkUserExists($userLogin);
- $this->checkUserIsNotSuperUser($userLogin);
-
- $db = Zend_Registry::get('db');
- $users = $db->fetchAll("SELECT idsite,access
- FROM ".Piwik_Common::prefixTable("access")
- ." WHERE login = ?", $userLogin);
- $return = array();
- foreach($users as $user)
- {
- $return[] = array(
- 'site' => $user['idsite'],
- 'access' => $user['access'],
- );
- }
- return $return;
- }
-
- /**
- * Returns the user information (login, password md5, alias, email, date_registered, etc.)
- *
- * @param string the user login
- *
- * @return array the user information
- */
- public function getUser( $userLogin )
- {
- Piwik::checkUserIsSuperUserOrTheUser($userLogin);
- $this->checkUserExists($userLogin);
- $this->checkUserIsNotSuperUser($userLogin);
-
- $db = Zend_Registry::get('db');
- $user = $db->fetchRow("SELECT *
- FROM ".Piwik_Common::prefixTable("user")
- ." WHERE login = ?", $userLogin);
- return $user;
- }
-
- /**
- * Returns the user information (login, password md5, alias, email, date_registered, etc.)
- *
- * @param string the user email
- *
- * @return array the user information
- */
- public function getUserByEmail( $userEmail )
- {
- Piwik::checkUserIsSuperUser();
- $this->checkUserEmailExists($userEmail);
-
- $db = Zend_Registry::get('db');
- $user = $db->fetchRow("SELECT *
- FROM ".Piwik_Common::prefixTable("user")
- ." WHERE email = ?", $userEmail);
- return $user;
- }
-
- private function checkLogin($userLogin)
- {
- if($this->userExists($userLogin))
- {
- throw new Exception(Piwik_TranslateException('UsersManager_ExceptionLoginExists', $userLogin));
- }
-
- Piwik::checkValidLoginString($userLogin);
- }
-
- private function checkEmail($email)
- {
- if($this->userEmailExists($email))
- {
- throw new Exception(Piwik_TranslateException('UsersManager_ExceptionEmailExists', $email));
- }
-
- if(!Piwik::isValidEmailString($email))
- {
- throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidEmail'));
- }
- }
-
- private function getCleanAlias($alias,$userLogin)
- {
- if(empty($alias))
- {
- $alias = $userLogin;
- }
- return $alias;
- }
-
- /**
- * Add a user in the database.
- * A user is defined by
- * - a login that has to be unique and valid
- * - a password that has to be valid
- * - an alias
- * - an email that has to be in a correct format
- *
- * @see userExists()
- * @see isValidLoginString()
- * @see isValidPasswordString()
- * @see isValidEmailString()
- *
- * @exception in case of an invalid parameter
- */
- public function addUser( $userLogin, $password, $email, $alias = false )
- {
- Piwik::checkUserIsSuperUser();
-
- $this->checkLogin($userLogin);
- $this->checkUserIsNotSuperUser($userLogin);
- $this->checkEmail($email);
-
- $password = Piwik_Common::unsanitizeInputValue($password);
- Piwik_UsersManager::checkPassword($password);
-
- $alias = $this->getCleanAlias($alias,$userLogin);
- $passwordTransformed = Piwik_UsersManager::getPasswordHash($password);
-
- $token_auth = $this->getTokenAuth($userLogin, $passwordTransformed);
-
- $db = Zend_Registry::get('db');
-
- $db->insert( Piwik_Common::prefixTable("user"), array(
- 'login' => $userLogin,
- 'password' => $passwordTransformed,
- 'alias' => $alias,
- 'email' => $email,
- 'token_auth' => $token_auth,
- 'date_registered' => Piwik_Date::now()->getDatetime()
- )
- );
-
- // we reload the access list which doesn't yet take in consideration this new user
- Zend_Registry::get('access')->reloadAccess();
- Piwik_Tracker_Cache::deleteTrackerCache();
-
- Piwik_PostEvent('UsersManager.addUser', $userLogin);
- }
-
- /**
- * Updates a user in the database.
- * Only login and password are required (case when we update the password).
- * When the password changes, the key token for this user will change, which could break
- * its API calls.
- *
- * @see addUser() for all the parameters
- */
- public function updateUser( $userLogin, $password = false, $email = false, $alias = false,
- $_isPasswordHashed = false )
- {
- Piwik::checkUserIsSuperUserOrTheUser($userLogin);
- $this->checkUserIsNotAnonymous( $userLogin );
- $this->checkUserIsNotSuperUser($userLogin);
- $userInfo = $this->getUser($userLogin);
-
- if(empty($password))
- {
- $password = $userInfo['password'];
- }
- else
- {
- $password = Piwik_Common::unsanitizeInputValue($password);
- if (!$_isPasswordHashed)
- {
- Piwik_UsersManager::checkPassword($password);
- $password = Piwik_UsersManager::getPasswordHash($password);
- }
- }
-
- if(empty($alias))
- {
- $alias = $userInfo['alias'];
- }
-
- if(empty($email))
- {
- $email = $userInfo['email'];
- }
-
- if($email != $userInfo['email'])
- {
- $this->checkEmail($email);
- }
-
- $alias = $this->getCleanAlias($alias,$userLogin);
- $token_auth = $this->getTokenAuth($userLogin,$password);
-
- $db = Zend_Registry::get('db');
-
- $db->update( Piwik_Common::prefixTable("user"),
- array(
- 'password' => $password,
- 'alias' => $alias,
- 'email' => $email,
- 'token_auth' => $token_auth,
- ),
- "login = '$userLogin'"
- );
- Piwik_Tracker_Cache::deleteTrackerCache();
-
- Piwik_PostEvent('UsersManager.updateUser', $userLogin);
- }
-
- /**
- * Delete a user and all its access, given its login.
- *
- * @param string $userLogin the user login.
- *
- * @throws Exception if the user doesn't exist
- *
- * @return bool true on success
- */
- public function deleteUser( $userLogin )
- {
- Piwik::checkUserIsSuperUser();
- $this->checkUserIsNotAnonymous( $userLogin );
- $this->checkUserIsNotSuperUser($userLogin);
- if(!$this->userExists($userLogin))
- {
- throw new Exception(Piwik_TranslateException("UsersManager_ExceptionDeleteDoesNotExist", $userLogin));
- }
-
- $this->deleteUserOnly( $userLogin );
- $this->deleteUserAccess( $userLogin );
- Piwik_Tracker_Cache::deleteTrackerCache();
- }
-
- /**
- * Returns true if the given userLogin is known in the database
- *
- * @return bool true if the user is known
- */
- public function userExists( $userLogin )
- {
- $count = Piwik_FetchOne("SELECT count(*)
- FROM ".Piwik_Common::prefixTable("user"). "
+ $return = array();
+ foreach ($users as $user) {
+ $return[$user['login']][] = $user['idsite'];
+ }
+ return $return;
+
+ }
+
+ /**
+ * For each user, returns his access level for the given $idSite.
+ * If a user doesn't have any access to the $idSite ('noaccess'),
+ * the user will not be in the returned array.
+ *
+ * @param string website ID
+ *
+ * @return array The returned array has the format
+ * array(
+ * login1 => 'view',
+ * login2 => 'admin',
+ * login3 => 'view',
+ * ...
+ * )
+ */
+ public function getUsersAccessFromSite($idSite)
+ {
+ Piwik::checkUserHasAdminAccess($idSite);
+
+ $db = Zend_Registry::get('db');
+ $users = $db->fetchAll("SELECT login,access
+ FROM " . Piwik_Common::prefixTable("access")
+ . " WHERE idsite = ?", $idSite);
+ $return = array();
+ foreach ($users as $user) {
+ $return[$user['login']] = $user['access'];
+ }
+ return $return;
+ }
+
+ public function getUsersWithSiteAccess($idSite, $access)
+ {
+ Piwik::checkUserHasAdminAccess($idSite);
+ $this->checkAccessType($access);
+
+ $db = Zend_Registry::get('db');
+ $users = $db->fetchAll("SELECT login
+ FROM " . Piwik_Common::prefixTable("access")
+ . " WHERE idsite = ? AND access = ?", array($idSite, $access));
+ $logins = array();
+ foreach ($users as $user) {
+ $logins[] = $user['login'];
+ }
+ if (empty($logins)) {
+ return array();
+ }
+ $logins = implode(',', $logins);
+ return $this->getUsers($logins);
+ }
+
+ /**
+ * For each website ID, returns the access level of the given $userLogin.
+ * If the user doesn't have any access to a website ('noaccess'),
+ * this website will not be in the returned array.
+ * If the user doesn't have any access, the returned array will be an empty array.
+ *
+ * @param string User that has to be valid
+ *
+ * @return array The returned array has the format
+ * array(
+ * idsite1 => 'view',
+ * idsite2 => 'admin',
+ * idsite3 => 'view',
+ * ...
+ * )
+ */
+ public function getSitesAccessFromUser($userLogin)
+ {
+ Piwik::checkUserIsSuperUser();
+ $this->checkUserExists($userLogin);
+ $this->checkUserIsNotSuperUser($userLogin);
+
+ $db = Zend_Registry::get('db');
+ $users = $db->fetchAll("SELECT idsite,access
+ FROM " . Piwik_Common::prefixTable("access")
+ . " WHERE login = ?", $userLogin);
+ $return = array();
+ foreach ($users as $user) {
+ $return[] = array(
+ 'site' => $user['idsite'],
+ 'access' => $user['access'],
+ );
+ }
+ return $return;
+ }
+
+ /**
+ * Returns the user information (login, password md5, alias, email, date_registered, etc.)
+ *
+ * @param string the user login
+ *
+ * @return array the user information
+ */
+ public function getUser($userLogin)
+ {
+ Piwik::checkUserIsSuperUserOrTheUser($userLogin);
+ $this->checkUserExists($userLogin);
+ $this->checkUserIsNotSuperUser($userLogin);
+
+ $db = Zend_Registry::get('db');
+ $user = $db->fetchRow("SELECT *
+ FROM " . Piwik_Common::prefixTable("user")
+ . " WHERE login = ?", $userLogin);
+ return $user;
+ }
+
+ /**
+ * Returns the user information (login, password md5, alias, email, date_registered, etc.)
+ *
+ * @param string the user email
+ *
+ * @return array the user information
+ */
+ public function getUserByEmail($userEmail)
+ {
+ Piwik::checkUserIsSuperUser();
+ $this->checkUserEmailExists($userEmail);
+
+ $db = Zend_Registry::get('db');
+ $user = $db->fetchRow("SELECT *
+ FROM " . Piwik_Common::prefixTable("user")
+ . " WHERE email = ?", $userEmail);
+ return $user;
+ }
+
+ private function checkLogin($userLogin)
+ {
+ if ($this->userExists($userLogin)) {
+ throw new Exception(Piwik_TranslateException('UsersManager_ExceptionLoginExists', $userLogin));
+ }
+
+ Piwik::checkValidLoginString($userLogin);
+ }
+
+ private function checkEmail($email)
+ {
+ if ($this->userEmailExists($email)) {
+ throw new Exception(Piwik_TranslateException('UsersManager_ExceptionEmailExists', $email));
+ }
+
+ if (!Piwik::isValidEmailString($email)) {
+ throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidEmail'));
+ }
+ }
+
+ private function getCleanAlias($alias, $userLogin)
+ {
+ if (empty($alias)) {
+ $alias = $userLogin;
+ }
+ return $alias;
+ }
+
+ /**
+ * Add a user in the database.
+ * A user is defined by
+ * - a login that has to be unique and valid
+ * - a password that has to be valid
+ * - an alias
+ * - an email that has to be in a correct format
+ *
+ * @see userExists()
+ * @see isValidLoginString()
+ * @see isValidPasswordString()
+ * @see isValidEmailString()
+ *
+ * @exception in case of an invalid parameter
+ */
+ public function addUser($userLogin, $password, $email, $alias = false)
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $this->checkLogin($userLogin);
+ $this->checkUserIsNotSuperUser($userLogin);
+ $this->checkEmail($email);
+
+ $password = Piwik_Common::unsanitizeInputValue($password);
+ Piwik_UsersManager::checkPassword($password);
+
+ $alias = $this->getCleanAlias($alias, $userLogin);
+ $passwordTransformed = Piwik_UsersManager::getPasswordHash($password);
+
+ $token_auth = $this->getTokenAuth($userLogin, $passwordTransformed);
+
+ $db = Zend_Registry::get('db');
+
+ $db->insert(Piwik_Common::prefixTable("user"), array(
+ 'login' => $userLogin,
+ 'password' => $passwordTransformed,
+ 'alias' => $alias,
+ 'email' => $email,
+ 'token_auth' => $token_auth,
+ 'date_registered' => Piwik_Date::now()->getDatetime()
+ )
+ );
+
+ // we reload the access list which doesn't yet take in consideration this new user
+ Zend_Registry::get('access')->reloadAccess();
+ Piwik_Tracker_Cache::deleteTrackerCache();
+
+ Piwik_PostEvent('UsersManager.addUser', $userLogin);
+ }
+
+ /**
+ * Updates a user in the database.
+ * Only login and password are required (case when we update the password).
+ * When the password changes, the key token for this user will change, which could break
+ * its API calls.
+ *
+ * @see addUser() for all the parameters
+ */
+ public function updateUser($userLogin, $password = false, $email = false, $alias = false,
+ $_isPasswordHashed = false)
+ {
+ Piwik::checkUserIsSuperUserOrTheUser($userLogin);
+ $this->checkUserIsNotAnonymous($userLogin);
+ $this->checkUserIsNotSuperUser($userLogin);
+ $userInfo = $this->getUser($userLogin);
+
+ if (empty($password)) {
+ $password = $userInfo['password'];
+ } else {
+ $password = Piwik_Common::unsanitizeInputValue($password);
+ if (!$_isPasswordHashed) {
+ Piwik_UsersManager::checkPassword($password);
+ $password = Piwik_UsersManager::getPasswordHash($password);
+ }
+ }
+
+ if (empty($alias)) {
+ $alias = $userInfo['alias'];
+ }
+
+ if (empty($email)) {
+ $email = $userInfo['email'];
+ }
+
+ if ($email != $userInfo['email']) {
+ $this->checkEmail($email);
+ }
+
+ $alias = $this->getCleanAlias($alias, $userLogin);
+ $token_auth = $this->getTokenAuth($userLogin, $password);
+
+ $db = Zend_Registry::get('db');
+
+ $db->update(Piwik_Common::prefixTable("user"),
+ array(
+ 'password' => $password,
+ 'alias' => $alias,
+ 'email' => $email,
+ 'token_auth' => $token_auth,
+ ),
+ "login = '$userLogin'"
+ );
+ Piwik_Tracker_Cache::deleteTrackerCache();
+
+ Piwik_PostEvent('UsersManager.updateUser', $userLogin);
+ }
+
+ /**
+ * Delete a user and all its access, given its login.
+ *
+ * @param string $userLogin the user login.
+ *
+ * @throws Exception if the user doesn't exist
+ *
+ * @return bool true on success
+ */
+ public function deleteUser($userLogin)
+ {
+ Piwik::checkUserIsSuperUser();
+ $this->checkUserIsNotAnonymous($userLogin);
+ $this->checkUserIsNotSuperUser($userLogin);
+ if (!$this->userExists($userLogin)) {
+ throw new Exception(Piwik_TranslateException("UsersManager_ExceptionDeleteDoesNotExist", $userLogin));
+ }
+
+ $this->deleteUserOnly($userLogin);
+ $this->deleteUserAccess($userLogin);
+ Piwik_Tracker_Cache::deleteTrackerCache();
+ }
+
+ /**
+ * Returns true if the given userLogin is known in the database
+ *
+ * @return bool true if the user is known
+ */
+ public function userExists($userLogin)
+ {
+ $count = Piwik_FetchOne("SELECT count(*)
+ FROM " . Piwik_Common::prefixTable("user") . "
WHERE login = ?", $userLogin);
- return $count != 0;
- }
-
- /**
- * Returns true if user with given email (userEmail) is known in the database, or the super user
- *
- * @return bool true if the user is known
- */
- public function userEmailExists( $userEmail )
- {
- Piwik::checkUserIsNotAnonymous();
- $count = Piwik_FetchOne("SELECT count(*)
- FROM ".Piwik_Common::prefixTable("user"). "
+ return $count != 0;
+ }
+
+ /**
+ * Returns true if user with given email (userEmail) is known in the database, or the super user
+ *
+ * @return bool true if the user is known
+ */
+ public function userEmailExists($userEmail)
+ {
+ Piwik::checkUserIsNotAnonymous();
+ $count = Piwik_FetchOne("SELECT count(*)
+ FROM " . Piwik_Common::prefixTable("user") . "
WHERE email = ?", $userEmail);
- return $count != 0
- || Piwik_Config::getInstance()->superuser['email'] == $userEmail;
- }
-
- /**
- * Set an access level to a given user for a list of websites ID.
- *
- * If access = 'noaccess' the current access (if any) will be deleted.
- * If access = 'view' or 'admin' the current access level is deleted and updated with the new value.
- *
- * @param string $userLogin The user login
- * @param string $access Access to grant. Must have one of the following value : noaccess, view, admin
- * @param int|array $idSites The array of idSites on which to apply the access level for the user.
- * If the value is "all" then we apply the access level to all the websites ID for which the current authentificated user has an 'admin' access.
- *
- * @throws Exception if the user doesn't exist
- * @throws Exception if the access parameter doesn't have a correct value
- * @throws Exception if any of the given website ID doesn't exist
- *
- * @return bool true on success
- */
- public function setUserAccess( $userLogin, $access, $idSites)
- {
- $this->checkAccessType( $access );
- $this->checkUserExists( $userLogin);
- $this->checkUserIsNotSuperUser($userLogin);
-
- if($userLogin == 'anonymous'
- && $access == 'admin')
- {
- throw new Exception(Piwik_TranslateException("UsersManager_ExceptionAdminAnonymous"));
- }
-
- // in case idSites is null we grant access to all the websites on which the current connected user
- // has an 'admin' access
- if($idSites === 'all')
- {
- $idSites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAdminAccess();
- }
- // in case the idSites is an integer we build an array
- else
- {
- $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites);
- }
-
- if(empty($idSites))
- {
- throw new Exception('Specify at least one website ID in &idSites=');
- }
- // it is possible to set user access on websites only for the websites admin
- // basically an admin can give the view or the admin access to any user for the websites he manages
- Piwik::checkUserHasAdminAccess( $idSites );
-
- $this->deleteUserAccess( $userLogin, $idSites);
-
- // delete UserAccess
- $db = Zend_Registry::get('db');
-
- // if the access is noaccess then we don't save it as this is the default value
- // when no access are specified
- if($access != 'noaccess')
- {
- foreach($idSites as $idsite)
- {
- $db->insert( Piwik_Common::prefixTable("access"),
- array( "idsite" => $idsite,
- "login" => $userLogin,
- "access" => $access)
- );
- }
- }
-
- // we reload the access list which doesn't yet take in consideration this new user access
- Zend_Registry::get('access')->reloadAccess();
- Piwik_Tracker_Cache::deleteTrackerCache();
- }
-
- /**
- * Throws an exception is the user login doesn't exist
- *
- * @param string $userLogin user login
- * @throws Exception if the user doesn't exist
- */
- private function checkUserExists( $userLogin )
- {
- if(!$this->userExists($userLogin))
- {
- throw new Exception(Piwik_TranslateException("UsersManager_ExceptionUserDoesNotExist", $userLogin));
- }
- }
-
- /**
- * Throws an exception is the user email cannot be found
- *
- * @param string $userEmail user email
- * @throws Exception if the user doesn't exist
- */
- private function checkUserEmailExists( $userEmail )
- {
- if(!$this->userEmailExists($userEmail))
- {
- throw new Exception(Piwik_TranslateException("UsersManager_ExceptionUserDoesNotExist", $userEmail));
- }
- }
-
- private function checkUserIsNotAnonymous( $userLogin )
- {
- if($userLogin == 'anonymous')
- {
- throw new Exception(Piwik_TranslateException("UsersManager_ExceptionEditAnonymous"));
- }
- }
-
- private function checkUserIsNotSuperUser( $userLogin )
- {
- if($userLogin == Piwik_Config::getInstance()->superuser['login'])
- {
- throw new Exception(Piwik_TranslateException("UsersManager_ExceptionSuperUser"));
- }
- }
-
- private function checkAccessType($access)
- {
- $accessList = Piwik_Access::getListAccess();
-
- // do not allow to set the superUser access
- unset($accessList[array_search("superuser", $accessList)]);
-
- if(!in_array($access,$accessList))
- {
- throw new Exception(Piwik_TranslateException("UsersManager_ExceptionAccessValues", implode(", ", $accessList)));
- }
- }
-
- /**
- * Delete a user given its login.
- * The user's access are not deleted.
- *
- * @param string the user login.
- *
- */
- private function deleteUserOnly( $userLogin )
- {
- $db = Zend_Registry::get('db');
- $db->query("DELETE FROM ".Piwik_Common::prefixTable("user")." WHERE login = ?", $userLogin);
-
- Piwik_PostEvent('UsersManager.deleteUser', $userLogin);
- }
-
-
- /**
- * Delete the user access for the given websites.
- * The array of idsite must be either null OR the values must have been checked before for their validity!
- *
- * @param string the user login
- * @param array array of idsites on which to delete the access. If null then delete all the access for this user.
- *
- * @return bool true on success
- */
- private function deleteUserAccess( $userLogin, $idSites = null )
- {
- $db = Zend_Registry::get('db');
-
- if(is_null($idSites))
- {
- $db->query( "DELETE FROM ".Piwik_Common::prefixTable("access").
- " WHERE login = ?",
- array( $userLogin) );
- }
- else
- {
- foreach($idSites as $idsite)
- {
- $db->query( "DELETE FROM ".Piwik_Common::prefixTable("access").
- " WHERE idsite = ? AND login = ?",
- array($idsite, $userLogin)
- );
- }
- }
- }
-
- /**
- * Generates a unique MD5 for the given login & password
- *
- * @param string $userLogin Login
- * @param string $md5Password MD5ied string of the password
- * @throws Exception
- * @return string
- */
- public function getTokenAuth($userLogin, $md5Password)
- {
- if(strlen($md5Password) != 32)
- {
- throw new Exception(Piwik_TranslateException('UsersManager_ExceptionPasswordMD5HashExpected'));
- }
- return md5($userLogin . $md5Password );
- }
+ return $count != 0
+ || Piwik_Config::getInstance()->superuser['email'] == $userEmail;
+ }
+
+ /**
+ * Set an access level to a given user for a list of websites ID.
+ *
+ * If access = 'noaccess' the current access (if any) will be deleted.
+ * If access = 'view' or 'admin' the current access level is deleted and updated with the new value.
+ *
+ * @param string $userLogin The user login
+ * @param string $access Access to grant. Must have one of the following value : noaccess, view, admin
+ * @param int|array $idSites The array of idSites on which to apply the access level for the user.
+ * If the value is "all" then we apply the access level to all the websites ID for which the current authentificated user has an 'admin' access.
+ *
+ * @throws Exception if the user doesn't exist
+ * @throws Exception if the access parameter doesn't have a correct value
+ * @throws Exception if any of the given website ID doesn't exist
+ *
+ * @return bool true on success
+ */
+ public function setUserAccess($userLogin, $access, $idSites)
+ {
+ $this->checkAccessType($access);
+ $this->checkUserExists($userLogin);
+ $this->checkUserIsNotSuperUser($userLogin);
+
+ if ($userLogin == 'anonymous'
+ && $access == 'admin'
+ ) {
+ throw new Exception(Piwik_TranslateException("UsersManager_ExceptionAdminAnonymous"));
+ }
+
+ // in case idSites is null we grant access to all the websites on which the current connected user
+ // has an 'admin' access
+ if ($idSites === 'all') {
+ $idSites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAdminAccess();
+ } // in case the idSites is an integer we build an array
+ else {
+ $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites);
+ }
+
+ if (empty($idSites)) {
+ throw new Exception('Specify at least one website ID in &idSites=');
+ }
+ // it is possible to set user access on websites only for the websites admin
+ // basically an admin can give the view or the admin access to any user for the websites he manages
+ Piwik::checkUserHasAdminAccess($idSites);
+
+ $this->deleteUserAccess($userLogin, $idSites);
+
+ // delete UserAccess
+ $db = Zend_Registry::get('db');
+
+ // if the access is noaccess then we don't save it as this is the default value
+ // when no access are specified
+ if ($access != 'noaccess') {
+ foreach ($idSites as $idsite) {
+ $db->insert(Piwik_Common::prefixTable("access"),
+ array("idsite" => $idsite,
+ "login" => $userLogin,
+ "access" => $access)
+ );
+ }
+ }
+
+ // we reload the access list which doesn't yet take in consideration this new user access
+ Zend_Registry::get('access')->reloadAccess();
+ Piwik_Tracker_Cache::deleteTrackerCache();
+ }
+
+ /**
+ * Throws an exception is the user login doesn't exist
+ *
+ * @param string $userLogin user login
+ * @throws Exception if the user doesn't exist
+ */
+ private function checkUserExists($userLogin)
+ {
+ if (!$this->userExists($userLogin)) {
+ throw new Exception(Piwik_TranslateException("UsersManager_ExceptionUserDoesNotExist", $userLogin));
+ }
+ }
+
+ /**
+ * Throws an exception is the user email cannot be found
+ *
+ * @param string $userEmail user email
+ * @throws Exception if the user doesn't exist
+ */
+ private function checkUserEmailExists($userEmail)
+ {
+ if (!$this->userEmailExists($userEmail)) {
+ throw new Exception(Piwik_TranslateException("UsersManager_ExceptionUserDoesNotExist", $userEmail));
+ }
+ }
+
+ private function checkUserIsNotAnonymous($userLogin)
+ {
+ if ($userLogin == 'anonymous') {
+ throw new Exception(Piwik_TranslateException("UsersManager_ExceptionEditAnonymous"));
+ }
+ }
+
+ private function checkUserIsNotSuperUser($userLogin)
+ {
+ if ($userLogin == Piwik_Config::getInstance()->superuser['login']) {
+ throw new Exception(Piwik_TranslateException("UsersManager_ExceptionSuperUser"));
+ }
+ }
+
+ private function checkAccessType($access)
+ {
+ $accessList = Piwik_Access::getListAccess();
+
+ // do not allow to set the superUser access
+ unset($accessList[array_search("superuser", $accessList)]);
+
+ if (!in_array($access, $accessList)) {
+ throw new Exception(Piwik_TranslateException("UsersManager_ExceptionAccessValues", implode(", ", $accessList)));
+ }
+ }
+
+ /**
+ * Delete a user given its login.
+ * The user's access are not deleted.
+ *
+ * @param string the user login.
+ *
+ */
+ private function deleteUserOnly($userLogin)
+ {
+ $db = Zend_Registry::get('db');
+ $db->query("DELETE FROM " . Piwik_Common::prefixTable("user") . " WHERE login = ?", $userLogin);
+
+ Piwik_PostEvent('UsersManager.deleteUser', $userLogin);
+ }
+
+
+ /**
+ * Delete the user access for the given websites.
+ * The array of idsite must be either null OR the values must have been checked before for their validity!
+ *
+ * @param string the user login
+ * @param array array of idsites on which to delete the access. If null then delete all the access for this user.
+ *
+ * @return bool true on success
+ */
+ private function deleteUserAccess($userLogin, $idSites = null)
+ {
+ $db = Zend_Registry::get('db');
+
+ if (is_null($idSites)) {
+ $db->query("DELETE FROM " . Piwik_Common::prefixTable("access") .
+ " WHERE login = ?",
+ array($userLogin));
+ } else {
+ foreach ($idSites as $idsite) {
+ $db->query("DELETE FROM " . Piwik_Common::prefixTable("access") .
+ " WHERE idsite = ? AND login = ?",
+ array($idsite, $userLogin)
+ );
+ }
+ }
+ }
+
+ /**
+ * Generates a unique MD5 for the given login & password
+ *
+ * @param string $userLogin Login
+ * @param string $md5Password MD5ied string of the password
+ * @throws Exception
+ * @return string
+ */
+ public function getTokenAuth($userLogin, $md5Password)
+ {
+ if (strlen($md5Password) != 32) {
+ throw new Exception(Piwik_TranslateException('UsersManager_ExceptionPasswordMD5HashExpected'));
+ }
+ return md5($userLogin . $md5Password);
+ }
}
diff --git a/plugins/UsersManager/Controller.php b/plugins/UsersManager/Controller.php
index c44a5df6e8..77d7f5f8ce 100644
--- a/plugins/UsersManager/Controller.php
+++ b/plugins/UsersManager/Controller.php
@@ -1,370 +1,335 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_UsersManager
*/
/**
- *
+ *
* @package Piwik_UsersManager
*/
class Piwik_UsersManager_Controller extends Piwik_Controller_Admin
{
- static function orderByName($a, $b)
- {
- return strcmp($a['name'], $b['name']);
- }
-
- /**
- * The "Manage Users and Permissions" Admin UI screen
- */
- function index()
- {
- Piwik::checkUserIsNotAnonymous();
-
- $view = Piwik_View::factory('UsersManager');
-
- $IdSitesAdmin = Piwik_SitesManager_API::getInstance()->getSitesIdWithAdminAccess();
- $idSiteSelected = 1;
-
- if(count($IdSitesAdmin) > 0)
- {
- $defaultWebsiteId = $IdSitesAdmin[0];
- $idSiteSelected = Piwik_Common::getRequestVar('idSite', $defaultWebsiteId);
- }
-
- if($idSiteSelected==='all')
- {
- $usersAccessByWebsite = array();
- $defaultReportSiteName = Piwik_Translate('UsersManager_ApplyToAllWebsites');
- }
- else
- {
- $usersAccessByWebsite = Piwik_UsersManager_API::getInstance()->getUsersAccessFromSite( $idSiteSelected );
- $defaultReportSiteName = Piwik_Site::getNameFor($idSiteSelected);
- }
-
- // we dont want to display the user currently logged so that the user can't change his settings from admin to view...
- $currentlyLogged = Piwik::getCurrentUserLogin();
- $usersLogin = Piwik_UsersManager_API::getInstance()->getUsersLogin();
- foreach($usersLogin as $login)
- {
- if(!isset($usersAccessByWebsite[$login]))
- {
- $usersAccessByWebsite[$login] = 'noaccess';
- }
- }
- unset($usersAccessByWebsite[$currentlyLogged]);
-
-
- // $usersAccessByWebsite is not supposed to contain unexistant logins, but it does when upgrading from some old Piwik version
- foreach($usersAccessByWebsite as $login => $access)
- {
- if(!in_array($login, $usersLogin))
- {
- unset($usersAccessByWebsite[$login]);
- continue;
- }
- }
-
- ksort($usersAccessByWebsite);
-
- $users = array();
- $usersAliasByLogin = array();
- if(Piwik::isUserHasSomeAdminAccess())
- {
- $users = Piwik_UsersManager_API::getInstance()->getUsers();
- foreach($users as $user)
- {
- $usersAliasByLogin[$user['login']] = $user['alias'];
- }
- }
- $view->anonymousHasViewAccess = $this->hasAnonymousUserViewAccess($usersAccessByWebsite);
- $view->idSiteSelected = $idSiteSelected;
- $view->defaultReportSiteName = $defaultReportSiteName;
- $view->users = $users;
- $view->usersAliasByLogin = $usersAliasByLogin;
- $view->usersCount = count($users) - 1;
- $view->usersAccessByWebsite = $usersAccessByWebsite;
- $websites = Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess();
- uasort($websites, array('Piwik_UsersManager_Controller', 'orderByName'));
- $view->websites = $websites;
- $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
- echo $view->render();
- }
-
- private function hasAnonymousUserViewAccess($usersAccessByWebsite)
- {
- $anonymousHasViewAccess = false;
- foreach ($usersAccessByWebsite as $login => $access) {
- if ($login == 'anonymous'
- && $access != 'noaccess'
- ) {
- $anonymousHasViewAccess = true;
- }
- }
- return $anonymousHasViewAccess;
- }
-
- /**
- * Returns default date for Piwik reports
- *
- * @param string $user
- * @return string today, yesterday, week, month, year
- */
- protected function getDefaultDateForUser($user)
- {
- $userSettingsDate = Piwik_UsersManager_API::getInstance()->getUserPreference($user, Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT_DATE);
- if($userSettingsDate === false)
- {
- return Piwik_Config::getInstance()->General['default_day'];
- }
- return $userSettingsDate;
- }
-
- /**
- * The "User Settings" admin UI screen view
- */
- public function userSettings()
- {
- Piwik::checkUserIsNotAnonymous();
-
- $view = Piwik_View::factory('userSettings');
-
- $userLogin = Piwik::getCurrentUserLogin();
- if(Piwik::isUserIsSuperUser())
- {
- $view->userAlias = $userLogin;
- $view->userEmail = Piwik::getSuperUserEmail();
- if(!Piwik_Config::getInstance()->isFileWritable())
- {
- $view->configFileNotWritable = true;
- }
- }
- else
- {
- $user = Piwik_UsersManager_API::getInstance()->getUser($userLogin);
- $view->userAlias = $user['alias'];
- $view->userEmail = $user['email'];
- }
-
- $defaultReport = Piwik_UsersManager_API::getInstance()->getUserPreference($userLogin, Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT);
- if($defaultReport === false)
- {
- $defaultReport = $this->getDefaultWebsiteId();
- }
- $view->defaultReport = $defaultReport;
-
- if ($defaultReport == 'MultiSites')
- {
- $view->defaultReportSiteName = Piwik_Site::getNameFor($this->getDefaultWebsiteId());
- }
- else
- {
- $view->defaultReportSiteName = Piwik_Site::getNameFor($defaultReport);
- }
-
- $view->defaultDate = $this->getDefaultDateForUser($userLogin);
- $view->availableDefaultDates = array(
- 'today' => Piwik_Translate('General_Today'),
- 'yesterday' => Piwik_Translate('General_Yesterday'),
- 'previous7' => Piwik_Translate('General_PreviousDays', 7),
- 'previous30' => Piwik_Translate('General_PreviousDays', 30),
- 'last7' => Piwik_Translate('General_LastDays', 7),
- 'last30' => Piwik_Translate('General_LastDays', 30),
- 'week' => Piwik_Translate('General_CurrentWeek'),
- 'month' => Piwik_Translate('General_CurrentMonth'),
- 'year' => Piwik_Translate('General_CurrentYear'),
- );
-
- $view->ignoreCookieSet = Piwik_Tracker_IgnoreCookie::isIgnoreCookieFound();
- $this->initViewAnonymousUserSettings($view);
- $view->piwikHost = Piwik_Url::getCurrentHost();
- $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
- echo $view->render();
- }
-
- public function setIgnoreCookie()
- {
- Piwik::checkUserHasSomeViewAccess();
- Piwik::checkUserIsNotAnonymous();
- $this->checkTokenInUrl();
-
- Piwik_Tracker_IgnoreCookie::setIgnoreCookie();
- Piwik::redirectToModule('UsersManager', 'userSettings', array('token_auth'=> false));
- }
-
- /**
- * The Super User can modify Anonymous user settings
- * @param Piwik_View $view
- */
- protected function initViewAnonymousUserSettings($view)
- {
- if(!Piwik::isUserIsSuperUser())
- {
- return;
- }
- $userLogin = 'anonymous';
-
- // Which websites are available to the anonymous users?
- $anonymousSitesAccess = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser($userLogin);
- $anonymousSites = array();
- foreach($anonymousSitesAccess as $info)
- {
- $idSite = $info['site'];
- $site = Piwik_SitesManager_API::getInstance()->getSiteFromId($idSite);
- // Work around manual website deletion
- if(!empty($site))
- {
- $anonymousSites[$idSite] = $site;
- }
- }
- $view->anonymousSites = $anonymousSites;
-
- // Which report is displayed by default to the anonymous user?
- $anonymousDefaultReport = Piwik_UsersManager_API::getInstance()->getUserPreference($userLogin, Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT);
- if($anonymousDefaultReport === false)
- {
- if(empty($anonymousSites))
- {
- $anonymousDefaultReport = Piwik::getLoginPluginName();
- }
- else
- {
- // we manually imitate what would happen, in case the anonymous user logs in
- // and is redirected to the first website available to him in the list
- // @see getDefaultWebsiteId()
- reset($anonymousSites);
- $anonymousDefaultReport = key($anonymousSites);
- }
- }
- $view->anonymousDefaultReport = $anonymousDefaultReport;
-
- $view->anonymousDefaultDate = $this->getDefaultDateForUser($userLogin);
- }
-
- /**
- * Records settings for the anonymous users (default report, default date)
- */
- public function recordAnonymousUserSettings()
- {
- $response = new Piwik_API_ResponseBuilder(Piwik_Common::getRequestVar('format'));
- try {
- Piwik::checkUserIsSuperUser();
- $this->checkTokenInUrl();
-
- $anonymousDefaultReport = Piwik_Common::getRequestVar('anonymousDefaultReport');
- $anonymousDefaultDate = Piwik_Common::getRequestVar('anonymousDefaultDate');
- $userLogin = 'anonymous';
- Piwik_UsersManager_API::getInstance()->setUserPreference($userLogin,
- Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT,
- $anonymousDefaultReport);
- Piwik_UsersManager_API::getInstance()->setUserPreference($userLogin,
- Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT_DATE,
- $anonymousDefaultDate);
- $toReturn = $response->getResponse();
- } catch(Exception $e ) {
- $toReturn = $response->getResponseException( $e );
- }
- echo $toReturn;
- }
-
- /**
- * Records settings from the "User Settings" page
- * @throws Exception
- */
- public function recordUserSettings()
- {
- $response = new Piwik_API_ResponseBuilder(Piwik_Common::getRequestVar('format'));
- try {
- $this->checkTokenInUrl();
-
- $alias = Piwik_Common::getRequestVar('alias');
- $email = Piwik_Common::getRequestVar('email');
- $defaultReport = Piwik_Common::getRequestVar('defaultReport');
- $defaultDate = Piwik_Common::getRequestVar('defaultDate');
-
- $newPassword = false;
- $password = Piwik_Common::getRequestvar('password', false);
- $passwordBis = Piwik_Common::getRequestvar('passwordBis', false);
- if(!empty($password)
- || !empty($passwordBis))
- {
- if($password != $passwordBis)
- {
- throw new Exception(Piwik_Translate('Login_PasswordsDoNotMatch'));
- }
- $newPassword = $password;
- }
-
- // UI disables password change on invalid host, but check here anyway
- if (!Piwik_Url::isValidHost()
- && $newPassword !== false)
- {
- throw new Exception("Cannot change password with untrusted hostname!");
- }
-
- $userLogin = Piwik::getCurrentUserLogin();
- if(Piwik::isUserIsSuperUser())
- {
- $superUser = Piwik_Config::getInstance()->superuser;
- $updatedSuperUser = false;
-
- if($newPassword !== false)
- {
- $newPassword = Piwik_Common::unsanitizeInputValue($newPassword);
- $md5PasswordSuperUser = md5($newPassword);
- $superUser['password'] = $md5PasswordSuperUser;
- $updatedSuperUser = true;
- }
- if($superUser['email'] != $email)
- {
- $superUser['email'] = $email;
- $updatedSuperUser = true;
- }
- if($updatedSuperUser)
- {
- Piwik_Config::getInstance()->superuser = $superUser;
- Piwik_Config::getInstance()->forceSave();
- }
- }
- else
- {
- Piwik_UsersManager_API::getInstance()->updateUser($userLogin, $newPassword, $email, $alias);
- if($newPassword !== false)
- {
- $newPassword = Piwik_Common::unsanitizeInputValue($newPassword);
- }
- }
-
- // logs the user in with the new password
- if($newPassword !== false)
- {
- $info = array(
- 'login' => $userLogin,
- 'md5Password' => md5($newPassword),
- 'rememberMe' => false,
- );
- Piwik_PostEvent('Login.initSession', $info);
- }
-
- Piwik_UsersManager_API::getInstance()->setUserPreference($userLogin,
- Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT,
- $defaultReport);
- Piwik_UsersManager_API::getInstance()->setUserPreference($userLogin,
- Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT_DATE,
- $defaultDate);
- $toReturn = $response->getResponse();
- } catch(Exception $e ) {
- $toReturn = $response->getResponseException( $e );
- }
- echo $toReturn;
- }
+ static function orderByName($a, $b)
+ {
+ return strcmp($a['name'], $b['name']);
+ }
+
+ /**
+ * The "Manage Users and Permissions" Admin UI screen
+ */
+ function index()
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ $view = Piwik_View::factory('UsersManager');
+
+ $IdSitesAdmin = Piwik_SitesManager_API::getInstance()->getSitesIdWithAdminAccess();
+ $idSiteSelected = 1;
+
+ if (count($IdSitesAdmin) > 0) {
+ $defaultWebsiteId = $IdSitesAdmin[0];
+ $idSiteSelected = Piwik_Common::getRequestVar('idSite', $defaultWebsiteId);
+ }
+
+ if ($idSiteSelected === 'all') {
+ $usersAccessByWebsite = array();
+ $defaultReportSiteName = Piwik_Translate('UsersManager_ApplyToAllWebsites');
+ } else {
+ $usersAccessByWebsite = Piwik_UsersManager_API::getInstance()->getUsersAccessFromSite($idSiteSelected);
+ $defaultReportSiteName = Piwik_Site::getNameFor($idSiteSelected);
+ }
+
+ // we dont want to display the user currently logged so that the user can't change his settings from admin to view...
+ $currentlyLogged = Piwik::getCurrentUserLogin();
+ $usersLogin = Piwik_UsersManager_API::getInstance()->getUsersLogin();
+ foreach ($usersLogin as $login) {
+ if (!isset($usersAccessByWebsite[$login])) {
+ $usersAccessByWebsite[$login] = 'noaccess';
+ }
+ }
+ unset($usersAccessByWebsite[$currentlyLogged]);
+
+
+ // $usersAccessByWebsite is not supposed to contain unexistant logins, but it does when upgrading from some old Piwik version
+ foreach ($usersAccessByWebsite as $login => $access) {
+ if (!in_array($login, $usersLogin)) {
+ unset($usersAccessByWebsite[$login]);
+ continue;
+ }
+ }
+
+ ksort($usersAccessByWebsite);
+
+ $users = array();
+ $usersAliasByLogin = array();
+ if (Piwik::isUserHasSomeAdminAccess()) {
+ $users = Piwik_UsersManager_API::getInstance()->getUsers();
+ foreach ($users as $user) {
+ $usersAliasByLogin[$user['login']] = $user['alias'];
+ }
+ }
+ $view->anonymousHasViewAccess = $this->hasAnonymousUserViewAccess($usersAccessByWebsite);
+ $view->idSiteSelected = $idSiteSelected;
+ $view->defaultReportSiteName = $defaultReportSiteName;
+ $view->users = $users;
+ $view->usersAliasByLogin = $usersAliasByLogin;
+ $view->usersCount = count($users) - 1;
+ $view->usersAccessByWebsite = $usersAccessByWebsite;
+ $websites = Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess();
+ uasort($websites, array('Piwik_UsersManager_Controller', 'orderByName'));
+ $view->websites = $websites;
+ $this->setBasicVariablesView($view);
+ $view->menu = Piwik_GetAdminMenu();
+ echo $view->render();
+ }
+
+ private function hasAnonymousUserViewAccess($usersAccessByWebsite)
+ {
+ $anonymousHasViewAccess = false;
+ foreach ($usersAccessByWebsite as $login => $access) {
+ if ($login == 'anonymous'
+ && $access != 'noaccess'
+ ) {
+ $anonymousHasViewAccess = true;
+ }
+ }
+ return $anonymousHasViewAccess;
+ }
+
+ /**
+ * Returns default date for Piwik reports
+ *
+ * @param string $user
+ * @return string today, yesterday, week, month, year
+ */
+ protected function getDefaultDateForUser($user)
+ {
+ $userSettingsDate = Piwik_UsersManager_API::getInstance()->getUserPreference($user, Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT_DATE);
+ if ($userSettingsDate === false) {
+ return Piwik_Config::getInstance()->General['default_day'];
+ }
+ return $userSettingsDate;
+ }
+
+ /**
+ * The "User Settings" admin UI screen view
+ */
+ public function userSettings()
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ $view = Piwik_View::factory('userSettings');
+
+ $userLogin = Piwik::getCurrentUserLogin();
+ if (Piwik::isUserIsSuperUser()) {
+ $view->userAlias = $userLogin;
+ $view->userEmail = Piwik::getSuperUserEmail();
+ if (!Piwik_Config::getInstance()->isFileWritable()) {
+ $view->configFileNotWritable = true;
+ }
+ } else {
+ $user = Piwik_UsersManager_API::getInstance()->getUser($userLogin);
+ $view->userAlias = $user['alias'];
+ $view->userEmail = $user['email'];
+ }
+
+ $defaultReport = Piwik_UsersManager_API::getInstance()->getUserPreference($userLogin, Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT);
+ if ($defaultReport === false) {
+ $defaultReport = $this->getDefaultWebsiteId();
+ }
+ $view->defaultReport = $defaultReport;
+
+ if ($defaultReport == 'MultiSites') {
+ $view->defaultReportSiteName = Piwik_Site::getNameFor($this->getDefaultWebsiteId());
+ } else {
+ $view->defaultReportSiteName = Piwik_Site::getNameFor($defaultReport);
+ }
+
+ $view->defaultDate = $this->getDefaultDateForUser($userLogin);
+ $view->availableDefaultDates = array(
+ 'today' => Piwik_Translate('General_Today'),
+ 'yesterday' => Piwik_Translate('General_Yesterday'),
+ 'previous7' => Piwik_Translate('General_PreviousDays', 7),
+ 'previous30' => Piwik_Translate('General_PreviousDays', 30),
+ 'last7' => Piwik_Translate('General_LastDays', 7),
+ 'last30' => Piwik_Translate('General_LastDays', 30),
+ 'week' => Piwik_Translate('General_CurrentWeek'),
+ 'month' => Piwik_Translate('General_CurrentMonth'),
+ 'year' => Piwik_Translate('General_CurrentYear'),
+ );
+
+ $view->ignoreCookieSet = Piwik_Tracker_IgnoreCookie::isIgnoreCookieFound();
+ $this->initViewAnonymousUserSettings($view);
+ $view->piwikHost = Piwik_Url::getCurrentHost();
+ $this->setBasicVariablesView($view);
+ $view->menu = Piwik_GetAdminMenu();
+ echo $view->render();
+ }
+
+ public function setIgnoreCookie()
+ {
+ Piwik::checkUserHasSomeViewAccess();
+ Piwik::checkUserIsNotAnonymous();
+ $this->checkTokenInUrl();
+
+ Piwik_Tracker_IgnoreCookie::setIgnoreCookie();
+ Piwik::redirectToModule('UsersManager', 'userSettings', array('token_auth' => false));
+ }
+
+ /**
+ * The Super User can modify Anonymous user settings
+ * @param Piwik_View $view
+ */
+ protected function initViewAnonymousUserSettings($view)
+ {
+ if (!Piwik::isUserIsSuperUser()) {
+ return;
+ }
+ $userLogin = 'anonymous';
+
+ // Which websites are available to the anonymous users?
+ $anonymousSitesAccess = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser($userLogin);
+ $anonymousSites = array();
+ foreach ($anonymousSitesAccess as $info) {
+ $idSite = $info['site'];
+ $site = Piwik_SitesManager_API::getInstance()->getSiteFromId($idSite);
+ // Work around manual website deletion
+ if (!empty($site)) {
+ $anonymousSites[$idSite] = $site;
+ }
+ }
+ $view->anonymousSites = $anonymousSites;
+
+ // Which report is displayed by default to the anonymous user?
+ $anonymousDefaultReport = Piwik_UsersManager_API::getInstance()->getUserPreference($userLogin, Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT);
+ if ($anonymousDefaultReport === false) {
+ if (empty($anonymousSites)) {
+ $anonymousDefaultReport = Piwik::getLoginPluginName();
+ } else {
+ // we manually imitate what would happen, in case the anonymous user logs in
+ // and is redirected to the first website available to him in the list
+ // @see getDefaultWebsiteId()
+ reset($anonymousSites);
+ $anonymousDefaultReport = key($anonymousSites);
+ }
+ }
+ $view->anonymousDefaultReport = $anonymousDefaultReport;
+
+ $view->anonymousDefaultDate = $this->getDefaultDateForUser($userLogin);
+ }
+
+ /**
+ * Records settings for the anonymous users (default report, default date)
+ */
+ public function recordAnonymousUserSettings()
+ {
+ $response = new Piwik_API_ResponseBuilder(Piwik_Common::getRequestVar('format'));
+ try {
+ Piwik::checkUserIsSuperUser();
+ $this->checkTokenInUrl();
+
+ $anonymousDefaultReport = Piwik_Common::getRequestVar('anonymousDefaultReport');
+ $anonymousDefaultDate = Piwik_Common::getRequestVar('anonymousDefaultDate');
+ $userLogin = 'anonymous';
+ Piwik_UsersManager_API::getInstance()->setUserPreference($userLogin,
+ Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT,
+ $anonymousDefaultReport);
+ Piwik_UsersManager_API::getInstance()->setUserPreference($userLogin,
+ Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT_DATE,
+ $anonymousDefaultDate);
+ $toReturn = $response->getResponse();
+ } catch (Exception $e) {
+ $toReturn = $response->getResponseException($e);
+ }
+ echo $toReturn;
+ }
+
+ /**
+ * Records settings from the "User Settings" page
+ * @throws Exception
+ */
+ public function recordUserSettings()
+ {
+ $response = new Piwik_API_ResponseBuilder(Piwik_Common::getRequestVar('format'));
+ try {
+ $this->checkTokenInUrl();
+
+ $alias = Piwik_Common::getRequestVar('alias');
+ $email = Piwik_Common::getRequestVar('email');
+ $defaultReport = Piwik_Common::getRequestVar('defaultReport');
+ $defaultDate = Piwik_Common::getRequestVar('defaultDate');
+
+ $newPassword = false;
+ $password = Piwik_Common::getRequestvar('password', false);
+ $passwordBis = Piwik_Common::getRequestvar('passwordBis', false);
+ if (!empty($password)
+ || !empty($passwordBis)
+ ) {
+ if ($password != $passwordBis) {
+ throw new Exception(Piwik_Translate('Login_PasswordsDoNotMatch'));
+ }
+ $newPassword = $password;
+ }
+
+ // UI disables password change on invalid host, but check here anyway
+ if (!Piwik_Url::isValidHost()
+ && $newPassword !== false
+ ) {
+ throw new Exception("Cannot change password with untrusted hostname!");
+ }
+
+ $userLogin = Piwik::getCurrentUserLogin();
+ if (Piwik::isUserIsSuperUser()) {
+ $superUser = Piwik_Config::getInstance()->superuser;
+ $updatedSuperUser = false;
+
+ if ($newPassword !== false) {
+ $newPassword = Piwik_Common::unsanitizeInputValue($newPassword);
+ $md5PasswordSuperUser = md5($newPassword);
+ $superUser['password'] = $md5PasswordSuperUser;
+ $updatedSuperUser = true;
+ }
+ if ($superUser['email'] != $email) {
+ $superUser['email'] = $email;
+ $updatedSuperUser = true;
+ }
+ if ($updatedSuperUser) {
+ Piwik_Config::getInstance()->superuser = $superUser;
+ Piwik_Config::getInstance()->forceSave();
+ }
+ } else {
+ Piwik_UsersManager_API::getInstance()->updateUser($userLogin, $newPassword, $email, $alias);
+ if ($newPassword !== false) {
+ $newPassword = Piwik_Common::unsanitizeInputValue($newPassword);
+ }
+ }
+
+ // logs the user in with the new password
+ if ($newPassword !== false) {
+ $info = array(
+ 'login' => $userLogin,
+ 'md5Password' => md5($newPassword),
+ 'rememberMe' => false,
+ );
+ Piwik_PostEvent('Login.initSession', $info);
+ }
+
+ Piwik_UsersManager_API::getInstance()->setUserPreference($userLogin,
+ Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT,
+ $defaultReport);
+ Piwik_UsersManager_API::getInstance()->setUserPreference($userLogin,
+ Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT_DATE,
+ $defaultDate);
+ $toReturn = $response->getResponse();
+ } catch (Exception $e) {
+ $toReturn = $response->getResponseException($e);
+ }
+ echo $toReturn;
+ }
}
diff --git a/plugins/UsersManager/UsersManager.php b/plugins/UsersManager/UsersManager.php
index 57e4d42a7e..41334931c2 100644
--- a/plugins/UsersManager/UsersManager.php
+++ b/plugins/UsersManager/UsersManager.php
@@ -16,142 +16,139 @@
*/
class Piwik_UsersManager extends Piwik_Plugin
{
- const PASSWORD_MIN_LENGTH = 6;
- const PASSWORD_MAX_LENGTH = 26;
+ const PASSWORD_MIN_LENGTH = 6;
+ const PASSWORD_MAX_LENGTH = 26;
- /**
- * Plugin information
- *
- * @see Piwik_Plugin
- *
- * @return array
- */
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('UsersManager_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
+ /**
+ * Plugin information
+ *
+ * @see Piwik_Plugin
+ *
+ * @return array
+ */
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('UsersManager_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
- return $info;
- }
+ return $info;
+ }
- /**
- * Get list of hooks to register.
- *
- * @see Piwik_PluginsManager.loadPlugin()
- *
- * @return array
- */
- function getListHooksRegistered()
- {
- return array(
- 'AdminMenu.add' => 'addMenu',
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'SitesManager.deleteSite' => 'deleteSite',
- 'Common.fetchWebsiteAttributes' => 'recordAdminUsersInCache',
- );
- }
+ /**
+ * Get list of hooks to register.
+ *
+ * @see Piwik_PluginsManager.loadPlugin()
+ *
+ * @return array
+ */
+ function getListHooksRegistered()
+ {
+ return array(
+ 'AdminMenu.add' => 'addMenu',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'SitesManager.deleteSite' => 'deleteSite',
+ 'Common.fetchWebsiteAttributes' => 'recordAdminUsersInCache',
+ );
+ }
- /**
- * Hooks when a website tracker cache is flushed (website/user updated, cache deleted, or empty cache)
- * Will record in the tracker config file the list of Admin token_auth for this website. This
- * will be used when the Tracking API is used with setIp(), setForceDateTime(), setVisitorId(), etc.
- *
- * @param Piwik_Event_Notification $notification notification object
- * @return void
- */
- function recordAdminUsersInCache($notification)
- {
- $idSite = $notification->getNotificationInfo();
- // add the 'hosts' entry in the website array
- $users = Piwik_UsersManager_API::getInstance()->getUsersWithSiteAccess($idSite, 'admin');
+ /**
+ * Hooks when a website tracker cache is flushed (website/user updated, cache deleted, or empty cache)
+ * Will record in the tracker config file the list of Admin token_auth for this website. This
+ * will be used when the Tracking API is used with setIp(), setForceDateTime(), setVisitorId(), etc.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ * @return void
+ */
+ function recordAdminUsersInCache($notification)
+ {
+ $idSite = $notification->getNotificationInfo();
+ // add the 'hosts' entry in the website array
+ $users = Piwik_UsersManager_API::getInstance()->getUsersWithSiteAccess($idSite, 'admin');
- $tokens = array();
- foreach($users as $user)
- {
- $tokens[] = $user['token_auth'];
- }
- $array =& $notification->getNotificationObject();
- $array['admin_token_auth'] = $tokens;
- }
+ $tokens = array();
+ foreach ($users as $user) {
+ $tokens[] = $user['token_auth'];
+ }
+ $array =& $notification->getNotificationObject();
+ $array['admin_token_auth'] = $tokens;
+ }
- /**
- * Delete user preferences associated with a particular site
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function deleteSite($notification)
- {
- $idSite = &$notification->getNotificationObject();
+ /**
+ * Delete user preferences associated with a particular site
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function deleteSite($notification)
+ {
+ $idSite = & $notification->getNotificationObject();
- Piwik_Option::getInstance()->deleteLike('%\_' . Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT, $idSite);
- }
+ Piwik_Option::getInstance()->deleteLike('%\_' . Piwik_UsersManager_API::PREFERENCE_DEFAULT_REPORT, $idSite);
+ }
- /**
- * Return list of plug-in specific JavaScript files to be imported by the asset manager
- *
- * @see Piwik_AssetManager
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles($notification)
- {
- $jsFiles = &$notification->getNotificationObject();
+ /**
+ * Return list of plug-in specific JavaScript files to be imported by the asset manager
+ *
+ * @see Piwik_AssetManager
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
- $jsFiles[] = "plugins/UsersManager/templates/UsersManager.js";
- $jsFiles[] = "plugins/UsersManager/templates/userSettings.js";
- }
+ $jsFiles[] = "plugins/UsersManager/templates/UsersManager.js";
+ $jsFiles[] = "plugins/UsersManager/templates/userSettings.js";
+ }
- /**
- * Add admin menu items
- */
- function addMenu()
- {
- Piwik_AddAdminSubMenu('CoreAdminHome_MenuManage', 'UsersManager_MenuUsers',
- array('module' => 'UsersManager', 'action' => 'index'),
- Piwik::isUserHasSomeAdminAccess(),
- $order = 2);
- Piwik_AddAdminSubMenu('CoreAdminHome_MenuManage', 'UsersManager_MenuUserSettings',
- array('module' => 'UsersManager', 'action' => 'userSettings'),
- Piwik::isUserHasSomeViewAccess(),
- $order = 3);
- }
+ /**
+ * Add admin menu items
+ */
+ function addMenu()
+ {
+ Piwik_AddAdminSubMenu('CoreAdminHome_MenuManage', 'UsersManager_MenuUsers',
+ array('module' => 'UsersManager', 'action' => 'index'),
+ Piwik::isUserHasSomeAdminAccess(),
+ $order = 2);
+ Piwik_AddAdminSubMenu('CoreAdminHome_MenuManage', 'UsersManager_MenuUserSettings',
+ array('module' => 'UsersManager', 'action' => 'userSettings'),
+ Piwik::isUserHasSomeViewAccess(),
+ $order = 3);
+ }
- /**
- * Returns true if the password is complex enough (at least 6 characters and max 26 characters)
- *
- * @param string email
- * @return bool
- */
- public static function isValidPasswordString($input)
- {
- if(!Piwik::isChecksEnabled()
- && !empty($input)
- )
- {
- return true;
- }
- $l = strlen($input);
- return $l >= self::PASSWORD_MIN_LENGTH && $l <= self::PASSWORD_MAX_LENGTH;
- }
+ /**
+ * Returns true if the password is complex enough (at least 6 characters and max 26 characters)
+ *
+ * @param string email
+ * @return bool
+ */
+ public static function isValidPasswordString($input)
+ {
+ if (!Piwik::isChecksEnabled()
+ && !empty($input)
+ ) {
+ return true;
+ }
+ $l = strlen($input);
+ return $l >= self::PASSWORD_MIN_LENGTH && $l <= self::PASSWORD_MAX_LENGTH;
+ }
- public static function checkPassword($password)
- {
- if(!self::isValidPasswordString($password))
- {
- throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidPassword', array(self::PASSWORD_MIN_LENGTH,
- self::PASSWORD_MAX_LENGTH)));
- }
- }
+ public static function checkPassword($password)
+ {
+ if (!self::isValidPasswordString($password)) {
+ throw new Exception(Piwik_TranslateException('UsersManager_ExceptionInvalidPassword', array(self::PASSWORD_MIN_LENGTH,
+ self::PASSWORD_MAX_LENGTH)));
+ }
+ }
- public static function getPasswordHash($password)
- {
- // if change here, should also edit the installation process
- // to change how the root pwd is saved in the config file
- return md5($password);
- }
+ public static function getPasswordHash($password)
+ {
+ // if change here, should also edit the installation process
+ // to change how the root pwd is saved in the config file
+ return md5($password);
+ }
}
diff --git a/plugins/UsersManager/templates/UsersManager.js b/plugins/UsersManager/templates/UsersManager.js
index 505e400f44..1da9a43943 100644
--- a/plugins/UsersManager/templates/UsersManager.js
+++ b/plugins/UsersManager/templates/UsersManager.js
@@ -5,14 +5,13 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function sendUpdateUserAJAX( row )
-{
- var parameters = {};
- parameters.userLogin = $(row).children('#userLogin').html();
- var password = $(row).find('input#password').val();
- if(password != '-') parameters.password = password;
- parameters.email = $(row).find('input#email').val();
- parameters.alias = $(row).find('input#alias').val();
+function sendUpdateUserAJAX(row) {
+ var parameters = {};
+ parameters.userLogin = $(row).children('#userLogin').html();
+ var password = $(row).find('input#password').val();
+ if (password != '-') parameters.password = password;
+ parameters.email = $(row).find('input#email').val();
+ parameters.alias = $(row).find('input#alias').val();
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
@@ -26,8 +25,7 @@ function sendUpdateUserAJAX( row )
ajaxHandler.send(true);
}
-function sendDeleteUserAJAX( login )
-{
+function sendDeleteUserAJAX(login) {
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
module: 'API',
@@ -41,13 +39,12 @@ function sendDeleteUserAJAX( login )
ajaxHandler.send(true);
}
-function sendAddUserAJAX( row )
-{
- var parameters = {};
- parameters.userLogin = $(row).find('input#useradd_login').val();
- parameters.password = $(row).find('input#useradd_password').val();
- parameters.email = $(row).find('input#useradd_email').val();
- parameters.alias = $(row).find('input#useradd_alias').val();
+function sendAddUserAJAX(row) {
+ var parameters = {};
+ parameters.userLogin = $(row).find('input#useradd_login').val();
+ parameters.password = $(row).find('input#useradd_password').val();
+ parameters.email = $(row).find('input#useradd_email').val();
+ parameters.alias = $(row).find('input#useradd_alias').val();
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
@@ -62,17 +59,15 @@ function sendAddUserAJAX( row )
ajaxHandler.send(true);
}
-function getIdSites()
-{
- return $('.custom_select_main_link').attr('siteid');
+function getIdSites() {
+ return $('.custom_select_main_link').attr('siteid');
}
-function sendUpdateUserAccess(login, access, successCallback)
-{
- var parameters = {};
- parameters.userLogin = login;
- parameters.access = access;
- parameters.idSites = getIdSites();
+function sendUpdateUserAccess(login, access, successCallback) {
+ var parameters = {};
+ parameters.userLogin = login;
+ parameters.access = access;
+ parameters.idSites = getIdSites();
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
@@ -87,171 +82,165 @@ function sendUpdateUserAccess(login, access, successCallback)
ajaxHandler.send(true);
}
-function submitOnEnter(e)
-{
- var key=e.keyCode || e.which;
- if (key==13)
- {
- $(this).find('.adduser').click();
- $(this).find('.updateuser').click();
- }
+function submitOnEnter(e) {
+ var key = e.keyCode || e.which;
+ if (key == 13) {
+ $(this).find('.adduser').click();
+ $(this).find('.updateuser').click();
+ }
}
-function launchAjaxRequest(self, successCallback)
-{
+function launchAjaxRequest(self, successCallback) {
sendUpdateUserAccess(
$(self).parent().parent().find('#login').html(), //if changed change also the modal
$(self).parent().attr('id'),
successCallback
);
}
-function hideAccessUpdated()
-{
- setTimeout(function(){
- $('#accessUpdated').fadeOut(500);
- }, 2000);
+function hideAccessUpdated() {
+ setTimeout(function () {
+ $('#accessUpdated').fadeOut(500);
+ }, 2000);
}
-function bindUpdateAccess()
-{
- var self = this;
- hideAccessUpdated(1);
- // callback called when the ajax request Update the user permissions is successful
- function successCallback (response)
- {
+function bindUpdateAccess() {
+ var self = this;
+ hideAccessUpdated(1);
+ // callback called when the ajax request Update the user permissions is successful
+ function successCallback(response) {
var mainDiv = $(self).parent().parent();
var login = $('#login', mainDiv).text();
mainDiv.find('.accessGranted')
- .attr("src","plugins/UsersManager/images/no-access.png" )
- .attr("class","updateAccess" )
+ .attr("src", "plugins/UsersManager/images/no-access.png")
+ .attr("class", "updateAccess")
.click(bindUpdateAccess)
- ;
+ ;
$(self)
- .attr('src',"plugins/UsersManager/images/ok.png" )
- .attr('class',"accessGranted" )
- ;
+ .attr('src', "plugins/UsersManager/images/ok.png")
+ .attr('class', "accessGranted")
+ ;
$('#accessUpdated').css('display', 'inline-block');
hideAccessUpdated();
// reload if user anonymous was updated, since we display a Notice message when anon has view access
- if(login == 'anonymous') {
+ if (login == 'anonymous') {
window.location.reload();
}
- }
-
- var idSite = getIdSites();
- if(idSite == 'all')
- {
- var target = this;
-
- //ask confirmation
- var userLogin = $(this).parent().parent().find('#login').text();
- $('#confirm').find('#login').text( userLogin ); // if changed here change also the launchAjaxRequest
+ }
- function onValidate()
- {
- launchAjaxRequest(target, successCallback);
- }
- piwikHelper.modalConfirm( '#confirm', {yes: onValidate})
- }
- else
- {
- launchAjaxRequest(this, successCallback);
- }
+ var idSite = getIdSites();
+ if (idSite == 'all') {
+ var target = this;
+
+ //ask confirmation
+ var userLogin = $(this).parent().parent().find('#login').text();
+ $('#confirm').find('#login').text(userLogin); // if changed here change also the launchAjaxRequest
+
+ function onValidate() {
+ launchAjaxRequest(target, successCallback);
+ }
+
+ piwikHelper.modalConfirm('#confirm', {yes: onValidate})
+ }
+ else {
+ launchAjaxRequest(this, successCallback);
+ }
}
-$(document).ready( function() {
- var alreadyEdited = new Array;
- // when click on edituser, the cells become editable
- $('.edituser')
- .click( function() {
- piwikHelper.hideAjaxError();
- var idRow = $(this).attr('id');
- if(alreadyEdited[idRow]==1) return;
- alreadyEdited[idRow] = 1;
- $('tr#'+idRow+' .editable').each(
- // make the fields editable
- // change the EDIT button to VALID button
- function (i,n) {
- var contentBefore = $(n).text();
- var idName = $(n).attr('id');
- if(idName != 'userLogin')
- {
- var contentAfter = '<input id="'+idName+'" value="'+piwikHelper.htmlEntities(contentBefore)+'" size="25" />';
- $(n).html(contentAfter);
- }
- }
- );
-
- $(this)
- .toggle()
- .parent()
- .prepend( $('<input type="submit" class="submit updateuser" value="'+_pk_translate('General_Save_js')+'" />')
- .click( function(){
- var onValidate = function() {
- sendUpdateUserAJAX($('tr#'+idRow));
- };
- if($('tr#'+idRow).find('input#password').val() != '-') {
- piwikHelper.modalConfirm( '#confirmPasswordChange', {yes: onValidate});
- } else {
- onValidate();
- }
- } )
- );
- });
-
- $('.editable').keypress( submitOnEnter );
-
- $('td.editable')
- .click( function(){ $(this).parent().find('.edituser').click(); } );
-
- // when click on deleteuser, the we ask for confirmation and then delete the user
- $('.deleteuser')
- .click( function() {
- piwikHelper.hideAjaxError();
- var idRow = $(this).attr('id');
- var loginToDelete = $(this).parent().parent().find('#userLogin').html();
- $('#confirmUserRemove h2').text(sprintf(_pk_translate('UsersManager_DeleteConfirm_js'),'"'+loginToDelete+'"'));
- piwikHelper.modalConfirm( '#confirmUserRemove', {yes: function(){ sendDeleteUserAJAX( loginToDelete ); }});
- }
- );
-
- $('.addrow').click( function() {
- piwikHelper.hideAjaxError();
- $(this).toggle();
-
- var numberOfRows = $('table#users')[0].rows.length;
- var newRowId = numberOfRows + 1;
- newRowId = 'row' + newRowId;
-
- $(' <tr id="'+newRowId+'">\
+$(document).ready(function () {
+ var alreadyEdited = new Array;
+ // when click on edituser, the cells become editable
+ $('.edituser')
+ .click(function () {
+ piwikHelper.hideAjaxError();
+ var idRow = $(this).attr('id');
+ if (alreadyEdited[idRow] == 1) return;
+ alreadyEdited[idRow] = 1;
+ $('tr#' + idRow + ' .editable').each(
+ // make the fields editable
+ // change the EDIT button to VALID button
+ function (i, n) {
+ var contentBefore = $(n).text();
+ var idName = $(n).attr('id');
+ if (idName != 'userLogin') {
+ var contentAfter = '<input id="' + idName + '" value="' + piwikHelper.htmlEntities(contentBefore) + '" size="25" />';
+ $(n).html(contentAfter);
+ }
+ }
+ );
+
+ $(this)
+ .toggle()
+ .parent()
+ .prepend($('<input type="submit" class="submit updateuser" value="' + _pk_translate('General_Save_js') + '" />')
+ .click(function () {
+ var onValidate = function () {
+ sendUpdateUserAJAX($('tr#' + idRow));
+ };
+ if ($('tr#' + idRow).find('input#password').val() != '-') {
+ piwikHelper.modalConfirm('#confirmPasswordChange', {yes: onValidate});
+ } else {
+ onValidate();
+ }
+ })
+ );
+ });
+
+ $('.editable').keypress(submitOnEnter);
+
+ $('td.editable')
+ .click(function () { $(this).parent().find('.edituser').click(); });
+
+ // when click on deleteuser, the we ask for confirmation and then delete the user
+ $('.deleteuser')
+ .click(function () {
+ piwikHelper.hideAjaxError();
+ var idRow = $(this).attr('id');
+ var loginToDelete = $(this).parent().parent().find('#userLogin').html();
+ $('#confirmUserRemove h2').text(sprintf(_pk_translate('UsersManager_DeleteConfirm_js'), '"' + loginToDelete + '"'));
+ piwikHelper.modalConfirm('#confirmUserRemove', {yes: function () { sendDeleteUserAJAX(loginToDelete); }});
+ }
+ );
+
+ $('.addrow').click(function () {
+ piwikHelper.hideAjaxError();
+ $(this).toggle();
+
+ var numberOfRows = $('table#users')[0].rows.length;
+ var newRowId = numberOfRows + 1;
+ newRowId = 'row' + newRowId;
+
+ $(' <tr id="' + newRowId + '">\
<td><input id="useradd_login" value="login?" size="10" /></td>\
<td><input id="useradd_password" value="password" size="10" /></td>\
<td><input id="useradd_email" value="email@domain.com" size="15" /></td>\
<td><input id="useradd_alias" value="alias" size="15" /></td>\
<td>-</td>\
- <td><input type="submit" class="submit adduser" value="'+_pk_translate('General_Save_js')+'" /></td>\
- <td><span class="cancel">'+sprintf(_pk_translate('General_OrCancel_js'),"","")+'</span></td>\
+ <td><input type="submit" class="submit adduser" value="' + _pk_translate('General_Save_js') + '" /></td>\
+ <td><span class="cancel">' + sprintf(_pk_translate('General_OrCancel_js'), "", "") + '</span></td>\
</tr>')
- .appendTo('#users')
- ;
- $('#'+newRowId).keypress( submitOnEnter );
- $('.adduser').click( function(){ sendAddUserAJAX($('tr#'+newRowId)); } );
- $('.cancel').click(function() { piwikHelper.hideAjaxError(); $(this).parents('tr').remove(); $('.addrow').toggle(); });
- });
+ .appendTo('#users')
+ ;
+ $('#' + newRowId).keypress(submitOnEnter);
+ $('.adduser').click(function () { sendAddUserAJAX($('tr#' + newRowId)); });
+ $('.cancel').click(function () {
+ piwikHelper.hideAjaxError();
+ $(this).parents('tr').remove();
+ $('.addrow').toggle();
+ });
+ });
+
+ $('.updateAccess')
+ .click(bindUpdateAccess);
- $('.updateAccess')
- .click( bindUpdateAccess );
-
- // when a site is selected, reload the page w/o showing the ajax loading element
- $('#usersManagerSiteSelect').bind('piwik:siteSelected', function(e, site) {
- if (site.id != piwik.idSite)
- {
- switchSite(
- site.id,
- site.name,
- false /* do not show main ajax loading animation */,
- true /* do not go to all websites dash */
- );
- }
- });
+ // when a site is selected, reload the page w/o showing the ajax loading element
+ $('#usersManagerSiteSelect').bind('piwik:siteSelected', function (e, site) {
+ if (site.id != piwik.idSite) {
+ switchSite(
+ site.id,
+ site.name,
+ false /* do not show main ajax loading animation */,
+ true /* do not go to all websites dash */
+ );
+ }
+ });
});
diff --git a/plugins/UsersManager/templates/UsersManager.tpl b/plugins/UsersManager/templates/UsersManager.tpl
index ae2c7bd814..c73e3bc182 100644
--- a/plugins/UsersManager/templates/UsersManager.tpl
+++ b/plugins/UsersManager/templates/UsersManager.tpl
@@ -2,149 +2,154 @@
{loadJavascriptTranslations plugins='UsersManager'}
{literal}
-<style type="text/css">
-.dialog {
- display: none;
- padding:20px 10px;
- color:#7A0101;
- cursor:wait;
- font-size:1.2em;
- font-weight:bold;
- text-align:center;
-}
-.editable:hover, .addrow:hover, .updateAccess:hover, .accessGranted:hover, .adduser:hover, .edituser:hover, .deleteuser:hover, .updateuser:hover, .cancel:hover{
- cursor: pointer;
-}
-.addrow {
- padding:1em;
- font-weight:bold;
-}
-.addrow a {
- text-decoration: none;
-}
-.addrow img {
- vertical-align: middle;
-}
-</style>
+ <style type="text/css">
+ .dialog {
+ display: none;
+ padding: 20px 10px;
+ color: #7A0101;
+ cursor: wait;
+ font-size: 1.2em;
+ font-weight: bold;
+ text-align: center;
+ }
+
+ .editable:hover, .addrow:hover, .updateAccess:hover, .accessGranted:hover, .adduser:hover, .edituser:hover, .deleteuser:hover, .updateuser:hover, .cancel:hover {
+ cursor: pointer;
+ }
+
+ .addrow {
+ padding: 1em;
+ font-weight: bold;
+ }
+
+ .addrow a {
+ text-decoration: none;
+ }
+
+ .addrow img {
+ vertical-align: middle;
+ }
+ </style>
{/literal}
<h2>{'UsersManager_ManageAccess'|translate}</h2>
<div id="sites">
- <section class="sites_selector_container">
- <p>{'UsersManager_MainDescription'|translate}</p>
- <div style="display:inline-block;margin-top:5px;">{'UsersManager_Sites'|translate}: </div>
-
- {capture name=applyAllSitesText assign=applyAllSitesText}
- <strong>{'UsersManager_ApplyToAllWebsites'|translate}</strong>
- {/capture}
- {include file="CoreHome/templates/sites_selection.tpl"
- siteName=$defaultReportSiteName idSite=$idSiteSelected allSitesItemText=$applyAllSitesText
- allWebsitesLinkLocation=top siteSelectorId="usersManagerSiteSelect" switchSiteOnSelect=false}
- </section>
+ <section class="sites_selector_container">
+ <p>{'UsersManager_MainDescription'|translate}</p>
+
+ <div style="display:inline-block;margin-top:5px;">{'UsersManager_Sites'|translate}:</div>
+
+ {capture name=applyAllSitesText assign=applyAllSitesText}
+ <strong>{'UsersManager_ApplyToAllWebsites'|translate}</strong>
+ {/capture}
+ {include file="CoreHome/templates/sites_selection.tpl"
+ siteName=$defaultReportSiteName idSite=$idSiteSelected allSitesItemText=$applyAllSitesText
+ allWebsitesLinkLocation=top siteSelectorId="usersManagerSiteSelect" switchSiteOnSelect=false}
+ </section>
</div>
{ajaxErrorDiv}
{ajaxLoadingDiv}
<div class="entityContainer" style='width:600px'>
- {if $anonymousHasViewAccess}
- <div class="ajaxSuccess" style="display:inline-block">
- {'UsersManager_AnonymousUserHasViewAccess'|translate:"'anonymous'":"'view'"}<br/>
- {'UsersManager_AnonymousUserHasViewAccess2'|translate}
- </div>
- {/if}
- <table class="entityTable dataTable" id="access" style="display:inline-table;width:500px;">
- <thead>
- <tr>
- <th class='first'>{'UsersManager_User'|translate}</th>
- <th>{'UsersManager_Alias'|translate}</th>
- <th>{'UsersManager_PrivNone'|translate}</th>
- <th>{'UsersManager_PrivView'|translate}</th>
- <th>{'UsersManager_PrivAdmin'|translate}</th>
- </tr>
- </thead>
-
- <tbody>
- {assign var=accesValid value="<img src='plugins/UsersManager/images/ok.png' class='accessGranted' />"}
- {assign var=accesInvalid value="<img src='plugins/UsersManager/images/no-access.png' class='updateAccess' />"}
- {foreach from=$usersAccessByWebsite key=login item=access}
- <tr>
- <td id='login'>{$login}</td>
- <td>{$usersAliasByLogin[$login]}</td>
- <td id='noaccess'>{if $access=='noaccess' and $idSiteSelected!='all'}{$accesValid}{else}{$accesInvalid}{/if}&nbsp;</td>
- <td id='view'>{if $access=='view' and $idSiteSelected!='all'}{$accesValid}{else}{$accesInvalid}{/if}&nbsp;</td>
- <td id='admin'>
- {if $login=='anonymous'}
- N/A
- {else}
- {if $access=='admin' and $idSiteSelected!='all'}{$accesValid}{else}{$accesInvalid}{/if}&nbsp;
- {/if}
- </td>
- </tr>
- {/foreach}
- </tbody>
- </table>
- <div id="accessUpdated" class="ajaxSuccess" style="display:none;vertical-align:top;">{'General_Done'|translate}!</div>
+ {if $anonymousHasViewAccess}
+ <div class="ajaxSuccess" style="display:inline-block">
+ {'UsersManager_AnonymousUserHasViewAccess'|translate:"'anonymous'":"'view'"}<br/>
+ {'UsersManager_AnonymousUserHasViewAccess2'|translate}
+ </div>
+ {/if}
+ <table class="entityTable dataTable" id="access" style="display:inline-table;width:500px;">
+ <thead>
+ <tr>
+ <th class='first'>{'UsersManager_User'|translate}</th>
+ <th>{'UsersManager_Alias'|translate}</th>
+ <th>{'UsersManager_PrivNone'|translate}</th>
+ <th>{'UsersManager_PrivView'|translate}</th>
+ <th>{'UsersManager_PrivAdmin'|translate}</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {assign var=accesValid value="<img src='plugins/UsersManager/images/ok.png' class='accessGranted' />"}
+ {assign var=accesInvalid value="<img src='plugins/UsersManager/images/no-access.png' class='updateAccess' />"}
+ {foreach from=$usersAccessByWebsite key=login item=access}
+ <tr>
+ <td id='login'>{$login}</td>
+ <td>{$usersAliasByLogin[$login]}</td>
+ <td id='noaccess'>{if $access=='noaccess' and $idSiteSelected!='all'}{$accesValid}{else}{$accesInvalid}{/if}&nbsp;</td>
+ <td id='view'>{if $access=='view' and $idSiteSelected!='all'}{$accesValid}{else}{$accesInvalid}{/if}&nbsp;</td>
+ <td id='admin'>
+ {if $login=='anonymous'}
+ N/A
+ {else}
+ {if $access=='admin' and $idSiteSelected!='all'}{$accesValid}{else}{$accesInvalid}{/if}&nbsp;
+ {/if}
+ </td>
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ <div id="accessUpdated" class="ajaxSuccess" style="display:none;vertical-align:top;">{'General_Done'|translate}!</div>
</div>
<div class="ui-confirm" id="confirm">
- <h2>{'UsersManager_ChangeAllConfirm'|translate:"<span id='login'></span>"}</h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_No'|translate}" />
-</div>
+ <h2>{'UsersManager_ChangeAllConfirm'|translate:"<span id='login'></span>"}</h2>
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_No'|translate}"/>
+</div>
{if $userIsSuperUser}
<div class="ui-confirm" id="confirmUserRemove">
<h2></h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_No'|translate}" />
- </div>
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_No'|translate}"/>
+ </div>
<div class="ui-confirm" id="confirmPasswordChange">
<h2>{'UsersManager_ChangePasswordConfirm'|translate}</h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_No'|translate}" />
- </div>
-
- <br />
- <h2>{'UsersManager_UsersManagement'|translate}</h2>
- <p>{'UsersManager_UsersManagementMainDescription'|translate}
- {'UsersManager_ThereAreCurrentlyNRegisteredUsers'|translate:"<b>$usersCount</b>"}</p>
-
- {ajaxErrorDiv id=ajaxErrorUsersManagement}
- {ajaxLoadingDiv id=ajaxLoadingUsersManagement}
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_No'|translate}"/>
+ </div>
+ <br/>
+ <h2>{'UsersManager_UsersManagement'|translate}</h2>
+ <p>{'UsersManager_UsersManagementMainDescription'|translate}
+ {'UsersManager_ThereAreCurrentlyNRegisteredUsers'|translate:"<b>$usersCount</b>"}</p>
+ {ajaxErrorDiv id=ajaxErrorUsersManagement}
+ {ajaxLoadingDiv id=ajaxLoadingUsersManagement}
+ <div class="entityContainer" style='margin-bottom:50px'>
+ <table class="entityTable dataTable" id="users">
+ <thead>
+ <tr>
+ <th>{'General_Username'|translate}</th>
+ <th>{'UsersManager_Password'|translate}</th>
+ <th>{'UsersManager_Email'|translate}</th>
+ <th>{'UsersManager_Alias'|translate}</th>
+ <th>token_auth</th>
+ <th>{'General_Edit'|translate}</th>
+ <th>{'General_Delete'|translate}</th>
+ </tr>
+ </thead>
- <div class="entityContainer" style='margin-bottom:50px'>
- <table class="entityTable dataTable" id="users">
- <thead>
- <tr>
- <th>{'General_Username'|translate}</th>
- <th>{'UsersManager_Password'|translate}</th>
- <th>{'UsersManager_Email'|translate}</th>
- <th>{'UsersManager_Alias'|translate}</th>
- <th>token_auth</th>
- <th>{'General_Edit'|translate}</th>
- <th>{'General_Delete'|translate}</th>
- </tr>
- </thead>
-
- <tbody>
- {foreach from=$users item=user key=i}
- {if $user.login != 'anonymous'}
- <tr class="editable" id="row{$i}">
- <td id="userLogin" class="editable">{$user.login}</td>
- <td id="password" class="editable">-</td>
- <td id="email" class="editable">{$user.email}</td>
- <td id="alias" class="editable">{$user.alias}</td>
- <td id="token_auth">{$user.token_auth}</td>
- <td><span class="edituser link_but" id="row{$i}"><img title="{'General_Edit'|translate}" src='themes/default/images/ico_edit.png' /> {'General_Edit'|translate} </span></td>
- <td><span class="deleteuser link_but" id="row{$i}"><img title="{'General_Delete'|translate}" src='themes/default/images/ico_delete.png' /> {'General_Delete'|translate} </span></td>
- </tr>
- {/if}
- {/foreach}
- </tbody>
- </table>
- <div class="addrow"><img src='plugins/UsersManager/images/add.png' /> {'UsersManager_AddUser'|translate}</div>
- </div>
+ <tbody>
+ {foreach from=$users item=user key=i}
+ {if $user.login != 'anonymous'}
+ <tr class="editable" id="row{$i}">
+ <td id="userLogin" class="editable">{$user.login}</td>
+ <td id="password" class="editable">-</td>
+ <td id="email" class="editable">{$user.email}</td>
+ <td id="alias" class="editable">{$user.alias}</td>
+ <td id="token_auth">{$user.token_auth}</td>
+ <td><span class="edituser link_but" id="row{$i}"><img title="{'General_Edit'|translate}"
+ src='themes/default/images/ico_edit.png'/> {'General_Edit'|translate} </span></td>
+ <td><span class="deleteuser link_but" id="row{$i}"><img title="{'General_Delete'|translate}"
+ src='themes/default/images/ico_delete.png'/> {'General_Delete'|translate} </span>
+ </td>
+ </tr>
+ {/if}
+ {/foreach}
+ </tbody>
+ </table>
+ <div class="addrow"><img src='plugins/UsersManager/images/add.png'/> {'UsersManager_AddUser'|translate}</div>
+ </div>
{/if}
{include file="CoreAdminHome/templates/footer.tpl"}
diff --git a/plugins/UsersManager/templates/userSettings.js b/plugins/UsersManager/templates/userSettings.js
index abb00cc68b..0bfdf3d8bb 100644
--- a/plugins/UsersManager/templates/userSettings.js
+++ b/plugins/UsersManager/templates/userSettings.js
@@ -5,38 +5,35 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function sendUserSettingsAJAX()
-{
- var params;
- var defaultDate = $('input[name=defaultDate]:checked').val();
- if (defaultDate == 'today' || defaultDate == 'yesterday') {
- params = 'period=day&date='+defaultDate;
- } else if(defaultDate.indexOf('last') >= 0
- || defaultDate.indexOf('previous') >= 0) {
- params = 'period=range&date='+defaultDate;
- } else {
- params = 'date=today&period='+defaultDate;
- }
+function sendUserSettingsAJAX() {
+ var params;
+ var defaultDate = $('input[name=defaultDate]:checked').val();
+ if (defaultDate == 'today' || defaultDate == 'yesterday') {
+ params = 'period=day&date=' + defaultDate;
+ } else if (defaultDate.indexOf('last') >= 0
+ || defaultDate.indexOf('previous') >= 0) {
+ params = 'period=range&date=' + defaultDate;
+ } else {
+ params = 'date=today&period=' + defaultDate;
+ }
- var alias = $('#alias').val();
- var email = $('#email').val();
- var password = $('#password').val();
- var passwordBis = $('#passwordBis').val();
- var defaultReport = $('input[name=defaultReport]:checked').val();
- if (defaultReport == 1) {
- defaultReport = $('#defaultReportSiteSelector .custom_select_main_link').attr('siteid');
- }
- var postParams = {};
+ var alias = $('#alias').val();
+ var email = $('#email').val();
+ var password = $('#password').val();
+ var passwordBis = $('#passwordBis').val();
+ var defaultReport = $('input[name=defaultReport]:checked').val();
+ if (defaultReport == 1) {
+ defaultReport = $('#defaultReportSiteSelector .custom_select_main_link').attr('siteid');
+ }
+ var postParams = {};
postParams.alias = alias;
postParams.email = email;
- if (password)
- {
+ if (password) {
postParams.password = password;
- }
- if (passwordBis)
- {
+ }
+ if (passwordBis) {
postParams.passwordBis = passwordBis;
- }
+ }
postParams.defaultReport = defaultReport;
postParams.defaultDate = defaultDate;
@@ -52,13 +49,12 @@ function sendUserSettingsAJAX()
ajaxHandler.setErrorElement('#ajaxErrorUserSettings');
ajaxHandler.send(true);
}
-function sendAnonymousUserSettingsAJAX()
-{
- var anonymousDefaultReport = $('input[name=anonymousDefaultReport]:checked').val();
- if (anonymousDefaultReport == 1) {
- anonymousDefaultReport = $('#anonymousDefaultReportWebsite option:selected').val();
- }
- var anonymousDefaultDate = $('input[name=anonymousDefaultDate]:checked').val();
+function sendAnonymousUserSettingsAJAX() {
+ var anonymousDefaultReport = $('input[name=anonymousDefaultReport]:checked').val();
+ if (anonymousDefaultReport == 1) {
+ anonymousDefaultReport = $('#anonymousDefaultReportWebsite option:selected').val();
+ }
+ var anonymousDefaultDate = $('input[name=anonymousDefaultDate]:checked').val();
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
@@ -76,22 +72,23 @@ function sendAnonymousUserSettingsAJAX()
ajaxHandler.send(true);
}
-$(document).ready( function() {
- $('#userSettingsSubmit').click( function() {
- if($('#password').length > 0 && $('#password').val() != '') {
- piwikHelper.modalConfirm( '#confirmPasswordChange', {yes: sendUserSettingsAJAX});
- } else {
+$(document).ready(function () {
+ $('#userSettingsSubmit').click(function () {
+ if ($('#password').length > 0 && $('#password').val() != '') {
+ piwikHelper.modalConfirm('#confirmPasswordChange', {yes: sendUserSettingsAJAX});
+ } else {
sendUserSettingsAJAX();
- }
-
- });
- $('#userSettingsTable input').keypress( function(e) {
- var key=e.keyCode || e.which;
- if (key==13) {
- $('#userSettingsSubmit').click();
- }});
-
- $('#anonymousUserSettingsSubmit').click( function() {
- sendAnonymousUserSettingsAJAX();
- });
+ }
+
+ });
+ $('#userSettingsTable input').keypress(function (e) {
+ var key = e.keyCode || e.which;
+ if (key == 13) {
+ $('#userSettingsSubmit').click();
+ }
+ });
+
+ $('#anonymousUserSettingsSubmit').click(function () {
+ sendAnonymousUserSettingsAJAX();
+ });
});
diff --git a/plugins/UsersManager/templates/userSettings.tpl b/plugins/UsersManager/templates/userSettings.tpl
index 72449625dd..3219d1f561 100644
--- a/plugins/UsersManager/templates/userSettings.tpl
+++ b/plugins/UsersManager/templates/userSettings.tpl
@@ -2,138 +2,149 @@
{loadJavascriptTranslations plugins='UsersManager'}
<h2>{'UsersManager_MenuUserSettings'|translate}</h2>
-<br />
+<br/>
<div class="ui-confirm" id="confirmPasswordChange">
<h2>{'UsersManager_ChangePasswordConfirm'|translate}</h2>
- <input role="yes" type="button" value="{'General_Yes'|translate}" />
- <input role="no" type="button" value="{'General_No'|translate}" />
-</div>
+ <input role="yes" type="button" value="{'General_Yes'|translate}"/>
+ <input role="no" type="button" value="{'General_No'|translate}"/>
+</div>
<table id='userSettingsTable' class="adminTable" style='width:1000px'>
-<tr>
- <td><label for="username">{'General_Username'|translate} </label></td>
- <td>
- <input size="25" value="{$userLogin}" id="username" disabled="disabled" />
- <span class='form-description'>{'UsersManager_YourUsernameCannotBeChanged'|translate}</span>
- </td>
-</tr>
+ <tr>
+ <td><label for="username">{'General_Username'|translate} </label></td>
+ <td>
+ <input size="25" value="{$userLogin}" id="username" disabled="disabled"/>
+ <span class='form-description'>{'UsersManager_YourUsernameCannotBeChanged'|translate}</span>
+ </td>
+ </tr>
-<tr>
- <td><label for="alias">{'UsersManager_Alias'|translate} </label></td>
- <td><input size="25" value="{$userAlias}" id="alias"{if $isSuperUser} disabled="disabled"{/if} />
- {if $isSuperUser}
- <span class='form-description'>
+ <tr>
+ <td><label for="alias">{'UsersManager_Alias'|translate} </label></td>
+ <td><input size="25" value="{$userAlias}" id="alias"{if $isSuperUser} disabled="disabled"{/if} />
+ {if $isSuperUser}
+ <span class='form-description'>
{'UsersManager_TheSuperUserAliasCannotBeChanged'|translate}
</span>
- {/if}
- </td>
-</tr>
-<tr>
- <td><label for="email">{'UsersManager_Email'|translate} </label></td>
- <td><input size="25" value="{$userEmail}" id="email" /></td>
-</tr>
-<tr>
- <td>{'UsersManager_ReportToLoadByDefault'|translate}</td>
- <td>
- <fieldset>
- <label><input type="radio" value="MultiSites" name="defaultReport"{if $defaultReport=='MultiSites'} checked="checked"{/if} /> {'General_AllWebsitesDashboard'|translate}</label><br />
- <label style="padding-right:12px;"><input type="radio" value="1" name="defaultReport"{if $defaultReport!='MultiSites'} checked="checked"{/if} /> {'General_DashboardForASpecificWebsite'|translate}</label>
- {if $defaultReport=='MultiSites'}{assign var=defaultReportIdSite value=1}{else}{assign var=defaultReportIdSite value=$defaultReport}{/if}
- {include file="CoreHome/templates/sites_selection.tpl"
- siteName=$defaultReportSiteName idSite=$defaultReportIdSite switchSiteOnSelect=false showAllSitesItem=false
- showSelectedSite=false siteSelectorId='defaultReportSiteSelector'}
- </fieldset>
- </td>
-</tr>
-<tr>
- <td>{'UsersManager_ReportDateToLoadByDefault'|translate}</td>
- <td>
- <fieldset>
- {foreach from=$availableDefaultDates key=value item=description}
- <label><input type="radio"{if $defaultDate==$value} checked="checked"{/if} value="{$value}" name="defaultDate" /> {$description}</label><br />
- {/foreach}
- </fieldset>
- </td>
-</tr>
+ {/if}
+ </td>
+ </tr>
+ <tr>
+ <td><label for="email">{'UsersManager_Email'|translate} </label></td>
+ <td><input size="25" value="{$userEmail}" id="email"/></td>
+ </tr>
+ <tr>
+ <td>{'UsersManager_ReportToLoadByDefault'|translate}</td>
+ <td>
+ <fieldset>
+ <label><input type="radio" value="MultiSites"
+ name="defaultReport"{if $defaultReport=='MultiSites'} checked="checked"{/if} /> {'General_AllWebsitesDashboard'|translate}</label><br/>
+ <label style="padding-right:12px;"><input type="radio" value="1"
+ name="defaultReport"{if $defaultReport!='MultiSites'} checked="checked"{/if} /> {'General_DashboardForASpecificWebsite'|translate}
+ </label>
+ {if $defaultReport=='MultiSites'}{assign var=defaultReportIdSite value=1}{else}{assign var=defaultReportIdSite value=$defaultReport}{/if}
+ {include file="CoreHome/templates/sites_selection.tpl"
+ siteName=$defaultReportSiteName idSite=$defaultReportIdSite switchSiteOnSelect=false showAllSitesItem=false
+ showSelectedSite=false siteSelectorId='defaultReportSiteSelector'}
+ </fieldset>
+ </td>
+ </tr>
+ <tr>
+ <td>{'UsersManager_ReportDateToLoadByDefault'|translate}</td>
+ <td>
+ <fieldset>
+ {foreach from=$availableDefaultDates key=value item=description}
+ <label><input type="radio"{if $defaultDate==$value} checked="checked"{/if} value="{$value}" name="defaultDate"/> {$description}</label>
+ <br/>
+ {/foreach}
+ </fieldset>
+ </td>
+ </tr>
-{if isset($isValidHost) && $isValidHost}
-<tr>
- <td><label for="email">{'UsersManager_ChangePassword'|translate} </label></td>
- <td><input size="25" value="" autocomplete="off" id="password" type="password" />
- <span class='form-description'>{'UsersManager_IfYouWouldLikeToChangeThePasswordTypeANewOne'|translate}</span>
- <br /><br /><input size="25" value="" autocomplete="off" id="passwordBis" type="password" />
- <span class='form-description'> {'UsersManager_TypeYourPasswordAgain'|translate}</span>
- </td>
-</tr>
-{/if}
+ {if isset($isValidHost) && $isValidHost}
+ <tr>
+ <td><label for="email">{'UsersManager_ChangePassword'|translate} </label></td>
+ <td><input size="25" value="" autocomplete="off" id="password" type="password"/>
+ <span class='form-description'>{'UsersManager_IfYouWouldLikeToChangeThePasswordTypeANewOne'|translate}</span>
+ <br/><br/><input size="25" value="" autocomplete="off" id="passwordBis" type="password"/>
+ <span class='form-description'> {'UsersManager_TypeYourPasswordAgain'|translate}</span>
+ </td>
+ </tr>
+ {/if}
</table>
{if !isset($isValidHost) || !$isValidHost}
-<div class="ajaxSuccess">
- {'UsersManager_InjectedHostCannotChangePwd'|translate:$invalidHost}&nbsp;{if !$isSuperUser}{'UsersManager_EmailYourAdministrator'|translate:$invalidHostMailLinkStart:'</a>'}{/if}
-</div>
-<br/>
+ <div class="ajaxSuccess">
+ {'UsersManager_InjectedHostCannotChangePwd'|translate:$invalidHost}
+ &nbsp;{if !$isSuperUser}{'UsersManager_EmailYourAdministrator'|translate:$invalidHostMailLinkStart:'</a>'}{/if}
+ </div>
+ <br/>
{/if}
{ajaxErrorDiv id=ajaxErrorUserSettings}
{ajaxLoadingDiv id=ajaxLoadingUserSettings}
-<input type="submit" value="{'General_Save'|translate}" id="userSettingsSubmit" class="submit" />
+<input type="submit" value="{'General_Save'|translate}" id="userSettingsSubmit" class="submit"/>
<br/><br/>
<a name='excludeCookie'></a><h2>{'UsersManager_ExcludeVisitsViaCookie'|translate}</h2>
<p>{if $ignoreCookieSet}{'UsersManager_YourVisitsAreIgnoredOnDomain'|translate:"<strong>":$piwikHost:"</strong>"}
-{else}{'UsersManager_YourVisitsAreNotIgnored'|translate:"<strong>":"</strong>"}{/if}</p>
+ {else}{'UsersManager_YourVisitsAreNotIgnored'|translate:"<strong>":"</strong>"}{/if}</p>
<span style='margin-left:20px'>
<a href='{url token_auth=$token_auth action=setIgnoreCookie}#excludeCookie'>&rsaquo; {if $ignoreCookieSet}{'UsersManager_ClickHereToDeleteTheCookie'|translate}
-{else}{'UsersManager_ClickHereToSetTheCookieOnDomain'|translate:$piwikHost}{/if}
-<br />
+ {else}{'UsersManager_ClickHereToSetTheCookieOnDomain'|translate:$piwikHost}{/if}
+ <br/>
</a></span>
<br/><br/>
{if $isSuperUser}
- <h2>{'UsersManager_MenuAnonymousUserSettings'|translate}</h2>
- {if count($anonymousSites) == 0}
- <h3 class='form-description'><b>{'UsersManager_NoteNoAnonymousUserAccessSettingsWontBeUsed2'|translate}</b></h3><br />
- {else}
- <br />
-
- {ajaxErrorDiv id=ajaxErrorAnonymousUserSettings}
- {ajaxLoadingDiv id=ajaxLoadingAnonymousUserSettings}
-
- <table id='anonymousUserSettingsTable' class="adminTable" style='width:850px;'>
- <tr>
- <td style='width:400px'>{'UsersManager_WhenUsersAreNotLoggedInAndVisitPiwikTheyShouldAccess'|translate}</td>
- <td>
- <fieldset>
- <label><input type="radio" value="Login" name="anonymousDefaultReport"{if $anonymousDefaultReport==$loginModule} checked="checked"{/if} /> {'UsersManager_TheLoginScreen'|translate}</label><br />
- <label><input {if empty($anonymousSites)}disabled="disabled" {/if}type="radio" value="MultiSites" name="anonymousDefaultReport"{if $anonymousDefaultReport=='MultiSites'} checked="checked"{/if} /> {'General_AllWebsitesDashboard'|translate}</label><br />
-
- <label><input {if empty($anonymousSites)}disabled="disabled" {/if}type="radio" value="1" name="anonymousDefaultReport"{if $anonymousDefaultReport>0} checked="checked"{/if} /> {'General_DashboardForASpecificWebsite'|translate}</label>
- {if !empty($anonymousSites)}
- <select id="anonymousDefaultReportWebsite">
- {foreach from=$anonymousSites item=info}
- <option value="{$info.idsite}" {if $anonymousDefaultReport==$info.idsite} selected="selected"{/if}>{$info.name}</option>
- {/foreach}
- </select>
- {/if}
- </fieldset>
- </td>
- </tr>
- <tr>
- <td>{'UsersManager_ForAnonymousUsersReportDateToLoadByDefault'|translate}</td>
- <td>
- <fieldset>
- {foreach from=$availableDefaultDates key=value item=description}
- <label><input type="radio" {if $anonymousDefaultDate==$value}checked="checked" {/if}value="{$value}" name="anonymousDefaultDate" /> {$description}</label><br />
- {/foreach}
- </fieldset>
- </td>
- </tr>
-
- </table>
-
- <input type="submit" value="{'General_Save'|translate}" id="anonymousUserSettingsSubmit" class="submit"/>
- {/if}
+ <h2>{'UsersManager_MenuAnonymousUserSettings'|translate}</h2>
+ {if count($anonymousSites) == 0}
+ <h3 class='form-description'><b>{'UsersManager_NoteNoAnonymousUserAccessSettingsWontBeUsed2'|translate}</b></h3>
+ <br/>
+ {else}
+ <br/>
+ {ajaxErrorDiv id=ajaxErrorAnonymousUserSettings}
+ {ajaxLoadingDiv id=ajaxLoadingAnonymousUserSettings}
+ <table id='anonymousUserSettingsTable' class="adminTable" style='width:850px;'>
+ <tr>
+ <td style='width:400px'>{'UsersManager_WhenUsersAreNotLoggedInAndVisitPiwikTheyShouldAccess'|translate}</td>
+ <td>
+ <fieldset>
+ <label><input type="radio" value="Login"
+ name="anonymousDefaultReport"{if $anonymousDefaultReport==$loginModule} checked="checked"{/if} /> {'UsersManager_TheLoginScreen'|translate}
+ </label><br/>
+ <label><input {if empty($anonymousSites)}disabled="disabled" {/if}type="radio" value="MultiSites"
+ name="anonymousDefaultReport"{if $anonymousDefaultReport=='MultiSites'} checked="checked"{/if} /> {'General_AllWebsitesDashboard'|translate}
+ </label><br/>
+
+ <label><input {if empty($anonymousSites)}disabled="disabled" {/if}type="radio" value="1"
+ name="anonymousDefaultReport"{if $anonymousDefaultReport>0} checked="checked"{/if} /> {'General_DashboardForASpecificWebsite'|translate}
+ </label>
+ {if !empty($anonymousSites)}
+ <select id="anonymousDefaultReportWebsite">
+ {foreach from=$anonymousSites item=info}
+ <option value="{$info.idsite}" {if $anonymousDefaultReport==$info.idsite} selected="selected"{/if}>{$info.name}</option>
+ {/foreach}
+ </select>
+ {/if}
+ </fieldset>
+ </td>
+ </tr>
+ <tr>
+ <td>{'UsersManager_ForAnonymousUsersReportDateToLoadByDefault'|translate}</td>
+ <td>
+ <fieldset>
+ {foreach from=$availableDefaultDates key=value item=description}
+ <label><input type="radio" {if $anonymousDefaultDate==$value}checked="checked" {/if}value="{$value}"
+ name="anonymousDefaultDate"/> {$description}</label>
+ <br/>
+ {/foreach}
+ </fieldset>
+ </td>
+ </tr>
+
+ </table>
+ <input type="submit" value="{'General_Save'|translate}" id="anonymousUserSettingsSubmit" class="submit"/>
+ {/if}
{/if}
diff --git a/plugins/VisitFrequency/API.php b/plugins/VisitFrequency/API.php
index fbefbc505e..d712b67831 100644
--- a/plugins/VisitFrequency/API.php
+++ b/plugins/VisitFrequency/API.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_VisitFrequency
*/
@@ -13,137 +13,127 @@
* VisitFrequency API lets you access a list of metrics related to Returning Visitors.
* @package Piwik_VisitFrequency
*/
-class Piwik_VisitFrequency_API
+class Piwik_VisitFrequency_API
{
- static private $instance = null;
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- public function get( $idSite, $period, $date, $segment = false, $columns = false )
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
-
- // array values are comma separated
- $columns = Piwik::getArrayFromApiParameter($columns);
- $tempColumns = array();
-
- $bounceRateReturningRequested = $averageVisitDurationReturningRequested = $actionsPerVisitReturningRequested = false;
- if(!empty($columns))
- {
- // make sure base metrics are there for processed metrics
- if(false !== ($bounceRateReturningRequested = array_search('bounce_rate_returning', $columns)))
- {
- if (!in_array('nb_visits_returning', $columns)) $tempColumns[] = 'nb_visits_returning';
- if (!in_array('bounce_count_returning', $columns)) $tempColumns[] = 'bounce_count_returning';
- unset($columns[$bounceRateReturningRequested]);
- }
- if(false !== ($actionsPerVisitReturningRequested = array_search('nb_actions_per_visit_returning', $columns)))
- {
- if (!in_array('nb_actions_returning', $columns)) $tempColumns[] = 'nb_actions_returning';
- if (!in_array('nb_visits_returning', $columns)) $tempColumns[] = 'nb_visits_returning';
- unset($columns[$actionsPerVisitReturningRequested]);
- }
- if(false !== ($averageVisitDurationReturningRequested = array_search('avg_time_on_site_returning', $columns)))
- {
- if (!in_array('sum_visit_length_returning', $columns)) $tempColumns[] = 'sum_visit_length_returning';
- if (!in_array('nb_visits_returning', $columns)) $tempColumns[] = 'nb_visits_returning';
- unset($columns[$averageVisitDurationReturningRequested]);
- }
-
- $tempColumns = array_unique($tempColumns);
- $columns = array_merge($columns, $tempColumns);
- }
- else
- {
- $bounceRateReturningRequested = $averageVisitDurationReturningRequested = $actionsPerVisitReturningRequested = true;
- $columns = array(
- 'nb_visits_returning',
- 'nb_actions_returning',
- 'max_actions_returning',
- 'sum_visit_length_returning',
- 'bounce_count_returning',
- 'nb_visits_converted_returning',
- );
-
- if($period=='day')
- {
- $columns = array_merge(array('nb_uniq_visitors_returning'), $columns);
- }
- }
- $dataTable = $archive->getDataTableFromNumeric($columns);
-
- // Process ratio metrics
- if($bounceRateReturningRequested !== false)
- {
- $dataTable->filter('ColumnCallbackAddColumnPercentage', array('bounce_rate_returning', 'bounce_count_returning', 'nb_visits_returning', 0));
- }
- if($actionsPerVisitReturningRequested !== false)
- {
- $dataTable->filter('ColumnCallbackAddColumnQuotient', array('nb_actions_per_visit_returning', 'nb_actions_returning', 'nb_visits_returning', 1));
- }
- if($averageVisitDurationReturningRequested !== false)
- {
- $dataTable->filter('ColumnCallbackAddColumnQuotient', array('avg_time_on_site_returning', 'sum_visit_length_returning', 'nb_visits_returning', 0));
- }
-
- // remove temporary metrics that were used to compute processed metrics
- $dataTable->deleteColumns($tempColumns);
-
- return $dataTable;
- }
-
- protected function getNumeric( $idSite, $period, $date, $toFetch )
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date );
- $dataTable = $archive->getNumeric($toFetch);
- return $dataTable;
- }
-
- /**
- * @ignore
- */
- public function getVisitsReturning( $idSite, $period, $date )
- {
- return $this->getNumeric( $idSite, $period, $date, 'nb_visits_returning');
- }
-
- /**
- * @ignore
- */
- public function getActionsReturning( $idSite, $period, $date )
- {
- return $this->getNumeric( $idSite, $period, $date, 'nb_actions_returning');
- }
-
- /**
- * @ignore
- */
- public function getSumVisitsLengthReturning( $idSite, $period, $date )
- {
- return $this->getNumeric( $idSite, $period, $date, 'sum_visit_length_returning');
- }
-
- /**
- * @ignore
- */
- public function getBounceCountReturning( $idSite, $period, $date )
- {
- return $this->getNumeric( $idSite, $period, $date, 'bounce_count_returning');
- }
-
- /**
- * @ignore
- */
- public function getConvertedVisitsReturning( $idSite, $period, $date )
- {
- return $this->getNumeric( $idSite, $period, $date, 'nb_visits_converted_returning');
- }
+ static private $instance = null;
+
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ public function get($idSite, $period, $date, $segment = false, $columns = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+
+ // array values are comma separated
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ $tempColumns = array();
+
+ $bounceRateReturningRequested = $averageVisitDurationReturningRequested = $actionsPerVisitReturningRequested = false;
+ if (!empty($columns)) {
+ // make sure base metrics are there for processed metrics
+ if (false !== ($bounceRateReturningRequested = array_search('bounce_rate_returning', $columns))) {
+ if (!in_array('nb_visits_returning', $columns)) $tempColumns[] = 'nb_visits_returning';
+ if (!in_array('bounce_count_returning', $columns)) $tempColumns[] = 'bounce_count_returning';
+ unset($columns[$bounceRateReturningRequested]);
+ }
+ if (false !== ($actionsPerVisitReturningRequested = array_search('nb_actions_per_visit_returning', $columns))) {
+ if (!in_array('nb_actions_returning', $columns)) $tempColumns[] = 'nb_actions_returning';
+ if (!in_array('nb_visits_returning', $columns)) $tempColumns[] = 'nb_visits_returning';
+ unset($columns[$actionsPerVisitReturningRequested]);
+ }
+ if (false !== ($averageVisitDurationReturningRequested = array_search('avg_time_on_site_returning', $columns))) {
+ if (!in_array('sum_visit_length_returning', $columns)) $tempColumns[] = 'sum_visit_length_returning';
+ if (!in_array('nb_visits_returning', $columns)) $tempColumns[] = 'nb_visits_returning';
+ unset($columns[$averageVisitDurationReturningRequested]);
+ }
+
+ $tempColumns = array_unique($tempColumns);
+ $columns = array_merge($columns, $tempColumns);
+ } else {
+ $bounceRateReturningRequested = $averageVisitDurationReturningRequested = $actionsPerVisitReturningRequested = true;
+ $columns = array(
+ 'nb_visits_returning',
+ 'nb_actions_returning',
+ 'max_actions_returning',
+ 'sum_visit_length_returning',
+ 'bounce_count_returning',
+ 'nb_visits_converted_returning',
+ );
+
+ if ($period == 'day') {
+ $columns = array_merge(array('nb_uniq_visitors_returning'), $columns);
+ }
+ }
+ $dataTable = $archive->getDataTableFromNumeric($columns);
+
+ // Process ratio metrics
+ if ($bounceRateReturningRequested !== false) {
+ $dataTable->filter('ColumnCallbackAddColumnPercentage', array('bounce_rate_returning', 'bounce_count_returning', 'nb_visits_returning', 0));
+ }
+ if ($actionsPerVisitReturningRequested !== false) {
+ $dataTable->filter('ColumnCallbackAddColumnQuotient', array('nb_actions_per_visit_returning', 'nb_actions_returning', 'nb_visits_returning', 1));
+ }
+ if ($averageVisitDurationReturningRequested !== false) {
+ $dataTable->filter('ColumnCallbackAddColumnQuotient', array('avg_time_on_site_returning', 'sum_visit_length_returning', 'nb_visits_returning', 0));
+ }
+
+ // remove temporary metrics that were used to compute processed metrics
+ $dataTable->deleteColumns($tempColumns);
+
+ return $dataTable;
+ }
+
+ protected function getNumeric($idSite, $period, $date, $toFetch)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date);
+ $dataTable = $archive->getNumeric($toFetch);
+ return $dataTable;
+ }
+
+ /**
+ * @ignore
+ */
+ public function getVisitsReturning($idSite, $period, $date)
+ {
+ return $this->getNumeric($idSite, $period, $date, 'nb_visits_returning');
+ }
+
+ /**
+ * @ignore
+ */
+ public function getActionsReturning($idSite, $period, $date)
+ {
+ return $this->getNumeric($idSite, $period, $date, 'nb_actions_returning');
+ }
+
+ /**
+ * @ignore
+ */
+ public function getSumVisitsLengthReturning($idSite, $period, $date)
+ {
+ return $this->getNumeric($idSite, $period, $date, 'sum_visit_length_returning');
+ }
+
+ /**
+ * @ignore
+ */
+ public function getBounceCountReturning($idSite, $period, $date)
+ {
+ return $this->getNumeric($idSite, $period, $date, 'bounce_count_returning');
+ }
+
+ /**
+ * @ignore
+ */
+ public function getConvertedVisitsReturning($idSite, $period, $date)
+ {
+ return $this->getNumeric($idSite, $period, $date, 'nb_visits_converted_returning');
+ }
}
diff --git a/plugins/VisitFrequency/Controller.php b/plugins/VisitFrequency/Controller.php
index ba7cf432d1..9709bc9774 100644
--- a/plugins/VisitFrequency/Controller.php
+++ b/plugins/VisitFrequency/Controller.php
@@ -15,91 +15,89 @@
*/
class Piwik_VisitFrequency_Controller extends Piwik_Controller
{
- function index()
- {
- $view = Piwik_View::factory('index');
- $view->graphEvolutionVisitFrequency = $this->getEvolutionGraph(true, array('nb_visits_returning') );
- $this->setSparklinesAndNumbers($view);
- echo $view->render();
- }
-
- public function getSparklines()
- {
- $view = Piwik_View::factory('sparklines');
- $this->setSparklinesAndNumbers($view);
- echo $view->render();
- }
-
- public function getEvolutionGraph( $fetch = false, array $columns = array())
- {
- if(empty($columns))
- {
- $columns = Piwik_Common::getRequestVar('columns');
- $columns = Piwik::getArrayFromApiParameter($columns);
- }
-
- $documentation = Piwik_Translate('VisitFrequency_ReturningVisitsDocumentation').'<br />'
- . Piwik_Translate('General_BrokenDownReportDocumentation').'<br />'
- . Piwik_Translate('VisitFrequency_ReturningVisitDocumentation');
-
- // Note: if you edit this array, maybe edit the code below as well
- $selectableColumns = array(
- // columns from VisitFrequency.get
- 'nb_visits_returning',
- 'nb_actions_returning',
- 'nb_actions_per_visit_returning',
- 'bounce_rate_returning',
- 'avg_time_on_site_returning',
- // columns from VisitsSummary.get
- 'nb_visits',
- 'nb_actions',
- 'nb_actions_per_visit',
- 'bounce_rate',
- 'avg_time_on_site'
- );
-
- $period = Piwik_Common::getRequestVar('period', false);
- if ($period == 'day')
- {
- // add number of unique (returning) visitors for period=day
- $selectableColumns = array_merge(
- array($selectableColumns[0]),
- array('nb_uniq_visitors_returning'),
- array_slice($selectableColumns, 1, -4),
- array('nb_uniq_visitors'),
- array_slice($selectableColumns, -4));
- }
-
- $view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, __FUNCTION__, $columns,
- $selectableColumns, $documentation);
-
- return $this->renderView($view, $fetch);
- }
-
- protected function setSparklinesAndNumbers($view)
- {
- $view->urlSparklineNbVisitsReturning = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_visits_returning')));
- $view->urlSparklineNbActionsReturning = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_actions_returning')));
- $view->urlSparklineActionsPerVisitReturning = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_actions_per_visit_returning')));
- $view->urlSparklineAvgVisitDurationReturning = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('avg_time_on_site_returning')));
- $view->urlSparklineBounceRateReturning = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('bounce_rate_returning')));
-
- $dataTableFrequency = $this->getSummary();
- $dataRow = $dataTableFrequency->getFirstRow();
- $nbVisitsReturning = $dataRow->getColumn('nb_visits_returning');
- $view->nbVisitsReturning = $nbVisitsReturning;
- $view->nbActionsReturning = $dataRow->getColumn('nb_actions_returning');
- $view->nbActionsPerVisitReturning = $dataRow->getColumn('nb_actions_per_visit_returning');
- $view->avgVisitDurationReturning = $dataRow->getColumn('avg_time_on_site_returning');
- $nbBouncedReturningVisits = $dataRow->getColumn('bounce_count_returning');
- $view->bounceRateReturning = Piwik::getPercentageSafe($nbBouncedReturningVisits, $nbVisitsReturning);
-
- }
+ function index()
+ {
+ $view = Piwik_View::factory('index');
+ $view->graphEvolutionVisitFrequency = $this->getEvolutionGraph(true, array('nb_visits_returning'));
+ $this->setSparklinesAndNumbers($view);
+ echo $view->render();
+ }
- protected function getSummary()
- {
- $requestString = "method=VisitFrequency.get&format=original";
- $request = new Piwik_API_Request($requestString);
- return $request->process();
- }
+ public function getSparklines()
+ {
+ $view = Piwik_View::factory('sparklines');
+ $this->setSparklinesAndNumbers($view);
+ echo $view->render();
+ }
+
+ public function getEvolutionGraph($fetch = false, array $columns = array())
+ {
+ if (empty($columns)) {
+ $columns = Piwik_Common::getRequestVar('columns');
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ }
+
+ $documentation = Piwik_Translate('VisitFrequency_ReturningVisitsDocumentation') . '<br />'
+ . Piwik_Translate('General_BrokenDownReportDocumentation') . '<br />'
+ . Piwik_Translate('VisitFrequency_ReturningVisitDocumentation');
+
+ // Note: if you edit this array, maybe edit the code below as well
+ $selectableColumns = array(
+ // columns from VisitFrequency.get
+ 'nb_visits_returning',
+ 'nb_actions_returning',
+ 'nb_actions_per_visit_returning',
+ 'bounce_rate_returning',
+ 'avg_time_on_site_returning',
+ // columns from VisitsSummary.get
+ 'nb_visits',
+ 'nb_actions',
+ 'nb_actions_per_visit',
+ 'bounce_rate',
+ 'avg_time_on_site'
+ );
+
+ $period = Piwik_Common::getRequestVar('period', false);
+ if ($period == 'day') {
+ // add number of unique (returning) visitors for period=day
+ $selectableColumns = array_merge(
+ array($selectableColumns[0]),
+ array('nb_uniq_visitors_returning'),
+ array_slice($selectableColumns, 1, -4),
+ array('nb_uniq_visitors'),
+ array_slice($selectableColumns, -4));
+ }
+
+ $view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, __FUNCTION__, $columns,
+ $selectableColumns, $documentation);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ protected function setSparklinesAndNumbers($view)
+ {
+ $view->urlSparklineNbVisitsReturning = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_visits_returning')));
+ $view->urlSparklineNbActionsReturning = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_actions_returning')));
+ $view->urlSparklineActionsPerVisitReturning = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_actions_per_visit_returning')));
+ $view->urlSparklineAvgVisitDurationReturning = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_time_on_site_returning')));
+ $view->urlSparklineBounceRateReturning = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('bounce_rate_returning')));
+
+ $dataTableFrequency = $this->getSummary();
+ $dataRow = $dataTableFrequency->getFirstRow();
+ $nbVisitsReturning = $dataRow->getColumn('nb_visits_returning');
+ $view->nbVisitsReturning = $nbVisitsReturning;
+ $view->nbActionsReturning = $dataRow->getColumn('nb_actions_returning');
+ $view->nbActionsPerVisitReturning = $dataRow->getColumn('nb_actions_per_visit_returning');
+ $view->avgVisitDurationReturning = $dataRow->getColumn('avg_time_on_site_returning');
+ $nbBouncedReturningVisits = $dataRow->getColumn('bounce_count_returning');
+ $view->bounceRateReturning = Piwik::getPercentageSafe($nbBouncedReturningVisits, $nbVisitsReturning);
+
+ }
+
+ protected function getSummary()
+ {
+ $requestString = "method=VisitFrequency.get&format=original";
+ $request = new Piwik_API_Request($requestString);
+ return $request->process();
+ }
}
diff --git a/plugins/VisitFrequency/VisitFrequency.php b/plugins/VisitFrequency/VisitFrequency.php
index adfcaf03c9..4659feee91 100644
--- a/plugins/VisitFrequency/VisitFrequency.php
+++ b/plugins/VisitFrequency/VisitFrequency.php
@@ -15,137 +15,135 @@
*/
class Piwik_VisitFrequency extends Piwik_Plugin
{
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('VisitFrequency_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- return $info;
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenu',
- 'API.getReportMetadata' => 'getReportMetadata',
- );
- return $hooks;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $reports = &$notification->getNotificationObject();
- $reports[] = array(
- 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('VisitFrequency_ColumnReturningVisits'),
- 'module' => 'VisitFrequency',
- 'action' => 'get',
- 'metrics' => array(
- 'nb_visits_returning' => Piwik_Translate('VisitFrequency_ColumnReturningVisits'),
- 'nb_actions_returning' => Piwik_Translate('VisitFrequency_ColumnActionsByReturningVisits'),
- 'avg_time_on_site_returning' => Piwik_Translate('VisitFrequency_ColumnAverageVisitDurationForReturningVisitors'),
- 'bounce_rate_returning' => Piwik_Translate('VisitFrequency_ColumnBounceRateForReturningVisits'),
- 'nb_actions_per_visit_returning' => Piwik_Translate('VisitFrequency_ColumnAvgActionsPerReturningVisit'),
- 'nb_uniq_visitors_returning' => Piwik_Translate('VisitFrequency_ColumnUniqueReturningVisitors'),
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('VisitFrequency_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenu',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ );
+ return $hooks;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $reports = & $notification->getNotificationObject();
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('VisitFrequency_ColumnReturningVisits'),
+ 'module' => 'VisitFrequency',
+ 'action' => 'get',
+ 'metrics' => array(
+ 'nb_visits_returning' => Piwik_Translate('VisitFrequency_ColumnReturningVisits'),
+ 'nb_actions_returning' => Piwik_Translate('VisitFrequency_ColumnActionsByReturningVisits'),
+ 'avg_time_on_site_returning' => Piwik_Translate('VisitFrequency_ColumnAverageVisitDurationForReturningVisitors'),
+ 'bounce_rate_returning' => Piwik_Translate('VisitFrequency_ColumnBounceRateForReturningVisits'),
+ 'nb_actions_per_visit_returning' => Piwik_Translate('VisitFrequency_ColumnAvgActionsPerReturningVisit'),
+ 'nb_uniq_visitors_returning' => Piwik_Translate('VisitFrequency_ColumnUniqueReturningVisitors'),
// Not displayed
// 'nb_visits_converted_returning',
// 'sum_visit_length_returning',
// 'max_actions_returning',
// 'bounce_count_returning',
- ),
- 'processedMetrics' => false,
- 'order' => 40
- );
- }
-
- function addWidgets()
- {
- Piwik_AddWidget( 'General_Visitors', 'VisitFrequency_WidgetOverview', 'VisitFrequency', 'getSparklines');
- Piwik_AddWidget( 'General_Visitors', 'VisitFrequency_WidgetGraphReturning', 'VisitFrequency', 'getEvolutionGraph', array('columns' => array('nb_visits_returning')));
- }
-
- function addMenu()
- {
- Piwik_AddMenu('General_Visitors', 'VisitFrequency_SubmenuFrequency', array('module' => 'VisitFrequency', 'action' => 'index'));
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archivePeriod( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $numericToSum = array(
- 'nb_visits_returning',
- 'nb_actions_returning',
- 'sum_visit_length_returning',
- 'bounce_count_returning',
- 'nb_visits_converted_returning',
- );
- $archiveProcessing->archiveNumericValuesSum($numericToSum);
- $archiveProcessing->archiveNumericValuesMax('max_actions_returning');
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archiveDay($notification)
- {
- /* @var $archiveProcessing Piwik_ArchiveProcessing */
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $select = "count(distinct log_visit.idvisitor) as nb_uniq_visitors_returning,
+ ),
+ 'processedMetrics' => false,
+ 'order' => 40
+ );
+ }
+
+ function addWidgets()
+ {
+ Piwik_AddWidget('General_Visitors', 'VisitFrequency_WidgetOverview', 'VisitFrequency', 'getSparklines');
+ Piwik_AddWidget('General_Visitors', 'VisitFrequency_WidgetGraphReturning', 'VisitFrequency', 'getEvolutionGraph', array('columns' => array('nb_visits_returning')));
+ }
+
+ function addMenu()
+ {
+ Piwik_AddMenu('General_Visitors', 'VisitFrequency_SubmenuFrequency', array('module' => 'VisitFrequency', 'action' => 'index'));
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ function archivePeriod($notification)
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $numericToSum = array(
+ 'nb_visits_returning',
+ 'nb_actions_returning',
+ 'sum_visit_length_returning',
+ 'bounce_count_returning',
+ 'nb_visits_converted_returning',
+ );
+ $archiveProcessing->archiveNumericValuesSum($numericToSum);
+ $archiveProcessing->archiveNumericValuesMax('max_actions_returning');
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ function archiveDay($notification)
+ {
+ /* @var $archiveProcessing Piwik_ArchiveProcessing */
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $select = "count(distinct log_visit.idvisitor) as nb_uniq_visitors_returning,
count(*) as nb_visits_returning,
sum(log_visit.visit_total_actions) as nb_actions_returning,
max(log_visit.visit_total_actions) as max_actions_returning,
sum(log_visit.visit_total_time) as sum_visit_length_returning,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as bounce_count_returning,
sum(case log_visit.visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted_returning";
-
- $from = "log_visit";
-
- $where = "log_visit.visit_last_action_time >= ?
+
+ $from = "log_visit";
+
+ $where = "log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?
AND log_visit.visitor_returning >= 1";
-
- $bind = array($archiveProcessing->getStartDatetimeUTC(),
- $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite);
-
- $query = $archiveProcessing->getSegment()->getSelectQuery($select, $from, $where, $bind);
-
- $row = $archiveProcessing->db->fetchRow($query['sql'], $query['bind']);
-
- if($row === false || $row === null)
- {
- $row['nb_visits_returning'] = 0;
- $row['nb_actions_returning'] = 0;
- $row['max_actions_returning'] = 0;
- $row['sum_visit_length_returning'] = 0;
- $row['bounce_count_returning'] = 0;
- $row['nb_visits_converted_returning'] = 0;
- }
-
- foreach($row as $name => $value)
- {
- $archiveProcessing->insertNumericRecord($name, $value);
- }
- }
+
+ $bind = array($archiveProcessing->getStartDatetimeUTC(),
+ $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite);
+
+ $query = $archiveProcessing->getSegment()->getSelectQuery($select, $from, $where, $bind);
+
+ $row = $archiveProcessing->db->fetchRow($query['sql'], $query['bind']);
+
+ if ($row === false || $row === null) {
+ $row['nb_visits_returning'] = 0;
+ $row['nb_actions_returning'] = 0;
+ $row['max_actions_returning'] = 0;
+ $row['sum_visit_length_returning'] = 0;
+ $row['bounce_count_returning'] = 0;
+ $row['nb_visits_converted_returning'] = 0;
+ }
+
+ foreach ($row as $name => $value) {
+ $archiveProcessing->insertNumericRecord($name, $value);
+ }
+ }
}
diff --git a/plugins/VisitFrequency/templates/index.tpl b/plugins/VisitFrequency/templates/index.tpl
index 94bbbfbcc9..d44c578c09 100644
--- a/plugins/VisitFrequency/templates/index.tpl
+++ b/plugins/VisitFrequency/templates/index.tpl
@@ -3,8 +3,8 @@
<a name="evolutionGraph" graphId="VisitFrequencygetEvolutionGraph"></a>
<h2>{'VisitFrequency_ColumnReturningVisits'|translate}</h2>
{$graphEvolutionVisitFrequency}
-<br />
+<br/>
{include file="VisitFrequency/templates/sparklines.tpl"}
-
+
{postEvent name="template_footerVisitsFrequency"}
diff --git a/plugins/VisitFrequency/templates/sparklines.tpl b/plugins/VisitFrequency/templates/sparklines.tpl
index 250d924e56..e3a3ba9dc4 100644
--- a/plugins/VisitFrequency/templates/sparklines.tpl
+++ b/plugins/VisitFrequency/templates/sparklines.tpl
@@ -1,13 +1,12 @@
-
<div class="sparkline">{sparkline src=$urlSparklineNbVisitsReturning}
-{'VisitFrequency_ReturnVisits'|translate:"<strong>$nbVisitsReturning</strong>"}</div>
+ {'VisitFrequency_ReturnVisits'|translate:"<strong>$nbVisitsReturning</strong>"}</div>
<div class="sparkline">{sparkline src=$urlSparklineNbActionsReturning}
-{'VisitFrequency_ReturnActions'|translate:"<strong>$nbActionsReturning</strong>"}</div>
+ {'VisitFrequency_ReturnActions'|translate:"<strong>$nbActionsReturning</strong>"}</div>
<div class="sparkline">{sparkline src=$urlSparklineActionsPerVisitReturning}
- {'VisitFrequency_ReturnAvgActions'|translate:"<strong>$nbActionsPerVisitReturning</strong>"}</div>
+ {'VisitFrequency_ReturnAvgActions'|translate:"<strong>$nbActionsPerVisitReturning</strong>"}</div>
<div class="sparkline">{sparkline src=$urlSparklineAvgVisitDurationReturning}
- {assign var=avgVisitDurationReturning value=$avgVisitDurationReturning|sumtime}
- {'VisitFrequency_ReturnAverageVisitDuration'|translate:"<strong>$avgVisitDurationReturning</strong>"}</div>
+ {assign var=avgVisitDurationReturning value=$avgVisitDurationReturning|sumtime}
+ {'VisitFrequency_ReturnAverageVisitDuration'|translate:"<strong>$avgVisitDurationReturning</strong>"}</div>
<div class="sparkline">{sparkline src=$urlSparklineBounceRateReturning}
- {'VisitFrequency_ReturnBounceRate'|translate:"<strong>$bounceRateReturning%</strong>"} </div>
+ {'VisitFrequency_ReturnBounceRate'|translate:"<strong>$bounceRateReturning%</strong>"} </div>
{include file="CoreHome/templates/sparkline_footer.tpl"}
diff --git a/plugins/VisitTime/API.php b/plugins/VisitTime/API.php
index 04e38a3768..3da0646fb7 100644
--- a/plugins/VisitTime/API.php
+++ b/plugins/VisitTime/API.php
@@ -1,184 +1,175 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_VisitTime
*/
/**
* VisitTime API lets you access reports by Hour (Server time), and by Hour Local Time of your visitors.
- *
+ *
* @package Piwik_VisitTime
*/
class Piwik_VisitTime_API
{
- static private $instance = null;
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- protected function getDataTable($name, $idSite, $period, $date, $segment )
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- $dataTable = $archive->getDataTable($name);
- $dataTable->filter('Sort', array('label', 'asc', true));
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getTimeLabel'));
- $dataTable->queueFilter('ReplaceColumnNames');
- return $dataTable;
- }
-
- public function getVisitInformationPerLocalTime( $idSite, $period, $date, $segment = false )
- {
- return $this->getDataTable('VisitTime_localTime', $idSite, $period, $date, $segment );
- }
-
- public function getVisitInformationPerServerTime( $idSite, $period, $date, $segment = false, $hideFutureHoursWhenToday = false )
- {
- $table = $this->getDataTable('VisitTime_serverTime', $idSite, $period, $date, $segment );
- if($hideFutureHoursWhenToday)
- {
- $table = $this->removeHoursInFuture($table, $idSite, $period, $date);
- }
- return $table;
- }
-
- /**
- * Returns datatable describing the number of visits for each day of the week.
- *
- * @param string $idSite The site ID. Cannot refer to multiple sites.
- * @param string $period The period type: day, week, year, range...
- * @param string $date The start date of the period. Cannot refer to multiple dates.
- * @param string $segment The segment.
- * @return Piwik_DataTable
- */
- public function getByDayOfWeek( $idSite, $period, $date, $segment = false )
- {
- Piwik::checkUserHasViewAccess( $idSite );
-
- // disabled for multiple sites/dates
- if (Piwik_Archive::isMultipleSites($idSite))
- {
- throw new Exception("VisitTime.getByDayOfWeek does not support multiple sites.");
- }
-
- if (Piwik_Archive::isMultiplePeriod($date, $period))
- {
- throw new Exception("VisitTime.getByDayOfWeek does not support multiple dates.");
- }
-
- // metrics to query
- $metrics = Piwik_ArchiveProcessing::getCoreMetrics();
-
- // get metric data for every day within the supplied period
- $oSite = new Piwik_Site($idSite);
- $oPeriod = Piwik_Archive::makePeriodFromQueryParams($oSite, $period, $date);
- $dateRange = $oPeriod->getDateStart()->toString().','.$oPeriod->getDateEnd()->toString();
-
- $archive = Piwik_Archive::build($idSite, 'day', $dateRange, $segment);
- $dataTable = $archive->getDataTableFromNumeric($metrics)->mergeChildren();
-
- // if there's no data for this report, don't bother w/ anything else
- if ($dataTable->getRowsCount() == 0)
- {
- return $dataTable;
- }
-
- // group by the day of the week (see below for dayOfWeekFromDate function)
- $dataTable->filter('GroupBy', array('label', 'Piwik_VisitTime_dayOfWeekFromDate'));
-
- // create new datatable w/ empty rows, then add calculated datatable
- $rows = array();
- foreach (array(1,2,3,4,5,6,7) as $day)
- {
- $rows[] = array('label' => $day, 'nb_visits' => 0);
- }
-
- $result = new Piwik_DataTable();
- $result->addRowsFromSimpleArray($rows);
- $result->addDataTable($dataTable);
-
- // set day of week integer as metadata
- $result->filter('ColumnCallbackAddMetadata', array('label', 'day_of_week'));
-
- // translate labels
- $result->filter('ColumnCallbackReplace', array('label', 'Piwik_VisitTime_translateDayOfWeek'));
-
- // set datatable metadata for period start & finish
- $result->setMetadata('date_start', $oPeriod->getDateStart());
- $result->setMetadata('date_end', $oPeriod->getDateEnd());
-
- return $result;
- }
-
- protected function removeHoursInFuture($table, $idSite, $period, $date)
- {
- $site = new Piwik_Site($idSite);
-
- if( $period == 'day'
- && ($date == 'today'
- || $date == Piwik_Date::factory('now', $site->getTimezone())->toString()))
- {
- $currentHour = Piwik_Date::factory('now', $site->getTimezone())->toString('G');
- // If no data for today, this is an exception to the API output rule, as we normally return nothing:
- // we shall return all hours of the day, with nb_visits = 0
- if($table->getRowsCount() == 0)
- {
- for($hour = 0; $hour <= $currentHour; $hour++)
- {
- $table->addRowFromSimpleArray( array('label' => $hour, 'nb_visits' => 0));
- }
- return $table;
- }
-
- $idsToDelete = array();
- foreach($table->getRows() as $id => $row)
- {
- $hour = $row->getColumn('label');
- if($hour > $currentHour)
- {
- $idsToDelete[] = $id;
- }
- }
- $table->deleteRows($idsToDelete);
- }
- return $table;
- }
+ static private $instance = null;
+
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ protected function getDataTable($name, $idSite, $period, $date, $segment)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ $dataTable = $archive->getDataTable($name);
+ $dataTable->filter('Sort', array('label', 'asc', true));
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getTimeLabel'));
+ $dataTable->queueFilter('ReplaceColumnNames');
+ return $dataTable;
+ }
+
+ public function getVisitInformationPerLocalTime($idSite, $period, $date, $segment = false)
+ {
+ return $this->getDataTable('VisitTime_localTime', $idSite, $period, $date, $segment);
+ }
+
+ public function getVisitInformationPerServerTime($idSite, $period, $date, $segment = false, $hideFutureHoursWhenToday = false)
+ {
+ $table = $this->getDataTable('VisitTime_serverTime', $idSite, $period, $date, $segment);
+ if ($hideFutureHoursWhenToday) {
+ $table = $this->removeHoursInFuture($table, $idSite, $period, $date);
+ }
+ return $table;
+ }
+
+ /**
+ * Returns datatable describing the number of visits for each day of the week.
+ *
+ * @param string $idSite The site ID. Cannot refer to multiple sites.
+ * @param string $period The period type: day, week, year, range...
+ * @param string $date The start date of the period. Cannot refer to multiple dates.
+ * @param string $segment The segment.
+ * @return Piwik_DataTable
+ */
+ public function getByDayOfWeek($idSite, $period, $date, $segment = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+
+ // disabled for multiple sites/dates
+ if (Piwik_Archive::isMultipleSites($idSite)) {
+ throw new Exception("VisitTime.getByDayOfWeek does not support multiple sites.");
+ }
+
+ if (Piwik_Archive::isMultiplePeriod($date, $period)) {
+ throw new Exception("VisitTime.getByDayOfWeek does not support multiple dates.");
+ }
+
+ // metrics to query
+ $metrics = Piwik_ArchiveProcessing::getCoreMetrics();
+
+ // get metric data for every day within the supplied period
+ $oSite = new Piwik_Site($idSite);
+ $oPeriod = Piwik_Archive::makePeriodFromQueryParams($oSite, $period, $date);
+ $dateRange = $oPeriod->getDateStart()->toString() . ',' . $oPeriod->getDateEnd()->toString();
+
+ $archive = Piwik_Archive::build($idSite, 'day', $dateRange, $segment);
+ $dataTable = $archive->getDataTableFromNumeric($metrics)->mergeChildren();
+
+ // if there's no data for this report, don't bother w/ anything else
+ if ($dataTable->getRowsCount() == 0) {
+ return $dataTable;
+ }
+
+ // group by the day of the week (see below for dayOfWeekFromDate function)
+ $dataTable->filter('GroupBy', array('label', 'Piwik_VisitTime_dayOfWeekFromDate'));
+
+ // create new datatable w/ empty rows, then add calculated datatable
+ $rows = array();
+ foreach (array(1, 2, 3, 4, 5, 6, 7) as $day) {
+ $rows[] = array('label' => $day, 'nb_visits' => 0);
+ }
+
+ $result = new Piwik_DataTable();
+ $result->addRowsFromSimpleArray($rows);
+ $result->addDataTable($dataTable);
+
+ // set day of week integer as metadata
+ $result->filter('ColumnCallbackAddMetadata', array('label', 'day_of_week'));
+
+ // translate labels
+ $result->filter('ColumnCallbackReplace', array('label', 'Piwik_VisitTime_translateDayOfWeek'));
+
+ // set datatable metadata for period start & finish
+ $result->setMetadata('date_start', $oPeriod->getDateStart());
+ $result->setMetadata('date_end', $oPeriod->getDateEnd());
+
+ return $result;
+ }
+
+ protected function removeHoursInFuture($table, $idSite, $period, $date)
+ {
+ $site = new Piwik_Site($idSite);
+
+ if ($period == 'day'
+ && ($date == 'today'
+ || $date == Piwik_Date::factory('now', $site->getTimezone())->toString())
+ ) {
+ $currentHour = Piwik_Date::factory('now', $site->getTimezone())->toString('G');
+ // If no data for today, this is an exception to the API output rule, as we normally return nothing:
+ // we shall return all hours of the day, with nb_visits = 0
+ if ($table->getRowsCount() == 0) {
+ for ($hour = 0; $hour <= $currentHour; $hour++) {
+ $table->addRowFromSimpleArray(array('label' => $hour, 'nb_visits' => 0));
+ }
+ return $table;
+ }
+
+ $idsToDelete = array();
+ foreach ($table->getRows() as $id => $row) {
+ $hour = $row->getColumn('label');
+ if ($hour > $currentHour) {
+ $idsToDelete[] = $id;
+ }
+ }
+ $table->deleteRows($idsToDelete);
+ }
+ return $table;
+ }
}
function Piwik_getTimeLabel($label)
{
- return sprintf(Piwik_Translate('VisitTime_NHour'), $label);
+ return sprintf(Piwik_Translate('VisitTime_NHour'), $label);
}
/**
* Returns the day of the week for a date string, without creating a new
* Piwik_Date instance.
- *
+ *
* @param string $dateStr
* @return int The day of the week (1-7)
*/
-function Piwik_VisitTime_dayOfWeekFromDate( $dateStr )
+function Piwik_VisitTime_dayOfWeekFromDate($dateStr)
{
- return date('N', strtotime($dateStr));
+ return date('N', strtotime($dateStr));
}
/**
* Returns translated long name of a day of the week.
- *
+ *
* @param int $dayOfWeek 1-7, for Sunday-Saturday
* @return string
*/
-function Piwik_VisitTime_translateDayOfWeek( $dayOfWeek )
+function Piwik_VisitTime_translateDayOfWeek($dayOfWeek)
{
- return Piwik_Translate('General_LongDay_'.$dayOfWeek);
+ return Piwik_Translate('General_LongDay_' . $dayOfWeek);
}
diff --git a/plugins/VisitTime/Controller.php b/plugins/VisitTime/Controller.php
index d56cf58f2b..95e5d97f4d 100644
--- a/plugins/VisitTime/Controller.php
+++ b/plugins/VisitTime/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_VisitTime
*/
@@ -13,96 +13,91 @@
*
* @package Piwik_VisitTime
*/
-class Piwik_VisitTime_Controller extends Piwik_Controller
+class Piwik_VisitTime_Controller extends Piwik_Controller
{
- public function index()
- {
- $view = Piwik_View::factory('index');
- $view->dataTableVisitInformationPerLocalTime = $this->getVisitInformationPerLocalTime(true);
- $view->dataTableVisitInformationPerServerTime = $this->getVisitInformationPerServerTime(true);
- echo $view->render();
- }
-
- public function getVisitInformationPerServerTime( $fetch = false)
- {
- $view = $this->getGraph(__FUNCTION__, 'VisitTime.getVisitInformationPerServerTime',
- 'VisitTime_ColumnServerTime');
-
- $view->setCustomParameter('hideFutureHoursWhenToday', 1);
- $view->enableShowGoals();
-
- return $this->renderView($view, $fetch);
- }
-
- public function getVisitInformationPerLocalTime( $fetch = false)
- {
- $view = $this->getGraph(__FUNCTION__, 'VisitTime.getVisitInformationPerLocalTime',
- 'VisitTime_ColumnLocalTime');
-
- // add the visits by day of week as a related report, if the current period is not 'day'
- if (Piwik_Common::getRequestVar('period', 'day') != 'day')
- {
- $view->addRelatedReports(Piwik_Translate('VisitTime_LocalTime'), array(
- 'VisitTime.getByDayOfWeek' => Piwik_Translate('VisitTime_VisitsByDayOfWeek')
- ));
- }
-
- return $this->renderView($view, $fetch);
- }
-
- public function getByDayOfWeek( $fetch = false )
- {
- $view = $this->getGraph(
- __FUNCTION__, 'VisitTime.getByDayOfWeek', 'VisitTime_DayOfWeek', $limit = 7, $sort = false);
- $view->disableSort();
-
- if ($view instanceof Piwik_ViewDataTable_GenerateGraphHTML)
- {
- $view->showAllTicks();
- }
-
- // get query params
- $idsite = Piwik_Common::getRequestVar('idSite');
- $date = Piwik_Common::getRequestVar('date');
- $period = Piwik_Common::getRequestVar('period');
-
- // create a period instance
- $oSite = new Piwik_Site($idsite);
- $oPeriod = Piwik_Archive::makePeriodFromQueryParams($oSite, $period, $date);
-
- // set the footer message using the period start & end date
- $start = $oPeriod->getDateStart()->toString();
- $end = $oPeriod->getDateEnd()->toString();
- if ($start == $end)
- {
- $dateRange = $start;
- }
- else
- {
- $dateRange = $start." &ndash; ".$end;
- }
-
- $view->setFooterMessage(Piwik_Translate('General_ReportGeneratedFrom', $dateRange));
-
- return $this->renderView($view, $fetch);
- }
-
- private function getGraph( $controllerMethod, $apiMethod, $labelTranslation, $limit = 24 )
- {
- $view = Piwik_ViewDataTable::factory('graphVerticalBar');
- $view->init($this->pluginName, $controllerMethod, $apiMethod);
-
-
- $view->setColumnTranslation('label', Piwik_Translate($labelTranslation));
- $view->setSortedColumn( 'label', 'asc' );
-
- $view->setLimit($limit);
- $view->setGraphLimit($limit);
- $view->disableSearchBox();
- $view->disableExcludeLowPopulation();
- $view->disableOffsetInformationAndPaginationControls();
- $this->setMetricsVariablesView($view);
-
- return $view;
- }
+ public function index()
+ {
+ $view = Piwik_View::factory('index');
+ $view->dataTableVisitInformationPerLocalTime = $this->getVisitInformationPerLocalTime(true);
+ $view->dataTableVisitInformationPerServerTime = $this->getVisitInformationPerServerTime(true);
+ echo $view->render();
+ }
+
+ public function getVisitInformationPerServerTime($fetch = false)
+ {
+ $view = $this->getGraph(__FUNCTION__, 'VisitTime.getVisitInformationPerServerTime',
+ 'VisitTime_ColumnServerTime');
+
+ $view->setCustomParameter('hideFutureHoursWhenToday', 1);
+ $view->enableShowGoals();
+
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getVisitInformationPerLocalTime($fetch = false)
+ {
+ $view = $this->getGraph(__FUNCTION__, 'VisitTime.getVisitInformationPerLocalTime',
+ 'VisitTime_ColumnLocalTime');
+
+ // add the visits by day of week as a related report, if the current period is not 'day'
+ if (Piwik_Common::getRequestVar('period', 'day') != 'day') {
+ $view->addRelatedReports(Piwik_Translate('VisitTime_LocalTime'), array(
+ 'VisitTime.getByDayOfWeek' => Piwik_Translate('VisitTime_VisitsByDayOfWeek')
+ ));
+ }
+
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getByDayOfWeek($fetch = false)
+ {
+ $view = $this->getGraph(
+ __FUNCTION__, 'VisitTime.getByDayOfWeek', 'VisitTime_DayOfWeek', $limit = 7, $sort = false);
+ $view->disableSort();
+
+ if ($view instanceof Piwik_ViewDataTable_GenerateGraphHTML) {
+ $view->showAllTicks();
+ }
+
+ // get query params
+ $idsite = Piwik_Common::getRequestVar('idSite');
+ $date = Piwik_Common::getRequestVar('date');
+ $period = Piwik_Common::getRequestVar('period');
+
+ // create a period instance
+ $oSite = new Piwik_Site($idsite);
+ $oPeriod = Piwik_Archive::makePeriodFromQueryParams($oSite, $period, $date);
+
+ // set the footer message using the period start & end date
+ $start = $oPeriod->getDateStart()->toString();
+ $end = $oPeriod->getDateEnd()->toString();
+ if ($start == $end) {
+ $dateRange = $start;
+ } else {
+ $dateRange = $start . " &ndash; " . $end;
+ }
+
+ $view->setFooterMessage(Piwik_Translate('General_ReportGeneratedFrom', $dateRange));
+
+ return $this->renderView($view, $fetch);
+ }
+
+ private function getGraph($controllerMethod, $apiMethod, $labelTranslation, $limit = 24)
+ {
+ $view = Piwik_ViewDataTable::factory('graphVerticalBar');
+ $view->init($this->pluginName, $controllerMethod, $apiMethod);
+
+
+ $view->setColumnTranslation('label', Piwik_Translate($labelTranslation));
+ $view->setSortedColumn('label', 'asc');
+
+ $view->setLimit($limit);
+ $view->setGraphLimit($limit);
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->disableOffsetInformationAndPaginationControls();
+ $this->setMetricsVariablesView($view);
+
+ return $view;
+ }
}
diff --git a/plugins/VisitTime/VisitTime.php b/plugins/VisitTime/VisitTime.php
index bbf26457ef..5b05342bed 100644
--- a/plugins/VisitTime/VisitTime.php
+++ b/plugins/VisitTime/VisitTime.php
@@ -15,215 +15,211 @@
*/
class Piwik_VisitTime extends Piwik_Plugin
{
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('VisitTime_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- return $info;
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenu',
- 'Goals.getReportsWithGoalMetrics' => 'getReportsWithGoalMetrics',
- 'API.getReportMetadata' => 'getReportMetadata',
- 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
- );
- return $hooks;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $reports = &$notification->getNotificationObject();
- $reports[] = array(
- 'category' => Piwik_Translate('VisitsSummary_VisitsSummary'),
- 'name' => Piwik_Translate('VisitTime_WidgetLocalTime'),
- 'module' => 'VisitTime',
- 'action' => 'getVisitInformationPerLocalTime',
- 'dimension' => Piwik_Translate('VisitTime_ColumnLocalTime'),
- 'documentation' => Piwik_Translate('VisitTime_WidgetLocalTimeDocumentation', array('<b>', '</b>')),
- 'constantRowsCount' => true,
- 'order' => 20
- );
-
- $reports[] = array(
- 'category' => Piwik_Translate('VisitsSummary_VisitsSummary'),
- 'name' => Piwik_Translate('VisitTime_WidgetServerTime'),
- 'module' => 'VisitTime',
- 'action' => 'getVisitInformationPerServerTime',
- 'dimension' => Piwik_Translate('VisitTime_ColumnServerTime'),
- 'documentation' => Piwik_Translate('VisitTime_WidgetServerTimeDocumentation', array('<b>', '</b>')),
- 'constantRowsCount' => true,
- 'order' => 15,
- );
-
- $reports[] = array(
- 'category' => Piwik_Translate('VisitsSummary_VisitsSummary'),
- 'name' => Piwik_Translate('VisitTime_VisitsByDayOfWeek'),
- 'module' => 'VisitTime',
- 'action' => 'getByDayOfWeek',
- 'dimension' => Piwik_Translate('VisitTime_DayOfWeek'),
- 'documentation' => Piwik_Translate('VisitTime_WidgetByDayOfWeekDocumentation'),
- 'constantRowsCount' => true,
- 'order' => 25,
- );
- }
-
- function addWidgets()
- {
- Piwik_AddWidget( 'VisitsSummary_VisitsSummary', 'VisitTime_WidgetLocalTime', 'VisitTime', 'getVisitInformationPerLocalTime');
- Piwik_AddWidget( 'VisitsSummary_VisitsSummary', 'VisitTime_WidgetServerTime', 'VisitTime', 'getVisitInformationPerServerTime');
- Piwik_AddWidget( 'VisitsSummary_VisitsSummary', 'VisitTime_VisitsByDayOfWeek', 'VisitTime', 'getByDayOfWeek');
- }
-
- function addMenu()
- {
- Piwik_AddMenu('General_Visitors', 'VisitTime_SubmenuTimes', array('module' => 'VisitTime', 'action' => 'index'));
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getReportsWithGoalMetrics( $notification )
- {
- $dimensions =& $notification->getNotificationObject();
- $dimensions[] = array('category' => Piwik_Translate('VisitTime_ColumnServerTime'),
- 'name' => Piwik_Translate('VisitTime_ColumnServerTime'),
- 'module' => 'VisitTime',
- 'action' => 'getVisitInformationPerServerTime',
- );
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getSegmentsMetadata($notification)
- {
- $segments =& $notification->getNotificationObject();
- $acceptedValues = "0, 1, 2, 3, ..., 20, 21, 22, 23";
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => Piwik_Translate('VisitTime_ColumnServerTime'),
- 'segment' => 'visitServerHour',
- 'sqlSegment' => 'HOUR(log_visit.visit_last_action_time)',
- 'acceptedValues' => $acceptedValues
- );
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => Piwik_Translate('VisitTime_ColumnLocalTime'),
- 'segment' => 'visitLocalHour',
- 'sqlSegment' => 'HOUR(log_visit.visitor_localtime)',
- 'acceptedValues' => $acceptedValues
- );
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archivePeriod( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = array(
- 'VisitTime_localTime',
- 'VisitTime_serverTime',
- );
- $archiveProcessing->archiveDataTable($dataTableToSum);
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- public function archiveDay( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveDayAggregateVisits($archiveProcessing);
- $this->archiveDayAggregateGoals($archiveProcessing);
- $this->archiveDayRecordInDatabase($archiveProcessing);
- }
-
- protected function archiveDayAggregateVisits($archiveProcessing)
- {
- $labelSQL = "HOUR(log_visit.visitor_localtime)";
- $this->interestByLocalTime = $archiveProcessing->getArrayInterestForLabel($labelSQL);
-
- $labelSQL = "HOUR(log_visit.visit_last_action_time)";
- $this->interestByServerTime = $archiveProcessing->getArrayInterestForLabel($labelSQL);
- }
-
- protected function convertServerTimeToLocalTimezone($interestByServerTime, $archiveProcessing)
- {
- $date = Piwik_Date::factory($archiveProcessing->getStartDatetimeUTC())->toString();
- $timezone = $archiveProcessing->site->getTimezone();
- $visitsByHourTz = array();
- foreach($interestByServerTime as $hour => $stats)
- {
- $datetime = $date . ' '.$hour.':00:00';
- $hourInTz = (int)Piwik_Date::factory($datetime, $timezone)->toString('H');
- $visitsByHourTz[$hourInTz] = $stats;
- }
- return $visitsByHourTz;
- }
-
- protected function archiveDayAggregateGoals($archiveProcessing)
- {
- $query = $archiveProcessing->queryConversionsByDimension("HOUR(log_conversion.server_time)");
-
- if($query === false) return;
-
- while($row = $query->fetch())
- {
- if(!isset($this->interestByServerTime[$row['label']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByServerTime[$row['label']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- $archiveProcessing->updateGoalStats($row, $this->interestByServerTime[$row['label']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- }
- $goalByServerTime = $this->convertServerTimeToLocalTimezone($this->interestByServerTime, $archiveProcessing);
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByServerTime);
- }
-
- protected function archiveDayRecordInDatabase($archiveProcessing)
- {
- $tableLocalTime = $archiveProcessing->getDataTableFromArray($this->interestByLocalTime);
- $this->makeSureAllHoursAreSet($tableLocalTime, $archiveProcessing);
- $archiveProcessing->insertBlobRecord('VisitTime_localTime', $tableLocalTime->getSerialized());
- destroy($tableLocalTime);
-
- $this->interestByServerTime = $this->convertServerTimeToLocalTimezone($this->interestByServerTime, $archiveProcessing);
- $tableServerTime = $archiveProcessing->getDataTableFromArray($this->interestByServerTime);
- $this->makeSureAllHoursAreSet($tableServerTime, $archiveProcessing);
- $archiveProcessing->insertBlobRecord('VisitTime_serverTime', $tableServerTime->getSerialized());
- destroy($tableServerTime);
- }
-
- private function makeSureAllHoursAreSet($table, $archiveProcessing)
- {
- for($i=0; $i<=23; $i++)
- {
- if($table->getRowFromLabel($i) === false)
- {
- $row = $archiveProcessing->getNewInterestRowLabeled($i);
- $table->addRow( $row );
- }
- }
- }
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('VisitTime_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenu',
+ 'Goals.getReportsWithGoalMetrics' => 'getReportsWithGoalMetrics',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ );
+ return $hooks;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $reports = & $notification->getNotificationObject();
+ $reports[] = array(
+ 'category' => Piwik_Translate('VisitsSummary_VisitsSummary'),
+ 'name' => Piwik_Translate('VisitTime_WidgetLocalTime'),
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerLocalTime',
+ 'dimension' => Piwik_Translate('VisitTime_ColumnLocalTime'),
+ 'documentation' => Piwik_Translate('VisitTime_WidgetLocalTimeDocumentation', array('<b>', '</b>')),
+ 'constantRowsCount' => true,
+ 'order' => 20
+ );
+
+ $reports[] = array(
+ 'category' => Piwik_Translate('VisitsSummary_VisitsSummary'),
+ 'name' => Piwik_Translate('VisitTime_WidgetServerTime'),
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerServerTime',
+ 'dimension' => Piwik_Translate('VisitTime_ColumnServerTime'),
+ 'documentation' => Piwik_Translate('VisitTime_WidgetServerTimeDocumentation', array('<b>', '</b>')),
+ 'constantRowsCount' => true,
+ 'order' => 15,
+ );
+
+ $reports[] = array(
+ 'category' => Piwik_Translate('VisitsSummary_VisitsSummary'),
+ 'name' => Piwik_Translate('VisitTime_VisitsByDayOfWeek'),
+ 'module' => 'VisitTime',
+ 'action' => 'getByDayOfWeek',
+ 'dimension' => Piwik_Translate('VisitTime_DayOfWeek'),
+ 'documentation' => Piwik_Translate('VisitTime_WidgetByDayOfWeekDocumentation'),
+ 'constantRowsCount' => true,
+ 'order' => 25,
+ );
+ }
+
+ function addWidgets()
+ {
+ Piwik_AddWidget('VisitsSummary_VisitsSummary', 'VisitTime_WidgetLocalTime', 'VisitTime', 'getVisitInformationPerLocalTime');
+ Piwik_AddWidget('VisitsSummary_VisitsSummary', 'VisitTime_WidgetServerTime', 'VisitTime', 'getVisitInformationPerServerTime');
+ Piwik_AddWidget('VisitsSummary_VisitsSummary', 'VisitTime_VisitsByDayOfWeek', 'VisitTime', 'getByDayOfWeek');
+ }
+
+ function addMenu()
+ {
+ Piwik_AddMenu('General_Visitors', 'VisitTime_SubmenuTimes', array('module' => 'VisitTime', 'action' => 'index'));
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getReportsWithGoalMetrics($notification)
+ {
+ $dimensions =& $notification->getNotificationObject();
+ $dimensions[] = array('category' => Piwik_Translate('VisitTime_ColumnServerTime'),
+ 'name' => Piwik_Translate('VisitTime_ColumnServerTime'),
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerServerTime',
+ );
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ $segments =& $notification->getNotificationObject();
+ $acceptedValues = "0, 1, 2, 3, ..., 20, 21, 22, 23";
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit',
+ 'name' => Piwik_Translate('VisitTime_ColumnServerTime'),
+ 'segment' => 'visitServerHour',
+ 'sqlSegment' => 'HOUR(log_visit.visit_last_action_time)',
+ 'acceptedValues' => $acceptedValues
+ );
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit',
+ 'name' => Piwik_Translate('VisitTime_ColumnLocalTime'),
+ 'segment' => 'visitLocalHour',
+ 'sqlSegment' => 'HOUR(log_visit.visitor_localtime)',
+ 'acceptedValues' => $acceptedValues
+ );
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ function archivePeriod($notification)
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $dataTableToSum = array(
+ 'VisitTime_localTime',
+ 'VisitTime_serverTime',
+ );
+ $archiveProcessing->archiveDataTable($dataTableToSum);
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ public function archiveDay($notification)
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $this->archiveDayAggregateVisits($archiveProcessing);
+ $this->archiveDayAggregateGoals($archiveProcessing);
+ $this->archiveDayRecordInDatabase($archiveProcessing);
+ }
+
+ protected function archiveDayAggregateVisits($archiveProcessing)
+ {
+ $labelSQL = "HOUR(log_visit.visitor_localtime)";
+ $this->interestByLocalTime = $archiveProcessing->getArrayInterestForLabel($labelSQL);
+
+ $labelSQL = "HOUR(log_visit.visit_last_action_time)";
+ $this->interestByServerTime = $archiveProcessing->getArrayInterestForLabel($labelSQL);
+ }
+
+ protected function convertServerTimeToLocalTimezone($interestByServerTime, $archiveProcessing)
+ {
+ $date = Piwik_Date::factory($archiveProcessing->getStartDatetimeUTC())->toString();
+ $timezone = $archiveProcessing->site->getTimezone();
+ $visitsByHourTz = array();
+ foreach ($interestByServerTime as $hour => $stats) {
+ $datetime = $date . ' ' . $hour . ':00:00';
+ $hourInTz = (int)Piwik_Date::factory($datetime, $timezone)->toString('H');
+ $visitsByHourTz[$hourInTz] = $stats;
+ }
+ return $visitsByHourTz;
+ }
+
+ protected function archiveDayAggregateGoals($archiveProcessing)
+ {
+ $query = $archiveProcessing->queryConversionsByDimension("HOUR(log_conversion.server_time)");
+
+ if ($query === false) return;
+
+ while ($row = $query->fetch()) {
+ if (!isset($this->interestByServerTime[$row['label']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByServerTime[$row['label']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+ $archiveProcessing->updateGoalStats($row, $this->interestByServerTime[$row['label']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
+ }
+ $goalByServerTime = $this->convertServerTimeToLocalTimezone($this->interestByServerTime, $archiveProcessing);
+ $archiveProcessing->enrichConversionsByLabelArray($this->interestByServerTime);
+ }
+
+ protected function archiveDayRecordInDatabase($archiveProcessing)
+ {
+ $tableLocalTime = $archiveProcessing->getDataTableFromArray($this->interestByLocalTime);
+ $this->makeSureAllHoursAreSet($tableLocalTime, $archiveProcessing);
+ $archiveProcessing->insertBlobRecord('VisitTime_localTime', $tableLocalTime->getSerialized());
+ destroy($tableLocalTime);
+
+ $this->interestByServerTime = $this->convertServerTimeToLocalTimezone($this->interestByServerTime, $archiveProcessing);
+ $tableServerTime = $archiveProcessing->getDataTableFromArray($this->interestByServerTime);
+ $this->makeSureAllHoursAreSet($tableServerTime, $archiveProcessing);
+ $archiveProcessing->insertBlobRecord('VisitTime_serverTime', $tableServerTime->getSerialized());
+ destroy($tableServerTime);
+ }
+
+ private function makeSureAllHoursAreSet($table, $archiveProcessing)
+ {
+ for ($i = 0; $i <= 23; $i++) {
+ if ($table->getRowFromLabel($i) === false) {
+ $row = $archiveProcessing->getNewInterestRowLabeled($i);
+ $table->addRow($row);
+ }
+ }
+ }
}
diff --git a/plugins/VisitTime/templates/index.tpl b/plugins/VisitTime/templates/index.tpl
index 82af946c7a..3d742d6293 100644
--- a/plugins/VisitTime/templates/index.tpl
+++ b/plugins/VisitTime/templates/index.tpl
@@ -1,9 +1,9 @@
<div id='leftcolumn'>
-<h2>{'VisitTime_LocalTime'|translate}</h2>
-{$dataTableVisitInformationPerLocalTime}
+ <h2>{'VisitTime_LocalTime'|translate}</h2>
+ {$dataTableVisitInformationPerLocalTime}
</div>
<div id='rightcolumn'>
-<h2>{'VisitTime_ServerTime'|translate}</h2>
-{$dataTableVisitInformationPerServerTime}
+ <h2>{'VisitTime_ServerTime'|translate}</h2>
+ {$dataTableVisitInformationPerServerTime}
</div>
diff --git a/plugins/VisitorGenerator/Controller.php b/plugins/VisitorGenerator/Controller.php
index ec75c39ace..54ebc3aea0 100644
--- a/plugins/VisitorGenerator/Controller.php
+++ b/plugins/VisitorGenerator/Controller.php
@@ -15,144 +15,142 @@
*/
class Piwik_VisitorGenerator_Controller extends Piwik_Controller_Admin
{
- public function index()
- {
- Piwik::checkUserIsSuperUser();
-
- $sitesList = Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess();
-
- $view = Piwik_View::factory('index');
- $this->setBasicVariablesView($view);
- $view->assign('sitesList', $sitesList);
- $view->nonce = Piwik_Nonce::getNonce('Piwik_VisitorGenerator.generate');
- $view->countActionsPerRun = count($this->getAccessLog());
- $view->accessLogPath = $this->getAccessLogPath();
- $view->menu = Piwik_GetAdminMenu();
- echo $view->render();
- }
- private function getAccessLogPath()
- {
- return PIWIK_INCLUDE_PATH . "/plugins/VisitorGenerator/data/access.log";
- }
- private function getAccessLog()
- {
- $log = file($this->getAccessLogPath());
- return $log;
- }
-
- public function generate()
- {
- Piwik::checkUserIsSuperUser();
- $nonce = Piwik_Common::getRequestVar('form_nonce', '', 'string', $_POST);
- if(Piwik_Common::getRequestVar('choice', 'no') != 'yes' ||
- !Piwik_Nonce::verifyNonce('Piwik_VisitorGenerator.generate', $nonce))
- {
- Piwik::redirectToModule('VisitorGenerator', 'index');
- }
- Piwik_Nonce::discardNonce('Piwik_VisitorGenerator.generate');
-
- $daysToCompute = Piwik_Common::getRequestVar('daysToCompute', 1, 'int');
-
- // get idSite from POST with fallback to GET
- $idSite = Piwik_Common::getRequestVar('idSite', false, 'int', $_GET);
- $idSite = Piwik_Common::getRequestVar('idSite', $idSite, 'int', $_POST);
-
- Piwik::setMaxExecutionTime(0);
-
- $timer = new Piwik_Timer;
- $time = time() - ($daysToCompute-1)*86400;
-
- $nbActionsTotal = 0;
- $dates = array();
- while($time <= time())
- {
- $nbActionsTotalThisDay = $this->generateVisits($time, $idSite);
- $dates[] = date("Y-m-d", $time);
- $time += 86400;
- $nbActionsTotal += $nbActionsTotalThisDay;
- }
-
- $api = Piwik_CoreAdminHome_API::getInstance();
- $api->invalidateArchivedReports($idSite, implode($dates, ","));
-
- $browserArchiving = Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled();
-
- // Init view
- $view = Piwik_View::factory('generate');
- $this->setBasicVariablesView($view);
- $view->menu = Piwik_GetAdminMenu();
- $view->assign('browserArchivingEnabled', $browserArchiving);
- $view->assign('timer', $timer);
- $view->assign('days', $daysToCompute);
- $view->assign('nbActionsTotal', $nbActionsTotal);
- $view->assign('nbRequestsPerSec', round($nbActionsTotal / $timer->getTime(),0));
- $view->assign('siteName', Piwik_Site::getNameFor($idSite));
- echo $view->render();
- }
-
- private function generateVisits($time = false, $idSite = 1)
- {
- $logs = $this->getAccessLog();
- if(empty($time)) $time = time();
- $date = date("Y-m-d", $time);
-
- $acceptLanguages = array(
- "el,fi;q=0.5",
- "de-de,de;q=0.8,en-us",
- "pl,en-us;q=0.7,en;q=",
- "zh-cn",
- "fr-ca",
- "en-us",
- "en-gb",
- "fr-be",
- "fr,de-ch;q=0.5",
- "fr",
- "fr-ch",
- "fr",
- );
- $prefix = Piwik_Url::getCurrentUrlWithoutFileName() . "piwik.php";
- $count = 0;
- foreach($logs as $log)
- {
- if (!preg_match('/^(\S+) \S+ \S+ \[(.*?)\] "GET (\S+.*?)" \d+ \d+ "(.*?)" "(.*?)"/', $log, $m)) {
- continue;
- }
- $ip = $m[1];
- $time = $m[2];
- $url = $m[3];
- $referrer = $m[4];
- $ua = $m[5];
-
- $start = strpos($url, 'piwik.php?') + strlen('piwik.php?');
- $url = substr($url, $start, strrpos($url, " ")-$start);
- $datetime = $date . " " . Piwik_Date::factory($time)->toString("H:i:s");
- $ip = strlen($ip) < 10 ? "13.5.111.3" : $ip;
-
- // Force date/ip & authenticate
- $url .= "&cdt=" . urlencode($datetime);
- if(strpos($url, 'cip') === false)
- {
- $url .= "&cip=" . $ip;
- }
- $url .= "&token_auth=" . Piwik::getCurrentUserTokenAuth();
- $url = $prefix . "?" . $url;
-
- // Make order IDs unique per day
- $url = str_replace("ec_id=", "ec_id=$date-", $url);
-
- // Disable provider plugin
- $url .= "&dp=1";
-
- // Replace idsite
- $url = preg_replace("/idsite=[0-9]+/", "idsite=$idSite", $url);
-
- $acceptLanguage = $acceptLanguages[$count % count($acceptLanguages)];
-
- if($output = Piwik_Http::sendHttpRequest($url, $timeout = 5, $ua, $path = null, $follow = 0, $acceptLanguage))
- {
- $count++;
- }
- }
- return $count;
- }
+ public function index()
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $sitesList = Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess();
+
+ $view = Piwik_View::factory('index');
+ $this->setBasicVariablesView($view);
+ $view->assign('sitesList', $sitesList);
+ $view->nonce = Piwik_Nonce::getNonce('Piwik_VisitorGenerator.generate');
+ $view->countActionsPerRun = count($this->getAccessLog());
+ $view->accessLogPath = $this->getAccessLogPath();
+ $view->menu = Piwik_GetAdminMenu();
+ echo $view->render();
+ }
+
+ private function getAccessLogPath()
+ {
+ return PIWIK_INCLUDE_PATH . "/plugins/VisitorGenerator/data/access.log";
+ }
+
+ private function getAccessLog()
+ {
+ $log = file($this->getAccessLogPath());
+ return $log;
+ }
+
+ public function generate()
+ {
+ Piwik::checkUserIsSuperUser();
+ $nonce = Piwik_Common::getRequestVar('form_nonce', '', 'string', $_POST);
+ if (Piwik_Common::getRequestVar('choice', 'no') != 'yes' ||
+ !Piwik_Nonce::verifyNonce('Piwik_VisitorGenerator.generate', $nonce)
+ ) {
+ Piwik::redirectToModule('VisitorGenerator', 'index');
+ }
+ Piwik_Nonce::discardNonce('Piwik_VisitorGenerator.generate');
+
+ $daysToCompute = Piwik_Common::getRequestVar('daysToCompute', 1, 'int');
+
+ // get idSite from POST with fallback to GET
+ $idSite = Piwik_Common::getRequestVar('idSite', false, 'int', $_GET);
+ $idSite = Piwik_Common::getRequestVar('idSite', $idSite, 'int', $_POST);
+
+ Piwik::setMaxExecutionTime(0);
+
+ $timer = new Piwik_Timer;
+ $time = time() - ($daysToCompute - 1) * 86400;
+
+ $nbActionsTotal = 0;
+ $dates = array();
+ while ($time <= time()) {
+ $nbActionsTotalThisDay = $this->generateVisits($time, $idSite);
+ $dates[] = date("Y-m-d", $time);
+ $time += 86400;
+ $nbActionsTotal += $nbActionsTotalThisDay;
+ }
+
+ $api = Piwik_CoreAdminHome_API::getInstance();
+ $api->invalidateArchivedReports($idSite, implode($dates, ","));
+
+ $browserArchiving = Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled();
+
+ // Init view
+ $view = Piwik_View::factory('generate');
+ $this->setBasicVariablesView($view);
+ $view->menu = Piwik_GetAdminMenu();
+ $view->assign('browserArchivingEnabled', $browserArchiving);
+ $view->assign('timer', $timer);
+ $view->assign('days', $daysToCompute);
+ $view->assign('nbActionsTotal', $nbActionsTotal);
+ $view->assign('nbRequestsPerSec', round($nbActionsTotal / $timer->getTime(), 0));
+ $view->assign('siteName', Piwik_Site::getNameFor($idSite));
+ echo $view->render();
+ }
+
+ private function generateVisits($time = false, $idSite = 1)
+ {
+ $logs = $this->getAccessLog();
+ if (empty($time)) $time = time();
+ $date = date("Y-m-d", $time);
+
+ $acceptLanguages = array(
+ "el,fi;q=0.5",
+ "de-de,de;q=0.8,en-us",
+ "pl,en-us;q=0.7,en;q=",
+ "zh-cn",
+ "fr-ca",
+ "en-us",
+ "en-gb",
+ "fr-be",
+ "fr,de-ch;q=0.5",
+ "fr",
+ "fr-ch",
+ "fr",
+ );
+ $prefix = Piwik_Url::getCurrentUrlWithoutFileName() . "piwik.php";
+ $count = 0;
+ foreach ($logs as $log) {
+ if (!preg_match('/^(\S+) \S+ \S+ \[(.*?)\] "GET (\S+.*?)" \d+ \d+ "(.*?)" "(.*?)"/', $log, $m)) {
+ continue;
+ }
+ $ip = $m[1];
+ $time = $m[2];
+ $url = $m[3];
+ $referrer = $m[4];
+ $ua = $m[5];
+
+ $start = strpos($url, 'piwik.php?') + strlen('piwik.php?');
+ $url = substr($url, $start, strrpos($url, " ") - $start);
+ $datetime = $date . " " . Piwik_Date::factory($time)->toString("H:i:s");
+ $ip = strlen($ip) < 10 ? "13.5.111.3" : $ip;
+
+ // Force date/ip & authenticate
+ $url .= "&cdt=" . urlencode($datetime);
+ if (strpos($url, 'cip') === false) {
+ $url .= "&cip=" . $ip;
+ }
+ $url .= "&token_auth=" . Piwik::getCurrentUserTokenAuth();
+ $url = $prefix . "?" . $url;
+
+ // Make order IDs unique per day
+ $url = str_replace("ec_id=", "ec_id=$date-", $url);
+
+ // Disable provider plugin
+ $url .= "&dp=1";
+
+ // Replace idsite
+ $url = preg_replace("/idsite=[0-9]+/", "idsite=$idSite", $url);
+
+ $acceptLanguage = $acceptLanguages[$count % count($acceptLanguages)];
+
+ if ($output = Piwik_Http::sendHttpRequest($url, $timeout = 5, $ua, $path = null, $follow = 0, $acceptLanguage)) {
+ $count++;
+ }
+ }
+ return $count;
+ }
}
diff --git a/plugins/VisitorGenerator/VisitorGenerator.php b/plugins/VisitorGenerator/VisitorGenerator.php
index 6249ab4df5..ebeb94e976 100644
--- a/plugins/VisitorGenerator/VisitorGenerator.php
+++ b/plugins/VisitorGenerator/VisitorGenerator.php
@@ -10,32 +10,36 @@
*/
/**
- *
+ *
* @package Piwik_VisitorGenerator
*/
-class Piwik_VisitorGenerator extends Piwik_Plugin {
+class Piwik_VisitorGenerator extends Piwik_Plugin
+{
- public function getInformation() {
- return array(
- 'description' => Piwik_Translate('VisitorGenerator_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('VisitorGenerator_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
- public function getListHooksRegistered() {
- return array(
- 'AdminMenu.add' => 'addMenu',
- );
- }
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'AdminMenu.add' => 'addMenu',
+ );
+ }
- public function addMenu() {
- Piwik_AddAdminSubMenu(
- 'CoreAdminHome_MenuDiagnostic', 'VisitorGenerator_VisitorGenerator',
- array('module' => 'VisitorGenerator', 'action' => 'index'),
- Piwik::isUserIsSuperUser(),
- $order = 20
- );
- }
+ public function addMenu()
+ {
+ Piwik_AddAdminSubMenu(
+ 'CoreAdminHome_MenuDiagnostic', 'VisitorGenerator_VisitorGenerator',
+ array('module' => 'VisitorGenerator', 'action' => 'index'),
+ Piwik::isUserIsSuperUser(),
+ $order = 20
+ );
+ }
}
diff --git a/plugins/VisitorGenerator/templates/generate.tpl b/plugins/VisitorGenerator/templates/generate.tpl
index 12d13fdac3..370d40b794 100644
--- a/plugins/VisitorGenerator/templates/generate.tpl
+++ b/plugins/VisitorGenerator/templates/generate.tpl
@@ -2,16 +2,17 @@
<h2>{'VisitorGenerator_VisitorGenerator'|translate}</h2>
-Generated visits for {$siteName} and for {'General_LastDays'|translate:$days}.<br />
-Generated {'General_NbActions'|translate}: {$nbActionsTotal}<br />
-{'VisitorGenerator_NbRequestsPerSec'|translate}: {$nbRequestsPerSec}<br />
+Generated visits for {$siteName} and for {'General_LastDays'|translate:$days}.<br/>
+Generated {'General_NbActions'|translate}: {$nbActionsTotal}<br/>
+{'VisitorGenerator_NbRequestsPerSec'|translate}: {$nbRequestsPerSec}<br/>
{$timer}</p>
<p><strong>
-{if $browserArchivingEnabled}
-The reports will be reprocessed the next time you visit the Piwik reports, it might take a few minutes.
-{else}
-Please re-run the archive.php Piwik script in the crontab to refresh the reports. <a href="http://piwik.org/docs/setup-auto-archiving/">See "How to Set up Auto-Archiving of Your Reports"</a>
-{/if}
-</strong>
+ {if $browserArchivingEnabled}
+ The reports will be reprocessed the next time you visit the Piwik reports, it might take a few minutes.
+ {else}
+ Please re-run the archive.php Piwik script in the crontab to refresh the reports.
+ <a href="http://piwik.org/docs/setup-auto-archiving/">See "How to Set up Auto-Archiving of Your Reports"</a>
+ {/if}
+ </strong>
</p>
{include file="CoreAdminHome/templates/footer.tpl"}
diff --git a/plugins/VisitorGenerator/templates/index.tpl b/plugins/VisitorGenerator/templates/index.tpl
index 944b82dee2..f3477285ac 100644
--- a/plugins/VisitorGenerator/templates/index.tpl
+++ b/plugins/VisitorGenerator/templates/index.tpl
@@ -2,36 +2,38 @@
<h2>{'VisitorGenerator_VisitorGenerator'|translate}</h2>
<p>{'VisitorGenerator_PluginDescription'|translate}</p>
-<p>You can overwrite the log file that is used to generate fake visits (change {$accessLogPath}). This is a log file of requests to "piwik.php" in the format "Apache combined log".</p>
+<p>You can overwrite the log file that is used to generate fake visits (change {$accessLogPath}). This is a log file of requests to "piwik.php" in the format
+ "Apache combined log".</p>
<form method="POST" action="{url module=VisitorGenerator action=generate}">
-<table class="adminTable" style="width: 600px;">
-<tr>
- <td><label for="idSite">{'General_ChooseWebsite'|translate}</label></td>
- <td>
- {include file="CoreHome/templates/sites_selection.tpl"
- showAllSitesItem=false showSelectedSite=true switchSiteOnSelect=false inputName=idSite}
- </td>
-</tr>
-<tr>
- <td><label for="daysToCompute">{'VisitorGenerator_DaysToCompute'|translate}</label></td>
- <td><input type="text" value="1" name="daysToCompute" /></td>
-</tr>
-</table>
-{'VisitorGenerator_Warning'|translate}<br />
-{'VisitorGenerator_NotReversible'|translate:'<b>':'</b>'}<br /><br />
-<p><strong>This will generate approximately {$countActionsPerRun} fake actions on this site for each day</strong>.<br/>
-</p>
-{'VisitorGenerator_AreYouSure'|translate}<br /><br/>
-<input type="checkbox" name="choice" id="choice" value="yes" /> <label for="choice">{'VisitorGenerator_ChoiceYes'|translate}</label>
-<br />
-<input type="hidden" value="{$token_auth}" name="token_auth" />
-<input type="hidden" value="{$nonce}" name="form_nonce" />
-<br/>
+ <table class="adminTable" style="width: 600px;">
+ <tr>
+ <td><label for="idSite">{'General_ChooseWebsite'|translate}</label></td>
+ <td>
+ {include file="CoreHome/templates/sites_selection.tpl"
+ showAllSitesItem=false showSelectedSite=true switchSiteOnSelect=false inputName=idSite}
+ </td>
+ </tr>
+ <tr>
+ <td><label for="daysToCompute">{'VisitorGenerator_DaysToCompute'|translate}</label></td>
+ <td><input type="text" value="1" name="daysToCompute"/></td>
+ </tr>
+ </table>
+ {'VisitorGenerator_Warning'|translate}<br/>
+ {'VisitorGenerator_NotReversible'|translate:'<b>':'</b>'}<br/><br/>
- NOTE: It might take a few minutes to generate visits and actions, please be patient...<br/>
- There is also a faster tool that will import large test data in Piwik, see the <a href='https://github.com/piwik/piwik/tree/master/tests#testing-data'>README</a>.</p>
- <br/>
-<input type="submit" value="{'VisitorGenerator_Submit'|translate}" name="submit" class="submit" />
+ <p><strong>This will generate approximately {$countActionsPerRun} fake actions on this site for each day</strong>.<br/>
+ </p>
+ {'VisitorGenerator_AreYouSure'|translate}<br/><br/>
+ <input type="checkbox" name="choice" id="choice" value="yes"/> <label for="choice">{'VisitorGenerator_ChoiceYes'|translate}</label>
+ <br/>
+ <input type="hidden" value="{$token_auth}" name="token_auth"/>
+ <input type="hidden" value="{$nonce}" name="form_nonce"/>
+ <br/>
+
+ NOTE: It might take a few minutes to generate visits and actions, please be patient...<br/>
+ There is also a faster tool that will import large test data in Piwik, see the <a href='https://github.com/piwik/piwik/tree/master/tests#testing-data'>README</a>.</p>
+ <br/>
+ <input type="submit" value="{'VisitorGenerator_Submit'|translate}" name="submit" class="submit"/>
</form>
{include file="CoreAdminHome/templates/footer.tpl"}
diff --git a/plugins/VisitorInterest/API.php b/plugins/VisitorInterest/API.php
index 72b43207f5..7adfd7ca4e 100644
--- a/plugins/VisitorInterest/API.php
+++ b/plugins/VisitorInterest/API.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_VisitorInterest
*/
@@ -12,118 +12,114 @@
/**
* VisitorInterest API lets you access two Visitor Engagement reports: number of visits per number of pages,
* and number of visits per visit duration.
- *
+ *
* @package Piwik_VisitorInterest
*/
-class Piwik_VisitorInterest_API
+class Piwik_VisitorInterest_API
{
- static private $instance = null;
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
+ static private $instance = null;
- protected function getDataTable($name, $idSite, $period, $date, $segment, $column = Piwik_Archive::INDEX_NB_VISITS)
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- $dataTable = $archive->getDataTable($name);
- $dataTable->queueFilter('ReplaceColumnNames');
- return $dataTable;
- }
-
- public function getNumberOfVisitsPerVisitDuration( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getDataTable('VisitorInterest_timeGap', $idSite, $period, $date, $segment);
- $dataTable->queueFilter('Sort', array('label', 'asc', true));
- $dataTable->queueFilter('BeautifyTimeRangeLabels', array(
- Piwik_Translate('VisitorInterest_BetweenXYSeconds'),
- Piwik_Translate('VisitorInterest_OneMinute'),
- Piwik_Translate('VisitorInterest_PlusXMin')));
- return $dataTable;
- }
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
- public function getNumberOfVisitsPerPage( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getDataTable('VisitorInterest_pageGap', $idSite, $period, $date, $segment);
- $dataTable->queueFilter('Sort', array('label', 'asc', true));
- $dataTable->queueFilter('BeautifyRangeLabels', array(
- Piwik_Translate('VisitorInterest_OnePage'),
- Piwik_Translate('VisitorInterest_NPages')));
- return $dataTable;
- }
-
- /**
- * Returns a DataTable that associates counts of days (N) with the count of visits that
- * occurred within N days of the last visit.
- *
- * @param int $idSite The site to select data from.
- * @param string $period The period type.
- * @param string $date The date type.
- * @param string|bool $segment The segment.
- * @return Piwik_DataTable the archived report data.
- */
- public function getNumberOfVisitsByDaysSinceLast( $idSite, $period, $date, $segment = false )
- {
- $recordName = 'VisitorInterest_daysSinceLastVisit';
- $dataTable = $this->getDataTable(
- $recordName, $idSite, $period, $date, $segment, Piwik_Archive::INDEX_NB_VISITS);
+ protected function getDataTable($name, $idSite, $period, $date, $segment, $column = Piwik_Archive::INDEX_NB_VISITS)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ $dataTable = $archive->getDataTable($name);
+ $dataTable->queueFilter('ReplaceColumnNames');
+ return $dataTable;
+ }
- $dataTable->queueFilter('BeautifyRangeLabels', array(
- Piwik_Translate('General_OneDay'), Piwik_Translate('General_NDays')));
-
- return $dataTable;
- }
-
- /**
- * Returns a DataTable that associates ranges of visit numbers with the count of visits
- * whose visit number falls within those ranges.
- *
- * @param int $idSite The site to select data from.
- * @param string $period The period type.
- * @param string $date The date type.
- * @param string|bool $segment The segment.
- * @return Piwik_DataTable the archived report data.
- */
- public function getNumberOfVisitsByVisitCount( $idSite, $period, $date, $segment = false )
- {
- $dataTable = $this->getDataTable(
- 'VisitorInterest_visitsByVisitCount', $idSite, $period, $date, $segment, Piwik_Archive::INDEX_NB_VISITS);
+ public function getNumberOfVisitsPerVisitDuration($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('VisitorInterest_timeGap', $idSite, $period, $date, $segment);
+ $dataTable->queueFilter('Sort', array('label', 'asc', true));
+ $dataTable->queueFilter('BeautifyTimeRangeLabels', array(
+ Piwik_Translate('VisitorInterest_BetweenXYSeconds'),
+ Piwik_Translate('VisitorInterest_OneMinute'),
+ Piwik_Translate('VisitorInterest_PlusXMin')));
+ return $dataTable;
+ }
- $dataTable->queueFilter('BeautifyRangeLabels', array(
- Piwik_Translate('General_OneVisit'), Piwik_Translate('General_NVisits')));
+ public function getNumberOfVisitsPerPage($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('VisitorInterest_pageGap', $idSite, $period, $date, $segment);
+ $dataTable->queueFilter('Sort', array('label', 'asc', true));
+ $dataTable->queueFilter('BeautifyRangeLabels', array(
+ Piwik_Translate('VisitorInterest_OnePage'),
+ Piwik_Translate('VisitorInterest_NPages')));
+ return $dataTable;
+ }
- // add visit percent column
- self::addVisitsPercentColumn($dataTable);
+ /**
+ * Returns a DataTable that associates counts of days (N) with the count of visits that
+ * occurred within N days of the last visit.
+ *
+ * @param int $idSite The site to select data from.
+ * @param string $period The period type.
+ * @param string $date The date type.
+ * @param string|bool $segment The segment.
+ * @return Piwik_DataTable the archived report data.
+ */
+ public function getNumberOfVisitsByDaysSinceLast($idSite, $period, $date, $segment = false)
+ {
+ $recordName = 'VisitorInterest_daysSinceLastVisit';
+ $dataTable = $this->getDataTable(
+ $recordName, $idSite, $period, $date, $segment, Piwik_Archive::INDEX_NB_VISITS);
- return $dataTable;
- }
+ $dataTable->queueFilter('BeautifyRangeLabels', array(
+ Piwik_Translate('General_OneDay'), Piwik_Translate('General_NDays')));
- /**
- * Utility function that adds a visit percent column to a data table,
- * regardless of whether the data table is an data table array or just
- * a data table.
- *
- * @param Piwik_DataTable $dataTable The data table to modify.
- */
- private static function addVisitsPercentColumn( $dataTable )
- {
- if ($dataTable instanceof Piwik_DataTable_Array)
- {
- foreach($dataTable->getArray() as $table)
- {
- self::addVisitsPercentColumn($table);
- }
- }
- else
- {
- $totalVisits = array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS));
- $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array(
- 'nb_visits_percentage', 'nb_visits', $totalVisits));
- }
- }
+ return $dataTable;
+ }
+
+ /**
+ * Returns a DataTable that associates ranges of visit numbers with the count of visits
+ * whose visit number falls within those ranges.
+ *
+ * @param int $idSite The site to select data from.
+ * @param string $period The period type.
+ * @param string $date The date type.
+ * @param string|bool $segment The segment.
+ * @return Piwik_DataTable the archived report data.
+ */
+ public function getNumberOfVisitsByVisitCount($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable(
+ 'VisitorInterest_visitsByVisitCount', $idSite, $period, $date, $segment, Piwik_Archive::INDEX_NB_VISITS);
+
+ $dataTable->queueFilter('BeautifyRangeLabels', array(
+ Piwik_Translate('General_OneVisit'), Piwik_Translate('General_NVisits')));
+
+ // add visit percent column
+ self::addVisitsPercentColumn($dataTable);
+
+ return $dataTable;
+ }
+
+ /**
+ * Utility function that adds a visit percent column to a data table,
+ * regardless of whether the data table is an data table array or just
+ * a data table.
+ *
+ * @param Piwik_DataTable $dataTable The data table to modify.
+ */
+ private static function addVisitsPercentColumn($dataTable)
+ {
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ foreach ($dataTable->getArray() as $table) {
+ self::addVisitsPercentColumn($table);
+ }
+ } else {
+ $totalVisits = array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS));
+ $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array(
+ 'nb_visits_percentage', 'nb_visits', $totalVisits));
+ }
+ }
}
diff --git a/plugins/VisitorInterest/Controller.php b/plugins/VisitorInterest/Controller.php
index 6acef00f43..ef50ad5cac 100644
--- a/plugins/VisitorInterest/Controller.php
+++ b/plugins/VisitorInterest/Controller.php
@@ -1,10 +1,10 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_VisitorInterest
*/
@@ -12,101 +12,101 @@
/**
* @package Piwik_VisitorInterest
*/
-class Piwik_VisitorInterest_Controller extends Piwik_Controller
+class Piwik_VisitorInterest_Controller extends Piwik_Controller
{
- function index()
- {
- $view = Piwik_View::factory('index');
- $view->dataTableNumberOfVisitsPerVisitDuration = $this->getNumberOfVisitsPerVisitDuration(true);
- $view->dataTableNumberOfVisitsPerPage = $this->getNumberOfVisitsPerPage(true);
- $view->dataTableNumberOfVisitsByVisitNum = $this->getNumberOfVisitsByVisitCount(true);
- $view->dataTableNumberOfVisitsByDaysSinceLast = $this->getNumberOfVisitsByDaysSinceLast(true);
- echo $view->render();
- }
-
- function getNumberOfVisitsPerVisitDuration( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory( 'cloud' );
- $view->init( $this->pluginName, __FUNCTION__, "VisitorInterest.getNumberOfVisitsPerVisitDuration" );
-
- $view->setColumnsToDisplay( array('label','nb_visits') );
- $view->setSortedColumn( 'label', 'asc' );
- $view->setColumnTranslation('label', Piwik_Translate('VisitorInterest_ColumnVisitDuration'));
- $view->setGraphLimit(10);
- $view->disableSort();
- $view->disableExcludeLowPopulation();
- $view->disableOffsetInformationAndPaginationControls();
- $view->disableSearchBox();
- $view->disableShowAllColumns();
-
- return $this->renderView($view, $fetch);
- }
-
- function getNumberOfVisitsPerPage( $fetch = false)
- {
- $view = Piwik_ViewDataTable::factory( 'cloud' );
- $view->init( $this->pluginName, __FUNCTION__, "VisitorInterest.getNumberOfVisitsPerPage" );
- $view->setColumnsToDisplay( array('label','nb_visits') );
- $view->setSortedColumn( 'label', 'asc' );
- $view->setColumnTranslation('label', Piwik_Translate('VisitorInterest_ColumnPagesPerVisit'));
- $view->setGraphLimit(10);
- $view->disableExcludeLowPopulation();
- $view->disableOffsetInformationAndPaginationControls();
- $view->disableSearchBox();
- $view->disableSort();
- $view->disableShowAllColumns();
-
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Returns a report that lists the count of visits for different ranges of
- * a visitor's visit number.
- *
- * @param bool $fetch Whether to return the rendered view as a string or echo it.
- * @return string The rendered report or nothing if $fetch is set to false.
- */
- public function getNumberOfVisitsByVisitCount( $fetch = false )
- {
- $view = Piwik_ViewDataTable::factory( );
- $view->init( $this->pluginName, __FUNCTION__, "VisitorInterest.getNumberOfVisitsByVisitCount" );
- $view->setColumnsToDisplay( array('label', 'nb_visits', 'nb_visits_percentage') );
- $view->setSortedColumn('label', 'asc');
- $view->setColumnTranslation('label', Piwik_Translate('VisitorInterest_VisitNum'));
- $view->setColumnTranslation('nb_visits_percentage', str_replace(' ', '&nbsp;', Piwik_Translate('General_ColumnPercentageVisits')));
- $view->disableExcludeLowPopulation();
- $view->disableOffsetInformationAndPaginationControls();
- $view->disableShowAllViewsIcons();
- $view->setLimit(15);
- $view->disableSearchBox();
- $view->disableSort();
- $view->disableShowAllColumns();
-
- return $this->renderView($view, $fetch);
- }
-
- /**
- * Returns a rendered report that lists the count of visits for different ranges
- * of days since a visitor's last visit.
- *
- * @param bool $fetch Whether to return the rendered view as a string or echo it.
- * @return string The rendered report or nothing if $fetch is set to false.
- */
- public function getNumberOfVisitsByDaysSinceLast( $fetch = false )
- {
- $view = Piwik_ViewDataTable::factory( );
- $view->init( $this->pluginName, __FUNCTION__, 'VisitorInterest.getNumberOfVisitsByDaysSinceLast' );
- $view->setColumnsToDisplay( array('label', 'nb_visits') );
- $view->setSortedColumn('label', 'asc');
- $view->setColumnTranslation('label', Piwik_Translate('General_DaysSinceLastVisit'));
- $view->disableExcludeLowPopulation();
- $view->disableOffsetInformationAndPaginationControls();
- $view->disableShowAllViewsIcons();
- $view->setLimit(15);
- $view->disableSearchBox();
- $view->disableSort();
- $view->disableShowAllColumns();
-
- return $this->renderView($view, $fetch);
- }
+ function index()
+ {
+ $view = Piwik_View::factory('index');
+ $view->dataTableNumberOfVisitsPerVisitDuration = $this->getNumberOfVisitsPerVisitDuration(true);
+ $view->dataTableNumberOfVisitsPerPage = $this->getNumberOfVisitsPerPage(true);
+ $view->dataTableNumberOfVisitsByVisitNum = $this->getNumberOfVisitsByVisitCount(true);
+ $view->dataTableNumberOfVisitsByDaysSinceLast = $this->getNumberOfVisitsByDaysSinceLast(true);
+ echo $view->render();
+ }
+
+ function getNumberOfVisitsPerVisitDuration($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory('cloud');
+ $view->init($this->pluginName, __FUNCTION__, "VisitorInterest.getNumberOfVisitsPerVisitDuration");
+
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
+ $view->setSortedColumn('label', 'asc');
+ $view->setColumnTranslation('label', Piwik_Translate('VisitorInterest_ColumnVisitDuration'));
+ $view->setGraphLimit(10);
+ $view->disableSort();
+ $view->disableExcludeLowPopulation();
+ $view->disableOffsetInformationAndPaginationControls();
+ $view->disableSearchBox();
+ $view->disableShowAllColumns();
+
+ return $this->renderView($view, $fetch);
+ }
+
+ function getNumberOfVisitsPerPage($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory('cloud');
+ $view->init($this->pluginName, __FUNCTION__, "VisitorInterest.getNumberOfVisitsPerPage");
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
+ $view->setSortedColumn('label', 'asc');
+ $view->setColumnTranslation('label', Piwik_Translate('VisitorInterest_ColumnPagesPerVisit'));
+ $view->setGraphLimit(10);
+ $view->disableExcludeLowPopulation();
+ $view->disableOffsetInformationAndPaginationControls();
+ $view->disableSearchBox();
+ $view->disableSort();
+ $view->disableShowAllColumns();
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Returns a report that lists the count of visits for different ranges of
+ * a visitor's visit number.
+ *
+ * @param bool $fetch Whether to return the rendered view as a string or echo it.
+ * @return string The rendered report or nothing if $fetch is set to false.
+ */
+ public function getNumberOfVisitsByVisitCount($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, "VisitorInterest.getNumberOfVisitsByVisitCount");
+ $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_visits_percentage'));
+ $view->setSortedColumn('label', 'asc');
+ $view->setColumnTranslation('label', Piwik_Translate('VisitorInterest_VisitNum'));
+ $view->setColumnTranslation('nb_visits_percentage', str_replace(' ', '&nbsp;', Piwik_Translate('General_ColumnPercentageVisits')));
+ $view->disableExcludeLowPopulation();
+ $view->disableOffsetInformationAndPaginationControls();
+ $view->disableShowAllViewsIcons();
+ $view->setLimit(15);
+ $view->disableSearchBox();
+ $view->disableSort();
+ $view->disableShowAllColumns();
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Returns a rendered report that lists the count of visits for different ranges
+ * of days since a visitor's last visit.
+ *
+ * @param bool $fetch Whether to return the rendered view as a string or echo it.
+ * @return string The rendered report or nothing if $fetch is set to false.
+ */
+ public function getNumberOfVisitsByDaysSinceLast($fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory();
+ $view->init($this->pluginName, __FUNCTION__, 'VisitorInterest.getNumberOfVisitsByDaysSinceLast');
+ $view->setColumnsToDisplay(array('label', 'nb_visits'));
+ $view->setSortedColumn('label', 'asc');
+ $view->setColumnTranslation('label', Piwik_Translate('General_DaysSinceLastVisit'));
+ $view->disableExcludeLowPopulation();
+ $view->disableOffsetInformationAndPaginationControls();
+ $view->disableShowAllViewsIcons();
+ $view->setLimit(15);
+ $view->disableSearchBox();
+ $view->disableSort();
+ $view->disableShowAllColumns();
+
+ return $this->renderView($view, $fetch);
+ }
}
diff --git a/plugins/VisitorInterest/VisitorInterest.php b/plugins/VisitorInterest/VisitorInterest.php
index d4895502ce..00689e5c2c 100644
--- a/plugins/VisitorInterest/VisitorInterest.php
+++ b/plugins/VisitorInterest/VisitorInterest.php
@@ -15,326 +15,321 @@
*/
class Piwik_VisitorInterest extends Piwik_Plugin
{
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('VisitorInterest_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- return $info;
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenu',
- 'API.getReportMetadata' => 'getReportMetadata',
- );
- return $hooks;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $reports = &$notification->getNotificationObject();
- $reports[] = array(
- 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('VisitorInterest_WidgetLengths'),
- 'module' => 'VisitorInterest',
- 'action' => 'getNumberOfVisitsPerVisitDuration',
- 'dimension' => Piwik_Translate('VisitorInterest_ColumnVisitDuration'),
- 'metrics' => array( 'nb_visits' ),
- 'processedMetrics' => false,
- 'constantRowsCount' => true,
- 'documentation' => Piwik_Translate('VisitorInterest_WidgetLengthsDocumentation')
- .'<br />'.Piwik_Translate('General_ChangeTagCloudView'),
- 'order' => 15
- );
-
- $reports[] = array(
- 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('VisitorInterest_WidgetPages'),
- 'module' => 'VisitorInterest',
- 'action' => 'getNumberOfVisitsPerPage',
- 'dimension' => Piwik_Translate('VisitorInterest_ColumnPagesPerVisit'),
- 'metrics' => array( 'nb_visits' ),
- 'processedMetrics' => false,
- 'constantRowsCount' => true,
- 'documentation' => Piwik_Translate('VisitorInterest_WidgetPagesDocumentation')
- .'<br />'.Piwik_Translate('General_ChangeTagCloudView'),
- 'order' => 20
- );
-
- $reports[] = array(
- 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('VisitorInterest_visitsByVisitCount'),
- 'module' => 'VisitorInterest',
- 'action' => 'getNumberOfVisitsByVisitCount',
- 'dimension' => Piwik_Translate('VisitorInterest_visitsByVisitCount'),
- 'metrics' => array(
- 'nb_visits',
- 'nb_visits_percentage' => Piwik_Translate('General_ColumnPercentageVisits')
- ),
- 'processedMetrics' => false,
- 'constantRowsCount' => true,
- 'documentation' => Piwik_Translate('VisitorInterest_WidgetVisitsByNumDocumentation')
- .'<br />'.Piwik_Translate('General_ChangeTagCloudView'),
- 'order' => 25
- );
-
- $reports[] = array(
- 'category' => Piwik_Translate('General_Visitors'),
- 'name' => Piwik_Translate('VisitorInterest_VisitsByDaysSinceLast'),
- 'module' => 'VisitorInterest',
- 'action' => 'getNumberOfVisitsByDaysSinceLast',
- 'dimension' => Piwik_Translate('VisitorInterest_VisitsByDaysSinceLast'),
- 'metrics' => array( 'nb_visits' ),
- 'processedMetrics' => false,
- 'constantRowsCount' => true,
- 'documentation' => Piwik_Translate('VisitorInterest_WidgetVisitsByDaysSinceLastDocumentation'),
- 'order' => 30
- );
- }
-
- function addWidgets()
- {
- Piwik_AddWidget( 'General_Visitors', 'VisitorInterest_WidgetLengths', 'VisitorInterest', 'getNumberOfVisitsPerVisitDuration');
- Piwik_AddWidget( 'General_Visitors', 'VisitorInterest_WidgetPages', 'VisitorInterest', 'getNumberOfVisitsPerPage');
- Piwik_AddWidget( 'General_Visitors', 'VisitorInterest_visitsByVisitCount', 'VisitorInterest', 'getNumberOfVisitsByVisitCount');
- Piwik_AddWidget( 'General_Visitors', 'VisitorInterest_WidgetVisitsByDaysSinceLast', 'VisitorInterest', 'getNumberOfVisitsByDaysSinceLast');
- }
-
- function addMenu()
- {
- Piwik_RenameMenuEntry('General_Visitors', 'VisitFrequency_SubmenuFrequency',
- 'General_Visitors', 'VisitorInterest_Engagement' );
- }
-
- function postLoad()
- {
- Piwik_AddAction('template_headerVisitsFrequency', array('Piwik_VisitorInterest','headerVisitsFrequency'));
- Piwik_AddAction('template_footerVisitsFrequency', array('Piwik_VisitorInterest','footerVisitsFrequency'));
- }
-
- // third element is unit (s for seconds, default is munutes)
- protected static $timeGap = array(
- array(0, 10, 's'),
- array(11, 30, 's'),
- array(31, 60, 's'),
- array(1, 2),
- array(2, 4),
- array(4, 7),
- array(7, 10),
- array(10, 15),
- array(15, 30),
- array(30)
- );
-
- protected static $pageGap = array(
- array(1, 1),
- array(2, 2),
- array(3, 3),
- array(4, 4),
- array(5, 5),
- array(6, 7),
- array(8, 10),
- array(11, 14),
- array(15, 20),
- array(20)
- );
-
- /**
- * The set of ranges used when calculating the 'visitors who visited at least N times' report.
- */
- protected static $visitNumberGap = array(
- array(1, 1),
- array(2, 2),
- array(3, 3),
- array(4, 4),
- array(5, 5),
- array(6, 6),
- array(7, 7),
- array(8, 8),
- array(9, 14),
- array(15, 25),
- array(26, 50),
- array(51, 100),
- array(101, 200),
- array(200)
- );
-
- /**
- * The set of ranges used when calculating the 'days since last visit' report.
- */
- protected static $daysSinceLastVisitGap = array(
- array(0, 0),
- array(1, 1),
- array(2, 2),
- array(3, 3),
- array(4, 4),
- array(5, 5),
- array(6, 6),
- array(7, 7),
- array(8, 14),
- array(15, 30),
- array(31, 60),
- array(61, 120),
- array(121, 364),
- array(364)
- );
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archivePeriod( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = array(
- 'VisitorInterest_timeGap',
- 'VisitorInterest_pageGap',
- 'VisitorInterest_visitsByVisitCount',
- 'VisitorInterest_daysSinceLastVisit'
- );
- $archiveProcessing->archiveDataTable($dataTableToSum);
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- public function archiveDay( $notification )
- {
- $this->archiveProcessing = $notification->getNotificationObject();
-
- if(!$this->archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- // these prefixes are prepended to the 'SELECT as' parts of each SELECT expression. detecting
- // these prefixes allows us to get all the data in one query.
- $timeGapPrefix = 'tg';
- $pageGapPrefix = 'pg';
- $visitsByVisitNumPrefix = 'vbvn';
- $daysSinceLastVisitPrefix = 'dslv';
-
- // extra condition for the SQL SELECT that makes sure only returning visits are counted
- // when creating the 'days since last visit' report. the SELECT expression below it
- // is used to count all new visits.
- $daysSinceLastExtraCondition = 'and log_visit.visitor_returning = 1';
- $selectAs = $daysSinceLastVisitPrefix.'General_NewVisits';
- $newVisitCountSelect = "sum(case when log_visit.visitor_returning = 0 then 1 else 0 end) as `$selectAs`";
-
- // create the select expressions to use
- $timeGapSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visit_total_time', self::getSecondsGap(), 'log_visit', $timeGapPrefix);
- $pageGapSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visit_total_actions', self::$pageGap, 'log_visit', $pageGapPrefix);
- $visitsByVisitNumSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visitor_count_visits', self::$visitNumberGap, 'log_visit', $visitsByVisitNumPrefix);
-
- $daysSinceLastVisitSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visitor_days_since_last', self::$daysSinceLastVisitGap, 'log_visit', $daysSinceLastVisitPrefix,
- $daysSinceLastExtraCondition);
- array_unshift($daysSinceLastVisitSelects, $newVisitCountSelect);
-
- $selects = array_merge(
- $timeGapSelects, $pageGapSelects, $visitsByVisitNumSelects, $daysSinceLastVisitSelects);
-
- // select data for every report
- $row = $this->archiveProcessing->queryVisitsSimple(implode(',', $selects));
-
- // archive visits by total time report
- $recordName = 'VisitorInterest_timeGap';
- $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $timeGapPrefix);
-
- // archive visits by total actions report
- $recordName = 'VisitorInterest_pageGap';
- $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $pageGapPrefix);
-
- // archive visits by visit number report
- $recordName = 'VisitorInterest_visitsByVisitCount';
- $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $visitsByVisitNumPrefix);
-
- // archive days since last visit report
- $recordName = 'VisitorInterest_daysSinceLastVisit';
- $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $daysSinceLastVisitPrefix);
- }
-
- /**
- * Transforms and returns the set of ranges used to calculate the 'visits by total time'
- * report from ranges in minutes to equivalent ranges in seconds.
- */
- protected static function getSecondsGap()
- {
- $secondsGap = array();
- foreach(self::$timeGap as $gap)
- {
- if (count($gap) == 3 && $gap[2] == 's') // if the units are already in seconds, just assign them
- {
- $secondsGap[] = array($gap[0], $gap[1]);
- }
- else if (count($gap) == 2)
- {
- $secondsGap[] = array($gap[0] * 60, $gap[1] * 60);
- }
- else
- {
- $secondsGap[] = array($gap[0] * 60);
- }
- }
- return $secondsGap;
- }
-
- /**
- * Creates and archives a DataTable from some (or all) elements of a supplied database
- * row.
- *
- * @param string $recordName The record name to use when inserting the new archive.
- * @param array $row The database row to use.
- * @param string $selectAsPrefix The string to look for as the prefix of SELECT as
- * expressions. Elements in $row that have a SELECT as
- * with this string as a prefix are used in creating
- * the DataTable.'
- */
- protected function archiveRangeStats($recordName, $row, $index, $selectAsPrefix)
- {
- // create the DataTable from parts of the result row
- $dataTable = $this->archiveProcessing->getSimpleDataTableFromRow($row, $index, $selectAsPrefix);
-
- // insert the data table as a blob archive
- $this->archiveProcessing->insertBlobRecord($recordName, $dataTable->getSerialized());
- destroy($dataTable);
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- static public function headerVisitsFrequency($notification)
- {
- $out =& $notification->getNotificationObject();
- $out = '<div id="leftcolumn">';
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- static public function footerVisitsFrequency($notification)
- {
- $out =& $notification->getNotificationObject();
- $out = '</div>
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('VisitorInterest_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenu',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ );
+ return $hooks;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $reports = & $notification->getNotificationObject();
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('VisitorInterest_WidgetLengths'),
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsPerVisitDuration',
+ 'dimension' => Piwik_Translate('VisitorInterest_ColumnVisitDuration'),
+ 'metrics' => array('nb_visits'),
+ 'processedMetrics' => false,
+ 'constantRowsCount' => true,
+ 'documentation' => Piwik_Translate('VisitorInterest_WidgetLengthsDocumentation')
+ . '<br />' . Piwik_Translate('General_ChangeTagCloudView'),
+ 'order' => 15
+ );
+
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('VisitorInterest_WidgetPages'),
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsPerPage',
+ 'dimension' => Piwik_Translate('VisitorInterest_ColumnPagesPerVisit'),
+ 'metrics' => array('nb_visits'),
+ 'processedMetrics' => false,
+ 'constantRowsCount' => true,
+ 'documentation' => Piwik_Translate('VisitorInterest_WidgetPagesDocumentation')
+ . '<br />' . Piwik_Translate('General_ChangeTagCloudView'),
+ 'order' => 20
+ );
+
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('VisitorInterest_visitsByVisitCount'),
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsByVisitCount',
+ 'dimension' => Piwik_Translate('VisitorInterest_visitsByVisitCount'),
+ 'metrics' => array(
+ 'nb_visits',
+ 'nb_visits_percentage' => Piwik_Translate('General_ColumnPercentageVisits')
+ ),
+ 'processedMetrics' => false,
+ 'constantRowsCount' => true,
+ 'documentation' => Piwik_Translate('VisitorInterest_WidgetVisitsByNumDocumentation')
+ . '<br />' . Piwik_Translate('General_ChangeTagCloudView'),
+ 'order' => 25
+ );
+
+ $reports[] = array(
+ 'category' => Piwik_Translate('General_Visitors'),
+ 'name' => Piwik_Translate('VisitorInterest_VisitsByDaysSinceLast'),
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsByDaysSinceLast',
+ 'dimension' => Piwik_Translate('VisitorInterest_VisitsByDaysSinceLast'),
+ 'metrics' => array('nb_visits'),
+ 'processedMetrics' => false,
+ 'constantRowsCount' => true,
+ 'documentation' => Piwik_Translate('VisitorInterest_WidgetVisitsByDaysSinceLastDocumentation'),
+ 'order' => 30
+ );
+ }
+
+ function addWidgets()
+ {
+ Piwik_AddWidget('General_Visitors', 'VisitorInterest_WidgetLengths', 'VisitorInterest', 'getNumberOfVisitsPerVisitDuration');
+ Piwik_AddWidget('General_Visitors', 'VisitorInterest_WidgetPages', 'VisitorInterest', 'getNumberOfVisitsPerPage');
+ Piwik_AddWidget('General_Visitors', 'VisitorInterest_visitsByVisitCount', 'VisitorInterest', 'getNumberOfVisitsByVisitCount');
+ Piwik_AddWidget('General_Visitors', 'VisitorInterest_WidgetVisitsByDaysSinceLast', 'VisitorInterest', 'getNumberOfVisitsByDaysSinceLast');
+ }
+
+ function addMenu()
+ {
+ Piwik_RenameMenuEntry('General_Visitors', 'VisitFrequency_SubmenuFrequency',
+ 'General_Visitors', 'VisitorInterest_Engagement');
+ }
+
+ function postLoad()
+ {
+ Piwik_AddAction('template_headerVisitsFrequency', array('Piwik_VisitorInterest', 'headerVisitsFrequency'));
+ Piwik_AddAction('template_footerVisitsFrequency', array('Piwik_VisitorInterest', 'footerVisitsFrequency'));
+ }
+
+ // third element is unit (s for seconds, default is munutes)
+ protected static $timeGap = array(
+ array(0, 10, 's'),
+ array(11, 30, 's'),
+ array(31, 60, 's'),
+ array(1, 2),
+ array(2, 4),
+ array(4, 7),
+ array(7, 10),
+ array(10, 15),
+ array(15, 30),
+ array(30)
+ );
+
+ protected static $pageGap = array(
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 7),
+ array(8, 10),
+ array(11, 14),
+ array(15, 20),
+ array(20)
+ );
+
+ /**
+ * The set of ranges used when calculating the 'visitors who visited at least N times' report.
+ */
+ protected static $visitNumberGap = array(
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 6),
+ array(7, 7),
+ array(8, 8),
+ array(9, 14),
+ array(15, 25),
+ array(26, 50),
+ array(51, 100),
+ array(101, 200),
+ array(200)
+ );
+
+ /**
+ * The set of ranges used when calculating the 'days since last visit' report.
+ */
+ protected static $daysSinceLastVisitGap = array(
+ array(0, 0),
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 6),
+ array(7, 7),
+ array(8, 14),
+ array(15, 30),
+ array(31, 60),
+ array(61, 120),
+ array(121, 364),
+ array(364)
+ );
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ function archivePeriod($notification)
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $dataTableToSum = array(
+ 'VisitorInterest_timeGap',
+ 'VisitorInterest_pageGap',
+ 'VisitorInterest_visitsByVisitCount',
+ 'VisitorInterest_daysSinceLastVisit'
+ );
+ $archiveProcessing->archiveDataTable($dataTableToSum);
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ public function archiveDay($notification)
+ {
+ $this->archiveProcessing = $notification->getNotificationObject();
+
+ if (!$this->archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ // these prefixes are prepended to the 'SELECT as' parts of each SELECT expression. detecting
+ // these prefixes allows us to get all the data in one query.
+ $timeGapPrefix = 'tg';
+ $pageGapPrefix = 'pg';
+ $visitsByVisitNumPrefix = 'vbvn';
+ $daysSinceLastVisitPrefix = 'dslv';
+
+ // extra condition for the SQL SELECT that makes sure only returning visits are counted
+ // when creating the 'days since last visit' report. the SELECT expression below it
+ // is used to count all new visits.
+ $daysSinceLastExtraCondition = 'and log_visit.visitor_returning = 1';
+ $selectAs = $daysSinceLastVisitPrefix . 'General_NewVisits';
+ $newVisitCountSelect = "sum(case when log_visit.visitor_returning = 0 then 1 else 0 end) as `$selectAs`";
+
+ // create the select expressions to use
+ $timeGapSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
+ 'visit_total_time', self::getSecondsGap(), 'log_visit', $timeGapPrefix);
+ $pageGapSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
+ 'visit_total_actions', self::$pageGap, 'log_visit', $pageGapPrefix);
+ $visitsByVisitNumSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
+ 'visitor_count_visits', self::$visitNumberGap, 'log_visit', $visitsByVisitNumPrefix);
+
+ $daysSinceLastVisitSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
+ 'visitor_days_since_last', self::$daysSinceLastVisitGap, 'log_visit', $daysSinceLastVisitPrefix,
+ $daysSinceLastExtraCondition);
+ array_unshift($daysSinceLastVisitSelects, $newVisitCountSelect);
+
+ $selects = array_merge(
+ $timeGapSelects, $pageGapSelects, $visitsByVisitNumSelects, $daysSinceLastVisitSelects);
+
+ // select data for every report
+ $row = $this->archiveProcessing->queryVisitsSimple(implode(',', $selects));
+
+ // archive visits by total time report
+ $recordName = 'VisitorInterest_timeGap';
+ $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $timeGapPrefix);
+
+ // archive visits by total actions report
+ $recordName = 'VisitorInterest_pageGap';
+ $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $pageGapPrefix);
+
+ // archive visits by visit number report
+ $recordName = 'VisitorInterest_visitsByVisitCount';
+ $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $visitsByVisitNumPrefix);
+
+ // archive days since last visit report
+ $recordName = 'VisitorInterest_daysSinceLastVisit';
+ $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $daysSinceLastVisitPrefix);
+ }
+
+ /**
+ * Transforms and returns the set of ranges used to calculate the 'visits by total time'
+ * report from ranges in minutes to equivalent ranges in seconds.
+ */
+ protected static function getSecondsGap()
+ {
+ $secondsGap = array();
+ foreach (self::$timeGap as $gap) {
+ if (count($gap) == 3 && $gap[2] == 's') // if the units are already in seconds, just assign them
+ {
+ $secondsGap[] = array($gap[0], $gap[1]);
+ } else if (count($gap) == 2) {
+ $secondsGap[] = array($gap[0] * 60, $gap[1] * 60);
+ } else {
+ $secondsGap[] = array($gap[0] * 60);
+ }
+ }
+ return $secondsGap;
+ }
+
+ /**
+ * Creates and archives a DataTable from some (or all) elements of a supplied database
+ * row.
+ *
+ * @param string $recordName The record name to use when inserting the new archive.
+ * @param array $row The database row to use.
+ * @param string $selectAsPrefix The string to look for as the prefix of SELECT as
+ * expressions. Elements in $row that have a SELECT as
+ * with this string as a prefix are used in creating
+ * the DataTable.'
+ */
+ protected function archiveRangeStats($recordName, $row, $index, $selectAsPrefix)
+ {
+ // create the DataTable from parts of the result row
+ $dataTable = $this->archiveProcessing->getSimpleDataTableFromRow($row, $index, $selectAsPrefix);
+
+ // insert the data table as a blob archive
+ $this->archiveProcessing->insertBlobRecord($recordName, $dataTable->getSerialized());
+ destroy($dataTable);
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ static public function headerVisitsFrequency($notification)
+ {
+ $out =& $notification->getNotificationObject();
+ $out = '<div id="leftcolumn">';
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ static public function footerVisitsFrequency($notification)
+ {
+ $out =& $notification->getNotificationObject();
+ $out = '</div>
<div id="rightcolumn">
';
- $out .= Piwik_FrontController::getInstance()->fetchDispatch('VisitorInterest','index');
- $out .= '</div>';
- }
+ $out .= Piwik_FrontController::getInstance()->fetchDispatch('VisitorInterest', 'index');
+ $out .= '</div>';
+ }
}
diff --git a/plugins/VisitorInterest/templates/index.tpl b/plugins/VisitorInterest/templates/index.tpl
index 22e3fa2421..6ba4cc6c5a 100644
--- a/plugins/VisitorInterest/templates/index.tpl
+++ b/plugins/VisitorInterest/templates/index.tpl
@@ -1,4 +1,3 @@
-
<h2>{'VisitorInterest_VisitsPerDuration'|translate}</h2>
{$dataTableNumberOfVisitsPerVisitDuration}
diff --git a/plugins/VisitsSummary/API.php b/plugins/VisitsSummary/API.php
index 9294e03b74..0864c517b5 100644
--- a/plugins/VisitsSummary/API.php
+++ b/plugins/VisitsSummary/API.php
@@ -17,146 +17,136 @@
*/
class Piwik_VisitsSummary_API
{
- static private $instance = null;
- /**
- * @return Piwik_VisitsSummary_API
- */
- static public function getInstance()
- {
- if (self::$instance == null)
- {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- public function get( $idSite, $period, $date, $segment = false, $columns = false)
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
-
- // array values are comma separated
- $columns = Piwik::getArrayFromApiParameter($columns);
- $tempColumns = array();
-
- $bounceRateRequested = $actionsPerVisitRequested = $averageVisitDurationRequested = false;
- if(!empty($columns))
- {
- // make sure base metrics are there for processed metrics
- if(false !== ($bounceRateRequested = array_search('bounce_rate', $columns)))
- {
- if (!in_array('nb_visits', $columns)) $tempColumns[] = 'nb_visits';
- if (!in_array('bounce_count', $columns)) $tempColumns[] = 'bounce_count';
- unset($columns[$bounceRateRequested]);
- }
- if(false !== ($actionsPerVisitRequested = array_search('nb_actions_per_visit', $columns)))
- {
- if (!in_array('nb_visits', $columns)) $tempColumns[] = 'nb_visits';
- if (!in_array('nb_actions', $columns)) $tempColumns[] = 'nb_actions';
- unset($columns[$actionsPerVisitRequested]);
- }
- if(false !== ($averageVisitDurationRequested = array_search('avg_time_on_site', $columns)))
- {
- if (!in_array('nb_visits', $columns)) $tempColumns[] = 'nb_visits';
- if (!in_array('sum_visit_length', $columns)) $tempColumns[] = 'sum_visit_length';
- unset($columns[$averageVisitDurationRequested]);
- }
- $tempColumns = array_unique($tempColumns);
- rsort($tempColumns);
- $columns = array_merge($columns, $tempColumns);
- }
- else
- {
- $bounceRateRequested = $actionsPerVisitRequested = $averageVisitDurationRequested = true;
- $columns = array(
- 'nb_visits',
- 'nb_actions',
- 'nb_visits_converted',
- 'bounce_count',
- 'sum_visit_length',
- 'max_actions'
- );
- if(Piwik::isUniqueVisitorsEnabled($period))
- {
- $columns = array_merge(array('nb_uniq_visitors'), $columns);
- }
- // Force reindex from 0 to N otherwise the SQL bind will fail
- $columns = array_values($columns);
- }
-
- $dataTable = $archive->getDataTableFromNumeric($columns);
-
- // Process ratio metrics from base metrics, when requested
- if($bounceRateRequested !== false)
- {
- $dataTable->filter('ColumnCallbackAddColumnPercentage', array('bounce_rate', 'bounce_count', 'nb_visits', 0));
- }
- if($actionsPerVisitRequested !== false)
- {
- $dataTable->filter('ColumnCallbackAddColumnQuotient', array('nb_actions_per_visit', 'nb_actions', 'nb_visits', 1));
- }
- if($averageVisitDurationRequested !== false)
- {
- $dataTable->filter('ColumnCallbackAddColumnQuotient', array('avg_time_on_site', 'sum_visit_length', 'nb_visits', 0));
- }
-
- // remove temp metrics that were used to compute processed metrics
- $dataTable->deleteColumns($tempColumns);
-
- return $dataTable;
- }
-
- protected function getNumeric( $idSite, $period, $date, $segment, $toFetch )
- {
- Piwik::checkUserHasViewAccess( $idSite );
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment );
- $dataTable = $archive->getNumeric($toFetch);
- return $dataTable;
- }
-
- public function getVisits( $idSite, $period, $date, $segment = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, 'nb_visits');
- }
-
- public function getUniqueVisitors( $idSite, $period, $date, $segment = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, 'nb_uniq_visitors');
- }
-
- public function getActions( $idSite, $period, $date, $segment = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, 'nb_actions');
- }
-
- public function getMaxActions( $idSite, $period, $date, $segment = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, 'max_actions');
- }
-
- public function getBounceCount( $idSite, $period, $date, $segment = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, 'bounce_count');
- }
-
- public function getVisitsConverted( $idSite, $period, $date, $segment = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, 'nb_visits_converted');
- }
-
- public function getSumVisitsLength( $idSite, $period, $date, $segment = false )
- {
- return $this->getNumeric( $idSite, $period, $date, $segment, 'sum_visit_length');
- }
-
- public function getSumVisitsLengthPretty( $idSite, $period, $date, $segment = false )
- {
- $table = $this->getSumVisitsLength( $idSite, $period, $date, $segment );
- if($table instanceof Piwik_DataTable_Array) {
- $table->filter('ColumnCallbackReplace', array(0, array('Piwik', 'getPrettyTimeFromSeconds')));
- } else {
- $table = Piwik::getPrettyTimeFromSeconds($table);
- }
- return $table;
- }
+ static private $instance = null;
+
+ /**
+ * @return Piwik_VisitsSummary_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ public function get($idSite, $period, $date, $segment = false, $columns = false)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+
+ // array values are comma separated
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ $tempColumns = array();
+
+ $bounceRateRequested = $actionsPerVisitRequested = $averageVisitDurationRequested = false;
+ if (!empty($columns)) {
+ // make sure base metrics are there for processed metrics
+ if (false !== ($bounceRateRequested = array_search('bounce_rate', $columns))) {
+ if (!in_array('nb_visits', $columns)) $tempColumns[] = 'nb_visits';
+ if (!in_array('bounce_count', $columns)) $tempColumns[] = 'bounce_count';
+ unset($columns[$bounceRateRequested]);
+ }
+ if (false !== ($actionsPerVisitRequested = array_search('nb_actions_per_visit', $columns))) {
+ if (!in_array('nb_visits', $columns)) $tempColumns[] = 'nb_visits';
+ if (!in_array('nb_actions', $columns)) $tempColumns[] = 'nb_actions';
+ unset($columns[$actionsPerVisitRequested]);
+ }
+ if (false !== ($averageVisitDurationRequested = array_search('avg_time_on_site', $columns))) {
+ if (!in_array('nb_visits', $columns)) $tempColumns[] = 'nb_visits';
+ if (!in_array('sum_visit_length', $columns)) $tempColumns[] = 'sum_visit_length';
+ unset($columns[$averageVisitDurationRequested]);
+ }
+ $tempColumns = array_unique($tempColumns);
+ rsort($tempColumns);
+ $columns = array_merge($columns, $tempColumns);
+ } else {
+ $bounceRateRequested = $actionsPerVisitRequested = $averageVisitDurationRequested = true;
+ $columns = array(
+ 'nb_visits',
+ 'nb_actions',
+ 'nb_visits_converted',
+ 'bounce_count',
+ 'sum_visit_length',
+ 'max_actions'
+ );
+ if (Piwik::isUniqueVisitorsEnabled($period)) {
+ $columns = array_merge(array('nb_uniq_visitors'), $columns);
+ }
+ // Force reindex from 0 to N otherwise the SQL bind will fail
+ $columns = array_values($columns);
+ }
+
+ $dataTable = $archive->getDataTableFromNumeric($columns);
+
+ // Process ratio metrics from base metrics, when requested
+ if ($bounceRateRequested !== false) {
+ $dataTable->filter('ColumnCallbackAddColumnPercentage', array('bounce_rate', 'bounce_count', 'nb_visits', 0));
+ }
+ if ($actionsPerVisitRequested !== false) {
+ $dataTable->filter('ColumnCallbackAddColumnQuotient', array('nb_actions_per_visit', 'nb_actions', 'nb_visits', 1));
+ }
+ if ($averageVisitDurationRequested !== false) {
+ $dataTable->filter('ColumnCallbackAddColumnQuotient', array('avg_time_on_site', 'sum_visit_length', 'nb_visits', 0));
+ }
+
+ // remove temp metrics that were used to compute processed metrics
+ $dataTable->deleteColumns($tempColumns);
+
+ return $dataTable;
+ }
+
+ protected function getNumeric($idSite, $period, $date, $segment, $toFetch)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ $dataTable = $archive->getNumeric($toFetch);
+ return $dataTable;
+ }
+
+ public function getVisits($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, 'nb_visits');
+ }
+
+ public function getUniqueVisitors($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, 'nb_uniq_visitors');
+ }
+
+ public function getActions($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, 'nb_actions');
+ }
+
+ public function getMaxActions($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, 'max_actions');
+ }
+
+ public function getBounceCount($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, 'bounce_count');
+ }
+
+ public function getVisitsConverted($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, 'nb_visits_converted');
+ }
+
+ public function getSumVisitsLength($idSite, $period, $date, $segment = false)
+ {
+ return $this->getNumeric($idSite, $period, $date, $segment, 'sum_visit_length');
+ }
+
+ public function getSumVisitsLengthPretty($idSite, $period, $date, $segment = false)
+ {
+ $table = $this->getSumVisitsLength($idSite, $period, $date, $segment);
+ if ($table instanceof Piwik_DataTable_Array) {
+ $table->filter('ColumnCallbackReplace', array(0, array('Piwik', 'getPrettyTimeFromSeconds')));
+ } else {
+ $table = Piwik::getPrettyTimeFromSeconds($table);
+ }
+ return $table;
+ }
}
diff --git a/plugins/VisitsSummary/Controller.php b/plugins/VisitsSummary/Controller.php
index dec6a70957..86ebf7b5ac 100644
--- a/plugins/VisitsSummary/Controller.php
+++ b/plugins/VisitsSummary/Controller.php
@@ -15,156 +15,153 @@
*/
class Piwik_VisitsSummary_Controller extends Piwik_Controller
{
- public function index()
- {
- $view = Piwik_View::factory('index');
- $this->setPeriodVariablesView($view);
- $view->graphEvolutionVisitsSummary = $this->getEvolutionGraph( true, array('nb_visits') );
- $this->setSparklinesAndNumbers($view);
- echo $view->render();
- }
-
- public function getSparklines()
- {
- $view = Piwik_View::factory('sparklines');
- $this->setPeriodVariablesView($view);
- $this->setSparklinesAndNumbers($view);
- echo $view->render();
- }
-
- public function getEvolutionGraph( $fetch = false, array $columns = array())
- {
- if(empty($columns))
- {
- $columns = Piwik_Common::getRequestVar('columns');
- $columns = Piwik::getArrayFromApiParameter($columns);
- }
-
- $documentation = Piwik_Translate('VisitsSummary_VisitsSummaryDocumentation').'<br />'
- . Piwik_Translate('General_BrokenDownReportDocumentation').'<br /><br />'
-
- . '<b>'.Piwik_Translate('General_ColumnNbVisits').':</b> '
- . Piwik_Translate('General_ColumnNbVisitsDocumentation').'<br />'
-
- . '<b>'.Piwik_Translate('General_ColumnNbUniqVisitors').':</b> '
- . Piwik_Translate('General_ColumnNbUniqVisitorsDocumentation').'<br />'
-
- . '<b>'.Piwik_Translate('General_ColumnNbActions').':</b> '
- . Piwik_Translate('General_ColumnNbActionsDocumentation').'<br />'
-
- . '<b>'.Piwik_Translate('General_ColumnActionsPerVisit').':</b> '
- . Piwik_Translate('General_ColumnActionsPerVisitDocumentation');
-
- $selectableColumns = array(
- // columns from VisitsSummary.get
- 'nb_visits',
- 'nb_uniq_visitors',
- 'avg_time_on_site',
- 'bounce_rate',
- 'nb_actions_per_visit',
- 'max_actions',
- 'nb_visits_converted',
- // columns from Actions.get
- 'nb_pageviews',
- 'nb_uniq_pageviews',
- 'nb_downloads',
- 'nb_uniq_downloads',
- 'nb_outlinks',
- 'nb_uniq_outlinks'
- );
-
- $idSite = Piwik_Common::getRequestVar('idSite');
- $displaySiteSearch = Piwik_Site::isSiteSearchEnabledFor($idSite);
-
- if($displaySiteSearch) {
- $selectableColumns[] = 'nb_searches';
- $selectableColumns[] = 'nb_keywords';
- }
- $view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, __FUNCTION__, $columns,
- $selectableColumns, $documentation);
-
- return $this->renderView($view, $fetch);
- }
-
- static public function getVisitsSummary()
- {
- $requestString = "method=VisitsSummary.get".
- "&format=original".
- // we disable filters for example "search for pattern", in the case this method is called
- // by a method that already calls the API with some generic filters applied
- "&disable_generic_filters=1";
- $request = new Piwik_API_Request($requestString);
- $result = $request->process();
- return empty($result) ? new Piwik_DataTable() : $result;
- }
-
- static public function getVisits()
- {
- $requestString = "method=VisitsSummary.getVisits".
- "&format=original".
- "&disable_generic_filters=1";
- $request = new Piwik_API_Request($requestString);
- return $request->process();
- }
-
- protected function setSparklinesAndNumbers($view)
- {
- $view->urlSparklineNbVisits = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => $view->displayUniqueVisitors ? array('nb_visits', 'nb_uniq_visitors') : array('nb_visits')));
- $view->urlSparklineNbPageviews = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_pageviews', 'nb_uniq_pageviews')));
- $view->urlSparklineNbDownloads = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_downloads', 'nb_uniq_downloads')));
- $view->urlSparklineNbOutlinks = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_outlinks', 'nb_uniq_outlinks')));
- $view->urlSparklineAvgVisitDuration = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('avg_time_on_site')));
- $view->urlSparklineMaxActions = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('max_actions')));
- $view->urlSparklineActionsPerVisit = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_actions_per_visit')));
- $view->urlSparklineBounceRate = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('bounce_rate')));
-
- $idSite = Piwik_Common::getRequestVar('idSite');
- $displaySiteSearch = Piwik_Site::isSiteSearchEnabledFor($idSite);
- if($displaySiteSearch)
- {
- $view->urlSparklineNbSearches = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_searches', 'nb_keywords')));
- }
- $view->displaySiteSearch = $displaySiteSearch;
-
- $dataTableVisit = self::getVisitsSummary();
- $dataRow = $dataTableVisit->getRowsCount() == 0 ? new Piwik_DataTable_Row() : $dataTableVisit->getFirstRow();
-
- $dataTableActions = Piwik_Actions_API::getInstance()->get($idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), Piwik_Common::getRequestVar('segment',false));
- $dataActionsRow =
- $dataTableActions->getRowsCount() == 0 ? new Piwik_DataTable_Row() : $dataTableActions->getFirstRow();
-
- $view->nbUniqVisitors = (int)$dataRow->getColumn('nb_uniq_visitors');
- $nbVisits = (int)$dataRow->getColumn('nb_visits');
- $view->nbVisits = $nbVisits;
- $view->nbPageviews = (int)$dataActionsRow->getColumn('nb_pageviews');
- $view->nbUniquePageviews = (int)$dataActionsRow->getColumn('nb_uniq_pageviews');
- $view->nbDownloads = (int)$dataActionsRow->getColumn('nb_downloads');
- $view->nbUniqueDownloads = (int)$dataActionsRow->getColumn('nb_uniq_downloads');
- $view->nbOutlinks = (int)$dataActionsRow->getColumn('nb_outlinks');
- $view->nbUniqueOutlinks = (int)$dataActionsRow->getColumn('nb_uniq_outlinks');
- $view->averageVisitDuration = $dataRow->getColumn('avg_time_on_site');
- $nbBouncedVisits = $dataRow->getColumn('bounce_count');
- $view->bounceRate = Piwik::getPercentageSafe($nbBouncedVisits, $nbVisits);
- $view->maxActions = (int)$dataRow->getColumn('max_actions');
- $view->nbActionsPerVisit = $dataRow->getColumn('nb_actions_per_visit');
-
- if($displaySiteSearch)
- {
- $view->nbSearches = (int)$dataActionsRow->getColumn('nb_searches');
- $view->nbKeywords = (int)$dataActionsRow->getColumn('nb_keywords');
- }
-
- // backward compatibility:
- // show actions if the finer metrics are not archived
- $view->showOnlyActions = false;
- if ( $dataActionsRow->getColumn('nb_pageviews')
- + $dataActionsRow->getColumn('nb_downloads')
- + $dataActionsRow->getColumn('nb_outlinks') == 0
- && $dataRow->getColumn('nb_actions') > 0)
- {
- $view->showOnlyActions = true;
- $view->nbActions = $dataRow->getColumn('nb_actions');
- $view->urlSparklineNbActions = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_actions')));
- }
- }
+ public function index()
+ {
+ $view = Piwik_View::factory('index');
+ $this->setPeriodVariablesView($view);
+ $view->graphEvolutionVisitsSummary = $this->getEvolutionGraph(true, array('nb_visits'));
+ $this->setSparklinesAndNumbers($view);
+ echo $view->render();
+ }
+
+ public function getSparklines()
+ {
+ $view = Piwik_View::factory('sparklines');
+ $this->setPeriodVariablesView($view);
+ $this->setSparklinesAndNumbers($view);
+ echo $view->render();
+ }
+
+ public function getEvolutionGraph($fetch = false, array $columns = array())
+ {
+ if (empty($columns)) {
+ $columns = Piwik_Common::getRequestVar('columns');
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ }
+
+ $documentation = Piwik_Translate('VisitsSummary_VisitsSummaryDocumentation') . '<br />'
+ . Piwik_Translate('General_BrokenDownReportDocumentation') . '<br /><br />'
+
+ . '<b>' . Piwik_Translate('General_ColumnNbVisits') . ':</b> '
+ . Piwik_Translate('General_ColumnNbVisitsDocumentation') . '<br />'
+
+ . '<b>' . Piwik_Translate('General_ColumnNbUniqVisitors') . ':</b> '
+ . Piwik_Translate('General_ColumnNbUniqVisitorsDocumentation') . '<br />'
+
+ . '<b>' . Piwik_Translate('General_ColumnNbActions') . ':</b> '
+ . Piwik_Translate('General_ColumnNbActionsDocumentation') . '<br />'
+
+ . '<b>' . Piwik_Translate('General_ColumnActionsPerVisit') . ':</b> '
+ . Piwik_Translate('General_ColumnActionsPerVisitDocumentation');
+
+ $selectableColumns = array(
+ // columns from VisitsSummary.get
+ 'nb_visits',
+ 'nb_uniq_visitors',
+ 'avg_time_on_site',
+ 'bounce_rate',
+ 'nb_actions_per_visit',
+ 'max_actions',
+ 'nb_visits_converted',
+ // columns from Actions.get
+ 'nb_pageviews',
+ 'nb_uniq_pageviews',
+ 'nb_downloads',
+ 'nb_uniq_downloads',
+ 'nb_outlinks',
+ 'nb_uniq_outlinks'
+ );
+
+ $idSite = Piwik_Common::getRequestVar('idSite');
+ $displaySiteSearch = Piwik_Site::isSiteSearchEnabledFor($idSite);
+
+ if ($displaySiteSearch) {
+ $selectableColumns[] = 'nb_searches';
+ $selectableColumns[] = 'nb_keywords';
+ }
+ $view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, __FUNCTION__, $columns,
+ $selectableColumns, $documentation);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ static public function getVisitsSummary()
+ {
+ $requestString = "method=VisitsSummary.get" .
+ "&format=original" .
+ // we disable filters for example "search for pattern", in the case this method is called
+ // by a method that already calls the API with some generic filters applied
+ "&disable_generic_filters=1";
+ $request = new Piwik_API_Request($requestString);
+ $result = $request->process();
+ return empty($result) ? new Piwik_DataTable() : $result;
+ }
+
+ static public function getVisits()
+ {
+ $requestString = "method=VisitsSummary.getVisits" .
+ "&format=original" .
+ "&disable_generic_filters=1";
+ $request = new Piwik_API_Request($requestString);
+ return $request->process();
+ }
+
+ protected function setSparklinesAndNumbers($view)
+ {
+ $view->urlSparklineNbVisits = $this->getUrlSparkline('getEvolutionGraph', array('columns' => $view->displayUniqueVisitors ? array('nb_visits', 'nb_uniq_visitors') : array('nb_visits')));
+ $view->urlSparklineNbPageviews = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_pageviews', 'nb_uniq_pageviews')));
+ $view->urlSparklineNbDownloads = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_downloads', 'nb_uniq_downloads')));
+ $view->urlSparklineNbOutlinks = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_outlinks', 'nb_uniq_outlinks')));
+ $view->urlSparklineAvgVisitDuration = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_time_on_site')));
+ $view->urlSparklineMaxActions = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('max_actions')));
+ $view->urlSparklineActionsPerVisit = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_actions_per_visit')));
+ $view->urlSparklineBounceRate = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('bounce_rate')));
+
+ $idSite = Piwik_Common::getRequestVar('idSite');
+ $displaySiteSearch = Piwik_Site::isSiteSearchEnabledFor($idSite);
+ if ($displaySiteSearch) {
+ $view->urlSparklineNbSearches = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_searches', 'nb_keywords')));
+ }
+ $view->displaySiteSearch = $displaySiteSearch;
+
+ $dataTableVisit = self::getVisitsSummary();
+ $dataRow = $dataTableVisit->getRowsCount() == 0 ? new Piwik_DataTable_Row() : $dataTableVisit->getFirstRow();
+
+ $dataTableActions = Piwik_Actions_API::getInstance()->get($idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), Piwik_Common::getRequestVar('segment', false));
+ $dataActionsRow =
+ $dataTableActions->getRowsCount() == 0 ? new Piwik_DataTable_Row() : $dataTableActions->getFirstRow();
+
+ $view->nbUniqVisitors = (int)$dataRow->getColumn('nb_uniq_visitors');
+ $nbVisits = (int)$dataRow->getColumn('nb_visits');
+ $view->nbVisits = $nbVisits;
+ $view->nbPageviews = (int)$dataActionsRow->getColumn('nb_pageviews');
+ $view->nbUniquePageviews = (int)$dataActionsRow->getColumn('nb_uniq_pageviews');
+ $view->nbDownloads = (int)$dataActionsRow->getColumn('nb_downloads');
+ $view->nbUniqueDownloads = (int)$dataActionsRow->getColumn('nb_uniq_downloads');
+ $view->nbOutlinks = (int)$dataActionsRow->getColumn('nb_outlinks');
+ $view->nbUniqueOutlinks = (int)$dataActionsRow->getColumn('nb_uniq_outlinks');
+ $view->averageVisitDuration = $dataRow->getColumn('avg_time_on_site');
+ $nbBouncedVisits = $dataRow->getColumn('bounce_count');
+ $view->bounceRate = Piwik::getPercentageSafe($nbBouncedVisits, $nbVisits);
+ $view->maxActions = (int)$dataRow->getColumn('max_actions');
+ $view->nbActionsPerVisit = $dataRow->getColumn('nb_actions_per_visit');
+
+ if ($displaySiteSearch) {
+ $view->nbSearches = (int)$dataActionsRow->getColumn('nb_searches');
+ $view->nbKeywords = (int)$dataActionsRow->getColumn('nb_keywords');
+ }
+
+ // backward compatibility:
+ // show actions if the finer metrics are not archived
+ $view->showOnlyActions = false;
+ if ($dataActionsRow->getColumn('nb_pageviews')
+ + $dataActionsRow->getColumn('nb_downloads')
+ + $dataActionsRow->getColumn('nb_outlinks') == 0
+ && $dataRow->getColumn('nb_actions') > 0
+ ) {
+ $view->showOnlyActions = true;
+ $view->nbActions = $dataRow->getColumn('nb_actions');
+ $view->urlSparklineNbActions = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_actions')));
+ }
+ }
}
diff --git a/plugins/VisitsSummary/VisitsSummary.php b/plugins/VisitsSummary/VisitsSummary.php
index 816472455b..87b320c947 100644
--- a/plugins/VisitsSummary/VisitsSummary.php
+++ b/plugins/VisitsSummary/VisitsSummary.php
@@ -19,66 +19,66 @@
*/
class Piwik_VisitsSummary extends Piwik_Plugin
{
- public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('VisitsSummary_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- return $info;
- }
-
- function getListHooksRegistered()
- {
- return array(
- 'API.getReportMetadata' => 'getReportMetadata',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenu',
- );
- }
+ public function getInformation()
+ {
+ $info = array(
+ 'description' => Piwik_Translate('VisitsSummary_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ return $info;
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $reports = &$notification->getNotificationObject();
- $reports[] = array(
- 'category' => Piwik_Translate('VisitsSummary_VisitsSummary'),
- 'name' => Piwik_Translate('VisitsSummary_VisitsSummary'),
- 'module' => 'VisitsSummary',
- 'action' => 'get',
- 'metrics' => array(
- 'nb_uniq_visitors',
- 'nb_visits',
- 'nb_actions',
- 'nb_actions_per_visit',
- 'bounce_rate',
- 'avg_time_on_site' => Piwik_Translate('General_VisitDuration'),
- 'max_actions' => Piwik_Translate('General_ColumnMaxActions'),
+ function getListHooksRegistered()
+ {
+ return array(
+ 'API.getReportMetadata' => 'getReportMetadata',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenu',
+ );
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $reports = & $notification->getNotificationObject();
+ $reports[] = array(
+ 'category' => Piwik_Translate('VisitsSummary_VisitsSummary'),
+ 'name' => Piwik_Translate('VisitsSummary_VisitsSummary'),
+ 'module' => 'VisitsSummary',
+ 'action' => 'get',
+ 'metrics' => array(
+ 'nb_uniq_visitors',
+ 'nb_visits',
+ 'nb_actions',
+ 'nb_actions_per_visit',
+ 'bounce_rate',
+ 'avg_time_on_site' => Piwik_Translate('General_VisitDuration'),
+ 'max_actions' => Piwik_Translate('General_ColumnMaxActions'),
// Used to process metrics, not displayed/used directly
// 'sum_visit_length',
// 'nb_visits_converted',
- ),
- 'processedMetrics' => false,
- 'order' => 1
- );
- }
-
- function addWidgets()
- {
- Piwik_AddWidget( 'VisitsSummary_VisitsSummary', 'VisitsSummary_WidgetLastVisits', 'VisitsSummary', 'getEvolutionGraph', array('columns' => array('nb_visits')));
- Piwik_AddWidget( 'VisitsSummary_VisitsSummary', 'VisitsSummary_WidgetVisits', 'VisitsSummary', 'getSparklines');
- Piwik_AddWidget( 'VisitsSummary_VisitsSummary', 'VisitsSummary_WidgetOverviewGraph', 'VisitsSummary', 'index');
- }
-
- function addMenu()
- {
- Piwik_AddMenu('General_Visitors', '', array('module' => 'VisitsSummary', 'action' => 'index'), true, 10);
- Piwik_AddMenu('General_Visitors', 'VisitsSummary_SubmenuOverview', array('module' => 'VisitsSummary', 'action' => 'index'), true, 1);
- }
+ ),
+ 'processedMetrics' => false,
+ 'order' => 1
+ );
+ }
+
+ function addWidgets()
+ {
+ Piwik_AddWidget('VisitsSummary_VisitsSummary', 'VisitsSummary_WidgetLastVisits', 'VisitsSummary', 'getEvolutionGraph', array('columns' => array('nb_visits')));
+ Piwik_AddWidget('VisitsSummary_VisitsSummary', 'VisitsSummary_WidgetVisits', 'VisitsSummary', 'getSparklines');
+ Piwik_AddWidget('VisitsSummary_VisitsSummary', 'VisitsSummary_WidgetOverviewGraph', 'VisitsSummary', 'index');
+ }
+
+ function addMenu()
+ {
+ Piwik_AddMenu('General_Visitors', '', array('module' => 'VisitsSummary', 'action' => 'index'), true, 10);
+ Piwik_AddMenu('General_Visitors', 'VisitsSummary_SubmenuOverview', array('module' => 'VisitsSummary', 'action' => 'index'), true, 1);
+ }
}
diff --git a/plugins/VisitsSummary/templates/sparklines.tpl b/plugins/VisitsSummary/templates/sparklines.tpl
index 907e58e918..a0302f407a 100644
--- a/plugins/VisitsSummary/templates/sparklines.tpl
+++ b/plugins/VisitsSummary/templates/sparklines.tpl
@@ -1,58 +1,58 @@
<div id='leftcolumn'>
- <div class="sparkline">
- {sparkline src=$urlSparklineNbVisits}
- {'VisitsSummary_NbVisits'|translate:"<strong>$nbVisits</strong>"}{if $displayUniqueVisitors},
- {'VisitsSummary_NbUniqueVisitors'|translate:"<strong>$nbUniqVisitors</strong>"}{/if}
- </div>
- <div class="sparkline">
- {sparkline src=$urlSparklineAvgVisitDuration}
- {assign var=averageVisitDuration value=$averageVisitDuration|sumtime}
- {'VisitsSummary_AverageVisitDuration'|translate:"<strong>$averageVisitDuration</strong>"}
- </div>
- <div class="sparkline">
- {sparkline src=$urlSparklineBounceRate}
- {'VisitsSummary_NbVisitsBounced'|translate:"<strong>$bounceRate%</strong>"}
- </div>
- <div class="sparkline">
- {sparkline src=$urlSparklineActionsPerVisit}
- {'VisitsSummary_NbActionsPerVisit'|translate:"<strong>$nbActionsPerVisit</strong>"}
- </div>
+ <div class="sparkline">
+ {sparkline src=$urlSparklineNbVisits}
+ {'VisitsSummary_NbVisits'|translate:"<strong>$nbVisits</strong>"}{if $displayUniqueVisitors},
+ {'VisitsSummary_NbUniqueVisitors'|translate:"<strong>$nbUniqVisitors</strong>"}{/if}
+ </div>
+ <div class="sparkline">
+ {sparkline src=$urlSparklineAvgVisitDuration}
+ {assign var=averageVisitDuration value=$averageVisitDuration|sumtime}
+ {'VisitsSummary_AverageVisitDuration'|translate:"<strong>$averageVisitDuration</strong>"}
+ </div>
+ <div class="sparkline">
+ {sparkline src=$urlSparklineBounceRate}
+ {'VisitsSummary_NbVisitsBounced'|translate:"<strong>$bounceRate%</strong>"}
+ </div>
+ <div class="sparkline">
+ {sparkline src=$urlSparklineActionsPerVisit}
+ {'VisitsSummary_NbActionsPerVisit'|translate:"<strong>$nbActionsPerVisit</strong>"}
+ </div>
</div>
<div id='rightcolumn'>
- {if $showOnlyActions}
- <div class="sparkline">
- {sparkline src=$urlSparklineNbActions}
- {'VisitsSummary_NbActionsDescription'|translate:"<strong>$nbActions</strong>"}
- </div>
- {else}
- <div class="sparkline">
- {sparkline src=$urlSparklineNbPageviews}
- {'VisitsSummary_NbPageviewsDescription'|translate:"<strong>$nbPageviews</strong>"|trim},
- {'VisitsSummary_NbUniquePageviewsDescription'|translate:"<strong>$nbUniquePageviews</strong>"}
- </div>
- {if $displaySiteSearch}
- <div class="sparkline">
- {sparkline src=$urlSparklineNbSearches}
- {'VisitsSummary_NbSearchesDescription'|translate:"<strong>$nbSearches</strong>"|trim},
- {'VisitsSummary_NbKeywordsDescription'|translate:"<strong>$nbKeywords</strong>"}
- </div>
- {/if}
- <div class="sparkline">
- {sparkline src=$urlSparklineNbDownloads}
- {'VisitsSummary_NbDownloadsDescription'|translate:"<strong>$nbDownloads</strong>"|trim},
- {'VisitsSummary_NbUniqueDownloadsDescription'|translate:"<strong>$nbUniqueDownloads</strong>"}
- </div>
- <div class="sparkline">
- {sparkline src=$urlSparklineNbOutlinks}
- {'VisitsSummary_NbOutlinksDescription'|translate:"<strong>$nbOutlinks</strong>"|trim},
- {'VisitsSummary_NbUniqueOutlinksDescription'|translate:"<strong>$nbUniqueOutlinks</strong>"}
- </div>
- {/if}
- <div class="sparkline">
- {sparkline src=$urlSparklineMaxActions}
- {'VisitsSummary_MaxNbActions'|translate:"<strong>$maxActions</strong>"}
- </div>
+ {if $showOnlyActions}
+ <div class="sparkline">
+ {sparkline src=$urlSparklineNbActions}
+ {'VisitsSummary_NbActionsDescription'|translate:"<strong>$nbActions</strong>"}
+ </div>
+ {else}
+ <div class="sparkline">
+ {sparkline src=$urlSparklineNbPageviews}
+ {'VisitsSummary_NbPageviewsDescription'|translate:"<strong>$nbPageviews</strong>"|trim},
+ {'VisitsSummary_NbUniquePageviewsDescription'|translate:"<strong>$nbUniquePageviews</strong>"}
+ </div>
+ {if $displaySiteSearch}
+ <div class="sparkline">
+ {sparkline src=$urlSparklineNbSearches}
+ {'VisitsSummary_NbSearchesDescription'|translate:"<strong>$nbSearches</strong>"|trim},
+ {'VisitsSummary_NbKeywordsDescription'|translate:"<strong>$nbKeywords</strong>"}
+ </div>
+ {/if}
+ <div class="sparkline">
+ {sparkline src=$urlSparklineNbDownloads}
+ {'VisitsSummary_NbDownloadsDescription'|translate:"<strong>$nbDownloads</strong>"|trim},
+ {'VisitsSummary_NbUniqueDownloadsDescription'|translate:"<strong>$nbUniqueDownloads</strong>"}
+ </div>
+ <div class="sparkline">
+ {sparkline src=$urlSparklineNbOutlinks}
+ {'VisitsSummary_NbOutlinksDescription'|translate:"<strong>$nbOutlinks</strong>"|trim},
+ {'VisitsSummary_NbUniqueOutlinksDescription'|translate:"<strong>$nbUniqueOutlinks</strong>"}
+ </div>
+ {/if}
+ <div class="sparkline">
+ {sparkline src=$urlSparklineMaxActions}
+ {'VisitsSummary_MaxNbActions'|translate:"<strong>$maxActions</strong>"}
+ </div>
</div>
<div style="clear:both;"></div>
diff --git a/plugins/Widgetize/Controller.php b/plugins/Widgetize/Controller.php
index cf084197b1..6441a84b48 100644
--- a/plugins/Widgetize/Controller.php
+++ b/plugins/Widgetize/Controller.php
@@ -1,77 +1,77 @@
<?php
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Widgetize
*/
/**
- *
+ *
* @package Piwik_Widgetize
*/
class Piwik_Widgetize_Controller extends Piwik_Controller
{
- function index()
- {
- $view = Piwik_View::factory('index');
- $view->availableWidgets = Piwik_Common::json_encode(Piwik_GetWidgetsList());
- $this->setGeneralVariablesView($view);
- echo $view->render();
- }
+ function index()
+ {
+ $view = Piwik_View::factory('index');
+ $view->availableWidgets = Piwik_Common::json_encode(Piwik_GetWidgetsList());
+ $this->setGeneralVariablesView($view);
+ echo $view->render();
+ }
- function testJsInclude1()
- {
- $view = Piwik_View::factory('test_jsinclude');
- $view->url1 = '?module=Widgetize&action=js&moduleToWidgetize=UserSettings&actionToWidgetize=getBrowser&idSite=1&period=day&date=yesterday';
- $view->url2 = '?module=Widgetize&action=js&moduleToWidgetize=API&actionToWidgetize=index&method=ExamplePlugin.getGoldenRatio&format=original';
- echo $view->render();
- }
-
- function testJsInclude2()
- {
- $view = Piwik_View::factory('test_jsinclude2');
- $view->url1 = '?module=Widgetize&action=js&moduleToWidgetize=UserSettings&actionToWidgetize=getBrowser&idSite=1&period=day&date=yesterday';
- $view->url2 = '?module=Widgetize&action=js&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry&idSite=1&period=day&date=yesterday&viewDataTable=cloud&show_footer=0';
- $view->url3 = '?module=Widgetize&action=js&moduleToWidgetize=Referers&actionToWidgetize=getKeywords&idSite=1&period=day&date=yesterday&viewDataTable=table&show_footer=0';
- echo $view->render();
- }
-
- /**
- * Disabled for now, not obvious that this is useful (iframe sounds like a better solution)
- */
- private function js()
- {
- Piwik_API_Request::reloadAuthUsingTokenAuth();
- $controllerName = Piwik_Common::getRequestVar('moduleToWidgetize');
- $actionName = Piwik_Common::getRequestVar('actionToWidgetize');
- $parameters = array ( $fetch = true );
- $content = Piwik_FrontController::getInstance()->fetchDispatch( $controllerName, $actionName, $parameters);
- $view = Piwik_View::factory('js');
- $content = str_replace(array("\t","\n","\r\n","\r"), "", $content);
- $view->content = $content;
- echo $view->render();
- }
+ function testJsInclude1()
+ {
+ $view = Piwik_View::factory('test_jsinclude');
+ $view->url1 = '?module=Widgetize&action=js&moduleToWidgetize=UserSettings&actionToWidgetize=getBrowser&idSite=1&period=day&date=yesterday';
+ $view->url2 = '?module=Widgetize&action=js&moduleToWidgetize=API&actionToWidgetize=index&method=ExamplePlugin.getGoldenRatio&format=original';
+ echo $view->render();
+ }
- function iframe()
- {
- Piwik_API_Request::reloadAuthUsingTokenAuth();
- $this->init();
- $controllerName = Piwik_Common::getRequestVar('moduleToWidgetize');
- $actionName = Piwik_Common::getRequestVar('actionToWidgetize');
- $parameters = array ( $fetch = true );
- $outputDataTable = Piwik_FrontController::getInstance()->fetchDispatch( $controllerName, $actionName, $parameters);
- if($controllerName == 'Dashboard' && $actionName == 'index') {
- $view = Piwik_View::factory('empty');
- } else {
- $view = Piwik_View::factory('iframe');
- }
- $this->setGeneralVariablesView($view);
- $view->setXFrameOptions('allow');
- $view->content = $outputDataTable;
- echo $view->render();
- }
+ function testJsInclude2()
+ {
+ $view = Piwik_View::factory('test_jsinclude2');
+ $view->url1 = '?module=Widgetize&action=js&moduleToWidgetize=UserSettings&actionToWidgetize=getBrowser&idSite=1&period=day&date=yesterday';
+ $view->url2 = '?module=Widgetize&action=js&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry&idSite=1&period=day&date=yesterday&viewDataTable=cloud&show_footer=0';
+ $view->url3 = '?module=Widgetize&action=js&moduleToWidgetize=Referers&actionToWidgetize=getKeywords&idSite=1&period=day&date=yesterday&viewDataTable=table&show_footer=0';
+ echo $view->render();
+ }
+
+ /**
+ * Disabled for now, not obvious that this is useful (iframe sounds like a better solution)
+ */
+ private function js()
+ {
+ Piwik_API_Request::reloadAuthUsingTokenAuth();
+ $controllerName = Piwik_Common::getRequestVar('moduleToWidgetize');
+ $actionName = Piwik_Common::getRequestVar('actionToWidgetize');
+ $parameters = array($fetch = true);
+ $content = Piwik_FrontController::getInstance()->fetchDispatch($controllerName, $actionName, $parameters);
+ $view = Piwik_View::factory('js');
+ $content = str_replace(array("\t", "\n", "\r\n", "\r"), "", $content);
+ $view->content = $content;
+ echo $view->render();
+ }
+
+ function iframe()
+ {
+ Piwik_API_Request::reloadAuthUsingTokenAuth();
+ $this->init();
+ $controllerName = Piwik_Common::getRequestVar('moduleToWidgetize');
+ $actionName = Piwik_Common::getRequestVar('actionToWidgetize');
+ $parameters = array($fetch = true);
+ $outputDataTable = Piwik_FrontController::getInstance()->fetchDispatch($controllerName, $actionName, $parameters);
+ if ($controllerName == 'Dashboard' && $actionName == 'index') {
+ $view = Piwik_View::factory('empty');
+ } else {
+ $view = Piwik_View::factory('iframe');
+ }
+ $this->setGeneralVariablesView($view);
+ $view->setXFrameOptions('allow');
+ $view->content = $outputDataTable;
+ echo $view->render();
+ }
}
diff --git a/plugins/Widgetize/Widgetize.php b/plugins/Widgetize/Widgetize.php
index 556e46c396..0201cfd9ef 100644
--- a/plugins/Widgetize/Widgetize.php
+++ b/plugins/Widgetize/Widgetize.php
@@ -2,74 +2,74 @@
/**
* Piwik - Open source web analytics
- *
+ *
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
+ *
* @category Piwik_Plugins
* @package Piwik_Widgetize
*/
/**
- *
+ *
* @package Piwik_Widgetize
*/
-class Piwik_Widgetize extends Piwik_Plugin
+class Piwik_Widgetize extends Piwik_Plugin
{
- public function getInformation()
- {
- return array(
- 'description' => Piwik_Translate('Widgetize_PluginDescription'),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- );
- }
-
- public function getListHooksRegistered()
- {
- return array(
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'TopMenu.add' => 'addTopMenu',
- );
- }
-
- public function addTopMenu()
- {
- $tooltip = Piwik_Translate('Widgetize_TopLinkTooltip');
- $urlParams = array('module' => 'Widgetize', 'action' => 'index');
-
- Piwik_AddTopMenu('General_Widgets', $urlParams, true, 5, $isHTML = false, $tooltip);
- }
+ public function getInformation()
+ {
+ return array(
+ 'description' => Piwik_Translate('Widgetize_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles($notification)
- {
- $jsFiles = &$notification->getNotificationObject();
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'TopMenu.add' => 'addTopMenu',
+ );
+ }
- $jsFiles[] = "libs/jquery/jquery.tooltip.js";
- $jsFiles[] = "libs/jquery/jquery.truncate.js";
- $jsFiles[] = "libs/jquery/jquery.scrollTo.js";
- $jsFiles[] = "themes/default/common.js";
- $jsFiles[] = "plugins/CoreHome/templates/datatable.js";
- $jsFiles[] = "plugins/Dashboard/templates/widgetMenu.js";
- $jsFiles[] = "plugins/Widgetize/templates/widgetize.js";
- }
+ public function addTopMenu()
+ {
+ $tooltip = Piwik_Translate('Widgetize_TopLinkTooltip');
+ $urlParams = array('module' => 'Widgetize', 'action' => 'index');
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles($notification)
- {
- $cssFiles = &$notification->getNotificationObject();
-
- $cssFiles[] = "plugins/CoreHome/templates/styles.css";
- $cssFiles[] = "plugins/CoreHome/templates/datatable.css";
- $cssFiles[] = "plugins/CoreHome/templates/cloud.css";
- $cssFiles[] = "plugins/Dashboard/templates/dashboard.css";
- }
+ Piwik_AddTopMenu('General_Widgets', $urlParams, true, 5, $isHTML = false, $tooltip);
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+
+ $jsFiles[] = "libs/jquery/jquery.tooltip.js";
+ $jsFiles[] = "libs/jquery/jquery.truncate.js";
+ $jsFiles[] = "libs/jquery/jquery.scrollTo.js";
+ $jsFiles[] = "themes/default/common.js";
+ $jsFiles[] = "plugins/CoreHome/templates/datatable.js";
+ $jsFiles[] = "plugins/Dashboard/templates/widgetMenu.js";
+ $jsFiles[] = "plugins/Widgetize/templates/widgetize.js";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+
+ $cssFiles[] = "plugins/CoreHome/templates/styles.css";
+ $cssFiles[] = "plugins/CoreHome/templates/datatable.css";
+ $cssFiles[] = "plugins/CoreHome/templates/cloud.css";
+ $cssFiles[] = "plugins/Dashboard/templates/dashboard.css";
+ }
}
diff --git a/plugins/Widgetize/templates/iframe.tpl b/plugins/Widgetize/templates/iframe.tpl
index 9ca106f5ef..eb8287098a 100644
--- a/plugins/Widgetize/templates/iframe.tpl
+++ b/plugins/Widgetize/templates/iframe.tpl
@@ -1,22 +1,22 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-{loadJavascriptTranslations plugins='CoreHome'}
-{include file="CoreHome/templates/js_global_variables.tpl"}
-<!--[if lt IE 9]>
-<script language="javascript" type="text/javascript" src="libs/jqplot/excanvas.min.js"></script>
-<![endif]-->
-{include file="CoreHome/templates/js_css_includes.tpl"}
-<!--[if IE]>
-<link rel="stylesheet" type="text/css" href="themes/default/ieonly.css" />
-<![endif]-->
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ {loadJavascriptTranslations plugins='CoreHome'}
+ {include file="CoreHome/templates/js_global_variables.tpl"}
+ <!--[if lt IE 9]>
+ <script language="javascript" type="text/javascript" src="libs/jqplot/excanvas.min.js"></script>
+ <![endif]-->
+ {include file="CoreHome/templates/js_css_includes.tpl"}
+ <!--[if IE]>
+ <link rel="stylesheet" type="text/css" href="themes/default/ieonly.css"/>
+ <![endif]-->
</head>
<body>
<div class="widget">
-{$content}
+ {$content}
</div>
</body>
diff --git a/plugins/Widgetize/templates/index.tpl b/plugins/Widgetize/templates/index.tpl
index 854e4e92da..d29684958f 100644
--- a/plugins/Widgetize/templates/index.tpl
+++ b/plugins/Widgetize/templates/index.tpl
@@ -6,71 +6,74 @@
{literal}
-<style type="text/css">
-.widgetize{
- width:100%;
- padding:15px 15px 0 15px;
- font-size:13px;
-}
-.widgetize p{
- padding: 0 0 20px 0;
-}
-.menu {
- display: inline;
-}
-.widgetize .formEmbedCode{
- font-size: 11px;
- text-decoration: none;
- background-color: #FBFDFF;
- border: 1px solid #ECECEC;
- width:220px;
-}
-
-#periodString {
- margin-left:15px;
-}
-
-.widgetize label {
- color:#666666;
- line-height:18px;
- margin-right:5px;
- font-weight:bold;
- padding-bottom:100px;
-}
-
-#embedThisWidgetIframe,
-#embedThisWidgetFlash,
-#embedThisWidgetEverywhere {
- margin-top:5px;
-}
-
-.menuSelected{
- font-weight:bold;
-}
-</style>
+ <style type="text/css">
+ .widgetize {
+ width: 100%;
+ padding: 15px 15px 0 15px;
+ font-size: 13px;
+ }
+
+ .widgetize p {
+ padding: 0 0 20px 0;
+ }
+
+ .menu {
+ display: inline;
+ }
+
+ .widgetize .formEmbedCode {
+ font-size: 11px;
+ text-decoration: none;
+ background-color: #FBFDFF;
+ border: 1px solid #ECECEC;
+ width: 220px;
+ }
+
+ #periodString {
+ margin-left: 15px;
+ }
+
+ .widgetize label {
+ color: #666666;
+ line-height: 18px;
+ margin-right: 5px;
+ font-weight: bold;
+ padding-bottom: 100px;
+ }
+
+ #embedThisWidgetIframe,
+ #embedThisWidgetFlash,
+ #embedThisWidgetEverywhere {
+ margin-top: 5px;
+ }
+
+ .menuSelected {
+ font-weight: bold;
+ }
+ </style>
{/literal}
<script type="text/javascript">
-{literal}
-$(document).ready( function() {
- var widgetized = new widgetize();
- var urlPath = document.location.protocol + '//' + document.location.hostname + (document.location.port == '' ? '' : (':' + document.location.port)) + document.location.pathname ;
- var dashboardUrl = urlPath + '?module=Widgetize&action=iframe&moduleToWidgetize=Dashboard&actionToWidgetize=index&idSite='+piwik.idSite+'&period=week&date=yesterday';
- $('#exportFullDashboard').html(
- widgetized.getInputFormWithHtml( 'dashboardEmbed', '<iframe src="'+ dashboardUrl +'" frameborder="0" marginheight="0" marginwidth="0" width="100%" height="100%"></iframe>')
- );
- $('#linkDashboardUrl').attr('href',dashboardUrl);
-
- var allWebsitesDashboardUrl = urlPath + '?module=Widgetize&action=iframe&moduleToWidgetize=MultiSites&actionToWidgetize=standalone&idSite='+piwik.idSite+'&period=week&date=yesterday';
- $('#exportAllWebsitesDashboard').html(
- widgetized.getInputFormWithHtml( 'allWebsitesDashboardEmbed', '<iframe src="'+ allWebsitesDashboardUrl +'" frameborder="0" marginheight="0" marginwidth="0" width="100%" height="100%"></iframe>')
- );
- $('#linkAllWebsitesDashboardUrl').attr('href',allWebsitesDashboardUrl);
- $('#widgetPreview').widgetPreview({
- onPreviewLoaded: widgetized.callbackAddExportButtonsUnderWidget
+ {literal}
+ $(document).ready(function () {
+ var widgetized = new widgetize();
+ var urlPath = document.location.protocol + '//' + document.location.hostname + (document.location.port == '' ? '' : (':' + document.location.port)) + document.location.pathname;
+ var dashboardUrl = urlPath + '?module=Widgetize&action=iframe&moduleToWidgetize=Dashboard&actionToWidgetize=index&idSite=' + piwik.idSite + '&period=week&date=yesterday';
+ $('#exportFullDashboard').html(
+ widgetized.getInputFormWithHtml('dashboardEmbed', '<iframe src="' + dashboardUrl + '" frameborder="0" marginheight="0" marginwidth="0" width="100%" height="100%"></iframe>')
+ );
+ $('#linkDashboardUrl').attr('href', dashboardUrl);
+
+ var allWebsitesDashboardUrl = urlPath + '?module=Widgetize&action=iframe&moduleToWidgetize=MultiSites&actionToWidgetize=standalone&idSite=' + piwik.idSite + '&period=week&date=yesterday';
+ $('#exportAllWebsitesDashboard').html(
+ widgetized.getInputFormWithHtml('allWebsitesDashboardEmbed', '<iframe src="' + allWebsitesDashboardUrl + '" frameborder="0" marginheight="0" marginwidth="0" width="100%" height="100%"></iframe>')
+ );
+ $('#linkAllWebsitesDashboardUrl').attr('href', allWebsitesDashboardUrl);
+ $('#widgetPreview').widgetPreview({
+ onPreviewLoaded: widgetized.callbackAddExportButtonsUnderWidget
+ });
});
-});
-{/literal}
+ {/literal}
</script>
<div class="top_controls_inner">
@@ -78,24 +81,34 @@ $(document).ready( function() {
</div>
<div class="widgetize">
- <p>With Piwik, you can export your Web Analytics reports on your blog, website, or intranet dashboard... in one click.
- <p><b>&rsaquo; Widget authentication:</b> If you want your widgets to be viewable by everybody, you first have to set the 'view' permissions
- to the anonymous user in the <a href='index.php?module=UsersManager' target='_blank'>Users Management section</a>.
- <br />Alternatively, if you are publishing widgets on a password protected or private page,
- you don't necessarily have to allow 'anonymous' to view your reports. In this case, you can add the secret token_auth parameter (found in the <a href='{url module=API action=listAllAPI}' target='_blank'>API page</a>) in the widget URL.
- </p>
- <p><b>&rsaquo; Widgetize the full dashboard:</b> You can also display the full Piwik dashboard in your application or website in an IFRAME (<a href='' target='_blank' id='linkDashboardUrl'>see example</a>).
- The date parameter can be set to a specific calendar date, "today", or "yesterday". The period parameter can be set to "day", "week", "month", or "year".
- The language parameter can be set to the language code of a translation, such as language=fr.
- For example, for idSite=1 and date=yesterday, you can write: <span id='exportFullDashboard'></span>
- </p>
- <p><b>&rsaquo; Widgetize the all websites dashboard in an IFRAME</b> (<a href='' target='_blank' id='linkAllWebsitesDashboardUrl'>see example</a>) <span id='exportAllWebsitesDashboard'></span>
- </p>
- <p> <b>&rsaquo; Select a report, and copy paste in your page the embed code below the widget:</b>
+ <p>With Piwik, you can export your Web Analytics reports on your blog, website, or intranet dashboard... in one click.
+
+ <p><b>&rsaquo; Widget authentication:</b> If you want your widgets to be viewable by everybody, you first have to set the 'view' permissions
+ to the anonymous user in the <a href='index.php?module=UsersManager' target='_blank'>Users Management section</a>.
+ <br/>Alternatively, if you are publishing widgets on a password protected or private page,
+ you don't necessarily have to allow 'anonymous' to view your reports. In this case, you can add the secret token_auth parameter (found in the <a
+ href='{url module=API action=listAllAPI}' target='_blank'>API page</a>) in the widget URL.
+ </p>
+
+ <p><b>&rsaquo; Widgetize the full dashboard:</b> You can also display the full Piwik dashboard in your application or website in an IFRAME (<a href=''
+ target='_blank'
+ id='linkDashboardUrl'>see
+ example</a>).
+ The date parameter can be set to a specific calendar date, "today", or "yesterday". The period parameter can be set to "day", "week", "month", or
+ "year".
+ The language parameter can be set to the language code of a translation, such as language=fr.
+ For example, for idSite=1 and date=yesterday, you can write: <span id='exportFullDashboard'></span>
+ </p>
+
+ <p><b>&rsaquo; Widgetize the all websites dashboard in an IFRAME</b> (<a href='' target='_blank' id='linkAllWebsitesDashboardUrl'>see example</a>) <span
+ id='exportAllWebsitesDashboard'></span>
+ </p>
+
+ <p><b>&rsaquo; Select a report, and copy paste in your page the embed code below the widget:</b>
<div id="widgetPreview"></div>
- <div id='iframeDivToExport' style='display:none;'></div>
+ <div id='iframeDivToExport' style='display:none;'></div>
</div>
diff --git a/plugins/Widgetize/templates/js.tpl b/plugins/Widgetize/templates/js.tpl
index 8c81a44bf1..8976875413 100644
--- a/plugins/Widgetize/templates/js.tpl
+++ b/plugins/Widgetize/templates/js.tpl
@@ -1,18 +1,30 @@
{loadJavascriptTranslations disableOutputScriptTag=1 plugins='CoreHome'}
-document.write('<link rel="stylesheet" type="text/css" href="{$piwikUrl}themes/default/common.css" />');
-document.write('<link rel="stylesheet" type="text/css" href="{$piwikUrl}plugins/CoreHome/templates/styles.css" />');
-document.write('<link rel="stylesheet" type="text/css" href="{$piwikUrl}plugins/CoreHome/templates/datatable.css" />');
-document.write('<link rel="stylesheet" type="text/css" href="{$piwikUrl}plugins/CoreHome/templates/cloud.css" />');
+document.write('
+<link rel="stylesheet" type="text/css" href="{$piwikUrl}themes/default/common.css"/>');
+document.write('
+<link rel="stylesheet" type="text/css" href="{$piwikUrl}plugins/CoreHome/templates/styles.css"/>');
+document.write('
+<link rel="stylesheet" type="text/css" href="{$piwikUrl}plugins/CoreHome/templates/datatable.css"/>');
+document.write('
+<link rel="stylesheet" type="text/css" href="{$piwikUrl}plugins/CoreHome/templates/cloud.css"/>');
-document.write('<scr'+'ipt type="text/javascript" src="{$piwikUrl}libs/jquery/jquery.js"></scr'+'ipt>');
-document.write('<scr'+'ipt type="text/javascript" src="{$piwikUrl}libs/jquery/jquery-ui.js"></scr'+'ipt>');
-document.write('<scr'+'ipt type="text/javascript" src="{$piwikUrl}libs/jquery/jquery.tooltip.js"></scr'+'ipt>');
-document.write('<scr'+'ipt type="text/javascript" src="{$piwikUrl}libs/jquery/jquery.truncate.js"></scr'+'ipt>');
-document.write('<scr'+'ipt type="text/javascript" src="{$piwikUrl}libs/javascript/sprintf.js"></scr'+'ipt>');
-document.write('<scr'+'ipt type="text/javascript" src="{$piwikUrl}themes/default/common.js"></scr'+'ipt>');
-document.write('<scr'+'ipt type="text/javascript" src="{$piwikUrl}plugins/CoreHome/templates/datatable.js"></scr'+'ipt>');
+document.write('
+<scr'+'ipt type="text/javascript" src="{$piwikUrl}libs/jquery/jquery.js"></scr'+'ipt>');
+document.write('
+<scr'+'ipt type="text/javascript" src="{$piwikUrl}libs/jquery/jquery-ui.js"></scr'+'ipt>');
+document.write('
+<scr'+'ipt type="text/javascript" src="{$piwikUrl}libs/jquery/jquery.tooltip.js"></scr'+'ipt>');
+document.write('
+<scr'+'ipt type="text/javascript" src="{$piwikUrl}libs/jquery/jquery.truncate.js"></scr'+'ipt>');
+document.write('
+<scr'+'ipt type="text/javascript" src="{$piwikUrl}libs/javascript/sprintf.js"></scr'+'ipt>');
+document.write('
+<scr'+'ipt type="text/javascript" src="{$piwikUrl}themes/default/common.js"></scr'+'ipt>');
+document.write('
+<scr'+'ipt type="text/javascript" src="{$piwikUrl}plugins/CoreHome/templates/datatable.js"></scr'+'ipt>');
var content = '{$content|escape:'javascript'}';
-document.write('<scr'+'ipt type="text/javascript">document.write(content)</scr'+'ipt>');
+document.write('
+<scr'+'ipt type="text/javascript">document.write(content)</scr'+'ipt>');
diff --git a/plugins/Widgetize/templates/test_jsinclude.tpl b/plugins/Widgetize/templates/test_jsinclude.tpl
index b2416acee3..10800a14c2 100644
--- a/plugins/Widgetize/templates/test_jsinclude.tpl
+++ b/plugins/Widgetize/templates/test_jsinclude.tpl
@@ -6,12 +6,15 @@
<h2>Test tag cloud in a JS include</h2>
<div style="width:500px">
-<script type="text/javascript" src="{$url1}"></script>
-<noscript>Powered by <a href="http://piwik.org">Piwik</a></div></noscript>
+ <script type="text/javascript" src="{$url1}"></script>
+ <noscript>Powered by <a href="http://piwik.org">Piwik</a>
+</div>
+</noscript>
</div>
<p>This text is after the JS INCLUDE</p>
<h2>Test calling the API in Javascript</h2>
+
<p>using a javascript include. This is a dirty way (much better to ajax call the API!) but an interesting show case.</P>
<script type="text/javascript" src="{$url2}"></script>
<noscript>Powered by <a href="http://piwik.org">Piwik</a></div></noscript>
diff --git a/plugins/Widgetize/templates/widgetize.js b/plugins/Widgetize/templates/widgetize.js
index ec8595eabf..e54210642f 100644
--- a/plugins/Widgetize/templates/widgetize.js
+++ b/plugins/Widgetize/templates/widgetize.js
@@ -5,84 +5,78 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function widgetize()
-{
- var self = this;
-
- this.getInputFormWithHtml = function(inputId, htmlEmbed)
- {
- return '<input class="formEmbedCode" id="'+inputId+'" value="'+ htmlEmbed.replace(/"/g, '&quot;') +'" onclick="javascript:document.getElementById(\''+inputId+'\').focus();document.getElementById(\''+inputId+'\').select();" readonly="true" type="text" />';
- }
-
- this.getEmbedUrl = function( parameters, exportFormat )
- {
- copyParameters = {};
- for(var variableName in parameters) {
- copyParameters[variableName] = parameters[variableName];
- }
- copyParameters['moduleToWidgetize'] = parameters['module'];
- copyParameters['actionToWidgetize'] = parameters['action'];
- delete copyParameters['action'];
- delete copyParameters['module'];
- var sourceUrl;
- sourceUrl = document.location.protocol + '//' + document.location.hostname + (document.location.port == '' ? '' : (':' + document.location.port)) + document.location.pathname + '?';
- sourceUrl += "module=Widgetize" +
- "&action="+exportFormat+
- "&"+piwikHelper.getQueryStringFromParameters(copyParameters)+
- "&idSite="+piwik.idSite+
- "&period="+piwik.period+
- "&date="+broadcast.getValueFromUrl('date')+
- "&disableLink=1&widget=1";
- return sourceUrl;
- }
-
- this.htmlentities = function(s)
- {
- return s.replace( /[<>&]/g, function(m) { return "&" + m.charCodeAt(0) + ";"; });
- }
-
- this.callbackAddExportButtonsUnderWidget = function ( widgetUniqueId,
- loadedWidgetElement)
- {
- widget = widgetsHelper.getWidgetObjectFromUniqueId(widgetUniqueId);
- widgetName = widget["name"];
- widgetParameters = widget['parameters'];
-
- var exportButtonsElement = $('<span id="exportButtons">');
+function widgetize() {
+ var self = this;
- var urlIframe = self.getEmbedUrl(widgetParameters, "iframe");
- // We first build the HTML code that will load the widget in an IFRAME
- var widgetIframeHtml = '<div id="widgetIframe">'+
- '<iframe width="100%" height="350" src="'+
- urlIframe +
- '" scrolling="no" frameborder="0" marginheight="0" marginwidth="0">'+
- '</iframe>'+
- '</div>';
+ this.getInputFormWithHtml = function (inputId, htmlEmbed) {
+ return '<input class="formEmbedCode" id="' + inputId + '" value="' + htmlEmbed.replace(/"/g, '&quot;') + '" onclick="javascript:document.getElementById(\'' + inputId + '\').focus();document.getElementById(\'' + inputId + '\').select();" readonly="true" type="text" />';
+ }
- // Add the input field containing the widget in an Iframe
- $(exportButtonsElement).append(
- '<div id="embedThisWidgetIframe">'+
- '<label for="embedThisWidgetIframeInput">&rsaquo; Embed Iframe</label>'+
- '<span id="embedThisWidgetIframeInput">'+
- self.getInputFormWithHtml('iframeEmbed', widgetIframeHtml)+
- '</span>'+
- '</div>' +
- '<div> <label for="embedThisWidgetDirectLink">&rsaquo; Direct Link</label>'+
- '<span id="embedThisWidgetDirectLink"> '+self.getInputFormWithHtml('directLinkEmbed', urlIframe)+' - <a href="'+urlIframe+'" target="_blank">'+_pk_translate('General_OpenInNewWindow_js')+'</a></span>'
- +'</div>'
- );
-
- // We then replace the div iframeDivToExport with the actual Iframe html
- $('#iframeDivToExport')
- .html(widgetIframeHtml);
+ this.getEmbedUrl = function (parameters, exportFormat) {
+ copyParameters = {};
+ for (var variableName in parameters) {
+ copyParameters[variableName] = parameters[variableName];
+ }
+ copyParameters['moduleToWidgetize'] = parameters['module'];
+ copyParameters['actionToWidgetize'] = parameters['action'];
+ delete copyParameters['action'];
+ delete copyParameters['module'];
+ var sourceUrl;
+ sourceUrl = document.location.protocol + '//' + document.location.hostname + (document.location.port == '' ? '' : (':' + document.location.port)) + document.location.pathname + '?';
+ sourceUrl += "module=Widgetize" +
+ "&action=" + exportFormat +
+ "&" + piwikHelper.getQueryStringFromParameters(copyParameters) +
+ "&idSite=" + piwik.idSite +
+ "&period=" + piwik.period +
+ "&date=" + broadcast.getValueFromUrl('date') +
+ "&disableLink=1&widget=1";
+ return sourceUrl;
+ }
- // Finally we append the content to the parent widget DIV
- $(loadedWidgetElement)
- .parent()
- .append(exportButtonsElement);
-
- // JS is buggy at least on IE
- //var widgetJS = '<script type="text/javascript" src="'+ getEmbedUrl(pluginId, actionId, "js") +'"></scr'+'ipt>';
- //divEmbedThisWidget.append('<br />Embed JS: '+ getInputFormWithHtml('javascriptEmbed', widgetJS));
- }
+ this.htmlentities = function (s) {
+ return s.replace(/[<>&]/g, function (m) { return "&" + m.charCodeAt(0) + ";"; });
+ }
+
+ this.callbackAddExportButtonsUnderWidget = function (widgetUniqueId, loadedWidgetElement) {
+ widget = widgetsHelper.getWidgetObjectFromUniqueId(widgetUniqueId);
+ widgetName = widget["name"];
+ widgetParameters = widget['parameters'];
+
+ var exportButtonsElement = $('<span id="exportButtons">');
+
+ var urlIframe = self.getEmbedUrl(widgetParameters, "iframe");
+ // We first build the HTML code that will load the widget in an IFRAME
+ var widgetIframeHtml = '<div id="widgetIframe">' +
+ '<iframe width="100%" height="350" src="' +
+ urlIframe +
+ '" scrolling="no" frameborder="0" marginheight="0" marginwidth="0">' +
+ '</iframe>' +
+ '</div>';
+
+ // Add the input field containing the widget in an Iframe
+ $(exportButtonsElement).append(
+ '<div id="embedThisWidgetIframe">' +
+ '<label for="embedThisWidgetIframeInput">&rsaquo; Embed Iframe</label>' +
+ '<span id="embedThisWidgetIframeInput">' +
+ self.getInputFormWithHtml('iframeEmbed', widgetIframeHtml) +
+ '</span>' +
+ '</div>' +
+ '<div> <label for="embedThisWidgetDirectLink">&rsaquo; Direct Link</label>' +
+ '<span id="embedThisWidgetDirectLink"> ' + self.getInputFormWithHtml('directLinkEmbed', urlIframe) + ' - <a href="' + urlIframe + '" target="_blank">' + _pk_translate('General_OpenInNewWindow_js') + '</a></span>'
+ + '</div>'
+ );
+
+ // We then replace the div iframeDivToExport with the actual Iframe html
+ $('#iframeDivToExport')
+ .html(widgetIframeHtml);
+
+ // Finally we append the content to the parent widget DIV
+ $(loadedWidgetElement)
+ .parent()
+ .append(exportButtonsElement);
+
+ // JS is buggy at least on IE
+ //var widgetJS = '<script type="text/javascript" src="'+ getEmbedUrl(pluginId, actionId, "js") +'"></scr'+'ipt>';
+ //divEmbedThisWidget.append('<br />Embed JS: '+ getInputFormWithHtml('javascriptEmbed', widgetJS));
+ }
}
diff --git a/tests/LocalTracker.php b/tests/LocalTracker.php
index b4d56cfff5..3afd96b452 100755
--- a/tests/LocalTracker.php
+++ b/tests/LocalTracker.php
@@ -2,123 +2,115 @@
$GLOBALS['PIWIK_TRACKER_DEBUG'] = false;
$GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS'] = false;
-if (!defined('PIWIK_ENABLE_TRACKING'))
-{
- define('PIWIK_ENABLE_TRACKING', true);
+if (!defined('PIWIK_ENABLE_TRACKING')) {
+ define('PIWIK_ENABLE_TRACKING', true);
}
-require_once PIWIK_INCLUDE_PATH.'/core/Tracker.php';
-require_once PIWIK_INCLUDE_PATH.'/core/Tracker/Db.php';
-require_once PIWIK_INCLUDE_PATH.'/core/Tracker/IgnoreCookie.php';
-require_once PIWIK_INCLUDE_PATH.'/core/Tracker/Visit.php';
-require_once PIWIK_INCLUDE_PATH.'/core/Tracker/GoalManager.php';
-require_once PIWIK_INCLUDE_PATH.'/core/Tracker/Action.php';
-require_once PIWIK_INCLUDE_PATH.'/libs/PiwikTracker/PiwikTracker.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Db.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/IgnoreCookie.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Visit.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/GoalManager.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Action.php';
+require_once PIWIK_INCLUDE_PATH . '/libs/PiwikTracker/PiwikTracker.php';
/**
* Tracker that uses core/Tracker.php directly.
*/
class Piwik_LocalTracker extends PiwikTracker
{
- protected function sendRequest( $url, $method = 'GET', $data = null, $force = false )
- {
- // if doing a bulk request, store the url
- if ($this->doBulkRequests && !$force)
- {
- $this->storedTrackingActions[] = $url;
- return true;
- }
-
- if ($method == 'POST')
- {
- $requests = array();
- foreach ($this->storedTrackingActions as $action)
- {
- $requests[] = $this->parseUrl($action);
- }
-
- $testEnvironmentArgs = array();
- }
- else
- {
- $testEnvironmentArgs = $this->parseUrl($url);
- $requests = array($testEnvironmentArgs);
- }
-
- // unset cached values
- Piwik_Tracker_Cache::$trackerCache = null;
- Piwik_Tracker::setForceIp(null);
- Piwik_Tracker::setForceDateTime(null);
- Piwik_Tracker::setForceVisitorId(null);
-
- // save some values
- $plugins = Piwik_Config::getInstance()->Plugins['Plugins'];
- $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'];
- $oldTrackerConfig = Piwik_Config::getInstance()->Tracker;
- Piwik_PluginsManager::getInstance()->unloadPlugins();
-
- // modify config
- $GLOBALS['PIWIK_TRACKER_MODE'] = true;
- $GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING'] = true;
- Piwik_Tracker::$initTrackerMode = false;
- Piwik_Tracker::setTestEnvironment($testEnvironmentArgs, $method);
-
- // set language
- $oldLang = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '';
- $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $this->acceptLanguage;
-
- // set user agent
- $oldUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
- $_SERVER['HTTP_USER_AGENT'] = $this->userAgent;
-
- // set cookie
- $oldCookie = $_COOKIE;
- parse_str(parse_url($this->requestCookie, PHP_URL_QUERY), $_COOKIE);
-
- // do tracking and capture output
- ob_start();
-
- $localTracker = new Piwik_Tracker();
- $localTracker->main($requests);
-
- $output = ob_get_contents();
-
- ob_end_clean();
-
- // restore vars
- Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'] = $pluginsTracker;
- Piwik_Config::getInstance()->Tracker = $oldTrackerConfig;
- $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $oldLang;
- $_SERVER['HTTP_USER_AGENT'] = $oldUserAgent;
- $_COOKIE = $oldCookie;
- $GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING'] = false;
- $GLOBALS['PIWIK_TRACKER_MODE'] = false;
- unset($_GET['bots']);
-
- // reload plugins
- Piwik_PluginsManager::getInstance()->loadPlugins($plugins);
-
- return $output;
- }
-
- private function parseUrl( $url )
- {
- // parse url
- $query = parse_url($url, PHP_URL_QUERY);
- if ($query === false)
- {
- return;
- }
-
- parse_str($query, $args);
-
- // make sure bots is set if needed
- if (isset($args['bots']))
- {
- $_GET['bots'] = true;
- }
-
- return $args;
- }
+ protected function sendRequest($url, $method = 'GET', $data = null, $force = false)
+ {
+ // if doing a bulk request, store the url
+ if ($this->doBulkRequests && !$force) {
+ $this->storedTrackingActions[] = $url;
+ return true;
+ }
+
+ if ($method == 'POST') {
+ $requests = array();
+ foreach ($this->storedTrackingActions as $action) {
+ $requests[] = $this->parseUrl($action);
+ }
+
+ $testEnvironmentArgs = array();
+ } else {
+ $testEnvironmentArgs = $this->parseUrl($url);
+ $requests = array($testEnvironmentArgs);
+ }
+
+ // unset cached values
+ Piwik_Tracker_Cache::$trackerCache = null;
+ Piwik_Tracker::setForceIp(null);
+ Piwik_Tracker::setForceDateTime(null);
+ Piwik_Tracker::setForceVisitorId(null);
+
+ // save some values
+ $plugins = Piwik_Config::getInstance()->Plugins['Plugins'];
+ $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'];
+ $oldTrackerConfig = Piwik_Config::getInstance()->Tracker;
+ Piwik_PluginsManager::getInstance()->unloadPlugins();
+
+ // modify config
+ $GLOBALS['PIWIK_TRACKER_MODE'] = true;
+ $GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING'] = true;
+ Piwik_Tracker::$initTrackerMode = false;
+ Piwik_Tracker::setTestEnvironment($testEnvironmentArgs, $method);
+
+ // set language
+ $oldLang = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '';
+ $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $this->acceptLanguage;
+
+ // set user agent
+ $oldUserAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
+ $_SERVER['HTTP_USER_AGENT'] = $this->userAgent;
+
+ // set cookie
+ $oldCookie = $_COOKIE;
+ parse_str(parse_url($this->requestCookie, PHP_URL_QUERY), $_COOKIE);
+
+ // do tracking and capture output
+ ob_start();
+
+ $localTracker = new Piwik_Tracker();
+ $localTracker->main($requests);
+
+ $output = ob_get_contents();
+
+ ob_end_clean();
+
+ // restore vars
+ Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'] = $pluginsTracker;
+ Piwik_Config::getInstance()->Tracker = $oldTrackerConfig;
+ $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $oldLang;
+ $_SERVER['HTTP_USER_AGENT'] = $oldUserAgent;
+ $_COOKIE = $oldCookie;
+ $GLOBALS['PIWIK_TRACKER_LOCAL_TRACKING'] = false;
+ $GLOBALS['PIWIK_TRACKER_MODE'] = false;
+ unset($_GET['bots']);
+
+ // reload plugins
+ Piwik_PluginsManager::getInstance()->loadPlugins($plugins);
+
+ return $output;
+ }
+
+ private function parseUrl($url)
+ {
+ // parse url
+ $query = parse_url($url, PHP_URL_QUERY);
+ if ($query === false) {
+ return;
+ }
+
+ parse_str($query, $args);
+
+ // make sure bots is set if needed
+ if (isset($args['bots'])) {
+ $_GET['bots'] = true;
+ }
+
+ return $args;
+ }
}
diff --git a/tests/PHPUnit/BaseFixture.php b/tests/PHPUnit/BaseFixture.php
index 9c7f202b27..9fc5946944 100644
--- a/tests/PHPUnit/BaseFixture.php
+++ b/tests/PHPUnit/BaseFixture.php
@@ -10,43 +10,43 @@
* Base type for all integration test fixtures. Integration test fixtures
* add visit and related data to the database before a test is run. Different
* tests can use the same fixtures.
- *
+ *
* This class defines a set of helper methods for fixture types. The helper
* methods are public, but ideally they should only be used by fixture types.
- *
+ *
* NOTE: YOU SHOULD NOT CREATE A NEW FIXTURE UNLESS THERE IS NO WAY TO MODIFY
* AN EXISTING FIXTURE TO HANDLE YOUR USE CASE.
- *
+ *
* Related TODO: we should try and reduce the amount of existing fixtures by
* merging some together.
*/
abstract class Test_Piwik_BaseFixture extends PHPUnit_Framework_Assert
{
- /** Adds data to Piwik. Creates sites, tracks visits, imports log files, etc. */
- public abstract function setUp();
-
- /** Does any clean up. Most of the time there will be no need to clean up. */
- public abstract function tearDown();
-
+ /** Adds data to Piwik. Creates sites, tracks visits, imports log files, etc. */
+ public abstract function setUp();
+
+ /** Does any clean up. Most of the time there will be no need to clean up. */
+ public abstract function tearDown();
+
/**
* Creates a website, then sets its creation date to a day earlier than specified dateTime
* Useful to create a website now, but force data to be archived back in the past.
*
- * @param string $dateTime eg '2010-01-01 12:34:56'
- * @param int $ecommerce
- * @param string $siteName
+ * @param string $dateTime eg '2010-01-01 12:34:56'
+ * @param int $ecommerce
+ * @param string $siteName
*
* @return int idSite of website created
*/
- public static function createWebsite( $dateTime, $ecommerce = 0, $siteName = 'Piwik test', $siteUrl = false,
- $siteSearch = 1, $searchKeywordParameters = null,
- $searchCategoryParameters = null )
+ public static function createWebsite($dateTime, $ecommerce = 0, $siteName = 'Piwik test', $siteUrl = false,
+ $siteSearch = 1, $searchKeywordParameters = null,
+ $searchCategoryParameters = null)
{
$idSite = Piwik_SitesManager_API::getInstance()->addSite(
$siteName,
$siteUrl === false ? "http://piwik.net/" : $siteUrl,
$ecommerce,
- $siteSearch , $searchKeywordParameters, $searchCategoryParameters,
+ $siteSearch, $searchKeywordParameters, $searchCategoryParameters,
$ips = null,
$excludedQueryParameters = null,
$timezone = null,
@@ -65,39 +65,38 @@ abstract class Test_Piwik_BaseFixture extends PHPUnit_Framework_Assert
return $idSite;
}
- /**
- * Returns URL to Piwik root.
- *
- * @return string
- */
- public static function getRootUrl()
- {
- $piwikUrl = Piwik_Url::getCurrentUrlWithoutFileName();
-
- $pathBeforeRoot = 'tests';
- // Running from a plugin
- if(strpos($piwikUrl, 'plugins/') !== false)
- {
- $pathBeforeRoot = 'plugins';
- }
-
- $testsInPath = strpos($piwikUrl, $pathBeforeRoot.'/');
- if($testsInPath !== false) {
- $piwikUrl = substr($piwikUrl, 0, $testsInPath);
- }
- return $piwikUrl;
- }
-
- /**
- * Returns URL to the proxy script, used to ensure piwik.php
- * uses the test environment, and allows variable overwriting
- *
- * @return string
- */
- public static function getTrackerUrl()
- {
- return self::getRootUrl().'tests/PHPUnit/proxy/piwik.php';
- }
+ /**
+ * Returns URL to Piwik root.
+ *
+ * @return string
+ */
+ public static function getRootUrl()
+ {
+ $piwikUrl = Piwik_Url::getCurrentUrlWithoutFileName();
+
+ $pathBeforeRoot = 'tests';
+ // Running from a plugin
+ if (strpos($piwikUrl, 'plugins/') !== false) {
+ $pathBeforeRoot = 'plugins';
+ }
+
+ $testsInPath = strpos($piwikUrl, $pathBeforeRoot . '/');
+ if ($testsInPath !== false) {
+ $piwikUrl = substr($piwikUrl, 0, $testsInPath);
+ }
+ return $piwikUrl;
+ }
+
+ /**
+ * Returns URL to the proxy script, used to ensure piwik.php
+ * uses the test environment, and allows variable overwriting
+ *
+ * @return string
+ */
+ public static function getTrackerUrl()
+ {
+ return self::getRootUrl() . 'tests/PHPUnit/proxy/piwik.php';
+ }
/**
* Returns a PiwikTracker object that you can then use to track pages or goals.
@@ -108,35 +107,31 @@ abstract class Test_Piwik_BaseFixture extends PHPUnit_Framework_Assert
*
* @return PiwikTracker
*/
- public static function getTracker($idSite, $dateTime, $defaultInit = true, $useLocal = false )
+ public static function getTracker($idSite, $dateTime, $defaultInit = true, $useLocal = false)
{
- if ($useLocal)
- {
- require_once PIWIK_INCLUDE_PATH . '/tests/LocalTracker.php';
- $t = new Piwik_LocalTracker($idSite, self::getTrackerUrl());
- }
- else
- {
- $t = new PiwikTracker( $idSite, self::getTrackerUrl());
+ if ($useLocal) {
+ require_once PIWIK_INCLUDE_PATH . '/tests/LocalTracker.php';
+ $t = new Piwik_LocalTracker($idSite, self::getTrackerUrl());
+ } else {
+ $t = new PiwikTracker($idSite, self::getTrackerUrl());
}
$t->setForceVisitDateTime($dateTime);
- if($defaultInit)
- {
+ if ($defaultInit) {
$t->setIp('156.5.3.2');
// Optional tracking
- $t->setUserAgent( "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729)");
+ $t->setUserAgent("Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729)");
$t->setBrowserLanguage('fr');
- $t->setLocalTime( '12:34:06' );
- $t->setResolution( 1024, 768 );
+ $t->setLocalTime('12:34:06');
+ $t->setResolution(1024, 768);
$t->setBrowserHasCookies(true);
$t->setPlugins($flash = true, $java = true, $director = false);
}
return $t;
}
- /**
+ /**
* Checks that the response is a GIF image as expected.
* Will fail the test if the response is not the expected GIF
*
@@ -147,173 +142,167 @@ abstract class Test_Piwik_BaseFixture extends PHPUnit_Framework_Assert
$trans_gif_64 = "R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
$expectedResponse = base64_decode($trans_gif_64);
self::assertEquals($expectedResponse, $response, "Expected GIF beacon, got: <br/>\n"
- . var_export($response, true)
- . "\n If you are stuck, you can enable \$GLOBALS['PIWIK_TRACKER_DEBUG']=true; in piwik.php to get more debug info."
+ . var_export($response, true)
+ . "\n If you are stuck, you can enable \$GLOBALS['PIWIK_TRACKER_DEBUG']=true; in piwik.php to get more debug info."
. base64_encode($response)
);
}
-
- public static function makeLocation( $city, $region, $country, $lat = null, $long = null )
- {
- return array(Piwik_UserCountry_LocationProvider::CITY_NAME_KEY => $city,
- Piwik_UserCountry_LocationProvider::REGION_CODE_KEY => $region,
- Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY => $country,
- Piwik_UserCountry_LocationProvider::LATITUDE_KEY => $lat,
- Piwik_UserCountry_LocationProvider::LONGITUDE_KEY => $long);
- }
-
- /**
- * Returns the super user token auth that can be used in tests. Can be used to
- * do bulk tracking.
- *
- * @return string
- */
- public static function getTokenAuth()
- {
- return Piwik_UsersManager_API::getInstance()->getTokenAuth(
- Piwik_Config::getInstance()->superuser['login'],
- Piwik_Config::getInstance()->superuser['password']
- );
+
+ public static function makeLocation($city, $region, $country, $lat = null, $long = null)
+ {
+ return array(Piwik_UserCountry_LocationProvider::CITY_NAME_KEY => $city,
+ Piwik_UserCountry_LocationProvider::REGION_CODE_KEY => $region,
+ Piwik_UserCountry_LocationProvider::COUNTRY_CODE_KEY => $country,
+ Piwik_UserCountry_LocationProvider::LATITUDE_KEY => $lat,
+ Piwik_UserCountry_LocationProvider::LONGITUDE_KEY => $long);
+ }
+
+ /**
+ * Returns the super user token auth that can be used in tests. Can be used to
+ * do bulk tracking.
+ *
+ * @return string
+ */
+ public static function getTokenAuth()
+ {
+ return Piwik_UsersManager_API::getInstance()->getTokenAuth(
+ Piwik_Config::getInstance()->superuser['login'],
+ Piwik_Config::getInstance()->superuser['password']
+ );
+ }
+
+ /**
+ * Create one MAIL and two MOBILE scheduled reports
+ *
+ * Reports sent by mail can contain PNG graphs when the user specifies it.
+ * Depending on the system under test, generated images differ slightly.
+ * Because of this discrepancy, PNG graphs are only tested if the system under test
+ * has the characteristics described in 'canImagesBeIncludedInScheduledReports'
+ *
+ * @see canImagesBeIncludedInScheduledReports
+ * @param int $idSite id of website created
+ */
+ public static function setUpScheduledReports($idSite)
+ {
+ // fake access is needed so API methods can call Piwik::getCurrentUserLogin(), e.g: 'PDFReports.addReport'
+ $pseudoMockAccess = new FakeAccess;
+ FakeAccess::$superUser = true;
+ Zend_Registry::set('access', $pseudoMockAccess);
+
+ // retrieve available reports
+ $availableReportMetadata = Piwik_PDFReports_API::getReportMetadata($idSite, Piwik_PDFReports::EMAIL_TYPE);
+
+ $availableReportIds = array();
+ foreach ($availableReportMetadata as $reportMetadata) {
+ $availableReportIds[] = $reportMetadata['uniqueId'];
+ }
+
+ //@review should we also test evolution graphs?
+ // set-up mail report
+ Piwik_PDFReports_API::getInstance()->addReport(
+ $idSite,
+ 'Mail Test report',
+ 'day', // overridden in getApiForTestingScheduledReports()
+ 0,
+ Piwik_PDFReports::EMAIL_TYPE,
+ Piwik_ReportRenderer::HTML_FORMAT, // overridden in getApiForTestingScheduledReports()
+ $availableReportIds,
+ array("displayFormat" => Piwik_PDFReports::DISPLAY_FORMAT_TABLES_ONLY)
+ );
+
+ // set-up sms report for one website
+ Piwik_PDFReports_API::getInstance()->addReport(
+ $idSite,
+ 'SMS Test report, one website',
+ 'day', // overridden in getApiForTestingScheduledReports()
+ 0,
+ Piwik_MobileMessaging::MOBILE_TYPE,
+ Piwik_MobileMessaging::SMS_FORMAT,
+ array("MultiSites_getOne"),
+ array("phoneNumbers" => array())
+ );
+
+ // set-up sms report for all websites
+ Piwik_PDFReports_API::getInstance()->addReport(
+ $idSite,
+ 'SMS Test report, all websites',
+ 'day', // overridden in getApiForTestingScheduledReports()
+ 0,
+ Piwik_MobileMessaging::MOBILE_TYPE,
+ Piwik_MobileMessaging::SMS_FORMAT,
+ array("MultiSites_getAll"),
+ array("phoneNumbers" => array())
+ );
+
+ if (self::canImagesBeIncludedInScheduledReports()) {
+ // set-up mail report with images
+ Piwik_PDFReports_API::getInstance()->addReport(
+ $idSite,
+ 'Mail Test report',
+ 'day', // overridden in getApiForTestingScheduledReports()
+ 0,
+ Piwik_PDFReports::EMAIL_TYPE,
+ Piwik_ReportRenderer::HTML_FORMAT, // overridden in getApiForTestingScheduledReports()
+ $availableReportIds,
+ array("displayFormat" => Piwik_PDFReports::DISPLAY_FORMAT_TABLES_AND_GRAPHS)
+ );
+ }
+ }
+
+ /**
+ * Return true if system under test has the following characteristics :
+ * - php_uname() contains 'precise32' or 'ubuntu'
+ * - phpversion() contains '5.3.10'
+ * - 'GD Version' equals '2.0'
+ */
+ public static function canImagesBeIncludedInScheduledReports()
+ {
+ $gdInfo = gd_info();
+ return
+ (stristr(php_uname(), 'precise32') || stristr(php_uname(), 'ubuntu')) &&
+ stristr(phpversion(), '5.3.10') &&
+ $gdInfo['GD Version'] == '2.0';
+ }
+
+ public static $geoIpDbUrl = 'http://piwik-team.s3.amazonaws.com/GeoIP.dat.gz';
+ public static $geoLiteCityDbUrl = 'http://piwik-team.s3.amazonaws.com/GeoLiteCity.dat.gz';
+
+ public static function downloadGeoIpDbs()
+ {
+ $geoIpOutputDir = PIWIK_INCLUDE_PATH . '/tests/lib/geoip-files';
+ self::downloadAndUnzip(self::$geoIpDbUrl, $geoIpOutputDir, 'GeoIP.dat');
+ self::downloadAndUnzip(self::$geoLiteCityDbUrl, $geoIpOutputDir, 'GeoIPCity.dat');
+ }
+
+ public static function downloadAndUnzip($url, $outputDir, $filename)
+ {
+ $bufferSize = 1024 * 1024;
+
+ if (!is_dir($outputDir)) {
+ mkdir($outputDir);
+ }
+
+ $deflatedOut = $outputDir . '/' . $filename;
+ $outfileName = $deflatedOut . '.gz';
+
+ if (file_exists($deflatedOut)) {
+ return;
+ }
+
+ $dump = fopen($url, 'rb');
+ $outfile = fopen($outfileName, 'wb');
+ $bytesRead = 0;
+ while (!feof($dump)) {
+ fwrite($outfile, fread($dump, $bufferSize), $bufferSize);
+ $bytesRead += $bufferSize;
+ }
+ fclose($dump);
+ fclose($outfile);
+
+ // unzip the dump
+ exec("gunzip -c \"" . $outfileName . "\" > \"$deflatedOut\"", $output, $return);
+ if ($return !== 0) {
+ throw new Exception("gunzip failed($return): " . implode("\n", $output));
+ }
}
-
- /**
- * Create one MAIL and two MOBILE scheduled reports
- *
- * Reports sent by mail can contain PNG graphs when the user specifies it.
- * Depending on the system under test, generated images differ slightly.
- * Because of this discrepancy, PNG graphs are only tested if the system under test
- * has the characteristics described in 'canImagesBeIncludedInScheduledReports'
- *
- * @see canImagesBeIncludedInScheduledReports
- * @param int $idSite id of website created
- */
- public static function setUpScheduledReports($idSite)
- {
- // fake access is needed so API methods can call Piwik::getCurrentUserLogin(), e.g: 'PDFReports.addReport'
- $pseudoMockAccess = new FakeAccess;
- FakeAccess::$superUser = true;
- Zend_Registry::set('access', $pseudoMockAccess);
-
- // retrieve available reports
- $availableReportMetadata = Piwik_PDFReports_API::getReportMetadata($idSite, Piwik_PDFReports::EMAIL_TYPE);
-
- $availableReportIds = array();
- foreach($availableReportMetadata as $reportMetadata)
- {
- $availableReportIds[] = $reportMetadata['uniqueId'];
- }
-
- //@review should we also test evolution graphs?
- // set-up mail report
- Piwik_PDFReports_API::getInstance()->addReport(
- $idSite,
- 'Mail Test report',
- 'day', // overridden in getApiForTestingScheduledReports()
- 0,
- Piwik_PDFReports::EMAIL_TYPE,
- Piwik_ReportRenderer::HTML_FORMAT, // overridden in getApiForTestingScheduledReports()
- $availableReportIds,
- array("displayFormat" => Piwik_PDFReports::DISPLAY_FORMAT_TABLES_ONLY)
- );
-
- // set-up sms report for one website
- Piwik_PDFReports_API::getInstance()->addReport(
- $idSite,
- 'SMS Test report, one website',
- 'day', // overridden in getApiForTestingScheduledReports()
- 0,
- Piwik_MobileMessaging::MOBILE_TYPE,
- Piwik_MobileMessaging::SMS_FORMAT,
- array("MultiSites_getOne"),
- array("phoneNumbers"=>array())
- );
-
- // set-up sms report for all websites
- Piwik_PDFReports_API::getInstance()->addReport(
- $idSite,
- 'SMS Test report, all websites',
- 'day', // overridden in getApiForTestingScheduledReports()
- 0,
- Piwik_MobileMessaging::MOBILE_TYPE,
- Piwik_MobileMessaging::SMS_FORMAT,
- array("MultiSites_getAll"),
- array("phoneNumbers"=>array())
- );
-
- if (self::canImagesBeIncludedInScheduledReports())
- {
- // set-up mail report with images
- Piwik_PDFReports_API::getInstance()->addReport(
- $idSite,
- 'Mail Test report',
- 'day', // overridden in getApiForTestingScheduledReports()
- 0,
- Piwik_PDFReports::EMAIL_TYPE,
- Piwik_ReportRenderer::HTML_FORMAT, // overridden in getApiForTestingScheduledReports()
- $availableReportIds,
- array("displayFormat" => Piwik_PDFReports::DISPLAY_FORMAT_TABLES_AND_GRAPHS)
- );
- }
- }
-
- /**
- * Return true if system under test has the following characteristics :
- * - php_uname() contains 'precise32' or 'ubuntu'
- * - phpversion() contains '5.3.10'
- * - 'GD Version' equals '2.0'
- */
- public static function canImagesBeIncludedInScheduledReports()
- {
- $gdInfo = gd_info();
- return
- (stristr(php_uname(), 'precise32') || stristr(php_uname(), 'ubuntu')) &&
- stristr(phpversion(), '5.3.10') &&
- $gdInfo['GD Version'] == '2.0';
- }
-
- public static $geoIpDbUrl = 'http://piwik-team.s3.amazonaws.com/GeoIP.dat.gz';
- public static $geoLiteCityDbUrl = 'http://piwik-team.s3.amazonaws.com/GeoLiteCity.dat.gz';
-
- public static function downloadGeoIpDbs()
- {
- $geoIpOutputDir = PIWIK_INCLUDE_PATH.'/tests/lib/geoip-files';
- self::downloadAndUnzip(self::$geoIpDbUrl, $geoIpOutputDir, 'GeoIP.dat');
- self::downloadAndUnzip(self::$geoLiteCityDbUrl, $geoIpOutputDir, 'GeoIPCity.dat');
- }
-
- public static function downloadAndUnzip( $url, $outputDir, $filename )
- {
- $bufferSize = 1024 * 1024;
-
- if (!is_dir($outputDir))
- {
- mkdir($outputDir);
- }
-
- $deflatedOut = $outputDir.'/'.$filename;
- $outfileName = $deflatedOut.'.gz';
-
- if (file_exists($deflatedOut))
- {
- return;
- }
-
- $dump = fopen($url, 'rb');
- $outfile = fopen($outfileName, 'wb');
- $bytesRead = 0;
- while (!feof($dump))
- {
- fwrite($outfile, fread($dump, $bufferSize), $bufferSize);
- $bytesRead += $bufferSize;
- }
- fclose($dump);
- fclose($outfile);
-
- // unzip the dump
- exec("gunzip -c \"".$outfileName."\" > \"$deflatedOut\"", $output, $return);
- if ($return !== 0)
- {
- throw new Exception("gunzip failed($return): ".implode("\n", $output));
- }
- }
}
diff --git a/tests/PHPUnit/BenchmarkTestCase.php b/tests/PHPUnit/BenchmarkTestCase.php
index 5b958edd2a..ad1dfd379e 100755
--- a/tests/PHPUnit/BenchmarkTestCase.php
+++ b/tests/PHPUnit/BenchmarkTestCase.php
@@ -9,9 +9,8 @@ require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/IntegrationTestCase.php';
require_once PIWIK_INCLUDE_PATH . '/tests/LocalTracker.php';
// require fixtures
-foreach (glob(PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Benchmarks/Fixtures/*.php') as $file)
-{
- require_once $file;
+foreach (glob(PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Benchmarks/Fixtures/*.php') as $file) {
+ require_once $file;
}
/**
@@ -19,82 +18,73 @@ foreach (glob(PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Benchmarks/Fixtures/*.php') a
*/
abstract class BenchmarkTestCase extends IntegrationTestCase
{
- protected static $fixture;
-
- public static function setUpBeforeClass()
- {
- $dbName = false;
- if (!empty($GLOBALS['PIWIK_BENCHMARK_DATABASE']))
- {
- $dbName = $GLOBALS['PIWIK_BENCHMARK_DATABASE'];
- }
-
- // connect to database
- self::createTestConfig();
- self::connectWithoutDatabase();
-
- // create specified fixture (global var not set, use default no-data fixture (see end of this file))
- if (empty($GLOBALS['PIWIK_BENCHMARK_FIXTURE']))
- {
- $fixtureName = 'Piwik_Test_Fixture_EmptyOneSite';
- }
- else
- {
- $fixtureName = 'Piwik_Test_Fixture_'.$GLOBALS['PIWIK_BENCHMARK_FIXTURE'];
- }
- self::$fixture = new $fixtureName;
-
- // figure out if the desired fixture has already been setup, and if not empty the database
- $installedFixture = false;
- try
- {
- if (isset(self::$fixture->tablesPrefix))
- {
- Piwik_Config::getInstance()->database['tables_prefix'] = self::$fixture->tablesPrefix;
- Piwik_Common::$cachedTablePrefix = null;
- }
-
- Piwik_Query("USE ".$dbName);
- $installedFixture = Piwik_GetOption('benchmark_fixture_name');
- }
- catch (Exception $ex)
- {
- // ignore
- }
-
- $createEmptyDatabase = $fixtureName != $installedFixture;
- parent::_setUpBeforeClass($dbName, $createEmptyDatabase, $createConfig = false);
-
- // if we created an empty database, setup the fixture
- if ($createEmptyDatabase)
- {
- self::$fixture->setUp();
- Piwik_SetOption('benchmark_fixture_name', $fixtureName);
- }
- }
-
- public static function tearDownAfterClass()
- {
- // only drop the database if PIWIK_BENCHMARK_DATABASE isn't set
- $dropDatabase = empty($GLOBALS['PIWIK_BENCHMARK_DATABASE']);
- parent::_tearDownAfterClass($dropDatabase);
- }
-
- /**
- * Creates a tracking object that invokes the tracker directly (w/o going through HTTP).
- */
- public static function getLocalTracker( $idSite )
- {
- $t = new Piwik_LocalTracker($idSite, IntegrationTestCase::getTrackerUrl());
- $t->setUserAgent( "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729)");
- $t->setBrowserLanguage('fr');
- $t->setLocalTime('12:34:06');
- $t->setResolution(1024, 768);
- $t->setBrowserHasCookies(true);
- $t->setPlugins($flash = true, $java = true, $director = false);
- $t->setTokenAuth(self::getTokenAuth());
- return $t;
- }
+ protected static $fixture;
+
+ public static function setUpBeforeClass()
+ {
+ $dbName = false;
+ if (!empty($GLOBALS['PIWIK_BENCHMARK_DATABASE'])) {
+ $dbName = $GLOBALS['PIWIK_BENCHMARK_DATABASE'];
+ }
+
+ // connect to database
+ self::createTestConfig();
+ self::connectWithoutDatabase();
+
+ // create specified fixture (global var not set, use default no-data fixture (see end of this file))
+ if (empty($GLOBALS['PIWIK_BENCHMARK_FIXTURE'])) {
+ $fixtureName = 'Piwik_Test_Fixture_EmptyOneSite';
+ } else {
+ $fixtureName = 'Piwik_Test_Fixture_' . $GLOBALS['PIWIK_BENCHMARK_FIXTURE'];
+ }
+ self::$fixture = new $fixtureName;
+
+ // figure out if the desired fixture has already been setup, and if not empty the database
+ $installedFixture = false;
+ try {
+ if (isset(self::$fixture->tablesPrefix)) {
+ Piwik_Config::getInstance()->database['tables_prefix'] = self::$fixture->tablesPrefix;
+ Piwik_Common::$cachedTablePrefix = null;
+ }
+
+ Piwik_Query("USE " . $dbName);
+ $installedFixture = Piwik_GetOption('benchmark_fixture_name');
+ } catch (Exception $ex) {
+ // ignore
+ }
+
+ $createEmptyDatabase = $fixtureName != $installedFixture;
+ parent::_setUpBeforeClass($dbName, $createEmptyDatabase, $createConfig = false);
+
+ // if we created an empty database, setup the fixture
+ if ($createEmptyDatabase) {
+ self::$fixture->setUp();
+ Piwik_SetOption('benchmark_fixture_name', $fixtureName);
+ }
+ }
+
+ public static function tearDownAfterClass()
+ {
+ // only drop the database if PIWIK_BENCHMARK_DATABASE isn't set
+ $dropDatabase = empty($GLOBALS['PIWIK_BENCHMARK_DATABASE']);
+ parent::_tearDownAfterClass($dropDatabase);
+ }
+
+ /**
+ * Creates a tracking object that invokes the tracker directly (w/o going through HTTP).
+ */
+ public static function getLocalTracker($idSite)
+ {
+ $t = new Piwik_LocalTracker($idSite, IntegrationTestCase::getTrackerUrl());
+ $t->setUserAgent("Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729)");
+ $t->setBrowserLanguage('fr');
+ $t->setLocalTime('12:34:06');
+ $t->setResolution(1024, 768);
+ $t->setBrowserHasCookies(true);
+ $t->setPlugins($flash = true, $java = true, $director = false);
+ $t->setTokenAuth(self::getTokenAuth());
+ return $t;
+ }
}
/**
@@ -102,19 +92,19 @@ abstract class BenchmarkTestCase extends IntegrationTestCase
*/
class Piwik_Test_Fixture_EmptyOneSite
{
- public $date = '2010-01-01';
- public $period = 'day';
- public $idSite = 1;
-
- public function setUp()
- {
- // add one site
- IntegrationTestCase::createWebsite(
- $this->date, $ecommerce = 1, $siteName = "Site #0", $siteUrl = "http://whatever.com/");
-
- // add two goals
- $goals = Piwik_Goals_API::getInstance();
- $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5);
- $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains');
- }
+ public $date = '2010-01-01';
+ public $period = 'day';
+ public $idSite = 1;
+
+ public function setUp()
+ {
+ // add one site
+ IntegrationTestCase::createWebsite(
+ $this->date, $ecommerce = 1, $siteName = "Site #0", $siteUrl = "http://whatever.com/");
+
+ // add two goals
+ $goals = Piwik_Goals_API::getInstance();
+ $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5);
+ $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains');
+ }
}
diff --git a/tests/PHPUnit/Benchmarks/ArchivingProcessBenchmark.php b/tests/PHPUnit/Benchmarks/ArchivingProcessBenchmark.php
index 4cbfa0c1e3..b84afff74c 100755
--- a/tests/PHPUnit/Benchmarks/ArchivingProcessBenchmark.php
+++ b/tests/PHPUnit/Benchmarks/ArchivingProcessBenchmark.php
@@ -12,17 +12,17 @@ require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/BenchmarkTestCase.php';
*/
class ArchivingProcessBenchmark extends BenchmarkTestCase
{
- public function setUp()
- {
- BenchmarkTestCase::deleteArchiveTables();
- }
-
- /**
+ public function setUp()
+ {
+ BenchmarkTestCase::deleteArchiveTables();
+ }
+
+ /**
* @group Benchmarks
* @group ArchivingProcess
- */
- public function testArchivingProcess()
- {
- Piwik_VisitsSummary_API::get(self::$fixture->idSite, self::$fixture->period, self::$fixture->date);
- }
+ */
+ public function testArchivingProcess()
+ {
+ Piwik_VisitsSummary_API::get(self::$fixture->idSite, self::$fixture->period, self::$fixture->date);
+ }
}
diff --git a/tests/PHPUnit/Benchmarks/Fixtures/OneSiteTwelveThousandVisitsOneYear.php b/tests/PHPUnit/Benchmarks/Fixtures/OneSiteTwelveThousandVisitsOneYear.php
index d2a1b7d807..619b288fad 100755
--- a/tests/PHPUnit/Benchmarks/Fixtures/OneSiteTwelveThousandVisitsOneYear.php
+++ b/tests/PHPUnit/Benchmarks/Fixtures/OneSiteTwelveThousandVisitsOneYear.php
@@ -11,59 +11,53 @@
*/
class Piwik_Test_Fixture_OneSiteTwelveThousandVisitsOneYear
{
- public $date = '2010-01-01';
- public $period = 'year';
- public $idSite = 1;
- public $idGoal1 = 1;
- public $idGoal2 = 2;
-
- public function setUp()
- {
- // add one site
- IntegrationTestCase::createWebsite(
- $this->date, $ecommerce = 1, $siteName = "Site #0", $siteUrl = "http://whatever.com/");
-
- // add two goals
- $goals = Piwik_Goals_API::getInstance();
- $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5);
- $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains');
-
- $urls = array();
- for ($i = 0; $i != 3; ++$i)
- {
- $url = "http://whatever.com/".($i-1)."/".($i+1);
- $title = "page view ".($i-1)." / ".($i+1);
- $urls[$url] = $title;
- }
-
- $visitTimes = array();
- $date = Piwik_Date::factory($this->date);
- for ($month = 0; $month != 12; ++$month)
- {
- for ($day = 0; $day != 25; ++$day)
- {
- $hour = ($time * 31) / 60.0;
- $visitTimes[] = $date->addPeriod($month, 'MONTH')->addDay($day)->getDatetime();
- }
- }
-
- // add 12,000 visits (1 visit a day from 40 visitors for 25 days of every month) w/ 3 pageviews each
- foreach ($visitTimes as $visitTime)
- {
- for ($visitor = 0; $visitor != 40; ++$visitor)
- {
- $t = BenchmarkTestCase::getLocalTracker($this->idSite);
-
- $ip = "157.5.6.".($visitor+1);
- $t->setIp($ip);
- $t->setForceVisitDateTime($visitTime);
-
- foreach ($urls as $url => $title)
- {
- $t->setUrl($url);
- $t->doTrackPageView($title);
- }
- }
- }
- }
+ public $date = '2010-01-01';
+ public $period = 'year';
+ public $idSite = 1;
+ public $idGoal1 = 1;
+ public $idGoal2 = 2;
+
+ public function setUp()
+ {
+ // add one site
+ IntegrationTestCase::createWebsite(
+ $this->date, $ecommerce = 1, $siteName = "Site #0", $siteUrl = "http://whatever.com/");
+
+ // add two goals
+ $goals = Piwik_Goals_API::getInstance();
+ $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5);
+ $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains');
+
+ $urls = array();
+ for ($i = 0; $i != 3; ++$i) {
+ $url = "http://whatever.com/" . ($i - 1) . "/" . ($i + 1);
+ $title = "page view " . ($i - 1) . " / " . ($i + 1);
+ $urls[$url] = $title;
+ }
+
+ $visitTimes = array();
+ $date = Piwik_Date::factory($this->date);
+ for ($month = 0; $month != 12; ++$month) {
+ for ($day = 0; $day != 25; ++$day) {
+ $hour = ($time * 31) / 60.0;
+ $visitTimes[] = $date->addPeriod($month, 'MONTH')->addDay($day)->getDatetime();
+ }
+ }
+
+ // add 12,000 visits (1 visit a day from 40 visitors for 25 days of every month) w/ 3 pageviews each
+ foreach ($visitTimes as $visitTime) {
+ for ($visitor = 0; $visitor != 40; ++$visitor) {
+ $t = BenchmarkTestCase::getLocalTracker($this->idSite);
+
+ $ip = "157.5.6." . ($visitor + 1);
+ $t->setIp($ip);
+ $t->setForceVisitDateTime($visitTime);
+
+ foreach ($urls as $url => $title) {
+ $t->setUrl($url);
+ $t->doTrackPageView($title);
+ }
+ }
+ }
+ }
}
diff --git a/tests/PHPUnit/Benchmarks/Fixtures/SqlDump.php b/tests/PHPUnit/Benchmarks/Fixtures/SqlDump.php
index ed9c0544e8..488814877f 100755
--- a/tests/PHPUnit/Benchmarks/Fixtures/SqlDump.php
+++ b/tests/PHPUnit/Benchmarks/Fixtures/SqlDump.php
@@ -11,60 +11,57 @@
*/
class Piwik_Test_Fixture_SqlDump
{
- public static $dumpUrl = "http://piwik-team.s3.amazonaws.com/generated-logs-one-day.sql.gz";
+ public static $dumpUrl = "http://piwik-team.s3.amazonaws.com/generated-logs-one-day.sql.gz";
- public $date = '2012-09-03';
- public $period = 'day';
- public $idSite = 'all';
- public $tablesPrefix = 'piwik_';
-
- public function setUp()
- {
- $dumpPath = PIWIK_INCLUDE_PATH.'/tmp/logdump.sql.gz';
- $deflatedDumpPath = PIWIK_INCLUDE_PATH.'/tmp/logdump.sql';
- $bufferSize = 1024 * 1024;
-
- // drop all tables
- Piwik::dropTables();
-
- // download data dump
- $dump = fopen(self::$dumpUrl, 'rb');
- $outfile = fopen($dumpPath, 'wb');
- $bytesRead = 0;
- while (!feof($dump))
- {
- fwrite($outfile, fread($dump, $bufferSize), $bufferSize);
- $bytesRead += $bufferSize;
- }
- fclose($dump);
- fclose($outfile);
-
- if ($bytesRead <= 40 * 1024 * 1024) // sanity check
- {
- throw new Exception("Could not download sql dump!");
- }
-
- // unzip the dump
- exec("gunzip -c \"".$dumpPath."\" > \"$deflatedDumpPath\"", $output, $return);
- if ($return !== 0)
- {
- throw new Exception("gunzip failed: ".implode("\n", $output));
- }
-
- // load the data into the correct database
- $user = Piwik_Config::getInstance()->database['username'];
- $password = Piwik_Config::getInstance()->database['password'];
- $dbName = Piwik_Config::getInstance()->database['dbname'];
- Piwik_Config::getInstance()->database['tables_prefix'] = 'piwik_';
- Piwik_Common::$cachedTablePrefix = null;
-
- exec("mysql -u \"$user\" \"--password=$password\" $dbName < \"".$deflatedDumpPath."\" 2>&1", $output, $return);
- if ($return !== 0)
- {
- throw new Exception("Failed to load sql dump: ".implode("\n", $output));
- }
-
- // make sure archiving will be called
- Piwik_ArchiveProcessing::setBrowserTriggerArchiving(true);
- }
+ public $date = '2012-09-03';
+ public $period = 'day';
+ public $idSite = 'all';
+ public $tablesPrefix = 'piwik_';
+
+ public function setUp()
+ {
+ $dumpPath = PIWIK_INCLUDE_PATH . '/tmp/logdump.sql.gz';
+ $deflatedDumpPath = PIWIK_INCLUDE_PATH . '/tmp/logdump.sql';
+ $bufferSize = 1024 * 1024;
+
+ // drop all tables
+ Piwik::dropTables();
+
+ // download data dump
+ $dump = fopen(self::$dumpUrl, 'rb');
+ $outfile = fopen($dumpPath, 'wb');
+ $bytesRead = 0;
+ while (!feof($dump)) {
+ fwrite($outfile, fread($dump, $bufferSize), $bufferSize);
+ $bytesRead += $bufferSize;
+ }
+ fclose($dump);
+ fclose($outfile);
+
+ if ($bytesRead <= 40 * 1024 * 1024) // sanity check
+ {
+ throw new Exception("Could not download sql dump!");
+ }
+
+ // unzip the dump
+ exec("gunzip -c \"" . $dumpPath . "\" > \"$deflatedDumpPath\"", $output, $return);
+ if ($return !== 0) {
+ throw new Exception("gunzip failed: " . implode("\n", $output));
+ }
+
+ // load the data into the correct database
+ $user = Piwik_Config::getInstance()->database['username'];
+ $password = Piwik_Config::getInstance()->database['password'];
+ $dbName = Piwik_Config::getInstance()->database['dbname'];
+ Piwik_Config::getInstance()->database['tables_prefix'] = 'piwik_';
+ Piwik_Common::$cachedTablePrefix = null;
+
+ exec("mysql -u \"$user\" \"--password=$password\" $dbName < \"" . $deflatedDumpPath . "\" 2>&1", $output, $return);
+ if ($return !== 0) {
+ throw new Exception("Failed to load sql dump: " . implode("\n", $output));
+ }
+
+ // make sure archiving will be called
+ Piwik_ArchiveProcessing::setBrowserTriggerArchiving(true);
+ }
}
diff --git a/tests/PHPUnit/Benchmarks/Fixtures/ThousandSitesTwelveVisitsEachOneDay.php b/tests/PHPUnit/Benchmarks/Fixtures/ThousandSitesTwelveVisitsEachOneDay.php
index 9329ba9c5b..f42d87b6e4 100755
--- a/tests/PHPUnit/Benchmarks/Fixtures/ThousandSitesTwelveVisitsEachOneDay.php
+++ b/tests/PHPUnit/Benchmarks/Fixtures/ThousandSitesTwelveVisitsEachOneDay.php
@@ -11,74 +11,63 @@
*/
class Piwik_Test_Fixture_ThousandSitesTwelvePageViewsEachOneDay
{
- public $date = '2010-01-01';
- public $period = 'day';
- public $idSite = 'all';
-
- public function setUp()
- {
- $sitesManager = Piwik_SitesManager_API::getInstance();
- $goals = Piwik_Goals_API::getInstance();
-
- // add one thousand sites
- $allIdSites = array();
- for ($i = 0; $i < 1000; ++$i)
- {
- $allIdSites[] = IntegrationTestCase::createWebsite($this->date, $ecommerce = 1, $siteName = "Site #$i");
- }
-
- // add goals to 500 sites
- $idGoals = array();
- foreach ($allIdSites as $idSite)
- {
- if ($idSite % 2 == 0)
- {
- $idGoal1 = $goals->addGoal($idSite, 'all', 'url', 'http', 'contains', false, 5);
- $idGoal2 = $goals->addGoal($idSite, 'all', 'url', 'http', 'contains');
- $idGoals[$idSite] = array($idGoal1, $idGoal2);
- }
- else
- {
- $idGoals[$idSite] = array();
- }
- }
+ public $date = '2010-01-01';
+ public $period = 'day';
+ public $idSite = 'all';
- $urls = array();
- for ($i = 0; $i != 3; ++$i)
- {
- $url = "http://whatever.com/".($i-1)."/".($i+1);
- $title = "page view ".($i-1)." / ".($i+1);
- $urls[$url] = $title;
- }
+ public function setUp()
+ {
+ $sitesManager = Piwik_SitesManager_API::getInstance();
+ $goals = Piwik_Goals_API::getInstance();
- $visitTimes = array();
- $date = Piwik_Date::factory($this->date);
- for ($i = 0; $i != 4; ++$i)
- {
- $visitTimes[] = $date->addHour($i)->getDatetime();
- }
-
- // add 12000 visits (3 visitors with 4 visits each for each site) w/ 3 pageviews each on one day
- foreach ($visitTimes as $visitTime)
- {
- foreach ($allIdSites as $idSite)
- {
- for ($visitor = 0; $visitor != 3; ++$visitor)
- {
- $t = BenchmarkTestCase::getLocalTracker($this->idSite);
+ // add one thousand sites
+ $allIdSites = array();
+ for ($i = 0; $i < 1000; ++$i) {
+ $allIdSites[] = IntegrationTestCase::createWebsite($this->date, $ecommerce = 1, $siteName = "Site #$i");
+ }
- $ip = "157.5.6.".($visitor+1);
- $t->setIp($ip);
-
- $t->setForceVisitDateTime($visitTime);
- foreach ($urls as $url => $title)
- {
- $t->setUrl($url);
- $t->doTrackPageView($title);
- }
- }
- }
- }
- }
+ // add goals to 500 sites
+ $idGoals = array();
+ foreach ($allIdSites as $idSite) {
+ if ($idSite % 2 == 0) {
+ $idGoal1 = $goals->addGoal($idSite, 'all', 'url', 'http', 'contains', false, 5);
+ $idGoal2 = $goals->addGoal($idSite, 'all', 'url', 'http', 'contains');
+ $idGoals[$idSite] = array($idGoal1, $idGoal2);
+ } else {
+ $idGoals[$idSite] = array();
+ }
+ }
+
+ $urls = array();
+ for ($i = 0; $i != 3; ++$i) {
+ $url = "http://whatever.com/" . ($i - 1) . "/" . ($i + 1);
+ $title = "page view " . ($i - 1) . " / " . ($i + 1);
+ $urls[$url] = $title;
+ }
+
+ $visitTimes = array();
+ $date = Piwik_Date::factory($this->date);
+ for ($i = 0; $i != 4; ++$i) {
+ $visitTimes[] = $date->addHour($i)->getDatetime();
+ }
+
+ // add 12000 visits (3 visitors with 4 visits each for each site) w/ 3 pageviews each on one day
+ foreach ($visitTimes as $visitTime) {
+ foreach ($allIdSites as $idSite) {
+ for ($visitor = 0; $visitor != 3; ++$visitor) {
+ $t = BenchmarkTestCase::getLocalTracker($this->idSite);
+
+ $ip = "157.5.6." . ($visitor + 1);
+ $t->setIp($ip);
+
+ $t->setForceVisitDateTime($visitTime);
+ foreach ($urls as $url => $title) {
+ $t->setUrl($url);
+ $t->doTrackPageView($title);
+ }
+ }
+ }
+ }
+ }
}
diff --git a/tests/PHPUnit/Benchmarks/TrackerBenchmark.php b/tests/PHPUnit/Benchmarks/TrackerBenchmark.php
index 737dd13097..d92bd632a8 100755
--- a/tests/PHPUnit/Benchmarks/TrackerBenchmark.php
+++ b/tests/PHPUnit/Benchmarks/TrackerBenchmark.php
@@ -13,67 +13,60 @@ require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/BenchmarkTestCase.php';
*/
class TrackerBenchmark extends BenchmarkTestCase
{
- private $urls = array();
- private $pageTitles = array();
- private $visitDates = array();
- private $visitTimes = array();
- private $t = null;
-
- public function setUp()
- {
- // set up action URLs
- for ($i = 0; $i != 100; ++$i)
- {
- $this->urls[] = "http://whatever.com/$i/".($i+1);
- $this->pageTitles[] = "Page Title $i / ".($i+1);
- }
-
- // set dates & times
- $date = Piwik_Date::factory(self::$fixture->date);
- for ($i = 0; $i != 25; ++$i)
- {
- $this->visitDates[] = $date->addDay($i)->toString('Y-m-d');
- }
- for ($i = 0; $i != 5; ++$i)
- {
- $this->visitTimes[] = $date->addHour($i)->toString('H:i:s');
- }
-
- // create tracker before tracking test
- $this->t = $this->getTracker(self::$fixture->idSite, self::$fixture->date);
- $this->t->setTokenAuth(self::getTokenAuth());
- $this->t->enableBulkTracking();
-
- // track 12,500 actions: 50 visitors w/ 5 visits each per day for 25 days w/ 2 actions per visit
- $urlIdx = 0;
- foreach ($this->visitDates as $date)
- {
- foreach ($this->visitTimes as $time)
- {
- for ($visitor = 0; $visitor != 50; ++$visitor)
- {
- $this->t->setIp('157.5.6.'.($visitor+1));
- $this->t->setForceVisitDateTime($date.' '.$time);
- for ($action = 0; $action != 2; ++$action)
- {
- $realIdx = $urlIdx % count($this->urls);
-
- $this->t->setUrl($this->urls[$realIdx]);
- $this->t->doTrackPageView($this->pageTitles[$realIdx]);
-
- ++$urlIdx;
- }
- }
- }
- }
- }
-
- /**
+ private $urls = array();
+ private $pageTitles = array();
+ private $visitDates = array();
+ private $visitTimes = array();
+ private $t = null;
+
+ public function setUp()
+ {
+ // set up action URLs
+ for ($i = 0; $i != 100; ++$i) {
+ $this->urls[] = "http://whatever.com/$i/" . ($i + 1);
+ $this->pageTitles[] = "Page Title $i / " . ($i + 1);
+ }
+
+ // set dates & times
+ $date = Piwik_Date::factory(self::$fixture->date);
+ for ($i = 0; $i != 25; ++$i) {
+ $this->visitDates[] = $date->addDay($i)->toString('Y-m-d');
+ }
+ for ($i = 0; $i != 5; ++$i) {
+ $this->visitTimes[] = $date->addHour($i)->toString('H:i:s');
+ }
+
+ // create tracker before tracking test
+ $this->t = $this->getTracker(self::$fixture->idSite, self::$fixture->date);
+ $this->t->setTokenAuth(self::getTokenAuth());
+ $this->t->enableBulkTracking();
+
+ // track 12,500 actions: 50 visitors w/ 5 visits each per day for 25 days w/ 2 actions per visit
+ $urlIdx = 0;
+ foreach ($this->visitDates as $date) {
+ foreach ($this->visitTimes as $time) {
+ for ($visitor = 0; $visitor != 50; ++$visitor) {
+ $this->t->setIp('157.5.6.' . ($visitor + 1));
+ $this->t->setForceVisitDateTime($date . ' ' . $time);
+ for ($action = 0; $action != 2; ++$action) {
+ $realIdx = $urlIdx % count($this->urls);
+
+ $this->t->setUrl($this->urls[$realIdx]);
+ $this->t->doTrackPageView($this->pageTitles[$realIdx]);
+
+ ++$urlIdx;
+ }
+ }
+ }
+ }
+ }
+
+ /**
* @group Benchmarks
* @group TrackerBenchmark
- */
- public function testTracker()
- {
- self::checkResponse($this->t->doBulkTrack());
- }
+ */
+ public function testTracker()
+ {
+ self::checkResponse($this->t->doBulkTrack());
+ }
}
diff --git a/tests/PHPUnit/Core/API/ResponseBuilderTest.php b/tests/PHPUnit/Core/API/ResponseBuilderTest.php
index 323e727415..01c67f56df 100644
--- a/tests/PHPUnit/Core/API/ResponseBuilderTest.php
+++ b/tests/PHPUnit/Core/API/ResponseBuilderTest.php
@@ -16,15 +16,15 @@ class API_ResponseBuilderTest extends PHPUnit_Framework_TestCase
*/
public function testConvertMultiDimensionalStandardArrayToJson()
{
- $input = array( "firstElement",
- array(
- "firstElement",
- "secondElement",
- ),
- "thirdElement");
+ $input = array("firstElement",
+ array(
+ "firstElement",
+ "secondElement",
+ ),
+ "thirdElement");
$expected = json_encode($input);
- $actual = Piwik_API_ResponseBuilder::convertMultiDimensionalArrayToJson($input);
+ $actual = Piwik_API_ResponseBuilder::convertMultiDimensionalArrayToJson($input);
$this->assertEquals($expected, $actual);
}
@@ -38,15 +38,15 @@ class API_ResponseBuilderTest extends PHPUnit_Framework_TestCase
public function testConvertMultiDimensionalAssociativeArrayToJson()
{
$input = array(
- "firstElement" => "isFirst",
- "secondElement" => array(
- "firstElement" => "isFirst",
- "secondElement" => "isSecond",
- ),
- "thirdElement" => "isThird");
+ "firstElement" => "isFirst",
+ "secondElement" => array(
+ "firstElement" => "isFirst",
+ "secondElement" => "isSecond",
+ ),
+ "thirdElement" => "isThird");
$expected = json_encode($input);
- $actual = Piwik_API_ResponseBuilder::convertMultiDimensionalArrayToJson($input);
+ $actual = Piwik_API_ResponseBuilder::convertMultiDimensionalArrayToJson($input);
$this->assertEquals($expected, $actual);
}
@@ -60,19 +60,19 @@ class API_ResponseBuilderTest extends PHPUnit_Framework_TestCase
public function testConvertMultiDimensionalMixedArrayToJson()
{
$input = array(
- "firstElement" => "isFirst",
- array(
- "firstElement",
- "secondElement",
- ),
- "thirdElement" => array(
- "firstElement" => "isFirst",
- "secondElement" => "isSecond",
- )
+ "firstElement" => "isFirst",
+ array(
+ "firstElement",
+ "secondElement",
+ ),
+ "thirdElement" => array(
+ "firstElement" => "isFirst",
+ "secondElement" => "isSecond",
+ )
);
$expected = json_encode($input);
- $actual = Piwik_API_ResponseBuilder::convertMultiDimensionalArrayToJson($input);
+ $actual = Piwik_API_ResponseBuilder::convertMultiDimensionalArrayToJson($input);
$this->assertEquals($expected, $actual);
}
}
diff --git a/tests/PHPUnit/Core/AccessTest.php b/tests/PHPUnit/Core/AccessTest.php
index f050f3f9d9..5fe6d21580 100644
--- a/tests/PHPUnit/Core/AccessTest.php
+++ b/tests/PHPUnit/Core/AccessTest.php
@@ -14,7 +14,7 @@ class AccessTest extends DatabaseTestCase
public function testGetListAccess()
{
$accessList = Piwik_Access::getListAccess();
- $shouldBe = array('noaccess', 'view', 'admin', 'superuser');
+ $shouldBe = array('noaccess', 'view', 'admin', 'superuser');
$this->assertEquals($shouldBe, $accessList);
}
@@ -157,11 +157,11 @@ class AccessTest extends DatabaseTestCase
$mock = $this->getMock(
'Piwik_Access',
array('getSitesIdWithAdminAccess')
- );
+ );
$mock->expects($this->once())
- ->method('getSitesIdWithAdminAccess')
- ->will($this->returnValue(array(2, 9)));
+ ->method('getSitesIdWithAdminAccess')
+ ->will($this->returnValue(array(2, 9)));
$mock->checkUserHasSomeAdminAccess();
}
@@ -198,11 +198,11 @@ class AccessTest extends DatabaseTestCase
$mock = $this->getMock(
'Piwik_Access',
array('getSitesIdWithAtLeastViewAccess')
- );
+ );
$mock->expects($this->once())
- ->method('getSitesIdWithAtLeastViewAccess')
- ->will($this->returnValue(array(1, 2, 3, 4)));
+ ->method('getSitesIdWithAtLeastViewAccess')
+ ->will($this->returnValue(array(1, 2, 3, 4)));
$mock->checkUserHasSomeViewAccess();
}
@@ -239,11 +239,11 @@ class AccessTest extends DatabaseTestCase
$mock = $this->getMock(
'Piwik_Access',
array('getSitesIdWithAtLeastViewAccess')
- );
+ );
$mock->expects($this->once())
- ->method('getSitesIdWithAtLeastViewAccess')
- ->will($this->returnValue(array(1, 2, 3, 4)));
+ ->method('getSitesIdWithAtLeastViewAccess')
+ ->will($this->returnValue(array(1, 2, 3, 4)));
$mock->checkUserHasViewAccess('1,3');
}
@@ -257,11 +257,11 @@ class AccessTest extends DatabaseTestCase
$mock = $this->getMock(
'Piwik_Access',
array('getSitesIdWithAtLeastViewAccess')
- );
+ );
$mock->expects($this->any())
- ->method('getSitesIdWithAtLeastViewAccess')
- ->will($this->returnValue(array(1, 2, 3, 4)));
+ ->method('getSitesIdWithAtLeastViewAccess')
+ ->will($this->returnValue(array(1, 2, 3, 4)));
$mock->checkUserHasViewAccess('all');
}
@@ -276,11 +276,11 @@ class AccessTest extends DatabaseTestCase
$mock = $this->getMock(
'Piwik_Access',
array('getSitesIdWithAtLeastViewAccess')
- );
+ );
$mock->expects($this->once())
- ->method('getSitesIdWithAtLeastViewAccess')
- ->will($this->returnValue(array(1, 2, 3, 4)));
+ ->method('getSitesIdWithAtLeastViewAccess')
+ ->will($this->returnValue(array(1, 2, 3, 4)));
$mock->checkUserHasViewAccess(array(1, 5));
}
@@ -317,11 +317,11 @@ class AccessTest extends DatabaseTestCase
$mock = $this->getMock(
'Piwik_Access',
array('getSitesIdWithAdminAccess')
- );
+ );
$mock->expects($this->once())
- ->method('getSitesIdWithAdminAccess')
- ->will($this->returnValue(array(1, 2, 3, 4)));
+ ->method('getSitesIdWithAdminAccess')
+ ->will($this->returnValue(array(1, 2, 3, 4)));
$mock->checkUserHasAdminAccess('1,3');
}
@@ -335,15 +335,15 @@ class AccessTest extends DatabaseTestCase
$mock = $this->getMock(
'Piwik_Access',
array('getSitesIdWithAdminAccess', 'getSitesIdWithAtLeastViewAccess')
- );
+ );
$mock->expects($this->any())
- ->method('getSitesIdWithAdminAccess')
- ->will($this->returnValue(array(1, 2, 3, 4)));
+ ->method('getSitesIdWithAdminAccess')
+ ->will($this->returnValue(array(1, 2, 3, 4)));
$mock->expects($this->any())
- ->method('getSitesIdWithAtLeastViewAccess')
- ->will($this->returnValue(array(1, 2, 3, 4)));
+ ->method('getSitesIdWithAtLeastViewAccess')
+ ->will($this->returnValue(array(1, 2, 3, 4)));
$mock->checkUserHasAdminAccess('all');
}
@@ -358,11 +358,11 @@ class AccessTest extends DatabaseTestCase
$mock = $this->getMock(
'Piwik_Access',
array('getSitesIdWithAdminAccess')
- );
+ );
$mock->expects($this->once())
- ->method('getSitesIdWithAdminAccess')
- ->will($this->returnValue(array(1, 2, 3, 4)));
+ ->method('getSitesIdWithAdminAccess')
+ ->will($this->returnValue(array(1, 2, 3, 4)));
$mock->checkUserHasAdminAccess(array(1, 5));
}
@@ -397,8 +397,8 @@ class AccessTest extends DatabaseTestCase
{
$mock = $this->getMock('Piwik_Login_Auth', array('authenticate'));
$mock->expects($this->once())
- ->method('authenticate')
- ->will($this->returnValue(new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS, 'login', 'token' )));
+ ->method('authenticate')
+ ->will($this->returnValue(new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS, 'login', 'token')));
$access = new Piwik_Access();
Zend_Registry::set('access', $access);
@@ -414,8 +414,8 @@ class AccessTest extends DatabaseTestCase
{
$mock = $this->getMock('Piwik_Login_Auth', array('authenticate'));
$mock->expects($this->once())
- ->method('authenticate')
- ->will($this->returnValue(new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS_SUPERUSER_AUTH_CODE, 'superuser', 'superusertoken' )));
+ ->method('authenticate')
+ ->will($this->returnValue(new Piwik_Auth_Result(Piwik_Auth_Result::SUCCESS_SUPERUSER_AUTH_CODE, 'superuser', 'superusertoken')));
$access = new Piwik_Access();
Zend_Registry::set('access', $access);
@@ -431,8 +431,8 @@ class AccessTest extends DatabaseTestCase
{
$mock = $this->getMock('Piwik_Login_Auth', array('authenticate'));
$mock->expects($this->once())
- ->method('authenticate')
- ->will($this->returnValue(new Piwik_Auth_Result(Piwik_Auth_Result::FAILURE_CREDENTIAL_INVALID, null, null )));
+ ->method('authenticate')
+ ->will($this->returnValue(new Piwik_Auth_Result(Piwik_Auth_Result::FAILURE_CREDENTIAL_INVALID, null, null)));
$access = new Piwik_Access();
Zend_Registry::set('access', $access);
diff --git a/tests/PHPUnit/Core/ArchiveProcessing/DayTest.php b/tests/PHPUnit/Core/ArchiveProcessing/DayTest.php
index e466f2a1e0..369b0ef2a3 100644
--- a/tests/PHPUnit/Core/ArchiveProcessing/DayTest.php
+++ b/tests/PHPUnit/Core/ArchiveProcessing/DayTest.php
@@ -14,21 +14,21 @@ class ArchiveProcessing_DayTest extends PHPUnit_Framework_TestCase
*/
public function testGenerateDataTableSimple()
{
- $row1 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'page1', 'visits' => 1, 'actions' => 2, '666' => 'evil' )));
-
+ $row1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'page1', 'visits' => 1, 'actions' => 2, '666' => 'evil')));
+
$input = array(
'page1' => $row1,
);
-
+
$table = new Piwik_DataTable;
$table->addRow($row1);
-
+
$tableGenerated = Piwik_ArchiveProcessing_Day::generateDataTable($input);
- $this->assertTrue(Piwik_DataTable::isEqual($table,$tableGenerated));
+ $this->assertTrue(Piwik_DataTable::isEqual($table, $tableGenerated));
}
-
+
/**
* @group Core
* @group ArchiveProcessing
@@ -36,25 +36,25 @@ class ArchiveProcessing_DayTest extends PHPUnit_Framework_TestCase
*/
public function testGenerateDataTable2rows()
{
- $row1 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'page1', 'visits' => 1, 'actions' => 2)));
- $row2 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'page2', 'visits' => 3, 'actions' => 5)));
-
+ $row1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'page1', 'visits' => 1, 'actions' => 2)));
+ $row2 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'page2', 'visits' => 3, 'actions' => 5)));
+
$input = array(
'page1' => $row1,
'page2' => $row2,
);
-
+
$table = new Piwik_DataTable;
$table->addRow($row1);
$table->addRow($row2);
-
+
$tableGenerated = Piwik_ArchiveProcessing_Day::generateDataTable($input);
-
- $this->assertTrue(Piwik_DataTable::isEqual($table,$tableGenerated));
+
+ $this->assertTrue(Piwik_DataTable::isEqual($table, $tableGenerated));
}
-
+
/**
* @group Core
* @group ArchiveProcessing
@@ -62,34 +62,34 @@ class ArchiveProcessing_DayTest extends PHPUnit_Framework_TestCase
*/
public function testGenerateDataTable1row2level()
{
- $row1 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'cat1', 'visits' => 3, 'actions' => 5 )));
-
- $rowLevel2 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'page1', 'visits' => 3, 'actions' => 5)));
+ $row1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'cat1', 'visits' => 3, 'actions' => 5)));
+
+ $rowLevel2 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'page1', 'visits' => 3, 'actions' => 5)));
$subtable = new Piwik_DataTable;
$subtable->addRow($rowLevel2);
$row1->addSubtable($subtable);
-
+
$table = new Piwik_DataTable;
$table->addRow($row1);
-
+
$input = array(
'cat1' => array(
'page1' => $rowLevel2,
)
);
-
+
$tableGenerated = Piwik_ArchiveProcessing_Day::generateDataTable($input);
-
+
$r1 = new Piwik_DataTable_Renderer_Console();
$r1->setTable($table);
$r2 = new Piwik_DataTable_Renderer_Console();
$r2->setTable($tableGenerated);
-
- $this->assertTrue(Piwik_DataTable::isEqual($table,$tableGenerated));
+
+ $this->assertTrue(Piwik_DataTable::isEqual($table, $tableGenerated));
}
-
+
/**
* @group Core
* @group ArchiveProcessing
@@ -98,57 +98,57 @@ class ArchiveProcessing_DayTest extends PHPUnit_Framework_TestCase
public function testGenerateDataTable2rows2level()
{
$table = new Piwik_DataTable;
-
+
//FIRST ROW + SUBTABLE
- $row1 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array( 'label' => 'cat1', 'visits' => 3, 'actions' => 5 )));
-
- $rowLevel2a = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array( 'label' => 'page1', 'visits' => 3, 'actions' => 5)));
+ $row1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'cat1', 'visits' => 3, 'actions' => 5)));
+
+ $rowLevel2a = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'page1', 'visits' => 3, 'actions' => 5)));
$subtable = new Piwik_DataTable;
$subtable->addRow($rowLevel2a);
$row1->addSubtable($subtable);
-
+
//-- add
$table->addRow($row1);
-
+
//SECOND ROW + SUBTABLE MULTI ROWS
- $row1 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'cat2', 'visits' => 13, 'actions' => 9 )));
-
- $rowLevel2b1 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'page2a', 'visits' => 6, 'actions' => 8)));
-
- $rowLevel2b2 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'page2b', 'visits' => 7, 'actions' => 1)));
+ $row1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'cat2', 'visits' => 13, 'actions' => 9)));
+
+ $rowLevel2b1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'page2a', 'visits' => 6, 'actions' => 8)));
+
+ $rowLevel2b2 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'page2b', 'visits' => 7, 'actions' => 1)));
$subtable = new Piwik_DataTable;
$subtable->addRow($rowLevel2b1);
$subtable->addRow($rowLevel2b2);
$row1->addSubtable($subtable);
-
+
//-- add
$table->addRow($row1);
-
+
// WHAT WE TEST
$input = array(
- 'cat1' => array(
+ 'cat1' => array(
'page1' => $rowLevel2a,
- ),
- 'cat2' => array(
+ ),
+ 'cat2' => array(
'page2a' => $rowLevel2b1,
'page2b' => $rowLevel2b2,
- )
- );
+ )
+ );
$tableGenerated = Piwik_ArchiveProcessing_Day::generateDataTable($input);
-
+
$r1 = new Piwik_DataTable_Renderer_Console();
$r1->setTable($table);
$r2 = new Piwik_DataTable_Renderer_Console();
$r2->setTable($tableGenerated);
-
- $this->assertTrue(Piwik_DataTable::isEqual($table,$tableGenerated));
+
+ $this->assertTrue(Piwik_DataTable::isEqual($table, $tableGenerated));
}
-
+
/**
* @group Core
* @group ArchiveProcessing
@@ -157,52 +157,52 @@ class ArchiveProcessing_DayTest extends PHPUnit_Framework_TestCase
public function testGenerateDataTable1row4levelMultiRows()
{
$table = new Piwik_DataTable;
-
+
//FIRST ROW + SUBTABLE
- $rowcat2 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => '456', 'visits' => 3, 'actions' => 5 )));
-
- $cat2 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'cat2', 'visits' => 3, 'actions' => 5 )));
-
- $rowcat1 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'pagecat1', 'visits' => 6, 'actions' => 4)));
-
- $cat1 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'cat1', 'visits' => 9, 'actions' => 9 )));
-
+ $rowcat2 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => '456', 'visits' => 3, 'actions' => 5)));
+
+ $cat2 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'cat2', 'visits' => 3, 'actions' => 5)));
+
+ $rowcat1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'pagecat1', 'visits' => 6, 'actions' => 4)));
+
+ $cat1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'cat1', 'visits' => 9, 'actions' => 9)));
+
$subtablecat2 = new Piwik_DataTable;
$subtablecat2->addRow($rowcat2);
$cat2->addSubtable($subtablecat2);
-
+
$subtablecat1 = new Piwik_DataTable;
$subtablecat1->addRow($rowcat1);
$subtablecat1->addRow($cat2);
-
+
$cat1->addSubtable($subtablecat1);
-
+
//-- add
$table->addRow($cat1);
-
+
// WHAT WE TEST
$input = array(
'cat1' => array(
'pagecat1' => $rowcat1,
- 'cat2' => array(
+ 'cat2' => array(
'pagecat2' => $rowcat2,
),
),
- );
+ );
$tableGenerated = Piwik_ArchiveProcessing_Day::generateDataTable($input);
-
+
$r1 = new Piwik_DataTable_Renderer_Console();
$r1->setTable($table);
$r2 = new Piwik_DataTable_Renderer_Console();
$r2->setTable($tableGenerated);
-
- $this->assertTrue(Piwik_DataTable::isEqual($table,$tableGenerated));
+
+ $this->assertTrue(Piwik_DataTable::isEqual($table, $tableGenerated));
}
-
+
/**
* @group Core
* @group ArchiveProcessing
@@ -211,33 +211,33 @@ class ArchiveProcessing_DayTest extends PHPUnit_Framework_TestCase
public function testGenerateDataTable1row4level()
{
$table = new Piwik_DataTable;
-
- $rowpagecat3 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => '123123', 'visits' => 3, 'actions' => 5 )));
-
- $rowcat3 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => '789.654', 'visits' => 3, 'actions' => 5 )));
- $rowcat2 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => 'cat2', 'visits' => 3, 'actions' => 5 )));
- $rowcat1 = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS =>
- array('label' => '&*()', 'visits' => 3, 'actions' => 5 )));
+
+ $rowpagecat3 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => '123123', 'visits' => 3, 'actions' => 5)));
+
+ $rowcat3 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => '789.654', 'visits' => 3, 'actions' => 5)));
+ $rowcat2 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => 'cat2', 'visits' => 3, 'actions' => 5)));
+ $rowcat1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS =>
+ array('label' => '&*()', 'visits' => 3, 'actions' => 5)));
$subtablerowpagecat3 = new Piwik_DataTable;
$subtablerowpagecat3->addRow($rowpagecat3);
$rowcat3->addSubtable($subtablerowpagecat3);
-
+
$subtablecat2 = new Piwik_DataTable;
$subtablecat2->addRow($rowcat3);
$rowcat2->addSubtable($subtablecat2);
-
-
+
+
$subtablecat1 = new Piwik_DataTable;
$subtablecat1->addRow($rowcat2);
$rowcat1->addSubtable($subtablecat1);
-
+
//-- add
$table->addRow($rowcat1);
-
+
// WHAT WE TEST
$input = array(
'&*()' => array(
@@ -248,13 +248,13 @@ class ArchiveProcessing_DayTest extends PHPUnit_Framework_TestCase
),
),
);
-
+
$tableGenerated = Piwik_ArchiveProcessing_Day::generateDataTable($input);
-
+
$r1 = new Piwik_DataTable_Renderer_Console();
$r1->setTable($table);
$r2 = new Piwik_DataTable_Renderer_Console();
$r2->setTable($tableGenerated);
- $this->assertTrue(Piwik_DataTable::isEqual($table,$tableGenerated));
+ $this->assertTrue(Piwik_DataTable::isEqual($table, $tableGenerated));
}
}
diff --git a/tests/PHPUnit/Core/ArchiveProcessingTest.php b/tests/PHPUnit/Core/ArchiveProcessingTest.php
index 07686d3366..530bcf545e 100644
--- a/tests/PHPUnit/Core/ArchiveProcessingTest.php
+++ b/tests/PHPUnit/Core/ArchiveProcessingTest.php
@@ -19,28 +19,28 @@ class ArchiveProcessingTest extends DatabaseTestCase
/**
* Creates a new website
- *
+ *
* @param string $timezone
* @return Piwik_Site
*/
private function _createWebsite($timezone = 'UTC')
{
$idSite = Piwik_SitesManager_API::getInstance()->addSite(
- "site1",
- array("http://piwik.net"),
- $ecommerce=0,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
- $excludedIps = "",
- $excludedQueryParameters = "",
- $timezone);
-
+ "site1",
+ array("http://piwik.net"),
+ $ecommerce = 0,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
+ $excludedIps = "",
+ $excludedQueryParameters = "",
+ $timezone);
+
Piwik_Site::clearCache();
return new Piwik_Site($idSite);
}
/**
* Creates a new ArchiveProcessing object
- *
+ *
* @param string $periodLabel
* @param string $dateLabel
* @param string $siteTimezone
@@ -51,7 +51,7 @@ class ArchiveProcessingTest extends DatabaseTestCase
$site = $this->_createWebsite($siteTimezone);
$date = Piwik_Date::factory($dateLabel);
$period = Piwik_Period::factory($periodLabel, $date);
-
+
$archiveProcessing = Piwik_ArchiveProcessing::factory($periodLabel);
$archiveProcessing->setSite($site);
$archiveProcessing->setPeriod($period);
@@ -69,11 +69,11 @@ class ArchiveProcessingTest extends DatabaseTestCase
{
$siteTimezone = 'UTC+10';
$now = time();
-
+
$dateLabel = date('Y-m-d', $now);
$archiveProcessing = $this->_createArchiveProcessing('month', $dateLabel, $siteTimezone);
$archiveProcessing->time = $now;
-
+
// min finished timestamp considered when looking at archive timestamp
$timeout = Piwik_ArchiveProcessing::getTodayArchiveTimeToLive();
$this->assertTrue($timeout >= 10);
@@ -83,7 +83,7 @@ class ArchiveProcessingTest extends DatabaseTestCase
$this->assertEquals($minTimestamp, $dateMinArchived, Piwik_Date::factory($minTimestamp)->getDatetime() . " != " . Piwik_Date::factory($dateMinArchived)->getDatetime());
$this->assertTrue($archiveProcessing->isArchiveTemporary());
}
-
+
/**
* test of validity of an archive, for a month in the past
* @group Core
@@ -92,11 +92,11 @@ class ArchiveProcessingTest extends DatabaseTestCase
public function testInitDayInPast()
{
$archiveProcessing = $this->_createArchiveProcessing('day', '2010-01-01', 'UTC');
-
+
// min finished timestamp considered when looking at archive timestamp
$dateMinArchived = Piwik_Date::factory('2010-01-02')->getTimestamp();
$this->assertEquals($archiveProcessing->getMinTimeArchivedProcessed() + 1, $dateMinArchived);
-
+
$this->assertEquals('2010-01-01 00:00:00', $archiveProcessing->getStartDatetimeUTC());
$this->assertEquals('2010-01-01 23:59:59', $archiveProcessing->getEndDatetimeUTC());
$this->assertFalse($archiveProcessing->isArchiveTemporary());
@@ -114,7 +114,7 @@ class ArchiveProcessingTest extends DatabaseTestCase
// min finished timestamp considered when looking at archive timestamp
$dateMinArchived = Piwik_Date::factory('2010-01-01 18:30:00');
$this->assertEquals($archiveProcessing->getMinTimeArchivedProcessed() + 1, $dateMinArchived->getTimestamp());
-
+
$this->assertEquals('2009-12-31 18:30:00', $archiveProcessing->getStartDatetimeUTC());
$this->assertEquals('2010-01-01 18:29:59', $archiveProcessing->getEndDatetimeUTC());
$this->assertFalse($archiveProcessing->isArchiveTemporary());
@@ -132,12 +132,12 @@ class ArchiveProcessingTest extends DatabaseTestCase
// min finished timestamp considered when looking at archive timestamp
$dateMinArchived = Piwik_Date::factory('2010-02-01 05:30:00');
$this->assertEquals($archiveProcessing->getMinTimeArchivedProcessed() + 1, $dateMinArchived->getTimestamp());
-
+
$this->assertEquals('2010-01-01 05:30:00', $archiveProcessing->getStartDatetimeUTC());
$this->assertEquals('2010-02-01 05:29:59', $archiveProcessing->getEndDatetimeUTC());
$this->assertFalse($archiveProcessing->isArchiveTemporary());
}
-
+
/**
* test of validity of an archive, for today's archive
* @group Core
@@ -151,10 +151,10 @@ class ArchiveProcessingTest extends DatabaseTestCase
$dateLabel = date('Y-m-d', $timestamp);
Piwik_ArchiveProcessing::setBrowserTriggerArchiving(true);
-
+
$archiveProcessing = $this->_createArchiveProcessing('day', $dateLabel, $siteTimezone);
$archiveProcessing->time = $now;
-
+
// we look at anything processed within the time to live range
$dateMinArchived = $now - Piwik_ArchiveProcessing::getTodayArchiveTimeToLive();
$this->assertEquals($dateMinArchived, $archiveProcessing->getMinTimeArchivedProcessed());
@@ -166,14 +166,13 @@ class ArchiveProcessingTest extends DatabaseTestCase
// see isArchivingDisabled()
// Running in CLI doesn't impact the time to live today's archive we are loading
// From CLI, we will not return data that is 'stale'
- if(!Piwik_Common::isPhpCliMode())
- {
+ if (!Piwik_Common::isPhpCliMode()) {
$dateMinArchived = 0;
}
$this->assertEquals($archiveProcessing->getMinTimeArchivedProcessed(), $dateMinArchived);
-
- $this->assertEquals(date('Y-m-d', $timestamp).' 01:00:00', $archiveProcessing->getStartDatetimeUTC());
- $this->assertEquals(date('Y-m-d', $timestamp+86400).' 00:59:59', $archiveProcessing->getEndDatetimeUTC());
+
+ $this->assertEquals(date('Y-m-d', $timestamp) . ' 01:00:00', $archiveProcessing->getStartDatetimeUTC());
+ $this->assertEquals(date('Y-m-d', $timestamp + 86400) . ' 00:59:59', $archiveProcessing->getEndDatetimeUTC());
$this->assertTrue($archiveProcessing->isArchiveTemporary());
}
@@ -184,8 +183,7 @@ class ArchiveProcessingTest extends DatabaseTestCase
*/
public function testInitTodayEurope()
{
- if(!Piwik::isTimezoneSupportEnabled())
- {
+ if (!Piwik::isTimezoneSupportEnabled()) {
$this->markTestSkipped('timezones needs to be supported');
}
@@ -210,17 +208,16 @@ class ArchiveProcessingTest extends DatabaseTestCase
// see isArchivingDisabled()
// Running in CLI doesn't impact the time to live today's archive we are loading
// From CLI, we will not return data that is 'stale'
- if(!Piwik_Common::isPhpCliMode())
- {
+ if (!Piwik_Common::isPhpCliMode()) {
$dateMinArchived = 0;
}
$this->assertEquals($archiveProcessing->getMinTimeArchivedProcessed(), $dateMinArchived);
// this test varies with DST
- $this->assertTrue($archiveProcessing->getStartDatetimeUTC() == date('Y-m-d', $timestamp-86400).' 22:00:00' ||
- $archiveProcessing->getStartDatetimeUTC() == date('Y-m-d', $timestamp-86400).' 23:00:00');
- $this->assertTrue($archiveProcessing->getEndDatetimeUTC() == date('Y-m-d', $timestamp).' 21:59:59' ||
- $archiveProcessing->getEndDatetimeUTC() == date('Y-m-d', $timestamp).' 22:59:59');
+ $this->assertTrue($archiveProcessing->getStartDatetimeUTC() == date('Y-m-d', $timestamp - 86400) . ' 22:00:00' ||
+ $archiveProcessing->getStartDatetimeUTC() == date('Y-m-d', $timestamp - 86400) . ' 23:00:00');
+ $this->assertTrue($archiveProcessing->getEndDatetimeUTC() == date('Y-m-d', $timestamp) . ' 21:59:59' ||
+ $archiveProcessing->getEndDatetimeUTC() == date('Y-m-d', $timestamp) . ' 22:59:59');
$this->assertTrue($archiveProcessing->isArchiveTemporary());
}
@@ -232,8 +229,7 @@ class ArchiveProcessingTest extends DatabaseTestCase
*/
public function testInitTodayToronto()
{
- if(!Piwik::isTimezoneSupportEnabled())
- {
+ if (!Piwik::isTimezoneSupportEnabled()) {
$this->markTestSkipped('timezones needs to be supported');
}
@@ -258,17 +254,16 @@ class ArchiveProcessingTest extends DatabaseTestCase
// see isArchivingDisabled()
// Running in CLI doesn't impact the time to live today's archive we are loading
// From CLI, we will not return data that is 'stale'
- if(!Piwik_Common::isPhpCliMode())
- {
+ if (!Piwik_Common::isPhpCliMode()) {
$dateMinArchived = 0;
}
$this->assertEquals($archiveProcessing->getMinTimeArchivedProcessed(), $dateMinArchived);
// this test varies with DST
- $this->assertTrue($archiveProcessing->getStartDatetimeUTC() == date('Y-m-d', $timestamp).' 04:00:00' ||
- $archiveProcessing->getStartDatetimeUTC() == date('Y-m-d', $timestamp).' 05:00:00');
- $this->assertTrue($archiveProcessing->getEndDatetimeUTC() == date('Y-m-d', $timestamp+86400).' 03:59:59' ||
- $archiveProcessing->getEndDatetimeUTC() == date('Y-m-d', $timestamp+86400).' 04:59:59');
+ $this->assertTrue($archiveProcessing->getStartDatetimeUTC() == date('Y-m-d', $timestamp) . ' 04:00:00' ||
+ $archiveProcessing->getStartDatetimeUTC() == date('Y-m-d', $timestamp) . ' 05:00:00');
+ $this->assertTrue($archiveProcessing->getEndDatetimeUTC() == date('Y-m-d', $timestamp + 86400) . ' 03:59:59' ||
+ $archiveProcessing->getEndDatetimeUTC() == date('Y-m-d', $timestamp + 86400) . ' 04:59:59');
$this->assertTrue($archiveProcessing->isArchiveTemporary());
}
@@ -283,50 +278,49 @@ class ArchiveProcessingTest extends DatabaseTestCase
$table = Piwik_Common::prefixTable('site_url');
$data = $this->_getDataInsert();
try {
- $didWeUseBulk = Piwik::tableInsertBatch($table,
- array('idsite', 'url'),
- $data,
- $throwException = true);
-
- } catch(Exception $e) {
- $didWeUseBulk = $e->getMessage();
- }
- $this->_checkLoadDataInFileWasUsed($didWeUseBulk);
-
- if($didWeUseBulk === true) {
- $this->_checkTableIsExpected($table, $data);
-
- // INSERT again the bulk. Because we use keyword LOCAL the data will be REPLACED automatically (see mysql doc)
- Piwik::tableInsertBatch($table, array('idsite', 'url'), $data);
- $this->_checkTableIsExpected($table, $data);
- }
+ $didWeUseBulk = Piwik::tableInsertBatch($table,
+ array('idsite', 'url'),
+ $data,
+ $throwException = true);
+
+ } catch (Exception $e) {
+ $didWeUseBulk = $e->getMessage();
+ }
+ $this->_checkLoadDataInFileWasUsed($didWeUseBulk);
+
+ if ($didWeUseBulk === true) {
+ $this->_checkTableIsExpected($table, $data);
+
+ // INSERT again the bulk. Because we use keyword LOCAL the data will be REPLACED automatically (see mysql doc)
+ Piwik::tableInsertBatch($table, array('idsite', 'url'), $data);
+ $this->_checkTableIsExpected($table, $data);
+ }
}
- protected function _checkLoadDataInFileWasUsed($didWeUseBulk)
- {
- static $skippedOnce = false;
- if($didWeUseBulk !== true
- && $skippedOnce === false
- // HACK: Only alert for MysqlI since PDO is often failing and Jenkins should always run MYSQLI + PDO
- // This helps "hiding" the warning on PDO Mysql but we have to make sure mysqli tests are always executed!
- && Piwik_Config::getInstance()->database['adapter'] == 'MYSQLI'
- )
- {
- $skippedOnce = true;
- $this->fail(
- 'Performance notice: LOAD DATA [LOCAL] INFILE query is not working, so Piwik will fallback to using plain INSERTs '
- . ' which will result in a slightly slower Archiving process.'
- . ". \n"
- . ' The error Messages from MySQL were: '
- . $didWeUseBulk
- . "\n\n Learn more how to enable LOAD LOCAL DATA INFILE see the Mysql doc (http://dev.mysql.com/doc/refman/5.0/en/load-data-local.html) "
- . "\n or ask in this Piwik ticket (http://dev.piwik.org/trac/ticket/3605)"
- );
- }
- return $didWeUseBulk;
- }
-
- /**
+ protected function _checkLoadDataInFileWasUsed($didWeUseBulk)
+ {
+ static $skippedOnce = false;
+ if ($didWeUseBulk !== true
+ && $skippedOnce === false
+ // HACK: Only alert for MysqlI since PDO is often failing and Jenkins should always run MYSQLI + PDO
+ // This helps "hiding" the warning on PDO Mysql but we have to make sure mysqli tests are always executed!
+ && Piwik_Config::getInstance()->database['adapter'] == 'MYSQLI'
+ ) {
+ $skippedOnce = true;
+ $this->fail(
+ 'Performance notice: LOAD DATA [LOCAL] INFILE query is not working, so Piwik will fallback to using plain INSERTs '
+ . ' which will result in a slightly slower Archiving process.'
+ . ". \n"
+ . ' The error Messages from MySQL were: '
+ . $didWeUseBulk
+ . "\n\n Learn more how to enable LOAD LOCAL DATA INFILE see the Mysql doc (http://dev.mysql.com/doc/refman/5.0/en/load-data-local.html) "
+ . "\n or ask in this Piwik ticket (http://dev.piwik.org/trac/ticket/3605)"
+ );
+ }
+ return $didWeUseBulk;
+ }
+
+ /**
* Testing plain inserts
* @group Core
* @group ArchiveProcessing
@@ -340,16 +334,16 @@ class ArchiveProcessingTest extends DatabaseTestCase
// If we insert AGAIN, expect to throw an error because the primary key already exists
try {
- Piwik::tableInsertBatchIterate($table, array('idsite', 'url'), $data, $ignoreWhenDuplicate = false);
+ Piwik::tableInsertBatchIterate($table, array('idsite', 'url'), $data, $ignoreWhenDuplicate = false);
} catch (Exception $e) {
// However if we insert with keyword REPLACE, then the new data should be saved
- Piwik::tableInsertBatchIterate($table, array('idsite', 'url'), $data, $ignoreWhenDuplicate = true );
+ Piwik::tableInsertBatchIterate($table, array('idsite', 'url'), $data, $ignoreWhenDuplicate = true);
$this->_checkTableIsExpected($table, $data);
return;
}
$this->fail('Exception expected');
}
-
+
/**
* Testing batch insert (BLOB)
* @group Core
@@ -364,27 +358,25 @@ class ArchiveProcessingTest extends DatabaseTestCase
$table = $archiveProcessing->getTableArchiveBlobName();
$data = $this->_getBlobDataInsert();
- try {
- $didWeUseBulk = Piwik::tableInsertBatch($table,
- array('idarchive', 'name', 'idsite', 'date1', 'date2', 'period', 'ts_archived', 'value'),
- $data,
- $throwException = true);
- } catch(Exception $e) {
- $didWeUseBulk = $e->getMessage();
- }
- $this->_checkLoadDataInFileWasUsed($didWeUseBulk);
-
- // If bulk wasn't used the exception was caught and the INSERT didn't work
- if($didWeUseBulk === true)
- {
- $this->_checkTableIsExpectedBlob($table, $data);
- }
+ try {
+ $didWeUseBulk = Piwik::tableInsertBatch($table,
+ array('idarchive', 'name', 'idsite', 'date1', 'date2', 'period', 'ts_archived', 'value'),
+ $data,
+ $throwException = true);
+ } catch (Exception $e) {
+ $didWeUseBulk = $e->getMessage();
+ }
+ $this->_checkLoadDataInFileWasUsed($didWeUseBulk);
+
+ // If bulk wasn't used the exception was caught and the INSERT didn't work
+ if ($didWeUseBulk === true) {
+ $this->_checkTableIsExpectedBlob($table, $data);
+ }
// INSERT again the bulk. Because we use keyword LOCAL the data will be REPLACED automatically (see mysql doc)
- $didWeUseBulk = Piwik::tableInsertBatch($table, array('idarchive', 'name', 'idsite', 'date1', 'date2', 'period', 'ts_archived', 'value'), $data);
- if($didWeUseBulk === true)
- {
- $this->_checkTableIsExpectedBlob($table, $data);
- }
+ $didWeUseBulk = Piwik::tableInsertBatch($table, array('idarchive', 'name', 'idsite', 'date1', 'date2', 'period', 'ts_archived', 'value'), $data);
+ if ($didWeUseBulk === true) {
+ $this->_checkTableIsExpectedBlob($table, $data);
+ }
}
/**
@@ -406,21 +398,21 @@ class ArchiveProcessingTest extends DatabaseTestCase
// If we insert AGAIN, expect to throw an error because the primary key already exist
try {
- Piwik::tableInsertBatchIterate($table, array('idarchive', 'name', 'idsite', 'date1', 'date2', 'period', 'ts_archived', 'value'), $data, $ignoreWhenDuplicate = false);
+ Piwik::tableInsertBatchIterate($table, array('idarchive', 'name', 'idsite', 'date1', 'date2', 'period', 'ts_archived', 'value'), $data, $ignoreWhenDuplicate = false);
} catch (Exception $e) {
// However if we insert with keyword REPLACE, then the new data should be saved
- Piwik::tableInsertBatchIterate($table, array('idarchive', 'name', 'idsite', 'date1', 'date2', 'period', 'ts_archived', 'value'), $data, $ignoreWhenDuplicate = true );
+ Piwik::tableInsertBatchIterate($table, array('idarchive', 'name', 'idsite', 'date1', 'date2', 'period', 'ts_archived', 'value'), $data, $ignoreWhenDuplicate = true);
$this->_checkTableIsExpectedBlob($table, $data);
return;
}
$this->fail('Exception expected');
}
-
-
+
+
protected function _checkTableIsExpected($table, $data)
{
- $fetched = Piwik_FetchAll('SELECT * FROM '.$table);
- foreach($data as $id => $row) {
+ $fetched = Piwik_FetchAll('SELECT * FROM ' . $table);
+ foreach ($data as $id => $row) {
$this->assertEquals($fetched[$id]['idsite'], $data[$id][0], "record $id is not {$data[$id][0]}");
$this->assertEquals($fetched[$id]['url'], $data[$id][1], "Record $id bug, not {$data[$id][1]} BUT {$fetched[$id]['url']}");
}
@@ -428,8 +420,8 @@ class ArchiveProcessingTest extends DatabaseTestCase
protected function _checkTableIsExpectedBlob($table, $data)
{
- $fetched = Piwik_FetchAll('SELECT * FROM '.$table);
- foreach($data as $id => $row) {
+ $fetched = Piwik_FetchAll('SELECT * FROM ' . $table);
+ foreach ($data as $id => $row) {
$this->assertEquals($fetched[$id]['idarchive'], $data[$id][0], "record $id idarchive is not '{$data[$id][0]}'");
$this->assertEquals($fetched[$id]['name'], $data[$id][1], "record $id name is not '{$data[$id][1]}'");
$this->assertEquals($fetched[$id]['idsite'], $data[$id][2], "record $id idsite is not '{$data[$id][2]}'");
@@ -480,13 +472,12 @@ class ArchiveProcessingTest extends DatabaseTestCase
{
$ts = '2011-03-31 17:48:00';
$str = '';
- for($i = 0; $i < 256; $i++)
- {
+ for ($i = 0; $i < 256; $i++) {
$str .= chr($i);
}
$array[] = array(1, 'bytes 0-255', 1, '2011-03-31', '2011-03-31', Piwik::$idPeriods['day'], $ts, $str);
- $array[] = array(2, 'compressed string', 1, '2011-03-31', '2011-03-31', Piwik::$idPeriods['day'], $ts, gzcompress( " \n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942"));
+ $array[] = array(2, 'compressed string', 1, '2011-03-31', '2011-03-31', Piwik::$idPeriods['day'], $ts, gzcompress(" \n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942\n \r \t teste eigaj oegheao geaoh guoea98742983 2 342942"));
$str = file_get_contents(PIWIK_PATH_TEST_TO_ROOT . '/tests/resources/lipsum.txt');
$array[] = array(3, 'lorem ipsum', 1, '2011-03-31', '2011-03-31', Piwik::$idPeriods['day'], $ts, $str);
diff --git a/tests/PHPUnit/Core/CommonTest.php b/tests/PHPUnit/Core/CommonTest.php
index 6a37576aee..ca117c6fae 100644
--- a/tests/PHPUnit/Core/CommonTest.php
+++ b/tests/PHPUnit/Core/CommonTest.php
@@ -33,7 +33,7 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
array('testhttp://test.com', false),
);
}
-
+
/**
* @dataProvider getUrls
* @group Core
@@ -51,76 +51,76 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
public function getInputValues()
{
return array( // input, output
- // sanitize an array OK
- array(
- array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52),
- array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52)
- ),
- array(
- array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52,
- array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52),
- array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52),
- array( array(array(array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52)))
- )),
- array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52,
- array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52),
- array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52),
- array( array(array(array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52)))
- ))
- ),
- // sanitize an array with bad value level1
- array(
- array('test1' => 't1', 't45', 'tea1"ta"e', 568, 1 => array('t<e"st'), 1.52),
- array('test1' => 't1', 't45', 'tea1&quot;ta&quot;e', 568, 1 => array('t&lt;e&quot;st'), 1.52)
- ),
- // sanitize an array with bad value level2
- array(
- array('tea1"ta"e' => array('t<e"st' => array('tgeag454554"t')), 1.52),
- array('tea1&quot;ta&quot;e' => array('t&lt;e&quot;st' => array('tgeag454554&quot;t')), 1.52)
- ),
- // sanitize a string unicode => no change
- array(
- " Поиск в Интернете Поgqegиск страниц на рgeqg8978усском",
- " Поиск в Интернете Поgqegиск страниц на рgeqg8978усском"
- ),
- // sanitize a bad string
- array(
- '& " < > 123abc\'',
- '&amp; &quot; &lt; &gt; 123abc&#039;'
- ),
- // test filter - expect new line and null byte to be filtered out
- array(
- "New\nLine\rNull\0Byte",
- 'NewLineNullByte'
- ),
- // double encoded - no change (document as user error)
- array(
- '%48%45%4C%00%4C%4F+%57%4F%52%4C%44',
- '%48%45%4C%00%4C%4F+%57%4F%52%4C%44'
- ),
- // sanitize an integer
- array( '121564564', '121564564' ),
- array( '121564564.0121', '121564564.0121' ),
- array( 121564564.0121, 121564564.0121 ),
- array( 12121, 12121 ),
- // sanitize HTML
- array(
- "<test toto='mama' piwik=\"cool\">Piwik!!!!!</test>",
- "&lt;test toto=&#039;mama&#039; piwik=&quot;cool&quot;&gt;Piwik!!!!!&lt;/test&gt;"
- ),
- // sanitize a SQL query
- array(
- "SELECT piwik FROM piwik_tests where test= 'super\"value' AND cool=toto #comment here",
- "SELECT piwik FROM piwik_tests where test= &#039;super&quot;value&#039; AND cool=toto #comment here"
- ),
- // sanitize php variables
- array( true, true ),
- array( false, false ),
- array( null, null ),
- array( "", "" ),
- );
+ // sanitize an array OK
+ array(
+ array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52),
+ array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52)
+ ),
+ array(
+ array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52,
+ array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52),
+ array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52),
+ array(array(array(array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52)))
+ )),
+ array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52,
+ array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52),
+ array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52),
+ array(array(array(array('test1' => 't1', 't45', "teatae", 4568, array('test'), 1.52)))
+ ))
+ ),
+ // sanitize an array with bad value level1
+ array(
+ array('test1' => 't1', 't45', 'tea1"ta"e', 568, 1 => array('t<e"st'), 1.52),
+ array('test1' => 't1', 't45', 'tea1&quot;ta&quot;e', 568, 1 => array('t&lt;e&quot;st'), 1.52)
+ ),
+ // sanitize an array with bad value level2
+ array(
+ array('tea1"ta"e' => array('t<e"st' => array('tgeag454554"t')), 1.52),
+ array('tea1&quot;ta&quot;e' => array('t&lt;e&quot;st' => array('tgeag454554&quot;t')), 1.52)
+ ),
+ // sanitize a string unicode => no change
+ array(
+ " Поиск в Интернете Поgqegиск страниц на рgeqg8978усском",
+ " Поиск в Интернете Поgqegиск страниц на рgeqg8978усском"
+ ),
+ // sanitize a bad string
+ array(
+ '& " < > 123abc\'',
+ '&amp; &quot; &lt; &gt; 123abc&#039;'
+ ),
+ // test filter - expect new line and null byte to be filtered out
+ array(
+ "New\nLine\rNull\0Byte",
+ 'NewLineNullByte'
+ ),
+ // double encoded - no change (document as user error)
+ array(
+ '%48%45%4C%00%4C%4F+%57%4F%52%4C%44',
+ '%48%45%4C%00%4C%4F+%57%4F%52%4C%44'
+ ),
+ // sanitize an integer
+ array('121564564', '121564564'),
+ array('121564564.0121', '121564564.0121'),
+ array(121564564.0121, 121564564.0121),
+ array(12121, 12121),
+ // sanitize HTML
+ array(
+ "<test toto='mama' piwik=\"cool\">Piwik!!!!!</test>",
+ "&lt;test toto=&#039;mama&#039; piwik=&quot;cool&quot;&gt;Piwik!!!!!&lt;/test&gt;"
+ ),
+ // sanitize a SQL query
+ array(
+ "SELECT piwik FROM piwik_tests where test= 'super\"value' AND cool=toto #comment here",
+ "SELECT piwik FROM piwik_tests where test= &#039;super&quot;value&#039; AND cool=toto #comment here"
+ ),
+ // sanitize php variables
+ array(true, true),
+ array(false, false),
+ array(null, null),
+ array("", ""),
+ );
}
-
+
/**
* @dataProvider getInputValues
* @group Core
@@ -129,18 +129,17 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
*/
public function testSanitizeInputValues($input, $output)
{
- if (version_compare(PHP_VERSION, '5.4') < 0)
- {
+ if (version_compare(PHP_VERSION, '5.4') < 0) {
$this->assertTrue(@set_magic_quotes_runtime(1));
$this->assertEquals(1, @get_magic_quotes_runtime());
- $this->assertEquals( $output, Piwik_Common::sanitizeInputValues($input));
-
+ $this->assertEquals($output, Piwik_Common::sanitizeInputValues($input));
+
$this->assertTrue(@set_magic_quotes_runtime(0));
$this->assertEquals(0, @get_magic_quotes_runtime());
- $this->assertEquals( $output, Piwik_Common::sanitizeInputValues($input));
+ $this->assertEquals($output, Piwik_Common::sanitizeInputValues($input));
}
}
-
+
/**
* emptyvarname => exception
* @group Core
@@ -150,14 +149,14 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
public function testGetRequestVarEmptyVarName()
{
try {
- $_GET['']=1;
+ $_GET[''] = 1;
Piwik_Common::getRequestVar('');
} catch (Exception $e) {
return;
}
$this->fail('Expected exception not raised');
}
-
+
/**
* nodefault Notype Novalue => exception
* @group Core
@@ -173,7 +172,7 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* nodefault Notype WithValue => value
* @group Core
@@ -184,9 +183,9 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
{
$_GET['test'] = 1413.431413;
$this->assertEquals($_GET['test'], Piwik_Common::getRequestVar('test'));
-
+
}
-
+
/**
* nodefault Withtype WithValue => exception cos type not matching
* @group Core
@@ -220,44 +219,44 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
$this->fail('Expected exception not raised');
}
-
+
/**
* Dataprovider for testGetRequestVar
*/
public function getRequestVarValues()
{
return array( // value of request var, default value, var type, expected
- array(1413.431413, 2, 'int', 2), // withdefault Withtype WithValue => value casted as type
- array(null, 'default', null, 'default'), // withdefault Notype NoValue => default value
- array(null, 'default', 'string', 'default'), // withdefault Withtype NoValue =>default value casted as type
- // integer as a default value / types
- array('', 45, 'int', 45),
- array(1413.431413, 45, 'int', 45),
- array('', 45, 'integer', 45),
- array('', 45.0, 'float', 45.0),
- array('', 45.25, 'float', 45.25),
- // string as a default value / types
- array('1413.431413', 45, 'int', 45),
- array('1413.431413', 45, 'string', '1413.431413'),
- array('', 45, 'string', '45'),
- array('', 'geaga', 'string', 'geaga'),
- array('', '&#039;}{}}{}{}&#039;', 'string', '&#039;}{}}{}{}&#039;'),
- array('', 'http://url?arg1=val1&arg2=val2', 'string', 'http://url?arg1=val1&amp;arg2=val2'),
- array('http://url?arg1=val1&arg2=val2', 'http://url?arg1=val1&arg2=val4', 'string', 'http://url?arg1=val1&amp;arg2=val2'),
- array(array("test", 1345524, array("gaga")), array(), 'array', array("test", 1345524, array("gaga"))), // array as a default value / types
- array(array("test", 1345524, array("gaga")), 45, 'string', "45"),
- array(array("test", 1345524, array("gaga")), array(1), 'array', array("test", 1345524, array("gaga"))),
- array(array("test", 1345524, array("gaga")), 4, 'int', 4),
- array('', array(1), 'array', array(1)),
- array('', array(), 'array', array()),
- // we give a number in a string and request for a number => it should give the string casted as a number
- array('45645646', 1, 'int', 45645646),
- array('45645646', 45, 'integer', 45645646),
- array('45645646', '45454', 'string', '45645646'),
- array('45645646', array(), 'array', array()),
- );
+ array(1413.431413, 2, 'int', 2), // withdefault Withtype WithValue => value casted as type
+ array(null, 'default', null, 'default'), // withdefault Notype NoValue => default value
+ array(null, 'default', 'string', 'default'), // withdefault Withtype NoValue =>default value casted as type
+ // integer as a default value / types
+ array('', 45, 'int', 45),
+ array(1413.431413, 45, 'int', 45),
+ array('', 45, 'integer', 45),
+ array('', 45.0, 'float', 45.0),
+ array('', 45.25, 'float', 45.25),
+ // string as a default value / types
+ array('1413.431413', 45, 'int', 45),
+ array('1413.431413', 45, 'string', '1413.431413'),
+ array('', 45, 'string', '45'),
+ array('', 'geaga', 'string', 'geaga'),
+ array('', '&#039;}{}}{}{}&#039;', 'string', '&#039;}{}}{}{}&#039;'),
+ array('', 'http://url?arg1=val1&arg2=val2', 'string', 'http://url?arg1=val1&amp;arg2=val2'),
+ array('http://url?arg1=val1&arg2=val2', 'http://url?arg1=val1&arg2=val4', 'string', 'http://url?arg1=val1&amp;arg2=val2'),
+ array(array("test", 1345524, array("gaga")), array(), 'array', array("test", 1345524, array("gaga"))), // array as a default value / types
+ array(array("test", 1345524, array("gaga")), 45, 'string', "45"),
+ array(array("test", 1345524, array("gaga")), array(1), 'array', array("test", 1345524, array("gaga"))),
+ array(array("test", 1345524, array("gaga")), 4, 'int', 4),
+ array('', array(1), 'array', array(1)),
+ array('', array(), 'array', array()),
+ // we give a number in a string and request for a number => it should give the string casted as a number
+ array('45645646', 1, 'int', 45645646),
+ array('45645646', 45, 'integer', 45645646),
+ array('45645646', '45454', 'string', '45645646'),
+ array('45645646', array(), 'array', array()),
+ );
}
-
+
/**
* @dataProvider getRequestVarValues
* @group Core
@@ -270,23 +269,23 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
$return = Piwik_Common::getRequestVar('test', $default, $type);
$this->assertEquals($expected, $return);
// validate correct type
- switch($type) {
+ switch ($type) {
case 'int':
case 'integer':
- $this->assertTrue( is_int($return) );
+ $this->assertTrue(is_int($return));
break;
case 'float':
- $this->assertTrue( is_float($return) );
+ $this->assertTrue(is_float($return));
break;
case 'string':
- $this->assertTrue( is_string($return) );
+ $this->assertTrue(is_string($return));
break;
case 'array':
- $this->assertTrue( is_array($return) );
+ $this->assertTrue(is_array($return));
break;
}
}
-
+
/**
* Dataprovider for testGetParameterFromQueryString
*/
@@ -309,28 +308,28 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
array('?x%5B%5D=A%26y%3D1', 'x', array('A%26y%3D1')),
// ?z=y&x[]=1
array('?z=y%26x%5b%5d%3d1', 'x', null),
-
+
// strange characters
array('toto=mama&mama=&tuytyt=Поиск в Интернете Поиск страниц на русском _*()!$!£$^!£$%&toto=mama second value', 'tuytyt', 'Поиск в Интернете Поиск страниц на русском _*()!$!£$^!£$%'),
-
+
// twice the parameter => returns the last value in the url
array('toto=mama&mama=&tuytyt=teaoi&toto=mama second value', 'toto', 'mama second value'),
-
+
// empty param
array('toto=mama&mama=&tuytyt=teaoi', 'mama', ''),
-
+
// missing parameter value => returns false
array('x', 'x', false),
array('toto=mama&mama&tuytyt=teaoi', 'mama', false),
-
+
// param not found => null
array('toto=mama&mama=titi', 'tot', null),
-
+
// empty query string => null
array('', 'test', null),
);
}
-
+
/**
* @dataProvider getQueryStrings
* @group Core
@@ -363,7 +362,7 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
'a' => false,
'b' => '',
'c' => '1',
- 'd' => array( false ),
+ 'd' => array(false),
'e' => array(''),
'f' => array('a'),
'g' => array('b', 'c'),
@@ -379,11 +378,10 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
public function testIsValidFilenameValidValues()
{
$valid = array(
- "test", "test.txt","test.......", "en-ZHsimplified",
- );
- foreach($valid as $toTest)
- {
- $this->assertTrue(Piwik_Common::isValidFilename($toTest), $toTest." not valid!");
+ "test", "test.txt", "test.......", "en-ZHsimplified",
+ );
+ foreach ($valid as $toTest) {
+ $this->assertTrue(Piwik_Common::isValidFilename($toTest), $toTest . " not valid!");
}
}
@@ -395,11 +393,10 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
public function testIsValidFilenameNotValidValues()
{
$notvalid = array(
- "../test", "/etc/htpasswd", '$var', ';test', '[bizarre]', '', ".htaccess", "very long long eogaioge ageja geau ghaeihieg heiagie aiughaeui hfilename",
- );
- foreach($notvalid as $toTest)
- {
- $this->assertFalse(Piwik_Common::isValidFilename($toTest), $toTest." valid but shouldn't!");
+ "../test", "/etc/htpasswd", '$var', ';test', '[bizarre]', '', ".htaccess", "very long long eogaioge ageja geau ghaeihieg heiagie aiughaeui hfilename",
+ );
+ foreach ($notvalid as $toTest) {
+ $this->assertFalse(Piwik_Common::isValidFilename($toTest), $toTest . " valid but shouldn't!");
}
}
@@ -409,36 +406,36 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
public function getBrowserLanguageData()
{
return array( // user agent, browser language
- array( "en-gb", "en-gb" ),
+ array("en-gb", "en-gb"),
- // filter quality attribute
- array( "en-us,en;q=0.5", "en-us,en" ),
+ // filter quality attribute
+ array("en-us,en;q=0.5", "en-us,en"),
- // bad user agents
- array( "en-us,chrome://global/locale/intl.properties", "en-us" ),
+ // bad user agents
+ array("en-us,chrome://global/locale/intl.properties", "en-us"),
- // unregistered language tag
- array( "en,en-securid", "en" ),
- array( "en-securid,en", "en" ),
- array( "en-us,en-securid,en", "en-us,en" ),
+ // unregistered language tag
+ array("en,en-securid", "en"),
+ array("en-securid,en", "en"),
+ array("en-us,en-securid,en", "en-us,en"),
- // accept private sub tags
- array( "en-us,x-en-securid", "en-us,x-en-securid" ),
- array( "en-us,en-x-securid", "en-us,en-x-securid" ),
+ // accept private sub tags
+ array("en-us,x-en-securid", "en-us,x-en-securid"),
+ array("en-us,en-x-securid", "en-us,en-x-securid"),
- // filter arbitrary white space
- array( "en-us, en", "en-us,en" ),
- array( "en-ca, en-us ,en", "en-ca,en-us,en" ),
+ // filter arbitrary white space
+ array("en-us, en", "en-us,en"),
+ array("en-ca, en-us ,en", "en-ca,en-us,en"),
- // handle comments
- array( " ( comment ) en-us (another comment) ", "en-us" ),
+ // handle comments
+ array(" ( comment ) en-us (another comment) ", "en-us"),
- // handle quoted pairs (embedded in comments)
- array( " ( \( start ) en-us ( \) end ) ", "en-us" ),
- array( " ( \) en-ca, \( ) en-us ( \) ,en ) ", "en-us" ),
- );
+ // handle quoted pairs (embedded in comments)
+ array(" ( \( start ) en-us ( \) end ) ", "en-us"),
+ array(" ( \) en-ca, \( ) en-us ( \) ,en ) ", "en-us"),
+ );
}
-
+
/**
* @dataProvider getBrowserLanguageData
* @group Core
@@ -447,29 +444,30 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
*/
public function testGetBrowserLanguage($useragent, $browserLanguage)
{
- $res = Piwik_Common::getBrowserLanguage( $useragent );
- $this->assertEquals( $browserLanguage, $res );
+ $res = Piwik_Common::getBrowserLanguage($useragent);
+ $this->assertEquals($browserLanguage, $res);
}
-
+
/**
* Dataprovider for testExtractCountryCodeFromBrowserLanguage
*/
- public function getCountryCodeTestData() {
-
+ public function getCountryCodeTestData()
+ {
+
return array( // browser language, valid countries, expected result
- array( "", array(), "xx" ),
- array( "", array("us" => 'amn'), "xx" ),
- array( "en", array("us" => 'amn'), "xx" ),
- array( "en-us", array("us" => 'amn'), "us" ),
- array( "en-ca", array("us" => 'amn'), "xx" ),
- array( "en-ca", array("us" => 'amn', "ca" => 'amn'), "ca" ),
- array( "fr-fr,fr-ca", array("us" => 'amn', "ca" => 'amn'), "ca" ),
- array( "fr-fr;q=1.0,fr-ca;q=0.9", array("us" => 'amn', "ca" => 'amn'), "ca" ),
- array( "fr-ca,fr;q=0.1", array("us" => 'amn', "ca" => 'amn'), "ca" ),
- array( "en-us,en;q=0.5", Piwik_Common::getCountriesList(), "us" ),
- array( "fr-ca,fr;q=0.1", array("fr" => 'eur', "us" => 'amn', "ca" => 'amn'), "ca" ),
- array( "fr-fr,fr-ca", array("fr" => 'eur', "us" => 'amn', "ca" => 'amn'), "fr" )
- );
+ array("", array(), "xx"),
+ array("", array("us" => 'amn'), "xx"),
+ array("en", array("us" => 'amn'), "xx"),
+ array("en-us", array("us" => 'amn'), "us"),
+ array("en-ca", array("us" => 'amn'), "xx"),
+ array("en-ca", array("us" => 'amn', "ca" => 'amn'), "ca"),
+ array("fr-fr,fr-ca", array("us" => 'amn', "ca" => 'amn'), "ca"),
+ array("fr-fr;q=1.0,fr-ca;q=0.9", array("us" => 'amn', "ca" => 'amn'), "ca"),
+ array("fr-ca,fr;q=0.1", array("us" => 'amn', "ca" => 'amn'), "ca"),
+ array("en-us,en;q=0.5", Piwik_Common::getCountriesList(), "us"),
+ array("fr-ca,fr;q=0.1", array("fr" => 'eur', "us" => 'amn', "ca" => 'amn'), "ca"),
+ array("fr-fr,fr-ca", array("fr" => 'eur', "us" => 'amn', "ca" => 'amn'), "fr")
+ );
}
/**
@@ -481,22 +479,23 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
public function testExtractCountryCodeFromBrowserLanguage($browserLanguage, $validCountries, $expected)
{
include 'DataFiles/LanguageToCountry.php';
-
- $this->assertEquals( $expected, Piwik_Common::extractCountryCodeFromBrowserLanguage( $browserLanguage, $validCountries, true ));
- $this->assertEquals( $expected, Piwik_Common::extractCountryCodeFromBrowserLanguage( $browserLanguage, $validCountries, false ));
+
+ $this->assertEquals($expected, Piwik_Common::extractCountryCodeFromBrowserLanguage($browserLanguage, $validCountries, true));
+ $this->assertEquals($expected, Piwik_Common::extractCountryCodeFromBrowserLanguage($browserLanguage, $validCountries, false));
}
/**
* Dataprovider for testExtractCountryCodeFromBrowserLanguageInfer
*/
- public function getCountryCodeTestDataInfer() {
-
+ public function getCountryCodeTestDataInfer()
+ {
+
return array( // browser language, valid countries, expected result (non-guess vs guess)
- array( "fr,en-us", array("us" => 'amn', "ca" => 'amn'), "us", "fr" ),
- array( "fr,en-us", array("fr" => 'eur', "us" => 'amn', "ca" => 'amn'), "us", "fr" ),
- array( "fr,fr-fr,en-us", array("fr" => 'eur', "us" => 'amn', "ca" => 'amn'), "fr", "fr" ),
- array( "fr-fr,fr,en-us", array("fr" => 'eur', "us" => 'amn', "ca" => 'amn'), "fr", "fr" )
- );
+ array("fr,en-us", array("us" => 'amn', "ca" => 'amn'), "us", "fr"),
+ array("fr,en-us", array("fr" => 'eur', "us" => 'amn', "ca" => 'amn'), "us", "fr"),
+ array("fr,fr-fr,en-us", array("fr" => 'eur', "us" => 'amn', "ca" => 'amn'), "fr", "fr"),
+ array("fr-fr,fr,en-us", array("fr" => 'eur', "us" => 'amn', "ca" => 'amn'), "fr", "fr")
+ );
}
/**
@@ -510,10 +509,10 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
include "DataFiles/LanguageToCountry.php";
// do not infer country from language
- $this->assertEquals( $expected, Piwik_Common::extractCountryCodeFromBrowserLanguage( $browserLanguage, $validCountries, $enableLanguageToCountryGuess = false ));
+ $this->assertEquals($expected, Piwik_Common::extractCountryCodeFromBrowserLanguage($browserLanguage, $validCountries, $enableLanguageToCountryGuess = false));
// infer country from language
- $this->assertEquals( $expectedInfer, Piwik_Common::extractCountryCodeFromBrowserLanguage( $browserLanguage, $validCountries, $enableLanguageToCountryGuess = true ));
+ $this->assertEquals($expectedInfer, Piwik_Common::extractCountryCodeFromBrowserLanguage($browserLanguage, $validCountries, $enableLanguageToCountryGuess = true));
}
/**
@@ -522,22 +521,22 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
public function getLanguageDataToExtract()
{
return array( // browser language, valid languages, expected result
- array( "fr-ca", array("fr"), "fr" ),
- array( "", array(), "xx" ),
- array( "", array("en"), "xx" ),
- array( "fr", array("en"), "xx" ),
- array( "en", array("en"), "en" ),
- array( "en-ca", array("en-ca"), "en-ca" ),
- array( "en-ca", array("en"), "en" ),
- array( "fr,en-us", array("fr", "en"), "fr" ),
- array( "fr,en-us", array("en", "fr"), "fr" ),
- array( "fr-fr,fr-ca", array("fr"), "fr" ),
- array( "fr-fr,fr-ca", array("fr-ca"), "fr-ca" ),
- array( "fr-fr;q=1.0,fr-ca;q=0.9", array("fr-ca"), "fr-ca" ),
- array( "fr-ca,fr;q=0.1", array("fr-ca"), "fr-ca" ),
- array( "r5,fr;q=1,de", array("fr", "de"), "fr" ),
- array( "Zen§gq1", array("en"), "xx" ),
- );
+ array("fr-ca", array("fr"), "fr"),
+ array("", array(), "xx"),
+ array("", array("en"), "xx"),
+ array("fr", array("en"), "xx"),
+ array("en", array("en"), "en"),
+ array("en-ca", array("en-ca"), "en-ca"),
+ array("en-ca", array("en"), "en"),
+ array("fr,en-us", array("fr", "en"), "fr"),
+ array("fr,en-us", array("en", "fr"), "fr"),
+ array("fr-fr,fr-ca", array("fr"), "fr"),
+ array("fr-fr,fr-ca", array("fr-ca"), "fr-ca"),
+ array("fr-fr;q=1.0,fr-ca;q=0.9", array("fr-ca"), "fr-ca"),
+ array("fr-ca,fr;q=0.1", array("fr-ca"), "fr-ca"),
+ array("r5,fr;q=1,de", array("fr", "de"), "fr"),
+ array("Zen§gq1", array("en"), "xx"),
+ );
}
/**
@@ -548,9 +547,9 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
*/
public function testExtractLanguageCodeFromBrowserLanguage($browserLanguage, $validLanguages, $expected)
{
- $this->assertEquals( $expected, Piwik_Common::extractLanguageCodeFromBrowserLanguage( $browserLanguage, $validLanguages ), "test with {$browserLanguage} failed, expected {$expected}");
+ $this->assertEquals($expected, Piwik_Common::extractLanguageCodeFromBrowserLanguage($browserLanguage, $validLanguages), "test with {$browserLanguage} failed, expected {$expected}");
}
-
+
/**
* @group Core
* @group Common
@@ -560,33 +559,27 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
include "DataFiles/SearchEngines.php";
$searchEngines = array();
- foreach($GLOBALS['Piwik_SearchEngines'] as $host => $info)
- {
- if(isset($info[2]) && $info[2] !== false)
- {
+ foreach ($GLOBALS['Piwik_SearchEngines'] as $host => $info) {
+ if (isset($info[2]) && $info[2] !== false) {
$this->assertTrue(strrpos($info[2], "{k}") !== false, $host . " search URL is not defined correctly, must contain the macro {k}");
}
- if(!array_key_exists($info[0], $searchEngines))
- {
+ if (!array_key_exists($info[0], $searchEngines)) {
$searchEngines[$info[0]] = true;
$this->assertTrue(strpos($host, '{}') === false, $host . " search URL is the master record and should not contain {}");
}
- if(isset($info[3]) && $info[3] !== false)
- {
+ if (isset($info[3]) && $info[3] !== false) {
$this->assertTrue(is_array($info[3]) || is_string($info[3]), $host . ' encoding must be either a string or an array');
- if (is_string($info[3]))
- {
+ if (is_string($info[3])) {
$this->assertTrue(trim($info[3]) !== '', $host . ' encoding cannot be an empty string');
$this->assertTrue(strpos($info[3], ' ') === false, $host . ' encoding cannot contain spaces');
}
- if (is_array($info[3]))
- {
+ if (is_array($info[3])) {
$this->assertTrue(count($info[3]) > 0, $host . ' encodings cannot be an empty array');
$this->assertTrue(strpos(serialize($info[3]), '""') === false, $host . ' encodings in array cannot be empty stringss');
$this->assertTrue(strpos(serialize($info[3]), ' ') === false, $host . ' encodings in array cannot contain spaces');
@@ -594,7 +587,7 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
}
}
}
-
+
/**
* Dataprovider for testExtractSearchEngineInformationFromUrl
*/
@@ -834,7 +827,7 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
array('name' => 'Google Images', 'keywords' => false)),
);
}
-
+
/**
* @dataProvider getSearchEngineUrls
* @group Core
@@ -867,7 +860,7 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
array('images.de.ask.com', 'images.{}.ask.com'),
);
}
-
+
/**
* @dataProvider getLossyUrls
* @group Core
diff --git a/tests/PHPUnit/Core/ConfigTest.php b/tests/PHPUnit/Core/ConfigTest.php
index 7fc7ff3f2f..ddd2619b71 100644
--- a/tests/PHPUnit/Core/ConfigTest.php
+++ b/tests/PHPUnit/Core/ConfigTest.php
@@ -59,7 +59,7 @@ class ConfigTest extends PHPUnit_Framework_TestCase
$this->assertEquals($stringWritten, $config->Category['test']);
$config->Category = array(
- 'test' => $config->Category['test'],
+ 'test' => $config->Category['test'],
'test2' => $stringWritten,
);
$this->assertEquals($stringWritten, $config->Category['test']);
@@ -145,7 +145,8 @@ class ConfigTest extends PHPUnit_Framework_TestCase
* Dataprovider for testArrayUnmerge
* @return array
*/
- public function getArrayUnmergeData() {
+ public function getArrayUnmergeData()
+ {
return array(
array('description of test', array(
array(),
@@ -211,7 +212,7 @@ class ConfigTest extends PHPUnit_Framework_TestCase
public function getDumpConfigData()
{
$header = "; <?php exit; ?> DO NOT REMOVE THIS LINE\n" .
- "; file automatically generated or modified by Piwik; you can manually override the default values in global.ini.php by redefining them in this file.\n";
+ "; file automatically generated or modified by Piwik; you can manually override the default values in global.ini.php by redefining them in this file.\n";
return array(
array('global only, not cached', array(
@@ -307,18 +308,18 @@ class ConfigTest extends PHPUnit_Framework_TestCase
array('sort, common sections', array(
array('Tracker' => array('anonymize' => '1'),
- 'General' => array('debug' => '1')),
+ 'General' => array('debug' => '1')),
array('General' => array('debug' => '0'),
- 'Tracker' => array('anonymize' => '0')),
+ 'Tracker' => array('anonymize' => '0')),
array('Tracker' => array('anonymize' => '2')),
$header . "[General]\ndebug = 1\n\n[Tracker]\nanonymize = 2\n\n",
)),
array('sort, common sections before new section', array(
array('Tracker' => array('anonymize' => '1'),
- 'General' => array('debug' => '1')),
+ 'General' => array('debug' => '1')),
array('General' => array('debug' => '0'),
- 'Tracker' => array('anonymize' => '0')),
+ 'Tracker' => array('anonymize' => '0')),
array('Segment' => array('dimension' => 'foo')),
$header . "[General]\ndebug = 1\n\n[Tracker]\nanonymize = 1\n\n[Segment]\ndimension = \"foo\"\n\n",
)),
@@ -326,7 +327,7 @@ class ConfigTest extends PHPUnit_Framework_TestCase
array('change back to default', array(
array('Tracker' => array('anonymize' => '1')),
array('Tracker' => array('anonymize' => '0'),
- 'General' => array('debug' => '1')),
+ 'General' => array('debug' => '1')),
array('Tracker' => array('anonymize' => '0')),
$header
)),
diff --git a/tests/PHPUnit/Core/CookieTest.php b/tests/PHPUnit/Core/CookieTest.php
index b64efad9b3..c3d622bbb6 100644
--- a/tests/PHPUnit/Core/CookieTest.php
+++ b/tests/PHPUnit/Core/CookieTest.php
@@ -42,11 +42,11 @@ class CookieTest extends PHPUnit_Framework_TestCase
public function testJsonSerialize($testData, $id)
{
// @see http://bugs.php.net/38680
- if(PHP_VERSION >= '5.2.0' && PHP_VERSION < '5.2.1') {
+ if (PHP_VERSION >= '5.2.0' && PHP_VERSION < '5.2.1') {
$this->markTestSkipped('see http://bugs.php.net/38680');
}
- $this->assertEquals( $testData, json_decode(json_encode($testData), $assoc = true), $id );
+ $this->assertEquals($testData, json_decode(json_encode($testData), $assoc = true), $id);
}
/**
@@ -83,10 +83,10 @@ class CookieTest extends PHPUnit_Framework_TestCase
*/
public function testSafeSerialize($id, $testData)
{
- $this->assertEquals( serialize($testData), safe_serialize($testData), $id );
- $this->assertEquals( $testData, unserialize(safe_serialize($testData)), $id );
- $this->assertSame( $testData, safe_unserialize(safe_serialize($testData)), $id );
- $this->assertSame( $testData, safe_unserialize(serialize($testData)), $id );
+ $this->assertEquals(serialize($testData), safe_serialize($testData), $id);
+ $this->assertEquals($testData, unserialize(safe_serialize($testData)), $id);
+ $this->assertSame($testData, safe_unserialize(safe_serialize($testData)), $id);
+ $this->assertSame($testData, safe_unserialize(serialize($testData)), $id);
}
/**
@@ -102,57 +102,57 @@ class CookieTest extends PHPUnit_Framework_TestCase
$testData = $tests['exp float'] = -5.0E+142;
// intentionally disabled; this doesn't work
// $this->assertEquals( safe_serialize($testData), serialize($testData) );
- $this->assertEquals( $testData, unserialize(safe_serialize($testData)) );
- $this->assertSame( $testData, safe_unserialize(safe_serialize($testData))) ;
+ $this->assertEquals($testData, unserialize(safe_serialize($testData)));
+ $this->assertSame($testData, safe_unserialize(safe_serialize($testData)));
// workaround: cast floats into strings
- $this->assertSame( $testData, safe_unserialize(serialize($testData)) );
+ $this->assertSame($testData, safe_unserialize(serialize($testData)));
$unserialized = array(
'announcement' => true,
- 'source' => array(
+ 'source' => array(
array(
'filename' => 'php-5.3.3.tar.bz2',
- 'name' => 'PHP 5.3.3 (tar.bz2)',
- 'md5' => '21ceeeb232813c10283a5ca1b4c87b48',
- 'date' => '22 July 2010',
+ 'name' => 'PHP 5.3.3 (tar.bz2)',
+ 'md5' => '21ceeeb232813c10283a5ca1b4c87b48',
+ 'date' => '22 July 2010',
),
array(
'filename' => 'php-5.3.3.tar.gz',
- 'name' => 'PHP 5.3.3 (tar.gz)',
- 'md5' => '5adf1a537895c2ec933fddd48e78d8a2',
- 'date' => '22 July 2010',
+ 'name' => 'PHP 5.3.3 (tar.gz)',
+ 'md5' => '5adf1a537895c2ec933fddd48e78d8a2',
+ 'date' => '22 July 2010',
),
),
- 'date' => '22 July 2010',
- 'version' => '5.3.3',
+ 'date' => '22 July 2010',
+ 'version' => '5.3.3',
);
$serialized = 'a:4:{s:12:"announcement";b:1;s:6:"source";a:2:{i:0;a:4:{s:8:"filename";s:17:"php-5.3.3.tar.bz2";s:4:"name";s:19:"PHP 5.3.3 (tar.bz2)";s:3:"md5";s:32:"21ceeeb232813c10283a5ca1b4c87b48";s:4:"date";s:12:"22 July 2010";}i:1;a:4:{s:8:"filename";s:16:"php-5.3.3.tar.gz";s:4:"name";s:18:"PHP 5.3.3 (tar.gz)";s:3:"md5";s:32:"5adf1a537895c2ec933fddd48e78d8a2";s:4:"date";s:12:"22 July 2010";}}s:4:"date";s:12:"22 July 2010";s:7:"version";s:5:"5.3.3";}';
- $this->assertSame( $unserialized, unserialize($serialized) );
- $this->assertEquals( $serialized, serialize($unserialized) );
+ $this->assertSame($unserialized, unserialize($serialized));
+ $this->assertEquals($serialized, serialize($unserialized));
- $this->assertSame( $unserialized, safe_unserialize($serialized) );
- $this->assertEquals( $serialized, safe_serialize($unserialized) );
- $this->assertSame( $unserialized, safe_unserialize(safe_serialize($unserialized)) );
- $this->assertEquals( $serialized, safe_serialize(safe_unserialize($serialized)) );
+ $this->assertSame($unserialized, safe_unserialize($serialized));
+ $this->assertEquals($serialized, safe_serialize($unserialized));
+ $this->assertSame($unserialized, safe_unserialize(safe_serialize($unserialized)));
+ $this->assertEquals($serialized, safe_serialize(safe_unserialize($serialized)));
$a = 'O:31:"Test_Piwik_Cookie_Phantom_Class":0:{}';
- $this->assertFalse( safe_unserialize($a), "test: unserializing an object where class not (yet) defined" );
+ $this->assertFalse(safe_unserialize($a), "test: unserializing an object where class not (yet) defined");
$a = 'O:28:"Test_Piwik_Cookie_Mock_Class":0:{}';
- $this->assertFalse( safe_unserialize($a), "test: unserializing an object where class is defined" );
+ $this->assertFalse(safe_unserialize($a), "test: unserializing an object where class is defined");
$a = 'a:1:{i:0;O:28:"Test_Piwik_Cookie_Mock_Class":0:{}}';
- $this->assertFalse( safe_unserialize($a), "test: unserializing nested object where class is defined" );
+ $this->assertFalse(safe_unserialize($a), "test: unserializing nested object where class is defined");
$a = 'a:2:{i:0;s:4:"test";i:1;O:28:"Test_Piwik_Cookie_Mock_Class":0:{}}';
- $this->assertFalse( safe_unserialize($a), "test: unserializing another nested object where class is defined" );
+ $this->assertFalse(safe_unserialize($a), "test: unserializing another nested object where class is defined");
- $a = 'O:28:"Test_Piwik_Cookie_Mock_Class":1:{s:34:"'."\0".'Test_Piwik_Cookie_Mock_Class'."\0".'name";s:4:"test";}';
- $this->assertFalse( safe_unserialize($a), "test: unserializing object with member where class is defined" );
+ $a = 'O:28:"Test_Piwik_Cookie_Mock_Class":1:{s:34:"' . "\0" . 'Test_Piwik_Cookie_Mock_Class' . "\0" . 'name";s:4:"test";}';
+ $this->assertFalse(safe_unserialize($a), "test: unserializing object with member where class is defined");
// arrays and objects cannot be used as keys, i.e., generates "Warning: Illegal offset type ..."
$a = 'a:2:{i:0;a:0:{}O:28:"Test_Piwik_Cookie_Mock_Class":0:{}s:4:"test";';
- $this->assertFalse( safe_unserialize($a), "test: unserializing with illegal key" );
+ $this->assertFalse(safe_unserialize($a), "test: unserializing with illegal key");
}
}
diff --git a/tests/PHPUnit/Core/DataTable/ArrayTest.php b/tests/PHPUnit/Core/DataTable/ArrayTest.php
index 18c44640d6..015939311a 100755
--- a/tests/PHPUnit/Core/DataTable/ArrayTest.php
+++ b/tests/PHPUnit/Core/DataTable/ArrayTest.php
@@ -14,9 +14,9 @@ class Test_Piwik_DataTable_Array extends PHPUnit_Framework_TestCase
$result = new Piwik_DataTable();
$result->addRowsFromArray(array(
- array(Piwik_DataTable_Row::COLUMNS => array('label'=> 'row1', 'col1' => 1)),
- array(Piwik_DataTable_Row::COLUMNS => array('label'=> 'row2', 'col1' => 2))
- ));
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'row1', 'col1' => 1)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'row2', 'col1' => 2))
+ ));
return $result;
}
@@ -38,7 +38,7 @@ class Test_Piwik_DataTable_Array extends PHPUnit_Framework_TestCase
{
$dataTable = new Piwik_DataTable_Array();
- $subDataTableArray1 = $this->createInstanceWithDataTables();
+ $subDataTableArray1 = $this->createInstanceWithDataTables();
$dataTable->addTable($subDataTableArray1, 'subArray1');
$subDataTableArray2 = $this->createInstanceWithDataTables();
diff --git a/tests/PHPUnit/Core/DataTable/Filter/AddSummaryRowTest.php b/tests/PHPUnit/Core/DataTable/Filter/AddSummaryRowTest.php
index b8da1b498e..5071d2b815 100644
--- a/tests/PHPUnit/Core/DataTable/Filter/AddSummaryRowTest.php
+++ b/tests/PHPUnit/Core/DataTable/Filter/AddSummaryRowTest.php
@@ -8,7 +8,7 @@
class DataTable_Filter_AddSummaryRowTest extends PHPUnit_Framework_TestCase
{
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -22,9 +22,9 @@ class DataTable_Filter_AddSummaryRowTest extends PHPUnit_Framework_TestCase
$this->assertEquals(5, $table->getRowsCount());
$this->assertTrue(Piwik_DataTable_Row::isEqual($table->getLastRow(), $this->getRow4()));
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -36,14 +36,14 @@ class DataTable_Filter_AddSummaryRowTest extends PHPUnit_Framework_TestCase
$filter = new Piwik_DataTable_Filter_AddSummaryRow($table, 2);
$filter->filter($table);
$this->assertEquals(3, $table->getRowsCount());
- $expectedRow = new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'nb' => 111)));
+ $expectedRow = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'nb' => 111)));
$this->assertTrue(Piwik_DataTable_Row::isEqual($table->getLastRow(), $expectedRow));
// check that column 'label' is forced to be first in summary row
$this->assertEquals(array_keys($table->getLastRow()->getColumns()), array_keys($expectedRow->getColumns()));
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -57,9 +57,9 @@ class DataTable_Filter_AddSummaryRowTest extends PHPUnit_Framework_TestCase
$this->assertEquals(5, $table->getRowsCount());
$this->assertTrue(Piwik_DataTable_Row::isEqual($table->getLastRow(), $this->getRow4()));
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -73,12 +73,12 @@ class DataTable_Filter_AddSummaryRowTest extends PHPUnit_Framework_TestCase
$filter2 = new Piwik_DataTable_Filter_AddSummaryRow($table, 2);
$filter2->filter($table);
$this->assertEquals(3, $table->getRowsCount());
- $expectedRow = new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'nb' => 111)));
+ $expectedRow = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'nb' => 111)));
$this->assertTrue(Piwik_DataTable_Row::isEqual($table->getLastRow(), $expectedRow));
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -90,25 +90,25 @@ class DataTable_Filter_AddSummaryRowTest extends PHPUnit_Framework_TestCase
$table1 = $this->getDataTableCount5();
$filter = new Piwik_DataTable_Filter_AddSummaryRow($table1, 3);
$filter->filter($table1);
-
+
// row0, row1, rowSummary2
$table2 = $this->getDataTableCount5();
$filter = new Piwik_DataTable_Filter_AddSummaryRow($table2, 2);
$filter->filter($table2);
-
+
// we expect row0+row0, row1+row1, row2, rowSummary1+rowSummary2
$expectedTable = new Piwik_DataTable;
- $expectedTable->addRow( new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>'amazon', 'nb' => 20000) )));
- $expectedTable->addRow( new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>'yahoo', 'nb' => 2000) )));
- $expectedTable->addRow( new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>'piwik', 'nb' => 100) )));
- $expectedTable->addRow( new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'nb' => 122) )));
-
+ $expectedTable->addRow(new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => 'amazon', 'nb' => 20000))));
+ $expectedTable->addRow(new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'nb' => 2000))));
+ $expectedTable->addRow(new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => 'piwik', 'nb' => 100))));
+ $expectedTable->addRow(new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'nb' => 122))));
+
$table1->addDataTable($table2);
$this->assertTrue(Piwik_DataTable::isEqual($expectedTable, $table1));
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -120,26 +120,26 @@ class DataTable_Filter_AddSummaryRowTest extends PHPUnit_Framework_TestCase
$table1 = $this->getDataTableCount5();
$filter = new Piwik_DataTable_Filter_AddSummaryRow($table1, 3);
$filter->filter($table1);
-
+
// row0, row1, row2, row3, row4
$table2 = $this->getDataTableCount5();
-
+
// we expect row0+row0, row1+row1, row2+row2, row3, row4, rowSummary1
$expectedTable = new Piwik_DataTable;
- $expectedTable->addRow( new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>'amazon', 'nb' => 20000) )));
- $expectedTable->addRow( new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>'yahoo', 'nb' => 2000) )));
- $expectedTable->addRow( new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>'piwik', 'nb' => 200) )));
- $expectedTable->addRow( $this->getRow3());
- $expectedTable->addRow( $this->getRow4());
- $expectedTable->addRow( new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'nb' => 11))));
-
+ $expectedTable->addRow(new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => 'amazon', 'nb' => 20000))));
+ $expectedTable->addRow(new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'nb' => 2000))));
+ $expectedTable->addRow(new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => 'piwik', 'nb' => 200))));
+ $expectedTable->addRow($this->getRow3());
+ $expectedTable->addRow($this->getRow4());
+ $expectedTable->addRow(new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'nb' => 11))));
+
$table1->addDataTable($table2);
$this->assertTrue(Piwik_DataTable::isEqual($expectedTable, $table1));
-
+
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -148,19 +148,19 @@ class DataTable_Filter_AddSummaryRowTest extends PHPUnit_Framework_TestCase
public function testWhenRowsInRandomOrderButSortSpecifiedShouldComputeSummaryRowAfterSort()
{
$table = new Piwik_DataTable;
- $table->addRow( $this->getRow3() );
- $table->addRow( $this->getRow2() );
- $table->addRow( $this->getRow4() );
- $table->addRow( $this->getRow1() );
- $table->addRow( $this->getRow0() );
-
+ $table->addRow($this->getRow3());
+ $table->addRow($this->getRow2());
+ $table->addRow($this->getRow4());
+ $table->addRow($this->getRow1());
+ $table->addRow($this->getRow0());
+
$filter = new Piwik_DataTable_Filter_AddSummaryRow($table, 2, Piwik_DataTable::LABEL_SUMMARY_ROW, $columnToSortBy = 'nb');
$filter->filter($table);
$this->assertEquals(3, $table->getRowsCount());
- $expectedRow = new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'nb' => 111)));
+ $expectedRow = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'nb' => 111)));
$this->assertTrue(Piwik_DataTable_Row::isEqual($table->getLastRow(), $expectedRow));
}
-
+
/**
* Returns table used for the tests
*
@@ -169,31 +169,36 @@ class DataTable_Filter_AddSummaryRowTest extends PHPUnit_Framework_TestCase
protected function getDataTableCount5()
{
$table = new Piwik_DataTable;
- $table->addRow( $this->getRow0() );
- $table->addRow( $this->getRow1() );
- $table->addRow( $this->getRow2() );
- $table->addRow( $this->getRow3() );
- $table->addRow( $this->getRow4() );
+ $table->addRow($this->getRow0());
+ $table->addRow($this->getRow1());
+ $table->addRow($this->getRow2());
+ $table->addRow($this->getRow3());
+ $table->addRow($this->getRow4());
return $table;
}
+
protected function getRow0()
{
- return new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('nb' => 10000, 'label'=>'amazon')));
+ return new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('nb' => 10000, 'label' => 'amazon')));
}
+
protected function getRow1()
{
- return new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>'yahoo', 'nb' => 1000)));
+ return new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'nb' => 1000)));
}
+
protected function getRow2()
{
- return new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>'piwik', 'nb' => 100)));
+ return new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => 'piwik', 'nb' => 100)));
}
+
protected function getRow3()
{
- return new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label'=>'ask', 'nb' => 10)));
+ return new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'nb' => 10)));
}
+
protected function getRow4()
{
- return new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('nb' => 1, 'label'=>'google')));
+ return new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => array('nb' => 1, 'label' => 'google')));
}
}
diff --git a/tests/PHPUnit/Core/DataTable/Filter/ExcludeLowPopulationTest.php b/tests/PHPUnit/Core/DataTable/Filter/ExcludeLowPopulationTest.php
index 8cc05b6b0c..5ebb772c72 100644
--- a/tests/PHPUnit/Core/DataTable/Filter/ExcludeLowPopulationTest.php
+++ b/tests/PHPUnit/Core/DataTable/Filter/ExcludeLowPopulationTest.php
@@ -12,19 +12,19 @@ class DataTable_Filter_ExcludeLowPopulationTest extends PHPUnit_Framework_TestCa
$table = new Piwik_DataTable;
$table->addRowsFromArray(
array(
- array(Piwik_DataTable_Row::COLUMNS => array('label'=>'zero', 'count' => 0)),
- array(Piwik_DataTable_Row::COLUMNS => array('label'=>'one', 'count' => 1)),
- array(Piwik_DataTable_Row::COLUMNS => array('label'=>'onedotfive', 'count' => 1.5)),
- array(Piwik_DataTable_Row::COLUMNS => array('label'=>'ten', 'count' => 10)),
- array(Piwik_DataTable_Row::COLUMNS => array('label'=>'ninety', 'count' => 90)),
- array(Piwik_DataTable_Row::COLUMNS => array('label'=>'hundred', 'count' => 100)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'zero', 'count' => 0)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'one', 'count' => 1)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'onedotfive', 'count' => 1.5)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ten', 'count' => 10)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ninety', 'count' => 90)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'hundred', 'count' => 100)),
)
);
return $table;
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -32,15 +32,15 @@ class DataTable_Filter_ExcludeLowPopulationTest extends PHPUnit_Framework_TestCa
*/
public function testStandardTable()
{
- $table = $this->getTestDataTable();
+ $table = $this->getTestDataTable();
$filter = new Piwik_DataTable_Filter_ExcludeLowPopulation($table, 'count', 1.1);
$filter->filter($table);
$this->assertEquals(4, $table->getRowsCount());
$this->assertEquals(array(1.5, 10, 90, 100), $table->getColumn('count'));
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -48,14 +48,14 @@ class DataTable_Filter_ExcludeLowPopulationTest extends PHPUnit_Framework_TestCa
*/
public function testFilterEqualOneDoesFilter()
{
- $table = $this->getTestDataTable();
+ $table = $this->getTestDataTable();
$filter = new Piwik_DataTable_Filter_ExcludeLowPopulation($table, 'count', 1);
$filter->filter($table);
$this->assertEquals(5, $table->getRowsCount());
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -63,15 +63,15 @@ class DataTable_Filter_ExcludeLowPopulationTest extends PHPUnit_Framework_TestCa
*/
public function testFilterEqualZeroDoesFilter()
{
- $table = $this->getTestDataTable();
+ $table = $this->getTestDataTable();
$filter = new Piwik_DataTable_Filter_ExcludeLowPopulation($table, 'count', 0);
$filter->filter($table);
$this->assertEquals(3, $table->getRowsCount());
$this->assertEquals(array(10, 90, 100), $table->getColumn('count'));
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -79,18 +79,17 @@ class DataTable_Filter_ExcludeLowPopulationTest extends PHPUnit_Framework_TestCa
*/
public function testFilterSpecifyExcludeLowPopulationThresholdDoesFilter()
{
- $table = $this->getTestDataTable();
+ $table = $this->getTestDataTable();
$filter = new Piwik_DataTable_Filter_ExcludeLowPopulation($table, 'count', 0, 0.4); //40%
$filter->filter($table);
$this->assertEquals(2, $table->getRowsCount());
$this->assertEquals(array(90, 100), $table->getColumn('count'));
}
-
-
+
/**
* Test to exclude low population filter
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -98,36 +97,36 @@ class DataTable_Filter_ExcludeLowPopulationTest extends PHPUnit_Framework_TestCa
*/
public function testFilterLowpop1()
{
-
+
$idcol = Piwik_DataTable_Row::COLUMNS;
-
+
$table = new Piwik_DataTable();
$rows = array(
- array( $idcol => array('label'=>'google', 'nb_visits' => 897)),//0
- array( $idcol => array('label'=>'ask', 'nb_visits' => -152)),//1
- array( $idcol => array('label'=>'piwik', 'nb_visits' => 1.5)),//2
- array( $idcol => array('label'=>'piwik2', 'nb_visits' => 1.4)),//2
- array( $idcol => array('label'=>'yahoo', 'nb_visits' => 154)),//3
- array( $idcol => array('label'=>'amazon', 'nb_visits' => 30)),//4
- array( $idcol => array('label'=>'238949', 'nb_visits' => 0)),//5
- array( $idcol => array('label'=>'Q*(%&*', 'nb_visits' => 1)),//6
- array( $idcol => array('label'=>'Q*(%&*2', 'nb_visits' => -1.5)),//6
+ array($idcol => array('label' => 'google', 'nb_visits' => 897)), //0
+ array($idcol => array('label' => 'ask', 'nb_visits' => -152)), //1
+ array($idcol => array('label' => 'piwik', 'nb_visits' => 1.5)), //2
+ array($idcol => array('label' => 'piwik2', 'nb_visits' => 1.4)), //2
+ array($idcol => array('label' => 'yahoo', 'nb_visits' => 154)), //3
+ array($idcol => array('label' => 'amazon', 'nb_visits' => 30)), //4
+ array($idcol => array('label' => '238949', 'nb_visits' => 0)), //5
+ array($idcol => array('label' => 'Q*(%&*', 'nb_visits' => 1)), //6
+ array($idcol => array('label' => 'Q*(%&*2', 'nb_visits' => -1.5)), //6
);
- $table->addRowsFromArray( $rows );
-
+ $table->addRowsFromArray($rows);
+
$expectedtable = new Piwik_DataTable();
$rows = array(
- array( $idcol => array('label'=>'google', 'nb_visits' => 897)),//0
- array( $idcol => array('label'=>'piwik', 'nb_visits' => 1.5)),//2
- array( $idcol => array('label'=>'piwik2', 'nb_visits' => 1.4)),//2
- array( $idcol => array('label'=>'yahoo', 'nb_visits' => 154)),//3
- array( $idcol => array('label'=>'amazon', 'nb_visits' => 30)),//4
+ array($idcol => array('label' => 'google', 'nb_visits' => 897)), //0
+ array($idcol => array('label' => 'piwik', 'nb_visits' => 1.5)), //2
+ array($idcol => array('label' => 'piwik2', 'nb_visits' => 1.4)), //2
+ array($idcol => array('label' => 'yahoo', 'nb_visits' => 154)), //3
+ array($idcol => array('label' => 'amazon', 'nb_visits' => 30)), //4
);
- $expectedtable->addRowsFromArray( $rows );
-
+ $expectedtable->addRowsFromArray($rows);
+
$filter = new Piwik_DataTable_Filter_ExcludeLowPopulation($table, 'nb_visits', 1.4);
$filter->filter($table);
-
+
$this->assertTrue(Piwik_DataTable::isEqual($table, $expectedtable));
}
}
diff --git a/tests/PHPUnit/Core/DataTable/Filter/LimitTest.php b/tests/PHPUnit/Core/DataTable/Filter/LimitTest.php
index ccd2a17577..37a9e20d58 100644
--- a/tests/PHPUnit/Core/DataTable/Filter/LimitTest.php
+++ b/tests/PHPUnit/Core/DataTable/Filter/LimitTest.php
@@ -17,23 +17,23 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
$table = new Piwik_DataTable;
$idcol = Piwik_DataTable_Row::COLUMNS;
$rows = array(
- array($idcol => array('label'=> 'google', 'idRow' => 0)),
- array($idcol => array('label'=> 'ask', 'idRow' => 1)),
- array($idcol => array('label'=> 'piwik', 'idRow' => 2)),
- array($idcol => array('label'=> 'yahoo', 'idRow' => 3)),
- array($idcol => array('label'=> 'amazon', 'idRow' => 4)),
- array($idcol => array('label'=> '238949', 'idRow' => 5)),
- array($idcol => array('label'=> 'test', 'idRow' => 6)),
- array($idcol => array('label'=> 'amazing', 'idRow' => 7)),
- array($idcol => array('label'=> 'great', 'idRow' => 8)),
- Piwik_DataTable::ID_SUMMARY_ROW => array($idcol => array('label'=> 'summary row', 'idRow' => 9)),
+ array($idcol => array('label' => 'google', 'idRow' => 0)),
+ array($idcol => array('label' => 'ask', 'idRow' => 1)),
+ array($idcol => array('label' => 'piwik', 'idRow' => 2)),
+ array($idcol => array('label' => 'yahoo', 'idRow' => 3)),
+ array($idcol => array('label' => 'amazon', 'idRow' => 4)),
+ array($idcol => array('label' => '238949', 'idRow' => 5)),
+ array($idcol => array('label' => 'test', 'idRow' => 6)),
+ array($idcol => array('label' => 'amazing', 'idRow' => 7)),
+ array($idcol => array('label' => 'great', 'idRow' => 8)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array($idcol => array('label' => 'summary row', 'idRow' => 9)),
);
- $table->addRowsFromArray( $rows );
+ $table->addRowsFromArray($rows);
return $table;
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -51,9 +51,9 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
$this->assertEquals(4, $table->getLastRow()->getColumn('idRow'));
$this->assertEquals(10, $table->getRowsCountBeforeLimitFilter());
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -71,9 +71,9 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
$this->assertEquals(8, $table->getLastRow()->getColumn('idRow'));
$this->assertEquals(10, $table->getRowsCountBeforeLimitFilter());
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -92,9 +92,9 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
$this->assertEquals(9, $table->getLastRow()->getColumn('idRow'));
$this->assertEquals(10, $table->getRowsCountBeforeLimitFilter());
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -113,9 +113,9 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
$this->assertEquals(9, $table->getLastRow()->getColumn('idRow'));
$this->assertEquals(10, $table->getRowsCountBeforeLimitFilter());
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -132,9 +132,9 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
$this->assertEquals(9, $table->getLastRow()->getColumn('idRow'));
$this->assertEquals(10, $table->getRowsCountBeforeLimitFilter());
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -152,9 +152,9 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
$this->assertEquals(9, $table->getLastRow()->getColumn('idRow'));
$this->assertEquals(10, $table->getRowsCountBeforeLimitFilter());
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -174,7 +174,7 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
}
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -192,9 +192,9 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
$this->assertEquals(9, $table->getLastRow()->getColumn('idRow'));
$this->assertEquals(10, $table->getRowsCountBeforeLimitFilter());
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -210,9 +210,9 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
$this->assertEquals(0, $table->getRowsCount());
$this->assertEquals(10, $table->getRowsCountBeforeLimitFilter());
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -228,10 +228,10 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
$this->assertEquals(0, $table->getRowsCount());
$this->assertEquals(10, $table->getRowsCountBeforeLimitFilter());
}
-
+
/**
* Test to filter a table with a offset, limit
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -240,37 +240,37 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
public function testFilterOffsetLimit()
{
$table = new Piwik_DataTable;
-
+
$idcol = Piwik_DataTable_Row::COLUMNS;
-
+
$rows = array(
- array( $idcol => array('label'=>'google')),//0
- array( $idcol => array('label'=>'ask')),//1
- array( $idcol => array('label'=>'piwik')),//2
- array( $idcol => array('label'=>'yahoo')),//3
- array( $idcol => array('label'=>'amazon')),//4
- array( $idcol => array('label'=>'238975247578949')),//5
- array( $idcol => array('label'=>'Q*(%&*("$&%*(&"$*")"))'))//6
+ array($idcol => array('label' => 'google')), //0
+ array($idcol => array('label' => 'ask')), //1
+ array($idcol => array('label' => 'piwik')), //2
+ array($idcol => array('label' => 'yahoo')), //3
+ array($idcol => array('label' => 'amazon')), //4
+ array($idcol => array('label' => '238975247578949')), //5
+ array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')) //6
);
-
- $table->addRowsFromArray( $rows );
-
+
+ $table->addRowsFromArray($rows);
+
$expectedtable = clone $table;
- $expectedtable->deleteRows(array(0,1,6));
-
+ $expectedtable->deleteRows(array(0, 1, 6));
+
$filter = new Piwik_DataTable_Filter_Limit($table, 2, 4);
$filter->filter($table);
-
- $colAfter=$colExpected=array();
- foreach($table->getRows() as $row) $colAfter[] = $row->getColumn('label');
- foreach($expectedtable->getRows() as $row) $colExpected[] = $row->getColumn('label');
-
+
+ $colAfter = $colExpected = array();
+ foreach ($table->getRows() as $row) $colAfter[] = $row->getColumn('label');
+ foreach ($expectedtable->getRows() as $row) $colExpected[] = $row->getColumn('label');
+
$this->assertEquals(array_values($expectedtable->getRows()), array_values($table->getRows()));
}
-
+
/**
* Test to filter a column with a offset, limit off bound
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -279,37 +279,37 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
public function testFilterOffsetLimitOffbound()
{
$table = new Piwik_DataTable;
-
+
$idcol = Piwik_DataTable_Row::COLUMNS;
-
+
$rows = array(
- array( $idcol => array('label'=>'google')),//0
- array( $idcol => array('label'=>'ask')),//1
- array( $idcol => array('label'=>'piwik')),//2
- array( $idcol => array('label'=>'yahoo')),//3
- array( $idcol => array('label'=>'amazon')),//4
- array( $idcol => array('label'=>'238975247578949')),//5
- array( $idcol => array('label'=>'Q*(%&*("$&%*(&"$*")"))'))//6
+ array($idcol => array('label' => 'google')), //0
+ array($idcol => array('label' => 'ask')), //1
+ array($idcol => array('label' => 'piwik')), //2
+ array($idcol => array('label' => 'yahoo')), //3
+ array($idcol => array('label' => 'amazon')), //4
+ array($idcol => array('label' => '238975247578949')), //5
+ array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')) //6
);
-
- $table->addRowsFromArray( $rows );
-
+
+ $table->addRowsFromArray($rows);
+
$expectedtable = clone $table;
- $expectedtable->deleteRows(array(0,1,3,4,5,6));
-
+ $expectedtable->deleteRows(array(0, 1, 3, 4, 5, 6));
+
$filter = new Piwik_DataTable_Filter_Limit($table, 2, 1);
$filter->filter($table);
-
- $colAfter=$colExpected=array();
- foreach($table->getRows() as $row) $colAfter[] = $row->getColumn('label');
- foreach($expectedtable->getRows() as $row) $colExpected[] = $row->getColumn('label');
-
+
+ $colAfter = $colExpected = array();
+ foreach ($table->getRows() as $row) $colAfter[] = $row->getColumn('label');
+ foreach ($expectedtable->getRows() as $row) $colExpected[] = $row->getColumn('label');
+
$this->assertEquals(array_values($expectedtable->getRows()), array_values($table->getRows()));
}
-
+
/**
* Test to filter a column with a offset, limit 2
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -318,36 +318,36 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
public function testFilterOffsetLimit2()
{
$table = new Piwik_DataTable;
-
+
$idcol = Piwik_DataTable_Row::COLUMNS;
-
+
$rows = array(
- array( $idcol => array('label'=>'google')),//0
- array( $idcol => array('label'=>'ask')),//1
- array( $idcol => array('label'=>'piwik')),//2
- array( $idcol => array('label'=>'yahoo')),//3
- array( $idcol => array('label'=>'amazon')),//4
- array( $idcol => array('label'=>'238975247578949')),//5
- array( $idcol => array('label'=>'Q*(%&*("$&%*(&"$*")"))'))//6
+ array($idcol => array('label' => 'google')), //0
+ array($idcol => array('label' => 'ask')), //1
+ array($idcol => array('label' => 'piwik')), //2
+ array($idcol => array('label' => 'yahoo')), //3
+ array($idcol => array('label' => 'amazon')), //4
+ array($idcol => array('label' => '238975247578949')), //5
+ array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')) //6
);
-
- $table->addRowsFromArray( $rows );
-
+
+ $table->addRowsFromArray($rows);
+
$expectedtable = clone $table;
-
+
$filter = new Piwik_DataTable_Filter_Limit($table, 0, 15);
$filter->filter($table);
-
- $colAfter=$colExpected=array();
- foreach($table->getRows() as $row) $colAfter[] = $row->getColumn('label');
- foreach($expectedtable->getRows() as $row) $colExpected[] = $row->getColumn('label');
-
+
+ $colAfter = $colExpected = array();
+ foreach ($table->getRows() as $row) $colAfter[] = $row->getColumn('label');
+ foreach ($expectedtable->getRows() as $row) $colExpected[] = $row->getColumn('label');
+
$this->assertEquals(array_values($expectedtable->getRows()), array_values($table->getRows()));
}
-
+
/**
* Test to filter a column with a offset, limit 3
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -356,31 +356,31 @@ class DataTable_Filter_LimitTest extends PHPUnit_Framework_TestCase
public function testFilterOffsetLimit3()
{
$table = new Piwik_DataTable;
-
+
$idcol = Piwik_DataTable_Row::COLUMNS;
-
+
$rows = array(
- array( $idcol => array('label'=>'google')),//0
- array( $idcol => array('label'=>'ask')),//1
- array( $idcol => array('label'=>'piwik')),//2
- array( $idcol => array('label'=>'yahoo')),//3
- array( $idcol => array('label'=>'amazon')),//4
- array( $idcol => array('label'=>'238975247578949')),//5
- array( $idcol => array('label'=>'Q*(%&*("$&%*(&"$*")"))'))//6
+ array($idcol => array('label' => 'google')), //0
+ array($idcol => array('label' => 'ask')), //1
+ array($idcol => array('label' => 'piwik')), //2
+ array($idcol => array('label' => 'yahoo')), //3
+ array($idcol => array('label' => 'amazon')), //4
+ array($idcol => array('label' => '238975247578949')), //5
+ array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')) //6
);
-
- $table->addRowsFromArray( $rows );
+
+ $table->addRowsFromArray($rows);
$expectedtable = new Piwik_DataTable;
-
+
$filter = new Piwik_DataTable_Filter_Limit($table, 8, 15);
$filter->filter($table);
-
- $colAfter=$colExpected=array();
- foreach($table->getRows() as $row) $colAfter[] = $row->getColumn('label');
- foreach($expectedtable->getRows() as $row) $colExpected[] = $row->getColumn('label');
-
+
+ $colAfter = $colExpected = array();
+ foreach ($table->getRows() as $row) $colAfter[] = $row->getColumn('label');
+ foreach ($expectedtable->getRows() as $row) $colExpected[] = $row->getColumn('label');
+
$this->assertEquals(array_values($expectedtable->getRows()), array_values($table->getRows()));
}
-
+
}
diff --git a/tests/PHPUnit/Core/DataTable/Filter/PatternRecursiveTest.php b/tests/PHPUnit/Core/DataTable/Filter/PatternRecursiveTest.php
index b107a8063a..5efc2dac7a 100644
--- a/tests/PHPUnit/Core/DataTable/Filter/PatternRecursiveTest.php
+++ b/tests/PHPUnit/Core/DataTable/Filter/PatternRecursiveTest.php
@@ -14,25 +14,25 @@ class DataTable_Filter_PatternRecursiveTest extends PHPUnit_Framework_TestCase
protected function getTable()
{
$subtableAskPath1 = new Piwik_DataTable();
- $subtableAskPath1->addRowsFromArray(array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'path1-index-page.html') ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'another-page') ),
- ));
+ $subtableAskPath1->addRowsFromArray(array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'path1-index-page.html')),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'another-page')),
+ ));
$subtableAsk = new Piwik_DataTable();
- $subtableAsk->addRowsFromArray(array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'path1'),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subtableAskPath1),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'index.html') ),
- ));
+ $subtableAsk->addRowsFromArray(array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'path1'),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subtableAskPath1),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'index.html')),
+ ));
$table = new Piwik_DataTable;
$rows = array(
- array( Piwik_DataTable_Row::COLUMNS => array('label'=>'http://www.ask.com'),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subtableAsk),
- array( Piwik_DataTable_Row::COLUMNS => array('label'=>'yahoo')),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'http://www.ask.com'),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subtableAsk),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo')),
);
- $table->addRowsFromArray( $rows );
+ $table->addRowsFromArray($rows);
return $table;
}
diff --git a/tests/PHPUnit/Core/DataTable/Filter/PatternTest.php b/tests/PHPUnit/Core/DataTable/Filter/PatternTest.php
index 4536a2f611..d90de04a44 100644
--- a/tests/PHPUnit/Core/DataTable/Filter/PatternTest.php
+++ b/tests/PHPUnit/Core/DataTable/Filter/PatternTest.php
@@ -14,17 +14,17 @@ class DataTable_Filter_PatternTest extends PHPUnit_Framework_TestCase
{
return array(
array(array('ask', array(1))),
- array(array('oo', array(0,3))),
+ array(array('oo', array(0, 3))),
array(array('^yah', array(3))),
array(array('\*', array(6))),
array(array('2/4', array(5))),
- array(array('amazon|yahoo', array(3,4))),
+ array(array('amazon|yahoo', array(3, 4))),
);
}
/**
* Test to filter a column with a pattern
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -34,21 +34,21 @@ class DataTable_Filter_PatternTest extends PHPUnit_Framework_TestCase
public function testFilterPattern($test)
{
$table = new Piwik_DataTable;
-
+
$idcol = Piwik_DataTable_Row::COLUMNS;
-
+
$rows = array(
- array( $idcol => array('label'=>'google')),
- array( $idcol => array('label'=>'ask')),
- array( $idcol => array('label'=>'piwik')),
- array( $idcol => array('label'=>'yahoo')),
- array( Piwik_DataTable_Row::METADATA => array('label'=>'amazon')),
- array( $idcol => array('label'=>'2389752/47578949')),
- array( $idcol => array('label'=>'Q*(%&*("$&%*(&"$*")"))'))
+ array($idcol => array('label' => 'google')),
+ array($idcol => array('label' => 'ask')),
+ array($idcol => array('label' => 'piwik')),
+ array($idcol => array('label' => 'yahoo')),
+ array(Piwik_DataTable_Row::METADATA => array('label' => 'amazon')),
+ array($idcol => array('label' => '2389752/47578949')),
+ array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))'))
);
- $table->addRowsFromArray( $rows );
+ $table->addRowsFromArray($rows);
$rowIds = array_keys($rows);
-
+
$pattern = $test[0];
$expectedRows = $test[1];
$rowToDelete = array_diff($rowIds, $expectedRows);
diff --git a/tests/PHPUnit/Core/DataTable/Filter/RangeCheckTest.php b/tests/PHPUnit/Core/DataTable/Filter/RangeCheckTest.php
index 53c91c8520..397333ce29 100644
--- a/tests/PHPUnit/Core/DataTable/Filter/RangeCheckTest.php
+++ b/tests/PHPUnit/Core/DataTable/Filter/RangeCheckTest.php
@@ -8,7 +8,7 @@
class DataTable_Filter_RangeCheckTest extends PHPUnit_Framework_TestCase
{
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
@@ -18,12 +18,12 @@ class DataTable_Filter_RangeCheckTest extends PHPUnit_Framework_TestCase
{
$table = new Piwik_DataTable();
$table->addRowsFromArray(array(
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => 3)), // --> 5
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 5)), // --> 5
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'test', 'count' => 7.5)), // --> 7.5
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'google', 'count' => 9)), // --> 9
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'count' => 10.1) // --> 10
- )));
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => 3)), // --> 5
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 5)), // --> 5
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'test', 'count' => 7.5)), // --> 7.5
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'google', 'count' => 9)), // --> 9
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'count' => 10.1) // --> 10
+ )));
$filter = new Piwik_DataTable_Filter_RangeCheck($table, 'count', 5, 10);
$filter->filter($table);
$expectedOrder = array(5, 5, 7.5, 9, 10);
@@ -42,13 +42,13 @@ class DataTable_Filter_RangeCheckTest extends PHPUnit_Framework_TestCase
{
$table = new Piwik_DataTable();
$table->addRowsFromArray(array(
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => '3')), // 3 is below minimum
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 'test')), // no number is below minimum
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'test', 'count' => 0x1232)), // number is over maximum
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'piwik', 'count' => 0x005)), // converted to 5 is ok
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'google', 'count' => '9test')), // converted to 9 is ok, so string will be kept
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'count' => 'test4') // can't be converted to number
- )));
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => '3')), // 3 is below minimum
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 'test')), // no number is below minimum
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'test', 'count' => 0x1232)), // number is over maximum
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'piwik', 'count' => 0x005)), // converted to 5 is ok
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'google', 'count' => '9test')), // converted to 9 is ok, so string will be kept
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'count' => 'test4') // can't be converted to number
+ )));
$filter = new Piwik_DataTable_Filter_RangeCheck($table, 'count', 3.97, 10);
$filter->filter($table);
$expectedOrder = array(3.97, 3.97, 10, 5, '9test', 3.97);
diff --git a/tests/PHPUnit/Core/DataTable/Filter/SortTest.php b/tests/PHPUnit/Core/DataTable/Filter/SortTest.php
index ca08a3596d..eb793bd136 100644
--- a/tests/PHPUnit/Core/DataTable/Filter/SortTest.php
+++ b/tests/PHPUnit/Core/DataTable/Filter/SortTest.php
@@ -8,124 +8,124 @@
class DataTable_Filter_SortTest extends PHPUnit_Framework_TestCase
{
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
* @group DataTable_Filter_Sort
*/
- public function testNormalSortDescending ()
+ public function testNormalSortDescending()
{
$table = new Piwik_DataTable();
$table->addRowsFromArray(array(
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => 100)),
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 0)),
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'count' => 10)
- )));
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => 100)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 0)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'count' => 10)
+ )));
$filter = new Piwik_DataTable_Filter_Sort($table, 'count', 'desc');
$filter->filter($table);
- $expectedOrder = array('ask' , 'yahoo' , 'nintendo');
+ $expectedOrder = array('ask', 'yahoo', 'nintendo');
$this->assertEquals($expectedOrder, $table->getColumn('label'));
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
* @group DataTable_Filter_Sort
*/
- public function testNormalSortAscending ()
+ public function testNormalSortAscending()
{
$table = new Piwik_DataTable();
$table->addRowsFromArray(array(
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => 100.5)),
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 0.5)),
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'count' => 10.5)
- )));
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => 100.5)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 0.5)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'yahoo', 'count' => 10.5)
+ )));
$filter = new Piwik_DataTable_Filter_Sort($table, 'count', 'asc');
$filter->filter($table);
- $expectedOrder = array('nintendo' , 'yahoo' , 'ask');
+ $expectedOrder = array('nintendo', 'yahoo', 'ask');
$this->assertEquals($expectedOrder, $table->getColumn('label'));
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
* @group DataTable_Filter_Sort
*/
- public function testMissingColumnValuesShouldAppearLastAfterSortAsc ()
+ public function testMissingColumnValuesShouldAppearLastAfterSortAsc()
{
$table = new Piwik_DataTable();
$table->addRowsFromArray(array(
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 1)),
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nocolumn')),
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nocolumnbis')),
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => 2)),
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'amazing')),
- Piwik_DataTable::ID_SUMMARY_ROW => array(Piwik_DataTable_Row::COLUMNS => array('label' => 'summary' , 'count' => 10)
- )));
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 1)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nocolumn')),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nocolumnbis')),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => 2)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'amazing')),
+ Piwik_DataTable::ID_SUMMARY_ROW => array(Piwik_DataTable_Row::COLUMNS => array('label' => 'summary', 'count' => 10)
+ )));
$filter = new Piwik_DataTable_Filter_Sort($table, 'count', 'asc');
$filter->filter($table);
- $expectedOrder = array('nintendo' , 'ask' , 'amazing' , 'nocolumnbis' , 'nocolumn' , 'summary');
+ $expectedOrder = array('nintendo', 'ask', 'amazing', 'nocolumnbis', 'nocolumn', 'summary');
$this->assertEquals($expectedOrder, $table->getColumn('label'));
}
-
+
/**
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
* @group DataTable_Filter_Sort
*/
- public function testMissingColumnValuesShouldAppearLastAfterSortDesc ()
+ public function testMissingColumnValuesShouldAppearLastAfterSortDesc()
{
$table = new Piwik_DataTable();
$table->addRowsFromArray(array(
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo' , 'count' => 1)),
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask' , 'count' => 2)),
- array(Piwik_DataTable_Row::COLUMNS => array('label' => 'amazing')),
- Piwik_DataTable::ID_SUMMARY_ROW => array(Piwik_DataTable_Row::COLUMNS => array('label' => 'summary' , 'count' => 10)
- )));
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'nintendo', 'count' => 1)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'count' => 2)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'amazing')),
+ Piwik_DataTable::ID_SUMMARY_ROW => array(Piwik_DataTable_Row::COLUMNS => array('label' => 'summary', 'count' => 10)
+ )));
$filter = new Piwik_DataTable_Filter_Sort($table, 'count', 'desc');
$filter->filter($table);
- $expectedOrder = array('ask' , 'nintendo' , 'amazing' , 'summary');
+ $expectedOrder = array('ask', 'nintendo', 'amazing', 'summary');
$this->assertEquals($expectedOrder, $table->getColumn('label'));
}
-
+
/**
* Test to sort by label
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
* @group DataTable_Filter_Sort
*/
- public function testFilterSortString ()
+ public function testFilterSortString()
{
$idcol = Piwik_DataTable_Row::COLUMNS;
$table = new Piwik_DataTable();
$rows = array(
- array($idcol => array('label' => 'google')) , //0
- array($idcol => array('label' => 'ask')) , //1
- array($idcol => array('label' => 'piwik')) , //2
- array($idcol => array('label' => 'yahoo')) , //3
- array($idcol => array('label' => 'amazon')) , //4
- array($idcol => array('label' => '238975247578949')) , //5
+ array($idcol => array('label' => 'google')), //0
+ array($idcol => array('label' => 'ask')), //1
+ array($idcol => array('label' => 'piwik')), //2
+ array($idcol => array('label' => 'yahoo')), //3
+ array($idcol => array('label' => 'amazon')), //4
+ array($idcol => array('label' => '238975247578949')), //5
array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')) //6
);
$table->addRowsFromArray($rows);
$expectedtable = new Piwik_DataTable();
$rows = array(
- array($idcol => array('label' => '238975247578949')) , //5
- array($idcol => array('label' => 'amazon')) , //4
- array($idcol => array('label' => 'ask')) , //1
- array($idcol => array('label' => 'google')) , //0
- array($idcol => array('label' => 'piwik')) , //2
- array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')) , //6
- array($idcol => array('label' => 'yahoo') )//3
+ array($idcol => array('label' => '238975247578949')), //5
+ array($idcol => array('label' => 'amazon')), //4
+ array($idcol => array('label' => 'ask')), //1
+ array($idcol => array('label' => 'google')), //0
+ array($idcol => array('label' => 'piwik')), //2
+ array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')), //6
+ array($idcol => array('label' => 'yahoo')) //3
);
$expectedtable->addRowsFromArray($rows);
$expectedtableReverse = new Piwik_DataTable();
@@ -139,39 +139,39 @@ class DataTable_Filter_SortTest extends PHPUnit_Framework_TestCase
$filter->filter($table);
$this->assertTrue(Piwik_DataTable::isEqual($table, $expectedtableReverse));
}
-
+
/**
* Test to sort by visit
- *
+ *
* @group Core
* @group DataTable
* @group DataTable_Filter
* @group DataTable_Filter_Sort
*/
- public function testFilterSortNumeric ()
+ public function testFilterSortNumeric()
{
$idcol = Piwik_DataTable_Row::COLUMNS;
$table = new Piwik_DataTable();
$rows = array(
- array($idcol => array('label' => 'google', 'nb_visits' => 897)) , //0
- array($idcol => array('label' => 'ask', 'nb_visits' => - 152)) , //1
- array($idcol => array('label' => 'piwik', 'nb_visits' => 1.5)) , //2
- array($idcol => array('label' => 'yahoo', 'nb_visits' => 154)) , //3
- array($idcol => array('label' => 'amazon', 'nb_visits' => 30)) , //4
- array($idcol => array('label' => '238949', 'nb_visits' => 0)) , //5
- array($idcol => array('label' => 'Q*(%&*', 'nb_visits' => 1))//6
+ array($idcol => array('label' => 'google', 'nb_visits' => 897)), //0
+ array($idcol => array('label' => 'ask', 'nb_visits' => -152)), //1
+ array($idcol => array('label' => 'piwik', 'nb_visits' => 1.5)), //2
+ array($idcol => array('label' => 'yahoo', 'nb_visits' => 154)), //3
+ array($idcol => array('label' => 'amazon', 'nb_visits' => 30)), //4
+ array($idcol => array('label' => '238949', 'nb_visits' => 0)), //5
+ array($idcol => array('label' => 'Q*(%&*', 'nb_visits' => 1)) //6
);
$table->addRowsFromArray($rows);
$expectedtable = new Piwik_DataTable();
$rows = array(
- array($idcol => array('label' => 'ask', 'nb_visits' => - 152)) , //1
- array($idcol => array('label' => '238949', 'nb_visits' => 0)) , //5
- array($idcol => array('label' => 'Q*(%&*', 'nb_visits' => 1)) , //6
- array($idcol => array('label' => 'piwik', 'nb_visits' => 1.5)) , //2
- array($idcol => array('label' => 'amazon', 'nb_visits' => 30)) , //4
- array($idcol => array('label' => 'yahoo', 'nb_visits' => 154)) , //3
- array($idcol => array('label' => 'google', 'nb_visits' => 897))//0
- );
+ array($idcol => array('label' => 'ask', 'nb_visits' => -152)), //1
+ array($idcol => array('label' => '238949', 'nb_visits' => 0)), //5
+ array($idcol => array('label' => 'Q*(%&*', 'nb_visits' => 1)), //6
+ array($idcol => array('label' => 'piwik', 'nb_visits' => 1.5)), //2
+ array($idcol => array('label' => 'amazon', 'nb_visits' => 30)), //4
+ array($idcol => array('label' => 'yahoo', 'nb_visits' => 154)), //3
+ array($idcol => array('label' => 'google', 'nb_visits' => 897)) //0
+ );
$expectedtable->addRowsFromArray($rows);
$expectedtableReverse = new Piwik_DataTable();
$expectedtableReverse->addRowsFromArray(array_reverse($rows));
diff --git a/tests/PHPUnit/Core/DataTable/Filter/TruncateTest.php b/tests/PHPUnit/Core/DataTable/Filter/TruncateTest.php
index 0c37cf3f1f..1d46658e63 100644
--- a/tests/PHPUnit/Core/DataTable/Filter/TruncateTest.php
+++ b/tests/PHPUnit/Core/DataTable/Filter/TruncateTest.php
@@ -8,57 +8,57 @@
class DataTable_Filter_TruncateTest extends PHPUnit_Framework_TestCase
{
/**
- * @group Core
- * @group DataTable
- * @group DataTable_Filter
- * @group DataTable_Filter_Truncate
- */
- public function testUnrelatedDataTableNotFiltered()
- {
- // remark: this unit test would become invalid and would need to be rewritten if
- // AddSummaryRow filter stops calling getRowsCount() on the DataTable being filtered.
- $mockedDataTable = $this->getMock('Piwik_DataTable', array('getRowsCount'));
- $mockedDataTable->expects($this->never())->method('getRowsCount');
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Filter
+ * @group DataTable_Filter_Truncate
+ */
+ public function testUnrelatedDataTableNotFiltered()
+ {
+ // remark: this unit test would become invalid and would need to be rewritten if
+ // AddSummaryRow filter stops calling getRowsCount() on the DataTable being filtered.
+ $mockedDataTable = $this->getMock('Piwik_DataTable', array('getRowsCount'));
+ $mockedDataTable->expects($this->never())->method('getRowsCount');
- $dataTableBeingFiltered = new Piwik_DataTable();
- $rowBeingFiltered = new Piwik_DataTable_Row();
- $dataTableBeingFiltered->addRow($rowBeingFiltered);
+ $dataTableBeingFiltered = new Piwik_DataTable();
+ $rowBeingFiltered = new Piwik_DataTable_Row();
+ $dataTableBeingFiltered->addRow($rowBeingFiltered);
- // we simulate the fact that the value of Piwik_DataTable_Row::DATATABLE_ASSOCIATED retrieved
- // from the database is in conflict with one of the Piwik_DataTable_Manager managed table identifiers.
- // This is a rare but legitimate case as identifiers are not thoroughly synchronized
- // when the expanded parameter is false.
- $rowBeingFiltered->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $mockedDataTable->getId();
+ // we simulate the fact that the value of Piwik_DataTable_Row::DATATABLE_ASSOCIATED retrieved
+ // from the database is in conflict with one of the Piwik_DataTable_Manager managed table identifiers.
+ // This is a rare but legitimate case as identifiers are not thoroughly synchronized
+ // when the expanded parameter is false.
+ $rowBeingFiltered->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $mockedDataTable->getId();
- $filter = new Piwik_DataTable_Filter_Truncate($dataTableBeingFiltered, 1);
- $filter->filter($dataTableBeingFiltered);
- }
+ $filter = new Piwik_DataTable_Filter_Truncate($dataTableBeingFiltered, 1);
+ $filter->filter($dataTableBeingFiltered);
+ }
- /**
- *
- * @group Core
- * @group DataTable
- * @group DataTable_Filter
- * @group DataTable_Filter_Truncate
- */
- public function testForInfiniteRecursion()
- {
- $dataTableBeingFiltered = new Piwik_DataTable();
+ /**
+ *
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Filter
+ * @group DataTable_Filter_Truncate
+ */
+ public function testForInfiniteRecursion()
+ {
+ $dataTableBeingFiltered = new Piwik_DataTable();
- // remark: this unit test would become invalid and would need to be rewritten if
- // Truncate filter stops calling getIdSubDataTable() on rows associated with a SubDataTable
- $rowBeingFiltered = $this->getMock('Piwik_DataTable_Row', array('getIdSubDataTable'));
- $rowBeingFiltered->expects($this->never())->method('getIdSubDataTable');
+ // remark: this unit test would become invalid and would need to be rewritten if
+ // Truncate filter stops calling getIdSubDataTable() on rows associated with a SubDataTable
+ $rowBeingFiltered = $this->getMock('Piwik_DataTable_Row', array('getIdSubDataTable'));
+ $rowBeingFiltered->expects($this->never())->method('getIdSubDataTable');
- $dataTableBeingFiltered->addRow($rowBeingFiltered);
+ $dataTableBeingFiltered->addRow($rowBeingFiltered);
- // we simulate a legitimate but rare circular reference between a Piwik_DataTable_Row and its
- // enclosing Piwik_DataTable.
- // This can happen because identifiers are not thoroughly synchronized when the expanded parameter
- // is false.
- $rowBeingFiltered->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $dataTableBeingFiltered->getId();
+ // we simulate a legitimate but rare circular reference between a Piwik_DataTable_Row and its
+ // enclosing Piwik_DataTable.
+ // This can happen because identifiers are not thoroughly synchronized when the expanded parameter
+ // is false.
+ $rowBeingFiltered->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $dataTableBeingFiltered->getId();
- $filter = new Piwik_DataTable_Filter_Truncate($dataTableBeingFiltered, 1);
- $filter->filter($dataTableBeingFiltered);
- }
+ $filter = new Piwik_DataTable_Filter_Truncate($dataTableBeingFiltered, 1);
+ $filter->filter($dataTableBeingFiltered);
+ }
}
diff --git a/tests/PHPUnit/Core/DataTable/Renderer/CSVTest.php b/tests/PHPUnit/Core/DataTable/Renderer/CSVTest.php
index 03082336bf..2780534248 100644
--- a/tests/PHPUnit/Core/DataTable/Renderer/CSVTest.php
+++ b/tests/PHPUnit/Core/DataTable/Renderer/CSVTest.php
@@ -23,30 +23,30 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
protected function _getDataTableTest()
{
$dataTable = new Piwik_DataTable();
-
- $arraySubTableForRow2 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'sub1', 'count' => 1, 'bool' => false) ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'sub2', 'count' => 2, 'bool' => true) ),
+
+ $arraySubTableForRow2 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'sub1', 'count' => 1, 'bool' => false)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'sub2', 'count' => 2, 'bool' => true)),
);
$subDataTableForRow2 = new Piwik_DataTable();
$subDataTableForRow2->addRowsFromArray($arraySubTableForRow2);
- $array = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google&copy;', 'bool' => false, 'goals' => array('idgoal=1' => array('revenue'=> 5.5, 'nb_conversions' => 10)), 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, 'max_actions' => '5', 'sum_visit_length' => 517, 'bounce_count' => 9),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com/display"and,properly', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'bool' => true, 'nb_visits' => 151, 'nb_actions' => 147, 'max_actions' => '50', 'sum_visit_length' => 517, 'bounce_count' => 90),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subDataTableForRow2,
- )
- );
+ $array = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google&copy;', 'bool' => false, 'goals' => array('idgoal=1' => array('revenue' => 5.5, 'nb_conversions' => 10)), 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, 'max_actions' => '5', 'sum_visit_length' => 517, 'bounce_count' => 9),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com/display"and,properly', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'bool' => true, 'nb_visits' => 151, 'nb_actions' => 147, 'max_actions' => '50', 'sum_visit_length' => 517, 'bounce_count' => 90),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subDataTableForRow2,
+ )
+ );
$dataTable->addRowsFromArray($array);
return $dataTable;
}
protected function _getDataTableSimpleTest()
{
- $array = array ( 'max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, 'nb_visits' => 66.0, 'nb_actions' => 151.0, 'sum_visit_length' => 5118.0, 'bounce_count' => 44.0, );
+ $array = array('max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, 'nb_visits' => 66.0, 'nb_actions' => 151.0, 'sum_visit_length' => 5118.0, 'bounce_count' => 44.0,);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
@@ -55,7 +55,7 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneRowTest()
{
- $array = array ( 'nb_visits' => 14.0 );
+ $array = array('nb_visits' => 14.0);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
@@ -70,7 +70,7 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneZeroRowTest()
{
- $array = array ( 'nb_visits' => 0 );
+ $array = array('nb_visits' => 0);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
return $table;
@@ -78,7 +78,7 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneFalseRowTest()
{
- $array = array ( 'is_excluded' => false );
+ $array = array('is_excluded' => false);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
return $table;
@@ -94,13 +94,13 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
public function testCSVTest1()
{
$dataTable = $this->_getDataTableTest();
-
+
$render = new Piwik_DataTable_Renderer_Csv();
$render->setTable($dataTable);
$render->convertToUnicode = false;
$expected = "label,bool,goals_idgoal=1_revenue,goals_idgoal=1_nb_conversions,nb_uniq_visitors,nb_visits,nb_actions,max_actions,sum_visit_length,bounce_count,metadata_url,metadata_logo\n" .
- "Google©,0,5.5,10,11,11,17,5,517,9,\"http://www.google.com/display\"\"and,properly\",./plugins/Referers/images/searchEngines/www.google.com.png\n" .
- "Yahoo!,1,,,15,151,147,50,517,90,http://www.yahoo.com,./plugins/Referers/images/searchEngines/www.yahoo.com.png";
+ "Google©,0,5.5,10,11,11,17,5,517,9,\"http://www.google.com/display\"\"and,properly\",./plugins/Referers/images/searchEngines/www.google.com.png\n" .
+ "Yahoo!,1,,,15,151,147,50,517,90,http://www.yahoo.com,./plugins/Referers/images/searchEngines/www.yahoo.com.png";
$rendered = $render->render();
$this->assertEquals($expected, $rendered);
@@ -198,26 +198,26 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
protected function _getDataTableArrayTest()
{
- $array1 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google', 'nb_uniq_visitors' => 11, 'nb_visits' => 11, ),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'nb_visits' => 151, ),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
- )
- );
+ $array1 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google', 'nb_uniq_visitors' => 11, 'nb_visits' => 11,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'nb_visits' => 151,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
+ )
+ );
$table1 = new Piwik_DataTable();
$table1->addRowsFromArray($array1);
- $array2 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google1&copy;', 'nb_uniq_visitors' => 110, 'nb_visits' => 110,),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com1', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!1', 'nb_uniq_visitors' => 150, 'nb_visits' => 1510,),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com1', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1'),
- )
- );
+ $array2 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google1&copy;', 'nb_uniq_visitors' => 110, 'nb_visits' => 110,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com1', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!1', 'nb_uniq_visitors' => 150, 'nb_visits' => 1510,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com1', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1'),
+ )
+ );
$table2 = new Piwik_DataTable();
$table2->addRowsFromArray($array2);
@@ -235,11 +235,11 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleArrayTest()
{
- $array1 = array ( 'max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, );
+ $array1 = array('max_actions' => 14.0, 'nb_uniq_visitors' => 57.0,);
$table1 = new Piwik_DataTable_Simple;
$table1->addRowsFromArray($array1);
- $array2 = array ( 'max_actions' => 140.0, 'nb_uniq_visitors' => 570.0, );
+ $array2 = array('max_actions' => 140.0, 'nb_uniq_visitors' => 570.0,);
$table2 = new Piwik_DataTable_Simple;
$table2->addRowsFromArray($array2);
@@ -256,10 +256,10 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneRowArrayTest()
{
- $array1 = array ( 'nb_visits' => 14.0 );
+ $array1 = array('nb_visits' => 14.0);
$table1 = new Piwik_DataTable_Simple;
$table1->addRowsFromArray($array1);
- $array2 = array ( 'nb_visits' => 15.0 );
+ $array2 = array('nb_visits' => 15.0);
$table2 = new Piwik_DataTable_Simple;
$table2->addRowsFromArray($array2);
@@ -311,10 +311,10 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
$render->setTable($dataTable);
$render->convertToUnicode = false;
$expected = "testKey,label,nb_uniq_visitors,nb_visits,metadata_url,metadata_logo\n" .
- "date1,Google,11,11,http://www.google.com,./plugins/Referers/images/searchEngines/www.google.com.png\n" .
- "date1,Yahoo!,15,151,http://www.yahoo.com,./plugins/Referers/images/searchEngines/www.yahoo.com.png\n" .
- "date2,Google1©,110,110,http://www.google.com1,./plugins/Referers/images/searchEngines/www.google.com.png1\n" .
- "date2,Yahoo!1,150,1510,http://www.yahoo.com1,./plugins/Referers/images/searchEngines/www.yahoo.com.png1";
+ "date1,Google,11,11,http://www.google.com,./plugins/Referers/images/searchEngines/www.google.com.png\n" .
+ "date1,Yahoo!,15,151,http://www.yahoo.com,./plugins/Referers/images/searchEngines/www.yahoo.com.png\n" .
+ "date2,Google1©,110,110,http://www.google.com1,./plugins/Referers/images/searchEngines/www.google.com.png1\n" .
+ "date2,Yahoo!1,150,1510,http://www.yahoo.com1,./plugins/Referers/images/searchEngines/www.yahoo.com.png1";
$rendered = $render->render();
$this->assertEquals($expected, $rendered);
@@ -368,13 +368,13 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
$render->setTable($dataTable);
$render->convertToUnicode = false;
$expected = "parentArrayKey,testKey,label,nb_uniq_visitors,nb_visits,metadata_url,metadata_logo\n" .
- "idSite,date1,Google,11,11,http://www.google.com,./plugins/Referers/images/searchEngines/www.google.com.png\n" .
- "idSite,date1,Yahoo!,15,151,http://www.yahoo.com,./plugins/Referers/images/searchEngines/www.yahoo.com.png\n" .
- "idSite,date2,Google1©,110,110,http://www.google.com1,./plugins/Referers/images/searchEngines/www.google.com.png1\n" .
- "idSite,date2,Yahoo!1,150,1510,http://www.yahoo.com1,./plugins/Referers/images/searchEngines/www.yahoo.com.png1";
+ "idSite,date1,Google,11,11,http://www.google.com,./plugins/Referers/images/searchEngines/www.google.com.png\n" .
+ "idSite,date1,Yahoo!,15,151,http://www.yahoo.com,./plugins/Referers/images/searchEngines/www.yahoo.com.png\n" .
+ "idSite,date2,Google1©,110,110,http://www.google.com1,./plugins/Referers/images/searchEngines/www.google.com.png1\n" .
+ "idSite,date2,Yahoo!1,150,1510,http://www.yahoo.com1,./plugins/Referers/images/searchEngines/www.yahoo.com.png1";
$rendered = $render->render();
- $this->assertEquals( $expected,$rendered);
+ $this->assertEquals($expected, $rendered);
}
/**
@@ -411,79 +411,79 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase
$rendered = $render->render();
$this->assertEquals($expected, $rendered);
}
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray1()
- {
- $data = array();
-
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray1()
+ {
+ $data = array();
+
$render = new Piwik_DataTable_Renderer_Csv();
$render->setTable($data);
$render->convertToUnicode = false;
$expected = 'No data available';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray2()
- {
- $data = array('a', 'b', 'c');
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray2()
+ {
+ $data = array('a', 'b', 'c');
+
$render = new Piwik_DataTable_Renderer_Csv();
$render->setTable($data);
$render->convertToUnicode = false;
$expected = 'a
b
c';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray3()
- {
- $data = array('a' => 'b', 'c' => 'd', 'e' => 'f', 5 => 'g');
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray3()
+ {
+ $data = array('a' => 'b', 'c' => 'd', 'e' => 'f', 5 => 'g');
+
$render = new Piwik_DataTable_Renderer_Csv();
$render->setTable($data);
$render->convertToUnicode = false;
$expected = 'a,c,e,5
b,d,f,g';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray4()
- {
- $data = array('a' => 'b');
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray4()
+ {
+ $data = array('a' => 'b');
+
$render = new Piwik_DataTable_Renderer_Csv();
$render->setTable($data);
$render->convertToUnicode = false;
$expected = 'b';
-
+
$this->assertEquals($expected, $render->render());
- }
+ }
}
diff --git a/tests/PHPUnit/Core/DataTable/Renderer/ConsoleTest.php b/tests/PHPUnit/Core/DataTable/Renderer/ConsoleTest.php
index 9821a8af20..9af0b9b640 100644
--- a/tests/PHPUnit/Core/DataTable/Renderer/ConsoleTest.php
+++ b/tests/PHPUnit/Core/DataTable/Renderer/ConsoleTest.php
@@ -26,30 +26,30 @@ class DataTable_Renderer_ConsoleTest extends PHPUnit_Framework_TestCase
public function testConsole2SubLevelAnd2Different()
{
$table = new Piwik_DataTable;
- $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 'visits'=>245,'visitors'=>245),
- Piwik_DataTable_Row::METADATA => array('logo' => 'test.png'),)
+ $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array('visits' => 245, 'visitors' => 245),
+ Piwik_DataTable_Row::METADATA => array('logo' => 'test.png'),)
);
$subsubtable = new Piwik_DataTable;
$idsubsubtable = $subsubtable->getId();
- $subsubtable->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 'visits'=>2)));
+ $subsubtable->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array('visits' => 2)));
$subtable = new Piwik_DataTable;
$idsubtable1 = $subtable->getId();
- $subtable->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 'visits'=>1),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subsubtable));
+ $subtable->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array('visits' => 1),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subsubtable));
- $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 'visits'=>3),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subtable)
+ $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array('visits' => 3),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subtable)
);
$subtable2 = new Piwik_DataTable;
$idsubtable2 = $subtable2->getId();
- $subtable2->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 'visits'=>5),));
+ $subtable2->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array('visits' => 5),));
- $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 'visits'=>9),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subtable2)
+ $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array('visits' => 9),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subtable2)
);
$expected = "- 1 ['visits' => 245, 'visitors' => 245] ['logo' => 'test.png'] [idsubtable = ]<br />\n- 2 ['visits' => 3] [] [idsubtable = $idsubtable1]<br />\n*- 1 ['visits' => 1] [] [idsubtable = $idsubsubtable]<br />\n**- 1 ['visits' => 2] [] [idsubtable = ]<br />\n- 3 ['visits' => 9] [] [idsubtable = $idsubtable2]<br />\n*- 1 ['visits' => 5] [] [idsubtable = ]<br />\n";
@@ -74,8 +74,8 @@ class DataTable_Renderer_ConsoleTest extends PHPUnit_Framework_TestCase
public function testConsoleSimple()
{
$table = new Piwik_DataTable;
- $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 'visits'=>245,'visitors'=>245),
- Piwik_DataTable_Row::METADATA => array('logo' => 'test.png'),)
+ $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array('visits' => 245, 'visitors' => 245),
+ Piwik_DataTable_Row::METADATA => array('logo' => 'test.png'),)
);
@@ -88,77 +88,77 @@ class DataTable_Renderer_ConsoleTest extends PHPUnit_Framework_TestCase
$this->assertEquals($expected, $rendered);
}
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray1()
- {
- $data = array();
-
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray1()
+ {
+ $data = array();
+
$render = new Piwik_DataTable_Renderer_Console();
$render->setTable($data);
$expected = 'Empty table<br />
';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray2()
- {
- $data = array('a', 'b', 'c');
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray2()
+ {
+ $data = array('a', 'b', 'c');
+
$render = new Piwik_DataTable_Renderer_Console();
$render->setTable($data);
$expected = "- 1 ['0' => 'a'] [] [idsubtable = ]<br />
- 2 ['0' => 'b'] [] [idsubtable = ]<br />
- 3 ['0' => 'c'] [] [idsubtable = ]<br />
";
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray3()
- {
- $data = array('a' => 'b', 'c' => 'd', 'e' => 'f', 5 => 'g');
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray3()
+ {
+ $data = array('a' => 'b', 'c' => 'd', 'e' => 'f', 5 => 'g');
+
$render = new Piwik_DataTable_Renderer_Console();
$render->setTable($data);
$expected = "- 1 ['a' => 'b', 'c' => 'd', 'e' => 'f', '5' => 'g'] [] [idsubtable = ]<br />
";
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray4()
- {
- $data = array('a' => 'b');
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray4()
+ {
+ $data = array('a' => 'b');
+
$render = new Piwik_DataTable_Renderer_Console();
$render->setTable($data);
$expected = "- 1 ['0' => 'b'] [] [idsubtable = ]<br />
";
-
+
$this->assertEquals($expected, $render->render());
- }
+ }
}
diff --git a/tests/PHPUnit/Core/DataTable/Renderer/JSONTest.php b/tests/PHPUnit/Core/DataTable/Renderer/JSONTest.php
index d03e0226bf..1572ce1097 100644
--- a/tests/PHPUnit/Core/DataTable/Renderer/JSONTest.php
+++ b/tests/PHPUnit/Core/DataTable/Renderer/JSONTest.php
@@ -23,30 +23,30 @@ class DataTable_Renderer_JSONTest extends PHPUnit_Framework_TestCase
protected function _getDataTableTest()
{
$dataTable = new Piwik_DataTable();
-
- $arraySubTableForRow2 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'sub1', 'count' => 1, 'bool' => false) ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'sub2', 'count' => 2, 'bool' => true) ),
+
+ $arraySubTableForRow2 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'sub1', 'count' => 1, 'bool' => false)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'sub2', 'count' => 2, 'bool' => true)),
);
$subDataTableForRow2 = new Piwik_DataTable();
$subDataTableForRow2->addRowsFromArray($arraySubTableForRow2);
- $array = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google&copy;', 'bool' => false, 'goals' => array('idgoal=1' => array('revenue'=> 5.5, 'nb_conversions' => 10)), 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, 'max_actions' => '5', 'sum_visit_length' => 517, 'bounce_count' => 9),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com/display"and,properly', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'bool' => true, 'nb_visits' => 151, 'nb_actions' => 147, 'max_actions' => '50', 'sum_visit_length' => 517, 'bounce_count' => 90),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subDataTableForRow2,
- )
- );
+ $array = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google&copy;', 'bool' => false, 'goals' => array('idgoal=1' => array('revenue' => 5.5, 'nb_conversions' => 10)), 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, 'max_actions' => '5', 'sum_visit_length' => 517, 'bounce_count' => 9),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com/display"and,properly', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'bool' => true, 'nb_visits' => 151, 'nb_actions' => 147, 'max_actions' => '50', 'sum_visit_length' => 517, 'bounce_count' => 90),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subDataTableForRow2,
+ )
+ );
$dataTable->addRowsFromArray($array);
return $dataTable;
}
protected function _getDataTableSimpleTest()
{
- $array = array ( 'max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, 'nb_visits' => 66.0, 'nb_actions' => 151.0, 'sum_visit_length' => 5118.0, 'bounce_count' => 44.0, );
+ $array = array('max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, 'nb_visits' => 66.0, 'nb_actions' => 151.0, 'sum_visit_length' => 5118.0, 'bounce_count' => 44.0,);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
@@ -55,7 +55,7 @@ class DataTable_Renderer_JSONTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneRowTest()
{
- $array = array ( 'nb_visits' => 14.0 );
+ $array = array('nb_visits' => 14.0);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
@@ -70,7 +70,7 @@ class DataTable_Renderer_JSONTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneZeroRowTest()
{
- $array = array ( 'nb_visits' => 0 );
+ $array = array('nb_visits' => 0);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
return $table;
@@ -78,7 +78,7 @@ class DataTable_Renderer_JSONTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneFalseRowTest()
{
- $array = array ( 'is_excluded' => false );
+ $array = array('is_excluded' => false);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
return $table;
@@ -186,26 +186,26 @@ class DataTable_Renderer_JSONTest extends PHPUnit_Framework_TestCase
protected function _getDataTableArrayTest()
{
- $array1 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google', 'nb_uniq_visitors' => 11, 'nb_visits' => 11, ),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'nb_visits' => 151, ),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
- )
- );
+ $array1 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google', 'nb_uniq_visitors' => 11, 'nb_visits' => 11,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'nb_visits' => 151,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
+ )
+ );
$table1 = new Piwik_DataTable();
$table1->addRowsFromArray($array1);
- $array2 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google1&copy;', 'nb_uniq_visitors' => 110, 'nb_visits' => 110,),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com1', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!1', 'nb_uniq_visitors' => 150, 'nb_visits' => 1510,),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com1', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1'),
- )
- );
+ $array2 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google1&copy;', 'nb_uniq_visitors' => 110, 'nb_visits' => 110,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com1', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!1', 'nb_uniq_visitors' => 150, 'nb_visits' => 1510,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com1', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1'),
+ )
+ );
$table2 = new Piwik_DataTable();
$table2->addRowsFromArray($array2);
@@ -223,11 +223,11 @@ class DataTable_Renderer_JSONTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleArrayTest()
{
- $array1 = array ( 'max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, );
+ $array1 = array('max_actions' => 14.0, 'nb_uniq_visitors' => 57.0,);
$table1 = new Piwik_DataTable_Simple;
$table1->addRowsFromArray($array1);
- $array2 = array ( 'max_actions' => 140.0, 'nb_uniq_visitors' => 570.0, );
+ $array2 = array('max_actions' => 140.0, 'nb_uniq_visitors' => 570.0,);
$table2 = new Piwik_DataTable_Simple;
$table2->addRowsFromArray($array2);
@@ -244,10 +244,10 @@ class DataTable_Renderer_JSONTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneRowArrayTest()
{
- $array1 = array ( 'nb_visits' => 14.0 );
+ $array1 = array('nb_visits' => 14.0);
$table1 = new Piwik_DataTable_Simple;
$table1->addRowsFromArray($array1);
- $array2 = array ( 'nb_visits' => 15.0 );
+ $array2 = array('nb_visits' => 15.0);
$table2 = new Piwik_DataTable_Simple;
$table2->addRowsFromArray($array2);
@@ -390,89 +390,89 @@ class DataTable_Renderer_JSONTest extends PHPUnit_Framework_TestCase
$this->assertEquals($expected, $rendered);
}
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray1()
- {
- $data = array();
-
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray1()
+ {
+ $data = array();
+
$render = new Piwik_DataTable_Renderer_Json();
$render->setTable($data);
$expected = '[]';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_JSON
- */
- public function testRenderArray2()
- {
- $data = array('a', 'b', 'c', array('a' => 'b'), array(1, 2));
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_JSON
+ */
+ public function testRenderArray2()
+ {
+ $data = array('a', 'b', 'c', array('a' => 'b'), array(1, 2));
+
$render = new Piwik_DataTable_Renderer_Json();
$render->setTable($data);
$expected = '["a","b","c",{"a":"b"},[1,2]]';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_JSON
- */
- public function testRenderArray3()
- {
- $data = array('a' => 'b', 'c' => 'd', 'e' => 'f', 5 => 'g');
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_JSON
+ */
+ public function testRenderArray3()
+ {
+ $data = array('a' => 'b', 'c' => 'd', 'e' => 'f', 5 => 'g');
+
$render = new Piwik_DataTable_Renderer_Json();
$render->setTable($data);
$expected = '[{"a":"b","c":"d","e":"f","5":"g"}]';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_JSON
- */
- public function testRenderArray4()
- {
- $data = array('a' => 'b', 'c' => array(1,2,3,4), 'e' => array('f' => 'g', 'h' => 'i', 'j' => 'k'));
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_JSON
+ */
+ public function testRenderArray4()
+ {
+ $data = array('a' => 'b', 'c' => array(1, 2, 3, 4), 'e' => array('f' => 'g', 'h' => 'i', 'j' => 'k'));
+
$render = new Piwik_DataTable_Renderer_Json();
$render->setTable($data);
$expected = '{"a":"b","c":[1,2,3,4],"e":{"f":"g","h":"i","j":"k"}}';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_JSON
- */
- public function testRenderArray5()
- {
- $data = array('a' => 'b');
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_JSON
+ */
+ public function testRenderArray5()
+ {
+ $data = array('a' => 'b');
+
$render = new Piwik_DataTable_Renderer_Json();
- $render->setTable($data);
- $expected = '[{"a":"b"}]';
-
+ $render->setTable($data);
+ $expected = '[{"a":"b"}]';
+
$this->assertEquals($expected, $render->render());
- }
+ }
}
diff --git a/tests/PHPUnit/Core/DataTable/Renderer/PHPTest.php b/tests/PHPUnit/Core/DataTable/Renderer/PHPTest.php
index 249a19ec5b..3058a513dd 100644
--- a/tests/PHPUnit/Core/DataTable/Renderer/PHPTest.php
+++ b/tests/PHPUnit/Core/DataTable/Renderer/PHPTest.php
@@ -23,30 +23,30 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
protected function _getDataTableTest()
{
$dataTable = new Piwik_DataTable();
-
- $arraySubTableForRow2 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'sub1', 'count' => 1, 'bool' => false) ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'sub2', 'count' => 2, 'bool' => true) ),
+
+ $arraySubTableForRow2 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'sub1', 'count' => 1, 'bool' => false)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'sub2', 'count' => 2, 'bool' => true)),
);
$subDataTableForRow2 = new Piwik_DataTable();
$subDataTableForRow2->addRowsFromArray($arraySubTableForRow2);
- $array = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google&copy;', 'bool' => false, 'goals' => array('idgoal=1' => array('revenue'=> 5.5, 'nb_conversions' => 10)), 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, 'max_actions' => '5', 'sum_visit_length' => 517, 'bounce_count' => 9),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com/display"and,properly', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'bool' => true, 'nb_visits' => 151, 'nb_actions' => 147, 'max_actions' => '50', 'sum_visit_length' => 517, 'bounce_count' => 90),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subDataTableForRow2,
- )
- );
+ $array = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google&copy;', 'bool' => false, 'goals' => array('idgoal=1' => array('revenue' => 5.5, 'nb_conversions' => 10)), 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, 'max_actions' => '5', 'sum_visit_length' => 517, 'bounce_count' => 9),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com/display"and,properly', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'bool' => true, 'nb_visits' => 151, 'nb_actions' => 147, 'max_actions' => '50', 'sum_visit_length' => 517, 'bounce_count' => 90),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subDataTableForRow2,
+ )
+ );
$dataTable->addRowsFromArray($array);
return $dataTable;
}
protected function _getDataTableSimpleTest()
{
- $array = array ( 'max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, 'nb_visits' => 66.0, 'nb_actions' => 151.0, 'sum_visit_length' => 5118.0, 'bounce_count' => 44.0, );
+ $array = array('max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, 'nb_visits' => 66.0, 'nb_actions' => 151.0, 'sum_visit_length' => 5118.0, 'bounce_count' => 44.0,);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
@@ -55,7 +55,7 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneRowTest()
{
- $array = array ( 'nb_visits' => 14.0 );
+ $array = array('nb_visits' => 14.0);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
@@ -70,7 +70,7 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneZeroRowTest()
{
- $array = array ( 'nb_visits' => 0 );
+ $array = array('nb_visits' => 0);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
return $table;
@@ -78,7 +78,7 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneFalseRowTest()
{
- $array = array ( 'is_excluded' => false );
+ $array = array('is_excluded' => false);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
return $table;
@@ -98,56 +98,56 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
$render->setTable($dataTable);
$render->setRenderSubTables(true);
- $expected = serialize(array (
- 0 =>
- array (
- 'label' => 'Google&copy;',
- 'bool' => false,
- 'goals' => array(
- 'idgoal=1' => array(
- 'revenue' => 5.5,
- 'nb_conversions' => 10,
- ),
- ),
- 'nb_uniq_visitors' => 11,
- 'nb_visits' => 11,
- 'nb_actions' => 17,
- 'max_actions' => '5',
- 'sum_visit_length' => 517,
- 'bounce_count' => 9,
- 'url' => 'http://www.google.com/display"and,properly',
- 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png',
- ),
- 1 =>
- array (
- 'label' => 'Yahoo!',
- 'nb_uniq_visitors' => 15,
- 'bool' => true,
- 'nb_visits' => 151,
- 'nb_actions' => 147,
- 'max_actions' => '50',
- 'sum_visit_length' => 517,
- 'bounce_count' => 90,
- 'url' => 'http://www.yahoo.com',
- 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png',
- 'idsubdatatable' => 2,
- 'subtable' =>
- array (
- 0 =>
- array (
- 'label' => 'sub1',
- 'count' => 1,
- 'bool' => false,
- ),
- 1 =>
- array (
- 'label' => 'sub2',
- 'count' => 2,
- 'bool' => true,
- ),
- ),
- ),
- ));
+ $expected = serialize(array(
+ 0 =>
+ array(
+ 'label' => 'Google&copy;',
+ 'bool' => false,
+ 'goals' => array(
+ 'idgoal=1' => array(
+ 'revenue' => 5.5,
+ 'nb_conversions' => 10,
+ ),
+ ),
+ 'nb_uniq_visitors' => 11,
+ 'nb_visits' => 11,
+ 'nb_actions' => 17,
+ 'max_actions' => '5',
+ 'sum_visit_length' => 517,
+ 'bounce_count' => 9,
+ 'url' => 'http://www.google.com/display"and,properly',
+ 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png',
+ ),
+ 1 =>
+ array(
+ 'label' => 'Yahoo!',
+ 'nb_uniq_visitors' => 15,
+ 'bool' => true,
+ 'nb_visits' => 151,
+ 'nb_actions' => 147,
+ 'max_actions' => '50',
+ 'sum_visit_length' => 517,
+ 'bounce_count' => 90,
+ 'url' => 'http://www.yahoo.com',
+ 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png',
+ 'idsubdatatable' => 2,
+ 'subtable' =>
+ array(
+ 0 =>
+ array(
+ 'label' => 'sub1',
+ 'count' => 1,
+ 'bool' => false,
+ ),
+ 1 =>
+ array(
+ 'label' => 'sub2',
+ 'count' => 2,
+ 'bool' => true,
+ ),
+ ),
+ ),
+ ));
$rendered = $render->render(null);
$this->assertEquals($expected, $rendered);
}
@@ -163,14 +163,14 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
$dataTable = $this->_getDataTableSimpleTest();
$render = new Piwik_DataTable_Renderer_Php();
$render->setTable($dataTable);
- $expected = serialize(array (
- 'max_actions' => 14.0,
- 'nb_uniq_visitors' => 57.0,
- 'nb_visits' => 66.0,
- 'nb_actions' => 151.0,
- 'sum_visit_length' => 5118.0,
- 'bounce_count' => 44.0,
- ));
+ $expected = serialize(array(
+ 'max_actions' => 14.0,
+ 'nb_uniq_visitors' => 57.0,
+ 'nb_visits' => 66.0,
+ 'nb_actions' => 151.0,
+ 'sum_visit_length' => 5118.0,
+ 'bounce_count' => 44.0,
+ ));
$this->assertEquals($expected, $render->render());
}
@@ -242,26 +242,26 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
protected function _getDataTableArrayTest()
{
- $array1 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google', 'nb_uniq_visitors' => 11, 'nb_visits' => 11, ),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'nb_visits' => 151, ),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
- )
- );
+ $array1 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google', 'nb_uniq_visitors' => 11, 'nb_visits' => 11,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'nb_visits' => 151,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
+ )
+ );
$table1 = new Piwik_DataTable();
$table1->addRowsFromArray($array1);
- $array2 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google1&copy;', 'nb_uniq_visitors' => 110, 'nb_visits' => 110,),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com1', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!1', 'nb_uniq_visitors' => 150, 'nb_visits' => 1510,),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com1', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1'),
- )
- );
+ $array2 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google1&copy;', 'nb_uniq_visitors' => 110, 'nb_visits' => 110,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com1', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!1', 'nb_uniq_visitors' => 150, 'nb_visits' => 1510,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com1', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1'),
+ )
+ );
$table2 = new Piwik_DataTable();
$table2->addRowsFromArray($array2);
@@ -279,11 +279,11 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleArrayTest()
{
- $array1 = array ( 'max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, );
+ $array1 = array('max_actions' => 14.0, 'nb_uniq_visitors' => 57.0,);
$table1 = new Piwik_DataTable_Simple;
$table1->addRowsFromArray($array1);
- $array2 = array ( 'max_actions' => 140.0, 'nb_uniq_visitors' => 570.0, );
+ $array2 = array('max_actions' => 140.0, 'nb_uniq_visitors' => 570.0,);
$table2 = new Piwik_DataTable_Simple;
$table2->addRowsFromArray($array2);
@@ -300,10 +300,10 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneRowArrayTest()
{
- $array1 = array ( 'nb_visits' => 14.0 );
+ $array1 = array('nb_visits' => 14.0);
$table1 = new Piwik_DataTable_Simple;
$table1->addRowsFromArray($array1);
- $array2 = array ( 'nb_visits' => 15.0 );
+ $array2 = array('nb_visits' => 15.0);
$table2 = new Piwik_DataTable_Simple;
$table2->addRowsFromArray($array2);
@@ -356,47 +356,47 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
$render->setTable($dataTable);
$rendered = $render->render();
- $expected = serialize(array (
- 'date1' =>
- array (
- 0 =>
- array (
- 'label' => 'Google',
- 'nb_uniq_visitors' => 11,
- 'nb_visits' => 11,
- 'url' => 'http://www.google.com',
- 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png',
- ),
- 1 =>
- array (
- 'label' => 'Yahoo!',
- 'nb_uniq_visitors' => 15,
- 'nb_visits' => 151,
- 'url' => 'http://www.yahoo.com',
- 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png',
- ),
- ),
- 'date2' =>
- array (
- 0 =>
- array (
- 'label' => 'Google1&copy;',
- 'nb_uniq_visitors' => 110,
- 'nb_visits' => 110,
- 'url' => 'http://www.google.com1',
- 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1',
- ),
- 1 =>
- array (
- 'label' => 'Yahoo!1',
- 'nb_uniq_visitors' => 150,
- 'nb_visits' => 1510,
- 'url' => 'http://www.yahoo.com1',
- 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1',
- ),
- ),
- 'date3' => array (),
- ));
+ $expected = serialize(array(
+ 'date1' =>
+ array(
+ 0 =>
+ array(
+ 'label' => 'Google',
+ 'nb_uniq_visitors' => 11,
+ 'nb_visits' => 11,
+ 'url' => 'http://www.google.com',
+ 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png',
+ ),
+ 1 =>
+ array(
+ 'label' => 'Yahoo!',
+ 'nb_uniq_visitors' => 15,
+ 'nb_visits' => 151,
+ 'url' => 'http://www.yahoo.com',
+ 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png',
+ ),
+ ),
+ 'date2' =>
+ array(
+ 0 =>
+ array(
+ 'label' => 'Google1&copy;',
+ 'nb_uniq_visitors' => 110,
+ 'nb_visits' => 110,
+ 'url' => 'http://www.google.com1',
+ 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1',
+ ),
+ 1 =>
+ array(
+ 'label' => 'Yahoo!1',
+ 'nb_uniq_visitors' => 150,
+ 'nb_visits' => 1510,
+ 'url' => 'http://www.yahoo.com1',
+ 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1',
+ ),
+ ),
+ 'date3' => array(),
+ ));
$this->assertEquals($expected, $rendered);
}
@@ -413,21 +413,20 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
$render->setTable($dataTable);
$rendered = $render->render();
- $expected = serialize(array (
- 'row1' =>
- array (
- 'max_actions' => 14.0,
- 'nb_uniq_visitors' => 57.0,
- ),
- 'row2' =>
- array (
- 'max_actions' => 140.0,
- 'nb_uniq_visitors' => 570.0,
- ),
- 'row3' =>
- array (
- ),
- ));
+ $expected = serialize(array(
+ 'row1' =>
+ array(
+ 'max_actions' => 14.0,
+ 'nb_uniq_visitors' => 57.0,
+ ),
+ 'row2' =>
+ array(
+ 'max_actions' => 140.0,
+ 'nb_uniq_visitors' => 570.0,
+ ),
+ 'row3' =>
+ array(),
+ ));
$this->assertEquals($expected, $rendered);
}
@@ -444,11 +443,11 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
$render->setTable($dataTable);
$rendered = $render->render();
- $expected = serialize(array (
- 'row1' => 14.0,
- 'row2' => 15.0,
- 'row3' => array(),
- ));
+ $expected = serialize(array(
+ 'row1' => 14.0,
+ 'row2' => 15.0,
+ 'row3' => array(),
+ ));
$this->assertEquals($expected, $rendered);
}
@@ -465,48 +464,48 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
$render->setTable($dataTable);
$rendered = $render->render();
- $expected = serialize(array('idSite'=>
- array (
- 'date1' =>
- array (
- 0 =>
- array (
- 'label' => 'Google',
- 'nb_uniq_visitors' => 11,
- 'nb_visits' => 11,
- 'url' => 'http://www.google.com',
- 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png',
- ),
- 1 =>
- array (
- 'label' => 'Yahoo!',
- 'nb_uniq_visitors' => 15,
- 'nb_visits' => 151,
- 'url' => 'http://www.yahoo.com',
- 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png',
- ),
- ),
- 'date2' =>
- array (
- 0 =>
- array (
- 'label' => 'Google1&copy;',
- 'nb_uniq_visitors' => 110,
- 'nb_visits' => 110,
- 'url' => 'http://www.google.com1',
- 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1',
- ),
- 1 =>
- array (
- 'label' => 'Yahoo!1',
- 'nb_uniq_visitors' => 150,
- 'nb_visits' => 1510,
- 'url' => 'http://www.yahoo.com1',
- 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1',
- ),
- ),
- 'date3' => array (),
- )));
+ $expected = serialize(array('idSite' =>
+ array(
+ 'date1' =>
+ array(
+ 0 =>
+ array(
+ 'label' => 'Google',
+ 'nb_uniq_visitors' => 11,
+ 'nb_visits' => 11,
+ 'url' => 'http://www.google.com',
+ 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png',
+ ),
+ 1 =>
+ array(
+ 'label' => 'Yahoo!',
+ 'nb_uniq_visitors' => 15,
+ 'nb_visits' => 151,
+ 'url' => 'http://www.yahoo.com',
+ 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png',
+ ),
+ ),
+ 'date2' =>
+ array(
+ 0 =>
+ array(
+ 'label' => 'Google1&copy;',
+ 'nb_uniq_visitors' => 110,
+ 'nb_visits' => 110,
+ 'url' => 'http://www.google.com1',
+ 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1',
+ ),
+ 1 =>
+ array(
+ 'label' => 'Yahoo!1',
+ 'nb_uniq_visitors' => 150,
+ 'nb_visits' => 1510,
+ 'url' => 'http://www.yahoo.com1',
+ 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1',
+ ),
+ ),
+ 'date3' => array(),
+ )));
$this->assertEquals($expected, $rendered);
}
@@ -524,22 +523,21 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
$render->setTable($dataTable);
$rendered = $render->render();
- $expected = serialize(array ('idSite'=>
- array(
- 'row1' =>
- array (
- 'max_actions' => 14.0,
- 'nb_uniq_visitors' => 57.0,
- ),
- 'row2' =>
- array (
- 'max_actions' => 140.0,
- 'nb_uniq_visitors' => 570.0,
- ),
- 'row3' =>
- array (
- ),
- )));
+ $expected = serialize(array('idSite' =>
+ array(
+ 'row1' =>
+ array(
+ 'max_actions' => 14.0,
+ 'nb_uniq_visitors' => 57.0,
+ ),
+ 'row2' =>
+ array(
+ 'max_actions' => 140.0,
+ 'nb_uniq_visitors' => 570.0,
+ ),
+ 'row3' =>
+ array(),
+ )));
$this->assertEquals($expected, $rendered);
}
@@ -556,12 +554,12 @@ class DataTable_Renderer_PHPTest extends PHPUnit_Framework_TestCase
$render->setTable($dataTable);
$rendered = $render->render();
- $expected = serialize(array ('idSite'=>
- array(
- 'row1' => 14.0,
- 'row2' => 15.0,
- 'row3' => array(),
- )));
+ $expected = serialize(array('idSite' =>
+ array(
+ 'row1' => 14.0,
+ 'row2' => 15.0,
+ 'row3' => array(),
+ )));
$this->assertEquals($expected, $rendered);
}
}
diff --git a/tests/PHPUnit/Core/DataTable/Renderer/XMLTest.php b/tests/PHPUnit/Core/DataTable/Renderer/XMLTest.php
index 13384f1a69..d4e676f5eb 100644
--- a/tests/PHPUnit/Core/DataTable/Renderer/XMLTest.php
+++ b/tests/PHPUnit/Core/DataTable/Renderer/XMLTest.php
@@ -23,30 +23,30 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
protected function _getDataTableTest()
{
$dataTable = new Piwik_DataTable();
-
- $arraySubTableForRow2 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'sub1', 'count' => 1, 'bool' => false) ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'sub2', 'count' => 2, 'bool' => true) ),
+
+ $arraySubTableForRow2 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'sub1', 'count' => 1, 'bool' => false)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'sub2', 'count' => 2, 'bool' => true)),
);
$subDataTableForRow2 = new Piwik_DataTable();
$subDataTableForRow2->addRowsFromArray($arraySubTableForRow2);
- $array = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google&copy;', 'bool' => false, 'goals' => array('idgoal=1' => array('revenue'=> 5.5, 'nb_conversions' => 10)), 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, 'max_actions' => '5', 'sum_visit_length' => 517, 'bounce_count' => 9),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com/display"and,properly', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'bool' => true, 'nb_visits' => 151, 'nb_actions' => 147, 'max_actions' => '50', 'sum_visit_length' => 517, 'bounce_count' => 90),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subDataTableForRow2,
- )
- );
+ $array = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google&copy;', 'bool' => false, 'goals' => array('idgoal=1' => array('revenue' => 5.5, 'nb_conversions' => 10)), 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, 'max_actions' => '5', 'sum_visit_length' => 517, 'bounce_count' => 9),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com/display"and,properly', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'bool' => true, 'nb_visits' => 151, 'nb_actions' => 147, 'max_actions' => '50', 'sum_visit_length' => 517, 'bounce_count' => 90),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subDataTableForRow2,
+ )
+ );
$dataTable->addRowsFromArray($array);
return $dataTable;
}
protected function _getDataTableSimpleTest()
{
- $array = array ( 'max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, 'nb_visits' => 66.0, 'nb_actions' => 151.0, 'sum_visit_length' => 5118.0, 'bounce_count' => 44.0, );
+ $array = array('max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, 'nb_visits' => 66.0, 'nb_actions' => 151.0, 'sum_visit_length' => 5118.0, 'bounce_count' => 44.0,);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
@@ -55,7 +55,7 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneRowTest()
{
- $array = array ( 'nb_visits' => 14.0 );
+ $array = array('nb_visits' => 14.0);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
@@ -70,7 +70,7 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneZeroRowTest()
{
- $array = array ( 'nb_visits' => 0 );
+ $array = array('nb_visits' => 0);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
return $table;
@@ -78,7 +78,7 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneFalseRowTest()
{
- $array = array ( 'is_excluded' => false );
+ $array = array('is_excluded' => false);
$table = new Piwik_DataTable_Simple;
$table->addRowsFromArray($array);
return $table;
@@ -242,26 +242,26 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
protected function _getDataTableArrayTest()
{
- $array1 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google', 'nb_uniq_visitors' => 11, 'nb_visits' => 11, ),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'nb_visits' => 151, ),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
- )
- );
+ $array1 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google', 'nb_uniq_visitors' => 11, 'nb_visits' => 11,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'nb_visits' => 151,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png'),
+ )
+ );
$table1 = new Piwik_DataTable();
$table1->addRowsFromArray($array1);
- $array2 = array (
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google1&copy;', 'nb_uniq_visitors' => 110, 'nb_visits' => 110,),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com1', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1'),
- ),
- array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!1', 'nb_uniq_visitors' => 150, 'nb_visits' => 1510,),
- Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com1', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1'),
- )
- );
+ $array2 = array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Google1&copy;', 'nb_uniq_visitors' => 110, 'nb_visits' => 110,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com1', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png1'),
+ ),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'Yahoo!1', 'nb_uniq_visitors' => 150, 'nb_visits' => 1510,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'http://www.yahoo.com1', 'logo' => './plugins/Referers/images/searchEngines/www.yahoo.com.png1'),
+ )
+ );
$table2 = new Piwik_DataTable();
$table2->addRowsFromArray($array2);
@@ -279,11 +279,11 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleArrayTest()
{
- $array1 = array ( 'max_actions' => 14.0, 'nb_uniq_visitors' => 57.0, );
+ $array1 = array('max_actions' => 14.0, 'nb_uniq_visitors' => 57.0,);
$table1 = new Piwik_DataTable_Simple;
$table1->addRowsFromArray($array1);
- $array2 = array ( 'max_actions' => 140.0, 'nb_uniq_visitors' => 570.0, );
+ $array2 = array('max_actions' => 140.0, 'nb_uniq_visitors' => 570.0,);
$table2 = new Piwik_DataTable_Simple;
$table2->addRowsFromArray($array2);
@@ -300,10 +300,10 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
protected function _getDataTableSimpleOneRowArrayTest()
{
- $array1 = array ( 'nb_visits' => 14.0 );
+ $array1 = array('nb_visits' => 14.0);
$table1 = new Piwik_DataTable_Simple;
$table1->addRowsFromArray($array1);
- $array2 = array ( 'nb_visits' => 15.0 );
+ $array2 = array('nb_visits' => 15.0);
$table2 = new Piwik_DataTable_Simple;
$table2->addRowsFromArray($array2);
@@ -546,38 +546,38 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
$rendered = $render->render();
$this->assertEquals($expected, $rendered);
}
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray1()
- {
- $data = array();
-
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray1()
+ {
+ $data = array();
+
$render = new Piwik_DataTable_Renderer_Xml();
$render->setTable($data);
$expected = '<?xml version="1.0" encoding="utf-8" ?>
<result />';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray2()
- {
- $data = array("firstElement",
- array("firstElement",
- "secondElement"),
- "thirdElement");
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray2()
+ {
+ $data = array("firstElement",
+ array("firstElement",
+ "secondElement"),
+ "thirdElement");
+
$render = new Piwik_DataTable_Renderer_Xml();
$render->setTable($data);
$expected = '<?xml version="1.0" encoding="utf-8" ?>
@@ -589,20 +589,20 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
</row>
<row>thirdElement</row>
</result>';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray3()
- {
- $data = array('a' => 'b', 'c' => 'd', 'e' => 'f', 5 => 'g');
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray3()
+ {
+ $data = array('a' => 'b', 'c' => 'd', 'e' => 'f', 5 => 'g');
+
$render = new Piwik_DataTable_Renderer_Xml();
$render->setTable($data);
$expected = '<?xml version="1.0" encoding="utf-8" ?>
@@ -614,20 +614,20 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
<row key="5">g</row>
</row>
</result>';
-
+
$this->assertEquals($expected, $render->render());
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group DataTable_Renderer
- * @group DataTable_Renderer_XML
- */
- public function testRenderArray4()
- {
- $data = array('c' => array(1,2,3,4), 'e' => array('f' => 'g', 'h' => 'i', 'j' => 'k'));
-
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group DataTable_Renderer
+ * @group DataTable_Renderer_XML
+ */
+ public function testRenderArray4()
+ {
+ $data = array('c' => array(1, 2, 3, 4), 'e' => array('f' => 'g', 'h' => 'i', 'j' => 'k'));
+
$render = new Piwik_DataTable_Renderer_Xml();
$render->setTable($data);
$expected = '<?xml version="1.0" encoding="utf-8" ?>
@@ -644,7 +644,7 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase
<j>k</j>
</e>
</result>';
-
+
$this->assertEquals($expected, $render->render());
- }
+ }
}
diff --git a/tests/PHPUnit/Core/DataTable/RowTest.php b/tests/PHPUnit/Core/DataTable/RowTest.php
index d138903ca9..2effe961c4 100644
--- a/tests/PHPUnit/Core/DataTable/RowTest.php
+++ b/tests/PHPUnit/Core/DataTable/RowTest.php
@@ -7,141 +7,141 @@
*/
class RowTest extends PHPUnit_Framework_TestCase
{
- /**
- *
- * @group Core
- * @group DataTable
- * @group Piwik_DataTable_Row
- */
- public function testDataTableAssociatedIsNegativeWhenSubDataTableInMemory()
- {
- $testRow = $this->getTestRowWithSubDataTableLoaded();
- $this->assertTrue($testRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] < 0);
- }
-
- /**
- *
- * @group Core
- * @group DataTable
- * @group Piwik_DataTable_Row
- */
- public function testDataTableAssociatedIsNegativeWhenSubDataTableAdded()
- {
- $testRow = $this->getTestRowWithSubDataTableNotLoaded();
- $testRow->addSubtable($this->getTestSubDataTable());
- $this->assertTrue($testRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] < 0);
- }
-
- /**
- *
- * @group Core
- * @group DataTable
- * @group Piwik_DataTable_Row
- */
- public function testDataTableAssociatedIsNegativeWhenSubDataTableSetted()
- {
- $testRow = $this->getTestRowWithSubDataTableNotLoaded();
- $testRow->setSubtable($this->getTestSubDataTable());
- $this->assertTrue($testRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] < 0);
- }
-
- /**
- *
- * @group Core
- * @group DataTable
- * @group Piwik_DataTable_Row
- */
- public function testIdSubDataTabledIsPositiveWhenSubDataTableInMemory()
- {
- $testRow = $this->getTestRowWithSubDataTableLoaded();
- $this->assertTrue($testRow->getIdSubDataTable() > 0);
- }
-
-
- /**
- * @group Core
- * @group DataTable
- * @group Piwik_DataTable_Row
- */
- public function testDataTableAssociatedIsPositiveOnSerializedRow()
- {
- $testRow = $this->getTestRowWithSubDataTableLoaded();
-
- // testDataTableAssociatedIsPositiveOnSerializedRow is only valid as long as the Row is not modified after being unserialized
- $this->assertFalse(method_exists($testRow, '__wakeup'));
-
- $serializedTestRow = serialize($testRow);
- $unserializedTestRow = unserialize($serializedTestRow);
-
- $this->assertTrue($unserializedTestRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] > 0);
- }
-
- /**
- * @group Core
- * @group DataTable
- * @group Piwik_DataTable_Row
- */
- public function testDataTableAssociatedIsNegativeAfterSerialize()
- {
- $testRow = $this->getTestRowWithSubDataTableLoaded();
-
- serialize($testRow);
-
- $testRow->cleanPostSerialize();
-
- $this->assertTrue($testRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] < 0);
- }
-
- /**
- *
- * @group Core
- * @group DataTable
- * @group Piwik_DataTable_Row
- */
- public function testIsSubDataTableLoadedIsTrueWhenSubDataTableInMemory()
- {
- $testRow = $this->getTestRowWithSubDataTableLoaded();
- $this->assertTrue($testRow->isSubtableLoaded());
- }
-
- /**
- *
- * @group Core
- * @group DataTable
- * @group Piwik_DataTable_Row
- */
- public function testIsSubDataTableLoadedIsFalseWhenSubDataTableNotInMemory()
- {
- $testRow = $this->getTestRowWithSubDataTableNotLoaded();
- $this->assertFalse($testRow->isSubtableLoaded());
- }
-
- protected function getTestRowWithSubDataTableLoaded()
- {
- $testSubDataTable = $this->getTestSubDataTable();
-
- $testRow = new Piwik_DataTable_Row(
- array(
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $testSubDataTable
- )
- );
-
- return $testRow;
- }
-
- protected function getTestSubDataTable()
- {
- return new Piwik_DataTable();
- }
-
- protected function getTestRowWithSubDataTableNotLoaded()
- {
- $testRow = new Piwik_DataTable_Row(
- array(
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => 50
- )
- );
-
- return $testRow;
- }
+ /**
+ *
+ * @group Core
+ * @group DataTable
+ * @group Piwik_DataTable_Row
+ */
+ public function testDataTableAssociatedIsNegativeWhenSubDataTableInMemory()
+ {
+ $testRow = $this->getTestRowWithSubDataTableLoaded();
+ $this->assertTrue($testRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] < 0);
+ }
+
+ /**
+ *
+ * @group Core
+ * @group DataTable
+ * @group Piwik_DataTable_Row
+ */
+ public function testDataTableAssociatedIsNegativeWhenSubDataTableAdded()
+ {
+ $testRow = $this->getTestRowWithSubDataTableNotLoaded();
+ $testRow->addSubtable($this->getTestSubDataTable());
+ $this->assertTrue($testRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] < 0);
+ }
+
+ /**
+ *
+ * @group Core
+ * @group DataTable
+ * @group Piwik_DataTable_Row
+ */
+ public function testDataTableAssociatedIsNegativeWhenSubDataTableSetted()
+ {
+ $testRow = $this->getTestRowWithSubDataTableNotLoaded();
+ $testRow->setSubtable($this->getTestSubDataTable());
+ $this->assertTrue($testRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] < 0);
+ }
+
+ /**
+ *
+ * @group Core
+ * @group DataTable
+ * @group Piwik_DataTable_Row
+ */
+ public function testIdSubDataTabledIsPositiveWhenSubDataTableInMemory()
+ {
+ $testRow = $this->getTestRowWithSubDataTableLoaded();
+ $this->assertTrue($testRow->getIdSubDataTable() > 0);
+ }
+
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group Piwik_DataTable_Row
+ */
+ public function testDataTableAssociatedIsPositiveOnSerializedRow()
+ {
+ $testRow = $this->getTestRowWithSubDataTableLoaded();
+
+ // testDataTableAssociatedIsPositiveOnSerializedRow is only valid as long as the Row is not modified after being unserialized
+ $this->assertFalse(method_exists($testRow, '__wakeup'));
+
+ $serializedTestRow = serialize($testRow);
+ $unserializedTestRow = unserialize($serializedTestRow);
+
+ $this->assertTrue($unserializedTestRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] > 0);
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ * @group Piwik_DataTable_Row
+ */
+ public function testDataTableAssociatedIsNegativeAfterSerialize()
+ {
+ $testRow = $this->getTestRowWithSubDataTableLoaded();
+
+ serialize($testRow);
+
+ $testRow->cleanPostSerialize();
+
+ $this->assertTrue($testRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] < 0);
+ }
+
+ /**
+ *
+ * @group Core
+ * @group DataTable
+ * @group Piwik_DataTable_Row
+ */
+ public function testIsSubDataTableLoadedIsTrueWhenSubDataTableInMemory()
+ {
+ $testRow = $this->getTestRowWithSubDataTableLoaded();
+ $this->assertTrue($testRow->isSubtableLoaded());
+ }
+
+ /**
+ *
+ * @group Core
+ * @group DataTable
+ * @group Piwik_DataTable_Row
+ */
+ public function testIsSubDataTableLoadedIsFalseWhenSubDataTableNotInMemory()
+ {
+ $testRow = $this->getTestRowWithSubDataTableNotLoaded();
+ $this->assertFalse($testRow->isSubtableLoaded());
+ }
+
+ protected function getTestRowWithSubDataTableLoaded()
+ {
+ $testSubDataTable = $this->getTestSubDataTable();
+
+ $testRow = new Piwik_DataTable_Row(
+ array(
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $testSubDataTable
+ )
+ );
+
+ return $testRow;
+ }
+
+ protected function getTestSubDataTable()
+ {
+ return new Piwik_DataTable();
+ }
+
+ protected function getTestRowWithSubDataTableNotLoaded()
+ {
+ $testRow = new Piwik_DataTable_Row(
+ array(
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => 50
+ )
+ );
+
+ return $testRow;
+ }
}
diff --git a/tests/PHPUnit/Core/DataTableTest.php b/tests/PHPUnit/Core/DataTableTest.php
index 0a18e25fc8..9249d7ea72 100644
--- a/tests/PHPUnit/Core/DataTableTest.php
+++ b/tests/PHPUnit/Core/DataTableTest.php
@@ -15,26 +15,26 @@ class DataTableTest extends PHPUnit_Framework_TestCase
{
$table = $this->_getDataTable1ForTest();
$this->assertEquals(4, $table->getRowsCount());
- $table->filter('Limit', array(2,2));
+ $table->filter('Limit', array(2, 2));
$this->assertEquals(2, $table->getRowsCount());
- $table->filter('Piwik_DataTable_Filter_Limit', array(0,1));
+ $table->filter('Piwik_DataTable_Filter_Limit', array(0, 1));
$this->assertEquals(1, $table->getRowsCount());
}
-
+
protected function _getSimpleTestDataTable()
{
$table = new Piwik_DataTable;
$table->addRowsFromArray(
- array(
- array(Piwik_DataTable_Row::COLUMNS => array('label'=>'ten', 'count' => 10)),
- array(Piwik_DataTable_Row::COLUMNS => array('label'=>'ninety', 'count' => 90)),
- array(Piwik_DataTable_Row::COLUMNS => array('label'=>'hundred', 'count' => 100)),
- Piwik_DataTable::ID_SUMMARY_ROW => array( Piwik_DataTable_Row::COLUMNS => array('label'=> 'summary', 'count' => 200))
+ array(
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ten', 'count' => 10)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ninety', 'count' => 90)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'hundred', 'count' => 100)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array(Piwik_DataTable_Row::COLUMNS => array('label' => 'summary', 'count' => 200))
)
);
return $table;
}
-
+
/**
* @group Core
* @group DataTable
@@ -42,10 +42,10 @@ class DataTableTest extends PHPUnit_Framework_TestCase
public function testRenameColumn()
{
$table = $this->_getSimpleTestDataTable();
- $this->assertEquals(array(10,90,100,200), $table->getColumn('count'));
+ $this->assertEquals(array(10, 90, 100, 200), $table->getColumn('count'));
$table->renameColumn('count', 'renamed');
$this->assertEquals(array(false, false, false, false), $table->getColumn('count'));
- $this->assertEquals(array(10,90,100,200), $table->getColumn('renamed'));
+ $this->assertEquals(array(10, 90, 100, 200), $table->getColumn('renamed'));
}
/**
@@ -55,7 +55,7 @@ class DataTableTest extends PHPUnit_Framework_TestCase
public function testDeleteColumn()
{
$table = $this->_getSimpleTestDataTable();
- $this->assertEquals(array(10,90,100,200), $table->getColumn('count'));
+ $this->assertEquals(array(10, 90, 100, 200), $table->getColumn('count'));
$table->deleteColumn('count');
$this->assertEquals(array(false, false, false, false), $table->getColumn('count'));
}
@@ -67,7 +67,7 @@ class DataTableTest extends PHPUnit_Framework_TestCase
public function testDeleteRow()
{
$table = $this->_getSimpleTestDataTable();
-
+
// normal row
$idToDelete = 1;
$this->assertEquals(2, count($table->getRowFromId($idToDelete)->getColumns()));
@@ -80,7 +80,7 @@ class DataTableTest extends PHPUnit_Framework_TestCase
$table->deleteRow($idToDelete);
$this->assertFalse($table->getRowFromId($idToDelete));
}
-
+
/**
* @group Core
* @group DataTable
@@ -92,10 +92,10 @@ class DataTableTest extends PHPUnit_Framework_TestCase
$this->assertEquals($table->getLastRow(), $table->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW));
$table->deleteRow(Piwik_DataTable::ID_SUMMARY_ROW);
-
- $this->assertEquals($table->getLastRow(), $table->getRowFromId($rowsCount-2));
+
+ $this->assertEquals($table->getLastRow(), $table->getRowFromId($rowsCount - 2));
}
-
+
/**
* @group Core
* @group DataTable
@@ -106,228 +106,226 @@ class DataTableTest extends PHPUnit_Framework_TestCase
$idTable1 = $table1->getId();
$table2 = $this->_getDataTable2ForTest();
$this->assertFalse($table2->getRowFromIdSubDataTable($idTable1));
-
+
$table2->getFirstRow()->addSubtable($table1);
$this->assertEquals($table2->getRowFromIdSubDataTable($idTable1), $table2->getFirstRow());
-
+
$table3 = $this->_getDataTable1ForTest();
$idTable3 = $table3->getId();
$table2->getLastRow()->addSubtable($table3);
$this->assertEquals($table2->getRowFromIdSubDataTable($idTable3), $table2->getLastRow());
}
-
+
/**
* we test the count rows and the count rows recursive version
* on a Simple array (1 level only)
- *
+ *
* @group Core
* @group DataTable
*/
public function testCountRowsSimple()
{
-
- $table = new Piwik_DataTable;
- $idcol = Piwik_DataTable_Row::COLUMNS;
- $rows = array(
- array( $idcol => array('label'=>'google')),
- array( $idcol => array('label'=>'ask')),
- array( $idcol => array('label'=>'piwik')),
- array( $idcol => array('label'=>'yahoo')),
- array( $idcol => array('label'=>'amazon')),
- array( $idcol => array('label'=>'238975247578949')),
- array( $idcol => array('label'=>'Q*(%&*("$&%*(&"$*")"))')));
-
- $table->addRowsFromArray( $rows );
-
- $this->assertEquals(count($rows), $table->getRowsCount());
- $this->assertEquals(count($rows), $table->getRowsCountRecursive());
+
+ $table = new Piwik_DataTable;
+ $idcol = Piwik_DataTable_Row::COLUMNS;
+ $rows = array(
+ array($idcol => array('label' => 'google')),
+ array($idcol => array('label' => 'ask')),
+ array($idcol => array('label' => 'piwik')),
+ array($idcol => array('label' => 'yahoo')),
+ array($idcol => array('label' => 'amazon')),
+ array($idcol => array('label' => '238975247578949')),
+ array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')));
+
+ $table->addRowsFromArray($rows);
+
+ $this->assertEquals(count($rows), $table->getRowsCount());
+ $this->assertEquals(count($rows), $table->getRowsCountRecursive());
}
+
/**
* we test the count rows and the count rows recursive version
* on a Complex array (rows with 2 and 3 levels only)
- *
- * the recursive count returns
- * the sum of the number of rows of all the subtables
+ *
+ * the recursive count returns
+ * the sum of the number of rows of all the subtables
* + the number of rows in the parent table
- *
+ *
* @group Core
* @group DataTable
*/
public function testCountRowsComplex()
{
-
+
$idcol = Piwik_DataTable_Row::COLUMNS;
$idsubtable = Piwik_DataTable_Row::DATATABLE_ASSOCIATED;
-
+
// table to go in the SUB table of RoW1
$tableSubOfSubOfRow1 = new Piwik_DataTable;
$rows1sub = array(
- array( $idcol => array('label'=>'google')),
- array( $idcol => array('label'=>'google78')),
- array( $idcol => array('label'=>'googlaegge')),
- array( $idcol => array('label'=>'gogeoggle')),
- array( $idcol => array('label'=>'goaegaegaogle')),
- array( $idcol => array('label'=>'ask')),
- array( $idcol => array('label'=>'238975247578949')),
+ array($idcol => array('label' => 'google')),
+ array($idcol => array('label' => 'google78')),
+ array($idcol => array('label' => 'googlaegge')),
+ array($idcol => array('label' => 'gogeoggle')),
+ array($idcol => array('label' => 'goaegaegaogle')),
+ array($idcol => array('label' => 'ask')),
+ array($idcol => array('label' => '238975247578949')),
);
- $tableSubOfSubOfRow1->addRowsFromArray( $rows1sub );
-
+ $tableSubOfSubOfRow1->addRowsFromArray($rows1sub);
+
// table to go in row1
$tableSubOfRow1 = new Piwik_DataTable;
$rows1 = array(
- array( $idcol => array('label'=>'google'), $idsubtable =>$tableSubOfSubOfRow1),
- array( $idcol => array('label'=>'ask')),
- array( $idcol => array('label'=>'238975247578949')),
+ array($idcol => array('label' => 'google'), $idsubtable => $tableSubOfSubOfRow1),
+ array($idcol => array('label' => 'ask')),
+ array($idcol => array('label' => '238975247578949')),
);
- $tableSubOfRow1->addRowsFromArray( $rows1 );
-
+ $tableSubOfRow1->addRowsFromArray($rows1);
+
// table to go in row2
$tableSubOfRow2 = new Piwik_DataTable;
$rows2 = array(
- array( $idcol => array('label'=>'google')),
- array( $idcol => array('label'=>'ask')),
- array( $idcol => array('label'=>'238975247578949')),
- array( $idcol => array('label'=>'agaegaesk')),
- array( $idcol => array('label'=>'23g 8975247578949')),
+ array($idcol => array('label' => 'google')),
+ array($idcol => array('label' => 'ask')),
+ array($idcol => array('label' => '238975247578949')),
+ array($idcol => array('label' => 'agaegaesk')),
+ array($idcol => array('label' => '23g 8975247578949')),
);
- $tableSubOfRow2->addRowsFromArray( $rows2 );
-
+ $tableSubOfRow2->addRowsFromArray($rows2);
+
// main parent table
$table = new Piwik_DataTable;
$rows = array(
- array( $idcol => array('label'=>'row1')),
- array( $idcol => array('label'=>'row2'),
- $idsubtable => $tableSubOfRow1),
- array( $idcol => array('label'=>'row3'),
- $idsubtable => $tableSubOfRow2),
+ array($idcol => array('label' => 'row1')),
+ array($idcol => array('label' => 'row2'),
+ $idsubtable => $tableSubOfRow1),
+ array($idcol => array('label' => 'row3'),
+ $idsubtable => $tableSubOfRow2),
);
- $table->addRowsFromArray( $rows );
-
-
+ $table->addRowsFromArray($rows);
+
+
$this->assertEquals(count($rows), $table->getRowsCount());
- $countAllRows = count($rows)+count($rows1)+count($rows2) + count($rows1sub);
+ $countAllRows = count($rows) + count($rows1) + count($rows2) + count($rows1sub);
$this->assertEquals($countAllRows, $table->getRowsCountRecursive());
}
-
+
/**
* Simple test of the DataTable_Row
- *
+ *
* @group Core
* @group DataTable
*/
public function testRow()
{
- $columns = array('test_column'=> 145,
- 092582495 => new Piwik_Timer,
- 'super'=>array('this column has an array value, amazing'));
- $metadata = array('logo'=> 'piwik.png',
- 'super'=>array('this column has an array value, amazing'));
+ $columns = array('test_column' => 145,
+ 092582495 => new Piwik_Timer,
+ 'super' => array('this column has an array value, amazing'));
+ $metadata = array('logo' => 'piwik.png',
+ 'super' => array('this column has an array value, amazing'));
$arrayRow = array(
- Piwik_DataTable_Row::COLUMNS => $columns,
+ Piwik_DataTable_Row::COLUMNS => $columns,
Piwik_DataTable_Row::METADATA => $metadata,
- 'fake useless key'=>38959,
- 43905724897=>'value');
+ 'fake useless key' => 38959,
+ 43905724897 => 'value');
$row = new Piwik_DataTable_Row($arrayRow);
-
+
$this->assertEquals($columns, $row->getColumns());
$this->assertEquals($metadata, $row->getMetadata());
$this->assertNull($row->getIdSubDataTable());
-
+
}
-
+
/**
* Simple test of the DataTable_Row
- *
+ *
* @group Core
* @group DataTable
*/
public function testSumRow()
{
- $columns = array('test_int'=> 145,
- 'test_float'=> 145.5,
- 'test_float3'=> 1.5,
- 'test_stringint'=> "145",
- "test" => 'string fake',
- 'integerArrayToSum'=>array( 1 => 1, 2 => 10.0, 3 => array(1 => 2, 2 => 3)),
- );
- $metadata = array('logo'=> 'piwik.png',
- 'super'=>array('this column has an array value, amazing'));
+ $columns = array('test_int' => 145,
+ 'test_float' => 145.5,
+ 'test_float3' => 1.5,
+ 'test_stringint' => "145",
+ "test" => 'string fake',
+ 'integerArrayToSum' => array(1 => 1, 2 => 10.0, 3 => array(1 => 2, 2 => 3)),
+ );
+ $metadata = array('logo' => 'piwik.png',
+ 'super' => array('this column has an array value, amazing'));
$arrayRow = array(
- Piwik_DataTable_Row::COLUMNS => $columns,
+ Piwik_DataTable_Row::COLUMNS => $columns,
Piwik_DataTable_Row::METADATA => $metadata,
- 'fake useless key'=>38959,
- 43905724897=>'value');
+ 'fake useless key' => 38959,
+ 43905724897 => 'value');
$row1 = new Piwik_DataTable_Row($arrayRow);
-
- $columns2 = array('test_int'=> 5,
- 'test_float'=> 4.5,
- 'test_float2'=> 14.5,
- 'test_stringint'=> "5",
- 0925824 => 'toto',
- 'integerArrayToSum'=>array( 1 => 5, 2 => 5.5, 3 => array(2 => 4)),
- );
- $finalRow = new Piwik_DataTable_Row( array(Piwik_DataTable_Row::COLUMNS => $columns2));
+
+ $columns2 = array('test_int' => 5,
+ 'test_float' => 4.5,
+ 'test_float2' => 14.5,
+ 'test_stringint' => "5",
+ 0925824 => 'toto',
+ 'integerArrayToSum' => array(1 => 5, 2 => 5.5, 3 => array(2 => 4)),
+ );
+ $finalRow = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => $columns2));
$finalRow->sumRow($row1);
- $columnsWanted = array('test_int'=> 150,
- 'test_float'=> 150.0,
- 'test_float2'=> 14.5,
- 'test_float3'=> 1.5,
- 'test_stringint'=> 150, //add also strings!!
- 'test' => 'string fake',
- 'integerArrayToSum' => array( 1 => 6, 2 => 15.5, 3 => array(1 => 2, 2 => 7)),
- 0925824 => 'toto',
- );
-
- // Also testing that metadata is copied over
- $rowWanted = new Piwik_DataTable_Row( array(Piwik_DataTable_Row::COLUMNS => $columnsWanted,Piwik_DataTable_Row::METADATA => $metadata));
- $this->assertTrue( Piwik_DataTable_Row::isEqual($rowWanted, $finalRow));
-
-
- // testing that, 'sumRow' does not result in extra unwanted attributes being serialized
- $expectedRow = 'O:19:"Piwik_DataTable_Row":1:{s:1:"c";a:3:{i:0;a:8:{s:8:"test_int";i:150;s:10:"test_float";d:150;s:11:"test_float2";d:14.5;s:14:"test_stringint";i:150;i:0;s:4:"toto";s:17:"integerArrayToSum";a:3:{i:1;i:6;i:2;d:15.5;i:3;a:2:{i:2;i:7;i:1;i:2;}}s:11:"test_float3";d:1.5;s:4:"test";s:11:"string fake";}i:1;a:2:{s:4:"logo";s:9:"piwik.png";s:5:"super";a:1:{i:0;s:39:"this column has an array value, amazing";}}i:3;N;}}';
- $this->assertEquals( serialize($finalRow), $expectedRow);
-
- // Testing sumRow with disabled metadata sum
- $rowWanted = new Piwik_DataTable_Row( array(Piwik_DataTable_Row::COLUMNS => $columnsWanted)); // no metadata
- $finalRow = new Piwik_DataTable_Row( array(Piwik_DataTable_Row::COLUMNS => $columns2));
- $finalRow->sumRow($row1, $enableCopyMetadata = false);
- $this->assertTrue( Piwik_DataTable_Row::isEqual($rowWanted, $finalRow));
+ $columnsWanted = array('test_int' => 150,
+ 'test_float' => 150.0,
+ 'test_float2' => 14.5,
+ 'test_float3' => 1.5,
+ 'test_stringint' => 150, //add also strings!!
+ 'test' => 'string fake',
+ 'integerArrayToSum' => array(1 => 6, 2 => 15.5, 3 => array(1 => 2, 2 => 7)),
+ 0925824 => 'toto',
+ );
+
+ // Also testing that metadata is copied over
+ $rowWanted = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => $columnsWanted, Piwik_DataTable_Row::METADATA => $metadata));
+ $this->assertTrue(Piwik_DataTable_Row::isEqual($rowWanted, $finalRow));
+
+
+ // testing that, 'sumRow' does not result in extra unwanted attributes being serialized
+ $expectedRow = 'O:19:"Piwik_DataTable_Row":1:{s:1:"c";a:3:{i:0;a:8:{s:8:"test_int";i:150;s:10:"test_float";d:150;s:11:"test_float2";d:14.5;s:14:"test_stringint";i:150;i:0;s:4:"toto";s:17:"integerArrayToSum";a:3:{i:1;i:6;i:2;d:15.5;i:3;a:2:{i:2;i:7;i:1;i:2;}}s:11:"test_float3";d:1.5;s:4:"test";s:11:"string fake";}i:1;a:2:{s:4:"logo";s:9:"piwik.png";s:5:"super";a:1:{i:0;s:39:"this column has an array value, amazing";}}i:3;N;}}';
+ $this->assertEquals(serialize($finalRow), $expectedRow);
+
+ // Testing sumRow with disabled metadata sum
+ $rowWanted = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => $columnsWanted)); // no metadata
+ $finalRow = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => $columns2));
+ $finalRow->sumRow($row1, $enableCopyMetadata = false);
+ $this->assertTrue(Piwik_DataTable_Row::isEqual($rowWanted, $finalRow));
}
- /**
- * Test that adding two string column values results in an exception.
- *
+ /**
+ * Test that adding two string column values results in an exception.
+ *
* @group Core
* @group DataTable
- */
- public function testSumRow_stringException()
- {
- $columns = array(
- 'super'=>array('this column has an array string that will be 0 when algorithm sums the value'),
- );
- $row1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => $columns));
-
- $columns2 = array(
- 'super'=>array('this column has geagaean array value, amazing'),
- );
- $row2 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => $columns2));
-
- // TODO: when phpunit 3.7 is released, can do check w/ "@expectedException Exception"
- try
- {
- $row2->sumRow($row1);
- $this->fail("sumRow did not throw when adding two string columns.");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
+ */
+ public function testSumRow_stringException()
+ {
+ $columns = array(
+ 'super' => array('this column has an array string that will be 0 when algorithm sums the value'),
+ );
+ $row1 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => $columns));
+
+ $columns2 = array(
+ 'super' => array('this column has geagaean array value, amazing'),
+ );
+ $row2 = new Piwik_DataTable_Row(array(Piwik_DataTable_Row::COLUMNS => $columns2));
+
+ // TODO: when phpunit 3.7 is released, can do check w/ "@expectedException Exception"
+ try {
+ $row2->sumRow($row1);
+ $this->fail("sumRow did not throw when adding two string columns.");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
/**
* Test serialize with an infinite recursion (a row linked to a table in the parent hierarchy)
* After 100 recursion must throw an exception
- *
+ *
* @group Core
* @group DataTable
*/
@@ -335,8 +333,8 @@ class DataTableTest extends PHPUnit_Framework_TestCase
{
try {
$table = new Piwik_DataTable;
- $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 'visits'=>245,'visitors'=>245),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $table,));
+ $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array('visits' => 245, 'visitors' => 245),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $table,));
$table->getSerialized();
} catch (Exception $e) {
@@ -344,57 +342,57 @@ class DataTableTest extends PHPUnit_Framework_TestCase
}
$this->fail('Expected exception not raised');
}
-
-
+
+
/**
* Test queing filters
- *
+ *
* @group Core
* @group DataTable
*/
public function testFilterQueueSortString()
{
-
+
$idcol = Piwik_DataTable_Row::COLUMNS;
-
+
$table = new Piwik_DataTable;
$rows = array(
- array( $idcol => array('label'=>'google')),//0
- array( $idcol => array('label'=>'tsk')),//1
- array( $idcol => array('label'=>'Q*(%&*("$&%*(&"$*")"))')),//2
- );
- $table->addRowsFromArray( $rows );
-
+ array($idcol => array('label' => 'google')), //0
+ array($idcol => array('label' => 'tsk')), //1
+ array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')), //2
+ );
+ $table->addRowsFromArray($rows);
+
$expectedtable = new Piwik_DataTable;
$rows = array(
- array( $idcol => array('label'=>'google')),//0
- array( $idcol => array('label'=>'Q*(%&*("$&%*(&"$*")"))')),//2
- array( $idcol => array('label'=>'tsk')),//1
- );
- $expectedtable->addRowsFromArray( $rows );
-
+ array($idcol => array('label' => 'google')), //0
+ array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')), //2
+ array($idcol => array('label' => 'tsk')), //1
+ );
+ $expectedtable->addRowsFromArray($rows);
+
$expectedtableReverse = new Piwik_DataTable;
$expectedtableReverse->addRowsFromArray(array_reverse($rows));
-
+
$tableCopy = clone $table;
$this->assertTrue(Piwik_DataTable::isEqual($tableCopy, $table));
-
+
// queue the filter and check the table didnt change
$table->queueFilter("Sort", array('label', 'asc'));
$this->assertTrue(Piwik_DataTable::isEqual($tableCopy, $table));
-
+
// apply filter and check the table is sorted
$table->applyQueuedFilters();
$this->assertTrue(Piwik_DataTable::isEqual($expectedtable, $table));
-
+
// apply one more filter check it hasnt changed
$table->queueFilter("Sort", array('label', 'desc'));
$this->assertTrue(Piwik_DataTable::isEqual($expectedtable, $table));
-
+
// now apply the second sort and check it is correctly sorted
$table->applyQueuedFilters();
$this->assertTrue(Piwik_DataTable::isEqual($expectedtableReverse, $table));
-
+
// do one more time to make sure it doesnt change
$table->applyQueuedFilters();
$this->assertTrue(Piwik_DataTable::isEqual($expectedtableReverse, $table));
@@ -402,11 +400,11 @@ class DataTableTest extends PHPUnit_Framework_TestCase
/**
* General tests that tries to test the normal behaviour of DataTable
- *
+ *
* We create some tables, add rows, some of the rows link to sub tables
- *
+ *
* Then we serialize everything, and we check that the unserialize give the same object back
- *
+ *
* @group Core
* @group DataTable
*/
@@ -417,11 +415,11 @@ class DataTableTest extends PHPUnit_Framework_TestCase
* does not take in consideration those tables
*/
$useless1 = new Piwik_DataTable;
- $useless1->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 13,),));
+ $useless1->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array(13,),));
/*
* end fake tables
*/
-
+
/*
* MAIN TABLE
*/
@@ -429,95 +427,94 @@ class DataTableTest extends PHPUnit_Framework_TestCase
$subtable = new Piwik_DataTable;
$idtable = $table->getId();
$idsubtable = $subtable->getId();
-
+
/*
* create some fake tables to make sure that the serialized array of the first TABLE
* does not take in consideration those tables
* -> we check that the DataTable_Manager is not impacting DataTable
*/
$useless2 = new Piwik_DataTable;
- $useless1->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 8487,),));
+ $useless1->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array(8487,),));
$useless3 = new Piwik_DataTable;
- $useless3->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 8487,),));
+ $useless3->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array(8487,),));
/*
* end fake tables
*/
-
- $row = array(Piwik_DataTable_Row::COLUMNS => array( 0 => 1554, 1 => 42, 2 => 657,3 => 155744,),
- Piwik_DataTable_Row::METADATA => array('logo' => 'test.png'));
+
+ $row = array(Piwik_DataTable_Row::COLUMNS => array(0 => 1554, 1 => 42, 2 => 657, 3 => 155744,),
+ Piwik_DataTable_Row::METADATA => array('logo' => 'test.png'));
$row = new Piwik_DataTable_Row($row);
-
+
$table->addRow($row);
- $table->addRowFromArray(array( Piwik_DataTable_Row::COLUMNS => array( 0 => 1554,1 => 42,),
- Piwik_DataTable_Row::METADATA => array('url' => 'piwik.org')));
-
- $table->addRowFromArray(array( Piwik_DataTable_Row::COLUMNS => array( 0 => 787877888787,),
- Piwik_DataTable_Row::METADATA => array('url' => 'OUPLA ADDED'),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subtable));
-
+ $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array(0 => 1554, 1 => 42,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'piwik.org')));
+
+ $table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array(0 => 787877888787,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'OUPLA ADDED'),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subtable));
+
/*
* SUB TABLE
- */
-
-
- $row = array( Piwik_DataTable_Row::COLUMNS => array( 0 => 1554,),
- Piwik_DataTable_Row::METADATA => array('searchengine' => 'google'),
- );
+ */
+
+
+ $row = array(Piwik_DataTable_Row::COLUMNS => array(0 => 1554,),
+ Piwik_DataTable_Row::METADATA => array('searchengine' => 'google'),
+ );
$subtable->addRowFromArray($row);
-
- $row = array( Piwik_DataTable_Row::COLUMNS => array( 0 => 84894,),
- Piwik_DataTable_Row::METADATA => array('searchengine' => 'yahoo'),
- );
+
+ $row = array(Piwik_DataTable_Row::COLUMNS => array(0 => 84894,),
+ Piwik_DataTable_Row::METADATA => array('searchengine' => 'yahoo'),
+ );
$subtable->addRowFromArray($row);
- $row = array( Piwik_DataTable_Row::COLUMNS => array( 0 => 4898978989,),
- Piwik_DataTable_Row::METADATA => array('searchengine' => 'ask'),
- );
+ $row = array(Piwik_DataTable_Row::COLUMNS => array(0 => 4898978989,),
+ Piwik_DataTable_Row::METADATA => array('searchengine' => 'ask'),
+ );
$subtable->addRowFromArray($row);
-
-
+
+
/*
* SUB SUB TABLE
*/
$subsubtable = new Piwik_DataTable;
- $subsubtable->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 245),
- Piwik_DataTable_Row::METADATA => array('yes' => 'subsubmetadata1'),)
- );
-
- $subsubtable->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array( 13,),
- Piwik_DataTable_Row::METADATA => array('yes' => 'subsubmetadata2'),)
- );
-
- $row = array( Piwik_DataTable_Row::COLUMNS => array( 0 => 666666666666666,),
- Piwik_DataTable_Row::METADATA => array('url' => 'NEW ROW ADDED'),
- Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subsubtable);
-
+ $subsubtable->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array(245),
+ Piwik_DataTable_Row::METADATA => array('yes' => 'subsubmetadata1'),)
+ );
+
+ $subsubtable->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => array(13,),
+ Piwik_DataTable_Row::METADATA => array('yes' => 'subsubmetadata2'),)
+ );
+
+ $row = array(Piwik_DataTable_Row::COLUMNS => array(0 => 666666666666666,),
+ Piwik_DataTable_Row::METADATA => array('url' => 'NEW ROW ADDED'),
+ Piwik_DataTable_Row::DATATABLE_ASSOCIATED => $subsubtable);
+
$subtable->addRowFromArray($row);
-
+
$idsubsubtable = $subsubtable->getId();
-
+
$serialized = ($table->getSerialized());
-
- $this->assertEquals(array_keys($serialized), array($idsubsubtable,$idsubtable,0));
-
- // In the next test we compare an unserialized datatable with its original instance.
- // The unserialized datatable rows will have positive DATATABLE_ASSOCIATED ids.
- // Positive DATATABLE_ASSOCIATED ids mean that the associated sub-datatables are not loaded in memory.
- // In this case, this is NOT true: we know that the sub-datatable is loaded in memory.
- // HOWEVER, because of datatable id conflicts happening in the datatable manager, it is not yet
- // possible to know, after unserializing a datatable, if its sub-datatables are loaded in memory.
- $expectedTableRows = array();
- foreach ($table->getRows() as $currentRow) {
- $expectedTableRow = clone $currentRow;
-
- $currentRowAssociatedDatatableId = $currentRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED];
- if($currentRowAssociatedDatatableId != null)
- {
- // making DATATABLE_ASSOCIATED ids positive
- $expectedTableRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = -1 * $currentRowAssociatedDatatableId;
- }
-
- $expectedTableRows[] = $expectedTableRow;
- }
+
+ $this->assertEquals(array_keys($serialized), array($idsubsubtable, $idsubtable, 0));
+
+ // In the next test we compare an unserialized datatable with its original instance.
+ // The unserialized datatable rows will have positive DATATABLE_ASSOCIATED ids.
+ // Positive DATATABLE_ASSOCIATED ids mean that the associated sub-datatables are not loaded in memory.
+ // In this case, this is NOT true: we know that the sub-datatable is loaded in memory.
+ // HOWEVER, because of datatable id conflicts happening in the datatable manager, it is not yet
+ // possible to know, after unserializing a datatable, if its sub-datatables are loaded in memory.
+ $expectedTableRows = array();
+ foreach ($table->getRows() as $currentRow) {
+ $expectedTableRow = clone $currentRow;
+
+ $currentRowAssociatedDatatableId = $currentRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED];
+ if ($currentRowAssociatedDatatableId != null) {
+ // making DATATABLE_ASSOCIATED ids positive
+ $expectedTableRow->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = -1 * $currentRowAssociatedDatatableId;
+ }
+
+ $expectedTableRows[] = $expectedTableRow;
+ }
$tableAfter = new Piwik_DataTable;
$tableAfter->addRowsFromSerializedArray($serialized[0]);
@@ -526,12 +523,12 @@ class DataTableTest extends PHPUnit_Framework_TestCase
$subsubtableAfter = new Piwik_DataTable;
$subsubtableAfter->addRowsFromSerializedArray($serialized[$idsubsubtable]);
- $this->assertEquals($subsubtable->getRows(),$subsubtableAfter->getRows());
-
+ $this->assertEquals($subsubtable->getRows(), $subsubtableAfter->getRows());
+
$this->assertEquals($table, Piwik_DataTable_Manager::getInstance()->getTable($idtable));
$this->assertEquals($subsubtable, Piwik_DataTable_Manager::getInstance()->getTable($idsubsubtable));
}
-
+
/**
* for all datatable->addDatatable tests we check that
* - row uniqueness is based on the label + presence of the SUBTABLE id
@@ -539,11 +536,11 @@ class DataTableTest extends PHPUnit_Framework_TestCase
* - no metadata are lost in the first datatable rows that have been changed
* - when a subtable
*/
-
-
+
+
/**
* add an empty datatable to a normal datatable
- *
+ *
* @group Core
* @group DataTable
*/
@@ -553,12 +550,12 @@ class DataTableTest extends PHPUnit_Framework_TestCase
$tableEmpty = new Piwik_DataTable;
$tableAfter = clone $table;
$tableAfter->addDataTable($tableEmpty);
- $this->assertTrue( Piwik_DataTable::isEqual($table, $tableAfter) );
+ $this->assertTrue(Piwik_DataTable::isEqual($table, $tableAfter));
}
-
+
/**
* add a normal datatable to an empty datatable
- *
+ *
* @group Core
* @group DataTable
*/
@@ -567,12 +564,12 @@ class DataTableTest extends PHPUnit_Framework_TestCase
$table = $this->_getDataTable1ForTest();
$tableEmpty = new Piwik_DataTable;
$tableEmpty->addDataTable($table);
- $this->assertTrue( Piwik_DataTable::isEqual($tableEmpty, $table) );
+ $this->assertTrue(Piwik_DataTable::isEqual($tableEmpty, $table));
}
/**
* add to the datatable another datatable// they don't have any row in common
- *
+ *
* @group Core
* @group DataTable
*/
@@ -580,234 +577,234 @@ class DataTableTest extends PHPUnit_Framework_TestCase
{
$table1 = $this->_getDataTable1ForTest();
$table2 = $this->_getDataTable2ForTest();
-
+
$table1->addDataTable($table2);
-
- $rowsExpected = array_merge($this->_getRowsDataTable1ForTest(),$this->_getRowsDataTable2ForTest());
+
+ $rowsExpected = array_merge($this->_getRowsDataTable1ForTest(), $this->_getRowsDataTable2ForTest());
$tableExpected = new Piwik_DataTable;
- $tableExpected->addRowsFromArray( $rowsExpected );
-
- $this->assertTrue( Piwik_DataTable::isEqual($table1, $tableExpected) );
+ $tableExpected->addRowsFromArray($rowsExpected);
+
+ $this->assertTrue(Piwik_DataTable::isEqual($table1, $tableExpected));
}
-
+
/**
* add 2 datatable with some common rows
- *
+ *
* @group Core
* @group DataTable
*/
public function testAddSimpleSomeCommonRow()
{
-
+
$idcol = Piwik_DataTable_Row::COLUMNS;
-
+
$rows = array(
- array( $idcol => array('label'=>'google', 'visits' => 1)),
- array( $idcol => array('label'=>'ask', 'visits' => 2)),
- array( $idcol => array('label'=>'123', 'visits' => 2)),
- Piwik_DataTable::ID_SUMMARY_ROW => array( $idcol => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 7))
- );
+ array($idcol => array('label' => 'google', 'visits' => 1)),
+ array($idcol => array('label' => 'ask', 'visits' => 2)),
+ array($idcol => array('label' => '123', 'visits' => 2)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array($idcol => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 7))
+ );
$table = new Piwik_DataTable;
- $table->addRowsFromArray( $rows );
-
+ $table->addRowsFromArray($rows);
+
$rows2 = array(
- array( $idcol => array('label'=>'test', 'visits' => 1)),
- array( $idcol => array('label'=>'ask', 'visits' => 111)),
- array( $idcol => array('label'=>' google ', 'visits' => 5)),
- array( $idcol => array('label'=>'123', 'visits' => 2)),
- );
+ array($idcol => array('label' => 'test', 'visits' => 1)),
+ array($idcol => array('label' => 'ask', 'visits' => 111)),
+ array($idcol => array('label' => ' google ', 'visits' => 5)),
+ array($idcol => array('label' => '123', 'visits' => 2)),
+ );
$table2 = new Piwik_DataTable;
- $table2->addRowsFromArray( $rows2 );
-
+ $table2->addRowsFromArray($rows2);
+
$table->addDataTable($table2);
-
+
$rowsExpected = array(
- array( $idcol => array('label'=>'google', 'visits' => 1)),
- array( $idcol => array('label'=>'ask', 'visits' => 113)),
- array( $idcol => array('label'=>'123', 'visits' => 4)),
- array( $idcol => array('label'=>'test', 'visits' => 1)),
- array( $idcol => array('label'=>' google ', 'visits' => 5)),
- Piwik_DataTable::ID_SUMMARY_ROW => array( $idcol => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 7))
- );
+ array($idcol => array('label' => 'google', 'visits' => 1)),
+ array($idcol => array('label' => 'ask', 'visits' => 113)),
+ array($idcol => array('label' => '123', 'visits' => 4)),
+ array($idcol => array('label' => 'test', 'visits' => 1)),
+ array($idcol => array('label' => ' google ', 'visits' => 5)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array($idcol => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 7))
+ );
$tableExpected = new Piwik_DataTable;
- $tableExpected->addRowsFromArray( $rowsExpected );
-
- $this->assertTrue( Piwik_DataTable::isEqual($table, $tableExpected) );
+ $tableExpected->addRowsFromArray($rowsExpected);
+
+ $this->assertTrue(Piwik_DataTable::isEqual($table, $tableExpected));
}
-
+
/**
* add 2 datatable with only common rows
- *
+ *
* @group Core
* @group DataTable
*/
public function testAddSimpleAllCommonRow()
{
$idcol = Piwik_DataTable_Row::COLUMNS;
-
+
$rows = array(
- array( $idcol => array('label'=>'google', 'visits' => 1)),
- array( $idcol => array('label'=>'ask', 'visits' => 2)),
- array( $idcol => array('label'=>'123', 'visits' => 2)),
- Piwik_DataTable::ID_SUMMARY_ROW => array( $idcol => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 7))
- );
+ array($idcol => array('label' => 'google', 'visits' => 1)),
+ array($idcol => array('label' => 'ask', 'visits' => 2)),
+ array($idcol => array('label' => '123', 'visits' => 2)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array($idcol => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 7))
+ );
$table = new Piwik_DataTable;
- $table->addRowsFromArray( $rows );
-
+ $table->addRowsFromArray($rows);
+
$rows2 = array(
- array( $idcol => array('label'=>'google', 'visits' => -1)),
- array( $idcol => array('label'=>'ask', 'visits' => 0)),
- array( $idcol => array('label'=>'123', 'visits' => 1.5)),
- Piwik_DataTable::ID_SUMMARY_ROW => array( $idcol => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 8))
- );
+ array($idcol => array('label' => 'google', 'visits' => -1)),
+ array($idcol => array('label' => 'ask', 'visits' => 0)),
+ array($idcol => array('label' => '123', 'visits' => 1.5)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array($idcol => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 8))
+ );
$table2 = new Piwik_DataTable;
- $table2->addRowsFromArray( $rows2 );
-
+ $table2->addRowsFromArray($rows2);
+
$table->addDataTable($table2);
-
+
$rowsExpected = array(
- array( $idcol => array('label'=>'google', 'visits' => 0)),
- array( $idcol => array('label'=>'ask', 'visits' => 2)),
- array( $idcol => array('label'=>'123', 'visits' => 3.5)),
- Piwik_DataTable::ID_SUMMARY_ROW => array( $idcol => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 15))
- );
+ array($idcol => array('label' => 'google', 'visits' => 0)),
+ array($idcol => array('label' => 'ask', 'visits' => 2)),
+ array($idcol => array('label' => '123', 'visits' => 3.5)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array($idcol => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 15))
+ );
$tableExpected = new Piwik_DataTable;
- $tableExpected->addRowsFromArray( $rowsExpected );
-
- $this->assertTrue( Piwik_DataTable::isEqual($table, $tableExpected) );
+ $tableExpected->addRowsFromArray($rowsExpected);
+
+ $this->assertTrue(Piwik_DataTable::isEqual($table, $tableExpected));
}
/**
* test add 2 different tables to the same table
- *
+ *
* @group Core
* @group DataTable
*/
public function testAddDataTable2times()
{
-
+
$idcol = Piwik_DataTable_Row::COLUMNS;
-
+
$rows = array(
- array( $idcol => array('label'=>'google', 'visits' => 1)),
- array( $idcol => array('label'=>'ask', 'visits' => 0)),
- array( $idcol => array('label'=>'123', 'visits' => 2)),
- Piwik_DataTable::ID_SUMMARY_ROW => array( $idcol => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 1))
- );
+ array($idcol => array('label' => 'google', 'visits' => 1)),
+ array($idcol => array('label' => 'ask', 'visits' => 0)),
+ array($idcol => array('label' => '123', 'visits' => 2)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array($idcol => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 1))
+ );
$table = new Piwik_DataTable;
- $table->addRowsFromArray( $rows );
-
+ $table->addRowsFromArray($rows);
+
$rows2 = array(
- array( $idcol => array('label'=>'google2', 'visits' => -1)),
- array( $idcol => array('label'=>'ask', 'visits' => 100)),
- array( $idcol => array('label'=>'123456', 'visits' => 1.5)),
- );
+ array($idcol => array('label' => 'google2', 'visits' => -1)),
+ array($idcol => array('label' => 'ask', 'visits' => 100)),
+ array($idcol => array('label' => '123456', 'visits' => 1.5)),
+ );
$table2 = new Piwik_DataTable;
- $table2->addRowsFromArray( $rows2 );
-
+ $table2->addRowsFromArray($rows2);
+
$rows3 = array(
- array( $idcol => array('label'=>'google2', 'visits' => -1)),
- array( $idcol => array('label'=>'ask', 'visits' => -10)),
- array( $idcol => array('label'=>'123ab', 'visits' => 1.5)),
- Piwik_DataTable::ID_SUMMARY_ROW => array( $idcol => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 3))
- );
+ array($idcol => array('label' => 'google2', 'visits' => -1)),
+ array($idcol => array('label' => 'ask', 'visits' => -10)),
+ array($idcol => array('label' => '123ab', 'visits' => 1.5)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array($idcol => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 3))
+ );
$table3 = new Piwik_DataTable;
- $table3->addRowsFromArray( $rows3 );
-
+ $table3->addRowsFromArray($rows3);
+
// add the 2 tables
$table->addDataTable($table2);
$table->addDataTable($table3);
-
+
$rowsExpected = array(
- array( $idcol => array('label'=>'google', 'visits' => 1)),
- array( $idcol => array('label'=>'ask', 'visits' => 90)),
- array( $idcol => array('label'=>'123', 'visits' => 2)),
- array( $idcol => array('label'=>'google2', 'visits' => -2)),
- array( $idcol => array('label'=>'123456', 'visits' => 1.5)),
- array( $idcol => array('label'=>'123ab', 'visits' => 1.5)),
- Piwik_DataTable::ID_SUMMARY_ROW => array( $idcol => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 4))
+ array($idcol => array('label' => 'google', 'visits' => 1)),
+ array($idcol => array('label' => 'ask', 'visits' => 90)),
+ array($idcol => array('label' => '123', 'visits' => 2)),
+ array($idcol => array('label' => 'google2', 'visits' => -2)),
+ array($idcol => array('label' => '123456', 'visits' => 1.5)),
+ array($idcol => array('label' => '123ab', 'visits' => 1.5)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array($idcol => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 4))
);
$tableExpected = new Piwik_DataTable;
- $tableExpected->addRowsFromArray( $rowsExpected );
-
- $this->assertTrue( Piwik_DataTable::isEqual($table, $tableExpected) );
+ $tableExpected->addRowsFromArray($rowsExpected);
+
+ $this->assertTrue(Piwik_DataTable::isEqual($table, $tableExpected));
+ }
+
+
+ /**
+ * @group Core
+ * @group DataTable
+ */
+ public function testUnrelatedDataTableNotDestructed()
+ {
+ $mockedDataTable = $this->getMock('Piwik_DataTable', array('__destruct'));
+ $mockedDataTable->expects($this->never())->method('__destruct');
+
+ $rowBeingDestructed = new Piwik_DataTable_Row();
+
+ // we simulate the fact that the value of Piwik_DataTable_Row::DATATABLE_ASSOCIATED retrieved
+ // from the database is in conflict with one of the Piwik_DataTable_Manager managed table identifiers.
+ // This is a rare but legitimate case as identifiers are not thoroughly synchronized
+ // when the expanded parameter is false.
+ $rowBeingDestructed->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $mockedDataTable->getId();
+
+ destroy($rowBeingDestructed);
}
+ /**
+ * @group Core
+ * @group DataTable
+ */
+ public function testGetSerializedCallsCleanPostSerialize()
+ {
+ $mockedDataTableRow = $this->getMock('Piwik_DataTable_Row', array('cleanPostSerialize'));
+ $mockedDataTableRow->expects($this->once())->method('cleanPostSerialize');
- /**
- * @group Core
- * @group DataTable
- */
- public function testUnrelatedDataTableNotDestructed()
- {
- $mockedDataTable = $this->getMock('Piwik_DataTable', array('__destruct'));
- $mockedDataTable->expects($this->never())->method('__destruct');
-
- $rowBeingDestructed = new Piwik_DataTable_Row();
-
- // we simulate the fact that the value of Piwik_DataTable_Row::DATATABLE_ASSOCIATED retrieved
- // from the database is in conflict with one of the Piwik_DataTable_Manager managed table identifiers.
- // This is a rare but legitimate case as identifiers are not thoroughly synchronized
- // when the expanded parameter is false.
- $rowBeingDestructed->c[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $mockedDataTable->getId();
-
- destroy($rowBeingDestructed);
- }
-
- /**
- * @group Core
- * @group DataTable
- */
- public function testGetSerializedCallsCleanPostSerialize()
- {
- $mockedDataTableRow = $this->getMock('Piwik_DataTable_Row', array('cleanPostSerialize'));
- $mockedDataTableRow->expects($this->once())->method('cleanPostSerialize');
-
- $dataTableBeingSerialized = new Piwik_DataTable();
- $dataTableBeingSerialized->addRow($mockedDataTableRow);
-
- $dataTableBeingSerialized->getSerialized();
- }
-
- /**
- * @group Core
- * @group DataTable
- */
- public function testSubDataTableIsDestructed()
- {
- $mockedDataTable = $this->getMock('Piwik_DataTable', array('__destruct'));
- $mockedDataTable->expects($this->once())->method('__destruct');
-
- $rowBeingDestructed = new Piwik_DataTable_Row();
- $rowBeingDestructed->setSubtable($mockedDataTable);
-
- destroy($rowBeingDestructed);
- }
-
- protected function _getDataTable1ForTest()
+ $dataTableBeingSerialized = new Piwik_DataTable();
+ $dataTableBeingSerialized->addRow($mockedDataTableRow);
+
+ $dataTableBeingSerialized->getSerialized();
+ }
+
+ /**
+ * @group Core
+ * @group DataTable
+ */
+ public function testSubDataTableIsDestructed()
+ {
+ $mockedDataTable = $this->getMock('Piwik_DataTable', array('__destruct'));
+ $mockedDataTable->expects($this->once())->method('__destruct');
+
+ $rowBeingDestructed = new Piwik_DataTable_Row();
+ $rowBeingDestructed->setSubtable($mockedDataTable);
+
+ destroy($rowBeingDestructed);
+ }
+
+ protected function _getDataTable1ForTest()
{
$rows = $this->_getRowsDataTable1ForTest();
$table = new Piwik_DataTable;
- $table->addRowsFromArray( $rows );
+ $table->addRowsFromArray($rows);
return $table;
}
protected function _getDataTable2ForTest()
{
- $rows = $this->_getRowsDataTable2ForTest();
+ $rows = $this->_getRowsDataTable2ForTest();
$table = new Piwik_DataTable;
- $table->addRowsFromArray( $rows );
+ $table->addRowsFromArray($rows);
return $table;
}
-
+
protected function _getRowsDataTable1ForTest()
{
$rows = array(
- array( Piwik_DataTable_Row::COLUMNS => array('label'=>'google', 'visits' => 1)),
- array( Piwik_DataTable_Row::COLUMNS => array('label'=>'ask', 'visits' => 2)),
- array( Piwik_DataTable_Row::COLUMNS => array('label'=>'123', 'visits' => 2)),
- Piwik_DataTable::ID_SUMMARY_ROW => array( Piwik_DataTable_Row::COLUMNS => array('label'=>Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 4))
-
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'google', 'visits' => 1)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'ask', 'visits' => 2)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => '123', 'visits' => 2)),
+ Piwik_DataTable::ID_SUMMARY_ROW => array(Piwik_DataTable_Row::COLUMNS => array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW, 'visits' => 4))
+
);
return $rows;
}
@@ -815,9 +812,9 @@ class DataTableTest extends PHPUnit_Framework_TestCase
protected function _getRowsDataTable2ForTest()
{
$rows = array(
- array( Piwik_DataTable_Row::COLUMNS => array('label'=>'test', 'visits' => 1)),
- array( Piwik_DataTable_Row::COLUMNS => array('label'=>' google ', 'visits' => 3)),
- array( Piwik_DataTable_Row::COLUMNS => array('label'=>'123a', 'visits' => 2)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => 'test', 'visits' => 1)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => ' google ', 'visits' => 3)),
+ array(Piwik_DataTable_Row::COLUMNS => array('label' => '123a', 'visits' => 2)),
);
return $rows;
}
diff --git a/tests/PHPUnit/Core/DateTest.php b/tests/PHPUnit/Core/DateTest.php
index 860b2d3b34..45d2e1af12 100644
--- a/tests/PHPUnit/Core/DateTest.php
+++ b/tests/PHPUnit/Core/DateTest.php
@@ -9,31 +9,31 @@ class DateTest extends PHPUnit_Framework_TestCase
{
/**
* create today object check that timestamp is correct (midnight)
- *
+ *
* @group Core
* @group Date
*/
public function testToday()
{
$date = Piwik_Date::today();
- $this->assertEquals( strtotime(date("Y-m-d "). " 00:00:00"), $date->getTimestamp());
-
+ $this->assertEquals(strtotime(date("Y-m-d ") . " 00:00:00"), $date->getTimestamp());
+
// test getDatetime()
- $this->assertEquals( $date->getDatetime(), $date->getDateStartUTC());
+ $this->assertEquals($date->getDatetime(), $date->getDateStartUTC());
$date = $date->setTime('12:00:00');
- $this->assertEquals( date('Y-m-d') . ' 12:00:00', $date->getDatetime());
+ $this->assertEquals(date('Y-m-d') . ' 12:00:00', $date->getDatetime());
}
-
+
/**
* create today object check that timestamp is correct (midnight)
- *
+ *
* @group Core
* @group Date
*/
public function testYesterday()
{
$date = Piwik_Date::yesterday();
- $this->assertEquals( strtotime(date("Y-m-d",strtotime('-1day')). " 00:00:00"), $date->getTimestamp());
+ $this->assertEquals(strtotime(date("Y-m-d", strtotime('-1day')) . " 00:00:00"), $date->getTimestamp());
}
/**
@@ -65,14 +65,13 @@ class DateTest extends PHPUnit_Framework_TestCase
$date = Piwik_Date::factory('now', 'Africa/Brazzaville');
$dateExpected = Piwik_Date::factory('now')->addHour(1);
$this->assertEquals($dateExpected->getDatetime(), $date->getDatetime());
-
+
// yesterday same time in Congo is the same as today in Congo - 24 hours
$date = Piwik_Date::factory('yesterdaySameTime', 'Africa/Brazzaville');
$dateExpected = Piwik_Date::factory('now', 'Africa/Brazzaville')->subHour(24);
$this->assertEquals($dateExpected->getDatetime(), $date->getDatetime());
- if(Piwik::isTimezoneSupportEnabled())
- {
+ if (Piwik::isTimezoneSupportEnabled()) {
// convert to/from local time
$now = time();
$date = Piwik_Date::factory($now, 'America/New_York');
@@ -84,7 +83,7 @@ class DateTest extends PHPUnit_Framework_TestCase
$this->assertEquals($now, $time);
}
}
-
+
/**
* @group Core
* @group Date
@@ -92,30 +91,29 @@ class DateTest extends PHPUnit_Framework_TestCase
public function testSetTimezoneDayInUTC()
{
$date = Piwik_Date::factory('2010-01-01');
-
+
$dayStart = '2010-01-01 00:00:00';
$dayEnd = '2010-01-01 23:59:59';
$this->assertEquals($dayStart, $date->getDateStartUTC());
$this->assertEquals($dayEnd, $date->getDateEndUTC());
-
+
// try with empty timezone
$date = $date->setTimezone('');
$this->assertEquals($dayStart, $date->getDateStartUTC());
$this->assertEquals($dayEnd, $date->getDateEndUTC());
-
+
$date = $date->setTimezone('UTC');
$this->assertEquals($dayStart, $date->getDateStartUTC());
$this->assertEquals($dayEnd, $date->getDateEndUTC());
- if(Piwik::isTimezoneSupportEnabled())
- {
+ if (Piwik::isTimezoneSupportEnabled()) {
$date = $date->setTimezone('Europe/Paris');
$utcDayStart = '2009-12-31 23:00:00';
$utcDayEnd = '2010-01-01 22:59:59';
$this->assertEquals($utcDayStart, $date->getDateStartUTC());
$this->assertEquals($utcDayEnd, $date->getDateEndUTC());
}
-
+
$date = $date->setTimezone('UTC+1');
$utcDayStart = '2009-12-31 23:00:00';
$utcDayEnd = '2010-01-01 22:59:59';
@@ -128,8 +126,7 @@ class DateTest extends PHPUnit_Framework_TestCase
$this->assertEquals($utcDayStart, $date->getDateStartUTC());
$this->assertEquals($utcDayEnd, $date->getDateEndUTC());
- if(Piwik::isTimezoneSupportEnabled())
- {
+ if (Piwik::isTimezoneSupportEnabled()) {
$date = $date->setTimezone('America/Vancouver');
$utcDayStart = '2010-01-01 08:00:00';
$utcDayEnd = '2010-01-02 07:59:59';
@@ -137,7 +134,7 @@ class DateTest extends PHPUnit_Framework_TestCase
$this->assertEquals($utcDayEnd, $date->getDateEndUTC());
}
}
-
+
/**
* @group Core
* @group Date
@@ -146,14 +143,13 @@ class DateTest extends PHPUnit_Framework_TestCase
{
$date = Piwik_Date::factory('2010-01-01');
$date = $date->setTimezone('UTC-1');
-
+
$timestamp = $date->getTimestamp();
$date = $date->addHour(0)->addHour(0)->addHour(0);
$this->assertEquals($timestamp, $date->getTimestamp());
-
- if(Piwik::isTimezoneSupportEnabled())
- {
+
+ if (Piwik::isTimezoneSupportEnabled()) {
$date = Piwik_Date::factory('2010-01-01')->setTimezone('Europe/Paris');
$dateExpected = clone $date;
$date = $date->addHour(2);
@@ -161,15 +157,14 @@ class DateTest extends PHPUnit_Framework_TestCase
$this->assertEquals($dateExpected->getTimestamp(), $date->getTimestamp());
}
}
-
+
/**
* @group Core
* @group Date
*/
public function testGetDateStartUTCEndDuringDstTimezone()
{
- if(Piwik::isTimezoneSupportEnabled())
- {
+ if (Piwik::isTimezoneSupportEnabled()) {
$date = Piwik_Date::factory('2010-03-28');
$date = $date->setTimezone('Europe/Paris');
@@ -180,7 +175,7 @@ class DateTest extends PHPUnit_Framework_TestCase
$this->assertEquals($utcDayEnd, $date->getDateEndUTC());
}
}
-
+
/**
* @group Core
* @group Date
@@ -194,12 +189,12 @@ class DateTest extends PHPUnit_Framework_TestCase
$this->assertEquals($dayExpected, $date->getDatetime());
$date = $date->subHour(0.3);
$this->assertEquals($dayStart, $date->getDatetime());
-
+
// add partial hours
$dayExpected = '2010-03-28 05:45:00';
$date = Piwik_Date::factory($dayStart)->addHour(5.75);
$this->assertEquals($dayExpected, $date->getDatetime());
-
+
// remove partial hours
$dayExpected = '2010-03-27 18:15:00';
$date = Piwik_Date::factory($dayStart)->subHour(5.75);
diff --git a/tests/PHPUnit/Core/HttpTest.php b/tests/PHPUnit/Core/HttpTest.php
index de285b2278..3d3ab62b8c 100644
--- a/tests/PHPUnit/Core/HttpTest.php
+++ b/tests/PHPUnit/Core/HttpTest.php
@@ -28,7 +28,7 @@ class HttpTest extends PHPUnit_Framework_TestCase
{
$this->assertNotNull(Piwik_Http::getTransportMethod());
$version = Piwik_Http::sendHttpRequestBy($method, 'http://api.piwik.org/1.0/getLatestVersion/', 5);
- $this->assertTrue( (boolean)preg_match('/^([0-9.]+)$/', $version) );
+ $this->assertTrue((boolean)preg_match('/^([0-9.]+)$/', $version));
}
/**
@@ -40,7 +40,7 @@ class HttpTest extends PHPUnit_Framework_TestCase
$destinationPath = PIWIK_USER_PATH . '/tmp/latest/LATEST';
Piwik_Http::fetchRemoteFile('http://api.piwik.org/1.0/getLatestVersion/', $destinationPath, 3);
$this->assertFileExists($destinationPath);
- $this->assertGreaterThan( 0, filesize($destinationPath) );
+ $this->assertGreaterThan(0, filesize($destinationPath));
}
/**
@@ -52,71 +52,69 @@ class HttpTest extends PHPUnit_Framework_TestCase
$destinationPath = PIWIK_USER_PATH . '/tmp/latest/latest.zip';
Piwik_Http::fetchRemoteFile('http://builds.piwik.org/latest.zip', $destinationPath, 3);
$this->assertFileExists($destinationPath);
- $this->assertGreaterThan( 0, filesize($destinationPath) );
+ $this->assertGreaterThan(0, filesize($destinationPath));
}
-
+
/**
* @group Core
* @group Http
* @dataProvider getMethodsToTest
*/
- public function testCustomByteRange( $method )
+ public function testCustomByteRange($method)
{
$result = Piwik_Http::sendHttpRequestBy(
- $method,
- 'http://builds.piwik.org/latest.zip',
- 5,
- $userAgent = null,
- $destinationPath = null,
- $file = null,
- $followDepth = 0,
- $acceptLanguage = false,
- $acceptInvalidSslCertificate = false,
- $byteRange = array(10, 20),
- $getExtendedInfo = true
- );
-
- if ($method != 'fopen')
- {
- $this->assertEquals(206, $result['status']);
- $this->assertTrue(isset($result['headers']['Content-Range']));
- $this->assertEquals('bytes 10-20/', substr($result['headers']['Content-Range'], 0, 12));
- $this->assertEquals('application/zip', $result['headers']['Content-Type']);
+ $method,
+ 'http://builds.piwik.org/latest.zip',
+ 5,
+ $userAgent = null,
+ $destinationPath = null,
+ $file = null,
+ $followDepth = 0,
+ $acceptLanguage = false,
+ $acceptInvalidSslCertificate = false,
+ $byteRange = array(10, 20),
+ $getExtendedInfo = true
+ );
+
+ if ($method != 'fopen') {
+ $this->assertEquals(206, $result['status']);
+ $this->assertTrue(isset($result['headers']['Content-Range']));
+ $this->assertEquals('bytes 10-20/', substr($result['headers']['Content-Range'], 0, 12));
+ $this->assertEquals('application/zip', $result['headers']['Content-Type']);
}
}
-
+
/**
* @group Core
* @group Http
* @dataProvider getMethodsToTest
*/
- public function testHEADOperation( $method )
+ public function testHEADOperation($method)
{
- if ($method == 'fopen')
- {
- return; // not supported w/ this method
- }
-
+ if ($method == 'fopen') {
+ return; // not supported w/ this method
+ }
+
$result = Piwik_Http::sendHttpRequestBy(
- $method,
- 'http://builds.piwik.org/latest.zip',
- 5,
- $userAgent = null,
- $destinationPath = null,
- $file = null,
- $followDepth = 0,
- $acceptLanguage = false,
- $acceptInvalidSslCertificate = false,
- $byteRange = false,
- $getExtendedInfo = true,
- $httpMethod = 'HEAD'
- );
-
- $this->assertEquals('', $result['data']);
- $this->assertEquals(200, $result['status']);
+ $method,
+ 'http://builds.piwik.org/latest.zip',
+ 5,
+ $userAgent = null,
+ $destinationPath = null,
+ $file = null,
+ $followDepth = 0,
+ $acceptLanguage = false,
+ $acceptInvalidSslCertificate = false,
+ $byteRange = false,
+ $getExtendedInfo = true,
+ $httpMethod = 'HEAD'
+ );
+
+ $this->assertEquals('', $result['data']);
+ $this->assertEquals(200, $result['status']);
- $this->assertTrue(isset($result['headers']['Content-Length']), "Content-Length header not set!");
- $this->assertTrue(is_numeric($result['headers']['Content-Length']), "Content-Length header not numeric!");
- $this->assertEquals('application/zip', $result['headers']['Content-Type']);
+ $this->assertTrue(isset($result['headers']['Content-Length']), "Content-Length header not set!");
+ $this->assertTrue(is_numeric($result['headers']['Content-Length']), "Content-Length header not numeric!");
+ $this->assertEquals('application/zip', $result['headers']['Content-Type']);
}
}
diff --git a/tests/PHPUnit/Core/IPTest.php b/tests/PHPUnit/Core/IPTest.php
index 8f477743b1..d319c2924e 100644
--- a/tests/PHPUnit/Core/IPTest.php
+++ b/tests/PHPUnit/Core/IPTest.php
@@ -10,7 +10,8 @@ class IPTest extends PHPUnit_Framework_TestCase
/**
* Dataprovider for testSanitizeIp
*/
- public function getIPData() {
+ public function getIPData()
+ {
return array( // input, output
// single IPv4 address
array('127.0.0.1', '127.0.0.1'),
@@ -48,7 +49,7 @@ class IPTest extends PHPUnit_Framework_TestCase
array('example.com:80', 'example.com'),
);
}
-
+
/**
* @dataProvider getIPData
* @group Core
@@ -89,7 +90,7 @@ class IPTest extends PHPUnit_Framework_TestCase
array('2001:5c0:1000:b::90f8/64', '2001:5c0:1000:b::90f8/64'),
);
}
-
+
/**
* @dataProvider getIPRangeData
* @group Core
@@ -165,7 +166,7 @@ class IPTest extends PHPUnit_Framework_TestCase
*/
public function testP2NInvalidInput($P)
{
- $this->assertEquals( "\x00\x00\x00\x00", Piwik_IP::P2N($P) );
+ $this->assertEquals("\x00\x00\x00\x00", Piwik_IP::P2N($P));
}
/**
@@ -369,70 +370,70 @@ class IPTest extends PHPUnit_Framework_TestCase
array('0', false),
// single IPv4
- array('127.0.0.1', array( "\x7f\x00\x00\x01", "\x7f\x00\x00\x01" )),
+ array('127.0.0.1', array("\x7f\x00\x00\x01", "\x7f\x00\x00\x01")),
// IPv4 with wildcards
- array('192.168.1.*', array( "\xc0\xa8\x01\x00", "\xc0\xa8\x01\xff" )),
- array('192.168.*.*', array( "\xc0\xa8\x00\x00", "\xc0\xa8\xff\xff" )),
- array('192.*.*.*', array( "\xc0\x00\x00\x00", "\xc0\xff\xff\xff" )),
- array('*.*.*.*', array( "\x00\x00\x00\x00", "\xff\xff\xff\xff" )),
+ array('192.168.1.*', array("\xc0\xa8\x01\x00", "\xc0\xa8\x01\xff")),
+ array('192.168.*.*', array("\xc0\xa8\x00\x00", "\xc0\xa8\xff\xff")),
+ array('192.*.*.*', array("\xc0\x00\x00\x00", "\xc0\xff\xff\xff")),
+ array('*.*.*.*', array("\x00\x00\x00\x00", "\xff\xff\xff\xff")),
// single IPv4 in expected CIDR notation
- array('192.168.1.1/24', array( "\xc0\xa8\x01\x00", "\xc0\xa8\x01\xff" )),
-
- array('192.168.1.127/32', array( "\xc0\xa8\x01\x7f", "\xc0\xa8\x01\x7f" )),
- array('192.168.1.127/31', array( "\xc0\xa8\x01\x7e", "\xc0\xa8\x01\x7f" )),
- array('192.168.1.127/30', array( "\xc0\xa8\x01\x7c", "\xc0\xa8\x01\x7f" )),
- array('192.168.1.127/29', array( "\xc0\xa8\x01\x78", "\xc0\xa8\x01\x7f" )),
- array('192.168.1.127/28', array( "\xc0\xa8\x01\x70", "\xc0\xa8\x01\x7f" )),
- array('192.168.1.127/27', array( "\xc0\xa8\x01\x60", "\xc0\xa8\x01\x7f" )),
- array('192.168.1.127/26', array( "\xc0\xa8\x01\x40", "\xc0\xa8\x01\x7f" )),
- array('192.168.1.127/25', array( "\xc0\xa8\x01\x00", "\xc0\xa8\x01\x7f" )),
-
- array('192.168.1.255/32', array( "\xc0\xa8\x01\xff", "\xc0\xa8\x01\xff" )),
- array('192.168.1.255/31', array( "\xc0\xa8\x01\xfe", "\xc0\xa8\x01\xff" )),
- array('192.168.1.255/30', array( "\xc0\xa8\x01\xfc", "\xc0\xa8\x01\xff" )),
- array('192.168.1.255/29', array( "\xc0\xa8\x01\xf8", "\xc0\xa8\x01\xff" )),
- array('192.168.1.255/28', array( "\xc0\xa8\x01\xf0", "\xc0\xa8\x01\xff" )),
- array('192.168.1.255/27', array( "\xc0\xa8\x01\xe0", "\xc0\xa8\x01\xff" )),
- array('192.168.1.255/26', array( "\xc0\xa8\x01\xc0", "\xc0\xa8\x01\xff" )),
- array('192.168.1.255/25', array( "\xc0\xa8\x01\x80", "\xc0\xa8\x01\xff" )),
-
- array('192.168.255.255/24', array( "\xc0\xa8\xff\x00", "\xc0\xa8\xff\xff" )),
- array('192.168.255.255/23', array( "\xc0\xa8\xfe\x00", "\xc0\xa8\xff\xff" )),
- array('192.168.255.255/22', array( "\xc0\xa8\xfc\x00", "\xc0\xa8\xff\xff" )),
- array('192.168.255.255/21', array( "\xc0\xa8\xf8\x00", "\xc0\xa8\xff\xff" )),
- array('192.168.255.255/20', array( "\xc0\xa8\xf0\x00", "\xc0\xa8\xff\xff" )),
- array('192.168.255.255/19', array( "\xc0\xa8\xe0\x00", "\xc0\xa8\xff\xff" )),
- array('192.168.255.255/18', array( "\xc0\xa8\xc0\x00", "\xc0\xa8\xff\xff" )),
- array('192.168.255.255/17', array( "\xc0\xa8\x80\x00", "\xc0\xa8\xff\xff" )),
+ array('192.168.1.1/24', array("\xc0\xa8\x01\x00", "\xc0\xa8\x01\xff")),
+
+ array('192.168.1.127/32', array("\xc0\xa8\x01\x7f", "\xc0\xa8\x01\x7f")),
+ array('192.168.1.127/31', array("\xc0\xa8\x01\x7e", "\xc0\xa8\x01\x7f")),
+ array('192.168.1.127/30', array("\xc0\xa8\x01\x7c", "\xc0\xa8\x01\x7f")),
+ array('192.168.1.127/29', array("\xc0\xa8\x01\x78", "\xc0\xa8\x01\x7f")),
+ array('192.168.1.127/28', array("\xc0\xa8\x01\x70", "\xc0\xa8\x01\x7f")),
+ array('192.168.1.127/27', array("\xc0\xa8\x01\x60", "\xc0\xa8\x01\x7f")),
+ array('192.168.1.127/26', array("\xc0\xa8\x01\x40", "\xc0\xa8\x01\x7f")),
+ array('192.168.1.127/25', array("\xc0\xa8\x01\x00", "\xc0\xa8\x01\x7f")),
+
+ array('192.168.1.255/32', array("\xc0\xa8\x01\xff", "\xc0\xa8\x01\xff")),
+ array('192.168.1.255/31', array("\xc0\xa8\x01\xfe", "\xc0\xa8\x01\xff")),
+ array('192.168.1.255/30', array("\xc0\xa8\x01\xfc", "\xc0\xa8\x01\xff")),
+ array('192.168.1.255/29', array("\xc0\xa8\x01\xf8", "\xc0\xa8\x01\xff")),
+ array('192.168.1.255/28', array("\xc0\xa8\x01\xf0", "\xc0\xa8\x01\xff")),
+ array('192.168.1.255/27', array("\xc0\xa8\x01\xe0", "\xc0\xa8\x01\xff")),
+ array('192.168.1.255/26', array("\xc0\xa8\x01\xc0", "\xc0\xa8\x01\xff")),
+ array('192.168.1.255/25', array("\xc0\xa8\x01\x80", "\xc0\xa8\x01\xff")),
+
+ array('192.168.255.255/24', array("\xc0\xa8\xff\x00", "\xc0\xa8\xff\xff")),
+ array('192.168.255.255/23', array("\xc0\xa8\xfe\x00", "\xc0\xa8\xff\xff")),
+ array('192.168.255.255/22', array("\xc0\xa8\xfc\x00", "\xc0\xa8\xff\xff")),
+ array('192.168.255.255/21', array("\xc0\xa8\xf8\x00", "\xc0\xa8\xff\xff")),
+ array('192.168.255.255/20', array("\xc0\xa8\xf0\x00", "\xc0\xa8\xff\xff")),
+ array('192.168.255.255/19', array("\xc0\xa8\xe0\x00", "\xc0\xa8\xff\xff")),
+ array('192.168.255.255/18', array("\xc0\xa8\xc0\x00", "\xc0\xa8\xff\xff")),
+ array('192.168.255.255/17', array("\xc0\xa8\x80\x00", "\xc0\xa8\xff\xff")),
// single IPv6
- array('::1', array( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" )),
+ array('::1', array("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")),
// single IPv6 in expected CIDR notation
- array('::1/128', array( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" )),
- array('::1/127', array( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" )),
- array('::fffe:7f00:1/120', array( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x7f\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x7f\x00\x00\xff" )),
- array('::ffff:127.0.0.1/120', array( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x7f\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x7f\x00\x00\xff" )),
-
- array('2001:ca11:911::b0b:15:dead/128', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xad", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xad" )),
- array('2001:ca11:911::b0b:15:dead/127', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xac", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xad" )),
- array('2001:ca11:911::b0b:15:dead/126', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xac", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xaf" )),
- array('2001:ca11:911::b0b:15:dead/125', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xa8", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xaf" )),
- array('2001:ca11:911::b0b:15:dead/124', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xa0", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xaf" )),
- array('2001:ca11:911::b0b:15:dead/123', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xa0", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xbf" )),
- array('2001:ca11:911::b0b:15:dead/122', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x80", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xbf" )),
- array('2001:ca11:911::b0b:15:dead/121', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x80", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xff" )),
- array('2001:ca11:911::b0b:15:dead/120', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xff" )),
- array('2001:ca11:911::b0b:15:dead/119', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff" )),
- array('2001:ca11:911::b0b:15:dead/118', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdc\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff" )),
- array('2001:ca11:911::b0b:15:dead/117', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xd8\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff" )),
- array('2001:ca11:911::b0b:15:dead/116', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xd0\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff" )),
- array('2001:ca11:911::b0b:15:dead/115', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xc0\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff" )),
- array('2001:ca11:911::b0b:15:dead/114', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xc0\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xff\xff" )),
- array('2001:ca11:911::b0b:15:dead/113', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\x80\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xff\xff" )),
- array('2001:ca11:911::b0b:15:dead/112', array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\x00\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xff\xff" )),
+ array('::1/128', array("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")),
+ array('::1/127', array("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01")),
+ array('::fffe:7f00:1/120', array("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x7f\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x7f\x00\x00\xff")),
+ array('::ffff:127.0.0.1/120', array("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x7f\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x7f\x00\x00\xff")),
+
+ array('2001:ca11:911::b0b:15:dead/128', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xad", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xad")),
+ array('2001:ca11:911::b0b:15:dead/127', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xac", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xad")),
+ array('2001:ca11:911::b0b:15:dead/126', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xac", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xaf")),
+ array('2001:ca11:911::b0b:15:dead/125', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xa8", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xaf")),
+ array('2001:ca11:911::b0b:15:dead/124', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xa0", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xaf")),
+ array('2001:ca11:911::b0b:15:dead/123', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xa0", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xbf")),
+ array('2001:ca11:911::b0b:15:dead/122', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x80", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xbf")),
+ array('2001:ca11:911::b0b:15:dead/121', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x80", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xff")),
+ array('2001:ca11:911::b0b:15:dead/120', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xff")),
+ array('2001:ca11:911::b0b:15:dead/119', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff")),
+ array('2001:ca11:911::b0b:15:dead/118', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdc\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff")),
+ array('2001:ca11:911::b0b:15:dead/117', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xd8\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff")),
+ array('2001:ca11:911::b0b:15:dead/116', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xd0\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff")),
+ array('2001:ca11:911::b0b:15:dead/115', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xc0\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff")),
+ array('2001:ca11:911::b0b:15:dead/114', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xc0\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xff\xff")),
+ array('2001:ca11:911::b0b:15:dead/113', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\x80\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xff\xff")),
+ array('2001:ca11:911::b0b:15:dead/112', array("\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\x00\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xff\xff")),
);
}
@@ -453,33 +454,33 @@ class IPTest extends PHPUnit_Framework_TestCase
{
return array(
array('192.168.1.10', array(
- '192.168.1.9' => false,
- '192.168.1.10' => true,
- '192.168.1.11' => false,
+ '192.168.1.9' => false,
+ '192.168.1.10' => true,
+ '192.168.1.11' => false,
// IPv6 addresses (including IPv4 mapped) have to be compared against IPv6 address ranges
'::ffff:192.168.1.10' => false,
)),
array('::ffff:192.168.1.10', array(
- '::ffff:192.168.1.9' => false,
- '::ffff:192.168.1.10' => true,
- '::ffff:c0a8:010a' => true,
+ '::ffff:192.168.1.9' => false,
+ '::ffff:192.168.1.10' => true,
+ '::ffff:c0a8:010a' => true,
'0000:0000:0000:0000:0000:ffff:c0a8:010a' => true,
- '::ffff:192.168.1.11' => false,
+ '::ffff:192.168.1.11' => false,
// conversely, IPv4 addresses have to be compared against IPv4 address ranges
- '192.168.1.10' => false,
+ '192.168.1.10' => false,
)),
array('192.168.1.10/32', array(
- '192.168.1.9' => false,
+ '192.168.1.9' => false,
'192.168.1.10' => true,
'192.168.1.11' => false,
)),
array('192.168.1.10/31', array(
- '192.168.1.9' => false,
+ '192.168.1.9' => false,
'192.168.1.10' => true,
'192.168.1.11' => true,
'192.168.1.12' => false,
@@ -489,49 +490,49 @@ class IPTest extends PHPUnit_Framework_TestCase
'192.168.1.127' => false,
'192.168.1.128' => true,
'192.168.1.255' => true,
- '192.168.2.0' => false,
+ '192.168.2.0' => false,
)),
array('192.168.1.10/24', array(
'192.168.0.255' => false,
- '192.168.1.0' => true,
- '192.168.1.1' => true,
- '192.168.1.2' => true,
- '192.168.1.3' => true,
- '192.168.1.4' => true,
- '192.168.1.7' => true,
- '192.168.1.8' => true,
- '192.168.1.15' => true,
- '192.168.1.16' => true,
- '192.168.1.31' => true,
- '192.168.1.32' => true,
- '192.168.1.63' => true,
- '192.168.1.64' => true,
+ '192.168.1.0' => true,
+ '192.168.1.1' => true,
+ '192.168.1.2' => true,
+ '192.168.1.3' => true,
+ '192.168.1.4' => true,
+ '192.168.1.7' => true,
+ '192.168.1.8' => true,
+ '192.168.1.15' => true,
+ '192.168.1.16' => true,
+ '192.168.1.31' => true,
+ '192.168.1.32' => true,
+ '192.168.1.63' => true,
+ '192.168.1.64' => true,
'192.168.1.127' => true,
'192.168.1.128' => true,
'192.168.1.255' => true,
- '192.168.2.0' => false,
+ '192.168.2.0' => false,
)),
array('192.168.1.*', array(
'192.168.0.255' => false,
- '192.168.1.0' => true,
- '192.168.1.1' => true,
- '192.168.1.2' => true,
- '192.168.1.3' => true,
- '192.168.1.4' => true,
- '192.168.1.7' => true,
- '192.168.1.8' => true,
- '192.168.1.15' => true,
- '192.168.1.16' => true,
- '192.168.1.31' => true,
- '192.168.1.32' => true,
- '192.168.1.63' => true,
- '192.168.1.64' => true,
+ '192.168.1.0' => true,
+ '192.168.1.1' => true,
+ '192.168.1.2' => true,
+ '192.168.1.3' => true,
+ '192.168.1.4' => true,
+ '192.168.1.7' => true,
+ '192.168.1.8' => true,
+ '192.168.1.15' => true,
+ '192.168.1.16' => true,
+ '192.168.1.31' => true,
+ '192.168.1.32' => true,
+ '192.168.1.63' => true,
+ '192.168.1.64' => true,
'192.168.1.127' => true,
'192.168.1.128' => true,
'192.168.1.255' => true,
- '192.168.2.0' => false,
+ '192.168.2.0' => false,
)),
);
}
@@ -543,16 +544,15 @@ class IPTest extends PHPUnit_Framework_TestCase
*/
public function testIsIpInRange($range, $test)
{
- foreach($test as $ip => $expected)
- {
+ foreach ($test as $ip => $expected) {
// range as a string
- $this->assertEquals( $expected, Piwik_IP::isIpInRange(Piwik_IP::P2N($ip), array($range)), "$ip in $range" );
+ $this->assertEquals($expected, Piwik_IP::isIpInRange(Piwik_IP::P2N($ip), array($range)), "$ip in $range");
// range as an array(low, high)
$aRange = Piwik_IP::getIpsForRange($range);
$aRange[0] = Piwik_IP::N2P($aRange[0]);
$aRange[1] = Piwik_IP::N2P($aRange[1]);
- $this->assertEquals( $expected, Piwik_IP::isIpInRange(Piwik_IP::P2N($ip), array($aRange)), "$ip in $range" );
+ $this->assertEquals($expected, Piwik_IP::isIpInRange(Piwik_IP::P2N($ip), array($aRange)), "$ip in $range");
}
}
@@ -686,13 +686,12 @@ class IPTest extends PHPUnit_Framework_TestCase
*/
public function testGetHostByAddr()
{
- $hosts = array( 'localhost', 'localhost.localdomain', strtolower(@php_uname('n')), '127.0.0.1' );
- $this->assertTrue( in_array(strtolower(Piwik_IP::getHostByAddr('127.0.0.1')), $hosts), '127.0.0.1 -> localhost' );
+ $hosts = array('localhost', 'localhost.localdomain', strtolower(@php_uname('n')), '127.0.0.1');
+ $this->assertTrue(in_array(strtolower(Piwik_IP::getHostByAddr('127.0.0.1')), $hosts), '127.0.0.1 -> localhost');
- if (!Piwik_Common::isWindows() || PHP_VERSION >= '5.3')
- {
- $hosts = array( 'ip6-localhost', 'localhost', 'localhost.localdomain', strtolower(@php_uname('n')), '::1' );
- $this->assertTrue( in_array(strtolower(Piwik_IP::getHostByAddr('::1')), $hosts), '::1 -> ip6-localhost' );
+ if (!Piwik_Common::isWindows() || PHP_VERSION >= '5.3') {
+ $hosts = array('ip6-localhost', 'localhost', 'localhost.localdomain', strtolower(@php_uname('n')), '::1');
+ $this->assertTrue(in_array(strtolower(Piwik_IP::getHostByAddr('::1')), $hosts), '::1 -> ip6-localhost');
}
}
@@ -702,21 +701,21 @@ class IPTest extends PHPUnit_Framework_TestCase
public function getInetNtopData()
{
return array(
- array('127.0.0.1', '7f000001'),
- array('192.232.131.222', 'c0e883de'),
- array('255.0.0.0', 'ff000000'),
- array('255.255.255.255', 'ffffffff'),
- array('::1', '00000000000000000000000000000001'),
- array('::101', '00000000000000000000000000000101'),
- array('::0.1.1.1', '00000000000000000000000000010101'),
- array('2001:260:0:10::1', '20010260000000100000000000000001'),
- array('2001:0:0:260::1', '20010000000002600000000000000001'),
- array('2001::260:0:0:10:1', '20010000000002600000000000100001'),
- array('2001:5c0:1000:b::90f8', '200105c01000000b00000000000090f8'),
- array('fe80::200:4cff:fe43:172f', 'fe8000000000000002004cfffe43172f'),
- array('::ffff:127.0.0.1', '00000000000000000000ffff7f000001'),
- array('::127.0.0.1', '0000000000000000000000007f000001'),
- array('::fff0:7f00:1', '00000000000000000000fff07f000001'),
+ array('127.0.0.1', '7f000001'),
+ array('192.232.131.222', 'c0e883de'),
+ array('255.0.0.0', 'ff000000'),
+ array('255.255.255.255', 'ffffffff'),
+ array('::1', '00000000000000000000000000000001'),
+ array('::101', '00000000000000000000000000000101'),
+ array('::0.1.1.1', '00000000000000000000000000010101'),
+ array('2001:260:0:10::1', '20010260000000100000000000000001'),
+ array('2001:0:0:260::1', '20010000000002600000000000000001'),
+ array('2001::260:0:0:10:1', '20010000000002600000000000100001'),
+ array('2001:5c0:1000:b::90f8', '200105c01000000b00000000000090f8'),
+ array('fe80::200:4cff:fe43:172f', 'fe8000000000000002004cfffe43172f'),
+ array('::ffff:127.0.0.1', '00000000000000000000ffff7f000001'),
+ array('::127.0.0.1', '0000000000000000000000007f000001'),
+ array('::fff0:7f00:1', '00000000000000000000fff07f000001'),
);
}
@@ -728,7 +727,7 @@ class IPTest extends PHPUnit_Framework_TestCase
public function testPhpCompatInetNtop($k, $v)
{
$this->assertEquals($k, php_compat_inet_ntop(pack('H*', $v)));
- if(!Piwik_Common::isWindows()) {
+ if (!Piwik_Common::isWindows()) {
$this->assertEquals($k, @inet_ntop(pack('H*', $v)));
}
}
@@ -740,59 +739,59 @@ class IPTest extends PHPUnit_Framework_TestCase
public function getInetPtonTestData()
{
return array(
- array('127.0.0.1', '7f000001'),
- array('192.232.131.222', 'c0e883de'),
- array('255.0.0.0', 'ff000000'),
- array('255.255.255.255', 'ffffffff'),
- array('::', '00000000000000000000000000000000'),
- array('::0', '00000000000000000000000000000000'),
- array('0::', '00000000000000000000000000000000'),
- array('0::0', '00000000000000000000000000000000'),
- array('::1', '00000000000000000000000000000001'),
- array('2001:260:0:10::1', '20010260000000100000000000000001'),
- array('2001:5c0:1000:b::90f8', '200105c01000000b00000000000090f8'),
- array('fe80::200:4cff:fe43:172f', 'fe8000000000000002004cfffe43172f'),
- array('::ffff:127.0.0.1', '00000000000000000000ffff7f000001'),
- array('::127.0.0.1', '0000000000000000000000007f000001'),
+ array('127.0.0.1', '7f000001'),
+ array('192.232.131.222', 'c0e883de'),
+ array('255.0.0.0', 'ff000000'),
+ array('255.255.255.255', 'ffffffff'),
+ array('::', '00000000000000000000000000000000'),
+ array('::0', '00000000000000000000000000000000'),
+ array('0::', '00000000000000000000000000000000'),
+ array('0::0', '00000000000000000000000000000000'),
+ array('::1', '00000000000000000000000000000001'),
+ array('2001:260:0:10::1', '20010260000000100000000000000001'),
+ array('2001:5c0:1000:b::90f8', '200105c01000000b00000000000090f8'),
+ array('fe80::200:4cff:fe43:172f', 'fe8000000000000002004cfffe43172f'),
+ array('::ffff:127.0.0.1', '00000000000000000000ffff7f000001'),
+ array('::127.0.0.1', '0000000000000000000000007f000001'),
// relaxed rules
- array('00000::', '00000000000000000000000000000000'),
- array('1:2:3:4:5:ffff:127.0.0.1', '00010002000300040005ffff7f000001'),
+ array('00000::', '00000000000000000000000000000000'),
+ array('1:2:3:4:5:ffff:127.0.0.1', '00010002000300040005ffff7f000001'),
// invalid input
- array(null, false),
- array(false, false),
- array(true, false),
- array('', false),
- array('0', false),
- array('07.07.07.07', false),
- array('1.', false),
- array('.1', false),
- array('1.1', false),
- array('.1.1.', false),
- array('1.1.1.', false),
- array('.1.1.1', false),
- array('1.2.3.4.', false),
- array('.1.2.3.4', false),
- array('1.2.3.256', false),
- array('a.b.c.d', false),
- array('::1::', false),
- array('1:2:3:4:::5:6', false),
- array('1:2:3:4:5:6:', false),
- array(':1:2:3:4:5:6', false),
- array('1:2:3:4:5:6:7:', false),
- array(':1:2:3:4:5:6:7', false),
- array('::11111:0', false),
- array('::g', false),
- array('::ffff:127.00.0.1', false),
- array('::ffff:127.0.0.01', false),
- array('::ffff:256.0.0.1', false),
- array('::ffff:1.256.0.1', false),
- array('::ffff:65536.0.0.1', false),
- array('::ffff:256.65536.0.1', false),
- array('::ffff:65536.65536.0.1', false),
- array('::ffff:7f01:0.1', false),
- array('ffff:127.0.0.1:ffff::', false),
+ array(null, false),
+ array(false, false),
+ array(true, false),
+ array('', false),
+ array('0', false),
+ array('07.07.07.07', false),
+ array('1.', false),
+ array('.1', false),
+ array('1.1', false),
+ array('.1.1.', false),
+ array('1.1.1.', false),
+ array('.1.1.1', false),
+ array('1.2.3.4.', false),
+ array('.1.2.3.4', false),
+ array('1.2.3.256', false),
+ array('a.b.c.d', false),
+ array('::1::', false),
+ array('1:2:3:4:::5:6', false),
+ array('1:2:3:4:5:6:', false),
+ array(':1:2:3:4:5:6', false),
+ array('1:2:3:4:5:6:7:', false),
+ array(':1:2:3:4:5:6:7', false),
+ array('::11111:0', false),
+ array('::g', false),
+ array('::ffff:127.00.0.1', false),
+ array('::ffff:127.0.0.01', false),
+ array('::ffff:256.0.0.1', false),
+ array('::ffff:1.256.0.1', false),
+ array('::ffff:65536.0.0.1', false),
+ array('::ffff:256.65536.0.1', false),
+ array('::ffff:65536.65536.0.1', false),
+ array('::ffff:7f01:0.1', false),
+ array('ffff:127.0.0.1:ffff::', false),
);
}
@@ -804,7 +803,7 @@ class IPTest extends PHPUnit_Framework_TestCase
public function testPhpCompatInetPton($k, $v)
{
$this->assertEquals($v, bin2hex(php_compat_inet_pton($k)));
- if(!Piwik_Common::isWindows()) {
+ if (!Piwik_Common::isWindows()) {
$this->assertEquals($v, bin2hex(@inet_pton($k)));
}
}
diff --git a/tests/PHPUnit/Core/JsProxyTest.php b/tests/PHPUnit/Core/JsProxyTest.php
index 8bab94f010..727b72a40c 100644
--- a/tests/PHPUnit/Core/JsProxyTest.php
+++ b/tests/PHPUnit/Core/JsProxyTest.php
@@ -27,7 +27,7 @@ class Test_Piwik_JsProxy extends PHPUnit_Framework_TestCase
function testPiwikPhp()
{
$curlHandle = curl_init();
- $url = $this->getStaticSrvUrl() . '/js/?idsite=1';
+ $url = $this->getStaticSrvUrl() . '/js/?idsite=1';
curl_setopt($curlHandle, CURLOPT_URL, $url);
curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
$fullResponse = curl_exec($curlHandle);
@@ -44,6 +44,6 @@ class Test_Piwik_JsProxy extends PHPUnit_Framework_TestCase
*/
private function getStaticSrvUrl()
{
- return Test_Piwik_BaseFixture::getRootUrl();
+ return Test_Piwik_BaseFixture::getRootUrl();
}
}
diff --git a/tests/PHPUnit/Core/NonceTest.php b/tests/PHPUnit/Core/NonceTest.php
index b734680b56..da64ee35c3 100644
--- a/tests/PHPUnit/Core/NonceTest.php
+++ b/tests/PHPUnit/Core/NonceTest.php
@@ -14,10 +14,10 @@ class NonceTest extends PHPUnit_Framework_TestCase
{
return array(
// HTTP_HOST => expected
- array('example.com', array( 'http://example.com', 'https://example.com' )),
- array('example.com:80', array( 'http://example.com', 'https://example.com' )),
- array('example.com:443', array( 'http://example.com', 'https://example.com' )),
- array('example.com:8080', array( 'http://example.com', 'https://example.com', 'http://example.com:8080', 'https://example.com:8080' )),
+ array('example.com', array('http://example.com', 'https://example.com')),
+ array('example.com:80', array('http://example.com', 'https://example.com')),
+ array('example.com:443', array('http://example.com', 'https://example.com')),
+ array('example.com:8080', array('http://example.com', 'https://example.com', 'http://example.com:8080', 'https://example.com:8080')),
);
}
@@ -31,6 +31,6 @@ class NonceTest extends PHPUnit_Framework_TestCase
Piwik_Config::getInstance()->General['enable_trusted_host_check'] = 0;
$_SERVER['HTTP_HOST'] = $host;
Piwik_Config::getInstance()->General['trusted_hosts'] = array('example.com');
- $this->assertEquals( $expected, Piwik_Nonce::getAcceptableOrigins(), $host );
+ $this->assertEquals($expected, Piwik_Nonce::getAcceptableOrigins(), $host);
}
}
diff --git a/tests/PHPUnit/Core/OptionTest.php b/tests/PHPUnit/Core/OptionTest.php
index 9c2ee3e9a9..63c5903636 100644
--- a/tests/PHPUnit/Core/OptionTest.php
+++ b/tests/PHPUnit/Core/OptionTest.php
@@ -19,11 +19,11 @@ class OptionTest extends DatabaseTestCase
$this->assertFalse(Piwik_Option::getInstance()->get('anonymous_defaultReport'));
// populate table, expect '1' (i.e., found)
- Piwik_Query("INSERT INTO ".Piwik_Common::prefixTable('option')." VALUES ('anonymous_defaultReport', '1', false)");
+ Piwik_Query("INSERT INTO " . Piwik_Common::prefixTable('option') . " VALUES ('anonymous_defaultReport', '1', false)");
$this->assertSame('1', Piwik_Option::getInstance()->get('anonymous_defaultReport'));
// delete row (bypassing API), expect '1' (i.e., from cache)
- Piwik_Query("DELETE FROM ".Piwik_Common::prefixTable('option')." WHERE option_name = ?", array('anonymous_defaultReport'));
+ Piwik_Query("DELETE FROM " . Piwik_Common::prefixTable('option') . " WHERE option_name = ?", array('anonymous_defaultReport'));
$this->assertSame('1', Piwik_Option::getInstance()->get('anonymous_defaultReport'));
// force cache reload, expect false (i.e., not found)
@@ -41,11 +41,11 @@ class OptionTest extends DatabaseTestCase
$this->assertFalse(Piwik_GetOption('anonymous_defaultReport'));
// populate table, expect '1' (i.e., found)
- Piwik_Query("INSERT INTO ".Piwik_Common::prefixTable('option')." VALUES ('anonymous_defaultReport', '1',true)");
+ Piwik_Query("INSERT INTO " . Piwik_Common::prefixTable('option') . " VALUES ('anonymous_defaultReport', '1',true)");
$this->assertSame('1', Piwik_GetOption('anonymous_defaultReport'));
// delete row (bypassing API), expect '1' (i.e., from cache)
- Piwik_Query("DELETE FROM ".Piwik_Common::prefixTable('option')." WHERE option_name = ?", array('anonymous_defaultReport'));
+ Piwik_Query("DELETE FROM " . Piwik_Common::prefixTable('option') . " WHERE option_name = ?", array('anonymous_defaultReport'));
$this->assertSame('1', Piwik_GetOption('anonymous_defaultReport'));
// force cache reload, expect false (i.e., not found)
@@ -60,7 +60,7 @@ class OptionTest extends DatabaseTestCase
public function testSet()
{
// empty table, expect false (i.e., not found)
- $this->assertFalse( Piwik_GetOption('anonymous_defaultReport'));
+ $this->assertFalse(Piwik_GetOption('anonymous_defaultReport'));
// populate table, expect '1'
Piwik_Option::getInstance()->set('anonymous_defaultReport', '1', true);
@@ -114,7 +114,7 @@ class OptionTest extends DatabaseTestCase
// deleted, expect false
Piwik_Option::getInstance()->delete('admin_defaultReport');
- $this->assertFalse( Piwik_Option::getInstance()->get('admin_defaultReport'));
+ $this->assertFalse(Piwik_Option::getInstance()->get('admin_defaultReport'));
}
/**
@@ -130,7 +130,7 @@ class OptionTest extends DatabaseTestCase
// insert guard - to test unescaped underscore
Piwik_SetOption('adefaultReport', '0', true);
- $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' );
+ $this->assertTrue(Piwik_GetOption('adefaultReport') === '0');
// populate table, expect '1'
Piwik_SetOption('anonymous_defaultReport', '1', true);
diff --git a/tests/PHPUnit/Core/Period/DayTest.php b/tests/PHPUnit/Core/Period/DayTest.php
index aed355c512..41117c4177 100644
--- a/tests/PHPUnit/Core/Period/DayTest.php
+++ b/tests/PHPUnit/Core/Period/DayTest.php
@@ -18,13 +18,13 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testInvalidDate()
{
try {
- $period = new Piwik_Period_Day( 'Invalid Date' );
+ $period = new Piwik_Period_Day('Invalid Date');
} catch (Exception $e) {
return;
}
$this->fail('Expected Exception not raised');
}
-
+
/**
* @group Core
* @group Period
@@ -32,9 +32,9 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
*/
public function testToString()
{
- $period = new Piwik_Period_Day( Piwik_Date::today());
+ $period = new Piwik_Period_Day(Piwik_Date::today());
$this->assertEquals(date("Y-m-d"), $period->getPrettyString());
- $this->assertEquals(date("Y-m-d"), (string) $period);
+ $this->assertEquals(date("Y-m-d"), (string)$period);
$this->assertEquals(date("Y-m-d"), $period->toString());
}
@@ -46,7 +46,7 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
*/
public function testDayIsFinishedToday()
{
- $period = new Piwik_Period_Day( Piwik_Date::today());
+ $period = new Piwik_Period_Day(Piwik_Date::today());
$this->assertEquals(date("Y-m-d"), $period->toString());
$this->assertEquals(array(), $period->getSubperiods());
$this->assertEquals(0, $period->getNumberOfSubperiods());
@@ -60,13 +60,13 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
*/
public function testDayIsFinishedYesterday()
{
-
- $period = new Piwik_Period_Day( Piwik_Date::yesterday());
- $this->assertEquals(date("Y-m-d", time()-86400), $period->toString());
+
+ $period = new Piwik_Period_Day(Piwik_Date::yesterday());
+ $this->assertEquals(date("Y-m-d", time() - 86400), $period->toString());
$this->assertEquals(array(), $period->getSubperiods());
$this->assertEquals(0, $period->getNumberOfSubperiods());
}
-
+
/**
* tomorrow is not finished
* @group Core
@@ -74,13 +74,13 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
* @group Period_Day
*/
public function testDayIsFinishedTomorrow()
- {
- $period = new Piwik_Period_Day( Piwik_Date::factory(date("Y-m-d",time()+86400)));
- $this->assertEquals(date("Y-m-d", time()+86400), $period->toString());
+ {
+ $period = new Piwik_Period_Day(Piwik_Date::factory(date("Y-m-d", time() + 86400)));
+ $this->assertEquals(date("Y-m-d", time() + 86400), $period->toString());
$this->assertEquals(array(), $period->getSubperiods());
$this->assertEquals(0, $period->getNumberOfSubperiods());
}
-
+
/**
* test day doesnt exist 31st feb
* @group Core
@@ -88,13 +88,13 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
* @group Period_Day
*/
public function testDayIsFinished31stfeb()
- {
- $period = new Piwik_Period_Day( Piwik_Date::factory("2007-02-31"));
+ {
+ $period = new Piwik_Period_Day(Piwik_Date::factory("2007-02-31"));
$this->assertEquals("2007-03-03", $period->toString());
$this->assertEquals(array(), $period->getSubperiods());
$this->assertEquals(0, $period->getNumberOfSubperiods());
}
-
+
/**
* test date that doesn't exist, should return the corresponding correct date
* @group Core
@@ -104,18 +104,18 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testDayGetDateStart1()
{
// create the period
- $period = new Piwik_Period_Day( Piwik_Date::factory("2007-02-31"));
-
+ $period = new Piwik_Period_Day(Piwik_Date::factory("2007-02-31"));
+
// start date
$startDate = $period->getDateStart();
-
+
// expected string
$this->assertEquals("2007-03-03", $startDate->toString());
-
+
// check that for a day, getDateStart = getStartEnd
$this->assertEquals($startDate, $period->getDateEnd());
}
-
+
/**
* test normal date
* @group Core
@@ -125,18 +125,18 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testDayGetDateStart2()
{
// create the period
- $period = new Piwik_Period_Day( Piwik_Date::factory("2007-01-03"));
-
+ $period = new Piwik_Period_Day(Piwik_Date::factory("2007-01-03"));
+
// start date
$startDate = $period->getDateStart();
-
+
// expected string
$this->assertEquals("2007-01-03", $startDate->toString());
-
+
// check that for a day, getDateStart = getStartEnd
- $this->assertEquals($startDate, $period->getDateEnd() );
+ $this->assertEquals($startDate, $period->getDateEnd());
}
-
+
/**
* test last day of year
* @group Core
@@ -146,18 +146,18 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testDayGetDateStart3()
{
// create the period
- $period = new Piwik_Period_Day( Piwik_Date::factory("2007-12-31"));
-
+ $period = new Piwik_Period_Day(Piwik_Date::factory("2007-12-31"));
+
// start date
$startDate = $period->getDateStart();
-
+
// expected string
$this->assertEquals("2007-12-31", $startDate->toString());
-
+
// check that for a day, getDateStart = getStartEnd
- $this->assertEquals($startDate, $period->getDateEnd() );
+ $this->assertEquals($startDate, $period->getDateEnd());
}
-
+
/**
* test date that doesn't exist, should return the corresponding correct date
* @group Core
@@ -167,15 +167,15 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testDayGetDateEnd1()
{
// create the period
- $period = new Piwik_Period_Day( Piwik_Date::factory("2007-02-31"));
-
+ $period = new Piwik_Period_Day(Piwik_Date::factory("2007-02-31"));
+
// end date
$endDate = $period->getDateEnd();
-
+
// expected string
$this->assertEquals("2007-03-03", $endDate->toString());
}
-
+
/**
* test normal date
* @group Core
@@ -185,15 +185,15 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testDayGetDateEnd2()
{
// create the period
- $period = new Piwik_Period_Day( Piwik_Date::factory("2007-04-15"));
-
+ $period = new Piwik_Period_Day(Piwik_Date::factory("2007-04-15"));
+
// end date
$endDate = $period->getDateEnd();
-
+
// expected string
$this->assertEquals("2007-04-15", $endDate->toString());
}
-
+
/**
* test last day of year
* @group Core
@@ -203,11 +203,11 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testDayGetDateEnd3()
{
// create the period
- $period = new Piwik_Period_Day( Piwik_Date::factory("2007-12-31"));
-
+ $period = new Piwik_Period_Day(Piwik_Date::factory("2007-12-31"));
+
// end date
$endDate = $period->getDateEnd();
-
+
// expected string
$this->assertEquals("2007-12-31", $endDate->toString());
}
@@ -221,7 +221,7 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testAddSubperiodFails()
{
// create the period
- $period = new Piwik_Period_Day( Piwik_Date::factory("2007-12-31"));
+ $period = new Piwik_Period_Day(Piwik_Date::factory("2007-12-31"));
try {
$period->addSubperiod('');
@@ -240,7 +240,7 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testGetLocalizedShortString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $month = new Piwik_Period_Day( Piwik_Date::factory('2024-10-09'));
+ $month = new Piwik_Period_Day(Piwik_Date::factory('2024-10-09'));
$shouldBe = 'Wed 9 Oct';
$this->assertEquals($shouldBe, $month->getLocalizedShortString());
}
@@ -253,7 +253,7 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testGetLocalizedLongString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $month = new Piwik_Period_Day( Piwik_Date::factory('2024-10-09'));
+ $month = new Piwik_Period_Day(Piwik_Date::factory('2024-10-09'));
$shouldBe = 'Wednesday 9 October 2024';
$this->assertEquals($shouldBe, $month->getLocalizedLongString());
}
@@ -266,7 +266,7 @@ class Period_DayTest extends PHPUnit_Framework_TestCase
public function testGetPrettyString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $month = new Piwik_Period_Day( Piwik_Date::factory('2024-10-09'));
+ $month = new Piwik_Period_Day(Piwik_Date::factory('2024-10-09'));
$shouldBe = '2024-10-09';
$this->assertEquals($shouldBe, $month->getPrettyString());
}
diff --git a/tests/PHPUnit/Core/Period/MonthTest.php b/tests/PHPUnit/Core/Period/MonthTest.php
index 95faa87c8e..4d74cbc434 100644
--- a/tests/PHPUnit/Core/Period/MonthTest.php
+++ b/tests/PHPUnit/Core/Period/MonthTest.php
@@ -18,8 +18,8 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
*/
public function testMonthDec()
{
- $month = new Piwik_Period_Month( Piwik_Date::factory("2006-12-31"));
- $correct=array(
+ $month = new Piwik_Period_Month(Piwik_Date::factory("2006-12-31"));
+ $correct = array(
"2006-12-01",
"2006-12-02",
"2006-12-03",
@@ -54,7 +54,7 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
$this->assertEquals($correct, $month->toString());
$this->assertEquals(31, $month->getNumberOfSubperiods());
}
-
+
/**
* testing month feb leap year
* @group Core
@@ -63,8 +63,8 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
*/
public function testMonthFebLeap()
{
- $month = new Piwik_Period_Month( Piwik_Date::factory("2024-02-11"));
- $correct=array(
+ $month = new Piwik_Period_Month(Piwik_Date::factory("2024-02-11"));
+ $correct = array(
"2024-02-01",
"2024-02-02",
"2024-02-03",
@@ -97,7 +97,7 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
$this->assertEquals($correct, $month->toString());
$this->assertEquals(29, $month->getNumberOfSubperiods());
}
-
+
/**
* testing month feb non-leap year
* @group Core
@@ -106,8 +106,8 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
*/
public function testMonthFebNonLeap()
{
- $month = new Piwik_Period_Month( Piwik_Date::factory("2023-02-11"));
- $correct=array(
+ $month = new Piwik_Period_Month(Piwik_Date::factory("2023-02-11"));
+ $correct = array(
"2023-02-01",
"2023-02-02",
"2023-02-03",
@@ -139,7 +139,7 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
$this->assertEquals($correct, $month->toString());
$this->assertEquals(28, $month->getNumberOfSubperiods());
}
-
+
/**
* testing jan
* @group Core
@@ -148,8 +148,8 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
*/
public function testMonthJan()
{
- $month = new Piwik_Period_Month( Piwik_Date::factory("2007-01-01"));
- $correct=array(
+ $month = new Piwik_Period_Month(Piwik_Date::factory("2007-01-01"));
+ $correct = array(
"2007-01-01",
"2007-01-02",
"2007-01-03",
@@ -184,7 +184,7 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
$this->assertEquals($correct, $month->toString());
$this->assertEquals(31, $month->getNumberOfSubperiods());
}
-
+
/**
* testing month containing a time change (DST)
* @group Core
@@ -193,8 +193,8 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
*/
public function testMonthDSTChangeMarch()
{
- $month = new Piwik_Period_Month( Piwik_Date::factory("2007-02-31"));
- $correct=array(
+ $month = new Piwik_Period_Month(Piwik_Date::factory("2007-02-31"));
+ $correct = array(
"2007-03-01",
"2007-03-02",
"2007-03-03",
@@ -229,7 +229,7 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
$this->assertEquals($correct, $month->toString());
$this->assertEquals(31, $month->getNumberOfSubperiods());
}
-
+
/**
* @group Core
* @group Period
@@ -237,8 +237,8 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
*/
public function testMonthDSTChangeOct()
{
- $month = new Piwik_Period_Month( Piwik_Date::factory("2017-10-31"));
- $correct=array(
+ $month = new Piwik_Period_Month(Piwik_Date::factory("2017-10-31"));
+ $correct = array(
"2017-10-01",
"2017-10-02",
"2017-10-03",
@@ -282,7 +282,7 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
public function testGetLocalizedShortString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $month = new Piwik_Period_Month( Piwik_Date::factory('2024-10-09'));
+ $month = new Piwik_Period_Month(Piwik_Date::factory('2024-10-09'));
$shouldBe = 'Oct 2024';
$this->assertEquals($shouldBe, $month->getLocalizedShortString());
}
@@ -295,7 +295,7 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
public function testGetLocalizedLongString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $month = new Piwik_Period_Month( Piwik_Date::factory('2024-10-09'));
+ $month = new Piwik_Period_Month(Piwik_Date::factory('2024-10-09'));
$shouldBe = '2024, October';
$this->assertEquals($shouldBe, $month->getLocalizedLongString());
}
@@ -308,7 +308,7 @@ class Period_MonthTest extends PHPUnit_Framework_TestCase
public function testGetPrettyString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $month = new Piwik_Period_Month( Piwik_Date::factory('2024-10-09'));
+ $month = new Piwik_Period_Month(Piwik_Date::factory('2024-10-09'));
$shouldBe = '2024-10';
$this->assertEquals($shouldBe, $month->getPrettyString());
}
diff --git a/tests/PHPUnit/Core/Period/RangeTest.php b/tests/PHPUnit/Core/Period/RangeTest.php
index 0f78925be7..ade9afc689 100644
--- a/tests/PHPUnit/Core/Period/RangeTest.php
+++ b/tests/PHPUnit/Core/Period/RangeTest.php
@@ -15,18 +15,18 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeToday()
{
- $range = new Piwik_Period_Range( 'day', 'last1' );
+ $range = new Piwik_Period_Range('day', 'last1');
$today = Piwik_Date::today();
- $correct=array(
+ $correct = array(
$today->toString(),
);
$correct = array_reverse($correct);
-
+
$this->assertEquals(1, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
/**
* @group Core
* @group Period
@@ -35,18 +35,18 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
public function testRangeTodayUtcPlus12()
{
// rather ugly test, UTC+23 doesn't exist, but it's a way to test that last1 in UTC+23 will be "our" UTC tomorrow
- $range = new Piwik_Period_Range( 'day', 'last1', 'UTC+23' );
+ $range = new Piwik_Period_Range('day', 'last1', 'UTC+23');
$today = Piwik_Date::now()->addHour(23);
-
- $correct=array(
+
+ $correct = array(
$today->toString(),
);
$correct = array_reverse($correct);
-
+
$this->assertEquals(1, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
// test range 2
/**
* @group Core
@@ -55,19 +55,19 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRange2days()
{
- $range = new Piwik_Period_Range( 'day', 'last2' );
+ $range = new Piwik_Period_Range('day', 'last2');
$today = Piwik_Date::today();
-
- $correct=array(
+
+ $correct = array(
$today->toString(),
$today->subDay(1)->toString()
);
$correct = array_reverse($correct);
-
- $this->assertEquals(2, $range->getNumberOfSubperiods());
+
+ $this->assertEquals(2, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
// test range 3
/**
* @group Core
@@ -76,20 +76,19 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRange5days()
{
- $range = new Piwik_Period_Range( 'day', 'last50' );
+ $range = new Piwik_Period_Range('day', 'last50');
$today = Piwik_Date::today();
-
+
$correct = array();
- for($i=0;$i<50;$i++)
- {
- $correct[]=$today->subDay($i)->toString();
+ for ($i = 0; $i < 50; $i++) {
+ $correct[] = $today->subDay($i)->toString();
}
$correct = array_reverse($correct);
-
+
$this->assertEquals(50, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
// test range 4
/**
* @group Core
@@ -98,20 +97,19 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangePrevious3days()
{
- $range = new Piwik_Period_Range( 'day', 'previous3' );
+ $range = new Piwik_Period_Range('day', 'previous3');
$yesterday = Piwik_Date::yesterday();
-
+
$correct = array();
- for($i=0;$i<3;$i++)
- {
- $correct[]=$yesterday->subDay($i)->toString();
+ for ($i = 0; $i < 3; $i++) {
+ $correct[] = $yesterday->subDay($i)->toString();
}
$correct = array_reverse($correct);
-
+
$this->assertEquals(3, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
// test range date1,date2
/**
* @group Core
@@ -120,15 +118,15 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeComma1()
{
-
- $range = new Piwik_Period_Range( 'day', '2008-01-01,2008-01-03' );
-
+
+ $range = new Piwik_Period_Range('day', '2008-01-01,2008-01-03');
+
$correct = array(
- '2008-01-01',
- '2008-01-02',
- '2008-01-03',
- );
-
+ '2008-01-01',
+ '2008-01-02',
+ '2008-01-03',
+ );
+
$this->assertEquals(3, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
@@ -141,29 +139,29 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeComma2()
{
-
- $range = new Piwik_Period_Range( 'day', '2007-12-22,2008-01-03' );
-
+
+ $range = new Piwik_Period_Range('day', '2007-12-22,2008-01-03');
+
$correct = array(
- '2007-12-22',
- '2007-12-23',
- '2007-12-24',
- '2007-12-25',
- '2007-12-26',
- '2007-12-27',
- '2007-12-28',
- '2007-12-29',
- '2007-12-30',
- '2007-12-31',
- '2008-01-01',
- '2008-01-02',
- '2008-01-03',
- );
-
+ '2007-12-22',
+ '2007-12-23',
+ '2007-12-24',
+ '2007-12-25',
+ '2007-12-26',
+ '2007-12-27',
+ '2007-12-28',
+ '2007-12-29',
+ '2007-12-30',
+ '2007-12-31',
+ '2008-01-01',
+ '2008-01-02',
+ '2008-01-03',
+ );
+
$this->assertEquals(13, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
// test range date1,date2
/**
* @group Core
@@ -172,45 +170,45 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeWeekcomma1()
{
- $range = new Piwik_Period_Range( 'week', '2007-12-22,2008-01-03' );
- $range2 = new Piwik_Period_Range( 'week', '2007-12-19,2008-01-03' );
-
+ $range = new Piwik_Period_Range('week', '2007-12-22,2008-01-03');
+ $range2 = new Piwik_Period_Range('week', '2007-12-19,2008-01-03');
+
$correct = array(
- array(
- '2007-12-17',
- '2007-12-18',
- '2007-12-19',
- '2007-12-20',
- '2007-12-21',
- '2007-12-22',
- '2007-12-23',
- ),
- array(
- '2007-12-24',
- '2007-12-25',
- '2007-12-26',
- '2007-12-27',
- '2007-12-28',
- '2007-12-29',
- '2007-12-30',
- ),
- array(
- '2007-12-31',
- '2008-01-01',
- '2008-01-02',
- '2008-01-03',
- '2008-01-04',
- '2008-01-05',
- '2008-01-06',
- )
+ array(
+ '2007-12-17',
+ '2007-12-18',
+ '2007-12-19',
+ '2007-12-20',
+ '2007-12-21',
+ '2007-12-22',
+ '2007-12-23',
+ ),
+ array(
+ '2007-12-24',
+ '2007-12-25',
+ '2007-12-26',
+ '2007-12-27',
+ '2007-12-28',
+ '2007-12-29',
+ '2007-12-30',
+ ),
+ array(
+ '2007-12-31',
+ '2008-01-01',
+ '2008-01-02',
+ '2008-01-03',
+ '2008-01-04',
+ '2008-01-05',
+ '2008-01-06',
+ )
);
-
+
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals(count($correct), $range2->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
$this->assertEquals($correct, $range2->toString());
}
-
+
// test range date1,date2
/**
* @group Core
@@ -219,43 +217,43 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeYearcomma1()
{
- $range = new Piwik_Period_Range( 'year', '2006-12-22,2007-01-03' );
-
+ $range = new Piwik_Period_Range('year', '2006-12-22,2007-01-03');
+
$correct = array(
- array (
- 0 => '2006-01-01',
- 1 => '2006-02-01',
- 2 => '2006-03-01',
- 3 => '2006-04-01',
- 4 => '2006-05-01',
- 5 => '2006-06-01',
- 6 => '2006-07-01',
- 7 => '2006-08-01',
- 8 => '2006-09-01',
- 9 => '2006-10-01',
+ array(
+ 0 => '2006-01-01',
+ 1 => '2006-02-01',
+ 2 => '2006-03-01',
+ 3 => '2006-04-01',
+ 4 => '2006-05-01',
+ 5 => '2006-06-01',
+ 6 => '2006-07-01',
+ 7 => '2006-08-01',
+ 8 => '2006-09-01',
+ 9 => '2006-10-01',
10 => '2006-11-01',
11 => '2006-12-01',
- ),
- 1 =>
- array (
- 0 => '2007-01-01',
- 1 => '2007-02-01',
- 2 => '2007-03-01',
- 3 => '2007-04-01',
- 4 => '2007-05-01',
- 5 => '2007-06-01',
- 6 => '2007-07-01',
- 7 => '2007-08-01',
- 8 => '2007-09-01',
- 9 => '2007-10-01',
+ ),
+ 1 =>
+ array(
+ 0 => '2007-01-01',
+ 1 => '2007-02-01',
+ 2 => '2007-03-01',
+ 3 => '2007-04-01',
+ 4 => '2007-05-01',
+ 5 => '2007-06-01',
+ 6 => '2007-07-01',
+ 7 => '2007-08-01',
+ 8 => '2007-09-01',
+ 9 => '2007-10-01',
10 => '2007-11-01',
11 => '2007-12-01',
- ),
+ ),
);
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
// test range date1,date2
/**
* @group Core
@@ -264,77 +262,77 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeMonthcomma1()
{
- $range = new Piwik_Period_Range( 'month', '2006-12-22,2007-01-03' );
-
+ $range = new Piwik_Period_Range('month', '2006-12-22,2007-01-03');
+
$correct = array(
- array(
- '2006-12-01',
- '2006-12-02',
- '2006-12-03',
- '2006-12-04',
- '2006-12-05',
- '2006-12-06',
- '2006-12-07',
- '2006-12-08',
- '2006-12-09',
- '2006-12-10',
- '2006-12-11',
- '2006-12-12',
- '2006-12-13',
- '2006-12-14',
- '2006-12-15',
- '2006-12-16',
- '2006-12-17',
- '2006-12-18',
- '2006-12-19',
- '2006-12-20',
- '2006-12-21',
- '2006-12-22',
- '2006-12-23',
- '2006-12-24',
- '2006-12-25',
- '2006-12-26',
- '2006-12-27',
- '2006-12-28',
- '2006-12-29',
- '2006-12-30',
- '2006-12-31',
- ),
- array(
- '2007-01-01',
- '2007-01-02',
- '2007-01-03',
- '2007-01-04',
- '2007-01-05',
- '2007-01-06',
- '2007-01-07',
- '2007-01-08',
- '2007-01-09',
- '2007-01-10',
- '2007-01-11',
- '2007-01-12',
- '2007-01-13',
- '2007-01-14',
- '2007-01-15',
- '2007-01-16',
- '2007-01-17',
- '2007-01-18',
- '2007-01-19',
- '2007-01-20',
- '2007-01-21',
- '2007-01-22',
- '2007-01-23',
- '2007-01-24',
- '2007-01-25',
- '2007-01-26',
- '2007-01-27',
- '2007-01-28',
- '2007-01-29',
- '2007-01-30',
- '2007-01-31',
- ),
+ array(
+ '2006-12-01',
+ '2006-12-02',
+ '2006-12-03',
+ '2006-12-04',
+ '2006-12-05',
+ '2006-12-06',
+ '2006-12-07',
+ '2006-12-08',
+ '2006-12-09',
+ '2006-12-10',
+ '2006-12-11',
+ '2006-12-12',
+ '2006-12-13',
+ '2006-12-14',
+ '2006-12-15',
+ '2006-12-16',
+ '2006-12-17',
+ '2006-12-18',
+ '2006-12-19',
+ '2006-12-20',
+ '2006-12-21',
+ '2006-12-22',
+ '2006-12-23',
+ '2006-12-24',
+ '2006-12-25',
+ '2006-12-26',
+ '2006-12-27',
+ '2006-12-28',
+ '2006-12-29',
+ '2006-12-30',
+ '2006-12-31',
+ ),
+ array(
+ '2007-01-01',
+ '2007-01-02',
+ '2007-01-03',
+ '2007-01-04',
+ '2007-01-05',
+ '2007-01-06',
+ '2007-01-07',
+ '2007-01-08',
+ '2007-01-09',
+ '2007-01-10',
+ '2007-01-11',
+ '2007-01-12',
+ '2007-01-13',
+ '2007-01-14',
+ '2007-01-15',
+ '2007-01-16',
+ '2007-01-17',
+ '2007-01-18',
+ '2007-01-19',
+ '2007-01-20',
+ '2007-01-21',
+ '2007-01-22',
+ '2007-01-23',
+ '2007-01-24',
+ '2007-01-25',
+ '2007-01-26',
+ '2007-01-27',
+ '2007-01-28',
+ '2007-01-29',
+ '2007-01-30',
+ '2007-01-31',
+ ),
);
-
+
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
@@ -347,24 +345,23 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeWeek()
{
- $range = new Piwik_Period_Range( 'week', 'last50' );
+ $range = new Piwik_Period_Range('week', 'last50');
$today = Piwik_Date::today();
-
+
$correct = array();
- for($i=0;$i<50;$i++)
- {
- $date = $today->subDay($i*7);
+ for ($i = 0; $i < 50; $i++) {
+ $date = $today->subDay($i * 7);
$week = new Piwik_Period_Week($date);
-
- $correct[]= $week->toString();
+
+ $correct[] = $week->toString();
}
$correct = array_reverse($correct);
-
-
+
+
$this->assertEquals(50, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
// test range WEEK last1
/**
* @group Core
@@ -373,12 +370,12 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeWeekLast1()
{
- $range = new Piwik_Period_Range( 'week', 'last1' );
+ $range = new Piwik_Period_Range('week', 'last1');
$currentWeek = new Piwik_Period_Week(Piwik_Date::today());
$this->assertEquals(1, $range->getNumberOfSubperiods());
$this->assertEquals(array($currentWeek->toString()), $range->toString());
}
-
+
// test range MONTH
/**
* @group Core
@@ -387,23 +384,22 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeMonth()
{
- $range = new Piwik_Period_Range( 'month', 'last20' );
+ $range = new Piwik_Period_Range('month', 'last20');
$today = Piwik_Date::today();
-
+
$correct = array();
- for($i=0;$i<20;$i++)
- {
+ for ($i = 0; $i < 20; $i++) {
$date = $today->subMonth($i);
$week = new Piwik_Period_Month($date);
-
- $correct[]= $week->toString();
+
+ $correct[] = $week->toString();
}
$correct = array_reverse($correct);
-
+
$this->assertEquals(20, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
// test range MONTH last1
/**
* @group Core
@@ -412,12 +408,12 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeMonthLast1()
{
- $range = new Piwik_Period_Range( 'month', 'last1' );
+ $range = new Piwik_Period_Range('month', 'last1');
$month = new Piwik_Period_Month(Piwik_Date::today());
$this->assertEquals(1, $range->getNumberOfSubperiods());
$this->assertEquals(array($month->toString()), $range->toString());
}
-
+
// test range PREVIOUS MONTH
/**
* @group Core
@@ -426,21 +422,20 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangePreviousmonth()
{
- $range = new Piwik_Period_Range( 'month', 'previous10' );
+ $range = new Piwik_Period_Range('month', 'previous10');
$end = Piwik_Date::today();
$end = $end->subMonth(1);
-
+
$correct = array();
- for($i=0;$i<10;$i++)
- {
+ for ($i = 0; $i < 10; $i++) {
$date = $end->subMonth($i);
$week = new Piwik_Period_Month($date);
-
- $correct[]= $week->toString();
+
+ $correct[] = $week->toString();
}
$correct = array_reverse($correct);
-
-
+
+
$this->assertEquals(10, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
@@ -453,23 +448,22 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeYear()
{
- $range = new Piwik_Period_Range( 'year', 'last10' );
+ $range = new Piwik_Period_Range('year', 'last10');
$today = Piwik_Date::today();
-
+
$correct = array();
- for($i=0;$i<10;$i++)
- {
- $date = $today->subMonth(12*$i);
+ for ($i = 0; $i < 10; $i++) {
+ $date = $today->subMonth(12 * $i);
$week = new Piwik_Period_Year($date);
-
- $correct[]= $week->toString();
+
+ $correct[] = $week->toString();
}
$correct = array_reverse($correct);
-
+
$this->assertEquals(10, $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
// test range YEAR last1
/**
* @group Core
@@ -478,12 +472,12 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testRangeYearLast1()
{
- $range = new Piwik_Period_Range( 'year', 'last1' );
+ $range = new Piwik_Period_Range('year', 'last1');
$currentYear = new Piwik_Period_Year(Piwik_Date::today());
$this->assertEquals(1, $range->getNumberOfSubperiods());
$this->assertEquals(array($currentYear->toString()), $range->toString());
}
-
+
/**
* @group Core
* @group Period
@@ -491,8 +485,8 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testCustomRangeWeekInsideEndingToday()
{
- $range = new Piwik_Period_Range( 'range', '2007-12-22,2008-01-03', 'UTC', Piwik_Date::factory('2008-01-03') );
-
+ $range = new Piwik_Period_Range('range', '2007-12-22,2008-01-03', 'UTC', Piwik_Date::factory('2008-01-03'));
+
$correct = array(
'2007-12-22',
'2007-12-23',
@@ -518,7 +512,7 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
/**
* @group Core
* @group Period
@@ -533,11 +527,10 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
Piwik_Date::factory('2008-02-14'),
Piwik_Date::factory('2009-02-14'),
);
-
- foreach($todays as $today)
- {
- $range = new Piwik_Period_Range( 'range', '2007-12-22,2008-01-03', 'UTC', $today );
-
+
+ foreach ($todays as $today) {
+ $range = new Piwik_Period_Range('range', '2007-12-22,2008-01-03', 'UTC', $today);
+
$correct = array(
'2007-12-22',
'2007-12-23',
@@ -559,7 +552,7 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
$this->assertEquals($correct, $range->toString());
}
}
-
+
/**
* @group Core
* @group Period
@@ -567,8 +560,8 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testCustomRangeOnlyDaysLessThanOneWeek()
{
- $range = new Piwik_Period_Range( 'range', '2007-12-30,2008-01-01' );
-
+ $range = new Piwik_Period_Range('range', '2007-12-30,2008-01-01');
+
$correct = array(
'2007-12-30',
'2007-12-31',
@@ -577,7 +570,7 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
/**
* @group Core
* @group Period
@@ -585,17 +578,17 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testCustomRangeOneWeekOnly()
{
- $range = new Piwik_Period_Range( 'range', '2007-12-31,2008-01-06' );
-
+ $range = new Piwik_Period_Range('range', '2007-12-31,2008-01-06');
+
$correct = array(
array(
- '2007-12-31',
- '2008-01-01',
- '2008-01-02',
- '2008-01-03',
- '2008-01-04',
- '2008-01-05',
- '2008-01-06',
+ '2007-12-31',
+ '2008-01-01',
+ '2008-01-02',
+ '2008-01-03',
+ '2008-01-04',
+ '2008-01-05',
+ '2008-01-06',
)
);
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
@@ -609,17 +602,17 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testCustomRangeStartsWithWeek()
{
- $range = new Piwik_Period_Range( 'range', '2007-12-31,2008-01-08' );
-
+ $range = new Piwik_Period_Range('range', '2007-12-31,2008-01-08');
+
$correct = array(
array(
- '2007-12-31',
- '2008-01-01',
- '2008-01-02',
- '2008-01-03',
- '2008-01-04',
- '2008-01-05',
- '2008-01-06',
+ '2007-12-31',
+ '2008-01-01',
+ '2008-01-02',
+ '2008-01-03',
+ '2008-01-04',
+ '2008-01-05',
+ '2008-01-06',
),
'2008-01-07',
'2008-01-08',
@@ -627,7 +620,7 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
/**
* @group Core
* @group Period
@@ -635,29 +628,29 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testCustomRangeEndsWithWeek()
{
- $range = new Piwik_Period_Range( 'range', '2007-12-21,2008-01-06' );
-
+ $range = new Piwik_Period_Range('range', '2007-12-21,2008-01-06');
+
$correct = array(
'2007-12-21',
'2007-12-22',
'2007-12-23',
array(
- '2007-12-24',
- '2007-12-25',
- '2007-12-26',
- '2007-12-27',
- '2007-12-28',
- '2007-12-29',
- '2007-12-30',
+ '2007-12-24',
+ '2007-12-25',
+ '2007-12-26',
+ '2007-12-27',
+ '2007-12-28',
+ '2007-12-29',
+ '2007-12-30',
),
array(
- '2007-12-31',
- '2008-01-01',
- '2008-01-02',
- '2008-01-03',
- '2008-01-04',
- '2008-01-05',
- '2008-01-06',
+ '2007-12-31',
+ '2008-01-01',
+ '2008-01-02',
+ '2008-01-03',
+ '2008-01-04',
+ '2008-01-05',
+ '2008-01-06',
),
);
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
@@ -671,61 +664,61 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testCustomRangeContainsMonthAndWeek()
{
- $range = new Piwik_Period_Range( 'range', '2011-09-18,2011-11-02', 'UTC', Piwik_Date::factory('2012-01-01') );
-
+ $range = new Piwik_Period_Range('range', '2011-09-18,2011-11-02', 'UTC', Piwik_Date::factory('2012-01-01'));
+
$correct = array(
-
- '2011-09-18',
- array(
- '2011-09-19',
- '2011-09-20',
- '2011-09-21',
- '2011-09-22',
- '2011-09-23',
- '2011-09-24',
- '2011-09-25',
- ),
-
- '2011-09-26',
- '2011-09-27',
- '2011-09-28',
- '2011-09-29',
- '2011-09-30',
- array(
- "2011-10-01",
- "2011-10-02",
- "2011-10-03",
- "2011-10-04",
- "2011-10-05",
- "2011-10-06",
- "2011-10-07",
- "2011-10-08",
- "2011-10-09",
- "2011-10-10",
- "2011-10-11",
- "2011-10-12",
- "2011-10-13",
- "2011-10-14",
- "2011-10-15",
- "2011-10-16",
- "2011-10-17",
- "2011-10-18",
- "2011-10-19",
- "2011-10-20",
- "2011-10-21",
- "2011-10-22",
- "2011-10-23",
- "2011-10-24",
- "2011-10-25",
- "2011-10-26",
- "2011-10-27",
- "2011-10-28",
- "2011-10-29",
- "2011-10-30",
- "2011-10-31",
- ),
- "2011-11-01",
- "2011-11-02",
+
+ '2011-09-18',
+ array(
+ '2011-09-19',
+ '2011-09-20',
+ '2011-09-21',
+ '2011-09-22',
+ '2011-09-23',
+ '2011-09-24',
+ '2011-09-25',
+ ),
+
+ '2011-09-26',
+ '2011-09-27',
+ '2011-09-28',
+ '2011-09-29',
+ '2011-09-30',
+ array(
+ "2011-10-01",
+ "2011-10-02",
+ "2011-10-03",
+ "2011-10-04",
+ "2011-10-05",
+ "2011-10-06",
+ "2011-10-07",
+ "2011-10-08",
+ "2011-10-09",
+ "2011-10-10",
+ "2011-10-11",
+ "2011-10-12",
+ "2011-10-13",
+ "2011-10-14",
+ "2011-10-15",
+ "2011-10-16",
+ "2011-10-17",
+ "2011-10-18",
+ "2011-10-19",
+ "2011-10-20",
+ "2011-10-21",
+ "2011-10-22",
+ "2011-10-23",
+ "2011-10-24",
+ "2011-10-25",
+ "2011-10-26",
+ "2011-10-27",
+ "2011-10-28",
+ "2011-10-29",
+ "2011-10-30",
+ "2011-10-31",
+ ),
+ "2011-11-01",
+ "2011-11-02",
);
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
@@ -748,45 +741,115 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
Piwik_Date::factory('2011-12-31'),
Piwik_Date::factory('2021-10-18')
);
- foreach($todays as $today)
- {
- $range = new Piwik_Period_Range( 'range', '2011-08-01,2011-10-17', 'UTC', $today );
-
+ foreach ($todays as $today) {
+ $range = new Piwik_Period_Range('range', '2011-08-01,2011-10-17', 'UTC', $today);
+
$correct = array(
-
- array(
- "2011-08-01",
- "2011-08-02",
- "2011-08-03",
- "2011-08-04",
- "2011-08-05",
- "2011-08-06",
- "2011-08-07",
- "2011-08-08",
- "2011-08-09",
- "2011-08-10",
- "2011-08-11",
- "2011-08-12",
- "2011-08-13",
- "2011-08-14",
- "2011-08-15",
- "2011-08-16",
- "2011-08-17",
- "2011-08-18",
- "2011-08-19",
- "2011-08-20",
- "2011-08-21",
- "2011-08-22",
- "2011-08-23",
- "2011-08-24",
- "2011-08-25",
- "2011-08-26",
- "2011-08-27",
- "2011-08-28",
- "2011-08-29",
- "2011-08-30",
- "2011-08-31",
- ),
+
+ array(
+ "2011-08-01",
+ "2011-08-02",
+ "2011-08-03",
+ "2011-08-04",
+ "2011-08-05",
+ "2011-08-06",
+ "2011-08-07",
+ "2011-08-08",
+ "2011-08-09",
+ "2011-08-10",
+ "2011-08-11",
+ "2011-08-12",
+ "2011-08-13",
+ "2011-08-14",
+ "2011-08-15",
+ "2011-08-16",
+ "2011-08-17",
+ "2011-08-18",
+ "2011-08-19",
+ "2011-08-20",
+ "2011-08-21",
+ "2011-08-22",
+ "2011-08-23",
+ "2011-08-24",
+ "2011-08-25",
+ "2011-08-26",
+ "2011-08-27",
+ "2011-08-28",
+ "2011-08-29",
+ "2011-08-30",
+ "2011-08-31",
+ ),
+ array(
+ "2011-09-01",
+ "2011-09-02",
+ "2011-09-03",
+ "2011-09-04",
+ "2011-09-05",
+ "2011-09-06",
+ "2011-09-07",
+ "2011-09-08",
+ "2011-09-09",
+ "2011-09-10",
+ "2011-09-11",
+ "2011-09-12",
+ "2011-09-13",
+ "2011-09-14",
+ "2011-09-15",
+ "2011-09-16",
+ "2011-09-17",
+ "2011-09-18",
+ "2011-09-19",
+ "2011-09-20",
+ "2011-09-21",
+ "2011-09-22",
+ "2011-09-23",
+ "2011-09-24",
+ "2011-09-25",
+ "2011-09-26",
+ "2011-09-27",
+ "2011-09-28",
+ "2011-09-29",
+ "2011-09-30",
+ ),
+ "2011-10-01",
+ "2011-10-02",
+
+ array(
+ "2011-10-03",
+ "2011-10-04",
+ "2011-10-05",
+ "2011-10-06",
+ "2011-10-07",
+ "2011-10-08",
+ "2011-10-09",
+ ),
+ array(
+ "2011-10-10",
+ "2011-10-11",
+ "2011-10-12",
+ "2011-10-13",
+ "2011-10-14",
+ "2011-10-15",
+ "2011-10-16",
+ ),
+ "2011-10-17",
+ );
+
+ $this->assertEquals(count($correct), $range->getNumberOfSubperiods());
+ $this->assertEquals($correct, $range->toString());
+ }
+ }
+
+ /**
+ * @group Core
+ * @group Period
+ * @group Period_Range
+ */
+ public function testCustomRangeOneMonthOnly()
+ {
+ $range = new Piwik_Period_Range('range', '2011-09-01,2011-09-30');
+
+ $correct = array(
array(
"2011-09-01",
"2011-09-02",
@@ -818,82 +881,11 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
"2011-09-28",
"2011-09-29",
"2011-09-30",
- ),
- "2011-10-01",
- "2011-10-02",
-
- array(
- "2011-10-03",
- "2011-10-04",
- "2011-10-05",
- "2011-10-06",
- "2011-10-07",
- "2011-10-08",
- "2011-10-09",
- ),
- array(
- "2011-10-10",
- "2011-10-11",
- "2011-10-12",
- "2011-10-13",
- "2011-10-14",
- "2011-10-15",
- "2011-10-16",
- ),
- "2011-10-17",
- );
-
- $this->assertEquals(count($correct), $range->getNumberOfSubperiods());
- $this->assertEquals($correct, $range->toString());
- }
- }
-
- /**
- * @group Core
- * @group Period
- * @group Period_Range
- */
- public function testCustomRangeOneMonthOnly()
- {
- $range = new Piwik_Period_Range( 'range', '2011-09-01,2011-09-30' );
-
- $correct = array(
- array(
- "2011-09-01",
- "2011-09-02",
- "2011-09-03",
- "2011-09-04",
- "2011-09-05",
- "2011-09-06",
- "2011-09-07",
- "2011-09-08",
- "2011-09-09",
- "2011-09-10",
- "2011-09-11",
- "2011-09-12",
- "2011-09-13",
- "2011-09-14",
- "2011-09-15",
- "2011-09-16",
- "2011-09-17",
- "2011-09-18",
- "2011-09-19",
- "2011-09-20",
- "2011-09-21",
- "2011-09-22",
- "2011-09-23",
- "2011-09-24",
- "2011-09-25",
- "2011-09-26",
- "2011-09-27",
- "2011-09-28",
- "2011-09-29",
- "2011-09-30",
- ));
+ ));
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
/**
* @group Core
* @group Period
@@ -901,56 +893,56 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function test_CustomRange_startsWithWeek_EndsWithMonth()
{
- $range = new Piwik_Period_Range( 'range', '2011-07-25,2011-08-31' );
-
+ $range = new Piwik_Period_Range('range', '2011-07-25,2011-08-31');
+
$correct = array(
-
- array(
- '2011-07-25',
- '2011-07-26',
- '2011-07-27',
- '2011-07-28',
- '2011-07-29',
- '2011-07-30',
- '2011-07-31',
- ),
- array(
- "2011-08-01",
- "2011-08-02",
- "2011-08-03",
- "2011-08-04",
- "2011-08-05",
- "2011-08-06",
- "2011-08-07",
- "2011-08-08",
- "2011-08-09",
- "2011-08-10",
- "2011-08-11",
- "2011-08-12",
- "2011-08-13",
- "2011-08-14",
- "2011-08-15",
- "2011-08-16",
- "2011-08-17",
- "2011-08-18",
- "2011-08-19",
- "2011-08-20",
- "2011-08-21",
- "2011-08-22",
- "2011-08-23",
- "2011-08-24",
- "2011-08-25",
- "2011-08-26",
- "2011-08-27",
- "2011-08-28",
- "2011-08-29",
- "2011-08-30",
- "2011-08-31",
- ));
+
+ array(
+ '2011-07-25',
+ '2011-07-26',
+ '2011-07-27',
+ '2011-07-28',
+ '2011-07-29',
+ '2011-07-30',
+ '2011-07-31',
+ ),
+ array(
+ "2011-08-01",
+ "2011-08-02",
+ "2011-08-03",
+ "2011-08-04",
+ "2011-08-05",
+ "2011-08-06",
+ "2011-08-07",
+ "2011-08-08",
+ "2011-08-09",
+ "2011-08-10",
+ "2011-08-11",
+ "2011-08-12",
+ "2011-08-13",
+ "2011-08-14",
+ "2011-08-15",
+ "2011-08-16",
+ "2011-08-17",
+ "2011-08-18",
+ "2011-08-19",
+ "2011-08-20",
+ "2011-08-21",
+ "2011-08-22",
+ "2011-08-23",
+ "2011-08-24",
+ "2011-08-25",
+ "2011-08-26",
+ "2011-08-27",
+ "2011-08-28",
+ "2011-08-29",
+ "2011-08-30",
+ "2011-08-31",
+ ));
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
/**
* @group Core
* @group Period
@@ -959,17 +951,17 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
public function testCustomRangeBeforeIsAfterYearRight()
{
try {
- $range = new Piwik_Period_Range( 'range', '2007-02-09,2007-02-01' );
+ $range = new Piwik_Period_Range('range', '2007-02-09,2007-02-01');
$this->assertEquals(0, $range->getNumberOfSubperiods());
$this->assertEquals(array(), $range->toString());
-
+
$range->getPrettyString();
} catch (Exception $e) {
return;
}
$this->fail('Expected exception not raised');
}
-
+
/**
* @group Core
* @group Period
@@ -977,13 +969,13 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testCustomRangeLastN()
{
- $range = new Piwik_Period_Range( 'range', 'last4' );
+ $range = new Piwik_Period_Range('range', 'last4');
$range->setDefaultEndDate(Piwik_Date::factory('2008-01-03'));
$correct = array(
- '2007-12-31',
- '2008-01-01',
- '2008-01-02',
- '2008-01-03',
+ '2007-12-31',
+ '2008-01-01',
+ '2008-01-02',
+ '2008-01-03',
);
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
@@ -996,17 +988,17 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testCustomRangePreviousN()
{
- $range = new Piwik_Period_Range( 'range', 'previous3' );
+ $range = new Piwik_Period_Range('range', 'previous3');
$range->setDefaultEndDate(Piwik_Date::factory('2008-01-03'));
$correct = array(
- '2007-12-31',
- '2008-01-01',
- '2008-01-02',
+ '2007-12-31',
+ '2008-01-01',
+ '2008-01-02',
);
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
/**
* @group Core
* @group Period
@@ -1014,16 +1006,16 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
*/
public function testCustomRangePreviousNEndToday()
{
- $range = new Piwik_Period_Range( 'range', 'previous3' );
+ $range = new Piwik_Period_Range('range', 'previous3');
$correct = array(
- date('Y-m-d',time()-86400*3),
- date('Y-m-d',time()-86400*2),
- date('Y-m-d',time()-86400*1),
+ date('Y-m-d', time() - 86400 * 3),
+ date('Y-m-d', time() - 86400 * 2),
+ date('Y-m-d', time() - 86400 * 1),
);
$this->assertEquals(count($correct), $range->getNumberOfSubperiods());
$this->assertEquals($correct, $range->toString());
}
-
+
/**
* @group Core
* @group Period
@@ -1032,7 +1024,7 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
public function testInvalidRangeThrows()
{
try {
- $range = new Piwik_Period_Range( 'range', '0001-01-01,today' );
+ $range = new Piwik_Period_Range('range', '0001-01-01,today');
$range->getLocalizedLongString();
} catch (Exception $e) {
return;
@@ -1048,7 +1040,7 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
public function testGetLocalizedShortString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $month = new Piwik_Period_Range( 'range', '2000-12-09,2001-02-01' );
+ $month = new Piwik_Period_Range('range', '2000-12-09,2001-02-01');
$shouldBe = '9 Dec 00 - 1 Feb 01';
$this->assertEquals($shouldBe, $month->getLocalizedShortString());
}
@@ -1061,7 +1053,7 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
public function testGetLocalizedLongString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $month = new Piwik_Period_Range( 'range', '2023-05-09,2023-05-21' );
+ $month = new Piwik_Period_Range('range', '2023-05-09,2023-05-21');
$shouldBe = '8 May 23 - 21 May 23';
$this->assertEquals($shouldBe, $month->getLocalizedLongString());
}
@@ -1074,31 +1066,31 @@ class Period_RangeTest extends PHPUnit_Framework_TestCase
public function testGetPrettyString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $month = new Piwik_Period_Range( 'range', '2007-02-09,2007-03-15' );
+ $month = new Piwik_Period_Range('range', '2007-02-09,2007-03-15');
$shouldBe = 'From 2007-02-09 to 2007-03-15';
$this->assertEquals($shouldBe, $month->getPrettyString());
}
-
- /**
- * Data provider for testLastNLimits.
- */
- public function getDataForLastNLimitsTest()
- {
- return array(array('day', 5*365 + 1, 5*365),
- array('week', 10*52 + 1, 10*52),
- array('month', 121, 120),
- array('year', 11, 10));
- }
-
- /**
- * @group Core
- * @group Period
- * @group Period_Range
+
+ /**
+ * Data provider for testLastNLimits.
+ */
+ public function getDataForLastNLimitsTest()
+ {
+ return array(array('day', 5 * 365 + 1, 5 * 365),
+ array('week', 10 * 52 + 1, 10 * 52),
+ array('month', 121, 120),
+ array('year', 11, 10));
+ }
+
+ /**
+ * @group Core
+ * @group Period
+ * @group Period_Range
* @dataProvider getDataForLastNLimitsTest
- */
- public function testLastNLimits($period, $lastN, $expectedLastN)
- {
- $range = new Piwik_Period_Range($period, 'last'.$lastN);
- $this->assertEquals($expectedLastN, $range->getNumberOfSubperiods());
- }
+ */
+ public function testLastNLimits($period, $lastN, $expectedLastN)
+ {
+ $range = new Piwik_Period_Range($period, 'last' . $lastN);
+ $this->assertEquals($expectedLastN, $range->getNumberOfSubperiods());
+ }
}
diff --git a/tests/PHPUnit/Core/Period/WeekTest.php b/tests/PHPUnit/Core/Period/WeekTest.php
index faa866e954..a6f1c758e2 100644
--- a/tests/PHPUnit/Core/Period/WeekTest.php
+++ b/tests/PHPUnit/Core/Period/WeekTest.php
@@ -18,8 +18,8 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
*/
public function testWeekBetween2years()
{
- $week = new Piwik_Period_Week( Piwik_Date::factory("2006-01-01"));
- $correct=array(
+ $week = new Piwik_Period_Week(Piwik_Date::factory("2006-01-01"));
+ $correct = array(
"2005-12-26",
"2005-12-27",
"2005-12-28",
@@ -30,7 +30,7 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
$this->assertEquals($correct, $week->toString());
$this->assertEquals(7, $week->getNumberOfSubperiods());
}
-
+
/**
* test week between 2 months Week Mai 29 To Mai 31 2006
* @group Core
@@ -39,8 +39,8 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
*/
public function testWeekBetween2month()
{
- $week = new Piwik_Period_Week( Piwik_Date::factory("2006-05-29"));
- $correct=array(
+ $week = new Piwik_Period_Week(Piwik_Date::factory("2006-05-29"));
+ $correct = array(
"2006-05-29",
"2006-05-30",
"2006-05-31",
@@ -51,7 +51,7 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
$this->assertEquals($correct, $week->toString());
$this->assertEquals(7, $week->getNumberOfSubperiods());
}
-
+
/**
* test week between feb and march for leap year
* @group Core
@@ -60,7 +60,7 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
*/
public function testWeekFebLeapyear()
{
- $correct=array(
+ $correct = array(
'2023-02-27',
'2023-02-28',
'2023-03-01',
@@ -68,15 +68,15 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
'2023-03-03',
'2023-03-04',
'2023-03-05',);
-
- $week = new Piwik_Period_Week( Piwik_Date::factory('2023-02-27'));
+
+ $week = new Piwik_Period_Week(Piwik_Date::factory('2023-02-27'));
$this->assertEquals($correct, $week->toString());
$this->assertEquals(7, $week->getNumberOfSubperiods());
- $week = new Piwik_Period_Week( Piwik_Date::factory('2023-03-01'));
+ $week = new Piwik_Period_Week(Piwik_Date::factory('2023-03-01'));
$this->assertEquals($correct, $week->toString());
$this->assertEquals(7, $week->getNumberOfSubperiods());
}
-
+
/**
* test week between feb and march for no leap year
* @group Core
@@ -85,7 +85,7 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
*/
public function testWeekFebnonLeapyear()
{
- $correct=array(
+ $correct = array(
'2024-02-26',
'2024-02-27',
'2024-02-28',
@@ -93,15 +93,15 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
'2024-03-01',
'2024-03-02',
'2024-03-03',);
-
- $week = new Piwik_Period_Week( Piwik_Date::factory('2024-02-27'));
+
+ $week = new Piwik_Period_Week(Piwik_Date::factory('2024-02-27'));
$this->assertEquals($correct, $week->toString());
$this->assertEquals(7, $week->getNumberOfSubperiods());
- $week = new Piwik_Period_Week( Piwik_Date::factory('2024-03-01'));
+ $week = new Piwik_Period_Week(Piwik_Date::factory('2024-03-01'));
$this->assertEquals($correct, $week->toString());
$this->assertEquals(7, $week->getNumberOfSubperiods());
}
-
+
/**
* test week normal middle of the month
* @group Core
@@ -110,7 +110,7 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
*/
public function testWeekMiddleofmonth()
{
- $correct=array(
+ $correct = array(
'2024-10-07',
'2024-10-08',
'2024-10-09',
@@ -118,8 +118,8 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
'2024-10-11',
'2024-10-12',
'2024-10-13',);
-
- $week = new Piwik_Period_Week( Piwik_Date::factory('2024-10-09'));
+
+ $week = new Piwik_Period_Week(Piwik_Date::factory('2024-10-09'));
$this->assertEquals($correct, $week->toString());
$this->assertEquals(7, $week->getNumberOfSubperiods());
}
@@ -132,7 +132,7 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
public function testGetLocalizedShortString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $week = new Piwik_Period_Week( Piwik_Date::factory('2024-10-09'));
+ $week = new Piwik_Period_Week(Piwik_Date::factory('2024-10-09'));
$shouldBe = '7 Oct - 13 Oct 24';
$this->assertEquals($shouldBe, $week->getLocalizedShortString());
}
@@ -145,7 +145,7 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
public function testGetLocalizedLongString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $week = new Piwik_Period_Week( Piwik_Date::factory('2024-10-09'));
+ $week = new Piwik_Period_Week(Piwik_Date::factory('2024-10-09'));
$shouldBe = 'Week 7 October - 13 October 2024';
$this->assertEquals($shouldBe, $week->getLocalizedLongString());
}
@@ -158,7 +158,7 @@ class Period_WeekTest extends PHPUnit_Framework_TestCase
public function testGetPrettyString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $week = new Piwik_Period_Week( Piwik_Date::factory('2024-10-09'));
+ $week = new Piwik_Period_Week(Piwik_Date::factory('2024-10-09'));
$shouldBe = 'From 2024-10-07 to 2024-10-13';
$this->assertEquals($shouldBe, $week->getPrettyString());
}
diff --git a/tests/PHPUnit/Core/Period/YearTest.php b/tests/PHPUnit/Core/Period/YearTest.php
index c96bb8c3f2..11ea08b2b0 100644
--- a/tests/PHPUnit/Core/Period/YearTest.php
+++ b/tests/PHPUnit/Core/Period/YearTest.php
@@ -18,7 +18,7 @@ class Period_YearTest extends PHPUnit_Framework_TestCase
*/
public function testYearNormalcase()
{
- $correct=array(
+ $correct = array(
'2024-01-01',
'2024-02-01',
'2024-03-01',
@@ -31,12 +31,12 @@ class Period_YearTest extends PHPUnit_Framework_TestCase
'2024-10-01',
'2024-11-01',
'2024-12-01',);
-
- $year = new Piwik_Period_Year( Piwik_Date::factory('2024-10-09'));
+
+ $year = new Piwik_Period_Year(Piwik_Date::factory('2024-10-09'));
$this->assertEquals(12, $year->getNumberOfSubperiods());
$this->assertEquals($correct, $year->toString());
}
-
+
/**
* test past
* @group Core
@@ -45,7 +45,7 @@ class Period_YearTest extends PHPUnit_Framework_TestCase
*/
public function testYearPastAndWrongdate()
{
- $correct=array(
+ $correct = array(
'2000-01-01',
'2000-02-01',
'2000-03-01',
@@ -59,8 +59,8 @@ class Period_YearTest extends PHPUnit_Framework_TestCase
'2000-11-01',
'2000-12-01',
);
-
- $year = new Piwik_Period_Year( Piwik_Date::factory('2000-02-15'));
+
+ $year = new Piwik_Period_Year(Piwik_Date::factory('2000-02-15'));
$this->assertEquals(12, $year->getNumberOfSubperiods());
$this->assertEquals($correct, $year->toString());
}
@@ -73,7 +73,7 @@ class Period_YearTest extends PHPUnit_Framework_TestCase
public function testGetLocalizedShortString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $year = new Piwik_Period_Year( Piwik_Date::factory('2024-10-09'));
+ $year = new Piwik_Period_Year(Piwik_Date::factory('2024-10-09'));
$shouldBe = '2024';
$this->assertEquals($shouldBe, $year->getLocalizedShortString());
}
@@ -86,7 +86,7 @@ class Period_YearTest extends PHPUnit_Framework_TestCase
public function testGetLocalizedLongString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $year = new Piwik_Period_Year( Piwik_Date::factory('2024-10-09'));
+ $year = new Piwik_Period_Year(Piwik_Date::factory('2024-10-09'));
$shouldBe = '2024';
$this->assertEquals($shouldBe, $year->getLocalizedLongString());
}
@@ -99,7 +99,7 @@ class Period_YearTest extends PHPUnit_Framework_TestCase
public function testGetPrettyString()
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $year = new Piwik_Period_Year( Piwik_Date::factory('2024-10-09'));
+ $year = new Piwik_Period_Year(Piwik_Date::factory('2024-10-09'));
$shouldBe = '2024';
$this->assertEquals($shouldBe, $year->getPrettyString());
}
diff --git a/tests/PHPUnit/Core/PeriodTest.php b/tests/PHPUnit/Core/PeriodTest.php
index 2cabd1157f..c7ab39662b 100644
--- a/tests/PHPUnit/Core/PeriodTest.php
+++ b/tests/PHPUnit/Core/PeriodTest.php
@@ -13,37 +13,37 @@ class PeriodTest extends PHPUnit_Framework_TestCase
*/
public function testGetId()
{
- $period = new Piwik_Period_Day( Piwik_Date::today() );
- $this->assertNotEquals( 0, $period->getId() );
- $period = new Piwik_Period_Week( Piwik_Date::today() );
- $this->assertNotEquals( 0, $period->getId() );
- $period = new Piwik_Period_Month( Piwik_Date::today() );
- $this->assertNotEquals( 0, $period->getId() );
- $period = new Piwik_Period_Year( Piwik_Date::today() );
- $this->assertNotEquals( 0, $period->getId() );
+ $period = new Piwik_Period_Day(Piwik_Date::today());
+ $this->assertNotEquals(0, $period->getId());
+ $period = new Piwik_Period_Week(Piwik_Date::today());
+ $this->assertNotEquals(0, $period->getId());
+ $period = new Piwik_Period_Month(Piwik_Date::today());
+ $this->assertNotEquals(0, $period->getId());
+ $period = new Piwik_Period_Year(Piwik_Date::today());
+ $this->assertNotEquals(0, $period->getId());
}
-
+
/**
* @group Core
* @group Period
*/
public function testGetLabel()
{
- $period = new Piwik_Period_Day( Piwik_Date::today() );
+ $period = new Piwik_Period_Day(Piwik_Date::today());
$label = $period->getLabel();
- $this->assertInternalType( 'string', $label);
+ $this->assertInternalType('string', $label);
$this->assertNotEmpty($label);
- $period = new Piwik_Period_Week( Piwik_Date::today() );
+ $period = new Piwik_Period_Week(Piwik_Date::today());
$label = $period->getLabel();
- $this->assertInternalType( 'string', $label);
+ $this->assertInternalType('string', $label);
$this->assertNotEmpty($label);
- $period = new Piwik_Period_Month( Piwik_Date::today() );
+ $period = new Piwik_Period_Month(Piwik_Date::today());
$label = $period->getLabel();
- $this->assertInternalType( 'string', $label);
+ $this->assertInternalType('string', $label);
$this->assertNotEmpty($label);
- $period = new Piwik_Period_Year( Piwik_Date::today() );
+ $period = new Piwik_Period_Year(Piwik_Date::today());
$label = $period->getLabel();
- $this->assertInternalType( 'string', $label);
+ $this->assertInternalType('string', $label);
$this->assertNotEmpty($label);
}
diff --git a/tests/PHPUnit/Core/PiwikTest.php b/tests/PHPUnit/Core/PiwikTest.php
index 0061af1585..fbb7741515 100644
--- a/tests/PHPUnit/Core/PiwikTest.php
+++ b/tests/PHPUnit/Core/PiwikTest.php
@@ -13,13 +13,13 @@ class PiwikTest extends DatabaseTestCase
public function getValidNumeric()
{
$valid = array(
- -1, 0 , 1, 1.5, -1.5, 21111, 89898, 99999999999, -4565656,
- (float)-1, (float)0 , (float)1, (float)1.5, (float)-1.5, (float)21111, (float)89898, (float)99999999999, (float)-4565656,
- (int)-1, (int)0 , (int)1, (int)1.5, (int)-1.5, (int)21111, (int)89898, (int)99999999999, (int)-4565656,
- '-1', '0' , '1', '1.5', '-1.5', '21111', '89898', '99999999999', '-4565656',
- '1e3','0x123', "-1e-2",
+ -1, 0, 1, 1.5, -1.5, 21111, 89898, 99999999999, -4565656,
+ (float)-1, (float)0, (float)1, (float)1.5, (float)-1.5, (float)21111, (float)89898, (float)99999999999, (float)-4565656,
+ (int)-1, (int)0, (int)1, (int)1.5, (int)-1.5, (int)21111, (int)89898, (int)99999999999, (int)-4565656,
+ '-1', '0', '1', '1.5', '-1.5', '21111', '89898', '99999999999', '-4565656',
+ '1e3', '0x123', "-1e-2",
);
- foreach($valid AS $key => $value) {
+ foreach ($valid AS $key => $value) {
$valid[$key] = array($value);
}
return $valid;
@@ -32,7 +32,7 @@ class PiwikTest extends DatabaseTestCase
*/
public function testIsNumericValid($toTest)
{
- $this->assertTrue(is_numeric($toTest), $toTest." not valid but should!");
+ $this->assertTrue(is_numeric($toTest), $toTest . " not valid but should!");
}
/**
@@ -41,9 +41,9 @@ class PiwikTest extends DatabaseTestCase
public function getInvalidNumeric()
{
$notValid = array(
- '-1.0.0', '1,2', '--1', '-.', '- 1', '1-',
+ '-1.0.0', '1,2', '--1', '-.', '- 1', '1-',
);
- foreach($notValid AS $key => $value) {
+ foreach ($notValid AS $key => $value) {
$notValid[$key] = array($value);
}
return $notValid;
@@ -56,7 +56,7 @@ class PiwikTest extends DatabaseTestCase
*/
public function testIsNumericNotValid($toTest)
{
- $this->assertFalse(is_numeric($toTest), $toTest." valid but shouldn't!");
+ $this->assertFalse(is_numeric($toTest), $toTest . " valid but shouldn't!");
}
/**
@@ -65,13 +65,13 @@ class PiwikTest extends DatabaseTestCase
*/
public function testSecureDiv()
{
- $this->assertSame( 3, Piwik::secureDiv( 9,3 ) );
- $this->assertSame( 0, Piwik::secureDiv( 9,0 ) );
- $this->assertSame( 10, Piwik::secureDiv( 10,1 ) );
- $this->assertSame( 10.0, Piwik::secureDiv( 10.0, 1.0 ) );
- $this->assertSame( 5.5, Piwik::secureDiv( 11.0, 2 ) );
- $this->assertSame( 0, Piwik::secureDiv( 11.0, 'a' ) );
-
+ $this->assertSame(3, Piwik::secureDiv(9, 3));
+ $this->assertSame(0, Piwik::secureDiv(9, 0));
+ $this->assertSame(10, Piwik::secureDiv(10, 1));
+ $this->assertSame(10.0, Piwik::secureDiv(10.0, 1.0));
+ $this->assertSame(5.5, Piwik::secureDiv(11.0, 2));
+ $this->assertSame(0, Piwik::secureDiv(11.0, 'a'));
+
}
/**
@@ -88,8 +88,8 @@ class PiwikTest extends DatabaseTestCase
array(86400 + 3600 * 10, array('1 days 10 hours', '34:00:00')),
array(86400 * 365, array('365 days 0 hours', '8760:00:00')),
array((86400 * (365.25 + 10)), array('1 years 10 days', '9006:00:00')),
- array(1.342, array('1.342s', '00:00:01.342')),
- array(.342, array('0.342s', '00:00:00.342')),
+ array(1.342, array('1.342s', '00:00:01.342')),
+ array(.342, array('0.342s', '00:00:00.342')),
);
}
@@ -102,7 +102,7 @@ class PiwikTest extends DatabaseTestCase
{
Piwik_Translate::getInstance()->loadEnglishTranslation();
- $sentenceExpected = str_replace(' ','&nbsp;', $expected[0]);
+ $sentenceExpected = str_replace(' ', '&nbsp;', $expected[0]);
$numericExpected = $expected[1];
$this->assertEquals($sentenceExpected, Piwik::getPrettyTimeFromSeconds($seconds, $sentence = true));
$this->assertEquals($numericExpected, Piwik::getPrettyTimeFromSeconds($seconds, $sentence = false));
@@ -129,7 +129,7 @@ class PiwikTest extends DatabaseTestCase
'alpha>beta',
'alpha?beta',
);
- foreach($notValid AS $key => $value) {
+ foreach ($notValid AS $key => $value) {
$notValid[$key] = array($value);
}
return $notValid;
@@ -160,7 +160,7 @@ class PiwikTest extends DatabaseTestCase
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
'shoot_puck@the-goal.com',
);
- foreach($valid AS $key => $value) {
+ foreach ($valid AS $key => $value) {
$valid[$key] = array($value);
}
return $valid;
@@ -176,16 +176,16 @@ class PiwikTest extends DatabaseTestCase
$this->assertNull(Piwik::checkValidLoginString($toTest));
}
- /**
- * Dataprovider for testGetPrettyValue
- */
- public function getGetPrettyValueTestCases()
- {
- return array(
- array('revenue', 12, '$ 12'),
- array('revenue_evolution', '100 %', '100 %'),
- );
- }
+ /**
+ * Dataprovider for testGetPrettyValue
+ */
+ public function getGetPrettyValueTestCases()
+ {
+ return array(
+ array('revenue', 12, '$ 12'),
+ array('revenue_evolution', '100 %', '100 %'),
+ );
+ }
/**
* @group Core
@@ -194,49 +194,49 @@ class PiwikTest extends DatabaseTestCase
*/
public function testGetPrettyValue($columnName, $value, $expected)
{
- $access = new Piwik_Access();
- Zend_Registry::set('access', $access);
- $access->setSuperUser(true);
-
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("test","http://test");
-
- $this->assertEquals(
- $expected,
- Piwik::getPrettyValue($idsite, $columnName, $value, false, false)
- );
- }
-
- /**
- * Data provider for testIsAssociativeArray.
- */
- public function getIsAssociativeArrayTestCases()
- {
- return array(
- array(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 4 => 'e', 5 => 'f'), false),
- array(array(-1 => 'a', 0 => 'a', 1 => 'a', 2 => 'a', 3 => 'a'), true),
- array(array(4 => 'a', 5 => 'a', 6 => 'a', 7 => 'a', 8 => 'a'), true),
- array(array(0 => 'a', 2 => 'a', 3 => 'a', 4 => 'a', 5 => 'a'), true),
- array(array('abc' => 'a', 0 => 'b', 'sdfds' => 'd'), true),
- array(array('abc' => 'def'), true)
- );
- }
-
- /**
+ $access = new Piwik_Access();
+ Zend_Registry::set('access', $access);
+ $access->setSuperUser(true);
+
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("test", "http://test");
+
+ $this->assertEquals(
+ $expected,
+ Piwik::getPrettyValue($idsite, $columnName, $value, false, false)
+ );
+ }
+
+ /**
+ * Data provider for testIsAssociativeArray.
+ */
+ public function getIsAssociativeArrayTestCases()
+ {
+ return array(
+ array(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 4 => 'e', 5 => 'f'), false),
+ array(array(-1 => 'a', 0 => 'a', 1 => 'a', 2 => 'a', 3 => 'a'), true),
+ array(array(4 => 'a', 5 => 'a', 6 => 'a', 7 => 'a', 8 => 'a'), true),
+ array(array(0 => 'a', 2 => 'a', 3 => 'a', 4 => 'a', 5 => 'a'), true),
+ array(array('abc' => 'a', 0 => 'b', 'sdfds' => 'd'), true),
+ array(array('abc' => 'def'), true)
+ );
+ }
+
+ /**
* @group Core
* @group Piwik
* @dataProvider getIsAssociativeArrayTestCases
- */
- public function testIsAssociativeArray( $array, $expected )
- {
- $this->assertEquals($expected, Piwik::isAssociativeArray($array));
- }
-
- /**
- * @group Core
- * @group Piwik
- */
- public function testCheckIfFileSystemIsNFSOnNonNFS()
- {
- $this->assertFalse(Piwik::checkIfFileSystemIsNFS());
- }
+ */
+ public function testIsAssociativeArray($array, $expected)
+ {
+ $this->assertEquals($expected, Piwik::isAssociativeArray($array));
+ }
+
+ /**
+ * @group Core
+ * @group Piwik
+ */
+ public function testCheckIfFileSystemIsNFSOnNonNFS()
+ {
+ $this->assertFalse(Piwik::checkIfFileSystemIsNFS());
+ }
}
diff --git a/tests/PHPUnit/Core/Plugin/ConfigTest.php b/tests/PHPUnit/Core/Plugin/ConfigTest.php
index a6d2924d29..db9421b94a 100644
--- a/tests/PHPUnit/Core/Plugin/ConfigTest.php
+++ b/tests/PHPUnit/Core/Plugin/ConfigTest.php
@@ -11,8 +11,7 @@ class Plugin_ConfigTest extends PHPUnit_Framework_TestCase
function setUp()
{
$path = PIWIK_USER_PATH . '/plugins/ExamplePlugin/config/local.config.php';
- if (file_exists($path))
- {
+ if (file_exists($path)) {
@unlink($path);
}
diff --git a/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php b/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php
index e50ec1e454..bd7983cdf6 100644
--- a/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php
+++ b/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php
@@ -25,7 +25,7 @@ class WidgetsListTest extends DatabaseTestCase
$_GET['idSite'] = 1;
$pluginsManager = Piwik_PluginsManager::getInstance();
- $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
+ $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
$pluginsManager->loadPlugins($pluginsToLoad);
Piwik_WidgetsList::_reset();
@@ -38,13 +38,13 @@ class WidgetsListTest extends DatabaseTestCase
// check if each category has the right number of widgets
$numberOfWidgets = array(
'VisitsSummary_VisitsSummary' => 6,
- 'Live!' => 3,
- 'General_Visitors' => 12,
- 'UserSettings_VisitorSettings' => 11,
- 'Actions_Actions' => 8,
- 'Actions_SubmenuSitesearch' => 5,
- 'Referers_Referers' => 7,
- 'Goals_Goals' => 1,
+ 'Live!' => 3,
+ 'General_Visitors' => 12,
+ 'UserSettings_VisitorSettings' => 11,
+ 'Actions_Actions' => 8,
+ 'Actions_SubmenuSitesearch' => 5,
+ 'Referers_Referers' => 7,
+ 'Goals_Goals' => 1,
'SEO' => 2,
'Example Widgets' => 4,
'ExamplePlugin_exampleWidgets' => 3
@@ -72,7 +72,7 @@ class WidgetsListTest extends DatabaseTestCase
$_GET['idSite'] = 1;
$pluginsManager = Piwik_PluginsManager::getInstance();
- $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
+ $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
$pluginsManager->loadPlugins($pluginsToLoad);
Piwik_WidgetsList::_reset();
@@ -84,12 +84,12 @@ class WidgetsListTest extends DatabaseTestCase
// check that the goal widget was added
$numberOfWidgets = array(
- 'Goals_Goals' => 2,
+ 'Goals_Goals' => 2,
);
foreach ($numberOfWidgets AS $category => $widgetCount) {
- $expected = count($widgets[$category]);
- $this->assertEquals($widgetCount, count($widgets[$category]));
+ $expected = count($widgets[$category]);
+ $this->assertEquals($widgetCount, count($widgets[$category]));
}
}
@@ -111,7 +111,7 @@ class WidgetsListTest extends DatabaseTestCase
$_GET['idSite'] = 1;
$pluginsManager = Piwik_PluginsManager::getInstance();
- $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
+ $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
$pluginsManager->loadPlugins($pluginsToLoad);
Piwik_WidgetsList::_reset();
@@ -123,8 +123,8 @@ class WidgetsListTest extends DatabaseTestCase
// check if each category has the right number of widgets
$numberOfWidgets = array(
- 'Goals_Goals' => 2,
- 'Goals_Ecommerce' => 5,
+ 'Goals_Goals' => 2,
+ 'Goals_Ecommerce' => 5,
);
foreach ($numberOfWidgets AS $category => $widgetCount) {
diff --git a/tests/PHPUnit/Core/RankingQueryTest.php b/tests/PHPUnit/Core/RankingQueryTest.php
index af7ee9a587..833efbfd0d 100644
--- a/tests/PHPUnit/Core/RankingQueryTest.php
+++ b/tests/PHPUnit/Core/RankingQueryTest.php
@@ -8,22 +8,22 @@
class RankingQueryTest extends PHPUnit_Framework_TestCase
{
-
+
/**
* @group Core
* @group RankingQuery
*/
public function testBasic()
{
- $query = new Piwik_RankingQuery();
- $query->addLabelColumn('label');
- $query->addColumn('column');
- $query->addColumn('columnSum', 'sum');
- $query->setLimit(10);
-
- $innerQuery = "SELECT label, column, columnSum FROM myTable";
-
- $expected = "
+ $query = new Piwik_RankingQuery();
+ $query->addLabelColumn('label');
+ $query->addColumn('column');
+ $query->addColumn('columnSum', 'sum');
+ $query->setLimit(10);
+
+ $innerQuery = "SELECT label, column, columnSum FROM myTable";
+
+ $expected = "
SELECT
CASE
WHEN counter = 11 THEN 'Others'
@@ -46,23 +46,23 @@ class RankingQueryTest extends PHPUnit_Framework_TestCase
) AS withCounter
GROUP BY counter
";
-
- $this->checkQuery($query, $innerQuery, $expected);
- }
-
- /**
- * @group Core
- * @group RankingQuery
- */
- public function testExcludeRows()
- {
- $query = new Piwik_RankingQuery(20);
- $query->addLabelColumn('label');
- $query->setColumnToMarkExcludedRows('exclude_marker');
-
- $innerQuery = "SELECT label, 1 AS exclude_marker FROM myTable";
-
- $expected = "
+
+ $this->checkQuery($query, $innerQuery, $expected);
+ }
+
+ /**
+ * @group Core
+ * @group RankingQuery
+ */
+ public function testExcludeRows()
+ {
+ $query = new Piwik_RankingQuery(20);
+ $query->addLabelColumn('label');
+ $query->setColumnToMarkExcludedRows('exclude_marker');
+
+ $innerQuery = "SELECT label, 1 AS exclude_marker FROM myTable";
+
+ $expected = "
SELECT
CASE
WHEN counter = 21 THEN 'Others'
@@ -84,23 +84,23 @@ class RankingQueryTest extends PHPUnit_Framework_TestCase
) AS withCounter
GROUP BY counter
";
-
- $this->checkQuery($query, $innerQuery, $expected);
- }
-
- /**
- * @group Core
- * @group RankingQuery
- */
- public function testPartitionResult()
- {
- $query = new Piwik_RankingQuery(1000);
- $query->addLabelColumn('label');
- $query->partitionResultIntoMultipleGroups('partition', array(1, 2, 3));
-
- $innerQuery = "SELECT label, partition FROM myTable";
-
- $expected = "
+
+ $this->checkQuery($query, $innerQuery, $expected);
+ }
+
+ /**
+ * @group Core
+ * @group RankingQuery
+ */
+ public function testPartitionResult()
+ {
+ $query = new Piwik_RankingQuery(1000);
+ $query->addLabelColumn('label');
+ $query->partitionResultIntoMultipleGroups('partition', array(1, 2, 3));
+
+ $innerQuery = "SELECT label, partition FROM myTable";
+
+ $expected = "
SELECT
CASE
WHEN counter = 1001 THEN 'Others'
@@ -128,24 +128,24 @@ class RankingQueryTest extends PHPUnit_Framework_TestCase
) AS withCounter
GROUP BY counter, `partition`
";
-
- $this->checkQuery($query, $innerQuery, $expected);
- }
-
- /**
- * @param Piwik_RankingQuery $rankingQuery
- * @param string $innerQuerySql
- * @param string $expected
- */
- private function checkQuery($rankingQuery, $innerQuerySql, $expected)
- {
- $query = $rankingQuery->generateQuery($innerQuerySql);
-
- $queryNoWhitespace = preg_replace("/\s+/", "", $query);
- $expectedNoWhitespace = preg_replace("/\s+/", "", $expected);
-
- $message = 'Unexpected query: '.$query;
- $this->assertEquals($queryNoWhitespace, $expectedNoWhitespace, $message);
- }
-
+
+ $this->checkQuery($query, $innerQuery, $expected);
+ }
+
+ /**
+ * @param Piwik_RankingQuery $rankingQuery
+ * @param string $innerQuerySql
+ * @param string $expected
+ */
+ private function checkQuery($rankingQuery, $innerQuerySql, $expected)
+ {
+ $query = $rankingQuery->generateQuery($innerQuerySql);
+
+ $queryNoWhitespace = preg_replace("/\s+/", "", $query);
+ $expectedNoWhitespace = preg_replace("/\s+/", "", $expected);
+
+ $message = 'Unexpected query: ' . $query;
+ $this->assertEquals($queryNoWhitespace, $expectedNoWhitespace, $message);
+ }
+
}
diff --git a/tests/PHPUnit/Core/ReleaseCheckListTest.php b/tests/PHPUnit/Core/ReleaseCheckListTest.php
index d0d9a75a5d..a14ed292f7 100644
--- a/tests/PHPUnit/Core/ReleaseCheckListTest.php
+++ b/tests/PHPUnit/Core/ReleaseCheckListTest.php
@@ -40,11 +40,10 @@ class ReleaseCheckListTest extends PHPUnit_Framework_TestCase
$section = key($key);
$optionName = current($key);
$value = null;
- if(isset($this->globalConfig[$section][$optionName]))
- {
+ if (isset($this->globalConfig[$section][$optionName])) {
$value = $this->globalConfig[$section][$optionName];
}
- $this->assertEquals($valueExpected, $value, "$section -> $optionName was '".var_export($value, true) ."', expected '".var_export($valueExpected,true)."'");
+ $this->assertEquals($valueExpected, $value, "$section -> $optionName was '" . var_export($value, true) . "', expected '" . var_export($valueExpected, true) . "'");
}
/**
@@ -55,10 +54,9 @@ class ReleaseCheckListTest extends PHPUnit_Framework_TestCase
{
$patternFailIfFound = '{debug}';
$files = Piwik::globr(PIWIK_INCLUDE_PATH . '/plugins', '*.tpl');
- foreach($files as $file)
- {
+ foreach ($files as $file) {
$content = file_get_contents($file);
- $this->assertFalse(strpos($content, $patternFailIfFound), 'found in '.$file);
+ $this->assertFalse(strpos($content, $patternFailIfFound), 'found in ' . $file);
}
}
@@ -69,15 +67,13 @@ class ReleaseCheckListTest extends PHPUnit_Framework_TestCase
public function testCheckThatGivenPluginsAreDisabledByDefault()
{
$pluginsShouldBeDisabled = array(
- 'AnonymizeIP',
- 'DBStats',
- 'SecurityInfo',
- 'VisitorGenerator',
+ 'AnonymizeIP',
+ 'DBStats',
+ 'SecurityInfo',
+ 'VisitorGenerator',
);
- foreach($pluginsShouldBeDisabled as $pluginName)
- {
- if(in_array($pluginName, $this->globalConfig['Plugins']['Plugins']))
- {
+ foreach ($pluginsShouldBeDisabled as $pluginName) {
+ if (in_array($pluginName, $this->globalConfig['Plugins']['Plugins'])) {
throw new Exception("Plugin $pluginName is enabled by default but shouldn't.");
}
}
@@ -102,7 +98,7 @@ class ReleaseCheckListTest extends PHPUnit_Framework_TestCase
public function testPiwikTrackerDebugIsOff()
{
$this->assertTrue(!isset($GLOBALS['PIWIK_TRACKER_DEBUG']));
-
+
$oldGet = $_GET;
$_GET = array('idsite' => 1);
@@ -110,7 +106,7 @@ class ReleaseCheckListTest extends PHPUnit_Framework_TestCase
ob_start();
include PIWIK_PATH_TEST_TO_ROOT . "/piwik.php";
ob_end_clean();
-
+
$_GET = $oldGet;
$this->assertTrue($GLOBALS['PIWIK_TRACKER_DEBUG'] === false);
@@ -125,13 +121,13 @@ class ReleaseCheckListTest extends PHPUnit_Framework_TestCase
Piwik::createConfigObject();
Piwik_Config::getInstance()->setTestEnvironment();
- $jqueryJs = file_get_contents( PIWIK_DOCUMENT_ROOT . '/libs/jquery/jquery.js', false, NULL, 0, 512 );
- $this->assertTrue( (boolean)preg_match('/jQuery (?:JavaScript Library )?v?([0-9.]+)/', $jqueryJs, $matches) );
- $this->assertEquals( Piwik_Config::getInstance()->General['jquery_version'], $matches[1] );
+ $jqueryJs = file_get_contents(PIWIK_DOCUMENT_ROOT . '/libs/jquery/jquery.js', false, NULL, 0, 512);
+ $this->assertTrue((boolean)preg_match('/jQuery (?:JavaScript Library )?v?([0-9.]+)/', $jqueryJs, $matches));
+ $this->assertEquals(Piwik_Config::getInstance()->General['jquery_version'], $matches[1]);
- $jqueryuiJs = file_get_contents( PIWIK_DOCUMENT_ROOT . '/libs/jquery/jquery-ui.js', false, NULL, 0, 512 );
- $this->assertTrue( (boolean)preg_match('/jQuery UI (?:- v)?([0-9.]+)/', $jqueryuiJs, $matches) );
- $this->assertEquals( Piwik_Config::getInstance()->General['jqueryui_version'], $matches[1] );
+ $jqueryuiJs = file_get_contents(PIWIK_DOCUMENT_ROOT . '/libs/jquery/jquery-ui.js', false, NULL, 0, 512);
+ $this->assertTrue((boolean)preg_match('/jQuery UI (?:- v)?([0-9.]+)/', $jqueryuiJs, $matches));
+ $this->assertEquals(Piwik_Config::getInstance()->General['jqueryui_version'], $matches[1]);
}
@@ -141,46 +137,40 @@ class ReleaseCheckListTest extends PHPUnit_Framework_TestCase
*/
public function testSvnEolStyle()
{
- if(Piwik_Common::isWindows()) {
+ if (Piwik_Common::isWindows()) {
// SVN native does not make this work on windows
return;
}
- foreach(Piwik::globr(PIWIK_DOCUMENT_ROOT, '*') as $file)
- {
+ foreach (Piwik::globr(PIWIK_DOCUMENT_ROOT, '*') as $file) {
// skip files in these folders
- if(strpos($file, '/.git/') !== false ||
+ if (strpos($file, '/.git/') !== false ||
strpos($file, '/documentation/') !== false ||
strpos($file, '/tests/') !== false ||
strpos($file, '/lang/') !== false ||
strpos($file, 'yuicompressor') !== false ||
- strpos($file, '/tmp/') !== false)
- {
+ strpos($file, '/tmp/') !== false
+ ) {
continue;
}
// skip files with these file extensions
- if(preg_match('/\.(bmp|fdf|gif|deflate|gz|ico|jar|jpg|p12|pdf|png|rar|swf|vsd|z|zip|ttf|so|dat|eps)$/', $file))
- {
+ if (preg_match('/\.(bmp|fdf|gif|deflate|gz|ico|jar|jpg|p12|pdf|png|rar|swf|vsd|z|zip|ttf|so|dat|eps)$/', $file)) {
continue;
}
- if(!is_dir($file))
- {
+ if (!is_dir($file)) {
$contents = file_get_contents($file);
// expect CRLF
- if(preg_match('/\.(bat|ps1)$/', $file))
- {
+ if (preg_match('/\.(bat|ps1)$/', $file)) {
$contents = str_replace("\r\n", '', $contents);
- $this->assertTrue(strpos($contents, "\n") === false, 'Incorrect line endings in '.$file);
- }
- else
- {
- // expect native
- $hasWindowsEOL = strpos($contents, "\r\n");
+ $this->assertTrue(strpos($contents, "\n") === false, 'Incorrect line endings in ' . $file);
+ } else {
+ // expect native
+ $hasWindowsEOL = strpos($contents, "\r\n");
- // overwrite translations files with incorrect line endings
- $this->assertTrue($hasWindowsEOL === false, 'Incorrect line endings \r\n found in '.$file);
+ // overwrite translations files with incorrect line endings
+ $this->assertTrue($hasWindowsEOL === false, 'Incorrect line endings \r\n found in ' . $file);
}
}
}
@@ -195,11 +185,11 @@ class ReleaseCheckListTest extends PHPUnit_Framework_TestCase
// check source against Snort rule 8443
// @see http://dev.piwik.org/trac/ticket/2203
$pattern = '/\x5b\x5c{2}.*\x5c{2}[\x22\x27]/';
- $contents = file_get_contents( PIWIK_DOCUMENT_ROOT . '/js/piwik.js' );
+ $contents = file_get_contents(PIWIK_DOCUMENT_ROOT . '/js/piwik.js');
- $this->assertTrue( preg_match($pattern, $contents) == 0 );
+ $this->assertTrue(preg_match($pattern, $contents) == 0);
- $contents = file_get_contents( PIWIK_DOCUMENT_ROOT . '/piwik.js' );
- $this->assertTrue( preg_match($pattern, $contents) == 0 );
+ $contents = file_get_contents(PIWIK_DOCUMENT_ROOT . '/piwik.js');
+ $this->assertTrue(preg_match($pattern, $contents) == 0);
}
}
diff --git a/tests/PHPUnit/Core/ScheduledTaskTest.php b/tests/PHPUnit/Core/ScheduledTaskTest.php
index 69b4329650..b58c4df6fa 100644
--- a/tests/PHPUnit/Core/ScheduledTaskTest.php
+++ b/tests/PHPUnit/Core/ScheduledTaskTest.php
@@ -9,37 +9,37 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/PDFReports/PDFReports.php';
class ScheduledTaskTest extends PHPUnit_Framework_TestCase
{
- /**
- * @group Core
- * @group ScheduledTask
- */
- public function testGetClassName()
- {
- $scheduledTask = new Piwik_ScheduledTask ( new Piwik_PDFReports(), null, null, null );
- $this->assertEquals('Piwik_PDFReports', $scheduledTask->getClassName());
- }
+ /**
+ * @group Core
+ * @group ScheduledTask
+ */
+ public function testGetClassName()
+ {
+ $scheduledTask = new Piwik_ScheduledTask (new Piwik_PDFReports(), null, null, null);
+ $this->assertEquals('Piwik_PDFReports', $scheduledTask->getClassName());
+ }
- /**
- * Dataprovider for testGetTaskName
- */
- public function getTaskNameTestCases()
- {
- return array(
- array('Piwik_CoreAdminHome.purgeOutdatedArchives', 'Piwik_CoreAdminHome', 'purgeOutdatedArchives', null),
- array('Piwik_CoreAdminHome.purgeOutdatedArchives_previous30', 'Piwik_CoreAdminHome', 'purgeOutdatedArchives', 'previous30'),
- array('Piwik_PDFReports.weeklySchedule', 'Piwik_PDFReports', 'weeklySchedule', null),
- array('Piwik_PDFReports.weeklySchedule_1', 'Piwik_PDFReports', 'weeklySchedule', 1),
- );
- }
+ /**
+ * Dataprovider for testGetTaskName
+ */
+ public function getTaskNameTestCases()
+ {
+ return array(
+ array('Piwik_CoreAdminHome.purgeOutdatedArchives', 'Piwik_CoreAdminHome', 'purgeOutdatedArchives', null),
+ array('Piwik_CoreAdminHome.purgeOutdatedArchives_previous30', 'Piwik_CoreAdminHome', 'purgeOutdatedArchives', 'previous30'),
+ array('Piwik_PDFReports.weeklySchedule', 'Piwik_PDFReports', 'weeklySchedule', null),
+ array('Piwik_PDFReports.weeklySchedule_1', 'Piwik_PDFReports', 'weeklySchedule', 1),
+ );
+ }
- /**
- * @group Core
- * @group ScheduledTask
- * @dataProvider getTaskNameTestCases
- */
- public function testGetTaskName($expectedTaskName, $className, $methodName, $methodParameter)
- {
- $this->assertEquals($expectedTaskName, Piwik_ScheduledTask::getTaskName($className, $methodName, $methodParameter));
- }
+ /**
+ * @group Core
+ * @group ScheduledTask
+ * @dataProvider getTaskNameTestCases
+ */
+ public function testGetTaskName($expectedTaskName, $className, $methodName, $methodParameter)
+ {
+ $this->assertEquals($expectedTaskName, Piwik_ScheduledTask::getTaskName($className, $methodName, $methodParameter));
+ }
}
diff --git a/tests/PHPUnit/Core/ScheduledTime/DailyTest.php b/tests/PHPUnit/Core/ScheduledTime/DailyTest.php
index 6fabb515a8..982e19cd12 100644
--- a/tests/PHPUnit/Core/ScheduledTime/DailyTest.php
+++ b/tests/PHPUnit/Core/ScheduledTime/DailyTest.php
@@ -16,13 +16,13 @@ class ScheduledTime_DailyTest extends PHPUnit_Framework_TestCase
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
- self::$_JANUARY_01_1971_09_00_00 = mktime(9,00,00,1,1,1971);
- self::$_JANUARY_01_1971_09_10_00 = mktime(9,10,00,1,1,1971);
- self::$_JANUARY_01_1971_12_10_00 = mktime(12,10,00,1,1,1971);
- self::$_JANUARY_02_1971_00_00_00 = mktime(0,00,00,1,2,1971);
- self::$_JANUARY_02_1971_09_00_00 = mktime(9,00,00,1,2,1971);
+ self::$_JANUARY_01_1971_09_00_00 = mktime(9, 00, 00, 1, 1, 1971);
+ self::$_JANUARY_01_1971_09_10_00 = mktime(9, 10, 00, 1, 1, 1971);
+ self::$_JANUARY_01_1971_12_10_00 = mktime(12, 10, 00, 1, 1, 1971);
+ self::$_JANUARY_02_1971_00_00_00 = mktime(0, 00, 00, 1, 2, 1971);
+ self::$_JANUARY_02_1971_09_00_00 = mktime(9, 00, 00, 1, 2, 1971);
}
-
+
/**
* Tests invalid call to setHour on Piwik_ScheduledTime_Daily
* @group Core
@@ -31,8 +31,7 @@ class ScheduledTime_DailyTest extends PHPUnit_Framework_TestCase
*/
public function testSetHourScheduledTimeDailyNegative()
{
- try
- {
+ try {
$dailySchedule = new Piwik_ScheduledTime_Daily();
$dailySchedule->setHour(-1);
@@ -58,7 +57,7 @@ class ScheduledTime_DailyTest extends PHPUnit_Framework_TestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* Tests forbidden call to setDay on Piwik_ScheduledTime_Daily
* @group Core
@@ -75,7 +74,7 @@ class ScheduledTime_DailyTest extends PHPUnit_Framework_TestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* Tests getRescheduledTime on Piwik_ScheduledTime_Daily with unspecified hour
* @group Core
@@ -96,8 +95,8 @@ class ScheduledTime_DailyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Daily', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_01_1971_09_10_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_01_1971_09_10_00));
$this->assertEquals(self::$_JANUARY_02_1971_00_00_00, $mock->getRescheduledTime());
}
@@ -121,8 +120,8 @@ class ScheduledTime_DailyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Daily', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_01_1971_09_00_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_01_1971_09_00_00));
$mock->setHour(9);
$this->assertEquals(self::$_JANUARY_02_1971_09_00_00, $mock->getRescheduledTime());
@@ -138,8 +137,8 @@ class ScheduledTime_DailyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Daily', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_01_1971_12_10_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_01_1971_12_10_00));
$mock->setHour(9);
$this->assertEquals(self::$_JANUARY_02_1971_09_00_00, $mock->getRescheduledTime());
@@ -155,8 +154,8 @@ class ScheduledTime_DailyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Daily', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_01_1971_12_10_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_01_1971_12_10_00));
$mock->setHour(0);
$this->assertEquals(self::$_JANUARY_02_1971_00_00_00, $mock->getRescheduledTime());
}
diff --git a/tests/PHPUnit/Core/ScheduledTime/HourlyTest.php b/tests/PHPUnit/Core/ScheduledTime/HourlyTest.php
index c50bb6a7c5..b0c808fe5e 100644
--- a/tests/PHPUnit/Core/ScheduledTime/HourlyTest.php
+++ b/tests/PHPUnit/Core/ScheduledTime/HourlyTest.php
@@ -14,11 +14,11 @@ class ScheduledTime_HourlyTest extends PHPUnit_Framework_TestCase
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
- self::$_JANUARY_01_1971_09_00_00 = mktime(9,00,00,1,1,1971);
- self::$_JANUARY_01_1971_09_10_00 = mktime(9,10,00,1,1,1971);
- self::$_JANUARY_01_1971_10_00_00 = mktime(10,00,00,1,1,1971);
+ self::$_JANUARY_01_1971_09_00_00 = mktime(9, 00, 00, 1, 1, 1971);
+ self::$_JANUARY_01_1971_09_10_00 = mktime(9, 10, 00, 1, 1, 1971);
+ self::$_JANUARY_01_1971_10_00_00 = mktime(10, 00, 00, 1, 1, 1971);
}
-
+
/**
* Tests forbidden call to setHour on Piwik_ScheduledTime_Hourly
* @group Core
@@ -52,7 +52,7 @@ class ScheduledTime_HourlyTest extends PHPUnit_Framework_TestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* Tests getRescheduledTime on Piwik_ScheduledTime_Hourly
* @group Core
@@ -72,8 +72,8 @@ class ScheduledTime_HourlyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Hourly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_01_1971_09_00_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_01_1971_09_00_00));
$this->assertEquals(self::$_JANUARY_01_1971_10_00_00, $mock->getRescheduledTime());
/*
@@ -87,8 +87,8 @@ class ScheduledTime_HourlyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Hourly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_01_1971_09_10_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_01_1971_09_10_00));
$this->assertEquals(self::$_JANUARY_01_1971_10_00_00, $mock->getRescheduledTime());
}
}
diff --git a/tests/PHPUnit/Core/ScheduledTime/MonthlyTest.php b/tests/PHPUnit/Core/ScheduledTime/MonthlyTest.php
index d24f957062..8bab11da43 100644
--- a/tests/PHPUnit/Core/ScheduledTime/MonthlyTest.php
+++ b/tests/PHPUnit/Core/ScheduledTime/MonthlyTest.php
@@ -20,15 +20,15 @@ class ScheduledTime_MonthlyTest extends PHPUnit_Framework_TestCase
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
- self::$_JANUARY_01_1971_09_00_00 = mktime(9,00,00,1,1,1971);
- self::$_JANUARY_02_1971_09_00_00 = mktime(9,00,00,1,2,1971);
- self::$_JANUARY_05_1971_09_00_00 = mktime(9,00,00,1,5,1971);
- self::$_JANUARY_15_1971_09_00_00 = mktime(9,00,00,1,15,1971);
- self::$_FEBRUARY_01_1971_00_00_00 = mktime(0,00,00,2,1,1971);
- self::$_FEBRUARY_02_1971_00_00_00 = mktime(0,00,00,2,2,1971);
- self::$_FEBRUARY_03_1971_09_00_00 = mktime(0,00,00,2,3,1971);
- self::$_FEBRUARY_21_1971_09_00_00 = mktime(0,00,00,2,21,1971);
- self::$_FEBRUARY_28_1971_00_00_00 = mktime(0,00,00,2,28,1971);
+ self::$_JANUARY_01_1971_09_00_00 = mktime(9, 00, 00, 1, 1, 1971);
+ self::$_JANUARY_02_1971_09_00_00 = mktime(9, 00, 00, 1, 2, 1971);
+ self::$_JANUARY_05_1971_09_00_00 = mktime(9, 00, 00, 1, 5, 1971);
+ self::$_JANUARY_15_1971_09_00_00 = mktime(9, 00, 00, 1, 15, 1971);
+ self::$_FEBRUARY_01_1971_00_00_00 = mktime(0, 00, 00, 2, 1, 1971);
+ self::$_FEBRUARY_02_1971_00_00_00 = mktime(0, 00, 00, 2, 2, 1971);
+ self::$_FEBRUARY_03_1971_09_00_00 = mktime(0, 00, 00, 2, 3, 1971);
+ self::$_FEBRUARY_21_1971_09_00_00 = mktime(0, 00, 00, 2, 21, 1971);
+ self::$_FEBRUARY_28_1971_00_00_00 = mktime(0, 00, 00, 2, 28, 1971);
}
/**
@@ -120,10 +120,10 @@ class ScheduledTime_MonthlyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Monthly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_01_1971_09_00_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_01_1971_09_00_00));
$this->assertEquals(self::$_FEBRUARY_01_1971_00_00_00, $mock->getRescheduledTime());
-
+
/*
* Test 2
*
@@ -137,12 +137,12 @@ class ScheduledTime_MonthlyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Monthly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_05_1971_09_00_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_05_1971_09_00_00));
$this->assertEquals(self::$_FEBRUARY_01_1971_00_00_00, $mock->getRescheduledTime());
}
-
+
/**
* Tests getRescheduledTime on Piwik_ScheduledTime_Monthly with unspecified hour and specified day
* @group Core
@@ -155,8 +155,8 @@ class ScheduledTime_MonthlyTest extends PHPUnit_Framework_TestCase
{
$mock = $this->getMock('Piwik_ScheduledTime_Monthly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$$currentTime));
+ ->method('getTime')
+ ->will($this->returnValue(self::$$currentTime));
$mock->setDay($day);
$this->assertEquals(self::$$expected, $mock->getRescheduledTime());
}
@@ -165,7 +165,8 @@ class ScheduledTime_MonthlyTest extends PHPUnit_Framework_TestCase
* DataProvider for testGetRescheduledTimeMonthlyUnspecifiedHourSpecifiedDay
* @return array
*/
- public function getSpecifiedDayData() {
+ public function getSpecifiedDayData()
+ {
return array(
/*
* Test 1
@@ -214,7 +215,7 @@ class ScheduledTime_MonthlyTest extends PHPUnit_Framework_TestCase
* Expected :
* getRescheduledTime returns Sunday February 28 1971 00:00:00 UTC
*/
- array('_JANUARY_15_1971_09_00_00', 31, '_FEBRUARY_28_1971_00_00_00')
+ array('_JANUARY_15_1971_09_00_00', 31, '_FEBRUARY_28_1971_00_00_00')
);
}
@@ -227,8 +228,8 @@ class ScheduledTime_MonthlyTest extends PHPUnit_Framework_TestCase
{
$mock = $this->getMock('Piwik_ScheduledTime_Monthly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_15_1971_09_00_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_15_1971_09_00_00));
$mock->setDayOfWeek(3, 0); // first wednesday
$this->assertEquals(self::$_FEBRUARY_03_1971_09_00_00, $mock->getRescheduledTime());
@@ -247,8 +248,8 @@ class ScheduledTime_MonthlyTest extends PHPUnit_Framework_TestCase
{
$mock = $this->getMock('Piwik_ScheduledTime_Monthly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_15_1971_09_00_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_15_1971_09_00_00));
try {
$mock->setDayOfWeek($day, $week);
} catch (Exception $e) {
@@ -261,7 +262,8 @@ class ScheduledTime_MonthlyTest extends PHPUnit_Framework_TestCase
* DataProvider for testMonthlyDayOfWeekInvalid
* @return array
*/
- public function getInvalidDayOfWeekData() {
+ public function getInvalidDayOfWeekData()
+ {
return array(
array(-4, 0),
array(8, 0),
diff --git a/tests/PHPUnit/Core/ScheduledTime/WeeklyTest.php b/tests/PHPUnit/Core/ScheduledTime/WeeklyTest.php
index b5738fb5c4..6812bc90c0 100644
--- a/tests/PHPUnit/Core/ScheduledTime/WeeklyTest.php
+++ b/tests/PHPUnit/Core/ScheduledTime/WeeklyTest.php
@@ -17,14 +17,14 @@ class ScheduledTime_WeeklyTest extends PHPUnit_Framework_TestCase
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
- self::$_JANUARY_01_1971_09_10_00 = mktime(9,10,00,1,1,1971);
- self::$_JANUARY_04_1971_00_00_00 = mktime(0,00,00,1,4,1971);
- self::$_JANUARY_04_1971_09_00_00 = mktime(9,00,00,1,4,1971);
- self::$_JANUARY_05_1971_09_00_00 = mktime(9,00,00,1,5,1971);
- self::$_JANUARY_11_1971_00_00_00 = mktime(0,00,00,1,11,1971);
- self::$_JANUARY_15_1971_00_00_00 = mktime(0,00,00,1,15,1971);
+ self::$_JANUARY_01_1971_09_10_00 = mktime(9, 10, 00, 1, 1, 1971);
+ self::$_JANUARY_04_1971_00_00_00 = mktime(0, 00, 00, 1, 4, 1971);
+ self::$_JANUARY_04_1971_09_00_00 = mktime(9, 00, 00, 1, 4, 1971);
+ self::$_JANUARY_05_1971_09_00_00 = mktime(9, 00, 00, 1, 5, 1971);
+ self::$_JANUARY_11_1971_00_00_00 = mktime(0, 00, 00, 1, 11, 1971);
+ self::$_JANUARY_15_1971_00_00_00 = mktime(0, 00, 00, 1, 15, 1971);
}
-
+
/**
* Tests invalid call to setHour on Piwik_ScheduledTime_Weekly
* @group Core
@@ -58,7 +58,7 @@ class ScheduledTime_WeeklyTest extends PHPUnit_Framework_TestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* Tests invalid call to setDay on Piwik_ScheduledTime_Weekly
* @group Core
@@ -114,11 +114,11 @@ class ScheduledTime_WeeklyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Weekly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_01_1971_09_10_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_01_1971_09_10_00));
$this->assertEquals(self::$_JANUARY_04_1971_00_00_00, $mock->getRescheduledTime());
}
-
+
/**
* Tests getRescheduledTime on Piwik_ScheduledTime_Weekly with specified hour and unspecified day
* @group Core
@@ -140,8 +140,8 @@ class ScheduledTime_WeeklyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Weekly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_01_1971_09_10_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_01_1971_09_10_00));
$mock->setHour(9);
$this->assertEquals(self::$_JANUARY_04_1971_09_00_00, $mock->getRescheduledTime());
}
@@ -167,11 +167,11 @@ class ScheduledTime_WeeklyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Weekly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_04_1971_09_00_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_04_1971_09_00_00));
$mock->setDay(1);
$this->assertEquals(self::$_JANUARY_11_1971_00_00_00, $mock->getRescheduledTime());
-
+
/*
* Test 2
*
@@ -185,8 +185,8 @@ class ScheduledTime_WeeklyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Weekly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_05_1971_09_00_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_05_1971_09_00_00));
$mock->setDay(1);
$this->assertEquals(self::$_JANUARY_11_1971_00_00_00, $mock->getRescheduledTime());
@@ -203,8 +203,8 @@ class ScheduledTime_WeeklyTest extends PHPUnit_Framework_TestCase
*/
$mock = $this->getMock('Piwik_ScheduledTime_Weekly', array('getTime'));
$mock->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue(self::$_JANUARY_04_1971_09_00_00));
+ ->method('getTime')
+ ->will($this->returnValue(self::$_JANUARY_04_1971_09_00_00));
$mock->setDay(5);
$this->assertEquals(self::$_JANUARY_15_1971_00_00_00, $mock->getRescheduledTime());
}
diff --git a/tests/PHPUnit/Core/SegmentExpressionTest.php b/tests/PHPUnit/Core/SegmentExpressionTest.php
index 62f87ee421..ea53e5d049 100644
--- a/tests/PHPUnit/Core/SegmentExpressionTest.php
+++ b/tests/PHPUnit/Core/SegmentExpressionTest.php
@@ -60,7 +60,7 @@ class SegmentExpressionTest extends PHPUnit_Framework_TestCase
return array(
array('A==B%', array('where' => " A = ? ", 'bind' => array('B%'))),
array('ABCDEF====B===', array('where' => " ABCDEF = ? ", 'bind' => array('==B==='))),
- array('A===B;CDEF!=C!=', array('where' => " A = ? AND CDEF <> ? ", 'bind' => array('=B', 'C!=' ))),
+ array('A===B;CDEF!=C!=', array('where' => " A = ? AND CDEF <> ? ", 'bind' => array('=B', 'C!='))),
array('A==B,C==D', array('where' => " (A = ? OR C = ? )", 'bind' => array('B', 'D'))),
array('A!=B;C==D', array('where' => " A <> ? AND C = ? ", 'bind' => array('B', 'D'))),
array('A!=B;C==D,E!=Hello World!=', array('where' => " A <> ? AND (C = ? OR E <> ? )", 'bind' => array('B', 'D', 'Hello World!='))),
@@ -70,7 +70,7 @@ class SegmentExpressionTest extends PHPUnit_Framework_TestCase
array('A<=B', array('where' => " A <= ? ", 'bind' => array('B'))),
array('A>=B', array('where' => " A >= ? ", 'bind' => array('B'))),
array('ABCDEF>=>=>=B===', array('where' => " ABCDEF >= ? ", 'bind' => array('>=>=B==='))),
- array('A>=<=B;CDEF>G;H>=I;J<K;L<=M', array('where' => " A >= ? AND CDEF > ? AND H >= ? AND J < ? AND L <= ? ", 'bind' => array('<=B', 'G','I','K','M' ))),
+ array('A>=<=B;CDEF>G;H>=I;J<K;L<=M', array('where' => " A >= ? AND CDEF > ? AND H >= ? AND J < ? AND L <= ? ", 'bind' => array('<=B', 'G', 'I', 'K', 'M'))),
array('A>=B;C>=D,E<w_ow great!', array('where' => " A >= ? AND (C >= ? OR E < ? )", 'bind' => array('B', 'D', 'w_ow great!'))),
array('A=@B_', array('where' => " A LIKE ? ", 'bind' => array('%B\_%'))),
diff --git a/tests/PHPUnit/Core/SegmentTest.php b/tests/PHPUnit/Core/SegmentTest.php
index 4b9249597b..2d021ea65c 100644
--- a/tests/PHPUnit/Core/SegmentTest.php
+++ b/tests/PHPUnit/Core/SegmentTest.php
@@ -15,10 +15,10 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$pseudoMockAccess = new FakeAccess;
FakeAccess::$superUser = true;
Zend_Registry::set('access', $pseudoMockAccess);
-
+
// Load and install plugins
$pluginsManager = Piwik_PluginsManager::getInstance();
- $pluginsManager->loadPlugins( Piwik_Config::getInstance()->Plugins['Plugins'] );
+ $pluginsManager->loadPlugins(Piwik_Config::getInstance()->Plugins['Plugins']);
}
public function tearDown()
@@ -29,8 +29,8 @@ class SegmentTest extends PHPUnit_Framework_TestCase
protected function _filterWhitsSpaces($valueToFilter)
{
- if(is_array($valueToFilter)) {
- foreach($valueToFilter AS $key => $value) {
+ if (is_array($valueToFilter)) {
+ foreach ($valueToFilter AS $key => $value) {
$valueToFilter[$key] = $this->_filterWhitsSpaces($value);
}
return $valueToFilter;
@@ -46,23 +46,23 @@ class SegmentTest extends PHPUnit_Framework_TestCase
// Normal segment
array('country==France', array(
'where' => ' log_visit.location_country = ? ',
- 'bind' => array('France'))),
+ 'bind' => array('France'))),
// unescape the comma please
array('country==a\,==', array(
'where' => ' log_visit.location_country = ? ',
- 'bind' => array('a,=='))),
+ 'bind' => array('a,=='))),
// AND, with 2 values rewrites
array('country==a;visitorType!=returning;visitorType==new', array(
'where' => ' log_visit.location_country = ? AND log_visit.visitor_returning <> ? AND log_visit.visitor_returning = ? ',
- 'bind' => array('a', '1', '0'))),
+ 'bind' => array('a', '1', '0'))),
// OR, with 2 value rewrites
array('referrerType==search,referrerType==direct', array(
- 'where'=>' (log_visit.referer_type = ? OR log_visit.referer_type = ? )',
- 'bind' => array(Piwik_Common::REFERER_TYPE_SEARCH_ENGINE,
- Piwik_Common::REFERER_TYPE_DIRECT_ENTRY))),
+ 'where' => ' (log_visit.referer_type = ? OR log_visit.referer_type = ? )',
+ 'bind' => array(Piwik_Common::REFERER_TYPE_SEARCH_ENGINE,
+ Piwik_Common::REFERER_TYPE_DIRECT_ENTRY))),
);
}
@@ -77,13 +77,13 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_visit';
$expected = array(
- 'sql' => '
+ 'sql' => '
SELECT
log_visit.idvisit
FROM
- '.Piwik_Common::prefixTable('log_visit').' AS log_visit
+ ' . Piwik_Common::prefixTable('log_visit') . ' AS log_visit
WHERE
- '.$expected['where'],
+ ' . $expected['where'],
'bind' => $expected['bind']
);
@@ -98,7 +98,7 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$this->assertEquals(32, strlen($segment->getHash()));
}
-
+
/**
* @group Core
* @group Segment
@@ -109,27 +109,27 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_visit';
$where = 'idsite = ?';
$bind = array(1);
-
+
$segment = 'customVariableName1==Test;visitorType==new';
$segment = new Piwik_Segment($segment, $idSites = array());
-
+
$query = $segment->getSelectQuery($select, $from, $where, $bind);
-
+
$expected = array(
- "sql" => "
+ "sql" => "
SELECT
*
FROM
- ".Piwik_Common::prefixTable('log_visit')." AS log_visit
+ " . Piwik_Common::prefixTable('log_visit') . " AS log_visit
WHERE
( idsite = ? )
AND
( log_visit.custom_var_k1 = ? AND log_visit.visitor_returning = ? )",
"bind" => array(1, 'Test', 0));
-
+
$this->assertEquals($this->_filterWhitsSpaces($expected), $this->_filterWhitsSpaces($query));
}
-
+
/**
* @group Core
* @group Segment
@@ -140,25 +140,25 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_link_visit_action';
$where = 'log_link_visit_action.idvisit = ?';
$bind = array(1);
-
+
$segment = 'customVariablePageName1==Test;visitorType==new';
$segment = new Piwik_Segment($segment, $idSites = array());
-
+
$query = $segment->getSelectQuery($select, $from, $where, $bind);
-
+
$expected = array(
- "sql" => "
+ "sql" => "
SELECT
*
FROM
- ".Piwik_Common::prefixTable('log_link_visit_action')." AS log_link_visit_action
- LEFT JOIN ".Piwik_Common::prefixTable('log_visit')." AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit
+ " . Piwik_Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action
+ LEFT JOIN " . Piwik_Common::prefixTable('log_visit') . " AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit
WHERE
( log_link_visit_action.idvisit = ? )
AND
( log_link_visit_action.custom_var_k1 = ? AND log_visit.visitor_returning = ? )",
"bind" => array(1, 'Test', 0));
-
+
$this->assertEquals($this->_filterWhitsSpaces($expected), $this->_filterWhitsSpaces($query));
}
@@ -172,14 +172,14 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_visit';
$where = 'log_visit.idvisit = ?';
$bind = array(1);
-
+
$segment = 'customVariablePageName1==Test;visitorType==new';
$segment = new Piwik_Segment($segment, $idSites = array());
-
+
$query = $segment->getSelectQuery($select, $from, $where, $bind);
-
+
$expected = array(
- "sql" => "
+ "sql" => "
SELECT
sum(log_inner.visit_total_actions) as nb_actions, max(log_inner.visit_total_actions) as max_actions, sum(log_inner.visit_total_time) as sum_visit_length
FROM
@@ -188,8 +188,8 @@ class SegmentTest extends PHPUnit_Framework_TestCase
log_visit.visit_total_actions,
log_visit.visit_total_time
FROM
- ".Piwik_Common::prefixTable('log_visit')." AS log_visit
- LEFT JOIN ".Piwik_Common::prefixTable('log_link_visit_action')." AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
+ " . Piwik_Common::prefixTable('log_visit') . " AS log_visit
+ LEFT JOIN " . Piwik_Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
WHERE
( log_visit.idvisit = ? )
AND
@@ -197,10 +197,10 @@ class SegmentTest extends PHPUnit_Framework_TestCase
GROUP BY log_visit.idvisit
) AS log_inner",
"bind" => array(1, 'Test', 0));
-
+
$this->assertEquals($this->_filterWhitsSpaces($expected), $this->_filterWhitsSpaces($query));
}
-
+
/**
* @group Core
* @group Segment
@@ -211,28 +211,28 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_link_visit_action';
$where = 'log_link_visit_action.idvisit = ?';
$bind = array(1);
-
+
$segment = 'customVariablePageName1==Test;visitConvertedGoalId==1;customVariablePageName2==Test2';
$segment = new Piwik_Segment($segment, $idSites = array());
-
+
$query = $segment->getSelectQuery($select, $from, $where, $bind);
-
+
$expected = array(
- "sql" => "
+ "sql" => "
SELECT
*
FROM
- ".Piwik_Common::prefixTable('log_link_visit_action')." AS log_link_visit_action
- LEFT JOIN ".Piwik_Common::prefixTable('log_conversion')." AS log_conversion ON log_conversion.idlink_va = log_link_visit_action.idlink_va AND log_conversion.idsite = log_link_visit_action.idsite
+ " . Piwik_Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action
+ LEFT JOIN " . Piwik_Common::prefixTable('log_conversion') . " AS log_conversion ON log_conversion.idlink_va = log_link_visit_action.idlink_va AND log_conversion.idsite = log_link_visit_action.idsite
WHERE
( log_link_visit_action.idvisit = ? )
AND
( log_link_visit_action.custom_var_k1 = ? AND log_conversion.idgoal = ? AND log_link_visit_action.custom_var_k2 = ? )",
"bind" => array(1, 'Test', 1, 'Test2'));
-
+
$this->assertEquals($this->_filterWhitsSpaces($expected), $this->_filterWhitsSpaces($query));
}
-
+
/**
* @group Core
* @group Segment
@@ -243,28 +243,28 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_conversion';
$where = 'log_conversion.idvisit = ?';
$bind = array(1);
-
+
$segment = 'visitConvertedGoalId!=2;customVariablePageName1==Test;visitConvertedGoalId==1';
$segment = new Piwik_Segment($segment, $idSites = array());
-
+
$query = $segment->getSelectQuery($select, $from, $where, $bind);
-
+
$expected = array(
- "sql" => "
+ "sql" => "
SELECT
*
FROM
- ".Piwik_Common::prefixTable('log_conversion')." AS log_conversion
- LEFT JOIN ".Piwik_Common::prefixTable('log_link_visit_action')." AS log_link_visit_action ON log_conversion.idlink_va = log_link_visit_action.idlink_va
+ " . Piwik_Common::prefixTable('log_conversion') . " AS log_conversion
+ LEFT JOIN " . Piwik_Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_conversion.idlink_va = log_link_visit_action.idlink_va
WHERE
( log_conversion.idvisit = ? )
AND
( log_conversion.idgoal <> ? AND log_link_visit_action.custom_var_k1 = ? AND log_conversion.idgoal = ? )",
"bind" => array(1, 2, 'Test', 1));
-
+
$this->assertEquals($this->_filterWhitsSpaces($expected), $this->_filterWhitsSpaces($query));
}
-
+
/**
* @group Core
* @group Segment
@@ -275,14 +275,14 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_visit';
$where = 'log_visit.idvisit = ?';
$bind = array(1);
-
+
$segment = 'visitConvertedGoalId==1';
$segment = new Piwik_Segment($segment, $idSites = array());
-
+
$query = $segment->getSelectQuery($select, $from, $where, $bind);
-
+
$expected = array(
- "sql" => "
+ "sql" => "
SELECT
log_inner.*
FROM
@@ -290,8 +290,8 @@ class SegmentTest extends PHPUnit_Framework_TestCase
SELECT
log_visit.*
FROM
- ".Piwik_Common::prefixTable('log_visit')." AS log_visit
- LEFT JOIN ".Piwik_Common::prefixTable('log_conversion')." AS log_conversion ON log_conversion.idvisit = log_visit.idvisit
+ " . Piwik_Common::prefixTable('log_visit') . " AS log_visit
+ LEFT JOIN " . Piwik_Common::prefixTable('log_conversion') . " AS log_conversion ON log_conversion.idvisit = log_visit.idvisit
WHERE
( log_visit.idvisit = ? )
AND
@@ -299,10 +299,10 @@ class SegmentTest extends PHPUnit_Framework_TestCase
GROUP BY log_visit.idvisit
) AS log_inner",
"bind" => array(1, 1));
-
+
$this->assertEquals($this->_filterWhitsSpaces($expected), $this->_filterWhitsSpaces($query));
}
-
+
/**
* @group Core
* @group Segemnt
@@ -313,27 +313,27 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_conversion';
$where = 'log_conversion.idvisit = ?';
$bind = array(1);
-
+
$segment = 'visitConvertedGoalId==1';
$segment = new Piwik_Segment($segment, $idSites = array());
-
+
$query = $segment->getSelectQuery($select, $from, $where, $bind);
-
+
$expected = array(
- "sql" => "
+ "sql" => "
SELECT
log_conversion.*
FROM
- ".Piwik_Common::prefixTable('log_conversion')." AS log_conversion
+ " . Piwik_Common::prefixTable('log_conversion') . " AS log_conversion
WHERE
( log_conversion.idvisit = ? )
AND
( log_conversion.idgoal = ? )",
"bind" => array(1, 1));
-
+
$this->assertEquals($this->_filterWhitsSpaces($expected), $this->_filterWhitsSpaces($query));
}
-
+
/**
* @group Core
* @group Segment
@@ -344,32 +344,32 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_conversion';
$where = 'log_conversion.idvisit = ?';
$bind = array(1);
-
+
$segment = 'visitConvertedGoalId==1,visitServerHour==12';
$segment = new Piwik_Segment($segment, $idSites = array());
-
+
$query = $segment->getSelectQuery($select, $from, $where, $bind);
-
+
$expected = array(
- "sql" => "
+ "sql" => "
SELECT
*
FROM
- ".Piwik_Common::prefixTable('log_conversion')." AS log_conversion
- LEFT JOIN ".Piwik_Common::prefixTable('log_visit')." AS log_visit ON log_conversion.idvisit = log_visit.idvisit
+ " . Piwik_Common::prefixTable('log_conversion') . " AS log_conversion
+ LEFT JOIN " . Piwik_Common::prefixTable('log_visit') . " AS log_visit ON log_conversion.idvisit = log_visit.idvisit
WHERE
( log_conversion.idvisit = ? )
AND
( (log_conversion.idgoal = ? OR HOUR(log_visit.visit_last_action_time) = ? ))",
"bind" => array(1, 1, 12));
-
+
$this->assertEquals($this->_filterWhitsSpaces($expected), $this->_filterWhitsSpaces($query));
}
-
+
/**
* visit is joined on action, then conversion is joined
* make sure that conversion is joined on action not visit
- *
+ *
* @group Core
* @group Segment
*/
@@ -379,31 +379,31 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_link_visit_action';
$where = false;
$bind = array();
-
+
$segment = 'visitServerHour==12;visitConvertedGoalId==1';
$segment = new Piwik_Segment($segment, $idSites = array());
-
+
$query = $segment->getSelectQuery($select, $from, $where, $bind);
-
+
$expected = array(
- "sql" => "
+ "sql" => "
SELECT
*
FROM
- ".Piwik_Common::prefixTable('log_link_visit_action')." AS log_link_visit_action
- LEFT JOIN ".Piwik_Common::prefixTable('log_visit')." AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit
- LEFT JOIN ".Piwik_Common::prefixTable('log_conversion')." AS log_conversion ON log_conversion.idlink_va = log_link_visit_action.idlink_va AND log_conversion.idsite = log_link_visit_action.idsite
+ " . Piwik_Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action
+ LEFT JOIN " . Piwik_Common::prefixTable('log_visit') . " AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit
+ LEFT JOIN " . Piwik_Common::prefixTable('log_conversion') . " AS log_conversion ON log_conversion.idlink_va = log_link_visit_action.idlink_va AND log_conversion.idsite = log_link_visit_action.idsite
WHERE
HOUR(log_visit.visit_last_action_time) = ? AND log_conversion.idgoal = ? ",
"bind" => array(12, 1));
-
+
$this->assertEquals($this->_filterWhitsSpaces($expected), $this->_filterWhitsSpaces($query));
}
-
+
/**
* join conversion on visit, then actions
* make sure actions are joined before conversions
- *
+ *
* @group Core
* @group Segment
*/
@@ -413,14 +413,14 @@ class SegmentTest extends PHPUnit_Framework_TestCase
$from = 'log_visit';
$where = false;
$bind = array();
-
+
$segment = 'visitConvertedGoalId==1;visitServerHour==12;customVariablePageName1==Test';
$segment = new Piwik_Segment($segment, $idSites = array());
-
+
$query = $segment->getSelectQuery($select, $from, $where, $bind);
-
+
$expected = array(
- "sql" => "
+ "sql" => "
SELECT
log_inner.*
FROM
@@ -428,15 +428,15 @@ class SegmentTest extends PHPUnit_Framework_TestCase
SELECT
log_visit.*
FROM
- ".Piwik_Common::prefixTable('log_visit')." AS log_visit
- LEFT JOIN ".Piwik_Common::prefixTable('log_link_visit_action')." AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
- LEFT JOIN ".Piwik_Common::prefixTable('log_conversion')." AS log_conversion ON log_conversion.idlink_va = log_link_visit_action.idlink_va AND log_conversion.idsite = log_link_visit_action.idsite
+ " . Piwik_Common::prefixTable('log_visit') . " AS log_visit
+ LEFT JOIN " . Piwik_Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
+ LEFT JOIN " . Piwik_Common::prefixTable('log_conversion') . " AS log_conversion ON log_conversion.idlink_va = log_link_visit_action.idlink_va AND log_conversion.idsite = log_link_visit_action.idsite
WHERE
log_conversion.idgoal = ? AND HOUR(log_visit.visit_last_action_time) = ? AND log_link_visit_action.custom_var_k1 = ?
GROUP BY log_visit.idvisit
) AS log_inner",
"bind" => array(1, 12, 'Test'));
-
+
$this->assertEquals($this->_filterWhitsSpaces($expected), $this->_filterWhitsSpaces($query));
}
@@ -451,6 +451,7 @@ class SegmentTest extends PHPUnit_Framework_TestCase
array('A=B')
);
}
+
/**
* @group Core
* @group Segment
diff --git a/tests/PHPUnit/Core/ServeStaticFileTest.php b/tests/PHPUnit/Core/ServeStaticFileTest.php
index 8e70d46cc5..253526f3ca 100644
--- a/tests/PHPUnit/Core/ServeStaticFileTest.php
+++ b/tests/PHPUnit/Core/ServeStaticFileTest.php
@@ -388,8 +388,8 @@ class Test_Piwik_ServeStaticFile extends PHPUnit_Framework_TestCase
*/
private function getStaticSrvUrl()
{
- $url = Test_Piwik_BaseFixture::getRootUrl();
- $url .= '/tests/resources/';
+ $url = Test_Piwik_BaseFixture::getRootUrl();
+ $url .= '/tests/resources/';
return $url . "staticFileServer.php?" . FILE_MODE_REQUEST_VAR . "=" . STATIC_SERVER_MODE .
"&" . SRV_MODE_REQUEST_VAR . "=";
diff --git a/tests/PHPUnit/Core/SqlTest.php b/tests/PHPUnit/Core/SqlTest.php
index 48c64b2cf5..14aebbf22c 100755
--- a/tests/PHPUnit/Core/SqlTest.php
+++ b/tests/PHPUnit/Core/SqlTest.php
@@ -7,37 +7,37 @@
*/
class SqlTest extends DatabaseTestCase
{
- public function setUp()
- {
- parent::setUp();
-
- // create two myisam tables
- Piwik_Exec("CREATE TABLE table1 (a INT) ENGINE=MYISAM");
- Piwik_Exec("CREATE TABLE table2 (b INT) ENGINE=MYISAM");
-
- // create two innodb tables
- Piwik_Exec("CREATE TABLE table3 (c INT) ENGINE=InnoDB");
- Piwik_Exec("CREATE TABLE table4 (d INT) ENGINE=InnoDB");
- }
-
- public function tearDown()
- {
- parent::tearDown();
- }
-
+ public function setUp()
+ {
+ parent::setUp();
+
+ // create two myisam tables
+ Piwik_Exec("CREATE TABLE table1 (a INT) ENGINE=MYISAM");
+ Piwik_Exec("CREATE TABLE table2 (b INT) ENGINE=MYISAM");
+
+ // create two innodb tables
+ Piwik_Exec("CREATE TABLE table3 (c INT) ENGINE=InnoDB");
+ Piwik_Exec("CREATE TABLE table4 (d INT) ENGINE=InnoDB");
+ }
+
+ public function tearDown()
+ {
+ parent::tearDown();
+ }
+
/**
* @group Core
* @group Unzip
*/
public function testOptimize()
{
- // make sure optimizing myisam tables works
- $this->assertTrue(Piwik_OptimizeTables(array('table1', 'table2')) !== false);
-
- // make sure optimizing both myisam & innodb results in optimizations
- $this->assertTrue(Piwik_OptimizeTables(array('table1', 'table2', 'table3', 'table4')) !== false);
-
- // make sure innodb tables are skipped
- $this->assertTrue(Piwik_OptimizeTables(array('table3', 'table4')) === false);
+ // make sure optimizing myisam tables works
+ $this->assertTrue(Piwik_OptimizeTables(array('table1', 'table2')) !== false);
+
+ // make sure optimizing both myisam & innodb results in optimizations
+ $this->assertTrue(Piwik_OptimizeTables(array('table1', 'table2', 'table3', 'table4')) !== false);
+
+ // make sure innodb tables are skipped
+ $this->assertTrue(Piwik_OptimizeTables(array('table3', 'table4')) === false);
}
}
diff --git a/tests/PHPUnit/Core/TablePartitioningTest.php b/tests/PHPUnit/Core/TablePartitioningTest.php
index b6f89574a6..9626b7aceb 100644
--- a/tests/PHPUnit/Core/TablePartitioningTest.php
+++ b/tests/PHPUnit/Core/TablePartitioningTest.php
@@ -22,7 +22,7 @@ class TablePartitioningTest extends DatabaseTestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* test table absent => create
* @group Core
@@ -30,22 +30,22 @@ class TablePartitioningTest extends DatabaseTestCase
*/
public function testNoTable()
{
- $tableName ='archive_numeric';
+ $tableName = 'archive_numeric';
$p = new Piwik_TablePartitioning_Monthly($tableName);
$timestamp = strtotime("10 September 2000");
$suffixShouldBe = "_2000_09";
$prefixTables = Piwik_Config::getInstance()->database['tables_prefix'];
- $tablename = $prefixTables.$tableName.$suffixShouldBe;
-
- $p->setTimestamp( $timestamp );
-
+ $tablename = $prefixTables . $tableName . $suffixShouldBe;
+
+ $p->setTimestamp($timestamp);
+
$allTablesInstalled = Piwik::getTablesInstalled($forceReload = true);
-
+
$this->assertContains($tablename, $allTablesInstalled);
$this->assertEquals($tablename, $p->getTableName());
$this->assertEquals($tablename, (string)$p->__toString());
}
-
+
/**
* test monthly
* @group Core
@@ -53,21 +53,21 @@ class TablePartitioningTest extends DatabaseTestCase
*/
public function testMonthlyPartition()
{
- $tableName ='archive_numeric';
+ $tableName = 'archive_numeric';
$p = new Piwik_TablePartitioning_Monthly($tableName);
$timestamp = strtotime("10 September 2000");
$suffixShouldBe = "_2000_09";
$prefixTables = Piwik_Config::getInstance()->database['tables_prefix'];
- $tablename = $prefixTables.$tableName.$suffixShouldBe;
-
- $p->setTimestamp( $timestamp );
-
- $allTablesInstalled = Piwik::getTablesInstalled( $forceReload = true );
+ $tablename = $prefixTables . $tableName . $suffixShouldBe;
+
+ $p->setTimestamp($timestamp);
+
+ $allTablesInstalled = Piwik::getTablesInstalled($forceReload = true);
$this->assertContains($tablename, $allTablesInstalled);
$this->assertEquals($tablename, $p->getTableName());
$this->assertEquals($tablename, (string)$p->__toString());
}
-
+
/**
* test daily
* @group Core
@@ -75,15 +75,15 @@ class TablePartitioningTest extends DatabaseTestCase
*/
public function testDailyPartition()
{
- $tableName ='archive_numeric';
+ $tableName = 'archive_numeric';
$p = new Piwik_TablePartitioning_Daily($tableName);
$timestamp = strtotime("10 September 2000");
$suffixShouldBe = "_2000_09_10";
$prefixTables = Piwik_Config::getInstance()->database['tables_prefix'];
- $tablename = $prefixTables.$tableName.$suffixShouldBe;
-
- $p->setTimestamp( $timestamp );
-
+ $tablename = $prefixTables . $tableName . $suffixShouldBe;
+
+ $p->setTimestamp($timestamp);
+
$allTablesInstalled = Piwik::getTablesInstalled();
$this->assertContains($tablename, $allTablesInstalled);
$this->assertEquals($tablename, $p->getTableName());
diff --git a/tests/PHPUnit/Core/TaskSchedulerTest.php b/tests/PHPUnit/Core/TaskSchedulerTest.php
index 81341055d2..a774666e5a 100644
--- a/tests/PHPUnit/Core/TaskSchedulerTest.php
+++ b/tests/PHPUnit/Core/TaskSchedulerTest.php
@@ -7,312 +7,317 @@
*/
class TaskSchedulerTest extends PHPUnit_Framework_TestCase
{
- private static function getTestTimetable()
- {
- return array(
- 'Piwik_CoreAdminHome.purgeOutdatedArchives' => 1355529607,
- 'Piwik_PrivacyManager.deleteReportData_1' => 1322229607,
- );
- }
-
- /**
- * Dataprovider for testGetTimetableFromOptionValue
- */
- public function getTimetableFromOptionValueTestCases()
- {
- return array(
-
- // invalid option values should return a fresh array
- array(array(), false),
- array(array(), null),
- array(array(), 1),
- array(array(), ''),
- array(array(), 'test'),
-
- // valid serialized array
- array(
- array(
- 'Piwik_CoreAdminHome.purgeOutdatedArchives' => 1355529607,
- 'Piwik_PrivacyManager.deleteReportData' => 1355529607,
- ),
- 'a:2:{s:41:"Piwik_CoreAdminHome.purgeOutdatedArchives";i:1355529607;s:37:"Piwik_PrivacyManager.deleteReportData";i:1355529607;}'
- ),
- );
- }
-
- /**
- * @group Core
- * @group TaskScheduler
- * @dataProvider getTimetableFromOptionValueTestCases
- */
- public function testGetTimetableFromOptionValue($expectedTimetable, $option)
- {
- $getTimetableFromOptionValue = new ReflectionMethod(
- 'Piwik_TaskScheduler', 'getTimetableFromOptionValue'
+ private static function getTestTimetable()
+ {
+ return array(
+ 'Piwik_CoreAdminHome.purgeOutdatedArchives' => 1355529607,
+ 'Piwik_PrivacyManager.deleteReportData_1' => 1322229607,
+ );
+ }
+
+ /**
+ * Dataprovider for testGetTimetableFromOptionValue
+ */
+ public function getTimetableFromOptionValueTestCases()
+ {
+ return array(
+
+ // invalid option values should return a fresh array
+ array(array(), false),
+ array(array(), null),
+ array(array(), 1),
+ array(array(), ''),
+ array(array(), 'test'),
+
+ // valid serialized array
+ array(
+ array(
+ 'Piwik_CoreAdminHome.purgeOutdatedArchives' => 1355529607,
+ 'Piwik_PrivacyManager.deleteReportData' => 1355529607,
+ ),
+ 'a:2:{s:41:"Piwik_CoreAdminHome.purgeOutdatedArchives";i:1355529607;s:37:"Piwik_PrivacyManager.deleteReportData";i:1355529607;}'
+ ),
+ );
+ }
+
+ /**
+ * @group Core
+ * @group TaskScheduler
+ * @dataProvider getTimetableFromOptionValueTestCases
+ */
+ public function testGetTimetableFromOptionValue($expectedTimetable, $option)
+ {
+ $getTimetableFromOptionValue = new ReflectionMethod(
+ 'Piwik_TaskScheduler', 'getTimetableFromOptionValue'
);
$getTimetableFromOptionValue->setAccessible(TRUE);
$this->assertEquals($expectedTimetable, $getTimetableFromOptionValue->invoke(new Piwik_TaskScheduler(), $option));
- }
-
- /**
- * Dataprovider for testTaskHasBeenScheduledOnce
- */
- public function taskHasBeenScheduledOnceTestCases()
- {
- $timetable = self::getTestTimetable();
-
- return array(
- array(true, 'Piwik_CoreAdminHome.purgeOutdatedArchives', $timetable),
- array(true, 'Piwik_PrivacyManager.deleteReportData_1', $timetable),
- array(false, 'Piwik_PDFReports.weeklySchedule"', $timetable)
- );
- }
-
- /**
- * @group Core
- * @group TaskScheduler
- * @dataProvider taskHasBeenScheduledOnceTestCases
- */
- public function testTaskHasBeenScheduledOnce($expectedDecision, $taskName, $timetable)
- {
- $taskHasBeenScheduledOnce = new ReflectionMethod(
- 'Piwik_TaskScheduler', 'taskHasBeenScheduledOnce'
- );
- $taskHasBeenScheduledOnce->setAccessible(TRUE);
-
- $this->assertEquals($expectedDecision, $taskHasBeenScheduledOnce->invoke(new Piwik_TaskScheduler(), $taskName, $timetable));
- }
-
- /**
- * Dataprovider for testGetScheduledTimeForMethod
- */
- public function getScheduledTimeForMethodTestCases()
- {
- $timetable = serialize(self::getTestTimetable());
-
- return array(
- array(1355529607, 'Piwik_CoreAdminHome', 'purgeOutdatedArchives', null, $timetable),
- array(1322229607, 'Piwik_PrivacyManager', 'deleteReportData', 1, $timetable),
- array(false, 'Piwik_PDFReports', 'weeklySchedule', null, $timetable)
- );
- }
-
- /**
- * @group Core
- * @group TaskScheduler
- * @dataProvider getScheduledTimeForMethodTestCases
- */
- public function testGetScheduledTimeForMethod($expectedTime, $className, $methodName, $methodParameter, $timetable)
- {
- self::stubPiwikOption($timetable);
-
- $this->assertEquals($expectedTime, Piwik_TaskScheduler::getScheduledTimeForMethod($className, $methodName, $methodParameter));
-
- self::resetPiwikOption();
- }
-
- /**
- * Dataprovider for testTaskShouldBeExecuted
- */
- public function taskShouldBeExecutedTestCases()
- {
- $timetable = self::getTestTimetable();
-
- // set a date in the future (should not run)
- $timetable['Piwik_CoreAdminHome.purgeOutdatedArchives'] = time() + 60000;
-
- // set now (should run)
- $timetable['Piwik_PrivacyManager.deleteReportData_1'] = time();
-
- return array(
- array(false, 'Piwik_CoreAdminHome.purgeOutdatedArchives', $timetable),
- array(true, 'Piwik_PrivacyManager.deleteReportData_1', $timetable),
- array(false, 'Piwik_PDFReports.weeklySchedule"', $timetable)
- );
- }
-
- /**
- * @group Core
- * @group TaskScheduler
- * @dataProvider taskShouldBeExecutedTestCases
- */
- public function testTaskShouldBeExecuted($expectedDecision, $taskName, $timetable)
- {
- $taskShouldBeExecuted = new ReflectionMethod(
- 'Piwik_TaskScheduler', 'taskShouldBeExecuted'
- );
- $taskShouldBeExecuted->setAccessible(TRUE);
-
- $this->assertEquals($expectedDecision, $taskShouldBeExecuted->invoke(new Piwik_TaskScheduler(), $taskName, $timetable));
- }
-
- /**
- * Dataprovider for testExecuteTask
- */
- public function executeTaskTestCases()
- {
- return array(
- array('scheduledTaskOne', null),
- array('scheduledTaskTwo', 'parameterValue'),
- array('scheduledTaskTwo', 1),
- );
- }
-
- /**
- * @group Core
- * @group TaskScheduler
- * @dataProvider executeTaskTestCases
- */
- public function testExecuteTask($methodName, $parameterValue)
- {
- // assert the scheduled method is executed once with the correct parameter
- $mock = $this->getMock('TaskSchedulerTest', array($methodName));
- $mock->expects($this->once())->method($methodName)->with($this->equalTo($parameterValue));
-
- $executeTask = new ReflectionMethod('Piwik_TaskScheduler', 'executeTask');
- $executeTask->setAccessible(TRUE);
-
- $this->assertNotEmpty($executeTask->invoke(
- new Piwik_TaskScheduler(),
- new Piwik_ScheduledTask ($mock, $methodName, $parameterValue, new Piwik_ScheduledTime_Daily())
- ));
- }
-
- /**
- * Dataprovider for testRunTasks
- */
- public function testRunTasksTestCases()
- {
- $systemTime = time();
-
- $dailySchedule = $this->getMock('Piwik_ScheduledTime_Daily', array('getTime'));
- $dailySchedule->expects($this->any())
- ->method('getTime')
- ->will($this->returnValue($systemTime));
-
- $scheduledTaskOne = new Piwik_ScheduledTask ($this, 'scheduledTaskOne', null, $dailySchedule);
- $scheduledTaskTwo = new Piwik_ScheduledTask ($this, 'scheduledTaskTwo', 1, $dailySchedule);
- $scheduledTaskThree = new Piwik_ScheduledTask ($this, 'scheduledTaskThree', null, $dailySchedule);
-
- $caseOneExpectedTable = array(
- 'TaskSchedulerTest.scheduledTaskOne' => $scheduledTaskOne->getRescheduledTime(),
- 'TaskSchedulerTest.scheduledTaskTwo_1' => $systemTime + 60000,
- 'TaskSchedulerTest.scheduledTaskThree' => $scheduledTaskThree->getRescheduledTime(),
- );
-
- $caseTwoTimetableBeforeExecution = $caseOneExpectedTable;
- $caseTwoTimetableBeforeExecution['TaskSchedulerTest.scheduledTaskThree'] = $systemTime; // simulate elapsed time between case 1 and 2
-
- return array(
-
- // case 1) contains :
- // - scheduledTaskOne: already scheduled before, should be executed and rescheduled
- // - scheduledTaskTwo: already scheduled before, should not be executed and therefore not rescheduled
- // - scheduledTaskThree: not already scheduled before, should be scheduled but not executed
- array(
- $caseOneExpectedTable,
-
- // methods that should be executed
- array(
- 'TaskSchedulerTest.scheduledTaskOne'
- ),
-
- // timetable before task execution
- array(
- 'TaskSchedulerTest.scheduledTaskOne' => $systemTime,
- 'TaskSchedulerTest.scheduledTaskTwo_1' => $systemTime + 60000,
- ),
- // configured tasks
- array(
- $scheduledTaskOne,
- $scheduledTaskTwo,
- $scheduledTaskThree,
- )
- ),
-
- // case 2) follows case 1) with :
- // - scheduledTaskOne: already scheduled before, should not be executed and therefore not rescheduled
- // - scheduledTaskTwo: not configured for execution anymore, should be removed from the timetable
- // - scheduledTaskThree: already scheduled before, should be executed and rescheduled
- array(
- // expected timetable
- array(
- 'TaskSchedulerTest.scheduledTaskOne' => $scheduledTaskOne->getRescheduledTime(),
- 'TaskSchedulerTest.scheduledTaskThree' => $scheduledTaskThree->getRescheduledTime()
- ),
-
- // methods that should be executed
- array(
- 'TaskSchedulerTest.scheduledTaskThree'
- ),
-
- // timetable before task execution
- $caseTwoTimetableBeforeExecution,
-
- // configured tasks
- array(
- $scheduledTaskOne,
+ }
+
+ /**
+ * Dataprovider for testTaskHasBeenScheduledOnce
+ */
+ public function taskHasBeenScheduledOnceTestCases()
+ {
+ $timetable = self::getTestTimetable();
+
+ return array(
+ array(true, 'Piwik_CoreAdminHome.purgeOutdatedArchives', $timetable),
+ array(true, 'Piwik_PrivacyManager.deleteReportData_1', $timetable),
+ array(false, 'Piwik_PDFReports.weeklySchedule"', $timetable)
+ );
+ }
+
+ /**
+ * @group Core
+ * @group TaskScheduler
+ * @dataProvider taskHasBeenScheduledOnceTestCases
+ */
+ public function testTaskHasBeenScheduledOnce($expectedDecision, $taskName, $timetable)
+ {
+ $taskHasBeenScheduledOnce = new ReflectionMethod(
+ 'Piwik_TaskScheduler', 'taskHasBeenScheduledOnce'
+ );
+ $taskHasBeenScheduledOnce->setAccessible(TRUE);
+
+ $this->assertEquals($expectedDecision, $taskHasBeenScheduledOnce->invoke(new Piwik_TaskScheduler(), $taskName, $timetable));
+ }
+
+ /**
+ * Dataprovider for testGetScheduledTimeForMethod
+ */
+ public function getScheduledTimeForMethodTestCases()
+ {
+ $timetable = serialize(self::getTestTimetable());
+
+ return array(
+ array(1355529607, 'Piwik_CoreAdminHome', 'purgeOutdatedArchives', null, $timetable),
+ array(1322229607, 'Piwik_PrivacyManager', 'deleteReportData', 1, $timetable),
+ array(false, 'Piwik_PDFReports', 'weeklySchedule', null, $timetable)
+ );
+ }
+
+ /**
+ * @group Core
+ * @group TaskScheduler
+ * @dataProvider getScheduledTimeForMethodTestCases
+ */
+ public function testGetScheduledTimeForMethod($expectedTime, $className, $methodName, $methodParameter, $timetable)
+ {
+ self::stubPiwikOption($timetable);
+
+ $this->assertEquals($expectedTime, Piwik_TaskScheduler::getScheduledTimeForMethod($className, $methodName, $methodParameter));
+
+ self::resetPiwikOption();
+ }
+
+ /**
+ * Dataprovider for testTaskShouldBeExecuted
+ */
+ public function taskShouldBeExecutedTestCases()
+ {
+ $timetable = self::getTestTimetable();
+
+ // set a date in the future (should not run)
+ $timetable['Piwik_CoreAdminHome.purgeOutdatedArchives'] = time() + 60000;
+
+ // set now (should run)
+ $timetable['Piwik_PrivacyManager.deleteReportData_1'] = time();
+
+ return array(
+ array(false, 'Piwik_CoreAdminHome.purgeOutdatedArchives', $timetable),
+ array(true, 'Piwik_PrivacyManager.deleteReportData_1', $timetable),
+ array(false, 'Piwik_PDFReports.weeklySchedule"', $timetable)
+ );
+ }
+
+ /**
+ * @group Core
+ * @group TaskScheduler
+ * @dataProvider taskShouldBeExecutedTestCases
+ */
+ public function testTaskShouldBeExecuted($expectedDecision, $taskName, $timetable)
+ {
+ $taskShouldBeExecuted = new ReflectionMethod(
+ 'Piwik_TaskScheduler', 'taskShouldBeExecuted'
+ );
+ $taskShouldBeExecuted->setAccessible(TRUE);
+
+ $this->assertEquals($expectedDecision, $taskShouldBeExecuted->invoke(new Piwik_TaskScheduler(), $taskName, $timetable));
+ }
+
+ /**
+ * Dataprovider for testExecuteTask
+ */
+ public function executeTaskTestCases()
+ {
+ return array(
+ array('scheduledTaskOne', null),
+ array('scheduledTaskTwo', 'parameterValue'),
+ array('scheduledTaskTwo', 1),
+ );
+ }
+
+ /**
+ * @group Core
+ * @group TaskScheduler
+ * @dataProvider executeTaskTestCases
+ */
+ public function testExecuteTask($methodName, $parameterValue)
+ {
+ // assert the scheduled method is executed once with the correct parameter
+ $mock = $this->getMock('TaskSchedulerTest', array($methodName));
+ $mock->expects($this->once())->method($methodName)->with($this->equalTo($parameterValue));
+
+ $executeTask = new ReflectionMethod('Piwik_TaskScheduler', 'executeTask');
+ $executeTask->setAccessible(TRUE);
+
+ $this->assertNotEmpty($executeTask->invoke(
+ new Piwik_TaskScheduler(),
+ new Piwik_ScheduledTask ($mock, $methodName, $parameterValue, new Piwik_ScheduledTime_Daily())
+ ));
+ }
+
+ /**
+ * Dataprovider for testRunTasks
+ */
+ public function testRunTasksTestCases()
+ {
+ $systemTime = time();
+
+ $dailySchedule = $this->getMock('Piwik_ScheduledTime_Daily', array('getTime'));
+ $dailySchedule->expects($this->any())
+ ->method('getTime')
+ ->will($this->returnValue($systemTime));
+
+ $scheduledTaskOne = new Piwik_ScheduledTask ($this, 'scheduledTaskOne', null, $dailySchedule);
+ $scheduledTaskTwo = new Piwik_ScheduledTask ($this, 'scheduledTaskTwo', 1, $dailySchedule);
+ $scheduledTaskThree = new Piwik_ScheduledTask ($this, 'scheduledTaskThree', null, $dailySchedule);
+
+ $caseOneExpectedTable = array(
+ 'TaskSchedulerTest.scheduledTaskOne' => $scheduledTaskOne->getRescheduledTime(),
+ 'TaskSchedulerTest.scheduledTaskTwo_1' => $systemTime + 60000,
+ 'TaskSchedulerTest.scheduledTaskThree' => $scheduledTaskThree->getRescheduledTime(),
+ );
+
+ $caseTwoTimetableBeforeExecution = $caseOneExpectedTable;
+ $caseTwoTimetableBeforeExecution['TaskSchedulerTest.scheduledTaskThree'] = $systemTime; // simulate elapsed time between case 1 and 2
+
+ return array(
+
+ // case 1) contains :
+ // - scheduledTaskOne: already scheduled before, should be executed and rescheduled
+ // - scheduledTaskTwo: already scheduled before, should not be executed and therefore not rescheduled
+ // - scheduledTaskThree: not already scheduled before, should be scheduled but not executed
+ array(
+ $caseOneExpectedTable,
+
+ // methods that should be executed
+ array(
+ 'TaskSchedulerTest.scheduledTaskOne'
+ ),
+
+ // timetable before task execution
+ array(
+ 'TaskSchedulerTest.scheduledTaskOne' => $systemTime,
+ 'TaskSchedulerTest.scheduledTaskTwo_1' => $systemTime + 60000,
+ ),
+ // configured tasks
+ array(
+ $scheduledTaskOne,
+ $scheduledTaskTwo,
+ $scheduledTaskThree,
+ )
+ ),
+
+ // case 2) follows case 1) with :
+ // - scheduledTaskOne: already scheduled before, should not be executed and therefore not rescheduled
+ // - scheduledTaskTwo: not configured for execution anymore, should be removed from the timetable
+ // - scheduledTaskThree: already scheduled before, should be executed and rescheduled
+ array(
+ // expected timetable
+ array(
+ 'TaskSchedulerTest.scheduledTaskOne' => $scheduledTaskOne->getRescheduledTime(),
+ 'TaskSchedulerTest.scheduledTaskThree' => $scheduledTaskThree->getRescheduledTime()
+ ),
+
+ // methods that should be executed
+ array(
+ 'TaskSchedulerTest.scheduledTaskThree'
+ ),
+
+ // timetable before task execution
+ $caseTwoTimetableBeforeExecution,
+
+ // configured tasks
+ array(
+ $scheduledTaskOne,
// $scheduledTaskTwo, Not configured anymore (ie. not returned after Piwik_TaskScheduler::GET_TASKS_EVENT is issued)
- $scheduledTaskThree,
- )
- ),
- );
- }
-
- public function scheduledTaskOne() { } // nothing to do
- public function scheduledTaskTwo($param) { } // nothing to do
- public function scheduledTaskThree() { } // nothing to do
-
- /**
- * @group Core
- * @group TaskScheduler
- * @dataProvider testRunTasksTestCases
- */
- public function testRunTasks($expectedTimetable, $expectedExecutedTasks, $timetableBeforeTaskExecution, $configuredTasks)
- {
- // stub the event dispatcher so we can control the returned event notification
- Piwik_PluginsManager::getInstance()->dispatcher = new MockEventDispatcher($configuredTasks);
-
- // stub the piwik option object to control the returned option value
- self::stubPiwikOption(serialize($timetableBeforeTaskExecution));
-
- // execute tasks
- $executionResults = Piwik_TaskScheduler::runTasks();
-
- // assert methods are executed
- $executedTasks = array();
- foreach($executionResults as $executionResult)
- {
- $executedTasks[] = $executionResult['task'];
- $this->assertNotEmpty($executionResult['output']);
- }
- $this->assertEquals($expectedExecutedTasks, $executedTasks);
-
- // assert the timetable is correctly updated
- $getTimetableFromOptionTable = new ReflectionMethod('Piwik_TaskScheduler', 'getTimetableFromOptionTable');
- $getTimetableFromOptionTable->setAccessible(TRUE);
- $this->assertEquals($expectedTimetable, $getTimetableFromOptionTable->invoke(new Piwik_TaskScheduler()));
-
- // restore event dispatcher & piwik options
- Piwik_PluginsManager::getInstance()->dispatcher = Event_Dispatcher::getInstance();
- self::resetPiwikOption();
- }
-
- private static function stubPiwikOption($timetable)
- {
- self::getReflectedPiwikOptionInstance()->setValue(new MockPiwikOption($timetable));
- }
-
- private static function resetPiwikOption()
- {
- self::getReflectedPiwikOptionInstance()->setValue(null);
- }
-
- private static function getReflectedPiwikOptionInstance()
- {
- $piwikOptionInstance = new ReflectionProperty('Piwik_Option', 'instance');
- $piwikOptionInstance->setAccessible(true);
- return $piwikOptionInstance;
- }
+ $scheduledTaskThree,
+ )
+ ),
+ );
+ }
+
+ public function scheduledTaskOne()
+ {
+ } // nothing to do
+ public function scheduledTaskTwo($param)
+ {
+ } // nothing to do
+ public function scheduledTaskThree()
+ {
+ } // nothing to do
+
+ /**
+ * @group Core
+ * @group TaskScheduler
+ * @dataProvider testRunTasksTestCases
+ */
+ public function testRunTasks($expectedTimetable, $expectedExecutedTasks, $timetableBeforeTaskExecution, $configuredTasks)
+ {
+ // stub the event dispatcher so we can control the returned event notification
+ Piwik_PluginsManager::getInstance()->dispatcher = new MockEventDispatcher($configuredTasks);
+
+ // stub the piwik option object to control the returned option value
+ self::stubPiwikOption(serialize($timetableBeforeTaskExecution));
+
+ // execute tasks
+ $executionResults = Piwik_TaskScheduler::runTasks();
+
+ // assert methods are executed
+ $executedTasks = array();
+ foreach ($executionResults as $executionResult) {
+ $executedTasks[] = $executionResult['task'];
+ $this->assertNotEmpty($executionResult['output']);
+ }
+ $this->assertEquals($expectedExecutedTasks, $executedTasks);
+
+ // assert the timetable is correctly updated
+ $getTimetableFromOptionTable = new ReflectionMethod('Piwik_TaskScheduler', 'getTimetableFromOptionTable');
+ $getTimetableFromOptionTable->setAccessible(TRUE);
+ $this->assertEquals($expectedTimetable, $getTimetableFromOptionTable->invoke(new Piwik_TaskScheduler()));
+
+ // restore event dispatcher & piwik options
+ Piwik_PluginsManager::getInstance()->dispatcher = Event_Dispatcher::getInstance();
+ self::resetPiwikOption();
+ }
+
+ private static function stubPiwikOption($timetable)
+ {
+ self::getReflectedPiwikOptionInstance()->setValue(new MockPiwikOption($timetable));
+ }
+
+ private static function resetPiwikOption()
+ {
+ self::getReflectedPiwikOptionInstance()->setValue(null);
+ }
+
+ private static function getReflectedPiwikOptionInstance()
+ {
+ $piwikOptionInstance = new ReflectionProperty('Piwik_Option', 'instance');
+ $piwikOptionInstance->setAccessible(true);
+ return $piwikOptionInstance;
+ }
}
diff --git a/tests/PHPUnit/Core/Tracker/ActionTest.php b/tests/PHPUnit/Core/Tracker/ActionTest.php
index 3e661c8611..f6cb3321de 100644
--- a/tests/PHPUnit/Core/Tracker/ActionTest.php
+++ b/tests/PHPUnit/Core/Tracker/ActionTest.php
@@ -14,7 +14,7 @@ class Tracker_ActionTest extends DatabaseTestCase
$config = Piwik_Config::getInstance();
$config->clear();
$config->setTestEnvironment($userFile, false);
-
+
Piwik_PluginsManager::getInstance()->loadPlugins(array('SitesManager'));
}
@@ -24,79 +24,79 @@ class Tracker_ActionTest extends DatabaseTestCase
FakeAccess::$superUser = true;
Zend_Registry::set('access', $pseudoMockAccess);
}
-
+
public function getTestUrls()
{
$campaignNameParam = 'test_campaign_name';
$campaignKwdParam = 'test_piwik_kwd';
-
+
$urls = array(
// a wrongly formatted url (parse_url returns false)
array('http:////wrongurl',
- array(false,
- false)),
-
+ array(false,
+ false)),
+
// a URL with all components
array('http://username:password@hostname:80/path?phpSESSID=value#anchor',
- array('http://username:password@hostname:80/path#anchor',
- 'http://username:password@hostname:80/path#anchor')),
-
+ array('http://username:password@hostname:80/path#anchor',
+ 'http://username:password@hostname:80/path#anchor')),
+
// a standard url with excluded campaign parameters
- array('http://a.com/index?p1=v1&'.$campaignNameParam.'=Adwords-CPC&'.$campaignKwdParam.'=My killer keyword',
- array('http://a.com/index?p1=v1',
- 'http://a.com/index?p1=v1')),
+ array('http://a.com/index?p1=v1&' . $campaignNameParam . '=Adwords-CPC&' . $campaignKwdParam . '=My killer keyword',
+ array('http://a.com/index?p1=v1',
+ 'http://a.com/index?p1=v1')),
// a standard url with excluded campaign parameters, GA style
array('http://a.com/index?p1=v1&utm_campaign=Adwords-CPC&utm_term=My killer keyword',
- array('http://a.com/index?p1=v1',
- 'http://a.com/index?p1=v1')),
-
+ array('http://a.com/index?p1=v1',
+ 'http://a.com/index?p1=v1')),
+
// testing with capital parameter
array('http://a.com/index?p1=v1&P2=v2&p3=v3',
- array('http://a.com/index?p1=v1&P2=v2&p3=v3',
- 'http://a.com/index?p1=v1&p3=v3')),
-
+ array('http://a.com/index?p1=v1&P2=v2&p3=v3',
+ 'http://a.com/index?p1=v1&p3=v3')),
+
// testing with array []
array('http://a.com/index?p1=v1&p2[]=v;2a&p2[]=v2b&p2[]=v2c&p3=v3&p4=v4',
- array('http://a.com/index?p1=v1&p2[]=v;2a&p2[]=v2b&p2[]=v2c&p3=v3&p4=v4',
- 'http://a.com/index?p1=v1&p3=v3')),
+ array('http://a.com/index?p1=v1&p2[]=v;2a&p2[]=v2b&p2[]=v2c&p3=v3&p4=v4',
+ 'http://a.com/index?p1=v1&p3=v3')),
// testing with missing value
array('http://a.com/index?p1=v1&p2=&p3=v3&p4',
- array('http://a.com/index?p1=v1&p2=&p3=v3&p4',
- 'http://a.com/index?p1=v1&p3=v3')),
+ array('http://a.com/index?p1=v1&p2=&p3=v3&p4',
+ 'http://a.com/index?p1=v1&p3=v3')),
array('http://a.com/index?p1&p2=v2&p3=v3&p4',
- array('http://a.com/index?p1&p2=v2&p3=v3&p4',
- 'http://a.com/index?p1&p3=v3')),
+ array('http://a.com/index?p1&p2=v2&p3=v3&p4',
+ 'http://a.com/index?p1&p3=v3')),
// testing with extra &&
array('http://a.com/index?p1=v1&&p2=v;2&p3=v%3b3&p4=v4&&',
- array('http://a.com/index?p1=v1&p2=v;2&p3=v%3b3&p4=v4',
- 'http://a.com/index?p1=v1&p3=v%3b3')),
+ array('http://a.com/index?p1=v1&p2=v;2&p3=v%3b3&p4=v4',
+ 'http://a.com/index?p1=v1&p3=v%3b3')),
// encode entities
array('http://a.com/index?p1=v1&p2%5B%5D=v2&p3=v3&p4=v4',
- array('http://a.com/index?p1=v1&p2[]=v2&p3=v3&p4=v4',
- 'http://a.com/index?p1=v1&p3=v3')),
+ array('http://a.com/index?p1=v1&p2[]=v2&p3=v3&p4=v4',
+ 'http://a.com/index?p1=v1&p3=v3')),
array('http://a.com/index?var%5Bvalue%5D%5Bdate%5D=01.01.2012',
- array('http://a.com/index?var[value][date]=01.01.2012',
- 'http://a.com/index')),
+ array('http://a.com/index?var[value][date]=01.01.2012',
+ 'http://a.com/index')),
// matrix parameters
array('http://a.com/index;jsessionid=value;p1=v1;p2=v2',
- array('http://a.com/index?p1=v1&p2=v2',
- 'http://a.com/index?p1=v1')),
+ array('http://a.com/index?p1=v1&p2=v2',
+ 'http://a.com/index?p1=v1')),
array('http://a.com/index;jsessionid=value?p1=v1&p2=v2',
- array('http://a.com/index?p1=v1&p2=v2',
- 'http://a.com/index?p1=v1')),
+ array('http://a.com/index?p1=v1&p2=v2',
+ 'http://a.com/index?p1=v1')),
);
-
+
return $urls;
}
-
+
/**
* No excluded query parameters specified, apart from the standard "session" parameters, always excluded
- *
+ *
* @group Core
* @group Tracker
* @group Tracker_Action
@@ -105,37 +105,37 @@ class Tracker_ActionTest extends DatabaseTestCase
public function testExcludeQueryParametersNone($url, $filteredUrl)
{
$this->setUpRootAccess();
- $idSite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'),$ecommerce=0,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
- $excludedIps = '', $excludedQueryParameters='', $timezone = null, $currency = null,
- $group = null, $startDate = null, $excludedUserAgents = null, $keepURLFragments = 1);
+ $idSite = Piwik_SitesManager_API::getInstance()->addSite("site1", array('http://example.org'), $ecommerce = 0,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
+ $excludedIps = '', $excludedQueryParameters = '', $timezone = null, $currency = null,
+ $group = null, $startDate = null, $excludedUserAgents = null, $keepURLFragments = 1);
$this->assertEquals($filteredUrl[0], Piwik_Tracker_Action::excludeQueryParametersFromUrl($url, $idSite));
}
- public function getTestUrlsHashtag()
- {
+ public function getTestUrlsHashtag()
+ {
$urls = array(
- // URL, Expected URL
- array('wrongurl/#', 'http://wrongurl/'),
- array('wrongurl/#t', 'http://wrongurl/#t'),
- array('wrongurl/#test', 'http://wrongurl/#test'),
- array('wrongurl/#test=1', 'http://wrongurl/#test=1'),
- array('wrongurl/#test=1#', 'http://wrongurl/#test=1'),
+ // URL, Expected URL
+ array('wrongurl/#', 'http://wrongurl/'),
+ array('wrongurl/#t', 'http://wrongurl/#t'),
+ array('wrongurl/#test', 'http://wrongurl/#test'),
+ array('wrongurl/#test=1', 'http://wrongurl/#test=1'),
+ array('wrongurl/#test=1#', 'http://wrongurl/#test=1'),
);
- return $urls;
- }
+ return $urls;
+ }
- /**
- * Test removing hash tag
- * @group Core
- * @group Tracker
- * @group Tracker_Action
- * @dataProvider getTestUrlsHashtag
- */
- public function testRemoveTrailingHashtag($url, $expectedUrl)
- {
- $this->assertEquals( Piwik_Tracker_Action::reconstructNormalizedUrl($url, Piwik_Tracker_Action::$urlPrefixMap['http://']), $expectedUrl);
- }
+ /**
+ * Test removing hash tag
+ * @group Core
+ * @group Tracker
+ * @group Tracker_Action
+ * @dataProvider getTestUrlsHashtag
+ */
+ public function testRemoveTrailingHashtag($url, $expectedUrl)
+ {
+ $this->assertEquals(Piwik_Tracker_Action::reconstructNormalizedUrl($url, Piwik_Tracker_Action::$urlPrefixMap['http://']), $expectedUrl);
+ }
/**
@@ -149,13 +149,13 @@ class Tracker_ActionTest extends DatabaseTestCase
{
$excludedQueryParameters = 'p4, p2, var[value][date]';
$this->setUpRootAccess();
- $idSite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'),$ecommerce=0,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
- $excludedIps = '', $excludedQueryParameters, $timezone = null, $currency = null,
- $group = null, $startDate = null, $excludedUserAgents = null, $keepURLFragments = 1);
+ $idSite = Piwik_SitesManager_API::getInstance()->addSite("site1", array('http://example.org'), $ecommerce = 0,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
+ $excludedIps = '', $excludedQueryParameters, $timezone = null, $currency = null,
+ $group = null, $startDate = null, $excludedUserAgents = null, $keepURLFragments = 1);
$this->assertEquals($filteredUrl[1], Piwik_Tracker_Action::excludeQueryParametersFromUrl($url, $idSite));
}
-
+
/**
* Testing with some website specific and some global excluded query parameters
* @group Core
@@ -169,16 +169,17 @@ class Tracker_ActionTest extends DatabaseTestCase
$excludedQueryParameters = 'P2,var[value][date]';
$excludedGlobalParameters = 'blabla, P4';
$this->setUpRootAccess();
- $idSite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'),$ecommerce=0,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
- $excludedIps = '', $excludedQueryParameters, $timezone = null, $currency = null,
- $group = null, $startDate = null, $excludedUserAgents = null, $keepURLFragments = 1);
+ $idSite = Piwik_SitesManager_API::getInstance()->addSite("site1", array('http://example.org'), $ecommerce = 0,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
+ $excludedIps = '', $excludedQueryParameters, $timezone = null, $currency = null,
+ $group = null, $startDate = null, $excludedUserAgents = null, $keepURLFragments = 1);
Piwik_SitesManager_API::getInstance()->setGlobalExcludedQueryParameters($excludedGlobalParameters);
$this->assertEquals($filteredUrl[1], Piwik_Tracker_Action::excludeQueryParametersFromUrl($url, $idSite));
}
-
-
- public function getExtractUrlData() {
+
+
+ public function getExtractUrlData()
+ {
return array(
// outlinks
array(
@@ -214,7 +215,7 @@ class Tracker_ActionTest extends DatabaseTestCase
array(
'request' => array('download' => 'http://example.org/*$test.zip'),
'expected' => array('name' => null,
- 'url' => 'http://example.org/*$test.zip',
+ 'url' => 'http://example.org/*$test.zip',
'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD),
),
@@ -222,132 +223,132 @@ class Tracker_ActionTest extends DatabaseTestCase
array(
'request' => array('download' => 'http://example.org/*$test.zip', 'action_name' => 'Download test.zip'),
'expected' => array('name' => 'Download test.zip',
- 'url' => 'http://example.org/*$test.zip',
+ 'url' => 'http://example.org/*$test.zip',
'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD),
),
-
+
// keep the case and multiple / in urls
array(
'request' => array('download' => 'http://example.org/CATEGORY/test///test.pdf'),
'expected' => array('name' => null,
- 'url' => 'http://example.org/CATEGORY/test///test.pdf',
+ 'url' => 'http://example.org/CATEGORY/test///test.pdf',
'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD),
),
-
+
// page view
array(
'request' => array('url' => 'http://example.org/'),
'expected' => array('name' => null,
- 'url' => 'http://example.org/',
+ 'url' => 'http://example.org/',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
array(
'request' => array('url' => 'http://example.org/', 'action_name' => 'Example.org Website'),
'expected' => array('name' => 'Example.org Website',
- 'url' => 'http://example.org/',
+ 'url' => 'http://example.org/',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
array(
'request' => array('url' => 'http://example.org/CATEGORY/'),
'expected' => array('name' => null,
- 'url' => 'http://example.org/CATEGORY/',
+ 'url' => 'http://example.org/CATEGORY/',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
array(
'request' => array('url' => 'http://example.org/CATEGORY/TEST', 'action_name' => 'Example.org / Category / test /'),
'expected' => array('name' => 'Example.org/Category/test',
- 'url' => 'http://example.org/CATEGORY/TEST',
+ 'url' => 'http://example.org/CATEGORY/TEST',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
array(
'request' => array('url' => 'http://example.org/?2,123'),
'expected' => array('name' => null,
- 'url' => 'http://example.org/?2,123',
+ 'url' => 'http://example.org/?2,123',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
// empty request
array(
'request' => array(),
- 'expected' => array('name' => null, 'url' => '',
+ 'expected' => array('name' => null, 'url' => '',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
array(
'request' => array('name' => null, 'url' => "\n"),
- 'expected' => array('name' => null, 'url' => '',
+ 'expected' => array('name' => null, 'url' => '',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
array(
- 'request' => array('url' => 'http://example.org/category/',
+ 'request' => array('url' => 'http://example.org/category/',
'action_name' => 'custom name with/one delimiter/two delimiters/'),
'expected' => array('name' => 'custom name with/one delimiter/two delimiters',
- 'url' => 'http://example.org/category/',
+ 'url' => 'http://example.org/category/',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
array(
- 'request' => array('url' => 'http://example.org/category/',
+ 'request' => array('url' => 'http://example.org/category/',
'action_name' => 'http://custom action name look like url/'),
'expected' => array('name' => 'http:/custom action name look like url',
- 'url' => 'http://example.org/category/',
+ 'url' => 'http://example.org/category/',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
// testing: delete tab, trimmed, not strtolowered
- array(
+ array(
'request' => array('url' => "http://example.org/category/test///test wOw "),
'expected' => array('name' => null,
- 'url' => 'http://example.org/category/test///test wOw',
+ 'url' => 'http://example.org/category/test///test wOw',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
// testing: inclusion of zero values in action name
array(
'request' => array('url' => "http://example.org/category/1/0/t/test"),
'expected' => array('name' => null,
- 'url' => 'http://example.org/category/1/0/t/test',
+ 'url' => 'http://example.org/category/1/0/t/test',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
// testing: action name ("Test &hellip;") - expect decoding of some html entities
array(
- 'request' => array('url' => 'http://example.org/ACTION/URL',
+ 'request' => array('url' => 'http://example.org/ACTION/URL',
'action_name' => "Test &hellip;"),
'expected' => array('name' => 'Test …',
- 'url' => 'http://example.org/ACTION/URL',
+ 'url' => 'http://example.org/ACTION/URL',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
// testing: action name ("Special &amp; chars") - expect no conversion of html special chars
array(
- 'request' => array('url' => 'http://example.org/ACTION/URL',
+ 'request' => array('url' => 'http://example.org/ACTION/URL',
'action_name' => "Special &amp; chars"),
'expected' => array('name' => 'Special &amp; chars',
- 'url' => 'http://example.org/ACTION/URL',
+ 'url' => 'http://example.org/ACTION/URL',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
// testing: action name ("Tést") - handle wide character
array(
- 'request' => array('url' => 'http://example.org/ACTION/URL',
+ 'request' => array('url' => 'http://example.org/ACTION/URL',
'action_name' => "Tést"),
'expected' => array('name' => 'Tést',
- 'url' => 'http://example.org/ACTION/URL',
+ 'url' => 'http://example.org/ACTION/URL',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
// testing: action name ("Tést") - handle UTF-8 byte sequence
array(
- 'request' => array('url' => 'http://example.org/ACTION/URL',
+ 'request' => array('url' => 'http://example.org/ACTION/URL',
'action_name' => "T\xc3\xa9st"),
'expected' => array('name' => 'Tést',
- 'url' => 'http://example.org/ACTION/URL',
+ 'url' => 'http://example.org/ACTION/URL',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
// testing: action name ("Tést") - invalid UTF-8 (e.g., ISO-8859-1) is not handled
array(
- 'request' => array('url' => 'http://example.org/ACTION/URL',
+ 'request' => array('url' => 'http://example.org/ACTION/URL',
'action_name' => "T\xe9st"),
'expected' => array('name' => version_compare(PHP_VERSION, '5.2.5') === -1 ? 'T\xe9st' : 'Tést',
- 'url' => 'http://example.org/ACTION/URL',
+ 'url' => 'http://example.org/ACTION/URL',
'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
),
);
}
-
+
/**
* @dataProvider getExtractUrlData
* @group Core
@@ -356,11 +357,11 @@ class Tracker_ActionTest extends DatabaseTestCase
*/
public function testExtractUrlAndActionNameFromRequest($request, $expected)
{
- $this->setUpRootAccess();
- $idSite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'));
- $action = new Test_Piwik_TrackerAction_extractUrlAndActionNameFromRequest();
- $action->setRequest($request);
- $action->setIdSite($idSite);
+ $this->setUpRootAccess();
+ $idSite = Piwik_SitesManager_API::getInstance()->addSite("site1", array('http://example.org'));
+ $action = new Test_Piwik_TrackerAction_extractUrlAndActionNameFromRequest();
+ $action->setRequest($request);
+ $action->setIdSite($idSite);
$this->assertEquals($action->public_extractUrlAndActionNameFromRequest(), $expected);
}
}
diff --git a/tests/PHPUnit/Core/Tracker/VisitTest.php b/tests/PHPUnit/Core/Tracker/VisitTest.php
index a56f1d27a2..0a262b9fc6 100644
--- a/tests/PHPUnit/Core/Tracker/VisitTest.php
+++ b/tests/PHPUnit/Core/Tracker/VisitTest.php
@@ -15,7 +15,7 @@ class Tracker_VisitTest extends DatabaseTestCase
$pseudoMockAccess = new FakeAccess;
FakeAccess::$superUser = true;
Zend_Registry::set('access', $pseudoMockAccess);
-
+
Piwik_PluginsManager::getInstance()->loadPlugins(array('SitesManager'));
}
@@ -26,41 +26,41 @@ class Tracker_VisitTest extends DatabaseTestCase
{
return array(
array('12.12.12.12', array(
- '12.12.12.12' => true,
- '12.12.12.11' => false,
- '12.12.12.13' => false,
- '0.0.0.0' => false,
+ '12.12.12.12' => true,
+ '12.12.12.11' => false,
+ '12.12.12.13' => false,
+ '0.0.0.0' => false,
'255.255.255.255' => false
)),
array('12.12.12.12/32', array(
- '12.12.12.12' => true,
- '12.12.12.11' => false,
- '12.12.12.13' => false,
- '0.0.0.0' => false,
+ '12.12.12.12' => true,
+ '12.12.12.11' => false,
+ '12.12.12.13' => false,
+ '0.0.0.0' => false,
'255.255.255.255' => false
)),
array('12.12.12.*', array(
- '12.12.12.0' => true,
- '12.12.12.255' => true,
- '12.12.12.12' => true,
- '12.12.11.255' => false,
- '12.12.13.0' => false,
- '0.0.0.0' => false,
+ '12.12.12.0' => true,
+ '12.12.12.255' => true,
+ '12.12.12.12' => true,
+ '12.12.11.255' => false,
+ '12.12.13.0' => false,
+ '0.0.0.0' => false,
'255.255.255.255' => false,
)),
array('12.12.12.0/24', array(
- '12.12.12.0' => true,
- '12.12.12.255' => true,
- '12.12.12.12' => true,
- '12.12.11.255' => false,
- '12.12.13.0' => false,
- '0.0.0.0' => false,
+ '12.12.12.0' => true,
+ '12.12.12.255' => true,
+ '12.12.12.12' => true,
+ '12.12.11.255' => false,
+ '12.12.13.0' => false,
+ '0.0.0.0' => false,
'255.255.255.255' => false,
)),
// add some ipv6 addresses!
);
}
-
+
/**
* @group Core
* @group Tracker
@@ -70,76 +70,75 @@ class Tracker_VisitTest extends DatabaseTestCase
public function testIsVisitorIpExcluded($excludedIp, $tests)
{
$visit = new Test_Piwik_TrackerVisit_public();
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("name","http://piwik.net/",$ecommerce=0,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIp);
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("name", "http://piwik.net/", $ecommerce = 0,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIp);
$visit->setRequest(array('idsite' => $idsite));
// test that IPs within the range, or the given IP, are excluded
- foreach($tests as $ip => $expected)
- {
+ foreach ($tests as $ip => $expected) {
$testIpIsExcluded = Piwik_IP::P2N($ip);
$this->assertSame($expected, $visit->public_isVisitorIpExcluded($testIpIsExcluded));
}
}
-
+
/**
* Dataprovider for testIsVisitorUserAgentExcluded.
*/
public function getExcludedUserAgentTestData()
{
- return array(
- array('', array(
- 'whatever' => false,
- '' => false,
- 'nlksdjfsldkjfsa' => false,
- )),
- array('mozilla', array(
- 'this has mozilla in it' => true,
- 'this doesn\'t' => false,
- 'partial presence: mozi' => false,
- )),
- array('cHrOmE,notinthere,&^%', array(
- 'chrome is here' => true,
- 'CHROME is here' => true,
- '12&^%345' => true,
- 'sfasdf' => false,
- )),
- );
+ return array(
+ array('', array(
+ 'whatever' => false,
+ '' => false,
+ 'nlksdjfsldkjfsa' => false,
+ )),
+ array('mozilla', array(
+ 'this has mozilla in it' => true,
+ 'this doesn\'t' => false,
+ 'partial presence: mozi' => false,
+ )),
+ array('cHrOmE,notinthere,&^%', array(
+ 'chrome is here' => true,
+ 'CHROME is here' => true,
+ '12&^%345' => true,
+ 'sfasdf' => false,
+ )),
+ );
+ }
+
+ /**
+ * @group Core
+ * @group Tracker
+ * @group Tracker_Visit
+ * @dataProvider getExcludedUserAgentTestData
+ */
+ public function testIsVisitorUserAgentExcluded($excludedUserAgent, $tests)
+ {
+ Piwik_SitesManager_API::getInstance()->setSiteSpecificUserAgentExcludeEnabled(true);
+
+ $visit = new Test_Piwik_TrackerVisit_public();
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("name", "http://piwik.net/", $ecommerce = 0,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIp = null,
+ $excludedQueryParameters = null, $timezone = null, $currency = null, $group = null, $startDate = null,
+ $excludedUserAgent);
+ $visit->setRequest(array('idsite' => $idsite));
+
+ // test that user agents that contain excluded user agent strings are excluded
+ foreach ($tests as $ua => $expected) {
+ $this->assertSame($expected, $visit->public_isUserAgentExcluded($ua), "Result if isUserAgentExcluded('$ua') was not " . ($expected ? 'true' : 'false') . ".");
+ }
}
-
- /**
- * @group Core
- * @group Tracker
- * @group Tracker_Visit
- * @dataProvider getExcludedUserAgentTestData
- */
- public function testIsVisitorUserAgentExcluded($excludedUserAgent, $tests)
- {
- Piwik_SitesManager_API::getInstance()->setSiteSpecificUserAgentExcludeEnabled(true);
-
- $visit = new Test_Piwik_TrackerVisit_public();
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("name","http://piwik.net/",$ecommerce=0,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIp=null,
- $excludedQueryParameters = null, $timezone = null, $currency = null, $group = null, $startDate = null,
- $excludedUserAgent);
- $visit->setRequest(array('idsite' => $idsite));
-
- // test that user agents that contain excluded user agent strings are excluded
- foreach ($tests as $ua => $expected)
- {
- $this->assertSame($expected, $visit->public_isUserAgentExcluded($ua), "Result if isUserAgentExcluded('$ua') was not ".($expected?'true':'false').".");
- }
- }
}
-class Test_Piwik_TrackerVisit_public extends Piwik_Tracker_Visit {
- public function public_isVisitorIpExcluded( $ip )
- {
- return $this->isVisitorIpExcluded($ip);
- }
-
- public function public_isUserAgentExcluded( $ua )
- {
- return $this->isUserAgentExcluded($ua);
- }
+class Test_Piwik_TrackerVisit_public extends Piwik_Tracker_Visit
+{
+ public function public_isVisitorIpExcluded($ip)
+ {
+ return $this->isVisitorIpExcluded($ip);
+ }
+
+ public function public_isUserAgentExcluded($ua)
+ {
+ return $this->isUserAgentExcluded($ua);
+ }
}
diff --git a/tests/PHPUnit/Core/TranslationWriterTest.php b/tests/PHPUnit/Core/TranslationWriterTest.php
index be5c90f93a..27a4fa597a 100644
--- a/tests/PHPUnit/Core/TranslationWriterTest.php
+++ b/tests/PHPUnit/Core/TranslationWriterTest.php
@@ -62,8 +62,7 @@ class TranslationWriterTest extends PHPUnit_Framework_TestCase
*/
public function testQuote($data, $expected)
{
- if(Piwik_Common::isWindows() && $data == "\n")
- {
+ if (Piwik_Common::isWindows() && $data == "\n") {
return;
}
$this->assertEquals($expected, Piwik_TranslationWriter::quote($data));
@@ -82,7 +81,7 @@ class TranslationWriterTest extends PHPUnit_Framework_TestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* @group Core
* @group TranslationWriter
@@ -96,7 +95,7 @@ class TranslationWriterTest extends PHPUnit_Framework_TestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* @group Core
* @group TranslationWriter
@@ -124,7 +123,7 @@ class TranslationWriterTest extends PHPUnit_Framework_TestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* @group Core
* @group TranslationWriter
@@ -151,9 +150,9 @@ class TranslationWriterTest extends PHPUnit_Framework_TestCase
$translations = array(
'General_Locale' => 'en_CA.UTF-8',
- 'General_Id' => 'Id',
- 'Goals_Goals' => 'Goals',
- 'Plugin_Body' => "Message\nBody",
+ 'General_Id' => 'Id',
+ 'Goals_Goals' => 'Goals',
+ 'Plugin_Body' => "Message\nBody",
);
@unlink($path);
@@ -163,7 +162,7 @@ class TranslationWriterTest extends PHPUnit_Framework_TestCase
$contents = file_get_contents($path);
$expected = "<?php\n\$translations = array(\n\t'General_Locale' => 'en_CA.UTF-8',\n\t'General_Id' => 'Id',\n\t'Goals_Goals' => 'Goals',\n\n\t// FOR REVIEW\n\t'Plugin_Body' => 'Message\nBody',\n);\n";
- if(Piwik_Common::isWindows()) $expected = str_replace("\r\n", "\n", $expected);
+ if (Piwik_Common::isWindows()) $expected = str_replace("\r\n", "\n", $expected);
$this->assertEquals($expected, $contents);
}
}
diff --git a/tests/PHPUnit/Core/UnzipTest.php b/tests/PHPUnit/Core/UnzipTest.php
index 40706a19df..8ec666dd79 100644
--- a/tests/PHPUnit/Core/UnzipTest.php
+++ b/tests/PHPUnit/Core/UnzipTest.php
@@ -16,10 +16,9 @@ class UnzipTest extends PHPUnit_Framework_TestCase
clearstatcache();
$extractDir = PIWIK_USER_PATH . '/tmp/latest/';
$test = 'relative';
- $filename = dirname(__FILE__) . '/Unzip/'.$test.'.zip';
+ $filename = dirname(__FILE__) . '/Unzip/' . $test . '.zip';
- if(class_exists('ZipArchive', false))
- {
+ if (class_exists('ZipArchive', false)) {
$unzip = Piwik_Unzip::factory('ZipArchive', $filename);
$res = $unzip->extract($extractDir);
$this->assertEquals(1, count($res));
@@ -63,10 +62,9 @@ class UnzipTest extends PHPUnit_Framework_TestCase
clearstatcache();
$extractDir = PIWIK_USER_PATH . '/tmp/latest/';
$test = 'zaatt';
- $filename = dirname(__FILE__) . '/Unzip/'.$test.'.zip';
+ $filename = dirname(__FILE__) . '/Unzip/' . $test . '.zip';
- if(class_exists('ZipArchive', false))
- {
+ if (class_exists('ZipArchive', false)) {
$unzip = new Piwik_Unzip_ZipArchive($filename);
$res = $unzip->extract($extractDir);
$this->assertEquals(0, $res);
@@ -96,10 +94,9 @@ class UnzipTest extends PHPUnit_Framework_TestCase
clearstatcache();
$extractDir = PIWIK_USER_PATH . '/tmp/latest/';
$test = 'zaabs';
- $filename = dirname(__FILE__) . '/Unzip/'.$test.'.zip';
+ $filename = dirname(__FILE__) . '/Unzip/' . $test . '.zip';
- if(class_exists('ZipArchive', false))
- {
+ if (class_exists('ZipArchive', false)) {
$unzip = new Piwik_Unzip_ZipArchive($filename);
$res = $unzip->extract($extractDir);
$this->assertEquals(0, $res);
@@ -177,48 +174,48 @@ class UnzipTest extends PHPUnit_Framework_TestCase
$this->assertContains('PCLZIP_ERR_MISSING_FILE', $unzip->errorInfo());
}
- /**
- * @group Core
- * @group Unzip
- */
- public function testGzipFile()
- {
- $extractDir = PIWIK_USER_PATH . '/tmp/latest/';
- $extractFile = $extractDir.'testgz.txt';
- $filename = dirname(__FILE__).'/Unzip/test.gz';
-
- $unzip = new Piwik_Unzip_Gzip($filename);
- $res = $unzip->extract($extractFile);
- $this->assertTrue($res);
-
- $this->assertFileContentsEquals('TESTSTRING', $extractFile);
- }
-
- /**
- * @group Core
- * @group Unzip
- */
- public function testTarGzFile()
- {
- $extractDir = PIWIK_USER_PATH.'/tmp/latest/';
- $filename = dirname(__FILE__).'/Unzip/test.tar.gz';
-
- $unzip = new Piwik_Unzip_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);
- }
+ /**
+ * @group Core
+ * @group Unzip
+ */
+ public function testGzipFile()
+ {
+ $extractDir = PIWIK_USER_PATH . '/tmp/latest/';
+ $extractFile = $extractDir . 'testgz.txt';
+ $filename = dirname(__FILE__) . '/Unzip/test.gz';
+
+ $unzip = new Piwik_Unzip_Gzip($filename);
+ $res = $unzip->extract($extractFile);
+ $this->assertTrue($res);
+
+ $this->assertFileContentsEquals('TESTSTRING', $extractFile);
+ }
+
+ /**
+ * @group Core
+ * @group Unzip
+ */
+ public function testTarGzFile()
+ {
+ $extractDir = PIWIK_USER_PATH . '/tmp/latest/';
+ $filename = dirname(__FILE__) . '/Unzip/test.tar.gz';
+
+ $unzip = new Piwik_Unzip_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/Core/UpdaterTest.php b/tests/PHPUnit/Core/UpdaterTest.php
index f177c52de2..546a9891b6 100644
--- a/tests/PHPUnit/Core/UpdaterTest.php
+++ b/tests/PHPUnit/Core/UpdaterTest.php
@@ -5,7 +5,7 @@
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-class UpdaterTest extends DatabaseTestCase
+class UpdaterTest extends DatabaseTestCase
{
/**
* @group Core
@@ -36,11 +36,11 @@ class UpdaterTest extends DatabaseTestCase
$this->assertEquals(1, count($componentsWithUpdateFile));
$updateFiles = $componentsWithUpdateFile['testpluginUpdates'];
$this->assertEquals(2, count($updateFiles));
-
+
$path = PIWIK_INCLUDE_PATH . '/tests/resources/Updater/testpluginUpdates/';
$expectedInOrder = array(
$path . '0.1beta2.php' => '0.1beta2',
- $path . '0.1.php' => '0.1'
+ $path . '0.1.php' => '0.1'
);
$this->assertEquals($expectedInOrder, array_map("basename", $updateFiles));
}
@@ -54,13 +54,13 @@ class UpdaterTest extends DatabaseTestCase
$updater = new Piwik_Updater();
$updater->pathUpdateFilePlugins = PIWIK_INCLUDE_PATH . '/tests/resources/Updater/%s/';
$updater->pathUpdateFileCore = PIWIK_INCLUDE_PATH . '/tests/resources/Updater/core/';
-
+
$updater->recordComponentSuccessfullyUpdated('testpluginUpdates', '0.1beta');
$updater->addComponentToCheck('testpluginUpdates', '0.1');
-
+
$updater->recordComponentSuccessfullyUpdated('core', '0.1');
$updater->addComponentToCheck('core', '0.3');
-
+
$componentsWithUpdateFile = $updater->getComponentsWithUpdateFile();
$this->assertEquals(2, count($componentsWithUpdateFile));
reset($componentsWithUpdateFile);
diff --git a/tests/PHPUnit/Core/UrlTest.php b/tests/PHPUnit/Core/UrlTest.php
index 27cacd9d4f..114adb365b 100644
--- a/tests/PHPUnit/Core/UrlTest.php
+++ b/tests/PHPUnit/Core/UrlTest.php
@@ -13,16 +13,15 @@ class UrlTest extends PHPUnit_Framework_TestCase
*/
public function testAllMethods()
{
- $this->assertEquals(Piwik_Url::getCurrentQueryStringWithParametersModified(array()),Piwik_Url::getCurrentQueryString() );
+ $this->assertEquals(Piwik_Url::getCurrentQueryStringWithParametersModified(array()), Piwik_Url::getCurrentQueryString());
$this->assertEquals(Piwik_Url::getCurrentUrl(), Piwik_Url::getCurrentUrlWithoutQueryString());
- $this->assertEquals(Piwik_Url::getCurrentUrl(), Piwik_Url::getCurrentScheme() . '://' . Piwik_Url::getCurrentHost() . Piwik_Url::getCurrentScriptName() );
-
+ $this->assertEquals(Piwik_Url::getCurrentUrl(), Piwik_Url::getCurrentScheme() . '://' . Piwik_Url::getCurrentHost() . Piwik_Url::getCurrentScriptName());
+
$_SERVER['QUERY_STRING'] = 'q=test';
-
+
$parameters = array_keys(Piwik_Url::getArrayFromCurrentQueryString());
$parametersNameToValue = array();
- foreach($parameters as $name)
- {
+ foreach ($parameters as $name) {
$parametersNameToValue[$name] = null;
}
$this->assertEquals('', Piwik_Url::getCurrentQueryStringWithParametersModified($parametersNameToValue));
@@ -31,7 +30,8 @@ class UrlTest extends PHPUnit_Framework_TestCase
/**
* Dataprovider for testGetCurrentHost()
*/
- public function getCurrentHosts() {
+ public function getCurrentHosts()
+ {
return array(
array('localhost IPv4', array('127.0.0.1', null, null, null, '127.0.0.1')),
array('localhost IPv6', array('[::1]', null, null, null, '[::1]')),
@@ -62,10 +62,10 @@ class UrlTest extends PHPUnit_Framework_TestCase
{
$_SERVER['HTTP_HOST'] = $test[0];
$_SERVER['HTTP_X_FORWARDED_HOST'] = $test[1];
- Piwik_Config::getInstance()->General['proxy_host_headers'] = array( $test[2] );
- Piwik_Config::getInstance()->General['proxy_ips'] = array( $test[3] );
+ Piwik_Config::getInstance()->General['proxy_host_headers'] = array($test[2]);
+ Piwik_Config::getInstance()->General['proxy_ips'] = array($test[3]);
Piwik_Config::getInstance()->General['enable_trusted_host_check'] = 0;
- $this->assertEquals( $test[4], Piwik_Url::getCurrentHost(), $description );
+ $this->assertEquals($test[4], Piwik_Url::getCurrentHost(), $description);
}
/**
@@ -123,7 +123,7 @@ class UrlTest extends PHPUnit_Framework_TestCase
array('www.example.com', 'http://www.example.com/path/', '/path/', 'http://crsf.example.com/path/', false),
);
}
-
+
/**
* @dataProvider getLocalUrls
* @group Core
@@ -137,13 +137,14 @@ class UrlTest extends PHPUnit_Framework_TestCase
Piwik_Config::getInstance()->General['enable_trusted_host_check'] = 1;
Piwik_Config::getInstance()->General['trusted_hosts'] = array($httphost);
$urlToTest = $testurl;
- $this->assertEquals( $result, Piwik_Url::isLocalUrl($urlToTest) );
+ $this->assertEquals($result, Piwik_Url::isLocalUrl($urlToTest));
}
/**
* Dataprovider for testGetCurrentUrlWithoutFilename
*/
- public function getCurrentUrlWithoutFilename() {
+ public function getCurrentUrlWithoutFilename()
+ {
return array(
array('http://example.com/', false, 'example.com', '/'),
array('https://example.org/', true, 'example.org', '/'),
@@ -161,8 +162,7 @@ class UrlTest extends PHPUnit_Framework_TestCase
{
$names = array('PATH_INFO', 'REQUEST_URI', 'SCRIPT_NAME', 'SCRIPT_FILENAME', 'argv', 'HTTPS', 'HTTP_HOST', 'QUERY_STRING', 'HTTP_REFERER');
- foreach($names as $name)
- {
+ foreach ($names as $name) {
unset($_SERVER[$name]);
}
@@ -176,7 +176,7 @@ class UrlTest extends PHPUnit_Framework_TestCase
$_SERVER['HTTP_HOST'] = $host;
Piwik_Config::getInstance()->General['trusted_hosts'] = array($host);
-
+
$url = Piwik_Url::getCurrentUrlWithoutFilename();
$this->assertEquals($expected, $url);
}
@@ -189,8 +189,7 @@ class UrlTest extends PHPUnit_Framework_TestCase
{
$names = array('PATH_INFO', 'REQUEST_URI', 'SCRIPT_NAME', 'SCRIPT_FILENAME', 'argv', 'HTTPS', 'HTTP_HOST', 'QUERY_STRING', 'HTTP_REFERER');
- foreach($names as $name)
- {
+ foreach ($names as $name) {
unset($_SERVER[$name]);
}
@@ -204,8 +203,7 @@ class UrlTest extends PHPUnit_Framework_TestCase
array('/path/index.php', '/path/index.php/route/3/?module=Fu&action=Bar#Hash', '/route/3/'),
);
- foreach($tests as $test)
- {
+ foreach ($tests as $test) {
list($expected, $uri, $pathInfo) = $test;
$_SERVER['REQUEST_URI'] = $uri;
@@ -247,7 +245,7 @@ class UrlTest extends PHPUnit_Framework_TestCase
*/
public function testIsValidHost($expected, $host, $trustedHosts, $description)
{
- Piwik_Config::getInstance()->General['enable_trusted_host_check'] = 1;
+ Piwik_Config::getInstance()->General['enable_trusted_host_check'] = 1;
Piwik_Config::getInstance()->General['trusted_hosts'] = $trustedHosts;
$this->assertEquals($expected, Piwik_Url::isValidHost($host), $description);
}
diff --git a/tests/PHPUnit/DatabaseTestCase.php b/tests/PHPUnit/DatabaseTestCase.php
index 760962e1d5..2b0627f251 100644
--- a/tests/PHPUnit/DatabaseTestCase.php
+++ b/tests/PHPUnit/DatabaseTestCase.php
@@ -8,10 +8,10 @@
/**
* Tests extending DatabaseTestCase are much slower to run: the setUp will
* create all Piwik tables in a freshly empty test database.
- *
- * This allows each test method to start from a clean DB and setup initial state to
+ *
+ * This allows each test method to start from a clean DB and setup initial state to
* then test it.
- *
+ *
*/
class DatabaseTestCase extends PHPUnit_Framework_TestCase
{
@@ -39,12 +39,12 @@ class DatabaseTestCase extends PHPUnit_Framework_TestCase
Piwik::createTables();
Piwik::createLogObject();
- Piwik_PluginsManager::getInstance()->loadPlugins(array());
+ Piwik_PluginsManager::getInstance()->loadPlugins(array());
- } catch(Exception $e) {
- $this->fail("TEST INITIALIZATION FAILED: " .$e->getMessage());
+ } catch (Exception $e) {
+ $this->fail("TEST INITIALIZATION FAILED: " . $e->getMessage());
}
-
+
include "DataFiles/SearchEngines.php";
include "DataFiles/Languages.php";
include "DataFiles/Countries.php";
@@ -60,11 +60,12 @@ class DatabaseTestCase extends PHPUnit_Framework_TestCase
parent::tearDown();
try {
$plugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins();
- foreach($plugins AS $plugin) {
+ foreach ($plugins AS $plugin) {
$plugin->uninstall();
}
Piwik_PluginsManager::getInstance()->unloadPlugins();
- } catch (Exception $e) {}
+ } catch (Exception $e) {
+ }
Piwik::dropDatabase();
Piwik_DataTable_Manager::getInstance()->deleteAll();
Piwik_Option::getInstance()->clearCache();
diff --git a/tests/PHPUnit/FakeAccess.php b/tests/PHPUnit/FakeAccess.php
index 032cbfc3c4..3d8f0710a4 100644
--- a/tests/PHPUnit/FakeAccess.php
+++ b/tests/PHPUnit/FakeAccess.php
@@ -17,10 +17,10 @@ class FakeAccess
public function __construct()
{
- self::$superUser = false;
+ self::$superUser = false;
self::$idSitesAdmin = array();
- self::$idSitesView = array();
- self::$identity = 'superUserLogin';
+ self::$idSitesView = array();
+ self::$identity = 'superUserLogin';
}
public static function setIdSitesAdmin($ids)
@@ -28,82 +28,68 @@ class FakeAccess
self::$superUser = false;
self::$idSitesAdmin = $ids;
}
+
public static function setIdSitesView($ids)
{
self::$superUser = false;
self::$idSitesView = $ids;
}
-
+
public static function checkUserIsSuperUser()
{
- if(!self::$superUser)
- {
+ if (!self::$superUser) {
throw new Exception("checkUserIsSuperUser Fake exception // string not to be tested");
}
}
-
+
public static function setSuperUser($bool = true)
{
self::$superUser = $bool;
}
public static function reloadAccess()
- {}
+ {
+ }
- public static function checkUserHasAdminAccess( $idSites )
+ public static function checkUserHasAdminAccess($idSites)
{
- if(!self::$superUser)
- {
- $websitesAccess=self::$idSitesAdmin;
- }
- else
- {
- $websitesAccess=Piwik_SitesManager_API::getInstance()->getAllSitesId();
+ if (!self::$superUser) {
+ $websitesAccess = self::$idSitesAdmin;
+ } else {
+ $websitesAccess = Piwik_SitesManager_API::getInstance()->getAllSitesId();
}
-
+
$idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites);
- foreach($idSites as $idsite)
- {
- if(!in_array($idsite, $websitesAccess))
- {
+ foreach ($idSites as $idsite) {
+ if (!in_array($idsite, $websitesAccess)) {
throw new Exception("checkUserHasAdminAccess Fake exception // string not to be tested");
}
}
}
-
+
//means at least view access
- public static function checkUserHasViewAccess( $idSites )
+ public static function checkUserHasViewAccess($idSites)
{
- if(!self::$superUser)
- {
- $websitesAccess=array_merge(self::$idSitesView,self::$idSitesAdmin);
- }
- else
- {
- $websitesAccess=Piwik_SitesManager_API::getInstance()->getAllSitesId();
+ if (!self::$superUser) {
+ $websitesAccess = array_merge(self::$idSitesView, self::$idSitesAdmin);
+ } else {
+ $websitesAccess = Piwik_SitesManager_API::getInstance()->getAllSitesId();
}
-
- if(!is_array($idSites))
- {
- if($idSites == 'all')
- {
- $idSites = Piwik_SitesManager_API::getInstance()->getAllSitesId();
- }
- else
- {
- $idSites = explode(',', $idSites);
- }
+
+ if (!is_array($idSites)) {
+ if ($idSites == 'all') {
+ $idSites = Piwik_SitesManager_API::getInstance()->getAllSitesId();
+ } else {
+ $idSites = explode(',', $idSites);
+ }
}
-
- if (empty($websitesAccess))
- {
- throw new Exception("checkUserHasViewAccess Fake exception // string not to be tested");
+
+ if (empty($websitesAccess)) {
+ throw new Exception("checkUserHasViewAccess Fake exception // string not to be tested");
}
-
- foreach($idSites as $idsite)
- {
- if(!in_array($idsite, $websitesAccess))
- {
+
+ foreach ($idSites as $idsite) {
+ if (!in_array($idsite, $websitesAccess)) {
throw new Exception("checkUserHasViewAccess Fake exception // string not to be tested");
}
}
@@ -111,15 +97,11 @@ class FakeAccess
public static function checkUserHasSomeViewAccess()
{
- if(!self::$superUser)
- {
- if( count(self::$idSitesView) == 0 )
- {
+ if (!self::$superUser) {
+ if (count(self::$idSitesView) == 0) {
throw new Exception("checkUserHasSomeViewAccess Fake exception // string not to be tested");
}
- }
- else
- {
+ } else {
return;
}
}
@@ -127,47 +109,41 @@ class FakeAccess
//means at least view access
public static function checkUserHasSomeAdminAccess()
{
- if(!self::$superUser)
- {
- if( count(self::$idSitesAdmin) == 0 )
- {
+ if (!self::$superUser) {
+ if (count(self::$idSitesAdmin) == 0) {
throw new Exception("checkUserHasSomeAdminAccess Fake exception // string not to be tested");
}
- }
- else
- {
+ } else {
return; //super user has some admin rights
}
}
-
+
public static function getLogin()
{
return self::$identity;
}
-
+
public static function getSitesIdWithAdminAccess()
{
- if(self::$superUser)
- {
+ if (self::$superUser) {
return Piwik_SitesManager_API::getInstance()->getAllSitesId();
}
- return self::$idSitesAdmin;
+ return self::$idSitesAdmin;
}
-
+
public static function getSitesIdWithViewAccess()
{
- if(self::$superUser)
- {
+ if (self::$superUser) {
return Piwik_SitesManager_API::getInstance()->getAllSitesId();
}
- return self::$idSitesView;
+ return self::$idSitesView;
}
+
public static function getSitesIdWithAtLeastViewAccess()
{
- if(self::$superUser)
- {
+ if (self::$superUser) {
return Piwik_SitesManager_API::getInstance()->getAllSitesId();
}
- return array_merge(self::$idSitesView,self::$idSitesAdmin);
+ return array_merge(self::$idSitesView, self::$idSitesAdmin);
}
}
diff --git a/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorId.php b/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorId.php
index 0f394ad92e..3768205fc6 100644
--- a/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorId.php
+++ b/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorId.php
@@ -11,20 +11,20 @@
*/
class Test_Piwik_Fixture_FewVisitsWithSetVisitorId extends Test_Piwik_BaseFixture
{
- public $idSite = 1;
+ public $idSite = 1;
public $dateTime = '2010-03-06 11:22:33';
-
+
public function setUp()
{
$this->setUpWebsitesAndGoals();
$this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
+
private function setUpWebsitesAndGoals()
{
// tests run in UTC, the Tracker in UTC
@@ -34,8 +34,8 @@ class Test_Piwik_Fixture_FewVisitsWithSetVisitorId extends Test_Piwik_BaseFixtur
private function trackVisits()
{
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
-
+ $idSite = $this->idSite;
+
$t = self::getTracker($idSite, $dateTime, $defaultInit = true);
// First, some basic tests
@@ -67,19 +67,19 @@ class Test_Piwik_Fixture_FewVisitsWithSetVisitorId extends Test_Piwik_BaseFixtur
try {
$t->setVisitorId('test');
$this->fail('should throw');
- } catch(Exception $e) {
+ } catch (Exception $e) {
//OK
}
try {
$t->setVisitorId('61e8');
$this->fail('should throw');
- } catch(Exception $e) {
+ } catch (Exception $e) {
//OK
}
try {
$t->setVisitorId('61e8cc2d51fea26dabcabcabc');
$this->fail('should throw');
- } catch(Exception $e) {
+ } catch (Exception $e) {
//OK
}
}
diff --git a/tests/PHPUnit/Fixtures/InvalidVisits.php b/tests/PHPUnit/Fixtures/InvalidVisits.php
index f7d7a004d6..a424f05c6d 100644
--- a/tests/PHPUnit/Fixtures/InvalidVisits.php
+++ b/tests/PHPUnit/Fixtures/InvalidVisits.php
@@ -12,20 +12,20 @@
*/
class Test_Piwik_Fixture_InvalidVisits extends Test_Piwik_BaseFixture
{
- public $idSite = 1;
- public $dateTime = '2009-01-04 00:11:42';
-
- public $trackInvalidRequests = true;
+ public $idSite = 1;
+ public $dateTime = '2009-01-04 00:11:42';
- public function setUp()
- {
+ public $trackInvalidRequests = true;
+
+ public function setUp()
+ {
$this->setUpWebsitesAndGoals();
$this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
private function setUpWebsitesAndGoals()
@@ -35,20 +35,19 @@ class Test_Piwik_Fixture_InvalidVisits extends Test_Piwik_BaseFixture
private function trackVisits()
{
- if (!$this->trackInvalidRequests)
- {
- return;
- }
-
+ if (!$this->trackInvalidRequests) {
+ return;
+ }
+
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
-
+ $idSite = $this->idSite;
+
Piwik_SitesManager_API::getInstance()->setSiteSpecificUserAgentExcludeEnabled(true);
Piwik_SitesManager_API::getInstance()->setGlobalExcludedUserAgents('globalexcludeduseragent');
// Trigger empty request
$trackerUrl = self::getTrackerUrl();
- $response = Piwik_Http::fetchRemoteFile($trackerUrl);
+ $response = Piwik_Http::fetchRemoteFile($trackerUrl);
self::assertTrue(strpos($response, 'is a free open source web') !== false, 'Piwik empty request response not correct: ' . $response);
$t = self::getTracker($idSite, $dateTime, $defaultInit = true);
@@ -60,8 +59,8 @@ class Test_Piwik_Fixture_InvalidVisits extends Test_Piwik_BaseFixture
// Test IP Exclusion works with or without IP exclusion
foreach (array(false, true) as $enable) {
$excludedIp = '154.1.12.34';
- Piwik_SitesManager_API::getInstance()->updateSite($idSite, 'new site name', $url = array('http://site.com'), $ecommerce = 0, $ss = 1, $ss_kwd = '', $ss_cat = '', $excludedIp . ',1.2.3.4', $excludedQueryParameters = null, $timezone = null, $currency = null, $group = null, $startDate = null, $excludedUserAgents = 'excludeduseragentstring');
-
+ Piwik_SitesManager_API::getInstance()->updateSite($idSite, 'new site name', $url = array('http://site.com'), $ecommerce = 0, $ss = 1, $ss_kwd = '', $ss_cat = '', $excludedIp . ',1.2.3.4', $excludedQueryParameters = null, $timezone = null, $currency = null, $group = null, $startDate = null, $excludedUserAgents = 'excludeduseragentstring');
+
// Enable IP Anonymization
$t->DEBUG_APPEND_URL = '&forceIpAnonymization=' . (int)$enable;
@@ -69,7 +68,7 @@ class Test_Piwik_Fixture_InvalidVisits extends Test_Piwik_BaseFixture
$t->setUserAgent('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729) (excludeduseragentstring)');
$t->setIp('211.1.2.3');
self::checkResponse($t->doTrackPageView('visit from excluded User Agent'));
-
+
// test w/ global excluded User Agent
$t->setUserAgent('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729) (globalexcludeduseragent)');
$t->setIp('211.1.2.3');
@@ -79,7 +78,7 @@ class Test_Piwik_Fixture_InvalidVisits extends Test_Piwik_BaseFixture
$t->setUserAgent('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729)'); // restore normal user agent
$t->setIp($excludedIp);
self::checkResponse($t->doTrackPageView('visit from IP excluded'));
-
+
// test with global list of excluded IPs
$excludedIpBis = '145.5.3.4';
Piwik_SitesManager_API::getInstance()->setGlobalExcludedIps($excludedIpBis);
diff --git a/tests/PHPUnit/Fixtures/ManySitesImportedLogs.php b/tests/PHPUnit/Fixtures/ManySitesImportedLogs.php
index de486be2b2..276cb761ef 100644
--- a/tests/PHPUnit/Fixtures/ManySitesImportedLogs.php
+++ b/tests/PHPUnit/Fixtures/ManySitesImportedLogs.php
@@ -2,7 +2,7 @@
/**
* Piwik - Open source web analytics
*
- * @link http://piwik.org
+ * @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
@@ -11,160 +11,157 @@
*/
class Test_Piwik_Fixture_ManySitesImportedLogs extends Test_Piwik_BaseFixture
{
- public $dateTime = '2010-03-06 11:22:33';
- public $idSite = 1;
- public $idSite2 = 2;
- public $idGoal = 1;
-
- public function setUp()
- {
+ public $dateTime = '2010-03-06 11:22:33';
+ public $idSite = 1;
+ public $idSite2 = 2;
+ public $idGoal = 1;
+
+ public function setUp()
+ {
$this->setUpWebsitesAndGoals();
- self::downloadGeoIpDbs();
-
- Piwik_UserCountry_LocationProvider::$providers = null;
- Piwik_UserCountry_LocationProvider_GeoIp::$geoIPDatabaseDir = 'tests/lib/geoip-files';
- Piwik_UserCountry_LocationProvider::setCurrentProvider('geoip_php');
-
+ self::downloadGeoIpDbs();
+
+ Piwik_UserCountry_LocationProvider::$providers = null;
+ Piwik_UserCountry_LocationProvider_GeoIp::$geoIPDatabaseDir = 'tests/lib/geoip-files';
+ Piwik_UserCountry_LocationProvider::setCurrentProvider('geoip_php');
+
$this->trackVisits();
- }
-
- public function tearDown()
- {
- Piwik_UserCountry_LocationProvider::$providers = null;
- Piwik_UserCountry_LocationProvider_GeoIp::$geoIPDatabaseDir = 'tests/lib/geoip-files';
- Piwik_UserCountry_LocationProvider::setCurrentProvider('default');
- }
-
+ }
+
+ public function tearDown()
+ {
+ Piwik_UserCountry_LocationProvider::$providers = null;
+ Piwik_UserCountry_LocationProvider_GeoIp::$geoIPDatabaseDir = 'tests/lib/geoip-files';
+ Piwik_UserCountry_LocationProvider::setCurrentProvider('default');
+ }
+
public function setUpWebsitesAndGoals()
{
- // for conversion testing
- self::createWebsite($this->dateTime);
- Piwik_Goals_API::getInstance()->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5);
- self::createWebsite($this->dateTime, $ecommerce = 0, $siteName = 'Piwik test two',
- $siteUrl = 'http://example-site-two.com');
+ // for conversion testing
+ self::createWebsite($this->dateTime);
+ Piwik_Goals_API::getInstance()->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5);
+ self::createWebsite($this->dateTime, $ecommerce = 0, $siteName = 'Piwik test two',
+ $siteUrl = 'http://example-site-two.com');
}
-
+
private function trackVisits()
{
- $this->logVisitsWithStaticResolver();
- $this->logVisitsWithAllEnabled();
- $this->replayLogFile();
- $this->logCustomFormat();
+ $this->logVisitsWithStaticResolver();
+ $this->logVisitsWithAllEnabled();
+ $this->replayLogFile();
+ $this->logCustomFormat();
+ }
+
+ /**
+ * Logs a couple visits for Aug 9, Aug 10, Aug 11 of 2012, for site we create.
+ */
+ private function logVisitsWithStaticResolver()
+ {
+ $logFile = PIWIK_INCLUDE_PATH . '/tests/resources/fake_logs.log'; # log file
+
+ $opts = array('--idsite' => $this->idSite,
+ '--token-auth' => self::getTokenAuth(),
+ '--recorders' => '4',
+ '--recorder-max-payload-size' => '2');
+
+ self::executeLogImporter($logFile, $opts);
+ }
+
+ /**
+ * Logs a couple visits for the site we created and two new sites that do not
+ * exist yet. Visits are from Aug 12, 13 & 14 of 2012.
+ */
+ public function logVisitsWithDynamicResolver()
+ {
+ $logFile = PIWIK_INCLUDE_PATH . '/tests/resources/fake_logs_dynamic.log'; # log file
+
+ $opts = array('--add-sites-new-hosts' => false,
+ '--token-auth' => self::getTokenAuth(),
+ '--recorders' => '4',
+ '--recorder-max-payload-size' => '1');
+
+ self::executeLogImporter($logFile, $opts);
+ }
+
+ /**
+ * Logs a couple visits for the site we created w/ all log importer options
+ * enabled. Visits are for Aug 11 of 2012.
+ */
+ private function logVisitsWithAllEnabled()
+ {
+ $logFile = PIWIK_INCLUDE_PATH . '/tests/resources/fake_logs_enable_all.log';
+
+ $opts = array('--idsite' => $this->idSite,
+ '--token-auth' => self::getTokenAuth(),
+ '--recorders' => '4',
+ '--recorder-max-payload-size' => '2',
+ '--enable-static' => false,
+ '--enable-bots' => false,
+ '--enable-http-errors' => false,
+ '--enable-http-redirects' => false,
+ '--enable-reverse-dns' => false);
+
+ self::executeLogImporter($logFile, $opts);
+ }
+
+ /**
+ * Logs a couple visit using log entries that are tracking requests to a piwik.php file.
+ * Adds two visits to idSite=1 and two to non-existant sites.
+ */
+ private function replayLogFile()
+ {
+ $logFile = PIWIK_INCLUDE_PATH . '/tests/resources/fake_logs_replay.log';
+
+ $opts = array('--token-auth' => self::getTokenAuth(),
+ '--recorders' => '4',
+ '--recorder-max-payload-size' => '2',
+ '--replay-tracking' => false);
+
+ self::executeLogImporter($logFile, $opts);
+ }
+
+ /**
+ * Imports a log file in custom format that contains generation time
+ */
+ private function logCustomFormat()
+ {
+ $logFile = PIWIK_INCLUDE_PATH . '/tests/resources/fake_logs_custom.log';
+
+ $opts = array('--idsite' => $this->idSite,
+ '--token-auth' => self::getTokenAuth(),
+ '--log-format-regex' => '(?P<ip>\S+) - - \[(?P<date>.*?) (?P<timezone>.*?)\] (?P<status>\S+) '
+ . '\"\S+ (?P<path>.*?) \S+\" (?P<generation_time_micro>\S+)');
+
+ self::executeLogImporter($logFile, $opts);
}
- /**
- * Logs a couple visits for Aug 9, Aug 10, Aug 11 of 2012, for site we create.
- */
- private function logVisitsWithStaticResolver()
+ private static function executeLogImporter($logFile, $options)
{
- $logFile = PIWIK_INCLUDE_PATH.'/tests/resources/fake_logs.log'; # log file
-
- $opts = array('--idsite' => $this->idSite,
- '--token-auth' => self::getTokenAuth(),
- '--recorders' => '4',
- '--recorder-max-payload-size' => '2');
-
- self::executeLogImporter($logFile, $opts);
- }
-
- /**
- * Logs a couple visits for the site we created and two new sites that do not
- * exist yet. Visits are from Aug 12, 13 & 14 of 2012.
- */
- public function logVisitsWithDynamicResolver()
- {
- $logFile = PIWIK_INCLUDE_PATH.'/tests/resources/fake_logs_dynamic.log'; # log file
-
- $opts = array('--add-sites-new-hosts' => false,
- '--token-auth' => self::getTokenAuth(),
- '--recorders' => '4',
- '--recorder-max-payload-size' => '1');
-
- self::executeLogImporter($logFile, $opts);
- }
-
- /**
- * Logs a couple visits for the site we created w/ all log importer options
- * enabled. Visits are for Aug 11 of 2012.
- */
- private function logVisitsWithAllEnabled()
- {
- $logFile = PIWIK_INCLUDE_PATH.'/tests/resources/fake_logs_enable_all.log';
-
- $opts = array('--idsite' => $this->idSite,
- '--token-auth' => self::getTokenAuth(),
- '--recorders' => '4',
- '--recorder-max-payload-size' => '2',
- '--enable-static' => false,
- '--enable-bots' => false,
- '--enable-http-errors' => false,
- '--enable-http-redirects' => false,
- '--enable-reverse-dns' => false);
-
- self::executeLogImporter($logFile, $opts);
- }
-
- /**
- * Logs a couple visit using log entries that are tracking requests to a piwik.php file.
- * Adds two visits to idSite=1 and two to non-existant sites.
- */
- private function replayLogFile()
- {
- $logFile = PIWIK_INCLUDE_PATH.'/tests/resources/fake_logs_replay.log';
-
- $opts = array('--token-auth' => self::getTokenAuth(),
- '--recorders' => '4',
- '--recorder-max-payload-size' => '2',
- '--replay-tracking' => false);
-
- self::executeLogImporter($logFile, $opts);
- }
-
- /**
- * Imports a log file in custom format that contains generation time
- */
- private function logCustomFormat()
- {
- $logFile = PIWIK_INCLUDE_PATH.'/tests/resources/fake_logs_custom.log';
-
- $opts = array('--idsite' => $this->idSite,
- '--token-auth' => self::getTokenAuth(),
- '--log-format-regex' => '(?P<ip>\S+) - - \[(?P<date>.*?) (?P<timezone>.*?)\] (?P<status>\S+) '
- . '\"\S+ (?P<path>.*?) \S+\" (?P<generation_time_micro>\S+)');
-
- self::executeLogImporter($logFile, $opts);
- }
-
- private static function executeLogImporter( $logFile, $options )
- {
- $python = Piwik_Common::isWindows() ? "C:\Python27\python.exe" : 'python';
-
- // create the command
- $cmd = $python
- . ' "'.PIWIK_INCLUDE_PATH.'/misc/log-analytics/import_logs.py" ' # script loc
- . '-ddd ' // debug
- . '--url="'.self::getRootUrl().'tests/PHPUnit/proxy/" ' # proxy so that piwik uses test config files
- ;
-
- foreach ($options as $name => $value)
- {
- $cmd .= $name;
- if ($value !== false)
- {
- $cmd .= '="'.$value.'"';
- }
- $cmd .= ' ';
- }
-
- $cmd .= '"'.$logFile.'" 2>&1';
-
- // run the command
- exec($cmd, $output, $result);
- if ($result !== 0)
- {
- throw new Exception("log importer failed: ".implode("\n", $output)."\n\ncommand used: $cmd");
- }
-
- return $output;
+ $python = Piwik_Common::isWindows() ? "C:\Python27\python.exe" : 'python';
+
+ // create the command
+ $cmd = $python
+ . ' "' . PIWIK_INCLUDE_PATH . '/misc/log-analytics/import_logs.py" ' # script loc
+ . '-ddd ' // debug
+ . '--url="' . self::getRootUrl() . 'tests/PHPUnit/proxy/" ' # proxy so that piwik uses test config files
+ ;
+
+ foreach ($options as $name => $value) {
+ $cmd .= $name;
+ if ($value !== false) {
+ $cmd .= '="' . $value . '"';
+ }
+ $cmd .= ' ';
+ }
+
+ $cmd .= '"' . $logFile . '" 2>&1';
+
+ // run the command
+ exec($cmd, $output, $result);
+ if ($result !== 0) {
+ throw new Exception("log importer failed: " . implode("\n", $output) . "\n\ncommand used: $cmd");
+ }
+
+ return $output;
}
}
diff --git a/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php b/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php
index 4aa03d8f6a..33f2f7006c 100644
--- a/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php
+++ b/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php
@@ -2,7 +2,7 @@
/**
* Piwik - Open source web analytics
*
- * @link http://piwik.org
+ * @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
@@ -14,176 +14,166 @@ require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/MockLocationProvider.php';
*/
class Test_Piwik_Fixture_ManyVisitsWithGeoIP extends Test_Piwik_BaseFixture
{
- const GEOIP_IMPL_TO_TEST = 'geoip_php';
-
- public $idSite = 1;
- public $dateTime = '2010-01-03 11:22:33';
-
- public $ips = array(
- '194.57.91.215', // in Besançon, FR (unicode city name)
- '::ffff:137.82.130.49', // in British Columbia (mapped ipv4)
- '137.82.130.0', // anonymization tests
- '137.82.0.0',
- '2001:db8:85a3:0:0:8a2e:370:7334', // ipv6 (geoip lookup not supported)
- '113.62.1.1', // in Lhasa, Tibet
- '151.100.101.92', // in Rome, Italy (using country DB, so only Italy will show)
- '103.29.196.229', // in Indonesia (Bali), (only Indonesia will show up)
- );
-
- public function setUp()
- {
- $this->setUpWebsitesAndGoals();
- self::downloadGeoIpDbs();
-
- $this->setMockLocationProvider();
- $this->trackVisits(9, false);
-
- $this->setLocationProvider('GeoIPCity.dat');
- $this->trackVisits(2, true, $useLocal = false);
- $this->trackVisits(4, true, $useLocal = false, $doBulk = true);
-
- $this->setLocationProvider('GeoIP.dat');
- $this->trackVisits(2, true);
-
- $this->trackOtherVisits();
-
- $this->setLocationProvider('GeoIPCity.dat');
- }
-
- public function tearDown()
- {
- $this->unsetLocationProvider();
- }
-
- private function setUpWebsitesAndGoals()
- {
- self::createWebsite($this->dateTime, 0, "Site 1");
+ const GEOIP_IMPL_TO_TEST = 'geoip_php';
+
+ public $idSite = 1;
+ public $dateTime = '2010-01-03 11:22:33';
+
+ public $ips = array(
+ '194.57.91.215', // in Besançon, FR (unicode city name)
+ '::ffff:137.82.130.49', // in British Columbia (mapped ipv4)
+ '137.82.130.0', // anonymization tests
+ '137.82.0.0',
+ '2001:db8:85a3:0:0:8a2e:370:7334', // ipv6 (geoip lookup not supported)
+ '113.62.1.1', // in Lhasa, Tibet
+ '151.100.101.92', // in Rome, Italy (using country DB, so only Italy will show)
+ '103.29.196.229', // in Indonesia (Bali), (only Indonesia will show up)
+ );
+
+ public function setUp()
+ {
+ $this->setUpWebsitesAndGoals();
+ self::downloadGeoIpDbs();
+
+ $this->setMockLocationProvider();
+ $this->trackVisits(9, false);
+
+ $this->setLocationProvider('GeoIPCity.dat');
+ $this->trackVisits(2, true, $useLocal = false);
+ $this->trackVisits(4, true, $useLocal = false, $doBulk = true);
+
+ $this->setLocationProvider('GeoIP.dat');
+ $this->trackVisits(2, true);
+
+ $this->trackOtherVisits();
+
+ $this->setLocationProvider('GeoIPCity.dat');
+ }
+
+ public function tearDown()
+ {
+ $this->unsetLocationProvider();
+ }
+
+ private function setUpWebsitesAndGoals()
+ {
+ self::createWebsite($this->dateTime, 0, "Site 1");
Piwik_Goals_API::getInstance()->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5);
- }
-
- private function trackVisits( $visitorCount, $setIp = false, $useLocal = true, $doBulk = false )
- {
- $dateTime = $this->dateTime;
- $idSite = $this->idSite;
-
- // use local tracker so mock location provider can be used
- $t = self::getTracker($idSite, $dateTime, $defaultInit = true, $useLocal);
- if ($doBulk)
- {
- $t->enableBulkTracking();
- $t->setTokenAuth(self::getTokenAuth());
- }
- for ($i = 0; $i != $visitorCount; ++$i)
- {
- $t->setNewVisitorId();
- if ($setIp)
- {
- $t->setIp(current($this->ips));
- next($this->ips);
- }
- else
- {
- $t->setIp("1.2.4.$i");
- }
-
- // first visit
- $date = Piwik_Date::factory($dateTime)->addDay($i);
- $t->setForceVisitDateTime($date->getDatetime());
- $t->setUrl("http://piwik.net/grue/lair");
- $r = $t->doTrackPageView('It\'s pitch black...');
- if (!$doBulk)
- {
- self::checkResponse($r);
- }
-
- // second visit
- $date = $date->addHour(1);
- $t->setForceVisitDateTime($date->getDatetime());
- $t->setUrl("http://piwik.net/space/quest/iv");
- $r = $t->doTrackPageView("Space Quest XII");
- if (!$doBulk)
- {
- self::checkResponse($r);
- }
- }
- if ($doBulk)
- {
- self::checkResponse($t->doBulkTrack());
- }
- }
-
- private function trackOtherVisits()
- {
- $dateTime = $this->dateTime;
- $idSite = $this->idSite;
-
- $t = self::getTracker($idSite, $dateTime, $defaultInit = true);
- $t->setTokenAuth(self::getTokenAuth());
- $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addDay(20)->getDatetime());
- $t->setIp('194.57.91.215');
- $t->setCountry('us');
- $t->setRegion('CA');
- $t->setCity('not a city');
- $t->setLatitude(1);
- $t->setLongitude(2);
- $t->setUrl("http://piwik.net/grue/lair");
- self::checkResponse($t->doTrackPageView('It\'s pitch black...'));
- }
-
- private function setLocationProvider( $file )
- {
- Piwik_UserCountry_LocationProvider_GeoIp::$dbNames['loc'] = array($file);
- Piwik_UserCountry_LocationProvider_GeoIp::$geoIPDatabaseDir = 'tests/lib/geoip-files';
- Piwik_UserCountry_LocationProvider::$providers = null;
- Piwik_UserCountry_LocationProvider::setCurrentProvider(self::GEOIP_IMPL_TO_TEST);
-
- if (Piwik_UserCountry_LocationProvider::getCurrentProviderId() !== self::GEOIP_IMPL_TO_TEST)
- {
- throw new Exception("Failed to set the current location provider to '".self::GEOIP_IMPL_TO_TEST."'.");
- }
-
- $possibleFiles = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames['loc'];
- if (Piwik_UserCountry_LocationProvider_GeoIp::getPathToGeoIpDatabase($possibleFiles) === false)
- {
- throw new Exception("The GeoIP location provider cannot find the '$file' file! Tests will fail.");
- }
- }
-
- private function setMockLocationProvider()
- {
- Piwik_UserCountry_LocationProvider::$providers = null;
- Piwik_UserCountry_LocationProvider::setCurrentProvider('mock_provider');
- MockLocationProvider::$locations = array(
- self::makeLocation('Stratford-upon-Avon', 'P3', 'gb', 123.456, 21.321), // template location
-
- // same region, different city, same country
- self::makeLocation('Nuneaton and Bedworth', 'P3', 'gb'),
-
- // same region, city & country (different lat/long)
- self::makeLocation('Stratford-upon-Avon', 'P3', 'gb', 124.456, 22.231),
-
- // same country, different region & city
- self::makeLocation('London', 'H9', 'gb'),
-
- // same country, different region, same city
- self::makeLocation('Stratford-upon-Avon', 'G5', 'gb'),
-
- // different country, diff region, same city
- self::makeLocation('Stratford-upon-Avon', '66', 'ru'),
-
- // different country, diff region (same as last), different city
- self::makeLocation('Hluboká nad Vltavou', '66', 'ru'),
-
- // different country, diff region (same as last), same city
- self::makeLocation('Stratford-upon-Avon', '66', 'mk'),
-
- // unknown location
- self::makeLocation(null, null, null),
- );
- }
-
- private function unsetLocationProvider()
- {
- Piwik_UserCountry_LocationProvider::setCurrentProvider('default');
- }
+ }
+
+ private function trackVisits($visitorCount, $setIp = false, $useLocal = true, $doBulk = false)
+ {
+ $dateTime = $this->dateTime;
+ $idSite = $this->idSite;
+
+ // use local tracker so mock location provider can be used
+ $t = self::getTracker($idSite, $dateTime, $defaultInit = true, $useLocal);
+ if ($doBulk) {
+ $t->enableBulkTracking();
+ $t->setTokenAuth(self::getTokenAuth());
+ }
+ for ($i = 0; $i != $visitorCount; ++$i) {
+ $t->setNewVisitorId();
+ if ($setIp) {
+ $t->setIp(current($this->ips));
+ next($this->ips);
+ } else {
+ $t->setIp("1.2.4.$i");
+ }
+
+ // first visit
+ $date = Piwik_Date::factory($dateTime)->addDay($i);
+ $t->setForceVisitDateTime($date->getDatetime());
+ $t->setUrl("http://piwik.net/grue/lair");
+ $r = $t->doTrackPageView('It\'s pitch black...');
+ if (!$doBulk) {
+ self::checkResponse($r);
+ }
+
+ // second visit
+ $date = $date->addHour(1);
+ $t->setForceVisitDateTime($date->getDatetime());
+ $t->setUrl("http://piwik.net/space/quest/iv");
+ $r = $t->doTrackPageView("Space Quest XII");
+ if (!$doBulk) {
+ self::checkResponse($r);
+ }
+ }
+ if ($doBulk) {
+ self::checkResponse($t->doBulkTrack());
+ }
+ }
+
+ private function trackOtherVisits()
+ {
+ $dateTime = $this->dateTime;
+ $idSite = $this->idSite;
+
+ $t = self::getTracker($idSite, $dateTime, $defaultInit = true);
+ $t->setTokenAuth(self::getTokenAuth());
+ $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addDay(20)->getDatetime());
+ $t->setIp('194.57.91.215');
+ $t->setCountry('us');
+ $t->setRegion('CA');
+ $t->setCity('not a city');
+ $t->setLatitude(1);
+ $t->setLongitude(2);
+ $t->setUrl("http://piwik.net/grue/lair");
+ self::checkResponse($t->doTrackPageView('It\'s pitch black...'));
+ }
+
+ private function setLocationProvider($file)
+ {
+ Piwik_UserCountry_LocationProvider_GeoIp::$dbNames['loc'] = array($file);
+ Piwik_UserCountry_LocationProvider_GeoIp::$geoIPDatabaseDir = 'tests/lib/geoip-files';
+ Piwik_UserCountry_LocationProvider::$providers = null;
+ Piwik_UserCountry_LocationProvider::setCurrentProvider(self::GEOIP_IMPL_TO_TEST);
+
+ if (Piwik_UserCountry_LocationProvider::getCurrentProviderId() !== self::GEOIP_IMPL_TO_TEST) {
+ throw new Exception("Failed to set the current location provider to '" . self::GEOIP_IMPL_TO_TEST . "'.");
+ }
+
+ $possibleFiles = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames['loc'];
+ if (Piwik_UserCountry_LocationProvider_GeoIp::getPathToGeoIpDatabase($possibleFiles) === false) {
+ throw new Exception("The GeoIP location provider cannot find the '$file' file! Tests will fail.");
+ }
+ }
+
+ private function setMockLocationProvider()
+ {
+ Piwik_UserCountry_LocationProvider::$providers = null;
+ Piwik_UserCountry_LocationProvider::setCurrentProvider('mock_provider');
+ MockLocationProvider::$locations = array(
+ self::makeLocation('Stratford-upon-Avon', 'P3', 'gb', 123.456, 21.321), // template location
+
+ // same region, different city, same country
+ self::makeLocation('Nuneaton and Bedworth', 'P3', 'gb'),
+
+ // same region, city & country (different lat/long)
+ self::makeLocation('Stratford-upon-Avon', 'P3', 'gb', 124.456, 22.231),
+
+ // same country, different region & city
+ self::makeLocation('London', 'H9', 'gb'),
+
+ // same country, different region, same city
+ self::makeLocation('Stratford-upon-Avon', 'G5', 'gb'),
+
+ // different country, diff region, same city
+ self::makeLocation('Stratford-upon-Avon', '66', 'ru'),
+
+ // different country, diff region (same as last), different city
+ self::makeLocation('Hluboká nad Vltavou', '66', 'ru'),
+
+ // different country, diff region (same as last), same city
+ self::makeLocation('Stratford-upon-Avon', '66', 'mk'),
+
+ // unknown location
+ self::makeLocation(null, null, null),
+ );
+ }
+
+ private function unsetLocationProvider()
+ {
+ Piwik_UserCountry_LocationProvider::setCurrentProvider('default');
+ }
}
diff --git a/tests/PHPUnit/Fixtures/ManyVisitsWithMockLocationProvider.php b/tests/PHPUnit/Fixtures/ManyVisitsWithMockLocationProvider.php
index ea116f545a..6c8d2a2834 100644
--- a/tests/PHPUnit/Fixtures/ManyVisitsWithMockLocationProvider.php
+++ b/tests/PHPUnit/Fixtures/ManyVisitsWithMockLocationProvider.php
@@ -13,197 +13,185 @@ require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/MockLocationProvider.php';
*/
class Test_Piwik_Fixture_ManyVisitsWithMockLocationProvider extends Test_Piwik_BaseFixture
{
- public $idSite = 1;
- public $dateTime = '2010-01-03 01:22:33';
-
- public function setUp()
- {
- $this->setUpWebsitesAndGoals();
- $this->setMockLocationProvider();
- $this->trackVisits();
- }
-
- public function tearDown()
- {
- $this->unsetMockLocationProvider();
- }
-
- private function setUpWebsitesAndGoals()
- {
- self::createWebsite($this->dateTime);
- }
-
- private function trackVisits()
- {
- $linuxFirefoxA = "Mozilla/5.0 (X11; Linux i686; rv:6.0) Gecko/20100101 Firefox/6.0";
- $win7FirefoxA = "Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.1.6) Gecko/20100101 Firefox/6.0";
- $win7ChromeA = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.38 Safari/532.0";
- $linuxChromeA = "Mozilla/5.0 (X11; Linux i686; rv:6.0) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.38 Safari/532.0";
- $linuxSafariA = "Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+";
- $iPadSafariA = "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+";
- $iPadFirefoxB = "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) Gecko/20100101 Firefox/14.0.1";
- $androidFirefoxB = "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) Gecko/20100101 Firefox/14.0.1";
- $androidChromeB = "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1";
- $androidIEA = "Mozilla/5.0 (compatible; MSIE 10.6; Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0";
- $iPhoneOperaA = "Opera/9.80 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; ja-jp) Presto/2.9.181 Version/12.00";
- $win8IEB = "Mozilla/5.0 (compatible; MSIE 10.0; Windows 8; Trident/5.0)";
- $winVistaIEB = "Mozilla/5.0 (compatible; MSIE 10.0; Windows Vista; Trident/5.0)";
- $osxOperaB = "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52";
- $userAgents = array(
- $linuxFirefoxA, $linuxFirefoxA, $win7FirefoxA, $win7ChromeA, $linuxChromeA, $linuxSafariA,
- $iPadSafariA, $iPadFirefoxB, $androidFirefoxB, $androidChromeB, $androidIEA, $iPhoneOperaA,
- $win8IEB, $winVistaIEB, $osxOperaB
- );
-
- $resolutions = array(
- "1920x1080", "1920x1080", "1920x1080", "1920x1080", "1366x768", "1366x768", "1366x768",
- "1280x1024", "1280x1024", "1280x1024", "1680x1050", "1680x1050", "1024x768", "800x600",
- "320x480"
- );
-
- $referrers = array(
- // website referrers (8)
- 'http://whatever0.com/0', 'http://whatever0.com/0', 'http://whatever0.com/1', 'http://whatever0.com/2',
- 'http://whatever1.com/0', 'http://whatever.com1/1', 'http://whatever1.com/2', 'http://whatever3.com/3',
-
- // search engines w/ keyword (12)
- 'http://www.google.com/search?q=this+search+term',
- 'http://www.google.com/search?q=that+search+term',
- 'http://search.yahoo.com/search?p=this+search+term',
- 'http://search.yahoo.com/search?p=that+search+term',
- 'http://www.ask.com/web?q=this+search+term',
- 'http://www.bing.com/search?q=search+term+1',
- 'http://search.babylon.com/?q=search+term+2',
- 'http://alexa.com/search?q=search+term+2',
- 'http://www.google.com/search?q=search+term+3',
- 'http://search.yahoo.com/search?p=search+term+4',
- 'http://www.ask.com/web?q=search+term+3',
- 'http://www.bing.com/search?q=search+term+4',
- );
-
- $customVars = array(
- 'name' => array('thing0', 'thing1', 'thing2', 'thing3', 'thing4', 'thing5', 'thing6', 'thing7',
- 'thing8', 'thing9', 'thing10', 'thing11', 'thing12', 'thing13', 'thing14',
- 'thing15', 'thing16', 'thing17', 'thing18', 'thing19'),
- 'rating' => array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20),
- 'tweeted' => array('y', 'n', 'm', 'n', 'y', 'n', 'y', 'n', 'y', 'n', 'y', 'n', 'y', 'n', 'y', 'n',
- 'm', 'n', 'm', 'n'),
- 'liked' => array('yes', 'y', 'y', 'no', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y',
- 'y', 'y', 'no', 'n'),
- );
- $downloadCustomVars = array(
- 'size' => array(1024, 1024, 1024, 2048, 2048, 3072, 3072, 3072, 3072, 4096, 4096, 4096,
- 512, 512, 256, 128, 64, 32, 48, 48)
- );
-
- $visitorCounter = 0;
- $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true, $useLocal = true);
-
- // track regular actions
- $this->trackActions($t, $visitorCounter, 'pageview', $userAgents, $resolutions, $referrers, $customVars);
-
- // track downloads
- $this->trackActions($t, $visitorCounter, 'download', $userAgents, $resolutions, null, $downloadCustomVars);
-
- // track outlinks
- $this->trackActions($t, $visitorCounter, 'outlink', $userAgents, $resolutions);
- }
-
- private function trackActions($t, &$visitorCounter, $actionType, $userAgents, $resolutions,
- $referrers = null, $customVars = null)
- {
- for ($i = 0; $i != 5; ++$i, ++$visitorCounter)
- {
- $visitDate = Piwik_Date::factory($this->dateTime);
-
- $t->setNewVisitorId();
- $t->setIp("156.5.3.$visitorCounter");
-
- $t->setUserAgent($userAgents[$visitorCounter]);
- list($w, $h) = explode('x', $resolutions[$visitorCounter]);
- $t->setResolution((int)$w, (int)$h);
-
- // one visit to root url
- $t->setUrl("http://piwik.net/$visitorCounter/");
- $t->setUrlReferrer(null);
- $t->setForceVisitDateTime($visitDate->getDatetime());
- $this->trackAction($t, $actionType, $visitorCounter, null);
-
- for ($j = 0; $j != 4; ++$j)
- {
- // NOTE: to test referers w/o creating too many visits, we don't actually track 4 actions, but
- // 4 separate visits
- $actionDate = $visitDate->addHour($j + 1);
-
- $actionIdx = $i * 4 + $j;
- $actionNum = $visitorCounter * 4 + $j;
-
- $t->setUrl("http://piwik.net/$visitorCounter/$actionNum");
- $t->setForceVisitDateTime($actionDate->getDatetime());
-
- if (!is_null($referrers))
- {
- $t->setUrlReferrer($referrers[$actionIdx]);
- }
- else
- {
- $t->setUrlReferrer(null);
- }
-
- if (!is_null($customVars))
- {
- $k = 1;
- foreach ($customVars as $name => $values)
- {
- $value = $values[$actionIdx];
- $t->setCustomVariable($k, $name, $value, $scope = 'page');
-
- ++$k;
- }
- }
-
- $this->trackAction($t, $actionType, $visitorCounter, $actionNum);
- }
- }
- }
-
- private function trackAction($t, $actionType, $visitorCounter, $actionNum)
- {
- if ($actionType == 'pageview')
- {
- self::checkResponse($t->doTrackPageView(
- is_null($actionNum) ? "title_$visitorCounter" : "title_$visitorCounter / title_$actionNum"));
- }
- else if ($actionType == 'download')
- {
- $root = is_null($actionNum) ? "http://cloudsite$visitorCounter.com"
- : "http://cloudsite$visitorCounter.com/$actionNum";
-
- self::checkResponse($t->doTrackAction("$root/download", 'download'));
- }
- else if ($actionType == 'outlink')
- {
- self::checkResponse($t->doTrackAction(is_null($actionNum) ? "http://othersite$visitorCounter.com/"
- : "http://othersite$visitorCounter.com/$actionNum/", 'link'));
- }
- }
-
- private function setMockLocationProvider()
- {
- Piwik_UserCountry_LocationProvider::setCurrentProvider('mock_provider');
- MockLocationProvider::$locations = array(
- self::makeLocation('Toronto', 'ON', 'CA'),
-
- self::makeLocation('Nice', 'B8', 'FR'),
-
- self::makeLocation('Melbourne', '07', 'AU'),
-
- self::makeLocation('Yokohama', '19', 'JP'),
- );
- }
-
- private function unsetMockLocationProvider()
- {
- Piwik_UserCountry_LocationProvider::setCurrentProvider('default');
- }
+ public $idSite = 1;
+ public $dateTime = '2010-01-03 01:22:33';
+
+ public function setUp()
+ {
+ $this->setUpWebsitesAndGoals();
+ $this->setMockLocationProvider();
+ $this->trackVisits();
+ }
+
+ public function tearDown()
+ {
+ $this->unsetMockLocationProvider();
+ }
+
+ private function setUpWebsitesAndGoals()
+ {
+ self::createWebsite($this->dateTime);
+ }
+
+ private function trackVisits()
+ {
+ $linuxFirefoxA = "Mozilla/5.0 (X11; Linux i686; rv:6.0) Gecko/20100101 Firefox/6.0";
+ $win7FirefoxA = "Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.1.6) Gecko/20100101 Firefox/6.0";
+ $win7ChromeA = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.38 Safari/532.0";
+ $linuxChromeA = "Mozilla/5.0 (X11; Linux i686; rv:6.0) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.38 Safari/532.0";
+ $linuxSafariA = "Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+";
+ $iPadSafariA = "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+";
+ $iPadFirefoxB = "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) Gecko/20100101 Firefox/14.0.1";
+ $androidFirefoxB = "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) Gecko/20100101 Firefox/14.0.1";
+ $androidChromeB = "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1";
+ $androidIEA = "Mozilla/5.0 (compatible; MSIE 10.6; Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0";
+ $iPhoneOperaA = "Opera/9.80 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; ja-jp) Presto/2.9.181 Version/12.00";
+ $win8IEB = "Mozilla/5.0 (compatible; MSIE 10.0; Windows 8; Trident/5.0)";
+ $winVistaIEB = "Mozilla/5.0 (compatible; MSIE 10.0; Windows Vista; Trident/5.0)";
+ $osxOperaB = "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52";
+ $userAgents = array(
+ $linuxFirefoxA, $linuxFirefoxA, $win7FirefoxA, $win7ChromeA, $linuxChromeA, $linuxSafariA,
+ $iPadSafariA, $iPadFirefoxB, $androidFirefoxB, $androidChromeB, $androidIEA, $iPhoneOperaA,
+ $win8IEB, $winVistaIEB, $osxOperaB
+ );
+
+ $resolutions = array(
+ "1920x1080", "1920x1080", "1920x1080", "1920x1080", "1366x768", "1366x768", "1366x768",
+ "1280x1024", "1280x1024", "1280x1024", "1680x1050", "1680x1050", "1024x768", "800x600",
+ "320x480"
+ );
+
+ $referrers = array(
+ // website referrers (8)
+ 'http://whatever0.com/0', 'http://whatever0.com/0', 'http://whatever0.com/1', 'http://whatever0.com/2',
+ 'http://whatever1.com/0', 'http://whatever.com1/1', 'http://whatever1.com/2', 'http://whatever3.com/3',
+
+ // search engines w/ keyword (12)
+ 'http://www.google.com/search?q=this+search+term',
+ 'http://www.google.com/search?q=that+search+term',
+ 'http://search.yahoo.com/search?p=this+search+term',
+ 'http://search.yahoo.com/search?p=that+search+term',
+ 'http://www.ask.com/web?q=this+search+term',
+ 'http://www.bing.com/search?q=search+term+1',
+ 'http://search.babylon.com/?q=search+term+2',
+ 'http://alexa.com/search?q=search+term+2',
+ 'http://www.google.com/search?q=search+term+3',
+ 'http://search.yahoo.com/search?p=search+term+4',
+ 'http://www.ask.com/web?q=search+term+3',
+ 'http://www.bing.com/search?q=search+term+4',
+ );
+
+ $customVars = array(
+ 'name' => array('thing0', 'thing1', 'thing2', 'thing3', 'thing4', 'thing5', 'thing6', 'thing7',
+ 'thing8', 'thing9', 'thing10', 'thing11', 'thing12', 'thing13', 'thing14',
+ 'thing15', 'thing16', 'thing17', 'thing18', 'thing19'),
+ 'rating' => array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20),
+ 'tweeted' => array('y', 'n', 'm', 'n', 'y', 'n', 'y', 'n', 'y', 'n', 'y', 'n', 'y', 'n', 'y', 'n',
+ 'm', 'n', 'm', 'n'),
+ 'liked' => array('yes', 'y', 'y', 'no', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y',
+ 'y', 'y', 'no', 'n'),
+ );
+ $downloadCustomVars = array(
+ 'size' => array(1024, 1024, 1024, 2048, 2048, 3072, 3072, 3072, 3072, 4096, 4096, 4096,
+ 512, 512, 256, 128, 64, 32, 48, 48)
+ );
+
+ $visitorCounter = 0;
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true, $useLocal = true);
+
+ // track regular actions
+ $this->trackActions($t, $visitorCounter, 'pageview', $userAgents, $resolutions, $referrers, $customVars);
+
+ // track downloads
+ $this->trackActions($t, $visitorCounter, 'download', $userAgents, $resolutions, null, $downloadCustomVars);
+
+ // track outlinks
+ $this->trackActions($t, $visitorCounter, 'outlink', $userAgents, $resolutions);
+ }
+
+ private function trackActions($t, &$visitorCounter, $actionType, $userAgents, $resolutions,
+ $referrers = null, $customVars = null)
+ {
+ for ($i = 0; $i != 5; ++$i, ++$visitorCounter) {
+ $visitDate = Piwik_Date::factory($this->dateTime);
+
+ $t->setNewVisitorId();
+ $t->setIp("156.5.3.$visitorCounter");
+
+ $t->setUserAgent($userAgents[$visitorCounter]);
+ list($w, $h) = explode('x', $resolutions[$visitorCounter]);
+ $t->setResolution((int)$w, (int)$h);
+
+ // one visit to root url
+ $t->setUrl("http://piwik.net/$visitorCounter/");
+ $t->setUrlReferrer(null);
+ $t->setForceVisitDateTime($visitDate->getDatetime());
+ $this->trackAction($t, $actionType, $visitorCounter, null);
+
+ for ($j = 0; $j != 4; ++$j) {
+ // NOTE: to test referers w/o creating too many visits, we don't actually track 4 actions, but
+ // 4 separate visits
+ $actionDate = $visitDate->addHour($j + 1);
+
+ $actionIdx = $i * 4 + $j;
+ $actionNum = $visitorCounter * 4 + $j;
+
+ $t->setUrl("http://piwik.net/$visitorCounter/$actionNum");
+ $t->setForceVisitDateTime($actionDate->getDatetime());
+
+ if (!is_null($referrers)) {
+ $t->setUrlReferrer($referrers[$actionIdx]);
+ } else {
+ $t->setUrlReferrer(null);
+ }
+
+ if (!is_null($customVars)) {
+ $k = 1;
+ foreach ($customVars as $name => $values) {
+ $value = $values[$actionIdx];
+ $t->setCustomVariable($k, $name, $value, $scope = 'page');
+
+ ++$k;
+ }
+ }
+
+ $this->trackAction($t, $actionType, $visitorCounter, $actionNum);
+ }
+ }
+ }
+
+ private function trackAction($t, $actionType, $visitorCounter, $actionNum)
+ {
+ if ($actionType == 'pageview') {
+ self::checkResponse($t->doTrackPageView(
+ is_null($actionNum) ? "title_$visitorCounter" : "title_$visitorCounter / title_$actionNum"));
+ } else if ($actionType == 'download') {
+ $root = is_null($actionNum) ? "http://cloudsite$visitorCounter.com"
+ : "http://cloudsite$visitorCounter.com/$actionNum";
+
+ self::checkResponse($t->doTrackAction("$root/download", 'download'));
+ } else if ($actionType == 'outlink') {
+ self::checkResponse($t->doTrackAction(is_null($actionNum) ? "http://othersite$visitorCounter.com/"
+ : "http://othersite$visitorCounter.com/$actionNum/", 'link'));
+ }
+ }
+
+ private function setMockLocationProvider()
+ {
+ Piwik_UserCountry_LocationProvider::setCurrentProvider('mock_provider');
+ MockLocationProvider::$locations = array(
+ self::makeLocation('Toronto', 'ON', 'CA'),
+
+ self::makeLocation('Nice', 'B8', 'FR'),
+
+ self::makeLocation('Melbourne', '07', 'AU'),
+
+ self::makeLocation('Yokohama', '19', 'JP'),
+ );
+ }
+
+ private function unsetMockLocationProvider()
+ {
+ Piwik_UserCountry_LocationProvider::setCurrentProvider('default');
+ }
}
diff --git a/tests/PHPUnit/Fixtures/ManyVisitsWithSubDirReferrersAndCustomVars.php b/tests/PHPUnit/Fixtures/ManyVisitsWithSubDirReferrersAndCustomVars.php
index b3782cce0f..44f19e6664 100644
--- a/tests/PHPUnit/Fixtures/ManyVisitsWithSubDirReferrersAndCustomVars.php
+++ b/tests/PHPUnit/Fixtures/ManyVisitsWithSubDirReferrersAndCustomVars.php
@@ -4,7 +4,8 @@
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */#
+ */
+#
/**
* Adds one site and tracks 13 visits all with custom variables and referrer URLs
@@ -13,19 +14,19 @@
class Test_Piwik_Fixture_ManyVisitsWithSubDirReferrersAndCustomVars extends Test_Piwik_BaseFixture
{
public $dateTime = '2010-03-06 11:22:33';
- public $idSite = 1;
-
+ public $idSite = 1;
+
public function setUp()
{
- $this->setUpWebsitesAndGoals();
- $this->trackVisits();
+ $this->setUpWebsitesAndGoals();
+ $this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
+
private function setUpWebsitesAndGoals()
{
self::createWebsite($this->dateTime);
@@ -34,18 +35,18 @@ class Test_Piwik_Fixture_ManyVisitsWithSubDirReferrersAndCustomVars extends Test
private function trackVisits()
{
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
+ $idSite = $this->idSite;
for ($referrerSite = 1; $referrerSite < 4; $referrerSite++) {
for ($referrerPage = 1; $referrerPage < 3; $referrerPage++) {
$offset = $referrerSite * 3 + $referrerPage;
- $t = self::getTracker($idSite, Piwik_Date::factory($dateTime)->addHour($offset)->getDatetime());
+ $t = self::getTracker($idSite, Piwik_Date::factory($dateTime)->addHour($offset)->getDatetime());
$t->setUrlReferrer('http://www.referrer' . $referrerSite . '.com/sub/dir/page' . $referrerPage . '.html');
$t->setCustomVariable(1, 'CustomVarVisit', 'CustomVarValue' . $referrerPage, 'visit');
for ($page = 0; $page < 3; $page++) {
$t->setUrl('http://example.org/dir' . $referrerSite . '/sub/dir/page' . $page . '.html');
$t->setCustomVariable(1, 'CustomVarPage', 'CustomVarValue' . $page, 'page');
- $t->setGenerationTime($referrerPage * $referrerSite * ($page + 1) * 100);
+ $t->setGenerationTime($referrerPage * $referrerSite * ($page + 1) * 100);
self::checkResponse($t->doTrackPageView('title'));
}
}
diff --git a/tests/PHPUnit/Fixtures/OneVisitSeveralPageViews.php b/tests/PHPUnit/Fixtures/OneVisitSeveralPageViews.php
index 14d523159b..3a3bd00ba2 100644
--- a/tests/PHPUnit/Fixtures/OneVisitSeveralPageViews.php
+++ b/tests/PHPUnit/Fixtures/OneVisitSeveralPageViews.php
@@ -12,19 +12,19 @@
class Test_Piwik_Fixture_OneVisitSeveralPageViews extends Test_Piwik_BaseFixture
{
public $dateTime = '2010-03-06 11:22:33';
- public $idSite = 1;
-
+ public $idSite = 1;
+
public function setUp()
{
$this->setUpWebsitesAndGoals();
$this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
+
private function setUpWebsitesAndGoals()
{
self::createWebsite($this->dateTime);
@@ -33,37 +33,37 @@ class Test_Piwik_Fixture_OneVisitSeveralPageViews extends Test_Piwik_BaseFixture
private function trackVisits()
{
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
- $t = self::getTracker($idSite, $dateTime, $defaultInit = true, $useThirdPartyCookie = 1);
+ $idSite = $this->idSite;
+ $t = self::getTracker($idSite, $dateTime, $defaultInit = true, $useThirdPartyCookie = 1);
$t->setUrlReferrer('http://www.google.com.vn/url?sa=t&rct=j&q=%3C%3E%26%5C%22the%20pdo%20extension%20is%20required%20for%20this%20adapter%20but%20the%20extension%20is%20not%20loaded&source=web&cd=4&ved=0FjAD&url=http%3A%2F%2Fforum.piwik.org%2Fread.php%3F2%2C1011&ei=y-HHAQ&usg=AFQjCN2-nt5_GgDeg&cad=rja');
$t->setUrl('http://example.org/%C3%A9%C3%A9%C3%A9%22%27...%20%3Cthis%20is%20cool%3E!');
- $t->setGenerationTime(523);
+ $t->setGenerationTime(523);
self::checkResponse($t->doTrackPageView('incredible title! <>,;'));
$t->setUrl('http://example.org/dir/file.php?foo=bar&foo2=bar');
$t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.2)->getDatetime());
- $t->setGenerationTime(123);
+ $t->setGenerationTime(123);
self::checkResponse($t->doTrackPageView('incredible title! <>,;'));
$t->setUrl('http://example.org/dir/file.php?foo=bar&foo2=bar2');
$t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.3)->getDatetime());
- $t->setGenerationTime(153);
+ $t->setGenerationTime(153);
self::checkResponse($t->doTrackPageView('incredible parent title! <>,; / subtitle <>,;'));
$t->setUrl('http://example.org/dir2/file.php?foo=bar&foo2=bar');
$t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.4)->getDatetime());
- $t->setGenerationTime(1233);
+ $t->setGenerationTime(1233);
self::checkResponse($t->doTrackPageView('incredible title! <>,;'));
$t->setUrl('http://example.org/dir2/sub/0/file.php');
$t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.4)->getDatetime());
- $t->setGenerationTime(837);
+ $t->setGenerationTime(837);
self::checkResponse($t->doTrackPageView('incredible title! <>,;'));
$t->setUrl('http://example.org/0');
$t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.4)->getDatetime());
- $t->setGenerationTime(635);
+ $t->setGenerationTime(635);
self::checkResponse($t->doTrackPageView('I am URL zero!'));
}
diff --git a/tests/PHPUnit/Fixtures/OneVisitWithAbnormalPageviewUrls.php b/tests/PHPUnit/Fixtures/OneVisitWithAbnormalPageviewUrls.php
index 439433cefe..ac67e3cb0f 100644
--- a/tests/PHPUnit/Fixtures/OneVisitWithAbnormalPageviewUrls.php
+++ b/tests/PHPUnit/Fixtures/OneVisitWithAbnormalPageviewUrls.php
@@ -13,19 +13,19 @@
class Test_Piwik_Fixture_OneVisitWithAbnormalPageviewUrls extends Test_Piwik_BaseFixture
{
public $dateTime = '2010-03-06 11:22:33';
- public $idSite = 1;
-
+ public $idSite = 1;
+
public function setUp()
{
- $this->setUpWebsitesAndGoals();
- $this->trackVisits();
+ $this->setUpWebsitesAndGoals();
+ $this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
+
private function setUpWebsitesAndGoals()
{
self::createWebsite($this->dateTime);
@@ -34,8 +34,8 @@ class Test_Piwik_Fixture_OneVisitWithAbnormalPageviewUrls extends Test_Piwik_Bas
private function trackVisits()
{
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
- $t = self::getTracker($idSite, $dateTime, $defaultInit = true, $useThirdPartyCookie = 1);
+ $idSite = $this->idSite;
+ $t = self::getTracker($idSite, $dateTime, $defaultInit = true, $useThirdPartyCookie = 1);
$t->setUrlReferrer('http://www.google.com/search?q=piwik');
$t->setUrl('http://example.org/foo/bar.html');
diff --git a/tests/PHPUnit/Fixtures/OneVisitorTwoVisits.php b/tests/PHPUnit/Fixtures/OneVisitorTwoVisits.php
index 3177a41c3f..bba9bdcd54 100644
--- a/tests/PHPUnit/Fixtures/OneVisitorTwoVisits.php
+++ b/tests/PHPUnit/Fixtures/OneVisitorTwoVisits.php
@@ -11,44 +11,42 @@
*/
class Test_Piwik_Fixture_OneVisitorTwoVisits extends Test_Piwik_BaseFixture
{
- public $idSite = 1;
- public $dateTime = '2010-03-06 11:22:33';
-
+ public $idSite = 1;
+ public $dateTime = '2010-03-06 11:22:33';
+
public $useThirdPartyCookies = false;
public $useSiteSearch = false;
public $excludeMozilla = false;
-
- public function setUp()
- {
- $this->setUpWebsitesAndGoals();
- $this->trackVisits();
- }
-
- public function tearDown()
- {
- // empty
- }
-
- private function setUpWebsitesAndGoals()
- {
- self::createWebsite($this->dateTime);
- }
-
- private function trackVisits()
- {
+
+ public function setUp()
+ {
+ $this->setUpWebsitesAndGoals();
+ $this->trackVisits();
+ }
+
+ public function tearDown()
+ {
+ // empty
+ }
+
+ private function setUpWebsitesAndGoals()
+ {
+ self::createWebsite($this->dateTime);
+ }
+
+ private function trackVisits()
+ {
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
+ $idSite = $this->idSite;
- if ($this->excludeMozilla)
- {
- Piwik_SitesManager_API::getInstance()->setSiteSpecificUserAgentExcludeEnabled(false);
+ if ($this->excludeMozilla) {
+ Piwik_SitesManager_API::getInstance()->setSiteSpecificUserAgentExcludeEnabled(false);
}
-
+
$t = self::getTracker($idSite, $dateTime, $defaultInit = true);
-
- if ($this->useThirdPartyCookies)
- {
- $t->DEBUG_APPEND_URL = '&forceUseThirdPartyCookie=1';
+
+ if ($this->useThirdPartyCookies) {
+ $t->DEBUG_APPEND_URL = '&forceUseThirdPartyCookie=1';
}
$t->disableCookieSupport();
@@ -58,21 +56,21 @@ class Test_Piwik_Fixture_OneVisitorTwoVisits extends Test_Piwik_BaseFixture
// testing URL excluded parameters
$parameterToExclude = 'excluded_parameter';
Piwik_SitesManager_API::getInstance()->updateSite(
- $idSite,
- 'new name',
- $url = array('http://site.com'),
- $ecommerce = 0,
- $siteSearch = $this->useSiteSearch ? 1 : 0,
- $searchKeywordParameters = $this->useSiteSearch ? '' : null,
- $searchCategoryParameters = $this->useSiteSearch ? 'notparam' : null,
- $excludedIps = null,
- $parameterToExclude . ',anotherParameter',
- $timezone = null,
- $currency = null,
- $group = null,
- $startDate = null,
- // test that visit won't be excluded since site-specific exclude is not enabled
- $excludedUserAgents = $this->excludeMozilla ? 'mozilla' : null
+ $idSite,
+ 'new name',
+ $url = array('http://site.com'),
+ $ecommerce = 0,
+ $siteSearch = $this->useSiteSearch ? 1 : 0,
+ $searchKeywordParameters = $this->useSiteSearch ? '' : null,
+ $searchCategoryParameters = $this->useSiteSearch ? 'notparam' : null,
+ $excludedIps = null,
+ $parameterToExclude . ',anotherParameter',
+ $timezone = null,
+ $currency = null,
+ $group = null,
+ $startDate = null,
+ // test that visit won't be excluded since site-specific exclude is not enabled
+ $excludedUserAgents = $this->excludeMozilla ? 'mozilla' : null
);
// Record 1st page view
@@ -113,26 +111,23 @@ class Test_Piwik_Fixture_OneVisitorTwoVisits extends Test_Piwik_BaseFixture
self::checkResponse($t->doTrackGoal($idGoal, $revenue = 42));
$t->setBrowserLanguage('fr');
-
- if ($this->useSiteSearch)
- {
- // Site Search request
- $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.42)->getDatetime());
- $t->setUrl('http://example.org/index.htm?q=Banks Own The World');
- self::checkResponse($t->doTrackPageView('Site Search request'));
-
- // Final page view (after 27 min)
- $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.45)->getDatetime());
- $t->setUrl('http://example.org/index.htm');
- self::checkResponse($t->doTrackPageView('Looking at homepage after site search...'));
+
+ if ($this->useSiteSearch) {
+ // Site Search request
+ $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.42)->getDatetime());
+ $t->setUrl('http://example.org/index.htm?q=Banks Own The World');
+ self::checkResponse($t->doTrackPageView('Site Search request'));
+
+ // Final page view (after 27 min)
+ $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.45)->getDatetime());
+ $t->setUrl('http://example.org/index.htm');
+ self::checkResponse($t->doTrackPageView('Looking at homepage after site search...'));
+ } else {
+ // Final page view (after 27 min)
+ $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.45)->getDatetime());
+ $t->setUrl('http://example.org/index.htm#ignoredFragment#');
+ self::checkResponse($t->doTrackPageView('Looking at homepage (again)...'));
}
- else
- {
- // Final page view (after 27 min)
- $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.45)->getDatetime());
- $t->setUrl('http://example.org/index.htm#ignoredFragment#');
- self::checkResponse($t->doTrackPageView('Looking at homepage (again)...'));
- }
// -
// End of first visit: 24min
@@ -152,5 +147,5 @@ class Test_Piwik_Fixture_OneVisitorTwoVisits extends Test_Piwik_BaseFixture
self::checkResponse($t->doTrackPageView('Checkout/Purchasing...'));
// -
// End of second visit
- }
+ }
}
diff --git a/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php b/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php
index 72b3b8d1c9..670bc635df 100644
--- a/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php
+++ b/tests/PHPUnit/Fixtures/SomeVisitsAllConversions.php
@@ -15,16 +15,16 @@ class Piwik_Test_Fixture_SomeVisitsAllConversions extends Test_Piwik_BaseFixture
public $idSite = 1;
public $idGoal_OneConversionPerVisit = 1;
public $idGoal_MultipleConversionPerVisit = 2;
-
+
public function setUp()
{
$this->setUpWebsitesAndGoals();
$this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
private function setUpWebsitesAndGoals()
@@ -33,22 +33,22 @@ class Piwik_Test_Fixture_SomeVisitsAllConversions extends Test_Piwik_BaseFixture
// First, a goal that is only recorded once per visit
Piwik_Goals_API::getInstance()->addGoal(
- $this->idSite, 'triggered js ONCE', 'title', 'Thank you', 'contains', $caseSensitive = false,
- $revenue = 10, $allowMultipleConversions = false
- );
+ $this->idSite, 'triggered js ONCE', 'title', 'Thank you', 'contains', $caseSensitive = false,
+ $revenue = 10, $allowMultipleConversions = false
+ );
// Second, a goal allowing multiple conversions
Piwik_Goals_API::getInstance()->addGoal(
- $this->idSite, 'triggered js MULTIPLE ALLOWED', 'manually', '', '', $caseSensitive = false,
- $revenue = 10, $allowMultipleConversions = true
- );
+ $this->idSite, 'triggered js MULTIPLE ALLOWED', 'manually', '', '', $caseSensitive = false,
+ $revenue = 10, $allowMultipleConversions = true
+ );
}
private function trackVisits()
{
- $dateTime = $this->dateTime;
- $idSite = $this->idSite;
- $idGoal_OneConversionPerVisit = $this->idGoal_OneConversionPerVisit;
+ $dateTime = $this->dateTime;
+ $idSite = $this->idSite;
+ $idGoal_OneConversionPerVisit = $this->idGoal_OneConversionPerVisit;
$idGoal_MultipleConversionPerVisit = $this->idGoal_MultipleConversionPerVisit;
$t = self::getTracker($idSite, $dateTime, $defaultInit = true);
@@ -68,7 +68,7 @@ class Piwik_Test_Fixture_SomeVisitsAllConversions extends Test_Piwik_BaseFixture
// Update & set to not allow multiple
$goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
- $goal = $goals[$idGoal_OneConversionPerVisit];
+ $goal = $goals[$idGoal_OneConversionPerVisit];
self::assertTrue($goal['allow_multiple'] == 0);
Piwik_Goals_API::getInstance()->updateGoal($idSite, $idGoal_OneConversionPerVisit, $goal['name'], @$goal['match_attribute'], @$goal['pattern'], @$goal['pattern_type'], @$goal['case_sensitive'], $goal['revenue'], $goal['allow_multiple'] = 1);
self::assertTrue($goal['allow_multiple'] == 1);
diff --git a/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php b/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php
index 4ccbdf624a..a3752ad05c 100644
--- a/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php
+++ b/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php
@@ -15,18 +15,18 @@ class Test_Piwik_Fixture_SomeVisitsCustomVariablesCampaignsNotHeuristics extends
public $dateTime = '2009-01-04 00:11:42';
public $idSite = 1;
public $idGoal = 1;
-
+
public function setUp()
{
$this->setUpWebsitesAndGoals();
$this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
+
private function setUpWebsitesAndGoals()
{
self::createWebsite($this->dateTime);
@@ -36,8 +36,8 @@ class Test_Piwik_Fixture_SomeVisitsCustomVariablesCampaignsNotHeuristics extends
private function trackVisits()
{
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
- $idGoal = $this->idGoal;
+ $idSite = $this->idSite;
+ $idGoal = $this->idGoal;
$t = self::getTracker($idSite, $dateTime, $defaultInit = true);
@@ -49,8 +49,8 @@ class Test_Piwik_Fixture_SomeVisitsCustomVariablesCampaignsNotHeuristics extends
self::assertTrue(strlen($visitorId) == 16);
// test setting/getting the first party cookie via the PHP Tracking Client
- $_COOKIE['_pk_id_1_1fff'] = 'ca0afe7b6b692ff5.1302307497.1.1302307497.1302307497';
- $_COOKIE['_pk_ref_1_1fff'] = '["YEAH","RIGHT!",1302307497,"http://referrer.example.org/page/sub?query=test&test2=test3"]';
+ $_COOKIE['_pk_id_1_1fff'] = 'ca0afe7b6b692ff5.1302307497.1.1302307497.1302307497';
+ $_COOKIE['_pk_ref_1_1fff'] = '["YEAH","RIGHT!",1302307497,"http://referrer.example.org/page/sub?query=test&test2=test3"]';
$_COOKIE['_pk_cvar_1_1fff'] = '{"1":["VAR 1 set, var 2 not set","yes"],"3":["var 3 set","yes!!!!"]}';
self::assertEquals('ca0afe7b6b692ff5', $t->getVisitorId());
self::assertEquals($t->getAttributionInfo(), $_COOKIE['_pk_ref_1_1fff']);
@@ -95,24 +95,24 @@ class Test_Piwik_Fixture_SomeVisitsCustomVariablesCampaignsNotHeuristics extends
$t4->setUrl('http://example.org/index.html?utm_campaign=GA+Campaign');
self::checkResponse($t4->doTrackPageView('first page'));
- // No campaign keyword specified, will use the referrer hostname
+ // No campaign keyword specified, will use the referrer hostname
$t4->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(4)->getDatetime());
$t4->setUrlReferrer('http://thing1.com/a/b/c.html?a=b&d=c');
$t4->setUrl('http://example.org/index.html?utm_campaign=GA+Campaign');
self::checkResponse($t4->doTrackPageView('second page'));
- // Test with Google adsense type URL:
- $adsenseRefererUrl = 'http://googleads.g.doubleclick.net/pagead/ads?client=ca-pub-12345&output=html&h=280&slotname=123&w=336&lmt=1359388321&202&url=http%3A%2F%2Fwww.adsense-publisher-website.org%2F&dt=123&bpp=13&shv=r22&jsv=1565606614&correlator=ss&ga_vid=aaa&ga_sid=1359435122&ga_hid=1801871121&ga_fc=0&u_tz=780&u_his=4&u_java=1&u_h=900&u_w=1600&u_ah=876&u_aw=1551&u_cd=24&u_nplug=4&u_nmime=5&dff=georgia&dfs=16&adx=33&ady=201&biw=1551&bih=792&oid=3&fu=0&ifi=1&dtd=608&p=http%3A//www.adsense-publisher-website.com';
- $t4->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(5)->getDatetime());
- $t4->setUrlReferrer($adsenseRefererUrl);
- $t4->setUrl('http://example.org/index.html?utm_campaign=Adsense campaign');
- self::checkResponse($t4->doTrackPageView('second page'));
-
- // Test with google Adwords URL
- $adwordsUrl = 'http://www.google.co.nz/aclk?sa=L&ai=uYmFyiZgAf0oO0J&num=3&sig=EpOCR4xQ&ved=ENEM&adurl=http://pixel.everesttech.net/3163/cq%3Fev_sid%3D3%26ev_cmpid%3D33%26ev_ln%3Dused%2520wii%2520consoles%26ev_crx%528386%26ev_mt%3Db%26ev_n%3Dg%26ev_ltx%3D%26ev_pl%3D%26ev_pos%3D1s2%26url%3Dhttp%253A//au.shopping.com/used%2520wii%2520consoles/products%253Flinkin_id%253D8077872&rct=j&q=nintendo+consoles+second+hand';
- $t4->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(6)->getDatetime());
- $t4->setUrlReferrer($adwordsUrl);
- $t4->setUrl('http://example.org/index.html?utm_campaign=Adwords campaign');
- self::checkResponse($t4->doTrackPageView('second page'));
+ // Test with Google adsense type URL:
+ $adsenseRefererUrl = 'http://googleads.g.doubleclick.net/pagead/ads?client=ca-pub-12345&output=html&h=280&slotname=123&w=336&lmt=1359388321&202&url=http%3A%2F%2Fwww.adsense-publisher-website.org%2F&dt=123&bpp=13&shv=r22&jsv=1565606614&correlator=ss&ga_vid=aaa&ga_sid=1359435122&ga_hid=1801871121&ga_fc=0&u_tz=780&u_his=4&u_java=1&u_h=900&u_w=1600&u_ah=876&u_aw=1551&u_cd=24&u_nplug=4&u_nmime=5&dff=georgia&dfs=16&adx=33&ady=201&biw=1551&bih=792&oid=3&fu=0&ifi=1&dtd=608&p=http%3A//www.adsense-publisher-website.com';
+ $t4->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(5)->getDatetime());
+ $t4->setUrlReferrer($adsenseRefererUrl);
+ $t4->setUrl('http://example.org/index.html?utm_campaign=Adsense campaign');
+ self::checkResponse($t4->doTrackPageView('second page'));
+
+ // Test with google Adwords URL
+ $adwordsUrl = 'http://www.google.co.nz/aclk?sa=L&ai=uYmFyiZgAf0oO0J&num=3&sig=EpOCR4xQ&ved=ENEM&adurl=http://pixel.everesttech.net/3163/cq%3Fev_sid%3D3%26ev_cmpid%3D33%26ev_ln%3Dused%2520wii%2520consoles%26ev_crx%528386%26ev_mt%3Db%26ev_n%3Dg%26ev_ltx%3D%26ev_pl%3D%26ev_pos%3D1s2%26url%3Dhttp%253A//au.shopping.com/used%2520wii%2520consoles/products%253Flinkin_id%253D8077872&rct=j&q=nintendo+consoles+second+hand';
+ $t4->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(6)->getDatetime());
+ $t4->setUrlReferrer($adwordsUrl);
+ $t4->setUrl('http://example.org/index.html?utm_campaign=Adwords campaign');
+ self::checkResponse($t4->doTrackPageView('second page'));
}
}
diff --git a/tests/PHPUnit/Fixtures/SomeVisitsManyPageviewsWithTransitions.php b/tests/PHPUnit/Fixtures/SomeVisitsManyPageviewsWithTransitions.php
index dc7ea10572..ed793bb0bc 100644
--- a/tests/PHPUnit/Fixtures/SomeVisitsManyPageviewsWithTransitions.php
+++ b/tests/PHPUnit/Fixtures/SomeVisitsManyPageviewsWithTransitions.php
@@ -12,80 +12,82 @@
*/
class Test_Piwik_Fixture_SomeVisitsManyPageviewsWithTransitions extends Test_Piwik_BaseFixture
{
- public $dateTime = '2010-03-06 11:22:33';
- public $idSite = 1;
-
- private $prefixCounter = 0;
-
- public function setUp()
- {
- $this->setUpWebsitesAndGoals();
- $this->trackVisits();
- }
-
- public function tearDown()
- {
- // empty
- }
+ public $dateTime = '2010-03-06 11:22:33';
+ public $idSite = 1;
- private function setUpWebsitesAndGoals()
- {
- self::createWebsite($this->dateTime);
- }
+ private $prefixCounter = 0;
- private function trackVisits()
- {
- $visit1 = $this->createVisit(1);
- $visit1->setUrlReferrer('http://www.google.com.vn/url?sa=t&rct=j&q=%3C%3E%26%5C%22the%20pdo%20extension%20is%20required%20for%20this%20adapter%20but%20the%20extension%20is%20not%20loaded&source=web&cd=4&ved=0FjAD&url=http%3A%2F%2Fforum.piwik.org%2Fread.php%3F2%2C1011&ei=y-HHAQ&usg=AFQjCN2-nt5_GgDeg&cad=rja');
- $this->trackPageView($visit1, 0, 'page/one.html');
- $this->trackPageView($visit1, 0.1, 'sub/dir/page2.html');
- $this->trackPageView($visit1, 0.2, 'page/one.html');
- $this->trackPageView($visit1, 0.3, 'the/third_page.html?foo=bar');
- $this->trackPageView($visit1, 0.4, 'page/one.html');
- $this->trackPageView($visit1, 0.5, 'the/third_page.html?foo=bar');
- $this->trackPageView($visit1, 0.6, 'page/one.html');
- $this->trackPageView($visit1, 0.7, 'the/third_page.html?foo=baz#anchor1');
- $this->trackPageView($visit1, 0.8, 'page/one.html');
- $this->trackPageView($visit1, 0.9, 'page/one.html');
- $this->trackPageView($visit1, 1.0, 'the/third_page.html?foo=baz#anchor2');
- $this->trackPageView($visit1, 1.1, 'page/one.html');
- $this->trackPageView($visit1, 1.2, 'page3.html');
-
- $visit2 = $this->createVisit(2);
- $visit2->setUrlReferrer('http://www.external.com.vn/referrerPage-notCounted.html');
- $this->trackPageView($visit2, 0, 'sub/dir/page2.html');
- $this->trackPageView($visit2, 0.1, 'the/third_page.html?foo=bar');
- $this->trackPageView($visit2, 0.2, 'page/one.html');
- $this->trackPageView($visit2, 0.3, 'the/third_page.html?foo=baz#anchor1');
-
- $visit3 = $this->createVisit(3);
- $visit3->setUrlReferrer('http://www.external.com.vn/referrerPage-counted.html');
- $this->trackPageView($visit3, 0.1, 'page/one.html');
- $this->trackPageView($visit3, 0.2, 'sub/dir/page2.html');
- $this->trackPageView($visit3, 0.3, 'page/one.html');
-
- $visit4 = $this->createVisit(4);
- $this->trackPageView($visit4, 0, 'page/one.html?pk_campaign=TestCampaign&pk_kwd=TestKeyword');
-
- $visit5 = $this->createVisit(5);
- $this->trackPageView($visit5, 0, 'page/one.html');
- }
-
- private function createVisit($id) {
- $visit = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
- $visit->setIp('156.5.3.'.$id);
- return $visit;
- }
-
- private function trackPageView($visit, $timeOffset, $path) {
- // rotate protocol and www to make sure it doesn't matter
- $prefixes = array('http://', 'http://www.', 'https://', 'https://');
- $prefix = $prefixes[$this->prefixCounter];
- $this->prefixCounter = ($this->prefixCounter + 1) % 4;
-
- /** @var $visit PiwikTracker */
- $visit->setUrl($prefix.'example.org/'.$path);
- $visit->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour($timeOffset)->getDatetime());
- self::checkResponse($visit->doTrackPageView('page title'));
- }
+ public function setUp()
+ {
+ $this->setUpWebsitesAndGoals();
+ $this->trackVisits();
+ }
+
+ public function tearDown()
+ {
+ // empty
+ }
+
+ private function setUpWebsitesAndGoals()
+ {
+ self::createWebsite($this->dateTime);
+ }
+
+ private function trackVisits()
+ {
+ $visit1 = $this->createVisit(1);
+ $visit1->setUrlReferrer('http://www.google.com.vn/url?sa=t&rct=j&q=%3C%3E%26%5C%22the%20pdo%20extension%20is%20required%20for%20this%20adapter%20but%20the%20extension%20is%20not%20loaded&source=web&cd=4&ved=0FjAD&url=http%3A%2F%2Fforum.piwik.org%2Fread.php%3F2%2C1011&ei=y-HHAQ&usg=AFQjCN2-nt5_GgDeg&cad=rja');
+ $this->trackPageView($visit1, 0, 'page/one.html');
+ $this->trackPageView($visit1, 0.1, 'sub/dir/page2.html');
+ $this->trackPageView($visit1, 0.2, 'page/one.html');
+ $this->trackPageView($visit1, 0.3, 'the/third_page.html?foo=bar');
+ $this->trackPageView($visit1, 0.4, 'page/one.html');
+ $this->trackPageView($visit1, 0.5, 'the/third_page.html?foo=bar');
+ $this->trackPageView($visit1, 0.6, 'page/one.html');
+ $this->trackPageView($visit1, 0.7, 'the/third_page.html?foo=baz#anchor1');
+ $this->trackPageView($visit1, 0.8, 'page/one.html');
+ $this->trackPageView($visit1, 0.9, 'page/one.html');
+ $this->trackPageView($visit1, 1.0, 'the/third_page.html?foo=baz#anchor2');
+ $this->trackPageView($visit1, 1.1, 'page/one.html');
+ $this->trackPageView($visit1, 1.2, 'page3.html');
+
+ $visit2 = $this->createVisit(2);
+ $visit2->setUrlReferrer('http://www.external.com.vn/referrerPage-notCounted.html');
+ $this->trackPageView($visit2, 0, 'sub/dir/page2.html');
+ $this->trackPageView($visit2, 0.1, 'the/third_page.html?foo=bar');
+ $this->trackPageView($visit2, 0.2, 'page/one.html');
+ $this->trackPageView($visit2, 0.3, 'the/third_page.html?foo=baz#anchor1');
+
+ $visit3 = $this->createVisit(3);
+ $visit3->setUrlReferrer('http://www.external.com.vn/referrerPage-counted.html');
+ $this->trackPageView($visit3, 0.1, 'page/one.html');
+ $this->trackPageView($visit3, 0.2, 'sub/dir/page2.html');
+ $this->trackPageView($visit3, 0.3, 'page/one.html');
+
+ $visit4 = $this->createVisit(4);
+ $this->trackPageView($visit4, 0, 'page/one.html?pk_campaign=TestCampaign&pk_kwd=TestKeyword');
+
+ $visit5 = $this->createVisit(5);
+ $this->trackPageView($visit5, 0, 'page/one.html');
+ }
+
+ private function createVisit($id)
+ {
+ $visit = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ $visit->setIp('156.5.3.' . $id);
+ return $visit;
+ }
+
+ private function trackPageView($visit, $timeOffset, $path)
+ {
+ // rotate protocol and www to make sure it doesn't matter
+ $prefixes = array('http://', 'http://www.', 'https://', 'https://');
+ $prefix = $prefixes[$this->prefixCounter];
+ $this->prefixCounter = ($this->prefixCounter + 1) % 4;
+
+ /** @var $visit PiwikTracker */
+ $visit->setUrl($prefix . 'example.org/' . $path);
+ $visit->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour($timeOffset)->getDatetime());
+ self::checkResponse($visit->doTrackPageView('page title'));
+ }
}
diff --git a/tests/PHPUnit/Fixtures/SomeVisitsWithLongUrls.php b/tests/PHPUnit/Fixtures/SomeVisitsWithLongUrls.php
index d0335f45db..9eb839ad53 100644
--- a/tests/PHPUnit/Fixtures/SomeVisitsWithLongUrls.php
+++ b/tests/PHPUnit/Fixtures/SomeVisitsWithLongUrls.php
@@ -13,19 +13,19 @@
class Test_Piwik_Fixture_SomeVisitsWithLongUrls extends Test_Piwik_BaseFixture
{
public $dateTime = '2010-03-06 01:22:33';
- public $idSite = 1;
-
+ public $idSite = 1;
+
public function setUp()
{
- $this->setUpWebsitesAndGoals();
- $this->trackVisits();
+ $this->setUpWebsitesAndGoals();
+ $this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
+
private function setUpWebsitesAndGoals()
{
self::createWebsite($this->dateTime);
@@ -35,7 +35,7 @@ class Test_Piwik_Fixture_SomeVisitsWithLongUrls extends Test_Piwik_BaseFixture
{
// tests run in UTC, the Tracker in UTC
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
+ $idSite = $this->idSite;
// Visit 1: keyword and few URLs
$t = self::getTracker($idSite, $dateTime, $defaultInit = true, $useThirdPartyCookie = 1);
diff --git a/tests/PHPUnit/Fixtures/SomeVisitsWithNonUnicodePageTitles.php b/tests/PHPUnit/Fixtures/SomeVisitsWithNonUnicodePageTitles.php
index b87111433b..b0dac3c1a8 100644
--- a/tests/PHPUnit/Fixtures/SomeVisitsWithNonUnicodePageTitles.php
+++ b/tests/PHPUnit/Fixtures/SomeVisitsWithNonUnicodePageTitles.php
@@ -2,7 +2,7 @@
/**
* Piwik - Open source web analytics
*
- * @link http://piwik.org
+ * @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
@@ -11,87 +11,87 @@
*/
class Test_Piwik_Fixture_SomeVisitsWithNonUnicodePageTitles extends Test_Piwik_BaseFixture
{
- public $idSite1 = 1;
- public $dateTime = '2010-01-03 11:22:33';
-
- public function setUp()
- {
- $this->setUpWebsites();
- $this->trackVisits();
- }
-
- public function tearDown()
- {
- // empty
- }
+ public $idSite1 = 1;
+ public $dateTime = '2010-01-03 11:22:33';
- /**
- * One site with custom search parameters,
- * One site using default search parameters,
- * One site with disabled site search
- */
- private function setUpWebsites()
- {
- Piwik_SitesManager_API::getInstance()->setGlobalSearchParameters($searchKeywordParameters='gkwd', $searchCategoryParameters='gcat');
- self::createWebsite(Piwik_Date::factory($this->dateTime)->getDatetime(), 0, "Site 1 - Site search", $siteurl=false, $search=1, $searchKwd='q,mykwd,p', $searchCat='cats' );
- }
+ public function setUp()
+ {
+ $this->setUpWebsites();
+ $this->trackVisits();
+ }
- private function trackVisits()
- {
- $idSite1 = $this->idSite1;
- $dateTime = $this->dateTime;
-
- self::assertTrue(function_exists('mb_check_encoding'), ' check mb_check_encoding ');
- self::assertTrue(function_exists('mb_convert_encoding'), ' check mb_convert_encoding ');
+ public function tearDown()
+ {
+ // empty
+ }
- // Visitor site1
- $visitor = self::getTracker($idSite1, $dateTime, $defaultInit = true);
-
- // Test w/ iso-8859-15
- $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.3)->getDatetime());
- $visitor->setUrlReferrer('http://anothersite.com/whatever.html?whatever=Ato%FC');
- // Also testing that the value is encoded when passed as an array
- $visitor->setUrl('http://example.org/index.htm?random=param&mykwd[]=Search 2%FC&test&cats= Search Kategory &search_count=INCORRECT!');
- $visitor->setPageCharset('iso-8859-15');
- self::checkResponse($visitor->doTrackPageView('Site Search results'));
- $visitor->setPageCharset('');
-
- // Test w/ windows-1251
- $visitor = self::getTracker($idSite1, $dateTime, $defaultInit = true);
- $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.3)->getDatetime());
- $visitor->setUrlReferrer('http://anothersite.com/whatever.html?txt=%EC%E5%F8%EA%EE%E2%FB%E5');
- $visitor->setUrl('http://example.org/page/index.htm?whatever=%EC%E5%F8%EA%EE%E2%FB%E5');
- $visitor->setPageCharset('windows-1251');
- self::checkResponse($visitor->doTrackPageView('Page title is always UTF-8'));
+ /**
+ * One site with custom search parameters,
+ * One site using default search parameters,
+ * One site with disabled site search
+ */
+ private function setUpWebsites()
+ {
+ Piwik_SitesManager_API::getInstance()->setGlobalSearchParameters($searchKeywordParameters = 'gkwd', $searchCategoryParameters = 'gcat');
+ self::createWebsite(Piwik_Date::factory($this->dateTime)->getDatetime(), 0, "Site 1 - Site search", $siteurl = false, $search = 1, $searchKwd = 'q,mykwd,p', $searchCat = 'cats');
+ }
- $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.4)->getDatetime());
- $nonUnicodeKeyword = '%EC%E5%F8%EA%EE%E2%FB%E5';
- $visitor->setUrl('http://example.org/page/index.htm?q='.$nonUnicodeKeyword);
- $visitor->setPageCharset('windows-1251');
- self::checkResponse($visitor->doTrackPageView('Site Search'));
+ private function trackVisits()
+ {
+ $idSite1 = $this->idSite1;
+ $dateTime = $this->dateTime;
+ self::assertTrue(function_exists('mb_check_encoding'), ' check mb_check_encoding ');
+ self::assertTrue(function_exists('mb_convert_encoding'), ' check mb_convert_encoding ');
- // Test URL with non unicode Site Search keyword
- $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.5)->getDatetime());
- //TESTS: on jenkins somehow the "<-was here" was cut off so removing this test case and simply append the wrong keyword
+ // Visitor site1
+ $visitor = self::getTracker($idSite1, $dateTime, $defaultInit = true);
+
+ // Test w/ iso-8859-15
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.3)->getDatetime());
+ $visitor->setUrlReferrer('http://anothersite.com/whatever.html?whatever=Ato%FC');
+ // Also testing that the value is encoded when passed as an array
+ $visitor->setUrl('http://example.org/index.htm?random=param&mykwd[]=Search 2%FC&test&cats= Search Kategory &search_count=INCORRECT!');
+ $visitor->setPageCharset('iso-8859-15');
+ self::checkResponse($visitor->doTrackPageView('Site Search results'));
+ $visitor->setPageCharset('');
+
+ // Test w/ windows-1251
+ $visitor = self::getTracker($idSite1, $dateTime, $defaultInit = true);
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.3)->getDatetime());
+ $visitor->setUrlReferrer('http://anothersite.com/whatever.html?txt=%EC%E5%F8%EA%EE%E2%FB%E5');
+ $visitor->setUrl('http://example.org/page/index.htm?whatever=%EC%E5%F8%EA%EE%E2%FB%E5');
+ $visitor->setPageCharset('windows-1251');
+ self::checkResponse($visitor->doTrackPageView('Page title is always UTF-8'));
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.4)->getDatetime());
+ $nonUnicodeKeyword = '%EC%E5%F8%EA%EE%E2%FB%E5';
+ $visitor->setUrl('http://example.org/page/index.htm?q=' . $nonUnicodeKeyword);
+ $visitor->setPageCharset('windows-1251');
+ self::checkResponse($visitor->doTrackPageView('Site Search'));
+
+
+ // Test URL with non unicode Site Search keyword
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.5)->getDatetime());
+ //TESTS: on jenkins somehow the "<-was here" was cut off so removing this test case and simply append the wrong keyword
// $visitor->setUrl('http://example.org/page/index.htm?q=non unicode keyword %EC%E5%F8%EAe%EE%E2%FBf%E5 <-was here');
- $visitor->setUrl('http://example.org/page/index.htm?q=non unicode keyword %EC%E5%F8%EA%EE%E2%FB%E5');
- $visitor->setPageCharset('utf-8');
- self::checkResponse($visitor->doTrackPageView('Site Search'));
+ $visitor->setUrl('http://example.org/page/index.htm?q=non unicode keyword %EC%E5%F8%EA%EE%E2%FB%E5');
+ $visitor->setPageCharset('utf-8');
+ self::checkResponse($visitor->doTrackPageView('Site Search'));
- $visitor->setPageCharset('');
- $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.5)->getDatetime());
- $visitor->setUrl('http://example.org/exit-page');
- self::checkResponse($visitor->doTrackPageView('Page title is always UTF-8'));
+ $visitor->setPageCharset('');
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.5)->getDatetime());
+ $visitor->setUrl('http://example.org/exit-page');
+ self::checkResponse($visitor->doTrackPageView('Page title is always UTF-8'));
- // Test set invalid page char set
- $visitor = self::getTracker($idSite1, $dateTime, $defaultInit = true);
- $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(1)->getDatetime());
- $visitor->setUrlReferrer('http://anothersite.com/whatever.html');
- $visitor->setUrl('http://example.org/index.htm?random=param&mykwd=a+keyword&test&cats= Search Kategory &search_count=INCORRECT!');
- $visitor->setPageCharset('GTF-42'); // galactic transformation format
- self::checkResponse($visitor->doTrackPageView('Site Search results'));
- $visitor->setPageCharset('');
- }
+ // Test set invalid page char set
+ $visitor = self::getTracker($idSite1, $dateTime, $defaultInit = true);
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(1)->getDatetime());
+ $visitor->setUrlReferrer('http://anothersite.com/whatever.html');
+ $visitor->setUrl('http://example.org/index.htm?random=param&mykwd=a+keyword&test&cats= Search Kategory &search_count=INCORRECT!');
+ $visitor->setPageCharset('GTF-42'); // galactic transformation format
+ self::checkResponse($visitor->doTrackPageView('Site Search results'));
+ $visitor->setPageCharset('');
+ }
}
diff --git a/tests/PHPUnit/Fixtures/ThreeGoalsOnePageview.php b/tests/PHPUnit/Fixtures/ThreeGoalsOnePageview.php
index 2e95f196d1..7f055f4de6 100644
--- a/tests/PHPUnit/Fixtures/ThreeGoalsOnePageview.php
+++ b/tests/PHPUnit/Fixtures/ThreeGoalsOnePageview.php
@@ -13,38 +13,38 @@
class Test_Piwik_Fixture_ThreeGoalsOnePageview extends Test_Piwik_BaseFixture
{
public $dateTime = '2009-01-04 00:11:42';
- public $idSite = 1;
- public $idGoal = 1;
- public $idGoal2 = 2;
- public $idGoal3 = 3;
-
- public function setUp()
- {
- $this->setUpWebsitesAndGoals();
- $this->trackVisits();
- }
-
- public function tearDown()
- {
- // empty
- }
+ public $idSite = 1;
+ public $idGoal = 1;
+ public $idGoal2 = 2;
+ public $idGoal3 = 3;
+
+ public function setUp()
+ {
+ $this->setUpWebsitesAndGoals();
+ $this->trackVisits();
+ }
+
+ public function tearDown()
+ {
+ // empty
+ }
private function setUpWebsitesAndGoals()
{
self::createWebsite($this->dateTime, $ecommerce = 1);
Piwik_Goals_API::getInstance()->addGoal(
- $this->idSite, 'Goal 1 - Thank you', 'title', 'Thank you', 'contains', $caseSensitive = false,
- $revenue = 10, $allowMultipleConversions = 1
- );
-
+ $this->idSite, 'Goal 1 - Thank you', 'title', 'Thank you', 'contains', $caseSensitive = false,
+ $revenue = 10, $allowMultipleConversions = 1
+ );
+
Piwik_Goals_API::getInstance()->addGoal(
- $this->idSite, 'Goal 2 - Hello', 'url', 'hellow', 'contains', $caseSensitive = false,
- $revenue = 10, $allowMultipleConversions = 0
- );
-
+ $this->idSite, 'Goal 2 - Hello', 'url', 'hellow', 'contains', $caseSensitive = false,
+ $revenue = 10, $allowMultipleConversions = 0
+ );
+
Piwik_Goals_API::getInstance()->addGoal($this->idSite, 'triggered js', 'manually', '', '');
}
-
+
private function trackVisits()
{
$t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
diff --git a/tests/PHPUnit/Fixtures/ThreeSitesWithManyVisitsWithSiteSearch.php b/tests/PHPUnit/Fixtures/ThreeSitesWithManyVisitsWithSiteSearch.php
index 8f02a2d875..ac93543b42 100644
--- a/tests/PHPUnit/Fixtures/ThreeSitesWithManyVisitsWithSiteSearch.php
+++ b/tests/PHPUnit/Fixtures/ThreeSitesWithManyVisitsWithSiteSearch.php
@@ -12,180 +12,180 @@
*/
class Test_Piwik_Fixture_ThreeSitesWithManyVisitsWithSiteSearch extends Test_Piwik_BaseFixture
{
- public $idSite1 = 1;
- public $idSite2 = 2;
- public $idSite3 = 3;
+ public $idSite1 = 1;
+ public $idSite2 = 2;
+ public $idSite3 = 3;
public $dateTime = '2010-01-03 11:22:33';
-
+
public function setUp()
{
self::setUpWebsites();
self::trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
- /**
- * One site with custom search parameters,
- * One site using default search parameters,
- * One site with disabled site search
- */
- protected function setUpWebsites()
+
+ /**
+ * One site with custom search parameters,
+ * One site using default search parameters,
+ * One site with disabled site search
+ */
+ protected function setUpWebsites()
{
- Piwik_SitesManager_API::getInstance()->setGlobalSearchParameters($searchKeywordParameters='gkwd', $searchCategoryParameters='gcat');
- self::createWebsite(Piwik_Date::factory($this->dateTime)->subHour(200)->getDatetime(), 0, "Site 1 - Site search", $siteurl=false, $search=1, $searchKwd='q,mykwd,p', $searchCat='cats' );
- self::createWebsite(Piwik_Date::factory($this->dateTime)->subHour(400)->getDatetime(), 0, "Site 2 - Site search use default", $siteurl = false, $search=1, $searchKwd='', $searchCat='' );
- self::createWebsite(Piwik_Date::factory($this->dateTime)->subHour(600)->getDatetime(), 0, "Site 3 - No site search", $siteurl = false, $search=0);
+ Piwik_SitesManager_API::getInstance()->setGlobalSearchParameters($searchKeywordParameters = 'gkwd', $searchCategoryParameters = 'gcat');
+ self::createWebsite(Piwik_Date::factory($this->dateTime)->subHour(200)->getDatetime(), 0, "Site 1 - Site search", $siteurl = false, $search = 1, $searchKwd = 'q,mykwd,p', $searchCat = 'cats');
+ self::createWebsite(Piwik_Date::factory($this->dateTime)->subHour(400)->getDatetime(), 0, "Site 2 - Site search use default", $siteurl = false, $search = 1, $searchKwd = '', $searchCat = '');
+ self::createWebsite(Piwik_Date::factory($this->dateTime)->subHour(600)->getDatetime(), 0, "Site 3 - No site search", $siteurl = false, $search = 0);
}
protected function trackVisits()
{
- $this->recordVisitorsSite1();
- $this->recordVisitorSite2();
- $this->recordVisitorSite3();
+ $this->recordVisitorsSite1();
+ $this->recordVisitorSite2();
+ $this->recordVisitorSite3();
+ }
+
+ protected function recordVisitorsSite1()
+ {
+ // -
+ // Visitor site1
+ $visitor = self::getTracker($this->idSite1, $this->dateTime, $defaultInit = true);
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?q=Search 1 ');
+ self::checkResponse($visitor->doTrackPageView('Site Search results'));
+
+ // Normal page view
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.22)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm');
+ self::checkResponse($visitor->doTrackPageView('Im just a page'));
+
+ // IS_FOLLOWING_SEARCH: Not this time
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.23)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?random=PAGEVIEW, NOT SEARCH&mykwd=&IS_FOLLOWING_SEARCH ONCE');
+ self::checkResponse($visitor->doTrackPageView('This is a pageview, not a Search - IS_FOLLOWING_SEARCH ONCE'));
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.25)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?standard=query&but=also#hash&q=' . urlencode('Search 1'));
+ self::checkResponse($visitor->doTrackPageView('Site Search results - URL Fragment'));
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.26)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm#q=Search 1&search_count=10');
+ self::checkResponse($visitor->doTrackPageView('Site Search results - URL Fragment'));
+
+ // &search_count=0 so it's a "No Result" keyword, but it will not appear in the report, because it also has other seraches with results
+ // and the archiving does a MAX()
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.27)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?hello=world#q=Search 1&search_count=0');
+ self::checkResponse($visitor->doTrackPageView('Site Search results - URL Fragment'));
+
+ // Testing with non urlencoded values
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.3)->getDatetime());
+ // ALso testing that array[] notation is detected
+ $visitor->setUrl('http://example.org/index.htm?random=param&mykwd[]=Search 2&test&cats= Search Category &search_count=INCORRECT!');
+ self::checkResponse($visitor->doTrackPageView('Site Search results'));
+
+ // Testing with urlencoded values
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.32)->getDatetime());
+ // Also testing with random case 'myKwd'
+ $visitor->setUrl('http://example.org/index.htm?random=param&myKwd=Search 1&test&cats=' . urlencode(' Search Category ') . ' &search_count=0');
+ self::checkResponse($visitor->doTrackPageView('Site Search results'));
+
+ // IS_FOLLOWING_SEARCH: Yes
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.35)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?random=PAGEVIEW, NOT SEARCH&mykwd=&IS_FOLLOWING_SEARCH ONCE');
+ self::checkResponse($visitor->doTrackPageView('This is a pageview, not a Search - IS_FOLLOWING_SEARCH ONCE'));
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.4)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?gkwd=SHOULD be a PageView, NOT a search');
+ self::checkResponse($visitor->doTrackPageView('Pageview, not search'));
+
+ $visitor->setUrl('http://example.org/hello?THIS IS A SITE SEARCH TRACKING API, NOT PAGEVIEW!');
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(24.41)->getDatetime());
+ self::checkResponse($visitor->doTrackSiteSearch("Keyword - Tracking API"));
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(24.42)->getDatetime());
+ self::checkResponse($visitor->doTrackSiteSearch("Keyword - Tracking API", "Category", $count = 5));
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(24.43)->getDatetime());
+ self::checkResponse($visitor->doTrackSiteSearch("No Result Keyword!", "Bad No Result Category :(", $count = 0));
+
+ // Keyword in iso-8859-15 charset with funny character
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(24.5)->getDatetime());
+ $visitor->setPageCharset('iso-8859-15');
+ $visitor->setUrl('http://example.org/index.htm?q=Final%20t%FCte%20Keyword%20Searched%20for%20now&search_count=10');
+ self::checkResponse($visitor->doTrackPageView(false));
+
+ // -
+ // Visitor BIS
+ $visitorB = self::getTracker($this->idSite1, $this->dateTime, $defaultInit = true);
+ $visitorB->setIp('156.66.6.66');
+ $visitorB->setResolution(1600, 1000);
+
+ $visitorB->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(2.26)->getDatetime());
+ $visitorB->setUrl('http://example.org/index.htm#q=' . urlencode('No Result Keyword!') . '&search_count=0');
+ self::checkResponse($visitorB->doTrackPageView('Site Search results - URL Fragment'));
+
+ $visitorB->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(2.27)->getDatetime());
+ $visitorB->setUrl('http://example.org/index.htm?hello=world#q=Search 1&search_count=10');
+ self::checkResponse($visitorB->doTrackPageView('Site Search results - URL Fragment'));
+
+ $visitorB->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(2.3)->getDatetime());
+ $visitorB->setUrl('http://example.org/index.htm?random=param&mykwd=Search 2&test&cats= Search Category &search_count=10');
+ self::checkResponse($visitorB->doTrackPageView('Site Search results'));
+ }
+
+ protected function recordVisitorSite3()
+ { // -
+ // Third new visitor on Idsite 3
+ $visitor = self::getTracker($this->idSite3, $this->dateTime, $defaultInit = true);
+ $visitor->setResolution(1801, 1301);
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?q=Search 1&IsPageView=1');
+ $visitor->setCustomVariable(1, 'test cvar name', 'test cvar value');
+ self::checkResponse($visitor->doTrackPageView('IsPageView'));
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.35)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?gkwd=test not a keyword&gcat=Cat not but not keyword, so this is not search');
+ self::checkResponse($visitor->doTrackPageView('This is a pageview, not a Search'));
+
+ // Testing UTF8 Title & URL
+ $crazyTitle = '%2C%20%C3%8Dslenska%2C%20Italiano%2C%20%E6%97%A5%E6%9C%AC%E8%AA%9E%2C%20%E1%83%A5%E1%83%90%E1%83%A0%E1%83%97%E1%83%A3%E1%83%9A%E1%83%98%2C%20%ED%95%9C%EA%B5%AD%EC%96%B4%2C%20Lietuvi%C5%B3%2C%20Latvie%C5%A1u%2C%20Norsk%20(bokm%C3%A5l)%2C%20Nederlands%2C%20Norsk%20(nynorsk)%2C%20Polski%2C%20Portugu%C3%AAs%20brasileiro%2C%20Portugu%C3%AAs%2C%20Rom%C3%A2n%C4%83%2C%20%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9%2C%20Slovensky%2C%20Sloven%C5%A1%C4%8Dina%2C%20Shqip%2C%20Srpski%2C%20Svenska%2C%20%E0%B0%A4%E0%B1%86%E0%B0%B2%E0%B1%81%E0%B0%97%E0%B1%81%2C%20%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%2C%20T%C3%BCrk%C3%A7e%2C%20%D0%A3%D0%BA%D1%80%D0%B0%D1%97%D0%BD%D1%81%D1%8C%D0%BA%D0%B0%2C%20%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87%2C%20%E7%B9%81%E9%AB%94%E4%B8%AD%E6%96%87.';
+ $visitor->setUrl('http://example.org/index.htm?' . $crazyTitle);
+ self::checkResponse($visitor->doTrackPageView('Pageview: ' . $crazyTitle));
}
- protected function recordVisitorsSite1()
- {
- // -
- // Visitor site1
- $visitor = self::getTracker($this->idSite1, $this->dateTime, $defaultInit = true);
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?q=Search 1 ');
- self::checkResponse($visitor->doTrackPageView('Site Search results'));
-
- // Normal page view
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.22)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm');
- self::checkResponse($visitor->doTrackPageView('Im just a page'));
-
- // IS_FOLLOWING_SEARCH: Not this time
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.23)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?random=PAGEVIEW, NOT SEARCH&mykwd=&IS_FOLLOWING_SEARCH ONCE');
- self::checkResponse($visitor->doTrackPageView('This is a pageview, not a Search - IS_FOLLOWING_SEARCH ONCE'));
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.25)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?standard=query&but=also#hash&q='.urlencode('Search 1'));
- self::checkResponse($visitor->doTrackPageView('Site Search results - URL Fragment'));
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.26)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm#q=Search 1&search_count=10');
- self::checkResponse($visitor->doTrackPageView('Site Search results - URL Fragment'));
-
- // &search_count=0 so it's a "No Result" keyword, but it will not appear in the report, because it also has other seraches with results
- // and the archiving does a MAX()
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.27)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?hello=world#q=Search 1&search_count=0');
- self::checkResponse($visitor->doTrackPageView('Site Search results - URL Fragment'));
-
- // Testing with non urlencoded values
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.3)->getDatetime());
- // ALso testing that array[] notation is detected
- $visitor->setUrl('http://example.org/index.htm?random=param&mykwd[]=Search 2&test&cats= Search Category &search_count=INCORRECT!');
- self::checkResponse($visitor->doTrackPageView('Site Search results'));
-
- // Testing with urlencoded values
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.32)->getDatetime());
- // Also testing with random case 'myKwd'
- $visitor->setUrl('http://example.org/index.htm?random=param&myKwd=Search 1&test&cats='.urlencode(' Search Category '). ' &search_count=0');
- self::checkResponse($visitor->doTrackPageView('Site Search results'));
-
- // IS_FOLLOWING_SEARCH: Yes
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.35)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?random=PAGEVIEW, NOT SEARCH&mykwd=&IS_FOLLOWING_SEARCH ONCE');
- self::checkResponse($visitor->doTrackPageView('This is a pageview, not a Search - IS_FOLLOWING_SEARCH ONCE'));
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.4)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?gkwd=SHOULD be a PageView, NOT a search');
- self::checkResponse($visitor->doTrackPageView('Pageview, not search'));
-
- $visitor->setUrl('http://example.org/hello?THIS IS A SITE SEARCH TRACKING API, NOT PAGEVIEW!');
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(24.41)->getDatetime());
- self::checkResponse($visitor->doTrackSiteSearch("Keyword - Tracking API"));
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(24.42)->getDatetime());
- self::checkResponse($visitor->doTrackSiteSearch("Keyword - Tracking API", "Category", $count = 5));
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(24.43)->getDatetime());
- self::checkResponse($visitor->doTrackSiteSearch("No Result Keyword!", "Bad No Result Category :(", $count = 0));
-
- // Keyword in iso-8859-15 charset with funny character
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(24.5)->getDatetime());
- $visitor->setPageCharset('iso-8859-15');
- $visitor->setUrl('http://example.org/index.htm?q=Final%20t%FCte%20Keyword%20Searched%20for%20now&search_count=10');
- self::checkResponse($visitor->doTrackPageView(false));
-
- // -
- // Visitor BIS
- $visitorB = self::getTracker($this->idSite1, $this->dateTime, $defaultInit = true);
- $visitorB->setIp('156.66.6.66');
- $visitorB->setResolution(1600, 1000);
-
- $visitorB->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(2.26)->getDatetime());
- $visitorB->setUrl('http://example.org/index.htm#q=' . urlencode('No Result Keyword!') . '&search_count=0');
- self::checkResponse($visitorB->doTrackPageView('Site Search results - URL Fragment'));
-
- $visitorB->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(2.27)->getDatetime());
- $visitorB->setUrl('http://example.org/index.htm?hello=world#q=Search 1&search_count=10');
- self::checkResponse($visitorB->doTrackPageView('Site Search results - URL Fragment'));
-
- $visitorB->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(2.3)->getDatetime());
- $visitorB->setUrl('http://example.org/index.htm?random=param&mykwd=Search 2&test&cats= Search Category &search_count=10');
- self::checkResponse($visitorB->doTrackPageView('Site Search results'));
- }
-
- protected function recordVisitorSite3()
- { // -
- // Third new visitor on Idsite 3
- $visitor = self::getTracker($this->idSite3, $this->dateTime, $defaultInit = true);
- $visitor->setResolution(1801, 1301);
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?q=Search 1&IsPageView=1');
- $visitor->setCustomVariable(1, 'test cvar name', 'test cvar value');
- self::checkResponse($visitor->doTrackPageView('IsPageView'));
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.35)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?gkwd=test not a keyword&gcat=Cat not but not keyword, so this is not search');
- self::checkResponse($visitor->doTrackPageView('This is a pageview, not a Search'));
-
- // Testing UTF8 Title & URL
- $crazyTitle = '%2C%20%C3%8Dslenska%2C%20Italiano%2C%20%E6%97%A5%E6%9C%AC%E8%AA%9E%2C%20%E1%83%A5%E1%83%90%E1%83%A0%E1%83%97%E1%83%A3%E1%83%9A%E1%83%98%2C%20%ED%95%9C%EA%B5%AD%EC%96%B4%2C%20Lietuvi%C5%B3%2C%20Latvie%C5%A1u%2C%20Norsk%20(bokm%C3%A5l)%2C%20Nederlands%2C%20Norsk%20(nynorsk)%2C%20Polski%2C%20Portugu%C3%AAs%20brasileiro%2C%20Portugu%C3%AAs%2C%20Rom%C3%A2n%C4%83%2C%20%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9%2C%20Slovensky%2C%20Sloven%C5%A1%C4%8Dina%2C%20Shqip%2C%20Srpski%2C%20Svenska%2C%20%E0%B0%A4%E0%B1%86%E0%B0%B2%E0%B1%81%E0%B0%97%E0%B1%81%2C%20%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%2C%20T%C3%BCrk%C3%A7e%2C%20%D0%A3%D0%BA%D1%80%D0%B0%D1%97%D0%BD%D1%81%D1%8C%D0%BA%D0%B0%2C%20%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87%2C%20%E7%B9%81%E9%AB%94%E4%B8%AD%E6%96%87.';
- $visitor->setUrl('http://example.org/index.htm?' . $crazyTitle);
- self::checkResponse($visitor->doTrackPageView('Pageview: ' . $crazyTitle));
- }
-
- protected function recordVisitorSite2()
- {
- $visitor = self::getTracker($this->idSite2, $this->dateTime, $defaultInit = true);
- $visitor->setResolution(801, 301);
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?q=Search 1&IsPageView=1');
- self::checkResponse($visitor->doTrackPageView('IsPageView'));
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.35)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?random=PAGEVIEW, NOT SEARCH&gcat=Cat not but not keyword, so this is not search');
- self::checkResponse($visitor->doTrackPageView('This is a pageview, not a Search'));
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.4)->getDatetime());
- $visitor->setUrl('http://example.org/index.htm?gkwd=SHOULD be a Search with no result!&search_count=0');
- self::checkResponse($visitor->doTrackPageView('This is a Search'));
-
- // Testing UTF8 keywords
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.45)->getDatetime());
- $crazySearchTerm = 'You%20can%20use%20Piwik%20in%3A%20%E1%8A%A0%E1%88%9B%E1%88%AD%E1%8A%9B%2C%20%D8%A7%D9%84%D8%B9%D8%B1%D8%A8%D9%8A%D8%A9%2C%20%D0%91%D0%B5%D0%BB%D0%B0%D1%80%D1%83%D1%81%D0%BA%D0%B0%D1%8F%2C%20%D0%91%D1%8A%D0%BB%D0%B3%D0%B0%D1%80%D1%81%D0%BA%D0%B8%2C%20Catal%C3%A0%2C%20%C4%8Cesky%2C%20Dansk%2C%20Deutsch%2C%20%CE%95%CE%BB%CE%BB%CE%B7%CE%BD%CE%B9%CE%BA%CE%AC%2C%20English%2C%20Espa%C3%B1ol%2C%20Eesti%20keel%2C%20Euskara%2C%20%D9%81%D8%A7%D8%B1%D8%B3%DB%8C%2C%20Suomi%2C%20Fran%C3%A7ais%2C%20Galego%2C%20%D7%A2%D7%91%D7%A8%D7%99%D7%AA%2C%20Magyar%2C%20Bahasa%20Indonesia%2C%20%C3%8Dslenska%2C%20Italiano%2C%20%E6%97%A5%E6%9C%AC%E8%AA%9E%2C%20%E1%83%A5%E1%83%90%E1%83%A0%E1%83%97%E1%83%A3%E1%83%9A%E1%83%98%2C%20%ED%95%9C%EA%B5%AD%EC%96%B4%2C%20Lietuvi%C5%B3%2C%20Latvie%C5%A1u%2C%20Norsk%20(bokm%C3%A5l)%2C%20Nederlands%2C%20Norsk%20(nynorsk)%2C%20Polski%2C%20Portugu%C3%AAs%20brasileiro%2C%20Portugu%C3%AAs%2C%20Rom%C3%A2n%C4%83%2C%20%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9%2C%20Slovensky%2C%20Sloven%C5%A1%C4%8Dina%2C%20Shqip%2C%20Srpski%2C%20Svenska%2C%20%E0%B0%A4%E0%B1%86%E0%B0%B2%E0%B1%81%E0%B0%97%E0%B1%81%2C%20%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%2C%20T%C3%BCrk%C3%A7e%2C%20%D0%A3%D0%BA%D1%80%D0%B0%D1%97%D0%BD%D1%81%D1%8C%D0%BA%D0%B0%2C%20%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87%2C%20%E7%B9%81%E9%AB%94%E4%B8%AD%E6%96%87.';
- $visitor->setUrl('http://example.org/index.htm?gkwd=' . $crazySearchTerm . '&gcat=' . $crazySearchTerm . '&search_count=1');
- self::checkResponse($visitor->doTrackPageView('Site Search with 1 result'));
-
- $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.5)->getDatetime());
- self::checkResponse($visitor->doTrackSiteSearch("No Result Keyword!", "Bad No Result Category bis :(", $count = 0));
- return array($defaultInit, $visitor);
- }
+ protected function recordVisitorSite2()
+ {
+ $visitor = self::getTracker($this->idSite2, $this->dateTime, $defaultInit = true);
+ $visitor->setResolution(801, 301);
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?q=Search 1&IsPageView=1');
+ self::checkResponse($visitor->doTrackPageView('IsPageView'));
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.35)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?random=PAGEVIEW, NOT SEARCH&gcat=Cat not but not keyword, so this is not search');
+ self::checkResponse($visitor->doTrackPageView('This is a pageview, not a Search'));
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.4)->getDatetime());
+ $visitor->setUrl('http://example.org/index.htm?gkwd=SHOULD be a Search with no result!&search_count=0');
+ self::checkResponse($visitor->doTrackPageView('This is a Search'));
+
+ // Testing UTF8 keywords
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.45)->getDatetime());
+ $crazySearchTerm = 'You%20can%20use%20Piwik%20in%3A%20%E1%8A%A0%E1%88%9B%E1%88%AD%E1%8A%9B%2C%20%D8%A7%D9%84%D8%B9%D8%B1%D8%A8%D9%8A%D8%A9%2C%20%D0%91%D0%B5%D0%BB%D0%B0%D1%80%D1%83%D1%81%D0%BA%D0%B0%D1%8F%2C%20%D0%91%D1%8A%D0%BB%D0%B3%D0%B0%D1%80%D1%81%D0%BA%D0%B8%2C%20Catal%C3%A0%2C%20%C4%8Cesky%2C%20Dansk%2C%20Deutsch%2C%20%CE%95%CE%BB%CE%BB%CE%B7%CE%BD%CE%B9%CE%BA%CE%AC%2C%20English%2C%20Espa%C3%B1ol%2C%20Eesti%20keel%2C%20Euskara%2C%20%D9%81%D8%A7%D8%B1%D8%B3%DB%8C%2C%20Suomi%2C%20Fran%C3%A7ais%2C%20Galego%2C%20%D7%A2%D7%91%D7%A8%D7%99%D7%AA%2C%20Magyar%2C%20Bahasa%20Indonesia%2C%20%C3%8Dslenska%2C%20Italiano%2C%20%E6%97%A5%E6%9C%AC%E8%AA%9E%2C%20%E1%83%A5%E1%83%90%E1%83%A0%E1%83%97%E1%83%A3%E1%83%9A%E1%83%98%2C%20%ED%95%9C%EA%B5%AD%EC%96%B4%2C%20Lietuvi%C5%B3%2C%20Latvie%C5%A1u%2C%20Norsk%20(bokm%C3%A5l)%2C%20Nederlands%2C%20Norsk%20(nynorsk)%2C%20Polski%2C%20Portugu%C3%AAs%20brasileiro%2C%20Portugu%C3%AAs%2C%20Rom%C3%A2n%C4%83%2C%20%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9%2C%20Slovensky%2C%20Sloven%C5%A1%C4%8Dina%2C%20Shqip%2C%20Srpski%2C%20Svenska%2C%20%E0%B0%A4%E0%B1%86%E0%B0%B2%E0%B1%81%E0%B0%97%E0%B1%81%2C%20%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2%2C%20T%C3%BCrk%C3%A7e%2C%20%D0%A3%D0%BA%D1%80%D0%B0%D1%97%D0%BD%D1%81%D1%8C%D0%BA%D0%B0%2C%20%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87%2C%20%E7%B9%81%E9%AB%94%E4%B8%AD%E6%96%87.';
+ $visitor->setUrl('http://example.org/index.htm?gkwd=' . $crazySearchTerm . '&gcat=' . $crazySearchTerm . '&search_count=1');
+ self::checkResponse($visitor->doTrackPageView('Site Search with 1 result'));
+
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($this->dateTime)->addHour(0.5)->getDatetime());
+ self::checkResponse($visitor->doTrackSiteSearch("No Result Keyword!", "Bad No Result Category bis :(", $count = 0));
+ return array($defaultInit, $visitor);
+ }
}
diff --git a/tests/PHPUnit/Fixtures/TwoSitesEcommerceOrderWithItems.php b/tests/PHPUnit/Fixtures/TwoSitesEcommerceOrderWithItems.php
index 543b2e29f4..2000580da8 100644
--- a/tests/PHPUnit/Fixtures/TwoSitesEcommerceOrderWithItems.php
+++ b/tests/PHPUnit/Fixtures/TwoSitesEcommerceOrderWithItems.php
@@ -11,44 +11,44 @@
*/
class Test_Piwik_Fixture_TwoSitesEcommerceOrderWithItems extends Test_Piwik_BaseFixture
{
- public $dateTime = '2011-04-05 00:11:42';
- public $idSite = 1;
- public $idSite2 = 2;
+ public $dateTime = '2011-04-05 00:11:42';
+ public $idSite = 1;
+ public $idSite2 = 2;
public $idGoalStandard = 1;
-
+
public function setUp()
{
$this->setUpWebsitesAndGoals();
- self::setUpScheduledReports($this->idSite);
+ self::setUpScheduledReports($this->idSite);
$this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
+
private function setUpWebsitesAndGoals()
{
self::createWebsite($this->dateTime, $ecommerce = 1);
self::createWebsite($this->dateTime);
Piwik_Goals_API::getInstance()->addGoal(
- $this->idSite, 'title match, triggered ONCE', 'title', 'incredible', 'contains',
- $caseSensitive = false, $revenue = 10, $allowMultipleConversions = true
- );
+ $this->idSite, 'title match, triggered ONCE', 'title', 'incredible', 'contains',
+ $caseSensitive = false, $revenue = 10, $allowMultipleConversions = true
+ );
}
private function trackVisits()
{
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
- $idSite2 = $this->idSite2;
+ $idSite = $this->idSite;
+ $idSite2 = $this->idSite2;
$t = self::getTracker($idSite, $dateTime, $defaultInit = true);
// VISIT NO 1
$t->setUrl('http://example.org/index.htm');
$category = 'Electronics & Cameras';
- $price = 1111.11111;
+ $price = 1111.11111;
// VIEW product page
$t->setEcommerceView('SKU2', 'PRODUCT name', $category, $price);
@@ -118,7 +118,7 @@ class Test_Piwik_Fixture_TwoSitesEcommerceOrderWithItems extends Test_Piwik_Base
// This is a frequent use case: ecommerce shops tracking the order from backoffice
// without passing the custom variable 1st party cookie along since it's not known by back office
$visitorCustomVarSave = $t->visitorCustomVar;
- $t->visitorCustomVar = false;
+ $t->visitorCustomVar = false;
self::checkResponse($t->doTrackEcommerceOrder($orderId = '1037nsjusu4s3894', $grandTotal = 2000, $subTotal = 1500, $tax = 400, $shipping = 100, $discount = 0));
$t->visitorCustomVar = $visitorCustomVarSave;
diff --git a/tests/PHPUnit/Fixtures/TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers.php b/tests/PHPUnit/Fixtures/TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers.php
index f03a086390..faf3d45696 100644
--- a/tests/PHPUnit/Fixtures/TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers.php
+++ b/tests/PHPUnit/Fixtures/TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers.php
@@ -20,79 +20,78 @@ class Test_Piwik_Fixture_TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferr
'peace "," not war', // testing a keyword containing ,
'justice )(&^#%$ NOT corruption!',
);
-
+
public function setUp()
{
$this->setUpWebsitesAndGoals();
$this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
private function setUpWebsitesAndGoals()
{
- $siteCreated = '2010-02-01 11:22:33';
-
+ $siteCreated = '2010-02-01 11:22:33';
+
self::createWebsite($siteCreated);
- Piwik_Goals_API::getInstance()->addGoal($this->idSite, 'triggered php', 'manually', '', '');
- Piwik_Goals_API::getInstance()->addGoal(
- $this->idSite, 'another triggered php', 'manually', '', '', false, false, true);
-
- self::createWebsite($siteCreated);
- }
+ Piwik_Goals_API::getInstance()->addGoal($this->idSite, 'triggered php', 'manually', '', '');
+ Piwik_Goals_API::getInstance()->addGoal(
+ $this->idSite, 'another triggered php', 'manually', '', '', false, false, true);
+
+ self::createWebsite($siteCreated);
+ }
private function trackVisits()
{
$dateTime = $this->today;
- $idSite = $this->idSite;
- $idSite2 = $this->idSite2;
-
+ $idSite = $this->idSite;
+ $idSite2 = $this->idSite2;
+
$mozillaUserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; fr; rv:1.9.1.6) Gecko/20100101 Firefox/6.0";
$operaUserAgent = "Opera/9.80 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; ja-jp) Presto/2.9.181 Version/12.00";
- $t = self::getTracker($idSite, $dateTime, $defaultInit = true);
- $t->setTokenAuth(self::getTokenAuth());
- $t->enableBulkTracking();
- for ($daysIntoPast = 30; $daysIntoPast >= 0; $daysIntoPast--)
- {
+ $t = self::getTracker($idSite, $dateTime, $defaultInit = true);
+ $t->setTokenAuth(self::getTokenAuth());
+ $t->enableBulkTracking();
+ for ($daysIntoPast = 30; $daysIntoPast >= 0; $daysIntoPast--) {
// Visit 1: referrer website + test page views
$visitDateTime = Piwik_Date::factory($dateTime)->subDay($daysIntoPast)->getDatetime();
-
+
$t->setNewVisitorId();
$t->setIdSite($idSite);
$t->setUserAgent($mozillaUserAgent);
-
+
$t->setUrlReferrer('http://www.referrer' . ($daysIntoPast % 5) . '.com/theReferrerPage' . ($daysIntoPast % 2) . '.html');
$t->setUrl('http://example.org/my/dir/page' . ($daysIntoPast % 4) . '?foo=bar&baz=bar');
$t->setForceVisitDateTime($visitDateTime);
- $t->setGenerationTime($daysIntoPast * 100 + 100);
+ $t->setGenerationTime($daysIntoPast * 100 + 100);
self::assertTrue($t->doTrackPageView('incredible title ' . ($daysIntoPast % 3)));
- // Trigger goal n°1 once
- self::assertTrue($t->doTrackGoal(1));
+ // Trigger goal n°1 once
+ self::assertTrue($t->doTrackGoal(1));
- // Trigger goal n°2 twice
- self::assertTrue($t->doTrackGoal(2));
- $t->setForceVisitDateTime(Piwik_Date::factory($visitDateTime)->addHour(0.1)->getDatetime());
- self::assertTrue($t->doTrackGoal(2));
+ // Trigger goal n°2 twice
+ self::assertTrue($t->doTrackGoal(2));
+ $t->setForceVisitDateTime(Piwik_Date::factory($visitDateTime)->addHour(0.1)->getDatetime());
+ self::assertTrue($t->doTrackGoal(2));
// VISIT 2: search engine
$t->setForceVisitDateTime(Piwik_Date::factory($visitDateTime)->addHour(3)->getDatetime());
$t->setUrlReferrer('http://google.com/search?q=' . urlencode($this->keywords[$daysIntoPast % 3]));
- $t->setGenerationTime($daysIntoPast * 100 + 200);
+ $t->setGenerationTime($daysIntoPast * 100 + 200);
self::assertTrue($t->doTrackPageView('not an incredible title '));
-
+
// VISIT 1 for idSite = 2
$t->setIdSite($idSite2);
$t->setNewVisitorId();
$t->setUserAgent($daysIntoPast % 2 == 0 ? $mozillaUserAgent : $operaUserAgent);
-
+
$t->setForceVisitDateTime($visitDateTime);
$t->setUrl('http://example.org/');
- $t->setGenerationTime($daysIntoPast * 100 + 300);
+ $t->setGenerationTime($daysIntoPast * 100 + 300);
self::assertTrue($t->doTrackPageView('so-so page title'));
}
self::checkResponse($t->doBulkTrack());
diff --git a/tests/PHPUnit/Fixtures/TwoSitesTwoVisitorsDifferentDays.php b/tests/PHPUnit/Fixtures/TwoSitesTwoVisitorsDifferentDays.php
index b5ae0fad67..ccfb6ca509 100644
--- a/tests/PHPUnit/Fixtures/TwoSitesTwoVisitorsDifferentDays.php
+++ b/tests/PHPUnit/Fixtures/TwoSitesTwoVisitorsDifferentDays.php
@@ -16,21 +16,21 @@ class Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays extends Test_Piwik_Bas
public $idGoal1 = 1;
public $idGoal2 = 2;
public $dateTime = '2010-01-03 11:22:33';
-
+
public $allowConversions = false;
-
+
public function setUp()
{
$this->setUpWebsitesAndGoals();
- self::setUpScheduledReports($this->idSite1);
+ self::setUpScheduledReports($this->idSite1);
$this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
+
private function setUpWebsitesAndGoals()
{
// tests run in UTC, the Tracker in UTC
@@ -43,46 +43,46 @@ class Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays extends Test_Piwik_Bas
Piwik_Goals_API::getInstance()->addGoal($this->idSite1, 'all', 'url', 'http', 'contains', false, 5);
Piwik_Goals_API::getInstance()->addGoal($this->idSite2, 'all', 'url', 'http', 'contains');
}
-
+
Piwik_SitesManager_API::getInstance()->updateSite(
- $this->idSite1, "Site 1", $urls = null, $ecommerce = null, $siteSearch = null,
- $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIps = null,
- $excludedQueryParameters = null, $timezone = null, $currency = null, $group = null,
- $startDate = null, $excludedUserAgents = null, $keepURLFragments = 2); // KEEP_URL_FRAGMENT_NO No for idSite 1
+ $this->idSite1, "Site 1", $urls = null, $ecommerce = null, $siteSearch = null,
+ $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIps = null,
+ $excludedQueryParameters = null, $timezone = null, $currency = null, $group = null,
+ $startDate = null, $excludedUserAgents = null, $keepURLFragments = 2); // KEEP_URL_FRAGMENT_NO No for idSite 1
Piwik_SitesManager_API::getInstance()->updateSite(
- $this->idSite2, "Site 2", $urls = null, $ecommerce = null, $siteSearch = null,
- $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIps = null,
- $excludedQueryParameters = null, $timezone = null, $currency = null, $group = null,
- $startDate = null, $excludedUserAgents = null, $keepURLFragments = 1); // KEEP_URL_FRAGMENT_YES Yes for idSite 2
+ $this->idSite2, "Site 2", $urls = null, $ecommerce = null, $siteSearch = null,
+ $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIps = null,
+ $excludedQueryParameters = null, $timezone = null, $currency = null, $group = null,
+ $startDate = null, $excludedUserAgents = null, $keepURLFragments = 1); // KEEP_URL_FRAGMENT_YES Yes for idSite 2
}
private function trackVisits()
{
$dateTime = $this->dateTime;
- $idSite = $this->idSite1;
- $idSite2 = $this->idSite2;
+ $idSite = $this->idSite1;
+ $idSite2 = $this->idSite2;
// -
// First visitor on Idsite 1: two page views
$datetimeSpanOverTwoDays = '2010-01-03 23:55:00';
- $visitorA = self::getTracker($idSite, $datetimeSpanOverTwoDays, $defaultInit = true);
+ $visitorA = self::getTracker($idSite, $datetimeSpanOverTwoDays, $defaultInit = true);
$visitorA->setUrlReferrer('http://referer.com/page.htm?param=valuewith some spaces');
$visitorA->setUrl('http://example.org/index.htm#ignoredFragment');
$visitorA->DEBUG_APPEND_URL = '&_idts=' . Piwik_Date::factory($datetimeSpanOverTwoDays)->getTimestamp();
- $visitorA->setGenerationTime(123);
+ $visitorA->setGenerationTime(123);
self::checkResponse($visitorA->doTrackPageView('first page view'));
$visitorA->setForceVisitDateTime(Piwik_Date::factory($datetimeSpanOverTwoDays)->addHour(0.1)->getDatetime());
// testing with empty URL and empty page title
$visitorA->setUrl(' ');
- $visitorA->setGenerationTime(223);
+ $visitorA->setGenerationTime(223);
self::checkResponse($visitorA->doTrackPageView(' '));
// -
// Second new visitor on Idsite 1: one page view
$visitorB = self::getTracker($idSite, $dateTime, $defaultInit = true);
$visitorB->enableBulkTracking();
- $visitorB->setTokenAuth(self::getTokenAuth());
+ $visitorB->setTokenAuth(self::getTokenAuth());
$visitorB->setIp('100.52.156.83');
$visitorB->setResolution(800, 300);
$visitorB->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(1)->getDatetime());
@@ -90,7 +90,7 @@ class Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays extends Test_Piwik_Bas
$visitorB->setUserAgent('Opera/9.63 (Windows NT 5.1; U; en) Presto/2.1.1');
$visitorB->setUrl('http://example.org/products');
$visitorB->DEBUG_APPEND_URL = '&_idts=' . Piwik_Date::factory($dateTime)->addHour(1)->getTimestamp();
- $visitorB->setGenerationTime(153);
+ $visitorB->setGenerationTime(153);
self::assertTrue($visitorB->doTrackPageView('first page view'));
// -
@@ -102,12 +102,12 @@ class Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays extends Test_Piwik_Bas
$visitorB->setUrlReferrer('http://referer.com/Other_Page.htm');
$visitorB->setUrl('http://example.org/index.htm');
- $visitorB->setGenerationTime(323);
+ $visitorB->setGenerationTime(323);
self::assertTrue($visitorB->doTrackPageView('second visitor/two days later/a new visit'));
// Second page view 6 minutes later
$visitorB->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(48)->addHour(0.1)->getDatetime());
$visitorB->setUrl('http://example.org/thankyou');
- $visitorB->setGenerationTime(173);
+ $visitorB->setGenerationTime(173);
self::assertTrue($visitorB->doTrackPageView('second visitor/two days later/second page view'));
// testing a strange combination causing an error in r3767
@@ -118,7 +118,7 @@ class Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays extends Test_Piwik_Bas
// Actions.getPageTitle tested with this title
$visitorB->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(48)->addHour(0.25)->getDatetime());
- $visitorB->setGenerationTime(452);
+ $visitorB->setGenerationTime(452);
self::assertTrue($visitorB->doTrackPageView('Checkout / Purchasing...'));
self::checkResponse($visitorB->doBulkTrack());
@@ -129,12 +129,12 @@ class Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays extends Test_Piwik_Bas
$visitorAsite2->setUrlReferrer('http://only-homepage-referer.com/');
$visitorAsite2->setUrl('http://example2.com/home#notIgnoredFragment#');
$visitorAsite2->DEBUG_APPEND_URL = '&_idts=' . Piwik_Date::factory($dateTime)->addHour(24)->getTimestamp();
- $visitorAsite2->setGenerationTime(193);
+ $visitorAsite2->setGenerationTime(193);
self::checkResponse($visitorAsite2->doTrackPageView('Website 2 page view'));
// test with invalid URL
$visitorAsite2->setUrl('this is invalid url');
// and an empty title
- $visitorAsite2->setGenerationTime(203);
+ $visitorAsite2->setGenerationTime(203);
self::checkResponse($visitorAsite2->doTrackPageView(''));
}
}
diff --git a/tests/PHPUnit/Fixtures/TwoSitesVisitsInPast.php b/tests/PHPUnit/Fixtures/TwoSitesVisitsInPast.php
index 2584eb8b46..d476bb7933 100644
--- a/tests/PHPUnit/Fixtures/TwoSitesVisitsInPast.php
+++ b/tests/PHPUnit/Fixtures/TwoSitesVisitsInPast.php
@@ -18,17 +18,17 @@ class Test_Piwik_Fixture_TwoSitesVisitsInPast extends Test_Piwik_BaseFixture
public $idSite = 1;
public $idSite2 = 2;
- public function setUp()
- {
+ public function setUp()
+ {
$this->setUpWebsitesAndGoals();
$this->trackVisits();
- }
-
- public function tearDown()
- {
- // empty
- }
-
+ }
+
+ public function tearDown()
+ {
+ // empty
+ }
+
public function setUpWebsitesAndGoals()
{
self::createWebsite($this->dateTimeFirstDateWebsite1);
diff --git a/tests/PHPUnit/Fixtures/TwoSitesWithAnnotations.php b/tests/PHPUnit/Fixtures/TwoSitesWithAnnotations.php
index 19d03b78e5..af5efd26be 100644
--- a/tests/PHPUnit/Fixtures/TwoSitesWithAnnotations.php
+++ b/tests/PHPUnit/Fixtures/TwoSitesWithAnnotations.php
@@ -11,61 +11,60 @@
*/
class Test_Piwik_Fixture_TwoSitesWithAnnotations extends Test_Piwik_BaseFixture
{
- public $dateTime = '2011-01-01 00:11:42';
- public $idSite1 = 1;
- public $idSite2 = 2;
-
- public function setUp()
- {
- $this->setUpWebsitesAndGoals();
- $this->addAnnotations();
- }
-
- public function tearDown()
- {
- // empty
- }
-
- private function addAnnotations()
- {
- // create fake access for fake username
- $access = new FakeAccess();
- FakeAccess::$superUser = true;
- Zend_Registry::set('access', $access);
-
- // add two annotations per week for three months, starring every third annotation
- // first month in 2011, second two in 2012
- $count = 0;
- $dateStart = Piwik_Date::factory('2011-12-01');
- $dateEnd = Piwik_Date::factory('2012-03-01');
- while ($dateStart->getTimestamp() < $dateEnd->getTimestamp())
- {
- $starred = $count % 3 == 0 ? 1 : 0;
- $site1Text = "$count: Site 1 annotation for ".$dateStart->toString();
- $site2Text = "$count: Site 2 annotation for ".$dateStart->toString();
-
- Piwik_Annotations_API::getInstance()->add($this->idSite1, $dateStart->toString(), $site1Text, $starred);
- Piwik_Annotations_API::getInstance()->add($this->idSite2, $dateStart->toString(), $site2Text, $starred);
-
- $nextDay = $dateStart->addDay(1);
- ++$count;
-
- $starred = $count % 3 == 0 ? 1 : 0;
- $site1Text = "$count: Site 1 annotation for ".$nextDay->toString();
- $site2Text = "$count: Site 2 annotation for ".$nextDay->toString();
-
- Piwik_Annotations_API::getInstance()->add($this->idSite1, $nextDay->toString(), $site1Text, $starred);
- Piwik_Annotations_API::getInstance()->add($this->idSite2, $nextDay->toString(), $site2Text, $starred);
-
- $dateStart = $dateStart->addPeriod(1, 'WEEK');
- ++$count;
- }
- }
-
- private function setUpWebsitesAndGoals()
- {
- // add two websites
- self::createWebsite($this->dateTime, $ecommerce = 1);
- self::createWebsite($this->dateTime, $ecommerce = 1);
- }
+ public $dateTime = '2011-01-01 00:11:42';
+ public $idSite1 = 1;
+ public $idSite2 = 2;
+
+ public function setUp()
+ {
+ $this->setUpWebsitesAndGoals();
+ $this->addAnnotations();
+ }
+
+ public function tearDown()
+ {
+ // empty
+ }
+
+ private function addAnnotations()
+ {
+ // create fake access for fake username
+ $access = new FakeAccess();
+ FakeAccess::$superUser = true;
+ Zend_Registry::set('access', $access);
+
+ // add two annotations per week for three months, starring every third annotation
+ // first month in 2011, second two in 2012
+ $count = 0;
+ $dateStart = Piwik_Date::factory('2011-12-01');
+ $dateEnd = Piwik_Date::factory('2012-03-01');
+ while ($dateStart->getTimestamp() < $dateEnd->getTimestamp()) {
+ $starred = $count % 3 == 0 ? 1 : 0;
+ $site1Text = "$count: Site 1 annotation for " . $dateStart->toString();
+ $site2Text = "$count: Site 2 annotation for " . $dateStart->toString();
+
+ Piwik_Annotations_API::getInstance()->add($this->idSite1, $dateStart->toString(), $site1Text, $starred);
+ Piwik_Annotations_API::getInstance()->add($this->idSite2, $dateStart->toString(), $site2Text, $starred);
+
+ $nextDay = $dateStart->addDay(1);
+ ++$count;
+
+ $starred = $count % 3 == 0 ? 1 : 0;
+ $site1Text = "$count: Site 1 annotation for " . $nextDay->toString();
+ $site2Text = "$count: Site 2 annotation for " . $nextDay->toString();
+
+ Piwik_Annotations_API::getInstance()->add($this->idSite1, $nextDay->toString(), $site1Text, $starred);
+ Piwik_Annotations_API::getInstance()->add($this->idSite2, $nextDay->toString(), $site2Text, $starred);
+
+ $dateStart = $dateStart->addPeriod(1, 'WEEK');
+ ++$count;
+ }
+ }
+
+ private function setUpWebsitesAndGoals()
+ {
+ // add two websites
+ self::createWebsite($this->dateTime, $ecommerce = 1);
+ self::createWebsite($this->dateTime, $ecommerce = 1);
+ }
}
diff --git a/tests/PHPUnit/Fixtures/TwoVisitsNoKeywordWithBot.php b/tests/PHPUnit/Fixtures/TwoVisitsNoKeywordWithBot.php
index afb73d687d..a0cf6d8128 100644
--- a/tests/PHPUnit/Fixtures/TwoVisitsNoKeywordWithBot.php
+++ b/tests/PHPUnit/Fixtures/TwoVisitsNoKeywordWithBot.php
@@ -13,19 +13,19 @@
class Test_Piwik_Fixture_TwoVisitsNoKeywordWithBot extends Test_Piwik_BaseFixture
{
public $dateTime = '2010-03-06 11:22:33';
- public $idSite = 1;
-
+ public $idSite = 1;
+
public function setUp()
{
$this->setUpWebsitesAndGoals();
$this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
+
private function setUpWebsitesAndGoals()
{
self::createWebsite($this->dateTime);
@@ -35,8 +35,8 @@ class Test_Piwik_Fixture_TwoVisitsNoKeywordWithBot extends Test_Piwik_BaseFixtur
{
// tests run in UTC, the Tracker in UTC
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
- $t = self::getTracker($idSite, $dateTime, $defaultInit = true, $useThirdPartyCookie = 1);
+ $idSite = $this->idSite;
+ $t = self::getTracker($idSite, $dateTime, $defaultInit = true, $useThirdPartyCookie = 1);
// Also testing to record this as a bot while specifically allowing bots
$t->setUserAgent('Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)');
@@ -56,7 +56,7 @@ class Test_Piwik_Fixture_TwoVisitsNoKeywordWithBot extends Test_Piwik_BaseFixtur
$t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(2)->getDatetime());
$t->setUrlReferrer('http://www.google.com.vn/url?sa=t&rct=j&q=%3C%3E%26%5C%22the%20pdo%20extension%20is%20required%20for%20this%20adapter%20but%20the%20extension%20is%20not%20loaded&source=web&cd=4&ved=0FjAD&url=http%3A%2F%2Fforum.piwik.org%2Fread.php%3F2%2C1011&ei=y-HHAQ&usg=AFQjCN2-nt5_GgDeg&cad=rja');
- // Test with empty title, that the output of Live is valid
+ // Test with empty title, that the output of Live is valid
self::checkResponse($t->doTrackPageView(''));
}
diff --git a/tests/PHPUnit/Fixtures/TwoVisitsWithCustomVariables.php b/tests/PHPUnit/Fixtures/TwoVisitsWithCustomVariables.php
index 12aa1b6213..070d266ecf 100644
--- a/tests/PHPUnit/Fixtures/TwoVisitsWithCustomVariables.php
+++ b/tests/PHPUnit/Fixtures/TwoVisitsWithCustomVariables.php
@@ -11,29 +11,29 @@
*/
class Test_Piwik_Fixture_TwoVisitsWithCustomVariables extends Test_Piwik_BaseFixture
{
- public $dateTime = '2010-01-03 11:22:33';
- public $idSite = 1;
- public $idGoal1 = 1;
- public $idGoal2 = 2;
+ public $dateTime = '2010-01-03 11:22:33';
+ public $idSite = 1;
+ public $idGoal1 = 1;
+ public $idGoal2 = 2;
public $visitorId = '61e8cc2d51fea26d';
- public $useEscapedQuotes = true;
+ public $useEscapedQuotes = true;
public $doExtraQuoteTests = true;
- public $resolutionWidthToUse = 1111;
- public $resolutionHeightToUse = 222;
-
- public function setUp()
- {
- $this->setUpWebsitesAndGoals();
- $this->trackVisits();
- }
-
- public function tearDown()
- {
- // empty
- }
-
+ public $resolutionWidthToUse = 1111;
+ public $resolutionHeightToUse = 222;
+
+ public function setUp()
+ {
+ $this->setUpWebsitesAndGoals();
+ $this->trackVisits();
+ }
+
+ public function tearDown()
+ {
+ // empty
+ }
+
private function setUpWebsitesAndGoals()
{
// tests run in UTC, the Tracker in UTC
@@ -45,9 +45,9 @@ class Test_Piwik_Fixture_TwoVisitsWithCustomVariables extends Test_Piwik_BaseFix
private function trackVisits()
{
$dateTime = $this->dateTime;
- $idSite = $this->idSite;
- $idGoal = $this->idGoal1;
- $idGoal2 = $this->idGoal2;
+ $idSite = $this->idSite;
+ $idGoal = $this->idGoal1;
+ $idGoal2 = $this->idGoal2;
$visitorA = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
// Used to test actual referer + keyword position in Live!
@@ -105,9 +105,8 @@ class Test_Piwik_Fixture_TwoVisitsWithCustomVariables extends Test_Piwik_BaseFix
// -
// Second new visitor on Idsite 1: one page view
$visitorB = self::getTracker($idSite, $dateTime, $defaultInit = true);
- if (!empty($this->visitorId))
- {
- $visitorB->setVisitorId($this->visitorId);
+ if (!empty($this->visitorId)) {
+ $visitorB->setVisitorId($this->visitorId);
}
$visitorB->setUrlReferrer('');
diff --git a/tests/PHPUnit/Fixtures/VisitsOverSeveralDays.php b/tests/PHPUnit/Fixtures/VisitsOverSeveralDays.php
index ded57b1057..e53db1ace5 100644
--- a/tests/PHPUnit/Fixtures/VisitsOverSeveralDays.php
+++ b/tests/PHPUnit/Fixtures/VisitsOverSeveralDays.php
@@ -7,7 +7,7 @@
*/
/**
- * Adds one website and tracks several visits from one visitor on
+ * Adds one website and tracks several visits from one visitor on
* different days that span about a month apart.
*/
class Test_Piwik_Fixture_VisitsOverSeveralDays extends Test_Piwik_BaseFixture
@@ -19,45 +19,45 @@ class Test_Piwik_Fixture_VisitsOverSeveralDays extends Test_Piwik_BaseFixture
'2011-01-15 01:00:00',
'2011-01-16 01:00:00',
);
-
- public $idSite = 1;
- public $idSite2 = 2;
-
+
+ public $idSite = 1;
+ public $idSite2 = 2;
+
// one per visit
public $referrerUrls = array(
- 'http://facebook.com/whatever',
- 'http://www.facebook.com/another/path',
- 'http://fb.me/?q=sdlfjs&n=slfjsd',
- 'http://twitter.com/whatever2',
- 'http://www.twitter.com/index?a=2334',
- 'http://t.co/id/?y=dsfs',
- 'http://www.flickr.com',
- 'http://xanga.com',
- 'http://skyrock.com',
- 'http://mixi.jp',
+ 'http://facebook.com/whatever',
+ 'http://www.facebook.com/another/path',
+ 'http://fb.me/?q=sdlfjs&n=slfjsd',
+ 'http://twitter.com/whatever2',
+ 'http://www.twitter.com/index?a=2334',
+ 'http://t.co/id/?y=dsfs',
+ 'http://www.flickr.com',
+ 'http://xanga.com',
+ 'http://skyrock.com',
+ 'http://mixi.jp',
);
-
+
public function setUp()
{
$this->setUpWebsitesAndGoals();
$this->trackVisits();
}
-
+
public function tearDown()
{
- // empty
+ // empty
}
-
+
private function setUpWebsitesAndGoals()
{
- self::createWebsite($this->dateTimes[0], $ecommerce = 0, $siteName = 'Site AAAAAA');
- self::createWebsite($this->dateTimes[0], $ecommerce = 0, $siteName = 'SITE BBbbBB');
+ self::createWebsite($this->dateTimes[0], $ecommerce = 0, $siteName = 'Site AAAAAA');
+ self::createWebsite($this->dateTimes[0], $ecommerce = 0, $siteName = 'SITE BBbbBB');
}
private function trackVisits()
{
$dateTimes = $this->dateTimes;
- $idSite = $this->idSite;
+ $idSite = $this->idSite;
$i = 0;
$ridx = 0;
@@ -85,14 +85,14 @@ class Test_Piwik_Fixture_VisitsOverSeveralDays extends Test_Piwik_BaseFixture
self::checkResponse($visitor->doTrackPageView('ou pas'));
- if($i <= 3 ) {
+ if ($i <= 3) {
- $visitor = self::getTracker($this->idSite2, $dateTime, $defaultInit = true);
- $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.1)->getDatetime());
- $visitor->setUrl('http://example.org/homepage');
- $visitor->setUrlReferrer($this->referrerUrls[$ridx-1]);
- self::checkResponse($visitor->doTrackPageView('Second website'));
- }
+ $visitor = self::getTracker($this->idSite2, $dateTime, $defaultInit = true);
+ $visitor->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.1)->getDatetime());
+ $visitor->setUrl('http://example.org/homepage');
+ $visitor->setUrlReferrer($this->referrerUrls[$ridx - 1]);
+ self::checkResponse($visitor->doTrackPageView('Second website'));
+ }
}
}
}
diff --git a/tests/PHPUnit/Integration/AnnotationsTest.php b/tests/PHPUnit/Integration/AnnotationsTest.php
index 112732731c..256f401eaa 100755
--- a/tests/PHPUnit/Integration/AnnotationsTest.php
+++ b/tests/PHPUnit/Integration/AnnotationsTest.php
@@ -8,355 +8,319 @@
class AnnotationsTest extends IntegrationTestCase
{
- public static $fixture = null;
-
- public function getOutputPrefix()
- {
- return 'annotations';
- }
-
- public function getApiForTesting()
- {
- $idSite1 = self::$fixture->idSite1;
- $idSite2 = self::$fixture->idSite2;
-
- return array(
-
- // get
- array('Annotations.get', array('idSite' => $idSite1,
- 'date' => '2012-01-01',
- 'periods' => 'day',
- 'otherRequestParameters' => array('idNote' => 1))),
-
- // getAll
- array('Annotations.getAll', array('idSite' => $idSite1,
- 'date' => '2011-12-01',
- 'periods' => array('day', 'week', 'month'))),
- array('Annotations.getAll', array('idSite' => $idSite1,
- 'date' => '2012-01-01',
- 'periods' => array('year'))),
- array('Annotations.getAll', array('idSite' => $idSite1,
- 'date' => '2012-03-01',
- 'periods' => array('week'),
- 'otherRequestParameters' => array('lastN' => 6),
- 'testSuffix' => '_lastN')),
- array('Annotations.getAll', array('idSite' => $idSite1,
- 'date' => '2012-01-15,2012-02-15',
- 'periods' => array('range'),
- 'otherRequestParameters' => array('lastN' => 6),
- 'testSuffix' => '_range')),
- array('Annotations.getAll', array('idSite' => 'all',
- 'date' => '2012-01-01',
- 'periods' => array('month'),
- 'testSuffix' => '_multipleSites')),
-
- // getAnnotationCountForDates
- array('Annotations.getAnnotationCountForDates', array('idSite' => $idSite1,
- 'date' => '2011-12-01',
- 'periods' => array('day', 'week', 'month'))),
- array('Annotations.getAnnotationCountForDates', array('idSite' => $idSite1,
- 'date' => '2012-01-01',
- 'periods' => array('year'))),
- array('Annotations.getAnnotationCountForDates', array('idSite' => $idSite1,
- 'date' => '2012-03-01',
- 'periods' => array('week'),
- 'otherRequestParameters' => array('lastN' => 6),
- 'testSuffix' => '_lastN')),
- array('Annotations.getAnnotationCountForDates', array('idSite' => $idSite1,
- 'date' => '2012-01-15,2012-02-15',
- 'periods' => array('range'),
- 'otherRequestParameters' => array('lastN' => 6),
- 'testSuffix' => '_range')),
- array('Annotations.getAnnotationCountForDates', array('idSite' => 'all',
- 'date' => '2012-01-01',
- 'periods' => array('month'),
- 'testSuffix' => '_multipleSites')),
- );
- }
-
- /**
- * @dataProvider getApiForTesting
- * @group Integration
- * @group Annotations
- */
- public function testApi($api, $params)
- {
- $this->runApiTests($api, $params);
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testAddMultipleSitesFail()
- {
- try
- {
- Piwik_Annotations_API::getInstance()->add("1,2,3", "2012-01-01", "whatever");
- $this->fail("add should fail when given multiple sites in idSite");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testAddInvalidDateFail()
- {
- try
- {
- Piwik_Annotations_API::getInstance()->add(self::$fixture->idSite1, "invaliddate", "whatever");
- $this->fail("add should fail when given invalid date");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testSaveMultipleSitesFail()
- {
- try
- {
- Piwik_Annotations_API::getInstance()->save("1,2,3", 0);
- $this->fail("save should fail when given multiple sites");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testSaveInvalidDateFail()
- {
- try
- {
- Piwik_Annotations_API::getInstance()->save(self::$fixture->idSite1, 0, "invaliddate");
- $this->fail("save should fail when given an invalid date");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testSaveInvalidNoteIdFail()
- {
- try
- {
- Piwik_Annotations_API::getInstance()->save(self::$fixture->idSite1, -1);
- $this->fail("save should fail when given an invalid note id");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testDeleteMultipleSitesFail()
- {
- try
- {
- Piwik_Annotations_API::getInstance()->delete("1,2,3", 0);
- $this->fail("delete should fail when given multiple site IDs");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testDeleteInvalidNoteIdFail()
- {
- try
- {
- Piwik_Annotations_API::getInstance()->delete(self::$fixture->idSite1, -1);
- $this->fail("delete should fail when given an invalid site ID");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testGetMultipleSitesFail()
- {
- try
- {
- Piwik_Annotations_API::getInstance()->get("1,2,3", 0);
- $this->fail("get should fail when given multiple site IDs");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testGetInvalidNoteIdFail()
- {
- try
- {
- Piwik_Annotations_API::getInstance()->get(self::$fixture->idSite1, -1);
- $this->fail("get should fail when given an invalid note ID");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testSaveSuccess()
- {
- Piwik_Annotations_API::getInstance()->save(
- self::$fixture->idSite1, 0, $date = '2011-04-01', $note = 'new note text', $starred = 1);
-
- $expectedAnnotation = array(
- 'date' => '2011-04-01',
- 'note' => 'new note text',
- 'starred' => 1,
- 'user' => 'superUserLogin',
- 'idNote' => 0,
- 'canEditOrDelete' => true
- );
- $this->assertEquals($expectedAnnotation, Piwik_Annotations_API::getInstance()->get(self::$fixture->idSite1, 0));
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testSaveNoChangesSuccess()
- {
- Piwik_Annotations_API::getInstance()->save(self::$fixture->idSite1, 1);
-
- $expectedAnnotation = array(
- 'date' => '2011-12-02',
- 'note' => '1: Site 1 annotation for 2011-12-02',
- 'starred' => 0,
- 'user' => 'superUserLogin',
- 'idNote' => 1,
- 'canEditOrDelete' => true
- );
- $this->assertEquals($expectedAnnotation, Piwik_Annotations_API::getInstance()->get(self::$fixture->idSite1, 1));
- }
-
- /**
- * @group Integration
- * @group Annotations
- */
- public function testDeleteSuccess()
- {
- Piwik_Annotations_API::getInstance()->delete(self::$fixture->idSite1, 1);
-
- try
- {
- Piwik_Annotations_API::getInstance()->get(self::$fixture->idSite1, 1);
- $this->fail("failed to delete annotation");
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
-
- public function getPermissionsFailData()
- {
- return array(
- // getAll
- array(false, false, "module=API&method=Annotations.getAll&idSite=1&date=2012-01-01&period=year", true, "getAll should throw if user does not have view access"),
-
- // get
- array(false, false, "module=API&method=Annotations.get&idSite=1&idNote=0", true, "get should throw if user does not have view access"),
-
- // getAnnotationCountForDates
- array(false, false, "module=API&method=Annotations.getAnnotationCountForDates&idSite=1&date=2012-01-01&period=year", true, "getAnnotationCountForDates should throw if user does not have view access"),
-
- // add
- array(false, false, "module=API&method=Annotations.add&idSite=1&date=2011-02-01&note=whatever", true, "add should throw if user does not have view access"),
- array(false, true, "module=API&method=Annotations.add&idSite=1&date=2011-02-01&note=whatever2", false, "add should not throw if user has view access"),
- array(true, true, "module=API&method=Annotations.add&idSite=1&date=2011-02-01&note=whatever3", false, "add should not throw if user has admin access"),
-
- // save
- array(false, false, "module=API&method=Annotations.save&idSite=1&idNote=0&date=2011-03-01&note=newnote", true, "save should throw if user does not have view access"),
- array(false, true, "module=API&method=Annotations.save&idSite=1&idNote=0&date=2011-03-01&note=newnote", true, "save should throw if user has view access but did not edit note"),
- array(true, true, "module=API&method=Annotations.save&idSite=1&idNote=0&date=2011-03-01&note=newnote", false, "save should not throw if user has admin access"),
-
- // delete
- array(false, false, "module=API&method=Annotations.delete&idSite=1&idNote=0", true, "delete should throw if user does not have view access"),
- array(false, true, "module=API&method=Annotations.delete&idSite=1&idNote=0", true, "delete should throw if user does not have view access"),
- array(true, true, "module=API&method=Annotations.delete&idSite=1&idNote=0", false, "delete should not throw if user has admin access"),
- );
- }
-
- /**
- * @dataProvider getPermissionsFailData
- * @group Integration
- * @group Annotations
- */
- public function testMethodPermissions( $hasAdminAccess, $hasViewAccess, $request, $checkException, $failMessage )
- {
- // create fake access that denies user access
- $access = new FakeAccess();
- FakeAccess::$superUser = false;
- FakeAccess::$idSitesAdmin = $hasAdminAccess ? array(self::$fixture->idSite1) : array();
- FakeAccess::$idSitesView = $hasViewAccess ? array(self::$fixture->idSite1) : array();
- Zend_Registry::set('access', $access);
-
- if ($checkException)
- {
- try
- {
- $request = new Piwik_API_Request($request);
- $request->process();
- $this->fail($failMessage);
- }
- catch (Exception $ex)
- {
- // pass
- }
- }
- else
- {
- $request = new Piwik_API_Request($request);
- $request->process();
- }
- }
+ public static $fixture = null;
+
+ public function getOutputPrefix()
+ {
+ return 'annotations';
+ }
+
+ public function getApiForTesting()
+ {
+ $idSite1 = self::$fixture->idSite1;
+ $idSite2 = self::$fixture->idSite2;
+
+ return array(
+
+ // get
+ array('Annotations.get', array('idSite' => $idSite1,
+ 'date' => '2012-01-01',
+ 'periods' => 'day',
+ 'otherRequestParameters' => array('idNote' => 1))),
+
+ // getAll
+ array('Annotations.getAll', array('idSite' => $idSite1,
+ 'date' => '2011-12-01',
+ 'periods' => array('day', 'week', 'month'))),
+ array('Annotations.getAll', array('idSite' => $idSite1,
+ 'date' => '2012-01-01',
+ 'periods' => array('year'))),
+ array('Annotations.getAll', array('idSite' => $idSite1,
+ 'date' => '2012-03-01',
+ 'periods' => array('week'),
+ 'otherRequestParameters' => array('lastN' => 6),
+ 'testSuffix' => '_lastN')),
+ array('Annotations.getAll', array('idSite' => $idSite1,
+ 'date' => '2012-01-15,2012-02-15',
+ 'periods' => array('range'),
+ 'otherRequestParameters' => array('lastN' => 6),
+ 'testSuffix' => '_range')),
+ array('Annotations.getAll', array('idSite' => 'all',
+ 'date' => '2012-01-01',
+ 'periods' => array('month'),
+ 'testSuffix' => '_multipleSites')),
+
+ // getAnnotationCountForDates
+ array('Annotations.getAnnotationCountForDates', array('idSite' => $idSite1,
+ 'date' => '2011-12-01',
+ 'periods' => array('day', 'week', 'month'))),
+ array('Annotations.getAnnotationCountForDates', array('idSite' => $idSite1,
+ 'date' => '2012-01-01',
+ 'periods' => array('year'))),
+ array('Annotations.getAnnotationCountForDates', array('idSite' => $idSite1,
+ 'date' => '2012-03-01',
+ 'periods' => array('week'),
+ 'otherRequestParameters' => array('lastN' => 6),
+ 'testSuffix' => '_lastN')),
+ array('Annotations.getAnnotationCountForDates', array('idSite' => $idSite1,
+ 'date' => '2012-01-15,2012-02-15',
+ 'periods' => array('range'),
+ 'otherRequestParameters' => array('lastN' => 6),
+ 'testSuffix' => '_range')),
+ array('Annotations.getAnnotationCountForDates', array('idSite' => 'all',
+ 'date' => '2012-01-01',
+ 'periods' => array('month'),
+ 'testSuffix' => '_multipleSites')),
+ );
+ }
+
+ /**
+ * @dataProvider getApiForTesting
+ * @group Integration
+ * @group Annotations
+ */
+ public function testApi($api, $params)
+ {
+ $this->runApiTests($api, $params);
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testAddMultipleSitesFail()
+ {
+ try {
+ Piwik_Annotations_API::getInstance()->add("1,2,3", "2012-01-01", "whatever");
+ $this->fail("add should fail when given multiple sites in idSite");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testAddInvalidDateFail()
+ {
+ try {
+ Piwik_Annotations_API::getInstance()->add(self::$fixture->idSite1, "invaliddate", "whatever");
+ $this->fail("add should fail when given invalid date");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testSaveMultipleSitesFail()
+ {
+ try {
+ Piwik_Annotations_API::getInstance()->save("1,2,3", 0);
+ $this->fail("save should fail when given multiple sites");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testSaveInvalidDateFail()
+ {
+ try {
+ Piwik_Annotations_API::getInstance()->save(self::$fixture->idSite1, 0, "invaliddate");
+ $this->fail("save should fail when given an invalid date");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testSaveInvalidNoteIdFail()
+ {
+ try {
+ Piwik_Annotations_API::getInstance()->save(self::$fixture->idSite1, -1);
+ $this->fail("save should fail when given an invalid note id");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testDeleteMultipleSitesFail()
+ {
+ try {
+ Piwik_Annotations_API::getInstance()->delete("1,2,3", 0);
+ $this->fail("delete should fail when given multiple site IDs");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testDeleteInvalidNoteIdFail()
+ {
+ try {
+ Piwik_Annotations_API::getInstance()->delete(self::$fixture->idSite1, -1);
+ $this->fail("delete should fail when given an invalid site ID");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testGetMultipleSitesFail()
+ {
+ try {
+ Piwik_Annotations_API::getInstance()->get("1,2,3", 0);
+ $this->fail("get should fail when given multiple site IDs");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testGetInvalidNoteIdFail()
+ {
+ try {
+ Piwik_Annotations_API::getInstance()->get(self::$fixture->idSite1, -1);
+ $this->fail("get should fail when given an invalid note ID");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testSaveSuccess()
+ {
+ Piwik_Annotations_API::getInstance()->save(
+ self::$fixture->idSite1, 0, $date = '2011-04-01', $note = 'new note text', $starred = 1);
+
+ $expectedAnnotation = array(
+ 'date' => '2011-04-01',
+ 'note' => 'new note text',
+ 'starred' => 1,
+ 'user' => 'superUserLogin',
+ 'idNote' => 0,
+ 'canEditOrDelete' => true
+ );
+ $this->assertEquals($expectedAnnotation, Piwik_Annotations_API::getInstance()->get(self::$fixture->idSite1, 0));
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testSaveNoChangesSuccess()
+ {
+ Piwik_Annotations_API::getInstance()->save(self::$fixture->idSite1, 1);
+
+ $expectedAnnotation = array(
+ 'date' => '2011-12-02',
+ 'note' => '1: Site 1 annotation for 2011-12-02',
+ 'starred' => 0,
+ 'user' => 'superUserLogin',
+ 'idNote' => 1,
+ 'canEditOrDelete' => true
+ );
+ $this->assertEquals($expectedAnnotation, Piwik_Annotations_API::getInstance()->get(self::$fixture->idSite1, 1));
+ }
+
+ /**
+ * @group Integration
+ * @group Annotations
+ */
+ public function testDeleteSuccess()
+ {
+ Piwik_Annotations_API::getInstance()->delete(self::$fixture->idSite1, 1);
+
+ try {
+ Piwik_Annotations_API::getInstance()->get(self::$fixture->idSite1, 1);
+ $this->fail("failed to delete annotation");
+ } catch (Exception $ex) {
+ // pass
+ }
+ }
+
+ public function getPermissionsFailData()
+ {
+ return array(
+ // getAll
+ array(false, false, "module=API&method=Annotations.getAll&idSite=1&date=2012-01-01&period=year", true, "getAll should throw if user does not have view access"),
+
+ // get
+ array(false, false, "module=API&method=Annotations.get&idSite=1&idNote=0", true, "get should throw if user does not have view access"),
+
+ // getAnnotationCountForDates
+ array(false, false, "module=API&method=Annotations.getAnnotationCountForDates&idSite=1&date=2012-01-01&period=year", true, "getAnnotationCountForDates should throw if user does not have view access"),
+
+ // add
+ array(false, false, "module=API&method=Annotations.add&idSite=1&date=2011-02-01&note=whatever", true, "add should throw if user does not have view access"),
+ array(false, true, "module=API&method=Annotations.add&idSite=1&date=2011-02-01&note=whatever2", false, "add should not throw if user has view access"),
+ array(true, true, "module=API&method=Annotations.add&idSite=1&date=2011-02-01&note=whatever3", false, "add should not throw if user has admin access"),
+
+ // save
+ array(false, false, "module=API&method=Annotations.save&idSite=1&idNote=0&date=2011-03-01&note=newnote", true, "save should throw if user does not have view access"),
+ array(false, true, "module=API&method=Annotations.save&idSite=1&idNote=0&date=2011-03-01&note=newnote", true, "save should throw if user has view access but did not edit note"),
+ array(true, true, "module=API&method=Annotations.save&idSite=1&idNote=0&date=2011-03-01&note=newnote", false, "save should not throw if user has admin access"),
+
+ // delete
+ array(false, false, "module=API&method=Annotations.delete&idSite=1&idNote=0", true, "delete should throw if user does not have view access"),
+ array(false, true, "module=API&method=Annotations.delete&idSite=1&idNote=0", true, "delete should throw if user does not have view access"),
+ array(true, true, "module=API&method=Annotations.delete&idSite=1&idNote=0", false, "delete should not throw if user has admin access"),
+ );
+ }
+
+ /**
+ * @dataProvider getPermissionsFailData
+ * @group Integration
+ * @group Annotations
+ */
+ public function testMethodPermissions($hasAdminAccess, $hasViewAccess, $request, $checkException, $failMessage)
+ {
+ // create fake access that denies user access
+ $access = new FakeAccess();
+ FakeAccess::$superUser = false;
+ FakeAccess::$idSitesAdmin = $hasAdminAccess ? array(self::$fixture->idSite1) : array();
+ FakeAccess::$idSitesView = $hasViewAccess ? array(self::$fixture->idSite1) : array();
+ Zend_Registry::set('access', $access);
+
+ if ($checkException) {
+ try {
+ $request = new Piwik_API_Request($request);
+ $request->process();
+ $this->fail($failMessage);
+ } catch (Exception $ex) {
+ // pass
+ }
+ } else {
+ $request = new Piwik_API_Request($request);
+ $request->process();
+ }
+ }
}
AnnotationsTest::$fixture = new Test_Piwik_Fixture_TwoSitesWithAnnotations();
diff --git a/tests/PHPUnit/Integration/ApiGetReportMetadataTest.php b/tests/PHPUnit/Integration/ApiGetReportMetadataTest.php
index 811e023bf3..cc0f41eede 100755
--- a/tests/PHPUnit/Integration/ApiGetReportMetadataTest.php
+++ b/tests/PHPUnit/Integration/ApiGetReportMetadataTest.php
@@ -7,13 +7,13 @@
*/
/**
- * This tests the output of the API plugin API
+ * This tests the output of the API plugin API
* It will return metadata about all API reports from all plugins
* as well as the data itself, pre-processed and ready to be displayed
*/
class Test_Piwik_Integration_ApiGetReportMetadata extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
public function setUp()
{
@@ -40,35 +40,35 @@ class Test_Piwik_Integration_ApiGetReportMetadata extends IntegrationTestCase
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $dateTime = self::$fixture->dateTime;
-
+ $idSite = self::$fixture->idSite;
+ $dateTime = self::$fixture->dateTime;
+
return array(
array('API', array('idSite' => $idSite, 'date' => $dateTime)),
-
- // test w/ hideMetricsDocs=true
- array('API.getMetadata', array('idSite' => $idSite, 'date' => $dateTime,
- 'apiModule' => 'Actions', 'apiAction' => 'get',
- 'testSuffix' => '_hideMetricsDoc',
- 'otherRequestParameters' => array('hideMetricsDoc' => 1)) ),
- array('API.getProcessedReport', array('idSite' => $idSite, 'date' => $dateTime,
- 'apiModule' => 'Actions', 'apiAction' => 'get',
- 'testSuffix' => '_hideMetricsDoc',
- 'otherRequestParameters' => array('hideMetricsDoc' => 1)) ),
+
+ // test w/ hideMetricsDocs=true
+ array('API.getMetadata', array('idSite' => $idSite, 'date' => $dateTime,
+ 'apiModule' => 'Actions', 'apiAction' => 'get',
+ 'testSuffix' => '_hideMetricsDoc',
+ 'otherRequestParameters' => array('hideMetricsDoc' => 1))),
+ array('API.getProcessedReport', array('idSite' => $idSite, 'date' => $dateTime,
+ 'apiModule' => 'Actions', 'apiAction' => 'get',
+ 'testSuffix' => '_hideMetricsDoc',
+ 'otherRequestParameters' => array('hideMetricsDoc' => 1))),
// Test w/ showRawMetrics=true
- array('API.getProcessedReport', array('idSite' => $idSite, 'date' => $dateTime,
- 'apiModule' => 'UserCountry', 'apiAction' => 'getCountry',
- 'testSuffix' => '_showRawMetrics',
- 'otherRequestParameters' => array('showRawMetrics' => 1)) ),
+ array('API.getProcessedReport', array('idSite' => $idSite, 'date' => $dateTime,
+ 'apiModule' => 'UserCountry', 'apiAction' => 'getCountry',
+ 'testSuffix' => '_showRawMetrics',
+ 'otherRequestParameters' => array('showRawMetrics' => 1))),
// Test w/ showRawMetrics=true
- array('Actions.getPageTitles', array('idSite' => $idSite, 'date' => $dateTime,
- 'testSuffix' => '_pageTitleZeroString') ),
+ array('Actions.getPageTitles', array('idSite' => $idSite, 'date' => $dateTime,
+ 'testSuffix' => '_pageTitleZeroString')),
// test php renderer w/ array data
- array('API.getDefaultMetricTranslations', array('idSite' => $idSite, 'date' => $dateTime,
- 'format' => 'php', 'testSuffix' => '_phpRenderer')),
+ array('API.getDefaultMetricTranslations', array('idSite' => $idSite, 'date' => $dateTime,
+ 'format' => 'php', 'testSuffix' => '_phpRenderer')),
);
}
diff --git a/tests/PHPUnit/Integration/ApiGetReportMetadata_yearTest.php b/tests/PHPUnit/Integration/ApiGetReportMetadata_yearTest.php
index 1921112be2..ff2c413ae4 100755
--- a/tests/PHPUnit/Integration/ApiGetReportMetadata_yearTest.php
+++ b/tests/PHPUnit/Integration/ApiGetReportMetadata_yearTest.php
@@ -7,12 +7,12 @@
*/
/**
- * test the Yearly metadata API response,
- * with no visits, with custom response language
+ * test the Yearly metadata API response,
+ * with no visits, with custom response language
*/
class Test_Piwik_Integration_ApiGetReportMetadata_Year extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
public function getApiForTesting()
{
@@ -24,7 +24,7 @@ class Test_Piwik_Integration_ApiGetReportMetadata_Year extends IntegrationTestCa
array('API.getProcessedReport', $params),
// @todo reenable me
//array('API.getReportMetadata', $params),
- //array('LanguagesManager.getTranslationsForLanguage', $params),
+ //array('LanguagesManager.getTranslationsForLanguage', $params),
array('LanguagesManager.getAvailableLanguageNames', $params),
array('SitesManager.getJavascriptTag', $params)
);
diff --git a/tests/PHPUnit/Integration/BlobReportLimitingTest.php b/tests/PHPUnit/Integration/BlobReportLimitingTest.php
index 2db36b32d4..918b177358 100755
--- a/tests/PHPUnit/Integration/BlobReportLimitingTest.php
+++ b/tests/PHPUnit/Integration/BlobReportLimitingTest.php
@@ -14,86 +14,85 @@ require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/MockLocationProvider.php';
*/
class Test_Piwik_Integration_BlobReportLimitingTest extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
-
- public static function setUpBeforeClass()
- {
- self::setUpConfigOptions();
- parent::_setUpBeforeClass($dbName = false, $createEmptyDatabase = true, $createConfig = false);
- parent::setUpFixture(self::$fixture);
- }
+ public static $fixture = null; // initialized below class definition
- public function getApiForTesting()
- {
- // TODO: test Provider plugin? Not sure if it's possible.
- $apiToCall = array(
- 'Actions.getPageUrls', 'Actions.getPageTitles', 'Actions.getDownloads', 'Actions.getOutlinks',
- 'CustomVariables.getCustomVariables',
- 'Referers.getRefererType', 'Referers.getKeywords', 'Referers.getSearchEngines',
- 'Referers.getWebsites', 'Referers.getAll', /* TODO 'Referers.getCampaigns', */
- 'UserSettings.getResolution', 'UserSettings.getConfiguration', 'UserSettings.getOS',
- 'UserSettings.getBrowserVersion',
- 'UserCountry.getRegion', 'UserCountry.getCity',
- );
-
- return array(
- array($apiToCall, array('idSite' => self::$fixture->idSite,
- 'date' => self::$fixture->dateTime,
- 'periods' => array('day')))
- );
- }
-
- /**
- * @dataProvider getApiForTesting
- * @group Integration
- * @group BlobReportLimiting
- */
- public function testApi($api, $params)
- {
- $this->runApiTests($api, $params);
- }
-
- /**
- * @group Integration
- * @group BlobReportLimiting
- */
- public function testApiWithRankingQuery()
- {
- // custom setup
- self::deleteArchiveTables();
- $generalConfig['datatable_archiving_maximum_rows_referers'] = 4;
- $generalConfig['datatable_archiving_maximum_rows_subtable_referers'] = 4;
- $generalConfig['datatable_archiving_maximum_rows_actions'] = 4;
- $generalConfig['datatable_archiving_maximum_rows_subtable_actions'] = 4;
- $generalConfig['datatable_archiving_maximum_rows_standard'] = 4;
- Piwik_Config::getInstance()->General['archiving_ranking_query_row_limit'] = 3;
- Piwik_Actions_ArchivingHelper::reloadConfig();
-
- foreach ($this->getApiForTesting() as $pair)
- {
- list($apiToCall, $params) = $pair;
- $params['testSuffix'] = '_rankingQuery';
-
- $this->runApiTests($apiToCall, $params);
- }
- }
+ public static function setUpBeforeClass()
+ {
+ self::setUpConfigOptions();
+ parent::_setUpBeforeClass($dbName = false, $createEmptyDatabase = true, $createConfig = false);
+ parent::setUpFixture(self::$fixture);
+ }
- public function getOutputPrefix()
- {
- return 'reportLimiting';
- }
-
- protected static function setUpConfigOptions()
- {
- self::createTestConfig();
- $generalConfig =& Piwik_Config::getInstance()->General;
- $generalConfig['datatable_archiving_maximum_rows_referers'] = 3;
- $generalConfig['datatable_archiving_maximum_rows_subtable_referers'] = 2;
- $generalConfig['datatable_archiving_maximum_rows_actions'] = 3;
- $generalConfig['datatable_archiving_maximum_rows_subtable_actions'] = 2;
- $generalConfig['datatable_archiving_maximum_rows_standard'] = 3;
- $generalConfig['archiving_ranking_query_row_limit'] = 50000;
- }
+ public function getApiForTesting()
+ {
+ // TODO: test Provider plugin? Not sure if it's possible.
+ $apiToCall = array(
+ 'Actions.getPageUrls', 'Actions.getPageTitles', 'Actions.getDownloads', 'Actions.getOutlinks',
+ 'CustomVariables.getCustomVariables',
+ 'Referers.getRefererType', 'Referers.getKeywords', 'Referers.getSearchEngines',
+ 'Referers.getWebsites', 'Referers.getAll', /* TODO 'Referers.getCampaigns', */
+ 'UserSettings.getResolution', 'UserSettings.getConfiguration', 'UserSettings.getOS',
+ 'UserSettings.getBrowserVersion',
+ 'UserCountry.getRegion', 'UserCountry.getCity',
+ );
+
+ return array(
+ array($apiToCall, array('idSite' => self::$fixture->idSite,
+ 'date' => self::$fixture->dateTime,
+ 'periods' => array('day')))
+ );
+ }
+
+ /**
+ * @dataProvider getApiForTesting
+ * @group Integration
+ * @group BlobReportLimiting
+ */
+ public function testApi($api, $params)
+ {
+ $this->runApiTests($api, $params);
+ }
+
+ /**
+ * @group Integration
+ * @group BlobReportLimiting
+ */
+ public function testApiWithRankingQuery()
+ {
+ // custom setup
+ self::deleteArchiveTables();
+ $generalConfig['datatable_archiving_maximum_rows_referers'] = 4;
+ $generalConfig['datatable_archiving_maximum_rows_subtable_referers'] = 4;
+ $generalConfig['datatable_archiving_maximum_rows_actions'] = 4;
+ $generalConfig['datatable_archiving_maximum_rows_subtable_actions'] = 4;
+ $generalConfig['datatable_archiving_maximum_rows_standard'] = 4;
+ Piwik_Config::getInstance()->General['archiving_ranking_query_row_limit'] = 3;
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+
+ foreach ($this->getApiForTesting() as $pair) {
+ list($apiToCall, $params) = $pair;
+ $params['testSuffix'] = '_rankingQuery';
+
+ $this->runApiTests($apiToCall, $params);
+ }
+ }
+
+ public function getOutputPrefix()
+ {
+ return 'reportLimiting';
+ }
+
+ protected static function setUpConfigOptions()
+ {
+ self::createTestConfig();
+ $generalConfig =& Piwik_Config::getInstance()->General;
+ $generalConfig['datatable_archiving_maximum_rows_referers'] = 3;
+ $generalConfig['datatable_archiving_maximum_rows_subtable_referers'] = 2;
+ $generalConfig['datatable_archiving_maximum_rows_actions'] = 3;
+ $generalConfig['datatable_archiving_maximum_rows_subtable_actions'] = 2;
+ $generalConfig['datatable_archiving_maximum_rows_standard'] = 3;
+ $generalConfig['archiving_ranking_query_row_limit'] = 50000;
+ }
}
Test_Piwik_Integration_BlobReportLimitingTest::$fixture = new Test_Piwik_Fixture_ManyVisitsWithMockLocationProvider();
diff --git a/tests/PHPUnit/Integration/CsvExportTest.php b/tests/PHPUnit/Integration/CsvExportTest.php
index 44cb1c5e41..737530cced 100755
--- a/tests/PHPUnit/Integration/CsvExportTest.php
+++ b/tests/PHPUnit/Integration/CsvExportTest.php
@@ -11,14 +11,14 @@
*/
class Test_Piwik_Integration_CsvExport extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $dateTime = self::$fixture->dateTime;
-
- $apiToCall = array('VisitsSummary.get', 'CustomVariables.getCustomVariables');
+ $idSite = self::$fixture->idSite;
+ $dateTime = self::$fixture->dateTime;
+
+ $apiToCall = array('VisitsSummary.get', 'CustomVariables.getCustomVariables');
$enExtraParam = array('expanded' => 0, 'flat' => 1, 'include_aggregate_rows' => 0, 'translateColumnNames' => 1);
diff --git a/tests/PHPUnit/Integration/EcommerceOrderWithItemsTest.php b/tests/PHPUnit/Integration/EcommerceOrderWithItemsTest.php
index 0bb23213c7..629f9706a3 100755
--- a/tests/PHPUnit/Integration/EcommerceOrderWithItemsTest.php
+++ b/tests/PHPUnit/Integration/EcommerceOrderWithItemsTest.php
@@ -11,16 +11,16 @@
*/
class Test_Piwik_Integration_EcommerceOrderWithItems extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
- /**
- * @group Integration
- * @group EcommerceOrderWithItems
- */
- public function testImagesIncludedInTests()
- {
- $this->alertWhenImagesExcludedFromTests();
- }
+ /**
+ * @group Integration
+ * @group EcommerceOrderWithItems
+ */
+ public function testImagesIncludedInTests()
+ {
+ $this->alertWhenImagesExcludedFromTests();
+ }
/**
* @dataProvider getApiForTesting
@@ -34,10 +34,10 @@ class Test_Piwik_Integration_EcommerceOrderWithItems extends IntegrationTestCase
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $idSite2 = self::$fixture->idSite2;
- $dateTime = self::$fixture->dateTime;
-
+ $idSite = self::$fixture->idSite;
+ $idSite2 = self::$fixture->idSite2;
+ $dateTime = self::$fixture->dateTime;
+
$dayApi = array('VisitsSummary.get', 'VisitTime', 'CustomVariables.getCustomVariables',
'Live.getLastVisitsDetails', 'UserCountry', 'API.getProcessedReport', 'Goals.get',
'Goals.getConversions', 'Goals.getItemsSku', 'Goals.getItemsName', 'Goals.getItemsCategory');
@@ -51,125 +51,125 @@ class Test_Piwik_Integration_EcommerceOrderWithItems extends IntegrationTestCase
// Normal standard goal
return array_merge(array(
- // day tests
- array($dayApi, array('idSite' => $idSite, 'date' => $dateTime, 'periods' => array('day'), 'otherRequestParameters' => array('_leavePiwikCoreVariables' => 1))),
-
- // goals API week tests
- array($goalWeekApi, array('idSite' => $idSite, 'date' => $dateTime, 'periods' => array('week'))),
-
- // abandoned carts tests
- array($goalItemApi, array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day', 'week'), 'abandonedCarts' => 1,
- 'testSuffix' => '_AbandonedCarts')),
-
- // multiple periods tests
- array($goalItemApi, array('idSite' => $idSite, 'date' => $dateTime, 'periods' => array('day'),
- 'setDateLastN' => true, 'testSuffix' => 'multipleDates')),
-
- // multiple periods & multiple websites tests
- array($goalItemApi, array('idSite' => sprintf("%u,%u", $idSite, $idSite2), 'date' => $dateTime,
- 'periods' => array('day'), 'setDateLastN' => true,
- 'testSuffix' => 'multipleDates_andMultipleWebsites')),
-
- // test metadata products
- array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'apiModule' => 'Goals',
- 'apiAction' => 'getItemsSku', 'testSuffix' => '_Metadata_ItemsSku')),
- array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'apiModule' => 'Goals',
- 'apiAction' => 'getItemsCategory', 'testSuffix' => '_Metadata_ItemsCategory')),
-
- // test metadata Goals.get for Ecommerce orders & Carts
- array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'apiModule' => 'Goals', 'apiAction' => 'get',
- 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER,
- 'testSuffix' => '_Metadata_Goals.Get_Order')),
- array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'apiModule' => 'Goals', 'apiAction' => 'get',
- 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART,
- 'testSuffix' => '_Metadata_Goals.Get_AbandonedCart')),
-
- // normal standard goal test
- array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'apiModule' => 'Goals', 'apiAction' => 'get',
- 'idGoal' => self::$fixture->idGoalStandard,
- 'testSuffix' => '_Metadata_Goals.Get_NormalGoal')),
-
- // non-existant goal test
- array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'apiModule' => 'Goals', 'apiAction' => 'get',
- 'idGoal' => 'FAKE IDGOAL',
- 'testSuffix' => '_Metadata_Goals.Get_NotExistingGoal')),
-
- // While we're at it, test for a standard Metadata report with zero entries
- array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'apiModule' => 'VisitTime',
- 'apiAction' => 'getVisitInformationPerServerTime',
- 'testSuffix' => '_Metadata_VisitTime.getVisitInformationPerServerTime')),
-
- // Standard non metadata Goals.get
- // test Goals.get with idGoal=ecommerceOrder and ecommerceAbandonedCart
- array('Goals.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day', 'week'), 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART,
- 'testSuffix' => '_GoalAbandonedCart')),
- array('Goals.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day', 'week'), 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER,
- 'testSuffix' => '_GoalOrder')),
- array('Goals.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day', 'week'), 'idGoal' => 1, 'testSuffix' => '_GoalMatchTitle')),
- array('Goals.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day', 'week'), 'idGoal' => '', 'testSuffix' => '_GoalOverall')),
-
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'segment' => 'visitEcommerceStatus==none',
- 'testSuffix' => '_SegmentNoEcommerce')),
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'testSuffix' => '_SegmentOrderedSomething',
- 'segment' => 'visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart')),
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'testSuffix' => '_SegmentAbandonedCart',
- 'segment' => 'visitEcommerceStatus==abandonedCart,visitEcommerceStatus==orderedThenAbandonedCart')),
-
- // test segment visitConvertedGoalId
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day', 'week'), 'testSuffix' => '_SegmentConvertedGoalId1',
- 'segment' => "visitConvertedGoalId==".self::$fixture->idGoalStandard)),
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'testSuffix' => '_SegmentDidNotConvertGoalId1',
- 'segment' => "visitConvertedGoalId!=".self::$fixture->idGoalStandard)),
-
- // test segment visitorType
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('week'), 'segment' => 'visitorType==new',
- 'testSuffix' => '_SegmentNewVisitors')),
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('week'), 'segment' => 'visitorType==returning',
- 'testSuffix' => '_SegmentReturningVisitors')),
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('week'), 'segment' => 'visitorType==returningCustomer',
- 'testSuffix' => '_SegmentReturningCustomers')),
-
- // test segment pageTitle
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => array('day'), 'segment' => 'pageTitle==incredible title!',
- 'testSuffix' => '_SegmentPageTitleMatch')),
-
- // test Live! output is OK also for the visit that just bought something (other visits leave an abandoned cart)
- array('Live.getLastVisitsDetails', array('idSite' => $idSite,
- 'date' => Piwik_Date::factory($dateTime)->addHour(30.65)->getDatetime(),
- 'periods' => array('day'), 'testSuffix' => '_LiveEcommerceStatusOrdered')),
-
- // test API.get method
- array('API.get', array('idSite' => $idSite, 'date' => $dateTime, 'periods' => array('day', 'week'),
- 'otherRequestParameters' => array(
- 'columns' => 'nb_pageviews,nb_visits,avg_time_on_site,nb_visits_converted'),
- 'testSuffix' => '_API_get')),
-
- // Website2
- array($goalWeekApi, array('idSite' => $idSite2, 'date' => $dateTime, 'periods' => array('week'),
- 'testSuffix' => '_Website2')),
-
- ), self::getApiForTestingScheduledReports($dateTime, 'week'));
+ // day tests
+ array($dayApi, array('idSite' => $idSite, 'date' => $dateTime, 'periods' => array('day'), 'otherRequestParameters' => array('_leavePiwikCoreVariables' => 1))),
+
+ // goals API week tests
+ array($goalWeekApi, array('idSite' => $idSite, 'date' => $dateTime, 'periods' => array('week'))),
+
+ // abandoned carts tests
+ array($goalItemApi, array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day', 'week'), 'abandonedCarts' => 1,
+ 'testSuffix' => '_AbandonedCarts')),
+
+ // multiple periods tests
+ array($goalItemApi, array('idSite' => $idSite, 'date' => $dateTime, 'periods' => array('day'),
+ 'setDateLastN' => true, 'testSuffix' => 'multipleDates')),
+
+ // multiple periods & multiple websites tests
+ array($goalItemApi, array('idSite' => sprintf("%u,%u", $idSite, $idSite2), 'date' => $dateTime,
+ 'periods' => array('day'), 'setDateLastN' => true,
+ 'testSuffix' => 'multipleDates_andMultipleWebsites')),
+
+ // test metadata products
+ array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'apiModule' => 'Goals',
+ 'apiAction' => 'getItemsSku', 'testSuffix' => '_Metadata_ItemsSku')),
+ array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'apiModule' => 'Goals',
+ 'apiAction' => 'getItemsCategory', 'testSuffix' => '_Metadata_ItemsCategory')),
+
+ // test metadata Goals.get for Ecommerce orders & Carts
+ array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'apiModule' => 'Goals', 'apiAction' => 'get',
+ 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER,
+ 'testSuffix' => '_Metadata_Goals.Get_Order')),
+ array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'apiModule' => 'Goals', 'apiAction' => 'get',
+ 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART,
+ 'testSuffix' => '_Metadata_Goals.Get_AbandonedCart')),
+
+ // normal standard goal test
+ array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'apiModule' => 'Goals', 'apiAction' => 'get',
+ 'idGoal' => self::$fixture->idGoalStandard,
+ 'testSuffix' => '_Metadata_Goals.Get_NormalGoal')),
+
+ // non-existant goal test
+ array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'apiModule' => 'Goals', 'apiAction' => 'get',
+ 'idGoal' => 'FAKE IDGOAL',
+ 'testSuffix' => '_Metadata_Goals.Get_NotExistingGoal')),
+
+ // While we're at it, test for a standard Metadata report with zero entries
+ array($processedReportApi, array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'apiModule' => 'VisitTime',
+ 'apiAction' => 'getVisitInformationPerServerTime',
+ 'testSuffix' => '_Metadata_VisitTime.getVisitInformationPerServerTime')),
+
+ // Standard non metadata Goals.get
+ // test Goals.get with idGoal=ecommerceOrder and ecommerceAbandonedCart
+ array('Goals.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day', 'week'), 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART,
+ 'testSuffix' => '_GoalAbandonedCart')),
+ array('Goals.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day', 'week'), 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER,
+ 'testSuffix' => '_GoalOrder')),
+ array('Goals.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day', 'week'), 'idGoal' => 1, 'testSuffix' => '_GoalMatchTitle')),
+ array('Goals.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day', 'week'), 'idGoal' => '', 'testSuffix' => '_GoalOverall')),
+
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'segment' => 'visitEcommerceStatus==none',
+ 'testSuffix' => '_SegmentNoEcommerce')),
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'testSuffix' => '_SegmentOrderedSomething',
+ 'segment' => 'visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart')),
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'testSuffix' => '_SegmentAbandonedCart',
+ 'segment' => 'visitEcommerceStatus==abandonedCart,visitEcommerceStatus==orderedThenAbandonedCart')),
+
+ // test segment visitConvertedGoalId
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day', 'week'), 'testSuffix' => '_SegmentConvertedGoalId1',
+ 'segment' => "visitConvertedGoalId==" . self::$fixture->idGoalStandard)),
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'testSuffix' => '_SegmentDidNotConvertGoalId1',
+ 'segment' => "visitConvertedGoalId!=" . self::$fixture->idGoalStandard)),
+
+ // test segment visitorType
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('week'), 'segment' => 'visitorType==new',
+ 'testSuffix' => '_SegmentNewVisitors')),
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('week'), 'segment' => 'visitorType==returning',
+ 'testSuffix' => '_SegmentReturningVisitors')),
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('week'), 'segment' => 'visitorType==returningCustomer',
+ 'testSuffix' => '_SegmentReturningCustomers')),
+
+ // test segment pageTitle
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => array('day'), 'segment' => 'pageTitle==incredible title!',
+ 'testSuffix' => '_SegmentPageTitleMatch')),
+
+ // test Live! output is OK also for the visit that just bought something (other visits leave an abandoned cart)
+ array('Live.getLastVisitsDetails', array('idSite' => $idSite,
+ 'date' => Piwik_Date::factory($dateTime)->addHour(30.65)->getDatetime(),
+ 'periods' => array('day'), 'testSuffix' => '_LiveEcommerceStatusOrdered')),
+
+ // test API.get method
+ array('API.get', array('idSite' => $idSite, 'date' => $dateTime, 'periods' => array('day', 'week'),
+ 'otherRequestParameters' => array(
+ 'columns' => 'nb_pageviews,nb_visits,avg_time_on_site,nb_visits_converted'),
+ 'testSuffix' => '_API_get')),
+
+ // Website2
+ array($goalWeekApi, array('idSite' => $idSite2, 'date' => $dateTime, 'periods' => array('week'),
+ 'testSuffix' => '_Website2')),
+
+ ), self::getApiForTestingScheduledReports($dateTime, 'week'));
}
public function getOutputPrefix()
diff --git a/tests/PHPUnit/Integration/FlattenReportsTest.php b/tests/PHPUnit/Integration/FlattenReportsTest.php
index 16d4580267..48ecdad576 100644
--- a/tests/PHPUnit/Integration/FlattenReportsTest.php
+++ b/tests/PHPUnit/Integration/FlattenReportsTest.php
@@ -11,7 +11,7 @@
*/
class Test_Piwik_Integration_FlattenReports extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -25,9 +25,9 @@ class Test_Piwik_Integration_FlattenReports extends IntegrationTestCase
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $dateTime = self::$fixture->dateTime;
-
+ $idSite = self::$fixture->idSite;
+ $dateTime = self::$fixture->dateTime;
+
$return = array();
// referrers
@@ -78,29 +78,29 @@ class Test_Piwik_Integration_FlattenReports extends IntegrationTestCase
)
));
- // test expanded=1 w/ idSubtable=X
- $return[] = array('Actions.getPageUrls', array('idSite' => $idSite,
- 'date' => $dateTime,
- 'periods' => array('week'),
- 'apiModule' => 'Actions',
- 'apiAction' => 'getPageUrls',
- 'supertableApi' => 'Actions.getPageUrls',
- 'testSuffix' => '_expandedSubtable',
- 'otherRequestParameters' => array('expanded' => '1')));
-
- // test flat=1 w/ filter_pattern_recursive
- $return[] = array('Actions.getPageUrls', array('idSite' => $idSite,
- 'date' => $dateTime,
- 'periods' => array('week'),
- 'apiModule' => 'Actions',
- 'apiAction' => 'getPageUrls',
- 'testSuffix' => '_flatFilterPatternRecursive',
- 'otherRequestParameters' => array(
- 'flat' => '1',
- 'expanded' => '0',
- 'filter_pattern_recursive' => 'dir2/'
- )));
-
+ // test expanded=1 w/ idSubtable=X
+ $return[] = array('Actions.getPageUrls', array('idSite' => $idSite,
+ 'date' => $dateTime,
+ 'periods' => array('week'),
+ 'apiModule' => 'Actions',
+ 'apiAction' => 'getPageUrls',
+ 'supertableApi' => 'Actions.getPageUrls',
+ 'testSuffix' => '_expandedSubtable',
+ 'otherRequestParameters' => array('expanded' => '1')));
+
+ // test flat=1 w/ filter_pattern_recursive
+ $return[] = array('Actions.getPageUrls', array('idSite' => $idSite,
+ 'date' => $dateTime,
+ 'periods' => array('week'),
+ 'apiModule' => 'Actions',
+ 'apiAction' => 'getPageUrls',
+ 'testSuffix' => '_flatFilterPatternRecursive',
+ 'otherRequestParameters' => array(
+ 'flat' => '1',
+ 'expanded' => '0',
+ 'filter_pattern_recursive' => 'dir2/'
+ )));
+
return $return;
}
@@ -111,5 +111,5 @@ class Test_Piwik_Integration_FlattenReports extends IntegrationTestCase
}
Test_Piwik_Integration_FlattenReports::$fixture =
- new Test_Piwik_Fixture_ManyVisitsWithSubDirReferrersAndCustomVars();
+ new Test_Piwik_Fixture_ManyVisitsWithSubDirReferrersAndCustomVars();
diff --git a/tests/PHPUnit/Integration/ImportLogsTest.php b/tests/PHPUnit/Integration/ImportLogsTest.php
index 9801ba4962..1e1027cb54 100755
--- a/tests/PHPUnit/Integration/ImportLogsTest.php
+++ b/tests/PHPUnit/Integration/ImportLogsTest.php
@@ -11,7 +11,7 @@
*/
class Test_Piwik_Integration_ImportLogs extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -25,49 +25,49 @@ class Test_Piwik_Integration_ImportLogs extends IntegrationTestCase
public function getApiForTesting()
{
- return array(
- array('all', array('idSite' => self::$fixture->idSite,
- 'date' => '2012-08-09',
- 'periods' => 'month')),
+ return array(
+ array('all', array('idSite' => self::$fixture->idSite,
+ 'date' => '2012-08-09',
+ 'periods' => 'month')),
- // report generated from custom log format including generation time
- array('Actions.getPageUrls', array('idSite' => self::$fixture->idSite,
- 'date' => '2012-09-30',
- 'periods' => 'day')),
-
- array('VisitsSummary.get', array('idSite' => self::$fixture->idSite2,
- 'date' => '2012-08-09',
- 'periods' => 'month',
- 'testSuffix' => '_siteIdTwo_TrackedUsingLogReplay')),
- );
+ // report generated from custom log format including generation time
+ array('Actions.getPageUrls', array('idSite' => self::$fixture->idSite,
+ 'date' => '2012-09-30',
+ 'periods' => 'day')),
+
+ array('VisitsSummary.get', array('idSite' => self::$fixture->idSite2,
+ 'date' => '2012-08-09',
+ 'periods' => 'month',
+ 'testSuffix' => '_siteIdTwo_TrackedUsingLogReplay')),
+ );
}
-
+
/**
* @group Integration
* @group ImportLogs
*/
public function testDynamicResolverSitesCreated()
{
- self::$fixture->logVisitsWithDynamicResolver();
-
- // reload access so new sites are viewable
- Zend_Registry::get('access')->setSuperUser(true);
-
- // make sure sites aren't created twice
- $piwikDotNet = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://piwik.net');
- $this->assertEquals(1, count($piwikDotNet));
-
- $anothersiteDotCom = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://anothersite.com');
- $this->assertEquals(1, count($anothersiteDotCom));
-
- $whateverDotCom = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://whatever.com');
- $this->assertEquals(1, count($whateverDotCom));
- }
+ self::$fixture->logVisitsWithDynamicResolver();
+
+ // reload access so new sites are viewable
+ Zend_Registry::get('access')->setSuperUser(true);
+
+ // make sure sites aren't created twice
+ $piwikDotNet = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://piwik.net');
+ $this->assertEquals(1, count($piwikDotNet));
+
+ $anothersiteDotCom = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://anothersite.com');
+ $this->assertEquals(1, count($anothersiteDotCom));
- public function getOutputPrefix()
- {
- return 'ImportLogs';
- }
+ $whateverDotCom = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://whatever.com');
+ $this->assertEquals(1, count($whateverDotCom));
+ }
+
+ public function getOutputPrefix()
+ {
+ return 'ImportLogs';
+ }
}
Test_Piwik_Integration_ImportLogs::$fixture = new Test_Piwik_Fixture_ManySitesImportedLogs();
diff --git a/tests/PHPUnit/Integration/LabelFilterTest.php b/tests/PHPUnit/Integration/LabelFilterTest.php
index 44126a963f..0141761bed 100644
--- a/tests/PHPUnit/Integration/LabelFilterTest.php
+++ b/tests/PHPUnit/Integration/LabelFilterTest.php
@@ -12,7 +12,7 @@
*/
class Test_Piwik_Integration_LabelFilter extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -26,19 +26,19 @@ class Test_Piwik_Integration_LabelFilter extends IntegrationTestCase
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $dateTime = self::$fixture->dateTime;
-
+ $idSite = self::$fixture->idSite;
+ $dateTime = self::$fixture->dateTime;
+
$labelsToTest = array(
- // first level
- 'shouldBeNoData' => 'nonExistent',
- 'dir' => ' dir ',
- '0' => '/0',
+ // first level
+ 'shouldBeNoData' => 'nonExistent',
+ 'dir' => ' dir ',
+ '0' => '/0',
- // TODO the label in the API output is ...&amp;#039;... why does it only work this way?
- 'thisiscool' => '/ééé&quot;&#039;... &lt;this is cool&gt;!',
+ // TODO the label in the API output is ...&amp;#039;... why does it only work this way?
+ 'thisiscool' => '/ééé&quot;&#039;... &lt;this is cool&gt;!',
- // second level
+ // second level
'dirnonExistent' => 'dir>nonExistent',
'dirfilephpfoobarfoo2bar' => 'dir>' . urlencode('/file.php?foo=bar&foo2=bar'),
@@ -59,57 +59,57 @@ class Test_Piwik_Integration_LabelFilter extends IntegrationTestCase
));
}
- $label = 'dir';
- $return[] = array('Actions.getPageUrls', array(
- 'testSuffix' => '_' . $label . '_range',
- 'idSite' => $idSite,
- 'date' => $dateTime,
- 'otherRequestParameters' => array(
- 'date' => '2010-03-06,2010-03-08',
- 'label' => urlencode($label),
- 'expanded' => 0
- )
- ));
-
- $return[] = array('Actions.getPageTitles', array(
- 'testSuffix' => '_titles',
- 'idSite' => $idSite,
- 'date' => $dateTime,
- 'otherRequestParameters' => array(
- // note: title has no blank prefixed here. in the report it has.
- 'label' => urlencode('incredible title! <>,;'),
- 'expanded' => 0
- )
- ));
-
- $return[] = array('Actions.getPageTitles', array(
- 'testSuffix' => '_titlesRecursive',
- 'idSite' => $idSite,
- 'date' => $dateTime,
- 'otherRequestParameters' => array(
- 'label' => urlencode(
- ' ' . // test trimming
- urlencode('incredible parent title! <>,;') .
- '>' .
- urlencode('subtitle <>,;')),
- 'expanded' => 0
- )
- ));
-
- $keyword = '&lt;&gt;&amp;\&quot;the pdo extension is required for this adapter but the extension is not loaded';
- $searchEngineTest = array(
- 'testSuffix' => '_keywords_html',
- 'idSite' => $idSite,
- 'date' => $dateTime,
- 'otherRequestParameters' => array(
- 'label' => urlencode('Google>' . urlencode($keyword)),
- 'expanded' => 0
- )
- );
- $return[] = array('Referers.getSearchEngines', $searchEngineTest);
-
- $searchEngineTest['otherRequestParameters']['label'] = urlencode('Google>' . urlencode(html_entity_decode($keyword)));
- $return[] = array('Referers.getSearchEngines', $searchEngineTest);
+ $label = 'dir';
+ $return[] = array('Actions.getPageUrls', array(
+ 'testSuffix' => '_' . $label . '_range',
+ 'idSite' => $idSite,
+ 'date' => $dateTime,
+ 'otherRequestParameters' => array(
+ 'date' => '2010-03-06,2010-03-08',
+ 'label' => urlencode($label),
+ 'expanded' => 0
+ )
+ ));
+
+ $return[] = array('Actions.getPageTitles', array(
+ 'testSuffix' => '_titles',
+ 'idSite' => $idSite,
+ 'date' => $dateTime,
+ 'otherRequestParameters' => array(
+ // note: title has no blank prefixed here. in the report it has.
+ 'label' => urlencode('incredible title! <>,;'),
+ 'expanded' => 0
+ )
+ ));
+
+ $return[] = array('Actions.getPageTitles', array(
+ 'testSuffix' => '_titlesRecursive',
+ 'idSite' => $idSite,
+ 'date' => $dateTime,
+ 'otherRequestParameters' => array(
+ 'label' => urlencode(
+ ' ' . // test trimming
+ urlencode('incredible parent title! <>,;') .
+ '>' .
+ urlencode('subtitle <>,;')),
+ 'expanded' => 0
+ )
+ ));
+
+ $keyword = '&lt;&gt;&amp;\&quot;the pdo extension is required for this adapter but the extension is not loaded';
+ $searchEngineTest = array(
+ 'testSuffix' => '_keywords_html',
+ 'idSite' => $idSite,
+ 'date' => $dateTime,
+ 'otherRequestParameters' => array(
+ 'label' => urlencode('Google>' . urlencode($keyword)),
+ 'expanded' => 0
+ )
+ );
+ $return[] = array('Referers.getSearchEngines', $searchEngineTest);
+
+ $searchEngineTest['otherRequestParameters']['label'] = urlencode('Google>' . urlencode(html_entity_decode($keyword)));
+ $return[] = array('Referers.getSearchEngines', $searchEngineTest);
return $return;
}
diff --git a/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php b/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php
index 0ab4ecab47..677c5c3ff7 100755
--- a/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php
+++ b/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php
@@ -2,7 +2,7 @@
/**
* Piwik - Open source web analytics
*
- * @link http://piwik.org
+ * @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
@@ -11,72 +11,72 @@ require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/MockLocationProvider.php';
/**
* Tests w/ 14 visitors w/ 2 visits each.
* Uses geoip location provider to test city/region reports.
- *
+ *
* TODO Test ServerBased GeoIP implementation somehow. (Use X-FORWARDED-FOR?)
* TODO Test PECL implementation somehow. (The PECL module must point to the test dir, not the real one.)
*/
class Test_Piwik_Integration_ManyVisitorsOneWebsiteTest extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
-
- /**
- * @dataProvider getApiForTesting
- * @group Integration
- * @group TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisabled
- */
- public function testApi($api, $params)
- {
- $this->runApiTests($api, $params);
- }
+ public static $fixture = null; // initialized below class definition
+
+ /**
+ * @dataProvider getApiForTesting
+ * @group Integration
+ * @group TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisabled
+ */
+ public function testApi($api, $params)
+ {
+ $this->runApiTests($api, $params);
+ }
+
+ public function getApiForTesting()
+ {
+ $idSite = self::$fixture->idSite;
+ $dateTime = self::$fixture->dateTime;
- public function getApiForTesting()
- {
- $idSite = self::$fixture->idSite;
- $dateTime = self::$fixture->dateTime;
-
- // Note: we must set 'UserCountry.getLocationFromIP' since it's "excluded" by default in setApiNotToCall
- $apiToCall = array('UserCountry');
+ // Note: we must set 'UserCountry.getLocationFromIP' since it's "excluded" by default in setApiNotToCall
+ $apiToCall = array('UserCountry');
- return array(
- array( $apiToCall,
- array( 'idSite' => $idSite,
- 'date' => $dateTime,
- 'periods' => array('month'))),
+ return array(
+ array($apiToCall,
+ array('idSite' => $idSite,
+ 'date' => $dateTime,
+ 'periods' => array('month'))),
- array($apiToCall, array('idSite' => $idSite,
- 'date' => $dateTime,
- 'periods' => array('month'),
- 'testSuffix' => '_segment_region',
- 'segment' => 'region==P3;country==gb')),
+ array($apiToCall, array('idSite' => $idSite,
+ 'date' => $dateTime,
+ 'periods' => array('month'),
+ 'testSuffix' => '_segment_region',
+ 'segment' => 'region==P3;country==gb')),
- array($apiToCall, array('idSite' => $idSite,
- 'date' => $dateTime,
- 'periods' => array('month'),
- 'testSuffix' => '_segment_city',
- 'segment' => 'city==Stratford-upon-Avon;region==P3;country==gb')),
+ array($apiToCall, array('idSite' => $idSite,
+ 'date' => $dateTime,
+ 'periods' => array('month'),
+ 'testSuffix' => '_segment_city',
+ 'segment' => 'city==Stratford-upon-Avon;region==P3;country==gb')),
- array($apiToCall, array('idSite' => $idSite,
- 'date' => $dateTime,
- 'periods' => array('month'),
- 'testSuffix' => '_segment_lat_long',
- 'segment' => 'lat>45;lat<49.3;long>-125;long<-122')),
+ array($apiToCall, array('idSite' => $idSite,
+ 'date' => $dateTime,
+ 'periods' => array('month'),
+ 'testSuffix' => '_segment_lat_long',
+ 'segment' => 'lat>45;lat<49.3;long>-125;long<-122')),
- array('UserCountry.getCountry', array('idSite' => $idSite,
- 'date' => $dateTime,
- 'periods' => array('month'),
- 'testSuffix' => '_segment_continent',
- 'segment' => 'continent==eur')),
+ array('UserCountry.getCountry', array('idSite' => $idSite,
+ 'date' => $dateTime,
+ 'periods' => array('month'),
+ 'testSuffix' => '_segment_continent',
+ 'segment' => 'continent==eur')),
- array(array('UserCountry.getLocationFromIP', 'Live.getLastVisitsDetails'), array(
- 'idSite' => $idSite,
- 'date' => $dateTime,
- 'periods' => array('month'),
- 'otherRequestParameters' => array('ip' => '194.57.91.215')
- )),
- );
- }
+ array(array('UserCountry.getLocationFromIP', 'Live.getLastVisitsDetails'), array(
+ 'idSite' => $idSite,
+ 'date' => $dateTime,
+ 'periods' => array('month'),
+ 'otherRequestParameters' => array('ip' => '194.57.91.215')
+ )),
+ );
+ }
}
Test_Piwik_Integration_ManyVisitorsOneWebsiteTest::$fixture
- = new Test_Piwik_Fixture_ManyVisitsWithGeoIP();
+ = new Test_Piwik_Fixture_ManyVisitsWithGeoIP();
diff --git a/tests/PHPUnit/Integration/NoVisitTest.php b/tests/PHPUnit/Integration/NoVisitTest.php
index c9b5d18238..50cbe20c65 100755
--- a/tests/PHPUnit/Integration/NoVisitTest.php
+++ b/tests/PHPUnit/Integration/NoVisitTest.php
@@ -13,7 +13,7 @@
*/
class Test_Piwik_Integration_NoVisit extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -29,8 +29,8 @@ class Test_Piwik_Integration_NoVisit extends IntegrationTestCase
{
// this will output empty XML result sets as no visit was tracked
return array(
- array('all', array('idSite' => self::$fixture->idSite,
- 'date' => self::$fixture->dateTime)),
+ array('all', array('idSite' => self::$fixture->idSite,
+ 'date' => self::$fixture->dateTime)),
array('all', array('idSite' => self::$fixture->idSite,
'date' => self::$fixture->dateTime,
'periods' => array('day', 'week'),
diff --git a/tests/PHPUnit/Integration/NonUnicodeTest.php b/tests/PHPUnit/Integration/NonUnicodeTest.php
index 995add7567..83628577ed 100755
--- a/tests/PHPUnit/Integration/NonUnicodeTest.php
+++ b/tests/PHPUnit/Integration/NonUnicodeTest.php
@@ -2,7 +2,7 @@
/**
* Piwik - Open source web analytics
*
- * @link http://piwik.org
+ * @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
@@ -12,41 +12,41 @@
*/
class Test_Piwik_Integration_NonUnicodeTest extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
-
- /**
- * @dataProvider getApiForTesting
- * @group Integration
- * @group NonUnicodeTest
- */
- public function testApi($api, $params)
- {
- $this->runApiTests($api, $params);
- }
-
- public function getApiForTesting()
- {
- $apiToCall = array(
- 'Actions.getSiteSearchKeywords',
- 'Actions.getPageTitles',
- 'Actions.getPageUrls',
- 'Referers.getWebsites',
- );
-
- return array(
- array($apiToCall, array('idSite' => self::$fixture->idSite1,
- 'date' => self::$fixture->dateTime,
- 'periods' => 'day'))
- );
- }
-
- public function getOutputPrefix()
- {
- return 'NonUnicode';
- }
+ public static $fixture = null; // initialized below class definition
+
+ /**
+ * @dataProvider getApiForTesting
+ * @group Integration
+ * @group NonUnicodeTest
+ */
+ public function testApi($api, $params)
+ {
+ $this->runApiTests($api, $params);
+ }
+
+ public function getApiForTesting()
+ {
+ $apiToCall = array(
+ 'Actions.getSiteSearchKeywords',
+ 'Actions.getPageTitles',
+ 'Actions.getPageUrls',
+ 'Referers.getWebsites',
+ );
+
+ return array(
+ array($apiToCall, array('idSite' => self::$fixture->idSite1,
+ 'date' => self::$fixture->dateTime,
+ 'periods' => 'day'))
+ );
+ }
+
+ public function getOutputPrefix()
+ {
+ return 'NonUnicode';
+ }
}
Test_Piwik_Integration_NonUnicodeTest::$fixture =
- new Test_Piwik_Fixture_SomeVisitsWithNonUnicodePageTitles();
+ new Test_Piwik_Fixture_SomeVisitsWithNonUnicodePageTitles();
diff --git a/tests/PHPUnit/Integration/OneVisitorOneWebsite_SeveralDaysDateRangeTest.php b/tests/PHPUnit/Integration/OneVisitorOneWebsite_SeveralDaysDateRangeTest.php
index 23f3b114b8..5a96ed8aed 100755
--- a/tests/PHPUnit/Integration/OneVisitorOneWebsite_SeveralDaysDateRangeTest.php
+++ b/tests/PHPUnit/Integration/OneVisitorOneWebsite_SeveralDaysDateRangeTest.php
@@ -15,7 +15,7 @@
*/
class Test_Piwik_Integration_OneVisitorOneWebsite_SeveralDaysDateRange extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -29,59 +29,59 @@ class Test_Piwik_Integration_OneVisitorOneWebsite_SeveralDaysDateRange extends I
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
-
+ $idSite = self::$fixture->idSite;
+
return array(
- // FIRST some MultiSites API goodness!
+ // FIRST some MultiSites API goodness!
// range test
- array('MultiSites.getAll', array( 'date' => '2010-12-15,2011-01-15',
- 'periods' => array('range')
- // Testing without &pattern= so should return all sites
- )),
+ array('MultiSites.getAll', array('date' => '2010-12-15,2011-01-15',
+ 'periods' => array('range')
+ // Testing without &pattern= so should return all sites
+ )),
// test several dates (tests use of IndexedByDate w/ 'date1,date2,etc.')
- array('MultiSites.getAll', array('date' => '2010-12-15',
- 'periods' => array('day'),
- 'testSuffix' => '_IndexedByDate',
- // Testing the pattern to getAll restrict websites using name matching
+ array('MultiSites.getAll', array('date' => '2010-12-15',
+ 'periods' => array('day'),
+ 'testSuffix' => '_IndexedByDate',
+ // Testing the pattern to getAll restrict websites using name matching
'otherRequestParameters' => array('pattern' => 'aAa')
)),
- // test getOne call used in MobileMessaging SMS reports
- array('MultiSites.getOne', array( 'date' => '2010-12-15,2011-01-15',
- 'periods' => array('range'),
- 'idSite' => $idSite,
- // Testing without &pattern= so should return all sites
- )),
+ // test getOne call used in MobileMessaging SMS reports
+ array('MultiSites.getOne', array('date' => '2010-12-15,2011-01-15',
+ 'periods' => array('range'),
+ 'idSite' => $idSite,
+ // Testing without &pattern= so should return all sites
+ )),
+
+ // test that multiple periods are not supported
+ array('MultiSites.getAll', array('date' => '2010-12-15,2011-01-15',
+ 'periods' => array('day'),
+ 'testSuffix' => '_MultipleDatesNotSupported',
+ )),
+
+ //---------------------------------------
+ // THEN some Socials tests. Share these...
+ array('Referers.getSocials', array('idSite' => 'all',
+ 'date' => '2010-12-13,2011-01-18',
+ 'periods' => array('range'))),
+
+ array('Referers.getSocials', array('idSite' => 'all',
+ 'date' => '2010-12-10',
+ 'periods' => array('day'),
+ 'setDateLastN' => true,
+ 'testSuffix' => '_IndexedByDate')),
- // test that multiple periods are not supported
- array('MultiSites.getAll', array( 'date' => '2010-12-15,2011-01-15',
- 'periods' => array('day'),
- 'testSuffix' => '_MultipleDatesNotSupported',
- )),
+ array('Referers.getUrlsForSocial', array('idSite' => 'all', // test w/o idSubtable
+ 'date' => '2010-12-13,2011-01-18',
+ 'periods' => 'range',
+ 'testSuffix' => '_noIdSubtable')),
- //---------------------------------------
- // THEN some Socials tests. Share these...
- array('Referers.getSocials', array('idSite' => 'all',
- 'date' => '2010-12-13,2011-01-18',
- 'periods' => array('range'))),
-
- array('Referers.getSocials', array('idSite' => 'all',
- 'date' => '2010-12-10',
- 'periods' => array('day'),
- 'setDateLastN' => true,
- 'testSuffix' => '_IndexedByDate')),
-
- array('Referers.getUrlsForSocial', array('idSite' => 'all', // test w/o idSubtable
- 'date' => '2010-12-13,2011-01-18',
- 'periods' => 'range',
- 'testSuffix' => '_noIdSubtable')),
-
- array('Referers.getUrlsForSocial', array('idSite' => 1, // test w/ idSubtable
- 'date' => '2010-12-13,2011-01-18',
- 'periods' => 'range',
- 'supertableApi' => 'Referers.getSocials')),
+ array('Referers.getUrlsForSocial', array('idSite' => 1, // test w/ idSubtable
+ 'date' => '2010-12-13,2011-01-18',
+ 'periods' => 'range',
+ 'supertableApi' => 'Referers.getSocials')),
);
}
@@ -92,5 +92,5 @@ class Test_Piwik_Integration_OneVisitorOneWebsite_SeveralDaysDateRange extends I
}
Test_Piwik_Integration_OneVisitorOneWebsite_SeveralDaysDateRange::$fixture
- = new Test_Piwik_Fixture_VisitsOverSeveralDays();
+ = new Test_Piwik_Fixture_VisitsOverSeveralDays();
diff --git a/tests/PHPUnit/Integration/OneVisitorOneWebsite_SeveralDaysDateRange_ArchivingTestsTest.php b/tests/PHPUnit/Integration/OneVisitorOneWebsite_SeveralDaysDateRange_ArchivingTestsTest.php
index 4632eeb60b..cbeaf000b5 100755
--- a/tests/PHPUnit/Integration/OneVisitorOneWebsite_SeveralDaysDateRange_ArchivingTestsTest.php
+++ b/tests/PHPUnit/Integration/OneVisitorOneWebsite_SeveralDaysDateRange_ArchivingTestsTest.php
@@ -12,7 +12,7 @@
*/
class Test_Piwik_Integration_OneVisitorOneWebsite_SeveralDaysDateRange_ArchivingTests extends IntegrationTestCase
{
- public static $fixture = null; // initialized below test definition
+ public static $fixture = null; // initialized below test definition
public function getOutputPrefix()
{
@@ -31,13 +31,13 @@ class Test_Piwik_Integration_OneVisitorOneWebsite_SeveralDaysDateRange_Archiving
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
-
+ $idSite = self::$fixture->idSite;
+
$apiToCall = array('Actions.getPageUrls',
- 'VisitsSummary.get',
- 'UserSettings.getResolution',
- 'VisitFrequency.get',
- 'VisitTime.getVisitInformationPerServerTime');
+ 'VisitsSummary.get',
+ 'UserSettings.getResolution',
+ 'VisitFrequency.get',
+ 'VisitTime.getVisitInformationPerServerTime');
// 2 segments: ALL and another way of expressing ALL but triggering the Segment code path
$segments = array(
@@ -83,15 +83,15 @@ class Test_Piwik_Integration_OneVisitorOneWebsite_SeveralDaysDateRange_Archiving
'archive_numeric_2011_01' => 0,
);
foreach ($tests as $table => $expectedRows) {
- $sql = "SELECT count(*) FROM " . Piwik_Common::prefixTable($table) . " WHERE period = " . Piwik::$idPeriods['range'];
+ $sql = "SELECT count(*) FROM " . Piwik_Common::prefixTable($table) . " WHERE period = " . Piwik::$idPeriods['range'];
$countBlobs = Zend_Registry::get('db')->fetchOne($sql);
- $this->assertEquals($expectedRows, $countBlobs, "$table expected $expectedRows, got $countBlobs");
+ $this->assertEquals($expectedRows, $countBlobs, "$table expected $expectedRows, got $countBlobs");
}
}
}
Test_Piwik_Integration_OneVisitorOneWebsite_SeveralDaysDateRange_ArchivingTests::$fixture
- = new Test_Piwik_Fixture_VisitsOverSeveralDays();
+ = new Test_Piwik_Fixture_VisitsOverSeveralDays();
diff --git a/tests/PHPUnit/Integration/OneVisitorTwoVisitsTest.php b/tests/PHPUnit/Integration/OneVisitorTwoVisitsTest.php
index 3035c8a718..5d07e49b24 100755
--- a/tests/PHPUnit/Integration/OneVisitorTwoVisitsTest.php
+++ b/tests/PHPUnit/Integration/OneVisitorTwoVisitsTest.php
@@ -18,18 +18,18 @@
*/
class Test_Piwik_Integration_OneVisitorTwoVisits extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class
-
- public function setUp()
- {
- Piwik_API_Proxy::getInstance()->setHideIgnoredFunctions(false);
- }
-
- public function tearDown()
- {
- Piwik_API_Proxy::getInstance()->setHideIgnoredFunctions(true);
- }
-
+ public static $fixture = null; // initialized below class
+
+ public function setUp()
+ {
+ Piwik_API_Proxy::getInstance()->setHideIgnoredFunctions(false);
+ }
+
+ public function tearDown()
+ {
+ Piwik_API_Proxy::getInstance()->setHideIgnoredFunctions(true);
+ }
+
/**
* @dataProvider getApiForTesting
* @group Integration
@@ -42,19 +42,18 @@ class Test_Piwik_Integration_OneVisitorTwoVisits extends IntegrationTestCase
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $dateTime = self::$fixture->dateTime;
-
+ $idSite = self::$fixture->idSite;
+ $dateTime = self::$fixture->dateTime;
+
$enExtraParam = array('expanded' => 1, 'flat' => 1, 'include_aggregate_rows' => 0, 'translateColumnNames' => 1);
- $bulkUrls = array(
- "idSite=".$idSite."&date=2010-03-06&format=json&expanded=1&period=day&method=VisitsSummary.get",
- "idSite=".$idSite."&date=2010-03-06&format=xml&expanded=1&period=day&method=VisitsSummary.get",
- "idSite=".$idSite."&date=2010-03-06&format=json&expanded=1&period=day&method="
+ $bulkUrls = array(
+ "idSite=" . $idSite . "&date=2010-03-06&format=json&expanded=1&period=day&method=VisitsSummary.get",
+ "idSite=" . $idSite . "&date=2010-03-06&format=xml&expanded=1&period=day&method=VisitsSummary.get",
+ "idSite=" . $idSite . "&date=2010-03-06&format=json&expanded=1&period=day&method="
. "VisitorInterest.getNumberOfVisitsPerVisitDuration"
);
- foreach ($bulkUrls as &$url)
- {
- $url = urlencode($url);
+ foreach ($bulkUrls as &$url) {
+ $url = urlencode($url);
}
return array(
array('all', array('idSite' => $idSite, 'date' => $dateTime)),
@@ -71,101 +70,99 @@ class Test_Piwik_Integration_OneVisitorTwoVisits extends IntegrationTestCase
'testSuffix' => '_csv')),
array('API.getBulkRequest', array('otherRequestParameters' => array('urls' => $bulkUrls))),
-
+
// test API.getProcessedReport w/ report that is its own 'actionToLoadSubTables'
- array('API.getProcessedReport', array('idSite' => $idSite,
- 'date' => $dateTime,
- 'periods' => array('week'),
- 'apiModule' => 'Actions',
- 'apiAction' => 'getPageUrls',
- 'supertableApi' => 'Actions.getPageUrls',
- 'testSuffix' => '__subtable')),
-
- // test hideColumns && showColumns parameters
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime, 'periods' => 'day',
- 'testSuffix' => '_hideColumns_',
- 'otherRequestParameters' => array(
- 'hideColumns' => 'nb_visits_converted,max_actions,bounce_count,nb_hits,'
- .'nb_visits,nb_actions,sum_visit_length,avg_time_on_site'
- ))),
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime, 'periods' => 'day',
- 'testSuffix' => '_showColumns_',
- 'otherRequestParameters' => array(
- 'showColumns' => 'nb_visits,nb_actions,nb_hits'
- ))),
- array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime, 'periods' => 'day',
- 'testSuffix' => '_hideAllColumns_',
- 'otherRequestParameters' => array(
- 'hideColumns' => 'nb_visits_converted,max_actions,bounce_count,nb_hits,'
- .'nb_visits,nb_actions,sum_visit_length,avg_time_on_site,'
- .'bounce_rate,nb_uniq_visitors,nb_actions_per_visit,'
- ))),
-
- // test hideColumns w/ API.getProcessedReport
- array('API.getProcessedReport', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => 'day', 'apiModule' => 'Actions',
- 'apiAction' => 'getPageTitles', 'testSuffix' => '_hideColumns_',
- 'otherRequestParameters' => array(
- 'hideColumns' => 'nb_visits_converted,xyzaug,entry_nb_visits,'.
- 'bounce_rate,nb_hits,nb_visits,avg_time_on_page,avg_time_generation'
- ))),
-
- array('API.getProcessedReport', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => 'day', 'apiModule' => 'Actions',
- 'apiAction' => 'getPageTitles', 'testSuffix' => '_showColumns_',
- 'otherRequestParameters' => array(
- 'showColumns' => 'nb_visits_converted,xuena,entry_nb_visits,'.
- 'bounce_rate,nb_hits'
- ))),
- array('API.getProcessedReport', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => 'day', 'apiModule' => 'VisitTime',
- 'apiAction' => 'getVisitInformationPerServerTime',
- 'testSuffix' => '_showColumnsWithProcessedMetrics_',
- 'otherRequestParameters' => array(
- 'showColumns' => 'nb_visits,revenue'
- ))),
-
- // test hideColumns w/ expanded=1
- array('Actions.getPageTitles', array('idSite' => $idSite, 'date' => $dateTime,
- 'periods' => 'day', 'testSuffix' => '_hideColumns_',
- 'otherRequestParameters' => array(
- 'hideColumns' => 'nb_visits_converted,entry_nb_visits,'.
- 'bounce_rate,nb_hits,nb_visits,sum_time_spent,'.
- 'entry_sum_visit_length,entry_bounce_count,exit_nb_visits,'.
- 'entry_nb_uniq_visitors,exit_nb_uniq_visitors,entry_nb_actions,'.
- 'avg_time_generation',
- 'expanded' => '1'
- ))),
+ array('API.getProcessedReport', array('idSite' => $idSite,
+ 'date' => $dateTime,
+ 'periods' => array('week'),
+ 'apiModule' => 'Actions',
+ 'apiAction' => 'getPageUrls',
+ 'supertableApi' => 'Actions.getPageUrls',
+ 'testSuffix' => '__subtable')),
+
+ // test hideColumns && showColumns parameters
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime, 'periods' => 'day',
+ 'testSuffix' => '_hideColumns_',
+ 'otherRequestParameters' => array(
+ 'hideColumns' => 'nb_visits_converted,max_actions,bounce_count,nb_hits,'
+ . 'nb_visits,nb_actions,sum_visit_length,avg_time_on_site'
+ ))),
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime, 'periods' => 'day',
+ 'testSuffix' => '_showColumns_',
+ 'otherRequestParameters' => array(
+ 'showColumns' => 'nb_visits,nb_actions,nb_hits'
+ ))),
+ array('VisitsSummary.get', array('idSite' => $idSite, 'date' => $dateTime, 'periods' => 'day',
+ 'testSuffix' => '_hideAllColumns_',
+ 'otherRequestParameters' => array(
+ 'hideColumns' => 'nb_visits_converted,max_actions,bounce_count,nb_hits,'
+ . 'nb_visits,nb_actions,sum_visit_length,avg_time_on_site,'
+ . 'bounce_rate,nb_uniq_visitors,nb_actions_per_visit,'
+ ))),
+
+ // test hideColumns w/ API.getProcessedReport
+ array('API.getProcessedReport', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => 'day', 'apiModule' => 'Actions',
+ 'apiAction' => 'getPageTitles', 'testSuffix' => '_hideColumns_',
+ 'otherRequestParameters' => array(
+ 'hideColumns' => 'nb_visits_converted,xyzaug,entry_nb_visits,' .
+ 'bounce_rate,nb_hits,nb_visits,avg_time_on_page,avg_time_generation'
+ ))),
+
+ array('API.getProcessedReport', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => 'day', 'apiModule' => 'Actions',
+ 'apiAction' => 'getPageTitles', 'testSuffix' => '_showColumns_',
+ 'otherRequestParameters' => array(
+ 'showColumns' => 'nb_visits_converted,xuena,entry_nb_visits,' .
+ 'bounce_rate,nb_hits'
+ ))),
+ array('API.getProcessedReport', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => 'day', 'apiModule' => 'VisitTime',
+ 'apiAction' => 'getVisitInformationPerServerTime',
+ 'testSuffix' => '_showColumnsWithProcessedMetrics_',
+ 'otherRequestParameters' => array(
+ 'showColumns' => 'nb_visits,revenue'
+ ))),
+
+ // test hideColumns w/ expanded=1
+ array('Actions.getPageTitles', array('idSite' => $idSite, 'date' => $dateTime,
+ 'periods' => 'day', 'testSuffix' => '_hideColumns_',
+ 'otherRequestParameters' => array(
+ 'hideColumns' => 'nb_visits_converted,entry_nb_visits,' .
+ 'bounce_rate,nb_hits,nb_visits,sum_time_spent,' .
+ 'entry_sum_visit_length,entry_bounce_count,exit_nb_visits,' .
+ 'entry_nb_uniq_visitors,exit_nb_uniq_visitors,entry_nb_actions,' .
+ 'avg_time_generation',
+ 'expanded' => '1'
+ ))),
);
}
-
- /**
- * Test that Archive_Single::preFetchBlob won't fetch extra unnecessary blobs.
- *
- * @group Integration
- * @group OneVisitorTwoVisits
- */
- public function testArchiveSinglePreFetchBlob()
- {
- $archive = Piwik_Archive::build(self::$fixture->idSite, 'day', self::$fixture->dateTime);
- $archive->preFetchBlob('Actions_actions');
- $cache = $archive->getBlobCache();
-
- $foundSubtable = false;
-
- $this->assertTrue(count($cache) > 0, "empty blob cache");
- foreach ($cache as $name => $value)
- {
- $this->assertTrue(strpos($name, "Actions_actions_url") === false, "found blob w/ name '$name'");
-
- if (strpos($name, "Actions_actions_") !== false)
- {
- $foundSubtable = true;
- }
- }
-
- $this->assertTrue($foundSubtable, "Actions_actions subtable was not loaded");
- }
+
+ /**
+ * Test that Archive_Single::preFetchBlob won't fetch extra unnecessary blobs.
+ *
+ * @group Integration
+ * @group OneVisitorTwoVisits
+ */
+ public function testArchiveSinglePreFetchBlob()
+ {
+ $archive = Piwik_Archive::build(self::$fixture->idSite, 'day', self::$fixture->dateTime);
+ $archive->preFetchBlob('Actions_actions');
+ $cache = $archive->getBlobCache();
+
+ $foundSubtable = false;
+
+ $this->assertTrue(count($cache) > 0, "empty blob cache");
+ foreach ($cache as $name => $value) {
+ $this->assertTrue(strpos($name, "Actions_actions_url") === false, "found blob w/ name '$name'");
+
+ if (strpos($name, "Actions_actions_") !== false) {
+ $foundSubtable = true;
+ }
+ }
+
+ $this->assertTrue($foundSubtable, "Actions_actions subtable was not loaded");
+ }
}
Test_Piwik_Integration_OneVisitorTwoVisits::$fixture = new Test_Piwik_Fixture_OneVisitorTwoVisits();
diff --git a/tests/PHPUnit/Integration/OneVisitorTwoVisits_withCookieSupportTest.php b/tests/PHPUnit/Integration/OneVisitorTwoVisits_withCookieSupportTest.php
index cdc3b669ea..6baa804192 100755
--- a/tests/PHPUnit/Integration/OneVisitorTwoVisits_withCookieSupportTest.php
+++ b/tests/PHPUnit/Integration/OneVisitorTwoVisits_withCookieSupportTest.php
@@ -12,7 +12,7 @@
*/
class Test_Piwik_Integration_OneVisitorTwoVisits_WithCookieSupport extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class
+ public static $fixture = null; // initialized below class
/**
* @dataProvider getApiForTesting
diff --git a/tests/PHPUnit/Integration/OneVisitor_LongUrlsTruncatedTest.php b/tests/PHPUnit/Integration/OneVisitor_LongUrlsTruncatedTest.php
index 8504b51b8d..4d18196094 100644
--- a/tests/PHPUnit/Integration/OneVisitor_LongUrlsTruncatedTest.php
+++ b/tests/PHPUnit/Integration/OneVisitor_LongUrlsTruncatedTest.php
@@ -10,7 +10,7 @@
*/
class Test_Piwik_Integration_OneVisitor_LongUrlsTruncated extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
diff --git a/tests/PHPUnit/Integration/OneVisitor_NoKeywordSpecifiedTest.php b/tests/PHPUnit/Integration/OneVisitor_NoKeywordSpecifiedTest.php
index de5fe1ab80..966c85f2ef 100755
--- a/tests/PHPUnit/Integration/OneVisitor_NoKeywordSpecifiedTest.php
+++ b/tests/PHPUnit/Integration/OneVisitor_NoKeywordSpecifiedTest.php
@@ -14,7 +14,7 @@
*/
class Test_Piwik_Integration_OneVisitor_NoKeywordSpecified extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -31,9 +31,9 @@ class Test_Piwik_Integration_OneVisitor_NoKeywordSpecified extends IntegrationTe
$apiToCall = array('Referers.getKeywords', 'Live.getLastVisitsDetails');
return array(
- array($apiToCall, array('idSite' => self::$fixture->idSite,
- 'date' => self::$fixture->dateTime,
- 'language' => 'fr') )
+ array($apiToCall, array('idSite' => self::$fixture->idSite,
+ 'date' => self::$fixture->dateTime,
+ 'language' => 'fr'))
);
}
diff --git a/tests/PHPUnit/Integration/PeriodIsRange_DateIsLastN_MetadataAndNormalAPITest.php b/tests/PHPUnit/Integration/PeriodIsRange_DateIsLastN_MetadataAndNormalAPITest.php
index 28586368cf..e8dd0e63bd 100755
--- a/tests/PHPUnit/Integration/PeriodIsRange_DateIsLastN_MetadataAndNormalAPITest.php
+++ b/tests/PHPUnit/Integration/PeriodIsRange_DateIsLastN_MetadataAndNormalAPITest.php
@@ -11,31 +11,30 @@
*/
class Test_Piwik_Integration_PeriodIsRange_DateIsLastN_MetadataAndNormalAPI extends IntegrationTestCase
{
- public static $fixture = null;
-
- static $shouldSkipTestThisTime = false;
+ public static $fixture = null;
- public static function setUpBeforeClass()
+ static $shouldSkipTestThisTime = false;
+
+ public static function setUpBeforeClass()
{
- self::$shouldSkipTestThisTime = in_array(date('G'), array(22, 23));
+ self::$shouldSkipTestThisTime = in_array(date('G'), array(22, 23));
- if (self::$shouldSkipTestThisTime)
- {
- print("\nSKIPPED test PeriodIsRange_DateIsLastN_MetadataAndNormalAPI since it fails around midnight...\n");
- return;
- }
+ if (self::$shouldSkipTestThisTime) {
+ print("\nSKIPPED test PeriodIsRange_DateIsLastN_MetadataAndNormalAPI since it fails around midnight...\n");
+ return;
+ }
self::$fixture->dateTime = Piwik_Date::factory('now')->getDateTime();
parent::setUpBeforeClass();
}
-
+
public static function tearDownAfterClass()
{
- if (self::$shouldSkipTestThisTime) {
- return;
- }
-
- parent::tearDownAfterClass();
+ if (self::$shouldSkipTestThisTime) {
+ return;
+ }
+
+ parent::tearDownAfterClass();
}
/**
@@ -45,18 +44,17 @@ class Test_Piwik_Integration_PeriodIsRange_DateIsLastN_MetadataAndNormalAPI exte
*/
public function testApi($api, $params)
{
- if (self::$shouldSkipTestThisTime)
- {
- return;
- }
- $this->runApiTests($api, $params);
+ if (self::$shouldSkipTestThisTime) {
+ return;
+ }
+ $this->runApiTests($api, $params);
}
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $visitorId = self::$fixture->visitorId;
-
+ $idSite = self::$fixture->idSite;
+ $visitorId = self::$fixture->visitorId;
+
$apiToCall = array(
'API.getProcessedReport',
'Actions.getPageUrls',
@@ -75,7 +73,7 @@ class Test_Piwik_Integration_PeriodIsRange_DateIsLastN_MetadataAndNormalAPI exte
'visitorId!=33c31e01394bdc63;daysSinceFirstVisit!=50',
//'pageUrl!=http://unknown/not/viewed',
);
- $dates = array(
+ $dates = array(
'last7',
Piwik_Date::factory('now')->subDay(6)->toString() . ',today',
Piwik_Date::factory('now')->subDay(6)->toString() . ',now',
@@ -86,7 +84,7 @@ class Test_Piwik_Integration_PeriodIsRange_DateIsLastN_MetadataAndNormalAPI exte
foreach ($dates as $date) {
$result[] = array($apiToCall, array('idSite' => $idSite, 'date' => $date,
'periods' => array('range'), 'segment' => $segment,
- // testing getLastVisitsForVisitor requires a visitor ID
+ // testing getLastVisitsForVisitor requires a visitor ID
'visitorId' => $visitorId));
}
}
@@ -101,6 +99,6 @@ class Test_Piwik_Integration_PeriodIsRange_DateIsLastN_MetadataAndNormalAPI exte
}
Test_Piwik_Integration_PeriodIsRange_DateIsLastN_MetadataAndNormalAPI::$fixture =
- new Test_Piwik_Fixture_TwoVisitsWithCustomVariables();
+ new Test_Piwik_Fixture_TwoVisitsWithCustomVariables();
Test_Piwik_Integration_PeriodIsRange_DateIsLastN_MetadataAndNormalAPI::$fixture->doExtraQuoteTests = false;
diff --git a/tests/PHPUnit/Integration/RowEvolutionTest.php b/tests/PHPUnit/Integration/RowEvolutionTest.php
index a0da7c54c1..616822417c 100755
--- a/tests/PHPUnit/Integration/RowEvolutionTest.php
+++ b/tests/PHPUnit/Integration/RowEvolutionTest.php
@@ -11,7 +11,7 @@
*/
class Test_Piwik_Integration_RowEvolution extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -25,11 +25,11 @@ class Test_Piwik_Integration_RowEvolution extends IntegrationTestCase
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $idSite2 = self::$fixture->idSite2;
- $today = self::$fixture->today;
- $keywords = self::$fixture->keywords;
-
+ $idSite = self::$fixture->idSite;
+ $idSite2 = self::$fixture->idSite2;
+ $today = self::$fixture->today;
+ $keywords = self::$fixture->keywords;
+
$return = array();
$config = array(
@@ -49,110 +49,110 @@ class Test_Piwik_Integration_RowEvolution extends IntegrationTestCase
$return[] = array('API.getRowEvolution', $config);
// Websites, hierarchical
- $config['testSuffix'] = '_referrer2';
- $referrerLabel = urlencode('www.referrer0.com') . '>' . urlencode('theReferrerPage1.html');
+ $config['testSuffix'] = '_referrer2';
+ $referrerLabel = urlencode('www.referrer0.com') . '>' . urlencode('theReferrerPage1.html');
$config['otherRequestParameters']['label'] = urlencode($referrerLabel);
- $return[] = array('API.getRowEvolution', $config);
+ $return[] = array('API.getRowEvolution', $config);
// Websites, multiple labels including one hierarchical
- $config['testSuffix'] = '_referrerMulti1';
- $referrerLabel = $referrerLabel . ',' . urlencode('www.referrer2.com');
+ $config['testSuffix'] = '_referrerMulti1';
+ $referrerLabel = $referrerLabel . ',' . urlencode('www.referrer2.com');
$config['otherRequestParameters']['label'] = urlencode($referrerLabel);
- $return[] = array('API.getRowEvolution', $config);
+ $return[] = array('API.getRowEvolution', $config);
// Keywords, label containing > and ,
$config['otherRequestParameters']['apiAction'] = 'getKeywords';
- $config['testSuffix'] = '_LabelReservedCharacters';
- $keywordsStr = urlencode($keywords[0]) . ',' . urlencode($keywords[1]);
- $config['otherRequestParameters']['label'] = urlencode($keywordsStr);
- $return[] = array('API.getRowEvolution', $config);
+ $config['testSuffix'] = '_LabelReservedCharacters';
+ $keywordsStr = urlencode($keywords[0]) . ',' . urlencode($keywords[1]);
+ $config['otherRequestParameters']['label'] = urlencode($keywordsStr);
+ $return[] = array('API.getRowEvolution', $config);
// Keywords, hierarchical
$config['otherRequestParameters']['apiAction'] = 'getSearchEngines';
- $config['testSuffix'] = '_LabelReservedCharactersHierarchical';
- $keywordsStr = "Google>" . urlencode(strtolower($keywords[0]))
+ $config['testSuffix'] = '_LabelReservedCharactersHierarchical';
+ $keywordsStr = "Google>" . urlencode(strtolower($keywords[0]))
. ',Google>' . urlencode(strtolower($keywords[1]))
. ',Google>' . urlencode(strtolower($keywords[2]));
// Test multiple labels search engines, Google should also have a 'logo' entry
$config['otherRequestParameters']['label'] = urlencode($keywordsStr . ",Google");
- $return[] = array('API.getRowEvolution', $config);
+ $return[] = array('API.getRowEvolution', $config);
// Actions > Pages titles, standard label
- $config['testSuffix'] = '_pageTitles';
- $config['periods'] = array('day', 'week');
+ $config['testSuffix'] = '_pageTitles';
+ $config['periods'] = array('day', 'week');
$config['otherRequestParameters']['apiModule'] = 'Actions';
$config['otherRequestParameters']['apiAction'] = 'getPageTitles';
- $config['otherRequestParameters']['label'] = urlencode('incredible title 0');
- $return[] = array('API.getRowEvolution', $config);
+ $config['otherRequestParameters']['label'] = urlencode('incredible title 0');
+ $return[] = array('API.getRowEvolution', $config);
// Actions > Page titles, multiple labels
- $config['testSuffix'] = '_pageTitlesMulti';
- $label = urlencode('incredible title 0') . ',' . urlencode('incredible title 2');
+ $config['testSuffix'] = '_pageTitlesMulti';
+ $label = urlencode('incredible title 0') . ',' . urlencode('incredible title 2');
$config['otherRequestParameters']['label'] = urlencode($label);
- $return[] = array('API.getRowEvolution', $config);
+ $return[] = array('API.getRowEvolution', $config);
// Actions > Page URLS, hierarchical label
- $config['testSuffix'] = '_pageUrls';
- $config['periods'] = array('range');
- $config['otherRequestParameters']['date'] = '2010-03-01,2010-03-06';
+ $config['testSuffix'] = '_pageUrls';
+ $config['periods'] = array('range');
+ $config['otherRequestParameters']['date'] = '2010-03-01,2010-03-06';
$config['otherRequestParameters']['apiModule'] = 'Actions';
$config['otherRequestParameters']['apiAction'] = 'getPageUrls';
- $config['otherRequestParameters']['label'] = urlencode('my>dir>' . urlencode('/page3?foo=bar&baz=bar'));
- $return[] = array('API.getRowEvolution', $config);
+ $config['otherRequestParameters']['label'] = urlencode('my>dir>' . urlencode('/page3?foo=bar&baz=bar'));
+ $return[] = array('API.getRowEvolution', $config);
// Goals > Visits Until Conversion, idGoal != 0
- $config['testSuffix'] = '_goals_visitsUntilConversion';
- $config['periods'] = array('day');
- $config['otherRequestParameters']['date'] = '2010-02-06,2010-03-06';
- $config['otherRequestParameters']['period'] = 'day';
+ $config['testSuffix'] = '_goals_visitsUntilConversion';
+ $config['periods'] = array('day');
+ $config['otherRequestParameters']['date'] = '2010-02-06,2010-03-06';
+ $config['otherRequestParameters']['period'] = 'day';
$config['otherRequestParameters']['apiModule'] = 'Goals';
$config['otherRequestParameters']['apiAction'] = 'getVisitsUntilConversion';
- $config['otherRequestParameters']['label'] = urlencode('1 visit, 2 visits');
- $config['otherRequestParameters']['idGoal'] = '2';
- $return[] = array('API.getRowEvolution', $config);
+ $config['otherRequestParameters']['label'] = urlencode('1 visit, 2 visits');
+ $config['otherRequestParameters']['idGoal'] = '2';
+ $return[] = array('API.getRowEvolution', $config);
// Goals > Visits Until Conversion, idGoal != 0, without specifying labels
- $config['testSuffix'] = '_goals_visitsUntilConversion_WithoutLabels';
- $config['periods'] = array('day');
- $config['otherRequestParameters']['date'] = '2010-02-06,2010-03-06';
- $config['otherRequestParameters']['period'] = 'day';
- $config['otherRequestParameters']['apiModule'] = 'Goals';
- $config['otherRequestParameters']['apiAction'] = 'getVisitsUntilConversion';
- $config['otherRequestParameters']['label'] = false;
- $config['otherRequestParameters']['filter_limit'] = 2;
+ $config['testSuffix'] = '_goals_visitsUntilConversion_WithoutLabels';
+ $config['periods'] = array('day');
+ $config['otherRequestParameters']['date'] = '2010-02-06,2010-03-06';
+ $config['otherRequestParameters']['period'] = 'day';
+ $config['otherRequestParameters']['apiModule'] = 'Goals';
+ $config['otherRequestParameters']['apiAction'] = 'getVisitsUntilConversion';
+ $config['otherRequestParameters']['label'] = false;
+ $config['otherRequestParameters']['filter_limit'] = 2;
$config['otherRequestParameters']['filter_sort_column'] = 'nb_conversions';
- $config['otherRequestParameters']['idGoal'] = '2';
- $return[] = array('API.getRowEvolution', $config);
-
+ $config['otherRequestParameters']['idGoal'] = '2';
+ $return[] = array('API.getRowEvolution', $config);
+
// test date range where most recent date has no data (for #3465)
$return[] = array('API.getRowEvolution', array(
- 'testSuffix' => '_multipleDates_lastNoData',
- 'periods' => 'month',
- 'idSite' => $idSite,
- 'date' => $today,
- 'otherRequestParameters' => array(
- 'date' => '2010-02-01,2010-04-08',
- 'period' => 'month',
- 'apiModule' => 'Referers',
- 'apiAction' => 'getKeywords',
- // no label
- )
+ 'testSuffix' => '_multipleDates_lastNoData',
+ 'periods' => 'month',
+ 'idSite' => $idSite,
+ 'date' => $today,
+ 'otherRequestParameters' => array(
+ 'date' => '2010-02-01,2010-04-08',
+ 'period' => 'month',
+ 'apiModule' => 'Referers',
+ 'apiAction' => 'getKeywords',
+ // no label
+ )
));
-
+
// test that reports that process row labels are treated correctly
$return[] = array('API.getRowEvolution', array(
- 'testSuffix' => '_processedRowLabel',
- 'periods' => 'day',
- 'idSite' => $idSite2,
- 'date' => $today,
- 'otherRequestParameters' => array(
- 'date' => '2010-03-01,2010-03-06',
- 'period' => 'month',
- 'apiModule' => 'UserSettings',
- 'apiAction' => 'getBrowser',
- 'label' => 'Firefox,Chrome,Opera'
- )
-
+ 'testSuffix' => '_processedRowLabel',
+ 'periods' => 'day',
+ 'idSite' => $idSite2,
+ 'date' => $today,
+ 'otherRequestParameters' => array(
+ 'date' => '2010-03-01,2010-03-06',
+ 'period' => 'month',
+ 'apiModule' => 'UserSettings',
+ 'apiAction' => 'getBrowser',
+ 'label' => 'Firefox,Chrome,Opera'
+ )
+
));
return $return;
@@ -165,5 +165,5 @@ class Test_Piwik_Integration_RowEvolution extends IntegrationTestCase
}
Test_Piwik_Integration_RowEvolution::$fixture
- = new Test_Piwik_Fixture_TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers();
+ = new Test_Piwik_Fixture_TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers();
diff --git a/tests/PHPUnit/Integration/SiteSearchTest.php b/tests/PHPUnit/Integration/SiteSearchTest.php
index 881f17530c..545c794b38 100755
--- a/tests/PHPUnit/Integration/SiteSearchTest.php
+++ b/tests/PHPUnit/Integration/SiteSearchTest.php
@@ -11,7 +11,7 @@
*/
class Test_Piwik_Integration_SiteSearch extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -27,23 +27,23 @@ class Test_Piwik_Integration_SiteSearch extends IntegrationTestCase
{
return array(
'Actions.get',
- 'Actions.getPageUrls',
+ 'Actions.getPageUrls',
'Actions.getPageTitles',
- 'CustomVariables.getCustomVariables',
- 'Actions.getSiteSearchKeywords',
- 'Actions.getSiteSearchCategories',
- 'Actions.getSiteSearchNoResultKeywords',
- 'Actions.getPageTitlesFollowingSiteSearch',
- 'Actions.getPageUrlsFollowingSiteSearch',
+ 'CustomVariables.getCustomVariables',
+ 'Actions.getSiteSearchKeywords',
+ 'Actions.getSiteSearchCategories',
+ 'Actions.getSiteSearchNoResultKeywords',
+ 'Actions.getPageTitlesFollowingSiteSearch',
+ 'Actions.getPageUrlsFollowingSiteSearch',
);
}
public function getApiForTesting()
{
- $dateTime = self::$fixture->dateTime;
- $idSite1 = self::$fixture->idSite1;
-
- $apiToCall = $this->getApiToCall();
+ $dateTime = self::$fixture->dateTime;
+ $idSite1 = self::$fixture->idSite1;
+
+ $apiToCall = $this->getApiToCall();
$periods = array('day', 'month');
@@ -53,15 +53,15 @@ class Test_Piwik_Integration_SiteSearch extends IntegrationTestCase
'date' => $dateTime,
'periods' => $periods,
'setDateLastN' => true,
- 'testSuffix' => '_AllSites')),
+ 'testSuffix' => '_AllSites')),
- // We also test a single period/single site to check that this use case (Reports per idSite in the response) works
- array($apiToCall, array(
- 'idSite' => $idSite1,
- 'date' => $dateTime,
- 'periods' => $periods,
- 'setDateLastN' => false,
- 'testSuffix' => '_NotLastNPeriods')),
+ // We also test a single period/single site to check that this use case (Reports per idSite in the response) works
+ array($apiToCall, array(
+ 'idSite' => $idSite1,
+ 'date' => $dateTime,
+ 'periods' => $periods,
+ 'setDateLastN' => false,
+ 'testSuffix' => '_NotLastNPeriods')),
);
// testing metadata API for multiple periods
diff --git a/tests/PHPUnit/Integration/TrackCustomVariablesAndCampaigns_ForceUsingVisitIdNotHeuristicsTest.php b/tests/PHPUnit/Integration/TrackCustomVariablesAndCampaigns_ForceUsingVisitIdNotHeuristicsTest.php
index 73b6f00efc..3616dfe3a9 100755
--- a/tests/PHPUnit/Integration/TrackCustomVariablesAndCampaigns_ForceUsingVisitIdNotHeuristicsTest.php
+++ b/tests/PHPUnit/Integration/TrackCustomVariablesAndCampaigns_ForceUsingVisitIdNotHeuristicsTest.php
@@ -12,7 +12,7 @@
*/
class Test_Piwik_Integration_TrackCustomVariablesAndCampaigns_ForceUsingVisitIdNotHeuristics extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -41,5 +41,5 @@ class Test_Piwik_Integration_TrackCustomVariablesAndCampaigns_ForceUsingVisitIdN
}
Test_Piwik_Integration_TrackCustomVariablesAndCampaigns_ForceUsingVisitIdNotHeuristics::$fixture =
- new Test_Piwik_Fixture_SomeVisitsCustomVariablesCampaignsNotHeuristics();
+ new Test_Piwik_Fixture_SomeVisitsCustomVariablesCampaignsNotHeuristics();
diff --git a/tests/PHPUnit/Integration/TrackGoals_AllowMultipleConversionsPerVisitTest.php b/tests/PHPUnit/Integration/TrackGoals_AllowMultipleConversionsPerVisitTest.php
index 0127a38161..1c7b05699f 100755
--- a/tests/PHPUnit/Integration/TrackGoals_AllowMultipleConversionsPerVisitTest.php
+++ b/tests/PHPUnit/Integration/TrackGoals_AllowMultipleConversionsPerVisitTest.php
@@ -12,7 +12,7 @@
*/
class Test_Piwik_Integration_TrackGoals_AllowMultipleConversionsPerVisit extends IntegrationTestCase
{
- public static $fixture = null;
+ public static $fixture = null;
/**
* @dataProvider getApiForTesting
@@ -21,7 +21,7 @@ class Test_Piwik_Integration_TrackGoals_AllowMultipleConversionsPerVisit extends
*/
public function testApi($api, $params)
{
- $this->runApiTests($api, $params);
+ $this->runApiTests($api, $params);
}
/**
@@ -58,4 +58,4 @@ class Test_Piwik_Integration_TrackGoals_AllowMultipleConversionsPerVisit extends
}
Test_Piwik_Integration_TrackGoals_AllowMultipleConversionsPerVisit::$fixture
- = new Piwik_Test_Fixture_SomeVisitsAllConversions();
+ = new Piwik_Test_Fixture_SomeVisitsAllConversions();
diff --git a/tests/PHPUnit/Integration/TrackingAPI_SetVisitorIdTest.php b/tests/PHPUnit/Integration/TrackingAPI_SetVisitorIdTest.php
index 0bce1fbad2..5ed1db98f0 100644
--- a/tests/PHPUnit/Integration/TrackingAPI_SetVisitorIdTest.php
+++ b/tests/PHPUnit/Integration/TrackingAPI_SetVisitorIdTest.php
@@ -12,7 +12,7 @@
*/
class Test_Piwik_Integration_TrackingAPI_SetVisitorId extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
public function setUp()
{
@@ -38,9 +38,9 @@ class Test_Piwik_Integration_TrackingAPI_SetVisitorId extends IntegrationTestCas
{
return array(
// test hideColumns && showColumns parameters
- array('VisitsSummary.get', array('idSite' => self::$fixture->idSite,
- 'date' => self::$fixture->dateTime,
- 'periods' => 'day',
+ array('VisitsSummary.get', array('idSite' => self::$fixture->idSite,
+ 'date' => self::$fixture->dateTime,
+ 'periods' => 'day',
'testSuffix' => '',
))
);
diff --git a/tests/PHPUnit/Integration/TransitionsTest.php b/tests/PHPUnit/Integration/TransitionsTest.php
index 1d9f8375e2..0523feec4c 100644
--- a/tests/PHPUnit/Integration/TransitionsTest.php
+++ b/tests/PHPUnit/Integration/TransitionsTest.php
@@ -5,36 +5,36 @@
*/
class Test_Piwik_Integration_Transitions extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
-
- /**
- * @dataProvider getApiForTesting
- * @group Integration
- * @group Transitions
- */
- public function testApi($api, $params)
- {
- $this->runApiTests($api, $params);
- }
-
- public function getApiForTesting()
- {
- $return = array();
- $return[] = array('Transitions.getTransitionsForPageUrl', array(
- 'idSite' => self::$fixture->idSite,
- 'date' => self::$fixture->dateTime,
- 'otherRequestParameters' => array(
- 'pageUrl' => 'http://example.org/page/one.html',
- 'limitBeforeGrouping' => 2
- )
+ public static $fixture = null; // initialized below class definition
+
+ /**
+ * @dataProvider getApiForTesting
+ * @group Integration
+ * @group Transitions
+ */
+ public function testApi($api, $params)
+ {
+ $this->runApiTests($api, $params);
+ }
+
+ public function getApiForTesting()
+ {
+ $return = array();
+ $return[] = array('Transitions.getTransitionsForPageUrl', array(
+ 'idSite' => self::$fixture->idSite,
+ 'date' => self::$fixture->dateTime,
+ 'otherRequestParameters' => array(
+ 'pageUrl' => 'http://example.org/page/one.html',
+ 'limitBeforeGrouping' => 2
+ )
));
- return $return;
- }
-
- public function getOutputPrefix()
- {
- return 'Transitions';
- }
+ return $return;
+ }
+
+ public function getOutputPrefix()
+ {
+ return 'Transitions';
+ }
}
Test_Piwik_Integration_Transitions::$fixture = new Test_Piwik_Fixture_SomeVisitsManyPageviewsWithTransitions();
diff --git a/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDaysTest.php b/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDaysTest.php
index a9c89a6a8e..5baf9b0aba 100755
--- a/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDaysTest.php
+++ b/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDaysTest.php
@@ -17,16 +17,16 @@
*/
class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
- /**
- * @group Integration
- * @group TwoVisitors_TwoWebsites_DifferentDays
- */
- public function testImagesIncludedInTests()
- {
- $this->alertWhenImagesExcludedFromTests();
- }
+ /**
+ * @group Integration
+ * @group TwoVisitors_TwoWebsites_DifferentDays
+ */
+ public function testImagesIncludedInTests()
+ {
+ $this->alertWhenImagesExcludedFromTests();
+ }
/**
* @dataProvider getApiForTesting
@@ -41,22 +41,22 @@ class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays extends Integ
protected function getApiToCall()
{
return array('VisitFrequency.get',
- 'VisitsSummary.get',
- 'Referers.getWebsites',
- 'Actions.getPageUrls',
- 'Actions.getPageTitles',
- 'Actions.getOutlinks',
- 'Actions.getPageTitle',
- 'Actions.getPageUrl',
- 'VisitorInterest.getNumberOfVisitsByDaysSinceLast');
+ 'VisitsSummary.get',
+ 'Referers.getWebsites',
+ 'Actions.getPageUrls',
+ 'Actions.getPageTitles',
+ 'Actions.getOutlinks',
+ 'Actions.getPageTitle',
+ 'Actions.getPageUrl',
+ 'VisitorInterest.getNumberOfVisitsByDaysSinceLast');
}
public function getApiForTesting()
{
- $idSite1 = self::$fixture->idSite1;
- $dateTime = self::$fixture->dateTime;
-
- $apiToCall = $this->getApiToCall();
+ $idSite1 = self::$fixture->idSite1;
+ $dateTime = self::$fixture->dateTime;
+
+ $apiToCall = $this->getApiToCall();
$singlePeriodApi = array('VisitsSummary.get', 'Goals.get');
$periods = array('day', 'week', 'month', 'year');
@@ -109,5 +109,5 @@ class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays extends Integ
}
Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays::$fixture =
- new Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays();
+ new Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays();
diff --git a/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisabledTest.php b/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisabledTest.php
index 4ec0475f66..89e28bf0b0 100755
--- a/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisabledTest.php
+++ b/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisabledTest.php
@@ -11,7 +11,7 @@
*/
class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisabled extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -25,8 +25,8 @@ class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisa
public function getApiForTesting()
{
- $dateTime = self::$fixture->dateTime;
-
+ $dateTime = self::$fixture->dateTime;
+
$periods = array('day', 'week', 'month', 'year');
return array(
@@ -60,6 +60,6 @@ class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisa
}
Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisabled::$fixture =
- new Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays();
+ new Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays();
Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_ArchivingDisabled::$fixture->allowConversions = true;
diff --git a/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDays_ConversionsTest.php b/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDays_ConversionsTest.php
index f1d13db228..711145107d 100755
--- a/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDays_ConversionsTest.php
+++ b/tests/PHPUnit/Integration/TwoVisitors_TwoWebsites_DifferentDays_ConversionsTest.php
@@ -14,7 +14,7 @@ require_once 'Goals/Goals.php';
*/
class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_Conversions extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -26,20 +26,20 @@ class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_Conversions e
$this->runApiTests($api, $params);
}
- public function getApiToCall()
- {
- return array('Goals.getDaysToConversion', 'MultiSites.getAll');
- }
-
- public function getApiForTesting()
- {
- $dateTime = self::$fixture->dateTime;
- $idSite1 = self::$fixture->idSite1;
-
- // NOTE: copied from TwoVisitors_TwoWebsites_DifferentDays (including the test or inheriting means
- // the test will get run by phpunit, even when we only want to run this one. should be put into
- // non-test class later.)
- $apiToCall = $this->getApiToCall();
+ public function getApiToCall()
+ {
+ return array('Goals.getDaysToConversion', 'MultiSites.getAll');
+ }
+
+ public function getApiForTesting()
+ {
+ $dateTime = self::$fixture->dateTime;
+ $idSite1 = self::$fixture->idSite1;
+
+ // NOTE: copied from TwoVisitors_TwoWebsites_DifferentDays (including the test or inheriting means
+ // the test will get run by phpunit, even when we only want to run this one. should be put into
+ // non-test class later.)
+ $apiToCall = $this->getApiToCall();
$singlePeriodApi = array('VisitsSummary.get', 'Goals.get');
$periods = array('day', 'week', 'month', 'year');
@@ -85,7 +85,7 @@ class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_Conversions e
// Tests that getting a visits summary metric (nb_visits) & a Goal's metric (Goal_revenue)
// at the same time works.
$dateTime = '2010-01-03,2010-01-06';
- $columns = 'nb_visits,' . Piwik_Goals::getRecordName('conversion_rate');
+ $columns = 'nb_visits,' . Piwik_Goals::getRecordName('conversion_rate');
$result[] = array(
'VisitsSummary.get', array('idSite' => 'all', 'date' => $dateTime, 'periods' => 'range',
@@ -103,6 +103,6 @@ class Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_Conversions e
}
Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_Conversions::$fixture =
- new Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays();
+ new Test_Piwik_Fixture_TwoSitesTwoVisitorsDifferentDays();
Test_Piwik_Integration_TwoVisitors_TwoWebsites_DifferentDays_Conversions::$fixture->allowConversions = true;
diff --git a/tests/PHPUnit/Integration/TwoVisitsWithCustomVariablesTest.php b/tests/PHPUnit/Integration/TwoVisitsWithCustomVariablesTest.php
index 188707de0a..f1be9716b3 100755
--- a/tests/PHPUnit/Integration/TwoVisitsWithCustomVariablesTest.php
+++ b/tests/PHPUnit/Integration/TwoVisitsWithCustomVariablesTest.php
@@ -11,13 +11,13 @@
*/
class Test_Piwik_Integration_TwoVisitsWithCustomVariables extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $dateTime = self::$fixture->dateTime;
-
+ $idSite = self::$fixture->idSite;
+ $dateTime = self::$fixture->dateTime;
+
$apiToCall = array('VisitsSummary.get', 'CustomVariables.getCustomVariables');
$return = array(
@@ -25,15 +25,15 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables extends IntegrationTes
'date' => $dateTime,
'periods' => array('day', 'week'),
'setDateLastN' => true)),
-
- // test getProcessedReport w/ custom variables subtable
- array('API.getProcessedReport', array('idSite' => $idSite,
- 'date' => $dateTime,
- 'periods' => 'day',
- 'apiModule' => 'CustomVariables',
- 'apiAction' => 'getCustomVariablesValuesFromNameId',
- 'supertableApi' => 'CustomVariables.getCustomVariables',
- 'testSuffix' => '__subtable' )),
+
+ // test getProcessedReport w/ custom variables subtable
+ array('API.getProcessedReport', array('idSite' => $idSite,
+ 'date' => $dateTime,
+ 'periods' => 'day',
+ 'apiModule' => 'CustomVariables',
+ 'apiAction' => 'getCustomVariablesValuesFromNameId',
+ 'supertableApi' => 'CustomVariables.getCustomVariables',
+ 'testSuffix' => '__subtable')),
);
return $return;
diff --git a/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentContainsTest.php b/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentContainsTest.php
index 585c10bbdb..648711e9bb 100755
--- a/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentContainsTest.php
+++ b/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentContainsTest.php
@@ -11,7 +11,7 @@
*/
class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentContains extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
public function getOutputPrefix()
{
@@ -30,12 +30,12 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentContains extend
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $dateTime = self::$fixture->dateTime;
-
+ $idSite = self::$fixture->idSite;
+ $dateTime = self::$fixture->dateTime;
+
$return = array();
- $api = array('Actions.getPageUrls', 'Actions.getPageTitles', 'VisitsSummary.get');
+ $api = array('Actions.getPageUrls', 'Actions.getPageTitles', 'VisitsSummary.get');
$segmentsToTest = array(
// array( SegmentString , TestSuffix , Array of API to test)
array("pageTitle=@*_)%", '_SegmentPageTitleContainsStrangeCharacters', array('Actions.getPageTitles', 'VisitsSummary.get')),
@@ -48,10 +48,10 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentContains extend
foreach ($segmentsToTest as $segment) {
// Also test "Page URL / Page title CONTAINS string" feature
$return[] = array($segment[2],
- array('idSite' => $idSite, 'date' => $dateTime, 'periods' => array('day'),
- 'setDateLastN' => false,
- 'segment' => $segment[0],
- 'testSuffix' => $segment[1])
+ array('idSite' => $idSite, 'date' => $dateTime, 'periods' => array('day'),
+ 'setDateLastN' => false,
+ 'segment' => $segment[0],
+ 'testSuffix' => $segment[1])
);
}
return $return;
@@ -59,6 +59,6 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentContains extend
}
Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentContains::$fixture
- = new Test_Piwik_Fixture_TwoVisitsWithCustomVariables();
+ = new Test_Piwik_Fixture_TwoVisitsWithCustomVariables();
Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentContains::$fixture->doExtraQuoteTests = false;
diff --git a/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalDataTest.php b/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalDataTest.php
index 26b136dff7..f217be8d0b 100755
--- a/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalDataTest.php
+++ b/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalDataTest.php
@@ -8,7 +8,7 @@
class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalData extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -27,7 +27,7 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoal
// Segment matching ALL
// + adding DOES NOT CONTAIN segment always matched, to test this particular operator
$resolution = self::$fixture->resolutionWidthToUse . 'x' . self::$fixture->resolutionHeightToUse;
- $segment = 'resolution==' . $resolution . ';customVariableName1!@randomvalue does not exist';
+ $segment = 'resolution==' . $resolution . ';customVariableName1!@randomvalue does not exist';
return array(
array($apiToCall, array('idSite' => 'all',
@@ -45,6 +45,6 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoal
}
Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalData::$fixture
- = new Test_Piwik_Fixture_TwoVisitsWithCustomVariables();
+ = new Test_Piwik_Fixture_TwoVisitsWithCustomVariables();
Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchALL_NoGoalData::$fixture->doExtraQuoteTests = false;
diff --git a/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchNONETest.php b/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchNONETest.php
index 000ed8bc96..0d721f4c7b 100755
--- a/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchNONETest.php
+++ b/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchNONETest.php
@@ -11,7 +11,7 @@
*/
class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchNONE extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -39,7 +39,7 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchNONE exten
public function getSegmentToTest()
{
// Segment matching NONE
- $segments = Piwik_API_API::getInstance()->getSegmentsMetadata(self::$fixture->idSite);
+ $segments = Piwik_API_API::getInstance()->getSegmentsMetadata(self::$fixture->idSite);
$segmentExpression = array();
$seenVisitorId = false;
@@ -47,7 +47,7 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchNONE exten
$value = 'campaign';
if ($segment['segment'] == 'visitorId') {
$seenVisitorId = true;
- $value = '34c31e04394bdc63';
+ $value = '34c31e04394bdc63';
}
if ($segment['segment'] == 'visitEcommerceStatus') {
$value = 'none';
@@ -71,6 +71,6 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchNONE exten
}
Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchNONE::$fixture
- = new Test_Piwik_Fixture_TwoVisitsWithCustomVariables();
+ = new Test_Piwik_Fixture_TwoVisitsWithCustomVariables();
Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchNONE::$fixture->doExtraQuoteTests = false;
diff --git a/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchVisitorTypeTest.php b/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchVisitorTypeTest.php
index f9a26a3276..17bc92db2d 100755
--- a/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchVisitorTypeTest.php
+++ b/tests/PHPUnit/Integration/TwoVisitsWithCustomVariables_SegmentMatchVisitorTypeTest.php
@@ -11,7 +11,7 @@
*/
class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchVisitorType extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -27,7 +27,7 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchVisitorTyp
{
// Segment matching some
$segments = array('customVariableName1==VisitorType;customVariableValue1==LoggedIn',
- 'customVariableName1==VisitorType;customVariableValue1=@LoggedI');
+ 'customVariableName1==VisitorType;customVariableValue1=@LoggedI');
$apiToCall = array('Referers.getKeywords', 'CustomVariables.getCustomVariables', 'VisitsSummary.get');
@@ -85,7 +85,7 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchVisitorTyp
'archive_numeric_2009_12' => (6 + 2 + 3) * 2,
);
foreach ($tests as $table => $expectedRows) {
- $sql = "SELECT count(*) FROM " . Piwik_Common::prefixTable($table);
+ $sql = "SELECT count(*) FROM " . Piwik_Common::prefixTable($table);
$countBlobs = Zend_Registry::get('db')->fetchOne($sql);
$this->assertEquals($expectedRows, $countBlobs, "$table: %s");
}
@@ -98,6 +98,6 @@ class Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchVisitorTyp
}
Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchVisitorType::$fixture
- = new Test_Piwik_Fixture_TwoVisitsWithCustomVariables();
+ = new Test_Piwik_Fixture_TwoVisitsWithCustomVariables();
Test_Piwik_Integration_TwoVisitsWithCustomVariables_SegmentMatchVisitorType::$fixture->doExtraQuoteTests = false;
diff --git a/tests/PHPUnit/Integration/UrlNormalizationTest.php b/tests/PHPUnit/Integration/UrlNormalizationTest.php
index 9a7678446f..690a2a57ba 100644
--- a/tests/PHPUnit/Integration/UrlNormalizationTest.php
+++ b/tests/PHPUnit/Integration/UrlNormalizationTest.php
@@ -4,7 +4,7 @@
*/
class Test_Piwik_Integration_UrlNormalization extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -18,10 +18,10 @@ class Test_Piwik_Integration_UrlNormalization extends IntegrationTestCase
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $dateTime = self::$fixture->dateTime;
-
- $return = array();
+ $idSite = self::$fixture->idSite;
+ $dateTime = self::$fixture->dateTime;
+
+ $return = array();
$return[] = array('Actions.getPageUrls', array(
'testSuffix' => '_urls',
'idSite' => $idSite,
@@ -32,7 +32,7 @@ class Test_Piwik_Integration_UrlNormalization extends IntegrationTestCase
'idSite' => $idSite,
'date' => $dateTime,
));
-
+
$return[] = array('Actions.getPageUrls', array(
'testSuffix' => '_pagesSegmented',
'idSite' => $idSite,
@@ -83,15 +83,15 @@ class Test_Piwik_Integration_UrlNormalization extends IntegrationTestCase
*/
public function testCheckPostConditions()
{
- $sql = "SELECT count(*) FROM " . Piwik_Common::prefixTable('log_action');
- $count = Zend_Registry::get('db')->fetchOne($sql);
+ $sql = "SELECT count(*) FROM " . Piwik_Common::prefixTable('log_action');
+ $count = Zend_Registry::get('db')->fetchOne($sql);
$expected = 9; // 4 urls + 5 titles
$this->assertEquals($expected, $count, "only $expected actions expected");
- $sql = "SELECT name, url_prefix FROM " . Piwik_Common::prefixTable('log_action')
+ $sql = "SELECT name, url_prefix FROM " . Piwik_Common::prefixTable('log_action')
. " WHERE type = " . Piwik_Tracker_Action::TYPE_ACTION_URL
. " ORDER BY idaction ASC";
- $urls = Zend_Registry::get('db')->fetchAll($sql);
+ $urls = Zend_Registry::get('db')->fetchAll($sql);
$expected = array(
array('name' => 'example.org/foo/bar.html', 'url_prefix' => 0),
array('name' => 'example.org/foo/bar2.html', 'url_prefix' => 3),
diff --git a/tests/PHPUnit/Integration/VisitsInPast_InvalidateOldReportsTest.php b/tests/PHPUnit/Integration/VisitsInPast_InvalidateOldReportsTest.php
index 851137cbb3..d93876413a 100644
--- a/tests/PHPUnit/Integration/VisitsInPast_InvalidateOldReportsTest.php
+++ b/tests/PHPUnit/Integration/VisitsInPast_InvalidateOldReportsTest.php
@@ -11,7 +11,7 @@
*/
class Test_Piwik_Integration_VisitsInPast_InvalidateOldReports extends IntegrationTestCase
{
- public static $fixture = null; // initialized below class definition
+ public static $fixture = null; // initialized below class definition
/**
* @dataProvider getApiForTesting
@@ -28,11 +28,11 @@ class Test_Piwik_Integration_VisitsInPast_InvalidateOldReports extends Integrati
*/
public function getApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $idSite2 = self::$fixture->idSite2;
- $dateTimeDateInPastWebsite1 = self::$fixture->dateTimeDateInPastWebsite1;
- $dateTimeDateInPastWebsite2 = self::$fixture->dateTimeDateInPastWebsite2;
-
+ $idSite = self::$fixture->idSite;
+ $idSite2 = self::$fixture->idSite2;
+ $dateTimeDateInPastWebsite1 = self::$fixture->dateTimeDateInPastWebsite1;
+ $dateTimeDateInPastWebsite2 = self::$fixture->dateTimeDateInPastWebsite2;
+
// We test a typical Numeric and a Recursive blob reports
$apiToCall = array('VisitsSummary.get', 'Actions.getPageUrls');
@@ -42,13 +42,13 @@ class Test_Piwik_Integration_VisitsInPast_InvalidateOldReports extends Integrati
// Build tests for the 2 websites
return array(
array($apiToCall, array('idSite' => $idSite,
- 'testSuffix' => 'Website'.$idSite.'_OldReportsShouldNotAppear',
+ 'testSuffix' => 'Website' . $idSite . '_OldReportsShouldNotAppear',
'date' => $dateTimeDateInPastWebsite1,
'periods' => 'month',
'setDateLastN' => 4, // 4months ahead
'otherRequestParameters' => array('expanded' => 1))),
array($apiToCall, array('idSite' => $idSite2,
- 'testSuffix' => 'Website'.$idSite2.'_OldReportsShouldNotAppear',
+ 'testSuffix' => 'Website' . $idSite2 . '_OldReportsShouldNotAppear',
'date' => $dateTimeDateInPastWebsite2,
'periods' => 'month',
'setDateLastN' => 4, // 4months ahead
@@ -64,9 +64,9 @@ class Test_Piwik_Integration_VisitsInPast_InvalidateOldReports extends Integrati
*/
public function testAnotherApi($api, $params)
{
- $idSite = self::$fixture->idSite;
- $idSite2 = self::$fixture->idSite2;
-
+ $idSite = self::$fixture->idSite;
+ $idSite2 = self::$fixture->idSite2;
+
// 1) Invalidate old reports for the 2 websites
// Test invalidate 1 date only
$r = new Piwik_API_Request("module=API&method=CoreAdminHome.invalidateArchivedReports&idSites=4,5,6,55,-1,s',1&dates=2010-01-03");
@@ -91,11 +91,11 @@ class Test_Piwik_Integration_VisitsInPast_InvalidateOldReports extends Integrati
*/
public function getAnotherApiForTesting()
{
- $idSite = self::$fixture->idSite;
- $idSite2 = self::$fixture->idSite2;
- $dateTimeDateInPastWebsite1 = self::$fixture->dateTimeDateInPastWebsite1;
- $dateTimeDateInPastWebsite2 = self::$fixture->dateTimeDateInPastWebsite2;
-
+ $idSite = self::$fixture->idSite;
+ $idSite2 = self::$fixture->idSite2;
+ $dateTimeDateInPastWebsite1 = self::$fixture->dateTimeDateInPastWebsite1;
+ $dateTimeDateInPastWebsite2 = self::$fixture->dateTimeDateInPastWebsite2;
+
$apiToCall = array('VisitsSummary.get', 'Actions.getPageUrls');
return array(
diff --git a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml
index e495613069..31af17abfe 100644
--- a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml
@@ -92,10 +92,10 @@
<result prettyDate="Monday 4 January 2010" />
<result prettyDate="Tuesday 5 January 2010">
<row>
- <idsubdatatable>72</idsubdatatable>
+
</row>
<row>
- <idsubdatatable>71</idsubdatatable>
+
</row>
</result>
<result prettyDate="Wednesday 6 January 2010" />
diff --git a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__PDFReports.generateReport_month.original.html b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__PDFReports.generateReport_month.original.html
index 2b5020a907..7ca0bbd340 100644
--- a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__PDFReports.generateReport_month.original.html
+++ b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__PDFReports.generateReport_month.original.html
@@ -1,10 +1,10 @@
<html>
<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body style="color: rgb(68,68,68);">
<a name="reportTop"/>
-<a target="_blank" href=""><img title="Go to Piwik" border="0" alt="Piwik" src='themes/default/images/logo-header.png' /></a>
+<a target="_blank" href=""><img title="Go to Piwik" border="0" alt="Piwik" src='themes/default/images/logo-header.png'/></a>
<h1 style="color: rgb(126,115,99); font-size: 11pt;">
Website Site 1
</h1>
@@ -245,7 +245,7 @@ Mobile vs Desktop
Browser language
</a>
</li>
-</ul><a name ="MultiSites_getAll"/>
+</ul><a name="MultiSites_getAll"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
All Websites dashboard
</h2>
@@ -334,7 +334,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitsSummary_get"/>
+</a><a name="VisitsSummary_get"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits Summary
</h2>
@@ -409,7 +409,7 @@ Avg. Visit Duration (in seconds)
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitTime_getVisitInformationPerServerTime"/>
+</a><a name="VisitTime_getVisitInformationPerServerTime"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits by Server Time
</h2>
@@ -995,7 +995,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitTime_getVisitInformationPerLocalTime"/>
+</a><a name="VisitTime_getVisitInformationPerLocalTime"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits by Local Time
</h2>
@@ -1581,7 +1581,7 @@ Visits by Local Time
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitTime_getByDayOfWeek"/>
+</a><a name="VisitTime_getByDayOfWeek"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits by Day of Week
</h2>
@@ -1776,7 +1776,7 @@ Sunday
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_get"/>
+</a><a name="Actions_get"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Actions - Main metrics
</h2>
@@ -1859,7 +1859,7 @@ Unique Keywords
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getPageUrls"/>
+</a><a name="Actions_getPageUrls"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Page URLs
</h2>
@@ -1993,7 +1993,7 @@ Page URL not defined
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getEntryPageUrls"/>
+</a><a name="Actions_getEntryPageUrls"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Entry pages
</h2>
@@ -2050,7 +2050,7 @@ Entry pages
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getExitPageUrls"/>
+</a><a name="Actions_getExitPageUrls"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Exit pages
</h2>
@@ -2107,7 +2107,7 @@ Exit pages
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getPageTitles"/>
+</a><a name="Actions_getPageTitles"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Page titles
</h2>
@@ -2233,7 +2233,7 @@ Page Name not defined
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getEntryPageTitles"/>
+</a><a name="Actions_getEntryPageTitles"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Entry page titles
</h2>
@@ -2286,7 +2286,7 @@ second visitor
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getExitPageTitles"/>
+</a><a name="Actions_getExitPageTitles"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Exit page titles
</h2>
@@ -2339,15 +2339,15 @@ Checkout
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getOutlinks"/>
+</a><a name="Actions_getOutlinks"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Outlinks
</h2>
-There is no data for this report.<a name ="Actions_getDownloads"/>
+There is no data for this report.<a name="Actions_getDownloads"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Downloads
</h2>
-There is no data for this report.<a name ="Referers_getRefererType"/>
+There is no data for this report.<a name="Referers_getRefererType"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Referrer Type
</h2>
@@ -2427,7 +2427,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Referers_getAll"/>
+</a><a name="Referers_getAll"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
All Referrers
</h2>
@@ -2484,11 +2484,11 @@ referer.com
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Referers_getKeywords"/>
+</a><a name="Referers_getKeywords"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Keywords
</h2>
-There is no data for this report.<a name ="Referers_getWebsites"/>
+There is no data for this report.<a name="Referers_getWebsites"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Websites
</h2>
@@ -2545,19 +2545,19 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Referers_getSearchEngines"/>
+</a><a name="Referers_getSearchEngines"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Search Engines
</h2>
-There is no data for this report.<a name ="Referers_getCampaigns"/>
+There is no data for this report.<a name="Referers_getCampaigns"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Campaigns
</h2>
-There is no data for this report.<a name ="Referers_getSocials"/>
+There is no data for this report.<a name="Referers_getSocials"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Social Networks
</h2>
-There is no data for this report.<a name ="Goals_get"/>
+There is no data for this report.<a name="Goals_get"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Goals
</h2>
@@ -2608,15 +2608,15 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getVisitsUntilConversion"/>
+</a><a name="Goals_getVisitsUntilConversion"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits to Conversion
</h2>
-There is no data for this report.<a name ="Goals_getDaysToConversion"/>
+There is no data for this report.<a name="Goals_getDaysToConversion"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Days to Conversion
</h2>
-There is no data for this report.<a name ="UserCountry_getCountry"/>
+There is no data for this report.<a name="UserCountry_getCountry"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Country
</h2>
@@ -2647,7 +2647,8 @@ Country
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserCountry/flags/fr.png'>&nbsp;
+<img src='plugins/UserCountry/flags/fr.png'>
+&nbsp;
France
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -2674,7 +2675,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserCountry_getContinent"/>
+</a><a name="UserCountry_getContinent"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Continent
</h2>
@@ -2731,7 +2732,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserCountry_getRegion"/>
+</a><a name="UserCountry_getRegion"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Region
</h2>
@@ -2762,7 +2763,8 @@ Region
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserCountry/flags/xx.png'>&nbsp;
+<img src='plugins/UserCountry/flags/xx.png'>
+&nbsp;
Unknown
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -2789,7 +2791,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserCountry_getCity"/>
+</a><a name="UserCountry_getCity"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
City
</h2>
@@ -2820,7 +2822,8 @@ City
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserCountry/flags/xx.png'>&nbsp;
+<img src='plugins/UserCountry/flags/xx.png'>
+&nbsp;
Unknown
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -2847,11 +2850,11 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="CustomVariables_getCustomVariables"/>
+</a><a name="CustomVariables_getCustomVariables"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Custom Variables
</h2>
-There is no data for this report.<a name ="VisitorInterest_getNumberOfVisitsPerVisitDuration"/>
+There is no data for this report.<a name="VisitorInterest_getNumberOfVisitsPerVisitDuration"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Length of Visits
</h2>
@@ -2950,7 +2953,7 @@ Length of Visits
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitorInterest_getNumberOfVisitsPerPage"/>
+</a><a name="VisitorInterest_getNumberOfVisitsPerPage"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Pages per Visit
</h2>
@@ -3049,7 +3052,7 @@ Pages per Visit
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitorInterest_getNumberOfVisitsByVisitCount"/>
+</a><a name="VisitorInterest_getNumberOfVisitsByVisitCount"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits by Visit Number
</h2>
@@ -3225,7 +3228,7 @@ Visits by Visit Number
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitorInterest_getNumberOfVisitsByDaysSinceLast"/>
+</a><a name="VisitorInterest_getNumberOfVisitsByDaysSinceLast"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits by days since last visit
</h2>
@@ -3364,7 +3367,7 @@ New visits
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitFrequency_get"/>
+</a><a name="VisitFrequency_get"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Returning Visits
</h2>
@@ -3431,7 +3434,7 @@ Unique returning visitors
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Provider_getProvider"/>
+</a><a name="Provider_getProvider"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Provider
</h2>
@@ -3490,7 +3493,7 @@ Unknown
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getResolution"/>
+</a><a name="UserSettings_getResolution"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Screen Resolution
</h2>
@@ -3570,7 +3573,7 @@ Screen Resolution
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getBrowser"/>
+</a><a name="UserSettings_getBrowser"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visitor Browser
</h2>
@@ -3601,7 +3604,8 @@ Visitor Browser
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/browsers/OP.gif'>&nbsp;
+<img src='plugins/UserSettings/images/browsers/OP.gif'>
+&nbsp;
Opera
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3625,7 +3629,8 @@ Opera
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/browsers/FF.gif'>&nbsp;
+<img src='plugins/UserSettings/images/browsers/FF.gif'>
+&nbsp;
Firefox
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3652,7 +3657,7 @@ Firefox
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getBrowserVersion"/>
+</a><a name="UserSettings_getBrowserVersion"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Browser Version
</h2>
@@ -3683,7 +3688,8 @@ Browser Version
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/browsers/OP.gif'>&nbsp;
+<img src='plugins/UserSettings/images/browsers/OP.gif'>
+&nbsp;
Opera 9.63
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3707,7 +3713,8 @@ Opera 9.63
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/browsers/FF.gif'>&nbsp;
+<img src='plugins/UserSettings/images/browsers/FF.gif'>
+&nbsp;
Firefox 3.6
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3734,7 +3741,7 @@ Firefox 3.6
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getBrowserType"/>
+</a><a name="UserSettings_getBrowserType"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Browser Family
</h2>
@@ -3814,7 +3821,7 @@ Gecko (Firefox)
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getPlugin"/>
+</a><a name="UserSettings_getPlugin"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Browser Plugins
</h2>
@@ -3833,7 +3840,8 @@ Browser Plugins
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/cookie.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/cookie.gif'>
+&nbsp;
Cookie
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3845,7 +3853,8 @@ Cookie
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/flash.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/flash.gif'>
+&nbsp;
Flash
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3857,7 +3866,8 @@ Flash
</tr>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/java.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/java.gif'>
+&nbsp;
Java
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3869,7 +3879,8 @@ Java
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/director.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/director.gif'>
+&nbsp;
Director
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3881,7 +3892,8 @@ Director
</tr>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/gears.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/gears.gif'>
+&nbsp;
Gears
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3893,7 +3905,8 @@ Gears
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/pdf.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/pdf.gif'>
+&nbsp;
Pdf
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3905,7 +3918,8 @@ Pdf
</tr>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/quicktime.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/quicktime.gif'>
+&nbsp;
Quicktime
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3917,7 +3931,8 @@ Quicktime
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/realplayer.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/realplayer.gif'>
+&nbsp;
Realplayer
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3929,7 +3944,8 @@ Realplayer
</tr>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/silverlight.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/silverlight.gif'>
+&nbsp;
Silverlight
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3941,7 +3957,8 @@ Silverlight
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/windowsmedia.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/windowsmedia.gif'>
+&nbsp;
Windowsmedia
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -3956,7 +3973,7 @@ Windowsmedia
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getWideScreen"/>
+</a><a name="UserSettings_getWideScreen"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Normal / Widescreen
</h2>
@@ -3987,7 +4004,8 @@ Normal / Widescreen
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/screens/dual.gif'>&nbsp;
+<img src='plugins/UserSettings/images/screens/dual.gif'>
+&nbsp;
Dual
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -4011,7 +4029,8 @@ Dual
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/screens/normal.gif'>&nbsp;
+<img src='plugins/UserSettings/images/screens/normal.gif'>
+&nbsp;
Normal
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -4038,7 +4057,7 @@ Normal
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getOS"/>
+</a><a name="UserSettings_getOS"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Operating System
</h2>
@@ -4069,7 +4088,8 @@ Operating System
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/os/WXP.gif'>&nbsp;
+<img src='plugins/UserSettings/images/os/WXP.gif'>
+&nbsp;
Windows XP
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -4096,7 +4116,7 @@ Windows XP
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getConfiguration"/>
+</a><a name="UserSettings_getConfiguration"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visitor Configuration
</h2>
@@ -4176,7 +4196,7 @@ Windows XP / Firefox / 1024x768
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getOSFamily"/>
+</a><a name="UserSettings_getOSFamily"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Operating System Family
</h2>
@@ -4207,7 +4227,8 @@ Operating System Family
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/os/WXP.gif'>&nbsp;
+<img src='plugins/UserSettings/images/os/WXP.gif'>
+&nbsp;
Windows
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -4234,7 +4255,7 @@ Windows
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getMobileVsDesktop"/>
+</a><a name="UserSettings_getMobileVsDesktop"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Mobile vs Desktop
</h2>
@@ -4265,7 +4286,8 @@ Mobile vs Desktop
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/screens/normal.gif'>&nbsp;
+<img src='plugins/UserSettings/images/screens/normal.gif'>
+&nbsp;
Desktop
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -4289,7 +4311,8 @@ Desktop
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/screens/mobile.gif'>&nbsp;
+<img src='plugins/UserSettings/images/screens/mobile.gif'>
+&nbsp;
Mobile
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -4316,7 +4339,7 @@ Mobile
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getLanguage"/>
+</a><a name="UserSettings_getLanguage"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Browser language
</h2>
diff --git a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__PDFReports.generateReport_week.original.html b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__PDFReports.generateReport_week.original.html
index 8b317600a8..aaa75b3f2a 100644
--- a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__PDFReports.generateReport_week.original.html
+++ b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__PDFReports.generateReport_week.original.html
@@ -1,10 +1,10 @@
<html>
<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body style="color: rgb(68,68,68);">
<a name="reportTop"/>
-<a target="_blank" href=""><img title="Go to Piwik" border="0" alt="Piwik" src='themes/default/images/logo-header.png' /></a>
+<a target="_blank" href=""><img title="Go to Piwik" border="0" alt="Piwik" src='themes/default/images/logo-header.png'/></a>
<h1 style="color: rgb(126,115,99); font-size: 11pt;">
Website Piwik test
</h1>
@@ -305,7 +305,7 @@ Mobile vs Desktop
Browser language
</a>
</li>
-</ul><a name ="MultiSites_getAll"/>
+</ul><a name="MultiSites_getAll"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
All Websites dashboard
</h2>
@@ -394,7 +394,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitsSummary_get"/>
+</a><a name="VisitsSummary_get"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits Summary
</h2>
@@ -469,7 +469,7 @@ Avg. Visit Duration (in seconds)
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitTime_getVisitInformationPerServerTime"/>
+</a><a name="VisitTime_getVisitInformationPerServerTime"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits by Server Time
</h2>
@@ -1055,7 +1055,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitTime_getVisitInformationPerLocalTime"/>
+</a><a name="VisitTime_getVisitInformationPerLocalTime"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits by Local Time
</h2>
@@ -1641,7 +1641,7 @@ Visits by Local Time
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitTime_getByDayOfWeek"/>
+</a><a name="VisitTime_getByDayOfWeek"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits by Day of Week
</h2>
@@ -1836,7 +1836,7 @@ Sunday
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_get_idGoal--ecommerceOrder"/>
+</a><a name="Goals_get_idGoal--ecommerceOrder"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Ecommerce Orders
</h2>
@@ -1935,7 +1935,7 @@ $ 3337.78
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getVisitsUntilConversion_idGoal--ecommerceOrder"/>
+</a><a name="Goals_getVisitsUntilConversion_idGoal--ecommerceOrder"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Ecommerce Orders - Visits to Conversion
</h2>
@@ -2058,7 +2058,7 @@ Ecommerce Orders - Visits to Conversion
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getDaysToConversion_idGoal--ecommerceOrder"/>
+</a><a name="Goals_getDaysToConversion_idGoal--ecommerceOrder"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Ecommerce Orders - Days to Conversion
</h2>
@@ -2189,7 +2189,7 @@ Ecommerce Orders - Days to Conversion
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_get_idGoal--ecommerceAbandonedCart"/>
+</a><a name="Goals_get_idGoal--ecommerceAbandonedCart"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Abandoned Carts
</h2>
@@ -2240,7 +2240,7 @@ Products left in cart
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getVisitsUntilConversion_idGoal--ecommerceAbandonedCart"/>
+</a><a name="Goals_getVisitsUntilConversion_idGoal--ecommerceAbandonedCart"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Abandoned Carts - Visits to Conversion
</h2>
@@ -2363,7 +2363,7 @@ Abandoned Carts - Visits to Conversion
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getDaysToConversion_idGoal--ecommerceAbandonedCart"/>
+</a><a name="Goals_getDaysToConversion_idGoal--ecommerceAbandonedCart"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Abandoned Carts - Days to Conversion
</h2>
@@ -2494,7 +2494,7 @@ Abandoned Carts - Days to Conversion
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getItemsSku"/>
+</a><a name="Goals_getItemsSku"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Product SKU
</h2>
@@ -2661,7 +2661,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getItemsName"/>
+</a><a name="Goals_getItemsName"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Product Name
</h2>
@@ -2854,7 +2854,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getItemsCategory"/>
+</a><a name="Goals_getItemsCategory"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Product Category
</h2>
@@ -3099,7 +3099,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_get"/>
+</a><a name="Actions_get"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Actions - Main metrics
</h2>
@@ -3182,7 +3182,7 @@ Unique Keywords
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getPageUrls"/>
+</a><a name="Actions_getPageUrls"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Page URLs
</h2>
@@ -3235,7 +3235,7 @@ Page URLs
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getEntryPageUrls"/>
+</a><a name="Actions_getEntryPageUrls"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Entry pages
</h2>
@@ -3276,7 +3276,7 @@ Entry pages
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getExitPageUrls"/>
+</a><a name="Actions_getExitPageUrls"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Exit pages
</h2>
@@ -3317,7 +3317,7 @@ Exit pages
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getPageTitles"/>
+</a><a name="Actions_getPageTitles"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Page titles
</h2>
@@ -3508,7 +3508,7 @@ Looking at product page
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getEntryPageTitles"/>
+</a><a name="Actions_getEntryPageTitles"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Entry page titles
</h2>
@@ -3575,7 +3575,7 @@ Looking at Electronics &amp; Cameras page with a page level custom variable
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getExitPageTitles"/>
+</a><a name="Actions_getExitPageTitles"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Exit page titles
</h2>
@@ -3628,15 +3628,15 @@ Another Product page with multiple categories
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Actions_getOutlinks"/>
+</a><a name="Actions_getOutlinks"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Outlinks
</h2>
-There is no data for this report.<a name ="Actions_getDownloads"/>
+There is no data for this report.<a name="Actions_getDownloads"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Downloads
</h2>
-There is no data for this report.<a name ="Referers_getRefererType"/>
+There is no data for this report.<a name="Referers_getRefererType"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Referrer Type
</h2>
@@ -3693,31 +3693,31 @@ $ 13361.11
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Referers_getAll"/>
+</a><a name="Referers_getAll"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
All Referrers
</h2>
-There is no data for this report.<a name ="Referers_getKeywords"/>
+There is no data for this report.<a name="Referers_getKeywords"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Keywords
</h2>
-There is no data for this report.<a name ="Referers_getWebsites"/>
+There is no data for this report.<a name="Referers_getWebsites"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Websites
</h2>
-There is no data for this report.<a name ="Referers_getSearchEngines"/>
+There is no data for this report.<a name="Referers_getSearchEngines"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Search Engines
</h2>
-There is no data for this report.<a name ="Referers_getCampaigns"/>
+There is no data for this report.<a name="Referers_getCampaigns"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Campaigns
</h2>
-There is no data for this report.<a name ="Referers_getSocials"/>
+There is no data for this report.<a name="Referers_getSocials"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Social Networks
</h2>
-There is no data for this report.<a name ="Goals_get"/>
+There is no data for this report.<a name="Goals_get"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Goals
</h2>
@@ -3768,7 +3768,7 @@ $ 13361.11
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getVisitsUntilConversion"/>
+</a><a name="Goals_getVisitsUntilConversion"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits to Conversion
</h2>
@@ -3933,7 +3933,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getDaysToConversion"/>
+</a><a name="Goals_getDaysToConversion"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Days to Conversion
</h2>
@@ -4109,7 +4109,7 @@ $ 0
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_get_idGoal--1"/>
+</a><a name="Goals_get_idGoal--1"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Goal title match, triggered ONCE
</h2>
@@ -4160,7 +4160,7 @@ $ 10
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getVisitsUntilConversion_idGoal--1"/>
+</a><a name="Goals_getVisitsUntilConversion_idGoal--1"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
title match, triggered ONCE - Visits to Conversion
</h2>
@@ -4283,7 +4283,7 @@ title match, triggered ONCE - Visits to Conversion
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Goals_getDaysToConversion_idGoal--1"/>
+</a><a name="Goals_getDaysToConversion_idGoal--1"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
title match, triggered ONCE - Days to Conversion
</h2>
@@ -4414,7 +4414,7 @@ title match, triggered ONCE - Days to Conversion
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserCountry_getCountry"/>
+</a><a name="UserCountry_getCountry"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Country
</h2>
@@ -4445,7 +4445,8 @@ Country
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserCountry/flags/fr.png'>&nbsp;
+<img src='plugins/UserCountry/flags/fr.png'>
+&nbsp;
France
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -4472,7 +4473,7 @@ $ 13361.11
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserCountry_getContinent"/>
+</a><a name="UserCountry_getContinent"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Continent
</h2>
@@ -4529,7 +4530,7 @@ $ 13361.11
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserCountry_getRegion"/>
+</a><a name="UserCountry_getRegion"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Region
</h2>
@@ -4560,7 +4561,8 @@ Region
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserCountry/flags/xx.png'>&nbsp;
+<img src='plugins/UserCountry/flags/xx.png'>
+&nbsp;
Unknown
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -4587,7 +4589,7 @@ $ 13361.11
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserCountry_getCity"/>
+</a><a name="UserCountry_getCity"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
City
</h2>
@@ -4618,7 +4620,8 @@ City
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserCountry/flags/xx.png'>&nbsp;
+<img src='plugins/UserCountry/flags/xx.png'>
+&nbsp;
Unknown
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -4645,7 +4648,7 @@ $ 13361.11
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="CustomVariables_getCustomVariables"/>
+</a><a name="CustomVariables_getCustomVariables"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Custom Variables
</h2>
@@ -4748,7 +4751,7 @@ $ 13351.11
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitorInterest_getNumberOfVisitsPerVisitDuration"/>
+</a><a name="VisitorInterest_getNumberOfVisitsPerVisitDuration"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Length of Visits
</h2>
@@ -4847,7 +4850,7 @@ Length of Visits
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitorInterest_getNumberOfVisitsPerPage"/>
+</a><a name="VisitorInterest_getNumberOfVisitsPerPage"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Pages per Visit
</h2>
@@ -4946,7 +4949,7 @@ Pages per Visit
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitorInterest_getNumberOfVisitsByVisitCount"/>
+</a><a name="VisitorInterest_getNumberOfVisitsByVisitCount"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits by Visit Number
</h2>
@@ -5122,7 +5125,7 @@ Visits by Visit Number
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitorInterest_getNumberOfVisitsByDaysSinceLast"/>
+</a><a name="VisitorInterest_getNumberOfVisitsByDaysSinceLast"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visits by days since last visit
</h2>
@@ -5261,7 +5264,7 @@ New visits
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="VisitFrequency_get"/>
+</a><a name="VisitFrequency_get"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Returning Visits
</h2>
@@ -5328,7 +5331,7 @@ Unique returning visitors
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="Provider_getProvider"/>
+</a><a name="Provider_getProvider"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Provider
</h2>
@@ -5387,7 +5390,7 @@ Unknown
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getResolution"/>
+</a><a name="UserSettings_getResolution"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Screen Resolution
</h2>
@@ -5444,7 +5447,7 @@ Screen Resolution
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getBrowser"/>
+</a><a name="UserSettings_getBrowser"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visitor Browser
</h2>
@@ -5475,7 +5478,8 @@ Visitor Browser
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/browsers/FF.gif'>&nbsp;
+<img src='plugins/UserSettings/images/browsers/FF.gif'>
+&nbsp;
Firefox
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5502,7 +5506,7 @@ Firefox
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getBrowserVersion"/>
+</a><a name="UserSettings_getBrowserVersion"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Browser Version
</h2>
@@ -5533,7 +5537,8 @@ Browser Version
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/browsers/FF.gif'>&nbsp;
+<img src='plugins/UserSettings/images/browsers/FF.gif'>
+&nbsp;
Firefox 3.6
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5560,7 +5565,7 @@ Firefox 3.6
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getBrowserType"/>
+</a><a name="UserSettings_getBrowserType"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Browser Family
</h2>
@@ -5617,7 +5622,7 @@ Gecko (Firefox)
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getPlugin"/>
+</a><a name="UserSettings_getPlugin"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Browser Plugins
</h2>
@@ -5636,7 +5641,8 @@ Browser Plugins
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/cookie.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/cookie.gif'>
+&nbsp;
Cookie
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5648,7 +5654,8 @@ Cookie
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/flash.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/flash.gif'>
+&nbsp;
Flash
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5660,7 +5667,8 @@ Flash
</tr>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/java.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/java.gif'>
+&nbsp;
Java
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5672,7 +5680,8 @@ Java
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/director.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/director.gif'>
+&nbsp;
Director
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5684,7 +5693,8 @@ Director
</tr>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/gears.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/gears.gif'>
+&nbsp;
Gears
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5696,7 +5706,8 @@ Gears
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/pdf.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/pdf.gif'>
+&nbsp;
Pdf
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5708,7 +5719,8 @@ Pdf
</tr>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/quicktime.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/quicktime.gif'>
+&nbsp;
Quicktime
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5720,7 +5732,8 @@ Quicktime
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/realplayer.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/realplayer.gif'>
+&nbsp;
Realplayer
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5732,7 +5745,8 @@ Realplayer
</tr>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/silverlight.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/silverlight.gif'>
+&nbsp;
Silverlight
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5744,7 +5758,8 @@ Silverlight
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/plugins/windowsmedia.gif'>&nbsp;
+<img src='plugins/UserSettings/images/plugins/windowsmedia.gif'>
+&nbsp;
Windowsmedia
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5759,7 +5774,7 @@ Windowsmedia
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getWideScreen"/>
+</a><a name="UserSettings_getWideScreen"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Normal / Widescreen
</h2>
@@ -5790,7 +5805,8 @@ Normal / Widescreen
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/screens/normal.gif'>&nbsp;
+<img src='plugins/UserSettings/images/screens/normal.gif'>
+&nbsp;
Normal
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5817,7 +5833,7 @@ Normal
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getOS"/>
+</a><a name="UserSettings_getOS"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Operating System
</h2>
@@ -5848,7 +5864,8 @@ Operating System
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/os/WXP.gif'>&nbsp;
+<img src='plugins/UserSettings/images/os/WXP.gif'>
+&nbsp;
Windows XP
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5875,7 +5892,7 @@ Windows XP
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getConfiguration"/>
+</a><a name="UserSettings_getConfiguration"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Visitor Configuration
</h2>
@@ -5932,7 +5949,7 @@ Windows XP / Firefox / 1024x768
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getOSFamily"/>
+</a><a name="UserSettings_getOSFamily"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Operating System Family
</h2>
@@ -5963,7 +5980,8 @@ Operating System Family
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/os/WXP.gif'>&nbsp;
+<img src='plugins/UserSettings/images/os/WXP.gif'>
+&nbsp;
Windows
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -5990,7 +6008,7 @@ Windows
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getMobileVsDesktop"/>
+</a><a name="UserSettings_getMobileVsDesktop"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Mobile vs Desktop
</h2>
@@ -6021,7 +6039,8 @@ Mobile vs Desktop
<tbody>
<tr style="">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/screens/normal.gif'>&nbsp;
+<img src='plugins/UserSettings/images/screens/normal.gif'>
+&nbsp;
Desktop
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -6045,7 +6064,8 @@ Desktop
</tr>
<tr style="background-color: rgb(249,250,250)">
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
-<img src='plugins/UserSettings/images/screens/mobile.gif'>&nbsp;
+<img src='plugins/UserSettings/images/screens/mobile.gif'>
+&nbsp;
Mobile
</td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
@@ -6072,7 +6092,7 @@ Mobile
<br/>
<a style="text-decoration:none; color: rgb(126,115,99); font-size: 9pt" href="#reportTop">
Back to top
-</a><a name ="UserSettings_getLanguage"/>
+</a><a name="UserSettings_getLanguage"/>
<h2 style="color: rgb(126,115,99); font-size: 11pt;">
Browser language
</h2>
diff --git a/tests/PHPUnit/IntegrationTestCase.php b/tests/PHPUnit/IntegrationTestCase.php
index 3ee7c7f59c..b81027fc31 100755
--- a/tests/PHPUnit/IntegrationTestCase.php
+++ b/tests/PHPUnit/IntegrationTestCase.php
@@ -21,101 +21,94 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
* @var string
*/
protected $lastLanguage;
-
- /**
- * Creates a config object for use w/ tests.
- */
- public static function createTestConfig()
- {
+
+ /**
+ * Creates a config object for use w/ tests.
+ */
+ public static function createTestConfig()
+ {
Piwik::createConfigObject();
Piwik_Config::getInstance()->setTestEnvironment();
- }
-
- /**
- * Connects to MySQL w/o specifying a database.
- */
- public static function connectWithoutDatabase()
- {
+ }
+
+ /**
+ * Connects to MySQL w/o specifying a database.
+ */
+ public static function connectWithoutDatabase()
+ {
$dbConfig = Piwik_Config::getInstance()->database;
$oldDbName = $dbConfig['dbname'];
$dbConfig['dbname'] = null;
-
+
Piwik::createDatabaseObject($dbConfig);
-
+
$dbConfig['dbname'] = $oldDbName;
- }
-
- public static function setUpBeforeClass()
- {
- self::_setUpBeforeClass();
-
- if (isset(static::$fixture))
- {
- self::setupFixture(static::$fixture);
- }
- }
-
- protected static function setupFixture( $fixture )
- {
- try
- {
- $fixture->setUp();
- }
- catch(Exception $e)
- {
- // Skip whole test suite if an error occurs while setup
- throw new PHPUnit_Framework_SkippedTestSuiteError($e->getMessage()."\n".$e->getTraceAsString());
- }
- }
-
- protected static function teardownFixture( $fixture )
- {
- if (isset(static::$fixture))
- {
- self::tearDownFixture(static::$fixture);
- }
-
- $fixture->tearDown();
- }
-
- /**
- * setupBeforeClass' implementation. Can be called by derived classes in case
- * they need to do some custom setup procedure.
- */
- public static function _setUpBeforeClass( $dbName = false, $createEmptyDatabase = true, $createConfig = true )
+ }
+
+ public static function setUpBeforeClass()
+ {
+ self::_setUpBeforeClass();
+
+ if (isset(static::$fixture)) {
+ self::setupFixture(static::$fixture);
+ }
+ }
+
+ protected static function setupFixture($fixture)
{
try {
- Piwik::$piwikUrlCache = '';
-
- if ($createConfig)
- {
- self::createTestConfig();
- }
-
- if ($dbName === false) // must be after test config is created
- {
- $dbName = Piwik_Config::getInstance()->database['dbname'];
- }
-
- self::connectWithoutDatabase();
- if ($createEmptyDatabase)
+ $fixture->setUp();
+ } catch (Exception $e) {
+ // Skip whole test suite if an error occurs while setup
+ throw new PHPUnit_Framework_SkippedTestSuiteError($e->getMessage() . "\n" . $e->getTraceAsString());
+ }
+ }
+
+ protected static function teardownFixture($fixture)
+ {
+ if (isset(static::$fixture)) {
+ self::tearDownFixture(static::$fixture);
+ }
+
+ $fixture->tearDown();
+ }
+
+ /**
+ * setupBeforeClass' implementation. Can be called by derived classes in case
+ * they need to do some custom setup procedure.
+ */
+ public static function _setUpBeforeClass($dbName = false, $createEmptyDatabase = true, $createConfig = true)
+ {
+ try {
+ Piwik::$piwikUrlCache = '';
+
+ if ($createConfig) {
+ self::createTestConfig();
+ }
+
+ if ($dbName === false) // must be after test config is created
{
+ $dbName = Piwik_Config::getInstance()->database['dbname'];
+ }
+
+ self::connectWithoutDatabase();
+ if ($createEmptyDatabase) {
Piwik::dropDatabase();
}
Piwik::createDatabase($dbName);
Piwik::disconnectDatabase();
-
- // reconnect once we're sure the database exists
- Piwik_Config::getInstance()->database['dbname'] = $dbName;
+
+ // reconnect once we're sure the database exists
+ Piwik_Config::getInstance()->database['dbname'] = $dbName;
Piwik::createDatabaseObject();
-
+
Piwik::createTables();
Piwik::createLogObject();
Piwik_PluginsManager::getInstance()->loadPlugins(array());
- } catch(Exception $e) {
- self::fail("TEST INITIALIZATION FAILED: " .$e->getMessage());
+ } catch (Exception $e) {
+ self::fail("TEST INITIALIZATION FAILED: " . $e->getMessage());
}
include "DataFiles/SearchEngines.php";
@@ -129,16 +122,16 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
// We need to be SU to create websites for tests
Piwik::setUserIsSuperUser();
- Piwik_Tracker_Cache::deleteTrackerCache();
+ Piwik_Tracker_Cache::deleteTrackerCache();
// Load and install plugins
$pluginsManager = Piwik_PluginsManager::getInstance();
$plugins = $pluginsManager->readPluginsDirectory();
- $pluginsManager->loadPlugins( $plugins );
+ $pluginsManager->loadPlugins($plugins);
if ($createEmptyDatabase) // only install if database is empty
{
- $pluginsManager->installLoadedPlugins();
+ $pluginsManager->installLoadedPlugins();
}
$_GET = $_REQUEST = array();
@@ -151,30 +144,29 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
// List of Modules, or Module.Method that should not be called as part of the XML output compare
// Usually these modules either return random changing data, or are already tested in specific unit tests.
self::setApiNotToCall(self::$defaultApiNotToCall);
- self::setApiToCall( array());
+ self::setApiToCall(array());
}
- public static function tearDownAfterClass()
- {
- self::_tearDownAfterClass();
- }
+ public static function tearDownAfterClass()
+ {
+ self::_tearDownAfterClass();
+ }
- public static function _tearDownAfterClass( $dropDatabase = true )
+ public static function _tearDownAfterClass($dropDatabase = true)
{
- Piwik::$piwikUrlCache = null;
-
+ Piwik::$piwikUrlCache = null;
+
try {
$plugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins();
- foreach($plugins AS $plugin) {
- if ($dropDatabase)
- {
- $plugin->uninstall();
+ foreach ($plugins AS $plugin) {
+ if ($dropDatabase) {
+ $plugin->uninstall();
}
}
Piwik_PluginsManager::getInstance()->unloadPlugins();
- } catch (Exception $e) {}
- if ($dropDatabase)
- {
+ } catch (Exception $e) {
+ }
+ if ($dropDatabase) {
Piwik::dropDatabase();
}
Piwik_DataTable_Manager::getInstance()->deleteAll();
@@ -195,8 +187,8 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
public function setUp()
{
- // Make sure the browser running the test does not influence the Country detection code
- $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en';
+ // Make sure the browser running the test does not influence the Country detection code
+ $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en';
$this->changeLanguage('en');
}
@@ -210,7 +202,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
'UsersManager',
'SitesManager',
'ExampleUI',
- 'Overlay',
+ 'Overlay',
'Live',
'SEO',
'ExampleAPI',
@@ -224,7 +216,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
);
const DEFAULT_USER_PASSWORD = 'nopass';
-
+
protected $missingExpectedFiles = array();
protected $comparisonFailures = array();
@@ -238,14 +230,12 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
* @throws Exception
* @return void
*/
- protected static function setApiToCall( $apiToCall )
+ protected static function setApiToCall($apiToCall)
{
- if(func_num_args() != 1)
- {
+ if (func_num_args() != 1) {
throw new Exception('setApiToCall expects an array');
}
- if(!is_array($apiToCall))
- {
+ if (!is_array($apiToCall)) {
$apiToCall = array($apiToCall);
}
self::$apiToCall = $apiToCall;
@@ -258,164 +248,160 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
*
* @return void
*/
- protected static function setApiNotToCall( $apiNotToCall )
+ protected static function setApiNotToCall($apiNotToCall)
{
- if(!is_array($apiNotToCall))
- {
+ if (!is_array($apiNotToCall)) {
$apiNotToCall = array($apiNotToCall);
}
self::$apiNotToCall = $apiNotToCall;
}
- protected function alertWhenImagesExcludedFromTests()
- {
- if(!Test_Piwik_BaseFixture::canImagesBeIncludedInScheduledReports())
- {
- $this->markTestSkipped(
- 'Do take note that scheduled reports are not being tested with images. ' .
- 'If images contained in scheduled reports have been altered, tests will fail on the Piwik QA Server. ' .
- 'To include images in the test suite, please use a machine with the following specifications : ' .
- 'OS = Linux precise32, PHP Version = 5.3.10 and GD Version = 2.0' .
- "\n Ignore this message if you're running on your dev machine, but pay attention when it comes from Jenkins."
-
- );
- }
- }
-
- /**
- * Return 4 Api Urls for testing scheduled reports :
- * - one in HTML format with all available reports
- * - one in PDF format with all available reports
- * - two in SMS (one for each available report: MultiSites.getOne & MultiSites.getAll)
- *
- * @param string $dateTime eg '2010-01-01 12:34:56'
- * @param string $period eg 'day', 'week', 'month', 'year'
- */
- protected static function getApiForTestingScheduledReports($dateTime, $period)
- {
- $apiCalls = array();
-
- // HTML Scheduled Report
- array_push(
- $apiCalls,
- array(
- 'PDFReports.generateReport',
- array(
- 'testSuffix' => '_scheduled_report_in_html_tables_only',
- 'date' => $dateTime,
- 'periods' => array($period),
- 'format' => 'original',
- 'fileExtension' => 'html',
- 'otherRequestParameters' => array(
- 'idReport' => 1,
- 'reportFormat' => Piwik_ReportRenderer::HTML_FORMAT,
- 'outputType' => Piwik_PDFReports_API::OUTPUT_RETURN
- )
- )
- )
- );
-
- // This particular PDF file looks different on recent PHP
- // Differences with expected in: tests/PHPUnit/Integration/processed/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__PDFReports.generateReport_week.original.pdf
- // Failed asserting that 486675 matches expected 486668.
- // So we disable this test for 5.4 and 5.5
- if(stristr(phpversion(), '5.4') === false && stristr(phpversion(), '5.5') === false)
- {
- // PDF Scheduled Report
- array_push(
- $apiCalls,
- array(
- 'PDFReports.generateReport',
- array(
- 'testSuffix' => '_scheduled_report_in_pdf_tables_only',
- 'date' => $dateTime,
- 'periods' => array($period),
- 'format' => 'original',
- 'fileExtension' => 'pdf',
- 'otherRequestParameters' => array(
- 'idReport' => 1,
- 'reportFormat' => Piwik_ReportRenderer::PDF_FORMAT,
- 'outputType' => Piwik_PDFReports_API::OUTPUT_RETURN
- )
- )
- )
- );
- }
-
- // SMS Scheduled Report, one site
- array_push(
- $apiCalls,
- array(
- 'PDFReports.generateReport',
- array(
- 'testSuffix' => '_scheduled_report_via_sms_one_site',
- 'date' => $dateTime,
- 'periods' => array($period),
- 'format' => 'original',
- 'fileExtension' => 'sms.txt',
- 'otherRequestParameters' => array(
- 'idReport' => 2,
- 'outputType' => Piwik_PDFReports_API::OUTPUT_RETURN
- )
- )
- )
- );
-
- // SMS Scheduled Report, all sites
- array_push(
- $apiCalls,
- array(
- 'PDFReports.generateReport',
- array(
- 'testSuffix' => '_scheduled_report_via_sms_all_sites',
- 'date' => $dateTime,
- 'periods' => array($period),
- 'format' => 'original',
- 'fileExtension' => 'sms.txt',
- 'otherRequestParameters' => array(
- 'idReport' => 3,
- 'outputType' => Piwik_PDFReports_API::OUTPUT_RETURN
- )
- )
- )
- );
-
- if(Test_Piwik_BaseFixture::canImagesBeIncludedInScheduledReports())
- {
- // HTML Scheduled Report with images
- array_push(
- $apiCalls,
- array(
- 'PDFReports.generateReport',
- array(
- 'testSuffix' => '_scheduled_report_in_html_tables_and_graph',
- 'date' => $dateTime,
- 'periods' => array($period),
- 'format' => 'original',
- 'fileExtension' => 'html',
- 'otherRequestParameters' => array(
- 'idReport' => 4,
- 'reportFormat' => Piwik_ReportRenderer::HTML_FORMAT,
- 'outputType' => Piwik_PDFReports_API::OUTPUT_RETURN
- )
- )
- )
- );
- }
-
- return $apiCalls;
- }
+ protected function alertWhenImagesExcludedFromTests()
+ {
+ if (!Test_Piwik_BaseFixture::canImagesBeIncludedInScheduledReports()) {
+ $this->markTestSkipped(
+ 'Do take note that scheduled reports are not being tested with images. ' .
+ 'If images contained in scheduled reports have been altered, tests will fail on the Piwik QA Server. ' .
+ 'To include images in the test suite, please use a machine with the following specifications : ' .
+ 'OS = Linux precise32, PHP Version = 5.3.10 and GD Version = 2.0' .
+ "\n Ignore this message if you're running on your dev machine, but pay attention when it comes from Jenkins."
+
+ );
+ }
+ }
+
+ /**
+ * Return 4 Api Urls for testing scheduled reports :
+ * - one in HTML format with all available reports
+ * - one in PDF format with all available reports
+ * - two in SMS (one for each available report: MultiSites.getOne & MultiSites.getAll)
+ *
+ * @param string $dateTime eg '2010-01-01 12:34:56'
+ * @param string $period eg 'day', 'week', 'month', 'year'
+ */
+ protected static function getApiForTestingScheduledReports($dateTime, $period)
+ {
+ $apiCalls = array();
+
+ // HTML Scheduled Report
+ array_push(
+ $apiCalls,
+ array(
+ 'PDFReports.generateReport',
+ array(
+ 'testSuffix' => '_scheduled_report_in_html_tables_only',
+ 'date' => $dateTime,
+ 'periods' => array($period),
+ 'format' => 'original',
+ 'fileExtension' => 'html',
+ 'otherRequestParameters' => array(
+ 'idReport' => 1,
+ 'reportFormat' => Piwik_ReportRenderer::HTML_FORMAT,
+ 'outputType' => Piwik_PDFReports_API::OUTPUT_RETURN
+ )
+ )
+ )
+ );
+
+ // This particular PDF file looks different on recent PHP
+ // Differences with expected in: tests/PHPUnit/Integration/processed/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__PDFReports.generateReport_week.original.pdf
+ // Failed asserting that 486675 matches expected 486668.
+ // So we disable this test for 5.4 and 5.5
+ if (stristr(phpversion(), '5.4') === false && stristr(phpversion(), '5.5') === false) {
+ // PDF Scheduled Report
+ array_push(
+ $apiCalls,
+ array(
+ 'PDFReports.generateReport',
+ array(
+ 'testSuffix' => '_scheduled_report_in_pdf_tables_only',
+ 'date' => $dateTime,
+ 'periods' => array($period),
+ 'format' => 'original',
+ 'fileExtension' => 'pdf',
+ 'otherRequestParameters' => array(
+ 'idReport' => 1,
+ 'reportFormat' => Piwik_ReportRenderer::PDF_FORMAT,
+ 'outputType' => Piwik_PDFReports_API::OUTPUT_RETURN
+ )
+ )
+ )
+ );
+ }
+
+ // SMS Scheduled Report, one site
+ array_push(
+ $apiCalls,
+ array(
+ 'PDFReports.generateReport',
+ array(
+ 'testSuffix' => '_scheduled_report_via_sms_one_site',
+ 'date' => $dateTime,
+ 'periods' => array($period),
+ 'format' => 'original',
+ 'fileExtension' => 'sms.txt',
+ 'otherRequestParameters' => array(
+ 'idReport' => 2,
+ 'outputType' => Piwik_PDFReports_API::OUTPUT_RETURN
+ )
+ )
+ )
+ );
+
+ // SMS Scheduled Report, all sites
+ array_push(
+ $apiCalls,
+ array(
+ 'PDFReports.generateReport',
+ array(
+ 'testSuffix' => '_scheduled_report_via_sms_all_sites',
+ 'date' => $dateTime,
+ 'periods' => array($period),
+ 'format' => 'original',
+ 'fileExtension' => 'sms.txt',
+ 'otherRequestParameters' => array(
+ 'idReport' => 3,
+ 'outputType' => Piwik_PDFReports_API::OUTPUT_RETURN
+ )
+ )
+ )
+ );
+
+ if (Test_Piwik_BaseFixture::canImagesBeIncludedInScheduledReports()) {
+ // HTML Scheduled Report with images
+ array_push(
+ $apiCalls,
+ array(
+ 'PDFReports.generateReport',
+ array(
+ 'testSuffix' => '_scheduled_report_in_html_tables_and_graph',
+ 'date' => $dateTime,
+ 'periods' => array($period),
+ 'format' => 'original',
+ 'fileExtension' => 'html',
+ 'otherRequestParameters' => array(
+ 'idReport' => 4,
+ 'reportFormat' => Piwik_ReportRenderer::HTML_FORMAT,
+ 'outputType' => Piwik_PDFReports_API::OUTPUT_RETURN
+ )
+ )
+ )
+ );
+ }
+
+ return $apiCalls;
+ }
/**
* Given a list of default parameters to set, returns the URLs of APIs to call
* If any API was specified in setApiToCall() we ensure only these are tested.
* If any API is set as excluded (see list below) then it will be ignored.
*
- * @param array $parametersToSet Parameters to set in api call
- * @param array $formats Array of 'format' to fetch from API
- * @param array $periods Array of 'period' to query API
- * @param bool $supertableApi
- * @param bool $setDateLastN If set to true, the 'date' parameter will be rewritten to query instead a range of dates, rather than one period only.
+ * @param array $parametersToSet Parameters to set in api call
+ * @param array $formats Array of 'format' to fetch from API
+ * @param array $periods Array of 'period' to query API
+ * @param bool $supertableApi
+ * @param bool $setDateLastN If set to true, the 'date' parameter will be rewritten to query instead a range of dates, rather than one period only.
* @param bool|string $language 2 letter language code, defaults to default piwik language
* @param bool|string $segment
* @param bool|string $fileExtension
@@ -423,29 +409,26 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
* @throws Exception
* @return array of API URLs query strings
*/
- protected function generateUrlsApi( $parametersToSet, $formats, $periods, $supertableApi = false, $setDateLastN = false, $language = false, $segment = false, $fileExtension = false )
+ protected function generateUrlsApi($parametersToSet, $formats, $periods, $supertableApi = false, $setDateLastN = false, $language = false, $segment = false, $fileExtension = false)
{
// Get the URLs to query against the API for all functions starting with get*
$skipped = $requestUrls = array();
$apiMetadata = new Piwik_API_DocumentationGenerator;
- foreach(Piwik_API_Proxy::getInstance()->getMetadata() as $class => $info)
- {
+ foreach (Piwik_API_Proxy::getInstance()->getMetadata() as $class => $info) {
$moduleName = Piwik_API_Proxy::getInstance()->getModuleNameFromClassName($class);
- foreach($info as $methodName => $infoMethod)
- {
- $apiId = $moduleName.'.'.$methodName;
+ foreach ($info as $methodName => $infoMethod) {
+ $apiId = $moduleName . '.' . $methodName;
// If Api to test were set, we only test these
- if(!empty(self::$apiToCall)
+ if (!empty(self::$apiToCall)
&& in_array($moduleName, self::$apiToCall) === false
- && in_array($apiId, self::$apiToCall) === false)
- {
+ && in_array($apiId, self::$apiToCall) === false
+ ) {
// echo "Skipped $apiId... \n";
$skipped[] = $apiId;
- continue;
- }
- // Excluded modules from test
- elseif(
+ continue;
+ } // Excluded modules from test
+ elseif (
((strpos($methodName, 'get') !== 0 && $methodName != 'generateReport')
|| in_array($moduleName, self::$apiNotToCall) === true
|| in_array($apiId, self::$apiNotToCall) === true
@@ -454,28 +437,23 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
|| $methodName == 'hasSVGLogo'
|| $methodName == 'getHeaderLogoUrl'
)
- )
- {
+ ) {
// echo "Skipped $apiId... \n";
$skipped[] = $apiId;
continue;
}
- foreach($periods as $period)
- {
+ foreach ($periods as $period) {
$parametersToSet['period'] = $period;
// If date must be a date range, we process this date range by adding 6 periods to it
- if($setDateLastN)
- {
- if(!isset($parametersToSet['dateRewriteBackup']))
- {
+ if ($setDateLastN) {
+ if (!isset($parametersToSet['dateRewriteBackup'])) {
$parametersToSet['dateRewriteBackup'] = $parametersToSet['date'];
}
$lastCount = (int)$setDateLastN;
- if($setDateLastN === true)
- {
+ if ($setDateLastN === true) {
$lastCount = 6;
}
$firstDate = $parametersToSet['dateRewriteBackup'];
@@ -484,52 +462,45 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
}
// Set response language
- if($language !== false)
- {
+ if ($language !== false) {
$parametersToSet['language'] = $language;
}
// set idSubtable if subtable API is set
- if ($supertableApi !== false)
- {
- $request = new Piwik_API_Request(array(
- 'module' => 'API',
- 'method' => $supertableApi,
- 'idSite' => $parametersToSet['idSite'],
- 'period' => $parametersToSet['period'],
- 'date' => $parametersToSet['date'],
- 'format' => 'php',
- 'serialize' => 0,
- ));
-
- // find first row w/ subtable
- foreach ($request->process() as $row)
- {
- if (isset($row['idsubdatatable']))
- {
- $parametersToSet['idSubtable'] = $row['idsubdatatable'];
- break;
- }
- }
-
- // if no subtable found, throw
- if (!isset($parametersToSet['idSubtable']))
- {
- throw new Exception(
- "Cannot find subtable to load for $apiId in $supertableApi.");
- }
+ if ($supertableApi !== false) {
+ $request = new Piwik_API_Request(array(
+ 'module' => 'API',
+ 'method' => $supertableApi,
+ 'idSite' => $parametersToSet['idSite'],
+ 'period' => $parametersToSet['period'],
+ 'date' => $parametersToSet['date'],
+ 'format' => 'php',
+ 'serialize' => 0,
+ ));
+
+ // find first row w/ subtable
+ foreach ($request->process() as $row) {
+ if (isset($row['idsubdatatable'])) {
+ $parametersToSet['idSubtable'] = $row['idsubdatatable'];
+ break;
+ }
+ }
+
+ // if no subtable found, throw
+ if (!isset($parametersToSet['idSubtable'])) {
+ throw new Exception(
+ "Cannot find subtable to load for $apiId in $supertableApi.");
+ }
}
// Generate for each specified format
- foreach($formats as $format)
- {
+ foreach ($formats as $format) {
$parametersToSet['format'] = $format;
$parametersToSet['hideIdSubDatable'] = 1;
$parametersToSet['serialize'] = 1;
$exampleUrl = $apiMetadata->getExampleUrl($class, $methodName, $parametersToSet);
- if($exampleUrl === false)
- {
+ if ($exampleUrl === false) {
$skipped[] = $apiId;
continue;
}
@@ -537,17 +508,15 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
// Remove the first ? in the query string
$exampleUrl = substr($exampleUrl, 1);
$apiRequestId = $apiId;
- if(strpos($exampleUrl, 'period=') !== false)
- {
+ if (strpos($exampleUrl, 'period=') !== false) {
$apiRequestId .= '_' . $period;
}
$apiRequestId .= '.' . $format;
- if($fileExtension)
- {
- $apiRequestId .= '.' . $fileExtension;
- }
+ if ($fileExtension) {
+ $apiRequestId .= '.' . $fileExtension;
+ }
$requestUrls[$apiRequestId] = $exampleUrl;
}
@@ -560,118 +529,109 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
/**
* Will return all api urls for the given data
*
- * @param string|array $formats String or array of formats to fetch from API
- * @param int|bool $idSite Id site
- * @param string|bool $dateTime Date time string of reports to request
+ * @param string|array $formats String or array of formats to fetch from API
+ * @param int|bool $idSite Id site
+ * @param string|bool $dateTime Date time string of reports to request
* @param array|bool|string $periods String or array of strings of periods (day, week, month, year)
- * @param bool $setDateLastN When set to true, 'date' parameter passed to API request will be rewritten to query a range of dates rather than 1 date only
- * @param string|bool $language 2 letter language code to request data in
- * @param string|bool $segment Custom Segment to query the data for
- * @param string|bool $visitorId Only used for Live! API testing
- * @param bool $abandonedCarts Only used in Goals API testing
- * @param bool $idGoal
- * @param bool $apiModule
- * @param bool $apiAction
- * @param array $otherRequestParameters
- * @param array|bool $supertableApi
- * @param array|bool $fileExtension
+ * @param bool $setDateLastN When set to true, 'date' parameter passed to API request will be rewritten to query a range of dates rather than 1 date only
+ * @param string|bool $language 2 letter language code to request data in
+ * @param string|bool $segment Custom Segment to query the data for
+ * @param string|bool $visitorId Only used for Live! API testing
+ * @param bool $abandonedCarts Only used in Goals API testing
+ * @param bool $idGoal
+ * @param bool $apiModule
+ * @param bool $apiAction
+ * @param array $otherRequestParameters
+ * @param array|bool $supertableApi
+ * @param array|bool $fileExtension
*
* @return array
*/
protected function _generateApiUrls($formats = 'xml', $idSite = false, $dateTime = false, $periods = false,
- $setDateLastN = false, $language = false, $segment = false, $visitorId = false,
- $abandonedCarts = false, $idGoal = false, $apiModule = false, $apiAction = false,
- $otherRequestParameters = array(), $supertableApi = false, $fileExtension = false)
+ $setDateLastN = false, $language = false, $segment = false, $visitorId = false,
+ $abandonedCarts = false, $idGoal = false, $apiModule = false, $apiAction = false,
+ $otherRequestParameters = array(), $supertableApi = false, $fileExtension = false)
{
list($pathProcessed, $pathExpected) = $this->getProcessedAndExpectedDirs();
- if($periods === false)
- {
+ if ($periods === false) {
$periods = 'day';
}
- if(!is_array($periods))
- {
+ if (!is_array($periods)) {
$periods = array($periods);
}
- if(!is_array($formats))
- {
+ if (!is_array($formats)) {
$formats = array($formats);
}
- if(!is_writable($pathProcessed))
- {
- $this->fail('To run the tests, you need to give write permissions to the following directory (create it if it doesn\'t exist).<code><br/>mkdir '. $pathProcessed.'<br/>chmod 777 '.$pathProcessed.'</code><br/>');
+ if (!is_writable($pathProcessed)) {
+ $this->fail('To run the tests, you need to give write permissions to the following directory (create it if it doesn\'t exist).<code><br/>mkdir ' . $pathProcessed . '<br/>chmod 777 ' . $pathProcessed . '</code><br/>');
}
$parametersToSet = array(
- 'idSite' => $idSite,
- 'date' => $periods == array('range') ? $dateTime : date('Y-m-d', strtotime($dateTime)),
- 'expanded' => '1',
- 'piwikUrl' => 'http://example.org/piwik/',
+ 'idSite' => $idSite,
+ 'date' => $periods == array('range') ? $dateTime : date('Y-m-d', strtotime($dateTime)),
+ 'expanded' => '1',
+ 'piwikUrl' => 'http://example.org/piwik/',
// Used in getKeywordsForPageUrl
- 'url' => 'http://example.org/store/purchase.htm',
+ 'url' => 'http://example.org/store/purchase.htm',
// Used in Actions.getPageUrl, .getDownload, etc.
// tied to Main.test.php doTest_oneVisitorTwoVisits
// will need refactoring when these same API functions are tested in a new function
- 'downloadUrl' => 'http://piwik.org/path/again/latest.zip?phpsessid=this is ignored when searching',
- 'outlinkUrl' => 'http://dev.piwik.org/svn',
- 'pageUrl' => 'http://example.org/index.htm?sessionid=this is also ignored by default',
- 'pageName' => ' Checkout / Purchasing... ',
+ 'downloadUrl' => 'http://piwik.org/path/again/latest.zip?phpsessid=this is ignored when searching',
+ 'outlinkUrl' => 'http://dev.piwik.org/svn',
+ 'pageUrl' => 'http://example.org/index.htm?sessionid=this is also ignored by default',
+ 'pageName' => ' Checkout / Purchasing... ',
// do not show the millisec timer in response or tests would always fail as value is changing
- 'showTimer' => 0,
+ 'showTimer' => 0,
- 'language' => $language ? $language : 'en',
- 'abandonedCarts' => $abandonedCarts ? 1 : 0,
- 'idSites' => $idSite,
+ 'language' => $language ? $language : 'en',
+ 'abandonedCarts' => $abandonedCarts ? 1 : 0,
+ 'idSites' => $idSite,
);
$parametersToSet = array_merge($parametersToSet, $otherRequestParameters);
- if(!empty($visitorId ))
- {
+ if (!empty($visitorId)) {
$parametersToSet['visitorId'] = $visitorId;
}
- if(!empty($apiModule ))
- {
+ if (!empty($apiModule)) {
$parametersToSet['apiModule'] = $apiModule;
}
- if(!empty($apiAction))
- {
+ if (!empty($apiAction)) {
$parametersToSet['apiAction'] = $apiAction;
}
- if(!empty($segment))
- {
+ if (!empty($segment)) {
$parametersToSet['segment'] = $segment;
}
- if($idGoal !== false)
- {
+ if ($idGoal !== false) {
$parametersToSet['idGoal'] = $idGoal;
}
$requestUrls = $this->generateUrlsApi($parametersToSet, $formats, $periods, $supertableApi, $setDateLastN, $language, $segment, $fileExtension);
- $this->checkEnoughUrlsAreTested($requestUrls);
+ $this->checkEnoughUrlsAreTested($requestUrls);
- return $requestUrls;
+ return $requestUrls;
+ }
+
+ protected function checkEnoughUrlsAreTested($requestUrls)
+ {
+ $countUrls = count($requestUrls);
+ $approximateCountApiToCall = count(self::$apiToCall);
+ if (empty($requestUrls)
+ || $approximateCountApiToCall > $countUrls
+ ) {
+ throw new Exception("Only generated $countUrls API calls to test but was expecting more for this test.\n" .
+ "Want to test APIs: " . implode(", ", self::$apiToCall) . ")\n" .
+ "But only generated these URLs: \n" . implode("\n", $requestUrls) . ")\n"
+ );
+ }
}
- protected function checkEnoughUrlsAreTested($requestUrls)
- {
- $countUrls = count($requestUrls);
- $approximateCountApiToCall = count(self::$apiToCall);
- if (empty($requestUrls)
- || $approximateCountApiToCall > $countUrls
- ) {
- throw new Exception("Only generated $countUrls API calls to test but was expecting more for this test.\n".
- "Want to test APIs: " . implode(", ", self::$apiToCall) . ")\n".
- "But only generated these URLs: \n" . implode("\n", $requestUrls) . ")\n"
- );
- }
- }
-
- protected function _testApiUrl($testName, $apiId, $requestUrl)
+ protected function _testApiUrl($testName, $apiId, $requestUrl)
{
$isLiveMustDeleteDates = strpos($requestUrl, 'Live.getLastVisits') !== false;
- $request = new Piwik_API_Request($requestUrl);
- $dateTime = Piwik_Common::getRequestVar('date', '', 'string', Piwik_Common::getArrayFromQueryString($requestUrl));
+ $request = new Piwik_API_Request($requestUrl);
+ $dateTime = Piwik_Common::getRequestVar('date', '', 'string', Piwik_Common::getArrayFromQueryString($requestUrl));
list($processedFilePath, $expectedFilePath) = $this->getProcessedAndExpectedPaths($testName, $apiId);
@@ -684,24 +644,24 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
$response = $this->removeAllLiveDatesFromXml($response);
}
- // normalize date markups and document ID in pdf files :
- // - /LastModified (D:20120820204023+00'00')
- // - /CreationDate (D:20120820202226+00'00')
- // - /ModDate (D:20120820202226+00'00')
- // - /M (D:20120820202226+00'00')
- // - /ID [ <0f5cc387dc28c0e13e682197f485fe65> <0f5cc387dc28c0e13e682197f485fe65> ]
- $response = preg_replace('/\(D:[0-9]{14}/', '(D:19700101000000', $response);
- $response = preg_replace('/\/ID \[ <.*> ]/', '', $response);
+ // normalize date markups and document ID in pdf files :
+ // - /LastModified (D:20120820204023+00'00')
+ // - /CreationDate (D:20120820202226+00'00')
+ // - /ModDate (D:20120820202226+00'00')
+ // - /M (D:20120820202226+00'00')
+ // - /ID [ <0f5cc387dc28c0e13e682197f485fe65> <0f5cc387dc28c0e13e682197f485fe65> ]
+ $response = preg_replace('/\(D:[0-9]{14}/', '(D:19700101000000', $response);
+ $response = preg_replace('/\/ID \[ <.*> ]/', '', $response);
file_put_contents($processedFilePath, $response);
$expected = $this->loadExpectedFile($expectedFilePath);
if (empty($expected)) {
- print("The expected file is not found at '$expectedFilePath'. The Processed response was:");
- print("\n----------------------------\n\n");
- var_dump($response);
- print("\n----------------------------\n");
- return;
+ print("The expected file is not found at '$expectedFilePath'. The Processed response was:");
+ print("\n----------------------------\n\n");
+ var_dump($response);
+ print("\n----------------------------\n");
+ return;
}
// @todo This should not vary between systems AFAIK... "idsubdatatable can differ"
@@ -724,22 +684,20 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
$response = $this->removeXmlElement($response, 'sum_daily_nb_uniq_visitors');
$expected = $this->removeXmlElement($expected, 'nb_visits_converted');
$response = $this->removeXmlElement($response, 'nb_visits_converted');
-
-
- if (strpos($requestUrl, 'date=') !== false)
- {
- $regex = "/date=[-0-9,%Ca-z]+/"; // need to remove %2C which is encoded ,
- $expected = preg_replace($regex, 'date=', $expected);
- $response = preg_replace($regex, 'date=', $response);
- }
+
+
+ if (strpos($requestUrl, 'date=') !== false) {
+ $regex = "/date=[-0-9,%Ca-z]+/"; // need to remove %2C which is encoded ,
+ $expected = preg_replace($regex, 'date=', $expected);
+ $response = preg_replace($regex, 'date=', $response);
+ }
}
// if idSubtable is in request URL, make sure idSubtable values are not in any urls
- if (strpos($requestUrl, 'idSubtable=') !== false)
- {
- $regex = "/idSubtable=[0-9]+/";
- $expected = preg_replace($regex, 'idSubtable=', $expected);
- $response = preg_replace($regex, 'idSubtable=', $response);
+ if (strpos($requestUrl, 'idSubtable=') !== false) {
+ $regex = "/idSubtable=[0-9]+/";
+ $expected = preg_replace($regex, 'idSubtable=', $expected);
+ $response = preg_replace($regex, 'idSubtable=', $response);
}
// is there a better way to test for the current DB type in use?
@@ -747,32 +705,29 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
// Do not test for TRUNCATE(SUM()) returning .00 on mysqli since this is not working
// http://bugs.php.net/bug.php?id=54508
$expected = str_replace('.000000</l', '</l', $expected); //lat/long
- $response = str_replace('.000000</l', '</l', $response); //lat/long
- $expected = str_replace('.00</revenue>', '</revenue>', $expected);
+ $response = str_replace('.000000</l', '</l', $response); //lat/long
+ $expected = str_replace('.00</revenue>', '</revenue>', $expected);
$response = str_replace('.00</revenue>', '</revenue>', $response);
- $response = str_replace('.1</revenue>', '</revenue>', $response);
- $expected = str_replace('.1</revenue>', '</revenue>', $expected);
- $expected = str_replace('.11</revenue>', '</revenue>', $expected);
- $response = str_replace('.11</revenue>', '</revenue>', $response);
+ $response = str_replace('.1</revenue>', '</revenue>', $response);
+ $expected = str_replace('.1</revenue>', '</revenue>', $expected);
+ $expected = str_replace('.11</revenue>', '</revenue>', $expected);
+ $response = str_replace('.11</revenue>', '</revenue>', $response);
}
- try
- {
- if (strpos($requestUrl, 'format=xml') !== false) {
- $this->assertXmlStringEqualsXmlString($expected, $response, "Differences with expected in: $processedFilePath");
- } else {
- $this->assertEquals(strlen($expected), strlen($response), "Differences with expected in: $processedFilePath");
- $this->assertEquals($expected, $response, "Differences with expected in: $processedFilePath");
- }
-
- if (trim($response) == trim($expected)) {
- file_put_contents($processedFilePath, $response);
- }
- }
- catch (Exception $ex)
- {
- $this->comparisonFailures[] = $ex;
- }
+ try {
+ if (strpos($requestUrl, 'format=xml') !== false) {
+ $this->assertXmlStringEqualsXmlString($expected, $response, "Differences with expected in: $processedFilePath");
+ } else {
+ $this->assertEquals(strlen($expected), strlen($response), "Differences with expected in: $processedFilePath");
+ $this->assertEquals($expected, $response, "Differences with expected in: $processedFilePath");
+ }
+
+ if (trim($response) == trim($expected)) {
+ file_put_contents($processedFilePath, $response);
+ }
+ } catch (Exception $ex) {
+ $this->comparisonFailures[] = $ex;
+ }
}
protected function removeAllLiveDatesFromXml($input)
@@ -791,7 +746,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
'serverTimePretty',
'visitorId'
);
- foreach($toRemove as $xml) {
+ foreach ($toRemove as $xml) {
$input = $this->removeXmlElement($input, $xml);
}
return $input;
@@ -804,13 +759,12 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
protected function removeXmlElement($input, $xmlElement, $testNotSmallAfter = true)
{
- // Only raise error if there was some data before
- $testNotSmallAfter = strlen($input > 100 ) && $testNotSmallAfter;
-
- $input = preg_replace('/(<'.$xmlElement.'>.+?<\/'.$xmlElement.'>)/', '', $input);
+ // Only raise error if there was some data before
+ $testNotSmallAfter = strlen($input > 100) && $testNotSmallAfter;
+
+ $input = preg_replace('/(<' . $xmlElement . '>.+?<\/' . $xmlElement . '>)/', '', $input);
//check we didn't delete the whole string
- if($testNotSmallAfter)
- {
+ if ($testNotSmallAfter) {
$this->assertTrue(strlen($input) > 100);
}
return $input;
@@ -825,8 +779,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
private function getProcessedAndExpectedPaths($testName, $testId, $format = null)
{
$filename = $testName . '__' . $testId;
- if ($format)
- {
+ if ($format) {
$filename .= ".$format";
}
@@ -838,8 +791,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
private function loadExpectedFile($filePath)
{
$result = @file_get_contents($filePath);
- if(empty($result))
- {
+ if (empty($result)) {
$expectedDir = dirname($filePath);
$this->missingExpectedFiles[] = $filePath;
return null;
@@ -885,7 +837,8 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
*
* All test options are optional, except 'idSite' & 'date'.
*/
- public function getApiForTesting() {
+ public function getApiForTesting()
+ {
return array();
}
@@ -899,28 +852,24 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
protected function _setCallableApi($api)
{
- if ($api == 'all')
- {
+ if ($api == 'all') {
self::setApiToCall(array());
self::setApiNotToCall(self::$defaultApiNotToCall);
- }
- else
- {
- if (!is_array($api))
- {
+ } else {
+ if (!is_array($api)) {
$api = array($api);
}
self::setApiToCall($api);
- if(!in_array('UserCountry.getLocationFromIP', $api)) {
- self::setApiNotToCall( array(
- 'API.getPiwikVersion',
- 'UserCountry.getLocationFromIP'
- ));
- } else {
- self::setApiNotToCall( array());
- }
+ if (!in_array('UserCountry.getLocationFromIP', $api)) {
+ self::setApiNotToCall(array(
+ 'API.getPiwikVersion',
+ 'UserCountry.getLocationFromIP'
+ ));
+ } else {
+ self::setApiNotToCall(array());
+ }
}
}
@@ -935,73 +884,65 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
$this->_setCallableApi($api);
- if (isset($params['disableArchiving']) && $params['disableArchiving'] === true)
- {
+ if (isset($params['disableArchiving']) && $params['disableArchiving'] === true) {
Piwik_ArchiveProcessing::$forceDisableArchiving = true;
- }
- else
- {
+ } else {
Piwik_ArchiveProcessing::$forceDisableArchiving = false;
}
- if (isset($params['language']))
- {
+ if (isset($params['language'])) {
$this->changeLanguage($params['language']);
}
$testSuffix = isset($params['testSuffix']) ? $params['testSuffix'] : '';
$requestUrls = $this->_generateApiUrls(
- isset($params['format']) ? $params['format'] : 'xml',
- isset($params['idSite']) ? $params['idSite'] : false,
- isset($params['date']) ? $params['date'] : false,
- isset($params['periods']) ? $params['periods'] : false,
- isset($params['setDateLastN']) ? $params['setDateLastN'] : false,
- isset($params['language']) ? $params['language'] : false,
- isset($params['segment']) ? $params['segment'] : false,
- isset($params['visitorId']) ? $params['visitorId'] : false,
- isset($params['abandonedCarts']) ? $params['abandonedCarts'] : false,
- isset($params['idGoal']) ? $params['idGoal'] : false,
- isset($params['apiModule']) ? $params['apiModule'] : false,
- isset($params['apiAction']) ? $params['apiAction'] : false,
- isset($params['otherRequestParameters']) ? $params['otherRequestParameters'] : array(),
- isset($params['supertableApi']) ? $params['supertableApi'] : false,
- isset($params['fileExtension']) ? $params['fileExtension'] : false);
-
- foreach($requestUrls as $apiId => $requestUrl)
- {
- $this->_testApiUrl( $testName . $testSuffix, $apiId, $requestUrl);
+ isset($params['format']) ? $params['format'] : 'xml',
+ isset($params['idSite']) ? $params['idSite'] : false,
+ isset($params['date']) ? $params['date'] : false,
+ isset($params['periods']) ? $params['periods'] : false,
+ isset($params['setDateLastN']) ? $params['setDateLastN'] : false,
+ isset($params['language']) ? $params['language'] : false,
+ isset($params['segment']) ? $params['segment'] : false,
+ isset($params['visitorId']) ? $params['visitorId'] : false,
+ isset($params['abandonedCarts']) ? $params['abandonedCarts'] : false,
+ isset($params['idGoal']) ? $params['idGoal'] : false,
+ isset($params['apiModule']) ? $params['apiModule'] : false,
+ isset($params['apiAction']) ? $params['apiAction'] : false,
+ isset($params['otherRequestParameters']) ? $params['otherRequestParameters'] : array(),
+ isset($params['supertableApi']) ? $params['supertableApi'] : false,
+ isset($params['fileExtension']) ? $params['fileExtension'] : false);
+
+ foreach ($requestUrls as $apiId => $requestUrl) {
+ $this->_testApiUrl($testName . $testSuffix, $apiId, $requestUrl);
}
// change the language back to en
- if ($this->lastLanguage != 'en')
- {
+ if ($this->lastLanguage != 'en') {
$this->changeLanguage('en');
}
-
- if (!empty($this->missingExpectedFiles))
- {
+
+ if (!empty($this->missingExpectedFiles)) {
$expectedDir = dirname(reset($this->missingExpectedFiles));
$this->markTestIncomplete(" ERROR: Could not find expected API output '"
- . implode("', '", $this->missingExpectedFiles)
- . "'. For new tests, to pass the test, you can copy files from the processed/ directory into"
- . " $expectedDir after checking that the output is valid. %s ");
+ . implode("', '", $this->missingExpectedFiles)
+ . "'. For new tests, to pass the test, you can copy files from the processed/ directory into"
+ . " $expectedDir after checking that the output is valid. %s ");
}
- // Display as one error all sub-failures
- if (!empty($this->comparisonFailures))
- {
- $messages = '';
- $i = 1;
- foreach($this->comparisonFailures as $failure) {
- $msg = $failure->getMessage();
- $msg = strtok($msg, "\n");
- $messages .= "\n#" . $i++ . ": " . $msg;
- }
- $messages .= " \n ";
- print($messages);
- $first = reset($this->comparisonFailures);
- throw $first;
+ // Display as one error all sub-failures
+ if (!empty($this->comparisonFailures)) {
+ $messages = '';
+ $i = 1;
+ foreach ($this->comparisonFailures as $failure) {
+ $msg = $failure->getMessage();
+ $msg = strtok($msg, "\n");
+ $messages .= "\n#" . $i++ . ": " . $msg;
+ }
+ $messages .= " \n ";
+ print($messages);
+ $first = reset($this->comparisonFailures);
+ throw $first;
}
}
@@ -1011,10 +952,9 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
*
* @param string $langId
*/
- protected function changeLanguage( $langId )
+ protected function changeLanguage($langId)
{
- if ($this->lastLanguage != $langId)
- {
+ if ($this->lastLanguage != $langId) {
$_GET['language'] = $langId;
Piwik_Translate::reset();
Piwik_Translate::getInstance()->reloadLanguage($langId);
@@ -1028,96 +968,82 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
*/
public function getPathToTestDirectory()
{
- return dirname(__FILE__).DIRECTORY_SEPARATOR.'Integration';
+ return dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Integration';
}
-
+
/**
* Returns an array associating table names w/ lists of row data.
- *
+ *
* @return array
*/
protected static function getDbTablesWithData()
{
- $result = array();
- foreach (Piwik::getTablesInstalled() as $tableName)
- {
- $result[$tableName] = Piwik_FetchAll("SELECT * FROM $tableName");
- }
- return $result;
+ $result = array();
+ foreach (Piwik::getTablesInstalled() as $tableName) {
+ $result[$tableName] = Piwik_FetchAll("SELECT * FROM $tableName");
+ }
+ return $result;
}
-
+
/**
* Truncates all tables then inserts the data in $tables into each
* mapped table.
- *
+ *
* @param array $tables Array mapping table names with arrays of row data.
*/
- protected static function restoreDbTables( $tables )
+ protected static function restoreDbTables($tables)
+ {
+ // truncate existing tables
+ Piwik::truncateAllTables();
+
+ // insert data
+ $existingTables = Piwik::getTablesInstalled();
+ foreach ($tables as $table => $rows) {
+ // create table if it's an archive table
+ if (strpos($table, 'archive_') !== false && !in_array($table, $existingTables)) {
+ $tableType = strpos($table, 'archive_numeric') !== false ? 'archive_numeric' : 'archive_blob';
+
+ $createSql = Piwik::getTableCreateSql($tableType);
+ $createSql = str_replace(Piwik_Common::prefixTable($tableType), $table, $createSql);
+ Piwik_Query($createSql);
+ }
+
+ if (empty($rows)) {
+ continue;
+ }
+
+ $rowsSql = array();
+ foreach ($rows as $row) {
+ $values = array();
+ foreach ($row as $name => $value) {
+ if (is_null($value)) {
+ $values[] = 'NULL';
+ } else if (is_numeric($value)) {
+ $values[] = $value;
+ } else if (!ctype_print($value)) {
+ $values[] = "x'" . bin2hex(substr($value, 1)) . "'";
+ } else {
+ $values[] = "'$value'";
+ }
+ }
+
+ $rowsSql[] = "(" . implode(',', $values) . ")";
+ }
+
+ $sql = "INSERT INTO $table VALUES " . implode(',', $rowsSql);
+ Piwik_Query($sql);
+ }
+ }
+
+ /**
+ * Drops all archive tables.
+ */
+ public static function deleteArchiveTables()
{
- // truncate existing tables
- Piwik::truncateAllTables();
-
- // insert data
- $existingTables = Piwik::getTablesInstalled();
- foreach ($tables as $table => $rows)
- {
- // create table if it's an archive table
- if (strpos($table, 'archive_') !== false && !in_array($table, $existingTables))
- {
- $tableType = strpos($table, 'archive_numeric') !== false ? 'archive_numeric' : 'archive_blob';
-
- $createSql = Piwik::getTableCreateSql($tableType);
- $createSql = str_replace(Piwik_Common::prefixTable($tableType), $table, $createSql);
- Piwik_Query($createSql);
- }
-
- if (empty($rows))
- {
- continue;
- }
-
- $rowsSql = array();
- foreach ($rows as $row)
- {
- $values = array();
- foreach ($row as $name => $value)
- {
- if (is_null($value))
- {
- $values[] = 'NULL';
- }
- else if (is_numeric($value))
- {
- $values[] = $value;
- }
- else if (!ctype_print($value))
- {
- $values[] = "x'".bin2hex(substr($value, 1))."'";
- }
- else
- {
- $values[] = "'$value'";
- }
- }
-
- $rowsSql[] = "(".implode(',', $values).")";
- }
-
- $sql = "INSERT INTO $table VALUES ".implode(',', $rowsSql);
- Piwik_Query($sql);
- }
+ foreach (Piwik::getTablesArchivesInstalled() as $table) {
+ Piwik_Query("DROP TABLE IF EXISTS $table");
+ }
+
+ Piwik_TablePartitioning::$tablesAlreadyInstalled = Piwik::getTablesInstalled($forceReload = true);
}
-
- /**
- * Drops all archive tables.
- */
- public static function deleteArchiveTables()
- {
- foreach (Piwik::getTablesArchivesInstalled() as $table)
- {
- Piwik_Query("DROP TABLE IF EXISTS $table");
- }
-
- Piwik_TablePartitioning::$tablesAlreadyInstalled = Piwik::getTablesInstalled($forceReload = true);
- }
}
diff --git a/tests/PHPUnit/MockEventDispatcher.php b/tests/PHPUnit/MockEventDispatcher.php
index a7026df572..a5528e61e0 100644
--- a/tests/PHPUnit/MockEventDispatcher.php
+++ b/tests/PHPUnit/MockEventDispatcher.php
@@ -7,16 +7,17 @@
*/
class MockEventDispatcher extends Event_Dispatcher
{
- private $forcedNotificationObject = false;
+ private $forcedNotificationObject = false;
- function __construct($forcedNotificationObject) {
- $this->forcedNotificationObject = $forcedNotificationObject;
- }
+ function __construct($forcedNotificationObject)
+ {
+ $this->forcedNotificationObject = $forcedNotificationObject;
+ }
- function &postNotification(&$notification, $pending = true, $bubble = true)
- {
- $notification->_notificationObject = $this->forcedNotificationObject;
+ function &postNotification(&$notification, $pending = true, $bubble = true)
+ {
+ $notification->_notificationObject = $this->forcedNotificationObject;
- return $notification;
- }
+ return $notification;
+ }
}
diff --git a/tests/PHPUnit/MockLocationProvider.php b/tests/PHPUnit/MockLocationProvider.php
index c8cbf12363..68c1c43502 100755
--- a/tests/PHPUnit/MockLocationProvider.php
+++ b/tests/PHPUnit/MockLocationProvider.php
@@ -8,46 +8,43 @@
class MockLocationProvider extends Piwik_UserCountry_LocationProvider
{
- public static $locations = array();
- private $currentLocation = 0;
- private $ipToLocations = array();
-
- public function getLocation( $info )
- {
- $ip = $info['ip'];
-
- if (isset($this->ipToLocations[$ip]))
- {
- $result = $this->ipToLocations[$ip];
- }
- else
- {
- $result = self::$locations[$this->currentLocation];
- $this->currentLocation = ($this->currentLocation + 1) % count(self::$locations);
-
- $this->ipToLocations[$ip] = $result;
- }
- $this->completeLocationResult($result);
- return $result;
- }
-
- public function getInfo()
- {
- return array('id' => 'mock_provider', 'title' => 'mock provider', 'description' => 'mock provider');
- }
-
- public function isAvailable()
- {
- return true;
- }
-
- public function isWorking()
- {
- return true;
- }
-
- public function getSupportedLocationInfo()
- {
- return array(); // unimplemented
- }
+ public static $locations = array();
+ private $currentLocation = 0;
+ private $ipToLocations = array();
+
+ public function getLocation($info)
+ {
+ $ip = $info['ip'];
+
+ if (isset($this->ipToLocations[$ip])) {
+ $result = $this->ipToLocations[$ip];
+ } else {
+ $result = self::$locations[$this->currentLocation];
+ $this->currentLocation = ($this->currentLocation + 1) % count(self::$locations);
+
+ $this->ipToLocations[$ip] = $result;
+ }
+ $this->completeLocationResult($result);
+ return $result;
+ }
+
+ public function getInfo()
+ {
+ return array('id' => 'mock_provider', 'title' => 'mock provider', 'description' => 'mock provider');
+ }
+
+ public function isAvailable()
+ {
+ return true;
+ }
+
+ public function isWorking()
+ {
+ return true;
+ }
+
+ public function getSupportedLocationInfo()
+ {
+ return array(); // unimplemented
+ }
}
diff --git a/tests/PHPUnit/MockPiwikOption.php b/tests/PHPUnit/MockPiwikOption.php
index 5ead64c3ae..455638032c 100644
--- a/tests/PHPUnit/MockPiwikOption.php
+++ b/tests/PHPUnit/MockPiwikOption.php
@@ -7,19 +7,20 @@
*/
class MockPiwikOption extends Piwik_Option
{
- private $forcedOptionValue = false;
+ private $forcedOptionValue = false;
- function __construct($forcedOptionValue) {
- $this->forcedOptionValue = $forcedOptionValue;
- }
+ function __construct($forcedOptionValue)
+ {
+ $this->forcedOptionValue = $forcedOptionValue;
+ }
- public function get($name)
- {
- return $this->forcedOptionValue;
- }
+ public function get($name)
+ {
+ return $this->forcedOptionValue;
+ }
- public function set($name, $value, $autoload = 0)
- {
- $this->forcedOptionValue = $value;
- }
+ public function set($name, $value, $autoload = 0)
+ {
+ $this->forcedOptionValue = $value;
+ }
}
diff --git a/tests/PHPUnit/Plugins/ActionsTest.php b/tests/PHPUnit/Plugins/ActionsTest.php
index 71b2036a11..ce3acfcf6e 100644
--- a/tests/PHPUnit/Plugins/ActionsTest.php
+++ b/tests/PHPUnit/Plugins/ActionsTest.php
@@ -23,84 +23,84 @@ class ActionsTests extends PHPUnit_Framework_TestCase
{
return array(
array(
- 'params' => array('name' => 'http://example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => null),
- 'expected' => array('/index'),
+ 'params' => array('name' => 'http://example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => null),
+ 'expected' => array('/index'),
),
array(
- 'params' => array('name' => 'example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 1),
- 'expected' => array('/index'),
+ 'params' => array('name' => 'example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 1),
+ 'expected' => array('/index'),
),
array(
- 'params' => array('name' => 'example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 2),
- 'expected' => array('/index'),
+ 'params' => array('name' => 'example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 2),
+ 'expected' => array('/index'),
),
array(
- 'params' => array('name' => 'example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 3),
- 'expected' => array('/index'),
+ 'params' => array('name' => 'example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 3),
+ 'expected' => array('/index'),
),
array(
- 'params' => array('name' => 'example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 4),
- 'expected' => array('/index'),
+ 'params' => array('name' => 'example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 4),
+ 'expected' => array('/index'),
),
array(
- 'params' => array('name' => 'example.org/path/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 4),
- 'expected' => array('path', '/index'),
+ 'params' => array('name' => 'example.org/path/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 4),
+ 'expected' => array('path', '/index'),
),
array(
- 'params' => array('name' => 'example.org/test/path', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 1),
- 'expected' => array('test', '/path'),
+ 'params' => array('name' => 'example.org/test/path', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 1),
+ 'expected' => array('test', '/path'),
),
array(
- 'params' => array('name' => 'http://example.org/path/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
- 'expected' => array('path', '/index'),
+ 'params' => array('name' => 'http://example.org/path/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
+ 'expected' => array('path', '/index'),
),
array(
- 'params' => array('name' => 'example.org/test/path', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 1),
- 'expected' => array('test', '/path'),
+ 'params' => array('name' => 'example.org/test/path', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL, 'urlPrefix' => 1),
+ 'expected' => array('test', '/path'),
),
array(
- 'params' => array('name' => 'Test / Path', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
- 'expected' => array('Test', '/Path'),
+ 'params' => array('name' => 'Test / Path', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
+ 'expected' => array('Test', '/Path'),
),
array(
- 'params' => array('name' => ' Test trim ', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
- 'expected' => array('/Test trim'),
+ 'params' => array('name' => ' Test trim ', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
+ 'expected' => array('/Test trim'),
),
array(
- 'params' => array('name' => 'Category / Subcategory', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME),
- 'expected' => array('Category', ' Subcategory'),
+ 'params' => array('name' => 'Category / Subcategory', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME),
+ 'expected' => array('Category', ' Subcategory'),
),
array(
- 'params' => array('name' => '/path/index.php?var=test', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME),
- 'expected' => array('path', ' index.php?var=test'),
+ 'params' => array('name' => '/path/index.php?var=test', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME),
+ 'expected' => array('path', ' index.php?var=test'),
),
array(
- 'params' => array('name' => 'http://example.org/path/Default.aspx#anchor', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME),
- 'expected' => array('path', ' Default.aspx#anchor'),
+ 'params' => array('name' => 'http://example.org/path/Default.aspx#anchor', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME),
+ 'expected' => array('path', ' Default.aspx#anchor'),
),
array(
- 'params' => array('name' => '', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME),
- 'expected' => array('Page Name not defined'),
+ 'params' => array('name' => '', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME),
+ 'expected' => array('Page Name not defined'),
),
array(
- 'params' => array('name' => '', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
- 'expected' => array('Page URL not defined'),
+ 'params' => array('name' => '', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL),
+ 'expected' => array('Page URL not defined'),
),
array(
- 'params' => array('name' => 'http://example.org/download.zip', 'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD),
- 'expected' => array('example.org', '/download.zip'),
+ 'params' => array('name' => 'http://example.org/download.zip', 'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD),
+ 'expected' => array('example.org', '/download.zip'),
),
array(
- 'params' => array('name' => 'http://example.org/download/1/', 'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD),
- 'expected' => array('example.org', '/download/1/'),
+ 'params' => array('name' => 'http://example.org/download/1/', 'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD),
+ 'expected' => array('example.org', '/download/1/'),
),
array(
- 'params' => array('name' => 'http://example.org/link', 'type' => Piwik_Tracker_Action::TYPE_OUTLINK),
- 'expected' => array('example.org', '/link'),
+ 'params' => array('name' => 'http://example.org/link', 'type' => Piwik_Tracker_Action::TYPE_OUTLINK),
+ 'expected' => array('example.org', '/link'),
),
array(
- 'params' => array('name' => 'http://example.org/some/path/', 'type' => Piwik_Tracker_Action::TYPE_OUTLINK),
- 'expected' => array('example.org', '/some/path/'),
+ 'params' => array('name' => 'http://example.org/some/path/', 'type' => Piwik_Tracker_Action::TYPE_OUTLINK),
+ 'expected' => array('example.org', '/some/path/'),
),
);
}
@@ -112,8 +112,8 @@ class ActionsTests extends PHPUnit_Framework_TestCase
*/
public function testGetActionExplodedNames($params, $expected)
{
- Piwik_Actions_ArchivingHelper::reloadConfig();
- $processed = Piwik_Actions_ArchivingHelper::getActionExplodedNames($params['name'], $params['type'], (isset($params['urlPrefix'])?$params['urlPrefix']:null));
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+ $processed = Piwik_Actions_ArchivingHelper::getActionExplodedNames($params['name'], $params['type'], (isset($params['urlPrefix']) ? $params['urlPrefix'] : null));
$this->assertEquals($expected, $processed);
}
}
diff --git a/tests/PHPUnit/Plugins/AnonymizeIPTest.php b/tests/PHPUnit/Plugins/AnonymizeIPTest.php
index b319b0351d..a164403130 100644
--- a/tests/PHPUnit/Plugins/AnonymizeIPTest.php
+++ b/tests/PHPUnit/Plugins/AnonymizeIPTest.php
@@ -10,22 +10,23 @@ require_once 'AnonymizeIP/AnonymizeIP.php';
class AnonymizeIPTest extends PHPUnit_Framework_TestCase
{
// IPv4 addresses and expected results
- public function getipv4Addresses() {
+ public function getipv4Addresses()
+ {
return array(
// ip, array( expected0, expected1, expected2, expected3, expected4 ),
- array('0.0.0.0' , array( "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('0.0.0.1' , array( "\x00\x00\x00\x01", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('0.0.0.255' , array( "\x00\x00\x00\xff", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('0.0.1.0' , array( "\x00\x00\x01\x00", "\x00\x00\x01\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('0.0.1.1' , array( "\x00\x00\x01\x01", "\x00\x00\x01\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('0.0.255.255' , array( "\x00\x00\xff\xff", "\x00\x00\xff\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('0.1.0.0' , array( "\x00\x01\x00\x00", "\x00\x01\x00\x00", "\x00\x01\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('0.1.1.1' , array( "\x00\x01\x01\x01", "\x00\x01\x01\x00", "\x00\x01\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('0.255.255.255' , array( "\x00\xff\xff\xff", "\x00\xff\xff\x00", "\x00\xff\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('1.0.0.0' , array( "\x01\x00\x00\x00", "\x01\x00\x00\x00", "\x01\x00\x00\x00", "\x01\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('127.255.255.255' , array( "\x7f\xff\xff\xff", "\x7f\xff\xff\x00", "\x7f\xff\x00\x00", "\x7f\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('128.0.0.0' , array( "\x80\x00\x00\x00", "\x80\x00\x00\x00", "\x80\x00\x00\x00", "\x80\x00\x00\x00", "\x00\x00\x00\x00" )),
- array('255.255.255.255' , array( "\xff\xff\xff\xff", "\xff\xff\xff\x00", "\xff\xff\x00\x00", "\xff\x00\x00\x00", "\x00\x00\x00\x00" )),
+ array('0.0.0.0', array("\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('0.0.0.1', array("\x00\x00\x00\x01", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('0.0.0.255', array("\x00\x00\x00\xff", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('0.0.1.0', array("\x00\x00\x01\x00", "\x00\x00\x01\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('0.0.1.1', array("\x00\x00\x01\x01", "\x00\x00\x01\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('0.0.255.255', array("\x00\x00\xff\xff", "\x00\x00\xff\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('0.1.0.0', array("\x00\x01\x00\x00", "\x00\x01\x00\x00", "\x00\x01\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('0.1.1.1', array("\x00\x01\x01\x01", "\x00\x01\x01\x00", "\x00\x01\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('0.255.255.255', array("\x00\xff\xff\xff", "\x00\xff\xff\x00", "\x00\xff\x00\x00", "\x00\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('1.0.0.0', array("\x01\x00\x00\x00", "\x01\x00\x00\x00", "\x01\x00\x00\x00", "\x01\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('127.255.255.255', array("\x7f\xff\xff\xff", "\x7f\xff\xff\x00", "\x7f\xff\x00\x00", "\x7f\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('128.0.0.0', array("\x80\x00\x00\x00", "\x80\x00\x00\x00", "\x80\x00\x00\x00", "\x80\x00\x00\x00", "\x00\x00\x00\x00")),
+ array('255.255.255.255', array("\xff\xff\xff\xff", "\xff\xff\xff\x00", "\xff\xff\x00\x00", "\xff\x00\x00\x00", "\x00\x00\x00\x00")),
);
}
@@ -37,24 +38,22 @@ class AnonymizeIPTest extends PHPUnit_Framework_TestCase
public function testApplyIPMask($ip, $expected)
{
// each IP is tested with 0 to 4 octets masked
- for($maskLength = 0; $maskLength <= 4; $maskLength++)
- {
+ for ($maskLength = 0; $maskLength <= 4; $maskLength++) {
$res = Piwik_AnonymizeIP::applyIPMask(Piwik_IP::P2N($ip), $maskLength);
- $this->assertEquals($expected[$maskLength], $res, "Got ".bin2hex($res).", Expected " . bin2hex($expected[$maskLength]));
+ $this->assertEquals($expected[$maskLength], $res, "Got " . bin2hex($res) . ", Expected " . bin2hex($expected[$maskLength]));
}
// edge case (bounds check)
$this->assertEquals("\x00\x00\x00\x00", Piwik_AnonymizeIP::applyIPMask(Piwik_IP::P2N($ip), 5));
// mask IPv4 mapped addresses
- for($maskLength = 0; $maskLength <= 4; $maskLength++)
- {
- $res = Piwik_AnonymizeIP::applyIPMask(Piwik_IP::P2N('::ffff:'.$ip), $maskLength);
- $this->assertEquals( $res, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".$expected[$maskLength], "Got ".bin2hex($res).", Expected " . bin2hex($expected[$maskLength]) );
+ for ($maskLength = 0; $maskLength <= 4; $maskLength++) {
+ $res = Piwik_AnonymizeIP::applyIPMask(Piwik_IP::P2N('::ffff:' . $ip), $maskLength);
+ $this->assertEquals($res, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" . $expected[$maskLength], "Got " . bin2hex($res) . ", Expected " . bin2hex($expected[$maskLength]));
}
- $this->assertEquals("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00", Piwik_AnonymizeIP::applyIPMask(Piwik_IP::P2N('::ffff:'.$ip), 5));
+ $this->assertEquals("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00", Piwik_AnonymizeIP::applyIPMask(Piwik_IP::P2N('::ffff:' . $ip), 5));
// edge case (bounds check)
- $this->assertEquals("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", Piwik_AnonymizeIP::applyIPMask(Piwik_IP::P2N('2001::ffff:'.$ip), 17));
+ $this->assertEquals("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", Piwik_AnonymizeIP::applyIPMask(Piwik_IP::P2N('2001::ffff:' . $ip), 17));
}
}
diff --git a/tests/PHPUnit/Plugins/LanguagesManagerTest.php b/tests/PHPUnit/Plugins/LanguagesManagerTest.php
index b58bf7312b..9acc5f3a9e 100755
--- a/tests/PHPUnit/Plugins/LanguagesManagerTest.php
+++ b/tests/PHPUnit/Plugins/LanguagesManagerTest.php
@@ -24,21 +24,21 @@ class Test_LanguagesManager extends PHPUnit_Framework_TestCase
function getTestDataForLanguageFiles()
{
- self::$allLanguages = Piwik_Common::getLanguagesList();
- self::$allCountries = Piwik_Common::getCountriesList();
+ self::$allLanguages = Piwik_Common::getLanguagesList();
+ self::$allCountries = Piwik_Common::getCountriesList();
self::$englishStringsWithParameters = array();
- self::$englishStringsIndexed = array();
- self::$expectedLanguageKeys = array();
- $englishStrings = Piwik_LanguagesManager_API::getInstance()->getTranslationsForLanguage('en');
+ self::$englishStringsIndexed = array();
+ self::$expectedLanguageKeys = array();
+ $englishStrings = Piwik_LanguagesManager_API::getInstance()->getTranslationsForLanguage('en');
foreach ($englishStrings as $englishString) {
$stringLabel = $englishString['label'];
$stringValue = $englishString['value'];
- $count = $this->getCountParametersToReplace($stringValue);
+ $count = $this->getCountParametersToReplace($stringValue);
if ($count > 0) {
self::$englishStringsWithParameters[$stringLabel] = $count;
}
self::$englishStringsIndexed[$stringLabel] = $stringValue;
- self::$expectedLanguageKeys[] = $stringLabel;
+ self::$expectedLanguageKeys[] = $stringLabel;
}
// we also test that none of the language php files outputs any character on the screen (eg. space before the <?php)
@@ -64,11 +64,11 @@ class Test_LanguagesManager extends PHPUnit_Framework_TestCase
{
self::$errors = array();
ob_start();
- $writeCleanedFile = false;
- $strings = Piwik_LanguagesManager_API::getInstance()->getTranslationsForLanguage($language);
- $content = ob_get_flush();
+ $writeCleanedFile = false;
+ $strings = Piwik_LanguagesManager_API::getInstance()->getTranslationsForLanguage($language);
+ $content = ob_get_flush();
$serializedStrings = serialize($strings);
- $invalids = array("<script", 'document.', 'javascript:', 'src=', 'BACKGROUND=', 'onload=');
+ $invalids = array("<script", 'document.', 'javascript:', 'src=', 'BACKGROUND=', 'onload=');
foreach ($invalids as $invalid) {
$this->assertTrue(stripos($serializedStrings, $invalid) === false, "$language: language file containing javascript");
}
@@ -80,31 +80,31 @@ class Test_LanguagesManager extends PHPUnit_Framework_TestCase
$stringLabel = $string['label'];
$stringValue = $string['value'];
- $plugin = substr($stringLabel, 0, strpos($stringLabel, '_'));
+ $plugin = substr($stringLabel, 0, strpos($stringLabel, '_'));
$plugins[$plugin] = true;
// Testing that the translated string is not empty => '',
if (empty($stringValue) || trim($stringValue) === '') {
- $writeCleanedFile = true;
- self::$errors[] = "$language: The string $stringLabel is empty in the translation file, removing the line.";
+ $writeCleanedFile = true;
+ self::$errors[] = "$language: The string $stringLabel is empty in the translation file, removing the line.";
$cleanedStrings[$stringLabel] = false;
} elseif (!in_array($stringLabel, self::$expectedLanguageKeys)
// translation files should not contain 3rd plugin translations, but if they are there, we shall not delete them
// since translators have spent time working on it... at least for now we shall leave them in (until V2 and plugin repository is done)
&& !in_array($plugin, array('GeoIP', 'Forecast', 'EntryPage', 'UserLanguage'))
) {
- $writeCleanedFile = true;
- self::$errors[] = "$language: The string $stringLabel was not found in the English language file, removing the line.";
+ $writeCleanedFile = true;
+ self::$errors[] = "$language: The string $stringLabel was not found in the English language file, removing the line.";
$cleanedStrings[$stringLabel] = false;
} else {
// checking that translated strings have the same number of %s as the english source strings
if (isset(self::$englishStringsWithParameters[$stringLabel])) {
$englishParametersCount = self::$englishStringsWithParameters[$stringLabel];
- $countTranslation = $this->getCountParametersToReplace($stringValue);
+ $countTranslation = $this->getCountParametersToReplace($stringValue);
if ($englishParametersCount != $countTranslation) {
// Write fixed file in given location
// Will trigger a ->fail()
$writeCleanedFile = true;
- self::$errors[] = "$language: The string $stringLabel has $englishParametersCount parameters in English, but $countTranslation in this translation.";
+ self::$errors[] = "$language: The string $stringLabel has $englishParametersCount parameters in English, but $countTranslation in this translation.";
} else {
$cleanedStrings[$stringLabel] = $stringValue;
}
@@ -124,8 +124,8 @@ class Test_LanguagesManager extends PHPUnit_Framework_TestCase
&& strpos($stringLabel, 'UserCountry_') === false
&& $language != 'de'
) {
- $writeCleanedFile = true;
- self::$errors[] = "$language: The string $stringLabel is the same as in English, removing...";
+ $writeCleanedFile = true;
+ self::$errors[] = "$language: The string $stringLabel is the same as in English, removing...";
$cleanedStrings[$stringLabel] = false;
}
// remove excessive line breaks (and leading/trailing whitespace) from translations
@@ -135,8 +135,8 @@ class Test_LanguagesManager extends PHPUnit_Framework_TestCase
$stringNoLineBreak = str_replace(array("\n", "\r"), " ", $stringNoLineBreak);
}
if ($cleanedStrings[$stringLabel] !== $stringNoLineBreak) {
- self::$errors[] = "$language: found unnecessary whitespace in some strings in $stringLabel";
- $writeCleanedFile = true;
+ self::$errors[] = "$language: found unnecessary whitespace in some strings in $stringLabel";
+ $writeCleanedFile = true;
$cleanedStrings[$stringLabel] = $stringNoLineBreak;
}
}
@@ -154,10 +154,10 @@ class Test_LanguagesManager extends PHPUnit_Framework_TestCase
}
if (isset($cleanedStrings[$stringLabel])) {
$currentString = $cleanedStrings[$stringLabel];
- $decoded = Piwik_TranslationWriter::clean($currentString);
+ $decoded = Piwik_TranslationWriter::clean($currentString);
if ($currentString != $decoded) {
- self::$errors[] = "$language: found encoded entities in $stringLabel, converting entities to characters";
- $writeCleanedFile = true;
+ self::$errors[] = "$language: found encoded entities in $stringLabel, converting entities to characters";
+ $writeCleanedFile = true;
$cleanedStrings[$stringLabel] = $decoded;
}
}
@@ -167,9 +167,9 @@ class Test_LanguagesManager extends PHPUnit_Framework_TestCase
if (!empty($cleanedStrings['General_LayoutDirection'])
&& !in_array($cleanedStrings['General_LayoutDirection'], array('rtl', 'ltr'))
) {
- $writeCleanedFile = true;
+ $writeCleanedFile = true;
$cleanedStrings['General_LayoutDirection'] = false;
- self::$errors[] = "$language: General_LayoutDirection must be rtl or ltr";
+ self::$errors[] = "$language: General_LayoutDirection must be rtl or ltr";
}
if ($writeCleanedFile) {
$path = Piwik_TranslationWriter::getTranslationPath($language, 'tmp');
@@ -218,7 +218,7 @@ class Test_LanguagesManager extends PHPUnit_Framework_TestCase
private function getCountParametersToReplace($string)
{
$sprintfParameters = array('%s', '%1$s', '%2$s', '%3$s', '%4$s', '%5$s', '%6$s');
- $count = 0;
+ $count = 0;
foreach ($sprintfParameters as $parameter) {
$count += substr_count($string, $parameter);
}
diff --git a/tests/PHPUnit/Plugins/LoginTest.php b/tests/PHPUnit/Plugins/LoginTest.php
index 5edbf1326b..783d137167 100644
--- a/tests/PHPUnit/Plugins/LoginTest.php
+++ b/tests/PHPUnit/Plugins/LoginTest.php
@@ -12,21 +12,21 @@ class LoginTest extends DatabaseTestCase
public function setUp()
{
parent::setUp();
-
+
// setup the access layer
$pseudoMockAccess = new FakeAccess;
- FakeAccess::setIdSitesView( array(1,2));
- FakeAccess::setIdSitesAdmin( array(3,4));
-
+ FakeAccess::setIdSitesView(array(1, 2));
+ FakeAccess::setIdSitesAdmin(array(3, 4));
+
//finally we set the user as a super user by default
FakeAccess::$superUser = true;
Zend_Registry::set('access', $pseudoMockAccess);
-
+
// we make sure the tests don't depend on the config file content
Piwik_Config::getInstance()->superuser = array(
- 'login'=>'superusertest',
- 'password'=>md5('passwordsuperusertest'),
- 'email'=>'superuser@example.com'
+ 'login' => 'superusertest',
+ 'password' => md5('passwordsuperusertest'),
+ 'email' => 'superuser@example.com'
);
}
@@ -204,12 +204,13 @@ class LoginTest extends DatabaseTestCase
$this->assertEquals(Piwik_Auth_Result::SUCCESS, $rc->getCode());
}
- protected function _setUpUser() {
- $user = array( 'login'=>'user',
- 'password'=>"geqgeagae",
- 'email'=>"test@test.com",
- 'alias'=>"alias");
- Piwik_UsersManager_API::getInstance()->addUser($user['login'],$user['password'] ,$user['email'] ,$user['alias'] );
+ protected function _setUpUser()
+ {
+ $user = array('login' => 'user',
+ 'password' => "geqgeagae",
+ 'email' => "test@test.com",
+ 'alias' => "alias");
+ Piwik_UsersManager_API::getInstance()->addUser($user['login'], $user['password'], $user['email'], $user['alias']);
$password = md5($user['password']);
$user['tokenAuth'] = Piwik_UsersManager_API::getInstance()->getTokenAuth($user['login'], $password);
return $user;
diff --git a/tests/PHPUnit/Plugins/MobileMessagingTest.php b/tests/PHPUnit/Plugins/MobileMessagingTest.php
index ac0b67fa07..94796c5da3 100644
--- a/tests/PHPUnit/Plugins/MobileMessagingTest.php
+++ b/tests/PHPUnit/Plugins/MobileMessagingTest.php
@@ -8,207 +8,207 @@
class MobileMessagingTest extends DatabaseTestCase
{
- protected $idSiteAccess;
+ protected $idSiteAccess;
- public function setUp()
- {
- parent::setUp();
+ public function setUp()
+ {
+ parent::setUp();
- // setup the access layer
- $pseudoMockAccess = new FakeAccess;
- FakeAccess::$superUser = true;
- //finally we set the user as a super user by default
- Zend_Registry::set('access', $pseudoMockAccess);
+ // setup the access layer
+ $pseudoMockAccess = new FakeAccess;
+ FakeAccess::$superUser = true;
+ //finally we set the user as a super user by default
+ Zend_Registry::set('access', $pseudoMockAccess);
- $this->idSiteAccess = Piwik_SitesManager_API::getInstance()->addSite("test","http://test");
+ $this->idSiteAccess = Piwik_SitesManager_API::getInstance()->addSite("test", "http://test");
- Piwik_PluginsManager::getInstance()->loadPlugins( array('PDFReports', 'MobileMessaging', 'MultiSites') );
- Piwik_PluginsManager::getInstance()->installLoadedPlugins();
- }
+ Piwik_PluginsManager::getInstance()->loadPlugins(array('PDFReports', 'MobileMessaging', 'MultiSites'));
+ Piwik_PluginsManager::getInstance()->installLoadedPlugins();
+ }
- /**
- * When the MultiSites plugin is not activated, the SMS content should invite the user to activate it back
- *
- * @group Plugins
- * @group MobileMessaging
- */
- public function testWarnUserViaSMSMultiSitesDeactivated()
- {
- // safety net
- Piwik_PluginsManager::getInstance()->loadPlugins(array('PDFReports', 'MobileMessaging'));
- $this->assertFalse(Piwik_PluginsManager::getInstance()->isPluginActivated('MultiSites'));
+ /**
+ * When the MultiSites plugin is not activated, the SMS content should invite the user to activate it back
+ *
+ * @group Plugins
+ * @group MobileMessaging
+ */
+ public function testWarnUserViaSMSMultiSitesDeactivated()
+ {
+ // safety net
+ Piwik_PluginsManager::getInstance()->loadPlugins(array('PDFReports', 'MobileMessaging'));
+ $this->assertFalse(Piwik_PluginsManager::getInstance()->isPluginActivated('MultiSites'));
- $PdfReportsAPIInstance = Piwik_PDFReports_API::getInstance();
- $reportId = $PdfReportsAPIInstance->addReport(
- $this->idSiteAccess,
- 'description',
- 'month',
- 0,
- 'mobile',
- 'sms',
- array(),
- array("phoneNumbers" => array('33698896656'))
- );
+ $PdfReportsAPIInstance = Piwik_PDFReports_API::getInstance();
+ $reportId = $PdfReportsAPIInstance->addReport(
+ $this->idSiteAccess,
+ 'description',
+ 'month',
+ 0,
+ 'mobile',
+ 'sms',
+ array(),
+ array("phoneNumbers" => array('33698896656'))
+ );
- list($outputFilename, $prettyDate, $websiteName, $additionalFiles) =
- $PdfReportsAPIInstance->generateReport(
- $reportId,
- '01-01-2010',
- 'en',
- 2
- );
+ list($outputFilename, $prettyDate, $websiteName, $additionalFiles) =
+ $PdfReportsAPIInstance->generateReport(
+ $reportId,
+ '01-01-2010',
+ 'en',
+ 2
+ );
- $handle = fopen($outputFilename, "r");
- $contents = fread($handle, filesize($outputFilename));
- fclose($handle);
+ $handle = fopen($outputFilename, "r");
+ $contents = fread($handle, filesize($outputFilename));
+ fclose($handle);
- $this->assertEquals(
- Piwik_Translate('MobileMessaging_MultiSites_Must_Be_Activated'),
- $contents
- );
- }
+ $this->assertEquals(
+ Piwik_Translate('MobileMessaging_MultiSites_Must_Be_Activated'),
+ $contents
+ );
+ }
- /**
- * Dataprovider for testTruncate
- */
- public function getTruncateTestCases()
- {
-
- $stdGSMx459 = str_repeat('a', 459);
-
- $extGSMx229 = str_repeat('€', 229);
-
- $alternatedGSMx153 = str_repeat('a€', 153);
-
- $GSMWithRegExpSpecialChars = $stdGSMx459 . '[\^$.|?*+()';
-
- $UCS2x201 = str_repeat('控', 201);
-
- // appended strings
- $stdGSMAppendedString = 'too long';
- $extGSMAppendedString = '[too long]';
- $UCS2AppendedString = '[控控]';
-
- return array(
-
- // maximum number of standard GSM characters
- array($stdGSMx459, $stdGSMx459, 3, 'N/A'),
-
- // maximum number of extended GSM characters
- array($extGSMx229, $extGSMx229, 3, 'N/A'),
-
- // maximum number of alternated GSM characters
- array($alternatedGSMx153, $alternatedGSMx153, 3, 'N/A'),
-
- // standard GSM, one 'a' too many, appended with standard GSM characters
- array(str_repeat('a', 451) . $stdGSMAppendedString, $stdGSMx459 . 'a', 3, $stdGSMAppendedString),
-
- // standard GSM, one 'a' too many, appended with extended GSM characters
- array(str_repeat('a', 447) . $extGSMAppendedString, $stdGSMx459 . 'a', 3, $extGSMAppendedString),
-
- // standard GSM, one 'a' too many, appended with UCS2 characters
- array(str_repeat('a', 197) . $UCS2AppendedString, $stdGSMx459 . 'a', 3, $UCS2AppendedString),
-
- // extended GSM, one '€' too many, appended with standard GSM characters
- array(str_repeat('€', 225) . $stdGSMAppendedString, $extGSMx229 . '€', 3, $stdGSMAppendedString),
-
- // extended GSM, one '€' too many, appended with extended GSM characters
- array(str_repeat('€', 223) . $extGSMAppendedString, $extGSMx229 . '€', 3, $extGSMAppendedString),
-
- // extended GSM, one '€' too many, appended with UCS2 characters
- array(str_repeat('€', 197) . $UCS2AppendedString, $extGSMx229 . '€', 3, $UCS2AppendedString),
-
- // alternated GSM, one 'a' too many, appended with standard GSM characters
- array(str_repeat('a€', 150) . 'a' . $stdGSMAppendedString, $alternatedGSMx153 . 'a', 3, $stdGSMAppendedString),
-
- // alternated GSM, one 'a' too many, appended with extended GSM characters
- array(str_repeat('a€', 149) . $extGSMAppendedString, $alternatedGSMx153 . 'a', 3, $extGSMAppendedString),
-
- // alternated GSM, one 'a' too many, appended with UCS2 characters
- array(str_repeat('a€', 98) . 'a' . $UCS2AppendedString, $alternatedGSMx153 . 'a', 3, $UCS2AppendedString),
-
- // alternated GSM, one '€' too many, appended with standard GSM characters
- array(str_repeat('a€', 150) . 'a' . $stdGSMAppendedString, $alternatedGSMx153 . '€', 3, $stdGSMAppendedString),
-
- // alternated GSM, one '€' too many, appended with extended GSM characters
- array(str_repeat('a€', 149) . $extGSMAppendedString, $alternatedGSMx153 . '€', 3, $extGSMAppendedString),
-
- // alternated GSM, one '€' too many, appended with UCS2 characters
- array(str_repeat('a€', 98) . 'a' . $UCS2AppendedString, $alternatedGSMx153 . '€', 3, $UCS2AppendedString),
-
- // GSM with RegExp reserved special chars
- array(str_repeat('a', 451) . $stdGSMAppendedString, $GSMWithRegExpSpecialChars, 3, $stdGSMAppendedString),
-
- // maximum number of UCS-2 characters
- array($UCS2x201, $UCS2x201, 3, 'N/A'),
-
- // UCS-2, one '控' too many, appended with UCS2 characters
- array(str_repeat('控', 197) . $UCS2AppendedString, $UCS2x201 . '控', 3, $UCS2AppendedString),
-
- // UCS-2, one '控' too many, appended with standard GSM characters
- array(str_repeat('控', 193) . $stdGSMAppendedString, $UCS2x201 . '控', 3, $stdGSMAppendedString),
- );
- }
-
- /**
- * @group Plugins
- * @group MobileMessaging
- * @dataProvider getTruncateTestCases
- */
- public function testTruncate($expected, $stringToTruncate, $maximumNumberOfConcatenatedSMS, $appendedString)
- {
- $this->assertEquals(
- $expected,
- Piwik_MobileMessaging_SMSProvider::truncate($stringToTruncate, $maximumNumberOfConcatenatedSMS, $appendedString)
- );
- }
-
-
- /**
- * Dataprovider for testContainsUCS2Characters
- */
- public function getContainsUCS2CharactersTestCases()
- {
- return array(
- array(false, 'too long'),
- array(false, '[too long]'),
- array(false, '€'),
- array(true, '[控控]'),
- );
- }
-
- /**
- * @group Plugins
- * @group MobileMessaging
- * @dataProvider getContainsUCS2CharactersTestCases
- */
- public function testContainsUCS2Characters($expected, $stringToTest)
- {
- $this->assertEquals(
- $expected,
- Piwik_MobileMessaging_SMSProvider::containsUCS2Characters($stringToTest)
- );
- }
-
- /**
- * @group Plugins
- * @group MobileMessaging
- */
- public function testSanitizePhoneNumber()
- {
- $this->assertEquals('676932647', Piwik_MobileMessaging_API::sanitizePhoneNumber(' 6 76 93 26 47'));
- }
-
- /**
- * @group Plugins
- * @group MobileMessaging
- */
- public function testPhoneNumberIsSanitized()
- {
- $mobileMessagingAPI = new Piwik_MobileMessaging_API();
- $mobileMessagingAPI->setSMSAPICredential('StubbedProvider', '');
- $mobileMessagingAPI->addPhoneNumber(' 6 76 93 26 47');
- $this->assertEquals('676932647', key($mobileMessagingAPI->getPhoneNumbers()));
- }
+ /**
+ * Dataprovider for testTruncate
+ */
+ public function getTruncateTestCases()
+ {
+
+ $stdGSMx459 = str_repeat('a', 459);
+
+ $extGSMx229 = str_repeat('€', 229);
+
+ $alternatedGSMx153 = str_repeat('a€', 153);
+
+ $GSMWithRegExpSpecialChars = $stdGSMx459 . '[\^$.|?*+()';
+
+ $UCS2x201 = str_repeat('控', 201);
+
+ // appended strings
+ $stdGSMAppendedString = 'too long';
+ $extGSMAppendedString = '[too long]';
+ $UCS2AppendedString = '[控控]';
+
+ return array(
+
+ // maximum number of standard GSM characters
+ array($stdGSMx459, $stdGSMx459, 3, 'N/A'),
+
+ // maximum number of extended GSM characters
+ array($extGSMx229, $extGSMx229, 3, 'N/A'),
+
+ // maximum number of alternated GSM characters
+ array($alternatedGSMx153, $alternatedGSMx153, 3, 'N/A'),
+
+ // standard GSM, one 'a' too many, appended with standard GSM characters
+ array(str_repeat('a', 451) . $stdGSMAppendedString, $stdGSMx459 . 'a', 3, $stdGSMAppendedString),
+
+ // standard GSM, one 'a' too many, appended with extended GSM characters
+ array(str_repeat('a', 447) . $extGSMAppendedString, $stdGSMx459 . 'a', 3, $extGSMAppendedString),
+
+ // standard GSM, one 'a' too many, appended with UCS2 characters
+ array(str_repeat('a', 197) . $UCS2AppendedString, $stdGSMx459 . 'a', 3, $UCS2AppendedString),
+
+ // extended GSM, one '€' too many, appended with standard GSM characters
+ array(str_repeat('€', 225) . $stdGSMAppendedString, $extGSMx229 . '€', 3, $stdGSMAppendedString),
+
+ // extended GSM, one '€' too many, appended with extended GSM characters
+ array(str_repeat('€', 223) . $extGSMAppendedString, $extGSMx229 . '€', 3, $extGSMAppendedString),
+
+ // extended GSM, one '€' too many, appended with UCS2 characters
+ array(str_repeat('€', 197) . $UCS2AppendedString, $extGSMx229 . '€', 3, $UCS2AppendedString),
+
+ // alternated GSM, one 'a' too many, appended with standard GSM characters
+ array(str_repeat('a€', 150) . 'a' . $stdGSMAppendedString, $alternatedGSMx153 . 'a', 3, $stdGSMAppendedString),
+
+ // alternated GSM, one 'a' too many, appended with extended GSM characters
+ array(str_repeat('a€', 149) . $extGSMAppendedString, $alternatedGSMx153 . 'a', 3, $extGSMAppendedString),
+
+ // alternated GSM, one 'a' too many, appended with UCS2 characters
+ array(str_repeat('a€', 98) . 'a' . $UCS2AppendedString, $alternatedGSMx153 . 'a', 3, $UCS2AppendedString),
+
+ // alternated GSM, one '€' too many, appended with standard GSM characters
+ array(str_repeat('a€', 150) . 'a' . $stdGSMAppendedString, $alternatedGSMx153 . '€', 3, $stdGSMAppendedString),
+
+ // alternated GSM, one '€' too many, appended with extended GSM characters
+ array(str_repeat('a€', 149) . $extGSMAppendedString, $alternatedGSMx153 . '€', 3, $extGSMAppendedString),
+
+ // alternated GSM, one '€' too many, appended with UCS2 characters
+ array(str_repeat('a€', 98) . 'a' . $UCS2AppendedString, $alternatedGSMx153 . '€', 3, $UCS2AppendedString),
+
+ // GSM with RegExp reserved special chars
+ array(str_repeat('a', 451) . $stdGSMAppendedString, $GSMWithRegExpSpecialChars, 3, $stdGSMAppendedString),
+
+ // maximum number of UCS-2 characters
+ array($UCS2x201, $UCS2x201, 3, 'N/A'),
+
+ // UCS-2, one '控' too many, appended with UCS2 characters
+ array(str_repeat('控', 197) . $UCS2AppendedString, $UCS2x201 . '控', 3, $UCS2AppendedString),
+
+ // UCS-2, one '控' too many, appended with standard GSM characters
+ array(str_repeat('控', 193) . $stdGSMAppendedString, $UCS2x201 . '控', 3, $stdGSMAppendedString),
+ );
+ }
+
+ /**
+ * @group Plugins
+ * @group MobileMessaging
+ * @dataProvider getTruncateTestCases
+ */
+ public function testTruncate($expected, $stringToTruncate, $maximumNumberOfConcatenatedSMS, $appendedString)
+ {
+ $this->assertEquals(
+ $expected,
+ Piwik_MobileMessaging_SMSProvider::truncate($stringToTruncate, $maximumNumberOfConcatenatedSMS, $appendedString)
+ );
+ }
+
+
+ /**
+ * Dataprovider for testContainsUCS2Characters
+ */
+ public function getContainsUCS2CharactersTestCases()
+ {
+ return array(
+ array(false, 'too long'),
+ array(false, '[too long]'),
+ array(false, '€'),
+ array(true, '[控控]'),
+ );
+ }
+
+ /**
+ * @group Plugins
+ * @group MobileMessaging
+ * @dataProvider getContainsUCS2CharactersTestCases
+ */
+ public function testContainsUCS2Characters($expected, $stringToTest)
+ {
+ $this->assertEquals(
+ $expected,
+ Piwik_MobileMessaging_SMSProvider::containsUCS2Characters($stringToTest)
+ );
+ }
+
+ /**
+ * @group Plugins
+ * @group MobileMessaging
+ */
+ public function testSanitizePhoneNumber()
+ {
+ $this->assertEquals('676932647', Piwik_MobileMessaging_API::sanitizePhoneNumber(' 6 76 93 26 47'));
+ }
+
+ /**
+ * @group Plugins
+ * @group MobileMessaging
+ */
+ public function testPhoneNumberIsSanitized()
+ {
+ $mobileMessagingAPI = new Piwik_MobileMessaging_API();
+ $mobileMessagingAPI->setSMSAPICredential('StubbedProvider', '');
+ $mobileMessagingAPI->addPhoneNumber(' 6 76 93 26 47');
+ $this->assertEquals('676932647', key($mobileMessagingAPI->getPhoneNumbers()));
+ }
}
diff --git a/tests/PHPUnit/Plugins/MultiSitesTest.php b/tests/PHPUnit/Plugins/MultiSitesTest.php
index 9052c4c4dc..8305809dc5 100644
--- a/tests/PHPUnit/Plugins/MultiSitesTest.php
+++ b/tests/PHPUnit/Plugins/MultiSitesTest.php
@@ -8,36 +8,36 @@
class MultiSitesTest extends DatabaseTestCase
{
- protected $idSiteAccess;
-
- public function setUp()
- {
- parent::setUp();
-
- $access = new Piwik_Access();
- Zend_Registry::set('access', $access);
- $access->setSuperUser(true);
-
- $this->idSiteAccess = Piwik_SitesManager_API::getInstance()->addSite("test","http://test");
-
- Piwik_PluginsManager::getInstance()->loadPlugins( array('MultiSites','VisitsSummary','Actions') );
- Piwik_PluginsManager::getInstance()->installLoadedPlugins();
- }
-
-
- /**
- * Testing that getOne returns a row even when there are no data
- * This is necessary otherwise Piwik_API_ResponseBuilder throws 'Call to a member function getColumns() on a non-object'
- *
- * @group Plugins
- * @group MultiSites
- */
- public function testWhenNoDataGetOneReturnsRow()
- {
- $dataTable = Piwik_MultiSites_API::getInstance()->getOne($this->idSiteAccess, 'month', '01-01-2010');
- $this->assertEquals(1, $dataTable->getRowsCount());
-
- // safety net
- $this->assertEquals(0, $dataTable->getFirstRow()->getColumn('nb_visits'));
- }
+ protected $idSiteAccess;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $access = new Piwik_Access();
+ Zend_Registry::set('access', $access);
+ $access->setSuperUser(true);
+
+ $this->idSiteAccess = Piwik_SitesManager_API::getInstance()->addSite("test", "http://test");
+
+ Piwik_PluginsManager::getInstance()->loadPlugins(array('MultiSites', 'VisitsSummary', 'Actions'));
+ Piwik_PluginsManager::getInstance()->installLoadedPlugins();
+ }
+
+
+ /**
+ * Testing that getOne returns a row even when there are no data
+ * This is necessary otherwise Piwik_API_ResponseBuilder throws 'Call to a member function getColumns() on a non-object'
+ *
+ * @group Plugins
+ * @group MultiSites
+ */
+ public function testWhenNoDataGetOneReturnsRow()
+ {
+ $dataTable = Piwik_MultiSites_API::getInstance()->getOne($this->idSiteAccess, 'month', '01-01-2010');
+ $this->assertEquals(1, $dataTable->getRowsCount());
+
+ // safety net
+ $this->assertEquals(0, $dataTable->getFirstRow()->getColumn('nb_visits'));
+ }
}
diff --git a/tests/PHPUnit/Plugins/PDFReportsTest.php b/tests/PHPUnit/Plugins/PDFReportsTest.php
index e619fc7913..31320e36e2 100644
--- a/tests/PHPUnit/Plugins/PDFReportsTest.php
+++ b/tests/PHPUnit/Plugins/PDFReportsTest.php
@@ -9,470 +9,465 @@ require_once 'PDFReports/PDFReports.php';
class PDFReportsTest extends DatabaseTestCase
{
- private $idSite = 1;
-
- public function setUp()
- {
- parent::setUp();
-
- // setup the access layer
- self::setSuperUser();
- Piwik_PluginsManager::getInstance()->loadPlugins(array('API', 'UserCountry', 'PDFReports', 'MobileMessaging'));
- Piwik_PluginsManager::getInstance()->installLoadedPlugins();
-
- Piwik_SitesManager_API::getInstance()->addSite("Test", array("http://piwik.net"));
-
- Piwik_SitesManager_API::getInstance()->addSite("Test", array("http://piwik.net"));
- FakeAccess::setIdSitesView(array($this->idSite, 2));
- Piwik_PDFReports_API::$cache = array();
- }
-
- /**
- * @group Plugins
- * @group PDFReports
- */
- public function testAddReportGetReports()
- {
- $data = array(
- 'idsite' => $this->idSite,
- 'description' => 'test description"',
- 'type' => 'email',
- 'period' => Piwik_ScheduledTime::PERIOD_DAY,
- 'hour' => '4',
- 'format' => 'pdf',
- 'reports' => array('UserCountry_getCountry'),
- 'parameters' => array(
- 'displayFormat' => '1',
- 'emailMe' => true,
- 'additionalEmails' => array('test@test.com', 't2@test.com'),
- 'evolutionGraph' => true
- )
- );
-
- $dataWebsiteTwo = $data;
- $dataWebsiteTwo['idsite'] = 2;
- $dataWebsiteTwo['period'] = Piwik_ScheduledTime::PERIOD_MONTH;
-
- self::addReport($dataWebsiteTwo);
-
- // Testing getReports without parameters
- $tmp = Piwik_PDFReports_API::getInstance()->getReports();
- $report = reset($tmp);
- $this->assertReportsEqual($report, $dataWebsiteTwo);
-
- $idReport = self::addReport($data);
-
- // Passing 3 parameters
- $tmp = Piwik_PDFReports_API::getInstance()->getReports($this->idSite, $data['period'], $idReport);
- $report = reset($tmp);
- $this->assertReportsEqual($report, $data);
-
- // Passing only idsite
- $tmp = Piwik_PDFReports_API::getInstance()->getReports($this->idSite);
- $report = reset($tmp);
- $this->assertReportsEqual($report, $data);
-
- // Passing only period
- $tmp = Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $data['period']);
- $report = reset($tmp);
- $this->assertReportsEqual($report, $data);
-
- // Passing only idreport
- $tmp = Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport);
- $report = reset($tmp);
- $this->assertReportsEqual($report, $data);
- }
-
- /**
- * @group Plugins
- * @group PDFReports
- */
- public function testGetReportsIdReportNotFound()
- {
- try
- {
- Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport = 1);
- } catch (Exception $e) {
- return;
- }
- $this->fail('Expected exception not raised');
- }
-
- /**
- * @group Plugins
- * @group PDFReports
- */
- public function testGetReportsInvalidPermission()
- {
- try
- {
- Piwik_PDFReports_API::getInstance()->getReports(
- $idSite = 44,
- $period = false,
- self::addReport(self::getDailyPDFReportData($this->idSite))
- );
-
- } catch (Exception $e) {
- return;
- }
- $this->fail('Expected exception not raised');
- }
-
- /**
- * @group Plugins
- * @group PDFReports
- */
- public function testAddReportInvalidWebsite()
- {
- try
- {
- self::addReport(self::getDailyPDFReportData(33));
- } catch (Exception $e) {
- return;
- }
- $this->fail('Expected exception not raised');
- }
-
- /**
- * @group Plugins
- * @group PDFReports
- */
- public function testAddReportInvalidPeriod()
- {
- try
- {
- $data = self::getDailyPDFReportData($this->idSite);
- $data['period'] = 'dx';
- self::addReport($data);
- } catch (Exception $e) {
- return;
- }
- $this->fail('Expected exception not raised');
- }
-
- /**
- * @group Plugins
- * @group PDFReports
- */
- public function testUpdateReport()
- {
- $idReport = self::addReport(self::getDailyPDFReportData($this->idSite));
- $dataAfter = self::getMonthlyEmailReportData($this->idSite);
-
- self::updateReport($idReport, $dataAfter);
-
- $reports = Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport);
-
- $this->assertReportsEqual(
- reset($reports),
- $dataAfter
- );
- }
-
- /**
- * @group Plugins
- * @group PDFReports
- */
- public function testDeleteReport()
- {
- // Deletes non existing report throws exception
- try {
- Piwik_PDFReports_API::getInstance()->deleteReport($idReport = 1);
- $this->fail('Exception not raised');
- } catch (Exception $e) {
- }
-
- $idReport = self::addReport(self::getMonthlyEmailReportData($this->idSite));
- $this->assertEquals(1, count(Piwik_PDFReports_API::getInstance()->getReports()));
- Piwik_PDFReports_API::getInstance()->deleteReport($idReport);
- $this->assertEquals(0, count(Piwik_PDFReports_API::getInstance()->getReports()));
- }
-
- /**
- * @group Plugins
- * @group PDFReports
- */
- public function testGetTopMenuTranslationKeyMobileMessagingInactive()
- {
- // unload MobileMessaging plugin
- Piwik_PluginsManager::getInstance()->loadPlugins(array('PDFReports'));
-
- $pdfReportPlugin = new Piwik_PDFReports();
- $this->assertEquals(
- Piwik_PDFReports::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY,
- $pdfReportPlugin->getTopMenuTranslationKey()
- );
- }
-
- /**
- * @group Plugins
- * @group PDFReports
- */
- public function testGetTopMenuTranslationKeyUserIsAnonymous()
- {
- $anonymousAccess = new FakeAccess;
- FakeAccess::$identity = 'anonymous';
- Zend_Registry::set('access', $anonymousAccess);
-
- $pdfReportPlugin = new Piwik_PDFReports();
- $this->assertEquals(
- Piwik_PDFReports::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY,
- $pdfReportPlugin->getTopMenuTranslationKey()
- );
- }
-
- /**
- * top menu should display 'Email & SMS reports' when the user has set-up a valid mobile provider account
- * even though there is no sms reports configured
- *
- * @group Plugins
- * @group PDFReports
- */
- public function testGetTopMenuTranslationKeyNoReportMobileAccountOK()
- {
- // set mobile provider account
- self::setSuperUser();
- Piwik_MobileMessaging_API::getInstance()->setSMSAPICredential('StubbedProvider', '');
-
- $pdfReportPlugin = new Piwik_PDFReports();
- $this->assertEquals(
- Piwik_PDFReports::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY,
- $pdfReportPlugin->getTopMenuTranslationKey()
- );
- }
-
- /**
- * top menu should display 'Email reports' when the user has not set-up a valid mobile provider account
- * and no reports at all have been configured
- *
- * @group Plugins
- * @group PDFReports
- */
- public function testGetTopMenuTranslationKeyNoReportMobileAccountKO()
- {
- $pdfReportPlugin = new Piwik_PDFReports();
- $this->assertEquals(
- Piwik_PDFReports::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY,
- $pdfReportPlugin->getTopMenuTranslationKey()
- );
- }
-
- /**
- * top menu should display 'Email & SMS reports' if there is at least one sms report
- * whatever the status of the mobile provider account
- *
- * @group Plugins
- * @group PDFReports
- */
- public function testGetTopMenuTranslationKeyOneSMSReportMobileAccountKO()
- {
- Piwik_PDFReports_API::getInstance()->addReport(
- 1,
- '',
- Piwik_ScheduledTime::PERIOD_DAY,
- 0,
- Piwik_MobileMessaging::MOBILE_TYPE,
- Piwik_MobileMessaging::SMS_FORMAT,
- array(),
- array(
- Piwik_MobileMessaging::PHONE_NUMBERS_PARAMETER => array()
- )
- );
-
- $pdfReportPlugin = new Piwik_PDFReports();
- $this->assertEquals(
- Piwik_PDFReports::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY,
- $pdfReportPlugin->getTopMenuTranslationKey()
- );
- }
-
- /**
- * top menu should display 'Email reports' if there are no SMS reports and at least one email report
- * whatever the status of the mobile provider account
- *
- * @group Plugins
- * @group PDFReports
- */
- public function testGetTopMenuTranslationKeyNoSMSReportAccountOK()
- {
- // set mobile provider account
- self::setSuperUser();
- Piwik_MobileMessaging_API::getInstance()->setSMSAPICredential('StubbedProvider', '');
-
- self::addReport(self::getMonthlyEmailReportData($this->idSite));
-
- $pdfReportPlugin = new Piwik_PDFReports();
- $this->assertEquals(
- Piwik_PDFReports::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY,
- $pdfReportPlugin->getTopMenuTranslationKey()
- );
- }
-
- /**
- * @group Plugins
- * @group PDFReports
- */
- public function testGetScheduledTasks()
- {
- // stub Piwik_PDFReports_API to control getReports() return values
- $report1 = self::getDailyPDFReportData($this->idSite);
- $report1['idreport'] = 1;
- $report1['hour'] = 0;
- $report1['deleted'] = 0;
-
- $report2 = self::getMonthlyEmailReportData($this->idSite);
- $report2['idreport'] = 2;
- $report2['idsite'] = 2;
- $report2['hour'] = 0;
- $report2['deleted'] = 0;
-
- $report3 = self::getMonthlyEmailReportData($this->idSite);
- $report3['idreport'] = 3;
- $report3['deleted'] = 1; // should not be scheduled
-
- $report4 = self::getMonthlyEmailReportData($this->idSite);
- $report4['idreport'] = 4;
- $report4['idsite'] = 1;
- $report4['hour'] = 8;
- $report4['deleted'] = 0;
-
- $report5 = self::getMonthlyEmailReportData($this->idSite);
- $report5['idreport'] = 5;
- $report5['idsite'] = 2;
- $report5['hour'] = 8;
- $report5['deleted'] = 0;
-
- // test no exception is raised when a scheduled report is set to never send
- $report6 = self::getMonthlyEmailReportData($this->idSite);
- $report6['idreport'] = 6;
- $report6['period'] = Piwik_ScheduledTime::PERIOD_NEVER;
- $report6['deleted'] = 0;
-
- $stubbedPDFReportsAPI = $this->getMock('Piwik_PDFReports_API');
- $stubbedPDFReportsAPI->expects($this->any())->method('getReports')->will($this->returnValue(
- array($report1, $report2, $report3, $report4, $report5, $report6))
- );
-
- $stubbedPDFReportsAPIClass = new ReflectionProperty('Piwik_PDFReports_API', 'instance');
- $stubbedPDFReportsAPIClass->setAccessible(true);
- $stubbedPDFReportsAPIClass->setValue($stubbedPDFReportsAPI);
-
- // initialize sites 1 and 2
- Piwik_Site::$infoSites = array(
- 1 => array('timezone' => 'Europe/Paris'),
- 2 => array('timezone' => 'UTC-6.5'),
- );
-
- // expected tasks
- $scheduleTask1 = new Piwik_ScheduledTime_Daily();
- $scheduleTask1->setHour(23); // paris is UTC-1, period ends at 23h UTC
-
- $scheduleTask2 = new Piwik_ScheduledTime_Monthly();
- $scheduleTask2->setHour(7); // site is UTC-6.5, period ends at 6h30 UTC, smallest resolution is hour
-
- $scheduleTask3 = new Piwik_ScheduledTime_Monthly();
- $scheduleTask3->setHour(7); // paris is UTC-1, configured to be sent at 8h
-
- $scheduleTask4 = new Piwik_ScheduledTime_Monthly();
- $scheduleTask4->setHour(15); // site is UTC-6.5, configured to be sent at 8h
-
- $expectedTasks = array(
- new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 1, $scheduleTask1),
- new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 2, $scheduleTask2),
- new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 4, $scheduleTask3),
- new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 5, $scheduleTask4),
- );
-
- $pdfReportPlugin = new Piwik_PDFReports();
- $tasks = array();
- $pdfReportPlugin->getScheduledTasks(new Piwik_Event_Notification($tasks, 'fakeEvent'));
- $this->assertEquals($expectedTasks, $tasks);
-
- // restore Piwik_PDFReports_API
- $stubbedPDFReportsAPIClass->setValue(null);
- }
-
- private function assertReportsEqual($report, $data)
- {
- foreach ($data as $key => $value)
- {
- if ($key == 'description') $value = substr($value, 0, 250);
- $this->assertEquals($value, $report[$key], "Error for $key for report " . var_export($report, true) . " and data " . var_export($data, true));
- }
- }
-
- private static function addReport($data)
- {
- $idReport = Piwik_PDFReports_API::getInstance()->addReport(
- $data['idsite'],
- $data['description'],
- $data['period'],
- $data['hour'],
- $data['type'],
- $data['format'],
- $data['reports'],
- $data['parameters']
- );
- return $idReport;
- }
-
- private static function getDailyPDFReportData($idSite)
- {
- return array(
- 'idsite' => $idSite,
- 'description' => 'test description"',
- 'period' => Piwik_ScheduledTime::PERIOD_DAY,
- 'hour' => '7',
- 'type' => 'email',
- 'format' => 'pdf',
- 'reports' => array('UserCountry_getCountry'),
- 'parameters' => array(
- 'displayFormat' => '1',
- 'emailMe' => true,
- 'additionalEmails' => array('test@test.com', 't2@test.com'),
- 'evolutionGraph' => false
- )
- );
- }
-
- private static function getMonthlyEmailReportData($idSite)
- {
- return array(
- 'idsite' => $idSite,
- 'description' => 'very very long and possibly truncated description. very very long and possibly truncated description. very very long and possibly truncated description. very very long and possibly truncated description. very very long and possibly truncated description. ',
- 'period' => Piwik_ScheduledTime::PERIOD_MONTH,
- 'hour' => '0',
- 'type' => 'email',
- 'format' => 'pdf',
- 'reports' => array('UserCountry_getContinent'),
- 'parameters' => array(
- 'displayFormat' => '1',
- 'emailMe' => false,
- 'additionalEmails' => array('blabla@ec.fr'),
- 'evolutionGraph' => false
- )
- );
- }
-
- private static function updateReport($idReport, $data)
- {
- $idReport = Piwik_PDFReports_API::getInstance()->updateReport(
- $idReport,
- $data['idsite'],
- $data['description'],
- $data['period'],
- $data['hour'],
- $data['type'],
- $data['format'],
- $data['reports'],
- $data['parameters']);
- return $idReport;
- }
-
- private static function setSuperUser()
- {
- $pseudoMockAccess = new FakeAccess;
- FakeAccess::$superUser = true;
- Zend_Registry::set('access', $pseudoMockAccess);
- }
+ private $idSite = 1;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ // setup the access layer
+ self::setSuperUser();
+ Piwik_PluginsManager::getInstance()->loadPlugins(array('API', 'UserCountry', 'PDFReports', 'MobileMessaging'));
+ Piwik_PluginsManager::getInstance()->installLoadedPlugins();
+
+ Piwik_SitesManager_API::getInstance()->addSite("Test", array("http://piwik.net"));
+
+ Piwik_SitesManager_API::getInstance()->addSite("Test", array("http://piwik.net"));
+ FakeAccess::setIdSitesView(array($this->idSite, 2));
+ Piwik_PDFReports_API::$cache = array();
+ }
+
+ /**
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testAddReportGetReports()
+ {
+ $data = array(
+ 'idsite' => $this->idSite,
+ 'description' => 'test description"',
+ 'type' => 'email',
+ 'period' => Piwik_ScheduledTime::PERIOD_DAY,
+ 'hour' => '4',
+ 'format' => 'pdf',
+ 'reports' => array('UserCountry_getCountry'),
+ 'parameters' => array(
+ 'displayFormat' => '1',
+ 'emailMe' => true,
+ 'additionalEmails' => array('test@test.com', 't2@test.com'),
+ 'evolutionGraph' => true
+ )
+ );
+
+ $dataWebsiteTwo = $data;
+ $dataWebsiteTwo['idsite'] = 2;
+ $dataWebsiteTwo['period'] = Piwik_ScheduledTime::PERIOD_MONTH;
+
+ self::addReport($dataWebsiteTwo);
+
+ // Testing getReports without parameters
+ $tmp = Piwik_PDFReports_API::getInstance()->getReports();
+ $report = reset($tmp);
+ $this->assertReportsEqual($report, $dataWebsiteTwo);
+
+ $idReport = self::addReport($data);
+
+ // Passing 3 parameters
+ $tmp = Piwik_PDFReports_API::getInstance()->getReports($this->idSite, $data['period'], $idReport);
+ $report = reset($tmp);
+ $this->assertReportsEqual($report, $data);
+
+ // Passing only idsite
+ $tmp = Piwik_PDFReports_API::getInstance()->getReports($this->idSite);
+ $report = reset($tmp);
+ $this->assertReportsEqual($report, $data);
+
+ // Passing only period
+ $tmp = Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $data['period']);
+ $report = reset($tmp);
+ $this->assertReportsEqual($report, $data);
+
+ // Passing only idreport
+ $tmp = Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport);
+ $report = reset($tmp);
+ $this->assertReportsEqual($report, $data);
+ }
+
+ /**
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testGetReportsIdReportNotFound()
+ {
+ try {
+ Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport = 1);
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail('Expected exception not raised');
+ }
+
+ /**
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testGetReportsInvalidPermission()
+ {
+ try {
+ Piwik_PDFReports_API::getInstance()->getReports(
+ $idSite = 44,
+ $period = false,
+ self::addReport(self::getDailyPDFReportData($this->idSite))
+ );
+
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail('Expected exception not raised');
+ }
+
+ /**
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testAddReportInvalidWebsite()
+ {
+ try {
+ self::addReport(self::getDailyPDFReportData(33));
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail('Expected exception not raised');
+ }
+
+ /**
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testAddReportInvalidPeriod()
+ {
+ try {
+ $data = self::getDailyPDFReportData($this->idSite);
+ $data['period'] = 'dx';
+ self::addReport($data);
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail('Expected exception not raised');
+ }
+
+ /**
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testUpdateReport()
+ {
+ $idReport = self::addReport(self::getDailyPDFReportData($this->idSite));
+ $dataAfter = self::getMonthlyEmailReportData($this->idSite);
+
+ self::updateReport($idReport, $dataAfter);
+
+ $reports = Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport);
+
+ $this->assertReportsEqual(
+ reset($reports),
+ $dataAfter
+ );
+ }
+
+ /**
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testDeleteReport()
+ {
+ // Deletes non existing report throws exception
+ try {
+ Piwik_PDFReports_API::getInstance()->deleteReport($idReport = 1);
+ $this->fail('Exception not raised');
+ } catch (Exception $e) {
+ }
+
+ $idReport = self::addReport(self::getMonthlyEmailReportData($this->idSite));
+ $this->assertEquals(1, count(Piwik_PDFReports_API::getInstance()->getReports()));
+ Piwik_PDFReports_API::getInstance()->deleteReport($idReport);
+ $this->assertEquals(0, count(Piwik_PDFReports_API::getInstance()->getReports()));
+ }
+
+ /**
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testGetTopMenuTranslationKeyMobileMessagingInactive()
+ {
+ // unload MobileMessaging plugin
+ Piwik_PluginsManager::getInstance()->loadPlugins(array('PDFReports'));
+
+ $pdfReportPlugin = new Piwik_PDFReports();
+ $this->assertEquals(
+ Piwik_PDFReports::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY,
+ $pdfReportPlugin->getTopMenuTranslationKey()
+ );
+ }
+
+ /**
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testGetTopMenuTranslationKeyUserIsAnonymous()
+ {
+ $anonymousAccess = new FakeAccess;
+ FakeAccess::$identity = 'anonymous';
+ Zend_Registry::set('access', $anonymousAccess);
+
+ $pdfReportPlugin = new Piwik_PDFReports();
+ $this->assertEquals(
+ Piwik_PDFReports::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY,
+ $pdfReportPlugin->getTopMenuTranslationKey()
+ );
+ }
+
+ /**
+ * top menu should display 'Email & SMS reports' when the user has set-up a valid mobile provider account
+ * even though there is no sms reports configured
+ *
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testGetTopMenuTranslationKeyNoReportMobileAccountOK()
+ {
+ // set mobile provider account
+ self::setSuperUser();
+ Piwik_MobileMessaging_API::getInstance()->setSMSAPICredential('StubbedProvider', '');
+
+ $pdfReportPlugin = new Piwik_PDFReports();
+ $this->assertEquals(
+ Piwik_PDFReports::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY,
+ $pdfReportPlugin->getTopMenuTranslationKey()
+ );
+ }
+
+ /**
+ * top menu should display 'Email reports' when the user has not set-up a valid mobile provider account
+ * and no reports at all have been configured
+ *
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testGetTopMenuTranslationKeyNoReportMobileAccountKO()
+ {
+ $pdfReportPlugin = new Piwik_PDFReports();
+ $this->assertEquals(
+ Piwik_PDFReports::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY,
+ $pdfReportPlugin->getTopMenuTranslationKey()
+ );
+ }
+
+ /**
+ * top menu should display 'Email & SMS reports' if there is at least one sms report
+ * whatever the status of the mobile provider account
+ *
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testGetTopMenuTranslationKeyOneSMSReportMobileAccountKO()
+ {
+ Piwik_PDFReports_API::getInstance()->addReport(
+ 1,
+ '',
+ Piwik_ScheduledTime::PERIOD_DAY,
+ 0,
+ Piwik_MobileMessaging::MOBILE_TYPE,
+ Piwik_MobileMessaging::SMS_FORMAT,
+ array(),
+ array(
+ Piwik_MobileMessaging::PHONE_NUMBERS_PARAMETER => array()
+ )
+ );
+
+ $pdfReportPlugin = new Piwik_PDFReports();
+ $this->assertEquals(
+ Piwik_PDFReports::MOBILE_MESSAGING_TOP_MENU_TRANSLATION_KEY,
+ $pdfReportPlugin->getTopMenuTranslationKey()
+ );
+ }
+
+ /**
+ * top menu should display 'Email reports' if there are no SMS reports and at least one email report
+ * whatever the status of the mobile provider account
+ *
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testGetTopMenuTranslationKeyNoSMSReportAccountOK()
+ {
+ // set mobile provider account
+ self::setSuperUser();
+ Piwik_MobileMessaging_API::getInstance()->setSMSAPICredential('StubbedProvider', '');
+
+ self::addReport(self::getMonthlyEmailReportData($this->idSite));
+
+ $pdfReportPlugin = new Piwik_PDFReports();
+ $this->assertEquals(
+ Piwik_PDFReports::PDF_REPORTS_TOP_MENU_TRANSLATION_KEY,
+ $pdfReportPlugin->getTopMenuTranslationKey()
+ );
+ }
+
+ /**
+ * @group Plugins
+ * @group PDFReports
+ */
+ public function testGetScheduledTasks()
+ {
+ // stub Piwik_PDFReports_API to control getReports() return values
+ $report1 = self::getDailyPDFReportData($this->idSite);
+ $report1['idreport'] = 1;
+ $report1['hour'] = 0;
+ $report1['deleted'] = 0;
+
+ $report2 = self::getMonthlyEmailReportData($this->idSite);
+ $report2['idreport'] = 2;
+ $report2['idsite'] = 2;
+ $report2['hour'] = 0;
+ $report2['deleted'] = 0;
+
+ $report3 = self::getMonthlyEmailReportData($this->idSite);
+ $report3['idreport'] = 3;
+ $report3['deleted'] = 1; // should not be scheduled
+
+ $report4 = self::getMonthlyEmailReportData($this->idSite);
+ $report4['idreport'] = 4;
+ $report4['idsite'] = 1;
+ $report4['hour'] = 8;
+ $report4['deleted'] = 0;
+
+ $report5 = self::getMonthlyEmailReportData($this->idSite);
+ $report5['idreport'] = 5;
+ $report5['idsite'] = 2;
+ $report5['hour'] = 8;
+ $report5['deleted'] = 0;
+
+ // test no exception is raised when a scheduled report is set to never send
+ $report6 = self::getMonthlyEmailReportData($this->idSite);
+ $report6['idreport'] = 6;
+ $report6['period'] = Piwik_ScheduledTime::PERIOD_NEVER;
+ $report6['deleted'] = 0;
+
+ $stubbedPDFReportsAPI = $this->getMock('Piwik_PDFReports_API');
+ $stubbedPDFReportsAPI->expects($this->any())->method('getReports')->will($this->returnValue(
+ array($report1, $report2, $report3, $report4, $report5, $report6))
+ );
+
+ $stubbedPDFReportsAPIClass = new ReflectionProperty('Piwik_PDFReports_API', 'instance');
+ $stubbedPDFReportsAPIClass->setAccessible(true);
+ $stubbedPDFReportsAPIClass->setValue($stubbedPDFReportsAPI);
+
+ // initialize sites 1 and 2
+ Piwik_Site::$infoSites = array(
+ 1 => array('timezone' => 'Europe/Paris'),
+ 2 => array('timezone' => 'UTC-6.5'),
+ );
+
+ // expected tasks
+ $scheduleTask1 = new Piwik_ScheduledTime_Daily();
+ $scheduleTask1->setHour(23); // paris is UTC-1, period ends at 23h UTC
+
+ $scheduleTask2 = new Piwik_ScheduledTime_Monthly();
+ $scheduleTask2->setHour(7); // site is UTC-6.5, period ends at 6h30 UTC, smallest resolution is hour
+
+ $scheduleTask3 = new Piwik_ScheduledTime_Monthly();
+ $scheduleTask3->setHour(7); // paris is UTC-1, configured to be sent at 8h
+
+ $scheduleTask4 = new Piwik_ScheduledTime_Monthly();
+ $scheduleTask4->setHour(15); // site is UTC-6.5, configured to be sent at 8h
+
+ $expectedTasks = array(
+ new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 1, $scheduleTask1),
+ new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 2, $scheduleTask2),
+ new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 4, $scheduleTask3),
+ new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 5, $scheduleTask4),
+ );
+
+ $pdfReportPlugin = new Piwik_PDFReports();
+ $tasks = array();
+ $pdfReportPlugin->getScheduledTasks(new Piwik_Event_Notification($tasks, 'fakeEvent'));
+ $this->assertEquals($expectedTasks, $tasks);
+
+ // restore Piwik_PDFReports_API
+ $stubbedPDFReportsAPIClass->setValue(null);
+ }
+
+ private function assertReportsEqual($report, $data)
+ {
+ foreach ($data as $key => $value) {
+ if ($key == 'description') $value = substr($value, 0, 250);
+ $this->assertEquals($value, $report[$key], "Error for $key for report " . var_export($report, true) . " and data " . var_export($data, true));
+ }
+ }
+
+ private static function addReport($data)
+ {
+ $idReport = Piwik_PDFReports_API::getInstance()->addReport(
+ $data['idsite'],
+ $data['description'],
+ $data['period'],
+ $data['hour'],
+ $data['type'],
+ $data['format'],
+ $data['reports'],
+ $data['parameters']
+ );
+ return $idReport;
+ }
+
+ private static function getDailyPDFReportData($idSite)
+ {
+ return array(
+ 'idsite' => $idSite,
+ 'description' => 'test description"',
+ 'period' => Piwik_ScheduledTime::PERIOD_DAY,
+ 'hour' => '7',
+ 'type' => 'email',
+ 'format' => 'pdf',
+ 'reports' => array('UserCountry_getCountry'),
+ 'parameters' => array(
+ 'displayFormat' => '1',
+ 'emailMe' => true,
+ 'additionalEmails' => array('test@test.com', 't2@test.com'),
+ 'evolutionGraph' => false
+ )
+ );
+ }
+
+ private static function getMonthlyEmailReportData($idSite)
+ {
+ return array(
+ 'idsite' => $idSite,
+ 'description' => 'very very long and possibly truncated description. very very long and possibly truncated description. very very long and possibly truncated description. very very long and possibly truncated description. very very long and possibly truncated description. ',
+ 'period' => Piwik_ScheduledTime::PERIOD_MONTH,
+ 'hour' => '0',
+ 'type' => 'email',
+ 'format' => 'pdf',
+ 'reports' => array('UserCountry_getContinent'),
+ 'parameters' => array(
+ 'displayFormat' => '1',
+ 'emailMe' => false,
+ 'additionalEmails' => array('blabla@ec.fr'),
+ 'evolutionGraph' => false
+ )
+ );
+ }
+
+ private static function updateReport($idReport, $data)
+ {
+ $idReport = Piwik_PDFReports_API::getInstance()->updateReport(
+ $idReport,
+ $data['idsite'],
+ $data['description'],
+ $data['period'],
+ $data['hour'],
+ $data['type'],
+ $data['format'],
+ $data['reports'],
+ $data['parameters']);
+ return $idReport;
+ }
+
+ private static function setSuperUser()
+ {
+ $pseudoMockAccess = new FakeAccess;
+ FakeAccess::$superUser = true;
+ Zend_Registry::set('access', $pseudoMockAccess);
+ }
}
diff --git a/tests/PHPUnit/Plugins/PrivacyManagerTest.php b/tests/PHPUnit/Plugins/PrivacyManagerTest.php
index 50aadf3f19..bee4871960 100755
--- a/tests/PHPUnit/Plugins/PrivacyManagerTest.php
+++ b/tests/PHPUnit/Plugins/PrivacyManagerTest.php
@@ -26,10 +26,10 @@ class PrivacyManagerTest extends IntegrationTestCase
private static $idSite = 1;
private static $dateTime = '2012-02-28';
private static $daysAgoStart = 50;
-
+
// maps table names w/ array rows
private static $dbData = null;
-
+
/**
* @var Piwik_PrivacyManager
*/
@@ -42,44 +42,43 @@ 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_ArchiveProcessing_Period::$enablePurgeOutdated = false;
+ // 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_ArchiveProcessing_Period::$enablePurgeOutdated = false;
- self::_addLogData();
- self::_addReportData();
+ self::_addLogData();
+ self::_addReportData();
- Piwik_ArchiveProcessing_Period::$enablePurgeOutdated = true;
+ Piwik_ArchiveProcessing_Period::$enablePurgeOutdated = true;
self::$dbData = self::getDbTablesWithData();
}
-
+
public function setUp()
{
- parent::setUp();
-
+ parent::setUp();
+
Piwik_PrivacyManager_LogDataPurger::$selectSegmentSize = 2;
Piwik_PrivacyManager_ReportsPurger::$selectSegmentSize = 2;
Piwik::$lockPrivilegeGranted = null;
-
+
self::restoreDbTables(self::$dbData);
-
+
$dateTime = Piwik_Date::factory(self::$dateTime);
-
+
// purging depends upon today's date, so 'older_than' parts must be dependent upon today
$today = Piwik_Date::factory('today');
$daysSinceToday = ($today->getTimestamp() - $dateTime->getTimestamp()) / (24 * 60 * 60);
-
+
$monthsSinceToday = 0;
- for ($date = $today; $date->toString('Y-m') != $dateTime->toString('Y-m'); $date = $date->subMonth(1))
- {
+ for ($date = $today; $date->toString('Y-m') != $dateTime->toString('Y-m'); $date = $date->subMonth(1)) {
++$monthsSinceToday;
}
-
+
// set default config
$settings = array();
$settings['delete_logs_enable'] = 1;
-
+
// purging log data from before 2012-01-24
$settings['delete_logs_older_than'] = 35 + $daysSinceToday;
$settings['delete_logs_schedule_lowest_interval'] = 7;
@@ -94,11 +93,11 @@ class PrivacyManagerTest extends IntegrationTestCase
$settings['delete_reports_keep_range_reports'] = 0;
$settings['delete_reports_keep_segment_reports'] = 0;
Piwik_PrivacyManager::savePurgeDataSettings($settings);
-
+
$this->settings = $settings;
$this->instance = new Piwik_PrivacyManager();
}
-
+
public function tearDown()
{
parent::tearDown();
@@ -107,11 +106,11 @@ class PrivacyManagerTest extends IntegrationTestCase
Piwik_Site::clearCache();
Piwik_Tracker_Cache::deleteTrackerCache();
Piwik_TablePartitioning::$tablesAlreadyInstalled = null;
-
+
$tempTableName = Piwik_Common::prefixTable(Piwik_PrivacyManager_LogDataPurger::TEMP_TABLE_NAME);
- Piwik_Query("DROP TABLE IF EXISTS ".$tempTableName);
+ Piwik_Query("DROP TABLE IF EXISTS " . $tempTableName);
}
-
+
/**
* Make sure the first time deleteLogData is run, nothing happens.
*
@@ -120,16 +119,16 @@ class PrivacyManagerTest extends IntegrationTestCase
*/
public function testDeleteLogDataInitialRun()
{
- $this->instance->deleteLogData();
+ $this->instance->deleteLogData();
// check that initial option is set
$this->assertEquals(
1, Piwik_GetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL));
-
+
// perform other checks
$this->_checkNoDataChanges();
}
-
+
/**
* Make sure the first time deleteReportData is run, nothing happens.
*
@@ -139,14 +138,14 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testDeleteReportDataInitialRun()
{
$this->instance->deleteReportData();
-
+
// check that initial option is set
$this->assertEquals(1, Piwik_GetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL));
-
+
// perform other checks
$this->_checkNoDataChanges();
}
-
+
/**
* Make sure the task is not run when its scheduled for later.
*
@@ -156,17 +155,17 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testPurgeDataNotTimeToRun()
{
$yesterdaySecs = Piwik_Date::factory('yesterday')->getTimestamp();
-
+
Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS, $yesterdaySecs);
Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_REPORTS, $yesterdaySecs);
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->_checkNoDataChanges();
}
-
+
/**
* Make sure purging data runs when scheduled.
*
@@ -177,43 +176,43 @@ class PrivacyManagerTest extends IntegrationTestCase
{
// get purge data prediction
$prediction = Piwik_PrivacyManager::getPurgeEstimate();
-
+
// perform checks on prediction
$expectedPrediction = array(
- Piwik_Common::prefixTable('log_conversion') => 6,
- Piwik_Common::prefixTable('log_link_visit_action') => 6,
- Piwik_Common::prefixTable('log_visit') => 3,
- Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
- Piwik_Common::prefixTable('archive_blob_2012_01') => -1
+ Piwik_Common::prefixTable('archive_blob_2012_01') => -1
);
$this->assertEquals($expectedPrediction, $prediction);
-
+
// purge data
$this->_setTimeToRun();
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->checkLogDataPurged();
-
+
$archiveTables = self::_getArchiveTableNames();
-
+
// January numeric table should be dropped
$this->assertFalse($this->_tableExists($archiveTables['numeric'][0])); // January
-
+
// Check february metric count (5 metrics per period w/ visits + 1 'done' archive for every period)
// + 1 garbage metric
$febRowCount = self::FEB_METRIC_ARCHIVE_COUNT * 5 + self::TOTAL_FEB_ARCHIVE_COUNT + 1;
$this->assertEquals($febRowCount, $this->_getTableCount($archiveTables['numeric'][1])); // February
-
+
// January blob table should be dropped
$this->assertFalse($this->_tableExists($archiveTables['blob'][0])); // January
-
+
// Check february blob count (1 blob per period w/ visits + 1 garbage report)
$this->assertEquals(self::FEB_METRIC_ARCHIVE_COUNT + 1, $this->_getTableCount($archiveTables['blob'][1])); // February
}
-
+
/**
* Make sure nothing happens when deleting logs & reports are both disabled.
*
@@ -223,26 +222,26 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testPurgeDataBothDisabled()
{
Piwik_PrivacyManager::savePurgeDataSettings(array(
- 'delete_logs_enable' => 0,
- 'delete_reports_enable' => 0
- ));
+ 'delete_logs_enable' => 0,
+ 'delete_reports_enable' => 0
+ ));
// get purge data prediction
$prediction = Piwik_PrivacyManager::getPurgeEstimate();
-
+
// perform checks on prediction
$expectedPrediction = array();
$this->assertEquals($expectedPrediction, $prediction);
-
+
// purge data
$this->_setTimeToRun();
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->_checkNoDataChanges();
}
-
+
/**
* Test that purgeData works when there's no data.
*
@@ -251,37 +250,36 @@ class PrivacyManagerTest extends IntegrationTestCase
*/
public function testPurgeDataDeleteLogsNoData()
{
- Piwik::truncateAllTables();
- foreach (Piwik::getTablesArchivesInstalled() as $table)
- {
- Piwik_Exec("DROP TABLE $table");
- }
-
+ Piwik::truncateAllTables();
+ foreach (Piwik::getTablesArchivesInstalled() as $table) {
+ Piwik_Exec("DROP TABLE $table");
+ }
+
// get purge data prediction
$prediction = Piwik_PrivacyManager::getPurgeEstimate();
-
+
// perform checks on prediction
$expectedPrediction = array();
$this->assertEquals($expectedPrediction, $prediction);
-
+
// purge data
$this->_setTimeToRun();
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->assertEquals(0, $this->_getTableCount('log_visit'));
$this->assertEquals(0, $this->_getTableCount('log_conversion'));
$this->assertEquals(0, $this->_getTableCount('log_link_visit_action'));
$this->assertEquals(0, $this->_getTableCount('log_conversion_item'));
-
+
$archiveTables = self::_getArchiveTableNames();
$this->assertFalse($this->_tableExists($archiveTables['numeric'][0])); // January
$this->assertFalse($this->_tableExists($archiveTables['numeric'][1])); // February
$this->assertFalse($this->_tableExists($archiveTables['blob'][0])); // January
$this->assertFalse($this->_tableExists($archiveTables['blob'][1])); // February
}
-
+
/**
* Test that purgeData works correctly when the 'keep basic metrics' setting is set to true.
*
@@ -291,48 +289,48 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testPurgeDataDeleteReportsKeepBasicMetrics()
{
Piwik_PrivacyManager::savePurgeDataSettings(array(
- 'delete_reports_keep_basic_metrics' => 1
- ));
-
+ 'delete_reports_keep_basic_metrics' => 1
+ ));
+
// get purge data prediction
$prediction = Piwik_PrivacyManager::getPurgeEstimate();
-
+
// perform checks on prediction
$expectedPrediction = array(
- Piwik_Common::prefixTable('log_conversion') => 6,
- Piwik_Common::prefixTable('log_link_visit_action') => 6,
- Piwik_Common::prefixTable('log_visit') => 3,
- Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
Piwik_Common::prefixTable('archive_numeric_2012_01') => 1, // remove the garbage metric
- Piwik_Common::prefixTable('archive_blob_2012_01') => -1
+ Piwik_Common::prefixTable('archive_blob_2012_01') => -1
);
$this->assertEquals($expectedPrediction, $prediction);
-
+
// purge data
$this->_setTimeToRun();
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->checkLogDataPurged();
-
+
$archiveTables = self::_getArchiveTableNames();
-
+
// all numeric metrics should be saved except the garbage metric
$janRowCount = $this->_getExpectedNumericArchiveCountJan() - 1;
$this->assertEquals($janRowCount, $this->_getTableCount($archiveTables['numeric'][0])); // January
-
+
// check february numerics not deleted
$febRowCount = $this->_getExpectedNumericArchiveCountFeb();
$this->assertEquals($febRowCount, $this->_getTableCount($archiveTables['numeric'][1])); // February
-
+
// check that the january blob table was dropped
$this->assertFalse($this->_tableExists($archiveTables['blob'][0])); // January
-
+
// check for no changes in the february blob table
$this->assertEquals(self::FEB_METRIC_ARCHIVE_COUNT + 1, $this->_getTableCount($archiveTables['blob'][1])); // February
}
-
+
/**
* Test that purgeData works correctly when the 'keep daily reports' setting is set to true.
*
@@ -342,33 +340,33 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testPurgeDataDeleteReportsKeepDailyReports()
{
Piwik_PrivacyManager::savePurgeDataSettings(array(
- 'delete_reports_keep_day_reports' => 1
- ));
+ 'delete_reports_keep_day_reports' => 1
+ ));
// get purge data prediction
$prediction = Piwik_PrivacyManager::getPurgeEstimate();
-
+
// perform checks on prediction
$expectedPrediction = array(
- Piwik_Common::prefixTable('log_conversion') => 6,
- Piwik_Common::prefixTable('log_link_visit_action') => 6,
- Piwik_Common::prefixTable('log_visit') => 3,
- Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
- Piwik_Common::prefixTable('archive_blob_2012_01') => 10 // removing 4 weeks, 1 month & 1 year + 1 garbage report + 2 range reports + 1 segmented report
+ Piwik_Common::prefixTable('archive_blob_2012_01') => 10 // removing 4 weeks, 1 month & 1 year + 1 garbage report + 2 range reports + 1 segmented report
);
$this->assertEquals($expectedPrediction, $prediction);
-
+
// purge data
$this->_setTimeToRun();
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->checkLogDataPurged();
$this->_checkReportsAndMetricsPurged($janBlobsRemaining = 5); // 5 blobs for 5 days
}
-
+
/**
* Test that purgeData works correctly when the 'keep weekly reports' setting is set to true.
*
@@ -378,33 +376,33 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testPurgeDataDeleteReportsKeepWeeklyReports()
{
Piwik_PrivacyManager::savePurgeDataSettings(array(
- 'delete_reports_keep_week_reports' => 1
- ));
+ 'delete_reports_keep_week_reports' => 1
+ ));
// get purge data prediction
$prediction = Piwik_PrivacyManager::getPurgeEstimate();
-
+
// perform checks on prediction
$expectedPrediction = array(
- Piwik_Common::prefixTable('log_conversion') => 6,
- Piwik_Common::prefixTable('log_link_visit_action') => 6,
- Piwik_Common::prefixTable('log_visit') => 3,
- Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
- Piwik_Common::prefixTable('archive_blob_2012_01') => 11 // 5 days, 1 month & 1 year to remove + 1 garbage report + 2 range reports + 1 segmented report
+ Piwik_Common::prefixTable('archive_blob_2012_01') => 11 // 5 days, 1 month & 1 year to remove + 1 garbage report + 2 range reports + 1 segmented report
);
$this->assertEquals($expectedPrediction, $prediction);
-
+
// purge data
$this->_setTimeToRun();
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->checkLogDataPurged();
$this->_checkReportsAndMetricsPurged($janBlobsRemaining = 4); // 4 blobs for 4 weeks
}
-
+
/**
* Test that purgeData works correctly when the 'keep monthly reports' setting is set to true.
*
@@ -414,33 +412,33 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testPurgeDataDeleteReportsKeepMonthlyReports()
{
Piwik_PrivacyManager::savePurgeDataSettings(array(
- 'delete_reports_keep_month_reports' => 1
- ));
+ 'delete_reports_keep_month_reports' => 1
+ ));
// get purge data prediction
$prediction = Piwik_PrivacyManager::getPurgeEstimate();
-
+
// perform checks on prediction
$expectedPrediction = array(
- Piwik_Common::prefixTable('log_conversion') => 6,
- Piwik_Common::prefixTable('log_link_visit_action') => 6,
- Piwik_Common::prefixTable('log_visit') => 3,
- Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
- Piwik_Common::prefixTable('archive_blob_2012_01') => 14 // 5 days, 4 weeks, 1 year to remove + 1 garbage report + 2 range reports + 1 segmented report
+ Piwik_Common::prefixTable('archive_blob_2012_01') => 14 // 5 days, 4 weeks, 1 year to remove + 1 garbage report + 2 range reports + 1 segmented report
);
$this->assertEquals($expectedPrediction, $prediction);
-
+
// purge data
$this->_setTimeToRun();
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->checkLogDataPurged();
$this->_checkReportsAndMetricsPurged($janBlobsRemaining = 1); // 1 blob for 1 month
}
-
+
/**
* Test that purgeData works correctly when the 'keep yearly reports' setting is set to true.
*
@@ -450,33 +448,33 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testPurgeDataDeleteReportsKeepYearlyReports()
{
Piwik_PrivacyManager::savePurgeDataSettings(array(
- 'delete_reports_keep_year_reports' => 1
- ));
+ 'delete_reports_keep_year_reports' => 1
+ ));
// get purge data prediction
$prediction = Piwik_PrivacyManager::getPurgeEstimate();
-
+
// perform checks on prediction
$expectedPrediction = array(
- Piwik_Common::prefixTable('log_conversion') => 6,
- Piwik_Common::prefixTable('log_link_visit_action') => 6,
- Piwik_Common::prefixTable('log_visit') => 3,
- Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
- Piwik_Common::prefixTable('archive_blob_2012_01') => 14 // 5 days, 4 weeks & 1 year to remove + 1 garbage report + 2 range reports + 1 segmented report
+ Piwik_Common::prefixTable('archive_blob_2012_01') => 14 // 5 days, 4 weeks & 1 year to remove + 1 garbage report + 2 range reports + 1 segmented report
);
$this->assertEquals($expectedPrediction, $prediction);
-
+
// purge data
$this->_setTimeToRun();
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->checkLogDataPurged();
$this->_checkReportsAndMetricsPurged($janBlobsRemaining = 1); // 1 blob for 1 year
}
-
+
/**
* Test no concurrency issues when deleting log data from log_action table.
*
@@ -486,26 +484,26 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testPurgeLogDataConcurrency()
{
Piwik_AddAction("LogDataPurger.actionsToKeepInserted.olderThan", array($this, 'addReferenceToUnusedAction'));
-
+
$purger = Piwik_PrivacyManager_LogDataPurger::make($this->settings, true);
-
+
$this->unusedIdAction = Piwik_FetchOne(
- "SELECT idaction FROM ".Piwik_Common::prefixTable('log_action')." WHERE name = ?",
+ "SELECT idaction FROM " . Piwik_Common::prefixTable('log_action') . " WHERE name = ?",
array('whatever.com/_40'));
$this->assertTrue($this->unusedIdAction > 0);
-
+
// purge data
$purger->purgeData();
-
+
// check that actions were purged
$this->assertEquals(22, $this->_getTableCount('log_action')); // January
-
+
// check that the unused action still exists
$count = Piwik_FetchOne(
- "SELECT COUNT(*) FROM ".Piwik_Common::prefixTable('log_action')." WHERE idaction = ?",
+ "SELECT COUNT(*) FROM " . Piwik_Common::prefixTable('log_action') . " WHERE idaction = ?",
array($this->unusedIdAction));
$this->assertEquals(1, $count);
-
+
$this->unusedIdAction = null; // so the hook won't get executed twice
}
@@ -518,33 +516,33 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testPurgeDataDeleteReportsKeepRangeReports()
{
Piwik_PrivacyManager::savePurgeDataSettings(array(
- 'delete_reports_keep_range_reports' => 1
- ));
-
+ 'delete_reports_keep_range_reports' => 1
+ ));
+
// get purge data prediction
$prediction = Piwik_PrivacyManager::getPurgeEstimate();
-
+
// perform checks on prediction
$expectedPrediction = array(
- Piwik_Common::prefixTable('log_conversion') => 6,
- Piwik_Common::prefixTable('log_link_visit_action') => 6,
- Piwik_Common::prefixTable('log_visit') => 3,
- Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
- Piwik_Common::prefixTable('archive_blob_2012_01') => 13 // 5 days, 4 weeks, 1 month & 1 year + 1 garbage report + 1 segmented report
+ Piwik_Common::prefixTable('archive_blob_2012_01') => 13 // 5 days, 4 weeks, 1 month & 1 year + 1 garbage report + 1 segmented report
);
$this->assertEquals($expectedPrediction, $prediction);
-
+
// purge data
$this->_setTimeToRun();
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->checkLogDataPurged();
$this->_checkReportsAndMetricsPurged($janBlobsRemaining = 2); // 2 range blobs
}
-
+
/**
* Tests that purgeData works correctly when the 'keep segment reports' setting is set to true.
*
@@ -554,36 +552,36 @@ class PrivacyManagerTest extends IntegrationTestCase
public function testPurgeDataDeleteReportsKeepSegmentsReports()
{
Piwik_PrivacyManager::savePurgeDataSettings(array(
- 'delete_reports_keep_day_reports' => 1,
- 'delete_reports_keep_segment_reports' => 1
- ));
-
+ 'delete_reports_keep_day_reports' => 1,
+ 'delete_reports_keep_segment_reports' => 1
+ ));
+
// get purge data prediction
$prediction = Piwik_PrivacyManager::getPurgeEstimate();
-
+
// perform checks on prediction
$expectedPrediction = array(
- Piwik_Common::prefixTable('log_conversion') => 6,
- Piwik_Common::prefixTable('log_link_visit_action') => 6,
- Piwik_Common::prefixTable('log_visit') => 3,
- Piwik_Common::prefixTable('log_conversion_item') => 3,
+ Piwik_Common::prefixTable('log_conversion') => 6,
+ Piwik_Common::prefixTable('log_link_visit_action') => 6,
+ Piwik_Common::prefixTable('log_visit') => 3,
+ Piwik_Common::prefixTable('log_conversion_item') => 3,
Piwik_Common::prefixTable('archive_numeric_2012_01') => -1,
- Piwik_Common::prefixTable('archive_blob_2012_01') => 9 // 4 weeks, 1 month & 1 year + 1 garbage report + 2 range reports
+ Piwik_Common::prefixTable('archive_blob_2012_01') => 9 // 4 weeks, 1 month & 1 year + 1 garbage report + 2 range reports
);
$this->assertEquals($expectedPrediction, $prediction);
-
+
// purge data
$this->_setTimeToRun();
$this->instance->deleteLogData();
$this->instance->deleteReportData();
-
+
// perform checks
$this->checkLogDataPurged();
$this->_checkReportsAndMetricsPurged($janBlobsRemaining = 6); // 1 segmented blob + 5 day blobs
}
-
+
// --- utility functions follow ---
-
+
protected static function _addLogData()
{
// tracks visits on the following days:
@@ -599,7 +597,7 @@ class PrivacyManagerTest extends IntegrationTestCase
// - 2012-02-23
// - 2012-02-28
// 6 visits in feb, 5 in jan
-
+
// following actions are created:
// - 'First page view'
// - 'Second page view'
@@ -609,86 +607,86 @@ class PrivacyManagerTest extends IntegrationTestCase
// - for every visit (11 visits total):
// - http://whatever.com/_{$daysSinceLastVisit}
// - http://whatever.com/42/{$daysSinceLastVisit}
-
+
$start = Piwik_Date::factory(self::$dateTime);
- self::$idSite = Test_Piwik_BaseFixture::createWebsite('2012-01-01', $ecommerce=1);
+ self::$idSite = Test_Piwik_BaseFixture::createWebsite('2012-01-01', $ecommerce = 1);
$idGoal = Piwik_Goals_API::getInstance()->addGoal(self::$idSite, 'match all', 'url', 'http', 'contains');
-
+
$t = Test_Piwik_BaseFixture::getTracker(self::$idSite, $start, $defaultInit = true);
$t->enableBulkTracking();
- $t->setTokenAuth(Test_Piwik_BaseFixture::getTokenAuth());
+ $t->setTokenAuth(Test_Piwik_BaseFixture::getTokenAuth());
for ($daysAgo = self::$daysAgoStart; $daysAgo >= 0; $daysAgo -= 5) // one visit every 5 days
{
$dateTime = $start->subDay($daysAgo)->toString();
-
+
$t->setForceVisitDateTime($dateTime);
$t->setUserAgent('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729)');
-
+
// use $daysAgo to make sure new actions are created for every day and aren't used again.
// when deleting visits, some of these actions will no longer be referenced in the DB.
$t->setUrl("http://whatever.com/_$daysAgo");
$t->doTrackPageView('First page view');
-
+
$t->setUrl("http://whatever.com/42/$daysAgo");
$t->doTrackPageView('Second page view');
-
- $t->addEcommerceItem($sku = 'SKU2', $name = 'Canon SLR' , $category = 'Electronics & Cameras',
- $price = 1500, $quantity = 1);
- $t->doTrackEcommerceOrder($orderId = '937nsjusu '.$dateTime, $grandTotal = 1111.11, $subTotal = 1000,
- $tax = 111, $shipping = 0.11, $discount = 666);
+
+ $t->addEcommerceItem($sku = 'SKU2', $name = 'Canon SLR', $category = 'Electronics & Cameras',
+ $price = 1500, $quantity = 1);
+ $t->doTrackEcommerceOrder($orderId = '937nsjusu ' . $dateTime, $grandTotal = 1111.11, $subTotal = 1000,
+ $tax = 111, $shipping = 0.11, $discount = 666);
}
- Test_Piwik_BaseFixture::checkResponse($t->doBulkTrack());
+ Test_Piwik_BaseFixture::checkResponse($t->doBulkTrack());
}
-
+
protected static function _addReportData()
{
- $date = Piwik_Date::factory(self::$dateTime);
-
+ $date = Piwik_Date::factory(self::$dateTime);
+
$archive = Piwik_Archive::build(self::$idSite, 'year', $date);
$archive->getNumeric('nb_visits', 'nb_hits');
-
+
Piwik_VisitorInterest_API::getInstance()->getNumberOfVisitsPerVisitDuration(self::$idSite, 'year', $date);
-
+
// months are added via the 'year' period, but weeks must be done manually
for ($daysAgo = self::$daysAgoStart; $daysAgo > 0; $daysAgo -= 7) // every week
{
$dateTime = $date->subDay($daysAgo);
-
+
$archive = Piwik_Archive::build(self::$idSite, 'week', $dateTime);
$archive->getNumeric('nb_visits');
-
+
Piwik_VisitorInterest_API::getInstance()->getNumberOfVisitsPerVisitDuration(
self::$idSite, 'week', $dateTime);
}
-
+
// add segment for one day
$archive = Piwik_Archive::build(self::$idSite, 'day', '2012-01-14', 'browserName==FF');
$archive->getNumeric('nb_visits', 'nb_hits');
-
+
Piwik_VisitorInterest_API::getInstance()->getNumberOfVisitsPerVisitDuration(
self::$idSite, 'day', '2012-01-14', 'browserName==FF');
-
+
// add range within January
$rangeEnd = Piwik_Date::factory('2012-01-29');
$rangeStart = $rangeEnd->subDay(1);
- $range = $rangeStart->toString('Y-m-d').",".$rangeEnd->toString('Y-m-d');
-
+ $range = $rangeStart->toString('Y-m-d') . "," . $rangeEnd->toString('Y-m-d');
+
$rangeArchive = Piwik_Archive::build(self::$idSite, 'range', $range);
$rangeArchive->getNumeric('nb_visits', 'nb_hits');
-
+
Piwik_VisitorInterest_API::getInstance()->getNumberOfVisitsPerVisitDuration(self::$idSite, 'range', $range);
-
+
// add range between January & February
$rangeStart = $rangeEnd;
$rangeEnd = $rangeStart->addDay(3);
- $range = $rangeStart->toString('Y-m-d').",".$rangeEnd->toString('Y-m-d');
-
+ $range = $rangeStart->toString('Y-m-d') . "," . $rangeEnd->toString('Y-m-d');
+
$rangeArchive = Piwik_Archive::build(self::$idSite, 'range', $range);
$rangeArchive->getNumeric('nb_visits', 'nb_hits');
-
+
Piwik_VisitorInterest_API::getInstance()->getNumberOfVisitsPerVisitDuration(self::$idSite, 'range', $range);
-
+
// when archiving is initiated, the archive metrics & reports for EVERY loaded plugin
// are archived. don't want this test to depend on every possible metric, so get rid of
// the unwanted archive data now.
@@ -699,39 +697,37 @@ class PrivacyManagerTest extends IntegrationTestCase
Piwik_Goals::getRecordName('nb_conversions', 1),
Piwik_Goals::getRecordName('revenue', Piwik_Tracker_GoalManager::IDGOAL_ORDER)
);
-
+
$archiveTables = self::_getArchiveTableNames();
- foreach ($archiveTables['numeric'] as $table)
- {
+ foreach ($archiveTables['numeric'] as $table) {
$realTable = Piwik_Common::prefixTable($table);
- Piwik_Query("DELETE FROM $realTable WHERE name NOT IN ('".implode("','", $metricsToSave)."') AND name NOT LIKE 'done%'");
+ Piwik_Query("DELETE FROM $realTable WHERE name NOT IN ('" . implode("','", $metricsToSave) . "') AND name NOT LIKE 'done%'");
}
- foreach ($archiveTables['blob'] as $table)
- {
+ foreach ($archiveTables['blob'] as $table) {
$realTable = Piwik_Common::prefixTable($table);
Piwik_Query("DELETE FROM $realTable WHERE name NOT IN ('VisitorInterest_timeGap')");
}
-
+
// add garbage metrics
$janDate1 = '2012-01-05';
$febDate1 = '2012-02-04';
-
+
$sql = "INSERT INTO %s (idarchive,name,idsite,date1,date2,period,ts_archived,value)
VALUES (10000,?,1,?,?,?,?,?)";
-
+
// one metric for jan & one for feb
Piwik_Query(sprintf($sql, Piwik_Common::prefixTable($archiveTables['numeric'][0])),
- array(self::GARBAGE_FIELD, $janDate1, $janDate1, $janDate1, 1, 100));
+ array(self::GARBAGE_FIELD, $janDate1, $janDate1, $janDate1, 1, 100));
Piwik_Query(sprintf($sql, Piwik_Common::prefixTable($archiveTables['numeric'][1])),
- array(self::GARBAGE_FIELD, $febDate1, $febDate1, $febDate1, 1, 200));
-
+ array(self::GARBAGE_FIELD, $febDate1, $febDate1, $febDate1, 1, 200));
+
// add garbage reports
Piwik_Query(sprintf($sql, Piwik_Common::prefixTable($archiveTables['blob'][0])),
- array(self::GARBAGE_FIELD, $janDate1, $janDate1, $janDate1, 10, 'blobval'));
+ array(self::GARBAGE_FIELD, $janDate1, $janDate1, $janDate1, 10, 'blobval'));
Piwik_Query(sprintf($sql, Piwik_Common::prefixTable($archiveTables['blob'][1])),
- array(self::GARBAGE_FIELD, $febDate1, $febDate1, $febDate1, 20, 'blobval'));
+ array(self::GARBAGE_FIELD, $febDate1, $febDate1, $febDate1, 20, 'blobval'));
}
-
+
protected function _checkNoDataChanges()
{
// 11 visits total w/ 2 actions per visit & 2 conversions per visit. 1 e-commerce order per visit.
@@ -740,44 +736,44 @@ class PrivacyManagerTest extends IntegrationTestCase
$this->assertEquals(22, $this->_getTableCount('log_link_visit_action'));
$this->assertEquals(11, $this->_getTableCount('log_conversion_item'));
$this->assertEquals(27, $this->_getTableCount('log_action'));
-
+
$archiveTables = self::_getArchiveTableNames();
-
+
$janMetricCount = $this->_getExpectedNumericArchiveCountJan();
$this->assertEquals($janMetricCount, $this->_getTableCount($archiveTables['numeric'][0])); // January
-
+
// no range metric for february
$febMetricCount = $this->_getExpectedNumericArchiveCountFeb();
$this->assertEquals($febMetricCount, $this->_getTableCount($archiveTables['numeric'][1])); // February
-
+
// 1 entry per period w/ visits + 1 garbage report + 2 range reports + 1 segment report
$this->assertEquals(self::JAN_METRIC_ARCHIVE_COUNT + 1 + 2 + 1, $this->_getTableCount($archiveTables['blob'][0])); // January
$this->assertEquals(self::FEB_METRIC_ARCHIVE_COUNT + 1, $this->_getTableCount($archiveTables['blob'][1])); // February
}
-
+
/**
* Helper method. Performs checks after reports are purged. Checks that the january numeric table
* was dropped, that the february metric & blob tables are unaffected, and that the january blob
* table has a certain number of blobs.
*/
- protected function _checkReportsAndMetricsPurged( $janBlobsRemaining )
+ protected function _checkReportsAndMetricsPurged($janBlobsRemaining)
{
$archiveTables = self::_getArchiveTableNames();
-
+
// check that the january numeric table was dropped
$this->assertFalse($this->_tableExists($archiveTables['numeric'][0])); // January
-
+
// check february numerics not deleted
$febRowCount = $this->_getExpectedNumericArchiveCountFeb();
$this->assertEquals($febRowCount, $this->_getTableCount($archiveTables['numeric'][1])); // February
-
+
// check the january blob count
$this->assertEquals($janBlobsRemaining, $this->_getTableCount($archiveTables['blob'][0])); // January
-
+
// check for no changes in the february blob table (1 blob for every period w/ visits in feb + 1 garbage report)
$this->assertEquals(self::FEB_METRIC_ARCHIVE_COUNT + 1, $this->_getTableCount($archiveTables['blob'][1])); // February
}
-
+
private function checkLogDataPurged()
{
// 3 days removed by purge, so 3 visits, 6 conversions, 6 visit actions, 3 e-commerce orders
@@ -788,7 +784,7 @@ class PrivacyManagerTest extends IntegrationTestCase
$this->assertEquals(8, $this->_getTableCount('log_conversion_item'));
$this->assertEquals(21, $this->_getTableCount('log_action'));
}
-
+
/**
* Event hook that adds a row into the DB that references unused idaction AFTER LogDataPurger
* does the insert into the temporary table. When log_actions are deleted, this idaction should still
@@ -797,49 +793,49 @@ class PrivacyManagerTest extends IntegrationTestCase
*
* @param Piwik_Event_Notification $notification notification object
*/
- public function addReferenceToUnusedAction( $notification )
+ public function addReferenceToUnusedAction($notification)
{
$unusedIdAction = $this->unusedIdAction;
if (empty($unusedIdAction)) // make sure we only do this for one test case
{
return;
}
-
+
$tempTableName = Piwik_Common::prefixTable(Piwik_PrivacyManager_LogDataPurger::TEMP_TABLE_NAME);
$logLinkVisitActionTable = Piwik_Common::prefixTable("log_link_visit_action");
-
+
$sql = "INSERT INTO $logLinkVisitActionTable
(idsite, idvisitor, server_time, idvisit, idaction_url, idaction_url_ref,
idaction_name, idaction_name_ref, time_spent_ref_action)
VALUES (1, 'abc', NOW(), 15, $unusedIdAction, $unusedIdAction,
$unusedIdAction, $unusedIdAction, 1000)";
-
+
Piwik_Query($sql);
}
-
+
protected function _setTimeToRun()
{
$lastDateSecs = Piwik_Date::factory('today')->subDay(8)->getTimestamp();
-
+
Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS, $lastDateSecs);
Piwik_SetOption(Piwik_PrivacyManager::OPTION_LAST_DELETE_PIWIK_REPORTS, $lastDateSecs);
}
-
- protected function _getTableCount( $tableName, $where = '' )
+
+ protected function _getTableCount($tableName, $where = '')
{
- $sql = "SELECT COUNT(*) FROM ".Piwik_Common::prefixTable($tableName)." $where";
+ $sql = "SELECT COUNT(*) FROM " . Piwik_Common::prefixTable($tableName) . " $where";
return Piwik_FetchOne($sql);
}
-
- protected function _tableExists( $tableName )
+
+ protected function _tableExists($tableName)
{
$dbName = Piwik_Config::getInstance()->database['dbname'];
-
+
$sql = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = ? AND table_name = ?";
return Piwik_FetchOne($sql, array($dbName, Piwik_Common::prefixTable($tableName))) == 1;
}
-
+
protected static function _getArchiveTableNames()
{
return array(
@@ -847,13 +843,13 @@ class PrivacyManagerTest extends IntegrationTestCase
'archive_numeric_2012_01',
'archive_numeric_2012_02'
),
- 'blob' => array(
+ 'blob' => array(
'archive_blob_2012_01',
'archive_blob_2012_02'
)
);
}
-
+
protected function _getExpectedNumericArchiveCountJan()
{
// 5 entries per period w/ visits
@@ -863,7 +859,7 @@ class PrivacyManagerTest extends IntegrationTestCase
// + 2 entries per segment (2 total) + 2 'done...' entries per segment (2 total)
return self::JAN_METRIC_ARCHIVE_COUNT * 5 + self::TOTAL_JAN_ARCHIVE_COUNT + 1 + 8 + 4;
}
-
+
protected function _getExpectedNumericArchiveCountFeb()
{
// (5 metrics per period w/ visits
diff --git a/tests/PHPUnit/Plugins/ReferersTest.php b/tests/PHPUnit/Plugins/ReferersTest.php
index 8757ebde67..547ec37576 100644
--- a/tests/PHPUnit/Plugins/ReferersTest.php
+++ b/tests/PHPUnit/Plugins/ReferersTest.php
@@ -17,8 +17,7 @@ class ReferersTest extends PHPUnit_Framework_TestCase
include PIWIK_PATH_TEST_TO_ROOT . '/core/DataFiles/SearchEngines.php';
$searchEngines = array();
- foreach($GLOBALS['Piwik_SearchEngines'] AS $url => $searchEngine)
- {
+ foreach ($GLOBALS['Piwik_SearchEngines'] AS $url => $searchEngine) {
$searchEngines[] = array($url, $searchEngine);
}
return $searchEngines;
@@ -26,7 +25,7 @@ class ReferersTest extends PHPUnit_Framework_TestCase
/**
* search engine has at least one keyword
- *
+ *
* @group Plugins
* @group Referers
* @dataProvider getSearchEngines
@@ -36,9 +35,8 @@ class ReferersTest extends PHPUnit_Framework_TestCase
// Get list of search engines and first appearing URL
static $searchEngines = array();
- $name = parse_url('http://'.$url);
- if(!array_key_exists($searchEngine[0], $searchEngines))
- {
+ $name = parse_url('http://' . $url);
+ if (!array_key_exists($searchEngine[0], $searchEngines)) {
$searchEngines[$searchEngine[0]] = $url;
$this->assertTrue(!empty($searchEngine[1]), $name['host']);
@@ -47,7 +45,7 @@ class ReferersTest extends PHPUnit_Framework_TestCase
/**
* search engine is defined in DataFiles/SearchEngines.php but there's no favicon
- *
+ *
* @group Plugins
* @group Referers
* @dataProvider getSearchEngines
@@ -60,9 +58,8 @@ class ReferersTest extends PHPUnit_Framework_TestCase
// Get list of search engines and first appearing URL
static $searchEngines = array();
- $name = parse_url('http://'.$url);
- if(!array_key_exists($searchEngine[0], $searchEngines))
- {
+ $name = parse_url('http://' . $url);
+ if (!array_key_exists($searchEngine[0], $searchEngines)) {
$searchEngines[$searchEngine[0]] = $url;
$this->assertTrue(in_array($name['host'] . '.png', $favicons), $name['host']);
@@ -71,7 +68,7 @@ class ReferersTest extends PHPUnit_Framework_TestCase
/**
* favicon exists but there's no corresponding search engine defined in DataFiles/SearchEngines.php
- *
+ *
* @group Plugins
* @group Referers
*/
@@ -81,21 +78,17 @@ class ReferersTest extends PHPUnit_Framework_TestCase
// Get list of search engines and first appearing URL
$searchEngines = array();
- foreach($GLOBALS['Piwik_SearchEngines'] as $url => $searchEngine)
- {
- $name = parse_url('http://'.$url);
- if(!array_key_exists($name['host'], $searchEngines))
- {
+ foreach ($GLOBALS['Piwik_SearchEngines'] as $url => $searchEngine) {
+ $name = parse_url('http://' . $url);
+ if (!array_key_exists($name['host'], $searchEngines)) {
$searchEngines[$name['host']] = true;
}
}
// Get list of existing favicons
$favicons = scandir(PIWIK_PATH_TEST_TO_ROOT . '/plugins/Referers/images/searchEngines/');
- foreach($favicons as $name)
- {
- if($name[0] == '.' || strpos($name, 'xx.') === 0)
- {
+ foreach ($favicons as $name) {
+ if ($name[0] == '.' || strpos($name, 'xx.') === 0) {
continue;
}
@@ -106,7 +99,7 @@ class ReferersTest extends PHPUnit_Framework_TestCase
/**
* get search engine host from url
- *
+ *
* @group Plugins
* @group Referers
*/
@@ -114,11 +107,10 @@ class ReferersTest extends PHPUnit_Framework_TestCase
{
$data = array(
'http://www.google.com/cse' => array('www.google.com', 'www.google.com/cse'),
- 'http://www.google.com' => array('www.google.com', 'www.google.com'),
+ 'http://www.google.com' => array('www.google.com', 'www.google.com'),
);
- foreach($data as $url => $expected)
- {
+ foreach ($data as $url => $expected) {
$this->assertEquals($expected[0], Piwik_getSearchEngineHostFromUrl($url));
$this->assertEquals($expected[1], Piwik_getSearchEngineHostPathFromUrl($url));
}
diff --git a/tests/PHPUnit/Plugins/SEOTest.php b/tests/PHPUnit/Plugins/SEOTest.php
index 2d3483cc9a..31272c919d 100644
--- a/tests/PHPUnit/Plugins/SEOTest.php
+++ b/tests/PHPUnit/Plugins/SEOTest.php
@@ -13,8 +13,8 @@ class SEOTest extends PHPUnit_Framework_TestCase
// setup the access layer
$pseudoMockAccess = new FakeAccess;
- FakeAccess::setIdSitesView( array(1,2));
- FakeAccess::setIdSitesAdmin( array(3,4));
+ FakeAccess::setIdSitesView(array(1, 2));
+ FakeAccess::setIdSitesAdmin(array(3, 4));
//finally we set the user as a super user by default
FakeAccess::$superUser = true;
@@ -42,8 +42,7 @@ class SEOTest extends PHPUnit_Framework_TestCase
$renderer = Piwik_DataTable_Renderer::factory('php');
$renderer->setSerialize(false);
$ranks = $renderer->render($dataTable);
- foreach ($ranks as $rank)
- {
+ foreach ($ranks as $rank) {
$this->assertNotEmpty($rank['rank'], $rank['id'] . ' expected non-zero rank, got [' . $rank['rank'] . ']');
}
}
diff --git a/tests/PHPUnit/Plugins/SitesManagerTest.php b/tests/PHPUnit/Plugins/SitesManagerTest.php
index 59d7670221..50aa07cd58 100644
--- a/tests/PHPUnit/Plugins/SitesManagerTest.php
+++ b/tests/PHPUnit/Plugins/SitesManagerTest.php
@@ -16,40 +16,40 @@ class SitesManagerTest extends DatabaseTestCase
FakeAccess::$superUser = true;
Zend_Registry::set('access', $pseudoMockAccess);
}
-
+
/**
* empty name -> exception
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testAddSiteEmptyName()
{
try {
- Piwik_SitesManager_API::getInstance()->addSite("",array("http://piwik.net"));
+ Piwik_SitesManager_API::getInstance()->addSite("", array("http://piwik.net"));
} catch (Exception $e) {
return;
}
$this->fail('Expected exception not raised');
}
-
+
/**
* DataProvider for testAddSiteWrongUrls
*/
public function getInvalidUrlData()
{
return array(
- array(array()), // no urls
+ array(array()), // no urls
array(array("")),
array(""),
array("httpww://piwik.net"),
array("httpww://piwik.net/gqg~#"),
);
}
-
+
/**
* wrong urls -> exception
- *
+ *
* @dataProvider getInvalidUrlData
* @group Plugins
* @group SitesManager
@@ -63,39 +63,39 @@ class SitesManagerTest extends DatabaseTestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* Test with valid IPs
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testAddSiteExcludedIpsAndtimezoneAndCurrencyAndExcludedQueryParametersValid()
{
$ips = '1.2.3.4,1.1.1.*,1.2.*.*,1.*.*.*';
- $timezone = 'Europe/Paris';
+ $timezone = 'Europe/Paris';
$currency = 'EUR';
$excludedQueryParameters = 'p1,P2, P33333';
$expectedExcludedQueryParameters = 'p1,P2,P33333';
$excludedUserAgents = " p1,P2, \nP3333 ";
$expectedExcludedUserAgents = "p1,P2,P3333";
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("name","http://piwik.net/", $ecommerce = 1,
- $siteSearch = 1, $searchKeywordParameters = 'search,param', $searchCategoryParameters = 'cat,category',
- $ips, $excludedQueryParameters,$timezone, $currency, $group = null, $startDate = null, $excludedUserAgents);
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("name", "http://piwik.net/", $ecommerce = 1,
+ $siteSearch = 1, $searchKeywordParameters = 'search,param', $searchCategoryParameters = 'cat,category',
+ $ips, $excludedQueryParameters, $timezone, $currency, $group = null, $startDate = null, $excludedUserAgents);
$siteInfo = Piwik_SitesManager_API::getInstance()->getSiteFromId($idsite);
$this->assertEquals($ips, $siteInfo['excluded_ips']);
$this->assertEquals($timezone, $siteInfo['timezone']);
$this->assertEquals($currency, $siteInfo['currency']);
- $this->assertEquals($ecommerce, $siteInfo['ecommerce']);
- $this->assertTrue(Piwik_Site::isEcommerceEnabledFor($idsite));
- $this->assertEquals($siteSearch, $siteInfo['sitesearch']);
- $this->assertTrue(Piwik_Site::isSiteSearchEnabledFor($idsite));
- $this->assertEquals($searchKeywordParameters, $siteInfo['sitesearch_keyword_parameters']);
- $this->assertEquals($searchCategoryParameters, $siteInfo['sitesearch_category_parameters']);
+ $this->assertEquals($ecommerce, $siteInfo['ecommerce']);
+ $this->assertTrue(Piwik_Site::isEcommerceEnabledFor($idsite));
+ $this->assertEquals($siteSearch, $siteInfo['sitesearch']);
+ $this->assertTrue(Piwik_Site::isSiteSearchEnabledFor($idsite));
+ $this->assertEquals($searchKeywordParameters, $siteInfo['sitesearch_keyword_parameters']);
+ $this->assertEquals($searchCategoryParameters, $siteInfo['sitesearch_category_parameters']);
$this->assertEquals($expectedExcludedQueryParameters, $siteInfo['excluded_parameters']);
$this->assertEquals($expectedExcludedUserAgents, $siteInfo['excluded_user_agents']);
}
-
+
/**
* dataProvider for testAddSiteExcludedIpsNotValid
*/
@@ -109,12 +109,12 @@ class SitesManagerTest extends DatabaseTestCase
array('*.*.1.1'),
array('*.*.*.1'),
array('1.1.1.1.1'),
- );
+ );
}
-
+
/**
* Test with invalid IPs
- *
+ *
* @dataProvider getInvalidIPsData
* @group Plugins
* @group SitesManager
@@ -122,17 +122,17 @@ class SitesManagerTest extends DatabaseTestCase
public function testAddSiteExcludedIpsNotValid($ip)
{
try {
- Piwik_SitesManager_API::getInstance()->addSite("name","http://piwik.net/", $ecommerce = 0,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $ip);
+ Piwik_SitesManager_API::getInstance()->addSite("name", "http://piwik.net/", $ecommerce = 0,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $ip);
} catch (Exception $e) {
return;
}
$this->fail('Expected exception not raised');
}
-
+
/**
* one url -> one main_url and nothing inserted as alias urls
- *
+ *
* @group Plugins
* @group SitesManager
*/
@@ -140,53 +140,54 @@ class SitesManagerTest extends DatabaseTestCase
{
$url = "http://piwik.net/";
$urlOK = "http://piwik.net";
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("name",$url);
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("name", $url);
$this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $idsite);
-
+
$siteInfo = Piwik_SitesManager_API::getInstance()->getSiteFromId($idsite);
$this->assertEquals($urlOK, $siteInfo['main_url']);
$this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($siteInfo['ts_created'])));
-
+
$siteUrls = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
$this->assertEquals(1, count($siteUrls));
}
-
+
/**
* several urls -> one main_url and others as alias urls
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testAddSiteSeveralUrls()
{
- $urls = array("http://piwik.net/","http://piwik.com","https://piwik.net/test/");
- $urlsOK = array("http://piwik.net","http://piwik.com","https://piwik.net/test");
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("super website",$urls);
+ $urls = array("http://piwik.net/", "http://piwik.com", "https://piwik.net/test/");
+ $urlsOK = array("http://piwik.net", "http://piwik.com", "https://piwik.net/test");
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("super website", $urls);
$this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $idsite);
-
+
$siteInfo = Piwik_SitesManager_API::getInstance()->getSiteFromId($idsite);
$this->assertEquals($urlsOK[0], $siteInfo['main_url']);
-
+
$siteUrls = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
$this->assertEquals($urlsOK, $siteUrls);
}
-
+
/**
* strange name
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testAddSiteStrangeName()
{
$name = "supertest(); ~@@()''!£\$'%%^'!£ போ";
- $idsite = Piwik_SitesManager_API::getInstance()->addSite($name,"http://piwik.net");
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite($name, "http://piwik.net");
$this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $idsite);
-
+
$siteInfo = Piwik_SitesManager_API::getInstance()->getSiteFromId($idsite);
$this->assertEquals($name, $siteInfo['name']);
-
+
}
+
/**
* adds a site
* use by several other unit tests
@@ -194,144 +195,144 @@ class SitesManagerTest extends DatabaseTestCase
protected function _addSite()
{
$name = "website ";
- $idsite = Piwik_SitesManager_API::getInstance()->addSite($name,array("http://piwik.net","http://piwik.com/test/"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite($name, array("http://piwik.net", "http://piwik.com/test/"));
$this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $idsite);
-
+
$siteInfo = Piwik_SitesManager_API::getInstance()->getSiteFromId($idsite);
$this->assertEquals($name, $siteInfo['name']);
$this->assertEquals("http://piwik.net", $siteInfo['main_url']);
-
+
$siteUrls = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
- $this->assertEquals(array("http://piwik.net","http://piwik.com/test"), $siteUrls);
-
+ $this->assertEquals(array("http://piwik.net", "http://piwik.com/test"), $siteUrls);
+
return $idsite;
}
-
+
/**
* no duplicate -> all the urls are saved
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testAddSiteUrlsnoDuplicate()
{
$idsite = $this->_addSite();
-
+
$siteUrlsBefore = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
-
- $toAdd = array( "http://piwik1.net",
- "http://piwik2.net",
- "http://piwik3.net/test/",
- "http://localhost/test",
- "http://localho5.st/test",
- "http://l42578gqege.f4",
- "http://super.com/test/test/atqata675675/te"
- );
+
+ $toAdd = array("http://piwik1.net",
+ "http://piwik2.net",
+ "http://piwik3.net/test/",
+ "http://localhost/test",
+ "http://localho5.st/test",
+ "http://l42578gqege.f4",
+ "http://super.com/test/test/atqata675675/te"
+ );
$toAddValid = array("http://piwik1.net",
- "http://piwik2.net",
+ "http://piwik2.net",
"http://piwik3.net/test",
- "http://localhost/test",
- "http://localho5.st/test",
- "http://l42578gqege.f4",
+ "http://localhost/test",
+ "http://localho5.st/test",
+ "http://l42578gqege.f4",
"http://super.com/test/test/atqata675675/te");
-
+
$insertedUrls = Piwik_SitesManager_API::getInstance()->addSiteAliasUrls($idsite, $toAdd);
$this->assertEquals(count($toAdd), $insertedUrls);
-
+
$siteUrlsAfter = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
-
+
$shouldHave = array_merge($siteUrlsBefore, $toAddValid);
sort($shouldHave);
-
+
sort($siteUrlsAfter);
-
+
$this->assertEquals($shouldHave, $siteUrlsAfter);
}
-
+
/**
* duplicate -> don't save the already existing URLs
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testAddSiteUrlsDuplicate()
- {
+ {
$idsite = $this->_addSite();
-
+
$siteUrlsBefore = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
-
- $toAdd = array_merge($siteUrlsBefore, array("http://piwik1.net","http://piwik2.net"));
-
+
+ $toAdd = array_merge($siteUrlsBefore, array("http://piwik1.net", "http://piwik2.net"));
+
$insertedUrls = Piwik_SitesManager_API::getInstance()->addSiteAliasUrls($idsite, $toAdd);
$this->assertEquals(count($toAdd) - count($siteUrlsBefore), $insertedUrls);
-
+
$siteUrlsAfter = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
-
+
$shouldHave = $toAdd;
sort($shouldHave);
-
+
sort($siteUrlsAfter);
-
+
$this->assertEquals($shouldHave, $siteUrlsAfter);
}
-
+
/**
* case empty array => nothing happens
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testAddSiteUrlsNoUrlsToAdd1()
{
$idsite = $this->_addSite();
-
+
$siteUrlsBefore = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
-
+
$toAdd = array();
-
+
$insertedUrls = Piwik_SitesManager_API::getInstance()->addSiteAliasUrls($idsite, $toAdd);
$this->assertEquals(count($toAdd), $insertedUrls);
-
+
$siteUrlsAfter = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
-
+
$shouldHave = $siteUrlsBefore;
sort($shouldHave);
-
+
sort($siteUrlsAfter);
-
+
$this->assertEquals($shouldHave, $siteUrlsAfter);
}
-
+
/**
* case array only duplicate => nothing happens
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testAddSiteUrlsNoUrlsToAdd2()
{
$idsite = $this->_addSite();
-
+
$siteUrlsBefore = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
-
+
$toAdd = $siteUrlsBefore;
-
+
$insertedUrls = Piwik_SitesManager_API::getInstance()->addSiteAliasUrls($idsite, $toAdd);
$this->assertEquals(0, $insertedUrls);
-
+
$siteUrlsAfter = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
-
+
$shouldHave = $siteUrlsBefore;
sort($shouldHave);
-
+
sort($siteUrlsAfter);
-
+
$this->assertEquals($shouldHave, $siteUrlsAfter);
}
-
+
/**
* wrong format urls => exception
- *
+ *
* @group Plugins
* @group SitesManager
*/
@@ -346,10 +347,10 @@ class SitesManagerTest extends DatabaseTestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* wrong idsite => no exception because simply no access to this resource
- *
+ *
* @group Plugins
* @group SitesManager
*/
@@ -363,10 +364,10 @@ class SitesManagerTest extends DatabaseTestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* wrong idsite => exception
- *
+ *
* @group Plugins
* @group SitesManager
*/
@@ -380,10 +381,10 @@ class SitesManagerTest extends DatabaseTestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* no Id -> empty array
- *
+ *
* @group Plugins
* @group SitesManager
*/
@@ -392,31 +393,31 @@ class SitesManagerTest extends DatabaseTestCase
$ids = Piwik_SitesManager_API::getInstance()->getAllSitesId();
$this->assertEquals(array(), $ids);
}
-
+
/**
* several Id -> normal array
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetAllSitesIdSeveralId()
{
- $name="tetq";
+ $name = "tetq";
$idsites = array(
- Piwik_SitesManager_API::getInstance()->addSite($name,array("http://piwik.net","http://piwik.com/test/")),
- Piwik_SitesManager_API::getInstance()->addSite($name,array("http://piwik.net","http://piwik.com/test/")),
- Piwik_SitesManager_API::getInstance()->addSite($name,array("http://piwik.net","http://piwik.com/test/")),
- Piwik_SitesManager_API::getInstance()->addSite($name,array("http://piwik.net","http://piwik.com/test/")),
- Piwik_SitesManager_API::getInstance()->addSite($name,array("http://piwik.net","http://piwik.com/test/")),
- );
-
+ Piwik_SitesManager_API::getInstance()->addSite($name, array("http://piwik.net", "http://piwik.com/test/")),
+ Piwik_SitesManager_API::getInstance()->addSite($name, array("http://piwik.net", "http://piwik.com/test/")),
+ Piwik_SitesManager_API::getInstance()->addSite($name, array("http://piwik.net", "http://piwik.com/test/")),
+ Piwik_SitesManager_API::getInstance()->addSite($name, array("http://piwik.net", "http://piwik.com/test/")),
+ Piwik_SitesManager_API::getInstance()->addSite($name, array("http://piwik.net", "http://piwik.com/test/")),
+ );
+
$ids = Piwik_SitesManager_API::getInstance()->getAllSitesId();
$this->assertEquals($idsites, $ids);
}
-
+
/**
* wrong id => exception
- *
+ *
* @group Plugins
* @group SitesManager
*/
@@ -432,7 +433,7 @@ class SitesManagerTest extends DatabaseTestCase
/**
* wrong id => exception
- *
+ *
* @group Plugins
* @group SitesManager
*/
@@ -448,18 +449,18 @@ class SitesManagerTest extends DatabaseTestCase
/**
* wrong id : no access => exception
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSiteFromIdWrongId3()
{
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site",array("http://piwik.net","http://piwik.com/test/"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site", array("http://piwik.net", "http://piwik.com/test/"));
$this->assertEquals(1, $idsite);
-
+
// set noaccess to site 1
- FakeAccess::setIdSitesView (array(2));
- FakeAccess::setIdSitesAdmin (array());
+ FakeAccess::setIdSitesView(array(2));
+ FakeAccess::setIdSitesAdmin(array());
try {
$siteInfo = Piwik_SitesManager_API::getInstance()->getSiteFromId(1);
@@ -468,192 +469,193 @@ class SitesManagerTest extends DatabaseTestCase
}
$this->fail('Expected exception not raised');
}
+
/**
* normal case
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSiteFromIdNormalId()
{
$name = "website ''";
- $idsite = Piwik_SitesManager_API::getInstance()->addSite($name,array("http://piwik.net","http://piwik.com/test/"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite($name, array("http://piwik.net", "http://piwik.com/test/"));
$this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $idsite);
-
+
$siteInfo = Piwik_SitesManager_API::getInstance()->getSiteFromId($idsite);
$this->assertEquals($name, $siteInfo['name']);
$this->assertEquals("http://piwik.net", $siteInfo['main_url']);
}
-
-
+
+
/**
* there is no admin site available -> array()
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSitesWithAdminAccessNoResult()
{
- FakeAccess::setIdSitesAdmin (array());
-
+ FakeAccess::setIdSitesAdmin(array());
+
$sites = Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess();
$this->assertEquals(array(), $sites);
}
-
+
/**
* normal case, admin and view and noaccess website => return only admin
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSitesWithAdminAccess()
{
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array("http://piwik.net","http://piwik.com/test/"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2",array("http://piwik.com/test/"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org"));
-
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array("http://piwik.net", "http://piwik.com/test/"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2", array("http://piwik.com/test/"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3", array("http://piwik.org"));
+
$resultWanted = array(
- 0 => array("idsite" => 1, "name" => "site1", "main_url" =>"http://piwik.net", "ecommerce" => 0, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
- 1 => array("idsite" => 3, "name" => "site3", "main_url" =>"http://piwik.org", "ecommerce" => 0,"excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
+ 0 => array("idsite" => 1, "name" => "site1", "main_url" => "http://piwik.net", "ecommerce" => 0, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
+ 1 => array("idsite" => 3, "name" => "site3", "main_url" => "http://piwik.org", "ecommerce" => 0, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
);
-
- FakeAccess::setIdSitesAdmin (array(1,3));
-
+
+ FakeAccess::setIdSitesAdmin(array(1, 3));
+
$sites = Piwik_SitesManager_API::getInstance()->getSitesWithAdminAccess();
-
+
// we dont test the ts_created
unset($sites[0]['ts_created']);
unset($sites[1]['ts_created']);
$this->assertEquals($resultWanted, $sites);
}
-
+
/**
* there is no admin site available -> array()
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSitesWithViewAccessNoResult()
{
- FakeAccess::setIdSitesView (array());
- FakeAccess::setIdSitesAdmin (array());
-
+ FakeAccess::setIdSitesView(array());
+ FakeAccess::setIdSitesAdmin(array());
+
$sites = Piwik_SitesManager_API::getInstance()->getSitesWithViewAccess();
$this->assertEquals(array(), $sites);
}
-
+
/**
* normal case, admin and view and noaccess website => return only admin
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSitesWithViewAccess()
{
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array("http://piwik.net","http://piwik.com/test/"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2",array("http://piwik.com/test/"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org"));
-
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array("http://piwik.net", "http://piwik.com/test/"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2", array("http://piwik.com/test/"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3", array("http://piwik.org"));
+
$resultWanted = array(
- 0 => array("idsite" => 1, "name" => "site1", "main_url" =>"http://piwik.net", "ecommerce" => 0, 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '',"excluded_ips" => "", 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
- 1 => array("idsite" => 3, "name" => "site3", "main_url" =>"http://piwik.org", "ecommerce" => 0, 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '',"excluded_ips" => "", 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
+ 0 => array("idsite" => 1, "name" => "site1", "main_url" => "http://piwik.net", "ecommerce" => 0, 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', "excluded_ips" => "", 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
+ 1 => array("idsite" => 3, "name" => "site3", "main_url" => "http://piwik.org", "ecommerce" => 0, 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', "excluded_ips" => "", 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
);
-
- FakeAccess::setIdSitesView (array(1,3));
- FakeAccess::setIdSitesAdmin (array());
-
+
+ FakeAccess::setIdSitesView(array(1, 3));
+ FakeAccess::setIdSitesAdmin(array());
+
$sites = Piwik_SitesManager_API::getInstance()->getSitesWithViewAccess();
// we dont test the ts_created
unset($sites[0]['ts_created']);
unset($sites[1]['ts_created']);
$this->assertEquals($resultWanted, $sites);
}
-
+
/**
* there is no admin site available -> array()
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSitesWithAtLeastViewAccessNoResult()
{
- FakeAccess::setIdSitesView (array());
- FakeAccess::setIdSitesAdmin (array());
-
+ FakeAccess::setIdSitesView(array());
+ FakeAccess::setIdSitesAdmin(array());
+
$sites = Piwik_SitesManager_API::getInstance()->getSitesWithAtLeastViewAccess();
$this->assertEquals(array(), $sites);
}
-
+
/**
* normal case, admin and view and noaccess website => return only admin
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSitesWithAtLeastViewAccess()
{
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array("http://piwik.net","http://piwik.com/test/"), $ecommerce=1);
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2",array("http://piwik.com/test/"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org"));
-
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array("http://piwik.net", "http://piwik.com/test/"), $ecommerce = 1);
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2", array("http://piwik.com/test/"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3", array("http://piwik.org"));
+
$resultWanted = array(
- 0 => array("idsite" => 1, "name" => "site1", "main_url" =>"http://piwik.net", "ecommerce" => 1,"excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
- 1 => array("idsite" => 3, "name" => "site3", "main_url" =>"http://piwik.org", "ecommerce" => 0,"excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
+ 0 => array("idsite" => 1, "name" => "site1", "main_url" => "http://piwik.net", "ecommerce" => 1, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
+ 1 => array("idsite" => 3, "name" => "site3", "main_url" => "http://piwik.org", "ecommerce" => 0, "excluded_ips" => "", 'sitesearch' => 1, 'sitesearch_keyword_parameters' => '', 'sitesearch_category_parameters' => '', 'excluded_parameters' => '', 'excluded_user_agents' => '', 'timezone' => 'UTC', 'currency' => 'USD', 'group' => '', 'keep_url_fragment' => 0),
);
-
- FakeAccess::setIdSitesView (array(1,3));
- FakeAccess::setIdSitesAdmin (array());
-
+
+ FakeAccess::setIdSitesView(array(1, 3));
+ FakeAccess::setIdSitesAdmin(array());
+
$sites = Piwik_SitesManager_API::getInstance()->getSitesWithAtLeastViewAccess();
// we dont test the ts_created
unset($sites[0]['ts_created']);
unset($sites[1]['ts_created']);
$this->assertEquals($resultWanted, $sites);
}
-
+
/**
* no urls for this site => array()
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSiteUrlsFromIdNoUrls()
{
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array("http://piwik.net"));
-
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array("http://piwik.net"));
+
$urls = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
$this->assertEquals(array("http://piwik.net"), $urls);
}
-
+
/**
* normal case
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSiteUrlsFromIdManyUrls()
{
$site = array("http://piwik.net",
- "http://piwik.org",
- "http://piwik.org",
- "http://piwik.com");
+ "http://piwik.org",
+ "http://piwik.org",
+ "http://piwik.com");
sort($site);
-
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",$site);
-
+
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", $site);
+
$siteWanted = array("http://piwik.net",
- "http://piwik.org",
- "http://piwik.com");
+ "http://piwik.org",
+ "http://piwik.com");
sort($siteWanted);
$urls = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
-
-
+
+
$this->assertEquals($siteWanted, $urls);
}
-
+
/**
* wrongId => exception
- *
+ *
* @group Plugins
* @group SitesManager
*/
@@ -668,42 +670,42 @@ class SitesManagerTest extends DatabaseTestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* one url => no change to alias urls
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testUpdateSiteOneUrl()
{
$urls = array("http://piwiknew.com",
- "http://piwiknew.net",
- "http://piwiknew.org",
- "http://piwiknew.fr");
+ "http://piwiknew.net",
+ "http://piwiknew.org",
+ "http://piwiknew.fr");
$idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", $urls);
$newMainUrl = "http://main.url";
-
+
// Also test that the group was set to empty, and is searchable
$websites = Piwik_SitesManager_API::getInstance()->getSitesFromGroup('');
$this->assertEquals(1, count($websites));
-
+
// the Update doesn't change the group field
- Piwik_SitesManager_API::getInstance()->updateSite($idsite, "test toto@{}", $newMainUrl );
+ Piwik_SitesManager_API::getInstance()->updateSite($idsite, "test toto@{}", $newMainUrl);
$websites = Piwik_SitesManager_API::getInstance()->getSitesFromGroup('');
$this->assertEquals(1, count($websites));
// Updating the group to something
$group = 'something';
- Piwik_SitesManager_API::getInstance()->updateSite($idsite, "test toto@{}", $newMainUrl, $ecommerce = 0, $ss = true, $ss_kwd = null, $ss_cat = '', $ips=null, $parametersExclude=null, $timezone=null, $currency=null, $group );
+ Piwik_SitesManager_API::getInstance()->updateSite($idsite, "test toto@{}", $newMainUrl, $ecommerce = 0, $ss = true, $ss_kwd = null, $ss_cat = '', $ips = null, $parametersExclude = null, $timezone = null, $currency = null, $group);
$websites = Piwik_SitesManager_API::getInstance()->getSitesFromGroup($group);
$this->assertEquals(1, count($websites));
$this->assertEquals(date('Y-m-d'), date('Y-m-d', strtotime($websites[0]['ts_created'])));
// Updating the group to nothing
$group = '';
- Piwik_SitesManager_API::getInstance()->updateSite($idsite, "test toto@{}", $newMainUrl, $ecommerce = 0, $ss = false, $ss_kwd = '', $ss_cat = null, $ips=null, $parametersExclude=null, $timezone=null, $currency=null, $group, $startDate = '2010-01-01' );
+ Piwik_SitesManager_API::getInstance()->updateSite($idsite, "test toto@{}", $newMainUrl, $ecommerce = 0, $ss = false, $ss_kwd = '', $ss_cat = null, $ips = null, $parametersExclude = null, $timezone = null, $currency = null, $group, $startDate = '2010-01-01');
$websites = Piwik_SitesManager_API::getInstance()->getSitesFromGroup($group);
$this->assertEquals(1, count($websites));
$this->assertEquals('2010-01-01', date('Y-m-d', strtotime($websites[0]['ts_created'])));
@@ -713,73 +715,73 @@ class SitesManagerTest extends DatabaseTestCase
$aliasUrls = array_slice($allUrls, 1);
$this->assertEquals(array(), $aliasUrls);
}
-
+
/**
* strange name and NO URL => name ok, main_url not updated
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testUpdateSiteStrangeNameNoUrl()
{
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1","http://main.url");
- $newName ="test toto@{'786'}";
-
- Piwik_SitesManager_API::getInstance()->updateSite($idsite, $newName );
-
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", "http://main.url");
+ $newName = "test toto@{'786'}";
+
+ Piwik_SitesManager_API::getInstance()->updateSite($idsite, $newName);
+
$site = Piwik_SitesManager_API::getInstance()->getSiteFromId($idsite);
-
+
$this->assertEquals($newName, $site['name']);
// url didn't change because parameter url NULL in updateSite
$this->assertEquals("http://main.url", $site['main_url']);
}
-
+
/**
* several urls => both main and alias are updated
* also test the update of group field
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testUpdateSiteSeveralUrlsAndGroup()
{
$urls = array("http://piwiknew.com",
- "http://piwiknew.net",
- "http://piwiknew.org",
- "http://piwiknew.fr");
-
+ "http://piwiknew.net",
+ "http://piwiknew.org",
+ "http://piwiknew.fr");
+
$group = 'GROUP Before';
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",$urls, $ecommerce =1,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
- $excludedIps = null, $excludedQueryParameters = null, $timezone = null, $currency = null, $group, $startDate = '2011-01-01');
-
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", $urls, $ecommerce = 1,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
+ $excludedIps = null, $excludedQueryParameters = null, $timezone = null, $currency = null, $group, $startDate = '2011-01-01');
+
$websites = Piwik_SitesManager_API::getInstance()->getSitesFromGroup($group);
$this->assertEquals(1, count($websites));
-
+
$newurls = array("http://piwiknew2.com",
- "http://piwiknew2.net",
- "http://piwiknew2.org",
- "http://piwiknew2.fr");
+ "http://piwiknew2.net",
+ "http://piwiknew2.org",
+ "http://piwiknew2.fr");
$groupAfter = ' GROUP After';
- Piwik_SitesManager_API::getInstance()->updateSite($idsite, "test toto@{}",$newurls, $ecommerce = 0,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
- $excludedIps = null, $excludedQueryParameters = null, $timezone = null, $currency = null, $groupAfter);
+ Piwik_SitesManager_API::getInstance()->updateSite($idsite, "test toto@{}", $newurls, $ecommerce = 0,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,
+ $excludedIps = null, $excludedQueryParameters = null, $timezone = null, $currency = null, $groupAfter);
// no result for the group before update
$websites = Piwik_SitesManager_API::getInstance()->getSitesFromGroup($group);
$this->assertEquals(0, count($websites));
-
+
// Testing that the group was updated properly (and testing that the group value is trimmed before inserted/searched)
$websites = Piwik_SitesManager_API::getInstance()->getSitesFromGroup($groupAfter . ' ');
$this->assertEquals(1, count($websites));
$this->assertEquals('2011-01-01', date('Y-m-d', strtotime($websites[0]['ts_created'])));
-
+
// Test fetch website groups
$expectedGroups = array(trim($groupAfter));
$fetched = Piwik_SitesManager_API::getInstance()->getSitesGroups();
$this->assertEquals($expectedGroups, $fetched);
-
+
$allUrls = Piwik_SitesManager_API::getInstance()->getSiteUrlsFromId($idsite);
sort($allUrls);
sort($newurls);
@@ -787,23 +789,22 @@ class SitesManagerTest extends DatabaseTestCase
}
/**
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSitesGroups()
{
- $groups = array( 'group1', ' group1 ', '', 'group2');
- $expectedGroups = array('group1','','group2');
- foreach($groups as $group)
- {
- Piwik_SitesManager_API::getInstance()->addSite("test toto@{}", 'http://example.org',$ecommerce=1,$siteSearch = null, $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIps = null, $excludedQueryParameters = null, $timezone = null, $currency = null, $group);
+ $groups = array('group1', ' group1 ', '', 'group2');
+ $expectedGroups = array('group1', '', 'group2');
+ foreach ($groups as $group) {
+ Piwik_SitesManager_API::getInstance()->addSite("test toto@{}", 'http://example.org', $ecommerce = 1, $siteSearch = null, $searchKeywordParameters = null, $searchCategoryParameters = null, $excludedIps = null, $excludedQueryParameters = null, $timezone = null, $currency = null, $group);
}
-
+
$this->assertEquals($expectedGroups, Piwik_SitesManager_API::getInstance()->getSitesGroups());
}
-
+
public function getInvalidTimezoneData()
{
return array(
@@ -811,9 +812,9 @@ class SitesManagerTest extends DatabaseTestCase
array('Paris'),
);
}
-
+
/**
- *
+ *
* @dataProvider getInvalidTimezoneData
* @group Plugins
* @group SitesManager
@@ -821,16 +822,16 @@ class SitesManagerTest extends DatabaseTestCase
public function testAddSitesInvalidTimezone($timezone)
{
try {
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'),$ecommerce=0,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,$ip= '', $params='', $timezone);
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array('http://example.org'), $ecommerce = 0,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $ip = '', $params = '', $timezone);
} catch (Exception $e) {
return;
}
$this->fail('Expected exception not raised');
}
-
+
/**
- *
+ *
* @group Plugins
* @group SitesManager
*/
@@ -838,16 +839,16 @@ class SitesManagerTest extends DatabaseTestCase
{
try {
$invalidCurrency = '€';
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'),$ecommerce=0,
- $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,'', 'UTC', $invalidCurrency);
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array('http://example.org'), $ecommerce = 0,
+ $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, '', 'UTC', $invalidCurrency);
} catch (Exception $e) {
return;
}
$this->fail('Expected exception not raised');
}
-
+
/**
- *
+ *
* @group Plugins
* @group SitesManager
*/
@@ -862,9 +863,9 @@ class SitesManagerTest extends DatabaseTestCase
$this->assertEquals('', $excludedIps);
$excludedQueryParameters = Piwik_SitesManager_API::getInstance()->getExcludedQueryParametersGlobal();
$this->assertEquals('', $excludedQueryParameters);
-
+
// test that when not specified, defaults are set as expected
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array('http://example.org'));
$site = new Piwik_Site($idsite);
$this->assertEquals('UTC', $site->getTimezone());
$this->assertEquals('USD', $site->getCurrency());
@@ -883,13 +884,13 @@ class SitesManagerTest extends DatabaseTestCase
Piwik_SitesManager_API::getInstance()->setDefaultCurrency($newDefaultCurrency);
$defaultCurrency = Piwik_SitesManager_API::getInstance()->getDefaultCurrency();
$this->assertEquals($newDefaultCurrency, $defaultCurrency);
-
+
// set the global IPs to exclude and get it
$newGlobalExcludedIps = '1.1.1.*,1.1.*.*,150.1.1.1';
Piwik_SitesManager_API::getInstance()->setGlobalExcludedIps($newGlobalExcludedIps);
$globalExcludedIps = Piwik_SitesManager_API::getInstance()->getExcludedIpsGlobal();
$this->assertEquals($newGlobalExcludedIps, $globalExcludedIps);
-
+
// set the global URL query params to exclude and get it
$newGlobalExcludedQueryParameters = 'PHPSESSID,blabla, TesT';
// removed the space
@@ -900,37 +901,37 @@ class SitesManagerTest extends DatabaseTestCase
// create a website and check that default currency and default timezone are set
// however, excluded IPs and excluded query Params are not returned
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'),$ecommerce=0,
- $siteSearch = 0, $searchKeywordParameters = 'test1,test2', $searchCategoryParameters = 'test2,test1',
- '', '', $newDefaultTimezone);
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array('http://example.org'), $ecommerce = 0,
+ $siteSearch = 0, $searchKeywordParameters = 'test1,test2', $searchCategoryParameters = 'test2,test1',
+ '', '', $newDefaultTimezone);
$site = new Piwik_Site($idsite);
$this->assertEquals($newDefaultTimezone, $site->getTimezone());
$this->assertEquals(date('Y-m-d'), $site->getCreationDate()->toString());
$this->assertEquals($newDefaultCurrency, $site->getCurrency());
- $this->assertEquals('', $site->getExcludedIps());
- $this->assertEquals('', $site->getExcludedQueryParameters());
- $this->assertEquals('test1,test2', $site->getSearchKeywordParameters());
- $this->assertEquals('test2,test1', $site->getSearchCategoryParameters());
- $this->assertFalse($site->isSiteSearchEnabled());
- $this->assertFalse(Piwik_Site::isSiteSearchEnabledFor($idsite));
- $this->assertFalse($site->isEcommerceEnabled());
- $this->assertFalse(Piwik_Site::isEcommerceEnabledFor($idsite));
+ $this->assertEquals('', $site->getExcludedIps());
+ $this->assertEquals('', $site->getExcludedQueryParameters());
+ $this->assertEquals('test1,test2', $site->getSearchKeywordParameters());
+ $this->assertEquals('test2,test1', $site->getSearchCategoryParameters());
+ $this->assertFalse($site->isSiteSearchEnabled());
+ $this->assertFalse(Piwik_Site::isSiteSearchEnabledFor($idsite));
+ $this->assertFalse($site->isEcommerceEnabled());
+ $this->assertFalse(Piwik_Site::isEcommerceEnabledFor($idsite));
}
-
+
/**
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSitesIdFromSiteUrlSuperUser()
{
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array("http://piwik.net","http://piwik.com"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2",array("http://piwik.com","http://piwik.net"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.com","http://piwik.org"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array("http://piwik.net", "http://piwik.com"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2", array("http://piwik.com", "http://piwik.net"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3", array("http://piwik.com", "http://piwik.org"));
$idsites = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://piwik.org');
$this->assertTrue(count($idsites) == 1);
-
+
$idsites = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://www.piwik.org');
$this->assertTrue(count($idsites) == 1);
@@ -942,15 +943,15 @@ class SitesManagerTest extends DatabaseTestCase
}
/**
- *
+ *
* @group Plugins
* @group SitesManager
*/
public function testGetSitesIdFromSiteUrlUser()
{
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array("http://www.piwik.net","http://piwik.com"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2",array("http://piwik.com","http://piwik.net"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.com","http://piwik.org"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array("http://www.piwik.net", "http://piwik.com"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2", array("http://piwik.com", "http://piwik.net"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3", array("http://piwik.com", "http://piwik.org"));
$saveAccess = Zend_Registry::get('access');
@@ -962,18 +963,18 @@ class SitesManagerTest extends DatabaseTestCase
Piwik_UsersManager_API::getInstance()->setUserAccess("user2", "admin", array(3));
Piwik_UsersManager_API::getInstance()->addUser("user3", "geqgegagae", "tegst3@tesgt.com", "alias");
- Piwik_UsersManager_API::getInstance()->setUserAccess("user3", "view", array(1,2));
+ Piwik_UsersManager_API::getInstance()->setUserAccess("user3", "view", array(1, 2));
Piwik_UsersManager_API::getInstance()->setUserAccess("user3", "admin", array(3));
$pseudoMockAccess = new FakeAccess;
FakeAccess::$superUser = false;
FakeAccess::$identity = 'user1';
- FakeAccess::setIdSitesView (array(1));
- FakeAccess::setIdSitesAdmin (array());
+ FakeAccess::setIdSitesView(array(1));
+ FakeAccess::setIdSitesAdmin(array());
Zend_Registry::set('access', $pseudoMockAccess);
$idsites = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://piwik.com');
$this->assertEquals(1, count($idsites));
-
+
// testing URL normalization
$idsites = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://www.piwik.com');
$this->assertEquals(1, count($idsites));
@@ -983,8 +984,8 @@ class SitesManagerTest extends DatabaseTestCase
$pseudoMockAccess = new FakeAccess;
FakeAccess::$superUser = false;
FakeAccess::$identity = 'user2';
- FakeAccess::setIdSitesView (array(1));
- FakeAccess::setIdSitesAdmin (array(3));
+ FakeAccess::setIdSitesView(array(1));
+ FakeAccess::setIdSitesAdmin(array(3));
Zend_Registry::set('access', $pseudoMockAccess);
$idsites = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://piwik.com');
$this->assertEquals(2, count($idsites));
@@ -992,8 +993,8 @@ class SitesManagerTest extends DatabaseTestCase
$pseudoMockAccess = new FakeAccess;
FakeAccess::$superUser = false;
FakeAccess::$identity = 'user3';
- FakeAccess::setIdSitesView (array(1,2));
- FakeAccess::setIdSitesAdmin (array(3));
+ FakeAccess::setIdSitesView(array(1, 2));
+ FakeAccess::setIdSitesAdmin(array(3));
Zend_Registry::set('access', $pseudoMockAccess);
$idsites = Piwik_SitesManager_API::getInstance()->getSitesIdFromSiteUrl('http://piwik.com');
$this->assertEquals(3, count($idsites));
@@ -1008,11 +1009,11 @@ class SitesManagerTest extends DatabaseTestCase
*/
public function testGetSitesFromTimezones()
{
- $idsite1 = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org"),null,$siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,null,null,'UTC');
- $idsite2 = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org"),null,$siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,null,null,'Pacific/Auckland');
- $idsite3 = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org"),null,$siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,null,null,'Pacific/Auckland');
- $idsite4 = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org"),null,$siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null,null,null,'UTC+10');
+ $idsite1 = Piwik_SitesManager_API::getInstance()->addSite("site3", array("http://piwik.org"), null, $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, null, null, 'UTC');
+ $idsite2 = Piwik_SitesManager_API::getInstance()->addSite("site3", array("http://piwik.org"), null, $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, null, null, 'Pacific/Auckland');
+ $idsite3 = Piwik_SitesManager_API::getInstance()->addSite("site3", array("http://piwik.org"), null, $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, null, null, 'Pacific/Auckland');
+ $idsite4 = Piwik_SitesManager_API::getInstance()->addSite("site3", array("http://piwik.org"), null, $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, null, null, 'UTC+10');
$result = Piwik_SitesManager_API::getInstance()->getSitesIdFromTimezones(array('UTC+10', 'Pacific/Auckland'));
- $this->assertEquals(array($idsite2,$idsite3,$idsite4), $result);
+ $this->assertEquals(array($idsite2, $idsite3, $idsite4), $result);
}
}
diff --git a/tests/PHPUnit/Plugins/UserCountryTest.php b/tests/PHPUnit/Plugins/UserCountryTest.php
index 4a41cd054e..dbf4168eaa 100644
--- a/tests/PHPUnit/Plugins/UserCountryTest.php
+++ b/tests/PHPUnit/Plugins/UserCountryTest.php
@@ -5,36 +5,36 @@
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-require_once PIWIK_INCLUDE_PATH.'/plugins/UserCountry/UserCountry.php';
+require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/UserCountry.php';
require_once 'UserCountry/functions.php';
-require_once PIWIK_INCLUDE_PATH.'/core/DataFiles/Countries.php';
+require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Countries.php';
class Test_Piwik_UserCountry extends PHPUnit_Framework_Testcase
{
/**
- *
+ *
* @group Plugins
* @group UserCountry
*/
public function testGetFlagFromCode()
{
$flag = Piwik_getFlagFromCode("us");
- $this->assertEquals( basename($flag), "us.png" );
+ $this->assertEquals(basename($flag), "us.png");
}
/**
- *
+ *
* @group Plugins
* @group UserCountry
*/
public function testGetFlagFromInvalidCode()
{
$flag = Piwik_getFlagFromCode("foo");
- $this->assertEquals( basename($flag), "xx.png" );
+ $this->assertEquals(basename($flag), "xx.png");
}
/**
- *
+ *
* @group Plugins
* @group UserCountry
*/
@@ -49,8 +49,7 @@ class Test_Piwik_UserCountry extends PHPUnit_Framework_Testcase
$flags = scandir(PIWIK_PATH_TEST_TO_ROOT . '/plugins/UserCountry/flags/');
// Get list of countries
- foreach($countries as $country => $continent)
- {
+ foreach ($countries as $country => $continent) {
// test continent
$this->assertContains($continent, $continents);
@@ -58,10 +57,8 @@ class Test_Piwik_UserCountry extends PHPUnit_Framework_Testcase
$this->assertContains($country . '.png', $flags);
}
- foreach($flags as $filename)
- {
- if($filename == '.' || $filename == '..')
- {
+ foreach ($flags as $filename) {
+ if ($filename == '.' || $filename == '..') {
continue;
}
@@ -71,80 +68,78 @@ class Test_Piwik_UserCountry extends PHPUnit_Framework_Testcase
$this->assertArrayHasKey($country, $countries, $filename);
}
}
-
+
// test that redundant checks work
public function testGeoIpUpdaterRedundantChecks()
{
- Piwik_UserCountry_LocationProvider_GeoIp::$geoIPDatabaseDir = 'tests/lib/geoip-files';
- Piwik_UserCountry_LocationProvider::$providers = null;
-
- // create empty ISP & Org files
- $this->createEmptyISPOrgFiles();
-
- // run redundant checks
- $updater = new Piwik_UserCountry_GeoIPAutoUpdater_publictestRedundantChecks();
- $updater->performRedundantDbChecks();
-
- // check that files are renamed correctly
- $this->checkBrokenGeoIPState();
-
- // create empty files again & run checks again
- $this->createEmptyISPOrgFiles();
- $updater->performRedundantDbChecks();
-
- // check that w/ broken files already there, redundant checks still work correctly
- $this->checkBrokenGeoIPState();
+ Piwik_UserCountry_LocationProvider_GeoIp::$geoIPDatabaseDir = 'tests/lib/geoip-files';
+ Piwik_UserCountry_LocationProvider::$providers = null;
+
+ // create empty ISP & Org files
+ $this->createEmptyISPOrgFiles();
+
+ // run redundant checks
+ $updater = new Piwik_UserCountry_GeoIPAutoUpdater_publictestRedundantChecks();
+ $updater->performRedundantDbChecks();
+
+ // check that files are renamed correctly
+ $this->checkBrokenGeoIPState();
+
+ // create empty files again & run checks again
+ $this->createEmptyISPOrgFiles();
+ $updater->performRedundantDbChecks();
+
+ // check that w/ broken files already there, redundant checks still work correctly
+ $this->checkBrokenGeoIPState();
}
-
+
public function setUp()
{
- Piwik::$shouldLog = null;
+ Piwik::$shouldLog = null;
}
-
+
public function tearDown()
{
- $geoIpDirPath = PIWIK_INCLUDE_PATH.'/tests/lib/geoip-files';
- $filesToRemove = array('GeoIPISP.dat.broken', 'GeoIPOrg.dat.broken', 'GeoIPISP.dat', 'GeoIPOrg.dat');
-
- foreach ($filesToRemove as $name)
- {
- $path = $geoIpDirPath.'/'.$name;
- if (file_exists($path))
- {
- unlink($path);
- }
- }
+ $geoIpDirPath = PIWIK_INCLUDE_PATH . '/tests/lib/geoip-files';
+ $filesToRemove = array('GeoIPISP.dat.broken', 'GeoIPOrg.dat.broken', 'GeoIPISP.dat', 'GeoIPOrg.dat');
+
+ foreach ($filesToRemove as $name) {
+ $path = $geoIpDirPath . '/' . $name;
+ if (file_exists($path)) {
+ unlink($path);
+ }
+ }
}
-
+
private function createEmptyISPOrgFiles()
{
- $geoIpDir = PIWIK_INCLUDE_PATH.'/tests/lib/geoip-files';
-
- $fd = fopen($geoIpDir.'/GeoIPISP.dat', 'w');
- fclose($fd);
-
- $fd = fopen($geoIpDir.'/GeoIPOrg.dat', 'w');
- fclose($fd);
+ $geoIpDir = PIWIK_INCLUDE_PATH . '/tests/lib/geoip-files';
+
+ $fd = fopen($geoIpDir . '/GeoIPISP.dat', 'w');
+ fclose($fd);
+
+ $fd = fopen($geoIpDir . '/GeoIPOrg.dat', 'w');
+ fclose($fd);
}
-
+
private function checkBrokenGeoIPState()
{
- $geoIpDir = PIWIK_INCLUDE_PATH.'/tests/lib/geoip-files';
-
- $this->assertFalse(file_exists($geoIpDir.'/GeoIPCity.dat.broken'));
-
- $this->assertFalse(file_exists($geoIpDir.'/GeoIPISP.dat'));
- $this->assertTrue(file_exists($geoIpDir.'/GeoIPISP.dat.broken'));
-
- $this->assertFalse(file_exists($geoIpDir.'/GeoIPOrg.dat'));
- $this->assertTrue(file_exists($geoIpDir.'/GeoIPOrg.dat.broken'));
+ $geoIpDir = PIWIK_INCLUDE_PATH . '/tests/lib/geoip-files';
+
+ $this->assertFalse(file_exists($geoIpDir . '/GeoIPCity.dat.broken'));
+
+ $this->assertFalse(file_exists($geoIpDir . '/GeoIPISP.dat'));
+ $this->assertTrue(file_exists($geoIpDir . '/GeoIPISP.dat.broken'));
+
+ $this->assertFalse(file_exists($geoIpDir . '/GeoIPOrg.dat'));
+ $this->assertTrue(file_exists($geoIpDir . '/GeoIPOrg.dat.broken'));
}
}
class Piwik_UserCountry_GeoIPAutoUpdater_publictestRedundantChecks extends Piwik_UserCountry_GeoIPAutoUpdater
{
- public function performRedundantDbChecks()
- {
- parent::performRedundantDbChecks();
- }
+ public function performRedundantDbChecks()
+ {
+ parent::performRedundantDbChecks();
+ }
}
diff --git a/tests/PHPUnit/Plugins/UserSettingsTest.php b/tests/PHPUnit/Plugins/UserSettingsTest.php
index a6dfc860f9..fb99c787a6 100644
--- a/tests/PHPUnit/Plugins/UserSettingsTest.php
+++ b/tests/PHPUnit/Plugins/UserSettingsTest.php
@@ -168,7 +168,7 @@ class UserSettingsTest extends PHPUnit_Framework_TestCase
array('BLB', 'BlackBerry', 'BlackBerry'))),
array('Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/0.0.1 Safari/534.11+', array(
- array('BP', 'PlayBook', 'PlayBook', '0.0', '0', '0', 'webkit' ),
+ array('BP', 'PlayBook', 'PlayBook', '0.0', '0', '0', 'webkit'),
array('QNX', 'QNX', 'QNX'))),
// BrowseX
@@ -1002,10 +1002,9 @@ class UserSettingsTest extends PHPUnit_Framework_TestCase
$res = UserAgentParser::getBrowser($userAgent);
$family = false;
- if($res === false)
+ if ($res === false)
$this->assertFalse($expected[0]);
- else
- {
+ else {
$family = Piwik_getBrowserFamily($res['id']);
$this->assertEquals($expected[0][0], $res['id']);
$this->assertEquals($expected[0][1], $res['name']);
diff --git a/tests/PHPUnit/Plugins/UsersManagerTest.php b/tests/PHPUnit/Plugins/UsersManagerTest.php
index 33a1faf95c..4b8bc9b0e8 100644
--- a/tests/PHPUnit/Plugins/UsersManagerTest.php
+++ b/tests/PHPUnit/Plugins/UsersManagerTest.php
@@ -10,140 +10,137 @@ class UsersManagerTest extends DatabaseTestCase
public function setUp()
{
parent::setUp();
-
+
Piwik_PluginsManager::getInstance()->loadPlugin('UsersManager');
Piwik_PluginsManager::getInstance()->installLoadedPlugins();
-
+
// setup the access layer
$pseudoMockAccess = new FakeAccess;
- FakeAccess::setIdSitesView( array(1,2));
- FakeAccess::setIdSitesAdmin( array(3,4));
-
+ FakeAccess::setIdSitesView(array(1, 2));
+ FakeAccess::setIdSitesAdmin(array(3, 4));
+
//finally we set the user as a super user by default
FakeAccess::$superUser = true;
Zend_Registry::set('access', $pseudoMockAccess);
-
+
// we make sure the tests don't depend on the config file content
Piwik_Config::getInstance()->superuser = array(
- 'login'=>'superusertest',
- 'password'=>'passwordsuperusertest',
- 'email'=>'superuser@example.com'
+ 'login' => 'superusertest',
+ 'password' => 'passwordsuperusertest',
+ 'email' => 'superuser@example.com'
);
}
private function _flatten($sitesAccess)
{
- $result = array();;
+ $result = array();
+ ;
- foreach($sitesAccess as $siteAccess)
- {
- $result[ $siteAccess['site'] ] = $siteAccess['access'];
+ foreach ($sitesAccess as $siteAccess) {
+ $result[$siteAccess['site']] = $siteAccess['access'];
}
return $result;
}
- private function _checkUserHasNotChanged($user, $newPassword, $newEmail = null, $newAlias= null)
+ private function _checkUserHasNotChanged($user, $newPassword, $newEmail = null, $newAlias = null)
{
- if(is_null($newEmail))
- {
+ if (is_null($newEmail)) {
$newEmail = $user['email'];
}
- if(is_null($newAlias))
- {
+ if (is_null($newAlias)) {
$newAlias = $user['alias'];
}
$userAfter = Piwik_UsersManager_API::getInstance()->getUser($user["login"]);
unset($userAfter['date_registered']);
-
+
// we now compute what the token auth should be, it should always be a hash of the login and the current password
// if the password has changed then the token_auth has changed!
- $user['token_auth']= Piwik_UsersManager_API::getInstance()->getTokenAuth($user["login"], md5($newPassword) );
-
- $user['password']=md5($newPassword);
- $user['email']=$newEmail;
- $user['alias']=$newAlias;
- $this->assertEquals($user,$userAfter);
+ $user['token_auth'] = Piwik_UsersManager_API::getInstance()->getTokenAuth($user["login"], md5($newPassword));
+
+ $user['password'] = md5($newPassword);
+ $user['email'] = $newEmail;
+ $user['alias'] = $newAlias;
+ $this->assertEquals($user, $userAfter);
}
-
+
/**
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testAllSuperUserIncluded()
{
Piwik_Config::getInstance()->superuser = array(
- 'login'=>'superusertest',
- 'password'=>'passwordsuperusertest',
- 'email'=>'superuser@example.com'
+ 'login' => 'superusertest',
+ 'password' => 'passwordsuperusertest',
+ 'email' => 'superuser@example.com'
);
-
- $user = array( 'login'=>'user',
- 'password'=>"geqgeagae",
- 'email'=>"test@test.com",
- 'alias'=>"alias");
- Piwik_UsersManager_API::getInstance()->addUser($user['login'],$user['password'] ,$user['email'] ,$user['alias'] );
-
+
+ $user = array('login' => 'user',
+ 'password' => "geqgeagae",
+ 'email' => "test@test.com",
+ 'alias' => "alias");
+ Piwik_UsersManager_API::getInstance()->addUser($user['login'], $user['password'], $user['email'], $user['alias']);
+
$exceptionNotRaised = false;
try {
- Piwik_UsersManager_API::getInstance()->addUser('superusertest','te','fake@fale.co','ega');
+ Piwik_UsersManager_API::getInstance()->addUser('superusertest', 'te', 'fake@fale.co', 'ega');
$exceptionNotRaised = true;
} catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionSuperUser)", $expected->getMessage());
}
try {
- Piwik_UsersManager_API::getInstance()->updateUser('superusertest','te','fake@fale.co','ega');
+ Piwik_UsersManager_API::getInstance()->updateUser('superusertest', 'te', 'fake@fale.co', 'ega');
$exceptionNotRaised = true;
} catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionSuperUser)", $expected->getMessage());
}
try {
- Piwik_UsersManager_API::getInstance()->deleteUser('superusertest','te','fake@fale.co','ega');
+ Piwik_UsersManager_API::getInstance()->deleteUser('superusertest', 'te', 'fake@fale.co', 'ega');
$exceptionNotRaised = true;
} catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionSuperUser)", $expected->getMessage());
}
try {
- Piwik_UsersManager_API::getInstance()->deleteUser('superusertest','te','fake@fale.co','ega');
+ Piwik_UsersManager_API::getInstance()->deleteUser('superusertest', 'te', 'fake@fale.co', 'ega');
$exceptionNotRaised = true;
} catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionSuperUser)", $expected->getMessage());
}
- if($exceptionNotRaised) {
+ if ($exceptionNotRaised) {
$this->fail();
}
}
-
+
/**
* bad password => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testUpdateUserBadpasswd()
{
- $login="login";
- $user = array('login'=>$login,
- 'password'=>"geqgeagae",
- 'email'=>"test@test.com",
- 'alias'=>"alias");
-
- Piwik_UsersManager_API::getInstance()->addUser($user['login'],$user['password'] ,$user['email'] ,$user['alias'] );
-
-
+ $login = "login";
+ $user = array('login' => $login,
+ 'password' => "geqgeagae",
+ 'email' => "test@test.com",
+ 'alias' => "alias");
+
+ Piwik_UsersManager_API::getInstance()->addUser($user['login'], $user['password'], $user['email'], $user['alias']);
+
+
try {
- Piwik_UsersManager_API::getInstance()->updateUser( $login, "pas");
- }
- catch (Exception $expected) {
+ Piwik_UsersManager_API::getInstance()->updateUser($login, "pas");
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionInvalidPassword)", $expected->getMessage());
-
- $this->_checkUserHasNotChanged($user,$user['password']);
+
+ $this->_checkUserHasNotChanged($user, $user['password']);
return;
}
$this->fail("Exception not raised.");
-
+
}
-
+
/**
* Dataprovider
*/
@@ -156,9 +153,9 @@ class UsersManagerTest extends DatabaseTestCase
array("geg'ag11gge@", "password", "email@email.com", "alias"), // wrong login / bad characters => exception
);
}
-
+
/**
- *
+ *
* @dataProvider getAddUserInvalidLoginData
* @group Plugins
* @group UsersManager
@@ -167,17 +164,16 @@ class UsersManagerTest extends DatabaseTestCase
{
try {
Piwik_UsersManager_API::getInstance()->addUser($userLogin, $password, $email, $alias);
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionInvalidLogin)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
+
/**
* existing login => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -186,13 +182,12 @@ class UsersManagerTest extends DatabaseTestCase
try {
Piwik_UsersManager_API::getInstance()->addUser("test", "password", "email@email.com", "alias");
Piwik_UsersManager_API::getInstance()->addUser("test", "password2", "em2ail@email.com", "al2ias");
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionLoginExists)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
-
+
}
/**
@@ -201,9 +196,9 @@ class UsersManagerTest extends DatabaseTestCase
public function getWrongPasswordTestData()
{
return array(
- array("geggeqgeqag", "pas", "email@email.com", "alias"), // too short -> exception
+ array("geggeqgeqag", "pas", "email@email.com", "alias"), // too short -> exception
array("ghqgeggg", "gegageqqqqqqqgeqgqeg84897897897897g122", "email@email.com", "alias"), // too long -> exception
- array("geggeqgeqag", "", "email@email.com", "alias"), // empty -> exception
+ array("geggeqgeqag", "", "email@email.com", "alias"), // empty -> exception
);
}
@@ -217,8 +212,7 @@ class UsersManagerTest extends DatabaseTestCase
{
try {
Piwik_UsersManager_API::getInstance()->addUser($userLogin, $password, $email, $alias);
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionInvalidPassword)", $expected->getMessage());
return;
}
@@ -248,121 +242,118 @@ class UsersManagerTest extends DatabaseTestCase
{
try {
Piwik_UsersManager_API::getInstance()->addUser($userLogin, $password, $email, $alias);
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(mail)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
+
/**
* empty email => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testAddUserEmptyEmail()
{
-
+
try {
Piwik_UsersManager_API::getInstance()->addUser("geggeqgeqag", "geqgeagae", "", "alias");
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(mail)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
+
/**
* empty alias => use login
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testAddUserEmptyAlias()
{
- $login ="geggeqgeqag";
+ $login = "geggeqgeqag";
Piwik_UsersManager_API::getInstance()->addUser($login, "geqgeagae", "mgeagi@geq.com", "");
$user = Piwik_UsersManager_API::getInstance()->getUser($login);
$this->assertEquals($login, $user['alias']);
$this->assertEquals($login, $user['login']);
}
-
+
/**
* no alias => use login
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testAddUserNoAliasSpecified()
{
- $login ="geggeqg455eqag";
+ $login = "geggeqg455eqag";
Piwik_UsersManager_API::getInstance()->addUser($login, "geqgeagae", "mgeagi@geq.com");
$user = Piwik_UsersManager_API::getInstance()->getUser($login);
$this->assertEquals($login, $user['alias']);
$this->assertEquals($login, $user['login']);
}
-
+
/**
* normal test case
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testAddUser()
{
- $login ="geggeq55eqag";
+ $login = "geggeq55eqag";
$password = "mypassword";
$email = "mgeag4544i@geq.com";
$alias = "her is my alias )(&|\" '£%*(&%+))";
-
+
$time = time();
Piwik_UsersManager_API::getInstance()->addUser($login, $password, $email, $alias);
$user = Piwik_UsersManager_API::getInstance()->getUser($login);
-
+
// check that the date registered is correct
- $this->assertTrue( $time <= strtotime($user['date_registered']) && strtotime($user['date_registered']) <= time(),
- "the date_registered ".strtotime($user['date_registered'])." is different from the time() ". time());
- $this->assertTrue($user['date_registered'] <= time() );
-
+ $this->assertTrue($time <= strtotime($user['date_registered']) && strtotime($user['date_registered']) <= time(),
+ "the date_registered " . strtotime($user['date_registered']) . " is different from the time() " . time());
+ $this->assertTrue($user['date_registered'] <= time());
+
// check that token is 32 chars
$this->assertEquals(32, strlen($user['password']));
-
+
// that the password has been md5
- $this->assertEquals(md5($login.md5($password)), $user['token_auth']);
-
+ $this->assertEquals(md5($login . md5($password)), $user['token_auth']);
+
// check that all fields are the same
$this->assertEquals($login, $user['login']);
$this->assertEquals(md5($password), $user['password']);
$this->assertEquals($email, $user['email']);
$this->assertEquals($alias, $user['alias']);
}
-
+
/**
* user doesnt exist => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testSeleteUserDoesntExist()
- {
+ {
Piwik_UsersManager_API::getInstance()->addUser("geggeqgeqag", "geqgeagae", "test@test.com", "alias");
-
+
try {
Piwik_UsersManager_API::getInstance()->deleteUser("geggeqggnew");
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionDeleteDoesNotExist)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
+
/**
* empty name, doesnt exists =>exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -370,17 +361,16 @@ class UsersManagerTest extends DatabaseTestCase
{
try {
Piwik_UsersManager_API::getInstance()->deleteUser("");
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionDeleteDoesNotExist)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
+
/**
* null user,, doesnt exists => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -388,59 +378,57 @@ class UsersManagerTest extends DatabaseTestCase
{
try {
Piwik_UsersManager_API::getInstance()->deleteUser(null);
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionDeleteDoesNotExist)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
+
/**
* normal case, user deleted
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testDeleteUser()
{
//create the 3 websites
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array("http://piwik.net","http://piwik.com/test/"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2",array("http://piwik.com/test/"));
- $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org"));
-
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1", array("http://piwik.net", "http://piwik.com/test/"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site2", array("http://piwik.com/test/"));
+ $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3", array("http://piwik.org"));
+
//add user and set some rights
Piwik_UsersManager_API::getInstance()->addUser("geggeqgeqag", "geqgeagae", "test@test.com", "alias");
- Piwik_UsersManager_API::getInstance()->setUserAccess("geggeqgeqag", "view", array(1,2));
- Piwik_UsersManager_API::getInstance()->setUserAccess("geggeqgeqag", "admin", array(1,3));
-
+ Piwik_UsersManager_API::getInstance()->setUserAccess("geggeqgeqag", "view", array(1, 2));
+ Piwik_UsersManager_API::getInstance()->setUserAccess("geggeqgeqag", "admin", array(1, 3));
+
// check rights are set
$this->assertNotEquals(array(), Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("geggeqgeqag"));
-
+
// delete the user
Piwik_UsersManager_API::getInstance()->deleteUser("geggeqgeqag");
-
+
// try to get it, it should raise an exception
try {
$user = Piwik_UsersManager_API::getInstance()->getUser("geggeqgeqag");
$this->fail("Exception not raised.");
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionUserDoesNotExist)", $expected->getMessage());
}
// add the same user
Piwik_UsersManager_API::getInstance()->addUser("geggeqgeqag", "geqgeagae", "test@test.com", "alias");
-
+
//checks access have been deleted
//to do so we recreate the same user login and check if the rights are still there
$this->assertEquals(array(), Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("geggeqgeqag"));
}
-
-
+
+
/**
* no user => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -449,44 +437,43 @@ class UsersManagerTest extends DatabaseTestCase
// try to get it, it should raise an exception
try {
$user = Piwik_UsersManager_API::getInstance()->getUser("geggeqgeqag");
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionUserDoesNotExist)", $expected->getMessage());
return;
}
-
+
$this->fail("Exception not raised.");
}
-
+
/**
* normal case
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function test_GetUser()
{
- $login ="geggeq55eqag";
+ $login = "geggeq55eqag";
$password = "mypassword";
$email = "mgeag4544i@geq.com";
$alias = "";
-
+
Piwik_UsersManager_API::getInstance()->addUser($login, $password, $email, $alias);
$user = Piwik_UsersManager_API::getInstance()->getUser($login);
-
+
// check that all fields are the same
$this->assertEquals($login, $user['login']);
$this->assertInternalType('string', $user['password']);
$this->assertInternalType('string', $user['date_registered']);
$this->assertEquals($email, $user['email']);
-
+
//alias shouldnt be empty even if no alias specified
$this->assertGreaterThan(0, strlen($user['alias']));
}
-
+
/**
* no user => empty array
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -494,11 +481,11 @@ class UsersManagerTest extends DatabaseTestCase
{
$this->assertEquals(Piwik_UsersManager_API::getInstance()->getUsers(), array());
}
-
+
/**
* normal case
* as well as selecting specific user names, comma separated
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -507,51 +494,50 @@ class UsersManagerTest extends DatabaseTestCase
Piwik_UsersManager_API::getInstance()->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
Piwik_UsersManager_API::getInstance()->addUser("geggeqge632ge56a4qag", "geqgegeagae", "tesggt@tesgt.com", "alias");
Piwik_UsersManager_API::getInstance()->addUser("geggeqgeqagqegg", "geqgeaggggae", "tesgggt@tesgt.com");
-
+
$users = Piwik_UsersManager_API::getInstance()->getUsers();
$users = $this->_removeNonTestableFieldsFromUsers($users);
- $user1 = array('login' => "gegg4564eqgeqag", 'password' => md5("geqgegagae"), 'alias' => "alias", 'email' => "tegst@tesgt.com");
- $user2 = array('login' => "geggeqge632ge56a4qag", 'password' => md5("geqgegeagae"),'alias' => "alias", 'email' => "tesggt@tesgt.com");
- $user3 = array('login' => "geggeqgeqagqegg", 'password' => md5("geqgeaggggae"), 'alias' => 'geggeqgeqagqegg','email' => "tesgggt@tesgt.com");
+ $user1 = array('login' => "gegg4564eqgeqag", 'password' => md5("geqgegagae"), 'alias' => "alias", 'email' => "tegst@tesgt.com");
+ $user2 = array('login' => "geggeqge632ge56a4qag", 'password' => md5("geqgegeagae"), 'alias' => "alias", 'email' => "tesggt@tesgt.com");
+ $user3 = array('login' => "geggeqgeqagqegg", 'password' => md5("geqgeaggggae"), 'alias' => 'geggeqgeqagqegg', 'email' => "tesgggt@tesgt.com");
$expectedUsers = array($user1, $user2, $user3);
$this->assertEquals($expectedUsers, $users);
$this->assertEquals(array($user1), $this->_removeNonTestableFieldsFromUsers(Piwik_UsersManager_API::getInstance()->getUsers('gegg4564eqgeqag')));
$this->assertEquals(array($user1, $user2), $this->_removeNonTestableFieldsFromUsers(Piwik_UsersManager_API::getInstance()->getUsers('gegg4564eqgeqag,geggeqge632ge56a4qag')));
-
+
}
-
+
protected function _removeNonTestableFieldsFromUsers($users)
{
- foreach($users as &$user)
- {
+ foreach ($users as &$user) {
unset($user['token_auth']);
unset($user['date_registered']);
}
return $users;
}
-
+
/**
* normal case
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testGetUsersLogin()
{
-
+
Piwik_UsersManager_API::getInstance()->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
Piwik_UsersManager_API::getInstance()->addUser("geggeqge632ge56a4qag", "geqgegeagae", "tesggt@tesgt.com", "alias");
Piwik_UsersManager_API::getInstance()->addUser("geggeqgeqagqegg", "geqgeaggggae", "tesgggt@tesgt.com");
-
+
$logins = Piwik_UsersManager_API::getInstance()->getUsersLogin();
-
+
$this->assertEquals(array("gegg4564eqgeqag", "geggeqge632ge56a4qag", "geggeqgeqagqegg"), $logins);
}
-
-
+
+
/**
* no login => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -560,89 +546,87 @@ class UsersManagerTest extends DatabaseTestCase
// try to get it, it should raise an exception
try {
Piwik_UsersManager_API::getInstance()->setUserAccess("nologin", "view", 1);
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionUserDoesNotExist)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
+
/**
* wrong access specified => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testSetUserAccessWrongAccess()
{
Piwik_UsersManager_API::getInstance()->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
-
+
// try to get it, it should raise an exception
try {
Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "viewnotknown", 1);
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionAccessValues)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
+
/**
* idsites = all => apply access to all websites with admin access
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testSetUserAccessIdsitesIsAll()
{
Piwik_UsersManager_API::getInstance()->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
-
+
FakeAccess::$superUser = false;
-
+
Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "view", "all");
-
+
FakeAccess::$superUser = true;
$access = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("gegg4564eqgeqag");
$access = $this->_flatten($access);
-
+
FakeAccess::$superUser = false;
- $this->assertEquals( array_keys($access), FakeAccess::getSitesIdWithAdminAccess());
-
+ $this->assertEquals(array_keys($access), FakeAccess::getSitesIdWithAdminAccess());
+
// we want to test the case for which we have actually set some rights
// if this is not OK then change the setUp method and add some admin rights for some websites
$this->assertGreaterThan(0, count(array_keys($access)));
}
-
+
/**
* idsites = all AND user is superuser=> apply access to all websites
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testSetUserAccessIdsitesIsAllSuperuser()
{
FakeAccess::$superUser = true;
-
- $id1=Piwik_SitesManager_API::getInstance()->addSite("test",array("http://piwik.net","http://piwik.com/test/"));
- $id2=Piwik_SitesManager_API::getInstance()->addSite("test2",array("http://piwik.net","http://piwik.com/test/"));
- $id3=Piwik_SitesManager_API::getInstance()->addSite("test3",array("http://piwik.net","http://piwik.com/test/"));
- $id4=Piwik_SitesManager_API::getInstance()->addSite("test4",array("http://piwik.net","http://piwik.com/test/"));
- $id5=Piwik_SitesManager_API::getInstance()->addSite("test5",array("http://piwik.net","http://piwik.com/test/"));
-
+
+ $id1 = Piwik_SitesManager_API::getInstance()->addSite("test", array("http://piwik.net", "http://piwik.com/test/"));
+ $id2 = Piwik_SitesManager_API::getInstance()->addSite("test2", array("http://piwik.net", "http://piwik.com/test/"));
+ $id3 = Piwik_SitesManager_API::getInstance()->addSite("test3", array("http://piwik.net", "http://piwik.com/test/"));
+ $id4 = Piwik_SitesManager_API::getInstance()->addSite("test4", array("http://piwik.net", "http://piwik.com/test/"));
+ $id5 = Piwik_SitesManager_API::getInstance()->addSite("test5", array("http://piwik.net", "http://piwik.com/test/"));
+
Piwik_UsersManager_API::getInstance()->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "view", "all");
-
+
$access = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("gegg4564eqgeqag");
$access = $this->_flatten($access);
- $this->assertEquals( array($id1,$id2,$id3,$id4,$id5), array_keys($access));
-
+ $this->assertEquals(array($id1, $id2, $id3, $id4, $id5), array_keys($access));
+
}
-
+
/**
* idsites is empty => no acccess set
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -650,98 +634,98 @@ class UsersManagerTest extends DatabaseTestCase
{
Piwik_UsersManager_API::getInstance()->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
- try {
- Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "view", array());
- $access = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("gegg4564eqgeqag");
- } catch(Exception $e) {
- return;
- }
- $this->fail('Expected exception not raised');
+ try {
+ Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "view", array());
+ $access = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("gegg4564eqgeqag");
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail('Expected exception not raised');
}
-
+
/**
* normal case, access set for only one site
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testSetUserAccessIdsitesOneSite()
{
Piwik_UsersManager_API::getInstance()->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
- $id1=Piwik_SitesManager_API::getInstance()->addSite("test",array("http://piwik.net","http://piwik.com/test/"));
-
+ $id1 = Piwik_SitesManager_API::getInstance()->addSite("test", array("http://piwik.net", "http://piwik.com/test/"));
+
Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "view", array(1));
-
+
$access = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("gegg4564eqgeqag");
$access = $this->_flatten($access);
- $this->assertEquals( array(1), array_keys($access));
+ $this->assertEquals(array(1), array_keys($access));
}
-
+
/**
* normal case, access set for multiple sites
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testSetUserAccessIdsitesMultipleSites()
{
-
+
Piwik_UsersManager_API::getInstance()->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
- $id1=Piwik_SitesManager_API::getInstance()->addSite("test",array("http://piwik.net","http://piwik.com/test/"));
- $id2=Piwik_SitesManager_API::getInstance()->addSite("test",array("http://piwik.net","http://piwik.com/test/"));
- $id3=Piwik_SitesManager_API::getInstance()->addSite("test",array("http://piwik.net","http://piwik.com/test/"));
-
- Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "view", array($id1,$id3));
-
+ $id1 = Piwik_SitesManager_API::getInstance()->addSite("test", array("http://piwik.net", "http://piwik.com/test/"));
+ $id2 = Piwik_SitesManager_API::getInstance()->addSite("test", array("http://piwik.net", "http://piwik.com/test/"));
+ $id3 = Piwik_SitesManager_API::getInstance()->addSite("test", array("http://piwik.net", "http://piwik.com/test/"));
+
+ Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "view", array($id1, $id3));
+
$access = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("gegg4564eqgeqag");
$access = $this->_flatten($access);
- $this->assertEquals( array($id1,$id3), array_keys($access));
-
+ $this->assertEquals(array($id1, $id3), array_keys($access));
+
}
-
+
/**
* normal case, string idSites comma separated access set for multiple sites
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testSetUserAccessWithIdSitesIsStringCommaSeparated()
{
Piwik_UsersManager_API::getInstance()->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
- $id1=Piwik_SitesManager_API::getInstance()->addSite("test",array("http://piwik.net","http://piwik.com/test/"));
- $id2=Piwik_SitesManager_API::getInstance()->addSite("test",array("http://piwik.net","http://piwik.com/test/"));
- $id3=Piwik_SitesManager_API::getInstance()->addSite("test",array("http://piwik.net","http://piwik.com/test/"));
-
+ $id1 = Piwik_SitesManager_API::getInstance()->addSite("test", array("http://piwik.net", "http://piwik.com/test/"));
+ $id2 = Piwik_SitesManager_API::getInstance()->addSite("test", array("http://piwik.net", "http://piwik.com/test/"));
+ $id3 = Piwik_SitesManager_API::getInstance()->addSite("test", array("http://piwik.net", "http://piwik.com/test/"));
+
Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "view", "1,3");
-
+
$access = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("gegg4564eqgeqag");
$access = $this->_flatten($access);
- $this->assertEquals( array($id1,$id3), array_keys($access));
+ $this->assertEquals(array($id1, $id3), array_keys($access));
}
-
+
/**
* normal case, set different acccess to different websites for one user
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testSetUserAccessMultipleCallDistinctAccessSameUser()
{
Piwik_UsersManager_API::getInstance()->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
- $id1=Piwik_SitesManager_API::getInstance()->addSite("test",array("http://piwik.net","http://piwik.com/test/"));
- $id2=Piwik_SitesManager_API::getInstance()->addSite("test",array("http://piwik.net","http://piwik.com/test/"));
-
+ $id1 = Piwik_SitesManager_API::getInstance()->addSite("test", array("http://piwik.net", "http://piwik.com/test/"));
+ $id2 = Piwik_SitesManager_API::getInstance()->addSite("test", array("http://piwik.net", "http://piwik.com/test/"));
+
Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "view", array($id1));
Piwik_UsersManager_API::getInstance()->setUserAccess("gegg4564eqgeqag", "admin", array($id2));
-
+
$access = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("gegg4564eqgeqag");
$access = $this->_flatten($access);
- $this->assertEquals( array($id1=>'view',$id2=>'admin'), $access);
+ $this->assertEquals(array($id1 => 'view', $id2 => 'admin'), $access);
}
-
+
/**
* normal case, set different access to different websites for multiple users
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -749,44 +733,44 @@ class UsersManagerTest extends DatabaseTestCase
{
Piwik_UsersManager_API::getInstance()->addUser("user1", "geqgegagae", "tegst@tesgt.com", "alias");
Piwik_UsersManager_API::getInstance()->addUser("user2", "geqgegagae", "tegst2@tesgt.com", "alias");
- $id1=Piwik_SitesManager_API::getInstance()->addSite("test1",array("http://piwik.net","http://piwik.com/test/"));
- $id2=Piwik_SitesManager_API::getInstance()->addSite("test2",array("http://piwik.net","http://piwik.com/test/"));
- $id3=Piwik_SitesManager_API::getInstance()->addSite("test2",array("http://piwik.net","http://piwik.com/test/"));
-
- Piwik_UsersManager_API::getInstance()->setUserAccess("user1", "view", array($id1,$id2));
+ $id1 = Piwik_SitesManager_API::getInstance()->addSite("test1", array("http://piwik.net", "http://piwik.com/test/"));
+ $id2 = Piwik_SitesManager_API::getInstance()->addSite("test2", array("http://piwik.net", "http://piwik.com/test/"));
+ $id3 = Piwik_SitesManager_API::getInstance()->addSite("test2", array("http://piwik.net", "http://piwik.com/test/"));
+
+ Piwik_UsersManager_API::getInstance()->setUserAccess("user1", "view", array($id1, $id2));
Piwik_UsersManager_API::getInstance()->setUserAccess("user2", "admin", array($id1));
Piwik_UsersManager_API::getInstance()->setUserAccess("user2", "view", array($id3, $id2));
-
+
$access1 = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("user1");
$access1 = $this->_flatten($access1);
$access2 = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("user2");
$access2 = $this->_flatten($access2);
- $wanted1 = array( $id1 => 'view', $id2 => 'view', );
- $wanted2 = array( $id1 => 'admin', $id2 => 'view', $id3 => 'view' );
-
+ $wanted1 = array($id1 => 'view', $id2 => 'view',);
+ $wanted2 = array($id1 => 'admin', $id2 => 'view', $id3 => 'view');
+
$this->assertEquals($wanted1, $access1);
$this->assertEquals($wanted2, $access2);
-
-
+
+
$access1 = Piwik_UsersManager_API::getInstance()->getUsersAccessFromSite($id1);
$access2 = Piwik_UsersManager_API::getInstance()->getUsersAccessFromSite($id2);
$access3 = Piwik_UsersManager_API::getInstance()->getUsersAccessFromSite($id3);
- $wanted1 = array( 'user1' => 'view', 'user2' => 'admin', );
- $wanted2 = array( 'user1' => 'view', 'user2' => 'view' );
- $wanted3 = array( 'user2' => 'view' );
-
+ $wanted1 = array('user1' => 'view', 'user2' => 'admin',);
+ $wanted2 = array('user1' => 'view', 'user2' => 'view');
+ $wanted3 = array('user2' => 'view');
+
$this->assertEquals($wanted1, $access1);
$this->assertEquals($wanted2, $access2);
$this->assertEquals($wanted3, $access3);
-
+
$access1 = Piwik_UsersManager_API::getInstance()->getUsersSitesFromAccess('view');
$access2 = Piwik_UsersManager_API::getInstance()->getUsersSitesFromAccess('admin');
- $wanted1 = array( 'user1' => array($id1,$id2), 'user2' => array($id2, $id3) );
- $wanted2 = array( 'user2' => array($id1) );
-
+ $wanted1 = array('user1' => array($id1, $id2), 'user2' => array($id2, $id3));
+ $wanted2 = array('user2' => array($id1));
+
$this->assertEquals($wanted1, $access1);
$this->assertEquals($wanted2, $access2);
-
+
// Test getUsersWithSiteAccess
$users = Piwik_UsersManager_API::getInstance()->getUsersWithSiteAccess($id1, $access = 'view');
$this->assertEquals(1, count($users));
@@ -799,33 +783,33 @@ class UsersManagerTest extends DatabaseTestCase
$users = Piwik_UsersManager_API::getInstance()->getUsersWithSiteAccess($id3, $access = 'admin');
$this->assertEquals(0, count($users));
}
-
+
/**
* we set access for one user for one site several times and check that it is updated
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testSetUserAccessMultipleCallOverwriteSingleUserOneSite()
{
Piwik_UsersManager_API::getInstance()->addUser("user1", "geqgegagae", "tegst@tesgt.com", "alias");
-
- $id1=Piwik_SitesManager_API::getInstance()->addSite("test1",array("http://piwik.net","http://piwik.com/test/"));
- $id2=Piwik_SitesManager_API::getInstance()->addSite("test2",array("http://piwik.net","http://piwik.com/test/"));
-
- Piwik_UsersManager_API::getInstance()->setUserAccess("user1", "view", array($id1,$id2));
+
+ $id1 = Piwik_SitesManager_API::getInstance()->addSite("test1", array("http://piwik.net", "http://piwik.com/test/"));
+ $id2 = Piwik_SitesManager_API::getInstance()->addSite("test2", array("http://piwik.net", "http://piwik.com/test/"));
+
+ Piwik_UsersManager_API::getInstance()->setUserAccess("user1", "view", array($id1, $id2));
Piwik_UsersManager_API::getInstance()->setUserAccess("user1", "admin", array($id1));
-
+
$access1 = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("user1");
$access1 = $this->_flatten($access1);
- $wanted1 = array( $id1 => 'admin', $id2 => 'view', );
-
+ $wanted1 = array($id1 => 'admin', $id2 => 'view',);
+
$this->assertEquals($wanted1, $access1);
}
-
+
/**
* wrong user => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -833,17 +817,16 @@ class UsersManagerTest extends DatabaseTestCase
{
try {
$access1 = Piwik_UsersManager_API::getInstance()->getSitesAccessFromUser("user1");
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionUserDoesNotExist)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
+
/**
*wrong idsite => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -856,10 +839,10 @@ class UsersManagerTest extends DatabaseTestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* wrong access =>exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -867,99 +850,97 @@ class UsersManagerTest extends DatabaseTestCase
{
try {
$access1 = Piwik_UsersManager_API::getInstance()->getUsersSitesFromAccess('unknown');
- }
- catch (Exception $expected) {
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionAccessValues)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
+
/**
* non existing login => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testUpdateUserWrongLogin()
{
try {
- Piwik_UsersManager_API::getInstance()->updateUser( "lolgin", "password");
- }
- catch (Exception $expected) {
+ Piwik_UsersManager_API::getInstance()->updateUser("lolgin", "password");
+ } catch (Exception $expected) {
$this->assertRegExp("(UsersManager_ExceptionUserDoesNotExist)", $expected->getMessage());
return;
}
$this->fail("Exception not raised.");
}
-
-
+
+
/**
* no email no alias => keep old ones
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testUpdateUserNoEmailNoAlias()
{
- $login="login";
- $user = array('login'=>$login,
- 'password'=>"geqgeagae",
- 'email'=>"test@test.com",
- 'alias'=>"alias");
-
- Piwik_UsersManager_API::getInstance()->addUser($user['login'],$user['password'] ,$user['email'] ,$user['alias'] );
-
- Piwik_UsersManager_API::getInstance()->updateUser( $login, "passowordOK");
-
+ $login = "login";
+ $user = array('login' => $login,
+ 'password' => "geqgeagae",
+ 'email' => "test@test.com",
+ 'alias' => "alias");
+
+ Piwik_UsersManager_API::getInstance()->addUser($user['login'], $user['password'], $user['email'], $user['alias']);
+
+ Piwik_UsersManager_API::getInstance()->updateUser($login, "passowordOK");
+
$this->_checkUserHasNotChanged($user, "passowordOK");
}
-
+
/**
*no email => keep old ones
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testUpdateUserNoEmail()
{
- $login="login";
- $user = array('login'=>$login,
- 'password'=>"geqgeagae",
- 'email'=>"test@test.com",
- 'alias'=>"alias");
-
- Piwik_UsersManager_API::getInstance()->addUser($user['login'],$user['password'] ,$user['email'] ,$user['alias'] );
-
- Piwik_UsersManager_API::getInstance()->updateUser( $login, "passowordOK", null, "newalias");
-
+ $login = "login";
+ $user = array('login' => $login,
+ 'password' => "geqgeagae",
+ 'email' => "test@test.com",
+ 'alias' => "alias");
+
+ Piwik_UsersManager_API::getInstance()->addUser($user['login'], $user['password'], $user['email'], $user['alias']);
+
+ Piwik_UsersManager_API::getInstance()->updateUser($login, "passowordOK", null, "newalias");
+
$this->_checkUserHasNotChanged($user, "passowordOK", null, "newalias");
}
-
+
/**
* no alias => keep old ones
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testUpdateUserNoAlias()
{
- $login="login";
- $user = array('login'=>$login,
- 'password'=>"geqgeagae",
- 'email'=>"test@test.com",
- 'alias'=>"alias");
-
- Piwik_UsersManager_API::getInstance()->addUser($user['login'],$user['password'] ,$user['email'] ,$user['alias'] );
-
- Piwik_UsersManager_API::getInstance()->updateUser( $login, "passowordOK", "email@geaga.com");
-
+ $login = "login";
+ $user = array('login' => $login,
+ 'password' => "geqgeagae",
+ 'email' => "test@test.com",
+ 'alias' => "alias");
+
+ Piwik_UsersManager_API::getInstance()->addUser($user['login'], $user['password'], $user['email'], $user['alias']);
+
+ Piwik_UsersManager_API::getInstance()->updateUser($login, "passowordOK", "email@geaga.com");
+
$this->_checkUserHasNotChanged($user, "passowordOK", "email@geaga.com");
}
-
+
/**
* check to modify as the user
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -968,10 +949,10 @@ class UsersManagerTest extends DatabaseTestCase
FakeAccess::$identity = 'login';
$this->testUpdateUserNoEmailNoAlias();
}
-
+
/**
* check to modify as being another user => exception
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -986,31 +967,31 @@ class UsersManagerTest extends DatabaseTestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* normal case, reused in other tests
- *
+ *
* @group Plugins
* @group UsersManager
*/
public function testUpdateUser()
{
- $login="login";
- $user = array('login'=>$login,
- 'password'=>"geqgeagae",
- 'email'=>"test@test.com",
- 'alias'=>"alias");
-
- Piwik_UsersManager_API::getInstance()->addUser($user['login'],$user['password'] ,$user['email'] ,$user['alias'] );
-
- Piwik_UsersManager_API::getInstance()->updateUser( $login, "passowordOK", "email@geaga.com", "NEW ALIAS");
-
+ $login = "login";
+ $user = array('login' => $login,
+ 'password' => "geqgeagae",
+ 'email' => "test@test.com",
+ 'alias' => "alias");
+
+ Piwik_UsersManager_API::getInstance()->addUser($user['login'], $user['password'], $user['email'], $user['alias']);
+
+ Piwik_UsersManager_API::getInstance()->updateUser($login, "passowordOK", "email@geaga.com", "NEW ALIAS");
+
$this->_checkUserHasNotChanged($user, "passowordOK", "email@geaga.com", "NEW ALIAS");
}
-
+
/**
* test getUserByEmail invalid mail
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -1023,10 +1004,10 @@ class UsersManagerTest extends DatabaseTestCase
}
$this->fail('Expected exception not raised');
}
-
+
/**
* test getUserByEmail
- *
+ *
* @group Plugins
* @group UsersManager
*/
@@ -1036,14 +1017,14 @@ class UsersManagerTest extends DatabaseTestCase
'password' => "geqgeagae",
'email' => "test@test.com",
'alias' => "alias");
-
- Piwik_UsersManager_API::getInstance()->addUser($user['login'],$user['password'] ,$user['email'] ,$user['alias'] );
-
+
+ Piwik_UsersManager_API::getInstance()->addUser($user['login'], $user['password'], $user['email'], $user['alias']);
+
$userByMail = Piwik_UsersManager_API::getInstance()->getUserByEmail($user['email']);
-
+
$this->assertEquals($user['login'], $userByMail['login']);
$this->assertEquals($user['email'], $userByMail['email']);
$this->assertEquals($user['alias'], $userByMail['alias']);
}
-
+
}
diff --git a/tests/PHPUnit/bootstrap.php b/tests/PHPUnit/bootstrap.php
index d7fce36cc5..882f3ae579 100644
--- a/tests/PHPUnit/bootstrap.php
+++ b/tests/PHPUnit/bootstrap.php
@@ -1,18 +1,14 @@
<?php
-if(!defined("PIWIK_PATH_TEST_TO_ROOT"))
-{
+if (!defined("PIWIK_PATH_TEST_TO_ROOT")) {
define('PIWIK_PATH_TEST_TO_ROOT', realpath(dirname(__FILE__) . '/../..'));
}
-if(!defined('PIWIK_USER_PATH'))
-{
+if (!defined('PIWIK_USER_PATH')) {
define('PIWIK_USER_PATH', PIWIK_PATH_TEST_TO_ROOT);
}
-if(!defined('PIWIK_INCLUDE_PATH'))
-{
+if (!defined('PIWIK_INCLUDE_PATH')) {
define('PIWIK_INCLUDE_PATH', PIWIK_PATH_TEST_TO_ROOT);
}
-if(!defined('PIWIK_INCLUDE_SEARCH_PATH'))
-{
+if (!defined('PIWIK_INCLUDE_SEARCH_PATH')) {
define('PIWIK_INCLUDE_SEARCH_PATH', get_include_path()
. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/core'
. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/libs'
@@ -21,27 +17,26 @@ if(!defined('PIWIK_INCLUDE_SEARCH_PATH'))
@ini_set('include_path', PIWIK_INCLUDE_SEARCH_PATH);
@set_include_path(PIWIK_INCLUDE_SEARCH_PATH);
@ini_set('memory_limit', -1);
-error_reporting(E_ALL|E_NOTICE);
+error_reporting(E_ALL | E_NOTICE);
@date_default_timezone_set('UTC');
-require_once PIWIK_INCLUDE_PATH .'/libs/upgradephp/upgrade.php';
-require_once PIWIK_INCLUDE_PATH .'/core/testMinimumPhpVersion.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Loader.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/IntegrationTestCase.php';
-require_once PIWIK_INCLUDE_PATH .'/tests/PHPUnit/FakeAccess.php';
-require_once PIWIK_INCLUDE_PATH .'/tests/PHPUnit/MockPiwikOption.php';
-require_once PIWIK_INCLUDE_PATH .'/tests/PHPUnit/MockEventDispatcher.php';
+require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php';
+require_once PIWIK_INCLUDE_PATH . '/core/testMinimumPhpVersion.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Loader.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/IntegrationTestCase.php';
+require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/FakeAccess.php';
+require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/MockPiwikOption.php';
+require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/MockEventDispatcher.php';
// required to build code coverage for uncovered files
-require_once PIWIK_INCLUDE_PATH .'/plugins/SecurityInfo/PhpSecInfo/PhpSecInfo.php';
+require_once PIWIK_INCLUDE_PATH . '/plugins/SecurityInfo/PhpSecInfo/PhpSecInfo.php';
// require test fixtures
-require_once PIWIK_INCLUDE_PATH.'/tests/PHPUnit/BaseFixture.php';
-foreach (glob(PIWIK_INCLUDE_PATH.'/tests/PHPUnit/Fixtures/*.php') as $file)
-{
- require_once $file;
+require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/BaseFixture.php';
+foreach (glob(PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Fixtures/*.php') as $file) {
+ require_once $file;
}
// General requirement checks & help: a webserver must be running for tests to work!
@@ -49,31 +44,32 @@ checkPiwikSetupForTests();
function checkPiwikSetupForTests()
{
- if(empty($_SERVER['REQUEST_URI'])
- || $_SERVER['REQUEST_URI'] == '@REQUEST_URI@') {
- echo "WARNING: for tests to pass, you must first:
+ if (empty($_SERVER['REQUEST_URI'])
+ || $_SERVER['REQUEST_URI'] == '@REQUEST_URI@'
+ ) {
+ echo "WARNING: for tests to pass, you must first:
1) Install webserver on localhost, eg. apache
2) Make these Piwik files available on the webserver, at eg. http://localhost/dev/piwik/ - Piwik does need to be installed to run tests, but this URL must work.
3) Copy phpunit.xml.dist to phpunit.xml
4) Edit in phpunit.xml the @REQUEST_URI@ and replace with the webserver path to Piwik, eg. '/dev/piwik/'
Try again and now the tests should run!";
- exit(1);
- }
+ exit(1);
+ }
- // Now testing if the webserver is running
- $piwikServerUrl = Test_Piwik_BaseFixture::getRootUrl();
- try {
- $fetched = Piwik_Http::sendHttpRequest($piwikServerUrl, $timeout = 3);
- } catch(Exception $e) {
- $fetched = "ERROR fetching: " . $e->getMessage();
- }
- $expectedString = 'plugins/CoreHome/templates/images/favicon.ico';
+ // Now testing if the webserver is running
+ $piwikServerUrl = Test_Piwik_BaseFixture::getRootUrl();
+ try {
+ $fetched = Piwik_Http::sendHttpRequest($piwikServerUrl, $timeout = 3);
+ } catch (Exception $e) {
+ $fetched = "ERROR fetching: " . $e->getMessage();
+ }
+ $expectedString = 'plugins/CoreHome/templates/images/favicon.ico';
- if(strpos($fetched, $expectedString) === false) {
- echo "\nPiwik should be running at: " . $piwikServerUrl
- . "\nbut this URL returned an unexpected response: '"
- . substr($fetched,0,300) . "...'\n\n";
- exit;
- }
+ if (strpos($fetched, $expectedString) === false) {
+ echo "\nPiwik should be running at: " . $piwikServerUrl
+ . "\nbut this URL returned an unexpected response: '"
+ . substr($fetched, 0, 300) . "...'\n\n";
+ exit;
+ }
}
diff --git a/tests/PHPUnit/config.ini.travis.php b/tests/PHPUnit/config.ini.travis.php
index e4336bfdd1..54530281bd 100644
--- a/tests/PHPUnit/config.ini.travis.php
+++ b/tests/PHPUnit/config.ini.travis.php
@@ -3,15 +3,15 @@
; tests on Travis-CI. Do not use this in production.
[superuser]
-login = admin
-password = 098f6bcd4621d373cade4e832627b4f6
-email = hello@example.org
+login = admin
+password = 098f6bcd4621d373cade4e832627b4f6
+email = hello@example.org
[database]
-host = localhost
-username = root
-password =
-dbname = piwik_test
-adapter = PDO_MYSQL ; PDO_MYSQL, MYSQLI, or PDO_PGSQL
-tables_prefix = piwik_
-;charset = utf8
+host = localhost
+username = root
+password =
+dbname = piwik_test
+adapter = PDO_MYSQL ; PDO_MYSQL, MYSQLI, or PDO_PGSQL
+tables_prefix = piwik_
+;charset = utf8
diff --git a/tests/PHPUnit/proxy/index.php b/tests/PHPUnit/proxy/index.php
index e3034ccb1c..fd59a24554 100644
--- a/tests/PHPUnit/proxy/index.php
+++ b/tests/PHPUnit/proxy/index.php
@@ -2,8 +2,8 @@
/**
* Proxy to index.php, but will use the Test DB
* Currently only used only for the test: tests/PHPUnit/Integration/ImportLogsTest.php
- * since other integration tests do not call index.php via http but use the Piwik_API_Request object
- *
+ * since other integration tests do not call index.php via http but use the Piwik_API_Request object
+ *
*/
// Wrapping the request inside ob_start() calls to ensure that the Test
@@ -13,25 +13,26 @@ ob_start();
define('PIWIK_INCLUDE_PATH', '../../..');
define('PIWIK_USER_PATH', PIWIK_INCLUDE_PATH);
-require_once PIWIK_INCLUDE_PATH .'/libs/upgradephp/upgrade.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Loader.php';
+require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Loader.php';
Piwik_Tracker::setTestEnvironment();
Piwik_Tracker_Cache::deleteTrackerCache();
-class Piwik_FrontController_Test extends Piwik_FrontController
+class Piwik_FrontController_Test extends Piwik_FrontController
{
- protected function createConfigObject()
- {
- // Config files forced to use the test database
- Piwik::createConfigObject();
- Piwik_Config::getInstance()->setTestEnvironment();
- }
- protected function createAccessObject()
- {
- parent::createAccessObject();
- Piwik::setUserIsSuperUser(true);
- }
+ protected function createConfigObject()
+ {
+ // Config files forced to use the test database
+ Piwik::createConfigObject();
+ Piwik_Config::getInstance()->setTestEnvironment();
+ }
+
+ protected function createAccessObject()
+ {
+ parent::createAccessObject();
+ Piwik::setUserIsSuperUser(true);
+ }
}
// Disable index.php dispatch since we do it manually below
@@ -41,5 +42,5 @@ include PIWIK_INCLUDE_PATH . '/index.php';
$controller = new Piwik_FrontController_Test;
$controller->init();
$controller->dispatch();
-
+
ob_flush();
diff --git a/tests/PHPUnit/proxy/piwik.php b/tests/PHPUnit/proxy/piwik.php
index b79fbc24b3..f70183ed16 100755
--- a/tests/PHPUnit/proxy/piwik.php
+++ b/tests/PHPUnit/proxy/piwik.php
@@ -1,12 +1,12 @@
<?php
/**
* Proxy to normal piwik.php, but in testing mode
- *
+ *
* - Use the tests database to record Tracking data
- * - Allows to overwrite the Visitor IP, and Server datetime
- *
+ * - Allows to overwrite the Visitor IP, and Server datetime
+ *
* @see Main.test.php
- *
+ *
*/
// Wrapping the request inside ob_start() calls to ensure that the Test
@@ -16,8 +16,8 @@ ob_start();
define('PIWIK_INCLUDE_PATH', '../../..');
define('PIWIK_USER_PATH', PIWIK_INCLUDE_PATH);
-require_once PIWIK_INCLUDE_PATH .'/libs/upgradephp/upgrade.php';
-require_once PIWIK_INCLUDE_PATH .'/core/Loader.php';
+require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php';
+require_once PIWIK_INCLUDE_PATH . '/core/Loader.php';
// Config files forced to use the test database
// Note that this also provides security for Piwik installs containing tests files:
diff --git a/tests/index.php b/tests/index.php
index 6a4e708988..c5acf9d64d 100644
--- a/tests/index.php
+++ b/tests/index.php
@@ -1,31 +1,38 @@
<html>
<body>
<p>Hello, world!</p>
+
<p>Today looks like an ideal day to write & run tests.</p>
<ul>
- <li><a href='lib/visualphpunit/'>Run unit & integration tests in the browser.</a> <br/><i>(If you're using apache, make sure the mod_rewrite module is enabled.)</i></li>
- <li><a href="javascript/">Run piwik.js Javascript unit & integration tests</a>. <br/><i>Note: the Javascript tests are not executed in Jenkins so must be run manually on major browsers after any change to piwik.js</i></li>
+ <li><a href='lib/visualphpunit/'>Run unit & integration tests in the browser.</a> <br/><i>(If you're using apache,
+ make sure the mod_rewrite module is enabled.)</i></li>
+ <li><a href="javascript/">Run piwik.js Javascript unit & integration tests</a>. <br/><i>Note: the Javascript tests
+ are not executed in Jenkins so must be run manually on major browsers after any change to piwik.js</i></li>
</ul>
<h4>Configuring VisualPHPUnit:</h4>
+
<p>You'll have to configure VisualPHPUnit before you can use it. To do so:
<ul>
- <li>In path-to-piwik/tests/lib/visualphpunit/app/config/bootstrap.php, set the value of 'pear_path' to the path where PEAR is located.</li>
- <li>If you want to use a phpunit.xml file, copy the phpunit.xml.dist file in path-to-piwik/tests/PHPUnit/phpunit.xml.dist & rename it to phpunit.xml. Then add the following to it:
+ <li>In path-to-piwik/tests/lib/visualphpunit/app/config/bootstrap.php, set the value of 'pear_path' to the path
+ where PEAR is located.
+ </li>
+ <li>If you want to use a phpunit.xml file, copy the phpunit.xml.dist file in
+ path-to-piwik/tests/PHPUnit/phpunit.xml.dist & rename it to phpunit.xml. Then add the following to it:
<pre>
<code>
- &lt;listeners&gt;
- &lt;listener class=&quot;PHPUnit_Util_Log_JSON&quot;&gt;&lt;/listener&gt;
- &lt;/listeners&gt;
- </code>
+ &lt;listeners&gt;
+ &lt;listener class=&quot;PHPUnit_Util_Log_JSON&quot;&gt;&lt;/listener&gt;
+ &lt;/listeners&gt;
+ </code>
</pre>
- </li>
+ </li>
</ul>
</p>
<p>If you are new to the wonderful world of testing, <a href='README.txt'>see the README</a> for an introduction.</p>
-
+
<img src='resources/disturbing-image.jpg' alt='I find your lack of tests disturbing'>
<br/><i><a href='http://www.flickr.com/photos/sebastian_bergmann/2282734669/'>Photo source & license</a></i>
</body>